@rsc-kit/core 0.1.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/README.md +95 -0
- package/SECURITY.md +60 -0
- package/dist/action.d.ts +90 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +137 -0
- package/dist/action.js.map +1 -0
- package/dist/build-rsc-vite.d.ts +39 -0
- package/dist/build-rsc-vite.d.ts.map +1 -0
- package/dist/build-rsc-vite.js +172 -0
- package/dist/build-rsc-vite.js.map +1 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +108 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/cli.js.map +1 -0
- package/dist/compile.d.ts +55 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +141 -0
- package/dist/compile.js.map +1 -0
- package/dist/devServer.d.ts +18 -0
- package/dist/devServer.d.ts.map +1 -0
- package/dist/devServer.js +70 -0
- package/dist/devServer.js.map +1 -0
- package/dist/devUrls.d.ts +11 -0
- package/dist/devUrls.d.ts.map +1 -0
- package/dist/devUrls.js +68 -0
- package/dist/devUrls.js.map +1 -0
- package/dist/embed.d.ts +21 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +119 -0
- package/dist/embed.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +38 -0
- package/dist/errors.js.map +1 -0
- package/dist/export.d.ts +41 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +100 -0
- package/dist/export.js.map +1 -0
- package/dist/files.d.ts +39 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +146 -0
- package/dist/files.js.map +1 -0
- package/dist/headers.d.ts +81 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +96 -0
- package/dist/headers.js.map +1 -0
- package/dist/host.d.ts +86 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +590 -0
- package/dist/host.js.map +1 -0
- package/dist/js/ActivityRouter.d.ts +47 -0
- package/dist/js/ActivityRouter.d.ts.map +1 -0
- package/dist/js/ActivityRouter.js +87 -0
- package/dist/js/ActivityRouter.js.map +1 -0
- package/dist/js/DocumentTitle.d.ts +22 -0
- package/dist/js/DocumentTitle.d.ts.map +1 -0
- package/dist/js/DocumentTitle.js +28 -0
- package/dist/js/DocumentTitle.js.map +1 -0
- package/dist/js/Form.d.ts +54 -0
- package/dist/js/Form.d.ts.map +1 -0
- package/dist/js/Form.js +225 -0
- package/dist/js/Form.js.map +1 -0
- package/dist/js/Link.d.ts +29 -0
- package/dist/js/Link.d.ts.map +1 -0
- package/dist/js/Link.js +112 -0
- package/dist/js/Link.js.map +1 -0
- package/dist/js/PathnameProvider.d.ts +8 -0
- package/dist/js/PathnameProvider.d.ts.map +1 -0
- package/dist/js/PathnameProvider.js +24 -0
- package/dist/js/PathnameProvider.js.map +1 -0
- package/dist/js/RedirectBoundary.d.ts +18 -0
- package/dist/js/RedirectBoundary.d.ts.map +1 -0
- package/dist/js/RedirectBoundary.js +34 -0
- package/dist/js/RedirectBoundary.js.map +1 -0
- package/dist/js/SegmentBoundary.d.ts +8 -0
- package/dist/js/SegmentBoundary.d.ts.map +1 -0
- package/dist/js/SegmentBoundary.js +38 -0
- package/dist/js/SegmentBoundary.js.map +1 -0
- package/dist/js/SlotBoundary.d.ts +15 -0
- package/dist/js/SlotBoundary.d.ts.map +1 -0
- package/dist/js/SlotBoundary.js +17 -0
- package/dist/js/SlotBoundary.js.map +1 -0
- package/dist/js/createViteRscApp.d.ts +8 -0
- package/dist/js/createViteRscApp.d.ts.map +1 -0
- package/dist/js/createViteRscApp.js +185 -0
- package/dist/js/createViteRscApp.js.map +1 -0
- package/dist/js/errors.d.ts +51 -0
- package/dist/js/errors.d.ts.map +1 -0
- package/dist/js/errors.js +93 -0
- package/dist/js/errors.js.map +1 -0
- package/dist/js/navigate.d.ts +118 -0
- package/dist/js/navigate.d.ts.map +1 -0
- package/dist/js/navigate.js +780 -0
- package/dist/js/navigate.js.map +1 -0
- package/dist/js/onlineStore.d.ts +23 -0
- package/dist/js/onlineStore.d.ts.map +1 -0
- package/dist/js/onlineStore.js +42 -0
- package/dist/js/onlineStore.js.map +1 -0
- package/dist/js/pagePayload.d.ts +15 -0
- package/dist/js/pagePayload.d.ts.map +1 -0
- package/dist/js/pagePayload.js +39 -0
- package/dist/js/pagePayload.js.map +1 -0
- package/dist/js/router.d.ts +29 -0
- package/dist/js/router.d.ts.map +1 -0
- package/dist/js/router.js +40 -0
- package/dist/js/router.js.map +1 -0
- package/dist/js/section.d.ts +16 -0
- package/dist/js/section.d.ts.map +1 -0
- package/dist/js/section.js +55 -0
- package/dist/js/section.js.map +1 -0
- package/dist/js/segmentStore.d.ts +69 -0
- package/dist/js/segmentStore.d.ts.map +1 -0
- package/dist/js/segmentStore.js +127 -0
- package/dist/js/segmentStore.js.map +1 -0
- package/dist/js/slotStore.d.ts +35 -0
- package/dist/js/slotStore.d.ts.map +1 -0
- package/dist/js/slotStore.js +46 -0
- package/dist/js/slotStore.js.map +1 -0
- package/dist/js/standardSchema.d.ts +47 -0
- package/dist/js/standardSchema.d.ts.map +1 -0
- package/dist/js/standardSchema.js +41 -0
- package/dist/js/standardSchema.js.map +1 -0
- package/dist/js/useForm.d.ts +43 -0
- package/dist/js/useForm.d.ts.map +1 -0
- package/dist/js/useForm.js +164 -0
- package/dist/js/useForm.js.map +1 -0
- package/dist/js/useLinkStatus.d.ts +2 -0
- package/dist/js/useLinkStatus.d.ts.map +1 -0
- package/dist/js/useLinkStatus.js +3 -0
- package/dist/js/useLinkStatus.js.map +1 -0
- package/dist/js/useOnline.d.ts +8 -0
- package/dist/js/useOnline.d.ts.map +1 -0
- package/dist/js/useOnline.js +20 -0
- package/dist/js/useOnline.js.map +1 -0
- package/dist/js/usePathname.d.ts +9 -0
- package/dist/js/usePathname.d.ts.map +1 -0
- package/dist/js/usePathname.js +32 -0
- package/dist/js/usePathname.js.map +1 -0
- package/dist/js/useSearchParams.d.ts +2 -0
- package/dist/js/useSearchParams.d.ts.map +1 -0
- package/dist/js/useSearchParams.js +60 -0
- package/dist/js/useSearchParams.js.map +1 -0
- package/dist/manifest.d.ts +66 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +13 -0
- package/dist/manifest.js.map +1 -0
- package/dist/prerender.d.ts +138 -0
- package/dist/prerender.d.ts.map +1 -0
- package/dist/prerender.js +387 -0
- package/dist/prerender.js.map +1 -0
- package/dist/redirect.d.ts +34 -0
- package/dist/redirect.d.ts.map +1 -0
- package/dist/redirect.js +108 -0
- package/dist/redirect.js.map +1 -0
- package/dist/redirectDigest.d.ts +20 -0
- package/dist/redirectDigest.d.ts.map +1 -0
- package/dist/redirectDigest.js +66 -0
- package/dist/redirectDigest.js.map +1 -0
- package/dist/request.d.ts +93 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +308 -0
- package/dist/request.js.map +1 -0
- package/dist/revalidate.d.ts +43 -0
- package/dist/revalidate.d.ts.map +1 -0
- package/dist/revalidate.js +99 -0
- package/dist/revalidate.js.map +1 -0
- package/dist/routes.d.ts +37 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +28 -0
- package/dist/routes.js.map +1 -0
- package/dist/routing.d.ts +52 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +146 -0
- package/dist/routing.js.map +1 -0
- package/dist/runtime.d.ts +36 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +86 -0
- package/dist/runtime.js.map +1 -0
- package/dist/safeUrl.d.ts +10 -0
- package/dist/safeUrl.d.ts.map +1 -0
- package/dist/safeUrl.js +40 -0
- package/dist/safeUrl.js.map +1 -0
- package/dist/streaming.d.ts +62 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +108 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/vite.d.ts +109 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +1909 -0
- package/dist/vite.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +863 -0
- package/dist/worker.js.map +1 -0
- package/package.json +223 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** What this needs from a runtime: somewhere to keep per-action state. */
|
|
2
|
+
interface Scope {
|
|
3
|
+
getStore(): Set<string> | undefined;
|
|
4
|
+
run<T>(store: Set<string>, fn: () => T): T;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* AsyncLocalStorage, wherever this happens to be running.
|
|
8
|
+
*
|
|
9
|
+
* The global first, because that is where a Worker has it — and where the
|
|
10
|
+
* engine puts it, since @vitejs/plugin-rsc assigns
|
|
11
|
+
* `globalThis.AsyncLocalStorage` for React's edge build. Then the module,
|
|
12
|
+
* which is where Node, Bun and Deno have it.
|
|
13
|
+
*
|
|
14
|
+
* There is no third branch on purpose. The engine bundle imports
|
|
15
|
+
* `node:async_hooks` statically, so async context is a requirement of the
|
|
16
|
+
* whole system rather than of this file: a runtime without it cannot load the
|
|
17
|
+
* engine at all, and a fallback here would be code that can never run.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveScope(from?: Record<string, unknown>): Promise<Scope>;
|
|
20
|
+
/**
|
|
21
|
+
* Mark a region of the current page for re-rendering.
|
|
22
|
+
*
|
|
23
|
+
* `'page'` and `'all'` re-render the page or the whole document; any other
|
|
24
|
+
* name is a section or a parallel slot. Unknown names are refused by the
|
|
25
|
+
* renderer with the names it does know, rather than quietly refreshing
|
|
26
|
+
* nothing.
|
|
27
|
+
*
|
|
28
|
+
* Outside an action this does nothing rather than throwing: it is reasonable
|
|
29
|
+
* for shared code to mark, and unreasonable for that to fail when the same
|
|
30
|
+
* function is called during an ordinary render.
|
|
31
|
+
*/
|
|
32
|
+
export declare function revalidate(target: string): void;
|
|
33
|
+
/** Whether anything is listening, for a host that wants to warn about the rest. */
|
|
34
|
+
export declare function isRevalidating(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Run an action with somewhere for its marks to go, and collect them.
|
|
37
|
+
*
|
|
38
|
+
* The marks are read *after* the action has run, because what it invalidated
|
|
39
|
+
* is only known once its work is done.
|
|
40
|
+
*/
|
|
41
|
+
export declare function withRevalidation<T>(run: (taken: () => string[]) => Promise<T>): Promise<T>;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=revalidate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revalidate.d.ts","sourceRoot":"","sources":["../src/revalidate.ts"],"names":[],"mappings":"AAmBA,0EAA0E;AAC1E,UAAU,KAAK;IACb,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACnC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;CAC3C;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAGhC,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAW,GACtC,OAAO,CAAC,KAAK,CAAC,CAQhB;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,mFAAmF;AACnF,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,CAAC,CAcZ"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Marking part of a page stale from inside a server action.
|
|
2
|
+
//
|
|
3
|
+
// The point is one round trip. An action that changes an order could tell the
|
|
4
|
+
// browser to go and re-fetch something, but then the answer the user is
|
|
5
|
+
// waiting on arrives before the screen is right, and the fix costs another
|
|
6
|
+
// request. Marking instead lets the action's own answer carry the re-rendered
|
|
7
|
+
// region back with it — the client applies it and never knows it asked.
|
|
8
|
+
//
|
|
9
|
+
// 'use server'
|
|
10
|
+
// export async function addOrder(order) {
|
|
11
|
+
// await orders.insert(order)
|
|
12
|
+
// revalidate('orders') // the section or slot by that name
|
|
13
|
+
// return { ok: true }
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// Scoped to the action that is running, not global, because two requests can
|
|
17
|
+
// be in flight at once and marking is per-request state. In Laravel this is a
|
|
18
|
+
// `scoped()` binding on the container.
|
|
19
|
+
/**
|
|
20
|
+
* One store, however many copies of this module exist.
|
|
21
|
+
*
|
|
22
|
+
* The app's actions are bundled into the server bundle; the host that runs
|
|
23
|
+
* them is not. So this module is loaded twice, and two scopes mean the action
|
|
24
|
+
* marks in one and the host reads the other — everything appears to work, the
|
|
25
|
+
* action's answer simply never carries anything back, and no error is raised
|
|
26
|
+
* on either side.
|
|
27
|
+
*
|
|
28
|
+
* The engine installs its host callable on globalThis for exactly this
|
|
29
|
+
* reason. This is the same crossing.
|
|
30
|
+
*/
|
|
31
|
+
const SCOPE = Symbol.for('@rsc-kit/core.revalidation-scope');
|
|
32
|
+
const globals = globalThis;
|
|
33
|
+
let ready = null;
|
|
34
|
+
let warned = false;
|
|
35
|
+
/**
|
|
36
|
+
* AsyncLocalStorage, wherever this happens to be running.
|
|
37
|
+
*
|
|
38
|
+
* The global first, because that is where a Worker has it — and where the
|
|
39
|
+
* engine puts it, since @vitejs/plugin-rsc assigns
|
|
40
|
+
* `globalThis.AsyncLocalStorage` for React's edge build. Then the module,
|
|
41
|
+
* which is where Node, Bun and Deno have it.
|
|
42
|
+
*
|
|
43
|
+
* There is no third branch on purpose. The engine bundle imports
|
|
44
|
+
* `node:async_hooks` statically, so async context is a requirement of the
|
|
45
|
+
* whole system rather than of this file: a runtime without it cannot load the
|
|
46
|
+
* engine at all, and a fallback here would be code that can never run.
|
|
47
|
+
*/
|
|
48
|
+
export async function resolveScope(
|
|
49
|
+
// Injectable so the branch a Worker takes can be tested from a runtime that
|
|
50
|
+
// would otherwise never reach it.
|
|
51
|
+
from = globals) {
|
|
52
|
+
const Ambient = from.AsyncLocalStorage;
|
|
53
|
+
if (Ambient)
|
|
54
|
+
return new Ambient();
|
|
55
|
+
const { AsyncLocalStorage } = await import('node:async_hooks');
|
|
56
|
+
return new AsyncLocalStorage();
|
|
57
|
+
}
|
|
58
|
+
/** The scope, once resolved. Null before the first action runs. */
|
|
59
|
+
function scope() {
|
|
60
|
+
return globals[SCOPE] ?? null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Mark a region of the current page for re-rendering.
|
|
64
|
+
*
|
|
65
|
+
* `'page'` and `'all'` re-render the page or the whole document; any other
|
|
66
|
+
* name is a section or a parallel slot. Unknown names are refused by the
|
|
67
|
+
* renderer with the names it does know, rather than quietly refreshing
|
|
68
|
+
* nothing.
|
|
69
|
+
*
|
|
70
|
+
* Outside an action this does nothing rather than throwing: it is reasonable
|
|
71
|
+
* for shared code to mark, and unreasonable for that to fail when the same
|
|
72
|
+
* function is called during an ordinary render.
|
|
73
|
+
*/
|
|
74
|
+
export function revalidate(target) {
|
|
75
|
+
scope()?.getStore()?.add(target);
|
|
76
|
+
}
|
|
77
|
+
/** Whether anything is listening, for a host that wants to warn about the rest. */
|
|
78
|
+
export function isRevalidating() {
|
|
79
|
+
return scope()?.getStore() !== undefined;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Run an action with somewhere for its marks to go, and collect them.
|
|
83
|
+
*
|
|
84
|
+
* The marks are read *after* the action has run, because what it invalidated
|
|
85
|
+
* is only known once its work is done.
|
|
86
|
+
*/
|
|
87
|
+
export async function withRevalidation(run) {
|
|
88
|
+
if (!globals[SCOPE]) {
|
|
89
|
+
// Resolved once and shared, for the same reason the scope itself is: a
|
|
90
|
+
// second copy of this module must not end up with a second scope.
|
|
91
|
+
ready ??= resolveScope().then((resolved) => {
|
|
92
|
+
globals[SCOPE] ??= resolved;
|
|
93
|
+
});
|
|
94
|
+
await ready;
|
|
95
|
+
}
|
|
96
|
+
const marked = new Set();
|
|
97
|
+
return await scope().run(marked, () => run(() => [...marked]));
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=revalidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revalidate.js","sourceRoot":"","sources":["../src/revalidate.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,8EAA8E;AAC9E,wEAAwE;AACxE,2EAA2E;AAC3E,8EAA8E;AAC9E,wEAAwE;AACxE,EAAE;AACF,iBAAiB;AACjB,4CAA4C;AAC5C,iCAAiC;AACjC,qEAAqE;AACrE,0BAA0B;AAC1B,MAAM;AACN,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,uCAAuC;AAQvC;;;;;;;;;;;GAWG;AACH,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;AAE5D,MAAM,OAAO,GAAG,UAA8C,CAAA;AAE9D,IAAI,KAAK,GAAyB,IAAI,CAAA;AACtC,IAAI,MAAM,GAAG,KAAK,CAAA;AAElB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;AAChC,4EAA4E;AAC5E,kCAAkC;AAClC,IAAI,GAA4B,OAAO;IAEvC,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAkD,CAAA;IAEvE,IAAI,OAAO;QAAE,OAAO,IAAI,OAAO,EAAE,CAAA;IAEjC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;IAE9D,OAAO,IAAI,iBAAiB,EAAwB,CAAA;AACtD,CAAC;AAED,mEAAmE;AACnE,SAAS,KAAK;IACZ,OAAQ,OAAO,CAAC,KAAK,CAAuB,IAAI,IAAI,CAAA;AACtD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;AAClC,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc;IAC5B,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,SAAS,CAAA;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAA0C;IAE1C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,uEAAuE;QACvE,kEAAkE;QAClE,KAAK,KAAK,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YACzC,OAAO,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAA;QAC7B,CAAC,CAAC,CAAA;QAEF,MAAM,KAAK,CAAA;IACb,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAA;IAEhC,OAAO,MAAM,KAAK,EAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Augmented by the generated `rsc-routes.d.ts`. Empty here on purpose.
|
|
3
|
+
*
|
|
4
|
+
* Declaration merging rather than a generic parameter, because the routes are
|
|
5
|
+
* a property of the project, not of each call site — threading them through
|
|
6
|
+
* every component that renders a Link is not a thing anyone would do twice.
|
|
7
|
+
*/
|
|
8
|
+
export interface Register {
|
|
9
|
+
}
|
|
10
|
+
/** The route patterns this app declared: `'/posts/[slug]'`. */
|
|
11
|
+
export type RoutePattern = Register extends {
|
|
12
|
+
routes: infer R extends string;
|
|
13
|
+
} ? R : string;
|
|
14
|
+
/** Whether anything was registered. `string` means the generator never ran. */
|
|
15
|
+
type Unregistered = string extends RoutePattern ? true : false;
|
|
16
|
+
/**
|
|
17
|
+
* A pattern with its dynamic segments opened up: `/posts/[slug]` accepts
|
|
18
|
+
* `/posts/anything`.
|
|
19
|
+
*
|
|
20
|
+
* Catch-all and single params both become `${string}`, which for a catch-all
|
|
21
|
+
* also swallows the slashes — `/docs/[...path]` accepts `/docs/a/b/c`.
|
|
22
|
+
*/
|
|
23
|
+
type Filled<P extends string> = P extends `${infer A}[...${string}]${infer B}` ? `${A}${string}${Filled<B>}` : P extends `${infer A}[${string}]${infer B}` ? `${A}${string}${Filled<B>}` : P;
|
|
24
|
+
/**
|
|
25
|
+
* Not a route, but a legitimate href: another site, a mail client, a phone
|
|
26
|
+
* number, an anchor on this page, a bare query string.
|
|
27
|
+
*/
|
|
28
|
+
type OffRoute = `${string}://${string}` | `mailto:${string}` | `tel:${string}` | `#${string}` | `?${string}`;
|
|
29
|
+
/**
|
|
30
|
+
* A url this app can answer, or one that deliberately leaves it.
|
|
31
|
+
*
|
|
32
|
+
* Cast when the destination is computed rather than written:
|
|
33
|
+
* `href={path as Href}`.
|
|
34
|
+
*/
|
|
35
|
+
export type Href = Unregistered extends true ? string : Filled<RoutePattern> | `${Filled<RoutePattern>}?${string}` | `${Filled<RoutePattern>}#${string}` | OffRoute;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AA2BA;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;CAAG;AAE5B,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GAAG,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAAG,CAAC,GAAG,MAAM,CAAA;AAE3F,+EAA+E;AAC/E,KAAK,YAAY,GAAG,MAAM,SAAS,YAAY,GAAG,IAAI,GAAG,KAAK,CAAA;AAE9D;;;;;;GAMG;AACH,KAAK,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,OAAO,MAAM,IAAI,MAAM,CAAC,EAAE,GAC1E,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAC3B,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,MAAM,IAAI,MAAM,CAAC,EAAE,GACzC,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAC3B,CAAC,CAAA;AAEP;;;GAGG;AACH,KAAK,QAAQ,GAAG,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,UAAU,MAAM,EAAE,GAAG,OAAO,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,CAAA;AAE5G;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,YAAY,SAAS,IAAI,GACxC,MAAM,GACN,MAAM,CAAC,YAAY,CAAC,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,EAAE,GAAG,QAAQ,CAAA"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Typed routes: the urls this app can actually answer, as a type.
|
|
2
|
+
//
|
|
3
|
+
// Laravel needs route() because the url lives in PHP and can move
|
|
4
|
+
// independently of the name it is called by. Here the url *is* the file path,
|
|
5
|
+
// so a name would be indirection that buys nothing. What is worth having is
|
|
6
|
+
// the other half — a link to a page that does not exist should fail at build
|
|
7
|
+
// time rather than in the browser.
|
|
8
|
+
//
|
|
9
|
+
// There is no route() builder to go with this, deliberately. A template
|
|
10
|
+
// literal is checked the same way — `/posts/${slug}` compiles and
|
|
11
|
+
// `/postz/${slug}` does not — so a builder would only wrap what the language
|
|
12
|
+
// already does. Encoding a value that is not url-safe is `encodeURIComponent`
|
|
13
|
+
// in the template, the same as anywhere else.
|
|
14
|
+
//
|
|
15
|
+
// The build already walks app/ and knows every route's segments, so it writes
|
|
16
|
+
// one line into the app's source dir:
|
|
17
|
+
//
|
|
18
|
+
// declare module '@rsc-kit/core/routes' {
|
|
19
|
+
// interface Register { routes: '/' | '/posts/[slug]' }
|
|
20
|
+
// }
|
|
21
|
+
//
|
|
22
|
+
// Everything below is derived from that union. An app that never runs the
|
|
23
|
+
// generator — a generic host, a Laravel app that has not rebuilt — registers
|
|
24
|
+
// nothing, `RoutePattern` stays `string`, and every url-taking API is exactly
|
|
25
|
+
// as permissive as it was before. That fallback is the reason this can ship
|
|
26
|
+
// without a flag.
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,kEAAkE;AAClE,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,mCAAmC;AACnC,EAAE;AACF,wEAAwE;AACxE,kEAAkE;AAClE,6EAA6E;AAC7E,8EAA8E;AAC9E,8CAA8C;AAC9C,EAAE;AACF,8EAA8E;AAC9E,sCAAsC;AACtC,EAAE;AACF,4CAA4C;AAC5C,2DAA2D;AAC3D,MAAM;AACN,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,kBAAkB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ManifestRoute, RouteManifest } from './manifest.js';
|
|
2
|
+
/** A matched route and the params its url segments bound. */
|
|
3
|
+
export interface MatchedRoute {
|
|
4
|
+
route: ManifestRoute;
|
|
5
|
+
params: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Match a pathname against the manifest's segments.
|
|
9
|
+
*
|
|
10
|
+
* The manifest stores segments rather than a pattern string, because the
|
|
11
|
+
* pattern is the host's dialect — Laravel writes `{slug}`, Hono writes
|
|
12
|
+
* `:slug`, and neither is the build's business. Matching them directly means
|
|
13
|
+
* no dialect at all.
|
|
14
|
+
*
|
|
15
|
+
* Static segments beat dynamic ones at the same position: /docs/new is the
|
|
16
|
+
* page called new, not the page called [slug] with slug=new.
|
|
17
|
+
*/
|
|
18
|
+
export declare function matchRoute(manifest: RouteManifest, pathname: string): MatchedRoute | null;
|
|
19
|
+
/**
|
|
20
|
+
* The interceptor registered for a slot, if this url has one.
|
|
21
|
+
*
|
|
22
|
+
* Matched against the url being navigated *to*: `(.)posts/[slug]` intercepts
|
|
23
|
+
* /posts/anything, and the params it binds are the target's, not the page the
|
|
24
|
+
* modal opens over.
|
|
25
|
+
*/
|
|
26
|
+
export declare function matchIntercept(manifest: RouteManifest, pathname: string, slot: string): {
|
|
27
|
+
component: string;
|
|
28
|
+
params: Record<string, string>;
|
|
29
|
+
} | null;
|
|
30
|
+
/**
|
|
31
|
+
* How much of the layout chain the client already holds.
|
|
32
|
+
*
|
|
33
|
+
* It sends the chain outermost-first; the shared prefix is what it can keep.
|
|
34
|
+
* Answering with a whole document instead is not merely wasteful — it replaces
|
|
35
|
+
* the root, and replacing the root unmounts every page retained behind the
|
|
36
|
+
* current one, so a half-typed form does not survive going back.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sharedDepth(held: string | null, chain: string[]): number;
|
|
39
|
+
/**
|
|
40
|
+
* What a page is remembered as, for retention and for the prefetch cache.
|
|
41
|
+
*
|
|
42
|
+
* The same url intercepted and not intercepted are two different things to go
|
|
43
|
+
* back to — a modal over the feed, and the post on its own page — so they
|
|
44
|
+
* cannot share a key or restoring one returns the other.
|
|
45
|
+
*
|
|
46
|
+
* Shared because both halves compute it: the host stores under this key and
|
|
47
|
+
* the client looks under it. Written out at each of the seven places that
|
|
48
|
+
* needed it, they only had to disagree once, and the symptom is a navigation
|
|
49
|
+
* that silently rebuilds a page it was holding.
|
|
50
|
+
*/
|
|
51
|
+
export declare function retentionKey(path: string, interceptSlot?: string | null): string;
|
|
52
|
+
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAgB,MAAM,eAAe,CAAA;AAE/E,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuBzF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CA8B9D;AA4BD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAWxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAIhF"}
|
package/dist/routing.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Matching a url to a route, and deciding how much of the page to send.
|
|
2
|
+
//
|
|
3
|
+
// Pure: no Request, no Response, no filesystem. That is what lets the same
|
|
4
|
+
// implementation serve three callers — the request handler, the prerenderer
|
|
5
|
+
// enumerating what to build, and the generated server bundle, which embeds its
|
|
6
|
+
// own route table and answers these without a manifest being passed around at
|
|
7
|
+
// all.
|
|
8
|
+
//
|
|
9
|
+
// Every host would otherwise write this, and would get the same two things
|
|
10
|
+
// wrong: answering /docs/new with [slug], and treating the layout chain as a
|
|
11
|
+
// set rather than a sequence.
|
|
12
|
+
/**
|
|
13
|
+
* Match a pathname against the manifest's segments.
|
|
14
|
+
*
|
|
15
|
+
* The manifest stores segments rather than a pattern string, because the
|
|
16
|
+
* pattern is the host's dialect — Laravel writes `{slug}`, Hono writes
|
|
17
|
+
* `:slug`, and neither is the build's business. Matching them directly means
|
|
18
|
+
* no dialect at all.
|
|
19
|
+
*
|
|
20
|
+
* Static segments beat dynamic ones at the same position: /docs/new is the
|
|
21
|
+
* page called new, not the page called [slug] with slug=new.
|
|
22
|
+
*/
|
|
23
|
+
export function matchRoute(manifest, pathname) {
|
|
24
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
25
|
+
let best = null;
|
|
26
|
+
let bestScore = -1;
|
|
27
|
+
for (const route of manifest.routes) {
|
|
28
|
+
const bound = bindSegments(route.segments, parts);
|
|
29
|
+
if (!bound)
|
|
30
|
+
continue;
|
|
31
|
+
// More static segments wins; a catch-all is the weakest possible match.
|
|
32
|
+
const score = route.segments.reduce((n, s) => n + (s.type === 'static' ? 2 : s.type === 'param' ? 1 : 0), 0);
|
|
33
|
+
if (score > bestScore) {
|
|
34
|
+
best = { route, params: bound };
|
|
35
|
+
bestScore = score;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return best;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The interceptor registered for a slot, if this url has one.
|
|
42
|
+
*
|
|
43
|
+
* Matched against the url being navigated *to*: `(.)posts/[slug]` intercepts
|
|
44
|
+
* /posts/anything, and the params it binds are the target's, not the page the
|
|
45
|
+
* modal opens over.
|
|
46
|
+
*/
|
|
47
|
+
export function matchIntercept(manifest, pathname, slot) {
|
|
48
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
49
|
+
let best = null;
|
|
50
|
+
let bestScore = -1;
|
|
51
|
+
for (const intercept of manifest.intercepts) {
|
|
52
|
+
if (intercept.slot !== slot)
|
|
53
|
+
continue;
|
|
54
|
+
const params = bindSegments(intercept.segments, parts);
|
|
55
|
+
if (!params)
|
|
56
|
+
continue;
|
|
57
|
+
// Scored exactly as matchRoute scores, and for the same reason: a static
|
|
58
|
+
// segment beats a dynamic one at the same position. Taking the first match
|
|
59
|
+
// instead made the two disagree about which route a url belongs to — and
|
|
60
|
+
// the guard is chosen by one while the content comes from the other, so a
|
|
61
|
+
// url could be checked against the unguarded route and rendered from the
|
|
62
|
+
// guarded one.
|
|
63
|
+
const score = intercept.segments.reduce((n, segment) => n + (segment.type === 'static' ? 2 : segment.type === 'param' ? 1 : 0), 0);
|
|
64
|
+
if (score > bestScore) {
|
|
65
|
+
best = { component: intercept.component, params };
|
|
66
|
+
bestScore = score;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return best;
|
|
70
|
+
}
|
|
71
|
+
function bindSegments(segments, parts) {
|
|
72
|
+
const params = {};
|
|
73
|
+
for (let i = 0; i < segments.length; i++) {
|
|
74
|
+
const segment = segments[i];
|
|
75
|
+
if (segment.type === 'catchAll') {
|
|
76
|
+
// Swallows the rest, including none of it.
|
|
77
|
+
params[segment.value] = parts.slice(i).join('/');
|
|
78
|
+
return params;
|
|
79
|
+
}
|
|
80
|
+
if (i >= parts.length)
|
|
81
|
+
return null;
|
|
82
|
+
if (segment.type === 'static') {
|
|
83
|
+
if (parts[i] !== segment.value)
|
|
84
|
+
return null;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
params[segment.value] = decodeURIComponent(parts[i]);
|
|
88
|
+
}
|
|
89
|
+
return segments.length === parts.length ? params : null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* How much of the layout chain the client already holds.
|
|
93
|
+
*
|
|
94
|
+
* It sends the chain outermost-first; the shared prefix is what it can keep.
|
|
95
|
+
* Answering with a whole document instead is not merely wasteful — it replaces
|
|
96
|
+
* the root, and replacing the root unmounts every page retained behind the
|
|
97
|
+
* current one, so a half-typed form does not survive going back.
|
|
98
|
+
*/
|
|
99
|
+
export function sharedDepth(held, chain) {
|
|
100
|
+
if (!held)
|
|
101
|
+
return 0;
|
|
102
|
+
const mounted = held.split(',').filter(Boolean);
|
|
103
|
+
let shared = 0;
|
|
104
|
+
while (shared < mounted.length && shared < chain.length && mounted[shared] === chain[shared]) {
|
|
105
|
+
shared++;
|
|
106
|
+
}
|
|
107
|
+
return shared;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* What a page is remembered as, for retention and for the prefetch cache.
|
|
111
|
+
*
|
|
112
|
+
* The same url intercepted and not intercepted are two different things to go
|
|
113
|
+
* back to — a modal over the feed, and the post on its own page — so they
|
|
114
|
+
* cannot share a key or restoring one returns the other.
|
|
115
|
+
*
|
|
116
|
+
* Shared because both halves compute it: the host stores under this key and
|
|
117
|
+
* the client looks under it. Written out at each of the seven places that
|
|
118
|
+
* needed it, they only had to disagree once, and the symptom is a navigation
|
|
119
|
+
* that silently rebuilds a page it was holding.
|
|
120
|
+
*/
|
|
121
|
+
export function retentionKey(path, interceptSlot) {
|
|
122
|
+
const normalised = normalisePath(path);
|
|
123
|
+
return interceptSlot ? `__intercept:${interceptSlot}:${normalised}` : normalised;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The same page written two ways is one key.
|
|
127
|
+
*
|
|
128
|
+
* A static host serves /orders as a directory, so the browser's url ends in a
|
|
129
|
+
* slash while the build wrote the page down as /orders. Left unequal, the
|
|
130
|
+
* entry retained for a page is never the one looked up on the way back: it
|
|
131
|
+
* stays in the document, hidden, while a second copy is fetched and rendered
|
|
132
|
+
* beside it — so the form you were filling in is there, and not the one you
|
|
133
|
+
* are looking at.
|
|
134
|
+
*/
|
|
135
|
+
function normalisePath(path) {
|
|
136
|
+
// Absolute urls reach this from the initial page, which is identified by
|
|
137
|
+
// href rather than by the path a link would use.
|
|
138
|
+
const withoutOrigin = path.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i, '');
|
|
139
|
+
const [pathname, rest = ''] = splitQuery(withoutOrigin);
|
|
140
|
+
return (pathname.replace(/\/+$/, '') || '/') + rest;
|
|
141
|
+
}
|
|
142
|
+
function splitQuery(url) {
|
|
143
|
+
const cut = url.search(/[?#]/);
|
|
144
|
+
return cut === -1 ? [url, ''] : [url.slice(0, cut), url.slice(cut)];
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type SocketLike = {
|
|
2
|
+
write(data: string | Uint8Array): number;
|
|
3
|
+
flush(): void;
|
|
4
|
+
/**
|
|
5
|
+
* Hang up. Optional only because a caller may hold a stub; every real
|
|
6
|
+
* transport has one, and a frame stream that cannot be parsed has to be
|
|
7
|
+
* ended rather than resynchronised — see the invalid-length branch in
|
|
8
|
+
* worker.ts for what resuming costs.
|
|
9
|
+
*/
|
|
10
|
+
end?(): void;
|
|
11
|
+
};
|
|
12
|
+
export interface SocketHandlers {
|
|
13
|
+
data(socket: SocketLike, data: Uint8Array): void | Promise<void>;
|
|
14
|
+
drain?(socket: SocketLike): void;
|
|
15
|
+
open?(socket: SocketLike): void;
|
|
16
|
+
close?(socket: SocketLike): void;
|
|
17
|
+
error?(socket: SocketLike, error: Error): void;
|
|
18
|
+
}
|
|
19
|
+
export interface ListenOptions {
|
|
20
|
+
unix?: string;
|
|
21
|
+
hostname?: string;
|
|
22
|
+
port?: number;
|
|
23
|
+
}
|
|
24
|
+
/** True when running under Bun rather than Node. */
|
|
25
|
+
export declare const isBun: boolean;
|
|
26
|
+
export declare const runtimeName: string;
|
|
27
|
+
/** Yield to the event loop so queued socket writes flush before continuing. */
|
|
28
|
+
export declare function yieldToEventLoop(): Promise<void>;
|
|
29
|
+
/** Every .ts/.js file under dir, as paths relative to it. */
|
|
30
|
+
export declare function scanScripts(dir: string): string[];
|
|
31
|
+
export interface Server {
|
|
32
|
+
stop(): void;
|
|
33
|
+
}
|
|
34
|
+
/** Listen on a Unix socket or TCP address, dispatching to Bun-shaped handlers. */
|
|
35
|
+
export declare function listen(options: ListenOptions, handlers: SocketHandlers): Server;
|
|
36
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;IACxC,KAAK,IAAI,IAAI,CAAA;IACb;;;;;OAKG;IACH,GAAG,CAAC,IAAI,IAAI,CAAA;CACb,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAChC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAC/B,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAChC,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,oDAAoD;AACpD,eAAO,MAAM,KAAK,SAA+D,CAAA;AAEjF,eAAO,MAAM,WAAW,QAAyB,CAAA;AAEjD,+EAA+E;AAC/E,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhD;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBjD;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,IAAI,IAAI,CAAA;CACb;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAW/E"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Runtime shim for the worker.
|
|
2
|
+
//
|
|
3
|
+
// The worker's only runtime-specific surface is the socket server, a yield to
|
|
4
|
+
// the event loop, and a directory walk. Bun and Node both provide all three;
|
|
5
|
+
// this presents them under one shape so worker.ts never branches.
|
|
6
|
+
//
|
|
7
|
+
// The write contract is Bun's: write() returns the number of bytes accepted, so
|
|
8
|
+
// a partial write can be queued and retried on drain. Node never writes
|
|
9
|
+
// partially — it queues internally — so the Node adapter always reports a full
|
|
10
|
+
// write and the caller's pending-buffer path simply never engages.
|
|
11
|
+
import { createServer } from 'node:net';
|
|
12
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
13
|
+
import { join, relative } from 'node:path';
|
|
14
|
+
/** True when running under Bun rather than Node. */
|
|
15
|
+
export const isBun = typeof globalThis.Bun !== 'undefined';
|
|
16
|
+
export const runtimeName = isBun ? 'bun' : 'node';
|
|
17
|
+
/** Yield to the event loop so queued socket writes flush before continuing. */
|
|
18
|
+
export function yieldToEventLoop() {
|
|
19
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
20
|
+
}
|
|
21
|
+
/** Every .ts/.js file under dir, as paths relative to it. */
|
|
22
|
+
export function scanScripts(dir) {
|
|
23
|
+
const found = [];
|
|
24
|
+
const walk = (current) => {
|
|
25
|
+
for (const entry of readdirSync(current)) {
|
|
26
|
+
const abs = join(current, entry);
|
|
27
|
+
if (statSync(abs).isDirectory()) {
|
|
28
|
+
walk(abs);
|
|
29
|
+
}
|
|
30
|
+
else if (/\.(ts|js)$/.test(entry)) {
|
|
31
|
+
found.push(relative(dir, abs));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
walk(dir);
|
|
36
|
+
return found;
|
|
37
|
+
}
|
|
38
|
+
/** Listen on a Unix socket or TCP address, dispatching to Bun-shaped handlers. */
|
|
39
|
+
export function listen(options, handlers) {
|
|
40
|
+
if (isBun) {
|
|
41
|
+
const server = globalThis.Bun.listen({
|
|
42
|
+
...(options.unix ? { unix: options.unix } : { hostname: options.hostname, port: options.port }),
|
|
43
|
+
socket: handlers,
|
|
44
|
+
});
|
|
45
|
+
return { stop: () => server.stop() };
|
|
46
|
+
}
|
|
47
|
+
return listenOnNode(options, handlers);
|
|
48
|
+
}
|
|
49
|
+
function listenOnNode(options, handlers) {
|
|
50
|
+
const server = createServer((connection) => {
|
|
51
|
+
// One adapter per connection: the worker uses the socket object as a Map
|
|
52
|
+
// key, so it has to be stable for the connection's lifetime.
|
|
53
|
+
const socket = {
|
|
54
|
+
write(data) {
|
|
55
|
+
const buffer = typeof data === 'string' ? Buffer.from(data, 'utf-8') : Buffer.from(data);
|
|
56
|
+
// Node buffers whatever the kernel would not take, so everything is
|
|
57
|
+
// always accepted. The boolean it returns is a backpressure hint, not
|
|
58
|
+
// a byte count.
|
|
59
|
+
connection.write(buffer);
|
|
60
|
+
return buffer.length;
|
|
61
|
+
},
|
|
62
|
+
flush() { },
|
|
63
|
+
end() {
|
|
64
|
+
connection.end();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
connection.on('data', (chunk) => {
|
|
68
|
+
// Always bytes: the socket is never given an encoding, so node hands
|
|
69
|
+
// back a Buffer rather than a string.
|
|
70
|
+
void handlers.data(socket, chunk);
|
|
71
|
+
});
|
|
72
|
+
connection.on('drain', () => handlers.drain?.(socket));
|
|
73
|
+
connection.on('close', () => handlers.close?.(socket));
|
|
74
|
+
connection.on('error', (error) => handlers.error?.(socket, error));
|
|
75
|
+
handlers.open?.(socket);
|
|
76
|
+
});
|
|
77
|
+
server.on('error', (error) => handlers.error?.(null, error));
|
|
78
|
+
if (options.unix) {
|
|
79
|
+
server.listen(options.unix);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
server.listen(options.port, options.hostname);
|
|
83
|
+
}
|
|
84
|
+
return { stop: () => server.close() };
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function isSafeRedirect(value: string): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* The same check, as a refusal.
|
|
4
|
+
*
|
|
5
|
+
* Says the value back, deliberately. A destination that fails this is far more
|
|
6
|
+
* often a bug in what the app computed than an attack, and a message that does
|
|
7
|
+
* not name the value leaves nothing to go on.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertSafeRedirect(value: string): void;
|
|
10
|
+
//# sourceMappingURL=safeUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safeUrl.d.ts","sourceRoot":"","sources":["../src/safeUrl.ts"],"names":[],"mappings":"AAeA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWrD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAQtD"}
|
package/dist/safeUrl.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Whether a redirect destination is a url a browser should be sent to.
|
|
2
|
+
//
|
|
3
|
+
// A destination is routinely computed rather than written — redirect()'s own
|
|
4
|
+
// documentation describes reading one from a cookie as the usual case — and it
|
|
5
|
+
// reaches location.href, location.replace and an inline script. A scheme other
|
|
6
|
+
// than http(s) in any of those is script execution rather than navigation.
|
|
7
|
+
//
|
|
8
|
+
// Parsed rather than pattern-matched. javascript: can be written with mixed
|
|
9
|
+
// case, with a newline inside it, or behind leading control characters, and
|
|
10
|
+
// each of those is a different regex to get wrong. The URL parser normalises
|
|
11
|
+
// all of it already, so asking it for the protocol is shorter and correct.
|
|
12
|
+
/** A base that cannot collide with a real origin, for resolving relative urls. */
|
|
13
|
+
const NOWHERE = 'https://rsc-kit.invalid/';
|
|
14
|
+
export function isSafeRedirect(value) {
|
|
15
|
+
try {
|
|
16
|
+
const { protocol } = new URL(value, NOWHERE);
|
|
17
|
+
// http and https only. Not data:, not blob:, not javascript:, and not a
|
|
18
|
+
// custom app scheme — a browser handing one of those to another program is
|
|
19
|
+
// not something a render should be able to trigger.
|
|
20
|
+
return protocol === 'http:' || protocol === 'https:';
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The same check, as a refusal.
|
|
28
|
+
*
|
|
29
|
+
* Says the value back, deliberately. A destination that fails this is far more
|
|
30
|
+
* often a bug in what the app computed than an attack, and a message that does
|
|
31
|
+
* not name the value leaves nothing to go on.
|
|
32
|
+
*/
|
|
33
|
+
export function assertSafeRedirect(value) {
|
|
34
|
+
if (isSafeRedirect(value))
|
|
35
|
+
return;
|
|
36
|
+
throw new Error('Not a url a redirect may go to: ' + JSON.stringify(value) + '. ' +
|
|
37
|
+
'A destination has to be a path or an http(s) url — any other scheme is ' +
|
|
38
|
+
'handed to the browser as something to run rather than somewhere to go.');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=safeUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safeUrl.js","sourceRoot":"","sources":["../src/safeUrl.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,+EAA+E;AAC/E,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,2EAA2E;AAE3E,kFAAkF;AAClF,MAAM,OAAO,GAAG,0BAA0B,CAAA;AAE1C,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAE5C,wEAAwE;QACxE,2EAA2E;QAC3E,oDAAoD;QACpD,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAA;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,cAAc,CAAC,KAAK,CAAC;QAAE,OAAM;IAEjC,MAAM,IAAI,KAAK,CACb,kCAAkC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI;QAC/D,yEAAyE;QACzE,wEAAwE,CAC3E,CAAA;AACH,CAAC"}
|