@vectoriox/iox-ui 5.54.0 → 5.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7567,7 +7567,7 @@ var SiteTemplateId;
7567
7567
  const STUDIO_TEMPLATE = SITE_TEMPLATES[SiteTemplateId.Studio];
7568
7568
  class SiteTemplateError extends Error {
7569
7569
  }
7570
- const clone$3 = (value) => JSON.parse(JSON.stringify(value));
7570
+ const clone$4 = (value) => JSON.parse(JSON.stringify(value));
7571
7571
  /**
7572
7572
  * The template as a Site Plan (`source.kind: template`), with the customer's business and look. A
7573
7573
  * required page cannot be left out, and an unknown key is refused — nothing is guessed.
@@ -7584,12 +7584,12 @@ function planFromTemplate(template, input) {
7584
7584
  return {
7585
7585
  version: SITE_PLAN_VERSION,
7586
7586
  source: { kind: SitePlanSourceKind.Template, templateId: template.id, templateVersion: template.version },
7587
- business: clone$3(input.business),
7588
- design: clone$3(input.design),
7589
- contentTypes: clone$3(template.contentTypes),
7590
- dataSources: clone$3(template.dataSources),
7591
- pages: clone$3(template.pages.filter(p => !omit.has(p.key))),
7592
- globals: clone$3(template.globals),
7587
+ business: clone$4(input.business),
7588
+ design: clone$4(input.design),
7589
+ contentTypes: clone$4(template.contentTypes),
7590
+ dataSources: clone$4(template.dataSources),
7591
+ pages: clone$4(template.pages.filter(p => !omit.has(p.key))),
7592
+ globals: clone$4(template.globals),
7593
7593
  };
7594
7594
  }
7595
7595
 
@@ -7598,7 +7598,7 @@ function planFromTemplate(template, input) {
7598
7598
  * wrong — never something to work around. */
7599
7599
  class ComposeError extends Error {
7600
7600
  }
7601
- const clone$2 = (value) => JSON.parse(JSON.stringify(value));
7601
+ const clone$3 = (value) => JSON.parse(JSON.stringify(value));
7602
7602
  /**
7603
7603
  * A NEW node of `type`, as the builder creates one (ComponentRegistryService.createConfig, serialized):
7604
7604
  * the type's constructor defaults, its text colour released to `inherit` (NEW_NODE_STYLE_DEFAULTS), then
@@ -7613,8 +7613,8 @@ function createSerializedNode(id, type, props = {}, elementStyle, options = {})
7613
7613
  const node = {
7614
7614
  id,
7615
7615
  type,
7616
- props: { ...clone$2(baseline.props), ...props },
7617
- styleProps: clone$2(baseline.styleProps),
7616
+ props: { ...clone$3(baseline.props), ...props },
7617
+ styleProps: clone$3(baseline.styleProps),
7618
7618
  bindings: [],
7619
7619
  interactions: [],
7620
7620
  };
@@ -7665,16 +7665,16 @@ function walkSerialized(nodes, visit, ancestors = []) {
7665
7665
  * own field names) whose key lists the gate READS — so the shape a model is told about is the shape refused —
7666
7666
  * and a collector that records each refusal at its dotted path. Generic over each gate's issue-code enum.
7667
7667
  */
7668
- const OPTIONAL_MARK = '?';
7668
+ const OPTIONAL_MARK$1 = '?';
7669
7669
  const DYNAMIC_KEY = /^\[[^\]]+\]\??$/;
7670
7670
  const isDynamic = (key) => DYNAMIC_KEY.test(key);
7671
7671
  /** Every fixed key an object of the shape may carry. */
7672
7672
  function shapeKeys(entry) {
7673
- return Object.keys(entry).filter(k => !isDynamic(k)).map(k => (k.endsWith(OPTIONAL_MARK) ? k.slice(0, -1) : k));
7673
+ return Object.keys(entry).filter(k => !isDynamic(k)).map(k => (k.endsWith(OPTIONAL_MARK$1) ? k.slice(0, -1) : k));
7674
7674
  }
7675
7675
  /** The keys an object of the shape must carry. */
7676
7676
  function requiredShapeKeys(entry) {
7677
- return Object.keys(entry).filter(k => !isDynamic(k) && !k.endsWith(OPTIONAL_MARK));
7677
+ return Object.keys(entry).filter(k => !isDynamic(k) && !k.endsWith(OPTIONAL_MARK$1));
7678
7678
  }
7679
7679
  const isPlainObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
7680
7680
  const isNonEmptyString = (v) => typeof v === 'string' && v.trim() !== '';
@@ -7729,6 +7729,83 @@ class PathIssueCollector {
7729
7729
  }
7730
7730
  }
7731
7731
 
7732
+ const OPTIONAL_MARK = '?';
7733
+ class JsonSchemaShapeError extends Error {
7734
+ }
7735
+ const jsonText = () => ({ type: 'string' });
7736
+ const jsonBoolean = () => ({ type: 'boolean' });
7737
+ const jsonInteger = () => ({ type: 'integer' });
7738
+ const jsonOneOf = (values) => ({ type: 'string', enum: [...values] });
7739
+ const jsonListOf = (items) => ({ type: 'array', items });
7740
+ const jsonAnyOf = (options) => ({ anyOf: [...options] });
7741
+ /** A closed object with exactly these properties. */
7742
+ function jsonClosedObject(properties, required, description) {
7743
+ return {
7744
+ type: 'object',
7745
+ properties,
7746
+ required: [...required],
7747
+ additionalProperties: false,
7748
+ ...(description ? { description } : {}),
7749
+ };
7750
+ }
7751
+ /** The table's own words for a key (`key` or `key?`). */
7752
+ function describeShapeKey(entry, key) {
7753
+ return String(entry[key] ?? entry[`${key}${OPTIONAL_MARK}`] ?? '');
7754
+ }
7755
+ /**
7756
+ * An object of one shape-table entry: exactly its fixed keys, its required keys required, each described by the
7757
+ * table; an entry's `[name]` keys are not object keys — `dynamic` writes them as one list. Throws when the properties
7758
+ * given are not the table's keys.
7759
+ */
7760
+ function jsonShapedObject(entry, properties, name = 'shape', dynamic) {
7761
+ const expected = [...shapeKeys(entry)].sort();
7762
+ const given = Object.keys(properties).sort();
7763
+ if (expected.join() !== given.join()) {
7764
+ throw new JsonSchemaShapeError(`${name}: the format's keys (${given.join(', ')}) are not the shape table's (${expected.join(', ')})`);
7765
+ }
7766
+ const dynamicKey = Object.keys(entry).find(k => /^\[[^\]]+\]\??$/.test(k));
7767
+ if (!!dynamic !== !!dynamicKey)
7768
+ throw new JsonSchemaShapeError(`${name}: ${dynamicKey ? `its ${dynamicKey} keys need a list on the wire` : 'it has no [name] keys to list'}`);
7769
+ const described = Object.fromEntries(Object.entries(properties).map(([key, schema]) => [key, { ...schema, description: describeShapeKey(entry, key) }]));
7770
+ if (dynamic && dynamicKey)
7771
+ described[dynamic.key] = { ...dynamic.schema, description: `${dynamicKey}: ${entry[dynamicKey]}` };
7772
+ return jsonClosedObject(described, requiredShapeKeys(entry));
7773
+ }
7774
+ /** Every keyword a schema uses — what a spec checks against the strict subset. */
7775
+ function jsonSchemaKeywords(node, out = new Set()) {
7776
+ if (Array.isArray(node))
7777
+ node.forEach(n => jsonSchemaKeywords(n, out));
7778
+ else if (node && typeof node === 'object') {
7779
+ for (const [k, v] of Object.entries(node)) {
7780
+ out.add(k);
7781
+ if (k === 'properties')
7782
+ Object.values(v).forEach(p => jsonSchemaKeywords(p, out));
7783
+ else if (k !== 'enum' && k !== 'required' && k !== 'description')
7784
+ jsonSchemaKeywords(v, out);
7785
+ }
7786
+ }
7787
+ return out;
7788
+ }
7789
+ /** The keywords the strict subset never uses. */
7790
+ const JSON_SCHEMA_BANNED_KEYWORDS = [
7791
+ 'minLength', 'maxLength', 'minimum', 'maximum', 'minItems', 'maxItems', 'pattern', 'oneOf', '$ref', 'const', 'format',
7792
+ ];
7793
+ /** Every object node of a schema (through properties, items and anyOf). */
7794
+ function jsonSchemaObjects(node, out = []) {
7795
+ if (!node || typeof node !== 'object')
7796
+ return out;
7797
+ const n = node;
7798
+ if (n['type'] === 'object')
7799
+ out.push(n);
7800
+ for (const p of Object.values(n['properties'] ?? {}))
7801
+ jsonSchemaObjects(p, out);
7802
+ if (n['items'])
7803
+ jsonSchemaObjects(n['items'], out);
7804
+ for (const option of n['anyOf'] ?? [])
7805
+ jsonSchemaObjects(option, out);
7806
+ return out;
7807
+ }
7808
+
7732
7809
  /**
7733
7810
  * The Site Plan validator — the gate in front of the composer. The wizard's plan and (later) the AI's
7734
7811
  * plan go through the SAME checks: an unknown pattern, a slot the pattern does not declare, a source
@@ -7776,20 +7853,20 @@ const RESERVED_PREFIX = '$';
7776
7853
  const ID_FIELD = '_id';
7777
7854
  /** The documented plan shape (contract `site.planShape`): its key lists are READ (`shape-check`), so a key
7778
7855
  * the plan documents is a key the gate validates — and an undocumented one is refused. */
