@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.
Files changed (183) hide show
  1. package/.turbo/turbo-build.log +131 -64
  2. package/CHANGELOG.md +19 -0
  3. package/dist/api/api.d.ts +38 -2
  4. package/dist/api/api.d.ts.map +1 -0
  5. package/dist/api/api.js +9 -2
  6. package/dist/api/api.js.map +1 -0
  7. package/dist/api/bind-services.d.ts +6 -0
  8. package/dist/api/bind-services.d.ts.map +1 -0
  9. package/dist/api/bind-services.js +20 -0
  10. package/dist/api/bind-services.js.map +1 -0
  11. package/dist/api/error.d.ts +26 -0
  12. package/dist/api/error.d.ts.map +1 -0
  13. package/dist/{api-DngJDcmO.js → api/error.js} +2 -8
  14. package/dist/api/error.js.map +1 -0
  15. package/dist/api/fragment-definition-builder.d.ts +313 -0
  16. package/dist/api/fragment-definition-builder.d.ts.map +1 -0
  17. package/dist/api/fragment-definition-builder.js +326 -0
  18. package/dist/api/fragment-definition-builder.js.map +1 -0
  19. package/dist/api/fragment-instantiator.d.ts +216 -0
  20. package/dist/api/fragment-instantiator.d.ts.map +1 -0
  21. package/dist/api/fragment-instantiator.js +487 -0
  22. package/dist/api/fragment-instantiator.js.map +1 -0
  23. package/dist/api/fragno-response.d.ts +30 -0
  24. package/dist/api/fragno-response.d.ts.map +1 -0
  25. package/dist/api/fragno-response.js +73 -0
  26. package/dist/api/fragno-response.js.map +1 -0
  27. package/dist/api/internal/path.d.ts +50 -0
  28. package/dist/api/internal/path.d.ts.map +1 -0
  29. package/dist/api/internal/path.js +76 -0
  30. package/dist/api/internal/path.js.map +1 -0
  31. package/dist/api/internal/response-stream.d.ts +43 -0
  32. package/dist/api/internal/response-stream.d.ts.map +1 -0
  33. package/dist/api/internal/response-stream.js +81 -0
  34. package/dist/api/internal/response-stream.js.map +1 -0
  35. package/dist/api/internal/route.js +10 -0
  36. package/dist/api/internal/route.js.map +1 -0
  37. package/dist/api/mutable-request-state.d.ts +82 -0
  38. package/dist/api/mutable-request-state.d.ts.map +1 -0
  39. package/dist/api/mutable-request-state.js +97 -0
  40. package/dist/api/mutable-request-state.js.map +1 -0
  41. package/dist/api/request-context-storage.d.ts +42 -0
  42. package/dist/api/request-context-storage.d.ts.map +1 -0
  43. package/dist/api/request-context-storage.js +43 -0
  44. package/dist/api/request-context-storage.js.map +1 -0
  45. package/dist/api/request-input-context.d.ts +89 -0
  46. package/dist/api/request-input-context.d.ts.map +1 -0
  47. package/dist/api/request-input-context.js +118 -0
  48. package/dist/api/request-input-context.js.map +1 -0
  49. package/dist/api/request-middleware.d.ts +50 -0
  50. package/dist/api/request-middleware.d.ts.map +1 -0
  51. package/dist/api/request-middleware.js +83 -0
  52. package/dist/api/request-middleware.js.map +1 -0
  53. package/dist/api/request-output-context.d.ts +41 -0
  54. package/dist/api/request-output-context.d.ts.map +1 -0
  55. package/dist/api/request-output-context.js +119 -0
  56. package/dist/api/request-output-context.js.map +1 -0
  57. package/dist/api/route-handler-input-options.d.ts +21 -0
  58. package/dist/api/route-handler-input-options.d.ts.map +1 -0
  59. package/dist/api/route.d.ts +54 -3
  60. package/dist/api/route.d.ts.map +1 -0
  61. package/dist/api/route.js +29 -2
  62. package/dist/api/route.js.map +1 -0
  63. package/dist/api/shared-types.d.ts +47 -0
  64. package/dist/api/shared-types.d.ts.map +1 -0
  65. package/dist/api/shared-types.js +1 -0
  66. package/dist/client/client-error.d.ts +60 -0
  67. package/dist/client/client-error.d.ts.map +1 -0
  68. package/dist/client/client-error.js +92 -0
  69. package/dist/client/client-error.js.map +1 -0
  70. package/dist/client/client.d.ts +210 -4
  71. package/dist/client/client.d.ts.map +1 -0
  72. package/dist/client/client.js +397 -6
  73. package/dist/client/client.js.map +1 -0
  74. package/dist/client/client.svelte.d.ts +5 -3
  75. package/dist/client/client.svelte.d.ts.map +1 -1
  76. package/dist/client/client.svelte.js +1 -5
  77. package/dist/client/client.svelte.js.map +1 -1
  78. package/dist/client/internal/fetcher-merge.js +36 -0
  79. package/dist/client/internal/fetcher-merge.js.map +1 -0
  80. package/dist/client/internal/ndjson-streaming.js +139 -0
  81. package/dist/client/internal/ndjson-streaming.js.map +1 -0
  82. package/dist/client/react.d.ts +5 -3
  83. package/dist/client/react.d.ts.map +1 -1
  84. package/dist/client/react.js +3 -5
  85. package/dist/client/react.js.map +1 -1
  86. package/dist/client/solid.d.ts +5 -3
  87. package/dist/client/solid.d.ts.map +1 -1
  88. package/dist/client/solid.js +2 -5
  89. package/dist/client/solid.js.map +1 -1
  90. package/dist/client/vanilla.d.ts +5 -3
  91. package/dist/client/vanilla.d.ts.map +1 -1
  92. package/dist/client/vanilla.js +2 -43
  93. package/dist/client/vanilla.js.map +1 -1
  94. package/dist/client/vue.d.ts +5 -3
  95. package/dist/client/vue.d.ts.map +1 -1
  96. package/dist/client/vue.js +1 -5
  97. package/dist/client/vue.js.map +1 -1
  98. package/dist/http/http-status.d.ts +26 -0
  99. package/dist/http/http-status.d.ts.map +1 -0
  100. package/dist/integrations/react-ssr.js +1 -1
  101. package/dist/internal/symbols.d.ts +9 -0
  102. package/dist/internal/symbols.d.ts.map +1 -0
  103. package/dist/internal/symbols.js +10 -0
  104. package/dist/internal/symbols.js.map +1 -0
  105. package/dist/mod-client.d.ts +36 -0
  106. package/dist/mod-client.d.ts.map +1 -0
  107. package/dist/mod-client.js +21 -0
  108. package/dist/mod-client.js.map +1 -0
  109. package/dist/mod.d.ts +7 -4
  110. package/dist/mod.js +4 -6
  111. package/dist/request/request.d.ts +4 -0
  112. package/dist/request/request.js +5 -0
  113. package/dist/test/test.d.ts +62 -35
  114. package/dist/test/test.d.ts.map +1 -1
  115. package/dist/test/test.js +75 -40
  116. package/dist/test/test.js.map +1 -1
  117. package/dist/util/async.js +40 -0
  118. package/dist/util/async.js.map +1 -0
  119. package/dist/util/content-type.js +49 -0
  120. package/dist/util/content-type.js.map +1 -0
  121. package/dist/util/nanostores.js +31 -0
  122. package/dist/util/nanostores.js.map +1 -0
  123. package/dist/{ssr-BByDVfFD.js → util/ssr.js} +2 -2
  124. package/dist/util/ssr.js.map +1 -0
  125. package/dist/util/types-util.d.ts +8 -0
  126. package/dist/util/types-util.d.ts.map +1 -0
  127. package/package.json +19 -12
  128. package/src/api/api.ts +41 -6
  129. package/src/api/bind-services.ts +42 -0
  130. package/src/api/fragment-definition-builder.extend.test.ts +810 -0
  131. package/src/api/fragment-definition-builder.test.ts +499 -0
  132. package/src/api/fragment-definition-builder.ts +1088 -0
  133. package/src/api/fragment-instantiator.test.ts +1488 -0
  134. package/src/api/fragment-instantiator.ts +1053 -0
  135. package/src/api/fragment-services.test.ts +727 -0
  136. package/src/api/request-context-storage.ts +64 -0
  137. package/src/api/request-middleware.test.ts +301 -225
  138. package/src/api/route.test.ts +87 -1
  139. package/src/api/route.ts +345 -24
  140. package/src/api/shared-types.ts +43 -0
  141. package/src/client/client-builder.test.ts +23 -23
  142. package/src/client/client.ssr.test.ts +3 -3
  143. package/src/client/client.svelte.test.ts +15 -15
  144. package/src/client/client.test.ts +22 -22
  145. package/src/client/client.ts +72 -12
  146. package/src/client/internal/fetcher-merge.ts +1 -1
  147. package/src/client/react.test.ts +2 -2
  148. package/src/client/solid.test.ts +2 -2
  149. package/src/client/vanilla.test.ts +2 -2
  150. package/src/client/vue.test.ts +2 -2
  151. package/src/internal/symbols.ts +5 -0
  152. package/src/mod-client.ts +59 -0
  153. package/src/mod.ts +26 -9
  154. package/src/request/request.ts +8 -0
  155. package/src/test/test.test.ts +200 -381
  156. package/src/test/test.ts +190 -117
  157. package/tsdown.config.ts +8 -5
  158. package/dist/api/fragment-builder.d.ts +0 -4
  159. package/dist/api/fragment-builder.js +0 -3
  160. package/dist/api/fragment-instantiation.d.ts +0 -4
  161. package/dist/api/fragment-instantiation.js +0 -6
  162. package/dist/api-BWN97TOr.d.ts +0 -377
  163. package/dist/api-BWN97TOr.d.ts.map +0 -1
  164. package/dist/api-DngJDcmO.js.map +0 -1
  165. package/dist/client-C5LsYHEI.js +0 -782
  166. package/dist/client-C5LsYHEI.js.map +0 -1
  167. package/dist/fragment-builder-DOnCVBqc.js +0 -47
  168. package/dist/fragment-builder-DOnCVBqc.js.map +0 -1
  169. package/dist/fragment-builder-MGr68GNb.d.ts +0 -409
  170. package/dist/fragment-builder-MGr68GNb.d.ts.map +0 -1
  171. package/dist/fragment-instantiation-C4wvwl6V.js +0 -446
  172. package/dist/fragment-instantiation-C4wvwl6V.js.map +0 -1
  173. package/dist/request-output-context-CdIjwmEN.js +0 -320
  174. package/dist/request-output-context-CdIjwmEN.js.map +0 -1
  175. package/dist/route-Bl9Zr1Yv.d.ts +0 -26
  176. package/dist/route-Bl9Zr1Yv.d.ts.map +0 -1
  177. package/dist/route-C5Uryylh.js +0 -21
  178. package/dist/route-C5Uryylh.js.map +0 -1
  179. package/dist/ssr-BByDVfFD.js.map +0 -1
  180. package/src/api/fragment-builder.ts +0 -80
  181. package/src/api/fragment-instantiation.test.ts +0 -460
  182. package/src/api/fragment-instantiation.ts +0 -499
  183. package/src/api/fragment.test.ts +0 -537
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async.js","names":["queue: T[]","unsubscribe: UnsubscribeFn | null","resolveNext: ((value: IteratorResult<T>) => void) | null"],"sources":["../../src/util/async.ts"],"sourcesContent":["type SubscribeFn<T> = (callback: (value: T) => void) => UnsubscribeFn | void;\ntype UnsubscribeFn = () => void;\n\n/**\n * Creates an async iterator from a subscribe function that follows the observable pattern.\n *\n * @template T The type of values produced by the store.\n * @param subscribe A function that subscribes to store updates. It receives a callback to be\n * called on each update, and returns an unsubscribe function.\n * @returns An async generator that yields store values as they are produced.\n */\nexport function createAsyncIteratorFromCallback<T>(\n subscribe: SubscribeFn<T>,\n): AsyncGenerator<T, void, unknown> {\n const queue: T[] = [];\n let unsubscribe: UnsubscribeFn | null = null;\n let resolveNext: ((value: IteratorResult<T>) => void) | null = null;\n\n const unsubscribeFunc = subscribe((value) => {\n if (resolveNext) {\n // If there's a pending promise, resolve it immediately\n resolveNext({ value, done: false });\n resolveNext = null;\n } else {\n // Otherwise, queue the value\n queue.push(value);\n }\n });\n\n // Store unsubscribe function if one was returned\n if (typeof unsubscribeFunc === \"function\") {\n unsubscribe = unsubscribeFunc;\n }\n\n return (async function* () {\n try {\n while (true) {\n if (queue.length > 0) {\n // Yield queued values\n yield queue.shift()!;\n } else {\n // Wait for the next value\n yield await new Promise<T>((resolve) => {\n resolveNext = (result) => {\n if (!result.done) {\n resolve(result.value);\n }\n };\n });\n }\n }\n } finally {\n // Clean up subscription on iterator termination\n if (unsubscribe) {\n unsubscribe();\n }\n }\n })();\n}\n\n/**\n * Waits for an async iterator to yield a value that meets a condition.\n *\n * @template T The type of values produced by the iterator.\n * @param iterable The async iterable to wait for.\n * @param condition A function that checks if a value meets the condition.\n * @param options Optional configuration options.\n * @returns A promise that resolves to the first value that meets the condition.\n */\nexport async function waitForAsyncIterator<T>(\n iterable: AsyncIterable<T>,\n condition: (value: T) => boolean,\n options: { timeout?: number } = {},\n): Promise<T> {\n const { timeout = 1000 } = options;\n\n // Create a timeout promise that rejects after the specified time\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`waitForAsyncIterator: Timeout after ${timeout}ms`));\n }, timeout);\n });\n\n // Create a promise that resolves when the condition is met\n const iteratorPromise = (async () => {\n for await (const value of iterable) {\n if (condition(value)) {\n return value;\n }\n }\n throw new Error(\"waitForAsyncIterator: Iterator completed without meeting condition\");\n })();\n\n // Race between the timeout and the iterator\n return Promise.race([iteratorPromise, timeoutPromise]);\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,gCACd,WACkC;CAClC,MAAMA,QAAa,EAAE;CACrB,IAAIC,cAAoC;CACxC,IAAIC,cAA2D;CAE/D,MAAM,kBAAkB,WAAW,UAAU;AAC3C,MAAI,aAAa;AAEf,eAAY;IAAE;IAAO,MAAM;IAAO,CAAC;AACnC,iBAAc;QAGd,OAAM,KAAK,MAAM;GAEnB;AAGF,KAAI,OAAO,oBAAoB,WAC7B,eAAc;AAGhB,SAAQ,mBAAmB;AACzB,MAAI;AACF,UAAO,KACL,KAAI,MAAM,SAAS,EAEjB,OAAM,MAAM,OAAO;OAGnB,OAAM,MAAM,IAAI,SAAY,YAAY;AACtC,mBAAe,WAAW;AACxB,SAAI,CAAC,OAAO,KACV,SAAQ,OAAO,MAAM;;KAGzB;YAGE;AAER,OAAI,YACF,cAAa;;KAGf"}
@@ -0,0 +1,49 @@
1
+ //#region src/util/content-type.ts
2
+ /**
3
+ * Parses a content-type header string into its components
4
+ *
5
+ * @param contentType - The content-type header value to parse
6
+ * @returns A ParsedContentType object or null if the input is invalid
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const { type, subtype, mediaType, parameters }
11
+ * = parseContentType("application/json; charset=utf-8");
12
+ * console.assert(type === "application");
13
+ * console.assert(subtype === "json");
14
+ * console.assert(mediaType === "application/json");
15
+ * console.assert(parameters["charset"] === "utf-8");
16
+ */
17
+ function parseContentType(contentType) {
18
+ if (!contentType || typeof contentType !== "string") return null;
19
+ const trimmed = contentType.trim();
20
+ if (!trimmed) return null;
21
+ const parts = trimmed.split(";").map((part) => part.trim());
22
+ const mediaType = parts[0];
23
+ if (!mediaType) return null;
24
+ const typeParts = mediaType.split("/");
25
+ if (typeParts.length !== 2) return null;
26
+ const [type, subtype] = typeParts.map((part) => part.trim().toLowerCase());
27
+ if (!type || !subtype) return null;
28
+ const parameters = {};
29
+ for (let i = 1; i < parts.length; i++) {
30
+ const param = parts[i];
31
+ const equalIndex = param.indexOf("=");
32
+ if (equalIndex > 0) {
33
+ const key = param.slice(0, equalIndex).trim().toLowerCase();
34
+ let value = param.slice(equalIndex + 1).trim();
35
+ if (value.startsWith("\"") && value.endsWith("\"")) value = value.slice(1, -1);
36
+ if (key) parameters[key] = value;
37
+ }
38
+ }
39
+ return {
40
+ type,
41
+ subtype,
42
+ mediaType: `${type}/${subtype}`,
43
+ parameters
44
+ };
45
+ }
46
+
47
+ //#endregion
48
+ export { parseContentType };
49
+ //# sourceMappingURL=content-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-type.js","names":["parameters: Record<string, string>"],"sources":["../../src/util/content-type.ts"],"sourcesContent":["/**\n * Represents a parsed content-type header\n */\nexport interface ParsedContentType {\n /** The main type (e.g., \"application\", \"text\", \"image\") */\n type: string;\n /** The subtype (e.g., \"json\", \"html\", \"png\") */\n subtype: string;\n /** The full media type (e.g., \"application/json\") */\n mediaType: string;\n /** Additional parameters like charset, boundary, etc. */\n parameters: Record<string, string>;\n}\n\n/**\n * Parses a content-type header string into its components\n *\n * @param contentType - The content-type header value to parse\n * @returns A ParsedContentType object or null if the input is invalid\n *\n * @example\n * ```ts\n * const { type, subtype, mediaType, parameters }\n * = parseContentType(\"application/json; charset=utf-8\");\n * console.assert(type === \"application\");\n * console.assert(subtype === \"json\");\n * console.assert(mediaType === \"application/json\");\n * console.assert(parameters[\"charset\"] === \"utf-8\");\n */\nexport function parseContentType(contentType: string | null | undefined): ParsedContentType | null {\n if (!contentType || typeof contentType !== \"string\") {\n return null;\n }\n\n const trimmed = contentType.trim();\n if (!trimmed) {\n return null;\n }\n\n const parts = trimmed.split(\";\").map((part) => part.trim());\n const mediaType = parts[0];\n\n if (!mediaType) {\n return null;\n }\n\n const typeParts = mediaType.split(\"/\");\n if (typeParts.length !== 2) {\n return null;\n }\n\n const [type, subtype] = typeParts.map((part) => part.trim().toLowerCase());\n\n if (!type || !subtype) {\n return null;\n }\n\n const parameters: Record<string, string> = {};\n\n for (let i = 1; i < parts.length; i++) {\n const param = parts[i];\n const equalIndex = param.indexOf(\"=\");\n\n if (equalIndex > 0) {\n const key = param.slice(0, equalIndex).trim().toLowerCase();\n let value = param.slice(equalIndex + 1).trim();\n\n if (value.startsWith('\"') && value.endsWith('\"')) {\n value = value.slice(1, -1);\n }\n\n if (key) {\n parameters[key] = value;\n }\n }\n }\n\n return {\n type,\n subtype,\n mediaType: `${type}/${subtype}`,\n parameters,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA6BA,SAAgB,iBAAiB,aAAkE;AACjG,KAAI,CAAC,eAAe,OAAO,gBAAgB,SACzC,QAAO;CAGT,MAAM,UAAU,YAAY,MAAM;AAClC,KAAI,CAAC,QACH,QAAO;CAGT,MAAM,QAAQ,QAAQ,MAAM,IAAI,CAAC,KAAK,SAAS,KAAK,MAAM,CAAC;CAC3D,MAAM,YAAY,MAAM;AAExB,KAAI,CAAC,UACH,QAAO;CAGT,MAAM,YAAY,UAAU,MAAM,IAAI;AACtC,KAAI,UAAU,WAAW,EACvB,QAAO;CAGT,MAAM,CAAC,MAAM,WAAW,UAAU,KAAK,SAAS,KAAK,MAAM,CAAC,aAAa,CAAC;AAE1E,KAAI,CAAC,QAAQ,CAAC,QACZ,QAAO;CAGT,MAAMA,aAAqC,EAAE;AAE7C,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,QAAQ,MAAM;EACpB,MAAM,aAAa,MAAM,QAAQ,IAAI;AAErC,MAAI,aAAa,GAAG;GAClB,MAAM,MAAM,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,aAAa;GAC3D,IAAI,QAAQ,MAAM,MAAM,aAAa,EAAE,CAAC,MAAM;AAE9C,OAAI,MAAM,WAAW,KAAI,IAAI,MAAM,SAAS,KAAI,CAC9C,SAAQ,MAAM,MAAM,GAAG,GAAG;AAG5B,OAAI,IACF,YAAW,OAAO;;;AAKxB,QAAO;EACL;EACA;EACA,WAAW,GAAG,KAAK,GAAG;EACtB;EACD"}
@@ -0,0 +1,31 @@
1
+ //#region src/util/nanostores.ts
2
+ /**
3
+ * Normalizes a value that could be a plain value, an Atom, or a Vue Ref to a plain value.
4
+ */
5
+ function unwrapAtom(value) {
6
+ if (value && typeof value === "object" && "get" in value && typeof value.get === "function") return value.get();
7
+ return value;
8
+ }
9
+ /**
10
+ * Normalizes an object where values can be plain values, Atoms, or Vue Refs.
11
+ * Returns a new object with all values normalized to plain values.
12
+ */
13
+ function unwrapObject(params) {
14
+ if (!params) return;
15
+ return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, unwrapAtom(value)]));
16
+ }
17
+ function isReadableAtom(value) {
18
+ if (!value) return false;
19
+ if (typeof value !== "object" || value === null) return false;
20
+ if (!("get" in value) || typeof value.get !== "function") return false;
21
+ if (!("lc" in value) || typeof value.lc !== "number") return false;
22
+ if (!("notify" in value) || typeof value.notify !== "function") return false;
23
+ if (!("off" in value) || typeof value.off !== "function") return false;
24
+ if (!("subscribe" in value) || typeof value.subscribe !== "function") return false;
25
+ if (!("value" in value)) return false;
26
+ return true;
27
+ }
28
+
29
+ //#endregion
30
+ export { isReadableAtom, unwrapObject };
31
+ //# sourceMappingURL=nanostores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nanostores.js","names":[],"sources":["../../src/util/nanostores.ts"],"sourcesContent":["import type { ReadableAtom } from \"nanostores\";\n\ntype MaybeAtom<T> = T | ReadableAtom<T>;\n\n/**\n * Normalizes a value that could be a plain value, an Atom, or a Vue Ref to a plain value.\n */\nexport function unwrapAtom<T>(value: MaybeAtom<T>): T {\n // Check if it's an Atom (has .get method)\n if (value && typeof value === \"object\" && \"get\" in value && typeof value.get === \"function\") {\n return value.get();\n }\n\n return value as T;\n}\n\n/**\n * Normalizes an object where values can be plain values, Atoms, or Vue Refs.\n * Returns a new object with all values normalized to plain values.\n */\nexport function unwrapObject<T>(\n params: Record<string, MaybeAtom<T>> | undefined,\n): Record<string, T> | undefined {\n if (!params) {\n return undefined;\n }\n\n return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, unwrapAtom(value)]));\n}\n\nexport function isReadableAtom(value: unknown): value is ReadableAtom<unknown> {\n if (!value) {\n return false;\n }\n\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n if (!(\"get\" in value) || typeof value.get !== \"function\") {\n return false;\n }\n\n if (!(\"lc\" in value) || typeof value.lc !== \"number\") {\n return false;\n }\n\n if (!(\"notify\" in value) || typeof value.notify !== \"function\") {\n return false;\n }\n\n if (!(\"off\" in value) || typeof value.off !== \"function\") {\n return false;\n }\n\n if (!(\"subscribe\" in value) || typeof value.subscribe !== \"function\") {\n return false;\n }\n\n if (!(\"value\" in value)) {\n return false;\n }\n\n return true;\n}\n"],"mappings":";;;;AAOA,SAAgB,WAAc,OAAwB;AAEpD,KAAI,SAAS,OAAO,UAAU,YAAY,SAAS,SAAS,OAAO,MAAM,QAAQ,WAC/E,QAAO,MAAM,KAAK;AAGpB,QAAO;;;;;;AAOT,SAAgB,aACd,QAC+B;AAC/B,KAAI,CAAC,OACH;AAGF,QAAO,OAAO,YAAY,OAAO,QAAQ,OAAO,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,WAAW,MAAM,CAAC,CAAC,CAAC;;AAGnG,SAAgB,eAAe,OAAgD;AAC7E,KAAI,CAAC,MACH,QAAO;AAGT,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;AAGT,KAAI,EAAE,SAAS,UAAU,OAAO,MAAM,QAAQ,WAC5C,QAAO;AAGT,KAAI,EAAE,QAAQ,UAAU,OAAO,MAAM,OAAO,SAC1C,QAAO;AAGT,KAAI,EAAE,YAAY,UAAU,OAAO,MAAM,WAAW,WAClD,QAAO;AAGT,KAAI,EAAE,SAAS,UAAU,OAAO,MAAM,QAAQ,WAC5C,QAAO;AAGT,KAAI,EAAE,eAAe,UAAU,OAAO,MAAM,cAAc,WACxD,QAAO;AAGT,KAAI,EAAE,WAAW,OACf,QAAO;AAGT,QAAO"}
@@ -44,5 +44,5 @@ async function getFinalStoreValues() {
44
44
  }
45
45
 
46
46
  //#endregion
47
- export { getInitialData as a, getFinalStoreValues as i, addStore as n, hydrateFromWindow as o, cleanStores as r, SSR_ENABLED as t };
48
- //# sourceMappingURL=ssr-BByDVfFD.js.map
47
+ export { SSR_ENABLED, addStore, cleanStores, getFinalStoreValues, getInitialData, hydrateFromWindow };
48
+ //# sourceMappingURL=ssr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr.js","names":["stores: FetcherStore[]","clientInitialData: Map<string, unknown> | undefined","stores"],"sources":["../../src/util/ssr.ts"],"sourcesContent":["import { allTasks } from \"nanostores\";\nimport type { FetcherStore } from \"@nanostores/query\";\n\nlet stores: FetcherStore[] = [];\n\nexport const SSR_ENABLED = false;\n\nexport function getStores() {\n return stores;\n}\n\nexport function addStore(store: FetcherStore) {\n stores.push(store);\n}\n\nexport function cleanStores() {\n stores = [];\n}\n\n// Client side\ndeclare global {\n interface Window {\n __FRAGNO_INITIAL_DATA__?: [string, unknown][];\n }\n}\n\nlet clientInitialData: Map<string, unknown> | undefined;\n\nexport function hydrateFromWindow() {\n if (typeof window !== \"undefined\" && window.__FRAGNO_INITIAL_DATA__) {\n clientInitialData = new Map(window.__FRAGNO_INITIAL_DATA__);\n delete window.__FRAGNO_INITIAL_DATA__;\n console.warn(\"hydrateFromWindow\", {\n clientInitialData: Array.from(clientInitialData.entries()),\n });\n }\n}\n\nexport function getInitialData(key: string): unknown | undefined {\n if (clientInitialData?.has(key)) {\n const data = clientInitialData.get(key);\n clientInitialData.delete(key);\n return data;\n }\n return undefined;\n}\n\nfunction listenToStores(): void {\n for (const store of getStores()) {\n // By calling `listen`, we trigger the fetcher function of the store.\n // This will start the data fetching process on the server.\n // We don't need to do anything with the return value of `listen`, as we\n // are only interested in starting the data fetching.\n store.listen(() => {});\n }\n}\n\n// Server side\nexport async function getFinalStoreValues(): Promise<Map<string, unknown>> {\n listenToStores();\n await allTasks();\n\n const stores = getStores();\n const storesInitialValue = new Map<string, unknown>();\n\n for (const store of stores) {\n const value = store.get();\n if (!value || !store.key || value.loading) {\n continue;\n }\n storesInitialValue.set(store.key, value.data);\n }\n\n return storesInitialValue;\n}\n"],"mappings":";;;AAGA,IAAIA,SAAyB,EAAE;AAE/B,MAAa,cAAc;AAE3B,SAAgB,YAAY;AAC1B,QAAO;;AAGT,SAAgB,SAAS,OAAqB;AAC5C,QAAO,KAAK,MAAM;;AAGpB,SAAgB,cAAc;AAC5B,UAAS,EAAE;;AAUb,IAAIC;AAEJ,SAAgB,oBAAoB;AAClC,KAAI,OAAO,WAAW,eAAe,OAAO,yBAAyB;AACnE,sBAAoB,IAAI,IAAI,OAAO,wBAAwB;AAC3D,SAAO,OAAO;AACd,UAAQ,KAAK,qBAAqB,EAChC,mBAAmB,MAAM,KAAK,kBAAkB,SAAS,CAAC,EAC3D,CAAC;;;AAIN,SAAgB,eAAe,KAAkC;AAC/D,KAAI,mBAAmB,IAAI,IAAI,EAAE;EAC/B,MAAM,OAAO,kBAAkB,IAAI,IAAI;AACvC,oBAAkB,OAAO,IAAI;AAC7B,SAAO;;;AAKX,SAAS,iBAAuB;AAC9B,MAAK,MAAM,SAAS,WAAW,CAK7B,OAAM,aAAa,GAAG;;AAK1B,eAAsB,sBAAqD;AACzE,iBAAgB;AAChB,OAAM,UAAU;CAEhB,MAAMC,WAAS,WAAW;CAC1B,MAAM,qCAAqB,IAAI,KAAsB;AAErD,MAAK,MAAM,SAASA,UAAQ;EAC1B,MAAM,QAAQ,MAAM,KAAK;AACzB,MAAI,CAAC,SAAS,CAAC,MAAM,OAAO,MAAM,QAChC;AAEF,qBAAmB,IAAI,MAAM,KAAK,MAAM,KAAK;;AAG/C,QAAO"}
@@ -0,0 +1,8 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+
3
+ //#region src/util/types-util.d.ts
4
+ type InferOr<T, U> = T extends NonNullable<StandardSchemaV1> ? StandardSchemaV1.InferOutput<T> : T extends undefined ? U : U;
5
+ type InferOrUnknown<T> = InferOr<T, unknown>;
6
+ //#endregion
7
+ export { InferOr, InferOrUnknown };
8
+ //# sourceMappingURL=types-util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-util.d.ts","names":[],"sources":["../../src/util/types-util.ts"],"sourcesContent":[],"mappings":";;;KAEY,gBACV,UAAU,YAAY,oBAClB,gBAAA,CAAiB,YAAY,KAC7B,sBACE,IACA;KAEI,oBAAoB,QAAQ"}
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@fragno-dev/core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "exports": {
5
5
  ".": {
6
6
  "development": "./src/mod.ts",
7
+ "browser": "./dist/mod-client.js",
7
8
  "types": "./dist/mod.d.ts",
8
9
  "default": "./dist/mod.js"
9
10
  },
@@ -17,21 +18,26 @@
17
18
  "types": "./dist/test/test.d.ts",
18
19
  "default": "./dist/test/test.js"
19
20
  },
20
- "./api/fragment-builder": {
21
- "development": "./src/api/fragment-builder.ts",
22
- "types": "./dist/api/fragment-builder.d.ts",
23
- "default": "./dist/api/fragment-builder.js"
24
- },
25
- "./api/fragment-instantiation": {
26
- "development": "./src/api/fragment-instantiation.ts",
27
- "types": "./dist/api/fragment-instantiation.d.ts",
28
- "default": "./dist/api/fragment-instantiation.js"
29
- },
30
- "./api/route": {
21
+ "./route": {
31
22
  "development": "./src/api/route.ts",
32
23
  "types": "./dist/api/route.d.ts",
33
24
  "default": "./dist/api/route.js"
34
25
  },
26
+ "./request": {
27
+ "development": "./src/request/request.ts",
28
+ "types": "./dist/request/request.d.ts",
29
+ "default": "./dist/request/request.js"
30
+ },
31
+ "./internal/request-context-storage": {
32
+ "development": "./src/api/request-context-storage.ts",
33
+ "types": "./dist/api/request-context-storage.d.ts",
34
+ "default": "./dist/api/request-context-storage.js"
35
+ },
36
+ "./internal/symbols": {
37
+ "development": "./src/internal/symbols.ts",
38
+ "types": "./dist/internal/symbols.d.ts",
39
+ "default": "./dist/internal/symbols.js"
40
+ },
35
41
  "./client": {
36
42
  "development": "./src/client/client.ts",
37
43
  "types": "./dist/client/client.d.ts",
@@ -73,6 +79,7 @@
73
79
  "module": "./dist/mod.js",
74
80
  "types": "./dist/mod.d.ts",
75
81
  "type": "module",
82
+ "sideEffects": false,
76
83
  "dependencies": {
77
84
  "@nanostores/query": "^0.3.4",
78
85
  "@nanostores/solid": "^1.1.1",
package/src/api/api.ts CHANGED
@@ -23,6 +23,8 @@ export type ValidPath<T extends string = string> = T extends `/${infer Rest}`
23
23
  : T
24
24
  : PathError<T, "Path must start with '/'.">; // Excludes paths not starting with "/"
25
25
 
26
+ export interface RequestThisContext {}
27
+
26
28
  export interface FragnoRouteConfig<
27
29
  TMethod extends HTTPMethod,
28
30
  TPath extends string,
@@ -30,6 +32,7 @@ export interface FragnoRouteConfig<
30
32
  TOutputSchema extends StandardSchemaV1 | undefined,
31
33
  TErrorCode extends string = string,
32
34
  TQueryParameters extends string = string,
35
+ TThisContext extends RequestThisContext = RequestThisContext,
33
36
  > {
34
37
  method: TMethod;
35
38
  path: TPath;
@@ -38,6 +41,7 @@ export interface FragnoRouteConfig<
38
41
  errorCodes?: readonly TErrorCode[];
39
42
  queryParameters?: readonly TQueryParameters[];
40
43
  handler(
44
+ this: TThisContext,
41
45
  inputCtx: RequestInputContext<TPath, TInputSchema>,
42
46
  outputCtx: RequestOutputContext<TOutputSchema, TErrorCode>,
43
47
  ): Promise<Response>;
@@ -50,6 +54,7 @@ export function addRoute<
50
54
  TOutputSchema extends StandardSchemaV1 | undefined,
51
55
  TErrorCode extends string = string,
52
56
  TQueryParameters extends string = string,
57
+ TThisContext extends RequestThisContext = RequestThisContext,
53
58
  >(
54
59
  route: FragnoRouteConfig<
55
60
  TMethod,
@@ -57,9 +62,18 @@ export function addRoute<
57
62
  undefined,
58
63
  TOutputSchema,
59
64
  TErrorCode,
60
- TQueryParameters
65
+ TQueryParameters,
66
+ TThisContext
61
67
  > & { inputSchema?: undefined },
62
- ): FragnoRouteConfig<TMethod, TPath, undefined, TOutputSchema, TErrorCode, TQueryParameters>;
68
+ ): FragnoRouteConfig<
69
+ TMethod,
70
+ TPath,
71
+ undefined,
72
+ TOutputSchema,
73
+ TErrorCode,
74
+ TQueryParameters,
75
+ TThisContext
76
+ >;
63
77
 
64
78
  // Overload for routes with inputSchema
65
79
  export function addRoute<
@@ -69,6 +83,7 @@ export function addRoute<
69
83
  TOutputSchema extends StandardSchemaV1 | undefined,
70
84
  TErrorCode extends string = string,
71
85
  TQueryParameters extends string = string,
86
+ TThisContext extends RequestThisContext = RequestThisContext,
72
87
  >(
73
88
  route: FragnoRouteConfig<
74
89
  TMethod,
@@ -76,11 +91,20 @@ export function addRoute<
76
91
  TInputSchema,
77
92
  TOutputSchema,
78
93
  TErrorCode,
79
- TQueryParameters
94
+ TQueryParameters,
95
+ TThisContext
80
96
  > & {
81
97
  inputSchema: TInputSchema;
82
98
  },
83
- ): FragnoRouteConfig<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters>;
99
+ ): FragnoRouteConfig<
100
+ TMethod,
101
+ TPath,
102
+ TInputSchema,
103
+ TOutputSchema,
104
+ TErrorCode,
105
+ TQueryParameters,
106
+ TThisContext
107
+ >;
84
108
 
85
109
  // Implementation
86
110
  export function addRoute<
@@ -90,6 +114,7 @@ export function addRoute<
90
114
  TOutputSchema extends StandardSchemaV1 | undefined,
91
115
  TErrorCode extends string = string,
92
116
  TQueryParameters extends string = string,
117
+ TThisContext extends RequestThisContext = RequestThisContext,
93
118
  >(
94
119
  route: FragnoRouteConfig<
95
120
  TMethod,
@@ -97,11 +122,21 @@ export function addRoute<
97
122
  TInputSchema,
98
123
  TOutputSchema,
99
124
  TErrorCode,
100
- TQueryParameters
125
+ TQueryParameters,
126
+ TThisContext
101
127
  >,
102
- ): FragnoRouteConfig<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> {
128
+ ): FragnoRouteConfig<
129
+ TMethod,
130
+ TPath,
131
+ TInputSchema,
132
+ TOutputSchema,
133
+ TErrorCode,
134
+ TQueryParameters,
135
+ TThisContext
136
+ > {
103
137
  return route;
104
138
  }
105
139
 
106
140
  export { FragnoApiError, FragnoApiValidationError } from "./error";
107
141
  export type { RouteHandlerInputOptions } from "./route-handler-input-options";
142
+ export type { FragnoPublicConfig } from "./shared-types";
@@ -0,0 +1,42 @@
1
+ // Type helper to remove 'this' parameter from functions
2
+ type OmitThisParameter<T> = T extends (this: infer _This, ...args: infer A) => infer R
3
+ ? (...args: A) => R
4
+ : T;
5
+
6
+ // Recursively remove 'this' parameter from all functions in an object
7
+ export type BoundServices<T> = {
8
+ [K in keyof T]: T[K] extends (...args: never[]) => unknown
9
+ ? OmitThisParameter<T[K]>
10
+ : T[K] extends Record<string, unknown>
11
+ ? BoundServices<T[K]>
12
+ : T[K];
13
+ };
14
+
15
+ /**
16
+ * Bind all functions in a service object to a specific context.
17
+ * This allows services to use `this` to access the context.
18
+ *
19
+ * @param services - The service object to bind
20
+ * @param context - The context to bind to (e.g., { getUnitOfWork })
21
+ * @returns A new object with all functions bound to the context
22
+ */
23
+ export function bindServicesToContext<T extends object, TContext extends object>(
24
+ services: T,
25
+ context: TContext,
26
+ ): BoundServices<T> {
27
+ const bound = {} as BoundServices<T>;
28
+
29
+ for (const [key, value] of Object.entries(services)) {
30
+ if (typeof value === "function") {
31
+ // Bind function to the provided context
32
+ bound[key as keyof T] = value.bind(context) as BoundServices<T>[keyof T];
33
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
34
+ // Recursively bind nested service objects
35
+ bound[key as keyof T] = bindServicesToContext(value, context) as BoundServices<T>[keyof T];
36
+ } else {
37
+ bound[key as keyof T] = value as BoundServices<T>[keyof T];
38
+ }
39
+ }
40
+
41
+ return bound;
42
+ }