@vesk/runtime 0.2.59 → 0.2.60

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ripple-blocks.d.ts","sourceRoot":"","sources":["../src/ripple-blocks.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAW,MAAM,kCAAkC,CAAC;AAGlF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAoBxD;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAE5C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAEhD;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,KAAK,CAEzF;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,SAAI,EAAE,KAAK,GAAE,OAAc,GAAG,KAAK,CAE/F;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,CAEzD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAExF;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,SAAI,EAAE,KAAK,GAAE,OAAc,GAAG,KAAK,CAElH;AAkBD,wBAAgB,KAAK,CACpB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAC/B,KAAK,GAAE,OAAc,EACrB,EAAE,CAAC,EAAE,SAAS,GACZ,KAAK,CAgCP;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,UAAQ,GAAG,IAAI,CAW9E;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,UAAQ,GAAG,IAAI,CAkBnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAY/C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAe9C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAiB/C;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,KAAK,GAAG,OAAO,CAezD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,UAAO,GAAG,IAAI,CA2BnE"}
1
+ {"version":3,"file":"ripple-blocks.d.ts","sourceRoot":"","sources":["../src/ripple-blocks.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAW,MAAM,kCAAkC,CAAC;AAGlF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAoBxD;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAE5C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAEhD;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,KAAK,CAEzF;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,SAAI,EAAE,KAAK,GAAE,OAAc,GAAG,KAAK,CAE/F;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,CAEzD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAExF;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,SAAI,EAAE,KAAK,GAAE,OAAc,GAAG,KAAK,CAElH;AAkBD,wBAAgB,KAAK,CACpB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,EAC/B,KAAK,GAAE,OAAc,EACrB,EAAE,CAAC,EAAE,SAAS,GACZ,KAAK,CAuCP;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,UAAQ,GAAG,IAAI,CAW9E;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,UAAQ,GAAG,IAAI,CAkBnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAY/C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAe9C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAiB/C;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,KAAK,GAAG,OAAO,CAezD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,UAAO,GAAG,IAAI,CA+BnE"}
@@ -1,5 +1,5 @@
1
1
  import { BLOCK_HAS_RUN, BRANCH_BLOCK, DERIVED, CONTAINS_TEARDOWN, DESTROYED, EFFECT_BLOCK, PAUSED, PRE_EFFECT_BLOCK, RENDER_BLOCK, ROOT_BLOCK, TRY_BLOCK, HEAD_BLOCK, DIRECT_CHILD_BLOCK, } from '@vesk/runtime/src/ripple-constants';
2
- import { active_block, active_component, active_reaction, create_component_ctx, is_block_dirty, run_block, run_teardown, schedule_update, } from '@vesk/runtime/src/ripple-runtime';
2
+ import { active_block, active_component, active_reaction, create_component_ctx, get_orphan_root, is_block_dirty, run_block, run_teardown, schedule_update, unsubscribe_reaction, } from '@vesk/runtime/src/ripple-runtime';
3
3
  export function user_effect(fn) {
4
4
  if (active_block === null) {
5
5
  throw new Error('effect() must be called within an active context, such as a component or effect');
@@ -69,6 +69,14 @@ export function block(flags, fn, state = null, co) {
69
69
  if (active_block !== null) {
70
70
  push_block(newBlock, active_block);
71
71
  }
72
+ else {
73
+ // Nothing was active — hang it off the orphan root instead of leaving
74
+ // `p === null`, where scheduling it walks an empty chain and the update
75
+ // is discarded. `push_block` does not set `p` (it is always the active
76
+ // block on the normal path), so it is set here.
77
+ newBlock.p = get_orphan_root();
78
+ push_block(newBlock, newBlock.p);
79
+ }
72
80
  if ((flags & EFFECT_BLOCK) !== 0) {
73
81
  schedule_update(newBlock);
74
82
  }
@@ -165,6 +173,9 @@ export function is_destroyed(target_block) {
165
173
  }
166
174
  export function destroy_block(block, remove_dom = true) {
167
175
  block.f ^= DESTROYED;
176
+ // A destroyed reaction must stop being a reader of any ownerless cell
177
+ // (module-scope store) — nothing will ever dirty-check it again.
178
+ unsubscribe_reaction(block);
168
179
  let removed = false;
169
180
  const f = block.f;
170
181
  if ((remove_dom && (f & (BRANCH_BLOCK | ROOT_BLOCK)) !== 0 && (f & TRY_BLOCK) === 0) ||
@@ -24,6 +24,7 @@ export interface Tracked<T = unknown> {
24
24
  c: number;
25
25
  d: DeferredTrackedEntry[] | null;
26
26
  f: number;
27
+ r: OwnerlessSubscribers | null;
27
28
  __v: unknown;
28
29
  get(): T;
29
30
  set(value: T): void;
@@ -39,11 +40,19 @@ export interface Derived<T = unknown> {
39
40
  co: Component | null;
40
41
  d: Dependency | null;
41
42
  f: number;
43
+ r: OwnerlessSubscribers | null;
42
44
  fn: () => unknown;
43
45
  __v: unknown;
44
46
  get(): T;
45
47
  set(value: T): void;
46
48
  }
49
+ /**
50
+ * A cell that has no owner block — a module-scope `track()` (the documented
51
+ * store pattern), or any cell created while no block was active. `set()` on one
52
+ * cannot walk up to a root block to schedule a flush, so its readers are kept
53
+ * here and invalidated directly. See `schedule_ownerless_update`.
54
+ */
55
+ export type OwnerlessSubscribers = Set<Block | Derived>;
47
56
  export interface Dependency {
48
57
  c: number;
49
58
  t: Tracked | Derived;
@@ -76,6 +85,27 @@ export declare let active_scope: Block | null;
76
85
  export declare let active_component: Component | null;
77
86
  export declare let active_namespace: keyof typeof NAMESPACE_URI;
78
87
  export declare let is_mutating_allowed: boolean;
88
+ /**
89
+ * The block every unparented block hangs off — a block created while no block
90
+ * was active (a component rendered outside a `root()`, a module-scope effect,
91
+ * a cell created at module scope).
92
+ *
93
+ * Without it `schedule_update()` walks an empty parent chain, pushes `null`
94
+ * into `queued_root_blocks`, and the update is dropped on the floor: the effect
95
+ * never runs, and a later write to its cell schedules nothing. Call sites used
96
+ * to each work around this privately (`runInBlockWindow` in the router,
97
+ * `runInHydrateBlock` in hydrate.ts) and every new one silently reintroduced the
98
+ * bug. The orphan root is the single place that makes "no active block" mean
99
+ * "rooted" instead of "disconnected".
100
+ *
101
+ * It is a plain object rather than a `block()` call because `block()` is what
102
+ * asks for it. It is never destroyed and never re-run: `is_block_dirty()`
103
+ * short-circuits on ROOT_BLOCK, so it only ever serves as the flush root that
104
+ * reaches its children.
105
+ */
106
+ export declare function get_orphan_root(): Block;
107
+ /** True when `block` is a block with no real owner — it lives on the orphan root. */
108
+ export declare function is_orphan(block: Block | null | undefined): boolean;
79
109
  export declare let disable_scoped_flush: boolean;
80
110
  export declare let tracking: boolean;
81
111
  export declare let teardown: boolean;
@@ -100,6 +130,12 @@ export declare function tick(): Promise<void>;
100
130
  export declare function queue_microtask(fn?: () => void): void;
101
131
  export declare function queue_post_block_flush_callback(fn: () => void): void;
102
132
  export declare function schedule_update(block: Block | null): void;
133
+ /**
134
+ * Drop `reaction` from every ownerless cell it read last time round. Called
135
+ * before a reaction re-runs (and when it is destroyed) so a dependency the new
136
+ * run no longer takes is not left subscribed forever.
137
+ */
138
+ export declare function unsubscribe_reaction(reaction: Block | Derived | null): void;
103
139
  export declare function get_derived(computed: Derived): unknown;
104
140
  export declare function get(t: unknown): unknown;
105
141
  export declare function get_tracked(tracked: Tracked): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"ripple-runtime.d.ts","sourceRoot":"","sources":["../src/ripple-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ripple-runtime.d.ts","sourceRoot":"","sources":["../src/ripple-runtime.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,48 @@ export let active_component = null;
11
11
  export let active_namespace = DEFAULT_NAMESPACE;
12
12
  export let is_mutating_allowed = true;
13
13
  const old_values = new Map();
14
+ let orphan_root = null;
15
+ /**
16
+ * The block every unparented block hangs off — a block created while no block
17
+ * was active (a component rendered outside a `root()`, a module-scope effect,
18
+ * a cell created at module scope).
19
+ *
20
+ * Without it `schedule_update()` walks an empty parent chain, pushes `null`
21
+ * into `queued_root_blocks`, and the update is dropped on the floor: the effect
22
+ * never runs, and a later write to its cell schedules nothing. Call sites used
23
+ * to each work around this privately (`runInBlockWindow` in the router,
24
+ * `runInHydrateBlock` in hydrate.ts) and every new one silently reintroduced the
25
+ * bug. The orphan root is the single place that makes "no active block" mean
26
+ * "rooted" instead of "disconnected".
27
+ *
28
+ * It is a plain object rather than a `block()` call because `block()` is what
29
+ * asks for it. It is never destroyed and never re-run: `is_block_dirty()`
30
+ * short-circuits on ROOT_BLOCK, so it only ever serves as the flush root that
31
+ * reaches its children.
32
+ */
33
+ export function get_orphan_root() {
34
+ if (orphan_root === null) {
35
+ orphan_root = {
36
+ co: null,
37
+ d: null,
38
+ first: null,
39
+ f: ROOT_BLOCK,
40
+ fn: null,
41
+ last: null,
42
+ next: null,
43
+ p: null,
44
+ prev: null,
45
+ s: null,
46
+ t: null,
47
+ tc: null,
48
+ };
49
+ }
50
+ return orphan_root;
51
+ }
52
+ /** True when `block` is a block with no real owner — it lives on the orphan root. */
53
+ export function is_orphan(block) {
54
+ return block === null || block === undefined || block === orphan_root;
55
+ }
14
56
  let scheduler_mode = FLUSH_MICROTASK;
15
57
  let is_micro_task_queued = false;
16
58
  let clock = 0;
@@ -164,6 +206,7 @@ export function run_block(block) {
164
206
  try {
165
207
  active_block = block;
166
208
  active_reaction = block;
209
+ unsubscribe_reaction(block);
167
210
  active_component = block.co;
168
211
  destroy_non_branch_children(block);
169
212
  run_teardown(block);
@@ -201,6 +244,7 @@ class TrackedValue {
201
244
  c;
202
245
  d;
203
246
  f;
247
+ r;
204
248
  __v;
205
249
  constructor(v, block, a) {
206
250
  this.a = a;
@@ -208,6 +252,7 @@ class TrackedValue {
208
252
  this.c = 0;
209
253
  this.d = null;
210
254
  this.f = TRACKED;
255
+ this.r = null;
211
256
  this.__v = v;
212
257
  }
213
258
  get() {
@@ -248,6 +293,7 @@ class DerivedValue {
248
293
  d;
249
294
  f;
250
295
  fn;
296
+ r;
251
297
  __v;
252
298
  constructor(fn, block, a) {
253
299
  this.a = a;
@@ -258,6 +304,7 @@ class DerivedValue {
258
304
  this.d = null;
259
305
  this.f = DERIVED;
260
306
  this.fn = fn;
307
+ this.r = null;
261
308
  this.__v = UNINITIALIZED;
262
309
  }
263
310
  get() {
@@ -290,10 +337,13 @@ class DerivedValue {
290
337
  }
291
338
  }
292
339
  export function tracked(v, block, get, set) {
293
- return new TrackedValue(v, block || active_block, get || set ? { get, set } : empty_get_set);
340
+ // `block || active_block!` used to store `null` here when nothing was
341
+ // active, which is what made a module-scope store cell unwritable: `set()`
342
+ // scheduled a `null` block and stopped.
343
+ return new TrackedValue(v, block || active_block || get_orphan_root(), get || set ? { get, set } : empty_get_set);
294
344
  }
295
345
  export function derived(fn, block, get, set) {
296
- return new DerivedValue(fn, block || active_block, get || set ? { get, set } : empty_get_set);
346
+ return new DerivedValue(fn, block || active_block || get_orphan_root(), get || set ? { get, set } : empty_get_set);
297
347
  }
298
348
  export function track(v, b, get, set) {
299
349
  if (is_ripple_object(v)) {
@@ -317,6 +367,14 @@ export function track(v, b, get, set) {
317
367
  }
318
368
  function create_dependency(tracked) {
319
369
  const reaction = active_reaction;
370
+ // A cell on the orphan root is a shared one — the module-scope store
371
+ // pattern. Its readers can sit under ANY root (a component rendered inside
372
+ // the router's block window reads a store created at module scope), so
373
+ // walking up from the cell reaches nobody. They are recorded here and
374
+ // invalidated directly instead. Owned cells keep the scoped-flush path.
375
+ if (is_orphan(tracked.b)) {
376
+ (tracked.r ??= new Set()).add(reaction);
377
+ }
320
378
  const existing = reaction.d;
321
379
  if (existing !== null) {
322
380
  reaction.d = existing.n;
@@ -499,6 +557,10 @@ export function queue_post_block_flush_callback(fn) {
499
557
  export function schedule_update(block) {
500
558
  if (block === null)
501
559
  return;
560
+ // Skip initializing orphan-root-scheduled blocks on SSR to avoid running
561
+ // effects that access the browser (e.g., "document").
562
+ if (block.p === get_orphan_root() && globalThis.__vsk_ssr)
563
+ return;
502
564
  if (scheduler_mode === FLUSH_MICROTASK) {
503
565
  queue_microtask();
504
566
  }
@@ -560,6 +622,52 @@ function register_dependency(tracked) {
560
622
  current.n = dependency;
561
623
  }
562
624
  }
625
+ /**
626
+ * Drop `reaction` from every ownerless cell it read last time round. Called
627
+ * before a reaction re-runs (and when it is destroyed) so a dependency the new
628
+ * run no longer takes is not left subscribed forever.
629
+ */
630
+ export function unsubscribe_reaction(reaction) {
631
+ if (reaction === null)
632
+ return;
633
+ let dep = reaction.d;
634
+ while (dep !== null) {
635
+ const t = dep.t;
636
+ if (is_orphan(t.b) && t.r !== null) {
637
+ t.r.delete(reaction);
638
+ }
639
+ dep = dep.n;
640
+ }
641
+ }
642
+ /**
643
+ * `set()` on a cell with no owner block: there is no parent chain to walk, so
644
+ * schedule the readers themselves. A reader that is itself a derived value is
645
+ * followed through, because re-running the derived is what bumps its clock and
646
+ * marks the effects that read IT dirty — the same chain the scoped walk relies
647
+ * on. `visited` keeps a diamond in the graph from looping.
648
+ */
649
+ function schedule_ownerless_update(tracked, visited) {
650
+ const subscribers = tracked.r;
651
+ if (subscribers === null)
652
+ return;
653
+ // Copy first: a reader that re-runs synchronously can re-subscribe.
654
+ for (const reaction of Array.from(subscribers)) {
655
+ if (visited !== null) {
656
+ if (visited.has(reaction))
657
+ continue;
658
+ visited.add(reaction);
659
+ }
660
+ if (reaction.f & DESTROYED) {
661
+ subscribers.delete(reaction);
662
+ continue;
663
+ }
664
+ if ((reaction.f & DERIVED) !== 0) {
665
+ schedule_ownerless_update(reaction, visited ?? new Set());
666
+ continue;
667
+ }
668
+ schedule_update(reaction);
669
+ }
670
+ }
563
671
  export function get_derived(computed) {
564
672
  update_derived(computed);
565
673
  if (tracking) {
@@ -645,7 +753,16 @@ export function set(tracked, value) {
645
753
  }
646
754
  tracked.__v = value;
647
755
  tracked.c = increment_clock();
648
- schedule_update(tracked_block);
756
+ if (!is_orphan(tracked_block)) {
757
+ schedule_update(tracked_block);
758
+ }
759
+ else {
760
+ // A shared cell (module-scope store, or any cell created with no
761
+ // active block) has no chain to walk up, and its readers can live
762
+ // under a different root than it does — scheduling it alone would
763
+ // reach nobody. Invalidate the readers themselves.
764
+ schedule_ownerless_update(tracked, null);
765
+ }
649
766
  }
650
767
  }
651
768
  export function untrack(fn) {
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Shared SSR persistence plumbing for `@vesk/runtime/store` (`createStore`)
3
+ * and `@vesk/runtime/path-store` (`createPathStore`). Both stores read and
4
+ * write through the same hooks so the adapter and the blocking pre-paint
5
+ * script serve every persisted store identically.
6
+ *
7
+ * How SSR continuity works:
8
+ * - On the SERVER a persisted store reads its value through an
9
+ * adapter-installed hook (`installPersistedStoreServerHooks`), typically
10
+ * backed by a request cookie, and registers its `storageKey` so the adapter
11
+ * can emit a blocking pre-paint script right after the render.
12
+ * - The pre-paint script is delivered as an ORIGIN-SERVED JavaScript file
13
+ * (like the `/ssr-data.js` hydration handoff), never inlined into the HTML:
14
+ * strict CSP without `unsafe-inline` must not block it. The adapter stashes
15
+ * the payload built by `buildStorePrePaintPayload` under a CSPRNG token and
16
+ * injects a `<script src="/store-init.js?t=<token>">` tag into `<head>`; the
17
+ * server serves that file once per token. It populates
18
+ * `window.__VESK_STORE_INIT__` before first paint from localStorage (with
19
+ * the server-rendered, cookie-read value as the first-visit fallback).
20
+ * - On the CLIENT the store prefers `window.__VESK_STORE_INIT__[key]`, then
21
+ * falls back to localStorage. It mirrors every write to a same-site cookie so
22
+ * the NEXT server render can read the user's actual value back — no theme
23
+ * flash, no default-content first paint, SEO-correct.
24
+ *
25
+ * Registration is bucketed per request token (`globalThis.__vsk_ssr_token`,
26
+ * the compiler's AsyncLocalStorage accessor) so concurrent SSR renders never
27
+ * mix each other's persisted keys. The adapter captures the render token at
28
+ * its `renderPageStream` call site and passes it to the registry drain, so
29
+ * even a streamed body pumped after the scope settled reports into the right
30
+ * bucket. Renders that genuinely run outside any scope use the default bucket.
31
+ *
32
+ * SSR-safe: nothing here touches `document`/`localStorage` at module scope,
33
+ * and every browser access is guarded + wrapped in try/catch. No `batch`.
34
+ */
35
+ export type PersistedStoreRead = (storageKey: string) => string | null | undefined;
36
+ export type PersistedStoreHooks = {
37
+ read?: PersistedStoreRead;
38
+ };
39
+ /**
40
+ * Install server-side persistence hooks (called by the adapter). `read`
41
+ * returns the persisted raw JSON string (typically from a request cookie) or
42
+ * null. The hook is a module-level singleton that reads request-scoped state
43
+ * itself (e.g. `globalThis.__vesk_request`), so installing it once per module
44
+ * load is safe under concurrent requests.
45
+ */
46
+ export declare function installPersistedStoreServerHooks(hooks: PersistedStoreHooks): void;
47
+ /** The cookie name a persisted store is mirrored to, for SSR continuity. */
48
+ export declare function persistedStoreCookieName(storageKey: string): string;
49
+ /**
50
+ * The storage keys of persisted stores constructed in this SSR render.
51
+ * `token` overrides the ambient request token for callers that drain the
52
+ * registry outside the render scope (the no-layout streaming path captures
53
+ * the render token at its `renderPageStream` call site and passes it here).
54
+ */
55
+ export declare function getPersistedStoreKeys(token?: string): string[];
56
+ /** Clear the constructed-key registry (called per SSR render by the adapter). */
57
+ export declare function resetPersistedStoreKeys(token?: string): void;
58
+ /**
59
+ * Read a persisted store's raw JSON for the current environment:
60
+ * - server: register the key (so the adapter emits the pre-paint script) and
61
+ * ask the installed read hook,
62
+ * - client: consume `window.__VESK_STORE_INIT__[key]` if present (then write
63
+ * it back to localStorage so a later client pickup still works), else read
64
+ * localStorage.
65
+ */
66
+ export declare function readPersistedStorage(storageKey: string): string | null;
67
+ /**
68
+ * Persist a store's raw JSON on the client: localStorage plus a same-site
69
+ * cookie mirror (best effort, small stores) so the next server render sees it.
70
+ */
71
+ export declare function writePersistedStorage(storageKey: string, raw: string): void;
72
+ /** Remove a persisted store's localStorage record and expire its cookie mirror. */
73
+ export declare function clearPersistedStorage(storageKey: string): void;
74
+ /**
75
+ * Plain JS that copies each persisted store's localStorage value into
76
+ * `window.__VESK_STORE_INIT__` before first paint. `serverValues` maps
77
+ * storageKey → the value the server rendered with (cookie read), used as a
78
+ * fallback so the very first client visit does not flash defaults.
79
+ *
80
+ * This is served as an origin-hosted script file (`/store-init.js?t=<token>`),
81
+ * never inlined into the HTML, so strict CSP without `unsafe-inline` cannot
82
+ * block it. Because the file has no HTML context, JSON.stringify output can be
83
+ * embedded directly (JSON is a valid JS literal) — no escaping needed. Safe
84
+ * for `</script>`-style content precisely because it never sits inside a
85
+ * script element.
86
+ */
87
+ export declare function buildStorePrePaintPayload(serverValues: Record<string, string | null>): string;
88
+ /** The `<script src>` reference for one store-init payload token. */
89
+ export declare function storeInitScriptTag(token: string): string;
90
+ //# sourceMappingURL=store-persistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-persistence.d.ts","sourceRoot":"","sources":["../src/store-persistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAEnF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,CAAC,EAAE,kBAAkB,CAAC;CAC3B,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAEjF;AAQD,4EAA4E;AAC5E,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnE;AAYD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAG9D;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsBtE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAoB3E;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAgB9D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,CAY7F;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,154 @@
1
+ let serverReadHook = null;
2
+ /**
3
+ * Install server-side persistence hooks (called by the adapter). `read`
4
+ * returns the persisted raw JSON string (typically from a request cookie) or
5
+ * null. The hook is a module-level singleton that reads request-scoped state
6
+ * itself (e.g. `globalThis.__vesk_request`), so installing it once per module
7
+ * load is safe under concurrent requests.
8
+ */
9
+ export function installPersistedStoreServerHooks(hooks) {
10
+ serverReadHook = typeof hooks.read === 'function' ? hooks.read : null;
11
+ }
12
+ /** The request token scoping persisted-key registrations, or '' outside a render. */
13
+ function currentSsrToken() {
14
+ const g = globalThis;
15
+ return typeof g.__vsk_ssr_token === 'string' ? g.__vsk_ssr_token : '';
16
+ }
17
+ /** The cookie name a persisted store is mirrored to, for SSR continuity. */
18
+ export function persistedStoreCookieName(storageKey) {
19
+ return 'vsk_store_' + encodeURIComponent(storageKey);
20
+ }
21
+ const pendingKeys = new Map();
22
+ function bucketFor(token) {
23
+ const set = pendingKeys.get(token);
24
+ if (set)
25
+ return set;
26
+ const fresh = new Set();
27
+ pendingKeys.set(token, fresh);
28
+ return fresh;
29
+ }
30
+ /**
31
+ * The storage keys of persisted stores constructed in this SSR render.
32
+ * `token` overrides the ambient request token for callers that drain the
33
+ * registry outside the render scope (the no-layout streaming path captures
34
+ * the render token at its `renderPageStream` call site and passes it here).
35
+ */
36
+ export function getPersistedStoreKeys(token) {
37
+ const set = pendingKeys.get(token ?? currentSsrToken());
38
+ return set ? Array.from(set) : [];
39
+ }
40
+ /** Clear the constructed-key registry (called per SSR render by the adapter). */
41
+ export function resetPersistedStoreKeys(token) {
42
+ pendingKeys.delete(token ?? currentSsrToken());
43
+ }
44
+ /**
45
+ * Read a persisted store's raw JSON for the current environment:
46
+ * - server: register the key (so the adapter emits the pre-paint script) and
47
+ * ask the installed read hook,
48
+ * - client: consume `window.__VESK_STORE_INIT__[key]` if present (then write
49
+ * it back to localStorage so a later client pickup still works), else read
50
+ * localStorage.
51
+ */
52
+ export function readPersistedStorage(storageKey) {
53
+ if (!storageKey)
54
+ return null;
55
+ if (typeof window === 'undefined') {
56
+ bucketFor(currentSsrToken()).add(storageKey);
57
+ return serverReadHook ? (serverReadHook(storageKey) ?? null) : null;
58
+ }
59
+ const init = globalThis.__VESK_STORE_INIT__;
60
+ if (init && init[storageKey] !== undefined) {
61
+ const raw = init[storageKey];
62
+ delete init[storageKey];
63
+ try {
64
+ localStorage.setItem(storageKey, raw);
65
+ }
66
+ catch {
67
+ /* storage blocked — the in-memory value still applies */
68
+ }
69
+ return raw;
70
+ }
71
+ try {
72
+ return localStorage.getItem(storageKey);
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ /**
79
+ * Persist a store's raw JSON on the client: localStorage plus a same-site
80
+ * cookie mirror (best effort, small stores) so the next server render sees it.
81
+ */
82
+ export function writePersistedStorage(storageKey, raw) {
83
+ if (!storageKey)
84
+ return;
85
+ if (typeof window !== 'undefined') {
86
+ try {
87
+ localStorage.setItem(storageKey, raw);
88
+ }
89
+ catch {
90
+ /* storage blocked — the cookie mirror still applies */
91
+ }
92
+ }
93
+ if (typeof document !== 'undefined') {
94
+ try {
95
+ document.cookie =
96
+ persistedStoreCookieName(storageKey) +
97
+ '=' +
98
+ encodeURIComponent(raw) +
99
+ '; path=/; max-age=31536000; SameSite=Lax';
100
+ }
101
+ catch {
102
+ /* cookie blocked — client still persists via localStorage */
103
+ }
104
+ }
105
+ }
106
+ /** Remove a persisted store's localStorage record and expire its cookie mirror. */
107
+ export function clearPersistedStorage(storageKey) {
108
+ if (!storageKey)
109
+ return;
110
+ if (typeof window !== 'undefined') {
111
+ try {
112
+ localStorage.removeItem(storageKey);
113
+ }
114
+ catch {
115
+ /* ignore */
116
+ }
117
+ }
118
+ if (typeof document !== 'undefined') {
119
+ try {
120
+ document.cookie = persistedStoreCookieName(storageKey) + '=; path=/; max-age=0';
121
+ }
122
+ catch {
123
+ /* ignore */
124
+ }
125
+ }
126
+ }
127
+ /**
128
+ * Plain JS that copies each persisted store's localStorage value into
129
+ * `window.__VESK_STORE_INIT__` before first paint. `serverValues` maps
130
+ * storageKey → the value the server rendered with (cookie read), used as a
131
+ * fallback so the very first client visit does not flash defaults.
132
+ *
133
+ * This is served as an origin-hosted script file (`/store-init.js?t=<token>`),
134
+ * never inlined into the HTML, so strict CSP without `unsafe-inline` cannot
135
+ * block it. Because the file has no HTML context, JSON.stringify output can be
136
+ * embedded directly (JSON is a valid JS literal) — no escaping needed. Safe
137
+ * for `</script>`-style content precisely because it never sits inside a
138
+ * script element.
139
+ */
140
+ export function buildStorePrePaintPayload(serverValues) {
141
+ return ('globalThis.__VESK_STORE_INIT__=(function(){' +
142
+ 'var S=' +
143
+ JSON.stringify(serverValues) +
144
+ ';' +
145
+ 'var E={};' +
146
+ 'for(var k in S){var v=null;try{v=localStorage.getItem(k);}catch(e){}' +
147
+ 'if(v===null)v=S[k];if(v!=null)E[k]=v;}' +
148
+ 'return E;' +
149
+ '})();\n');
150
+ }
151
+ /** The `<script src>` reference for one store-init payload token. */
152
+ export function storeInitScriptTag(token) {
153
+ return '<script src="/store-init.js?t=' + token + '" data-vesk-store-init></script>';
154
+ }
@@ -0,0 +1,61 @@
1
+ import type { StoreMiddleware } from '@vesk/types';
2
+ /**
3
+ * @typedef Subscriber
4
+ * A callback that receives the current store state or derived value.
5
+ * @example
6
+ * const unsub = store.subscribe(state => console.log(state));
7
+ */
8
+ type Subscriber<T> = (value: T) => void;
9
+ /**
10
+ * @typedef StoreOptions
11
+ * Options for creating a store.
12
+ * @property persist Whether to persist store in localStorage (default: false)
13
+ * @property storageKey The key to use in localStorage if persist is true
14
+ * @property devtools Whether to send devtools events (default: false)
15
+ */
16
+ interface StoreOptions {
17
+ persist?: boolean;
18
+ storageKey?: string;
19
+ devtools?: boolean;
20
+ }
21
+ /**
22
+ * @typedef Middleware
23
+ * A function that runs before or after each update.
24
+ * @param state Current state of the store
25
+ * @param action Optional string describing the event
26
+ * @param payload Optional data passed to update/set
27
+ */
28
+ type Middleware<T> = StoreMiddleware<T>;
29
+ /**
30
+ * @function createStore
31
+ * Creates a reactive store with optional persistence and tools like watch & middleware.
32
+ *
33
+ * @param initial The initial state object
34
+ * @param options Optional store options
35
+ * @returns Store API with get, set, update, delete, subscribe, derive, watch, use, clear, batch, reset, rehydrate, toJSON
36
+ *
37
+ * @example
38
+ * const counterStore = createStore({ count: 0 });
39
+ */
40
+ export declare function createStore<T extends object>(initial: T, options?: StoreOptions): {
41
+ get: () => T;
42
+ set: (next: T, action?: string) => void;
43
+ update: {
44
+ (partial: Partial<T>): void;
45
+ (partialFn: (prev: T) => Partial<T>): void;
46
+ };
47
+ delete: (keys: (keyof T) | (keyof T)[], action?: string) => void;
48
+ clear: () => void;
49
+ subscribe: (fn: Subscriber<T>, selector?: (state: T) => any, equals?: (a: any, b: any) => boolean) => () => boolean;
50
+ watch: <K>(selector: (s: T) => K, callback: (n: K, o: K) => void, equals?: (a: K, b: K) => boolean) => () => void;
51
+ use: (middleware: Middleware<T>) => () => void;
52
+ derive: <K>(selector: (s: T) => K, equals?: (a: K, b: K) => boolean) => {
53
+ subscribe(cb: (v: K) => void): () => boolean;
54
+ };
55
+ batch: <R>(fn: () => R) => R;
56
+ reset: () => void;
57
+ rehydrate: (newInitial: Partial<T>) => void;
58
+ toJSON: () => T;
59
+ };
60
+ export {};
61
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD;;;;;GAKG;AACH,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAExC;;;;;;GAMG;AACH,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,KAAK,UAAU,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;AAExC;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,OAAO,EAAE,CAAC,EACV,OAAO,GAAE,YAAiB;eAqFV,CAAC;gBAKE,CAAC;;kBAaK,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;oBACf,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;;mBAe/B,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE;;oBA2D3C,UAAU,CAAC,CAAC,CAAC,aACN,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,WACnB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,OAAO;YAgCvB,CAAC,YACJ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YACX,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,WACrB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO;sBAoBT,UAAU,CAAC,CAAC,CAAC;aAgBtB,CAAC,YACL,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WACZ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO;sBAGhB,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI;;YAtGjB,CAAC,MAAM,MAAM,CAAC,KAAG,CAAC;;4BAhBF,OAAO,CAAC,CAAC,CAAC;;EAoJ1C"}