@zoahhq/cli 0.1.4 → 0.1.6

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.mjs +1725 -1117
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -338,13 +338,21 @@ var init_colors = __esm({
338
338
  var client_exports = {};
339
339
  __export(client_exports, {
340
340
  ApiClient: () => ApiClient,
341
- ApiError: () => ApiError
341
+ ApiError: () => ApiError,
342
+ ImportFailedError: () => ImportFailedError
342
343
  });
343
344
  import { request as request2 } from "undici";
344
- var ApiError, ApiClient;
345
+ var ImportFailedError, ApiError, ApiClient;
345
346
  var init_client = __esm({
346
347
  "src/api/client.ts"() {
347
348
  "use strict";
349
+ ImportFailedError = class extends Error {
350
+ constructor(reason) {
351
+ super(reason);
352
+ this.reason = reason;
353
+ this.name = "ImportFailedError";
354
+ }
355
+ };
348
356
  ApiError = class extends Error {
349
357
  status;
350
358
  body;
@@ -441,11 +449,7 @@ var init_client = __esm({
441
449
  return status.result;
442
450
  }
443
451
  if (status.status === "failed") {
444
- throw new ApiError(
445
- 500,
446
- status.error ?? "",
447
- `Import failed: ${status.error ?? "unknown error"}`
448
- );
452
+ throw new ImportFailedError(status.error ?? "unknown error");
449
453
  }
450
454
  }
451
455
  }
@@ -7232,8 +7236,8 @@ function expandBoxShorthand(raw) {
7232
7236
  return { top: all, right: all, bottom: all, left: all };
7233
7237
  }
7234
7238
  if (parts.length === 2) {
7235
- const [v14, h4] = parts;
7236
- return { top: v14, right: h4, bottom: v14, left: h4 };
7239
+ const [v15, h4] = parts;
7240
+ return { top: v15, right: h4, bottom: v15, left: h4 };
7237
7241
  }
7238
7242
  if (parts.length === 3) {
7239
7243
  const [t3, h4, b4] = parts;
@@ -7271,17 +7275,17 @@ function parseBorderShorthand(raw, ctx, property) {
7271
7275
  return result;
7272
7276
  }
7273
7277
  function looksLikeColor(value) {
7274
- const v14 = value.trim().toLowerCase();
7275
- if (v14 === "")
7278
+ const v15 = value.trim().toLowerCase();
7279
+ if (v15 === "")
7276
7280
  return false;
7277
- if (v14.startsWith("#"))
7281
+ if (v15.startsWith("#"))
7278
7282
  return true;
7279
- if (v14.startsWith("rgb(") || v14.startsWith("rgba(") || v14.startsWith("hsl(") || v14.startsWith("hsla(") || v14.startsWith("oklch(") || v14.startsWith("oklab(") || v14.startsWith("color(") || v14.startsWith("hwb(")) {
7283
+ if (v15.startsWith("rgb(") || v15.startsWith("rgba(") || v15.startsWith("hsl(") || v15.startsWith("hsla(") || v15.startsWith("oklch(") || v15.startsWith("oklab(") || v15.startsWith("color(") || v15.startsWith("hwb(")) {
7280
7284
  return true;
7281
7285
  }
7282
- if (COLOR_KEYWORDS.has(v14))
7286
+ if (COLOR_KEYWORDS.has(v15))
7283
7287
  return true;
7284
- if (/^[a-z]+$/.test(v14) && v14.length > 2)
7288
+ if (/^[a-z]+$/.test(v15) && v15.length > 2)
7285
7289
  return true;
7286
7290
  return false;
7287
7291
  }
@@ -7357,30 +7361,30 @@ function mapToNodeLayout(css, ctx) {
7357
7361
  out.visibility = "none";
7358
7362
  break;
7359
7363
  }
7360
- const v14 = isOneOf(value, Object.values(LayoutTypes));
7361
- if (v14) {
7362
- out.display = v14;
7364
+ const v15 = isOneOf(value, Object.values(LayoutTypes));
7365
+ if (v15) {
7366
+ out.display = v15;
7363
7367
  } else {
7364
7368
  pushIssue(ctx, "CSS_DISPLAY_VALUE_UNSUPPORTED", `Unsupported display value: "${value}"`);
7365
7369
  }
7366
7370
  break;
7367
7371
  }
7368
7372
  case "visibility": {
7369
- const v14 = isOneOf(value, Object.values(VisibilityTypes));
7370
- if (v14)
7371
- out.visibility = v14;
7373
+ const v15 = isOneOf(value, Object.values(VisibilityTypes));
7374
+ if (v15)
7375
+ out.visibility = v15;
7372
7376
  break;
7373
7377
  }
7374
7378
  case "flex-direction": {
7375
- const v14 = isOneOf(value, Object.values(FlexDirectionTypes));
7376
- if (v14)
7377
- out.flexDirection = v14;
7379
+ const v15 = isOneOf(value, Object.values(FlexDirectionTypes));
7380
+ if (v15)
7381
+ out.flexDirection = v15;
7378
7382
  break;
7379
7383
  }
7380
7384
  case "flex-wrap": {
7381
- const v14 = isOneOf(value, Object.values(FlexWrapTypes));
7382
- if (v14)
7383
- out.flexWrap = v14;
7385
+ const v15 = isOneOf(value, Object.values(FlexWrapTypes));
7386
+ if (v15)
7387
+ out.flexWrap = v15;
7384
7388
  break;
7385
7389
  }
7386
7390
  case "flex-grow": {
@@ -7416,27 +7420,27 @@ function mapToNodeLayout(css, ctx) {
7416
7420
  break;
7417
7421
  }
7418
7422
  case "justify-content": {
7419
- const v14 = isOneOf(value, Object.values(JustifyContentTypes));
7420
- if (v14)
7421
- out.justifyContent = v14;
7423
+ const v15 = isOneOf(value, Object.values(JustifyContentTypes));
7424
+ if (v15)
7425
+ out.justifyContent = v15;
7422
7426
  break;
7423
7427
  }
7424
7428
  case "justify-items": {
7425
- const v14 = isOneOf(value, Object.values(JustifyItemsTypes));
7426
- if (v14)
7427
- out.justifyItems = v14;
7429
+ const v15 = isOneOf(value, Object.values(JustifyItemsTypes));
7430
+ if (v15)
7431
+ out.justifyItems = v15;
7428
7432
  break;
7429
7433
  }
7430
7434
  case "align-content": {
7431
- const v14 = isOneOf(value, Object.values(JustifyContentTypes));
7432
- if (v14)
7433
- out.alignContent = v14;
7435
+ const v15 = isOneOf(value, Object.values(JustifyContentTypes));
7436
+ if (v15)
7437
+ out.alignContent = v15;
7434
7438
  break;
7435
7439
  }
7436
7440
  case "align-items": {
7437
- const v14 = isOneOf(value, Object.values(JustifyItemsTypes));
7438
- if (v14)
7439
- out.alignItems = v14;
7441
+ const v15 = isOneOf(value, Object.values(JustifyItemsTypes));
7442
+ if (v15)
7443
+ out.alignItems = v15;
7440
7444
  break;
7441
7445
  }
7442
7446
  case "gap": {
@@ -7740,10 +7744,10 @@ function mapToNodeAppearance(css, ctx) {
7740
7744
  const expanded = expandBoxShorthand(value);
7741
7745
  const sides = ["top", "right", "bottom", "left"];
7742
7746
  for (const side of sides) {
7743
- const v14 = expanded[side];
7744
- if (v14 === void 0)
7747
+ const v15 = expanded[side];
7748
+ if (v15 === void 0)
7745
7749
  continue;
7746
- const s5 = isOneOf(v14, Object.values(BorderStyles));
7750
+ const s5 = isOneOf(v15, Object.values(BorderStyles));
7747
7751
  if (s5)
7748
7752
  setBorderStyle(side, s5);
7749
7753
  }
@@ -7836,9 +7840,9 @@ function mapToNodeAppearance(css, ctx) {
7836
7840
  break;
7837
7841
  }
7838
7842
  case "cursor": {
7839
- const v14 = isOneOf(value, Object.values(CursorTypes));
7840
- if (v14)
7841
- out.cursor = v14;
7843
+ const v15 = isOneOf(value, Object.values(CursorTypes));
7844
+ if (v15)
7845
+ out.cursor = v15;
7842
7846
  else
7843
7847
  pushIssue(ctx, "CSS_CURSOR_VALUE_UNSUPPORTED", `Unsupported cursor value: "${value}"`);
7844
7848
  break;
@@ -7867,9 +7871,9 @@ function mapToNodeAppearance(css, ctx) {
7867
7871
  break;
7868
7872
  }
7869
7873
  case "font-style": {
7870
- const v14 = isOneOf(value, Object.values(FontStyleTypes));
7871
- if (v14)
7872
- text.fontStyle = v14;
7874
+ const v15 = isOneOf(value, Object.values(FontStyleTypes));
7875
+ if (v15)
7876
+ text.fontStyle = v15;
7873
7877
  break;
7874
7878
  }
7875
7879
  case "line-height": {
@@ -7890,27 +7894,27 @@ function mapToNodeAppearance(css, ctx) {
7890
7894
  break;
7891
7895
  }
7892
7896
  case "text-align": {
7893
- const v14 = isOneOf(value, Object.values(TextAlignTypes));
7894
- if (v14)
7895
- text.textAlign = v14;
7897
+ const v15 = isOneOf(value, Object.values(TextAlignTypes));
7898
+ if (v15)
7899
+ text.textAlign = v15;
7896
7900
  break;
7897
7901
  }
7898
7902
  case "text-transform": {
7899
- const v14 = isOneOf(value, Object.values(TextTransformTypes));
7900
- if (v14)
7901
- text.textTransform = v14;
7903
+ const v15 = isOneOf(value, Object.values(TextTransformTypes));
7904
+ if (v15)
7905
+ text.textTransform = v15;
7902
7906
  break;
7903
7907
  }
7904
7908
  case "text-overflow": {
7905
- const v14 = isOneOf(value, Object.values(TextOverflowTypes));
7906
- if (v14)
7907
- text.textOverflow = v14;
7909
+ const v15 = isOneOf(value, Object.values(TextOverflowTypes));
7910
+ if (v15)
7911
+ text.textOverflow = v15;
7908
7912
  break;
7909
7913
  }
7910
7914
  case "white-space": {
7911
- const v14 = isOneOf(value, Object.values(WhiteSpaceTypes));
7912
- if (v14)
7913
- text.whiteSpace = v14;
7915
+ const v15 = isOneOf(value, Object.values(WhiteSpaceTypes));
7916
+ if (v15)
7917
+ text.whiteSpace = v15;
7914
7918
  break;
7915
7919
  }
7916
7920
  default:
@@ -7942,9 +7946,9 @@ function mapToNodePosition(css, ctx) {
7942
7946
  continue;
7943
7947
  switch (key) {
7944
7948
  case "position": {
7945
- const v14 = isOneOf(value, Object.values(PositionTypes));
7946
- if (v14)
7947
- out.type = v14;
7949
+ const v15 = isOneOf(value, Object.values(PositionTypes));
7950
+ if (v15)
7951
+ out.type = v15;
7948
7952
  else
7949
7953
  pushIssue(ctx, "CSS_POSITION_VALUE_UNSUPPORTED", `Unsupported position value: "${value}"`);
7950
7954
  break;
@@ -7974,23 +7978,23 @@ function mapToNodePosition(css, ctx) {
7974
7978
  break;
7975
7979
  }
7976
7980
  case "overflow": {
7977
- const v14 = isOneOf(value, Object.values(OverflowTypes));
7978
- if (v14) {
7979
- out.overflowX = v14;
7980
- out.overflowY = v14;
7981
+ const v15 = isOneOf(value, Object.values(OverflowTypes));
7982
+ if (v15) {
7983
+ out.overflowX = v15;
7984
+ out.overflowY = v15;
7981
7985
  }
7982
7986
  break;
7983
7987
  }
7984
7988
  case "overflow-x": {
7985
- const v14 = isOneOf(value, Object.values(OverflowTypes));
7986
- if (v14)
7987
- out.overflowX = v14;
7989
+ const v15 = isOneOf(value, Object.values(OverflowTypes));
7990
+ if (v15)
7991
+ out.overflowX = v15;
7988
7992
  break;
7989
7993
  }
7990
7994
  case "overflow-y": {
7991
- const v14 = isOneOf(value, Object.values(OverflowTypes));
7992
- if (v14)
7993
- out.overflowY = v14;
7995
+ const v15 = isOneOf(value, Object.values(OverflowTypes));
7996
+ if (v15)
7997
+ out.overflowY = v15;
7994
7998
  break;
7995
7999
  }
7996
8000
  case "z-index": {
@@ -24401,7 +24405,7 @@ function detectActiveExportCondition(packageRoot, resolvedAbs) {
24401
24405
  const pkg = readPackageJson(packageRoot);
24402
24406
  const exportsField = pkg?.exports;
24403
24407
  if (!exportsField || typeof exportsField !== "object") return null;
24404
- const isConditionMap = (v14) => typeof v14 === "object" && v14 !== null && !Array.isArray(v14);
24408
+ const isConditionMap = (v15) => typeof v15 === "object" && v15 !== null && !Array.isArray(v15);
24405
24409
  function valueMatches(value) {
24406
24410
  if (typeof value === "string") {
24407
24411
  const abs = resolve3(packageRoot, value);
@@ -25114,7 +25118,7 @@ function readRootVars(cssPaths) {
25114
25118
  const vars = /* @__PURE__ */ new Map();
25115
25119
  for (const path4 of cssPaths) {
25116
25120
  const text = safeRead(path4);
25117
- if (text) for (const [k, v14] of parseRootVars(text)) vars.set(k, v14);
25121
+ if (text) for (const [k, v15] of parseRootVars(text)) vars.set(k, v15);
25118
25122
  }
25119
25123
  return vars;
25120
25124
  }
@@ -25156,8 +25160,8 @@ function parseThemeColorTokens(cssPaths) {
25156
25160
  for (const path4 of cssPaths) {
25157
25161
  const text = safeRead(path4);
25158
25162
  if (!text) continue;
25159
- for (const [k, v14] of parseRootVars(text)) rootVars.set(k, v14);
25160
- for (const [k, v14] of parseThemeColorMap(text)) themeColors.set(k, v14);
25163
+ for (const [k, v15] of parseRootVars(text)) rootVars.set(k, v15);
25164
+ for (const [k, v15] of parseThemeColorMap(text)) themeColors.set(k, v15);
25161
25165
  }
25162
25166
  const names = themeColors.size > 0 ? themeColors : new Map(rootVars);
25163
25167
  const out = [];
@@ -25174,10 +25178,10 @@ function parseThemeColorTokens(cssPaths) {
25174
25178
  return out;
25175
25179
  }
25176
25180
  function isColorValue(value) {
25177
- const v14 = value.trim().toLowerCase();
25178
- if (v14.startsWith("#")) return true;
25179
- if (/^(rgb|rgba|hsl|hsla|oklch|oklab|color|hwb)\(/.test(v14)) return true;
25180
- return CSS_NAMED_COLORS.has(v14);
25181
+ const v15 = value.trim().toLowerCase();
25182
+ if (v15.startsWith("#")) return true;
25183
+ if (/^(rgb|rgba|hsl|hsla|oklch|oklab|color|hwb)\(/.test(v15)) return true;
25184
+ return CSS_NAMED_COLORS.has(v15);
25181
25185
  }
25182
25186
  var CSS_ENTRY_CANDIDATES, CSS_NAMED_COLORS;
25183
25187
  var init_tailwind_tokens = __esm({
@@ -25301,14 +25305,14 @@ function ruleToCss(rule, getVar) {
25301
25305
  return Object.keys(out).length > 0 ? out : null;
25302
25306
  }
25303
25307
  function resolveValue(value, getVar) {
25304
- let v14 = value.trim();
25305
- const mix = /^color-mix\([^,]+,\s*(.+?)\s+\d+(?:\.\d+)?%\s*,/.exec(v14);
25306
- if (mix) v14 = mix[1].trim();
25307
- v14 = substituteVars(v14, getVar);
25308
- if (v14.includes("var(")) return null;
25309
- if (v14.startsWith("calc(")) return evalCalc(v14);
25310
- if (v14.includes("calc(") || v14.includes("color-mix(")) return null;
25311
- return v14;
25308
+ let v15 = value.trim();
25309
+ const mix = /^color-mix\([^,]+,\s*(.+?)\s+\d+(?:\.\d+)?%\s*,/.exec(v15);
25310
+ if (mix) v15 = mix[1].trim();
25311
+ v15 = substituteVars(v15, getVar);
25312
+ if (v15.includes("var(")) return null;
25313
+ if (v15.startsWith("calc(")) return evalCalc(v15);
25314
+ if (v15.includes("calc(") || v15.includes("color-mix(")) return null;
25315
+ return v15;
25312
25316
  }
25313
25317
  function substituteVars(value, getVar) {
25314
25318
  return value.replace(
@@ -25340,11 +25344,11 @@ function evalCalc(value) {
25340
25344
  return null;
25341
25345
  }
25342
25346
  function resolveThemeValue(raw, getVar) {
25343
- const v14 = substituteVars(raw, getVar);
25344
- if (v14.includes("var(")) return null;
25345
- if (v14.startsWith("calc(")) return evalCalc(v14);
25346
- if (v14.includes("calc(")) return null;
25347
- return v14;
25347
+ const v15 = substituteVars(raw, getVar);
25348
+ if (v15.includes("var(")) return null;
25349
+ if (v15.startsWith("calc(")) return evalCalc(v15);
25350
+ if (v15.includes("calc(")) return null;
25351
+ return v15;
25348
25352
  }
25349
25353
  function themeFromDesignSystem(ds, getVar) {
25350
25354
  const theme = {};
@@ -25519,8 +25523,8 @@ function resolveColor(theme, token) {
25519
25523
  const colors = theme.colors;
25520
25524
  if (!colors) return void 0;
25521
25525
  if (token in colors) {
25522
- const v14 = colors[token];
25523
- if (typeof v14 === "string") return v14;
25526
+ const v15 = colors[token];
25527
+ if (typeof v15 === "string") return v15;
25524
25528
  }
25525
25529
  const dashIdx = token.indexOf("-");
25526
25530
  if (dashIdx > 0) {
@@ -25644,12 +25648,12 @@ function looksLikeColorValue(value) {
25644
25648
  function classToCss(className, theme) {
25645
25649
  if (className in STATIC_CLASS_TABLE) return STATIC_CLASS_TABLE[className];
25646
25650
  if (className === "rounded") {
25647
- const v14 = resolveBorderRadius(theme, "DEFAULT");
25648
- return v14 ? { "border-radius": v14 } : null;
25651
+ const v15 = resolveBorderRadius(theme, "DEFAULT");
25652
+ return v15 ? { "border-radius": v15 } : null;
25649
25653
  }
25650
25654
  if (className === "border") {
25651
- const v14 = resolveBorderWidth(theme, "DEFAULT") ?? "1px";
25652
- return { "border-width": v14, "border-style": "solid" };
25655
+ const v15 = resolveBorderWidth(theme, "DEFAULT") ?? "1px";
25656
+ return { "border-width": v15, "border-style": "solid" };
25653
25657
  }
25654
25658
  if (className === "shadow") {
25655
25659
  return { "box-shadow": theme.boxShadow?.DEFAULT ?? SHADOW_TABLE.DEFAULT };
@@ -25687,64 +25691,64 @@ function classToCss(className, theme) {
25687
25691
  }
25688
25692
  switch (prefix) {
25689
25693
  case "p": {
25690
- const v14 = resolveSpacing(theme, arg);
25691
- return v14 ? { padding: v14 } : null;
25694
+ const v15 = resolveSpacing(theme, arg);
25695
+ return v15 ? { padding: v15 } : null;
25692
25696
  }
25693
25697
  case "px": {
25694
- const v14 = resolveSpacing(theme, arg);
25695
- return v14 ? { "padding-left": v14, "padding-right": v14 } : null;
25698
+ const v15 = resolveSpacing(theme, arg);
25699
+ return v15 ? { "padding-left": v15, "padding-right": v15 } : null;
25696
25700
  }
25697
25701
  case "py": {
25698
- const v14 = resolveSpacing(theme, arg);
25699
- return v14 ? { "padding-top": v14, "padding-bottom": v14 } : null;
25702
+ const v15 = resolveSpacing(theme, arg);
25703
+ return v15 ? { "padding-top": v15, "padding-bottom": v15 } : null;
25700
25704
  }
25701
25705
  case "pt": {
25702
- const v14 = resolveSpacing(theme, arg);
25703
- return v14 ? { "padding-top": v14 } : null;
25706
+ const v15 = resolveSpacing(theme, arg);
25707
+ return v15 ? { "padding-top": v15 } : null;
25704
25708
  }
25705
25709
  case "pr": {
25706
- const v14 = resolveSpacing(theme, arg);
25707
- return v14 ? { "padding-right": v14 } : null;
25710
+ const v15 = resolveSpacing(theme, arg);
25711
+ return v15 ? { "padding-right": v15 } : null;
25708
25712
  }
25709
25713
  case "pb": {
25710
- const v14 = resolveSpacing(theme, arg);
25711
- return v14 ? { "padding-bottom": v14 } : null;
25714
+ const v15 = resolveSpacing(theme, arg);
25715
+ return v15 ? { "padding-bottom": v15 } : null;
25712
25716
  }
25713
25717
  case "pl": {
25714
- const v14 = resolveSpacing(theme, arg);
25715
- return v14 ? { "padding-left": v14 } : null;
25718
+ const v15 = resolveSpacing(theme, arg);
25719
+ return v15 ? { "padding-left": v15 } : null;
25716
25720
  }
25717
25721
  case "m": {
25718
- const v14 = resolveSpacing(theme, arg);
25719
- return v14 ? { margin: v14 } : null;
25722
+ const v15 = resolveSpacing(theme, arg);
25723
+ return v15 ? { margin: v15 } : null;
25720
25724
  }
25721
25725
  case "mx": {
25722
- const v14 = resolveSpacing(theme, arg);
25723
- return v14 ? { "margin-left": v14, "margin-right": v14 } : null;
25726
+ const v15 = resolveSpacing(theme, arg);
25727
+ return v15 ? { "margin-left": v15, "margin-right": v15 } : null;
25724
25728
  }
25725
25729
  case "my": {
25726
- const v14 = resolveSpacing(theme, arg);
25727
- return v14 ? { "margin-top": v14, "margin-bottom": v14 } : null;
25730
+ const v15 = resolveSpacing(theme, arg);
25731
+ return v15 ? { "margin-top": v15, "margin-bottom": v15 } : null;
25728
25732
  }
25729
25733
  case "mt": {
25730
- const v14 = resolveSpacing(theme, arg);
25731
- return v14 ? { "margin-top": v14 } : null;
25734
+ const v15 = resolveSpacing(theme, arg);
25735
+ return v15 ? { "margin-top": v15 } : null;
25732
25736
  }
25733
25737
  case "mr": {
25734
- const v14 = resolveSpacing(theme, arg);
25735
- return v14 ? { "margin-right": v14 } : null;
25738
+ const v15 = resolveSpacing(theme, arg);
25739
+ return v15 ? { "margin-right": v15 } : null;
25736
25740
  }
25737
25741
  case "mb": {
25738
- const v14 = resolveSpacing(theme, arg);
25739
- return v14 ? { "margin-bottom": v14 } : null;
25742
+ const v15 = resolveSpacing(theme, arg);
25743
+ return v15 ? { "margin-bottom": v15 } : null;
25740
25744
  }
25741
25745
  case "ml": {
25742
- const v14 = resolveSpacing(theme, arg);
25743
- return v14 ? { "margin-left": v14 } : null;
25746
+ const v15 = resolveSpacing(theme, arg);
25747
+ return v15 ? { "margin-left": v15 } : null;
25744
25748
  }
25745
25749
  case "gap": {
25746
- const v14 = resolveSpacing(theme, arg);
25747
- return v14 ? { gap: v14 } : null;
25750
+ const v15 = resolveSpacing(theme, arg);
25751
+ return v15 ? { gap: v15 } : null;
25748
25752
  }
25749
25753
  case "w": {
25750
25754
  if (arg === "screen") return { width: "100vw" };
@@ -25752,8 +25756,8 @@ function classToCss(className, theme) {
25752
25756
  if (kw) return { width: kw };
25753
25757
  const frac = resolveFraction(arg);
25754
25758
  if (frac) return { width: frac };
25755
- const v14 = resolveSpacing(theme, arg);
25756
- return v14 ? { width: v14 } : null;
25759
+ const v15 = resolveSpacing(theme, arg);
25760
+ return v15 ? { width: v15 } : null;
25757
25761
  }
25758
25762
  case "h": {
25759
25763
  if (arg === "screen") return { height: "100vh" };
@@ -25761,16 +25765,16 @@ function classToCss(className, theme) {
25761
25765
  if (kw) return { height: kw };
25762
25766
  const frac = resolveFraction(arg);
25763
25767
  if (frac) return { height: frac };
25764
- const v14 = resolveSpacing(theme, arg);
25765
- return v14 ? { height: v14 } : null;
25768
+ const v15 = resolveSpacing(theme, arg);
25769
+ return v15 ? { height: v15 } : null;
25766
25770
  }
25767
25771
  case "size": {
25768
25772
  const kw = sizingKeyword(arg);
25769
25773
  if (kw) return { width: kw, height: kw };
25770
25774
  const frac = resolveFraction(arg);
25771
25775
  if (frac) return { width: frac, height: frac };
25772
- const v14 = resolveSpacing(theme, arg);
25773
- return v14 ? { width: v14, height: v14 } : null;
25776
+ const v15 = resolveSpacing(theme, arg);
25777
+ return v15 ? { width: v15, height: v15 } : null;
25774
25778
  }
25775
25779
  case "cursor":
25776
25780
  return { cursor: arg };
@@ -25785,8 +25789,8 @@ function classToCss(className, theme) {
25785
25789
  if (rgba) return { "background-color": rgba };
25786
25790
  }
25787
25791
  }
25788
- const v14 = resolveColor(theme, arg) ?? resolveBaseColor(arg);
25789
- return v14 ? { "background-color": v14 } : null;
25792
+ const v15 = resolveColor(theme, arg) ?? resolveBaseColor(arg);
25793
+ return v15 ? { "background-color": v15 } : null;
25790
25794
  }
25791
25795
  case "text": {
25792
25796
  const size = resolveFontSize(theme, arg);
@@ -25817,8 +25821,8 @@ function classToCss(className, theme) {
25817
25821
  return null;
25818
25822
  }
25819
25823
  case "rounded": {
25820
- const v14 = resolveBorderRadius(theme, arg === "" ? "DEFAULT" : arg);
25821
- return v14 ? { "border-radius": v14 } : null;
25824
+ const v15 = resolveBorderRadius(theme, arg === "" ? "DEFAULT" : arg);
25825
+ return v15 ? { "border-radius": v15 } : null;
25822
25826
  }
25823
25827
  case "border": {
25824
25828
  const sideMap = {
@@ -25860,34 +25864,34 @@ function classToCss(className, theme) {
25860
25864
  return null;
25861
25865
  }
25862
25866
  case "top": {
25863
- const v14 = resolveSpacing(theme, arg);
25864
- return v14 ? { top: v14 } : null;
25867
+ const v15 = resolveSpacing(theme, arg);
25868
+ return v15 ? { top: v15 } : null;
25865
25869
  }
25866
25870
  case "right": {
25867
- const v14 = resolveSpacing(theme, arg);
25868
- return v14 ? { right: v14 } : null;
25871
+ const v15 = resolveSpacing(theme, arg);
25872
+ return v15 ? { right: v15 } : null;
25869
25873
  }
25870
25874
  case "bottom": {
25871
- const v14 = resolveSpacing(theme, arg);
25872
- return v14 ? { bottom: v14 } : null;
25875
+ const v15 = resolveSpacing(theme, arg);
25876
+ return v15 ? { bottom: v15 } : null;
25873
25877
  }
25874
25878
  case "left": {
25875
- const v14 = resolveSpacing(theme, arg);
25876
- return v14 ? { left: v14 } : null;
25879
+ const v15 = resolveSpacing(theme, arg);
25880
+ return v15 ? { left: v15 } : null;
25877
25881
  }
25878
25882
  case "inset": {
25879
25883
  const xMatch = /^x-(.+)$/.exec(arg);
25880
25884
  if (xMatch) {
25881
- const v15 = resolveSpacing(theme, xMatch[1]);
25882
- return v15 ? { left: v15, right: v15 } : null;
25885
+ const v16 = resolveSpacing(theme, xMatch[1]);
25886
+ return v16 ? { left: v16, right: v16 } : null;
25883
25887
  }
25884
25888
  const yMatch = /^y-(.+)$/.exec(arg);
25885
25889
  if (yMatch) {
25886
- const v15 = resolveSpacing(theme, yMatch[1]);
25887
- return v15 ? { top: v15, bottom: v15 } : null;
25890
+ const v16 = resolveSpacing(theme, yMatch[1]);
25891
+ return v16 ? { top: v16, bottom: v16 } : null;
25888
25892
  }
25889
- const v14 = resolveSpacing(theme, arg);
25890
- return v14 ? { top: v14, right: v14, bottom: v14, left: v14 } : null;
25893
+ const v15 = resolveSpacing(theme, arg);
25894
+ return v15 ? { top: v15, right: v15, bottom: v15, left: v15 } : null;
25891
25895
  }
25892
25896
  case "tracking": {
25893
25897
  const value = theme.letterSpacing?.[arg] ?? TRACKING_NAMED_TABLE[arg];
@@ -25911,8 +25915,8 @@ function classToCss(className, theme) {
25911
25915
  }
25912
25916
  if (sizeArg === "full") return { [property]: "100%" };
25913
25917
  if (sizeArg === "auto") return { [property]: "auto" };
25914
- const v14 = resolveSpacing(theme, sizeArg);
25915
- return v14 ? { [property]: v14 } : null;
25918
+ const v15 = resolveSpacing(theme, sizeArg);
25919
+ return v15 ? { [property]: v15 } : null;
25916
25920
  }
25917
25921
  case "max": {
25918
25922
  const m4 = /^([hw])-(.+)$/.exec(arg);
@@ -25925,8 +25929,8 @@ function classToCss(className, theme) {
25925
25929
  }
25926
25930
  if (sizeArg === "full") return { [property]: "100%" };
25927
25931
  if (sizeArg === "auto") return { [property]: "auto" };
25928
- const v14 = resolveSpacing(theme, sizeArg);
25929
- return v14 ? { [property]: v14 } : null;
25932
+ const v15 = resolveSpacing(theme, sizeArg);
25933
+ return v15 ? { [property]: v15 } : null;
25930
25934
  }
25931
25935
  case "flex": {
25932
25936
  if (arg in FLEX_SHORTHAND_TABLE) {
@@ -25977,17 +25981,17 @@ function classToCss(className, theme) {
25977
25981
  case "leading": {
25978
25982
  const named = theme.lineHeight?.[arg] ?? LEADING_NAMED_TABLE[arg];
25979
25983
  if (named) return { "line-height": named };
25980
- const v14 = resolveSpacing(theme, arg);
25981
- return v14 ? { "line-height": v14 } : null;
25984
+ const v15 = resolveSpacing(theme, arg);
25985
+ return v15 ? { "line-height": v15 } : null;
25982
25986
  }
25983
25987
  case "space": {
25984
25988
  const m4 = /^([xy])-(.+)$/.exec(arg);
25985
25989
  if (!m4) return null;
25986
25990
  const axis = m4[1];
25987
25991
  const sizeArg = m4[2];
25988
- const v14 = resolveSpacing(theme, sizeArg);
25989
- if (v14 === void 0) return null;
25990
- return axis === "x" ? { "column-gap": v14 } : { "row-gap": v14 };
25992
+ const v15 = resolveSpacing(theme, sizeArg);
25993
+ if (v15 === void 0) return null;
25994
+ return axis === "x" ? { "column-gap": v15 } : { "row-gap": v15 };
25991
25995
  }
25992
25996
  default:
25993
25997
  return null;
@@ -26507,9 +26511,9 @@ function payloadTypeForTsType(t3, genericNames) {
26507
26511
  let allStringLiterals = true;
26508
26512
  for (const m4 of members) {
26509
26513
  if (m4.isStringLiteral()) {
26510
- const v14 = m4.getLiteralValue();
26511
- if (v14 === "") continue;
26512
- literals.push(v14);
26514
+ const v15 = m4.getLiteralValue();
26515
+ if (v15 === "") continue;
26516
+ literals.push(v15);
26513
26517
  } else if (m4.getFlags() & TypeFlags.Undefined) {
26514
26518
  } else if (m4.getFlags() & TypeFlags.Null) {
26515
26519
  } else {
@@ -27189,8 +27193,8 @@ function declarationsFromRule(rule) {
27189
27193
  return out;
27190
27194
  }
27191
27195
  function mergeInto(target, source) {
27192
- for (const [k, v14] of Object.entries(source)) {
27193
- target[k] = v14;
27196
+ for (const [k, v15] of Object.entries(source)) {
27197
+ target[k] = v15;
27194
27198
  }
27195
27199
  }
27196
27200
  function parseCssClasses(absolutePath) {
@@ -27352,7 +27356,7 @@ function buildSourceFileResolverContext(sourceFile, emit) {
27352
27356
  for (const [cls, decls] of registry.classes) {
27353
27357
  const existing = globalCssClasses.get(cls);
27354
27358
  if (existing) {
27355
- for (const [k, v14] of Object.entries(decls)) existing[k] = v14;
27359
+ for (const [k, v15] of Object.entries(decls)) existing[k] = v15;
27356
27360
  } else {
27357
27361
  globalCssClasses.set(cls, { ...decls });
27358
27362
  }
@@ -27366,7 +27370,7 @@ function buildSourceFileResolverContext(sourceFile, emit) {
27366
27370
  for (const [pseudo, decls] of perPseudo) {
27367
27371
  const existingDecls = existing.get(pseudo);
27368
27372
  if (existingDecls) {
27369
- for (const [k, v14] of Object.entries(decls)) existingDecls[k] = v14;
27373
+ for (const [k, v15] of Object.entries(decls)) existingDecls[k] = v15;
27370
27374
  } else {
27371
27375
  existing.set(pseudo, { ...decls });
27372
27376
  }
@@ -27502,7 +27506,7 @@ function generateSlotPropName(slotName) {
27502
27506
  }
27503
27507
  function inferSlotName(ctx, hint) {
27504
27508
  const candidates = [hint.receiver, hint.leaf, hint.parentTag].filter(
27505
- (v14) => Boolean(v14 && v14.length > 0)
27509
+ (v15) => Boolean(v15 && v15.length > 0)
27506
27510
  );
27507
27511
  let base = candidates[0];
27508
27512
  if (!base) base = `slot${ctx.slots.length + 1}`;
@@ -28094,7 +28098,7 @@ function mergePropPseudoStyles(result, propName, perValuePseudo) {
28094
28098
  if (perValuePseudo.size === 0) return;
28095
28099
  const existing = result.propPseudoStyles.get(propName);
28096
28100
  if (existing) {
28097
- for (const [k, v14] of perValuePseudo) existing.set(k, v14);
28101
+ for (const [k, v15] of perValuePseudo) existing.set(k, v15);
28098
28102
  } else {
28099
28103
  result.propPseudoStyles.set(propName, new Map(perValuePseudo));
28100
28104
  }
@@ -28116,7 +28120,7 @@ function applySpanResolution(result, resolution) {
28116
28120
  if (resolution.kind === "variantOverrides") {
28117
28121
  const existing = result.variantOverrides.get(resolution.propName);
28118
28122
  if (existing) {
28119
- for (const [k, v14] of resolution.perValue) existing.set(k, v14);
28123
+ for (const [k, v15] of resolution.perValue) existing.set(k, v15);
28120
28124
  } else {
28121
28125
  result.variantOverrides.set(
28122
28126
  resolution.propName,
@@ -28143,7 +28147,7 @@ function applySpanResolution(result, resolution) {
28143
28147
  for (const group of resolution.groups) {
28144
28148
  const existing = result.variantOverrides.get(group.propName);
28145
28149
  if (existing) {
28146
- for (const [k, v14] of group.perValue) existing.set(k, v14);
28150
+ for (const [k, v15] of group.perValue) existing.set(k, v15);
28147
28151
  } else {
28148
28152
  result.variantOverrides.set(group.propName, new Map(group.perValue));
28149
28153
  }
@@ -28733,8 +28737,8 @@ function collectOverride(nodeTempId, path4, value, out) {
28733
28737
  return;
28734
28738
  }
28735
28739
  if (Array.isArray(value)) return;
28736
- for (const [k, v14] of Object.entries(value)) {
28737
- collectOverride(nodeTempId, `${path4}.${k}`, v14, out);
28740
+ for (const [k, v15] of Object.entries(value)) {
28741
+ collectOverride(nodeTempId, `${path4}.${k}`, v15, out);
28738
28742
  }
28739
28743
  }
28740
28744
  function flushTransitionFragments(node, fragments) {
@@ -29273,7 +29277,7 @@ function attachVariantPseudoStyles(props, variants, unresolved, propPseudoOverri
29273
29277
  }
29274
29278
  if (entries.length === 0) continue;
29275
29279
  let variant = variants.find(
29276
- (v14) => v14.selector?.propTempId === prop.tempId && v14.selector.value === value
29280
+ (v15) => v15.selector?.propTempId === prop.tempId && v15.selector.value === value
29277
29281
  );
29278
29282
  if (!variant) {
29279
29283
  variant = {
@@ -31797,6 +31801,18 @@ var init_user2 = __esm({
31797
31801
  pixelMode: z41.enum(PixelModeTypes).optional(),
31798
31802
  /** Whether to show pixel grid overlay */
31799
31803
  showPixelGrid: z41.boolean().optional(),
31804
+ /**
31805
+ * When this person finished or skipped the first-project Welcome, as epoch
31806
+ * milliseconds. Unset means the Welcome has not played. Per user, not per
31807
+ * organization: a person who joins a second workspace has seen it.
31808
+ */
31809
+ welcomeSeenAt: z41.number().optional(),
31810
+ /**
31811
+ * The new-project option this person picked last time, by option id
31812
+ * (`blank`, `import`, `studio`, `animate`, `app`). The return-visit menu
31813
+ * focuses it so Enter repeats it.
31814
+ */
31815
+ lastProjectStart: z41.string().optional(),
31800
31816
  /**
31801
31817
  * Whether editor feedback SOUNDS play (snap ticks, placement clicks, tool
31802
31818
  * arming). Unset means on — the absence of a choice is not a choice of
@@ -33354,6 +33370,51 @@ var init_dist = __esm({
33354
33370
  }
33355
33371
  });
33356
33372
 
33373
+ // ../opaque/dist/client/network/hydrationScheduler.js
33374
+ function yieldToMain() {
33375
+ const sched = globalThis.scheduler;
33376
+ if (sched?.yield)
33377
+ return sched.yield();
33378
+ if (typeof MessageChannel !== "undefined") {
33379
+ return new Promise((resolve6) => {
33380
+ const channel = new MessageChannel();
33381
+ channel.port1.onmessage = () => resolve6();
33382
+ channel.port2.postMessage(null);
33383
+ });
33384
+ }
33385
+ return new Promise((resolve6) => setTimeout(resolve6, 0));
33386
+ }
33387
+ function createHydrationSlicer(options = {}) {
33388
+ const now3 = options.now ?? (() => performance.now());
33389
+ let budgetMs = DEFAULT_BUDGET_MS;
33390
+ let sliceStart = now3();
33391
+ return {
33392
+ async tick() {
33393
+ const elapsed = now3() - sliceStart;
33394
+ if (elapsed < budgetMs)
33395
+ return;
33396
+ if (elapsed > FRAME_BUDGET_MS) {
33397
+ budgetMs = Math.max(MIN_BUDGET_MS, budgetMs - 2);
33398
+ } else if (elapsed < budgetMs + 2) {
33399
+ budgetMs = Math.min(MAX_BUDGET_MS, budgetMs + 1);
33400
+ }
33401
+ await yieldToMain();
33402
+ options.onResume?.();
33403
+ sliceStart = now3();
33404
+ }
33405
+ };
33406
+ }
33407
+ var FRAME_BUDGET_MS, DEFAULT_BUDGET_MS, MIN_BUDGET_MS, MAX_BUDGET_MS;
33408
+ var init_hydrationScheduler = __esm({
33409
+ "../opaque/dist/client/network/hydrationScheduler.js"() {
33410
+ "use strict";
33411
+ FRAME_BUDGET_MS = 16;
33412
+ DEFAULT_BUDGET_MS = 8;
33413
+ MIN_BUDGET_MS = 4;
33414
+ MAX_BUDGET_MS = 12;
33415
+ }
33416
+ });
33417
+
33357
33418
  // ../opaque/dist/staffMode.js
33358
33419
  var staffMode;
33359
33420
  var init_staffMode = __esm({
@@ -33761,6 +33822,7 @@ var init_utils2 = __esm({
33761
33822
  "../opaque/dist/client/files/utils.js"() {
33762
33823
  "use strict";
33763
33824
  init_imageHeader();
33825
+ init_files();
33764
33826
  }
33765
33827
  });
33766
33828
 
@@ -34661,11 +34723,18 @@ function determineActionScope(action, context) {
34661
34723
  };
34662
34724
  }
34663
34725
  if (!routingInfo.isBranched) {
34726
+ if (action.targetScope?.scope === routingInfo.scopeName) {
34727
+ return {
34728
+ success: true,
34729
+ scopeInfo: action.targetScope,
34730
+ scopeName: routingInfo.scopeName
34731
+ };
34732
+ }
34664
34733
  if (action.targetScope) {
34665
34734
  return {
34666
34735
  success: false,
34667
34736
  code: "INVALID_TARGET_SCOPE",
34668
- message: `Unbranched ${entityType} action cannot carry a branch target scope`
34737
+ message: `Unbranched ${entityType} action cannot target '${action.targetScope.scope}' scope`
34669
34738
  };
34670
34739
  }
34671
34740
  if (!context.scopeId) {
@@ -37140,7 +37209,7 @@ var init_LocalCache = __esm({
37140
37209
  if (!excluded || excluded.size === 0)
37141
37210
  return false;
37142
37211
  const provenance = this.actionIds.get(key);
37143
- const touched = provenance !== void 0 && Object.values(provenance).some((a5) => excluded.has(a5)) || Object.values(this.fieldVersions.get(key) ?? {}).some((versions) => versions.some((v14) => v14.actionId !== void 0 && excluded.has(v14.actionId)));
37212
+ const touched = provenance !== void 0 && Object.values(provenance).some((a5) => excluded.has(a5)) || Object.values(this.fieldVersions.get(key) ?? {}).some((versions) => versions.some((v15) => v15.actionId !== void 0 && excluded.has(v15.actionId)));
37144
37213
  if (!touched)
37145
37214
  return false;
37146
37215
  const state = this.syncedState.get(key);
@@ -37219,19 +37288,19 @@ var init_LocalCache = __esm({
37219
37288
  const versions = byField[field];
37220
37289
  if (!versions || versions.length === 0)
37221
37290
  continue;
37222
- if (!versions.some((v14) => v14.source === "event"))
37291
+ if (!versions.some((v15) => v15.source === "event"))
37223
37292
  continue;
37224
37293
  const prov = fieldActions?.[field];
37225
- const provKnown = prov !== void 0 && versions.some((v14) => v14.actionId === prov);
37294
+ const provKnown = prov !== void 0 && versions.some((v15) => v15.actionId === prov);
37226
37295
  if (!provKnown) {
37227
- const matchesKnown = versions.some((v14) => stableValueEqual(v14.value ?? null, incoming ?? null));
37296
+ const matchesKnown = versions.some((v15) => stableValueEqual(v15.value ?? null, incoming ?? null));
37228
37297
  if (!matchesKnown) {
37229
37298
  let maxHlc = null;
37230
- for (const v14 of versions) {
37231
- if (v14.actionId !== void 0 && excluded?.has(v14.actionId))
37299
+ for (const v15 of versions) {
37300
+ if (v15.actionId !== void 0 && excluded?.has(v15.actionId))
37232
37301
  continue;
37233
- if (this.compareNullableHlc(v14.hlc, maxHlc) > 0)
37234
- maxHlc = v14.hlc;
37302
+ if (this.compareNullableHlc(v15.hlc, maxHlc) > 0)
37303
+ maxHlc = v15.hlc;
37235
37304
  }
37236
37305
  versions.push({
37237
37306
  value: incoming,
@@ -37244,13 +37313,13 @@ var init_LocalCache = __esm({
37244
37313
  }
37245
37314
  }
37246
37315
  let winner = null;
37247
- for (const v14 of versions) {
37248
- const authoringActionId = v14.source === "event" ? v14.actionId : v14.source === "snapshot" ? v14.actionId ?? prov ?? null : null;
37316
+ for (const v15 of versions) {
37317
+ const authoringActionId = v15.source === "event" ? v15.actionId : v15.source === "snapshot" ? v15.actionId ?? prov ?? null : null;
37249
37318
  if (authoringActionId && excluded?.has(authoringActionId)) {
37250
37319
  continue;
37251
37320
  }
37252
- if (!winner || this.compareNullableHlc(v14.hlc, winner.hlc) >= 0) {
37253
- winner = v14;
37321
+ if (!winner || this.compareNullableHlc(v15.hlc, winner.hlc) >= 0) {
37322
+ winner = v15;
37254
37323
  }
37255
37324
  }
37256
37325
  if (winner) {
@@ -37854,7 +37923,7 @@ var init_LocalCache = __esm({
37854
37923
  materializeOneFieldVersion(targetKey, field, version, merge) {
37855
37924
  const byField = this.fieldVersions.get(targetKey) ?? {};
37856
37925
  const versions = byField[field] ?? [];
37857
- const dup = versions.some((v14) => version.eventId !== void 0 ? v14.eventId === version.eventId : v14.mergeId === merge.id && v14.hlc === version.hlc && v14.value === version.value);
37926
+ const dup = versions.some((v15) => version.eventId !== void 0 ? v15.eventId === version.eventId : v15.mergeId === merge.id && v15.hlc === version.hlc && v15.value === version.value);
37858
37927
  if (dup)
37859
37928
  return false;
37860
37929
  versions.push({ ...version, mergeId: merge.id });
@@ -37867,7 +37936,7 @@ var init_LocalCache = __esm({
37867
37936
  /** Delete-version analogue of {@link materializeOneFieldVersion}. */
37868
37937
  materializeOneDeleteVersion(targetKey, version, merge) {
37869
37938
  const versions = this.deleteVersions.get(targetKey) ?? [];
37870
- const dup = versions.some((v14) => version.eventId !== void 0 ? v14.eventId === version.eventId : v14.mergeId === merge.id && v14.hlc === version.hlc);
37939
+ const dup = versions.some((v15) => version.eventId !== void 0 ? v15.eventId === version.eventId : v15.mergeId === merge.id && v15.hlc === version.hlc);
37871
37940
  if (dup)
37872
37941
  return false;
37873
37942
  versions.push({ ...version, mergeId: merge.id });
@@ -37904,17 +37973,17 @@ var init_LocalCache = __esm({
37904
37973
  const state = { id: entityId };
37905
37974
  for (const [field, versions] of Object.entries(byField)) {
37906
37975
  let winner = null;
37907
- for (const v14 of versions) {
37976
+ for (const v15 of versions) {
37908
37977
  if (!winner) {
37909
- winner = v14;
37978
+ winner = v15;
37910
37979
  continue;
37911
37980
  }
37912
- const cmp = this.compareNullableHlc(v14.hlc, winner.hlc);
37981
+ const cmp = this.compareNullableHlc(v15.hlc, winner.hlc);
37913
37982
  if (cmp > 0) {
37914
- winner = v14;
37983
+ winner = v15;
37915
37984
  } else if (cmp === 0) {
37916
- if (winner.mergeId !== void 0 && v14.mergeId === void 0) {
37917
- winner = v14;
37985
+ if (winner.mergeId !== void 0 && v15.mergeId === void 0) {
37986
+ winner = v15;
37918
37987
  }
37919
37988
  }
37920
37989
  }
@@ -38121,7 +38190,7 @@ var init_LocalCache = __esm({
38121
38190
  if (parsed.branch !== branch)
38122
38191
  continue;
38123
38192
  for (const versions of Object.values(byField)) {
38124
- if (versions.some((v14) => v14.actionId === actionId)) {
38193
+ if (versions.some((v15) => v15.actionId === actionId)) {
38125
38194
  affected.add(parsed.entityId);
38126
38195
  break;
38127
38196
  }
@@ -38139,7 +38208,7 @@ var init_LocalCache = __esm({
38139
38208
  const parsed = this.parseKey(key);
38140
38209
  if (parsed.branch !== branch)
38141
38210
  continue;
38142
- if (versions.some((v14) => v14.actionId === actionId)) {
38211
+ if (versions.some((v15) => v15.actionId === actionId)) {
38143
38212
  affected.add(parsed.entityId);
38144
38213
  }
38145
38214
  }
@@ -38196,13 +38265,13 @@ var init_LocalCache = __esm({
38196
38265
  for (const [field, versions] of Object.entries(byField)) {
38197
38266
  let winner = null;
38198
38267
  let winnerActionId = null;
38199
- for (const v14 of versions) {
38200
- const authoringActionId = v14.source === "event" ? v14.actionId : v14.source === "snapshot" ? v14.actionId ?? fieldActions?.[field] ?? null : null;
38268
+ for (const v15 of versions) {
38269
+ const authoringActionId = v15.source === "event" ? v15.actionId : v15.source === "snapshot" ? v15.actionId ?? fieldActions?.[field] ?? null : null;
38201
38270
  if (authoringActionId && excluded?.has(authoringActionId)) {
38202
38271
  continue;
38203
38272
  }
38204
- if (!winner || this.compareNullableHlc(v14.hlc, winner.hlc) >= 0) {
38205
- winner = v14;
38273
+ if (!winner || this.compareNullableHlc(v15.hlc, winner.hlc) >= 0) {
38274
+ winner = v15;
38206
38275
  winnerActionId = authoringActionId ?? null;
38207
38276
  }
38208
38277
  }
@@ -38256,7 +38325,7 @@ var init_LocalCache = __esm({
38256
38325
  continue;
38257
38326
  let keyTouched = false;
38258
38327
  for (const [field, versions] of Object.entries(byField)) {
38259
- const filtered = versions.filter((v14) => v14.mergeId !== merge.id);
38328
+ const filtered = versions.filter((v15) => v15.mergeId !== merge.id);
38260
38329
  if (filtered.length !== versions.length) {
38261
38330
  keyTouched = true;
38262
38331
  if (filtered.length === 0)
@@ -38275,7 +38344,7 @@ var init_LocalCache = __esm({
38275
38344
  const parsed = this.parseKey(key);
38276
38345
  if (parsed.branch !== merge.targetBranchId)
38277
38346
  continue;
38278
- const filtered = versions.filter((v14) => v14.mergeId !== merge.id);
38347
+ const filtered = versions.filter((v15) => v15.mergeId !== merge.id);
38279
38348
  if (filtered.length !== versions.length) {
38280
38349
  if (filtered.length === 0)
38281
38350
  this.deleteVersions.delete(key);
@@ -39001,6 +39070,7 @@ var init_LocalCache = __esm({
39001
39070
  */
39002
39071
  evictEntityReadCaches(id, branch, field) {
39003
39072
  if (field && field.startsWith("_op.")) {
39073
+ this.childrenCache.clear();
39004
39074
  this.invalidateReadCaches();
39005
39075
  return;
39006
39076
  }
@@ -39146,13 +39216,13 @@ var init_LocalCache = __esm({
39146
39216
  return null;
39147
39217
  const mergeIdsByField = {};
39148
39218
  for (const [field, versions] of Object.entries(byField)) {
39149
- mergeIdsByField[field] = versions.map((v14) => v14.mergeId);
39219
+ mergeIdsByField[field] = versions.map((v15) => v15.mergeId);
39150
39220
  }
39151
39221
  const deletes = this.deleteVersions.get(key) ?? [];
39152
39222
  return {
39153
39223
  fields: Object.keys(byField),
39154
39224
  mergeIdsByField,
39155
- deleteMergeIds: deletes.map((v14) => v14.mergeId)
39225
+ deleteMergeIds: deletes.map((v15) => v15.mergeId)
39156
39226
  };
39157
39227
  }
39158
39228
  /**
@@ -39978,6 +40048,794 @@ var init_cache = __esm({
39978
40048
  }
39979
40049
  });
39980
40050
 
40051
+ // ../opaque/dist/client/storage/constants.js
40052
+ var DB_NAME, DB_VERSION, STORES, SYNC_SNAPSHOT_EVENT_ID_PREFIX;
40053
+ var init_constants2 = __esm({
40054
+ "../opaque/dist/client/storage/constants.js"() {
40055
+ "use strict";
40056
+ DB_NAME = "opaque";
40057
+ DB_VERSION = 12;
40058
+ STORES = {
40059
+ INGESTIONS: "ingestions",
40060
+ LATEST_STATE: "latestState",
40061
+ EVENTS: "events",
40062
+ TOMBSTONE_CURSORS: "tombstoneCursors",
40063
+ PENDING_UPLOADS: "pendingUploads",
40064
+ LOADED_SELECTIONS: "loadedSelections"
40065
+ };
40066
+ SYNC_SNAPSHOT_EVENT_ID_PREFIX = "sync_";
40067
+ }
40068
+ });
40069
+
40070
+ // ../opaque/dist/client/storage/migrations/v1.js
40071
+ var v1;
40072
+ var init_v1 = __esm({
40073
+ "../opaque/dist/client/storage/migrations/v1.js"() {
40074
+ "use strict";
40075
+ init_constants2();
40076
+ v1 = {
40077
+ version: 1,
40078
+ description: "Initial stores: localEvents + entityCache",
40079
+ up(db) {
40080
+ if (!db.objectStoreNames.contains(STORES.LATEST_STATE)) {
40081
+ const entityCache = db.createObjectStore(STORES.LATEST_STATE, {
40082
+ keyPath: "entityId"
40083
+ });
40084
+ entityCache.createIndex("by_scope", ["scope", "scopeId"]);
40085
+ entityCache.createIndex("by_type", "entityType");
40086
+ entityCache.createIndex("by_updated", "updatedAt");
40087
+ }
40088
+ if (!db.objectStoreNames.contains(STORES.EVENTS)) {
40089
+ const localEvents = db.createObjectStore(STORES.EVENTS, {
40090
+ keyPath: "id"
40091
+ });
40092
+ localEvents.createIndex("by_entity", "entityId");
40093
+ localEvents.createIndex("by_hlc", "hlc");
40094
+ localEvents.createIndex("by_action", "actionId");
40095
+ }
40096
+ }
40097
+ };
40098
+ }
40099
+ });
40100
+
40101
+ // ../opaque/dist/client/storage/migrations/v2.js
40102
+ var v2;
40103
+ var init_v2 = __esm({
40104
+ "../opaque/dist/client/storage/migrations/v2.js"() {
40105
+ "use strict";
40106
+ init_constants2();
40107
+ v2 = {
40108
+ version: 2,
40109
+ description: "Add by_synced index on localEvents",
40110
+ up(_db, tx) {
40111
+ if (!tx.objectStoreNames.contains(STORES.EVENTS))
40112
+ return;
40113
+ const localEvents = tx.objectStore(STORES.EVENTS);
40114
+ if (!localEvents.indexNames.contains("by_synced")) {
40115
+ localEvents.createIndex("by_synced", "synced");
40116
+ }
40117
+ }
40118
+ };
40119
+ }
40120
+ });
40121
+
40122
+ // ../opaque/dist/client/storage/migrations/v3.js
40123
+ var DEPRECATED_STORES, v3;
40124
+ var init_v3 = __esm({
40125
+ "../opaque/dist/client/storage/migrations/v3.js"() {
40126
+ "use strict";
40127
+ DEPRECATED_STORES = ["pendingActions", "knownState", "scopeMetadata"];
40128
+ v3 = {
40129
+ version: 3,
40130
+ description: "Drop deprecated pendingActions / knownState / scopeMetadata stores",
40131
+ up(db) {
40132
+ for (const name of DEPRECATED_STORES) {
40133
+ if (db.objectStoreNames.contains(name)) {
40134
+ db.deleteObjectStore(name);
40135
+ }
40136
+ }
40137
+ }
40138
+ };
40139
+ }
40140
+ });
40141
+
40142
+ // ../opaque/dist/client/storage/migrations/v4.js
40143
+ var v4;
40144
+ var init_v4 = __esm({
40145
+ "../opaque/dist/client/storage/migrations/v4.js"() {
40146
+ "use strict";
40147
+ init_constants2();
40148
+ v4 = {
40149
+ version: 4,
40150
+ description: "Add by_user_branch index on localEvents",
40151
+ up(_db, tx) {
40152
+ if (!tx.objectStoreNames.contains(STORES.EVENTS))
40153
+ return;
40154
+ const localEvents = tx.objectStore(STORES.EVENTS);
40155
+ if (!localEvents.indexNames.contains("by_user_branch")) {
40156
+ localEvents.createIndex("by_user_branch", ["userId", "branchId"]);
40157
+ }
40158
+ }
40159
+ };
40160
+ }
40161
+ });
40162
+
40163
+ // ../opaque/dist/client/storage/migrations/v5.js
40164
+ var v5;
40165
+ var init_v5 = __esm({
40166
+ "../opaque/dist/client/storage/migrations/v5.js"() {
40167
+ "use strict";
40168
+ init_constants2();
40169
+ v5 = {
40170
+ version: 5,
40171
+ description: "Branch-aware entityCache (composite key [entityId, branchId]); recreate both stores",
40172
+ up(db) {
40173
+ if (db.objectStoreNames.contains(STORES.LATEST_STATE)) {
40174
+ db.deleteObjectStore(STORES.LATEST_STATE);
40175
+ }
40176
+ if (db.objectStoreNames.contains(STORES.EVENTS)) {
40177
+ db.deleteObjectStore(STORES.EVENTS);
40178
+ }
40179
+ const entityCache = db.createObjectStore(STORES.LATEST_STATE, {
40180
+ keyPath: ["entityId", "branchId"]
40181
+ });
40182
+ entityCache.createIndex("by_entity", "entityId");
40183
+ entityCache.createIndex("by_scope", ["scope", "scopeId"]);
40184
+ entityCache.createIndex("by_type", "entityType");
40185
+ entityCache.createIndex("by_updated", "updatedAt");
40186
+ const localEvents = db.createObjectStore(STORES.EVENTS, {
40187
+ keyPath: "id"
40188
+ });
40189
+ localEvents.createIndex("by_entity", "entityId");
40190
+ localEvents.createIndex("by_hlc", "hlc");
40191
+ localEvents.createIndex("by_action", "actionId");
40192
+ localEvents.createIndex("by_synced", "synced");
40193
+ localEvents.createIndex("by_user_branch", ["userId", "branchId"]);
40194
+ }
40195
+ };
40196
+ }
40197
+ });
40198
+
40199
+ // ../opaque/dist/client/storage/migrations/v6.js
40200
+ var v6;
40201
+ var init_v6 = __esm({
40202
+ "../opaque/dist/client/storage/migrations/v6.js"() {
40203
+ "use strict";
40204
+ init_constants2();
40205
+ v6 = {
40206
+ version: 6,
40207
+ description: "tombstoneCursors store \u2014 per-scope hard-delete catch-up delivery cursor",
40208
+ up(db) {
40209
+ if (!db.objectStoreNames.contains(STORES.TOMBSTONE_CURSORS)) {
40210
+ db.createObjectStore(STORES.TOMBSTONE_CURSORS, { keyPath: "scopeId" });
40211
+ }
40212
+ }
40213
+ };
40214
+ }
40215
+ });
40216
+
40217
+ // ../opaque/dist/client/storage/migrations/v7.js
40218
+ var v7;
40219
+ var init_v7 = __esm({
40220
+ "../opaque/dist/client/storage/migrations/v7.js"() {
40221
+ "use strict";
40222
+ init_constants2();
40223
+ v7 = {
40224
+ version: 7,
40225
+ description: "pendingUploads store \u2014 durable offline queue of file bytes awaiting upload",
40226
+ up(db) {
40227
+ if (!db.objectStoreNames.contains(STORES.PENDING_UPLOADS)) {
40228
+ const store = db.createObjectStore(STORES.PENDING_UPLOADS, {
40229
+ keyPath: "id"
40230
+ });
40231
+ store.createIndex("by_status", "status", { unique: false });
40232
+ }
40233
+ }
40234
+ };
40235
+ }
40236
+ });
40237
+
40238
+ // ../opaque/dist/client/storage/migrations/v8.js
40239
+ var LEGACY_STORE_NAMES, v8;
40240
+ var init_v8 = __esm({
40241
+ "../opaque/dist/client/storage/migrations/v8.js"() {
40242
+ "use strict";
40243
+ init_constants2();
40244
+ LEGACY_STORE_NAMES = ["localEvents", "entityCache"];
40245
+ v8 = {
40246
+ version: 8,
40247
+ description: "Rename core stores to canonical server names: localEvents\u2192events, entityCache\u2192latestState",
40248
+ up(db) {
40249
+ for (const legacy of LEGACY_STORE_NAMES) {
40250
+ if (db.objectStoreNames.contains(legacy)) {
40251
+ db.deleteObjectStore(legacy);
40252
+ }
40253
+ }
40254
+ if (!db.objectStoreNames.contains(STORES.LATEST_STATE)) {
40255
+ const latestState = db.createObjectStore(STORES.LATEST_STATE, {
40256
+ keyPath: ["entityId", "branchId"]
40257
+ });
40258
+ latestState.createIndex("by_entity", "entityId");
40259
+ latestState.createIndex("by_scope", ["scope", "scopeId"]);
40260
+ latestState.createIndex("by_type", "entityType");
40261
+ latestState.createIndex("by_updated", "updatedAt");
40262
+ }
40263
+ if (!db.objectStoreNames.contains(STORES.EVENTS)) {
40264
+ const events = db.createObjectStore(STORES.EVENTS, { keyPath: "id" });
40265
+ events.createIndex("by_entity", "entityId");
40266
+ events.createIndex("by_hlc", "hlc");
40267
+ events.createIndex("by_action", "actionId");
40268
+ events.createIndex("by_synced", "synced");
40269
+ events.createIndex("by_user_branch", ["userId", "branchId"]);
40270
+ }
40271
+ }
40272
+ };
40273
+ }
40274
+ });
40275
+
40276
+ // ../opaque/dist/client/storage/migrations/v9.js
40277
+ var v9;
40278
+ var init_v9 = __esm({
40279
+ "../opaque/dist/client/storage/migrations/v9.js"() {
40280
+ "use strict";
40281
+ init_constants2();
40282
+ v9 = {
40283
+ version: 9,
40284
+ description: "loadedSelections store \u2014 server-confirmed (entity, select, branch) load keys",
40285
+ up(db) {
40286
+ if (!db.objectStoreNames.contains(STORES.LOADED_SELECTIONS)) {
40287
+ db.createObjectStore(STORES.LOADED_SELECTIONS, { keyPath: "key" });
40288
+ }
40289
+ }
40290
+ };
40291
+ }
40292
+ });
40293
+
40294
+ // ../opaque/dist/client/storage/migrations/v10.js
40295
+ var LEGACY_TEXT_ENTITY_TYPES, v10;
40296
+ var init_v10 = __esm({
40297
+ "../opaque/dist/client/storage/migrations/v10.js"() {
40298
+ "use strict";
40299
+ init_constants2();
40300
+ LEGACY_TEXT_ENTITY_TYPES = ["opRichTextRun", "opRichTextCursor"];
40301
+ v10 = {
40302
+ version: 10,
40303
+ description: "Drop cached latestState records under the pre-rename text entity types (opRichTextRun/opRichTextCursor) and the loadedSelections claims that describe them; they re-sync under opTextRun/opTextCursor",
40304
+ up(db, tx) {
40305
+ if (!db.objectStoreNames.contains(STORES.LATEST_STATE))
40306
+ return;
40307
+ const store = tx.objectStore(STORES.LATEST_STATE);
40308
+ if (!store.indexNames.contains("by_type"))
40309
+ return;
40310
+ const swallow = (request3) => {
40311
+ request3.onerror = (event) => {
40312
+ event.preventDefault();
40313
+ event.stopPropagation();
40314
+ };
40315
+ };
40316
+ let purged = false;
40317
+ const clearLoadedSelectionsOnce = () => {
40318
+ if (purged)
40319
+ return;
40320
+ purged = true;
40321
+ if (!db.objectStoreNames.contains(STORES.LOADED_SELECTIONS))
40322
+ return;
40323
+ swallow(tx.objectStore(STORES.LOADED_SELECTIONS).clear());
40324
+ };
40325
+ const byType = store.index("by_type");
40326
+ for (const legacyType of LEGACY_TEXT_ENTITY_TYPES) {
40327
+ const cursorRequest = byType.openCursor(IDBKeyRange.only(legacyType));
40328
+ cursorRequest.onsuccess = () => {
40329
+ const cursor = cursorRequest.result;
40330
+ if (!cursor)
40331
+ return;
40332
+ swallow(cursor.delete());
40333
+ clearLoadedSelectionsOnce();
40334
+ cursor.continue();
40335
+ };
40336
+ swallow(cursorRequest);
40337
+ }
40338
+ }
40339
+ };
40340
+ }
40341
+ });
40342
+
40343
+ // ../opaque/dist/client/storage/migrations/v11.js
40344
+ var v11;
40345
+ var init_v11 = __esm({
40346
+ "../opaque/dist/client/storage/migrations/v11.js"() {
40347
+ "use strict";
40348
+ init_constants2();
40349
+ v11 = {
40350
+ version: 11,
40351
+ description: "events by_branch_type index \u2014 branch-wide exclusion seed",
40352
+ up(_db, tx) {
40353
+ if (!tx.objectStoreNames.contains(STORES.EVENTS))
40354
+ return;
40355
+ const events = tx.objectStore(STORES.EVENTS);
40356
+ if (!events.indexNames.contains("by_branch_type")) {
40357
+ events.createIndex("by_branch_type", ["branchId", "type"]);
40358
+ }
40359
+ }
40360
+ };
40361
+ }
40362
+ });
40363
+
40364
+ // ../opaque/dist/client/storage/migrations/v12.js
40365
+ var v12;
40366
+ var init_v12 = __esm({
40367
+ "../opaque/dist/client/storage/migrations/v12.js"() {
40368
+ "use strict";
40369
+ init_constants2();
40370
+ v12 = {
40371
+ version: 12,
40372
+ description: "Local ingestion receipts",
40373
+ up(db) {
40374
+ db.createObjectStore(STORES.INGESTIONS, { keyPath: "id" });
40375
+ }
40376
+ };
40377
+ }
40378
+ });
40379
+
40380
+ // ../opaque/dist/client/storage/migrations/index.js
40381
+ function runMigrations(db, tx, oldVersion) {
40382
+ for (const migration of MIGRATIONS) {
40383
+ if (migration.version > oldVersion) {
40384
+ migration.up(db, tx, oldVersion);
40385
+ }
40386
+ }
40387
+ }
40388
+ var MIGRATIONS;
40389
+ var init_migrations = __esm({
40390
+ "../opaque/dist/client/storage/migrations/index.js"() {
40391
+ "use strict";
40392
+ init_v1();
40393
+ init_v2();
40394
+ init_v3();
40395
+ init_v4();
40396
+ init_v5();
40397
+ init_v6();
40398
+ init_v7();
40399
+ init_v8();
40400
+ init_v9();
40401
+ init_v10();
40402
+ init_v11();
40403
+ init_v12();
40404
+ MIGRATIONS = [
40405
+ v1,
40406
+ v2,
40407
+ v3,
40408
+ v4,
40409
+ v5,
40410
+ v6,
40411
+ v7,
40412
+ v8,
40413
+ v9,
40414
+ v10,
40415
+ v11,
40416
+ v12
40417
+ ].sort((a5, b4) => a5.version - b4.version);
40418
+ }
40419
+ });
40420
+
40421
+ // ../opaque/dist/client/storage/schema.js
40422
+ function openDatabase(timeoutMs = OPEN_DATABASE_TIMEOUT_MS, databaseName = DB_NAME) {
40423
+ return new Promise((resolve6, reject) => {
40424
+ const request3 = indexedDB.open(databaseName, DB_VERSION);
40425
+ let settled = false;
40426
+ const timer = setTimeout(() => {
40427
+ if (settled)
40428
+ return;
40429
+ settled = true;
40430
+ reject(new IndexedDbOpenBlockedError("timeout"));
40431
+ }, timeoutMs);
40432
+ const settle = (fn2) => {
40433
+ if (settled)
40434
+ return;
40435
+ settled = true;
40436
+ clearTimeout(timer);
40437
+ fn2();
40438
+ };
40439
+ request3.onblocked = () => {
40440
+ settle(() => reject(new IndexedDbOpenBlockedError("blocked")));
40441
+ };
40442
+ request3.onerror = () => settle(() => reject(request3.error));
40443
+ request3.onsuccess = () => {
40444
+ const db = request3.result;
40445
+ if (settled) {
40446
+ db.close();
40447
+ return;
40448
+ }
40449
+ db.onversionchange = () => db.close();
40450
+ settle(() => resolve6(db));
40451
+ };
40452
+ request3.onupgradeneeded = (event) => {
40453
+ const db = request3.result;
40454
+ const tx = request3.transaction;
40455
+ if (!tx)
40456
+ return;
40457
+ runMigrations(db, tx, event.oldVersion);
40458
+ };
40459
+ });
40460
+ }
40461
+ function idbGet(db, storeName, key) {
40462
+ return new Promise((resolve6, reject) => {
40463
+ const tx = db.transaction(storeName, "readonly");
40464
+ const store = tx.objectStore(storeName);
40465
+ const request3 = store.get(key);
40466
+ request3.onsuccess = () => resolve6(request3.result);
40467
+ request3.onerror = () => reject(request3.error);
40468
+ });
40469
+ }
40470
+ function idbPut(db, storeName, value) {
40471
+ return new Promise((resolve6, reject) => {
40472
+ const tx = db.transaction(storeName, "readwrite");
40473
+ const store = tx.objectStore(storeName);
40474
+ const request3 = store.put(value);
40475
+ request3.onsuccess = () => resolve6(request3.result);
40476
+ request3.onerror = () => reject(request3.error);
40477
+ });
40478
+ }
40479
+ function idbDelete(db, storeName, key) {
40480
+ return new Promise((resolve6, reject) => {
40481
+ const tx = db.transaction(storeName, "readwrite");
40482
+ const store = tx.objectStore(storeName);
40483
+ const request3 = store.delete(key);
40484
+ request3.onsuccess = () => resolve6();
40485
+ request3.onerror = () => reject(request3.error);
40486
+ });
40487
+ }
40488
+ function idbGetAll(db, storeName) {
40489
+ return new Promise((resolve6, reject) => {
40490
+ const tx = db.transaction(storeName, "readonly");
40491
+ const store = tx.objectStore(storeName);
40492
+ const request3 = store.getAll();
40493
+ request3.onsuccess = () => resolve6(request3.result);
40494
+ request3.onerror = () => reject(request3.error);
40495
+ });
40496
+ }
40497
+ function idbGetAllByIndex(db, storeName, indexName, key) {
40498
+ return new Promise((resolve6, reject) => {
40499
+ const tx = db.transaction(storeName, "readonly");
40500
+ const store = tx.objectStore(storeName);
40501
+ const index = store.index(indexName);
40502
+ const request3 = index.getAll(key);
40503
+ request3.onsuccess = () => resolve6(request3.result);
40504
+ request3.onerror = () => reject(request3.error);
40505
+ });
40506
+ }
40507
+ function idbGetEntityLastHlc(db, entityId) {
40508
+ return new Promise((resolve6, reject) => {
40509
+ const tx = db.transaction(STORES.LATEST_STATE, "readonly");
40510
+ const index = tx.objectStore(STORES.LATEST_STATE).index("by_entity");
40511
+ const request3 = index.getAll(entityId);
40512
+ request3.onsuccess = () => {
40513
+ const records2 = request3.result;
40514
+ if (records2.length === 0)
40515
+ return resolve6(null);
40516
+ let max = records2[0].lastHlc;
40517
+ for (const r of records2)
40518
+ if (r.lastHlc > max)
40519
+ max = r.lastHlc;
40520
+ resolve6(max);
40521
+ };
40522
+ request3.onerror = () => reject(request3.error);
40523
+ });
40524
+ }
40525
+ function idbPutMany(db, storeName, values) {
40526
+ return new Promise((resolve6, reject) => {
40527
+ const tx = db.transaction(storeName, "readwrite");
40528
+ const store = tx.objectStore(storeName);
40529
+ tx.oncomplete = () => resolve6();
40530
+ tx.onerror = () => reject(tx.error);
40531
+ for (const value of values) {
40532
+ store.put(value);
40533
+ }
40534
+ });
40535
+ }
40536
+ function idbDeleteMany(db, storeName, keys) {
40537
+ return new Promise((resolve6, reject) => {
40538
+ const tx = db.transaction(storeName, "readwrite");
40539
+ const store = tx.objectStore(storeName);
40540
+ tx.oncomplete = () => resolve6();
40541
+ tx.onerror = () => reject(tx.error);
40542
+ for (const key of keys) {
40543
+ store.delete(key);
40544
+ }
40545
+ });
40546
+ }
40547
+ function idbClear(db, storeName) {
40548
+ return new Promise((resolve6, reject) => {
40549
+ const tx = db.transaction(storeName, "readwrite");
40550
+ const store = tx.objectStore(storeName);
40551
+ const request3 = store.clear();
40552
+ request3.onsuccess = () => resolve6();
40553
+ request3.onerror = () => reject(request3.error);
40554
+ });
40555
+ }
40556
+ var OPEN_DATABASE_TIMEOUT_MS, IndexedDbOpenBlockedError;
40557
+ var init_schema4 = __esm({
40558
+ "../opaque/dist/client/storage/schema.js"() {
40559
+ "use strict";
40560
+ init_migrations();
40561
+ init_constants2();
40562
+ init_constants2();
40563
+ OPEN_DATABASE_TIMEOUT_MS = 5e3;
40564
+ IndexedDbOpenBlockedError = class extends Error {
40565
+ constructor(reason) {
40566
+ super(reason === "blocked" ? "IndexedDB open blocked: another tab holds an older version of the database" : `IndexedDB open did not settle within ${OPEN_DATABASE_TIMEOUT_MS}ms`);
40567
+ this.name = "IndexedDbOpenBlockedError";
40568
+ }
40569
+ };
40570
+ }
40571
+ });
40572
+
40573
+ // ../opaque/dist/client/storage/utils.js
40574
+ function getMaxHlc(events) {
40575
+ if (events.length === 0)
40576
+ return "0";
40577
+ return events.reduce((max, e2) => HLC.compare(e2.hlc, max) > 0 ? e2.hlc : max, "0");
40578
+ }
40579
+ function eventBranchId(event) {
40580
+ return event.branchId ?? void 0;
40581
+ }
40582
+ function groupEventsByBranch(events) {
40583
+ const groups = /* @__PURE__ */ new Map();
40584
+ for (const event of events) {
40585
+ const key = eventBranchId(event) ?? "";
40586
+ const bucket = groups.get(key);
40587
+ if (bucket)
40588
+ bucket.push(event);
40589
+ else
40590
+ groups.set(key, [event]);
40591
+ }
40592
+ return groups;
40593
+ }
40594
+ function applyLWWEvents(events, existingState, existingLastHlc, existingFieldHlcs, existingFieldActions) {
40595
+ const state = existingState ? { ...existingState } : {};
40596
+ const fieldHlcs = existingFieldHlcs ? { ...existingFieldHlcs } : {};
40597
+ if (!existingFieldHlcs && existingState && existingLastHlc) {
40598
+ for (const key of Object.keys(existingState)) {
40599
+ fieldHlcs[key] = existingLastHlc;
40600
+ }
40601
+ }
40602
+ const fieldActions = existingFieldActions ? { ...existingFieldActions } : {};
40603
+ const sortedEvents = sortByHlc(events, "asc");
40604
+ for (const event of sortedEvents) {
40605
+ if (isTombstoneEvent(event)) {
40606
+ state.deletedAt = event.hlc;
40607
+ fieldHlcs.deletedAt = event.hlc;
40608
+ fieldActions.deletedAt = event.actionId;
40609
+ continue;
40610
+ }
40611
+ if (event.type === "upsert" && event.field) {
40612
+ const currentHlc = fieldHlcs[event.field];
40613
+ if (!currentHlc || HLC.compare(event.hlc, currentHlc) > 0) {
40614
+ state[event.field] = event.value;
40615
+ fieldHlcs[event.field] = event.hlc;
40616
+ fieldActions[event.field] = event.actionId;
40617
+ }
40618
+ }
40619
+ }
40620
+ if (events.length > 0) {
40621
+ const firstEvent = events[0];
40622
+ state.id = firstEvent.entityId;
40623
+ state.modelName = firstEvent.entityType;
40624
+ }
40625
+ const eventsMaxHlc = getMaxHlc(events);
40626
+ const lastHlc = existingLastHlc && HLC.compare(existingLastHlc, eventsMaxHlc) > 0 ? existingLastHlc : eventsMaxHlc;
40627
+ return { state, fieldHlcs, fieldActions, lastHlc };
40628
+ }
40629
+ var init_utils4 = __esm({
40630
+ "../opaque/dist/client/storage/utils.js"() {
40631
+ "use strict";
40632
+ init_types2();
40633
+ init_hlc();
40634
+ }
40635
+ });
40636
+
40637
+ // ../opaque/dist/client/storage/indexeddb/utils.js
40638
+ function foldLatestStateRecords(records2) {
40639
+ if (records2.length === 0)
40640
+ return null;
40641
+ if (records2.length === 1) {
40642
+ return {
40643
+ state: records2[0].state,
40644
+ ...records2[0].fieldActions !== void 0 && {
40645
+ fieldActions: records2[0].fieldActions
40646
+ },
40647
+ ...records2[0].fieldHlcs !== void 0 && {
40648
+ fieldHlcs: records2[0].fieldHlcs
40649
+ }
40650
+ };
40651
+ }
40652
+ const ordered = [...records2].sort((a5, b4) => HLC.compare(a5.lastHlc, b4.lastHlc));
40653
+ const merged = {};
40654
+ const mergedActions = {};
40655
+ let anyActions = false;
40656
+ const mergedHlcs = {};
40657
+ let anyHlcs = false;
40658
+ for (const record of ordered) {
40659
+ for (const [field, value] of Object.entries(record.state)) {
40660
+ merged[field] = value;
40661
+ const action = record.fieldActions?.[field];
40662
+ if (action !== void 0) {
40663
+ mergedActions[field] = action;
40664
+ anyActions = true;
40665
+ } else {
40666
+ delete mergedActions[field];
40667
+ }
40668
+ const hlc = record.fieldHlcs?.[field];
40669
+ if (hlc !== void 0) {
40670
+ mergedHlcs[field] = hlc;
40671
+ anyHlcs = true;
40672
+ } else {
40673
+ delete mergedHlcs[field];
40674
+ }
40675
+ }
40676
+ }
40677
+ return {
40678
+ state: merged,
40679
+ ...anyActions && { fieldActions: mergedActions },
40680
+ ...anyHlcs && { fieldHlcs: mergedHlcs }
40681
+ };
40682
+ }
40683
+ function groupBy(items, keyFn) {
40684
+ const groups = {};
40685
+ for (const item of items) {
40686
+ const key = keyFn(item);
40687
+ if (!groups[key]) {
40688
+ groups[key] = [];
40689
+ }
40690
+ groups[key].push(item);
40691
+ }
40692
+ return groups;
40693
+ }
40694
+ function eventToRecord(event, synced = 1, actionTargetScope, actionHardDeleteScope) {
40695
+ return {
40696
+ id: event.id,
40697
+ entityId: event.entityId,
40698
+ entityType: event.entityType,
40699
+ field: event.field,
40700
+ value: event.value,
40701
+ previousValue: event.previousValue,
40702
+ hlc: event.hlc,
40703
+ clientId: event.clientId,
40704
+ userId: event.userId,
40705
+ actionId: event.actionId,
40706
+ actionLabel: event.actionLabel,
40707
+ type: event.type,
40708
+ mergeId: event.mergeId,
40709
+ targetId: event.targetId,
40710
+ branchId: event.branchId,
40711
+ parentScopeId: event.parentScopeId,
40712
+ actionTargetScope,
40713
+ actionHardDeleteScope,
40714
+ synced
40715
+ };
40716
+ }
40717
+ function recordToEvent(record) {
40718
+ return {
40719
+ id: record.id,
40720
+ type: record.type,
40721
+ entityId: record.entityId,
40722
+ entityType: record.entityType,
40723
+ field: record.field,
40724
+ value: record.value,
40725
+ previousValue: record.previousValue,
40726
+ hlc: record.hlc,
40727
+ clientId: record.clientId,
40728
+ userId: record.userId,
40729
+ actionId: record.actionId,
40730
+ actionLabel: record.actionLabel,
40731
+ mergeId: record.mergeId,
40732
+ targetId: record.targetId,
40733
+ branchId: record.branchId,
40734
+ parentScopeId: record.parentScopeId
40735
+ };
40736
+ }
40737
+ var init_utils5 = __esm({
40738
+ "../opaque/dist/client/storage/indexeddb/utils.js"() {
40739
+ "use strict";
40740
+ init_hlc();
40741
+ }
40742
+ });
40743
+
40744
+ // ../opaque/dist/client/storage/indexeddb/ingestion.js
40745
+ function prepareBranchIngestion(actions, receipt) {
40746
+ const started = performance.now();
40747
+ if (!actions.length || receipt.actionIds.length !== actions.length || actions.some((action, index) => action.id !== receipt.actionIds[index])) {
40748
+ throw new Error("The receipt must identify every ingestion action in order.");
40749
+ }
40750
+ const groups = /* @__PURE__ */ new Map();
40751
+ const eventRecords = actions.flatMap((action) => {
40752
+ if (!action.events.length)
40753
+ throw new Error("An ingestion action cannot be empty.");
40754
+ if (action.targetScope?.scope !== "branch" || action.targetScope.scopeId !== receipt.scopeId || action.targetScope.branchId !== receipt.branchId) {
40755
+ throw new Error("The ingestion action has a different target scope.");
40756
+ }
40757
+ for (const event of action.events) {
40758
+ if (event.actionId !== action.id)
40759
+ throw new Error("The event has a different action identity.");
40760
+ if (event.type !== "upsert" || event.branchId !== receipt.branchId) {
40761
+ throw new Error("Ingestion requires branch-scoped upserts.");
40762
+ }
40763
+ const group = groups.get(event.entityId) ?? [];
40764
+ group.push(event);
40765
+ groups.set(event.entityId, group);
40766
+ }
40767
+ return action.events.map((event) => eventToRecord(event, 0, action.targetScope));
40768
+ });
40769
+ const states = [];
40770
+ for (const [entityId, events] of groups) {
40771
+ if (!events.some((event) => event.field === "createdAt")) {
40772
+ throw new Error("Ingestion only accepts fresh entities.");
40773
+ }
40774
+ if (events.some((event) => event.entityType !== events[0].entityType))
40775
+ throw new Error("An entity cannot have multiple models.");
40776
+ const fields = new Set(events.map((event) => event.field));
40777
+ if (fields.size !== events.length)
40778
+ throw new Error("Duplicate initial field event.");
40779
+ states.push({
40780
+ entityId,
40781
+ entityType: events[0].entityType,
40782
+ branchId: receipt.branchId,
40783
+ scopeId: receipt.branchId,
40784
+ scope: CollabScope.branch,
40785
+ ...applyLWWEvents(events),
40786
+ updatedAt: Date.now()
40787
+ });
40788
+ }
40789
+ return { eventRecords, states, prepareMs: performance.now() - started };
40790
+ }
40791
+ async function stageBranchIngestion(db, actions, receipt, prepared = prepareBranchIngestion(actions, receipt)) {
40792
+ const { eventRecords, states, prepareMs } = prepared;
40793
+ const transactionStarted = performance.now();
40794
+ await new Promise((resolve6, reject) => {
40795
+ const tx = db.transaction([STORES.EVENTS, STORES.LATEST_STATE, STORES.INGESTIONS], "readwrite");
40796
+ tx.oncomplete = () => resolve6();
40797
+ tx.onabort = () => reject(tx.error ?? new Error("Ingestion transaction aborted."));
40798
+ tx.onerror = () => {
40799
+ };
40800
+ const eventsStore = tx.objectStore(STORES.EVENTS);
40801
+ const stateStore = tx.objectStore(STORES.LATEST_STATE);
40802
+ const receiptStore = tx.objectStore(STORES.INGESTIONS);
40803
+ const stateEnd = eventRecords.length + states.length;
40804
+ const total = stateEnd + 1;
40805
+ let cursor = 0;
40806
+ const enqueueBatch = () => {
40807
+ try {
40808
+ let lastRequest;
40809
+ const end = Math.min(cursor + INGESTION_REQUEST_BATCH_SIZE, total);
40810
+ for (; cursor < end; cursor++) {
40811
+ lastRequest = cursor < eventRecords.length ? eventsStore.add(eventRecords[cursor]) : cursor < stateEnd ? stateStore.add(states[cursor - eventRecords.length]) : receiptStore.add(receipt);
40812
+ }
40813
+ if (cursor < total && lastRequest)
40814
+ lastRequest.onsuccess = enqueueBatch;
40815
+ } catch (error) {
40816
+ tx.abort();
40817
+ reject(error);
40818
+ }
40819
+ };
40820
+ enqueueBatch();
40821
+ });
40822
+ return {
40823
+ prepareMs,
40824
+ transactionMs: performance.now() - transactionStarted
40825
+ };
40826
+ }
40827
+ var INGESTION_REQUEST_BATCH_SIZE;
40828
+ var init_ingestion = __esm({
40829
+ "../opaque/dist/client/storage/indexeddb/ingestion.js"() {
40830
+ "use strict";
40831
+ init_types2();
40832
+ init_schema4();
40833
+ init_utils4();
40834
+ init_utils5();
40835
+ INGESTION_REQUEST_BATCH_SIZE = 128;
40836
+ }
40837
+ });
40838
+
39981
40839
  // ../opaque/dist/client/actor.js
39982
40840
  function actorFields(source) {
39983
40841
  const actorId = source.getActorId?.() ?? null;
@@ -40011,6 +40869,7 @@ function flushActionPack() {
40011
40869
  label: pack.label
40012
40870
  });
40013
40871
  pack.label = void 0;
40872
+ pack.shipped.push({ actionId, eventCount: events.length });
40014
40873
  }
40015
40874
  function joinActionPackStandalone(events, client2) {
40016
40875
  const pack = currentPack;
@@ -40270,9 +41129,9 @@ var require_bin = __commonJS({
40270
41129
  const gkm = b4[k - 1 - Z4 * Math.floor((k - 1) / Z4)];
40271
41130
  const gkp = b4[k + 1 - Z4 * Math.floor((k + 1) / Z4)];
40272
41131
  const u3 = k === -h4 || k !== h4 && gkm < gkp ? gkp : gkm + 1;
40273
- const v14 = u3 - k;
41132
+ const v15 = u3 - k;
40274
41133
  let x6 = u3;
40275
- let y7 = v14;
41134
+ let y7 = v15;
40276
41135
  while (x6 < N6 && y7 < M4 && eq(i3 + x6, j3 + y7))
40277
41136
  x6++, y7++;
40278
41137
  b4[k - Z4 * Math.floor(k / Z4)] = x6;
@@ -40283,7 +41142,7 @@ var require_bin = __commonJS({
40283
41142
  stack_base[stack_top++] = j3 + y7;
40284
41143
  stack_base[stack_top++] = M4 - y7;
40285
41144
  N6 = u3;
40286
- M4 = v14;
41145
+ M4 = v15;
40287
41146
  Z4 = 2 * (Math.min(N6, M4) + 1);
40288
41147
  continue Z_block;
40289
41148
  } else
@@ -40294,9 +41153,9 @@ var require_bin = __commonJS({
40294
41153
  const pkm = b4[Z4 + k - 1 - Z4 * Math.floor((k - 1) / Z4)];
40295
41154
  const pkp = b4[Z4 + k + 1 - Z4 * Math.floor((k + 1) / Z4)];
40296
41155
  const u3 = k === -h4 || k !== h4 && pkm < pkp ? pkp : pkm + 1;
40297
- const v14 = u3 - k;
41156
+ const v15 = u3 - k;
40298
41157
  let x6 = u3;
40299
- let y7 = v14;
41158
+ let y7 = v15;
40300
41159
  while (x6 < N6 && y7 < M4 && eq(offsetx - x6, offsety - y7))
40301
41160
  x6++, y7++;
40302
41161
  b4[Z4 + k - Z4 * Math.floor(k / Z4)] = x6;
@@ -40304,8 +41163,8 @@ var require_bin = __commonJS({
40304
41163
  if (h4 > 0 || x6 !== u3) {
40305
41164
  stack_base[stack_top++] = i3 + N6 - u3;
40306
41165
  stack_base[stack_top++] = u3;
40307
- stack_base[stack_top++] = j3 + M4 - v14;
40308
- stack_base[stack_top++] = v14;
41166
+ stack_base[stack_top++] = j3 + M4 - v15;
41167
+ stack_base[stack_top++] = v15;
40309
41168
  N6 = N6 - x6;
40310
41169
  M4 = M4 - y7;
40311
41170
  Z4 = 2 * (Math.min(N6, M4) + 1);
@@ -40447,16 +41306,16 @@ var require_bin = __commonJS({
40447
41306
  }
40448
41307
  return rec;
40449
41308
  }
40450
- const v14 = rec.value;
40451
- const sx = v14[0];
40452
- const ex = v14[1];
40453
- const ey = v14[3];
41309
+ const v15 = rec.value;
41310
+ const sx = v15[0];
41311
+ const ex = v15[1];
41312
+ const ey = v15[3];
40454
41313
  const { i: i3, j: j3 } = this;
40455
41314
  if (i3 !== sx) {
40456
- v14.length--;
40457
- v14[0] = i3;
40458
- v14[1] = j3;
40459
- v14[2] = sx - i3;
41315
+ v15.length--;
41316
+ v15[0] = i3;
41317
+ v15[1] = j3;
41318
+ v15[2] = sx - i3;
40460
41319
  }
40461
41320
  this.i = ex;
40462
41321
  this.j = ey;
@@ -40469,9 +41328,9 @@ var require_bin = __commonJS({
40469
41328
  exports.lcs = lcs;
40470
41329
  function* calcPatch2(xs, ys, eq) {
40471
41330
  const slice = ArrayBuffer.isView(xs) ? Uint8Array.prototype.subarray : xs.slice;
40472
- for (const v14 of diff3(xs, ys, eq)) {
40473
- v14[2] = slice.call(ys, v14[2], v14[3]);
40474
- yield v14;
41331
+ for (const v15 of diff3(xs, ys, eq)) {
41332
+ v15[2] = slice.call(ys, v15[2], v15[3]);
41333
+ yield v15;
40475
41334
  }
40476
41335
  }
40477
41336
  exports.calcPatch = calcPatch2;
@@ -43472,509 +44331,6 @@ var init_gap_check = __esm({
43472
44331
  }
43473
44332
  });
43474
44333
 
43475
- // ../opaque/dist/client/storage/constants.js
43476
- var DB_NAME, DB_VERSION, STORES, SYNC_SNAPSHOT_EVENT_ID_PREFIX;
43477
- var init_constants2 = __esm({
43478
- "../opaque/dist/client/storage/constants.js"() {
43479
- "use strict";
43480
- DB_NAME = "opaque";
43481
- DB_VERSION = 11;
43482
- STORES = {
43483
- LATEST_STATE: "latestState",
43484
- EVENTS: "events",
43485
- TOMBSTONE_CURSORS: "tombstoneCursors",
43486
- PENDING_UPLOADS: "pendingUploads",
43487
- LOADED_SELECTIONS: "loadedSelections"
43488
- };
43489
- SYNC_SNAPSHOT_EVENT_ID_PREFIX = "sync_";
43490
- }
43491
- });
43492
-
43493
- // ../opaque/dist/client/storage/migrations/v1.js
43494
- var v1;
43495
- var init_v1 = __esm({
43496
- "../opaque/dist/client/storage/migrations/v1.js"() {
43497
- "use strict";
43498
- init_constants2();
43499
- v1 = {
43500
- version: 1,
43501
- description: "Initial stores: localEvents + entityCache",
43502
- up(db) {
43503
- if (!db.objectStoreNames.contains(STORES.LATEST_STATE)) {
43504
- const entityCache = db.createObjectStore(STORES.LATEST_STATE, {
43505
- keyPath: "entityId"
43506
- });
43507
- entityCache.createIndex("by_scope", ["scope", "scopeId"]);
43508
- entityCache.createIndex("by_type", "entityType");
43509
- entityCache.createIndex("by_updated", "updatedAt");
43510
- }
43511
- if (!db.objectStoreNames.contains(STORES.EVENTS)) {
43512
- const localEvents = db.createObjectStore(STORES.EVENTS, {
43513
- keyPath: "id"
43514
- });
43515
- localEvents.createIndex("by_entity", "entityId");
43516
- localEvents.createIndex("by_hlc", "hlc");
43517
- localEvents.createIndex("by_action", "actionId");
43518
- }
43519
- }
43520
- };
43521
- }
43522
- });
43523
-
43524
- // ../opaque/dist/client/storage/migrations/v2.js
43525
- var v2;
43526
- var init_v2 = __esm({
43527
- "../opaque/dist/client/storage/migrations/v2.js"() {
43528
- "use strict";
43529
- init_constants2();
43530
- v2 = {
43531
- version: 2,
43532
- description: "Add by_synced index on localEvents",
43533
- up(_db, tx) {
43534
- if (!tx.objectStoreNames.contains(STORES.EVENTS))
43535
- return;
43536
- const localEvents = tx.objectStore(STORES.EVENTS);
43537
- if (!localEvents.indexNames.contains("by_synced")) {
43538
- localEvents.createIndex("by_synced", "synced");
43539
- }
43540
- }
43541
- };
43542
- }
43543
- });
43544
-
43545
- // ../opaque/dist/client/storage/migrations/v3.js
43546
- var DEPRECATED_STORES, v3;
43547
- var init_v3 = __esm({
43548
- "../opaque/dist/client/storage/migrations/v3.js"() {
43549
- "use strict";
43550
- DEPRECATED_STORES = ["pendingActions", "knownState", "scopeMetadata"];
43551
- v3 = {
43552
- version: 3,
43553
- description: "Drop deprecated pendingActions / knownState / scopeMetadata stores",
43554
- up(db) {
43555
- for (const name of DEPRECATED_STORES) {
43556
- if (db.objectStoreNames.contains(name)) {
43557
- db.deleteObjectStore(name);
43558
- }
43559
- }
43560
- }
43561
- };
43562
- }
43563
- });
43564
-
43565
- // ../opaque/dist/client/storage/migrations/v4.js
43566
- var v4;
43567
- var init_v4 = __esm({
43568
- "../opaque/dist/client/storage/migrations/v4.js"() {
43569
- "use strict";
43570
- init_constants2();
43571
- v4 = {
43572
- version: 4,
43573
- description: "Add by_user_branch index on localEvents",
43574
- up(_db, tx) {
43575
- if (!tx.objectStoreNames.contains(STORES.EVENTS))
43576
- return;
43577
- const localEvents = tx.objectStore(STORES.EVENTS);
43578
- if (!localEvents.indexNames.contains("by_user_branch")) {
43579
- localEvents.createIndex("by_user_branch", ["userId", "branchId"]);
43580
- }
43581
- }
43582
- };
43583
- }
43584
- });
43585
-
43586
- // ../opaque/dist/client/storage/migrations/v5.js
43587
- var v5;
43588
- var init_v5 = __esm({
43589
- "../opaque/dist/client/storage/migrations/v5.js"() {
43590
- "use strict";
43591
- init_constants2();
43592
- v5 = {
43593
- version: 5,
43594
- description: "Branch-aware entityCache (composite key [entityId, branchId]); recreate both stores",
43595
- up(db) {
43596
- if (db.objectStoreNames.contains(STORES.LATEST_STATE)) {
43597
- db.deleteObjectStore(STORES.LATEST_STATE);
43598
- }
43599
- if (db.objectStoreNames.contains(STORES.EVENTS)) {
43600
- db.deleteObjectStore(STORES.EVENTS);
43601
- }
43602
- const entityCache = db.createObjectStore(STORES.LATEST_STATE, {
43603
- keyPath: ["entityId", "branchId"]
43604
- });
43605
- entityCache.createIndex("by_entity", "entityId");
43606
- entityCache.createIndex("by_scope", ["scope", "scopeId"]);
43607
- entityCache.createIndex("by_type", "entityType");
43608
- entityCache.createIndex("by_updated", "updatedAt");
43609
- const localEvents = db.createObjectStore(STORES.EVENTS, {
43610
- keyPath: "id"
43611
- });
43612
- localEvents.createIndex("by_entity", "entityId");
43613
- localEvents.createIndex("by_hlc", "hlc");
43614
- localEvents.createIndex("by_action", "actionId");
43615
- localEvents.createIndex("by_synced", "synced");
43616
- localEvents.createIndex("by_user_branch", ["userId", "branchId"]);
43617
- }
43618
- };
43619
- }
43620
- });
43621
-
43622
- // ../opaque/dist/client/storage/migrations/v6.js
43623
- var v6;
43624
- var init_v6 = __esm({
43625
- "../opaque/dist/client/storage/migrations/v6.js"() {
43626
- "use strict";
43627
- init_constants2();
43628
- v6 = {
43629
- version: 6,
43630
- description: "tombstoneCursors store \u2014 per-scope hard-delete catch-up delivery cursor",
43631
- up(db) {
43632
- if (!db.objectStoreNames.contains(STORES.TOMBSTONE_CURSORS)) {
43633
- db.createObjectStore(STORES.TOMBSTONE_CURSORS, { keyPath: "scopeId" });
43634
- }
43635
- }
43636
- };
43637
- }
43638
- });
43639
-
43640
- // ../opaque/dist/client/storage/migrations/v7.js
43641
- var v7;
43642
- var init_v7 = __esm({
43643
- "../opaque/dist/client/storage/migrations/v7.js"() {
43644
- "use strict";
43645
- init_constants2();
43646
- v7 = {
43647
- version: 7,
43648
- description: "pendingUploads store \u2014 durable offline queue of file bytes awaiting upload",
43649
- up(db) {
43650
- if (!db.objectStoreNames.contains(STORES.PENDING_UPLOADS)) {
43651
- const store = db.createObjectStore(STORES.PENDING_UPLOADS, {
43652
- keyPath: "id"
43653
- });
43654
- store.createIndex("by_status", "status", { unique: false });
43655
- }
43656
- }
43657
- };
43658
- }
43659
- });
43660
-
43661
- // ../opaque/dist/client/storage/migrations/v8.js
43662
- var LEGACY_STORE_NAMES, v8;
43663
- var init_v8 = __esm({
43664
- "../opaque/dist/client/storage/migrations/v8.js"() {
43665
- "use strict";
43666
- init_constants2();
43667
- LEGACY_STORE_NAMES = ["localEvents", "entityCache"];
43668
- v8 = {
43669
- version: 8,
43670
- description: "Rename core stores to canonical server names: localEvents\u2192events, entityCache\u2192latestState",
43671
- up(db) {
43672
- for (const legacy of LEGACY_STORE_NAMES) {
43673
- if (db.objectStoreNames.contains(legacy)) {
43674
- db.deleteObjectStore(legacy);
43675
- }
43676
- }
43677
- if (!db.objectStoreNames.contains(STORES.LATEST_STATE)) {
43678
- const latestState = db.createObjectStore(STORES.LATEST_STATE, {
43679
- keyPath: ["entityId", "branchId"]
43680
- });
43681
- latestState.createIndex("by_entity", "entityId");
43682
- latestState.createIndex("by_scope", ["scope", "scopeId"]);
43683
- latestState.createIndex("by_type", "entityType");
43684
- latestState.createIndex("by_updated", "updatedAt");
43685
- }
43686
- if (!db.objectStoreNames.contains(STORES.EVENTS)) {
43687
- const events = db.createObjectStore(STORES.EVENTS, { keyPath: "id" });
43688
- events.createIndex("by_entity", "entityId");
43689
- events.createIndex("by_hlc", "hlc");
43690
- events.createIndex("by_action", "actionId");
43691
- events.createIndex("by_synced", "synced");
43692
- events.createIndex("by_user_branch", ["userId", "branchId"]);
43693
- }
43694
- }
43695
- };
43696
- }
43697
- });
43698
-
43699
- // ../opaque/dist/client/storage/migrations/v9.js
43700
- var v9;
43701
- var init_v9 = __esm({
43702
- "../opaque/dist/client/storage/migrations/v9.js"() {
43703
- "use strict";
43704
- init_constants2();
43705
- v9 = {
43706
- version: 9,
43707
- description: "loadedSelections store \u2014 server-confirmed (entity, select, branch) load keys",
43708
- up(db) {
43709
- if (!db.objectStoreNames.contains(STORES.LOADED_SELECTIONS)) {
43710
- db.createObjectStore(STORES.LOADED_SELECTIONS, { keyPath: "key" });
43711
- }
43712
- }
43713
- };
43714
- }
43715
- });
43716
-
43717
- // ../opaque/dist/client/storage/migrations/v10.js
43718
- var LEGACY_TEXT_ENTITY_TYPES, v10;
43719
- var init_v10 = __esm({
43720
- "../opaque/dist/client/storage/migrations/v10.js"() {
43721
- "use strict";
43722
- init_constants2();
43723
- LEGACY_TEXT_ENTITY_TYPES = ["opRichTextRun", "opRichTextCursor"];
43724
- v10 = {
43725
- version: 10,
43726
- description: "Drop cached latestState records under the pre-rename text entity types (opRichTextRun/opRichTextCursor) and the loadedSelections claims that describe them; they re-sync under opTextRun/opTextCursor",
43727
- up(db, tx) {
43728
- if (!db.objectStoreNames.contains(STORES.LATEST_STATE))
43729
- return;
43730
- const store = tx.objectStore(STORES.LATEST_STATE);
43731
- if (!store.indexNames.contains("by_type"))
43732
- return;
43733
- const swallow = (request3) => {
43734
- request3.onerror = (event) => {
43735
- event.preventDefault();
43736
- event.stopPropagation();
43737
- };
43738
- };
43739
- let purged = false;
43740
- const clearLoadedSelectionsOnce = () => {
43741
- if (purged)
43742
- return;
43743
- purged = true;
43744
- if (!db.objectStoreNames.contains(STORES.LOADED_SELECTIONS))
43745
- return;
43746
- swallow(tx.objectStore(STORES.LOADED_SELECTIONS).clear());
43747
- };
43748
- const byType = store.index("by_type");
43749
- for (const legacyType of LEGACY_TEXT_ENTITY_TYPES) {
43750
- const cursorRequest = byType.openCursor(IDBKeyRange.only(legacyType));
43751
- cursorRequest.onsuccess = () => {
43752
- const cursor = cursorRequest.result;
43753
- if (!cursor)
43754
- return;
43755
- swallow(cursor.delete());
43756
- clearLoadedSelectionsOnce();
43757
- cursor.continue();
43758
- };
43759
- swallow(cursorRequest);
43760
- }
43761
- }
43762
- };
43763
- }
43764
- });
43765
-
43766
- // ../opaque/dist/client/storage/migrations/v11.js
43767
- var v11;
43768
- var init_v11 = __esm({
43769
- "../opaque/dist/client/storage/migrations/v11.js"() {
43770
- "use strict";
43771
- init_constants2();
43772
- v11 = {
43773
- version: 11,
43774
- description: "events by_branch_type index \u2014 branch-wide exclusion seed",
43775
- up(_db, tx) {
43776
- if (!tx.objectStoreNames.contains(STORES.EVENTS))
43777
- return;
43778
- const events = tx.objectStore(STORES.EVENTS);
43779
- if (!events.indexNames.contains("by_branch_type")) {
43780
- events.createIndex("by_branch_type", ["branchId", "type"]);
43781
- }
43782
- }
43783
- };
43784
- }
43785
- });
43786
-
43787
- // ../opaque/dist/client/storage/migrations/index.js
43788
- function runMigrations(db, tx, oldVersion) {
43789
- for (const migration of MIGRATIONS) {
43790
- if (migration.version > oldVersion) {
43791
- migration.up(db, tx, oldVersion);
43792
- }
43793
- }
43794
- }
43795
- var MIGRATIONS;
43796
- var init_migrations = __esm({
43797
- "../opaque/dist/client/storage/migrations/index.js"() {
43798
- "use strict";
43799
- init_v1();
43800
- init_v2();
43801
- init_v3();
43802
- init_v4();
43803
- init_v5();
43804
- init_v6();
43805
- init_v7();
43806
- init_v8();
43807
- init_v9();
43808
- init_v10();
43809
- init_v11();
43810
- MIGRATIONS = [
43811
- v1,
43812
- v2,
43813
- v3,
43814
- v4,
43815
- v5,
43816
- v6,
43817
- v7,
43818
- v8,
43819
- v9,
43820
- v10,
43821
- v11
43822
- ].sort((a5, b4) => a5.version - b4.version);
43823
- }
43824
- });
43825
-
43826
- // ../opaque/dist/client/storage/schema.js
43827
- function openDatabase(timeoutMs = OPEN_DATABASE_TIMEOUT_MS, databaseName = DB_NAME) {
43828
- return new Promise((resolve6, reject) => {
43829
- const request3 = indexedDB.open(databaseName, DB_VERSION);
43830
- let settled = false;
43831
- const timer = setTimeout(() => {
43832
- if (settled)
43833
- return;
43834
- settled = true;
43835
- reject(new IndexedDbOpenBlockedError("timeout"));
43836
- }, timeoutMs);
43837
- const settle = (fn2) => {
43838
- if (settled)
43839
- return;
43840
- settled = true;
43841
- clearTimeout(timer);
43842
- fn2();
43843
- };
43844
- request3.onblocked = () => {
43845
- settle(() => reject(new IndexedDbOpenBlockedError("blocked")));
43846
- };
43847
- request3.onerror = () => settle(() => reject(request3.error));
43848
- request3.onsuccess = () => {
43849
- const db = request3.result;
43850
- if (settled) {
43851
- db.close();
43852
- return;
43853
- }
43854
- db.onversionchange = () => db.close();
43855
- settle(() => resolve6(db));
43856
- };
43857
- request3.onupgradeneeded = (event) => {
43858
- const db = request3.result;
43859
- const tx = request3.transaction;
43860
- if (!tx)
43861
- return;
43862
- runMigrations(db, tx, event.oldVersion);
43863
- };
43864
- });
43865
- }
43866
- function idbGet(db, storeName, key) {
43867
- return new Promise((resolve6, reject) => {
43868
- const tx = db.transaction(storeName, "readonly");
43869
- const store = tx.objectStore(storeName);
43870
- const request3 = store.get(key);
43871
- request3.onsuccess = () => resolve6(request3.result);
43872
- request3.onerror = () => reject(request3.error);
43873
- });
43874
- }
43875
- function idbPut(db, storeName, value) {
43876
- return new Promise((resolve6, reject) => {
43877
- const tx = db.transaction(storeName, "readwrite");
43878
- const store = tx.objectStore(storeName);
43879
- const request3 = store.put(value);
43880
- request3.onsuccess = () => resolve6(request3.result);
43881
- request3.onerror = () => reject(request3.error);
43882
- });
43883
- }
43884
- function idbDelete(db, storeName, key) {
43885
- return new Promise((resolve6, reject) => {
43886
- const tx = db.transaction(storeName, "readwrite");
43887
- const store = tx.objectStore(storeName);
43888
- const request3 = store.delete(key);
43889
- request3.onsuccess = () => resolve6();
43890
- request3.onerror = () => reject(request3.error);
43891
- });
43892
- }
43893
- function idbGetAll(db, storeName) {
43894
- return new Promise((resolve6, reject) => {
43895
- const tx = db.transaction(storeName, "readonly");
43896
- const store = tx.objectStore(storeName);
43897
- const request3 = store.getAll();
43898
- request3.onsuccess = () => resolve6(request3.result);
43899
- request3.onerror = () => reject(request3.error);
43900
- });
43901
- }
43902
- function idbGetAllByIndex(db, storeName, indexName, key) {
43903
- return new Promise((resolve6, reject) => {
43904
- const tx = db.transaction(storeName, "readonly");
43905
- const store = tx.objectStore(storeName);
43906
- const index = store.index(indexName);
43907
- const request3 = index.getAll(key);
43908
- request3.onsuccess = () => resolve6(request3.result);
43909
- request3.onerror = () => reject(request3.error);
43910
- });
43911
- }
43912
- function idbGetEntityLastHlc(db, entityId) {
43913
- return new Promise((resolve6, reject) => {
43914
- const tx = db.transaction(STORES.LATEST_STATE, "readonly");
43915
- const index = tx.objectStore(STORES.LATEST_STATE).index("by_entity");
43916
- const request3 = index.getAll(entityId);
43917
- request3.onsuccess = () => {
43918
- const records2 = request3.result;
43919
- if (records2.length === 0)
43920
- return resolve6(null);
43921
- let max = records2[0].lastHlc;
43922
- for (const r of records2)
43923
- if (r.lastHlc > max)
43924
- max = r.lastHlc;
43925
- resolve6(max);
43926
- };
43927
- request3.onerror = () => reject(request3.error);
43928
- });
43929
- }
43930
- function idbPutMany(db, storeName, values) {
43931
- return new Promise((resolve6, reject) => {
43932
- const tx = db.transaction(storeName, "readwrite");
43933
- const store = tx.objectStore(storeName);
43934
- tx.oncomplete = () => resolve6();
43935
- tx.onerror = () => reject(tx.error);
43936
- for (const value of values) {
43937
- store.put(value);
43938
- }
43939
- });
43940
- }
43941
- function idbDeleteMany(db, storeName, keys) {
43942
- return new Promise((resolve6, reject) => {
43943
- const tx = db.transaction(storeName, "readwrite");
43944
- const store = tx.objectStore(storeName);
43945
- tx.oncomplete = () => resolve6();
43946
- tx.onerror = () => reject(tx.error);
43947
- for (const key of keys) {
43948
- store.delete(key);
43949
- }
43950
- });
43951
- }
43952
- function idbClear(db, storeName) {
43953
- return new Promise((resolve6, reject) => {
43954
- const tx = db.transaction(storeName, "readwrite");
43955
- const store = tx.objectStore(storeName);
43956
- const request3 = store.clear();
43957
- request3.onsuccess = () => resolve6();
43958
- request3.onerror = () => reject(request3.error);
43959
- });
43960
- }
43961
- var OPEN_DATABASE_TIMEOUT_MS, IndexedDbOpenBlockedError;
43962
- var init_schema4 = __esm({
43963
- "../opaque/dist/client/storage/schema.js"() {
43964
- "use strict";
43965
- init_migrations();
43966
- init_constants2();
43967
- init_constants2();
43968
- OPEN_DATABASE_TIMEOUT_MS = 5e3;
43969
- IndexedDbOpenBlockedError = class extends Error {
43970
- constructor(reason) {
43971
- super(reason === "blocked" ? "IndexedDB open blocked: another tab holds an older version of the database" : `IndexedDB open did not settle within ${OPEN_DATABASE_TIMEOUT_MS}ms`);
43972
- this.name = "IndexedDbOpenBlockedError";
43973
- }
43974
- };
43975
- }
43976
- });
43977
-
43978
44334
  // ../opaque/dist/client/storage/writeQueue.js
43979
44335
  var WriteQueue;
43980
44336
  var init_writeQueue = __esm({
@@ -44189,177 +44545,6 @@ var init_knownState = __esm({
44189
44545
  }
44190
44546
  });
44191
44547
 
44192
- // ../opaque/dist/client/storage/utils.js
44193
- function getMaxHlc(events) {
44194
- if (events.length === 0)
44195
- return "0";
44196
- return events.reduce((max, e2) => HLC.compare(e2.hlc, max) > 0 ? e2.hlc : max, "0");
44197
- }
44198
- function eventBranchId(event) {
44199
- return event.branchId ?? void 0;
44200
- }
44201
- function groupEventsByBranch(events) {
44202
- const groups = /* @__PURE__ */ new Map();
44203
- for (const event of events) {
44204
- const key = eventBranchId(event) ?? "";
44205
- const bucket = groups.get(key);
44206
- if (bucket)
44207
- bucket.push(event);
44208
- else
44209
- groups.set(key, [event]);
44210
- }
44211
- return groups;
44212
- }
44213
- function applyLWWEvents(events, existingState, existingLastHlc, existingFieldHlcs, existingFieldActions) {
44214
- const state = existingState ? { ...existingState } : {};
44215
- const fieldHlcs = existingFieldHlcs ? { ...existingFieldHlcs } : {};
44216
- if (!existingFieldHlcs && existingState && existingLastHlc) {
44217
- for (const key of Object.keys(existingState)) {
44218
- fieldHlcs[key] = existingLastHlc;
44219
- }
44220
- }
44221
- const fieldActions = existingFieldActions ? { ...existingFieldActions } : {};
44222
- const sortedEvents = sortByHlc(events, "asc");
44223
- for (const event of sortedEvents) {
44224
- if (isTombstoneEvent(event)) {
44225
- state.deletedAt = event.hlc;
44226
- fieldHlcs.deletedAt = event.hlc;
44227
- fieldActions.deletedAt = event.actionId;
44228
- continue;
44229
- }
44230
- if (event.type === "upsert" && event.field) {
44231
- const currentHlc = fieldHlcs[event.field];
44232
- if (!currentHlc || HLC.compare(event.hlc, currentHlc) > 0) {
44233
- state[event.field] = event.value;
44234
- fieldHlcs[event.field] = event.hlc;
44235
- fieldActions[event.field] = event.actionId;
44236
- }
44237
- }
44238
- }
44239
- if (events.length > 0) {
44240
- const firstEvent = events[0];
44241
- state.id = firstEvent.entityId;
44242
- state.modelName = firstEvent.entityType;
44243
- }
44244
- const eventsMaxHlc = getMaxHlc(events);
44245
- const lastHlc = existingLastHlc && HLC.compare(existingLastHlc, eventsMaxHlc) > 0 ? existingLastHlc : eventsMaxHlc;
44246
- return { state, fieldHlcs, fieldActions, lastHlc };
44247
- }
44248
- var init_utils4 = __esm({
44249
- "../opaque/dist/client/storage/utils.js"() {
44250
- "use strict";
44251
- init_types2();
44252
- init_hlc();
44253
- }
44254
- });
44255
-
44256
- // ../opaque/dist/client/storage/indexeddb/utils.js
44257
- function foldLatestStateRecords(records2) {
44258
- if (records2.length === 0)
44259
- return null;
44260
- if (records2.length === 1) {
44261
- return {
44262
- state: records2[0].state,
44263
- ...records2[0].fieldActions !== void 0 && {
44264
- fieldActions: records2[0].fieldActions
44265
- },
44266
- ...records2[0].fieldHlcs !== void 0 && {
44267
- fieldHlcs: records2[0].fieldHlcs
44268
- }
44269
- };
44270
- }
44271
- const ordered = [...records2].sort((a5, b4) => HLC.compare(a5.lastHlc, b4.lastHlc));
44272
- const merged = {};
44273
- const mergedActions = {};
44274
- let anyActions = false;
44275
- const mergedHlcs = {};
44276
- let anyHlcs = false;
44277
- for (const record of ordered) {
44278
- for (const [field, value] of Object.entries(record.state)) {
44279
- merged[field] = value;
44280
- const action = record.fieldActions?.[field];
44281
- if (action !== void 0) {
44282
- mergedActions[field] = action;
44283
- anyActions = true;
44284
- } else {
44285
- delete mergedActions[field];
44286
- }
44287
- const hlc = record.fieldHlcs?.[field];
44288
- if (hlc !== void 0) {
44289
- mergedHlcs[field] = hlc;
44290
- anyHlcs = true;
44291
- } else {
44292
- delete mergedHlcs[field];
44293
- }
44294
- }
44295
- }
44296
- return {
44297
- state: merged,
44298
- ...anyActions && { fieldActions: mergedActions },
44299
- ...anyHlcs && { fieldHlcs: mergedHlcs }
44300
- };
44301
- }
44302
- function groupBy(items, keyFn) {
44303
- const groups = {};
44304
- for (const item of items) {
44305
- const key = keyFn(item);
44306
- if (!groups[key]) {
44307
- groups[key] = [];
44308
- }
44309
- groups[key].push(item);
44310
- }
44311
- return groups;
44312
- }
44313
- function eventToRecord(event, synced = 1, actionTargetScope, actionHardDeleteScope) {
44314
- return {
44315
- id: event.id,
44316
- entityId: event.entityId,
44317
- entityType: event.entityType,
44318
- field: event.field,
44319
- value: event.value,
44320
- previousValue: event.previousValue,
44321
- hlc: event.hlc,
44322
- clientId: event.clientId,
44323
- userId: event.userId,
44324
- actionId: event.actionId,
44325
- actionLabel: event.actionLabel,
44326
- type: event.type,
44327
- mergeId: event.mergeId,
44328
- targetId: event.targetId,
44329
- branchId: event.branchId,
44330
- parentScopeId: event.parentScopeId,
44331
- actionTargetScope,
44332
- actionHardDeleteScope,
44333
- synced
44334
- };
44335
- }
44336
- function recordToEvent(record) {
44337
- return {
44338
- id: record.id,
44339
- type: record.type,
44340
- entityId: record.entityId,
44341
- entityType: record.entityType,
44342
- field: record.field,
44343
- value: record.value,
44344
- previousValue: record.previousValue,
44345
- hlc: record.hlc,
44346
- clientId: record.clientId,
44347
- userId: record.userId,
44348
- actionId: record.actionId,
44349
- actionLabel: record.actionLabel,
44350
- mergeId: record.mergeId,
44351
- targetId: record.targetId,
44352
- branchId: record.branchId,
44353
- parentScopeId: record.parentScopeId
44354
- };
44355
- }
44356
- var init_utils5 = __esm({
44357
- "../opaque/dist/client/storage/indexeddb/utils.js"() {
44358
- "use strict";
44359
- init_hlc();
44360
- }
44361
- });
44362
-
44363
44548
  // ../opaque/dist/client/storage/entityLoader.js
44364
44549
  var SINGLE_NAMESPACE_METADATA_TYPES, REDACTED_STATE_KEYS, EntityLoader;
44365
44550
  var init_entityLoader = __esm({
@@ -44872,6 +45057,12 @@ function inferScopeFromEntityType(entityType) {
44872
45057
  }
44873
45058
  return CollabScope.branch;
44874
45059
  }
45060
+ function transactionRequest(request3) {
45061
+ return new Promise((resolve6, reject) => {
45062
+ request3.onsuccess = () => resolve6(request3.result);
45063
+ request3.onerror = () => reject(request3.error);
45064
+ });
45065
+ }
44875
45066
  var LatestStateManager;
44876
45067
  var init_latestState = __esm({
44877
45068
  "../opaque/dist/client/storage/indexeddb/latestState.js"() {
@@ -44899,7 +45090,7 @@ var init_latestState = __esm({
44899
45090
  * being lost on reload. `scopeId` is the branch the caller wrote under; it's
44900
45091
  * the namespace for events that don't carry their own `branchId`.
44901
45092
  */
44902
- async updateEntityCache(entityId, events, scopeId) {
45093
+ async updateEntityCache(entityId, events, scopeId, transaction) {
44903
45094
  const byBranch = /* @__PURE__ */ new Map();
44904
45095
  for (const event of events) {
44905
45096
  const branchId = eventBranchId(event) ?? (isTextContentType(event.entityType) || isUnbranchedCustomScopeType(event.entityType) ? GLOBAL_NAMESPACE_BRANCH_KEY : scopeId);
@@ -44910,12 +45101,15 @@ var init_latestState = __esm({
44910
45101
  byBranch.set(branchId, [event]);
44911
45102
  }
44912
45103
  for (const [branchId, branchEvents] of byBranch) {
44913
- await this.writeBranchRecord(entityId, branchId, branchEvents, scopeId);
45104
+ await this.writeBranchRecord(entityId, branchId, branchEvents, scopeId, transaction);
44914
45105
  }
44915
45106
  }
44916
45107
  /** Fold one branch's events onto its existing record and persist it. */
44917
- async writeBranchRecord(entityId, branchId, events, fallbackScopeId) {
44918
- const existing = await idbGet(this.db, STORES.LATEST_STATE, [entityId, branchId]);
45108
+ async writeBranchRecord(entityId, branchId, events, fallbackScopeId, transaction) {
45109
+ const existing = transaction ? await transactionRequest(transaction.objectStore(STORES.LATEST_STATE).get([entityId, branchId])) : await idbGet(this.db, STORES.LATEST_STATE, [
45110
+ entityId,
45111
+ branchId
45112
+ ]);
44919
45113
  const entityType = events[0]?.entityType ?? existing?.entityType ?? "unknown";
44920
45114
  const contentCohabitsBranch = isTextContentType(entityType) && (isBranchId(branchId) || branchId === GLOBAL_NAMESPACE_BRANCH_KEY);
44921
45115
  const scope = existing?.scope ?? (contentCohabitsBranch ? CollabScope.branch : inferScopeFromEntityType(entityType));
@@ -44959,7 +45153,10 @@ var init_latestState = __esm({
44959
45153
  fieldActions,
44960
45154
  updatedAt: Date.now()
44961
45155
  };
44962
- await idbPut(this.db, STORES.LATEST_STATE, cacheRecord);
45156
+ if (transaction)
45157
+ await transactionRequest(transaction.objectStore(STORES.LATEST_STATE).put(cacheRecord));
45158
+ else
45159
+ await idbPut(this.db, STORES.LATEST_STATE, cacheRecord);
44963
45160
  }
44964
45161
  /**
44965
45162
  * Remove a dropped action's contribution from the entities' LATEST_STATE
@@ -45351,8 +45548,10 @@ var PendingActionsManager;
45351
45548
  var init_pendingActions = __esm({
45352
45549
  "../opaque/dist/client/storage/indexeddb/pendingActions.js"() {
45353
45550
  "use strict";
45551
+ init_dist();
45354
45552
  init_schema4();
45355
45553
  init_utils4();
45554
+ init_hlc();
45356
45555
  init_utils5();
45357
45556
  PendingActionsManager = class {
45358
45557
  db;
@@ -45365,18 +45564,52 @@ var init_pendingActions = __esm({
45365
45564
  }
45366
45565
  /**
45367
45566
  * Store a locally-created action (not yet sent to server).
45368
- * Writes to EVENTS (source of truth) and LATEST_STATE (computed state).
45567
+ * Commit EVENTS and LATEST_STATE together so refresh cannot restore a partial action.
45369
45568
  *
45370
45569
  * @param action - The action to store
45371
45570
  * @param scopeId - The scope ID (branch ID) for entity cache updates
45372
45571
  */
45373
45572
  async storePendingAction(action, scopeId) {
45374
- const eventRecords = action.events.map((e2) => eventToRecord(e2, 0, action.targetScope, action.hardDeleteScope));
45375
- await idbPutMany(this.db, STORES.EVENTS, eventRecords);
45376
- const byEntity = groupBy(action.events, (e2) => e2.entityId);
45377
- for (const [entityId, entityEvents] of Object.entries(byEntity)) {
45378
- await this.latestStateManager.updateEntityCache(entityId, entityEvents, scopeId);
45573
+ const started = performance.now();
45574
+ const tx = this.db.transaction([STORES.EVENTS, STORES.LATEST_STATE], "readwrite");
45575
+ const completion = new Promise((resolve6, reject) => {
45576
+ tx.oncomplete = () => resolve6();
45577
+ tx.onabort = () => reject(tx.error ?? new Error("Action transaction aborted."));
45578
+ tx.onerror = () => {
45579
+ };
45580
+ });
45581
+ void completion.catch(() => {
45582
+ });
45583
+ let eventsStored = started;
45584
+ try {
45585
+ for (const event of action.events) {
45586
+ tx.objectStore(STORES.EVENTS).put({
45587
+ ...eventToRecord(event, 0, action.targetScope, action.hardDeleteScope),
45588
+ actionLabel: event.actionLabel ?? action.label
45589
+ });
45590
+ }
45591
+ eventsStored = performance.now();
45592
+ const byEntity = groupBy(action.events, (event) => event.entityId);
45593
+ for (const [entityId, entityEvents] of Object.entries(byEntity)) {
45594
+ await this.latestStateManager.updateEntityCache(entityId, entityEvents, scopeId, tx);
45595
+ }
45596
+ await completion;
45597
+ } catch (error) {
45598
+ try {
45599
+ tx.abort();
45600
+ } catch {
45601
+ }
45602
+ await completion.catch(() => {
45603
+ });
45604
+ throw error;
45379
45605
  }
45606
+ traceMark("client.action.persist", {
45607
+ actionId: action.id,
45608
+ branchId: scopeId,
45609
+ events: action.events.length,
45610
+ eventStoreMs: eventsStored - started,
45611
+ latestStateMs: performance.now() - eventsStored
45612
+ });
45380
45613
  }
45381
45614
  /**
45382
45615
  * Get all pending actions (unsynced events grouped by actionId).
@@ -45387,7 +45620,11 @@ var init_pendingActions = __esm({
45387
45620
  if (unsyncedEvents.length === 0) {
45388
45621
  return [];
45389
45622
  }
45390
- const byAction = groupBy(unsyncedEvents.map(recordToEvent), (e2) => e2.actionId);
45623
+ const byAction = groupBy(
45624
+ // IndexedDB indexes return primary-key order. Routing uses the first authored event.
45625
+ sortByHlc(unsyncedEvents).map(recordToEvent),
45626
+ (e2) => e2.actionId
45627
+ );
45391
45628
  const recordsByAction = groupBy(unsyncedEvents, (record) => record.actionId);
45392
45629
  const actions = Object.entries(byAction).map(([actionId, events]) => {
45393
45630
  const records2 = recordsByAction[actionId] ?? [];
@@ -46024,6 +46261,7 @@ var init_indexeddb = __esm({
46024
46261
  init_scopeMetadata();
46025
46262
  init_cleanup();
46026
46263
  init_utils5();
46264
+ init_ingestion();
46027
46265
  init_utils5();
46028
46266
  init_latestState();
46029
46267
  init_pendingActions();
@@ -46033,6 +46271,13 @@ var init_indexeddb = __esm({
46033
46271
  init_scopeMetadata();
46034
46272
  init_cleanup();
46035
46273
  BrowserDBStorage = class _BrowserDBStorage {
46274
+ /** Persist fresh events, current state, and the receipt atomically. */
46275
+ async stageBranchIngestion(actions, receipt, prepared) {
46276
+ return stageBranchIngestion(this.db, actions, receipt, prepared);
46277
+ }
46278
+ async getIngestionReceipt(id) {
46279
+ return await idbGet(this.db, STORES.INGESTIONS, id) ?? void 0;
46280
+ }
46036
46281
  db;
46037
46282
  writeQueue;
46038
46283
  knownStateManager;
@@ -47035,6 +47280,11 @@ function handleContext(state, message, cache4, eventHandlers, storage2 = null) {
47035
47280
  state.pendingContextResolve = null;
47036
47281
  state.pendingContextReject = null;
47037
47282
  }
47283
+ if (state.pendingActionReplay && contextMessageMatchesDesired(state, message)) {
47284
+ const replay = state.pendingActionReplay;
47285
+ state.pendingActionReplay = null;
47286
+ replay();
47287
+ }
47038
47288
  const orgChanged = prevOrgId !== nextOrgId;
47039
47289
  const scopeChanged = !scopesEqual(prevScope, nextScope);
47040
47290
  if (orgChanged || scopeChanged) {
@@ -47233,14 +47483,13 @@ function handleAuthenticated(accountId, authState, currentBranch, eventHandlers,
47233
47483
  });
47234
47484
  emitAuthenticated(eventHandlers, accountId);
47235
47485
  const hasDesired = contextState.desiredOrgId !== null || contextState.desiredScope !== null || contextState.desiredRealmId !== null;
47486
+ contextState.pendingActionReplay = hasDesired ? syncPendingActions2 : null;
47236
47487
  if (hasDesired) {
47237
47488
  setOpaqueContext(contextState, {
47238
47489
  orgId: contextState.desiredOrgId,
47239
47490
  scope: contextState.desiredScope,
47240
47491
  realmId: contextState.desiredRealmId
47241
- }, send2, contextTimeout, mode).then(() => {
47242
- syncPendingActions2();
47243
- }).catch((err) => {
47492
+ }, send2, contextTimeout, mode).catch((err) => {
47244
47493
  staffMode.log("[OpaqueClient] Context restore on auth failed; deferring pending actions:", err);
47245
47494
  });
47246
47495
  } else {
@@ -47740,7 +47989,7 @@ function handleInvalidate(message, cache4, storage2, eventHandlers, isActionAwai
47740
47989
  });
47741
47990
  }
47742
47991
  }
47743
- function sendAction(action, storage2, eventHandlers, send2, scopeId, serverProtocolVersion = 1) {
47992
+ function sendAction(action, storage2, eventHandlers, send2, scopeId, serverProtocolVersion = 1, alreadyPersisted = false) {
47744
47993
  if (!action.meta?.requestId) {
47745
47994
  const ambientReqId = currentRequestId();
47746
47995
  const pinnedReqId = ambientReqId && ambientReqId.length > 0 && !ambientReqId.startsWith("sess_") ? ambientReqId : void 0;
@@ -47748,7 +47997,8 @@ function sendAction(action, storage2, eventHandlers, send2, scopeId, serverProto
47748
47997
  action = { ...action, meta: { ...action.meta, requestId: reqId } };
47749
47998
  }
47750
47999
  const storageScopeId = action.targetScope?.scope === "branch" ? action.targetScope.branchId : scopeId;
47751
- fireAndForget(storage2?.storePendingAction(action, storageScopeId), "[OpaqueClient] Failed to store pending action:");
48000
+ if (!alreadyPersisted)
48001
+ fireAndForget(storage2?.storePendingAction(action, storageScopeId), "[OpaqueClient] Failed to store pending action:");
47752
48002
  emitAction(eventHandlers, action);
47753
48003
  const frame = {
47754
48004
  type: "action",
@@ -47849,51 +48099,6 @@ var init_org_membership = __esm({
47849
48099
  }
47850
48100
  });
47851
48101
 
47852
- // ../opaque/dist/client/network/hydrationScheduler.js
47853
- function yieldToMain() {
47854
- const sched = globalThis.scheduler;
47855
- if (sched?.yield)
47856
- return sched.yield();
47857
- if (typeof MessageChannel !== "undefined") {
47858
- return new Promise((resolve6) => {
47859
- const channel = new MessageChannel();
47860
- channel.port1.onmessage = () => resolve6();
47861
- channel.port2.postMessage(null);
47862
- });
47863
- }
47864
- return new Promise((resolve6) => setTimeout(resolve6, 0));
47865
- }
47866
- function createHydrationSlicer(options = {}) {
47867
- const now3 = options.now ?? (() => performance.now());
47868
- let budgetMs = DEFAULT_BUDGET_MS;
47869
- let sliceStart = now3();
47870
- return {
47871
- async tick() {
47872
- const elapsed = now3() - sliceStart;
47873
- if (elapsed < budgetMs)
47874
- return;
47875
- if (elapsed > FRAME_BUDGET_MS) {
47876
- budgetMs = Math.max(MIN_BUDGET_MS, budgetMs - 2);
47877
- } else if (elapsed < budgetMs + 2) {
47878
- budgetMs = Math.min(MAX_BUDGET_MS, budgetMs + 1);
47879
- }
47880
- await yieldToMain();
47881
- options.onResume?.();
47882
- sliceStart = now3();
47883
- }
47884
- };
47885
- }
47886
- var FRAME_BUDGET_MS, DEFAULT_BUDGET_MS, MIN_BUDGET_MS, MAX_BUDGET_MS;
47887
- var init_hydrationScheduler = __esm({
47888
- "../opaque/dist/client/network/hydrationScheduler.js"() {
47889
- "use strict";
47890
- FRAME_BUDGET_MS = 16;
47891
- DEFAULT_BUDGET_MS = 8;
47892
- MIN_BUDGET_MS = 4;
47893
- MAX_BUDGET_MS = 12;
47894
- }
47895
- });
47896
-
47897
48102
  // ../opaque/dist/client/network/offline.js
47898
48103
  function isBranchScopedAction(action) {
47899
48104
  const entityType = action.events[0]?.entityType;
@@ -48373,10 +48578,10 @@ function stripAutoFields(value) {
48373
48578
  return value.map(stripAutoFields);
48374
48579
  if (value && typeof value === "object") {
48375
48580
  const out = {};
48376
- for (const [k, v14] of Object.entries(value)) {
48581
+ for (const [k, v15] of Object.entries(value)) {
48377
48582
  if (k === "id" || k === "createdAt" || k === "updatedAt")
48378
48583
  continue;
48379
- out[k] = stripAutoFields(v14);
48584
+ out[k] = stripAutoFields(v15);
48380
48585
  }
48381
48586
  return out;
48382
48587
  }
@@ -51584,6 +51789,7 @@ var init_client2 = __esm({
51584
51789
  "../opaque/dist/client/client.js"() {
51585
51790
  "use strict";
51586
51791
  init_dist();
51792
+ init_hydrationScheduler();
51587
51793
  init_messaging();
51588
51794
  init_events2();
51589
51795
  init_utils();
@@ -51597,6 +51803,7 @@ var init_client2 = __esm({
51597
51803
  init_files();
51598
51804
  init_cache();
51599
51805
  init_global();
51806
+ init_ingestion();
51600
51807
  init_adopt();
51601
51808
  init_scope_sync();
51602
51809
  init_gap_check();
@@ -52827,6 +53034,152 @@ var init_client2 = __esm({
52827
53034
  // server-side yet). Chaining onto a single Promise preserves the
52828
53035
  // order in which sendAction was called.
52829
53036
  actionQueue = Promise.resolve();
53037
+ stagedActions = /* @__PURE__ */ new Map();
53038
+ pendingIngestions = [];
53039
+ ingestionListeners = /* @__PURE__ */ new Set();
53040
+ ingestionRetries = /* @__PURE__ */ new Map();
53041
+ reservedIngestionEntities = /* @__PURE__ */ new Set();
53042
+ getPendingIngestions = () => this.pendingIngestions;
53043
+ subscribeIngestions = (listener) => {
53044
+ this.ingestionListeners.add(listener);
53045
+ return () => {
53046
+ this.ingestionListeners.delete(listener);
53047
+ };
53048
+ };
53049
+ retryIngestion(id) {
53050
+ this.ingestionRetries.get(id)?.();
53051
+ }
53052
+ setIngestionStatus(receipt, status) {
53053
+ this.pendingIngestions = this.pendingIngestions.filter((entry) => entry.id !== receipt.id);
53054
+ if (status)
53055
+ this.pendingIngestions = [
53056
+ ...this.pendingIngestions,
53057
+ { id: receipt.id, branchId: receipt.branchId, status }
53058
+ ];
53059
+ for (const listener of this.ingestionListeners)
53060
+ listener();
53061
+ }
53062
+ /** Publish fresh entities while one retryable transaction persists them. */
53063
+ async startBranchIngestion(actions, receipt) {
53064
+ const started = performance.now();
53065
+ if (this.ingestionRetries.has(receipt.id))
53066
+ throw new Error("Ingestion is already pending.");
53067
+ const storage2 = this.validateBranchIngestion(actions, receipt);
53068
+ const preflightMs = performance.now() - started;
53069
+ const prepared = prepareBranchIngestion(actions, receipt);
53070
+ let completePersistence;
53071
+ const persistence = new Promise((resolve6) => {
53072
+ completePersistence = resolve6;
53073
+ });
53074
+ let completePublication;
53075
+ const publication = new Promise((resolve6) => {
53076
+ completePublication = resolve6;
53077
+ });
53078
+ let saving = false;
53079
+ const attempt = () => {
53080
+ if (saving)
53081
+ return;
53082
+ saving = true;
53083
+ this.setIngestionStatus(receipt, "saving");
53084
+ void storage2.stageBranchIngestion(actions, receipt, prepared).then((timings) => {
53085
+ this.ingestionRetries.delete(receipt.id);
53086
+ this.setIngestionStatus(receipt);
53087
+ completePersistence(timings);
53088
+ }, (error) => {
53089
+ saving = false;
53090
+ this.setIngestionStatus(receipt, "failed");
53091
+ console.error("[OpaqueClient] Ingestion persistence failed; retry is available:", error);
53092
+ });
53093
+ };
53094
+ this.ingestionRetries.set(receipt.id, attempt);
53095
+ this.actionQueue = this.actionQueue.then(() => Promise.all([persistence, publication])).then(() => {
53096
+ });
53097
+ for (const action of actions) {
53098
+ for (const event of action.events)
53099
+ this.reservedIngestionEntities.add(`${receipt.branchId}:${event.entityId}`);
53100
+ this.stagedActions.set(action.id, action.events.length);
53101
+ this.sendAction(action);
53102
+ }
53103
+ attempt();
53104
+ const cache4 = await this.publishBranchIngestion(actions, receipt, true);
53105
+ completePublication();
53106
+ for (const action of actions) {
53107
+ for (const event of action.events)
53108
+ this.reservedIngestionEntities.delete(`${receipt.branchId}:${event.entityId}`);
53109
+ }
53110
+ return { preflightMs, ...cache4, persistence };
53111
+ }
53112
+ validateBranchIngestion(actions, receipt) {
53113
+ const storage2 = this.getStorage();
53114
+ if (!storage2)
53115
+ throw new Error("Browser storage is not ready.");
53116
+ if (this.cache.getEntity(receipt.branchId, receipt.branchId)?.onlyAcceptsMerges)
53117
+ throw new Error("The target branch only accepts merges.");
53118
+ const checkedEntities = /* @__PURE__ */ new Set();
53119
+ for (const action of actions) {
53120
+ if (action.events.length > MAX_INBOUND_ACTION_EVENTS)
53121
+ throw new Error("Ingestion action exceeds the event limit.");
53122
+ if (messageByteLength({ type: "action", action }) > MAX_OUTBOUND_MESSAGE_BYTES)
53123
+ throw new Error("Ingestion action exceeds the frame limit.");
53124
+ for (const event of action.events) {
53125
+ if (checkedEntities.has(event.entityId))
53126
+ continue;
53127
+ checkedEntities.add(event.entityId);
53128
+ if (this.reservedIngestionEntities.has(`${receipt.branchId}:${event.entityId}`) || this.cache.getEntity(event.entityId, receipt.branchId))
53129
+ throw new Error("Ingestion cannot overwrite an existing entity.");
53130
+ }
53131
+ }
53132
+ return storage2;
53133
+ }
53134
+ /** Commit fresh branch events before publishing them to local readers. */
53135
+ async ingestBranchActions(actions, receipt) {
53136
+ const started = performance.now();
53137
+ const storage2 = this.validateBranchIngestion(actions, receipt);
53138
+ const commitStart = performance.now();
53139
+ const storageTimings = await storage2.stageBranchIngestion(actions, receipt);
53140
+ const commitMs = performance.now() - commitStart;
53141
+ const cache4 = await this.publishBranchIngestion(actions, receipt);
53142
+ return {
53143
+ commitMs,
53144
+ ...cache4,
53145
+ preflightMs: commitStart - started,
53146
+ storagePrepareMs: storageTimings.prepareMs,
53147
+ storageTransactionMs: storageTimings.transactionMs
53148
+ };
53149
+ }
53150
+ async publishBranchIngestion(actions, receipt, alreadyQueued = false) {
53151
+ const cacheStart = performance.now();
53152
+ const slicer = createHydrationSlicer();
53153
+ let lastEntity = "";
53154
+ let cacheApplied = cacheStart;
53155
+ this.cache.beginInvalidationBatch();
53156
+ try {
53157
+ for (const action of actions) {
53158
+ for (const event of action.events) {
53159
+ if (event.entityId !== lastEntity) {
53160
+ await slicer.tick();
53161
+ lastEntity = event.entityId;
53162
+ }
53163
+ this.cache.applyPendingEvent(event, receipt.branchId);
53164
+ }
53165
+ if (!alreadyQueued)
53166
+ this.stagedActions.set(action.id, action.events.length);
53167
+ }
53168
+ cacheApplied = performance.now();
53169
+ if (!alreadyQueued)
53170
+ for (const action of actions)
53171
+ this.sendAction(action);
53172
+ } finally {
53173
+ this.cache.endInvalidationBatch();
53174
+ }
53175
+ this.emitLocalChange();
53176
+ const finished = performance.now();
53177
+ return {
53178
+ cacheMs: finished - cacheStart,
53179
+ cacheApplyMs: cacheApplied - cacheStart,
53180
+ cachePublishMs: finished - cacheApplied
53181
+ };
53182
+ }
52830
53183
  // Action methods
52831
53184
  sendAction(action) {
52832
53185
  const scopedAction = this.withActionTargetScope(action);
@@ -52888,6 +53241,7 @@ var init_client2 = __esm({
52888
53241
  const overByteCap = byteLength > MAX_OUTBOUND_MESSAGE_BYTES;
52889
53242
  const overEventCap = action.events.length > MAX_INBOUND_ACTION_EVENTS;
52890
53243
  if (overByteCap || overEventCap) {
53244
+ this.stagedActions.delete(action.id);
52891
53245
  this.cache.removeByActionId(action.id);
52892
53246
  fireAndForget(this.connectionState.storage?.deletePendingAction(action.id), "[OpaqueClient] Failed to drop oversized action:");
52893
53247
  staffMode.log("[OpaqueClient] Action refused: exceeds a delivery limit (dropped):", {
@@ -52901,14 +53255,21 @@ var init_client2 = __esm({
52901
53255
  ]
52902
53256
  });
52903
53257
  traceMark("client.action.refused_oversized", { actionId: action.id, byteLength, events: action.events.length }, action.meta?.requestId);
53258
+ const refusal = overByteCap ? `This change is too large to sync (${byteLength} bytes, limit ${MAX_OUTBOUND_MESSAGE_BYTES}) and was reverted.` : `This change is too large to sync (${action.events.length} events, limit ${MAX_INBOUND_ACTION_EVENTS}) and was reverted.`;
53259
+ this.rejectActionAck(action.id, new Error(refusal));
52904
53260
  emitRejection(this.eventHandlers, {
52905
53261
  code: "MESSAGE_TOO_LARGE",
52906
- message: overByteCap ? `This change is too large to sync (${byteLength} bytes, limit ${MAX_OUTBOUND_MESSAGE_BYTES}) and was reverted.` : `This change is too large to sync (${action.events.length} events, limit ${MAX_INBOUND_ACTION_EVENTS}) and was reverted.`,
53262
+ message: refusal,
52907
53263
  actionId: action.id
52908
53264
  });
52909
53265
  return;
52910
53266
  }
52911
- sendAction(action, this.connectionState.storage, this.eventHandlers, (msg, preEncoded) => this.send(msg, preEncoded), this.currentBranch, this.authState.serverProtocolVersion);
53267
+ const stagedCount = this.stagedActions.get(action.id);
53268
+ if (stagedCount !== void 0 && stagedCount !== action.events.length) {
53269
+ await this.connectionState.storage?.storePendingAction(action, action.targetScope?.scope === "branch" ? action.targetScope.branchId : this.currentBranch);
53270
+ }
53271
+ this.stagedActions.delete(action.id);
53272
+ sendAction(action, this.connectionState.storage, this.eventHandlers, (msg, preEncoded) => this.send(msg, preEncoded), this.currentBranch, this.authState.serverProtocolVersion, stagedCount !== void 0);
52912
53273
  }
52913
53274
  async flushActions() {
52914
53275
  await this.actionQueue;
@@ -54092,6 +54453,12 @@ var init_opaque_config = __esm({
54092
54453
  // publishes again. A project that has never published has no registry row,
54093
54454
  // and the handler treats that as done rather than as an error.
54094
54455
  "project.setPackagePrivacy",
54456
+ // Brand actions. `brand.traceLogo` traces one picked logo candidate (a
54457
+ // PNG crop) into one master SVG and returns it in the request result. It
54458
+ // rides the Brand, reads the crop from the request's own project scope,
54459
+ // and spends CPU, not money, so it has no ADMIN_ONLY_ACTIONS entry. The
54460
+ // handler is packages/service-worker/src/handlers/logo.ts.
54461
+ "brand.traceLogo",
54095
54462
  // Marketing actions. Registers a newly created account in Resend: segment
54096
54463
  // membership plus topic subscriptions. Runs on the User entity, off the
54097
54464
  // sign-up request path, so a Resend outage or rate limit retries through the
@@ -54185,12 +54552,12 @@ function def(type, description, input, options) {
54185
54552
  function getOpaqueActionDef(type) {
54186
54553
  return OPAQUE_ACTIONS.find((a5) => a5.type === type);
54187
54554
  }
54188
- var shaderLayersSchema, variableValue, STYLE_KEYS, STYLE_TRAPS, nodeUpdatesSchema, ORGANIZATION_ACTION_TYPES, componentId, pageId, variantId, OPAQUE_ACTIONS;
54555
+ var shaderLayersSchema, variableValue, STYLE_KEYS, STYLE_TRAPS, SVG_ARTWORK_DESCRIPTION, nodeUpdatesSchema, ORGANIZATION_ACTION_TYPES, componentId, pageId, variantId, OPAQUE_ACTIONS;
54189
54556
  var init_opaqueActions = __esm({
54190
54557
  "../shared/dist/ai/opaque/opaqueActions.js"() {
54191
54558
  "use strict";
54192
54559
  init_ai_action_style_keys();
54193
- shaderLayersSchema = z52.array(z52.record(z52.string(), z52.unknown())).describe("Shader layer specs, one per layer. Each matches the ShaderLayer shape: name (string), sortOrder (number), visible (boolean), effectType (e.g. 'meshGradient', 'perlinNoise'), blendMode, opacity/animated/animationSpeed/scale/offsetX/offsetY/rotation and effect-specific numeric params as plain numbers, color1/color2/color3/colorBack as hex or rgb(a) strings, colors as an array of such strings.");
54560
+ shaderLayersSchema = z52.array(z52.record(z52.string(), z52.unknown())).describe("Shader layer specs, one per layer. Each matches the ShaderLayer shape: name (string), sortOrder (number), visible (boolean), effectType (e.g. 'meshGradient', 'perlinNoise'), blendMode, opacity/animated/animationSpeed/scale/offsetX/offsetY/rotation and effect-specific numeric params as plain numbers, color1/color2/color3/colorBack as hex or rgb(a) strings, colors as an array of such strings. Only catalog effects are supported; this field does not accept custom GLSL or WGSL source.");
54194
54561
  variableValue = z52.union([
54195
54562
  z52.string(),
54196
54563
  z52.number(),
@@ -54209,10 +54576,11 @@ var init_opaqueActions = __esm({
54209
54576
  "`display` / `flex` \u2192 use flexDirection, justifyContent, alignItems",
54210
54577
  "`textColor` \u2192 use color"
54211
54578
  ].join("; ");
54579
+ SVG_ARTWORK_DESCRIPTION = "Accepts a complete SVG document or bare path data. Imports supported geometry into an editable vector network. The renderer uses SVG baked from that network, not the original document. Supply a viewBox to define the artwork dimensions. Check warnings for unsupported elements. This is not a runtime for scripts or SVG/CSS animation.";
54212
54580
  nodeUpdatesSchema = z52.object({
54213
54581
  name: z52.string().optional().describe("Layer name"),
54214
54582
  innerText: z52.string().optional().describe("Text content (text nodes only)"),
54215
- svgPath: z52.string().optional().describe("Import SVG markup or path data as editable vector artwork. This promotes the node to 'vector' and replaces its entire network. Replacement does not preserve existing layer IDs, constraints, or variable bindings."),
54583
+ svgPath: z52.string().optional().describe(SVG_ARTWORK_DESCRIPTION + " This promotes the node to 'vector' and replaces its entire network. Replacement does not preserve existing layer IDs, constraints, or variable bindings."),
54216
54584
  shaderLayers: shaderLayersSchema.optional().describe("Replaces the node's shader layers - GPU effects drawn over the node's own content. Boxes only."),
54217
54585
  // Real node fields that were previously unreachable through BOTH edit
54218
54586
  // surfaces: they are not styles, so they fell into `styles.base` and were
@@ -54225,7 +54593,7 @@ var init_opaqueActions = __esm({
54225
54593
  className: z52.string().optional().describe("CSS class name(s) applied to the rendered element."),
54226
54594
  url: z52.string().optional().describe("Link destination for a link node."),
54227
54595
  target: z52.string().optional().describe("Link target: 'self' or 'blank'."),
54228
- frameUrl: z52.string().optional().describe("URL an iframe node loads."),
54596
+ frameUrl: z52.string().optional().describe("URL an iframe node loads. Canvas and deployment use an empty sandbox, which blocks scripts. Use svgPath to import editable vector geometry. It does not preserve an animation document."),
54229
54597
  styles: z52.object({
54230
54598
  base: z52.record(z52.string(), z52.unknown()).optional().describe(`Visual properties. VALID KEYS \u2014 no others are accepted, and one invalid key rejects the WHOLE edit: ${STYLE_KEYS.join(", ")}. Common mistakes: ${STYLE_TRAPS}.`)
54231
54599
  }).optional()
@@ -54277,7 +54645,7 @@ var init_opaqueActions = __esm({
54277
54645
  "textarea",
54278
54646
  "text"
54279
54647
  ]).optional().describe("For nodeType 'input': which form control this node renders (default 'text')."),
54280
- svgPath: z52.string().optional().describe("Required when nodeType is 'vector': the SVG markup/path."),
54648
+ svgPath: z52.string().optional().describe("Required when nodeType is 'vector'. " + SVG_ARTWORK_DESCRIPTION),
54281
54649
  shaderLayers: shaderLayersSchema.optional().describe("Shader effects for a 'box' node."),
54282
54650
  updates: nodeUpdatesSchema.optional()
54283
54651
  })),
@@ -54300,9 +54668,15 @@ var init_opaqueActions = __esm({
54300
54668
  name: z52.string().optional().describe("Name for the new component (auto-generated if omitted)")
54301
54669
  })),
54302
54670
  def("deleteComponent", "Delete a component. The component and its node graph are tombstoned, not hard-deleted - they remain recoverable through branch history.", z52.object({ componentId })),
54303
- def("createPage", "Create a new draft page with no nodes. Auto-names 'Page N' (deduped) when name is omitted.", z52.object({
54671
+ def("createPage", "Create a new draft page with no nodes. Auto-names 'Page N' (deduped) when name is omitted. Use createRoute for a page that publishes to a URL.", z52.object({
54304
54672
  name: z52.string().optional().describe("Name for the new page (auto-generated if omitted)")
54305
54673
  })),
54674
+ def("createRoute", "Create a publishable route with one Body node. Returns pageId, bodyNodeId, slug, path, and isIndex. Add content inside bodyNodeId with createNode or applyCanvasEdits. The first root route becomes the homepage automatically. An explicit isIndex claim transfers the homepage from the current route. Use parentPageId to nest routes; slug is one URL segment. Deployment remains a separate action.", z52.object({
54675
+ name: z52.string().trim().min(1).optional().describe("Display name. Defaults to a unique Route N."),
54676
+ slug: z52.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).optional().describe("URL segment, such as signal-noise. Omit to generate a unique slug from the name. Explicit collisions are rejected."),
54677
+ parentPageId: pageId.optional().describe("Existing route in this branch to use as the parent. Omit for a root route."),
54678
+ isIndex: z52.boolean().optional().describe("Set true to make this root route the homepage. The first root route is always the homepage.")
54679
+ })),
54306
54680
  def("moveNode", "Reparent a node within its page or variant. Pass no newParentNodeId to move it to the top level. Cannot move a node between pages or variants, or into its own subtree.", z52.object({
54307
54681
  componentId: componentId.optional(),
54308
54682
  pageId: pageId.optional(),
@@ -55019,6 +55393,8 @@ var init_client3 = __esm({
55019
55393
  init_optext();
55020
55394
  init_storage();
55021
55395
  init_org_membership();
55396
+ init_compactAction();
55397
+ init_messaging();
55022
55398
  }
55023
55399
  });
55024
55400
 
@@ -55120,7 +55496,7 @@ function getDefaultVariant(component) {
55120
55496
  if (variants.length === 0) {
55121
55497
  return null;
55122
55498
  }
55123
- const groupless = variants.find((v14) => v14 && !v14.variantGroups);
55499
+ const groupless = variants.find((v15) => v15 && !v15.variantGroups);
55124
55500
  return (_a2 = groupless !== null && groupless !== void 0 ? groupless : variants[0]) !== null && _a2 !== void 0 ? _a2 : null;
55125
55501
  }
55126
55502
  function getNodes(parent, variantId2) {
@@ -55135,7 +55511,7 @@ function getNodes(parent, variantId2) {
55135
55511
  return void 0;
55136
55512
  }
55137
55513
  if (variantId2) {
55138
- const variant = variants.find((v14) => v14 && v14.id === variantId2);
55514
+ const variant = variants.find((v15) => v15 && v15.id === variantId2);
55139
55515
  return variant ? nodesOf(variant) : void 0;
55140
55516
  } else {
55141
55517
  const defaultVariant = getDefaultVariant(parent);
@@ -55154,7 +55530,7 @@ function resolveTargetList(parent, variantId2) {
55154
55530
  if (variants.length === 0) {
55155
55531
  return null;
55156
55532
  }
55157
- const variant = variantId2 ? variants.find((v14) => v14 && v14.id === variantId2) : getDefaultVariant(parent);
55533
+ const variant = variantId2 ? variants.find((v15) => v15 && v15.id === variantId2) : getDefaultVariant(parent);
55158
55534
  if (!variant)
55159
55535
  return null;
55160
55536
  return getOpList(variant, "nodes");
@@ -56179,7 +56555,7 @@ function getOverrideBoundVariable(override, variables) {
56179
56555
  var _a2;
56180
56556
  if (!override.variableId || !variables)
56181
56557
  return void 0;
56182
- return (_a2 = variables.find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === override.variableId)) !== null && _a2 !== void 0 ? _a2 : void 0;
56558
+ return (_a2 = variables.find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === override.variableId)) !== null && _a2 !== void 0 ? _a2 : void 0;
56183
56559
  }
56184
56560
  function resolveNumberOrVariable(prop, variables, propOverrides) {
56185
56561
  if (prop === void 0 || prop === null)
@@ -56203,7 +56579,7 @@ function resolveNumberOrVariable(prop, variables, propOverrides) {
56203
56579
  }
56204
56580
  }
56205
56581
  if (variables) {
56206
- const variable = variables.find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === prop.variableId);
56582
+ const variable = variables.find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === prop.variableId);
56207
56583
  if (variable && variable.type === "number") {
56208
56584
  const numberValue = getNumberFromVariable(variable);
56209
56585
  if (numberValue !== void 0) {
@@ -56241,7 +56617,7 @@ function resolveEnumOrVariable(prop, variables, propOverrides) {
56241
56617
  }
56242
56618
  }
56243
56619
  if (variables) {
56244
- const variable = variables.find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === prop.variableId);
56620
+ const variable = variables.find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === prop.variableId);
56245
56621
  if (variable && variable.type === "enum" && variable.enumValue) {
56246
56622
  return variable.enumValue;
56247
56623
  }
@@ -56278,7 +56654,7 @@ function resolveColor2(color2, variables, propOverrides) {
56278
56654
  if (override === null || override === void 0 ? void 0 : override.colorValue) {
56279
56655
  let overrideColor = override.colorValue;
56280
56656
  if (overrideColor.variableId && variables) {
56281
- const innerVar = variables.find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === overrideColor.variableId);
56657
+ const innerVar = variables.find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === overrideColor.variableId);
56282
56658
  if (innerVar && innerVar.type === "color") {
56283
56659
  const innerColor = getColorFromVariable(innerVar);
56284
56660
  if (innerColor)
@@ -56298,7 +56674,7 @@ function resolveColor2(color2, variables, propOverrides) {
56298
56674
  }
56299
56675
  }
56300
56676
  if (variables) {
56301
- const variable = variables.find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === color2.variableId);
56677
+ const variable = variables.find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === color2.variableId);
56302
56678
  if (variable && variable.type === "color") {
56303
56679
  const colorValue = getColorFromVariable(variable);
56304
56680
  if (colorValue) {
@@ -56638,7 +57014,7 @@ var init_chunk_F4W4HJCD = __esm({
56638
57014
  return { l: 116 * f4 - 16, a: 500 * (C2(u3 / Ko) - f4) || 0, b: 200 * (f4 - C2(x6 / Vo)) || 0, alpha: s(s5, 3), colorSpace: "lab" };
56639
57015
  };
56640
57016
  Dn = (n5, o3) => {
56641
- let { l: t3, a: s5, b: r } = n5, { l, a: a5, b: u3 } = o3, e2 = Math.PI / 180, x6 = 180 / Math.PI, f4 = Math.sqrt(s5 ** 2 + r ** 2), wo = Math.sqrt(a5 ** 2 + u3 ** 2), v14 = ((f4 + wo) / 2) ** 7, V3 = 0.5 * (1 - Math.sqrt(v14 / (v14 + 25 ** 7))), Y3 = s5 * (1 + V3), Z4 = a5 * (1 + V3), k = Math.sqrt(Y3 ** 2 + r ** 2), $5 = Math.sqrt(Z4 ** 2 + u3 ** 2), N6 = (k + $5) / 2, W4 = Y3 === 0 && r === 0 ? 0 : Math.atan2(r, Y3) * x6, E = Z4 === 0 && u3 === 0 ? 0 : Math.atan2(u3, Z4) * x6, i3 = W4 < 0 ? W4 + 360 : W4, X4 = E < 0 ? E + 360 : E, q2 = X4 - i3, j3 = Math.abs(X4 - i3);
57017
+ let { l: t3, a: s5, b: r } = n5, { l, a: a5, b: u3 } = o3, e2 = Math.PI / 180, x6 = 180 / Math.PI, f4 = Math.sqrt(s5 ** 2 + r ** 2), wo = Math.sqrt(a5 ** 2 + u3 ** 2), v15 = ((f4 + wo) / 2) ** 7, V3 = 0.5 * (1 - Math.sqrt(v15 / (v15 + 25 ** 7))), Y3 = s5 * (1 + V3), Z4 = a5 * (1 + V3), k = Math.sqrt(Y3 ** 2 + r ** 2), $5 = Math.sqrt(Z4 ** 2 + u3 ** 2), N6 = (k + $5) / 2, W4 = Y3 === 0 && r === 0 ? 0 : Math.atan2(r, Y3) * x6, E = Z4 === 0 && u3 === 0 ? 0 : Math.atan2(u3, Z4) * x6, i3 = W4 < 0 ? W4 + 360 : W4, X4 = E < 0 ? E + 360 : E, q2 = X4 - i3, j3 = Math.abs(X4 - i3);
56642
57018
  j3 > 180 && X4 <= i3 ? q2 += 360 : j3 > 180 && X4 > i3 && (q2 -= 360);
56643
57019
  let J4 = (t3 + l) / 2, D2 = i3 + X4;
56644
57020
  j3 <= 180 ? D2 /= 2 : D2 = (i3 + X4 < 360 ? D2 + 360 : D2 - 360) / 2;
@@ -57353,7 +57729,7 @@ var init_a11y = __esm({
57353
57729
  });
57354
57730
 
57355
57731
  // ../../node_modules/.pnpm/@colordx+core@5.4.3/node_modules/@colordx/core/dist/plugins/p3.mjs
57356
- var $4, I3, v13, K2;
57732
+ var $4, I3, v14, K2;
57357
57733
  var init_p3 = __esm({
57358
57734
  "../../node_modules/.pnpm/@colordx+core@5.4.3/node_modules/@colordx/core/dist/plugins/p3.mjs"() {
57359
57735
  "use strict";
@@ -57362,7 +57738,7 @@ var init_p3 = __esm({
57362
57738
  init_chunk_IOXRZECH();
57363
57739
  $4 = (r, o3, e2) => cn(...z55(r, o3, e2));
57364
57740
  I3 = Math.PI / 180;
57365
- v13 = (r$1, o3, e$1) => {
57741
+ v14 = (r$1, o3, e$1) => {
57366
57742
  r$1.toGamutP3 = (n5) => {
57367
57743
  let t3 = jn(n5, xn2, $4);
57368
57744
  if (t3 === null) return new r$1(n5);
@@ -57376,7 +57752,7 @@ var init_p3 = __esm({
57376
57752
  return u3 < 1 ? `color(display-p3 ${t3} ${m4} ${a5} / ${u3})` : `color(display-p3 ${t3} ${m4} ${a5})`;
57377
57753
  }, o3.push(Sn, yn), e$1.push([Sn, "p3"], [yn, "p3"]);
57378
57754
  };
57379
- K2 = v13;
57755
+ K2 = v14;
57380
57756
  }
57381
57757
  });
57382
57758
 
@@ -57787,14 +58163,82 @@ var init_deployed_naming = __esm({
57787
58163
  });
57788
58164
 
57789
58165
  // ../shared/dist/utils/route-helpers.js
58166
+ function isReservedSlug(slug) {
58167
+ return RESERVED_SLUGS.includes(slug.toLowerCase());
58168
+ }
58169
+ function generateSlug(name) {
58170
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "page";
58171
+ }
58172
+ function getRouteFullPath(page, allPages) {
58173
+ if (page.isIndex && !page.parentPageId) {
58174
+ return "/";
58175
+ }
58176
+ const slugs = [];
58177
+ let currentPage = page;
58178
+ while (currentPage) {
58179
+ const isRootIndex = currentPage.isIndex && !currentPage.parentPageId;
58180
+ if (!isRootIndex) {
58181
+ const slug = currentPage.slug || generateSlug(currentPage.name || "page");
58182
+ slugs.unshift(slug);
58183
+ }
58184
+ if (currentPage.parentPageId) {
58185
+ currentPage = allPages.find((p4) => p4 && p4.id === currentPage.parentPageId);
58186
+ } else {
58187
+ currentPage = void 0;
58188
+ }
58189
+ }
58190
+ if (slugs.length === 0) {
58191
+ return "/";
58192
+ }
58193
+ if (slugs.length === 1 && slugs[0] === "index") {
58194
+ return "/";
58195
+ }
58196
+ return "/" + slugs.join("/");
58197
+ }
58198
+ function isSlugUnique(slug, parentId, pageId2, allPages) {
58199
+ const siblings = allPages.filter((p4) => p4 && p4.type === "route" && p4.parentPageId === parentId && p4.id !== pageId2);
58200
+ return !siblings.some((p4) => (p4.slug || generateSlug(p4.name || "")) === slug);
58201
+ }
58202
+ function generateUniqueSlug(name, parentId, pageId2, allPages) {
58203
+ let slug = generateSlug(name);
58204
+ let counter = 1;
58205
+ while (!isSlugUnique(slug, parentId, pageId2, allPages)) {
58206
+ slug = `${generateSlug(name)}-${counter}`;
58207
+ counter++;
58208
+ }
58209
+ return slug;
58210
+ }
58211
+ function getNextSortOrder(parentId, allPages) {
58212
+ const siblings = allPages.filter((p4) => p4 && p4.type === "route" && p4.parentPageId === parentId);
58213
+ if (siblings.length === 0) {
58214
+ return 0;
58215
+ }
58216
+ const maxOrder = Math.max(...siblings.map((p4) => p4.sortOrder || 0));
58217
+ return maxOrder + 1;
58218
+ }
58219
+ function shouldNewRouteBeIndex(allPages, route = {}) {
58220
+ if (route.parentPageId)
58221
+ return false;
58222
+ if (route.requestedIndex)
58223
+ return true;
58224
+ return !allPages.some((page) => page.type === "route" && page.isIndex && !page.parentPageId);
58225
+ }
57790
58226
  function indexRouteDeleteError(page) {
57791
58227
  return page.type === "route" && page.isIndex && !page.parentPageId ? INDEX_ROUTE_DELETE_MESSAGE : void 0;
57792
58228
  }
57793
- var INDEX_ROUTE_DELETE_MESSAGE, ROUTE_GUARD_REJECTION_CODE, ROUTE_GUARD_REJECTION_CODES;
58229
+ function clearOtherIndexPages(pageIdToKeep, allPages) {
58230
+ for (const page of allPages) {
58231
+ if (page && page.isIndex && page.id !== pageIdToKeep) {
58232
+ Page.update(page.id, { isIndex: false });
58233
+ }
58234
+ }
58235
+ }
58236
+ var RESERVED_SLUGS, INDEX_ROUTE_DELETE_MESSAGE, ROUTE_GUARD_REJECTION_CODE, ROUTE_GUARD_REJECTION_CODES;
57794
58237
  var init_route_helpers = __esm({
57795
58238
  "../shared/dist/utils/route-helpers.js"() {
57796
58239
  "use strict";
57797
58240
  init_page();
58241
+ RESERVED_SLUGS = ["_assets", "_worker"];
57798
58242
  INDEX_ROUTE_DELETE_MESSAGE = "You can't delete the index route. Set another route as the index first.";
57799
58243
  ROUTE_GUARD_REJECTION_CODE = {
57800
58244
  duplicateIndexRoute: "DUPLICATE_INDEX_ROUTE",
@@ -65753,7 +66197,8 @@ function uniqueProjectSlug(base, existingSlugs) {
65753
66197
  return `${base}-${counter}`;
65754
66198
  }
65755
66199
  function generateUniqueProjectSlug(name, existingSlugs) {
65756
- return uniqueProjectSlug(generateProjectSlug(name), existingSlugs);
66200
+ const suffix = crypto.randomUUID().replaceAll("-", "").slice(0, 12);
66201
+ return uniqueProjectSlug(`${generateProjectSlug(name)}-${suffix}`, existingSlugs);
65757
66202
  }
65758
66203
  var init_project_slug = __esm({
65759
66204
  "../shared/dist/utils/project-slug.js"() {
@@ -65805,7 +66250,7 @@ function createProjectRealms(projectId, organizationAdminsGroupId) {
65805
66250
  });
65806
66251
  return { realmsGroupId, realmIds };
65807
66252
  }
65808
- function createProjectInOrganization(organization, projectName, creator, { sharingMode = SharingModeTypes.Organization, orgAccessLevel = AccessLevelTypes.Collaborator, existingProjectSlugs = [], seedIndexRoute = false, seedBrandAndManifesto = false, onActionDispatched } = {}) {
66253
+ function createProjectInOrganization(organization, projectName, creator, { sharingMode = SharingModeTypes.Organization, orgAccessLevel = AccessLevelTypes.Collaborator, existingProjectSlugs = [], seedIndexRoute = false, seedInitialContent = true, seedBrandAndManifesto = false, initialBrief, onActionDispatched } = {}) {
65809
66254
  var _a2;
65810
66255
  let orgForGroups = organization;
65811
66256
  const needsMembers = sharingMode === SharingModeTypes.Organization;
@@ -65849,6 +66294,7 @@ function createProjectInOrganization(organization, projectName, creator, { shari
65849
66294
  mainBranchId: mainBranch.id,
65850
66295
  currentBranch: ref(mainBranch)
65851
66296
  });
66297
+ project = Object.assign(Object.assign({}, project), { mainBranchId: mainBranch.id, currentBranch: ref(mainBranch) });
65852
66298
  });
65853
66299
  if (createEvents[0])
65854
66300
  onActionDispatched === null || onActionDispatched === void 0 ? void 0 : onActionDispatched(createEvents[0].actionId);
@@ -65861,6 +66307,8 @@ function createProjectInOrganization(organization, projectName, creator, { shari
65861
66307
  branchId: mainBranch.id
65862
66308
  }
65863
66309
  };
66310
+ if (!seedInitialContent)
66311
+ return project;
65864
66312
  const seedEvents = batch(Object.assign(Object.assign({}, seedBatchOptions), {
65865
66313
  // The label names what the action actually carries, so this class of
65866
66314
  // regression stays visible in the event log.
@@ -65885,7 +66333,18 @@ function createProjectInOrganization(organization, projectName, creator, { shari
65885
66333
  if (seedBrandAndManifesto) {
65886
66334
  const seededBrand = Brand.create({});
65887
66335
  Branch2.update(mainBranch.id, { brand: ref(seededBrand) });
65888
- const seededManifesto = Manifesto.create({});
66336
+ const seededManifesto = Manifesto.create(initialBrief ? {
66337
+ purpose: ManifestoPurpose.create({
66338
+ oneLiner: initialBrief,
66339
+ provenance: ManifestoProvenance.create({
66340
+ basis: ManifestoBasisTypes.User,
66341
+ confidence: ManifestoConfidenceTypes.High,
66342
+ source: "user",
66343
+ stampedAt: /* @__PURE__ */ new Date()
66344
+ })
66345
+ }),
66346
+ status: ManifestoStatusTypes.Draft
66347
+ } : {});
65889
66348
  Branch2.update(mainBranch.id, { manifesto: ref(seededManifesto) });
65890
66349
  }
65891
66350
  });
@@ -66000,6 +66459,13 @@ var init_founding_card = __esm({
66000
66459
  }
66001
66460
  });
66002
66461
 
66462
+ // ../shared/dist/utils/brand-logo-trace.js
66463
+ var init_brand_logo_trace = __esm({
66464
+ "../shared/dist/utils/brand-logo-trace.js"() {
66465
+ "use strict";
66466
+ }
66467
+ });
66468
+
66003
66469
  // ../shared/dist/utils/code-file-text.js
66004
66470
  function normalizeCodeFileText(text) {
66005
66471
  const lf = normalizeEol(text);
@@ -67341,7 +67807,7 @@ var init_pr2 = __esm({
67341
67807
  // ../shared/dist/utils/variant-helpers.js
67342
67808
  function getValueName(group, valueId) {
67343
67809
  var _a2, _b;
67344
- return (_b = (_a2 = group.validValues) === null || _a2 === void 0 ? void 0 : _a2.find((v14) => v14.id === valueId)) === null || _b === void 0 ? void 0 : _b.name;
67810
+ return (_b = (_a2 = group.validValues) === null || _a2 === void 0 ? void 0 : _a2.find((v15) => v15.id === valueId)) === null || _b === void 0 ? void 0 : _b.name;
67345
67811
  }
67346
67812
  var init_variant_helpers = __esm({
67347
67813
  "../shared/dist/utils/variant-helpers.js"() {
@@ -87174,6 +87640,7 @@ var init_dist4 = __esm({
87174
87640
  init_id_generation();
87175
87641
  init_vectorNetwork2();
87176
87642
  init_founding_card();
87643
+ init_brand_logo_trace();
87177
87644
  init_code_file_text();
87178
87645
  init_code_file_batches();
87179
87646
  init_app_schema();
@@ -89257,7 +89724,7 @@ var CLI_VERSION;
89257
89724
  var init_version = __esm({
89258
89725
  "src/config/version.ts"() {
89259
89726
  "use strict";
89260
- CLI_VERSION = true ? "0.1.4" : "0.0.0-dev";
89727
+ CLI_VERSION = true ? "0.1.6" : "0.0.0-dev";
89261
89728
  }
89262
89729
  });
89263
89730
 
@@ -89391,8 +89858,8 @@ function distanceQuintic(p4, p0, p1, p22, p32) {
89391
89858
  function bernsteinRoots(w5, lo2, hi, out, depth) {
89392
89859
  let sign = 0;
89393
89860
  let crossings = 0;
89394
- for (const v14 of w5) {
89395
- const s5 = v14 > 1e-14 ? 1 : v14 < -1e-14 ? -1 : 0;
89861
+ for (const v15 of w5) {
89862
+ const s5 = v15 > 1e-14 ? 1 : v15 < -1e-14 ? -1 : 0;
89396
89863
  if (s5 !== 0) {
89397
89864
  if (sign !== 0 && s5 !== sign)
89398
89865
  crossings++;
@@ -89420,8 +89887,8 @@ function bernsteinRoots(w5, lo2, hi, out, depth) {
89420
89887
  const right = tri.map((row) => row[row.length - 1]).reverse();
89421
89888
  const mid = (lo2 + hi) / 2;
89422
89889
  let scale = 0;
89423
- for (const v14 of w5)
89424
- scale = Math.max(scale, Math.abs(v14));
89890
+ for (const v15 of w5)
89891
+ scale = Math.max(scale, Math.abs(v15));
89425
89892
  if (Math.abs(tri[n5 - 1][0]) <= 1e-12 * (1 + scale))
89426
89893
  out.push(mid);
89427
89894
  bernsteinRoots(left, lo2, mid, out, depth + 1);
@@ -89766,8 +90233,8 @@ var clamp, clamp01;
89766
90233
  var init_num = __esm({
89767
90234
  "../vector-engine/dist/vector/math/num.js"() {
89768
90235
  "use strict";
89769
- clamp = (v14, min, max) => v14 < min ? min : v14 > max ? max : v14;
89770
- clamp01 = (v14) => v14 < 0 ? 0 : v14 > 1 ? 1 : v14;
90236
+ clamp = (v15, min, max) => v15 < min ? min : v15 > max ? max : v15;
90237
+ clamp01 = (v15) => v15 < 0 ? 0 : v15 > 1 ? 1 : v15;
89771
90238
  }
89772
90239
  });
89773
90240
 
@@ -89914,8 +90381,8 @@ var init_linalg = __esm({
89914
90381
  // ../vector-engine/dist/vector/solver/math/cg.js
89915
90382
  function schurCG(rows, winv, n5, rhs) {
89916
90383
  const m4 = rows.length;
89917
- const apply = (v14, out) => {
89918
- const t3 = applyT(v14);
90384
+ const apply = (v15, out) => {
90385
+ const t3 = applyT(v15);
89919
90386
  for (let i3 = 0; i3 < m4; i3++) {
89920
90387
  const r3 = rows[i3];
89921
90388
  let s5 = 0;
@@ -89925,11 +90392,11 @@ function schurCG(rows, winv, n5, rhs) {
89925
90392
  }
89926
90393
  };
89927
90394
  const scratch = new Array(n5);
89928
- const applyT = (v14) => {
90395
+ const applyT = (v15) => {
89929
90396
  scratch.fill(0);
89930
90397
  for (let i3 = 0; i3 < m4; i3++) {
89931
90398
  const r3 = rows[i3];
89932
- const vi = v14[i3];
90399
+ const vi = v15[i3];
89933
90400
  if (vi === 0)
89934
90401
  continue;
89935
90402
  for (let k = 0; k < r3.cols.length; k++)
@@ -90224,22 +90691,22 @@ function galloc() {
90224
90691
  function resetPool() {
90225
90692
  poolIdx = 0;
90226
90693
  }
90227
- function dvar(v14, col) {
90694
+ function dvar(v15, col) {
90228
90695
  if (valuesOnly)
90229
- return { v: v14, g: EMPTY_GRAD };
90696
+ return { v: v15, g: EMPTY_GRAD };
90230
90697
  const g2 = galloc();
90231
90698
  g2.cols[0] = col;
90232
90699
  g2.vals[0] = 1;
90233
90700
  g2.n = 1;
90234
- return { v: v14, g: g2 };
90701
+ return { v: v15, g: g2 };
90235
90702
  }
90236
- function chain(v14, ka, a5, kb, b4) {
90703
+ function chain(v15, ka, a5, kb, b4) {
90237
90704
  if (valuesOnly)
90238
- return { v: v14, g: EMPTY_GRAD };
90705
+ return { v: v15, g: EMPTY_GRAD };
90239
90706
  const ag = a5.g;
90240
90707
  const bg = b4.g;
90241
90708
  if (bg.n === 0 && ag.n === 0)
90242
- return { v: v14, g: EMPTY_GRAD };
90709
+ return { v: v15, g: EMPTY_GRAD };
90243
90710
  const g2 = galloc();
90244
90711
  let i3 = 0;
90245
90712
  let j3 = 0;
@@ -90260,7 +90727,7 @@ function chain(v14, ka, a5, kb, b4) {
90260
90727
  n5++;
90261
90728
  }
90262
90729
  g2.n = n5;
90263
- return { v: v14, g: g2 };
90730
+ return { v: v15, g: g2 };
90264
90731
  }
90265
90732
  function div(a5, b4) {
90266
90733
  const bv = b4.v || 1;
@@ -90851,10 +91318,10 @@ function curvatureRows(sa, sb, atIn, ctx) {
90851
91318
  if (cubicSide && (sa.kind === "line" || sb.kind === "line")) {
90852
91319
  const fromA = at === cubicSide.a;
90853
91320
  const u3 = vsub(P4(fromA ? cubicSide.h1 : cubicSide.h2), P4(at));
90854
- const v14 = vsub(P4(fromA ? cubicSide.h2 : cubicSide.h1), P4(fromA ? cubicSide.h1 : cubicSide.h2));
91321
+ const v15 = vsub(P4(fromA ? cubicSide.h2 : cubicSide.h1), P4(fromA ? cubicSide.h1 : cubicSide.h2));
90855
91322
  return [
90856
91323
  ...tangentRows(sa, sb, atIn, ctx),
90857
- asRow(scl(1 / L3, div(cross(u3, v14), vlen(u3))))
91324
+ asRow(scl(1 / L3, div(cross(u3, v15), vlen(u3))))
90858
91325
  ];
90859
91326
  }
90860
91327
  return [
@@ -90876,7 +91343,7 @@ var init_residuals = __esm({
90876
91343
  pool = [];
90877
91344
  poolIdx = 0;
90878
91345
  valuesOnly = false;
90879
- dc = (v14) => ({ v: v14, g: EMPTY_GRAD });
91346
+ dc = (v15) => ({ v: v15, g: EMPTY_GRAD });
90880
91347
  lin = (ka, a5, kb, b4) => chain(ka * a5.v + kb * b4.v, ka, a5, kb, b4);
90881
91348
  add = (a5, b4) => lin(1, a5, 1, b4);
90882
91349
  sub = (a5, b4) => lin(1, a5, -1, b4);
@@ -91124,28 +91591,28 @@ function rankFilter(J4, c3, rel) {
91124
91591
  for (let r = 0; r < J4.length; r++) {
91125
91592
  const row = J4[r];
91126
91593
  let rowNorm = 0;
91127
- for (const v15 of row)
91128
- rowNorm += v15 * v15;
91594
+ for (const v16 of row)
91595
+ rowNorm += v16 * v16;
91129
91596
  rowNorm = Math.sqrt(rowNorm);
91130
91597
  if (rowNorm === 0)
91131
91598
  continue;
91132
- const v14 = row.slice();
91599
+ const v15 = row.slice();
91133
91600
  for (const b4 of basis) {
91134
91601
  let d4 = 0;
91135
- for (let i3 = 0; i3 < v14.length; i3++)
91136
- d4 += v14[i3] * b4[i3];
91137
- for (let i3 = 0; i3 < v14.length; i3++)
91138
- v14[i3] -= d4 * b4[i3];
91602
+ for (let i3 = 0; i3 < v15.length; i3++)
91603
+ d4 += v15[i3] * b4[i3];
91604
+ for (let i3 = 0; i3 < v15.length; i3++)
91605
+ v15[i3] -= d4 * b4[i3];
91139
91606
  }
91140
91607
  let pn2 = 0;
91141
- for (const e2 of v14)
91608
+ for (const e2 of v15)
91142
91609
  pn2 += e2 * e2;
91143
91610
  pn2 = Math.sqrt(pn2);
91144
91611
  if (pn2 <= rel * rowNorm)
91145
91612
  continue;
91146
- for (let i3 = 0; i3 < v14.length; i3++)
91147
- v14[i3] /= pn2;
91148
- basis.push(v14);
91613
+ for (let i3 = 0; i3 < v15.length; i3++)
91614
+ v15[i3] /= pn2;
91615
+ basis.push(v15);
91149
91616
  keptJ.push(row);
91150
91617
  keptC.push(c3[r]);
91151
91618
  kept.push(r);
@@ -91174,7 +91641,7 @@ function gram(J4, winv) {
91174
91641
  }
91175
91642
  function kktStep(rows, g2, n5, winv, rungFloor = 0, cg) {
91176
91643
  const free = rows.filter((r) => r.cols.length > 0);
91177
- const delta = g2.map((v14) => -v14);
91644
+ const delta = g2.map((v15) => -v15);
91178
91645
  if (free.length === 0)
91179
91646
  return delta;
91180
91647
  if (free.length >= CG_MIN_ROWS && rungFloor === 0 && !cg?.futile) {
@@ -91450,11 +91917,11 @@ function gramSpectrum(rows, withVectors) {
91450
91917
  if (rows.length === 0)
91451
91918
  return { rank: 0, values: [], vectors: [] };
91452
91919
  const { values, vectors } = jacobiEigen(gramMatrix(rows), withVectors);
91453
- const lmax = values.reduce((mx, v14) => Math.max(mx, v14), 0);
91920
+ const lmax = values.reduce((mx, v15) => Math.max(mx, v15), 0);
91454
91921
  const tol = GRAM_EIG_TOL * (lmax || 1);
91455
91922
  let rank = 0;
91456
- for (const v14 of values)
91457
- if (v14 > tol)
91923
+ for (const v15 of values)
91924
+ if (v15 > tol)
91458
91925
  rank++;
91459
91926
  return { rank, values, vectors };
91460
91927
  }
@@ -91546,10 +92013,10 @@ function solve(graph, constraints, trace) {
91546
92013
  const sub3 = subsystem(graph, cons, touched);
91547
92014
  const vm = buildVarMap(sub3.graph, [], new Set(Object.keys(sub3.graph.points)));
91548
92015
  for (const row of evalResidualValues(sub3.graph, sub3.constraints, [], vm)) {
91549
- const v14 = Math.abs(row.value);
91550
- if (v14 > cmax)
91551
- cmax = v14;
91552
- if (v14 >= EPS)
92016
+ const v15 = Math.abs(row.value);
92017
+ if (v15 > cmax)
92018
+ cmax = v15;
92019
+ if (v15 >= EPS)
91553
92020
  converged = false;
91554
92021
  }
91555
92022
  }
@@ -92166,7 +92633,7 @@ function bernProd3(f4, g2) {
92166
92633
  function cubicCircleTs(p4, c3, r) {
92167
92634
  const dx = p4.map((pt) => pt.x - c3.x);
92168
92635
  const dy = p4.map((pt) => pt.y - c3.y);
92169
- const w5 = bernProd3(dx, dx).map((v14, k) => v14 + bernProd3(dy, dy)[k] - r * r);
92636
+ const w5 = bernProd3(dx, dx).map((v15, k) => v15 + bernProd3(dy, dy)[k] - r * r);
92170
92637
  const raw = [];
92171
92638
  bernsteinRoots(w5, 0, 1, raw, 0);
92172
92639
  const xs = p4.map((q2) => q2.x);
@@ -92186,7 +92653,7 @@ function cubicCubicTs(pa, pb) {
92186
92653
  x1: Math.max(p4[0].x, p4[1].x, p4[2].x, p4[3].x),
92187
92654
  y1: Math.max(p4[0].y, p4[1].y, p4[2].y, p4[3].y)
92188
92655
  });
92189
- const overlaps = (u3, v14) => u3.x0 <= v14.x1 + HULL_PAD && v14.x0 <= u3.x1 + HULL_PAD && u3.y0 <= v14.y1 + HULL_PAD && v14.y0 <= u3.y1 + HULL_PAD;
92656
+ const overlaps = (u3, v15) => u3.x0 <= v15.x1 + HULL_PAD && v15.x0 <= u3.x1 + HULL_PAD && u3.y0 <= v15.y1 + HULL_PAD && v15.y0 <= u3.y1 + HULL_PAD;
92190
92657
  const stack = [{ a0: 0, a1: 1, ca: pa, b0: 0, b1: 1, cb: pb }];
92191
92658
  const seeds = [];
92192
92659
  let budget = 2e4;
@@ -92781,7 +93248,7 @@ function buildPlanar(graph, chains) {
92781
93248
  const ids = [...geoms.keys()];
92782
93249
  const nodes = [];
92783
93250
  const cells = /* @__PURE__ */ new Map();
92784
- const cellOf = (v14) => Math.floor(v14 / WELD_EPS);
93251
+ const cellOf = (v15) => Math.floor(v15 / WELD_EPS);
92785
93252
  const weld = (p4) => {
92786
93253
  const cx = cellOf(p4.x);
92787
93254
  const cy = cellOf(p4.y);
@@ -93520,10 +93987,10 @@ function faceCentroidOf(face, geoms) {
93520
93987
  const t3 = (t0 + t1) / 2 + (t1 - t0) / 2 * G8_N[i3];
93521
93988
  const w5 = G8_W[i3] * (t1 - t0) / 2;
93522
93989
  const p4 = geomPointAt(geom, t3);
93523
- const v14 = geomVelocityAt(geom, t3);
93524
- a5 += w5 * (p4.x * v14.y - p4.y * v14.x) / 2;
93525
- cx += w5 * p4.x * p4.x * v14.y / 2;
93526
- cy -= w5 * p4.y * p4.y * v14.x / 2;
93990
+ const v15 = geomVelocityAt(geom, t3);
93991
+ a5 += w5 * (p4.x * v15.y - p4.y * v15.x) / 2;
93992
+ cx += w5 * p4.x * p4.x * v15.y / 2;
93993
+ cy -= w5 * p4.y * p4.y * v15.x / 2;
93527
93994
  }
93528
93995
  }
93529
93996
  }
@@ -94027,8 +94494,8 @@ function isIdentity(m4) {
94027
94494
  function applyAffine(m4, p4) {
94028
94495
  return { x: m4.a * p4.x + m4.c * p4.y + m4.e, y: m4.b * p4.x + m4.d * p4.y + m4.f };
94029
94496
  }
94030
- function applyLinear(m4, v14) {
94031
- return { x: m4.a * v14.x + m4.c * v14.y, y: m4.b * v14.x + m4.d * v14.y };
94497
+ function applyLinear(m4, v15) {
94498
+ return { x: m4.a * v15.x + m4.c * v15.y, y: m4.b * v15.x + m4.d * v15.y };
94032
94499
  }
94033
94500
  function rotation(rad) {
94034
94501
  const cos = Math.cos(rad);
@@ -94419,7 +94886,7 @@ function resolveScope(path4, node) {
94419
94886
  if (!src || src.kind !== "path")
94420
94887
  continue;
94421
94888
  const srcFrame = pathFrame(src);
94422
- const map = (v14) => reframe(v14, srcFrame, dest);
94889
+ const map = (v15) => reframe(v15, srcFrame, dest);
94423
94890
  const addPoint = (pid) => {
94424
94891
  const p4 = src.graph.points[pid];
94425
94892
  if (!p4)
@@ -94665,7 +95132,7 @@ function readViewBox(attrSource) {
94665
95132
  if (!box)
94666
95133
  return null;
94667
95134
  const n5 = (box[1] ?? box[2] ?? "").trim().split(/[\s,]+/).map(Number);
94668
- if (n5.length !== 4 || !n5.every((v14) => Number.isFinite(v14)) || n5[2] <= 0 || n5[3] <= 0)
95135
+ if (n5.length !== 4 || !n5.every((v15) => Number.isFinite(v15)) || n5[2] <= 0 || n5[3] <= 0)
94669
95136
  return null;
94670
95137
  return { x: n5[0], y: n5[1], width: n5[2], height: n5[3] };
94671
95138
  }
@@ -94679,8 +95146,8 @@ function svgFrame(text) {
94679
95146
  return box;
94680
95147
  const dim2 = (name) => {
94681
95148
  const m4 = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "i").exec(attrs);
94682
- const v14 = m4 ? parseFloat((m4[1] ?? m4[2] ?? "").trim()) : NaN;
94683
- return Number.isFinite(v14) && v14 > 0 ? v14 : null;
95149
+ const v15 = m4 ? parseFloat((m4[1] ?? m4[2] ?? "").trim()) : NaN;
95150
+ return Number.isFinite(v15) && v15 > 0 ? v15 : null;
94684
95151
  };
94685
95152
  const w5 = dim2("width");
94686
95153
  const h4 = dim2("height");
@@ -94730,7 +95197,7 @@ function parseTransform(source) {
94730
95197
  read = re.lastIndex;
94731
95198
  const body = t3[2].trim();
94732
95199
  const n5 = body === "" ? [] : body.split(/[\s,]+/).map(Number);
94733
- if (n5.some((v14) => !Number.isFinite(v14)))
95200
+ if (n5.some((v15) => !Number.isFinite(v15)))
94734
95201
  return null;
94735
95202
  const step = transformStep(t3[1], n5);
94736
95203
  if (!step)
@@ -94751,7 +95218,7 @@ function len2(raw, fallback = 0) {
94751
95218
  function points(raw) {
94752
95219
  if (!raw)
94753
95220
  return [];
94754
- const n5 = raw.trim().split(/[\s,]+/).map(Number).filter((v14) => Number.isFinite(v14));
95221
+ const n5 = raw.trim().split(/[\s,]+/).map(Number).filter((v15) => Number.isFinite(v15));
94755
95222
  return n5.length % 2 === 0 ? n5 : n5.slice(0, -1);
94756
95223
  }
94757
95224
  function shapeElementPathData(name, attrs) {
@@ -94847,12 +95314,12 @@ function inheritPaint(parent, own) {
94847
95314
  }
94848
95315
  }
94849
95316
  for (const key of INHERITED_PAINT) {
94850
- const v14 = own[key];
94851
- if (v14 === void 0)
95317
+ const v15 = own[key];
95318
+ if (v15 === void 0)
94852
95319
  continue;
94853
- if (v14.trim().toLowerCase() === "inherit")
95320
+ if (v15.trim().toLowerCase() === "inherit")
94854
95321
  continue;
94855
- out[key] = v14;
95322
+ out[key] = v15;
94856
95323
  }
94857
95324
  return out;
94858
95325
  }
@@ -96145,7 +96612,7 @@ function polygonVertices(raw) {
96145
96612
  if (!raw)
96146
96613
  return null;
96147
96614
  const n5 = raw.trim().split(/[\s,]+/).map(Number);
96148
- if (n5.some((v14) => !Number.isFinite(v14)))
96615
+ if (n5.some((v15) => !Number.isFinite(v15)))
96149
96616
  return null;
96150
96617
  if (n5.length < 6 || n5.length % 2 !== 0)
96151
96618
  return null;
@@ -96311,9 +96778,9 @@ function parseSvgGradient(markup, resolve6) {
96311
96778
  let dy = at("y2", 0) - at("y1", 0);
96312
96779
  const t3 = parseTransform(attr("gradientTransform") ?? "");
96313
96780
  if (t3) {
96314
- const v14 = applyLinear(t3, { x: dx, y: dy });
96315
- dx = v14.x;
96316
- dy = v14.y;
96781
+ const v15 = applyLinear(t3, { x: dx, y: dy });
96782
+ dx = v15.x;
96783
+ dy = v15.y;
96317
96784
  }
96318
96785
  const angle = dx === 0 && dy === 0 ? void 0 : angleOf(dx, dy);
96319
96786
  return { type: "linear", stops, repeating, angle };
@@ -96374,11 +96841,11 @@ function fitCircle(pts) {
96374
96841
  if (Math.abs(d4) < 1e-12)
96375
96842
  return null;
96376
96843
  const rhs = [sxz, syz, sz];
96377
- const col = (i3) => m4.map((row, k) => row.map((v15, j3) => j3 === i3 ? rhs[k] : v15));
96844
+ const col = (i3) => m4.map((row, k) => row.map((v16, j3) => j3 === i3 ? rhs[k] : v16));
96378
96845
  const u3 = det(col(0)) / d4;
96379
- const v14 = det(col(1)) / d4;
96846
+ const v15 = det(col(1)) / d4;
96380
96847
  const w5 = det(col(2)) / d4;
96381
- const center = { x: u3 / 2, y: v14 / 2 };
96848
+ const center = { x: u3 / 2, y: v15 / 2 };
96382
96849
  const rsq = w5 + center.x * center.x + center.y * center.y;
96383
96850
  if (!(rsq > 0) || !Number.isFinite(rsq))
96384
96851
  return null;
@@ -96503,7 +96970,7 @@ function inferConstraints(graph, opts = {}) {
96503
96970
  own.add(pair(mine[i3], mine[j3]));
96504
96971
  }
96505
96972
  const places = Object.keys(graph.points).filter((id) => !skip.has(id));
96506
- const cell = (v14) => Math.round(v14 / quantum);
96973
+ const cell = (v15) => Math.round(v15 / quantum);
96507
96974
  const grid = /* @__PURE__ */ new Map();
96508
96975
  for (const id of places) {
96509
96976
  const p4 = graph.points[id];
@@ -96992,7 +97459,7 @@ function collapseRuns(points2, segments, chains, retired, welded, tol) {
96992
97459
  return s5 && (s5.kind === "line" || s5.kind === "arc") ? s5 : null;
96993
97460
  };
96994
97461
  const graph = { points: points2, segments };
96995
- const away = (s5, v14) => edgeTangentAt(graph, s5, v14);
97462
+ const away = (s5, v15) => edgeTangentAt(graph, s5, v15);
96996
97463
  const atVertex = /* @__PURE__ */ new Map();
96997
97464
  for (const s5 of Object.values(segments)) {
96998
97465
  if (s5.kind === "cubic")
@@ -97053,9 +97520,9 @@ function collapseRuns(points2, segments, chains, retired, welded, tol) {
97053
97520
  const ends = [];
97054
97521
  for (const side of ["a", "b"]) {
97055
97522
  let cur = start;
97056
- let v14 = cur[side];
97523
+ let v15 = cur[side];
97057
97524
  for (; ; ) {
97058
- const nxt = partner.get(cur.id)?.get(v14);
97525
+ const nxt = partner.get(cur.id)?.get(v15);
97059
97526
  const ns = nxt ? joinable(nxt) : null;
97060
97527
  if (!ns || done.has(ns.id))
97061
97528
  break;
@@ -97064,10 +97531,10 @@ function collapseRuns(points2, segments, chains, retired, welded, tol) {
97064
97531
  seq.unshift(ns.id);
97065
97532
  else
97066
97533
  seq.push(ns.id);
97067
- v14 = ns.a === v14 ? ns.b : ns.a;
97534
+ v15 = ns.a === v15 ? ns.b : ns.a;
97068
97535
  cur = ns;
97069
97536
  }
97070
- ends.push(v14);
97537
+ ends.push(v15);
97071
97538
  }
97072
97539
  if (seq.length < 2)
97073
97540
  continue;
@@ -98464,14 +98931,14 @@ var init_dist5 = __esm({
98464
98931
  function checkJsonSafe(value, rootPath = "value") {
98465
98932
  const violations2 = [];
98466
98933
  const seen = /* @__PURE__ */ new WeakSet();
98467
- const walk = (v14, path4) => {
98934
+ const walk = (v15, path4) => {
98468
98935
  var _a2;
98469
- if (v14 === null || v14 === void 0)
98936
+ if (v15 === null || v15 === void 0)
98470
98937
  return;
98471
- const t3 = typeof v14;
98938
+ const t3 = typeof v15;
98472
98939
  if (t3 === "number") {
98473
- if (!Number.isFinite(v14)) {
98474
- violations2.push({ path: path4, reason: `is ${String(v14)}` });
98940
+ if (!Number.isFinite(v15)) {
98941
+ violations2.push({ path: path4, reason: `is ${String(v15)}` });
98475
98942
  }
98476
98943
  return;
98477
98944
  }
@@ -98481,7 +98948,7 @@ function checkJsonSafe(value, rootPath = "value") {
98481
98948
  }
98482
98949
  if (t3 !== "object")
98483
98950
  return;
98484
- const o3 = v14;
98951
+ const o3 = v15;
98485
98952
  if (seen.has(o3)) {
98486
98953
  violations2.push({ path: path4, reason: "is a cycle" });
98487
98954
  return;
@@ -98517,7 +98984,7 @@ var init_jsonSafety = __esm({
98517
98984
  "use strict";
98518
98985
  JsonSafetyError = class extends Error {
98519
98986
  constructor(violations2) {
98520
- const shown = violations2.slice(0, 5).map((v14) => `${v14.path} ${v14.reason}`).join(", ");
98987
+ const shown = violations2.slice(0, 5).map((v15) => `${v15.path} ${v15.reason}`).join(", ");
98521
98988
  const more = violations2.length > 5 ? ` (+${violations2.length - 5} more)` : "";
98522
98989
  super(`Refusing to write a vector value that a document cannot carry: ${shown}${more}. Nothing downstream catches this \u2014 Opaque persists NaN as null and OpJson skips validation on write, so the row would become permanently unreadable.`);
98523
98990
  this.name = "JsonSafetyError";
@@ -98871,7 +99338,7 @@ function gradientReference(gradient, id, defs) {
98871
99338
  return `url(#${id})`;
98872
99339
  }
98873
99340
  function serializeGradientDef(def3) {
98874
- const attrs = Object.entries(def3.attrs).map(([k, v14]) => `${attrName(k)}="${escapeAttr(String(v14))}"`).join(" ");
99341
+ const attrs = Object.entries(def3.attrs).map(([k, v15]) => `${attrName(k)}="${escapeAttr(String(v15))}"`).join(" ");
98875
99342
  const stops = def3.stops.map((s5) => `<stop offset="${escapeAttr(s5.offset)}" stop-color="${escapeAttr(s5.color)}"/>`).join("");
98876
99343
  return `<${def3.tag} ${attrs}>${stops}</${def3.tag}>`;
98877
99344
  }
@@ -100781,8 +101248,8 @@ function createBackgroundsList(specs) {
100781
101248
  function isOpaqueObject(value) {
100782
101249
  if (!value || typeof value !== "object")
100783
101250
  return false;
100784
- const v14 = value;
100785
- return "id" in v14 && "modelName" in v14;
101251
+ const v15 = value;
101252
+ return "id" in v15 && "modelName" in v15;
100786
101253
  }
100787
101254
  var init_creators = __esm({
100788
101255
  "../shared/dist/ai/executor/property-updater/creators.js"() {
@@ -100801,20 +101268,20 @@ function isMeasurementValue(value) {
100801
101268
  function isBlurSpec(value) {
100802
101269
  if (typeof value !== "object" || value === null)
100803
101270
  return false;
100804
- const v14 = value;
100805
- return "value" in v14;
101271
+ const v15 = value;
101272
+ return "value" in v15;
100806
101273
  }
100807
101274
  function isDimensionsSpec(value) {
100808
101275
  if (typeof value !== "object" || value === null)
100809
101276
  return false;
100810
- const v14 = value;
100811
- return "top" in v14 || "right" in v14 || "bottom" in v14 || "left" in v14;
101277
+ const v15 = value;
101278
+ return "top" in v15 || "right" in v15 || "bottom" in v15 || "left" in v15;
100812
101279
  }
100813
101280
  function isCornersSpec(value) {
100814
101281
  if (typeof value !== "object" || value === null)
100815
101282
  return false;
100816
- const v14 = value;
100817
- return "topLeft" in v14 || "topRight" in v14 || "bottomLeft" in v14 || "bottomRight" in v14;
101283
+ const v15 = value;
101284
+ return "topLeft" in v15 || "topRight" in v15 || "bottomLeft" in v15 || "bottomRight" in v15;
100818
101285
  }
100819
101286
  var init_types6 = __esm({
100820
101287
  "../shared/dist/ai/executor/property-updater/types.js"() {
@@ -101194,6 +101661,9 @@ function createComponentInBranch(branch, name) {
101194
101661
  getOpList(branch, "components").prepend(component);
101195
101662
  return component;
101196
101663
  }
101664
+ function createPageBodyNode(overrides) {
101665
+ return createNodeWithDefaults("page", Object.assign(Object.assign({}, overrides), { node: Object.assign({ name: PAGE_BODY_NAME, sortOrder: 1 }, overrides === null || overrides === void 0 ? void 0 : overrides.node) }));
101666
+ }
101197
101667
  function createPageInBranch(branch, name) {
101198
101668
  const page = Page.create({
101199
101669
  name,
@@ -101203,7 +101673,20 @@ function createPageInBranch(branch, name) {
101203
101673
  getOpList(branch, "pages").prepend(page);
101204
101674
  return page;
101205
101675
  }
101206
- var newCanvas;
101676
+ function createRouteInBranch(branch, name, route) {
101677
+ const siblings = getOpList(branch, "pages").asArray().filter((p4) => p4 != null);
101678
+ const isIndex = shouldNewRouteBeIndex(siblings, {
101679
+ requestedIndex: route.isIndex,
101680
+ parentPageId: route.parentPageId
101681
+ });
101682
+ if (isIndex) {
101683
+ clearOtherIndexPages("", siblings);
101684
+ }
101685
+ const page = Page.create(Object.assign(Object.assign({ type: PageTypes.Route, name, slug: route.slug, sortOrder: route.sortOrder, parentPageId: route.parentPageId }, isIndex ? { isIndex: true } : {}), { nodes: ListOfNodes.create([createPageBodyNode()]), canvas: ref(newCanvas()) }));
101686
+ getOpList(branch, "pages").append(page);
101687
+ return page;
101688
+ }
101689
+ var newCanvas, PAGE_BODY_NAME;
101207
101690
  var init_canvas_authoring = __esm({
101208
101691
  "../shared/dist/ai/executor/canvas-authoring.js"() {
101209
101692
  "use strict";
@@ -101212,6 +101695,89 @@ var init_canvas_authoring = __esm({
101212
101695
  init_schema();
101213
101696
  init_sanitizeIdentifier();
101214
101697
  newCanvas = (overrides) => Canvas.create(Object.assign({ zoom: 1, position: { x: 0, y: 0 }, backgroundColor: canvasBackgroundColor() }, overrides));
101698
+ PAGE_BODY_NAME = "Body";
101699
+ }
101700
+ });
101701
+
101702
+ // ../shared/dist/ai/executor/routeCreation.js
101703
+ function planRouteCreation(action, pages) {
101704
+ var _a2, _b;
101705
+ const parentPageId = action.parentPageId;
101706
+ if (parentPageId !== void 0) {
101707
+ const parent = pages.find((page) => page.id === parentPageId);
101708
+ if (!parent || parent.type !== PageTypes.Route) {
101709
+ return {
101710
+ error: "parentPageId must name an existing route in this branch."
101711
+ };
101712
+ }
101713
+ if (action.isIndex) {
101714
+ return { error: "Only a root route can be the homepage." };
101715
+ }
101716
+ }
101717
+ const name = (_a2 = action.name) !== null && _a2 !== void 0 ? _a2 : nextSequentialName(pages.filter((page) => page.type === PageTypes.Route && page.parentPageId === parentPageId).map((page) => page.name), (n5) => `Route ${n5}`);
101718
+ const isIndex = shouldNewRouteBeIndex(pages, {
101719
+ requestedIndex: action.isIndex,
101720
+ parentPageId
101721
+ });
101722
+ let slug = (_b = action.slug) !== null && _b !== void 0 ? _b : generateUniqueSlug(name, parentPageId, "", pages);
101723
+ if (isReservedSlug(slug)) {
101724
+ return { error: `The slug "${slug}" is reserved.` };
101725
+ }
101726
+ if (!isSlugUnique(slug, parentPageId, "", pages)) {
101727
+ return {
101728
+ error: `Another route at this level already uses the slug "${slug}".`
101729
+ };
101730
+ }
101731
+ const existing = pages.filter((page) => page.type === PageTypes.Route).map((page) => ({
101732
+ id: page.id,
101733
+ name: page.name,
101734
+ slug: page.slug,
101735
+ parentPageId: page.parentPageId,
101736
+ isIndex: isIndex ? false : page.isIndex
101737
+ }));
101738
+ const paths = /* @__PURE__ */ new Set();
101739
+ for (const page of existing) {
101740
+ const path5 = getRouteFullPath(page, existing);
101741
+ if (paths.has(path5)) {
101742
+ return {
101743
+ error: `Route creation would leave multiple routes at "${path5}". Resolve the conflicting routes first.`
101744
+ };
101745
+ }
101746
+ paths.add(path5);
101747
+ }
101748
+ const candidate = { id: "", name, slug, parentPageId, isIndex };
101749
+ let path4 = getRouteFullPath(candidate, existing);
101750
+ const baseSlug = slug;
101751
+ let suffix = 1;
101752
+ while (paths.has(path4)) {
101753
+ if (action.slug !== void 0 || isIndex) {
101754
+ return {
101755
+ error: `Another route already publishes at "${path4}". Choose a different slug.`
101756
+ };
101757
+ }
101758
+ slug = `${baseSlug}-${suffix++}`;
101759
+ if (!isSlugUnique(slug, parentPageId, "", pages))
101760
+ continue;
101761
+ candidate.slug = slug;
101762
+ path4 = getRouteFullPath(candidate, existing);
101763
+ }
101764
+ return {
101765
+ name,
101766
+ path: path4,
101767
+ route: {
101768
+ slug,
101769
+ parentPageId,
101770
+ isIndex,
101771
+ sortOrder: getNextSortOrder(parentPageId, pages)
101772
+ }
101773
+ };
101774
+ }
101775
+ var init_routeCreation = __esm({
101776
+ "../shared/dist/ai/executor/routeCreation.js"() {
101777
+ "use strict";
101778
+ init_page();
101779
+ init_route_helpers();
101780
+ init_canvas_authoring();
101215
101781
  }
101216
101782
  });
101217
101783
 
@@ -101803,6 +102369,8 @@ function executeAction(action, context) {
101803
102369
  return executeDeleteComponent(action, context);
101804
102370
  case "createPage":
101805
102371
  return executeCreatePage(action, context);
102372
+ case "createRoute":
102373
+ return executeCreateRoute(action, context);
101806
102374
  case "updatePage":
101807
102375
  return executeUpdatePage(action, context);
101808
102376
  case "moveNode":
@@ -103128,6 +103696,34 @@ function executeCreatePage(action, context) {
103128
103696
  const page = createPageInBranch(branch, name);
103129
103697
  return { action, success: true, data: { pageId: page.id } };
103130
103698
  }
103699
+ function executeCreateRoute(action, context) {
103700
+ var _a2;
103701
+ const definition = OPAQUE_ACTIONS.find((entry) => entry.type === "createRoute");
103702
+ const parsed = definition.input.safeParse(action);
103703
+ if (!parsed.success) {
103704
+ return { action, success: false, error: parsed.error.message };
103705
+ }
103706
+ const pages = Array.from(getBranchPages(context.branch)).filter((page2) => page2 != null);
103707
+ const plan = planRouteCreation(Object.assign({ type: "createRoute" }, parsed.data), pages);
103708
+ if (plan.error !== void 0) {
103709
+ return { action, success: false, error: plan.error };
103710
+ }
103711
+ const page = createRouteInBranch(context.branch, plan.name, plan.route);
103712
+ const body = findPageBodyNode(pageNodes(page));
103713
+ return {
103714
+ action,
103715
+ success: true,
103716
+ data: {
103717
+ pageId: page.id,
103718
+ bodyNodeId: body.id,
103719
+ name: plan.name,
103720
+ slug: plan.route.slug,
103721
+ path: plan.path,
103722
+ isIndex: plan.route.isIndex,
103723
+ parentPageId: (_a2 = plan.route.parentPageId) !== null && _a2 !== void 0 ? _a2 : null
103724
+ }
103725
+ };
103726
+ }
103131
103727
  function executeDeletePage(action, context) {
103132
103728
  const { branch } = context;
103133
103729
  if (!action.pageId) {
@@ -103256,8 +103852,8 @@ function executeCreateComponentProp(action, context) {
103256
103852
  }
103257
103853
  const exposedIds = new Set(Array.from(propsOf(component)).filter((p4) => p4 !== null).map((p4) => p4.variableId));
103258
103854
  const themeVariables = getDefaultThemeVariables2(branch);
103259
- const allVariables = themeVariables ? Array.from(themeVariablesVariables(themeVariables)).filter((v14) => v14 !== null) : [];
103260
- const exposedNames = new Set(allVariables.filter((v14) => exposedIds.has(v14.id)).map((v14) => v14.name));
103855
+ const allVariables = themeVariables ? Array.from(themeVariablesVariables(themeVariables)).filter((v15) => v15 !== null) : [];
103856
+ const exposedNames = new Set(allVariables.filter((v15) => exposedIds.has(v15.id)).map((v15) => v15.name));
103261
103857
  if (exposedNames.has(normalizedName)) {
103262
103858
  return {
103263
103859
  action,
@@ -103343,7 +103939,7 @@ function executeSetPropValue(action, context) {
103343
103939
  };
103344
103940
  }
103345
103941
  const themeVariables = getDefaultThemeVariables2(branch);
103346
- const variable = themeVariables ? Array.from(themeVariablesVariables(themeVariables)).find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === prop.variableId) : void 0;
103942
+ const variable = themeVariables ? Array.from(themeVariablesVariables(themeVariables)).find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === prop.variableId) : void 0;
103347
103943
  if (!variable) {
103348
103944
  return {
103349
103945
  action,
@@ -103726,11 +104322,11 @@ function executeGetCanvasState(action, context) {
103726
104322
  };
103727
104323
  const themeVariables = getDefaultThemeVariables2(branch);
103728
104324
  if (themeVariables) {
103729
- for (const v14 of themeVariablesVariables(themeVariables)) {
103730
- if (!v14 || v14.componentId)
104325
+ for (const v15 of themeVariablesVariables(themeVariables)) {
104326
+ if (!v15 || v15.componentId)
103731
104327
  continue;
103732
104328
  variablesSummary.totalCount++;
103733
- const type = v14.type || "unknown";
104329
+ const type = v15.type || "unknown";
103734
104330
  variablesSummary.byType[type] = (variablesSummary.byType[type] || 0) + 1;
103735
104331
  }
103736
104332
  }
@@ -103975,14 +104571,14 @@ function serializeNodeDetailsForAI(rawNode, branchId, branch, hasChildren = fals
103975
104571
  appearance && branchId ? resolveLiveRef(appearance[side], branchId) : appearance === null || appearance === void 0 ? void 0 : appearance[side]
103976
104572
  ]);
103977
104573
  let bg;
103978
- const val = (v14) => {
104574
+ const val = (v15) => {
103979
104575
  var _a3;
103980
- return (_a3 = derefEntity(v14, branchId)) === null || _a3 === void 0 ? void 0 : _a3.value;
104576
+ return (_a3 = derefEntity(v15, branchId)) === null || _a3 === void 0 ? void 0 : _a3.value;
103981
104577
  };
103982
- const measure = (v14) => measurementText(derefEntity(v14, branchId));
103983
- const put = (key, v14) => {
103984
- if (v14 !== void 0 && v14 !== null)
103985
- styles[key] = v14;
104578
+ const measure = (v15) => measurementText(derefEntity(v15, branchId));
104579
+ const put = (key, v15) => {
104580
+ if (v15 !== void 0 && v15 !== null)
104581
+ styles[key] = v15;
103986
104582
  };
103987
104583
  if (layout) {
103988
104584
  const l = layout;
@@ -104066,7 +104662,7 @@ function serializeNodeDetailsForAI(rawNode, branchId, branch, hasChildren = fals
104066
104662
  corners[corner] = text2;
104067
104663
  }
104068
104664
  const values = Object.values(corners);
104069
- styles.borderRadius = values.length === 4 && values.every((v14) => v14 === values[0]) ? values[0] : corners;
104665
+ styles.borderRadius = values.length === 4 && values.every((v15) => v15 === values[0]) ? values[0] : corners;
104070
104666
  }
104071
104667
  if (text) {
104072
104668
  if (((_b = text.fontSize) === null || _b === void 0 ? void 0 : _b.value) !== void 0) {
@@ -104338,10 +104934,10 @@ function executeQueryVariants(action, context) {
104338
104934
  continue;
104339
104935
  const values = [];
104340
104936
  if (group.validValues) {
104341
- for (const v14 of group.validValues) {
104342
- if (!v14)
104937
+ for (const v15 of group.validValues) {
104938
+ if (!v15)
104343
104939
  continue;
104344
- values.push({ id: v14.id, name: v14.name });
104940
+ values.push({ id: v15.id, name: v15.name });
104345
104941
  }
104346
104942
  }
104347
104943
  variantGroups.push({
@@ -104538,7 +105134,7 @@ function executeAddVariantGroupValue(action, context) {
104538
105134
  }
104539
105135
  const group = component.variantGroups[groupIndex];
104540
105136
  const currentValues = group.validValues || [];
104541
- if (currentValues.some((v14) => v14.name === action.valueName)) {
105137
+ if (currentValues.some((v15) => v15.name === action.valueName)) {
104542
105138
  return {
104543
105139
  action,
104544
105140
  success: false,
@@ -104606,10 +105202,10 @@ function executeCreateVariant(action, context) {
104606
105202
  error: `Component not found: ${action.componentId}`
104607
105203
  };
104608
105204
  }
104609
- const existingVariants = componentVariants(component).filter((v14) => v14 !== null && v14 !== void 0);
105205
+ const existingVariants = componentVariants(component).filter((v15) => v15 !== null && v15 !== void 0);
104610
105206
  let sourceVariant;
104611
105207
  if (action.sourceVariantId) {
104612
- sourceVariant = existingVariants.find((v14) => v14.id === action.sourceVariantId);
105208
+ sourceVariant = existingVariants.find((v15) => v15.id === action.sourceVariantId);
104613
105209
  if (!sourceVariant) {
104614
105210
  return {
104615
105211
  action,
@@ -104618,7 +105214,7 @@ function executeCreateVariant(action, context) {
104618
105214
  };
104619
105215
  }
104620
105216
  } else {
104621
- sourceVariant = existingVariants.find((v14) => !v14.variantGroups);
105217
+ sourceVariant = existingVariants.find((v15) => !v15.variantGroups);
104622
105218
  if (!sourceVariant && existingVariants.length > 0) {
104623
105219
  sourceVariant = existingVariants[0];
104624
105220
  }
@@ -104638,16 +105234,16 @@ function executeCreateVariant(action, context) {
104638
105234
  return {
104639
105235
  action,
104640
105236
  success: false,
104641
- error: `createVariant: ${valueId} is not a value of group "${group.name}". Its values are ${((_b = group.validValues) !== null && _b !== void 0 ? _b : []).map((v14) => `${v14.name} (${v14.id})`).join(", ")}.`
105237
+ error: `createVariant: ${valueId} is not a value of group "${group.name}". Its values are ${((_b = group.validValues) !== null && _b !== void 0 ? _b : []).map((v15) => `${v15.name} (${v15.id})`).join(", ")}.`
104642
105238
  };
104643
105239
  }
104644
105240
  }
104645
- const requested = Object.entries(action.variantGroupValues).sort(([a5], [b4]) => a5.localeCompare(b4)).map(([g2, v14]) => `${g2}=${v14}`).join(",");
105241
+ const requested = Object.entries(action.variantGroupValues).sort(([a5], [b4]) => a5.localeCompare(b4)).map(([g2, v15]) => `${g2}=${v15}`).join(",");
104646
105242
  const collision = existingVariants.find((variant) => {
104647
105243
  const groupsOf = variant === null || variant === void 0 ? void 0 : variant.variantGroups;
104648
105244
  if (!groupsOf)
104649
105245
  return false;
104650
- const key = Object.entries(groupsOf).sort(([a5], [b4]) => a5.localeCompare(b4)).map(([g2, v14]) => `${g2}=${v14}`).join(",");
105246
+ const key = Object.entries(groupsOf).sort(([a5], [b4]) => a5.localeCompare(b4)).map(([g2, v15]) => `${g2}=${v15}`).join(",");
104651
105247
  return key === requested;
104652
105248
  });
104653
105249
  if (collision) {
@@ -105826,9 +106422,9 @@ function findBranchVariable(branch, variableId) {
105826
106422
  const themeVariables = getDefaultThemeVariables2(branch);
105827
106423
  if (!themeVariables)
105828
106424
  return void 0;
105829
- for (const v14 of themeVariablesVariables(themeVariables)) {
105830
- if (v14 && v14.id === variableId)
105831
- return v14;
106425
+ for (const v15 of themeVariablesVariables(themeVariables)) {
106426
+ if (v15 && v15.id === variableId)
106427
+ return v15;
105832
106428
  }
105833
106429
  return void 0;
105834
106430
  }
@@ -105918,8 +106514,8 @@ function executeCreateSlot(action, context) {
105918
106514
  error: `createSlot: node ${action.nodeId} already holds slot ${box.slotId}. One box, one slot.`
105919
106515
  };
105920
106516
  }
105921
- const existing = ((_a2 = component.slots) !== null && _a2 !== void 0 ? _a2 : []).filter((v14) => !!v14);
105922
- const clash = existing.find((v14) => v14.name === action.name);
106517
+ const existing = ((_a2 = component.slots) !== null && _a2 !== void 0 ? _a2 : []).filter((v15) => !!v15);
106518
+ const clash = existing.find((v15) => v15.name === action.name);
105923
106519
  if (clash) {
105924
106520
  return {
105925
106521
  action,
@@ -106474,7 +107070,7 @@ function executeCreateNodeEvent(action, context) {
106474
107070
  };
106475
107071
  }
106476
107072
  const themeVariables = getDefaultThemeVariables2(branch);
106477
- targetVariable = themeVariables ? (_b = Array.from(themeVariablesVariables(themeVariables)).find((v14) => (v14 === null || v14 === void 0 ? void 0 : v14.id) === prop.variableId)) !== null && _b !== void 0 ? _b : void 0 : void 0;
107073
+ targetVariable = themeVariables ? (_b = Array.from(themeVariablesVariables(themeVariables)).find((v15) => (v15 === null || v15 === void 0 ? void 0 : v15.id) === prop.variableId)) !== null && _b !== void 0 ? _b : void 0 : void 0;
106478
107074
  if (!targetVariable) {
106479
107075
  return {
106480
107076
  action,
@@ -106889,6 +107485,8 @@ var init_actionExecutor = __esm({
106889
107485
  init_pageStructure();
106890
107486
  init_nodeUtils();
106891
107487
  init_canvas_authoring();
107488
+ init_routeCreation();
107489
+ init_opaqueActions();
106892
107490
  init_sanitizeIdentifier();
106893
107491
  init_variableCreation();
106894
107492
  init_codeFileVerbs();
@@ -107968,8 +108566,8 @@ var init_ai_action_api = __esm({
107968
108566
  instanceComponentId: "string (optional) - Required when nodeType is 'instance': the component this instance renders (e.g. 'cp_...'; older ids use 'op_...').",
107969
108567
  instanceVariantId: "string (optional) - Optional: the variant of instanceComponentId this instance points at. Defaults to the master component's default variant.",
107970
108568
  formElementType: "string (optional) - For nodeType 'input': which form control this node renders (default 'text').",
107971
- svgPath: "string (optional) - Required when nodeType is 'vector': the SVG markup/path.",
107972
- shaderLayers: "string[] (optional) - Shader layer specs, one per layer. Each matches the ShaderLayer shape: name (string), sortOrder (number), visible (boolean), effectType (e.g. 'meshGradient', 'perlinNoise'), blendMode, opacity/animated/animationSpeed/scale/offsetX/offsetY/rotation and effect-specific numeric params as plain numbers, color1/color2/color3/colorBack as hex or rgb(a) strings, colors as an array of such strings.",
108569
+ svgPath: "string (optional) - Required when nodeType is 'vector'. Accepts a complete SVG document or bare path data. Imports supported geometry into an editable vector network. The renderer uses SVG baked from that network, not the original document. Supply a viewBox to define the artwork dimensions. Check warnings for unsupported elements. This is not a runtime for scripts or SVG/CSS animation.",
108570
+ shaderLayers: "string[] (optional) - Shader layer specs, one per layer. Each matches the ShaderLayer shape: name (string), sortOrder (number), visible (boolean), effectType (e.g. 'meshGradient', 'perlinNoise'), blendMode, opacity/animated/animationSpeed/scale/offsetX/offsetY/rotation and effect-specific numeric params as plain numbers, color1/color2/color3/colorBack as hex or rgb(a) strings, colors as an array of such strings. Only catalog effects are supported; this field does not accept custom GLSL or WGSL source.",
107973
108571
  updates: "object (optional) - Initial/updated values: `styles` for visual properties, `name` for the layer name, `innerText` for text content (text nodes only), `svgPath` to promote to vector, `shaderLayers` for shader effects, plus the semantic node fields (placeholder, inputValue, inputTextType, htmlTag, className, url, target, frameUrl)."
107974
108572
  }
107975
108573
  },
@@ -108012,11 +108610,21 @@ var init_ai_action_api = __esm({
108012
108610
  },
108013
108611
  {
108014
108612
  type: "createPage",
108015
- description: "Create a new draft page with no nodes. Auto-names 'Page N' (deduped) when name is omitted.",
108613
+ description: "Create a new draft page with no nodes. Auto-names 'Page N' (deduped) when name is omitted. Use createRoute for a page that publishes to a URL.",
108016
108614
  parameters: {
108017
108615
  name: "string (optional) - Name for the new page (auto-generated if omitted)"
108018
108616
  }
108019
108617
  },
108618
+ {
108619
+ type: "createRoute",
108620
+ description: "Create a publishable route with one Body node. Returns pageId, bodyNodeId, slug, path, and isIndex. Add content inside bodyNodeId with createNode or applyCanvasEdits. The first root route becomes the homepage automatically. An explicit isIndex claim transfers the homepage from the current route. Use parentPageId to nest routes; slug is one URL segment. Deployment remains a separate action.",
108621
+ parameters: {
108622
+ name: "string (optional) - Display name. Defaults to a unique Route N.",
108623
+ slug: "string (optional) - URL segment, such as signal-noise. Omit to generate a unique slug from the name. Explicit collisions are rejected.",
108624
+ parentPageId: "string (optional) - ID of the page (e.g. 'pg_...'; older ids use 'op_...')",
108625
+ isIndex: "boolean (optional) - Set true to make this root route the homepage. The first root route is always the homepage."
108626
+ }
108627
+ },
108020
108628
  {
108021
108629
  type: "moveNode",
108022
108630
  description: "Reparent a node within its page or variant. Pass no newParentNodeId to move it to the top level. Cannot move a node between pages or variants, or into its own subtree.",
@@ -111774,11 +112382,11 @@ function TextInput({
111774
112382
  }
111775
112383
  if (key.backspace || key.delete) {
111776
112384
  setTouched(true);
111777
- setValue((v14) => v14.slice(0, -1));
112385
+ setValue((v15) => v15.slice(0, -1));
111778
112386
  return;
111779
112387
  }
111780
112388
  if (input && !key.ctrl && !key.meta && !key.escape) {
111781
- setValue((v14) => touched ? v14 + input : input);
112389
+ setValue((v15) => touched ? v15 + input : input);
111782
112390
  setTouched(true);
111783
112391
  }
111784
112392
  });
@@ -111854,7 +112462,7 @@ var confirm = (prompt, yes, no) => function Confirm({ value, onFinish }) {
111854
112462
  { value: "yes", label: yes },
111855
112463
  { value: "no", label: no }
111856
112464
  ],
111857
- onSelect: (v14) => onFinish?.(v14 === "yes")
112465
+ onSelect: (v15) => onFinish?.(v15 === "yes")
111858
112466
  }
111859
112467
  )
111860
112468
  ] });
@@ -111977,7 +112585,7 @@ function createFlow() {
111977
112585
  /* @__PURE__ */ jsx2(Em, { children: spec.prompt }),
111978
112586
  spec.hint ? /* @__PURE__ */ jsx2(Dim, { children: spec.hint }) : null
111979
112587
  ] }),
111980
- /* @__PURE__ */ jsx2(Select, { items: spec.options, onSelect: (v14) => onFinish?.(v14) })
112588
+ /* @__PURE__ */ jsx2(Select, { items: spec.options, onSelect: (v15) => onFinish?.(v15) })
111981
112589
  ] });
111982
112590
  };
111983
112591
  do {
@@ -112103,7 +112711,7 @@ var intro = ({
112103
112711
  { value: "continue", label: copy.goLabel },
112104
112712
  { value: "exit", label: "Exit" }
112105
112713
  ],
112106
- onSelect: (v14) => onFinish?.(v14 === "continue")
112714
+ onSelect: (v15) => onFinish?.(v15 === "continue")
112107
112715
  }
112108
112716
  ) : null
112109
112717
  ] });
@@ -112162,7 +112770,7 @@ var askProjectName = (initial) => function AskProjectName({ value, onFinish }) {
112162
112770
  TextInput,
112163
112771
  {
112164
112772
  initial,
112165
- onSubmit: (v14) => onFinish?.(v14.trim() || initial)
112773
+ onSubmit: (v15) => onFinish?.(v15.trim() || initial)
112166
112774
  }
112167
112775
  )
112168
112776
  ] });
@@ -112259,7 +112867,7 @@ function payloadToScanReport(local) {
112259
112867
  const payloadComponent = local.payload.components.find(
112260
112868
  (p4) => p4.tempId === c3.tempId
112261
112869
  );
112262
- const variants = payloadComponent?.variants?.map((v14) => v14.name) ?? [];
112870
+ const variants = payloadComponent?.variants?.map((v15) => v15.name) ?? [];
112263
112871
  return {
112264
112872
  name: c3.name,
112265
112873
  file: path.relative(process.cwd(), c3.filePath),