@jxsuite/runtime 6.1.0 → 6.2.1
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/runtime.js +37 -5
- package/dist/runtime.js.map +3 -3
- package/package.json +2 -2
- package/src/runtime.ts +76 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/runtime",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.2.1",
|
|
4
4
|
"description": "JSON-native reactive web component runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"upgrade": "bun update --latest && bun install"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@jxsuite/schema": "^5.
|
|
36
|
+
"@jxsuite/schema": "^5.4.0",
|
|
37
37
|
"@vue/reactivity": "3.5.44"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
package/src/runtime.ts
CHANGED
|
@@ -388,6 +388,19 @@ export async function resolve(source: string | JxDocument, ctx?: JxContext): Pro
|
|
|
388
388
|
`app shell (a single-page fallback), so this path is not served.`,
|
|
389
389
|
);
|
|
390
390
|
}
|
|
391
|
+
/* A site origin answers `.jxn` as JSON, so only an editing server or a bare file host gets here
|
|
392
|
+
with KDL, and the parser is the host's to supply: a lazy import here would be inlined by any
|
|
393
|
+
host that bundles the runtime into one file, and ship the parser to every reader. */
|
|
394
|
+
if (JXN_URL.test(source) && !contentType.includes("json")) {
|
|
395
|
+
if (!_documentParser) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Jx: ${source} is a .jxn file and this page has no parser for it. Serve it from a Jx ` +
|
|
398
|
+
`site, which answers JSON, or call setDocumentParser(parseJxn) with parseJxn from ` +
|
|
399
|
+
`@jxsuite/schema/jxn.`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
return _documentParser(await res.text(), source);
|
|
403
|
+
}
|
|
391
404
|
/* SAFETY: trust boundary. `resolve` fetches only URLs that name a Jx document (a `Jx()` source,
|
|
392
405
|
an `$elements` entry, a `$switch` case), and the host answered 2xx with a non-HTML body, so
|
|
393
406
|
the JSON is that document; the runtime does not schema-validate documents by design. */
|
|
@@ -399,6 +412,28 @@ export async function resolve(source: string | JxDocument, ctx?: JxContext): Pro
|
|
|
399
412
|
|
|
400
413
|
// ─── Step 2: Build scope ──────────────────────────────────────────────────────
|
|
401
414
|
|
|
415
|
+
/** The path part of a URL naming a `.jxn` file. */
|
|
416
|
+
const JXN_URL = /\.jxn(?:[?#]|$)/iu;
|
|
417
|
+
|
|
418
|
+
/** What reads a `.jxn` file's text. Absent, the runtime refuses one rather than guessing. */
|
|
419
|
+
let _documentParser: ((text: string, source: string) => JxDocument | Promise<JxDocument>) | null =
|
|
420
|
+
null;
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Supply what reads a `.jxn` file, for a host that serves one as its text.
|
|
424
|
+
*
|
|
425
|
+
* A compiled site never needs this, and a `$ref` from a Jx site origin does not: both get JSON.
|
|
426
|
+
* Studio's canvas does, because an editing server answers a file as it is.
|
|
427
|
+
*
|
|
428
|
+
* @param parser `parseJxn` from `@jxsuite/schema/jxn`, or a function that loads it first, or null
|
|
429
|
+
* to take it away again.
|
|
430
|
+
*/
|
|
431
|
+
export function setDocumentParser(
|
|
432
|
+
parser: ((text: string, source: string) => JxDocument | Promise<JxDocument>) | null,
|
|
433
|
+
): void {
|
|
434
|
+
_documentParser = parser;
|
|
435
|
+
}
|
|
436
|
+
|
|
402
437
|
/** JSON Schema keywords used to identify pure type definitions (Shape 2b). */
|
|
403
438
|
const SCHEMA_KEYWORDS = new Set([
|
|
404
439
|
"type",
|
|
@@ -1686,7 +1721,15 @@ export const RESERVED_KEYS = new Set([
|
|
|
1686
1721
|
]);
|
|
1687
1722
|
|
|
1688
1723
|
/**
|
|
1689
|
-
* A child scope:
|
|
1724
|
+
* A child scope: a `$map` row, or a node carrying `$`-prefixed local bindings or `$props`. Reads
|
|
1725
|
+
* fall through to `parent`, and so does a write to any key the chain above already holds.
|
|
1726
|
+
*
|
|
1727
|
+
* The write is the half a bare `Object.create(parent)` gets wrong, and it gets it wrong silently.
|
|
1728
|
+
* An assignment that reaches the reactive proxy through a prototype chain arrives with the child as
|
|
1729
|
+
* its receiver, so the proxy defines the value on the child and triggers nothing: the handler's
|
|
1730
|
+
* `count += 1` shadows `count`, the root never changes, and the bindings reading it never re-run. A
|
|
1731
|
+
* compiled document assigns to the root directly, so the same document counted there and sat still
|
|
1732
|
+
* here. A value this scope owns is declared with {@link bindLocal}, which is what shadows.
|
|
1690
1733
|
*
|
|
1691
1734
|
* @param {JxScope} parent
|
|
1692
1735
|
* @returns {JxScope}
|
|
@@ -1694,7 +1737,35 @@ export const RESERVED_KEYS = new Set([
|
|
|
1694
1737
|
function inheritScope(parent: JxScope): JxScope {
|
|
1695
1738
|
/* SAFETY: `Object.create` is typed `any`, but what it returns is a plain object whose prototype
|
|
1696
1739
|
is `parent`, so it is keyed by string exactly as the scope it extends is. */
|
|
1697
|
-
|
|
1740
|
+
const local = Object.create(parent) as JxScope;
|
|
1741
|
+
return new Proxy(local, {
|
|
1742
|
+
set(target, key: string, value: JxScopeValue) {
|
|
1743
|
+
if (Object.hasOwn(target, key)) {
|
|
1744
|
+
return Reflect.set(target, key, value);
|
|
1745
|
+
}
|
|
1746
|
+
if (key in parent) {
|
|
1747
|
+
return Reflect.set(parent, key, value);
|
|
1748
|
+
}
|
|
1749
|
+
bindLocal(target, key, value);
|
|
1750
|
+
return true;
|
|
1751
|
+
},
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/**
|
|
1756
|
+
* Declare `key` on a child scope itself, shadowing whatever its parents hold under that name.
|
|
1757
|
+
*
|
|
1758
|
+
* @param {JxScope} scope
|
|
1759
|
+
* @param {string} key
|
|
1760
|
+
* @param {JxScopeValue} value
|
|
1761
|
+
*/
|
|
1762
|
+
function bindLocal(scope: JxScope, key: string, value: JxScopeValue): void {
|
|
1763
|
+
Object.defineProperty(scope, key, {
|
|
1764
|
+
configurable: true,
|
|
1765
|
+
enumerable: true,
|
|
1766
|
+
value,
|
|
1767
|
+
writable: true,
|
|
1768
|
+
});
|
|
1698
1769
|
}
|
|
1699
1770
|
|
|
1700
1771
|
/**
|
|
@@ -1819,7 +1890,7 @@ export function renderNode(
|
|
|
1819
1890
|
if (localState === state) {
|
|
1820
1891
|
localState = inheritScope(state);
|
|
1821
1892
|
}
|
|
1822
|
-
localState
|
|
1893
|
+
bindLocal(localState, key, isRefValue(val) ? resolveRef(val.$ref, state) : val);
|
|
1823
1894
|
}
|
|
1824
1895
|
}
|
|
1825
1896
|
|
|
@@ -3060,7 +3131,7 @@ function renderMappedArrayInto(
|
|
|
3060
3131
|
): MappedRow => {
|
|
3061
3132
|
const map = shallowReactive({ index, item });
|
|
3062
3133
|
const child = inheritScope(state);
|
|
3063
|
-
child
|
|
3134
|
+
bindLocal(child, "$map", map);
|
|
3064
3135
|
// The flat aliases stay readable by legacy readers, and follow the reactive context.
|
|
3065
3136
|
Object.defineProperty(child, "$map/item", { enumerable: true, get: () => map.item });
|
|
3066
3137
|
Object.defineProperty(child, "$map/index", { enumerable: true, get: () => map.index });
|
|
@@ -4417,7 +4488,7 @@ function mergeProps(def: JxElement, parentState: JxScope): JxScope {
|
|
|
4417
4488
|
if (refusePrivateProp(k, "$props")) {
|
|
4418
4489
|
continue;
|
|
4419
4490
|
}
|
|
4420
|
-
child
|
|
4491
|
+
bindLocal(child, k, isRefValue(v) ? resolveRef(v.$ref, parentState) : v);
|
|
4421
4492
|
}
|
|
4422
4493
|
return child;
|
|
4423
4494
|
}
|