@fragno-dev/core 0.1.7 → 0.1.8
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.
- package/.turbo/turbo-build.log +45 -53
- package/CHANGELOG.md +6 -0
- package/dist/api/api.d.ts +2 -2
- package/dist/api/api.js +3 -2
- package/dist/api/fragment-builder.d.ts +2 -4
- package/dist/api/fragment-builder.js +1 -1
- package/dist/api/fragment-instantiation.d.ts +2 -4
- package/dist/api/fragment-instantiation.js +3 -5
- package/dist/api/route.d.ts +2 -3
- package/dist/api/route.js +1 -1
- package/dist/api-BFrUCIsF.d.ts +963 -0
- package/dist/api-BFrUCIsF.d.ts.map +1 -0
- package/dist/client/client.d.ts +1 -3
- package/dist/client/client.js +4 -5
- package/dist/client/client.svelte.d.ts +2 -3
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +4 -5
- package/dist/client/client.svelte.js.map +1 -1
- package/dist/client/react.d.ts +2 -3
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +4 -5
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +2 -3
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +4 -5
- package/dist/client/solid.js.map +1 -1
- package/dist/client/vanilla.d.ts +2 -3
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +4 -5
- package/dist/client/vanilla.js.map +1 -1
- package/dist/client/vue.d.ts +2 -3
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +4 -5
- package/dist/client/vue.js.map +1 -1
- package/dist/{client-C5LsYHEI.js → client-DAFHcKqA.js} +4 -4
- package/dist/{client-C5LsYHEI.js.map → client-DAFHcKqA.js.map} +1 -1
- package/dist/fragment-builder-Boh2vNHq.js +108 -0
- package/dist/fragment-builder-Boh2vNHq.js.map +1 -0
- package/dist/fragment-instantiation-DUT-HLl1.js +898 -0
- package/dist/fragment-instantiation-DUT-HLl1.js.map +1 -0
- package/dist/integrations/react-ssr.js +1 -1
- package/dist/mod.d.ts +2 -4
- package/dist/mod.js +4 -6
- package/dist/{route-C5Uryylh.js → route-C4CyNHkC.js} +8 -3
- package/dist/route-C4CyNHkC.js.map +1 -0
- package/dist/{ssr-BByDVfFD.js → ssr-kyKI7pqH.js} +1 -1
- package/dist/{ssr-BByDVfFD.js.map → ssr-kyKI7pqH.js.map} +1 -1
- package/dist/test/test.d.ts +6 -7
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js +9 -7
- package/dist/test/test.js.map +1 -1
- package/package.json +1 -1
- package/src/api/api.ts +45 -6
- package/src/api/fragment-builder.ts +463 -25
- package/src/api/fragment-instantiation.test.ts +249 -7
- package/src/api/fragment-instantiation.ts +283 -16
- package/src/api/fragment-services.test.ts +462 -0
- package/src/api/fragment.test.ts +65 -17
- package/src/api/request-middleware.test.ts +6 -3
- package/src/api/route.test.ts +111 -1
- package/src/api/route.ts +323 -14
- package/src/mod.ts +11 -1
- package/src/test/test.test.ts +20 -15
- package/src/test/test.ts +48 -9
- package/dist/api-BWN97TOr.d.ts +0 -377
- package/dist/api-BWN97TOr.d.ts.map +0 -1
- package/dist/api-DngJDcmO.js +0 -54
- package/dist/api-DngJDcmO.js.map +0 -1
- package/dist/fragment-builder-DOnCVBqc.js +0 -47
- package/dist/fragment-builder-DOnCVBqc.js.map +0 -1
- package/dist/fragment-builder-MGr68GNb.d.ts +0 -409
- package/dist/fragment-builder-MGr68GNb.d.ts.map +0 -1
- package/dist/fragment-instantiation-C4wvwl6V.js +0 -446
- package/dist/fragment-instantiation-C4wvwl6V.js.map +0 -1
- package/dist/request-output-context-CdIjwmEN.js +0 -320
- package/dist/request-output-context-CdIjwmEN.js.map +0 -1
- package/dist/route-Bl9Zr1Yv.d.ts +0 -26
- package/dist/route-Bl9Zr1Yv.d.ts.map +0 -1
- package/dist/route-C5Uryylh.js.map +0 -1
|
@@ -1,26 +1,92 @@
|
|
|
1
|
+
import type { RequestThisContext } from "./api";
|
|
1
2
|
import type { FragnoPublicConfig } from "./fragment-instantiation";
|
|
3
|
+
import type { RequestInputContext } from "./request-input-context";
|
|
4
|
+
import type { RequestOutputContext } from "./request-output-context";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Metadata for a service dependency
|
|
8
|
+
*/
|
|
9
|
+
interface ServiceMetadata {
|
|
10
|
+
/** Name of the service */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Whether this service is required (false means optional) */
|
|
13
|
+
required: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type RouteHandler = (
|
|
17
|
+
this: RequestThisContext,
|
|
18
|
+
inputContext: RequestInputContext,
|
|
19
|
+
outputContext: RequestOutputContext,
|
|
20
|
+
) => Promise<Response>;
|
|
2
21
|
|
|
3
22
|
export interface FragmentDefinition<
|
|
4
23
|
TConfig,
|
|
5
24
|
TDeps = {},
|
|
6
|
-
TServices
|
|
25
|
+
TServices = {},
|
|
7
26
|
TAdditionalContext extends Record<string, unknown> = {},
|
|
27
|
+
TUsedServices = {},
|
|
28
|
+
TProvidedServices = {},
|
|
29
|
+
TThisContext extends RequestThisContext = RequestThisContext,
|
|
8
30
|
> {
|
|
9
31
|
name: string;
|
|
10
32
|
dependencies?: (config: TConfig, options: FragnoPublicConfig) => TDeps;
|
|
11
|
-
services?: (
|
|
33
|
+
services?: (
|
|
34
|
+
config: TConfig,
|
|
35
|
+
options: FragnoPublicConfig,
|
|
36
|
+
deps: TDeps & TUsedServices,
|
|
37
|
+
) => TServices;
|
|
12
38
|
additionalContext?: TAdditionalContext;
|
|
39
|
+
createHandlerWrapper?: (
|
|
40
|
+
options: FragnoPublicConfig,
|
|
41
|
+
) => (
|
|
42
|
+
handler: (this: TThisContext, ...args: Parameters<RouteHandler>) => ReturnType<RouteHandler>,
|
|
43
|
+
) => RouteHandler;
|
|
44
|
+
/** Services that this fragment uses (can be required or optional) */
|
|
45
|
+
usedServices?: {
|
|
46
|
+
[K in keyof TUsedServices]: ServiceMetadata;
|
|
47
|
+
};
|
|
48
|
+
/** Services that this fragment provides to other fragments (can be a factory function or direct object) */
|
|
49
|
+
providedServices?:
|
|
50
|
+
| {
|
|
51
|
+
[K in keyof TProvidedServices]: TProvidedServices[K];
|
|
52
|
+
}
|
|
53
|
+
| ((
|
|
54
|
+
config: TConfig,
|
|
55
|
+
options: FragnoPublicConfig,
|
|
56
|
+
deps: TDeps & TUsedServices,
|
|
57
|
+
) => TProvidedServices);
|
|
13
58
|
}
|
|
14
59
|
|
|
15
60
|
export class FragmentBuilder<
|
|
16
61
|
const TConfig,
|
|
17
62
|
const TDeps = {},
|
|
18
|
-
const TServices
|
|
63
|
+
const TServices = {},
|
|
19
64
|
const TAdditionalContext extends Record<string, unknown> = {},
|
|
65
|
+
const TUsedServices = {},
|
|
66
|
+
const TProvidedServices = {},
|
|
67
|
+
const TThisContext extends RequestThisContext = RequestThisContext,
|
|
20
68
|
> {
|
|
21
|
-
#definition: FragmentDefinition<
|
|
69
|
+
#definition: FragmentDefinition<
|
|
70
|
+
TConfig,
|
|
71
|
+
TDeps,
|
|
72
|
+
TServices,
|
|
73
|
+
TAdditionalContext,
|
|
74
|
+
TUsedServices,
|
|
75
|
+
TProvidedServices,
|
|
76
|
+
TThisContext
|
|
77
|
+
>;
|
|
22
78
|
|
|
23
|
-
constructor(
|
|
79
|
+
constructor(
|
|
80
|
+
definition: FragmentDefinition<
|
|
81
|
+
TConfig,
|
|
82
|
+
TDeps,
|
|
83
|
+
TServices,
|
|
84
|
+
TAdditionalContext,
|
|
85
|
+
TUsedServices,
|
|
86
|
+
TProvidedServices,
|
|
87
|
+
TThisContext
|
|
88
|
+
>,
|
|
89
|
+
) {
|
|
24
90
|
this.#definition = definition;
|
|
25
91
|
}
|
|
26
92
|
|
|
@@ -36,8 +102,36 @@ export class FragmentBuilder<
|
|
|
36
102
|
fn: (
|
|
37
103
|
context: { config: TConfig; fragnoConfig: FragnoPublicConfig } & TAdditionalContext,
|
|
38
104
|
) => TNewDeps,
|
|
39
|
-
): FragmentBuilder<
|
|
40
|
-
|
|
105
|
+
): FragmentBuilder<
|
|
106
|
+
TConfig,
|
|
107
|
+
TNewDeps,
|
|
108
|
+
{},
|
|
109
|
+
TAdditionalContext,
|
|
110
|
+
TUsedServices,
|
|
111
|
+
TProvidedServices,
|
|
112
|
+
TThisContext
|
|
113
|
+
> {
|
|
114
|
+
// Safe cast: If providedServices is a function, we need to update its signature to use TNewDeps.
|
|
115
|
+
// This is safe because the function will be called with the new deps at runtime.
|
|
116
|
+
const providedServices = this.#definition.providedServices;
|
|
117
|
+
const recastProvidedServices =
|
|
118
|
+
typeof providedServices === "function"
|
|
119
|
+
? (providedServices as unknown as (
|
|
120
|
+
config: TConfig,
|
|
121
|
+
options: FragnoPublicConfig,
|
|
122
|
+
deps: TNewDeps & TUsedServices,
|
|
123
|
+
) => TProvidedServices)
|
|
124
|
+
: providedServices;
|
|
125
|
+
|
|
126
|
+
return new FragmentBuilder<
|
|
127
|
+
TConfig,
|
|
128
|
+
TNewDeps,
|
|
129
|
+
{},
|
|
130
|
+
TAdditionalContext,
|
|
131
|
+
TUsedServices,
|
|
132
|
+
TProvidedServices,
|
|
133
|
+
TThisContext
|
|
134
|
+
>({
|
|
41
135
|
name: this.#definition.name,
|
|
42
136
|
dependencies: (config: TConfig, options: FragnoPublicConfig) => {
|
|
43
137
|
return fn({ config, fragnoConfig: options } as {
|
|
@@ -47,33 +141,377 @@ export class FragmentBuilder<
|
|
|
47
141
|
},
|
|
48
142
|
services: undefined,
|
|
49
143
|
additionalContext: this.#definition.additionalContext,
|
|
144
|
+
usedServices: this.#definition.usedServices,
|
|
145
|
+
providedServices: recastProvidedServices,
|
|
50
146
|
});
|
|
51
147
|
}
|
|
52
148
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Declare that this fragment uses a service.
|
|
151
|
+
* By default, the service is required. Pass { optional: true } to make it optional.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* // Required service
|
|
156
|
+
* defineFragment("my-fragment")
|
|
157
|
+
* .usesService<"email", IEmailService>("email")
|
|
158
|
+
* .providesService(({ deps, define }) => define({
|
|
159
|
+
* sendWelcome: () => deps.email.send(...)
|
|
160
|
+
* }))
|
|
161
|
+
*
|
|
162
|
+
* // Optional service
|
|
163
|
+
* defineFragment("my-fragment")
|
|
164
|
+
* .usesService<"email", IEmailService>("email", { optional: true })
|
|
165
|
+
* .providesService(({ deps, define }) => define({
|
|
166
|
+
* sendWelcome: () => {
|
|
167
|
+
* if (deps.email) {
|
|
168
|
+
* deps.email.send(...)
|
|
169
|
+
* }
|
|
170
|
+
* }
|
|
171
|
+
* }))
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
usesService<TServiceName extends string, TService>(
|
|
175
|
+
serviceName: TServiceName,
|
|
176
|
+
options?: { optional?: false },
|
|
177
|
+
): FragmentBuilder<
|
|
178
|
+
TConfig,
|
|
179
|
+
TDeps,
|
|
180
|
+
TServices,
|
|
181
|
+
TAdditionalContext,
|
|
182
|
+
TUsedServices & { [K in TServiceName]: TService },
|
|
183
|
+
TProvidedServices,
|
|
184
|
+
TThisContext
|
|
185
|
+
>;
|
|
186
|
+
usesService<TServiceName extends string, TService>(
|
|
187
|
+
serviceName: TServiceName,
|
|
188
|
+
options: { optional: true },
|
|
189
|
+
): FragmentBuilder<
|
|
190
|
+
TConfig,
|
|
191
|
+
TDeps,
|
|
192
|
+
TServices,
|
|
193
|
+
TAdditionalContext,
|
|
194
|
+
TUsedServices & { [K in TServiceName]: TService | undefined },
|
|
195
|
+
TProvidedServices,
|
|
196
|
+
TThisContext
|
|
197
|
+
>;
|
|
198
|
+
usesService<TServiceName extends string, TService>(
|
|
199
|
+
serviceName: TServiceName,
|
|
200
|
+
options?: { optional?: boolean },
|
|
201
|
+
): FragmentBuilder<
|
|
202
|
+
TConfig,
|
|
203
|
+
TDeps,
|
|
204
|
+
TServices,
|
|
205
|
+
TAdditionalContext,
|
|
206
|
+
TUsedServices & { [K in TServiceName]: TService | TService | undefined },
|
|
207
|
+
TProvidedServices,
|
|
208
|
+
TThisContext
|
|
209
|
+
> {
|
|
210
|
+
const isOptional = options?.optional ?? false;
|
|
211
|
+
return new FragmentBuilder<
|
|
212
|
+
TConfig,
|
|
213
|
+
TDeps,
|
|
214
|
+
TServices,
|
|
215
|
+
TAdditionalContext,
|
|
216
|
+
TUsedServices & { [K in TServiceName]: TService | (TService | undefined) },
|
|
217
|
+
TProvidedServices,
|
|
218
|
+
TThisContext
|
|
219
|
+
>({
|
|
63
220
|
name: this.#definition.name,
|
|
64
221
|
dependencies: this.#definition.dependencies,
|
|
65
|
-
services:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
222
|
+
services: this.#definition.services as
|
|
223
|
+
| ((
|
|
224
|
+
config: TConfig,
|
|
225
|
+
options: FragnoPublicConfig,
|
|
226
|
+
deps: TDeps &
|
|
227
|
+
(TUsedServices & { [K in TServiceName]: TService | (TService | undefined) }),
|
|
228
|
+
) => TServices)
|
|
229
|
+
| undefined,
|
|
72
230
|
additionalContext: this.#definition.additionalContext,
|
|
231
|
+
usedServices: {
|
|
232
|
+
...this.#definition.usedServices,
|
|
233
|
+
[serviceName]: { name: serviceName, required: !isOptional },
|
|
234
|
+
} as {
|
|
235
|
+
[K in keyof (TUsedServices & {
|
|
236
|
+
[K in TServiceName]: TService | (TService | undefined);
|
|
237
|
+
})]: ServiceMetadata;
|
|
238
|
+
},
|
|
239
|
+
providedServices: this.#definition.providedServices,
|
|
73
240
|
});
|
|
74
241
|
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Define services for this fragment (unnamed) using a callback.
|
|
245
|
+
* Use the `defineService` function from the callback context for proper typing (optional).
|
|
246
|
+
*/
|
|
247
|
+
providesService<TNewServices>(
|
|
248
|
+
fn: (context: {
|
|
249
|
+
config: TConfig;
|
|
250
|
+
fragnoConfig: FragnoPublicConfig;
|
|
251
|
+
deps: TDeps & TUsedServices;
|
|
252
|
+
defineService: <T>(services: T) => T;
|
|
253
|
+
}) => TNewServices,
|
|
254
|
+
): FragmentBuilder<
|
|
255
|
+
TConfig,
|
|
256
|
+
TDeps,
|
|
257
|
+
TNewServices,
|
|
258
|
+
TAdditionalContext,
|
|
259
|
+
TUsedServices,
|
|
260
|
+
TProvidedServices,
|
|
261
|
+
TThisContext
|
|
262
|
+
>;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Define services for this fragment (unnamed) using a direct object.
|
|
266
|
+
*/
|
|
267
|
+
providesService<TNewServices>(
|
|
268
|
+
services: TNewServices,
|
|
269
|
+
): FragmentBuilder<
|
|
270
|
+
TConfig,
|
|
271
|
+
TDeps,
|
|
272
|
+
TNewServices,
|
|
273
|
+
TAdditionalContext,
|
|
274
|
+
TUsedServices,
|
|
275
|
+
TProvidedServices,
|
|
276
|
+
TThisContext
|
|
277
|
+
>;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Provide a named service using a callback.
|
|
281
|
+
* Use the `defineService` function from the callback context for proper typing (optional).
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```ts
|
|
285
|
+
* interface IEmailService {
|
|
286
|
+
* send(to: string, subject: string, body: string): Promise<void>;
|
|
287
|
+
* }
|
|
288
|
+
*
|
|
289
|
+
* defineFragment("email-fragment")
|
|
290
|
+
* .providesService<"email", IEmailService>("email", ({ defineService }) => defineService({
|
|
291
|
+
* send: async (to, subject, body) => {
|
|
292
|
+
* // implementation
|
|
293
|
+
* }
|
|
294
|
+
* }))
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
providesService<TServiceName extends string, TService>(
|
|
298
|
+
serviceName: TServiceName,
|
|
299
|
+
fn: (context: {
|
|
300
|
+
config: TConfig;
|
|
301
|
+
fragnoConfig: FragnoPublicConfig;
|
|
302
|
+
deps: TDeps & TUsedServices;
|
|
303
|
+
defineService: <T>(services: T) => T;
|
|
304
|
+
}) => TService,
|
|
305
|
+
): FragmentBuilder<
|
|
306
|
+
TConfig,
|
|
307
|
+
TDeps,
|
|
308
|
+
TServices,
|
|
309
|
+
TAdditionalContext,
|
|
310
|
+
TUsedServices,
|
|
311
|
+
TProvidedServices & { [K in TServiceName]: TService },
|
|
312
|
+
TThisContext
|
|
313
|
+
>;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Provide a named service using a direct object.
|
|
317
|
+
*/
|
|
318
|
+
providesService<TServiceName extends string, TService>(
|
|
319
|
+
serviceName: TServiceName,
|
|
320
|
+
service: TService,
|
|
321
|
+
): FragmentBuilder<
|
|
322
|
+
TConfig,
|
|
323
|
+
TDeps,
|
|
324
|
+
TServices,
|
|
325
|
+
TAdditionalContext,
|
|
326
|
+
TUsedServices,
|
|
327
|
+
TProvidedServices & { [K in TServiceName]: TService },
|
|
328
|
+
TThisContext
|
|
329
|
+
>;
|
|
330
|
+
|
|
331
|
+
providesService<TServiceName extends string, TService>(
|
|
332
|
+
...args:
|
|
333
|
+
| [
|
|
334
|
+
fnOrServices:
|
|
335
|
+
| ((context: {
|
|
336
|
+
config: TConfig;
|
|
337
|
+
fragnoConfig: FragnoPublicConfig;
|
|
338
|
+
deps: TDeps & TUsedServices;
|
|
339
|
+
defineService: <T>(services: T) => T;
|
|
340
|
+
}) => TService)
|
|
341
|
+
| TService,
|
|
342
|
+
]
|
|
343
|
+
| [
|
|
344
|
+
serviceName: TServiceName,
|
|
345
|
+
fnOrService:
|
|
346
|
+
| ((context: {
|
|
347
|
+
config: TConfig;
|
|
348
|
+
fragnoConfig: FragnoPublicConfig;
|
|
349
|
+
deps: TDeps & TUsedServices;
|
|
350
|
+
defineService: <T>(services: T) => T;
|
|
351
|
+
}) => TService)
|
|
352
|
+
| TService,
|
|
353
|
+
]
|
|
354
|
+
):
|
|
355
|
+
| FragmentBuilder<
|
|
356
|
+
TConfig,
|
|
357
|
+
TDeps,
|
|
358
|
+
TService,
|
|
359
|
+
TAdditionalContext,
|
|
360
|
+
TUsedServices,
|
|
361
|
+
TProvidedServices,
|
|
362
|
+
TThisContext
|
|
363
|
+
>
|
|
364
|
+
| FragmentBuilder<
|
|
365
|
+
TConfig,
|
|
366
|
+
TDeps,
|
|
367
|
+
TServices,
|
|
368
|
+
TAdditionalContext,
|
|
369
|
+
TUsedServices,
|
|
370
|
+
TProvidedServices & { [K in TServiceName]: TService },
|
|
371
|
+
TThisContext
|
|
372
|
+
> {
|
|
373
|
+
// The defineService function is just an identity function at runtime
|
|
374
|
+
const defineService = <T>(services: T) => services;
|
|
375
|
+
|
|
376
|
+
if (args.length === 1) {
|
|
377
|
+
// Unnamed service
|
|
378
|
+
const [fnOrServices] = args;
|
|
379
|
+
|
|
380
|
+
// Create a callback that provides the full context including defineService
|
|
381
|
+
const servicesCallback = (
|
|
382
|
+
config: TConfig,
|
|
383
|
+
options: FragnoPublicConfig,
|
|
384
|
+
deps: TDeps & TUsedServices,
|
|
385
|
+
): TService => {
|
|
386
|
+
if (typeof fnOrServices === "function") {
|
|
387
|
+
// It's a factory function - call it with context
|
|
388
|
+
const fn = fnOrServices as (context: {
|
|
389
|
+
config: TConfig;
|
|
390
|
+
fragnoConfig: FragnoPublicConfig;
|
|
391
|
+
deps: TDeps & TUsedServices;
|
|
392
|
+
defineService: <T>(services: T) => T;
|
|
393
|
+
}) => TService;
|
|
394
|
+
return fn({
|
|
395
|
+
config,
|
|
396
|
+
fragnoConfig: options,
|
|
397
|
+
deps,
|
|
398
|
+
defineService,
|
|
399
|
+
});
|
|
400
|
+
} else {
|
|
401
|
+
// It's a direct service object
|
|
402
|
+
return fnOrServices;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
return new FragmentBuilder<
|
|
407
|
+
TConfig,
|
|
408
|
+
TDeps,
|
|
409
|
+
TService,
|
|
410
|
+
TAdditionalContext,
|
|
411
|
+
TUsedServices,
|
|
412
|
+
TProvidedServices,
|
|
413
|
+
TThisContext
|
|
414
|
+
>({
|
|
415
|
+
name: this.#definition.name,
|
|
416
|
+
dependencies: this.#definition.dependencies,
|
|
417
|
+
services: servicesCallback as (
|
|
418
|
+
config: TConfig,
|
|
419
|
+
options: FragnoPublicConfig,
|
|
420
|
+
deps: TDeps & TUsedServices,
|
|
421
|
+
) => TService,
|
|
422
|
+
additionalContext: this.#definition.additionalContext,
|
|
423
|
+
usedServices: this.#definition.usedServices,
|
|
424
|
+
providedServices: this.#definition.providedServices,
|
|
425
|
+
});
|
|
426
|
+
} else {
|
|
427
|
+
// Named service
|
|
428
|
+
const [serviceName, fnOrService] = args;
|
|
429
|
+
|
|
430
|
+
// Create a callback that provides the full context including defineService
|
|
431
|
+
const createService = (
|
|
432
|
+
config: TConfig,
|
|
433
|
+
options: FragnoPublicConfig,
|
|
434
|
+
deps: TDeps & TUsedServices,
|
|
435
|
+
): TService => {
|
|
436
|
+
if (typeof fnOrService === "function") {
|
|
437
|
+
// It's a factory function - call it with context
|
|
438
|
+
const fn = fnOrService as (context: {
|
|
439
|
+
config: TConfig;
|
|
440
|
+
fragnoConfig: FragnoPublicConfig;
|
|
441
|
+
deps: TDeps & TUsedServices;
|
|
442
|
+
defineService: <T>(services: T) => T;
|
|
443
|
+
}) => TService;
|
|
444
|
+
return fn({
|
|
445
|
+
config,
|
|
446
|
+
fragnoConfig: options,
|
|
447
|
+
deps,
|
|
448
|
+
defineService,
|
|
449
|
+
});
|
|
450
|
+
} else {
|
|
451
|
+
// It's a direct service object
|
|
452
|
+
return fnOrService;
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
// We need to handle both function and object forms of providedServices
|
|
457
|
+
const existingProvidedServices = this.#definition.providedServices;
|
|
458
|
+
const newProvidedServices:
|
|
459
|
+
| {
|
|
460
|
+
[K in keyof (TProvidedServices & {
|
|
461
|
+
[K in TServiceName]: TService;
|
|
462
|
+
})]: (TProvidedServices & {
|
|
463
|
+
[K in TServiceName]: TService;
|
|
464
|
+
})[K];
|
|
465
|
+
}
|
|
466
|
+
| ((
|
|
467
|
+
config: TConfig,
|
|
468
|
+
options: FragnoPublicConfig,
|
|
469
|
+
deps: TDeps & TUsedServices,
|
|
470
|
+
) => TProvidedServices & { [K in TServiceName]: TService }) =
|
|
471
|
+
typeof existingProvidedServices === "function"
|
|
472
|
+
? // If existing is a function, create a new function that calls both
|
|
473
|
+
(config: TConfig, options: FragnoPublicConfig, deps: TDeps & TUsedServices) => {
|
|
474
|
+
const existing = existingProvidedServices(config, options, deps);
|
|
475
|
+
const newService = createService(config, options, deps);
|
|
476
|
+
return {
|
|
477
|
+
...existing,
|
|
478
|
+
[serviceName]: newService,
|
|
479
|
+
} as TProvidedServices & { [K in TServiceName]: TService };
|
|
480
|
+
}
|
|
481
|
+
: // If existing is an object or undefined, spread it
|
|
482
|
+
({
|
|
483
|
+
...existingProvidedServices,
|
|
484
|
+
[serviceName]: createService,
|
|
485
|
+
} as {
|
|
486
|
+
[K in keyof (TProvidedServices & {
|
|
487
|
+
[K in TServiceName]: TService;
|
|
488
|
+
})]: (TProvidedServices & {
|
|
489
|
+
[K in TServiceName]: TService;
|
|
490
|
+
})[K];
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
return new FragmentBuilder<
|
|
494
|
+
TConfig,
|
|
495
|
+
TDeps,
|
|
496
|
+
TServices,
|
|
497
|
+
TAdditionalContext,
|
|
498
|
+
TUsedServices,
|
|
499
|
+
TProvidedServices & { [K in TServiceName]: TService },
|
|
500
|
+
TThisContext
|
|
501
|
+
>({
|
|
502
|
+
name: this.#definition.name,
|
|
503
|
+
dependencies: this.#definition.dependencies,
|
|
504
|
+
services: this.#definition.services,
|
|
505
|
+
additionalContext: this.#definition.additionalContext,
|
|
506
|
+
usedServices: this.#definition.usedServices,
|
|
507
|
+
providedServices: newProvidedServices,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
75
511
|
}
|
|
76
|
-
export function defineFragment<TConfig = {}>(
|
|
512
|
+
export function defineFragment<TConfig = {}>(
|
|
513
|
+
name: string,
|
|
514
|
+
): FragmentBuilder<TConfig, {}, {}, {}, {}, {}, RequestThisContext> {
|
|
77
515
|
return new FragmentBuilder({
|
|
78
516
|
name,
|
|
79
517
|
});
|