@xtrable-ltd/nanoesis 0.1.31 → 0.1.33
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.d.ts +41 -4
- package/dist/adapter-azure-blob.js +90 -4
- package/dist/{chunk-BCWZRKMF.js → chunk-C3QPGXG5.js} +14 -4
- package/dist/{chunk-GFQT7BYP.js → chunk-P6NDWIKK.js} +201 -83
- package/dist/editor-api.d.ts +11 -5
- package/dist/editor-api.js +2 -2
- package/dist/index.d.ts +102 -2
- package/dist/index.js +9 -1
- package/dist/mcp.js +3 -3
- package/editor/assets/{MigrationsPane-Drxje7Nq.js → MigrationsPane-BuaKhsie.js} +1 -1
- package/editor/assets/{TemplatesPane-CxO1IknP.js → TemplatesPane-Bg_b0htA.js} +7 -7
- package/editor/assets/{cssMode-BPla51Av.js → cssMode-Bcwjp8T3.js} +1 -1
- package/editor/assets/{freemarker2-Bob6Bse-.js → freemarker2-Cgqj3dEx.js} +1 -1
- package/editor/assets/{handlebars-DsQdZgzp.js → handlebars-CLf2-RZf.js} +1 -1
- package/editor/assets/{html-BNBtLdPe.js → html-BrWGWRh6.js} +1 -1
- package/editor/assets/{htmlMode-6kyy4G1O.js → htmlMode-D9skx8gp.js} +1 -1
- package/editor/assets/index-CY5Kehuu.js +142 -0
- package/editor/assets/{javascript-Dx8xuybD.js → javascript-KaABDboM.js} +1 -1
- package/editor/assets/{jsonMode-DWRGUqMk.js → jsonMode-0usLs2ME.js} +1 -1
- package/editor/assets/{liquid-DlIa11aQ.js → liquid-DYna-Clp.js} +1 -1
- package/editor/assets/{mdx-DR0q2TJm.js → mdx-f7LAIls6.js} +1 -1
- package/editor/assets/{python-Cbswpzux.js → python-Bh21_mvq.js} +1 -1
- package/editor/assets/{razor-IYEYh5Ox.js → razor-D_530Ymg.js} +1 -1
- package/editor/assets/{tsMode-DQrbmWSC.js → tsMode-CTRVjt9n.js} +1 -1
- package/editor/assets/{typescript-DiBU2jVJ.js → typescript-D084tkOf.js} +1 -1
- package/editor/assets/{xml-CCxs0QRL.js → xml-BFulX1C6.js} +1 -1
- package/editor/assets/{yaml-C1tH0_iR.js → yaml-CgZsYmUt.js} +1 -1
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/editor/assets/index-BG-8SSzq.js +0 -142
|
@@ -144,24 +144,51 @@ function parseSortFile(raw) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// ../engine/src/store/blob-store.ts
|
|
147
|
+
function isConditionalBlobStore(store) {
|
|
148
|
+
const candidate = store;
|
|
149
|
+
return typeof candidate.getVersioned === "function" && typeof candidate.putConditional === "function";
|
|
150
|
+
}
|
|
147
151
|
var InMemoryBlobStore = class {
|
|
148
152
|
blobs = /* @__PURE__ */ new Map();
|
|
153
|
+
/** Per-key version, bumped on every write, so {@link putConditional} can detect a
|
|
154
|
+
* concurrent overwrite the same way an ETag does on a real store. */
|
|
155
|
+
versions = /* @__PURE__ */ new Map();
|
|
156
|
+
nextVersion = 1;
|
|
149
157
|
constructor(initial) {
|
|
150
158
|
if (initial === void 0) return;
|
|
151
159
|
const encoder = new TextEncoder();
|
|
152
160
|
for (const [key, value] of Object.entries(initial)) {
|
|
153
|
-
this.
|
|
161
|
+
this.set(key, copy(typeof value === "string" ? encoder.encode(value) : value));
|
|
154
162
|
}
|
|
155
163
|
}
|
|
164
|
+
set(key, bytes) {
|
|
165
|
+
this.blobs.set(key, bytes);
|
|
166
|
+
const version = this.nextVersion;
|
|
167
|
+
this.nextVersion += 1;
|
|
168
|
+
this.versions.set(key, version);
|
|
169
|
+
return version;
|
|
170
|
+
}
|
|
156
171
|
async get(key) {
|
|
157
172
|
const value = this.blobs.get(key);
|
|
158
173
|
return value === void 0 ? void 0 : copy(value);
|
|
159
174
|
}
|
|
160
175
|
async put(key, bytes) {
|
|
161
|
-
this.
|
|
176
|
+
this.set(key, copy(bytes));
|
|
162
177
|
}
|
|
163
178
|
async delete(key) {
|
|
164
179
|
this.blobs.delete(key);
|
|
180
|
+
this.versions.delete(key);
|
|
181
|
+
}
|
|
182
|
+
async getVersioned(key) {
|
|
183
|
+
const value = this.blobs.get(key);
|
|
184
|
+
if (value === void 0) return void 0;
|
|
185
|
+
return { bytes: copy(value), version: String(this.versions.get(key)) };
|
|
186
|
+
}
|
|
187
|
+
async putConditional(key, bytes, expected) {
|
|
188
|
+
const current = this.versions.get(key);
|
|
189
|
+
const currentTag = current === void 0 ? null : String(current);
|
|
190
|
+
if (currentTag !== expected) return void 0;
|
|
191
|
+
return String(this.set(key, copy(bytes)));
|
|
165
192
|
}
|
|
166
193
|
};
|
|
167
194
|
function copy(bytes) {
|
|
@@ -218,6 +245,59 @@ function buildRedirects(rules, liveUrls) {
|
|
|
218
245
|
return { path: OUTPUT_PATH, contents };
|
|
219
246
|
}
|
|
220
247
|
|
|
248
|
+
// ../engine/src/content/source.ts
|
|
249
|
+
var NotFoundError = class extends Error {
|
|
250
|
+
constructor(path) {
|
|
251
|
+
super(`No such file in content source: ${path}`);
|
|
252
|
+
this.path = path;
|
|
253
|
+
this.name = "NotFoundError";
|
|
254
|
+
}
|
|
255
|
+
path;
|
|
256
|
+
};
|
|
257
|
+
function normalizePath(path) {
|
|
258
|
+
return path.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
259
|
+
}
|
|
260
|
+
var InMemoryContentSource = class {
|
|
261
|
+
files;
|
|
262
|
+
constructor(files) {
|
|
263
|
+
this.files = new Map(Object.entries(files).map(([key, value]) => [normalizePath(key), value]));
|
|
264
|
+
}
|
|
265
|
+
async readText(path) {
|
|
266
|
+
const value = this.files.get(normalizePath(path));
|
|
267
|
+
if (value === void 0) throw new NotFoundError(path);
|
|
268
|
+
return typeof value === "string" ? value : new TextDecoder().decode(value);
|
|
269
|
+
}
|
|
270
|
+
async readBytes(path) {
|
|
271
|
+
const value = this.files.get(normalizePath(path));
|
|
272
|
+
if (value === void 0) throw new NotFoundError(path);
|
|
273
|
+
return typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
274
|
+
}
|
|
275
|
+
async exists(path) {
|
|
276
|
+
const target = normalizePath(path);
|
|
277
|
+
if (target === "") return true;
|
|
278
|
+
if (this.files.has(target)) return true;
|
|
279
|
+
const prefix = `${target}/`;
|
|
280
|
+
for (const key of this.files.keys()) {
|
|
281
|
+
if (key.startsWith(prefix)) return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
async list(dir) {
|
|
286
|
+
const base = normalizePath(dir);
|
|
287
|
+
const prefix = base === "" ? "" : `${base}/`;
|
|
288
|
+
const entries = /* @__PURE__ */ new Map();
|
|
289
|
+
for (const key of this.files.keys()) {
|
|
290
|
+
if (prefix !== "" && !key.startsWith(prefix)) continue;
|
|
291
|
+
const rest = key.slice(prefix.length);
|
|
292
|
+
if (rest === "") continue;
|
|
293
|
+
const slash = rest.indexOf("/");
|
|
294
|
+
if (slash === -1) entries.set(rest, "file");
|
|
295
|
+
else entries.set(rest.slice(0, slash), "dir");
|
|
296
|
+
}
|
|
297
|
+
return [...entries].map(([name, kind]) => ({ name, kind })).sort((a, b) => a.name.localeCompare(b.name));
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
221
301
|
// ../engine/src/content/loader.ts
|
|
222
302
|
var SORT_FILE = "_sort.json";
|
|
223
303
|
var REDIRECTS_FILE = "_redirects.json";
|
|
@@ -249,10 +329,8 @@ async function loadDir(source, dirPath, slug, treePath) {
|
|
|
249
329
|
if (entry.name === SORT_FILE || entry.name === REDIRECTS_FILE || entry.name === SITE_CONFIG_FILE || !entry.name.endsWith(ITEM_EXT))
|
|
250
330
|
continue;
|
|
251
331
|
const childSlug = entry.name.slice(0, -ITEM_EXT.length);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
await loadItem(source, join(dirPath, entry.name), childSlug, treePath)
|
|
255
|
-
);
|
|
332
|
+
const item = await loadItemIfPresent(source, join(dirPath, entry.name), childSlug, treePath);
|
|
333
|
+
if (item !== void 0) childMap.set(childSlug, item);
|
|
256
334
|
} else {
|
|
257
335
|
if (entry.name === ASSETS_DIR) continue;
|
|
258
336
|
const childPath = join(treePath, entry.name);
|
|
@@ -273,6 +351,14 @@ async function loadDir(source, dirPath, slug, treePath) {
|
|
|
273
351
|
...sort.defaultTemplate !== void 0 && { defaultTemplate: sort.defaultTemplate }
|
|
274
352
|
};
|
|
275
353
|
}
|
|
354
|
+
async function loadItemIfPresent(source, filePath, slug, parentPath2) {
|
|
355
|
+
try {
|
|
356
|
+
return await loadItem(source, filePath, slug, parentPath2);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
if (error instanceof NotFoundError) return void 0;
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
276
362
|
async function loadItem(source, filePath, slug, parentPath2) {
|
|
277
363
|
const raw = await source.readText(filePath);
|
|
278
364
|
try {
|
|
@@ -352,7 +438,11 @@ async function loadComponentFiles(source, componentsDir, extension) {
|
|
|
352
438
|
await walk(full);
|
|
353
439
|
} else if (entry.name.endsWith(suffix)) {
|
|
354
440
|
const key = entry.name.slice(0, -suffix.length).toLowerCase();
|
|
355
|
-
|
|
441
|
+
try {
|
|
442
|
+
map.set(key, await source.readText(full));
|
|
443
|
+
} catch (error) {
|
|
444
|
+
if (!(error instanceof NotFoundError)) throw error;
|
|
445
|
+
}
|
|
356
446
|
}
|
|
357
447
|
}
|
|
358
448
|
};
|
|
@@ -810,6 +900,9 @@ function emptyIndex() {
|
|
|
810
900
|
async function loadIndex(store) {
|
|
811
901
|
const live = parseIndex(await store.get(INDEX_KEY));
|
|
812
902
|
if (live !== void 0) return live;
|
|
903
|
+
return recoverFromBackups(store);
|
|
904
|
+
}
|
|
905
|
+
async function recoverFromBackups(store) {
|
|
813
906
|
let best;
|
|
814
907
|
for (let slot = 0; slot < BACKUP_RING_SIZE; slot += 1) {
|
|
815
908
|
const candidate = parseIndex(await store.get(backupKey(slot)));
|
|
@@ -819,6 +912,18 @@ async function loadIndex(store) {
|
|
|
819
912
|
}
|
|
820
913
|
return best ?? emptyIndex();
|
|
821
914
|
}
|
|
915
|
+
async function loadIndexVersioned(store) {
|
|
916
|
+
const live = await store.getVersioned(INDEX_KEY);
|
|
917
|
+
if (live === void 0) return { index: await recoverFromBackups(store), version: null };
|
|
918
|
+
const parsed = parseIndex(live.bytes);
|
|
919
|
+
return { index: parsed ?? await recoverFromBackups(store), version: live.version };
|
|
920
|
+
}
|
|
921
|
+
async function saveIndexConditional(store, prev, nextKeys) {
|
|
922
|
+
await store.put(backupKey(prev.index.version % BACKUP_RING_SIZE), serialize2(prev.index));
|
|
923
|
+
const next = freezeIndex(prev.index.version + 1, nextKeys);
|
|
924
|
+
const version = await store.putConditional(INDEX_KEY, serialize2(next), prev.version);
|
|
925
|
+
return version === void 0 ? void 0 : { index: next, version };
|
|
926
|
+
}
|
|
822
927
|
async function saveIndex(store, prev, nextKeys) {
|
|
823
928
|
await store.put(backupKey(prev.version % BACKUP_RING_SIZE), serialize2(prev));
|
|
824
929
|
const next = freezeIndex(prev.version + 1, nextKeys);
|
|
@@ -916,6 +1021,47 @@ var IndexedStore = class {
|
|
|
916
1021
|
}
|
|
917
1022
|
})();
|
|
918
1023
|
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Apply a key-set change to the index and persist it (DESIGN §11d). `mutate` receives the
|
|
1026
|
+
* current key set and adds/removes in place; it must be a pure function of that set (it can
|
|
1027
|
+
* be re-run on a fresh read), because on a store with the optimistic-concurrency capability
|
|
1028
|
+
* this runs as a compare-and-set retry loop: read the live index + version, apply `mutate`,
|
|
1029
|
+
* write only if the version is unchanged, and retry on a conflict. This is what stops a
|
|
1030
|
+
* second host instance's blind overwrite from evicting a key this one just added
|
|
1031
|
+
* (NANOESIS-MCP-ISSUES Issue 1). A store without the capability keeps the single-writer
|
|
1032
|
+
* path, correct under the in-process {@link mutationLock} alone. Either way an unchanged key
|
|
1033
|
+
* set writes nothing (overwriting an existing item stays a single blob `put`).
|
|
1034
|
+
*
|
|
1035
|
+
* Callers do their blob writes/deletes first (content-first, §11d) and pass only the
|
|
1036
|
+
* resulting index delta here, so a failed blob op never advances the index.
|
|
1037
|
+
*/
|
|
1038
|
+
async commitIndex(mutate) {
|
|
1039
|
+
const store = this.store;
|
|
1040
|
+
if (isConditionalBlobStore(store)) {
|
|
1041
|
+
for (let attempt = 0; attempt < MAX_INDEX_CAS_ATTEMPTS; attempt += 1) {
|
|
1042
|
+
const current = await loadIndexVersioned(store);
|
|
1043
|
+
const keys2 = new Set(current.index.keys);
|
|
1044
|
+
mutate(keys2);
|
|
1045
|
+
if (sameKeySet(keys2, current.index.keys)) {
|
|
1046
|
+
this.index = current.index;
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const saved = await saveIndexConditional(store, current, [...keys2]);
|
|
1050
|
+
if (saved !== void 0) {
|
|
1051
|
+
this.index = saved.index;
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
throw new Error(
|
|
1056
|
+
"content index: too many concurrent modifications; retry the operation (the index could not be saved after repeated conflicts)"
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
const index = await this.loaded();
|
|
1060
|
+
const keys = new Set(index.keys);
|
|
1061
|
+
mutate(keys);
|
|
1062
|
+
if (sameKeySet(keys, index.keys)) return;
|
|
1063
|
+
this.index = await saveIndex(this.store, index, [...keys]);
|
|
1064
|
+
}
|
|
919
1065
|
async list(dir) {
|
|
920
1066
|
return childrenOf((await this.loaded()).keys, dir);
|
|
921
1067
|
}
|
|
@@ -924,7 +1070,7 @@ var IndexedStore = class {
|
|
|
924
1070
|
}
|
|
925
1071
|
async readBytes(path) {
|
|
926
1072
|
const bytes = await this.store.get(path);
|
|
927
|
-
if (bytes === void 0) throw new
|
|
1073
|
+
if (bytes === void 0) throw new NotFoundError(path);
|
|
928
1074
|
return bytes;
|
|
929
1075
|
}
|
|
930
1076
|
async exists(path) {
|
|
@@ -965,8 +1111,8 @@ var IndexedStore = class {
|
|
|
965
1111
|
schemaDelta = computeSchemaDelta(oldFields, newFields);
|
|
966
1112
|
const destructive = schemaDelta.removed.length > 0 || schemaDelta.typeChanged.some((change) => change.destructive);
|
|
967
1113
|
if (destructive) {
|
|
968
|
-
const
|
|
969
|
-
const siblings =
|
|
1114
|
+
const index = await this.loaded();
|
|
1115
|
+
const siblings = index.keys.filter((k) => k.startsWith(`${stampCandidate.dir}/`) && k.endsWith(".html")).map((k) => k.slice(stampCandidate.dir.length + 1, -".html".length));
|
|
970
1116
|
const version = nextVersionNumber(siblings);
|
|
971
1117
|
stampDecision = {
|
|
972
1118
|
snapshotPath: `${stampCandidate.dir}/${stampCandidate.name}@v${version}.html`,
|
|
@@ -976,22 +1122,13 @@ var IndexedStore = class {
|
|
|
976
1122
|
}
|
|
977
1123
|
}
|
|
978
1124
|
await this.store.put(target, bytes);
|
|
979
|
-
const index = await this.loaded();
|
|
980
|
-
let nextIndex = index;
|
|
981
|
-
if (!nextIndex.keys.includes(target)) {
|
|
982
|
-
nextIndex = await saveIndex(this.store, nextIndex, [...nextIndex.keys, target]);
|
|
983
|
-
}
|
|
984
1125
|
let stamped;
|
|
985
1126
|
let stampIncomplete;
|
|
1127
|
+
let snapshotToIndex;
|
|
986
1128
|
if (stampDecision !== void 0) {
|
|
987
1129
|
try {
|
|
988
1130
|
await this.store.put(stampDecision.snapshotPath, stampDecision.bytes);
|
|
989
|
-
|
|
990
|
-
nextIndex = await saveIndex(this.store, nextIndex, [
|
|
991
|
-
...nextIndex.keys,
|
|
992
|
-
stampDecision.snapshotPath
|
|
993
|
-
]);
|
|
994
|
-
}
|
|
1131
|
+
snapshotToIndex = stampDecision.snapshotPath;
|
|
995
1132
|
stamped = {
|
|
996
1133
|
name: stampCandidate.name,
|
|
997
1134
|
version: stampDecision.version,
|
|
@@ -1001,7 +1138,10 @@ var IndexedStore = class {
|
|
|
1001
1138
|
stampIncomplete = true;
|
|
1002
1139
|
}
|
|
1003
1140
|
}
|
|
1004
|
-
this.
|
|
1141
|
+
await this.commitIndex((keys) => {
|
|
1142
|
+
keys.add(target);
|
|
1143
|
+
if (snapshotToIndex !== void 0) keys.add(snapshotToIndex);
|
|
1144
|
+
});
|
|
1005
1145
|
let parseDiagnostics;
|
|
1006
1146
|
if (isContentItemPath(target)) {
|
|
1007
1147
|
parseDiagnostics = contentParseDiagnosticsFor(bytes);
|
|
@@ -1037,11 +1177,7 @@ var IndexedStore = class {
|
|
|
1037
1177
|
const version = nextVersionNumber(siblings);
|
|
1038
1178
|
const snapshotPath = `${dir}/${name}@v${version}.html`;
|
|
1039
1179
|
await this.store.put(snapshotPath, bytes);
|
|
1040
|
-
|
|
1041
|
-
this.index = await saveIndex(this.store, index, [...index.keys, snapshotPath]);
|
|
1042
|
-
} else {
|
|
1043
|
-
this.index = index;
|
|
1044
|
-
}
|
|
1180
|
+
await this.commitIndex((keys) => keys.add(snapshotPath));
|
|
1045
1181
|
return { name, version, snapshotPath };
|
|
1046
1182
|
});
|
|
1047
1183
|
}
|
|
@@ -1061,8 +1197,11 @@ var IndexedStore = class {
|
|
|
1061
1197
|
return;
|
|
1062
1198
|
}
|
|
1063
1199
|
await Promise.all(removed.map((k) => this.store.delete(k)));
|
|
1064
|
-
|
|
1065
|
-
|
|
1200
|
+
await this.commitIndex((keys) => {
|
|
1201
|
+
for (const k of [...keys]) {
|
|
1202
|
+
if (k === target || k.startsWith(prefix)) keys.delete(k);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1066
1205
|
});
|
|
1067
1206
|
}
|
|
1068
1207
|
/**
|
|
@@ -1099,8 +1238,10 @@ var IndexedStore = class {
|
|
|
1099
1238
|
await this.store.delete(move.from);
|
|
1100
1239
|
}
|
|
1101
1240
|
const movedFrom = new Set(moves.map((move) => move.from));
|
|
1102
|
-
|
|
1103
|
-
|
|
1241
|
+
await this.commitIndex((keys) => {
|
|
1242
|
+
for (const from2 of movedFrom) keys.delete(from2);
|
|
1243
|
+
for (const move of moves) keys.add(move.to);
|
|
1244
|
+
});
|
|
1104
1245
|
return { ok: true };
|
|
1105
1246
|
});
|
|
1106
1247
|
}
|
|
@@ -1143,6 +1284,12 @@ function contentParseDiagnosticsFor(bytes) {
|
|
|
1143
1284
|
return void 0;
|
|
1144
1285
|
}
|
|
1145
1286
|
}
|
|
1287
|
+
var MAX_INDEX_CAS_ATTEMPTS = 50;
|
|
1288
|
+
function sameKeySet(set, sorted) {
|
|
1289
|
+
if (set.size !== sorted.length) return false;
|
|
1290
|
+
for (const key of sorted) if (!set.has(key)) return false;
|
|
1291
|
+
return true;
|
|
1292
|
+
}
|
|
1146
1293
|
function guarded(key) {
|
|
1147
1294
|
if (key === "" || key.startsWith(RESERVED_PREFIX)) {
|
|
1148
1295
|
throw new Error(`Refusing to mutate a reserved key: ${key === "" ? "(root)" : key}`);
|
|
@@ -1174,51 +1321,6 @@ function pathExists(keys, path) {
|
|
|
1174
1321
|
return keys.some((key) => key.startsWith(prefix));
|
|
1175
1322
|
}
|
|
1176
1323
|
|
|
1177
|
-
// ../engine/src/content/source.ts
|
|
1178
|
-
function normalizePath(path) {
|
|
1179
|
-
return path.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1180
|
-
}
|
|
1181
|
-
var InMemoryContentSource = class {
|
|
1182
|
-
files;
|
|
1183
|
-
constructor(files) {
|
|
1184
|
-
this.files = new Map(Object.entries(files).map(([key, value]) => [normalizePath(key), value]));
|
|
1185
|
-
}
|
|
1186
|
-
async readText(path) {
|
|
1187
|
-
const value = this.files.get(normalizePath(path));
|
|
1188
|
-
if (value === void 0) throw new Error(`No such file in content source: ${path}`);
|
|
1189
|
-
return typeof value === "string" ? value : new TextDecoder().decode(value);
|
|
1190
|
-
}
|
|
1191
|
-
async readBytes(path) {
|
|
1192
|
-
const value = this.files.get(normalizePath(path));
|
|
1193
|
-
if (value === void 0) throw new Error(`No such file in content source: ${path}`);
|
|
1194
|
-
return typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
1195
|
-
}
|
|
1196
|
-
async exists(path) {
|
|
1197
|
-
const target = normalizePath(path);
|
|
1198
|
-
if (target === "") return true;
|
|
1199
|
-
if (this.files.has(target)) return true;
|
|
1200
|
-
const prefix = `${target}/`;
|
|
1201
|
-
for (const key of this.files.keys()) {
|
|
1202
|
-
if (key.startsWith(prefix)) return true;
|
|
1203
|
-
}
|
|
1204
|
-
return false;
|
|
1205
|
-
}
|
|
1206
|
-
async list(dir) {
|
|
1207
|
-
const base = normalizePath(dir);
|
|
1208
|
-
const prefix = base === "" ? "" : `${base}/`;
|
|
1209
|
-
const entries = /* @__PURE__ */ new Map();
|
|
1210
|
-
for (const key of this.files.keys()) {
|
|
1211
|
-
if (prefix !== "" && !key.startsWith(prefix)) continue;
|
|
1212
|
-
const rest = key.slice(prefix.length);
|
|
1213
|
-
if (rest === "") continue;
|
|
1214
|
-
const slash = rest.indexOf("/");
|
|
1215
|
-
if (slash === -1) entries.set(rest, "file");
|
|
1216
|
-
else entries.set(rest.slice(0, slash), "dir");
|
|
1217
|
-
}
|
|
1218
|
-
return [...entries].map(([name, kind]) => ({ name, kind })).sort((a, b) => a.name.localeCompare(b.name));
|
|
1219
|
-
}
|
|
1220
|
-
};
|
|
1221
|
-
|
|
1222
1324
|
// ../engine/src/url/references.ts
|
|
1223
1325
|
var REFERENCE_PREFIX = "ref:";
|
|
1224
1326
|
function referenceTarget(value) {
|
|
@@ -2802,6 +2904,9 @@ async function validateSite(source) {
|
|
|
2802
2904
|
}
|
|
2803
2905
|
const shellRequiredFields = shellAnalysis?.requiredFields ?? [];
|
|
2804
2906
|
const shellConstraints = shellAnalysis?.constraints ?? /* @__PURE__ */ new Map();
|
|
2907
|
+
const shellFieldNames = new Set(
|
|
2908
|
+
documentShell !== void 0 ? deriveFields(documentShell, components).map((f) => f.name) : []
|
|
2909
|
+
);
|
|
2805
2910
|
const diagnostics = [];
|
|
2806
2911
|
const add = (severity, code, message, path) => {
|
|
2807
2912
|
diagnostics.push({ severity, code, message, ...path !== void 0 && { path } });
|
|
@@ -2970,7 +3075,9 @@ async function validateSite(source) {
|
|
|
2970
3075
|
if (!isVersionedTemplateName(templateName)) {
|
|
2971
3076
|
const expectedFields = expectedFieldsFor(templateName);
|
|
2972
3077
|
const itemFieldNames = Object.keys(node.item.fields);
|
|
2973
|
-
const orphans = itemFieldNames.filter(
|
|
3078
|
+
const orphans = itemFieldNames.filter(
|
|
3079
|
+
(name) => !expectedFields.has(name) && !shellFieldNames.has(name)
|
|
3080
|
+
);
|
|
2974
3081
|
if (orphans.length > 0) {
|
|
2975
3082
|
add(
|
|
2976
3083
|
"warning",
|
|
@@ -3212,13 +3319,20 @@ async function publishSite(source, sink, options = {}) {
|
|
|
3212
3319
|
);
|
|
3213
3320
|
const byPath = /* @__PURE__ */ new Map();
|
|
3214
3321
|
for (const artifact of [...stamped, ...passthrough]) byPath.set(artifact.path, artifact);
|
|
3322
|
+
const everything = [...byPath.values()];
|
|
3323
|
+
const pages = everything.filter((artifact) => artifact.path.endsWith(".html"));
|
|
3324
|
+
const resources = everything.filter((artifact) => !artifact.path.endsWith(".html"));
|
|
3215
3325
|
const uploadTotal = byPath.size;
|
|
3216
3326
|
let uploaded = 0;
|
|
3217
|
-
|
|
3218
|
-
await
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3327
|
+
const writeGroup = async (group) => {
|
|
3328
|
+
await mapWithConcurrency(group, writeConcurrency, async (artifact) => {
|
|
3329
|
+
await sink.write(artifact.path, artifact.contents, cacheControlFor(artifact));
|
|
3330
|
+
uploaded += 1;
|
|
3331
|
+
onProgress?.({ phase: "upload", written: uploaded, total: uploadTotal });
|
|
3332
|
+
});
|
|
3333
|
+
};
|
|
3334
|
+
await writeGroup(resources);
|
|
3335
|
+
await writeGroup(pages);
|
|
3222
3336
|
if (purge !== noopPurgeService) onProgress?.({ phase: "purge" });
|
|
3223
3337
|
await purge.purgeAll();
|
|
3224
3338
|
onProgress?.({ phase: "done", written: uploadTotal, summary });
|
|
@@ -3472,9 +3586,12 @@ export {
|
|
|
3472
3586
|
ContentParseError,
|
|
3473
3587
|
parseContentItem,
|
|
3474
3588
|
parseSortFile,
|
|
3589
|
+
isConditionalBlobStore,
|
|
3475
3590
|
InMemoryBlobStore,
|
|
3476
3591
|
parseRedirects,
|
|
3477
3592
|
buildRedirects,
|
|
3593
|
+
NotFoundError,
|
|
3594
|
+
InMemoryContentSource,
|
|
3478
3595
|
DEFAULT_DIRS,
|
|
3479
3596
|
loadContentTree,
|
|
3480
3597
|
loadRedirects,
|
|
@@ -3504,10 +3621,11 @@ export {
|
|
|
3504
3621
|
RESERVED_PREFIX,
|
|
3505
3622
|
emptyIndex,
|
|
3506
3623
|
loadIndex,
|
|
3624
|
+
loadIndexVersioned,
|
|
3625
|
+
saveIndexConditional,
|
|
3507
3626
|
saveIndex,
|
|
3508
3627
|
reconcileIndex,
|
|
3509
3628
|
IndexedStore,
|
|
3510
|
-
InMemoryContentSource,
|
|
3511
3629
|
analyzeTemplate,
|
|
3512
3630
|
pendingMigrations,
|
|
3513
3631
|
bestFitSnapshot,
|
package/dist/editor-api.d.ts
CHANGED
|
@@ -222,7 +222,12 @@ interface EditorConfig {
|
|
|
222
222
|
readonly prebuild?: PreBuildHook | (() => Promise<PreBuildHook | undefined>);
|
|
223
223
|
/** Admin diagnostics/self-heal; defaults to {@link buildDefaultDiagnostics}. */
|
|
224
224
|
readonly diagnostics?: DiagnosticRegistry;
|
|
225
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* Whether a publish clears pages the new build no longer emits (default true). A
|
|
227
|
+
* `prune`-capable website does this as a post-publish orphan sweep (overwrite-in-place,
|
|
228
|
+
* zero downtime); a wipe-only website clears up front instead. Set false to never delete
|
|
229
|
+
* (overwrite-only, stale pages linger). Names `wipeBeforePublish` for back-compat.
|
|
230
|
+
*/
|
|
226
231
|
readonly wipeBeforePublish?: boolean;
|
|
227
232
|
}
|
|
228
233
|
/** A wired editor: mount {@link handleApi} at `/api/*` and call {@link publish} to go live. */
|
|
@@ -244,12 +249,13 @@ interface Editor {
|
|
|
244
249
|
* Wire a complete nanoesis editor from a storage pair, a login, and a few optional
|
|
245
250
|
* capabilities. This is the whole integration surface (DESIGN §11c): everything that used
|
|
246
251
|
* to be hand-assembled per host, the content index over the store, the publish source/sink,
|
|
247
|
-
* index reconcile, the
|
|
252
|
+
* index reconcile, the orphan sweep, and the {@link ApiDeps} bag, is built here, so
|
|
248
253
|
* internals (`IndexedStore`, `ArtifactSink`, reconcile plumbing) never reach the adopter.
|
|
249
254
|
*
|
|
250
|
-
* `publish()` validates **
|
|
251
|
-
*
|
|
252
|
-
*
|
|
255
|
+
* `publish()` validates **first**: an invalid site returns its errors and never touches the
|
|
256
|
+
* live files. A `prune`-capable website then overwrites every artifact in place and sweeps
|
|
257
|
+
* the orphans afterwards, so a publish never blanks the live site, not even for the upload
|
|
258
|
+
* window (the footgun the old "host wipes, then publishes" wiring carried).
|
|
253
259
|
*/
|
|
254
260
|
declare function createEditor(config: EditorConfig): Editor;
|
|
255
261
|
/**
|
package/dist/editor-api.js
CHANGED
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
serveEditorAsset,
|
|
22
22
|
templateSnapshotIntegrityDiagnostic,
|
|
23
23
|
templateSuffixConflictDiagnostic
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-C3QPGXG5.js";
|
|
25
|
+
import "./chunk-P6NDWIKK.js";
|
|
26
26
|
export {
|
|
27
27
|
FileBrandingStore,
|
|
28
28
|
InMemoryBrandingStore,
|
package/dist/index.d.ts
CHANGED
|
@@ -158,6 +158,37 @@ interface BlobStore {
|
|
|
158
158
|
/** Remove `key`. Deleting an absent key is a no-op (idempotent). */
|
|
159
159
|
delete(key: string): Promise<void>;
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Optional optimistic-concurrency capability a {@link BlobStore} may also implement
|
|
163
|
+
* (DESIGN §11d). It exists for exactly one blob: the content index, which is mutated by
|
|
164
|
+
* read-modify-write. With a single writer the in-process mutex on `IndexedStore` is enough,
|
|
165
|
+
* but a horizontally-scaled host (e.g. Azure Consumption spinning a second instance under
|
|
166
|
+
* load) runs two writers against one index blob, and a blind overwrite there loses updates,
|
|
167
|
+
* one instance's `saveIndex` evicts a key the other just added, though its content blob is
|
|
168
|
+
* fine (the "files exist but missing from the index" churn, NANOESIS-MCP-ISSUES Issue 1).
|
|
169
|
+
*
|
|
170
|
+
* When a store implements this, `IndexedStore` performs a compare-and-set on the index:
|
|
171
|
+
* read the current version, re-apply its key delta, write only if the version is unchanged,
|
|
172
|
+
* and retry on a conflict. A store without it (a `Map`, a folder) keeps the blind path,
|
|
173
|
+
* single-process hosts neither need nor pay for the capability. It is therefore an *optional*
|
|
174
|
+
* add-on, like {@link Storage.wipe}: the minimal adopter surface stays get/put/delete.
|
|
175
|
+
*/
|
|
176
|
+
interface ConditionalBlobStore {
|
|
177
|
+
/** The bytes at `key` plus an opaque version tag (e.g. an ETag), or `undefined` if absent. */
|
|
178
|
+
getVersioned(key: string): Promise<{
|
|
179
|
+
bytes: Uint8Array;
|
|
180
|
+
version: string;
|
|
181
|
+
} | undefined>;
|
|
182
|
+
/**
|
|
183
|
+
* Write `key` only if its current version matches `expected` (or, when `expected` is
|
|
184
|
+
* `null`, only if the key does not yet exist). Resolves to the new version on success, or
|
|
185
|
+
* `undefined` when the precondition failed because another writer got there first, the
|
|
186
|
+
* signal for the caller to re-read and retry. A non-precondition failure still rejects.
|
|
187
|
+
*/
|
|
188
|
+
putConditional(key: string, bytes: Uint8Array, expected: string | null): Promise<string | undefined>;
|
|
189
|
+
}
|
|
190
|
+
/** Whether a {@link BlobStore} also offers the optimistic-concurrency capability. */
|
|
191
|
+
declare function isConditionalBlobStore(store: BlobStore): store is BlobStore & ConditionalBlobStore;
|
|
161
192
|
/**
|
|
162
193
|
* The single storage contract an adopter implements (DESIGN §11c): a {@link BlobStore}
|
|
163
194
|
* (get/put/delete) plus an optional {@link wipe}. The same shape backs both the editor's
|
|
@@ -173,6 +204,17 @@ interface Storage extends BlobStore {
|
|
|
173
204
|
* (and so cannot clear itself) omits it; the publish then only overwrites.
|
|
174
205
|
*/
|
|
175
206
|
wipe?(): Promise<void>;
|
|
207
|
+
/**
|
|
208
|
+
* Optional: delete every key **not** in `keep`, leaving the kept keys untouched. The
|
|
209
|
+
* zero-downtime alternative to {@link wipe} for a published website: the host overwrites
|
|
210
|
+
* every artifact in place (each key flips old→new atomically, so no URL is ever missing),
|
|
211
|
+
* then calls `prune` with the just-written path set to sweep only the orphans, pages that
|
|
212
|
+
* no longer exist. Unlike `wipe` (clear *before* the upload, blanking the live site for the
|
|
213
|
+
* whole write), `prune` runs *after* a successful upload and only removes what the new
|
|
214
|
+
* publish did not write. A store that cannot enumerate omits it; the host falls back to
|
|
215
|
+
* `wipe`, or to overwrite-only when neither is available.
|
|
216
|
+
*/
|
|
217
|
+
prune?(keep: ReadonlySet<string>): Promise<void>;
|
|
176
218
|
}
|
|
177
219
|
/**
|
|
178
220
|
* In-memory {@link BlobStore} backed by a plain map, the test double the engine's
|
|
@@ -180,12 +222,22 @@ interface Storage extends BlobStore {
|
|
|
180
222
|
* interchangeable). It copies bytes in and out, so a caller can never mutate stored
|
|
181
223
|
* data by holding on to a buffer.
|
|
182
224
|
*/
|
|
183
|
-
declare class InMemoryBlobStore implements BlobStore {
|
|
225
|
+
declare class InMemoryBlobStore implements BlobStore, ConditionalBlobStore {
|
|
184
226
|
private readonly blobs;
|
|
227
|
+
/** Per-key version, bumped on every write, so {@link putConditional} can detect a
|
|
228
|
+
* concurrent overwrite the same way an ETag does on a real store. */
|
|
229
|
+
private readonly versions;
|
|
230
|
+
private nextVersion;
|
|
185
231
|
constructor(initial?: Readonly<Record<string, Uint8Array | string>>);
|
|
232
|
+
private set;
|
|
186
233
|
get(key: string): Promise<Uint8Array | undefined>;
|
|
187
234
|
put(key: string, bytes: Uint8Array): Promise<void>;
|
|
188
235
|
delete(key: string): Promise<void>;
|
|
236
|
+
getVersioned(key: string): Promise<{
|
|
237
|
+
bytes: Uint8Array;
|
|
238
|
+
version: string;
|
|
239
|
+
} | undefined>;
|
|
240
|
+
putConditional(key: string, bytes: Uint8Array, expected: string | null): Promise<string | undefined>;
|
|
189
241
|
}
|
|
190
242
|
|
|
191
243
|
/**
|
|
@@ -195,6 +247,17 @@ declare class InMemoryBlobStore implements BlobStore {
|
|
|
195
247
|
* relative to the site root (e.g. "content/blog/post.json").
|
|
196
248
|
*/
|
|
197
249
|
type EntryKind = 'file' | 'dir';
|
|
250
|
+
/**
|
|
251
|
+
* Thrown by a {@link ContentSource} read when the path does not exist, as opposed to a
|
|
252
|
+
* real I/O failure (a network error, a permission error). Loaders catch *this* to tell
|
|
253
|
+
* index drift (a key the index lists but whose blob is gone) apart from an error they must
|
|
254
|
+
* not swallow: a listed-but-missing item is skipped, a genuine read failure still
|
|
255
|
+
* propagates. Carries the requested path for diagnostics.
|
|
256
|
+
*/
|
|
257
|
+
declare class NotFoundError extends Error {
|
|
258
|
+
readonly path: string;
|
|
259
|
+
constructor(path: string);
|
|
260
|
+
}
|
|
198
261
|
interface DirEntry {
|
|
199
262
|
readonly name: string;
|
|
200
263
|
readonly kind: EntryKind;
|
|
@@ -874,6 +937,28 @@ declare function emptyIndex(): ContentIndex;
|
|
|
874
937
|
* but cannot be enumerated until something rewrites the index).
|
|
875
938
|
*/
|
|
876
939
|
declare function loadIndex(store: BlobStore): Promise<ContentIndex>;
|
|
940
|
+
/** The index paired with the live index blob's version tag, for a compare-and-set save
|
|
941
|
+
* (DESIGN §11d). `version` is `null` when no live index blob exists yet, so the first save
|
|
942
|
+
* creates it (`putConditional` with `expected: null`). */
|
|
943
|
+
interface VersionedIndex {
|
|
944
|
+
readonly index: ContentIndex;
|
|
945
|
+
readonly version: string | null;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Load the index together with the version tag needed to write it back safely under
|
|
949
|
+
* optimistic concurrency. A present-but-corrupt live blob recovers from the backup ring but
|
|
950
|
+
* keeps the live version, so the next {@link saveIndexConditional} overwrites the corrupt
|
|
951
|
+
* copy rather than failing forever; an absent live blob recovers read-only with `version:
|
|
952
|
+
* null`.
|
|
953
|
+
*/
|
|
954
|
+
declare function loadIndexVersioned(store: BlobStore & ConditionalBlobStore): Promise<VersionedIndex>;
|
|
955
|
+
/**
|
|
956
|
+
* Compare-and-set save (DESIGN §11d): back up the index being replaced (best-effort,
|
|
957
|
+
* unconditional), then write the new index *only if* the live version still matches the one
|
|
958
|
+
* read in `prev`. Returns the new {@link VersionedIndex} on success, or `undefined` when a
|
|
959
|
+
* concurrent writer won the race, the caller re-reads and re-applies its delta, then retries.
|
|
960
|
+
*/
|
|
961
|
+
declare function saveIndexConditional(store: BlobStore & ConditionalBlobStore, prev: VersionedIndex, nextKeys: readonly string[]): Promise<VersionedIndex | undefined>;
|
|
877
962
|
/**
|
|
878
963
|
* Write a new index whose keys are `nextKeys`, backing up the one it replaces first
|
|
879
964
|
* (DESIGN §11d). The previous index goes into its ring slot (`version % ring`, oldest
|
|
@@ -943,6 +1028,21 @@ declare class IndexedStore implements WorkingStore {
|
|
|
943
1028
|
/** The index, loaded once on first need and cached (mutations replace the cached copy). */
|
|
944
1029
|
private loaded;
|
|
945
1030
|
private serializeMutation;
|
|
1031
|
+
/**
|
|
1032
|
+
* Apply a key-set change to the index and persist it (DESIGN §11d). `mutate` receives the
|
|
1033
|
+
* current key set and adds/removes in place; it must be a pure function of that set (it can
|
|
1034
|
+
* be re-run on a fresh read), because on a store with the optimistic-concurrency capability
|
|
1035
|
+
* this runs as a compare-and-set retry loop: read the live index + version, apply `mutate`,
|
|
1036
|
+
* write only if the version is unchanged, and retry on a conflict. This is what stops a
|
|
1037
|
+
* second host instance's blind overwrite from evicting a key this one just added
|
|
1038
|
+
* (NANOESIS-MCP-ISSUES Issue 1). A store without the capability keeps the single-writer
|
|
1039
|
+
* path, correct under the in-process {@link mutationLock} alone. Either way an unchanged key
|
|
1040
|
+
* set writes nothing (overwriting an existing item stays a single blob `put`).
|
|
1041
|
+
*
|
|
1042
|
+
* Callers do their blob writes/deletes first (content-first, §11d) and pass only the
|
|
1043
|
+
* resulting index delta here, so a failed blob op never advances the index.
|
|
1044
|
+
*/
|
|
1045
|
+
private commitIndex;
|
|
946
1046
|
list(dir: string): Promise<readonly DirEntry[]>;
|
|
947
1047
|
readText(path: string): Promise<string>;
|
|
948
1048
|
readBytes(path: string): Promise<Uint8Array>;
|
|
@@ -1870,4 +1970,4 @@ declare function createDiagnosticRegistry(): DiagnosticRegistry;
|
|
|
1870
1970
|
|
|
1871
1971
|
declare const workingStoreRoundTripDiagnostic: Diagnostic;
|
|
1872
1972
|
|
|
1873
|
-
export { type Artifact, type ArtifactSink, type AuthEndpoints, type AuthResult, type AuthorDirectory, type AuthorEntry, type AuthorOption, type AuthorRef, type AuthoringReference, type BlobStore, type BoundItem, CACHE_IMMUTABLE, CACHE_REVALIDATE, 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 ProgressReporter, type ProgressSummary, type PublishOptions, type PublishPlan, type PublishProgress, type PublishResource, 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, cacheControlFor, 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, planPublish, processImage, publishSite, reconcileIndex, renderAuthors, renderReferenceMarkdown, sanitizeUrl, saveIndex, slugify, snapshotName, textContent, toAuthorRefs, urlForItem, validateSite, valueKindOf, versionNumber, wholeValueToken, workingStoreRoundTripDiagnostic };
|
|
1973
|
+
export { type Artifact, type ArtifactSink, type AuthEndpoints, type AuthResult, type AuthorDirectory, type AuthorEntry, type AuthorOption, type AuthorRef, type AuthoringReference, type BlobStore, type BoundItem, CACHE_IMMUTABLE, CACHE_REVALIDATE, type ChangePasswordRequest, type ChangePasswordSuccess, type CollectionConfig, type CollectionQuery, type CompileInput, type CompilePageOptions, type CompileSiteOptions, type CompiledPage, type ComponentMap, type ConditionalBlobStore, 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, NotFoundError, type PageEntry, type PendingMigrationItem, type PendingMigrations, type PreBuildHook, type Principal, type ProgressReporter, type ProgressSummary, type PublishOptions, type PublishPlan, type PublishProgress, type PublishResource, 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 VersionedIndex, type WorkingStore, analyzeTemplate, applyMigration, baseTemplateName, bestFitSnapshot, buildAuthoringReference, buildContentIndex, buildPictureMarkup, buildRedirects, buildResolveContext, buildRss, buildSitemap, cacheControlFor, canEdit, compilePage, compileSite, compileTemplate, computeSchemaDelta, contentHash, contentTypeFor, createDiagnosticRegistry, deriveFields, detectStamp, emptyIndex, escapeHtmlAttribute, escapeHtmlText, escapeJsonStringContent, findTokens, hasRole, humanize, inferControl, isConditionalBlobStore, isDestructiveTypeChange, isFieldType, isReservedVersionedPath, isVersionedTemplateName, joinAuthors, loadComponentScripts, loadComponentStyles, loadComponents, loadContentTree, loadDocumentShell, loadIndex, loadIndexVersioned, loadRedirects, loadSiteConfig, loadTemplate, nextVersionNumber, noopPurgeService, outputPathForItem, parseContentItem, parseRedirects, parseSortFile, pendingMigrations, planPublish, processImage, publishSite, reconcileIndex, renderAuthors, renderReferenceMarkdown, sanitizeUrl, saveIndex, saveIndexConditional, slugify, snapshotName, textContent, toAuthorRefs, urlForItem, validateSite, valueKindOf, versionNumber, wholeValueToken, workingStoreRoundTripDiagnostic };
|