Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(core): Reimplement fetch source as async generator (#3043)

authored by

Phil Pluckthun and committed by
GitHub
81cf542b d41fac83

+214 -255
+7
.changeset/real-donkeys-act.md
··· 1 + --- 2 + '@urql/core': patch 3 + --- 4 + 5 + Replace fetch source implementation with async generator implementation, based on Wonka's `fromAsyncIterable`. 6 + This also further hardens our support for the "Incremental Delivery" specification and 7 + refactors its implementation and covers more edge cases.
+1
exchanges/execute/src/execute.test.ts
··· 196 196 197 197 fetchMock.mockResolvedValue({ 198 198 status: 200, 199 + headers: { get: () => 'application/json' }, 199 200 text: vi 200 201 .fn() 201 202 .mockResolvedValue(JSON.stringify({ data: mockHttpResponseData })),
+10 -2
exchanges/graphcache/src/offlineExchange.test.ts
··· 62 62 63 63 describe('storage', () => { 64 64 it('should read the metadata and dispatch operations on initialization', () => { 65 - const client = createClient({ url: 'http://0.0.0.0' }); 65 + const client = createClient({ 66 + url: 'http://0.0.0.0', 67 + exchanges: [], 68 + }); 69 + 66 70 const reexecuteOperation = vi 67 71 .spyOn(client, 'reexecuteOperation') 68 72 .mockImplementation(() => undefined); ··· 100 104 it('should intercept errored mutations', () => { 101 105 const onlineSpy = vi.spyOn(navigator, 'onLine', 'get'); 102 106 103 - const client = createClient({ url: 'http://0.0.0.0' }); 107 + const client = createClient({ 108 + url: 'http://0.0.0.0', 109 + exchanges: [], 110 + }); 111 + 104 112 const queryOp = client.createRequestOperation('query', { 105 113 key: 1, 106 114 query: queryOne,
+6 -54
exchanges/multipart-fetch/src/multipartFetchExchange.test.ts
··· 1 - import { Client, OperationResult, makeOperation } from '@urql/core'; 2 - import { empty, fromValue, pipe, Source, subscribe, toPromise } from 'wonka'; 1 + import { Client, OperationResult } from '@urql/core'; 2 + import { empty, fromValue, pipe, Source, toPromise } from 'wonka'; 3 + 3 4 import { 4 5 vi, 5 6 expect, ··· 22 23 23 24 const fetch = (global as any).fetch as Mock; 24 25 const abort = vi.fn(); 25 - 26 - const abortError = new Error(); 27 - abortError.name = 'AbortError'; 28 26 29 27 beforeAll(() => { 30 28 (global as any).AbortController = function AbortController() { ··· 61 59 beforeEach(() => { 62 60 fetch.mockResolvedValue({ 63 61 status: 200, 62 + headers: { get: () => 'application/json' }, 64 63 text: vi.fn().mockResolvedValue(response), 65 64 }); 66 65 }); ··· 130 129 beforeEach(() => { 131 130 fetch.mockResolvedValue({ 132 131 status: 400, 132 + headers: { get: () => 'application/json' }, 133 133 text: vi.fn().mockResolvedValue('{}'), 134 134 }); 135 135 }); ··· 165 165 it('ignores the error when a result is available', async () => { 166 166 fetch.mockResolvedValue({ 167 167 status: 400, 168 + headers: { get: () => 'application/json' }, 168 169 text: vi.fn().mockResolvedValue(response), 169 170 }); 170 171 ··· 177 178 expect(data.data).toEqual(JSON.parse(response).data); 178 179 }); 179 180 }); 180 - 181 - describe('on teardown', () => { 182 - const fail = () => { 183 - expect(true).toEqual(false); 184 - }; 185 - 186 - it('does not start the outgoing request on immediate teardowns', () => { 187 - fetch.mockRejectedValueOnce(abortError); 188 - 189 - const { unsubscribe } = pipe( 190 - fromValue(queryOperation), 191 - multipartFetchExchange(exchangeArgs), 192 - subscribe(fail) 193 - ); 194 - 195 - unsubscribe(); 196 - expect(fetch).toHaveBeenCalledTimes(0); 197 - expect(abort).toHaveBeenCalledTimes(1); 198 - }); 199 - 200 - it('aborts the outgoing request', async () => { 201 - fetch.mockRejectedValueOnce(abortError); 202 - 203 - const { unsubscribe } = pipe( 204 - fromValue(queryOperation), 205 - multipartFetchExchange(exchangeArgs), 206 - subscribe(fail) 207 - ); 208 - 209 - await Promise.resolve(); 210 - 211 - unsubscribe(); 212 - expect(fetch).toHaveBeenCalledTimes(1); 213 - expect(abort).toHaveBeenCalledTimes(1); 214 - }); 215 - 216 - it('does not call the query', () => { 217 - pipe( 218 - fromValue( 219 - makeOperation('teardown', queryOperation, queryOperation.context) 220 - ), 221 - multipartFetchExchange(exchangeArgs), 222 - subscribe(fail) 223 - ); 224 - 225 - expect(fetch).toHaveBeenCalledTimes(0); 226 - expect(abort).toHaveBeenCalledTimes(0); 227 - }); 228 - });
+16
exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
··· 36 36 }); 37 37 38 38 fetch.mockResolvedValueOnce({ 39 + status: 200, 40 + headers: { get: () => 'application/json' }, 39 41 text: () => Promise.resolve(expected), 40 42 }); 41 43 ··· 63 65 64 66 fetch 65 67 .mockResolvedValueOnce({ 68 + status: 200, 69 + headers: { get: () => 'application/json' }, 66 70 text: () => Promise.resolve(expectedMiss), 67 71 }) 68 72 .mockResolvedValueOnce({ 73 + status: 200, 74 + headers: { get: () => 'application/json' }, 69 75 text: () => Promise.resolve(expectedRetry), 70 76 }); 71 77 ··· 94 100 95 101 fetch 96 102 .mockResolvedValueOnce({ 103 + status: 200, 104 + headers: { get: () => 'application/json' }, 97 105 text: () => Promise.resolve(expectedMiss), 98 106 }) 99 107 .mockResolvedValueOnce({ 108 + status: 200, 109 + headers: { get: () => 'application/json' }, 100 110 text: () => Promise.resolve(expectedRetry), 101 111 }); 102 112 ··· 127 137 128 138 fetch 129 139 .mockResolvedValueOnce({ 140 + status: 200, 141 + headers: { get: () => 'application/json' }, 130 142 text: () => Promise.resolve(expectedMiss), 131 143 }) 132 144 .mockResolvedValueOnce({ 145 + status: 200, 146 + headers: { get: () => 'application/json' }, 133 147 text: () => Promise.resolve(expectedRetry), 134 148 }) 135 149 .mockResolvedValueOnce({ 150 + status: 200, 151 + headers: { get: () => 'application/json' }, 136 152 text: () => Promise.resolve(expectedRetry), 137 153 }); 138 154
+13 -4
packages/core/src/exchanges/fetch.test.ts
··· 62 62 beforeEach(() => { 63 63 fetch.mockResolvedValue({ 64 64 status: 200, 65 + headers: { get: () => 'application/json' }, 65 66 text: vi.fn().mockResolvedValue(response), 66 67 }); 67 68 }); ··· 91 92 beforeEach(() => { 92 93 fetch.mockResolvedValue({ 93 94 status: 400, 95 + headers: { get: () => 'application/json' }, 94 96 text: vi.fn().mockResolvedValue(JSON.stringify({})), 95 97 }); 96 98 }); ··· 126 128 it('ignores the error when a result is available', async () => { 127 129 fetch.mockResolvedValue({ 128 130 status: 400, 131 + headers: { get: () => 'application/json' }, 129 132 text: vi.fn().mockResolvedValue(response), 130 133 }); 131 134 ··· 143 146 const fail = () => { 144 147 expect(true).toEqual(false); 145 148 }; 149 + 146 150 it('does not start the outgoing request on immediate teardowns', () => { 147 - fetch.mockRejectedValueOnce(abortError); 151 + fetch.mockResolvedValue(new Response('text', { status: 200 })); 148 152 149 153 const { unsubscribe } = pipe( 150 154 fromValue(queryOperation), ··· 154 158 155 159 unsubscribe(); 156 160 expect(fetch).toHaveBeenCalledTimes(0); 157 - expect(abort).toHaveBeenCalledTimes(1); 161 + expect(abort).toHaveBeenCalledTimes(0); 158 162 }); 159 163 160 164 it('aborts the outgoing request', async () => { 161 - fetch.mockRejectedValueOnce(abortError); 165 + fetch.mockResolvedValue(new Response('text', { status: 200 })); 162 166 163 167 const { unsubscribe } = pipe( 164 168 fromValue(queryOperation), ··· 169 173 await Promise.resolve(); 170 174 171 175 unsubscribe(); 176 + 177 + // NOTE: We can only observe the async iterator's final run after a macro tick 178 + await new Promise(resolve => setTimeout(resolve)); 172 179 expect(fetch).toHaveBeenCalledTimes(1); 173 - expect(abort).toHaveBeenCalledTimes(1); 180 + expect(abort).toHaveBeenCalled(); 174 181 }); 175 182 176 183 it('does not call the query', () => { 184 + fetch.mockResolvedValue(new Response('text', { status: 200 })); 185 + 177 186 pipe( 178 187 fromValue( 179 188 makeOperation('teardown', queryOperation, queryOperation.context)
+3
packages/core/src/internal/__snapshots__/fetchSource.test.ts.snap
··· 606 606 { 607 607 "type": "return", 608 608 "value": { 609 + "headers": { 610 + "get": [Function], 611 + }, 609 612 "status": 200, 610 613 "text": [MockFunction spy] { 611 614 "calls": [
+9 -7
packages/core/src/internal/fetchSource.test.ts
··· 19 19 const fetch = (global as any).fetch as Mock; 20 20 const abort = vi.fn(); 21 21 22 - const abortError = new Error(); 23 - abortError.name = 'AbortError'; 24 - 25 22 beforeAll(() => { 26 23 (global as any).AbortController = function AbortController() { 27 24 this.signal = undefined; ··· 51 48 beforeEach(() => { 52 49 fetch.mockResolvedValue({ 53 50 status: 200, 51 + headers: { get: () => 'application/json' }, 54 52 text: vi.fn().mockResolvedValue(response), 55 53 }); 56 54 }); ··· 73 71 const fetchOptions = {}; 74 72 const fetcher = vi.fn().mockResolvedValue({ 75 73 status: 200, 74 + headers: { get: () => 'application/json' }, 76 75 text: vi.fn().mockResolvedValue(response), 77 76 }); 78 77 ··· 102 101 fetch.mockResolvedValue({ 103 102 status: 400, 104 103 statusText: 'Forbidden', 104 + headers: { get: () => 'application/json' }, 105 105 text: vi.fn().mockResolvedValue('{}'), 106 106 }); 107 107 }); ··· 165 165 }; 166 166 167 167 it('does not start the outgoing request on immediate teardowns', () => { 168 - fetch.mockRejectedValue(abortError); 168 + fetch.mockResolvedValue(new Response('text', { status: 200 })); 169 169 170 170 const { unsubscribe } = pipe( 171 171 makeFetchSource(queryOperation, 'https://test.com/graphql', {}), ··· 174 174 175 175 unsubscribe(); 176 176 expect(fetch).toHaveBeenCalledTimes(0); 177 - expect(abort).toHaveBeenCalledTimes(1); 177 + expect(abort).toHaveBeenCalledTimes(0); 178 178 }); 179 179 180 180 it('aborts the outgoing request', async () => { 181 - fetch.mockRejectedValue(abortError); 181 + fetch.mockResolvedValue(new Response('text', { status: 200 })); 182 182 183 183 const { unsubscribe } = pipe( 184 184 makeFetchSource(queryOperation, 'https://test.com/graphql', {}), ··· 186 186 ); 187 187 188 188 await Promise.resolve(); 189 - 190 189 unsubscribe(); 190 + 191 + // NOTE: We can only observe the async iterator's final run after a macro tick 192 + await new Promise(resolve => setTimeout(resolve)); 191 193 expect(fetch).toHaveBeenCalledTimes(1); 192 194 expect(abort).toHaveBeenCalledTimes(1); 193 195 });
+116 -166
packages/core/src/internal/fetchSource.ts
··· 1 - import { Source, make } from 'wonka'; 2 - import { Operation, OperationResult } from '../types'; 1 + import { Source, fromAsyncIterable } from 'wonka'; 2 + import { Operation, OperationResult, ExecutionResult } from '../types'; 3 3 import { makeResult, makeErrorResult, mergeResultPatch } from '../utils'; 4 4 5 5 const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null; 6 - const jsonHeaderRe = /content-type:[^\r\n]*application\/json/i; 7 6 const boundaryHeaderRe = /boundary="?([^=";]+)"?/i; 8 7 9 - type ChunkData = { done: false; value: Buffer | Uint8Array } | { done: true }; 8 + type ChunkData = Buffer | Uint8Array; 10 9 11 10 // NOTE: We're avoiding referencing the `Buffer` global here to prevent 12 11 // auto-polyfilling in Webpack ··· 15 14 ? (input as Buffer).toString() 16 15 : decoder!.decode(input as ArrayBuffer); 17 16 17 + async function* streamBody(response: Response): AsyncIterableIterator<string> { 18 + if (response.body![Symbol.asyncIterator]) { 19 + for await (const chunk of response.body! as any) 20 + yield toString(chunk as ChunkData); 21 + } else { 22 + const reader = response.body!.getReader(); 23 + let result: ReadableStreamReadResult<ChunkData>; 24 + try { 25 + while (!(result = await reader.read()).done) yield toString(result.value); 26 + } finally { 27 + reader.cancel(); 28 + } 29 + } 30 + } 31 + 32 + async function* parseMultipartMixed( 33 + contentType: string, 34 + response: Response 35 + ): AsyncIterableIterator<ExecutionResult> { 36 + const boundaryHeader = contentType.match(boundaryHeaderRe); 37 + const boundary = '--' + (boundaryHeader ? boundaryHeader[1] : '-'); 38 + 39 + let buffer = ''; 40 + let isPreamble = true; 41 + let boundaryIndex: number; 42 + let payload: any; 43 + 44 + chunks: for await (const chunk of streamBody(response)) { 45 + buffer += chunk; 46 + while ((boundaryIndex = buffer.indexOf(boundary)) > -1) { 47 + if (isPreamble) { 48 + isPreamble = false; 49 + } else { 50 + const chunk = buffer.slice( 51 + buffer.indexOf('\r\n\r\n') + 4, 52 + boundaryIndex 53 + ); 54 + 55 + try { 56 + yield (payload = JSON.parse(chunk)); 57 + } catch (error) { 58 + if (!payload) throw error; 59 + } 60 + } 61 + 62 + buffer = buffer.slice(boundaryIndex + boundary.length); 63 + if (buffer.startsWith('--') || (payload && !payload.hasNext)) 64 + break chunks; 65 + } 66 + } 67 + 68 + if (payload && payload.hasNext) yield { hasNext: false }; 69 + } 70 + 71 + async function* fetchOperation( 72 + operation: Operation, 73 + url: string, 74 + fetchOptions: RequestInit 75 + ) { 76 + let abortController: AbortController | void; 77 + let result: OperationResult | null = null; 78 + let response: Response; 79 + 80 + try { 81 + if (typeof AbortController !== 'undefined') { 82 + fetchOptions.signal = (abortController = new AbortController()).signal; 83 + } 84 + 85 + // Delay for a tick to give the Client a chance to cancel the request 86 + // if a teardown comes in immediately 87 + await Promise.resolve(); 88 + response = await (operation.context.fetch || fetch)(url, fetchOptions); 89 + const contentType = response.headers.get('Content-Type') || ''; 90 + if (/text\//i.test(contentType)) { 91 + const text = await response.text(); 92 + return yield makeErrorResult(operation, new Error(text), response); 93 + } else if (!/multipart\/mixed/i.test(contentType)) { 94 + const text = await response.text(); 95 + return yield makeResult(operation, JSON.parse(text), response); 96 + } 97 + 98 + const iterator = parseMultipartMixed(contentType, response); 99 + for await (const payload of iterator) { 100 + yield (result = result 101 + ? mergeResultPatch(result, payload, response) 102 + : makeResult(operation, payload, response)); 103 + } 104 + 105 + if (!result) { 106 + yield (result = makeResult(operation, {}, response)); 107 + } 108 + } catch (error: any) { 109 + if (result) { 110 + throw error; 111 + } 112 + 113 + yield makeErrorResult( 114 + operation, 115 + (response!.status < 200 || response!.status >= 300) && 116 + response!.statusText 117 + ? new Error(response!.statusText) 118 + : error, 119 + response! 120 + ); 121 + } finally { 122 + if (abortController) abortController.abort(); 123 + } 124 + } 125 + 18 126 /** Makes a GraphQL HTTP request to a given API by wrapping around the Fetch API. 19 127 * 20 128 * @param operation - The {@link Operation} that should be sent via GraphQL over HTTP. ··· 42 150 * 43 151 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} for the Fetch API spec. 44 152 */ 45 - export const makeFetchSource = ( 153 + export function makeFetchSource( 46 154 operation: Operation, 47 155 url: string, 48 156 fetchOptions: RequestInit 49 - ): Source<OperationResult> => { 50 - const maxStatus = fetchOptions.redirect === 'manual' ? 400 : 300; 51 - const fetcher = operation.context.fetch; 52 - 53 - return make<OperationResult>(({ next, complete }) => { 54 - const abortController = 55 - typeof AbortController !== 'undefined' ? new AbortController() : null; 56 - if (abortController) { 57 - fetchOptions.signal = abortController.signal; 58 - } 59 - 60 - let hasResults = false; 61 - // DERIVATIVE: Copyright (c) 2021 Marais Rossouw <hi@marais.io> 62 - // See: https://github.com/maraisr/meros/blob/219fe95/src/browser.ts 63 - const executeIncrementalFetch = ( 64 - onResult: (result: OperationResult) => void, 65 - operation: Operation, 66 - response: Response 67 - ): Promise<void> => { 68 - // NOTE: Guarding against fetch polyfills here 69 - const contentType = 70 - (response.headers && response.headers.get('Content-Type')) || ''; 71 - if (/text\//i.test(contentType)) { 72 - return response.text().then(text => { 73 - onResult(makeErrorResult(operation, new Error(text), response)); 74 - }); 75 - } else if (!/multipart\/mixed/i.test(contentType)) { 76 - return response.text().then(payload => { 77 - onResult(makeResult(operation, JSON.parse(payload), response)); 78 - }); 79 - } 80 - 81 - let boundary = '---'; 82 - const boundaryHeader = contentType.match(boundaryHeaderRe); 83 - if (boundaryHeader) boundary = '--' + boundaryHeader[1]; 84 - 85 - let read: () => Promise<ChunkData>; 86 - let cancel = () => { 87 - /*noop*/ 88 - }; 89 - if (response[Symbol.asyncIterator]) { 90 - const iterator = response[Symbol.asyncIterator](); 91 - read = iterator.next.bind(iterator); 92 - } else if ('body' in response && response.body) { 93 - const reader = response.body.getReader(); 94 - cancel = () => reader.cancel(); 95 - read = () => reader.read(); 96 - } else { 97 - throw new TypeError('Streaming requests unsupported'); 98 - } 99 - 100 - let buffer = ''; 101 - let isPreamble = true; 102 - let nextResult: OperationResult | null = null; 103 - let prevResult: OperationResult | null = null; 104 - 105 - function next(data: ChunkData): Promise<void> | void { 106 - if (!data.done) { 107 - const chunk = toString(data.value); 108 - let boundaryIndex = chunk.indexOf(boundary); 109 - if (boundaryIndex > -1) { 110 - boundaryIndex += buffer.length; 111 - } else { 112 - boundaryIndex = buffer.indexOf(boundary); 113 - } 114 - 115 - buffer += chunk; 116 - while (boundaryIndex > -1) { 117 - const current = buffer.slice(0, boundaryIndex); 118 - const next = buffer.slice(boundaryIndex + boundary.length); 119 - 120 - if (isPreamble) { 121 - isPreamble = false; 122 - } else { 123 - const headersEnd = current.indexOf('\r\n\r\n') + 4; 124 - const headers = current.slice(0, headersEnd); 125 - const body = current.slice( 126 - headersEnd, 127 - current.lastIndexOf('\r\n') 128 - ); 129 - 130 - let payload: any; 131 - if (jsonHeaderRe.test(headers)) { 132 - try { 133 - payload = JSON.parse(body); 134 - nextResult = prevResult = prevResult 135 - ? mergeResultPatch(prevResult, payload, response) 136 - : makeResult(operation, payload, response); 137 - } catch (_error) {} 138 - } 139 - 140 - if (next.slice(0, 2) === '--' || (payload && !payload.hasNext)) { 141 - if (!prevResult) 142 - return onResult(makeResult(operation, {}, response)); 143 - break; 144 - } 145 - } 146 - 147 - buffer = next; 148 - boundaryIndex = buffer.indexOf(boundary); 149 - } 150 - } else { 151 - hasResults = true; 152 - } 153 - 154 - if (nextResult) { 155 - onResult(nextResult); 156 - nextResult = null; 157 - } 158 - 159 - if (!data.done && (!prevResult || prevResult.hasNext)) { 160 - return read().then(next); 161 - } 162 - } 163 - 164 - return read().then(next).finally(cancel); 165 - }; 166 - 167 - let ended = false; 168 - let statusNotOk = false; 169 - let response: Response; 170 - 171 - Promise.resolve() 172 - .then(() => { 173 - if (ended) return; 174 - return (fetcher || fetch)(url, fetchOptions); 175 - }) 176 - .then((_response: Response | void) => { 177 - if (!_response) return; 178 - response = _response; 179 - statusNotOk = response.status < 200 || response.status >= maxStatus; 180 - return executeIncrementalFetch(next, operation, response); 181 - }) 182 - .then(complete) 183 - .catch((error: Error) => { 184 - if (hasResults) { 185 - throw error; 186 - } 187 - 188 - const result = makeErrorResult( 189 - operation, 190 - statusNotOk 191 - ? response.statusText 192 - ? new Error(response.statusText) 193 - : error 194 - : error, 195 - response 196 - ); 197 - 198 - next(result); 199 - complete(); 200 - }); 201 - 202 - return () => { 203 - ended = true; 204 - if (abortController) { 205 - abortController.abort(); 206 - } 207 - }; 208 - }); 209 - }; 157 + ): Source<OperationResult> { 158 + return fromAsyncIterable(fetchOperation(operation, url, fetchOptions)); 159 + }
+7 -2
packages/svelte-urql/src/mutationStore.test.ts
··· 6 6 import { mutationStore } from './mutationStore'; 7 7 8 8 describe('mutationStore', () => { 9 - const client = createClient({ url: 'https://example.com' }); 9 + const client = createClient({ 10 + url: 'noop', 11 + exchanges: [], 12 + }); 13 + 10 14 const variables = {}; 11 15 const context = {}; 16 + 12 17 const query = 13 18 'mutation ($input: Example!) { doExample(input: $input) { id } }'; 14 19 const store = mutationStore({ ··· 26 31 27 32 it('fills the store with correct values', () => { 28 33 expect(get(store).operation.kind).toBe('mutation'); 29 - expect(get(store).operation.context.url).toBe('https://example.com'); 34 + expect(get(store).operation.context.url).toBe('noop'); 30 35 expect(get(store).operation.variables).toBe(variables); 31 36 32 37 expect(print(get(store).operation.query)).toMatchInlineSnapshot(`
+5 -1
packages/svelte-urql/src/queryStore.test.ts
··· 5 5 import { queryStore } from './queryStore'; 6 6 7 7 describe('queryStore', () => { 8 - const client = createClient({ url: 'https://example.com' }); 8 + const client = createClient({ 9 + url: 'https://example.com', 10 + exchanges: [], 11 + }); 12 + 9 13 const variables = {}; 10 14 const context = {}; 11 15 const query = '{ test }';
+5 -1
packages/svelte-urql/src/subscriptionStore.test.ts
··· 5 5 import { subscriptionStore } from './subscriptionStore'; 6 6 7 7 describe('subscriptionStore', () => { 8 - const client = createClient({ url: 'https://example.com' }); 8 + const client = createClient({ 9 + url: 'https://example.com', 10 + exchanges: [], 11 + }); 12 + 9 13 const variables = {}; 10 14 const context = {}; 11 15 const query = `subscription ($input: ExampleInput) { exampleSubscribe(input: $input) { data } }`;
+16 -18
pnpm-lock.yaml
··· 595 595 '@babel/generator': 7.20.4 596 596 '@babel/helper-module-transforms': 7.20.2 597 597 '@babel/helpers': 7.20.1 598 - '@babel/parser': 7.20.3 598 + '@babel/parser': 7.20.15 599 599 '@babel/template': 7.18.10 600 600 '@babel/traverse': 7.20.1 601 - '@babel/types': 7.20.2 601 + '@babel/types': 7.20.7 602 602 convert-source-map: 1.7.0 603 603 debug: 4.3.4 604 604 gensync: 1.0.0-beta.2 ··· 650 650 resolution: {integrity: sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==} 651 651 dependencies: 652 652 '@babel/helper-explode-assignable-expression': 7.13.0 653 - '@babel/types': 7.20.2 653 + '@babel/types': 7.20.7 654 654 655 655 /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.2: 656 656 resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} ··· 729 729 /@babel/helper-explode-assignable-expression/7.13.0: 730 730 resolution: {integrity: sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==} 731 731 dependencies: 732 - '@babel/types': 7.20.2 732 + '@babel/types': 7.20.7 733 733 734 734 /@babel/helper-function-name/7.19.0: 735 735 resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} 736 736 engines: {node: '>=6.9.0'} 737 737 dependencies: 738 738 '@babel/template': 7.18.10 739 - '@babel/types': 7.20.2 739 + '@babel/types': 7.20.7 740 740 741 741 /@babel/helper-hoist-variables/7.18.6: 742 742 resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} 743 743 engines: {node: '>=6.9.0'} 744 744 dependencies: 745 - '@babel/types': 7.20.2 745 + '@babel/types': 7.20.7 746 746 747 747 /@babel/helper-member-expression-to-functions/7.13.12: 748 748 resolution: {integrity: sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==} 749 749 dependencies: 750 - '@babel/types': 7.20.2 750 + '@babel/types': 7.20.7 751 751 752 752 /@babel/helper-module-imports/7.18.6: 753 753 resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} ··· 773 773 /@babel/helper-optimise-call-expression/7.12.13: 774 774 resolution: {integrity: sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==} 775 775 dependencies: 776 - '@babel/types': 7.20.2 776 + '@babel/types': 7.20.7 777 777 778 778 /@babel/helper-plugin-utils/7.10.4: 779 779 resolution: {integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==} ··· 787 787 dependencies: 788 788 '@babel/helper-annotate-as-pure': 7.18.6 789 789 '@babel/helper-wrap-function': 7.13.0 790 - '@babel/types': 7.20.2 790 + '@babel/types': 7.20.7 791 791 transitivePeerDependencies: 792 792 - supports-color 793 793 ··· 797 797 '@babel/helper-member-expression-to-functions': 7.13.12 798 798 '@babel/helper-optimise-call-expression': 7.12.13 799 799 '@babel/traverse': 7.20.1 800 - '@babel/types': 7.20.2 800 + '@babel/types': 7.20.7 801 801 transitivePeerDependencies: 802 802 - supports-color 803 803 ··· 805 805 resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} 806 806 engines: {node: '>=6.9.0'} 807 807 dependencies: 808 - '@babel/types': 7.20.2 808 + '@babel/types': 7.20.7 809 809 810 810 /@babel/helper-skip-transparent-expression-wrappers/7.12.1: 811 811 resolution: {integrity: sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==} 812 812 dependencies: 813 - '@babel/types': 7.20.2 813 + '@babel/types': 7.20.7 814 814 815 815 /@babel/helper-split-export-declaration/7.18.6: 816 816 resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} 817 817 engines: {node: '>=6.9.0'} 818 818 dependencies: 819 - '@babel/types': 7.20.2 819 + '@babel/types': 7.20.7 820 820 821 821 /@babel/helper-string-parser/7.19.4: 822 822 resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} ··· 836 836 '@babel/helper-function-name': 7.19.0 837 837 '@babel/template': 7.18.10 838 838 '@babel/traverse': 7.20.1 839 - '@babel/types': 7.20.2 839 + '@babel/types': 7.20.7 840 840 transitivePeerDependencies: 841 841 - supports-color 842 842 ··· 864 864 hasBin: true 865 865 dependencies: 866 866 '@babel/types': 7.20.7 867 - dev: true 868 867 869 868 /@babel/parser/7.20.3: 870 869 resolution: {integrity: sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==} ··· 1648 1647 '@babel/plugin-transform-unicode-escapes': 7.12.13_@babel+core@7.20.2 1649 1648 '@babel/plugin-transform-unicode-regex': 7.12.13_@babel+core@7.20.2 1650 1649 '@babel/preset-modules': 0.1.4_@babel+core@7.20.2 1651 - '@babel/types': 7.20.2 1650 + '@babel/types': 7.20.7 1652 1651 babel-plugin-polyfill-corejs2: 0.2.0_@babel+core@7.20.2 1653 1652 babel-plugin-polyfill-corejs3: 0.2.0_@babel+core@7.20.2 1654 1653 babel-plugin-polyfill-regenerator: 0.2.0_@babel+core@7.20.2 ··· 1677 1676 '@babel/helper-plugin-utils': 7.20.2 1678 1677 '@babel/plugin-proposal-unicode-property-regex': 7.12.13_@babel+core@7.20.2 1679 1678 '@babel/plugin-transform-dotall-regex': 7.12.13_@babel+core@7.20.2 1680 - '@babel/types': 7.20.2 1679 + '@babel/types': 7.20.7 1681 1680 esutils: 2.0.3 1682 1681 1683 1682 /@babel/preset-react/7.13.13_@babel+core@7.20.2: ··· 1797 1796 '@babel/helper-string-parser': 7.19.4 1798 1797 '@babel/helper-validator-identifier': 7.19.1 1799 1798 to-fast-properties: 2.0.0 1800 - dev: true 1801 1799 1802 1800 /@babel/types/7.8.3: 1803 1801 resolution: {integrity: sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==}