@fuaran-ui/ops 0.21.0 → 0.22.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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { FORM_FIELD_KIND_NAMES, NODE_KIND_GROUPS, defaults, controlValueDefaults, MAX_NODE_DEPTH, MAX_NODES, MAX_STRING_LENGTH, MAX_JSON_DEPTH, MAX_ARRAY_LENGTH, narrows, admitAll, NODE_KIND_NAMES, admits, policyHint, projectSelectionField } from '@fuaran-ui/schema';
1
+ import { FORM_FIELD_KIND_NAMES, NODE_KIND_GROUPS, defaults, controlValueDefaults, MAX_DOCUMENT_BYTES, MAX_NODE_DEPTH, MAX_NODES, MAX_JSON_DEPTH, MAX_ARRAY_LENGTH, MAX_STRING_LENGTH, narrows, admitAll, NODE_KIND_NAMES, admits, policyHint, MAX_EXPR_NODES, projectSelectionField } from '@fuaran-ui/schema';
2
2
 
3
3
  // src/encode.ts
4
4
  var assertNever = (x) => {
@@ -384,9 +384,12 @@ var binding = (b, staticEnc = objValue) => {
384
384
  }
385
385
  case "Computed":
386
386
  return caseObj("Computed", [["fn", CLOSURE]]);
387
- // No wire fields: the instant is host-furnished at resolve time.
387
+ // Phase 765 the INSTANT is never on the wire: it is host-furnished at
388
+ // resolve time. Phase 1533 — the declared GRAIN is, and only when it is not
389
+ // the `Second` default, so a grain-less `Now` is the same bytes it has
390
+ // always been.
388
391
  case "Now":
389
- return caseObj("Now", []);
392
+ return caseObj("Now", b.grain !== void 0 ? [["grain", str(b.grain)]] : []);
390
393
  case "I18n": {
391
394
  const fields = [];
392
395
  if (b.args !== void 0) {
@@ -398,14 +401,18 @@ var binding = (b, staticEnc = objValue) => {
398
401
  fields.push(["key", str(b.key)]);
399
402
  return caseObj("I18n", fields);
400
403
  }
401
- case "Local":
402
- return caseObj("Local", [
404
+ case "Local": {
405
+ const localFields = [
403
406
  ["flushOn", flushTrigger(b.local.flushOn)],
404
407
  ["format", CLOSURE],
405
408
  ["initialFrom", binding(b.local.initialFrom, staticEnc)],
406
- ["onCommit", CLOSURE],
407
409
  ["parse", CLOSURE]
408
- ]);
410
+ ];
411
+ if (b.local.codec !== void 0) localFields.push(["codec", formatIntent(b.local.codec)]);
412
+ if (b.local.commitTo !== void 0) localFields.push(["commitTo", str(b.local.commitTo)]);
413
+ if (b.local.onCommit !== void 0) localFields.push(["onCommit", CLOSURE]);
414
+ return caseObj("Local", localFields);
415
+ }
409
416
  case "Format":
410
417
  return caseObj("Format", [
411
418
  ["format", formatIntent(b.format)],
@@ -435,6 +442,22 @@ var binding = (b, staticEnc = objValue) => {
435
442
  ]
436
443
  ]);
437
444
  }
445
+ case "Expr": {
446
+ const exprParams = b.params !== void 0 && b.params.length > 0 ? [
447
+ [
448
+ "params",
449
+ jArray(
450
+ b.params.map(
451
+ (p) => jObject([
452
+ ["from", binding(p.from)],
453
+ ["name", str(p.name)]
454
+ ])
455
+ )
456
+ )
457
+ ]
458
+ ] : [];
459
+ return caseObj("Expr", [["expr", colExpr(b.expr)], ...exprParams]);
460
+ }
438
461
  case "Invoke":
439
462
  return caseObj("Invoke", [
440
463
  ["args", invokeArgs(b.args)],
@@ -464,8 +487,11 @@ var action = (a) => {
464
487
  ["channel", str(a.channel)],
465
488
  ["payload", jsonValue(a.payload)]
466
489
  ]);
467
- case "Navigate":
468
- return caseObj("Navigate", [["route", str(a.route)]]);
490
+ case "Navigate": {
491
+ const fields = [["route", textSource(a.route)]];
492
+ if (a.target !== "Self") fields.push(["target", str(a.target)]);
493
+ return caseObj("Navigate", fields);
494
+ }
469
495
  case "SetState": {
470
496
  const fields = [["key", str(a.key)]];
471
497
  if (a.value !== void 0) fields.push(["value", jsonValue(a.value)]);
@@ -485,6 +511,15 @@ var action = (a) => {
485
511
  return caseObj("WriteToClipboard", [["text", textSource(a.text)]]);
486
512
  case "Print":
487
513
  return caseObj("Print", []);
514
+ case "Confirm": {
515
+ const fields = [];
516
+ if (a.onCancel !== void 0) fields.push(["onCancel", action(a.onCancel)]);
517
+ fields.push(["onConfirm", action(a.onConfirm)]);
518
+ fields.push(["prompt", textSource(a.prompt)]);
519
+ return caseObj("Confirm", fields);
520
+ }
521
+ case "Focus":
522
+ return caseObj("Focus", [["nodeId", str(a.nodeId)]]);
488
523
  case "ReadFileBody":
489
524
  return caseObj("ReadFileBody", [
490
525
  ["encoding", str(a.encoding)],
@@ -559,6 +594,10 @@ var formatIntent = (f) => {
559
594
  ["style", str(f.style)],
560
595
  ["unit", str(f.unit)]
561
596
  ]);
597
+ // Phase 1533 — `unit` omitted when absent: its absence is the
598
+ // auto-selection request, not a default that could be spelled out.
599
+ case "Since":
600
+ return caseObj("Since", f.unit !== void 0 ? [["unit", str(f.unit)]] : []);
562
601
  default:
563
602
  return assertNever(f);
564
603
  }
@@ -1341,6 +1380,25 @@ var gridSpec = (s) => {
1341
1380
  }
1342
1381
  return jObject(fields);
1343
1382
  };
1383
+ var chartAnnotationX = (a) => a.kind === "Category" ? caseObj("Category", [["key", str(a.key)]]) : caseObj("Date", [["iso", str(a.iso)]]);
1384
+ var chartAnnotationRange = (r) => r.kind === "ValueRange" ? caseObj("ValueRange", [
1385
+ ["from", num(r.from)],
1386
+ ["to", num(r.to)]
1387
+ ]) : caseObj("XRange", [
1388
+ ["from", chartAnnotationX(r.from)],
1389
+ ["to", chartAnnotationX(r.to)]
1390
+ ]);
1391
+ var chartAnnotation = (a) => {
1392
+ const label = a.label !== void 0 ? [["label", textSource(a.label)]] : [];
1393
+ switch (a.kind) {
1394
+ case "ReferenceLine":
1395
+ return caseObj("ReferenceLine", [["value", num(a.value)], ...label]);
1396
+ case "EventMarker":
1397
+ return caseObj("EventMarker", [["at", chartAnnotationX(a.at)], ...label]);
1398
+ default:
1399
+ return caseObj("RangeBand", [["range", chartAnnotationRange(a.range)], ...label]);
1400
+ }
1401
+ };
1344
1402
  var chartSpec = (s) => {
1345
1403
  const fields = [
1346
1404
  ["kind", str(s.kind)],
@@ -1358,6 +1416,8 @@ var chartSpec = (s) => {
1358
1416
  if (s.legendPosition !== void 0) fields.push(["legendPosition", str(s.legendPosition)]);
1359
1417
  if (s.dataLabels !== void 0) fields.push(["dataLabels", str(s.dataLabels)]);
1360
1418
  if (s.xScale !== void 0) fields.push(["xScale", str(s.xScale)]);
1419
+ if (s.annotations !== void 0)
1420
+ fields.push(["annotations", jArray(s.annotations.map(chartAnnotation))]);
1361
1421
  if (s.onPointClick !== void 0) fields.push(["onPointClick", CLOSURE]);
1362
1422
  return jObject(fields);
1363
1423
  };
@@ -1640,10 +1700,14 @@ var nodeKind = (k) => {
1640
1700
  [
1641
1701
  "cases",
1642
1702
  jArray(
1703
+ // Phase 1535 — exactly one of `match` and `when` is present, so
1704
+ // exactly one is emitted. `jObject` sorts, so the pair's relative
1705
+ // order here is immaterial.
1643
1706
  k.spec.cases.map(
1644
1707
  (c) => jObject([
1645
1708
  ["child", node(c.child)],
1646
- ["match", str(c.match)]
1709
+ ...c.match !== void 0 ? [["match", str(c.match)]] : [],
1710
+ ...c.when !== void 0 ? [["when", binding(c.when, bool)]] : []
1647
1711
  ])
1648
1712
  )
1649
1713
  )
@@ -1738,6 +1802,7 @@ var node = (n) => {
1738
1802
  if (!isDefaultStyle(n.style)) fields.push(["style", semanticStyle(n.style)]);
1739
1803
  if (n.accessibility !== void 0) fields.push(["accessibility", accessibility(n.accessibility)]);
1740
1804
  if (n.tooltip !== void 0) fields.push(["tooltip", textSource(n.tooltip)]);
1805
+ if (n.visible !== void 0) fields.push(["visible", binding(n.visible, bool)]);
1741
1806
  return jObject(fields);
1742
1807
  };
1743
1808
  var treeOp = (op) => {
@@ -2893,6 +2958,51 @@ var substituteListParams = (listEnv, pipeline) => pipeline.map((t) => {
2893
2958
  return t;
2894
2959
  }
2895
2960
  });
2961
+
2962
+ // src/localCodec.ts
2963
+ var identityFormat = (v) => {
2964
+ if (v === null || v === void 0) return "";
2965
+ if (typeof v === "string") return v;
2966
+ if (typeof v === "boolean") return v ? "true" : "false";
2967
+ if (typeof v === "number") {
2968
+ if (Number.isNaN(v)) return "NaN";
2969
+ if (v === Infinity) return "Infinity";
2970
+ if (v === -Infinity) return "-Infinity";
2971
+ return formatFiniteDouble(v);
2972
+ }
2973
+ return String(v);
2974
+ };
2975
+ var tryNumberText = (text) => {
2976
+ const s = text.replace(/^[ \t\n\r]+|[ \t\n\r]+$/g, "");
2977
+ if (!/^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?$/.test(s)) return void 0;
2978
+ const v = Number(s);
2979
+ return Number.isFinite(v) ? v : void 0;
2980
+ };
2981
+ var scalarOfText = (text) => {
2982
+ if (text === "true") return true;
2983
+ if (text === "false") return false;
2984
+ return tryNumberText(text);
2985
+ };
2986
+ var fixedText = (decimals, v) => {
2987
+ if (!Number.isFinite(v)) return identityFormat(v);
2988
+ const d = decimals < 0 ? 0 : Math.trunc(decimals);
2989
+ const neg = v < 0;
2990
+ const scaled = Math.floor(Math.abs(v) * Math.pow(10, d) + 0.5);
2991
+ const whole = formatFiniteDouble(scaled);
2992
+ const body = d === 0 ? whole : (() => {
2993
+ const padded = whole.length <= d ? "0".repeat(d + 1 - whole.length) + whole : whole;
2994
+ return padded.slice(0, padded.length - d) + "." + padded.slice(padded.length - d);
2995
+ })();
2996
+ return neg && scaled !== 0 ? "-" + body : body;
2997
+ };
2998
+ var numberText = (decimals, v) => decimals !== void 0 && typeof v === "number" ? fixedText(decimals, v) : identityFormat(v);
2999
+ var WireSurvivabilityError = class extends Error {
3000
+ constructor(message) {
3001
+ super(message);
3002
+ this.name = "WireSurvivabilityError";
3003
+ }
3004
+ };
3005
+ var DECODED_COMPUTED_MESSAGE = "Binding.Computed has no wire projection (decoded from a '<closure>' sentinel) \u2014 use Binding.Expr / Transform / State";
2896
3006
  var peek = (s) => s.pos < s.text.length ? s.text[s.pos] : " ";
2897
3007
  var advance = (s) => {
2898
3008
  s.pos += 1;
@@ -2923,51 +3033,94 @@ var expectChar = (s, ch) => {
2923
3033
  return fail(s, `expected '${ch}' but found '${peek(s)}'`);
2924
3034
  };
2925
3035
  var HEX_DIGIT = /[0-9a-fA-F]/;
3036
+ var HIGH_SURROGATE_FIRST = 55296;
3037
+ var HIGH_SURROGATE_LAST = 56319;
3038
+ var LOW_SURROGATE_FIRST = 56320;
3039
+ var LOW_SURROGATE_LAST = 57343;
2926
3040
  var parseStringRaw = (s) => {
2927
3041
  const open = expectChar(s, '"');
2928
3042
  if (!open.ok) return open;
2929
3043
  let out = "";
3044
+ let codePoints = 0;
3045
+ let pendingHigh = false;
3046
+ const append = (unit) => {
3047
+ const code = unit.charCodeAt(0);
3048
+ const isHigh = code >= HIGH_SURROGATE_FIRST && code <= HIGH_SURROGATE_LAST;
3049
+ const isLow = code >= LOW_SURROGATE_FIRST && code <= LOW_SURROGATE_LAST;
3050
+ if (isHigh) {
3051
+ if (pendingHigh) {
3052
+ return {
3053
+ message: "unpaired high surrogate: it must be followed by a low surrogate",
3054
+ offset: s.pos
3055
+ };
3056
+ }
3057
+ pendingHigh = true;
3058
+ codePoints += 1;
3059
+ } else if (isLow) {
3060
+ if (!pendingHigh) {
3061
+ return {
3062
+ message: "unpaired low surrogate: it must be preceded by a high surrogate",
3063
+ offset: s.pos
3064
+ };
3065
+ }
3066
+ pendingHigh = false;
3067
+ } else if (pendingHigh) {
3068
+ return {
3069
+ message: "unpaired high surrogate: it must be followed by a low surrogate",
3070
+ offset: s.pos
3071
+ };
3072
+ } else {
3073
+ codePoints += 1;
3074
+ }
3075
+ if (codePoints > MAX_STRING_LENGTH) {
3076
+ return {
3077
+ message: `string is longer than the wire limit MAX_STRING_LENGTH = ${MAX_STRING_LENGTH}`,
3078
+ offset: s.pos,
3079
+ limit: true
3080
+ };
3081
+ }
3082
+ out += unit;
3083
+ return void 0;
3084
+ };
2930
3085
  for (; ; ) {
2931
3086
  if (s.pos >= s.text.length) return fail(s, "unterminated string");
2932
3087
  const c = s.text[s.pos];
2933
3088
  advance(s);
2934
3089
  if (c === '"') {
3090
+ if (pendingHigh) {
3091
+ return fail(s, "unpaired high surrogate at the end of a string");
3092
+ }
2935
3093
  return { ok: true, value: out };
2936
3094
  }
2937
- if (out.length > MAX_STRING_LENGTH) {
2938
- return failLimit(
2939
- s,
2940
- `string is longer than the wire limit MAX_STRING_LENGTH = ${MAX_STRING_LENGTH}`
2941
- );
2942
- }
2943
3095
  if (c === "\\") {
2944
3096
  if (s.pos >= s.text.length) return fail(s, "unterminated escape");
2945
3097
  const esc = s.text[s.pos];
2946
3098
  advance(s);
3099
+ let unit;
2947
3100
  switch (esc) {
2948
3101
  case '"':
2949
- out += '"';
3102
+ unit = '"';
2950
3103
  break;
2951
3104
  case "\\":
2952
- out += "\\";
3105
+ unit = "\\";
2953
3106
  break;
2954
3107
  case "/":
2955
- out += "/";
3108
+ unit = "/";
2956
3109
  break;
2957
3110
  case "b":
2958
- out += "\b";
3111
+ unit = "\b";
2959
3112
  break;
2960
3113
  case "f":
2961
- out += "\f";
3114
+ unit = "\f";
2962
3115
  break;
2963
3116
  case "n":
2964
- out += "\n";
3117
+ unit = "\n";
2965
3118
  break;
2966
3119
  case "r":
2967
- out += "\r";
3120
+ unit = "\r";
2968
3121
  break;
2969
3122
  case "t":
2970
- out += " ";
3123
+ unit = " ";
2971
3124
  break;
2972
3125
  case "u": {
2973
3126
  if (s.pos + 4 > s.text.length) return fail(s, "incomplete \\u escape");
@@ -2976,26 +3129,58 @@ var parseStringRaw = (s) => {
2976
3129
  return fail(s, `invalid \\u escape '${hex}'`);
2977
3130
  }
2978
3131
  s.pos += 4;
2979
- out += String.fromCharCode(parseInt(hex, 16));
3132
+ unit = String.fromCharCode(parseInt(hex, 16));
2980
3133
  break;
2981
3134
  }
2982
3135
  default:
2983
3136
  return fail(s, `unknown escape '\\${esc}'`);
2984
3137
  }
3138
+ const bad = append(unit);
3139
+ if (bad) return { ok: false, error: bad };
3140
+ } else if (c.charCodeAt(0) < 32) {
3141
+ return fail(
3142
+ s,
3143
+ `raw control character U+${c.charCodeAt(0).toString(16).padStart(4, "0").toUpperCase()} in a string must be escaped`
3144
+ );
2985
3145
  } else {
2986
- out += c;
3146
+ const bad = append(c);
3147
+ if (bad) return { ok: false, error: bad };
2987
3148
  }
2988
3149
  }
2989
3150
  };
2990
3151
  var isNumberChar = (c) => c === "-" || c === "+" || c === "." || c === "e" || c === "E" || c >= "0" && c <= "9";
3152
+ var isRfc8259Number = (slice) => {
3153
+ const n = slice.length;
3154
+ let i = 0;
3155
+ const digit = (k) => k < n && slice[k] >= "0" && slice[k] <= "9";
3156
+ if (i < n && slice[i] === "-") i += 1;
3157
+ if (!digit(i)) return false;
3158
+ if (slice[i] === "0") {
3159
+ i += 1;
3160
+ } else {
3161
+ while (digit(i)) i += 1;
3162
+ }
3163
+ if (i < n && slice[i] === ".") {
3164
+ i += 1;
3165
+ if (!digit(i)) return false;
3166
+ while (digit(i)) i += 1;
3167
+ }
3168
+ if (i < n && (slice[i] === "e" || slice[i] === "E")) {
3169
+ i += 1;
3170
+ if (i < n && (slice[i] === "+" || slice[i] === "-")) i += 1;
3171
+ if (!digit(i)) return false;
3172
+ while (digit(i)) i += 1;
3173
+ }
3174
+ return i === n;
3175
+ };
2991
3176
  var parseNumberRaw = (s) => {
2992
3177
  const start = s.pos;
2993
3178
  while (s.pos < s.text.length && isNumberChar(s.text[s.pos])) {
2994
3179
  advance(s);
2995
3180
  }
2996
3181
  const slice = s.text.substring(start, s.pos);
2997
- if (slice.length === 0) {
2998
- return fail(s, `invalid number '${slice}'`);
3182
+ if (!isRfc8259Number(slice)) {
3183
+ return fail(s, `'${slice}' is not a JSON number (RFC 8259 grammar)`);
2999
3184
  }
3000
3185
  const n = Number(slice);
3001
3186
  if (Number.isNaN(n)) {
@@ -3082,6 +3267,10 @@ var parseObjectValue = (s) => {
3082
3267
  `object has more members than the wire limit MAX_ARRAY_LENGTH = ${MAX_ARRAY_LENGTH}`
3083
3268
  );
3084
3269
  }
3270
+ if (fields.has(keyR.value)) {
3271
+ s.depth -= 1;
3272
+ return fail(s, `duplicate object member '${keyR.value}'`);
3273
+ }
3085
3274
  fields.set(keyR.value, valR.value);
3086
3275
  skipWs(s);
3087
3276
  const c = peek(s);
@@ -3141,13 +3330,52 @@ var parseArrayValue = (s) => {
3141
3330
  }
3142
3331
  }
3143
3332
  };
3333
+ var documentBytes = (input) => {
3334
+ if (input.length > MAX_DOCUMENT_BYTES) return input.length;
3335
+ if (input.length <= MAX_DOCUMENT_BYTES / 3) return input.length;
3336
+ let total = 0;
3337
+ for (let i = 0; i < input.length; i += 1) {
3338
+ const c = input.charCodeAt(i);
3339
+ if (c < 128) {
3340
+ total += 1;
3341
+ } else if (c < 2048) {
3342
+ total += 2;
3343
+ } else if (c >= HIGH_SURROGATE_FIRST && c <= HIGH_SURROGATE_LAST && i + 1 < input.length) {
3344
+ total += 4;
3345
+ i += 1;
3346
+ } else {
3347
+ total += 3;
3348
+ }
3349
+ }
3350
+ return total;
3351
+ };
3144
3352
  var parse = (input) => {
3353
+ const bytes = documentBytes(input);
3354
+ if (bytes > MAX_DOCUMENT_BYTES) {
3355
+ return {
3356
+ ok: false,
3357
+ error: {
3358
+ message: `document of ${bytes} UTF-8 bytes exceeds the wire limit MAX_DOCUMENT_BYTES = ${MAX_DOCUMENT_BYTES}`,
3359
+ offset: 0,
3360
+ limit: true
3361
+ }
3362
+ };
3363
+ }
3145
3364
  const s = { text: input, pos: 0, depth: 0 };
3146
3365
  skipWs(s);
3147
3366
  if (s.pos >= s.text.length) {
3148
3367
  return { ok: false, error: { message: "input is empty", offset: 0 } };
3149
3368
  }
3150
- return parseValue(s);
3369
+ const value = parseValue(s);
3370
+ if (!value.ok) return value;
3371
+ skipWs(s);
3372
+ if (s.pos < s.text.length) {
3373
+ return fail(
3374
+ s,
3375
+ `unexpected content after the root value ('${s.text[s.pos]}'); a wire artefact is a single JSON document`
3376
+ );
3377
+ }
3378
+ return value;
3151
3379
  };
3152
3380
  var field = (fields, key) => fields.get(key);
3153
3381
 
@@ -3192,9 +3420,31 @@ var requireFloat = (path, jRaw) => {
3192
3420
  }
3193
3421
  return wrongType(path, "JSON number (or 'NaN' / 'Infinity' / '-Infinity' sentinel string)");
3194
3422
  };
3423
+ var INT32_MIN = -2147483648;
3424
+ var INT32_MAX = 2147483647;
3425
+ var isInt32Slot = (n) => Number.isFinite(n) && Number.isInteger(n) && n >= INT32_MIN && n <= INT32_MAX;
3195
3426
  var requireInt = (path, jRaw) => {
3196
3427
  const j = unwrapStaticEnvelope(jRaw);
3197
- return j.kind === "JNumber" ? ok(Math.trunc(j.value)) : wrongType(path, "JSON number (integer)");
3428
+ if (j.kind !== "JNumber") return wrongType(path, "JSON number (integer)");
3429
+ if (!Number.isFinite(j.value)) {
3430
+ return wrongType(
3431
+ path,
3432
+ "JSON number (a finite 32-bit integer; the non-finite sentinels are a float slot's, not an integer slot's)"
3433
+ );
3434
+ }
3435
+ if (!Number.isInteger(j.value)) {
3436
+ return wrongType(
3437
+ path,
3438
+ "JSON number (a 32-bit integer; a fractional value is not truncated at an integer slot)"
3439
+ );
3440
+ }
3441
+ if (!isInt32Slot(j.value)) {
3442
+ return wrongType(
3443
+ path,
3444
+ "JSON number (a 32-bit integer; the value is outside the range this slot can hold)"
3445
+ );
3446
+ }
3447
+ return ok(j.value);
3198
3448
  };
3199
3449
  var requireArray = (path, j) => j.kind === "JArray" ? ok(j.items) : wrongType(path, "JSON array");
3200
3450
  var tryField = (fields, key) => fields.get(key);
@@ -3353,6 +3603,7 @@ var decodeHeadingVariant = (p, j) => {
3353
3603
  var decodeImageVariant = (p, j) => bareEnum(p, j, ["Default", "Avatar", "Rounded"], "ImageVariant");
3354
3604
  var decodeImageFit = (p, j) => bareEnum(p, j, ["Natural", "Cover", "Contain"], "ImageFit");
3355
3605
  var decodeModalityKind = (p, j) => bareEnum(p, j, ["Modal", "Popover"], "ModalityKind");
3606
+ var decodeNavigateTarget = (p, j) => bareEnum(p, j, ["Self", "Blank"], "NavigateTarget");
3356
3607
  var decodeImageAspect = (p, j) => bareEnum(
3357
3608
  p,
3358
3609
  j,
@@ -3430,6 +3681,149 @@ var decodeChartKind = (p, j) => bareEnum(p, j, ["Line", "Bar", "Area", "Pie", "S
3430
3681
  var decodeChartLegendPosition = (p, j) => bareEnum(p, j, ["Top", "Right", "Bottom", "None"], "ChartLegendPosition");
3431
3682
  var decodeChartDataLabels = (p, j) => bareEnum(p, j, ["Off", "Ends"], "ChartDataLabels");
3432
3683
  var decodeChartXScale = (p, j) => bareEnum(p, j, ["Category", "Temporal"], "ChartXScale");
3684
+ var annotationIsLeapYear = (y) => y % 4 === 0 && y % 100 !== 0 || y % 400 === 0;
3685
+ var annotationDaysInMonth = (y, m) => m === 2 ? annotationIsLeapYear(y) ? 29 : 28 : m === 4 || m === 6 || m === 9 || m === 11 ? 30 : 31;
3686
+ var isCanonicalIsoDay = (text) => {
3687
+ if (text.length < 10) return false;
3688
+ if (text[4] !== "-" || text[7] !== "-") return false;
3689
+ if (text.length > 10 && text[10] !== "T") return false;
3690
+ const digits = (start, len) => {
3691
+ let acc = 0;
3692
+ for (let k = start; k < start + len; k += 1) {
3693
+ const c = text.charCodeAt(k);
3694
+ if (c < 48 || c > 57) return void 0;
3695
+ acc = acc * 10 + (c - 48);
3696
+ }
3697
+ return acc;
3698
+ };
3699
+ const y = digits(0, 4);
3700
+ const m = digits(5, 2);
3701
+ const d = digits(8, 2);
3702
+ if (y === void 0 || m === void 0 || d === void 0) return false;
3703
+ return m >= 1 && m <= 12 && d >= 1 && d <= annotationDaysInMonth(y, m);
3704
+ };
3705
+ var decodeChartAnnotationX = (path, j) => {
3706
+ const fo = requireObject(path, j);
3707
+ if (!fo.ok) return fo;
3708
+ const f = fo.value;
3709
+ const disc = requireDiscriminator(path, f);
3710
+ if (!disc.ok) return disc;
3711
+ switch (disc.value) {
3712
+ case "Category": {
3713
+ const key = reqField(path, f, "key", "category key (the band's own label)", requireString);
3714
+ if (!key.ok) return key;
3715
+ return ok({ kind: "Category", key: key.value });
3716
+ }
3717
+ case "Date": {
3718
+ const iso = reqField(path, f, "iso", "ISO-8601 date (YYYY-MM-DD)", requireString);
3719
+ if (!iso.ok) return iso;
3720
+ if (!isCanonicalIsoDay(iso.value))
3721
+ return wrongType(
3722
+ `${path}.iso`,
3723
+ "a canonical ISO-8601 date (YYYY-MM-DD, optionally followed by a time) naming a real calendar day \u2014 an event marker\u2019s date is the address it is drawn at, and an unreadable one would place the marker at 1970-01-01 and drag the axis back with it"
3724
+ );
3725
+ return ok({ kind: "Date", iso: iso.value });
3726
+ }
3727
+ default:
3728
+ return unknownDuCase(path, disc.value, "Category, Date");
3729
+ }
3730
+ };
3731
+ var decodeChartAnnotationRange = (path, j) => {
3732
+ const finite = (slot, v) => {
3733
+ const r = requireFloat(`${path}.${slot}`, v);
3734
+ if (!r.ok) return r;
3735
+ return Number.isFinite(r.value) ? r : wrongType(
3736
+ `${path}.${slot}`,
3737
+ "a FINITE JSON number \u2014 a range band's end names a place on the value axis, and NaN / Infinity names none; give the value in the axis's own units, or drop the annotation"
3738
+ );
3739
+ };
3740
+ const fo = requireObject(path, j);
3741
+ if (!fo.ok) return fo;
3742
+ const f = fo.value;
3743
+ const disc = requireDiscriminator(path, f);
3744
+ if (!disc.ok) return disc;
3745
+ switch (disc.value) {
3746
+ case "ValueRange": {
3747
+ const fromJ = requireField(path, f, "from", "range-band lower value (a finite JSON number)");
3748
+ if (!fromJ.ok) return fromJ;
3749
+ const from = finite("from", fromJ.value);
3750
+ if (!from.ok) return from;
3751
+ const toJ = requireField(path, f, "to", "range-band upper value (a finite JSON number)");
3752
+ if (!toJ.ok) return toJ;
3753
+ const to = finite("to", toJ.value);
3754
+ if (!to.ok) return to;
3755
+ if (from.value > to.value)
3756
+ return wrongType(
3757
+ path,
3758
+ "an ORDERED pair \u2014 a range band runs from its lower value to its upper one, and this pair runs backwards; swapping the ends silently would draw a band the author did not describe"
3759
+ );
3760
+ return ok({ kind: "ValueRange", from: from.value, to: to.value });
3761
+ }
3762
+ case "XRange": {
3763
+ const fromJ = requireField(
3764
+ path,
3765
+ f,
3766
+ "from",
3767
+ "range-band lower x address (a ChartAnnotationX)"
3768
+ );
3769
+ if (!fromJ.ok) return fromJ;
3770
+ const from = decodeChartAnnotationX(`${path}.from`, fromJ.value);
3771
+ if (!from.ok) return from;
3772
+ const toJ = requireField(path, f, "to", "range-band upper x address (a ChartAnnotationX)");
3773
+ if (!toJ.ok) return toJ;
3774
+ const to = decodeChartAnnotationX(`${path}.to`, toJ.value);
3775
+ if (!to.ok) return to;
3776
+ if (from.value.kind === "Date" && to.value.kind === "Date" && from.value.iso > to.value.iso)
3777
+ return wrongType(
3778
+ path,
3779
+ "an ORDERED pair \u2014 a range band runs from its earlier date to its later one, and this pair runs backwards; swapping the ends silently would draw a band the author did not describe"
3780
+ );
3781
+ return ok({ kind: "XRange", from: from.value, to: to.value });
3782
+ }
3783
+ default:
3784
+ return unknownDuCase(path, disc.value, "ValueRange, XRange");
3785
+ }
3786
+ };
3787
+ var decodeChartAnnotation = (path, j) => {
3788
+ const fo = requireObject(path, j);
3789
+ if (!fo.ok) return fo;
3790
+ const f = fo.value;
3791
+ const disc = requireDiscriminator(path, f);
3792
+ if (!disc.ok) return disc;
3793
+ const label = optField(path, f, "label", decodeTextSource);
3794
+ if (!label.ok) return label;
3795
+ const withLabel = label.value !== void 0 ? { label: label.value } : {};
3796
+ switch (disc.value) {
3797
+ case "ReferenceLine": {
3798
+ const valueJ = requireField(path, f, "value", "reference-line value (a finite JSON number)");
3799
+ if (!valueJ.ok) return valueJ;
3800
+ const value = requireFloat(`${path}.value`, valueJ.value);
3801
+ if (!value.ok) return value;
3802
+ if (!Number.isFinite(value.value))
3803
+ return wrongType(
3804
+ `${path}.value`,
3805
+ "a FINITE JSON number \u2014 a reference line names a place on the value axis, and NaN / Infinity names none; give the value in the axis's own units, or drop the annotation"
3806
+ );
3807
+ return ok({ kind: "ReferenceLine", value: value.value, ...withLabel });
3808
+ }
3809
+ case "EventMarker": {
3810
+ const atJ = requireField(path, f, "at", "event-marker x address (a ChartAnnotationX)");
3811
+ if (!atJ.ok) return atJ;
3812
+ const at = decodeChartAnnotationX(`${path}.at`, atJ.value);
3813
+ if (!at.ok) return at;
3814
+ return ok({ kind: "EventMarker", at: at.value, ...withLabel });
3815
+ }
3816
+ case "RangeBand": {
3817
+ const rangeJ = requireField(path, f, "range", "range-band pair (a ChartAnnotationRange)");
3818
+ if (!rangeJ.ok) return rangeJ;
3819
+ const range = decodeChartAnnotationRange(`${path}.range`, rangeJ.value);
3820
+ if (!range.ok) return range;
3821
+ return ok({ kind: "RangeBand", range: range.value, ...withLabel });
3822
+ }
3823
+ default:
3824
+ return unknownDuCase(path, disc.value, "ReferenceLine, EventMarker, RangeBand");
3825
+ }
3826
+ };
3433
3827
  var decodeFileReadEncoding = (p, j) => bareEnum(p, j, ["Text", "Base64", "DataUrl"], "FileReadEncoding");
3434
3828
  var decodeAriaRole = (p, j) => {
3435
3829
  if (j.kind !== "JString") return wrongType(p, "JSON string (ARIA role)");
@@ -3444,6 +3838,7 @@ var decodeRelativeTimeUnit = (p, j) => bareEnum(
3444
3838
  ["Second", "Minute", "Hour", "Day", "Week", "Month", "Year"],
3445
3839
  "RelativeTimeUnit"
3446
3840
  );
3841
+ var decodeTimeGrain = (p, j) => bareEnum(p, j, ["Second", "Minute", "Hour", "Day"], "TimeGrain");
3447
3842
  var decodeCellFormat = (path, j) => {
3448
3843
  const fo = requireObject(path, j);
3449
3844
  if (!fo.ok) return fo;
@@ -3543,11 +3938,17 @@ var decodeFormat = (path, j) => {
3543
3938
  if (!style.ok) return style;
3544
3939
  return ok({ kind: "Duration", unit: unit.value, style: style.value });
3545
3940
  }
3941
+ case "Since": {
3942
+ const u = tryField(f, "unit");
3943
+ if (u === void 0) return ok({ kind: "Since" });
3944
+ const r = decodeRelativeTimeUnit(`${path}.unit`, u);
3945
+ return r.ok ? ok({ kind: "Since", unit: r.value }) : r;
3946
+ }
3546
3947
  default:
3547
3948
  return unknownDuCase(
3548
3949
  path,
3549
3950
  d.value,
3550
- "Number | Currency | Percent | Date | RelativeTime | Duration"
3951
+ "Number | Currency | Percent | Date | RelativeTime | Duration | Since"
3551
3952
  );
3552
3953
  }
3553
3954
  };
@@ -3701,7 +4102,13 @@ var cFieldAliased = (f, canonical, alias) => {
3701
4102
  };
3702
4103
  var cStr = (j) => j.kind === "JString" ? cok(j.value) : cerr("malformed: expected string, got " + astKind(j));
3703
4104
  var cArr = (j) => j.kind === "JArray" ? cok(j.items) : cerr("malformed: expected array, got " + astKind(j));
3704
- var cInt = (j) => j.kind === "JNumber" ? cok(Math.trunc(j.value)) : cerr("malformed: expected int, got " + astKind(j));
4105
+ var cInt = (j) => {
4106
+ if (j.kind !== "JNumber") return cerr("malformed: expected int, got " + astKind(j));
4107
+ if (!isInt32Slot(j.value)) {
4108
+ return cerr(`malformed: expected a 32-bit integer, got ${j.value}`);
4109
+ }
4110
+ return cok(j.value);
4111
+ };
3705
4112
  var cMapM = (xs, fn) => {
3706
4113
  const out = [];
3707
4114
  for (const x of xs) {
@@ -3732,7 +4139,7 @@ var decodeCellLit = (j) => {
3732
4139
  if (v === void 0) return mismatch;
3733
4140
  switch (t) {
3734
4141
  case "Int":
3735
- return v.kind === "JNumber" ? cok({ kind: "Int", value: Math.trunc(v.value) }) : mismatch;
4142
+ return v.kind === "JNumber" && isInt32Slot(v.value) ? cok({ kind: "Int", value: v.value }) : mismatch;
3736
4143
  case "Float":
3737
4144
  return v.kind === "JNumber" ? cok({ kind: "Float", value: v.value }) : mismatch;
3738
4145
  case "Bool":
@@ -4435,6 +4842,137 @@ var decodeInvokeArgs = (path, j) => {
4435
4842
  return ok({ addr: addr.value, value: value.value });
4436
4843
  });
4437
4844
  };
4845
+ var exprAdmissible = (root) => {
4846
+ let count = 0;
4847
+ let sawCol = false;
4848
+ const walk = (e) => {
4849
+ count += 1;
4850
+ if (sawCol || count > MAX_EXPR_NODES) return;
4851
+ switch (e.kind) {
4852
+ case "col":
4853
+ sawCol = true;
4854
+ return;
4855
+ case "lit":
4856
+ case "param":
4857
+ return;
4858
+ case "binary":
4859
+ walk(e.left);
4860
+ walk(e.right);
4861
+ return;
4862
+ case "not":
4863
+ case "cast":
4864
+ case "isNull":
4865
+ walk(e.expr);
4866
+ return;
4867
+ case "coalesce":
4868
+ e.exprs.forEach(walk);
4869
+ return;
4870
+ case "apply":
4871
+ e.args.forEach(walk);
4872
+ return;
4873
+ case "case":
4874
+ for (const c of e.cases) {
4875
+ walk(c.when);
4876
+ walk(c.then);
4877
+ }
4878
+ walk(e.else);
4879
+ return;
4880
+ case "in":
4881
+ walk(e.expr);
4882
+ e.items.forEach(walk);
4883
+ return;
4884
+ case "inParam":
4885
+ walk(e.expr);
4886
+ return;
4887
+ }
4888
+ };
4889
+ walk(root);
4890
+ if (sawCol) return "col";
4891
+ if (count > MAX_EXPR_NODES) return "limit";
4892
+ return "ok";
4893
+ };
4894
+ var colExprParamNames = (root) => {
4895
+ const seen = [];
4896
+ const push = (n) => {
4897
+ if (!seen.includes(n)) seen.push(n);
4898
+ };
4899
+ const walk = (e) => {
4900
+ switch (e.kind) {
4901
+ case "col":
4902
+ case "lit":
4903
+ return;
4904
+ case "param":
4905
+ push(e.name);
4906
+ return;
4907
+ case "binary":
4908
+ walk(e.left);
4909
+ walk(e.right);
4910
+ return;
4911
+ case "not":
4912
+ case "cast":
4913
+ case "isNull":
4914
+ walk(e.expr);
4915
+ return;
4916
+ case "coalesce":
4917
+ e.exprs.forEach(walk);
4918
+ return;
4919
+ case "apply":
4920
+ e.args.forEach(walk);
4921
+ return;
4922
+ case "case":
4923
+ for (const c of e.cases) {
4924
+ walk(c.when);
4925
+ walk(c.then);
4926
+ }
4927
+ walk(e.else);
4928
+ return;
4929
+ case "in":
4930
+ walk(e.expr);
4931
+ e.items.forEach(walk);
4932
+ return;
4933
+ case "inParam":
4934
+ walk(e.expr);
4935
+ push(e.param);
4936
+ return;
4937
+ }
4938
+ };
4939
+ walk(root);
4940
+ return seen;
4941
+ };
4942
+ var decodeExprParams = (path, f) => {
4943
+ const paramsField = tryField(f, "params");
4944
+ if (paramsField === void 0) return ok(void 0);
4945
+ if (paramsField.kind === "JObject") {
4946
+ const entries = [...paramsField.fields.entries()].sort(
4947
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
4948
+ );
4949
+ const out = [];
4950
+ for (const [name, v] of entries) {
4951
+ const from = decodeBinding(`${path}.params.${name}.from`, v);
4952
+ if (!from.ok) return from;
4953
+ out.push({ name, from: from.value });
4954
+ }
4955
+ return ok(out);
4956
+ }
4957
+ const arr = requireArray(`${path}.params`, paramsField);
4958
+ if (!arr.ok) return arr;
4959
+ return traverseIndexed(arr.value, (_i, el) => {
4960
+ const po = requireObject(`${path}.params[]`, el);
4961
+ if (!po.ok) return po;
4962
+ const name = reqField(`${path}.params[]`, po.value, "name", "param name string", requireString);
4963
+ if (!name.ok) return name;
4964
+ const from = reqFieldAliased(
4965
+ `${path}.params[]`,
4966
+ po.value,
4967
+ "from",
4968
+ ["value"],
4969
+ "param source Binding",
4970
+ decodeBinding
4971
+ );
4972
+ if (!from.ok) return from;
4973
+ return ok({ name: name.value, from: from.value });
4974
+ });
4975
+ };
4438
4976
  var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), placeholder = OPAQUE2) => {
4439
4977
  if (j.kind === "JArray" || j.kind === "JString" || j.kind === "JNumber" || j.kind === "JBool") {
4440
4978
  const parsed = parseStatic(path, j);
@@ -4533,14 +5071,23 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4533
5071
  return ok({ kind: "State", key: key.value, defaultValue });
4534
5072
  }
4535
5073
  case "Computed":
4536
- return ok({ kind: "Computed", compute: () => void 0 });
5074
+ return ok({
5075
+ kind: "Computed",
5076
+ compute: () => {
5077
+ throw new WireSurvivabilityError(DECODED_COMPUTED_MESSAGE);
5078
+ }
5079
+ });
4537
5080
  // The projection decodes to the IDENTITY (the Phase 427 Selection fix
4538
5081
  // replayed): the host-furnished instant is already the wire-shaped string,
4539
5082
  // so a decoded reader receives it as-is. A value-discarding placeholder
4540
5083
  // here would make every decoded `Now` resolve to nothing even when the
4541
5084
  // host furnishes the instant.
4542
- case "Now":
4543
- return ok({ kind: "Now", project: (iso) => iso });
5085
+ case "Now": {
5086
+ const g = tryField(f, "grain");
5087
+ if (g === void 0) return ok({ kind: "Now", project: (iso) => iso });
5088
+ const r = decodeTimeGrain(`${path}.grain`, g);
5089
+ return r.ok ? ok({ kind: "Now", project: (iso) => iso, grain: r.value }) : r;
5090
+ }
4544
5091
  case "I18n": {
4545
5092
  const key = reqField(path, f, "key", "i18n key string", requireString);
4546
5093
  if (!key.ok) return key;
@@ -4555,6 +5102,37 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4555
5102
  return ok(b);
4556
5103
  }
4557
5104
  case "Local": {
5105
+ const codecJ = tryField(f, "codec");
5106
+ let codec;
5107
+ if (codecJ !== void 0) {
5108
+ const c = decodeFormat(`${path}.codec`, codecJ);
5109
+ if (!c.ok) return c;
5110
+ if (c.value.kind !== "Number") {
5111
+ return makeError(
5112
+ "WRONG_TYPE",
5113
+ `${path}.codec`,
5114
+ "Binding.Local 'codec' must be a Format case with a total, locale-independent inverse \u2014 only 'Number' has one",
5115
+ 'use {"$type":"Number","decimals":2}, or drop the codec and let the buffer use the identity; a locale-rendered format (Currency / Date / RelativeTime / Since / Duration) cannot be parsed back from what the reader typed'
5116
+ );
5117
+ }
5118
+ codec = c.value;
5119
+ }
5120
+ const onCommitPresent = tryField(f, "onCommit") !== void 0;
5121
+ const commitToJ = tryField(f, "commitTo");
5122
+ let commitTo;
5123
+ if (commitToJ !== void 0) {
5124
+ if (onCommitPresent) {
5125
+ return makeError(
5126
+ "WRONG_TYPE",
5127
+ `${path}.commitTo`,
5128
+ "Binding.Local carries both 'onCommit' and 'commitTo' \u2014 exactly one commit destination is allowed",
5129
+ "either onCommit (a host closure, which crosses the wire only as the closure sentinel) or commitTo (the State key the flush writes); a decoding host can honour only the second, so keeping both makes the same document commit to two different places depending on who read it"
5130
+ );
5131
+ }
5132
+ const ct = requireString(`${path}.commitTo`, commitToJ);
5133
+ if (!ct.ok) return ct;
5134
+ commitTo = ct.value;
5135
+ }
4558
5136
  const initial = reqField(
4559
5137
  path,
4560
5138
  f,
@@ -4566,15 +5144,35 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4566
5144
  const flushJ = tryField(f, "flushOn");
4567
5145
  const flush = flushJ === void 0 ? ok({ kind: "OnBlur" }) : decodeLocalFlushTrigger(`${path}.flushOn`, flushJ);
4568
5146
  if (!flush.ok) return flush;
4569
- const b = {
4570
- kind: "Local",
4571
- local: {
4572
- initialFrom: initial.value,
4573
- flushOn: flush.value,
4574
- onCommit: () => void 0,
4575
- parse: () => ({ ok: false, error: CLOSURE2 })
4576
- }
5147
+ const refusalOf = (raw) => `Binding.Local: '${raw}' is not a value this field accepts`;
5148
+ const identityParse = (raw) => {
5149
+ const asText = parseStatic(`${path}.parse`, { kind: "JString", value: raw });
5150
+ if (asText.ok) return { ok: true, value: asText.value };
5151
+ const scalar = scalarOfText(raw);
5152
+ if (scalar === void 0) return { ok: false, error: refusalOf(raw) };
5153
+ const asScalar = parseStatic(
5154
+ `${path}.parse`,
5155
+ typeof scalar === "number" ? { kind: "JNumber", value: scalar } : { kind: "JBool", value: scalar }
5156
+ );
5157
+ return asScalar.ok ? { ok: true, value: asScalar.value } : { ok: false, error: refusalOf(raw) };
4577
5158
  };
5159
+ const decimals = codec !== void 0 && codec.kind === "Number" ? codec.decimals : void 0;
5160
+ const codecParse = (raw) => {
5161
+ const n = tryNumberText(raw);
5162
+ if (n === void 0) return { ok: false, error: refusalOf(raw) };
5163
+ const parsed = parseStatic(`${path}.parse`, { kind: "JNumber", value: n });
5164
+ return parsed.ok ? { ok: true, value: parsed.value } : { ok: false, error: refusalOf(raw) };
5165
+ };
5166
+ const local = {
5167
+ initialFrom: initial.value,
5168
+ flushOn: flush.value,
5169
+ format: codec !== void 0 ? (v) => numberText(decimals, v) : (v) => identityFormat(v),
5170
+ parse: codec !== void 0 ? codecParse : identityParse,
5171
+ ...onCommitPresent ? { onCommit: () => void 0 } : {},
5172
+ ...codec !== void 0 ? { codec } : {},
5173
+ ...commitTo !== void 0 ? { commitTo } : {}
5174
+ };
5175
+ const b = { kind: "Local", local };
4578
5176
  return ok(b);
4579
5177
  }
4580
5178
  case "Format": {
@@ -4646,52 +5244,9 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4646
5244
  }
4647
5245
  const pipe = decodePipelineCore(pipeJ.value);
4648
5246
  if (!pipe.ok) return makeError("WRONG_TYPE", `${path}.pipeline`, pipe.error);
4649
- const paramsField = tryField(f, "params");
4650
- let params;
4651
- if (paramsField !== void 0 && paramsField.kind === "JObject") {
4652
- const entries = [...paramsField.fields.entries()].sort(
4653
- ([a], [b2]) => a < b2 ? -1 : a > b2 ? 1 : 0
4654
- );
4655
- const out = [];
4656
- let coerceErr;
4657
- for (const [name, v] of entries) {
4658
- const from = decodeBinding(`${path}.params.${name}.from`, v);
4659
- if (!from.ok) {
4660
- coerceErr = from;
4661
- break;
4662
- }
4663
- out.push({ name, from: from.value });
4664
- }
4665
- if (coerceErr !== void 0) return coerceErr;
4666
- params = out;
4667
- } else if (paramsField !== void 0) {
4668
- const arr = requireArray(`${path}.params`, paramsField);
4669
- if (!arr.ok) return arr;
4670
- const decoded = traverseIndexed(arr.value, (_i, el) => {
4671
- const po = requireObject(`${path}.params[]`, el);
4672
- if (!po.ok) return po;
4673
- const name = reqField(
4674
- `${path}.params[]`,
4675
- po.value,
4676
- "name",
4677
- "param name string",
4678
- requireString
4679
- );
4680
- if (!name.ok) return name;
4681
- const from = reqFieldAliased(
4682
- `${path}.params[]`,
4683
- po.value,
4684
- "from",
4685
- ["value"],
4686
- "param source Binding",
4687
- decodeBinding
4688
- );
4689
- if (!from.ok) return from;
4690
- return ok({ name: name.value, from: from.value });
4691
- });
4692
- if (!decoded.ok) return decoded;
4693
- params = decoded.value;
4694
- }
5247
+ const paramsR = decodeExprParams(path, f);
5248
+ if (!paramsR.ok) return paramsR;
5249
+ const params = paramsR.value;
4695
5250
  const b = {
4696
5251
  kind: "Transform",
4697
5252
  source,
@@ -4700,6 +5255,47 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4700
5255
  };
4701
5256
  return ok(b);
4702
5257
  }
5258
+ case "Expr": {
5259
+ const exprJ = requireField(path, f, "expr", "ColExpr object");
5260
+ if (!exprJ.ok) return exprJ;
5261
+ const expr = decodeColExprCore(exprJ.value);
5262
+ if (!expr.ok) return makeError("WRONG_TYPE", `${path}.expr`, expr.error);
5263
+ const verdict = exprAdmissible(expr.value);
5264
+ if (verdict === "col") {
5265
+ return makeError(
5266
+ "WRONG_TYPE",
5267
+ `${path}.expr`,
5268
+ "a `col` reference is not admitted inside an Expr binding \u2014 an Expr evaluates against its params alone and has no row for a column name to read. Use `Binding.Transform`, whose source supplies the frame, and put the column expression in a `derive` step",
5269
+ "a ColExpr over `param` / `lit` / operators only (no `col`)"
5270
+ );
5271
+ }
5272
+ if (verdict === "limit") {
5273
+ return makeError(
5274
+ "LIMIT_EXCEEDED",
5275
+ `${path}.expr`,
5276
+ `expression exceeds the maximum of ${MAX_EXPR_NODES} expression nodes (WIRE_FORMAT 21)`,
5277
+ `at most ${MAX_EXPR_NODES} ColExpr nodes in one Expr binding`
5278
+ );
5279
+ }
5280
+ const exprParams = decodeExprParams(path, f);
5281
+ if (!exprParams.ok) return exprParams;
5282
+ const bound = new Set((exprParams.value ?? []).map((p) => p.name));
5283
+ const missing = colExprParamNames(expr.value).filter((n) => !bound.has(n));
5284
+ if (missing.length > 0) {
5285
+ return makeError(
5286
+ "WRONG_TYPE",
5287
+ `${path}.expr`,
5288
+ `the expression reads param(s) ${missing.map((n) => `'${n}'`).join(", ")} that this binding's \`params\` does not bind \u2014 an Expr has no rows and no filter to prune, so an unbound param has no value to take; add a params entry naming each, or drop the reference`,
5289
+ '{"$type":"Expr","expr":{\u2026},"params":[{"name":"<name>","from":<Binding>}]}'
5290
+ );
5291
+ }
5292
+ const b = {
5293
+ kind: "Expr",
5294
+ expr: expr.value,
5295
+ ...exprParams.value !== void 0 ? { params: exprParams.value } : {}
5296
+ };
5297
+ return ok(b);
5298
+ }
4703
5299
  case "Invoke": {
4704
5300
  const cid = reqField(path, f, "capabilityId", "capability id string", requireString);
4705
5301
  if (!cid.ok) return cid;
@@ -5043,10 +5639,14 @@ var decodeAction = (path, j) => {
5043
5639
  f,
5044
5640
  "route",
5045
5641
  ["href", "url", "to"],
5046
- "route string",
5047
- requireString
5642
+ "route TextSource",
5643
+ decodeTextSource
5048
5644
  );
5049
- return r.ok ? ok({ kind: "Navigate", route: r.value }) : r;
5645
+ if (!r.ok) return r;
5646
+ const targetJ = tryField(f, "target");
5647
+ const target = targetJ === void 0 ? ok("Self") : decodeNavigateTarget(`${path}.target`, targetJ);
5648
+ if (!target.ok) return target;
5649
+ return ok({ kind: "Navigate", route: r.value, target: target.value });
5050
5650
  }
5051
5651
  case "SetState": {
5052
5652
  const key = reqField(path, f, "key", "state key string", requireString);
@@ -5116,6 +5716,51 @@ var decodeAction = (path, j) => {
5116
5716
  }
5117
5717
  return ok({ kind: "Print" });
5118
5718
  }
5719
+ case "Confirm": {
5720
+ const nestedConfirmPath = (p, a) => {
5721
+ if (a.kind === "Confirm") return p;
5722
+ if (a.kind === "Chain") {
5723
+ for (let i = 0; i < a.actions.length; i++) {
5724
+ const found = nestedConfirmPath(`${p}.ops[${i}]`, a.actions[i]);
5725
+ if (found !== void 0) return found;
5726
+ }
5727
+ }
5728
+ return void 0;
5729
+ };
5730
+ const prompt = reqField(path, f, "prompt", "confirm prompt TextSource", decodeTextSource);
5731
+ if (!prompt.ok) return prompt;
5732
+ const confirmJ = requireField(path, f, "onConfirm", "Action to dispatch on acceptance");
5733
+ if (!confirmJ.ok) return confirmJ;
5734
+ const onConfirm = decodeAction(`${path}.onConfirm`, confirmJ.value);
5735
+ if (!onConfirm.ok) return onConfirm;
5736
+ const nestedInConfirm = nestedConfirmPath(`${path}.onConfirm`, onConfirm.value);
5737
+ if (nestedInConfirm !== void 0)
5738
+ return wrongType(
5739
+ nestedInConfirm,
5740
+ "any action but Confirm \u2014 confirmation is bounded at one question (WIRE_FORMAT.md \xA73.6.22)"
5741
+ );
5742
+ const cancelJ = f.get("onCancel");
5743
+ if (cancelJ === void 0)
5744
+ return ok({ kind: "Confirm", prompt: prompt.value, onConfirm: onConfirm.value });
5745
+ const onCancel = decodeAction(`${path}.onCancel`, cancelJ);
5746
+ if (!onCancel.ok) return onCancel;
5747
+ const nestedInCancel = nestedConfirmPath(`${path}.onCancel`, onCancel.value);
5748
+ if (nestedInCancel !== void 0)
5749
+ return wrongType(
5750
+ nestedInCancel,
5751
+ "any action but Confirm \u2014 confirmation is bounded at one question (WIRE_FORMAT.md \xA73.6.22)"
5752
+ );
5753
+ return ok({
5754
+ kind: "Confirm",
5755
+ prompt: prompt.value,
5756
+ onConfirm: onConfirm.value,
5757
+ onCancel: onCancel.value
5758
+ });
5759
+ }
5760
+ case "Focus": {
5761
+ const r = reqField(path, f, "nodeId", "NodeId string of the node to focus", requireString);
5762
+ return r.ok ? ok({ kind: "Focus", nodeId: r.value }) : r;
5763
+ }
5119
5764
  case "ReadFileBody": {
5120
5765
  const fileId = reqField(path, f, "fileRef", "FileRef id string", requireString);
5121
5766
  if (!fileId.ok) return fileId;
@@ -7103,6 +7748,18 @@ var decodeChartSpec = (path, j) => {
7103
7748
  if (!dataLabels.ok) return dataLabels;
7104
7749
  const xScale = optField(path, f, "xScale", decodeChartXScale);
7105
7750
  if (!xScale.ok) return xScale;
7751
+ const annotationsJ = tryField(f, "annotations");
7752
+ let annotations;
7753
+ if (annotationsJ !== void 0) {
7754
+ const arr2 = requireArray(`${path}.annotations`, annotationsJ);
7755
+ if (!arr2.ok) return arr2;
7756
+ const decoded = traverseIndexed(
7757
+ arr2.value,
7758
+ (i, item) => decodeChartAnnotation(`${path}.annotations[${i}]`, item)
7759
+ );
7760
+ if (!decoded.ok) return decoded;
7761
+ annotations = decoded.value;
7762
+ }
7106
7763
  const hasPointClick = tryField(f, "onPointClick") !== void 0;
7107
7764
  const stacked = optField(path, f, "stacked", requireBool);
7108
7765
  if (!stacked.ok) return stacked;
@@ -7120,6 +7777,7 @@ var decodeChartSpec = (path, j) => {
7120
7777
  ...legendPosition.value !== void 0 ? { legendPosition: legendPosition.value } : {},
7121
7778
  ...dataLabels.value !== void 0 ? { dataLabels: dataLabels.value } : {},
7122
7779
  ...xScale.value !== void 0 ? { xScale: xScale.value } : {},
7780
+ ...annotations !== void 0 ? { annotations } : {},
7123
7781
  ...hasPointClick ? { onPointClick: () => placeholderAction } : {}
7124
7782
  });
7125
7783
  };
@@ -7818,11 +8476,29 @@ var decodeNodeKind = (path, j) => {
7818
8476
  const cp = `${path}.cases[${i}]`;
7819
8477
  const co = requireObject(cp, item);
7820
8478
  if (!co.ok) return co;
7821
- const m = reqField(cp, co.value, "match", "Switch case match string", requireString);
7822
- if (!m.ok) return m;
8479
+ const mJ = tryField(co.value, "match");
8480
+ const wJ = tryField(co.value, "when");
8481
+ if (mJ !== void 0 && wJ !== void 0)
8482
+ return wrongType(
8483
+ `${cp}.when`,
8484
+ "either 'match' (a literal string compared against the switch's `on` selector) or 'when' (a Binding<bool> predicate evaluated at render time, needing no selector); remove one"
8485
+ );
8486
+ if (mJ === void 0 && wJ === void 0)
8487
+ return missingField(
8488
+ cp,
8489
+ "match",
8490
+ "a literal string under 'match' (compared against the switch's `on` selector), or a Binding<bool> under 'when' (a predicate evaluated at render time)"
8491
+ );
7823
8492
  const c = reqField(cp, co.value, "child", "Switch case child Node", decodeNodeAst);
7824
8493
  if (!c.ok) return c;
7825
- return ok({ match: m.value, child: c.value });
8494
+ if (mJ !== void 0) {
8495
+ const m = requireString(`${cp}.match`, mJ);
8496
+ if (!m.ok) return m;
8497
+ return ok({ match: m.value, child: c.value });
8498
+ }
8499
+ const w = decodeBindingBool(`${cp}.when`, wJ);
8500
+ if (!w.ok) return w;
8501
+ return ok({ when: w.value, child: c.value });
7826
8502
  });
7827
8503
  if (!cases.ok) return cases;
7828
8504
  const def = reqField(path, f, "default", "Switch default Node", decodeNodeAst);
@@ -8168,13 +8844,16 @@ var decodeNodeAstInner = (path, j) => {
8168
8844
  if (!accessibility2.ok) return accessibility2;
8169
8845
  const tooltip = optField(path, f, "tooltip", decodeTextSource);
8170
8846
  if (!tooltip.ok) return tooltip;
8847
+ const visible = optField(path, f, "visible", decodeBindingBool);
8848
+ if (!visible.ok) return visible;
8171
8849
  return ok({
8172
8850
  id: idStr.value,
8173
8851
  kind: kind.value,
8174
8852
  state: state.value,
8175
8853
  style: style.value,
8176
8854
  ...accessibility2.value !== void 0 ? { accessibility: accessibility2.value } : {},
8177
- ...tooltip.value !== void 0 ? { tooltip: tooltip.value } : {}
8855
+ ...tooltip.value !== void 0 ? { tooltip: tooltip.value } : {},
8856
+ ...visible.value !== void 0 ? { visible: visible.value } : {}
8178
8857
  });
8179
8858
  };
8180
8859
  var decodeTreeOpAst = (path, j) => {
@@ -8383,10 +9062,238 @@ var decodeOp = (json, policy) => {
8383
9062
  resetWalk(policy);
8384
9063
  return decodeTreeOpAst("$", parsed.value);
8385
9064
  };
9065
+ var decodeOps = (json, policy) => {
9066
+ const parsed = parse(json);
9067
+ if (!parsed.ok) return parseFailure(parsed.error);
9068
+ resetWalk(policy);
9069
+ const entries = parsed.value.kind === "JArray" ? parsed.value.items : [parsed.value];
9070
+ const ops = [];
9071
+ for (let i = 0; i < entries.length; i += 1) {
9072
+ const path = parsed.value.kind === "JArray" ? `$[${i}]` : "$";
9073
+ const decoded = decodeTreeOpAst(path, entries[i]);
9074
+ if (!decoded.ok) return decoded;
9075
+ ops.push(decoded.value);
9076
+ }
9077
+ return ok(ops);
9078
+ };
9079
+
9080
+ // src/capabilityDecl.ts
9081
+ var dok2 = (value) => ({ ok: true, value });
9082
+ var derr2 = (error) => ({ ok: false, error });
9083
+ var HOST_WIRE = {
9084
+ Pure: "pure",
9085
+ ReadsHost: "readsHost",
9086
+ WritesHost: "writesHost"
9087
+ };
9088
+ var DETERMINISM_WIRE = {
9089
+ Deterministic: "deterministic",
9090
+ Clock: "clock",
9091
+ Random: "random",
9092
+ Network: "network"
9093
+ };
9094
+ var PLACEMENT_WIRE = {
9095
+ BuildTime: "buildTime",
9096
+ Server: "server",
9097
+ ClientDeclarative: "clientDeclarative",
9098
+ ClientIsland: "clientIsland",
9099
+ Precomputed: "precomputed"
9100
+ };
9101
+ var SPACE_WIRE = {
9102
+ IntRange: "intRange",
9103
+ FloatRange: "floatRange",
9104
+ StringLen: "stringLen",
9105
+ Enum: "enum",
9106
+ AnyString: "anyString"
9107
+ };
9108
+ var ISLANDS = ["pyodide", "fable", "js"];
9109
+ var invert = (m) => Object.fromEntries(Object.entries(m).map(([k, v]) => [v, k]));
9110
+ var HOST_OF = invert(HOST_WIRE);
9111
+ var DETERMINISM_OF = invert(DETERMINISM_WIRE);
9112
+ var PLACEMENT_OF = invert(PLACEMENT_WIRE);
9113
+ var SPACE_OF = invert(SPACE_WIRE);
9114
+ var spaceJson = (s) => {
9115
+ switch (s.kind) {
9116
+ case "IntRange":
9117
+ return caseObj(SPACE_WIRE[s.kind], [
9118
+ ["min", num(s.min)],
9119
+ ["max", num(s.max)]
9120
+ ]);
9121
+ case "FloatRange":
9122
+ return caseObj(SPACE_WIRE[s.kind], [
9123
+ ["min", num(s.min)],
9124
+ ["max", num(s.max)]
9125
+ ]);
9126
+ case "StringLen":
9127
+ return caseObj(SPACE_WIRE[s.kind], [
9128
+ ["min", num(s.minLen)],
9129
+ ["max", num(s.maxLen)]
9130
+ ]);
9131
+ case "Enum":
9132
+ return caseObj(SPACE_WIRE[s.kind], [["values", jArray(s.choices.map(str))]]);
9133
+ case "AnyString":
9134
+ return caseObj(SPACE_WIRE[s.kind], []);
9135
+ }
9136
+ };
9137
+ var entryJson = (e) => {
9138
+ const fields = [
9139
+ ["addr", str(e.addr)],
9140
+ ["name", str(e.name)],
9141
+ ["kind", str(e.kind)],
9142
+ ["required", bool(e.required)]
9143
+ ];
9144
+ if (e.space !== void 0) fields.push(["space", spaceJson(e.space)]);
9145
+ if (e.slotKind !== void 0) fields.push(["slotKind", str(e.slotKind)]);
9146
+ return jObject(fields);
9147
+ };
9148
+ var placementJson = (p) => p.kind === "ClientIsland" ? caseObj(PLACEMENT_WIRE[p.kind], [["island", str(p.island)]]) : caseObj(PLACEMENT_WIRE[p.kind], []);
9149
+ var signatureJson = (sg) => jObject([
9150
+ ["name", str(sg.name)],
9151
+ [
9152
+ "effect",
9153
+ jObject([
9154
+ ["host", str(HOST_WIRE[sg.effect.hostEffect])],
9155
+ ["determinism", str(DETERMINISM_WIRE[sg.effect.determinism])]
9156
+ ])
9157
+ ],
9158
+ ["holes", jArray(sg.holes.map(entryJson))]
9159
+ ]);
9160
+ var encodeCapabilityDeclaration = (cap) => caseObj("capability", [
9161
+ ["id", str(cap.id)],
9162
+ ["signature", signatureJson(cap.signature)],
9163
+ ["determinism", str(DETERMINISM_WIRE[cap.determinism])],
9164
+ ["placement", placementJson(cap.placement)]
9165
+ ]);
9166
+ var DeclError = class extends Error {
9167
+ };
9168
+ var fail2 = (message) => {
9169
+ throw new DeclError(message);
9170
+ };
9171
+ var asObject = (v, what) => typeof v !== "object" || v === null || Array.isArray(v) ? fail2(`${what} is not an object`) : v;
9172
+ var strAt = (o, k) => {
9173
+ const v = o[k];
9174
+ return typeof v === "string" ? v : fail2(`missing or non-string field: ${k}`);
9175
+ };
9176
+ var numAt = (o, k) => {
9177
+ const v = o[k];
9178
+ return typeof v === "number" && Number.isFinite(v) ? v : fail2(`missing or non-finite numeric field: ${k}`);
9179
+ };
9180
+ var spaceOf = (raw) => {
9181
+ const o = asObject(raw, "value-space");
9182
+ const tag = strAt(o, "$type");
9183
+ const kind = SPACE_OF[tag];
9184
+ switch (kind) {
9185
+ case "IntRange":
9186
+ return { kind, min: numAt(o, "min"), max: numAt(o, "max") };
9187
+ case "FloatRange":
9188
+ return { kind, min: numAt(o, "min"), max: numAt(o, "max") };
9189
+ case "StringLen":
9190
+ return { kind, minLen: numAt(o, "min"), maxLen: numAt(o, "max") };
9191
+ case "Enum": {
9192
+ const values = o["values"];
9193
+ if (!Array.isArray(values) || values.some((x) => typeof x !== "string"))
9194
+ fail2("enum values must be a string array");
9195
+ return { kind, choices: values };
9196
+ }
9197
+ case "AnyString":
9198
+ return { kind };
9199
+ default:
9200
+ return fail2(
9201
+ `unknown value-space kind: ${tag}; expected one of: ${Object.values(SPACE_WIRE).join(", ")}`
9202
+ );
9203
+ }
9204
+ };
9205
+ var entryOf = (raw) => {
9206
+ const o = asObject(raw, "signature hole");
9207
+ if ("actionEffect" in o) {
9208
+ fail2(
9209
+ "this host's signature entry carries no action-effect axis, so a hole declaring `actionEffect` cannot round-trip"
9210
+ );
9211
+ }
9212
+ const kind = strAt(o, "kind");
9213
+ if (kind !== "value" && kind !== "slot" && kind !== "repeat")
9214
+ fail2(`unknown hole kind: ${kind}; expected one of: value, slot, repeat`);
9215
+ const required = o["required"];
9216
+ if (typeof required !== "boolean") fail2("missing or non-boolean field: required");
9217
+ return {
9218
+ addr: strAt(o, "addr"),
9219
+ name: strAt(o, "name"),
9220
+ kind,
9221
+ required,
9222
+ ..."space" in o ? { space: spaceOf(o["space"]) } : {},
9223
+ ..."slotKind" in o ? { slotKind: strAt(o, "slotKind") } : {}
9224
+ };
9225
+ };
9226
+ var placementOf = (raw) => {
9227
+ const o = asObject(raw, "placement");
9228
+ const tag = strAt(o, "$type");
9229
+ const kind = PLACEMENT_OF[tag];
9230
+ if (kind === void 0)
9231
+ fail2(`unknown placement: ${tag}; expected one of: ${Object.values(PLACEMENT_WIRE).join(", ")}`);
9232
+ if (kind === "ClientIsland") {
9233
+ const island = strAt(o, "island");
9234
+ if (!ISLANDS.includes(island))
9235
+ fail2(`unknown island kind: ${island}; expected one of: ${ISLANDS.join(", ")}`);
9236
+ return { kind, island };
9237
+ }
9238
+ return { kind };
9239
+ };
9240
+ var declarationOf = (json) => {
9241
+ let parsed;
9242
+ try {
9243
+ parsed = JSON.parse(json);
9244
+ } catch {
9245
+ return fail2("capability declaration is not well-formed JSON");
9246
+ }
9247
+ const o = asObject(parsed, "capability declaration");
9248
+ const tag = o["$type"];
9249
+ if (tag !== void 0 && tag !== "capability")
9250
+ fail2(`not a capability declaration: $type '${String(tag)}'`);
9251
+ const sigObj = asObject(o["signature"], "signature");
9252
+ const effectObj = asObject(sigObj["effect"], "effect");
9253
+ const hostTag = strAt(effectObj, "host");
9254
+ const hostEffect = HOST_OF[hostTag];
9255
+ if (hostEffect === void 0)
9256
+ fail2(
9257
+ `unknown host effect: ${hostTag}; expected one of: ${Object.values(HOST_WIRE).join(", ")}`
9258
+ );
9259
+ const detTag = strAt(effectObj, "determinism");
9260
+ const determinism = DETERMINISM_OF[detTag];
9261
+ if (determinism === void 0)
9262
+ fail2(
9263
+ `unknown determinism: ${detTag}; expected one of: ${Object.values(DETERMINISM_WIRE).join(", ")}`
9264
+ );
9265
+ const holes = sigObj["holes"];
9266
+ if (!Array.isArray(holes)) fail2("missing or non-array field: holes");
9267
+ const signature = {
9268
+ name: strAt(sigObj, "name"),
9269
+ holes: holes.map(entryOf),
9270
+ effect: { hostEffect, determinism }
9271
+ };
9272
+ const wireTag = strAt(o, "determinism");
9273
+ const expectedTag = DETERMINISM_WIRE[determinism];
9274
+ if (wireTag !== expectedTag)
9275
+ fail2(
9276
+ `capability determinism disagrees with signature effect: wire '${wireTag}' vs signature '${expectedTag}'`
9277
+ );
9278
+ return {
9279
+ id: strAt(o, "id"),
9280
+ signature,
9281
+ determinism,
9282
+ placement: placementOf(o["placement"])
9283
+ };
9284
+ };
9285
+ var decodeCapabilityDeclaration = (json) => {
9286
+ try {
9287
+ return dok2(declarationOf(json));
9288
+ } catch (e) {
9289
+ if (e instanceof DeclError) return derr2(e.message);
9290
+ throw e;
9291
+ }
9292
+ };
8386
9293
 
8387
9294
  // src/apply.ts
8388
9295
  var ok2 = (value) => ({ ok: true, value });
8389
- var fail2 = (code, message, batchIndex) => ({
9296
+ var fail3 = (code, message, batchIndex) => ({
8390
9297
  ok: false,
8391
9298
  error: batchIndex === void 0 ? { code, message } : { code, message, batchIndex }
8392
9299
  });
@@ -9223,22 +10130,22 @@ var applyOne = (op, root, telem) => {
9223
10130
  case "EditNode": {
9224
10131
  const updatedTree = mapNode(op.target, (n) => ({ ...n, kind: op.newKind }), root);
9225
10132
  if (updatedTree === void 0)
9226
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10133
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9227
10134
  telem.push({ op: "EditNode", targetId: op.target });
9228
10135
  return ok2(updatedTree);
9229
10136
  }
9230
10137
  case "UpdateProp": {
9231
10138
  const parsed = parsePath(op.path);
9232
10139
  if (!parsed.ok)
9233
- return fail2("PathInvalid", `Path '${op.path}' is structurally invalid: ${parsed.error}.`);
10140
+ return fail3("PathInvalid", `Path '${op.path}' is structurally invalid: ${parsed.error}.`);
9234
10141
  const targetNode = findNode(op.target, root);
9235
10142
  if (targetNode === void 0)
9236
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10143
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9237
10144
  const segs = parsed.value;
9238
10145
  const finish = (kind) => {
9239
10146
  const newTree = mapNode(op.target, (n) => ({ ...n, kind }), root);
9240
10147
  if (newTree === void 0)
9241
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10148
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9242
10149
  telem.push({ op: "UpdateProp", targetId: op.target });
9243
10150
  return ok2(newTree);
9244
10151
  };
@@ -9248,14 +10155,14 @@ var applyOne = (op, root, telem) => {
9248
10155
  case "updated":
9249
10156
  return finish(outcome2.kind);
9250
10157
  case "unknownField":
9251
- return fail2("FieldNotFound", `Field '${op.path}' not found on node '${op.target}'.`);
10158
+ return fail3("FieldNotFound", `Field '${op.path}' not found on node '${op.target}'.`);
9252
10159
  case "notSupported":
9253
- return fail2(
10160
+ return fail3(
9254
10161
  "PathNotSupportedYet",
9255
10162
  `Path '${op.path}' on node '${op.target}' is not yet supported by the apply engine.`
9256
10163
  );
9257
10164
  case "typeMismatch":
9258
- return fail2(
10165
+ return fail3(
9259
10166
  "KindMismatch",
9260
10167
  `UpdateProp value for '${op.path}' on node '${op.target}' does not match the field's expected type: ${outcome2.detail}`
9261
10168
  );
@@ -9267,27 +10174,27 @@ var applyOne = (op, root, telem) => {
9267
10174
  case "updated":
9268
10175
  return finish(outcome.kind);
9269
10176
  case "missingIndex":
9270
- return fail2(
10177
+ return fail3(
9271
10178
  "PathInvalid",
9272
10179
  `Field '${outcome.listField}' on node '${op.target}' is a list \u2014 address an element with a 0-based index (the list has ${outcome.count} element(s)).`
9273
10180
  );
9274
10181
  case "indexOutOfRange":
9275
- return fail2(
10182
+ return fail3(
9276
10183
  "PositionOutOfRange",
9277
10184
  `Index ${outcome.requested} is out of range for '${outcome.listField}' on node '${op.target}' (${outcome.count === 0 ? "the list is empty" : `valid: 0..${outcome.count - 1}`}).`
9278
10185
  );
9279
10186
  case "fieldNotFound":
9280
- return fail2(
10187
+ return fail3(
9281
10188
  "FieldNotFound",
9282
10189
  `Field '${outcome.segment}' (in path '${op.path}') not found on node '${op.target}'. Available at this segment: ${outcome.available.join(", ")}.`
9283
10190
  );
9284
10191
  case "notSupported":
9285
- return fail2(
10192
+ return fail3(
9286
10193
  "PathNotSupportedYet",
9287
10194
  `Path '${op.path}' on node '${op.target}' is not yet supported by the apply engine.`
9288
10195
  );
9289
10196
  case "typeMismatch":
9290
- return fail2(
10197
+ return fail3(
9291
10198
  "KindMismatch",
9292
10199
  `UpdateProp value for '${op.path}' on node '${op.target}' does not match the field's expected type: ${outcome.detail}`
9293
10200
  );
@@ -9297,20 +10204,20 @@ var applyOne = (op, root, telem) => {
9297
10204
  case "ReplaceBinding": {
9298
10205
  const targetNode = findNode(op.target, root);
9299
10206
  if (targetNode === void 0)
9300
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10207
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9301
10208
  const newKind = replaceBinding(op.slot, op.binding, targetNode.kind);
9302
10209
  if (newKind === void 0)
9303
- return fail2("SlotNotFound", `Binding slot '${op.slot}' not found on node '${op.target}'.`);
10210
+ return fail3("SlotNotFound", `Binding slot '${op.slot}' not found on node '${op.target}'.`);
9304
10211
  const newTree = mapNode(op.target, (n) => ({ ...n, kind: newKind }), root);
9305
10212
  if (newTree === void 0)
9306
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10213
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9307
10214
  telem.push({ op: "ReplaceBinding", targetId: op.target });
9308
10215
  return ok2(newTree);
9309
10216
  }
9310
10217
  case "UpdateStyle": {
9311
10218
  const newTree = mapNode(op.target, (n) => ({ ...n, style: op.style }), root);
9312
10219
  if (newTree === void 0)
9313
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10220
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9314
10221
  telem.push({ op: "UpdateStyle", targetId: op.target });
9315
10222
  return ok2(newTree);
9316
10223
  }
@@ -9321,39 +10228,39 @@ var applyOne = (op, root, telem) => {
9321
10228
  root
9322
10229
  );
9323
10230
  if (newTree === void 0)
9324
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10231
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9325
10232
  telem.push({ op: "UpdateState", targetId: op.target });
9326
10233
  return ok2(newTree);
9327
10234
  }
9328
10235
  case "InsertChild": {
9329
10236
  const parent = findNode(op.parentId, root);
9330
10237
  if (parent === void 0)
9331
- return fail2("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
10238
+ return fail3("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
9332
10239
  const children = layoutChildren(parent);
9333
10240
  if (children === void 0)
9334
- return fail2(
10241
+ return fail3(
9335
10242
  "ChildlessKind",
9336
10243
  `Node '${op.parentId}' (kind=${parent.kind.kind}) has no children field \u2014 only Layout kinds accept structural child ops.`
9337
10244
  );
9338
10245
  const existing = new Set(allNodeIds(root));
9339
10246
  const duplicate = allNodeIds(op.child).find((id) => existing.has(id));
9340
10247
  if (duplicate !== void 0)
9341
- return fail2(
10248
+ return fail3(
9342
10249
  "DuplicateNodeId",
9343
10250
  `NodeId '${duplicate}' is already present in the tree; ids must be unique.`
9344
10251
  );
9345
10252
  const newChildren = [...children, op.child];
9346
10253
  const newTree = mapNode(op.parentId, (n) => withLayoutChildren(n, newChildren), root);
9347
10254
  if (newTree === void 0)
9348
- return fail2("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
10255
+ return fail3("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
9349
10256
  telem.push({ op: "InsertChild", targetId: op.parentId });
9350
10257
  return ok2(newTree);
9351
10258
  }
9352
10259
  case "RemoveNode": {
9353
- if (idOf(root) === op.target) return fail2("KindMismatch", "Cannot RemoveNode the root.");
10260
+ if (idOf(root) === op.target) return fail3("KindMismatch", "Cannot RemoveNode the root.");
9354
10261
  const parent = findLayoutParent(op.target, root);
9355
10262
  if (parent === void 0)
9356
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10263
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9357
10264
  const children = layoutChildren(parent);
9358
10265
  const newTree = mapNode(
9359
10266
  idOf(parent),
@@ -9364,26 +10271,26 @@ var applyOne = (op, root, telem) => {
9364
10271
  root
9365
10272
  );
9366
10273
  if (newTree === void 0)
9367
- return fail2("ParentNotFound", `Parent node '${idOf(parent)}' not found in tree.`);
10274
+ return fail3("ParentNotFound", `Parent node '${idOf(parent)}' not found in tree.`);
9368
10275
  telem.push({ op: "RemoveNode", targetId: op.target });
9369
10276
  return ok2(newTree);
9370
10277
  }
9371
10278
  case "MoveNode": {
9372
10279
  if (op.target === op.newParentId)
9373
- return fail2("KindMismatch", "Cannot move a node into itself.");
10280
+ return fail3("KindMismatch", "Cannot move a node into itself.");
9374
10281
  if (isAncestor(op.target, op.newParentId, root))
9375
- return fail2(
10282
+ return fail3(
9376
10283
  "KindMismatch",
9377
10284
  "Cannot move a node into its own descendant (would create a cycle)."
9378
10285
  );
9379
10286
  const moving = findNode(op.target, root);
9380
10287
  if (moving === void 0)
9381
- return fail2("NodeNotFound", `Node '${op.target}' not found in tree.`);
10288
+ return fail3("NodeNotFound", `Node '${op.target}' not found in tree.`);
9382
10289
  const newParent = findNode(op.newParentId, root);
9383
10290
  if (newParent === void 0)
9384
- return fail2("ParentNotFound", `Parent node '${op.newParentId}' not found in tree.`);
10291
+ return fail3("ParentNotFound", `Parent node '${op.newParentId}' not found in tree.`);
9385
10292
  if (layoutChildren(newParent) === void 0)
9386
- return fail2(
10293
+ return fail3(
9387
10294
  "ChildlessKind",
9388
10295
  `Node '${op.newParentId}' (kind=${newParent.kind.kind}) has no children field.`
9389
10296
  );
@@ -9401,10 +10308,10 @@ var applyOne = (op, root, telem) => {
9401
10308
  case "ReorderChildren": {
9402
10309
  const parent = findNode(op.parentId, root);
9403
10310
  if (parent === void 0)
9404
- return fail2("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
10311
+ return fail3("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
9405
10312
  const children = layoutChildren(parent);
9406
10313
  if (children === void 0)
9407
- return fail2(
10314
+ return fail3(
9408
10315
  "ChildlessKind",
9409
10316
  `Node '${op.parentId}' (kind=${parent.kind.kind}) has no children field.`
9410
10317
  );
@@ -9412,7 +10319,7 @@ var applyOne = (op, root, telem) => {
9412
10319
  const sortedCurrent = [...currentIds].sort();
9413
10320
  const sortedNew = [...op.newOrder].map((x) => x).sort();
9414
10321
  if (sortedCurrent.length !== sortedNew.length || sortedCurrent.some((id, i) => id !== sortedNew[i]))
9415
- return fail2(
10322
+ return fail3(
9416
10323
  "OrderingMismatch",
9417
10324
  `ReorderChildren for '${op.parentId}' did not list exactly the current child ids.`
9418
10325
  );
@@ -9420,7 +10327,7 @@ var applyOne = (op, root, telem) => {
9420
10327
  const reordered = op.newOrder.map((id) => byId.get(id)).filter((c) => c !== void 0);
9421
10328
  const newTree = mapNode(op.parentId, (n) => withLayoutChildren(n, reordered), root);
9422
10329
  if (newTree === void 0)
9423
- return fail2("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
10330
+ return fail3("ParentNotFound", `Parent node '${op.parentId}' not found in tree.`);
9424
10331
  telem.push({ op: "ReorderChildren", targetId: op.parentId });
9425
10332
  return ok2(newTree);
9426
10333
  }
@@ -9445,7 +10352,7 @@ var applyOne = (op, root, telem) => {
9445
10352
  return ok2(state);
9446
10353
  }
9447
10354
  }
9448
- return fail2("KindMismatch", "unreachable apply branch");
10355
+ return fail3("KindMismatch", "unreachable apply branch");
9449
10356
  };
9450
10357
  var apply = (tree, op) => {
9451
10358
  const telem = [];
@@ -9768,11 +10675,20 @@ var isPureAddition = (baseIds, headIds) => {
9768
10675
  return survive.length === baseIds.length && survive.every((v, i) => v === baseIds[i]) && headKept.length === baseIds.length && headKept.every((v, i) => v === baseIds[i]);
9769
10676
  };
9770
10677
  var eqOpt = (x, y) => (x ?? null) === (y ?? null);
9771
- var pickField = (conflicts, nodeId, facet, baseV, aV, bV) => {
10678
+ var refusal = (nodeId, facet, cls, base, aValue, bValue) => ({
10679
+ nodeId,
10680
+ facet,
10681
+ class: cls,
10682
+ base,
10683
+ a: { value: aValue },
10684
+ b: { value: bValue },
10685
+ primacyHeld: false
10686
+ });
10687
+ var pickField = (conflicts, nodeId, facet, dflt, baseV, aV, bV) => {
9772
10688
  const aCh = !eqOpt(aV, baseV);
9773
10689
  const bCh = !eqOpt(bV, baseV);
9774
10690
  if (aCh && bCh && !eqOpt(aV, bV)) {
9775
- conflicts.push({ nodeId, facet });
10691
+ conflicts.push(refusal(nodeId, facet, "ConcurrentEdit", baseV ?? dflt, aV ?? dflt, bV ?? dflt));
9776
10692
  return baseV;
9777
10693
  }
9778
10694
  if (aCh) return aV;
@@ -9783,7 +10699,7 @@ var pickCanonical = (conflicts, nodeId, facet, baseC, aC, bC) => {
9783
10699
  const aCh = aC !== baseC;
9784
10700
  const bCh = bC !== baseC;
9785
10701
  if (aCh && bCh && aC !== bC) {
9786
- conflicts.push({ nodeId, facet });
10702
+ conflicts.push(refusal(nodeId, facet, "ConcurrentEdit", baseC, aC, bC));
9787
10703
  return 0;
9788
10704
  }
9789
10705
  if (aCh) return 1;
@@ -9794,18 +10710,28 @@ var mergeStyle = (conflicts, id, base, a, b) => {
9794
10710
  const bs = base.style;
9795
10711
  const as_ = a.style;
9796
10712
  const bsB = b.style;
9797
- const tone = pickField(conflicts, id, "style.tone", bs.tone, as_.tone, bsB.tone);
9798
- const weight = pickField(conflicts, id, "style.weight", bs.weight, as_.weight, bsB.weight);
10713
+ const d = defaults.style;
10714
+ const tone = pickField(conflicts, id, "style.tone", d.tone, bs.tone, as_.tone, bsB.tone);
10715
+ const weight = pickField(
10716
+ conflicts,
10717
+ id,
10718
+ "style.weight",
10719
+ d.weight,
10720
+ bs.weight,
10721
+ as_.weight,
10722
+ bsB.weight
10723
+ );
9799
10724
  const emphasis = pickField(
9800
10725
  conflicts,
9801
10726
  id,
9802
10727
  "style.emphasis",
10728
+ d.emphasis,
9803
10729
  bs.emphasis,
9804
10730
  as_.emphasis,
9805
10731
  bsB.emphasis
9806
10732
  );
9807
- const role = pickField(conflicts, id, "style.role", bs.role, as_.role, bsB.role);
9808
- const voice = pickField(conflicts, id, "style.voice", bs.voice, as_.voice, bsB.voice);
10733
+ const role = pickField(conflicts, id, "style.role", d.role, bs.role, as_.role, bsB.role);
10734
+ const voice = pickField(conflicts, id, "style.voice", d.voice, bs.voice, as_.voice, bsB.voice);
9809
10735
  const style = { tone, weight, emphasis };
9810
10736
  return {
9811
10737
  ...style,
@@ -9861,8 +10787,15 @@ var merge3 = (conflicts, base, aOpt, bOpt) => {
9861
10787
  const bc = baseMap.get(cid);
9862
10788
  if (bc !== void 0) return merge3(conflicts, bc, aMap.get(cid), bMap.get(cid));
9863
10789
  const ac = aMap.get(cid);
9864
- if (ac !== void 0) return ac;
9865
10790
  const bb = bMap.get(cid);
10791
+ if (ac !== void 0 && bb !== void 0) {
10792
+ const acC = encodeNode(ac);
10793
+ const bcC = encodeNode(bb);
10794
+ if (acC === bcC) return ac;
10795
+ conflicts.push(refusal(cid, "insert", "ConcurrentEdit", "", acC, bcC));
10796
+ return ordinal(acC, bcC) <= 0 ? ac : bb;
10797
+ }
10798
+ if (ac !== void 0) return ac;
9866
10799
  if (bb !== void 0) return bb;
9867
10800
  throw new Error(`merge3: child id ${cid} vanished`);
9868
10801
  };
@@ -9873,6 +10806,8 @@ var merge3 = (conflicts, base, aOpt, bOpt) => {
9873
10806
  mergedChildren = aIds.map(recurseChild);
9874
10807
  } else if (!aStruct && bStruct) {
9875
10808
  mergedChildren = bIds.map(recurseChild);
10809
+ } else if (JSON.stringify(aIds) === JSON.stringify(bIds)) {
10810
+ mergedChildren = aIds.map(recurseChild);
9876
10811
  } else {
9877
10812
  const baseSet = new Set(baseIds);
9878
10813
  const aNew = aIds.filter((i) => !baseSet.has(i));
@@ -9884,7 +10819,16 @@ var merge3 = (conflicts, base, aOpt, bOpt) => {
9884
10819
  const newIds = [.../* @__PURE__ */ new Set([...aNew, ...bNew])].sort(ordinal);
9885
10820
  mergedChildren = [...survivors, ...newIds.map(recurseChild)];
9886
10821
  } else {
9887
- conflicts.push({ nodeId: id, facet: "children" });
10822
+ conflicts.push(
10823
+ refusal(
10824
+ id,
10825
+ "children",
10826
+ "ReorderVsStructural",
10827
+ baseIds.join(","),
10828
+ aIds.join(","),
10829
+ bIds.join(",")
10830
+ )
10831
+ );
9888
10832
  mergedChildren = baseIds.map(recurseChild);
9889
10833
  }
9890
10834
  }
@@ -9896,6 +10840,21 @@ var merge3Way = (base, a, b) => {
9896
10840
  const merged = merge3(conflicts, base, a, b);
9897
10841
  return conflicts.length === 0 ? { ok: true, tree: merged } : { ok: false, conflicts };
9898
10842
  };
10843
+ var sortConflictsCanonical = (conflicts) => [...conflicts].sort((x, y) => ordinal(x.nodeId, y.nodeId) || ordinal(x.facet, y.facet));
10844
+ var escapeJson = (s) => {
10845
+ let out = '"';
10846
+ for (const ch of s) {
10847
+ if (ch === '"') out += '\\"';
10848
+ else if (ch === "\\") out += "\\\\";
10849
+ else if (ch < " ") out += `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`;
10850
+ else out += ch;
10851
+ }
10852
+ return out + '"';
10853
+ };
10854
+ var encodeSide = (side) => `{"tag":${side.tag === void 0 ? "null" : escapeJson(side.tag)},"value":${escapeJson(side.value)}}`;
10855
+ var encodeMergeEnvelope = (conflicts) => "[" + sortConflictsCanonical(conflicts).map(
10856
+ (c) => `{"a":${encodeSide(c.a)},"b":${encodeSide(c.b)},"base":${escapeJson(c.base)},"class":${escapeJson(c.class)},"facet":${escapeJson(c.facet)},"nodeId":${escapeJson(c.nodeId)},"primacyHeld":${c.primacyHeld ? "true" : "false"}}`
10857
+ ).join(",") + "]";
9899
10858
 
9900
10859
  // src/versioning.ts
9901
10860
  var renderProfile = (p) => `${p.name}@${p.major}.${p.minor}`;
@@ -10783,6 +11742,6 @@ var withStateSeeds = (tree, sources) => {
10783
11742
  return { ...sources, state: { ...seeds, ...sources.state ?? {} } };
10784
11743
  };
10785
11744
 
10786
- export { ELICITATION_KEY, ELICITATION_VERSION, HOST_RESERVED_PREFIX, PAYLOAD_KEY, PROFILE_KEY, REQUIRED_PROFILE_KEY, apply, canPlace, cellString, coerce, collectStateSeeds, coreV1, decodeDagRecord, decodeElicitation, decodeElicitationOutcome, decodeEnvelope, decodeEnvelopeAst, decodeNode, decodeNodeTolerant, decodeOp, decodeTolerant, derivedFreshIds, duplicateOp, duplicateOpWith, encodeCell, encodeColExpr, encodeDagRecord, encodeDataSource, encodeElicitation, encodeElicitationOutcome, encodeEnvelope2 as encodeEnvelope, encodeNode, encodeOp, encodePipeline, evalErrorString, evalPipeline, evalPipelineInEnv, evalPipelineWith, evalPipelineWithInEnv, evalSource, field as jsonField, liveValueToTable, merge3Way, moveOp, negotiate, negotiateEnvelope, noResolve, nudgeOp, parse, pasteOp, pasteOpWith, pipelineParams, placeOp, reencodeNode, renderAstCanonical, renderProfile, sequentialFreshIds, stepParams, substituteListParams, tryParseProfile, validateAnswer, validateAnswerAt, validateAnswerDocument, withStateSeeds };
11745
+ export { DECODED_COMPUTED_MESSAGE, ELICITATION_KEY, ELICITATION_VERSION, HOST_RESERVED_PREFIX, PAYLOAD_KEY, PROFILE_KEY, REQUIRED_PROFILE_KEY, WireSurvivabilityError, apply, canPlace, cellString, coerce, collectStateSeeds, coreV1, decodeCapabilityDeclaration, decodeDagRecord, decodeDataSource, decodeElicitation, decodeElicitationOutcome, decodeEnvelope, decodeEnvelopeAst, decodeNode, decodeNodeTolerant, decodeOp, decodeOps, decodePipelineCore as decodePipeline, decodeTolerant, derivedFreshIds, duplicateOp, duplicateOpWith, encodeCapabilityDeclaration, encodeCell, encodeColExpr, encodeDagRecord, encodeDataSource, encodeElicitation, encodeElicitationOutcome, encodeEnvelope2 as encodeEnvelope, encodeMergeEnvelope, encodeNode, encodeOp, encodePipeline, evalErrorString, evalPipeline, evalPipelineInEnv, evalPipelineWith, evalPipelineWithInEnv, evalSource, fixedText, identityFormat, field as jsonField, liveValueToTable, merge3Way, moveOp, negotiate, negotiateEnvelope, noResolve, nudgeOp, numberText, parse, pasteOp, pasteOpWith, pipelineParams, placeOp, reencodeNode, renderAstCanonical, renderProfile, scalarOfText, sequentialFreshIds, sortConflictsCanonical, stepParams, substituteListParams, tryNumberText, tryParseProfile, validateAnswer, validateAnswerAt, validateAnswerDocument, withStateSeeds };
10787
11746
  //# sourceMappingURL=index.js.map
10788
11747
  //# sourceMappingURL=index.js.map