@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
@@ -1,6 +1,6 @@
1
1
  import { test, expect, expectTypeOf, describe } from "vitest";
2
- import { defineRoute, defineRoutes, type ExtractFragmentServices } from "./route";
3
- import { defineFragment } from "./fragment-builder";
2
+ import { defineRoute, defineRoutes } from "./route";
3
+ import { defineFragment } from "./fragment-definition-builder";
4
4
  import { z } from "zod";
5
5
 
6
6
  describe("defineRoute", () => {
@@ -176,43 +176,18 @@ describe("defineRoute", () => {
176
176
  });
177
177
  });
178
178
 
179
- describe("ExtractFragmentServices", () => {
180
- test("extracts services from fragment with .providesService()", () => {
179
+ // ExtractFragmentServices tests removed - use ExtractFragmentServices instead
180
+
181
+ describe("defineRoutes", () => {
182
+ test("defineRoutes extracts services correctly for route factory", () => {
181
183
  const fragment = defineFragment<{}>("test-fragment")
182
- .withDependencies(() => ({ dep: "value" }))
183
- .providesService(({ defineService }) =>
184
+ .providesBaseService(({ defineService }) =>
184
185
  defineService({
185
186
  getUserById: async (id: string) => ({ id, name: "John" }),
186
187
  createUser: async (name: string) => ({ id: "123", name }),
187
188
  }),
188
- );
189
-
190
- type Services = ExtractFragmentServices<typeof fragment>;
191
-
192
- expectTypeOf<Services>().toMatchObjectType<{
193
- getUserById: (id: string) => Promise<{ id: string; name: string }>;
194
- createUser: (name: string) => Promise<{ id: string; name: string }>;
195
- }>();
196
- });
197
-
198
- test("ExtractFragmentServices returns never for fragment without services", () => {
199
- const fragment = defineFragment<{}>("test-fragment").withDependencies(() => ({ dep: "value" }));
200
-
201
- type Services = ExtractFragmentServices<typeof fragment>;
202
-
203
- // Fragment with no services should have empty services object
204
- expectTypeOf<Services>().toEqualTypeOf<{}>();
205
- });
206
- });
207
-
208
- describe("defineRoutes", () => {
209
- test("defineRoutes extracts services correctly for route factory", () => {
210
- const fragment = defineFragment<{}>("test-fragment").providesService(({ defineService }) =>
211
- defineService({
212
- getUserById: async (id: string) => ({ id, name: "John" }),
213
- createUser: async (name: string) => ({ id: "123", name }),
214
- }),
215
- );
189
+ )
190
+ .build();
216
191
 
217
192
  const routeFactory = defineRoutes(fragment).create(({ services, config, deps }) => {
218
193
  // Type check that services are properly extracted
@@ -248,13 +223,14 @@ describe("defineRoutes", () => {
248
223
  .withDependencies(({ config }) => ({
249
224
  authClient: { apiKey: config.apiKey },
250
225
  }))
251
- .providesService(({ deps, defineService }) =>
226
+ .providesBaseService(({ deps, defineService }) =>
252
227
  defineService({
253
228
  validateToken: async (_token: string) => {
254
229
  return { valid: true, apiKey: deps.authClient.apiKey };
255
230
  },
256
231
  }),
257
- );
232
+ )
233
+ .build();
258
234
 
259
235
  const routeFactory = defineRoutes(fragment).create(({ services, config, deps }) => {
260
236
  // Type check all context properties
package/src/api/route.ts CHANGED
@@ -1,25 +1,24 @@
1
+ // oxlint-disable no-explicit-any
2
+
1
3
  import type { StandardSchemaV1 } from "@standard-schema/spec";
2
4
  import type { FragnoRouteConfig, HTTPMethod, RequestThisContext } from "./api";
3
- import type { FragmentDefinition } from "./fragment-builder";
5
+ import type { FragmentDefinition } from "./fragment-definition-builder";
6
+ import type { BoundServices } from "./bind-services";
4
7
 
5
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
8
  export type AnyFragnoRouteConfig = FragnoRouteConfig<HTTPMethod, string, any, any, any, any, any>;
7
9
 
8
- export type AnyFragmentBuilder = {
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- readonly definition: FragmentDefinition<any, any, any, any, any, any, any>;
11
- };
12
-
13
- export interface RouteFactoryContext<TConfig, TDeps, TServices> {
10
+ export interface RouteFactoryContext<TConfig, TDeps, TServices, TServiceDeps = {}> {
14
11
  config: TConfig;
15
12
  deps: TDeps;
16
13
  services: TServices;
14
+ serviceDeps: TServiceDeps;
17
15
  }
18
16
 
19
17
  export type RouteFactory<
20
18
  TConfig,
21
19
  TDeps,
22
20
  TServices,
21
+ TServiceDeps,
23
22
  TRoutes extends readonly FragnoRouteConfig<
24
23
  HTTPMethod,
25
24
  string,
@@ -29,32 +28,39 @@ export type RouteFactory<
29
28
  string,
30
29
  RequestThisContext
31
30
  >[],
32
- > = (context: RouteFactoryContext<TConfig, TDeps, TServices>) => TRoutes;
31
+ > = (context: RouteFactoryContext<TConfig, TDeps, TServices, TServiceDeps>) => TRoutes;
33
32
 
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- export type AnyRouteOrFactory = AnyFragnoRouteConfig | RouteFactory<any, any, any, any>;
33
+ /**
34
+ * @internal
35
+ */
36
+ export type AnyRouteOrFactory = AnyFragnoRouteConfig | RouteFactory<any, any, any, any, any>;
36
37
 
38
+ /**
39
+ * @internal
40
+ */
37
41
  export type FlattenRouteFactories<T extends readonly AnyRouteOrFactory[]> = T extends readonly [
38
42
  infer First,
39
43
  ...infer Rest extends readonly AnyRouteOrFactory[],
40
44
  ]
41
- ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
- First extends RouteFactory<any, any, any, infer TRoutes>
45
+ ? First extends RouteFactory<any, any, any, any, infer TRoutes>
43
46
  ? [...TRoutes, ...FlattenRouteFactories<Rest>]
44
47
  : [First, ...FlattenRouteFactories<Rest>]
45
48
  : [];
46
49
 
47
- // Helper to resolve route factories into routes
50
+ /**
51
+ * Helper to resolve route factories into routes
52
+ * @internal
53
+ */
48
54
  export function resolveRouteFactories<
49
55
  TConfig,
50
56
  TDeps,
51
57
  TServices,
58
+ TServiceDeps,
52
59
  const TRoutesOrFactories extends readonly AnyRouteOrFactory[],
53
60
  >(
54
- context: RouteFactoryContext<TConfig, TDeps, TServices>,
61
+ context: RouteFactoryContext<TConfig, TDeps, TServices, TServiceDeps>,
55
62
  routesOrFactories: TRoutesOrFactories,
56
63
  ): FlattenRouteFactories<TRoutesOrFactories> {
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
64
  const routes: any[] = [];
59
65
 
60
66
  for (const item of routesOrFactories) {
@@ -162,107 +168,111 @@ export function defineRoute<
162
168
  return config;
163
169
  }
164
170
 
165
- // Type helpers to extract types from FragmentBuilder or DatabaseFragmentBuilder
166
- // DatabaseFragmentBuilder has 6 type parameters: TSchema, TConfig, TDeps, TServices, TUsedServices, TProvidedServices
167
- // FragmentBuilder has 6 type parameters: TConfig, TDeps, TServices, TAdditionalContext, TUsedServices, TProvidedServices
171
+ // ============================================================================
172
+ // Type extractors for FragmentDefinition
173
+ // ============================================================================
168
174
 
169
- // Helper to get the return type of the definition getter
170
- // Use T['definition'] to access the property type
171
- type GetDefinition<T> = T extends { definition: unknown } ? T["definition"] : never;
175
+ export type AnyFragmentDefinition = FragmentDefinition<
176
+ any,
177
+ any,
178
+ any,
179
+ any,
180
+ any,
181
+ any,
182
+ any,
183
+ any,
184
+ any,
185
+ any
186
+ >;
172
187
 
173
- // Extract config
188
+ // Extract config from FragmentDefinition
174
189
  export type ExtractFragmentConfig<T> =
175
- GetDefinition<T> extends FragmentDefinition<
176
- infer TConfig,
177
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
+ T extends FragmentDefinition<infer TConfig, any, any, any, any, any, any, any, any, any>
191
+ ? TConfig
192
+ : never;
193
+
194
+ // Extract deps from FragmentDefinition
195
+ export type ExtractFragmentDeps<T> =
196
+ T extends FragmentDefinition<any, any, infer TDeps, any, any, any, any, any, any, any>
197
+ ? TDeps
198
+ : never;
199
+
200
+ // Extract services from FragmentDefinition
201
+ // This extracts both base services (flat) and named services (nested)
202
+ // The result matches the structure of fragment.services at runtime
203
+ export type ExtractFragmentServices<T> =
204
+ T extends FragmentDefinition<
205
+ any,
206
+ any,
207
+ any,
208
+ infer TBaseServices,
209
+ infer TServices,
178
210
  any,
179
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
211
  any,
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
212
  any,
183
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
184
213
  any,
185
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
186
214
  any
187
215
  >
188
- ? TConfig
216
+ ? BoundServices<TBaseServices & TServices>
189
217
  : never;
190
218
 
191
- // Extract deps
192
- export type ExtractFragmentDeps<T> =
193
- GetDefinition<T> extends FragmentDefinition<
194
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
219
+ // Extract service dependencies from FragmentDefinition
220
+ export type ExtractFragmentServiceDeps<T> =
221
+ T extends FragmentDefinition<
222
+ any,
223
+ any,
224
+ any,
225
+ any,
226
+ any,
227
+ infer TServiceDependencies,
195
228
  any,
196
- infer TDeps,
197
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
229
  any,
199
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
200
230
  any,
201
- infer TUsedServices,
202
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
203
231
  any
204
232
  >
205
- ? TDeps & TUsedServices
233
+ ? TServiceDependencies
206
234
  : never;
207
235
 
208
- // Helper to recursively bind services (removes `this` parameter from methods)
209
- type OmitThisParameter<T> = T extends (this: infer _This, ...args: infer A) => infer R
210
- ? (...args: A) => R
211
- : T;
212
-
213
- type BoundServicesLocal<T> = {
214
- [K in keyof T]: T[K] extends (...args: never[]) => unknown
215
- ? OmitThisParameter<T[K]>
216
- : T[K] extends Record<string, unknown>
217
- ? BoundServicesLocal<T[K]>
218
- : T[K];
219
- };
220
-
221
- // Extract services (merges both withServices and providesService)
222
- // First try to extract from $types if available (for DatabaseFragmentBuilder)
223
- // Otherwise fall back to extracting from definition
224
- export type ExtractFragmentServices<T> = T extends {
225
- $types: { services: infer S; providedServices: infer P };
226
- }
227
- ? BoundServicesLocal<S & P>
228
- : GetDefinition<T> extends FragmentDefinition<
229
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
- any,
231
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
232
- any,
233
- infer TServices,
234
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
235
- any,
236
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
237
- any,
238
- infer TProvidedServices
239
- >
240
- ? TServices & TProvidedServices
241
- : never;
236
+ // Extract service this context from FragmentDefinition (used by services)
237
+ export type ExtractFragmentServiceThisContext<T> =
238
+ T extends FragmentDefinition<
239
+ any,
240
+ any,
241
+ any,
242
+ any,
243
+ any,
244
+ any,
245
+ any,
246
+ infer TServiceThisContext,
247
+ any,
248
+ any
249
+ >
250
+ ? TServiceThisContext
251
+ : RequestThisContext;
242
252
 
243
- // Extract the this context type from the fragment builder
244
- export type ExtractThisContext<T> =
245
- GetDefinition<T> extends FragmentDefinition<
246
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
253
+ // Extract handler this context from FragmentDefinition (used by route handlers)
254
+ export type ExtractFragmentHandlerThisContext<T> =
255
+ T extends FragmentDefinition<
256
+ any,
247
257
  any,
248
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
249
258
  any,
250
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
259
  any,
252
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
253
260
  any,
254
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
261
  any,
256
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
257
262
  any,
258
- infer TThisContext
263
+ any,
264
+ infer THandlerThisContext,
265
+ any
259
266
  >
260
- ? TThisContext
267
+ ? THandlerThisContext
261
268
  : RequestThisContext;
262
269
 
263
- // Overload that infers types from FragmentBuilder or DatabaseFragmentBuilder (runtime value)
264
- export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
265
- fragmentBuilder: TFragmentBuilder,
270
+ // Legacy: Extract this context from FragmentDefinition (defaults to service context for backwards compatibility)
271
+ export type ExtractFragmentThisContext<T> = ExtractFragmentServiceThisContext<T>;
272
+
273
+ // Overload that infers types from FragmentDefinition (runtime value)
274
+ export function defineRoutes<const TDefinition extends AnyFragmentDefinition>(
275
+ definition: TDefinition,
266
276
  ): {
267
277
  create: <
268
278
  const TRoutes extends readonly FragnoRouteConfig<
@@ -272,14 +282,15 @@ export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
272
282
  StandardSchemaV1 | undefined,
273
283
  string,
274
284
  string,
275
- ExtractThisContext<TFragmentBuilder>
285
+ ExtractFragmentHandlerThisContext<TDefinition>
276
286
  >[],
277
287
  >(
278
288
  fn: (
279
289
  context: RouteFactoryContext<
280
- ExtractFragmentConfig<TFragmentBuilder>,
281
- ExtractFragmentDeps<TFragmentBuilder>,
282
- ExtractFragmentServices<TFragmentBuilder>
290
+ ExtractFragmentConfig<TDefinition>,
291
+ ExtractFragmentDeps<TDefinition>,
292
+ ExtractFragmentServices<TDefinition>,
293
+ ExtractFragmentServiceDeps<TDefinition>
283
294
  > & {
284
295
  defineRoute: <
285
296
  const TMethod extends HTTPMethod,
@@ -296,7 +307,7 @@ export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
296
307
  TOutputSchema,
297
308
  TErrorCode,
298
309
  TQueryParameters,
299
- ExtractThisContext<TFragmentBuilder>
310
+ ExtractFragmentHandlerThisContext<TDefinition>
300
311
  >,
301
312
  ) => FragnoRouteConfig<
302
313
  TMethod,
@@ -305,20 +316,21 @@ export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
305
316
  TOutputSchema,
306
317
  TErrorCode,
307
318
  TQueryParameters,
308
- ExtractThisContext<TFragmentBuilder>
319
+ ExtractFragmentHandlerThisContext<TDefinition>
309
320
  >;
310
321
  },
311
322
  ) => TRoutes,
312
323
  ) => RouteFactory<
313
- ExtractFragmentConfig<TFragmentBuilder>,
314
- ExtractFragmentDeps<TFragmentBuilder>,
315
- ExtractFragmentServices<TFragmentBuilder>,
324
+ ExtractFragmentConfig<TDefinition>,
325
+ ExtractFragmentDeps<TDefinition>,
326
+ ExtractFragmentServices<TDefinition>,
327
+ ExtractFragmentServiceDeps<TDefinition>,
316
328
  TRoutes
317
329
  >;
318
330
  };
319
331
 
320
- // Overload that infers types from FragmentBuilder or DatabaseFragmentBuilder (type parameter)
321
- export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(): {
332
+ // Overload that infers types from FragmentDefinition (type parameter only)
333
+ export function defineRoutes<const TDefinition extends AnyFragmentDefinition>(): {
322
334
  create: <
323
335
  const TRoutes extends readonly FragnoRouteConfig<
324
336
  HTTPMethod,
@@ -327,14 +339,15 @@ export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
327
339
  StandardSchemaV1 | undefined,
328
340
  string,
329
341
  string,
330
- ExtractThisContext<TFragmentBuilder>
342
+ ExtractFragmentHandlerThisContext<TDefinition>
331
343
  >[],
332
344
  >(
333
345
  fn: (
334
346
  context: RouteFactoryContext<
335
- ExtractFragmentConfig<TFragmentBuilder>,
336
- ExtractFragmentDeps<TFragmentBuilder>,
337
- ExtractFragmentServices<TFragmentBuilder>
347
+ ExtractFragmentConfig<TDefinition>,
348
+ ExtractFragmentDeps<TDefinition>,
349
+ ExtractFragmentServices<TDefinition>,
350
+ ExtractFragmentServiceDeps<TDefinition>
338
351
  > & {
339
352
  defineRoute: <
340
353
  const TMethod extends HTTPMethod,
@@ -351,7 +364,7 @@ export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
351
364
  TOutputSchema,
352
365
  TErrorCode,
353
366
  TQueryParameters,
354
- ExtractThisContext<TFragmentBuilder>
367
+ ExtractFragmentHandlerThisContext<TDefinition>
355
368
  >,
356
369
  ) => FragnoRouteConfig<
357
370
  TMethod,
@@ -360,48 +373,25 @@ export function defineRoutes<const TFragmentBuilder extends AnyFragmentBuilder>(
360
373
  TOutputSchema,
361
374
  TErrorCode,
362
375
  TQueryParameters,
363
- ExtractThisContext<TFragmentBuilder>
376
+ ExtractFragmentHandlerThisContext<TDefinition>
364
377
  >;
365
378
  },
366
379
  ) => TRoutes,
367
380
  ) => RouteFactory<
368
- ExtractFragmentConfig<TFragmentBuilder>,
369
- ExtractFragmentDeps<TFragmentBuilder>,
370
- ExtractFragmentServices<TFragmentBuilder>,
381
+ ExtractFragmentConfig<TDefinition>,
382
+ ExtractFragmentDeps<TDefinition>,
383
+ ExtractFragmentServices<TDefinition>,
384
+ ExtractFragmentServiceDeps<TDefinition>,
371
385
  TRoutes
372
386
  >;
373
387
  };
374
388
 
375
- // Overload that accepts manual type parameters
376
- export function defineRoutes<TConfig = {}, TDeps = {}, TServices = {}>(): {
377
- create: <
378
- const TRoutes extends readonly FragnoRouteConfig<
379
- HTTPMethod,
380
- string,
381
- StandardSchemaV1 | undefined,
382
- StandardSchemaV1 | undefined,
383
- string,
384
- string,
385
- RequestThisContext
386
- >[],
387
- >(
388
- fn: (
389
- context: RouteFactoryContext<TConfig, TDeps, TServices> & {
390
- defineRoute: typeof defineRoute;
391
- },
392
- ) => TRoutes,
393
- ) => RouteFactory<TConfig, TDeps, TServices, TRoutes>;
394
- };
395
-
396
389
  // Implementation
397
390
  export function defineRoutes<
398
- const TConfig = {},
399
- const TDeps = {},
400
- const TServices = {},
401
- const TFragmentBuilder extends AnyFragmentBuilder | undefined = undefined,
391
+ const TDefinition extends AnyFragmentDefinition | undefined = undefined,
402
392
  >(
403
393
  // Parameter is only used for type inference, not runtime
404
- _fragmentBuilder?: TFragmentBuilder,
394
+ _definition?: TDefinition,
405
395
  ) {
406
396
  return {
407
397
  create: <
@@ -412,41 +402,63 @@ export function defineRoutes<
412
402
  StandardSchemaV1 | undefined,
413
403
  string,
414
404
  string,
415
- RequestThisContext
405
+ TDefinition extends AnyFragmentDefinition
406
+ ? ExtractFragmentThisContext<TDefinition>
407
+ : RequestThisContext
416
408
  >[],
417
409
  >(
418
410
  fn: (
419
411
  context: RouteFactoryContext<
420
- TFragmentBuilder extends AnyFragmentBuilder
421
- ? ExtractFragmentConfig<TFragmentBuilder>
422
- : TConfig,
423
- TFragmentBuilder extends AnyFragmentBuilder
424
- ? ExtractFragmentDeps<TFragmentBuilder>
425
- : TDeps,
426
- TFragmentBuilder extends AnyFragmentBuilder
427
- ? ExtractFragmentServices<TFragmentBuilder>
428
- : TServices
412
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentConfig<TDefinition> : {},
413
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentDeps<TDefinition> : {},
414
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServices<TDefinition> : {},
415
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServiceDeps<TDefinition> : {}
429
416
  > & {
430
- defineRoute: typeof defineRoute;
417
+ defineRoute: <
418
+ const TMethod extends HTTPMethod,
419
+ const TPath extends string,
420
+ const TInputSchema extends StandardSchemaV1 | undefined,
421
+ const TOutputSchema extends StandardSchemaV1 | undefined,
422
+ const TErrorCode extends string = string,
423
+ const TQueryParameters extends string = string,
424
+ >(
425
+ config: FragnoRouteConfig<
426
+ TMethod,
427
+ TPath,
428
+ TInputSchema,
429
+ TOutputSchema,
430
+ TErrorCode,
431
+ TQueryParameters,
432
+ TDefinition extends AnyFragmentDefinition
433
+ ? ExtractFragmentThisContext<TDefinition>
434
+ : RequestThisContext
435
+ >,
436
+ ) => FragnoRouteConfig<
437
+ TMethod,
438
+ TPath,
439
+ TInputSchema,
440
+ TOutputSchema,
441
+ TErrorCode,
442
+ TQueryParameters,
443
+ TDefinition extends AnyFragmentDefinition
444
+ ? ExtractFragmentThisContext<TDefinition>
445
+ : RequestThisContext
446
+ >;
431
447
  },
432
448
  ) => TRoutes,
433
449
  ): RouteFactory<
434
- TFragmentBuilder extends AnyFragmentBuilder
435
- ? ExtractFragmentConfig<TFragmentBuilder>
436
- : TConfig,
437
- TFragmentBuilder extends AnyFragmentBuilder ? ExtractFragmentDeps<TFragmentBuilder> : TDeps,
438
- TFragmentBuilder extends AnyFragmentBuilder
439
- ? ExtractFragmentServices<TFragmentBuilder>
440
- : TServices,
450
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentConfig<TDefinition> : {},
451
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentDeps<TDefinition> : {},
452
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServices<TDefinition> : {},
453
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServiceDeps<TDefinition> : {},
441
454
  TRoutes
442
455
  > => {
443
456
  // Create a wrapper around the callback that adds the defineRoute function
444
- return (ctx: RouteFactoryContext<unknown, unknown, unknown>) => {
457
+ return (ctx: RouteFactoryContext<unknown, unknown, unknown, unknown>) => {
445
458
  const extendedCtx = {
446
459
  ...ctx,
447
460
  defineRoute,
448
461
  };
449
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
450
462
  return fn(extendedCtx as any);
451
463
  };
452
464
  },
@@ -0,0 +1,43 @@
1
+ import type { HTTPMethod } from "./api";
2
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
3
+
4
+ /**
5
+ * Public configuration for Fragno fragments on the server side.
6
+ */
7
+ export interface FragnoPublicConfig {
8
+ mountRoute?: string;
9
+ }
10
+
11
+ /**
12
+ * Configuration for custom fetch behavior in client-side fragments.
13
+ */
14
+ export type FetcherConfig =
15
+ | { type: "options"; options: RequestInit }
16
+ | { type: "function"; fetcher: typeof fetch };
17
+
18
+ /**
19
+ * Public configuration for Fragno fragments on the client side.
20
+ */
21
+ export interface FragnoPublicClientConfig {
22
+ mountRoute?: string;
23
+ baseUrl?: string;
24
+ fetcherConfig?: FetcherConfig;
25
+ }
26
+
27
+ /**
28
+ * Shared configuration for fragment routes.
29
+ */
30
+ export interface FragnoFragmentSharedConfig<
31
+ TRoutes extends readonly {
32
+ method: HTTPMethod;
33
+ path: string;
34
+ inputSchema?: StandardSchemaV1 | undefined;
35
+ outputSchema?: StandardSchemaV1 | undefined;
36
+ errorCodes?: readonly string[];
37
+ queryParameters?: readonly string[];
38
+ pathParameters?: readonly string[];
39
+ }[] = [],
40
+ > {
41
+ name: string;
42
+ routes: TRoutes;
43
+ }