@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
@@ -1,5 +1,6 @@
1
1
  import { test, expect, expectTypeOf, describe } from "vitest";
2
- import { defineRoute } from "./route";
2
+ import { defineRoute, defineRoutes } from "./route";
3
+ import { defineFragment } from "./fragment-definition-builder";
3
4
  import { z } from "zod";
4
5
 
5
6
  describe("defineRoute", () => {
@@ -174,3 +175,88 @@ describe("defineRoute", () => {
174
175
  // Currently defineRoute doesn't enforce ValidPath constraints
175
176
  });
176
177
  });
178
+
179
+ // ExtractFragmentServices tests removed - use ExtractFragmentServices instead
180
+
181
+ describe("defineRoutes", () => {
182
+ test("defineRoutes extracts services correctly for route factory", () => {
183
+ const fragment = defineFragment<{}>("test-fragment")
184
+ .providesBaseService(({ defineService }) =>
185
+ defineService({
186
+ getUserById: async (id: string) => ({ id, name: "John" }),
187
+ createUser: async (name: string) => ({ id: "123", name }),
188
+ }),
189
+ )
190
+ .build();
191
+
192
+ const routeFactory = defineRoutes(fragment).create(({ services, config, deps }) => {
193
+ // Type check that services are properly extracted
194
+ expectTypeOf(services).toMatchObjectType<{
195
+ getUserById: (id: string) => Promise<{ id: string; name: string }>;
196
+ createUser: (name: string) => Promise<{ id: string; name: string }>;
197
+ }>();
198
+
199
+ expectTypeOf(config).toEqualTypeOf<{}>();
200
+ expectTypeOf(deps).toEqualTypeOf<{}>();
201
+
202
+ return [
203
+ defineRoute({
204
+ method: "GET",
205
+ path: "/users/:id",
206
+ outputSchema: z.object({ id: z.string(), name: z.string() }),
207
+ handler: async ({ pathParams }, { json }) => {
208
+ // Services should be accessible here via closure
209
+ const user = await services.getUserById(pathParams.id);
210
+ return json(user);
211
+ },
212
+ }),
213
+ ];
214
+ });
215
+
216
+ // routeFactory is a function that returns routes when called
217
+ expect(routeFactory).toBeDefined();
218
+ expect(typeof routeFactory).toBe("function");
219
+ });
220
+
221
+ test("defineRoutes with dependencies and services", () => {
222
+ const fragment = defineFragment<{ apiKey: string }>("auth-fragment")
223
+ .withDependencies(({ config }) => ({
224
+ authClient: { apiKey: config.apiKey },
225
+ }))
226
+ .providesBaseService(({ deps, defineService }) =>
227
+ defineService({
228
+ validateToken: async (_token: string) => {
229
+ return { valid: true, apiKey: deps.authClient.apiKey };
230
+ },
231
+ }),
232
+ )
233
+ .build();
234
+
235
+ const routeFactory = defineRoutes(fragment).create(({ services, config, deps }) => {
236
+ // Type check all context properties
237
+ expectTypeOf(config).toEqualTypeOf<{ apiKey: string }>();
238
+ expectTypeOf(deps).toMatchObjectType<{ authClient: { apiKey: string } }>();
239
+ expectTypeOf(services).toMatchObjectType<{
240
+ validateToken: (token: string) => Promise<{ valid: boolean; apiKey: string }>;
241
+ }>();
242
+
243
+ return [
244
+ defineRoute({
245
+ method: "POST",
246
+ path: "/auth/validate",
247
+ inputSchema: z.object({ token: z.string() }),
248
+ outputSchema: z.object({ valid: z.boolean() }),
249
+ handler: async ({ input }, { json }) => {
250
+ const { token } = await input!.valid();
251
+ const result = await services.validateToken(token);
252
+ return json({ valid: result.valid });
253
+ },
254
+ }),
255
+ ];
256
+ });
257
+
258
+ // routeFactory is a function that returns routes when called
259
+ expect(routeFactory).toBeDefined();
260
+ expect(typeof routeFactory).toBe("function");
261
+ });
262
+ });
package/src/api/route.ts CHANGED
@@ -1,53 +1,66 @@
1
+ // oxlint-disable no-explicit-any
2
+
1
3
  import type { StandardSchemaV1 } from "@standard-schema/spec";
2
- import type { FragnoRouteConfig, HTTPMethod } from "./api";
4
+ import type { FragnoRouteConfig, HTTPMethod, RequestThisContext } from "./api";
5
+ import type { FragmentDefinition } from "./fragment-definition-builder";
6
+ import type { BoundServices } from "./bind-services";
3
7
 
