vfs-kit 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +145 -2
  3. package/dist/VfsAdapter-BWjniD9Y.d.mts +57 -0
  4. package/dist/VfsAdapter-DOBt_TyL.d.ts +57 -0
  5. package/dist/VfsEngine-B6nhgyjQ.d.mts +152 -0
  6. package/dist/VfsEngine-DLx0iUpi.d.ts +152 -0
  7. package/dist/VfsNode-D10gxL5W.d.mts +48 -0
  8. package/dist/VfsNode-D10gxL5W.d.ts +48 -0
  9. package/dist/adapters/index.d.mts +201 -0
  10. package/dist/adapters/index.d.ts +201 -0
  11. package/dist/adapters/index.js +1159 -0
  12. package/dist/adapters/index.js.map +1 -0
  13. package/dist/adapters/index.mjs +1159 -0
  14. package/dist/adapters/index.mjs.map +1 -0
  15. package/dist/chunk-2FEJBM4N.js +60 -0
  16. package/dist/chunk-2FEJBM4N.js.map +1 -0
  17. package/dist/chunk-7OQI6PNM.mjs +60 -0
  18. package/dist/chunk-7OQI6PNM.mjs.map +1 -0
  19. package/dist/chunk-ALWOZGZI.mjs +23 -0
  20. package/dist/chunk-ALWOZGZI.mjs.map +1 -0
  21. package/dist/chunk-POSVS4C7.mjs +531 -0
  22. package/dist/chunk-POSVS4C7.mjs.map +1 -0
  23. package/dist/chunk-R3ROYAMW.js +23 -0
  24. package/dist/chunk-R3ROYAMW.js.map +1 -0
  25. package/dist/chunk-SWRBVSS6.mjs +16 -0
  26. package/dist/chunk-SWRBVSS6.mjs.map +1 -0
  27. package/dist/chunk-U2CKTXY7.js +16 -0
  28. package/dist/chunk-U2CKTXY7.js.map +1 -0
  29. package/dist/chunk-WZVVI3HX.js +531 -0
  30. package/dist/chunk-WZVVI3HX.js.map +1 -0
  31. package/dist/components/index.d.mts +193 -0
  32. package/dist/components/index.d.ts +193 -0
  33. package/dist/components/index.js +1197 -0
  34. package/dist/components/index.js.map +1 -0
  35. package/dist/components/index.mjs +1197 -0
  36. package/dist/components/index.mjs.map +1 -0
  37. package/dist/hooks/index.d.mts +120 -0
  38. package/dist/hooks/index.d.ts +120 -0
  39. package/dist/hooks/index.js +51 -0
  40. package/dist/hooks/index.js.map +1 -0
  41. package/dist/hooks/index.mjs +51 -0
  42. package/dist/hooks/index.mjs.map +1 -0
  43. package/dist/index.d.mts +42 -0
  44. package/dist/index.d.ts +42 -0
  45. package/dist/index.js +530 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/index.mjs +530 -0
  48. package/dist/index.mjs.map +1 -0
  49. package/dist/useVfsTabs-ZHDaLrM1.d.mts +39 -0
  50. package/dist/useVfsTabs-ZHDaLrM1.d.ts +39 -0
  51. package/package.json +63 -30
  52. package/index.js +0 -7
