@fragno-dev/core 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +72 -62
- package/CHANGELOG.md +28 -0
- package/dist/api/api.d.ts +3 -2
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js +2 -1
- package/dist/api/api.js.map +1 -1
- package/dist/api/bind-services.d.ts +0 -1
- package/dist/api/bind-services.d.ts.map +1 -1
- package/dist/api/bind-services.js.map +1 -1
- package/dist/api/error.d.ts.map +1 -1
- package/dist/api/error.js.map +1 -1
- package/dist/api/fragment-definition-builder.d.ts +26 -44
- package/dist/api/fragment-definition-builder.d.ts.map +1 -1
- package/dist/api/fragment-definition-builder.js +15 -22
- package/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/api/fragment-instantiator.d.ts +51 -37
- package/dist/api/fragment-instantiator.d.ts.map +1 -1
- package/dist/api/fragment-instantiator.js +74 -69
- package/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/api/request-context-storage.d.ts +4 -0
- package/dist/api/request-context-storage.d.ts.map +1 -1
- package/dist/api/request-context-storage.js +6 -0
- package/dist/api/request-context-storage.js.map +1 -1
- package/dist/api/request-input-context.d.ts.map +1 -1
- package/dist/api/request-input-context.js.map +1 -1
- package/dist/api/request-middleware.d.ts +1 -1
- package/dist/api/request-middleware.d.ts.map +1 -1
- package/dist/api/request-middleware.js.map +1 -1
- package/dist/api/request-output-context.d.ts +1 -1
- package/dist/api/request-output-context.d.ts.map +1 -1
- package/dist/api/request-output-context.js.map +1 -1
- package/dist/api/route-caller.d.ts +30 -0
- package/dist/api/route-caller.d.ts.map +1 -0
- package/dist/api/route-caller.js +63 -0
- package/dist/api/route-caller.js.map +1 -0
- package/dist/api/route-handler-input-options.d.ts.map +1 -1
- package/dist/api/route.d.ts +1 -1
- package/dist/api/route.d.ts.map +1 -1
- package/dist/api/route.js.map +1 -1
- package/dist/api/shared-types.d.ts.map +1 -1
- package/dist/client/client-error.d.ts.map +1 -1
- package/dist/client/client-error.js.map +1 -1
- package/dist/client/client.d.ts +91 -52
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +25 -9
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.svelte.d.ts +6 -5
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +10 -2
- package/dist/client/client.svelte.js.map +1 -1
- package/dist/client/internal/ndjson-streaming.js.map +1 -1
- package/dist/client/react.d.ts +5 -4
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +104 -12
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +7 -5
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +23 -9
- package/dist/client/solid.js.map +1 -1
- package/dist/client/vanilla.d.ts +16 -4
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +21 -1
- package/dist/client/vanilla.js.map +1 -1
- package/dist/client/vue.d.ts +7 -5
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +18 -10
- package/dist/client/vue.js.map +1 -1
- package/dist/id.d.ts +2 -0
- package/dist/id.js +3 -0
- package/dist/internal/cuid.d.ts +16 -0
- package/dist/internal/cuid.d.ts.map +1 -0
- package/dist/internal/cuid.js +82 -0
- package/dist/internal/cuid.js.map +1 -0
- package/dist/mod-client.d.ts +5 -4
- package/dist/mod-client.d.ts.map +1 -1
- package/dist/mod-client.js +7 -5
- package/dist/mod-client.js.map +1 -1
- package/dist/mod.d.ts +6 -5
- package/dist/mod.js +2 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/test/test.d.ts +6 -6
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js.map +1 -1
- package/dist/util/ssr.js.map +1 -1
- package/package.json +24 -40
- package/src/api/api.test.ts +3 -1
- package/src/api/api.ts +6 -0
- package/src/api/bind-services.ts +0 -5
- package/src/api/error.ts +1 -0
- package/src/api/fragment-definition-builder.extend.test.ts +2 -1
- package/src/api/fragment-definition-builder.test.ts +2 -1
- package/src/api/fragment-definition-builder.ts +49 -124
- package/src/api/fragment-instantiator.test.ts +92 -233
- package/src/api/fragment-instantiator.ts +228 -196
- package/src/api/fragment-services.test.ts +1 -0
- package/src/api/internal/path-runtime.test.ts +1 -0
- package/src/api/internal/path-type.test.ts +3 -1
- package/src/api/internal/route.test.ts +1 -0
- package/src/api/request-context-storage.ts +7 -0
- package/src/api/request-input-context.test.ts +4 -2
- package/src/api/request-input-context.ts +2 -1
- package/src/api/request-middleware.test.ts +9 -14
- package/src/api/request-middleware.ts +3 -2
- package/src/api/request-output-context.test.ts +3 -1
- package/src/api/request-output-context.ts +2 -1
- package/src/api/route-caller.test.ts +195 -0
- package/src/api/route-caller.ts +167 -0
- package/src/api/route-handler-input-options.ts +2 -1
- package/src/api/route.test.ts +4 -2
- package/src/api/route.ts +2 -1
- package/src/api/shared-types.ts +2 -1
- package/src/client/client-builder.test.ts +4 -2
- package/src/client/client-error.test.ts +2 -1
- package/src/client/client-error.ts +1 -1
- package/src/client/client-types.test.ts +19 -5
- package/src/client/client.ssr.test.ts +6 -4
- package/src/client/client.svelte.test.ts +18 -9
- package/src/client/client.svelte.ts +38 -13
- package/src/client/client.test.ts +49 -10
- package/src/client/client.ts +291 -141
- package/src/client/internal/ndjson-streaming.test.ts +6 -3
- package/src/client/internal/ndjson-streaming.ts +1 -0
- package/src/client/react.test.ts +176 -6
- package/src/client/react.ts +226 -31
- package/src/client/solid.test.ts +29 -5
- package/src/client/solid.ts +60 -22
- package/src/client/vanilla.test.ts +148 -6
- package/src/client/vanilla.ts +63 -9
- package/src/client/vue.test.ts +223 -84
- package/src/client/vue.ts +57 -30
- package/src/id.ts +1 -0
- package/src/internal/cuid.test.ts +164 -0
- package/src/internal/cuid.ts +133 -0
- package/src/mod-client.ts +4 -2
- package/src/mod.ts +3 -2
- package/src/runtime.ts +1 -1
- package/src/test/test.test.ts +4 -2
- package/src/test/test.ts +7 -9
- package/src/util/async.test.ts +1 -0
- package/src/util/content-type.test.ts +1 -0
- package/src/util/nanostores.test.ts +3 -1
- package/src/util/ssr.ts +1 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +1 -0
- package/vitest.config.ts +2 -1
package/dist/client/client.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { resolveRouteFactories } from "../api/route.js";
|
|
2
|
+
import { buildPath, extractPathParams } from "../api/internal/path.js";
|
|
2
3
|
import { getMountRoute } from "../api/internal/route.js";
|
|
3
4
|
import { RequestInputContext } from "../api/request-input-context.js";
|
|
4
5
|
import { RequestOutputContext } from "../api/request-output-context.js";
|
|
5
|
-
import { buildPath, extractPathParams } from "../api/internal/path.js";
|
|
6
|
-
import { FragnoClientApiError, FragnoClientError, FragnoClientFetchAbortError, FragnoClientFetchError, FragnoClientFetchNetworkError, FragnoClientUnknownApiError } from "./client-error.js";
|
|
7
6
|
import { parseContentType } from "../util/content-type.js";
|
|
8
|
-
import { handleNdjsonStreamingFirstItem } from "./internal/ndjson-streaming.js";
|
|
9
|
-
import { SSR_ENABLED, addStore, getInitialData } from "../util/ssr.js";
|
|
10
7
|
import { unwrapObject } from "../util/nanostores.js";
|
|
8
|
+
import { SSR_ENABLED, addStore, getInitialData } from "../util/ssr.js";
|
|
9
|
+
import { FragnoClientApiError, FragnoClientError, FragnoClientFetchAbortError, FragnoClientFetchError, FragnoClientFetchNetworkError, FragnoClientUnknownApiError } from "./client-error.js";
|
|
11
10
|
import { mergeFetcherConfigs } from "./internal/fetcher-merge.js";
|
|
11
|
+
import { handleNdjsonStreamingFirstItem } from "./internal/ndjson-streaming.js";
|
|
12
12
|
import { computed, task } from "nanostores";
|
|
13
13
|
import { nanoquery } from "@nanostores/query";
|
|
14
14
|
|
|
@@ -73,6 +73,18 @@ function prepareRequestBody(body, contentType) {
|
|
|
73
73
|
headers: { "Content-Type": "application/json" }
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
+
async function schemaAllowsUndefined(schema) {
|
|
77
|
+
try {
|
|
78
|
+
return !(await schema["~standard"].validate(void 0)).issues;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function assertBodyProvided(body, inputSchema, errorMessage) {
|
|
84
|
+
if (typeof body !== "undefined" || inputSchema === void 0) return;
|
|
85
|
+
if (await schemaAllowsUndefined(inputSchema)) return;
|
|
86
|
+
throw new Error(errorMessage);
|
|
87
|
+
}
|
|
76
88
|
/**
|
|
77
89
|
* Merge request headers from multiple sources.
|
|
78
90
|
* Returns undefined if there are no headers to merge.
|
|
@@ -183,9 +195,13 @@ var ClientBuilder = class {
|
|
|
183
195
|
get cacheEntries() {
|
|
184
196
|
return Object.fromEntries(this.#cache.entries());
|
|
185
197
|
}
|
|
186
|
-
createStore(
|
|
198
|
+
createStore(input) {
|
|
199
|
+
if (typeof input === "function") return {
|
|
200
|
+
factory: input,
|
|
201
|
+
[STORE_SYMBOL]: true
|
|
202
|
+
};
|
|
187
203
|
return {
|
|
188
|
-
obj,
|
|
204
|
+
obj: input,
|
|
189
205
|
[STORE_SYMBOL]: true
|
|
190
206
|
};
|
|
191
207
|
}
|
|
@@ -218,7 +234,7 @@ var ClientBuilder = class {
|
|
|
218
234
|
}
|
|
219
235
|
#getFetcher() {
|
|
220
236
|
if (this.#fetcherConfig?.type === "function") return this.#fetcherConfig.fetcher;
|
|
221
|
-
return fetch;
|
|
237
|
+
return globalThis.fetch.bind(globalThis);
|
|
222
238
|
}
|
|
223
239
|
#getFetcherOptions() {
|
|
224
240
|
if (this.#fetcherConfig?.type === "options") return this.#fetcherConfig.options;
|
|
@@ -392,7 +408,7 @@ var ClientBuilder = class {
|
|
|
392
408
|
const mutatorStore = this.#createMutatorStore(async ({ data }) => {
|
|
393
409
|
if (typeof window === "undefined") {}
|
|
394
410
|
const { body, path, query } = data;
|
|
395
|
-
|
|
411
|
+
await assertBodyProvided(body, route.inputSchema, "Body is required.");
|
|
396
412
|
const response = await executeMutateQuery({
|
|
397
413
|
body,
|
|
398
414
|
path,
|
|
@@ -431,7 +447,7 @@ var ClientBuilder = class {
|
|
|
431
447
|
} });
|
|
432
448
|
const mutateQuery = (async (data) => {
|
|
433
449
|
const { body, path, query } = data;
|
|
434
|
-
|
|
450
|
+
await assertBodyProvided(body, route.inputSchema, "Body is required for mutateQuery");
|
|
435
451
|
const response = await executeMutateQuery({
|
|
436
452
|
body,
|
|
437
453
|
path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["result: Record<string, string>","#publicConfig","#fragmentConfig","#fetcherConfig","#cache","#createFetcherStore","#createMutatorStore","#invalidateKeys","#getFetcher","#getFetcherOptions","#createRouteQueryHook","#createRouteQueryMutator","response: Response","requestOptions: RequestInit & { duplex?: \"half\" }","mutatorStore: FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >[\"mutatorStore\"]","#invalidate","fragmentConfig: FragnoFragmentSharedConfig<FlattenRouteFactories<TRoutesOrFactories>>"],"sources":["../../src/client/client.ts"],"sourcesContent":["import { nanoquery, type FetcherStore, type MutatorStore } from \"@nanostores/query\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { computed, task, type ReadableAtom, type Store } from \"nanostores\";\nimport type {\n FragnoRouteConfig,\n HTTPMethod,\n NonGetHTTPMethod,\n RequestThisContext,\n RouteContentType,\n} from \"../api/api\";\nimport {\n buildPath,\n extractPathParams,\n type ExtractPathParams,\n type ExtractPathParamsOrWiden,\n type MaybeExtractPathParamsOrWiden,\n} from \"../api/internal/path\";\nimport { getMountRoute } from \"../api/internal/route\";\nimport { RequestInputContext } from \"../api/request-input-context\";\nimport { RequestOutputContext } from \"../api/request-output-context\";\nimport type {\n FetcherConfig,\n FragnoFragmentSharedConfig,\n FragnoPublicClientConfig,\n FragnoPublicConfig,\n} from \"../api/shared-types\";\nimport { FragnoClientApiError, FragnoClientError, FragnoClientFetchError } from \"./client-error\";\nimport type { InferOr } from \"../util/types-util\";\nimport { parseContentType } from \"../util/content-type\";\nimport {\n handleNdjsonStreamingFirstItem,\n type NdjsonStreamingStore,\n} from \"./internal/ndjson-streaming\";\nimport { addStore, getInitialData, SSR_ENABLED } from \"../util/ssr\";\nimport { unwrapObject } from \"../util/nanostores\";\nimport type { FragmentDefinition } from \"../api/fragment-definition-builder\";\nimport type { AnyFragnoInstantiatedFragment } from \"../api/fragment-instantiator\";\nimport {\n type AnyRouteOrFactory,\n type FlattenRouteFactories,\n resolveRouteFactories,\n} from \"../api/route\";\nimport { mergeFetcherConfigs } from \"./internal/fetcher-merge\";\n\n/**\n * Symbols used to identify hook types\n */\nconst GET_HOOK_SYMBOL = Symbol(\"fragno-get-hook\");\nconst MUTATOR_HOOK_SYMBOL = Symbol(\"fragno-mutator-hook\");\nconst STORE_SYMBOL = Symbol(\"fragno-store\");\n\n/**\n * Check if a value contains files that should be sent as FormData.\n * @internal\n */\nfunction containsFiles(value: unknown): boolean {\n if (value instanceof File || value instanceof Blob) {\n return true;\n }\n\n if (value instanceof FormData) {\n return true;\n }\n\n if (typeof value === \"object\" && value !== null) {\n return Object.values(value).some(\n (v) => v instanceof File || v instanceof Blob || v instanceof FormData,\n );\n }\n\n return false;\n}\n\n/**\n * Convert an object containing files to FormData.\n * Handles nested File/Blob values by appending them directly.\n * Other values are JSON-stringified.\n * @internal\n */\nfunction toFormData(value: object): FormData {\n const formData = new FormData();\n\n for (const [key, val] of Object.entries(value)) {\n if (val instanceof File) {\n formData.append(key, val, val.name);\n } else if (val instanceof Blob) {\n formData.append(key, val);\n } else if (val !== undefined && val !== null) {\n // For non-file values, stringify if needed\n formData.append(key, typeof val === \"string\" ? val : JSON.stringify(val));\n }\n }\n\n return formData;\n}\n\n/**\n * Prepare request body and headers for sending.\n * Handles FormData (file uploads) vs JSON data.\n * @internal\n */\nfunction prepareRequestBody(\n body: unknown,\n contentType?: RouteContentType,\n): { body: BodyInit | undefined; headers?: HeadersInit } {\n if (body === undefined) {\n return { body: undefined };\n }\n\n if (contentType === \"application/octet-stream\") {\n if (\n body instanceof ReadableStream ||\n body instanceof Blob ||\n body instanceof File ||\n body instanceof ArrayBuffer ||\n body instanceof Uint8Array\n ) {\n return { body: body as BodyInit, headers: { \"Content-Type\": \"application/octet-stream\" } };\n }\n\n throw new Error(\n \"Octet-stream routes only accept Blob, File, ArrayBuffer, Uint8Array, or ReadableStream bodies.\",\n );\n }\n\n // If already FormData, send as-is (browser sets Content-Type with boundary)\n if (body instanceof FormData) {\n return { body };\n }\n\n // If body is directly a File or Blob, wrap it in FormData\n if (body instanceof File) {\n const formData = new FormData();\n formData.append(\"file\", body, body.name);\n return { body: formData };\n }\n\n if (body instanceof Blob) {\n const formData = new FormData();\n formData.append(\"file\", body);\n return { body: formData };\n }\n\n // If object contains files, convert to FormData\n if (typeof body === \"object\" && body !== null && containsFiles(body)) {\n return { body: toFormData(body) };\n }\n\n // Otherwise, JSON-stringify\n return {\n body: JSON.stringify(body),\n headers: { \"Content-Type\": \"application/json\" },\n };\n}\n\n/**\n * Merge request headers from multiple sources.\n * Returns undefined if there are no headers to merge.\n * @internal\n */\nfunction mergeRequestHeaders(\n ...headerSources: (HeadersInit | undefined)[]\n): Record<string, string> | undefined {\n const result: Record<string, string> = {};\n let hasHeaders = false;\n\n for (const source of headerSources) {\n if (!source) {\n continue;\n }\n\n if (source instanceof Headers) {\n for (const [key, value] of source.entries()) {\n result[key] = value;\n hasHeaders = true;\n }\n } else if (Array.isArray(source)) {\n for (const [key, value] of source) {\n result[key] = value;\n hasHeaders = true;\n }\n } else {\n for (const [key, value] of Object.entries(source)) {\n result[key] = value;\n hasHeaders = true;\n }\n }\n }\n\n return hasHeaders ? result : undefined;\n}\n\n/**\n * Extract only GET routes from a library config's routes array\n * @internal\n */\nexport type ExtractGetRoutes<\n T extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> = {\n [K in keyof T]: T[K] extends FragnoRouteConfig<\n infer Method,\n infer Path,\n infer Input,\n infer Output,\n infer ErrorCode,\n infer QueryParams\n >\n ? Method extends \"GET\"\n ? FragnoRouteConfig<Method, Path, Input, Output, ErrorCode, QueryParams>\n : never\n : never;\n}[number][];\n\n/**\n * Extract the path from a route configuration for a given method\n * @internal\n */\nexport type ExtractRoutePath<\n T extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n TExpectedMethod extends HTTPMethod = HTTPMethod,\n> = {\n [K in keyof T]: T[K] extends FragnoRouteConfig<\n infer Method,\n infer Path,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >\n ? Method extends TExpectedMethod\n ? Path\n : never\n : never;\n}[number];\n\n/**\n * @internal\n */\nexport type ExtractGetRoutePaths<\n T extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> = ExtractRoutePath<T, \"GET\">;\n\n/**\n * @internal\n */\nexport type ExtractNonGetRoutePaths<\n T extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> = ExtractRoutePath<T, NonGetHTTPMethod>;\n\n/**\n * Extract the route configuration type(s) for a given path from a routes array.\n * Optionally narrow by HTTP method via the third type parameter.\n *\n * Defaults to extracting all methods for the matching path, producing a union\n * if multiple methods exist for the same path.\n * @internal\n */\nexport type ExtractRouteByPath<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n TPath extends string,\n TMethod extends HTTPMethod = HTTPMethod,\n> = {\n [K in keyof TRoutes]: TRoutes[K] extends FragnoRouteConfig<\n infer M,\n TPath,\n infer Input,\n infer Output,\n infer ErrorCode,\n infer QueryParams\n >\n ? M extends TMethod\n ? FragnoRouteConfig<M, TPath, Input, Output, ErrorCode, QueryParams>\n : never\n : never;\n}[number];\n\n/**\n * Extract the output schema type for a specific route path from a routes array\n * @internal\n */\nexport type ExtractOutputSchemaForPath<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined\n >[],\n TPath extends string,\n> = {\n [K in keyof TRoutes]: TRoutes[K] extends FragnoRouteConfig<\n infer Method,\n TPath,\n StandardSchemaV1 | undefined,\n infer Output\n >\n ? Method extends \"GET\"\n ? Output\n : never\n : never;\n}[number];\n\n/**\n * Check if a path exists as a GET route in the routes array\n * @internal\n */\nexport type IsValidGetRoutePath<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n TPath extends string,\n> = TPath extends ExtractGetRoutePaths<TRoutes> ? true : false;\n\n/**\n * Utility type to ensure only valid GET route paths can be used\n * @internal\n */\nexport type ValidateGetRoutePath<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n TPath extends string,\n> =\n TPath extends ExtractGetRoutePaths<TRoutes>\n ? TPath\n : `Error: Path '${TPath}' is not a valid GET route. Available GET routes: ${ExtractGetRoutePaths<TRoutes>}`;\n\n/**\n * Helper type to check if a routes array has any GET routes\n * @internal\n */\nexport type HasGetRoutes<\n T extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> = ExtractGetRoutePaths<T> extends never ? false : true;\n\n/**\n * @internal\n */\nexport type ObjectContainingStoreField<T extends object> = T extends Store\n ? T\n : {\n [K in keyof T]: T[K] extends Store ? { [P in K]: T[P] } & Partial<Omit<T, K>> : never;\n }[keyof T] extends never\n ? never\n : T;\n\n/**\n * @internal\n */\nexport type FragnoStoreData<T extends object> = {\n obj: T;\n [STORE_SYMBOL]: true;\n};\n\nexport type FragnoClientHookData<\n TMethod extends HTTPMethod,\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = {\n route: FragnoRouteConfig<\n TMethod,\n TPath,\n StandardSchemaV1 | undefined,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >;\n query(args?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string>;\n query?: Record<TQueryParameters, string | undefined>;\n }): Promise<StandardSchemaV1.InferOutput<TOutputSchema>>;\n store(args?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>;\n query?: Record<TQueryParameters, string | undefined | ReadableAtom<string | undefined>>;\n }): FetcherStore<StandardSchemaV1.InferOutput<TOutputSchema>, FragnoClientError<TErrorCode>>;\n [GET_HOOK_SYMBOL]: true;\n} & {\n // Phantom field that preserves the specific TOutputSchema type parameter\n // in the structural type. This makes the type covariant, allowing more\n // specific schema types (like z.ZodString) to be assigned to variables\n // typed with more general schema types (like StandardSchemaV1<any, any>)\n readonly _outputSchema?: TOutputSchema;\n};\n\nexport type FragnoClientMutatorData<\n TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = {\n route: FragnoRouteConfig<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >;\n\n mutateQuery(args?: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<TPath, string>;\n query?: Record<TQueryParameters, string | undefined>;\n }): Promise<InferOr<TOutputSchema, undefined>>;\n\n mutatorStore: MutatorStore<\n {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>;\n query?: Record<TQueryParameters, string | undefined | ReadableAtom<string | undefined>>;\n },\n InferOr<TOutputSchema, undefined>,\n FragnoClientError<TErrorCode>\n >;\n [MUTATOR_HOOK_SYMBOL]: true;\n} & {\n readonly _inputSchema?: TInputSchema;\n readonly _outputSchema?: TOutputSchema;\n};\n\n/**\n * @internal\n */\nexport function buildUrl<TPath extends string>(\n config: {\n baseUrl?: string;\n mountRoute: string;\n path: TPath;\n },\n params: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n },\n): string {\n const { baseUrl = \"\", mountRoute, path } = config;\n const { pathParams, queryParams } = params ?? {};\n\n const normalizedPathParams = unwrapObject(pathParams) as ExtractPathParams<TPath, string>;\n const normalizedQueryParams = unwrapObject(queryParams) ?? {};\n\n // Filter out undefined values to prevent URLSearchParams from converting them to string \"undefined\"\n const filteredQueryParams = Object.fromEntries(\n Object.entries(normalizedQueryParams).filter(([_, value]) => value !== undefined),\n ) as Record<string, string>;\n\n const searchParams = new URLSearchParams(filteredQueryParams);\n const builtPath = buildPath(path, normalizedPathParams ?? {});\n const search = searchParams.toString() ? `?${searchParams.toString()}` : \"\";\n return `${baseUrl}${mountRoute}${builtPath}${search}`;\n}\n\n/**\n * This method returns an array, which can be passed directly to nanostores.\n *\n * The returned array is always: path, pathParams (In order they appear in the path), queryParams (In alphabetical order)\n * Missing pathParams are replaced with \"<missing>\".\n * Atoms with undefined values are wrapped in computed atoms that map undefined to \"\" to avoid nanoquery treating the key as incomplete.\n * @param path\n * @param params\n * @returns\n * @internal\n */\nexport function getCacheKey<TMethod extends HTTPMethod, TPath extends string>(\n method: TMethod,\n path: TPath,\n params?: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n },\n): (string | ReadableAtom<string>)[] {\n if (!params) {\n return [method, path];\n }\n\n const { pathParams, queryParams } = params;\n\n const pathParamNames = extractPathParams(path);\n const pathParamValues = pathParamNames.map((name) => pathParams?.[name] ?? \"<missing>\");\n\n const queryParamValues = queryParams\n ? Object.keys(queryParams)\n .sort()\n .map((key) => {\n const value = queryParams[key];\n // If it's an atom, wrap it to convert undefined to \"\"\n if (value && typeof value === \"object\" && \"get\" in value) {\n return computed(value as ReadableAtom<string | undefined>, (v) => v ?? \"\");\n }\n // Plain string value (or undefined)\n return value ?? \"\";\n })\n : [];\n\n return [method, path, ...pathParamValues, ...queryParamValues];\n}\n\nfunction isStreamingResponse(response: Response): false | \"ndjson\" | \"octet-stream\" {\n const contentType = parseContentType(response.headers.get(\"content-type\"));\n\n if (!contentType) {\n // Always assume 'normal' JSON by default.\n return false;\n }\n\n const isChunked = response.headers.get(\"transfer-encoding\") === \"chunked\";\n\n if (!isChunked) {\n return false;\n }\n\n if (contentType.subtype === \"octet-stream\") {\n // TODO(Wilco): This is not actually supported properly\n return \"octet-stream\";\n }\n\n if (contentType.subtype === \"x-ndjson\") {\n return \"ndjson\";\n }\n\n return false;\n}\n\n// Type guard to check if a hook is a GET hook\n/**\n * @internal\n */\nexport function isGetHook<\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: unknown,\n): hook is FragnoClientHookData<\"GET\", TPath, TOutputSchema, TErrorCode, TQueryParameters> {\n return (\n typeof hook === \"object\" &&\n hook !== null &&\n GET_HOOK_SYMBOL in hook &&\n hook[GET_HOOK_SYMBOL] === true\n );\n}\n\n// Type guard to check if a hook is a mutator\n/**\n * @internal\n */\nexport function isMutatorHook<\n TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: unknown,\n): hook is FragnoClientMutatorData<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n> {\n return (\n typeof hook === \"object\" &&\n hook !== null &&\n MUTATOR_HOOK_SYMBOL in hook &&\n hook[MUTATOR_HOOK_SYMBOL] === true\n );\n}\n\n/**\n * @internal\n */\nexport function isStore<TStore extends Store>(obj: unknown): obj is FragnoStoreData<TStore> {\n return (\n typeof obj === \"object\" && obj !== null && STORE_SYMBOL in obj && obj[STORE_SYMBOL] === true\n );\n}\n\ntype OnErrorRetryFn = (opts: {\n error: unknown;\n key: string;\n retryCount: number;\n}) => number | undefined;\n\nexport type CreateHookOptions = {\n /**\n * A function that will be called when an error occurs. Implements an exponential backoff strategy\n * when left undefined. When null, retries will be disabled. The number returned (> 0) by the\n * callback will determine in how many ms to retry next.\n */\n onErrorRetry?: OnErrorRetryFn | null;\n};\n\ntype OnInvalidateFn<TPath extends string> = (\n invalidate: <TInnerPath extends string>(\n method: HTTPMethod,\n path: TInnerPath,\n params: {\n pathParams?: MaybeExtractPathParamsOrWiden<TInnerPath, string>;\n queryParams?: Record<string, string>;\n },\n ) => void,\n params: {\n pathParams: MaybeExtractPathParamsOrWiden<TPath, string>;\n queryParams?: Record<string, string>;\n },\n) => void;\n\n/**\n * @internal\n */\nexport type CacheLine = {\n data: unknown;\n error: unknown;\n retryCount: number;\n created: number;\n expires: number;\n};\n\nexport class ClientBuilder<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n TFragmentConfig extends FragnoFragmentSharedConfig<TRoutes>,\n> {\n #publicConfig: FragnoPublicClientConfig;\n #fragmentConfig: TFragmentConfig;\n #fetcherConfig?: FetcherConfig;\n\n #cache = new Map<string, CacheLine>();\n\n #createFetcherStore;\n #createMutatorStore;\n #invalidateKeys;\n\n constructor(publicConfig: FragnoPublicClientConfig, fragmentConfig: TFragmentConfig) {\n this.#publicConfig = publicConfig;\n this.#fragmentConfig = fragmentConfig;\n this.#fetcherConfig = publicConfig.fetcherConfig;\n\n const [createFetcherStore, createMutatorStore, { invalidateKeys }] = nanoquery({\n cache: this.#cache,\n });\n this.#createFetcherStore = createFetcherStore;\n this.#createMutatorStore = createMutatorStore;\n this.#invalidateKeys = invalidateKeys;\n }\n\n get cacheEntries(): Readonly<Record<string, CacheLine>> {\n return Object.fromEntries(this.#cache.entries());\n }\n\n createStore<const T extends object>(obj: T): FragnoStoreData<T> {\n return { obj: obj, [STORE_SYMBOL]: true };\n }\n\n /**\n * Build a URL for a custom backend call using the configured baseUrl and mountRoute.\n * Useful for fragment authors who need to make custom fetch calls.\n */\n buildUrl<TPath extends string>(\n path: TPath,\n params?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n },\n ): string {\n const baseUrl = this.#publicConfig.baseUrl ?? \"\";\n const mountRoute = getMountRoute({\n name: this.#fragmentConfig.name,\n mountRoute: this.#publicConfig.mountRoute,\n });\n\n return buildUrl(\n { baseUrl, mountRoute, path },\n { pathParams: params?.path, queryParams: params?.query },\n );\n }\n\n /**\n * Get the configured fetcher function for custom backend calls.\n * Returns fetch with merged options applied.\n */\n getFetcher(): {\n fetcher: typeof fetch;\n defaultOptions: RequestInit | undefined;\n } {\n return {\n fetcher: this.#getFetcher(),\n defaultOptions: this.#getFetcherOptions(),\n };\n }\n\n #getFetcher(): typeof fetch {\n if (this.#fetcherConfig?.type === \"function\") {\n return this.#fetcherConfig.fetcher;\n }\n return fetch;\n }\n\n #getFetcherOptions(): RequestInit | undefined {\n if (this.#fetcherConfig?.type === \"options\") {\n return this.#fetcherConfig.options;\n }\n return undefined;\n }\n\n createHook<TPath extends ExtractGetRoutePaths<TFragmentConfig[\"routes\"]>>(\n path: ValidateGetRoutePath<TFragmentConfig[\"routes\"], TPath>,\n options?: CreateHookOptions,\n ): FragnoClientHookData<\n \"GET\",\n TPath,\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath, \"GET\">[\"outputSchema\"]>,\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath, \"GET\">[\"errorCodes\"]>[number],\n NonNullable<\n ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath, \"GET\">[\"queryParameters\"]\n >[number]\n > {\n const route = this.#fragmentConfig.routes.find(\n (\n r,\n ): r is FragnoRouteConfig<\n \"GET\",\n TPath,\n StandardSchemaV1 | undefined,\n StandardSchemaV1,\n string,\n string\n > => r.path === path && r.method === \"GET\" && r.outputSchema !== undefined,\n );\n\n if (!route) {\n throw new Error(`Route '${path}' not found or is not a GET route with an output schema.`);\n }\n\n return this.#createRouteQueryHook(route, options);\n }\n\n createMutator<TPath extends ExtractNonGetRoutePaths<TFragmentConfig[\"routes\"]>>(\n method: NonGetHTTPMethod,\n path: TPath,\n onInvalidate?: OnInvalidateFn<TPath>,\n ): FragnoClientMutatorData<\n NonGetHTTPMethod, // TODO: This can be any Method, but should be related to TPath\n TPath,\n ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"inputSchema\"],\n ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"outputSchema\"],\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"errorCodes\"]>[number],\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"queryParameters\"]>[number]\n > {\n type TRoute = ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>;\n\n const route = this.#fragmentConfig.routes.find(\n (\n r,\n ): r is FragnoRouteConfig<\n NonGetHTTPMethod,\n TPath,\n TRoute[\"inputSchema\"],\n TRoute[\"outputSchema\"],\n string,\n string\n > => r.method !== \"GET\" && r.path === path && r.method === method,\n );\n\n if (!route) {\n throw new Error(\n `Route '${path}' not found or is a GET route with an input and output schema.`,\n );\n }\n\n return this.#createRouteQueryMutator(route, onInvalidate);\n }\n\n #createRouteQueryHook<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n >(\n route: FragnoRouteConfig<\n \"GET\",\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >,\n options: CreateHookOptions = {},\n ): FragnoClientHookData<\"GET\", TPath, TOutputSchema, TErrorCode, TQueryParameters> {\n if (route.method !== \"GET\") {\n throw new Error(\n `Only GET routes are supported for hooks. Route '${route.path}' is a ${route.method} route.`,\n );\n }\n\n if (!route.outputSchema) {\n throw new Error(\n `Output schema is required for GET routes. Route '${route.path}' has no output schema.`,\n );\n }\n\n const baseUrl = this.#publicConfig.baseUrl ?? \"\";\n const mountRoute = getMountRoute({\n name: this.#fragmentConfig.name,\n mountRoute: this.#publicConfig.mountRoute,\n });\n const fetcher = this.#getFetcher();\n const fetcherOptions = this.#getFetcherOptions();\n\n async function callServerSideHandler(params: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n }): Promise<Response> {\n const { pathParams, queryParams } = params ?? {};\n\n const normalizedPathParams = unwrapObject(pathParams) as ExtractPathParams<TPath, string>;\n const normalizedQueryParams = unwrapObject(queryParams) ?? {};\n\n // Filter out undefined values to prevent URLSearchParams from converting them to string \"undefined\"\n const filteredQueryParams = Object.fromEntries(\n Object.entries(normalizedQueryParams).filter(([_, value]) => value !== undefined),\n ) as Record<string, string>;\n\n const searchParams = new URLSearchParams(filteredQueryParams);\n\n const result = await route.handler(\n RequestInputContext.fromSSRContext({\n method: route.method,\n path: route.path,\n pathParams: normalizedPathParams,\n searchParams,\n }),\n new RequestOutputContext(route.outputSchema),\n );\n\n return result;\n }\n\n async function executeQuery(params?: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n }): Promise<Response> {\n const { pathParams, queryParams } = params ?? {};\n\n if (typeof window === \"undefined\") {\n return task(async () => callServerSideHandler({ pathParams, queryParams }));\n }\n\n const url = buildUrl({ baseUrl, mountRoute, path: route.path }, { pathParams, queryParams });\n\n let response: Response;\n try {\n response = fetcherOptions ? await fetcher(url, fetcherOptions) : await fetcher(url);\n } catch (error) {\n throw FragnoClientFetchError.fromUnknownFetchError(error);\n }\n\n if (!response.ok) {\n throw await FragnoClientApiError.fromResponse<TErrorCode>(response);\n }\n\n return response;\n }\n\n return {\n route,\n store: (args) => {\n const { path, query } = args ?? {};\n\n const key = getCacheKey(route.method, route.path, {\n pathParams: path,\n queryParams: query,\n });\n\n const store = this.#createFetcherStore<\n StandardSchemaV1.InferOutput<TOutputSchema>,\n FragnoClientError<TErrorCode>\n >(key, {\n fetcher: async (): Promise<StandardSchemaV1.InferOutput<TOutputSchema>> => {\n if (SSR_ENABLED) {\n const initialData = getInitialData(\n key.map((d) => (typeof d === \"string\" ? d : d.get())).join(\"\"),\n );\n\n if (initialData) {\n return initialData;\n }\n }\n\n const response = await executeQuery({ pathParams: path, queryParams: query });\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return response.json() as Promise<StandardSchemaV1.InferOutput<TOutputSchema>>;\n }\n\n if (typeof window === \"undefined\") {\n return [];\n }\n\n if (isStreaming === \"ndjson\") {\n const storeAdapter: NdjsonStreamingStore<TOutputSchema, TErrorCode> = {\n setData: (value) => {\n store.set({\n ...store.get(),\n loading: !(Array.isArray(value) && value.length > 0),\n data: value as InferOr<TOutputSchema, undefined>,\n });\n },\n setError: (value) => {\n store.set({\n ...store.get(),\n error: value,\n });\n },\n };\n\n // Start streaming in background and return first item\n const { firstItem } = await handleNdjsonStreamingFirstItem(response, storeAdapter);\n return [firstItem];\n }\n\n if (isStreaming === \"octet-stream\") {\n // TODO(Wilco): Implement this\n throw new Error(\"Octet-stream streaming is not supported.\");\n }\n\n throw new Error(\"Unreachable\");\n },\n\n onErrorRetry: options?.onErrorRetry,\n dedupeTime: Infinity,\n });\n\n if (typeof window === \"undefined\") {\n addStore(store);\n }\n\n return store;\n },\n query: async (args) => {\n const { path, query } = args ?? {};\n\n const response = await executeQuery({ pathParams: path, queryParams: query });\n\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return (await response.json()) as StandardSchemaV1.InferOutput<TOutputSchema>;\n }\n\n if (isStreaming === \"ndjson\") {\n const { streamingPromise } = await handleNdjsonStreamingFirstItem(response);\n // Resolves once the stream is done\n return await streamingPromise;\n }\n\n if (isStreaming === \"octet-stream\") {\n // TODO(Wilco): Implement this\n throw new Error(\"Octet-stream streaming is not supported.\");\n }\n\n throw new Error(\"Unreachable\");\n },\n [GET_HOOK_SYMBOL]: true,\n };\n }\n\n #createRouteQueryMutator<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n >(\n route: FragnoRouteConfig<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >,\n onInvalidate: OnInvalidateFn<TPath> = (invalidate, params) =>\n invalidate(\"GET\", route.path, params),\n ): FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n > {\n const method = route.method;\n\n const baseUrl = this.#publicConfig.baseUrl ?? \"\";\n const mountRoute = getMountRoute({\n name: this.#fragmentConfig.name,\n mountRoute: this.#publicConfig.mountRoute,\n });\n const fetcher = this.#getFetcher();\n const fetcherOptions = this.#getFetcherOptions();\n\n async function executeMutateQuery({\n body,\n path,\n query,\n }: {\n body?: InferOr<TInputSchema, undefined>;\n path?: ExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n }): Promise<Response> {\n if (typeof window === \"undefined\") {\n return task(async () =>\n route.handler(\n RequestInputContext.fromSSRContext({\n inputSchema: route.inputSchema,\n method,\n path: route.path,\n pathParams: (path ?? {}) as ExtractPathParams<TPath, string>,\n searchParams: new URLSearchParams(query),\n body,\n }),\n new RequestOutputContext(route.outputSchema),\n ),\n );\n }\n\n const url = buildUrl(\n { baseUrl, mountRoute, path: route.path },\n { pathParams: path, queryParams: query },\n );\n\n let response: Response;\n try {\n const { body: preparedBody, headers: bodyHeaders } = prepareRequestBody(\n body,\n route.contentType,\n );\n\n // Merge headers: fetcherOptions headers + body-specific headers (e.g., Content-Type for JSON)\n // For FormData, bodyHeaders is undefined and browser sets Content-Type with boundary automatically\n const mergedHeaders = mergeRequestHeaders(\n fetcherOptions?.headers as HeadersInit | undefined,\n bodyHeaders,\n );\n\n const requestOptions: RequestInit & { duplex?: \"half\" } = {\n ...fetcherOptions,\n method,\n body: preparedBody,\n ...(mergedHeaders ? { headers: mergedHeaders } : {}),\n };\n if (preparedBody instanceof ReadableStream) {\n requestOptions.duplex = \"half\";\n }\n response = await fetcher(url, requestOptions);\n } catch (error) {\n throw FragnoClientFetchError.fromUnknownFetchError(error);\n }\n\n if (!response.ok) {\n throw await FragnoClientApiError.fromResponse<TErrorCode>(response);\n }\n\n return response;\n }\n\n const mutatorStore: FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >[\"mutatorStore\"] = this.#createMutatorStore(\n async ({ data }) => {\n if (typeof window === \"undefined\") {\n // TODO(Wilco): Handle server-side rendering.\n }\n\n const { body, path, query } = data as {\n body?: InferOr<TInputSchema, undefined>;\n path?: ExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n };\n\n if (typeof body === \"undefined\" && route.inputSchema !== undefined) {\n throw new Error(\"Body is required.\");\n }\n\n const response = await executeMutateQuery({ body, path, query });\n\n onInvalidate(this.#invalidate.bind(this), {\n pathParams: (path ?? {}) as MaybeExtractPathParamsOrWiden<TPath, string>,\n queryParams: query,\n });\n\n if (response.status === 201 || response.status === 204) {\n return undefined;\n }\n\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return response.json();\n }\n\n if (typeof window === \"undefined\") {\n return [];\n }\n\n if (isStreaming === \"ndjson\") {\n const storeAdapter: NdjsonStreamingStore<NonNullable<TOutputSchema>, TErrorCode> = {\n setData: (value) => {\n mutatorStore.set({\n ...mutatorStore.get(),\n loading: !(Array.isArray(value) && value.length > 0),\n data: value as InferOr<TOutputSchema, undefined>,\n });\n },\n setError: (value) => {\n mutatorStore.set({\n ...mutatorStore.get(),\n error: value,\n });\n },\n };\n\n // Start streaming in background and return first item\n const { firstItem } = await handleNdjsonStreamingFirstItem(response, storeAdapter);\n\n // Return the first item immediately. The streaming will continue in the background\n return [firstItem];\n }\n\n if (isStreaming === \"octet-stream\") {\n // TODO(Wilco): Implement this\n throw new Error(\"Octet-stream streaming is not supported.\");\n }\n\n throw new Error(\"Unreachable\");\n },\n {\n onError: (error) => {\n console.error(\"Error in mutatorStore\", error);\n },\n },\n );\n\n const mutateQuery = (async (data) => {\n // TypeScript infers the fields to not exist, even though they might\n const { body, path, query } = data as {\n body?: InferOr<TInputSchema, undefined>;\n path?: ExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n };\n\n if (typeof body === \"undefined\" && route.inputSchema !== undefined) {\n throw new Error(\"Body is required for mutateQuery\");\n }\n\n const response = await executeMutateQuery({ body, path, query });\n\n if (response.status === 201 || response.status === 204) {\n return undefined;\n }\n\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return response.json();\n }\n\n if (isStreaming === \"ndjson\") {\n const { streamingPromise } = await handleNdjsonStreamingFirstItem(response);\n // Resolves once the stream is done, i.e. we block until done\n return await streamingPromise;\n }\n\n if (isStreaming === \"octet-stream\") {\n throw new Error(\"Octet-stream streaming is not supported for mutations\");\n }\n\n throw new Error(\"Unreachable\");\n }) satisfies FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >[\"mutateQuery\"];\n\n return {\n route,\n mutateQuery,\n mutatorStore,\n [MUTATOR_HOOK_SYMBOL]: true,\n };\n }\n\n #invalidate<TPath extends string>(\n method: HTTPMethod,\n path: TPath,\n params: {\n pathParams?: MaybeExtractPathParamsOrWiden<TPath, string>;\n queryParams?: Record<string, string>;\n },\n ) {\n const prefixArray = getCacheKey(method, path, {\n pathParams: params?.pathParams,\n queryParams: params?.queryParams,\n });\n\n const prefix = prefixArray.map((k) => (typeof k === \"string\" ? k : k.get())).join(\"\");\n\n this.#invalidateKeys((key) => key.startsWith(prefix));\n }\n}\n\n/**\n * Create a client builder for fragments using the new fragment definition API.\n * This is the same as createClientBuilder but works with FragmentDefinition.\n */\nexport function createClientBuilder<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {},\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n publicConfig: FragnoPublicClientConfig,\n routesOrFactories: TRoutesOrFactories,\n authorFetcherConfig?: FetcherConfig,\n): ClientBuilder<\n FlattenRouteFactories<TRoutesOrFactories>,\n FragnoFragmentSharedConfig<FlattenRouteFactories<TRoutesOrFactories>>\n> {\n // For client-side, we resolve route factories with dummy context\n // This will be removed by the bundle plugin anyway\n const dummyContext = {\n config: {} as TConfig,\n deps: {} as TDeps,\n services: {} as TBaseServices & TServices,\n serviceDeps: {},\n };\n\n const routes = resolveRouteFactories(dummyContext, routesOrFactories);\n\n const fragmentConfig: FragnoFragmentSharedConfig<FlattenRouteFactories<TRoutesOrFactories>> = {\n name: definition.name,\n routes,\n };\n\n const mountRoute = getMountRoute({\n name: definition.name,\n mountRoute: publicConfig.mountRoute,\n });\n const mergedFetcherConfig = mergeFetcherConfigs(authorFetcherConfig, publicConfig.fetcherConfig);\n const fullPublicConfig = {\n ...publicConfig,\n mountRoute,\n fetcherConfig: mergedFetcherConfig,\n };\n\n return new ClientBuilder(fullPublicConfig, fragmentConfig);\n}\n\nexport * from \"./client-error\";\nexport type { FetcherConfig, FragnoPublicClientConfig } from \"../api/shared-types\";\nexport type { FragnoFragmentSharedConfig } from \"../api/fragment-instantiator\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["result: Record<string, string>","#publicConfig","#fragmentConfig","#fetcherConfig","#cache","#createFetcherStore","#createMutatorStore","#invalidateKeys","#getFetcher","#getFetcherOptions","#createRouteQueryHook","#createRouteQueryMutator","response: Response","requestOptions: RequestInit & { duplex?: \"half\" }","mutatorStore: FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >[\"mutatorStore\"]","#invalidate","fragmentConfig: FragnoFragmentSharedConfig<FlattenRouteFactories<TRoutesOrFactories>>"],"sources":["../../src/client/client.ts"],"sourcesContent":["import { computed, task, type ReadableAtom, type Store } from \"nanostores\";\n\nimport { nanoquery, type FetcherStore, type MutatorStore } from \"@nanostores/query\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nimport type {\n FragnoRouteConfig,\n HTTPMethod,\n NonGetHTTPMethod,\n RequestThisContext,\n RouteContentType,\n} from \"../api/api\";\nimport type { FragmentDefinition } from \"../api/fragment-definition-builder\";\nimport {\n buildPath,\n extractPathParams,\n type ExtractPathParams,\n type ExtractPathParamsOrWiden,\n type MaybeExtractPathParamsOrWiden,\n} from \"../api/internal/path\";\nimport { getMountRoute } from \"../api/internal/route\";\nimport { RequestInputContext } from \"../api/request-input-context\";\nimport { RequestOutputContext } from \"../api/request-output-context\";\nimport {\n type AnyFragnoRouteConfig,\n type AnyRouteOrFactory,\n type FlattenRouteFactories,\n resolveRouteFactories,\n} from \"../api/route\";\nimport type {\n FetcherConfig,\n FragnoFragmentSharedConfig,\n FragnoPublicClientConfig,\n FragnoPublicConfig,\n} from \"../api/shared-types\";\nimport { parseContentType } from \"../util/content-type\";\nimport { unwrapObject } from \"../util/nanostores\";\nimport { addStore, getInitialData, SSR_ENABLED } from \"../util/ssr\";\nimport type { InferOr } from \"../util/types-util\";\nimport { FragnoClientApiError, FragnoClientError, FragnoClientFetchError } from \"./client-error\";\nimport { mergeFetcherConfigs } from \"./internal/fetcher-merge\";\nimport {\n handleNdjsonStreamingFirstItem,\n type NdjsonStreamingStore,\n} from \"./internal/ndjson-streaming\";\n\n/**\n * Symbols used to identify hook types\n */\nconst GET_HOOK_SYMBOL = Symbol(\"fragno-get-hook\");\nconst MUTATOR_HOOK_SYMBOL = Symbol(\"fragno-mutator-hook\");\nconst STORE_SYMBOL = Symbol(\"fragno-store\");\n\n/**\n * Check if a value contains files that should be sent as FormData.\n * @internal\n */\nfunction containsFiles(value: unknown): boolean {\n if (value instanceof File || value instanceof Blob) {\n return true;\n }\n\n if (value instanceof FormData) {\n return true;\n }\n\n if (typeof value === \"object\" && value !== null) {\n return Object.values(value).some(\n (v) => v instanceof File || v instanceof Blob || v instanceof FormData,\n );\n }\n\n return false;\n}\n\n/**\n * Convert an object containing files to FormData.\n * Handles nested File/Blob values by appending them directly.\n * Other values are JSON-stringified.\n * @internal\n */\nfunction toFormData(value: object): FormData {\n const formData = new FormData();\n\n for (const [key, val] of Object.entries(value)) {\n if (val instanceof File) {\n formData.append(key, val, val.name);\n } else if (val instanceof Blob) {\n formData.append(key, val);\n } else if (val !== undefined && val !== null) {\n // For non-file values, stringify if needed\n formData.append(key, typeof val === \"string\" ? val : JSON.stringify(val));\n }\n }\n\n return formData;\n}\n\n/**\n * Prepare request body and headers for sending.\n * Handles FormData (file uploads) vs JSON data.\n * @internal\n */\nfunction prepareRequestBody(\n body: unknown,\n contentType?: RouteContentType,\n): { body: BodyInit | undefined; headers?: HeadersInit } {\n if (body === undefined) {\n return { body: undefined };\n }\n\n if (contentType === \"application/octet-stream\") {\n if (\n body instanceof ReadableStream ||\n body instanceof Blob ||\n body instanceof File ||\n body instanceof ArrayBuffer ||\n body instanceof Uint8Array\n ) {\n return { body: body as BodyInit, headers: { \"Content-Type\": \"application/octet-stream\" } };\n }\n\n throw new Error(\n \"Octet-stream routes only accept Blob, File, ArrayBuffer, Uint8Array, or ReadableStream bodies.\",\n );\n }\n\n // If already FormData, send as-is (browser sets Content-Type with boundary)\n if (body instanceof FormData) {\n return { body };\n }\n\n // If body is directly a File or Blob, wrap it in FormData\n if (body instanceof File) {\n const formData = new FormData();\n formData.append(\"file\", body, body.name);\n return { body: formData };\n }\n\n if (body instanceof Blob) {\n const formData = new FormData();\n formData.append(\"file\", body);\n return { body: formData };\n }\n\n // If object contains files, convert to FormData\n if (typeof body === \"object\" && body !== null && containsFiles(body)) {\n return { body: toFormData(body) };\n }\n\n // Otherwise, JSON-stringify\n return {\n body: JSON.stringify(body),\n headers: { \"Content-Type\": \"application/json\" },\n };\n}\n\nasync function schemaAllowsUndefined(schema: StandardSchemaV1): Promise<boolean> {\n try {\n const result = await schema[\"~standard\"].validate(undefined);\n return !result.issues;\n } catch {\n return false;\n }\n}\n\nasync function assertBodyProvided(\n body: unknown,\n inputSchema: StandardSchemaV1 | undefined,\n errorMessage: string,\n): Promise<void> {\n if (typeof body !== \"undefined\" || inputSchema === undefined) {\n return;\n }\n\n if (await schemaAllowsUndefined(inputSchema)) {\n return;\n }\n\n throw new Error(errorMessage);\n}\n\n/**\n * Merge request headers from multiple sources.\n * Returns undefined if there are no headers to merge.\n * @internal\n */\nfunction mergeRequestHeaders(\n ...headerSources: (HeadersInit | undefined)[]\n): Record<string, string> | undefined {\n const result: Record<string, string> = {};\n let hasHeaders = false;\n\n for (const source of headerSources) {\n if (!source) {\n continue;\n }\n\n if (source instanceof Headers) {\n for (const [key, value] of source.entries()) {\n result[key] = value;\n hasHeaders = true;\n }\n } else if (Array.isArray(source)) {\n for (const [key, value] of source) {\n result[key] = value;\n hasHeaders = true;\n }\n } else {\n for (const [key, value] of Object.entries(source)) {\n result[key] = value;\n hasHeaders = true;\n }\n }\n }\n\n return hasHeaders ? result : undefined;\n}\n\n/**\n * @internal\n */\ntype FilterRouteByMethod<TRoute, TExpectedMethod extends HTTPMethod> =\n TRoute extends FragnoRouteConfig<\n infer TMethod,\n infer TPath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters,\n infer TThisContext\n >\n ? [Extract<TMethod, TExpectedMethod>] extends [never]\n ? [Extract<TExpectedMethod, TMethod>] extends [never]\n ? never\n : FragnoRouteConfig<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : FragnoRouteConfig<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : never;\n\n/**\n * @internal\n */\ntype FilterRouteByPath<TRoute, TPath extends string> =\n TRoute extends FragnoRouteConfig<\n infer TMethod,\n infer TRoutePath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters,\n infer TThisContext\n >\n ? [Extract<TRoutePath, TPath>] extends [never]\n ? [Extract<TPath, TRoutePath>] extends [never]\n ? never\n : FragnoRouteConfig<\n TMethod,\n TRoutePath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : FragnoRouteConfig<\n TMethod,\n TRoutePath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : never;\n\n/**\n * @internal\n */\ntype ExtractGetRoutesExact<T extends readonly AnyFragnoRouteConfig[]> = {\n [K in keyof T]: T[K] extends FragnoRouteConfig<\n infer TMethod,\n infer TPath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters,\n infer TThisContext\n >\n ? TMethod extends \"GET\"\n ? FragnoRouteConfig<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : never\n : never;\n}[number][];\n\n/**\n * Extract only GET routes from a library config's routes array\n * @internal\n */\nexport type ExtractGetRoutes<T extends readonly AnyFragnoRouteConfig[]> = ExtractGetRoutesExact<T>;\n\n/**\n * @internal\n */\ntype ExtractRoutePathExact<\n T extends readonly AnyFragnoRouteConfig[],\n TExpectedMethod extends HTTPMethod = HTTPMethod,\n> = {\n [K in keyof T]: T[K] extends FragnoRouteConfig<\n infer TMethod,\n infer TPath,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string,\n RequestThisContext\n >\n ? TMethod extends TExpectedMethod\n ? TPath\n : never\n : never;\n}[number];\n\n/**\n * @internal\n */\ntype ExtractRoutePathLoose<\n T extends readonly AnyFragnoRouteConfig[],\n TExpectedMethod extends HTTPMethod = HTTPMethod,\n> = {\n [K in keyof T]: FilterRouteByMethod<T[K], TExpectedMethod> extends FragnoRouteConfig<\n infer _TMethod,\n infer TPath,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string,\n RequestThisContext\n >\n ? TPath\n : never;\n}[number];\n\n/**\n * @internal\n */\ntype HasWidenedRouteShape<T extends readonly AnyFragnoRouteConfig[]> =\n T[number] extends infer TRoute\n ? TRoute extends FragnoRouteConfig<\n infer TMethod,\n infer TPath,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string,\n RequestThisContext\n >\n ? string extends TPath\n ? true\n : HTTPMethod extends TMethod\n ? true\n : false\n : false\n : false;\n\n/**\n * Extract the path from a route configuration for a given method\n * @internal\n */\nexport type ExtractRoutePath<\n T extends readonly AnyFragnoRouteConfig[],\n TExpectedMethod extends HTTPMethod = HTTPMethod,\n> = [ExtractRoutePathExact<T, TExpectedMethod>] extends [never]\n ? HasWidenedRouteShape<T> extends true\n ? ExtractRoutePathLoose<T, TExpectedMethod> & string\n : never\n : ExtractRoutePathExact<T, TExpectedMethod>;\n\n/**\n * @internal\n */\nexport type ExtractGetRoutePaths<T extends readonly AnyFragnoRouteConfig[]> = ExtractRoutePath<\n T,\n \"GET\"\n>;\n\n/**\n * @internal\n */\nexport type ExtractNonGetRoutePaths<T extends readonly AnyFragnoRouteConfig[]> = ExtractRoutePath<\n T,\n NonGetHTTPMethod\n>;\n\n/**\n * @internal\n */\ntype ExtractRouteByPathExact<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TPath extends string,\n TMethod extends HTTPMethod = HTTPMethod,\n> = {\n [K in keyof TRoutes]: TRoutes[K] extends FragnoRouteConfig<\n infer TRouteMethod,\n TPath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters,\n infer TThisContext\n >\n ? TRouteMethod extends TMethod\n ? FragnoRouteConfig<\n TRouteMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : never\n : never;\n}[number];\n\n/**\n * @internal\n */\ntype ExtractRouteByPathLoose<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TPath extends string,\n TMethod extends HTTPMethod = HTTPMethod,\n> = {\n [K in keyof TRoutes]: FilterRouteByPath<FilterRouteByMethod<TRoutes[K], TMethod>, TPath>;\n}[number];\n\n/**\n * Extract the route configuration type(s) for a given path from a routes array.\n * Optionally narrow by HTTP method via the third type parameter.\n *\n * Defaults to extracting all methods for the matching path, producing a union\n * if multiple methods exist for the same path.\n * @internal\n */\nexport type ExtractRouteByPath<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TPath extends string,\n TMethod extends HTTPMethod = HTTPMethod,\n> = [ExtractRouteByPathExact<TRoutes, TPath, TMethod>] extends [never]\n ? HasWidenedRouteShape<TRoutes> extends true\n ? ExtractRouteByPathLoose<TRoutes, TPath, TMethod>\n : never\n : ExtractRouteByPathExact<TRoutes, TPath, TMethod>;\n\n/**\n * Extract the output schema type for a specific route path from a routes array\n * @internal\n */\nexport type ExtractOutputSchemaForPath<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TPath extends string,\n> =\n ExtractRouteByPath<TRoutes, TPath, \"GET\"> extends {\n outputSchema?: infer TOutputSchema;\n }\n ? TOutputSchema\n : never;\n\n/**\n * Check if a path exists as a GET route in the routes array\n * @internal\n */\nexport type IsValidGetRoutePath<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TPath extends string,\n> = TPath extends ExtractGetRoutePaths<TRoutes> ? true : false;\n\n/**\n * Utility type to ensure only valid GET route paths can be used\n * @internal\n */\nexport type ValidateGetRoutePath<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TPath extends string,\n> =\n TPath extends ExtractGetRoutePaths<TRoutes>\n ? TPath\n : `Error: Path '${TPath}' is not a valid GET route. Available GET routes: ${ExtractGetRoutePaths<TRoutes>}`;\n\n/**\n * Helper type to check if a routes array has any GET routes\n * @internal\n */\nexport type HasGetRoutes<T extends readonly AnyFragnoRouteConfig[]> =\n ExtractGetRoutePaths<T> extends never ? false : true;\n\n/**\n * @internal\n */\nexport type ObjectContainingStoreField<T extends object> = T extends Store\n ? T\n : {\n [K in keyof T]: T[K] extends Store ? { [P in K]: T[P] } & Partial<Omit<T, K>> : never;\n }[keyof T] extends never\n ? never\n : T;\n\n/**\n * @internal\n */\nexport type FragnoStoreObjectData<T extends object> = {\n obj: T;\n [STORE_SYMBOL]: true;\n};\n\nexport type FragnoStoreFactoryData<T extends object, TArgs extends unknown[] = []> = {\n factory: (...args: TArgs) => T;\n [STORE_SYMBOL]: true;\n};\n\nexport type FragnoStoreData<T extends object, TArgs extends unknown[] = []> =\n | FragnoStoreObjectData<T>\n | FragnoStoreFactoryData<T, TArgs>;\n\nexport type FragnoClientHookData<\n TMethod extends HTTPMethod,\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = {\n route: FragnoRouteConfig<\n TMethod,\n TPath,\n StandardSchemaV1 | undefined,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >;\n query(args?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string>;\n query?: Record<TQueryParameters, string | undefined>;\n }): Promise<StandardSchemaV1.InferOutput<TOutputSchema>>;\n store(args?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>;\n query?: Record<TQueryParameters, string | undefined | ReadableAtom<string | undefined>>;\n }): FetcherStore<StandardSchemaV1.InferOutput<TOutputSchema>, FragnoClientError<TErrorCode>>;\n [GET_HOOK_SYMBOL]: true;\n} & {\n // Phantom field that preserves the specific TOutputSchema type parameter\n // in the structural type. This makes the type covariant, allowing more\n // specific schema types (like z.ZodString) to be assigned to variables\n // typed with more general schema types (like StandardSchemaV1<any, any>)\n readonly _outputSchema?: TOutputSchema;\n};\n\nexport type FragnoClientMutatorData<\n TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = {\n route: FragnoRouteConfig<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >;\n\n mutateQuery(args?: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<TPath, string>;\n query?: Record<TQueryParameters, string | undefined>;\n }): Promise<InferOr<TOutputSchema, undefined>>;\n\n mutatorStore: MutatorStore<\n {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>;\n query?: Record<TQueryParameters, string | undefined | ReadableAtom<string | undefined>>;\n },\n InferOr<TOutputSchema, undefined>,\n FragnoClientError<TErrorCode>\n >;\n [MUTATOR_HOOK_SYMBOL]: true;\n} & {\n readonly _inputSchema?: TInputSchema;\n readonly _outputSchema?: TOutputSchema;\n};\n\n/**\n * @internal\n */\nexport function buildUrl<TPath extends string>(\n config: {\n baseUrl?: string;\n mountRoute: string;\n path: TPath;\n },\n params: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n },\n): string {\n const { baseUrl = \"\", mountRoute, path } = config;\n const { pathParams, queryParams } = params ?? {};\n\n const normalizedPathParams = unwrapObject(pathParams) as ExtractPathParams<TPath, string>;\n const normalizedQueryParams = unwrapObject(queryParams) ?? {};\n\n // Filter out undefined values to prevent URLSearchParams from converting them to string \"undefined\"\n const filteredQueryParams = Object.fromEntries(\n Object.entries(normalizedQueryParams).filter(([_, value]) => value !== undefined),\n ) as Record<string, string>;\n\n const searchParams = new URLSearchParams(filteredQueryParams);\n const builtPath = buildPath(path, normalizedPathParams ?? {});\n const search = searchParams.toString() ? `?${searchParams.toString()}` : \"\";\n return `${baseUrl}${mountRoute}${builtPath}${search}`;\n}\n\n/**\n * This method returns an array, which can be passed directly to nanostores.\n *\n * The returned array is always: path, pathParams (In order they appear in the path), queryParams (In alphabetical order)\n * Missing pathParams are replaced with \"<missing>\".\n * Atoms with undefined values are wrapped in computed atoms that map undefined to \"\" to avoid nanoquery treating the key as incomplete.\n * @param path\n * @param params\n * @returns\n * @internal\n */\nexport function getCacheKey<TMethod extends HTTPMethod, TPath extends string>(\n method: TMethod,\n path: TPath,\n params?: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n },\n): (string | ReadableAtom<string>)[] {\n if (!params) {\n return [method, path];\n }\n\n const { pathParams, queryParams } = params;\n\n const pathParamNames = extractPathParams(path);\n const pathParamValues = pathParamNames.map((name) => pathParams?.[name] ?? \"<missing>\");\n\n const queryParamValues = queryParams\n ? Object.keys(queryParams)\n .sort()\n .map((key) => {\n const value = queryParams[key];\n // If it's an atom, wrap it to convert undefined to \"\"\n if (value && typeof value === \"object\" && \"get\" in value) {\n return computed(value as ReadableAtom<string | undefined>, (v) => v ?? \"\");\n }\n // Plain string value (or undefined)\n return value ?? \"\";\n })\n : [];\n\n return [method, path, ...pathParamValues, ...queryParamValues];\n}\n\nfunction isStreamingResponse(response: Response): false | \"ndjson\" | \"octet-stream\" {\n const contentType = parseContentType(response.headers.get(\"content-type\"));\n\n if (!contentType) {\n // Always assume 'normal' JSON by default.\n return false;\n }\n\n const isChunked = response.headers.get(\"transfer-encoding\") === \"chunked\";\n\n if (!isChunked) {\n return false;\n }\n\n if (contentType.subtype === \"octet-stream\") {\n // TODO(Wilco): This is not actually supported properly\n return \"octet-stream\";\n }\n\n if (contentType.subtype === \"x-ndjson\") {\n return \"ndjson\";\n }\n\n return false;\n}\n\n// Type guard to check if a hook is a GET hook\n/**\n * @internal\n */\nexport function isGetHook<\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: unknown,\n): hook is FragnoClientHookData<\"GET\", TPath, TOutputSchema, TErrorCode, TQueryParameters> {\n return (\n typeof hook === \"object\" &&\n hook !== null &&\n GET_HOOK_SYMBOL in hook &&\n hook[GET_HOOK_SYMBOL] === true\n );\n}\n\n// Type guard to check if a hook is a mutator\n/**\n * @internal\n */\nexport function isMutatorHook<\n TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: unknown,\n): hook is FragnoClientMutatorData<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n> {\n return (\n typeof hook === \"object\" &&\n hook !== null &&\n MUTATOR_HOOK_SYMBOL in hook &&\n hook[MUTATOR_HOOK_SYMBOL] === true\n );\n}\n\n/**\n * @internal\n */\nexport function isStore<TStore extends object, TArgs extends unknown[] = []>(\n obj: unknown,\n): obj is FragnoStoreData<TStore, TArgs> {\n return (\n typeof obj === \"object\" && obj !== null && STORE_SYMBOL in obj && obj[STORE_SYMBOL] === true\n );\n}\n\ntype OnErrorRetryFn = (opts: {\n error: unknown;\n key: string;\n retryCount: number;\n}) => number | undefined;\n\nexport type CreateHookOptions = {\n /**\n * A function that will be called when an error occurs. Implements an exponential backoff strategy\n * when left undefined. When null, retries will be disabled. The number returned (> 0) by the\n * callback will determine in how many ms to retry next.\n */\n onErrorRetry?: OnErrorRetryFn | null;\n};\n\ntype OnInvalidateFn<TPath extends string> = (\n invalidate: <TInnerPath extends string>(\n method: HTTPMethod,\n path: TInnerPath,\n params: {\n pathParams?: MaybeExtractPathParamsOrWiden<TInnerPath, string>;\n queryParams?: Record<string, string>;\n },\n ) => void,\n params: {\n pathParams: MaybeExtractPathParamsOrWiden<TPath, string>;\n queryParams?: Record<string, string>;\n },\n) => void;\n\n/**\n * @internal\n */\nexport type CacheLine = {\n data: unknown;\n error: unknown;\n retryCount: number;\n created: number;\n expires: number;\n};\n\nexport class ClientBuilder<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n TFragmentConfig extends FragnoFragmentSharedConfig<TRoutes>,\n> {\n #publicConfig: FragnoPublicClientConfig;\n #fragmentConfig: TFragmentConfig;\n #fetcherConfig?: FetcherConfig;\n\n #cache = new Map<string, CacheLine>();\n\n #createFetcherStore;\n #createMutatorStore;\n #invalidateKeys;\n\n constructor(publicConfig: FragnoPublicClientConfig, fragmentConfig: TFragmentConfig) {\n this.#publicConfig = publicConfig;\n this.#fragmentConfig = fragmentConfig;\n this.#fetcherConfig = publicConfig.fetcherConfig;\n\n const [createFetcherStore, createMutatorStore, { invalidateKeys }] = nanoquery({\n cache: this.#cache,\n });\n this.#createFetcherStore = createFetcherStore;\n this.#createMutatorStore = createMutatorStore;\n this.#invalidateKeys = invalidateKeys;\n }\n\n get cacheEntries(): Readonly<Record<string, CacheLine>> {\n return Object.fromEntries(this.#cache.entries());\n }\n\n createStore<const TArgs extends unknown[], const T extends object>(\n factory: (...args: TArgs) => T,\n ): FragnoStoreFactoryData<T, TArgs>;\n createStore<const T extends object>(obj: T): FragnoStoreObjectData<T>;\n createStore<const TArgs extends unknown[], const T extends object>(\n input: T | ((...args: TArgs) => T),\n ): FragnoStoreData<T, TArgs> {\n if (typeof input === \"function\") {\n return { factory: input as (...args: TArgs) => T, [STORE_SYMBOL]: true };\n }\n\n return { obj: input, [STORE_SYMBOL]: true };\n }\n\n /**\n * Build a URL for a custom backend call using the configured baseUrl and mountRoute.\n * Useful for fragment authors who need to make custom fetch calls.\n */\n buildUrl<TPath extends string>(\n path: TPath,\n params?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n },\n ): string {\n const baseUrl = this.#publicConfig.baseUrl ?? \"\";\n const mountRoute = getMountRoute({\n name: this.#fragmentConfig.name,\n mountRoute: this.#publicConfig.mountRoute,\n });\n\n return buildUrl(\n { baseUrl, mountRoute, path },\n { pathParams: params?.path, queryParams: params?.query },\n );\n }\n\n /**\n * Get the configured fetcher function for custom backend calls.\n * Returns fetch with merged options applied.\n */\n getFetcher(): {\n fetcher: typeof fetch;\n defaultOptions: RequestInit | undefined;\n } {\n return {\n fetcher: this.#getFetcher(),\n defaultOptions: this.#getFetcherOptions(),\n };\n }\n\n #getFetcher(): typeof fetch {\n if (this.#fetcherConfig?.type === \"function\") {\n return this.#fetcherConfig.fetcher;\n }\n return globalThis.fetch.bind(globalThis);\n }\n\n #getFetcherOptions(): RequestInit | undefined {\n if (this.#fetcherConfig?.type === \"options\") {\n return this.#fetcherConfig.options;\n }\n return undefined;\n }\n\n createHook<TPath extends ExtractGetRoutePaths<TFragmentConfig[\"routes\"]>>(\n path: ValidateGetRoutePath<TFragmentConfig[\"routes\"], TPath>,\n options?: CreateHookOptions,\n ): FragnoClientHookData<\n \"GET\",\n TPath,\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath, \"GET\">[\"outputSchema\"]>,\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath, \"GET\">[\"errorCodes\"]>[number],\n NonNullable<\n ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath, \"GET\">[\"queryParameters\"]\n >[number]\n > {\n const route = this.#fragmentConfig.routes.find(\n (\n r,\n ): r is FragnoRouteConfig<\n \"GET\",\n TPath,\n StandardSchemaV1 | undefined,\n StandardSchemaV1,\n string,\n string\n > => r.path === path && r.method === \"GET\" && r.outputSchema !== undefined,\n );\n\n if (!route) {\n throw new Error(`Route '${path}' not found or is not a GET route with an output schema.`);\n }\n\n return this.#createRouteQueryHook(route, options);\n }\n\n createMutator<TPath extends ExtractNonGetRoutePaths<TFragmentConfig[\"routes\"]>>(\n method: NonGetHTTPMethod,\n path: TPath,\n onInvalidate?: OnInvalidateFn<TPath>,\n ): FragnoClientMutatorData<\n NonGetHTTPMethod, // TODO: This can be any Method, but should be related to TPath\n TPath,\n ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"inputSchema\"],\n ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"outputSchema\"],\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"errorCodes\"]>[number],\n NonNullable<ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>[\"queryParameters\"]>[number]\n > {\n type TRoute = ExtractRouteByPath<TFragmentConfig[\"routes\"], TPath>;\n\n const route = this.#fragmentConfig.routes.find(\n (\n r,\n ): r is FragnoRouteConfig<\n NonGetHTTPMethod,\n TPath,\n TRoute[\"inputSchema\"],\n TRoute[\"outputSchema\"],\n string,\n string\n > => r.method !== \"GET\" && r.path === path && r.method === method,\n );\n\n if (!route) {\n throw new Error(\n `Route '${path}' not found or is a GET route with an input and output schema.`,\n );\n }\n\n return this.#createRouteQueryMutator(route, onInvalidate);\n }\n\n #createRouteQueryHook<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n >(\n route: FragnoRouteConfig<\n \"GET\",\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >,\n options: CreateHookOptions = {},\n ): FragnoClientHookData<\"GET\", TPath, TOutputSchema, TErrorCode, TQueryParameters> {\n if (route.method !== \"GET\") {\n throw new Error(\n `Only GET routes are supported for hooks. Route '${route.path}' is a ${route.method} route.`,\n );\n }\n\n if (!route.outputSchema) {\n throw new Error(\n `Output schema is required for GET routes. Route '${route.path}' has no output schema.`,\n );\n }\n\n const baseUrl = this.#publicConfig.baseUrl ?? \"\";\n const mountRoute = getMountRoute({\n name: this.#fragmentConfig.name,\n mountRoute: this.#publicConfig.mountRoute,\n });\n const fetcher = this.#getFetcher();\n const fetcherOptions = this.#getFetcherOptions();\n\n async function callServerSideHandler(params: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n }): Promise<Response> {\n const { pathParams, queryParams } = params ?? {};\n\n const normalizedPathParams = unwrapObject(pathParams) as ExtractPathParams<TPath, string>;\n const normalizedQueryParams = unwrapObject(queryParams) ?? {};\n\n // Filter out undefined values to prevent URLSearchParams from converting them to string \"undefined\"\n const filteredQueryParams = Object.fromEntries(\n Object.entries(normalizedQueryParams).filter(([_, value]) => value !== undefined),\n ) as Record<string, string>;\n\n const searchParams = new URLSearchParams(filteredQueryParams);\n\n const result = await route.handler(\n RequestInputContext.fromSSRContext({\n method: route.method,\n path: route.path,\n pathParams: normalizedPathParams,\n searchParams,\n }),\n new RequestOutputContext(route.outputSchema),\n );\n\n return result;\n }\n\n async function executeQuery(params?: {\n pathParams?: Record<string, string | ReadableAtom<string>>;\n queryParams?: Record<string, string | undefined | ReadableAtom<string | undefined>>;\n }): Promise<Response> {\n const { pathParams, queryParams } = params ?? {};\n\n if (typeof window === \"undefined\") {\n return task(async () => callServerSideHandler({ pathParams, queryParams }));\n }\n\n const url = buildUrl({ baseUrl, mountRoute, path: route.path }, { pathParams, queryParams });\n\n let response: Response;\n try {\n response = fetcherOptions ? await fetcher(url, fetcherOptions) : await fetcher(url);\n } catch (error) {\n throw FragnoClientFetchError.fromUnknownFetchError(error);\n }\n\n if (!response.ok) {\n throw await FragnoClientApiError.fromResponse<TErrorCode>(response);\n }\n\n return response;\n }\n\n return {\n route,\n store: (args) => {\n const { path, query } = args ?? {};\n\n const key = getCacheKey(route.method, route.path, {\n pathParams: path,\n queryParams: query,\n });\n\n const store = this.#createFetcherStore<\n StandardSchemaV1.InferOutput<TOutputSchema>,\n FragnoClientError<TErrorCode>\n >(key, {\n fetcher: async (): Promise<StandardSchemaV1.InferOutput<TOutputSchema>> => {\n if (SSR_ENABLED) {\n const initialData = getInitialData(\n key.map((d) => (typeof d === \"string\" ? d : d.get())).join(\"\"),\n );\n\n if (initialData) {\n return initialData;\n }\n }\n\n const response = await executeQuery({ pathParams: path, queryParams: query });\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return response.json() as Promise<StandardSchemaV1.InferOutput<TOutputSchema>>;\n }\n\n if (typeof window === \"undefined\") {\n return [];\n }\n\n if (isStreaming === \"ndjson\") {\n const storeAdapter: NdjsonStreamingStore<TOutputSchema, TErrorCode> = {\n setData: (value) => {\n store.set({\n ...store.get(),\n loading: !(Array.isArray(value) && value.length > 0),\n data: value as InferOr<TOutputSchema, undefined>,\n });\n },\n setError: (value) => {\n store.set({\n ...store.get(),\n error: value,\n });\n },\n };\n\n // Start streaming in background and return first item\n const { firstItem } = await handleNdjsonStreamingFirstItem(response, storeAdapter);\n return [firstItem];\n }\n\n if (isStreaming === \"octet-stream\") {\n // TODO(Wilco): Implement this\n throw new Error(\"Octet-stream streaming is not supported.\");\n }\n\n throw new Error(\"Unreachable\");\n },\n\n onErrorRetry: options?.onErrorRetry,\n dedupeTime: Infinity,\n });\n\n if (typeof window === \"undefined\") {\n addStore(store);\n }\n\n return store;\n },\n query: async (args) => {\n const { path, query } = args ?? {};\n\n const response = await executeQuery({ pathParams: path, queryParams: query });\n\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return (await response.json()) as StandardSchemaV1.InferOutput<TOutputSchema>;\n }\n\n if (isStreaming === \"ndjson\") {\n const { streamingPromise } = await handleNdjsonStreamingFirstItem(response);\n // Resolves once the stream is done\n return await streamingPromise;\n }\n\n if (isStreaming === \"octet-stream\") {\n // TODO(Wilco): Implement this\n throw new Error(\"Octet-stream streaming is not supported.\");\n }\n\n throw new Error(\"Unreachable\");\n },\n [GET_HOOK_SYMBOL]: true,\n };\n }\n\n #createRouteQueryMutator<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n >(\n route: FragnoRouteConfig<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >,\n onInvalidate: OnInvalidateFn<TPath> = (invalidate, params) =>\n invalidate(\"GET\", route.path, params),\n ): FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n > {\n const method = route.method;\n\n const baseUrl = this.#publicConfig.baseUrl ?? \"\";\n const mountRoute = getMountRoute({\n name: this.#fragmentConfig.name,\n mountRoute: this.#publicConfig.mountRoute,\n });\n const fetcher = this.#getFetcher();\n const fetcherOptions = this.#getFetcherOptions();\n\n async function executeMutateQuery({\n body,\n path,\n query,\n }: {\n body?: InferOr<TInputSchema, undefined>;\n path?: ExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n }): Promise<Response> {\n if (typeof window === \"undefined\") {\n return task(async () =>\n route.handler(\n RequestInputContext.fromSSRContext({\n inputSchema: route.inputSchema,\n method,\n path: route.path,\n pathParams: (path ?? {}) as ExtractPathParams<TPath, string>,\n searchParams: new URLSearchParams(query),\n body,\n }),\n new RequestOutputContext(route.outputSchema),\n ),\n );\n }\n\n const url = buildUrl(\n { baseUrl, mountRoute, path: route.path },\n { pathParams: path, queryParams: query },\n );\n\n let response: Response;\n try {\n const { body: preparedBody, headers: bodyHeaders } = prepareRequestBody(\n body,\n route.contentType,\n );\n\n // Merge headers: fetcherOptions headers + body-specific headers (e.g., Content-Type for JSON)\n // For FormData, bodyHeaders is undefined and browser sets Content-Type with boundary automatically\n const mergedHeaders = mergeRequestHeaders(\n fetcherOptions?.headers as HeadersInit | undefined,\n bodyHeaders,\n );\n\n const requestOptions: RequestInit & { duplex?: \"half\" } = {\n ...fetcherOptions,\n method,\n body: preparedBody,\n ...(mergedHeaders ? { headers: mergedHeaders } : {}),\n };\n if (preparedBody instanceof ReadableStream) {\n requestOptions.duplex = \"half\";\n }\n response = await fetcher(url, requestOptions);\n } catch (error) {\n throw FragnoClientFetchError.fromUnknownFetchError(error);\n }\n\n if (!response.ok) {\n throw await FragnoClientApiError.fromResponse<TErrorCode>(response);\n }\n\n return response;\n }\n\n const mutatorStore: FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >[\"mutatorStore\"] = this.#createMutatorStore(\n async ({ data }) => {\n if (typeof window === \"undefined\") {\n // TODO(Wilco): Handle server-side rendering.\n }\n\n const { body, path, query } = data as {\n body?: InferOr<TInputSchema, undefined>;\n path?: ExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n };\n\n await assertBodyProvided(body, route.inputSchema, \"Body is required.\");\n\n const response = await executeMutateQuery({ body, path, query });\n\n onInvalidate(this.#invalidate.bind(this), {\n pathParams: (path ?? {}) as MaybeExtractPathParamsOrWiden<TPath, string>,\n queryParams: query,\n });\n\n if (response.status === 201 || response.status === 204) {\n return undefined;\n }\n\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return response.json();\n }\n\n if (typeof window === \"undefined\") {\n return [];\n }\n\n if (isStreaming === \"ndjson\") {\n const storeAdapter: NdjsonStreamingStore<NonNullable<TOutputSchema>, TErrorCode> = {\n setData: (value) => {\n mutatorStore.set({\n ...mutatorStore.get(),\n loading: !(Array.isArray(value) && value.length > 0),\n data: value as InferOr<TOutputSchema, undefined>,\n });\n },\n setError: (value) => {\n mutatorStore.set({\n ...mutatorStore.get(),\n error: value,\n });\n },\n };\n\n // Start streaming in background and return first item\n const { firstItem } = await handleNdjsonStreamingFirstItem(response, storeAdapter);\n\n // Return the first item immediately. The streaming will continue in the background\n return [firstItem];\n }\n\n if (isStreaming === \"octet-stream\") {\n // TODO(Wilco): Implement this\n throw new Error(\"Octet-stream streaming is not supported.\");\n }\n\n throw new Error(\"Unreachable\");\n },\n {\n onError: (error) => {\n console.error(\"Error in mutatorStore\", error);\n },\n },\n );\n\n const mutateQuery = (async (data) => {\n // TypeScript infers the fields to not exist, even though they might\n const { body, path, query } = data as {\n body?: InferOr<TInputSchema, undefined>;\n path?: ExtractPathParamsOrWiden<TPath, string>;\n query?: Record<string, string>;\n };\n\n await assertBodyProvided(body, route.inputSchema, \"Body is required for mutateQuery\");\n\n const response = await executeMutateQuery({ body, path, query });\n\n if (response.status === 201 || response.status === 204) {\n return undefined;\n }\n\n const isStreaming = isStreamingResponse(response);\n\n if (!isStreaming) {\n return response.json();\n }\n\n if (isStreaming === \"ndjson\") {\n const { streamingPromise } = await handleNdjsonStreamingFirstItem(response);\n // Resolves once the stream is done, i.e. we block until done\n return await streamingPromise;\n }\n\n if (isStreaming === \"octet-stream\") {\n throw new Error(\"Octet-stream streaming is not supported for mutations\");\n }\n\n throw new Error(\"Unreachable\");\n }) satisfies FragnoClientMutatorData<\n NonGetHTTPMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >[\"mutateQuery\"];\n\n return {\n route,\n mutateQuery,\n mutatorStore,\n [MUTATOR_HOOK_SYMBOL]: true,\n };\n }\n\n #invalidate<TPath extends string>(\n method: HTTPMethod,\n path: TPath,\n params: {\n pathParams?: MaybeExtractPathParamsOrWiden<TPath, string>;\n queryParams?: Record<string, string>;\n },\n ) {\n const prefixArray = getCacheKey(method, path, {\n pathParams: params?.pathParams,\n queryParams: params?.queryParams,\n });\n\n const prefix = prefixArray.map((k) => (typeof k === \"string\" ? k : k.get())).join(\"\");\n\n this.#invalidateKeys((key) => key.startsWith(prefix));\n }\n}\n\n/**\n * Create a client builder for fragments using the new fragment definition API.\n * This is the same as createClientBuilder but works with FragmentDefinition.\n */\nexport function createClientBuilder<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TInternalRoutes extends readonly AnyRouteOrFactory[] = readonly [],\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TInternalRoutes\n >,\n publicConfig: FragnoPublicClientConfig,\n routesOrFactories: TRoutesOrFactories,\n authorFetcherConfig?: FetcherConfig,\n): ClientBuilder<\n FlattenRouteFactories<TRoutesOrFactories>,\n FragnoFragmentSharedConfig<FlattenRouteFactories<TRoutesOrFactories>>\n> {\n // For client-side, we resolve route factories with dummy context\n // This will be removed by the bundle plugin anyway\n const dummyContext = {\n config: {} as TConfig,\n deps: {} as TDeps,\n services: {} as TBaseServices & TServices,\n serviceDeps: {},\n };\n\n const routes = resolveRouteFactories(dummyContext, routesOrFactories);\n\n const fragmentConfig: FragnoFragmentSharedConfig<FlattenRouteFactories<TRoutesOrFactories>> = {\n name: definition.name,\n routes,\n };\n\n const mountRoute = getMountRoute({\n name: definition.name,\n mountRoute: publicConfig.mountRoute,\n });\n const mergedFetcherConfig = mergeFetcherConfigs(authorFetcherConfig, publicConfig.fetcherConfig);\n const fullPublicConfig = {\n ...publicConfig,\n mountRoute,\n fetcherConfig: mergedFetcherConfig,\n };\n\n return new ClientBuilder(fullPublicConfig, fragmentConfig);\n}\n\nexport * from \"./client-error\";\nexport type { FetcherConfig, FragnoPublicClientConfig } from \"../api/shared-types\";\nexport type { FragnoFragmentSharedConfig } from \"../api/fragment-instantiator\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiDA,MAAM,kBAAkB,OAAO,kBAAkB;AACjD,MAAM,sBAAsB,OAAO,sBAAsB;AACzD,MAAM,eAAe,OAAO,eAAe;;;;;AAM3C,SAAS,cAAc,OAAyB;AAC9C,KAAI,iBAAiB,QAAQ,iBAAiB,KAC5C,QAAO;AAGT,KAAI,iBAAiB,SACnB,QAAO;AAGT,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO,OAAO,OAAO,MAAM,CAAC,MACzB,MAAM,aAAa,QAAQ,aAAa,QAAQ,aAAa,SAC/D;AAGH,QAAO;;;;;;;;AAST,SAAS,WAAW,OAAyB;CAC3C,MAAM,WAAW,IAAI,UAAU;AAE/B,MAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,MAAM,CAC5C,KAAI,eAAe,KACjB,UAAS,OAAO,KAAK,KAAK,IAAI,KAAK;UAC1B,eAAe,KACxB,UAAS,OAAO,KAAK,IAAI;UAChB,QAAQ,UAAa,QAAQ,KAEtC,UAAS,OAAO,KAAK,OAAO,QAAQ,WAAW,MAAM,KAAK,UAAU,IAAI,CAAC;AAI7E,QAAO;;;;;;;AAQT,SAAS,mBACP,MACA,aACuD;AACvD,KAAI,SAAS,OACX,QAAO,EAAE,MAAM,QAAW;AAG5B,KAAI,gBAAgB,4BAA4B;AAC9C,MACE,gBAAgB,kBAChB,gBAAgB,QAChB,gBAAgB,QAChB,gBAAgB,eAChB,gBAAgB,WAEhB,QAAO;GAAQ;GAAkB,SAAS,EAAE,gBAAgB,4BAA4B;GAAE;AAG5F,QAAM,IAAI,MACR,iGACD;;AAIH,KAAI,gBAAgB,SAClB,QAAO,EAAE,MAAM;AAIjB,KAAI,gBAAgB,MAAM;EACxB,MAAM,WAAW,IAAI,UAAU;AAC/B,WAAS,OAAO,QAAQ,MAAM,KAAK,KAAK;AACxC,SAAO,EAAE,MAAM,UAAU;;AAG3B,KAAI,gBAAgB,MAAM;EACxB,MAAM,WAAW,IAAI,UAAU;AAC/B,WAAS,OAAO,QAAQ,KAAK;AAC7B,SAAO,EAAE,MAAM,UAAU;;AAI3B,KAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,cAAc,KAAK,CAClE,QAAO,EAAE,MAAM,WAAW,KAAK,EAAE;AAInC,QAAO;EACL,MAAM,KAAK,UAAU,KAAK;EAC1B,SAAS,EAAE,gBAAgB,oBAAoB;EAChD;;AAGH,eAAe,sBAAsB,QAA4C;AAC/E,KAAI;AAEF,SAAO,EADQ,MAAM,OAAO,aAAa,SAAS,OAAU,EAC7C;SACT;AACN,SAAO;;;AAIX,eAAe,mBACb,MACA,aACA,cACe;AACf,KAAI,OAAO,SAAS,eAAe,gBAAgB,OACjD;AAGF,KAAI,MAAM,sBAAsB,YAAY,CAC1C;AAGF,OAAM,IAAI,MAAM,aAAa;;;;;;;AAQ/B,SAAS,oBACP,GAAG,eACiC;CACpC,MAAMA,SAAiC,EAAE;CACzC,IAAI,aAAa;AAEjB,MAAK,MAAM,UAAU,eAAe;AAClC,MAAI,CAAC,OACH;AAGF,MAAI,kBAAkB,QACpB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,EAAE;AAC3C,UAAO,OAAO;AACd,gBAAa;;WAEN,MAAM,QAAQ,OAAO,CAC9B,MAAK,MAAM,CAAC,KAAK,UAAU,QAAQ;AACjC,UAAO,OAAO;AACd,gBAAa;;MAGf,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;AACjD,UAAO,OAAO;AACd,gBAAa;;;AAKnB,QAAO,aAAa,SAAS;;;;;AAoZ/B,SAAgB,SACd,QAKA,QAIQ;CACR,MAAM,EAAE,UAAU,IAAI,YAAY,SAAS;CAC3C,MAAM,EAAE,YAAY,gBAAgB,UAAU,EAAE;CAEhD,MAAM,uBAAuB,aAAa,WAAW;CACrD,MAAM,wBAAwB,aAAa,YAAY,IAAI,EAAE;CAG7D,MAAM,sBAAsB,OAAO,YACjC,OAAO,QAAQ,sBAAsB,CAAC,QAAQ,CAAC,GAAG,WAAW,UAAU,OAAU,CAClF;CAED,MAAM,eAAe,IAAI,gBAAgB,oBAAoB;AAG7D,QAAO,GAAG,UAAU,aAFF,UAAU,MAAM,wBAAwB,EAAE,CAAC,GAC9C,aAAa,UAAU,GAAG,IAAI,aAAa,UAAU,KAAK;;;;;;;;;;;;;AAe3E,SAAgB,YACd,QACA,MACA,QAImC;AACnC,KAAI,CAAC,OACH,QAAO,CAAC,QAAQ,KAAK;CAGvB,MAAM,EAAE,YAAY,gBAAgB;CAGpC,MAAM,kBADiB,kBAAkB,KAAK,CACP,KAAK,SAAS,aAAa,SAAS,YAAY;CAEvF,MAAM,mBAAmB,cACrB,OAAO,KAAK,YAAY,CACrB,MAAM,CACN,KAAK,QAAQ;EACZ,MAAM,QAAQ,YAAY;AAE1B,MAAI,SAAS,OAAO,UAAU,YAAY,SAAS,MACjD,QAAO,SAAS,QAA4C,MAAM,KAAK,GAAG;AAG5E,SAAO,SAAS;GAChB,GACJ,EAAE;AAEN,QAAO;EAAC;EAAQ;EAAM,GAAG;EAAiB,GAAG;EAAiB;;AAGhE,SAAS,oBAAoB,UAAuD;CAClF,MAAM,cAAc,iBAAiB,SAAS,QAAQ,IAAI,eAAe,CAAC;AAE1E,KAAI,CAAC,YAEH,QAAO;AAKT,KAAI,EAFc,SAAS,QAAQ,IAAI,oBAAoB,KAAK,WAG9D,QAAO;AAGT,KAAI,YAAY,YAAY,eAE1B,QAAO;AAGT,KAAI,YAAY,YAAY,WAC1B,QAAO;AAGT,QAAO;;;;;AAOT,SAAgB,UAMd,MACyF;AACzF,QACE,OAAO,SAAS,YAChB,SAAS,QACT,mBAAmB,QACnB,KAAK,qBAAqB;;;;;AAQ9B,SAAgB,cAQd,MAQA;AACA,QACE,OAAO,SAAS,YAChB,SAAS,QACT,uBAAuB,QACvB,KAAK,yBAAyB;;;;;AAOlC,SAAgB,QACd,KACuC;AACvC,QACE,OAAO,QAAQ,YAAY,QAAQ,QAAQ,gBAAgB,OAAO,IAAI,kBAAkB;;AA6C5F,IAAa,gBAAb,MAUE;CACA;CACA;CACA;CAEA,yBAAS,IAAI,KAAwB;CAErC;CACA;CACA;CAEA,YAAY,cAAwC,gBAAiC;AACnF,QAAKC,eAAgB;AACrB,QAAKC,iBAAkB;AACvB,QAAKC,gBAAiB,aAAa;EAEnC,MAAM,CAAC,oBAAoB,oBAAoB,EAAE,oBAAoB,UAAU,EAC7E,OAAO,MAAKC,OACb,CAAC;AACF,QAAKC,qBAAsB;AAC3B,QAAKC,qBAAsB;AAC3B,QAAKC,iBAAkB;;CAGzB,IAAI,eAAoD;AACtD,SAAO,OAAO,YAAY,MAAKH,MAAO,SAAS,CAAC;;CAOlD,YACE,OAC2B;AAC3B,MAAI,OAAO,UAAU,WACnB,QAAO;GAAE,SAAS;IAAiC,eAAe;GAAM;AAG1E,SAAO;GAAE,KAAK;IAAQ,eAAe;GAAM;;;;;;CAO7C,SACE,MACA,QAIQ;AAOR,SAAO,SACL;GAAE,SAPY,MAAKH,aAAc,WAAW;GAOjC,YANM,cAAc;IAC/B,MAAM,MAAKC,eAAgB;IAC3B,YAAY,MAAKD,aAAc;IAChC,CAAC;GAGuB;GAAM,EAC7B;GAAE,YAAY,QAAQ;GAAM,aAAa,QAAQ;GAAO,CACzD;;;;;;CAOH,aAGE;AACA,SAAO;GACL,SAAS,MAAKO,YAAa;GAC3B,gBAAgB,MAAKC,mBAAoB;GAC1C;;CAGH,cAA4B;AAC1B,MAAI,MAAKN,eAAgB,SAAS,WAChC,QAAO,MAAKA,cAAe;AAE7B,SAAO,WAAW,MAAM,KAAK,WAAW;;CAG1C,qBAA8C;AAC5C,MAAI,MAAKA,eAAgB,SAAS,UAChC,QAAO,MAAKA,cAAe;;CAK/B,WACE,MACA,SASA;EACA,MAAM,QAAQ,MAAKD,eAAgB,OAAO,MAEtC,MAQG,EAAE,SAAS,QAAQ,EAAE,WAAW,SAAS,EAAE,iBAAiB,OAClE;AAED,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,UAAU,KAAK,0DAA0D;AAG3F,SAAO,MAAKQ,qBAAsB,OAAO,QAAQ;;CAGnD,cACE,QACA,MACA,cAQA;EAGA,MAAM,QAAQ,MAAKR,eAAgB,OAAO,MAEtC,MAQG,EAAE,WAAW,SAAS,EAAE,SAAS,QAAQ,EAAE,WAAW,OAC5D;AAED,MAAI,CAAC,MACH,OAAM,IAAI,MACR,UAAU,KAAK,gEAChB;AAGH,SAAO,MAAKS,wBAAyB,OAAO,aAAa;;CAG3D,sBAOE,OAQA,UAA6B,EAAE,EACkD;AACjF,MAAI,MAAM,WAAW,MACnB,OAAM,IAAI,MACR,mDAAmD,MAAM,KAAK,SAAS,MAAM,OAAO,SACrF;AAGH,MAAI,CAAC,MAAM,aACT,OAAM,IAAI,MACR,oDAAoD,MAAM,KAAK,yBAChE;EAGH,MAAM,UAAU,MAAKV,aAAc,WAAW;EAC9C,MAAM,aAAa,cAAc;GAC/B,MAAM,MAAKC,eAAgB;GAC3B,YAAY,MAAKD,aAAc;GAChC,CAAC;EACF,MAAM,UAAU,MAAKO,YAAa;EAClC,MAAM,iBAAiB,MAAKC,mBAAoB;EAEhD,eAAe,sBAAsB,QAGf;GACpB,MAAM,EAAE,YAAY,gBAAgB,UAAU,EAAE;GAEhD,MAAM,uBAAuB,aAAa,WAAW;GACrD,MAAM,wBAAwB,aAAa,YAAY,IAAI,EAAE;GAG7D,MAAM,sBAAsB,OAAO,YACjC,OAAO,QAAQ,sBAAsB,CAAC,QAAQ,CAAC,GAAG,WAAW,UAAU,OAAU,CAClF;GAED,MAAM,eAAe,IAAI,gBAAgB,oBAAoB;AAY7D,UAVe,MAAM,MAAM,QACzB,oBAAoB,eAAe;IACjC,QAAQ,MAAM;IACd,MAAM,MAAM;IACZ,YAAY;IACZ;IACD,CAAC,EACF,IAAI,qBAAqB,MAAM,aAAa,CAC7C;;EAKH,eAAe,aAAa,QAGN;GACpB,MAAM,EAAE,YAAY,gBAAgB,UAAU,EAAE;AAEhD,OAAI,OAAO,WAAW,YACpB,QAAO,KAAK,YAAY,sBAAsB;IAAE;IAAY;IAAa,CAAC,CAAC;GAG7E,MAAM,MAAM,SAAS;IAAE;IAAS;IAAY,MAAM,MAAM;IAAM,EAAE;IAAE;IAAY;IAAa,CAAC;GAE5F,IAAIG;AACJ,OAAI;AACF,eAAW,iBAAiB,MAAM,QAAQ,KAAK,eAAe,GAAG,MAAM,QAAQ,IAAI;YAC5E,OAAO;AACd,UAAM,uBAAuB,sBAAsB,MAAM;;AAG3D,OAAI,CAAC,SAAS,GACZ,OAAM,MAAM,qBAAqB,aAAyB,SAAS;AAGrE,UAAO;;AAGT,SAAO;GACL;GACA,QAAQ,SAAS;IACf,MAAM,EAAE,MAAM,UAAU,QAAQ,EAAE;IAElC,MAAM,MAAM,YAAY,MAAM,QAAQ,MAAM,MAAM;KAChD,YAAY;KACZ,aAAa;KACd,CAAC;IAEF,MAAM,QAAQ,MAAKP,mBAGjB,KAAK;KACL,SAAS,YAAkE;AACzE,UAAI,aAAa;OACf,MAAM,cAAc,eAClB,IAAI,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,KAAK,CAAE,CAAC,KAAK,GAAG,CAC/D;AAED,WAAI,YACF,QAAO;;MAIX,MAAM,WAAW,MAAM,aAAa;OAAE,YAAY;OAAM,aAAa;OAAO,CAAC;MAC7E,MAAM,cAAc,oBAAoB,SAAS;AAEjD,UAAI,CAAC,YACH,QAAO,SAAS,MAAM;AAGxB,UAAI,OAAO,WAAW,YACpB,QAAO,EAAE;AAGX,UAAI,gBAAgB,UAAU;OAkB5B,MAAM,EAAE,cAAc,MAAM,+BAA+B,UAjBW;QACpE,UAAU,UAAU;AAClB,eAAM,IAAI;UACR,GAAG,MAAM,KAAK;UACd,SAAS,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,SAAS;UAClD,MAAM;UACP,CAAC;;QAEJ,WAAW,UAAU;AACnB,eAAM,IAAI;UACR,GAAG,MAAM,KAAK;UACd,OAAO;UACR,CAAC;;QAEL,CAGiF;AAClF,cAAO,CAAC,UAAU;;AAGpB,UAAI,gBAAgB,eAElB,OAAM,IAAI,MAAM,2CAA2C;AAG7D,YAAM,IAAI,MAAM,cAAc;;KAGhC,cAAc,SAAS;KACvB,YAAY;KACb,CAAC;AAEF,QAAI,OAAO,WAAW,YACpB,UAAS,MAAM;AAGjB,WAAO;;GAET,OAAO,OAAO,SAAS;IACrB,MAAM,EAAE,MAAM,UAAU,QAAQ,EAAE;IAElC,MAAM,WAAW,MAAM,aAAa;KAAE,YAAY;KAAM,aAAa;KAAO,CAAC;IAE7E,MAAM,cAAc,oBAAoB,SAAS;AAEjD,QAAI,CAAC,YACH,QAAQ,MAAM,SAAS,MAAM;AAG/B,QAAI,gBAAgB,UAAU;KAC5B,MAAM,EAAE,qBAAqB,MAAM,+BAA+B,SAAS;AAE3E,YAAO,MAAM;;AAGf,QAAI,gBAAgB,eAElB,OAAM,IAAI,MAAM,2CAA2C;AAG7D,UAAM,IAAI,MAAM,cAAc;;IAE/B,kBAAkB;GACpB;;CAGH,yBAOE,OAQA,gBAAuC,YAAY,WACjD,WAAW,OAAO,MAAM,MAAM,OAAO,EAQvC;EACA,MAAM,SAAS,MAAM;EAErB,MAAM,UAAU,MAAKJ,aAAc,WAAW;EAC9C,MAAM,aAAa,cAAc;GAC/B,MAAM,MAAKC,eAAgB;GAC3B,YAAY,MAAKD,aAAc;GAChC,CAAC;EACF,MAAM,UAAU,MAAKO,YAAa;EAClC,MAAM,iBAAiB,MAAKC,mBAAoB;EAEhD,eAAe,mBAAmB,EAChC,MACA,MACA,SAKoB;AACpB,OAAI,OAAO,WAAW,YACpB,QAAO,KAAK,YACV,MAAM,QACJ,oBAAoB,eAAe;IACjC,aAAa,MAAM;IACnB;IACA,MAAM,MAAM;IACZ,YAAa,QAAQ,EAAE;IACvB,cAAc,IAAI,gBAAgB,MAAM;IACxC;IACD,CAAC,EACF,IAAI,qBAAqB,MAAM,aAAa,CAC7C,CACF;GAGH,MAAM,MAAM,SACV;IAAE;IAAS;IAAY,MAAM,MAAM;IAAM,EACzC;IAAE,YAAY;IAAM,aAAa;IAAO,CACzC;GAED,IAAIG;AACJ,OAAI;IACF,MAAM,EAAE,MAAM,cAAc,SAAS,gBAAgB,mBACnD,MACA,MAAM,YACP;IAID,MAAM,gBAAgB,oBACpB,gBAAgB,SAChB,YACD;IAED,MAAMC,iBAAoD;KACxD,GAAG;KACH;KACA,MAAM;KACN,GAAI,gBAAgB,EAAE,SAAS,eAAe,GAAG,EAAE;KACpD;AACD,QAAI,wBAAwB,eAC1B,gBAAe,SAAS;AAE1B,eAAW,MAAM,QAAQ,KAAK,eAAe;YACtC,OAAO;AACd,UAAM,uBAAuB,sBAAsB,MAAM;;AAG3D,OAAI,CAAC,SAAS,GACZ,OAAM,MAAM,qBAAqB,aAAyB,SAAS;AAGrE,UAAO;;EAGT,MAAMC,eAOc,MAAKR,mBACvB,OAAO,EAAE,WAAW;AAClB,OAAI,OAAO,WAAW,aAAa;GAInC,MAAM,EAAE,MAAM,MAAM,UAAU;AAM9B,SAAM,mBAAmB,MAAM,MAAM,aAAa,oBAAoB;GAEtE,MAAM,WAAW,MAAM,mBAAmB;IAAE;IAAM;IAAM;IAAO,CAAC;AAEhE,gBAAa,MAAKS,WAAY,KAAK,KAAK,EAAE;IACxC,YAAa,QAAQ,EAAE;IACvB,aAAa;IACd,CAAC;AAEF,OAAI,SAAS,WAAW,OAAO,SAAS,WAAW,IACjD;GAGF,MAAM,cAAc,oBAAoB,SAAS;AAEjD,OAAI,CAAC,YACH,QAAO,SAAS,MAAM;AAGxB,OAAI,OAAO,WAAW,YACpB,QAAO,EAAE;AAGX,OAAI,gBAAgB,UAAU;IAkB5B,MAAM,EAAE,cAAc,MAAM,+BAA+B,UAjBwB;KACjF,UAAU,UAAU;AAClB,mBAAa,IAAI;OACf,GAAG,aAAa,KAAK;OACrB,SAAS,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,SAAS;OAClD,MAAM;OACP,CAAC;;KAEJ,WAAW,UAAU;AACnB,mBAAa,IAAI;OACf,GAAG,aAAa,KAAK;OACrB,OAAO;OACR,CAAC;;KAEL,CAGiF;AAGlF,WAAO,CAAC,UAAU;;AAGpB,OAAI,gBAAgB,eAElB,OAAM,IAAI,MAAM,2CAA2C;AAG7D,SAAM,IAAI,MAAM,cAAc;KAEhC,EACE,UAAU,UAAU;AAClB,WAAQ,MAAM,yBAAyB,MAAM;KAEhD,CACF;EAED,MAAM,eAAe,OAAO,SAAS;GAEnC,MAAM,EAAE,MAAM,MAAM,UAAU;AAM9B,SAAM,mBAAmB,MAAM,MAAM,aAAa,mCAAmC;GAErF,MAAM,WAAW,MAAM,mBAAmB;IAAE;IAAM;IAAM;IAAO,CAAC;AAEhE,OAAI,SAAS,WAAW,OAAO,SAAS,WAAW,IACjD;GAGF,MAAM,cAAc,oBAAoB,SAAS;AAEjD,OAAI,CAAC,YACH,QAAO,SAAS,MAAM;AAGxB,OAAI,gBAAgB,UAAU;IAC5B,MAAM,EAAE,qBAAqB,MAAM,+BAA+B,SAAS;AAE3E,WAAO,MAAM;;AAGf,OAAI,gBAAgB,eAClB,OAAM,IAAI,MAAM,wDAAwD;AAG1E,SAAM,IAAI,MAAM,cAAc;;AAUhC,SAAO;GACL;GACA;GACA;IACC,sBAAsB;GACxB;;CAGH,YACE,QACA,MACA,QAIA;EAMA,MAAM,SALc,YAAY,QAAQ,MAAM;GAC5C,YAAY,QAAQ;GACpB,aAAa,QAAQ;GACtB,CAAC,CAEyB,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,KAAK,CAAE,CAAC,KAAK,GAAG;AAErF,QAAKR,gBAAiB,QAAQ,IAAI,WAAW,OAAO,CAAC;;;;;;;AAQzD,SAAgB,oBAcd,YAaA,cACA,mBACA,qBAIA;CAUA,MAAM,SAAS,sBAPM;EACnB,QAAQ,EAAE;EACV,MAAM,EAAE;EACR,UAAU,EAAE;EACZ,aAAa,EAAE;EAChB,EAEkD,kBAAkB;CAErE,MAAMS,iBAAwF;EAC5F,MAAM,WAAW;EACjB;EACD;CAED,MAAM,aAAa,cAAc;EAC/B,MAAM,WAAW;EACjB,YAAY,aAAa;EAC1B,CAAC;CACF,MAAM,sBAAsB,oBAAoB,qBAAqB,aAAa,cAAc;AAOhG,QAAO,IAAI,cANc;EACvB,GAAG;EACH;EACA,eAAe;EAChB,EAE0C,eAAe"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { MaybeExtractPathParamsOrWiden, QueryParamsHint } from "../api/internal/path.js";
|
|
2
2
|
import { InferOr } from "../util/types-util.js";
|
|
3
|
-
import { NonGetHTTPMethod } from "../api/api.js";
|
|
4
3
|
import { FragnoClientError } from "./client-error.js";
|
|
5
|
-
import { FragnoClientHookData, FragnoClientMutatorData, FragnoStoreData } from "./client.js";
|
|
4
|
+
import { FragnoClientHookData, FragnoClientMutatorData, FragnoStoreData, FragnoStoreFactoryData, FragnoStoreObjectData } from "./client.js";
|
|
5
|
+
import { NonGetHTTPMethod } from "../api/api.js";
|
|
6
6
|
import { ReadableAtom } from "nanostores";
|
|
7
7
|
import { Readable } from "svelte/store";
|
|
8
8
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
@@ -28,8 +28,9 @@ type FragnoSvelteMutator<_TMethod extends NonGetHTTPMethod, TPath$1 extends stri
|
|
|
28
28
|
};
|
|
29
29
|
declare function readableToAtom<T>(value: Readable<T>): ReadableAtom<T>;
|
|
30
30
|
declare function runeToAtom<T>(value: () => T): ReadableAtom<T>;
|
|
31
|
-
|
|
32
|
-
declare function
|
|
31
|
+
type FragnoSvelteStore<T extends object, TArgs extends unknown[] = []> = TArgs extends [] ? T : (...args: TArgs) => T;
|
|
32
|
+
declare function createSvelteStore<T extends object, TArgs extends unknown[]>(hook: FragnoStoreData<T, TArgs>): FragnoSvelteStore<T, TArgs>;
|
|
33
|
+
declare function useFragno<T extends Record<string, unknown>>(clientObj: T): { [K in keyof T]: T[K] extends FragnoClientHookData<"GET", infer TPath, infer TOutputSchema, infer TErrorCode, infer TQueryParameters> ? FragnoSvelteHook<"GET", TPath, TOutputSchema, TErrorCode, TQueryParameters> : T[K] extends FragnoClientMutatorData<infer M, infer TPath, infer TInputSchema, infer TOutputSchema, infer TErrorCode, infer TQueryParameters> ? FragnoSvelteMutator<M, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> : T[K] extends FragnoStoreObjectData<infer TStoreObj> ? FragnoSvelteStore<TStoreObj, []> : T[K] extends FragnoStoreFactoryData<infer TStoreObj, infer TStoreArgs> ? FragnoSvelteStore<TStoreObj, TStoreArgs> : T[K] };
|
|
33
34
|
//#endregion
|
|
34
|
-
export { FragnoSvelteHook, FragnoSvelteMutator, createSvelteStore, readableToAtom, runeToAtom, useFragno };
|
|
35
|
+
export { FragnoSvelteHook, FragnoSvelteMutator, FragnoSvelteStore, createSvelteStore, readableToAtom, runeToAtom, useFragno };
|
|
35
36
|
//# sourceMappingURL=client.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.svelte.d.ts","names":[],"sources":["../../src/client/client.svelte.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"client.svelte.d.ts","names":[],"sources":["../../src/client/client.svelte.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isGetHook, isMutatorHook, isStore } from "./client.js";
|
|
2
2
|
import { atom } from "nanostores";
|
|
3
|
-
import { get, writable } from "svelte/store";
|
|
4
3
|
import { onDestroy } from "svelte";
|
|
4
|
+
import { get, writable } from "svelte/store";
|
|
5
5
|
|
|
6
6
|
//#region src/client/client.svelte.ts
|
|
7
7
|
function isReadable(value) {
|
|
@@ -100,7 +100,15 @@ function createSvelteMutator(hook) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
function createSvelteStore(hook) {
|
|
103
|
-
return hook.obj;
|
|
103
|
+
if ("obj" in hook) return hook.obj;
|
|
104
|
+
return ((...args) => {
|
|
105
|
+
const value = hook.factory(...args);
|
|
106
|
+
const disposer = value[Symbol.dispose];
|
|
107
|
+
if (typeof disposer === "function") onDestroy(() => {
|
|
108
|
+
disposer.call(value);
|
|
109
|
+
});
|
|
110
|
+
return value;
|
|
111
|
+
});
|
|
104
112
|
}
|
|
105
113
|
function useFragno(clientObj) {
|
|
106
114
|
const result = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.svelte.js","names":["pathParams: Record<string, string | ReadableAtom<string>>","queryParams: Record<string, string | ReadableAtom<string>>"],"sources":["../../src/client/client.svelte.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { atom, type ReadableAtom } from \"nanostores\";\nimport type { NonGetHTTPMethod } from \"../api/api\";\nimport {\n isGetHook,\n isMutatorHook,\n isStore,\n type FragnoClientHookData,\n type FragnoClientMutatorData,\n type FragnoStoreData,\n} from \"./client\";\nimport type { FragnoClientError } from \"./client-error\";\nimport type { InferOr } from \"../util/types-util\";\nimport type { MaybeExtractPathParamsOrWiden, QueryParamsHint } from \"../api/internal/path\";\n\nimport { writable, type Readable, get } from \"svelte/store\";\nimport { onDestroy } from \"svelte\";\n\nexport type FragnoSvelteHook<\n _TMethod extends \"GET\",\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = (args?: {\n path?:\n | MaybeExtractPathParamsOrWiden<TPath, string | Readable<string> | ReadableAtom<string>>\n | (() => string);\n query?: QueryParamsHint<\n TQueryParameters,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n}) => {\n data: Readable<InferOr<TOutputSchema, undefined>>;\n loading: Readable<boolean>;\n error: Readable<FragnoClientError<TErrorCode[number]> | undefined>;\n};\n\nexport type FragnoSvelteMutator<\n _TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = () => {\n mutate: (args: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<\n TPath,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n query?: QueryParamsHint<\n TQueryParameters,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n }) => Promise<InferOr<TOutputSchema, undefined>>;\n loading: Readable<boolean | undefined>;\n error: Readable<FragnoClientError<TErrorCode[number]> | undefined>;\n data: Readable<InferOr<TOutputSchema, undefined>>;\n};\n\nfunction isReadable(value: unknown): value is Readable<string> {\n return typeof value === \"object\" && value !== null && \"subscribe\" in value;\n}\n\nfunction isCallable(value: unknown): value is () => string {\n return typeof value === \"function\";\n}\n\nexport function readableToAtom<T>(value: Readable<T>): ReadableAtom<T> {\n const a = atom(get(value));\n\n value.subscribe((newVal) => {\n a.set(newVal);\n });\n\n return a;\n}\n\nexport function runeToAtom<T>(value: () => T): ReadableAtom<T> {\n const a = atom(value());\n\n $effect(() => {\n a.set(value());\n });\n\n return a;\n}\n\nfunction createSvelteHook<\n TMethod extends \"GET\",\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: FragnoClientHookData<TMethod, TPath, TOutputSchema, TErrorCode, TQueryParameters>,\n): FragnoSvelteHook<TMethod, TPath, TOutputSchema, TErrorCode, TQueryParameters> {\n return ({ path, query } = {}) => {\n const pathParams: Record<string, string | ReadableAtom<string>> = {};\n const queryParams: Record<string, string | ReadableAtom<string>> = {};\n\n for (const [key, value] of Object.entries(path ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string> | (() => string);\n if (isCallable(v)) {\n pathParams[key] = runeToAtom(v);\n } else {\n pathParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string> | (() => string);\n if (isCallable(v)) {\n queryParams[key] = runeToAtom(v);\n } else {\n queryParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n }\n\n const store = hook.store({\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n\n const data = writable<InferOr<TOutputSchema, undefined>>(undefined);\n const loading = writable<boolean>(false);\n const error = writable<FragnoClientError<TErrorCode[number]> | undefined>(undefined);\n\n const unsubscribe = store.subscribe((updatedStoreValue) => {\n data.set(updatedStoreValue.data as InferOr<TOutputSchema, undefined>);\n loading.set(updatedStoreValue.loading);\n error.set(updatedStoreValue.error);\n });\n\n onDestroy(() => {\n unsubscribe();\n });\n\n return {\n data,\n loading,\n error,\n };\n };\n}\n\nfunction createSvelteMutator<\n TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: FragnoClientMutatorData<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >,\n): FragnoSvelteMutator<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> {\n return () => {\n const data = writable<InferOr<TOutputSchema, undefined>>(undefined);\n const loading = writable<boolean | undefined>(undefined);\n const error = writable<FragnoClientError<TErrorCode[number]> | undefined>(undefined);\n\n // Subscribe to the mutator store and sync with our Svelte stores\n const unsubscribe = hook.mutatorStore.subscribe((storeValue) => {\n data.set(storeValue.data as InferOr<TOutputSchema, undefined>);\n loading.set(storeValue.loading);\n error.set(storeValue.error);\n });\n\n onDestroy(() => {\n unsubscribe();\n });\n\n // Create a wrapped mutate function that handles Svelte readable stores\n const mutate = async (args: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<\n TPath,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n query?: QueryParamsHint<\n TQueryParameters,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n }) => {\n const { body, path, query } = args;\n\n const pathParams: Record<string, string | ReadableAtom<string>> = {};\n const queryParams: Record<string, string | ReadableAtom<string>> = {};\n\n for (const [key, value] of Object.entries(path ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string>;\n pathParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string>;\n queryParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n\n // Call the store's mutate function with normalized params\n return hook.mutatorStore.mutate({\n body,\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n };\n\n return {\n mutate,\n loading,\n error,\n data,\n };\n };\n}\n\nexport function createSvelteStore<T extends object>(hook: FragnoStoreData<T>): T {\n // Since nanostores already implement Svelte's store contract,\n // we can return the store object directly for use with $ syntax\n return hook.obj;\n}\n\nexport function useFragno<T extends Record<string, unknown>>(\n clientObj: T,\n): {\n [K in keyof T]: T[K] extends FragnoClientHookData<\n \"GET\",\n infer TPath,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters\n >\n ? FragnoSvelteHook<\"GET\", TPath, TOutputSchema, TErrorCode, TQueryParameters>\n : T[K] extends FragnoClientMutatorData<\n infer M,\n infer TPath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters\n >\n ? FragnoSvelteMutator<M, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters>\n : T[K] extends FragnoStoreData<infer TStoreObj>\n ? TStoreObj\n : T[K];\n} {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = {} as any;\n\n for (const key in clientObj) {\n if (!Object.prototype.hasOwnProperty.call(clientObj, key)) {\n continue;\n }\n\n const hook = clientObj[key];\n if (isGetHook(hook)) {\n result[key] = createSvelteHook(hook);\n } else if (isMutatorHook(hook)) {\n result[key] = createSvelteMutator(hook);\n } else if (isStore(hook)) {\n result[key] = createSvelteStore(hook);\n } else {\n // Pass through non-hook values unchanged\n result[key] = hook;\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.svelte.js","names":["pathParams: Record<string, string | ReadableAtom<string>>","queryParams: Record<string, string | ReadableAtom<string>>"],"sources":["../../src/client/client.svelte.ts"],"sourcesContent":["import { atom, type ReadableAtom } from \"nanostores\";\nimport { onDestroy } from \"svelte\";\nimport { writable, type Readable, get } from \"svelte/store\";\n\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nimport type { NonGetHTTPMethod } from \"../api/api\";\nimport type { MaybeExtractPathParamsOrWiden, QueryParamsHint } from \"../api/internal/path\";\nimport type { InferOr } from \"../util/types-util\";\nimport {\n isGetHook,\n isMutatorHook,\n isStore,\n type FragnoClientHookData,\n type FragnoClientMutatorData,\n type FragnoStoreData,\n type FragnoStoreFactoryData,\n type FragnoStoreObjectData,\n} from \"./client\";\nimport type { FragnoClientError } from \"./client-error\";\n\nexport type FragnoSvelteHook<\n _TMethod extends \"GET\",\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = (args?: {\n path?:\n | MaybeExtractPathParamsOrWiden<TPath, string | Readable<string> | ReadableAtom<string>>\n | (() => string);\n query?: QueryParamsHint<\n TQueryParameters,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n}) => {\n data: Readable<InferOr<TOutputSchema, undefined>>;\n loading: Readable<boolean>;\n error: Readable<FragnoClientError<TErrorCode[number]> | undefined>;\n};\n\nexport type FragnoSvelteMutator<\n _TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = () => {\n mutate: (args: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<\n TPath,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n query?: QueryParamsHint<\n TQueryParameters,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n }) => Promise<InferOr<TOutputSchema, undefined>>;\n loading: Readable<boolean | undefined>;\n error: Readable<FragnoClientError<TErrorCode[number]> | undefined>;\n data: Readable<InferOr<TOutputSchema, undefined>>;\n};\n\nfunction isReadable(value: unknown): value is Readable<string> {\n return typeof value === \"object\" && value !== null && \"subscribe\" in value;\n}\n\nfunction isCallable(value: unknown): value is () => string {\n return typeof value === \"function\";\n}\n\nexport function readableToAtom<T>(value: Readable<T>): ReadableAtom<T> {\n const a = atom(get(value));\n\n value.subscribe((newVal) => {\n a.set(newVal);\n });\n\n return a;\n}\n\nexport function runeToAtom<T>(value: () => T): ReadableAtom<T> {\n const a = atom(value());\n\n $effect(() => {\n a.set(value());\n });\n\n return a;\n}\n\nfunction createSvelteHook<\n TMethod extends \"GET\",\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: FragnoClientHookData<TMethod, TPath, TOutputSchema, TErrorCode, TQueryParameters>,\n): FragnoSvelteHook<TMethod, TPath, TOutputSchema, TErrorCode, TQueryParameters> {\n return ({ path, query } = {}) => {\n const pathParams: Record<string, string | ReadableAtom<string>> = {};\n const queryParams: Record<string, string | ReadableAtom<string>> = {};\n\n for (const [key, value] of Object.entries(path ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string> | (() => string);\n if (isCallable(v)) {\n pathParams[key] = runeToAtom(v);\n } else {\n pathParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string> | (() => string);\n if (isCallable(v)) {\n queryParams[key] = runeToAtom(v);\n } else {\n queryParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n }\n\n const store = hook.store({\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n\n const data = writable<InferOr<TOutputSchema, undefined>>(undefined);\n const loading = writable<boolean>(false);\n const error = writable<FragnoClientError<TErrorCode[number]> | undefined>(undefined);\n\n const unsubscribe = store.subscribe((updatedStoreValue) => {\n data.set(updatedStoreValue.data as InferOr<TOutputSchema, undefined>);\n loading.set(updatedStoreValue.loading);\n error.set(updatedStoreValue.error);\n });\n\n onDestroy(() => {\n unsubscribe();\n });\n\n return {\n data,\n loading,\n error,\n };\n };\n}\n\nfunction createSvelteMutator<\n TMethod extends NonGetHTTPMethod,\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined,\n TOutputSchema extends StandardSchemaV1 | undefined,\n TErrorCode extends string,\n TQueryParameters extends string,\n>(\n hook: FragnoClientMutatorData<\n TMethod,\n TPath,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters\n >,\n): FragnoSvelteMutator<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> {\n return () => {\n const data = writable<InferOr<TOutputSchema, undefined>>(undefined);\n const loading = writable<boolean | undefined>(undefined);\n const error = writable<FragnoClientError<TErrorCode[number]> | undefined>(undefined);\n\n // Subscribe to the mutator store and sync with our Svelte stores\n const unsubscribe = hook.mutatorStore.subscribe((storeValue) => {\n data.set(storeValue.data as InferOr<TOutputSchema, undefined>);\n loading.set(storeValue.loading);\n error.set(storeValue.error);\n });\n\n onDestroy(() => {\n unsubscribe();\n });\n\n // Create a wrapped mutate function that handles Svelte readable stores\n const mutate = async (args: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<\n TPath,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n query?: QueryParamsHint<\n TQueryParameters,\n string | Readable<string> | ReadableAtom<string> | (() => string)\n >;\n }) => {\n const { body, path, query } = args;\n\n const pathParams: Record<string, string | ReadableAtom<string>> = {};\n const queryParams: Record<string, string | ReadableAtom<string>> = {};\n\n for (const [key, value] of Object.entries(path ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string>;\n pathParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n const v = value as string | Readable<string> | ReadableAtom<string>;\n queryParams[key] = isReadable(v) ? readableToAtom(v) : v;\n }\n\n // Call the store's mutate function with normalized params\n return hook.mutatorStore.mutate({\n body,\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n };\n\n return {\n mutate,\n loading,\n error,\n data,\n };\n };\n}\n\nexport type FragnoSvelteStore<T extends object, TArgs extends unknown[] = []> = TArgs extends []\n ? T\n : (...args: TArgs) => T;\n\nexport function createSvelteStore<T extends object, TArgs extends unknown[]>(\n hook: FragnoStoreData<T, TArgs>,\n): FragnoSvelteStore<T, TArgs> {\n if (\"obj\" in hook) {\n // Since nanostores already implement Svelte's store contract,\n // we can return the store object directly for use with $ syntax\n return hook.obj as FragnoSvelteStore<T, TArgs>;\n }\n\n return ((...args: TArgs) => {\n const value = hook.factory(...args);\n const disposer = value[Symbol.dispose as keyof typeof value];\n if (typeof disposer === \"function\") {\n onDestroy(() => {\n disposer.call(value);\n });\n }\n\n return value;\n }) as FragnoSvelteStore<T, TArgs>;\n}\n\nexport function useFragno<T extends Record<string, unknown>>(\n clientObj: T,\n): {\n [K in keyof T]: T[K] extends FragnoClientHookData<\n \"GET\",\n infer TPath,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters\n >\n ? FragnoSvelteHook<\"GET\", TPath, TOutputSchema, TErrorCode, TQueryParameters>\n : T[K] extends FragnoClientMutatorData<\n infer M,\n infer TPath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters\n >\n ? FragnoSvelteMutator<M, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters>\n : T[K] extends FragnoStoreObjectData<infer TStoreObj>\n ? FragnoSvelteStore<TStoreObj, []>\n : T[K] extends FragnoStoreFactoryData<infer TStoreObj, infer TStoreArgs>\n ? FragnoSvelteStore<TStoreObj, TStoreArgs>\n : T[K];\n} {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result = {} as any;\n\n for (const key in clientObj) {\n if (!Object.prototype.hasOwnProperty.call(clientObj, key)) {\n continue;\n }\n\n const hook = clientObj[key];\n if (isGetHook(hook)) {\n result[key] = createSvelteHook(hook);\n } else if (isMutatorHook(hook)) {\n result[key] = createSvelteMutator(hook);\n } else if (isStore(hook)) {\n result[key] = createSvelteStore(hook);\n } else {\n // Pass through non-hook values unchanged\n result[key] = hook;\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ndjson-streaming.js","names":["firstItem: StandardSchemaV1.InferOutput<TOutputSchema> | null","items: StandardSchemaV1.InferOutput<TOutputSchema>[]","lines"],"sources":["../../../src/client/internal/ndjson-streaming.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport {\n FragnoClientError,\n FragnoClientFetchError,\n FragnoClientFetchAbortError,\n FragnoClientUnknownApiError,\n} from \"../client-error\";\n\n/**\n * Creates a promise that rejects when the abort signal is triggered\n */\nfunction createAbortPromise(abortSignal: AbortSignal): Promise<never> {\n return new Promise<never>((_, reject) => {\n const abortHandler = () => {\n reject(new FragnoClientFetchAbortError(\"Operation was aborted\"));\n };\n\n if (abortSignal.aborted) {\n abortHandler();\n } else {\n abortSignal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n}\n\n/**\n * Result of NDJSON streaming that includes the first item and a promise for the streaming continuation\n */\nexport interface NdjsonStreamingResult<T> {\n firstItem: T;\n streamingPromise: Promise<T[]>;\n}\n\nexport interface NdjsonStreamingStore<\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n> {\n setData(value: StandardSchemaV1.InferOutput<TOutputSchema>): void;\n setError(value: FragnoClientError<TErrorCode>): void;\n}\n\n/**\n * Handles NDJSON streaming responses by returning the first item from the fetcher\n * and then continuing to stream updates via the store's mutate method.\n *\n * This makes it so that we can wait until the first chunk before updating the store, if we did\n * not do this, `loading` would briefly be false before the first item would be populated in the\n * result.\n *\n * @param response - The fetch Response object containing the NDJSON stream\n * @param store - The fetcher store to update with streaming data\n * @param abortSignal - Optional AbortSignal to cancel the streaming operation\n * @returns A promise that resolves to an object containing the first item and a streaming promise\n */\nexport async function handleNdjsonStreamingFirstItem<\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n>(\n response: Response,\n store?: NdjsonStreamingStore<TOutputSchema, TErrorCode>,\n options: { abortSignal?: AbortSignal } = {},\n): Promise<NdjsonStreamingResult<StandardSchemaV1.InferOutput<TOutputSchema>>> {\n if (!response.body) {\n throw new FragnoClientFetchError(\"Streaming response has no body\", \"NO_BODY\");\n }\n\n const { abortSignal } = options;\n\n if (abortSignal?.aborted) {\n throw new FragnoClientFetchAbortError(\"Operation was aborted\");\n }\n\n const decoder = new TextDecoder();\n const reader = response.body.getReader();\n let buffer = \"\";\n let firstItem: StandardSchemaV1.InferOutput<TOutputSchema> | null = null;\n const items: StandardSchemaV1.InferOutput<TOutputSchema>[] = [];\n\n try {\n // Read until we get the first item\n while (firstItem === null) {\n // Check for abort signal before each read\n if (abortSignal?.aborted) {\n reader.releaseLock();\n throw new FragnoClientFetchAbortError(\"Operation was aborted\");\n }\n\n const { done, value } = await (abortSignal\n ? Promise.race([reader.read(), createAbortPromise(abortSignal)])\n : reader.read());\n\n if (done) {\n break;\n }\n\n // Decode the chunk and add to buffer\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n try {\n const jsonObject = JSON.parse(line) as StandardSchemaV1.InferOutput<TOutputSchema>;\n items.push(jsonObject);\n\n if (firstItem === null) {\n firstItem = jsonObject;\n // We don't call store.setKey here for the first item\n // The caller will handle it via the return value\n\n // Start background streaming for remaining items and return the promise\n const streamingPromise = continueStreaming(\n reader,\n decoder,\n buffer,\n items,\n store,\n abortSignal,\n );\n return {\n firstItem,\n streamingPromise,\n };\n }\n } catch (parseError) {\n throw new FragnoClientUnknownApiError(\"Failed to parse NDJSON line\", 500, {\n cause: parseError,\n });\n }\n }\n }\n\n // If we get here and haven't returned a first item, the stream was empty\n if (firstItem === null) {\n reader.releaseLock();\n throw new FragnoClientUnknownApiError(\"NDJSON stream contained no valid items\", 500);\n }\n\n // This should never be reached, but TypeScript needs it\n reader.releaseLock();\n throw new FragnoClientFetchError(\"Unexpected end of stream processing\", \"NO_BODY\");\n } catch (error) {\n // Handle errors during streaming\n if (error instanceof FragnoClientError) {\n store?.setError(error);\n throw error;\n } else {\n // TODO: Not sure about the typing here\n const clientError = new FragnoClientUnknownApiError(\"Unknown streaming error\", 500, {\n cause: error,\n }) as unknown as FragnoClientError<TErrorCode>;\n store?.setError(clientError);\n throw clientError;\n }\n }\n}\n\n/**\n * Continues streaming the remaining items in the background\n */\n// FIXME: Shitty code\nasync function continueStreaming<TOutputSchema extends StandardSchemaV1, TErrorCode extends string>(\n reader: ReadableStreamDefaultReader<Uint8Array>,\n decoder: TextDecoder,\n initialBuffer: string,\n items: StandardSchemaV1.InferOutput<TOutputSchema>[],\n store?: NdjsonStreamingStore<TOutputSchema, TErrorCode>,\n abortSignal?: AbortSignal,\n): Promise<StandardSchemaV1.InferOutput<TOutputSchema>[]> {\n let buffer = initialBuffer;\n\n try {\n while (true) {\n // Check for abort signal before each read\n if (abortSignal?.aborted) {\n throw new FragnoClientFetchAbortError(\"Operation was aborted\");\n }\n\n const { done, value } = await (abortSignal\n ? Promise.race([reader.read(), createAbortPromise(abortSignal)])\n : reader.read());\n\n if (done) {\n // Process any remaining buffer content\n if (buffer.trim()) {\n const lines = buffer.split(\"\\n\");\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n try {\n const jsonObject = JSON.parse(line) as StandardSchemaV1.InferOutput<TOutputSchema>;\n items.push(jsonObject);\n store?.setData([...items]);\n } catch (parseError) {\n throw new FragnoClientUnknownApiError(\"Failed to parse NDJSON line\", 400, {\n cause: parseError,\n });\n }\n }\n }\n break;\n }\n\n // Decode the chunk and add to buffer\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n try {\n const jsonObject = JSON.parse(line) as StandardSchemaV1.InferOutput<TOutputSchema>;\n items.push(jsonObject);\n store?.setData([...items]);\n } catch (parseError) {\n throw new FragnoClientUnknownApiError(\"Failed to parse NDJSON line\", 400, {\n cause: parseError,\n });\n }\n }\n }\n } catch (error) {\n if (error instanceof FragnoClientError) {\n store?.setError(error);\n } else {\n const clientError = new FragnoClientUnknownApiError(\"Unknown streaming error\", 400, {\n cause: error,\n }) as unknown as FragnoClientError<TErrorCode>;\n store?.setError(clientError);\n throw clientError;\n }\n\n throw error;\n } finally {\n reader.releaseLock();\n }\n\n return items;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ndjson-streaming.js","names":["firstItem: StandardSchemaV1.InferOutput<TOutputSchema> | null","items: StandardSchemaV1.InferOutput<TOutputSchema>[]","lines"],"sources":["../../../src/client/internal/ndjson-streaming.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nimport {\n FragnoClientError,\n FragnoClientFetchError,\n FragnoClientFetchAbortError,\n FragnoClientUnknownApiError,\n} from \"../client-error\";\n\n/**\n * Creates a promise that rejects when the abort signal is triggered\n */\nfunction createAbortPromise(abortSignal: AbortSignal): Promise<never> {\n return new Promise<never>((_, reject) => {\n const abortHandler = () => {\n reject(new FragnoClientFetchAbortError(\"Operation was aborted\"));\n };\n\n if (abortSignal.aborted) {\n abortHandler();\n } else {\n abortSignal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n}\n\n/**\n * Result of NDJSON streaming that includes the first item and a promise for the streaming continuation\n */\nexport interface NdjsonStreamingResult<T> {\n firstItem: T;\n streamingPromise: Promise<T[]>;\n}\n\nexport interface NdjsonStreamingStore<\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n> {\n setData(value: StandardSchemaV1.InferOutput<TOutputSchema>): void;\n setError(value: FragnoClientError<TErrorCode>): void;\n}\n\n/**\n * Handles NDJSON streaming responses by returning the first item from the fetcher\n * and then continuing to stream updates via the store's mutate method.\n *\n * This makes it so that we can wait until the first chunk before updating the store, if we did\n * not do this, `loading` would briefly be false before the first item would be populated in the\n * result.\n *\n * @param response - The fetch Response object containing the NDJSON stream\n * @param store - The fetcher store to update with streaming data\n * @param abortSignal - Optional AbortSignal to cancel the streaming operation\n * @returns A promise that resolves to an object containing the first item and a streaming promise\n */\nexport async function handleNdjsonStreamingFirstItem<\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n>(\n response: Response,\n store?: NdjsonStreamingStore<TOutputSchema, TErrorCode>,\n options: { abortSignal?: AbortSignal } = {},\n): Promise<NdjsonStreamingResult<StandardSchemaV1.InferOutput<TOutputSchema>>> {\n if (!response.body) {\n throw new FragnoClientFetchError(\"Streaming response has no body\", \"NO_BODY\");\n }\n\n const { abortSignal } = options;\n\n if (abortSignal?.aborted) {\n throw new FragnoClientFetchAbortError(\"Operation was aborted\");\n }\n\n const decoder = new TextDecoder();\n const reader = response.body.getReader();\n let buffer = \"\";\n let firstItem: StandardSchemaV1.InferOutput<TOutputSchema> | null = null;\n const items: StandardSchemaV1.InferOutput<TOutputSchema>[] = [];\n\n try {\n // Read until we get the first item\n while (firstItem === null) {\n // Check for abort signal before each read\n if (abortSignal?.aborted) {\n reader.releaseLock();\n throw new FragnoClientFetchAbortError(\"Operation was aborted\");\n }\n\n const { done, value } = await (abortSignal\n ? Promise.race([reader.read(), createAbortPromise(abortSignal)])\n : reader.read());\n\n if (done) {\n break;\n }\n\n // Decode the chunk and add to buffer\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n try {\n const jsonObject = JSON.parse(line) as StandardSchemaV1.InferOutput<TOutputSchema>;\n items.push(jsonObject);\n\n if (firstItem === null) {\n firstItem = jsonObject;\n // We don't call store.setKey here for the first item\n // The caller will handle it via the return value\n\n // Start background streaming for remaining items and return the promise\n const streamingPromise = continueStreaming(\n reader,\n decoder,\n buffer,\n items,\n store,\n abortSignal,\n );\n return {\n firstItem,\n streamingPromise,\n };\n }\n } catch (parseError) {\n throw new FragnoClientUnknownApiError(\"Failed to parse NDJSON line\", 500, {\n cause: parseError,\n });\n }\n }\n }\n\n // If we get here and haven't returned a first item, the stream was empty\n if (firstItem === null) {\n reader.releaseLock();\n throw new FragnoClientUnknownApiError(\"NDJSON stream contained no valid items\", 500);\n }\n\n // This should never be reached, but TypeScript needs it\n reader.releaseLock();\n throw new FragnoClientFetchError(\"Unexpected end of stream processing\", \"NO_BODY\");\n } catch (error) {\n // Handle errors during streaming\n if (error instanceof FragnoClientError) {\n store?.setError(error);\n throw error;\n } else {\n // TODO: Not sure about the typing here\n const clientError = new FragnoClientUnknownApiError(\"Unknown streaming error\", 500, {\n cause: error,\n }) as unknown as FragnoClientError<TErrorCode>;\n store?.setError(clientError);\n throw clientError;\n }\n }\n}\n\n/**\n * Continues streaming the remaining items in the background\n */\n// FIXME: Shitty code\nasync function continueStreaming<TOutputSchema extends StandardSchemaV1, TErrorCode extends string>(\n reader: ReadableStreamDefaultReader<Uint8Array>,\n decoder: TextDecoder,\n initialBuffer: string,\n items: StandardSchemaV1.InferOutput<TOutputSchema>[],\n store?: NdjsonStreamingStore<TOutputSchema, TErrorCode>,\n abortSignal?: AbortSignal,\n): Promise<StandardSchemaV1.InferOutput<TOutputSchema>[]> {\n let buffer = initialBuffer;\n\n try {\n while (true) {\n // Check for abort signal before each read\n if (abortSignal?.aborted) {\n throw new FragnoClientFetchAbortError(\"Operation was aborted\");\n }\n\n const { done, value } = await (abortSignal\n ? Promise.race([reader.read(), createAbortPromise(abortSignal)])\n : reader.read());\n\n if (done) {\n // Process any remaining buffer content\n if (buffer.trim()) {\n const lines = buffer.split(\"\\n\");\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n try {\n const jsonObject = JSON.parse(line) as StandardSchemaV1.InferOutput<TOutputSchema>;\n items.push(jsonObject);\n store?.setData([...items]);\n } catch (parseError) {\n throw new FragnoClientUnknownApiError(\"Failed to parse NDJSON line\", 400, {\n cause: parseError,\n });\n }\n }\n }\n break;\n }\n\n // Decode the chunk and add to buffer\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n\n try {\n const jsonObject = JSON.parse(line) as StandardSchemaV1.InferOutput<TOutputSchema>;\n items.push(jsonObject);\n store?.setData([...items]);\n } catch (parseError) {\n throw new FragnoClientUnknownApiError(\"Failed to parse NDJSON line\", 400, {\n cause: parseError,\n });\n }\n }\n }\n } catch (error) {\n if (error instanceof FragnoClientError) {\n store?.setError(error);\n } else {\n const clientError = new FragnoClientUnknownApiError(\"Unknown streaming error\", 400, {\n cause: error,\n }) as unknown as FragnoClientError<TErrorCode>;\n store?.setError(clientError);\n throw clientError;\n }\n\n throw error;\n } finally {\n reader.releaseLock();\n }\n\n return items;\n}\n"],"mappings":"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"}
|
package/dist/client/react.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ExtractPathParamsOrWiden, HasPathParams, MaybeExtractPathParamsOrWiden, QueryParamsHint } from "../api/internal/path.js";
|
|
2
2
|
import { InferOr } from "../util/types-util.js";
|
|
3
|
-
import { NonGetHTTPMethod } from "../api/api.js";
|
|
4
3
|
import { FragnoClientError } from "./client-error.js";
|
|
5
|
-
import { FragnoClientHookData, FragnoClientMutatorData,
|
|
4
|
+
import { FragnoClientHookData, FragnoClientMutatorData, FragnoStoreFactoryData, FragnoStoreObjectData } from "./client.js";
|
|
5
|
+
import { NonGetHTTPMethod } from "../api/api.js";
|
|
6
6
|
import { ReadableAtom, Store, StoreValue } from "nanostores";
|
|
7
7
|
import { FetcherValue } from "@nanostores/query";
|
|
8
8
|
import * as react0 from "react";
|
|
@@ -31,8 +31,9 @@ type FragnoReactMutator<_TMethod extends NonGetHTTPMethod, TPath$1 extends strin
|
|
|
31
31
|
/**
|
|
32
32
|
* Type helper that unwraps any Store fields of the object into StoreValues
|
|
33
33
|
*/
|
|
34
|
-
type
|
|
35
|
-
|
|
34
|
+
type FragnoReactStoreValue<T extends object> = T extends Store<infer TStore> ? StoreValue<TStore> : { [K in keyof T]: T[K] extends Store ? StoreValue<T[K]> : T[K] };
|
|
35
|
+
type FragnoReactStore<T extends object, TArgs extends unknown[] = []> = (...args: TArgs) => FragnoReactStoreValue<T>;
|
|
36
|
+
declare function useFragno<T extends Record<string, unknown>>(clientObj: T): { [K in keyof T]: T[K] extends FragnoClientHookData<"GET", infer TPath, infer TOutputSchema, infer TErrorCode, infer TQueryParameters> ? FragnoReactHook<"GET", TPath, TOutputSchema, TErrorCode, TQueryParameters> : T[K] extends FragnoClientMutatorData<infer TMethod, infer TPath, infer TInput, infer TOutput, infer TError, infer TQueryParameters> ? FragnoReactMutator<TMethod, TPath, TInput, TOutput, TError, TQueryParameters> : T[K] extends FragnoStoreObjectData<infer TStoreObj> ? FragnoReactStore<TStoreObj, []> : T[K] extends FragnoStoreFactoryData<infer TStoreObj, infer TStoreArgs> ? FragnoReactStore<TStoreObj, TStoreArgs> : T[K] };
|
|
36
37
|
type StoreKeys<T> = T extends {
|
|
37
38
|
setKey: (k: infer K, v: unknown) => unknown;
|
|
38
39
|
} ? K : never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","names":[],"sources":["../../src/client/react.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"react.d.ts","names":[],"sources":["../../src/client/react.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;KAkCY,wFAGY,yFAAA;SAIf,8BAA8B,kBAAgB;EAP3C,KAAA,CAAA,EAQF,eARiB,CAQD,kBARC,EAAA,MAAA,GAQ0B,YAR1B,CAAA,MAAA,CAAA,CAAA;CAGH,EAAA,GAMlB,YANkB,CAOtB,gBAAA,CAAiB,WAPK,CAOO,eAPP,CAAA,EAQtB,iBARsB,CAQJ,WARI,CAQQ,YARR,CAAA,CAAA,CAAA;AAIe,KAO3B,kBAP2B,CAAA,iBAQpB,gBARoB,EAAA,gBAAA,MAAA,EAAA,qBAUhB,gBAVgB,GAAA,SAAA,EAAA,wBAWf,gBAXe,GAAA,SAAA,EAAA,qBAAA,MAAA,EAAA,2BAAA,MAAA,CAAA,GAAA,GAAA,GAAA;EAAgB,MAAA,EAAA,CAAA;IAAA,IAAA;IAAA,IAAA;IAAA;EAC7C,CAD6C,EAAA;IAA9C,IAAA,CAAA,EAoBE,OApBF,CAoBU,YApBV,EAAA,SAAA,CAAA;IACiB,IAAA,CAAA,EAoBf,aApBe,CAoBD,OApBC,CAAA,SAAA,IAAA,GAqBlB,wBArBkB,CAqBO,OArBP,EAAA,MAAA,GAqBuB,YArBvB,CAAA,MAAA,CAAA,CAAA,GAAA,SAAA;IAA2B,KAAA,CAAA,EAuBzC,eAvByC,CAuBzB,kBAvByB,EAAA,MAAA,GAuBE,YAvBF,CAAA,MAAA,CAAA,CAAA;EAA3C,CAAA,EAAA,GAwBF,OAxBE,CAwBM,OAxBN,CAwBc,eAxBd,EAAA,SAAA,CAAA,CAAA;EAEqB,OAAA,CAAA,EAAA,OAAA,GAAA,SAAA;EAA7B,KAAA,CAAA,EAwBQ,iBAxBS,CAwBS,WAxBT,CAwBqB,YAxBrB,CAAA,CAAA,MAAA,CAAA,CAAA,GAAA,SAAA;EACa,IAAA,CAAA,EAwBvB,OAxBuB,CAwBf,eAxBe,EAAA,SAAA,CAAA,GAAA,SAAA;CAAZ;;;;AAGpB,KAyEK,qBAzEyB,CAAA,UAAA,MAAA,CAAA,GA0E5B,CA1E4B,SA0ElB,KA1EkB,CAAA,KAAA,OAAA,CAAA,GA2ExB,UA3EwB,CA2Eb,MA3Ea,CAAA,GAAA,QACX,MA4EC,CA5ED,GA4EK,CA5EL,CA4EO,CA5EP,CAAA,SA4EkB,KA5ElB,GA4E0B,UA5E1B,CA4EqC,CA5ErC,CA4EuC,CA5EvC,CAAA,CAAA,GA4E6C,CA5E7C,CA4E+C,CA5E/C,CAAA,EAEI;AACC,KA4EZ,gBA5EY,CAAA,UAAA,MAAA,EAAA,cAAA,OAAA,EAAA,GAAA,EAAA,CAAA,GAAA,CAAA,GAAA,IAAA,EA6Eb,KA7Ea,EAAA,GA8EnB,qBA9EmB,CA8EG,CA9EH,CAAA;AAKpB,iBAiRY,SAjRZ,CAAA,UAiRgC,MAjRhC,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,SAAA,EAkRS,CAlRT,CAAA,EAAA,QACA,MAmRU,CAnRV,GAmRc,CAnRd,CAmRgB,CAnRhB,CAAA,SAmR2B,oBAnR3B,CAAA,KAAA,EAAA,KAAA,MAAA,EAAA,KAAA,cAAA,EAAA,KAAA,WAAA,EAAA,KAAA,iBAAA,CAAA,GA0RE,eA1RF,CAAA,KAAA,EA0RyB,KA1RzB,EA0RgC,aA1RhC,EA0R+C,UA1R/C,EA0R2D,gBA1R3D,CAAA,GA2RE,CA3RF,CA2RI,CA3RJ,CAAA,SA2Re,uBA3Rf,CAAA,KAAA,QAAA,EAAA,KAAA,MAAA,EAAA,KAAA,OAAA,EAAA,KAAA,QAAA,EAAA,KAAA,OAAA,EAAA,KAAA,iBAAA,CAAA,GAmSI,kBAnSJ,CAmSuB,OAnSvB,EAmSgC,KAnShC,EAmSuC,MAnSvC,EAmS+C,OAnS/C,EAmSwD,MAnSxD,EAmSgE,gBAnShE,CAAA,GAoSI,CApSJ,CAoSM,CApSN,CAAA,SAoSiB,qBApSjB,CAAA,KAAA,UAAA,CAAA,GAqSM,gBArSN,CAqSuB,SArSvB,EAAA,EAAA,CAAA,GAsSM,CAtSN,CAsSQ,CAtSR,CAAA,SAsSmB,sBAtSnB,CAAA,KAAA,UAAA,EAAA,KAAA,WAAA,CAAA,GAuSQ,gBAvSR,CAuSyB,SAvSzB,EAuSoC,UAvSpC,CAAA,GAwSQ,CAxSR,CAwSU,CAxSV,CAAA,EACA;KAiUC,SA/Tc,CAAA,CAAA,CAAA,GA+TC,CA/TD,SAAA;EAAR,MAAA,EAAA,CAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,EAAA,OAAA,EAAA,GAAA,OAAA;CACc,GA8TuD,CA9TvD,GAAA,KAAA;AAAd,UAgUM,eAhUN,CAAA,SAAA,CAAA,CAAA;EACsB;;;;;;EAGT,IAAA,CAAA,EAmUf,cAnUe;EAAR;;;EAEY,IAAA,CAAA,EAsUnB,SAtUmB,CAsUT,SAtUS,CAAA,EAAA;;AACX,iBAwUD,QAxUC,CAAA,kBAwU0B,KAxU1B,CAAA,CAAA,KAAA,EAyUR,SAzUQ,EAAA,OAAA,CAAA,EA0UN,eA1UM,CA0UU,SA1UV,CAAA,CAAA,EA2Ud,UA3Uc,CA2UH,SA3UG,CAAA;AAAR,iBAyWO,cAAA,CAzWP;EAAA;AACP,CADO,EAAA;EAAO,QAAA,EAyWyC,KAAA,CAAM,SAzW/C;AACd,CAAA,CAAA,EAwWsE,MAAA,CAAE,SArThD"}
|