@jxsuite/runtime 6.3.0 → 7.0.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/dist/runtime.js CHANGED
@@ -1511,7 +1511,91 @@ function maskCode(body) {
1511
1511
  }
1512
1512
  var masked = new Map;
1513
1513
  var MASKED_LIMIT = 2048;
1514
- function scanCode(body) {
1514
+ function templateExpressions(template) {
1515
+ if (!template.includes("${")) {
1516
+ return NO_EXPRESSIONS;
1517
+ }
1518
+ const known = scanned.get(template);
1519
+ if (known !== undefined) {
1520
+ return known;
1521
+ }
1522
+ const found = [];
1523
+ let from = 0;
1524
+ while (from < template.length) {
1525
+ const spans = [];
1526
+ scanCode(template.slice(from), spans);
1527
+ let unclosed = -1;
1528
+ for (let i = 0;i < spans.length; i += 2) {
1529
+ const start = from + (spans[i] ?? 0);
1530
+ const end = spans[i + 1] ?? -1;
1531
+ if (end === -1) {
1532
+ unclosed = start;
1533
+ break;
1534
+ }
1535
+ found.push(expressionAt(template, start, from + end));
1536
+ }
1537
+ if (unclosed === -1) {
1538
+ break;
1539
+ }
1540
+ const end = closeByBraceDepth(template, unclosed);
1541
+ if (end === -1) {
1542
+ from = unclosed + 2;
1543
+ } else {
1544
+ found.push(expressionAt(template, unclosed, end));
1545
+ from = end;
1546
+ }
1547
+ }
1548
+ if (scanned.size >= MASKED_LIMIT) {
1549
+ scanned.clear();
1550
+ }
1551
+ scanned.set(template, found);
1552
+ return found;
1553
+ }
1554
+ var NO_EXPRESSIONS = [];
1555
+ var scanned = new Map;
1556
+ function expressionAt(template, start, end) {
1557
+ return { end, expr: template.slice(start + 2, end - 1), start };
1558
+ }
1559
+ function closeByBraceDepth(template, start) {
1560
+ let depth = 0;
1561
+ for (let at = start + 1;at < template.length; at += 1) {
1562
+ const char = template.charAt(at);
1563
+ if (char === "{") {
1564
+ depth += 1;
1565
+ } else if (char === "}") {
1566
+ depth -= 1;
1567
+ if (depth === 0) {
1568
+ return at + 1;
1569
+ }
1570
+ }
1571
+ }
1572
+ return -1;
1573
+ }
1574
+ var BEFORE_OPERAND = new Set("(,=:[!&|?{;+-*%<>~^");
1575
+ function endsUpdateOperator(body, before, at) {
1576
+ return (before === "+" || before === "-") && body.charAt(at - 1) === before;
1577
+ }
1578
+ function regexLiteralEnd(body, at) {
1579
+ let inClass = false;
1580
+ for (let i = at + 1;i < body.length; i += 1) {
1581
+ const char = body.charAt(i);
1582
+ if (char === `
1583
+ `) {
1584
+ return -1;
1585
+ }
1586
+ if (char === "\\") {
1587
+ i += 1;
1588
+ } else if (char === "[") {
1589
+ inClass = true;
1590
+ } else if (char === "]") {
1591
+ inClass = false;
1592
+ } else if (char === "/" && !inClass) {
1593
+ return i + 1;
1594
+ }
1595
+ }
1596
+ return -1;
1597
+ }
1598
+ function scanCode(body, spans) {
1515
1599
  const out = body.split("");
1516
1600
  const blank = (at) => {
1517
1601
  if (out[at] !== undefined && out[at] !== `
@@ -1520,7 +1604,9 @@ function scanCode(body) {
1520
1604
  }
1521
1605
  };
1522
1606
  const substitutions = [];
1523
- let inTemplate = false;
1607
+ let inTemplate = spans !== undefined;
1608
+ let before = "";
1609
+ let beforeAt = -1;
1524
1610
  let at = 0;
1525
1611
  while (at < body.length) {
1526
1612
  const char = body.charAt(at);
@@ -1529,12 +1615,17 @@ function scanCode(body) {
1529
1615
  blank(at);
1530
1616
  blank(at + 1);
1531
1617
  at += 2;
1532
- } else if (char === "`") {
1618
+ } else if (char === "`" && !(spans && substitutions.length === 0)) {
1533
1619
  inTemplate = false;
1620
+ before = char;
1534
1621
  at += 1;
1535
1622
  } else if (char === "$" && body.charAt(at + 1) === "{") {
1623
+ if (substitutions.length === 0) {
1624
+ spans?.push(at, -1);
1625
+ }
1536
1626
  substitutions.push(0);
1537
1627
  inTemplate = false;
1628
+ before = "{";
1538
1629
  at += 2;
1539
1630
  } else {
1540
1631
  blank(at);
@@ -1563,10 +1654,18 @@ function scanCode(body) {
1563
1654
  blank(at);
1564
1655
  at += 1;
1565
1656
  }
1657
+ before = char;
1566
1658
  at += 1;
1567
1659
  } else if (char === "`") {
1568
1660
  inTemplate = true;
1569
1661
  at += 1;
1662
+ } else if (spans && char === "/" && BEFORE_OPERAND.has(before) && !endsUpdateOperator(body, before, beforeAt) && regexLiteralEnd(body, at) > 0) {
1663
+ const stop = regexLiteralEnd(body, at);
1664
+ for (at += 1;at < stop - 1; at += 1) {
1665
+ blank(at);
1666
+ }
1667
+ before = "/";
1668
+ at = stop;
1570
1669
  } else {
1571
1670
  const open = substitutions.length - 1;
1572
1671
  if (open >= 0 && char === "{") {
@@ -1575,10 +1674,17 @@ function scanCode(body) {
1575
1674
  if (substitutions[open] === 0) {
1576
1675
  substitutions.pop();
1577
1676
  inTemplate = true;
1677
+ if (spans && substitutions.length === 0) {
1678
+ spans[spans.length - 1] = at + 1;
1679
+ }
1578
1680
  } else {
1579
1681
  substitutions[open] = (substitutions[open] ?? 1) - 1;
1580
1682
  }
1581
1683
  }
1684
+ if (char.trim() !== "") {
1685
+ before = char;
1686
+ beforeAt = at;
1687
+ }
1582
1688
  at += 1;
1583
1689
  }
1584
1690
  }
@@ -2225,12 +2331,77 @@ function withPageLocale(path, args, state) {
2225
2331
  filled[index] = locale;
2226
2332
  return filled;
2227
2333
  }
2334
+ function windowRoot() {
2335
+ const root = globalThis.window;
2336
+ return root ?? globalThis;
2337
+ }
2228
2338
  function isBlessedGlobal(ref) {
2229
2339
  return ref.startsWith("window#/") && BLESSED_GLOBALS.has(ref.slice("window#/".length));
2230
2340
  }
2231
2341
  function isMutating(op) {
2232
2342
  return MUTATING_OPS.has(op);
2233
2343
  }
2344
+ function operandMutates(operand) {
2345
+ if (Array.isArray(operand)) {
2346
+ return operand.some((item) => operandMutates(item));
2347
+ }
2348
+ if (operand === undefined || !isNodeOperand(operand)) {
2349
+ return false;
2350
+ }
2351
+ return isMutating(operand.operator) || [operand.target, operand.value, operand.initial, operand.default].some((part) => operandMutates(part)) || Object.values(operand.cases ?? {}).some((part) => operandMutates(part));
2352
+ }
2353
+ function isPureNamedFormula(def) {
2354
+ return isNamedFormulaDef(def) && !operandMutates(def.$expression);
2355
+ }
2356
+ var OUTSIDE_SCOPE_REF = /^(?:\$args\/|\$reduce\/|\$map\/|window#\/|document#\/|event#\/)/;
2357
+ function collectScopeReads(operand, into) {
2358
+ if (Array.isArray(operand)) {
2359
+ for (const item of operand) {
2360
+ collectScopeReads(item, into);
2361
+ }
2362
+ return;
2363
+ }
2364
+ if (operand === undefined || !isNonNullObject(operand)) {
2365
+ return;
2366
+ }
2367
+ if (isRefOperand(operand)) {
2368
+ if (!OUTSIDE_SCOPE_REF.test(operand.$ref)) {
2369
+ const path = operand.$ref.replace(/^(?:#\/state\/|parent#\/)/, "");
2370
+ into.add(refSegments(path)[0] ?? "");
2371
+ }
2372
+ return;
2373
+ }
2374
+ if (isNodeOperand(operand)) {
2375
+ const parts = [operand.target, operand.value, operand.initial, operand.default];
2376
+ for (const part of [...parts, ...Object.values(operand.cases ?? {})]) {
2377
+ collectScopeReads(part, into);
2378
+ }
2379
+ }
2380
+ }
2381
+ function formulaScopeReads(def) {
2382
+ const reads = new Set;
2383
+ collectScopeReads(def.$expression, reads);
2384
+ return [...reads];
2385
+ }
2386
+ function inheritableFormulas(projectState) {
2387
+ const candidates = new Map;
2388
+ for (const [key, def] of Object.entries(projectState ?? {})) {
2389
+ if (!key.startsWith("$") && isPureNamedFormula(def)) {
2390
+ candidates.set(key, def);
2391
+ }
2392
+ }
2393
+ let settled = false;
2394
+ while (!settled) {
2395
+ settled = true;
2396
+ for (const [key, def] of candidates) {
2397
+ if (formulaScopeReads(def).some((read) => !candidates.has(read))) {
2398
+ candidates.delete(key);
2399
+ settled = false;
2400
+ }
2401
+ }
2402
+ }
2403
+ return Object.fromEntries(candidates);
2404
+ }
2234
2405
  var MAX_CALL_DEPTH = 64;
2235
2406
  var MAX_REPORT_DEPTH = 64;
2236
2407
  function subTrace(trace, seg) {
@@ -2324,7 +2495,7 @@ function resolveExprRef(ref, state, event, iterCtx) {
2324
2495
  return readPath(state, ref.slice("parent#/".length));
2325
2496
  }
2326
2497
  if (ref.startsWith("window#/")) {
2327
- return readPath(globalThis.window, ref.slice("window#/".length));
2498
+ return readPath(windowRoot(), ref.slice("window#/".length));
2328
2499
  }
2329
2500
  if (ref.startsWith("document#/")) {
2330
2501
  return readPath(globalThis.document, ref.slice("document#/".length));
@@ -2440,9 +2611,10 @@ function evaluateNode(node, state, event, iterCtx, trace) {
2440
2611
  if (helper) {
2441
2612
  return helper(...withPageLocale(globalPath, argValues, state));
2442
2613
  }
2443
- const fn = readPath(globalThis.window, globalPath);
2614
+ const root = windowRoot();
2615
+ const fn = readPath(root, globalPath);
2444
2616
  const lastSlash = globalPath.lastIndexOf("/");
2445
- const thisArg = lastSlash === -1 ? globalThis.window : readPath(globalThis.window, globalPath.slice(0, lastSlash));
2617
+ const thisArg = lastSlash === -1 ? root : readPath(root, globalPath.slice(0, lastSlash));
2446
2618
  return fn.apply(thisArg, argValues);
2447
2619
  }
2448
2620
  const callee = resolveExprRef(calleeRef, state, event, iterCtx);
@@ -2796,6 +2968,7 @@ async function runStatements(statements, state, event, opts = {}) {
2796
2968
 
2797
2969
  // src/body-eraser.ts
2798
2970
  var eraser = null;
2971
+ var moduleLoader = null;
2799
2972
  var loader = null;
2800
2973
  var loading = null;
2801
2974
  var erased = new Map;
@@ -2806,6 +2979,35 @@ function setBodyEraser(fn) {
2806
2979
  loading = null;
2807
2980
  }
2808
2981
  }
2982
+ function setModuleLoader(fn) {
2983
+ moduleLoader = fn;
2984
+ if (!fn) {
2985
+ loading = null;
2986
+ }
2987
+ }
2988
+ function canLoadModules() {
2989
+ return moduleLoader !== null || loader !== null;
2990
+ }
2991
+ function fetchEraser(load) {
2992
+ loading ??= load().then((module) => {
2993
+ if (loader !== load) {
2994
+ return;
2995
+ }
2996
+ if (!eraser) {
2997
+ setBodyEraser(module.eraseBody);
2998
+ }
2999
+ if (!moduleLoader && module.loadModule) {
3000
+ setModuleLoader(module.loadModule);
3001
+ }
3002
+ });
3003
+ return loading;
3004
+ }
3005
+ async function moduleLoaderFor() {
3006
+ if (!moduleLoader && loader) {
3007
+ await fetchEraser(loader);
3008
+ }
3009
+ return moduleLoader;
3010
+ }
2809
3011
  function setBodyEraserLoader(load) {
2810
3012
  loader = load;
2811
3013
  loading = null;
@@ -2864,12 +3066,7 @@ async function prepareBodies(doc) {
2864
3066
  if (!bodiesOf(tree).some((body) => bodyNeedsEraser(body))) {
2865
3067
  return;
2866
3068
  }
2867
- loading ??= load().then((module) => {
2868
- if (loader === load) {
2869
- setBodyEraser(module.eraseBody);
2870
- }
2871
- });
2872
- await loading;
3069
+ await fetchEraser(load);
2873
3070
  }
2874
3071
  function javaScriptOf(body, where) {
2875
3072
  if (!eraser) {
@@ -2899,6 +3096,75 @@ function compileBody(params, body, where) {
2899
3096
  }
2900
3097
  }
2901
3098
 
3099
+ // ../schema/src/document-scripts.ts
3100
+ var SCRIPT_SPECIFIER_PREFIX = "jx-script:";
3101
+ var CLIENT_SCRIPT_REF = "#client";
3102
+ var SERVER_SCRIPT_REF = "#server";
3103
+ var TYPESCRIPT_PATH = /\.[cm]?ts$/iu;
3104
+ function isTypeScriptPath(pathname) {
3105
+ return TYPESCRIPT_PATH.test(pathname);
3106
+ }
3107
+ function scriptRefSide(src) {
3108
+ if (src === CLIENT_SCRIPT_REF) {
3109
+ return "client";
3110
+ }
3111
+ if (src === SERVER_SCRIPT_REF) {
3112
+ return "server";
3113
+ }
3114
+ return null;
3115
+ }
3116
+ function normalizedPath(path) {
3117
+ return path.replaceAll("\\", "/").replace(/^(?:\.\/)+/, "");
3118
+ }
3119
+ function scriptSpecifier(path, side) {
3120
+ return `${SCRIPT_SPECIFIER_PREFIX}${normalizedPath(path)}#${side}`;
3121
+ }
3122
+ function isScriptSpecifier(src) {
3123
+ return src.startsWith(SCRIPT_SPECIFIER_PREFIX);
3124
+ }
3125
+ function parseScriptSpecifier(src) {
3126
+ if (!isScriptSpecifier(src)) {
3127
+ return null;
3128
+ }
3129
+ const rest = src.slice(SCRIPT_SPECIFIER_PREFIX.length);
3130
+ const hash = rest.lastIndexOf("#");
3131
+ if (hash <= 0) {
3132
+ return null;
3133
+ }
3134
+ const side = rest.slice(hash + 1);
3135
+ if (side !== "client" && side !== "server") {
3136
+ return null;
3137
+ }
3138
+ return { path: rest.slice(0, hash), side };
3139
+ }
3140
+ function qualifiedValue(value, path) {
3141
+ if (Array.isArray(value)) {
3142
+ const items = value.map((item) => qualifiedValue(item, path));
3143
+ return items.some((item, index) => item !== value[index]) ? items : value;
3144
+ }
3145
+ if (!isJsonObject(value)) {
3146
+ return value;
3147
+ }
3148
+ let copy = null;
3149
+ for (const [key, member] of Object.entries(value)) {
3150
+ let next = member;
3151
+ if (key === "$src" && isString2(member)) {
3152
+ const side = scriptRefSide(member);
3153
+ next = side === null ? member : scriptSpecifier(path, side);
3154
+ } else if (key !== "$script") {
3155
+ next = qualifiedValue(member, path);
3156
+ }
3157
+ if (next !== member) {
3158
+ copy ??= { ...value };
3159
+ copy[key] = next;
3160
+ }
3161
+ }
3162
+ return copy === null ? value : copy;
3163
+ }
3164
+ function qualifyScripts(doc, path) {
3165
+ return qualifiedValue(doc, path);
3166
+ }
3167
+
2902
3168
  // src/cookie.ts
2903
3169
  var HOST_PREFIX = "__Host-";
2904
3170
  var SECURE_PREFIX = "__Secure-";
@@ -2980,10 +3246,11 @@ function assertHostScope(scope) {
2980
3246
  }
2981
3247
  }
2982
3248
  }
2983
- async function mount(doc, target, options = {}) {
3249
+ async function mount(source, target, options = {}) {
2984
3250
  const hostScope = options.scope ? hostScopeOf(options.scope) : {};
2985
3251
  assertHostScope(hostScope);
2986
3252
  const base = options.base ? new URL(options.base, location.href).href : location.href;
3253
+ const doc = inlineDocument(source, base);
2987
3254
  const ctx = {
2988
3255
  base,
2989
3256
  media: options.media ?? doc.$media ?? null,
@@ -3019,6 +3286,15 @@ async function mount(doc, target, options = {}) {
3019
3286
  if (options.onNodeMoved) {
3020
3287
  renderOptions.onNodeMoved = options.onNodeMoved;
3021
3288
  }
3289
+ if (options.rowLimit !== undefined) {
3290
+ renderOptions.rowLimit = options.rowLimit;
3291
+ }
3292
+ if (options.emptyRow) {
3293
+ renderOptions.emptyRow = options.emptyRow;
3294
+ }
3295
+ if (options.onListReconciled) {
3296
+ renderOptions.onListReconciled = options.onListReconciled;
3297
+ }
3022
3298
  const { result: root, stop } = runScoped(() => renderNode(doc, scope, renderOptions));
3023
3299
  ctx.root = root;
3024
3300
  let mounted = false;
@@ -3065,7 +3341,7 @@ async function resolve(source, ctx) {
3065
3341
  }
3066
3342
  const served = ctx?.resolver?.(source);
3067
3343
  if (served) {
3068
- return served;
3344
+ return withOwnScripts(await served, source);
3069
3345
  }
3070
3346
  if (_resolveCache.has(source)) {
3071
3347
  return _resolveCache.get(source);
@@ -3086,7 +3362,8 @@ async function resolve(source, ctx) {
3086
3362
  }
3087
3363
  return await res.json();
3088
3364
  });
3089
- const p = fetched.then(async (doc) => {
3365
+ const p = fetched.then(async (read) => {
3366
+ const doc = withOwnScripts(read, source);
3090
3367
  await prepareBodies(doc);
3091
3368
  return doc;
3092
3369
  });
@@ -3119,6 +3396,10 @@ var _autoRequestConfig = { skip: false };
3119
3396
  function setSkipAutoRequests(v) {
3120
3397
  _autoRequestConfig.skip = v;
3121
3398
  }
3399
+ var _remoteLookupHandler = null;
3400
+ function setRemoteLookupHandler(handler) {
3401
+ _remoteLookupHandler = handler;
3402
+ }
3122
3403
  var _resolveToken = null;
3123
3404
  function setResolveToken(token) {
3124
3405
  _resolveToken = token || null;
@@ -3175,8 +3456,20 @@ function setCanvasDelinkCommands(on) {
3175
3456
  _canvasDelinkCommands = on;
3176
3457
  }
3177
3458
  var _canvasInsideStampedHost = false;
3459
+ function insideStampedHost(inside, fn) {
3460
+ const was = _canvasInsideStampedHost;
3461
+ _canvasInsideStampedHost = inside;
3462
+ try {
3463
+ return fn();
3464
+ } finally {
3465
+ _canvasInsideStampedHost = was;
3466
+ }
3467
+ }
3178
3468
  function canvasStamped(el) {
3179
- return _canvasInsideStampedHost || el.dataset.jxPath !== undefined;
3469
+ return _canvasInsideStampedHost || carriesCanvasStamp(el);
3470
+ }
3471
+ function carriesCanvasStamp(el) {
3472
+ return el.dataset.jxPath !== undefined || el.dataset.jxEchoPath !== undefined;
3180
3473
  }
3181
3474
  function canvasAttrName(el, key) {
3182
3475
  if (_canvasDelinkAnchors && key === "href" && (el.tagName === "A" || el.tagName === "AREA")) {
@@ -3274,7 +3567,8 @@ function boundPropKey(key, val, state) {
3274
3567
  }
3275
3568
  return sub;
3276
3569
  }
3277
- async function buildScope(doc, parentScope = {}, base = location.href, ctx) {
3570
+ async function buildScope(source, parentScope = {}, base = location.href, ctx) {
3571
+ const doc = inlineDocument(source, base);
3278
3572
  const dispatchRoot = () => ctx?.root ?? null;
3279
3573
  const raw = {};
3280
3574
  const defs = doc.state ?? {};
@@ -3488,6 +3782,7 @@ function bindTemplate(source, run, fallback) {
3488
3782
  run();
3489
3783
  heldTemplateErrors.delete(run);
3490
3784
  } catch (error) {
3785
+ _templateThrows += 1;
3491
3786
  const thrown = error instanceof Error ? error : new Error(String(error));
3492
3787
  if (_templateErrorHandler) {
3493
3788
  _templateErrorHandler(thrown, source);
@@ -3500,6 +3795,8 @@ function bindTemplate(source, run, fallback) {
3500
3795
  }
3501
3796
  });
3502
3797
  }
3798
+ var _templateThrows = 0;
3799
+ var _listRedraws = 0;
3503
3800
  function rowContext(state) {
3504
3801
  return state.$map;
3505
3802
  }
@@ -3507,19 +3804,8 @@ function isSingleExpression(str) {
3507
3804
  if (!str.startsWith("${") || !str.endsWith("}")) {
3508
3805
  return false;
3509
3806
  }
3510
- let depth = 0;
3511
- for (let i = 1;i < str.length; i += 1) {
3512
- const c = str[i];
3513
- if (c === "{") {
3514
- depth += 1;
3515
- } else if (c === "}") {
3516
- depth -= 1;
3517
- if (depth === 0) {
3518
- return i === str.length - 1;
3519
- }
3520
- }
3521
- }
3522
- return false;
3807
+ const [only, ...more] = templateExpressions(str);
3808
+ return only !== undefined && more.length === 0 && only.start === 0 && only.end === str.length;
3523
3809
  }
3524
3810
  function evaluateValueTemplate(str, state) {
3525
3811
  if (!isSingleExpression(str)) {
@@ -3532,6 +3818,9 @@ function evaluateValueTemplate(str, state) {
3532
3818
  }
3533
3819
  return fn(...templateArgs(state));
3534
3820
  }
3821
+ function evaluateTemplateValue(str, scope) {
3822
+ return evaluateValueTemplate(str, scope);
3823
+ }
3535
3824
  var _npmModuleResolver = null;
3536
3825
  function setNpmModuleResolver(resolver) {
3537
3826
  _npmModuleResolver = resolver;
@@ -3542,6 +3831,13 @@ async function importSource(src, base) {
3542
3831
  const specifier = src.slice(NPM_SCHEME.length);
3543
3832
  return importModule(_npmModuleResolver?.(specifier) ?? specifier);
3544
3833
  }
3834
+ const typed = typeScriptSource(src, base);
3835
+ if (typed !== null && (canLoadModules() || isScriptSpecifier(src))) {
3836
+ const loaded = await importTypeScript(typed, src, base);
3837
+ if (loaded) {
3838
+ return loaded;
3839
+ }
3840
+ }
3545
3841
  if (base) {
3546
3842
  try {
3547
3843
  return await importModule(new URL(src, base).href);
@@ -3551,6 +3847,70 @@ async function importSource(src, base) {
3551
3847
  }
3552
3848
  return importModule(src);
3553
3849
  }
3850
+ var FETCHED_SCHEMES = new Set(["http:", "https:"]);
3851
+ function typeScriptSource(src, base) {
3852
+ if (isScriptSpecifier(src)) {
3853
+ return src;
3854
+ }
3855
+ if (scriptRefSide(src) !== null) {
3856
+ throw new Error(`Jx: $src "${src}" names the script of the document it is written in, and that document ` + `has no $script`);
3857
+ }
3858
+ const from = base ?? location.href;
3859
+ if (!URL.canParse(src, from)) {
3860
+ return null;
3861
+ }
3862
+ const url = new URL(src, from);
3863
+ return FETCHED_SCHEMES.has(url.protocol) && isTypeScriptPath(url.pathname) ? url.href : null;
3864
+ }
3865
+ async function importTypeScript(specifier, src, base) {
3866
+ const load = await moduleLoaderFor();
3867
+ if (load) {
3868
+ const module = await load(specifier, {
3869
+ base,
3870
+ resolveNpm: _npmModuleResolver,
3871
+ text: _documentScripts.get(src)
3872
+ });
3873
+ return module;
3874
+ }
3875
+ if (isScriptSpecifier(src)) {
3876
+ throw new Error(`Jx: ${parseScriptSpecifier(src)?.path ?? src} has a script, and this page has nothing ` + `that can load one. Give it a module loader with setModuleLoader(loadModule), loadModule ` + `from @jxsuite/erase/browser, or let setBodyEraserLoader fetch that file when it is needed.`);
3877
+ }
3878
+ return null;
3879
+ }
3880
+ var _documentScripts = new Map;
3881
+ function withOwnScripts(doc, source) {
3882
+ if (!doc.$script) {
3883
+ return doc;
3884
+ }
3885
+ const address = URL.canParse(source, location.href) ? new URL(source, location.href).href : source;
3886
+ const { client } = doc.$script;
3887
+ const specifier = scriptSpecifier(address, "client");
3888
+ if (isString2(client) && _documentScripts.get(specifier) !== client) {
3889
+ if (_documentScripts.has(specifier) && !_hostSeeded.has(specifier)) {
3890
+ _moduleCache.delete(specifier);
3891
+ }
3892
+ _documentScripts.set(specifier, client);
3893
+ }
3894
+ const own = qualifyScripts(doc, address);
3895
+ _addressed.set(own, own);
3896
+ return own;
3897
+ }
3898
+ var _addressed = new WeakMap;
3899
+ var _inlineCount = 0;
3900
+ function inlineDocument(doc, base) {
3901
+ if (!doc.$script) {
3902
+ return doc;
3903
+ }
3904
+ let own = _addressed.get(doc);
3905
+ if (!own) {
3906
+ _inlineCount += 1;
3907
+ const address = new URL(base, location.href);
3908
+ address.hash = `jx-inline-${_inlineCount}`;
3909
+ own = withOwnScripts(doc, address.href);
3910
+ _addressed.set(doc, own);
3911
+ }
3912
+ return own;
3913
+ }
3554
3914
  function importModule(specifier) {
3555
3915
  return import(specifier);
3556
3916
  }
@@ -3594,6 +3954,28 @@ function seededModule(src) {
3594
3954
  _moduleLoaders.set(src, load);
3595
3955
  return load;
3596
3956
  }
3957
+ function acquireModule(src, base) {
3958
+ const seeded = seededModule(src);
3959
+ if (seeded) {
3960
+ return seeded;
3961
+ }
3962
+ const loading = importSource(src, base);
3963
+ loading.then((mod) => {
3964
+ _moduleCache.set(src, mod);
3965
+ }, () => {});
3966
+ return loading;
3967
+ }
3968
+ function exportedFunction(def, key, src, mod) {
3969
+ const exportName = def.$export ?? key;
3970
+ const candidate = mod[exportName] ?? mod.default?.[exportName];
3971
+ if (!isFunction2(candidate)) {
3972
+ throw new TypeError(`Jx: export "${exportName}" not found or not a function in "${src}"`);
3973
+ }
3974
+ return candidate;
3975
+ }
3976
+ async function importFunctionExport(def, key, src, base) {
3977
+ return exportedFunction(def, key, src, await acquireModule(src, base));
3978
+ }
3597
3979
  async function resolveFunction(def, state, key, role, base) {
3598
3980
  if (!def.body && !def.$src) {
3599
3981
  const params = handlerParamNames(def);
@@ -3612,27 +3994,25 @@ async function resolveFunction(def, state, key, role, base) {
3612
3994
  configurable: true,
3613
3995
  value: def.name ?? key
3614
3996
  });
3997
+ } else if (def.$lazy === true && def.$src) {
3998
+ const { $src: src } = def;
3999
+ if (role === "computed") {
4000
+ throw new Error(`Jx: "${key}" declares "$lazy": true but is used as a computed value, not called. A lazily loaded function resolves to a promise; a computed must resolve now. Remove "$lazy", or call ${key}() from a handler instead of binding its value.`);
4001
+ }
4002
+ let loaded;
4003
+ const load = () => loaded ??= importFunctionExport(def, key, src, base);
4004
+ fn = (...args) => load().then((target) => target(...args));
4005
+ Object.defineProperty(fn, "name", {
4006
+ configurable: true,
4007
+ value: def.name ?? key
4008
+ });
4009
+ return fn;
3615
4010
  } else {
3616
- const src = def.$src;
3617
- if (!src) {
4011
+ if (!def.$src) {
3618
4012
  throw new Error(`Jx: '${key}' has neither body nor $src`);
3619
4013
  }
3620
- const exportName = def.$export ?? key;
3621
- let mod;
3622
- const seeded = seededModule(src);
3623
- if (seeded instanceof Promise) {
3624
- mod = await seeded;
3625
- } else if (seeded) {
3626
- mod = seeded;
3627
- } else {
3628
- mod = await importSource(src, base);
3629
- _moduleCache.set(src, mod);
3630
- }
3631
- const candidate = mod[exportName] ?? mod.default?.[exportName];
3632
- if (!isFunction2(candidate)) {
3633
- throw new TypeError(`Jx: export "${exportName}" not found or not a function in "${src}"`);
3634
- }
3635
- fn = candidate;
4014
+ const acquired = acquireModule(def.$src, base);
4015
+ fn = exportedFunction(def, key, def.$src, acquired instanceof Promise ? await acquired : acquired);
3636
4016
  }
3637
4017
  if (role === "computed") {
3638
4018
  return computed(() => fn(state));
@@ -3653,6 +4033,7 @@ var RESERVED_KEYS = new Set([
3653
4033
  "$prototype",
3654
4034
  "$src",
3655
4035
  "$export",
4036
+ "$script",
3656
4037
  "$media",
3657
4038
  "$map",
3658
4039
  "timing",
@@ -3787,6 +4168,8 @@ function renderNode(def, state, options) {
3787
4168
  const childOpts = options || childNs ? { ...options, _ns: childNs, _path: [...path, "children", i] } : undefined;
3788
4169
  if (isMappedArray(child)) {
3789
4170
  renderMappedArrayInto(el, child, localState, childOpts);
4171
+ } else if (isString2(child) && isSingleExpression(child)) {
4172
+ renderTemplatedChild(el, child, localState, childOpts);
3790
4173
  } else {
3791
4174
  el.append(renderNode(child, localState, childOpts));
3792
4175
  }
@@ -3801,7 +4184,6 @@ function renderNode(def, state, options) {
3801
4184
  return el;
3802
4185
  }
3803
4186
  function renderComputedChildren(el, template, state, options) {
3804
- const path = options._path ?? [];
3805
4187
  let live = null;
3806
4188
  const retire = () => {
3807
4189
  el.replaceChildren();
@@ -3814,21 +4196,62 @@ function renderComputedChildren(el, template, state, options) {
3814
4196
  if (!Array.isArray(nodes)) {
3815
4197
  return;
3816
4198
  }
3817
- const scope = effectScope(true);
3818
- live = scope;
3819
- pauseTracking();
3820
- try {
3821
- scope.run(() => {
3822
- for (const [i, child] of nodes.entries()) {
3823
- el.append(renderNode(child, state, { ...options, _path: [...path, i] }));
3824
- }
3825
- });
3826
- } finally {
3827
- resetTracking();
3828
- }
4199
+ live = effectScope(true);
4200
+ drawTemplateNodes(live, nodes, state, options, (node) => {
4201
+ el.append(node);
4202
+ });
3829
4203
  }, retire);
3830
4204
  onScopeDispose(() => live?.stop(), true);
3831
4205
  }
4206
+ function drawTemplateNodes(scope, nodes, state, options, place) {
4207
+ const path = options?._path ?? [];
4208
+ const members = [...nodes];
4209
+ pauseTracking();
4210
+ try {
4211
+ scope.run(() => {
4212
+ for (const [i, child] of members.entries()) {
4213
+ place(renderNode(child, state, options ? { ...options, _path: [...path, i] } : undefined));
4214
+ }
4215
+ });
4216
+ } finally {
4217
+ resetTracking();
4218
+ }
4219
+ }
4220
+ function renderTemplatedChild(parent, template, state, options) {
4221
+ const anchor = document.createTextNode(template);
4222
+ parent.append(anchor);
4223
+ let live = null;
4224
+ let drawn = [];
4225
+ const retire = () => {
4226
+ for (const node of drawn) {
4227
+ node.remove();
4228
+ }
4229
+ drawn = [];
4230
+ live?.stop();
4231
+ live = null;
4232
+ };
4233
+ bindTemplate(template, () => {
4234
+ const value = evaluateValueTemplate(template, state);
4235
+ retire();
4236
+ if (!Array.isArray(value)) {
4237
+ anchor.textContent = templateText(value);
4238
+ return;
4239
+ }
4240
+ anchor.textContent = "";
4241
+ live = effectScope(true);
4242
+ drawTemplateNodes(live, value, state, options, (node) => {
4243
+ drawn.push(node);
4244
+ anchor.before(node);
4245
+ });
4246
+ }, () => {
4247
+ retire();
4248
+ anchor.textContent = "";
4249
+ });
4250
+ onScopeDispose(() => live?.stop(), true);
4251
+ }
4252
+ function templateText(value) {
4253
+ return `${value}`;
4254
+ }
3832
4255
  function applyProperties(el, def, state) {
3833
4256
  for (const [key, val] of Object.entries(def)) {
3834
4257
  if (RESERVED_KEYS.has(key)) {
@@ -4202,10 +4625,14 @@ function applyStyleInto(el, styleDef, mediaQueries, state, live) {
4202
4625
  const scope = effectScope();
4203
4626
  scope.run(() => {
4204
4627
  for (const { name, value } of sources) {
4205
- effect(() => {
4206
- const resolved = isRef2(value) ? resolveRef(value.$ref, state) : evaluateTemplate(value, state);
4628
+ const write = (resolved) => {
4207
4629
  el.style.setProperty(name, canvasStyleValue(String(resolved ?? "")));
4208
- });
4630
+ };
4631
+ if (isRef2(value)) {
4632
+ effect(() => write(resolveRef(value.$ref, state)));
4633
+ } else {
4634
+ bindTemplate(value, () => write(evaluateTemplate(value, state)));
4635
+ }
4209
4636
  }
4210
4637
  });
4211
4638
  entry.stop = () => scope.stop();
@@ -4259,6 +4686,7 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
4259
4686
  const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef, key: keyRef } = arrayDef;
4260
4687
  const keyPointer = isRef2(keyRef) ? keyRef.$ref : null;
4261
4688
  const instancePath = _instancePath;
4689
+ const insideStamped = _canvasInsideStampedHost;
4262
4690
  let warned = false;
4263
4691
  const warnOnce = (message) => {
4264
4692
  if (!warned) {
@@ -4280,7 +4708,9 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
4280
4708
  const scope = effectScope(true);
4281
4709
  const childOpts = options ? { ...options, _path: [...path, "map", index] } : undefined;
4282
4710
  const node = scope.run(() => renderNode(mapDef, child, childOpts));
4283
- return { key, map, node, scope };
4711
+ const row = { key, map, node, scope };
4712
+ listRows.set(node, row);
4713
+ return row;
4284
4714
  };
4285
4715
  const reconcile = (list, keys) => {
4286
4716
  const previous = new Map;
@@ -4335,8 +4765,17 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
4335
4765
  items = [...unsorted].toSorted(compare);
4336
4766
  }
4337
4767
  }
4338
- const list = Array.isArray(items) && mapDef ? items : [];
4339
- const keys = list.map((item, index) => mappedRowKey(keyPointer, item, index, warnOnce));
4768
+ let list = Array.isArray(items) && mapDef ? items : [];
4769
+ const count = list.length;
4770
+ const placeholder = count === 0 && mapDef ? options?.emptyRow?.(arrayDef, path) : undefined;
4771
+ if (placeholder !== undefined) {
4772
+ list = [placeholder];
4773
+ }
4774
+ const limit = options?.rowLimit;
4775
+ if (isNumber(limit) && limit >= 0 && list.length > limit) {
4776
+ list = list.slice(0, Math.floor(limit));
4777
+ }
4778
+ const keys = placeholder === undefined ? list.map((item, index) => mappedRowKey(keyPointer, item, index, warnOnce)) : list.map(() => EMPTY_ROW_KEY);
4340
4779
  const seen = new Set;
4341
4780
  for (let i = 0;i < keys.length; i++) {
4342
4781
  if (seen.has(keys[i])) {
@@ -4348,7 +4787,13 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
4348
4787
  }
4349
4788
  pauseTracking();
4350
4789
  try {
4351
- underInstancePath(reexpand(instancePath), () => reconcile(list, keys));
4790
+ underInstancePath(reexpand(instancePath), () => insideStampedHost(insideStamped, () => reconcile(list, keys)));
4791
+ options?.onListReconciled?.(path, {
4792
+ count,
4793
+ empty: count === 0,
4794
+ nodes: rows.map((row) => row.node),
4795
+ rendered: rows.length
4796
+ });
4352
4797
  } finally {
4353
4798
  resetTracking();
4354
4799
  }
@@ -4357,6 +4802,7 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
4357
4802
  let stopped = false;
4358
4803
  const runner = effect(update, {
4359
4804
  scheduler: () => {
4805
+ _listRedraws += 1;
4360
4806
  if (queued) {
4361
4807
  return;
4362
4808
  }
@@ -4377,6 +4823,21 @@ function renderMappedArrayInto(parentEl, arrayDef, state, options) {
4377
4823
  rows = [];
4378
4824
  }, true);
4379
4825
  }
4826
+ var EMPTY_ROW_KEY = Object.freeze({ emptyRow: true });
4827
+ var listRows = new WeakMap;
4828
+ function replaceListRow(current, next, stop) {
4829
+ const row = listRows.get(current);
4830
+ if (!row || row.node !== current) {
4831
+ return false;
4832
+ }
4833
+ row.node.replaceWith(next);
4834
+ row.scope.stop();
4835
+ listRows.delete(current);
4836
+ row.node = next;
4837
+ row.scope = { stop: () => stop?.() };
4838
+ listRows.set(next, row);
4839
+ return true;
4840
+ }
4380
4841
  function mappedRowKey(pointer, item, index, warnOnce) {
4381
4842
  if (pointer === null) {
4382
4843
  return index;
@@ -4413,6 +4874,7 @@ function renderSwitch(def, state, options) {
4413
4874
  const path = options?._path ?? [];
4414
4875
  const container = document.createElement(resolveTagName(def.tagName, state));
4415
4876
  const instancePath = _instancePath;
4877
+ const insideStamped = _canvasInsideStampedHost;
4416
4878
  if (options?.onNodeCreated) {
4417
4879
  options.onNodeCreated(container, path, def, state);
4418
4880
  }
@@ -4455,18 +4917,18 @@ function renderSwitch(def, state, options) {
4455
4917
  if (gen !== generation) {
4456
4918
  return;
4457
4919
  }
4458
- const childScope = await buildScope(doc, {}, href, options?._ctx);
4920
+ const childScope = await buildScope(withProjectFormulas(doc), {}, href, options?._ctx);
4459
4921
  if (gen !== generation) {
4460
4922
  return;
4461
4923
  }
4462
4924
  container.replaceChildren();
4463
- underInstancePath(reexpand(instancePath), () => scope.run(() => container.append(renderNode(doc, childScope, childOpts))));
4925
+ underInstancePath(reexpand(instancePath), () => insideStampedHost(insideStamped, () => scope.run(() => container.append(renderNode(doc, childScope, childOpts)))));
4464
4926
  }).catch((error) => console.error("Jx $switch: failed to load external case", caseDef.$ref, error));
4465
4927
  return;
4466
4928
  }
4467
4929
  pauseTracking();
4468
4930
  try {
4469
- underInstancePath(reexpand(instancePath), () => scope.run(() => container.append(renderNode(caseDef, state, childOpts))));
4931
+ underInstancePath(reexpand(instancePath), () => insideStampedHost(insideStamped, () => scope.run(() => container.append(renderNode(caseDef, state, childOpts)))));
4470
4932
  } finally {
4471
4933
  resetTracking();
4472
4934
  }
@@ -4502,18 +4964,46 @@ async function resolvePrototype(def, state, key, base, ctx) {
4502
4964
  clearTimeout(debounceTimer);
4503
4965
  }
4504
4966
  });
4967
+ const request = (signal) => fetch(url, {
4968
+ method: def.method ?? "GET",
4969
+ ...signal && { signal },
4970
+ ...def.headers && { headers: def.headers },
4971
+ ...def.body && {
4972
+ body: isNonNullObject(def.body) ? JSON.stringify(def.body) : def.body
4973
+ }
4974
+ }).then((r) => r.ok ? r.json() : Promise.reject(r.statusText));
4505
4975
  const doFetch = () => {
4506
- holdTemplateErrorsFor(fetch(url, {
4507
- method: def.method ?? "GET",
4508
- signal: controller.signal,
4509
- ...def.headers && { headers: def.headers },
4510
- ...def.body && {
4511
- body: isNonNullObject(def.body) ? JSON.stringify(def.body) : def.body
4976
+ const handler = _remoteLookupHandler;
4977
+ let lookup;
4978
+ if (handler) {
4979
+ const answer = handler({
4980
+ key,
4981
+ kind: "request",
4982
+ request: {
4983
+ method: def.method ?? "GET",
4984
+ url,
4985
+ ...def.headers && { headers: def.headers },
4986
+ ...def.body !== undefined && { body: def.body }
4987
+ },
4988
+ scope: state
4989
+ }, () => request());
4990
+ if (answer === null) {
4991
+ return;
4992
+ }
4993
+ if (!(answer instanceof Promise)) {
4994
+ s.value = answer.value;
4995
+ return;
4996
+ }
4997
+ lookup = answer;
4998
+ } else {
4999
+ lookup = request(controller.signal);
5000
+ }
5001
+ holdTemplateErrorsFor(lookup.then((d) => {
5002
+ if (!controller.signal.aborted) {
5003
+ s.value = d;
4512
5004
  }
4513
- }).then((r) => r.ok ? r.json() : Promise.reject(r.statusText)).then((d) => {
4514
- s.value = d;
4515
5005
  }).catch((error) => {
4516
- if (!(error instanceof Error && error.name === "AbortError")) {
5006
+ if (!controller.signal.aborted && !(error instanceof Error && error.name === "AbortError")) {
4517
5007
  s.value = { error: String(error) };
4518
5008
  }
4519
5009
  }));
@@ -4864,24 +5354,46 @@ async function resolveViaDevProxy(def, state, key, base) {
4864
5354
  }
4865
5355
  return s;
4866
5356
  }
4867
- function latestServerCall(s, invoke, label) {
5357
+ function latestServerCall(s, invoke, label, describe) {
4868
5358
  let seq = 0;
4869
5359
  return (args) => {
4870
5360
  seq += 1;
4871
5361
  const n = seq;
4872
- holdTemplateErrorsFor(new Promise((settle) => {
5362
+ const run = () => new Promise((settle) => {
4873
5363
  settle(invoke(args));
4874
- }).then((result) => {
5364
+ });
5365
+ const answer = _remoteLookupHandler ? _remoteLookupHandler(describe(args), run) : run();
5366
+ if (answer === null) {
5367
+ return;
5368
+ }
5369
+ if (!(answer instanceof Promise)) {
5370
+ s.value = answer.value;
5371
+ return;
5372
+ }
5373
+ holdTemplateErrorsFor(answer.then((result) => {
4875
5374
  if (n === seq) {
4876
5375
  s.value = result;
4877
5376
  }
4878
5377
  }).catch((error) => {
4879
- if (n === seq) {
5378
+ if (n === seq && !(error instanceof Error && error.name === "AbortError")) {
4880
5379
  console.error(label, error);
4881
5380
  }
4882
5381
  }));
4883
5382
  };
4884
5383
  }
5384
+ function serverLookup(def, state, key, args, base) {
5385
+ return {
5386
+ key,
5387
+ kind: "server",
5388
+ request: {
5389
+ $export: def.$export,
5390
+ $src: def.$src,
5391
+ arguments: args,
5392
+ ...base !== undefined && { $base: base }
5393
+ },
5394
+ scope: state
5395
+ };
5396
+ }
4885
5397
  async function resolveServerFunction(def, state, key, base) {
4886
5398
  const src = def.$src;
4887
5399
  const exportName = def.$export;
@@ -4909,7 +5421,7 @@ async function resolveServerFunction(def, state, key, base) {
4909
5421
  };
4910
5422
  const s = ref(null);
4911
5423
  if (hasReactiveArg) {
4912
- const call = latestServerCall(s, fn, "Jx server function:");
5424
+ const call = latestServerCall(s, fn, "Jx server function:", (args) => serverLookup(def, state, key, args, base));
4913
5425
  effect(() => {
4914
5426
  const args = resolveArgs();
4915
5427
  onEffectCleanup(() => {});
@@ -4946,7 +5458,7 @@ async function resolveServerFunctionViaProxy(def, state, key, base) {
4946
5458
  return r.json();
4947
5459
  });
4948
5460
  const s = ref(null);
4949
- const call = latestServerCall(s, doResolve, "Jx server proxy:");
5461
+ const call = latestServerCall(s, doResolve, "Jx server proxy:", (args) => serverLookup(def, state, key, args, base));
4950
5462
  if (hasReactiveArg) {
4951
5463
  effect(() => {
4952
5464
  const args = resolveArgs();
@@ -5089,6 +5601,23 @@ async function redefineElement(doc, baseUrl) {
5089
5601
  }
5090
5602
  _elementDefs.set(tagName, { base, doc });
5091
5603
  }
5604
+ var _projectFormulas = {};
5605
+ var _withFormulas = new WeakMap;
5606
+ function setProjectFormulas(projectState) {
5607
+ _projectFormulas = inheritableFormulas(projectState);
5608
+ _withFormulas = new WeakMap;
5609
+ }
5610
+ function withProjectFormulas(doc) {
5611
+ const cached = _withFormulas.get(doc);
5612
+ if (cached) {
5613
+ return cached;
5614
+ }
5615
+ const own = doc.state ?? {};
5616
+ const added = Object.entries(_projectFormulas).filter(([key]) => !(key in own));
5617
+ const merged = added.length > 0 ? { ...doc, state: { ...Object.fromEntries(added), ...own } } : doc;
5618
+ _withFormulas.set(doc, merged);
5619
+ return merged;
5620
+ }
5092
5621
  function setRootMedia(map) {
5093
5622
  _rootMedia = map ?? {};
5094
5623
  }
@@ -5235,7 +5764,7 @@ async function defineElement(source, baseUrl) {
5235
5764
  _definedSources.add(base);
5236
5765
  source_ = await resolve(source);
5237
5766
  } else {
5238
- source_ = source;
5767
+ source_ = inlineDocument(source, new URL(base ?? location.href, location.href).href);
5239
5768
  }
5240
5769
  base ??= location.href;
5241
5770
  const { tagName } = source_;
@@ -5252,6 +5781,7 @@ async function defineElement(source, baseUrl) {
5252
5781
  const initialDef = source_;
5253
5782
  const initialBase = base;
5254
5783
  const observedAttrs = initialDef.observedAttributes ?? [];
5784
+ _observedAttributes.set(tagName, observedAttrs);
5255
5785
  const ElementClass = class extends HTMLElement {
5256
5786
  _jxInitialized = false;
5257
5787
  _state = null;
@@ -5269,7 +5799,8 @@ async function defineElement(source, baseUrl) {
5269
5799
  const enclosingPath = _instancePath;
5270
5800
  const supplied = {};
5271
5801
  const live = _elementDefs.get(tagName) ?? { base: initialBase, doc: initialDef };
5272
- const def = live.doc;
5802
+ _connectedDefs.set(this, live.doc);
5803
+ const def = withProjectFormulas(live.doc);
5273
5804
  const defBase = live.base;
5274
5805
  const state = await buildScope(def, {}, defBase, { base: defBase, media: null, root: this });
5275
5806
  for (const attr of observedAttrs) {
@@ -5326,10 +5857,14 @@ async function defineElement(source, baseUrl) {
5326
5857
  reportUnrendered(this, tagName, error instanceof Error ? error : new Error(String(error)));
5327
5858
  return;
5328
5859
  }
5860
+ const declared = live.doc.state ?? {};
5329
5861
  for (const key of Object.keys(def.state ?? {})) {
5330
5862
  if (isPrivateStateKey(key)) {
5331
5863
  continue;
5332
5864
  }
5865
+ if (!(key in declared) && !(key in supplied)) {
5866
+ continue;
5867
+ }
5333
5868
  const native = nativeSetter(this, key);
5334
5869
  Object.defineProperty(this, key, {
5335
5870
  configurable: true,
@@ -5355,7 +5890,7 @@ async function defineElement(source, baseUrl) {
5355
5890
  const children = Array.isArray(def.children) ? def.children : [];
5356
5891
  const draw = () => {
5357
5892
  const wasInside = _canvasInsideStampedHost;
5358
- _canvasInsideStampedHost ||= this.dataset.jxPath !== undefined;
5893
+ _canvasInsideStampedHost ||= carriesCanvasStamp(this);
5359
5894
  let failure = null;
5360
5895
  try {
5361
5896
  underInstancePath(ownPath, () => {
@@ -5445,6 +5980,195 @@ async function defineElement(source, baseUrl) {
5445
5980
  };
5446
5981
  customElements.define(tagName, ElementClass);
5447
5982
  }
5983
+ function writeInstanceProp(el, key, resolved) {
5984
+ const value = isString2(resolved) ? canvasAssetValue(el.tagName, key, resolved) : resolved;
5985
+ elementProperties(el)[key] = value;
5986
+ const instanceState = instanceStateOf(el);
5987
+ if (instanceState && key in HTMLElement.prototype && Object.hasOwn(toRaw(instanceState), key)) {
5988
+ instanceState[key] = value;
5989
+ }
5990
+ }
5991
+ var _observedAttributes = new Map;
5992
+ var _connectedDefs = new WeakMap;
5993
+ function drawnByCurrent(el) {
5994
+ const drawn = _connectedDefs.get(el);
5995
+ return drawn !== undefined && drawn === _elementDefs.get(el.localName)?.doc;
5996
+ }
5997
+ function producedElsewhere(entry) {
5998
+ if (!isJsonObject(entry)) {
5999
+ return false;
6000
+ }
6001
+ const { $prototype: prototype } = entry;
6002
+ if (prototype !== undefined) {
6003
+ return prototype !== "Function";
6004
+ }
6005
+ return "$src" in entry || "timing" in entry;
6006
+ }
6007
+ function isScalarProp(value) {
6008
+ return isString2(value) && !isTemplateString(value) || isNumber(value) || isBoolean(value);
6009
+ }
6010
+ var VERBATIM_PROPS = new Set(["id", "lang", "role", "slot", "title"]);
6011
+ function isBoundProp(value) {
6012
+ return isRef2(value) || isTemplateString(value);
6013
+ }
6014
+ function rendersOnce(def, drawing = new Set) {
6015
+ const declared = def.state ?? {};
6016
+ if ("onMount" in declared || Object.values(declared).some((entry) => producedElsewhere(entry))) {
6017
+ return true;
6018
+ }
6019
+ drawing.add(def);
6020
+ const walk = (value, isRoot, redrawn) => {
6021
+ if (Array.isArray(value)) {
6022
+ return value.some((item) => walk(item, false, redrawn));
6023
+ }
6024
+ if (!isNonNullObject(value)) {
6025
+ return false;
6026
+ }
6027
+ const node = Object.fromEntries(Object.entries(value));
6028
+ const { attributes, tagName } = node;
6029
+ if (tagName !== undefined && !isString2(tagName)) {
6030
+ return true;
6031
+ }
6032
+ const attrs = isJsonObject(attributes) ? attributes : {};
6033
+ if (tagName === "slot" && (redrawn || isBoundProp(attrs.name) || isBoundProp(node.name) || isBoundProp(node.textContent) || node.$switch !== undefined)) {
6034
+ return true;
6035
+ }
6036
+ if (isBoundProp(node.innerHTML) || !isRoot && isBoundProp(node.children)) {
6037
+ return true;
6038
+ }
6039
+ const isInstance = !isRoot && isString2(tagName) && tagName.includes("-");
6040
+ const inner = isInstance ? _elementDefs.get(tagName)?.doc : undefined;
6041
+ if (isInstance && handsOverOnce(node, attrs, inner)) {
6042
+ return true;
6043
+ }
6044
+ if (inner && (drawing.has(inner) || rendersOnce(inner, drawing))) {
6045
+ return true;
6046
+ }
6047
+ for (const [key, entry] of Object.entries(node)) {
6048
+ if (isRoot && (key === "state" || key === "$props")) {
6049
+ continue;
6050
+ }
6051
+ if (key === "$props") {
6052
+ if (!isInstance && isJsonObject(entry) && Object.values(entry).some((v) => isRef2(v))) {
6053
+ return true;
6054
+ }
6055
+ continue;
6056
+ }
6057
+ if ((key === "id" || key.startsWith("$") && !RESERVED_KEYS.has(key)) && isRef2(entry)) {
6058
+ return true;
6059
+ }
6060
+ const drawn = key === "cases" && isJsonObject(entry) ? Object.values(entry) : entry;
6061
+ if (walk(drawn, false, redrawn || key === "cases" || key === "map")) {
6062
+ return true;
6063
+ }
6064
+ }
6065
+ return false;
6066
+ };
6067
+ const answer = walk(def, true, false);
6068
+ drawing.delete(def);
6069
+ return answer;
6070
+ }
6071
+ function handsOverOnce(node, attrs, inner) {
6072
+ if (isBoundProp(node.textContent)) {
6073
+ return true;
6074
+ }
6075
+ const kids = Array.isArray(node.children) ? node.children : [];
6076
+ if (kids.some((kid) => isBoundProp(kid) || isMappedArray(kid) || isJsonObject(kid) && (isBoundProp(kid.slot) || isJsonObject(kid.attributes) && isBoundProp(kid.attributes.slot)))) {
6077
+ return true;
6078
+ }
6079
+ const coerced = (given) => Object.entries(given).some(([key, value]) => isBoundProp(value) && (key in HTMLElement.prototype) && key !== "textContent");
6080
+ if (coerced(node) || isJsonObject(node.$props) && coerced(node.$props)) {
6081
+ return true;
6082
+ }
6083
+ const observed = isString2(node.tagName) ? _observedAttributes.get(node.tagName) ?? [] : [];
6084
+ return Object.entries(attrs).some(([name, given]) => {
6085
+ if (!isBoundProp(given)) {
6086
+ return false;
6087
+ }
6088
+ if (name.startsWith("props.")) {
6089
+ return true;
6090
+ }
6091
+ const prop = name.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
6092
+ return !inner || prop in (inner.state ?? {}) && !observed.includes(name);
6093
+ });
6094
+ }
6095
+ function mountedFromProps(host, state) {
6096
+ if (isScopeHook(state.onMount) || !drawnByCurrent(host)) {
6097
+ return true;
6098
+ }
6099
+ for (const inner of host.querySelectorAll("*")) {
6100
+ const innerState = instanceStateOf(inner);
6101
+ if (!innerState || !isScopeHook(innerState.onMount) && drawnByCurrent(inner)) {
6102
+ continue;
6103
+ }
6104
+ let owner = inner;
6105
+ while (owner && owner !== host && owner.dataset.jxPath === undefined) {
6106
+ owner = owner.parentElement;
6107
+ }
6108
+ if (owner === host) {
6109
+ return true;
6110
+ }
6111
+ }
6112
+ return false;
6113
+ }
6114
+ function updateInstanceProps(el, before, after) {
6115
+ const state = instanceStateOf(el);
6116
+ const registered = _elementDefs.get(el.localName)?.doc;
6117
+ if (!state || !registered) {
6118
+ return false;
6119
+ }
6120
+ const def = withProjectFormulas(registered);
6121
+ const defaults = declaredDefaults(def);
6122
+ const declared = def.state ?? {};
6123
+ const observed = _observedAttributes.get(el.localName) ?? [];
6124
+ const writes = [];
6125
+ for (const key of new Set([...Object.keys(before ?? {}), ...Object.keys(after ?? {})])) {
6126
+ const was = before?.[key];
6127
+ const now = after?.[key];
6128
+ if (isPrivateStateKey(key) || JSON.stringify(was) === JSON.stringify(now)) {
6129
+ continue;
6130
+ }
6131
+ if (was !== undefined && !isScalarProp(was) || now !== undefined && !isScalarProp(now) || producedElsewhere(declared[key]) || key in declared && !isScalarProp(toRaw(state)[key]) || key === "_state" || key === "_jxInitialized") {
6132
+ return false;
6133
+ }
6134
+ if (key in HTMLElement.prototype && !(VERBATIM_PROPS.has(key) && (key in declared) && isString2(now))) {
6135
+ return false;
6136
+ }
6137
+ if (now !== undefined) {
6138
+ writes.push(() => {
6139
+ writeInstanceProp(el, key, now);
6140
+ });
6141
+ continue;
6142
+ }
6143
+ if (!(key in declared)) {
6144
+ writes.push(() => {
6145
+ Reflect.deleteProperty(el, key);
6146
+ });
6147
+ continue;
6148
+ }
6149
+ const fallback = defaults.get(key);
6150
+ const attribute = key.replaceAll(/[A-Z]/gu, (c) => `-${c.toLowerCase()}`);
6151
+ if (!isScalarProp(fallback) || observed.includes(attribute) && el.hasAttribute(attribute)) {
6152
+ return false;
6153
+ }
6154
+ writes.push(() => {
6155
+ state[key] = fallback;
6156
+ });
6157
+ }
6158
+ if (writes.length === 0 || mountedFromProps(el, state) || rendersOnce(def)) {
6159
+ return false;
6160
+ }
6161
+ const thrown = _templateThrows;
6162
+ const redraws = _listRedraws;
6163
+ try {
6164
+ for (const write of writes) {
6165
+ write();
6166
+ }
6167
+ } catch {
6168
+ return false;
6169
+ }
6170
+ return _templateThrows === thrown && _listRedraws === redraws;
6171
+ }
5448
6172
  function renderCustomElementWithProps(def, state, options, path) {
5449
6173
  const el = document.createElement(resolveTagName(def.tagName, state));
5450
6174
  if (options?.onNodeCreated) {
@@ -5455,12 +6179,7 @@ function renderCustomElementWithProps(def, state, options, path) {
5455
6179
  continue;
5456
6180
  }
5457
6181
  const write = (resolved) => {
5458
- const value = isString2(resolved) ? canvasAssetValue(el.tagName, key, resolved) : resolved;
5459
- elementProperties(el)[key] = value;
5460
- const instanceState = instanceStateOf(el);
5461
- if (instanceState && key in HTMLElement.prototype && Object.hasOwn(toRaw(instanceState), key)) {
5462
- instanceState[key] = value;
5463
- }
6182
+ writeInstanceProp(el, key, resolved);
5464
6183
  };
5465
6184
  if (isRef2(val)) {
5466
6185
  const refVal = val;
@@ -5488,6 +6207,8 @@ function renderCustomElementWithProps(def, state, options, path) {
5488
6207
  const childOpts = options && path ? { ...options, _path: [...path, "children", i] } : undefined;
5489
6208
  if (isMappedArray(child)) {
5490
6209
  renderMappedArrayInto(el, child, state, childOpts);
6210
+ } else if (isString2(child) && isSingleExpression(child)) {
6211
+ renderTemplatedChild(el, child, state, childOpts);
5491
6212
  } else {
5492
6213
  el.append(renderNode(child, state, childOpts));
5493
6214
  }
@@ -5610,6 +6331,7 @@ export {
5610
6331
  documentStyleText,
5611
6332
  elementDefinition,
5612
6333
  enumeratedAttrNames,
6334
+ evaluateTemplateValue,
5613
6335
  hasSchemaKeywords,
5614
6336
  hashCss,
5615
6337
  isDeclarationAtRule,
@@ -5628,6 +6350,7 @@ export {
5628
6350
  redefineElement,
5629
6351
  releaseElementStyles,
5630
6352
  renderNode,
6353
+ replaceListRow,
5631
6354
  resetDocumentStyles,
5632
6355
  resolve,
5633
6356
  resolveAtQuery,
@@ -5645,7 +6368,10 @@ export {
5645
6368
  setCanvasDelinkPopovers,
5646
6369
  setCanvasViewportTranspose,
5647
6370
  setDocumentParser,
6371
+ setModuleLoader,
5648
6372
  setNpmModuleResolver,
6373
+ setProjectFormulas,
6374
+ setRemoteLookupHandler,
5649
6375
  setResolveToken,
5650
6376
  setRootMedia,
5651
6377
  setSkipAutoRequests,
@@ -5657,8 +6383,9 @@ export {
5657
6383
  toCSSText,
5658
6384
  transposeCanvasOverlaySelector,
5659
6385
  transposeCanvasPopoverSelector,
5660
- transposeCanvasUnits
6386
+ transposeCanvasUnits,
6387
+ updateInstanceProps
5661
6388
  };
5662
6389
 
5663
- //# debugId=E573B4C42927B2CC64756E2164756E21
6390
+ //# debugId=A3ACE1824EF1406664756E2164756E21
5664
6391
  //# sourceMappingURL=runtime.js.map