@fragno-dev/core 0.1.10 → 0.2.0

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.
Files changed (69) hide show
  1. package/.turbo/turbo-build.log +139 -131
  2. package/CHANGELOG.md +63 -0
  3. package/dist/api/api.d.ts +23 -5
  4. package/dist/api/api.d.ts.map +1 -1
  5. package/dist/api/api.js.map +1 -1
  6. package/dist/api/fragment-definition-builder.d.ts +17 -7
  7. package/dist/api/fragment-definition-builder.d.ts.map +1 -1
  8. package/dist/api/fragment-definition-builder.js +3 -2
  9. package/dist/api/fragment-definition-builder.js.map +1 -1
  10. package/dist/api/fragment-instantiator.d.ts +129 -32
  11. package/dist/api/fragment-instantiator.d.ts.map +1 -1
  12. package/dist/api/fragment-instantiator.js +232 -50
  13. package/dist/api/fragment-instantiator.js.map +1 -1
  14. package/dist/api/request-input-context.d.ts +57 -1
  15. package/dist/api/request-input-context.d.ts.map +1 -1
  16. package/dist/api/request-input-context.js +67 -0
  17. package/dist/api/request-input-context.js.map +1 -1
  18. package/dist/api/request-middleware.d.ts +1 -1
  19. package/dist/api/request-middleware.d.ts.map +1 -1
  20. package/dist/api/request-middleware.js.map +1 -1
  21. package/dist/api/route.d.ts +7 -7
  22. package/dist/api/route.d.ts.map +1 -1
  23. package/dist/api/route.js.map +1 -1
  24. package/dist/client/client.d.ts +4 -3
  25. package/dist/client/client.d.ts.map +1 -1
  26. package/dist/client/client.js +103 -7
  27. package/dist/client/client.js.map +1 -1
  28. package/dist/client/vue.d.ts +7 -3
  29. package/dist/client/vue.d.ts.map +1 -1
  30. package/dist/client/vue.js +16 -1
  31. package/dist/client/vue.js.map +1 -1
  32. package/dist/internal/trace-context.d.ts +23 -0
  33. package/dist/internal/trace-context.d.ts.map +1 -0
  34. package/dist/internal/trace-context.js +14 -0
  35. package/dist/internal/trace-context.js.map +1 -0
  36. package/dist/mod-client.d.ts +5 -27
  37. package/dist/mod-client.d.ts.map +1 -1
  38. package/dist/mod-client.js +50 -13
  39. package/dist/mod-client.js.map +1 -1
  40. package/dist/mod.d.ts +4 -3
  41. package/dist/mod.js +2 -1
  42. package/dist/runtime.d.ts +15 -0
  43. package/dist/runtime.d.ts.map +1 -0
  44. package/dist/runtime.js +33 -0
  45. package/dist/runtime.js.map +1 -0
  46. package/dist/test/test.d.ts +2 -2
  47. package/dist/test/test.d.ts.map +1 -1
  48. package/dist/test/test.js.map +1 -1
  49. package/package.json +31 -18
  50. package/src/api/api.ts +24 -0
  51. package/src/api/fragment-definition-builder.ts +36 -17
  52. package/src/api/fragment-instantiator.test.ts +429 -1
  53. package/src/api/fragment-instantiator.ts +572 -58
  54. package/src/api/internal/path-runtime.test.ts +7 -0
  55. package/src/api/request-input-context.test.ts +152 -0
  56. package/src/api/request-input-context.ts +85 -0
  57. package/src/api/request-middleware.test.ts +47 -1
  58. package/src/api/request-middleware.ts +1 -1
  59. package/src/api/route.ts +7 -2
  60. package/src/client/client.test.ts +195 -0
  61. package/src/client/client.ts +185 -10
  62. package/src/client/vue.test.ts +253 -3
  63. package/src/client/vue.ts +44 -1
  64. package/src/internal/trace-context.ts +35 -0
  65. package/src/mod-client.ts +89 -9
  66. package/src/mod.ts +7 -1
  67. package/src/runtime.ts +48 -0
  68. package/src/test/test.ts +13 -4
  69. package/tsdown.config.ts +1 -0
@@ -1,4 +1,5 @@
1
- import { isGetHook, isMutatorHook } from "./client.js";
1
+ import { isReadableAtom } from "../util/nanostores.js";
2
+ import { isGetHook, isMutatorHook, isStore } from "./client.js";
2
3
  import { atom } from "nanostores";
3
4
  import { computed as computed$1, getCurrentScope, isRef, onScopeDispose, ref, shallowRef, watch } from "vue";
4
5
 
@@ -80,6 +81,19 @@ function createVueMutator(hook) {
80
81
  };
81
82
  };
82
83
  }