@@ -0,0 +1,1159 @@
1
+ "use client";
2
+ import {
3
+ VfsAdapter
4
+ } from "../chunk-ALWOZGZI.mjs";
5
+ import {
6
+ __async,
7
+ __spreadProps,
8
+ __spreadValues
9
+ } from "../chunk-7OQI6PNM.mjs";
10
+
11
+ // src/adapters/InMemoryAdapter.ts
12
+ import { nanoid } from "nanoid";
13
+ var InMemoryAdapter = class extends VfsAdapter {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.supportsHistory = true;
17
+ this.store = /* @__PURE__ */ new Map();
18
+ this.orders = /* @__PURE__ */ new Map();
19
+ this.snapshots = /* @__PURE__ */ new Map();
20
+ this.listeners = /* @__PURE__ */ new Set();
21
+ }
22
+ generateId() {
23
+ return nanoid();
24
+ }
25
+ mutate(op, nodeIds, fn) {
26
+ const result = fn();
27
+ this.notifyChanged({ op, nodeIds, timestamp: Date.now() });
28
+ return result;
29
+ }
30
+ notifyChanged(change) {
31
+ for (const listener of this.listeners) listener(change);
32
+ }
33
+ makeFileNode(params, id) {
34
+ var _a, _b;
35
+ const now = Date.now();
36
+ return {
37
+ id,
38
+ name: params.name,
39
+ kind: "file",
40
+ parentId: params.parentId,
41
+ mimeType: (_a = params.mimeType) != null ? _a : "application/octet-stream",
42
+ size: 0,
43
+ sortIndex: null,
44
+ lockedBy: null,
45
+ createdAt: now,
46
+ updatedAt: now,
47
+ deletedAt: null,
48
+ meta: (_b = params.meta) != null ? _b : {}
49
+ };
50
+ }
51
+ makeFolderNode(params, id) {
52
+ var _a;
53
+ const now = Date.now();
54
+ return {
55
+ id,
56
+ name: params.name,
57
+ kind: "folder",
58
+ parentId: params.parentId,
59
+ sortIndex: null,
60
+ lockedBy: null,
61
+ createdAt: now,
62
+ updatedAt: now,
63
+ deletedAt: null,
64
+ meta: (_a = params.meta) != null ? _a : {}
65
+ };
66
+ }
67
+ getNodes(ids) {
68
+ return __async(this, null, function* () {
69
+ if (ids.length === 0) {
70
+ return [...this.store.values()].map((e) => e.node).filter((n) => n.deletedAt === null);
71
+ }
72
+ return ids.map((id) => {
73
+ var _a;
74
+ return (_a = this.store.get(id)) == null ? void 0 : _a.node;
75
+ }).filter((n) => !!n);
76
+ });
77
+ }
78
+ getNodeById(id) {
79
+ return __async(this, null, function* () {
80
+ var _a, _b;
81
+ return (_b = (_a = this.store.get(id)) == null ? void 0 : _a.node) != null ? _b : null;
82
+ });
83
+ }
84
+ getNodeByPath(path) {
85
+ return __async(this, null, function* () {
86
+ var _a, _b;
87
+ const parts = path.replace(/^\//, "").split("/").filter(Boolean);
88
+ if (parts.length === 0) return null;
89
+ let parentId = null;
90
+ for (const part of parts) {
91
+ const match = [...this.store.values()].find(
92
+ (e) => e.node.name === part && e.node.parentId === parentId && e.node.deletedAt === null
93
+ );
94
+ if (!match) return null;
95
+ parentId = match.node.id;
96
+ }
97
+ return parentId ? (_b = (_a = this.store.get(parentId)) == null ? void 0 : _a.node) != null ? _b : null : null;
98
+ });
99
+ }
100
+ getChildren(parentId, options) {
101
+ return __async(this, null, function* () {
102
+ return [...this.store.values()].map((e) => e.node).filter(
103
+ (n) => n.parentId === parentId && ((options == null ? void 0 : options.includeTrashed) ? true : n.deletedAt === null)
104
+ );
105
+ });
106
+ }
107
+ getTrashed() {
108
+ return __async(this, null, function* () {
109
+ return [...this.store.values()].map((e) => e.node).filter((n) => n.deletedAt !== null);
110
+ });
111
+ }
112
+ search(query, options) {
113
+ return __async(this, null, function* () {
114
+ const lower = query.toLowerCase();
115
+ return [...this.store.values()].map((e) => e.node).filter((n) => {
116
+ if (!(options == null ? void 0 : options.includeTrashed) && n.deletedAt !== null) return false;
117
+ if ((options == null ? void 0 : options.kind) && n.kind !== options.kind) return false;
118
+ if ((options == null ? void 0 : options.scope) && !this.isDescendantOf(n.id, options.scope)) return false;
119
+ return n.name.toLowerCase().includes(lower);
120
+ });
121
+ });
122
+ }
123
+ readFile(id) {
124
+ return __async(this, null, function* () {
125
+ const entry = this.store.get(id);
126
+ if (!entry || entry.node.kind !== "file") {
127
+ throw new Error(`File not found: ${id}`);
128
+ }
129
+ return entry.content;
130
+ });
131
+ }
132
+ writeFile(id, content) {
133
+ return __async(this, null, function* () {
134
+ const entry = this.store.get(id);
135
+ if (!entry || entry.node.kind !== "file") {
136
+ throw new Error(`File not found: ${id}`);
137
+ }
138
+ this.mutate("write", [id], () => {
139
+ entry.content = content;
140
+ entry.node.size = content.byteLength;
141
+ entry.node.updatedAt = Date.now();
142
+ });
143
+ });
144
+ }
145
+ createFile(params) {
146
+ return __async(this, null, function* () {
147
+ const id = this.generateId();
148
+ const node = this.makeFileNode(params, id);
149
+ return this.mutate("create", [id], () => {
150
+ this.store.set(id, { node, content: new Uint8Array() });
151
+ return node;
152
+ });
153
+ });
154
+ }
155
+ createFolder(params) {
156
+ return __async(this, null, function* () {
157
+ const id = this.generateId();
158
+ const node = this.makeFolderNode(params, id);
159
+ return this.mutate("create", [id], () => {
160
+ this.store.set(id, { node });
161
+ return node;
162
+ });
163
+ });
164
+ }
165
+ updateNode(id, updates) {
166
+ return __async(this, null, function* () {
167
+ const entry = this.store.get(id);
168
+ if (!entry) throw new Error(`Node not found: ${id}`);
169
+ return this.mutate("rename", [id], () => {
170
+ Object.assign(entry.node, __spreadProps(__spreadValues({}, updates), { updatedAt: Date.now() }));
171
+ return entry.node;
172
+ });
173
+ });
174
+ }
175
+ deleteNode(id, permanent = false) {
176
+ return __async(this, null, function* () {
177
+ const entry = this.store.get(id);
178
+ if (!entry) throw new Error(`Node not found: ${id}`);
179
+ this.mutate("delete", [id], () => {
180
+ if (permanent) {
181
+ this.deleteRecursive(id);
182
+ } else {
183
+ entry.node.deletedAt = Date.now();
184
+ }
185
+ });
186
+ });
187
+ }
188
+ deleteNodes(ids, permanent = false) {
189
+ return __async(this, null, function* () {
190
+ this.mutate("delete", ids, () => {
191
+ for (const id of ids) {
192
+ const entry = this.store.get(id);
193
+ if (!entry) continue;
194
+ if (permanent) {
195
+ this.deleteRecursive(id);
196
+ } else {
197
+ entry.node.deletedAt = Date.now();
198
+ }
199
+ }
200
+ });
201
+ });
202
+ }
203
+ restoreNode(id) {
204
+ return __async(this, null, function* () {
205
+ const entry = this.store.get(id);
206
+ if (!entry) throw new Error(`Node not found: ${id}`);
207
+ return this.mutate("restore", [id], () => {
208
+ entry.node.deletedAt = null;
209
+ entry.node.updatedAt = Date.now();
210
+ return entry.node;
211
+ });
212
+ });
213
+ }
214
+ purgeNode(id) {
215
+ return __async(this, null, function* () {
216
+ this.mutate("purge", [id], () => this.deleteRecursive(id));
217
+ });
218
+ }
219
+ moveNode(id, newParentId) {
220
+ return __async(this, null, function* () {
221
+ const entry = this.store.get(id);
222
+ if (!entry) throw new Error(`Node not found: ${id}`);
223
+ return this.mutate("move", [id], () => {
224
+ entry.node.parentId = newParentId;
225
+ entry.node.updatedAt = Date.now();
226
+ return entry.node;
227
+ });
228
+ });
229
+ }
230
+ moveNodes(ids, newParentId) {
231
+ return __async(this, null, function* () {
232
+ return this.mutate("move", ids, () => {
233
+ const moved = [];
234
+ for (const id of ids) {
235
+ const entry = this.store.get(id);
236
+ if (!entry) continue;
237
+ const before = __spreadValues({}, entry.node);
238
+ entry.node.parentId = newParentId;
239
+ entry.node.updatedAt = Date.now();
240
+ moved.push(before);
241
+ }
242
+ return moved;
243
+ });
244
+ });
245
+ }
246
+ lockNode(id, sessionId) {
247
+ return __async(this, null, function* () {
248
+ const entry = this.store.get(id);
249
+ if (!entry) throw new Error(`Node not found: ${id}`);
250
+ if (entry.node.lockedBy && entry.node.lockedBy !== sessionId) {
251
+ throw new Error(`Node ${id} is locked by another session`);
252
+ }
253
+ return this.mutate("lock", [id], () => {
254
+ entry.node.lockedBy = sessionId;
255
+ entry.node.updatedAt = Date.now();
256
+ return entry.node;
257
+ });
258
+ });
259
+ }
260
+ unlockNode(id, sessionId) {
261
+ return __async(this, null, function* () {
262
+ const entry = this.store.get(id);
263
+ if (!entry) throw new Error(`Node not found: ${id}`);
264
+ if (entry.node.lockedBy !== sessionId) {
265
+ throw new Error(`Cannot unlock node ${id} \u2014 session mismatch`);
266
+ }
267
+ return this.mutate("unlock", [id], () => {
268
+ entry.node.lockedBy = null;
269
+ entry.node.updatedAt = Date.now();
270
+ return entry.node;
271
+ });
272
+ });
273
+ }
274
+ getOrder(parentId) {
275
+ return __async(this, null, function* () {
276
+ var _a;
277
+ return (_a = this.orders.get(parentId != null ? parentId : "root")) != null ? _a : null;
278
+ });
279
+ }
280
+ setOrder(parentId, orderedIds) {
281
+ return __async(this, null, function* () {
282
+ this.mutate("reorder", [], () => {
283
+ this.orders.set(parentId != null ? parentId : "root", { parentId, orderedIds });
284
+ });
285
+ });
286
+ }
287
+ getSnapshots(fileId) {
288
+ return __async(this, null, function* () {
289
+ var _a;
290
+ return (_a = this.snapshots.get(fileId)) != null ? _a : [];
291
+ });
292
+ }
293
+ saveSnapshot(fileId, content, label) {
294
+ return __async(this, null, function* () {
295
+ var _a;
296
+ const existing = (_a = this.snapshots.get(fileId)) != null ? _a : [];
297
+ const snapshot = {
298
+ id: this.generateId(),
299
+ fileId,
300
+ index: existing.length,
301
+ content,
302
+ createdAt: Date.now(),
303
+ label
304
+ };
305
+ return this.mutate("snapshot", [fileId], () => {
306
+ const updated = [...existing, snapshot];
307
+ this.snapshots.set(fileId, updated);
308
+ return snapshot;
309
+ });
310
+ });
311
+ }
312
+ restoreSnapshot(fileId, index) {
313
+ return __async(this, null, function* () {
314
+ const snaps = this.snapshots.get(fileId);
315
+ if (!snaps) throw new Error(`No snapshots for file: ${fileId}`);
316
+ const snap = snaps.find((s) => s.index === index);
317
+ if (!snap) throw new Error(`Snapshot ${index} not found for file: ${fileId}`);
318
+ yield this.writeFile(fileId, snap.content);
319
+ });
320
+ }
321
+ deleteSnapshot(fileId, index) {
322
+ return __async(this, null, function* () {
323
+ const snaps = this.snapshots.get(fileId);
324
+ if (!snaps) throw new Error(`No snapshots for file: ${fileId}`);
325
+ this.mutate("snapshot", [fileId], () => {
326
+ this.snapshots.set(
327
+ fileId,
328
+ snaps.filter((s) => s.index !== index)
329
+ );
330
+ });
331
+ });
332
+ }
333
+ onChanged(callback) {
334
+ this.listeners.add(callback);
335
+ return () => this.listeners.delete(callback);
336
+ }
337
+ deleteRecursive(id) {
338
+ for (const entry of this.store.values()) {
339
+ if (entry.node.parentId === id) {
340
+ this.deleteRecursive(entry.node.id);
341
+ }
342
+ }
343
+ this.store.delete(id);
344
+ this.snapshots.delete(id);
345
+ }
346
+ isDescendantOf(id, ancestorId) {
347
+ var _a, _b;
348
+ let current = (_a = this.store.get(id)) == null ? void 0 : _a.node;
349
+ while (current == null ? void 0 : current.parentId) {
350
+ if (current.parentId === ancestorId) return true;
351
+ current = (_b = this.store.get(current.parentId)) == null ? void 0 : _b.node;
352
+ }
353
+ return false;
354
+ }
355
+ seed(nodes) {
356
+ for (const { node, content } of nodes) {
357
+ if (node.kind === "file") {
358
+ this.store.set(node.id, {
359
+ node,
360
+ content: content != null ? content : new Uint8Array()
361
+ });
362
+ } else {
363
+ this.store.set(node.id, { node });
364
+ }
365
+ }
366
+ }
367
+ clear() {
368
+ this.store.clear();
369
+ this.orders.clear();
370
+ this.snapshots.clear();
371
+ }
372
+ snapshot() {
373
+ return new Map(this.store);
374
+ }
375
+ };
376
+
377
+ // src/adapters/IndexedDBAdapter.ts
378
+ import { openDB } from "idb";
379
+ import { nanoid as nanoid2 } from "nanoid";
380
+ var DB_VERSION = 1;
381
+ var IndexedDBAdapter = class extends VfsAdapter {
382
+ constructor(options) {
383
+ var _a, _b;
384
+ super();
385
+ this.supportsHistory = true;
386
+ this.db = null;
387
+ this.listeners = /* @__PURE__ */ new Set();
388
+ this.channel = null;
389
+ this.options = {
390
+ dbName: options.dbName,
391
+ dbVersion: (_a = options.dbVersion) != null ? _a : DB_VERSION,
392
+ isolation: (_b = options.isolation) != null ? _b : "private"
393
+ };
394
+ }
395
+ open() {
396
+ return __async(this, null, function* () {
397
+ this.db = yield openDB(
398
+ this.options.dbName,
399
+ this.options.dbVersion,
400
+ {
401
+ upgrade(db) {
402
+ if (!db.objectStoreNames.contains("nodes")) {
403
+ const nodeStore = db.createObjectStore("nodes", { keyPath: "id" });
404
+ nodeStore.createIndex("by_parent", "parentId", { unique: false });
405
+ nodeStore.createIndex("by_deleted", "deletedAt", { unique: false });
406
+ nodeStore.createIndex("by_name", "name", { unique: false });
407
+ }
408
+ if (!db.objectStoreNames.contains("content")) {
409
+ db.createObjectStore("content", { keyPath: "id" });
410
+ }
411
+ if (!db.objectStoreNames.contains("orders")) {
412
+ db.createObjectStore("orders", { keyPath: "key" });
413
+ }
414
+ if (!db.objectStoreNames.contains("snapshots")) {
415
+ const snapStore = db.createObjectStore("snapshots", { keyPath: "id" });
416
+ snapStore.createIndex("by_file", "fileId", { unique: false });
417
+ }
418
+ if (!db.objectStoreNames.contains("meta")) {
419
+ db.createObjectStore("meta", { keyPath: "key" });
420
+ }
421
+ }
422
+ }
423
+ );
424
+ if (this.options.isolation === "shared") {
425
+ this.channel = new BroadcastChannel(`vfs-kit:${this.options.dbName}`);
426
+ this.channel.onmessage = (e) => {
427
+ this.notifyListeners(e.data);
428
+ };
429
+ }
430
+ });
431
+ }
432
+ close() {
433
+ var _a, _b;
434
+ (_a = this.db) == null ? void 0 : _a.close();
435
+ (_b = this.channel) == null ? void 0 : _b.close();
436
+ this.db = null;
437
+ this.channel = null;
438
+ }
439
+ get idb() {
440
+ if (!this.db) throw new Error(
441
+ `IndexedDBAdapter: call open() before using the adapter`
442
+ );
443
+ return this.db;
444
+ }
445
+ generateId() {
446
+ return nanoid2();
447
+ }
448
+ mutate(op, nodeIds, fn) {
449
+ return __async(this, null, function* () {
450
+ var _a;
451
+ const result = yield fn();
452
+ const change = { op, nodeIds, timestamp: Date.now() };
453
+ this.notifyListeners(change);
454
+ (_a = this.channel) == null ? void 0 : _a.postMessage(change);
455
+ return result;
456
+ });
457
+ }
458
+ notifyListeners(change) {
459
+ for (const listener of this.listeners) listener(change);
460
+ }
461
+ makeFileNode(params, id) {
462
+ var _a, _b;
463
+ const now = Date.now();
464
+ return {
465
+ id,
466
+ name: params.name,
467
+ kind: "file",
468
+ parentId: params.parentId,
469
+ mimeType: (_a = params.mimeType) != null ? _a : "application/octet-stream",
470
+ size: 0,
471
+ sortIndex: null,
472
+ lockedBy: null,
473
+ createdAt: now,
474
+ updatedAt: now,
475
+ deletedAt: null,
476
+ meta: (_b = params.meta) != null ? _b : {}
477
+ };
478
+ }
479
+ makeFolderNode(params, id) {
480
+ var _a;
481
+ const now = Date.now();
482
+ return {
483
+ id,
484
+ name: params.name,
485
+ kind: "folder",
486
+ parentId: params.parentId,
487
+ sortIndex: null,
488
+ lockedBy: null,
489
+ createdAt: now,
490
+ updatedAt: now,
491
+ deletedAt: null,
492
+ meta: (_a = params.meta) != null ? _a : {}
493
+ };
494
+ }
495
+ getNodes(ids) {
496
+ return __async(this, null, function* () {
497
+ if (ids.length === 0) {
498
+ return this.idb.getAll("nodes");
499
+ }
500
+ const tx = this.idb.transaction("nodes", "readonly");
501
+ const results = yield Promise.all(ids.map((id) => tx.store.get(id)));
502
+ return results.filter((n) => !!n);
503
+ });
504
+ }
505
+ getNodeById(id) {
506
+ return __async(this, null, function* () {
507
+ var _a;
508
+ return (_a = yield this.idb.get("nodes", id)) != null ? _a : null;
509
+ });
510
+ }
511
+ getNodeByPath(path) {
512
+ return __async(this, null, function* () {
513
+ const parts = path.replace(/^\//, "").split("/").filter(Boolean);
514
+ if (parts.length === 0) return null;
515
+ let parentId = null;
516
+ for (const part of parts) {
517
+ const all = yield this.idb.getAllFromIndex(
518
+ "nodes",
519
+ "by_parent",
520
+ parentId
521
+ );
522
+ const match = all.find(
523
+ (n) => n.name === part && n.deletedAt === null
524
+ );
525
+ if (!match) return null;
526
+ parentId = match.id;
527
+ }
528
+ return parentId ? this.getNodeById(parentId) : null;
529
+ });
530
+ }
531
+ getChildren(parentId, options) {
532
+ return __async(this, null, function* () {
533
+ const nodes = yield this.idb.getAllFromIndex(
534
+ "nodes",
535
+ "by_parent",
536
+ parentId
537
+ );
538
+ return (options == null ? void 0 : options.includeTrashed) ? nodes : nodes.filter((n) => n.deletedAt === null);
539
+ });
540
+ }
541
+ getTrashed() {
542
+ return __async(this, null, function* () {
543
+ const all = yield this.idb.getAll("nodes");
544
+ return all.filter((n) => n.deletedAt !== null);
545
+ });
546
+ }
547
+ search(query, options) {
548
+ return __async(this, null, function* () {
549
+ const lower = query.toLowerCase();
550
+ const all = yield this.idb.getAllFromIndex(
551
+ "nodes",
552
+ "by_name"
553
+ );
554
+ return all.filter((n) => {
555
+ if (!(options == null ? void 0 : options.includeTrashed) && n.deletedAt !== null) return false;
556
+ if ((options == null ? void 0 : options.kind) && n.kind !== options.kind) return false;
557
+ if (!n.name.toLowerCase().includes(lower)) return false;
558
+ if (options == null ? void 0 : options.scope) {
559
+ return this.isDescendantOf(n, options.scope, all);
560
+ }
561
+ return true;
562
+ });
563
+ });
564
+ }
565
+ readFile(id) {
566
+ return __async(this, null, function* () {
567
+ const entry = yield this.idb.get("content", id);
568
+ if (!entry) throw new Error(`Content not found for file: ${id}`);
569
+ return entry.data;
570
+ });
571
+ }
572
+ writeFile(id, content) {
573
+ return __async(this, null, function* () {
574
+ yield this.mutate("write", [id], () => __async(this, null, function* () {
575
+ const tx = this.idb.transaction(["content", "nodes"], "readwrite");
576
+ const node = yield tx.objectStore("nodes").get(id);
577
+ if (!node || node.kind !== "file") {
578
+ throw new Error(`File not found: ${id}`);
579
+ }
580
+ node.size = content.byteLength;
581
+ node.updatedAt = Date.now();
582
+ yield tx.objectStore("content").put({ id, data: content });
583
+ yield tx.objectStore("nodes").put(node);
584
+ yield tx.done;
585
+ }));
586
+ });
587
+ }
588
+ createFile(params) {
589
+ return __async(this, null, function* () {
590
+ const id = this.generateId();
591
+ const node = this.makeFileNode(params, id);
592
+ return this.mutate("create", [id], () => __async(this, null, function* () {
593
+ const tx = this.idb.transaction(["nodes", "content"], "readwrite");
594
+ yield tx.objectStore("nodes").put(node);
595
+ yield tx.objectStore("content").put({ id, data: new Uint8Array() });
596
+ yield tx.done;
597
+ return node;
598
+ }));
599
+ });
600
+ }
601
+ createFolder(params) {
602
+ return __async(this, null, function* () {
603
+ const id = this.generateId();
604
+ const node = this.makeFolderNode(params, id);
605
+ return this.mutate("create", [id], () => __async(this, null, function* () {
606
+ yield this.idb.put("nodes", node);
607
+ return node;
608
+ }));
609
+ });
610
+ }
611
+ updateNode(id, updates) {
612
+ return __async(this, null, function* () {
613
+ return this.mutate("rename", [id], () => __async(this, null, function* () {
614
+ const node = yield this.idb.get("nodes", id);
615
+ if (!node) throw new Error(`Node not found: ${id}`);
616
+ const updated = __spreadProps(__spreadValues(__spreadValues({}, node), updates), { updatedAt: Date.now() });
617
+ yield this.idb.put("nodes", updated);
618
+ return updated;
619
+ }));
620
+ });
621
+ }
622
+ deleteNode(id, permanent = false) {
623
+ return __async(this, null, function* () {
624
+ yield this.mutate("delete", [id], () => __async(this, null, function* () {
625
+ if (permanent) {
626
+ yield this.deleteRecursive(id);
627
+ } else {
628
+ const node = yield this.idb.get("nodes", id);
629
+ if (!node) throw new Error(`Node not found: ${id}`);
630
+ yield this.idb.put("nodes", __spreadProps(__spreadValues({}, node), {
631
+ deletedAt: Date.now()
632
+ }));
633
+ }
634
+ }));
635
+ });
636
+ }
637
+ deleteNodes(ids, permanent = false) {
638
+ return __async(this, null, function* () {
639
+ yield this.mutate("delete", ids, () => __async(this, null, function* () {
640
+ const tx = this.idb.transaction("nodes", "readwrite");
641
+ for (const id of ids) {
642
+ if (permanent) {
643
+ yield this.deleteRecursive(id);
644
+ } else {
645
+ const node = yield tx.store.get(id);
646
+ if (!node) continue;
647
+ yield tx.store.put(__spreadProps(__spreadValues({}, node), { deletedAt: Date.now() }));
648
+ }
649
+ }
650
+ yield tx.done;
651
+ }));
652
+ });
653
+ }
654
+ restoreNode(id) {
655
+ return __async(this, null, function* () {
656
+ return this.mutate("restore", [id], () => __async(this, null, function* () {
657
+ const node = yield this.idb.get("nodes", id);
658
+ if (!node) throw new Error(`Node not found: ${id}`);
659
+ const restored = __spreadProps(__spreadValues({}, node), { deletedAt: null, updatedAt: Date.now() });
660
+ yield this.idb.put("nodes", restored);
661
+ return restored;
662
+ }));
663
+ });
664
+ }
665
+ purgeNode(id) {
666
+ return __async(this, null, function* () {
667
+ yield this.mutate("purge", [id], () => this.deleteRecursive(id));
668
+ });
669
+ }
670
+ moveNode(id, newParentId) {
671
+ return __async(this, null, function* () {
672
+ return this.mutate("move", [id], () => __async(this, null, function* () {
673
+ const node = yield this.idb.get("nodes", id);
674
+ if (!node) throw new Error(`Node not found: ${id}`);
675
+ const moved = __spreadProps(__spreadValues({}, node), { parentId: newParentId, updatedAt: Date.now() });
676
+ yield this.idb.put("nodes", moved);
677
+ return moved;
678
+ }));
679
+ });
680
+ }
681
+ moveNodes(ids, newParentId) {
682
+ return __async(this, null, function* () {
683
+ return this.mutate("move", ids, () => __async(this, null, function* () {
684
+ const tx = this.idb.transaction("nodes", "readwrite");
685
+ const moved = [];
686
+ for (const id of ids) {
687
+ const node = yield tx.store.get(id);
688
+ if (!node) continue;
689
+ const updated = __spreadProps(__spreadValues({}, node), { parentId: newParentId, updatedAt: Date.now() });
690
+ yield tx.store.put(updated);
691
+ moved.push(updated);
692
+ }
693
+ yield tx.done;
694
+ return moved;
695
+ }));
696
+ });
697
+ }
698
+ lockNode(id, sessionId) {
699
+ return __async(this, null, function* () {
700
+ return this.mutate("lock", [id], () => __async(this, null, function* () {
701
+ const node = yield this.idb.get("nodes", id);
702
+ if (!node) throw new Error(`Node not found: ${id}`);
703
+ if (node.lockedBy && node.lockedBy !== sessionId) {
704
+ throw new Error(`Node ${id} is locked by another session`);
705
+ }
706
+ const locked = __spreadProps(__spreadValues({}, node), { lockedBy: sessionId, updatedAt: Date.now() });
707
+ yield this.idb.put("nodes", locked);
708
+ return locked;
709
+ }));
710
+ });
711
+ }
712
+ unlockNode(id, sessionId) {
713
+ return __async(this, null, function* () {
714
+ return this.mutate("unlock", [id], () => __async(this, null, function* () {
715
+ const node = yield this.idb.get("nodes", id);
716
+ if (!node) throw new Error(`Node not found: ${id}`);
717
+ if (node.lockedBy !== sessionId) {
718
+ throw new Error(`Cannot unlock node ${id} \u2014 session mismatch`);
719
+ }
720
+ const unlocked = __spreadProps(__spreadValues({}, node), { lockedBy: null, updatedAt: Date.now() });
721
+ yield this.idb.put("nodes", unlocked);
722
+ return unlocked;
723
+ }));
724
+ });
725
+ }
726
+ getOrder(parentId) {
727
+ return __async(this, null, function* () {
728
+ const key = parentId != null ? parentId : "root";
729
+ const record = yield this.idb.get("orders", key);
730
+ return record ? { parentId: record.parentId, orderedIds: record.orderedIds } : null;
731
+ });
732
+ }
733
+ setOrder(parentId, orderedIds) {
734
+ return __async(this, null, function* () {
735
+ yield this.mutate("reorder", [], () => __async(this, null, function* () {
736
+ const key = parentId != null ? parentId : "root";
737
+ yield this.idb.put("orders", { key, parentId, orderedIds });
738
+ }));
739
+ });
740
+ }
741
+ getSnapshots(fileId) {
742
+ return __async(this, null, function* () {
743
+ return this.idb.getAllFromIndex("snapshots", "by_file", fileId);
744
+ });
745
+ }
746
+ saveSnapshot(fileId, content, label) {
747
+ return __async(this, null, function* () {
748
+ return this.mutate("snapshot", [fileId], () => __async(this, null, function* () {
749
+ const existing = yield this.idb.getAllFromIndex(
750
+ "snapshots",
751
+ "by_file",
752
+ fileId
753
+ );
754
+ if (existing.length >= 50) {
755
+ const oldest = existing.sort((a, b) => a.index - b.index)[0];
756
+ yield this.idb.delete("snapshots", oldest.id);
757
+ }
758
+ const snapshot = {
759
+ id: this.generateId(),
760
+ fileId,
761
+ index: existing.length,
762
+ content,
763
+ createdAt: Date.now(),
764
+ label
765
+ };
766
+ yield this.idb.put("snapshots", snapshot);
767
+ return snapshot;
768
+ }));
769
+ });
770
+ }
771
+ restoreSnapshot(fileId, index) {
772
+ return __async(this, null, function* () {
773
+ const all = yield this.idb.getAllFromIndex("snapshots", "by_file", fileId);
774
+ const snap = all.find((s) => s.index === index);
775
+ if (!snap) throw new Error(`Snapshot ${index} not found for file: ${fileId}`);
776
+ yield this.writeFile(fileId, snap.content);
777
+ });
778
+ }
779
+ deleteSnapshot(fileId, index) {
780
+ return __async(this, null, function* () {
781
+ yield this.mutate("snapshot", [fileId], () => __async(this, null, function* () {
782
+ const all = yield this.idb.getAllFromIndex("snapshots", "by_file", fileId);
783
+ const snap = all.find((s) => s.index === index);
784
+ if (!snap) throw new Error(`Snapshot ${index} not found`);
785
+ yield this.idb.delete("snapshots", snap.id);
786
+ }));
787
+ });
788
+ }
789
+ onChanged(callback) {
790
+ this.listeners.add(callback);
791
+ return () => this.listeners.delete(callback);
792
+ }
793
+ // ── Private helpers ────────────────────────────────────────────────────
794
+ deleteRecursive(id) {
795
+ return __async(this, null, function* () {
796
+ const children = yield this.idb.getAllFromIndex(
797
+ "nodes",
798
+ "by_parent",
799
+ id
800
+ );
801
+ for (const child of children) {
802
+ yield this.deleteRecursive(child.id);
803
+ }
804
+ yield this.idb.delete("nodes", id);
805
+ yield this.idb.delete("content", id);
806
+ yield this.idb.delete("snapshots", id);
807
+ });
808
+ }
809
+ isDescendantOf(node, ancestorId, allNodes) {
810
+ const index = new Map(allNodes.map((n) => [n.id, n]));
811
+ let current = node;
812
+ while (current == null ? void 0 : current.parentId) {
813
+ if (current.parentId === ancestorId) return true;
814
+ current = index.get(current.parentId);
815
+ }
816
+ return false;
817
+ }
818
+ };
819
+
820
+ // src/adapters/RestAdapter.ts
821
+ import { nanoid as nanoid3 } from "nanoid";
822
+ var defaultEndpoints = (base) => ({
823
+ getNode: (id) => `${base}/nodes/${id}`,
824
+ getNodes: (ids) => `${base}/nodes?ids=${ids.join(",")}`,
825
+ getNodeByPath: (path) => `${base}/nodes/by-path?path=${encodeURIComponent(path)}`,
826
+ getChildren: (parentId) => `${base}/nodes/${parentId != null ? parentId : "root"}/children`,
827
+ getTrashed: () => `${base}/nodes/trashed`,
828
+ search: (query) => `${base}/nodes/search?q=${encodeURIComponent(query)}`,
829
+ readFile: (id) => `${base}/content/${id}`,
830
+ writeFile: (id) => `${base}/content/${id}`,
831
+ createFile: () => `${base}/nodes/file`,
832
+ createFolder: () => `${base}/nodes/folder`,
833
+ updateNode: (id) => `${base}/nodes/${id}`,
834
+ deleteNode: (id) => `${base}/nodes/${id}`,
835
+ deleteNodes: () => `${base}/nodes/batch/delete`,
836
+ restoreNode: (id) => `${base}/nodes/${id}/restore`,
837
+ purgeNode: (id) => `${base}/nodes/${id}/purge`,
838
+ moveNode: (id) => `${base}/nodes/${id}/move`,
839
+ moveNodes: () => `${base}/nodes/batch/move`,
840
+ lockNode: (id) => `${base}/nodes/${id}/lock`,
841
+ unlockNode: (id) => `${base}/nodes/${id}/unlock`,
842
+ getOrder: (parentId) => `${base}/orders/${parentId != null ? parentId : "root"}`,
843
+ setOrder: (parentId) => `${base}/orders/${parentId != null ? parentId : "root"}`,
844
+ getSnapshots: (fileId) => `${base}/snapshots/${fileId}`,
845
+ saveSnapshot: (fileId) => `${base}/snapshots/${fileId}`,
846
+ restoreSnapshot: (fileId, index) => `${base}/snapshots/${fileId}/${index}/restore`,
847
+ deleteSnapshot: (fileId, index) => `${base}/snapshots/${fileId}/${index}`,
848
+ subscribe: () => `${base}/events`
849
+ });
850
+ var RestAdapter = class extends VfsAdapter {
851
+ constructor(options) {
852
+ var _a, _b;
853
+ super();
854
+ this.listeners = /* @__PURE__ */ new Set();
855
+ this.eventSource = null;
856
+ this.options = options;
857
+ this.supportsHistory = (_a = options.supportsHistory) != null ? _a : true;
858
+ this.fetch = (_b = options.fetch) != null ? _b : globalThis.fetch.bind(globalThis);
859
+ this.endpoints = __spreadValues(__spreadValues({}, defaultEndpoints(options.baseUrl)), options.endpoints);
860
+ }
861
+ open() {
862
+ return __async(this, null, function* () {
863
+ yield this.connectRealtime();
864
+ });
865
+ }
866
+ close() {
867
+ var _a;
868
+ (_a = this.eventSource) == null ? void 0 : _a.close();
869
+ this.eventSource = null;
870
+ }
871
+ connectRealtime() {
872
+ return __async(this, null, function* () {
873
+ try {
874
+ const url = this.endpoints.subscribe();
875
+ const es = new EventSource(url);
876
+ es.onmessage = (e) => {
877
+ try {
878
+ const change = JSON.parse(e.data);
879
+ this.notifyListeners(change);
880
+ } catch (e2) {
881
+ }
882
+ };
883
+ es.onerror = () => {
884
+ es.close();
885
+ this.eventSource = null;
886
+ this.fallbackRealtime();
887
+ };
888
+ this.eventSource = es;
889
+ } catch (e) {
890
+ this.fallbackRealtime();
891
+ }
892
+ });
893
+ }
894
+ fallbackRealtime() {
895
+ if (!this.options.realtimeFallback) return;
896
+ this.options.realtimeFallback(
897
+ (change) => this.notifyListeners(change),
898
+ (err) => console.error("[RestAdapter] realtime fallback error:", err)
899
+ );
900
+ }
901
+ notifyListeners(change) {
902
+ for (const listener of this.listeners) listener(change);
903
+ }
904
+ generateId() {
905
+ return nanoid3();
906
+ }
907
+ get(url) {
908
+ return __async(this, null, function* () {
909
+ const res = yield this.fetch(url, { method: "GET" });
910
+ if (!res.ok) throw new Error(`GET ${url} failed: ${res.status}`);
911
+ return res.json();
912
+ });
913
+ }
914
+ post(url, body) {
915
+ return __async(this, null, function* () {
916
+ const res = yield this.fetch(url, {
917
+ method: "POST",
918
+ headers: { "Content-Type": "application/json" },
919
+ body: body ? JSON.stringify(body) : void 0
920
+ });
921
+ if (!res.ok) throw new Error(`POST ${url} failed: ${res.status}`);
922
+ return res.json();
923
+ });
924
+ }
925
+ patch(url, body) {
926
+ return __async(this, null, function* () {
927
+ const res = yield this.fetch(url, {
928
+ method: "PATCH",
929
+ headers: { "Content-Type": "application/json" },
930
+ body: JSON.stringify(body)
931
+ });
932
+ if (!res.ok) throw new Error(`PATCH ${url} failed: ${res.status}`);
933
+ return res.json();
934
+ });
935
+ }
936
+ delete(url, body) {
937
+ return __async(this, null, function* () {
938
+ const res = yield this.fetch(url, {
939
+ method: "DELETE",
940
+ headers: body ? { "Content-Type": "application/json" } : {},
941
+ body: body ? JSON.stringify(body) : void 0
942
+ });
943
+ if (!res.ok) throw new Error(`DELETE ${url} failed: ${res.status}`);
944
+ });
945
+ }
946
+ postBinary(url, content) {
947
+ return __async(this, null, function* () {
948
+ const res = yield this.fetch(url, {
949
+ method: "PUT",
950
+ headers: { "Content-Type": "application/octet-stream" },
951
+ body: content.buffer
952
+ });
953
+ if (!res.ok) throw new Error(`PUT ${url} failed: ${res.status}`);
954
+ });
955
+ }
956
+ mutate(fn) {
957
+ return __async(this, null, function* () {
958
+ return fn();
959
+ });
960
+ }
961
+ getNodes(ids) {
962
+ return __async(this, null, function* () {
963
+ if (ids.length === 0) return this.get(this.endpoints.getNodes([]));
964
+ return this.get(this.endpoints.getNodes(ids));
965
+ });
966
+ }
967
+ getNodeById(id) {
968
+ return __async(this, null, function* () {
969
+ try {
970
+ return yield this.get(this.endpoints.getNode(id));
971
+ } catch (e) {
972
+ return null;
973
+ }
974
+ });
975
+ }
976
+ getNodeByPath(path) {
977
+ return __async(this, null, function* () {
978
+ try {
979
+ return yield this.get(this.endpoints.getNodeByPath(path));
980
+ } catch (e) {
981
+ return null;
982
+ }
983
+ });
984
+ }
985
+ getChildren(parentId, options) {
986
+ return __async(this, null, function* () {
987
+ const url = this.endpoints.getChildren(parentId);
988
+ const fullUrl = (options == null ? void 0 : options.includeTrashed) ? `${url}?includeTrashed=true` : url;
989
+ return this.get(fullUrl);
990
+ });
991
+ }
992
+ getTrashed() {
993
+ return __async(this, null, function* () {
994
+ return this.get(this.endpoints.getTrashed());
995
+ });
996
+ }
997
+ search(query, options) {
998
+ return __async(this, null, function* () {
999
+ const params = new URLSearchParams({ q: query });
1000
+ if (options == null ? void 0 : options.kind) params.set("kind", options.kind);
1001
+ if (options == null ? void 0 : options.scope) params.set("scope", options.scope);
1002
+ if (options == null ? void 0 : options.includeTrashed) params.set("includeTrashed", "true");
1003
+ return this.get(`${this.endpoints.search(query)}?${params}`);
1004
+ });
1005
+ }
1006
+ readFile(id) {
1007
+ return __async(this, null, function* () {
1008
+ const res = yield this.fetch(
1009
+ this.endpoints.readFile(id),
1010
+ { method: "GET" }
1011
+ );
1012
+ if (!res.ok) throw new Error(`readFile ${id} failed: ${res.status}`);
1013
+ return new Uint8Array(yield res.arrayBuffer());
1014
+ });
1015
+ }
1016
+ writeFile(id, content) {
1017
+ return __async(this, null, function* () {
1018
+ yield this.mutate(
1019
+ () => this.postBinary(this.endpoints.writeFile(id), content)
1020
+ );
1021
+ });
1022
+ }
1023
+ createFile(params) {
1024
+ return __async(this, null, function* () {
1025
+ return this.mutate(
1026
+ () => this.post(this.endpoints.createFile(), params)
1027
+ );
1028
+ });
1029
+ }
1030
+ createFolder(params) {
1031
+ return __async(this, null, function* () {
1032
+ return this.mutate(
1033
+ () => this.post(this.endpoints.createFolder(), params)
1034
+ );
1035
+ });
1036
+ }
1037
+ updateNode(id, updates) {
1038
+ return __async(this, null, function* () {
1039
+ return this.mutate(
1040
+ () => this.patch(this.endpoints.updateNode(id), updates)
1041
+ );
1042
+ });
1043
+ }
1044
+ deleteNode(id, permanent = false) {
1045
+ return __async(this, null, function* () {
1046
+ return this.mutate(
1047
+ () => this.delete(this.endpoints.deleteNode(id), { permanent })
1048
+ );
1049
+ });
1050
+ }
1051
+ deleteNodes(ids, permanent = false) {
1052
+ return __async(this, null, function* () {
1053
+ return this.mutate(
1054
+ () => this.delete(this.endpoints.deleteNodes(), { ids, permanent })
1055
+ );
1056
+ });
1057
+ }
1058
+ restoreNode(id) {
1059
+ return __async(this, null, function* () {
1060
+ return this.mutate(
1061
+ () => this.post(this.endpoints.restoreNode(id))
1062
+ );
1063
+ });
1064
+ }
1065
+ purgeNode(id) {
1066
+ return __async(this, null, function* () {
1067
+ return this.mutate(
1068
+ () => this.delete(this.endpoints.purgeNode(id))
1069
+ );
1070
+ });
1071
+ }
1072
+ moveNode(id, newParentId) {
1073
+ return __async(this, null, function* () {
1074
+ return this.mutate(
1075
+ () => this.post(this.endpoints.moveNode(id), { newParentId })
1076
+ );
1077
+ });
1078
+ }
1079
+ moveNodes(ids, newParentId) {
1080
+ return __async(this, null, function* () {
1081
+ return this.mutate(
1082
+ () => this.post(this.endpoints.moveNodes(), { ids, newParentId })
1083
+ );
1084
+ });
1085
+ }
1086
+ lockNode(id, sessionId) {
1087
+ return __async(this, null, function* () {
1088
+ return this.mutate(
1089
+ () => this.post(this.endpoints.lockNode(id), { sessionId })
1090
+ );
1091
+ });
1092
+ }
1093
+ unlockNode(id, sessionId) {
1094
+ return __async(this, null, function* () {
1095
+ return this.mutate(
1096
+ () => this.post(this.endpoints.unlockNode(id), { sessionId })
1097
+ );
1098
+ });
1099
+ }
1100
+ getOrder(parentId) {
1101
+ return __async(this, null, function* () {
1102
+ try {
1103
+ return yield this.get(this.endpoints.getOrder(parentId));
1104
+ } catch (e) {
1105
+ return null;
1106
+ }
1107
+ });
1108
+ }
1109
+ setOrder(parentId, orderedIds) {
1110
+ return __async(this, null, function* () {
1111
+ yield this.mutate(
1112
+ () => this.post(this.endpoints.setOrder(parentId), { orderedIds })
1113
+ );
1114
+ });
1115
+ }
1116
+ getSnapshots(fileId) {
1117
+ return __async(this, null, function* () {
1118
+ return this.get(this.endpoints.getSnapshots(fileId));
1119
+ });
1120
+ }
1121
+ saveSnapshot(fileId, content, label) {
1122
+ return __async(this, null, function* () {
1123
+ const res = yield this.fetch(
1124
+ this.endpoints.saveSnapshot(fileId),
1125
+ {
1126
+ method: "POST",
1127
+ headers: __spreadValues({ "Content-Type": "application/octet-stream" }, label ? { "x-snapshot-label": label } : {}),
1128
+ body: content.buffer
1129
+ }
1130
+ );
1131
+ if (!res.ok) throw new Error(`saveSnapshot failed: ${res.status}`);
1132
+ return res.json();
1133
+ });
1134
+ }
1135
+ restoreSnapshot(fileId, index) {
1136
+ return __async(this, null, function* () {
1137
+ yield this.mutate(
1138
+ () => this.post(this.endpoints.restoreSnapshot(fileId, index))
1139
+ );
1140
+ });
1141
+ }
1142
+ deleteSnapshot(fileId, index) {
1143
+ return __async(this, null, function* () {
1144
+ yield this.mutate(
1145
+ () => this.delete(this.endpoints.deleteSnapshot(fileId, index))
1146
+ );
1147
+ });
1148
+ }
1149
+ onChanged(callback) {
1150
+ this.listeners.add(callback);
1151
+ return () => this.listeners.delete(callback);
1152
+ }
1153
+ };
1154
+ export {
1155
+ InMemoryAdapter,
1156
+ IndexedDBAdapter,
1157
+ RestAdapter
1158
+ };
1159
+ //# sourceMappingURL=index.mjs.map