@xtrable-ltd/nanoesis 0.1.21 → 0.1.22

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.
@@ -1,5 +1,5 @@
1
1
  import { ContainerClient } from '@azure/storage-blob';
2
- import { BlobStore, ArtifactSink } from '@nanoesis/engine';
2
+ import { BlobStore, ArtifactSink, Storage } from '@nanoesis/engine';
3
3
  export { contentTypeFor } from '@nanoesis/engine';
4
4
 
5
5
  /**
@@ -34,7 +34,7 @@ declare class InMemoryBlobContainer implements BlobContainer {
34
34
  constructor(seed?: Readonly<Record<string, Uint8Array | string>>);
35
35
  list(prefix: string): Promise<readonly string[]>;
36
36
  read(name: string): Promise<Uint8Array | null>;
37
- write(name: string, data: Uint8Array): Promise<void>;
37
+ write(name: string, data: Uint8Array, contentType?: string): Promise<void>;
38
38
  remove(name: string): Promise<void>;
39
39
  /** Test helper: the current blob names. */
40
40
  get names(): readonly string[];
@@ -90,6 +90,15 @@ declare class BlobArtifactSink implements ArtifactSink {
90
90
  write(path: string, contents: string | Uint8Array): Promise<void>;
91
91
  }
92
92
 
93
+ /**
94
+ * A ready-made {@link Storage} over an Azure Blob container: get/put/delete (via
95
+ * {@link BlobContainerStore}) plus `wipe` (delete every blob). Pass it to `createEditor`
96
+ * as `editorFiles` (the working container) and `website` (the published `$web` container).
97
+ * For the working container, also pass `enumerate: () => container.list('')` so the editor
98
+ * can reconcile its content index.
99
+ */
100
+ declare function azureStorage(container: BlobContainer): Storage;
101
+
93
102
  /**
94
103
  * Pure path/name helper for the blob adapters. Blob storage is a *flat* namespace, a blob
95
104
  * is just a name like "content/blog/post.json", so the only mapping the SDK glue needs is
@@ -100,4 +109,4 @@ declare class BlobArtifactSink implements ArtifactSink {
100
109
  /** Normalise a site path to a blob name: forward slashes, no leading/trailing slash. */
101
110
  declare function normalizeBlobName(path: string): string;
102
111
 