84
+ function createVueStore(hook) {
85
+ if (isReadableAtom(hook.obj)) return (() => useStore(hook.obj).value);
86
+ return (() => {
87
+ const result = {};
88
+ for (const key in hook.obj) {
89
+ if (!Object.prototype.hasOwnProperty.call(hook.obj, key)) continue;
90
+ const value = hook.obj[key];
91
+ if (isReadableAtom(value)) result[key] = useStore(value).value;
92
+ else result[key] = value;
93
+ }
94
+ return result;
95
+ });
96
+ }
83
97
  function useFragno(clientObj) {
84
98
  const result = {};
85
99
  for (const key in clientObj) {
@@ -87,6 +101,7 @@ function useFragno(clientObj) {
87
101
  const hook = clientObj[key];
88
102
  if (isGetHook(hook)) result[key] = createVueHook(hook);
89
103
  else if (isMutatorHook(hook)) result[key] = createVueMutator(hook);
104
+ else if (isStore(hook)) result[key] = createVueStore(hook);
90
105
  else result[key] = hook;
91
106
  }
92
107
  return result;
@@ -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","result: any"],"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 isStore,\n type FragnoClientMutatorData,\n type FragnoClientHookData,\n type FragnoStoreData,\n} from \"./client\";\nimport { isReadableAtom } from \"../util/nanostores\";\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 * Type helper that unwraps any Store fields of the object into StoreValues\n */\nexport type FragnoVueStore<T extends object> = () => T extends Store<infer TStore>\n ? StoreValue<TStore>\n : {\n [K in keyof T]: T[K] extends Store ? StoreValue<T[K]> : T[K];\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\n// Helper function to create a Vue composable from a store\nfunction createVueStore<const T extends object>(hook: FragnoStoreData<T>): FragnoVueStore<T> {\n if (isReadableAtom(hook.obj)) {\n return (() => useStore(hook.obj as Store).value) as FragnoVueStore<T>;\n }\n\n return (() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result: any = {};\n\n for (const key in hook.obj) {\n if (!Object.prototype.hasOwnProperty.call(hook.obj, key)) {\n continue;\n }\n\n const value = hook.obj[key];\n if (isReadableAtom(value)) {\n result[key] = useStore(value).value;\n } else {\n result[key] = value;\n }\n }\n\n return result;\n }) as FragnoVueStore<T>;\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] extends FragnoStoreData<infer TStoreObj>\n ? FragnoVueStore<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] = createVueHook(hook);\n } else if (isMutatorHook(hook)) {\n result[key] = createVueMutator(hook);\n } else if (isStore(hook)) {\n result[key] = createVueStore(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":";;;;;;;;;;;;;AAmEA,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;;;AAKL,SAAS,eAAuC,MAA6C;AAC3F,KAAI,eAAe,KAAK,IAAI,CAC1B,eAAc,SAAS,KAAK,IAAa,CAAC;AAG5C,eAAc;EAEZ,MAAMC,SAAc,EAAE;AAEtB,OAAK,MAAM,OAAO,KAAK,KAAK;AAC1B,OAAI,CAAC,OAAO,UAAU,eAAe,KAAK,KAAK,KAAK,IAAI,CACtD;GAGF,MAAM,QAAQ,KAAK,IAAI;AACvB,OAAI,eAAe,MAAM,CACvB,QAAO,OAAO,SAAS,MAAM,CAAC;OAE9B,QAAO,OAAO;;AAIlB,SAAO;;;AAIX,SAAgB,UACd,WAsBA;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;WAC3B,QAAQ,KAAK,CACtB,QAAO,OAAO,eAAe,KAAK;MAGlC,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,23 @@
1
+ //#region src/internal/trace-context.d.ts
2
+ type FragnoCoreTraceEvent = {
3
+ type: "route-input";
4
+ method: string;
5
+ path: string;
6
+ pathParams: Record<string, string>;
7
+ queryParams: [string, string][];
8
+ headers: [string, string][];
9
+ body: unknown;
10
+ } | {
11
+ type: "middleware-decision";
12
+ method: string;
13
+ path: string;
14
+ outcome: "allow" | "deny";
15
+ status?: number;
16
+ };
17
+ type FragnoTraceRecorder = (event: FragnoCoreTraceEvent) => void;
18
+ declare const runWithTraceRecorder: <T>(recorder: FragnoTraceRecorder, callback: () => T) => T;
19
+ declare const getTraceRecorder: () => FragnoTraceRecorder | undefined;
20
+ declare const recordTraceEvent: (event: FragnoCoreTraceEvent) => void;
21
+ //#endregion
22
+ export { FragnoCoreTraceEvent, FragnoTraceRecorder, getTraceRecorder, recordTraceEvent, runWithTraceRecorder };
23
+ //# sourceMappingURL=trace-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.d.ts","names":[],"sources":["../../src/internal/trace-context.ts"],"sourcesContent":[],"mappings":";KAEY,oBAAA;EAAA,IAAA,EAAA,aAAA;EAkBA,MAAA,EAAA,MAAA;EAIC,IAAA,EAAA,MAAA;EAAqC,UAAA,EAjBhC,MAiBgC,CAAA,MAAA,EAAA,MAAA,CAAA;EAAqC,WAAA,EAAA,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA;EAAI,OAAA,EAAA,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA;EACrD,IAAA,EAAA,OAAA;AAEtC,CAAA,GAAa;EAEA,IAAA,EAAA,qBAKZ;;;;;;KAdW,mBAAA,WAA8B;cAI7B,oCAAqC,qCAAqC,MAAI;cAG9E,wBAAuB;cAEvB,0BAA2B"}
@@ -0,0 +1,14 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ //#region src/internal/trace-context.ts
4
+ const traceStorage = new AsyncLocalStorage();
5
+ const runWithTraceRecorder = (recorder, callback) => traceStorage.run(recorder, callback);
6
+ const getTraceRecorder = () => traceStorage.getStore();
7
+ const recordTraceEvent = (event) => {
8
+ const recorder = traceStorage.getStore();
9
+ if (recorder) recorder(event);
10
+ };
11
+
12
+ //#endregion
13
+ export { getTraceRecorder, recordTraceEvent, runWithTraceRecorder };
14
+ //# sourceMappingURL=trace-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.js","names":[],"sources":["../../src/internal/trace-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport type FragnoCoreTraceEvent =\n | {\n type: \"route-input\";\n method: string;\n path: string;\n pathParams: Record<string, string>;\n queryParams: [string, string][];\n headers: [string, string][];\n body: unknown;\n }\n | {\n type: \"middleware-decision\";\n method: string;\n path: string;\n outcome: \"allow\" | \"deny\";\n status?: number;\n };\n\nexport type FragnoTraceRecorder = (event: FragnoCoreTraceEvent) => void;\n\nconst traceStorage = new AsyncLocalStorage<FragnoTraceRecorder>();\n\nexport const runWithTraceRecorder = <T>(recorder: FragnoTraceRecorder, callback: () => T): T =>\n traceStorage.run(recorder, callback);\n\nexport const getTraceRecorder = (): FragnoTraceRecorder | undefined => traceStorage.getStore();\n\nexport const recordTraceEvent = (event: FragnoCoreTraceEvent): void => {\n const recorder = traceStorage.getStore();\n if (recorder) {\n recorder(event);\n }\n};\n"],"mappings":";;;AAsBA,MAAM,eAAe,IAAI,mBAAwC;AAEjE,MAAa,wBAA2B,UAA+B,aACrE,aAAa,IAAI,UAAU,SAAS;AAEtC,MAAa,yBAA0D,aAAa,UAAU;AAE9F,MAAa,oBAAoB,UAAsC;CACrE,MAAM,WAAW,aAAa,UAAU;AACxC,KAAI,SACF,UAAS,MAAM"}
@@ -3,35 +3,13 @@ import { FragnoRouteConfig, RequestThisContext } from "./api/api.js";
3
3
  import { BoundServices } from "./api/bind-services.js";
4
4
  import { RouteFactory, RouteFactoryContext, defineRoute, defineRoutes } from "./api/route.js";
5
5
  import { instantiatedFragmentFakeSymbol } from "./internal/symbols.js";
6
- import { FragmentInstantiationBuilder, FragnoInstantiatedFragment } from "./api/fragment-instantiator.js";
6
+ import { FragmentInstantiationBuilder, FragnoInstantiatedFragment, IFragmentInstantiationBuilder, InstantiatedFragmentFromDefinition } from "./api/fragment-instantiator.js";
7
7
  import { FragmentDefinition, FragmentDefinitionBuilder, ServiceConstructorFn, ServiceContext } from "./api/fragment-definition-builder.js";
8
+ import { FragnoRuntime, defaultFragnoRuntime } from "./runtime.js";
8
9
 
9
10
  //#region src/mod-client.d.ts
10
- declare function defineFragment(_name: string): {
11
- withDependencies: () => /*elided*/any;
12
- providesBaseService: () => /*elided*/any;
13
- providesService: () => /*elided*/any;
14
- providesPrivateService: () => /*elided*/any;
15
- usesService: () => /*elided*/any;
16
- usesOptionalService: () => /*elided*/any;
17
- withRequestStorage: () => /*elided*/any;
18
- withExternalRequestStorage: () => /*elided*/any;
19
- withThisContext: () => /*elided*/any;
20
- withLinkedFragment: () => /*elided*/any;
21
- extend: () => /*elided*/any;
22
- build: () => {};
23
- };
24
- declare function instantiate(_definition: unknown): {
25
- withConfig: () => /*elided*/any;
26
- withRoutes: () => /*elided*/any;
27
- withOptions: () => /*elided*/any;
28
- withServices: () => /*elided*/any;
29
- build: () => {};
30
- definition: {};
31
- routes: never[];
32
- config: undefined;
33
- options: undefined;
34
- };
11
+ declare function defineFragment(_name: string): object;
12
+ declare function instantiate(_definition: unknown): IFragmentInstantiationBuilder;
35
13
  //#endregion
36
- 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, instantiate, instantiatedFragmentFakeSymbol };
14
+ export { type BoundServices, type FragmentDefinition, FragmentDefinitionBuilder, type FragmentInstantiationBuilder, type FragnoInstantiatedFragment, type FragnoPublicConfig, type FragnoRouteConfig, type FragnoRuntime, type InstantiatedFragmentFromDefinition, type RequestThisContext, type RouteFactory, type RouteFactoryContext, type ServiceConstructorFn, type ServiceContext, defaultFragnoRuntime, defineFragment, defineRoute, defineRoutes, instantiate, instantiatedFragmentFakeSymbol };
37
15
  //# sourceMappingURL=mod-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mod-client.d.ts","names":[],"sources":["../src/mod-client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAyBgB,cAAA;;;;;;;EAAA,kBAAc,EAAA,GAAA,GAAA,UAAA,GAAA;EAuBd,0BAAW,EAAA,GAAA,GAAA,UAAA,GAAA;;;;;;iBAAX,WAAA"}
