@xtrable-ltd/nanoesis 0.1.10 → 0.1.12

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 (32) hide show
  1. package/dist/adapter-azure-blob.js +1 -1
  2. package/dist/chunk-EN5SMEWJ.js +1221 -0
  3. package/dist/{chunk-UZQ7UP2B.js → chunk-XO3CT6GL.js} +167 -15
  4. package/dist/editor-api.js +21 -1197
  5. package/dist/index.d.ts +139 -76
  6. package/dist/index.js +5 -1
  7. package/dist/mcp.d.ts +48 -0
  8. package/dist/mcp.js +76 -0
  9. package/editor/assets/{MigrationsPane-_FGonx4-.js → MigrationsPane-BYGqWBAA.js} +1 -1
  10. package/editor/assets/{TemplatesPane-Z6Bn69Hb.js → TemplatesPane-B5hn_v0Z.js} +168 -164
  11. package/editor/assets/{TemplatesPane-CiLiMCc8.css → TemplatesPane-D0gGehUt.css} +1 -1
  12. package/editor/assets/{cssMode-dkQrIPWx.js → cssMode-BbIf5k6I.js} +1 -1
  13. package/editor/assets/{freemarker2-DEqcFFWa.js → freemarker2-DoW0pSYV.js} +1 -1
  14. package/editor/assets/{handlebars-C6ojANWr.js → handlebars-DLlET-qc.js} +1 -1
  15. package/editor/assets/{html-BmiAmVUD.js → html-4khbqrhe.js} +1 -1
  16. package/editor/assets/{htmlMode-BBmUqToI.js → htmlMode-DblHkZ-k.js} +1 -1
  17. package/editor/assets/index-CkESQLMV.css +7 -0
  18. package/editor/assets/index-Do1drqEQ.js +138 -0
  19. package/editor/assets/{javascript-Cxm2TfJy.js → javascript-CgPO2Hmj.js} +1 -1
  20. package/editor/assets/{jsonMode-CW5012Hx.js → jsonMode-BrWh2436.js} +1 -1
  21. package/editor/assets/{liquid-DrS7ilHv.js → liquid-BsQJXwPT.js} +1 -1
  22. package/editor/assets/{mdx-CwdSU5o1.js → mdx-AO8t67gx.js} +1 -1
  23. package/editor/assets/{python-CALCR0yC.js → python-3w4sZj5c.js} +1 -1
  24. package/editor/assets/{razor-SVCo2LoM.js → razor-BFsvo06w.js} +1 -1
  25. package/editor/assets/{tsMode-CzXfTR_Q.js → tsMode-QrC4ERjp.js} +1 -1
  26. package/editor/assets/{typescript-CP0Ovrv7.js → typescript-BXJ3QLad.js} +1 -1
  27. package/editor/assets/{xml-B2yqloTa.js → xml-CxKYn1FP.js} +1 -1
  28. package/editor/assets/{yaml-DTLJhzgY.js → yaml-BmWLvF7Q.js} +1 -1
  29. package/editor/index.html +2 -2
  30. package/package.json +7 -1
  31. package/editor/assets/index-DEz8GUII.css +0 -7
  32. package/editor/assets/index-LtCzUHAw.js +0 -138
@@ -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,