@fragno-dev/core 0.1.7 → 0.1.9
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 +131 -64
- package/CHANGELOG.md +19 -0
- package/dist/api/api.d.ts +38 -2
- package/dist/api/api.d.ts.map +1 -0
- package/dist/api/api.js +9 -2
- package/dist/api/api.js.map +1 -0
- package/dist/api/bind-services.d.ts +6 -0
- package/dist/api/bind-services.d.ts.map +1 -0
- package/dist/api/bind-services.js +20 -0
- package/dist/api/bind-services.js.map +1 -0
- package/dist/api/error.d.ts +26 -0
- package/dist/api/error.d.ts.map +1 -0
- package/dist/{api-DngJDcmO.js → api/error.js} +2 -8
- package/dist/api/error.js.map +1 -0
- package/dist/api/fragment-definition-builder.d.ts +313 -0
- package/dist/api/fragment-definition-builder.d.ts.map +1 -0
- package/dist/api/fragment-definition-builder.js +326 -0
- package/dist/api/fragment-definition-builder.js.map +1 -0
- package/dist/api/fragment-instantiator.d.ts +216 -0
- package/dist/api/fragment-instantiator.d.ts.map +1 -0
- package/dist/api/fragment-instantiator.js +487 -0
- package/dist/api/fragment-instantiator.js.map +1 -0
- package/dist/api/fragno-response.d.ts +30 -0
- package/dist/api/fragno-response.d.ts.map +1 -0
- package/dist/api/fragno-response.js +73 -0
- package/dist/api/fragno-response.js.map +1 -0
- package/dist/api/internal/path.d.ts +50 -0
- package/dist/api/internal/path.d.ts.map +1 -0
- package/dist/api/internal/path.js +76 -0
- package/dist/api/internal/path.js.map +1 -0
- package/dist/api/internal/response-stream.d.ts +43 -0
- package/dist/api/internal/response-stream.d.ts.map +1 -0
- package/dist/api/internal/response-stream.js +81 -0
- package/dist/api/internal/response-stream.js.map +1 -0
- package/dist/api/internal/route.js +10 -0
- package/dist/api/internal/route.js.map +1 -0
- package/dist/api/mutable-request-state.d.ts +82 -0
- package/dist/api/mutable-request-state.d.ts.map +1 -0
- package/dist/api/mutable-request-state.js +97 -0
- package/dist/api/mutable-request-state.js.map +1 -0
- package/dist/api/request-context-storage.d.ts +42 -0
- package/dist/api/request-context-storage.d.ts.map +1 -0
- package/dist/api/request-context-storage.js +43 -0
- package/dist/api/request-context-storage.js.map +1 -0
- package/dist/api/request-input-context.d.ts +89 -0
- package/dist/api/request-input-context.d.ts.map +1 -0
- package/dist/api/request-input-context.js +118 -0
- package/dist/api/request-input-context.js.map +1 -0
- package/dist/api/request-middleware.d.ts +50 -0
- package/dist/api/request-middleware.d.ts.map +1 -0
- package/dist/api/request-middleware.js +83 -0
- package/dist/api/request-middleware.js.map +1 -0
- package/dist/api/request-output-context.d.ts +41 -0
- package/dist/api/request-output-context.d.ts.map +1 -0
- package/dist/api/request-output-context.js +119 -0
- package/dist/api/request-output-context.js.map +1 -0
- package/dist/api/route-handler-input-options.d.ts +21 -0
- package/dist/api/route-handler-input-options.d.ts.map +1 -0
- package/dist/api/route.d.ts +54 -3
- package/dist/api/route.d.ts.map +1 -0
- package/dist/api/route.js +29 -2
- package/dist/api/route.js.map +1 -0
- package/dist/api/shared-types.d.ts +47 -0
- package/dist/api/shared-types.d.ts.map +1 -0
- package/dist/api/shared-types.js +1 -0
- package/dist/client/client-error.d.ts +60 -0
- package/dist/client/client-error.d.ts.map +1 -0
- package/dist/client/client-error.js +92 -0
- package/dist/client/client-error.js.map +1 -0
- package/dist/client/client.d.ts +210 -4
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +397 -6
- package/dist/client/client.js.map +1 -0
- package/dist/client/client.svelte.d.ts +5 -3
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +1 -5
- package/dist/client/client.svelte.js.map +1 -1
- package/dist/client/internal/fetcher-merge.js +36 -0
- package/dist/client/internal/fetcher-merge.js.map +1 -0
- package/dist/client/internal/ndjson-streaming.js +139 -0
- package/dist/client/internal/ndjson-streaming.js.map +1 -0
- package/dist/client/react.d.ts +5 -3
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +3 -5
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +5 -3
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +2 -5
- package/dist/client/solid.js.map +1 -1
- package/dist/client/vanilla.d.ts +5 -3
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +2 -43
- package/dist/client/vanilla.js.map +1 -1
- package/dist/client/vue.d.ts +5 -3
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +1 -5
- package/dist/client/vue.js.map +1 -1
- package/dist/http/http-status.d.ts +26 -0
- package/dist/http/http-status.d.ts.map +1 -0
- package/dist/integrations/react-ssr.js +1 -1
- package/dist/internal/symbols.d.ts +9 -0
- package/dist/internal/symbols.d.ts.map +1 -0
- package/dist/internal/symbols.js +10 -0
- package/dist/internal/symbols.js.map +1 -0
- package/dist/mod-client.d.ts +36 -0
- package/dist/mod-client.d.ts.map +1 -0
- package/dist/mod-client.js +21 -0
- package/dist/mod-client.js.map +1 -0
- package/dist/mod.d.ts +7 -4
- package/dist/mod.js +4 -6
- package/dist/request/request.d.ts +4 -0
- package/dist/request/request.js +5 -0
- package/dist/test/test.d.ts +62 -35
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js +75 -40
- package/dist/test/test.js.map +1 -1
- package/dist/util/async.js +40 -0
- package/dist/util/async.js.map +1 -0
- package/dist/util/content-type.js +49 -0
- package/dist/util/content-type.js.map +1 -0
- package/dist/util/nanostores.js +31 -0
- package/dist/util/nanostores.js.map +1 -0
- package/dist/{ssr-BByDVfFD.js → util/ssr.js} +2 -2
- package/dist/util/ssr.js.map +1 -0
- package/dist/util/types-util.d.ts +8 -0
- package/dist/util/types-util.d.ts.map +1 -0
- package/package.json +19 -12
- package/src/api/api.ts +41 -6
- package/src/api/bind-services.ts +42 -0
- package/src/api/fragment-definition-builder.extend.test.ts +810 -0
- package/src/api/fragment-definition-builder.test.ts +499 -0
- package/src/api/fragment-definition-builder.ts +1088 -0
- package/src/api/fragment-instantiator.test.ts +1488 -0
- package/src/api/fragment-instantiator.ts +1053 -0
- package/src/api/fragment-services.test.ts +727 -0
- package/src/api/request-context-storage.ts +64 -0
- package/src/api/request-middleware.test.ts +301 -225
- package/src/api/route.test.ts +87 -1
- package/src/api/route.ts +345 -24
- package/src/api/shared-types.ts +43 -0
- package/src/client/client-builder.test.ts +23 -23
- package/src/client/client.ssr.test.ts +3 -3
- package/src/client/client.svelte.test.ts +15 -15
- package/src/client/client.test.ts +22 -22
- package/src/client/client.ts +72 -12
- package/src/client/internal/fetcher-merge.ts +1 -1
- package/src/client/react.test.ts +2 -2
- package/src/client/solid.test.ts +2 -2
- package/src/client/vanilla.test.ts +2 -2
- package/src/client/vue.test.ts +2 -2
- package/src/internal/symbols.ts +5 -0
- package/src/mod-client.ts +59 -0
- package/src/mod.ts +26 -9
- package/src/request/request.ts +8 -0
- package/src/test/test.test.ts +200 -381
- package/src/test/test.ts +190 -117
- package/tsdown.config.ts +8 -5
- package/dist/api/fragment-builder.d.ts +0 -4
- package/dist/api/fragment-builder.js +0 -3
- package/dist/api/fragment-instantiation.d.ts +0 -4
- package/dist/api/fragment-instantiation.js +0 -6
- package/dist/api-BWN97TOr.d.ts +0 -377
- package/dist/api-BWN97TOr.d.ts.map +0 -1
- package/dist/api-DngJDcmO.js.map +0 -1
- package/dist/client-C5LsYHEI.js +0 -782
- package/dist/client-C5LsYHEI.js.map +0 -1
- package/dist/fragment-builder-DOnCVBqc.js +0 -47
- package/dist/fragment-builder-DOnCVBqc.js.map +0 -1
- package/dist/fragment-builder-MGr68GNb.d.ts +0 -409
- package/dist/fragment-builder-MGr68GNb.d.ts.map +0 -1
- package/dist/fragment-instantiation-C4wvwl6V.js +0 -446
- package/dist/fragment-instantiation-C4wvwl6V.js.map +0 -1
- package/dist/request-output-context-CdIjwmEN.js +0 -320
- package/dist/request-output-context-CdIjwmEN.js.map +0 -1
- package/dist/route-Bl9Zr1Yv.d.ts +0 -26
- package/dist/route-Bl9Zr1Yv.d.ts.map +0 -1
- package/dist/route-C5Uryylh.js +0 -21
- package/dist/route-C5Uryylh.js.map +0 -1
- package/dist/ssr-BByDVfFD.js.map +0 -1
- package/src/api/fragment-builder.ts +0 -80
- package/src/api/fragment-instantiation.test.ts +0 -460
- package/src/api/fragment-instantiation.ts +0 -499
- package/src/api/fragment.test.ts +0 -537
package/dist/client/vue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.js","names":["ref","pathParams: Record<string, string | ReadableAtom<string>>","queryParams: Record<string, string | ReadableAtom<string>>","computed"],"sources":["../../src/client/vue.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { atom, type ReadableAtom, type Store, type StoreValue } from \"nanostores\";\nimport type { DeepReadonly, Ref, ShallowRef, UnwrapNestedRefs } from \"vue\";\nimport { computed, getCurrentScope, isRef, onScopeDispose, ref, shallowRef, watch } from \"vue\";\nimport type { NonGetHTTPMethod } from \"../api/api\";\nimport {\n isGetHook,\n isMutatorHook,\n type FragnoClientMutatorData,\n type FragnoClientHookData,\n} from \"./client\";\nimport type { FragnoClientError } from \"./client-error\";\nimport type { MaybeExtractPathParamsOrWiden, QueryParamsHint } from \"../api/internal/path\";\nimport type { InferOr } from \"../util/types-util\";\n\nexport type FragnoVueHook<\n _TMethod extends \"GET\",\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = (args?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string | Ref<string> | ReadableAtom<string>>;\n query?: QueryParamsHint<TQueryParameters, string | Ref<string> | ReadableAtom<string>>;\n}) => {\n data: Ref<InferOr<TOutputSchema, undefined>>;\n loading: Ref<boolean>;\n error: Ref<FragnoClientError<TErrorCode[number]> | undefined>;\n};\n\nexport type FragnoVueMutator<\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<TPath, string | Ref<string> | ReadableAtom<string>>;\n query?: QueryParamsHint<TQueryParameters, string | Ref<string> | ReadableAtom<string>>;\n }) => Promise<InferOr<TOutputSchema, undefined>>;\n loading: Ref<boolean | undefined>;\n error: Ref<FragnoClientError<TErrorCode[number]> | undefined>;\n data: Ref<InferOr<TOutputSchema, undefined>>;\n};\n\n/**\n * Converts a Vue Ref to a NanoStore Atom.\n *\n * This is used to convert Vue refs to atoms, so that we can use them in the store.\n *\n * @private\n */\nexport function refToAtom<T>(ref: Ref<T>): ReadableAtom<T> {\n const a = atom(ref.value);\n\n watch(ref, (newVal) => {\n a.set(newVal);\n });\n\n // TODO: Do we need to unsubscribe, or is this handled by `onScopeDispose` below?\n\n return a;\n}\n\n// Helper function to create a Vue composable from a GET hook\n// We want 1 store per hook, so on updates to params, we need to update the store instead of creating a new one.\n// Nanostores only works with atoms (or strings), so we need to convert vue refs to atoms.\nfunction createVueHook<\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): FragnoVueHook<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 | Ref<string> | ReadableAtom<string>;\n pathParams[key] = isRef(v) ? refToAtom(v) : v;\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n // Dunno why the cast is necessary\n const v = value as string | Ref<string> | ReadableAtom<string>;\n queryParams[key] = isRef(v) ? (refToAtom(v) as ReadableAtom<string>) : v;\n }\n\n const store = hook.store({\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n\n const data = ref();\n const loading = ref();\n const error = ref();\n\n const unsubscribe = store.subscribe((updatedStoreValue) => {\n data.value = updatedStoreValue.data;\n loading.value = updatedStoreValue.loading;\n error.value = updatedStoreValue.error;\n });\n\n if (getCurrentScope()) {\n onScopeDispose(() => {\n unsubscribe();\n });\n }\n\n return {\n data,\n loading,\n error,\n };\n };\n}\n\n// Helper function to create a Vue mutator from a mutator hook\nfunction createVueMutator<\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): FragnoVueMutator<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> {\n return () => {\n const store = useStore(hook.mutatorStore);\n\n // Create a wrapped mutate function that handles Vue refs\n const mutate = async (args: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<TPath, string | Ref<string> | ReadableAtom<string>>;\n query?: QueryParamsHint<TQueryParameters, string | Ref<string> | ReadableAtom<string>>;\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 | Ref<string> | ReadableAtom<string>;\n pathParams[key] = isRef(v) ? v.value : v;\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n const v = value as string | Ref<string> | ReadableAtom<string>;\n queryParams[key] = isRef(v) ? v.value : v;\n }\n\n // Call the store's mutate function with normalized params\n return store.value.mutate({\n body,\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n };\n\n // Return the store-like object with Vue reactive refs\n return {\n mutate,\n loading: computed(() => store.value.loading),\n error: computed(() => store.value.error),\n data: computed(() => store.value.data) as Ref<InferOr<TOutputSchema, undefined>>,\n };\n };\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 ? FragnoVueHook<\"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 ? FragnoVueMutator<M, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters>\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] = createVueHook(hook);\n } else if (isMutatorHook(hook)) {\n result[key] = createVueMutator(hook);\n } else {\n // Pass through non-hook values unchanged\n result[key] = hook;\n }\n }\n\n return result;\n}\n\nexport function useStore<SomeStore extends Store, Value extends StoreValue<SomeStore>>(\n store: SomeStore,\n): DeepReadonly<UnwrapNestedRefs<ShallowRef<Value>>> {\n const state = shallowRef();\n\n const unsubscribe = store.subscribe((value) => {\n state.value = value;\n });\n\n if (getCurrentScope()) {\n onScopeDispose(unsubscribe);\n }\n\n return state;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuDA,SAAgB,UAAa,OAA8B;CACzD,MAAM,IAAI,KAAKA,MAAI,MAAM;AAEzB,OAAMA,QAAM,WAAW;AACrB,IAAE,IAAI,OAAO;GACb;AAIF,QAAO;;AAMT,SAAS,cAOP,MAC4E;AAC5E,SAAQ,EAAE,MAAM,UAAU,EAAE,KAAK;EAC/B,MAAMC,aAA4D,EAAE;EACpE,MAAMC,cAA6D,EAAE;AAErE,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE,CAAC,EAAE;GACrD,MAAM,IAAI;AACV,cAAW,OAAO,MAAM,EAAE,GAAG,UAAU,EAAE,GAAG;;AAG9C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,EAAE;GAEtD,MAAM,IAAI;AACV,eAAY,OAAO,MAAM,EAAE,GAAI,UAAU,EAAE,GAA4B;;EAGzE,MAAM,QAAQ,KAAK,MAAM;GACvB,MAAM;GACN,OAAO;GACR,CAAC;EAEF,MAAM,OAAO,KAAK;EAClB,MAAM,UAAU,KAAK;EACrB,MAAM,QAAQ,KAAK;EAEnB,MAAM,cAAc,MAAM,WAAW,sBAAsB;AACzD,QAAK,QAAQ,kBAAkB;AAC/B,WAAQ,QAAQ,kBAAkB;AAClC,SAAM,QAAQ,kBAAkB;IAChC;AAEF,MAAI,iBAAiB,CACnB,sBAAqB;AACnB,gBAAa;IACb;AAGJ,SAAO;GACL;GACA;GACA;GACD;;;AAKL,SAAS,iBAQP,MAQ6F;AAC7F,cAAa;EACX,MAAM,QAAQ,SAAS,KAAK,aAAa;EAGzC,MAAM,SAAS,OAAO,SAIhB;GACJ,MAAM,EAAE,MAAM,MAAM,UAAU;GAE9B,MAAMD,aAA4D,EAAE;GACpE,MAAMC,cAA6D,EAAE;AAErE,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE,CAAC,EAAE;IACrD,MAAM,IAAI;AACV,eAAW,OAAO,MAAM,EAAE,GAAG,EAAE,QAAQ;;AAGzC,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,EAAE;IACtD,MAAM,IAAI;AACV,gBAAY,OAAO,MAAM,EAAE,GAAG,EAAE,QAAQ;;AAI1C,UAAO,MAAM,MAAM,OAAO;IACxB;IACA,MAAM;IACN,OAAO;IACR,CAAC;;AAIJ,SAAO;GACL;GACA,SAASC,iBAAe,MAAM,MAAM,QAAQ;GAC5C,OAAOA,iBAAe,MAAM,MAAM,MAAM;GACxC,MAAMA,iBAAe,MAAM,MAAM,KAAK;GACvC;;;AAIL,SAAgB,UACd,WAoBA;CAEA,MAAM,SAAS,EAAE;AAEjB,MAAK,MAAM,OAAO,WAAW;AAC3B,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,WAAW,IAAI,CACvD;EAGF,MAAM,OAAO,UAAU;AACvB,MAAI,UAAU,KAAK,CACjB,QAAO,OAAO,cAAc,KAAK;WACxB,cAAc,KAAK,CAC5B,QAAO,OAAO,iBAAiB,KAAK;MAGpC,QAAO,OAAO;;AAIlB,QAAO;;AAGT,SAAgB,SACd,OACmD;CACnD,MAAM,QAAQ,YAAY;CAE1B,MAAM,cAAc,MAAM,WAAW,UAAU;AAC7C,QAAM,QAAQ;GACd;AAEF,KAAI,iBAAiB,CACnB,gBAAe,YAAY;AAG7B,QAAO"}
|
|
1
|
+
{"version":3,"file":"vue.js","names":["ref","pathParams: Record<string, string | ReadableAtom<string>>","queryParams: Record<string, string | ReadableAtom<string>>","computed"],"sources":["../../src/client/vue.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { atom, type ReadableAtom, type Store, type StoreValue } from \"nanostores\";\nimport type { DeepReadonly, Ref, ShallowRef, UnwrapNestedRefs } from \"vue\";\nimport { computed, getCurrentScope, isRef, onScopeDispose, ref, shallowRef, watch } from \"vue\";\nimport type { NonGetHTTPMethod } from \"../api/api\";\nimport {\n isGetHook,\n isMutatorHook,\n type FragnoClientMutatorData,\n type FragnoClientHookData,\n} from \"./client\";\nimport type { FragnoClientError } from \"./client-error\";\nimport type { MaybeExtractPathParamsOrWiden, QueryParamsHint } from \"../api/internal/path\";\nimport type { InferOr } from \"../util/types-util\";\n\nexport type FragnoVueHook<\n _TMethod extends \"GET\",\n TPath extends string,\n TOutputSchema extends StandardSchemaV1,\n TErrorCode extends string,\n TQueryParameters extends string,\n> = (args?: {\n path?: MaybeExtractPathParamsOrWiden<TPath, string | Ref<string> | ReadableAtom<string>>;\n query?: QueryParamsHint<TQueryParameters, string | Ref<string> | ReadableAtom<string>>;\n}) => {\n data: Ref<InferOr<TOutputSchema, undefined>>;\n loading: Ref<boolean>;\n error: Ref<FragnoClientError<TErrorCode[number]> | undefined>;\n};\n\nexport type FragnoVueMutator<\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<TPath, string | Ref<string> | ReadableAtom<string>>;\n query?: QueryParamsHint<TQueryParameters, string | Ref<string> | ReadableAtom<string>>;\n }) => Promise<InferOr<TOutputSchema, undefined>>;\n loading: Ref<boolean | undefined>;\n error: Ref<FragnoClientError<TErrorCode[number]> | undefined>;\n data: Ref<InferOr<TOutputSchema, undefined>>;\n};\n\n/**\n * Converts a Vue Ref to a NanoStore Atom.\n *\n * This is used to convert Vue refs to atoms, so that we can use them in the store.\n *\n * @private\n */\nexport function refToAtom<T>(ref: Ref<T>): ReadableAtom<T> {\n const a = atom(ref.value);\n\n watch(ref, (newVal) => {\n a.set(newVal);\n });\n\n // TODO: Do we need to unsubscribe, or is this handled by `onScopeDispose` below?\n\n return a;\n}\n\n// Helper function to create a Vue composable from a GET hook\n// We want 1 store per hook, so on updates to params, we need to update the store instead of creating a new one.\n// Nanostores only works with atoms (or strings), so we need to convert vue refs to atoms.\nfunction createVueHook<\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): FragnoVueHook<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 | Ref<string> | ReadableAtom<string>;\n pathParams[key] = isRef(v) ? refToAtom(v) : v;\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n // Dunno why the cast is necessary\n const v = value as string | Ref<string> | ReadableAtom<string>;\n queryParams[key] = isRef(v) ? (refToAtom(v) as ReadableAtom<string>) : v;\n }\n\n const store = hook.store({\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n\n const data = ref();\n const loading = ref();\n const error = ref();\n\n const unsubscribe = store.subscribe((updatedStoreValue) => {\n data.value = updatedStoreValue.data;\n loading.value = updatedStoreValue.loading;\n error.value = updatedStoreValue.error;\n });\n\n if (getCurrentScope()) {\n onScopeDispose(() => {\n unsubscribe();\n });\n }\n\n return {\n data,\n loading,\n error,\n };\n };\n}\n\n// Helper function to create a Vue mutator from a mutator hook\nfunction createVueMutator<\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): FragnoVueMutator<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> {\n return () => {\n const store = useStore(hook.mutatorStore);\n\n // Create a wrapped mutate function that handles Vue refs\n const mutate = async (args: {\n body?: InferOr<TInputSchema, undefined>;\n path?: MaybeExtractPathParamsOrWiden<TPath, string | Ref<string> | ReadableAtom<string>>;\n query?: QueryParamsHint<TQueryParameters, string | Ref<string> | ReadableAtom<string>>;\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 | Ref<string> | ReadableAtom<string>;\n pathParams[key] = isRef(v) ? v.value : v;\n }\n\n for (const [key, value] of Object.entries(query ?? {})) {\n const v = value as string | Ref<string> | ReadableAtom<string>;\n queryParams[key] = isRef(v) ? v.value : v;\n }\n\n // Call the store's mutate function with normalized params\n return store.value.mutate({\n body,\n path: pathParams as MaybeExtractPathParamsOrWiden<TPath, string | ReadableAtom<string>>,\n query: queryParams,\n });\n };\n\n // Return the store-like object with Vue reactive refs\n return {\n mutate,\n loading: computed(() => store.value.loading),\n error: computed(() => store.value.error),\n data: computed(() => store.value.data) as Ref<InferOr<TOutputSchema, undefined>>,\n };\n };\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 ? FragnoVueHook<\"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 ? FragnoVueMutator<M, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters>\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] = createVueHook(hook);\n } else if (isMutatorHook(hook)) {\n result[key] = createVueMutator(hook);\n } else {\n // Pass through non-hook values unchanged\n result[key] = hook;\n }\n }\n\n return result;\n}\n\nexport function useStore<SomeStore extends Store, Value extends StoreValue<SomeStore>>(\n store: SomeStore,\n): DeepReadonly<UnwrapNestedRefs<ShallowRef<Value>>> {\n const state = shallowRef();\n\n const unsubscribe = store.subscribe((value) => {\n state.value = value;\n });\n\n if (getCurrentScope()) {\n onScopeDispose(unsubscribe);\n }\n\n return state;\n}\n"],"mappings":";;;;;;;;;;;;AAuDA,SAAgB,UAAa,OAA8B;CACzD,MAAM,IAAI,KAAKA,MAAI,MAAM;AAEzB,OAAMA,QAAM,WAAW;AACrB,IAAE,IAAI,OAAO;GACb;AAIF,QAAO;;AAMT,SAAS,cAOP,MAC4E;AAC5E,SAAQ,EAAE,MAAM,UAAU,EAAE,KAAK;EAC/B,MAAMC,aAA4D,EAAE;EACpE,MAAMC,cAA6D,EAAE;AAErE,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE,CAAC,EAAE;GACrD,MAAM,IAAI;AACV,cAAW,OAAO,MAAM,EAAE,GAAG,UAAU,EAAE,GAAG;;AAG9C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,EAAE;GAEtD,MAAM,IAAI;AACV,eAAY,OAAO,MAAM,EAAE,GAAI,UAAU,EAAE,GAA4B;;EAGzE,MAAM,QAAQ,KAAK,MAAM;GACvB,MAAM;GACN,OAAO;GACR,CAAC;EAEF,MAAM,OAAO,KAAK;EAClB,MAAM,UAAU,KAAK;EACrB,MAAM,QAAQ,KAAK;EAEnB,MAAM,cAAc,MAAM,WAAW,sBAAsB;AACzD,QAAK,QAAQ,kBAAkB;AAC/B,WAAQ,QAAQ,kBAAkB;AAClC,SAAM,QAAQ,kBAAkB;IAChC;AAEF,MAAI,iBAAiB,CACnB,sBAAqB;AACnB,gBAAa;IACb;AAGJ,SAAO;GACL;GACA;GACA;GACD;;;AAKL,SAAS,iBAQP,MAQ6F;AAC7F,cAAa;EACX,MAAM,QAAQ,SAAS,KAAK,aAAa;EAGzC,MAAM,SAAS,OAAO,SAIhB;GACJ,MAAM,EAAE,MAAM,MAAM,UAAU;GAE9B,MAAMD,aAA4D,EAAE;GACpE,MAAMC,cAA6D,EAAE;AAErE,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE,CAAC,EAAE;IACrD,MAAM,IAAI;AACV,eAAW,OAAO,MAAM,EAAE,GAAG,EAAE,QAAQ;;AAGzC,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,EAAE;IACtD,MAAM,IAAI;AACV,gBAAY,OAAO,MAAM,EAAE,GAAG,EAAE,QAAQ;;AAI1C,UAAO,MAAM,MAAM,OAAO;IACxB;IACA,MAAM;IACN,OAAO;IACR,CAAC;;AAIJ,SAAO;GACL;GACA,SAASC,iBAAe,MAAM,MAAM,QAAQ;GAC5C,OAAOA,iBAAe,MAAM,MAAM,MAAM;GACxC,MAAMA,iBAAe,MAAM,MAAM,KAAK;GACvC;;;AAIL,SAAgB,UACd,WAoBA;CAEA,MAAM,SAAS,EAAE;AAEjB,MAAK,MAAM,OAAO,WAAW;AAC3B,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,WAAW,IAAI,CACvD;EAGF,MAAM,OAAO,UAAU;AACvB,MAAI,UAAU,KAAK,CACjB,QAAO,OAAO,cAAc,KAAK;WACxB,cAAc,KAAK,CAC5B,QAAO,OAAO,iBAAiB,KAAK;MAGpC,QAAO,OAAO;;AAIlB,QAAO;;AAGT,SAAgB,SACd,OACmD;CACnD,MAAM,QAAQ,YAAY;CAE1B,MAAM,cAAc,MAAM,WAAW,UAAU;AAC7C,QAAM,QAAQ;GACd;AAEF,KAAI,iBAAiB,CACnB,gBAAe,YAAY;AAG7B,QAAO"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/http/http-status.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @module
|
|
4
|
+
* HTTP Status utility.
|
|
5
|
+
*
|
|
6
|
+
* Modified from honojs/hono
|
|
7
|
+
* Original source: https://github.com/honojs/hono/blob/0e3db674ad3f40be215a55a18062dd8e387ce525/src/utils/http-status.ts
|
|
8
|
+
* License: MIT
|
|
9
|
+
* Date obtained: August 28 2025
|
|
10
|
+
* Copyright (c) 2021-present Yusuke Wada and Hono contributors
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
type InfoStatusCode = 100 | 101 | 102 | 103;
|
|
14
|
+
type SuccessStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226;
|
|
15
|
+
type DeprecatedStatusCode = 305 | 306;
|
|
16
|
+
type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | DeprecatedStatusCode | 307 | 308;
|
|
17
|
+
type ClientErrorStatusCode = 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451;
|
|
18
|
+
type ServerErrorStatusCode = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
|
|
19
|
+
/**
|
|
20
|
+
* If you want to use an unofficial status, use `UnofficialStatusCode`.
|
|
21
|
+
*/
|
|
22
|
+
type StatusCode = InfoStatusCode | SuccessStatusCode | RedirectStatusCode | ClientErrorStatusCode | ServerErrorStatusCode;
|
|
23
|
+
type ContentlessStatusCode = 101 | 204 | 205 | 304;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { ContentlessStatusCode, StatusCode };
|
|
26
|
+
//# sourceMappingURL=http-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-status.d.ts","names":[],"sources":["../../src/http/http-status.ts"],"sourcesContent":[],"mappings":";;AAYA;AACA;AACA;AACA;AACA;AA8BA;AAKA;;;;AAII,KA3CQ,cAAA,GA2CR,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA;AACA,KA3CQ,iBAAA,GA2CR,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA;AAAqB,KA1Cb,oBAAA,GA0Ca,GAAA,GAAA,GAAA;AAEb,KA3CA,kBAAA,GA2CqB,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GA3C8B,oBA2C9B,GAAA,GAAA,GAAA,GAAA;KA1CrB,qBAAA;KA8BA,qBAAA;;;;KAKA,UAAA,GACR,iBACA,oBACA,qBACA,wBACA;KAEQ,qBAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/internal/symbols.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Not actually a symbol, since we might be dealing with multiple instances of this code.
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
declare const instantiatedFragmentFakeSymbol: "$fragno-instantiated-fragment";
|
|
7
|
+
//#endregion
|
|
8
|
+
export { instantiatedFragmentFakeSymbol };
|
|
9
|
+
//# sourceMappingURL=symbols.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.d.ts","names":[],"sources":["../../src/internal/symbols.ts"],"sourcesContent":[],"mappings":";;AAIA;;;cAAa"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/internal/symbols.ts
|
|
2
|
+
/**
|
|
3
|
+
* Not actually a symbol, since we might be dealing with multiple instances of this code.
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
const instantiatedFragmentFakeSymbol = "$fragno-instantiated-fragment";
|
|
7
|
+
|
|
8
|
+
//#endregion
|
|
9
|
+
export { instantiatedFragmentFakeSymbol };
|
|
10
|
+
//# sourceMappingURL=symbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.js","names":[],"sources":["../../src/internal/symbols.ts"],"sourcesContent":["/**\n * Not actually a symbol, since we might be dealing with multiple instances of this code.\n * @internal\n */\nexport const instantiatedFragmentFakeSymbol = \"$fragno-instantiated-fragment\" as const;\n"],"mappings":";;;;;AAIA,MAAa,iCAAiC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FragnoPublicConfig } from "./api/shared-types.js";
|
|
2
|
+
import { FragnoRouteConfig, RequestThisContext } from "./api/api.js";
|
|
3
|
+
import { BoundServices } from "./api/bind-services.js";
|
|
4
|
+
import { RouteFactory, RouteFactoryContext, defineRoute, defineRoutes } from "./api/route.js";
|
|
5
|
+
import { instantiatedFragmentFakeSymbol } from "./internal/symbols.js";
|
|
6
|
+
import { FragmentInstantiationBuilder, FragnoInstantiatedFragment } from "./api/fragment-instantiator.js";
|
|
7
|
+
import { FragmentDefinition, FragmentDefinitionBuilder, ServiceConstructorFn, ServiceContext } from "./api/fragment-definition-builder.js";
|
|
8
|
+
|
|
9
|
+
//#region src/mod-client.d.ts
|
|
10
|
+
declare function defineFragment(_name: string): {
|
|
11
|
+
withDependencies: () => {
|
|
12
|
+
withDependencies: () => {};
|
|
13
|
+
};
|
|
14
|
+
providesBaseService: () => {
|
|
15
|
+
providesBaseService: () => {};
|
|
16
|
+
};
|
|
17
|
+
providesService: () => {
|
|
18
|
+
providesService: () => {};
|
|
19
|
+
};
|
|
20
|
+
withRequestStorage: () => {
|
|
21
|
+
withRequestStorage: () => {};
|
|
22
|
+
};
|
|
23
|
+
withExternalRequestStorage: () => {
|
|
24
|
+
withExternalRequestStorage: () => {};
|
|
25
|
+
};
|
|
26
|
+
withRequestThisContext: () => {
|
|
27
|
+
withRequestThisContext: () => {};
|
|
28
|
+
};
|
|
29
|
+
extend: () => {
|
|
30
|
+
extend: () => {};
|
|
31
|
+
};
|
|
32
|
+
build: () => {};
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { type BoundServices, type FragmentDefinition, FragmentDefinitionBuilder, type FragmentInstantiationBuilder, type FragnoInstantiatedFragment, type FragnoPublicConfig, type FragnoRouteConfig, type RequestThisContext, type RouteFactory, type RouteFactoryContext, type ServiceConstructorFn, type ServiceContext, defineFragment, defineRoute, defineRoutes, instantiatedFragmentFakeSymbol };
|
|
36
|
+
//# sourceMappingURL=mod-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod-client.d.ts","names":[],"sources":["../src/mod-client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAyBgB,cAAA;;;;;;;EAAA,eAAA,EAAc,GAAA,GAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FragmentDefinitionBuilder } from "./api/fragment-definition-builder.js";
|
|
2
|
+
import { defineRoute, defineRoutes } from "./api/route.js";
|
|
3
|
+
import { instantiatedFragmentFakeSymbol } from "./internal/symbols.js";
|
|
4
|
+
|
|
5
|
+
//#region src/mod-client.ts
|
|
6
|
+
function defineFragment(_name) {
|
|
7
|
+
return {
|
|
8
|
+
withDependencies: () => ({ withDependencies: () => ({}) }),
|
|
9
|
+
providesBaseService: () => ({ providesBaseService: () => ({}) }),
|
|
10
|
+
providesService: () => ({ providesService: () => ({}) }),
|
|
11
|
+
withRequestStorage: () => ({ withRequestStorage: () => ({}) }),
|
|
12
|
+
withExternalRequestStorage: () => ({ withExternalRequestStorage: () => ({}) }),
|
|
13
|
+
withRequestThisContext: () => ({ withRequestThisContext: () => ({}) }),
|
|
14
|
+
extend: () => ({ extend: () => ({}) }),
|
|
15
|
+
build: () => ({})
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { FragmentDefinitionBuilder, defineFragment, defineRoute, defineRoutes, instantiatedFragmentFakeSymbol };
|
|
21
|
+
//# sourceMappingURL=mod-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod-client.js","names":[],"sources":["../src/mod-client.ts"],"sourcesContent":["// ============================================================================\n// Client-side entry point for @fragno-dev/core\n// This file mirrors mod.ts but provides stub implementations for server-side\n// APIs that are stripped by unplugin-fragno during browser builds.\n// ============================================================================\n\n// ============================================================================\n// Fragment Definition and Instantiation\n// ============================================================================\n\n// Re-export types only\nexport type {\n FragmentDefinition,\n ServiceContext,\n ServiceConstructorFn,\n} from \"./api/fragment-definition-builder\";\n\nexport type {\n FragmentInstantiationBuilder,\n FragnoInstantiatedFragment,\n BoundServices,\n} from \"./api/fragment-instantiator\";\n\n// Stub implementation for defineFragment\n// This is stripped by unplugin-fragno in browser builds\nexport function defineFragment(_name: string) {\n return {\n withDependencies: () => ({ withDependencies: () => ({}) }),\n providesBaseService: () => ({ providesBaseService: () => ({}) }),\n providesService: () => ({ providesService: () => ({}) }),\n withRequestStorage: () => ({ withRequestStorage: () => ({}) }),\n withExternalRequestStorage: () => ({ withExternalRequestStorage: () => ({}) }),\n withRequestThisContext: () => ({ withRequestThisContext: () => ({}) }),\n extend: () => ({ extend: () => ({}) }),\n build: () => ({}),\n };\n}\n\n// Re-export the builder class (for type compatibility)\nexport { FragmentDefinitionBuilder } from \"./api/fragment-definition-builder\";\n\n// ============================================================================\n// Core Configuration\n// ============================================================================\nexport type { FragnoPublicConfig } from \"./api/shared-types\";\n\n// ============================================================================\n// Route Definition\n// ============================================================================\nexport {\n defineRoute,\n defineRoutes,\n type RouteFactory,\n type RouteFactoryContext,\n} from \"./api/route\";\n\nexport type { FragnoRouteConfig, RequestThisContext } from \"./api/api\";\n\nexport { instantiatedFragmentFakeSymbol } from \"./internal/symbols\";\n"],"mappings":";;;;;AAyBA,SAAgB,eAAe,OAAe;AAC5C,QAAO;EACL,yBAAyB,EAAE,yBAAyB,EAAE,GAAG;EACzD,4BAA4B,EAAE,4BAA4B,EAAE,GAAG;EAC/D,wBAAwB,EAAE,wBAAwB,EAAE,GAAG;EACvD,2BAA2B,EAAE,2BAA2B,EAAE,GAAG;EAC7D,mCAAmC,EAAE,mCAAmC,EAAE,GAAG;EAC7E,+BAA+B,EAAE,+BAA+B,EAAE,GAAG;EACrE,eAAe,EAAE,eAAe,EAAE,GAAG;EACrC,cAAc,EAAE;EACjB"}
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { FragnoPublicConfig } from "./api/shared-types.js";
|
|
2
|
+
import { FragnoRouteConfig, RequestThisContext } from "./api/api.js";
|
|
3
|
+
import { BoundServices } from "./api/bind-services.js";
|
|
4
|
+
import { RouteFactory, RouteFactoryContext, defineRoute, defineRoutes } from "./api/route.js";
|
|
5
|
+
import { AnyFragnoInstantiatedFragment, FragmentInstantiationBuilder, FragnoInstantiatedFragment, instantiate } from "./api/fragment-instantiator.js";
|
|
6
|
+
import { ExtractLinkedServices, FragmentDefinition, FragmentDefinitionBuilder, LinkedFragmentCallback, ServiceConstructorFn, ServiceContext, defineFragment } from "./api/fragment-definition-builder.js";
|
|
7
|
+
export { type AnyFragnoInstantiatedFragment, type BoundServices, type ExtractLinkedServices, type FragmentDefinition, FragmentDefinitionBuilder, type FragmentInstantiationBuilder, type FragnoInstantiatedFragment, type FragnoPublicConfig, type FragnoRouteConfig, type LinkedFragmentCallback, type RequestThisContext, type RouteFactory, type RouteFactoryContext, type ServiceConstructorFn, type ServiceContext, defineFragment, defineRoute, defineRoutes, instantiate };
|
package/dist/mod.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "./api
|
|
3
|
-
import "./
|
|
4
|
-
import { n as defineRoutes, t as defineRoute } from "./route-C5Uryylh.js";
|
|
5
|
-
import { t as createFragment } from "./fragment-instantiation-C4wvwl6V.js";
|
|
1
|
+
import { FragmentDefinitionBuilder, defineFragment } from "./api/fragment-definition-builder.js";
|
|
2
|
+
import { defineRoute, defineRoutes } from "./api/route.js";
|
|
3
|
+
import { instantiate } from "./api/fragment-instantiator.js";
|
|
6
4
|
|
|
7
|
-
export {
|
|
5
|
+
export { FragmentDefinitionBuilder, defineFragment, defineRoute, defineRoutes, instantiate };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { RequestInputContext } from "../api/request-input-context.js";
|
|
2
|
+
import { RequestOutputContext } from "../api/request-output-context.js";
|
|
3
|
+
import { FragnoMiddlewareCallback, RequestMiddlewareInputContext, RequestMiddlewareOptions, RequestMiddlewareOutputContext } from "../api/request-middleware.js";
|
|
4
|
+
export { type FragnoMiddlewareCallback, RequestInputContext, RequestMiddlewareInputContext, type RequestMiddlewareOptions, RequestMiddlewareOutputContext, RequestOutputContext };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { RequestInputContext } from "../api/request-input-context.js";
|
|
2
|
+
import { RequestOutputContext } from "../api/request-output-context.js";
|
|
3
|
+
import { RequestMiddlewareInputContext, RequestMiddlewareOutputContext } from "../api/request-middleware.js";
|
|
4
|
+
|
|
5
|
+
export { RequestInputContext, RequestMiddlewareInputContext, RequestMiddlewareOutputContext, RequestOutputContext };
|
package/dist/test/test.d.ts
CHANGED
|
@@ -1,52 +1,82 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { RouteHandlerInputOptions } from "../api/route-handler-input-options.js";
|
|
2
|
+
import { FragnoPublicConfig } from "../api/shared-types.js";
|
|
3
|
+
import { RequestThisContext } from "../api/api.js";
|
|
4
|
+
import { BoundServices } from "../api/bind-services.js";
|
|
5
|
+
import { AnyRouteOrFactory, FlattenRouteFactories } from "../api/route.js";
|
|
6
|
+
import { FragnoResponse } from "../api/fragno-response.js";
|
|
7
|
+
import { FragnoInstantiatedFragment } from "../api/fragment-instantiator.js";
|
|
8
|
+
import { FragmentDefinition, FragmentDefinitionBuilder } from "../api/fragment-definition-builder.js";
|
|
5
9
|
|
|
6
10
|
//#region src/test/test.d.ts
|
|
7
|
-
|
|
11
|
+
type TestBaseServices<TDeps> = {
|
|
12
|
+
/**
|
|
13
|
+
* Access to fragment dependencies for testing purposes.
|
|
14
|
+
*/
|
|
15
|
+
deps: TDeps;
|
|
16
|
+
};
|
|
8
17
|
/**
|
|
9
|
-
*
|
|
18
|
+
* Extension function that adds test utilities to a fragment definition.
|
|
19
|
+
* This adds a `test` service to base services with access to deps.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const definition = defineFragment("my-fragment")
|
|
24
|
+
* .withDependencies(({ config }) => ({ client: createClient(config) }))
|
|
25
|
+
* .extend(withTestUtils())
|
|
26
|
+
* .build();
|
|
27
|
+
*
|
|
28
|
+
* const fragment = createFragmentForTest(definition, [], { config: {...} });
|
|
29
|
+
* expect(fragment.services.test.deps.client).toBeDefined();
|
|
30
|
+
* ```
|
|
10
31
|
*/
|
|
11
|
-
|
|
12
|
-
config: TConfig;
|
|
13
|
-
options?: Partial<TOptions>;
|
|
14
|
-
deps?: Partial<TDeps>;
|
|
15
|
-
services?: Partial<TServices>;
|
|
16
|
-
additionalContext?: Partial<TAdditionalContext>;
|
|
17
|
-
}
|
|
32
|
+
declare function withTestUtils(): <TConfig, TOptions extends FragnoPublicConfig, TDeps, TBaseServices, TServices, TServiceDeps, TPrivateServices, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage>(builder: FragmentDefinitionBuilder<TConfig, TOptions, TDeps, TBaseServices, TServices, TServiceDeps, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage>) => FragmentDefinitionBuilder<TConfig, TOptions, TDeps, TBaseServices & TestBaseServices<TDeps>, TServices, TServiceDeps, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage>;
|
|
18
33
|
/**
|
|
19
|
-
*
|
|
34
|
+
* Options for creating a test fragment with the new architecture
|
|
20
35
|
*/
|
|
21
|
-
interface
|
|
36
|
+
interface CreateFragmentForTestOptions<TConfig, TOptions extends FragnoPublicConfig, TServiceDependencies> {
|
|
22
37
|
config: TConfig;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
additionalContext: TAdditionalContext & TOptions;
|
|
26
|
-
callRoute: <const TMethod extends HTTPMethod, const TPath extends ExtractRoutePath<TRoutes, TMethod>>(method: TMethod, path: TPath, inputOptions?: RouteHandlerInputOptions<TPath, ExtractRouteByPath<TRoutes, TPath, TMethod>["inputSchema"]>) => Promise<FragnoResponse<InferOrUnknown<NonNullable<ExtractRouteByPath<TRoutes, TPath, TMethod>["outputSchema"]>>>>;
|
|
38
|
+
options?: Partial<TOptions>;
|
|
39
|
+
serviceImplementations?: TServiceDependencies;
|
|
27
40
|
}
|
|
28
41
|
/**
|
|
29
|
-
* Create a fragment instance for testing with optional dependency
|
|
42
|
+
* Create a fragment instance for testing with optional service dependency overrides.
|
|
43
|
+
* This uses the new fragment definition and instantiation architecture.
|
|
30
44
|
*
|
|
31
|
-
*
|
|
45
|
+
* **Important:** Use `.extend(withTestUtils())` before `.build()` to expose deps via `services.test.deps`.
|
|
46
|
+
*
|
|
47
|
+
* Returns an instantiated fragment with:
|
|
48
|
+
* - `services` - Services (base + named) from the fragment definition
|
|
49
|
+
* - `services.test.deps` - Dependencies (only if you used .extend(withTestUtils()))
|
|
50
|
+
* - `callRoute()` - Type-safe method to call routes directly
|
|
51
|
+
* - `handler()` - Request handler for integration
|
|
52
|
+
* - `inContext()` - Run code within request context
|
|
53
|
+
*
|
|
54
|
+
* @param definition - The fragment definition from builder.build()
|
|
32
55
|
* @param routesOrFactories - Route configurations or route factories
|
|
33
|
-
* @param options - Configuration and optional overrides
|
|
34
|
-
* @returns
|
|
56
|
+
* @param options - Configuration and optional overrides
|
|
57
|
+
* @returns An instantiated fragment ready for testing
|
|
35
58
|
*
|
|
36
59
|
* @example
|
|
37
60
|
* ```typescript
|
|
61
|
+
* const definition = defineFragment<{ apiKey: string }>("test")
|
|
62
|
+
* .withDependencies(({ config }) => ({ client: createClient(config.apiKey) }))
|
|
63
|
+
* .providesService("api", ({ deps }) => ({ call: () => deps.client.call() }))
|
|
64
|
+
* .extend(withTestUtils()) // <- Add test utilities
|
|
65
|
+
* .build();
|
|
66
|
+
*
|
|
38
67
|
* const fragment = createFragmentForTest(
|
|
39
|
-
*
|
|
40
|
-
* [
|
|
68
|
+
* definition,
|
|
69
|
+
* [route1, route2],
|
|
41
70
|
* {
|
|
42
|
-
* config: {
|
|
43
|
-
* options: { mountRoute: "/api/
|
|
44
|
-
* services: {
|
|
45
|
-
* generateStreamMessages: mockGenerator
|
|
46
|
-
* }
|
|
71
|
+
* config: { apiKey: "test-key" },
|
|
72
|
+
* options: { mountRoute: "/api/test" }
|
|
47
73
|
* }
|
|
48
74
|
* );
|
|
49
75
|
*
|
|
76
|
+
* // Access deps via test utilities
|
|
77
|
+
* expect(fragment.services.test.deps.client).toBeDefined();
|
|
78
|
+
* expect(fragment.services.api.call()).toBeDefined();
|
|
79
|
+
*
|
|
50
80
|
* // Call routes directly by method and path
|
|
51
81
|
* const response = await fragment.callRoute("POST", "/login", {
|
|
52
82
|
* body: { username: "test", password: "test123" }
|
|
@@ -57,10 +87,7 @@ interface FragmentForTest<TConfig, TDeps, TServices, TAdditionalContext extends
|
|
|
57
87
|
* }
|
|
58
88
|
* ```
|
|
59
89
|
*/
|
|
60
|
-
declare function createFragmentForTest<TConfig, TDeps, TServices extends Record<string, unknown>,
|
|
61
|
-
definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
|
|
62
|
-
$requiredOptions: TOptions;
|
|
63
|
-
}, routesOrFactories: TRoutesOrFactories, options: CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>): FragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions, FlattenRouteFactories<TRoutesOrFactories>>;
|
|
90
|
+
declare function createFragmentForTest<TConfig, TOptions extends FragnoPublicConfig, TDeps, TBaseServices extends Record<string, unknown>, TServices extends Record<string, unknown>, TServiceDependencies, TPrivateServices extends Record<string, unknown>, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, const TRoutesOrFactories extends readonly AnyRouteOrFactory[]>(definition: FragmentDefinition<TConfig, TOptions, TDeps, TBaseServices, TServices, TServiceDependencies, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage>, routesOrFactories: TRoutesOrFactories, options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>): FragnoInstantiatedFragment<FlattenRouteFactories<TRoutesOrFactories>, TDeps, BoundServices<TBaseServices & TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage>;
|
|
64
91
|
//#endregion
|
|
65
|
-
export { CreateFragmentForTestOptions,
|
|
92
|
+
export { CreateFragmentForTestOptions, type FragnoResponse, type RouteHandlerInputOptions, TestBaseServices, createFragmentForTest, withTestUtils };
|
|
66
93
|
//# sourceMappingURL=test.d.ts.map
|
package/dist/test/test.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","names":[],"sources":["../../src/test/test.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"test.d.ts","names":[],"sources":["../../src/test/test.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;KAeY;;AAAZ;AAsBA;EAGqB,IAAA,EArBb,KAqBa;CAMW;;;;;;;;;;;;;;;;AAoB5B,iBA7BY,aAAA,CAAA,CA6BZ,EAAA,CAAA,OAAA,EAAA,iBA1BiB,kBA0BjB,EAAA,KAAA,EAAA,aAAA,EAAA,SAAA,EAAA,YAAA,EAAA,gBAAA,EAAA,4BApB4B,kBAoB5B,EAAA,4BAnB4B,kBAmB5B,EAAA,eAAA,CAAA,CAAA,OAAA,EAhBS,yBAgBT,CAfE,OAeF,EAdE,QAcF,EAbE,KAaF,EAZE,aAYF,EAXE,SAWF,EAVE,YAUF,EATE,gBASF,EARE,mBAQF,EAPE,mBAOF,EANE,eAMF,CAAA,EAAA,GAJC,yBAID,CAHA,OAGA,EAFA,QAEA,EADA,KACA,EAAA,aAAA,GAAgB,gBAAhB,CAAiC,KAAjC,CAAA,EACA,SADA,EAEA,YAFA,EAGA,gBAHA,EAIA,mBAJA,EAKA,mBALA,EAMA,eANA,CAAA;;;;AAEA,UA+Da,4BA/Db,CAAA,OAAA,EAAA,iBAiEe,kBAjEf,EAAA,oBAAA,CAAA,CAAA;EACA,MAAA,EAmEM,OAnEN;EACA,OAAA,CAAA,EAmEQ,OAnER,CAmEgB,QAnEhB,CAAA;EACA,sBAAA,CAAA,EAmEuB,oBAnEvB;;;;AA4DJ;;;;;;;AA2DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,gDAEG,iDAEK,2CACJ,wEAEO,qDACG,gDACA,+EAEc,iCAE9B,mBACV,SACA,UACA,OACA,eACA,WACA,sBACA,kBACA,qBACA,qBACA,qCAEiB,6BACV,6BAA6B,SAAS,UAAU,wBACxD,2BACD,sBAAsB,qBACtB,OACA,cAAc,gBAAgB,YAC9B,qBACA,qBACA"}
|
package/dist/test/test.js
CHANGED
|
@@ -1,31 +1,84 @@
|
|
|
1
|
-
import "../api-
|
|
2
|
-
import "../
|
|
3
|
-
import "../route-C5Uryylh.js";
|
|
4
|
-
import { t as createFragment } from "../fragment-instantiation-C4wvwl6V.js";
|
|
1
|
+
import { FragmentDefinitionBuilder } from "../api/fragment-definition-builder.js";
|
|
2
|
+
import { instantiateFragment } from "../api/fragment-instantiator.js";
|
|
5
3
|
|
|
6
4
|
//#region src/test/test.ts
|
|
7
5
|
/**
|
|
8
|
-
*
|
|
6
|
+
* Extension function that adds test utilities to a fragment definition.
|
|
7
|
+
* This adds a `test` service to base services with access to deps.
|
|
9
8
|
*
|
|
10
|
-
* @
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const definition = defineFragment("my-fragment")
|
|
12
|
+
* .withDependencies(({ config }) => ({ client: createClient(config) }))
|
|
13
|
+
* .extend(withTestUtils())
|
|
14
|
+
* .build();
|
|
15
|
+
*
|
|
16
|
+
* const fragment = createFragmentForTest(definition, [], { config: {...} });
|
|
17
|
+
* expect(fragment.services.test.deps.client).toBeDefined();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function withTestUtils() {
|
|
21
|
+
return (builder) => {
|
|
22
|
+
const currentBaseDef = builder.build();
|
|
23
|
+
const currentBaseServices = currentBaseDef.baseServices;
|
|
24
|
+
const newBaseServices = (context) => {
|
|
25
|
+
const existingServices = currentBaseServices ? currentBaseServices(context) : {};
|
|
26
|
+
const testUtils = { deps: context.deps };
|
|
27
|
+
return {
|
|
28
|
+
...existingServices,
|
|
29
|
+
...testUtils
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
return new FragmentDefinitionBuilder(builder.name, {
|
|
33
|
+
dependencies: currentBaseDef.dependencies,
|
|
34
|
+
baseServices: newBaseServices,
|
|
35
|
+
namedServices: currentBaseDef.namedServices,
|
|
36
|
+
privateServices: currentBaseDef.privateServices,
|
|
37
|
+
serviceDependencies: currentBaseDef.serviceDependencies,
|
|
38
|
+
createRequestStorage: currentBaseDef.createRequestStorage,
|
|
39
|
+
createThisContext: currentBaseDef.createThisContext
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a fragment instance for testing with optional service dependency overrides.
|
|
45
|
+
* This uses the new fragment definition and instantiation architecture.
|
|
46
|
+
*
|
|
47
|
+
* **Important:** Use `.extend(withTestUtils())` before `.build()` to expose deps via `services.test.deps`.
|
|
48
|
+
*
|
|
49
|
+
* Returns an instantiated fragment with:
|
|
50
|
+
* - `services` - Services (base + named) from the fragment definition
|
|
51
|
+
* - `services.test.deps` - Dependencies (only if you used .extend(withTestUtils()))
|
|
52
|
+
* - `callRoute()` - Type-safe method to call routes directly
|
|
53
|
+
* - `handler()` - Request handler for integration
|
|
54
|
+
* - `inContext()` - Run code within request context
|
|
55
|
+
*
|
|
56
|
+
* @param definition - The fragment definition from builder.build()
|
|
11
57
|
* @param routesOrFactories - Route configurations or route factories
|
|
12
|
-
* @param options - Configuration and optional overrides
|
|
13
|
-
* @returns
|
|
58
|
+
* @param options - Configuration and optional overrides
|
|
59
|
+
* @returns An instantiated fragment ready for testing
|
|
14
60
|
*
|
|
15
61
|
* @example
|
|
16
62
|
* ```typescript
|
|
63
|
+
* const definition = defineFragment<{ apiKey: string }>("test")
|
|
64
|
+
* .withDependencies(({ config }) => ({ client: createClient(config.apiKey) }))
|
|
65
|
+
* .providesService("api", ({ deps }) => ({ call: () => deps.client.call() }))
|
|
66
|
+
* .extend(withTestUtils()) // <- Add test utilities
|
|
67
|
+
* .build();
|
|
68
|
+
*
|
|
17
69
|
* const fragment = createFragmentForTest(
|
|
18
|
-
*
|
|
19
|
-
* [
|
|
70
|
+
* definition,
|
|
71
|
+
* [route1, route2],
|
|
20
72
|
* {
|
|
21
|
-
* config: {
|
|
22
|
-
* options: { mountRoute: "/api/
|
|
23
|
-
* services: {
|
|
24
|
-
* generateStreamMessages: mockGenerator
|
|
25
|
-
* }
|
|
73
|
+
* config: { apiKey: "test-key" },
|
|
74
|
+
* options: { mountRoute: "/api/test" }
|
|
26
75
|
* }
|
|
27
76
|
* );
|
|
28
77
|
*
|
|
78
|
+
* // Access deps via test utilities
|
|
79
|
+
* expect(fragment.services.test.deps.client).toBeDefined();
|
|
80
|
+
* expect(fragment.services.api.call()).toBeDefined();
|
|
81
|
+
*
|
|
29
82
|
* // Call routes directly by method and path
|
|
30
83
|
* const response = await fragment.callRoute("POST", "/login", {
|
|
31
84
|
* body: { username: "test", password: "test123" }
|
|
@@ -36,32 +89,14 @@ import { t as createFragment } from "../fragment-instantiation-C4wvwl6V.js";
|
|
|
36
89
|
* }
|
|
37
90
|
* ```
|
|
38
91
|
*/
|
|
39
|
-
function createFragmentForTest(
|
|
40
|
-
const { config, options: fragmentOptions = {},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
const services = {
|
|
47
|
-
...definition.services ? definition.services(config, fragmentOptions, deps) : {},
|
|
48
|
-
...servicesOverride
|
|
49
|
-
};
|
|
50
|
-
const additionalContext = {
|
|
51
|
-
...definition.additionalContext,
|
|
52
|
-
...fragmentOptions,
|
|
53
|
-
...additionalContextOverride
|
|
54
|
-
};
|
|
55
|
-
const fragment = createFragment(fragmentBuilder, config, routesOrFactories, fragmentOptions);
|
|
56
|
-
return {
|
|
57
|
-
config,
|
|
58
|
-
deps,
|
|
59
|
-
services,
|
|
60
|
-
additionalContext,
|
|
61
|
-
callRoute: (method, path, inputOptions) => fragment.callRoute(method, path, inputOptions)
|
|
62
|
-
};
|
|
92
|
+
function createFragmentForTest(definition, routesOrFactories, options) {
|
|
93
|
+
const { config, options: fragmentOptions = {}, serviceImplementations } = options;
|
|
94
|
+
return instantiateFragment(definition, config, routesOrFactories, {
|
|
95
|
+
mountRoute: "/api/test",
|
|
96
|
+
...fragmentOptions
|
|
97
|
+
}, serviceImplementations);
|
|
63
98
|
}
|
|
64
99
|
|
|
65
100
|
//#endregion
|
|
66
|
-
export { createFragmentForTest };
|
|
101
|
+
export { createFragmentForTest, withTestUtils };
|
|
67
102
|
//# sourceMappingURL=test.js.map
|
package/dist/test/test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.js","names":[],"sources":["../../src/test/test.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"test.js","names":["newBaseServices: ServiceConstructorFn<\n TConfig,\n TOptions,\n TDeps,\n TServiceDeps,\n TPrivateServices,\n TBaseServices & TestBaseServices<TDeps>,\n TServiceThisContext\n >","testUtils: TestBaseServices<TDeps>"],"sources":["../../src/test/test.ts"],"sourcesContent":["import type { RequestThisContext } from \"../api/api\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"../api/route\";\nimport type { FragnoPublicConfig } from \"../api/shared-types\";\nimport {\n FragmentDefinitionBuilder,\n type FragmentDefinition,\n type ServiceConstructorFn,\n} from \"../api/fragment-definition-builder\";\nimport { instantiateFragment, type FragnoInstantiatedFragment } from \"../api/fragment-instantiator\";\nimport type { BoundServices } from \"../api/bind-services\";\n\n// Re-export for convenience\nexport type { RouteHandlerInputOptions } from \"../api/route-handler-input-options\";\nexport type { FragnoResponse } from \"../api/fragno-response\";\n\nexport type TestBaseServices<TDeps> = {\n /**\n * Access to fragment dependencies for testing purposes.\n */\n deps: TDeps;\n};\n\n/**\n * Extension function that adds test utilities to a fragment definition.\n * This adds a `test` service to base services with access to deps.\n *\n * @example\n * ```typescript\n * const definition = defineFragment(\"my-fragment\")\n * .withDependencies(({ config }) => ({ client: createClient(config) }))\n * .extend(withTestUtils())\n * .build();\n *\n * const fragment = createFragmentForTest(definition, [], { config: {...} });\n * expect(fragment.services.test.deps.client).toBeDefined();\n * ```\n */\nexport function withTestUtils() {\n return <\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n >(\n builder: FragmentDefinitionBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n >,\n ): FragmentDefinitionBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices & TestBaseServices<TDeps>,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n > => {\n // Get the current base services factory\n const currentBaseDef = builder.build();\n const currentBaseServices = currentBaseDef.baseServices;\n\n // Create new base services factory that merges test utilities\n const newBaseServices: ServiceConstructorFn<\n TConfig,\n TOptions,\n TDeps,\n TServiceDeps,\n TPrivateServices,\n TBaseServices & TestBaseServices<TDeps>,\n TServiceThisContext\n > = (context) => {\n // Call existing base services if they exist\n const existingServices = currentBaseServices\n ? currentBaseServices(context)\n : ({} as TBaseServices);\n\n // Add test utilities\n const testUtils: TestBaseServices<TDeps> = {\n deps: context.deps,\n };\n\n return {\n ...existingServices,\n ...testUtils,\n } as TBaseServices & TestBaseServices<TDeps>;\n };\n\n // Create new builder with updated base services\n return new FragmentDefinitionBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices & TestBaseServices<TDeps>,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n >(builder.name, {\n dependencies: currentBaseDef.dependencies,\n baseServices: newBaseServices,\n namedServices: currentBaseDef.namedServices,\n privateServices: currentBaseDef.privateServices,\n serviceDependencies: currentBaseDef.serviceDependencies,\n createRequestStorage: currentBaseDef.createRequestStorage,\n createThisContext: currentBaseDef.createThisContext,\n });\n };\n}\n\n/**\n * Options for creating a test fragment with the new architecture\n */\nexport interface CreateFragmentForTestOptions<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TServiceDependencies,\n> {\n config: TConfig;\n options?: Partial<TOptions>;\n serviceImplementations?: TServiceDependencies;\n}\n\n/**\n * Create a fragment instance for testing with optional service dependency overrides.\n * This uses the new fragment definition and instantiation architecture.\n *\n * **Important:** Use `.extend(withTestUtils())` before `.build()` to expose deps via `services.test.deps`.\n *\n * Returns an instantiated fragment with:\n * - `services` - Services (base + named) from the fragment definition\n * - `services.test.deps` - Dependencies (only if you used .extend(withTestUtils()))\n * - `callRoute()` - Type-safe method to call routes directly\n * - `handler()` - Request handler for integration\n * - `inContext()` - Run code within request context\n *\n * @param definition - The fragment definition from builder.build()\n * @param routesOrFactories - Route configurations or route factories\n * @param options - Configuration and optional overrides\n * @returns An instantiated fragment ready for testing\n *\n * @example\n * ```typescript\n * const definition = defineFragment<{ apiKey: string }>(\"test\")\n * .withDependencies(({ config }) => ({ client: createClient(config.apiKey) }))\n * .providesService(\"api\", ({ deps }) => ({ call: () => deps.client.call() }))\n * .extend(withTestUtils()) // <- Add test utilities\n * .build();\n *\n * const fragment = createFragmentForTest(\n * definition,\n * [route1, route2],\n * {\n * config: { apiKey: \"test-key\" },\n * options: { mountRoute: \"/api/test\" }\n * }\n * );\n *\n * // Access deps via test utilities\n * expect(fragment.services.test.deps.client).toBeDefined();\n * expect(fragment.services.api.call()).toBeDefined();\n *\n * // Call routes directly by method and path\n * const response = await fragment.callRoute(\"POST\", \"/login\", {\n * body: { username: \"test\", password: \"test123\" }\n * });\n *\n * if (response.type === 'json') {\n * expect(response.data).toEqual({...});\n * }\n * ```\n */\nexport function createFragmentForTest<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\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 >,\n routesOrFactories: TRoutesOrFactories,\n options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>,\n): FragnoInstantiatedFragment<\n FlattenRouteFactories<TRoutesOrFactories>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n> {\n const { config, options: fragmentOptions = {} as TOptions, serviceImplementations } = options;\n\n // Use default mountRoute for testing if not provided\n const fullOptions = {\n mountRoute: \"/api/test\",\n ...fragmentOptions,\n } as TOptions;\n\n // Instantiate and return the fragment directly\n return instantiateFragment(\n definition,\n config,\n routesOrFactories,\n fullOptions,\n serviceImplementations,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,gBAAgB;AAC9B,SAYE,YAuBG;EAEH,MAAM,iBAAiB,QAAQ,OAAO;EACtC,MAAM,sBAAsB,eAAe;EAG3C,MAAMA,mBAQD,YAAY;GAEf,MAAM,mBAAmB,sBACrB,oBAAoB,QAAQ,GAC3B,EAAE;GAGP,MAAMC,YAAqC,EACzC,MAAM,QAAQ,MACf;AAED,UAAO;IACL,GAAG;IACH,GAAG;IACJ;;AAIH,SAAO,IAAI,0BAWT,QAAQ,MAAM;GACd,cAAc,eAAe;GAC7B,cAAc;GACd,eAAe,eAAe;GAC9B,iBAAiB,eAAe;GAChC,qBAAqB,eAAe;GACpC,sBAAsB,eAAe;GACrC,mBAAmB,eAAe;GACnC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEN,SAAgB,sBAad,YAYA,mBACA,SAQA;CACA,MAAM,EAAE,QAAQ,SAAS,kBAAkB,EAAE,EAAc,2BAA2B;AAStF,QAAO,oBACL,YACA,QACA,mBATkB;EAClB,YAAY;EACZ,GAAG;EACJ,EAQC,uBACD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/util/async.ts
|
|
2
|
+
/**
|
|
3
|
+
* Creates an async iterator from a subscribe function that follows the observable pattern.
|
|
4
|
+
*
|
|
5
|
+
* @template T The type of values produced by the store.
|
|
6
|
+
* @param subscribe A function that subscribes to store updates. It receives a callback to be
|
|
7
|
+
* called on each update, and returns an unsubscribe function.
|
|
8
|
+
* @returns An async generator that yields store values as they are produced.
|
|
9
|
+
*/
|
|
10
|
+
function createAsyncIteratorFromCallback(subscribe) {
|
|
11
|
+
const queue = [];
|
|
12
|
+
let unsubscribe = null;
|
|
13
|
+
let resolveNext = null;
|
|
14
|
+
const unsubscribeFunc = subscribe((value) => {
|
|
15
|
+
if (resolveNext) {
|
|
16
|
+
resolveNext({
|
|
17
|
+
value,
|
|
18
|
+
done: false
|
|
19
|
+
});
|
|
20
|
+
resolveNext = null;
|
|
21
|
+
} else queue.push(value);
|
|
22
|
+
});
|
|
23
|
+
if (typeof unsubscribeFunc === "function") unsubscribe = unsubscribeFunc;
|
|
24
|
+
return (async function* () {
|
|
25
|
+
try {
|
|
26
|
+
while (true) if (queue.length > 0) yield queue.shift();
|
|
27
|
+
else yield await new Promise((resolve) => {
|
|
28
|
+
resolveNext = (result) => {
|
|
29
|
+
if (!result.done) resolve(result.value);
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
} finally {
|
|
33
|
+
if (unsubscribe) unsubscribe();
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { createAsyncIteratorFromCallback };
|
|
40
|
+
//# sourceMappingURL=async.js.map
|