1
+ {"version":3,"file":"mod-client.d.ts","names":[],"sources":["../src/mod-client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;iBAgCgB,cAAA;iBAyCA,WAAA,wBAAgC"}
@@ -1,10 +1,12 @@
1
+ import { instantiatedFragmentFakeSymbol } from "./internal/symbols.js";
1
2
  import { FragmentDefinitionBuilder } from "./api/fragment-definition-builder.js";
3
+ import { defaultFragnoRuntime } from "./runtime.js";
2
4
  import { defineRoute, defineRoutes } from "./api/route.js";
3
- import { instantiatedFragmentFakeSymbol } from "./internal/symbols.js";
4
5
 
5
6
  //#region src/mod-client.ts
6
7
  function defineFragment(_name) {
7
- const stub = {
8
+ const definitionStub = { name: _name };
9
+ const stub = new Proxy({
8
10
  withDependencies: () => stub,
9
11
  providesBaseService: () => stub,
10
12
  providesService: () => stub,
@@ -16,25 +18,60 @@ function defineFragment(_name) {
16
18
  withThisContext: () => stub,
17
19
  withLinkedFragment: () => stub,
18
20
  extend: () => stub,
19
- build: () => ({})
20
- };
21
+ build: () => definitionStub,
22
+ provideHooks: () => stub
23
+ }, { get(target, prop) {
24
+ if (prop in target) return target[prop];
25
+ return () => stub;
26
+ } });
21
27
  return stub;
22
28
  }
23
29
  function instantiate(_definition) {
24
- const stub = {
25
- withConfig: () => stub,
26
- withRoutes: () => stub,
27
- withOptions: () => stub,
28
- withServices: () => stub,
29
- build: () => ({}),
30
+ const fragmentStubMethods = {
31
+ [instantiatedFragmentFakeSymbol]: instantiatedFragmentFakeSymbol,
32
+ name: "",
33
+ routes: [],
34
+ services: {},
35
+ mountRoute: "",
36
+ get $internal() {
37
+ return {
38
+ deps: {},
39
+ options: {},
40
+ linkedFragments: {}
41
+ };
42
+ },
43
+ withMiddleware: () => fragmentStub,
44
+ inContext: (callback) => callback(),
45
+ handlersFor: () => ({}),
46
+ handler: async () => new Response(),
47
+ callRoute: async () => ({
48
+ ok: true,
49
+ data: void 0,
50
+ error: void 0
51
+ }),
52
+ callRouteRaw: async () => new Response()
53
+ };
54
+ const fragmentStub = new Proxy(fragmentStubMethods, { get(target, prop) {
55
+ if (prop in target) return target[prop];
56
+ return () => fragmentStub;
57
+ } });
58
+ const builderStub = new Proxy({
59
+ withConfig: () => builderStub,
60
+ withRoutes: () => builderStub,
61
+ withOptions: () => builderStub,
62
+ withServices: () => builderStub,
63
+ build: () => fragmentStub,
30
64
  definition: {},
31
65
  routes: [],
32
66
  config: void 0,
33
67
  options: void 0
34
- };
35
- return stub;
68
+ }, { get(target, prop) {
69
+ if (prop in target) return target[prop];
70
+ return () => builderStub;
71
+ } });
72
+ return builderStub;
36
73
  }
37
74
 
38
75
  //#endregion
39
- export { FragmentDefinitionBuilder, defineFragment, defineRoute, defineRoutes, instantiate, instantiatedFragmentFakeSymbol };
76
+ export { FragmentDefinitionBuilder, defaultFragnoRuntime, defineFragment, defineRoute, defineRoutes, instantiate, instantiatedFragmentFakeSymbol };
40
77
  //# sourceMappingURL=mod-client.js.map
@@ -1 +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 const stub = {\n withDependencies: () => stub,\n providesBaseService: () => stub,\n providesService: () => stub,\n providesPrivateService: () => stub,\n usesService: () => stub,\n usesOptionalService: () => stub,\n withRequestStorage: () => stub,\n withExternalRequestStorage: () => stub,\n withThisContext: () => stub,\n withLinkedFragment: () => stub,\n extend: () => stub,\n build: () => ({}),\n };\n return stub;\n}\n\n// Re-export the builder class (for type compatibility)\nexport { FragmentDefinitionBuilder } from \"./api/fragment-definition-builder\";\n\n// Stub implementation for instantiate\n// This is stripped by unplugin-fragno in browser builds\nexport function instantiate(_definition: unknown) {\n const stub = {\n withConfig: () => stub,\n withRoutes: () => stub,\n withOptions: () => stub,\n withServices: () => stub,\n build: () => ({}),\n definition: {},\n routes: [],\n config: undefined,\n options: undefined,\n };\n return stub;\n}\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;CAC5C,MAAM,OAAO;EACX,wBAAwB;EACxB,2BAA2B;EAC3B,uBAAuB;EACvB,8BAA8B;EAC9B,mBAAmB;EACnB,2BAA2B;EAC3B,0BAA0B;EAC1B,kCAAkC;EAClC,uBAAuB;EACvB,0BAA0B;EAC1B,cAAc;EACd,cAAc,EAAE;EACjB;AACD,QAAO;;AAQT,SAAgB,YAAY,aAAsB;CAChD,MAAM,OAAO;EACX,kBAAkB;EAClB,kBAAkB;EAClB,mBAAmB;EACnB,oBAAoB;EACpB,cAAc,EAAE;EAChB,YAAY,EAAE;EACd,QAAQ,EAAE;EACV,QAAQ;EACR,SAAS;EACV;AACD,QAAO"}
1
+ {"version":3,"file":"mod-client.js","names":["stub: object","fragmentStub: IFragnoInstantiatedFragment","builderStub: IFragmentInstantiationBuilder"],"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 InstantiatedFragmentFromDefinition,\n} from \"./api/fragment-instantiator\";\n\nimport type {\n IFragmentInstantiationBuilder,\n IFragnoInstantiatedFragment,\n} from \"./api/fragment-instantiator\";\nimport { instantiatedFragmentFakeSymbol } from \"./internal/symbols\";\n\n// Stub implementation for defineFragment\n// This is stripped by unplugin-fragno in browser builds\nexport function defineFragment(_name: string) {\n const definitionStub = {\n name: _name,\n };\n\n const stubMethods = {\n withDependencies: () => stub,\n providesBaseService: () => stub,\n providesService: () => stub,\n providesPrivateService: () => stub,\n usesService: () => stub,\n usesOptionalService: () => stub,\n withRequestStorage: () => stub,\n withExternalRequestStorage: () => stub,\n withThisContext: () => stub,\n withLinkedFragment: () => stub,\n extend: () => stub,\n build: () => definitionStub,\n // From fragno-db\n provideHooks: () => stub,\n };\n\n // Wrap with Proxy to handle any additional methods (e.g. from extend())\n const stub: object = new Proxy(stubMethods, {\n get(target, prop) {\n if (prop in target) {\n return target[prop as keyof typeof target];\n }\n // Return a function that returns the stub for method chaining\n return () => stub;\n },\n });\n\n return stub;\n}\n\n// Re-export the builder class (for type compatibility)\nexport { FragmentDefinitionBuilder } from \"./api/fragment-definition-builder\";\n\n// Stub implementation for instantiate\n// This is stripped by unplugin-fragno in browser builds\nexport function instantiate(_definition: unknown) {\n const fragmentStubMethods = {\n [instantiatedFragmentFakeSymbol]: instantiatedFragmentFakeSymbol,\n name: \"\",\n routes: [],\n services: {},\n mountRoute: \"\",\n get $internal() {\n return {\n deps: {},\n options: {},\n linkedFragments: {},\n };\n },\n withMiddleware: () => fragmentStub,\n inContext: <T>(callback: () => T) => callback(),\n handlersFor: () => ({}),\n handler: async () => new Response(),\n callRoute: async () => ({ ok: true, data: undefined, error: undefined }),\n callRouteRaw: async () => new Response(),\n };\n\n // Wrap with Proxy to handle any additional methods\n const fragmentStub: IFragnoInstantiatedFragment = new Proxy(\n fragmentStubMethods as IFragnoInstantiatedFragment,\n {\n get(target, prop) {\n if (prop in target) {\n return target[prop as keyof typeof target];\n }\n // Return a function that returns the stub for method chaining\n return () => fragmentStub;\n },\n },\n );\n\n const builderStubMethods = {\n withConfig: () => builderStub,\n withRoutes: () => builderStub,\n withOptions: () => builderStub,\n withServices: () => builderStub,\n build: () => fragmentStub,\n definition: {},\n routes: [],\n config: undefined,\n options: undefined,\n };\n\n // Wrap with Proxy to handle any additional methods\n const builderStub: IFragmentInstantiationBuilder = new Proxy(\n builderStubMethods as IFragmentInstantiationBuilder,\n {\n get(target, prop) {\n if (prop in target) {\n return target[prop as keyof typeof target];\n }\n // Return a function that returns the stub for method chaining\n return () => builderStub;\n },\n },\n );\n\n return builderStub;\n}\n\n// ============================================================================\n// Core Configuration\n// ============================================================================\nexport type { FragnoPublicConfig } from \"./api/shared-types\";\n\n// ============================================================================\n// Runtime\n// ============================================================================\nexport { defaultFragnoRuntime, type FragnoRuntime } from \"./runtime\";\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":";;;;;;AAgCA,SAAgB,eAAe,OAAe;CAC5C,MAAM,iBAAiB,EACrB,MAAM,OACP;CAoBD,MAAMA,OAAe,IAAI,MAlBL;EAClB,wBAAwB;EACxB,2BAA2B;EAC3B,uBAAuB;EACvB,8BAA8B;EAC9B,mBAAmB;EACnB,2BAA2B;EAC3B,0BAA0B;EAC1B,kCAAkC;EAClC,uBAAuB;EACvB,0BAA0B;EAC1B,cAAc;EACd,aAAa;EAEb,oBAAoB;EACrB,EAG2C,EAC1C,IAAI,QAAQ,MAAM;AAChB,MAAI,QAAQ,OACV,QAAO,OAAO;AAGhB,eAAa;IAEhB,CAAC;AAEF,QAAO;;AAQT,SAAgB,YAAY,aAAsB;CAChD,MAAM,sBAAsB;GACzB,iCAAiC;EAClC,MAAM;EACN,QAAQ,EAAE;EACV,UAAU,EAAE;EACZ,YAAY;EACZ,IAAI,YAAY;AACd,UAAO;IACL,MAAM,EAAE;IACR,SAAS,EAAE;IACX,iBAAiB,EAAE;IACpB;;EAEH,sBAAsB;EACtB,YAAe,aAAsB,UAAU;EAC/C,oBAAoB,EAAE;EACtB,SAAS,YAAY,IAAI,UAAU;EACnC,WAAW,aAAa;GAAE,IAAI;GAAM,MAAM;GAAW,OAAO;GAAW;EACvE,cAAc,YAAY,IAAI,UAAU;EACzC;CAGD,MAAMC,eAA4C,IAAI,MACpD,qBACA,EACE,IAAI,QAAQ,MAAM;AAChB,MAAI,QAAQ,OACV,QAAO,OAAO;AAGhB,eAAa;IAEhB,CACF;CAeD,MAAMC,cAA6C,IAAI,MAb5B;EACzB,kBAAkB;EAClB,kBAAkB;EAClB,mBAAmB;EACnB,oBAAoB;EACpB,aAAa;EACb,YAAY,EAAE;EACd,QAAQ,EAAE;EACV,QAAQ;EACR,SAAS;EACV,EAKC,EACE,IAAI,QAAQ,MAAM;AAChB,MAAI,QAAQ,OACV,QAAO,OAAO;AAGhB,eAAa;IAEhB,CACF;AAED,QAAO"}
package/dist/mod.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { FragnoPublicConfig } from "./api/shared-types.js";
2
- import { FragnoRouteConfig, RequestThisContext } from "./api/api.js";
2
+ import { FragnoRouteConfig, RequestThisContext, RouteContentType } from "./api/api.js";
3
3
  import { BoundServices } from "./api/bind-services.js";
4
4
  import { RouteFactory, RouteFactoryContext, defineRoute, defineRoutes } from "./api/route.js";
5
- import { AnyFragnoInstantiatedFragment, FragmentInstantiationBuilder, FragnoInstantiatedFragment, instantiate } from "./api/fragment-instantiator.js";
5
+ import { AnyFragnoInstantiatedFragment, FragmentInstantiationBuilder, FragnoInstantiatedFragment, InstantiatedFragmentFromDefinition, instantiate } from "./api/fragment-instantiator.js";
6
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 };
7
+ import { FragnoRuntime, defaultFragnoRuntime } from "./runtime.js";
8
+ export { type AnyFragnoInstantiatedFragment, type BoundServices, type ExtractLinkedServices, type FragmentDefinition, FragmentDefinitionBuilder, type FragmentInstantiationBuilder, type FragnoInstantiatedFragment, type FragnoPublicConfig, type FragnoRouteConfig, type FragnoRuntime, type InstantiatedFragmentFromDefinition, type LinkedFragmentCallback, type RequestThisContext, type RouteContentType, type RouteFactory, type RouteFactoryContext, type ServiceConstructorFn, type ServiceContext, defaultFragnoRuntime, defineFragment, defineRoute, defineRoutes, instantiate };
package/dist/mod.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { FragmentDefinitionBuilder, defineFragment } from "./api/fragment-definition-builder.js";
2
+ import { defaultFragnoRuntime } from "./runtime.js";
2
3
  import { defineRoute, defineRoutes } from "./api/route.js";
