@fragno-dev/core 0.1.11 → 0.2.0
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 +50 -42
- package/CHANGELOG.md +51 -0
- package/dist/api/api.d.ts +19 -1
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js.map +1 -1
- package/dist/api/fragment-definition-builder.d.ts +17 -7
- package/dist/api/fragment-definition-builder.d.ts.map +1 -1
- package/dist/api/fragment-definition-builder.js +3 -2
- package/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/api/fragment-instantiator.d.ts +23 -16
- package/dist/api/fragment-instantiator.d.ts.map +1 -1
- package/dist/api/fragment-instantiator.js +163 -19
- package/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/api/request-input-context.d.ts +57 -1
- package/dist/api/request-input-context.d.ts.map +1 -1
- package/dist/api/request-input-context.js +67 -0
- package/dist/api/request-input-context.js.map +1 -1
- package/dist/api/request-middleware.d.ts +1 -1
- package/dist/api/request-middleware.d.ts.map +1 -1
- package/dist/api/request-middleware.js.map +1 -1
- package/dist/api/route.d.ts +7 -7
- package/dist/api/route.d.ts.map +1 -1
- package/dist/api/route.js.map +1 -1
- package/dist/client/client.d.ts +4 -3
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +103 -7
- package/dist/client/client.js.map +1 -1
- package/dist/client/vue.d.ts +7 -3
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +16 -1
- package/dist/client/vue.js.map +1 -1
- package/dist/internal/trace-context.d.ts +23 -0
- package/dist/internal/trace-context.d.ts.map +1 -0
- package/dist/internal/trace-context.js +14 -0
- package/dist/internal/trace-context.js.map +1 -0
- package/dist/mod-client.d.ts +3 -17
- package/dist/mod-client.d.ts.map +1 -1
- package/dist/mod-client.js +20 -10
- package/dist/mod-client.js.map +1 -1
- package/dist/mod.d.ts +3 -2
- package/dist/mod.js +2 -1
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +33 -0
- package/dist/runtime.js.map +1 -0
- package/dist/test/test.d.ts +2 -2
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js.map +1 -1
- package/package.json +23 -17
- package/src/api/api.ts +22 -0
- package/src/api/fragment-definition-builder.ts +36 -17
- package/src/api/fragment-instantiator.test.ts +286 -0
- package/src/api/fragment-instantiator.ts +338 -31
- package/src/api/internal/path-runtime.test.ts +7 -0
- package/src/api/request-input-context.test.ts +152 -0
- package/src/api/request-input-context.ts +85 -0
- package/src/api/request-middleware.test.ts +47 -1
- package/src/api/request-middleware.ts +1 -1
- package/src/api/route.ts +7 -2
- package/src/client/client.test.ts +195 -0
- package/src/client/client.ts +185 -10
- package/src/client/vue.test.ts +253 -3
- package/src/client/vue.ts +44 -1
- package/src/internal/trace-context.ts +35 -0
- package/src/mod-client.ts +51 -7
- package/src/mod.ts +6 -1
- package/src/runtime.ts +48 -0
- package/src/test/test.ts +13 -4
- package/tsdown.config.ts +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/runtime.d.ts
|
|
2
|
+
type FragnoRuntime = {
|
|
3
|
+
time: {
|
|
4
|
+
now: () => Date;
|
|
5
|
+
};
|
|
6
|
+
random: {
|
|
7
|
+
float: () => number;
|
|
8
|
+
uuid: () => string;
|
|
9
|
+
cuid: () => string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
declare const defaultFragnoRuntime: FragnoRuntime;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { FragnoRuntime, defaultFragnoRuntime };
|
|
15
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","names":[],"sources":["../src/runtime.ts"],"sourcesContent":[],"mappings":";KAEY,aAAA;EAAA,IAAA,EAAA;IAoCC,GAAA,EAAA,GAAA,GAlCE,IAkCF;;;;;;;;cAAA,sBAAsB"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime.ts
|
|
4
|
+
const fallbackUuid = () => {
|
|
5
|
+
const bytes = new Uint8Array(16);
|
|
6
|
+
for (let i = 0; i < bytes.length; i += 1) bytes[i] = Math.floor(Math.random() * 256);
|
|
7
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
8
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
9
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
|
|
10
|
+
return [
|
|
11
|
+
hex.slice(0, 4).join(""),
|
|
12
|
+
hex.slice(4, 6).join(""),
|
|
13
|
+
hex.slice(6, 8).join(""),
|
|
14
|
+
hex.slice(8, 10).join(""),
|
|
15
|
+
hex.slice(10, 16).join("")
|
|
16
|
+
].join("-");
|
|
17
|
+
};
|
|
18
|
+
const defaultUuid = () => {
|
|
19
|
+
const cryptoApi = globalThis.crypto;
|
|
20
|
+
return cryptoApi?.randomUUID ? cryptoApi.randomUUID() : fallbackUuid();
|
|
21
|
+
};
|
|
22
|
+
const defaultFragnoRuntime = {
|
|
23
|
+
time: { now: () => /* @__PURE__ */ new Date() },
|
|
24
|
+
random: {
|
|
25
|
+
float: () => Math.random(),
|
|
26
|
+
uuid: () => defaultUuid(),
|
|
27
|
+
cuid: () => createId()
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { defaultFragnoRuntime };
|
|
33
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","names":["defaultFragnoRuntime: FragnoRuntime"],"sources":["../src/runtime.ts"],"sourcesContent":["import { createId } from \"@paralleldrive/cuid2\";\n\nexport type FragnoRuntime = {\n time: {\n now: () => Date;\n };\n random: {\n float: () => number;\n uuid: () => string;\n cuid: () => string;\n };\n};\n\nconst fallbackUuid = () => {\n const bytes = new Uint8Array(16);\n for (let i = 0; i < bytes.length; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n\n // RFC 4122 variant + version 4\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return [\n hex.slice(0, 4).join(\"\"),\n hex.slice(4, 6).join(\"\"),\n hex.slice(6, 8).join(\"\"),\n hex.slice(8, 10).join(\"\"),\n hex.slice(10, 16).join(\"\"),\n ].join(\"-\");\n};\n\nconst defaultUuid = () => {\n const cryptoApi = globalThis.crypto;\n return cryptoApi?.randomUUID ? cryptoApi.randomUUID() : fallbackUuid();\n};\n\nexport const defaultFragnoRuntime: FragnoRuntime = {\n time: {\n now: () => new Date(),\n },\n random: {\n float: () => Math.random(),\n uuid: () => defaultUuid(),\n cuid: () => createId(),\n },\n};\n"],"mappings":";;;AAaA,MAAM,qBAAqB;CACzB,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EACrC,OAAM,KAAK,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI;AAI5C,OAAM,KAAM,MAAM,KAAK,KAAQ;AAC/B,OAAM,KAAM,MAAM,KAAK,KAAQ;CAE/B,MAAM,MAAM,MAAM,KAAK,QAAQ,SAAS,KAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAC3E,QAAO;EACL,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;EACxB,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;EACxB,IAAI,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;EACxB,IAAI,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG;EACzB,IAAI,MAAM,IAAI,GAAG,CAAC,KAAK,GAAG;EAC3B,CAAC,KAAK,IAAI;;AAGb,MAAM,oBAAoB;CACxB,MAAM,YAAY,WAAW;AAC7B,QAAO,WAAW,aAAa,UAAU,YAAY,GAAG,cAAc;;AAGxE,MAAaA,uBAAsC;CACjD,MAAM,EACJ,2BAAW,IAAI,MAAM,EACtB;CACD,QAAQ;EACN,aAAa,KAAK,QAAQ;EAC1B,YAAY,aAAa;EACzB,YAAY,UAAU;EACvB;CACF"}
|
package/dist/test/test.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { RequestThisContext } from "../api/api.js";
|
|
|
4
4
|
import { BoundServices } from "../api/bind-services.js";
|
|
5
5
|
import { AnyRouteOrFactory, FlattenRouteFactories } from "../api/route.js";
|
|
6
6
|
import { FragnoResponse } from "../api/fragno-response.js";
|
|
7
|
-
import { FragnoInstantiatedFragment } from "../api/fragment-instantiator.js";
|
|
7
|
+
import { AnyFragnoInstantiatedFragment, FragnoInstantiatedFragment, RoutesWithInternal } from "../api/fragment-instantiator.js";
|
|
8
8
|
import { FragmentDefinition, FragmentDefinitionBuilder } from "../api/fragment-definition-builder.js";
|
|
9
9
|
|
|
10
10
|
//#region src/test/test.d.ts
|
|
@@ -87,7 +87,7 @@ interface CreateFragmentForTestOptions<TConfig, TOptions extends FragnoPublicCon
|
|
|
87
87
|
* }
|
|
88
88
|
* ```
|
|
89
89
|
*/
|
|
90
|
-
declare function createFragmentForTest<TConfig, TOptions extends FragnoPublicConfig, TDeps, TBaseServices extends Record<string, unknown>, TServices extends Record<string, unknown>, TServiceDependencies, TPrivateServices extends Record<string, unknown>, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, const TRoutesOrFactories extends readonly AnyRouteOrFactory[]>(definition: FragmentDefinition<TConfig, TOptions, TDeps, TBaseServices, TServices, TServiceDependencies, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage>, routesOrFactories: TRoutesOrFactories, options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>): FragnoInstantiatedFragment<FlattenRouteFactories<TRoutesOrFactories>, TDeps, BoundServices<TBaseServices & TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage>;
|
|
90
|
+
declare function createFragmentForTest<TConfig, TOptions extends FragnoPublicConfig, TDeps, TBaseServices extends Record<string, unknown>, TServices extends Record<string, unknown>, TServiceDependencies, TPrivateServices extends Record<string, unknown>, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, const TRoutesOrFactories extends readonly AnyRouteOrFactory[], TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {}>(definition: FragmentDefinition<TConfig, TOptions, TDeps, TBaseServices, TServices, TServiceDependencies, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage, TLinkedFragments>, routesOrFactories: TRoutesOrFactories, options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>): FragnoInstantiatedFragment<RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TLinkedFragments>, TDeps, BoundServices<TBaseServices & TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage, TOptions, TLinkedFragments>;
|
|
91
91
|
//#endregion
|
|
92
92
|
export { CreateFragmentForTestOptions, type FragnoResponse, type RouteHandlerInputOptions, TestBaseServices, createFragmentForTest, withTestUtils };
|
|
93
93
|
//# sourceMappingURL=test.d.ts.map
|
package/dist/test/test.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","names":[],"sources":["../../src/test/test.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"test.d.ts","names":[],"sources":["../../src/test/test.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;KAoBY;;AAAZ;AAsBA;EAGqB,IAAA,EArBb,KAqBa;CAMW;;;;;;;;;;;;;;;;AAoB5B,iBA7BY,aAAA,CAAA,CA6BZ,EAAA,CAAA,OAAA,EAAA,iBA1BiB,kBA0BjB,EAAA,KAAA,EAAA,aAAA,EAAA,SAAA,EAAA,YAAA,EAAA,gBAAA,EAAA,4BApB4B,kBAoB5B,EAAA,4BAnB4B,kBAmB5B,EAAA,eAAA,CAAA,CAAA,OAAA,EAhBS,yBAgBT,CAfE,OAeF,EAdE,QAcF,EAbE,KAaF,EAZE,aAYF,EAXE,SAWF,EAVE,YAUF,EATE,gBASF,EARE,mBAQF,EAPE,mBAOF,EANE,eAMF,CAAA,EAAA,GAJC,yBAID,CAHA,OAGA,EAFA,QAEA,EADA,KACA,EAAA,aAAA,GAAgB,gBAAhB,CAAiC,KAAjC,CAAA,EACA,SADA,EAEA,YAFA,EAGA,gBAHA,EAIA,mBAJA,EAKA,mBALA,EAMA,eANA,CAAA;;;;AAEA,UA+Da,4BA/Db,CAAA,OAAA,EAAA,iBAiEe,kBAjEf,EAAA,oBAAA,CAAA,CAAA;EACA,MAAA,EAmEM,OAnEN;EACA,OAAA,CAAA,EAmEQ,OAnER,CAmEgB,QAnEhB,CAAA;EACA,sBAAA,CAAA,EAmEuB,oBAnEvB;;;;AA4DJ;;;;;;;AA2DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG,iBA7Ba,qBA6Bb,CAAA,OAAA,EAAA,iBA3BgB,kBA2BhB,EAAA,KAAA,EAAA,sBAzBqB,MAyBrB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,kBAxBiB,MAwBjB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,oBAAA,EAAA,yBAtBwB,MAsBxB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,4BArB2B,kBAqB3B,EAAA,4BApB2B,kBAoB3B,EAAA,eAAA,EAAA,iCAAA,SAlByC,iBAkBzC,EAAA,EAAA,yBAjBwB,MAiBxB,CAAA,MAAA,EAjBuC,6BAiBvC,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,UAAA,EAfW,kBAeX,CAdC,OAcD,EAbC,QAaD,EAZC,KAYD,EAXC,aAWD,EAVC,SAUD,EATC,oBASD,EARC,gBAQD,EAPC,mBAOD,EANC,mBAMD,EALC,eAKD,EAJC,gBAID,CAAA,EAAA,iBAAA,EAFkB,kBAElB,EAAA,OAAA,EADQ,4BACR,CADqC,OACrC,EAD8C,QAC9C,EADwD,oBACxD,CAAA,CAAA,EAAA,0BAAA,CACD,kBADC,CACkB,qBADlB,CACwC,kBADxC,CAAA,EAC6D,gBAD7D,CAAA,EAED,KAFC,EAGD,aAHC,CAGa,aAHb,GAG6B,SAH7B,CAAA,EAID,mBAJC,EAKD,mBALC,EAMD,eANC,EAOD,QAPC,EAQD,gBARC,CAAA"}
|
package/dist/test/test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.js","names":["newBaseServices: ServiceConstructorFn<\n TConfig,\n TOptions,\n TDeps,\n TServiceDeps,\n TPrivateServices,\n TBaseServices & TestBaseServices<TDeps>,\n TServiceThisContext\n >","testUtils: TestBaseServices<TDeps>"],"sources":["../../src/test/test.ts"],"sourcesContent":["import type { RequestThisContext } from \"../api/api\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"../api/route\";\nimport type { FragnoPublicConfig } from \"../api/shared-types\";\nimport {\n FragmentDefinitionBuilder,\n type FragmentDefinition,\n type ServiceConstructorFn,\n} from \"../api/fragment-definition-builder\";\nimport {
|
|
1
|
+
{"version":3,"file":"test.js","names":["newBaseServices: ServiceConstructorFn<\n TConfig,\n TOptions,\n TDeps,\n TServiceDeps,\n TPrivateServices,\n TBaseServices & TestBaseServices<TDeps>,\n TServiceThisContext\n >","testUtils: TestBaseServices<TDeps>"],"sources":["../../src/test/test.ts"],"sourcesContent":["import type { RequestThisContext } from \"../api/api\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"../api/route\";\nimport type { FragnoPublicConfig } from \"../api/shared-types\";\nimport {\n FragmentDefinitionBuilder,\n type FragmentDefinition,\n type ServiceConstructorFn,\n} from \"../api/fragment-definition-builder\";\nimport {\n instantiateFragment,\n type AnyFragnoInstantiatedFragment,\n type FragnoInstantiatedFragment,\n type RoutesWithInternal,\n} from \"../api/fragment-instantiator\";\nimport type { BoundServices } from \"../api/bind-services\";\n\n// Re-export for convenience\nexport type { RouteHandlerInputOptions } from \"../api/route-handler-input-options\";\nexport type { FragnoResponse } from \"../api/fragno-response\";\n\nexport type TestBaseServices<TDeps> = {\n /**\n * Access to fragment dependencies for testing purposes.\n */\n deps: TDeps;\n};\n\n/**\n * Extension function that adds test utilities to a fragment definition.\n * This adds a `test` service to base services with access to deps.\n *\n * @example\n * ```typescript\n * const definition = defineFragment(\"my-fragment\")\n * .withDependencies(({ config }) => ({ client: createClient(config) }))\n * .extend(withTestUtils())\n * .build();\n *\n * const fragment = createFragmentForTest(definition, [], { config: {...} });\n * expect(fragment.services.test.deps.client).toBeDefined();\n * ```\n */\nexport function withTestUtils() {\n return <\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n >(\n builder: FragmentDefinitionBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n >,\n ): FragmentDefinitionBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices & TestBaseServices<TDeps>,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n > => {\n // Get the current base services factory\n const currentBaseDef = builder.build();\n const currentBaseServices = currentBaseDef.baseServices;\n\n // Create new base services factory that merges test utilities\n const newBaseServices: ServiceConstructorFn<\n TConfig,\n TOptions,\n TDeps,\n TServiceDeps,\n TPrivateServices,\n TBaseServices & TestBaseServices<TDeps>,\n TServiceThisContext\n > = (context) => {\n // Call existing base services if they exist\n const existingServices = currentBaseServices\n ? currentBaseServices(context)\n : ({} as TBaseServices);\n\n // Add test utilities\n const testUtils: TestBaseServices<TDeps> = {\n deps: context.deps,\n };\n\n return {\n ...existingServices,\n ...testUtils,\n } as TBaseServices & TestBaseServices<TDeps>;\n };\n\n // Create new builder with updated base services\n return new FragmentDefinitionBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices & TestBaseServices<TDeps>,\n TServices,\n TServiceDeps,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage\n >(builder.name, {\n dependencies: currentBaseDef.dependencies,\n baseServices: newBaseServices,\n namedServices: currentBaseDef.namedServices,\n privateServices: currentBaseDef.privateServices,\n serviceDependencies: currentBaseDef.serviceDependencies,\n createRequestStorage: currentBaseDef.createRequestStorage,\n createThisContext: currentBaseDef.createThisContext,\n });\n };\n}\n\n/**\n * Options for creating a test fragment with the new architecture\n */\nexport interface CreateFragmentForTestOptions<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TServiceDependencies,\n> {\n config: TConfig;\n options?: Partial<TOptions>;\n serviceImplementations?: TServiceDependencies;\n}\n\n/**\n * Create a fragment instance for testing with optional service dependency overrides.\n * This uses the new fragment definition and instantiation architecture.\n *\n * **Important:** Use `.extend(withTestUtils())` before `.build()` to expose deps via `services.test.deps`.\n *\n * Returns an instantiated fragment with:\n * - `services` - Services (base + named) from the fragment definition\n * - `services.test.deps` - Dependencies (only if you used .extend(withTestUtils()))\n * - `callRoute()` - Type-safe method to call routes directly\n * - `handler()` - Request handler for integration\n * - `inContext()` - Run code within request context\n *\n * @param definition - The fragment definition from builder.build()\n * @param routesOrFactories - Route configurations or route factories\n * @param options - Configuration and optional overrides\n * @returns An instantiated fragment ready for testing\n *\n * @example\n * ```typescript\n * const definition = defineFragment<{ apiKey: string }>(\"test\")\n * .withDependencies(({ config }) => ({ client: createClient(config.apiKey) }))\n * .providesService(\"api\", ({ deps }) => ({ call: () => deps.client.call() }))\n * .extend(withTestUtils()) // <- Add test utilities\n * .build();\n *\n * const fragment = createFragmentForTest(\n * definition,\n * [route1, route2],\n * {\n * config: { apiKey: \"test-key\" },\n * options: { mountRoute: \"/api/test\" }\n * }\n * );\n *\n * // Access deps via test utilities\n * expect(fragment.services.test.deps.client).toBeDefined();\n * expect(fragment.services.api.call()).toBeDefined();\n *\n * // Call routes directly by method and path\n * const response = await fragment.callRoute(\"POST\", \"/login\", {\n * body: { username: \"test\", password: \"test123\" }\n * });\n *\n * if (response.type === 'json') {\n * expect(response.data).toEqual({...});\n * }\n * ```\n */\nexport function createFragmentForTest<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {},\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n routesOrFactories: TRoutesOrFactories,\n options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>,\n): FragnoInstantiatedFragment<\n RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TLinkedFragments>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\n> {\n const { config, options: fragmentOptions = {} as TOptions, serviceImplementations } = options;\n\n // Use default mountRoute for testing if not provided\n const fullOptions = {\n mountRoute: \"/api/test\",\n ...fragmentOptions,\n } as TOptions;\n\n // Instantiate and return the fragment directly\n return instantiateFragment(\n definition,\n config,\n routesOrFactories,\n fullOptions,\n serviceImplementations,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,gBAAgB;AAC9B,SAYE,YAuBG;EAEH,MAAM,iBAAiB,QAAQ,OAAO;EACtC,MAAM,sBAAsB,eAAe;EAG3C,MAAMA,mBAQD,YAAY;GAEf,MAAM,mBAAmB,sBACrB,oBAAoB,QAAQ,GAC3B,EAAE;GAGP,MAAMC,YAAqC,EACzC,MAAM,QAAQ,MACf;AAED,UAAO;IACL,GAAG;IACH,GAAG;IACJ;;AAIH,SAAO,IAAI,0BAWT,QAAQ,MAAM;GACd,cAAc,eAAe;GAC7B,cAAc;GACd,eAAe,eAAe;GAC9B,iBAAiB,eAAe;GAChC,qBAAqB,eAAe;GACpC,sBAAsB,eAAe;GACrC,mBAAmB,eAAe;GACnC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEN,SAAgB,sBAcd,YAaA,mBACA,SAUA;CACA,MAAM,EAAE,QAAQ,SAAS,kBAAkB,EAAE,EAAc,2BAA2B;AAStF,QAAO,oBACL,YACA,QACA,mBATkB;EAClB,YAAY;EACZ,GAAG;EACJ,EAQC,uBACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"workerd": {
|
|
@@ -40,6 +40,11 @@
|
|
|
40
40
|
"types": "./dist/api/request-context-storage.d.ts",
|
|
41
41
|
"default": "./dist/api/request-context-storage.js"
|
|
42
42
|
},
|
|
43
|
+
"./internal/trace-context": {
|
|
44
|
+
"development": "./src/internal/trace-context.ts",
|
|
45
|
+
"types": "./dist/internal/trace-context.d.ts",
|
|
46
|
+
"default": "./dist/internal/trace-context.js"
|
|
47
|
+
},
|
|
43
48
|
"./internal/symbols": {
|
|
44
49
|
"development": "./src/internal/symbols.ts",
|
|
45
50
|
"types": "./dist/internal/symbols.d.ts",
|
|
@@ -88,29 +93,30 @@
|
|
|
88
93
|
"type": "module",
|
|
89
94
|
"sideEffects": false,
|
|
90
95
|
"dependencies": {
|
|
96
|
+
"@paralleldrive/cuid2": "^2.3.1",
|
|
91
97
|
"@nanostores/query": "^0.3.4",
|
|
92
98
|
"@nanostores/solid": "^1.1.1",
|
|
93
|
-
"@standard-schema/spec": "^1.
|
|
94
|
-
"nanostores": "^1.0
|
|
95
|
-
"rou3": "^0.7.
|
|
99
|
+
"@standard-schema/spec": "^1.1.0",
|
|
100
|
+
"nanostores": "^1.1.0",
|
|
101
|
+
"rou3": "^0.7.12"
|
|
96
102
|
},
|
|
97
103
|
"devDependencies": {
|
|
98
|
-
"@sveltejs/vite-plugin-svelte": "^6.2.
|
|
99
|
-
"@testing-library/react": "^16.3.
|
|
100
|
-
"@testing-library/svelte": "^5.
|
|
104
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
105
|
+
"@testing-library/react": "^16.3.2",
|
|
106
|
+
"@testing-library/svelte": "^5.3.1",
|
|
101
107
|
"@testing-library/vue": "^8.1.0",
|
|
102
|
-
"@types/node": "^22",
|
|
103
|
-
"@types/react": "^19.
|
|
104
|
-
"@types/react-dom": "^19.
|
|
108
|
+
"@types/node": "^22.19.7",
|
|
109
|
+
"@types/react": "^19.2.8",
|
|
110
|
+
"@types/react-dom": "^19.2.3",
|
|
105
111
|
"@vitest/coverage-istanbul": "^3.2.4",
|
|
106
|
-
"happy-dom": "^20.
|
|
107
|
-
"react": "^19.0.
|
|
108
|
-
"react-dom": "^19.0.
|
|
109
|
-
"
|
|
110
|
-
"
|
|
112
|
+
"happy-dom": "^20.3.3",
|
|
113
|
+
"react": "^19.0.3",
|
|
114
|
+
"react-dom": "^19.0.3",
|
|
115
|
+
"solid-js": "^1.9.10",
|
|
116
|
+
"svelte": "^5.47.0",
|
|
111
117
|
"vitest": "^3.2.4",
|
|
112
|
-
"vue": "^3",
|
|
113
|
-
"zod": "^4.
|
|
118
|
+
"vue": "^3.5.27",
|
|
119
|
+
"zod": "^4.3.5",
|
|
114
120
|
"@fragno-private/typescript-config": "0.0.1",
|
|
115
121
|
"@fragno-private/vitest-config": "0.0.0"
|
|
116
122
|
},
|
package/src/api/api.ts
CHANGED
|
@@ -27,6 +27,17 @@ export type ValidPath<T extends string = string> = T extends `/${infer Rest}`
|
|
|
27
27
|
|
|
28
28
|
export interface RequestThisContext {}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Content types that can be accepted by a route.
|
|
32
|
+
*
|
|
33
|
+
* - `"application/json"` (default): JSON request body, validated against inputSchema
|
|
34
|
+
* - `"multipart/form-data"`: FormData request body (file uploads), no schema validation
|
|
35
|
+
*/
|
|
36
|
+
export type RouteContentType =
|
|
37
|
+
| "application/json"
|
|
38
|
+
| "multipart/form-data"
|
|
39
|
+
| "application/octet-stream";
|
|
40
|
+
|
|
30
41
|
export interface FragnoRouteConfig<
|
|
31
42
|
TMethod extends HTTPMethod,
|
|
32
43
|
TPath extends string,
|
|
@@ -38,6 +49,17 @@ export interface FragnoRouteConfig<
|
|
|
38
49
|
> {
|
|
39
50
|
method: TMethod;
|
|
40
51
|
path: TPath;
|
|
52
|
+
/**
|
|
53
|
+
* The expected content type for this route's request body.
|
|
54
|
+
*
|
|
55
|
+
* - `"application/json"` (default): Expects JSON body, will be validated against inputSchema
|
|
56
|
+
* - `"multipart/form-data"`: Expects FormData body (for file uploads), use `ctx.formData()` in handler
|
|
57
|
+
*
|
|
58
|
+
* The server will reject requests with mismatched Content-Type headers.
|
|
59
|
+
*
|
|
60
|
+
* @default "application/json"
|
|
61
|
+
*/
|
|
62
|
+
contentType?: RouteContentType;
|
|
41
63
|
inputSchema?: TInputSchema;
|
|
42
64
|
outputSchema?: TOutputSchema;
|
|
43
65
|
errorCodes?: readonly TErrorCode[];
|
|
@@ -24,12 +24,12 @@ export type LinkedFragmentCallback<
|
|
|
24
24
|
TConfig,
|
|
25
25
|
TOptions extends FragnoPublicConfig,
|
|
26
26
|
TServiceDependencies,
|
|
27
|
+
TFragment extends AnyFragnoInstantiatedFragment = AnyFragnoInstantiatedFragment,
|
|
27
28
|
> = (context: {
|
|
28
29
|
config: TConfig;
|
|
29
30
|
options: TOptions;
|
|
30
31
|
serviceDependencies?: TServiceDependencies;
|
|
31
|
-
|
|
32
|
-
}) => FragnoInstantiatedFragment<any, any, any, any, any, any, any>;
|
|
32
|
+
}) => TFragment;
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Extract the services type from a FragnoInstantiatedFragment
|
|
@@ -223,12 +223,27 @@ export interface FragmentDefinition<
|
|
|
223
223
|
deps: TDeps;
|
|
224
224
|
}) => RequestContextStorage<TRequestStorage>;
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Optional factory for internal data attached to fragment.$internal.
|
|
228
|
+
*/
|
|
229
|
+
internalDataFactory?: (context: {
|
|
230
|
+
config: TConfig;
|
|
231
|
+
options: TOptions;
|
|
232
|
+
deps: TDeps;
|
|
233
|
+
linkedFragments: TLinkedFragments;
|
|
234
|
+
}) => Record<string, unknown> | void;
|
|
235
|
+
|
|
226
236
|
/**
|
|
227
237
|
* Optional linked fragments that will be automatically instantiated with this fragment.
|
|
228
238
|
* Linked fragments are service-only and share the same config/options as the parent.
|
|
229
239
|
*/
|
|
230
240
|
linkedFragments?: {
|
|
231
|
-
[K in keyof TLinkedFragments]: LinkedFragmentCallback<
|
|
241
|
+
[K in keyof TLinkedFragments]: LinkedFragmentCallback<
|
|
242
|
+
TConfig,
|
|
243
|
+
TOptions,
|
|
244
|
+
TServiceDependencies,
|
|
245
|
+
TLinkedFragments[K]
|
|
246
|
+
>;
|
|
232
247
|
};
|
|
233
248
|
|
|
234
249
|
$serviceThisContext?: TServiceThisContext;
|
|
@@ -307,7 +322,12 @@ export class FragmentDefinitionBuilder<
|
|
|
307
322
|
deps: TDeps;
|
|
308
323
|
}) => RequestContextStorage<TRequestStorage>;
|
|
309
324
|
#linkedFragments?: {
|
|
310
|
-
[K in keyof TLinkedFragments]: LinkedFragmentCallback<
|
|
325
|
+
[K in keyof TLinkedFragments]: LinkedFragmentCallback<
|
|
326
|
+
TConfig,
|
|
327
|
+
TOptions,
|
|
328
|
+
TServiceDependencies,
|
|
329
|
+
TLinkedFragments[K]
|
|
330
|
+
>;
|
|
311
331
|
};
|
|
312
332
|
|
|
313
333
|
constructor(
|
|
@@ -368,7 +388,8 @@ export class FragmentDefinitionBuilder<
|
|
|
368
388
|
[K in keyof TLinkedFragments]: LinkedFragmentCallback<
|
|
369
389
|
TConfig,
|
|
370
390
|
TOptions,
|
|
371
|
-
TServiceDependencies
|
|
391
|
+
TServiceDependencies,
|
|
392
|
+
TLinkedFragments[K]
|
|
372
393
|
>;
|
|
373
394
|
};
|
|
374
395
|
},
|
|
@@ -966,15 +987,13 @@ export class FragmentDefinitionBuilder<
|
|
|
966
987
|
|
|
967
988
|
/**
|
|
968
989
|
* Register a linked fragment that will be automatically instantiated.
|
|
969
|
-
* Linked fragments
|
|
970
|
-
*
|
|
990
|
+
* Linked fragments share the same config/options as the parent and their services
|
|
991
|
+
* are exposed as private services. Routes are not exposed by default, but the
|
|
992
|
+
* instantiator may mount internal linked fragment routes under an internal prefix.
|
|
971
993
|
*/
|
|
972
|
-
withLinkedFragment<
|
|
973
|
-
const TName extends string,
|
|
974
|
-
TCallback extends LinkedFragmentCallback<TConfig, TOptions, TServiceDependencies>,
|
|
975
|
-
>(
|
|
994
|
+
withLinkedFragment<const TName extends string, TFragment extends AnyFragnoInstantiatedFragment>(
|
|
976
995
|
name: TName,
|
|
977
|
-
callback:
|
|
996
|
+
callback: LinkedFragmentCallback<TConfig, TOptions, TServiceDependencies, TFragment>,
|
|
978
997
|
): FragmentDefinitionBuilder<
|
|
979
998
|
TConfig,
|
|
980
999
|
TOptions,
|
|
@@ -982,18 +1001,18 @@ export class FragmentDefinitionBuilder<
|
|
|
982
1001
|
TBaseServices,
|
|
983
1002
|
TServices,
|
|
984
1003
|
TServiceDependencies,
|
|
985
|
-
TPrivateServices & ExtractLinkedServices<
|
|
1004
|
+
TPrivateServices & ExtractLinkedServices<() => TFragment>,
|
|
986
1005
|
TServiceThisContext,
|
|
987
1006
|
THandlerThisContext,
|
|
988
1007
|
TRequestStorage,
|
|
989
|
-
TLinkedFragments & { [K in TName]:
|
|
1008
|
+
TLinkedFragments & { [K in TName]: TFragment }
|
|
990
1009
|
> {
|
|
991
1010
|
const newLinkedFragments = {
|
|
992
1011
|
...this.#linkedFragments,
|
|
993
1012
|
[name]: callback,
|
|
994
1013
|
};
|
|
995
1014
|
|
|
996
|
-
// Cast is safe: We're declaring that the returned builder has TPrivateServices & ExtractLinkedServices<
|
|
1015
|
+
// Cast is safe: We're declaring that the returned builder has TPrivateServices & ExtractLinkedServices<TFragment>,
|
|
997
1016
|
// even though the runtime privateServices hasn't changed yet. The linked fragment services will be
|
|
998
1017
|
// merged into privateServices at instantiation time by the instantiator.
|
|
999
1018
|
return new FragmentDefinitionBuilder(this.#name, {
|
|
@@ -1013,11 +1032,11 @@ export class FragmentDefinitionBuilder<
|
|
|
1013
1032
|
TBaseServices,
|
|
1014
1033
|
TServices,
|
|
1015
1034
|
TServiceDependencies,
|
|
1016
|
-
TPrivateServices & ExtractLinkedServices<
|
|
1035
|
+
TPrivateServices & ExtractLinkedServices<() => TFragment>,
|
|
1017
1036
|
TServiceThisContext,
|
|
1018
1037
|
THandlerThisContext,
|
|
1019
1038
|
TRequestStorage,
|
|
1020
|
-
TLinkedFragments & { [K in TName]:
|
|
1039
|
+
TLinkedFragments & { [K in TName]: TFragment }
|
|
1021
1040
|
>;
|
|
1022
1041
|
}
|
|
1023
1042
|
|