@xtrable-ltd/nanoesis 0.1.10 → 0.1.11

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 (29) hide show
  1. package/dist/adapter-azure-blob.js +1 -1
  2. package/dist/{chunk-UZQ7UP2B.js → chunk-XO3CT6GL.js} +167 -15
  3. package/dist/editor-api.js +5 -4
  4. package/dist/index.d.ts +139 -76
  5. package/dist/index.js +5 -1
  6. package/editor/assets/{MigrationsPane-_FGonx4-.js → MigrationsPane-BYGqWBAA.js} +1 -1
  7. package/editor/assets/{TemplatesPane-Z6Bn69Hb.js → TemplatesPane-B5hn_v0Z.js} +168 -164
  8. package/editor/assets/{TemplatesPane-CiLiMCc8.css → TemplatesPane-D0gGehUt.css} +1 -1
  9. package/editor/assets/{cssMode-dkQrIPWx.js → cssMode-BbIf5k6I.js} +1 -1
  10. package/editor/assets/{freemarker2-DEqcFFWa.js → freemarker2-DoW0pSYV.js} +1 -1
  11. package/editor/assets/{handlebars-C6ojANWr.js → handlebars-DLlET-qc.js} +1 -1
  12. package/editor/assets/{html-BmiAmVUD.js → html-4khbqrhe.js} +1 -1
  13. package/editor/assets/{htmlMode-BBmUqToI.js → htmlMode-DblHkZ-k.js} +1 -1
  14. package/editor/assets/index-CkESQLMV.css +7 -0
  15. package/editor/assets/index-Do1drqEQ.js +138 -0
  16. package/editor/assets/{javascript-Cxm2TfJy.js → javascript-CgPO2Hmj.js} +1 -1
  17. package/editor/assets/{jsonMode-CW5012Hx.js → jsonMode-BrWh2436.js} +1 -1
  18. package/editor/assets/{liquid-DrS7ilHv.js → liquid-BsQJXwPT.js} +1 -1
  19. package/editor/assets/{mdx-CwdSU5o1.js → mdx-AO8t67gx.js} +1 -1
  20. package/editor/assets/{python-CALCR0yC.js → python-3w4sZj5c.js} +1 -1
  21. package/editor/assets/{razor-SVCo2LoM.js → razor-BFsvo06w.js} +1 -1
  22. package/editor/assets/{tsMode-CzXfTR_Q.js → tsMode-QrC4ERjp.js} +1 -1
  23. package/editor/assets/{typescript-CP0Ovrv7.js → typescript-BXJ3QLad.js} +1 -1
  24. package/editor/assets/{xml-B2yqloTa.js → xml-CxKYn1FP.js} +1 -1
  25. package/editor/assets/{yaml-DTLJhzgY.js → yaml-BmWLvF7Q.js} +1 -1
  26. package/editor/index.html +2 -2
  27. package/package.json +1 -1
  28. package/editor/assets/index-DEz8GUII.css +0 -7
  29. package/editor/assets/index-LtCzUHAw.js +0 -138
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  contentTypeFor
3
- } from "./chunk-UZQ7UP2B.js";
3
+ } from "./chunk-XO3CT6GL.js";
4
4
 
5
5
  // ../../adapters/azure-blob/src/container.ts
