@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.
- package/dist/adapter-azure-blob.js +1 -1
- package/dist/{chunk-UZQ7UP2B.js → chunk-XO3CT6GL.js} +167 -15
- package/dist/editor-api.js +5 -4
- package/dist/index.d.ts +139 -76
- package/dist/index.js +5 -1
- package/editor/assets/{MigrationsPane-_FGonx4-.js → MigrationsPane-BYGqWBAA.js} +1 -1
- package/editor/assets/{TemplatesPane-Z6Bn69Hb.js → TemplatesPane-B5hn_v0Z.js} +168 -164
- package/editor/assets/{TemplatesPane-CiLiMCc8.css → TemplatesPane-D0gGehUt.css} +1 -1
- package/editor/assets/{cssMode-dkQrIPWx.js → cssMode-BbIf5k6I.js} +1 -1
- package/editor/assets/{freemarker2-DEqcFFWa.js → freemarker2-DoW0pSYV.js} +1 -1
- package/editor/assets/{handlebars-C6ojANWr.js → handlebars-DLlET-qc.js} +1 -1
- package/editor/assets/{html-BmiAmVUD.js → html-4khbqrhe.js} +1 -1
- package/editor/assets/{htmlMode-BBmUqToI.js → htmlMode-DblHkZ-k.js} +1 -1
- package/editor/assets/index-CkESQLMV.css +7 -0
- package/editor/assets/index-Do1drqEQ.js +138 -0
- package/editor/assets/{javascript-Cxm2TfJy.js → javascript-CgPO2Hmj.js} +1 -1
- package/editor/assets/{jsonMode-CW5012Hx.js → jsonMode-BrWh2436.js} +1 -1
- package/editor/assets/{liquid-DrS7ilHv.js → liquid-BsQJXwPT.js} +1 -1
- package/editor/assets/{mdx-CwdSU5o1.js → mdx-AO8t67gx.js} +1 -1
- package/editor/assets/{python-CALCR0yC.js → python-3w4sZj5c.js} +1 -1
- package/editor/assets/{razor-SVCo2LoM.js → razor-BFsvo06w.js} +1 -1
- package/editor/assets/{tsMode-CzXfTR_Q.js → tsMode-QrC4ERjp.js} +1 -1
- package/editor/assets/{typescript-CP0Ovrv7.js → typescript-BXJ3QLad.js} +1 -1
- package/editor/assets/{xml-B2yqloTa.js → xml-CxKYn1FP.js} +1 -1
- package/editor/assets/{yaml-DTLJhzgY.js → yaml-BmWLvF7Q.js} +1 -1
- package/editor/index.html +2 -2
- package/package.json +1 -1
- package/editor/assets/index-DEz8GUII.css +0 -7
- 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
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
const
|
|
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
|
-
|
|
480
|
-
|
|
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
|
|
483
|
-
if (!
|
|
506
|
+
for (const [name, newField] of newByName) {
|
|
507
|
+
if (!oldByName.has(name)) added.push({ name, type: newField.type });
|
|
484
508
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
|
699
|
-
|
|
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,
|
package/dist/editor-api.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
renderReferenceMarkdown,
|
|
16
16
|
validateSite,
|
|
17
17
|
workingStoreRoundTripDiagnostic
|
|
18
|
-
} from "./chunk-
|
|
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
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
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
|
-
*
|
|
497
|
-
* the same `deriveFields` the editor's form generator uses
|
|
498
|
-
* is taken against is identical to what the form shows
|
|
499
|
-
*
|
|
500
|
-
*
|
|
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-
|
|
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-
|
|
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};
|