@fragno-dev/core 0.1.8 → 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 (176) hide show
  1. package/.turbo/turbo-build.log +131 -56
  2. package/CHANGELOG.md +13 -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 -3
  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/error.js +48 -0
  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 -2
  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 -2
  71. package/dist/client/client.d.ts.map +1 -0
  72. package/dist/client/client.js +397 -5
  73. package/dist/client/client.js.map +1 -0
  74. package/dist/client/client.svelte.d.ts +5 -2
  75. package/dist/client/client.svelte.d.ts.map +1 -1
  76. package/dist/client/client.svelte.js +1 -4
  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 -2
  83. package/dist/client/react.d.ts.map +1 -1
  84. package/dist/client/react.js +3 -4
  85. package/dist/client/react.js.map +1 -1
  86. package/dist/client/solid.d.ts +5 -2
  87. package/dist/client/solid.d.ts.map +1 -1
  88. package/dist/client/solid.js +2 -4
  89. package/dist/client/solid.js.map +1 -1
  90. package/dist/client/vanilla.d.ts +5 -2
  91. package/dist/client/vanilla.d.ts.map +1 -1
  92. package/dist/client/vanilla.js +2 -42
  93. package/dist/client/vanilla.js.map +1 -1
  94. package/dist/client/vue.d.ts +5 -2
  95. package/dist/client/vue.d.ts.map +1 -1
  96. package/dist/client/vue.js +1 -4
  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 -2
  110. package/dist/mod.js +4 -4
  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 -34
  114. package/dist/test/test.d.ts.map +1 -1
  115. package/dist/test/test.js +75 -42
  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-kyKI7pqH.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 +1 -5
  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 +454 -189
  136. package/src/api/request-context-storage.ts +64 -0
  137. package/src/api/request-middleware.test.ts +301 -228
  138. package/src/api/route.test.ts +12 -36
  139. package/src/api/route.ts +167 -155
  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 +22 -15
  154. package/src/request/request.ts +8 -0
  155. package/src/test/test.test.ts +189 -375
  156. package/src/test/test.ts +186 -152
  157. package/tsdown.config.ts +8 -5
  158. package/dist/api/fragment-builder.d.ts +0 -2
  159. package/dist/api/fragment-builder.js +0 -3
  160. package/dist/api/fragment-instantiation.d.ts +0 -2
  161. package/dist/api/fragment-instantiation.js +0 -4
  162. package/dist/api-BFrUCIsF.d.ts +0 -963
  163. package/dist/api-BFrUCIsF.d.ts.map +0 -1
  164. package/dist/client-DAFHcKqA.js +0 -782
  165. package/dist/client-DAFHcKqA.js.map +0 -1
  166. package/dist/fragment-builder-Boh2vNHq.js +0 -108
  167. package/dist/fragment-builder-Boh2vNHq.js.map +0 -1
  168. package/dist/fragment-instantiation-DUT-HLl1.js +0 -898
  169. package/dist/fragment-instantiation-DUT-HLl1.js.map +0 -1
  170. package/dist/route-C4CyNHkC.js +0 -26
  171. package/dist/route-C4CyNHkC.js.map +0 -1
  172. package/dist/ssr-kyKI7pqH.js.map +0 -1
  173. package/src/api/fragment-builder.ts +0 -518
  174. package/src/api/fragment-instantiation.test.ts +0 -702
  175. package/src/api/fragment-instantiation.ts +0 -766
  176. package/src/api/fragment.test.ts +0 -585
@@ -0,0 +1,40 @@
1
+ //#region src/util/async.ts
2
+ /**
3
+ * Creates an async iterator from a subscribe function that follows the observable pattern.
4
+ *
5
+ * @template T The type of values produced by the store.
6
+ * @param subscribe A function that subscribes to store updates. It receives a callback to be
7
+ * called on each update, and returns an unsubscribe function.
8
+ * @returns An async generator that yields store values as they are produced.
9
+ */
10
+ function createAsyncIteratorFromCallback(subscribe) {
11
+ const queue = [];
12
+ let unsubscribe = null;
13
+ let resolveNext = null;
14
+ const unsubscribeFunc = subscribe((value) => {
15
+ if (resolveNext) {
16
+ resolveNext({
17
+ value,
18
+ done: false
19
+ });
20
+ resolveNext = null;
21
+ } else queue.push(value);
22
+ });
23
+ if (typeof unsubscribeFunc === "function") unsubscribe = unsubscribeFunc;
24
+ return (async function* () {
25
+ try {
26
+ while (true) if (queue.length > 0) yield queue.shift();
27
+ else yield await new Promise((resolve) => {
28
+ resolveNext = (result) => {
29
+ if (!result.done) resolve(result.value);
30
+ };
31
+ });
32
+ } finally {
33
+ if (unsubscribe) unsubscribe();
34
+ }
35
+ })();
36
+ }
37
+
38
+ //#endregion
39
+ export { createAsyncIteratorFromCallback };
40
+ //# sourceMappingURL=async.js.map
@@ -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-kyKI7pqH.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.8",
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,10 +23,6 @@ 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
- /**
27
- * Base ServiceContext interface. Can be augmented by packages like @fragno-dev/db.
28
- */
29
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
30
26
  export interface RequestThisContext {}
31
27
 
32
28
  export interface FragnoRouteConfig<
@@ -143,4 +139,4 @@ export function addRoute<
143
139
 
144
140
  export { FragnoApiError, FragnoApiValidationError } from "./error";
145
141
  export type { RouteHandlerInputOptions } from "./route-handler-input-options";
146
- export { instantiateFragment, FragmentInstantiationBuilder } from "./fragment-instantiation";
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
+ }