3
4
  import { instantiate } from "./api/fragment-instantiator.js";
4
5
 
5
- export { FragmentDefinitionBuilder, defineFragment, defineRoute, defineRoutes, instantiate };
6
+ export { FragmentDefinitionBuilder, defaultFragnoRuntime, defineFragment, defineRoute, defineRoutes, instantiate };
@@ -0,0 +1,15 @@
1
+ //#region src/runtime.d.ts
2
+ type FragnoRuntime = {
3
+ time: {
4
+ now: () => Date;
5
+ };
6
+ random: {
7
+ float: () => number;
8
+ uuid: () => string;
9
+ cuid: () => string;
10
+ };
11
+ };
12
+ declare const defaultFragnoRuntime: FragnoRuntime;
13
+ //#endregion
14
+ export { FragnoRuntime, defaultFragnoRuntime };
15
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","names":[],"sources":["../src/runtime.ts"],"sourcesContent":[],"mappings":";KAEY,aAAA;EAAA,IAAA,EAAA;IAoCC,GAAA,EAAA,GAAA,GAlCE,IAkCF;;;;;;;;cAAA,sBAAsB"}
@@ -0,0 +1,33 @@
1
+ import { createId } from "@paralleldrive/cuid2";
2
+
3
+ //#region src/runtime.ts
4
+ const fallbackUuid = () => {
5
+ const bytes = new Uint8Array(16);
6
+ for (let i = 0; i < bytes.length; i += 1) bytes[i] = Math.floor(Math.random() * 256);
7
+ bytes[6] = bytes[6] & 15 | 64;
8
+ bytes[8] = bytes[8] & 63 | 128;
9
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
10
+ return [
11
+ hex.slice(0, 4).join(""),
12
+ hex.slice(4, 6).join(""),
13
+ hex.slice(6, 8).join(""),
14
+ hex.slice(8, 10).join(""),
15
+ hex.slice(10, 16).join("")
16
+ ].join("-");
17
+ };
18
+ const defaultUuid = () => {
19
+ const cryptoApi = globalThis.crypto;
20
+ return cryptoApi?.randomUUID ? cryptoApi.randomUUID() : fallbackUuid();
21
+ };
22
+ const defaultFragnoRuntime = {
23
+ time: { now: () => /* @__PURE__ */ new Date() },
24
+ random: {
25
+ float: () => Math.random(),
26
+ uuid: () => defaultUuid(),
27
+ cuid: () => createId()
28
+ }
29
+ };
30
+
31
+ //#endregion
32
+ export { defaultFragnoRuntime };
33
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","names":["defaultFragnoRuntime: FragnoRuntime"],"sources":["../src/runtime.ts"],"sourcesContent":["import { createId } from \"@paralleldrive/cuid2\";\n\nexport type FragnoRuntime = {\n time: {\n now: () => Date;\n };\n random: {\n float: () => number;\n uuid: () => string;\n cuid: () => string;\n };\n};\n\nconst fallbackUuid = () => {\n const bytes = new Uint8Array(16);\n for (let i = 0; i < bytes.length; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n\n // RFC 4122 variant + version 4\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return [\n hex.slice(0, 4).join(\"\"),\n hex.slice(4, 6).join(\"\"),\n hex.slice(6, 8).join(\"\"),\n hex.slice(8, 10).join(\"\"),\n hex.slice(10, 16).join(\"\"),\n ].join(\"-\");\n};\n\nconst defaultUuid = () => {\n const cryptoApi = globalThis.crypto;\n return cryptoApi?.randomUUID ? cryptoApi.randomUUID() : fallbackUuid();\n};\n\nexport const defaultFragnoRuntime: FragnoRuntime = {\n time: {\n now: () => new Date(),\n },\n random: {\n float: () => Math.random(),\n uuid: () => defaultUuid(),\n cuid: () => createId(),\n },\n};\n"],"mappings":";;;AAaA,MAAM,qBAAqB;CACzB,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EACrC,OAAM,KAAK,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI;AAI5C,OAAM,KAAM,MAAM,KAAK,KAAQ;AAC/B,OAAM,KAAM,MAAM,KAAK,KAAQ;CAE/B,MAAM,MAAM,MAAM,KAAK,QAAQ,SAAS,KAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAC3E,QAAO;EACL,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;EACxB,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;EACxB,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;EACxB,IAAI,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG;EACzB,IAAI,MAAM,IAAI,GAAG,CAAC,KAAK,GAAG;EAC3B,CAAC,KAAK,IAAI;;AAGb,MAAM,oBAAoB;CACxB,MAAM,YAAY,WAAW;AAC7B,QAAO,WAAW,aAAa,UAAU,YAAY,GAAG,cAAc;;AAGxE,MAAaA,uBAAsC;CACjD,MAAM,EACJ,2BAAW,IAAI,MAAM,EACtB;CACD,QAAQ;EACN,aAAa,KAAK,QAAQ;EAC1B,YAAY,aAAa;EACzB,YAAY,UAAU;EACvB;CACF"}
@@ -4,7 +4,7 @@ import { RequestThisContext } from "../api/api.js";
4
4
  import { BoundServices } from "../api/bind-services.js";
