@fragno-dev/core 0.1.11 → 0.2.2
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 +87 -69
- package/CHANGELOG.md +79 -0
- package/dist/api/api.d.ts +21 -2
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js +2 -1
- package/dist/api/api.js.map +1 -1
- package/dist/api/bind-services.d.ts +0 -1
- package/dist/api/bind-services.d.ts.map +1 -1
- package/dist/api/bind-services.js.map +1 -1
- package/dist/api/error.d.ts.map +1 -1
- package/dist/api/error.js.map +1 -1
- package/dist/api/fragment-definition-builder.d.ts +32 -40
- package/dist/api/fragment-definition-builder.d.ts.map +1 -1
- package/dist/api/fragment-definition-builder.js +15 -21
- package/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/api/fragment-instantiator.d.ts +51 -30
- package/dist/api/fragment-instantiator.d.ts.map +1 -1
- package/dist/api/fragment-instantiator.js +201 -52
- package/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/api/request-context-storage.d.ts +4 -0
- package/dist/api/request-context-storage.d.ts.map +1 -1
- package/dist/api/request-context-storage.js +6 -0
- package/dist/api/request-context-storage.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 +2 -2
- package/dist/api/request-middleware.d.ts.map +1 -1
- package/dist/api/request-middleware.js.map +1 -1
- package/dist/api/request-output-context.d.ts +1 -1
- package/dist/api/request-output-context.d.ts.map +1 -1
- package/dist/api/request-output-context.js.map +1 -1
- package/dist/api/route-caller.d.ts +30 -0
- package/dist/api/route-caller.d.ts.map +1 -0
- package/dist/api/route-caller.js +63 -0
- package/dist/api/route-caller.js.map +1 -0
- package/dist/api/route-handler-input-options.d.ts.map +1 -1
- package/dist/api/route.d.ts +8 -8
- package/dist/api/route.d.ts.map +1 -1
- package/dist/api/route.js.map +1 -1
- package/dist/api/shared-types.d.ts.map +1 -1
- package/dist/client/client-error.d.ts.map +1 -1
- package/dist/client/client-error.js.map +1 -1
- package/dist/client/client.d.ts +90 -50
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +128 -16
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.svelte.d.ts +6 -5
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +10 -2
- package/dist/client/client.svelte.js.map +1 -1
- package/dist/client/internal/ndjson-streaming.js.map +1 -1
- package/dist/client/react.d.ts +5 -4
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +104 -12
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +7 -5
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +23 -9
- package/dist/client/solid.js.map +1 -1
- package/dist/client/vanilla.d.ts +16 -4
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +21 -1
- package/dist/client/vanilla.js.map +1 -1
- package/dist/client/vue.d.ts +10 -4
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +24 -1
- package/dist/client/vue.js.map +1 -1
- package/dist/id.d.ts +2 -0
- package/dist/id.js +3 -0
- package/dist/internal/cuid.d.ts +16 -0
- package/dist/internal/cuid.d.ts.map +1 -0
- package/dist/internal/cuid.js +82 -0
- package/dist/internal/cuid.js.map +1 -0
- 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 +7 -20
- package/dist/mod-client.d.ts.map +1 -1
- package/dist/mod-client.js +25 -13
- package/dist/mod-client.js.map +1 -1
- package/dist/mod.d.ts +8 -6
- package/dist/mod.js +3 -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 +6 -6
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js.map +1 -1
- package/dist/util/ssr.js.map +1 -1
- package/package.json +42 -52
- package/src/api/api.test.ts +3 -1
- package/src/api/api.ts +28 -0
- package/src/api/bind-services.ts +0 -5
- package/src/api/error.ts +1 -0
- package/src/api/fragment-definition-builder.extend.test.ts +2 -1
- package/src/api/fragment-definition-builder.test.ts +2 -1
- package/src/api/fragment-definition-builder.ts +56 -112
- package/src/api/fragment-instantiator.test.ts +311 -166
- package/src/api/fragment-instantiator.ts +470 -131
- package/src/api/fragment-services.test.ts +1 -0
- package/src/api/internal/path-runtime.test.ts +8 -0
- package/src/api/internal/path-type.test.ts +3 -1
- package/src/api/internal/route.test.ts +1 -0
- package/src/api/request-context-storage.ts +7 -0
- package/src/api/request-input-context.test.ts +156 -2
- package/src/api/request-input-context.ts +87 -1
- package/src/api/request-middleware.test.ts +43 -2
- package/src/api/request-middleware.ts +4 -3
- package/src/api/request-output-context.test.ts +3 -1
- package/src/api/request-output-context.ts +2 -1
- package/src/api/route-caller.test.ts +195 -0
- package/src/api/route-caller.ts +167 -0
- package/src/api/route-handler-input-options.ts +2 -1
- package/src/api/route.test.ts +4 -2
- package/src/api/route.ts +9 -3
- package/src/api/shared-types.ts +2 -1
- package/src/client/client-builder.test.ts +4 -2
- package/src/client/client-error.test.ts +2 -1
- package/src/client/client-error.ts +1 -1
- package/src/client/client-types.test.ts +19 -5
- package/src/client/client.ssr.test.ts +6 -4
- package/src/client/client.svelte.test.ts +18 -9
- package/src/client/client.svelte.ts +38 -13
- package/src/client/client.test.ts +244 -10
- package/src/client/client.ts +473 -148
- package/src/client/internal/ndjson-streaming.test.ts +6 -3
- package/src/client/internal/ndjson-streaming.ts +1 -0
- package/src/client/react.test.ts +176 -6
- package/src/client/react.ts +226 -31
- package/src/client/solid.test.ts +29 -5
- package/src/client/solid.ts +60 -22
- package/src/client/vanilla.test.ts +148 -6
- package/src/client/vanilla.ts +63 -9
- package/src/client/vue.test.ts +397 -8
- package/src/client/vue.ts +74 -4
- package/src/id.ts +1 -0
- package/src/internal/cuid.test.ts +164 -0
- package/src/internal/cuid.ts +133 -0
- package/src/internal/trace-context.ts +35 -0
- package/src/mod-client.ts +55 -9
- package/src/mod.ts +9 -3
- package/src/runtime.ts +48 -0
- package/src/test/test.test.ts +4 -2
- package/src/test/test.ts +14 -7
- package/src/util/async.test.ts +1 -0
- package/src/util/content-type.test.ts +1 -0
- package/src/util/nanostores.test.ts +3 -1
- package/src/util/ssr.ts +1 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +2 -0
- package/vitest.config.ts +2 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const defaultLength = 24;
|
|
2
|
+
const bigLength = 32;
|
|
3
|
+
const initialCountMax = 476782367;
|
|
4
|
+
const randomPoolSize = 128;
|
|
5
|
+
const uint32ToFloatScale = 1 / 4294967296;
|
|
6
|
+
|
|
7
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz";
|
|
8
|
+
const entropyAlphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
9
|
+
|
|
10
|
+
const createCryptoRandom = (cryptoApi = globalThis.crypto) => {
|
|
11
|
+
if (!cryptoApi?.getRandomValues) {
|
|
12
|
+
throw new Error("Fragno cuid requires globalThis.crypto.getRandomValues()");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const values = new Uint32Array(randomPoolSize);
|
|
16
|
+
let offset = values.length;
|
|
17
|
+
|
|
18
|
+
return () => {
|
|
19
|
+
if (offset >= values.length) {
|
|
20
|
+
cryptoApi.getRandomValues(values);
|
|
21
|
+
offset = 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const value = values[offset]!;
|
|
25
|
+
offset += 1;
|
|
26
|
+
return value * uint32ToFloatScale;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const cryptoRandom = (() => {
|
|
31
|
+
let random: (() => number) | undefined;
|
|
32
|
+
|
|
33
|
+
return () => {
|
|
34
|
+
random ??= createCryptoRandom();
|
|
35
|
+
return random();
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
const pickIndex = (random: () => number, length: number) => {
|
|
40
|
+
const value = random();
|
|
41
|
+
|
|
42
|
+
if (value <= 0) {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (value >= 1) {
|
|
47
|
+
return length - 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Math.floor(value * length);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const createEntropy = (length = 4, random = cryptoRandom) => {
|
|
54
|
+
let entropy = "";
|
|
55
|
+
|
|
56
|
+
while (entropy.length < length) {
|
|
57
|
+
entropy += entropyAlphabet[pickIndex(random, entropyAlphabet.length)] ?? "0";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return entropy;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const hash53 = (input: string, seed = 0) => {
|
|
64
|
+
let h1 = 0xdeadbeef ^ seed;
|
|
65
|
+
let h2 = 0x41c6ce57 ^ seed;
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
68
|
+
const code = input.charCodeAt(i);
|
|
69
|
+
h1 = Math.imul(h1 ^ code, 2654435761);
|
|
70
|
+
h2 = Math.imul(h2 ^ code, 1597334677);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
74
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
75
|
+
|
|
76
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const hash = (input: string, length = bigLength) => {
|
|
80
|
+
let output = "";
|
|
81
|
+
let seed = 0;
|
|
82
|
+
|
|
83
|
+
while (output.length < length) {
|
|
84
|
+
output += hash53(`${seed}:${input}`, seed).toString(36);
|
|
85
|
+
seed += 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return output.slice(0, length);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const createFingerprint = ({
|
|
92
|
+
random = cryptoRandom,
|
|
93
|
+
}: {
|
|
94
|
+
random?: () => number;
|
|
95
|
+
} = {}) => createEntropy(bigLength, random);
|
|
96
|
+
|
|
97
|
+
const createCounter = (count: number) => () => count++;
|
|
98
|
+
|
|
99
|
+
export const init = ({
|
|
100
|
+
random = cryptoRandom,
|
|
101
|
+
counter,
|
|
102
|
+
length = defaultLength,
|
|
103
|
+
fingerprint,
|
|
104
|
+
}: {
|
|
105
|
+
random?: () => number;
|
|
106
|
+
counter?: () => number;
|
|
107
|
+
length?: number;
|
|
108
|
+
fingerprint?: string;
|
|
109
|
+
} = {}) => {
|
|
110
|
+
const normalizedLength = Math.max(1, Math.floor(length));
|
|
111
|
+
let resolvedCounter = counter;
|
|
112
|
+
let resolvedFingerprint = fingerprint;
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
resolvedCounter ??= createCounter(pickIndex(random, initialCountMax));
|
|
116
|
+
resolvedFingerprint ??= createFingerprint({ random });
|
|
117
|
+
|
|
118
|
+
const firstLetter = alphabet[pickIndex(random, alphabet.length)] ?? "a";
|
|
119
|
+
|
|
120
|
+
if (normalizedLength === 1) {
|
|
121
|
+
return firstLetter;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const time = Date.now().toString(36);
|
|
125
|
+
const count = resolvedCounter().toString(36);
|
|
126
|
+
const salt = createEntropy(normalizedLength, random);
|
|
127
|
+
const body = hash(`${time}:${salt}:${count}:${resolvedFingerprint}`, normalizedLength - 1);
|
|
128
|
+
return `${firstLetter}${body}`;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Safe to keep at module scope: init() defers all randomness until the first ID request.
|
|
133
|
+
export const createId = init();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
|
|
3
|
+
export type FragnoCoreTraceEvent =
|
|
4
|
+
| {
|
|
5
|
+
type: "route-input";
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
pathParams: Record<string, string>;
|
|
9
|
+
queryParams: [string, string][];
|
|
10
|
+
headers: [string, string][];
|
|
11
|
+
body: unknown;
|
|
12
|
+
}
|
|
13
|
+
| {
|
|
14
|
+
type: "middleware-decision";
|
|
15
|
+
method: string;
|
|
16
|
+
path: string;
|
|
17
|
+
outcome: "allow" | "deny";
|
|
18
|
+
status?: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type FragnoTraceRecorder = (event: FragnoCoreTraceEvent) => void;
|
|
22
|
+
|
|
23
|
+
const traceStorage = new AsyncLocalStorage<FragnoTraceRecorder>();
|
|
24
|
+
|
|
25
|
+
export const runWithTraceRecorder = <T>(recorder: FragnoTraceRecorder, callback: () => T): T =>
|
|
26
|
+
traceStorage.run(recorder, callback);
|
|
27
|
+
|
|
28
|
+
export const getTraceRecorder = (): FragnoTraceRecorder | undefined => traceStorage.getStore();
|
|
29
|
+
|
|
30
|
+
export const recordTraceEvent = (event: FragnoCoreTraceEvent): void => {
|
|
31
|
+
const recorder = traceStorage.getStore();
|
|
32
|
+
if (recorder) {
|
|
33
|
+
recorder(event);
|
|
34
|
+
}
|
|
35
|
+
};
|
package/src/mod-client.ts
CHANGED
|
@@ -35,7 +35,7 @@ export function defineFragment(_name: string) {
|
|
|
35
35
|
name: _name,
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
const
|
|
38
|
+
const stubMethods = {
|
|
39
39
|
withDependencies: () => stub,
|
|
40
40
|
providesBaseService: () => stub,
|
|
41
41
|
providesService: () => stub,
|
|
@@ -45,10 +45,25 @@ export function defineFragment(_name: string) {
|
|
|
45
45
|
withRequestStorage: () => stub,
|
|
46
46
|
withExternalRequestStorage: () => stub,
|
|
47
47
|
withThisContext: () => stub,
|
|
48
|
-
|
|
48
|
+
withInternalRoutes: () => stub,
|
|
49
49
|
extend: () => stub,
|
|
50
50
|
build: () => definitionStub,
|
|
51
|
+
// From fragno-db
|
|
52
|
+
provideHooks: () => stub,
|
|
53
|
+
withSyncCommands: () => stub,
|
|
51
54
|
};
|
|
55
|
+
|
|
56
|
+
// Wrap with Proxy to handle any additional methods (e.g. from extend())
|
|
57
|
+
const stub: object = new Proxy(stubMethods, {
|
|
58
|
+
get(target, prop) {
|
|
59
|
+
if (prop in target) {
|
|
60
|
+
return target[prop as keyof typeof target];
|
|
61
|
+
}
|
|
62
|
+
// Return a function that returns the stub for method chaining
|
|
63
|
+
return () => stub;
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
52
67
|
return stub;
|
|
53
68
|
}
|
|
54
69
|
|
|
@@ -58,7 +73,7 @@ export { FragmentDefinitionBuilder } from "./api/fragment-definition-builder";
|
|
|
58
73
|
// Stub implementation for instantiate
|
|
59
74
|
// This is stripped by unplugin-fragno in browser builds
|
|
60
75
|
export function instantiate(_definition: unknown) {
|
|
61
|
-
const
|
|
76
|
+
const fragmentStubMethods = {
|
|
62
77
|
[instantiatedFragmentFakeSymbol]: instantiatedFragmentFakeSymbol,
|
|
63
78
|
name: "",
|
|
64
79
|
routes: [],
|
|
@@ -68,21 +83,32 @@ export function instantiate(_definition: unknown) {
|
|
|
68
83
|
return {
|
|
69
84
|
deps: {},
|
|
70
85
|
options: {},
|
|
71
|
-
linkedFragments: {},
|
|
72
86
|
};
|
|
73
87
|
},
|
|
74
|
-
withMiddleware: () =>
|
|
75
|
-
// throw new Error("withMiddleware is not supported in browser builds");
|
|
76
|
-
return fragmentStub;
|
|
77
|
-
},
|
|
88
|
+
withMiddleware: () => fragmentStub,
|
|
78
89
|
inContext: <T>(callback: () => T) => callback(),
|
|
90
|
+
callServices: async (_serviceCalls: () => unknown) => [],
|
|
79
91
|
handlersFor: () => ({}),
|
|
80
92
|
handler: async () => new Response(),
|
|
81
93
|
callRoute: async () => ({ ok: true, data: undefined, error: undefined }),
|
|
82
94
|
callRouteRaw: async () => new Response(),
|
|
83
95
|
};
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
// Wrap with Proxy to handle any additional methods
|
|
98
|
+
const fragmentStub: IFragnoInstantiatedFragment = new Proxy(
|
|
99
|
+
fragmentStubMethods as IFragnoInstantiatedFragment,
|
|
100
|
+
{
|
|
101
|
+
get(target, prop) {
|
|
102
|
+
if (prop in target) {
|
|
103
|
+
return target[prop as keyof typeof target];
|
|
104
|
+
}
|
|
105
|
+
// Return a function that returns the stub for method chaining
|
|
106
|
+
return () => fragmentStub;
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const builderStubMethods = {
|
|
86
112
|
withConfig: () => builderStub,
|
|
87
113
|
withRoutes: () => builderStub,
|
|
88
114
|
withOptions: () => builderStub,
|
|
@@ -94,6 +120,20 @@ export function instantiate(_definition: unknown) {
|
|
|
94
120
|
options: undefined,
|
|
95
121
|
};
|
|
96
122
|
|
|
123
|
+
// Wrap with Proxy to handle any additional methods
|
|
124
|
+
const builderStub: IFragmentInstantiationBuilder = new Proxy(
|
|
125
|
+
builderStubMethods as IFragmentInstantiationBuilder,
|
|
126
|
+
{
|
|
127
|
+
get(target, prop) {
|
|
128
|
+
if (prop in target) {
|
|
129
|
+
return target[prop as keyof typeof target];
|
|
130
|
+
}
|
|
131
|
+
// Return a function that returns the stub for method chaining
|
|
132
|
+
return () => builderStub;
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
97
137
|
return builderStub;
|
|
98
138
|
}
|
|
99
139
|
|
|
@@ -102,6 +142,12 @@ export function instantiate(_definition: unknown) {
|
|
|
102
142
|
// ============================================================================
|
|
103
143
|
export type { FragnoPublicConfig } from "./api/shared-types";
|
|
104
144
|
|
|
145
|
+
// ============================================================================
|
|
146
|
+
// Runtime
|
|
147
|
+
// ============================================================================
|
|
148
|
+
export { defaultFragnoRuntime, type FragnoRuntime } from "./runtime";
|
|
149
|
+
export { createId, init } from "./id";
|
|
150
|
+
|
|
105
151
|
// ============================================================================
|
|
106
152
|
// Route Definition
|
|
107
153
|
// ============================================================================
|
package/src/mod.ts
CHANGED
|
@@ -7,8 +7,6 @@ export {
|
|
|
7
7
|
type FragmentDefinition,
|
|
8
8
|
type ServiceContext,
|
|
9
9
|
type ServiceConstructorFn,
|
|
10
|
-
type LinkedFragmentCallback,
|
|
11
|
-
type ExtractLinkedServices,
|
|
12
10
|
} from "./api/fragment-definition-builder";
|
|
13
11
|
|
|
14
12
|
export {
|
|
@@ -18,6 +16,7 @@ export {
|
|
|
18
16
|
type AnyFragnoInstantiatedFragment,
|
|
19
17
|
type BoundServices,
|
|
20
18
|
type InstantiatedFragmentFromDefinition,
|
|
19
|
+
type FragnoRequestLifecycleContext,
|
|
21
20
|
} from "./api/fragment-instantiator";
|
|
22
21
|
|
|
23
22
|
// ============================================================================
|
|
@@ -25,14 +24,21 @@ export {
|
|
|
25
24
|
// ============================================================================
|
|
26
25
|
export type { FragnoPublicConfig } from "./api/shared-types";
|
|
27
26
|
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Runtime
|
|
29
|
+
// ============================================================================
|
|
30
|
+
export { defaultFragnoRuntime, type FragnoRuntime } from "./runtime";
|
|
31
|
+
export { createId, init } from "./id";
|
|
32
|
+
|
|
28
33
|
// ============================================================================
|
|
29
34
|
// Route Definition
|
|
30
35
|
// ============================================================================
|
|
31
36
|
export {
|
|
32
37
|
defineRoute,
|
|
33
38
|
defineRoutes,
|
|
39
|
+
type AnyRouteOrFactory,
|
|
34
40
|
type RouteFactory,
|
|
35
41
|
type RouteFactoryContext,
|
|
36
42
|
} from "./api/route";
|
|
37
43
|
|
|
38
|
-
export { type FragnoRouteConfig, type RequestThisContext } from "./api/api";
|
|
44
|
+
export { type FragnoRouteConfig, type RequestThisContext, type RouteContentType } from "./api/api";
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createId } from "./id";
|
|
2
|
+
|
|
3
|
+
export type FragnoRuntime = {
|
|
4
|
+
time: {
|
|
5
|
+
now: () => Date;
|
|
6
|
+
};
|
|
7
|
+
random: {
|
|
8
|
+
float: () => number;
|
|
9
|
+
uuid: () => string;
|
|
10
|
+
cuid: () => string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const fallbackUuid = () => {
|
|
15
|
+
const bytes = new Uint8Array(16);
|
|
16
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
17
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// RFC 4122 variant + version 4
|
|
21
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
22
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
23
|
+
|
|
24
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
|
|
25
|
+
return [
|
|
26
|
+
hex.slice(0, 4).join(""),
|
|
27
|
+
hex.slice(4, 6).join(""),
|
|
28
|
+
hex.slice(6, 8).join(""),
|
|
29
|
+
hex.slice(8, 10).join(""),
|
|
30
|
+
hex.slice(10, 16).join(""),
|
|
31
|
+
].join("-");
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const defaultUuid = () => {
|
|
35
|
+
const cryptoApi = globalThis.crypto;
|
|
36
|
+
return cryptoApi?.randomUUID ? cryptoApi.randomUUID() : fallbackUuid();
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const defaultFragnoRuntime: FragnoRuntime = {
|
|
40
|
+
time: {
|
|
41
|
+
now: () => new Date(),
|
|
42
|
+
},
|
|
43
|
+
random: {
|
|
44
|
+
float: () => Math.random(),
|
|
45
|
+
uuid: () => defaultUuid(),
|
|
46
|
+
cuid: () => createId(),
|
|
47
|
+
},
|
|
48
|
+
};
|
package/src/test/test.test.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
3
5
|
import { defineFragment } from "../api/fragment-definition-builder";
|
|
4
6
|
import { defineRoutes } from "../api/route";
|
|
5
|
-
import {
|
|
7
|
+
import { createFragmentForTest, withTestUtils } from "./test";
|
|
6
8
|
|
|
7
9
|
describe("withTestUtils extension", () => {
|
|
8
10
|
it("should expose deps via services.deps", () => {
|
package/src/test/test.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { RequestThisContext } from "../api/api";
|
|
2
|
-
import type {
|
|
3
|
-
import type { FragnoPublicConfig } from "../api/shared-types";
|
|
2
|
+
import type { BoundServices } from "../api/bind-services";
|
|
4
3
|
import {
|
|
5
4
|
FragmentDefinitionBuilder,
|
|
6
5
|
type FragmentDefinition,
|
|
7
6
|
type ServiceConstructorFn,
|
|
8
7
|
} from "../api/fragment-definition-builder";
|
|
9
|
-
import {
|
|
10
|
-
|
|
8
|
+
import {
|
|
9
|
+
instantiateFragment,
|
|
10
|
+
type FragnoInstantiatedFragment,
|
|
11
|
+
type RoutesWithInternal,
|
|
12
|
+
} from "../api/fragment-instantiator";
|
|
13
|
+
import type { AnyRouteOrFactory, FlattenRouteFactories } from "../api/route";
|
|
14
|
+
import type { FragnoPublicConfig } from "../api/shared-types";
|
|
11
15
|
|
|
12
16
|
// Re-export for convenience
|
|
13
17
|
export type { RouteHandlerInputOptions } from "../api/route-handler-input-options";
|
|
@@ -200,6 +204,7 @@ export function createFragmentForTest<
|
|
|
200
204
|
THandlerThisContext extends RequestThisContext,
|
|
201
205
|
TRequestStorage,
|
|
202
206
|
const TRoutesOrFactories extends readonly AnyRouteOrFactory[],
|
|
207
|
+
TInternalRoutes extends readonly AnyRouteOrFactory[] = readonly [],
|
|
203
208
|
>(
|
|
204
209
|
definition: FragmentDefinition<
|
|
205
210
|
TConfig,
|
|
@@ -211,17 +216,19 @@ export function createFragmentForTest<
|
|
|
211
216
|
TPrivateServices,
|
|
212
217
|
TServiceThisContext,
|
|
213
218
|
THandlerThisContext,
|
|
214
|
-
TRequestStorage
|
|
219
|
+
TRequestStorage,
|
|
220
|
+
TInternalRoutes
|
|
215
221
|
>,
|
|
216
222
|
routesOrFactories: TRoutesOrFactories,
|
|
217
223
|
options: CreateFragmentForTestOptions<TConfig, TOptions, TServiceDependencies>,
|
|
218
224
|
): FragnoInstantiatedFragment<
|
|
219
|
-
FlattenRouteFactories<TRoutesOrFactories>,
|
|
225
|
+
RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TInternalRoutes>,
|
|
220
226
|
TDeps,
|
|
221
227
|
BoundServices<TBaseServices & TServices>,
|
|
222
228
|
TServiceThisContext,
|
|
223
229
|
THandlerThisContext,
|
|
224
|
-
TRequestStorage
|
|
230
|
+
TRequestStorage,
|
|
231
|
+
TOptions
|
|
225
232
|
> {
|
|
226
233
|
const { config, options: fragmentOptions = {} as TOptions, serviceImplementations } = options;
|
|
227
234
|
|
package/src/util/async.test.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { atom, map } from "nanostores";
|
|
4
4
|
|
|
5
|
+
import { isReadableAtom } from "./nanostores";
|
|
6
|
+
|
|
5
7
|
describe("nanostores", () => {
|
|
6
8
|
test("isReadableAtom should return true for a readable atom", () => {
|
|
7
9
|
const store = atom(0);
|
package/src/util/ssr.ts
CHANGED
package/tsconfig.json
CHANGED
package/tsdown.config.ts
CHANGED
|
@@ -12,6 +12,7 @@ export default defineConfig({
|
|
|
12
12
|
"./src/api/request-context-storage.ts",
|
|
13
13
|
"./src/request/request.ts",
|
|
14
14
|
"./src/client/client.ts",
|
|
15
|
+
"./src/id.ts",
|
|
15
16
|
"./src/client/vanilla.ts",
|
|
16
17
|
"./src/client/client.svelte.ts",
|
|
17
18
|
"./src/client/react.ts",
|
|
@@ -22,6 +23,7 @@ export default defineConfig({
|
|
|
22
23
|
"./src/integrations/react-ssr.ts",
|
|
23
24
|
"./src/integrations/svelte-kit.ts",
|
|
24
25
|
"./src/test/test.ts",
|
|
26
|
+
"./src/internal/trace-context.ts",
|
|
25
27
|
"./src/internal/symbols.ts",
|
|
26
28
|
],
|
|
27
29
|
dts: true,
|
package/vitest.config.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { svelteTesting } from "@testing-library/svelte/vite";
|
|
1
2
|
import { defineConfig, mergeConfig } from "vitest/config";
|
|
3
|
+
|
|
2
4
|
import { baseConfig } from "@fragno-private/vitest-config";
|
|
3
5
|
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
|
4
|
-
import { svelteTesting } from "@testing-library/svelte/vite";
|
|
5
6
|
|
|
6
7
|
export default defineConfig(
|
|
7
8
|
mergeConfig(baseConfig, {
|