diff --git a/.changeset/calm-brooms-sit.md b/.changeset/calm-brooms-sit.md new file mode 100644 index 0000000000..ed9fcae49c --- /dev/null +++ b/.changeset/calm-brooms-sit.md @@ -0,0 +1,6 @@ +--- +'houdini-svelte': major +'houdini': major +--- + +Removed this.error and this.redirect from function variables diff --git a/.changeset/tender-goats-flow.md b/.changeset/tender-goats-flow.md new file mode 100644 index 0000000000..894bc7bc92 --- /dev/null +++ b/.changeset/tender-goats-flow.md @@ -0,0 +1,5 @@ +--- +'houdini-svelte': patch +--- + +Query variables can now be inferred from route params diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eda9fb3e6d..91b9a32e31 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,7 +10,7 @@ on: # every commit on master push: branches: - - main + - 'main' jobs: format: @@ -63,7 +63,7 @@ jobs: id: pnpm-install with: version: 7 - + - name: Get pnpm store directory id: pnpm-cache run: | @@ -107,7 +107,7 @@ jobs: id: pnpm-install with: version: 7 - + - name: Get pnpm store directory id: pnpm-cache run: | @@ -121,7 +121,7 @@ jobs: ${{ matrix.os }}-pnpm-store- - name: Install dependencies run: pnpm install - + - name: Cache playwright binaries (ubuntu) uses: actions/cache@v3 if: matrix.os == 'ubuntu-latest' @@ -167,7 +167,7 @@ jobs: id: pnpm-install with: version: 7 - + - name: Get pnpm store directory id: pnpm-cache run: | @@ -193,4 +193,4 @@ jobs: run: pnpm --filter sveltekit run lint - name: End-to-End check - run: pnpm --filter sveltekit run check \ No newline at end of file + run: pnpm --filter sveltekit run check diff --git a/.gitignore b/.gitignore index 4f389a7100..1b60304e38 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ $houdini build/** dist/** .next/** + +vite.config.js.timestamp-* diff --git a/e2e/_api/server.mjs b/e2e/_api/server.mjs index bdd9a6fbff..c62032e179 100755 --- a/e2e/_api/server.mjs +++ b/e2e/_api/server.mjs @@ -6,7 +6,7 @@ import { resolvers, typeDefs } from './graphql.mjs' async function main() { const yogaApp = createServer({ - hostname: "::", + hostname: '::', logging: true, schema: { typeDefs, @@ -51,13 +51,13 @@ mutation AddUser { onSubscribe: async (ctx, msg) => { // prettier-ignore const { - schema, - execute, - subscribe, - contextFactory, - parse, - validate - } = yogaApp.getEnveloped(ctx); + schema, + execute, + subscribe, + contextFactory, + parse, + validate + } = yogaApp.getEnveloped(ctx); const args = { schema, @@ -72,7 +72,10 @@ mutation AddUser { } const errors = validate(args.schema, args.document) - if (errors.length) return errors + if (errors.length) { + console.log(errors) + return errors + } return args }, }, diff --git a/e2e/sveltekit/package.json b/e2e/sveltekit/package.json index f6f678ddfd..2f088a6945 100644 --- a/e2e/sveltekit/package.json +++ b/e2e/sveltekit/package.json @@ -37,7 +37,7 @@ "prettier": "^2.5.1", "prettier-plugin-svelte": "^2.5.0", "svelte": "3.55.0", - "svelte-check": "^2.2.6", + "svelte-check": "^3.0.1", "svelte-preprocess": "^5.0.0", "tslib": "^2.3.1", "typescript": "^4.9", diff --git a/e2e/sveltekit/src/lib/utils/routes.ts b/e2e/sveltekit/src/lib/utils/routes.ts index 55159253d3..7d7ce6eabc 100644 --- a/e2e/sveltekit/src/lib/utils/routes.ts +++ b/e2e/sveltekit/src/lib/utils/routes.ts @@ -3,7 +3,7 @@ export const routes = { GraphQL: 'http://localhost:4000/graphql', // features - Query_param: '/query-param', + nested_routes: '/nested-routes', isFetching_with_load: '/isFetching/with_load', isFetching_without_load: '/isFetching/without_load', isFetching_route_1: '/isFetching/route_1', @@ -44,6 +44,11 @@ export const routes = { Plugin_query_afterLoad: '/plugin/query/afterLoad', Plugin_query_onError: '/plugin/query/onError', Plugin_query_layout: '/plugin/query/layout', + Plugin_query_inferInput_userRoute_params: '/plugin/query/infer-input/user-testSnapshot-1', + Plugin_query_inferInput_optional: '/plugin/query/infer-input/optional', + Plugin_query_inferInput_optional2: '/plugin/query/infer-input/optional2', + Plugin_query_inferInput_customFunction: + '/plugin/query/infer-input/custom-function-testSnapshot-1', Plugin_subscription_renders: '/plugin/subscription/renders', diff --git a/e2e/sveltekit/src/routes/query-param/+error.svelte b/e2e/sveltekit/src/routes/nested-routes/+error.svelte similarity index 100% rename from e2e/sveltekit/src/routes/query-param/+error.svelte rename to e2e/sveltekit/src/routes/nested-routes/+error.svelte diff --git a/e2e/sveltekit/src/routes/query-param/+page.server.ts b/e2e/sveltekit/src/routes/nested-routes/+page.server.ts similarity index 83% rename from e2e/sveltekit/src/routes/query-param/+page.server.ts rename to e2e/sveltekit/src/routes/nested-routes/+page.server.ts index 4227aeb337..46362223a6 100644 --- a/e2e/sveltekit/src/routes/query-param/+page.server.ts +++ b/e2e/sveltekit/src/routes/nested-routes/+page.server.ts @@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async (event) => { - if (event.url.pathname.endsWith('query-param')) { + if (event.url.pathname.endsWith('nested-routes')) { // fallback to user 2... because why not? throw redirect(307, event.url.pathname + '/user-2'); } diff --git a/e2e/sveltekit/src/routes/query-param/Loading.svelte b/e2e/sveltekit/src/routes/nested-routes/Loading.svelte similarity index 100% rename from e2e/sveltekit/src/routes/query-param/Loading.svelte rename to e2e/sveltekit/src/routes/nested-routes/Loading.svelte diff --git a/e2e/sveltekit/src/routes/query-param/UserName.svelte b/e2e/sveltekit/src/routes/nested-routes/UserName.svelte similarity index 100% rename from e2e/sveltekit/src/routes/query-param/UserName.svelte rename to e2e/sveltekit/src/routes/nested-routes/UserName.svelte diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/+layout.gql b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/+layout.gql similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/+layout.gql rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/+layout.gql diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/+layout.svelte b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/+layout.svelte similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/+layout.svelte rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/+layout.svelte diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/+layout.ts b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/+layout.ts similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/+layout.ts rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/+layout.ts diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/+page.server.ts b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/+page.server.ts similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/+page.server.ts rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/+page.server.ts diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/birth/+page.gql b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/birth/+page.gql similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/birth/+page.gql rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/birth/+page.gql diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/birth/+page.svelte b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/birth/+page.svelte similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/birth/+page.svelte rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/birth/+page.svelte diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/birth/+page.ts b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/birth/+page.ts similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/birth/+page.ts rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/birth/+page.ts diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/friends/+page.gql b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/friends/+page.gql similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/friends/+page.gql rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/friends/+page.gql diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/friends/+page.svelte b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/friends/+page.svelte similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/friends/+page.svelte rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/friends/+page.svelte diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/friends/+page.ts b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/friends/+page.ts similarity index 100% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/friends/+page.ts rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/friends/+page.ts diff --git a/e2e/sveltekit/src/routes/query-param/user-[userId]/spec.ts b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/spec.ts similarity index 92% rename from e2e/sveltekit/src/routes/query-param/user-[userId]/spec.ts rename to e2e/sveltekit/src/routes/nested-routes/user-[userId]/spec.ts index b6d46c1e04..b231510dd9 100644 --- a/e2e/sveltekit/src/routes/query-param/user-[userId]/spec.ts +++ b/e2e/sveltekit/src/routes/nested-routes/user-[userId]/spec.ts @@ -4,7 +4,7 @@ import { expectToBe, expect_n_gql, goto, navSelector } from '../../../lib/utils/ test.describe('+Layout.gql', () => { test('No GraphQL request & response happen (SSR)', async ({ page }) => { - await goto(page, routes.Query_param); + await goto(page, routes.nested_routes); await expectToBe(page, 'Page: Samuel Jackson', 'h3'); }); @@ -13,7 +13,7 @@ test.describe('+Layout.gql', () => { }) => { await goto(page, routes.Home); - const listStr = await expect_n_gql(page, navSelector(routes.Query_param), 1); + const listStr = await expect_n_gql(page, navSelector(routes.nested_routes), 1); const expected = [`{"data":{"user":{"id":"Page_User:2","name":"Samuel Jackson"}}}`]; expect(listStr).toStrictEqual(expected); }); diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.gql b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.gql new file mode 100644 index 0000000000..13e1b96ba4 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.gql @@ -0,0 +1,5 @@ +query CustomFunctionRouteParamsUserQuery($snapshot: String! = "test", $id: ID! = "1") { + user(id: $id, snapshot: $snapshot) { + name + } +} diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.svelte b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.svelte new file mode 100644 index 0000000000..c9577ca513 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.svelte @@ -0,0 +1,11 @@ + + +
+ {$CustomFunctionRouteParamsUserQuery.data?.user.name} +
diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.ts b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.ts new file mode 100644 index 0000000000..8bae199366 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/+page.ts @@ -0,0 +1,8 @@ +import type { CustomFunctionRouteParamsUserQueryVariables } from './$houdini'; + +export const _CustomFunctionRouteParamsUserQueryVariables: CustomFunctionRouteParamsUserQueryVariables = + () => { + return { + id: '2' + }; + }; diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/spec.ts b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/spec.ts new file mode 100644 index 0000000000..61ecd518b9 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/custom-function-[snapshot]-[id]/spec.ts @@ -0,0 +1,11 @@ +import { test } from '@playwright/test'; +import { routes } from '../../../../../lib/utils/routes.js'; +import { expectToBe, goto } from '../../../../../lib/utils/testsHelper.js'; + +test.describe('query variables from route params', () => { + test('custom function', async ({ page }) => { + await goto(page, routes.Plugin_query_inferInput_customFunction); + + await expectToBe(page, 'Samuel Jackson'); + }); +}); diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/+page.gql b/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/+page.gql new file mode 100644 index 0000000000..92ed5aabc4 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/+page.gql @@ -0,0 +1,5 @@ +query OptionalRouteParamsUserQuery($snapshot: String! = "test", $id: ID! = "1") { + user(id: $id, snapshot: $snapshot) { + name + } +} diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/+page.svelte b/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/+page.svelte new file mode 100644 index 0000000000..2269207997 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/+page.svelte @@ -0,0 +1,11 @@ + + +
+ {$OptionalRouteParamsUserQuery.data?.user.name} +
diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/spec.ts b/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/spec.ts new file mode 100644 index 0000000000..b0c3277a41 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/optional[[id]]/spec.ts @@ -0,0 +1,11 @@ +import { test } from '@playwright/test'; +import { routes } from '../../../../../lib/utils/routes.js'; +import { expectToBe, goto } from '../../../../../lib/utils/testsHelper.js'; + +test.describe('query variables from route params', () => { + test('optional param in query, no value', async ({ page }) => { + await goto(page, routes.Plugin_query_inferInput_optional); + + await expectToBe(page, 'Bruce Willis'); + }); +}); diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/+page.gql b/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/+page.gql new file mode 100644 index 0000000000..91ea511e68 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/+page.gql @@ -0,0 +1,5 @@ +query RouteParamsUserQuery($snapshot: String!, $id: ID!) { + user(id: $id, snapshot: $snapshot) { + name + } +} diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/+page.svelte b/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/+page.svelte new file mode 100644 index 0000000000..72ebe2bd70 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/+page.svelte @@ -0,0 +1,11 @@ + + +
+ {$RouteParamsUserQuery.data?.user.name} +
diff --git a/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/spec.ts b/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/spec.ts new file mode 100644 index 0000000000..5316679737 --- /dev/null +++ b/e2e/sveltekit/src/routes/plugin/query/infer-input/user-[snapshot]-[id]/spec.ts @@ -0,0 +1,11 @@ +import { test } from '@playwright/test'; +import { routes } from '../../../../../lib/utils/routes.js'; +import { expectToBe, goto } from '../../../../../lib/utils/testsHelper.js'; + +test.describe('query variables from route params', () => { + test('happy path', async ({ page }) => { + await goto(page, routes.Plugin_query_inferInput_userRoute_params); + + await expectToBe(page, 'Bruce Willis'); + }); +}); diff --git a/e2e/sveltekit/src/routes/plugin/query/variables-error/+page.js b/e2e/sveltekit/src/routes/plugin/query/variables-error/+page.js index e518202817..5fd43bbf21 100644 --- a/e2e/sveltekit/src/routes/plugin/query/variables-error/+page.js +++ b/e2e/sveltekit/src/routes/plugin/query/variables-error/+page.js @@ -1,4 +1,5 @@ +import { error } from '@sveltejs/kit'; + export function _PreprocessorTestQueryErrorVariables() { - // @ts-ignore - return this.error(403, 'test'); + throw error(403, 'test'); } diff --git a/example/src/routes/[filter]/+page.ts b/example/src/routes/[filter]/+page.ts index b45c70ef45..d9747c572d 100644 --- a/example/src/routes/[filter]/+page.ts +++ b/example/src/routes/[filter]/+page.ts @@ -1,4 +1,5 @@ import { graphql } from '$houdini' +import { error } from '@sveltejs/kit' export const _houdini_load = graphql` query AllItems($completed: Boolean) @cache(policy: CacheOrNetwork) { @@ -32,7 +33,7 @@ export function _AllItemsVariables({ params }) { // make sure we recognize the value if (!['active', 'completed', 'all'].includes(params.filter)) { - return this.error(400, "filter must be one of 'active' or 'completed'") + throw error(400, "filter must be one of 'active' or 'completed'") } return { diff --git a/packages/houdini-plugin-svelte-global-stores/README.md b/packages/houdini-plugin-svelte-global-stores/README.md index ae8dc5dd14..1f3ebad49b 100644 --- a/packages/houdini-plugin-svelte-global-stores/README.md +++ b/packages/houdini-plugin-svelte-global-stores/README.md @@ -73,7 +73,7 @@ query MyAwesomeQuery { } ``` -```javascript +```typescript // src/routes/myRoute/+page.js import { GQL_MyAwesomeQuery } from '$houdini' ``` @@ -117,7 +117,7 @@ Fragments stores can be created from your external documents by using the `.get` Using a query store inside of an endpoint looks very similar to the `load` function: just pass the event you are handed in your route function: -```javascript +```typescript import { GQL_MyQuery } from '$houdini' export async function get(event) { diff --git a/packages/houdini-plugin-svelte-global-stores/src/plugin/codegen/stores/fragment.test.ts b/packages/houdini-plugin-svelte-global-stores/src/plugin/codegen/stores/fragment.test.ts index 0537330851..8b275426fa 100644 --- a/packages/houdini-plugin-svelte-global-stores/src/plugin/codegen/stores/fragment.test.ts +++ b/packages/houdini-plugin-svelte-global-stores/src/plugin/codegen/stores/fragment.test.ts @@ -49,6 +49,10 @@ test('global fragment type', async function () { ) expect(contents).toMatchInlineSnapshot( - '"import { TestFragment1Store } from \'../../houdini-svelte/stores\'\\n\\nexport const GQL_TestFragment1: TestFragment1Store"' + ` + "import { TestFragment1Store } from '../../houdini-svelte/stores' + + export const GQL_TestFragment1: TestFragment1Store" + ` ) }) diff --git a/packages/houdini-svelte/src/plugin/codegen/routes/index.ts b/packages/houdini-svelte/src/plugin/codegen/routes/index.ts index df2838cfed..40925687f0 100644 --- a/packages/houdini-svelte/src/plugin/codegen/routes/index.ts +++ b/packages/houdini-svelte/src/plugin/codegen/routes/index.ts @@ -9,6 +9,7 @@ import { walk_routes, } from '../../kit' import { houdini_after_load_fn, houdini_before_load_fn, houdini_on_error_fn } from '../../naming' +import { route_params } from '../../routing' export default async function svelteKitGenerator( framework: Framework, @@ -29,7 +30,7 @@ export default async function svelteKitGenerator( layoutExports, pageExports, }) { - //remove testing later + // remove testing later const relativePath = path.relative(config.routesDir, dirpath) const target = path.join(type_route_dir(config), relativePath, config.typeRootFile) @@ -78,11 +79,13 @@ export default async function svelteKitGenerator( const layout_append_VariablesFunction = append_VariablesFunction( 'Layout', + dirpath, config, uniqueLayoutQueries ) const page_append_VariablesFunction = append_VariablesFunction( 'Page', + dirpath, config, uniquePageQueries ) @@ -115,7 +118,13 @@ export default async function svelteKitGenerator( //name our sections let typeImports = splitString[0] - let utilityTypes = splitString[1] + let utilityTypes = + splitString[1] + + ` + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null + } + ` let typeExports = splitString[2] // lots of comparisons but helpful to prevent unnecessary imports @@ -256,9 +265,13 @@ function getTypeImports( function append_VariablesFunction( type: `Page` | `Layout`, + filepath: string, config: Config, queries: OperationDefinitionNode[] ) { + const { params } = route_params(filepath) + const garunteed_args = params.filter((param) => !param.optional).map((param) => param.name) + return queries .map((query) => { const name = query.name!.value @@ -267,9 +280,25 @@ function append_VariablesFunction( return '' } + // if a garunteed arg matches one of the args of the query, its not required + // regardless of what the $input type says + const make_optional: string[] = [] + for (const def of query.variableDefinitions) { + if (garunteed_args.includes(def.variable.name.value)) { + make_optional.push(`'${def.variable.name.value}'`) + } + } + + // build up the input type + let input_type = `${name}$input` + if (make_optional.length > 0) { + input_type = `MakeOptional<${input_type}, ${make_optional.join(' | ')}>` + } + + // define the variable function return `\nexport type ${config.variableFunctionName( name - )} = VariableFunction<${type}Params, ${name}$input>;` + )} = VariableFunction<${type}Params, ${input_type}>;` }) .join('\n') } diff --git a/packages/houdini-svelte/src/plugin/codegen/routes/kit.test.ts b/packages/houdini-svelte/src/plugin/codegen/routes/kit.test.ts index 02fedbea15..8ceed1d3b4 100644 --- a/packages/houdini-svelte/src/plugin/codegen/routes/kit.test.ts +++ b/packages/houdini-svelte/src/plugin/codegen/routes/kit.test.ts @@ -113,6 +113,11 @@ test('generates types for inline layout queries', async function () { type LayoutParams = RouteParams & {}; type LayoutParentData = EnsureDefined<{}>; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + export type LayoutServerData = null; export type LayoutLoad = OutputDataShape> = Kit.Load; export type LayoutLoadEvent = Parameters[0]; @@ -193,6 +198,11 @@ test('generates types for inline page queries', async function () { } & U : never; type PageParentData = EnsureDefined; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + type PageParams = PageLoadEvent["params"]; export type PageServerData = null; export type PageLoad = OutputDataShape> = Kit.Load; @@ -210,8 +220,8 @@ test('generates types for layout queries', async function () { [config.routesDir]: { myProfile: { '+layout.gql': ` -query MyLayoutQuery { - viewer { +query MyLayoutQuery { + viewer { id } } @@ -272,6 +282,11 @@ query MyLayoutQuery { type LayoutParams = RouteParams & {}; type LayoutParentData = EnsureDefined<{}>; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + export type LayoutServerData = null; export type LayoutLoad = OutputDataShape> = Kit.Load; export type LayoutLoadEvent = Parameters[0]; @@ -350,6 +365,11 @@ query MyPageQuery { } & U : never; type PageParentData = EnsureDefined; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + type PageParams = PageLoadEvent["params"]; export type PageServerData = null; export type PageLoad = OutputDataShape> = Kit.Load; @@ -448,6 +468,11 @@ test('generates types for layout onError', async function () { type LayoutParams = RouteParams & {}; type LayoutParentData = EnsureDefined<{}>; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + export type LayoutServerData = null; export type LayoutLoad = OutputDataShape> = Kit.Load; export type LayoutLoadEvent = Parameters[0]; @@ -560,6 +585,11 @@ test('generates types for page onError', async function () { } & U : never; type PageParentData = EnsureDefined; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + type PageParams = PageLoadEvent["params"]; export type PageServerData = null; export type PageLoad = OutputDataShape> = Kit.Load; @@ -674,6 +704,11 @@ test('generates types for layout beforeLoad', async function () { type LayoutParams = RouteParams & {}; type LayoutParentData = EnsureDefined<{}>; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + export type LayoutServerData = null; export type LayoutLoad = OutputDataShape> = Kit.Load; export type LayoutLoadEvent = Parameters[0]; @@ -780,6 +815,11 @@ test('generates types for page beforeLoad', async function () { } & U : never; type PageParentData = EnsureDefined; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + type PageParams = PageLoadEvent["params"]; export type PageServerData = null; export type PageLoad = OutputDataShape> = Kit.Load; @@ -888,6 +928,11 @@ test('generates types for layout afterLoad', async function () { type LayoutParams = RouteParams & {}; type LayoutParentData = EnsureDefined<{}>; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + export type LayoutServerData = null; export type LayoutLoad = OutputDataShape> = Kit.Load; export type LayoutLoadEvent = Parameters[0]; @@ -1005,6 +1050,11 @@ test('generates types for page afterLoad', async function () { } & U : never; type PageParentData = EnsureDefined; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + type PageParams = PageLoadEvent["params"]; export type PageServerData = null; export type PageLoad = OutputDataShape> = Kit.Load; @@ -1035,3 +1085,91 @@ test('generates types for page afterLoad', async function () { export type MyPageLoad1QueryVariables = VariableFunction; `) }) + +test('Marks required query arguments as optional if the url param provides it', async function () { + // create the mock filesystem + await fs.mock({ + [path.join(config.routesDir, '[userID]', '+layout.js')]: ` + import { graphql } from '$houdini' + + export const _houdini_load = graphql\`query MyPageLoad1Query($userID: ID!) { + viewer(id: $userID) { + id + } + }\` + `, + }) + + await fs.mock({ + [path.join(config.projectRoot, '.svelte-kit')]: { + types: { + src: { + routes: { + '[userID]': { + '$types.d.ts': default_layout_types, + }, + }, + }, + }, + }, + }) + + // execute the generator + await generate({ config, documents: [], framework: 'kit', plugin_root }) + + // load the contents of the file + const queryContents = await fs.readFile( + path.join(type_route_dir(config), '[userID]', '$houdini.d.ts') + ) + expect(queryContents).toBeTruthy() + const parsedQuery = (await parseJS(queryContents!))?.script + + // verify contents + expect(parsedQuery).toMatchInlineSnapshot(` + import type * as Kit from "@sveltejs/kit"; + import type { VariableFunction } from "../../../../plugins/houdini-svelte/runtime/types"; + import { MyPageLoad1Query$result, MyPageLoad1Query$input } from "../../../../artifacts/MyPageLoad1Query"; + import { MyPageLoad1QueryStore } from "../../../../plugins/houdini-svelte/stores/MyPageLoad1Query"; + + type Expand = T extends infer O ? { + [K in keyof O]: O[K]; + } : never; + + type RouteParams = {}; + type MaybeWithVoid = {} extends T ? T | void : T; + + export type RequiredKeys = { + [K in keyof T]?: {} extends { + [P in K]: T[K]; + } ? never : K; + }[keyof T]; + + type OutputDataShape = MaybeWithVoid> & Partial> & Record>; + type EnsureDefined = T extends null | undefined ? {} : T; + + type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { + [P in Exclude]?: never; + } & U : never; + + type LayoutParams = RouteParams & {}; + type LayoutParentData = EnsureDefined<{}>; + + type MakeOptional = Omit & { + [Key in Keys]?: Target[Key] | undefined | null; + }; + + export type LayoutServerData = null; + export type LayoutLoad = OutputDataShape> = Kit.Load; + export type LayoutLoadEvent = Parameters[0]; + + export type LayoutData = Expand> & OptionalUnion>>> & { + MyPageLoad1Query: MyPageLoad1QueryStore; + }>; + + type LoadInput = { + MyPageLoad1Query: MyPageLoad1Query$input; + }; + + export type MyPageLoad1QueryVariables = VariableFunction>; + `) +}) diff --git a/packages/houdini-svelte/src/plugin/routing.ts b/packages/houdini-svelte/src/plugin/routing.ts new file mode 100644 index 0000000000..8a4a632193 --- /dev/null +++ b/packages/houdini-svelte/src/plugin/routing.ts @@ -0,0 +1,224 @@ +/** + * This file is copied from the SvelteKit source code under the MIT license found at the bottom of the file + */ +const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/ + +export type RouteParam = { + name: string + matcher: string + optional: boolean + rest: boolean + chained: boolean +} + +export interface ParamMatcher { + (param: string): boolean +} + +/** + * Creates the regex pattern, extracts parameter names, and generates types for a route + */ +export function route_params(id: string) { + const params: RouteParam[] = [] + + const pattern = + id === '/' + ? /^\/$/ + : new RegExp( + `^${get_route_segments(id) + .map((segment) => { + // special case — /[...rest]/ could contain zero segments + const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment) + if (rest_match) { + params.push({ + name: rest_match[1], + matcher: rest_match[2], + optional: false, + rest: true, + chained: true, + }) + return '(?:/(.*))?' + } + // special case — /[[optional]]/ could contain zero segments + const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(segment) + if (optional_match) { + params.push({ + name: optional_match[1], + matcher: optional_match[2], + optional: true, + rest: false, + chained: true, + }) + return '(?:/([^/]+))?' + } + + if (!segment) { + return + } + + const parts = segment.split(/\[(.+?)\](?!\])/) + const result = parts + .map((content, i) => { + if (i % 2) { + if (content.startsWith('x+')) { + return escape( + String.fromCharCode(parseInt(content.slice(2), 16)) + ) + } + + if (content.startsWith('u+')) { + return escape( + String.fromCharCode( + ...content + .slice(2) + .split('-') + .map((code) => parseInt(code, 16)) + ) + ) + } + + const match = param_pattern.exec(content) + if (!match) { + throw new Error( + `Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.` + ) + } + + const [, is_optional, is_rest, name, matcher] = match + // It's assumed that the following invalid route id cases are already checked + // - unbalanced brackets + // - optional param following rest param + + params.push({ + name, + matcher, + optional: !!is_optional, + rest: !!is_rest, + chained: is_rest ? i === 1 && parts[0] === '' : false, + }) + return is_rest + ? '(.*?)' + : is_optional + ? '([^/]*)?' + : '([^/]+?)' + } + + return escape(content) + }) + .join('') + + return '/' + result + }) + .join('')}/?$` + ) + + return { pattern, params } +} + +/** + * Returns `false` for `(group)` segments + */ +function affects_path(segment: string) { + return !/^\([^)]+\)$/.test(segment) +} + +/** + * Splits a route id into its segments, removing segments that + * don't affect the path (i.e. groups). The root route is represented by `/` + * and will be returned as `['']`. + */ +export function get_route_segments(route: string) { + return route.slice(1).split('/').filter(affects_path) +} + +export function exec( + match: RegExpMatchArray, + params: RouteParam[], + matchers: Record +) { + const result: Record = {} + + const values = match.slice(1) + + let buffered = '' + + for (let i = 0; i < params.length; i += 1) { + const param = params[i] + let value = values[i] + + if (param.chained && param.rest && buffered) { + // in the `[[lang=lang]]/[...rest]` case, if `lang` didn't + // match, we roll it over into the rest value + value = value ? buffered + '/' + value : buffered + } + + buffered = '' + + if (value === undefined) { + // if `value` is undefined, it means this is + // an optional or rest parameter + if (param.rest) result[param.name] = '' + } else { + if (param.matcher && !matchers[param.matcher](value)) { + // in the `/[[a=b]]/[[c=d]]` case, if the value didn't satisfy the `b` matcher, + // try again with the next segment by shifting values rightwards + if (param.optional && param.chained) { + // @ts-expect-error TypeScript is... wrong + let j = values.indexOf(undefined, i) + + if (j === -1) { + // we can't shift values any further, so hang on to this value + // so it can be rolled into a subsequent `[...rest]` param + const next = params[i + 1] + if (next?.rest && next.chained) { + buffered = value + } else { + return + } + } + + while (j >= i) { + values[j] = values[j - 1] + j -= 1 + } + + continue + } + + // otherwise, if the matcher returns `false`, the route did not match + return + } + + result[param.name] = value + } + } + + if (buffered) return + return result +} + +function escape(str: string) { + return ( + str + .normalize() + // escape [ and ] before escaping other characters, since they are used in the replacements + .replace(/[[\]]/g, '\\$&') + // replace %, /, ? and # with their encoded versions because decode_pathname leaves them untouched + .replace(/%/g, '%25') + .replace(/\//g, '%2[Ff]') + .replace(/\?/g, '%3[Ff]') + .replace(/#/g, '%23') + // escape characters that have special meaning in regex + .replace(/[.*+?^${}()|\\]/g, '\\$&') + ) +} + +/** +Copyright (c) 2020 [these people](https://github.com/sveltejs/kit/graphs/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/packages/houdini-svelte/src/plugin/transforms/query.test.ts b/packages/houdini-svelte/src/plugin/transforms/componentQuery.test.ts similarity index 100% rename from packages/houdini-svelte/src/plugin/transforms/query.test.ts rename to packages/houdini-svelte/src/plugin/transforms/componentQuery.test.ts diff --git a/packages/houdini-svelte/src/plugin/transforms/query.ts b/packages/houdini-svelte/src/plugin/transforms/componentQuery.ts similarity index 84% rename from packages/houdini-svelte/src/plugin/transforms/query.ts rename to packages/houdini-svelte/src/plugin/transforms/componentQuery.ts index 37a24099af..3486b360a8 100644 --- a/packages/houdini-svelte/src/plugin/transforms/query.ts +++ b/packages/houdini-svelte/src/plugin/transforms/componentQuery.ts @@ -1,7 +1,7 @@ import { logYellow } from '@kitql/helper' import { ExpressionKind } from 'ast-types/lib/gen/kinds' import * as graphql from 'graphql' -import { Config, operation_requires_variables, find_graphql, Script, formatErrors } from 'houdini' +import { Config, find_graphql, Script, formatErrors } from 'houdini' import { find_exported_fn, find_insert_index, ensure_imports, TransformPage } from 'houdini/vite' import * as recast from 'recast' @@ -13,8 +13,6 @@ const AST = recast.types.builders type ExportNamedDeclaration = recast.types.namedTypes.ExportNamedDeclaration type VariableDeclaration = recast.types.namedTypes.VariableDeclaration -type Identifier = recast.types.namedTypes.Identifier -type Statement = recast.types.namedTypes.Statement export default async function QueryProcessor(config: Config, page: SvelteTransformPage) { // only consider consider components in this processor @@ -78,15 +76,15 @@ export default async function QueryProcessor(config: Config, page: SvelteTransfo const factory = ensure_imports({ script: page.script, config: page.config, - import: [`${query.name}Store`], - sourceModule: store_import_path({ config, name: query.name }), + import: [`${query.name!.value}Store`], + sourceModule: store_import_path({ config, name: query.name!.value }), }).ids[0] page.script.body.splice( find_insert_index(page.script), 0, AST.variableDeclaration('const', [ - AST.variableDeclarator(store_id(query.name), AST.newExpression(factory, [])), + AST.variableDeclarator(store_id(query.name!.value), AST.newExpression(factory, [])), ]) ) } @@ -96,11 +94,15 @@ export default async function QueryProcessor(config: Config, page: SvelteTransfo // a variable to hold the query input ...queries.flatMap((query) => { // if the query does not have variables, just define something local - const variable_fn = query_variable_fn(query.name) + const variable_fn = query_variable_fn(query.name!.value) const has_variables = find_exported_fn(page.script.body, variable_fn) // If we need the variables function, but it's missing... let's display a message - if (query.variables && has_variables === null) { + if ( + query.variableDefinitions && + query.variableDefinitions?.length > 0 && + has_variables === null + ) { formatErrors({ filepath: page.filepath, message: `Could not find required variable function: ${logYellow( @@ -122,7 +124,7 @@ export default async function QueryProcessor(config: Config, page: SvelteTransfo AST.objectProperty( AST.identifier('artifact'), AST.memberExpression( - store_id(query.name), + store_id(query.name!.value), AST.identifier('artifact') ) ), @@ -167,21 +169,21 @@ export default async function QueryProcessor(config: Config, page: SvelteTransfo ), [ AST.arrowFunctionExpression( - [local_input_id(query.name)], + [local_input_id(query.name!.value)], // load the query AST.logicalExpression( '&&', AST.identifier('isBrowser'), AST.callExpression( AST.memberExpression( - store_id(query.name), + store_id(query.name!.value), AST.identifier('fetch') ), [ AST.objectExpression([ AST.objectProperty( AST.identifier('variables'), - local_input_id(query.name) + local_input_id(query.name!.value) ), ]), ] @@ -208,10 +210,7 @@ export async function find_inline_queries( } // build up a list of the queries we run into - const queries: { - name: string - variables: boolean - }[] = [] + const queries: LoadTarget[] = [] // look for inline queries await find_graphql(page.config, parsed, { @@ -236,30 +235,17 @@ export async function find_inline_queries( // if the graphql tag was inside of a call expression, we need to assume that it's a // part of an inline document. if the operation is a query, we need to add it to the list // so that the load function can have the correct contents - const { parsedDocument, parent } = tag + const { parsedDocument } = tag const operation = page.config.extractQueryDefinition(parsedDocument) - queries.push({ - name: operation.name!.value, - // an operation requires variables if there is any non-null variable that doesn't have a default value - variables: operation_requires_variables(operation), - }) + queries.push(operation) tag.node.replaceWith(store_id(operation.name!.value)) }, }) - return queries.map((query) => { - return { - store_id: AST.identifier(''), - name: query.name, - variables: query.variables, - } - }) + return queries } -export type LoadTarget = { - name: string - variables: boolean -} +export type LoadTarget = graphql.OperationDefinitionNode const local_input_id = (name: string) => AST.identifier(`_${name}_Input`) diff --git a/packages/houdini-svelte/src/plugin/transforms/index.ts b/packages/houdini-svelte/src/plugin/transforms/index.ts index 6dbf022938..84298a61be 100644 --- a/packages/houdini-svelte/src/plugin/transforms/index.ts +++ b/packages/houdini-svelte/src/plugin/transforms/index.ts @@ -4,8 +4,8 @@ import * as recast from 'recast' import { ParsedFile, parseSvelte } from '../extract' import { Framework } from '../kit' +import query from './componentQuery' import kit from './kit' -import query from './query' import reactive from './reactive' import tags from './tags' import { SvelteTransformPage } from './types' diff --git a/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts b/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts index b427491e31..c19b4a2501 100644 --- a/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts +++ b/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts @@ -273,6 +273,7 @@ describe('kit route processor', function () { import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; import _TestQueryArtifact from "$houdini/artifacts/TestQuery"; export async function _TestQueryVariables(page) { @@ -281,17 +282,23 @@ describe('kit route processor', function () { }; } + async function __houdini___TestQueryVariables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _TestQueryVariables(event), + artifact: _TestQueryArtifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); const houdiniConfig = await getCurrentConfig(); const promises = []; const inputs = {}; - - inputs["TestQuery"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _TestQueryVariables, - "artifact": _TestQueryArtifact - }); + inputs["TestQuery"] = await __houdini___TestQueryVariables(houdiniConfig, context); promises.push(load_TestQuery({ "variables": inputs["TestQuery"], @@ -404,17 +411,29 @@ describe('kit route processor', function () { import { MyQuery2Store } from "$houdini/plugins/houdini-svelte/stores/MyQuery2"; import { MyQuery1Store } from "$houdini/plugins/houdini-svelte/stores/MyQuery1"; import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery"; - import _MyQuery2Artifact from "$houdini/artifacts/MyQuery2"; import { load_MyQuery2 } from "$houdini/plugins/houdini-svelte/stores/MyQuery2"; import { load_MyQuery1 } from "$houdini/plugins/houdini-svelte/stores/MyQuery1"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import _MyQuery2Artifact from "$houdini/artifacts/MyQuery2"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; import _TestQueryArtifact from "$houdini/artifacts/TestQuery"; const store1 = new MyQuery1Store(); const store2 = new MyQuery2Store(); export function _MyQuery2Variables() {} export const _houdini_load = [store1, store2]; + async function __houdini___MyQuery2Variables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _MyQuery2Variables(event), + artifact: _MyQuery2Artifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); const houdiniConfig = await getCurrentConfig(); @@ -428,11 +447,7 @@ describe('kit route processor', function () { "blocking": false })); - inputs["MyQuery2"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _MyQuery2Variables, - "artifact": _MyQuery2Artifact - }); + inputs["MyQuery2"] = await __houdini___MyQuery2Variables(houdiniConfig, context); promises.push(load_MyQuery2({ "variables": inputs["MyQuery2"], @@ -633,6 +648,7 @@ test('beforeLoad hook', async function () { import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; import _TestQueryArtifact from "$houdini/artifacts/TestQuery"; export async function _houdini_beforeLoad() { @@ -645,6 +661,17 @@ test('beforeLoad hook', async function () { }; } + async function __houdini___TestQueryVariables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _TestQueryVariables(event), + artifact: _TestQueryArtifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); @@ -656,12 +683,7 @@ test('beforeLoad hook', async function () { const houdiniConfig = await getCurrentConfig(); const promises = []; const inputs = {}; - - inputs["TestQuery"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _TestQueryVariables, - "artifact": _TestQueryArtifact - }); + inputs["TestQuery"] = await __houdini___TestQueryVariables(houdiniConfig, context); promises.push(load_TestQuery({ "variables": inputs["TestQuery"], @@ -809,6 +831,7 @@ test('afterLoad hook', async function () { import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; import _TestQueryArtifact from "$houdini/artifacts/TestQuery"; export async function _houdini_afterLoad() { @@ -821,17 +844,23 @@ test('afterLoad hook', async function () { }; } + async function __houdini___TestQueryVariables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _TestQueryVariables(event), + artifact: _TestQueryArtifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); const houdiniConfig = await getCurrentConfig(); const promises = []; const inputs = {}; - - inputs["TestQuery"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _TestQueryVariables, - "artifact": _TestQueryArtifact - }); + inputs["TestQuery"] = await __houdini___TestQueryVariables(houdiniConfig, context); promises.push(load_TestQuery({ "variables": inputs["TestQuery"], @@ -991,6 +1020,7 @@ test('both beforeLoad and afterLoad hooks', async function () { import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; import _TestQueryArtifact from "$houdini/artifacts/TestQuery"; export async function _houdini_beforeLoad() { @@ -1007,6 +1037,17 @@ test('both beforeLoad and afterLoad hooks', async function () { }; } + async function __houdini___TestQueryVariables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _TestQueryVariables(event), + artifact: _TestQueryArtifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); @@ -1018,12 +1059,7 @@ test('both beforeLoad and afterLoad hooks', async function () { const houdiniConfig = await getCurrentConfig(); const promises = []; const inputs = {}; - - inputs["TestQuery"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _TestQueryVariables, - "artifact": _TestQueryArtifact - }); + inputs["TestQuery"] = await __houdini___TestQueryVariables(houdiniConfig, context); promises.push(load_TestQuery({ "variables": inputs["TestQuery"], @@ -1080,16 +1116,28 @@ test('layout loads', async function () { expect(route.layout_script).toMatchInlineSnapshot(` import { MyQuery2Store } from "$houdini/plugins/houdini-svelte/stores/MyQuery2"; import { MyQuery1Store } from "$houdini/plugins/houdini-svelte/stores/MyQuery1"; - import _MyQuery2Artifact from "$houdini/artifacts/MyQuery2"; import { load_MyQuery2 } from "$houdini/plugins/houdini-svelte/stores/MyQuery2"; import { load_MyQuery1 } from "$houdini/plugins/houdini-svelte/stores/MyQuery1"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import _MyQuery2Artifact from "$houdini/artifacts/MyQuery2"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; const store1 = new MyQuery1Store(); const store2 = new MyQuery2Store(); export function _MyQuery2Variables() {} export const _houdini_load = [store1, store2]; + async function __houdini___MyQuery2Variables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _MyQuery2Variables(event), + artifact: _MyQuery2Artifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); const houdiniConfig = await getCurrentConfig(); @@ -1103,11 +1151,7 @@ test('layout loads', async function () { "blocking": false })); - inputs["MyQuery2"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _MyQuery2Variables, - "artifact": _MyQuery2Artifact - }); + inputs["MyQuery2"] = await __houdini___MyQuery2Variables(houdiniConfig, context); promises.push(load_MyQuery2({ "variables": inputs["MyQuery2"], @@ -1307,6 +1351,7 @@ test('onError hook', async function () { import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery"; import { getCurrentConfig } from "$houdini/runtime/lib/config"; import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; import _TestQueryArtifact from "$houdini/artifacts/TestQuery"; export async function _houdini_onError() { @@ -1319,17 +1364,23 @@ test('onError hook', async function () { }; } + async function __houdini___TestQueryVariables(config, event) { + const result = {}; + + Object.assign(result, await marshalInputs({ + input: await _TestQueryVariables(event), + artifact: _TestQueryArtifact + })); + + return result; + } + export async function load(context) { const houdini_context = new RequestContext(context); const houdiniConfig = await getCurrentConfig(); const promises = []; const inputs = {}; - - inputs["TestQuery"] = await houdini_context.computeInput({ - "config": houdiniConfig, - "variableFunction": _TestQueryVariables, - "artifact": _TestQueryArtifact - }); + inputs["TestQuery"] = await __houdini___TestQueryVariables(houdiniConfig, context); promises.push(load_TestQuery({ "variables": inputs["TestQuery"], @@ -1358,6 +1409,143 @@ test('onError hook', async function () { `) }) +test('route params, no variable function', async function () { + const route = await route_test({ + route_path: '[userID]/profile', + script: ` + export const _houdini_load = graphql(\` + query UserInfo($userID: ID!) { + user(id: $userID) { + firstName + } + } + \`) + `, + }) + + expect(route.script).toMatchInlineSnapshot(` + import { UserInfoStore } from "$houdini/plugins/houdini-svelte/stores/UserInfo"; + import { load_UserInfo } from "$houdini/plugins/houdini-svelte/stores/UserInfo"; + import { getCurrentConfig } from "$houdini/runtime/lib/config"; + import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; + export const _houdini_load = new UserInfoStore(); + + async function __houdini___UserInfoVariables(config, event) { + const result = { + userID: parseScalar(config, "ID", event.params.userID) + }; + + return result; + } + + export async function load(context) { + const houdini_context = new RequestContext(context); + const houdiniConfig = await getCurrentConfig(); + const promises = []; + const inputs = {}; + inputs["UserInfo"] = await __houdini___UserInfoVariables(houdiniConfig, context); + + promises.push(load_UserInfo({ + "variables": inputs["UserInfo"], + "event": context, + "blocking": false + })); + + let result = {}; + + try { + result = Object.assign({}, ...(await Promise.all(promises))); + } catch (err) { + throw err; + } + + return { + ...houdini_context.returnValue, + ...result + }; + } + `) +}) + +test('route params with variable function', async function () { + const route = await route_test({ + route_path: '[userID]/profile', + script: ` + export const _houdini_load = graphql(\` + query UserInfo($userID: ID!) { + user(id: $userID) { + firstName + } + } + \`) + + export function _UserInfoVariables(event) { + return { + userID: '1' + } + } + `, + }) + + expect(route.script).toMatchInlineSnapshot(` + import { UserInfoStore } from "$houdini/plugins/houdini-svelte/stores/UserInfo"; + import { load_UserInfo } from "$houdini/plugins/houdini-svelte/stores/UserInfo"; + import { getCurrentConfig } from "$houdini/runtime/lib/config"; + import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session"; + import _UserInfoArtifact from "$houdini/artifacts/UserInfo"; + import { parseScalar, marshalInputs } from "$houdini/runtime/lib/scalars"; + export const _houdini_load = new UserInfoStore(); + + export function _UserInfoVariables(event) { + return { + userID: "1" + }; + } + + async function __houdini___UserInfoVariables(config, event) { + const result = { + userID: parseScalar(config, "ID", event.params.userID) + }; + + Object.assign(result, await marshalInputs({ + input: await _UserInfoVariables(event), + artifact: _UserInfoArtifact + })); + + return result; + } + + export async function load(context) { + const houdini_context = new RequestContext(context); + const houdiniConfig = await getCurrentConfig(); + const promises = []; + const inputs = {}; + inputs["UserInfo"] = await __houdini___UserInfoVariables(houdiniConfig, context); + + promises.push(load_UserInfo({ + "variables": inputs["UserInfo"], + "event": context, + "blocking": false + })); + + let result = {}; + + try { + result = Object.assign({}, ...(await Promise.all(promises))); + } catch (err) { + throw err; + } + + return { + ...houdini_context.returnValue, + ...result + }; + } + `) +}) + +test.todo('overlapping query parameters') test('existing loads with parens', async function () { const route = await route_test({ script: ` diff --git a/packages/houdini-svelte/src/plugin/transforms/kit/load.ts b/packages/houdini-svelte/src/plugin/transforms/kit/load.ts index 8c0087aee3..a38594be9c 100644 --- a/packages/houdini-svelte/src/plugin/transforms/kit/load.ts +++ b/packages/houdini-svelte/src/plugin/transforms/kit/load.ts @@ -2,7 +2,7 @@ import { logYellow } from '@kitql/helper' import type { IdentifierKind, StatementKind } from 'ast-types/lib/gen/kinds' import type { namedTypes } from 'ast-types/lib/gen/namedTypes' import * as graphql from 'graphql' -import { formatErrors, fs, operation_requires_variables } from 'houdini' +import { formatErrors, fs, TypeWrapper, unwrapType } from 'houdini' import { artifact_import, ensure_imports, find_insert_index } from 'houdini/vite' import * as recast from 'recast' @@ -22,11 +22,11 @@ import { import { houdini_after_load_fn, houdini_before_load_fn, - houdini_load_fn, houdini_on_error_fn, query_variable_fn, } from '../../naming' -import { find_inline_queries, LoadTarget } from '../query' +import { route_params, RouteParam } from '../../routing' +import { find_inline_queries, LoadTarget } from '../componentQuery' import { SvelteTransformPage } from '../types' const AST = recast.types.builders @@ -71,13 +71,9 @@ export default async function kit_load_generator(page: SvelteTransformPage) { find_page_info(page), ]) - const houdini_load_queries = [] + const houdini_load_queries: LoadTarget[] = [] for (const [i, target] of (page_info.houdini_load ?? []).entries()) { - houdini_load_queries.push({ - name: target.name!.value, - variables: operation_requires_variables(target), - store_id: AST.memberExpression(AST.identifier(houdini_load_fn), AST.literal(i)), - }) + houdini_load_queries.push(target) } // add the load functions @@ -154,18 +150,19 @@ function add_load({ // let's verify that we have all of the variable functions we need before we mutate anything let invalid = false for (const query of queries) { - const variable_fn = query_variable_fn(query.name) - // if the page doesn't export a function with the correct name, something is wrong - if (!page_info.exports.includes(variable_fn) && query.variables) { - // tell them we're missing something - formatErrors({ - filepath: page.filepath, - message: `Could not find required variable function: ${logYellow( - variable_fn - )}. maybe its not exported? `, - }) - - // don't go any further + // if the query doesn't have any variables, there's nothing to do + if (!query.variableDefinitions || query.variableDefinitions.length === 0) { + continue + } + + // add the internal variable function to the page and we'll call that when generating the load + const variable_fn = query_variable_fn(query.name!.value) + try { + page.script.body.push( + variable_function_for_query(page, query, page_info.exports.includes(variable_fn)) + ) + } catch (e) { + formatErrors(e) invalid = true } } @@ -258,40 +255,21 @@ function add_load({ const { ids } = ensure_imports({ script: page.script, config: page.config, - import: [`load_${query.name}`], - sourceModule: store_import_path({ config: page.config, name: query.name }), + import: [`load_${query.name!.value}`], + sourceModule: store_import_path({ config: page.config, name: query.name!.value }), }) const load_fn = ids[0] - const variables = page_info.exports.includes(query_variable_fn(query.name)) - ? AST.awaitExpression( - AST.callExpression( - AST.memberExpression(request_context, AST.identifier('computeInput')), - [ - AST.objectExpression([ - AST.objectProperty( - AST.literal('config'), - AST.identifier('houdiniConfig') - ), - AST.objectProperty( - AST.literal('variableFunction'), - AST.identifier(query_variable_fn(query.name)) - ), - AST.objectProperty( - AST.literal('artifact'), - artifact_import({ - config: page.config, - script: page.script, - page, - artifact: { name: query.name }, - }).id - ), - ]), - ] - ) - ) - : AST.objectExpression([]) + const variables = + (query.variableDefinitions?.length ?? 0) > 0 + ? AST.awaitExpression( + AST.callExpression(AST.identifier(__variable_fn_name(query.name!.value)), [ + AST.identifier('houdiniConfig'), + AST.identifier('context'), + ]) + ) + : AST.objectExpression([]) preload_fn.body.body.splice( insert_index++, @@ -299,7 +277,7 @@ function add_load({ AST.expressionStatement( AST.assignmentExpression( '=', - AST.memberExpression(input_obj, AST.literal(query.name)), + AST.memberExpression(input_obj, AST.literal(query.name!.value)), variables ) ) @@ -315,7 +293,7 @@ function add_load({ AST.objectExpression([ AST.objectProperty( AST.literal('variables'), - AST.memberExpression(input_obj, AST.literal(query.name)) + AST.memberExpression(input_obj, AST.literal(query.name!.value)) ), AST.objectProperty(AST.literal('event'), AST.identifier('context')), AST.objectProperty( @@ -452,10 +430,7 @@ async function find_special_query( return null } - return { - name: definition.name!.value, - variables: operation_requires_variables(definition), - } + return definition } function load_hook_statements( @@ -517,3 +492,151 @@ function unexported_data_error(filepath: string) { description: `This is not allowed in a route since it would conflict with Houdini's generated code`, } } + +function variable_function_for_query( + page: SvelteTransformPage, + query: graphql.OperationDefinitionNode, + has_local: boolean +): namedTypes.FunctionDeclaration { + // there needs to be a local function if any of the required arguments for + // the query do not have a non-optional value from the url + + // find the paramters we are passed to from the URL + const params = route_params(page.filepath).params.reduce( + (acc, param) => ({ + ...acc, + [param.name]: param, + }), + {} as Record + ) + + // make sure that we have the utility to handle scalar args + ensure_imports({ + config: page.config, + script: page.script, + import: ['parseScalar', 'marshalInputs'], + sourceModule: '$houdini/runtime/lib/scalars', + }) + + // find any arguments that aren't optional but not given by the URL + const missing_args = [] + const has_args: Record = {} + for (const definition of query.variableDefinitions ?? []) { + const unwrapped = unwrapType(page.config, definition.type) + + // we need to remember the definition if + // the argument to the operation is non-null + // the url param doesn't exist or does exist but is optional + if ( + unwrapped.wrappers[unwrapped.wrappers.length - 1] === TypeWrapper.NonNull && + !definition.defaultValue && + (!params[definition.variable.name.value] || + params[definition.variable.name.value].optional) + ) { + missing_args.push(definition.variable.name) + } + + // if the query variable is a route param, add it to the specific pile + if (params[definition.variable.name.value]) { + has_args[definition.variable.name.value] = unwrapped.type.name + } + } + + // if there are missing args but no local function then we have a problem + if (missing_args.length > 0 && !has_local) { + throw { + filepath: page.filepath, + message: `Could not find required variable function: ${logYellow( + query_variable_fn(query.name!.value) + )}. maybe its not exported?`, + } + } + + // build up a function that mixes the appropriate url parameters with the + // value of the variable function + const fn_body: StatementKind[] = [ + // we'll collect everything in a local variable + AST.variableDeclaration('const', [ + AST.variableDeclarator( + AST.identifier('result'), + AST.objectExpression( + Object.entries(has_args).map(([arg, type]) => + AST.objectProperty( + AST.identifier(arg), + AST.callExpression(AST.identifier('parseScalar'), [ + AST.identifier('config'), + AST.stringLiteral(type), + AST.memberExpression( + AST.memberExpression( + AST.identifier('event'), + AST.identifier('params') + ), + AST.identifier(arg) + ), + ]) + ) + ) + ) + ), + ]), + ] + + // if there is a local function we need to call it and add the return value to + // the running object + if (has_local) { + fn_body.push( + AST.expressionStatement( + AST.callExpression( + AST.memberExpression(AST.identifier('Object'), AST.identifier('assign')), + [ + AST.identifier('result'), + AST.awaitExpression( + AST.callExpression(AST.identifier('marshalInputs'), [ + AST.objectExpression([ + AST.objectProperty( + AST.identifier('input'), + AST.awaitExpression( + AST.callExpression( + AST.identifier( + query_variable_fn(query.name!.value) + ), + [AST.identifier('event')] + ) + ) + ), + AST.objectProperty( + AST.identifier('artifact'), + artifact_import({ + config: page.config, + script: page.script, + page, + artifact: { name: query.name!.value }, + }).id + ), + ]), + ]) + ), + ] + ) + ) + ) + } + + // at the end of the function, return the result + fn_body.push(AST.returnStatement(AST.identifier('result'))) + + // build up the function declaration + const declaration = AST.functionDeclaration( + AST.identifier(__variable_fn_name(query.name!.value)), + [AST.identifier('config'), AST.identifier('event')], + AST.blockStatement(fn_body) + ) + declaration.async = true + + // we're done + return declaration +} + +function __variable_fn_name(name: string) { + return `__houdini__` + query_variable_fn(name) +} diff --git a/packages/houdini-svelte/src/runtime/session.ts b/packages/houdini-svelte/src/runtime/session.ts index 7df88c34df..cf5f2a53f2 100644 --- a/packages/houdini-svelte/src/runtime/session.ts +++ b/packages/houdini-svelte/src/runtime/session.ts @@ -1,12 +1,5 @@ -import { marshalInputs } from '$houdini/runtime/lib/scalars' -import { - MutationArtifact, - QueryArtifact, - QueryResult, - SubscriptionArtifact, -} from '$houdini/runtime/lib/types' +import { QueryResult } from '$houdini/runtime/lib/types' import { error, LoadEvent, redirect, RequestEvent } from '@sveltejs/kit' -import { GraphQLError } from 'graphql' import { get } from 'svelte/store' import { isBrowser } from './adapter' @@ -39,15 +32,6 @@ export class RequestContext { return fetch(input, init) } - graphqlErrors(payload: { errors?: GraphQLError[] }) { - // if we have a list of errors - if (payload.errors) { - return this.error(500, payload.errors.map(({ message }) => message).join('\n')) - } - - return this.error(500, 'Encountered invalid response: ' + JSON.stringify(payload)) - } - // This hook fires before executing any queries, it allows custom props to be passed to the component. async invokeLoadHook({ variant, @@ -101,20 +85,6 @@ export class RequestContext { this.returnValue = result } - - // compute the inputs for an operation should reflect the framework's conventions. - async computeInput({ - variableFunction, - artifact, - }: { - variableFunction: KitBeforeLoad - artifact: QueryArtifact | MutationArtifact | SubscriptionArtifact - }) { - // call the variable function to match the framework - let input = await variableFunction.call(this, this.loadEvent) - - return await marshalInputs({ artifact, input }) - } } type KitBeforeLoad = (ctx: BeforeLoadArgs) => Record | Promise> diff --git a/packages/houdini-svelte/src/test/index.ts b/packages/houdini-svelte/src/test/index.ts index dfacdfc880..0a9316594a 100644 --- a/packages/houdini-svelte/src/test/index.ts +++ b/packages/houdini-svelte/src/test/index.ts @@ -14,6 +14,7 @@ const schema = ` type Query { viewer: User + field(input: Int!): Int } type Mutation { @@ -66,6 +67,7 @@ export async function route_test({ layout_script = '', config: extra, framework = 'kit', + route_path = '', }: { component?: string script?: string @@ -75,6 +77,7 @@ export async function route_test({ layout_script?: string config?: Partial framework?: Framework + route_path?: string }): Promise<{ component: Script | null script: Script | null @@ -85,8 +88,8 @@ export async function route_test({ const config = await test_config({ schema, ...extra }) // scripts live in src/routes/+page.svelte - const page_path = path.join(process.cwd(), 'src/routes', '+page.svelte') - const layout_path = path.join(process.cwd(), 'src/routes', '+layout.svelte') + const page_path = path.join(process.cwd(), 'src/routes', route_path, '+page.svelte') + const layout_path = path.join(process.cwd(), 'src/routes', route_path, '+layout.svelte') const layout_script_path = route_data_path(config, layout_path) await fs.mkdirp(path.dirname(page_path)) diff --git a/packages/houdini/src/codegen/generators/artifacts/inputs.ts b/packages/houdini/src/codegen/generators/artifacts/inputs.ts index 430da31e49..7a8f927918 100644 --- a/packages/houdini/src/codegen/generators/artifacts/inputs.ts +++ b/packages/houdini/src/codegen/generators/artifacts/inputs.ts @@ -1,9 +1,9 @@ import * as graphql from 'graphql' import * as recast from 'recast' +import { unwrapType } from '../../../lib' import { Config } from '../../../lib/config' import { InputObject } from '../../../runtime/lib/types' -import { unwrapType } from '../../utils' const AST = recast.types.builders diff --git a/packages/houdini/src/codegen/generators/typescript/addReferencedInputTypes.ts b/packages/houdini/src/codegen/generators/typescript/addReferencedInputTypes.ts index b192c79f74..dcc94f5829 100644 --- a/packages/houdini/src/codegen/generators/typescript/addReferencedInputTypes.ts +++ b/packages/houdini/src/codegen/generators/typescript/addReferencedInputTypes.ts @@ -2,8 +2,7 @@ import type { StatementKind, TSPropertySignatureKind } from 'ast-types/lib/gen/k import * as graphql from 'graphql' import * as recast from 'recast' -import { Config, ensureImports, HoudiniError } from '../../../lib' -import { unwrapType } from '../../utils' +import { Config, ensureImports, HoudiniError, unwrapType } from '../../../lib' import { tsTypeReference } from './typeReference' const AST = recast.types.builders diff --git a/packages/houdini/src/codegen/generators/typescript/imperativeCache.ts b/packages/houdini/src/codegen/generators/typescript/imperativeCache.ts index aff6724991..441f4850df 100644 --- a/packages/houdini/src/codegen/generators/typescript/imperativeCache.ts +++ b/packages/houdini/src/codegen/generators/typescript/imperativeCache.ts @@ -9,8 +9,9 @@ import { path, keyFieldsForType, parentTypeFromAncestors, + TypeWrapper, + unwrapType, } from '../../../lib' -import { TypeWrapper, unwrapType } from '../../utils' import { addReferencedInputTypes } from './addReferencedInputTypes' import { tsTypeReference } from './typeReference' import { scalarPropertyValue } from './types' diff --git a/packages/houdini/src/codegen/generators/typescript/inlineType.ts b/packages/houdini/src/codegen/generators/typescript/inlineType.ts index e8ce2d047e..40f19cc658 100644 --- a/packages/houdini/src/codegen/generators/typescript/inlineType.ts +++ b/packages/houdini/src/codegen/generators/typescript/inlineType.ts @@ -2,8 +2,7 @@ import type { TSTypeKind, StatementKind } from 'ast-types/lib/gen/kinds' import * as graphql from 'graphql' import * as recast from 'recast' -import { Config, ensureImports, HoudiniError } from '../../../lib' -import { TypeWrapper, unwrapType } from '../../utils' +import { Config, ensureImports, HoudiniError, TypeWrapper, unwrapType } from '../../../lib' import { nullableField, readonlyProperty, scalarPropertyValue } from './types' const AST = recast.types.builders diff --git a/packages/houdini/src/codegen/generators/typescript/typeReference.ts b/packages/houdini/src/codegen/generators/typescript/typeReference.ts index 6ab89c7dd2..d8cb43ac7d 100644 --- a/packages/houdini/src/codegen/generators/typescript/typeReference.ts +++ b/packages/houdini/src/codegen/generators/typescript/typeReference.ts @@ -2,8 +2,7 @@ import type { TSTypeKind } from 'ast-types/lib/gen/kinds' import * as graphql from 'graphql' import * as recast from 'recast' -import { Config } from '../../../lib' -import { TypeWrapper, unwrapType } from '../../utils' +import { Config, TypeWrapper, unwrapType } from '../../../lib' import { nullableField, scalarPropertyValue } from './types' const AST = recast.types.builders diff --git a/packages/houdini/src/codegen/transforms/addID.ts b/packages/houdini/src/codegen/transforms/addID.ts index 8ae0046828..9f33261a52 100644 --- a/packages/houdini/src/codegen/transforms/addID.ts +++ b/packages/houdini/src/codegen/transforms/addID.ts @@ -1,7 +1,6 @@ import * as graphql from 'graphql' -import { Config, parentTypeFromAncestors, CollectedGraphQLDocument } from '../../lib' -import { unwrapType } from '../utils' +import { Config, parentTypeFromAncestors, CollectedGraphQLDocument, unwrapType } from '../../lib' // typename adds __typename to the selection set of any unions or interfaces export default async function addID( diff --git a/packages/houdini/src/codegen/transforms/list.ts b/packages/houdini/src/codegen/transforms/list.ts index 5f45c0e6f1..b99f0ad58a 100644 --- a/packages/houdini/src/codegen/transforms/list.ts +++ b/packages/houdini/src/codegen/transforms/list.ts @@ -7,9 +7,10 @@ import { HoudiniError, CollectedGraphQLDocument, siteURL, + TypeWrapper, + unwrapType, } from '../../lib' import { ArtifactKind } from '../../runtime/lib/types' -import { TypeWrapper, unwrapType } from '../utils' import { objectIdentificationSelection } from '../utils/objectIdentificationSelection' import { pageInfoSelection } from './paginate' diff --git a/packages/houdini/src/codegen/transforms/lists.test.ts b/packages/houdini/src/codegen/transforms/lists.test.ts index 8b389a7863..f9b7ddc558 100644 --- a/packages/houdini/src/codegen/transforms/lists.test.ts +++ b/packages/houdini/src/codegen/transforms/lists.test.ts @@ -467,7 +467,7 @@ test('cannot use list directive if id is not a valid field', async function () { nbError++ // @ts-ignore expect(error[0].description).toMatchInlineSnapshot( - '"@list on \\u001b[32mLegend\\u001b[37m\\u001b[0m as a configuration issue. Object identification missing: \\"\\u001b[33mid\\u001b[37m\\u001b[0m\\". Check \'Custom IDs\' if needed."' + '"@list on Legend as a configuration issue. Object identification missing: \\"id\\". Check \'Custom IDs\' if needed."' ) } expect(nbError).toBe(1) diff --git a/packages/houdini/src/codegen/transforms/paginate.ts b/packages/houdini/src/codegen/transforms/paginate.ts index 839db085ca..c69d00ce0a 100644 --- a/packages/houdini/src/codegen/transforms/paginate.ts +++ b/packages/houdini/src/codegen/transforms/paginate.ts @@ -1,8 +1,14 @@ import * as graphql from 'graphql' -import { Config, HoudiniError, parentTypeFromAncestors, CollectedGraphQLDocument } from '../../lib' +import { + Config, + HoudiniError, + parentTypeFromAncestors, + CollectedGraphQLDocument, + unwrapType, + wrapType, +} from '../../lib' import { ArtifactKind, RefetchUpdateMode } from '../../runtime/lib/types' -import { unwrapType, wrapType } from '../utils' // the paginate transform is responsible for preparing a fragment marked for pagination // to be embedded in the query that will be used to fetch additional data. That means it diff --git a/packages/houdini/src/codegen/transforms/typename.ts b/packages/houdini/src/codegen/transforms/typename.ts index 462d0a9c0f..9230eb98f8 100644 --- a/packages/houdini/src/codegen/transforms/typename.ts +++ b/packages/houdini/src/codegen/transforms/typename.ts @@ -1,7 +1,6 @@ import * as graphql from 'graphql' -import { Config, parentTypeFromAncestors, CollectedGraphQLDocument } from '../../lib' -import { unwrapType } from '../utils' +import { Config, parentTypeFromAncestors, CollectedGraphQLDocument, unwrapType } from '../../lib' // typename adds __typename to the selection set of any unions or interfaces export default async function addTypename( diff --git a/packages/houdini/src/codegen/utils/graphql.test.ts b/packages/houdini/src/codegen/utils/graphql.test.ts deleted file mode 100644 index d75a146aab..0000000000 --- a/packages/houdini/src/codegen/utils/graphql.test.ts +++ /dev/null @@ -1,285 +0,0 @@ -import * as graphql from 'graphql' -import { test, expect, describe } from 'vitest' - -import { testConfig } from '../../test' -import { flattenSelections } from './flattenSelections' -import { TypeWrapper, unwrapType } from './graphql' - -const config = testConfig({ defaultFragmentMasking: 'disable' }) - -describe('unwrapType', () => { - test('nullable list of non-null', function () { - const type: graphql.TypeNode = { - kind: graphql.Kind.LIST_TYPE, - type: { - kind: graphql.Kind.NON_NULL_TYPE, - type: { - kind: graphql.Kind.NAMED_TYPE, - name: { - kind: graphql.Kind.NAME, - value: 'User', - }, - }, - }, - } - - const unwrapped = unwrapType(testConfig(), type) - - // make sure we can get the inner type - expect(unwrapped.type.name).toEqual('User') - - // and that we have the correct set of wrappers - expect(unwrapped.wrappers).toEqual([ - TypeWrapper.NonNull, - TypeWrapper.List, - TypeWrapper.Nullable, - ]) - }) - - test('non-null list of non-null', function () { - const type: graphql.TypeNode = { - kind: graphql.Kind.NON_NULL_TYPE, - type: { - kind: graphql.Kind.LIST_TYPE, - type: { - kind: graphql.Kind.NON_NULL_TYPE, - type: { - kind: graphql.Kind.NAMED_TYPE, - name: { - kind: graphql.Kind.NAME, - value: 'User', - }, - }, - }, - }, - } - - const unwrapped = unwrapType(testConfig(), type) - - // make sure we can get the inner type - expect(unwrapped.type.name).toEqual('User') - - // and that we have the correct set of wrappers - expect(unwrapped.wrappers).toEqual([ - TypeWrapper.NonNull, - TypeWrapper.List, - TypeWrapper.NonNull, - ]) - }) - - test('non-null', function () { - const type: graphql.TypeNode = { - kind: graphql.Kind.NON_NULL_TYPE, - type: { - kind: graphql.Kind.NAMED_TYPE, - name: { - kind: graphql.Kind.NAME, - value: 'User', - }, - }, - } - - const unwrapped = unwrapType(testConfig(), type) - - // make sure we can get the inner type - expect(unwrapped.type.name).toEqual('User') - - // and that we have the correct set of wrappers - expect(unwrapped.wrappers).toEqual([TypeWrapper.NonNull]) - }) - - test('nullable', function () { - const type: graphql.TypeNode = { - kind: graphql.Kind.NAMED_TYPE, - name: { - kind: graphql.Kind.NAME, - value: 'User', - }, - } - - const unwrapped = unwrapType(testConfig(), type) - - // make sure we can get the inner type - expect(unwrapped.type.name).toEqual('User') - - // and that we have the correct set of wrappers - expect(unwrapped.wrappers).toEqual([TypeWrapper.Nullable]) - }) - - test('nullable list of nullable', function () { - const type: graphql.TypeNode = { - kind: graphql.Kind.LIST_TYPE, - type: { - kind: graphql.Kind.NAMED_TYPE, - name: { - kind: graphql.Kind.NAME, - value: 'User', - }, - }, - } - - const unwrapped = unwrapType(testConfig(), type) - - // make sure we can get the inner type - expect(unwrapped.type.name).toEqual('User') - - // and that we have the correct set of wrappers - expect(unwrapped.wrappers).toEqual([ - TypeWrapper.Nullable, - TypeWrapper.List, - TypeWrapper.Nullable, - ]) - }) - - test('non-null list of nullable', function () { - const type: graphql.TypeNode = { - kind: graphql.Kind.NON_NULL_TYPE, - type: { - kind: graphql.Kind.LIST_TYPE, - type: { - kind: graphql.Kind.NAMED_TYPE, - name: { - kind: graphql.Kind.NAME, - value: 'User', - }, - }, - }, - } - - const unwrapped = unwrapType(testConfig(), type) - - // make sure we can get the inner type - expect(unwrapped.type.name).toEqual('User') - - // and that we have the correct set of wrappers - expect(unwrapped.wrappers).toEqual([ - TypeWrapper.Nullable, - TypeWrapper.List, - TypeWrapper.NonNull, - ]) - }) -}) - -const fragmentDefinitions = ( - graphql.parse(` - fragment Foo on User { - id - } -`).definitions as graphql.FragmentDefinitionNode[] -).reduce( - (acc, defn) => ({ - ...acc, - [defn.name.value]: defn, - }), - {} -) - -function getSelections(doc: string): readonly graphql.SelectionNode[] { - return (graphql.parse(doc).definitions[0] as graphql.OperationDefinitionNode).selectionSet - .selections -} - -function testFlatten(doc: string, applyFragments: boolean = true): graphql.OperationDefinitionNode { - const flat = flattenSelections({ - config, - applyFragments: true, - filepath: '', - fragmentDefinitions, - selections: getSelections(doc), - }) - - return { - kind: 'OperationDefinition', - operation: 'query', - selectionSet: { kind: 'SelectionSet', selections: flat }, - } -} - -describe('flattenSelection', function () { - test('applies fragment definitions', function () { - expect( - testFlatten(` - { - user { - ...Foo - } - } - `) - ).toMatchInlineSnapshot(` - { - user { - ... on User { - id - } - ...Foo - } - } - `) - }) - - test('merges field selections', function () { - expect( - testFlatten(` - { - user { - id - } - user { - name - } - } - `) - ).toMatchInlineSnapshot(` - { - user { - id - name - } - } - `) - }) - - test('flattens nested inline fragments', function () { - expect( - testFlatten(` - { - friends { - ... on Friend { - name - ... on User { - id - } - ... on Ghost { - id - } - ... on Cat { - id - } - } - ... on Cat { - name - } - } - } - `) - ).toMatchInlineSnapshot(` - { - friends { - ... on Friend { - name - } - ... on User { - id - } - ... on Ghost { - id - } - ... on Cat { - id - name - } - } - } - `) - }) -}) diff --git a/packages/houdini/src/codegen/utils/graphql.ts b/packages/houdini/src/codegen/utils/graphql.ts deleted file mode 100644 index 7de089696f..0000000000 --- a/packages/houdini/src/codegen/utils/graphql.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as graphql from 'graphql' - -import { Config } from '../../lib' - -export function unwrapType( - config: Config, - type: any, - wrappers: TypeWrapper[] = [] -): { type: graphql.GraphQLNamedType; wrappers: TypeWrapper[] } { - // if we are looking at a non null type - if (type.kind === 'NonNullType') { - return unwrapType(config, type.type, [TypeWrapper.NonNull, ...wrappers]) - } - if (type instanceof graphql.GraphQLNonNull) { - return unwrapType(config, type.ofType, [TypeWrapper.NonNull, ...wrappers]) - } - - // if the last thing we added was not a non-null indicator - if (wrappers[0] !== TypeWrapper.NonNull) { - // add the nullable mark - wrappers.unshift(TypeWrapper.Nullable) - } - - if (type.kind === 'ListType') { - return unwrapType(config, type.type, [TypeWrapper.List, ...wrappers]) - } - if (type instanceof graphql.GraphQLList) { - return unwrapType(config, type.ofType, [TypeWrapper.List, ...wrappers]) - } - - // get the named type - const namedType = config.schema.getType(type.name.value || type.name) - if (!namedType) { - throw new Error('Could not unwrap type: ' + JSON.stringify(type)) - } - - // don't add any wrappers - return { type: namedType, wrappers } -} - -export function wrapType({ - type, - wrappers, -}: { - type: graphql.GraphQLNamedType - wrappers: TypeWrapper[] -}): graphql.TypeNode { - const head = wrappers[0] - const tail = wrappers.slice(1) - - let kind: graphql.TypeNode['kind'] = graphql.Kind.NAMED_TYPE - if (head === TypeWrapper.List) { - kind = graphql.Kind.LIST_TYPE - } else if (head === TypeWrapper.NonNull) { - kind = graphql.Kind.NON_NULL_TYPE - } - - if (kind === 'NamedType') { - return { - kind, - name: { - kind: graphql.Kind.NAME, - value: type.name, - }, - } - } - - return { - kind, - // @ts-ignore - type: wrapType({ type, wrappers: tail }), - } -} - -export enum TypeWrapper { - Nullable = 'Nullable', - List = 'List', - NonNull = 'NonNull', -} diff --git a/packages/houdini/src/codegen/utils/index.ts b/packages/houdini/src/codegen/utils/index.ts index 76afc3b4ac..36879b0aa2 100644 --- a/packages/houdini/src/codegen/utils/index.ts +++ b/packages/houdini/src/codegen/utils/index.ts @@ -1,5 +1,4 @@ export * from './commonjs' export * from './flattenSelections' -export * from './graphql' export * from './moduleExport' export * from './murmur' diff --git a/packages/houdini/src/codegen/validators/typeCheck.ts b/packages/houdini/src/codegen/validators/typeCheck.ts index b868fd02e8..f9efd2a229 100755 --- a/packages/houdini/src/codegen/validators/typeCheck.ts +++ b/packages/houdini/src/codegen/validators/typeCheck.ts @@ -9,6 +9,7 @@ import { HoudiniError, siteURL, CollectedGraphQLDocument, + unwrapType, } from '../../lib' import { FragmentArgument, @@ -16,7 +17,6 @@ import { withArguments, } from '../transforms/fragmentVariables' import { connectionSelection } from '../transforms/list' -import { unwrapType } from '../utils' // typeCheck verifies that the documents are valid instead of waiting // for the compiler to fail later down the line. diff --git a/packages/houdini/src/lib/graphql.test.ts b/packages/houdini/src/lib/graphql.test.ts index de2accb1e5..efcd5211fc 100644 --- a/packages/houdini/src/lib/graphql.test.ts +++ b/packages/houdini/src/lib/graphql.test.ts @@ -1,211 +1,285 @@ import * as graphql from 'graphql' -import { test, expect } from 'vitest' - -import { parentTypeFromAncestors } from './graphql' - -test('can find ancestor from type', function () { - // define a schema we'll test against - const schema = graphql.buildSchema(` - type User { - id: ID! - name: String! - } - - type Query { - users: [User!]! - } - `) - - const doc = graphql.parse(` - query { - users { - id - } - } - `) - - // we should - let foundType = '' - - graphql.visit(doc, { - Field(node, key, parent, path, ancestors) { - if (node.name.value === 'id') { - foundType = parentTypeFromAncestors(schema, '', ancestors).name - } - }, +import { test, expect, describe } from 'vitest' + +import { flattenSelections } from '../codegen/utils/flattenSelections' +import { testConfig } from '../test' +import { TypeWrapper, unwrapType } from './graphql' + +const config = testConfig({ defaultFragmentMasking: 'disable' }) + +describe('unwrapType', () => { + test('nullable list of non-null', function () { + const type: graphql.TypeNode = { + kind: graphql.Kind.LIST_TYPE, + type: { + kind: graphql.Kind.NON_NULL_TYPE, + type: { + kind: graphql.Kind.NAMED_TYPE, + name: { + kind: graphql.Kind.NAME, + value: 'User', + }, + }, + }, + } + + const unwrapped = unwrapType(testConfig(), type) + + // make sure we can get the inner type + expect(unwrapped.type.name).toEqual('User') + + // and that we have the correct set of wrappers + expect(unwrapped.wrappers).toEqual([ + TypeWrapper.NonNull, + TypeWrapper.List, + TypeWrapper.Nullable, + ]) }) - expect(foundType).toEqual('User') -}) + test('non-null list of non-null', function () { + const type: graphql.TypeNode = { + kind: graphql.Kind.NON_NULL_TYPE, + type: { + kind: graphql.Kind.LIST_TYPE, + type: { + kind: graphql.Kind.NON_NULL_TYPE, + type: { + kind: graphql.Kind.NAMED_TYPE, + name: { + kind: graphql.Kind.NAME, + value: 'User', + }, + }, + }, + }, + } + + const unwrapped = unwrapType(testConfig(), type) + + // make sure we can get the inner type + expect(unwrapped.type.name).toEqual('User') + + // and that we have the correct set of wrappers + expect(unwrapped.wrappers).toEqual([ + TypeWrapper.NonNull, + TypeWrapper.List, + TypeWrapper.NonNull, + ]) + }) -test('inline fragments', function () { - // define a schema we'll test against - const schema = graphql.buildSchema(` - type User implements Node { - id: ID! - name: String! - } - - interface Node { - id: ID! - } - - type Query { - nodes: [Node!]! - } - `) - - const doc = graphql.parse(` - query { - nodes { - ... on User { - id - } - } - } - `) - - // we should - let foundType = '' - - graphql.visit(doc, { - Field(node, key, parent, path, ancestors) { - if (node.name.value === 'id') { - foundType = parentTypeFromAncestors(schema, '', ancestors).name - } - }, + test('non-null', function () { + const type: graphql.TypeNode = { + kind: graphql.Kind.NON_NULL_TYPE, + type: { + kind: graphql.Kind.NAMED_TYPE, + name: { + kind: graphql.Kind.NAME, + value: 'User', + }, + }, + } + + const unwrapped = unwrapType(testConfig(), type) + + // make sure we can get the inner type + expect(unwrapped.type.name).toEqual('User') + + // and that we have the correct set of wrappers + expect(unwrapped.wrappers).toEqual([TypeWrapper.NonNull]) }) - expect(foundType).toEqual('User') -}) + test('nullable', function () { + const type: graphql.TypeNode = { + kind: graphql.Kind.NAMED_TYPE, + name: { + kind: graphql.Kind.NAME, + value: 'User', + }, + } -test('nested inline fragments', function () { - // define a schema we'll test against - const schema = graphql.buildSchema(` - type User implements Node { - id: ID! - name: String! - } - - interface Node { - id: ID! - } - - type Query { - nodes: [Node!]! - } - `) - - const doc = graphql.parse(` - query { - nodes { - ... on User { - ... on Node { - id - } - } - } - } - `) - - // we should - let foundType = '' - - graphql.visit(doc, { - Field(node, key, parent, path, ancestors) { - if (node.name.value === 'id') { - foundType = parentTypeFromAncestors(schema, '', ancestors).name - } - }, + const unwrapped = unwrapType(testConfig(), type) + + // make sure we can get the inner type + expect(unwrapped.type.name).toEqual('User') + + // and that we have the correct set of wrappers + expect(unwrapped.wrappers).toEqual([TypeWrapper.Nullable]) }) - expect(foundType).toEqual('Node') + test('nullable list of nullable', function () { + const type: graphql.TypeNode = { + kind: graphql.Kind.LIST_TYPE, + type: { + kind: graphql.Kind.NAMED_TYPE, + name: { + kind: graphql.Kind.NAME, + value: 'User', + }, + }, + } + + const unwrapped = unwrapType(testConfig(), type) + + // make sure we can get the inner type + expect(unwrapped.type.name).toEqual('User') + + // and that we have the correct set of wrappers + expect(unwrapped.wrappers).toEqual([ + TypeWrapper.Nullable, + TypeWrapper.List, + TypeWrapper.Nullable, + ]) + }) + + test('non-null list of nullable', function () { + const type: graphql.TypeNode = { + kind: graphql.Kind.NON_NULL_TYPE, + type: { + kind: graphql.Kind.LIST_TYPE, + type: { + kind: graphql.Kind.NAMED_TYPE, + name: { + kind: graphql.Kind.NAME, + value: 'User', + }, + }, + }, + } + + const unwrapped = unwrapType(testConfig(), type) + + // make sure we can get the inner type + expect(unwrapped.type.name).toEqual('User') + + // and that we have the correct set of wrappers + expect(unwrapped.wrappers).toEqual([ + TypeWrapper.Nullable, + TypeWrapper.List, + TypeWrapper.NonNull, + ]) + }) }) -test('can find interface ancestor from type', function () { - // define a schema we'll test against - const schema = graphql.buildSchema(` - type User implements Node { - id: ID! - name: String! - } - - interface Node { - id: ID! - } - - type Query { - nodes: [Node!]! - } - `) - - const doc = graphql.parse(` - query { - nodes { - id - } - } - `) - - // we should - let foundType = '' - - graphql.visit(doc, { - Field(node, key, parent, path, ancestors) { - if (node.name.value === 'id') { - foundType = parentTypeFromAncestors(schema, '', ancestors).name - } - }, +const fragmentDefinitions = ( + graphql.parse(` + fragment Foo on User { + id + } +`).definitions as graphql.FragmentDefinitionNode[] +).reduce( + (acc, defn) => ({ + ...acc, + [defn.name.value]: defn, + }), + {} +) + +function getSelections(doc: string): readonly graphql.SelectionNode[] { + return (graphql.parse(doc).definitions[0] as graphql.OperationDefinitionNode).selectionSet + .selections +} + +function testFlatten(doc: string, applyFragments: boolean = true): graphql.OperationDefinitionNode { + const flat = flattenSelections({ + config, + applyFragments: true, + filepath: '', + fragmentDefinitions, + selections: getSelections(doc), }) - expect(foundType).toEqual('Node') -}) + return { + kind: 'OperationDefinition', + operation: 'query', + selectionSet: { kind: 'SelectionSet', selections: flat }, + } +} + +describe('flattenSelection', function () { + test('applies fragment definitions', function () { + expect( + testFlatten(` + { + user { + ...Foo + } + } + `) + ).toMatchInlineSnapshot(` + { + user { + ... on User { + id + } + ...Foo + } + } + `) + }) -test('union ancestor', function () { - const schema = graphql.buildSchema(` - union UnionType = TypeB | TypeA - - type TypeA { - id: String - objective: TypeB - } - - type TypeB { - family: TypeA - objective: TypeB - id: String - } - - - type Query { - types: [UnionType] - } - `) - - const doc = graphql.parse(` - query Friends { - types { - ... on TypeA { - objective { - id - } - } - } - } - `) - - let foundType = '' - - // make sure its valid first - expect(graphql.validate(schema, doc)).toHaveLength(0) - - graphql.visit(doc, { - Field(node, key, parent, path, ancestors) { - if (node.name.value === 'id') { - foundType = parentTypeFromAncestors(schema, '', ancestors).name + test('merges field selections', function () { + expect( + testFlatten(` + { + user { + id + } + user { + name + } + } + `) + ).toMatchInlineSnapshot(` + { + user { + id + name + } } - }, + `) }) - expect(foundType).toEqual('TypeB') + test('flattens nested inline fragments', function () { + expect( + testFlatten(` + { + friends { + ... on Friend { + name + ... on User { + id + } + ... on Ghost { + id + } + ... on Cat { + id + } + } + ... on Cat { + name + } + } + } + `) + ).toMatchInlineSnapshot(` + { + friends { + ... on Friend { + name + } + ... on User { + id + } + ... on Ghost { + id + } + ... on Cat { + id + name + } + } + } + `) + }) }) diff --git a/packages/houdini/src/lib/graphql.ts b/packages/houdini/src/lib/graphql.ts index ece6ab55f7..0021514b34 100644 --- a/packages/houdini/src/lib/graphql.ts +++ b/packages/houdini/src/lib/graphql.ts @@ -1,6 +1,7 @@ import crypto from 'crypto' import * as graphql from 'graphql' +import { Config } from '.' import { HoudiniError } from './error' import * as path from './path' @@ -198,3 +199,79 @@ export function operation_requires_variables(operation: graphql.OperationDefinit ) ) } + +export function unwrapType( + config: Config, + type: any, + wrappers: TypeWrapper[] = [] +): { type: graphql.GraphQLNamedType; wrappers: TypeWrapper[] } { + // if we are looking at a non null type + if (type.kind === 'NonNullType') { + return unwrapType(config, type.type, [TypeWrapper.NonNull, ...wrappers]) + } + if (type instanceof graphql.GraphQLNonNull) { + return unwrapType(config, type.ofType, [TypeWrapper.NonNull, ...wrappers]) + } + + // if the last thing we added was not a non-null indicator + if (wrappers[0] !== TypeWrapper.NonNull) { + // add the nullable mark + wrappers.unshift(TypeWrapper.Nullable) + } + + if (type.kind === 'ListType') { + return unwrapType(config, type.type, [TypeWrapper.List, ...wrappers]) + } + if (type instanceof graphql.GraphQLList) { + return unwrapType(config, type.ofType, [TypeWrapper.List, ...wrappers]) + } + + // get the named type + const namedType = config.schema.getType(type.name.value || type.name) + if (!namedType) { + throw new Error('Could not unwrap type: ' + JSON.stringify(type)) + } + + // don't add any wrappers + return { type: namedType, wrappers } +} + +export function wrapType({ + type, + wrappers, +}: { + type: graphql.GraphQLNamedType + wrappers: TypeWrapper[] +}): graphql.TypeNode { + const head = wrappers[0] + const tail = wrappers.slice(1) + + let kind: graphql.TypeNode['kind'] = graphql.Kind.NAMED_TYPE + if (head === TypeWrapper.List) { + kind = graphql.Kind.LIST_TYPE + } else if (head === TypeWrapper.NonNull) { + kind = graphql.Kind.NON_NULL_TYPE + } + + if (kind === 'NamedType') { + return { + kind, + name: { + kind: graphql.Kind.NAME, + value: type.name, + }, + } + } + + return { + kind, + // @ts-ignore + type: wrapType({ type, wrappers: tail }), + } +} + +export enum TypeWrapper { + Nullable = 'Nullable', + List = 'List', + NonNull = 'NonNull', +} diff --git a/packages/houdini/src/runtime/lib/config.ts b/packages/houdini/src/runtime/lib/config.ts index 86e251a922..10369dfa0e 100644 --- a/packages/houdini/src/runtime/lib/config.ts +++ b/packages/houdini/src/runtime/lib/config.ts @@ -209,7 +209,9 @@ export type TypeConfig = { export type ScalarSpec = { // the type to use at runtime type: string - // the function to call that serializes the type for the API + // the function to call that serializes the type for the API. If you are using this + // scalar as the input to a query through a route parameter, this function will receive + // the value as a string in addition to your complex value. marshal?: (val: any) => any // the function to call that turns the API's response into _ClientType unmarshal?: (val: any) => any diff --git a/packages/houdini/src/runtime/lib/scalars.test.ts b/packages/houdini/src/runtime/lib/scalars.test.ts index bd7d22232a..46c1418a42 100644 --- a/packages/houdini/src/runtime/lib/scalars.test.ts +++ b/packages/houdini/src/runtime/lib/scalars.test.ts @@ -2,7 +2,7 @@ import { test, expect, describe, beforeEach } from 'vitest' import { testConfigFile } from '../../test' import { setMockConfig } from './config' -import { marshalInputs, marshalSelection, unmarshalSelection } from './scalars' +import { marshalInputs, marshalSelection, parseScalar, unmarshalSelection } from './scalars' import { ArtifactKind, QueryArtifact, SubscriptionSelection } from './types' beforeEach(() => @@ -924,3 +924,67 @@ describe('marshal selection', function () { }) }) }) + +describe('parseScalar', function () { + const table = [ + { + title: 'String', + type: 'String', + value: 'asf', + expected: 'asf', + }, + { + title: 'ID', + type: 'ID', + value: 'asf', + expected: 'asf', + }, + { + title: 'Int', + type: 'Int', + value: '1', + expected: 1, + }, + { + title: 'Boolean', + type: 'Boolean', + value: 'true', + expected: true, + }, + { + title: 'Float', + type: 'Float', + value: '1.0', + expected: 1.0, + }, + { + title: 'Custom with Marshal', + type: 'MyScalar', + value: '1.0', + expected: 100.0, + }, + { + title: 'Custom mo Marshal', + type: 'MyScalar2', + value: '1.0', + expected: '1.0', + }, + ] + + const config = testConfigFile({ + scalars: { + MyScalar: { + type: 'number', + marshal(val: string) { + return parseInt(val, 10) * 100 + }, + }, + }, + }) + + for (const row of table) { + test(row.title, function () { + expect(parseScalar(config, row.type, row.value)).toEqual(row.expected) + }) + } +}) diff --git a/packages/houdini/src/runtime/lib/scalars.ts b/packages/houdini/src/runtime/lib/scalars.ts index 56efd294f6..2514514921 100644 --- a/packages/houdini/src/runtime/lib/scalars.ts +++ b/packages/houdini/src/runtime/lib/scalars.ts @@ -173,7 +173,7 @@ export function unmarshalSelection( } // is the type something that requires marshaling if (config.scalars?.[type]?.marshal) { - const unmarshalFn = config.scalars[type].unmarshal + const unmarshalFn = config.scalars[type]?.unmarshal if (!unmarshalFn) { throw new Error( `scalar type ${type} is missing an \`unmarshal\` function. see https://github.com/AlecAivazis/houdini#%EF%B8%8Fcustom-scalars` @@ -198,3 +198,33 @@ export function isScalar(config: ConfigFile, type: string) { .concat(Object.keys(config.scalars || {})) .includes(type) } + +export function parseScalar( + config: ConfigFile, + type: string, + value: string +): string | number | boolean { + if (type === 'Boolean') { + return value === 'true' + } + if (type === 'ID') { + return value + } + if (type === 'String') { + return value + } + if (type === 'Int') { + return parseInt(value, 10) + } + if (type === 'Float') { + return parseFloat(value) + } + + // if we have a special parse function, use it + if (config.scalars?.[type]?.marshal) { + return config.scalars[type]?.marshal!(value) + } + + // we dont recognize the type, just use the string value + return value +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5845738561..9810c7e6c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,9 @@ importers: memfs: 3.4.12 recast: 0.23.1 devDependencies: - '@changesets/changelog-git': 0.1.13 - '@changesets/changelog-github': 0.4.7 - '@changesets/cli': 2.25.2 + '@changesets/changelog-git': 0.1.14 + '@changesets/changelog-github': 0.4.8 + '@changesets/cli': 2.26.0 '@playwright/test': 1.28.1 '@theguild/eslint-config': 0.1.2_ha6vam6werchizxrnqvarmz2zu '@trivago/prettier-plugin-sort-imports': 3.4.0_prettier@2.8.1 @@ -72,8 +72,8 @@ importers: typescript: ^4.9 dependencies: cross-env: 7.0.3 - eslint-config-next: 13.0.7_ha6vam6werchizxrnqvarmz2zu - next: 13.0.7_biqbaboplfbrettd7655fr4n2y + eslint-config-next: 13.1.1_ha6vam6werchizxrnqvarmz2zu + next: 13.1.1_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 devDependencies: @@ -105,7 +105,7 @@ importers: prettier: ^2.5.1 prettier-plugin-svelte: ^2.5.0 svelte: 3.55.0 - svelte-check: ^2.2.6 + svelte-check: ^3.0.1 svelte-preprocess: ^5.0.0 tslib: ^2.3.1 typescript: ^4.9 @@ -130,7 +130,7 @@ importers: prettier: 2.8.1 prettier-plugin-svelte: 2.9.0_ajxj753sv7dbwexjherrch25ta svelte: 3.55.0 - svelte-check: 2.10.2_svelte@3.55.0 + svelte-check: 3.0.1_svelte@3.55.0 svelte-preprocess: 5.0.0_niwyv7xychq2ag6arq5eqxbomm tslib: 2.4.1 typescript: 4.9.4 @@ -154,13 +154,13 @@ importers: vite: ^4.0.1 ws: ^8.8.1 dependencies: - graphql-tag: 2.12.6_graphql@15.8.0 - graphql-ws: 5.11.2_graphql@15.8.0 + graphql-tag: 2.12.6_graphql@16.6.0 + graphql-ws: 5.11.2_graphql@16.6.0 devDependencies: - '@graphql-yoga/node': 2.13.13_graphql@15.8.0 + '@graphql-yoga/node': 2.13.13_graphql@16.6.0 '@sveltejs/kit': 1.0.0_svelte@3.55.0+vite@4.0.1 concurrently: 6.5.1 - graphql: 15.8.0 + graphql: 16.6.0 houdini: link:../packages/houdini houdini-svelte: link:../packages/houdini-svelte svelte: 3.55.0 @@ -224,7 +224,7 @@ importers: vite-plugin-watch-and-run: ^1.1.0 vitest: ^0.23.4 dependencies: - '@babel/parser': 7.20.5 + '@babel/parser': 7.20.7 '@graphql-tools/schema': 9.0.12_graphql@15.8.0 '@kitql/helper': 0.5.0 '@types/estree': 1.0.0 @@ -239,7 +239,7 @@ importers: graphql: 15.8.0 memfs: 3.4.12 micromatch: 4.0.5 - minimatch: 5.1.1 + minimatch: 5.1.2 node-fetch: 3.3.0 npx-import: 1.1.4 prompts: 2.4.2 @@ -290,7 +290,7 @@ importers: recast: ^0.23.1 scripts: workspace:^ dependencies: - '@babel/parser': 7.20.5 + '@babel/parser': 7.20.7 estraverse: 5.3.0 graphql: 15.8.0 houdini: link:../houdini @@ -298,7 +298,7 @@ importers: devDependencies: '@types/estraverse': 5.1.2 '@types/next': 9.0.0_biqbaboplfbrettd7655fr4n2y - next: 13.0.7_biqbaboplfbrettd7655fr4n2y + next: 13.1.1_biqbaboplfbrettd7655fr4n2y scripts: link:../_scripts packages/houdini-svelte: @@ -332,6 +332,7 @@ importers: site: specifiers: + '@babel/parser': ^7.20.7 '@sveltejs/adapter-vercel': 1.0.0 '@sveltejs/kit': 1.0.0 '@typescript-eslint/eslint-plugin': ^5.10.1 @@ -339,25 +340,30 @@ importers: eslint: ^8.12.0 eslint-config-prettier: ^8.3.0 eslint-plugin-svelte3: ^4.0.0 + estree-walker: ^3.0.1 feather-icons: ^4.28.0 flexsearch: ^0.7.31 + gatsby-remark-code-titles: ^1.1.0 husky: ^7.0.4 lint-staged: ^12.3.4 lodash: ^4.17.21 mdsvex: ^0.10.6 node-html-parser: ^5.4.1 - prettier: ^2.5.1 - prettier-plugin-svelte: ^2.7.0 + prettier: ^2.8.1 + prettier-plugin-svelte: ^2.9.0 prism-svelte: ^0.5.0 prismjs: ^1.28.0 + query-string: ^8.1.0 + recast: ^0.23.1 rehype-autolink-headings: ^6.1.1 rehype-slug: ^5.0.1 svelte: ^3.55.0 - svelte-check: ^2.8.0 + svelte-check: ^3.0.1 svelte-kit-cookie-session: 3.0.6 svelte-preprocess: ^5.0.0 tslib: ^2.3.1 typescript: ^4.9 + unist-util-visit: ^4.1.1 vite: ^4.0.1 vite-plugin-replace: ^0.1.1 dependencies: @@ -368,13 +374,16 @@ importers: node-html-parser: 5.4.2 prism-svelte: 0.5.0 prismjs: 1.29.0 + query-string: 8.1.0 rehype-autolink-headings: 6.1.1 rehype-slug: 5.1.0 svelte: 3.55.0 svelte-preprocess: 5.0.0_niwyv7xychq2ag6arq5eqxbomm + unist-util-visit: 4.1.1 vite: 4.0.1 vite-plugin-replace: 0.1.1_vite@4.0.1 devDependencies: + '@babel/parser': 7.20.7 '@sveltejs/adapter-vercel': 1.0.0_@sveltejs+kit@1.0.0 '@sveltejs/kit': 1.0.0_svelte@3.55.0+vite@4.0.1 '@typescript-eslint/eslint-plugin': 5.46.1_imrg37k3svwu377c6q7gkarwmi @@ -382,11 +391,14 @@ importers: eslint: 8.29.0 eslint-config-prettier: 8.5.0_eslint@8.29.0 eslint-plugin-svelte3: 4.0.0_ffnabc34fed75fjymbd45uofx4 + estree-walker: 3.0.1 + gatsby-remark-code-titles: 1.1.0 husky: 7.0.4 lint-staged: 12.5.0 prettier: 2.8.1 prettier-plugin-svelte: 2.9.0_ajxj753sv7dbwexjherrch25ta - svelte-check: 2.10.2_svelte@3.55.0 + recast: 0.23.1 + svelte-check: 3.0.1_svelte@3.55.0 svelte-kit-cookie-session: 3.0.6 tslib: 2.4.1 typescript: 4.9.4 @@ -411,41 +423,13 @@ packages: dependencies: '@babel/highlight': 7.18.6 - /@babel/compat-data/7.20.5: + /@babel/compat-data/7.20.10: resolution: { - integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==, + integrity: sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==, } engines: { node: '>=6.9.0' } - /@babel/core/7.12.9: - resolution: - { - integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 - '@babel/helper-module-transforms': 7.20.2 - '@babel/helpers': 7.20.6 - '@babel/parser': 7.20.5 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.1 - lodash: 4.17.21 - resolve: 1.22.1 - semver: 5.7.1 - source-map: 0.5.7 - transitivePeerDependencies: - - supports-color - dev: true - optional: true - /@babel/core/7.17.8: resolution: { @@ -456,47 +440,46 @@ packages: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 '@babel/generator': 7.17.7 - '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.17.8 - '@babel/helper-module-transforms': 7.20.2 - '@babel/helpers': 7.20.6 - '@babel/parser': 7.20.5 - '@babel/template': 7.18.10 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.17.8 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helpers': 7.20.7 + '@babel/parser': 7.20.7 + '@babel/template': 7.20.7 '@babel/traverse': 7.17.3 - '@babel/types': 7.17.0 + '@babel/types': 7.20.7 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 - json5: 2.2.1 + json5: 2.2.2 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/core/7.20.5: + /@babel/core/7.20.7: resolution: { - integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==, + integrity: sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==, } engines: { node: '>=6.9.0' } dependencies: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 - '@babel/helper-module-transforms': 7.20.2 - '@babel/helpers': 7.20.6 - '@babel/parser': 7.20.5 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/generator': 7.20.7 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.7 + '@babel/helper-module-transforms': 7.20.11 + '@babel/helpers': 7.20.7 + '@babel/parser': 7.20.7 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.10 + '@babel/types': 7.20.7 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 - json5: 2.2.1 + json5: 2.2.2 semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: false /@babel/generator/7.17.7: resolution: @@ -505,53 +488,54 @@ packages: } engines: { node: '>=6.9.0' } dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.20.7 jsesc: 2.5.2 source-map: 0.5.7 dev: true - /@babel/generator/7.20.5: + /@babel/generator/7.20.7: resolution: { - integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==, + integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==, } engines: { node: '>=6.9.0' } dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 - /@babel/helper-compilation-targets/7.20.0_@babel+core@7.17.8: + /@babel/helper-compilation-targets/7.20.7_@babel+core@7.17.8: resolution: { - integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==, + integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==, } engines: { node: '>=6.9.0' } peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.20.5 + '@babel/compat-data': 7.20.10 '@babel/core': 7.17.8 '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 + lru-cache: 5.1.1 semver: 6.3.0 dev: true - /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5: + /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.7: resolution: { - integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==, + integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==, } engines: { node: '>=6.9.0' } peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 + '@babel/compat-data': 7.20.10 + '@babel/core': 7.20.7 '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 + lru-cache: 5.1.1 semver: 6.3.0 - dev: false /@babel/helper-environment-visitor/7.18.9: resolution: @@ -567,8 +551,8 @@ packages: } engines: { node: '>=6.9.0' } dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.20.5 + '@babel/template': 7.20.7 + '@babel/types': 7.20.7 /@babel/helper-hoist-variables/7.18.6: resolution: @@ -577,7 +561,7 @@ packages: } engines: { node: '>=6.9.0' } dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 /@babel/helper-module-imports/7.18.6: resolution: @@ -586,12 +570,12 @@ packages: } engines: { node: '>=6.9.0' } dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 - /@babel/helper-module-transforms/7.20.2: + /@babel/helper-module-transforms/7.20.11: resolution: { - integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==, + integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==, } engines: { node: '>=6.9.0' } dependencies: @@ -600,9 +584,9 @@ packages: '@babel/helper-simple-access': 7.20.2 '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.10 + '@babel/types': 7.20.7 transitivePeerDependencies: - supports-color @@ -628,7 +612,7 @@ packages: } engines: { node: '>=6.9.0' } dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 /@babel/helper-split-export-declaration/7.18.6: resolution: @@ -637,7 +621,7 @@ packages: } engines: { node: '>=6.9.0' } dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 /@babel/helper-string-parser/7.19.4: resolution: @@ -660,16 +644,16 @@ packages: } engines: { node: '>=6.9.0' } - /@babel/helpers/7.20.6: + /@babel/helpers/7.20.7: resolution: { - integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==, + integrity: sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==, } engines: { node: '>=6.9.0' } dependencies: - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/template': 7.20.7 + '@babel/traverse': 7.20.10 + '@babel/types': 7.20.7 transitivePeerDependencies: - supports-color @@ -692,20 +676,20 @@ packages: engines: { node: '>=6.0.0' } hasBin: true dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.20.7 dev: true - /@babel/parser/7.20.5: + /@babel/parser/7.20.7: resolution: { - integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==, + integrity: sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==, } engines: { node: '>=6.0.0' } hasBin: true dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 - /@babel/plugin-proposal-object-rest-spread/7.12.1_@babel+core@7.12.9: + /@babel/plugin-proposal-object-rest-spread/7.12.1_@babel+core@7.20.7: resolution: { integrity: sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==, @@ -713,14 +697,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.12.9 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.10.4 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 - '@babel/plugin-transform-parameters': 7.20.5_@babel+core@7.12.9 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.20.7 dev: true optional: true - /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.5: + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.7: resolution: { integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, @@ -728,11 +712,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.20.5: + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.20.7: resolution: { integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, @@ -740,11 +724,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.5: + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.7: resolution: { integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, @@ -752,11 +736,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.20.5: + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.20.7: resolution: { integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, @@ -764,11 +748,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.5: + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.7: resolution: { integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, @@ -776,11 +760,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-jsx/7.12.1_@babel+core@7.12.9: + /@babel/plugin-syntax-jsx/7.12.1_@babel+core@7.20.7: resolution: { integrity: sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==, @@ -788,12 +772,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.12.9 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.10.4 dev: true optional: true - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.5: + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.7: resolution: { integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==, @@ -802,11 +786,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.5: + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.7: resolution: { integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, @@ -814,11 +798,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.5: + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.7: resolution: { integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, @@ -826,11 +810,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.5: + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.7: resolution: { integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, @@ -838,24 +822,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.12.9: - resolution: - { - integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - optional: true - - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.5: + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.7: resolution: { integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, @@ -863,11 +834,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 - dev: false - /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.5: + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.7: resolution: { integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, @@ -875,11 +845,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.5: + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.7: resolution: { integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, @@ -887,11 +857,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.5: + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.7: resolution: { integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, @@ -900,11 +870,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.5: + /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.7: resolution: { integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==, @@ -913,53 +883,53 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: false - /@babel/plugin-transform-parameters/7.20.5_@babel+core@7.12.9: + /@babel/plugin-transform-parameters/7.20.7_@babel+core@7.20.7: resolution: { - integrity: sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==, + integrity: sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==, } engines: { node: '>=6.9.0' } peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.12.9 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.20.2 dev: true optional: true - /@babel/runtime-corejs3/7.20.6: + /@babel/runtime-corejs3/7.20.7: resolution: { - integrity: sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==, + integrity: sha512-jr9lCZ4RbRQmCR28Q8U8Fu49zvFqLxTY9AMOUz+iyMohMoAgpEcVxY+wJNay99oXOpOcCTODkk70NDN2aaJEeg==, } engines: { node: '>=6.9.0' } dependencies: core-js-pure: 3.26.1 regenerator-runtime: 0.13.11 - /@babel/runtime/7.20.6: + /@babel/runtime/7.20.7: resolution: { - integrity: sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==, + integrity: sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==, } engines: { node: '>=6.9.0' } dependencies: regenerator-runtime: 0.13.11 - /@babel/template/7.18.10: + /@babel/template/7.20.7: resolution: { - integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==, + integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==, } engines: { node: '>=6.9.0' } dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 + '@babel/parser': 7.20.7 + '@babel/types': 7.20.7 /@babel/traverse/7.17.3: resolution: @@ -974,29 +944,29 @@ packages: '@babel/helper-function-name': 7.19.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.18.9 - '@babel/types': 7.17.0 + '@babel/parser': 7.20.7 + '@babel/types': 7.20.7 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/traverse/7.20.5: + /@babel/traverse/7.20.10: resolution: { - integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==, + integrity: sha512-oSf1juCgymrSez8NI4A2sr4+uB/mFd9MXplYGPEBnfAuWmmyeVcHa6xLPiaRBcXkcb/28bgxmQLTVwFKE1yfsg==, } engines: { node: '>=6.9.0' } dependencies: '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 + '@babel/generator': 7.20.7 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.19.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 + '@babel/parser': 7.20.7 + '@babel/types': 7.20.7 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: @@ -1013,10 +983,10 @@ packages: to-fast-properties: 2.0.0 dev: true - /@babel/types/7.20.5: + /@babel/types/7.20.7: resolution: { - integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==, + integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==, } engines: { node: '>=6.9.0' } dependencies: @@ -1024,17 +994,17 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - /@changesets/apply-release-plan/6.1.2: + /@changesets/apply-release-plan/6.1.3: resolution: { - integrity: sha512-H8TV9E/WtJsDfoDVbrDGPXmkZFSv7W2KLqp4xX4MKZXshb0hsQZUNowUa8pnus9qb/5OZrFFRVsUsDCVHNW/AQ==, + integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==, } dependencies: - '@babel/runtime': 7.20.6 - '@changesets/config': 2.2.0 + '@babel/runtime': 7.20.7 + '@changesets/config': 2.3.0 '@changesets/get-version-range-type': 0.3.2 - '@changesets/git': 1.5.0 - '@changesets/types': 5.2.0 + '@changesets/git': 2.0.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 fs-extra: 7.0.1 @@ -1045,63 +1015,63 @@ packages: semver: 5.7.1 dev: true - /@changesets/assemble-release-plan/5.2.2: + /@changesets/assemble-release-plan/5.2.3: resolution: { - integrity: sha512-B1qxErQd85AeZgZFZw2bDKyOfdXHhG+X5S+W3Da2yCem8l/pRy4G/S7iOpEcMwg6lH8q2ZhgbZZwZ817D+aLuQ==, + integrity: sha512-g7EVZCmnWz3zMBAdrcKhid4hkHT+Ft1n0mLussFMcB1dE2zCuwcvGoy9ec3yOgPGF4hoMtgHaMIk3T3TBdvU9g==, } dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.20.7 '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 - '@changesets/types': 5.2.0 + '@changesets/get-dependents-graph': 1.3.5 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 semver: 5.7.1 dev: true - /@changesets/changelog-git/0.1.13: + /@changesets/changelog-git/0.1.14: resolution: { - integrity: sha512-zvJ50Q+EUALzeawAxax6nF2WIcSsC5PwbuLeWkckS8ulWnuPYx8Fn/Sjd3rF46OzeKA8t30loYYV6TIzp4DIdg==, + integrity: sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA==, } dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 dev: true - /@changesets/changelog-github/0.4.7: + /@changesets/changelog-github/0.4.8: resolution: { - integrity: sha512-UUG5sKwShs5ha1GFnayUpZNcDGWoY7F5XxhOEHS62sDPOtoHQZsG3j1nC5RxZ3M1URHA321cwVZHeXgu99Y3ew==, + integrity: sha512-jR1DHibkMAb5v/8ym77E4AMNWZKB5NPzw5a5Wtqm1JepAuIF+hrKp2u04NKM14oBZhHglkCfrla9uq8ORnK/dw==, } dependencies: - '@changesets/get-github-info': 0.5.1 - '@changesets/types': 5.2.0 + '@changesets/get-github-info': 0.5.2 + '@changesets/types': 5.2.1 dotenv: 8.6.0 transitivePeerDependencies: - encoding dev: true - /@changesets/cli/2.25.2: + /@changesets/cli/2.26.0: resolution: { - integrity: sha512-ACScBJXI3kRyMd2R8n8SzfttDHi4tmKSwVwXBazJOylQItSRSF4cGmej2E4FVf/eNfGy6THkL9GzAahU9ErZrA==, + integrity: sha512-0cbTiDms+ICTVtEwAFLNW0jBNex9f5+fFv3I771nBvdnV/mOjd1QJ4+f8KtVSOrwD9SJkk9xbDkWFb0oXd8d1Q==, } hasBin: true dependencies: - '@babel/runtime': 7.20.6 - '@changesets/apply-release-plan': 6.1.2 - '@changesets/assemble-release-plan': 5.2.2 - '@changesets/changelog-git': 0.1.13 - '@changesets/config': 2.2.0 + '@babel/runtime': 7.20.7 + '@changesets/apply-release-plan': 6.1.3 + '@changesets/assemble-release-plan': 5.2.3 + '@changesets/changelog-git': 0.1.14 + '@changesets/config': 2.3.0 '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 - '@changesets/get-release-plan': 3.0.15 - '@changesets/git': 1.5.0 + '@changesets/get-dependents-graph': 1.3.5 + '@changesets/get-release-plan': 3.0.16 + '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 - '@changesets/pre': 1.0.13 - '@changesets/read': 0.5.8 - '@changesets/types': 5.2.0 - '@changesets/write': 0.2.2 + '@changesets/pre': 1.0.14 + '@changesets/read': 0.5.9 + '@changesets/types': 5.2.1 + '@changesets/write': 0.2.3 '@manypkg/get-packages': 1.1.3 '@types/is-ci': 3.0.0 '@types/semver': 6.2.3 @@ -1123,16 +1093,16 @@ packages: tty-table: 4.1.6 dev: true - /@changesets/config/2.2.0: + /@changesets/config/2.3.0: resolution: { - integrity: sha512-GGaokp3nm5FEDk/Fv2PCRcQCOxGKKPRZ7prcMqxEr7VSsG75MnChQE8plaW1k6V8L2bJE+jZWiRm19LbnproOw==, + integrity: sha512-EgP/px6mhCx8QeaMAvWtRrgyxW08k/Bx2tpGT+M84jEdX37v3VKfh4Cz1BkwrYKuMV2HZKeHOh8sHvja/HcXfQ==, } dependencies: '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 + '@changesets/get-dependents-graph': 1.3.5 '@changesets/logger': 0.0.5 - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.5 @@ -1147,23 +1117,23 @@ packages: extendable-error: 0.1.7 dev: true - /@changesets/get-dependents-graph/1.3.4: + /@changesets/get-dependents-graph/1.3.5: resolution: { - integrity: sha512-+C4AOrrFY146ydrgKOo5vTZfj7vetNu1tWshOID+UjPUU9afYGDXI8yLnAeib1ffeBXV3TuGVcyphKpJ3cKe+A==, + integrity: sha512-w1eEvnWlbVDIY8mWXqWuYE9oKhvIaBhzqzo4ITSJY9hgoqQ3RoBqwlcAzg11qHxv/b8ReDWnMrpjpKrW6m1ZTA==, } dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 semver: 5.7.1 dev: true - /@changesets/get-github-info/0.5.1: + /@changesets/get-github-info/0.5.2: resolution: { - integrity: sha512-w2yl3AuG+hFuEEmT6j1zDlg7GQLM/J2UxTmk0uJBMdRqHni4zXGe/vUlPfLom5KfX3cRfHc0hzGvloDPjWFNZw==, + integrity: sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg==, } dependencies: dataloader: 1.4.0 @@ -1172,18 +1142,18 @@ packages: - encoding dev: true - /@changesets/get-release-plan/3.0.15: + /@changesets/get-release-plan/3.0.16: resolution: { - integrity: sha512-W1tFwxE178/en+zSj/Nqbc3mvz88mcdqUMJhRzN1jDYqN3QI4ifVaRF9mcWUU+KI0gyYEtYR65tour690PqTcA==, + integrity: sha512-OpP9QILpBp1bY2YNIKFzwigKh7Qe9KizRsZomzLe6pK8IUo8onkAAVUD8+JRKSr8R7d4+JRuQrfSSNlEwKyPYg==, } dependencies: - '@babel/runtime': 7.20.6 - '@changesets/assemble-release-plan': 5.2.2 - '@changesets/config': 2.2.0 - '@changesets/pre': 1.0.13 - '@changesets/read': 0.5.8 - '@changesets/types': 5.2.0 + '@babel/runtime': 7.20.7 + '@changesets/assemble-release-plan': 5.2.3 + '@changesets/config': 2.3.0 + '@changesets/pre': 1.0.14 + '@changesets/read': 0.5.9 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 dev: true @@ -1194,17 +1164,18 @@ packages: } dev: true - /@changesets/git/1.5.0: + /@changesets/git/2.0.0: resolution: { - integrity: sha512-Xo8AT2G7rQJSwV87c8PwMm6BAc98BnufRMsML7m7Iw8Or18WFvFmxqG5aOL5PBvhgq9KrKvaeIBNIymracSuHg==, + integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==, } dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.20.7 '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 + micromatch: 4.0.5 spawndamnit: 2.0.0 dev: true @@ -1217,40 +1188,40 @@ packages: chalk: 2.4.2 dev: true - /@changesets/parse/0.3.15: + /@changesets/parse/0.3.16: resolution: { - integrity: sha512-3eDVqVuBtp63i+BxEWHPFj2P1s3syk0PTrk2d94W9JD30iG+OER0Y6n65TeLlY8T2yB9Fvj6Ev5Gg0+cKe/ZUA==, + integrity: sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg==, } dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 js-yaml: 3.14.1 dev: true - /@changesets/pre/1.0.13: + /@changesets/pre/1.0.14: resolution: { - integrity: sha512-jrZc766+kGZHDukjKhpBXhBJjVQMied4Fu076y9guY1D3H622NOw8AQaLV3oQsDtKBTrT2AUFjt9Z2Y9Qx+GfA==, + integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==, } dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.20.7 '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.0 + '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 dev: true - /@changesets/read/0.5.8: + /@changesets/read/0.5.9: resolution: { - integrity: sha512-eYaNfxemgX7f7ELC58e7yqQICW5FB7V+bd1lKt7g57mxUrTveYME+JPaBPpYx02nP53XI6CQp6YxnR9NfmFPKw==, + integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==, } dependencies: - '@babel/runtime': 7.20.6 - '@changesets/git': 1.5.0 + '@babel/runtime': 7.20.7 + '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 - '@changesets/parse': 0.3.15 - '@changesets/types': 5.2.0 + '@changesets/parse': 0.3.16 + '@changesets/types': 5.2.1 chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -1263,21 +1234,21 @@ packages: } dev: true - /@changesets/types/5.2.0: + /@changesets/types/5.2.1: resolution: { - integrity: sha512-km/66KOqJC+eicZXsm2oq8A8bVTSpkZJ60iPV/Nl5Z5c7p9kk8xxh6XGRTlnludHldxOOfudhnDN2qPxtHmXzA==, + integrity: sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==, } dev: true - /@changesets/write/0.2.2: + /@changesets/write/0.2.3: resolution: { - integrity: sha512-kCYNHyF3xaId1Q/QE+DF3UTrHTyg3Cj/f++T8S8/EkC+jh1uK2LFnM9h+EzV+fsmnZDrs7r0J4LLpeI/VWC5Hg==, + integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==, } dependencies: - '@babel/runtime': 7.20.6 - '@changesets/types': 5.2.0 + '@babel/runtime': 7.20.7 + '@changesets/types': 5.2.1 fs-extra: 7.0.1 human-id: 1.0.2 prettier: 2.8.1 @@ -1294,6 +1265,35 @@ packages: '@envelop/types': 2.4.0_graphql@15.8.0 graphql: 15.8.0 tslib: 2.4.0 + dev: false + + /@envelop/core/2.6.0_graphql@16.6.0: + resolution: + { + integrity: sha512-yTptKinJN//i6m1kXUbnLBl/FobzddI4ehURAMS08eRUOQwAuXqJU9r8VdTav8nIZLb4t6cuDWFb3n331LiwLw==, + } + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@envelop/types': 2.4.0_graphql@16.6.0 + graphql: 16.6.0 + tslib: 2.4.0 + dev: true + + /@envelop/parser-cache/4.7.0_4hr55tbjlvoppd2sokdhrbpreq: + resolution: + { + integrity: sha512-63NfXDcW/vGn4U6NFxaZ0JbYWAcJb9A6jhTvghsSz1ZS+Dny/ci8bVSgVmM1q+N56hPyGsVPuyI+rIc71mPU5g==, + } + peerDependencies: + '@envelop/core': ^2.6.0 + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@envelop/core': 2.6.0_graphql@16.6.0 + graphql: 16.6.0 + lru-cache: 6.0.0 + tslib: 2.4.1 + dev: true /@envelop/parser-cache/4.7.0_nhznfxrlclsvs4aen6pcdf2xd4: resolution: @@ -1308,6 +1308,7 @@ packages: graphql: 15.8.0 lru-cache: 6.0.0 tslib: 2.4.1 + dev: false /@envelop/types/2.4.0_graphql@15.8.0: resolution: @@ -1319,6 +1320,34 @@ packages: dependencies: graphql: 15.8.0 tslib: 2.4.1 + dev: false + + /@envelop/types/2.4.0_graphql@16.6.0: + resolution: + { + integrity: sha512-pjxS98cDQBS84X29VcwzH3aJ/KiLCGwyMxuj7/5FkdiaCXAD1JEvKEj9LARWlFYj1bY43uII4+UptFebrhiIaw==, + } + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + graphql: 16.6.0 + tslib: 2.4.1 + dev: true + + /@envelop/validation-cache/4.7.0_4hr55tbjlvoppd2sokdhrbpreq: + resolution: + { + integrity: sha512-PzL+GfWJRT+JjsJqZAIxHKEkvkM3hxkeytS5O0QLXT8kURNBV28r+Kdnn2RCF5+6ILhyGpiDb60vaquBi7g4lw==, + } + peerDependencies: + '@envelop/core': ^2.6.0 + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@envelop/core': 2.6.0_graphql@16.6.0 + graphql: 16.6.0 + lru-cache: 6.0.0 + tslib: 2.4.1 + dev: true /@envelop/validation-cache/4.7.0_nhznfxrlclsvs4aen6pcdf2xd4: resolution: @@ -1333,6 +1362,7 @@ packages: graphql: 15.8.0 lru-cache: 6.0.0 tslib: 2.4.1 + dev: false /@esbuild/android-arm/0.15.18: resolution: @@ -1628,6 +1658,20 @@ packages: '@graphql-tools/utils': 9.1.3_graphql@15.8.0 graphql: 15.8.0 tslib: 2.4.1 + dev: false + + /@graphql-tools/merge/8.3.14_graphql@16.6.0: + resolution: + { + integrity: sha512-zV0MU1DnxJLIB0wpL4N3u21agEiYFsjm6DI130jqHpwF0pR9HkF+Ni65BNfts4zQelP0GjkHltG+opaozAJ1NA==, + } + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.1.3_graphql@16.6.0 + graphql: 16.6.0 + tslib: 2.4.1 + dev: true /@graphql-tools/schema/9.0.12_graphql@15.8.0: resolution: @@ -1642,6 +1686,22 @@ packages: graphql: 15.8.0 tslib: 2.4.1 value-or-promise: 1.0.11 + dev: false + + /@graphql-tools/schema/9.0.12_graphql@16.6.0: + resolution: + { + integrity: sha512-DmezcEltQai0V1y96nwm0Kg11FDS/INEFekD4nnVgzBqawvznWqK6D6bujn+cw6kivoIr3Uq//QmU/hBlBzUlQ==, + } + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/merge': 8.3.14_graphql@16.6.0 + '@graphql-tools/utils': 9.1.3_graphql@16.6.0 + graphql: 16.6.0 + tslib: 2.4.1 + value-or-promise: 1.0.11 + dev: true /@graphql-tools/utils/8.13.1_graphql@15.8.0: resolution: @@ -1653,6 +1713,19 @@ packages: dependencies: graphql: 15.8.0 tslib: 2.4.1 + dev: false + + /@graphql-tools/utils/8.13.1_graphql@16.6.0: + resolution: + { + integrity: sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw==, + } + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.6.0 + tslib: 2.4.1 + dev: true /@graphql-tools/utils/9.1.3_graphql@15.8.0: resolution: @@ -1664,6 +1737,19 @@ packages: dependencies: graphql: 15.8.0 tslib: 2.4.1 + dev: false + + /@graphql-tools/utils/9.1.3_graphql@16.6.0: + resolution: + { + integrity: sha512-bbJyKhs6awp1/OmP+WKA1GOyu9UbgZGkhIj5srmiMGLHohEOKMjW784Sk0BZil1w2x95UPu0WHw6/d/HVCACCg==, + } + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 16.6.0 + tslib: 2.4.1 + dev: true /@graphql-typed-document-node/core/3.1.1_graphql@15.8.0: resolution: @@ -1674,6 +1760,18 @@ packages: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 15.8.0 + dev: false + + /@graphql-typed-document-node/core/3.1.1_graphql@16.6.0: + resolution: + { + integrity: sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==, + } + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + graphql: 16.6.0 + dev: true /@graphql-yoga/common/2.12.12_graphql@15.8.0: resolution: @@ -1696,6 +1794,30 @@ packages: tslib: 2.4.1 transitivePeerDependencies: - encoding + dev: false + + /@graphql-yoga/common/2.12.12_graphql@16.6.0: + resolution: + { + integrity: sha512-La2ygIw2qlIJZrRGT4nW70Nam7gQ2xZkOn0FDCnKWSJhQ4nHw4aFAkeHIJdZGK0u2TqtXRrNSAj5cb/TZoqUiQ==, + } + peerDependencies: + graphql: ^15.2.0 || ^16.0.0 + dependencies: + '@envelop/core': 2.6.0_graphql@16.6.0 + '@envelop/parser-cache': 4.7.0_4hr55tbjlvoppd2sokdhrbpreq + '@envelop/validation-cache': 4.7.0_4hr55tbjlvoppd2sokdhrbpreq + '@graphql-tools/schema': 9.0.12_graphql@16.6.0 + '@graphql-tools/utils': 8.13.1_graphql@16.6.0 + '@graphql-typed-document-node/core': 3.1.1_graphql@16.6.0 + '@graphql-yoga/subscription': 2.2.3 + '@whatwg-node/fetch': 0.3.2 + dset: 3.1.2 + graphql: 16.6.0 + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + dev: true /@graphql-yoga/node/2.13.13_graphql@15.8.0: resolution: @@ -1714,6 +1836,26 @@ packages: tslib: 2.4.1 transitivePeerDependencies: - encoding + dev: false + + /@graphql-yoga/node/2.13.13_graphql@16.6.0: + resolution: + { + integrity: sha512-3NmdEq3BkuVLRbo5yUi401sBiwowSKgY8O1DN1RwYdHRr0nu2dXzlYEETf4XLymyP6mKsVfQgsy7HQjwsc1oNw==, + } + peerDependencies: + graphql: ^15.2.0 || ^16.0.0 + dependencies: + '@envelop/core': 2.6.0_graphql@16.6.0 + '@graphql-tools/utils': 8.13.1_graphql@16.6.0 + '@graphql-yoga/common': 2.12.12_graphql@16.6.0 + '@graphql-yoga/subscription': 2.2.3 + '@whatwg-node/fetch': 0.3.2 + graphql: 16.6.0 + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + dev: true /@graphql-yoga/subscription/2.2.3: resolution: @@ -1809,7 +1951,7 @@ packages: } engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.20.7 '@jest/types': 29.3.1 '@jridgewell/trace-mapping': 0.3.17 babel-plugin-istanbul: 6.1.1 @@ -1923,7 +2065,7 @@ packages: integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==, } dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.20.7 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -1935,7 +2077,7 @@ packages: integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==, } dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.20.7 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -1972,25 +2114,25 @@ packages: dev: true optional: true - /@next/env/13.0.7: + /@next/env/13.1.1: resolution: { - integrity: sha512-ZBclBRB7DbkSswXgbJ+muF5RxfgmAuQKAWL8tcm86aZmoiL1ZainxQK0hMcMYdh+IYG8UObAKV2wKB5O+6P4ng==, + integrity: sha512-vFMyXtPjSAiOXOywMojxfKIqE3VWN5RCAx+tT3AS3pcKjMLFTCJFUWsKv8hC+87Z1F4W3r68qTwDFZIFmd5Xkw==, } - /@next/eslint-plugin-next/13.0.7: + /@next/eslint-plugin-next/13.1.1: resolution: { - integrity: sha512-Q/Z0V3D3UpKhhzFU6/s17wD4rqJ+ZDGded8UpqNyzX1nUdD+/PnsZexPhSIZ2Yf/c8QESeirmJVRb3eAfCQkRQ==, + integrity: sha512-SBrOFS8PC3nQ5aeZmawJkjKkWjwK9RoxvBSv/86nZp0ubdoVQoko8r8htALd9ufp16NhacCdqhu9bzZLDWtALQ==, } dependencies: glob: 7.1.7 dev: false - /@next/swc-android-arm-eabi/13.0.7: + /@next/swc-android-arm-eabi/13.1.1: resolution: { - integrity: sha512-QTEamOK/LCwBf05GZ261rULMbZEpE3TYdjHlXfznV+nXwTztzkBNFXwP67gv2wW44BROzgi/vrR9H8oP+J5jxg==, + integrity: sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A==, } engines: { node: '>= 10' } cpu: [arm] @@ -1998,10 +2140,10 @@ packages: requiresBuild: true optional: true - /@next/swc-android-arm64/13.0.7: + /@next/swc-android-arm64/13.1.1: resolution: { - integrity: sha512-wcy2H0Tl9ME8vKy2GnJZ7Mybwys+43F/Eh2Pvph7mSDpMbYBJ6iA0zeY62iYYXxlZhnAID3+h79FUqUEakkClw==, + integrity: sha512-eCiZhTzjySubNqUnNkQCjU3Fh+ep3C6b5DCM5FKzsTH/3Gr/4Y7EiaPZKILbvnXmhWtKPIdcY6Zjx51t4VeTfA==, } engines: { node: '>= 10' } cpu: [arm64] @@ -2009,10 +2151,10 @@ packages: requiresBuild: true optional: true - /@next/swc-darwin-arm64/13.0.7: + /@next/swc-darwin-arm64/13.1.1: resolution: { - integrity: sha512-F/mU7csN1/J2cqXJPMgTQ6MwAbc1pJ6sp6W+X0z5JEY4IFDzxKd3wRc3pCiNF7j8xW381JlNpWxhjCctnNmfaw==, + integrity: sha512-9zRJSSIwER5tu9ADDkPw5rIZ+Np44HTXpYMr0rkM656IvssowPxmhK0rTreC1gpUCYwFsRbxarUJnJsTWiutPg==, } engines: { node: '>= 10' } cpu: [arm64] @@ -2020,10 +2162,10 @@ packages: requiresBuild: true optional: true - /@next/swc-darwin-x64/13.0.7: + /@next/swc-darwin-x64/13.1.1: resolution: { - integrity: sha512-636AuRQynCPnIPRVzcCk5B7OMq9XjaYam2T0HeWUCE6y7EqEO3kxiuZ4QmN81T7A6Ydb+JnivYrLelHXmgdj6A==, + integrity: sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA==, } engines: { node: '>= 10' } cpu: [x64] @@ -2031,10 +2173,10 @@ packages: requiresBuild: true optional: true - /@next/swc-freebsd-x64/13.0.7: + /@next/swc-freebsd-x64/13.1.1: resolution: { - integrity: sha512-92XAMzNgQazowZ9t7uZmHRA5VdBl/SwEdrf5UybdfRovsxB4r3+yJWEvFaqYpSEp0gwndbwLokJdpz7OwFdL3Q==, + integrity: sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA==, } engines: { node: '>= 10' } cpu: [x64] @@ -2042,10 +2184,10 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-arm-gnueabihf/13.0.7: + /@next/swc-linux-arm-gnueabihf/13.1.1: resolution: { - integrity: sha512-3r1CWl5P6I5n5Yxip8EXv/Rfu2Cp6wVmIOpvmczyUR82j+bcMkwPAcUjNkG/vMCagS4xV7NElrcdGb39iFmfLg==, + integrity: sha512-CnsxmKHco9sosBs1XcvCXP845Db+Wx1G0qouV5+Gr+HT/ZlDYEWKoHVDgnJXLVEQzq4FmHddBNGbXvgqM1Gfkg==, } engines: { node: '>= 10' } cpu: [arm] @@ -2053,10 +2195,10 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-arm64-gnu/13.0.7: + /@next/swc-linux-arm64-gnu/13.1.1: resolution: { - integrity: sha512-RXo8tt6ppiwyS6hpDw3JdAjKcdVewsefxnxk9xOH4mRhMyq9V2lQx0e24X/dRiZqkx3jnWReR2WRrUlgN1UkSQ==, + integrity: sha512-JfDq1eri5Dif+VDpTkONRd083780nsMCOKoFG87wA0sa4xL8LGcXIBAkUGIC1uVy9SMsr2scA9CySLD/i+Oqiw==, } engines: { node: '>= 10' } cpu: [arm64] @@ -2064,10 +2206,10 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-arm64-musl/13.0.7: + /@next/swc-linux-arm64-musl/13.1.1: resolution: { - integrity: sha512-RWpnW+bmfXyxyY7iARbueYDGuIF+BEp3etLeYh/RUNHb9PhOHLDgJOG8haGSykud3a6CcyBI8hEjqOhoObaDpw==, + integrity: sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA==, } engines: { node: '>= 10' } cpu: [arm64] @@ -2075,10 +2217,10 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-x64-gnu/13.0.7: + /@next/swc-linux-x64-gnu/13.1.1: resolution: { - integrity: sha512-/ygUIiMMTYnbKlFs5Ba9J5k/tNxFWy8eI1bBF8UuMTvV8QJHl/aLDiA5dwsei2kk99/cu3eay62JnJXkSk3RSQ==, + integrity: sha512-nnjuBrbzvqaOJaV+XgT8/+lmXrSCOt1YYZn/irbDb2fR2QprL6Q7WJNgwsZNxiLSfLdv+2RJGGegBx9sLBEzGA==, } engines: { node: '>= 10' } cpu: [x64] @@ -2086,10 +2228,10 @@ packages: requiresBuild: true optional: true - /@next/swc-linux-x64-musl/13.0.7: + /@next/swc-linux-x64-musl/13.1.1: resolution: { - integrity: sha512-dLzr6AL77USJN0ejgx5AS8O8SbFlbYTzs0XwAWag4oQpUG2p3ARvxwQgYQ0Z+6EP0zIRZ/XfLkN/mhsyi3m4PA==, + integrity: sha512-CM9xnAQNIZ8zf/igbIT/i3xWbQZYaF397H+JroF5VMOCUleElaMdQLL5riJml8wUfPoN3dtfn2s4peSr3azz/g==, } engines: { node: '>= 10' } cpu: [x64] @@ -2097,10 +2239,10 @@ packages: requiresBuild: true optional: true - /@next/swc-win32-arm64-msvc/13.0.7: + /@next/swc-win32-arm64-msvc/13.1.1: resolution: { - integrity: sha512-+vFIVa82AwqFkpFClKT+n73fGxrhAZ2u1u3mDYEBdxO6c9U4Pj3S5tZFsGFK9kLT/bFvf/eeVOICSLCC7MSgJQ==, + integrity: sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ==, } engines: { node: '>= 10' } cpu: [arm64] @@ -2108,10 +2250,10 @@ packages: requiresBuild: true optional: true - /@next/swc-win32-ia32-msvc/13.0.7: + /@next/swc-win32-ia32-msvc/13.1.1: resolution: { - integrity: sha512-RNLXIhp+assD39dQY9oHhDxw+/qSJRARKhOFsHfOtf8yEfCHqcKkn3X/L+ih60ntaEqK294y1WkMk6ylotsxwA==, + integrity: sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg==, } engines: { node: '>= 10' } cpu: [ia32] @@ -2119,10 +2261,10 @@ packages: requiresBuild: true optional: true - /@next/swc-win32-x64-msvc/13.0.7: + /@next/swc-win32-x64-msvc/13.1.1: resolution: { - integrity: sha512-kvdnlLcrnEq72ZP0lqe2Z5NqvB9N5uSCvtXJ0PhKvNncWWd0fEG9Ec9erXgwCmVlM2ytw41k9/uuQ+SVw4Pihw==, + integrity: sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg==, } engines: { node: '>= 10' } cpu: [x64] @@ -2394,7 +2536,7 @@ packages: integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==, } dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.20.7 dev: false /@types/braces/3.0.1: @@ -2565,7 +2707,7 @@ packages: } deprecated: This is a stub types definition. next provides its own type definitions, so you do not need this installed. dependencies: - next: 13.0.7_biqbaboplfbrettd7655fr4n2y + next: 13.1.1_biqbaboplfbrettd7655fr4n2y transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -2604,10 +2746,10 @@ packages: dev: true optional: true - /@types/prettier/2.7.1: + /@types/prettier/2.7.2: resolution: { - integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==, + integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==, } dev: false @@ -2881,7 +3023,7 @@ packages: integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==, } dependencies: - '@babel/parser': 7.20.5 + '@babel/parser': 7.20.7 '@vue/shared': 3.2.45 estree-walker: 2.0.2 source-map: 0.6.1 @@ -2903,7 +3045,7 @@ packages: integrity: sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==, } dependencies: - '@babel/parser': 7.20.5 + '@babel/parser': 7.20.7 '@vue/compiler-core': 3.2.45 '@vue/compiler-dom': 3.2.45 '@vue/compiler-ssr': 3.2.45 @@ -2931,7 +3073,7 @@ packages: integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==, } dependencies: - '@babel/parser': 7.20.5 + '@babel/parser': 7.20.7 '@vue/compiler-core': 3.2.45 '@vue/shared': 3.2.45 estree-walker: 2.0.2 @@ -3147,8 +3289,8 @@ packages: } engines: { node: '>=6.0' } dependencies: - '@babel/runtime': 7.20.6 - '@babel/runtime-corejs3': 7.20.6 + '@babel/runtime': 7.20.7 + '@babel/runtime-corejs3': 7.20.7 /array-includes/3.1.6: resolution: @@ -3235,7 +3377,6 @@ packages: is-nan: 1.3.2 object-is: 1.1.5 util: 0.12.5 - dev: false /assertion-error/1.1.0: resolution: @@ -3258,7 +3399,6 @@ packages: engines: { node: '>=4' } dependencies: tslib: 2.4.1 - dev: false /astral-regex/2.0.0: resolution: @@ -3281,7 +3421,6 @@ packages: integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==, } engines: { node: '>= 0.4' } - dev: false /axe-core/4.6.1: resolution: @@ -3312,7 +3451,7 @@ packages: - supports-color dev: false - /babel-preset-current-node-syntax/1.0.1_@babel+core@7.20.5: + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.20.7: resolution: { integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==, @@ -3320,19 +3459,19 @@ packages: peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.5 - '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.5 - '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.20.5 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.5 + '@babel/core': 7.20.7 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.7 + '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.7 + '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.20.7 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.7 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.7 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.7 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.7 dev: false /bail/1.0.5: @@ -3434,9 +3573,9 @@ packages: engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } hasBin: true dependencies: - caniuse-lite: 1.0.30001439 + caniuse-lite: 1.0.30001441 electron-to-chromium: 1.4.284 - node-releases: 2.0.7 + node-releases: 2.0.8 update-browserslist-db: 1.0.10_browserslist@4.21.4 /bser/2.1.1: @@ -3522,10 +3661,10 @@ packages: } engines: { node: '>=6' } - /caniuse-lite/1.0.30001439: + /caniuse-lite/1.0.30001441: resolution: { - integrity: sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==, + integrity: sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==, } /ccount/1.1.0: @@ -4076,7 +4215,7 @@ packages: supports-color: optional: true dependencies: - ms: 2.1.3 + ms: 2.1.2 /debug/4.3.4: resolution: @@ -4127,6 +4266,22 @@ packages: engines: { node: '>=0.10.0' } dev: true + /decode-uri-component/0.2.2: + resolution: + { + integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==, + } + engines: { node: '>=0.10' } + dev: true + + /decode-uri-component/0.4.1: + resolution: + { + integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==, + } + engines: { node: '>=14.16' } + dev: false + /deep-eql/4.1.3: resolution: { @@ -4414,7 +4569,6 @@ packages: { integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==, } - dev: false /es6-promise/3.3.1: resolution: @@ -4751,10 +4905,10 @@ packages: } engines: { node: '>=10' } - /eslint-config-next/13.0.7_ha6vam6werchizxrnqvarmz2zu: + /eslint-config-next/13.1.1_ha6vam6werchizxrnqvarmz2zu: resolution: { - integrity: sha512-X7DB7iDJ9iHi5DAZbnFdWm4M0dwarj5h5y6Vpm9INCYzFgAwSWslq3v0qjYEjtUO5IQ8n1WK6IU5FkOQ2HBhOA==, + integrity: sha512-/5S2XGWlGaiqrRhzpn51ux5JUSLwx8PVK2keLi5xk7QmhfYB8PqE6R6SlVw6hgnf/VexvUXSrlNJ/su00NhtHQ==, } peerDependencies: eslint: ^7.23.0 || ^8.0.0 @@ -4763,7 +4917,7 @@ packages: typescript: optional: true dependencies: - '@next/eslint-plugin-next': 13.0.7 + '@next/eslint-plugin-next': 13.1.1 '@rushstack/eslint-patch': 1.2.0 '@typescript-eslint/parser': 5.46.1_ha6vam6werchizxrnqvarmz2zu eslint: 8.29.0 @@ -4988,7 +5142,7 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.20.7 aria-query: 4.2.2 array-includes: 3.1.6 ast-types-flow: 0.0.7 @@ -5331,7 +5485,6 @@ packages: { integrity: sha512-woY0RUD87WzMBUiZLx8NsYr23N5BKsOMZHhu2hoNRVh6NXGfoiT1KOL8G3UHlJAnEDGmfa5ubNA/AacfG+Kb0g==, } - dev: false /esutils/2.0.3: resolution: @@ -5522,6 +5675,14 @@ packages: dependencies: to-regex-range: 5.0.1 + /filter-obj/5.1.0: + resolution: + { + integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==, + } + engines: { node: '>=14.16' } + dev: false + /find-cache-dir/3.3.2: resolution: { @@ -5594,7 +5755,6 @@ packages: } dependencies: is-callable: 1.2.7 - dev: false /form-data-encoder/1.7.2: resolution: @@ -5715,6 +5875,16 @@ packages: integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, } + /gatsby-remark-code-titles/1.1.0: + resolution: + { + integrity: sha512-RuNqziXi99eBIj5NJP0TgdzAxzWFL+ArGRb3961Ff9Tto/nCvmyqR1qySaWKXtkOgeqoVUlqAFNUCyEAyNuc8w==, + } + dependencies: + query-string: 6.0.0 + unist-util-visit: 1.3.1 + dev: true + /gauge/3.0.2: resolution: { @@ -5859,7 +6029,7 @@ packages: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 5.1.1 + minimatch: 5.1.2 once: 1.4.0 dev: false @@ -5951,7 +6121,7 @@ packages: graphql: 15.8.0 dev: false - /graphql-tag/2.12.6_graphql@15.8.0: + /graphql-tag/2.12.6_graphql@16.6.0: resolution: { integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==, @@ -5960,7 +6130,7 @@ packages: peerDependencies: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: - graphql: 15.8.0 + graphql: 16.6.0 tslib: 2.4.1 dev: false @@ -5976,6 +6146,18 @@ packages: graphql: 15.8.0 dev: false + /graphql-ws/5.11.2_graphql@16.6.0: + resolution: + { + integrity: sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w==, + } + engines: { node: '>=10' } + peerDependencies: + graphql: '>=0.11 <=16' + dependencies: + graphql: 16.6.0 + dev: false + /graphql/15.8.0: resolution: { @@ -5983,6 +6165,13 @@ packages: } engines: { node: '>= 10.x' } + /graphql/16.6.0: + resolution: + { + integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==, + } + engines: { node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0 } + /hard-rejection/2.1.0: resolution: { @@ -6258,7 +6447,6 @@ packages: dependencies: call-bind: 1.0.2 has-tostringtag: 1.0.0 - dev: false /is-arrayish/0.2.1: resolution: @@ -6394,7 +6582,6 @@ packages: engines: { node: '>= 0.4' } dependencies: has-tostringtag: 1.0.0 - dev: false /is-glob/4.0.3: resolution: @@ -6422,7 +6609,6 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - dev: false /is-negative-zero/2.0.2: resolution: @@ -6553,7 +6739,6 @@ packages: for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 - dev: false /is-weakref/1.0.2: resolution: @@ -6618,8 +6803,8 @@ packages: } engines: { node: '>=8' } dependencies: - '@babel/core': 7.20.5 - '@babel/parser': 7.20.5 + '@babel/core': 7.20.7 + '@babel/parser': 7.20.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -6723,18 +6908,18 @@ packages: } engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } dependencies: - '@babel/core': 7.20.5 - '@babel/generator': 7.20.5 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5 - '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.5 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/core': 7.20.7 + '@babel/generator': 7.20.7 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.7 + '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.7 + '@babel/traverse': 7.20.10 + '@babel/types': 7.20.7 '@jest/expect-utils': 29.3.1 '@jest/transform': 29.3.1 '@jest/types': 29.3.1 '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.1 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.5 + '@types/prettier': 2.7.2 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.7 chalk: 4.1.2 expect: 29.3.1 graceful-fs: 4.2.10 @@ -6846,10 +7031,10 @@ packages: dependencies: minimist: 1.2.7 - /json5/2.2.1: + /json5/2.2.2: resolution: { - integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==, + integrity: sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==, } engines: { node: '>=6' } hasBin: true @@ -7101,6 +7286,14 @@ packages: yallist: 2.1.2 dev: true + /lru-cache/5.1.1: + resolution: + { + integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, + } + dependencies: + yallist: 3.1.1 + /lru-cache/6.0.0: resolution: { @@ -7333,10 +7526,10 @@ packages: dependencies: brace-expansion: 1.1.11 - /minimatch/5.1.1: + /minimatch/5.1.2: resolution: { - integrity: sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==, + integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==, } engines: { node: '>=10' } dependencies: @@ -7444,12 +7637,6 @@ packages: integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==, } - /ms/2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } - /nanoid/3.3.4: resolution: { @@ -7471,10 +7658,10 @@ packages: integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, } - /next/13.0.7_biqbaboplfbrettd7655fr4n2y: + /next/13.1.1_biqbaboplfbrettd7655fr4n2y: resolution: { - integrity: sha512-YfTifqX9vfHm+rSU/H/3xvzOHDkYuMuh4wsvTjiqj9h7qHEF7KHB66X4qrH96Po+ohdid4JY8YVGPziDwdXL0A==, + integrity: sha512-R5eBAaIa3X7LJeYvv1bMdGnAVF4fVToEjim7MkflceFPuANY3YyvFxXee/A+acrSYwYPvOvf7f6v/BM/48ea5w==, } engines: { node: '>=14.6.0' } hasBin: true @@ -7492,27 +7679,27 @@ packages: sass: optional: true dependencies: - '@next/env': 13.0.7 + '@next/env': 13.1.1 '@swc/helpers': 0.4.14 - caniuse-lite: 1.0.30001439 + caniuse-lite: 1.0.30001441 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - styled-jsx: 5.1.0_react@18.2.0 + styled-jsx: 5.1.1_react@18.2.0 optionalDependencies: - '@next/swc-android-arm-eabi': 13.0.7 - '@next/swc-android-arm64': 13.0.7 - '@next/swc-darwin-arm64': 13.0.7 - '@next/swc-darwin-x64': 13.0.7 - '@next/swc-freebsd-x64': 13.0.7 - '@next/swc-linux-arm-gnueabihf': 13.0.7 - '@next/swc-linux-arm64-gnu': 13.0.7 - '@next/swc-linux-arm64-musl': 13.0.7 - '@next/swc-linux-x64-gnu': 13.0.7 - '@next/swc-linux-x64-musl': 13.0.7 - '@next/swc-win32-arm64-msvc': 13.0.7 - '@next/swc-win32-ia32-msvc': 13.0.7 - '@next/swc-win32-x64-msvc': 13.0.7 + '@next/swc-android-arm-eabi': 13.1.1 + '@next/swc-android-arm64': 13.1.1 + '@next/swc-darwin-arm64': 13.1.1 + '@next/swc-darwin-x64': 13.1.1 + '@next/swc-freebsd-x64': 13.1.1 + '@next/swc-linux-arm-gnueabihf': 13.1.1 + '@next/swc-linux-arm64-gnu': 13.1.1 + '@next/swc-linux-arm64-musl': 13.1.1 + '@next/swc-linux-x64-gnu': 13.1.1 + '@next/swc-linux-x64-musl': 13.1.1 + '@next/swc-win32-arm64-msvc': 13.1.1 + '@next/swc-win32-ia32-msvc': 13.1.1 + '@next/swc-win32-x64-msvc': 13.1.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -7575,10 +7762,10 @@ packages: } dev: false - /node-releases/2.0.7: + /node-releases/2.0.8: resolution: { - integrity: sha512-EJ3rzxL9pTWPjk5arA0s0dgXpnyiAbJDE6wHT62g7VsgrgQgmmZ+Ru++M1BFofncWja+Pnn3rEr3fieRySAdKQ==, + integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==, } /nopt/5.0.0: @@ -7686,7 +7873,6 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - dev: false /object-keys/1.1.1: resolution: @@ -8194,6 +8380,29 @@ packages: } engines: { node: '>=6.0.0' } + /query-string/6.0.0: + resolution: + { + integrity: sha512-QKgEnEoiigFPShVqMFp91YPaYGSaR4j3VIMVl+yKEm8jSgZzOuoFvY4s5mQxHAA/j5pexab5DtZv6W+JpQfjhw==, + } + engines: { node: '>=6' } + dependencies: + decode-uri-component: 0.2.2 + strict-uri-encode: 2.0.0 + dev: true + + /query-string/8.1.0: + resolution: + { + integrity: sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw==, + } + engines: { node: '>=14.16' } + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + dev: false + /queue-microtask/1.2.3: resolution: { @@ -8314,7 +8523,6 @@ packages: esprima: 4.0.1 source-map: 0.6.1 tslib: 2.4.1 - dev: false /redent/3.0.0: resolution: @@ -8395,10 +8603,10 @@ packages: integrity: sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==, } dependencies: - '@babel/core': 7.12.9 + '@babel/core': 7.20.7 '@babel/helper-plugin-utils': 7.10.4 - '@babel/plugin-proposal-object-rest-spread': 7.12.1_@babel+core@7.12.9 - '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.12.9 + '@babel/plugin-proposal-object-rest-spread': 7.12.1_@babel+core@7.20.7 + '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.20.7 '@mdx-js/util': 1.6.22 is-alphabetical: 1.0.4 remark-parse: 8.0.3 @@ -8967,6 +9175,14 @@ packages: } dev: true + /split-on-first/3.0.0: + resolution: + { + integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==, + } + engines: { node: '>=12' } + dev: false + /sprintf-js/1.0.3: resolution: { @@ -9007,6 +9223,14 @@ packages: } engines: { node: '>=10.0.0' } + /strict-uri-encode/2.0.0: + resolution: + { + integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==, + } + engines: { node: '>=4' } + dev: true + /string-argv/0.3.1: resolution: { @@ -9162,10 +9386,10 @@ packages: acorn: 8.8.1 dev: true - /styled-jsx/5.1.0_react@18.2.0: + /styled-jsx/5.1.1_react@18.2.0: resolution: { - integrity: sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==, + integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==, } engines: { node: '>= 12.0.0' } peerDependencies: @@ -9223,14 +9447,14 @@ packages: } engines: { node: '>= 0.4' } - /svelte-check/2.10.2_svelte@3.55.0: + /svelte-check/3.0.1_svelte@3.55.0: resolution: { - integrity: sha512-h1Tuiir0m8J5yqN+Vx6qgKKk1L871e6a9o7rMwVWfu8Qs6Wg7x2R+wcxS3SO3VpW5JCxCat90rxPsZMYgz+HaQ==, + integrity: sha512-7YpHYWv6V2qhcvVeAlXixUPAlpLCXB1nZEQK0EItB3PtuYmENhKclbc5uKSJTodTwWR1y+4stKGcbH30k6A3Yw==, } hasBin: true peerDependencies: - svelte: ^3.24.0 + svelte: ^3.55.0 dependencies: '@jridgewell/trace-mapping': 0.3.17 chokidar: 3.5.3 @@ -9239,13 +9463,12 @@ packages: picocolors: 1.0.0 sade: 1.8.1 svelte: 3.55.0 - svelte-preprocess: 4.10.7_niwyv7xychq2ag6arq5eqxbomm + svelte-preprocess: 5.0.0_niwyv7xychq2ag6arq5eqxbomm typescript: 4.9.4 transitivePeerDependencies: - '@babel/core' - coffeescript - less - - node-sass - postcss - postcss-load-config - pug @@ -9274,60 +9497,6 @@ packages: zencrypt: 0.0.7 dev: true - /svelte-preprocess/4.10.7_niwyv7xychq2ag6arq5eqxbomm: - resolution: - { - integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==, - } - engines: { node: '>= 9.11.2' } - requiresBuild: true - peerDependencies: - '@babel/core': ^7.10.2 - coffeescript: ^2.5.1 - less: ^3.11.3 || ^4.0.0 - node-sass: '*' - postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 - pug: ^3.0.0 - sass: ^1.26.8 - stylus: ^0.55.0 - sugarss: ^2.0.0 - svelte: ^3.23.0 - typescript: ^3.9.5 || ^4.0.0 - peerDependenciesMeta: - '@babel/core': - optional: true - coffeescript: - optional: true - less: - optional: true - node-sass: - optional: true - postcss: - optional: true - postcss-load-config: - optional: true - pug: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - typescript: - optional: true - dependencies: - '@types/pug': 2.0.6 - '@types/sass': 1.43.1 - detect-indent: 6.1.0 - magic-string: 0.25.9 - sorcery: 0.10.0 - strip-indent: 3.0.0 - svelte: 3.55.0 - typescript: 4.9.4 - dev: true - /svelte-preprocess/5.0.0_niwyv7xychq2ag6arq5eqxbomm: resolution: { @@ -9859,7 +10028,6 @@ packages: integrity: sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==, } dependencies: - '@types/unist': 2.0.6 bail: 1.0.5 extend: 3.0.2 is-buffer: 2.0.5 @@ -9875,7 +10043,6 @@ packages: integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==, } dependencies: - '@types/unist': 2.0.6 bail: 1.0.5 extend: 3.0.2 is-buffer: 2.0.5 @@ -9885,6 +10052,13 @@ packages: dev: true optional: true + /unist-util-is/2.1.3: + resolution: + { + integrity: sha512-4WbQX2iwfr/+PfM4U3zd2VNXY+dWtZsN1fLnWEi2QQXA4qyDYAZcDMfXUX0Cu6XZUHHAO9q4nyxxLT4Awk1qUA==, + } + dev: true + /unist-util-is/4.1.0: resolution: { @@ -9948,6 +10122,15 @@ packages: unist-util-is: 5.1.1 dev: false + /unist-util-visit/1.3.1: + resolution: + { + integrity: sha512-0fdB9EQJU0tho5tK0VzOJzAQpPv2LyLZ030b10GxuzAWEfvd54mpY7BMjQ1L69k2YNvL+SvxRzH0yUIehOO8aA==, + } + dependencies: + unist-util-is: 2.1.3 + dev: true + /unist-util-visit/2.0.3: resolution: { @@ -10026,7 +10209,6 @@ packages: is-generator-function: 1.0.10 is-typed-array: 1.1.10 which-typed-array: 1.1.9 - dev: false /uuid/8.3.2: resolution: @@ -10417,7 +10599,6 @@ packages: gopd: 1.0.1 has-tostringtag: 1.0.0 is-typed-array: 1.1.10 - dev: false /which/1.3.1: resolution: @@ -10542,6 +10723,12 @@ packages: } dev: true + /yallist/3.1.1: + resolution: + { + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, + } + /yallist/4.0.0: resolution: { diff --git a/site/package.json b/site/package.json index 4045d9e466..894240ba4e 100644 --- a/site/package.json +++ b/site/package.json @@ -16,6 +16,7 @@ "src/**/*.svelte": "npm run format" }, "devDependencies": { + "@babel/parser": "^7.20.7", "@sveltejs/adapter-vercel": "1.0.0", "@sveltejs/kit": "1.0.0", "@typescript-eslint/eslint-plugin": "^5.10.1", @@ -23,11 +24,14 @@ "eslint": "^8.12.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^4.0.0", + "estree-walker": "^3.0.1", + "gatsby-remark-code-titles": "^1.1.0", "husky": "^7.0.4", "lint-staged": "^12.3.4", - "prettier": "^2.5.1", - "prettier-plugin-svelte": "^2.7.0", - "svelte-check": "^2.8.0", + "prettier": "^2.8.1", + "prettier-plugin-svelte": "^2.9.0", + "recast": "^0.23.1", + "svelte-check": "^3.0.1", "svelte-kit-cookie-session": "3.0.6", "tslib": "^2.3.1", "typescript": "^4.9" @@ -37,14 +41,16 @@ "flexsearch": "^0.7.31", "lodash": "^4.17.21", "mdsvex": "^0.10.6", + "node-html-parser": "^5.4.1", "prism-svelte": "^0.5.0", "prismjs": "^1.28.0", - "vite-plugin-replace": "^0.1.1", - "node-html-parser": "^5.4.1", + "query-string": "^8.1.0", "rehype-autolink-headings": "^6.1.1", "rehype-slug": "^5.0.1", "svelte": "^3.55.0", "svelte-preprocess": "^5.0.0", - "vite": "^4.0.1" + "unist-util-visit": "^4.1.1", + "vite": "^4.0.1", + "vite-plugin-replace": "^0.1.1" } } diff --git a/site/plugins/code-titles.js b/site/plugins/code-titles.js new file mode 100644 index 0000000000..e7b4943274 --- /dev/null +++ b/site/plugins/code-titles.js @@ -0,0 +1,54 @@ +import { visit } from 'unist-util-visit' +import { format } from './docs-lang.js' + +export function codeTitles() { + return function gatsbyRemarkCodeTitles(tree, file) { + visit(tree, 'code', (node, index, parent) => { + const [language, paramsStr] = (node.lang || '').split(':') + + const params = + paramsStr?.split('&').reduce((acc, param) => { + const [title, value] = param.split('=') + return { + ...acc, + [title]: value + } + }, {}) ?? {} + + if (!language || !paramsStr) { + return + } + + let extraClass = '' + if (params.typescriptToggle) { + extraClass = + params.typescriptToggle === 'true' ? 'example-typescript' : 'example-javascript' + } + if (!params.title) { + extraClass += ' empty' + } + + const titleNode = { + type: 'html', + value: ` +
${params.title ?? ''}
+ `.trim() + } + + /* + * Splice a node back into the Markdown AST with custom title + */ + parent.children.splice(index, 0, titleNode) + + // remove any prettier ignore lines + node.value = node.value.replaceAll(/\s+\/\/ prettier-ignore/g, '\n').replaceAll('\t', ' ') + + /* + * Reset to just the language + */ + node.lang = language + }) + + return tree + } +} diff --git a/site/plugins/docs-lang.js b/site/plugins/docs-lang.js new file mode 100644 index 0000000000..3104a0a0c2 --- /dev/null +++ b/site/plugins/docs-lang.js @@ -0,0 +1,252 @@ +// this preprocessor is responsible for leaving 2 different code blocks behind +// the UI will hide/show the appropriate one depending on which language the user +// has selected + +import * as recast from 'recast' +import * as typeScriptParser from 'recast/parsers/typescript.js' +import prettier from 'prettier' + +const AST = recast.types.builders + +export default { + async markup({ content, filename }) { + // only consider .svx files + if (!filename.endsWith('.svx')) { + return + } + + // instead of getting super fancy with an AST, we're just going to + // do operations on the string content. + + // the transformation we use depends on the matching codeblock + const transformation = { + typescript: transformTypescript, + svelte: transformSvelte + } + + // look for each language we care about + for (const [language, transform] of Object.entries(transformation)) { + // in order to add the codeblocks back, we need to keep track of the list + // in reverse order (so the index is always valid) + const newBlocks = [] + + // our goal is to look for the content between ```typescript and ``` + for (const match of [...content.matchAll(new RegExp('```' + language, 'gi'))]) { + // find the end of the codeblock + for (let endIndex = match.index + 3; endIndex < content.length; endIndex++) { + // look for the index where the 3 characters are ``` + if (content.slice(endIndex, endIndex + 3) !== '```') { + continue + } + // we actually need to treat the block as ending 2 indices later + endIndex += 3 + + // the content of the block is between the two indices + const blockContent = content.slice(match.index, endIndex) + + // only add something if the typescript toggle is enabled + if (!blockContent.includes('typescriptToggle=true')) { + break + } + + // if the language + + // push the new block at the beginning + newBlocks.unshift({ + index: endIndex, + // transform the typescript source into the javascript equivalent + block: await transform(blockContent) + }) + + // we're done processing this block + break + } + } + + // now that we have the list of codeblocks, insert them into the original script + for (const { index, block } of newBlocks) { + content = insert(content, block, index) + } + } + + return { + code: content + } + } +} + +async function transformSvelte(content) { + // if we have a typescript script, there's something to do + if (content.includes('' + + // the content that needs processing is betetween the end of the open and the start of the close + const start = content.indexOf(open) + open.length + const end = content.indexOf(close) + + // transform the content between the tags + let transformed = (await transformTypescript(content.substring(start, end))).trim() + '\n' + // the transformation needs to be indented one level + transformed = ' ' + transformed.replaceAll('\n', '\n ') + + // add the transformed content to the script + content = insert(content, transformed, start, end) + // the final closing tag needs to be outdented + .replace(' ', '') + + // the first thing we want to do is flag the toggle as off + content = content.replace('typescriptToggle=true', 'typescriptToggle=false') + // remove the lang="ts" portion + content = content.replace(' lang="ts"', '') + } + + return content +} + +export async function transformTypescript(content) { + // the first thing we need to do is mark this as a javascript plugin instead + content = content.replace('```typescript', '```javascript') + content = content.replace('.ts', '.js') + content = content.replace('typescriptToggle=true', 'typescriptToggle=false') + + // the actual source is between the first and last line break. we'll leave the first line break + // in place so that we can use them as markers for every line in the following loop + const start = content.indexOf('\n') + const end = content.lastIndexOf('\n') + const source = content.substring(start, end) + + // we're going to build up the new version as a separate list + const transformed = [] + + // keep track of every type import and where it comes from so we can add comments + // to the javascript source + const importSource = {} + + // loop over top level statement + for (const statement of await parseJS(source).body) { + // if the statement is a type import, we need to track where it came from + // and not add it to the final result + if (statement.type === 'ImportDeclaration' && statement.importKind === 'type') { + let source = statement.source.value + for (const { imported } of statement.specifiers) { + importSource[imported.name] = source + } + continue + } + + // if we are looking at a variable declaration, we need to strip the type declaration and + // add a comment over it + if (statement.type === 'VariableDeclaration') { + transformVariable(statement, statement, importSource) + } + + // if we are looking at an exported variable, we need to transform the underlying statement + if (statement.type === 'ExportNamedDeclaration') { + if (statement.declaration.type === 'VariableDeclaration') { + transformVariable(statement.declaration, statement, importSource) + } else if (statement.declaration.type === 'FunctionDeclaration') { + transformFunction(statement.declaration, statement, importSource) + } + } + + // the statement is okay to add to the list + transformed.push(statement) + } + + return ( + content.substring(0, start) + + '\n' + + format(recast.print(AST.program(transformed)).code) + + '\n' + + content.substring(end).trim() + ) +} + +// transform a variable declaration +function transformVariable(statement, parent, importPaths) { + if (statement.declarations[0] && statement.declarations[0].id.typeAnnotation) { + const declaration = statement.declarations[0] + + // the type to assign to the variable + let targetType = '' + + // dry up the type annotation reference + const typeAnnotation = declaration.id.typeAnnotation.typeAnnotation + + // if the type it was declared as a reference to another type + if (typeAnnotation.typeName) { + const typeName = typeAnnotation.typeName.name + const source = importPaths[typeName] + targetType = `import('${source}').${typeName}` + } + // the type could be declared as a number + else { + targetType = recast.print(typeAnnotation).code + } + + // remove the type annotation + declaration.id.typeAnnotation = null + + parent.comments = [...(parent.comments ?? []), AST.commentBlock(` @type { ${targetType} } `)] + } +} + +function transformFunction(statement, parent, importPaths) { + // build up a list of the params for the function + const params = [] + + for (const param of statement.params) { + // if the param has a type we need to add it to the list + if (param.typeAnnotation) { + params.push({ + name: param.name, + type: param.typeAnnotation.typeAnnotation.typeName.name + }) + + // clear the annotation + param.typeAnnotation = null + } + } + + // if we have parameters in function signature we need to build up the comment + if (params.length > 0) { + // create the comment + const comment = params + .map( + (param) => + `@param { import('${importPaths[param.type]}').${param.type} } ${param.name ?? ''}` + ) + .join('\n') + + parent.comments = [...(parent.comments ?? []), commentBlock(comment)] + } +} + +function parseJS(str) { + return recast.parse(str || '', { + parser: typeScriptParser + }).program +} + +function commentBlock(comment) { + // every new line need a new line, an asterix and a space + return AST.commentBlock('*\n * ' + comment.replaceAll('\n', '\n * ') + '\n ') +} + +function insert(str, content, start, finish = start) { + return str.substring(0, start) + '\n' + content + str.substring(finish) +} + +export function format(str) { + return prettier.format(str, { + tabWidth: 4, + semi: false, + singleQuote: true, + printWidth: 100, + plugins: ['prettier-plugin-svelte'], + parser: 'babel-ts' + }) +} diff --git a/site/plugins/docs-lang.test.js b/site/plugins/docs-lang.test.js new file mode 100644 index 0000000000..90e297fb05 --- /dev/null +++ b/site/plugins/docs-lang.test.js @@ -0,0 +1,72 @@ +import { test, expect } from 'vitest' +import { transformTypescript } from './docs-lang' + +test('change file title', async function () { + await expect( + transformTypescript(`\`\`\`typescript:title=route/to/typescript.ts + console.log('hello') + \`\`\``) + ).resolves.toMatchInlineSnapshot(` + "\`\`\`javascript:title=route/to/typescript.js + console.log('hello') + + \`\`\`" + `) +}) + +test('strip type declarations from variable declarations', async function () { + await expect( + transformTypescript(`\`\`\`typescript + import type { Type } from './source' + const Foo: Type = () => { + console.log('hello') + } + \`\`\``) + ).resolves.toMatchInlineSnapshot(` + "\`\`\`javascript + /* @type { import('./source').Type } */ + const Foo = () => { + console.log('hello') + } + + \`\`\`" + `) +}) + +test('strip type declarations from exported variable declarations', async function () { + await expect( + transformTypescript(`\`\`\`typescript + import type { Type } from './source' + export const Foo: Type = () => { + console.log('hello') + } + \`\`\``) + ).resolves.toMatchInlineSnapshot(` + "\`\`\`javascript + /* @type { import('./source').Type } */ + export const Foo = () => { + console.log('hello') + } + + \`\`\`" + `) +}) + +test('function arguments', async function () { + await expect( + transformTypescript(`\`\`\`typescript + import type { Type } from './source' + export function test(event: Type) { + + } + \`\`\``) + ).resolves.toMatchInlineSnapshot(` + "\`\`\`javascript + /** + * @param { import('./source').Type } event + */ + export function test(event) {} + + \`\`\`" + `) +}) diff --git a/site/src/components/ThemeSwitcher.svelte b/site/src/components/ThemeSwitcher.svelte deleted file mode 100644 index b04b73a635..0000000000 --- a/site/src/components/ThemeSwitcher.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - {#if ui_theme === 1} - - - {:else} - - - {/if} - - - -
- -
- - diff --git a/site/src/components/Toolbar.svelte b/site/src/components/Toolbar.svelte new file mode 100644 index 0000000000..f977ab1da6 --- /dev/null +++ b/site/src/components/Toolbar.svelte @@ -0,0 +1,185 @@ + + + + + + {#if lang === 'ts'} + + {:else} + + {/if} + + {#if ui_theme === 1} + + + {:else} + + + {/if} + + +
+ + +
+ + diff --git a/site/src/components/index.js b/site/src/components/index.js index 2ff0047e8e..e84c22dd69 100644 --- a/site/src/components/index.js +++ b/site/src/components/index.js @@ -6,4 +6,5 @@ export { default as DeepDive } from './DeepDive.svelte' export { default as Transformation } from './Transformation.svelte' export { default as Warning } from './Warning.svelte' export { default as Highlight } from './Highlight.svelte' + export * from './search' diff --git a/site/src/components/search/SearchInput.svelte b/site/src/components/search/SearchInput.svelte index aee92b0e89..1875b7a529 100644 --- a/site/src/components/search/SearchInput.svelte +++ b/site/src/components/search/SearchInput.svelte @@ -41,6 +41,12 @@ outline: none; } + @media (max-width: 1000px) { + button { + flex-grow: 1; + } + } + span { margin-left: 10px; margin-top: 4px; diff --git a/site/src/layouts/_page.svelte b/site/src/layouts/_page.svelte index a7dfa28d94..04eab06f74 100644 --- a/site/src/layouts/_page.svelte +++ b/site/src/layouts/_page.svelte @@ -4,7 +4,7 @@ import { onMount } from 'svelte' import throttle from 'lodash/throttle.js' import { browser } from '$app/environment' - import ThemeSwitcher from '~/components/ThemeSwitcher.svelte' + import Toolbar from '~/components/Toolbar.svelte' export let title = '' export let link = '' @@ -13,9 +13,13 @@ export let data + // pull the values out of the loader and configure the toolbar $: ui_theme = browser ? parseInt(document.cookie.match('(^|;)\\s*' + 'ui_theme' + '\\s*=\\s*([^;]+)')?.pop() || '0') : data?.ui_theme + $: lang = browser + ? document.cookie.match('(^|;)\\s*' + 'lang' + '\\s*=\\s*([^;]+)')?.pop() || 'js' + : data?.lang $: logo_src = ui_theme === 0 ? '/images/logo.svg' : '/images/logo-dark.svg' @@ -129,7 +133,7 @@ Houdini - +