@xtrable-ltd/nanoesis 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-azure-blob.js +1 -1
- package/dist/{chunk-S23S4ZAK.js → chunk-Y7PUOM5I.js} +86 -49
- package/dist/editor-api.js +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1 -1
- package/editor/assets/{TemplatesPane-CEKbBX1Y.js → TemplatesPane-CtH_G1zX.js} +176 -176
- package/editor/assets/{cssMode-q1Nw_pOI.js → cssMode-Bp_d9uU6.js} +1 -1
- package/editor/assets/{freemarker2-D4uWo9kP.js → freemarker2-Bjkifjqk.js} +1 -1
- package/editor/assets/{handlebars-DIXKS_PM.js → handlebars-BRat5rme.js} +1 -1
- package/editor/assets/{html-WMfbPtFq.js → html-BABGfo5F.js} +1 -1
- package/editor/assets/{htmlMode-D1MWdaop.js → htmlMode-iA96VUY3.js} +1 -1
- package/editor/assets/{index-erqFXslE.js → index-B11qMUOV.js} +60 -60
- package/editor/assets/{javascript-C0daj-se.js → javascript-Py4Moqda.js} +1 -1
- package/editor/assets/{jsonMode-F4j8hmn2.js → jsonMode-ChrNU-Y4.js} +1 -1
- package/editor/assets/{liquid-BmXUpyt6.js → liquid-CB6iNJlZ.js} +1 -1
- package/editor/assets/{mdx-wzY123NO.js → mdx-DOQaWGy9.js} +1 -1
- package/editor/assets/{python-WOKQ0Svl.js → python-QNihkXeJ.js} +1 -1
- package/editor/assets/{razor-B9Yg1Ydt.js → razor-bRuM_KbJ.js} +1 -1
- package/editor/assets/{tsMode-966q_lbW.js → tsMode-CuAeEMPg.js} +1 -1
- package/editor/assets/{typescript-dxbYO44F.js → typescript-Dw4AxvYT.js} +1 -1
- package/editor/assets/{xml-BHa7-pyl.js → xml-mN7rLnTp.js} +1 -1
- package/editor/assets/{yaml-CcgAVYu7.js → yaml-tN2lKdvN.js} +1 -1
- package/editor/index.html +1 -1
- package/package.json +1 -1
|
@@ -237,6 +237,20 @@ var IndexedStore = class {
|
|
|
237
237
|
store;
|
|
238
238
|
index;
|
|
239
239
|
loading;
|
|
240
|
+
/**
|
|
241
|
+
* Per-instance mutex (DESIGN §11d): every mutation (`write`/`delete`/`rename`/`reconcile`)
|
|
242
|
+
* runs through {@link serializeMutation}, which chains onto the previous mutation's
|
|
243
|
+
* completion. Without it, two concurrent mutations both read the cached `this.index`,
|
|
244
|
+
* each compute their "next" key set from that stale snapshot, and the second
|
|
245
|
+
* `saveIndex` clobbers the first — the file writes land in blob, but the index keeps
|
|
246
|
+
* stale references or loses fresh ones (the "I deleted it and it's still there" or
|
|
247
|
+
* "I added it and it didn't appear" bugs surfaced dogfooding the marketing site,
|
|
248
|
+
* 2026-05-28). Reads (`list`/`exists`/`readBytes`) are *not* serialised; an
|
|
249
|
+
* in-flight mutation simply means a reader sees the pre-mutation index, eventually
|
|
250
|
+
* consistent and safe. Crashes inside `work()` release the lock so a single failure
|
|
251
|
+
* does not deadlock subsequent operations.
|
|
252
|
+
*/
|
|
253
|
+
mutationLock = Promise.resolve();
|
|
240
254
|
/** The index, loaded once on first need and cached (mutations replace the cached copy). */
|
|
241
255
|
async loaded() {
|
|
242
256
|
if (this.index !== void 0) return this.index;
|
|
@@ -244,6 +258,21 @@ var IndexedStore = class {
|
|
|
244
258
|
this.index = await this.loading;
|
|
245
259
|
return this.index;
|
|
246
260
|
}
|
|
261
|
+
serializeMutation(work) {
|
|
262
|
+
const previous = this.mutationLock;
|
|
263
|
+
let release;
|
|
264
|
+
this.mutationLock = new Promise((resolve) => {
|
|
265
|
+
release = resolve;
|
|
266
|
+
});
|
|
267
|
+
return (async () => {
|
|
268
|
+
try {
|
|
269
|
+
await previous;
|
|
270
|
+
return await work();
|
|
271
|
+
} finally {
|
|
272
|
+
release();
|
|
273
|
+
}
|
|
274
|
+
})();
|
|
275
|
+
}
|
|
247
276
|
async list(dir) {
|
|
248
277
|
return childrenOf((await this.loaded()).keys, dir);
|
|
249
278
|
}
|
|
@@ -262,31 +291,35 @@ var IndexedStore = class {
|
|
|
262
291
|
* Create or overwrite `key`. The index is rewritten only when `key` is new (an
|
|
263
292
|
* overwrite leaves the key set unchanged, so editing an item is a single `put`).
|
|
264
293
|
*/
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
294
|
+
write(key, bytes) {
|
|
295
|
+
return this.serializeMutation(async () => {
|
|
296
|
+
const target = guarded(normalize(key));
|
|
297
|
+
await this.store.put(target, bytes);
|
|
298
|
+
const index = await this.loaded();
|
|
299
|
+
if (!index.keys.includes(target)) {
|
|
300
|
+
this.index = await saveIndex(this.store, index, [...index.keys, target]);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
272
303
|
}
|
|
273
304
|
/**
|
|
274
305
|
* Delete a file, or a whole directory subtree (every key under `key/`). Idempotent: a
|
|
275
306
|
* path the index does not know is still deleted from the store (clearing an orphan),
|
|
276
307
|
* and deleting nothing is a no-op.
|
|
277
308
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
309
|
+
delete(key) {
|
|
310
|
+
return this.serializeMutation(async () => {
|
|
311
|
+
const target = guarded(normalize(key));
|
|
312
|
+
const index = await this.loaded();
|
|
313
|
+
const prefix = `${target}/`;
|
|
314
|
+
const removed = index.keys.filter((k) => k === target || k.startsWith(prefix));
|
|
315
|
+
if (removed.length === 0) {
|
|
316
|
+
await this.store.delete(target);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
await Promise.all(removed.map((k) => this.store.delete(k)));
|
|
320
|
+
const remaining = index.keys.filter((k) => k !== target && !k.startsWith(prefix));
|
|
321
|
+
this.index = await saveIndex(this.store, index, remaining);
|
|
322
|
+
});
|
|
290
323
|
}
|
|
291
324
|
/**
|
|
292
325
|
* Move/rename a file, or a whole directory subtree (every key under `from/` is remapped
|
|
@@ -295,32 +328,34 @@ var IndexedStore = class {
|
|
|
295
328
|
* `/api/rename` enforces. (Mutating the reserved namespace is a programmer error and
|
|
296
329
|
* still throws.)
|
|
297
330
|
*/
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
331
|
+
rename(from, to) {
|
|
332
|
+
return this.serializeMutation(async () => {
|
|
333
|
+
const source = guarded(normalize(from));
|
|
334
|
+
const dest = guarded(normalize(to));
|
|
335
|
+
if (source === dest) return { ok: true };
|
|
336
|
+
const index = await this.loaded();
|
|
337
|
+
const sourcePrefix = `${source}/`;
|
|
338
|
+
const affected = index.keys.filter((k) => k === source || k.startsWith(sourcePrefix));
|
|
339
|
+
if (affected.length === 0) return { ok: false, reason: "missing" };
|
|
340
|
+
const destPrefix = `${dest}/`;
|
|
341
|
+
if (index.keys.some((k) => k === dest || k.startsWith(destPrefix))) {
|
|
342
|
+
return { ok: false, reason: "exists" };
|
|
343
|
+
}
|
|
344
|
+
const moves = affected.map((k) => ({
|
|
345
|
+
from: k,
|
|
346
|
+
to: k === source ? dest : dest + k.slice(source.length)
|
|
347
|
+
}));
|
|
348
|
+
for (const move of moves) {
|
|
349
|
+
const bytes = await this.store.get(move.from);
|
|
350
|
+
if (bytes === void 0) continue;
|
|
351
|
+
await this.store.put(move.to, bytes);
|
|
352
|
+
await this.store.delete(move.from);
|
|
353
|
+
}
|
|
354
|
+
const movedFrom = new Set(moves.map((move) => move.from));
|
|
355
|
+
const next = index.keys.filter((k) => !movedFrom.has(k)).concat(moves.map((m) => m.to));
|
|
356
|
+
this.index = await saveIndex(this.store, index, next);
|
|
357
|
+
return { ok: true };
|
|
358
|
+
});
|
|
324
359
|
}
|
|
325
360
|
/**
|
|
326
361
|
* Rebuild this store's index from the *actual* keys the underlying store holds (DESIGN
|
|
@@ -330,10 +365,12 @@ var IndexedStore = class {
|
|
|
330
365
|
* {@link reconcileIndex} on a fresh store, this also refreshes the cached in-memory
|
|
331
366
|
* index, so this live instance sees the recovered files immediately.
|
|
332
367
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
368
|
+
reconcile(actualKeys) {
|
|
369
|
+
return this.serializeMutation(async () => {
|
|
370
|
+
const result = await reconcileIndex(this.store, actualKeys);
|
|
371
|
+
this.index = result.index;
|
|
372
|
+
return result;
|
|
373
|
+
});
|
|
337
374
|
}
|
|
338
375
|
};
|
|
339
376
|
function guarded(key) {
|
package/dist/editor-api.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -281,9 +281,24 @@ declare class IndexedStore implements WorkingStore {
|
|
|
281
281
|
private readonly store;
|
|
282
282
|
private index;
|
|
283
283
|
private loading;
|
|
284
|
+
/**
|
|
285
|
+
* Per-instance mutex (DESIGN §11d): every mutation (`write`/`delete`/`rename`/`reconcile`)
|
|
286
|
+
* runs through {@link serializeMutation}, which chains onto the previous mutation's
|
|
287
|
+
* completion. Without it, two concurrent mutations both read the cached `this.index`,
|
|
288
|
+
* each compute their "next" key set from that stale snapshot, and the second
|
|
289
|
+
* `saveIndex` clobbers the first — the file writes land in blob, but the index keeps
|
|
290
|
+
* stale references or loses fresh ones (the "I deleted it and it's still there" or
|
|
291
|
+
* "I added it and it didn't appear" bugs surfaced dogfooding the marketing site,
|
|
292
|
+
* 2026-05-28). Reads (`list`/`exists`/`readBytes`) are *not* serialised; an
|
|
293
|
+
* in-flight mutation simply means a reader sees the pre-mutation index, eventually
|
|
294
|
+
* consistent and safe. Crashes inside `work()` release the lock so a single failure
|
|
295
|
+
* does not deadlock subsequent operations.
|
|
296
|
+
*/
|
|
297
|
+
private mutationLock;
|
|
284
298
|
constructor(store: BlobStore);
|
|
285
299
|
/** The index, loaded once on first need and cached (mutations replace the cached copy). */
|
|
286
300
|
private loaded;
|
|
301
|
+
private serializeMutation;
|
|
287
302
|
list(dir: string): Promise<readonly DirEntry[]>;
|
|
288
303
|
readText(path: string): Promise<string>;
|
|
289
304
|
readBytes(path: string): Promise<Uint8Array>;
|