diff --git a/eslint.config.js b/eslint.config.js index 84fe016d6..137d6c94a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -20,5 +20,9 @@ export default tsEslint.config({ }, rules: { ['@typescript-eslint/only-throw-error']: 'off', + ['@typescript-eslint/no-unsafe-assignment']: 'off', + ['@typescript-eslint/no-unsafe-call']: 'off', + ['@typescript-eslint/no-unsafe-member-access']: 'off', + ['@typescript-eslint/ban-types']: 'off', }, }) diff --git a/examples/$/generated-clients/atlas/modules/SchemaIndex.ts b/examples/$/generated-clients/atlas/modules/SchemaIndex.ts index 97905e470..42a6539ce 100644 --- a/examples/$/generated-clients/atlas/modules/SchemaIndex.ts +++ b/examples/$/generated-clients/atlas/modules/SchemaIndex.ts @@ -4,6 +4,7 @@ import type * as Schema from './SchemaBuildtime.js' export interface Index { name: 'Atlas' + RootTypesPresent: ['Query'] Root: { Query: Schema.Root.Query Mutation: null diff --git a/examples/$/generated-clients/atlas/modules/SchemaRuntime.ts b/examples/$/generated-clients/atlas/modules/SchemaRuntime.ts index eecf1397c..4f4d2dffe 100644 --- a/examples/$/generated-clients/atlas/modules/SchemaRuntime.ts +++ b/examples/$/generated-clients/atlas/modules/SchemaRuntime.ts @@ -100,6 +100,7 @@ export const Query = $.Object$(`Query`, { export const $Index = { name: 'Atlas' as const, + RootTypesPresent: ['Query'] as const, Root: { Query, Mutation: null, diff --git a/examples/$/generated-clients/pokemon/modules/SchemaIndex.ts b/examples/$/generated-clients/pokemon/modules/SchemaIndex.ts index 0c7e25f21..5956c1ef5 100644 --- a/examples/$/generated-clients/pokemon/modules/SchemaIndex.ts +++ b/examples/$/generated-clients/pokemon/modules/SchemaIndex.ts @@ -4,6 +4,7 @@ import type * as Schema from './SchemaBuildtime.js' export interface Index { name: 'Pokemon' + RootTypesPresent: ['Query', 'Mutation'] Root: { Query: Schema.Root.Query Mutation: Schema.Root.Mutation diff --git a/examples/$/generated-clients/pokemon/modules/SchemaRuntime.ts b/examples/$/generated-clients/pokemon/modules/SchemaRuntime.ts index 3165d423e..f2f99b53f 100644 --- a/examples/$/generated-clients/pokemon/modules/SchemaRuntime.ts +++ b/examples/$/generated-clients/pokemon/modules/SchemaRuntime.ts @@ -47,6 +47,7 @@ export const Query = $.Object$(`Query`, { export const $Index = { name: 'Pokemon' as const, + RootTypesPresent: ['Query', 'Mutation'] as const, Root: { Query, Mutation, diff --git a/examples/$/show.ts b/examples/$/show.ts index 1fc6bb16a..5977d0fa4 100644 --- a/examples/$/show.ts +++ b/examples/$/show.ts @@ -6,19 +6,18 @@ type Logger = typeof console.log | typeof globalThis.process.stdout.write export const show = <$Logger extends Logger = typeof console.log>( value: unknown, - logger?: $Logger, + subTitle?: string, ): ReturnType<$Logger> => { - const write = logger ?? console.log + const write = console.log const inspected = inspect(value, { depth: null, colors: true }) - const message = renderShow(inspected) + const message = renderShow(inspected, subTitle) return write(message) as ReturnType<$Logger> } export const showJson = <$Logger extends Logger = typeof console.log>( value: unknown, - logger?: $Logger, ): ReturnType<$Logger> => { - const write = logger ?? console.log + const write = console.log const encoded = JSON.stringify(value, null, 2) const message = renderShow(encoded) return write(message) as ReturnType<$Logger> @@ -26,8 +25,8 @@ export const showJson = <$Logger extends Logger = typeof console.log>( export const showPartition = `---------------------------------------- SHOW ----------------------------------------` -const renderShow = (value: string) => { - return showPartition + '\n' + value +const renderShow = (value: string, subTitle?: string) => { + return showPartition + (subTitle ? `\n${subTitle}` : '') + '\n' + value } export const interceptAndShowOutput = (): void => { @@ -37,3 +36,10 @@ export const interceptAndShowOutput = (): void => { return originalWrite(renderShow(value)) } } + +export const interceptAndShowUncaughtErrors = () => { + process.on('uncaughtException', (error) => { + show(error, 'UNCAUGHT EXCEPTION:\n') + process.exit(1) + }) +} diff --git a/examples/__outputs__/extension|extension_opentelemetry.output.txt b/examples/__outputs__/extension|extension_opentelemetry.output.txt index 7b2adc016..c7814192a 100644 --- a/examples/__outputs__/extension|extension_opentelemetry.output.txt +++ b/examples/__outputs__/extension|extension_opentelemetry.output.txt @@ -9,14 +9,14 @@ } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'encode', - id: '0f8e4e2d2fa7a1d1', + id: 'bcca0b855b22b8af', kind: 0, - timestamp: 1726068743834000, - duration: 442.792, + timestamp: 1726346409548000, + duration: 448.042, attributes: {}, status: { code: 0 }, events: [], @@ -33,14 +33,14 @@ } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'pack', - id: '37766e2e0fa6ea2e', + id: '884350df7488294f', kind: 0, - timestamp: 1726068743836000, - duration: 808.5, + timestamp: 1726346409551000, + duration: 1024.667, attributes: {}, status: { code: 0 }, events: [], @@ -57,14 +57,14 @@ } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'exchange', - id: 'ed9ae7aad6fd1e69', + id: 'c5b5c072b7c8c20d', kind: 0, - timestamp: 1726068743837000, - duration: 329989.458, + timestamp: 1726346409553000, + duration: 194155.958, attributes: {}, status: { code: 0 }, events: [], @@ -81,14 +81,14 @@ } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'unpack', - id: 'd0d9cbd74e358490', + id: '6956cc361339e79b', kind: 0, - timestamp: 1726068744168000, - duration: 1907.291, + timestamp: 1726346409747000, + duration: 4696.875, attributes: {}, status: { code: 0 }, events: [], @@ -105,14 +105,14 @@ } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'decode', - id: '51a5859eae82dd62', + id: '5093bc556bcde250', kind: 0, - timestamp: 1726068744170000, - duration: 97.958, + timestamp: 1726346409753000, + duration: 286.459, attributes: {}, status: { code: 0 }, events: [], @@ -129,14 +129,14 @@ } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', + traceId: '62449852d475be9dce6a74f0b0555f54', parentId: undefined, traceState: undefined, name: 'request', - id: 'e3b3fd57f531674b', + id: 'b3ba100af8bce057', kind: 0, - timestamp: 1726068743834000, - duration: 336135.125, + timestamp: 1726346409547000, + duration: 205724.167, attributes: {}, status: { code: 0 }, events: [], diff --git a/examples/__outputs__/extension|extension_or-throw.output.txt b/examples/__outputs__/extension|extension_or-throw.output.txt new file mode 100644 index 000000000..4c92eae2c --- /dev/null +++ b/examples/__outputs__/extension|extension_or-throw.output.txt @@ -0,0 +1,36 @@ +---------------------------------------- SHOW ---------------------------------------- +ContextualError: There was an error in the extension "anonymous" (use named functions to improve this error message) while running hook "encode". + at runPipeline (/some/path/to/runPipeline.ts:XX:XX) + at async Object.run (/some/path/to/main.ts:XX:XX) + at async run (/some/path/to/client.ts:XX:XX) + at async executeRootType (/some/path/to/client.ts:XX:XX) + at async executeRootTypeField (/some/path/to/client.ts:XX:XX) + at async (/some/path/to/extension|extension_or-throw.ts:XX:XX) { + context: { + hookName: 'encode', + source: 'extension', + extensionName: 'anonymous' + }, + cause: Error: Something went wrong. + at (/some/path/to/extension|extension_or-throw.ts:XX:XX) + at applyBody (/some/path/to/main.ts:XX:XX) +} +---------------------------------------- SHOW ---------------------------------------- +UNCAUGHT EXCEPTION: + +ContextualError: There was an error in the extension "anonymous" (use named functions to improve this error message) while running hook "encode". + at runPipeline (/some/path/to/runPipeline.ts:XX:XX) + at async Object.run (/some/path/to/main.ts:XX:XX) + at async run (/some/path/to/client.ts:XX:XX) + at async executeRootType (/some/path/to/client.ts:XX:XX) + at async executeRootTypeField (/some/path/to/client.ts:XX:XX) + at async (/some/path/to/extension|extension_or-throw.ts:XX:XX) { + context: { + hookName: 'encode', + source: 'extension', + extensionName: 'anonymous' + }, + cause: Error: Something went wrong. + at (/some/path/to/extension|extension_or-throw.ts:XX:XX) + at applyBody (/some/path/to/main.ts:XX:XX) +} \ No newline at end of file diff --git a/examples/__outputs__/other|transport-memory.output.txt b/examples/__outputs__/other|transport-memory.output.txt index 673c0f2e2..6a9c0f055 100644 --- a/examples/__outputs__/other|transport-memory.output.txt +++ b/examples/__outputs__/other|transport-memory.output.txt @@ -1,6 +1,4 @@ ---------------------------------------- SHOW ---------------------------------------- { - "data": { - "foo": "bar" - } + "foo": "bar" } \ No newline at end of file diff --git a/examples/__outputs__/output|output_envelope.output.txt b/examples/__outputs__/output|output_envelope.output.txt index 97bde0284..ebbbd062f 100644 --- a/examples/__outputs__/output|output_envelope.output.txt +++ b/examples/__outputs__/output|output_envelope.output.txt @@ -19,7 +19,7 @@ headers: Headers { connection: 'keep-alive', 'content-length': '119', - 'x-served-by': 'cache-yul1970045-YUL', + 'x-served-by': 'cache-yul1970038-YUL', 'accept-ranges': 'bytes', date: 'Sun, 08 Sep 2024 18:13:26 GMT', 'content-type': 'application/graphql-response+json; charset=utf-8', @@ -32,13 +32,13 @@ 'alt-svc': 'h3=":443"; ma=86400', 'access-control-allow-origin': '*', 'x-powered-by': 'Stellate', - age: '249539', + age: '527204', 'cache-control': 'public, s-maxage=2628000, stale-while-revalidate=2628000', 'x-cache': 'HIT', - 'x-cache-hits': '5', + 'x-cache-hits': '33', 'gcdn-cache': 'HIT', - 'stellate-rate-limit-budget-remaining': '41', - 'stellate-rate-limit-rules': '"IP limit";type="RequestCount";budget=50;limited=?0;remaining=41;refill=60', + 'stellate-rate-limit-budget-remaining': '46', + 'stellate-rate-limit-rules': '"IP limit";type="RequestCount";budget=50;limited=?0;remaining=46;refill=60', 'stellate-rate-limit-decision': 'pass', 'stellate-rate-limit-budget-required': '5', 'content-encoding': 'br' diff --git a/examples/__outputs__/transport-http|transport-http_extension_headers__dynamicHeaders.output.txt b/examples/__outputs__/transport-http|transport-http_extension_headers__dynamicHeaders.output.txt index 647e62759..433eaf649 100644 --- a/examples/__outputs__/transport-http|transport-http_extension_headers__dynamicHeaders.output.txt +++ b/examples/__outputs__/transport-http|transport-http_extension_headers__dynamicHeaders.output.txt @@ -4,7 +4,7 @@ headers: Headers { accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', 'content-type': 'application/json', - 'x-sent-at-time': '1726068743905' + 'x-sent-at-time': '1726346409373' }, signal: undefined, method: 'post', diff --git a/examples/__outputs__/transport-http|transport-http_method-get.output.txt b/examples/__outputs__/transport-http|transport-http_method-get.output.txt index 8047d5a65..e984002f0 100644 --- a/examples/__outputs__/transport-http|transport-http_method-get.output.txt +++ b/examples/__outputs__/transport-http|transport-http_method-get.output.txt @@ -21,7 +21,7 @@ searchParams: URLSearchParams {}, hash: '' }, - body: '{"query":"mutation addPokemon(attack:0, defense:0, hp:1, name:\\"Nano\\") { name }"}' + body: '{"query":"mutation { addPokemon(attack:0, defense:0, hp:1, name:\\"Nano\\") { name } }"}' } ---------------------------------------- SHOW ---------------------------------------- { diff --git a/examples/extension|extension_opentelemetry.ts b/examples/extension|extension_opentelemetry.ts index 0329425e9..66e3f96e3 100644 --- a/examples/extension|extension_opentelemetry.ts +++ b/examples/extension|extension_opentelemetry.ts @@ -16,5 +16,5 @@ provider.addSpanProcessor(processor) provider.register() const graffle = Atlas.create().use(Opentelemetry()) -const result = await graffle.rawString({ document: `query { continents { name } }` }) -show(result.data) +const data = await graffle.rawString({ document: `query { continents { name } }` }) +show(data) diff --git a/examples/extension|extension_or-throw.ts b/examples/extension|extension_or-throw.ts new file mode 100644 index 000000000..db10dbc09 --- /dev/null +++ b/examples/extension|extension_or-throw.ts @@ -0,0 +1,22 @@ +/** + * This example shows how to use the Or Throw extension to throw errors for one-off cases. + */ + +import { OrThrow } from '../src/entrypoints/extensions.js' +import { Atlas } from './$/generated-clients/atlas/__.js' +import { interceptAndShowUncaughtErrors, show } from './$/show.js' + +interceptAndShowUncaughtErrors() + +const atlas = Atlas + .create({ output: { defaults: { errorChannel: `return` } } }) + .use(OrThrow()) + .anyware(({ encode: _ }) => { + throw new Error(`Something went wrong.`) + }) + +const result1 = await atlas.query.continents({ name: true }) +show(result1) + +const result2 = await atlas.query.continentsOrThrow({ name: true }) +result2 // This line will never be reached because of thrown error. diff --git a/examples/other|transport-memory.ts b/examples/other|transport-memory.ts index f0fd54e33..768f919b5 100644 --- a/examples/other|transport-memory.ts +++ b/examples/other|transport-memory.ts @@ -20,6 +20,6 @@ const schema = new GraphQLSchema({ const graffle = Graffle.create({ schema }) -const result = await graffle.rawString({ document: `{ foo }` }) +const data = await graffle.rawString({ document: `{ foo }` }) -showJson(result) +showJson(data) diff --git a/examples/output|output_default.ts b/examples/output|output_default.ts index af450cebc..43c7fa2df 100644 --- a/examples/output|output_default.ts +++ b/examples/output|output_default.ts @@ -7,6 +7,6 @@ import { show } from './$/helpers.js' const atlas = Atlas.create() -const result = await atlas.query.continents({ name: true }) +const continents = await atlas.query.continents({ name: true }) -show(result) +show(continents) diff --git a/examples/output|output_envelope_envelope-error__envelope-error.ts b/examples/output|output_envelope_envelope-error__envelope-error.ts index 9e9abb0bb..36d0dac71 100644 --- a/examples/output|output_envelope_envelope-error__envelope-error.ts +++ b/examples/output|output_envelope_envelope-error__envelope-error.ts @@ -18,7 +18,7 @@ const atlas = Atlas }, }, }) - .use(({ encode: _ }) => { + .anyware(({ encode: _ }) => { throw new Error(`Something went wrong.`) //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }) diff --git a/examples/output|output_envelope_envelope_error-throw__envelope-error-throw.ts b/examples/output|output_envelope_envelope_error-throw__envelope-error-throw.ts index 799c047c5..541f9d372 100644 --- a/examples/output|output_envelope_envelope_error-throw__envelope-error-throw.ts +++ b/examples/output|output_envelope_envelope_error-throw__envelope-error-throw.ts @@ -5,18 +5,20 @@ import { Atlas } from './$/generated-clients/atlas/__.js' // dprint-ignore -const atlas = Atlas.create({ - output: { - envelope: { - errors: { - execution: false, - other: false, // default - } +const atlas = Atlas + .create({ + output: { + envelope: { + errors: { + execution: false, + other: false, // default + } + }, }, - }, -}).use(({ encode: _ }) => { - throw new Error(`Something went wrong.`) -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -}) + }) + .anyware(({ encode: _ }) => { + throw new Error(`Something went wrong.`) + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + }) await atlas.query.continents({ name: true }) diff --git a/examples/output|output_return-error.ts b/examples/output|output_return-error.ts index 1320437ae..2c6aa5c22 100644 --- a/examples/output|output_return-error.ts +++ b/examples/output|output_return-error.ts @@ -16,13 +16,13 @@ const atlas = Atlas }, }) // dprint-ignore - .use(({ encode: _ }) => { + .anyware(({ encode: _ }) => { throw new Error(`Something went wrong.`) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }) -const result = await atlas.query.continents({ name: true }) -type _result = typeof result +const continents = await atlas.query.continents({ name: true }) +type _continents = typeof continents // ^? -show(result) +show(continents) diff --git a/examples/output|output_return-error_return-error-execution__return-error-execution.ts b/examples/output|output_return-error_return-error-execution__return-error-execution.ts index 6ac039f04..142f8c6a1 100644 --- a/examples/output|output_return-error_return-error-execution__return-error-execution.ts +++ b/examples/output|output_return-error_return-error-execution__return-error-execution.ts @@ -33,7 +33,7 @@ show(result) try { await pokemon - .use(({ encode: _ }) => { + .anyware(({ encode: _ }) => { throw new Error(`Something went wrong.`) }) .query diff --git a/examples/raw|raw.ts b/examples/raw|raw.ts index ff436076e..d09e06749 100644 --- a/examples/raw|raw.ts +++ b/examples/raw|raw.ts @@ -2,14 +2,17 @@ * This example shows how to send a request using a Document instance for the GraphQL document. */ +import { Opentelemetry, OrThrow } from '../src/entrypoints/extensions.js' import { gql, Graffle } from '../src/entrypoints/main.js' import { publicGraphQLSchemaEndpoints, show } from './$/helpers.js' const graffle = Graffle.create({ schema: publicGraphQLSchemaEndpoints.Atlas, }) + .use(OrThrow()) + .use(Opentelemetry()) -const result = await graffle.raw({ +const data = await graffle.raw({ document: gql` query countries ($filter: [String!]) { countries (filter: { name: { in: $filter } }) { @@ -23,4 +26,4 @@ const result = await graffle.raw({ variables: { filter: [`Canada`, `Germany`, `Japan`] }, }) -show(result.data) +show(data) diff --git a/examples/raw|raw_rawString__rawString.ts b/examples/raw|raw_rawString__rawString.ts index 5ef654e8e..aa1034de5 100644 --- a/examples/raw|raw_rawString__rawString.ts +++ b/examples/raw|raw_rawString__rawString.ts @@ -17,8 +17,6 @@ const document = /* gql */ ` } ` -const result = await graffle.rawString({ - document, -}) +const data = await graffle.rawString({ document }) -show(result.data) +show(data) diff --git a/examples/raw|raw_rawString_rawTyped__rawString-typed.ts b/examples/raw|raw_rawString_rawTyped__rawString-typed.ts index b0a12f440..43c0aeae7 100644 --- a/examples/raw|raw_rawString_rawTyped__rawString-typed.ts +++ b/examples/raw|raw_rawString_rawTyped__rawString-typed.ts @@ -33,9 +33,9 @@ const document: Document = /* gql */ ` } ` -const result = await graffle.rawString({ +const data = await graffle.rawString({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] }, }) -show(result.data?.countries) +show(data?.countries) diff --git a/examples/raw|raw_rawTyped__raw-typed.ts b/examples/raw|raw_rawTyped__raw-typed.ts index bcd9aabfb..1c7c5a66a 100644 --- a/examples/raw|raw_rawTyped__raw-typed.ts +++ b/examples/raw|raw_rawTyped__raw-typed.ts @@ -30,9 +30,9 @@ const graffle = Graffle.create({ } ` - const result = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) + const data = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) - show(result.data?.countries) + show(data?.countries) } /*************************************** Variation 2 *************************************** @@ -60,7 +60,7 @@ const graffle = Graffle.create({ } ` - const result = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) + const data = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) - show(result.data?.countries) + show(data?.countries) } diff --git a/examples/transport-http|transport-http_extension_fetch__custom-fetch.ts b/examples/transport-http|transport-http_extension_fetch__custom-fetch.ts index a70c4be2b..06cfe03f4 100644 --- a/examples/transport-http|transport-http_extension_fetch__custom-fetch.ts +++ b/examples/transport-http|transport-http_extension_fetch__custom-fetch.ts @@ -9,19 +9,16 @@ import { publicGraphQLSchemaEndpoints } from './$/helpers.js' const graffle = Graffle .create({ schema: publicGraphQLSchemaEndpoints.Atlas }) - .use({ - name: `CustomFetch`, - anyware: async ({ exchange }) => { - return await exchange({ - using: { - fetch: async () => { - return new Response(JSON.stringify({ data: { countries: [{ name: `Canada Mocked!` }] } })) - }, + .anyware(({ exchange }) => + exchange({ + using: { + fetch: async () => { + return new Response(JSON.stringify({ data: { countries: [{ name: `Canada Mocked!` }] } })) }, - }) - }, - }) + }, + }) + ) -const countries = await graffle.rawString({ document: `{ countries { name } }` }) +const data = await graffle.rawString({ document: `{ countries { name } }` }) -showJson(countries.data) +showJson(data) diff --git a/examples/transport-http|transport-http_extension_headers__dynamicHeaders.ts b/examples/transport-http|transport-http_extension_headers__dynamicHeaders.ts index 482ab0643..3a6fd6bc0 100644 --- a/examples/transport-http|transport-http_extension_headers__dynamicHeaders.ts +++ b/examples/transport-http|transport-http_extension_headers__dynamicHeaders.ts @@ -9,7 +9,7 @@ const graffle = Graffle .create({ schema: publicGraphQLSchemaEndpoints.Atlas, }) - .use(async ({ pack }) => { + .anyware(async ({ pack }) => { return await pack({ input: { ...pack.input, @@ -19,7 +19,7 @@ const graffle = Graffle }, }) }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { // todo wrong type / runtime value show(exchange.input.request) return exchange() diff --git a/examples/transport-http|transport-http_headers_raw__headers.ts b/examples/transport-http|transport-http_headers_raw__headers.ts index 3088e524a..c88d4fc07 100644 --- a/examples/transport-http|transport-http_headers_raw__headers.ts +++ b/examples/transport-http|transport-http_headers_raw__headers.ts @@ -25,7 +25,7 @@ const graffle = Graffle .with({ transport: { headers: { 'x-something-to-unset': `` } }, }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { show(exchange.input.request.headers) return exchange() }) diff --git a/examples/transport-http|transport-http_method-get.ts b/examples/transport-http|transport-http_method-get.ts index bb289403f..884f3b46a 100644 --- a/examples/transport-http|transport-http_method-get.ts +++ b/examples/transport-http|transport-http_method-get.ts @@ -14,14 +14,14 @@ const graffle = Pokemon schema: server.url, transport: { methodMode: `getReads` }, // [!code highlight] }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { show(exchange.input.request) return exchange() }) // The following request will use an HTTP POST method because it is // using a "mutation" type of operation. -await graffle.rawString({ document: `mutation addPokemon(attack:0, defense:0, hp:1, name:"Nano") { name }` }) +await graffle.rawString({ document: `mutation { addPokemon(attack:0, defense:0, hp:1, name:"Nano") { name } }` }) // The following request will use an HTTP GET method because it // is using a "query" type of operation. diff --git a/examples/transport-http|transport-http_raw.ts b/examples/transport-http|transport-http_raw.ts index 08f6ec2f9..a39410491 100644 --- a/examples/transport-http|transport-http_raw.ts +++ b/examples/transport-http|transport-http_raw.ts @@ -15,7 +15,7 @@ const graffle = Graffle }, }, }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { show(exchange.input.request) return exchange() }) diff --git a/package.json b/package.json index 0fd546ad9..e35a4c5de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graffle", - "description": "Minimalist Progressively Type Safe GraphQL Client For JavaScript.", + "description": "Simple GraphQL Client for JavaScript. Minimal. Extensible. Type Safe. Runs everywhere.", "version": "0.0.0-dripip", "packageManager": "pnpm@9.10.0", "type": "module", @@ -80,6 +80,7 @@ "build:docs": "doctoc README.md --notitle && dprint fmt README.md", "build": "pnpm clean && pnpm tsc --project tsconfig.build.json && chmod +x ./build/cli/generate.js", "clean": "tsc --build --clean && rm -rf build", + "test:unit": "vitest --exclude tests/examples", "test": "vitest", "test:web": "vitest --environment jsdom", "test:types": "vitest --typecheck", @@ -132,7 +133,7 @@ "@typescript-eslint/parser": "^8.5.0", "doctoc": "^2.2.1", "dripip": "^0.10.0", - "es-toolkit": "^1.17.0", + "es-toolkit": "^1.18.0", "eslint": "^9.10.0", "eslint-config-prisma": "^0.6.0", "eslint-plugin-deprecation": "^3.0.0", @@ -153,12 +154,12 @@ "json-bigint": "^1.0.0", "publint": "^0.2.10", "strip-ansi": "^7.1.0", - "tsx": "^4.19.0", + "tsx": "^4.19.1", "type-fest": "^4.26.1", "typescript": "^5.6.2", "typescript-eslint": "^8.5.0", "vitepress": "^1.3.4", - "vitest": "^2.0.5", + "vitest": "^2.1.1", "zod": "^3.23.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4c78a851..1010dbb08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -79,8 +79,8 @@ importers: specifier: ^0.10.0 version: 0.10.0 es-toolkit: - specifier: ^1.17.0 - version: 1.17.0 + specifier: ^1.18.0 + version: 1.18.0 eslint: specifier: ^9.10.0 version: 9.10.0 @@ -142,7 +142,7 @@ importers: specifier: ^7.1.0 version: 7.1.0 tsx: - specifier: ^4.19.0 + specifier: ^4.19.1 version: 4.19.1 type-fest: specifier: ^4.26.1 @@ -155,10 +155,10 @@ importers: version: 8.5.0(eslint@9.10.0)(typescript@5.6.2) vitepress: specifier: ^1.3.4 - version: 1.3.4(@algolia/client-search@5.3.2)(@types/node@22.5.4)(postcss@8.4.45)(search-insights@2.17.1)(typescript@5.6.2) + version: 1.3.4(@algolia/client-search@5.4.1)(@types/node@22.5.4)(postcss@8.4.45)(search-insights@2.17.2)(typescript@5.6.2) vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@22.5.4)(happy-dom@15.7.3)(jsdom@25.0.0) + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.4)(happy-dom@15.7.4)(jsdom@25.0.0) zod: specifier: ^3.23.8 version: 3.23.8 @@ -203,8 +203,8 @@ packages: '@algolia/client-common@4.24.0': resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==} - '@algolia/client-common@5.3.2': - resolution: {integrity: sha512-4OxrO3q2FNgXCuOO8hUMt5saPwmY1efNLd9zzXbADnSwQHQeuAKLC/b2DZC+i9bCXXk3uENm7dKf4nm29/H0gw==} + '@algolia/client-common@5.4.1': + resolution: {integrity: sha512-IffPD+CETiR8YJMVC1lcjnhETLpJ2L0ORZCbbRvwo/S11D1j/keR7AqKVMn4TseRJCfjmBFOcFrC+m4sXjyQWA==} engines: {node: '>= 14.0.0'} '@algolia/client-personalization@4.24.0': @@ -213,8 +213,8 @@ packages: '@algolia/client-search@4.24.0': resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==} - '@algolia/client-search@5.3.2': - resolution: {integrity: sha512-iJN3nylaSh9XuqUK0DRrRi/qwVqvA4PiWPydUB1a5NaceH7ng254GL4xwntAsX7RPIb8gQ2t65Gqoy5ToLs0kA==} + '@algolia/client-search@5.4.1': + resolution: {integrity: sha512-nCgWY2p0tZgBqJKmA5E6B3VW+7uqxi1Orf88zNWOihJBRFeOV932pzG4vGrX9l0+p0o/vJabYxuomO35rEt5dw==} engines: {node: '>= 14.0.0'} '@algolia/logger-common@4.24.0': @@ -229,27 +229,27 @@ packages: '@algolia/requester-browser-xhr@4.24.0': resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==} - '@algolia/requester-browser-xhr@5.3.2': - resolution: {integrity: sha512-5DVcWHuZZLbso2mgIsZ2KgPZGCQbgzgpjkAZZGlOc9xEEqUe3K7ZurqYKkfhYJmo7OQGoyEiqFNa71b3Yyparw==} + '@algolia/requester-browser-xhr@5.4.1': + resolution: {integrity: sha512-J6+YfU+maR0nIbsYRHoq0UpneilX97hrZzPuuvSoBojQmPo8PeCXKGeT/F0D8uFI6G4CMTKEPGmQYrC9IpCbcQ==} engines: {node: '>= 14.0.0'} '@algolia/requester-common@4.24.0': resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==} + '@algolia/requester-fetch@5.4.1': + resolution: {integrity: sha512-AO/C1pqqpIS8p2IsfM5x92S+UBKkcIen5dHfMEh1rnV0ArWDreeqrtxMD2A+6AjQVwYeZNy56w7o7PVIm6mc8g==} + engines: {node: '>= 14.0.0'} + '@algolia/requester-node-http@4.24.0': resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==} - '@algolia/requester-node-http@5.3.2': - resolution: {integrity: sha512-SgBoGAvPZRkTUkBvb4GU3qT7zVWJ947MQCdLBmZ4gkYwYkTq56NMmthMgpQY/Hfl08oOPQOILofSZnmEprqHng==} + '@algolia/requester-node-http@5.4.1': + resolution: {integrity: sha512-2Y3vffc91egwFxz0SjXFEH4q8nvlNJHcz+0//NaWItRU68AvD+3aI/j66STPjkLQOC0Ku6ckA9ChhbOVfrv+Uw==} engines: {node: '>= 14.0.0'} '@algolia/transporter@4.24.0': resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@andrewbranch/untar.js@1.0.3': resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==} @@ -720,24 +720,9 @@ packages: resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@kamilkisiela/fast-url-parser@1.1.4': resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} @@ -933,94 +918,103 @@ packages: '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - '@rollup/rollup-android-arm-eabi@4.21.2': - resolution: {integrity: sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==} + '@rollup/rollup-android-arm-eabi@4.21.3': + resolution: {integrity: sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.21.2': - resolution: {integrity: sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==} + '@rollup/rollup-android-arm64@4.21.3': + resolution: {integrity: sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.21.2': - resolution: {integrity: sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==} + '@rollup/rollup-darwin-arm64@4.21.3': + resolution: {integrity: sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.21.2': - resolution: {integrity: sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==} + '@rollup/rollup-darwin-x64@4.21.3': + resolution: {integrity: sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.21.2': - resolution: {integrity: sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==} + '@rollup/rollup-linux-arm-gnueabihf@4.21.3': + resolution: {integrity: sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.21.2': - resolution: {integrity: sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==} + '@rollup/rollup-linux-arm-musleabihf@4.21.3': + resolution: {integrity: sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.21.2': - resolution: {integrity: sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==} + '@rollup/rollup-linux-arm64-gnu@4.21.3': + resolution: {integrity: sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.21.2': - resolution: {integrity: sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==} + '@rollup/rollup-linux-arm64-musl@4.21.3': + resolution: {integrity: sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.21.2': - resolution: {integrity: sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.21.3': + resolution: {integrity: sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.21.2': - resolution: {integrity: sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==} + '@rollup/rollup-linux-riscv64-gnu@4.21.3': + resolution: {integrity: sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.21.2': - resolution: {integrity: sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==} + '@rollup/rollup-linux-s390x-gnu@4.21.3': + resolution: {integrity: sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.21.2': - resolution: {integrity: sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==} + '@rollup/rollup-linux-x64-gnu@4.21.3': + resolution: {integrity: sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.21.2': - resolution: {integrity: sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==} + '@rollup/rollup-linux-x64-musl@4.21.3': + resolution: {integrity: sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.21.2': - resolution: {integrity: sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==} + '@rollup/rollup-win32-arm64-msvc@4.21.3': + resolution: {integrity: sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.21.2': - resolution: {integrity: sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==} + '@rollup/rollup-win32-ia32-msvc@4.21.3': + resolution: {integrity: sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.21.2': - resolution: {integrity: sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==} + '@rollup/rollup-win32-x64-msvc@4.21.3': + resolution: {integrity: sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==} cpu: [x64] os: [win32] '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@shikijs/core@1.16.3': - resolution: {integrity: sha512-yETIvrETCeC39gSPIiSADmjri9FwKmxz0QvONMtTIUYlKZe90CJkvcjPksayC2VQOtzOJonEiULUa8v8crUQvA==} + '@shikijs/core@1.17.6': + resolution: {integrity: sha512-9ztslig6/YmCg/XwESAXbKjAjOhaq6HVced9NY6qcbDz1X5g/S90Wco2vMjBNX/6V71ASkzri76JewSGPa7kiQ==} + + '@shikijs/engine-javascript@1.17.6': + resolution: {integrity: sha512-5EEZj8tVcierNxm4V0UMS2PVoflb0UJPalWWV8l9rRg+oOfnr5VivqBJbkyq5grltVPvByIXvVbY8GSM/356jQ==} + + '@shikijs/engine-oniguruma@1.17.6': + resolution: {integrity: sha512-NLfWDMXFYe0nDHFbEoyZdz89aIIey3bTfF3zLYSUNTXks5s4uinZVmuPOFf1HfTeGqIn8uErJSBc3VnpJO7Alw==} - '@shikijs/transformers@1.16.3': - resolution: {integrity: sha512-bu4IcpUWmch4NvIWQgyMk2r9sH1XNZjUFgu56d3TPD1wLmBB/krctzVYgmurQ45X4dBEpNZdNvdG3v5B27taSw==} + '@shikijs/transformers@1.17.6': + resolution: {integrity: sha512-k2VAfbm19bF0ofPaEdWSQJrzT47ukCQcajWQsiGMwcH9DFBFFpFHzWTuwagmxwywAClCZiBkhP6U3uxngbJgRw==} + + '@shikijs/types@1.17.6': + resolution: {integrity: sha512-ndTFa2TJi2w51ddKQDn3Jy8f6K4E5Q2x3dA3Hmsd3+YmxDQ10UWHjcw7VbVbKzv3VcUvYPLy+z9neqytSzUMUg==} '@shikijs/vscode-textmate@9.2.2': resolution: {integrity: sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==} @@ -1100,6 +1094,9 @@ packages: '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} @@ -1302,6 +1299,9 @@ packages: resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@vitejs/plugin-vue@5.1.3': resolution: {integrity: sha512-3xbWsKEKXYlmX82aOHufFQVnkbMC/v8fLpWwh6hWOUrK5fbbtBh9Q/WWse27BFgSy2/e2c0fz5Scgya9h2GLhw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1309,61 +1309,73 @@ packages: vite: ^5.0.0 vue: ^3.2.25 - '@vitest/expect@2.0.5': - resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + '@vitest/expect@2.1.1': + resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} - '@vitest/pretty-format@2.0.5': - resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} + '@vitest/mocker@2.1.1': + resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} + peerDependencies: + '@vitest/spy': 2.1.1 + msw: ^2.3.5 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.1': + resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} - '@vitest/runner@2.0.5': - resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} + '@vitest/runner@2.1.1': + resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} - '@vitest/snapshot@2.0.5': - resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + '@vitest/snapshot@2.1.1': + resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} - '@vitest/spy@2.0.5': - resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/spy@2.1.1': + resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} - '@vitest/utils@2.0.5': - resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + '@vitest/utils@2.1.1': + resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} - '@vue/compiler-core@3.5.4': - resolution: {integrity: sha512-oNwn+BAt3n9dK9uAYvI+XGlutwuTq/wfj4xCBaZCqwwVIGtD7D6ViihEbyYZrDHIHTDE3Q6oL3/hqmAyFEy9DQ==} + '@vue/compiler-core@3.5.5': + resolution: {integrity: sha512-ZrxcY8JMoV+kgDrmRwlDufz0SjDZ7jfoNZiIBluAACMBmgr55o/jTbxnyrccH6VSJXnFaDI4Ik1UFCiq9r8i7w==} - '@vue/compiler-dom@3.5.4': - resolution: {integrity: sha512-yP9RRs4BDLOLfldn6ah+AGCNovGjMbL9uHvhDHf5wan4dAHLnFGOkqtfE7PPe4HTXIqE7l/NILdYw53bo1C8jw==} + '@vue/compiler-dom@3.5.5': + resolution: {integrity: sha512-HSvK5q1gmBbxRse3S0Wt34RcKuOyjDJKDDMuF3i7NC+QkDFrbAqw8NnrEm/z7zFDxWZa4/5eUwsBOMQzm1RHBA==} - '@vue/compiler-sfc@3.5.4': - resolution: {integrity: sha512-P+yiPhL+NYH7m0ZgCq7AQR2q7OIE+mpAEgtkqEeH9oHSdIRvUO+4X6MPvblJIWcoe4YC5a2Gdf/RsoyP8FFiPQ==} + '@vue/compiler-sfc@3.5.5': + resolution: {integrity: sha512-MzBHDxwZhgQPHrwJ5tj92gdTYRCuPDSZr8PY3+JFv8cv2UD5/WayH5yo0kKCkKfrtJhc39jNSMityHrkMSbfnA==} - '@vue/compiler-ssr@3.5.4': - resolution: {integrity: sha512-acESdTXsxPnYr2C4Blv0ggx5zIFMgOzZmYU2UgvIff9POdRGbRNBHRyzHAnizcItvpgerSKQbllUc9USp3V7eg==} + '@vue/compiler-ssr@3.5.5': + resolution: {integrity: sha512-oFasHnpv/upubjJEmqiTKQYb4qS3ziJddf4UVWuFw6ebk/QTrTUc+AUoTJdo39x9g+AOQBzhOU0ICCRuUjvkmw==} - '@vue/devtools-api@7.4.4': - resolution: {integrity: sha512-Iqqy9yBFWBbPb/jHlJzU/OrU+iHSJ/e9p/v5pZhm/L5pUCX26z32bvvjPa28vMXxRehbAZTgX8zovOeqBTnhdg==} + '@vue/devtools-api@7.4.5': + resolution: {integrity: sha512-PX9uXirHOY2P99kb1cP3DxWZojFW3acNMqd+l4i5nKcqY59trXTOfwDZXt2Qifu0OU1izAQb76Ur6NPVldF2KQ==} - '@vue/devtools-kit@7.4.4': - resolution: {integrity: sha512-awK/4NfsUG0nQ7qnTM37m7ZkEUMREyPh8taFCX+uQYps/MTFEum0AD05VeGDRMXwWvMmGIcWX9xp8ZiBddY0jw==} + '@vue/devtools-kit@7.4.5': + resolution: {integrity: sha512-Uuki4Z6Bc/ExvtlPkeDNGSAe4580R+HPcVABfTE9TF7BTz3Nntk7vxIRUyWblZkUEcB/x+wn2uofyt5i2LaUew==} - '@vue/devtools-shared@7.4.4': - resolution: {integrity: sha512-yeJULXFHOKIm8yL2JFO050a9ztTVqOCKTqN9JHFxGTJN0b+gjtfn6zC+FfyHUgjwCwf6E3hfKrlohtthcqoYqw==} + '@vue/devtools-shared@7.4.5': + resolution: {integrity: sha512-2XgUOkL/7QDmyYI9J7cm+rz/qBhcGv+W5+i1fhwdQ0HQ1RowhdK66F0QBuJSz/5k12opJY8eN6m03/XZMs7imQ==} - '@vue/reactivity@3.5.4': - resolution: {integrity: sha512-HKKbEuP7tYSGCq4e4nK6ZW6l5hyG66OUetefBp4budUyjvAYsnQDf+bgFzg2RAgnH0CInyqXwD9y47jwJEHrQw==} + '@vue/reactivity@3.5.5': + resolution: {integrity: sha512-V4tTWElZQhT73PSK3Wnax9R9m4qvMX+LeKHnfylZc6SLh4Jc5/BPakp6e3zEhKWi5AN8TDzRkGnLkp8OqycYng==} - '@vue/runtime-core@3.5.4': - resolution: {integrity: sha512-f3ek2sTA0AFu0n+w+kCtz567Euqqa3eHewvo4klwS7mWfSj/A+UmYTwsnUFo35KeyAFY60JgrCGvEBsu1n/3LA==} + '@vue/runtime-core@3.5.5': + resolution: {integrity: sha512-2/CFaRN17jgsXy4MpigWFBCAMmLkXPb4CjaHrndglwYSra7ajvkH2cat21dscuXaH91G8fXAeg5gCyxWJ+wCRA==} - '@vue/runtime-dom@3.5.4': - resolution: {integrity: sha512-ofyc0w6rbD5KtjhP1i9hGOKdxGpvmuB1jprP7Djlj0X7R5J/oLwuNuE98GJ8WW31Hu2VxQHtk/LYTAlW8xrJdw==} + '@vue/runtime-dom@3.5.5': + resolution: {integrity: sha512-0bQGgCuL+4Muz5PsCLgF4Ata9BTdhHi5VjsxtTDyI0Wy4MgoSvBGaA6bDc7W7CGgZOyirf9LNeetMYHQ05pgpw==} - '@vue/server-renderer@3.5.4': - resolution: {integrity: sha512-FbjV6DJLgKRetMYFBA1UXCroCiED/Ckr53/ba9wivyd7D/Xw9fpo0T6zXzCnxQwyvkyrL7y6plgYhWhNjGxY5g==} + '@vue/server-renderer@3.5.5': + resolution: {integrity: sha512-XjRamLIq5f47cxgy+hiX7zUIY+4RHdPDVrPvvMDAUTdW5RJWX/S0ji/rCbm3LWTT/9Co9bvQME8ZI15ahL4/Qw==} peerDependencies: - vue: 3.5.4 + vue: 3.5.5 - '@vue/shared@3.5.4': - resolution: {integrity: sha512-L2MCDD8l7yC62Te5UUyPVpmexhL9ipVnYRw9CsWfm/BGRL5FwDX4a25bcJ/OJSD3+Hx+k/a8LDKcG2AFdJV3BA==} + '@vue/shared@3.5.5': + resolution: {integrity: sha512-0KyMXyEgnmFAs6rNUL+6eUHtUCqCaNrVd+AW3MX3LyA0Yry5SA0Km03CDKiOua1x1WWnIr+W9+S0GMFoSDWERQ==} '@vueuse/core@11.0.3': resolution: {integrity: sha512-RENlh64+SYA9XMExmmH1a3TPqeIuJBNNB/63GT35MZI+zpru3oMRUA6cEFr9HmGqEgUisurwGwnIieF6qu3aXw==} @@ -1579,6 +1591,9 @@ packages: ccount@1.1.0: resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} engines: {node: '>=12'} @@ -1595,9 +1610,15 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + character-entities-legacy@1.1.4: resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@1.2.4: resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} @@ -1641,6 +1662,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -1757,6 +1781,13 @@ packages: deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff3@0.0.3: resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==} @@ -1854,8 +1885,8 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - es-toolkit@1.17.0: - resolution: {integrity: sha512-aJvpNxK7d+I+Rt9tmdwzelxTe4EwtxX1Kv0xv6ZTRWJBpMCxe0vxTLLW4STz6pHYjtyTTrEkonNXLaBsYHg2Yw==} + es-toolkit@1.18.0: + resolution: {integrity: sha512-J/KKeqozMvgpPSUqZSpt59ZOTku2UrU9cmqmLBt4TmFQ2KCLGf3fXK6Xb2ud5CrpCUHFl/ThijjJkXwa/8sf2g==} esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} @@ -2091,10 +2122,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - execa@9.3.1: resolution: {integrity: sha512-gdhefCCNy/8tpH/2+ajP9IQc14vXchNdd0weyzSJEFURhRMGncQ+zKFxwjAufIewPEJm9BPOaJnvg2UtlH2gPQ==} engines: {node: ^18.19.0 || >=20.5.0} @@ -2164,8 +2191,8 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - focus-trap@7.5.4: - resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} + focus-trap@7.6.0: + resolution: {integrity: sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==} for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -2232,10 +2259,6 @@ packages: resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==} engines: {node: '>=4'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - get-stream@9.0.1: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} @@ -2244,8 +2267,8 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.0: - resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} @@ -2324,8 +2347,8 @@ packages: resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - happy-dom@15.7.3: - resolution: {integrity: sha512-w3RUaYNXFJX5LiNVhOJLK4GqCB1bFj1FvELtpon3HrN8gUpS09V0Vvm4/BBRRj7mLUE1+ch8PKv1JxEp/0IHjA==} + happy-dom@15.7.4: + resolution: {integrity: sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ==} engines: {node: '>=18.0.0'} har-schema@2.0.0: @@ -2367,6 +2390,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-html@9.0.2: + resolution: {integrity: sha512-RP5wNpj5nm1Z8cloDv4Sl4RS8jH5HYa0v93YB6Wb4poEzgMo/dAAL0KcT4974dCjcNG5pkLqTImeFHHCwwfY3g==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -2380,6 +2409,9 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@7.2.0: resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} @@ -2395,10 +2427,6 @@ packages: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - human-signals@8.0.0: resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==} engines: {node: '>=18.18.0'} @@ -2543,10 +2571,6 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-stream@4.0.1: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} @@ -2744,15 +2768,15 @@ packages: mdast-util-gfm@0.1.2: resolution: {integrity: sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==} + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-markdown@0.6.5: resolution: {integrity: sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==} mdast-util-to-string@2.0.0: resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2781,6 +2805,21 @@ packages: micromark-extension-gfm@0.3.3: resolution: {integrity: sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==} + micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + + micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + + micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + + micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + + micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + micromark@2.11.4: resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} @@ -2796,10 +2835,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -2910,12 +2945,8 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - oniguruma-to-js@0.3.3: - resolution: {integrity: sha512-m90/WEhgs8g4BxG37+Nu3YrMfJDs2YXtYtIllhsEPR+wP3+K4EZk6dDUvy2v2K4MNFDDOYKL4/yqYPXDqyozTQ==} + oniguruma-to-js@0.4.3: + resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -3075,6 +3106,9 @@ packages: resolution: {integrity: sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==} engines: {node: '>=18'} + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -3189,8 +3223,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rollup@4.21.2: - resolution: {integrity: sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==} + rollup@4.21.3: + resolution: {integrity: sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3222,8 +3256,8 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - search-insights@2.17.1: - resolution: {integrity: sha512-HHFjYH/0AqXacETlIbe9EYc3UNlQYGNNTY0fZ/sWl6SweX+GDxq9NB5+RVoPLgEFuOtCz7M9dhYxqDnhbbF0eQ==} + search-insights@2.17.2: + resolution: {integrity: sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==} semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} @@ -3258,8 +3292,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@1.16.3: - resolution: {integrity: sha512-GypUE+fEd06FqDs63LSAVlmq7WsahhPQU62cgZxGF+TJT5LjD2k7HTxXj4/CKOVuMM3+wWQ1t4Y5oooeJFRRBQ==} + shiki@1.17.6: + resolution: {integrity: sha512-RejGugKpDM75vh6YtF9R771acxHRDikC/01kxsUGW+Pnaz3pTY+c8aZB5CnD7p0vuFPs1HaoAIU/4E+NCfS+mQ==} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} @@ -3297,6 +3331,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -3350,6 +3387,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3362,10 +3402,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-final-newline@4.0.0: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} @@ -3409,6 +3445,9 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinypool@1.0.1: resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3444,10 +3483,13 @@ packages: resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} - traverse@0.6.9: - resolution: {integrity: sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==} + traverse@0.6.10: + resolution: {integrity: sha512-hN4uFRxbK+PX56DxYiGHsTn2dME3TVr9vbNqlQGcGcPhJAn+tdP126iA+TArMpI4YSgnTkMWyoLl5bf81Hi5TA==} engines: {node: '>= 0.4'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + trough@1.0.5: resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} @@ -3571,12 +3613,27 @@ packages: unist-util-is@4.1.0: resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-visit-parents@3.1.1: resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} @@ -3626,16 +3683,22 @@ packages: vfile-message@2.0.4: resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + vfile@4.2.1: resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} - vite-node@2.0.5: - resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-node@2.1.1: + resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite@5.4.3: - resolution: {integrity: sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==} + vite@5.4.5: + resolution: {integrity: sha512-pXqR0qtb2bTwLkev4SE3r4abCNioP3GkjvIDLlzziPpXtHgiJIjuKl+1GN6ESOT3wMjG3JTeARopj2SwYaHTOA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3677,15 +3740,15 @@ packages: postcss: optional: true - vitest@2.0.5: - resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} + vitest@2.1.1: + resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.5 - '@vitest/ui': 2.0.5 + '@vitest/browser': 2.1.1 + '@vitest/ui': 2.1.1 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -3713,8 +3776,8 @@ packages: '@vue/composition-api': optional: true - vue@3.5.4: - resolution: {integrity: sha512-3yAj2gkmiY+i7+22A1PWM+kjOVXjU74UPINcTiN7grIVPyFFI0lpGwHlV/4xydDmobaBn7/xmi+YG8HeSlCTcg==} + vue@3.5.5: + resolution: {integrity: sha512-ybC+xn67K4+df1yVeov4UjBGyVcXM0a1g7JVZr+pWVUX3xF6ntXU0wIjkTkduZBUIpxTlsftJSxz2kwhsT7dgA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -3832,34 +3895,37 @@ packages: zwitch@1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: - '@algolia/autocomplete-core@1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0)(search-insights@2.17.1)': + '@algolia/autocomplete-core@1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0)(search-insights@2.17.2)': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0)(search-insights@2.17.1) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0) + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0)(search-insights@2.17.2) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0)(search-insights@2.17.1)': + '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0)(search-insights@2.17.2)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0) - search-insights: 2.17.1 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0) + search-insights: 2.17.2 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0)': + '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0) - '@algolia/client-search': 5.3.2 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0) + '@algolia/client-search': 5.4.1 algoliasearch: 4.24.0 - '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0)': + '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0)': dependencies: - '@algolia/client-search': 5.3.2 + '@algolia/client-search': 5.4.1 algoliasearch: 4.24.0 '@algolia/cache-browser-local-storage@4.24.0': @@ -3890,7 +3956,7 @@ snapshots: '@algolia/requester-common': 4.24.0 '@algolia/transporter': 4.24.0 - '@algolia/client-common@5.3.2': {} + '@algolia/client-common@5.4.1': {} '@algolia/client-personalization@4.24.0': dependencies: @@ -3904,11 +3970,12 @@ snapshots: '@algolia/requester-common': 4.24.0 '@algolia/transporter': 4.24.0 - '@algolia/client-search@5.3.2': + '@algolia/client-search@5.4.1': dependencies: - '@algolia/client-common': 5.3.2 - '@algolia/requester-browser-xhr': 5.3.2 - '@algolia/requester-node-http': 5.3.2 + '@algolia/client-common': 5.4.1 + '@algolia/requester-browser-xhr': 5.4.1 + '@algolia/requester-fetch': 5.4.1 + '@algolia/requester-node-http': 5.4.1 '@algolia/logger-common@4.24.0': {} @@ -3934,19 +4001,23 @@ snapshots: dependencies: '@algolia/requester-common': 4.24.0 - '@algolia/requester-browser-xhr@5.3.2': + '@algolia/requester-browser-xhr@5.4.1': dependencies: - '@algolia/client-common': 5.3.2 + '@algolia/client-common': 5.4.1 '@algolia/requester-common@4.24.0': {} + '@algolia/requester-fetch@5.4.1': + dependencies: + '@algolia/client-common': 5.4.1 + '@algolia/requester-node-http@4.24.0': dependencies: '@algolia/requester-common': 4.24.0 - '@algolia/requester-node-http@5.3.2': + '@algolia/requester-node-http@5.4.1': dependencies: - '@algolia/client-common': 5.3.2 + '@algolia/client-common': 5.4.1 '@algolia/transporter@4.24.0': dependencies: @@ -3954,11 +4025,6 @@ snapshots: '@algolia/logger-common': 4.24.0 '@algolia/requester-common': 4.24.0 - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - '@andrewbranch/untar.js@1.0.3': {} '@arethetypeswrong/cli@0.16.2': @@ -4000,9 +4066,9 @@ snapshots: '@docsearch/css@3.6.1': {} - '@docsearch/js@3.6.1(@algolia/client-search@5.3.2)(search-insights@2.17.1)': + '@docsearch/js@3.6.1(@algolia/client-search@5.4.1)(search-insights@2.17.2)': dependencies: - '@docsearch/react': 3.6.1(@algolia/client-search@5.3.2)(search-insights@2.17.1) + '@docsearch/react': 3.6.1(@algolia/client-search@5.4.1)(search-insights@2.17.2) preact: 10.23.2 transitivePeerDependencies: - '@algolia/client-search' @@ -4011,14 +4077,14 @@ snapshots: - react-dom - search-insights - '@docsearch/react@3.6.1(@algolia/client-search@5.3.2)(search-insights@2.17.1)': + '@docsearch/react@3.6.1(@algolia/client-search@5.4.1)(search-insights@2.17.2)': dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0)(search-insights@2.17.1) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@5.3.2)(algoliasearch@4.24.0) + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0)(search-insights@2.17.2) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@5.4.1)(algoliasearch@4.24.0) '@docsearch/css': 3.6.1 algoliasearch: 4.24.0 optionalDependencies: - search-insights: 2.17.1 + search-insights: 2.17.2 transitivePeerDependencies: - '@algolia/client-search' @@ -4295,23 +4361,8 @@ snapshots: '@humanwhocodes/retry@0.3.0': {} - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - '@kamilkisiela/fast-url-parser@1.1.4': {} '@kwsites/file-exists@1.1.1': @@ -4611,66 +4662,83 @@ snapshots: '@repeaterjs/repeater@3.0.6': {} - '@rollup/rollup-android-arm-eabi@4.21.2': + '@rollup/rollup-android-arm-eabi@4.21.3': optional: true - '@rollup/rollup-android-arm64@4.21.2': + '@rollup/rollup-android-arm64@4.21.3': optional: true - '@rollup/rollup-darwin-arm64@4.21.2': + '@rollup/rollup-darwin-arm64@4.21.3': optional: true - '@rollup/rollup-darwin-x64@4.21.2': + '@rollup/rollup-darwin-x64@4.21.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.21.2': + '@rollup/rollup-linux-arm-gnueabihf@4.21.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.21.2': + '@rollup/rollup-linux-arm-musleabihf@4.21.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.21.2': + '@rollup/rollup-linux-arm64-gnu@4.21.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.21.2': + '@rollup/rollup-linux-arm64-musl@4.21.3': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.21.2': + '@rollup/rollup-linux-powerpc64le-gnu@4.21.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.21.2': + '@rollup/rollup-linux-riscv64-gnu@4.21.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.21.2': + '@rollup/rollup-linux-s390x-gnu@4.21.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.21.2': + '@rollup/rollup-linux-x64-gnu@4.21.3': optional: true - '@rollup/rollup-linux-x64-musl@4.21.2': + '@rollup/rollup-linux-x64-musl@4.21.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.21.2': + '@rollup/rollup-win32-arm64-msvc@4.21.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.21.2': + '@rollup/rollup-win32-ia32-msvc@4.21.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.21.2': + '@rollup/rollup-win32-x64-msvc@4.21.3': optional: true '@sec-ant/readable-stream@0.4.1': {} - '@shikijs/core@1.16.3': + '@shikijs/core@1.17.6': dependencies: + '@shikijs/engine-javascript': 1.17.6 + '@shikijs/engine-oniguruma': 1.17.6 + '@shikijs/types': 1.17.6 '@shikijs/vscode-textmate': 9.2.2 '@types/hast': 3.0.4 - oniguruma-to-js: 0.3.3 - regex: 4.3.2 + hast-util-to-html: 9.0.2 - '@shikijs/transformers@1.16.3': + '@shikijs/engine-javascript@1.17.6': dependencies: - shiki: 1.16.3 + '@shikijs/types': 1.17.6 + oniguruma-to-js: 0.4.3 + + '@shikijs/engine-oniguruma@1.17.6': + dependencies: + '@shikijs/types': 1.17.6 + '@shikijs/vscode-textmate': 9.2.2 + + '@shikijs/transformers@1.17.6': + dependencies: + shiki: 1.17.6 + + '@shikijs/types@1.17.6': + dependencies: + '@shikijs/vscode-textmate': 9.2.2 + '@types/hast': 3.0.4 '@shikijs/vscode-textmate@9.2.2': {} @@ -4691,7 +4759,7 @@ snapshots: remark-frontmatter: 3.0.0 remark-gfm: 1.0.0 remark-parse: 9.0.0 - traverse: 0.6.9 + traverse: 0.6.10 unified: 9.2.2 transitivePeerDependencies: - supports-color @@ -4763,6 +4831,10 @@ snapshots: dependencies: '@types/unist': 2.0.11 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/mdurl@2.0.0': {} '@types/mime@1.3.5': {} @@ -5030,81 +5102,90 @@ snapshots: '@typescript-eslint/types': 8.5.0 eslint-visitor-keys: 3.4.3 - '@vitejs/plugin-vue@5.1.3(vite@5.4.3(@types/node@22.5.4))(vue@3.5.4(typescript@5.6.2))': + '@ungap/structured-clone@1.2.0': {} + + '@vitejs/plugin-vue@5.1.3(vite@5.4.5(@types/node@22.5.4))(vue@3.5.5(typescript@5.6.2))': dependencies: - vite: 5.4.3(@types/node@22.5.4) - vue: 3.5.4(typescript@5.6.2) + vite: 5.4.5(@types/node@22.5.4) + vue: 3.5.5(typescript@5.6.2) - '@vitest/expect@2.0.5': + '@vitest/expect@2.1.1': dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/pretty-format@2.0.5': + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.5(@types/node@22.5.4))': + dependencies: + '@vitest/spy': 2.1.1 + estree-walker: 3.0.3 + magic-string: 0.30.11 + optionalDependencies: + vite: 5.4.5(@types/node@22.5.4) + + '@vitest/pretty-format@2.1.1': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.0.5': + '@vitest/runner@2.1.1': dependencies: - '@vitest/utils': 2.0.5 + '@vitest/utils': 2.1.1 pathe: 1.1.2 - '@vitest/snapshot@2.0.5': + '@vitest/snapshot@2.1.1': dependencies: - '@vitest/pretty-format': 2.0.5 + '@vitest/pretty-format': 2.1.1 magic-string: 0.30.11 pathe: 1.1.2 - '@vitest/spy@2.0.5': + '@vitest/spy@2.1.1': dependencies: tinyspy: 3.0.2 - '@vitest/utils@2.0.5': + '@vitest/utils@2.1.1': dependencies: - '@vitest/pretty-format': 2.0.5 - estree-walker: 3.0.3 + '@vitest/pretty-format': 2.1.1 loupe: 3.1.1 tinyrainbow: 1.2.0 - '@vue/compiler-core@3.5.4': + '@vue/compiler-core@3.5.5': dependencies: '@babel/parser': 7.25.6 - '@vue/shared': 3.5.4 + '@vue/shared': 3.5.5 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.4': + '@vue/compiler-dom@3.5.5': dependencies: - '@vue/compiler-core': 3.5.4 - '@vue/shared': 3.5.4 + '@vue/compiler-core': 3.5.5 + '@vue/shared': 3.5.5 - '@vue/compiler-sfc@3.5.4': + '@vue/compiler-sfc@3.5.5': dependencies: '@babel/parser': 7.25.6 - '@vue/compiler-core': 3.5.4 - '@vue/compiler-dom': 3.5.4 - '@vue/compiler-ssr': 3.5.4 - '@vue/shared': 3.5.4 + '@vue/compiler-core': 3.5.5 + '@vue/compiler-dom': 3.5.5 + '@vue/compiler-ssr': 3.5.5 + '@vue/shared': 3.5.5 estree-walker: 2.0.2 magic-string: 0.30.11 postcss: 8.4.45 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.4': + '@vue/compiler-ssr@3.5.5': dependencies: - '@vue/compiler-dom': 3.5.4 - '@vue/shared': 3.5.4 + '@vue/compiler-dom': 3.5.5 + '@vue/shared': 3.5.5 - '@vue/devtools-api@7.4.4': + '@vue/devtools-api@7.4.5': dependencies: - '@vue/devtools-kit': 7.4.4 + '@vue/devtools-kit': 7.4.5 - '@vue/devtools-kit@7.4.4': + '@vue/devtools-kit@7.4.5': dependencies: - '@vue/devtools-shared': 7.4.4 + '@vue/devtools-shared': 7.4.5 birpc: 0.2.17 hookable: 5.5.3 mitt: 3.0.1 @@ -5112,60 +5193,60 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.1 - '@vue/devtools-shared@7.4.4': + '@vue/devtools-shared@7.4.5': dependencies: rfdc: 1.4.1 - '@vue/reactivity@3.5.4': + '@vue/reactivity@3.5.5': dependencies: - '@vue/shared': 3.5.4 + '@vue/shared': 3.5.5 - '@vue/runtime-core@3.5.4': + '@vue/runtime-core@3.5.5': dependencies: - '@vue/reactivity': 3.5.4 - '@vue/shared': 3.5.4 + '@vue/reactivity': 3.5.5 + '@vue/shared': 3.5.5 - '@vue/runtime-dom@3.5.4': + '@vue/runtime-dom@3.5.5': dependencies: - '@vue/reactivity': 3.5.4 - '@vue/runtime-core': 3.5.4 - '@vue/shared': 3.5.4 + '@vue/reactivity': 3.5.5 + '@vue/runtime-core': 3.5.5 + '@vue/shared': 3.5.5 csstype: 3.1.3 - '@vue/server-renderer@3.5.4(vue@3.5.4(typescript@5.6.2))': + '@vue/server-renderer@3.5.5(vue@3.5.5(typescript@5.6.2))': dependencies: - '@vue/compiler-ssr': 3.5.4 - '@vue/shared': 3.5.4 - vue: 3.5.4(typescript@5.6.2) + '@vue/compiler-ssr': 3.5.5 + '@vue/shared': 3.5.5 + vue: 3.5.5(typescript@5.6.2) - '@vue/shared@3.5.4': {} + '@vue/shared@3.5.5': {} - '@vueuse/core@11.0.3(vue@3.5.4(typescript@5.6.2))': + '@vueuse/core@11.0.3(vue@3.5.5(typescript@5.6.2))': dependencies: '@types/web-bluetooth': 0.0.20 '@vueuse/metadata': 11.0.3 - '@vueuse/shared': 11.0.3(vue@3.5.4(typescript@5.6.2)) - vue-demi: 0.14.10(vue@3.5.4(typescript@5.6.2)) + '@vueuse/shared': 11.0.3(vue@3.5.5(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.5(typescript@5.6.2)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/integrations@11.0.3(focus-trap@7.5.4)(vue@3.5.4(typescript@5.6.2))': + '@vueuse/integrations@11.0.3(focus-trap@7.6.0)(vue@3.5.5(typescript@5.6.2))': dependencies: - '@vueuse/core': 11.0.3(vue@3.5.4(typescript@5.6.2)) - '@vueuse/shared': 11.0.3(vue@3.5.4(typescript@5.6.2)) - vue-demi: 0.14.10(vue@3.5.4(typescript@5.6.2)) + '@vueuse/core': 11.0.3(vue@3.5.5(typescript@5.6.2)) + '@vueuse/shared': 11.0.3(vue@3.5.5(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.5(typescript@5.6.2)) optionalDependencies: - focus-trap: 7.5.4 + focus-trap: 7.6.0 transitivePeerDependencies: - '@vue/composition-api' - vue '@vueuse/metadata@11.0.3': {} - '@vueuse/shared@11.0.3(vue@3.5.4(typescript@5.6.2))': + '@vueuse/shared@11.0.3(vue@3.5.5(typescript@5.6.2))': dependencies: - vue-demi: 0.14.10(vue@3.5.4(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.5(typescript@5.6.2)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -5363,6 +5444,8 @@ snapshots: ccount@1.1.0: {} + ccount@2.0.1: {} + chai@5.1.1: dependencies: assertion-error: 2.0.1 @@ -5380,8 +5463,12 @@ snapshots: char-regex@1.0.2: {} + character-entities-html4@2.1.0: {} + character-entities-legacy@1.1.4: {} + character-entities-legacy@3.0.0: {} + character-entities@1.2.4: {} character-reference-invalid@1.1.4: {} @@ -5427,6 +5514,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} comment-parser@1.1.5: {} @@ -5524,6 +5613,12 @@ snapshots: deprecation@2.3.1: {} + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + diff3@0.0.3: {} dir-glob@3.0.1: @@ -5696,7 +5791,7 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - es-toolkit@1.17.0: {} + es-toolkit@1.18.0: {} esbuild@0.21.5: optionalDependencies: @@ -6033,18 +6128,6 @@ snapshots: esutils@2.0.3: {} - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - execa@9.3.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 @@ -6124,7 +6207,7 @@ snapshots: flatted@3.3.1: {} - focus-trap@7.5.4: + focus-trap@7.6.0: dependencies: tabbable: 6.2.0 @@ -6193,8 +6276,6 @@ snapshots: get-stdin@6.0.0: {} - get-stream@8.0.1: {} - get-stream@9.0.1: dependencies: '@sec-ant/readable-stream': 0.4.1 @@ -6206,7 +6287,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.4 - get-tsconfig@4.8.0: + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -6306,7 +6387,7 @@ snapshots: graphql@16.9.0: {} - happy-dom@15.7.3: + happy-dom@15.7.4: dependencies: entities: 4.5.0 webidl-conversions: 7.0.0 @@ -6342,6 +6423,24 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-html@9.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + highlight.js@10.7.3: {} hookable@5.5.3: {} @@ -6352,6 +6451,8 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 + html-void-elements@3.0.0: {} + htmlparser2@7.2.0: dependencies: domelementtype: 2.3.0 @@ -6379,8 +6480,6 @@ snapshots: transitivePeerDependencies: - supports-color - human-signals@5.0.0: {} - human-signals@8.0.0: {} iconv-lite@0.6.3: @@ -6497,8 +6596,6 @@ snapshots: dependencies: call-bind: 1.0.7 - is-stream@3.0.0: {} - is-stream@4.0.1: {} is-string@1.0.7: @@ -6741,6 +6838,18 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.0 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + mdast-util-to-markdown@0.6.5: dependencies: '@types/unist': 2.0.11 @@ -6752,8 +6861,6 @@ snapshots: mdast-util-to-string@2.0.0: {} - merge-stream@2.0.0: {} - merge2@1.4.1: {} micromark-extension-footnote@0.3.2: @@ -6803,6 +6910,23 @@ snapshots: transitivePeerDependencies: - supports-color + micromark-util-character@2.1.0: + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-util-encode@2.0.0: {} + + micromark-util-sanitize-uri@2.0.0: + dependencies: + micromark-util-character: 2.1.0 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + + micromark-util-symbol@2.0.0: {} + + micromark-util-types@2.0.0: {} + micromark@2.11.4: dependencies: debug: 4.3.7 @@ -6821,8 +6945,6 @@ snapshots: dependencies: mime-db: 1.52.0 - mimic-fn@4.0.0: {} - mimic-response@3.1.0: {} minimatch@3.1.2: @@ -6925,11 +7047,9 @@ snapshots: dependencies: wrappy: 1.0.2 - onetime@6.0.0: + oniguruma-to-js@0.4.3: dependencies: - mimic-fn: 4.0.0 - - oniguruma-to-js@0.3.3: {} + regex: 4.3.2 optionator@0.9.4: dependencies: @@ -7061,6 +7181,8 @@ snapshots: dependencies: parse-ms: 4.0.0 + property-information@6.5.0: {} + psl@1.9.0: {} publint@0.2.10: @@ -7187,26 +7309,26 @@ snapshots: dependencies: glob: 7.2.3 - rollup@4.21.2: + rollup@4.21.3: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.21.2 - '@rollup/rollup-android-arm64': 4.21.2 - '@rollup/rollup-darwin-arm64': 4.21.2 - '@rollup/rollup-darwin-x64': 4.21.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.21.2 - '@rollup/rollup-linux-arm-musleabihf': 4.21.2 - '@rollup/rollup-linux-arm64-gnu': 4.21.2 - '@rollup/rollup-linux-arm64-musl': 4.21.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.21.2 - '@rollup/rollup-linux-riscv64-gnu': 4.21.2 - '@rollup/rollup-linux-s390x-gnu': 4.21.2 - '@rollup/rollup-linux-x64-gnu': 4.21.2 - '@rollup/rollup-linux-x64-musl': 4.21.2 - '@rollup/rollup-win32-arm64-msvc': 4.21.2 - '@rollup/rollup-win32-ia32-msvc': 4.21.2 - '@rollup/rollup-win32-x64-msvc': 4.21.2 + '@rollup/rollup-android-arm-eabi': 4.21.3 + '@rollup/rollup-android-arm64': 4.21.3 + '@rollup/rollup-darwin-arm64': 4.21.3 + '@rollup/rollup-darwin-x64': 4.21.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.21.3 + '@rollup/rollup-linux-arm-musleabihf': 4.21.3 + '@rollup/rollup-linux-arm64-gnu': 4.21.3 + '@rollup/rollup-linux-arm64-musl': 4.21.3 + '@rollup/rollup-linux-powerpc64le-gnu': 4.21.3 + '@rollup/rollup-linux-riscv64-gnu': 4.21.3 + '@rollup/rollup-linux-s390x-gnu': 4.21.3 + '@rollup/rollup-linux-x64-gnu': 4.21.3 + '@rollup/rollup-linux-x64-musl': 4.21.3 + '@rollup/rollup-win32-arm64-msvc': 4.21.3 + '@rollup/rollup-win32-ia32-msvc': 4.21.3 + '@rollup/rollup-win32-x64-msvc': 4.21.3 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -7240,7 +7362,7 @@ snapshots: dependencies: xmlchars: 2.2.0 - search-insights@2.17.1: {} + search-insights@2.17.2: {} semver@5.7.2: {} @@ -7275,9 +7397,12 @@ snapshots: shebang-regex@3.0.0: {} - shiki@1.16.3: + shiki@1.17.6: dependencies: - '@shikijs/core': 1.16.3 + '@shikijs/core': 1.17.6 + '@shikijs/engine-javascript': 1.17.6 + '@shikijs/engine-oniguruma': 1.17.6 + '@shikijs/types': 1.17.6 '@shikijs/vscode-textmate': 9.2.2 '@types/hast': 3.0.4 @@ -7318,6 +7443,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -7385,6 +7512,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -7395,8 +7527,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@3.0.0: {} - strip-final-newline@4.0.0: {} strip-json-comments@3.1.1: {} @@ -7432,6 +7562,8 @@ snapshots: tinybench@2.9.0: {} + tinyexec@0.3.0: {} + tinypool@1.0.1: {} tinyrainbow@1.2.0: {} @@ -7462,12 +7594,14 @@ snapshots: dependencies: punycode: 2.3.1 - traverse@0.6.9: + traverse@0.6.10: dependencies: gopd: 1.0.1 typedarray.prototype.slice: 1.0.3 which-typed-array: 1.1.15 + trim-lines@3.0.1: {} + trough@1.0.5: {} ts-api-utils@1.3.0(typescript@5.6.2): @@ -7495,7 +7629,7 @@ snapshots: tsx@4.19.1: dependencies: esbuild: 0.23.1 - get-tsconfig: 4.8.0 + get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 @@ -7607,15 +7741,38 @@ snapshots: unist-util-is@4.1.0: {} + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position@2.0.3: dependencies: '@types/unist': 2.0.11 + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit-parents@3.1.1: dependencies: '@types/unist': 2.0.11 unist-util-is: 4.1.0 + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + universal-user-agent@6.0.1: {} universalify@0.1.2: {} @@ -7659,6 +7816,11 @@ snapshots: '@types/unist': 2.0.11 unist-util-stringify-position: 2.0.3 + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + vfile@4.2.1: dependencies: '@types/unist': 2.0.11 @@ -7666,13 +7828,17 @@ snapshots: unist-util-stringify-position: 2.0.3 vfile-message: 2.0.4 - vite-node@2.0.5(@types/node@22.5.4): + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite-node@2.1.1(@types/node@22.5.4): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 - tinyrainbow: 1.2.0 - vite: 5.4.3(@types/node@22.5.4) + vite: 5.4.5(@types/node@22.5.4) transitivePeerDependencies: - '@types/node' - less @@ -7684,33 +7850,33 @@ snapshots: - supports-color - terser - vite@5.4.3(@types/node@22.5.4): + vite@5.4.5(@types/node@22.5.4): dependencies: esbuild: 0.21.5 postcss: 8.4.45 - rollup: 4.21.2 + rollup: 4.21.3 optionalDependencies: '@types/node': 22.5.4 fsevents: 2.3.3 - vitepress@1.3.4(@algolia/client-search@5.3.2)(@types/node@22.5.4)(postcss@8.4.45)(search-insights@2.17.1)(typescript@5.6.2): + vitepress@1.3.4(@algolia/client-search@5.4.1)(@types/node@22.5.4)(postcss@8.4.45)(search-insights@2.17.2)(typescript@5.6.2): dependencies: '@docsearch/css': 3.6.1 - '@docsearch/js': 3.6.1(@algolia/client-search@5.3.2)(search-insights@2.17.1) - '@shikijs/core': 1.16.3 - '@shikijs/transformers': 1.16.3 + '@docsearch/js': 3.6.1(@algolia/client-search@5.4.1)(search-insights@2.17.2) + '@shikijs/core': 1.17.6 + '@shikijs/transformers': 1.17.6 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.1.3(vite@5.4.3(@types/node@22.5.4))(vue@3.5.4(typescript@5.6.2)) - '@vue/devtools-api': 7.4.4 - '@vue/shared': 3.5.4 - '@vueuse/core': 11.0.3(vue@3.5.4(typescript@5.6.2)) - '@vueuse/integrations': 11.0.3(focus-trap@7.5.4)(vue@3.5.4(typescript@5.6.2)) - focus-trap: 7.5.4 + '@vitejs/plugin-vue': 5.1.3(vite@5.4.5(@types/node@22.5.4))(vue@3.5.5(typescript@5.6.2)) + '@vue/devtools-api': 7.4.5 + '@vue/shared': 3.5.5 + '@vueuse/core': 11.0.3(vue@3.5.5(typescript@5.6.2)) + '@vueuse/integrations': 11.0.3(focus-trap@7.6.0)(vue@3.5.5(typescript@5.6.2)) + focus-trap: 7.6.0 mark.js: 8.11.1 minisearch: 7.1.0 - shiki: 1.16.3 - vite: 5.4.3(@types/node@22.5.4) - vue: 3.5.4(typescript@5.6.2) + shiki: 1.17.6 + vite: 5.4.5(@types/node@22.5.4) + vue: 3.5.5(typescript@5.6.2) optionalDependencies: postcss: 8.4.45 transitivePeerDependencies: @@ -7741,34 +7907,35 @@ snapshots: - typescript - universal-cookie - vitest@2.0.5(@types/node@22.5.4)(happy-dom@15.7.3)(jsdom@25.0.0): + vitest@2.1.1(@types/node@22.5.4)(happy-dom@15.7.4)(jsdom@25.0.0): dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@vitest/expect': 2.1.1 + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.5(@types/node@22.5.4)) + '@vitest/pretty-format': 2.1.1 + '@vitest/runner': 2.1.1 + '@vitest/snapshot': 2.1.1 + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 chai: 5.1.1 debug: 4.3.7 - execa: 8.0.1 magic-string: 0.30.11 pathe: 1.1.2 std-env: 3.7.0 tinybench: 2.9.0 + tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.3(@types/node@22.5.4) - vite-node: 2.0.5(@types/node@22.5.4) + vite: 5.4.5(@types/node@22.5.4) + vite-node: 2.1.1(@types/node@22.5.4) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.5.4 - happy-dom: 15.7.3 + happy-dom: 15.7.4 jsdom: 25.0.0 transitivePeerDependencies: - less - lightningcss + - msw - sass - sass-embedded - stylus @@ -7776,17 +7943,17 @@ snapshots: - supports-color - terser - vue-demi@0.14.10(vue@3.5.4(typescript@5.6.2)): + vue-demi@0.14.10(vue@3.5.5(typescript@5.6.2)): dependencies: - vue: 3.5.4(typescript@5.6.2) + vue: 3.5.5(typescript@5.6.2) - vue@3.5.4(typescript@5.6.2): + vue@3.5.5(typescript@5.6.2): dependencies: - '@vue/compiler-dom': 3.5.4 - '@vue/compiler-sfc': 3.5.4 - '@vue/runtime-dom': 3.5.4 - '@vue/server-renderer': 3.5.4(vue@3.5.4(typescript@5.6.2)) - '@vue/shared': 3.5.4 + '@vue/compiler-dom': 3.5.5 + '@vue/compiler-sfc': 3.5.5 + '@vue/runtime-dom': 3.5.5 + '@vue/server-renderer': 3.5.5(vue@3.5.5(typescript@5.6.2)) + '@vue/shared': 3.5.5 optionalDependencies: typescript: 5.6.2 @@ -7889,3 +8056,5 @@ snapshots: zod@3.23.8: {} zwitch@1.0.5: {} + + zwitch@2.0.4: {} diff --git a/scripts/generate-examples-derivatives/generate-docs.ts b/scripts/generate-examples-derivatives/generate-docs.ts index 2fff8d742..316fe113a 100644 --- a/scripts/generate-examples-derivatives/generate-docs.ts +++ b/scripts/generate-examples-derivatives/generate-docs.ts @@ -24,6 +24,8 @@ export const generateDocs = async (examples: Example[]) => { options: { ignore: [`./website/content/examples/index.md`] }, }) + await FS.mkdir(`./website/content/examples`, { recursive: true }) + await Promise.all(examplesTransformed.map(async (example) => { await FS.writeFile(`./website/content/examples/${example.fileName.canonical}.md`, example.file.content) })) @@ -91,9 +93,10 @@ export const generateDocs = async (examples: Example[]) => { await Promise.all( Object.entries(groups).map(async ([groupName, examples]) => { const codeLinks = examples.map(example => { - return `[${example.fileName.canonicalTitle}](../../examples/${example.fileName.canonical}.md)` - }).join(` / `) - const code = `###### Examples -> ${codeLinks}` + return `${example.fileName.canonicalTitle}` + }).join(` `) + const code = + `` await FS.writeFile(`./website/content/guides/_example_links/${groupName}.md`, code) }), ) @@ -161,7 +164,8 @@ const transformRewriteHelperImports = (example: Example) => { .replaceAll(/^import.*\$\/helpers.*$\n/gm, ``) .replaceAll(`documentQueryContinents`, `{ document: \`${documentQueryContinents.document}\` }`) .replaceAll(`publicGraphQLSchemaEndpoints.Atlas`, `\`${publicGraphQLSchemaEndpoints.Atlas}\``) - .replaceAll(/interceptAndShowOutput.*\n/g, ``) + .replaceAll(/interceptAndShowOutput.*\n\n?/g, ``) + .replaceAll(/interceptAndShowUncaughtErrors.*\n\n?/g, ``) .replaceAll(/showJson|show/g, consoleLog) // We disabled this because the popover gets in the way of output below often. // .replaceAll(/(^console.log.*$)/gm, `$1\n//${` `.repeat(consoleLog.length - 1)}^?`) diff --git a/scripts/generate-examples-derivatives/helpers.ts b/scripts/generate-examples-derivatives/helpers.ts index 5502e4144..80b738714 100644 --- a/scripts/generate-examples-derivatives/helpers.ts +++ b/scripts/generate-examples-derivatives/helpers.ts @@ -1,5 +1,5 @@ import { capitalize, kebabCase } from 'es-toolkit' -import { execa } from 'execa' +import { execa, ExecaError } from 'execa' import { globby } from 'globby' import stripAnsi from 'strip-ansi' import { showPartition } from '../../examples/$/helpers.js' @@ -154,7 +154,16 @@ export const runExample = async (filePath: string) => { let exampleOutput = `` - exampleOutput = result.failed ? result.stderr : result.stdout + // todo: better understand the Execa API + if (filePath.includes(`_or-throw`)) { + if (result instanceof ExecaError) { + // @ts-expect-error fixme + exampleOutput = result.stdout + } + } else { + exampleOutput = result.failed ? result.stderr : result.stdout + } + exampleOutput = stripAnsi(exampleOutput) exampleOutput = rewriteDynamicError(exampleOutput) diff --git a/src/cli/_helpers.ts b/src/cli/_helpers.ts index b3c1351e0..68290bc92 100644 --- a/src/cli/_helpers.ts +++ b/src/cli/_helpers.ts @@ -11,13 +11,13 @@ export const introspectionQuery = async (endpoint: URL): Promise - const result = await Graffle.create({ schema: endpoint }).rawStringOrThrow({ + const data = await Graffle.create({ schema: endpoint }).rawString({ document: introspectionQueryDocument, }) - if (!result.data) { + if (!data) { throw new Error(`No data returned for introspection query.`) } - return result.data + return data } diff --git a/src/entrypoints/extensions.ts b/src/entrypoints/extensions.ts index 60171f5ca..5c1b6065a 100644 --- a/src/entrypoints/extensions.ts +++ b/src/entrypoints/extensions.ts @@ -1,2 +1,3 @@ export { Opentelemetry } from '../layers/7_extensions/Opentelemetry/Opentelemetry.js' +export { OrThrow } from '../layers/7_extensions/OrThrow/OrThrow.js' export { Upload } from '../layers/7_extensions/Upload/Upload.js' diff --git a/src/entrypoints/main.ts b/src/entrypoints/main.ts index 08a4f77f5..07b1c2509 100644 --- a/src/entrypoints/main.ts +++ b/src/entrypoints/main.ts @@ -1,8 +1,12 @@ export { execute } from '../layers/0_functions/execute.js' export { gql } from '../layers/0_functions/gql.js' export { type TypedDocumentString } from '../layers/0_functions/types.js' +export { createExtension, type Extension } from '../layers/6_client/extension.js' // todo figure this export out. Was just put there to resolve a type error about "...cannot be named..." export * from '../layers/6_client/Settings/Input.js' // todo figure this export out. Was just put there to resolve a type error about "...cannot be named..." +export type { BuilderRequestMethods } from '../layers/6_client/client.js' +export { type Config as BuilderConfig } from '../layers/6_client/Settings/Config.js' +export { type WithInput } from '../layers/6_client/Settings/inputIncrementable/inputIncrementable.js' export * from '../lib/prelude.js' export * from './__Graffle.js' diff --git a/src/layers/0_functions/gql.ts b/src/layers/0_functions/gql.ts index c82cdbe51..016ad5c25 100644 --- a/src/layers/0_functions/gql.ts +++ b/src/layers/0_functions/gql.ts @@ -1,5 +1,6 @@ import type { TypedQueryDocumentNode } from 'graphql' import { parse } from 'graphql' +import type { SomeData } from '../../lib/graphql.js' /** * Returns the string with any variables given interpolated and then parsed into a DocumentNode. @@ -15,7 +16,7 @@ import { parse } from 'graphql' * * Several tools in the Node GraphQL ecosystem are hardcoded to specially treat any template tag named "gql". For example see this prettier issue: https://github.com/prettier/prettier/issues/4360. Using this template tag has no runtime effect beyond variable interpolation. */ -export const gql = <$Data, $Variables>( +export const gql = <$Data extends SomeData, $Variables>( chunks: TemplateStringsArray, ...variables: unknown[] ): TypedQueryDocumentNode<$Data, $Variables> => { diff --git a/src/layers/0_functions/types.ts b/src/layers/0_functions/types.ts index ca80ebe37..112b2d861 100644 --- a/src/layers/0_functions/types.ts +++ b/src/layers/0_functions/types.ts @@ -31,7 +31,7 @@ type GetVariablesInputFromString<$Document extends TypedDocumentString> = ? HasRequiredKeys> extends true ? { variables: VariablesOf<$Document> } : { variables?: VariablesOf<$Document> } - : {} // eslint-disable-line + : {} // dprint-ignore type GetVariablesInputFromDocumentNode<$Document extends TypedQueryDocumentNode> = @@ -39,7 +39,7 @@ type GetVariablesInputFromDocumentNode<$Document extends TypedQueryDocumentNode> ? HasRequiredKeys> extends true ? { variables: VariablesOf<$Document> } : { variables?: VariablesOf<$Document> } - : {} // eslint-disable-line + : {} export type HasVariables<$Document extends TypedQueryDocumentNode | TypedDocumentString> = Negate< IsEmptyObject> @@ -55,5 +55,5 @@ type VariablesOf<$Document extends TypedQueryDocumentNode | TypedDocumentString> // TODO open issue asking for core library to expose this type. export interface TypedDocumentString, TVariables = Record> - extends String, DocumentTypeDecoration // eslint-disable-line + extends String, DocumentTypeDecoration {} diff --git a/src/layers/1_Schema/Field.ts b/src/layers/1_Schema/Field.ts index 053a7c33c..957f5b3a9 100644 --- a/src/layers/1_Schema/Field.ts +++ b/src/layers/1_Schema/Field.ts @@ -14,7 +14,7 @@ export const field = <$Type extends Output.Any, $Args extends null | Args = ): Field<$Type, $Args> => { return { // At type level "type" is not a thunk - type: type as any, // eslint-disable-line + type: type as any, args, } } diff --git a/src/layers/1_Schema/Hybrid/types/Scalar/Scalar.ts b/src/layers/1_Schema/Hybrid/types/Scalar/Scalar.ts index 1518d9dea..3e82c5f33 100644 --- a/src/layers/1_Schema/Hybrid/types/Scalar/Scalar.ts +++ b/src/layers/1_Schema/Hybrid/types/Scalar/Scalar.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types */ - import type { GlobalRegistry } from '../../../../2_generator/globalRegistry.js' import type { Codec } from './codec.js' import { JavaScriptScalarCodecs } from './nativeScalarCodecs.js' @@ -21,7 +19,7 @@ export const create = <$Name extends string, $Decoded, $Encoded extends Standard ): Scalar<$Name, $Decoded, $Encoded> => ({ kind: ScalarKind, name: name, - codec: codec as any, // eslint-disable-line + codec: codec as any, }) export const scalar = <$Name extends string, $Codec extends Codec>( @@ -30,7 +28,7 @@ export const scalar = <$Name extends string, $Codec extends Codec>( ): Scalar<$Name, $Codec> => ({ kind: ScalarKind, name: name, - codec: codec as any, // eslint-disable-line + codec: codec as any, }) export interface Scalar< diff --git a/src/layers/1_Schema/Input/Input.ts b/src/layers/1_Schema/Input/Input.ts index 526979591..3d230bd26 100644 --- a/src/layers/1_Schema/Input/Input.ts +++ b/src/layers/1_Schema/Input/Input.ts @@ -9,7 +9,7 @@ export * from './types/Nullable.js' export const field = <$Type extends Any>(type: MaybeThunk<$Type>): Field<$Type> => { return { // Thunks do not exist at the type level - type: type as any, // eslint-disable-line + type: type as any, } } diff --git a/src/layers/1_Schema/Input/types/Nullable.ts b/src/layers/1_Schema/Input/types/Nullable.ts index 89b296582..31ae3ef6b 100644 --- a/src/layers/1_Schema/Input/types/Nullable.ts +++ b/src/layers/1_Schema/Input/types/Nullable.ts @@ -9,7 +9,7 @@ export type Nullable<$InnerType extends InnerType> = Base.Nullable<$InnerType> export const Nullable = <$InnerType extends InnerType>(type: MaybeThunk<$InnerType>): Nullable<$InnerType> => ({ kind: `nullable`, // at type level "type" is not a thunk - type: type as any, // eslint-disable-line + type: type as any, }) // dprint-ignore diff --git a/src/layers/1_Schema/Output/types/List.ts b/src/layers/1_Schema/Output/types/List.ts index 9efc7d216..f486effea 100644 --- a/src/layers/1_Schema/Output/types/List.ts +++ b/src/layers/1_Schema/Output/types/List.ts @@ -9,5 +9,5 @@ export type List<$Type extends InnerType> = Base.List<$Type> export const List = <$Type extends InnerType>(type: MaybeThunk<$Type>): List<$Type> => ({ kind: `list`, // at type level "type" is not a thunk - type: type as any, // eslint-disable-line + type: type as any, }) diff --git a/src/layers/1_Schema/Output/types/Nullable.ts b/src/layers/1_Schema/Output/types/Nullable.ts index 86815f8df..030743e91 100644 --- a/src/layers/1_Schema/Output/types/Nullable.ts +++ b/src/layers/1_Schema/Output/types/Nullable.ts @@ -12,5 +12,5 @@ export const Nullable = <$Type extends InnerType>( ): Nullable<$Type> => ({ kind: `nullable`, // at type level "type" is not a thunk - type: type as any, // eslint-disable-line + type: type as any, }) diff --git a/src/layers/1_Schema/core/Index.ts b/src/layers/1_Schema/core/Index.ts index c5ff18bf6..4ddd4aff7 100644 --- a/src/layers/1_Schema/core/Index.ts +++ b/src/layers/1_Schema/core/Index.ts @@ -1,3 +1,4 @@ +import type { RootTypeName } from '../../../lib/graphql.js' import type { GlobalRegistry } from '../../2_generator/globalRegistry.js' import type { Output } from '../Output/__.js' @@ -5,8 +6,10 @@ import type { Output } from '../Output/__.js' * A generic schema index type. Any particular schema index will be a subtype of this, with * additional specificity such as on objects where here `Record` is used. */ +// todo make all readonly? export interface Index { name: GlobalRegistry.SchemaNames + RootTypesPresent: readonly RootTypeName[] Root: { Query: null | Output.Object$2 Mutation: null | Output.Object$2 diff --git a/src/layers/2_generator/__snapshots__/files.test.ts.snap b/src/layers/2_generator/__snapshots__/files.test.ts.snap index edac11012..7bf67d736 100644 --- a/src/layers/2_generator/__snapshots__/files.test.ts.snap +++ b/src/layers/2_generator/__snapshots__/files.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`schema2 1`] = ` -"import { createPrefilled } from '../../../../src/entrypoints/client.js' +"import { createPrefilled } from '../../../../../src/entrypoints/client.js' import { $defaultSchemaUrl, $Index } from './SchemaRuntime.js' @@ -15,9 +15,15 @@ exports[`schema2 2`] = ` `; exports[`schema2 3`] = ` -"export { create } from './Client.js' -export { isError } from './Error.js' -export { Select } from './Select.js' +"// We import the global module for good measure although it is not clear it is always needed. +// It at least helps with Twoslash wherein without this import here Twoslash will not include the global module. +// In real TypeScript projects it seems the global module is included automatically. But there could be certain tsconfig +// setups where this still indeed does help. +import './modules/Global.js' + +export { create } from './modules/Client.js' +export { isError } from './modules/Error.js' +export { Select } from './modules/Select.js' " `; @@ -36,21 +42,21 @@ const ErrorObjectsTypeNameSelected = Object.values(ErrorObjectsTypeNameSelectedE type ErrorObjectsTypeNameSelected = (typeof ErrorObjectsTypeNameSelected)[number] export const isError = <$Value>(value: $Value): value is Include<$Value, ErrorObjectsTypeNameSelected> => { - return typeof value === 'object' && value !== null && '__typename' in value + return typeof value === \`object\` && value !== null && \`__typename\` in value && ErrorObjectsTypeNameSelected.some(_ => _.__typename === value.__typename) } " `; exports[`schema2 5`] = ` -"import type { ResultSet, SelectionSet } from '../../../../src/entrypoints/schema.js' -import type { Index } from './Index.js' +"import type { ResultSet, SelectionSet } from '../../../../../src/entrypoints/schema.js' +import type { Index } from './SchemaIndex.js' // Runtime // ------- -import { createSelect } from '../../../../src/entrypoints/client.js' -export const Select = createSelect('default') +import { createSelect } from '../../../../../src/entrypoints/client.js' +export const Select = createSelect(\`default\`) // Buildtime // --------- @@ -162,6 +168,7 @@ import type * as Schema from './SchemaBuildtime.js' export interface Index { name: 'default' + RootTypesPresent: ['Query', 'Mutation'] Root: { Query: Schema.Root.Query Mutation: Schema.Root.Mutation @@ -216,7 +223,7 @@ export interface Index { `; exports[`schema2 7`] = ` -"import type * as $ from '../../../../src/entrypoints/schema.js' +"import type * as $ from '../../../../../src/entrypoints/schema.js' import type * as $Scalar from './Scalar.ts' // ------------------------------------------------------------ // @@ -552,15 +559,15 @@ export namespace Union { `; exports[`schema2 8`] = ` -"export * from '../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' -export * from '../../customScalarCodecs.js' +"export * from '../../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' +export * from '../../../customScalarCodecs.js' " `; exports[`schema2 9`] = ` "/* eslint-disable */ -import * as $ from '../../../../src/entrypoints/schema.js' +import * as $ from '../../../../../src/entrypoints/schema.js' import * as $Scalar from './Scalar.js' export const $defaultSchemaUrl = undefined @@ -789,6 +796,7 @@ export const Query = $.Object$(\`Query\`, { export const $Index = { name: 'default' as const, + RootTypesPresent: ['Query', 'Mutation'] as const, Root: { Query, Mutation, diff --git a/src/layers/2_generator/code/SchemaBuildtime.ts b/src/layers/2_generator/code/SchemaBuildtime.ts index 60dae6f1e..7116531a6 100644 --- a/src/layers/2_generator/code/SchemaBuildtime.ts +++ b/src/layers/2_generator/code/SchemaBuildtime.ts @@ -77,7 +77,7 @@ const defineConcreteRenderers = < key, (config: Config, node: any) => { if (!node) return `` - return renderer(config, node) // eslint-disable-line + return renderer(config, node) }, ] }), @@ -91,7 +91,7 @@ const dispatchToReferenceRenderer = (config: Config, node: AnyClass): string => // @ts-expect-error fixme const getReferenceRenderer = (node: N): (typeof referenceRenderers)[ClassToName] => { // @ts-expect-error lookup - const renderer = referenceRenderers[node.constructor.name] // eslint-disable-line + const renderer = referenceRenderers[node.constructor.name] if (!renderer) { throw new Error(`No renderer found for class: ${node.constructor.name}`) } @@ -112,11 +112,11 @@ const dispatchToConcreteRenderer = ( node: GraphQLNamedType, ): string => { // @ts-expect-error lookup - const renderer = concreteRenderers[node.constructor.name] // eslint-disable-line + const renderer = concreteRenderers[node.constructor.name] if (!renderer) { throw new Error(`No renderer found for class: ${node.constructor.name}`) } - return renderer(config, node) // eslint-disable-line + return renderer(config, node) } const concreteRenderers = defineConcreteRenderers({ diff --git a/src/layers/2_generator/code/SchemaIndex.ts b/src/layers/2_generator/code/SchemaIndex.ts index 5a37de6ba..fc9339094 100644 --- a/src/layers/2_generator/code/SchemaIndex.ts +++ b/src/layers/2_generator/code/SchemaIndex.ts @@ -12,16 +12,25 @@ export const { generate: generateIndex, moduleName: moduleNameIndex } = createCo code.push(`/* eslint-disable */\n`) code.push(`import type * as ${SchemaNamespace} from './${moduleNameSchemaBuildtime}.js'\n`) + const rootTypesPresence = { + Query: hasQuery(config.typeMapByKind), + Mutation: hasMutation(config.typeMapByKind), + Subscription: hasSubscription(config.typeMapByKind), + } + code.push(Code.export$( Code.interface$( `Index`, Code.objectFrom({ name: Code.quote(config.name), + RootTypesPresent: `[${ + Object.entries(rootTypesPresence).filter(([_, present]) => present).map(([_]) => Code.quote(_)).join(`, `) + }]`, Root: { type: Code.objectFrom({ - Query: hasQuery(config.typeMapByKind) ? `${SchemaNamespace}.Root.Query` : null, - Mutation: hasMutation(config.typeMapByKind) ? `${SchemaNamespace}.Root.Mutation` : null, - Subscription: hasSubscription(config.typeMapByKind) ? `${SchemaNamespace}.Root.Subscription` : null, + Query: rootTypesPresence.Query ? `${SchemaNamespace}.Root.Query` : null, + Mutation: rootTypesPresence.Mutation ? `${SchemaNamespace}.Root.Mutation` : null, + Subscription: rootTypesPresence.Subscription ? `${SchemaNamespace}.Root.Subscription` : null, }), }, objects: Code.objectFromEntries( diff --git a/src/layers/2_generator/code/SchemaRuntime.ts b/src/layers/2_generator/code/SchemaRuntime.ts index df1615666..eedb07804 100644 --- a/src/layers/2_generator/code/SchemaRuntime.ts +++ b/src/layers/2_generator/code/SchemaRuntime.ts @@ -21,6 +21,7 @@ import { isScalarType, isUnionType, } from 'graphql' +import { Code } from '../../../lib/Code.js' import type { AnyClass, AnyGraphQLOutputField } from '../../../lib/graphql.js' import { hasMutation, hasQuery, hasSubscription, unwrapToNamed, unwrapToNonNull } from '../../../lib/graphql.js' import { createCodeGenerator } from '../createCodeGenerator.js' @@ -66,14 +67,22 @@ export const { generate: generateRuntimeSchema, moduleName: moduleNameSchemaRunt ) const index = (config: Config) => { + const rootTypesPresence = { + Query: hasQuery(config.typeMapByKind), + Mutation: hasMutation(config.typeMapByKind), + Subscription: hasSubscription(config.typeMapByKind), + } // todo input objects for decode/encode input object fields return ` export const $Index = { name: "${config.name}" as const, + RootTypesPresent: [${ + Object.entries(rootTypesPresence).filter(([_, present]) => present).map(([_]) => Code.quote(_)).join(`, `) + }] as const, Root: { - Query ${hasQuery(config.typeMapByKind) ? `` : `:null`} , - Mutation ${hasMutation(config.typeMapByKind) ? `` : `:null`}, - Subscription ${hasSubscription(config.typeMapByKind) ? `` : `:null`} + Query ${rootTypesPresence.Query ? `` : `:null`} , + Mutation ${rootTypesPresence.Mutation ? `` : `:null`}, + Subscription ${rootTypesPresence.Subscription ? `` : `:null`} }, objects: { ${config.typeMapByKind.GraphQLObjectType.map(type => type.name).join(`,\n`)} diff --git a/src/layers/2_generator/files.test.ts b/src/layers/2_generator/files.test.ts index d090ba86f..58aaf42d8 100644 --- a/src/layers/2_generator/files.test.ts +++ b/src/layers/2_generator/files.test.ts @@ -3,7 +3,7 @@ import { expect, test } from 'vitest' test(`schema2`, async () => { expect( - await readFile(`./tests/_/schema/generated/Client.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/Client.ts`, `utf8`), ).toMatchSnapshot() expect( await readFile(`./tests/_/schema/generated/__.ts`, `utf8`), @@ -12,21 +12,21 @@ test(`schema2`, async () => { await readFile(`./tests/_/schema/generated/_.ts`, `utf8`), ).toMatchSnapshot() expect( - await readFile(`./tests/_/schema/generated/Error.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/Error.ts`, `utf8`), ).toMatchSnapshot() expect( - await readFile(`./tests/_/schema/generated/Select.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/Select.ts`, `utf8`), ).toMatchSnapshot() expect( - await readFile(`./tests/_/schema/generated/Index.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/SchemaIndex.ts`, `utf8`), ).toMatchSnapshot() expect( - await readFile(`./tests/_/schema/generated/SchemaBuildtime.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/SchemaBuildtime.ts`, `utf8`), ).toMatchSnapshot() expect( - await readFile(`./tests/_/schema/generated/Scalar.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/Scalar.ts`, `utf8`), ).toMatchSnapshot() expect( - await readFile(`./tests/_/schema/generated/SchemaRuntime.ts`, `utf8`), + await readFile(`./tests/_/schema/generated/modules/SchemaRuntime.ts`, `utf8`), ).toMatchSnapshot() }) diff --git a/src/layers/2_generator/files.ts b/src/layers/2_generator/files.ts index daf59a39d..e4dd8a68b 100644 --- a/src/layers/2_generator/files.ts +++ b/src/layers/2_generator/files.ts @@ -63,11 +63,12 @@ export const generateFiles = async (input: Input) => { const sourceDirPath = input.sourceDirPath ?? process.cwd() const schemaSource = await resolveSourceSchema(input) const outputDirPath = input.outputDirPath ?? Path.join(process.cwd(), `./graffle`) + const outputModulesDirPath = Path.join(outputDirPath, `/modules`) // todo support other extensions: .tsx,.js,.mjs,.cjs const customScalarCodecsFilePath = input.sourceCustomScalarCodecsFilePath ?? Path.join(sourceDirPath, `customScalarCodecs.ts`) const customScalarCodecsImportPath = Path.relative( - outputDirPath, + outputModulesDirPath, customScalarCodecsFilePath.replace(/\.ts$/, `.js`), ) const customScalarCodecsPathExists = await fileExists(customScalarCodecsFilePath) diff --git a/src/layers/2_generator/helpers.ts b/src/layers/2_generator/helpers.ts index c0a7cbe1c..e7c824ff8 100644 --- a/src/layers/2_generator/helpers.ts +++ b/src/layers/2_generator/helpers.ts @@ -6,7 +6,7 @@ export const title = (title: string) => { } export const typeTitle = (config: Config, typeName: string) => { // @ts-expect-error ignoreme - // eslint-disable-next-line + const hasItems = config.typeMapByKind[`GraphQL${typeName}Type`]?.length > 0 const title = `${typeName} Types` const titleDecorated = `// ${title}\n// ${`-`.repeat(title.length)}\n` diff --git a/src/layers/3_SelectionSet/encode.test.ts b/src/layers/3_SelectionSet/encode.test.ts index 9c83e7e1e..682984634 100644 --- a/src/layers/3_SelectionSet/encode.test.ts +++ b/src/layers/3_SelectionSet/encode.test.ts @@ -1,8 +1,8 @@ import { parse, print } from 'graphql' import { describe, expect, test } from 'vitest' import { db } from '../../../tests/_/db.js' -import type { Index } from '../../../tests/_/schema/generated/Index.js' -import { $Index as schemaIndex } from '../../../tests/_/schema/generated/SchemaRuntime.js' +import type { Index } from '../../../tests/_/schema/generated/modules/SchemaIndex.js' +import { $Index as schemaIndex } from '../../../tests/_/schema/generated/modules/SchemaRuntime.js' import { outputConfigDefault } from '../6_client/Settings/Config.js' import type { SelectionSet } from './__.js' import type { Context } from './encode.js' @@ -28,7 +28,7 @@ const testEachArgs = [ output: outputConfigDefault, transport: { type: `memory`, config: { methodMode: `post` } }, name: schemaIndex[`name`], - // eslint-disable-next-line + initialInput: {} as any, }, } diff --git a/src/layers/3_SelectionSet/encode.ts b/src/layers/3_SelectionSet/encode.ts index 06c528f80..de166ae5a 100644 --- a/src/layers/3_SelectionSet/encode.ts +++ b/src/layers/3_SelectionSet/encode.ts @@ -135,7 +135,7 @@ const resolveArgs = (context: Context, schemaField: Schema.SomeField, ss: Indica return `(${ argEntries.map(([argFieldName, v]) => { - const schemaArgField = schemaArgs.fields[argFieldName] as Schema.Input.Any | undefined // eslint-disable-line + const schemaArgField = schemaArgs.fields[argFieldName] as Schema.Input.Any | undefined if (!schemaArgField) throw new Error(`Arg field ${argFieldName} not found in schema.`) const valueEncoded = resolveArgValue(context, schemaArgField, v) return `${argFieldName}: ${valueEncoded}` diff --git a/src/layers/3_SelectionSet/types.test-d.ts b/src/layers/3_SelectionSet/types.test-d.ts index 2f13727a5..ce382061b 100644 --- a/src/layers/3_SelectionSet/types.test-d.ts +++ b/src/layers/3_SelectionSet/types.test-d.ts @@ -1,5 +1,5 @@ import { assertType, expectTypeOf, test } from 'vitest' -import type { Index } from '../../../tests/_/schema/generated/Index.js' +import type { Index } from '../../../tests/_/schema/generated/modules/SchemaIndex.js' import type { SelectionSet } from './__.js' type Q = SelectionSet.Query diff --git a/src/layers/3_SelectionSet/types.ts b/src/layers/3_SelectionSet/types.ts index bf26ff90f..4129669e6 100644 --- a/src/layers/3_SelectionSet/types.ts +++ b/src/layers/3_SelectionSet/types.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types */ - import type { MaybeList, StringNonEmpty, Values } from '../../lib/prelude.js' import type { TSError } from '../../lib/TSError.js' import type { diff --git a/src/layers/4_ResultSet/types.test-d.ts b/src/layers/4_ResultSet/types.test-d.ts index 24aa76a34..7278038c2 100644 --- a/src/layers/4_ResultSet/types.test-d.ts +++ b/src/layers/4_ResultSet/types.test-d.ts @@ -1,8 +1,6 @@ -/* eslint-disable @typescript-eslint/ban-types */ - import { expectTypeOf, test } from 'vitest' -import type { Index } from '../../../tests/_/schema/generated/Index.js' -import type * as Schema from '../../../tests/_/schema/generated/SchemaBuildtime.js' +import type * as Schema from '../../../tests/_/schema/generated/modules/SchemaBuildtime.js' +import type { Index } from '../../../tests/_/schema/generated/modules/SchemaIndex.js' import type { SelectionSet } from '../3_SelectionSet/__.js' import type { ResultSet } from './__.js' diff --git a/src/layers/4_ResultSet/types.ts b/src/layers/4_ResultSet/types.ts index 96a29e7f2..97b048801 100644 --- a/src/layers/4_ResultSet/types.ts +++ b/src/layers/4_ResultSet/types.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types */ - import type { Simplify } from 'type-fest' import type { ExcludeNull, GetKeyOr, SimplifyDeep } from '../../lib/prelude.js' import type { TSError } from '../../lib/TSError.js' diff --git a/src/layers/5_core/core.ts b/src/layers/5_core/core.ts index 8309d398e..d48d5d9f2 100644 --- a/src/layers/5_core/core.ts +++ b/src/layers/5_core/core.ts @@ -41,13 +41,12 @@ import type { const getRootIndexOrThrow = (context: ContextInterfaceTyped, rootTypeName: string) => { // @ts-expect-error - // eslint-disable-next-line + const rootIndex = context.schemaIndex.Root[rootTypeName] if (!rootIndex) throw new Error(`Root type not found: ${rootTypeName}`) return rootIndex } -// eslint-disable-next-line type InterfaceInput = | ({ interface: InterfaceTyped @@ -60,7 +59,7 @@ type InterfaceInput = } & RawProperties) // dprint-ignore -// eslint-disable-next-line + type TransportInput<$Config extends Config, $HttpProperties = {}, $MemoryProperties = {}> = | ( TransportHttp extends $Config['transport']['type'] diff --git a/src/layers/5_createExtension/createExtension.ts b/src/layers/5_createExtension/createExtension.ts deleted file mode 100644 index d2a5a7edb..000000000 --- a/src/layers/5_createExtension/createExtension.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { Extension } from '../6_client/client.js' - -export const createExtension = (input: Extension) => input diff --git a/src/layers/5_select/select.test-d.ts b/src/layers/5_select/select.test-d.ts index efa5bf5fa..8556f80c7 100644 --- a/src/layers/5_select/select.test-d.ts +++ b/src/layers/5_select/select.test-d.ts @@ -7,8 +7,8 @@ describe(`select`, () => { const select = create(`default`) it(`returns the input for any method name`, () => { - const s = select as any // eslint-disable-line - expect(s.anything(1)).toEqual(1) // eslint-disable-line + const s = select as any + expect(s.anything(1)).toEqual(1) }) it(`has type safe methods`, () => { diff --git a/src/layers/5_select/select.test.ts b/src/layers/5_select/select.test.ts index 909cf330a..3aa9b0ead 100644 --- a/src/layers/5_select/select.test.ts +++ b/src/layers/5_select/select.test.ts @@ -4,8 +4,8 @@ import { create } from './select.js' const select = create(`default`) test(`returns the input for any method name`, () => { - const s = select as any // eslint-disable-line - expect(s.anything(1)).toEqual(1) // eslint-disable-line + const s = select as any + expect(s.anything(1)).toEqual(1) }) test(`has type safe methods`, () => { diff --git a/src/layers/6_client/RootTypeMethods.ts b/src/layers/6_client/RootTypeMethods.ts index 38b62d04d..b6dcb0594 100644 --- a/src/layers/6_client/RootTypeMethods.ts +++ b/src/layers/6_client/RootTypeMethods.ts @@ -1,16 +1,11 @@ -import type { OperationTypeName } from '../../lib/graphql.js' +import type { CamelCase } from 'type-fest' import type { Exact } from '../../lib/prelude.js' import type { TSError } from '../../lib/TSError.js' import type { InputFieldsAllNullable, Schema } from '../1_Schema/__.js' import type { SelectionSet } from '../3_SelectionSet/__.js' import type { ResultSet } from '../4_ResultSet/__.js' import type { ResolveOutputReturnRootField, ResolveOutputReturnRootType, SimplifyOutput } from './handleOutput.js' -import type { - AugmentRootTypeSelectionWithTypename, - Config, - CreateSelectionTypename, - OrThrowifyConfig, -} from './Settings/Config.js' +import type { AugmentRootTypeSelectionWithTypename, Config, CreateSelectionTypename } from './Settings/Config.js' type RootTypeFieldContext = { Config: Config @@ -21,9 +16,12 @@ type RootTypeFieldContext = { } // dprint-ignore -export type GetRootTypeMethods<$Config extends Config, $Index extends Schema.Index> = { - [$OperationName in OperationTypeName as $Index['Root'][Capitalize<$OperationName>] extends null ? never : $OperationName]: - RootTypeMethods<$Config, $Index, Capitalize<$OperationName>> +export type BuilderRequestMethodsGeneratedRootTypes<$Config extends Config, $Index extends Schema.Index> = { + // todo + // [$OperationName in $Index['OperationsPresent'][number]]: + // RootTypeMethods<$Config, $Index, OperationNameToRootType> + [$RootTypeName in $Index['RootTypesPresent'][number] as CamelCase<$RootTypeName>]: + RootTypeMethods<$Config, $Index, $RootTypeName> } // dprint-ignore @@ -32,8 +30,6 @@ export type RootTypeMethods<$Config extends Config, $Index extends Schema.Index, ( & { $batch: RootMethod<$Config, $Index, $RootTypeName> - // @ts-expect-error fixme - $batchOrThrow: RootMethod, $Index, $RootTypeName> } & { [$RootTypeFieldName in keyof $Index['Root'][$RootTypeName]['fields'] & string]: @@ -45,22 +41,11 @@ export type RootTypeMethods<$Config extends Config, $Index extends Schema.Index, Field: $Index['Root'][$RootTypeName]['fields'][$RootTypeFieldName] }> } - & { - [$RootTypeFieldName in keyof $Index['Root'][$RootTypeName]['fields'] & string as `${$RootTypeFieldName}OrThrow`]: - // @ts-expect-error fixme - RootTypeFieldMethod<{ - Config: OrThrowifyConfig<$Config>, - Index: $Index, - RootTypeName: $RootTypeName, - RootTypeFieldName: $RootTypeFieldName - Field: $Index['Root'][$RootTypeName]['fields'][$RootTypeFieldName] - }> - } ) : TSError<'RootTypeMethods', `Your schema does not have the root type "${$RootTypeName}".`> // dprint-ignore -type RootMethod<$Config extends Config, $Index extends Schema.Index, $RootTypeName extends Schema.RootTypeName> = +export type RootMethod<$Config extends Config, $Index extends Schema.Index, $RootTypeName extends Schema.RootTypeName> = <$SelectionSet extends object>(selectionSet: Exact<$SelectionSet, SelectionSet.Root<$Index, $RootTypeName>>) => Promise< ResolveOutputReturnRootType<$Config, $Index, ResultSet.Root, $Index, $RootTypeName>> diff --git a/src/layers/6_client/Settings/Config.ts b/src/layers/6_client/Settings/Config.ts index 473239320..8fbc1a935 100644 --- a/src/layers/6_client/Settings/Config.ts +++ b/src/layers/6_client/Settings/Config.ts @@ -1,4 +1,4 @@ -import type { ConfigManager, RequireProperties, StringKeyof } from '../../../lib/prelude.js' +import type { RequireProperties, StringKeyof } from '../../../lib/prelude.js' import type { Schema } from '../../1_Schema/__.js' import type { GlobalRegistry } from '../../2_generator/globalRegistry.js' import type { SelectionSet } from '../../3_SelectionSet/__.js' @@ -23,11 +23,12 @@ export const readConfigErrorCategoryOutputChannel = ( return config.output.errors[errorCategory] } -export const traditionalGraphqlOutput: OutputConfig = { +export const traditionalGraphqlOutput = { defaults: { errorChannel: `throw` }, envelope: { enabled: true, errors: { execution: true, other: false, schema: false } }, errors: { execution: `default`, other: `default`, schema: false }, -} +} satisfies OutputConfig + export const traditionalGraphqlOutputThrowing: OutputConfig = { ...traditionalGraphqlOutput, envelope: { @@ -114,11 +115,6 @@ export type Config = { } } -// todo this changed, check tests, add new tests as needed. -// dprint-ignore -export type OrThrowifyConfig<$Config extends Config> = - ConfigManager.Set<$Config, ['output', 'errors'], { other: 'throw', execution: 'throw', schema: 'throw' }> - /** * We inject __typename select when: * 1. using schema errors @@ -129,7 +125,7 @@ type TypenameSelection = { __typename: true } // dprint-ignore export type CreateSelectionTypename<$Config extends Config, $Index extends Schema.Index> = - IsNeedSelectionTypename<$Config, $Index> extends true ? TypenameSelection : {} // eslint-disable-line + IsNeedSelectionTypename<$Config, $Index> extends true ? TypenameSelection : {} // dprint-ignore export type IsNeedSelectionTypename<$Config extends Config, $Index extends Schema.Index> = @@ -147,7 +143,7 @@ export type AugmentRootTypeSelectionWithTypename< > = IsNeedSelectionTypename<$Config, $Index> extends true ? { [$Key in StringKeyof<$Selection>]: & $Selection[$Key] - & (IsRootFieldNameAResultField<$Index, $RootTypeName, $Key> extends true ? TypenameSelection : {}) // eslint-disable-line + & (IsRootFieldNameAResultField<$Index, $RootTypeName, $Key> extends true ? TypenameSelection : {}) } : $Selection diff --git a/src/layers/6_client/Settings/Input.ts b/src/layers/6_client/Settings/Input.ts index 877d97fcc..67d6e6342 100644 --- a/src/layers/6_client/Settings/Input.ts +++ b/src/layers/6_client/Settings/Input.ts @@ -1,7 +1,7 @@ import type { GraphQLSchema } from 'graphql' import type { Schema } from '../../1_Schema/__.js' import type { GlobalRegistry } from '../../2_generator/globalRegistry.js' -import type { InputIncrementable } from './inputIncrementable/inputIncrementable.js' +import type { WithInput } from './inputIncrementable/inputIncrementable.js' export type URLInput = URL | string @@ -63,7 +63,7 @@ export type InputBase<$Schema extends GlobalRegistry.SchemaUnion> = ) // eslint-disable-next-line // @ts-ignore passes after generation - & InputIncrementable<{ name: $Schema['name']; transport: 'http' }> + & WithInput<{ name: $Schema['name']; transport: 'http' }> ) | ( & ( @@ -79,5 +79,5 @@ export type InputBase<$Schema extends GlobalRegistry.SchemaUnion> = ) // eslint-disable-next-line // @ts-ignore passes after generation - & InputIncrementable<{ name: $Schema['name']; transport: 'memory' }> + & WithInput<{ name: $Schema['name']; transport: 'memory' }> ) diff --git a/src/layers/6_client/Settings/InputToConfig.ts b/src/layers/6_client/Settings/InputToConfig.ts index 0b762ca25..489f639bc 100644 --- a/src/layers/6_client/Settings/InputToConfig.ts +++ b/src/layers/6_client/Settings/InputToConfig.ts @@ -73,7 +73,7 @@ export const inputToConfig = <$Input extends InputStatic = +export type WithInput<$Context extends IncrementableInputContext = IncrementableInputContext> = & { /** * Configure output behavior, such as if errors should be returned or thrown. @@ -25,5 +25,5 @@ export type IncrementableInputContext = { } // dprint-ignore -export type AddIncrementalInput<$Config extends Config, $Input extends InputIncrementable> = +export type AddIncrementalInput<$Config extends Config, $Input extends WithInput> = InputToConfig<$Config['initialInput'] & $Input> diff --git a/src/layers/6_client/Settings/inputIncrementable/output.ts b/src/layers/6_client/Settings/inputIncrementable/output.ts index 4beea6456..b4391efbb 100644 --- a/src/layers/6_client/Settings/inputIncrementable/output.ts +++ b/src/layers/6_client/Settings/inputIncrementable/output.ts @@ -50,5 +50,5 @@ export type OutputInput<$Context extends IncrementableInputContext> = schema?: false | OutputChannelConfig } } - : {} // eslint-disable-line + : {} ) diff --git a/src/layers/6_client/client.batch.test.ts b/src/layers/6_client/client.batch.test.ts index 31be0dd71..a2963d74a 100644 --- a/src/layers/6_client/client.batch.test.ts +++ b/src/layers/6_client/client.batch.test.ts @@ -2,8 +2,9 @@ import { describe, expect, test } from 'vitest' import { db } from '../../../tests/_/db.js' import { Graffle } from '../../../tests/_/schema/generated/__.js' import * as Schema from '../../../tests/_/schema/schema.js' +import { OrThrow } from '../7_extensions/OrThrow/OrThrow.js' -const graffle = Graffle.create({ schema: Schema.schema }) +const graffle = Graffle.create({ schema: Schema.schema }).use(OrThrow()) // dprint-ignore describe(`query`, () => { @@ -20,5 +21,5 @@ describe(`query`, () => { test(`error`, async () => { await expect(graffle.query.$batchOrThrow({ error: true })).rejects.toMatchObject(db.errorAggregate) }) - }) + }) }) diff --git a/src/layers/6_client/client.error.test-d.ts b/src/layers/6_client/client.error.test-d.ts index 5da670729..7c8f5652c 100644 --- a/src/layers/6_client/client.error.test-d.ts +++ b/src/layers/6_client/client.error.test-d.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { expectTypeOf, test } from 'vitest' import { Graffle } from '../../../tests/_/schema/generated/__.js' -import { isError } from '../../../tests/_/schema/generated/Error.js' +import { isError } from '../../../tests/_/schema/generated/modules/Error.js' import * as Schema from '../../../tests/_/schema/schema.js' const client = Graffle.create({ schema: Schema.schema }) diff --git a/src/layers/6_client/client.extend.test.ts b/src/layers/6_client/client.extend.test.ts index 444749813..aa5c30e7c 100644 --- a/src/layers/6_client/client.extend.test.ts +++ b/src/layers/6_client/client.extend.test.ts @@ -4,12 +4,13 @@ import { db } from '../../../tests/_/db.js' import { createResponse, test } from '../../../tests/_/helpers.js' import { Graffle } from '../../../tests/_/schema/generated/__.js' import { oops } from '../../lib/anyware/specHelpers.js' +import { OrThrow } from '../7_extensions/OrThrow/OrThrow.js' const client = Graffle.create({ schema: 'https://foo', output: { defaults: { errorChannel: 'return' } } }) const headers = { 'x-foo': 'bar' } test('using an extension returns a copy of the client', () => { - const client2 = client.use(async () => {}) + const client2 = client.use(OrThrow()) expect(client2 !== client).toBe(true) }) @@ -19,7 +20,7 @@ describe(`entrypoint pack`, () => { expect(input.headers.get('x-foo')).toEqual(headers['x-foo']) return createResponse({ data: { id: db.id } }) }) - const client2 = client.use(async ({ pack }) => { + const client2 = client.anyware(async ({ pack }) => { return await pack({ input: { ...pack.input, headers } }) }) expect(await client2.query.id()).toEqual(db.id) @@ -28,7 +29,7 @@ describe(`entrypoint pack`, () => { fetch.mockImplementationOnce(async () => { return createResponse({ data: { id: db.id } }) }) - const client2 = client.use(async ({ pack }) => { + const client2 = client.anyware(async ({ pack }) => { const { exchange } = await pack({ input: { ...pack.input, headers } }) return await exchange({ input: exchange.input }) }) diff --git a/src/layers/6_client/client.raw.test-d.ts b/src/layers/6_client/client.raw.test-d.ts index 28bbb4712..4937b74ea 100644 --- a/src/layers/6_client/client.raw.test-d.ts +++ b/src/layers/6_client/client.raw.test-d.ts @@ -4,7 +4,7 @@ import { test } from '../../../tests/_/helpers.js' import { Graffle } from '../../../tests/_/schema/generated/__.js' import { schema } from '../../../tests/_/schema/schema.js' import { gql } from '../0_functions/gql.js' -import type { Envelope } from './handleOutput.js' +import type { RawResolveOutputReturnRootType } from './handleOutput.js' const g = Graffle.create({ schema }) @@ -15,9 +15,8 @@ describe(`TypedQueryDocumentNode with just data (no variables)`, () => { } ` test(`envelope data is typed`, () => { - // todo is it correct that result.data could be null or undefined? expectTypeOf(g.raw({ document })).resolves.toEqualTypeOf< - Envelope + RawResolveOutputReturnRootType >() }) test('variables are not allowed to be passed in', () => { @@ -34,7 +33,7 @@ describe(`TypedQueryDocumentNode with data and optional variables`, () => { ` test(`envelope data is typed`, () => { expectTypeOf(g.raw({ document })).resolves.toEqualTypeOf< - Envelope + RawResolveOutputReturnRootType >() }) test('variables are typed and allowed to be passed in or omitted', () => { diff --git a/src/layers/6_client/client.raw.test.ts b/src/layers/6_client/client.raw.test.ts index c249c75a6..e9a7f0e68 100644 --- a/src/layers/6_client/client.raw.test.ts +++ b/src/layers/6_client/client.raw.test.ts @@ -2,33 +2,17 @@ import { beforeEach, describe, expect } from 'vitest' import { test } from '../../../tests/_/helpers.js' import { Graffle } from '../../../tests/_/schema/generated/__.js' import { schema } from '../../../tests/_/schema/schema.js' -import { createExtension } from '../5_createExtension/createExtension.js' +import { createExtension } from './extension.js' // todo test with custom scalars const graffle = Graffle.create({ schema }) -test(`.rawOrThrow() throws if errors array non-empty`, async () => { - await expect(graffle.rawStringOrThrow({ document: `query {}` })).rejects.toMatchInlineSnapshot( - `[ContextualAggregateError: One or more errors in the execution result.]`, - ) -}) - -test(`.raw() returns errors in array`, async () => { - await expect(graffle.rawString({ document: `query {}` })).resolves.toMatchInlineSnapshot(` - { - "errors": [ - [GraphQLError: Syntax Error: Expected Name, found "}".], - ], - } - `) -}) - describe(`memory transport`, () => { let input: object | undefined - const spyInput = createExtension({ + const spyExchangeInput = createExtension({ name: `spy`, - anyware: ({ exchange }) => { + onRequest: ({ exchange }) => { if (exchange.input.transport === `memory`) { input = exchange.input } @@ -40,14 +24,12 @@ describe(`memory transport`, () => { }) describe(`operationName`, () => { test(`undefined by default`, async () => { - await graffle.use(spyInput).rawString({ document: `query { id }` }) + await graffle.use(spyExchangeInput).rawString({ document: `query { id }` }) expect(input).toMatchObject({ operationName: undefined }) }) test(`reflects explicit value`, async () => { - await graffle.use(spyInput).rawString({ document: `query { id }`, operationName: `foo` }) + await graffle.use(spyExchangeInput).rawString({ document: `query foo { id }`, operationName: `foo` }) expect(input).toMatchObject({ operationName: `foo` }) }) }) }) - -// todo http transport diff --git a/src/layers/6_client/client.rawString.test-d.ts b/src/layers/6_client/client.rawString.test-d.ts index 9f1624815..c67703ae1 100644 --- a/src/layers/6_client/client.rawString.test-d.ts +++ b/src/layers/6_client/client.rawString.test-d.ts @@ -4,7 +4,7 @@ import { test } from '../../../tests/_/helpers.js' import { Graffle } from '../../../tests/_/schema/generated/__.js' import { schema } from '../../../tests/_/schema/schema.js' import type { TypedDocumentString } from '../0_functions/types.js' -import type { Envelope } from './handleOutput.js' +import type { Envelope, RawResolveOutputReturnRootType } from './handleOutput.js' const g = Graffle.create({ schema }) @@ -16,9 +16,8 @@ describe(`TypedDocumentString with just data (no variables)`, () => { ` as TypedDocumentString<{ id: string }, {}> test(`envelope data is typed`, () => { - // todo is it correct that result.data could be null or undefined? expectTypeOf(g.rawString({ document })).resolves.toEqualTypeOf< - Envelope + RawResolveOutputReturnRootType >() }) test('variables are not allowed to be passed in', () => { @@ -36,7 +35,7 @@ describe(`TypedQueryDocumentNode with data and optional variables`, () => { // dprint-ignore test(`envelope data is typed`, () => { - expectTypeOf(g.rawString({ document })).resolves.toEqualTypeOf>() + expectTypeOf(g.rawString({ document })).resolves.toEqualTypeOf>() }) test('variables are typed and allowed to be passed in or omitted', () => { // Expect no type errors below diff --git a/src/layers/6_client/client.test.ts b/src/layers/6_client/client.test.ts index 1818f8fc0..69fcd01a3 100644 --- a/src/layers/6_client/client.test.ts +++ b/src/layers/6_client/client.test.ts @@ -3,11 +3,12 @@ import { createResponse, test } from '../../../tests/_/helpers.js' import { Graffle as Graffle2 } from '../../../tests/_/schema/generated/__.js' import { schema } from '../../../tests/_/schema/schema.js' import { Graffle } from '../../entrypoints/main.js' +import { OrThrow } from '../7_extensions/OrThrow/OrThrow.js' const endpoint = new URL(`https://foo.io/api/graphql`) describe(`without schemaIndex only raw is available`, () => { - const graffle = Graffle.create({ schema: endpoint }) + const graffle = Graffle.create({ schema: endpoint }).use(OrThrow()) test(`unavailable methods`, () => { // @ts-expect-error @@ -21,8 +22,8 @@ describe(`without schemaIndex only raw is available`, () => { }) test(`available methods`, () => { - expect(graffle.raw).toBeTypeOf(`function`) // eslint-disable-line - expect(graffle.rawOrThrow).toBeTypeOf(`function`) // eslint-disable-line + expect(graffle.raw).toBeTypeOf(`function`) + expect(graffle.rawOrThrow).toBeTypeOf(`function`) }) }) diff --git a/src/layers/6_client/client.transport-http.test.ts b/src/layers/6_client/client.transport-http.test.ts index fe404bb90..6767315f8 100644 --- a/src/layers/6_client/client.transport-http.test.ts +++ b/src/layers/6_client/client.transport-http.test.ts @@ -9,7 +9,7 @@ import type { CoreExchangeGetRequest, CoreExchangePostRequest } from './transpor const schema = new URL(`https://foo.io/api/graphql`) test(`anyware hooks are typed to http transport`, () => { - Graffle.create({ schema }).use(async ({ encode }) => { + Graffle.create({ schema }).anyware(async ({ encode }) => { expectTypeOf(encode.input.transport).toEqualTypeOf(Transport.http) const { pack } = await encode() expectTypeOf(pack.input.transport).toEqualTypeOf(Transport.http) diff --git a/src/layers/6_client/client.transport-memory.test.ts b/src/layers/6_client/client.transport-memory.test.ts index 3e8a88650..1d21a864c 100644 --- a/src/layers/6_client/client.transport-memory.test.ts +++ b/src/layers/6_client/client.transport-memory.test.ts @@ -5,7 +5,7 @@ import { Graffle } from '../../entrypoints/main.js' import { Transport } from '../5_core/types.js' test(`anyware hooks are typed to memory transport`, () => { - Graffle.create({ schema }).use(async ({ encode }) => { + Graffle.create({ schema }).anyware(async ({ encode }) => { expectTypeOf(encode.input.transport).toEqualTypeOf(Transport.memory) const { pack } = await encode() expectTypeOf(pack.input.transport).toEqualTypeOf(Transport.memory) diff --git a/src/layers/6_client/client.ts b/src/layers/6_client/client.ts index 396271501..df7db7b4f 100644 --- a/src/layers/6_client/client.ts +++ b/src/layers/6_client/client.ts @@ -3,6 +3,7 @@ import type { Anyware } from '../../lib/anyware/__.js' import type { Errors } from '../../lib/errors/__.js' import { isOperationTypeName, operationTypeNameToRootTypeName, type RootTypeName } from '../../lib/graphql.js' import { mergeRequestInit } from '../../lib/http.js' +import { proxyGet, type SimplifyExceptError } from '../../lib/prelude.js' import type { BaseInput, BaseInput_, TypedDocumentString } from '../0_functions/types.js' import { Schema } from '../1_Schema/__.js' import { readMaybeThunk } from '../1_Schema/core/helpers.js' @@ -12,16 +13,12 @@ import { Core } from '../5_core/__.js' import { type HookDefEncode } from '../5_core/core.js' import { type InterfaceRaw, type TransportHttp } from '../5_core/types.js' import type { DocumentFn } from './document.js' -import { type Envelope, handleOutput } from './handleOutput.js' -import type { GetRootTypeMethods } from './RootTypeMethods.js' -import { - type Config, - readConfigErrorCategoryOutputChannel, - traditionalGraphqlOutput, - traditionalGraphqlOutputThrowing, -} from './Settings/Config.js' +import { createExtension, type Extension, type ExtensionCallBuilderMerge } from './extension.js' +import { handleOutput, type RawResolveOutputReturnRootType } from './handleOutput.js' +import type { BuilderRequestMethodsGeneratedRootTypes } from './RootTypeMethods.js' +import { type Config } from './Settings/Config.js' import { type InputStatic } from './Settings/Input.js' -import type { AddIncrementalInput, InputIncrementable } from './Settings/inputIncrementable/inputIncrementable.js' +import type { AddIncrementalInput, WithInput } from './Settings/inputIncrementable/inputIncrementable.js' import { type InputToConfig, inputToConfig } from './Settings/InputToConfig.js' /** @@ -47,7 +44,7 @@ export type GraffleExecutionResultVar<$Config extends Config = Config> = */ response?: Response } - : {}) // eslint-disable-line + : {}) ) | ErrorsOther @@ -79,47 +76,66 @@ const resolveRawParameters = (parameters: RawParameters) => { return parameters[0] } -// todo no config needed? // dprint-ignore -export type ClientRaw<$Config extends Config> = { - rawString<$Data, $Variables>(input: BaseInput>): Promise> - rawStringOrThrow<$Data, $Variables>(input: BaseInput>): Promise> +export type BuilderRequestMethods<$Config extends Config, $Index extends null | Schema.Index >= + & BuilderRequestMethodsStatic<$Config> + & ( + $Index extends Schema.Index + ? BuilderRequestMethodsGenerated<$Config, $Index> + : {} + ) - raw<$Data, $Variables>(input: BaseInput>): Promise> - rawOrThrow<$Data, $Variables>(input: BaseInput>): Promise> +// todo no config needed? +// dprint-ignore +export type BuilderRequestMethodsStatic<$Config extends Config> = { + raw: <$Data extends Record, $Variables>(input: BaseInput>) => + Promise>> + rawString: <$Data extends Record, $Variables>(input: BaseInput>) => + Promise> } -export type Extension = { - name: string - anyware?: Anyware.Extension2 +// dprint +export type BuilderRequestMethodsGenerated<$Config extends Config, $Index extends Schema.Index> = + & BuilderRequestMethodsGeneratedStatic<$Config, $Index> + & BuilderRequestMethodsGeneratedRootTypes<$Config, $Index> + +export type BuilderRequestMethodsGeneratedStatic<$Config extends Config, $Index extends Schema.Index> = { + document: DocumentFn<$Config, $Index> } // dprint-ignore -export type Client<$Index extends Schema.Index | null, $Config extends Config> = +export type Client<$Index extends Schema.Index | null, $Config extends Config, $AdditionalMethods = unknown> = { internal: { config: $Config } } - & ClientRaw<$Config> + & $AdditionalMethods + & BuilderRequestMethodsStatic<$Config> & ( $Index extends Schema.Index + // todo OmitDeeply ? ClientTyped<$Index, $Config> - : {} // eslint-disable-line + : {} ) & { // eslint-disable-next-line // @ts-ignore passes after generation - with: <$Input extends InputIncrementable<$Config>>(input: $Input) => Client<$Index, AddIncrementalInput<$Config, $Input>> - use: (extension: Extension | Anyware.Extension2>) => Client<$Index, $Config> - retry: (extension: Anyware.Extension2) => Client<$Index, $Config> + with: <$Input extends WithInput<$Config>>(input: $Input) => + // eslint-disable-next-line + // @ts-ignore passes after generation + Client<$Index, AddIncrementalInput<$Config, $Input>> + use: <$Extension extends Extension>(extension: $Extension) => + Client<$Index, $Config, $AdditionalMethods & ExtensionCallBuilderMerge<$Extension, { Index:$Index, Config:$Config, AdditionalMethods:$AdditionalMethods }>> + anyware: (anyware: Anyware.Extension2>) => + Client<$Index, $Config, $AdditionalMethods> + retry: (extension: Anyware.Extension2) => + Client<$Index, $Config> } export type ClientTyped<$Index extends Schema.Index, $Config extends Config> = - & { - document: DocumentFn<$Config, $Index> - } - & GetRootTypeMethods<$Config, $Index> + & BuilderRequestMethodsGeneratedStatic<$Config, $Index> + & BuilderRequestMethodsGeneratedRootTypes<$Config, $Index> // dprint-ignore type Create = <$Input extends InputStatic>(input: $Input) => @@ -142,7 +158,7 @@ export const create: Create = (input) => { retry: null, input, } - return create_(initialState) + return createWithState(initialState) } interface CreateState { @@ -151,7 +167,7 @@ interface CreateState { extensions: Extension[] } -const create_ = ( +const createWithState = ( state: CreateState, ) => { /** @@ -229,17 +245,13 @@ const create_ = ( get: (_, key) => { if (typeof key === `symbol`) throw new Error(`Symbols not supported.`) - // todo We need to document that in order for this to 100% work none of the user's root type fields can end with "OrThrow". - const isOrThrow = key.endsWith(`OrThrow`) - const contextWithReturnModeSet = isOrThrow ? contextConfigSetOrThrow(context) : context - if (key.startsWith(`$batch`)) { return async (selectionSetOrIndicator: SelectionSetOrIndicator) => - executeRootType(contextWithReturnModeSet, rootTypeName, selectionSetOrIndicator as GraphQLObjectSelection) + executeRootType(context, rootTypeName, selectionSetOrIndicator as GraphQLObjectSelection) } else { - const fieldName = isOrThrow ? key.slice(0, -7) : key + const fieldName = key return (selectionSetOrArgs: SelectionSetOrArgs) => - executeRootTypeField(contextWithReturnModeSet, rootTypeName, fieldName, selectionSetOrArgs) + executeRootTypeField(context, rootTypeName, fieldName, selectionSetOrArgs) } }, }) @@ -255,8 +267,8 @@ const create_ = ( const run = async (context: Context, initialInput: HookDefEncode['input']) => { const result = await Core.anyware.run({ initialInput, - retryingExtension: context.retry as any, // eslint-disable-line - extensions: context.extensions.filter(_ => _.anyware !== undefined).map(_ => _.anyware!) as any, // eslint-disable-line + retryingExtension: context.retry as any, + extensions: context.extensions.filter(_ => _.onRequest !== undefined).map(_ => _.onRequest!) as any, }) as GraffleExecutionResultVar return handleOutput(context, result) @@ -280,38 +292,37 @@ const create_ = ( } // @ts-expect-error ignoreme - const client: Client = { + const clientDirect: Client = { internal: { config: context.config, }, raw: async (...args: RawParameters) => { const input = resolveRawParameters(args) - const contextWithOutputSet = updateContextConfig(context, { ...context.config, output: traditionalGraphqlOutput }) - return await runRaw(contextWithOutputSet, input) - }, - rawOrThrow: async (...args: RawParameters) => { - const input = resolveRawParameters(args) - const contextWithOutputSet = updateContextConfig(context, { - ...context.config, - output: traditionalGraphqlOutputThrowing, - }) - return await runRaw(contextWithOutputSet, input) + // const contextWithOutputSet = updateContextConfig(context, { ...context.config, output: traditionalGraphqlOutput }) + return await runRaw(context, input) }, + // rawOrThrow: async (...args: RawParameters) => { + // const input = resolveRawParameters(args) + // const contextWithOutputSet = updateContextConfig(context, { + // ...context.config, + // output: traditionalGraphqlOutputThrowing, + // }) + // return await runRaw(contextWithOutputSet, input) + // }, rawString: async (...args: RawParameters) => { - // eslint-disable-next-line - return await client.raw(...args) - }, - rawStringOrThrow: async (...args: RawParameters) => { - // eslint-disable-next-line - return await client.rawOrThrow(...args) + return await clientDirect.raw(...args) }, - with: (input: InputIncrementable) => { - return create_({ + // rawStringOrThrow: async (...args: RawParameters) => { + // // eslint-disable-next-line + // return await client.rawOrThrow(...args) + // }, + with: (input: WithInput) => { + return createWithState({ ...state, // @ts-expect-error fixme input: { ...state.input, - output: state.input.output, + output: input.output, transport: { ...state.input.transport, ...input.transport, @@ -320,15 +331,17 @@ const create_ = ( }, }) }, - use: (extensionOrAnyware: Extension | Anyware.Extension2) => { - const extension = typeof extensionOrAnyware === `function` - ? { anyware: extensionOrAnyware, name: extensionOrAnyware.name } - : extensionOrAnyware - // todo test that adding extensions returns a copy of client - return create_({ ...state, extensions: [...state.extensions, extension] }) + use: (extension: Extension) => { + return createWithState({ ...state, extensions: [...state.extensions, extension] }) }, - retry: (extension: Anyware.Extension2) => { - return create_({ ...state, retry: extension }) + anyware: (anyware: Anyware.Extension2) => { + return createWithState({ + ...state, + extensions: [...state.extensions, createExtension({ name: `InlineAnyware`, onRequest: anyware })], + }) + }, + retry: (anyware: Anyware.Extension2) => { + return createWithState({ ...state, retry: anyware }) }, } @@ -339,7 +352,7 @@ const create_ = ( schemaIndex: state.input.schemaIndex, } - Object.assign(client, { + Object.assign(clientDirect, { document: (documentObject: DocumentObject) => { const hasMultipleOperations = Object.keys(documentObject).length > 1 @@ -372,14 +385,6 @@ const create_ = ( const { selection, rootTypeName } = processInput(maybeOperationName) return await executeRootType(typedContext, rootTypeName, selection) }, - runOrThrow: async (maybeOperationName: string) => { - const { selection, rootTypeName } = processInput(maybeOperationName) - return await executeRootType( - contextConfigSetOrThrow(typedContext), - rootTypeName, - selection, - ) - }, } }, query: createRootTypeMethods(typedContext, `Query`), @@ -389,51 +394,20 @@ const create_ = ( }) } - return client -} + const clientProxy = proxyGet(clientDirect, ({ path, property }) => { + const onGetHandlers = state.extensions.map(_ => _.onBuilderGet).filter(_ => _ !== undefined) -const contextConfigSetOrThrow = <$Context extends Context>(context: $Context): $Context => { - if (isContextConfigOrThrowSemantics(context)) return context - - return updateContextConfig(context, { - ...context.config, - output: { - ...context.config.output, - errors: { - execution: `throw`, - other: `throw`, - schema: `throw`, - }, - envelope: { - ...context.config.output.envelope, - errors: { - execution: false, - other: false, - schema: false, - }, - }, - }, - }) -} - -const isContextConfigOrThrowSemantics = ({ config }: Context): boolean => { - const isAllCategoriesThrowOrDisabled = readConfigErrorCategoryOutputChannel(config, `execution`) === `throw` - && readConfigErrorCategoryOutputChannel(config, `other`) === `throw` - && (readConfigErrorCategoryOutputChannel(config, `schema`) === `throw` - || readConfigErrorCategoryOutputChannel(config, `schema`) === `throw`) // todo: or false and not using schema errors - - if (!isAllCategoriesThrowOrDisabled) return false + for (const onGetHandler of onGetHandlers) { + const result = onGetHandler({ context, client: clientDirect, path, property }) + if (result !== undefined) return result + } - if ( - config.output.envelope.enabled - && Object.values(config.output.envelope.errors.execution).filter(_ => _ === true).length > 0 - ) { - return false - } + return undefined + }) as any as Client - return true + return clientProxy as any } -const updateContextConfig = <$Context extends Context>(context: $Context, config: Config): $Context => { - return { ...context, config: { ...context.config, ...config } } -} +// const updateContextConfig = <$Context extends Context>(context: $Context, config: Config): $Context => { +// return { ...context, config: { ...context.config, ...config } } +// } diff --git a/src/layers/6_client/document.test-d.ts b/src/layers/6_client/document.test-d.ts index ea3978698..fecea4b6d 100644 --- a/src/layers/6_client/document.test-d.ts +++ b/src/layers/6_client/document.test-d.ts @@ -72,22 +72,3 @@ describe(`document(...).run()`, () => { expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>() }) }) - -describe(`document(...).runOrThrow()`, () => { - describe(`query result field`, () => { - test(`with __typename`, () => { - const result = graffle.document({ x: { query: { resultNonNull: { __typename: true } } } }).runOrThrow() - expectTypeOf(result).resolves.toEqualTypeOf<{ resultNonNull: { __typename: 'Object1' } }>() - }) - test(`without __typename`, () => { - const result = graffle.document({ x: { query: { resultNonNull: {} } } }).runOrThrow() - expectTypeOf(result).resolves.toEqualTypeOf<{ resultNonNull: { __typename: 'Object1' } }>() - }) - test(`multiple via alias`, () => { - const result = graffle.document({ x: { query: { resultNonNull: {}, resultNonNull_as_x: {} } } }).runOrThrow() - expectTypeOf(result).resolves.toEqualTypeOf< - { resultNonNull: { __typename: 'Object1' }; x: { __typename: 'Object1' } } - >() - }) - }) -}) diff --git a/src/layers/6_client/document.test.ts b/src/layers/6_client/document.test.ts index da9ee4b96..6fe398eb1 100644 --- a/src/layers/6_client/document.test.ts +++ b/src/layers/6_client/document.test.ts @@ -86,29 +86,3 @@ test(`document with one mutation and one query`, async () => { await expect(run(`foo`)).resolves.toEqual({ id: db.id1 }) await expect(run(`bar`)).resolves.toEqual({ idNonNull: db.id1 }) }) - -describe(`document(...).runOrThrow()`, () => { - describe(`query result field`, () => { - test(`with __typename`, async () => { - const result = graffle.document({ - x: { query: { resultNonNull: { $: { case: `ErrorOne` }, __typename: true } } }, - }) - .runOrThrow() - await expect(result).rejects.toMatchInlineSnapshot(`[Error: Failure on field resultNonNull: ErrorOne]`) - }) - test(`without __typename`, async () => { - const result = graffle.document({ x: { query: { resultNonNull: { $: { case: `ErrorOne` } } } } }).runOrThrow() - await expect(result).rejects.toMatchInlineSnapshot( - `[Error: Failure on field resultNonNull: ErrorOne]`, - ) - }) - test.todo(`multiple via alias`, async () => { - const result = graffle.document({ - x: { query: { resultNonNull: { $: { case: `ErrorOne` } }, resultNonNull_as_x: { $: { case: `ErrorOne` } } } }, - }).runOrThrow() - await expect(result).rejects.toMatchInlineSnapshot( - `[ContextualAggregateError: Two or more schema errors in the execution result.]`, - ) - }) - }) -}) diff --git a/src/layers/6_client/document.ts b/src/layers/6_client/document.ts index ff49ca17d..3fff08a6f 100644 --- a/src/layers/6_client/document.ts +++ b/src/layers/6_client/document.ts @@ -7,30 +7,23 @@ import { SelectionSet } from '../3_SelectionSet/__.js' import type { Context, DocumentObject } from '../3_SelectionSet/encode.js' import type { ResultSet } from '../4_ResultSet/__.js' import type { ResolveOutputReturnRootType } from './handleOutput.js' -import type { AugmentRootTypeSelectionWithTypename, Config, OrThrowifyConfig } from './Settings/Config.js' +import type { AugmentRootTypeSelectionWithTypename, Config } from './Settings/Config.js' // dprint-ignore export type DocumentFn<$Config extends Config, $Index extends Schema.Index> = -<$Document extends Document<$Index>>(document: ValidateDocumentOperationNames>) => - { - run: < - $Name extends keyof $Document & string, - $Params extends (IsMultipleKeys<$Document> extends true ? [name: $Name] : ([] | [name: $Name | undefined])), - >(...params: $Params) => Promise< - ResolveOutputReturnRootType<$Config, $Index, ResultSet.Root, $Index, GetRootType<$Document[$Name]>>> - > - runOrThrow: < - $Name extends keyof $Document & string, - $Params extends (IsMultipleKeys<$Document> extends true ? [name: $Name] : ([] | [name: $Name | undefined])), - >(...params: $Params) => Promise< - ResolveOutputReturnRootType< - // @ts-expect-error fixme - OrThrowifyConfig<$Config>, - $Index, - // @ts-expect-error fixme - ResultSet.Root, $Index, $Document[$Name]>, $Index, GetRootType<$Document[$Name]>>> - > - } + <$Document extends Document<$Index>>(document: ValidateDocumentOperationNames>) => + DocumentRunner<$Config, $Index, $Document> + +// dprint-ignore +export type DocumentRunner<$Config extends Config, $Index extends Schema.Index, $Document extends Document<$Index>> = { + run: < + $Name extends keyof $Document & string, + $Params extends (IsMultipleKeys<$Document> extends true ? [name: $Name] : ([] | [name: $Name | undefined])), + >(...params: $Params) => Promise< + // $Config + ResolveOutputReturnRootType<$Config, $Index, ResultSet.Root, $Index, GetRootType<$Document[$Name]>>> + > +} export const toDocumentString = ( context: Context, @@ -39,7 +32,7 @@ export const toDocumentString = ( return Object.entries(document).map(([operationName, operationDocument]) => { const operationType = `query` in operationDocument ? `query` : `mutation` const rootType = operationTypeNameToRootTypeName[operationType] - const rootTypeDocument = (operationDocument as any)[operationType] as SelectionSet.Print.GraphQLObjectSelection // eslint-disable-line + const rootTypeDocument = (operationDocument as any)[operationType] as SelectionSet.Print.GraphQLObjectSelection const schemaRootType = context.schemaIndex[`Root`][rootType] if (!schemaRootType) throw new Error(`Schema has no ${rootType} root type`) diff --git a/src/layers/6_client/extension.ts b/src/layers/6_client/extension.ts new file mode 100644 index 000000000..9c6dac75e --- /dev/null +++ b/src/layers/6_client/extension.ts @@ -0,0 +1,64 @@ +import type { Anyware } from '../../lib/anyware/__.js' +import type { HKT } from '../../lib/hkt/__.js' +import type { Fn } from '../../lib/hkt/hkt.js' +import type { Core } from '../5_core/__.js' +import type { Client, Context } from './client.js' +import type { Config } from './Settings/Config.js' + +export interface ExtensionBuilderPassthrough extends Extension { + return: this['params']['AdditionalMethods'] +} + +type TypeCallParams = { Config: unknown; Index: unknown; AdditionalMethods: unknown } + +export type ExtensionCallBuilderMerge<$Extension extends Extension, $Params extends TypeCallParams> = + ($Extension & { params: $Params })['builderMerge'] + +export interface Extension extends Base, Fn { + /** + * todo + */ + builderMerge: {} +} + +interface Base { + /** + * The name of the extension + */ + name: string + /** + * Anyware executed on every request. + */ + onRequest?: Anyware.Extension2 + /** + * Hook into "get" events on the builder proxy. Useful for adding new methods or manipulating existing ones. + * + * Invoked when a non-record-like-object is reached. For example these: + * + * - graffle.use (property: "use") + * - graffle.query.foo (property: "foo", path: ["query"]) + * + * Return nothing/`undefined` to passthrough. + * + * Anything else returned becomes the result of the proxy "get" event. + * + * When there are multiple extensions with "onBuilderGet" handlers they form a execution stack starting from the first registered extension. + * The first handler to return something short circuits the rest. + */ + onBuilderGet?: ( + input: { + context: Context + path: string[] + property: string + client: Client + }, + ) => unknown +} + +type ToBase<$Extension extends Extension> = Omit, 'builderMerge'> + +export const createExtension = <$Extension extends Extension = Extension>( + extension: ToBase<$Extension>, +): $Extension => { + return extension as $Extension +} diff --git a/src/layers/6_client/handleOutput.ts b/src/layers/6_client/handleOutput.ts index ee1b623f1..a5f033133 100644 --- a/src/layers/6_client/handleOutput.ts +++ b/src/layers/6_client/handleOutput.ts @@ -3,7 +3,7 @@ import type { Simplify } from 'type-fest' import type { ConditionalSimplify } from 'type-fest/source/conditional-simplify.js' import { Errors } from '../../lib/errors/__.js' import type { GraphQLExecutionResultError } from '../../lib/graphql.js' -import { isPlainObject, type SimplifyExceptError, type Values } from '../../lib/prelude.js' +import { isRecordLikeObject, type SimplifyExceptError, type Values } from '../../lib/prelude.js' import type { Schema } from '../1_Schema/__.js' import { Transport } from '../5_core/types.js' import type { Context, ErrorsOther, GraffleExecutionResultVar, TypedContext } from './client.js' @@ -71,7 +71,7 @@ export const handleOutput = ( { if (isTypedContext(context)) { if (c.errors.schema !== false) { - if (!isPlainObject(result.data)) throw new Error(`Expected data to be an object.`) + if (!isRecordLikeObject(result.data)) throw new Error(`Expected data to be an object.`) const schemaErrors = Object.entries(result.data).map(([rootFieldName, rootFieldValue]) => { // todo this check would be nice but it doesn't account for aliases right now. To achieve this we would // need to have the selection set available to use and then do a costly analysis for all fields that were aliases. @@ -80,7 +80,7 @@ export const handleOutput = ( // const isResultField = Boolean(schemaIndex.error.rootResultFields.Query[rootFieldName]) // if (!isResultField) return null // if (!isPlainObject(rootFieldValue)) return new Error(`Expected result field to be an object.`) - if (!isPlainObject(rootFieldValue)) return null + if (!isRecordLikeObject(rootFieldValue)) return null const __typename = rootFieldValue[`__typename`] if (typeof __typename !== `string`) throw new Error(`Expected __typename to be selected and a string.`) const isErrorObject = Boolean( @@ -127,6 +127,27 @@ const isAbortError = (error: any): error is DOMException & { name: 'AbortError' const isTypedContext = (context: Context): context is TypedContext => `schemaIndex` in context +// dprint-ignore +export type RawResolveOutputReturnRootType<$Config extends Config, $Data> = + SimplifyExceptError< + | IfConfiguredGetOutputErrorReturns<$Config> + | ( + $Config['output']['envelope']['enabled'] extends true + // todo even when envelope is enabled, its possible errors can not be included in its output. + // When not, undefined should be removed from the data property. + ? Envelope<$Config, $Data> + // Note 1 + // `undefined` is not a possible type because that would only happen if an error occurred. + // If an error occurs when the envelope is disabled then either it throws or is returned. + // No case swallows the error and returns undefined data. + // + // Note 2 + // null is possible because of GraphQL null propagation. + // todo We need to integrate this reality into the the other typed non-envelope output types too. + : $Data | null + ) + > + // dprint-ignore export type ResolveOutputReturnRootType<$Config extends Config, $Index extends Schema.Index, $Data> = SimplifyExceptError< @@ -204,14 +225,14 @@ export type Envelope<$Config extends Config, $Data = unknown, $Errors extends Re & ( $Config['transport']['type'] extends 'http' ? { response: Response } - : {} // eslint-disable-line + : {} ) // todo remove use of errors type variable. Rely only on $Config. & ( $Errors extends [] - ? {} // eslint-disable-line + ? {} : IsEnvelopeWithoutErrors<$Config> extends true - ? {} // eslint-disable-line + ? {} : { errors?: ReadonlyArray } diff --git a/src/layers/6_client/rootTypeMethods.test.ts b/src/layers/6_client/rootTypeMethods.test.ts index f4c0e4011..a474f7b86 100644 --- a/src/layers/6_client/rootTypeMethods.test.ts +++ b/src/layers/6_client/rootTypeMethods.test.ts @@ -2,8 +2,9 @@ import { describe, expect, test } from 'vitest' import { db } from '../../../tests/_/db.js' import { Graffle } from '../../../tests/_/schema/generated/__.js' import * as Schema from '../../../tests/_/schema/schema.js' +import { OrThrow } from '../7_extensions/OrThrow/OrThrow.js' -const graffle = Graffle.create({ schema: Schema.schema }) +const graffle = Graffle.create({ schema: Schema.schema }).use(OrThrow()) // dprint-ignore describe(`query`, () => { diff --git a/src/layers/6_client/types.ts b/src/layers/6_client/types.ts index 68440421a..ecccbc423 100644 --- a/src/layers/6_client/types.ts +++ b/src/layers/6_client/types.ts @@ -1,6 +1,10 @@ import type { TypedQueryDocumentNode } from 'graphql' +import type { SomeData } from '../../lib/graphql.js' import type { TypedDocumentString } from '../0_functions/types.js' -export type DocumentInput = string | TypedDocumentString | TypedQueryDocumentNode +export type DocumentInput<$Data extends SomeData = SomeData, V = any> = + | string + | TypedDocumentString<$Data, V> + | TypedQueryDocumentNode<$Data, V> export type OperationNameInput = string diff --git a/src/layers/7_extensions/Opentelemetry/Opentelemetry.ts b/src/layers/7_extensions/Opentelemetry/Opentelemetry.ts index 03eb7c793..08f9681e9 100644 --- a/src/layers/7_extensions/Opentelemetry/Opentelemetry.ts +++ b/src/layers/7_extensions/Opentelemetry/Opentelemetry.ts @@ -1,5 +1,5 @@ import { trace, type Tracer } from '@opentelemetry/api' -import { createExtension } from '../../5_createExtension/createExtension.js' +import { createExtension } from '../../6_client/extension.js' import { createConfig, type Input } from './config.js' const name = `Opentelemetry` @@ -11,7 +11,7 @@ export const Opentelemetry = (input?: Input) => { return createExtension({ name, - anyware: async ({ encode }) => { + onRequest: async ({ encode }) => { return await startActiveGraffleSpan(`request`, async () => { const { pack } = await startActiveGraffleSpan(`encode`, encode) const { exchange } = await startActiveGraffleSpan(`pack`, pack) diff --git a/src/layers/7_extensions/OrThrow/OrThrow.test-d.ts b/src/layers/7_extensions/OrThrow/OrThrow.test-d.ts new file mode 100644 index 000000000..e7c22109b --- /dev/null +++ b/src/layers/7_extensions/OrThrow/OrThrow.test-d.ts @@ -0,0 +1,31 @@ +import { describe, expectTypeOf, test } from 'vitest' +import { Graffle } from '../../../../tests/_/schema/generated/__.js' +import { schema } from '../../../../tests/_/schema/schema.js' +import { OrThrow } from './OrThrow.js' + +const graffle = Graffle.create({ schema }).use(OrThrow()) + +describe(`documentOrThrow(...).run()`, () => { + describe(`query result field`, () => { + test(`with __typename`, async () => { + const result = await graffle.documentOrThrow({ x: { query: { resultNonNull: { __typename: true } } } }).run() + expectTypeOf(result).toEqualTypeOf<{ resultNonNull: { __typename: 'Object1' } }>() + }) + test(`without __typename`, async () => { + const result = await graffle.documentOrThrow({ x: { query: { resultNonNull: {} } } }).run() + expectTypeOf(result).toEqualTypeOf<{ resultNonNull: { __typename: 'Object1' } }>() + }) + test(`multiple via alias`, async () => { + const result = await graffle.documentOrThrow({ x: { query: { resultNonNull: {}, resultNonNull_as_x: {} } } }) + .run() + expectTypeOf(result).toEqualTypeOf< + { resultNonNull: { __typename: 'Object1' }; x: { __typename: 'Object1' } } + >() + }) + }) +}) + +test(`can customize the suffix`, () => { + const graffle = Graffle.create({ schema }).use(OrThrow({ suffix: `_` })) + graffle.document_ +}) diff --git a/src/layers/7_extensions/OrThrow/OrThrow.test.ts b/src/layers/7_extensions/OrThrow/OrThrow.test.ts new file mode 100644 index 000000000..44bfb888c --- /dev/null +++ b/src/layers/7_extensions/OrThrow/OrThrow.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, test } from 'vitest' +import { Graffle } from '../../../../tests/_/schema/generated/__.js' +import { schema } from '../../../../tests/_/schema/schema.js' +import { OrThrow } from './OrThrow.js' + +const graffle = Graffle.create({ schema }).use(OrThrow()) + +describe(`document(...).runOrThrow()`, () => { + describe(`query result field`, () => { + test(`with __typename`, async () => { + const result = graffle.documentOrThrow({ + x: { query: { resultNonNull: { $: { case: `ErrorOne` }, __typename: true } } }, + }) + .run() + await expect(result).rejects.toMatchInlineSnapshot(`[Error: Failure on field resultNonNull: ErrorOne]`) + }) + test(`without __typename`, async () => { + const result = graffle.documentOrThrow({ x: { query: { resultNonNull: { $: { case: `ErrorOne` } } } } }).run() + await expect(result).rejects.toMatchInlineSnapshot( + `[Error: Failure on field resultNonNull: ErrorOne]`, + ) + }) + test.todo(`multiple via alias`, async () => { + const result = graffle.documentOrThrow({ + x: { query: { resultNonNull: { $: { case: `ErrorOne` } }, resultNonNull_as_x: { $: { case: `ErrorOne` } } } }, + }).run() + await expect(result).rejects.toMatchInlineSnapshot( + `[ContextualAggregateError: Two or more schema errors in the execution result.]`, + ) + }) + }) +}) + +test(`.rawOrThrow() throws if errors array non-empty`, async () => { + await expect(graffle.rawStringOrThrow({ document: `query {}` })).rejects.toMatchInlineSnapshot( + `[ContextualAggregateError: One or more errors in the execution result.]`, + ) +}) diff --git a/src/layers/7_extensions/OrThrow/OrThrow.ts b/src/layers/7_extensions/OrThrow/OrThrow.ts new file mode 100644 index 000000000..f146d6452 --- /dev/null +++ b/src/layers/7_extensions/OrThrow/OrThrow.ts @@ -0,0 +1,69 @@ +import { + type BuilderConfig, + type BuilderRequestMethods, + createExtension, + type Extension, + type WithInput, +} from '../../../entrypoints/main.js' +import { type ConfigManager, getValueAtPath, type SuffixMethodsDeep } from '../../../lib/prelude.js' +import { type Config, createConfig, type Input } from './config.js' + +export const OrThrow = (input?: $Input) => { + const config = createConfig(input) + + return createExtension>>({ + name: `OrThrow`, + onBuilderGet: ({ client, property, path }) => { + if (!property.endsWith(config.suffix)) return + + // todo redesign input to allow to force throw always + // todo pull pre-configured config from core + const graffleConfig: WithInput = { + output: { + envelope: { + enabled: client.internal.config.output.envelope.enabled, + errors: { + execution: false, + other: false, + // @ts-expect-error + schema: false, + }, + }, + errors: { + execution: `throw`, + other: `throw`, + // @ts-expect-error + schema: `throw`, + }, + }, + } + + return (...args: [...unknown[]]) => { + const redirectedPath = [...path, property.slice(0, config.suffix.length * -1)] + const clientReconfigured = client.with(graffleConfig) + const value = getValueAtPath(clientReconfigured, redirectedPath) + const valueType = typeof value + if (valueType !== `function`) { + throw new Error(`Expected function at path ${redirectedPath.join(`.`)} but got ${valueType}`) + } + return (value as any)(...args) + } + }, + }) +} + +interface OrThrowExtension<$Input extends Config> extends Extension { + builderMerge: SuffixMethodsDeep< + $Input['suffix'], + BuilderRequestMethods< + // @ts-expect-error fixme + OrThrowifyConfig, + this['params']['Index'] + > + > +} + +// todo this changed, check tests, add new tests as needed. +// dprint-ignore +export type OrThrowifyConfig<$BuilderConfig extends BuilderConfig> = + ConfigManager.Set<$BuilderConfig, ['output', 'errors'], { other: 'throw', execution: 'throw', schema: 'throw' }> diff --git a/src/layers/7_extensions/OrThrow/config.ts b/src/layers/7_extensions/OrThrow/config.ts new file mode 100644 index 000000000..509e9d714 --- /dev/null +++ b/src/layers/7_extensions/OrThrow/config.ts @@ -0,0 +1,35 @@ +import type { ConfigManager } from '../../../lib/prelude.js' + +export type Input = { + /** + * @defaultValue `OrThrow` + */ + suffix?: string +} + +export type Config = { + suffix: string +} + +export const defaultConfig = { + suffix: `OrThrow` as const, +} satisfies Config + +export type DefaultConfig = { + suffix: typeof defaultConfig.suffix +} + +export const createConfig = (input?: Input): Config => { + return { + suffix: input?.suffix ?? defaultConfig.suffix, + } +} + +// dprint-ignore +export type createConfig<$Input extends Input> = + // If base type then can mean nothing was passed into constrained optional input. + Input extends $Input + ? DefaultConfig + : { + suffix: ConfigManager.OrDefault<$Input['suffix'], DefaultConfig['suffix']> + } diff --git a/src/layers/7_extensions/Upload/Upload.test.ts b/src/layers/7_extensions/Upload/Upload.test.ts index 9aba2ccef..302c4091c 100644 --- a/src/layers/7_extensions/Upload/Upload.test.ts +++ b/src/layers/7_extensions/Upload/Upload.test.ts @@ -1,7 +1,6 @@ // todo in order to test jsdom, we need to boot the server in a separate process // @vitest-environment node -import { omit } from 'es-toolkit' import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest' import { schema } from '../../../../tests/_/schemaUpload/schema.js' import { Graffle } from '../../../entrypoints/main.js' @@ -27,7 +26,9 @@ beforeAll(async () => { }) beforeEach(() => { - graffle = Graffle.create({ schema: schemaServer.url }).use(Upload) + // todo direct assignment leads to "excessive ..." error + const graffle_ = Graffle.create({ schema: schemaServer.url }).use(Upload()) + graffle = graffle_ }) afterAll(async () => { @@ -35,23 +36,19 @@ afterAll(async () => { }) test(`upload`, async () => { - const result = await graffle.rawString({ + const data = await graffle.rawString({ document: ` mutation ($blob: Upload!) { readTextFile(blob: $blob) } `, variables: { - blob: new Blob([`Hello World`], { type: `text/plain` }) as any, // eslint-disable-line + blob: new Blob([`Hello World`], { type: `text/plain` }) as any, }, }) - expect(omit(result, [`response`])).toMatchInlineSnapshot(` + expect(data).toMatchInlineSnapshot(` { - "data": { - "readTextFile": "Hello World", - }, - "errors": undefined, - "extensions": undefined, + "readTextFile": "Hello World", } `) }) diff --git a/src/layers/7_extensions/Upload/Upload.ts b/src/layers/7_extensions/Upload/Upload.ts index 00cdc185a..177634df1 100644 --- a/src/layers/7_extensions/Upload/Upload.ts +++ b/src/layers/7_extensions/Upload/Upload.ts @@ -1,58 +1,43 @@ +import { createExtension } from '../../../entrypoints/main.js' import type { StandardScalarVariables } from '../../../lib/graphql.js' -import { createExtension } from '../../5_createExtension/createExtension.js' import { createBody } from './createBody.js' /** * @see https://github.com/jaydenseric/graphql-multipart-request-spec */ -export const Upload = createExtension({ - name: `Upload`, - anyware: async ({ pack }) => { - // const { pack } = await encode({ - // using: { - // body: (input) => { - // const hasUploadScalarVariable = input.variables && isUsingUploadScalar(input.variables) - // if (!hasUploadScalarVariable) return +export const Upload = () => + createExtension({ + name: `Upload`, + onRequest: async ({ pack }) => { + // Remove the content-type header so that fetch sets it automatically upon seeing the body is a FormData instance. + // @see https://muffinman.io/blog/uploading-files-using-fetch-multipart-form-data/ + // @see https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data + // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition + return await pack({ + // todo rename "using" to "with" + using: { + body: (input) => { + const hasUploadScalarVariable = input.variables && isUsingUploadScalar(input.variables) + if (!hasUploadScalarVariable) return - // // TODO we can probably get file upload working for in-memory schemas too :) - // if (encode.input.transport !== `http`) throw new Error(`Must be using http transport to use "Upload" scalar.`) + // TODO we can probably get file upload working for in-memory schemas too :) + if (pack.input.transport !== `http`) throw new Error(`Must be using http transport to use "Upload" scalar.`) - // return createBody({ - // query: input.query, - // variables: input.variables!, - // }) - // }, - // }, - // }) - // Remove the content-type header so that fetch sets it automatically upon seeing the body is a FormData instance. - // @see https://muffinman.io/blog/uploading-files-using-fetch-multipart-form-data/ - // @see https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data - // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition - return await pack({ - // todo rename "using" to "with" - using: { - body: (input) => { - const hasUploadScalarVariable = input.variables && isUsingUploadScalar(input.variables) - if (!hasUploadScalarVariable) return - - // TODO we can probably get file upload working for in-memory schemas too :) - if (pack.input.transport !== `http`) throw new Error(`Must be using http transport to use "Upload" scalar.`) - - return createBody({ - query: input.query, - variables: input.variables!, - }) + return createBody({ + query: input.query, + variables: input.variables!, + }) + }, }, - }, - input: { - ...pack.input, - headers: { - 'content-type': ``, + input: { + ...pack.input, + headers: { + 'content-type': ``, + }, }, - }, - }) - }, -}) + }) + }, + }) const isUsingUploadScalar = (_variables: StandardScalarVariables) => { return Object.values(_variables).some(_ => _ instanceof Blob) diff --git a/src/lib/anyware/__.test-d.ts b/src/lib/anyware/__.test-d.ts index f14cf952d..f5cf0cfb2 100644 --- a/src/lib/anyware/__.test-d.ts +++ b/src/lib/anyware/__.test-d.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { describe, expectTypeOf, test } from 'vitest' -import { Result } from '../../../tests/_/schema/generated/SchemaRuntime.js' +import { Result } from '../../../tests/_/schema/generated/modules/SchemaRuntime.js' import { ContextualError } from '../errors/ContextualError.js' import { type MaybePromise } from '../prelude.js' import { Anyware } from './__.js' diff --git a/src/lib/anyware/main.ts b/src/lib/anyware/main.ts index e5de1ef0d..45722718f 100644 --- a/src/lib/anyware/main.ts +++ b/src/lib/anyware/main.ts @@ -80,7 +80,7 @@ type Hook< & (<$$Input extends $HookMap[$Name]['input']>( input?: { input?: $$Input - } & (keyof $HookMap[$Name]['slots'] extends never ? {} : { using?: SlotInputify<$HookMap[$Name]['slots']> }), // eslint-disable-line + } & (keyof $HookMap[$Name]['slots'] extends never ? {} : { using?: SlotInputify<$HookMap[$Name]['slots']> }), ) => HookReturn<$HookSequence, $HookMap, $Result, $Name, $Options>) & { [hookSymbol]: HookSymbol @@ -224,7 +224,7 @@ const createPassthrough = (hookName: string) => async (hookEnvelope: SomeHookEnv if (!hook) { throw new Errors.ContextualError(`Hook not found in hook envelope`, { hookName }) } - return await hook({ input: hook.input }) // eslint-disable-line + return await hook({ input: hook.input }) } type Config = Required @@ -352,7 +352,7 @@ const toInternalExtension = (core: Core, config: Config, extension: ExtensionInp const passthroughs = hooksBeforeEntrypoint.map((hookName) => createPassthrough(hookName)) let currentChunkPromiseChain = currentChunk.promise for (const passthrough of passthroughs) { - currentChunkPromiseChain = currentChunkPromiseChain.then(passthrough) // eslint-disable-line + currentChunkPromiseChain = currentChunkPromiseChain.then(passthrough) } void currentChunkPromiseChain.then(applyBody) diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 16a37afc0..3f3e868ed 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -253,6 +253,8 @@ export type StandardScalarVariables = { [key: string]: string | boolean | null | number | StandardScalarVariables } +export type SomeData = Record + export type GraphQLExecutionResultError = Errors.ContextualAggregateError export const OperationTypes = { diff --git a/src/lib/graphqlHTTP.ts b/src/lib/graphqlHTTP.ts index e5cb21330..27403a4da 100644 --- a/src/lib/graphqlHTTP.ts +++ b/src/lib/graphqlHTTP.ts @@ -2,7 +2,7 @@ import type { GraphQLFormattedError } from 'graphql' import { type ExecutionResult, GraphQLError } from 'graphql' import type { GraphQLRequestEncoded, StandardScalarVariables } from './graphql.js' import { CONTENT_TYPE_GQL, CONTENT_TYPE_JSON } from './http.js' -import { isPlainObject } from './prelude.js' +import { isRecordLikeObject } from './prelude.js' export type ExecutionInput = { query: string @@ -23,7 +23,7 @@ export const parseExecutionResult = (result: unknown): ExecutionResult => { if ( !Array.isArray(result.errors) || result.errors.some( - error => !(isPlainObject(error) && `message` in error && typeof error[`message`] === `string`), + error => !(isRecordLikeObject(error) && `message` in error && typeof error[`message`] === `string`), ) ) { throw new Error(`Invalid execution result: errors is not array of formatted errors`) // prettier-ignore @@ -35,14 +35,16 @@ export const parseExecutionResult = (result: unknown): ExecutionResult => { // todo add test coverage for case of null. @see https://github.com/jasonkuhrt/graffle/issues/739 if (`data` in result) { - if (!isPlainObject(result.data) && result.data !== null) { + if (!isRecordLikeObject(result.data) && result.data !== null) { throw new Error(`Invalid execution result: data is not plain object`) // prettier-ignore } data = result.data } if (`extensions` in result) { - if (!isPlainObject(result.extensions)) throw new Error(`Invalid execution result: extensions is not plain object`) // prettier-ignore + if (!isRecordLikeObject(result.extensions)) { + throw new Error(`Invalid execution result: extensions is not plain object`) // prettier-ignore + } extensions = result.extensions } diff --git a/src/lib/hkt/__.ts b/src/lib/hkt/__.ts new file mode 100644 index 000000000..9a1691e28 --- /dev/null +++ b/src/lib/hkt/__.ts @@ -0,0 +1 @@ +export * as HKT from './hkt.js' diff --git a/src/lib/hkt/hkt.ts b/src/lib/hkt/hkt.ts new file mode 100644 index 000000000..377765430 --- /dev/null +++ b/src/lib/hkt/hkt.ts @@ -0,0 +1,37 @@ +/** + * A Higher Kinded Type (HKT). + */ +export interface Fn<$Params = unknown, $Return = unknown> { + params: $Params + return: $Return +} + +/** + * Apply a Higher Kinded Type (HKT). + */ +// dprint-ignore +export type Call<$Fn extends Fn, $Params> = + ($Fn & { params: $Params })['return'] + +// +// Utilities +// +// +// +export type Remove<$Fn extends Fn> = Omit<$Fn, keyof Fn> + +/** + * A function that outputs its input. + */ +export interface IdentityFn<$Params> extends Fn<$Params> { + return: this['params'] +} + +/** + * Work with optional HKTs. If the argument is a `Fn` then call it with teh given arguments. Otherwise, just return the type as was. + */ +// dprint-ignore +export type CallOrReturn<$MaybeFn, $Args> = + $MaybeFn extends Fn + ? Call<$MaybeFn, $Args> + : $MaybeFn diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index ca8e6757f..84ce5ea07 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -1,5 +1,6 @@ -import type { Simplify } from 'type-fest' +import type { IsUnknown, Simplify } from 'type-fest' import type { ConditionalSimplify, ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify.js' +import type { IsPlainObject } from 'type-fest/source/internal/object.js' /* eslint-disable */ export type RemoveIndex = { @@ -84,7 +85,7 @@ export const casesExhausted = (value: never): never => { throw new Error(`Unhandled case: ${String(value)}`) } -export const isPlainObject = (value: unknown): value is Record => { +export const isRecordLikeObject = (value: unknown): value is Record => { return typeof value === `object` && value !== null && !Array.isArray(value) } @@ -341,14 +342,16 @@ export namespace ConfigManager { export type ReadOrDefault<$Obj, $Path extends Path, $Default> = OrDefault, $Default> - export type OrDefault<$Value, $Default> = $Value extends undefined ? $Default : $Value + export type OrDefault<$Value, $Default> = IsUnknown<$Value> extends true ? $Default + : $Value extends undefined ? $Default + : $Value // dprint-ignore export type Read<$Value, $Path extends [...string[]]> = - $Value extends undefined ? undefined + $Value extends undefined ? undefined : $Path extends [infer P1 extends string, ...infer PN extends string[]] ? - $Value extends object ? P1 extends keyof $Value ? Read<$Value[P1], PN> : undefined - : undefined + $Value extends object ? P1 extends keyof $Value ? Read<$Value[P1], PN> : undefined + : undefined : $Value export type SetProperty<$Obj extends object, $Prop extends keyof $Obj, $Type extends $Obj[$Prop]> = @@ -410,3 +413,65 @@ export const throwNull = (value: V): Exclude => { if (value === null) throw new Error('Unexpected null value.') return value as Exclude } + +export const proxyGet = <$Target>( + target: $Target, + handler: (input: { property: string; path: string[] }) => unknown, + path: string[] = [], +): $Target => { + return new Proxy(target, { + get: (target: any, property: string, receiver: any) => { + const value = Reflect.get(target, property, receiver) + + if (isRecordLikeObject(value)) { + return proxyGet(value, handler, [...path, property]) + } + + return handler({ property, path }) ?? Reflect.get(target, property, receiver) + }, + }) +} + +type PathToValue = Path extends [infer First, ...infer Rest] + ? First extends keyof T ? Rest extends string[] ? PathToValue + : never + : never + : T + +export const getValueAtPath = ( + obj: T, + path: Path, +): PathToValue | undefined => { + return path.reduce((acc, key) => acc?.[key], obj) +} + +export type SuffixKeyNames<$Suffix extends string, $Object extends object> = { + [$Key in keyof $Object & string as `${$Key}${$Suffix}`]: $Object[$Key] +} + +// dprint-ignore +export type SuffixMethodsDeep<$Suffix extends string, $Object> = { + [ + $Key in keyof $Object & string + as $Object[$Key] extends AnyFunction + ? `${$Key}${$Suffix}` + : $Key + ]: + IsPlainObject<$Object[$Key]> extends true + ? SuffixMethodsDeep<$Suffix, $Object[$Key]> + : $Object[$Key] +} + +type AnyFunction = (...args: any[]) => any + +type _test = SimplifyDeep< + SuffixMethodsDeep<'Foo', { + a: () => void + b: { + c: () => void + d: { + e: () => void + } + } + }> +> diff --git a/tests/_/helpers.ts b/tests/_/helpers.ts index 84ef4d31d..46622fb87 100644 --- a/tests/_/helpers.ts +++ b/tests/_/helpers.ts @@ -9,7 +9,7 @@ export const createResponse = (body: object) => interface Fixtures { fetch: Mock<(request: Request) => Promise> - graffle: Client + graffle: Client } export const test = testBase.extend({ diff --git a/tests/_/schema/generated/Scalar.ts b/tests/_/schema/generated/Scalar.ts deleted file mode 100644 index 2e11dcd1c..000000000 --- a/tests/_/schema/generated/Scalar.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' -export * from '../../customScalarCodecs.js' diff --git a/tests/_/schema/generated/_.ts b/tests/_/schema/generated/_.ts index 5e2498a39..0d3b8d175 100644 --- a/tests/_/schema/generated/_.ts +++ b/tests/_/schema/generated/_.ts @@ -1,3 +1,9 @@ -export { create } from './Client.js' -export { isError } from './Error.js' -export { Select } from './Select.js' +// We import the global module for good measure although it is not clear it is always needed. +// It at least helps with Twoslash wherein without this import here Twoslash will not include the global module. +// In real TypeScript projects it seems the global module is included automatically. But there could be certain tsconfig +// setups where this still indeed does help. +import './modules/Global.js' + +export { create } from './modules/Client.js' +export { isError } from './modules/Error.js' +export { Select } from './modules/Select.js' diff --git a/tests/_/schema/generated/Client.ts b/tests/_/schema/generated/modules/Client.ts similarity index 65% rename from tests/_/schema/generated/Client.ts rename to tests/_/schema/generated/modules/Client.ts index 19ce78c53..652e047c1 100644 --- a/tests/_/schema/generated/Client.ts +++ b/tests/_/schema/generated/modules/Client.ts @@ -1,4 +1,4 @@ -import { createPrefilled } from '../../../../src/entrypoints/client.js' +import { createPrefilled } from '../../../../../src/entrypoints/client.js' import { $defaultSchemaUrl, $Index } from './SchemaRuntime.js' diff --git a/tests/_/schema/generated/Error.ts b/tests/_/schema/generated/modules/Error.ts similarity index 88% rename from tests/_/schema/generated/Error.ts rename to tests/_/schema/generated/modules/Error.ts index 116c46f0b..97bd3c91b 100644 --- a/tests/_/schema/generated/Error.ts +++ b/tests/_/schema/generated/modules/Error.ts @@ -12,6 +12,6 @@ const ErrorObjectsTypeNameSelected = Object.values(ErrorObjectsTypeNameSelectedE type ErrorObjectsTypeNameSelected = (typeof ErrorObjectsTypeNameSelected)[number] export const isError = <$Value>(value: $Value): value is Include<$Value, ErrorObjectsTypeNameSelected> => { - return typeof value === 'object' && value !== null && '__typename' in value + return typeof value === `object` && value !== null && `__typename` in value && ErrorObjectsTypeNameSelected.some(_ => _.__typename === value.__typename) } diff --git a/tests/_/schema/generated/Global.ts b/tests/_/schema/generated/modules/Global.ts similarity index 74% rename from tests/_/schema/generated/Global.ts rename to tests/_/schema/generated/modules/Global.ts index 70373731b..b040cc02a 100644 --- a/tests/_/schema/generated/Global.ts +++ b/tests/_/schema/generated/modules/Global.ts @@ -1,6 +1,6 @@ -import type { Index } from './Index.js' +import type { Index } from './SchemaIndex.js' -import type * as CustomScalar from '../../customScalarCodecs.js' +import type * as CustomScalar from '../../../customScalarCodecs.js' declare global { export namespace GraffleGlobalTypes { diff --git a/tests/_/schema/generated/modules/Scalar.ts b/tests/_/schema/generated/modules/Scalar.ts new file mode 100644 index 000000000..4b8d2d7c6 --- /dev/null +++ b/tests/_/schema/generated/modules/Scalar.ts @@ -0,0 +1,2 @@ +export * from '../../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' +export * from '../../../customScalarCodecs.js' diff --git a/tests/_/schema/generated/SchemaBuildtime.ts b/tests/_/schema/generated/modules/SchemaBuildtime.ts similarity index 99% rename from tests/_/schema/generated/SchemaBuildtime.ts rename to tests/_/schema/generated/modules/SchemaBuildtime.ts index b2311d27c..00ef8d110 100644 --- a/tests/_/schema/generated/SchemaBuildtime.ts +++ b/tests/_/schema/generated/modules/SchemaBuildtime.ts @@ -1,4 +1,4 @@ -import type * as $ from '../../../../src/entrypoints/schema.js' +import type * as $ from '../../../../../src/entrypoints/schema.js' import type * as $Scalar from './Scalar.ts' // ------------------------------------------------------------ // diff --git a/tests/_/schema/generated/Index.ts b/tests/_/schema/generated/modules/SchemaIndex.ts similarity index 97% rename from tests/_/schema/generated/Index.ts rename to tests/_/schema/generated/modules/SchemaIndex.ts index 62afc2c45..2688ccfca 100644 --- a/tests/_/schema/generated/Index.ts +++ b/tests/_/schema/generated/modules/SchemaIndex.ts @@ -4,6 +4,7 @@ import type * as Schema from './SchemaBuildtime.js' export interface Index { name: 'default' + RootTypesPresent: ['Query', 'Mutation'] Root: { Query: Schema.Root.Query Mutation: Schema.Root.Mutation diff --git a/tests/_/schema/generated/SchemaRuntime.ts b/tests/_/schema/generated/modules/SchemaRuntime.ts similarity index 99% rename from tests/_/schema/generated/SchemaRuntime.ts rename to tests/_/schema/generated/modules/SchemaRuntime.ts index c86e8de91..3b74a0477 100644 --- a/tests/_/schema/generated/SchemaRuntime.ts +++ b/tests/_/schema/generated/modules/SchemaRuntime.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -import * as $ from '../../../../src/entrypoints/schema.js' +import * as $ from '../../../../../src/entrypoints/schema.js' import * as $Scalar from './Scalar.js' export const $defaultSchemaUrl = undefined @@ -229,6 +229,7 @@ export const Query = $.Object$(`Query`, { export const $Index = { name: 'default' as const, + RootTypesPresent: ['Query', 'Mutation'] as const, Root: { Query, Mutation, diff --git a/tests/_/schema/generated/Select.ts b/tests/_/schema/generated/modules/Select.ts similarity index 94% rename from tests/_/schema/generated/Select.ts rename to tests/_/schema/generated/modules/Select.ts index cfe8f1c5e..8da158d3c 100644 --- a/tests/_/schema/generated/Select.ts +++ b/tests/_/schema/generated/modules/Select.ts @@ -1,11 +1,11 @@ -import type { ResultSet, SelectionSet } from '../../../../src/entrypoints/schema.js' -import type { Index } from './Index.js' +import type { ResultSet, SelectionSet } from '../../../../../src/entrypoints/schema.js' +import type { Index } from './SchemaIndex.js' // Runtime // ------- -import { createSelect } from '../../../../src/entrypoints/client.js' -export const Select = createSelect('default') +import { createSelect } from '../../../../../src/entrypoints/client.js' +export const Select = createSelect(`default`) // Buildtime // --------- diff --git a/tests/_/schema/schema.ts b/tests/_/schema/schema.ts index 2e1c5c48a..389225416 100644 --- a/tests/_/schema/schema.ts +++ b/tests/_/schema/schema.ts @@ -432,5 +432,5 @@ builder.queryType({ export const schema = builder.toSchema({ sortSchema: true }) -export type { Index } from './generated/Index.js' -export { $Index } from './generated/SchemaRuntime.js' +export type { Index } from './generated/modules/SchemaIndex.js' +export { $Index } from './generated/modules/SchemaRuntime.js' diff --git a/tests/_/schemaGenerate.ts b/tests/_/schemaGenerate.ts index 43220bb7d..a09cd01c5 100644 --- a/tests/_/schemaGenerate.ts +++ b/tests/_/schemaGenerate.ts @@ -23,14 +23,15 @@ const generate = async ( outputDirPath: join(sourceDirPath, `/generated`), code: { libraryPaths: { - client: `../../../../src/entrypoints/client.js`, - schema: `../../../../src/entrypoints/schema.js`, - scalars: `../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js`, + client: `../../../../../src/entrypoints/client.js`, + schema: `../../../../../src/entrypoints/schema.js`, + scalars: `../../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js`, }, }, name: input.name, ...input.options, }) + console.log(`generated at`, sourceDirPath) } await generate({ diff --git a/tests/_/schemaMutationOnly/generated/Scalar.ts b/tests/_/schemaMutationOnly/generated/Scalar.ts deleted file mode 100644 index 2e11dcd1c..000000000 --- a/tests/_/schemaMutationOnly/generated/Scalar.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' -export * from '../../customScalarCodecs.js' diff --git a/tests/_/schemaMutationOnly/generated/_.ts b/tests/_/schemaMutationOnly/generated/_.ts index 5e2498a39..0d3b8d175 100644 --- a/tests/_/schemaMutationOnly/generated/_.ts +++ b/tests/_/schemaMutationOnly/generated/_.ts @@ -1,3 +1,9 @@ -export { create } from './Client.js' -export { isError } from './Error.js' -export { Select } from './Select.js' +// We import the global module for good measure although it is not clear it is always needed. +// It at least helps with Twoslash wherein without this import here Twoslash will not include the global module. +// In real TypeScript projects it seems the global module is included automatically. But there could be certain tsconfig +// setups where this still indeed does help. +import './modules/Global.js' + +export { create } from './modules/Client.js' +export { isError } from './modules/Error.js' +export { Select } from './modules/Select.js' diff --git a/tests/_/schemaMutationOnly/generated/Client.ts b/tests/_/schemaMutationOnly/generated/modules/Client.ts similarity index 66% rename from tests/_/schemaMutationOnly/generated/Client.ts rename to tests/_/schemaMutationOnly/generated/modules/Client.ts index 6a139aa4d..58f95d837 100644 --- a/tests/_/schemaMutationOnly/generated/Client.ts +++ b/tests/_/schemaMutationOnly/generated/modules/Client.ts @@ -1,4 +1,4 @@ -import { createPrefilled } from '../../../../src/entrypoints/client.js' +import { createPrefilled } from '../../../../../src/entrypoints/client.js' import { $defaultSchemaUrl, $Index } from './SchemaRuntime.js' diff --git a/tests/_/schemaMutationOnly/generated/Error.ts b/tests/_/schemaMutationOnly/generated/modules/Error.ts similarity index 87% rename from tests/_/schemaMutationOnly/generated/Error.ts rename to tests/_/schemaMutationOnly/generated/modules/Error.ts index 6a480e85c..cd9eff6c7 100644 --- a/tests/_/schemaMutationOnly/generated/Error.ts +++ b/tests/_/schemaMutationOnly/generated/modules/Error.ts @@ -9,6 +9,6 @@ const ErrorObjectsTypeNameSelected = Object.values(ErrorObjectsTypeNameSelectedE type ErrorObjectsTypeNameSelected = (typeof ErrorObjectsTypeNameSelected)[number] export const isError = <$Value>(value: $Value): value is Include<$Value, ErrorObjectsTypeNameSelected> => { - return typeof value === 'object' && value !== null && '__typename' in value + return typeof value === `object` && value !== null && `__typename` in value && ErrorObjectsTypeNameSelected.some(_ => _.__typename === value.__typename) } diff --git a/tests/_/schemaMutationOnly/generated/Global.ts b/tests/_/schemaMutationOnly/generated/modules/Global.ts similarity index 86% rename from tests/_/schemaMutationOnly/generated/Global.ts rename to tests/_/schemaMutationOnly/generated/modules/Global.ts index 469c65cee..2faab311e 100644 --- a/tests/_/schemaMutationOnly/generated/Global.ts +++ b/tests/_/schemaMutationOnly/generated/modules/Global.ts @@ -1,4 +1,4 @@ -import type { Index } from './Index.js' +import type { Index } from './SchemaIndex.js' declare global { export namespace GraffleGlobalTypes { diff --git a/tests/_/schemaMutationOnly/generated/modules/Scalar.ts b/tests/_/schemaMutationOnly/generated/modules/Scalar.ts new file mode 100644 index 000000000..4b8d2d7c6 --- /dev/null +++ b/tests/_/schemaMutationOnly/generated/modules/Scalar.ts @@ -0,0 +1,2 @@ +export * from '../../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' +export * from '../../../customScalarCodecs.js' diff --git a/tests/_/schemaMutationOnly/generated/SchemaBuildtime.ts b/tests/_/schemaMutationOnly/generated/modules/SchemaBuildtime.ts similarity index 96% rename from tests/_/schemaMutationOnly/generated/SchemaBuildtime.ts rename to tests/_/schemaMutationOnly/generated/modules/SchemaBuildtime.ts index b7efd2be1..f7b44aa8d 100644 --- a/tests/_/schemaMutationOnly/generated/SchemaBuildtime.ts +++ b/tests/_/schemaMutationOnly/generated/modules/SchemaBuildtime.ts @@ -1,4 +1,4 @@ -import type * as $ from '../../../../src/entrypoints/schema.js' +import type * as $ from '../../../../../src/entrypoints/schema.js' import type * as $Scalar from './Scalar.ts' // ------------------------------------------------------------ // diff --git a/tests/_/schemaMutationOnly/generated/Index.ts b/tests/_/schemaMutationOnly/generated/modules/SchemaIndex.ts similarity index 92% rename from tests/_/schemaMutationOnly/generated/Index.ts rename to tests/_/schemaMutationOnly/generated/modules/SchemaIndex.ts index a3ca1101e..b2b9ec474 100644 --- a/tests/_/schemaMutationOnly/generated/Index.ts +++ b/tests/_/schemaMutationOnly/generated/modules/SchemaIndex.ts @@ -4,6 +4,7 @@ import type * as Schema from './SchemaBuildtime.js' export interface Index { name: 'MutationOnly' + RootTypesPresent: ['Mutation'] Root: { Query: null Mutation: Schema.Root.Mutation diff --git a/tests/_/schemaMutationOnly/generated/SchemaRuntime.ts b/tests/_/schemaMutationOnly/generated/modules/SchemaRuntime.ts similarity index 87% rename from tests/_/schemaMutationOnly/generated/SchemaRuntime.ts rename to tests/_/schemaMutationOnly/generated/modules/SchemaRuntime.ts index 8aa448315..ad90cffdb 100644 --- a/tests/_/schemaMutationOnly/generated/SchemaRuntime.ts +++ b/tests/_/schemaMutationOnly/generated/modules/SchemaRuntime.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -import * as $ from '../../../../src/entrypoints/schema.js' +import * as $ from '../../../../../src/entrypoints/schema.js' import * as $Scalar from './Scalar.js' export const $defaultSchemaUrl = undefined @@ -13,6 +13,7 @@ export const Mutation = $.Object$(`Mutation`, { export const $Index = { name: 'MutationOnly' as const, + RootTypesPresent: ['Mutation'] as const, Root: { Query: null, Mutation, diff --git a/tests/_/schemaMutationOnly/generated/Select.ts b/tests/_/schemaMutationOnly/generated/modules/Select.ts similarity index 62% rename from tests/_/schemaMutationOnly/generated/Select.ts rename to tests/_/schemaMutationOnly/generated/modules/Select.ts index 605584e09..79b20a5a8 100644 --- a/tests/_/schemaMutationOnly/generated/Select.ts +++ b/tests/_/schemaMutationOnly/generated/modules/Select.ts @@ -1,11 +1,11 @@ -import type { ResultSet, SelectionSet } from '../../../../src/entrypoints/schema.js' -import type { Index } from './Index.js' +import type { ResultSet, SelectionSet } from '../../../../../src/entrypoints/schema.js' +import type { Index } from './SchemaIndex.js' // Runtime // ------- -import { createSelect } from '../../../../src/entrypoints/client.js' -export const Select = createSelect('default') +import { createSelect } from '../../../../../src/entrypoints/client.js' +export const Select = createSelect(`default`) // Buildtime // --------- diff --git a/tests/_/schemaMutationOnly/schema.ts b/tests/_/schemaMutationOnly/schema.ts index e8d005eb4..a7e3d0b49 100644 --- a/tests/_/schemaMutationOnly/schema.ts +++ b/tests/_/schemaMutationOnly/schema.ts @@ -14,5 +14,5 @@ export const schema = builder.toSchema({ sortSchema: true, }) -export type { Index } from './generated/Index.js' -export { $Index } from './generated/SchemaRuntime.js' +export type { Index } from './generated/modules/SchemaIndex.js' +export { $Index } from './generated/modules/SchemaRuntime.js' diff --git a/tests/_/schemaQueryOnly/generated/Scalar.ts b/tests/_/schemaQueryOnly/generated/Scalar.ts deleted file mode 100644 index 2e11dcd1c..000000000 --- a/tests/_/schemaQueryOnly/generated/Scalar.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' -export * from '../../customScalarCodecs.js' diff --git a/tests/_/schemaQueryOnly/generated/_.ts b/tests/_/schemaQueryOnly/generated/_.ts index 5e2498a39..0d3b8d175 100644 --- a/tests/_/schemaQueryOnly/generated/_.ts +++ b/tests/_/schemaQueryOnly/generated/_.ts @@ -1,3 +1,9 @@ -export { create } from './Client.js' -export { isError } from './Error.js' -export { Select } from './Select.js' +// We import the global module for good measure although it is not clear it is always needed. +// It at least helps with Twoslash wherein without this import here Twoslash will not include the global module. +// In real TypeScript projects it seems the global module is included automatically. But there could be certain tsconfig +// setups where this still indeed does help. +import './modules/Global.js' + +export { create } from './modules/Client.js' +export { isError } from './modules/Error.js' +export { Select } from './modules/Select.js' diff --git a/tests/_/schemaQueryOnly/generated/Client.ts b/tests/_/schemaQueryOnly/generated/modules/Client.ts similarity index 65% rename from tests/_/schemaQueryOnly/generated/Client.ts rename to tests/_/schemaQueryOnly/generated/modules/Client.ts index 8726608ee..6ce5361e2 100644 --- a/tests/_/schemaQueryOnly/generated/Client.ts +++ b/tests/_/schemaQueryOnly/generated/modules/Client.ts @@ -1,4 +1,4 @@ -import { createPrefilled } from '../../../../src/entrypoints/client.js' +import { createPrefilled } from '../../../../../src/entrypoints/client.js' import { $defaultSchemaUrl, $Index } from './SchemaRuntime.js' diff --git a/tests/_/schemaQueryOnly/generated/Error.ts b/tests/_/schemaQueryOnly/generated/modules/Error.ts similarity index 87% rename from tests/_/schemaQueryOnly/generated/Error.ts rename to tests/_/schemaQueryOnly/generated/modules/Error.ts index 6a480e85c..cd9eff6c7 100644 --- a/tests/_/schemaQueryOnly/generated/Error.ts +++ b/tests/_/schemaQueryOnly/generated/modules/Error.ts @@ -9,6 +9,6 @@ const ErrorObjectsTypeNameSelected = Object.values(ErrorObjectsTypeNameSelectedE type ErrorObjectsTypeNameSelected = (typeof ErrorObjectsTypeNameSelected)[number] export const isError = <$Value>(value: $Value): value is Include<$Value, ErrorObjectsTypeNameSelected> => { - return typeof value === 'object' && value !== null && '__typename' in value + return typeof value === `object` && value !== null && `__typename` in value && ErrorObjectsTypeNameSelected.some(_ => _.__typename === value.__typename) } diff --git a/tests/_/schemaQueryOnly/generated/Global.ts b/tests/_/schemaQueryOnly/generated/modules/Global.ts similarity index 86% rename from tests/_/schemaQueryOnly/generated/Global.ts rename to tests/_/schemaQueryOnly/generated/modules/Global.ts index f3a58e79f..37729ae59 100644 --- a/tests/_/schemaQueryOnly/generated/Global.ts +++ b/tests/_/schemaQueryOnly/generated/modules/Global.ts @@ -1,4 +1,4 @@ -import type { Index } from './Index.js' +import type { Index } from './SchemaIndex.js' declare global { export namespace GraffleGlobalTypes { diff --git a/tests/_/schemaQueryOnly/generated/modules/Scalar.ts b/tests/_/schemaQueryOnly/generated/modules/Scalar.ts new file mode 100644 index 000000000..4b8d2d7c6 --- /dev/null +++ b/tests/_/schemaQueryOnly/generated/modules/Scalar.ts @@ -0,0 +1,2 @@ +export * from '../../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js' +export * from '../../../customScalarCodecs.js' diff --git a/tests/_/schemaQueryOnly/generated/SchemaBuildtime.ts b/tests/_/schemaQueryOnly/generated/modules/SchemaBuildtime.ts similarity index 96% rename from tests/_/schemaQueryOnly/generated/SchemaBuildtime.ts rename to tests/_/schemaQueryOnly/generated/modules/SchemaBuildtime.ts index ffa7b189a..1c593bf2a 100644 --- a/tests/_/schemaQueryOnly/generated/SchemaBuildtime.ts +++ b/tests/_/schemaQueryOnly/generated/modules/SchemaBuildtime.ts @@ -1,4 +1,4 @@ -import type * as $ from '../../../../src/entrypoints/schema.js' +import type * as $ from '../../../../../src/entrypoints/schema.js' import type * as $Scalar from './Scalar.ts' // ------------------------------------------------------------ // diff --git a/tests/_/schemaQueryOnly/generated/Index.ts b/tests/_/schemaQueryOnly/generated/modules/SchemaIndex.ts similarity index 92% rename from tests/_/schemaQueryOnly/generated/Index.ts rename to tests/_/schemaQueryOnly/generated/modules/SchemaIndex.ts index 10019219e..aafcffa23 100644 --- a/tests/_/schemaQueryOnly/generated/Index.ts +++ b/tests/_/schemaQueryOnly/generated/modules/SchemaIndex.ts @@ -4,6 +4,7 @@ import type * as Schema from './SchemaBuildtime.js' export interface Index { name: 'QueryOnly' + RootTypesPresent: ['Query'] Root: { Query: Schema.Root.Query Mutation: null diff --git a/tests/_/schemaQueryOnly/generated/SchemaRuntime.ts b/tests/_/schemaQueryOnly/generated/modules/SchemaRuntime.ts similarity index 87% rename from tests/_/schemaQueryOnly/generated/SchemaRuntime.ts rename to tests/_/schemaQueryOnly/generated/modules/SchemaRuntime.ts index 50f573e0b..876733676 100644 --- a/tests/_/schemaQueryOnly/generated/SchemaRuntime.ts +++ b/tests/_/schemaQueryOnly/generated/modules/SchemaRuntime.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -import * as $ from '../../../../src/entrypoints/schema.js' +import * as $ from '../../../../../src/entrypoints/schema.js' import * as $Scalar from './Scalar.js' export const $defaultSchemaUrl = undefined @@ -13,6 +13,7 @@ export const Query = $.Object$(`Query`, { export const $Index = { name: 'QueryOnly' as const, + RootTypesPresent: ['Query'] as const, Root: { Query, Mutation: null, diff --git a/tests/_/schemaQueryOnly/generated/Select.ts b/tests/_/schemaQueryOnly/generated/modules/Select.ts similarity index 62% rename from tests/_/schemaQueryOnly/generated/Select.ts rename to tests/_/schemaQueryOnly/generated/modules/Select.ts index b08828887..edc188220 100644 --- a/tests/_/schemaQueryOnly/generated/Select.ts +++ b/tests/_/schemaQueryOnly/generated/modules/Select.ts @@ -1,11 +1,11 @@ -import type { ResultSet, SelectionSet } from '../../../../src/entrypoints/schema.js' -import type { Index } from './Index.js' +import type { ResultSet, SelectionSet } from '../../../../../src/entrypoints/schema.js' +import type { Index } from './SchemaIndex.js' // Runtime // ------- -import { createSelect } from '../../../../src/entrypoints/client.js' -export const Select = createSelect('default') +import { createSelect } from '../../../../../src/entrypoints/client.js' +export const Select = createSelect(`default`) // Buildtime // --------- diff --git a/tests/_/schemaQueryOnly/schema.ts b/tests/_/schemaQueryOnly/schema.ts index 5696cd3d5..039d6c75c 100644 --- a/tests/_/schemaQueryOnly/schema.ts +++ b/tests/_/schemaQueryOnly/schema.ts @@ -14,5 +14,5 @@ export const schema = builder.toSchema({ sortSchema: true, }) -export type { Index } from './generated/Index.js' -export { $Index } from './generated/SchemaRuntime.js' +export type { Index } from './generated/modules/SchemaIndex.js' +export { $Index } from './generated/modules/SchemaRuntime.js' diff --git a/tests/examples/extension|extension_or-throw.test.ts b/tests/examples/extension|extension_or-throw.test.ts new file mode 100644 index 000000000..af966b811 --- /dev/null +++ b/tests/examples/extension|extension_or-throw.test.ts @@ -0,0 +1,19 @@ +// @vitest-environment node + +// WARNING: +// This test is generated by scripts/generate-example-derivatives/generate.ts +// Do not modify this file directly. + +import { expect, test } from 'vitest' +import { runExample } from '../../scripts/generate-examples-derivatives/helpers.js' + +test(`extension|extension_or-throw`, async () => { + const exampleResult = await runExample(`./examples/extension|extension_or-throw.ts`) + // Examples should output their data results. + const exampleResultMaybeEncoded = exampleResult + // If ever outputs vary by Node version, you can use this to snapshot by Node version. + // const nodeMajor = process.version.match(/v(\d+)/)?.[1] ?? `unknown` + await expect(exampleResultMaybeEncoded).toMatchFileSnapshot( + `../.././examples/__outputs__/extension|extension_or-throw.output.txt`, + ) +}) diff --git a/website/.vitepress/config.ts b/website/.vitepress/config.ts index 6c1b3d773..103ee2e42 100644 --- a/website/.vitepress/config.ts +++ b/website/.vitepress/config.ts @@ -1,10 +1,69 @@ import { transformerTwoslash } from '@shikijs/vitepress-twoslash' +import { capitalize } from 'es-toolkit' import { ModuleKind, ModuleResolutionKind } from 'typescript' import { defineConfig } from 'vitepress' +import { generateSidebar, Sidebar, SidebarItem, SidebarMulti, SidebarMultiItem } from 'vitepress-sidebar' import { sidebarExamples } from './configExamples' +const prefixPattern = /\d+_/g + +const sidebarMultiVisitItems = (sidebarMulti: SidebarMulti, visitor: (sidebarItem: SidebarItem) => void) => { + Object.values(sidebarMulti).forEach(sidebar => sidebar.items.forEach(_ => sidebarItemVisitItems(_, visitor))) + return sidebarMulti +} + +const sidebarItemVisitItems = (sidebarItem: SidebarItem, visitor: (sidebarItem: SidebarItem) => void) => { + visitor(sidebarItem) + sidebarItem.items?.forEach(_ => sidebarItemVisitItems(_, visitor)) +} + +const fixLinks = (sidebarMulti: SidebarMulti) => { + return sidebarMultiVisitItems(sidebarMulti, (sidebarItem) => { + sidebarItem.link = sidebarItem.link?.replaceAll(prefixPattern, '') + }) +} + +const fixTitles = (sidebarMulti: SidebarMulti) => { + return sidebarMultiVisitItems(sidebarMulti, (sidebarItem) => { + const [title, maybeHtml] = sidebarItem.text?.split('<') as [string, string | undefined] + if (sidebarItem.text) { + sidebarItem.text = capitalize(title.replaceAll(/-/g, ' ')) + (maybeHtml ? `<${maybeHtml}` : '') + } + }) +} + +/** + * @see https://vitepress-sidebar.cdget.com/guide/api + */ +const sidebars = fixTitles(fixLinks(generateSidebar([ + { + scanStartPath: 'content/guides', + resolvePath: '/guides/', + excludeFolders: ['_example_links'], + // collapsed: false, + // capitalizeEachWords: true, + // hyphenToSpace: true, + prefixSeparator: '_', + removePrefixAfterOrdering: true, + useTitleFromFrontmatter: true, + useTitleFromFileHeading: true, + keepMarkdownSyntaxFromTitle: true, + }, +]) as SidebarMulti)) + +// console.log(sidebars['/guides/'].items[0]) + // https://vitepress.dev/reference/site-config export default defineConfig({ + /** + * @see https://github.com/pillarjs/path-to-regexp/blob/8b7440438f726cce7a891f9325dd79a65978347f/Readme.md + */ + // dprint-ignore + rewrites: { + 'guides/{:_(\\d+_)}?:one/{:_(\\d+_)}?:two/{:_(\\d+_)}?:three' : 'guides/:one/:two/:three', + 'guides/{:_(\\d+_)}?:one/{:_(\\d+_)}?:two' : 'guides/:one/:two', + 'guides/{:prefixOne(\\d+_)}?:one' : 'guides/:one' + }, title: 'Graffle', description: 'Minimalist Progressively Type Safe GraphQL Client For JavaScript.', cleanUrls: true, @@ -30,11 +89,6 @@ export default defineConfig({ module: ModuleKind.ESNext, noErrorTruncation: true, }, - - extraFiles: { - // 'foo.ts': - // 'export function ref(value: T): Ref { return { value } }\ninterface Ref { value: string }', - }, // Instead of automatically putting underlines over every property and variable, // only do so for the ones we explicitly ask for in our markdown. // shouldGetHoverInfo: (x) => { @@ -68,92 +122,7 @@ export default defineConfig({ { text: 'Introduction', link: 'examples/index' }, ...sidebarExamples, ], - '/guides/': [ - { - text: 'Overview', - collapsed: false, - items: [ - { text: 'Introduction', link: '/guides/overview/introduction' }, - { - text: 'Getting Started', - // link: '/overview/getting-started', - items: [{ - text: 'Static Client', - link: '/guides/overview/getting-started-static', - }, { - text: - 'Generated Client ⩕', - link: '/guides/overview/getting-started-generated', - }], - }, - - { text: 'Output', link: '/guides/overview/output' }, - { text: 'Anyware', link: '/guides/overview/anyware' }, - ], - }, - { - text: 'Transports', - collapsed: false, - items: [ - { text: 'HTTP', link: '/guides/transports/http' }, - { text: 'Memory', link: '/guides/transports/memory' }, - ], - }, - { - text: 'Methods', - collapsed: false, - items: [ - { text: 'Raw', link: '/methods/raw' }, - { text: 'Or Throw', link: '/guides/methods/or-throw' }, - { - text: 'Document ⩕', - link: '/guides/methods/document', - }, - { - text: 'Batch ⩕', - link: '/guides/methods/batch', - }, - { - text: 'Root Fields ⩕', - link: '/guides/methods/root-fields', - }, - ], - }, - { - text: - 'GQL Feature Mapping ⩕', - collapsed: false, - items: [ - { text: 'Arguments', link: '/guides/graphql-feature-mapping/arguments' }, - { text: 'Aliases', link: '/guides/graphql-feature-mapping/aliases' }, - { text: 'Enums', link: '/guides/graphql-feature-mapping/enums' }, - { text: 'Interfaces', link: '/guides/graphql-feature-mapping/interfaces' }, - { text: 'Unions', link: '/guides/graphql-feature-mapping/unions' }, - { text: 'Directives', link: '/guides/graphql-feature-mapping/directives' }, - { text: 'Custom Scalars', link: '/guides/graphql-feature-mapping/custom-scalars' }, - { text: 'Selection Groups', link: '/guides/graphql-feature-mapping/selection-groups' }, - ], - }, - { - text: 'Misc ⩕', - collapsed: false, - items: [ - { text: 'Schema Errors', link: '/guides/misc/schema-errors' }, - { text: 'Select', link: '/guides/misc/select' }, - { text: 'Extension Authoring', link: '/guides/misc/extension-authoring' }, - { text: 'About Generation', link: '/guides/misc/about-generation' }, - ], - }, - { - text: 'Extensions', - collapsed: false, - items: [ - { text: 'Opentelemetry', link: '/guides/extensions/opentelemetry' }, - { text: 'File Upload', link: '/guides/extensions/file-upload' }, - { text: 'Or Throw', link: '/guides/extensions/or-throw' }, - ], - }, - ], + ...sidebars, }, socialLinks: [ { icon: 'github', link: 'https://github.com/jasonkuhrt/graffle' }, diff --git a/website/.vitepress/configExamples.ts b/website/.vitepress/configExamples.ts index 780fe6cc0..74c1694c6 100644 --- a/website/.vitepress/configExamples.ts +++ b/website/.vitepress/configExamples.ts @@ -8,6 +8,10 @@ { "text": "Opentelemetry", "link": "/examples/extension-opentelemetry" + }, + { + "text": "Or Throw", + "link": "/examples/extension-or-throw" } ] }, diff --git a/website/.vitepress/theme/custom.css b/website/.vitepress/theme/custom.css index 9bfcb0c85..860ab16e3 100644 --- a/website/.vitepress/theme/custom.css +++ b/website/.vitepress/theme/custom.css @@ -45,15 +45,15 @@ .VPContent h6 { opacity: 0.6; - font-size: 0.6rem; - text-transform: uppercase; - font-weight: 900; + font-size: 0.7rem; + /* text-transform: uppercase; */ + /* font-weight: 900; */ } .VPContent h6 a { opacity: 0.6; - font-size: 0.6rem; - text-transform: uppercase; + /* font-size: 0.6rem; */ + /* text-transform: uppercase; */ font-weight: 900; } @@ -119,3 +119,24 @@ #graffle-warning-message a { text-decoration: underline; } + +/* Example Links */ +.ExampleLinks { + opacity: 0.6; + font-size: 0.7rem; +} + +.ExampleLinksSeparator { + + display: inline-block; + margin: auto 0.25rem; +} + +.ExampleLinksSeparator:before { + content: '•' +} + +.ExampleLinksTitleSeparator { + display: inline-block; + margin: auto 0.25rem; +} diff --git a/website/content/examples/extension-opentelemetry.md b/website/content/examples/extension-opentelemetry.md index 918d80c73..525b3c7ab 100644 --- a/website/content/examples/extension-opentelemetry.md +++ b/website/content/examples/extension-opentelemetry.md @@ -11,7 +11,6 @@ import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node' import { Opentelemetry } from 'graffle/extensions' import { Graffle as Atlas } from './graffle/__.js' - // Setup Opentelemetry // 1. Initialize the OpenTelemetry provider // 2. Register the provider to make the OpenTelemetry API use it @@ -22,8 +21,8 @@ provider.addSpanProcessor(processor) provider.register() const graffle = Atlas.create().use(Opentelemetry()) -const result = await graffle.rawString({ document: `query { continents { name } }` }) -console.log(result.data) +const data = await graffle.rawString({ document: `query { continents { name } }` }) +console.log(data) ``` @@ -41,14 +40,14 @@ console.log(result.data) } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'encode', - id: '0f8e4e2d2fa7a1d1', + id: 'bcca0b855b22b8af', kind: 0, - timestamp: 1726068743834000, - duration: 442.792, + timestamp: 1726346409548000, + duration: 448.042, attributes: {}, status: { code: 0 }, events: [], @@ -68,14 +67,14 @@ console.log(result.data) } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'pack', - id: '37766e2e0fa6ea2e', + id: '884350df7488294f', kind: 0, - timestamp: 1726068743836000, - duration: 808.5, + timestamp: 1726346409551000, + duration: 1024.667, attributes: {}, status: { code: 0 }, events: [], @@ -95,14 +94,14 @@ console.log(result.data) } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'exchange', - id: 'ed9ae7aad6fd1e69', + id: 'c5b5c072b7c8c20d', kind: 0, - timestamp: 1726068743837000, - duration: 329989.458, + timestamp: 1726346409553000, + duration: 194155.958, attributes: {}, status: { code: 0 }, events: [], @@ -122,14 +121,14 @@ console.log(result.data) } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'unpack', - id: 'd0d9cbd74e358490', + id: '6956cc361339e79b', kind: 0, - timestamp: 1726068744168000, - duration: 1907.291, + timestamp: 1726346409747000, + duration: 4696.875, attributes: {}, status: { code: 0 }, events: [], @@ -149,14 +148,14 @@ console.log(result.data) } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', - parentId: 'e3b3fd57f531674b', + traceId: '62449852d475be9dce6a74f0b0555f54', + parentId: 'b3ba100af8bce057', traceState: undefined, name: 'decode', - id: '51a5859eae82dd62', + id: '5093bc556bcde250', kind: 0, - timestamp: 1726068744170000, - duration: 97.958, + timestamp: 1726346409753000, + duration: 286.459, attributes: {}, status: { code: 0 }, events: [], @@ -176,14 +175,14 @@ console.log(result.data) } }, instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined }, - traceId: '93ece70aa164958fd1b3d3c6c89e9f70', + traceId: '62449852d475be9dce6a74f0b0555f54', parentId: undefined, traceState: undefined, name: 'request', - id: 'e3b3fd57f531674b', + id: 'b3ba100af8bce057', kind: 0, - timestamp: 1726068743834000, - duration: 336135.125, + timestamp: 1726346409547000, + duration: 205724.167, attributes: {}, status: { code: 0 }, events: [], diff --git a/website/content/examples/extension-or-throw.md b/website/content/examples/extension-or-throw.md new file mode 100644 index 000000000..61e3b04c5 --- /dev/null +++ b/website/content/examples/extension-or-throw.md @@ -0,0 +1,72 @@ +--- +aside: false +--- + +# Or Throw + +This example shows how to use the Or Throw extension to throw errors for one-off cases. + + +```ts twoslash +import { OrThrow } from 'graffle/extensions' +import { Graffle as Atlas } from './graffle/__.js' + +const atlas = Atlas + .create({ output: { defaults: { errorChannel: `return` } } }) + .use(OrThrow()) + .anyware(({ encode: _ }) => { + throw new Error(`Something went wrong.`) + }) + +const result1 = await atlas.query.continents({ name: true }) +console.log(result1) + +const result2 = await atlas.query.continentsOrThrow({ name: true }) +result2 // This line will never be reached because of thrown error. +``` + + +#### Outputs + + +```txt +ContextualError: There was an error in the extension "anonymous" (use named functions to improve this error message) while running hook "encode". + at runPipeline (/some/path/to/runPipeline.ts:XX:XX) + at async Object.run (/some/path/to/main.ts:XX:XX) + at async run (/some/path/to/client.ts:XX:XX) + at async executeRootType (/some/path/to/client.ts:XX:XX) + at async executeRootTypeField (/some/path/to/client.ts:XX:XX) + at async (/some/path/to/extension|extension_or-throw.ts:XX:XX) { + context: { + hookName: 'encode', + source: 'extension', + extensionName: 'anonymous' + }, + cause: Error: Something went wrong. + at (/some/path/to/extension|extension_or-throw.ts:XX:XX) + at applyBody (/some/path/to/main.ts:XX:XX) +} +``` + + +```txt +UNCAUGHT EXCEPTION: + +ContextualError: There was an error in the extension "anonymous" (use named functions to improve this error message) while running hook "encode". + at runPipeline (/some/path/to/runPipeline.ts:XX:XX) + at async Object.run (/some/path/to/main.ts:XX:XX) + at async run (/some/path/to/client.ts:XX:XX) + at async executeRootType (/some/path/to/client.ts:XX:XX) + at async executeRootTypeField (/some/path/to/client.ts:XX:XX) + at async (/some/path/to/extension|extension_or-throw.ts:XX:XX) { + context: { + hookName: 'encode', + source: 'extension', + extensionName: 'anonymous' + }, + cause: Error: Something went wrong. + at (/some/path/to/extension|extension_or-throw.ts:XX:XX) + at applyBody (/some/path/to/main.ts:XX:XX) +} +``` + diff --git a/website/content/examples/other-transport-memory.md b/website/content/examples/other-transport-memory.md index 45d408b39..0e3310921 100644 --- a/website/content/examples/other-transport-memory.md +++ b/website/content/examples/other-transport-memory.md @@ -25,9 +25,9 @@ const schema = new GraphQLSchema({ const graffle = Graffle.create({ schema }) -const result = await graffle.rawString({ document: `{ foo }` }) +const data = await graffle.rawString({ document: `{ foo }` }) -console.log(result) +console.log(data) ``` @@ -36,9 +36,7 @@ console.log(result) ```json { - "data": { - "foo": "bar" - } + "foo": "bar" } ``` diff --git a/website/content/examples/output-default.md b/website/content/examples/output-default.md index da0c8ec2b..e4a942f69 100644 --- a/website/content/examples/output-default.md +++ b/website/content/examples/output-default.md @@ -12,9 +12,9 @@ import { Graffle as Atlas } from './graffle/__.js' const atlas = Atlas.create() -const result = await atlas.query.continents({ name: true }) +const continents = await atlas.query.continents({ name: true }) -console.log(result) +console.log(continents) ``` diff --git a/website/content/examples/output-envelope-error-throw.md b/website/content/examples/output-envelope-error-throw.md index cb8235993..d2c313f47 100644 --- a/website/content/examples/output-envelope-error-throw.md +++ b/website/content/examples/output-envelope-error-throw.md @@ -10,19 +10,21 @@ This example shows how to configure output to throw errors even when using the e ```ts twoslash import { Graffle as Atlas } from './graffle/__.js' -const atlas = Atlas.create({ - output: { - envelope: { - errors: { - execution: false, - other: false, // default - } +const atlas = Atlas + .create({ + output: { + envelope: { + errors: { + execution: false, + other: false, // default + } + }, }, - }, -}).use(({ encode: _ }) => { - throw new Error(`Something went wrong.`) -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -}) + }) + .anyware(({ encode: _ }) => { + throw new Error(`Something went wrong.`) + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + }) await atlas.query.continents({ name: true }) ``` diff --git a/website/content/examples/output-envelope-error.md b/website/content/examples/output-envelope-error.md index c7dbc7949..ee495de0a 100644 --- a/website/content/examples/output-envelope-error.md +++ b/website/content/examples/output-envelope-error.md @@ -22,7 +22,7 @@ const atlas = Atlas }, }, }) - .use(({ encode: _ }) => { + .anyware(({ encode: _ }) => { throw new Error(`Something went wrong.`) //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }) diff --git a/website/content/examples/output-envelope.md b/website/content/examples/output-envelope.md index af7151898..edfad92d8 100644 --- a/website/content/examples/output-envelope.md +++ b/website/content/examples/output-envelope.md @@ -46,7 +46,7 @@ console.log(result) headers: Headers { connection: 'keep-alive', 'content-length': '119', - 'x-served-by': 'cache-yul1970045-YUL', + 'x-served-by': 'cache-yul1970038-YUL', 'accept-ranges': 'bytes', date: 'Sun, 08 Sep 2024 18:13:26 GMT', 'content-type': 'application/graphql-response+json; charset=utf-8', @@ -59,13 +59,13 @@ console.log(result) 'alt-svc': 'h3=":443"; ma=86400', 'access-control-allow-origin': '*', 'x-powered-by': 'Stellate', - age: '249539', + age: '527204', 'cache-control': 'public, s-maxage=2628000, stale-while-revalidate=2628000', 'x-cache': 'HIT', - 'x-cache-hits': '5', + 'x-cache-hits': '33', 'gcdn-cache': 'HIT', - 'stellate-rate-limit-budget-remaining': '41', - 'stellate-rate-limit-rules': '"IP limit";type="RequestCount";budget=50;limited=?0;remaining=41;refill=60', + 'stellate-rate-limit-budget-remaining': '46', + 'stellate-rate-limit-rules': '"IP limit";type="RequestCount";budget=50;limited=?0;remaining=46;refill=60', 'stellate-rate-limit-decision': 'pass', 'stellate-rate-limit-budget-required': '5', 'content-encoding': 'br' diff --git a/website/content/examples/output-return-error-execution.md b/website/content/examples/output-return-error-execution.md index ae2ccd582..ee7911b2b 100644 --- a/website/content/examples/output-return-error-execution.md +++ b/website/content/examples/output-return-error-execution.md @@ -36,7 +36,7 @@ console.log(result) try { await pokemon - .use(({ encode: _ }) => { + .anyware(({ encode: _ }) => { throw new Error(`Something went wrong.`) }) .query diff --git a/website/content/examples/output-return-error.md b/website/content/examples/output-return-error.md index b691aa6ff..1a8ceef95 100644 --- a/website/content/examples/output-return-error.md +++ b/website/content/examples/output-return-error.md @@ -19,16 +19,16 @@ const atlas = Atlas }, }, }) - .use(({ encode: _ }) => { + .anyware(({ encode: _ }) => { throw new Error(`Something went wrong.`) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }) -const result = await atlas.query.continents({ name: true }) -type _result = typeof result +const continents = await atlas.query.continents({ name: true }) +type _continents = typeof continents // ^? -console.log(result) +console.log(continents) ``` diff --git a/website/content/examples/raw-raw-string-typed.md b/website/content/examples/raw-raw-string-typed.md index 6c4cf9cc6..af5a623a3 100644 --- a/website/content/examples/raw-raw-string-typed.md +++ b/website/content/examples/raw-raw-string-typed.md @@ -38,12 +38,12 @@ const document: Document = /* gql */ ` } ` -const result = await graffle.rawString({ +const data = await graffle.rawString({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] }, }) -console.log(result.data?.countries) +console.log(data?.countries) ``` diff --git a/website/content/examples/raw-raw-string.md b/website/content/examples/raw-raw-string.md index b5bc1cd2d..b77502644 100644 --- a/website/content/examples/raw-raw-string.md +++ b/website/content/examples/raw-raw-string.md @@ -22,11 +22,9 @@ const document = /* gql */ ` } ` -const result = await graffle.rawString({ - document, -}) +const data = await graffle.rawString({ document }) -console.log(result.data) +console.log(data) ``` diff --git a/website/content/examples/raw-raw-typed.md b/website/content/examples/raw-raw-typed.md index 3d7f1c789..c1570be7e 100644 --- a/website/content/examples/raw-raw-typed.md +++ b/website/content/examples/raw-raw-typed.md @@ -35,9 +35,9 @@ const graffle = Graffle.create({ } ` - const result = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) + const data = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) - console.log(result.data?.countries) + console.log(data?.countries) } /*************************************** Variation 2 *************************************** @@ -65,9 +65,9 @@ const graffle = Graffle.create({ } ` - const result = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) + const data = await graffle.raw({ document, variables: { filter: [`Canada`, `Germany`, `Japan`] } }) - console.log(result.data?.countries) + console.log(data?.countries) } ``` diff --git a/website/content/examples/raw-raw.md b/website/content/examples/raw-raw.md index 51a5d1cc2..478d2ff3a 100644 --- a/website/content/examples/raw-raw.md +++ b/website/content/examples/raw-raw.md @@ -8,13 +8,16 @@ This example shows how to send a request using a Document instance for the Graph ```ts twoslash +import { Opentelemetry, OrThrow } from 'graffle/extensions' import { gql, Graffle } from 'graffle' const graffle = Graffle.create({ schema: `https://countries.trevorblades.com/graphql`, }) + .use(OrThrow()) + .use(Opentelemetry()) -const result = await graffle.raw({ +const data = await graffle.raw({ document: gql` query countries ($filter: [String!]) { countries (filter: { name: { in: $filter } }) { @@ -28,7 +31,7 @@ const result = await graffle.raw({ variables: { filter: [`Canada`, `Germany`, `Japan`] }, }) -console.log(result.data) +console.log(data) ``` diff --git a/website/content/examples/transport-http-custom-fetch.md b/website/content/examples/transport-http-custom-fetch.md index 8301c86f7..098301966 100644 --- a/website/content/examples/transport-http-custom-fetch.md +++ b/website/content/examples/transport-http-custom-fetch.md @@ -12,22 +12,19 @@ import { Graffle } from 'graffle' const graffle = Graffle .create({ schema: `https://countries.trevorblades.com/graphql` }) - .use({ - name: `CustomFetch`, - anyware: async ({ exchange }) => { - return await exchange({ - using: { - fetch: async () => { - return new Response(JSON.stringify({ data: { countries: [{ name: `Canada Mocked!` }] } })) - }, + .anyware(({ exchange }) => + exchange({ + using: { + fetch: async () => { + return new Response(JSON.stringify({ data: { countries: [{ name: `Canada Mocked!` }] } })) }, - }) - }, - }) + }, + }) + ) -const countries = await graffle.rawString({ document: `{ countries { name } }` }) +const data = await graffle.rawString({ document: `{ countries { name } }` }) -console.log(countries.data) +console.log(data) ``` diff --git a/website/content/examples/transport-http-dynamic-headers.md b/website/content/examples/transport-http-dynamic-headers.md index 4d369c058..5d90484f5 100644 --- a/website/content/examples/transport-http-dynamic-headers.md +++ b/website/content/examples/transport-http-dynamic-headers.md @@ -14,7 +14,7 @@ const graffle = Graffle .create({ schema: `https://countries.trevorblades.com/graphql`, }) - .use(async ({ pack }) => { + .anyware(async ({ pack }) => { return await pack({ input: { ...pack.input, @@ -24,7 +24,7 @@ const graffle = Graffle }, }) }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { // todo wrong type / runtime value console.log(exchange.input.request) return exchange() @@ -43,7 +43,7 @@ await graffle.rawString({ document: `{ languages { code } }` }) headers: Headers { accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', 'content-type': 'application/json', - 'x-sent-at-time': '1726068743905' + 'x-sent-at-time': '1726346409373' }, signal: undefined, method: 'post', diff --git a/website/content/examples/transport-http-headers.md b/website/content/examples/transport-http-headers.md index e6ce0192f..d49c21cad 100644 --- a/website/content/examples/transport-http-headers.md +++ b/website/content/examples/transport-http-headers.md @@ -29,7 +29,7 @@ const graffle = Graffle .with({ transport: { headers: { 'x-something-to-unset': `` } }, }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { console.log(exchange.input.request.headers) return exchange() }) diff --git a/website/content/examples/transport-http-method-get.md b/website/content/examples/transport-http-method-get.md index 946112c32..e9f336016 100644 --- a/website/content/examples/transport-http-method-get.md +++ b/website/content/examples/transport-http-method-get.md @@ -17,14 +17,14 @@ const graffle = Pokemon schema: `http://localhost:3000/graphql`, transport: { methodMode: `getReads` }, // [!code highlight] }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { console.log(exchange.input.request) return exchange() }) // The following request will use an HTTP POST method because it is // using a "mutation" type of operation. -await graffle.rawString({ document: `mutation addPokemon(attack:0, defense:0, hp:1, name:"Nano") { name }` }) +await graffle.rawString({ document: `mutation { addPokemon(attack:0, defense:0, hp:1, name:"Nano") { name } }` }) // The following request will use an HTTP GET method because it // is using a "query" type of operation. @@ -45,20 +45,20 @@ await graffle.rawString({ document: `query { pokemonByName(name:"Nano") { hp } } signal: undefined, method: 'post', url: URL { - href: 'http://localhost:3001/graphql', - origin: 'http://localhost:3001', + href: 'http://localhost:3000/graphql', + origin: 'http://localhost:3000', protocol: 'http:', username: '', password: '', - host: 'localhost:3001', + host: 'localhost:3000', hostname: 'localhost', - port: '3001', + port: '3000', pathname: '/graphql', search: '', searchParams: URLSearchParams {}, hash: '' }, - body: '{"query":"mutation addPokemon(attack:0, defense:0, hp:1, name:\\"Nano\\") { name }"}' + body: '{"query":"mutation { addPokemon(attack:0, defense:0, hp:1, name:\\"Nano\\") { name } }"}' } ``` @@ -72,14 +72,14 @@ await graffle.rawString({ document: `query { pokemonByName(name:"Nano") { hp } } signal: undefined, method: 'get', url: URL { - href: 'http://localhost:3001/graphql?query=query+%7B+pokemonByName%28name%3A%22Nano%22%29+%7B+hp+%7D+%7D', - origin: 'http://localhost:3001', + href: 'http://localhost:3000/graphql?query=query+%7B+pokemonByName%28name%3A%22Nano%22%29+%7B+hp+%7D+%7D', + origin: 'http://localhost:3000', protocol: 'http:', username: '', password: '', - host: 'localhost:3001', + host: 'localhost:3000', hostname: 'localhost', - port: '3001', + port: '3000', pathname: '/graphql', search: '?query=query+%7B+pokemonByName%28name%3A%22Nano%22%29+%7B+hp+%7D+%7D', searchParams: URLSearchParams { 'query' => 'query { pokemonByName(name:"Nano") { hp } }' }, diff --git a/website/content/examples/transport-http-raw.md b/website/content/examples/transport-http-raw.md index 4b68c0883..7fe6ba872 100644 --- a/website/content/examples/transport-http-raw.md +++ b/website/content/examples/transport-http-raw.md @@ -19,7 +19,7 @@ const graffle = Graffle }, }, }) - .use(async ({ exchange }) => { + .anyware(async ({ exchange }) => { console.log(exchange.input.request) return exchange() }) diff --git a/website/content/guides/overview/introduction.md b/website/content/guides/10_overview/10_introduction.md similarity index 100% rename from website/content/guides/overview/introduction.md rename to website/content/guides/10_overview/10_introduction.md diff --git a/website/content/guides/overview/getting-started-static.md b/website/content/guides/10_overview/20_getting-started/10_static.md similarity index 92% rename from website/content/guides/overview/getting-started-static.md rename to website/content/guides/10_overview/20_getting-started/10_static.md index 4eb17ff1d..942180f7d 100644 --- a/website/content/guides/overview/getting-started-static.md +++ b/website/content/guides/10_overview/20_getting-started/10_static.md @@ -1,3 +1,7 @@ +--- +title: Static +--- + # Getting Started ## Install Package @@ -25,7 +29,7 @@ const graffle = Graffle.create({ }) // ---cut--- -const result = await graffle.rawString({ +const data = await graffle.rawString({ document: ` query countries ($filter: [String!]) { countries (filter: { name: { in: $filter } }) { @@ -39,7 +43,7 @@ const result = await graffle.rawString({ variables: { filter: [`Canada`, `Germany`, `Japan`] }, }) -console.log(result.data) +console.log(data) // ^? ``` diff --git a/website/content/guides/overview/getting-started-generated.md b/website/content/guides/10_overview/20_getting-started/20_generated.md similarity index 98% rename from website/content/guides/overview/getting-started-generated.md rename to website/content/guides/10_overview/20_getting-started/20_generated.md index bff2891c7..1571166cb 100644 --- a/website/content/guides/overview/getting-started-generated.md +++ b/website/content/guides/10_overview/20_getting-started/20_generated.md @@ -1,3 +1,7 @@ +--- +title: Generated +--- + # Getting Started The following takes you through a simple example to demonstrate how generation works and what it gives you. @@ -58,10 +62,10 @@ Now you're ready to send a request (run e.g. `pnpm tsx main.ts`). Before getting import { Graffle } from './graffle/__.js' const graffle = Graffle.create() // ---cut--- -const result = await graffle.rawString({ +const data = await graffle.rawString({ document: `query { continents { name } }`, }) -console.log(result.data) +console.log(data) ``` diff --git a/website/content/guides/overview/output.md b/website/content/guides/10_overview/30_output.md similarity index 100% rename from website/content/guides/overview/output.md rename to website/content/guides/10_overview/30_output.md diff --git a/website/content/guides/overview/anyware.md b/website/content/guides/10_overview/40_anyware.md similarity index 100% rename from website/content/guides/overview/anyware.md rename to website/content/guides/10_overview/40_anyware.md diff --git a/website/content/guides/20_methods/batch.md b/website/content/guides/20_methods/batch.md new file mode 100644 index 000000000..be9313232 --- /dev/null +++ b/website/content/guides/20_methods/batch.md @@ -0,0 +1 @@ +# Batch ⩕ diff --git a/website/content/guides/20_methods/document.md b/website/content/guides/20_methods/document.md new file mode 100644 index 000000000..c9d0d15aa --- /dev/null +++ b/website/content/guides/20_methods/document.md @@ -0,0 +1 @@ +# Document ⩕ diff --git a/website/content/guides/methods/raw.md b/website/content/guides/20_methods/raw.md similarity index 100% rename from website/content/guides/methods/raw.md rename to website/content/guides/20_methods/raw.md diff --git a/website/content/guides/20_methods/root-fields.md b/website/content/guides/20_methods/root-fields.md new file mode 100644 index 000000000..44e903618 --- /dev/null +++ b/website/content/guides/20_methods/root-fields.md @@ -0,0 +1 @@ +# Root Fields ⩕ diff --git a/website/content/guides/20_methods/with.md b/website/content/guides/20_methods/with.md new file mode 100644 index 000000000..517304b6e --- /dev/null +++ b/website/content/guides/20_methods/with.md @@ -0,0 +1,5 @@ +# With + + + +The `with` method allows you to incremental configure the client. Each invocation creates a lightweight _copy_ thus allowing you to branch specialized variants of your client into local use-cases without affecting a top level shared instance. diff --git a/website/content/guides/transports/http.md b/website/content/guides/30_transports/http.md similarity index 100% rename from website/content/guides/transports/http.md rename to website/content/guides/30_transports/http.md diff --git a/website/content/guides/transports/memory.md b/website/content/guides/30_transports/memory.md similarity index 100% rename from website/content/guides/transports/memory.md rename to website/content/guides/30_transports/memory.md diff --git a/website/content/guides/40_graphql-feature-mapping/aliases.md b/website/content/guides/40_graphql-feature-mapping/aliases.md new file mode 100644 index 000000000..77d9f5b09 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/aliases.md @@ -0,0 +1 @@ +# Aliases ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/arguments.md b/website/content/guides/40_graphql-feature-mapping/arguments.md new file mode 100644 index 000000000..df2cbc983 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/arguments.md @@ -0,0 +1 @@ +# Arguments ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/custom-scalars.md b/website/content/guides/40_graphql-feature-mapping/custom-scalars.md new file mode 100644 index 000000000..0bc2ebc38 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/custom-scalars.md @@ -0,0 +1 @@ +# Custom Scalars ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/directives.md b/website/content/guides/40_graphql-feature-mapping/directives.md new file mode 100644 index 000000000..c28ee4454 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/directives.md @@ -0,0 +1 @@ +# Directives ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/enums.md b/website/content/guides/40_graphql-feature-mapping/enums.md new file mode 100644 index 000000000..0583da244 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/enums.md @@ -0,0 +1 @@ +# Enums ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/interfaces.md b/website/content/guides/40_graphql-feature-mapping/interfaces.md new file mode 100644 index 000000000..cfcd9678c --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/interfaces.md @@ -0,0 +1 @@ +# Interfaces ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/selection-groups.md b/website/content/guides/40_graphql-feature-mapping/selection-groups.md new file mode 100644 index 000000000..e34ad3730 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/selection-groups.md @@ -0,0 +1 @@ +# Selection Groups ⩕ diff --git a/website/content/guides/40_graphql-feature-mapping/unions.md b/website/content/guides/40_graphql-feature-mapping/unions.md new file mode 100644 index 000000000..a4504d094 --- /dev/null +++ b/website/content/guides/40_graphql-feature-mapping/unions.md @@ -0,0 +1 @@ +# Unions ⩕ diff --git a/website/content/guides/50_misc/Select.md b/website/content/guides/50_misc/Select.md new file mode 100644 index 000000000..13396f6bd --- /dev/null +++ b/website/content/guides/50_misc/Select.md @@ -0,0 +1 @@ +# Select ⩕ diff --git a/website/content/guides/misc/about-generation.md b/website/content/guides/50_misc/about-generation.md similarity index 95% rename from website/content/guides/misc/about-generation.md rename to website/content/guides/50_misc/about-generation.md index 20a006635..46e7401e3 100644 --- a/website/content/guides/misc/about-generation.md +++ b/website/content/guides/50_misc/about-generation.md @@ -1,4 +1,4 @@ -# About Generation +# About Generation ⩕ This guide is an overview of using generation. Individual features enabled by generation are discussed in other guides. There is a [practical tutorial in getting started](../overview/getting-started-generated.md). But if you're trying to build a mental model of what Graffle means when it talks about generation or generally want more detail on generation tools, then this guide is for you. diff --git a/website/content/guides/misc/extension-authoring.md b/website/content/guides/50_misc/extension-authoring.md similarity index 100% rename from website/content/guides/misc/extension-authoring.md rename to website/content/guides/50_misc/extension-authoring.md diff --git a/website/content/guides/misc/schema-errors.md b/website/content/guides/50_misc/schema-errors.md similarity index 53% rename from website/content/guides/misc/schema-errors.md rename to website/content/guides/50_misc/schema-errors.md index 2855bc8bc..0c02669e0 100644 --- a/website/content/guides/misc/schema-errors.md +++ b/website/content/guides/50_misc/schema-errors.md @@ -1,4 +1,4 @@ -# Schema Errors +# Schema Errors ⩕ There is a GraphQL schema design pattern that advocates for encoding errors into your schema. It generally has two parts: One, objects that represent errors; Two, root fields that return unions of one success object and multiple error objects. The benefit of this approach is letting users know about error states and enabling clients to receive them in a type safe way. The general net positive is higher quality and easier to develop software. @@ -34,3 +34,65 @@ if (isError(result)) { result // type is narrowed to just success } ``` + +## How It Works + +This is achieved by automatically adding `__typename` field to each object-like root field's selection set. Then the returned object name can be analyzed. Example: + +```graphql +mutation { + foo { + ... on Bar { + id + } + } +} +``` + +becomes: + +```graphql +mutation { + foo { + __typename + ... on Bar { + id + } + } +} +``` + +The error that gets thrown is an instance of `Error` and its message is generic. However if all your error objects share an interface that contains a `message` field of `String` type then that message will be automatically be queried for used in the thrown Error. Example: + +```graphql +mutation { + foo { + ... on Bar { + id + } + } +} +``` + +becomes: + +```graphql +mutation { + foo { + __typename + ... on Error { # You could name your interface anything. + message # Your interface must have a message field of String type. + } + ... on Bar { + id + } + } +} +``` + +Example: + +```ts +const result = await client.mutation.fooOrThrow({ onBar: { id } }) +result // type is narrowed to just Bar case. +``` diff --git a/website/content/guides/extensions/opentelemetry.md b/website/content/guides/60_extensions/opentelemetry.md similarity index 60% rename from website/content/guides/extensions/opentelemetry.md rename to website/content/guides/60_extensions/opentelemetry.md index b937f4b99..9b4f4b5e2 100644 --- a/website/content/guides/extensions/opentelemetry.md +++ b/website/content/guides/60_extensions/opentelemetry.md @@ -2,16 +2,25 @@ -You can Instrument requests from Graffle with [OpenTelemetry](https://opentelemetry.io) using the `Opentelemetry` extension. Check out the example to get started. You'll also find output there that shows each span created, allowing you to see its parent, attributes, etc. +The `Opentelemetry` extension instruments requests with [OpenTelemetry](https://opentelemetry.io). -## Dependencies +## Getting Started -Graffle has an optional peer-dependency on `@opentelemetry/api`. To use this extension you'll need to install a compatible version into your project. You'll most likely need a handful of other `@opentelemetry/*` dependencies too. Check out the example for a working demo and some of what those deps might be. +`Opentelemetry` is a first party extension shipping in the graffle package. You will need to install some peer dependencies though. + +Graffle has an optional peer-dependency on `@opentelemetry/api`. You'll need to install a compatible version into your project and likely a handful of other `@opentelemetry/*` dependencies too. Check out the example for a working demo and some of what those deps might be. ```sh pnpm add @opentelemetry/api ``` +```ts twoslash +import { Graffle } from 'graffle' +import { Opentelemetry } from 'graffle/extensions' + +const graffle = Graffle.create({ schema: '...' }).use(Opentelemetry()) +``` + ## Span Structure Each request executed has a span created named `request` under a tracer called (by default) `graffle`. Within the request there is a span for each [hook](/todo). diff --git a/website/content/guides/60_extensions/or-throw.md b/website/content/guides/60_extensions/or-throw.md new file mode 100644 index 000000000..190f5d39c --- /dev/null +++ b/website/content/guides/60_extensions/or-throw.md @@ -0,0 +1,125 @@ +# Or Throw + + + +## Introduction + +The `OrThrow` extension decorates the builder with new methods for sending requests that will always throw on any error. + +If you do not configure your base instance's output to always throw but still find utility for that in some cases then this extension brings you a more convenient way to achieve that than one-off requests to [`.with`](../methods/with.md), whilst maintaining type safety. + +Example before/after: + +```ts +graffle.with({ + output: { + envelope: { + // enabled: true | false <-- Your choice here + errors: { + execution: false, + other: false, + schema: false, + }, + }, + errors: { + execution: `throw`, + other: `throw`, + schema: `throw`, + }, + }, +}).query.foo() +``` + +```ts +graffle.query.fooOrThrow() +``` + +## Getting Started + +`OrThrow` is a first party extension shipping in the graffle package. + +```ts twoslash +import { Graffle } from 'graffle' +import { OrThrow } from 'graffle/extensions' + +const graffle = Graffle.create({ schema: '...' }).use(OrThrow()) +``` + +Then access any request method's Or Throw variant: + +```ts +graffle.documentOrThrow(...).run(...) +graffle.query.fooOrThrow() +graffle.mutation.fooOrThrow() +``` + +## Methods + +For every request method on the client a new method is added whose name is the same base name with `"OrThrow"` (by default) added as a suffix. For example: + +``` +Base Added by Or Throw +---- ----------------- +graffle.raw graffle.rawOrThrow +graffle.document graffle.documentOrThrow +graffle.query.foo graffle.query.fooOrThrow +graffle.query.bar graffle.query.barOrThrow +... ... +``` + +Non-request methods like `"graffle.use(...)"` are ignored, so you will not see `graffle.useOrThrow` etc. + +## Configuration + +### Suffix + +You can change the suffix to something else if you prefer. For example: + +```ts twoslash +import { Graffle } from 'graffle' +import { OrThrow } from 'graffle/extensions' +// ---cut--- +const graffle = Graffle + .create({ schema: '...' }) + .use(OrThrow({ suffix: '_' })) +// ^^^^^^^^^^^ + +graffle.raw_ +// ^^^^ +``` + +## Known Issues + +### Conflicting Schema Root Field Names + +> [!info] You can probably ignore this issue but for thoroughness and transparency it is documented here should you be one of the few affected. If you do, please open an issue to help us prioritize a fix (there is one) or better yet submit a PR (hint: have the extension use the runtime schema index to ensure it only deals with its OrThrow added methods). + +Imagine that you are: + +1. Using the generated client +2. Using the Or Throw extension +3. Using its default suffix (`OrThrow`) +4. And you have a schema that includes this: + ```gql + Query { + doThingOrThrow: IgnoreMe + # ^^^^^^^ Notice this. + } + ``` + +Then, your client would include these methods: + +```ts +graffle.query.fooOrThrow() +graffle.query.fooOrThrowOrThrow() +``` + +_The issue is that both methods will run using the `OrThrow` semantics meaning `fooOrThrow` will always be executed as if you executed `fooOrThrowOrThrow`._ + +Workarounds: + +1. Use [`.document(...)`](../20_methods/document.md) e.g. + ```ts + graffle.document({ query: { fooOrThrow: {/* ... */} } }) + ``` +2. [Change the Or Throw suffix](#suffix) to something that doesn't conflict with your schema. diff --git a/website/content/guides/60_extensions/upload.md b/website/content/guides/60_extensions/upload.md new file mode 100644 index 000000000..2b7d1d93a --- /dev/null +++ b/website/content/guides/60_extensions/upload.md @@ -0,0 +1,16 @@ +# Upload + + + +Adds support for [GraphQL Multipart Request](https://github.com/jaydenseric/graphql-multipart-request-spec) to Graffle. + +## Getting Started + +`Upload` is a first party extension shipping in the graffle package. + +```ts twoslash +import { Graffle } from 'graffle' +import { Upload } from 'graffle/extensions' + +const graffle = Graffle.create({ schema: '...' }).use(Upload()) +``` diff --git a/website/content/guides/_example_links/abort.md b/website/content/guides/_example_links/abort.md index cf791fe22..89c82c344 100644 --- a/website/content/guides/_example_links/abort.md +++ b/website/content/guides/_example_links/abort.md @@ -1 +1 @@ -###### Examples -> [Abort](../../examples/transport-http-abort.md) + diff --git a/website/content/guides/_example_links/arguments.md b/website/content/guides/_example_links/arguments.md index 2d9a167ae..41b120b71 100644 --- a/website/content/guides/_example_links/arguments.md +++ b/website/content/guides/_example_links/arguments.md @@ -1 +1 @@ -###### Examples -> [Arguments](../../examples/generated-arguments.md) + diff --git a/website/content/guides/_example_links/default.md b/website/content/guides/_example_links/default.md index 37f0d3cc9..8f817f92f 100644 --- a/website/content/guides/_example_links/default.md +++ b/website/content/guides/_example_links/default.md @@ -1 +1 @@ -###### Examples -> [Default](../../examples/output-default.md) + diff --git a/website/content/guides/_example_links/envelope-error.md b/website/content/guides/_example_links/envelope-error.md index 7c5215773..4db1ac1a1 100644 --- a/website/content/guides/_example_links/envelope-error.md +++ b/website/content/guides/_example_links/envelope-error.md @@ -1 +1 @@ -###### Examples -> [Envelope Error](../../examples/output-envelope-error.md) + diff --git a/website/content/guides/_example_links/envelope.md b/website/content/guides/_example_links/envelope.md index 4e7a95f27..9b59c62af 100644 --- a/website/content/guides/_example_links/envelope.md +++ b/website/content/guides/_example_links/envelope.md @@ -1 +1 @@ -###### Examples -> [Envelope](../../examples/output-envelope.md) / [Envelope Error](../../examples/output-envelope-error.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/envelope_envelope-error.md b/website/content/guides/_example_links/envelope_envelope-error.md index 7c5215773..4db1ac1a1 100644 --- a/website/content/guides/_example_links/envelope_envelope-error.md +++ b/website/content/guides/_example_links/envelope_envelope-error.md @@ -1 +1 @@ -###### Examples -> [Envelope Error](../../examples/output-envelope-error.md) + diff --git a/website/content/guides/_example_links/envelope_envelope.md b/website/content/guides/_example_links/envelope_envelope.md index ad08b8ff8..96d0f39e8 100644 --- a/website/content/guides/_example_links/envelope_envelope.md +++ b/website/content/guides/_example_links/envelope_envelope.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/envelope_envelope_error-throw.md b/website/content/guides/_example_links/envelope_envelope_error-throw.md index ad08b8ff8..96d0f39e8 100644 --- a/website/content/guides/_example_links/envelope_envelope_error-throw.md +++ b/website/content/guides/_example_links/envelope_envelope_error-throw.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/envelope_error-throw.md b/website/content/guides/_example_links/envelope_error-throw.md index 75adc0935..620796314 100644 --- a/website/content/guides/_example_links/envelope_error-throw.md +++ b/website/content/guides/_example_links/envelope_error-throw.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/error-throw.md b/website/content/guides/_example_links/error-throw.md index ad08b8ff8..96d0f39e8 100644 --- a/website/content/guides/_example_links/error-throw.md +++ b/website/content/guides/_example_links/error-throw.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/extension.md b/website/content/guides/_example_links/extension.md index f198f988f..ec6eab6fb 100644 --- a/website/content/guides/_example_links/extension.md +++ b/website/content/guides/_example_links/extension.md @@ -1 +1 @@ -###### Examples -> [Opentelemetry](../../examples/extension-opentelemetry.md) / [Custom Fetch](../../examples/transport-http-custom-fetch.md) / [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) + diff --git a/website/content/guides/_example_links/extension_fetch.md b/website/content/guides/_example_links/extension_fetch.md index 444e13e59..941e8caff 100644 --- a/website/content/guides/_example_links/extension_fetch.md +++ b/website/content/guides/_example_links/extension_fetch.md @@ -1 +1 @@ -###### Examples -> [Custom Fetch](../../examples/transport-http-custom-fetch.md) + diff --git a/website/content/guides/_example_links/extension_headers.md b/website/content/guides/_example_links/extension_headers.md index 87fcb99d2..0adf1b51a 100644 --- a/website/content/guides/_example_links/extension_headers.md +++ b/website/content/guides/_example_links/extension_headers.md @@ -1 +1 @@ -###### Examples -> [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) + diff --git a/website/content/guides/_example_links/extension_opentelemetry.md b/website/content/guides/_example_links/extension_opentelemetry.md index a361ac7d1..926c771f7 100644 --- a/website/content/guides/_example_links/extension_opentelemetry.md +++ b/website/content/guides/_example_links/extension_opentelemetry.md @@ -1 +1 @@ -###### Examples -> [Opentelemetry](../../examples/extension-opentelemetry.md) + diff --git a/website/content/guides/_example_links/extension_or-throw.md b/website/content/guides/_example_links/extension_or-throw.md new file mode 100644 index 000000000..c71020917 --- /dev/null +++ b/website/content/guides/_example_links/extension_or-throw.md @@ -0,0 +1 @@ + diff --git a/website/content/guides/_example_links/fetch.md b/website/content/guides/_example_links/fetch.md index 444e13e59..941e8caff 100644 --- a/website/content/guides/_example_links/fetch.md +++ b/website/content/guides/_example_links/fetch.md @@ -1 +1 @@ -###### Examples -> [Custom Fetch](../../examples/transport-http-custom-fetch.md) + diff --git a/website/content/guides/_example_links/generated.md b/website/content/guides/_example_links/generated.md index 2d9a167ae..41b120b71 100644 --- a/website/content/guides/_example_links/generated.md +++ b/website/content/guides/_example_links/generated.md @@ -1 +1 @@ -###### Examples -> [Arguments](../../examples/generated-arguments.md) + diff --git a/website/content/guides/_example_links/generated_arguments.md b/website/content/guides/_example_links/generated_arguments.md index 2d9a167ae..41b120b71 100644 --- a/website/content/guides/_example_links/generated_arguments.md +++ b/website/content/guides/_example_links/generated_arguments.md @@ -1 +1 @@ -###### Examples -> [Arguments](../../examples/generated-arguments.md) + diff --git a/website/content/guides/_example_links/headers.md b/website/content/guides/_example_links/headers.md index ea8e00493..d2084f3d8 100644 --- a/website/content/guides/_example_links/headers.md +++ b/website/content/guides/_example_links/headers.md @@ -1 +1 @@ -###### Examples -> [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) / [Headers](../../examples/transport-http-headers.md) + diff --git a/website/content/guides/_example_links/headers_raw.md b/website/content/guides/_example_links/headers_raw.md index 467b1a73e..fe6c7f7d0 100644 --- a/website/content/guides/_example_links/headers_raw.md +++ b/website/content/guides/_example_links/headers_raw.md @@ -1 +1 @@ -###### Examples -> [Headers](../../examples/transport-http-headers.md) + diff --git a/website/content/guides/_example_links/method-get.md b/website/content/guides/_example_links/method-get.md index 3ab83cf61..50eaad37b 100644 --- a/website/content/guides/_example_links/method-get.md +++ b/website/content/guides/_example_links/method-get.md @@ -1 +1 @@ -###### Examples -> [Method Get](../../examples/transport-http-method-get.md) + diff --git a/website/content/guides/_example_links/opentelemetry.md b/website/content/guides/_example_links/opentelemetry.md index a361ac7d1..926c771f7 100644 --- a/website/content/guides/_example_links/opentelemetry.md +++ b/website/content/guides/_example_links/opentelemetry.md @@ -1 +1 @@ -###### Examples -> [Opentelemetry](../../examples/extension-opentelemetry.md) + diff --git a/website/content/guides/_example_links/or-throw.md b/website/content/guides/_example_links/or-throw.md new file mode 100644 index 000000000..c71020917 --- /dev/null +++ b/website/content/guides/_example_links/or-throw.md @@ -0,0 +1 @@ + diff --git a/website/content/guides/_example_links/output.md b/website/content/guides/_example_links/output.md index c14e046e0..21a1c21ad 100644 --- a/website/content/guides/_example_links/output.md +++ b/website/content/guides/_example_links/output.md @@ -1 +1 @@ -###### Examples -> [Default](../../examples/output-default.md) / [Envelope](../../examples/output-envelope.md) / [Envelope Error](../../examples/output-envelope-error.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) / [Return Error](../../examples/output-return-error.md) / [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/output_default.md b/website/content/guides/_example_links/output_default.md index 37f0d3cc9..8f817f92f 100644 --- a/website/content/guides/_example_links/output_default.md +++ b/website/content/guides/_example_links/output_default.md @@ -1 +1 @@ -###### Examples -> [Default](../../examples/output-default.md) + diff --git a/website/content/guides/_example_links/output_envelope-error.md b/website/content/guides/_example_links/output_envelope-error.md index 7c5215773..4db1ac1a1 100644 --- a/website/content/guides/_example_links/output_envelope-error.md +++ b/website/content/guides/_example_links/output_envelope-error.md @@ -1 +1 @@ -###### Examples -> [Envelope Error](../../examples/output-envelope-error.md) + diff --git a/website/content/guides/_example_links/output_envelope.md b/website/content/guides/_example_links/output_envelope.md index 4e7a95f27..9b59c62af 100644 --- a/website/content/guides/_example_links/output_envelope.md +++ b/website/content/guides/_example_links/output_envelope.md @@ -1 +1 @@ -###### Examples -> [Envelope](../../examples/output-envelope.md) / [Envelope Error](../../examples/output-envelope-error.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/output_envelope_envelope-error.md b/website/content/guides/_example_links/output_envelope_envelope-error.md index 7c5215773..4db1ac1a1 100644 --- a/website/content/guides/_example_links/output_envelope_envelope-error.md +++ b/website/content/guides/_example_links/output_envelope_envelope-error.md @@ -1 +1 @@ -###### Examples -> [Envelope Error](../../examples/output-envelope-error.md) + diff --git a/website/content/guides/_example_links/output_envelope_envelope.md b/website/content/guides/_example_links/output_envelope_envelope.md index ad08b8ff8..96d0f39e8 100644 --- a/website/content/guides/_example_links/output_envelope_envelope.md +++ b/website/content/guides/_example_links/output_envelope_envelope.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/output_envelope_envelope_error-throw.md b/website/content/guides/_example_links/output_envelope_envelope_error-throw.md index ad08b8ff8..96d0f39e8 100644 --- a/website/content/guides/_example_links/output_envelope_envelope_error-throw.md +++ b/website/content/guides/_example_links/output_envelope_envelope_error-throw.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/output_envelope_error-throw.md b/website/content/guides/_example_links/output_envelope_error-throw.md index 75adc0935..620796314 100644 --- a/website/content/guides/_example_links/output_envelope_error-throw.md +++ b/website/content/guides/_example_links/output_envelope_error-throw.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) / [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/output_error-throw.md b/website/content/guides/_example_links/output_error-throw.md index ad08b8ff8..96d0f39e8 100644 --- a/website/content/guides/_example_links/output_error-throw.md +++ b/website/content/guides/_example_links/output_error-throw.md @@ -1 +1 @@ -###### Examples -> [Envelope Error Throw](../../examples/output-envelope-error-throw.md) + diff --git a/website/content/guides/_example_links/output_return-error-execution.md b/website/content/guides/_example_links/output_return-error-execution.md index 0cec1fd3a..9d6178d4f 100644 --- a/website/content/guides/_example_links/output_return-error-execution.md +++ b/website/content/guides/_example_links/output_return-error-execution.md @@ -1 +1 @@ -###### Examples -> [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/output_return-error.md b/website/content/guides/_example_links/output_return-error.md index 99300c64f..14617602c 100644 --- a/website/content/guides/_example_links/output_return-error.md +++ b/website/content/guides/_example_links/output_return-error.md @@ -1 +1 @@ -###### Examples -> [Return Error](../../examples/output-return-error.md) / [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/output_return-error_return-error-execution.md b/website/content/guides/_example_links/output_return-error_return-error-execution.md index 0cec1fd3a..9d6178d4f 100644 --- a/website/content/guides/_example_links/output_return-error_return-error-execution.md +++ b/website/content/guides/_example_links/output_return-error_return-error-execution.md @@ -1 +1 @@ -###### Examples -> [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/raw.md b/website/content/guides/_example_links/raw.md index bfca402b3..bb3273bc0 100644 --- a/website/content/guides/_example_links/raw.md +++ b/website/content/guides/_example_links/raw.md @@ -1 +1 @@ -###### Examples -> [Raw](../../examples/raw-raw.md) / [Raw String](../../examples/raw-raw-string.md) / [Raw String Typed](../../examples/raw-raw-string-typed.md) / [Raw Typed](../../examples/raw-raw-typed.md) / [Headers](../../examples/transport-http-headers.md) / [Raw](../../examples/transport-http-raw.md) + diff --git a/website/content/guides/_example_links/rawString.md b/website/content/guides/_example_links/rawString.md index 72301a7ed..5613b3788 100644 --- a/website/content/guides/_example_links/rawString.md +++ b/website/content/guides/_example_links/rawString.md @@ -1 +1 @@ -###### Examples -> [Raw String](../../examples/raw-raw-string.md) / [Raw String Typed](../../examples/raw-raw-string-typed.md) + diff --git a/website/content/guides/_example_links/rawString_rawTyped.md b/website/content/guides/_example_links/rawString_rawTyped.md index 15ee419e4..7fc242589 100644 --- a/website/content/guides/_example_links/rawString_rawTyped.md +++ b/website/content/guides/_example_links/rawString_rawTyped.md @@ -1 +1 @@ -###### Examples -> [Raw String Typed](../../examples/raw-raw-string-typed.md) + diff --git a/website/content/guides/_example_links/rawTyped.md b/website/content/guides/_example_links/rawTyped.md index 6f367a11c..3debc5791 100644 --- a/website/content/guides/_example_links/rawTyped.md +++ b/website/content/guides/_example_links/rawTyped.md @@ -1 +1 @@ -###### Examples -> [Raw String Typed](../../examples/raw-raw-string-typed.md) / [Raw Typed](../../examples/raw-raw-typed.md) + diff --git a/website/content/guides/_example_links/raw_rawString.md b/website/content/guides/_example_links/raw_rawString.md index 72301a7ed..5613b3788 100644 --- a/website/content/guides/_example_links/raw_rawString.md +++ b/website/content/guides/_example_links/raw_rawString.md @@ -1 +1 @@ -###### Examples -> [Raw String](../../examples/raw-raw-string.md) / [Raw String Typed](../../examples/raw-raw-string-typed.md) + diff --git a/website/content/guides/_example_links/raw_rawString_rawTyped.md b/website/content/guides/_example_links/raw_rawString_rawTyped.md index 15ee419e4..7fc242589 100644 --- a/website/content/guides/_example_links/raw_rawString_rawTyped.md +++ b/website/content/guides/_example_links/raw_rawString_rawTyped.md @@ -1 +1 @@ -###### Examples -> [Raw String Typed](../../examples/raw-raw-string-typed.md) + diff --git a/website/content/guides/_example_links/raw_rawTyped.md b/website/content/guides/_example_links/raw_rawTyped.md index 6f367a11c..3debc5791 100644 --- a/website/content/guides/_example_links/raw_rawTyped.md +++ b/website/content/guides/_example_links/raw_rawTyped.md @@ -1 +1 @@ -###### Examples -> [Raw String Typed](../../examples/raw-raw-string-typed.md) / [Raw Typed](../../examples/raw-raw-typed.md) + diff --git a/website/content/guides/_example_links/return-error-execution.md b/website/content/guides/_example_links/return-error-execution.md index 0cec1fd3a..9d6178d4f 100644 --- a/website/content/guides/_example_links/return-error-execution.md +++ b/website/content/guides/_example_links/return-error-execution.md @@ -1 +1 @@ -###### Examples -> [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/return-error.md b/website/content/guides/_example_links/return-error.md index 99300c64f..14617602c 100644 --- a/website/content/guides/_example_links/return-error.md +++ b/website/content/guides/_example_links/return-error.md @@ -1 +1 @@ -###### Examples -> [Return Error](../../examples/output-return-error.md) / [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/return-error_return-error-execution.md b/website/content/guides/_example_links/return-error_return-error-execution.md index 0cec1fd3a..9d6178d4f 100644 --- a/website/content/guides/_example_links/return-error_return-error-execution.md +++ b/website/content/guides/_example_links/return-error_return-error-execution.md @@ -1 +1 @@ -###### Examples -> [Return Error Execution](../../examples/output-return-error-execution.md) + diff --git a/website/content/guides/_example_links/transport-http.md b/website/content/guides/_example_links/transport-http.md index 335da9bd9..e2eb3fe13 100644 --- a/website/content/guides/_example_links/transport-http.md +++ b/website/content/guides/_example_links/transport-http.md @@ -1 +1 @@ -###### Examples -> [Abort](../../examples/transport-http-abort.md) / [Custom Fetch](../../examples/transport-http-custom-fetch.md) / [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) / [Headers](../../examples/transport-http-headers.md) / [Method Get](../../examples/transport-http-method-get.md) / [Raw](../../examples/transport-http-raw.md) + diff --git a/website/content/guides/_example_links/transport-http_abort.md b/website/content/guides/_example_links/transport-http_abort.md index cf791fe22..89c82c344 100644 --- a/website/content/guides/_example_links/transport-http_abort.md +++ b/website/content/guides/_example_links/transport-http_abort.md @@ -1 +1 @@ -###### Examples -> [Abort](../../examples/transport-http-abort.md) + diff --git a/website/content/guides/_example_links/transport-http_extension.md b/website/content/guides/_example_links/transport-http_extension.md index 4f6535c57..c01db6f0d 100644 --- a/website/content/guides/_example_links/transport-http_extension.md +++ b/website/content/guides/_example_links/transport-http_extension.md @@ -1 +1 @@ -###### Examples -> [Custom Fetch](../../examples/transport-http-custom-fetch.md) / [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) + diff --git a/website/content/guides/_example_links/transport-http_extension_fetch.md b/website/content/guides/_example_links/transport-http_extension_fetch.md index 444e13e59..941e8caff 100644 --- a/website/content/guides/_example_links/transport-http_extension_fetch.md +++ b/website/content/guides/_example_links/transport-http_extension_fetch.md @@ -1 +1 @@ -###### Examples -> [Custom Fetch](../../examples/transport-http-custom-fetch.md) + diff --git a/website/content/guides/_example_links/transport-http_extension_headers.md b/website/content/guides/_example_links/transport-http_extension_headers.md index 87fcb99d2..0adf1b51a 100644 --- a/website/content/guides/_example_links/transport-http_extension_headers.md +++ b/website/content/guides/_example_links/transport-http_extension_headers.md @@ -1 +1 @@ -###### Examples -> [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) + diff --git a/website/content/guides/_example_links/transport-http_fetch.md b/website/content/guides/_example_links/transport-http_fetch.md index 444e13e59..941e8caff 100644 --- a/website/content/guides/_example_links/transport-http_fetch.md +++ b/website/content/guides/_example_links/transport-http_fetch.md @@ -1 +1 @@ -###### Examples -> [Custom Fetch](../../examples/transport-http-custom-fetch.md) + diff --git a/website/content/guides/_example_links/transport-http_headers.md b/website/content/guides/_example_links/transport-http_headers.md index ea8e00493..d2084f3d8 100644 --- a/website/content/guides/_example_links/transport-http_headers.md +++ b/website/content/guides/_example_links/transport-http_headers.md @@ -1 +1 @@ -###### Examples -> [Dynamic Headers](../../examples/transport-http-dynamic-headers.md) / [Headers](../../examples/transport-http-headers.md) + diff --git a/website/content/guides/_example_links/transport-http_headers_raw.md b/website/content/guides/_example_links/transport-http_headers_raw.md index 467b1a73e..fe6c7f7d0 100644 --- a/website/content/guides/_example_links/transport-http_headers_raw.md +++ b/website/content/guides/_example_links/transport-http_headers_raw.md @@ -1 +1 @@ -###### Examples -> [Headers](../../examples/transport-http-headers.md) + diff --git a/website/content/guides/_example_links/transport-http_method-get.md b/website/content/guides/_example_links/transport-http_method-get.md index 3ab83cf61..50eaad37b 100644 --- a/website/content/guides/_example_links/transport-http_method-get.md +++ b/website/content/guides/_example_links/transport-http_method-get.md @@ -1 +1 @@ -###### Examples -> [Method Get](../../examples/transport-http-method-get.md) + diff --git a/website/content/guides/_example_links/transport-http_raw.md b/website/content/guides/_example_links/transport-http_raw.md index 3056106ec..3819c1502 100644 --- a/website/content/guides/_example_links/transport-http_raw.md +++ b/website/content/guides/_example_links/transport-http_raw.md @@ -1 +1 @@ -###### Examples -> [Headers](../../examples/transport-http-headers.md) / [Raw](../../examples/transport-http-raw.md) + diff --git a/website/content/guides/_example_links/transport-memory.md b/website/content/guides/_example_links/transport-memory.md index 56d2f79c5..0d4acc894 100644 --- a/website/content/guides/_example_links/transport-memory.md +++ b/website/content/guides/_example_links/transport-memory.md @@ -1 +1 @@ -###### Examples -> [Transport Memory](../../examples/other-transport-memory.md) + diff --git a/website/content/guides/extensions/file-upload.md b/website/content/guides/extensions/file-upload.md deleted file mode 100644 index 10e347081..000000000 --- a/website/content/guides/extensions/file-upload.md +++ /dev/null @@ -1 +0,0 @@ -# File Upload diff --git a/website/content/guides/extensions/or-throw.md b/website/content/guides/extensions/or-throw.md deleted file mode 100644 index 7dc271cc4..000000000 --- a/website/content/guides/extensions/or-throw.md +++ /dev/null @@ -1 +0,0 @@ -# Or Throw diff --git a/website/content/guides/graphql-feature-mapping/aliases.md b/website/content/guides/graphql-feature-mapping/aliases.md deleted file mode 100644 index 4a3730eeb..000000000 --- a/website/content/guides/graphql-feature-mapping/aliases.md +++ /dev/null @@ -1 +0,0 @@ -# Aliases diff --git a/website/content/guides/graphql-feature-mapping/arguments.md b/website/content/guides/graphql-feature-mapping/arguments.md deleted file mode 100644 index 6c0994714..000000000 --- a/website/content/guides/graphql-feature-mapping/arguments.md +++ /dev/null @@ -1 +0,0 @@ -# Arguments diff --git a/website/content/guides/graphql-feature-mapping/custom-scalars.md b/website/content/guides/graphql-feature-mapping/custom-scalars.md deleted file mode 100644 index cc30d3436..000000000 --- a/website/content/guides/graphql-feature-mapping/custom-scalars.md +++ /dev/null @@ -1 +0,0 @@ -# Custom Scalars diff --git a/website/content/guides/graphql-feature-mapping/directives.md b/website/content/guides/graphql-feature-mapping/directives.md deleted file mode 100644 index dab690fad..000000000 --- a/website/content/guides/graphql-feature-mapping/directives.md +++ /dev/null @@ -1 +0,0 @@ -# Directives diff --git a/website/content/guides/graphql-feature-mapping/enums.md b/website/content/guides/graphql-feature-mapping/enums.md deleted file mode 100644 index ece8d08a7..000000000 --- a/website/content/guides/graphql-feature-mapping/enums.md +++ /dev/null @@ -1 +0,0 @@ -# Enums diff --git a/website/content/guides/graphql-feature-mapping/interfaces.md b/website/content/guides/graphql-feature-mapping/interfaces.md deleted file mode 100644 index 89ce18fbf..000000000 --- a/website/content/guides/graphql-feature-mapping/interfaces.md +++ /dev/null @@ -1 +0,0 @@ -# Interfaces diff --git a/website/content/guides/graphql-feature-mapping/selection-groups.md b/website/content/guides/graphql-feature-mapping/selection-groups.md deleted file mode 100644 index a1399ee28..000000000 --- a/website/content/guides/graphql-feature-mapping/selection-groups.md +++ /dev/null @@ -1 +0,0 @@ -# Selection Groups diff --git a/website/content/guides/graphql-feature-mapping/unions.md b/website/content/guides/graphql-feature-mapping/unions.md deleted file mode 100644 index 2fd4bf52c..000000000 --- a/website/content/guides/graphql-feature-mapping/unions.md +++ /dev/null @@ -1 +0,0 @@ -# Unions diff --git a/website/content/guides/methods/batch.md b/website/content/guides/methods/batch.md deleted file mode 100644 index 6bdde42ac..000000000 --- a/website/content/guides/methods/batch.md +++ /dev/null @@ -1 +0,0 @@ -# Batch diff --git a/website/content/guides/methods/document.md b/website/content/guides/methods/document.md deleted file mode 100644 index 6e1c08724..000000000 --- a/website/content/guides/methods/document.md +++ /dev/null @@ -1 +0,0 @@ -# Document diff --git a/website/content/guides/methods/or-throw.md b/website/content/guides/methods/or-throw.md deleted file mode 100644 index 9d7c774ff..000000000 --- a/website/content/guides/methods/or-throw.md +++ /dev/null @@ -1,63 +0,0 @@ -# Or Throw - -You can use `orThrow` method variants to only return the success. - -This is achieved by automatically adding `__typename` field to each object-like root field's selection set. Then the returned object name can be analyzed. Example: - -```graphql -mutation { - foo { - ... on Bar { - id - } - } -} -``` - -becomes: - -```graphql -mutation { - foo { - __typename - ... on Bar { - id - } - } -} -``` - -The error that gets thrown is an instance of `Error` and its message is generic. However if all your error objects share an interface that contains a `message` field of `String` type then that message will be automatically be queried for used in the thrown Error. Example: - -```graphql -mutation { - foo { - ... on Bar { - id - } - } -} -``` - -becomes: - -```graphql -mutation { - foo { - __typename - ... on Error { # You could name your interface anything. - message # Your interface must have a message field of String type. - } - ... on Bar { - id - } - } -} -``` - -Example: - -```ts -const result = await client.mutation.fooOrThrow({ onBar: { id } }) -result // type is narrowed to just Bar case. -``` diff --git a/website/content/guides/methods/root-fields.md b/website/content/guides/methods/root-fields.md deleted file mode 100644 index 305d0d0c9..000000000 --- a/website/content/guides/methods/root-fields.md +++ /dev/null @@ -1 +0,0 @@ -# Root Fields diff --git a/website/content/guides/misc/Select.md b/website/content/guides/misc/Select.md deleted file mode 100644 index 9dfe3fd88..000000000 --- a/website/content/guides/misc/Select.md +++ /dev/null @@ -1 +0,0 @@ -# Select diff --git a/website/content/index.md b/website/content/index.md index ab62497de..4de380175 100644 --- a/website/content/index.md +++ b/website/content/index.md @@ -17,10 +17,11 @@ hero: alt: Graffle features: - title: Spec Compliant - details: Graffle complies with the GraphQL over HTTP and GraphQL Multipart Request specifications. + details: Graffle complies with the GraphQL over HTTP and GraphQL Multipart Request specifications. - title: Extensible - # TODO Ability for extensions to add methods. details: Powerful type-safe extension system. Intercept and manipulate inputs, outputs, and core with hooks; Add new methods; And more. + - title: Ecosystem + details: Ready to go extensions for things like OpenTelemetry and file uploads to meet real world project needs. - title: In-Memory Schemas Too details: Not just a great way to query GraphQL APIs. Execute documents against in memory schemas just as easily with nearly the same interface. # TODO support for subscription type. @@ -30,9 +31,8 @@ features: details: All result types are automatically inferred based on your document structure across all GraphQL features including selection sets, directives, fragments, interfaces, and unions. - title: Schema Tailored Methods
( gen ) details: Range of methods to suit your input needs ranging from creating whole documents to selecting on exactly one Query Mutation or Subscription field. - # TODO add one more to have an even number of features. - # - title: Schema Errors
( gen ) - # details: First class support for schemas that have modelled errors into their design. Result Fields can be made to throw on errors or automatically map to error classes. + - title: Schema Errors
( gen ) + details: First class support for schemas that have modelled errors into their design. Result Fields can be made to throw on errors or automatically map to error classes. ---