mvcc-api 1.0.3 → 1.2.0

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.
@@ -20,251 +20,374 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- AsyncMVCCManager: () => AsyncMVCCManager,
24
23
  AsyncMVCCStrategy: () => AsyncMVCCStrategy,
25
24
  AsyncMVCCTransaction: () => AsyncMVCCTransaction,
26
- SyncMVCCManager: () => SyncMVCCManager,
27
25
  SyncMVCCStrategy: () => SyncMVCCStrategy,
28
26
  SyncMVCCTransaction: () => SyncMVCCTransaction
29
27
  });
30
28
  module.exports = __toCommonJS(src_exports);
31
29
 
32
- // src/core/base/Manager.ts
33
- var MVCCManager = class {
34
- version;
35
- strategy;
36
- versionIndex;
37
- activeTransactions;
38
- deletedCache;
39
- constructor(strategy) {
40
- this.strategy = strategy;
41
- this.version = 0;
42
- this.activeTransactions = /* @__PURE__ */ new Set();
43
- this.deletedCache = /* @__PURE__ */ new Map();
44
- this.versionIndex = /* @__PURE__ */ new Map();
45
- }
46
- _removeTransaction(tx) {
47
- this.activeTransactions.delete(tx);
48
- this._cleanupDeletedCache();
49
- }
50
- _cleanupDeletedCache() {
51
- let minVersion = this.version;
52
- for (const tx of this.activeTransactions) {
53
- minVersion = Math.min(minVersion, tx.snapshotVersion);
54
- }
55
- for (const [key, versions] of this.versionIndex.entries()) {
56
- const toKeep = [];
57
- let keptOldVersion = false;
58
- let i = versions.length;
59
- while (i--) {
60
- const v = versions[i];
61
- if (v.version > minVersion) {
62
- toKeep.unshift(v);
63
- } else if (!keptOldVersion) {
64
- toKeep.unshift(v);
65
- keptOldVersion = true;
66
- }
67
- }
68
- if (toKeep.length === 0) {
69
- this.versionIndex.delete(key);
70
- } else {
71
- this.versionIndex.set(key, toKeep);
72
- }
73
- }
74
- for (const [key, versions] of this.deletedCache.entries()) {
75
- const filtered = versions.filter((v) => v.deletedAtVersion >= minVersion);
76
- if (filtered.length === 0) {
77
- this.deletedCache.delete(key);
78
- } else {
79
- this.deletedCache.set(key, filtered);
80
- }
81
- }
82
- }
83
- };
84
-
85
30
  // src/core/base/Strategy.ts
86
31
  var MVCCStrategy = class {
87
32
  };
88
33
 
89
34
  // src/core/base/Transaction.ts
