@astroscope/wormhole 1.0.0 → 1.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 +11 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/server.d.ts +19 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -8
- package/dist/server.js.map +1 -1
- package/dist/{types-0PQTD8gI.d.ts → types-Q2gaO_N_.d.ts} +2 -2
- package/dist/types-Q2gaO_N_.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/astro/WormholeScript.astro +10 -2
- package/dist/types-0PQTD8gI.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -209,6 +209,17 @@ import { open } from '@astroscope/wormhole/server';
|
|
|
209
209
|
return open(myWormhole, { count: 0 }, () => next());
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
To open several wormholes at once, pass an array of `[wormhole, data]` pairs — each pair is type-checked against its own wormhole:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
return open([
|
|
216
|
+
[cartStore, cart],
|
|
217
|
+
[sessionStore, session],
|
|
218
|
+
], () => next());
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Nested `open()` calls shadow only the wormholes they name; everything else stays visible.
|
|
222
|
+
|
|
212
223
|
### `useWormhole(wh)` <sub>React only</sub>
|
|
213
224
|
|
|
214
225
|
React hook that reads the wormhole and re-renders on changes. Uses `useSyncExternalStore` internally.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,8 +15,9 @@ function defineWormhole(name) {
|
|
|
15
15
|
get() {
|
|
16
16
|
if (store !== void 0) return store;
|
|
17
17
|
const getter = globalThis[key];
|
|
18
|
-
|
|
19
|
-
throw new Error(`wormhole "${name}" is not initialized`);
|
|
18
|
+
const value = typeof getter === "function" ? getter() : void 0;
|
|
19
|
+
if (value === void 0) throw new Error(`wormhole "${name}" is not initialized`);
|
|
20
|
+
return value;
|
|
20
21
|
},
|
|
21
22
|
set(data) {
|
|
22
23
|
if (typeof globalThis.window === "undefined") throw new Error(`wormhole "${name}" set() cannot be called on the server as it is not request-scoped; use open(wormhole, data, fn) from "@astroscope/wormhole/server" instead`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/define.ts"],"sourcesContent":["import type { DeepReadonly, Wormhole } from './types.js';\n\n/**\n * Define a wormhole that transfers state from server middleware to client components.\n *\n * **Security:** wormhole data is serialized into the HTML and sent to the browser.\n * Never store secrets (tokens, API keys, credentials) in a wormhole.\n */\nexport function defineWormhole<T>(name: string): Wormhole<T> {\n const listeners = new Set<(data: DeepReadonly<T>) => void>();\n const key = `__wormhole_${name}__`;\n let store: DeepReadonly<T> | undefined;\n\n return {\n name,\n key,\n\n get(): DeepReadonly<T> {\n if (store !== undefined) return store;\n\n const getter = (globalThis as any)[key];\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/define.ts"],"sourcesContent":["import type { DeepReadonly, Wormhole } from './types.js';\n\n/**\n * Define a wormhole that transfers state from server middleware to client components.\n *\n * **Security:** wormhole data is serialized into the HTML and sent to the browser.\n * Never store secrets (tokens, API keys, credentials) in a wormhole.\n */\nexport function defineWormhole<T>(name: string): Wormhole<T> {\n const listeners = new Set<(data: DeepReadonly<T>) => void>();\n const key = `__wormhole_${name}__`;\n let store: DeepReadonly<T> | undefined;\n\n return {\n name,\n key,\n\n get(): DeepReadonly<T> {\n if (store !== undefined) return store;\n\n const getter = (globalThis as any)[key];\n const value = typeof getter === 'function' ? (getter() as DeepReadonly<T> | undefined) : undefined;\n\n if (value === undefined) {\n throw new Error(`wormhole \"${name}\" is not initialized`);\n }\n\n return value;\n },\n\n set(data: DeepReadonly<T>): void {\n if (typeof (globalThis as any).window === 'undefined') {\n throw new Error(\n `wormhole \"${name}\" set() cannot be called on the server as it is not request-scoped; use open(wormhole, data, fn) from \"@astroscope/wormhole/server\" instead`,\n );\n }\n\n store = data;\n\n for (const fn of listeners) fn(data);\n },\n\n subscribe(fn: (data: DeepReadonly<T>) => void): () => void {\n listeners.add(fn);\n\n return () => {\n listeners.delete(fn);\n };\n },\n };\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,eAAkB,MAA2B;CAC3D,MAAM,4BAAY,IAAI,IAAqC;CAC3D,MAAM,MAAM,cAAc,KAAK;CAC/B,IAAI;CAEJ,OAAO;EACL;EACA;EAEA,MAAuB;GACrB,IAAI,UAAU,KAAA,GAAW,OAAO;GAEhC,MAAM,SAAU,WAAmB;GACnC,MAAM,QAAQ,OAAO,WAAW,aAAc,OAAO,IAAoC,KAAA;GAEzF,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,aAAa,KAAK,qBAAqB;GAGzD,OAAO;EACT;EAEA,IAAI,MAA6B;GAC/B,IAAI,OAAQ,WAAmB,WAAW,aACxC,MAAM,IAAI,MACR,aAAa,KAAK,4IACpB;GAGF,QAAQ;GAER,KAAK,MAAM,MAAM,WAAW,GAAG,IAAI;EACrC;EAEA,UAAU,IAAiD;GACzD,UAAU,IAAI,EAAE;GAEhB,aAAa;IACX,UAAU,OAAO,EAAE;GACrB;EACF;CACF;AACF"}
|
package/dist/react/index.d.ts
CHANGED
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
import { n as Wormhole, t as DeepReadonly } from "./types-
|
|
1
|
+
import { n as Wormhole, t as DeepReadonly } from "./types-Q2gaO_N_.js";
|
|
2
2
|
|
|
3
3
|
//#region src/server.d.ts
|
|
4
|
-
|
|
4
|
+
/** One [wormhole, data] pair per element — data is checked against its own wormhole's T. */
|
|
5
|
+
type WormholeEntries<Ts extends readonly unknown[]> = { [K in keyof Ts]: readonly [Wormhole<Ts[K]>, DeepReadonly<Ts[K]>] };
|
|
6
|
+
/**
|
|
7
|
+
* Open a wormhole for the duration of `fn` — `wormhole.get()` returns `data`
|
|
8
|
+
* anywhere in the async execution rooted at `fn`.
|
|
9
|
+
*/
|
|
10
|
+
declare function open<T, R>(wormhole: Wormhole<T>, data: DeepReadonly<T>, fn: () => R): R;
|
|
11
|
+
/**
|
|
12
|
+
* Open several wormholes at once for the duration of `fn`.
|
|
13
|
+
*
|
|
14
|
+
* ```typescript
|
|
15
|
+
* return open([
|
|
16
|
+
* [cartStore, cart],
|
|
17
|
+
* [sessionStore, session],
|
|
18
|
+
* ], () => next());
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function open<Ts extends readonly unknown[], R>(entries: readonly [...WormholeEntries<Ts>], fn: () => R): R;
|
|
5
22
|
//#endregion
|
|
6
23
|
export { open };
|
|
7
24
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"mappings":";;;;KAIK,eAAA,gDACS,EAAA,aAAe,QAAA,CAAS,EAAA,CAAG,CAAA,IAAK,YAAA,CAAa,EAAA,CAAG,CAAA;;;;;iBAa9C,IAAA,OAAW,QAAA,EAAU,QAAA,CAAS,CAAA,GAAI,IAAA,EAAM,YAAA,CAAa,CAAA,GAAI,EAAA,QAAU,CAAA,GAAI,CAAA;;;;;;;;;;;iBAWvE,IAAA,mCAAuC,OAAA,eAAsB,eAAA,CAAgB,EAAA,IAAM,EAAA,QAAU,CAAA,GAAI,CAAA"}
|
package/dist/server.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
//#region src/server.ts
|
|
3
|
-
const
|
|
4
|
-
function open(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
const als = globalThis[Symbol.for("@astroscope/wormhole/als")] ??= new AsyncLocalStorage();
|
|
4
|
+
function open(...args) {
|
|
5
|
+
const [entries, fn] = Array.isArray(args[0]) ? args : [[[args[0], args[1]]], args[2]];
|
|
6
|
+
const ctx = new Map(als.getStore());
|
|
7
|
+
for (const [wormhole, data] of entries) {
|
|
8
|
+
ctx.set(wormhole.key, data);
|
|
9
|
+
globalThis[wormhole.key] ??= () => als.getStore()?.get(wormhole.key);
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
return als.run(data, fn);
|
|
11
|
+
return als.run(ctx, fn);
|
|
12
12
|
}
|
|
13
13
|
//#endregion
|
|
14
14
|
export { open };
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { DeepReadonly, Wormhole } from './types.js';\n\
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { DeepReadonly, Wormhole } from './types.js';\n\n/** One [wormhole, data] pair per element — data is checked against its own wormhole's T. */\ntype WormholeEntries<Ts extends readonly unknown[]> = {\n [K in keyof Ts]: readonly [Wormhole<Ts[K]>, DeepReadonly<Ts[K]>];\n};\n\n// a single ALS holds an immutable map of all open wormholes; keyed on globalThis\n// so the vite-runner and native module instances share one context\nconst als: AsyncLocalStorage<ReadonlyMap<string, unknown>> = ((globalThis as any)[\n Symbol.for('@astroscope/wormhole/als')\n] ??= new AsyncLocalStorage());\n\n/**\n * Open a wormhole for the duration of `fn` — `wormhole.get()` returns `data`\n * anywhere in the async execution rooted at `fn`.\n */\nexport function open<T, R>(wormhole: Wormhole<T>, data: DeepReadonly<T>, fn: () => R): R;\n/**\n * Open several wormholes at once for the duration of `fn`.\n *\n * ```typescript\n * return open([\n * [cartStore, cart],\n * [sessionStore, session],\n * ], () => next());\n * ```\n */\nexport function open<Ts extends readonly unknown[], R>(entries: readonly [...WormholeEntries<Ts>], fn: () => R): R;\nexport function open(...args: unknown[]): unknown {\n const [entries, fn] = (Array.isArray(args[0]) ? args : [[[args[0], args[1]]], args[2]]) as [\n readonly (readonly [Wormhole<unknown>, unknown])[],\n () => unknown,\n ];\n\n const ctx = new Map(als.getStore());\n\n for (const [wormhole, data] of entries) {\n ctx.set(wormhole.key, data);\n\n (globalThis as any)[wormhole.key] ??= () => als.getStore()?.get(wormhole.key);\n }\n\n return als.run(ctx, fn);\n}\n"],"mappings":";;AAUA,MAAM,MAAwD,WAC5D,OAAO,IAAI,0BAA0B,OACjC,IAAI,kBAAkB;AAkB5B,SAAgB,KAAK,GAAG,MAA0B;CAChD,MAAM,CAAC,SAAS,MAAO,MAAM,QAAQ,KAAK,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE;CAKrF,MAAM,MAAM,IAAI,IAAI,IAAI,SAAS,CAAC;CAElC,KAAK,MAAM,CAAC,UAAU,SAAS,SAAS;EACtC,IAAI,IAAI,SAAS,KAAK,IAAI;EAE1B,WAAoB,SAAS,eAAe,IAAI,SAAS,CAAC,EAAE,IAAI,SAAS,GAAG;CAC9E;CAEA,OAAO,IAAI,IAAI,KAAK,EAAE;AACxB"}
|
|
@@ -15,7 +15,7 @@ type DeepReadonly<T> = T extends ((...args: never[]) => unknown) ? T : T extends
|
|
|
15
15
|
* The stored value is exposed as deeply readonly — the only way to change it is `set()`
|
|
16
16
|
* (client) or `open()` (server) with a new value, which keeps subscribers in sync.
|
|
17
17
|
*/
|
|
18
|
-
interface Wormhole<T> {
|
|
18
|
+
interface Wormhole<in out T> {
|
|
19
19
|
readonly name: string;
|
|
20
20
|
readonly key: string;
|
|
21
21
|
get(): DeepReadonly<T>;
|
|
@@ -28,4 +28,4 @@ interface Wormhole<T> {
|
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
30
|
export { Wormhole as n, DeepReadonly as t };
|
|
31
|
-
//# sourceMappingURL=types-
|
|
31
|
+
//# sourceMappingURL=types-Q2gaO_N_.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-Q2gaO_N_.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;AAMA;;;;;KAAY,YAAA,MAAkB,CAAA,cAAc,IAAA,yBACxC,CAAA,GACA,CAAA,yCACyB,CAAA,GAAI,YAAA,CAAa,CAAA,CAAE,CAAA,OAC1C,CAAA;;;;;;;;;;UAaW,QAAA;EAAA,SACN,IAAA;EAAA,SACA,GAAA;EACT,GAAA,IAAO,YAAA,CAAa,CAAA;EAjBwB;;;;EAsB5C,GAAA,CAAI,IAAA,EAAM,YAAA,CAAa,CAAA;EACvB,SAAA,CAAU,EAAA,GAAK,IAAA,EAAM,YAAA,CAAa,CAAA;AAAA"}
|
package/package.json
CHANGED
|
@@ -4,12 +4,20 @@ interface Props {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
const { wormhole } = Astro.props;
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
let data: unknown;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
data = wormhole.get();
|
|
12
|
+
} catch {
|
|
13
|
+
data = undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
const key = wormhole.key;
|
|
9
17
|
---
|
|
10
18
|
|
|
11
19
|
{
|
|
12
|
-
data ? (
|
|
20
|
+
data !== undefined ? (
|
|
13
21
|
<script is:inline define:vars={{ data, key }}>
|
|
14
22
|
globalThis[key] = function(){return data;}
|
|
15
23
|
</script>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-0PQTD8gI.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;AAMA;;;;;KAAY,YAAA,MAAkB,CAAA,cAAc,IAAA,yBACxC,CAAA,GACA,CAAA,yCACyB,CAAA,GAAI,YAAA,CAAa,CAAA,CAAE,CAAA,OAC1C,CAAA;;;;;;;;;;UAWW,QAAA;EAAA,SACN,IAAA;EAAA,SACA,GAAA;EACT,GAAA,IAAO,YAAA,CAAa,CAAA;EAfwB;;;;EAoB5C,GAAA,CAAI,IAAA,EAAM,YAAA,CAAa,CAAA;EACvB,SAAA,CAAU,EAAA,GAAK,IAAA,EAAM,YAAA,CAAa,CAAA;AAAA"}
|