@vesk/runtime 0.2.41 → 0.2.43
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/dist/form.d.ts.map +1 -1
- package/dist/form.js +7 -1
- package/dist/hydrate.d.ts +1 -1
- package/dist/hydrate.d.ts.map +1 -1
- package/dist/hydrate.js +131 -38
- package/dist/index-client.d.ts +2 -0
- package/dist/index-client.d.ts.map +1 -1
- package/dist/index-client.js +1 -0
- package/dist/index-server.d.ts +2 -0
- package/dist/index-server.d.ts.map +1 -1
- package/dist/index-server.js +1 -0
- package/dist/primitive.d.ts +119 -0
- package/dist/primitive.d.ts.map +1 -0
- package/dist/primitive.js +234 -0
- package/package.json +2 -2
package/dist/form.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACrB;AAED,UAAU,SAAS;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACrB;AAsCD,wBAAgB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAErD;AAED,wBAAgB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAElD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEjE;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEjE;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEhE;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEhF;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAoDhH;AA+GD,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACrB;AAED,UAAU,SAAS;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACrB;AAsCD,wBAAgB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAErD;AAED,wBAAgB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAElD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEjE;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEjE;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEhE;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAEhF;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAoDhH;AA+GD,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAoD9G"}
|
package/dist/form.js
CHANGED
|
@@ -244,7 +244,13 @@ export function Form(props, registry, hydrate) {
|
|
|
244
244
|
return `<form ${attrStr}>${children || ''}</form>`;
|
|
245
245
|
}
|
|
246
246
|
if (hydrate && hydrate.nextElement) {
|
|
247
|
-
|
|
247
|
+
// The compiler may deposit the static residue that precedes this
|
|
248
|
+
// self-claiming component (for example <h1> before <Form>). Consume it
|
|
249
|
+
// before claiming the form root; otherwise the runtime would inspect
|
|
250
|
+
// the heading as the form slot, detach it as a mismatch, and only find
|
|
251
|
+
// the real form through the broad querySelector fallback.
|
|
252
|
+
const skipK = typeof hydrate.takeSkipK === 'function' ? hydrate.takeSkipK() : 0;
|
|
253
|
+
let form = hydrate.nextElement('form', skipK);
|
|
248
254
|
if (form && !form.parentNode && hydrate.root) {
|
|
249
255
|
const existing = hydrate.root.querySelector('form');
|
|
250
256
|
if (existing)
|
package/dist/hydrate.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface ClaimByKeyOptions {
|
|
|
20
20
|
export interface HydrateWalker {
|
|
21
21
|
root: HTMLElement | null;
|
|
22
22
|
done(): boolean;
|
|
23
|
-
nextElement(tag?: string): Element;
|
|
23
|
+
nextElement(tag?: string, skipK?: number): Element;
|
|
24
24
|
/**
|
|
25
25
|
* Claim an element like `nextElement` but without stripping its direct text
|
|
26
26
|
* children. Used by static-component stubs whose SSR content is preserved
|
package/dist/hydrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../src/hydrate.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,OAAO,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC;IAChB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../src/hydrate.ts"],"names":[],"mappings":"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"}
|
package/dist/hydrate.js
CHANGED
|
@@ -38,16 +38,32 @@ export function reactiveProps(props) {
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
// Dev-mode hydration-integrity canary (T1)
|
|
40
40
|
//
|
|
41
|
-
// Claiming the SSR DOM is inherently positional:
|
|
42
|
-
// wrong slot fails silently in production
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// * runs `assertFullyHydrated()` at the end of every full hydration so
|
|
47
|
-
// unclaimed phantom markers are reported instead of rotting in the DOM.
|
|
41
|
+
// Claiming the SSR DOM is inherently positional: an element claimed from the
|
|
42
|
+
// wrong slot fails silently in production. Hydration bookkeeping therefore
|
|
43
|
+
// lives in WeakMaps, never in DOM attributes. The canary audits the records at
|
|
44
|
+
// the end of every full hydration and warns when markers or structural claim
|
|
45
|
+
// records remain unresolved.
|
|
48
46
|
// ---------------------------------------------------------------------------
|
|
49
47
|
let __hydrateDevMode = true;
|
|
50
|
-
|
|
48
|
+
// The first map answers "was this element adopted?" without observable DOM
|
|
49
|
+
// mutation. The second lets auditHydration inspect records even after a safe
|
|
50
|
+
// recovery detached a ghost: walker records stay reachable from their live
|
|
51
|
+
// owner root without keeping an otherwise-unreachable node globally alive.
|
|
52
|
+
const claimRegistry = new WeakMap();
|
|
53
|
+
const walkerClaimRecords = new WeakMap();
|
|
54
|
+
function recordClaim(root, record) {
|
|
55
|
+
claimRegistry.set(record.el, record);
|
|
56
|
+
if (!root)
|
|
57
|
+
return;
|
|
58
|
+
const records = walkerClaimRecords.get(root);
|
|
59
|
+
if (records)
|
|
60
|
+
records.push(record);
|
|
61
|
+
else
|
|
62
|
+
walkerClaimRecords.set(root, [record]);
|
|
63
|
+
}
|
|
64
|
+
function isAdopted(el) {
|
|
65
|
+
return claimRegistry.get(el)?.state === 'adopted';
|
|
66
|
+
}
|
|
51
67
|
export function setHydrateDevMode(enabled) {
|
|
52
68
|
__hydrateDevMode = enabled;
|
|
53
69
|
}
|
|
@@ -89,16 +105,15 @@ function reportMiss(kind, detail, loud) {
|
|
|
89
105
|
if (loud)
|
|
90
106
|
devWarn(`${kind}: ${detail}`);
|
|
91
107
|
}
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
108
|
+
function recordAdopted(root, el, expectedTag) {
|
|
109
|
+
recordClaim(root, {
|
|
110
|
+
state: 'adopted',
|
|
111
|
+
el,
|
|
112
|
+
owner: root,
|
|
113
|
+
expectedTag,
|
|
114
|
+
actualTag: el.tagName.toLowerCase(),
|
|
115
|
+
detached: false,
|
|
116
|
+
});
|
|
102
117
|
}
|
|
103
118
|
// Strip the direct text children of a claimed element. Static text inside a
|
|
104
119
|
// claimed subtree is re-created client-side as fresh text nodes, so the
|
|
@@ -176,8 +191,8 @@ function scanTwins(container) {
|
|
|
176
191
|
let a = false;
|
|
177
192
|
let b = false;
|
|
178
193
|
try {
|
|
179
|
-
a = el
|
|
180
|
-
b = prev
|
|
194
|
+
a = isAdopted(el);
|
|
195
|
+
b = isAdopted(prev);
|
|
181
196
|
}
|
|
182
197
|
catch {
|
|
183
198
|
continue;
|
|
@@ -236,6 +251,32 @@ export function auditHydration(container) {
|
|
|
236
251
|
if (!sample && container.outerHTML)
|
|
237
252
|
sample = String(container.outerHTML).slice(0, 800);
|
|
238
253
|
}
|
|
254
|
+
const structuralRecords = [];
|
|
255
|
+
const seenRecords = new Set();
|
|
256
|
+
const collectWalkerRecords = (el) => {
|
|
257
|
+
const records = walkerClaimRecords.get(el);
|
|
258
|
+
if (records) {
|
|
259
|
+
for (const record of records) {
|
|
260
|
+
if (!seenRecords.has(record)) {
|
|
261
|
+
seenRecords.add(record);
|
|
262
|
+
structuralRecords.push(record);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (let i = 0; i < el.children.length; i++)
|
|
267
|
+
collectWalkerRecords(el.children[i]);
|
|
268
|
+
};
|
|
269
|
+
collectWalkerRecords(container);
|
|
270
|
+
const structuralGhosts = structuralRecords.filter((record) => record.state === 'ghost');
|
|
271
|
+
if (structuralGhosts.length > 0) {
|
|
272
|
+
unclaimed += structuralGhosts.length;
|
|
273
|
+
if (orphanNames.length < 5) {
|
|
274
|
+
orphanNames.push(`${structuralGhosts.length} structural hydration ghost(s) (${structuralGhosts
|
|
275
|
+
.slice(0, 3)
|
|
276
|
+
.map((record) => `<${record.actualTag}> expected <${record.expectedTag}>`)
|
|
277
|
+
.join(', ')})`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
239
280
|
const twinRes = scanTwins(container);
|
|
240
281
|
const issues = [];
|
|
241
282
|
if (unclaimed > 0) {
|
|
@@ -268,6 +309,19 @@ export function auditHydration(container) {
|
|
|
268
309
|
// Best-effort repair; the report above already recorded it.
|
|
269
310
|
}
|
|
270
311
|
}
|
|
312
|
+
for (const record of structuralGhosts) {
|
|
313
|
+
try {
|
|
314
|
+
// Only detach a still-connected ghost whose immediate parent is
|
|
315
|
+
// the walker's own root. Never sweep arbitrary descendants.
|
|
316
|
+
if (record.detached || !record.owner || record.el.parentNode !== record.owner)
|
|
317
|
+
continue;
|
|
318
|
+
record.el.remove();
|
|
319
|
+
record.detached = true;
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// Best-effort repair; the report above already recorded it.
|
|
323
|
+
}
|
|
324
|
+
}
|
|
271
325
|
}
|
|
272
326
|
if (__mismatchHandler) {
|
|
273
327
|
for (const issue of issues) {
|
|
@@ -308,7 +362,7 @@ export function assertFullyHydrated(container) {
|
|
|
308
362
|
}
|
|
309
363
|
function underClaimedAncestor(el, container) {
|
|
310
364
|
for (let n = el; n && n !== container; n = n.parentElement) {
|
|
311
|
-
if (n.nodeType === 1 && n
|
|
365
|
+
if (n.nodeType === 1 && isAdopted(n))
|
|
312
366
|
return true;
|
|
313
367
|
}
|
|
314
368
|
return false;
|
|
@@ -384,7 +438,7 @@ function checkMarkerIdentity(marker, el) {
|
|
|
384
438
|
}
|
|
385
439
|
}
|
|
386
440
|
}
|
|
387
|
-
function adoptElement(marker, tag) {
|
|
441
|
+
function adoptElement(root, marker, tag) {
|
|
388
442
|
const el = marker.nextElementSibling;
|
|
389
443
|
if (!el) {
|
|
390
444
|
marker.remove();
|
|
@@ -401,12 +455,12 @@ function adoptElement(marker, tag) {
|
|
|
401
455
|
marker.remove();
|
|
402
456
|
stripDirectTextNodes(el);
|
|
403
457
|
captureSsrElementChildren(el);
|
|
404
|
-
|
|
458
|
+
recordAdopted(root, el, tag || el.tagName.toLowerCase());
|
|
405
459
|
return el;
|
|
406
460
|
}
|
|
407
461
|
// Like adoptElement but preserves direct text children. Used by static-component
|
|
408
462
|
// stubs whose SSR content is kept as-is (no client-side re-rendering).
|
|
409
|
-
function adoptElementRaw(marker, tag) {
|
|
463
|
+
function adoptElementRaw(root, marker, tag) {
|
|
410
464
|
const el = marker.nextElementSibling;
|
|
411
465
|
if (!el) {
|
|
412
466
|
marker.remove();
|
|
@@ -417,7 +471,7 @@ function adoptElementRaw(marker, tag) {
|
|
|
417
471
|
checkMarkerIdentity(marker, el);
|
|
418
472
|
marker.remove();
|
|
419
473
|
captureSsrElementChildren(el);
|
|
420
|
-
|
|
474
|
+
recordAdopted(root, el, tag || el.tagName.toLowerCase());
|
|
421
475
|
return el;
|
|
422
476
|
}
|
|
423
477
|
class WalkerEngine {
|
|
@@ -513,7 +567,7 @@ class WalkerEngine {
|
|
|
513
567
|
break;
|
|
514
568
|
}
|
|
515
569
|
this.idx++;
|
|
516
|
-
const adopted = adoptElement(tm.comment, tag);
|
|
570
|
+
const adopted = adoptElement(this.root, tm.comment, tag);
|
|
517
571
|
if (adopted === null) {
|
|
518
572
|
// SSR rendered no element after this marker. Fall out to a
|
|
519
573
|
// fresh element instead of hunting through the remaining
|
|
@@ -589,7 +643,7 @@ class WalkerEngine {
|
|
|
589
643
|
break;
|
|
590
644
|
}
|
|
591
645
|
this.idx++;
|
|
592
|
-
const adopted = adoptElementRaw(tm.comment, tag);
|
|
646
|
+
const adopted = adoptElementRaw(this.root, tm.comment, tag);
|
|
593
647
|
if (adopted === null) {
|
|
594
648
|
tm.state = 'claimed';
|
|
595
649
|
break;
|
|
@@ -694,7 +748,7 @@ class WalkerEngine {
|
|
|
694
748
|
tm.comment.remove();
|
|
695
749
|
stripDirectTextNodes(el);
|
|
696
750
|
captureSsrElementChildren(el);
|
|
697
|
-
|
|
751
|
+
recordAdopted(this.root, el, el.tagName.toLowerCase());
|
|
698
752
|
return { el };
|
|
699
753
|
}
|
|
700
754
|
claimByKey(key, options) {
|
|
@@ -822,8 +876,9 @@ class WalkerEngine {
|
|
|
822
876
|
// caller already consumed — static residues it retrieved from `__vsk_ssrEls`)
|
|
823
877
|
// and by tag. The compiler emits `nextElement(tag, skipK)` claims with the
|
|
824
878
|
// descriptor implicit in the tag; divergence follows §2.2 step 4: a tag
|
|
825
|
-
// mismatch consumes exactly one slot, reports,
|
|
826
|
-
//
|
|
879
|
+
// mismatch consumes exactly one slot, reports, detaches the provably-owned
|
|
880
|
+
// direct-child ghost, and hands back a fresh element that the caller mounts.
|
|
881
|
+
// The SSR node is never bound.
|
|
827
882
|
//
|
|
828
883
|
// The same engine backs `createHydrateChildWalker`, so runtime components that
|
|
829
884
|
// claim offspring positionally (loading stubs, slot content) keep working with
|
|
@@ -923,12 +978,41 @@ class StructuralWalker {
|
|
|
923
978
|
const el = this.els[target];
|
|
924
979
|
if (tag && el.tagName.toLowerCase() !== tag) {
|
|
925
980
|
// §2.2 step 4: descriptor failed. Consume exactly ONE slot and
|
|
926
|
-
// hand back a fresh element
|
|
927
|
-
//
|
|
928
|
-
// or leaves it inert for the sweep.
|
|
981
|
+
// hand back a fresh element. The direct-child SSR ghost is
|
|
982
|
+
// detached below; nested recovery remains owned by its child walker.
|
|
929
983
|
this.idx = this.advancePastSkippable(target + 1);
|
|
930
|
-
|
|
931
|
-
|
|
984
|
+
const ghost = {
|
|
985
|
+
state: 'ghost',
|
|
986
|
+
el,
|
|
987
|
+
owner: this.root,
|
|
988
|
+
expectedTag: tag || 'div',
|
|
989
|
+
actualTag: el.tagName.toLowerCase(),
|
|
990
|
+
detached: false,
|
|
991
|
+
};
|
|
992
|
+
recordClaim(this.root, ghost);
|
|
993
|
+
// This is the walker's own immediate child slot, so detachment is
|
|
994
|
+
// provably boundary-local. Never remove nested content here: a
|
|
995
|
+
// nested mismatch belongs to the child walker's own recovery.
|
|
996
|
+
if (this.root && el.parentNode === this.root) {
|
|
997
|
+
try {
|
|
998
|
+
el.remove();
|
|
999
|
+
ghost.detached = true;
|
|
1000
|
+
}
|
|
1001
|
+
catch {
|
|
1002
|
+
// The final sweep will report/repair a still-owned ghost.
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const fresh = document.createElement(tag || 'div');
|
|
1006
|
+
recordClaim(this.root, {
|
|
1007
|
+
state: 'fresh',
|
|
1008
|
+
el: fresh,
|
|
1009
|
+
owner: this.root,
|
|
1010
|
+
expectedTag: tag || 'div',
|
|
1011
|
+
actualTag: fresh.tagName.toLowerCase(),
|
|
1012
|
+
detached: false,
|
|
1013
|
+
});
|
|
1014
|
+
reportMiss('tag-mismatch', `descriptor wanted <${tag}> but the SSR slot holds <${el.tagName.toLowerCase()}>; consumed one slot, detached the local ghost, and rendered fresh.`, false);
|
|
1015
|
+
return fresh;
|
|
932
1016
|
}
|
|
933
1017
|
if (this.adopted.has(el)) {
|
|
934
1018
|
// Double-claim guard: a node already adopted (cross-position
|
|
@@ -941,16 +1025,25 @@ class StructuralWalker {
|
|
|
941
1025
|
if (!raw)
|
|
942
1026
|
stripDirectTextNodes(el);
|
|
943
1027
|
captureSsrElementChildren(el);
|
|
944
|
-
|
|
1028
|
+
recordAdopted(this.root, el, tag || el.tagName.toLowerCase());
|
|
945
1029
|
return el;
|
|
946
1030
|
}
|
|
947
1031
|
this.idx = this.els.length;
|
|
1032
|
+
const fresh = document.createElement(tag || 'div');
|
|
1033
|
+
recordClaim(this.root, {
|
|
1034
|
+
state: 'fresh',
|
|
1035
|
+
el: fresh,
|
|
1036
|
+
owner: this.root,
|
|
1037
|
+
expectedTag: tag || 'div',
|
|
1038
|
+
actualTag: fresh.tagName.toLowerCase(),
|
|
1039
|
+
detached: false,
|
|
1040
|
+
});
|
|
948
1041
|
reportMiss('exhausted', `hydration claim missed <${tag || 'element'}>; the client rendered more or different content than SSR.`,
|
|
949
1042
|
// Not loud: an exhausted walker is the normal post-hydration
|
|
950
1043
|
// re-render shape (every further claim legitimately builds fresh
|
|
951
1044
|
// nodes) — telemetry-only, mirroring the marker engine.
|
|
952
1045
|
false);
|
|
953
|
-
return
|
|
1046
|
+
return fresh;
|
|
954
1047
|
}
|
|
955
1048
|
nextElement(tag, skipK = 0) {
|
|
956
1049
|
return this.claimAt(tag, skipK, false);
|
|
@@ -1016,7 +1109,7 @@ class StructuralWalker {
|
|
|
1016
1109
|
this.adopted.add(el);
|
|
1017
1110
|
stripDirectTextNodes(el);
|
|
1018
1111
|
captureSsrElementChildren(el);
|
|
1019
|
-
|
|
1112
|
+
recordAdopted(this.root, el, el.tagName.toLowerCase());
|
|
1020
1113
|
return { el };
|
|
1021
1114
|
}
|
|
1022
1115
|
this.idx = this.els.length;
|
package/dist/index-client.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export { Experiment } from '@vesk/runtime/src/experiment';
|
|
|
20
20
|
export { Form, Field, required, email, minLength, maxLength, pattern, custom } from '@vesk/runtime/src/form';
|
|
21
21
|
export { Md } from '@vesk/runtime/src/md';
|
|
22
22
|
export { Show, For, Switch, Match } from '@vesk/runtime/src/headless';
|
|
23
|
+
export { primitive, leaf, isPrimitive, isLeaf, isVeskComponent, toDomNode, rerenderNode } from '@vesk/runtime/src/primitive';
|
|
24
|
+
export type { VeskLeafComponent, VeskPrimitiveComponent, VeskTaggedLeafComponent, VeskComponentRegistry, VeskScope } from '@vesk/types';
|
|
23
25
|
export { LoadingIndicator, useLoadingIndicator, configureLoadingIndicator, getLoadingProgress, isLoadingActive, getLoadingError, LOADING_INDICATOR_DEFAULT_COLOR, LOADING_INDICATOR_DEFAULT_ERROR_COLOR } from '@vesk/runtime/src/loading-indicator';
|
|
24
26
|
export type { LoadingIndicatorHandle, LoadingIndicatorOptions, LoadingIndicatorProps } from '@vesk/runtime/src/loading-indicator';
|
|
25
27
|
export { defineAction, getAction, clearActions, validateActionInput, issuesToFieldMap, isFormAction } from '@vesk/runtime/src/action';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-client.d.ts","sourceRoot":"","sources":["../src/index-client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-client.d.ts","sourceRoot":"","sources":["../src/index-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index-client.js
CHANGED
|
@@ -16,5 +16,6 @@ export { Experiment } from '@vesk/runtime/src/experiment';
|
|
|
16
16
|
export { Form, Field, required, email, minLength, maxLength, pattern, custom } from '@vesk/runtime/src/form';
|
|
17
17
|
export { Md } from '@vesk/runtime/src/md';
|
|
18
18
|
export { Show, For, Switch, Match } from '@vesk/runtime/src/headless';
|
|
19
|
+
export { primitive, leaf, isPrimitive, isLeaf, isVeskComponent, toDomNode, rerenderNode } from '@vesk/runtime/src/primitive';
|
|
19
20
|
export { LoadingIndicator, useLoadingIndicator, configureLoadingIndicator, getLoadingProgress, isLoadingActive, getLoadingError, LOADING_INDICATOR_DEFAULT_COLOR, LOADING_INDICATOR_DEFAULT_ERROR_COLOR } from '@vesk/runtime/src/loading-indicator';
|
|
20
21
|
export { defineAction, getAction, clearActions, validateActionInput, issuesToFieldMap, isFormAction } from '@vesk/runtime/src/action';
|
package/dist/index-server.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { Experiment } from '@vesk/runtime/src/experiment';
|
|
|
13
13
|
export { Form, Field, required, email, minLength, maxLength, pattern, custom } from '@vesk/runtime/src/form';
|
|
14
14
|
export { Md } from '@vesk/runtime/src/md';
|
|
15
15
|
export { Show, For, Switch, Match } from '@vesk/runtime/src/headless';
|
|
16
|
+
export { primitive, leaf, isPrimitive, isLeaf, isVeskComponent, toDomNode, rerenderNode } from '@vesk/runtime/src/primitive';
|
|
17
|
+
export type { VeskLeafComponent, VeskPrimitiveComponent, VeskTaggedLeafComponent, VeskComponentRegistry, VeskScope } from '@vesk/types';
|
|
16
18
|
export { LoadingIndicator, useLoadingIndicator, configureLoadingIndicator, getLoadingProgress, isLoadingActive, getLoadingError } from '@vesk/runtime/src/loading-indicator';
|
|
17
19
|
export type { LoadingIndicatorHandle, LoadingIndicatorOptions, LoadingIndicatorProps } from '@vesk/runtime/src/loading-indicator';
|
|
18
20
|
export { defineAction, getAction, clearActions, validateActionInput, issuesToFieldMap, isFormAction } from '@vesk/runtime/src/action';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-server.d.ts","sourceRoot":"","sources":["../src/index-server.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-server.d.ts","sourceRoot":"","sources":["../src/index-server.ts"],"names":[],"mappings":"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"}
|
package/dist/index-server.js
CHANGED
|
@@ -13,5 +13,6 @@ export { Experiment } from '@vesk/runtime/src/experiment';
|
|
|
13
13
|
export { Form, Field, required, email, minLength, maxLength, pattern, custom } from '@vesk/runtime/src/form';
|
|
14
14
|
export { Md } from '@vesk/runtime/src/md';
|
|
15
15
|
export { Show, For, Switch, Match } from '@vesk/runtime/src/headless';
|
|
16
|
+
export { primitive, leaf, isPrimitive, isLeaf, isVeskComponent, toDomNode, rerenderNode } from '@vesk/runtime/src/primitive';
|
|
16
17
|
export { LoadingIndicator, useLoadingIndicator, configureLoadingIndicator, getLoadingProgress, isLoadingActive, getLoadingError } from '@vesk/runtime/src/loading-indicator';
|
|
17
18
|
export { defineAction, getAction, clearActions, validateActionInput, issuesToFieldMap, isFormAction } from '@vesk/runtime/src/action';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `primitive` / `leaf` — the two authoring wrappers for the Vesk component ABI.
|
|
3
|
+
*
|
|
4
|
+
* Every component call in Vesk, including a bare `<Badge/>` with no
|
|
5
|
+
* attributes, is invoked as `callee(props, __registry, __veskScope)`. The
|
|
6
|
+
* compiler always passes an object literal at the call site (`{}` when there
|
|
7
|
+
* are no attributes), so `props` is never nullish coming *from the framework*.
|
|
8
|
+
*
|
|
9
|
+
* The gap this module closes is the other direction: a component exported from
|
|
10
|
+
* a module is an ordinary function, and an ordinary function can be called
|
|
11
|
+
* with no arguments at all — `Badge()` from a helper, a test, a render
|
|
12
|
+
* callback, or a manual `registry.get('Badge')!(undefined, reg, scope)` probe.
|
|
13
|
+
* A hand-written `function Badge(props) { return props.children }` throws
|
|
14
|
+
* `Cannot read properties of undefined` in that case. `primitive()` removes
|
|
15
|
+
* that whole class of failure by normalizing nullish props to a frozen empty
|
|
16
|
+
* object before the wrapped function ever sees them.
|
|
17
|
+
*
|
|
18
|
+
* `leaf()` is the behavioural counterpart: stateful widgets, own click
|
|
19
|
+
* handlers, async components. They implement the `VeskLeafComponent` ABI
|
|
20
|
+
* directly — the wrapper only stamps the marker so the two kinds can be told
|
|
21
|
+
* apart at runtime.
|
|
22
|
+
*
|
|
23
|
+
* Both wrappers are pure: no `document`/`window` access, no top-level side
|
|
24
|
+
* effects, safe to import from the client and server barrels alike.
|
|
25
|
+
*/
|
|
26
|
+
import type { VeskComponentRegistry, VeskLeafComponent, VeskPrimitiveComponent, VeskScope, VeskTaggedLeafComponent } from '@vesk/types';
|
|
27
|
+
/**
|
|
28
|
+
* Wrap a presentational component so it is safe to call with no arguments.
|
|
29
|
+
*
|
|
30
|
+
* The wrapped function is *not* a copy of `fn` — it forwards `props`,
|
|
31
|
+
* `__registry` and the scope untouched, so identity-sensitive behavior
|
|
32
|
+
* (scope preference, `__veskScope`) is unchanged. Only a nullish `props` is
|
|
33
|
+
* substituted; a real props object is never cloned, merged or stripped, which
|
|
34
|
+
* matters because on the client `props.children` can be a live reactive
|
|
35
|
+
* container that must reach `fn` by reference.
|
|
36
|
+
*
|
|
37
|
+
* @param fn - The presentational component. Receives `(props, registry, scope)`.
|
|
38
|
+
* @returns A {@link VeskPrimitiveComponent} callable as `Badge()` or `<Badge/>`.
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const Badge = primitive((props) => `<span class="badge">${props.label ?? ''}</span>`);
|
|
42
|
+
* Badge(); // '' — no throw
|
|
43
|
+
* Badge({ label: 'new' }); // '<span class="badge">new</span>'
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function primitive<P extends object = Record<string, unknown>>(fn: (props: P, registry?: VeskComponentRegistry, scope?: VeskScope) => unknown | Promise<unknown>): VeskPrimitiveComponent<P>;
|
|
47
|
+
/**
|
|
48
|
+
* Tag a stateful/async component as a leaf so `isLeaf()` can identify it.
|
|
49
|
+
*
|
|
50
|
+
* Behaviourally a no-op — the function is returned as-is (plus the marker) so
|
|
51
|
+
* the `__veskScope` property the SSR renderer assigns to compiled components
|
|
52
|
+
* stays writable. Use it to document intent and to let tooling distinguish a
|
|
53
|
+
* behavioural widget from a `primitive()` one; do not use it to give a
|
|
54
|
+
* primitive extra behavior it cannot support.
|
|
55
|
+
*
|
|
56
|
+
* @param fn - The leaf component implementing `(props, registry, scope)`.
|
|
57
|
+
* @returns The same function, tagged with `__veskLeaf`.
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const Tabs = leaf((props, registry, scope) => {
|
|
61
|
+
* const &[open] = track(0);
|
|
62
|
+
* return `<div>${open}</div>`;
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function leaf<P extends object = Record<string, unknown>>(fn: VeskLeafComponent<P>): VeskTaggedLeafComponent<P>;
|
|
67
|
+
/** True when `value` was produced by {@link primitive}. */
|
|
68
|
+
export declare function isPrimitive(value: unknown): value is VeskPrimitiveComponent;
|
|
69
|
+
/** True when `value` was tagged by {@link leaf}. */
|
|
70
|
+
export declare function isLeaf(value: unknown): value is VeskTaggedLeafComponent;
|
|
71
|
+
/**
|
|
72
|
+
* True when `value` is callable as a Vesk component — i.e. it is a function at
|
|
73
|
+
* all. Every registry entry satisfies this; the distinction that matters for
|
|
74
|
+
* authoring is {@link isPrimitive} vs {@link isLeaf}.
|
|
75
|
+
*/
|
|
76
|
+
export declare function isVeskComponent(value: unknown): value is VeskLeafComponent;
|
|
77
|
+
/**
|
|
78
|
+
* Normalize a component call's return value into something the client renderer
|
|
79
|
+
* can insert.
|
|
80
|
+
*
|
|
81
|
+
* A compiled `.vsk` component returns a DOM `Node`, and the runtime components
|
|
82
|
+
* (`Image`, `Form`, `Md`, …) build one when `document` exists. A `primitive()`
|
|
83
|
+
* or `leaf()` written as a template string has no such branch — it returns a
|
|
84
|
+
* string on BOTH sides, because that is the whole point of the terse form. The
|
|
85
|
+
* server concatenates it into the HTML; the client would otherwise hand a
|
|
86
|
+
* string to `appendChild`/`replaceChild` and throw "parameter 1 is not of type
|
|
87
|
+
* 'Node'", taking the page's error boundary with it.
|
|
88
|
+
*
|
|
89
|
+
* So: a `Node` passes through untouched, a string is parsed into its root
|
|
90
|
+
* node(s), and every other value (including `null`/`undefined`/`false`, which
|
|
91
|
+
* callers test for) is returned as-is. On the server there is nothing to
|
|
92
|
+
* normalize into, so strings pass through unchanged.
|
|
93
|
+
*/
|
|
94
|
+
export declare function toDomNode<T>(value: T): T | Node | DocumentFragment;
|
|
95
|
+
/**
|
|
96
|
+
* Keep a rendered markup node in sync with the tracked values it was built
|
|
97
|
+
* from, without re-invoking the component at mount.
|
|
98
|
+
*
|
|
99
|
+
* A `primitive()` or `leaf()` written as a template string is called once and
|
|
100
|
+
* its result is converted to DOM once. Tracked values it READ (through the
|
|
101
|
+
* `reactiveProps` proxy) therefore subscribe nobody: a later write updated the
|
|
102
|
+
* cell and left the markup stale, so a leaf driven by a parent's counter never
|
|
103
|
+
* moved. Compiled components avoid this because their bindings re-run; string
|
|
104
|
+
* components have no bindings, so the runtime re-runs the call and swaps the
|
|
105
|
+
* produced nodes in place.
|
|
106
|
+
*
|
|
107
|
+
* The dep list is read by the effect purely to SUBSCRIBE. The first effect run
|
|
108
|
+
* therefore performs no re-render — it compares against the values captured at
|
|
109
|
+
* mount, which also covers a write that lands between mount and the first
|
|
110
|
+
* flush. Re-rendering is skipped when the node has been detached (its owner was
|
|
111
|
+
* torn down), so a stale effect can never graft nodes back into the document.
|
|
112
|
+
*
|
|
113
|
+
* @param node - The node (or fragment) the mount already inserted.
|
|
114
|
+
* @param deps - Tracked cells / values the markup was derived from.
|
|
115
|
+
* @param call - Re-invokes the component and returns fresh markup.
|
|
116
|
+
* @returns `node`, so the call can wrap the mount expression in place.
|
|
117
|
+
*/
|
|
118
|
+
export declare function rerenderNode<T>(node: T, deps: readonly unknown[], call: () => unknown): T;
|
|
119
|
+
//# sourceMappingURL=primitive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitive.d.ts","sourceRoot":"","sources":["../src/primitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EACX,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,SAAS,EACT,uBAAuB,EACvB,MAAM,aAAa,CAAC;AAmBrB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,qBAAqB,EAAE,KAAK,CAAC,EAAE,SAAS,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAC/F,sBAAsB,CAAC,CAAC,CAAC,CAS3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,GACtB,uBAAuB,CAAC,CAAC,CAAC,CAG5B;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CAE3E;AAED,oDAAoD;AACpD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,uBAAuB,CAEvE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE1E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,gBAAgB,CAwBlE;AAYD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,OAAO,GAAG,CAAC,CA4CzF"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `primitive` / `leaf` — the two authoring wrappers for the Vesk component ABI.
|
|
3
|
+
*
|
|
4
|
+
* Every component call in Vesk, including a bare `<Badge/>` with no
|
|
5
|
+
* attributes, is invoked as `callee(props, __registry, __veskScope)`. The
|
|
6
|
+
* compiler always passes an object literal at the call site (`{}` when there
|
|
7
|
+
* are no attributes), so `props` is never nullish coming *from the framework*.
|
|
8
|
+
*
|
|
9
|
+
* The gap this module closes is the other direction: a component exported from
|
|
10
|
+
* a module is an ordinary function, and an ordinary function can be called
|
|
11
|
+
* with no arguments at all — `Badge()` from a helper, a test, a render
|
|
12
|
+
* callback, or a manual `registry.get('Badge')!(undefined, reg, scope)` probe.
|
|
13
|
+
* A hand-written `function Badge(props) { return props.children }` throws
|
|
14
|
+
* `Cannot read properties of undefined` in that case. `primitive()` removes
|
|
15
|
+
* that whole class of failure by normalizing nullish props to a frozen empty
|
|
16
|
+
* object before the wrapped function ever sees them.
|
|
17
|
+
*
|
|
18
|
+
* `leaf()` is the behavioural counterpart: stateful widgets, own click
|
|
19
|
+
* handlers, async components. They implement the `VeskLeafComponent` ABI
|
|
20
|
+
* directly — the wrapper only stamps the marker so the two kinds can be told
|
|
21
|
+
* apart at runtime.
|
|
22
|
+
*
|
|
23
|
+
* Both wrappers are pure: no `document`/`window` access, no top-level side
|
|
24
|
+
* effects, safe to import from the client and server barrels alike.
|
|
25
|
+
*/
|
|
26
|
+
import { get } from '@vesk/runtime/src/ripple-runtime';
|
|
27
|
+
import { effect } from '@vesk/runtime/src/ripple-blocks';
|
|
28
|
+
/**
|
|
29
|
+
* Shared stand-in for "called with no props". Frozen so a primitive that
|
|
30
|
+
* mutates `props` by mistake fails loudly in strict mode instead of leaking
|
|
31
|
+
* state between unrelated zero-arg calls.
|
|
32
|
+
*/
|
|
33
|
+
const EMPTY_PROPS = Object.freeze({});
|
|
34
|
+
/**
|
|
35
|
+
* Wrap a presentational component so it is safe to call with no arguments.
|
|
36
|
+
*
|
|
37
|
+
* The wrapped function is *not* a copy of `fn` — it forwards `props`,
|
|
38
|
+
* `__registry` and the scope untouched, so identity-sensitive behavior
|
|
39
|
+
* (scope preference, `__veskScope`) is unchanged. Only a nullish `props` is
|
|
40
|
+
* substituted; a real props object is never cloned, merged or stripped, which
|
|
41
|
+
* matters because on the client `props.children` can be a live reactive
|
|
42
|
+
* container that must reach `fn` by reference.
|
|
43
|
+
*
|
|
44
|
+
* @param fn - The presentational component. Receives `(props, registry, scope)`.
|
|
45
|
+
* @returns A {@link VeskPrimitiveComponent} callable as `Badge()` or `<Badge/>`.
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const Badge = primitive((props) => `<span class="badge">${props.label ?? ''}</span>`);
|
|
49
|
+
* Badge(); // '' — no throw
|
|
50
|
+
* Badge({ label: 'new' }); // '<span class="badge">new</span>'
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function primitive(fn) {
|
|
54
|
+
const wrapped = ((props, registry, scope) => {
|
|
55
|
+
// Nullish-only guard: any other value is the caller's business. A frozen
|
|
56
|
+
// singleton avoids an allocation per zero-arg call and keeps `Object.is`
|
|
57
|
+
// stable for the common no-props case.
|
|
58
|
+
return fn(props == null ? EMPTY_PROPS : props, registry, scope);
|
|
59
|
+
});
|
|
60
|
+
wrapped.__veskPrimitive = true;
|
|
61
|
+
return wrapped;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Tag a stateful/async component as a leaf so `isLeaf()` can identify it.
|
|
65
|
+
*
|
|
66
|
+
* Behaviourally a no-op — the function is returned as-is (plus the marker) so
|
|
67
|
+
* the `__veskScope` property the SSR renderer assigns to compiled components
|
|
68
|
+
* stays writable. Use it to document intent and to let tooling distinguish a
|
|
69
|
+
* behavioural widget from a `primitive()` one; do not use it to give a
|
|
70
|
+
* primitive extra behavior it cannot support.
|
|
71
|
+
*
|
|
72
|
+
* @param fn - The leaf component implementing `(props, registry, scope)`.
|
|
73
|
+
* @returns The same function, tagged with `__veskLeaf`.
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const Tabs = leaf((props, registry, scope) => {
|
|
77
|
+
* const &[open] = track(0);
|
|
78
|
+
* return `<div>${open}</div>`;
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export function leaf(fn) {
|
|
83
|
+
fn.__veskLeaf = true;
|
|
84
|
+
return fn;
|
|
85
|
+
}
|
|
86
|
+
/** True when `value` was produced by {@link primitive}. */
|
|
87
|
+
export function isPrimitive(value) {
|
|
88
|
+
return typeof value === 'function' && value.__veskPrimitive === true;
|
|
89
|
+
}
|
|
90
|
+
/** True when `value` was tagged by {@link leaf}. */
|
|
91
|
+
export function isLeaf(value) {
|
|
92
|
+
return typeof value === 'function' && value.__veskLeaf === true;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* True when `value` is callable as a Vesk component — i.e. it is a function at
|
|
96
|
+
* all. Every registry entry satisfies this; the distinction that matters for
|
|
97
|
+
* authoring is {@link isPrimitive} vs {@link isLeaf}.
|
|
98
|
+
*/
|
|
99
|
+
export function isVeskComponent(value) {
|
|
100
|
+
return typeof value === 'function';
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Normalize a component call's return value into something the client renderer
|
|
104
|
+
* can insert.
|
|
105
|
+
*
|
|
106
|
+
* A compiled `.vsk` component returns a DOM `Node`, and the runtime components
|
|
107
|
+
* (`Image`, `Form`, `Md`, …) build one when `document` exists. A `primitive()`
|
|
108
|
+
* or `leaf()` written as a template string has no such branch — it returns a
|
|
109
|
+
* string on BOTH sides, because that is the whole point of the terse form. The
|
|
110
|
+
* server concatenates it into the HTML; the client would otherwise hand a
|
|
111
|
+
* string to `appendChild`/`replaceChild` and throw "parameter 1 is not of type
|
|
112
|
+
* 'Node'", taking the page's error boundary with it.
|
|
113
|
+
*
|
|
114
|
+
* So: a `Node` passes through untouched, a string is parsed into its root
|
|
115
|
+
* node(s), and every other value (including `null`/`undefined`/`false`, which
|
|
116
|
+
* callers test for) is returned as-is. On the server there is nothing to
|
|
117
|
+
* normalize into, so strings pass through unchanged.
|
|
118
|
+
*/
|
|
119
|
+
export function toDomNode(value) {
|
|
120
|
+
if (typeof document === 'undefined')
|
|
121
|
+
return value;
|
|
122
|
+
if (value == null || typeof value === 'boolean')
|
|
123
|
+
return value;
|
|
124
|
+
if (typeof value.nodeType === 'number') {
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
if (typeof value !== 'string')
|
|
128
|
+
return value;
|
|
129
|
+
if (value === '')
|
|
130
|
+
return document.createDocumentFragment();
|
|
131
|
+
const template = document.createElement('template');
|
|
132
|
+
template.innerHTML = value;
|
|
133
|
+
const { content } = template;
|
|
134
|
+
// A single root becomes that node; several roots stay a fragment so the
|
|
135
|
+
// caller can splice them in without inventing a wrapper element that was
|
|
136
|
+
// never in the SSR markup.
|
|
137
|
+
if (content.childNodes.length === 1) {
|
|
138
|
+
const only = content.firstChild;
|
|
139
|
+
// Detach before returning: a node still parented to the template's
|
|
140
|
+
// content fragment reads as "already mounted" to the compiler's
|
|
141
|
+
// hydrate guard (`value.parentNode == null`), so the fresh node would
|
|
142
|
+
// never be inserted and the claimed SSR node would be retired instead.
|
|
143
|
+
content.removeChild(only);
|
|
144
|
+
return only;
|
|
145
|
+
}
|
|
146
|
+
return content;
|
|
147
|
+
}
|
|
148
|
+
/** True when `value` is a tracked cell (the shape `get`/`set` operate on). */
|
|
149
|
+
function isCell(value) {
|
|
150
|
+
return typeof value === 'object' && value !== null && typeof value.f === 'number';
|
|
151
|
+
}
|
|
152
|
+
/** Read a dep as the value a render would see: cells unwrap, literals pass. */
|
|
153
|
+
function readDep(dep) {
|
|
154
|
+
return isCell(dep) ? get(dep) : dep;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Keep a rendered markup node in sync with the tracked values it was built
|
|
158
|
+
* from, without re-invoking the component at mount.
|
|
159
|
+
*
|
|
160
|
+
* A `primitive()` or `leaf()` written as a template string is called once and
|
|
161
|
+
* its result is converted to DOM once. Tracked values it READ (through the
|
|
162
|
+
* `reactiveProps` proxy) therefore subscribe nobody: a later write updated the
|
|
163
|
+
* cell and left the markup stale, so a leaf driven by a parent's counter never
|
|
164
|
+
* moved. Compiled components avoid this because their bindings re-run; string
|
|
165
|
+
* components have no bindings, so the runtime re-runs the call and swaps the
|
|
166
|
+
* produced nodes in place.
|
|
167
|
+
*
|
|
168
|
+
* The dep list is read by the effect purely to SUBSCRIBE. The first effect run
|
|
169
|
+
* therefore performs no re-render — it compares against the values captured at
|
|
170
|
+
* mount, which also covers a write that lands between mount and the first
|
|
171
|
+
* flush. Re-rendering is skipped when the node has been detached (its owner was
|
|
172
|
+
* torn down), so a stale effect can never graft nodes back into the document.
|
|
173
|
+
*
|
|
174
|
+
* @param node - The node (or fragment) the mount already inserted.
|
|
175
|
+
* @param deps - Tracked cells / values the markup was derived from.
|
|
176
|
+
* @param call - Re-invokes the component and returns fresh markup.
|
|
177
|
+
* @returns `node`, so the call can wrap the mount expression in place.
|
|
178
|
+
*/
|
|
179
|
+
export function rerenderNode(node, deps, call) {
|
|
180
|
+
if (typeof document === 'undefined')
|
|
181
|
+
return node;
|
|
182
|
+
if (deps.length === 0)
|
|
183
|
+
return node;
|
|
184
|
+
// The markup this call site owns. A component that rendered several roots
|
|
185
|
+
// hands them over as a list: the call site drains the fragment into the
|
|
186
|
+
// mount point, so by the time this runs the fragment itself is detached and
|
|
187
|
+
// useless as a handle. The list is followed as one range, which lets a
|
|
188
|
+
// later render add, drop or reorder roots.
|
|
189
|
+
let owned = null;
|
|
190
|
+
let current = null;
|
|
191
|
+
if (Array.isArray(node)) {
|
|
192
|
+
const roots = node.filter((n) => n && typeof n.nodeType === 'number');
|
|
193
|
+
owned = roots.length ? roots : null;
|
|
194
|
+
current = owned ? owned[0] : null;
|
|
195
|
+
}
|
|
196
|
+
else if (node && typeof node.nodeType === 'number') {
|
|
197
|
+
current = node;
|
|
198
|
+
}
|
|
199
|
+
if (!current)
|
|
200
|
+
return node;
|
|
201
|
+
const liveRange = () => {
|
|
202
|
+
if (owned)
|
|
203
|
+
return owned;
|
|
204
|
+
return current && current.parentNode ? [current] : null;
|
|
205
|
+
};
|
|
206
|
+
let seen = deps.map(readDep);
|
|
207
|
+
effect(() => {
|
|
208
|
+
const now = deps.map(readDep);
|
|
209
|
+
if (now.length === seen.length && now.every((v, i) => Object.is(v, seen[i])))
|
|
210
|
+
return;
|
|
211
|
+
seen = now;
|
|
212
|
+
// Detachment is checked BEFORE re-invoking: once the owner is gone the
|
|
213
|
+
// component must not run again (side effects, fetches) and its markup
|
|
214
|
+
// must never be grafted back into the document.
|
|
215
|
+
const old = liveRange();
|
|
216
|
+
if (!old || !old[0].parentNode)
|
|
217
|
+
return;
|
|
218
|
+
const next = toDomNode(call());
|
|
219
|
+
if (!next || typeof next.nodeType !== 'number')
|
|
220
|
+
return;
|
|
221
|
+
// Snapshot the children before inserting: moving them out empties the
|
|
222
|
+
// fragment, which is what tells us whether this render had several roots.
|
|
223
|
+
const fresh = next.nodeType === 11 ? Array.from(next.childNodes) : [next];
|
|
224
|
+
const parent = old[0].parentNode;
|
|
225
|
+
for (const freshNode of fresh)
|
|
226
|
+
parent.insertBefore(freshNode, old[0]);
|
|
227
|
+
for (const oldNode of old)
|
|
228
|
+
if (oldNode.parentNode)
|
|
229
|
+
oldNode.parentNode.removeChild(oldNode);
|
|
230
|
+
owned = next.nodeType === 11 ? fresh : null;
|
|
231
|
+
current = fresh[0] ?? current;
|
|
232
|
+
});
|
|
233
|
+
return node;
|
|
234
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vesk/runtime",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.43",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vesk runtime — reactivity, blocks, DOM operations, hydration",
|
|
6
6
|
"main": "./dist/index-client.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@vesk/compiler": "^0.2.
|
|
54
|
+
"@vesk/compiler": "^0.2.43"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"typecheck": "tsc --noEmit",
|