├── .prettierignore ├── packages ├── client │ ├── test │ │ ├── mocks │ │ │ ├── foo.txt │ │ │ └── MockTransport.ts │ │ ├── namespace.spec.ts │ │ ├── resource.spec.ts │ │ ├── embed.spec.ts │ │ ├── HTTPMethods.spec.ts │ │ ├── slug.spec.ts │ │ ├── param.spec.ts │ │ ├── header.spec.ts │ │ ├── discover.spec.ts │ │ ├── file.spec.ts │ │ ├── request.spec.ts │ │ ├── Client.spec.ts │ │ ├── Transport │ │ │ └── index.spec.ts │ │ └── middlewares.spec.ts │ ├── jest.setup.js │ ├── README.md │ ├── jest.config.js │ ├── src │ │ ├── index.ts │ │ ├── Client.types.ts │ │ ├── Transport.types.ts │ │ ├── Transport.ts │ │ └── Client.ts │ ├── tsconfig.json │ ├── package.json │ └── tsconfig.tsbuildinfo ├── react │ ├── README.md │ ├── jest.config.js │ ├── src │ │ ├── index.ts │ │ ├── provider.types.ts │ │ ├── provider.tsx │ │ ├── withClient.tsx │ │ └── useClient.ts │ ├── tsconfig.json │ ├── test │ │ ├── withClient.spec.tsx │ │ └── useClient.spec.tsx │ └── package.json └── support │ ├── jest.config.js │ ├── README.md │ ├── tsconfig.json │ ├── src │ ├── HTTPError.ts │ └── index.ts │ ├── test │ ├── HTTPError.spec.ts │ └── index.spec.ts │ └── package.json ├── examples ├── next │ ├── .gitignore │ ├── readme.md │ ├── package.json │ ├── pages │ │ └── index.js │ └── package-lock.json ├── parcel │ ├── .gitignore │ ├── package.json │ ├── readme.md │ ├── index.html │ ├── index.js │ └── package-lock.json ├── react │ ├── readme.md │ ├── public │ │ ├── favicon.ico │ │ ├── manifest.json │ │ └── index.html │ ├── src │ │ ├── index.js │ │ └── App.js │ ├── .gitignore │ └── package.json └── transports │ ├── axios.js │ ├── ky.js │ ├── ky-universal.js │ └── superagent.js ├── lerna.json ├── .gitignore ├── .prettierrc ├── .editorconfig ├── .github ├── renovate.json ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── issue_template.md └── workflows │ └── node.yml ├── docs ├── v3 │ └── readme.md ├── v1 │ └── readme.md └── v2 │ └── readme.md ├── jest.config.js ├── .eslintrc ├── README.md ├── package.json ├── tsconfig.json ├── LICENSE └── CONTRIBUTING.md /.prettierignore: -------------------------------------------------------------------------------- 1 | packages/*/dist 2 | -------------------------------------------------------------------------------- /packages/client/test/mocks/foo.txt: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /examples/next/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | out 3 | node_modules 4 | -------------------------------------------------------------------------------- /examples/parcel/.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | dist 3 | node_modules 4 | -------------------------------------------------------------------------------- /packages/client/jest.setup.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch'); 2 | -------------------------------------------------------------------------------- /examples/next/readme.md: -------------------------------------------------------------------------------- 1 | # Next example 2 | 3 | 1. Run `npm install` 4 | 2. Run `npm run dev` 5 | -------------------------------------------------------------------------------- /examples/react/readme.md: -------------------------------------------------------------------------------- 1 | # React example 2 | 3 | 1. Run `npm install` 4 | 2. Run `npm run start` 5 | -------------------------------------------------------------------------------- /examples/react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylletjs/yllet/HEAD/examples/react/public/favicon.ico -------------------------------------------------------------------------------- /packages/client/README.md: -------------------------------------------------------------------------------- 1 | # yllet 2 | 3 | Please refer to its documentation [here](https://github.com/ylletjs/yllet). 4 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # yllet-react 2 | 3 | Please refer to its documentation [here](https://github.com/ylletjs/yllet). 4 | -------------------------------------------------------------------------------- /examples/parcel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel-example", 3 | "dependencies": { 4 | "@yllet/client": "^3.0.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/parcel/readme.md: -------------------------------------------------------------------------------- 1 | # Parcel example 2 | 3 | 1. Run `npm install` 4 | 2. Run `npm install -g parcel-bundler` 5 | 3. Run `parcel index.html` 6 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "npmClient": "yarn", 6 | "useWorkspaces": true, 7 | "version": "3.0.2" 8 | } 9 | -------------------------------------------------------------------------------- /packages/support/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: '@yllet/support', 3 | transform: { 4 | '^.+\\.ts?$': 'ts-jest', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/react/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: '@yllet/react', 3 | testEnvironment: 'jsdom', 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest', 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Provider } from './provider'; 2 | export { default as withClient } from './withClient'; 3 | export { default as useClient } from './useClient'; 4 | -------------------------------------------------------------------------------- /examples/react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /packages/support/README.md: -------------------------------------------------------------------------------- 1 | # `support` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const support = require('support'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src" 6 | }, 7 | "exclude": ["dist", "test"] 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | packages/*/dist/ 4 | packages/*/node_modules/ 5 | .jest-* 6 | browserstack.err 7 | .DS_Store 8 | /index.js 9 | /test* 10 | /coverage 11 | tsconfig.tsbuildinfo 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "trailingComma": "all", 6 | "printWidth": 100, 7 | "tabWidth": 2, 8 | "semi": true 9 | } 10 | -------------------------------------------------------------------------------- /packages/client/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: '@yllet/client', 3 | setupFilesAfterEnv: ['/jest.setup.js'], 4 | transform: { 5 | '^.+\\.ts?$': 'ts-jest', 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/client/src/index.ts: -------------------------------------------------------------------------------- 1 | export type { Options, Params, Next, Middleware } from './Client.types'; 2 | export type { Transport } from './Transport.types'; 3 | export { default as default } from './Client'; 4 | -------------------------------------------------------------------------------- /examples/parcel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Yllet demo 4 | 5 | 6 |

Posts

7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /packages/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src" 6 | }, 7 | "exclude": ["dist", "test"], 8 | "references": [{ "path": "../support" }] 9 | } 10 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src", 6 | "jsx": "react" 7 | }, 8 | "exclude": ["dist", "test"], 9 | "references": [{ "path": "../client" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/client/src/Client.types.ts: -------------------------------------------------------------------------------- 1 | import Client from '.'; 2 | 3 | export type Options = Record; 4 | 5 | export type Params = Record; 6 | 7 | export type Next = () => void; 8 | 9 | export type Middleware = (client: Client, next: Next) => void; 10 | -------------------------------------------------------------------------------- /packages/react/src/provider.types.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import Client from '@yllet/client'; 3 | export interface ContextValue { 4 | client: Client | null; 5 | } 6 | 7 | export type ProviderProps = { 8 | client: Client; 9 | children: ReactNode; 10 | }; 11 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "rangeStrategy": "bump", 6 | "automerge": true, 7 | "major": { 8 | "automerge": false 9 | }, 10 | "minor": { 11 | "automerge": false 12 | }, 13 | "separateMultipleMajor": true 14 | } 15 | -------------------------------------------------------------------------------- /docs/v3/readme.md: -------------------------------------------------------------------------------- 1 | # Yllet docs v3 2 | 3 | Version 3 is a rewritten version of [version 2](../v2/readme.md) in TypeScript with a few changes. 4 | The documentation for [version 2](../v2/readme.md) still applies 5 | 6 | ## Changes 7 | 8 | - TypeScript types 9 | - `withClientData` function is removed 10 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | projects: ['/packages/*/jest.config.js'], 5 | testPathIgnorePatterns: ['/packages/(?:.+?)/dist/'], 6 | coveragePathIgnorePatterns: ['/packages/(?:.+?)/dist/'], 7 | coverageReporters: ['html', 'json', 'lcov', 'text', 'clover'] 8 | }; 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ### Description 8 | 9 | (please provide us with clear instructions on how you verified your changes work) 10 | 11 | ### Checklist 12 | 13 | - [ ] Bug fix? 14 | - [ ] New feature? 15 | - [ ] Deprecations? 16 | - [ ] Created tests, if possible 17 | -------------------------------------------------------------------------------- /packages/react/src/provider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { ContextValue, ProviderProps } from './provider.types'; 3 | 4 | export const Context = React.createContext({ client: null }); 5 | 6 | export const Provider = ({ client, children }: ProviderProps) => ( 7 | {children} 8 | ); 9 | -------------------------------------------------------------------------------- /packages/react/src/withClient.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { Context } from './provider'; 3 | 4 | const withClient = 5 | (Component: React.ComponentType) => 6 | ({ ...props }) => { 7 | const context = useContext(Context); 8 | props.client = context.client; 9 | return ; 10 | }; 11 | 12 | export default withClient; 13 | -------------------------------------------------------------------------------- /examples/parcel/index.js: -------------------------------------------------------------------------------- 1 | import Client from '@yllet/client'; 2 | 3 | const list = document.getElementById('posts'); 4 | 5 | const client = new Client({ 6 | endpoint: 'http://wordpress.test/wp-json/' 7 | }); 8 | 9 | client 10 | .posts() 11 | .get() 12 | .then((res) => { 13 | res.forEach((p) => { 14 | list.innerHTML += '
  • ' + p.title.rendered + '
  • '; 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /examples/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@yllet/react": "^3.0.1", 7 | "next": "^12.1.0", 8 | "react": "^17.0.2", 9 | "react-dom": "^17.0.2" 10 | }, 11 | "scripts": { 12 | "export": "next export", 13 | "start": "next start", 14 | "build": "next build", 15 | "dev": "next" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /packages/client/src/Transport.types.ts: -------------------------------------------------------------------------------- 1 | export interface Transport { 2 | delete(url: string, data: Record | undefined, config: Record): any; 3 | get(url: string, data: Record | undefined, config: Record): any; 4 | post(url: string, data: Record | undefined, config: Record): any; 5 | put(url: string, data: Record | undefined, config: Record): any; 6 | } 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "node": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "plugins": ["@typescript-eslint"], 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "rules": { 14 | "@typescript-eslint/ban-ts-comment": 0, 15 | "@typescript-eslint/explicit-module-boundary-types": 0, 16 | "@typescript-eslint/no-explicit-any": 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/support/src/HTTPError.ts: -------------------------------------------------------------------------------- 1 | export default class HTTPError extends Error { 2 | /** 3 | * Name of the error. 4 | * 5 | * @var {string} 6 | */ 7 | name: string; 8 | 9 | /** 10 | * The response. 11 | * 12 | * @var {object} 13 | */ 14 | response: Record; 15 | 16 | /** 17 | * HTTPError constructor. 18 | * 19 | * @param {object} response 20 | */ 21 | constructor(response: Record) { 22 | super(response.statusText); 23 | this.name = 'HTTPError'; 24 | this.response = response; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/react/src/useClient.ts: -------------------------------------------------------------------------------- 1 | import Client from '@yllet/client'; 2 | import type { Options } from '@yllet/client'; 3 | import { useMemo, useContext } from 'react'; 4 | import { Context } from './provider'; 5 | 6 | /** 7 | * Use client creates a new client. 8 | * 9 | * @param {object} options 10 | */ 11 | const useClient = (options: string | Options = {}) => { 12 | const context = useContext(Context); 13 | 14 | if (context.client) { 15 | return context.client; 16 | } 17 | 18 | return useMemo(() => new Client(options), []); 19 | }; 20 | 21 | export default useClient; 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ### What I expected 8 | 9 | (write your answer here) 10 | 11 | ### What happened instead 12 | 13 | (write your answer here) 14 | 15 | ### Steps to reproduce 16 | 17 | (write your steps here:) 18 | 19 | 1. 20 | 2. 21 | 3. 22 | 23 | ### Environment 24 | 25 | 1. `node -v`: 26 | 2. `npm -v`: 27 | 3. `yarn --version` (if you use Yarn): 28 | 29 | Then, specify: 30 | 31 | 1. Operating system: 32 | 2. Browser and version (if relevant): 33 | -------------------------------------------------------------------------------- /packages/react/test/withClient.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Client from '@yllet/client' 3 | import { render } from '@testing-library/react'; 4 | import { Provider, withClient } from '../src'; 5 | 6 | const Foo = ({ client }:any) => {client.options.endpoint}; 7 | 8 | test('withClient', async () => { 9 | const endpoint = 'http://wordpress.test/wp-json'; 10 | const client = new Client({ endpoint }); 11 | const Connected = withClient(Foo); 12 | const wrapper = render( 13 | 14 | 15 | 16 | ); 17 | expect(wrapper.getByText(endpoint)).not.toBeUndefined(); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/react/test/useClient.spec.tsx: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks' 2 | import { useClient } from '../src'; 3 | 4 | describe('useClient', () => { 5 | it('test useClient', () => { 6 | const endpoint = 'https://demo.wp-api.org/wp-json'; 7 | const { result } = renderHook(() => useClient({ endpoint })); 8 | expect(result.current.options.endpoint).toBe(endpoint); 9 | }); 10 | 11 | it('test useClient with string', () => { 12 | const endpoint = 'https://demo.wp-api.org/wp-json'; 13 | const { result } = renderHook(() => useClient(endpoint)); 14 | expect(result.current.options.endpoint).toBe(endpoint); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /packages/client/test/mocks/MockTransport.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/features/promise'; 2 | import { fn } from 'jest-mock'; 3 | 4 | export default class MockTransport { 5 | responses: Record = {}; 6 | 7 | constructor(responses = {}) { 8 | this.responses = responses; 9 | this.resetMocks(); 10 | } 11 | 12 | resetMocks() { 13 | ['post', 'get', 'put', 'delete'].forEach((verb) => { 14 | this[verb] = fn((url, data, config) => this.request(verb)); 15 | }); 16 | } 17 | 18 | request(verb: string) { 19 | return new Promise((resolve, reject) => { 20 | if (this.responses[verb]) { 21 | resolve(this.responses[verb]); 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ### What I expected 12 | 13 | (write your answer here) 14 | 15 | ### What happened instead 16 | 17 | (write your answer here) 18 | 19 | ### Steps to reproduce 20 | 21 | (write your steps here:) 22 | 23 | 1. 24 | 2. 25 | 3. 26 | 27 | ### Environment 28 | 29 | 1. `node -v`: 30 | 2. `npm -v`: 31 | 3. `yarn --version` (if you use Yarn): 32 | 33 | Then, specify: 34 | 35 | 1. Operating system: 36 | 2. Browser and version (if relevant): 37 | -------------------------------------------------------------------------------- /packages/client/test/namespace.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | 5 | const transport = new MockTransport(); 6 | const endpoint = 'http://wordpress.test/wp-json'; 7 | const client = new Client({ transport, endpoint }); 8 | 9 | describe('Client.namespace', () => { 10 | beforeEach(() => { 11 | transport.resetMocks(); 12 | }); 13 | 14 | it('sets the current namespace', () => { 15 | client.namespace('wc/v1'); 16 | expect(client.options.namespace).toBe('wc/v1'); 17 | }); 18 | 19 | it('has fluent interface', () => { 20 | const returnValue = client.namespace('wc/v1'); 21 | expect(returnValue).toBe(client); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/client/test/resource.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | 5 | const transport = new MockTransport(); 6 | const endpoint = 'http://wordpress.test/wp-json'; 7 | const client = new Client({ transport, endpoint }); 8 | 9 | describe('Client.resource', () => { 10 | beforeEach(() => { 11 | transport.resetMocks(); 12 | }); 13 | 14 | it('sets the current path', () => { 15 | client.resource('products'); 16 | expect(client.options.resource).toBe('products'); 17 | }); 18 | 19 | it('has fluent interface', () => { 20 | const returnValue = client.resource('products'); 21 | expect(returnValue).toBe(client); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | 6 | # Run tests for any PRs. 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [12.x, 14.x, 16.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3.7.0 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - name: yarn install 24 | run: yarn install 25 | - name: yarn lint 26 | run: yarn lint 27 | - name: yarn build 28 | run: yarn build 29 | - name: yarn test 30 | run: yarn test 31 | env: 32 | CI: true 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yllet 2 | 3 | [![Build Status](https://github.com/ylletjs/yllet/workflows/build/badge.svg)](https://github.com/ylletjs/yllet/actions) 4 | ![npm (scoped)](https://img.shields.io/npm/v/@yllet/client) 5 | ![npm bundle size (scoped version)](https://img.shields.io/bundlephobia/minzip/@yllet/client) 6 | 7 | 8 | ## Docs 9 | 10 | - [Version 1](docs/v1) 11 | - [Version 2](docs/v2) 12 | - [Version 3](docs/v3) 13 | 14 | ## Contributing 15 | 16 | We'd love to have your helping hand on yllet! See [CONTRIBUTING.md](https://github.com/ylletjs/yllet/blob/master/CONTRIBUTING.md) for more information. 17 | 18 | ## License 19 | 20 | MIT © [Fredrik Forsmo](https://github.com/frozzare) 21 | -------------------------------------------------------------------------------- /examples/next/pages/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useClient } from '@yllet/react'; 3 | 4 | export default () => { 5 | const endpoint = 'http://wordpress.test/wp-json/'; 6 | const client = useClient({ endpoint }); 7 | const [posts, setPosts] = useState([]); 8 | 9 | useEffect(() => { 10 | const fetchPosts = async () => { 11 | const response = await client.posts().get(); 12 | setPosts(response); 13 | }; 14 | 15 | fetchPosts(); 16 | }, [client]); 17 | 18 | return ( 19 |
    20 |

    Posts

    21 | {!posts.length &&

    Loading...

    } 22 |
      23 | {posts.map((p, pi) => ( 24 |
    • {p.title.rendered}
    • 25 | ))} 26 |
    27 |
    28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /examples/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@yllet/react": "^3.0.1", 7 | "react": "^17.0.2", 8 | "react-dom": "^17.0.2", 9 | "react-scripts": "^4.0.3" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject" 16 | }, 17 | "eslintConfig": { 18 | "extends": "react-app" 19 | }, 20 | "browserslist": { 21 | "production": [ 22 | ">0.2%", 23 | "not dead", 24 | "not op_mini all" 25 | ], 26 | "development": [ 27 | "last 1 chrome version", 28 | "last 1 firefox version", 29 | "last 1 safari version" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/react/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useClient } from '@yllet/react'; 3 | 4 | function App() { 5 | const endpoint = 'http://wordpress.test/wp-json/'; 6 | const client = useClient({ endpoint }); 7 | const [posts, setPosts] = useState([]); 8 | 9 | useEffect(() => { 10 | const fetchPosts = async () => { 11 | const response = await client.posts().get(); 12 | setPosts(response); 13 | }; 14 | 15 | fetchPosts(); 16 | }, [client]); 17 | 18 | return ( 19 |
    20 |

    Posts

    21 | {!posts.length &&

    Loading...

    } 22 |
      23 | {posts.map((p, pi) => ( 24 |
    • {p.title.rendered}
    • 25 | ))} 26 |
    27 |
    28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /packages/client/test/embed.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | 5 | const transport = new MockTransport(); 6 | const endpoint = 'http://wordpress.test/wp-json'; 7 | const client = new Client({ transport, endpoint }); 8 | 9 | const params = { 10 | title: 'Hello World', 11 | content: 'Welcome to the Wordpress API' 12 | }; 13 | 14 | describe('Client.embed', () => { 15 | it('can enable embed mode', () => { 16 | client.embed().request('get', params); 17 | expect((transport as any).get.mock.calls[0][1]).toEqual({ 18 | _embed: true, 19 | ...params 20 | }); 21 | }); 22 | 23 | // it('has fluent interface', () => { 24 | // const returnValue = client.embed(); 25 | // expect(returnValue).toBe(client); 26 | // }); 27 | }); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yllet", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/*" 6 | ], 7 | "devDependencies": { 8 | "@types/jest": "^28.1.8", 9 | "@typescript-eslint/eslint-plugin": "^6.0.0", 10 | "@typescript-eslint/parser": "^6.0.0", 11 | "eslint": "^8.44.0", 12 | "isomorphic-fetch": "^3.0.0", 13 | "jest": "^28.1.3", 14 | "jest-mock": "28.1.3", 15 | "lerna": "^4.0.0", 16 | "prettier": "^2.8.8", 17 | "rimraf": "^3.0.2", 18 | "ts-jest": "^28.0.8", 19 | "typescript": "^4.9.5" 20 | }, 21 | "scripts": { 22 | "bootstrap": "lerna bootstrap --use-workspaces", 23 | "build": "lerna run build", 24 | "start": "lerna run start", 25 | "test": "jest packages/**", 26 | "postinstall": "lerna bootstrap", 27 | "lint": "eslint packages/**/src --ext .ts", 28 | "format": "prettier --write 'packages/**/src/**/*.ts*' packages/**/test/**/*.ts*" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["es2020", "dom"], 5 | "allowJs": false, 6 | "declaration": true, 7 | "emitDeclarationOnly": false, 8 | "outDir": "dist/types", 9 | "rootDirs": ["src"], 10 | "noEmit": false, 11 | "strict": true, 12 | "moduleResolution": "node", 13 | "allowSyntheticDefaultImports": true, 14 | "esModuleInterop": true, 15 | "experimentalDecorators": true, 16 | "emitDecoratorMetadata": true, 17 | "skipLibCheck": true, 18 | "baseUrl": "src", 19 | "forceConsistentCasingInFileNames": true, 20 | "noImplicitReturns": true, 21 | "suppressImplicitAnyIndexErrors": true, 22 | "noUnusedLocals": true, 23 | "composite": true 24 | }, 25 | "exclude": ["packages/**/test/**", "packages/**/dist/**"], 26 | "references": [ 27 | { "path": "./packages/client" }, 28 | { "path": "./packages/react" }, 29 | { "path": "./packages/support" } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /examples/transports/axios.js: -------------------------------------------------------------------------------- 1 | import AxiosClient from 'axios'; 2 | import FormData from 'isomorphic-form-data'; 3 | 4 | class AxiosTransport { 5 | axios = undefined; 6 | 7 | constructor(axios) { 8 | this.axios = typeof axios === 'undefined' ? AxiosClient : axios; 9 | 10 | ['post', 'get', 'put', 'delete'].forEach((verb) => { 11 | this[verb] = (url, data, config) => this.request(verb, url, data, config); 12 | }); 13 | } 14 | 15 | request(verb, url, data, config = {}) { 16 | const request = { 17 | ...config, 18 | url, 19 | method: verb.toUpperCase() 20 | }; 21 | 22 | if (['PUT', 'POST'].includes(verb.toUpperCase())) { 23 | request.data = data; 24 | } else { 25 | if (data instanceof FormData) { 26 | throw new TypeError( 27 | 'Unable to encode FormData for GET, DELETE requests' 28 | ); 29 | } 30 | request.params = data; 31 | } 32 | 33 | return this.axios(request).then((response) => response.data); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/client/test/HTTPMethods.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | 5 | const METHODS = { 6 | get: 'get', 7 | create: 'post', 8 | update: 'put', 9 | delete: 'delete' 10 | }; 11 | 12 | describe('Client.METHODS', () => { 13 | Object.keys(METHODS).forEach((method) => { 14 | const verb = METHODS[method]; 15 | it(`"${method}" calls correct HTTP verb on transport`, () => { 16 | const transport = new MockTransport(); 17 | const client = new Client({ 18 | endpoint: 'http://wordpress.test/wp-json/', 19 | transport 20 | }); 21 | client[method]('products', { foo: 'bar' }); 22 | expect((transport[verb] as any).mock.calls.length).toBe(1); 23 | expect((transport[verb] as any).mock.calls[0][0]).toBe( 24 | 'http://wordpress.test/wp-json/wp/v2/products' 25 | ); 26 | expect((transport[verb] as any).mock.calls[0][1]).toEqual({ foo: 'bar' }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/support/test/HTTPError.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { HTTPError } from '../src'; 3 | 4 | describe('HTTPError', () => { 5 | it('has error message', () => { 6 | const response = { 7 | status: 422, 8 | statusText: 'Invalid input data' 9 | }; 10 | const error = new HTTPError(response); 11 | expect(error.message).toBe('Invalid input data'); 12 | }); 13 | 14 | it('has correct name', () => { 15 | const response = { 16 | status: 422, 17 | statusText: 'Invalid input data' 18 | }; 19 | const error = new HTTPError(response); 20 | expect(error.name).toBe('HTTPError'); 21 | }); 22 | 23 | it('assigns response as property', () => { 24 | const response = { 25 | status: 422, 26 | statusText: 'Invalid input data' 27 | }; 28 | const error = new HTTPError(response); 29 | expect(error.response).toEqual(response); 30 | expect(error.response.status).toBe(response.status); 31 | expect(error.response.statusText).toBe(response.statusText); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/client/test/slug.spec.ts: -------------------------------------------------------------------------------- 1 | import Client from '../src'; 2 | import MockTransport from './mocks/MockTransport'; 3 | import expect from 'expect'; 4 | 5 | const responses = { 6 | get: { 7 | posts: [ 8 | { id: 1, title: 'post-1' }, 9 | { id: 2, title: 'post-2' }, 10 | { id: 3, title: 'post-3' } 11 | ] 12 | } 13 | }; 14 | 15 | const transport = new MockTransport(responses); 16 | const endpoint = 'http://wordpress.test/wp-json'; 17 | const client = new Client({ transport, endpoint }); 18 | 19 | describe('Client.slug', () => { 20 | it('return first post', async () => { 21 | await client.posts().slug('about'); 22 | expect((transport as any).get.mock.calls[0][0]).toBe( 23 | `${endpoint}/wp/v2/posts` 24 | ); 25 | }); 26 | 27 | it('has the right params', async () => { 28 | await client.posts().slug('about', { 29 | search: 'search' 30 | }); 31 | expect((transport as any).get.mock.calls[1][1]).toEqual({ 32 | search: 'search', 33 | per_page: 1, 34 | slug: 'about' 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/client/test/param.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | 5 | const transport = new MockTransport(); 6 | const endpoint = 'http://wordpress.test/wp-json'; 7 | const client = new Client({ transport, endpoint }); 8 | 9 | describe('Client.resource', () => { 10 | beforeEach(() => { 11 | transport.resetMocks(); 12 | }); 13 | 14 | it('can return a param', () => { 15 | client.params.foo = 'bar'; 16 | expect(client.param('foo')).toBe('bar'); 17 | }); 18 | 19 | it('can set a param', () => { 20 | client.param('test', 'foo'); 21 | expect(client.params.test).toBe('foo'); 22 | }); 23 | 24 | it('can set a param object', () => { 25 | client.param({ a: '1', b: '2' }); 26 | expect(client.params).toEqual({ 27 | foo: 'bar', 28 | test: 'foo', 29 | a: '1', 30 | b: '2' 31 | }); 32 | }); 33 | 34 | it('has fluent interface', () => { 35 | const returnValue = client.param({ a: '1', b: '2' }); 36 | expect(returnValue).toBe(client); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/client/test/header.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | 5 | const transport = new MockTransport(); 6 | const endpoint = 'http://wordpress.test/wp-json'; 7 | const client = new Client({ transport, endpoint }); 8 | 9 | describe('Client.header', () => { 10 | beforeEach(() => { 11 | transport.resetMocks(); 12 | }); 13 | 14 | it('set a single header', () => { 15 | client.header('X-Foo', 'bar'); 16 | expect(client.options.config.headers['X-Foo']).toBe('bar'); 17 | }); 18 | 19 | it('can return headers', () => { 20 | expect(client.header('X-Foo')).toBe('bar'); 21 | }); 22 | 23 | it('set a headers object', () => { 24 | client.header({ 'X-Foo': '1', 'X-Bar': '2' }); 25 | expect(client.options.config.headers).toEqual({ 26 | 'Content-Type': 'application/json', 27 | 'X-Foo': '1', 28 | 'X-Bar': '2' 29 | }); 30 | }); 31 | 32 | it('has fluent interface', () => { 33 | const returnValue = client.header('X-Foo', 'bar'); 34 | expect(returnValue).toBe(client); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Fredrik Forsmo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/transports/ky.js: -------------------------------------------------------------------------------- 1 | import { qsEncode } from '@yllet/support'; 2 | import KyClient from 'ky'; 3 | import FormData from 'isomorphic-form-data'; 4 | 5 | class KyTransport { 6 | constructor(ky) { 7 | this.ky = typeof ky === 'undefined' ? KyClient : ky; 8 | ['post', 'get', 'put', 'delete'].forEach((verb) => { 9 | this[verb] = (url, data, config) => this.request(verb, url, data, config); 10 | }); 11 | } 12 | 13 | async request(verb, url, data, config = {}) { 14 | const request = { 15 | ...config, 16 | method: verb.toUpperCase() 17 | }; 18 | 19 | if (data) { 20 | if (['PUT', 'POST'].includes(verb.toUpperCase())) { 21 | request.body = data instanceof FormData ? data : JSON.stringify(data); 22 | } else { 23 | if (data instanceof FormData) { 24 | throw new TypeError( 25 | 'Unable to encode FormData for GET, DELETE requests' 26 | ); 27 | } 28 | 29 | const qs = qsEncode(data); 30 | 31 | if (qs.length) { 32 | url = `${url}?${qs}`; 33 | } 34 | } 35 | } 36 | 37 | return await this.ky(url, request).json(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/transports/ky-universal.js: -------------------------------------------------------------------------------- 1 | import { qsEncode } from '@yllet/support'; 2 | import KyClient from 'ky-universal'; 3 | import FormData from 'isomorphic-form-data'; 4 | 5 | class KyTransport { 6 | constructor(ky) { 7 | this.ky = typeof ky === 'undefined' ? KyClient : ky; 8 | ['post', 'get', 'put', 'delete'].forEach((verb) => { 9 | this[verb] = (url, data, config) => this.request(verb, url, data, config); 10 | }); 11 | } 12 | 13 | async request(verb, url, data, config = {}) { 14 | const request = { 15 | ...config, 16 | method: verb.toUpperCase() 17 | }; 18 | 19 | if (data) { 20 | if (['PUT', 'POST'].includes(verb.toUpperCase())) { 21 | request.body = data instanceof FormData ? data : JSON.stringify(data); 22 | } else { 23 | if (data instanceof FormData) { 24 | throw new TypeError( 25 | 'Unable to encode FormData for GET, DELETE requests' 26 | ); 27 | } 28 | 29 | const qs = qsEncode(data); 30 | 31 | if (qs.length) { 32 | url = `${url}?${qs}`; 33 | } 34 | } 35 | } 36 | 37 | return await this.ky(url, request).json(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/support/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@yllet/support", 3 | "private": false, 4 | "version": "3.0.1", 5 | "repository": "ylletjs/yllet", 6 | "description": "Support package for Yllet", 7 | "author": "Fredrik Forsmo ", 8 | "homepage": "https://github.com/ylletjs/yllet#readme", 9 | "license": "MIT", 10 | "main": "dist/cjs/index.js", 11 | "module": "dist/esm/index.js", 12 | "types": "dist/types", 13 | "files": [ 14 | "README.md", 15 | "dist/" 16 | ], 17 | "scripts": { 18 | "prebuild": "rimraf dist", 19 | "build": "yarn build:cjs && yarn build:esm && yarn build:types", 20 | "build:cjs": "tsc --target es5 --module commonjs --outDir dist/cjs", 21 | "build:esm": "tsc --target es2020 --module es2020 --outDir dist/esm", 22 | "build:types": "tsc --target es2020 --module es2020 --outDir dist/types --emitDeclarationOnly", 23 | "postbuild": "rimraf dist/cjs/*.d.ts dist/esm/*.d.ts", 24 | "preversion": "yarn run build" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/ylletjs/yllet/issues" 28 | }, 29 | "gitHead": "a2a2a3d8db1a79d619928f935f18c1bd05c8622d", 30 | "publishConfig": { 31 | "access": "public" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/transports/superagent.js: -------------------------------------------------------------------------------- 1 | import SuperagentClient from 'superagent'; 2 | 3 | class SuperagentTransport { 4 | constructor(superagent) { 5 | this.superagent = 6 | typeof superagent === 'undefined' ? SuperagentClient : superagent; 7 | 8 | ['post', 'get', 'put', 'delete'].forEach((verb) => { 9 | this[verb] = (url, data, config) => this.request(verb, url, data, config); 10 | }); 11 | } 12 | 13 | async request(verb, url, data, config = {}) { 14 | let request = this.superagent[verb](url); 15 | 16 | if (data) { 17 | if (['PUT', 'POST'].includes(verb.toUpperCase())) { 18 | request = request.send( 19 | data instanceof FormData ? data : JSON.stringify(data) 20 | ); 21 | } else { 22 | if (data instanceof FormData) { 23 | throw new TypeError( 24 | 'Unable to encode FormData for GET, DELETE requests' 25 | ); 26 | } 27 | 28 | request = request.query(data); 29 | } 30 | } 31 | 32 | Object.keys(config.headers).forEach((key) => { 33 | request = request.set(key, config.headers[key]); 34 | }); 35 | 36 | return request.then((res) => { 37 | return JSON.parse(res.text); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/client/test/discover.spec.ts: -------------------------------------------------------------------------------- 1 | import Client from '../src'; 2 | import MockTransport from './mocks/MockTransport'; 3 | import expect from 'expect'; 4 | 5 | describe('Client.discover', () => { 6 | it('requests discovery route', () => { 7 | const transport = new MockTransport(); 8 | const client = new Client({ transport }); 9 | client.discover('http://demo.wp-api.org'); 10 | expect((transport as any).get.mock.calls[0][0]).toBe( 11 | 'http://demo.wp-api.org' 12 | ); 13 | expect((transport as any).get.mock.calls[0][1]).toEqual({ 14 | rest_route: '/' 15 | }); 16 | }); 17 | 18 | it('returns api route', () => { 19 | const transport = new MockTransport({ 20 | get: { 21 | routes: { 22 | '/': { 23 | _links: { 24 | self: 'http://demo.wp-api.org/wp-json/' 25 | } 26 | } 27 | } 28 | } 29 | }); 30 | const client = new Client({ transport }); 31 | client 32 | .discover('http://demo.wp-api.org') 33 | .then((response) => 34 | expect(response).toBe('http://demo.wp-api.org/wp-json/') 35 | ); 36 | }); 37 | 38 | it('throws Error', () => { 39 | const transport = new MockTransport({ 40 | get: {} 41 | }); 42 | const client = new Client({ transport }); 43 | client.discover('http://demo.wp-api.org').catch((error) => { 44 | expect(error instanceof Error).toBe(true); 45 | expect(error.message).toBe('Unable to find the REST API'); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@yllet/client", 3 | "private": false, 4 | "version": "3.0.1", 5 | "repository": "ylletjs/yllet", 6 | "description": "JavaScript client for the WordPress REST API", 7 | "author": "Fredrik Forsmo ", 8 | "keywords": [ 9 | "wordpress", 10 | "rest", 11 | "api", 12 | "client", 13 | "isomorphic", 14 | "headless" 15 | ], 16 | "main": "dist/cjs/index.js", 17 | "module": "dist/esm/index.js", 18 | "types": "dist/types", 19 | "files": [ 20 | "README.md", 21 | "dist/" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/ylletjs/yllet/issues" 25 | }, 26 | "scripts": { 27 | "prebuild": "rimraf dist", 28 | "build": "yarn build:cjs && yarn build:esm && yarn build:types", 29 | "build:cjs": "tsc --target es5 --module commonjs --outDir dist/cjs && echo \"module.exports = exports.default;\" >> dist/cjs/index.js", 30 | "build:esm": "tsc --target es2020 --module es2020 --outDir dist/esm", 31 | "build:types": "tsc --target es2020 --module es2020 --outDir dist/types --emitDeclarationOnly", 32 | "postbuild": "rimraf dist/cjs/*.d.ts dist/esm/*.d.ts", 33 | "preversion": "yarn run build" 34 | }, 35 | "dependencies": { 36 | "@yllet/support": "^3.0.1", 37 | "isomorphic-form-data": "^2.0.0" 38 | }, 39 | "license": "MIT", 40 | "devDependencies": { 41 | "@types/isomorphic-form-data": "2.0.1", 42 | "fetch-mock": "^9.11.0" 43 | }, 44 | "gitHead": "a2a2a3d8db1a79d619928f935f18c1bd05c8622d", 45 | "publishConfig": { 46 | "access": "public" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/client/test/file.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import fs from 'fs'; 3 | import Client from '../src'; 4 | import FormData from 'isomorphic-form-data'; 5 | import MockTransport from './mocks/MockTransport'; 6 | 7 | const transport = new MockTransport(); 8 | const endpoint = 'http://wordpress.test/wp-json'; 9 | const client = new Client({ transport, endpoint }); 10 | 11 | const isFsAvailable = typeof fs.createReadStream === 'function'; 12 | 13 | const isRunningInBrowser = Boolean(process.env.BROWSER_ENV); 14 | 15 | function runTests(file: any) { 16 | describe('Client.file', () => { 17 | beforeEach(() => { 18 | transport.resetMocks(); 19 | }); 20 | 21 | it('can attach file from argument', () => { 22 | client.file(file, 'foo.txt'); 23 | expect(client.formData instanceof FormData).toBe(true); 24 | }); 25 | 26 | it('adds correct headers to request', () => { 27 | client.file(file, 'foo.txt'); 28 | expect(client.options.config.headers['Content-Type']).toBe( 29 | 'multipart/form-data' 30 | ); 31 | expect(client.options.config.headers['Content-Disposition']).toMatch( 32 | /attachment; filename=foo.txt/ 33 | ); 34 | }); 35 | 36 | it('has fluent interface', () => { 37 | const returnValue = client.file(file, 'foo.txt'); 38 | expect(returnValue).toBe(client); 39 | }); 40 | }); 41 | } 42 | 43 | // run tests 44 | 45 | if (isFsAvailable) { 46 | const file = fs.createReadStream(`${__dirname}/mocks/foo.txt`); 47 | runTests(file); 48 | } 49 | 50 | if (isRunningInBrowser) { 51 | var file = new Blob(['foo'], { type: 'text/plain' }); 52 | runTests(file); 53 | } 54 | -------------------------------------------------------------------------------- /examples/react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | React App 23 | 24 | 25 | 26 |
    27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@yllet/react", 3 | "private": false, 4 | "version": "3.0.2", 5 | "repository": "ylletjs/yllet", 6 | "description": "React bindings for Yllet", 7 | "author": "Fredrik Forsmo ", 8 | "keywords": [ 9 | "wordpress", 10 | "rest", 11 | "api", 12 | "client", 13 | "react", 14 | "isomorphic", 15 | "headless" 16 | ], 17 | "main": "dist/cjs/index.js", 18 | "module": "dist/esm/index.js", 19 | "types": "dist/types", 20 | "files": [ 21 | "README.md", 22 | "dist/" 23 | ], 24 | "bugs": { 25 | "url": "https://github.com/ylletjs/yllet/issues" 26 | }, 27 | "scripts": { 28 | "prebuild": "rimraf dist", 29 | "build": "yarn build:cjs && yarn build:esm && yarn build:types", 30 | "build:cjs": "tsc --target es5 --module commonjs --outDir dist/cjs", 31 | "build:esm": "tsc --target es2020 --module es2020 --outDir dist/esm", 32 | "build:types": "tsc --target es2020 --module es2020 --outDir dist/types --emitDeclarationOnly", 33 | "postbuild": "rimraf dist/cjs/*.d.ts dist/esm/*.d.ts", 34 | "preversion": "yarn run build" 35 | }, 36 | "peerDependencies": { 37 | "react": "*" 38 | }, 39 | "dependencies": { 40 | "@yllet/client": "^3.0.1", 41 | "invariant": "^2.2.4", 42 | "prop-types": "^15.8.1" 43 | }, 44 | "devDependencies": { 45 | "@testing-library/jest-dom": "^5.16.5", 46 | "@testing-library/react": "^13.4.0", 47 | "@testing-library/react-hooks": "^8.0.1", 48 | "@types/invariant": "2.2.35", 49 | "jest-environment-jsdom": "^28.1.3", 50 | "@types/react": "^18.2.17", 51 | "react": "^18.2.0", 52 | "react-dom": "^18.2.0" 53 | }, 54 | "license": "MIT", 55 | "gitHead": "a2a2a3d8db1a79d619928f935f18c1bd05c8622d", 56 | "publishConfig": { 57 | "access": "public" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Yllet 2 | 3 | Everyone is welcome to contribute with patches, bug-fixes and new features. This is and will always be a community driven project. 4 | 5 | ## Core Ideas 6 | 7 | The purpose of Yllet is to create a easy WordPress REST API client that works with both React and non-React project. If a third party plugin works with the REST API it then it will work with Yllet. 8 | 9 | This project will never add plugin specific support since it's goal is to support the WordPress REST API. 10 | 11 | ## Submitting a Pull Request 12 | 13 | Good pull requests with patches, improvements or new features is always welcome. They should remain focused in scope and avoid containing unrelated commits. 14 | 15 | Please follow the projects code style and try to add tests for your changes. 16 | 17 | * Fork [ylletjs/ylelt](https://github.com/ylletjs/yllet) on Github and add the upstream remote. 18 | 19 | ``` 20 | git clone https://github.com//yllet.git 21 | cd yllet 22 | git remote add upstream https://github.com/ylletjs/yllet.git 23 | ``` 24 | 25 | This is useful if you cloned your repo a while ago and now wants to update it. 26 | 27 | ``` 28 | git checkout master 29 | git pull upstream master 30 | ``` 31 | 32 | * Create a new branch: 33 | 34 | ``` 35 | git checkout -b 36 | ``` 37 | 38 | * Make sure to update, or add to the tests when appropriate. 39 | * Commit your changes to your fork. 40 | * Locally merge (or rebase) the upstream development branch into your topic branch: 41 | 42 | ``` 43 | git pull [--rebase] upstream master 44 | ``` 45 | 46 | * Push to your branch: 47 | 48 | ``` 49 | git push origin 50 | ``` 51 | 52 | * [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description against the `master` branch. 53 | 54 | **Note:** 55 | If you are making several changes at once please divide them into multiple pull requests. 56 | 57 | ## Folder Structure 58 | 59 | Yllet it's a monorepo containg all packages. These packages can be found in the [`packages/`](https://github.com/ylletjs/yllet/tree/master/packages) directory. 60 | 61 | ## License 62 | 63 | By contributing your code, you agree to license your contribution under the [MIT license](https://github.com/ylletjs/yllet/blob/master/LICENSE). 64 | -------------------------------------------------------------------------------- /packages/client/test/request.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import Client from '../src'; 3 | import FormData from 'isomorphic-form-data'; 4 | import MockTransport from './mocks/MockTransport'; 5 | 6 | const transport = new MockTransport(); 7 | const endpoint = 'http://wordpress.test/wp-json'; 8 | const client = new Client({ transport, endpoint }); 9 | 10 | const params = { 11 | title: 'Hello World', 12 | content: 'Welcome to the Wordpress API' 13 | }; 14 | 15 | describe('Client.request', () => { 16 | beforeEach(() => { 17 | transport.resetMocks(); 18 | }); 19 | 20 | it('accepts path as 2nd param', () => { 21 | client.request('post', 'products'); 22 | expect((transport as any).post.mock.calls[0][0]).toBe( 23 | `${endpoint}/wp/v2/products` 24 | ); 25 | }); 26 | 27 | it('accepts params as 2nd param', () => { 28 | client.request('post', params); 29 | expect((transport as any).post.mock.calls[0][0]).toBe(`${endpoint}/wp/v2`); 30 | expect((transport as any).post.mock.calls[0][1]).toEqual(params); 31 | }); 32 | 33 | it('handles invalid paths', () => { 34 | client.request('post', undefined as any); 35 | expect((transport as any).post.mock.calls[0][0]).toBe(`${endpoint}/wp/v2`); 36 | client.request('post', 123 as any); 37 | expect((transport as any).post.mock.calls[1][0]).toBe( 38 | `${endpoint}/wp/v2/123` 39 | ); 40 | client.request('post', null as any); 41 | expect((transport as any).post.mock.calls[2][0]).toBe(`${endpoint}/wp/v2`); 42 | client.request('post', {}); 43 | expect((transport as any).post.mock.calls[3][0]).toBe(`${endpoint}/wp/v2`); 44 | client.request('post', 'products/variations/123'); 45 | expect((transport as any).post.mock.calls[4][0]).toBe( 46 | `${endpoint}/wp/v2/products/variations/123` 47 | ); 48 | }); 49 | 50 | it('makes verb lowercase', () => { 51 | client.request('POST', undefined as any); 52 | expect((transport as any).post.mock.calls[0][0]).toBe(`${endpoint}/wp/v2`); 53 | }); 54 | 55 | it('merges global params', () => { 56 | client.params = { a: '1', b: '2' }; 57 | client.request('post', params); 58 | expect((transport as any).post.mock.calls[0][1]).toEqual({ 59 | ...client.params, 60 | ...params 61 | }); 62 | }); 63 | 64 | it('converts request params to snake case', () => { 65 | client.params = { weLikeCamels: '1', andClimbingHills: '2' }; 66 | client.request('post', { andGentleWavesLikeThis: '3' }); 67 | expect((transport as any).post.mock.calls[0][1]).toEqual({ 68 | weLikeCamels: '1', 69 | andClimbingHills: '2', 70 | and_gentle_waves_like_this: '3' 71 | }); 72 | }); 73 | 74 | it('can send a file', () => { 75 | const formData = new FormData(); 76 | client.formData = formData; 77 | client.params = { a: '1', b: '2' }; 78 | client.request('post', params); 79 | 80 | const result = (transport as any).post.mock.calls[0][1]; 81 | expect(result instanceof FormData).toEqual(true); 82 | }); 83 | 84 | it('merges global request config', () => { 85 | client.options.config = { a: '1', b: '2' }; 86 | client.request('post'); 87 | expect((transport as any).post.mock.calls[0][2]).toEqual({ 88 | ...client.options.config 89 | }); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /packages/client/src/Transport.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { HTTPError, qsEncode } from '@yllet/support'; 3 | import FormData from 'isomorphic-form-data'; 4 | import type { Transport as ITransport } from './Transport.types'; 5 | class Transport implements ITransport { 6 | /** 7 | * Send delete request. 8 | * 9 | * @param {string} url 10 | * @param {object} data 11 | * @param {object} config 12 | * 13 | * @return {object} 14 | */ 15 | delete( 16 | url: string, 17 | data: Record | undefined = undefined, 18 | config: Record = {}, 19 | ): any { 20 | return this.request('delete', url, data, config); 21 | } 22 | 23 | /** 24 | * Send get request. 25 | * 26 | * @param {string} url 27 | * @param {object} data 28 | * @param {object} config 29 | * 30 | * @return {object} 31 | */ 32 | get( 33 | url: string, 34 | data: Record | undefined = undefined, 35 | config: Record = {}, 36 | ): any { 37 | return this.request('get', url, data, config); 38 | } 39 | 40 | /** 41 | * Send post request. 42 | * 43 | * @param {string} url 44 | * @param {object} data 45 | * @param {object} config 46 | * 47 | * @return {object} 48 | */ 49 | post( 50 | url: string, 51 | data: Record | undefined = undefined, 52 | config: Record = {}, 53 | ): any { 54 | return this.request('post', url, data, config); 55 | } 56 | 57 | /** 58 | * Send put request. 59 | * 60 | * @param {string} url 61 | * @param {object} data 62 | * @param {object} config 63 | * 64 | * @return {object} 65 | */ 66 | put( 67 | url: string, 68 | data: Record | undefined = undefined, 69 | config: Record = {}, 70 | ): any { 71 | return this.request('put', url, data, config); 72 | } 73 | 74 | /** 75 | * Sned request against url with data. 76 | * 77 | * @param {string} verb 78 | * @param {string} url 79 | * @param {object} data 80 | * @param {object} config 81 | * 82 | * @return {object} 83 | */ 84 | private request( 85 | verb: string, 86 | url: string, 87 | data: Record | undefined = undefined, 88 | config: Record = {}, 89 | ): any { 90 | const request: RequestInit = { 91 | ...config, 92 | method: verb.toUpperCase(), 93 | }; 94 | 95 | if (data) { 96 | if (['PUT', 'POST'].includes(verb.toUpperCase())) { 97 | request.body = data instanceof FormData ? (data as any) : JSON.stringify(data); 98 | } else { 99 | if (data instanceof FormData) { 100 | throw new TypeError('Unable to encode FormData for GET, DELETE requests'); 101 | } 102 | 103 | const qs = qsEncode(data); 104 | 105 | if (qs.length) { 106 | url = `${url}?${qs}`; 107 | } 108 | } 109 | } 110 | 111 | return fetch(url, request).then((response) => { 112 | return response.json().then((data) => { 113 | if (!response.ok) { 114 | throw new HTTPError(data); 115 | } 116 | 117 | return data; 118 | }); 119 | }); 120 | } 121 | } 122 | 123 | export default Transport; 124 | -------------------------------------------------------------------------------- /packages/support/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Export HTTPError. 3 | */ 4 | export { default as HTTPError } from './HTTPError'; 5 | 6 | /** 7 | * Test if given value is a object or not. 8 | * 9 | * @param {object} obj 10 | * 11 | * @return {bool} 12 | */ 13 | export const isObject = (obj: any): boolean => 14 | Object.prototype.toString.call(obj) === '[object Object]'; 15 | 16 | /** 17 | * Convert camel case to snake case. 18 | * 19 | * @param {string} str 20 | * 21 | * @return {string} 22 | */ 23 | const toSnakeCase = (str: string): string => 24 | str 25 | .replace(/[\w]([A-Z])/g, function (m) { 26 | return m[0] + '_' + m[1]; 27 | }) 28 | .toLowerCase(); 29 | 30 | /** 31 | * Convert object keys to snake case. 32 | * 33 | * @param {object} obj 34 | * 35 | * @return {object} 36 | */ 37 | export const objectKeysToSnakeCase = (obj: Record): Record => 38 | Object.keys(obj).reduce((previous, current) => { 39 | if (isObject(obj[current])) { 40 | obj[current] = objectKeysToSnakeCase(obj[current]); 41 | } 42 | 43 | if (obj[current] instanceof Array) { 44 | previous[toSnakeCase(current)] = obj[current].map(objectKeysToSnakeCase); 45 | } else { 46 | previous[toSnakeCase(current)] = obj[current]; 47 | } 48 | 49 | return previous; 50 | }, {}); 51 | 52 | /** 53 | * Flatten array. 54 | * 55 | * @param {array} list 56 | * 57 | * @return {array} 58 | */ 59 | const flattenArray = (list: any[]): any[] => 60 | list.reduce((a, b) => a.concat(Array.isArray(b) ? flattenArray(b) : b), []); 61 | 62 | /** 63 | * Encode objects as query strings. 64 | * 65 | * @param {object} params 66 | * @param {string} prefix 67 | * 68 | * @return {string} 69 | */ 70 | export const qsEncode = (params: Record, prefix = ''): string => 71 | Object.keys(params) 72 | .map((key) => { 73 | const isArray = params.constructor === Array; 74 | const value = isArray ? flattenArray(params)[key] : params[key]; 75 | 76 | if (isArray) { 77 | key = `${prefix}[]`; 78 | } else if (params.constructor === Object) { 79 | key = prefix ? `${prefix}[${key}]` : key; 80 | } 81 | 82 | if (typeof value === 'object') { 83 | return qsEncode(value, key); 84 | } 85 | 86 | return `${key}=${encodeURIComponent(value)}`; 87 | }) 88 | .join('&'); 89 | 90 | /** 91 | * Merge objects deep. 92 | * 93 | * @param {object} target 94 | * @param {object} source 95 | * 96 | * @return {object} 97 | */ 98 | export const mergeObjects = (target: Record, source: Record) => { 99 | if (!isObject(target) && !isObject(source)) { 100 | return target; 101 | } 102 | 103 | for (const key in source) { 104 | if (source[key] === null && (target[key] === undefined || target[key] === null)) { 105 | target[key] = null; 106 | } else if (source[key] instanceof Array) { 107 | if (!target[key]) { 108 | target[key] = []; 109 | } 110 | 111 | target[key] = target[key].concat(source[key]); 112 | } else if (isObject(source[key])) { 113 | if (!isObject(target[key])) { 114 | target[key] = {}; 115 | } 116 | 117 | mergeObjects(target[key], source[key]); 118 | } else { 119 | if (target === undefined) { 120 | target = {}; 121 | } 122 | 123 | target[key] = source[key]; 124 | } 125 | } 126 | 127 | return target; 128 | }; 129 | -------------------------------------------------------------------------------- /packages/client/test/Client.spec.ts: -------------------------------------------------------------------------------- 1 | import Client from '../src'; 2 | import MockTransport from './mocks/MockTransport'; 3 | import expect from 'expect'; 4 | 5 | const transport = new MockTransport(); 6 | const endpoint = 'http://wordpress.test/wp-json'; 7 | const client = new Client({ transport, endpoint }); 8 | const defaultOptions = { 9 | endpoint: '', 10 | namespace: 'wp/v2', 11 | nonce: '', 12 | config: { 13 | headers: { 14 | 'Content-Type': 'application/json' 15 | } 16 | }, 17 | resource: '', 18 | restore: true 19 | }; 20 | 21 | describe('Client', () => { 22 | beforeEach(() => { 23 | transport.resetMocks(); 24 | }); 25 | 26 | it('sets transport property', () => { 27 | expect(client.transport).toBe(transport); 28 | }); 29 | 30 | it('throws error when missing transport', () => { 31 | try { 32 | new Client({ transport: undefined }); 33 | } catch (error: any) { 34 | expect(error instanceof TypeError).toBe(true); 35 | expect(error.message).toBe('Transport is required option, none was set.'); 36 | } 37 | }); 38 | 39 | it('has default options', () => { 40 | const ylletClient = new Client({ transport }); 41 | expect(ylletClient.options).toEqual(defaultOptions); 42 | }); 43 | 44 | it('handle bad options input', () => { 45 | const ylletClient = new Client(undefined); 46 | expect(ylletClient.options).toEqual(defaultOptions); 47 | }); 48 | 49 | it('has nonce header', () => { 50 | const ylletClient = new Client({ 51 | nonce: 'XYZ' 52 | }); 53 | expect(ylletClient.options.config.headers['X-WP-Nonce']).toEqual('XYZ'); 54 | }); 55 | 56 | it('just endpoint option', () => { 57 | const ylletClient = new Client(endpoint); 58 | expect(ylletClient.options).toEqual({ 59 | ...defaultOptions, 60 | endpoint: endpoint 61 | }); 62 | }); 63 | 64 | it('bad options input', () => { 65 | const ylletClient = new Client(undefined); 66 | expect(ylletClient.options).toEqual(defaultOptions); 67 | }); 68 | 69 | it('merges options', () => { 70 | const ylletClient = new Client({ 71 | transport, 72 | headers: { 73 | 'X-Test': 'Test' 74 | }, 75 | endpoint: 'https://wordpress.test/wp-json', 76 | config: { 77 | foo: 'bar' 78 | } 79 | }); 80 | expect(ylletClient.options).toEqual({ 81 | endpoint: 'https://wordpress.test/wp-json', 82 | namespace: 'wp/v2', 83 | nonce: '', 84 | config: { 85 | foo: 'bar', 86 | headers: { 87 | 'Content-Type': 'application/json', 88 | 'X-Test': 'Test' 89 | } 90 | }, 91 | resource: '', 92 | restore: true 93 | }); 94 | }); 95 | 96 | it('has HTTP methods', () => { 97 | expect(typeof client.get).toBe('function'); 98 | expect(typeof client.create).toBe('function'); 99 | expect(typeof client.update).toBe('function'); 100 | expect(typeof client.delete).toBe('function'); 101 | }); 102 | 103 | it('has API Resource methods', () => { 104 | [ 105 | 'categories', 106 | 'comments', 107 | 'media', 108 | 'statuses', 109 | 'pages', 110 | 'posts', 111 | 'settings', 112 | 'tags', 113 | 'taxonomies', 114 | 'types', 115 | 'users', 116 | 'search' 117 | ].forEach((method) => { 118 | client[method](); 119 | expect(client.options.resource).toBe(method); 120 | }); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /packages/client/test/Transport/index.spec.ts: -------------------------------------------------------------------------------- 1 | import FormData from 'isomorphic-form-data'; 2 | import fetchMock from 'fetch-mock'; 3 | import Transport from '../../src/Transport'; 4 | // @ts-ignore 5 | import { HTTPError } from '@yllet/support'; 6 | 7 | const transport = new Transport(); 8 | const endpoint = 'https://wp.com/wp-json'; 9 | const verbs = ['GET', 'POST', 'PUT', 'DELETE']; 10 | 11 | beforeEach(() => { 12 | fetchMock.reset(); 13 | }); 14 | 15 | describe('request calls', () => { 16 | verbs.forEach((verb) => { 17 | it(`${verb} calls fetch once`, () => { 18 | fetchMock.once(endpoint, {}); 19 | transport[verb.toLocaleLowerCase()](endpoint).catch(console.log); 20 | expect((fetchMock.calls() as any).length).toEqual(1); 21 | }); 22 | }); 23 | }); 24 | 25 | describe('verbs', () => { 26 | verbs.forEach((verb) => { 27 | it(`${verb} sends correct http verb`, () => { 28 | fetchMock.once( 29 | endpoint, 30 | {}, 31 | { 32 | method: verb, 33 | }, 34 | ); 35 | transport[verb.toLocaleLowerCase()](endpoint); 36 | expect((fetchMock.calls() as any)[0][1].method).toEqual(verb); 37 | }); 38 | }); 39 | }); 40 | 41 | describe('url', () => { 42 | verbs.forEach((verb) => { 43 | it(`${verb} calls correct url`, () => { 44 | fetchMock.once(endpoint, {}); 45 | transport[verb.toLocaleLowerCase()](endpoint); 46 | expect((fetchMock.calls() as any)[0][0]).toEqual(endpoint); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('headers', () => { 52 | const config = { 53 | headers: { 54 | 'X-Foo': 'bar', 55 | }, 56 | }; 57 | 58 | verbs.forEach((verb) => { 59 | it(`${verb} sends correct headers`, () => { 60 | fetchMock.once(endpoint, {}); 61 | transport[verb.toLocaleLowerCase()](endpoint, {}, config); 62 | expect((fetchMock.calls() as any)[0][1].headers).toEqual(config.headers); 63 | }); 64 | }); 65 | }); 66 | 67 | describe('merge config', () => { 68 | const config = { 69 | foo: 'bar', 70 | bar: 'foo', 71 | }; 72 | 73 | verbs.forEach((verb) => { 74 | let expected = { 75 | ...config, 76 | method: verb, 77 | }; 78 | 79 | if (['POST', 'PUT'].includes(verb)) { 80 | expected = { 81 | ...expected, 82 | body: '{}', 83 | } as any; 84 | } 85 | 86 | it(`${verb} passes custom config`, () => { 87 | fetchMock.once(endpoint, {}); 88 | transport[verb.toLocaleLowerCase()](endpoint, {}, config); 89 | expect((fetchMock.calls() as any)[0][1]).toEqual(expected); 90 | }); 91 | }); 92 | }); 93 | 94 | describe('with data', () => { 95 | const data = { foo: 'bar', posts: [21, 33, 150] }; 96 | 97 | verbs.forEach((verb) => { 98 | it(`${verb} sends data`, () => { 99 | if (['GET', 'DELETE'].includes(verb)) { 100 | fetchMock.once('*', {}); 101 | transport[verb.toLocaleLowerCase()](endpoint, data); 102 | expect((fetchMock.calls() as any)[0][0]).toBe( 103 | endpoint + '?foo=bar&posts[]=21&posts[]=33&posts[]=150', 104 | ); 105 | expect((fetchMock.calls() as any)[0][1].body).toBe(undefined); 106 | } else { 107 | fetchMock.once('*', {}); 108 | transport[verb.toLocaleLowerCase()](endpoint, data); 109 | expect((fetchMock.calls() as any)[0][1].body).toEqual(JSON.stringify(data)); 110 | } 111 | }); 112 | }); 113 | }); 114 | 115 | describe('with form data', () => { 116 | const formData = new FormData(); 117 | formData.append('foo', 'bar'); 118 | 119 | verbs.forEach((verb) => { 120 | it(`${verb} sends form data`, () => { 121 | if (['GET', 'DELETE'].includes(verb)) { 122 | try { 123 | fetchMock.once(endpoint, {}); 124 | transport[verb.toLocaleLowerCase()](endpoint, formData); 125 | } catch (error: any) { 126 | expect(error instanceof TypeError).toBe(true); 127 | expect(error.message).toBe('Unable to encode FormData for GET, DELETE requests'); 128 | } 129 | } else { 130 | fetchMock.once(endpoint, {}); 131 | transport[verb.toLocaleLowerCase()](endpoint, formData); 132 | expect((fetchMock.calls() as any)[0][1].body instanceof FormData).toBe(true); 133 | } 134 | }); 135 | }); 136 | }); 137 | 138 | describe('without data', () => { 139 | verbs.forEach((verb) => { 140 | it(`${verb} sends data`, () => { 141 | fetchMock.once(endpoint, {}); 142 | transport[verb.toLocaleLowerCase()](endpoint); 143 | if (['GET', 'DELETE'].includes(verb)) { 144 | expect((fetchMock.calls() as any)[0][0]).toBe(endpoint); 145 | expect((fetchMock.calls() as any)[0][1].body).toBe(undefined); 146 | } else { 147 | expect((fetchMock.calls() as any)[0][1].body).toBe(undefined); 148 | } 149 | }); 150 | }); 151 | }); 152 | 153 | describe('returns json', () => { 154 | verbs.forEach((verb) => { 155 | it(`${verb} returns data`, () => { 156 | fetchMock.once(endpoint, { data: { mock: 'response' } }); 157 | transport[verb.toLocaleLowerCase()](endpoint).then((response: any) => { 158 | expect(response.data).toEqual({ mock: 'response' }); 159 | }); 160 | }); 161 | }); 162 | }); 163 | 164 | describe('http exceptions', () => { 165 | const response = { 166 | status: 503, 167 | body: { foo: 'bar' }, 168 | }; 169 | 170 | verbs.forEach((verb) => { 171 | it(`${verb} throws http exceptions`, () => { 172 | fetchMock.once('*', response); 173 | return transport[verb.toLocaleLowerCase()](endpoint).catch((error: any) => { 174 | expect(error.toString()).toContain('HTTPError'); 175 | expect(error.response).toEqual({ foo: 'bar' }); 176 | }); 177 | }); 178 | }); 179 | }); 180 | -------------------------------------------------------------------------------- /packages/client/test/middlewares.spec.ts: -------------------------------------------------------------------------------- 1 | import Client from '../src'; 2 | import type { Next } from '../src'; 3 | import MockTransport from './mocks/MockTransport'; 4 | import expect from 'expect'; 5 | 6 | const responses = { 7 | get: { 8 | posts: {} 9 | } 10 | }; 11 | const transport = new MockTransport(responses); 12 | const endpoint = 'http://wordpress.test/wp-json'; 13 | const client = new Client({ transport, endpoint }); 14 | 15 | const middlewareOne = (client: Client, next: Next) => { 16 | client.header('X-Foo', 'Bar'); 17 | return next(); 18 | }; 19 | 20 | const middlewareTwo = async (client: Client, next: Next) => { 21 | if (typeof jest !== 'undefined') { 22 | jest.useFakeTimers(); 23 | setTimeout(() => { 24 | client.header('X-Foo', 'Bar'); 25 | return next(); 26 | }, 1000); 27 | jest.runAllTimers(); 28 | } else { 29 | await new Promise((r) => setTimeout(r, 1000)); 30 | client.header('X-Foo', 'Bar'); 31 | return next(); 32 | } 33 | }; 34 | 35 | const middlewareThree = (client: Client, next: Next) => { 36 | client 37 | .endpoint('https://woocommerce.com/wp-json') 38 | .namespace('wc/v3') 39 | .resource('products'); 40 | next(); 41 | }; 42 | 43 | const middlewareFour = (client: Client, next: Next) => { 44 | client.transport = new MockTransport() as any; 45 | next(); 46 | }; 47 | 48 | describe('Middlewares', () => { 49 | beforeEach(() => { 50 | transport.resetMocks(); 51 | }); 52 | 53 | it('default middleware property', () => { 54 | expect(client.middlewares).toEqual([]); 55 | }); 56 | 57 | it('add middlewares as option', () => { 58 | const ylletClient = new Client({ middlewares: [middlewareOne] }); 59 | expect(ylletClient.middlewares).toEqual([middlewareOne]); 60 | }); 61 | 62 | it('add middlewares with use', () => { 63 | const ylletClient = new Client(); 64 | expect(ylletClient.middlewares).toEqual([]); 65 | 66 | ylletClient.use(middlewareOne); 67 | expect(ylletClient.middlewares).toEqual([middlewareOne]); 68 | 69 | ylletClient.use([middlewareOne]); 70 | expect(ylletClient.middlewares).toEqual([middlewareOne, middlewareOne]); 71 | }); 72 | 73 | it('run middleware one', async () => { 74 | const ylletClient = new Client({ 75 | headers: { 'X-Requested-With': 'Yllet' }, 76 | middlewares: [middlewareOne], 77 | transport 78 | }); 79 | expect((transport as any).get.mock.calls).toEqual([]); 80 | 81 | ylletClient 82 | .posts() 83 | .get() 84 | .then((res) => { 85 | expect(res).toBe(responses.get); 86 | expect((transport as any).get.mock.calls[0][0]).toBe('/wp/v2/posts'); 87 | expect( 88 | (transport as any).get.mock.calls[0][2].headers['X-Requested-With'] 89 | ).toBe('Yllet'); 90 | expect((transport as any).get.mock.calls[0][2].headers['X-Foo']).toBe( 91 | 'Bar' 92 | ); 93 | }); 94 | 95 | ylletClient 96 | .posts() 97 | .get() 98 | .then((res) => { 99 | expect(res).toBe(responses.get); 100 | expect((transport as any).get.mock.calls[1][0]).toBe('/wp/v2/posts'); 101 | expect( 102 | (transport as any).get.mock.calls[1][2].headers['X-Requested-With'] 103 | ).toBe('Yllet'); 104 | expect((transport as any).get.mock.calls[1][2].headers['X-Foo']).toBe( 105 | 'Bar' 106 | ); 107 | }); 108 | }); 109 | 110 | it('run middleware two', async () => { 111 | const ylletClient = new Client({ 112 | headers: { 'X-Requested-With': 'Yllet' }, 113 | middlewares: [middlewareTwo], 114 | transport 115 | }); 116 | expect((transport as any).get.mock.calls).toEqual([]); 117 | 118 | if (typeof jest !== 'undefined') { 119 | const res = await ylletClient.posts().get(); 120 | expect(res).toBe(responses.get); 121 | expect((transport as any).get.mock.calls[0][0]).toBe('/wp/v2/posts'); 122 | expect( 123 | (transport as any).get.mock.calls[0][2].headers['X-Requested-With'] 124 | ).toBe('Yllet'); 125 | expect((transport as any).get.mock.calls[0][2].headers['X-Foo']).toBe( 126 | 'Bar' 127 | ); 128 | } 129 | 130 | ylletClient 131 | .posts() 132 | .get() 133 | .then((res) => { 134 | expect(res).toBe(responses.get); 135 | expect((transport as any).get.mock.calls[1][0]).toBe('/wp/v2/posts'); 136 | expect( 137 | (transport as any).get.mock.calls[1][2].headers['X-Requested-With'] 138 | ).toBe('Yllet'); 139 | expect((transport as any).get.mock.calls[1][2].headers['X-Foo']).toBe( 140 | 'Bar' 141 | ); 142 | }); 143 | }); 144 | 145 | it('run middleware three', async () => { 146 | const ylletClient = new Client({ 147 | headers: { 'X-Requested-With': 'Yllet' }, 148 | middlewares: [middlewareThree], 149 | restore: false, 150 | transport 151 | }); 152 | expect((transport as any).get.mock.calls).toEqual([]); 153 | 154 | ylletClient 155 | .posts() 156 | .get() 157 | .then((res) => { 158 | expect(res).toBe(responses.get); 159 | expect((transport as any).get.mock.calls[0][0]).toBe('/wp/v2/posts'); 160 | expect(ylletClient.options.endpoint).toBe(''); 161 | expect(ylletClient.options.namespace).toBe('wp/v2'); 162 | expect(ylletClient.options.resource).toBe('posts'); 163 | }); 164 | }); 165 | 166 | it('run middleware four', async () => { 167 | const ylletClient = new Client({ 168 | headers: { 'X-Requested-With': 'Yllet' }, 169 | middlewares: [middlewareFour], 170 | restore: false, 171 | transport 172 | }); 173 | 174 | expect((transport as any).get.mock.calls).toEqual([]); 175 | expect(ylletClient.transport).toBeInstanceOf(MockTransport); 176 | 177 | ylletClient 178 | .posts() 179 | .get() 180 | .then((res) => { 181 | expect(res).toBe(responses.get); 182 | console.log(ylletClient.transport); 183 | expect(ylletClient.transport).toBeInstanceOf(MockTransport); 184 | }); 185 | }); 186 | }); 187 | -------------------------------------------------------------------------------- /examples/parcel/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel-example", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "parcel-example", 8 | "dependencies": { 9 | "@yllet/client": "^3.0.1" 10 | } 11 | }, 12 | "node_modules/@yllet/client": { 13 | "version": "3.0.1", 14 | "resolved": "https://registry.npmjs.org/@yllet/client/-/client-3.0.1.tgz", 15 | "integrity": "sha512-ktS3gUIh/CPbbkhvrIxQ40Ws2YdBNiOZKcLOJKdsZnnfEWNus9CUUMUV/8Ev4a1TI1N6WVnvwgMNciiuwdWR1Q==", 16 | "dependencies": { 17 | "@yllet/support": "^3.0.1", 18 | "isomorphic-form-data": "^2.0.0" 19 | } 20 | }, 21 | "node_modules/@yllet/support": { 22 | "version": "3.0.1", 23 | "resolved": "https://registry.npmjs.org/@yllet/support/-/support-3.0.1.tgz", 24 | "integrity": "sha512-NGbwwlgt8CK0RiR+rzj9ak3ikUaL4M7k9ebnEHbjlLPRfiufBy/1tUA3obaUYms3p/SI8J0it0HEUn7UXQmMzg==" 25 | }, 26 | "node_modules/asynckit": { 27 | "version": "0.4.0", 28 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 29 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 30 | }, 31 | "node_modules/combined-stream": { 32 | "version": "1.0.8", 33 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 34 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 35 | "dependencies": { 36 | "delayed-stream": "~1.0.0" 37 | }, 38 | "engines": { 39 | "node": ">= 0.8" 40 | } 41 | }, 42 | "node_modules/delayed-stream": { 43 | "version": "1.0.0", 44 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 45 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 46 | "engines": { 47 | "node": ">=0.4.0" 48 | } 49 | }, 50 | "node_modules/form-data": { 51 | "version": "2.5.1", 52 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 53 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 54 | "dependencies": { 55 | "asynckit": "^0.4.0", 56 | "combined-stream": "^1.0.6", 57 | "mime-types": "^2.1.12" 58 | }, 59 | "engines": { 60 | "node": ">= 0.12" 61 | } 62 | }, 63 | "node_modules/isomorphic-form-data": { 64 | "version": "2.0.0", 65 | "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz", 66 | "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==", 67 | "dependencies": { 68 | "form-data": "^2.3.2" 69 | } 70 | }, 71 | "node_modules/mime-db": { 72 | "version": "1.42.0", 73 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", 74 | "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", 75 | "engines": { 76 | "node": ">= 0.6" 77 | } 78 | }, 79 | "node_modules/mime-types": { 80 | "version": "2.1.25", 81 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", 82 | "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", 83 | "dependencies": { 84 | "mime-db": "1.42.0" 85 | }, 86 | "engines": { 87 | "node": ">= 0.6" 88 | } 89 | } 90 | }, 91 | "dependencies": { 92 | "@yllet/client": { 93 | "version": "3.0.1", 94 | "resolved": "https://registry.npmjs.org/@yllet/client/-/client-3.0.1.tgz", 95 | "integrity": "sha512-ktS3gUIh/CPbbkhvrIxQ40Ws2YdBNiOZKcLOJKdsZnnfEWNus9CUUMUV/8Ev4a1TI1N6WVnvwgMNciiuwdWR1Q==", 96 | "requires": { 97 | "@yllet/support": "^3.0.1", 98 | "isomorphic-form-data": "^2.0.0" 99 | } 100 | }, 101 | "@yllet/support": { 102 | "version": "3.0.1", 103 | "resolved": "https://registry.npmjs.org/@yllet/support/-/support-3.0.1.tgz", 104 | "integrity": "sha512-NGbwwlgt8CK0RiR+rzj9ak3ikUaL4M7k9ebnEHbjlLPRfiufBy/1tUA3obaUYms3p/SI8J0it0HEUn7UXQmMzg==" 105 | }, 106 | "asynckit": { 107 | "version": "0.4.0", 108 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 109 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 110 | }, 111 | "combined-stream": { 112 | "version": "1.0.8", 113 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 114 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 115 | "requires": { 116 | "delayed-stream": "~1.0.0" 117 | } 118 | }, 119 | "delayed-stream": { 120 | "version": "1.0.0", 121 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 122 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 123 | }, 124 | "form-data": { 125 | "version": "2.5.1", 126 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 127 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 128 | "requires": { 129 | "asynckit": "^0.4.0", 130 | "combined-stream": "^1.0.6", 131 | "mime-types": "^2.1.12" 132 | } 133 | }, 134 | "isomorphic-form-data": { 135 | "version": "2.0.0", 136 | "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz", 137 | "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==", 138 | "requires": { 139 | "form-data": "^2.3.2" 140 | } 141 | }, 142 | "mime-db": { 143 | "version": "1.42.0", 144 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", 145 | "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" 146 | }, 147 | "mime-types": { 148 | "version": "2.1.25", 149 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", 150 | "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", 151 | "requires": { 152 | "mime-db": "1.42.0" 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /docs/v1/readme.md: -------------------------------------------------------------------------------- 1 | # Yllet docs v1 2 | 3 | Yllet is a set of packages for the WordPress API for both React and non-React projects. The client is built on top of [axios](https://github.com/axios/axios). 4 | 5 | ## Installation 6 | 7 | To install the WordPress API client: 8 | 9 | ``` 10 | npm install --save yllet 11 | ``` 12 | 13 | To install the package with React bindings: 14 | 15 | ``` 16 | npm install --save yllet-react 17 | ``` 18 | 19 | ## Usage 20 | 21 | Fetch all posts: 22 | 23 | ```js 24 | import Client from 'yllet'; 25 | 26 | const client = new Client({ 27 | endpoint: 'https://demo.wp-api.org/wp-json/' 28 | }); 29 | 30 | client 31 | .posts() 32 | .get() 33 | .then((res) => { 34 | console.log(res.data); 35 | }) 36 | .catch((err) => { 37 | console.log('Error: ' + err.message); 38 | }); 39 | ``` 40 | 41 | ## Client options 42 | 43 | ```js 44 | { 45 | // Basic auth. 46 | auth: { 47 | username: '', 48 | password: '' 49 | }, 50 | 51 | // Axios settings. 52 | axios: {}, 53 | 54 | // HTTP headers. 55 | headers: {}, 56 | 57 | // WordPress API endpoint. 58 | endpoint: '', 59 | 60 | // Default namespace. 61 | namespace: 'wp/v2' 62 | } 63 | ``` 64 | 65 | If you need to pass something special to Axios you can use `axios` object. Both `auth` and `headers` will be merged to `axios` object. 66 | 67 | You can also set headers using `client.header()` with key/value or object. 68 | 69 | The Axios instance is accessable on the client instance, just use `client.axios` and then you can use interceptors or something other. 70 | 71 | ## Resources 72 | 73 | Yllet client instance provides the following basic request methods: 74 | 75 | - `client.categories()` 76 | - `client.comments()` 77 | - `client.media()` 78 | - `client.statuses()` 79 | - `client.posts()` 80 | - `client.pages()` 81 | - `client.settings()` 82 | - `client.tags()` 83 | - `client.taxonomies()` 84 | - `client.types()` 85 | - `client.users()` 86 | 87 | Using any request methods sets the path so you don't have to write `client.get('posts/1')` each time instead you can just write `client.posts().get(1)` 88 | 89 | Adding custom request methods is easy (example [WooCommerce REST API](https://woocommerce.github.io/woocommerce-rest-api-docs/)): 90 | 91 | ```js 92 | // Example: http://wordpress.test/wp-json/wc/v2/products 93 | client.products = () => client.namespace('wc/v2').resource('products'); 94 | ``` 95 | 96 | Then you can just call `client.products()` like you do with `client.posts()` 97 | 98 | ```js 99 | client 100 | .products() 101 | .get() 102 | .then((res) => { 103 | console.log(res.data); 104 | }) 105 | .catch((err) => { 106 | console.log('Error: ' + err.message); 107 | }); 108 | ``` 109 | 110 | ## Params 111 | 112 | You can pass a object with the same name as as the existing params. You can write `per_page` or `perPage` when the param contains a underscore. 113 | 114 | ```js 115 | client 116 | .posts() 117 | .get({ 118 | slug: 'hello-world', 119 | perPage: 1 // or per_page 120 | }) 121 | .then((res) => { 122 | console.log(res.data); 123 | }) 124 | .catch((err) => { 125 | console.log('Error: ' + err.message); 126 | }); 127 | ``` 128 | 129 | You can also set global params using key/value or object: 130 | 131 | ```js 132 | client.param('source', 'yllet'); 133 | ``` 134 | 135 | All global params can be accessed with `client.params` 136 | 137 | ## Embed data 138 | 139 | WordPress API support embedding of resources and instead of having to provide `_embed=true` as a param on every request we can simpley use `embed()` before any request method. 140 | 141 | More about WordPress API embedding can you read [here](https://developer.wordpress.org/rest-api/using-the-rest-api/linking-and-embedding/#embedding). 142 | 143 | ```js 144 | client 145 | .posts() 146 | .embed() 147 | .get({ 148 | slug: 'hello-world' 149 | }) 150 | .then((res) => { 151 | console.log(res.data); 152 | }) 153 | .catch((err) => { 154 | console.log('Error: ' + err.message); 155 | }); 156 | ``` 157 | 158 | ## File uploading 159 | 160 | When Uploading a file you should use `client.file(file, [name])` to specify a file or a file buffer to attach to the request with a name (optional). 161 | 162 | Browser example: 163 | 164 | ```js 165 | const file = document.getElementById('my-file').files[0]; 166 | 167 | client 168 | .media() 169 | .file(file) 170 | .create({ 171 | title: 'Test image' 172 | }) 173 | .then((res) => { 174 | console.log(res.data); 175 | }) 176 | .catch((err) => { 177 | console.log('Error: ', err); 178 | }); 179 | ``` 180 | 181 | Node example: 182 | 183 | ```js 184 | client 185 | .media() 186 | .file(fs.createReadStream('me.jpg')) 187 | .create({ 188 | title: 'Test image' 189 | }) 190 | .then((res) => { 191 | console.log(res.data); 192 | }) 193 | .catch((err) => { 194 | console.log('Error: ', err); 195 | }); 196 | ``` 197 | 198 | ## Discover the REST API from a URL 199 | 200 | ```js 201 | import Client from 'yllet'; 202 | 203 | Client.discover('http://demo.wp-api.org/') 204 | .then((url) => { 205 | console.log(url); 206 | }) 207 | .catch((err) => { 208 | console.log('Error: ', err); 209 | }); 210 | ``` 211 | 212 | ## React bindings 213 | 214 | Yllets React package contains a provider component that you can use like this: 215 | 216 | ```js 217 | import React from 'react'; 218 | import ReactDOM from 'react-dom'; 219 | import App from './App'; 220 | 221 | import Client from 'yllet'; 222 | import { Provider } from 'yllet-react'; 223 | 224 | const client = new Client({ 225 | endpoint: 'https://demo.wp-api.org/wp-json/' 226 | }); 227 | 228 | ReactDOM.render( 229 | 230 | 231 | , 232 | document.getElementById('root') 233 | ); 234 | ``` 235 | 236 | In your application component you can use the `withClient` component enhancer to pass along the `client` to your component. 237 | 238 | ```js 239 | import React from 'react'; 240 | import { withClient } from 'yllet-react'; 241 | 242 | class App extends React.Component {} 243 | 244 | export default withClient(App); 245 | ``` 246 | 247 | Then you can use `this.props.client` the same way as if it was a standalone client. 248 | 249 | You can also use `withClientData` to pass the response data or the error from WordPress REST API. 250 | 251 | ```js 252 | import React from 'react'; 253 | import { withClientData } from 'yllet-react'; 254 | 255 | class Post extends React.Component {} 256 | 257 | export default withClientData((client, props) => { 258 | return client.posts().get(1); 259 | })(Post); 260 | ``` 261 | 262 | Then you can use `this.props.data` or `this.props.error` and `this.props.loading` 263 | -------------------------------------------------------------------------------- /packages/support/test/index.spec.ts: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import { 3 | isObject, 4 | mergeObjects, 5 | objectKeysToSnakeCase, 6 | qsEncode 7 | } from '../src'; 8 | 9 | describe('isObject', () => { 10 | it('can test for objects', () => { 11 | [ 12 | [true, {}], 13 | [false, 1], 14 | [false, false], 15 | [false, []], 16 | [false, ''], 17 | [false, 1.2], 18 | [false, new Error()] 19 | ].forEach((d) => { 20 | expect(d[0]).toBe(isObject(d[1])); 21 | }); 22 | }); 23 | }); 24 | 25 | describe('objectKeysToSnakeCase', () => { 26 | it('can snake case object keys', () => { 27 | [ 28 | [ 29 | { 30 | test_data: 1 31 | }, 32 | { 33 | testData: 1 34 | } 35 | ], 36 | [ 37 | { 38 | test: { 39 | test_data: 1 40 | } 41 | }, 42 | { 43 | test: { 44 | testData: 1 45 | } 46 | } 47 | ], 48 | [ 49 | { 50 | abc: [ 51 | { 52 | abc: { 53 | abc_data: 1 54 | } 55 | } 56 | ] 57 | }, 58 | { 59 | abc: [ 60 | { 61 | abc: { 62 | abcData: 1 63 | } 64 | } 65 | ] 66 | } 67 | ] 68 | ].forEach((d) => { 69 | const obj = objectKeysToSnakeCase(d[1]); 70 | expect(d[0]).toEqual(obj); 71 | }); 72 | }); 73 | }); 74 | 75 | describe('qsEncode', () => { 76 | it('convert object to query string', () => { 77 | [ 78 | [ 79 | { 80 | a: 100, 81 | b: 'with spaces', 82 | c: [1, 2, 3], 83 | d: { x: 9, y: 8 } 84 | }, 85 | 'a=100&b=with%20spaces&c[]=1&c[]=2&c[]=3&d[x]=9&d[y]=8' 86 | ], 87 | [ 88 | { 89 | foo: 'bar', 90 | posts: [21, 33, 150] 91 | }, 92 | 'foo=bar&posts[]=21&posts[]=33&posts[]=150' 93 | ], 94 | [ 95 | { 96 | foo: [1, [2], 3], 97 | bar: ['one', ['two'], 'three'] 98 | }, 99 | 'foo[]=1&foo[]=2&foo[]=3&bar[]=one&bar[]=two&bar[]=three' 100 | ], 101 | [ 102 | { 103 | hello: 'world', 104 | foo: 'bar', 105 | colors: ['red', 'green', 'blue', 'yellow'], 106 | numbers: [100, 200, [300]], 107 | on: true, 108 | off: false, 109 | purple: 'haze' 110 | }, 111 | 'hello=world&foo=bar&colors[]=red&colors[]=green&colors[]=blue&colors[]=yellow&numbers[]=100&numbers[]=200&numbers[]=300&on=true&off=false&purple=haze' 112 | ] 113 | ].forEach((d) => { 114 | // @ts-ignore 115 | const str = qsEncode(d[0]); 116 | // @ts-ignore 117 | expect(str).toEqual(d[1]); 118 | }); 119 | }); 120 | }); 121 | 122 | describe('mergeObjects', () => { 123 | it('can merge objects', () => { 124 | [ 125 | [undefined, undefined, undefined], 126 | [ 127 | { 128 | foo: null 129 | }, 130 | { 131 | foo: null 132 | }, 133 | { 134 | foo: null 135 | } 136 | ], 137 | [ 138 | { 139 | foo: { bar: 3 }, 140 | array: [ 141 | { 142 | does: 'work', 143 | too: [1, 2, 3] 144 | } 145 | ] 146 | }, 147 | { 148 | foo: { baz: 4 }, 149 | quux: 5, 150 | array: [ 151 | { 152 | does: 'work', 153 | too: [4, 5, 6] 154 | }, 155 | { 156 | really: 'yes' 157 | } 158 | ], 159 | array2: ['bar'] 160 | }, 161 | { 162 | foo: { 163 | bar: 3, 164 | baz: 4 165 | }, 166 | array: [ 167 | { 168 | does: 'work', 169 | too: [1, 2, 3] 170 | }, 171 | { 172 | does: 'work', 173 | too: [4, 5, 6] 174 | }, 175 | { 176 | really: 'yes' 177 | } 178 | ], 179 | array2: ['bar'], 180 | quux: 5 181 | } 182 | ], 183 | [ 184 | { 185 | key1: 'value1', 186 | key3: 'value3' 187 | }, 188 | { 189 | key1: 'changed', 190 | key2: 'value2' 191 | }, 192 | { 193 | key1: 'changed', 194 | key2: 'value2', 195 | key3: 'value3' 196 | } 197 | ], 198 | [ 199 | { 200 | key1: { 201 | subkey1: 'value1', 202 | subkey2: 'value2' 203 | } 204 | }, 205 | { 206 | key1: { 207 | subkey1: 'changed', 208 | subkey3: 'added' 209 | } 210 | }, 211 | { 212 | key1: { 213 | subkey1: 'changed', 214 | subkey2: 'value2', 215 | subkey3: 'added' 216 | } 217 | } 218 | ], 219 | [ 220 | { 221 | key1: 'value1', 222 | key2: 'value2' 223 | }, 224 | { 225 | key1: { 226 | subkey1: 'subvalue1', 227 | subkey2: 'subvalue2' 228 | } 229 | }, 230 | { 231 | key1: { 232 | subkey1: 'subvalue1', 233 | subkey2: 'subvalue2' 234 | }, 235 | key2: 'value2' 236 | } 237 | ], 238 | [ 239 | { 240 | b: { 241 | c: {} 242 | } 243 | }, 244 | { 245 | a: {} 246 | }, 247 | { 248 | a: {}, 249 | b: { 250 | c: {} 251 | } 252 | } 253 | ], 254 | [ 255 | { 256 | a: { 257 | d: 'bar' 258 | } 259 | }, 260 | { 261 | b: { 262 | c: 'foo' 263 | } 264 | }, 265 | { 266 | a: { 267 | d: 'bar' 268 | }, 269 | b: { 270 | c: 'foo' 271 | } 272 | } 273 | ], 274 | [ 275 | { 276 | key1: { 277 | subkey1: 'subvalue1', 278 | subkey2: 'subvalue2' 279 | }, 280 | key2: 'value2' 281 | }, 282 | { 283 | key1: 'value1' 284 | }, 285 | { 286 | key1: 'value1', 287 | key2: 'value2' 288 | } 289 | ], 290 | [ 291 | { 292 | config: {} 293 | }, 294 | { 295 | config: { 296 | headers: { 297 | key1: 'value1' 298 | } 299 | } 300 | }, 301 | { 302 | config: { 303 | headers: { 304 | key1: 'value1' 305 | } 306 | } 307 | } 308 | ] 309 | ].forEach((d) => { 310 | // @ts-ignore 311 | const obj = mergeObjects(d[0], d[1]); 312 | // @ts-ignore 313 | expect(obj).toEqual(d[2]); 314 | }); 315 | }); 316 | }); 317 | -------------------------------------------------------------------------------- /docs/v2/readme.md: -------------------------------------------------------------------------------- 1 | # Yllet docs v2 2 | 3 | Yllet is a set of packages for the WordPress API for both React and non-React projects. The client is built on top of fetch, you can add your own transport by creating it, you can read more [here](#transport-layers). 4 | 5 | Yllet comes with [tree-shakable](https://webpack.js.org/guides/tree-shaking/) ESM or CJS builds, that fits into the modern ES6 JavaScript ecosystem. All code are tiny and well tested with both unit and browser tests. 6 | 7 | ## Installation 8 | 9 | To install the WordPress API client 10 | 11 | ``` 12 | npm install --save @yllet/client 13 | ``` 14 | 15 | To install the package with React bindings 16 | 17 | ``` 18 | npm install --save @yllet/react 19 | ``` 20 | 21 | ## Usage 22 | 23 | Fetch all posts 24 | 25 | ```js 26 | import Client from '@yllet/client'; 27 | 28 | const client = new Client({ 29 | endpoint: 'https://demo.wp-api.org/wp-json/' 30 | }); 31 | 32 | client 33 | .posts() 34 | .get() 35 | .then((res) => { 36 | console.log(res.data); 37 | }) 38 | .catch((err) => { 39 | console.log('Error: ' + err.message); 40 | }); 41 | ``` 42 | 43 | ## Client options 44 | 45 | ```js 46 | { 47 | // HTTP Headers 48 | headers: { 49 | 'Content-Type': 'application/json' 50 | }, 51 | 52 | // WordPress API endpoint. 53 | endpoint: '', 54 | 55 | // Default namespace. 56 | namespace: 'wp/v2' 57 | 58 | // WP Nonce, adds X-WP-Nonce header. 59 | nonce: '', 60 | 61 | // Restore default options when using endpoint, namespace and resource methods. 62 | restore: true, 63 | } 64 | ``` 65 | 66 | ## Resources 67 | 68 | Yllet client instance provides the following basic request methods 69 | 70 | - `client.categories()` 71 | - `client.comments()` 72 | - `client.media()` 73 | - `client.statuses()` 74 | - `client.posts()` 75 | - `client.pages()` 76 | - `client.search()` 77 | - `client.settings()` 78 | - `client.tags()` 79 | - `client.taxonomies()` 80 | - `client.types()` 81 | - `client.users()` 82 | 83 | Using any request methods sets the path so you don't have to write `client.get('posts/1')` each time instead you can just write `client.posts().get(1)` 84 | 85 | Adding custom request methods is easy (example [WooCommerce REST API](https://woocommerce.github.io/woocommerce-rest-api-docs/)) 86 | 87 | ```js 88 | // To call another endpoint, namespace and resource. 89 | client 90 | .endpoint('https://wp.com/wp-json') 91 | .namespace('wc/v2') 92 | .resource('products') 93 | .get() 94 | .then((res) => { 95 | console.log(res.data); 96 | }) 97 | .catch((err) => { 98 | console.log('Error: ' + err.message); 99 | }); 100 | 101 | // To store the client with new namespace, resource and endpoint. 102 | client.products = () => 103 | client 104 | .endpoint('https://wp.com/wp-json') 105 | .namespace('wc/v2') 106 | .resource('products'); 107 | 108 | // Then you can just call `client.products()` like you do with `client.posts()` 109 | client 110 | .products() 111 | .get() 112 | .then((res) => { 113 | console.log(res.data); 114 | }) 115 | .catch((err) => { 116 | console.log('Error: ' + err.message); 117 | }); 118 | ``` 119 | 120 | ## HTTP Methods 121 | 122 | Client instances also provide access to HTTP methods to access API resources. 123 | 124 | ```js 125 | // HTTP GET 126 | client.get(); 127 | 128 | // HTTP POST 129 | client.create(); 130 | 131 | // HTTP PATCH 132 | client.update(); 133 | 134 | // HTTP DELETE 135 | client.delete(); 136 | ``` 137 | 138 | Examples 139 | 140 | ```js 141 | // Create post. 142 | const post = client.posts().create({ 143 | title: 'Post title', 144 | content: '

    Post content

    ' 145 | }); 146 | 147 | // Update post. 148 | client.posts().update(post.id, { 149 | title: 'Post title 2' 150 | }); 151 | 152 | // Delete post. 153 | client.posts().delete(post.id); 154 | ``` 155 | 156 | ## Params 157 | 158 | You can pass a object with the same name as as the existing params. You can write `per_page` or `perPage` when the param contains a underscore. 159 | 160 | ```js 161 | client 162 | .posts() 163 | .get({ 164 | slug: 'hello-world', 165 | perPage: 1 // or per_page 166 | }) 167 | .then((res) => { 168 | console.log(res.data); 169 | }) 170 | .catch((err) => { 171 | console.log('Error: ' + err.message); 172 | }); 173 | ``` 174 | 175 | You can also set global params using key/value or object 176 | 177 | ```js 178 | client.param('source', 'yllet'); 179 | ``` 180 | 181 | All global params can be accessed with `client.params` 182 | 183 | ## Embed data 184 | 185 | WordPress API support embedding of resources and instead of having to provide `_embed=true` as a param on every request we can simpley use `embed()` before any request method. 186 | 187 | More about WordPress API embedding can you read [here](https://developer.wordpress.org/rest-api/using-the-rest-api/linking-and-embedding/#embedding). 188 | 189 | ```js 190 | client 191 | .posts() 192 | .embed() 193 | .get({ 194 | slug: 'hello-world' 195 | }) 196 | .then((res) => { 197 | console.log(res.data); 198 | }) 199 | .catch((err) => { 200 | console.log('Error: ' + err.message); 201 | }); 202 | ``` 203 | 204 | ## File uploading 205 | 206 | When Uploading a file you should use `client.file(file, [name])` to specify a file or a file buffer to attach to the request with a name (optional). 207 | 208 | Browser example: 209 | 210 | ```js 211 | const file = document.getElementById('my-file').files[0]; 212 | 213 | client 214 | .media() 215 | .file(file) 216 | .create({ 217 | title: 'Test image' 218 | }) 219 | .then((res) => { 220 | console.log(res.data); 221 | }) 222 | .catch((err) => { 223 | console.log('Error: ', err); 224 | }); 225 | ``` 226 | 227 | Node example: 228 | 229 | ```js 230 | import fs from 'fs'; 231 | 232 | client 233 | .media() 234 | .file(fs.createReadStream('me.jpg')) 235 | .create({ 236 | title: 'Test image' 237 | }) 238 | .then((res) => { 239 | console.log(res.data); 240 | }) 241 | .catch((err) => { 242 | console.log('Error: ', err); 243 | }); 244 | ``` 245 | 246 | ## Authentication 247 | 248 | Yllet version 2 offers an header based auth using the WordPress nonce. 249 | 250 | Adding a JavaScript object with `endpoint` and `nonce` 251 | 252 | ```php 253 | wp_localize_script( 'wp-api', 'wpApiSettings', array( 254 | 'endpoint' => esc_url_raw( rest_url() ), 255 | 'nonce' => wp_create_nonce( 'wp_rest' ) 256 | ) ); 257 | ``` 258 | 259 | The client will add `X-WP-Nonce` header when adding `nonce` option. 260 | 261 | ```js 262 | import Client from '@yllet/client'; 263 | 264 | const client = new Client({ 265 | endpoint: window.wpApiSettings.endpoint, 266 | nonce: window.wpApiSettings.nonce 267 | }); 268 | ``` 269 | 270 | ## Discover the REST API from a URL 271 | 272 | ```js 273 | import Client from '@yllet/client'; 274 | 275 | Client.discover('http://demo.wp-api.org/') 276 | .then((url) => { 277 | console.log(url); 278 | }) 279 | .catch((err) => { 280 | console.log('Error: ', err); 281 | }); 282 | ``` 283 | 284 | ## Middlewares 285 | 286 | Yllet has middlewares since 2.1.0 and can modify transport, add headers, change options or something else. All middlewares runs before the request. It's important to run the `next` function to run the next middleware or the actual request. 287 | 288 | Examples 289 | 290 | ```js 291 | client.use((client, next) => { 292 | client.transport = new CustomTransport(); 293 | next(); 294 | }); 295 | 296 | client.use((client, next) => { 297 | client.header('X-Foo', 'Bar'); 298 | next(); 299 | }); 300 | 301 | client.use((client, next) => { 302 | setTimeout(next, 1000); 303 | }); 304 | 305 | client.use(async (client, next) => { 306 | await something(); 307 | return next(); 308 | }); 309 | ``` 310 | 311 | ## Transport layers 312 | 313 | Yllet supports different transport layers so you can use your own favorite HTTP client, e.g axios. 314 | 315 | ### Fetch 316 | 317 | To support older browsers we recommend to use [isomorphic-unfetch](https://github.com/developit/unfetch/tree/master/packages/isomorphic-unfetch), a minimal polyfill for fetch which allows for usage in both client and server settings. 318 | 319 | Installation 320 | 321 | ``` 322 | npm install --save @yllet/client isomorphic-unfetch 323 | ``` 324 | 325 | Usage 326 | 327 | ```js 328 | import 'isomorphic-unfetch'; 329 | import Client from '@yllet/client'; 330 | 331 | const client = new Client({ 332 | endpoint: 'https://demo.wp-api.org/wp-json' 333 | }); 334 | ``` 335 | 336 | ### Others 337 | 338 | To add a custom transport to the client you just pass a transport class. Yllet don't offer any other transports than fetch right now, but it's kind of easy to build one. 339 | 340 | You can find examples of transports [here](../../examples/transports) 341 | 342 | To use another transport class, just pass it in as a option 343 | 344 | ```js 345 | const client = new Client({ 346 | endpoint: 'https://demo.wp-api.org/wp-json/', 347 | transport: new AxiosTransport() 348 | }); 349 | ``` 350 | 351 | ## React bindings 352 | 353 | To install the package with React bindings 354 | 355 | ``` 356 | npm install --save @yllet/react 357 | ``` 358 | 359 | ### Provider component 360 | 361 | Yllets React package contains a provider component that you can use like this 362 | 363 | ```js 364 | import React from 'react'; 365 | import ReactDOM from 'react-dom'; 366 | import App from './App'; 367 | 368 | import Client from '@yllet/client'; 369 | import { Provider } from '@yllet/react'; 370 | 371 | const client = new Client({ 372 | endpoint: 'https://demo.wp-api.org/wp-json/' 373 | }); 374 | 375 | ReactDOM.render( 376 | 377 | 378 | , 379 | document.getElementById('root') 380 | ); 381 | ``` 382 | 383 | In your application component you can use the `withClient` component enhancer to pass along the `client` to your component. 384 | 385 | ```js 386 | import React from 'react'; 387 | import { withClient } from '@yllet/react'; 388 | 389 | class App extends React.Component {} 390 | 391 | export default withClient(App); 392 | ``` 393 | 394 | Then you can use `this.props.client` the same way as if it was a standalone client. 395 | 396 | You can also use `withClientData` to pass the response data or the error from WordPress REST API. 397 | 398 | ```js 399 | import React from 'react'; 400 | import { withClientData } from '@yllet/react'; 401 | 402 | class Post extends React.Component {} 403 | 404 | export default withClientData((client, props) => { 405 | return client.posts().get(1); 406 | })(Post); 407 | ``` 408 | 409 | Then you can use `this.props.data` or `this.props.error` and `this.props.loading` 410 | 411 | ### Hooks 412 | 413 | In version 2 we also add a hook to create a client 414 | 415 | ```js 416 | import React, { useEffect, useState } from 'react'; 417 | import { useClient } from '@yllet/react'; 418 | 419 | function App() { 420 | const endpoint = 'https://demo.wp-api.org/wp-json/'; 421 | const client = useClient({ endpoint }); 422 | const [posts, setPosts] = useState([]); 423 | 424 | useEffect(() => { 425 | const fetchPosts = async () => { 426 | const response = await client.posts().get(); 427 | setPosts(response); 428 | }; 429 | fetchPosts(); 430 | }, [client]); 431 | 432 | return ( 433 |
    434 |

    Posts

    435 |
      436 | {posts.map((p, pi) => ( 437 |
    • {p.title.rendered}
    • 438 | ))} 439 |
    440 |
    441 | ); 442 | } 443 | 444 | export default App; 445 | ``` 446 | -------------------------------------------------------------------------------- /packages/client/src/Client.ts: -------------------------------------------------------------------------------- 1 | import FormData from 'isomorphic-form-data'; 2 | import { mergeObjects, isObject, objectKeysToSnakeCase } from '@yllet/support'; 3 | import Transport from './Transport'; 4 | import type { Options, Middleware, Params } from './Client.types'; 5 | 6 | class Client { 7 | /** 8 | * Request config. 9 | * 10 | * @var {object} 11 | */ 12 | config: Record = {}; 13 | 14 | /** 15 | * File attachment. 16 | * 17 | * @var {FormData} 18 | */ 19 | formData: any = undefined; 20 | 21 | /** 22 | * Request middlewares. 23 | * 24 | * @var {array} 25 | */ 26 | middlewares: Middleware[] = []; 27 | 28 | /** 29 | * Client options. 30 | * 31 | * @var {object} 32 | */ 33 | options: Options = { 34 | config: {}, 35 | endpoint: '', 36 | headers: { 37 | 'Content-Type': 'application/json', 38 | }, 39 | namespace: 'wp/v2', 40 | nonce: '', 41 | resource: '', 42 | restore: true, 43 | }; 44 | 45 | /** 46 | * Request params. 47 | * 48 | * @var {object} 49 | */ 50 | params: Params = {}; 51 | 52 | /** 53 | * Transport layer. 54 | * 55 | * @var {Transport} 56 | */ 57 | transport = new Transport(); 58 | 59 | /** 60 | * Client constructor. 61 | * 62 | * @param {string|object} options 63 | */ 64 | constructor(options: string | Options = {}) { 65 | if (typeof options === 'string') { 66 | options = { 67 | endpoint: options, 68 | }; 69 | } 70 | 71 | this._setupOptions(options); 72 | } 73 | 74 | /** 75 | * Returns real options object. 76 | * 77 | * @param {object} options 78 | * 79 | * @return {object} 80 | */ 81 | _setupOptions(options: Options = {}) { 82 | if (!isObject(options)) { 83 | options = this.options; 84 | } 85 | 86 | if (!isObject(options.config)) { 87 | options.config = { 88 | headers: {}, 89 | }; 90 | } 91 | 92 | // Set middlewares. 93 | this.middlewares = Array.isArray(options.middlewares) ? options.middlewares : []; 94 | delete options.middlewares; 95 | 96 | // Set transport. 97 | this.transport = options.transport ? options.transport : this.transport; 98 | delete options.transport; 99 | 100 | // Merge headers and create config object. 101 | const headers = mergeObjects( 102 | options.config.headers, 103 | mergeObjects(this.options.headers, options.headers || {}), 104 | ); 105 | options.config = { ...options.config, headers: { ...headers } }; 106 | 107 | // Add nonce if any. 108 | if (options.nonce) { 109 | options.config.headers['X-WP-Nonce'] = options.nonce; 110 | } 111 | 112 | // Merge options. 113 | options = mergeObjects(this.options, options); 114 | 115 | // Delete headers since it's in the config object. 116 | delete options.headers; 117 | 118 | this.options = options; 119 | } 120 | 121 | /** 122 | * Returns full request url. 123 | * 124 | * @param {string} path 125 | * 126 | * @return {string} 127 | */ 128 | _getUrl(path: string): string { 129 | const { endpoint, namespace, resource } = this.options; 130 | 131 | const safePath = String(path || ''); 132 | const safeEndpoint = endpoint.replace(namespace, '').replace(/\/$/, '') + '/'; 133 | const safeResource = resource.replace(/^\/|\/$/g, ''); 134 | const safeNamespace = namespace.replace(/^\/|\/$/g, '') + (safeResource || safePath ? '/' : ''); 135 | 136 | return ( 137 | safeEndpoint + safeNamespace + safeResource + (safeResource && safePath ? '/' : '') + safePath 138 | ); 139 | } 140 | 141 | /** 142 | * Returns request params. 143 | * 144 | * @param {object} params 145 | * 146 | * @return {object} 147 | */ 148 | _getParams(params: Params): Params { 149 | let merged: Params; 150 | params = isObject(params) ? objectKeysToSnakeCase(params) : {}; 151 | merged = { ...this.params, ...params }; 152 | 153 | if (this.formData instanceof FormData) { 154 | Object.keys(merged).forEach((key) => { 155 | this.formData.append(key, merged[key]); 156 | }); 157 | merged = this.formData; 158 | } 159 | 160 | return merged; 161 | } 162 | 163 | /** 164 | * Run middlewares. 165 | * 166 | * @param {function} last 167 | * 168 | * @return {function} 169 | */ 170 | async _runMiddlewares(last: Middleware) { 171 | // eslint-disable-next-line @typescript-eslint/no-this-alias 172 | const self = this; 173 | const { endpoint, namespace, resource } = this.options; 174 | const next = async () => { 175 | const middleware = self.middlewares.shift(); 176 | 177 | self.options = { 178 | ...self.options, 179 | namespace, 180 | resource, 181 | endpoint, 182 | }; 183 | 184 | if (!middleware) { 185 | return await last.call(this, self, next); 186 | } 187 | 188 | if (typeof middleware === 'function') { 189 | await middleware.call(this, self, next); 190 | } 191 | 192 | return self; 193 | }; 194 | 195 | return await next(); 196 | } 197 | 198 | /** 199 | * Discover the REST API from a URL. 200 | * 201 | * @param {string} url 202 | * 203 | * @return {Promise} 204 | */ 205 | discover(url: string): Promise { 206 | return this.transport 207 | .get(url, { 208 | rest_route: '/', 209 | }) 210 | .then((response: Record) => { 211 | if (isObject(response.routes)) { 212 | return response.routes['/']._links.self; 213 | } 214 | 215 | throw new Error('Unable to find the REST API'); 216 | }); 217 | } 218 | 219 | /** 220 | * Enable embed mode. 221 | * 222 | * @return {Client} 223 | */ 224 | embed() { 225 | return this.param('_embed', true); 226 | } 227 | 228 | /** 229 | * Sets the endpoint. 230 | * 231 | * @param {string} endpoint 232 | * 233 | * @return {Client} 234 | */ 235 | endpoint(endpoint: string) { 236 | this.options.endpoint = endpoint; 237 | return this; 238 | } 239 | 240 | /** 241 | * 242 | * Specify a file or a file buffer to attach to the request with a name (optional). 243 | * 244 | * @param {string} file 245 | * @param {string} name 246 | * 247 | * @return {Client} 248 | */ 249 | file(file: any, name = '') { 250 | const formData = new FormData(); 251 | formData.append('file', file); 252 | 253 | this.header('Content-Type', 'multipart/form-data'); 254 | this.header('Content-Disposition', `attachment; filename=${name}`); 255 | 256 | this.formData = formData; 257 | 258 | return this; 259 | } 260 | 261 | /** 262 | * Set a single header or headers object. 263 | * 264 | * @param {object|string} headers 265 | * @param {string} value 266 | * 267 | * @return {Client|string} 268 | */ 269 | header(key: string | Params, value: any = null) { 270 | let { headers = {} } = this.options.config; 271 | 272 | if (typeof key === 'string' && !value) { 273 | return headers[key]; 274 | } 275 | 276 | if (typeof key === 'string') { 277 | headers[key] = value; 278 | } else { 279 | headers = { ...headers, ...key }; 280 | } 281 | 282 | this.options.config = { ...this.options.config, headers: { ...headers } }; 283 | 284 | return this; 285 | } 286 | 287 | /** 288 | * Modify namespace that is used. 289 | * 290 | * @param {string} namespace 291 | * 292 | * @return {Client} 293 | */ 294 | namespace(namespace: string) { 295 | this.options.namespace = namespace; 296 | return this; 297 | } 298 | 299 | /** 300 | * Get the categories resource. 301 | * 302 | * @return {Client} 303 | */ 304 | categories() { 305 | return this.resource('categories'); 306 | } 307 | 308 | /** 309 | * Get the comments resource. 310 | * 311 | * @return {Client} 312 | */ 313 | comments() { 314 | return this.resource('comments'); 315 | } 316 | 317 | /** 318 | * Get the media resource. 319 | * 320 | * @return {Client} 321 | */ 322 | media() { 323 | return this.resource('media'); 324 | } 325 | 326 | /** 327 | * Get the statuses resource. 328 | * 329 | * @return {Client} 330 | */ 331 | statuses() { 332 | return this.resource('statuses'); 333 | } 334 | 335 | /** 336 | * Get the pages resource. 337 | * 338 | * @return {Client} 339 | */ 340 | pages() { 341 | return this.resource('pages'); 342 | } 343 | 344 | /** 345 | * Get the posts resource. 346 | * 347 | * @return {Client} 348 | */ 349 | posts() { 350 | return this.resource('posts'); 351 | } 352 | 353 | /** 354 | * Get the settings resource. 355 | * 356 | * @return {Client} 357 | */ 358 | settings() { 359 | return this.resource('settings'); 360 | } 361 | 362 | /** 363 | * Get the tags resource. 364 | * 365 | * @return {Client} 366 | */ 367 | tags() { 368 | return this.resource('tags'); 369 | } 370 | 371 | /** 372 | * Get the taxonomies resource. 373 | * 374 | * @return {Client} 375 | */ 376 | taxonomies() { 377 | return this.resource('taxonomies'); 378 | } 379 | 380 | /** 381 | * Get the types resource. 382 | * 383 | * @return {Client} 384 | */ 385 | types() { 386 | return this.resource('types'); 387 | } 388 | 389 | /** 390 | * Get the users resource. 391 | * 392 | * @return {Client} 393 | */ 394 | users() { 395 | return this.resource('users'); 396 | } 397 | 398 | /** 399 | * Get the search resource. 400 | * 401 | * @return {Client} 402 | */ 403 | search() { 404 | return this.resource('search'); 405 | } 406 | 407 | /** 408 | * Sets the resource path. 409 | * 410 | * @param {string} resource 411 | * 412 | * @return {Client} 413 | */ 414 | resource(resource: string) { 415 | this.options.resource = resource; 416 | return this; 417 | } 418 | 419 | /** 420 | * Set/Get global param. 421 | * 422 | * @param {string|object} key 423 | * @param {object} value 424 | * 425 | * @return {Client|object} 426 | */ 427 | param(key: string | Params, value: any = null) { 428 | if (typeof key === 'string' && !value) { 429 | return this.params[key]; 430 | } 431 | 432 | if (typeof key === 'string') { 433 | this.params[key] = value; 434 | } else { 435 | this.params = { ...this.params, ...key }; 436 | } 437 | 438 | return this; 439 | } 440 | 441 | /** 442 | * Fetch content by slug. 443 | * 444 | * @param {string} slug 445 | * @param {object} params 446 | * 447 | * @return {object} 448 | */ 449 | slug(slug: string, params: Params = {}) { 450 | return this.request('get', { 451 | ...params, 452 | per_page: 1, 453 | slug, 454 | }).then((res) => res[0]); 455 | } 456 | 457 | /** 458 | * Send GET request. 459 | * 460 | * @param {string} path 461 | * @param {object} params 462 | * 463 | * @return {Promise} 464 | */ 465 | get(path = '', params: Params = {}): Promise { 466 | return this.request('get', path, params); 467 | } 468 | 469 | /** 470 | * Send Create/POST request. 471 | * 472 | * @param {string} path 473 | * @param {object} params 474 | * 475 | * @return {Promise} 476 | */ 477 | create(path = '', params: Params = {}): Promise { 478 | return this.request('post', path, params); 479 | } 480 | 481 | /** 482 | * Send Update/PUT request. 483 | * 484 | * @param {string} path 485 | * @param {object} params 486 | * 487 | * @return {Promise} 488 | */ 489 | update(path = '', params: Params = {}): Promise { 490 | return this.request('put', path, params); 491 | } 492 | 493 | /** 494 | * Send Delete request. 495 | * 496 | * @param {string} path 497 | * @param {object} params 498 | * 499 | * @return {Promise} 500 | */ 501 | delete(path = '', params: Params = {}): Promise { 502 | return this.request('delete', path, params); 503 | } 504 | 505 | /** 506 | * Send API request 507 | * 508 | * @param {string} verb 509 | * @param {string} path 510 | * @param {object} params 511 | * 512 | * @return {Promise} 513 | */ 514 | request(verb: string, path: string | Params = '', params: Params = {}): Promise { 515 | if (isObject(path)) { 516 | params = path as any; 517 | path = ''; 518 | } 519 | 520 | return new Promise((resolve) => { 521 | this._runMiddlewares(() => { 522 | const response = this.transport[verb.toLowerCase()]( 523 | this._getUrl(path as string), 524 | this._getParams(params), 525 | this.options.config, 526 | ); 527 | 528 | resolve(response); 529 | }); 530 | }); 531 | } 532 | 533 | /** 534 | * Add middlewares that should be runned before request. 535 | * 536 | * @param {array|function} fn 537 | */ 538 | use(fn: Middleware[] | Middleware) { 539 | if (!Array.isArray(fn)) { 540 | fn = [fn]; 541 | } 542 | 543 | this.middlewares = this.middlewares.concat(fn); 544 | 545 | return this; 546 | } 547 | } 548 | 549 | export default Client; 550 | -------------------------------------------------------------------------------- /packages/client/tsconfig.tsbuildinfo: -------------------------------------------------------------------------------- 1 | {"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/ts3.6/base.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/base.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/form-data/index.d.ts","../../node_modules/@types/isomorphic-form-data/index.d.ts","./src/transport.ts","./src/index.types.ts","./src/index.ts","../../node_modules/@types/aria-query/index.d.ts","../../node_modules/@babel/types/lib/index.d.ts","../../node_modules/@types/babel__generator/index.d.ts","../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/@types/babel__template/index.d.ts","../../node_modules/@types/babel__traverse/index.d.ts","../../node_modules/@types/babel__core/index.d.ts","../../node_modules/@types/graceful-fs/index.d.ts","../../node_modules/@types/invariant/index.d.ts","../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../node_modules/@types/istanbul-lib-report/index.d.ts","../../node_modules/@types/istanbul-reports/index.d.ts","../../node_modules/jest-diff/build/cleanupsemantic.d.ts","../../node_modules/pretty-format/build/types.d.ts","../../node_modules/pretty-format/build/index.d.ts","../../node_modules/jest-diff/build/types.d.ts","../../node_modules/jest-diff/build/difflines.d.ts","../../node_modules/jest-diff/build/printdiffs.d.ts","../../node_modules/jest-diff/build/index.d.ts","../../node_modules/@types/jest/index.d.ts","../../node_modules/@types/json5/index.d.ts","../../node_modules/@types/minimatch/index.d.ts","../../node_modules/@types/minimist/index.d.ts","../../node_modules/@types/normalize-package-data/index.d.ts","../../node_modules/@types/parse-json/index.d.ts","../../node_modules/@types/prettier/index.d.ts","../../node_modules/@types/prop-types/index.d.ts","../../node_modules/@types/react/global.d.ts","../../node_modules/csstype/index.d.ts","../../node_modules/@types/scheduler/tracing.d.ts","../../node_modules/@types/react/index.d.ts","../../node_modules/@types/react-dom/index.d.ts","../../node_modules/@types/react-test-renderer/index.d.ts","../../node_modules/@types/scheduler/index.d.ts","../../node_modules/@types/stack-utils/index.d.ts","../../node_modules/@types/testing-library__jest-dom/index.d.ts","../../node_modules/@types/yargs-parser/index.d.ts","../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"6adbf5efd0e374ff5f427a4f26a5a413e9734eee5067a0e86da69aea41910b52","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06",{"version":"abba1071bfd89e55e88a054b0c851ea3e8a494c340d0f3fab19eb18f6afb0c9e","affectsGlobalScope":true},{"version":"d8996609230d17e90484a2dd58f22668f9a05a3bfe00bfb1d6271171e54a31fb","affectsGlobalScope":true},{"version":"43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"4378fc8122ec9d1a685b01eb66c46f62aba6b239ca7228bb6483bcf8259ee493","affectsGlobalScope":true},{"version":"0d5f52b3174bee6edb81260ebcd792692c32c81fd55499d69531496f3f2b25e7","affectsGlobalScope":true},{"version":"810627a82ac06fb5166da5ada4159c4ec11978dfbb0805fe804c86406dab8357","affectsGlobalScope":true},{"version":"62d80405c46c3f4c527ee657ae9d43fda65a0bf582292429aea1e69144a522a6","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"75ec0bdd727d887f1b79ed6619412ea72ba3c81d92d0787ccb64bab18d261f14","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"1b3fe904465430e030c93239a348f05e1be80640d91f2f004c3512c2c2c89f34","affectsGlobalScope":true},{"version":"10bbdc1981b8d9310ee75bfac28ee0477bb2353e8529da8cff7cb26c409cb5e8","affectsGlobalScope":true},{"version":"25b4a0c4fab47c373ee49df4c239826ee3430019fc0c1b5e59edc3e398b7468d","affectsGlobalScope":true},"d20f08527645f62facb2d66c2b7bd31ea964b59c897d00bddb1efe8c13890b72","5726b5ce952dc5beaeb08d5f64236632501568a54a390363d2339ba1dc5393b1","674bedbfd2004e233e2a266a3d2286e524f0d58787a98522d834d6ccda1d215a","714637d594e1a38a075091fe464ca91c6abc0b154784b4287f6883200e28ccef",{"version":"23edba5f47d3409810c563fe8034ae2c59e718e1ef8570f4152ccdde1915a096","affectsGlobalScope":true},"0e9c55f894ca2d9cf63b5b0d43a8cec1772dd560233fd16275bc7a485eb82f83","d53b352a01645c470a0d8c31bf290ba791fc28ade0ce187a4a50f5c2f826f75e","5f0a09de75bd965c21dc6d73671ba88830272f9ed62897bb0aa9754b369b1eed","2b34e7fcba9e1f24e7f54ba5c8be5a8895b0b8b444ccf6548e04acdee0899317",{"version":"06d2be99c3dd2ff52114d02ee443ba486ab482423df1941d3c97d6a92e924d70","affectsGlobalScope":true},{"version":"bfd4f140c07091b5e8a963c89e6fa3f44b6cfcbc11471b465cf63e2d020ad0eb","affectsGlobalScope":true},"a106a0bea088b70879ac88ff606dc253c0cc474ea05ad3a282b8bfb1091ae576","c98ce957db9eebd75f53edda3f6893e05ab2d2283b5667b18e31bcdb6427ed10","1f08bd8305d4a789a68f71ab622156dfff993aa51a2aa58b9ccf166cc6f9fcf7","9aff68f1b847b846d3d50a58c9f8f99389bedd0258d1b1c201f11b97ecfd36f8","1978992206803f5761e99e893d93b25abc818c5fe619674fdf2ae02b29f641ba","05fbe81f09fc455a2c343d2458d2b3c600c90b92b22926be765ee79326be9466","8e7d6dae9e19bbe47600dcfd4418db85b30ae7351474ea0aad5e628f9845d340","f20ea392f7f27feb7a90e5a24319a4e365b07bf83c39a547711fe7ff9df68657","32542c4660ecda892a333a533feedba31738ee538ef6a78eb73af647137bc3fc","0ecacea5047d1a7d350e7049dbd22f26435be5e8736a81a56afec5b3264db1ca","ffcb4ebde21f83370ed402583888b28651d2eb7f05bfec9482eb46d82adedd7f",{"version":"06c004006016a51c4d1855527a523562c329dc44c473931c65f10373281f730e","affectsGlobalScope":true},"a7b43c69f9602d198825e403ee34e5d64f83c48b391b2897e8c0e6f72bca35f8","f4a3fc4efc6944e7b7bd4ccfa45e0df68b6359808e6cf9d061f04fd964a7b2d3","73cad675aead7a2c05cf934e7e700c61d84b2037ac1d576c3f751199b25331da","8c3137ba3583ec18484429ec1c8eff89efdc42730542f157b38b102fdccc0c71","d84300d886b45a198c346158e4ff7ae361cc7bc1c3deab44afb3db7de56b5d25","94ca7beec4e274d32362b54e0133152f7b4be9487db7b005070c03880b6363aa","2d713cbcbd5bcc38d91546eaeea7bb1c8686dc4a2995a28556d957b1b9de11d9","bbf21f210782db4193359010a4710786add43e3b50aa42fc0d371f45b4e4d8d3","0b7733d83619ac4e3963e2a9f7c75dc1e9af6850cb2354c9554977813092c10a","3ce933f0c3955f67f67eb7d6b5c83c2c54a18472c1d6f2bb651e51dd40c84837","631e96db896d645f7132c488ad34a16d71fd2be9f44696f8c98289ee1c8cbfa9","2c77230d381cba81eb6f87cda2fbfff6c0427c6546c2e2590110effff37c58f7","da86ee9a2f09a4583db1d5e37815894967e1f694ad9f3c25e84e0e4d40411e14","141a943e5690105898a67537a470f70b56d0e183441b56051d929e902376b7b2","ddc086b1adac44e2fccf55422da1e90fa970e659d77f99712422a421564b4877","515ef1d99036ff0dafa5bf738e02222edea94e0d97a0aa0ff277ac5e96b57977",{"version":"2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1","affectsGlobalScope":true},"780058f4a804c8bdcdd2f60e7af64b2bc57d149c1586ee3db732a84d659a50bf","ae68a04912ee5a0f589276f9ec60b095f8c40d48128a4575b3fdd7d93806931c","19d580a3b42ad5caeaee266ae958260e23f2df0549ee201c886c8bd7a4f01d4e","e61a21e9418f279bc480394a94d1581b2dee73747adcbdef999b6737e34d721b","9c4c395e927045b324877acdc4bfb95f128f36bc9f073266a2f0342495075a4f","e91ad231af87f864b3f07cd0e39b1cf6c133988156f087c1c3ccb0a5491c9115","4ebd5321ca0246abcf379398077a0886b8f0e324dabd4d7aac4dfb69dc83dad2","f7250885f826e127b44ec9fee8201049f529f5199ed3ffff5f3136b337c244d8","9c160fccfe02c9cbfb087b2bf43a33a2aee1379c4b07fc262f9c35d986719d87","99f7128e39d438d12ed8f15d78128559e63d1ade552b3afcb215149aaa0d7e0e","5024433f8da3a7968f6d12cffd32f2cefae4442a9ad1c965fa2d23342338b700","272c2dac4baaf7fdd2d7efeef0fa2547af54cc21883c5e138b8c4d1661697a54","b25c5f2970d06c729f464c0aeaa64b1a5b5f1355aa93554bb5f9c199b8624b1e","64b867c61effed7b5bc0cc06b3d8eac23b067a3fba581fc7d3c292fa593e6a45","3051751533eee92572241b3cef28333212401408c4e7aa21718714b793c0f4ed","691aea9772797ca98334eb743e7686e29325b02c6931391bcee4cc7bf27a9f3b","d0b0a00cf31968a33baeaadf974ce4e5e7edf58cea5288765293f41ba5e72b3a","3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba","6acf8a984c2728906edeba2098c5c51f44a4520bc4a48c65b973bd785537178f","de18acda71730bac52f4b256ce7511bb56cc21f6f114c59c46782eff2f632857","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","905c3e8f7ddaa6c391b60c05b2f4c3931d7127ad717a080359db3df510b7bdab","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","462bccdf75fcafc1ae8c30400c9425e1a4681db5d605d1a0edb4f990a54d8094","5923d8facbac6ecf7c84739a5c701a57af94a6f6648d6229a6c768cf28f0f8cb","7adecb2c3238794c378d336a8182d4c3dd2c4fa6fa1785e2797a3db550edea62","dc12dc0e5aa06f4e1a7692149b78f89116af823b9e1f1e4eae140cd3e0e674e6","1bfc6565b90c8771615cd8cfcf9b36efc0275e5e83ac7d9181307e96eb495161","8a8a96898906f065f296665e411f51010b51372fa260d5373bf9f64356703190",{"version":"e9f2cdc4e98e73a606ff68c470a8cb4f23cd638c47649d71b90a2d9413102080","affectsGlobalScope":true},"96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","95c22bc19835e28e2e524a4bb8898eb5f2107b640d7279a6d3aade261916bbf2","e437d83044ba17246a861aa9691aa14223ff4a9d6f338ab1269c41c758586a88","c9ad058b2cc9ce6dc2ed92960d6d009e8c04bef46d3f5312283debca6869f613","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","9d9e658d1d5b805562749ce383ef8c67ccb796394d8734d9c138788d7dab6ee3","f7b46d22a307739c145e5fddf537818038fdfffd580d79ed717f4d4d37249380",{"version":"ecf78e637f710f340ec08d5d92b3f31b134a46a4fcf2e758690d8c46ce62cba6","affectsGlobalScope":true},"381899b8d1d4c1be716f18cb5242ba39f66f4b1e31d45af62a32a99f8edcb39d","f5a8b384f182b3851cec3596ccc96cb7464f8d3469f48c74bf2befb782a19de5",{"version":"1bc82f5b3bb93df76d19730c84467b0b346187198537135d63a672956f323720","affectsGlobalScope":true},"45a63e17814c570ea59407f231ef9c561510bd6edb36f17479b09b44619496c6","60aaac5fb1858fbd4c4eb40e01706eb227eed9eca5c665564bd146971280dbd3","74b0245c42990ed8a849df955db3f4362c81b13f799ebc981b7bec2d5b414a57","c6c4fea9acc55d5e38ff2b70d57ab0b5cdbd08f8bc5d7a226e322cea128c5b57",{"version":"dd4d769260a2b8cf19f0d2457f3142b24f083237932b0d1edb5fd728a989c8bd","affectsGlobalScope":true},"3bdd93ec24853e61bfa4c63ebaa425ff3e474156e87a47d90122e1d8cc717c1f","6ba73232c9d3267ca36ddb83e335d474d2c0e167481e3dec416c782894e11438"],"options":{"allowSyntheticDefaultImports":true,"composite":true,"declaration":true,"emitDeclarationOnly":false,"emitDecoratorMetadata":true,"esModuleInterop":true,"experimentalDecorators":true,"noImplicitReturns":true,"noUnusedLocals":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":true,"suppressImplicitAnyIndexErrors":true,"target":1},"fileIdsList":[[81],[81,82,83,84,85],[81,83],[41,74],[75],[89],[90],[94,98],[72],[30],[71,72],[31],[32,40,41,48,57],[32,33,40,48],[64],[35,36,41,49],[36,57],[37,38,40,48],[38],[39,40],[40],[40,41,42,57,63],[41,42],[43,48,57,63],[40,41,43,44,48,57,60,63],[43,45,60,63],[73],[40,46],[47,63],[38,40,48,57],[49],[50],[30,51],[62],[53],[54],[40,55],[55,56,64,66],[40,57],[58],[59],[48,60],[61],[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70],[48,62],[54,63],[57,65],[66],[70],[40,42,57,63,66,67],[57,68],[110],[106,107,108,109],[99],[116],[43,57,74],[92,95],[92,95,96,97],[94],[93],[76,77,78],[79],[76]],"referencedMap":[[83,1],[86,2],[82,1],[84,3],[85,1],[87,4],[76,5],[90,6],[91,7],[99,8],[72,9],[30,10],[73,11],[31,12],[32,13],[33,14],[34,15],[35,16],[36,17],[37,18],[38,19],[39,20],[40,21],[41,22],[42,23],[43,24],[44,25],[45,26],[74,27],[46,28],[47,29],[48,30],[49,31],[50,32],[51,33],[52,34],[53,35],[54,36],[55,37],[56,38],[57,39],[58,40],[59,41],[60,42],[61,43],[71,44],[62,45],[63,46],[64,15],[65,47],[66,48],[70,49],[67,50],[68,51],[111,52],[112,52],[110,53],[115,54],[117,55],[75,56],[96,57],[98,58],[97,57],[95,59],[94,60],[79,61],[78,62],[77,63]],"exportedModulesMap":[[83,1],[86,2],[82,1],[84,3],[85,1],[87,4],[76,5],[90,6],[91,7],[99,8],[72,9],[30,10],[73,11],[31,12],[32,13],[33,14],[34,15],[35,16],[36,17],[37,18],[38,19],[39,20],[40,21],[41,22],[42,23],[43,24],[44,25],[45,26],[74,27],[46,28],[47,29],[48,30],[49,31],[50,32],[51,33],[52,34],[53,35],[54,36],[55,37],[56,38],[57,39],[58,40],[59,41],[60,42],[61,43],[71,44],[62,45],[63,46],[64,15],[65,47],[66,48],[70,49],[67,50],[68,51],[111,52],[112,52],[110,53],[115,54],[117,55],[75,56],[96,57],[98,58],[97,57],[95,59],[94,60],[79,61],[78,62],[77,63]],"semanticDiagnosticsPerFile":[83,81,80,86,82,84,85,87,88,76,89,90,91,99,100,101,102,72,30,73,31,32,33,34,35,36,37,38,39,40,41,42,29,69,43,44,45,74,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,71,62,63,64,65,66,70,67,68,103,104,105,106,111,112,107,110,113,109,114,115,116,117,108,75,92,96,98,97,95,94,93,6,8,7,2,9,10,11,12,13,14,15,16,3,4,20,17,18,19,21,22,23,5,24,25,26,27,1,28,79,78,77]},"version":"4.5.2"} 2 | -------------------------------------------------------------------------------- /examples/next/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-example", 3 | "version": "0.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "next-example", 9 | "version": "0.1.0", 10 | "dependencies": { 11 | "@yllet/react": "^3.0.1", 12 | "next": "^12.1.0", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2" 15 | } 16 | }, 17 | "node_modules/@next/env": { 18 | "version": "12.1.0", 19 | "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz", 20 | "integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==" 21 | }, 22 | "node_modules/@next/swc-android-arm64": { 23 | "version": "12.1.0", 24 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz", 25 | "integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==", 26 | "cpu": [ 27 | "arm64" 28 | ], 29 | "optional": true, 30 | "os": [ 31 | "android" 32 | ], 33 | "engines": { 34 | "node": ">= 10" 35 | } 36 | }, 37 | "node_modules/@next/swc-darwin-arm64": { 38 | "version": "12.1.0", 39 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz", 40 | "integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==", 41 | "cpu": [ 42 | "arm64" 43 | ], 44 | "optional": true, 45 | "os": [ 46 | "darwin" 47 | ], 48 | "engines": { 49 | "node": ">= 10" 50 | } 51 | }, 52 | "node_modules/@next/swc-darwin-x64": { 53 | "version": "12.1.0", 54 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz", 55 | "integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==", 56 | "cpu": [ 57 | "x64" 58 | ], 59 | "optional": true, 60 | "os": [ 61 | "darwin" 62 | ], 63 | "engines": { 64 | "node": ">= 10" 65 | } 66 | }, 67 | "node_modules/@next/swc-linux-arm-gnueabihf": { 68 | "version": "12.1.0", 69 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz", 70 | "integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==", 71 | "cpu": [ 72 | "arm" 73 | ], 74 | "optional": true, 75 | "os": [ 76 | "linux" 77 | ], 78 | "engines": { 79 | "node": ">= 10" 80 | } 81 | }, 82 | "node_modules/@next/swc-linux-arm64-gnu": { 83 | "version": "12.1.0", 84 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz", 85 | "integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==", 86 | "cpu": [ 87 | "arm64" 88 | ], 89 | "optional": true, 90 | "os": [ 91 | "linux" 92 | ], 93 | "engines": { 94 | "node": ">= 10" 95 | } 96 | }, 97 | "node_modules/@next/swc-linux-arm64-musl": { 98 | "version": "12.1.0", 99 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz", 100 | "integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==", 101 | "cpu": [ 102 | "arm64" 103 | ], 104 | "optional": true, 105 | "os": [ 106 | "linux" 107 | ], 108 | "engines": { 109 | "node": ">= 10" 110 | } 111 | }, 112 | "node_modules/@next/swc-linux-x64-gnu": { 113 | "version": "12.1.0", 114 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz", 115 | "integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==", 116 | "cpu": [ 117 | "x64" 118 | ], 119 | "optional": true, 120 | "os": [ 121 | "linux" 122 | ], 123 | "engines": { 124 | "node": ">= 10" 125 | } 126 | }, 127 | "node_modules/@next/swc-linux-x64-musl": { 128 | "version": "12.1.0", 129 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz", 130 | "integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==", 131 | "cpu": [ 132 | "x64" 133 | ], 134 | "optional": true, 135 | "os": [ 136 | "linux" 137 | ], 138 | "engines": { 139 | "node": ">= 10" 140 | } 141 | }, 142 | "node_modules/@next/swc-win32-arm64-msvc": { 143 | "version": "12.1.0", 144 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz", 145 | "integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==", 146 | "cpu": [ 147 | "arm64" 148 | ], 149 | "optional": true, 150 | "os": [ 151 | "win32" 152 | ], 153 | "engines": { 154 | "node": ">= 10" 155 | } 156 | }, 157 | "node_modules/@next/swc-win32-ia32-msvc": { 158 | "version": "12.1.0", 159 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz", 160 | "integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==", 161 | "cpu": [ 162 | "ia32" 163 | ], 164 | "optional": true, 165 | "os": [ 166 | "win32" 167 | ], 168 | "engines": { 169 | "node": ">= 10" 170 | } 171 | }, 172 | "node_modules/@next/swc-win32-x64-msvc": { 173 | "version": "12.1.0", 174 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz", 175 | "integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==", 176 | "cpu": [ 177 | "x64" 178 | ], 179 | "optional": true, 180 | "os": [ 181 | "win32" 182 | ], 183 | "engines": { 184 | "node": ">= 10" 185 | } 186 | }, 187 | "node_modules/@yllet/client": { 188 | "version": "3.0.1", 189 | "resolved": "https://registry.npmjs.org/@yllet/client/-/client-3.0.1.tgz", 190 | "integrity": "sha512-ktS3gUIh/CPbbkhvrIxQ40Ws2YdBNiOZKcLOJKdsZnnfEWNus9CUUMUV/8Ev4a1TI1N6WVnvwgMNciiuwdWR1Q==", 191 | "dependencies": { 192 | "@yllet/support": "^3.0.1", 193 | "isomorphic-form-data": "^2.0.0" 194 | } 195 | }, 196 | "node_modules/@yllet/react": { 197 | "version": "3.0.1", 198 | "resolved": "https://registry.npmjs.org/@yllet/react/-/react-3.0.1.tgz", 199 | "integrity": "sha512-cKlR21US/uR1HOlsZRJBBmk8dcMiufPIcL4Oukm0PLNHEgDL04C75kbzW8DRQlme4vH1mcI1WUPNb8ulBafAhA==", 200 | "dependencies": { 201 | "@yllet/client": "^3.0.1", 202 | "invariant": "^2.2.4", 203 | "prop-types": "^15.7.2" 204 | }, 205 | "peerDependencies": { 206 | "react": "*" 207 | } 208 | }, 209 | "node_modules/@yllet/support": { 210 | "version": "3.0.1", 211 | "resolved": "https://registry.npmjs.org/@yllet/support/-/support-3.0.1.tgz", 212 | "integrity": "sha512-NGbwwlgt8CK0RiR+rzj9ak3ikUaL4M7k9ebnEHbjlLPRfiufBy/1tUA3obaUYms3p/SI8J0it0HEUn7UXQmMzg==" 213 | }, 214 | "node_modules/asynckit": { 215 | "version": "0.4.0", 216 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 217 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 218 | }, 219 | "node_modules/caniuse-lite": { 220 | "version": "1.0.30001303", 221 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001303.tgz", 222 | "integrity": "sha512-/Mqc1oESndUNszJP0kx0UaQU9kEv9nNtJ7Kn8AdA0mNnH8eR1cj0kG+NbNuC1Wq/b21eA8prhKRA3bbkjONegQ==", 223 | "funding": { 224 | "type": "opencollective", 225 | "url": "https://opencollective.com/browserslist" 226 | } 227 | }, 228 | "node_modules/combined-stream": { 229 | "version": "1.0.8", 230 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 231 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 232 | "dependencies": { 233 | "delayed-stream": "~1.0.0" 234 | }, 235 | "engines": { 236 | "node": ">= 0.8" 237 | } 238 | }, 239 | "node_modules/delayed-stream": { 240 | "version": "1.0.0", 241 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 242 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 243 | "engines": { 244 | "node": ">=0.4.0" 245 | } 246 | }, 247 | "node_modules/form-data": { 248 | "version": "2.5.1", 249 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 250 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 251 | "dependencies": { 252 | "asynckit": "^0.4.0", 253 | "combined-stream": "^1.0.6", 254 | "mime-types": "^2.1.12" 255 | }, 256 | "engines": { 257 | "node": ">= 0.12" 258 | } 259 | }, 260 | "node_modules/invariant": { 261 | "version": "2.2.4", 262 | "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", 263 | "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", 264 | "dependencies": { 265 | "loose-envify": "^1.0.0" 266 | } 267 | }, 268 | "node_modules/isomorphic-form-data": { 269 | "version": "2.0.0", 270 | "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz", 271 | "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==", 272 | "dependencies": { 273 | "form-data": "^2.3.2" 274 | } 275 | }, 276 | "node_modules/js-tokens": { 277 | "version": "4.0.0", 278 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 279 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 280 | }, 281 | "node_modules/loose-envify": { 282 | "version": "1.4.0", 283 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 284 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 285 | "dependencies": { 286 | "js-tokens": "^3.0.0 || ^4.0.0" 287 | }, 288 | "bin": { 289 | "loose-envify": "cli.js" 290 | } 291 | }, 292 | "node_modules/mime-db": { 293 | "version": "1.51.0", 294 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 295 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", 296 | "engines": { 297 | "node": ">= 0.6" 298 | } 299 | }, 300 | "node_modules/mime-types": { 301 | "version": "2.1.34", 302 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 303 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 304 | "dependencies": { 305 | "mime-db": "1.51.0" 306 | }, 307 | "engines": { 308 | "node": ">= 0.6" 309 | } 310 | }, 311 | "node_modules/nanoid": { 312 | "version": "3.2.0", 313 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 314 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", 315 | "bin": { 316 | "nanoid": "bin/nanoid.cjs" 317 | }, 318 | "engines": { 319 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 320 | } 321 | }, 322 | "node_modules/next": { 323 | "version": "12.1.0", 324 | "resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz", 325 | "integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==", 326 | "dependencies": { 327 | "@next/env": "12.1.0", 328 | "caniuse-lite": "^1.0.30001283", 329 | "postcss": "8.4.5", 330 | "styled-jsx": "5.0.0", 331 | "use-subscription": "1.5.1" 332 | }, 333 | "bin": { 334 | "next": "dist/bin/next" 335 | }, 336 | "engines": { 337 | "node": ">=12.22.0" 338 | }, 339 | "optionalDependencies": { 340 | "@next/swc-android-arm64": "12.1.0", 341 | "@next/swc-darwin-arm64": "12.1.0", 342 | "@next/swc-darwin-x64": "12.1.0", 343 | "@next/swc-linux-arm-gnueabihf": "12.1.0", 344 | "@next/swc-linux-arm64-gnu": "12.1.0", 345 | "@next/swc-linux-arm64-musl": "12.1.0", 346 | "@next/swc-linux-x64-gnu": "12.1.0", 347 | "@next/swc-linux-x64-musl": "12.1.0", 348 | "@next/swc-win32-arm64-msvc": "12.1.0", 349 | "@next/swc-win32-ia32-msvc": "12.1.0", 350 | "@next/swc-win32-x64-msvc": "12.1.0" 351 | }, 352 | "peerDependencies": { 353 | "fibers": ">= 3.1.0", 354 | "node-sass": "^6.0.0 || ^7.0.0", 355 | "react": "^17.0.2 || ^18.0.0-0", 356 | "react-dom": "^17.0.2 || ^18.0.0-0", 357 | "sass": "^1.3.0" 358 | }, 359 | "peerDependenciesMeta": { 360 | "fibers": { 361 | "optional": true 362 | }, 363 | "node-sass": { 364 | "optional": true 365 | }, 366 | "sass": { 367 | "optional": true 368 | } 369 | } 370 | }, 371 | "node_modules/object-assign": { 372 | "version": "4.1.1", 373 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 374 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 375 | "engines": { 376 | "node": ">=0.10.0" 377 | } 378 | }, 379 | "node_modules/picocolors": { 380 | "version": "1.0.0", 381 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 382 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 383 | }, 384 | "node_modules/postcss": { 385 | "version": "8.4.5", 386 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", 387 | "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", 388 | "dependencies": { 389 | "nanoid": "^3.1.30", 390 | "picocolors": "^1.0.0", 391 | "source-map-js": "^1.0.1" 392 | }, 393 | "engines": { 394 | "node": "^10 || ^12 || >=14" 395 | }, 396 | "funding": { 397 | "type": "opencollective", 398 | "url": "https://opencollective.com/postcss/" 399 | } 400 | }, 401 | "node_modules/prop-types": { 402 | "version": "15.7.2", 403 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 404 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 405 | "dependencies": { 406 | "loose-envify": "^1.4.0", 407 | "object-assign": "^4.1.1", 408 | "react-is": "^16.8.1" 409 | } 410 | }, 411 | "node_modules/react": { 412 | "version": "17.0.2", 413 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 414 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 415 | "dependencies": { 416 | "loose-envify": "^1.1.0", 417 | "object-assign": "^4.1.1" 418 | }, 419 | "engines": { 420 | "node": ">=0.10.0" 421 | } 422 | }, 423 | "node_modules/react-dom": { 424 | "version": "17.0.2", 425 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 426 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 427 | "dependencies": { 428 | "loose-envify": "^1.1.0", 429 | "object-assign": "^4.1.1", 430 | "scheduler": "^0.20.2" 431 | }, 432 | "peerDependencies": { 433 | "react": "17.0.2" 434 | } 435 | }, 436 | "node_modules/react-is": { 437 | "version": "16.8.6", 438 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", 439 | "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" 440 | }, 441 | "node_modules/scheduler": { 442 | "version": "0.20.2", 443 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 444 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 445 | "dependencies": { 446 | "loose-envify": "^1.1.0", 447 | "object-assign": "^4.1.1" 448 | } 449 | }, 450 | "node_modules/source-map-js": { 451 | "version": "1.0.2", 452 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 453 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 454 | "engines": { 455 | "node": ">=0.10.0" 456 | } 457 | }, 458 | "node_modules/styled-jsx": { 459 | "version": "5.0.0", 460 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.0.tgz", 461 | "integrity": "sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==", 462 | "engines": { 463 | "node": ">= 12.0.0" 464 | }, 465 | "peerDependencies": { 466 | "react": ">= 16.8.0 || 17.x.x || 18.x.x" 467 | }, 468 | "peerDependenciesMeta": { 469 | "@babel/core": { 470 | "optional": true 471 | }, 472 | "babel-plugin-macros": { 473 | "optional": true 474 | } 475 | } 476 | }, 477 | "node_modules/use-subscription": { 478 | "version": "1.5.1", 479 | "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", 480 | "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", 481 | "dependencies": { 482 | "object-assign": "^4.1.1" 483 | }, 484 | "peerDependencies": { 485 | "react": "^16.8.0 || ^17.0.0" 486 | } 487 | } 488 | }, 489 | "dependencies": { 490 | "@next/env": { 491 | "version": "12.1.0", 492 | "resolved": "https://registry.npmjs.org/@next/env/-/env-12.1.0.tgz", 493 | "integrity": "sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==" 494 | }, 495 | "@next/swc-android-arm64": { 496 | "version": "12.1.0", 497 | "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz", 498 | "integrity": "sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==", 499 | "optional": true 500 | }, 501 | "@next/swc-darwin-arm64": { 502 | "version": "12.1.0", 503 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz", 504 | "integrity": "sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==", 505 | "optional": true 506 | }, 507 | "@next/swc-darwin-x64": { 508 | "version": "12.1.0", 509 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz", 510 | "integrity": "sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==", 511 | "optional": true 512 | }, 513 | "@next/swc-linux-arm-gnueabihf": { 514 | "version": "12.1.0", 515 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz", 516 | "integrity": "sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==", 517 | "optional": true 518 | }, 519 | "@next/swc-linux-arm64-gnu": { 520 | "version": "12.1.0", 521 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz", 522 | "integrity": "sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==", 523 | "optional": true 524 | }, 525 | "@next/swc-linux-arm64-musl": { 526 | "version": "12.1.0", 527 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz", 528 | "integrity": "sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==", 529 | "optional": true 530 | }, 531 | "@next/swc-linux-x64-gnu": { 532 | "version": "12.1.0", 533 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz", 534 | "integrity": "sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==", 535 | "optional": true 536 | }, 537 | "@next/swc-linux-x64-musl": { 538 | "version": "12.1.0", 539 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz", 540 | "integrity": "sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==", 541 | "optional": true 542 | }, 543 | "@next/swc-win32-arm64-msvc": { 544 | "version": "12.1.0", 545 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz", 546 | "integrity": "sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==", 547 | "optional": true 548 | }, 549 | "@next/swc-win32-ia32-msvc": { 550 | "version": "12.1.0", 551 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz", 552 | "integrity": "sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==", 553 | "optional": true 554 | }, 555 | "@next/swc-win32-x64-msvc": { 556 | "version": "12.1.0", 557 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz", 558 | "integrity": "sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==", 559 | "optional": true 560 | }, 561 | "@yllet/client": { 562 | "version": "3.0.1", 563 | "resolved": "https://registry.npmjs.org/@yllet/client/-/client-3.0.1.tgz", 564 | "integrity": "sha512-ktS3gUIh/CPbbkhvrIxQ40Ws2YdBNiOZKcLOJKdsZnnfEWNus9CUUMUV/8Ev4a1TI1N6WVnvwgMNciiuwdWR1Q==", 565 | "requires": { 566 | "@yllet/support": "^3.0.1", 567 | "isomorphic-form-data": "^2.0.0" 568 | } 569 | }, 570 | "@yllet/react": { 571 | "version": "3.0.1", 572 | "resolved": "https://registry.npmjs.org/@yllet/react/-/react-3.0.1.tgz", 573 | "integrity": "sha512-cKlR21US/uR1HOlsZRJBBmk8dcMiufPIcL4Oukm0PLNHEgDL04C75kbzW8DRQlme4vH1mcI1WUPNb8ulBafAhA==", 574 | "requires": { 575 | "@yllet/client": "^3.0.1", 576 | "invariant": "^2.2.4", 577 | "prop-types": "^15.7.2" 578 | } 579 | }, 580 | "@yllet/support": { 581 | "version": "3.0.1", 582 | "resolved": "https://registry.npmjs.org/@yllet/support/-/support-3.0.1.tgz", 583 | "integrity": "sha512-NGbwwlgt8CK0RiR+rzj9ak3ikUaL4M7k9ebnEHbjlLPRfiufBy/1tUA3obaUYms3p/SI8J0it0HEUn7UXQmMzg==" 584 | }, 585 | "asynckit": { 586 | "version": "0.4.0", 587 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 588 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 589 | }, 590 | "caniuse-lite": { 591 | "version": "1.0.30001303", 592 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001303.tgz", 593 | "integrity": "sha512-/Mqc1oESndUNszJP0kx0UaQU9kEv9nNtJ7Kn8AdA0mNnH8eR1cj0kG+NbNuC1Wq/b21eA8prhKRA3bbkjONegQ==" 594 | }, 595 | "combined-stream": { 596 | "version": "1.0.8", 597 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 598 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 599 | "requires": { 600 | "delayed-stream": "~1.0.0" 601 | } 602 | }, 603 | "delayed-stream": { 604 | "version": "1.0.0", 605 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 606 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 607 | }, 608 | "form-data": { 609 | "version": "2.5.1", 610 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 611 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 612 | "requires": { 613 | "asynckit": "^0.4.0", 614 | "combined-stream": "^1.0.6", 615 | "mime-types": "^2.1.12" 616 | } 617 | }, 618 | "invariant": { 619 | "version": "2.2.4", 620 | "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", 621 | "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", 622 | "requires": { 623 | "loose-envify": "^1.0.0" 624 | } 625 | }, 626 | "isomorphic-form-data": { 627 | "version": "2.0.0", 628 | "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz", 629 | "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==", 630 | "requires": { 631 | "form-data": "^2.3.2" 632 | } 633 | }, 634 | "js-tokens": { 635 | "version": "4.0.0", 636 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 637 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 638 | }, 639 | "loose-envify": { 640 | "version": "1.4.0", 641 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 642 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 643 | "requires": { 644 | "js-tokens": "^3.0.0 || ^4.0.0" 645 | } 646 | }, 647 | "mime-db": { 648 | "version": "1.51.0", 649 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 650 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" 651 | }, 652 | "mime-types": { 653 | "version": "2.1.34", 654 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 655 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 656 | "requires": { 657 | "mime-db": "1.51.0" 658 | } 659 | }, 660 | "nanoid": { 661 | "version": "3.2.0", 662 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 663 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==" 664 | }, 665 | "next": { 666 | "version": "12.1.0", 667 | "resolved": "https://registry.npmjs.org/next/-/next-12.1.0.tgz", 668 | "integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==", 669 | "requires": { 670 | "@next/env": "12.1.0", 671 | "@next/swc-android-arm64": "12.1.0", 672 | "@next/swc-darwin-arm64": "12.1.0", 673 | "@next/swc-darwin-x64": "12.1.0", 674 | "@next/swc-linux-arm-gnueabihf": "12.1.0", 675 | "@next/swc-linux-arm64-gnu": "12.1.0", 676 | "@next/swc-linux-arm64-musl": "12.1.0", 677 | "@next/swc-linux-x64-gnu": "12.1.0", 678 | "@next/swc-linux-x64-musl": "12.1.0", 679 | "@next/swc-win32-arm64-msvc": "12.1.0", 680 | "@next/swc-win32-ia32-msvc": "12.1.0", 681 | "@next/swc-win32-x64-msvc": "12.1.0", 682 | "caniuse-lite": "^1.0.30001283", 683 | "postcss": "8.4.5", 684 | "styled-jsx": "5.0.0", 685 | "use-subscription": "1.5.1" 686 | } 687 | }, 688 | "object-assign": { 689 | "version": "4.1.1", 690 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 691 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 692 | }, 693 | "picocolors": { 694 | "version": "1.0.0", 695 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 696 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 697 | }, 698 | "postcss": { 699 | "version": "8.4.5", 700 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", 701 | "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", 702 | "requires": { 703 | "nanoid": "^3.1.30", 704 | "picocolors": "^1.0.0", 705 | "source-map-js": "^1.0.1" 706 | } 707 | }, 708 | "prop-types": { 709 | "version": "15.7.2", 710 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 711 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 712 | "requires": { 713 | "loose-envify": "^1.4.0", 714 | "object-assign": "^4.1.1", 715 | "react-is": "^16.8.1" 716 | } 717 | }, 718 | "react": { 719 | "version": "17.0.2", 720 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 721 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 722 | "requires": { 723 | "loose-envify": "^1.1.0", 724 | "object-assign": "^4.1.1" 725 | } 726 | }, 727 | "react-dom": { 728 | "version": "17.0.2", 729 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 730 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 731 | "requires": { 732 | "loose-envify": "^1.1.0", 733 | "object-assign": "^4.1.1", 734 | "scheduler": "^0.20.2" 735 | } 736 | }, 737 | "react-is": { 738 | "version": "16.8.6", 739 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", 740 | "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" 741 | }, 742 | "scheduler": { 743 | "version": "0.20.2", 744 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 745 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 746 | "requires": { 747 | "loose-envify": "^1.1.0", 748 | "object-assign": "^4.1.1" 749 | } 750 | }, 751 | "source-map-js": { 752 | "version": "1.0.2", 753 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 754 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" 755 | }, 756 | "styled-jsx": { 757 | "version": "5.0.0", 758 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.0.tgz", 759 | "integrity": "sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==", 760 | "requires": {} 761 | }, 762 | "use-subscription": { 763 | "version": "1.5.1", 764 | "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", 765 | "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", 766 | "requires": { 767 | "object-assign": "^4.1.1" 768 | } 769 | } 770 | } 771 | } 772 | --------------------------------------------------------------------------------