7779
- const SHAPE = SPEC_SITE_PLAN_SHAPE;
7780
- const PLAN_KEYS = shapeKeys(SHAPE['plan']);
7781
- const TEMPLATE_SOURCE_KEYS = shapeKeys(SHAPE['sourceTemplate']);
7782
- const AI_SOURCE_KEYS = shapeKeys(SHAPE['sourceAi']);
7783
- const BUSINESS_KEYS = shapeKeys(SHAPE['business']);
7784
- const DESIGN_REQUIRED = requiredShapeKeys(SHAPE['design']);
7785
- const DESIGN_KEYS = shapeKeys(SHAPE['design']);
7786
- const CONTENT_TYPE_KEYS = shapeKeys(SHAPE['contentType']);
7787
- const FIELD_KEYS = shapeKeys(SHAPE['field']);
7788
- const SEED_FIXED_KEYS = shapeKeys(SHAPE['seed']);
7789
- const CMS_SOURCE_KEYS = shapeKeys(SHAPE['cmsDataSource']);
7790
- const RELATIVE_SOURCE_KEYS = shapeKeys(SHAPE['relativeDataSource']);
7791
- const PAGE_KEYS = shapeKeys(SHAPE['page']);
7792
- const SECTION_KEYS = shapeKeys(SHAPE['section']);
7856
+ const SHAPE$1 = SPEC_SITE_PLAN_SHAPE;
7857
+ const PLAN_KEYS = shapeKeys(SHAPE$1['plan']);
7858
+ const TEMPLATE_SOURCE_KEYS = shapeKeys(SHAPE$1['sourceTemplate']);
7859
+ const AI_SOURCE_KEYS = shapeKeys(SHAPE$1['sourceAi']);
7860
+ const BUSINESS_KEYS = shapeKeys(SHAPE$1['business']);
7861
+ const DESIGN_REQUIRED = requiredShapeKeys(SHAPE$1['design']);
7862
+ const DESIGN_KEYS = shapeKeys(SHAPE$1['design']);
7863
+ const CONTENT_TYPE_KEYS = shapeKeys(SHAPE$1['contentType']);
7864
+ const FIELD_KEYS = shapeKeys(SHAPE$1['field']);
7865
+ const SEED_FIXED_KEYS = shapeKeys(SHAPE$1['seed']);
7866
+ const CMS_SOURCE_KEYS = shapeKeys(SHAPE$1['cmsDataSource']);
7867
+ const RELATIVE_SOURCE_KEYS = shapeKeys(SHAPE$1['relativeDataSource']);
7868
+ const PAGE_KEYS = shapeKeys(SHAPE$1['page']);
7869
+ const SECTION_KEYS = shapeKeys(SHAPE$1['section']);
7793
7870
  const GLOBAL_POSITION = {
7794
7871
  [GlobalSlot.Header]: PatternGlobalPosition.Before,
7795
7872
  [GlobalSlot.Footer]: PatternGlobalPosition.After,
@@ -7801,11 +7878,11 @@ const has = (table, key) => Object.prototype.hasOwnProperty.call(table, key);
7801
7878
  function routeParams(route) {
7802
7879
  return [...route.matchAll(ROUTE_PARAM$1)].map(m => m[1]);
7803
7880
  }
7804
- class Collector extends PathIssueCollector {
7881
+ let Collector$1 = class Collector extends PathIssueCollector {
7805
7882
  constructor() {
7806
7883
  super('plan', { shape: SitePlanIssueCode.Shape, unknownKey: SitePlanIssueCode.UnknownKey, duplicate: SitePlanIssueCode.Duplicate });
7807
7884
  }
7808
- }
7885
+ };
7809
7886
  function validateSource(c, source) {
7810
7887
  if (!isPlainObject(source))
7811
7888
  return c.add(SitePlanIssueCode.Shape, 'source', 'must be an object');
@@ -8143,7 +8220,7 @@ function validateGlobals(c, globals) {
8143
8220
  }
8144
8221
  /** Check a Site Plan against the plan shape and the pattern library. Never throws, never repairs. */
8145
8222
  function validateSitePlan(plan) {
8146
- const c = new Collector();
8223
+ const c = new Collector$1();
8147
8224
  if (!isPlainObject(plan)) {
8148
8225
  c.add(SitePlanIssueCode.Shape, '', 'a site plan is an object');
8149
8226
  return { valid: false, issues: c.issues };
@@ -8506,7 +8583,7 @@ function bar(kit, root, pages, shape, count, drawerId, at) {
8506
8583
  return b;
8507
8584
  }
8508
8585
  // ── the edit ────────────────────────────────────────────────────────────────────────────────
8509
- const clone$1 = (v) => JSON.parse(JSON.stringify(v));
8586
+ const clone$2 = (v) => JSON.parse(JSON.stringify(v));
8510
8587
  function removeRole(node, role) {
8511
8588
  let removed = null;
8512
8589
  const visit = (n) => {
@@ -8558,7 +8635,7 @@ function clampBarCount(n, shape) {
8558
8635
  function planNavShape(globals, shape, options = {}, navId) {
8559
8636
  if (!Object.values(NavShape).includes(shape))
8560
8637
  throw new NavShapeError(`unknown phone menu shape "${shape}"`);
8561
- const out = clone$1(globals);
8638
+ const out = clone$2(globals);
8562
8639
  const root = findNavRoot(out, navId);
8563
8640
  if (!root)
8564
8641
  throw new NavShapeError('no Navigation bar among the globals');
@@ -8678,7 +8755,7 @@ function nameParts(root) {
8678
8755
  * bar does not link to yet (Home when every page is there). Refused at five (Q2). Input not mutated.
8679
8756
  */
8680
8757
  function addBarItem(globals, options = {}) {
8681
- const out = clone$1(globals);
8758
+ const out = clone$2(globals);
8682
8759
  const root = findNavRoot(out);
8683
8760
  const b = find(out, NavRole.Bar);
8684
8761
  if (!root || !b)
@@ -8698,7 +8775,7 @@ function addBarItem(globals, options = {}) {
8698
8775
  return { globals: out, added: true };
8699
8776
  }
8700
8777
  function barOf(globals) {
8701
- const out = clone$1(globals);
8778
+ const out = clone$2(globals);
8702
8779
  const b = find(out, NavRole.Bar);
8703
8780
  return { out, kids: b ? (b.children ?? (b.children = [])) : null };
8704
8781
  }
@@ -8839,7 +8916,7 @@ const COMPOSED = { releaseLayoutDefaults: true };
8839
8916
  const TEXT_VARIABLE = /\{(year|business\.name)\}/g;
8840
8917
  /** What `page.*` may read on a node stamped for a navigation page. */
8841
8918
  const STAMPED_PAGE_FIELDS = new Set(['name', 'route']);
8842
- const clone = (value) => JSON.parse(JSON.stringify(value));
8919
+ const clone$1 = (value) => JSON.parse(JSON.stringify(value));
8843
8920
  /** 32-bit FNV-1a, base 36 — a short stable id fragment. */
8844
8921
  function stableHash(text) {
8845
8922
  let h = 0x811c9dc5;
@@ -9612,10 +9689,10 @@ function composeSite(plan, context) {
9612
9689
  globals = planNavShape(globals, shape, {}, slot).globals;
9613
9690
  }
9614
9691
  return {
9615
- source: clone(plan.source),
9616
- business: clone(plan.business),
9617
- design: resolveDesignChoices(clone(plan.design)),
9618
- schemas: clone(plan.contentTypes),
9692
+ source: clone$1(plan.source),
9693
+ business: clone$1(plan.business),
9694
+ design: resolveDesignChoices(clone$1(plan.design)),
9695
+ schemas: clone$1(plan.contentTypes),
9619
9696
  items: Object.fromEntries(plan.contentTypes.map(t => [t.name, composeItems(t)])),
9620
9697
  dataSources: plan.dataSources.map(composeDataSource),
9621
9698
  pages,
@@ -9731,13 +9808,13 @@ const ENTRANCE_TRIGGER = 'viewportEnter';
9731
9808
  const ENTRANCE_EASING = 'ease-out';
9732
9809
  const LEVELS = SPEC_AXES.animations;
9733
9810
  const FAMILIES = SPEC_AXES.animationFamilies;
9734
- const NOTHING = { content: null, parts: {} };
9811
+ const NOTHING$1 = { content: null, parts: {} };
9735
9812
  /** What a section of `family` moves at `level` — nothing for an unknown level or family. */
9736
9813
  function sectionEntrances(level, family) {
9737
9814
  const lv = level ? LEVELS[level] : undefined;
9738
9815
  const allow = FAMILIES[family] ?? [];
9739
9816
  if (!lv || !allow.length)
9740
- return NOTHING;
9817
+ return NOTHING$1;
9741
9818
  if (lv.parts.content && allow.includes(EntrancePart.Content))
9742
9819
  return { content: lv.parts.content, parts: {} };
9743
9820
  const parts = {};
@@ -10009,6 +10086,14 @@ var AiStep;
10009
10086
  AiStep["NameStructures"] = "scanNaming";
10010
10087
  })(AiStep || (AiStep = {}));
10011
10088
  const AI_STEPS = Object.values(AiStep);
10089
+ /** Each step's name on the wire (the control plane's `label`, the console) — the screens translate their own. */
10090
+ const AI_STEP_LABELS = {
10091
+ [AiStep.ReadMessage]: 'Read your message',
10092
+ [AiStep.PlanSite]: 'Plan the site',
10093
+ [AiStep.ChangePlan]: 'Change the plan',
10094
+ [AiStep.MakePictures]: 'Make pictures',
10095
+ [AiStep.NameStructures]: 'Name repeated structures',
10096
+ };
10012
10097
  /** What a model must be able to do for a step to run on it. */
10013
10098
  var AiCapability;
10014
10099
  (function (AiCapability) {
@@ -10028,7 +10113,8 @@ const AI_STEP_TYPICAL_TOKENS = {
10028
10113
  [AiStep.ReadMessage]: { input: 8_800, output: 1_500, cacheWrite: 0, cacheRead: 0 },
10029
10114
  [AiStep.PlanSite]: { input: 12_000, output: 7_000, cacheWrite: 0, cacheRead: 0 },
10030
10115
  [AiStep.ChangePlan]: { input: 6_000, output: 500, cacheWrite: 0, cacheRead: 0 },
10031
- [AiStep.MakePictures]: { input: 1_120, output: 0, cacheWrite: 0, cacheRead: 0 },
10116
+ // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4
10117
+ [AiStep.MakePictures]: { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
10032
10118
  [AiStep.NameStructures]: { input: 80_000, output: 10_000, cacheWrite: 0, cacheRead: 0 },
10033
10119
  };
10034
10120
  /** 1 credit = 1 US cent of what the AI actually cost (decision D4). */
@@ -10042,7 +10128,8 @@ function costUsd(tokens, price) {
10042
10128
  return tokens.input * perToken(price.input, 0)
10043
10129
  + tokens.output * perToken(price.output, 0)
10044
10130
  + tokens.cacheWrite * perToken(price.cacheWrite, price.input)
10045
- + tokens.cacheRead * perToken(price.cacheRead, price.input);
10131
+ + tokens.cacheRead * perToken(price.cacheRead, price.input)
10132
+ + (tokens.imageOutput ?? 0) * perToken(price.imageOutput, price.output);
10046
10133
  }
10047
10134
  /** Dollars → credits, kept to a millionth of a credit (a short read costs a fraction of one). */
10048
10135
  function creditsOfUsd(usd) {
@@ -10122,6 +10209,724 @@ var AiCallStatus;
10122
10209
  AiCallStatus["Interrupted"] = "interrupted";
10123
10210
  })(AiCallStatus || (AiCallStatus = {}));
10124
10211
 
10212
+ /**
10213
+ * The structured-output format a model writes a Site Plan in (AI plan §3d step 4, M1-3) — built from the SAME shape
10214
+ * table `validateSitePlan` reads (`SPEC_SITE_PLAN_SHAPE`) and the SAME pattern library, so the plan a model is told
10215
+ * about is the plan the gate refuses. ONE source: a key the table gains breaks this builder until it is written here.
10216
+ *
10217
+ * Two maps of the shape are written as LISTS on the wire, because no strict mode can say "any key": a section's
10218
+ * `slots` (`[{ slot, text }]`, the slot names closed per pattern) and a seed's field values (`values: [{ field,
10219
+ * value }]`). `sitePlanFromWire` turns them back; nothing else differs. Every section is one `anyOf` branch per
10220
+ * pattern — its id, its declared slots, its optional parts, a binding only when it takes one — and the design, the
10221
+ * source and the version are PINNED to the owner's pick and this run (one-value enums), so the provider cannot let
10222
+ * a model change the look. The gate still checks both (`plan.design` deep-equals the pick).
10223
+ */
10224
+ const SITE_PLAN_SCHEMA_NAME = 'site_plan';
10225
+ /** The wire keys of the two maps written as lists. */
10226
+ var SitePlanWireKey;
10227
+ (function (SitePlanWireKey) {
10228
+ SitePlanWireKey["SlotName"] = "slot";
10229
+ SitePlanWireKey["SlotText"] = "text";
10230
+ SitePlanWireKey["SeedValues"] = "values";
10231
+ SitePlanWireKey["SeedField"] = "field";
10232
+ SitePlanWireKey["SeedValue"] = "value";
10233
+ })(SitePlanWireKey || (SitePlanWireKey = {}));
10234
+ const SHAPE = SPEC_SITE_PLAN_SHAPE;
10235
+ const one$1 = (value) => jsonOneOf([value]);
10236
+ const oneBoolean = (value) => ({ type: 'boolean', enum: [value] });
10237
+ /** The pick, key by key, as one-value enums; an optional key the pick leaves out may only be left out (or empty). */
10238
+ function pinnedDesign(design) {
10239
+ const entry = SHAPE['design'];
10240
+ const values = design;
10241
+ const properties = {};
10242
+ for (const key of Object.keys(entry).map(k => k.replace(/\?$/, ''))) {
10243
+ const v = values[key];
10244
+ if (typeof v === 'string')
10245
+ properties[key] = one$1(v);
10246
+ else if (typeof v === 'boolean')
10247
+ properties[key] = oneBoolean(v);
10248
+ else if (isPlainObject(v))
10249
+ properties[key] = jsonClosedObject(Object.fromEntries(Object.entries(v).map(([role, family]) => [role, one$1(String(family))])), Object.keys(v));
10250
+ else
10251
+ properties[key] = jsonClosedObject({}, []);
10252
+ }
10253
+ const shaped = jsonShapedObject(entry, properties, 'design');
10254
+ // Everything the pick carries is required: a model may not leave a choice out and let a default stand in.
10255
+ const given = Object.keys(values).filter(k => values[k] !== undefined && k in properties);
10256
+ return { ...shaped, required: [...new Set([...shaped['required'], ...given])] };
10257
+ }
10258
+ function sourceSchema(source) {
10259
+ if (source.kind === SitePlanSourceKind.Ai)
10260
+ return jsonShapedObject(SHAPE['sourceAi'], { kind: one$1(source.kind), runId: one$1(source.runId) }, 'sourceAi');
10261
+ return jsonShapedObject(SHAPE['sourceTemplate'], {
10262
+ kind: one$1(source.kind), templateId: one$1(source.templateId), templateVersion: { type: 'integer', enum: [source.templateVersion] },
10263
+ }, 'sourceTemplate');
10264
+ }
10265
+ function businessSchema() {
10266
+ const facts = jsonClosedObject(Object.fromEntries(FACT_KEYS.map(key => [key, jsonText()])), []);
10267
+ return jsonShapedObject(SHAPE['business'], { name: jsonText(), tagline: jsonText(), facts }, 'business');
10268
+ }
10269
+ /** A content type's field. */
10270
+ function fieldJsonSchema() {
10271
+ return jsonShapedObject(SHAPE['field'], { name: jsonText(), type: jsonText(), controller: jsonText(), required: jsonBoolean(), note: jsonText() }, 'field');
10272
+ }
10273
+ /** A content type, its seed's field values as a list (`values: [{ field, value }]`). */
10274
+ function contentTypeJsonSchema() {
10275
+ const value = jsonAnyOf([jsonText(), jsonListOf(jsonText())]);
10276
+ const values = jsonListOf(jsonClosedObject({ [SitePlanWireKey.SeedField]: jsonText(), [SitePlanWireKey.SeedValue]: value }, [SitePlanWireKey.SeedField, SitePlanWireKey.SeedValue]));
10277
+ const seed = jsonShapedObject(SHAPE['seed'], { count: jsonInteger(), sample: jsonBoolean(), isPublished: jsonBoolean() }, 'seed', { key: SitePlanWireKey.SeedValues, schema: values });
10278
+ return jsonShapedObject(SHAPE['contentType'], {
10279
+ name: jsonText(), label: jsonText(), previewTextField: jsonText(), previewImageField: jsonText(), fields: jsonListOf(fieldJsonSchema()), seed,
10280
+ }, 'contentType');
10281
+ }
10282
+ /** A data source: a cms list / single item, or a relative one. */
10283
+ function dataSourceJsonSchema() {
10284
+ const byRoute = jsonClosedObject({ field: jsonText(), routeParam: jsonText() }, ['field', 'routeParam']);
10285
+ const cms = jsonShapedObject(SHAPE['cmsDataSource'], {
10286
+ alias: jsonText(), type: one$1(PlanDataSourceType.Cms), mode: jsonOneOf(Object.values(PlanDataSourceMode)),
10287
+ contentType: jsonText(), limit: jsonInteger(), filterByRouteParam: byRoute,
10288
+ }, 'cmsDataSource');
10289
+ const relative = jsonShapedObject(SHAPE['relativeDataSource'], {
10290
+ alias: jsonText(), type: one$1(PlanDataSourceType.Relative), of: jsonText(), op: jsonOneOf(Object.values(RelativeOp)), within: jsonText(),
10291
+ }, 'relativeDataSource');
10292
+ return jsonAnyOf([cms, relative]);
10293
+ }
10294
+ /** The patterns a page section may use (every non-global one), in library order. */
10295
+ function sectionPatternIds() {
10296
+ return Object.entries(SITE_PATTERNS).filter(([, p]) => !p.global).map(([id]) => id);
10297
+ }
10298
+ /** A key a pattern has nothing for: only `{}` (or leaving it out) — the decoder drops it. */
10299
+ const NOTHING = () => jsonClosedObject({}, []);
10300
+ /** One section branch: this pattern's id, its declared slots (a list), its optional parts, a binding when it takes one. */
10301
+ function sectionBranch(id, pattern) {
10302
+ const slots = patternSlots(pattern);
10303
+ const parts = patternOptionalParts(pattern);
10304
+ const slotList = slots.length
10305
+ ? jsonListOf(jsonClosedObject({ [SitePlanWireKey.SlotName]: jsonOneOf(slots), [SitePlanWireKey.SlotText]: jsonText() }, [SitePlanWireKey.SlotName, SitePlanWireKey.SlotText]))
10306
+ : NOTHING();
10307
+ const branch = jsonShapedObject(SHAPE['section'], {
10308
+ pattern: one$1(id),
10309
+ slots: slotList,
10310
+ binding: patternTakesBinding(pattern) ? jsonText() : NOTHING(),
10311
+ without: parts.length ? jsonListOf(jsonOneOf(parts)) : NOTHING(),
10312
+ }, 'section');
10313
+ return { ...branch, description: `${id}: ${pattern.label}` };
10314
+ }
10315
+ /** A page section: one branch per pattern. */
10316
+ function sectionJsonSchema() {
10317
+ return jsonAnyOf(sectionPatternIds().map(id => sectionBranch(id, SITE_PATTERNS[id])));
10318
+ }
10319
+ /** A page. */
10320
+ function pageJsonSchema() {
10321
+ return jsonShapedObject(SHAPE['page'], {
10322
+ key: jsonText(), name: jsonText(), route: jsonText(), isHome: jsonBoolean(), required: jsonBoolean(), inNav: jsonBoolean(),
10323
+ template: jsonText(), routeSource: jsonText(), sections: jsonListOf(sectionJsonSchema()),
10324
+ }, 'page');
10325
+ }
10326
+ const globalIds = (position) => Object.entries(SITE_PATTERNS).filter(([, p]) => p.global === position).map(([id]) => id);
10327
+ /** The Site Plan format, pinned to the owner's pick and this run. */
10328
+ function sitePlanJsonSchema(pin) {
10329
+ const globals = jsonShapedObject(SHAPE['globals'], {
10330
+ [GlobalSlot.Header]: jsonOneOf(globalIds(PatternGlobalPosition.Before)),
10331
+ [GlobalSlot.Footer]: jsonOneOf(globalIds(PatternGlobalPosition.After)),
10332
+ }, 'globals');
10333
+ return jsonShapedObject(SHAPE['plan'], {
10334
+ version: one$1(SITE_PLAN_VERSION),
10335
+ source: sourceSchema(pin.source),
10336
+ business: businessSchema(),
10337
+ design: pinnedDesign(pin.design),
10338
+ contentTypes: jsonListOf(contentTypeJsonSchema()),
10339
+ dataSources: jsonListOf(dataSourceJsonSchema()),
10340
+ pages: jsonListOf(pageJsonSchema()),
10341
+ globals,
10342
+ }, 'plan');
10343
+ }
10344
+ // ── The wire → the plan ─────────────────────────────────────────────────────────────────────────────
10345
+ const isEmptyObject = (v) => isPlainObject(v) && Object.keys(v).length === 0;
10346
+ /** `[{ slot, text }]` → `{ slot: text }`; anything that is not that list is left for the gate to refuse. */
10347
+ function slotsFromWire(slots) {
10348
+ if (!Array.isArray(slots))
10349
+ return slots;
10350
+ const out = {};
10351
+ for (const entry of slots) {
10352
+ if (!isPlainObject(entry) || typeof entry[SitePlanWireKey.SlotName] !== 'string')
10353
+ return slots;
10354
+ out[entry[SitePlanWireKey.SlotName]] = entry[SitePlanWireKey.SlotText];
10355
+ }
10356
+ return out;
10357
+ }
10358
+ /** A section off the wire: its slots back to a map; a key the pattern has nothing for (`{}`) dropped. */
10359
+ function sectionFromWire(section) {
10360
+ if (!isPlainObject(section))
10361
+ return section;
10362
+ const out = { ...section };
10363
+ for (const key of ['slots', 'binding', 'without'])
10364
+ if (isEmptyObject(out[key]))
10365
+ delete out[key];
10366
+ if ('slots' in out)
10367
+ out['slots'] = slotsFromWire(out['slots']);
10368
+ return out;
10369
+ }
10370
+ /** A content type off the wire: its seed's `values` list back to field keys. */
10371
+ function contentTypeFromWire(type) {
10372
+ if (!isPlainObject(type) || !isPlainObject(type['seed']))
10373
+ return type;
10374
+ const { [SitePlanWireKey.SeedValues]: values, ...seed } = type['seed'];
10375
+ if (Array.isArray(values)) {
10376
+ for (const entry of values) {
10377
+ if (isPlainObject(entry) && typeof entry[SitePlanWireKey.SeedField] === 'string')
10378
+ seed[entry[SitePlanWireKey.SeedField]] = entry[SitePlanWireKey.SeedValue];
10379
+ }
10380
+ }
10381
+ else if (values !== undefined)
10382
+ seed[SitePlanWireKey.SeedValues] = values;
10383
+ return { ...type, seed };
10384
+ }
10385
+ function pageFromWire(page) {
10386
+ if (!isPlainObject(page) || !Array.isArray(page['sections']))
10387
+ return page;
10388
+ return { ...page, sections: page['sections'].map(sectionFromWire) };
10389
+ }
10390
+ /** The design off the wire: an empty optional object (a choice the pick does not carry) dropped. */
10391
+ function designFromWire(design) {
10392
+ if (!isPlainObject(design))
10393
+ return design;
10394
+ return Object.fromEntries(Object.entries(design).filter(([, v]) => !isEmptyObject(v)));
10395
+ }
10396
+ /**
10397
+ * A model's answer → a Site Plan: the two listed maps back to maps, nothing else touched. Never repairs — whatever the
10398
+ * answer got wrong is still wrong, for `validateSitePlan` to refuse with a path.
10399
+ */
10400
+ function sitePlanFromWire(wire) {
10401
+ if (!isPlainObject(wire))
10402
+ return wire;
10403
+ const out = { ...wire };
10404
+ if (Array.isArray(out['contentTypes']))
10405
+ out['contentTypes'] = out['contentTypes'].map(contentTypeFromWire);
10406
+ if (Array.isArray(out['pages']))
10407
+ out['pages'] = out['pages'].map(pageFromWire);
10408
+ if ('design' in out)
10409
+ out['design'] = designFromWire(out['design']);
10410
+ return out;
10411
+ }
10412
+ /** The plan → the wire (what a re-ask shows the model, and what the plan edits' prompt carries). */
10413
+ function sitePlanToWire(plan) {
10414
+ const slotsToWire = (slots) => Object.entries(slots ?? {}).map(([slot, text]) => ({ [SitePlanWireKey.SlotName]: slot, [SitePlanWireKey.SlotText]: text }));
10415
+ return {
10416
+ ...plan,
10417
+ contentTypes: plan.contentTypes.map(t => {
10418
+ if (!t.seed)
10419
+ return t;
10420
+ const { count, sample, isPublished, ...values } = t.seed;
10421
+ return { ...t, seed: { count, sample, isPublished, [SitePlanWireKey.SeedValues]: Object.entries(values).map(([field, value]) => ({ [SitePlanWireKey.SeedField]: field, [SitePlanWireKey.SeedValue]: value })) } };
10422
+ }),
10423
+ pages: plan.pages.map(p => ({ ...p, sections: p.sections.map(s => (s.slots ? { ...s, slots: slotsToWire(s.slots) } : s)) })),
10424
+ };
10425
+ }
10426
+
10427
+ /**
10428
+ * Changing a plan by sentence (AI plan §3d "The Plan prompt box", M1-5; reused by M2 after a build): the CLOSED list
10429
+ * of operations (N-D6) a model may write against the current plan, and the ONE pure `applyPlanEdits` that carries them
10430
+ * out. Nothing is written until Build, so an edit is a plan → plan function; the result goes through
10431
+ * `validateSitePlan` like any plan. Pages are named by `key`, sections by their index on the page. A set of edits is
10432
+ * applied whole or not at all: one edit that cannot apply refuses the set, with a path, and the plan is unchanged.
10433
+ */
10434
+ var PlanEditOp;
10435
+ (function (PlanEditOp) {
10436
+ PlanEditOp["AddPage"] = "addPage";
10437
+ PlanEditOp["RemovePage"] = "removePage";
10438
+ PlanEditOp["RenamePage"] = "renamePage";
10439
+ PlanEditOp["MergePages"] = "mergePages";
10440
+ /** Its place in the menu (the plan's page order). */
10441
+ PlanEditOp["MovePage"] = "movePage";
10442
+ PlanEditOp["AddSection"] = "addSection";
10443
+ PlanEditOp["RemoveSection"] = "removeSection";
10444
+ PlanEditOp["MoveSection"] = "moveSection";
10445
+ PlanEditOp["SwapPattern"] = "swapPattern";
10446
+ PlanEditOp["SetSlot"] = "setSlot";
10447
+ PlanEditOp["AddContentType"] = "addContentType";
10448
+ PlanEditOp["AddField"] = "addField";
10449
+ PlanEditOp["SetSectionData"] = "setSectionData";
10450
+ })(PlanEditOp || (PlanEditOp = {}));
10451
+ /**
10452
+ * Every op's keys, explained — what the format is built from and what `validatePlanEdits` reads (`key?` optional).
10453
+ * A page is named by its `key`; a section by its 0-based `index` on that page.
10454
+ */
10455
+ const PLAN_EDIT_SHAPE = {
10456
+ [PlanEditOp.AddPage]: { op: "'addPage'", page: 'page — a whole new page (a unique key and route)', 'after?': 'string — the key of the page it follows in the menu; absent: last' },
10457
+ [PlanEditOp.RemovePage]: { op: "'removePage'", page: 'string — the key of the page to remove (never the home page)' },
10458
+ [PlanEditOp.RenamePage]: { op: "'renamePage'", page: 'string — a page key', 'name?': 'string — its new name (the menu label)', 'route?': 'string — its new route' },
10459
+ [PlanEditOp.MergePages]: { op: "'mergePages'", into: 'string — the page that stays', from: 'string — the page whose sections move into it, then removed' },
10460
+ [PlanEditOp.MovePage]: { op: "'movePage'", page: 'string — a page key', to: 'integer — its new 0-based place in the menu' },
10461
+ [PlanEditOp.AddSection]: { op: "'addSection'", page: 'string — a page key', section: 'section', 'at?': 'integer — its 0-based place on the page; absent: last' },
10462
+ [PlanEditOp.RemoveSection]: { op: "'removeSection'", page: 'string — a page key', index: 'integer — the section' },
10463
+ [PlanEditOp.MoveSection]: { op: "'moveSection'", page: 'string — a page key', from: 'integer — the section', to: 'integer — its new place' },
10464
+ [PlanEditOp.SwapPattern]: { op: "'swapPattern'", page: 'string — a page key', index: 'integer — the section', pattern: 'string — the pattern it becomes (its slots kept where the new one has them)' },
10465
+ [PlanEditOp.SetSlot]: { op: "'setSlot'", page: 'string — a page key', index: 'integer — the section', slot: "string — one of the pattern's slots", text: "string — the words, in the owner's language and voice" },
10466
+ [PlanEditOp.AddContentType]: { op: "'addContentType'", contentType: 'contentType — a unique name', 'dataSources?': 'dataSource[] — the list / single sources its pages read' },
10467
+ [PlanEditOp.AddField]: { op: "'addField'", contentType: 'string — a content type of the plan', field: 'field', 'sample?': "string | string[] — the samples' value for it" },
10468
+ [PlanEditOp.SetSectionData]: { op: "'setSectionData'", page: 'string — a page key', index: 'integer — the section', binding: 'string — the list source the section repeats' },
10469
+ };
10470
+ var PlanChangeKind;
10471
+ (function (PlanChangeKind) {
10472
+ PlanChangeKind["Page"] = "page";
10473
+ PlanChangeKind["Section"] = "section";
10474
+ PlanChangeKind["ContentType"] = "contentType";
10475
+ PlanChangeKind["Field"] = "field";
10476
+ PlanChangeKind["DataSource"] = "dataSource";
10477
+ })(PlanChangeKind || (PlanChangeKind = {}));
10478
+ var PlanChangeAction;
10479
+ (function (PlanChangeAction) {
10480
+ PlanChangeAction["Added"] = "added";
10481
+ PlanChangeAction["Removed"] = "removed";
10482
+ PlanChangeAction["Changed"] = "changed";
10483
+ PlanChangeAction["Moved"] = "moved";
10484
+ })(PlanChangeAction || (PlanChangeAction = {}));
10485
+ var PlanEditIssueCode;
10486
+ (function (PlanEditIssueCode) {
10487
+ PlanEditIssueCode["Shape"] = "shape";
10488
+ PlanEditIssueCode["UnknownKey"] = "unknown-key";
10489
+ PlanEditIssueCode["Duplicate"] = "duplicate";
10490
+ PlanEditIssueCode["UnknownOp"] = "unknown-op";
10491
+ PlanEditIssueCode["UnknownPage"] = "unknown-page";
10492
+ PlanEditIssueCode["UnknownSection"] = "unknown-section";
10493
+ PlanEditIssueCode["UnknownPattern"] = "unknown-pattern";
10494
+ PlanEditIssueCode["UnknownSlot"] = "unknown-slot";
10495
+ PlanEditIssueCode["UnknownContentType"] = "unknown-content-type";
10496
+ PlanEditIssueCode["UnknownDataSource"] = "unknown-data-source";
10497
+ /** The home page stays (a plan has exactly one). */
10498
+ PlanEditIssueCode["HomePage"] = "home-page";
10499
+ /** A template page (one item per route) and a plain page cannot be merged. */
10500
+ PlanEditIssueCode["MergeTemplate"] = "merge-template";
10501
+ })(PlanEditIssueCode || (PlanEditIssueCode = {}));
10502
+ class Collector extends PathIssueCollector {
10503
+ constructor() {
10504
+ super('plan edit', { shape: PlanEditIssueCode.Shape, unknownKey: PlanEditIssueCode.UnknownKey, duplicate: PlanEditIssueCode.Duplicate });
10505
+ }
10506
+ }
10507
+ const OPS = Object.values(PlanEditOp);
10508
+ /** A page's own header section (`page-header.simple`) — what a merge leaves behind with the page it named. */
10509
+ const PAGE_HEADER_FAMILY = 'header';
10510
+ const clone = (v) => JSON.parse(JSON.stringify(v));
10511
+ const clamp = (n, max) => Math.max(0, Math.min(Math.trunc(n), max));
10512
+ /** The edits' structure: a known op, its keys (`PLAN_EDIT_SHAPE`), integers where an index is. Never repairs. */
10513
+ function validatePlanEdits(edits) {
10514
+ const c = new Collector();
10515
+ if (!Array.isArray(edits)) {
10516
+ c.add(PlanEditIssueCode.Shape, 'edits', 'must be an array');
10517
+ return { valid: false, issues: c.issues };
10518
+ }
10519
+ edits.forEach((raw, i) => {
10520
+ const path = `edits[${i}]`;
10521
+ if (!isPlainObject(raw))
10522
+ return c.add(PlanEditIssueCode.Shape, path, 'must be an object');
10523
+ const op = raw['op'];
10524
+ if (typeof op !== 'string' || !OPS.includes(op))
10525
+ return c.add(PlanEditIssueCode.UnknownOp, `${path}.op`, `must be one of ${OPS.join(', ')}`);
10526
+ const entry = PLAN_EDIT_SHAPE[op];
10527
+ c.keys(raw, shapeKeys(entry), path);
10528
+ for (const [key, text] of Object.entries(entry)) {
10529
+ const optional = key.endsWith('?');
10530
+ const name = optional ? key.slice(0, -1) : key;
10531
+ const v = raw[name];
10532
+ if (v === undefined) {
10533
+ if (!optional)
10534
+ c.add(PlanEditIssueCode.Shape, `${path}.${name}`, 'is required');
10535
+ continue;
10536
+ }
10537
+ if (text.startsWith('string | string[]')) {
10538
+ if (typeof v !== 'string' && !(Array.isArray(v) && v.every(x => typeof x === 'string')))
10539
+ c.add(PlanEditIssueCode.Shape, `${path}.${name}`, 'must be a string or a list of strings');
10540
+ }
10541
+ else if (text.startsWith('string') && !isNonEmptyString(v))
10542
+ c.add(PlanEditIssueCode.Shape, `${path}.${name}`, 'must be a non-empty string');
10543
+ if (text.startsWith('integer') && !Number.isInteger(v))
10544
+ c.add(PlanEditIssueCode.Shape, `${path}.${name}`, 'must be a whole number');
10545
+ if (['page', 'section', 'contentType', 'field'].includes(text.split(' ')[0]) && !isPlainObject(v))
10546
+ c.add(PlanEditIssueCode.Shape, `${path}.${name}`, 'must be an object');
10547
+ if (text.startsWith('dataSource[]') && !Array.isArray(v))
10548
+ c.add(PlanEditIssueCode.Shape, `${path}.${name}`, 'must be an array');
10549
+ }
10550
+ });
10551
+ return { valid: c.issues.length === 0, issues: c.issues };
10552
+ }
10553
+ /** The aliases a page reads (its route source and its sections' bindings). */
10554
+ function aliasesOf(pages) {
10555
+ const out = new Set();
10556
+ for (const page of pages) {
10557
+ if (page.routeSource)
10558
+ out.add(page.routeSource);
10559
+ for (const s of page.sections)
10560
+ if (s.binding)
10561
+ out.add(s.binding);
10562
+ }
10563
+ return out;
10564
+ }
10565
+ /** A relative source (`project.next`'s) is read while its subject is, and keeps the list it walks. */
10566
+ function withDependencies(aliases, sources) {
10567
+ const out = new Set(aliases);
10568
+ for (const s of sources) {
10569
+ if (s.type === PlanDataSourceType.Relative && aliases.has(s.of)) {
10570
+ out.add(s.alias);
10571
+ out.add(s.within);
10572
+ }
10573
+ }
10574
+ return out;
10575
+ }
10576
+ const pageIndex = (d, key) => d.pages.findIndex(p => p.key === key);
10577
+ function findPage(d, key, c, path) {
10578
+ const at = pageIndex(d, key);
10579
+ if (at < 0)
10580
+ c.add(PlanEditIssueCode.UnknownPage, path, `no page "${String(key)}" in the plan (${d.pages.map(p => p.key).join(', ')})`);
10581
+ return at;
10582
+ }
10583
+ function findSection(d, pageAt, index, c, path) {
10584
+ const ok = Number.isInteger(index) && index >= 0 && index < d.pages[pageAt].sections.length;
10585
+ if (!ok)
10586
+ c.add(PlanEditIssueCode.UnknownSection, path, `page "${d.pages[pageAt].key}" has sections 0–${d.pages[pageAt].sections.length - 1}`);
10587
+ return ok;
10588
+ }
10589
+ const setSections = (d, at, sections) => { d.pages[at] = { ...d.pages[at], sections }; };
10590
+ /** One handler per op — a data table, never an if-chain. */
10591
+ const APPLY = {
10592
+ [PlanEditOp.AddPage]: (d, e, c, path, changed) => {
10593
+ const page = { ...e.page, sections: (e.page.sections ?? []).map(sectionFromWire) };
10594
+ if (pageIndex(d, page.key) >= 0)
10595
+ return c.add(PlanEditIssueCode.Duplicate, `${path}.page.key`, `page "${page.key}" is already in the plan`);
10596
+ const after = e.after === undefined ? d.pages.length - 1 : findPage(d, e.after, c, `${path}.after`);
10597
+ if (after < 0 && e.after !== undefined)
10598
+ return;
10599
+ d.pages.splice(after + 1, 0, page);
10600
+ changed.push({ kind: PlanChangeKind.Page, action: PlanChangeAction.Added, page: page.key, name: page.name });
10601
+ },
10602
+ [PlanEditOp.RemovePage]: (d, e, c, path, changed) => {
10603
+ const at = findPage(d, e.page, c, `${path}.page`);
10604
+ if (at < 0)
10605
+ return;
10606
+ if (d.pages[at].isHome)
10607
+ return c.add(PlanEditIssueCode.HomePage, `${path}.page`, 'the home page stays — rename or change it instead');
10608
+ const [gone] = d.pages.splice(at, 1);
10609
+ changed.push({ kind: PlanChangeKind.Page, action: PlanChangeAction.Removed, page: gone.key, name: gone.name });
10610
+ },
10611
+ [PlanEditOp.RenamePage]: (d, e, c, path, changed) => {
10612
+ const at = findPage(d, e.page, c, `${path}.page`);
10613
+ if (at < 0)
10614
+ return;
10615
+ d.pages[at] = { ...d.pages[at], ...(e.name !== undefined ? { name: e.name } : {}), ...(e.route !== undefined ? { route: e.route } : {}) };
10616
+ changed.push({ kind: PlanChangeKind.Page, action: PlanChangeAction.Changed, page: d.pages[at].key, name: d.pages[at].name });
10617
+ },
10618
+ [PlanEditOp.MergePages]: (d, e, c, path, changed) => {
10619
+ const into = findPage(d, e.into, c, `${path}.into`);
10620
+ const from = findPage(d, e.from, c, `${path}.from`);
10621
+ if (into < 0 || from < 0)
10622
+ return;
10623
+ if (into === from)
10624
+ return c.add(PlanEditIssueCode.Duplicate, `${path}.from`, 'a page is merged into another page');
10625
+ const a = d.pages[into];
10626
+ const b = d.pages[from];
10627
+ if (!!a.template !== !!b.template)
10628
+ return c.add(PlanEditIssueCode.MergeTemplate, `${path}.from`, 'a page per item and a plain page cannot be merged');
10629
+ if (b.isHome)
10630
+ return c.add(PlanEditIssueCode.HomePage, `${path}.from`, 'the home page stays — merge the other page into it');
10631
+ const moved = b.sections.filter((s, i) => !(i === 0 && patternById(s.pattern)?.family === PAGE_HEADER_FAMILY));
10632
+ setSections(d, into, [...a.sections, ...moved]);
10633
+ moved.forEach((_, k) => changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Added, page: a.key, index: a.sections.length + k }));
10634
+ d.pages.splice(from, 1);
10635
+ changed.push({ kind: PlanChangeKind.Page, action: PlanChangeAction.Changed, page: a.key, name: a.name });
10636
+ changed.push({ kind: PlanChangeKind.Page, action: PlanChangeAction.Removed, page: b.key, name: b.name });
10637
+ },
10638
+ [PlanEditOp.MovePage]: (d, e, c, path, changed) => {
10639
+ const at = findPage(d, e.page, c, `${path}.page`);
10640
+ if (at < 0)
10641
+ return;
10642
+ const [page] = d.pages.splice(at, 1);
10643
+ d.pages.splice(clamp(e.to, d.pages.length), 0, page);
10644
+ changed.push({ kind: PlanChangeKind.Page, action: PlanChangeAction.Moved, page: page.key, name: page.name });
10645
+ },
10646
+ [PlanEditOp.AddSection]: (d, e, c, path, changed) => {
10647
+ const at = findPage(d, e.page, c, `${path}.page`);
10648
+ if (at < 0)
10649
+ return;
10650
+ const sections = [...d.pages[at].sections];
10651
+ const index = e.at === undefined ? sections.length : clamp(e.at, sections.length);
10652
+ sections.splice(index, 0, sectionFromWire(e.section));
10653
+ setSections(d, at, sections);
10654
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Added, page: d.pages[at].key, index });
10655
+ },
10656
+ [PlanEditOp.RemoveSection]: (d, e, c, path, changed) => {
10657
+ const at = findPage(d, e.page, c, `${path}.page`);
10658
+ if (at < 0 || !findSection(d, at, e.index, c, `${path}.index`))
10659
+ return;
10660
+ setSections(d, at, d.pages[at].sections.filter((_, i) => i !== e.index));
10661
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Removed, page: d.pages[at].key, index: e.index });
10662
+ },
10663
+ [PlanEditOp.MoveSection]: (d, e, c, path, changed) => {
10664
+ const at = findPage(d, e.page, c, `${path}.page`);
10665
+ if (at < 0 || !findSection(d, at, e.from, c, `${path}.from`))
10666
+ return;
10667
+ const sections = [...d.pages[at].sections];
10668
+ const [section] = sections.splice(e.from, 1);
10669
+ const to = clamp(e.to, sections.length);
10670
+ sections.splice(to, 0, section);
10671
+ setSections(d, at, sections);
10672
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Moved, page: d.pages[at].key, index: to });
10673
+ },
10674
+ [PlanEditOp.SwapPattern]: (d, e, c, path, changed) => {
10675
+ const at = findPage(d, e.page, c, `${path}.page`);
10676
+ if (at < 0 || !findSection(d, at, e.index, c, `${path}.index`))
10677
+ return;
10678
+ const next = patternById(e.pattern);
10679
+ if (!next || next.global)
10680
+ return c.add(PlanEditIssueCode.UnknownPattern, `${path}.pattern`, `no section pattern "${String(e.pattern)}"`);
10681
+ const old = d.pages[at].sections[e.index];
10682
+ const slots = Object.fromEntries(Object.entries(old.slots ?? {}).filter(([slot]) => patternSlots(next).includes(slot)));
10683
+ const without = (old.without ?? []).filter(w => patternOptionalParts(next).includes(w));
10684
+ const section = {
10685
+ pattern: e.pattern,
10686
+ ...(Object.keys(slots).length ? { slots } : {}),
10687
+ ...(old.binding && patternTakesBinding(next) ? { binding: old.binding } : {}),
10688
+ ...(without.length ? { without } : {}),
10689
+ };
10690
+ setSections(d, at, d.pages[at].sections.map((s, i) => (i === e.index ? section : s)));
10691
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Changed, page: d.pages[at].key, index: e.index });
10692
+ },
10693
+ [PlanEditOp.SetSlot]: (d, e, c, path, changed) => {
10694
+ const at = findPage(d, e.page, c, `${path}.page`);
10695
+ if (at < 0 || !findSection(d, at, e.index, c, `${path}.index`))
10696
+ return;
10697
+ const section = d.pages[at].sections[e.index];
10698
+ const declared = patternSlots(patternById(section.pattern));
10699
+ if (!declared.includes(e.slot))
10700
+ return c.add(PlanEditIssueCode.UnknownSlot, `${path}.slot`, `"${section.pattern}" has no slot "${e.slot}" (${declared.join(', ') || 'none'})`);
10701
+ setSections(d, at, d.pages[at].sections.map((s, i) => (i === e.index ? { ...s, slots: { ...(s.slots ?? {}), [e.slot]: e.text } } : s)));
10702
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Changed, page: d.pages[at].key, index: e.index });
10703
+ },
10704
+ [PlanEditOp.AddContentType]: (d, e, c, path, changed) => {
10705
+ const type = contentTypeFromWire(e.contentType);
10706
+ if (d.contentTypes.some(t => t.name === type.name))
10707
+ return c.add(PlanEditIssueCode.Duplicate, `${path}.contentType.name`, `content type "${type.name}" is already in the plan`);
10708
+ d.contentTypes.push(type);
10709
+ changed.push({ kind: PlanChangeKind.ContentType, action: PlanChangeAction.Added, name: type.name });
10710
+ for (const [k, source] of (e.dataSources ?? []).entries()) {
10711
+ if (d.dataSources.some(s => s.alias === source.alias)) {
10712
+ c.add(PlanEditIssueCode.Duplicate, `${path}.dataSources[${k}].alias`, `data source "${source.alias}" is already in the plan`);
10713
+ continue;
10714
+ }
10715
+ d.dataSources.push(source);
10716
+ changed.push({ kind: PlanChangeKind.DataSource, action: PlanChangeAction.Added, name: source.alias });
10717
+ }
10718
+ },
10719
+ [PlanEditOp.AddField]: (d, e, c, path, changed) => {
10720
+ const at = d.contentTypes.findIndex(t => t.name === e.contentType);
10721
+ if (at < 0)
10722
+ return c.add(PlanEditIssueCode.UnknownContentType, `${path}.contentType`, `no content type "${String(e.contentType)}" in the plan`);
10723
+ const type = d.contentTypes[at];
10724
+ if (type.fields.some(f => f.name === e.field.name))
10725
+ return c.add(PlanEditIssueCode.Duplicate, `${path}.field.name`, `"${type.name}" already has a field "${e.field.name}"`);
10726
+ const seed = type.seed ? { ...type.seed, [e.field.name]: e.sample ?? '' } : undefined;
10727
+ d.contentTypes[at] = { ...type, fields: [...type.fields, e.field], ...(seed ? { seed } : {}) };
10728
+ changed.push({ kind: PlanChangeKind.Field, action: PlanChangeAction.Added, name: `${type.name}.${e.field.name}` });
10729
+ },
10730
+ [PlanEditOp.SetSectionData]: (d, e, c, path, changed) => {
10731
+ const at = findPage(d, e.page, c, `${path}.page`);
10732
+ if (at < 0 || !findSection(d, at, e.index, c, `${path}.index`))
10733
+ return;
10734
+ const source = d.dataSources.find(s => s.alias === e.binding);
10735
+ if (!source || source.type !== PlanDataSourceType.Cms || source.mode !== PlanDataSourceMode.Query) {
10736
+ return c.add(PlanEditIssueCode.UnknownDataSource, `${path}.binding`, `no list source "${String(e.binding)}" in the plan`);
10737
+ }
10738
+ setSections(d, at, d.pages[at].sections.map((s, i) => (i === e.index ? { ...s, binding: e.binding } : s)));
10739
+ changed.push({ kind: PlanChangeKind.Section, action: PlanChangeAction.Changed, page: d.pages[at].key, index: e.index });
10740
+ },
10741
+ };
10742
+ /**
10743
+ * Apply a set of edits to a plan — all of them, or none (any issue refuses the set and returns the plan unchanged). The
10744
+ * sources only removed pages and sections read are dropped with them; sources nothing read before are kept.
10745
+ * The result still has to pass `validateSitePlan`.
10746
+ */
10747
+ function applyPlanEdits(plan, edits) {
10748
+ const structure = validatePlanEdits(edits);
10749
+ if (!structure.valid)
10750
+ return { plan, changed: [], issues: structure.issues };
10751
+ const draft = { pages: clone([...plan.pages]), contentTypes: clone([...plan.contentTypes]), dataSources: clone([...plan.dataSources]) };
10752
+ const c = new Collector();
10753
+ const changed = [];
10754
+ edits.forEach((edit, i) => APPLY[edit.op](draft, edit, c, `edits[${i}]`, changed));
10755
+ if (c.issues.length)
10756
+ return { plan, changed: [], issues: c.issues };
10757
+ const before = withDependencies(aliasesOf(plan.pages), plan.dataSources);
10758
+ const after = withDependencies(aliasesOf(draft.pages), draft.dataSources);
10759
+ const added = new Set(changed.filter(ch => ch.kind === PlanChangeKind.DataSource).map(ch => ch.name));
10760
+ const dataSources = draft.dataSources.filter(s => after.has(s.alias) || !before.has(s.alias) || added.has(s.alias));
10761
+ for (const s of draft.dataSources)
10762
+ if (!dataSources.includes(s))
10763
+ changed.push({ kind: PlanChangeKind.DataSource, action: PlanChangeAction.Removed, name: s.alias });
10764
+ return { plan: { ...plan, pages: draft.pages, contentTypes: draft.contentTypes, dataSources }, changed, issues: [] };
10765
+ }
10766
+ // ── The format a model writes edits in ─────────────────────────────────────────────────────────────────
10767
+ const PLAN_EDITS_SCHEMA_NAME = 'plan_edits';
10768
+ /** A sentence's answer: the edits, or why the ops cannot say it (one line, in the owner's language). */
10769
+ var PlanEditsOutcome;
10770
+ (function (PlanEditsOutcome) {
10771
+ PlanEditsOutcome["Edits"] = "edits";
10772
+ PlanEditsOutcome["Cannot"] = "cannot";
10773
+ })(PlanEditsOutcome || (PlanEditsOutcome = {}));
10774
+ /** The answer's own keys, explained. */
10775
+ const PLAN_EDITS_ANSWER_SHAPE = {
10776
+ outcome: "'edits' | 'cannot' — cannot: the operations cannot do what the sentence asks",
10777
+ edits: 'edit[] — applied in order, all or none; empty when cannot',
10778
+ reason: "string — when cannot, ONE short line for the owner in their language (what to do instead); else ''",
10779
+ };
10780
+ const one = (value) => jsonOneOf([value]);
10781
+ /** The schema of each op's value types (what `PLAN_EDIT_SHAPE` names). */
10782
+ function opSchema(op) {
10783
+ const entry = PLAN_EDIT_SHAPE[op];
10784
+ const valueOf = (key) => {
10785
+ const text = entry[key] ?? entry[`${key}?`];
10786
+ if (key === 'op')
10787
+ return one(op);
10788
+ if (key === 'pattern')
10789
+ return jsonOneOf(sectionPatternIds());
10790
+ const head = text.split(' ')[0];
10791
+ const table = {
10792
+ page: pageJsonSchema, section: sectionJsonSchema, contentType: contentTypeJsonSchema, field: fieldJsonSchema,
10793
+ 'dataSource[]': () => jsonListOf(dataSourceJsonSchema()), integer: jsonInteger, string: jsonText,
10794
+ };
10795
+ if (text.startsWith('string | string[]'))
10796
+ return jsonAnyOf([jsonText(), jsonListOf(jsonText())]);
10797
+ return (table[head] ?? jsonText)();
10798
+ };
10799
+ return jsonShapedObject(entry, Object.fromEntries(shapeKeys(entry).map(key => [key, valueOf(key)])), op);
10800
+ }
10801
+ /** The format a sentence's answer is written in: the outcome, the edits (one `anyOf` branch per op), the reason. */
10802
+ function planEditsJsonSchema() {
10803
+ return jsonShapedObject(PLAN_EDITS_ANSWER_SHAPE, {
10804
+ outcome: jsonOneOf(Object.values(PlanEditsOutcome)),
10805
+ edits: jsonListOf(jsonAnyOf(OPS.map(op => opSchema(op)))),
10806
+ reason: jsonText(),
10807
+ }, 'planEdits');
10808
+ }
10809
+ /** A model's answer → edits (sections and content types off the wire); never repaired. */
10810
+ function planEditsFromWire(answer) {
10811
+ if (!isPlainObject(answer) || !Array.isArray(answer['edits']))
10812
+ return answer;
10813
+ const edits = answer['edits'].map(raw => {
10814
+ if (!isPlainObject(raw))
10815
+ return raw;
10816
+ const e = { ...raw };
10817
+ if (isPlainObject(e['page']) && Array.isArray(e['page']['sections'])) {
10818
+ e['page'] = { ...e['page'], sections: e['page']['sections'].map(sectionFromWire) };
10819
+ }
10820
+ if (isPlainObject(e['section']))
10821
+ e['section'] = sectionFromWire(e['section']);
10822
+ if (isPlainObject(e['contentType']))
10823
+ e['contentType'] = contentTypeFromWire(e['contentType']);
10824
+ return e;
10825
+ });
10826
+ return { ...answer, edits };
10827
+ }
10828
+
10829
+ /**
10830
+ * The plan stage's word checks (AI plan §3d / §3h, M1-3): every text a model WROTE into a plan — the tagline, page
10831
+ * names, section slots, content-type labels, sample values — scanned for the words the owner and their kind of
10832
+ * business must not use (`avoidWords`, the analysis's + the type's `words`), and for a fact the owner never gave
10833
+ * (an email, phone number, price or opening time that is not one of the owner's own facts). A finding is a refusal
10834
+ * with a path — the model rewrites; nothing here repairs a word. Pure.
10835
+ */
10836
+ var PlanWordIssueCode;
10837
+ (function (PlanWordIssueCode) {
10838
+ /** A word the owner (or their kind of business) must not use. */
10839
+ PlanWordIssueCode["AvoidWord"] = "avoid-word";
10840
+ /** Something that looks like a fact — an email, phone, price or time — that is not one of the owner's facts. */
10841
+ PlanWordIssueCode["InventedFact"] = "invented-fact";
10842
+ /** `business.facts` holds a fact the owner did not give, or not in the owner's words. */
10843
+ PlanWordIssueCode["FactNotTheOwners"] = "fact-not-the-owners";
10844
+ })(PlanWordIssueCode || (PlanWordIssueCode = {}));
10845
+ /** Every text in a plan a model writes (never ids, routes, field names or pattern ids). */
10846
+ function writtenTexts(plan) {
10847
+ const out = [];
10848
+ const add = (path, text) => { if (typeof text === 'string' && text.trim())
10849
+ out.push({ path, text }); };
10850
+ add('business.tagline', plan.business?.tagline);
10851
+ plan.pages?.forEach((page, p) => {
10852
+ add(`pages[${p}].name`, page.name);
10853
+ page.sections?.forEach((section, s) => {
10854
+ for (const [slot, text] of Object.entries(section.slots ?? {}))
10855
+ add(`pages[${p}].sections[${s}].slots.${slot}`, text);
10856
+ });
10857
+ });
10858
+ plan.contentTypes?.forEach((type, t) => {
10859
+ add(`contentTypes[${t}].label`, type.label);
10860
+ for (const [field, value] of Object.entries(type.seed ?? {})) {
10861
+ const values = Array.isArray(value) ? value : [value];
10862
+ values.forEach((v, k) => add(`contentTypes[${t}].seed.${field}${Array.isArray(value) ? `[${k}]` : ''}`, v));
10863
+ }
10864
+ });
10865
+ return out;
10866
+ }
10867
+ const HEBREW = /[֐-׿]/;
10868
+ /** Hebrew joins up to two one-letter words (and, the, in, to, from, that, as) to the next: "והטוב" holds "טוב". */
10869
+ const HEBREW_PREFIXES = '[והבלמשכ]{0,2}';
10870
+ const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10871
+ const normalise = (s) => s.normalize('NFKC').toLowerCase();
10872
+ /** A word (or phrase) as a whole-word pattern; Hebrew allows its one-letter prefixes. */
10873
+ function wordPattern(word) {
10874
+ const w = escape(normalise(word.trim())).replace(/\s+/g, '\\s+');
10875
+ const prefix = HEBREW.test(word) ? HEBREW_PREFIXES : '';
10876
+ return new RegExp(`(?<![\\p{L}\\p{N}])${prefix}${w}(?![\\p{L}\\p{N}])`, 'u');
10877
+ }
10878
+ /** What a fact looks like when a model writes one — each judged against the owner's facts. */
10879
+ const FACT_SHAPES = [
10880
+ { kind: 'email', pattern: /[\p{L}\p{N}._%+-]+@[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)+/gu, key: m => m.toLowerCase() },
10881
+ { kind: 'phone', pattern: /\+?\d[\d\s().-]{6,}\d/g, key: m => m.replace(/\D/g, '') },
10882
+ { kind: 'price', pattern: /(?:[$€£₪]\s?\d[\d,.]*|\d[\d,.]*\s?(?:[$€£₪]|ש"ח|NIS|USD|EUR|GBP))/gu, key: m => m.replace(/[^\d]/g, '') },
10883
+ { kind: 'time', pattern: /(?<!\d)\d{1,2}[:.]\d{2}(?!\d)/g, key: m => m.replace(/\D/g, '') },
10884
+ ];
10885
+ const MIN_PHONE_DIGITS = 8;
10886
+ /** "2019 – 2024" is two years, not a phone number. */
10887
+ const YEAR_RANGE = /^\s*\d{4}\s*[-–]\s*\d{4}\s*$/;
10888
+ function ownedKeys(facts) {
10889
+ const keys = new Set();
10890
+ for (const value of Object.values(facts)) {
10891
+ if (typeof value !== 'string')
10892
+ continue;
10893
+ for (const shape of FACT_SHAPES)
10894
+ for (const m of value.match(shape.pattern) ?? [])
10895
+ keys.add(`${shape.kind}:${shape.key(m)}`);
10896
+ }
10897
+ return keys;
10898
+ }
10899
+ /** The words to avoid and the facts never given, in every written text of the plan. */
10900
+ function planWordIssues(plan, ctx) {
10901
+ const issues = [];
10902
+ const avoid = [...new Set(ctx.avoidWords.map(w => w.trim()).filter(Boolean))].map(w => ({ word: w, pattern: wordPattern(w) }));
10903
+ const owned = ownedKeys(ctx.facts);
10904
+ for (const { path, text } of writtenTexts(plan)) {
10905
+ const words = normalise(text);
10906
+ for (const a of avoid)
10907
+ if (a.pattern.test(words))
10908
+ issues.push({ code: PlanWordIssueCode.AvoidWord, path, message: `uses "${a.word}" — a word this site must not use` });
10909
+ for (const shape of FACT_SHAPES) {
10910
+ for (const m of text.match(shape.pattern) ?? []) {
10911
+ const key = shape.key(m);
10912
+ if (shape.kind === 'phone' && (key.length < MIN_PHONE_DIGITS || YEAR_RANGE.test(m)))
10913
+ continue;
10914
+ if (!owned.has(`${shape.kind}:${key}`))
10915
+ issues.push({ code: PlanWordIssueCode.InventedFact, path, message: `"${m.trim()}" looks like a ${shape.kind} the owner never gave — leave it out` });
10916
+ }
10917
+ }
10918
+ }
10919
+ for (const [key, value] of Object.entries(plan.business?.facts ?? {})) {
10920
+ const own = ctx.facts[key];
10921
+ if (!FACT_KEYS.includes(key))
10922
+ continue;
10923
+ if (typeof own !== 'string' || normalise(own).trim() !== normalise(String(value)).trim()) {
10924
+ issues.push({ code: PlanWordIssueCode.FactNotTheOwners, path: `business.facts.${key}`, message: own ? `must be the owner's own words: "${own}"` : 'the owner did not give it — leave it out (it becomes a placeholder)' });
10925
+ }
10926
+ }
10927
+ return issues;
10928
+ }
10929
+
10125
10930
  /**
10126
10931
  * @vectoriox/iox-ui/generation-core — the shared, FRAMEWORK-FREE site-plan core (design-system plan
10127
10932
  * §6b, W1): the Site Plan shape + its validator, the pattern library and the Studio template (both
@@ -10138,5 +10943,5 @@ var AiCallStatus;
10138
10943
  * Generated bundle index. Do not edit.
10139
10944
  */
10140
10945
 
10141
- export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_NEEDS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiGenerationStage, AiKeyMode, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CREDITS_PER_USD, CardBindTarget, ComposeError, ComposeIssueCode, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, FACT_KEYS, FINAL_GENERATION_STAGES, GlobalSlot, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanDataSourceMode, PlanDataSourceType, PreflightRefusalCode, RelativeOp, SAMPLE_IMAGE_REF, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_TEMPLATES, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, addBarItem, altSourceScopes, assertStyleSlots, assertValidSitePlan, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, costUsd, createSerializedNode, creditsOfUsd, describePlanData, entranceAction, entranceInteraction, estimateStepCredits, failedBuildStep, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, parseSampleImageRef, pathAt, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, patternTakesBinding, phoneMenuPages, planFromTemplate, planNavShape, releasedLayoutDefaults, removeBarItem, requiredShapeKeys, routeParams, sampleImageRef, sectionEntrances, setStyleProps, shapeKeys, siteNavPages, staggerDelay, validateSitePlan, walkSerialized };
10946
+ export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiGenerationStage, AiKeyMode, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CREDITS_PER_USD, CardBindTarget, ComposeError, ComposeIssueCode, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, FACT_KEYS, FINAL_GENERATION_STAGES, GlobalSlot, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PLAN_EDITS_ANSWER_SHAPE, PLAN_EDITS_SCHEMA_NAME, PLAN_EDIT_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanWordIssueCode, PreflightRefusalCode, RelativeOp, SAMPLE_IMAGE_REF, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_PATTERNS, SITE_PLAN_SCHEMA_NAME, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_TEMPLATES, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SitePlanWireKey, SiteTemplateError, SiteTemplateId, StepState, addBarItem, altSourceScopes, applyPlanEdits, assertStyleSlots, assertValidSitePlan, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, contentTypeFromWire, contentTypeJsonSchema, costUsd, createSerializedNode, creditsOfUsd, dataSourceJsonSchema, describePlanData, describeShapeKey, entranceAction, entranceInteraction, estimateStepCredits, failedBuildStep, fieldJsonSchema, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, pageJsonSchema, parseSampleImageRef, pathAt, patternById, patternNeedsBinding, patternNodes, patternOptionalParts, patternScopes, patternSlots, patternTakesBinding, phoneMenuPages, planEditsFromWire, planEditsJsonSchema, planFromTemplate, planNavShape, planWordIssues, releasedLayoutDefaults, removeBarItem, requiredShapeKeys, routeParams, sampleImageRef, sectionEntrances, sectionFromWire, sectionJsonSchema, sectionPatternIds, setStyleProps, shapeKeys, siteNavPages, sitePlanFromWire, sitePlanJsonSchema, sitePlanToWire, staggerDelay, validatePlanEdits, validateSitePlan, walkSerialized, wordPattern, writtenTexts };
10142
10947
  //# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map