@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
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,
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { ApiDeps } from '@nanoesis/editor-api';
2
+
3
+ /**
4
+ * Identity reported to MCP clients on `initialize`. Defaults: `{ name: 'nanoesis', version:
5
+ * '0.0.0' }`. Adopters embedding the HTTP route in their own host typically override `name`
6
+ * with their site identifier and `version` with their app version so client UIs identify
7
+ * the deployment, not the framework.
8
+ */
9
+ interface McpServerIdentity {
10
+ readonly name?: string;
11
+ readonly version?: string;
12
+ }
13
+
14
+ /**
15
+ * Options for {@link handleMcpRequest}. Both fields are passed straight through to the
16
+ * MCP `Server`'s `initialize` response so client UIs (Claude Desktop, Cursor, …) identify
17
+ * the deployment rather than the framework.
18
+ */
19
+ type HandleMcpRequestOptions = McpServerIdentity;
20
+ /**
21
+ * Handle a single MCP HTTP request over the SDK's web-standard Streamable HTTP transport
22
+ * (DESIGN §11c/§14). Stateless: each call builds a fresh `Server` + `Transport` pair, so
23
+ * the caller's per-request `Authorization: Bearer …` header — propagated by the SDK into
24
+ * the request handlers as `extra.requestInfo.headers` — drives the editor's per-user role
25
+ * gate exactly as `/api/*` does.
26
+ *
27
+ * The signature is Fetch-API neutral: any runtime that hands a handler a standard
28
+ * `Request` (Azure Functions v4, Cloudflare Workers, plain Node 18+, Bun, Deno, Hono)
29
+ * wires it the same way:
30
+ *
31
+ * ```ts
32
+ * import { handleMcpRequest } from '@xtrable-ltd/nanoesis/mcp';
33
+ * // ... build `deps` once at startup
34
+ * app.http('mcp', {
35
+ * methods: ['POST', 'GET', 'DELETE'],
36
+ * handler: (req) => handleMcpRequest(deps, req, { name: 'my-site', version: pkg.version }),
37
+ * });
38
+ * ```
39
+ *
40
+ * Runtimes whose request shape is not a standard `Request` (e.g. Azure Functions' enhanced
41
+ * HttpRequest, Express' IncomingMessage) translate to/from `Request`/`Response` at the
42
+ * boundary — that translation is adopter-side because it depends on the host runtime; the
43
+ * MCP server construction and transport handling, which used to be ~120 lines of inlined
44
+ * boilerplate per adopter, is now this one call.
45
+ */
46
+ declare function handleMcpRequest(deps: ApiDeps, request: Request, opts?: HandleMcpRequestOptions): Promise<Response>;
47
+
48
+ export { type HandleMcpRequestOptions, handleMcpRequest };
package/dist/mcp.js ADDED
@@ -0,0 +1,76 @@
1
+ import {
2
+ MCP_RESOURCES,
3
+ MCP_TOOLS,
4
+ callMcpTool,
5
+ readMcpResource
6
+ } from "./chunk-EN5SMEWJ.js";
7
+ import "./chunk-XO3CT6GL.js";
8
+
9
+ // ../../hosts/host-mcp/src/http.ts
10
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
11
+
12
+ // ../../hosts/host-mcp/src/server.ts
13
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
+ import {
15
+ CallToolRequestSchema,
16
+ ListResourcesRequestSchema,
17
+ ListToolsRequestSchema,
18
+ ReadResourceRequestSchema
19
+ } from "@modelcontextprotocol/sdk/types.js";
20
+ var BEARER_PREFIX = /^Bearer\s+/i;
21
+ function bearerFrom(headers) {
22
+ if (headers === void 0) return void 0;
23
+ const raw = headers["authorization"] ?? headers["Authorization"];
24
+ const value = Array.isArray(raw) ? raw[0] : raw;
25
+ if (value === void 0) return void 0;
26
+ const match = BEARER_PREFIX.exec(value);
27
+ return match ? value.slice(match[0].length).trim() : void 0;
28
+ }
29
+ function createMcpServer(deps, identity) {
30
+ const server = new Server(
31
+ { name: identity?.name ?? "nanoesis", version: identity?.version ?? "0.0.0" },
32
+ { capabilities: { tools: {}, resources: {} } }
33
+ );
34
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...MCP_TOOLS] }));
35
+ server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
36
+ const args = req.params.arguments ?? {};
37
+ const token = bearerFrom(extra.requestInfo?.headers);
38
+ const result = await callMcpTool(
39
+ deps,
40
+ req.params.name,
41
+ args,
42
+ token !== void 0 ? { token } : {}
43
+ );
44
+ return { content: [{ type: "text", text: result.text }], isError: result.isError };
45
+ });
46
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
47
+ resources: [...MCP_RESOURCES]
48
+ }));
49
+ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
50
+ const { uri } = req.params;
51
+ const resource = readMcpResource(uri);
52
+ if (resource === void 0) throw new Error(`Unknown resource: ${uri}`);
53
+ return { contents: [{ uri, mimeType: resource.mimeType, text: resource.text }] };
54
+ });
55
+ return server;
56
+ }
57
+
58
+ // ../../hosts/host-mcp/src/http.ts
59
+ var DEFAULT_VERSION = true ? "0.1.12" : "0.0.0-workspace";
60
+ async function handleMcpRequest(deps, request, opts) {
61
+ const server = createMcpServer(deps, {
62
+ name: opts?.name ?? "nanoesis",
63
+ version: opts?.version ?? DEFAULT_VERSION
64
+ });
65
+ const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
66
+ await server.connect(transport);
67
+ try {
68
+ return await transport.handleRequest(request);
69
+ } finally {
70
+ await transport.close();
71
+ await server.close();
72
+ }
73
+ }
74
+ export {
75
+ handleMcpRequest
76
+ };
@@ -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};