5
5
  import { AnyRouteOrFactory, FlattenRouteFactories } from "../api/route.js";
6
6
  import { FragnoResponse } from "../api/fragno-response.js";
7
- import { FragnoInstantiatedFragment } from "../api/fragment-instantiator.js";
7
+ import { AnyFragnoInstantiatedFragment, FragnoInstantiatedFragment, RoutesWithInternal } from "../api/fragment-instantiator.js";
8
8
  import { FragmentDefinition, FragmentDefinitionBuilder } from "../api/fragment-definition-builder.js";
9
9
 
10
10
  //#region src/test/test.d.ts
@@ -87,7 +87,7 @@ interface CreateFragmentForTestOptions<TConfig, TOptions extends FragnoPublicCon
87
87
  * }
88
88
  * ```
89
89
  */
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>;
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[], TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {}>(definition: FragmentDefinition<TConfig, TOptions, TDeps, TBaseServices, TServices, TServiceDependencies, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage, TLinkedFragments>, routesOrFactories: TRoutesOrFactories, options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>): FragnoInstantiatedFragment<RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TLinkedFragments>, TDeps, BoundServices<TBaseServices & TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage, TOptions, TLinkedFragments>;
91
91
  //#endregion
92
92
  export { CreateFragmentForTestOptions, type FragnoResponse, type RouteHandlerInputOptions, TestBaseServices, createFragmentForTest, withTestUtils };
93
93
  //# sourceMappingURL=test.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"test.d.ts","names":[],"sources":["../../src/test/test.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;KAoBY;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG,iBA7Ba,qBA6Bb,CAAA,OAAA,EAAA,iBA3BgB,kBA2BhB,EAAA,KAAA,EAAA,sBAzBqB,MAyBrB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,kBAxBiB,MAwBjB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,oBAAA,EAAA,yBAtBwB,MAsBxB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,4BArB2B,kBAqB3B,EAAA,4BApB2B,kBAoB3B,EAAA,eAAA,EAAA,iCAAA,SAlByC,iBAkBzC,EAAA,EAAA,yBAjBwB,MAiBxB,CAAA,MAAA,EAjBuC,6BAiBvC,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,UAAA,EAfW,kBAeX,CAdC,OAcD,EAbC,QAaD,EAZC,KAYD,EAXC,aAWD,EAVC,SAUD,EATC,oBASD,EARC,gBAQD,EAPC,mBAOD,EANC,mBAMD,EALC,eAKD,EAJC,gBAID,CAAA,EAAA,iBAAA,EAFkB,kBAElB,EAAA,OAAA,EADQ,4BACR,CADqC,OACrC,EAD8C,QAC9C,EADwD,oBACxD,CAAA,CAAA,EAAA,0BAAA,CACD,kBADC,CACkB,qBADlB,CACwC,kBADxC,CAAA,EAC6D,gBAD7D,CAAA,EAED,KAFC,EAGD,aAHC,CAGa,aAHb,GAG6B,SAH7B,CAAA,EAID,mBAJC,EAKD,mBALC,EAMD,eANC,EAOD,QAPC,EAQD,gBARC,CAAA"}
@@ -1 +1 @@
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"}
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 {\n instantiateFragment,\n type AnyFragnoInstantiatedFragment,\n type FragnoInstantiatedFragment,\n type RoutesWithInternal,\n} 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 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 routesOrFactories: TRoutesOrFactories,\n options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>,\n): FragnoInstantiatedFragment<\n RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TLinkedFragments>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\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":";;;;;;;;;;;;;;;;;;;AA0CA,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,sBAcd,YAaA,mBACA,SAUA;CACA,MAAM,EAAE,QAAQ,SAAS,kBAAkB,EAAE,EAAc,2BAA2B;AAStF,QAAO,oBACL,YACA,QACA,mBATkB;EAClB,YAAY;EACZ,GAAG;EACJ,EAQC,uBACD"}
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "@fragno-dev/core",
3
- "version": "0.1.10",
3
+ "version": "0.2.0",
4
4
  "exports": {
5
5
  ".": {
6
- "development": "./src/mod.ts",
6
+ "workerd": {
7
+ "development": "./src/mod.ts",
8
+ "default": "./dist/mod.js"
9
+ },
10
+ "development": {
11
+ "browser": "./src/mod-client.ts",
12
+ "default": "./src/mod.ts"
13
+ },
7
14
  "browser": "./dist/mod-client.js",
8
15
  "types": "./dist/mod.d.ts",
9
16
  "default": "./dist/mod.js"
@@ -33,6 +40,11 @@
33
40
  "types": "./dist/api/request-context-storage.d.ts",
34
41
  "default": "./dist/api/request-context-storage.js"
35
42
  },
43
+ "./internal/trace-context": {
44
+ "development": "./src/internal/trace-context.ts",
45
+ "types": "./dist/internal/trace-context.d.ts",
46
+ "default": "./dist/internal/trace-context.js"
47
+ },
36
48
  "./internal/symbols": {
37
49
  "development": "./src/internal/symbols.ts",
38
50
  "types": "./dist/internal/symbols.d.ts",
@@ -81,29 +93,30 @@
81
93
  "type": "module",
82
94
  "sideEffects": false,
83
95
  "dependencies": {
96
+ "@paralleldrive/cuid2": "^2.3.1",
84
97
  "@nanostores/query": "^0.3.4",
85
98
  "@nanostores/solid": "^1.1.1",
86
- "@standard-schema/spec": "^1.0.0",
87
- "nanostores": "^1.0.1",
88
- "rou3": "^0.7.3"
99
+ "@standard-schema/spec": "^1.1.0",
100
+ "nanostores": "^1.1.0",
101
+ "rou3": "^0.7.12"
89
102
  },
90
103
  "devDependencies": {
91
- "@sveltejs/vite-plugin-svelte": "^6.2.0",
92
- "@testing-library/react": "^16.3.0",
93
- "@testing-library/svelte": "^5.2.8",
104
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
105
+ "@testing-library/react": "^16.3.2",
106
+ "@testing-library/svelte": "^5.3.1",
94
107
  "@testing-library/vue": "^8.1.0",
95
- "@types/node": "^22",
96
- "@types/react": "^19.0.0",
97
- "@types/react-dom": "^19.0.0",
108
+ "@types/node": "^22.19.7",
109
+ "@types/react": "^19.2.8",
110
+ "@types/react-dom": "^19.2.3",
98
111
  "@vitest/coverage-istanbul": "^3.2.4",
99
- "happy-dom": "^20.0.0",
100
- "react": "^19.0.0",
101
- "react-dom": "^19.0.0",
102
- "svelte": "^5.1.0",
103
- "solid-js": "^1.9.3",
112
+ "happy-dom": "^20.3.3",
113
+ "react": "^19.0.3",
114
+ "react-dom": "^19.0.3",
115
+ "solid-js": "^1.9.10",
116
+ "svelte": "^5.47.0",
104
117
  "vitest": "^3.2.4",
105
- "vue": "^3",
106
- "zod": "^4.0.5",
118
+ "vue": "^3.5.27",
119
+ "zod": "^4.3.5",
107
120
  "@fragno-private/typescript-config": "0.0.1",
108
121
  "@fragno-private/vitest-config": "0.0.0"
109
122
  },
package/src/api/api.ts CHANGED
@@ -2,6 +2,8 @@ import type { StandardSchemaV1 } from "@standard-schema/spec";
2
2
  import type { RequestInputContext } from "./request-input-context";
3
3
  import type { RequestOutputContext } from "./request-output-context";
4
4
 
5
+ export type { StandardSchemaV1 } from "@standard-schema/spec";
6
+
5
7
  export type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
6
8
  export type NonGetHTTPMethod = Exclude<HTTPMethod, "GET">;
7
9
 
@@ -25,6 +27,17 @@ export type ValidPath<T extends string = string> = T extends `/${infer Rest}`
25
27
 
26
28
  export interface RequestThisContext {}
27
29
 
30
+ /**
31
+ * Content types that can be accepted by a route.
32
+ *
33
+ * - `"application/json"` (default): JSON request body, validated against inputSchema
34
+ * - `"multipart/form-data"`: FormData request body (file uploads), no schema validation
35
+ */
36
+ export type RouteContentType =
37
+ | "application/json"
38
+ | "multipart/form-data"
39
+ | "application/octet-stream";
40
+
28
41
  export interface FragnoRouteConfig<
29
42
  TMethod extends HTTPMethod,
30
43
  TPath extends string,
@@ -36,6 +49,17 @@ export interface FragnoRouteConfig<
36
49
  > {
37
50
  method: TMethod;
38
51
  path: TPath;
52
+ /**
53
+ * The expected content type for this route's request body.
54
+ *
55
+ * - `"application/json"` (default): Expects JSON body, will be validated against inputSchema
56
+ * - `"multipart/form-data"`: Expects FormData body (for file uploads), use `ctx.formData()` in handler
57
+ *
58
+ * The server will reject requests with mismatched Content-Type headers.
59
+ *
60
+ * @default "application/json"
61
+ */
62
+ contentType?: RouteContentType;
39
63
  inputSchema?: TInputSchema;
40
64
  outputSchema?: TOutputSchema;
41
65
  errorCodes?: readonly TErrorCode[];