103
- export { AzureBlobContainer, BlobArtifactSink, type BlobContainer, BlobContainerStore, InMemoryBlobContainer, normalizeBlobName };
112
+ export { AzureBlobContainer, BlobArtifactSink, type BlobContainer, BlobContainerStore, InMemoryBlobContainer, azureStorage, normalizeBlobName };
@@ -20,7 +20,8 @@ var InMemoryBlobContainer = class {
20
20
  async read(name) {
21
21
  return this.blobs.get(name) ?? null;
22
22
  }
23
- async write(name, data) {
23
+ async write(name, data, contentType) {
24
+ void contentType;
24
25
  this.blobs.set(name, data);
25
26
  }
26
27
  async remove(name) {
@@ -117,11 +118,25 @@ var BlobArtifactSink = class {
117
118
  await this.container.write(name, data, contentTypeFor(name));
118
119
  }
119
120
  };
121
+
122
+ // ../../adapters/azure-blob/src/azure-storage.ts
123
+ function azureStorage(container) {
124
+ const store = new BlobContainerStore(container);
125
+ return {
126
+ get: (key) => store.get(key),
127
+ put: (key, bytes) => store.put(key, bytes),
128
+ delete: (key) => store.delete(key),
129
+ wipe: async () => {
130
+ for (const name of await container.list("")) await container.remove(name);
131
+ }
132
+ };
133
+ }
120
134
  export {
121
135
  AzureBlobContainer,
122
136
  BlobArtifactSink,
123
137
  BlobContainerStore,
124
138
  InMemoryBlobContainer,
139
+ azureStorage,
125
140
  contentTypeFor,
126
141
  normalizeBlobName
127
142
  };
@@ -1,4 +1,4 @@
1
- import { BlobStore, ArtifactSink } from '@nanoesis/engine';
1
+ import { BlobStore, ArtifactSink, Storage } from '@nanoesis/engine';
2
2
 
3
3
  /**
4
4
  * Filesystem implementation of the engine's {@link BlobStore} port (DESIGN §11c): get /
@@ -35,4 +35,13 @@ declare class FsArtifactSink implements ArtifactSink {
35
35
  wipe(): Promise<void>;
36
36
  }
37
37
 
38
- export { FsArtifactSink, FsBlobStore };
38
+ /**
39
+ * A ready-made {@link Storage} over a local directory: get/put/delete (via
40
+ * {@link FsBlobStore}) plus `wipe` (remove the directory). Pass it straight to
41
+ * `createEditor` as `editorFiles` and/or `website` for a zero-implementation local host
42
+ * (DESIGN §11c): `createEditor({ editorFiles: folderStorage('./site'), website:
43
+ * folderStorage('./public'), login: devNoAuth() })`.
44
+ */
45
+ declare function folderStorage(root: string): Storage;
46
+
47
+ export { FsArtifactSink, FsBlobStore, folderStorage };
@@ -48,7 +48,20 @@ var FsArtifactSink = class {
48
48
  await rm2(this.root, { recursive: true, force: true });
49
49
  }
50
50
  };
51
+
52
+ // ../../adapters/fs/src/folder-storage.ts
53
+ import { rm as rm3 } from "fs/promises";
54
+ function folderStorage(root) {
55
+ const blobs = new FsBlobStore(root);
56
+ return {
57
+ get: (key) => blobs.get(key),
58
+ put: (key, bytes) => blobs.put(key, bytes),
59
+ delete: (key) => blobs.delete(key),
60
+ wipe: () => rm3(root, { recursive: true, force: true })
61
+ };
62
+ }
51
63
  export {
52
64
  FsArtifactSink,
53
- FsBlobStore
65
+ FsBlobStore,
66
+ folderStorage
54
67
  };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ IndexedStore,
2
3
  analyzeTemplate,
3
4
  applyMigration,
4
5
  baseTemplateName,
@@ -12,6 +13,7 @@ import {
12
13
  loadComponents,
13
14
  loadTemplate,
14
15
  pendingMigrations,
16
+ publishSite,
15
17
  renderReferenceMarkdown,
16
18
  validateSite,
17
19
  workingStoreRoundTripDiagnostic
@@ -633,202 +635,6 @@ function authorDirectory(users) {
633
635
  };
634
636
  }
635
637
 
636
- // ../editor-api/src/mcp.ts
637
- function str(args, key) {
638
- const value = args[key];
639
- return typeof value === "string" ? value : "";
640
- }
641
- function request(method, path, query, bodyText, token) {
642
- const body = bodyText === void 0 ? new Uint8Array() : new TextEncoder().encode(bodyText);
643
- return {
644
- method,
645
- path,
646
- query: new URLSearchParams(query),
647
- getHeader: (name) => name.toLowerCase() === "authorization" && token !== void 0 ? `Bearer ${token}` : void 0,
648
- body: async () => body
649
- };
650
- }
651
- var pathArg = {
652
- type: "string",
653
- description: 'Site-relative path with forward slashes, e.g. "content/blog/post.json".'
654
- };
655
- var TOOL_SPECS = [
656
- {
657
- name: "list_dir",
658
- description: "List the immediate children (files and folders) of a site directory. Use '' or omit for the site root; the top-level folders are content/ (pages), templates/, components/, and public/.",
659
- inputSchema: {
660
- type: "object",
661
- properties: {
662
- path: { type: "string", description: "Directory path, '' for the site root." }
663
- },
664
- additionalProperties: false
665
- },
666
- toRequest: (args, token) => request("GET", "/api/list", { dir: str(args, "path") }, void 0, token)
667
- },
668
- {
669
- name: "read_file",
670
- description: "Read a file's text contents (content JSON, a template/component's HTML, CSS, etc.). Reports an error if the file does not exist.",
671
- inputSchema: {
672
- type: "object",
673
- properties: { path: pathArg },
674
- required: ["path"],
675
- additionalProperties: false
676
- },
677
- toRequest: (args, token) => request("GET", "/api/read", { path: str(args, "path") }, void 0, token)
678
- },
679
- {
680
- name: "describe_template",
681
- description: "List the fields a template defines (name, type, label, whether required, length limits), so you know what a page using it needs. Pass the template name without its path or extension, e.g. 'article' for templates/article.html.",
682
- inputSchema: {
683
- type: "object",
684
- properties: {
685
- name: {
686
- type: "string",
687
- description: 'Template name without path or extension, e.g. "article".'
688
- }
689
- },
690
- required: ["name"],
691
- additionalProperties: false
692
- },
693
- toRequest: (args, token) => request("GET", "/api/template-fields", { name: str(args, "name") }, void 0, token)
694
- },
695
- {
696
- name: "write_file",
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. For content/*.json writes the response may include `parseDiagnostics.unknownTopLevelKeys` \u2014 top-level keys the parser dropped because they are not system keys; content values belong under `fields` (system keys are case-sensitive: `isPublished`, not `IsPublished`). 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).",
698
- inputSchema: {
699
- type: "object",
700
- properties: {
701
- path: pathArg,
702
- contents: { type: "string", description: "The full new text contents of the file." }
703
- },
704
- required: ["path", "contents"],
705
- additionalProperties: false
706
- },
707
- toRequest: (args, token) => request("POST", "/api/write", { path: str(args, "path") }, str(args, "contents"), token)
708
- },
709
- {
710
- name: "delete_path",
711
- description: "Delete a file or a whole folder subtree. Same role rules as write_file. Deleting a path that does not exist is a no-op.",
712
- inputSchema: {
713
- type: "object",
714
- properties: { path: pathArg },
715
- required: ["path"],
716
- additionalProperties: false
717
- },
718
- toRequest: (args, token) => request("POST", "/api/delete", { path: str(args, "path") }, void 0, token)
719
- },
720
- {
721
- name: "rename_path",
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).",
723
- inputSchema: {
724
- type: "object",
725
- properties: {
726
- from: { type: "string", description: "The current path." },
727
- to: { type: "string", description: "The new path." }
728
- },
729
- required: ["from", "to"],
730
- additionalProperties: false
731
- },
732
- toRequest: (args, token) => request(
733
- "POST",
734
- "/api/rename",
735
- { from: str(args, "from"), to: str(args, "to") },
736
- void 0,
737
- token
738
- )
739
- },
740
- {
741
- name: "validate",
742
- description: "Run the validation gate over the whole site and return its diagnostics, without publishing. Use it to check your work: errors would block a publish, warnings (e.g. length constraints) only inform.",
743
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
744
- toRequest: (_args, token) => request("GET", "/api/validate", {}, void 0, token)
745
- },
746
- {
747
- name: "publish",
748
- description: "Compile and publish the whole site. Runs the validation gate first; if anything would break, it reports the problems and writes nothing.",
749
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
750
- toRequest: (_args, token) => request("POST", "/api/publish", {}, void 0, token)
751
- },
752
- {
753
- name: "list_pending_migrations",
754
- description: "List content items whose JSON fields don't match their bound template's schema (orphan fields or missing required fields). Use this after a destructive template edit (you'll see a `stamped` record on write_file) to see what needs migrating, or any time to check site-wide drift.",
755
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
756
- toRequest: (_args, token) => request("GET", "/api/migrations", {}, void 0, token)
757
- },
758
- {
759
- name: "preview_migration",
760
- description: 'For one content item, return the current template HTML, the best-fit snapshot HTML (the "before"), the list of orphan fields with their values, and the names of fields the current template defines. Use this to plan how to migrate (drop, rename, or keep each orphan).',
761
- inputSchema: {
762
- type: "object",
763
- properties: { path: pathArg },
764
- required: ["path"],
765
- additionalProperties: false
766
- },
767
- toRequest: (args, token) => request("GET", "/api/migrations/preview", { path: str(args, "path") }, void 0, token)
768
- },
769
- {
770
- name: "apply_migration",
771
- description: "Resolve a content item's schema drift in one go. `resolution` is a JSON object: `drop` (array of orphan field names to delete), `rename` (object mapping orphan field name \u2192 current template field name to copy the value into), `keep` (array of orphan field names to leave untouched), and `fill` (object mapping current template field name \u2192 value, for missing required fields). Returns the refreshed pending list.",
772
- inputSchema: {
773
- type: "object",
774
- properties: {
775
- path: pathArg,
776
- resolution: {
777
- type: "string",
778
- description: 'A JSON object encoded as a string: { "drop": string[], "rename": { from: to }, "keep": string[], "fill": { name: value } }.'
779
- }
780
- },
781
- required: ["path", "resolution"],
782
- additionalProperties: false
783
- },
784
- toRequest: (args, token) => request(
785
- "POST",
786
- "/api/migrations/apply",
787
- {},
788
- JSON.stringify({
789
- path: str(args, "path"),
790
- resolution: parseResolutionArg(str(args, "resolution"))
791
- }),
792
- token
793
- )
794
- }
795
- ];
796
- function parseResolutionArg(raw) {
797
- if (raw === "") return {};
798
- try {
799
- return JSON.parse(raw);
800
- } catch {
801
- return {};
802
- }
803
- }
804
- var MCP_TOOLS = TOOL_SPECS.map(
805
- ({ name, description, inputSchema }) => ({ name, description, inputSchema })
806
- );
807
- function toResult(response) {
808
- const text = typeof response.body === "string" ? response.body : response.body === void 0 ? "" : new TextDecoder().decode(response.body);
809
- return { text, isError: response.status >= 400 };
810
- }
811
- async function callMcpTool(deps, name, args = {}, options = {}) {
812
- const spec = TOOL_SPECS.find((tool) => tool.name === name);
813
- if (spec === void 0) return { text: `Unknown tool: ${name}`, isError: true };
814
- return toResult(await handleApi(deps, spec.toRequest(args, options.token)));
815
- }
816
- var REFERENCE_URI = "nanoesis://reference";
817
- var MCP_RESOURCES = [
818
- {
819
- uri: REFERENCE_URI,
820
- name: "nanoesis authoring reference",
821
- description: "The generated reference for nanoesis templates and content: tokens, field types, annotations, loops, components, and the document shell. Read this before writing templates or content.",
822
- mimeType: "text/markdown"
823
- }
824
- ];
825
- function readMcpResource(uri) {
826
- if (uri === REFERENCE_URI) {
827
- return { text: renderReferenceMarkdown(buildAuthoringReference()), mimeType: "text/markdown" };
828
- }
829
- return void 0;
830
- }
831
-
832
638
  // ../editor-api/src/diagnose.ts
833
639
  function buildDefaultDiagnostics() {
834
640
  const registry = createDiagnosticRegistry();
@@ -1101,8 +907,313 @@ async function anyPublishedContentItem(store, dir = "content") {
1101
907
  return false;
1102
908
  }
1103
909
 
910
+ // ../editor-api/src/create-editor.ts
911
+ function asSink(storage) {
912
+ const encoder = new TextEncoder();
913
+ return {
914
+ write: (path, contents) => storage.put(path, typeof contents === "string" ? encoder.encode(contents) : contents)
915
+ };
916
+ }
917
+ function createEditor(config) {
918
+ const working = new IndexedStore(config.editorFiles);
919
+ const sink = asSink(config.website);
920
+ const wipeBeforePublish = config.wipeBeforePublish ?? true;
921
+ const reconcile = config.enumerate === void 0 ? void 0 : async () => working.reconcile([...await config.enumerate()]);
922
+ const publish = async () => {
923
+ const validation = await validateSite(working);
924
+ if (validation.ok && wipeBeforePublish && config.website.wipe !== void 0) {
925
+ await config.website.wipe();
926
+ }
927
+ const dir = config.users === void 0 ? void 0 : authorDirectory(await config.users());
928
+ const prebuild = typeof config.prebuild === "function" ? await config.prebuild() : config.prebuild;
929
+ return publishSite(working, sink, {
930
+ ...config.images !== void 0 && { imageEncoder: config.images },
931
+ ...config.purge !== void 0 && { purge: config.purge },
932
+ ...config.baseUrl !== void 0 && { baseUrl: config.baseUrl },
933
+ ...dir !== void 0 && { authorDirectory: dir },
934
+ ...prebuild !== void 0 && { prebuild }
935
+ });
936
+ };
937
+ const users = config.users;
938
+ const deps = {
939
+ store: working,
940
+ identity: config.login,
941
+ publish,
942
+ diagnostics: config.diagnostics ?? buildDefaultDiagnostics(),
943
+ ...reconcile !== void 0 && { reconcile },
944
+ ...config.authEndpoints !== void 0 && { authEndpoints: config.authEndpoints },
945
+ ...config.userAdmin !== void 0 && { userAdmin: config.userAdmin },
946
+ ...config.branding !== void 0 && { branding: config.branding },
947
+ ...users !== void 0 && { authors: async () => authorOptions(await users()) }
948
+ };
949
+ return {
950
+ handleApi: (req) => handleApi(deps, req),
951
+ publish,
952
+ store: working,
953
+ deps
954
+ };
955
+ }
956
+ function devNoAuth() {
957
+ console.warn(
958
+ "[nanoesis] devNoAuth(): every request is treated as an admin. Do not use in production."
959
+ );
960
+ return {
961
+ authenticate: async () => ({
962
+ userId: "dev",
963
+ username: "dev",
964
+ roles: ["author", "developer", "admin"]
965
+ })
966
+ };
967
+ }
968
+
969
+ // ../editor-api/src/serve-asset.ts
970
+ import { readFile } from "fs/promises";
971
+ import { resolve, sep } from "path";
972
+ var MIME = {
973
+ ".html": "text/html; charset=utf-8",
974
+ ".js": "application/javascript; charset=utf-8",
975
+ ".mjs": "application/javascript; charset=utf-8",
976
+ ".css": "text/css; charset=utf-8",
977
+ ".json": "application/json; charset=utf-8",
978
+ ".svg": "image/svg+xml",
979
+ ".png": "image/png",
980
+ ".jpg": "image/jpeg",
981
+ ".jpeg": "image/jpeg",
982
+ ".ico": "image/x-icon",
983
+ ".woff": "font/woff",
984
+ ".woff2": "font/woff2",
985
+ ".map": "application/json; charset=utf-8"
986
+ };
987
+ function mimeFor(path) {
988
+ const dot = path.lastIndexOf(".");
989
+ return dot < 0 ? "application/octet-stream" : MIME[path.slice(dot).toLowerCase()] ?? "application/octet-stream";
990
+ }
991
+ async function serveEditorAsset(distDir, pathname, options = {}) {
992
+ if (pathname === "/config.json" && options.config !== void 0) {
993
+ return {
994
+ status: 200,
995
+ headers: { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" },
996
+ body: JSON.stringify(options.config)
997
+ };
998
+ }
999
+ const base = resolve(distDir);
1000
+ const requested = pathname === "/" || pathname === "" ? "/index.html" : pathname;
1001
+ const filePath = resolve(base, requested.replace(/^\/+/, ""));
1002
+ if (filePath !== base && !filePath.startsWith(base + sep)) {
1003
+ return { status: 403, headers: { "content-type": "text/plain" }, body: "Forbidden" };
1004
+ }
1005
+ try {
1006
+ const bytes = new Uint8Array(await readFile(filePath));
1007
+ return { status: 200, headers: { "content-type": mimeFor(filePath) }, body: bytes };
1008
+ } catch (error) {
1009
+ if (error.code !== "ENOENT") throw error;
1010
+ const indexBytes = new Uint8Array(await readFile(resolve(base, "index.html")));
1011
+ return {
1012
+ status: 200,
1013
+ headers: { "content-type": "text/html; charset=utf-8" },
1014
+ body: indexBytes
1015
+ };
1016
+ }
1017
+ }
1018
+
1019
+ // ../editor-api/src/mcp.ts
1020
+ function str(args, key) {
1021
+ const value = args[key];
1022
+ return typeof value === "string" ? value : "";
1023
+ }
1024
+ function request(method, path, query, bodyText, token) {
1025
+ const body = bodyText === void 0 ? new Uint8Array() : new TextEncoder().encode(bodyText);
1026
+ return {
1027
+ method,
1028
+ path,
1029
+ query: new URLSearchParams(query),
1030
+ getHeader: (name) => name.toLowerCase() === "authorization" && token !== void 0 ? `Bearer ${token}` : void 0,
1031
+ body: async () => body
1032
+ };
1033
+ }
1034
+ var pathArg = {
1035
+ type: "string",
1036
+ description: 'Site-relative path with forward slashes, e.g. "content/blog/post.json".'
1037
+ };
1038
+ var TOOL_SPECS = [
1039
+ {
1040
+ name: "list_dir",
1041
+ description: "List the immediate children (files and folders) of a site directory. Use '' or omit for the site root; the top-level folders are content/ (pages), templates/, components/, and public/.",
1042
+ inputSchema: {
1043
+ type: "object",
1044
+ properties: {
1045
+ path: { type: "string", description: "Directory path, '' for the site root." }
1046
+ },
1047
+ additionalProperties: false
1048
+ },
1049
+ toRequest: (args, token) => request("GET", "/api/list", { dir: str(args, "path") }, void 0, token)
1050
+ },
1051
+ {
1052
+ name: "read_file",
1053
+ description: "Read a file's text contents (content JSON, a template/component's HTML, CSS, etc.). Reports an error if the file does not exist.",
1054
+ inputSchema: {
1055
+ type: "object",
1056
+ properties: { path: pathArg },
1057
+ required: ["path"],
1058
+ additionalProperties: false
1059
+ },
1060
+ toRequest: (args, token) => request("GET", "/api/read", { path: str(args, "path") }, void 0, token)
1061
+ },
1062
+ {
1063
+ name: "describe_template",
1064
+ description: "List the fields a template defines (name, type, label, whether required, length limits), so you know what a page using it needs. Pass the template name without its path or extension, e.g. 'article' for templates/article.html.",
1065
+ inputSchema: {
1066
+ type: "object",
1067
+ properties: {
1068
+ name: {
1069
+ type: "string",
1070
+ description: 'Template name without path or extension, e.g. "article".'
1071
+ }
1072
+ },
1073
+ required: ["name"],
1074
+ additionalProperties: false
1075
+ },
1076
+ toRequest: (args, token) => request("GET", "/api/template-fields", { name: str(args, "name") }, void 0, token)
1077
+ },
1078
+ {
1079
+ name: "write_file",
1080
+ 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. For content/*.json writes the response may include `parseDiagnostics.unknownTopLevelKeys` \u2014 top-level keys the parser dropped because they are not system keys; content values belong under `fields` (system keys are case-sensitive: `isPublished`, not `IsPublished`). 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).",
1081
+ inputSchema: {
1082
+ type: "object",
1083
+ properties: {
1084
+ path: pathArg,
1085
+ contents: { type: "string", description: "The full new text contents of the file." }
1086
+ },
1087
+ required: ["path", "contents"],
1088
+ additionalProperties: false
1089
+ },
1090
+ toRequest: (args, token) => request("POST", "/api/write", { path: str(args, "path") }, str(args, "contents"), token)
1091
+ },
1092
+ {
1093
+ name: "delete_path",
1094
+ description: "Delete a file or a whole folder subtree. Same role rules as write_file. Deleting a path that does not exist is a no-op.",
1095
+ inputSchema: {
1096
+ type: "object",
1097
+ properties: { path: pathArg },
1098
+ required: ["path"],
1099
+ additionalProperties: false
1100
+ },
1101
+ toRequest: (args, token) => request("POST", "/api/delete", { path: str(args, "path") }, void 0, token)
1102
+ },
1103
+ {
1104
+ name: "rename_path",
1105
+ 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).",
1106
+ inputSchema: {
1107
+ type: "object",
1108
+ properties: {
1109
+ from: { type: "string", description: "The current path." },
1110
+ to: { type: "string", description: "The new path." }
1111
+ },
1112
+ required: ["from", "to"],
1113
+ additionalProperties: false
1114
+ },
1115
+ toRequest: (args, token) => request(
1116
+ "POST",
1117
+ "/api/rename",
1118
+ { from: str(args, "from"), to: str(args, "to") },
1119
+ void 0,
1120
+ token
1121
+ )
1122
+ },
1123
+ {
1124
+ name: "validate",
1125
+ description: "Run the validation gate over the whole site and return its diagnostics, without publishing. Use it to check your work: errors would block a publish, warnings (e.g. length constraints) only inform.",
1126
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
1127
+ toRequest: (_args, token) => request("GET", "/api/validate", {}, void 0, token)
1128
+ },
1129
+ {
1130
+ name: "publish",
1131
+ description: "Compile and publish the whole site. Runs the validation gate first; if anything would break, it reports the problems and writes nothing.",
1132
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
1133
+ toRequest: (_args, token) => request("POST", "/api/publish", {}, void 0, token)
1134
+ },
1135
+ {
1136
+ name: "list_pending_migrations",
1137
+ description: "List content items whose JSON fields don't match their bound template's schema (orphan fields or missing required fields). Use this after a destructive template edit (you'll see a `stamped` record on write_file) to see what needs migrating, or any time to check site-wide drift.",
1138
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
1139
+ toRequest: (_args, token) => request("GET", "/api/migrations", {}, void 0, token)
1140
+ },
1141
+ {
1142
+ name: "preview_migration",
1143
+ description: 'For one content item, return the current template HTML, the best-fit snapshot HTML (the "before"), the list of orphan fields with their values, and the names of fields the current template defines. Use this to plan how to migrate (drop, rename, or keep each orphan).',
1144
+ inputSchema: {
1145
+ type: "object",
1146
+ properties: { path: pathArg },
1147
+ required: ["path"],
1148
+ additionalProperties: false
1149
+ },
1150
+ toRequest: (args, token) => request("GET", "/api/migrations/preview", { path: str(args, "path") }, void 0, token)
1151
+ },
1152
+ {
1153
+ name: "apply_migration",
1154
+ description: "Resolve a content item's schema drift in one go. `resolution` is a JSON object: `drop` (array of orphan field names to delete), `rename` (object mapping orphan field name \u2192 current template field name to copy the value into), `keep` (array of orphan field names to leave untouched), and `fill` (object mapping current template field name \u2192 value, for missing required fields). Returns the refreshed pending list.",
1155
+ inputSchema: {
1156
+ type: "object",
1157
+ properties: {
1158
+ path: pathArg,
1159
+ resolution: {
1160
+ type: "string",
1161
+ description: 'A JSON object encoded as a string: { "drop": string[], "rename": { from: to }, "keep": string[], "fill": { name: value } }.'
1162
+ }
1163
+ },
1164
+ required: ["path", "resolution"],
1165
+ additionalProperties: false
1166
+ },
1167
+ toRequest: (args, token) => request(
1168
+ "POST",
1169
+ "/api/migrations/apply",
1170
+ {},
1171
+ JSON.stringify({
1172
+ path: str(args, "path"),
1173
+ resolution: parseResolutionArg(str(args, "resolution"))
1174
+ }),
1175
+ token
1176
+ )
1177
+ }
1178
+ ];
1179
+ function parseResolutionArg(raw) {
1180
+ if (raw === "") return {};
1181
+ try {
1182
+ return JSON.parse(raw);
1183
+ } catch {
1184
+ return {};
1185
+ }
1186
+ }
1187
+ var MCP_TOOLS = TOOL_SPECS.map(
1188
+ ({ name, description, inputSchema }) => ({ name, description, inputSchema })
1189
+ );
1190
+ function toResult(response) {
1191
+ const text = typeof response.body === "string" ? response.body : response.body === void 0 ? "" : new TextDecoder().decode(response.body);
1192
+ return { text, isError: response.status >= 400 };
1193
+ }
1194
+ async function callMcpTool(deps, name, args = {}, options = {}) {
1195
+ const spec = TOOL_SPECS.find((tool) => tool.name === name);
1196
+ if (spec === void 0) return { text: `Unknown tool: ${name}`, isError: true };
1197
+ return toResult(await handleApi(deps, spec.toRequest(args, options.token)));
1198
+ }
1199
+ var REFERENCE_URI = "nanoesis://reference";
1200
+ var MCP_RESOURCES = [
1201
+ {
1202
+ uri: REFERENCE_URI,
1203
+ name: "nanoesis authoring reference",
1204
+ description: "The generated reference for nanoesis templates and content: tokens, field types, annotations, loops, components, and the document shell. Read this before writing templates or content.",
1205
+ mimeType: "text/markdown"
1206
+ }
1207
+ ];
1208
+ function readMcpResource(uri) {
1209
+ if (uri === REFERENCE_URI) {
1210
+ return { text: renderReferenceMarkdown(buildAuthoringReference()), mimeType: "text/markdown" };
1211
+ }
1212
+ return void 0;
1213
+ }
1214
+
1104
1215
  // ../editor-api/src/branding.ts
1105
- import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
1216
+ import { mkdir, readFile as readFile2, rename, rm, writeFile } from "fs/promises";
1106
1217
  import { dirname, join } from "path";
1107
1218
  var EMPTY = { name: null, logo: null };
1108
1219
  var InMemoryBrandingStore = class {
@@ -1144,7 +1255,7 @@ var FileBrandingStore = class {
1144
1255
  logoPath;
1145
1256
  async loadMeta() {
1146
1257
  try {
1147
- const raw = await readFile(this.metaPath, "utf8");
1258
+ const raw = await readFile2(this.metaPath, "utf8");
1148
1259
  const text = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
1149
1260
  const parsed = JSON.parse(text);
1150
1261
  return {
@@ -1172,7 +1283,7 @@ var FileBrandingStore = class {
1172
1283
  const meta = await this.loadMeta();
1173
1284
  if (meta.logo === null) return null;
1174
1285
  try {
1175
- const bytes = await readFile(this.logoPath);
1286
+ const bytes = await readFile2(this.logoPath);
1176
1287
  return { bytes: new Uint8Array(bytes), contentType: meta.logo.contentType };
1177
1288
  } catch (error) {
1178
1289
  if (error.code === "ENOENT") return null;
@@ -1203,10 +1314,6 @@ export {
1203
1314
  handleApi,
1204
1315
  authorOptions,
1205
1316
  authorDirectory,
1206
- MCP_TOOLS,
1207
- callMcpTool,
1208
- MCP_RESOURCES,
1209
- readMcpResource,
1210
1317
  buildDefaultDiagnostics,
1211
1318
  homeTemplateMissingDiagnostic,
1212
1319
  recreateHomeTemplateRepair,
@@ -1216,6 +1323,13 @@ export {
1216
1323
  templateSuffixConflictDiagnostic,
1217
1324
  copySnapshotToCurrentRepair,
1218
1325
  pendingMigrationsDiagnostic,
1326
+ createEditor,
1327
+ devNoAuth,
1328
+ serveEditorAsset,
1329
+ MCP_TOOLS,
1330
+ callMcpTool,
1331
+ MCP_RESOURCES,
1332
+ readMcpResource,
1219
1333
  InMemoryBrandingStore,
1220
1334
  FileBrandingStore
1221
1335
  };
@@ -1,4 +1,5 @@
1
- import { WorkingStore, IdentityProvider, PublishResult, ReconcileResult, AuthEndpoints, UserAdminEndpoints, AuthorOption, DiagnosticRegistry, UserSummary, AuthorDirectory, Repair, DiagnosticCheck } from '@nanoesis/engine';
1
+ import { WorkingStore, IdentityProvider, PublishResult, ReconcileResult, AuthEndpoints, UserAdminEndpoints, AuthorOption, DiagnosticRegistry, Storage, ImageEncoder, PurgeService, UserSummary, PreBuildHook, AuthorDirectory, Repair, DiagnosticCheck } from '@nanoesis/engine';
2
+ export { Storage } from '@nanoesis/engine';
2
3
 
3
4
  /**
4
5
  * Editor white-labelling storage (DESIGN §11, Phase B). This is the *editor app's*
@@ -158,6 +159,105 @@ interface ApiDeps {
158
159
  */
159
160
  declare function handleApi(deps: ApiDeps, req: ApiRequest): Promise<ApiResponse>;
160
161
 
162
+ /** Everything {@link createEditor} needs. Three things are required; the rest light up features. */
163
+ interface EditorConfig {
164
+ /** Where the editor reads and writes your editable files (content, templates, assets). */
165
+ readonly editorFiles: Storage;
166
+ /** Where the built website is written on publish (your live site, a folder, a CDN origin). */
167
+ readonly website: Storage;
168
+ /** Who may edit. Use {@link devNoAuth} locally; swap for a real provider in production. */
169
+ readonly login: IdentityProvider;
170
+ /**
171
+ * Optional full key scan of `editorFiles`. When supplied, the editor can self-heal its
172
+ * content index (`POST /api/reconcile`) and warn about files that bypassed it. Pass the
173
+ * adapter's native listing (e.g. an Azure container's `list`); omit on a store that
174
+ * cannot enumerate.
175
+ */
176
+ readonly enumerate?: () => Promise<readonly string[]>;
177
+ /** Turns `<img>` into responsive `<picture>` (AVIF/WebP/JPG). Pass the sharp adapter. */
178
+ readonly images?: ImageEncoder;
179
+ /** Clears a CDN cache after a successful publish. */
180
+ readonly purge?: PurgeService;
181
+ /** Absolute site URL; enables `sitemap.xml` and absolute links. */
182
+ readonly baseUrl?: string;
183
+ /** The users a byline may name (DESIGN §6.11): powers the authors picker and bylines. */
184
+ readonly users?: () => Promise<readonly UserSummary[]>;
185
+ /** Editor white-labelling (name + logo). */
186
+ readonly branding?: BrandingStore;
187
+ /** Credential routes (login/refresh/logout); omit for trusted-header / gateway auth. */
188
+ readonly authEndpoints?: AuthEndpoints;
189
+ /** Admin user-management routes; omit when users are managed upstream. */
190
+ readonly userAdmin?: UserAdminEndpoints;
191
+ /**
192
+ * A build step (Tailwind, esbuild, …) run before each publish. Pass a {@link PreBuildHook}
193
+ * for a fixed command, or a function resolved on every publish so a host can pick up a
194
+ * site's build-command change without a restart.
195
+ */
196
+ readonly prebuild?: PreBuildHook | (() => Promise<PreBuildHook | undefined>);
197
+ /** Admin diagnostics/self-heal; defaults to {@link buildDefaultDiagnostics}. */
198
+ readonly diagnostics?: DiagnosticRegistry;
199
+ /** Clear the website before republishing (default true). Needs `website.wipe`. */
200
+ readonly wipeBeforePublish?: boolean;
201
+ }
202
+ /** A wired editor: mount {@link handleApi} at `/api/*` and call {@link publish} to go live. */
203
+ interface Editor {
204
+ /** Handle one editor API request. Mount at `/api/*` in your HTTP server. */
205
+ handleApi(req: ApiRequest): Promise<ApiResponse>;
206
+ /** Validate, then (optionally wipe and) build the site into the website store. */
207
+ publish(): Promise<PublishResult>;
208
+ /** The underlying working store, for advanced hosts that need direct access. */
209
+ readonly store: WorkingStore;
210
+ /**
211
+ * The assembled {@link ApiDeps}, an escape hatch for advanced transports (e.g. the MCP
212
+ * server) that dispatch through the dependencies directly rather than via `handleApi`.
213
+ * Most hosts ignore this.
214
+ */
215
+ readonly deps: ApiDeps;
216
+ }
217
+ /**
218
+ * Wire a complete nanoesis editor from a storage pair, a login, and a few optional
219
+ * capabilities. This is the whole integration surface (DESIGN §11c): everything that used
220
+ * to be hand-assembled per host, the content index over the store, the publish source/sink,
221
+ * index reconcile, the wipe-before-publish, and the {@link ApiDeps} bag, is built here, so
222
+ * internals (`IndexedStore`, `ArtifactSink`, reconcile plumbing) never reach the adopter.
223
+ *
224
+ * `publish()` validates **before** it wipes: an invalid site returns its errors and never
225
+ * touches the live files, so a failed publish can never blank the website (the footgun the
226
+ * old "host wipes, then publishes" wiring carried).
227
+ */
228
+ declare function createEditor(config: EditorConfig): Editor;
229
+ /**
230
+ * An {@link IdentityProvider} that treats every request as a full admin, no login. For
231
+ * **local development only**, it prints a warning on use. Swap for a real provider (e.g.
232
+ * `@nanoesis/adapter-local-jwt`) before exposing the editor to anyone.
233
+ */
234
+ declare function devNoAuth(): IdentityProvider;
235
+
236
+ /** A transport-agnostic response: a host maps this onto its own framework's response. */
237
+ interface AssetResponse {
238
+ readonly status: number;
239
+ readonly headers: Record<string, string>;
240
+ readonly body: Uint8Array | string;
241
+ }
242
+ /** Options for {@link serveEditorAsset}. */
243
+ interface ServeEditorOptions {
244
+ /**
245
+ * Served verbatim as `/config.json` (JSON, never cached). The editor SPA fetches this at
246
+ * boot to learn its API base URL, e.g. `{ apiUrl: 'https://editor.example.com' }`. Omit
247
+ * for a same-origin host where the SPA and API share a domain.
248
+ */
249
+ readonly config?: Record<string, unknown>;
250
+ }
251
+ /**
252
+ * Serve one request for the bundled editor SPA from `distDir` (the `editorDist` shipped in
253
+ * `@xtrable-ltd/nanoesis`). Returns the requested file, or falls back to `index.html` for
254
+ * any unknown path so the editor's client-side router takes over (DESIGN §11c). Mount it on
255
+ * every non-`/api/*` GET. A path that escapes `distDir` is refused (403).
256
+ *
257
+ * This is the SPA-serving glue every host used to hand-write; one helper replaces it.
258
+ */
259
+ declare function serveEditorAsset(distDir: string, pathname: string, options?: ServeEditorOptions): Promise<AssetResponse>;
260
+
161
261
  /** The byline picker's options, display name + stable handle, sorted by display name. */
162
262
  declare function authorOptions(users: readonly UserSummary[]): AuthorOption[];
163
263
  /**
@@ -323,4 +423,4 @@ declare const copySnapshotToCurrentRepair: Repair;
323
423
  */
324
424
  declare const pendingMigrationsDiagnostic: DiagnosticCheck;
325
425
 
326
- export { type ApiDeps, type ApiRequest, type ApiResponse, type BrandingLogo, type BrandingLogoMeta, type BrandingState, type BrandingStore, FileBrandingStore, InMemoryBrandingStore, MCP_RESOURCES, MCP_TOOLS, type McpCallOptions, type McpResourceDef, type McpToolDef, type McpToolResult, SCAFFOLD_FILES, authorDirectory, authorOptions, buildDefaultDiagnostics, callMcpTool, copySnapshotToCurrentRepair, handleApi, homeTemplateMissingDiagnostic, noPublishedContentDiagnostic, pendingMigrationsDiagnostic, readMcpResource, rebindItemToCurrentRepair, recreateHomeTemplateRepair, templateSnapshotIntegrityDiagnostic, templateSuffixConflictDiagnostic };
426
+ export { type ApiDeps, type ApiRequest, type ApiResponse, type AssetResponse, type BrandingLogo, type BrandingLogoMeta, type BrandingState, type BrandingStore, type Editor, type EditorConfig, FileBrandingStore, InMemoryBrandingStore, MCP_RESOURCES, MCP_TOOLS, type McpCallOptions, type McpResourceDef, type McpToolDef, type McpToolResult, SCAFFOLD_FILES, type ServeEditorOptions, authorDirectory, authorOptions, buildDefaultDiagnostics, callMcpTool, copySnapshotToCurrentRepair, createEditor, devNoAuth, handleApi, homeTemplateMissingDiagnostic, noPublishedContentDiagnostic, pendingMigrationsDiagnostic, readMcpResource, rebindItemToCurrentRepair, recreateHomeTemplateRepair, serveEditorAsset, templateSnapshotIntegrityDiagnostic, templateSuffixConflictDiagnostic };
@@ -9,6 +9,8 @@ import {
9
9
  buildDefaultDiagnostics,
10
10
  callMcpTool,
11
11
  copySnapshotToCurrentRepair,
12
+ createEditor,
13
+ devNoAuth,
12
14
  handleApi,
13
15
  homeTemplateMissingDiagnostic,
14
16
  noPublishedContentDiagnostic,
@@ -16,9 +18,10 @@ import {
16
18
  readMcpResource,
17
19
  rebindItemToCurrentRepair,
18
20
  recreateHomeTemplateRepair,
21
+ serveEditorAsset,
19
22
  templateSnapshotIntegrityDiagnostic,
20
23
  templateSuffixConflictDiagnostic
21
- } from "./chunk-26VCV3IE.js";
24
+ } from "./chunk-SHGYQTZ7.js";
22
25
  import "./chunk-WHK3SBV7.js";
23
26
  export {
24
27
  FileBrandingStore,
@@ -31,6 +34,8 @@ export {
31
34
  buildDefaultDiagnostics,
32
35
  callMcpTool,
33
36
  copySnapshotToCurrentRepair,
37
+ createEditor,
38
+ devNoAuth,
34
39
  handleApi,
35
40
  homeTemplateMissingDiagnostic,
36
41
  noPublishedContentDiagnostic,
@@ -38,6 +43,7 @@ export {
38
43
  readMcpResource,
39
44
  rebindItemToCurrentRepair,
40
45
  recreateHomeTemplateRepair,
46
+ serveEditorAsset,
41
47
  templateSnapshotIntegrityDiagnostic,
42
48
  templateSuffixConflictDiagnostic
43
49
  };
package/dist/index.d.ts CHANGED
@@ -154,6 +154,22 @@ interface BlobStore {
154
154
  /** Remove `key`. Deleting an absent key is a no-op (idempotent). */
155
155
  delete(key: string): Promise<void>;
156
156
  }
157
+ /**
158
+ * The single storage contract an adopter implements (DESIGN §11c): a {@link BlobStore}
159
+ * (get/put/delete) plus an optional {@link wipe}. The same shape backs both the editor's
160
+ * working files and the published website, so there is one interface to learn, not a
161
+ * separate read port and write sink. `createEditor` (in `@nanoesis/editor-api`) consumes
162
+ * this; the ready-made adapters (e.g. `folderStorage`) implement it, so most adopters write
163
+ * none of it.
164
+ */
165
+ interface Storage extends BlobStore {
166
+ /**
167
+ * Optional: remove everything in the store. Used to clear the previous publish before
168
+ * re-emitting the website, so deleted pages disappear. A store that cannot enumerate
169
+ * (and so cannot clear itself) omits it; the publish then only overwrites.
170
+ */
171
+ wipe?(): Promise<void>;
172
+ }
157
173
  /**
158
174
  * In-memory {@link BlobStore} backed by a plain map, the test double the engine's
159
175
  * store and index tests run against (the LSP guarantee that real adapters are
@@ -1698,4 +1714,4 @@ declare function createDiagnosticRegistry(): DiagnosticRegistry;
1698
1714
 
1699
1715
  declare const workingStoreRoundTripDiagnostic: Diagnostic;
1700
1716
 
1701
- 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 };
1717
+ 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 Storage, 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/mcp.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  MCP_TOOLS,
4
4
  callMcpTool,
5
5
  readMcpResource
6
- } from "./chunk-26VCV3IE.js";
6
+ } from "./chunk-SHGYQTZ7.js";
7
7
  import "./chunk-WHK3SBV7.js";
8
8
 
9
9
  // ../../hosts/host-mcp/src/http.ts
@@ -56,7 +56,7 @@ function createMcpServer(deps, identity) {
56
56
  }
57
57
 
58
58
  // ../../hosts/host-mcp/src/http.ts
59
- var DEFAULT_VERSION = true ? "0.1.21" : "0.0.0-workspace";
59
+ var DEFAULT_VERSION = true ? "0.1.22" : "0.0.0-workspace";
60
60
  async function handleMcpRequest(deps, request, opts) {
61
61
  const server = createMcpServer(deps, {
62
62
  name: opts?.name ?? "nanoesis",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtrable-ltd/nanoesis",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "nanoesis: a static-first publishing compiler. The engine, the mountable editor API, the storage/image/auth adapters, and the editor UI bundle, in one package (DESIGN 11c).",
5
5
  "license": "MIT",
6
6
  "author": "Xtrable Ltd",