6
6
  var InMemoryBlobContainer = class {
@@ -471,20 +471,58 @@ function freeze(field) {
471
471
  }
472
472
 
473
473
  // ../engine/src/template/stamp.ts
474
- function detectStamp(oldSource, newSource, components = /* @__PURE__ */ new Map()) {
475
- const oldFields = new Set(deriveFields(oldSource, components).map((f) => f.name));
476
- const newFields = new Set(deriveFields(newSource, components).map((f) => f.name));
477
- const removed = [];
474
+ function isDestructiveTypeChange(from, to) {
475
+ if (from === to) return false;
476
+ const fromKind = valueKindOf(from);
477
+ const toKind = valueKindOf(to);
478
+ if (toKind === "html") return false;
479
+ if (fromKind === "html") return true;
480
+ if (from === "date" && to === "time" || from === "time" && to === "date") return true;
481
+ const isFreeText = (t) => t === "shorttext" || t === "text" || t === "code" || t === "email" || t === "phone";
482
+ if (isFreeText(from) && (to === "date" || to === "time")) return true;
483
+ if (fromKind === toKind) return false;
484
+ if (toKind === "text") return false;
485
+ return true;
486
+ }
487
+ function computeSchemaDelta(oldFields, newFields) {
488
+ const oldByName = new Map(oldFields.map((f) => [f.name, f]));
489
+ const newByName = new Map(newFields.map((f) => [f.name, f]));
478
490
  const added = [];
479
- for (const name of oldFields) {
480
- if (!newFields.has(name)) removed.push(name);
491
+ const removed = [];
492
+ const typeChanged = [];
493
+ for (const [name, oldField] of oldByName) {
494
+ const newField = newByName.get(name);
495
+ if (newField === void 0) {
496
+ removed.push({ name, type: oldField.type });
497
+ } else if (newField.type !== oldField.type) {
498
+ typeChanged.push({
499
+ name,
500
+ from: oldField.type,
501
+ to: newField.type,
502
+ destructive: isDestructiveTypeChange(oldField.type, newField.type)
503
+ });
504
+ }
481
505
  }
482
- for (const name of newFields) {
483
- if (!oldFields.has(name)) added.push(name);
506
+ for (const [name, newField] of newByName) {
507
+ if (!oldByName.has(name)) added.push({ name, type: newField.type });
484
508
  }
485
- removed.sort();
486
- added.sort();
487
- return { destructive: removed.length > 0, removedTokens: removed, addedTokens: added };
509
+ return {
510
+ added: added.sort((a, b) => a.name.localeCompare(b.name)),
511
+ removed: removed.sort((a, b) => a.name.localeCompare(b.name)),
512
+ typeChanged: typeChanged.sort((a, b) => a.name.localeCompare(b.name))
513
+ };
514
+ }
515
+ function detectStamp(oldSource, newSource, components = /* @__PURE__ */ new Map()) {
516
+ const oldFields = deriveFields(oldSource, components);
517
+ const newFields = deriveFields(newSource, components);
518
+ const delta = computeSchemaDelta(oldFields, newFields);
519
+ const destructive = delta.removed.length > 0 || delta.typeChanged.some((change) => change.destructive);
520
+ return {
521
+ destructive,
522
+ removedTokens: delta.removed.map((r) => r.name),
523
+ addedTokens: delta.added.map((a) => a.name),
524
+ typeChanged: delta.typeChanged
525
+ };
488
526
  }
489
527
 
490
528
  // ../engine/src/template/versions.ts
@@ -692,11 +730,15 @@ var IndexedStore = class {
692
730
  const stampCandidate = stampTargetOf(target);
693
731
  const previousBytes = stampCandidate !== void 0 ? await this.store.get(target) : void 0;
694
732
  let stampDecision;
733
+ let schemaDelta;
695
734
  if (stampCandidate !== void 0 && previousBytes !== void 0) {
696
735
  const oldSource = new TextDecoder().decode(previousBytes);
697
736
  const newSource = new TextDecoder().decode(bytes);
698
- const decision = detectStamp(oldSource, newSource);
699
- if (decision.destructive) {
737
+ const oldFields = deriveFields(oldSource);
738
+ const newFields = deriveFields(newSource);
739
+ schemaDelta = computeSchemaDelta(oldFields, newFields);
740
+ const destructive = schemaDelta.removed.length > 0 || schemaDelta.typeChanged.some((change) => change.destructive);
741
+ if (destructive) {
700
742
  const index2 = await this.loaded();
701
743
  const siblings = index2.keys.filter((k) => k.startsWith(`${stampCandidate.dir}/`) && k.endsWith(".html")).map((k) => k.slice(stampCandidate.dir.length + 1, -".html".length));
702
744
  const version = nextVersionNumber(siblings);
@@ -736,7 +778,8 @@ var IndexedStore = class {
736
778
  this.index = nextIndex;
737
779
  return {
738
780
  ...stamped !== void 0 && { stamped },
739
- ...stampIncomplete === true && { stampIncomplete: true }
781
+ ...stampIncomplete === true && { stampIncomplete: true },
782
+ ...schemaDelta !== void 0 && { schemaDelta }
740
783
  };
741
784
  });
742
785
  }
@@ -2378,7 +2421,8 @@ function buildAuthoringReference(context = {}) {
2378
2421
  annotationsSection(),
2379
2422
  loopsSection(context),
2380
2423
  componentsSection(context),
2381
- shellSection()
2424
+ shellSection(),
2425
+ guardrailsSection()
2382
2426
  ]
2383
2427
  };
2384
2428
  }
@@ -2523,6 +2567,41 @@ function componentsSection(context) {
2523
2567
  entries
2524
2568
  };
2525
2569
  }
2570
+ function guardrailsSection() {
2571
+ return {
2572
+ title: "Authoring guardrails for LLMs",
2573
+ intro: "Common pitfalls when authoring through MCP, where you can't see the editor form's shape change. Most stem from the same root: data-* annotations bind to the element they sit on, not to the token inside it.",
2574
+ entries: [
2575
+ {
2576
+ name: "data-* annotations are element-bound",
2577
+ summary: "data-type, data-required, data-field, data-minlength, and data-maxlength apply to the *element* that carries them \u2014 moving a token outside its annotated container silently strips those properties from the schema.",
2578
+ detail: 'Example: `<div data-type="richtext">{body}</div>` makes body a rich-text field. If you change it to `<div data-type="richtext"></div>{body}`, body drops back to plain shorttext. The compiler still substitutes the token at fragment root, but existing HTML content for body now renders escaped.',
2579
+ example: '<!-- WRONG (drops the annotation) -->\n<div data-type="richtext"></div>{body}\n\n<!-- RIGHT -->\n<div data-type="richtext">{body}</div>'
2580
+ },
2581
+ {
2582
+ name: "After write_file on a template, check `schemaDelta`",
2583
+ summary: "Every write to templates/*.html or components/*.html returns a `schemaDelta` describing added/removed/typeChanged fields. If `typeChanged` contains `destructive: true` entries or `removed` is non-empty, an auto-stamp has been written and authored content may need migration \u2014 surface to the user before further edits.",
2584
+ detail: "`schemaDelta` shape: `{added: [{name, type}], removed: [{name, type}], typeChanged: [{name, from, to, destructive}]}`. Empty arrays everywhere means the edit changed only whitespace, comments, or non-token markup \u2014 safe."
2585
+ },
2586
+ {
2587
+ name: "After rename_path on a template, search content for the old name",
2588
+ summary: 'Renaming `templates/article.html` to `templates/post.html` leaves every content item with `template: "article"` bound to a now-missing template. The validation gate will report these on publish, but the cheaper check is `list_pending_migrations` followed by editing the items\' `template` fields.'
2589
+ },
2590
+ {
2591
+ name: "After delete_path on a template, expect publish-blocking errors",
2592
+ summary: "Deleting a template that any item binds to will fail validation with `template-missing` per item. Use `list_pending_migrations` first to see who would be affected, and consider whether to rename items to a different template before deleting."
2593
+ },
2594
+ {
2595
+ name: "Read describe_template before editing a content item",
2596
+ summary: "A content item's `fields` object MUST match the bound template's schema (field names and value shapes). `describe_template` returns the field list with types so you build the right shape on the first try; mismatched keys become orphan fields the gate warns about."
2597
+ },
2598
+ {
2599
+ name: "richtext values are HTML, shorttext/text are plain text",
2600
+ summary: "For a `richtext` field, write the value as HTML (`<p>...</p>` etc). For `shorttext` or `text`, write plain text \u2014 any HTML you include is escaped at render time. If the gate emits a `content.type-drift` warning, a field's type changed destructively (commonly richtext \u2192 shorttext) and the old HTML content now renders as escaped text."
2601
+ }
2602
+ ]
2603
+ };
2604
+ }
2526
2605
  function shellSection() {
2527
2606
  return {
2528
2607
  title: "The document shell",
@@ -2637,6 +2716,65 @@ async function validateSite(source) {
2637
2716
  expectedFieldsCache.set(templateName, fields);
2638
2717
  return fields;
2639
2718
  };
2719
+ const driftFieldsCache = /* @__PURE__ */ new Map();
2720
+ const driftFieldsFor = async (templateName) => {
2721
+ if (driftFieldsCache.has(templateName)) return driftFieldsCache.get(templateName) ?? null;
2722
+ const highest = await highestSnapshotName(templateName);
2723
+ if (highest === null) {
2724
+ driftFieldsCache.set(templateName, null);
2725
+ return null;
2726
+ }
2727
+ let snapshotHtml;
2728
+ try {
2729
+ snapshotHtml = await loadTemplate(source, highest);
2730
+ } catch {
2731
+ driftFieldsCache.set(templateName, null);
2732
+ return null;
2733
+ }
2734
+ const currentHtml = sourceCache.get(templateName);
2735
+ if (currentHtml === void 0) {
2736
+ driftFieldsCache.set(templateName, null);
2737
+ return null;
2738
+ }
2739
+ const delta = computeSchemaDelta(
2740
+ deriveFields(snapshotHtml, components),
2741
+ deriveFields(currentHtml, components)
2742
+ );
2743
+ const destructive = new Set(
2744
+ delta.typeChanged.filter((change) => change.destructive).map((change) => change.name)
2745
+ );
2746
+ driftFieldsCache.set(templateName, destructive);
2747
+ return destructive;
2748
+ };
2749
+ const snapshotNameCache = /* @__PURE__ */ new Map();
2750
+ const highestSnapshotName = async (baseName) => {
2751
+ if (snapshotNameCache.has(baseName)) return snapshotNameCache.get(baseName) ?? null;
2752
+ const slash = baseName.lastIndexOf("/");
2753
+ const dir = slash === -1 ? "templates" : `templates/${baseName.slice(0, slash)}`;
2754
+ const stem = slash === -1 ? baseName : baseName.slice(slash + 1);
2755
+ let entries;
2756
+ try {
2757
+ entries = await source.list(dir);
2758
+ } catch {
2759
+ snapshotNameCache.set(baseName, null);
2760
+ return null;
2761
+ }
2762
+ let highest = 0;
2763
+ let highestStem = null;
2764
+ const prefix = `${stem}@v`;
2765
+ for (const entry of entries) {
2766
+ if (entry.kind !== "file" || !entry.name.endsWith(".html")) continue;
2767
+ const entryStem = entry.name.slice(0, -".html".length);
2768
+ if (!entryStem.startsWith(prefix)) continue;
2769
+ const version = versionNumber(entryStem);
2770
+ if (version !== null && version > highest) {
2771
+ highest = version;
2772
+ highestStem = slash === -1 ? entryStem : `${baseName.slice(0, slash)}/${entryStem}`;
2773
+ }
2774
+ }
2775
+ snapshotNameCache.set(baseName, highestStem);
2776
+ return highestStem;
2777
+ };
2640
2778
  const urlOwners = /* @__PURE__ */ new Map();
2641
2779
  for (const { node, templateName } of published) {
2642
2780
  const owners = urlOwners.get(urlForItem(node.path)) ?? [];
@@ -2674,6 +2812,18 @@ async function validateSite(source) {
2674
2812
  node.path
2675
2813
  );
2676
2814
  }
2815
+ const driftFields = await driftFieldsFor(templateName);
2816
+ if (driftFields !== null && driftFields.size > 0) {
2817
+ const affected = itemFieldNames.filter((name) => driftFields.has(name));
2818
+ if (affected.length > 0) {
2819
+ add(
2820
+ "warning",
2821
+ "content.type-drift",
2822
+ `"${node.path}" has field${affected.length === 1 ? "" : "s"} whose type changed destructively in template "${templateName}": ${affected.map((f) => `"${f}"`).join(", ")}. The value will render under the new type (commonly HTML escaped as text). Open the Migrations workspace to migrate.`,
2823
+ node.path
2824
+ );
2825
+ }
2826
+ }
2677
2827
  }
2678
2828
  const scope = buildScope2(node.item);
2679
2829
  for (const field of analysis.requiredFields) {
@@ -3110,6 +3260,8 @@ export {
3110
3260
  findTokens,
3111
3261
  wholeValueToken,
3112
3262
  deriveFields,
3263
+ isDestructiveTypeChange,
3264
+ computeSchemaDelta,
3113
3265
  detectStamp,
3114
3266
  isVersionedTemplateName,
3115
3267
  baseTemplateName,
@@ -15,7 +15,7 @@ import {
15
15
  renderReferenceMarkdown,
16
16
  validateSite,
17
17
  workingStoreRoundTripDiagnostic
18
- } from "./chunk-UZQ7UP2B.js";
18
+ } from "./chunk-XO3CT6GL.js";
19
19
 
20
20
  // ../editor-api/src/scaffold.ts
21
21
  var HOME_HTML = `<!doctype html>
@@ -499,7 +499,8 @@ async function dispatchApi(deps, req) {
499
499
  return json(200, {
500
500
  ok: true,
501
501
  ...result.stamped !== void 0 && { stamped: result.stamped },
502
- ...result.stampIncomplete === true && { stampIncomplete: true }
502
+ ...result.stampIncomplete === true && { stampIncomplete: true },
503
+ ...result.schemaDelta !== void 0 && { schemaDelta: result.schemaDelta }
503
504
  });
504
505
  } catch (error) {
505
506
  return json(500, { ok: false, error: String(error) });
@@ -693,7 +694,7 @@ var TOOL_SPECS = [
693
694
  },
694
695
  {
695
696
  name: "write_file",
696
- description: "Create or overwrite a text file. content/ requires the author role; templates/, components/, and public/ require the developer role. Read the nanoesis://reference resource first for the authoring syntax.",
697
+ description: "Create or overwrite a text file. content/ requires the author role; templates/, components/, and public/ require the developer role. Read the nanoesis://reference resource first for the authoring syntax. For template/component paths the response includes `schemaDelta` (added/removed/typeChanged fields) \u2014 if `typeChanged` contains `destructive: true` or `removed` is non-empty, an auto-stamp has been written and authored content may need migration, so surface to the user before further edits. See the reference's 'Authoring guardrails for LLMs' section for the common pitfalls (notably: `data-*` annotations bind to the element, not the token inside it \u2014 moving a token out of its annotated container silently strips them).",
697
698
  inputSchema: {
698
699
  type: "object",
699
700
  properties: {
@@ -718,7 +719,7 @@ var TOOL_SPECS = [
718
719
  },
719
720
  {
720
721
  name: "rename_path",
721
- description: "Move or rename a file or folder subtree. Refuses to overwrite an existing destination. Requires write rights at both the source and the destination.",
722
+ description: "Move or rename a file or folder subtree. Refuses to overwrite an existing destination. Requires write rights at both the source and the destination. Renaming a template also requires updating every content item bound to the old name (the validation gate will catch unbound items on publish, but `list_pending_migrations` is the cheaper check).",
722
723
  inputSchema: {
723
724
  type: "object",
724
725
  properties: {
package/dist/index.d.ts CHANGED
@@ -458,22 +458,108 @@ interface CompileInput {
458
458
  declare function compileTemplate(input: CompileInput): string;
459
459
 
460
460
  /**
461
- * Stamping decisions (PROPOSAL-template-versioning §4.1 + §6). `detectStamp` is pure
462
- * given the old and new template sources, it decides whether a destructive change is
463
- * taking place and which tokens are removed / added. The actual snapshot write happens
464
- * inside {@link IndexedStore.write}'s auto-stamp path, which has direct access to the
465
- * underlying BlobStore (so it can write reserved-version paths that the public
466
- * `write` rejects); a standalone `stampSnapshot(store, ...)` for the manual
467
- * "Stamp version" action is deferred to Phase 2 when the editor's button + the
468
- * matching `/api/templates/stamp` route land.
461
+ * The field-type registry (DESIGN §6.7). Field types and their rules are defined
462
+ * *once* here and shared by the compiler, the editor's form generator, and (later)
463
+ * Monaco's IntelliSense. This is the OCP extension point (CLAUDE.md §3): adding a
464
+ * field type means adding an entry here, never editing a switch in the compiler or
465
+ * the inference cascade.
466
+ */
467
+ type FieldType = 'image' | 'file' | 'url' | 'email' | 'phone' | 'date' | 'time' | 'code' | 'richtext' | 'authors' | 'text' | 'shorttext';
468
+ /** How a resolved value is placed into output, the compiler reads this, never the type name. */
469
+ type ValueKind = 'text' | 'html' | 'url' | 'asset' | 'authors';
470
+ interface FieldTypeDef {
471
+ readonly type: FieldType;
472
+ readonly valueKind: ValueKind;
473
+ /** Default editor control hint, consumed by the form generator (DESIGN §7). */
474
+ readonly control: string;
475
+ readonly multiline: boolean;
476
+ }
477
+ declare const FIELD_TYPES: Readonly<Record<FieldType, FieldTypeDef>>;
478
+ declare function isFieldType(value: string): value is FieldType;
479
+ declare function valueKindOf(type: FieldType): ValueKind;
480
+
481
+ /**
482
+ * Derive the editor's form from a template, "the template is the schema"
483
+ * (DESIGN §5.5/§6). Walking the same parse the compiler uses, each page-scope
484
+ * token becomes one field whose control is inferred from where it sits, deduped by
485
+ * name and labelled. This is the single source of truth the editor's form
486
+ * generator and the compiler share (DESIGN §6.7).
487
+ *
488
+ * Inference is **component-aware**: when a component is used (`<doc file="{x}">`),
489
+ * the bound field's type comes from how that prop is used *inside* the component,
490
+ * not from the attribute's position on the usage. A component's prop types are
491
+ * simply the field types of its own body, so the same derivation resolves them,
492
+ * recursively, through nested components, with a cycle guard.
493
+ */
494
+ interface DerivedField {
495
+ readonly name: string;
496
+ readonly type: FieldType;
497
+ readonly label: string;
498
+ readonly required: boolean;
499
+ readonly multiline: boolean;
500
+ readonly help?: string;
501
+ /** Minimum character length (`data-minlength`); the gate warns when shorter. */
502
+ readonly minLength?: number;
503
+ /** Maximum character length (`data-maxlength`); the gate warns when longer. */
504
+ readonly maxLength?: number;
505
+ /** A curated multi-reference (a `data-each` loop field). */
506
+ readonly multiple?: boolean;
507
+ /** Collection the reference picker is scoped to (`data-pick-from`). */
508
+ readonly pickFrom?: string;
509
+ }
510
+ /** Author-declared length bounds on a field value (`data-minlength`/`data-maxlength`). */
511
+ interface LengthConstraints {
512
+ readonly minLength?: number;
513
+ readonly maxLength?: number;
514
+ }
515
+ declare function deriveFields(template: string, components?: ComponentMap): DerivedField[];
516
+
517
+ /**
518
+ * Stamping decisions (PROPOSAL-template-versioning §4.1, PROPOSAL-llm-guardrails §4).
519
+ * `detectStamp` is pure — given the old and new template sources, it decides whether a
520
+ * destructive change is taking place and reports the schema delta (added / removed
521
+ * fields, plus per-token type changes).
522
+ *
523
+ * Two destructive shapes (PROPOSAL-llm-guardrails §1, both share the same recovery
524
+ * story — auto-stamp a snapshot, flag for migration):
469
525
  *
470
- * The destructive-change definition is **token-set asymmetric** (PROPOSAL §2.2): a
471
- * proper superset (additions only) is non-destructive and does not stamp; any removed
472
- * token (including the "rename" case, which reads as remove + add) is destructive and
473
- * triggers a stamp. This is the rule the editor and MCP wire identically through
474
- * `IndexedStore.write`, so the safety story is the same on every authoring path.
526
+ * 1. **Token-set asymmetric.** A token removed or renamed. The token *doesn't exist*
527
+ * in the new schema, so content authored against it has nowhere to render.
528
+ * 2. **Token retained, type changed destructively.** The token *still exists*, but
529
+ * its inferred type can't losslessly render data authored under the old type
530
+ * (`richtext shorttext` silently escapes HTML; `shorttext image` reinterprets
531
+ * free text as a path). The §4 rule in {@link isDestructiveTypeChange} encodes
532
+ * which transitions are destructive vs widening.
533
+ *
534
+ * Both flow through `IndexedStore.write`'s auto-stamp path identically — the editor
535
+ * UI and MCP both see the same recovery affordance, regardless of which destructive
536
+ * shape triggered it.
475
537
  */
476
538
  type TemplateKind = 'template' | 'component';
539
+ /** A field reference (name + inferred type) in the schema delta returned by `computeSchemaDelta`. */
540
+ interface SchemaFieldRef {
541
+ readonly name: string;
542
+ readonly type: FieldType;
543
+ }
544
+ /** A type change on a token retained across the edit (PROPOSAL-llm-guardrails §4-§5). */
545
+ interface TypeChange {
546
+ readonly name: string;
547
+ readonly from: FieldType;
548
+ readonly to: FieldType;
549
+ /** True when the change can't losslessly render data authored under `from`. */
550
+ readonly destructive: boolean;
551
+ }
552
+ /**
553
+ * Structured "what changed" report (PROPOSAL-llm-guardrails §5). Returned by
554
+ * `IndexedStore.write` for template/component writes so callers (MCP, editor UI)
555
+ * can react without re-deriving themselves. Empty arrays everywhere when an edit
556
+ * changes only whitespace, comments, or non-token markup.
557
+ */
558
+ interface SchemaDelta {
559
+ readonly added: readonly SchemaFieldRef[];
560
+ readonly removed: readonly SchemaFieldRef[];
561
+ readonly typeChanged: readonly TypeChange[];
562
+ }
477
563
  /** What `detectStamp` learned from comparing two template sources. */
478
564
  interface StampDecision {
479
565
  readonly destructive: boolean;
@@ -481,6 +567,8 @@ interface StampDecision {
481
567
  readonly removedTokens: readonly string[];
482
568
  /** Tokens present in `newSource` but absent in `oldSource`, sorted for stability. */
483
569
  readonly addedTokens: readonly string[];
570
+ /** Tokens retained but whose inferred type changed (PROPOSAL-llm-guardrails §1). */
571
+ readonly typeChanged: readonly TypeChange[];
484
572
  }
485
573
  /** The record `stampSnapshot` returns (and that the write path surfaces as `stamped`). */
486
574
  interface StampRecord {
@@ -491,13 +579,35 @@ interface StampRecord {
491
579
  /** The full path the snapshot was written to. */
492
580
  readonly snapshotPath: string;
493
581
  }
582
+ /**
583
+ * Whether a field's type change from `from` to `to` can't losslessly render content
584
+ * authored against `from`. Drives both auto-stamp (destructive triggers a snapshot)
585
+ * and the validation gate's `content.type-drift` warning per-item.
586
+ *
587
+ * The rule (PROPOSAL-llm-guardrails §4) reduces to:
588
+ * - `anything → richtext` is safe (HTML escapes text fine).
589
+ * - `richtext → anything else` is destructive (HTML becomes escaped text).
590
+ * - Free-text → strict date/time is destructive (ISO required, free text won't parse).
591
+ * - `date ↔ time` is destructive (both ISO but different shapes; values don't transfer).
592
+ * - Same `valueKind` otherwise is non-destructive (text ↔ text, asset ↔ asset).
593
+ * - Cross-`valueKind` into text is non-destructive (text representation works).
594
+ * - Everything else cross-kind is destructive (text → asset/url/authors; asset ↔ url; …).
595
+ */
596
+ declare function isDestructiveTypeChange(from: FieldType, to: FieldType): boolean;
597
+ /**
598
+ * Compute the schema delta between two field lists. The richer counterpart to the
599
+ * token-name diff in {@link detectStamp} — used by `IndexedStore.write` to surface
600
+ * `schemaDelta` (PROPOSAL-llm-guardrails §5) without re-deriving on the caller side.
601
+ * Pure; component-aware via `deriveFields`.
602
+ */
603
+ declare function computeSchemaDelta(oldFields: readonly DerivedField[], newFields: readonly DerivedField[]): SchemaDelta;
494
604
  /**
495
605
  * Decide whether the change from `oldSource` to `newSource` is destructive, and report
496
- * which tokens were removed / added. Pure — no I/O. The field-name set is derived via
497
- * the same `deriveFields` the editor's form generator uses (so the "schema" the decision
498
- * is taken against is identical to what the form shows the user). Component-aware:
499
- * when components are passed, fields bound to component props are categorised by how
500
- * the component uses them internally, matching the compiler's resolution.
606
+ * the schema diff (added / removed / typeChanged). Pure — no I/O. The field-name +
607
+ * type set is derived via the same `deriveFields` the editor's form generator uses
608
+ * (so the "schema" the decision is taken against is identical to what the form shows).
609
+ * Component-aware: fields bound to component props are categorised by how that
610
+ * component uses them internally, matching the compiler's resolution.
501
611
  */
502
612
  declare function detectStamp(oldSource: string, newSource: string, components?: ComponentMap): StampDecision;
503
613
 
@@ -533,6 +643,16 @@ interface WriteResult {
533
643
  * is known to be unavailable for items affected by this particular stamp.
534
644
  */
535
645
  readonly stampIncomplete?: boolean;
646
+ /**
647
+ * The schema diff between the old and new bytes for template/component writes
648
+ * (PROPOSAL-llm-guardrails §5). Present whenever the target is a template or
649
+ * component and there was a prior version; undefined for content writes, public
650
+ * files, and brand-new templates. Carries `added`/`removed` (with types) and
651
+ * `typeChanged` (with per-change `destructive` flag). MCP callers consume this to
652
+ * react to schema impact without re-deriving; the editor surfaces a summary in the
653
+ * stamp banner.
654
+ */
655
+ readonly schemaDelta?: SchemaDelta;
536
656
  }
537
657
  /**
538
658
  * The editor's working store (DESIGN §11c): a read {@link ContentSource} for
@@ -802,27 +922,6 @@ declare const DOCUMENT_SHELL = "document";
802
922
  */
803
923
  declare function loadDocumentShell(source: ContentSource, templatesDir?: string): Promise<string | undefined>;
804
924
 
805
- /**
806
- * The field-type registry (DESIGN §6.7). Field types and their rules are defined
807
- * *once* here and shared by the compiler, the editor's form generator, and (later)
808
- * Monaco's IntelliSense. This is the OCP extension point (CLAUDE.md §3): adding a
809
- * field type means adding an entry here, never editing a switch in the compiler or
810
- * the inference cascade.
811
- */
812
- type FieldType = 'image' | 'file' | 'url' | 'email' | 'phone' | 'date' | 'time' | 'code' | 'richtext' | 'authors' | 'text' | 'shorttext';
813
- /** How a resolved value is placed into output, the compiler reads this, never the type name. */
814
- type ValueKind = 'text' | 'html' | 'url' | 'asset' | 'authors';
815
- interface FieldTypeDef {
816
- readonly type: FieldType;
817
- readonly valueKind: ValueKind;
818
- /** Default editor control hint, consumed by the form generator (DESIGN §7). */
819
- readonly control: string;
820
- readonly multiline: boolean;
821
- }
822
- declare const FIELD_TYPES: Readonly<Record<FieldType, FieldTypeDef>>;
823
- declare function isFieldType(value: string): value is FieldType;
824
- declare function valueKindOf(type: FieldType): ValueKind;
825
-
826
925
  /**
827
926
  * Contextual token inference (DESIGN §6.2). A token's control type is inferred from
828
927
  * *where it sits*, via a precedence cascade:
@@ -1047,42 +1146,6 @@ declare function buildRss(entries: readonly PageEntry[], options: RssOptions): A
1047
1146
  */
1048
1147
  declare function buildContentIndex(entries: readonly PageEntry[]): Artifact;
1049
1148
 
1050
- /**
1051
- * Derive the editor's form from a template, "the template is the schema"
1052
- * (DESIGN §5.5/§6). Walking the same parse the compiler uses, each page-scope
1053
- * token becomes one field whose control is inferred from where it sits, deduped by
1054
- * name and labelled. This is the single source of truth the editor's form
1055
- * generator and the compiler share (DESIGN §6.7).
1056
- *
1057
- * Inference is **component-aware**: when a component is used (`<doc file="{x}">`),
1058
- * the bound field's type comes from how that prop is used *inside* the component,
1059
- * not from the attribute's position on the usage. A component's prop types are
1060
- * simply the field types of its own body, so the same derivation resolves them,
1061
- * recursively, through nested components, with a cycle guard.
1062
- */
1063
- interface DerivedField {
1064
- readonly name: string;
1065
- readonly type: FieldType;
1066
- readonly label: string;
1067
- readonly required: boolean;
1068
- readonly multiline: boolean;
1069
- readonly help?: string;
1070
- /** Minimum character length (`data-minlength`); the gate warns when shorter. */
1071
- readonly minLength?: number;
1072
- /** Maximum character length (`data-maxlength`); the gate warns when longer. */
1073
- readonly maxLength?: number;
1074
- /** A curated multi-reference (a `data-each` loop field). */
1075
- readonly multiple?: boolean;
1076
- /** Collection the reference picker is scoped to (`data-pick-from`). */
1077
- readonly pickFrom?: string;
1078
- }
1079
- /** Author-declared length bounds on a field value (`data-minlength`/`data-maxlength`). */
1080
- interface LengthConstraints {
1081
- readonly minLength?: number;
1082
- readonly maxLength?: number;
1083
- }
1084
- declare function deriveFields(template: string, components?: ComponentMap): DerivedField[];
1085
-
1086
1149
  /**
1087
1150
  * Static analysis of a template's structure, used by the validation gate, the
1088
1151
  * media phase, and (later) the editor. Extracts the signals downstream needs:
@@ -1597,4 +1660,4 @@ declare function createDiagnosticRegistry(): DiagnosticRegistry;
1597
1660
 
1598
1661
  declare const workingStoreRoundTripDiagnostic: Diagnostic;
1599
1662
 
1600
- export { type Artifact, type ArtifactSink, type AuthEndpoints, type AuthResult, type AuthorDirectory, type AuthorEntry, type AuthorOption, type AuthorRef, type AuthoringReference, type BlobStore, type BoundItem, type ChangePasswordRequest, type ChangePasswordSuccess, type CollectionConfig, type CollectionQuery, type CompileInput, type CompilePageOptions, type CompileSiteOptions, type CompiledPage, type ComponentMap, type ContentIndex, type ContentItem, ContentParseError, type ContentSource, type CreateTokenSuccess, type CreateUserRequest, DEFAULT_DIRS, DOCUMENT_SHELL, type DerivedField, type DiagnoseDeps, type Severity as DiagnoseSeverity, type Source as DiagnoseSource, type Diagnostic$1 as Diagnostic, type Diagnostic as DiagnosticCheck, type DiagnosticRegistry, type DirEntry, type DirNode, type EncodeRequest, type EncodedImage, type EncodedVariant, type EntryKind, FIELD_TYPES, type FieldPrimitive, type FieldRecord, type FieldType, type FieldTypeDef, type FieldValue, type Finding, type IdentityProvider, type ImageEncoder, type ImageFormat, type ImageInfo, InMemoryArtifactSink, InMemoryBlobStore, InMemoryContentSource, IndexedStore, type ItemNode, type LengthConstraints, type LoginRequest, type LoginSuccess, type MediaResolver, type MigrationResolution, type PageEntry, type PendingMigrationItem, type PendingMigrations, type PreBuildHook, type Principal, type PublishOptions, type PublishResult, type PublishSummary, type PurgeService, RESERVED_PREFIX, type ReconcileResult, type RedirectRule, type ReferenceContext, type ReferenceEntry, type ReferenceSection, type RefreshSuccess, type RenameResult, type Repair, type RepairArgs, type ResetPasswordRequest, type ResolveContext, type Role, type RssOptions, type Scope, type Severity$1 as Severity, type SiteConfig, type SortFile, type StampDecision, type StampRecord, type TemplateAnalysis, type TemplateKind, type TokenContext, type TokenRef, type TreeNode, type UpdateUserRequest, type UserAdminEndpoints, type UserSummary, type ValidationResult, type ValueKind, type WorkingStore, analyzeTemplate, applyMigration, baseTemplateName, bestFitSnapshot, buildAuthoringReference, buildContentIndex, buildPictureMarkup, buildRedirects, buildResolveContext, buildRss, buildSitemap, canEdit, compilePage, compileSite, compileTemplate, contentHash, contentTypeFor, createDiagnosticRegistry, deriveFields, detectStamp, emptyIndex, escapeHtmlAttribute, escapeHtmlText, escapeJsonStringContent, findTokens, hasRole, humanize, inferControl, isFieldType, isReservedVersionedPath, isVersionedTemplateName, joinAuthors, loadComponentScripts, loadComponentStyles, loadComponents, loadContentTree, loadDocumentShell, loadIndex, loadRedirects, loadSiteConfig, loadTemplate, nextVersionNumber, noopPurgeService, outputPathForItem, parseContentItem, parseRedirects, parseSortFile, pendingMigrations, processImage, publishSite, reconcileIndex, renderAuthors, renderReferenceMarkdown, sanitizeUrl, saveIndex, slugify, snapshotName, textContent, toAuthorRefs, urlForItem, validateSite, valueKindOf, versionNumber, wholeValueToken, workingStoreRoundTripDiagnostic };
1663
+ export { type Artifact, type ArtifactSink, type AuthEndpoints, type AuthResult, type AuthorDirectory, type AuthorEntry, type AuthorOption, type AuthorRef, type AuthoringReference, type BlobStore, type BoundItem, type ChangePasswordRequest, type ChangePasswordSuccess, type CollectionConfig, type CollectionQuery, type CompileInput, type CompilePageOptions, type CompileSiteOptions, type CompiledPage, type ComponentMap, type ContentIndex, type ContentItem, ContentParseError, type ContentSource, type CreateTokenSuccess, type CreateUserRequest, DEFAULT_DIRS, DOCUMENT_SHELL, type DerivedField, type DiagnoseDeps, type Severity as DiagnoseSeverity, type Source as DiagnoseSource, type Diagnostic$1 as Diagnostic, type Diagnostic as DiagnosticCheck, type DiagnosticRegistry, type DirEntry, type DirNode, type EncodeRequest, type EncodedImage, type EncodedVariant, type EntryKind, FIELD_TYPES, type FieldPrimitive, type FieldRecord, type FieldType, type FieldTypeDef, type FieldValue, type Finding, type IdentityProvider, type ImageEncoder, type ImageFormat, type ImageInfo, InMemoryArtifactSink, InMemoryBlobStore, InMemoryContentSource, IndexedStore, type ItemNode, type LengthConstraints, type LoginRequest, type LoginSuccess, type MediaResolver, type MigrationResolution, type PageEntry, type PendingMigrationItem, type PendingMigrations, type PreBuildHook, type Principal, type PublishOptions, type PublishResult, type PublishSummary, type PurgeService, RESERVED_PREFIX, type ReconcileResult, type RedirectRule, type ReferenceContext, type ReferenceEntry, type ReferenceSection, type RefreshSuccess, type RenameResult, type Repair, type RepairArgs, type ResetPasswordRequest, type ResolveContext, type Role, type RssOptions, type SchemaDelta, type SchemaFieldRef, type Scope, type Severity$1 as Severity, type SiteConfig, type SortFile, type StampDecision, type StampRecord, type TemplateAnalysis, type TemplateKind, type TokenContext, type TokenRef, type TreeNode, type TypeChange, type UpdateUserRequest, type UserAdminEndpoints, type UserSummary, type ValidationResult, type ValueKind, type WorkingStore, analyzeTemplate, applyMigration, baseTemplateName, bestFitSnapshot, buildAuthoringReference, buildContentIndex, buildPictureMarkup, buildRedirects, buildResolveContext, buildRss, buildSitemap, canEdit, compilePage, compileSite, compileTemplate, computeSchemaDelta, contentHash, contentTypeFor, createDiagnosticRegistry, deriveFields, detectStamp, emptyIndex, escapeHtmlAttribute, escapeHtmlText, escapeJsonStringContent, findTokens, hasRole, humanize, inferControl, isDestructiveTypeChange, isFieldType, isReservedVersionedPath, isVersionedTemplateName, joinAuthors, loadComponentScripts, loadComponentStyles, loadComponents, loadContentTree, loadDocumentShell, loadIndex, loadRedirects, loadSiteConfig, loadTemplate, nextVersionNumber, noopPurgeService, outputPathForItem, parseContentItem, parseRedirects, parseSortFile, pendingMigrations, processImage, publishSite, reconcileIndex, renderAuthors, renderReferenceMarkdown, sanitizeUrl, saveIndex, slugify, snapshotName, textContent, toAuthorRefs, urlForItem, validateSite, valueKindOf, versionNumber, wholeValueToken, workingStoreRoundTripDiagnostic };
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  compilePage,
24
24
  compileSite,
25
25
  compileTemplate,
26
+ computeSchemaDelta,
26
27
  contentHash,
27
28
  contentTypeFor,
28
29
  createDiagnosticRegistry,
@@ -36,6 +37,7 @@ import {
36
37
  hasRole,
37
38
  humanize,
38
39
  inferControl,
40
+ isDestructiveTypeChange,
39
41
  isFieldType,
40
42
  isReservedVersionedPath,
41
43
  isVersionedTemplateName,
@@ -73,7 +75,7 @@ import {
73
75
  versionNumber,
74
76
  wholeValueToken,
75
77
  workingStoreRoundTripDiagnostic
76
- } from "./chunk-UZQ7UP2B.js";
78
+ } from "./chunk-XO3CT6GL.js";
77
79
  export {
78
80
  ContentParseError,
79
81
  DEFAULT_DIRS,
@@ -99,6 +101,7 @@ export {
99
101
  compilePage,
100
102
  compileSite,
101
103
  compileTemplate,
104
+ computeSchemaDelta,
102
105
  contentHash,
103
106
  contentTypeFor,
104
107
  createDiagnosticRegistry,
@@ -112,6 +115,7 @@ export {
112
115
  hasRole,
113
116
  humanize,
114
117
  inferControl,
118
+ isDestructiveTypeChange,
115
119
  isFieldType,
116
120
  isReservedVersionedPath,
117
121
  isVersionedTemplateName,
@@ -1,4 +1,4 @@
1
- import{a3 as Ie,ao as F,a1 as Te,V as g,J as k,G as e,f as n,W as Ye,l as a,B as Q,ap as h,t as j,ak as o,E as _,ad as v,m as ze,y as U,s as He,g as Qe,au as Ue,ai as c,aq as W,ae as X,af as _e,K as Xe,ac as Ze,Y as ea}from"./index-LtCzUHAw.js";var aa=_('<p class="placeholder svelte-1lpfi31">Loading preview…</p>'),ta=_('<p class="error svelte-1lpfi31" role="alert"> </p>'),la=_("<option> </option>"),sa=_('<select class="svelte-1lpfi31"></select>'),ra=_('<li class="orphan svelte-1lpfi31"><div class="orphan-head svelte-1lpfi31"><code class="orphan-name svelte-1lpfi31"> </code> <span class="orphan-value svelte-1lpfi31"> </span></div> <div class="orphan-actions svelte-1lpfi31" role="radiogroup"><label class="svelte-1lpfi31"><input type="radio" value="drop"/> Drop</label> <label class="svelte-1lpfi31"><input type="radio" value="keep"/> Keep (unrendered)</label> <label class="svelte-1lpfi31"><input type="radio" value="rename"/> Rename to</label> <!></div></li>'),ia=_(`<section class="resolutions svelte-1lpfi31" aria-label="Orphan field resolutions"><h3 class="svelte-1lpfi31">Orphan fields</h3> <p class="hint svelte-1lpfi31">These fields exist in this item's JSON but the current template doesn't render them.
1
+ import{a3 as Ie,ao as F,a1 as Te,V as g,J as k,G as e,f as n,W as Ye,l as a,B as Q,ap as h,t as j,ak as o,E as _,ad as v,m as ze,y as U,s as He,g as Qe,au as Ue,ai as c,aq as W,ae as X,af as _e,K as Xe,ac as Ze,Y as ea}from"./index-Do1drqEQ.js";var aa=_('<p class="placeholder svelte-1lpfi31">Loading preview…</p>'),ta=_('<p class="error svelte-1lpfi31" role="alert"> </p>'),la=_("<option> </option>"),sa=_('<select class="svelte-1lpfi31"></select>'),ra=_('<li class="orphan svelte-1lpfi31"><div class="orphan-head svelte-1lpfi31"><code class="orphan-name svelte-1lpfi31"> </code> <span class="orphan-value svelte-1lpfi31"> </span></div> <div class="orphan-actions svelte-1lpfi31" role="radiogroup"><label class="svelte-1lpfi31"><input type="radio" value="drop"/> Drop</label> <label class="svelte-1lpfi31"><input type="radio" value="keep"/> Keep (unrendered)</label> <label class="svelte-1lpfi31"><input type="radio" value="rename"/> Rename to</label> <!></div></li>'),ia=_(`<section class="resolutions svelte-1lpfi31" aria-label="Orphan field resolutions"><h3 class="svelte-1lpfi31">Orphan fields</h3> <p class="hint svelte-1lpfi31">These fields exist in this item's JSON but the current template doesn't render them.
2
2
  Pick what to do with each.</p> <ul class="orphans svelte-1lpfi31"></ul></section>`),na=_('<p class="error svelte-1lpfi31" role="alert"> </p>'),oa=_('<div class="diff svelte-1lpfi31"><section class="pane svelte-1lpfi31" aria-label="Previous version (left)"><header class="pane-head svelte-1lpfi31"><!></header> <pre class="source svelte-1lpfi31"> </pre></section> <section class="pane svelte-1lpfi31" aria-label="Current template (right)"><header class="pane-head svelte-1lpfi31"> </header> <pre class="source svelte-1lpfi31"> </pre></section></div> <!> <!> <div class="commit-bar svelte-1lpfi31"><button type="button" class="primary svelte-1lpfi31"> </button></div>',1),va=_('<header class="detail-head svelte-1lpfi31"><button type="button" class="back svelte-1lpfi31">← Back</button> <h2 class="svelte-1lpfi31"> </h2></header> <!>',1),pa=_('<p class="error svelte-1lpfi31" role="alert"> </p>'),ca=_('<p class="placeholder svelte-1lpfi31">Loading…</p>'),fa=_(`<div class="empty svelte-1lpfi31"><h3 class="svelte-1lpfi31">All caught up</h3> <p>Every content item's fields match its bound template. Migrations show up here when a
3
3
  destructive template edit (or a manual hand-edit) leaves an item with orphan fields.</p></div>`),da=_('<li class="item"><button class="item-row svelte-1lpfi31" type="button"><span class="item-path svelte-1lpfi31"><code> </code></span> <span class="item-summary svelte-1lpfi31"><!> <!> <!></span> <span class="open-icon svelte-1lpfi31">→</span></button></li>'),ua=_('<section class="group svelte-1lpfi31"><h3 class="group-title svelte-1lpfi31"><code class="svelte-1lpfi31"> </code> <span class="count svelte-1lpfi31"> </span></h3> <ul class="items svelte-1lpfi31"></ul></section>'),ma=_('<header class="list-head svelte-1lpfi31"><h2>Migrations</h2> <button type="button" class="svelte-1lpfi31"> </button></header> <!>',1),_a=_('<div class="migrations svelte-1lpfi31"><!> <!></div>');function ga(Ee,Le){Ie(Le,!0);let M=F(null),d=F(null),Z=F(!1),R=F(null),r=F(Te({})),he=F(Te({})),A=F(!1),K=F(null);g.ensureLoaded();async function Re(s){v(M,s,!0),v(r,{},!0),v(he,{},!0),v(d,null),v(R,null),v(Z,!0);try{v(d,await ea(s),!0);const f={};for(const l of e(d).orphans){const u=Se(l.name,e(d).currentTemplateFields);f[l.name]=u?{rename:u}:"keep"}v(r,f,!0)}catch(f){v(R,f instanceof Error?f.message:String(f),!0)}finally{v(Z,!1)}}function ge(){v(M,null),v(d,null),v(R,null)}function Se(s,f){const l=s.toLowerCase();for(const u of f)if(u.toLowerCase().startsWith(l)||u.toLowerCase().endsWith(l))return u;return null}async function Ce(){if(!(e(M)===null||e(d)===null)){v(A,!0),v(K,null);try{const s={drop:Object.entries(e(r)).filter(([,l])=>l==="drop").map(([l])=>l),keep:Object.entries(e(r)).filter(([,l])=>l==="keep").map(([l])=>l),rename:Object.fromEntries(Object.entries(e(r)).filter(([,l])=>typeof l=="object"&&l!==null&&"rename"in l).map(([l,u])=>[l,u.rename])),fill:{...e(he)}},f=await Qe(e(M),s);g.refresh().catch(()=>{}),ge()}catch(s){v(K,s instanceof Error?s.message:String(s),!0)}finally{v(A,!1)}}}function Pe(s){return typeof s=="string"?s:JSON.stringify(s)}const qe=Ue(()=>g.list===null?[]:Object.entries(g.list.byTemplate).map(([s,f])=>({template:s,items:[...f].sort((l,u)=>l.path.localeCompare(u.path))})));var be=_a(),ye=a(be);{var Be=s=>{var f=va(),l=Q(f),u=a(l),ee=o(u,2),ae=a(ee),te=o(l,2);{var le=i=>{var m=aa();n(i,m)},se=i=>{var m=ta(),w=a(m);h(()=>c(w,e(R))),n(i,m)},re=i=>{var m=oa(),w=Q(m),E=a(w),O=a(E),N=a(O);{var V=p=>{var y=W();h(()=>c(y,`Before — ${e(d).left.template??""}`)),n(p,y)},$=p=>{var y=W("Before — no snapshot available");n(p,y)};k(N,p=>{e(d).left?p(V):p($,-1)})}var ie=o(O,2),ne=a(ie),oe=o(E,2),D=a(oe),S=a(D),b=o(D,2),G=a(b),C=o(w,2);{var I=p=>{var y=ia(),P=o(a(y),4);U(P,21,()=>e(d).orphans,q=>q.name,(q,t)=>{var x=ra(),z=a(x),xe=a(z),Ke=a(xe),Ne=o(xe,2),Ve=a(Ne),ke=o(z,2),we=a(ke),fe=a(we),Fe=o(we,2),de=a(Fe),je=o(Fe,2),ue=a(je),$e=o(je,2);{var De=B=>{var T=sa();U(T,20,()=>e(d).currentTemplateFields,J=>J,(J,me)=>{var H=la(),Ge=a(H),Oe={};h(()=>{c(Ge,me),Oe!==(Oe=me)&&(H.value=(H.__value=me)??"")}),n(J,H)});var Me;Xe(T),h(()=>{Me!==(Me=e(r)[e(t).name].rename)&&(T.value=(T.__value=e(r)[e(t).name].rename)??"",Ze(T,e(r)[e(t).name].rename))}),j("change",T,J=>v(r,{...e(r),[e(t).name]:{rename:J.currentTarget.value}},!0)),n(B,T)};k($e,B=>{typeof e(r)[e(t).name]=="object"&&e(r)[e(t).name]!==null&&"rename"in e(r)[e(t).name]&&B(De)})}h(B=>{c(Ke,e(t).name),c(Ve,B),X(ke,"aria-label",`Resolution for ${e(t).name}`),X(fe,"name",`d-${e(t).name}`),_e(fe,e(r)[e(t).name]==="drop"),X(de,"name",`d-${e(t).name}`),_e(de,e(r)[e(t).name]==="keep"),X(ue,"name",`d-${e(t).name}`),_e(ue,typeof e(r)[e(t).name]=="object"&&e(r)[e(t).name]!==null&&"rename"in e(r)[e(t).name])},[()=>Pe(e(t).value)]),j("change",fe,()=>v(r,{...e(r),[e(t).name]:"drop"},!0)),j("change",de,()=>v(r,{...e(r),[e(t).name]:"keep"},!0)),j("change",ue,()=>v(r,{...e(r),[e(t).name]:{rename:e(d).currentTemplateFields[0]??""}},!0)),n(q,x)}),n(p,y)};k(C,p=>{e(d).orphans.length>0&&p(I)})}var Y=o(C,2);{var ve=p=>{var y=na(),P=a(y);h(()=>c(P,e(K))),n(p,y)};k(Y,p=>{e(K)&&p(ve)})}var pe=o(Y,2),L=a(pe),ce=a(L);h(()=>{var p;c(ne,((p=e(d).left)==null?void 0:p.html)??"(no snapshot covers this item)"),c(S,`After — ${e(d).right.template??""}`),c(G,e(d).right.html),L.disabled=e(A)||e(d).orphans.length===0,c(ce,e(A)?"Migrating…":"Migrate item")}),j("click",L,Ce),n(i,m)};k(te,i=>{e(Z)?i(le):e(R)?i(se,1):e(d)&&i(re,2)})}h(()=>c(ae,e(M))),j("click",u,ge),n(s,f)},Je=s=>{var f=ma(),l=Q(f),u=o(a(l),2),ee=a(u),ae=o(l,2);{var te=i=>{var m=pa(),w=a(m);h(()=>c(w,g.error)),n(i,m)},le=i=>{var m=ca();n(i,m)},se=i=>{var m=fa();n(i,m)},re=i=>{var m=ze(),w=Q(m);U(w,17,()=>e(qe),E=>E.template,(E,O)=>{var N=ua(),V=a(N),$=a(V),ie=a($),ne=o($,2),oe=a(ne),D=o(V,2);U(D,21,()=>e(O).items,S=>S.path,(S,b)=>{var G=da(),C=a(G),I=a(C),Y=a(I),ve=a(Y),pe=o(I,2),L=a(pe);{var ce=t=>{var x=W();h(z=>c(x,`${e(b).orphanFields.length??""} orphan field${e(b).orphanFields.length===1?"":"s"}:
4
4
  ${z??""}`),[()=>e(b).orphanFields.join(", ")]),n(t,x)};k(L,t=>{e(b).orphanFields.length>0&&t(ce)})}var p=o(L,2);{var y=t=>{var x=W();h(()=>c(x,`· ${e(b).missingRequiredFields.length??""} missing required`)),n(t,x)};k(p,t=>{e(b).missingRequiredFields.length>0&&t(y)})}var P=o(p,2);{var q=t=>{var x=W();h(()=>c(x,`· best-fit: ${e(b).bestFitSnapshot??""}`)),n(t,x)};k(P,t=>{e(b).bestFitSnapshot&&t(q)})}h(()=>c(ve,e(b).path)),j("click",C,()=>Re(e(b).path)),n(S,G)}),h(()=>{c(ie,e(O).template),c(oe,`${e(O).items.length??""} item${e(O).items.length===1?"":"s"}`)}),n(E,N)}),n(i,m)};k(ae,i=>{g.error?i(te):g.loading&&g.list===null?i(le,1):g.count===0?i(se,2):i(re,-1)})}h(()=>{u.disabled=g.loading,c(ee,g.loading?"Refreshing…":"Refresh")}),j("click",u,()=>g.refresh()),n(s,f)};k(ye,s=>{e(M)!==null?s(Be):s(Je,-1)})}var We=o(ye,2);{var Ae=s=>{};k(We,s=>{!e(M)&&g.list===null&&s(Ae)})}n(Ee,be),Ye()}He(["click","change"]);export{ga as default};