90
35
  var MVCCTransaction = class {
91
- manager;
92
36
  committed;
93
37
  snapshotVersion;
38
+ snapshotLocalVersion;
94
39
  writeBuffer;
95
40
  deleteBuffer;
96
- constructor(manager, snapshotVersion) {
97
- this.manager = manager;
98
- this.snapshotVersion = snapshotVersion;
41
+ createdKeys;
42
+ // create()로 생성된 키 추적
43
+ deletedValues;
44
+ // delete 시 삭제 전 값 저장
45
+ // Nested Transaction Properties
46
+ parent;
47
+ localVersion;
48
+ // Local version for Nested Conflict Detection
49
+ keyVersions;
50
+ // Key -> Local Version (When it was modified locally)
51
+ // Root Transaction Properties (Only populated if this is Root)
52
+ root;
53
+ strategy;
54
+ version = 0;
55
+ versionIndex = /* @__PURE__ */ new Map();
56
+ deletedCache = /* @__PURE__ */ new Map();
57
+ activeTransactions = /* @__PURE__ */ new Set();
58
+ constructor(strategy, parent, snapshotVersion) {
59
+ this.snapshotVersion = snapshotVersion ?? 0;
99
60
  this.writeBuffer = /* @__PURE__ */ new Map();
100
61
  this.deleteBuffer = /* @__PURE__ */ new Set();
62
+ this.createdKeys = /* @__PURE__ */ new Set();
63
+ this.deletedValues = /* @__PURE__ */ new Map();
101
64
  this.committed = false;
102
- }
103
- /**
104
- * Schedules a creation (insert) of a key-value pair.
105
- * Throws if the transaction is already committed.
106
- * @param key The key to create.
107
- * @param value The value to store.
108
- * @returns The transaction instance for chaining.
109
- */
110
- create(key, value) {
111
- if (this.committed) throw new Error("Transaction already committed");
65
+ this.parent = parent;
66
+ this.keyVersions = /* @__PURE__ */ new Map();
67
+ if (parent) {
68
+ this.localVersion = parent.localVersion;
69
+ this.snapshotLocalVersion = parent.localVersion;
70
+ this.strategy = void 0;
71
+ this.root = parent.root;
72
+ } else {
73
+ if (!strategy) throw new Error("Root Transaction must get Strategy");
74
+ this.strategy = strategy;
75
+ this.version = 0;
76
+ this.localVersion = 0;
77
+ this.snapshotLocalVersion = 0;
78
+ this.root = this;
79
+ }
80
+ }
81
+ isRoot() {
82
+ return !this.parent;
83
+ }
84
+ // --- Internal buffer manipulation helpers ---
85
+ _bufferCreate(key, value) {
86
+ this.localVersion++;
112
87
  this.writeBuffer.set(key, value);
113
- return this;
88
+ this.createdKeys.add(key);
89
+ this.deleteBuffer.delete(key);
90
+ this.keyVersions.set(key, this.localVersion);
114
91
  }
115
- /**
116
- * Schedules a write (update) of a key-value pair.
117
- * Overwrites any existing value in the buffer.
118
- * @param key The key to write.
119
- * @param value The value to store.
120
- * @returns The transaction instance for chaining.
121
- */
122
- write(key, value) {
123
- if (this.committed) throw new Error("Transaction already committed");
92
+ _bufferWrite(key, value) {
93
+ this.localVersion++;
124
94
  this.writeBuffer.set(key, value);
125
95
  this.deleteBuffer.delete(key);
126
- return this;
96
+ this.keyVersions.set(key, this.localVersion);
127
97
  }
128
- /**
129
- * Schedules a deletion of a key.
130
- * @param key The key to delete.
131
- * @returns The transaction instance for chaining.
132
- */
133
- delete(key) {
134
- if (this.committed) throw new Error("Transaction already committed");
98
+ _bufferDelete(key) {
99
+ this.localVersion++;
135
100
  this.deleteBuffer.add(key);
136
101
  this.writeBuffer.delete(key);
137
- return this;
102
+ this.createdKeys.delete(key);
103
+ this.keyVersions.set(key, this.localVersion);
138
104
  }
139
105
  /**
140
106
  * Rolls back the transaction.
141
107
  * Clears all buffers and marks the transaction as finished.
142
- * @returns The transaction instance.
108
+ * @returns The result object with success, created, updated, and deleted keys.
143
109
  */
144
110
  rollback() {
111
+ const created = [];
112
+ const updated = [];
113
+ for (const [key, data] of this.writeBuffer.entries()) {
114
+ if (this.createdKeys.has(key)) {
115
+ created.push({ key, data });
116
+ } else {
117
+ updated.push({ key, data });
118
+ }
119
+ }
120
+ const deleted = [];
121
+ for (const key of this.deleteBuffer) {
122
+ const data = this.deletedValues.get(key);
123
+ if (data !== void 0) {
124
+ deleted.push({ key, data });
125
+ }
126
+ }
145
127
  this.writeBuffer.clear();
146
128
  this.deleteBuffer.clear();
129
+ this.createdKeys.clear();
130
+ this.deletedValues.clear();
147
131
  this.committed = true;
148
- this.manager._removeTransaction(this);
149
- return this;
132
+ if (this.root !== this) {
133
+ this.root.activeTransactions.delete(this);
134
+ }
135
+ return { success: true, created, updated, deleted };
150
136
  }
151
137
  };
152
138
 
139
+ // src/core/sync/Strategy.ts
140
+ var SyncMVCCStrategy = class extends MVCCStrategy {
141
+ };
142
+
153
143
  // src/core/sync/Transaction.ts
154
- var SyncMVCCTransaction = class extends MVCCTransaction {
155
- read(key) {
144
+ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
145
+ create(key, value) {
156
146
  if (this.committed) throw new Error("Transaction already committed");
157
- if (this.writeBuffer.has(key)) {
158
- return this.writeBuffer.get(key);
147
+ if (this.writeBuffer.has(key) || !this.deleteBuffer.has(key) && this.read(key) !== null) {
148
+ throw new Error(`Key already exists: ${key}`);
159
149
  }
160
- if (this.deleteBuffer.has(key)) {
161
- return null;
150
+ this._bufferCreate(key, value);
151
+ return this;
152
+ }
153
+ write(key, value) {
154
+ if (this.committed) throw new Error("Transaction already committed");
155
+ if (!this.writeBuffer.has(key) && (this.deleteBuffer.has(key) || this.read(key) === null)) {
156
+ throw new Error(`Key not found: ${key}`);
162
157
  }
163
- return this.manager._diskRead(key, this.snapshotVersion);
158
+ this._bufferWrite(key, value);
159
+ return this;
164
160
  }
165
- commit() {
161
+ delete(key) {
166
162
  if (this.committed) throw new Error("Transaction already committed");
167
- this.manager._commit(this);
168
- this.committed = true;
169
- this.manager._removeTransaction(this);
163
+ let valueToDelete = null;
164
+ if (this.writeBuffer.has(key)) {
165
+ valueToDelete = this.writeBuffer.get(key);
166
+ } else if (!this.deleteBuffer.has(key)) {
167
+ valueToDelete = this.read(key);
168
+ }
169
+ if (valueToDelete === null) {
170
+ throw new Error(`Key not found: ${key}`);
171
+ }
172
+ this.deletedValues.set(key, valueToDelete);
173
+ this._bufferDelete(key);
170
174
  return this;
171
175
  }
172
- };
173
-
174
- // src/core/sync/Manager.ts
175
- var SyncMVCCManager = class extends MVCCManager {
176
- constructor(strategy) {
177
- super(strategy);
176
+ createNested() {
177
+ if (this.committed) throw new Error("Transaction already committed");
178
+ const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
179
+ const child = new _SyncMVCCTransaction(void 0, this, childVersion);
180
+ this.root.activeTransactions.add(child);
181
+ return child;
178
182
  }
179
- createTransaction() {
180
- const tx = new SyncMVCCTransaction(this, this.version);
181
- this.activeTransactions.add(tx);
182
- return tx;
183
+ read(key) {
184
+ if (this.committed) throw new Error("Transaction already committed");
185
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
186
+ if (this.deleteBuffer.has(key)) return null;
187
+ return this.root._diskRead(key, this.snapshotVersion);
183
188
  }
184
- _diskWrite(key, value, version) {
185
- if (this.strategy.exists(key)) {
186
- const oldValue = this.strategy.read(key);
187
- if (!this.deletedCache.has(key)) {
188
- this.deletedCache.set(key, []);
189
+ _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
190
+ if (this.writeBuffer.has(key)) {
191
+ const keyModVersion = this.keyVersions.get(key);
192
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
193
+ return this.writeBuffer.get(key);
189
194
  }
190
- this.deletedCache.get(key).push({ value: oldValue, deletedAtVersion: version });
191
195
  }
192
- this.strategy.write(key, value);
193
- if (!this.versionIndex.has(key)) {
194
- this.versionIndex.set(key, []);
196
+ if (this.deleteBuffer.has(key)) {
197
+ const keyModVersion = this.keyVersions.get(key);
198
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
199
+ return null;
200
+ }
201
+ }
202
+ if (this.parent) {
203
+ return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
204
+ } else {
205
+ return this._diskRead(key, snapshotVersion);
195
206
  }
196
- this.versionIndex.get(key).push({ version, exists: true });
197
207
  }
198
- _diskRead(key, snapshotVersion) {
199
- if (!this.versionIndex.has(key) && !this.deletedCache.has(key)) {
200
- if (this.strategy.exists(key)) {
201
- return this.strategy.read(key);
208
+ commit() {
209
+ if (this.committed) {
210
+ return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
211
+ }
212
+ const created = [];
213
+ const updated = [];
214
+ for (const [key, data] of this.writeBuffer.entries()) {
215
+ if (this.createdKeys.has(key)) {
216
+ created.push({ key, data });
217
+ } else {
218
+ updated.push({ key, data });
202
219
  }
203
- return null;
204
220
  }
205
- const versions = this.versionIndex.get(key);
206
- if (versions && versions.length > 0) {
207
- const visibleVersions = versions.filter((v) => v.version <= snapshotVersion && v.exists);
208
- if (visibleVersions.length > 0) {
209
- const newerVersions = versions.filter((v) => v.version > snapshotVersion);
210
- if (newerVersions.length === 0) {
211
- return this.strategy.read(key);
221
+ const deleted = [];
222
+ for (const key of this.deleteBuffer) {
223
+ const data = this.deletedValues.get(key);
224
+ if (data !== void 0) {
225
+ deleted.push({ key, data });
226
+ }
227
+ }
228
+ if (this.parent) {
229
+ const error = this.parent._merge(this);
230
+ if (error) {
231
+ return { success: false, error, created: [], updated: [], deleted: [] };
232
+ }
233
+ this.committed = true;
234
+ } else {
235
+ if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
236
+ const error = this._merge(this);
237
+ if (error) {
238
+ return { success: false, error, created: [], updated: [], deleted: [] };
212
239
  }
240
+ this.writeBuffer.clear();
241
+ this.deleteBuffer.clear();
242
+ this.createdKeys.clear();
243
+ this.deletedValues.clear();
244
+ this.keyVersions.clear();
245
+ this.localVersion = 0;
213
246
  }
214
247
  }
215
- const cached = this.deletedCache.get(key);
216
- if (cached) {
217
- const visibleEntries = cached.filter((v) => v.deletedAtVersion > snapshotVersion);
218
- if (visibleEntries.length > 0) {
219
- visibleEntries.sort((a, b) => a.deletedAtVersion - b.deletedAtVersion);
220
- return visibleEntries[0].value;
248
+ return { success: true, created, updated, deleted };
249
+ }
250
+ _merge(child) {
251
+ if (this.parent) {
252
+ for (const key of child.writeBuffer.keys()) {
253
+ const lastModLocalVer = this.keyVersions.get(key);
254
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
255
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
256
+ }
257
+ }
258
+ for (const key of child.deleteBuffer) {
259
+ const lastModLocalVer = this.keyVersions.get(key);
260
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
261
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
262
+ }
263
+ }
264
+ const newLocalVersion = this.localVersion + 1;
265
+ for (const key of child.writeBuffer.keys()) {
266
+ this.writeBuffer.set(key, child.writeBuffer.get(key));
267
+ this.deleteBuffer.delete(key);
268
+ this.keyVersions.set(key, newLocalVersion);
269
+ if (child.createdKeys.has(key)) {
270
+ this.createdKeys.add(key);
271
+ }
272
+ }
273
+ for (const key of child.deleteBuffer) {
274
+ this.deleteBuffer.add(key);
275
+ this.writeBuffer.delete(key);
276
+ this.createdKeys.delete(key);
277
+ this.keyVersions.set(key, newLocalVersion);
278
+ const deletedValue = child.deletedValues.get(key);
279
+ if (deletedValue !== void 0) {
280
+ this.deletedValues.set(key, deletedValue);
281
+ }
282
+ }
283
+ this.localVersion = newLocalVersion;
284
+ this.root.activeTransactions.delete(child);
285
+ } else {
286
+ this.root.activeTransactions.delete(child);
287
+ const newVersion = this.version + 1;
288
+ const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
289
+ for (const key of modifiedKeys) {
290
+ const versions = this.versionIndex.get(key);
291
+ if (versions && versions.length > 0) {
292
+ const lastVer = versions[versions.length - 1].version;
293
+ if (lastVer > child.snapshotVersion) {
294
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`;
295
+ }
296
+ }
221
297
  }
298
+ for (const [key, value] of child.writeBuffer) {
299
+ this._diskWrite(key, value, newVersion);
300
+ }
301
+ for (const key of child.deleteBuffer) {
302
+ this._diskDelete(key, newVersion);
303
+ }
304
+ this.version = newVersion;
305
+ this._cleanupDeletedCache();
222
306
  }
223
307
  return null;
224
308
  }
225
- _diskDelete(key, snapshotVersion) {
226
- if (this.strategy.exists(key)) {
227
- const data = this.strategy.read(key);
228
- if (!this.deletedCache.has(key)) {
229
- this.deletedCache.set(key, []);
309
+ // --- Internal IO Helpers (Root Only) ---
310
+ _diskWrite(key, value, version) {
311
+ const strategy = this.strategy;
312
+ if (!strategy) throw new Error("Root Transaction missing strategy");
313
+ if (strategy.exists(key)) {
314
+ const currentVal = strategy.read(key);
315
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
316
+ this.deletedCache.get(key).push({
317
+ value: currentVal,
318
+ deletedAtVersion: version
319
+ });
320
+ }
321
+ strategy.write(key, value);
322
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
323
+ this.versionIndex.get(key).push({ version, exists: true });
324
+ }
325
+ _diskRead(key, snapshotVersion) {
326
+ const strategy = this.strategy;
327
+ if (!strategy) throw new Error("Root Transaction missing strategy");
328
+ const versions = this.versionIndex.get(key);
329
+ if (!versions) {
330
+ return strategy.exists(key) ? strategy.read(key) : null;
331
+ }
332
+ let targetVerObj = null;
333
+ let nextVerObj = null;
334
+ for (const v of versions) {
335
+ if (v.version <= snapshotVersion) {
336
+ targetVerObj = v;
337
+ } else {
338
+ nextVerObj = v;
339
+ break;
230
340
  }
231
- this.deletedCache.get(key).push({ deletedAtVersion: snapshotVersion, value: data });
232
- this.strategy.delete(key);
233
341
  }
234
- if (!this.versionIndex.has(key)) {
235
- this.versionIndex.set(key, []);
342
+ if (!targetVerObj || !targetVerObj.exists) return null;
343
+ if (!nextVerObj) {
344
+ return strategy.read(key);
345
+ }
346
+ const cached = this.deletedCache.get(key);
347
+ if (cached) {
348
+ const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
349
+ if (match) return match.value;
236
350
  }
351
+ return null;
352
+ }
353
+ _diskDelete(key, snapshotVersion) {
354
+ const strategy = this.strategy;
355
+ if (!strategy) throw new Error("Root Transaction missing strategy");
356
+ if (strategy.exists(key)) {
357
+ const currentVal = strategy.read(key);
358
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
359
+ this.deletedCache.get(key).push({
360
+ value: currentVal,
361
+ deletedAtVersion: snapshotVersion
362
+ });
363
+ }
364
+ strategy.delete(key);
365
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
237
366
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
238
367
  }
239
- _commit(tx) {
240
- const isReadOnly = tx.writeBuffer.size === 0 && tx.deleteBuffer.size === 0;
241
- if (!isReadOnly && this.version > tx.snapshotVersion) {
242
- const affectedKeys = /* @__PURE__ */ new Set([
243
- ...tx.writeBuffer.keys(),
244
- ...tx.deleteBuffer
245
- ]);
246
- for (const key of affectedKeys) {
247
- const versions = this.versionIndex.get(key);
248
- if (versions && versions.length > 0) {
249
- const latestVersion = versions[versions.length - 1].version;
250
- if (latestVersion > tx.snapshotVersion) {
251
- throw new Error(`Commit conflict: file '${key}' was modified by another transaction`);
252
- }
368
+ _cleanupDeletedCache() {
369
+ if (this.deletedCache.size === 0) return;
370
+ let minActiveVersion = this.version;
371
+ if (this.activeTransactions.size > 0) {
372
+ for (const tx of this.activeTransactions) {
373
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
374
+ minActiveVersion = tx.snapshotVersion;
253
375
  }
254
376
  }
255
377
  }
256
- this.version++;
257
- for (const key of tx.deleteBuffer) {
258
- this._diskDelete(key, this.version);
259
- }
260
- for (const [key, value] of tx.writeBuffer) {
261
- this._diskWrite(key, value, this.version);
378
+ for (const [key, cachedList] of this.deletedCache) {
379
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
380
+ if (remaining.length === 0) {
381
+ this.deletedCache.delete(key);
382
+ } else {
383
+ this.deletedCache.set(key, remaining);
384
+ }
262
385
  }
263
386
  }
264
387
  };
265
388
 
266
- // src/core/sync/Strategy.ts
267
- var SyncMVCCStrategy = class extends MVCCStrategy {
389
+ // src/core/async/Strategy.ts
390
+ var AsyncMVCCStrategy = class extends MVCCStrategy {
268
391
  };
269
392
 
270
393
  // node_modules/ryoiki/dist/esm/index.mjs
@@ -526,140 +649,261 @@ var Ryoiki = class _Ryoiki {
526
649
  };
527
650
 
528
651
  // src/core/async/Transaction.ts
529
- var AsyncMVCCTransaction = class extends MVCCTransaction {
652
+ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
653
+ lock = new Ryoiki();
654
+ async writeLock(fn) {
655
+ let lockId;
656
+ return this.lock.writeLock(async (_lockId) => {
657
+ lockId = _lockId;
658
+ return fn();
659
+ }).finally(() => {
660
+ this.lock.writeUnlock(lockId);
661
+ });
662
+ }
663
+ async create(key, value) {
664
+ if (this.committed) throw new Error("Transaction already committed");
665
+ if (this.writeBuffer.has(key) || !this.deleteBuffer.has(key) && await this.read(key) !== null) {
666
+ throw new Error(`Key already exists: ${key}`);
667
+ }
668
+ this._bufferCreate(key, value);
669
+ return this;
670
+ }
671
+ async write(key, value) {
672
+ if (this.committed) throw new Error("Transaction already committed");
673
+ if (!this.writeBuffer.has(key) && (this.deleteBuffer.has(key) || await this.read(key) === null)) {
674
+ throw new Error(`Key not found: ${key}`);
675
+ }
676
+ this._bufferWrite(key, value);
677
+ return this;
678
+ }
679
+ async delete(key) {
680
+ if (this.committed) throw new Error("Transaction already committed");
681
+ let valueToDelete = null;
682
+ if (this.writeBuffer.has(key)) {
683
+ valueToDelete = this.writeBuffer.get(key);
684
+ } else if (!this.deleteBuffer.has(key)) {
685
+ valueToDelete = await this.read(key);
686
+ }
687
+ if (valueToDelete === null) {
688
+ throw new Error(`Key not found: ${key}`);
689
+ }
690
+ this.deletedValues.set(key, valueToDelete);
691
+ this._bufferDelete(key);
692
+ return this;
693
+ }
694
+ createNested() {
695
+ if (this.committed) throw new Error("Transaction already committed");
696
+ const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
697
+ const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
698
+ this.root.activeTransactions.add(child);
699
+ return child;
700
+ }
530
701
  async read(key) {
531
702
  if (this.committed) throw new Error("Transaction already committed");
703
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
704
+ if (this.deleteBuffer.has(key)) return null;
705
+ return this.root._diskRead(key, this.snapshotVersion);
706
+ }
707
+ async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
532
708
  if (this.writeBuffer.has(key)) {
533
- return this.writeBuffer.get(key);
709
+ const keyModVersion = this.keyVersions.get(key);
710
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
711
+ return this.writeBuffer.get(key);
712
+ }
534
713
  }
535
714
  if (this.deleteBuffer.has(key)) {
536
- return null;
715
+ const keyModVersion = this.keyVersions.get(key);
716
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
717
+ return null;
718
+ }
719
+ }
720
+ if (this.parent) {
721
+ return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
722
+ } else {
723
+ return this._diskRead(key, snapshotVersion);
537
724
  }
538
- return this.manager._diskRead(key, this.snapshotVersion);
539
725
  }
540
726
  async commit() {
541
- return this.manager.writeLock(async () => {
542
- if (this.committed) throw new Error("Transaction already committed");
543
- await this.manager._commit(this);
544
- this.committed = true;
545
- this.manager._removeTransaction(this);
546
- return this;
727
+ return this.writeLock(async () => {
728
+ if (this.committed) {
729
+ return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
730
+ }
731
+ const created = [];
732
+ const updated = [];
733
+ for (const [key, data] of this.writeBuffer.entries()) {
734
+ if (this.createdKeys.has(key)) {
735
+ created.push({ key, data });
736
+ } else {
737
+ updated.push({ key, data });
738
+ }
739
+ }
740
+ const deleted = [];
741
+ for (const key of this.deleteBuffer) {
742
+ const data = this.deletedValues.get(key);
743
+ if (data !== void 0) {
744
+ deleted.push({ key, data });
745
+ }
746
+ }
747
+ if (this.parent) {
748
+ const error = await this.parent._merge(this);
749
+ if (error) {
750
+ return { success: false, error, created: [], updated: [], deleted: [] };
751
+ }
752
+ this.committed = true;
753
+ } else {
754
+ if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
755
+ const error = await this._merge(this);
756
+ if (error) {
757
+ return { success: false, error, created: [], updated: [], deleted: [] };
758
+ }
759
+ this.writeBuffer.clear();
760
+ this.deleteBuffer.clear();
761
+ this.createdKeys.clear();
762
+ this.deletedValues.clear();
763
+ this.keyVersions.clear();
764
+ this.localVersion = 0;
765
+ }
766
+ }
767
+ return { success: true, created, updated, deleted };
547
768
  });
548
769
  }
549
- };
550
-
551
- // src/core/async/Manager.ts
552
- var AsyncMVCCManager = class extends MVCCManager {
553
- lock;
554
- constructor(strategy) {
555
- super(strategy);
556
- this.lock = new Ryoiki();
557
- }
558
- createTransaction() {
559
- const tx = new AsyncMVCCTransaction(this, this.version);
560
- this.activeTransactions.add(tx);
561
- return tx;
562
- }
563
- async writeLock(fn) {
564
- let lockId;
565
- return this.lock.writeLock(async (_lockId) => {
566
- lockId = _lockId;
567
- return fn();
568
- }).finally(() => {
569
- this.lock.writeUnlock(lockId);
770
+ async _merge(child) {
771
+ return this.writeLock(async () => {
772
+ if (this.parent) {
773
+ for (const key of child.writeBuffer.keys()) {
774
+ const lastModLocalVer = this.keyVersions.get(key);
775
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
776
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
777
+ }
778
+ }
779
+ for (const key of child.deleteBuffer) {
780
+ const lastModLocalVer = this.keyVersions.get(key);
781
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
782
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
783
+ }
784
+ }
785
+ const newLocalVersion = this.localVersion + 1;
786
+ for (const key of child.writeBuffer.keys()) {
787
+ this.writeBuffer.set(key, child.writeBuffer.get(key));
788
+ this.deleteBuffer.delete(key);
789
+ this.keyVersions.set(key, newLocalVersion);
790
+ if (child.createdKeys.has(key)) {
791
+ this.createdKeys.add(key);
792
+ }
793
+ }
794
+ for (const key of child.deleteBuffer) {
795
+ this.deleteBuffer.add(key);
796
+ this.writeBuffer.delete(key);
797
+ this.createdKeys.delete(key);
798
+ this.keyVersions.set(key, newLocalVersion);
799
+ const deletedValue = child.deletedValues.get(key);
800
+ if (deletedValue !== void 0) {
801
+ this.deletedValues.set(key, deletedValue);
802
+ }
803
+ }
804
+ this.localVersion = newLocalVersion;
805
+ this.root.activeTransactions.delete(child);
806
+ } else {
807
+ this.root.activeTransactions.delete(child);
808
+ const newVersion = this.version + 1;
809
+ const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
810
+ for (const key of modifiedKeys) {
811
+ const versions = this.versionIndex.get(key);
812
+ if (versions && versions.length > 0) {
813
+ const lastVer = versions[versions.length - 1].version;
814
+ if (lastVer > child.snapshotVersion) {
815
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`;
816
+ }
817
+ }
818
+ }
819
+ for (const [key, value] of child.writeBuffer) {
820
+ await this._diskWrite(key, value, newVersion);
821
+ }
822
+ for (const key of child.deleteBuffer) {
823
+ await this._diskDelete(key, newVersion);
824
+ }
825
+ this.version = newVersion;
826
+ this._cleanupDeletedCache();
827
+ }
828
+ return null;
570
829
  });
571
830
  }
831
+ // --- Internal IO Helpers (Root Only) ---
572
832
  async _diskWrite(key, value, version) {
573
- if (await this.strategy.exists(key)) {
574
- const oldValue = await this.strategy.read(key);
575
- if (!this.deletedCache.has(key)) {
576
- this.deletedCache.set(key, []);
577
- }
578
- this.deletedCache.get(key).push({ value: oldValue, deletedAtVersion: version });
579
- }
580
- await this.strategy.write(key, value);
581
- if (!this.versionIndex.has(key)) {
582
- this.versionIndex.set(key, []);
583
- }
833
+ const strategy = this.strategy;
834
+ if (!strategy) throw new Error("Root Transaction missing strategy");
835
+ if (await strategy.exists(key)) {
836
+ const currentVal = await strategy.read(key);
837
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
838
+ this.deletedCache.get(key).push({
839
+ value: currentVal,
840
+ deletedAtVersion: version
841
+ });
842
+ }
843
+ await strategy.write(key, value);
844
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
584
845
  this.versionIndex.get(key).push({ version, exists: true });
585
846
  }
586
847
  async _diskRead(key, snapshotVersion) {
587
- if (!this.versionIndex.has(key) && !this.deletedCache.has(key)) {
588
- if (await this.strategy.exists(key)) {
589
- return this.strategy.read(key);
590
- }
591
- return null;
592
- }
848
+ const strategy = this.strategy;
849
+ if (!strategy) throw new Error("Root Transaction missing strategy");
593
850
  const versions = this.versionIndex.get(key);
594
- if (versions && versions.length > 0) {
595
- const visibleVersions = versions.filter((v) => v.version <= snapshotVersion && v.exists);
596
- if (visibleVersions.length > 0) {
597
- const newerVersions = versions.filter((v) => v.version > snapshotVersion);
598
- if (newerVersions.length === 0) {
599
- return this.strategy.read(key);
600
- }
851
+ if (!versions) {
852
+ return await strategy.exists(key) ? strategy.read(key) : null;
853
+ }
854
+ let targetVerObj = null;
855
+ let nextVerObj = null;
856
+ for (const v of versions) {
857
+ if (v.version <= snapshotVersion) {
858
+ targetVerObj = v;
859
+ } else {
860
+ nextVerObj = v;
861
+ break;
601
862
  }
602
863
  }
864
+ if (!targetVerObj || !targetVerObj.exists) return null;
865
+ if (!nextVerObj) {
866
+ return strategy.read(key);
867
+ }
603
868
  const cached = this.deletedCache.get(key);
604
869
  if (cached) {
605
- const visibleEntries = cached.filter((v) => v.deletedAtVersion > snapshotVersion);
606
- if (visibleEntries.length > 0) {
607
- visibleEntries.sort((a, b) => a.deletedAtVersion - b.deletedAtVersion);
608
- return visibleEntries[0].value;
609
- }
870
+ const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
871
+ if (match) return match.value;
610
872
  }
611
873
  return null;
612
874
  }
613
875
  async _diskDelete(key, snapshotVersion) {
614
- if (await this.strategy.exists(key)) {
615
- const data = await this.strategy.read(key);
616
- if (!this.deletedCache.has(key)) {
617
- this.deletedCache.set(key, []);
618
- }
619
- this.deletedCache.get(key).push({ deletedAtVersion: snapshotVersion, value: data });
620
- await this.strategy.delete(key);
621
- }
622
- if (!this.versionIndex.has(key)) {
623
- this.versionIndex.set(key, []);
624
- }
876
+ const strategy = this.strategy;
877
+ if (!strategy) throw new Error("Root Transaction missing strategy");
878
+ if (await strategy.exists(key)) {
879
+ const currentVal = await strategy.read(key);
880
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
881
+ this.deletedCache.get(key).push({
882
+ value: currentVal,
883
+ deletedAtVersion: snapshotVersion
884
+ });
885
+ }
886
+ await strategy.delete(key);
887
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
625
888
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
626
889
  }
627
- async _commit(tx) {
628
- const isReadOnly = tx.writeBuffer.size === 0 && tx.deleteBuffer.size === 0;
629
- if (!isReadOnly && this.version > tx.snapshotVersion) {
630
- const affectedKeys = /* @__PURE__ */ new Set([
631
- ...tx.writeBuffer.keys(),
632
- ...tx.deleteBuffer
633
- ]);
634
- for (const key of affectedKeys) {
635
- const versions = this.versionIndex.get(key);
636
- if (versions && versions.length > 0) {
637
- const latestVersion = versions[versions.length - 1].version;
638
- if (latestVersion > tx.snapshotVersion) {
639
- throw new Error(`Commit conflict: file '${key}' was modified by another transaction`);
640
- }
890
+ _cleanupDeletedCache() {
891
+ if (this.deletedCache.size === 0) return;
892
+ let minActiveVersion = this.version;
893
+ if (this.activeTransactions.size > 0) {
894
+ for (const tx of this.activeTransactions) {
895
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
896
+ minActiveVersion = tx.snapshotVersion;
641
897
  }
642
898
  }
643
899
  }
644
- this.version++;
645
- for (const key of tx.deleteBuffer) {
646
- await this._diskDelete(key, this.version);
647
- }
648
- for (const [key, value] of tx.writeBuffer) {
649
- await this._diskWrite(key, value, this.version);
900
+ for (const [key, cachedList] of this.deletedCache) {
901
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
902
+ if (remaining.length === 0) {
903
+ this.deletedCache.delete(key);
904
+ } else {
905
+ this.deletedCache.set(key, remaining);
906
+ }
650
907
  }
651
908
  }
652
909
  };
653
-
654
- // src/core/async/Strategy.ts
655
- var AsyncMVCCStrategy = class extends MVCCStrategy {
656
- };
657
- // Annotate the CommonJS export names for ESM import in node:
658
- 0 && (module.exports = {
659
- AsyncMVCCManager,
660
- AsyncMVCCStrategy,
661
- AsyncMVCCTransaction,
662
- SyncMVCCManager,
663
- SyncMVCCStrategy,
664
- SyncMVCCTransaction
665
- });