4
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- export type AnyFragnoRouteConfig = FragnoRouteConfig<HTTPMethod, string, any, any, any, any>;
8
+ export type AnyFragnoRouteConfig = FragnoRouteConfig<HTTPMethod, string, any, any, any, any, any>;
6
9
 
7
- export interface RouteFactoryContext<TConfig, TDeps, TServices> {
10
+ export interface RouteFactoryContext<TConfig, TDeps, TServices, TServiceDeps = {}> {
8
11
  config: TConfig;
9
12
  deps: TDeps;
10
13
  services: TServices;
14
+ serviceDeps: TServiceDeps;
11
15
  }
12
16
 
13
17
  export type RouteFactory<
14
18
  TConfig,
15
19
  TDeps,
16
20
  TServices,
21
+ TServiceDeps,
17
22
  TRoutes extends readonly FragnoRouteConfig<
18
23
  HTTPMethod,
19
24
  string,
20
25
  StandardSchemaV1 | undefined,
21
26
  StandardSchemaV1 | undefined,
22
27
  string,
23
- string
28
+ string,
29
+ RequestThisContext
24
30
  >[],
25
- > = (context: RouteFactoryContext<TConfig, TDeps, TServices>) => TRoutes;
31
+ > = (context: RouteFactoryContext<TConfig, TDeps, TServices, TServiceDeps>) => TRoutes;
26
32
 
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
- export type AnyRouteOrFactory = AnyFragnoRouteConfig | RouteFactory<any, any, any, any>;
33
+ /**
34
+ * @internal
35
+ */
36
+ export type AnyRouteOrFactory = AnyFragnoRouteConfig | RouteFactory<any, any, any, any, any>;
29
37
 
38
+ /**
39
+ * @internal
40
+ */
30
41
  export type FlattenRouteFactories<T extends readonly AnyRouteOrFactory[]> = T extends readonly [
31
42
  infer First,
32
43
  ...infer Rest extends readonly AnyRouteOrFactory[],
33
44
  ]
34
- ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- First extends RouteFactory<any, any, any, infer TRoutes>
45
+ ? First extends RouteFactory<any, any, any, any, infer TRoutes>
36
46
  ? [...TRoutes, ...FlattenRouteFactories<Rest>]
37
47
  : [First, ...FlattenRouteFactories<Rest>]
38
48
  : [];
39
49
 
40
- // Helper to resolve route factories into routes
50
+ /**
51
+ * Helper to resolve route factories into routes
52
+ * @internal
53
+ */
41
54
  export function resolveRouteFactories<
42
55
  TConfig,
43
56
  TDeps,
44
57
  TServices,
58
+ TServiceDeps,
45
59
  const TRoutesOrFactories extends readonly AnyRouteOrFactory[],
46
60
  >(
47
- context: RouteFactoryContext<TConfig, TDeps, TServices>,
61
+ context: RouteFactoryContext<TConfig, TDeps, TServices, TServiceDeps>,
48
62
  routesOrFactories: TRoutesOrFactories,
49
63
  ): FlattenRouteFactories<TRoutesOrFactories> {
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
64
  const routes: any[] = [];
52
65
 
53
66
  for (const item of routesOrFactories) {
@@ -74,6 +87,7 @@ export function defineRoute<
74
87
  const TOutputSchema extends StandardSchemaV1 | undefined,
75
88
  const TErrorCode extends string = string,
76
89
  const TQueryParameters extends string = string,
90
+ const TThisContext extends RequestThisContext = RequestThisContext,
77
91
  >(
78
92
  config: FragnoRouteConfig<
79
93
  TMethod,
@@ -81,9 +95,18 @@ export function defineRoute<
81
95
  undefined,
82
96
  TOutputSchema,
83
97
  TErrorCode,
84
- TQueryParameters
98
+ TQueryParameters,
99
+ TThisContext
85
100
  > & { inputSchema?: undefined },
86
- ): FragnoRouteConfig<TMethod, TPath, undefined, TOutputSchema, TErrorCode, TQueryParameters>;
101
+ ): FragnoRouteConfig<
102
+ TMethod,
103
+ TPath,
104
+ undefined,
105
+ TOutputSchema,
106
+ TErrorCode,
107
+ TQueryParameters,
108
+ TThisContext
109
+ >;
87
110
 
88
111
  // Overload for routes with inputSchema
89
112
  export function defineRoute<
@@ -93,6 +116,7 @@ export function defineRoute<
93
116
  const TOutputSchema extends StandardSchemaV1 | undefined,
94
117
  const TErrorCode extends string = string,
95
118
  const TQueryParameters extends string = string,
119
+ const TThisContext extends RequestThisContext = RequestThisContext,
96
120
  >(
97
121
  config: FragnoRouteConfig<
98
122
  TMethod,
@@ -100,9 +124,18 @@ export function defineRoute<
100
124
  TInputSchema,
101
125
  TOutputSchema,
102
126
  TErrorCode,
103
- TQueryParameters
127
+ TQueryParameters,
128
+ TThisContext
104
129
  > & { inputSchema: TInputSchema },
105
- ): FragnoRouteConfig<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters>;
130
+ ): FragnoRouteConfig<
131
+ TMethod,
132
+ TPath,
133
+ TInputSchema,
134
+ TOutputSchema,
135
+ TErrorCode,
136
+ TQueryParameters,
137
+ TThisContext
138
+ >;
106
139
 
107
140
  // implementation
108
141
  export function defineRoute<
@@ -112,6 +145,7 @@ export function defineRoute<
112
145
  const TOutputSchema extends StandardSchemaV1 | undefined,
113
146
  const TErrorCode extends string = string,
114
147
  const TQueryParameters extends string = string,
148
+ const TThisContext extends RequestThisContext = RequestThisContext,
115
149
  >(
116
150
  config: FragnoRouteConfig<
117
151
  TMethod,
@@ -119,13 +153,246 @@ export function defineRoute<
119
153
  TInputSchema,
120
154
  TOutputSchema,
121
155
  TErrorCode,
122
- TQueryParameters
156
+ TQueryParameters,
157
+ TThisContext
123
158
  >,
124
- ): FragnoRouteConfig<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters> {
159
+ ): FragnoRouteConfig<
160
+ TMethod,
161
+ TPath,
162
+ TInputSchema,
163
+ TOutputSchema,
164
+ TErrorCode,
165
+ TQueryParameters,
166
+ TThisContext
167
+ > {
125
168
  return config;
126
169
  }
127
170
 
128
- export function defineRoutes<TConfig = {}, TDeps = {}, TServices = {}>() {
171
+ // ============================================================================
172
+ // Type extractors for FragmentDefinition
173
+ // ============================================================================
174
+
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
+ >;
187
+
188
+ // Extract config from FragmentDefinition
189
+ export type ExtractFragmentConfig<T> =
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,
210
+ any,
211
+ any,
212
+ any,
213
+ any,
214
+ any
215
+ >
216
+ ? BoundServices<TBaseServices & TServices>
217
+ : never;
218
+
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,
228
+ any,
229
+ any,
230
+ any,
231
+ any
232
+ >
233
+ ? TServiceDependencies
234
+ : never;
235
+
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;
252
+
253
+ // Extract handler this context from FragmentDefinition (used by route handlers)
254
+ export type ExtractFragmentHandlerThisContext<T> =
255
+ T extends FragmentDefinition<
256
+ any,
257
+ any,
258
+ any,
259
+ any,
260
+ any,
261
+ any,
262
+ any,
263
+ any,
264
+ infer THandlerThisContext,
265
+ any
266
+ >
267
+ ? THandlerThisContext
268
+ : RequestThisContext;
269
+
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,
276
+ ): {
277
+ create: <
278
+ const TRoutes extends readonly FragnoRouteConfig<
279
+ HTTPMethod,
280
+ string,
281
+ StandardSchemaV1 | undefined,
282
+ StandardSchemaV1 | undefined,
283
+ string,
284
+ string,
285
+ ExtractFragmentHandlerThisContext<TDefinition>
286
+ >[],
287
+ >(
288
+ fn: (
289
+ context: RouteFactoryContext<
290
+ ExtractFragmentConfig<TDefinition>,
291
+ ExtractFragmentDeps<TDefinition>,
292
+ ExtractFragmentServices<TDefinition>,
293
+ ExtractFragmentServiceDeps<TDefinition>
294
+ > & {
295
+ defineRoute: <
296
+ const TMethod extends HTTPMethod,
297
+ const TPath extends string,
298
+ const TInputSchema extends StandardSchemaV1 | undefined,
299
+ const TOutputSchema extends StandardSchemaV1 | undefined,
300
+ const TErrorCode extends string = string,
301
+ const TQueryParameters extends string = string,
302
+ >(
303
+ config: FragnoRouteConfig<
304
+ TMethod,
305
+ TPath,
306
+ TInputSchema,
307
+ TOutputSchema,
308
+ TErrorCode,
309
+ TQueryParameters,
310
+ ExtractFragmentHandlerThisContext<TDefinition>
311
+ >,
312
+ ) => FragnoRouteConfig<
313
+ TMethod,
314
+ TPath,
315
+ TInputSchema,
316
+ TOutputSchema,
317
+ TErrorCode,
318
+ TQueryParameters,
319
+ ExtractFragmentHandlerThisContext<TDefinition>
320
+ >;
321
+ },
322
+ ) => TRoutes,
323
+ ) => RouteFactory<
324
+ ExtractFragmentConfig<TDefinition>,
325
+ ExtractFragmentDeps<TDefinition>,
326
+ ExtractFragmentServices<TDefinition>,
327
+ ExtractFragmentServiceDeps<TDefinition>,
328
+ TRoutes
329
+ >;
330
+ };
331
+
332
+ // Overload that infers types from FragmentDefinition (type parameter only)
333
+ export function defineRoutes<const TDefinition extends AnyFragmentDefinition>(): {
334
+ create: <
335
+ const TRoutes extends readonly FragnoRouteConfig<
336
+ HTTPMethod,
337
+ string,
338
+ StandardSchemaV1 | undefined,
339
+ StandardSchemaV1 | undefined,
340
+ string,
341
+ string,
342
+ ExtractFragmentHandlerThisContext<TDefinition>
343
+ >[],
344
+ >(
345
+ fn: (
346
+ context: RouteFactoryContext<
347
+ ExtractFragmentConfig<TDefinition>,
348
+ ExtractFragmentDeps<TDefinition>,
349
+ ExtractFragmentServices<TDefinition>,
350
+ ExtractFragmentServiceDeps<TDefinition>
351
+ > & {
352
+ defineRoute: <
353
+ const TMethod extends HTTPMethod,
354
+ const TPath extends string,
355
+ const TInputSchema extends StandardSchemaV1 | undefined,
356
+ const TOutputSchema extends StandardSchemaV1 | undefined,
357
+ const TErrorCode extends string = string,
358
+ const TQueryParameters extends string = string,
359
+ >(
360
+ config: FragnoRouteConfig<
361
+ TMethod,
362
+ TPath,
363
+ TInputSchema,
364
+ TOutputSchema,
365
+ TErrorCode,
366
+ TQueryParameters,
367
+ ExtractFragmentHandlerThisContext<TDefinition>
368
+ >,
369
+ ) => FragnoRouteConfig<
370
+ TMethod,
371
+ TPath,
372
+ TInputSchema,
373
+ TOutputSchema,
374
+ TErrorCode,
375
+ TQueryParameters,
376
+ ExtractFragmentHandlerThisContext<TDefinition>
377
+ >;
378
+ },
379
+ ) => TRoutes,
380
+ ) => RouteFactory<
381
+ ExtractFragmentConfig<TDefinition>,
382
+ ExtractFragmentDeps<TDefinition>,
383
+ ExtractFragmentServices<TDefinition>,
384
+ ExtractFragmentServiceDeps<TDefinition>,
385
+ TRoutes
386
+ >;
387
+ };
388
+
389
+ // Implementation
390
+ export function defineRoutes<
391
+ const TDefinition extends AnyFragmentDefinition | undefined = undefined,
392
+ >(
393
+ // Parameter is only used for type inference, not runtime
394
+ _definition?: TDefinition,
395
+ ) {
129
396
  return {
130
397
  create: <
131
398
  const TRoutes extends readonly FragnoRouteConfig<
@@ -134,12 +401,66 @@ export function defineRoutes<TConfig = {}, TDeps = {}, TServices = {}>() {
134
401
  StandardSchemaV1 | undefined,
135
402
  StandardSchemaV1 | undefined,
136
403
  string,
137
- string
404
+ string,
405
+ TDefinition extends AnyFragmentDefinition
406
+ ? ExtractFragmentThisContext<TDefinition>
407
+ : RequestThisContext
138
408
  >[],
139
409
  >(
140
- fn: (context: RouteFactoryContext<TConfig, TDeps, TServices>) => TRoutes,
141
- ): RouteFactory<TConfig, TDeps, TServices, TRoutes> => {
142
- return fn;
410
+ fn: (
411
+ context: RouteFactoryContext<
412
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentConfig<TDefinition> : {},
413
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentDeps<TDefinition> : {},
414
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServices<TDefinition> : {},
415
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServiceDeps<TDefinition> : {}
416
+ > & {
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
+ >;
447
+ },
448
+ ) => TRoutes,
449
+ ): RouteFactory<
450
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentConfig<TDefinition> : {},
451
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentDeps<TDefinition> : {},
452
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServices<TDefinition> : {},
453
+ TDefinition extends AnyFragmentDefinition ? ExtractFragmentServiceDeps<TDefinition> : {},
454
+ TRoutes
455
+ > => {
456
+ // Create a wrapper around the callback that adds the defineRoute function
457
+ return (ctx: RouteFactoryContext<unknown, unknown, unknown, unknown>) => {
458
+ const extendedCtx = {
459
+ ...ctx,
460
+ defineRoute,
461
+ };
462
+ return fn(extendedCtx as any);
463
+ };
143
464
  },
144
465
  };
145
466
  }
@@ -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
+ }