dataply 0.0.1 → 0.0.3

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/cjs/index.js CHANGED
@@ -36,6 +36,7 @@ __export(src_exports, {
36
36
  CacheEntanglementSync: () => CacheEntanglementSync2,
37
37
  Dataply: () => Dataply,
38
38
  DataplyAPI: () => DataplyAPI,
39
+ GlobalTransaction: () => GlobalTransaction,
39
40
  InMemoryStoreStrategyAsync: () => InMemoryStoreStrategyAsync,
40
41
  InMemoryStoreStrategySync: () => InMemoryStoreStrategySync,
41
42
  InvertedWeakMap: () => InvertedWeakMap,
@@ -2906,6 +2907,320 @@ var InvertedWeakMap = class {
2906
2907
  // src/core/DataplyAPI.ts
2907
2908
  var import_node_fs3 = __toESM(require("node:fs"));
2908
2909
 
2910
+ // node_modules/hookall/dist/esm/index.mjs
2911
+ var HookallStore = class extends WeakMap {
2912
+ ensure(obj, key) {
2913
+ if (!this.has(obj)) {
2914
+ const scope2 = {};
2915
+ this.set(obj, scope2);
2916
+ }
2917
+ const scope = this.get(obj);
2918
+ if (!Object.prototype.hasOwnProperty.call(scope, key)) {
2919
+ scope[key] = /* @__PURE__ */ new Map();
2920
+ }
2921
+ return scope[key];
2922
+ }
2923
+ };
2924
+ var Hookall = class _Hookall {
2925
+ static Global = {};
2926
+ static _Store = new HookallStore();
2927
+ beforeHooks;
2928
+ afterHooks;
2929
+ /**
2930
+ * Create hook system. you can pass a target object or undefined.
2931
+ * If you pass a object, the hook system will be work for object locally. You're going to want this kind of usage in general.
2932
+ * If not specified, will be work for global. This is useful when you want to share your work with multiple files.
2933
+ * @param target The object to work with locally. If not specified, will be work for global.
2934
+ */
2935
+ constructor(target) {
2936
+ this.beforeHooks = _Hookall._Store.ensure(target, "before");
2937
+ this.afterHooks = _Hookall._Store.ensure(target, "after");
2938
+ }
2939
+ _ensureCommand(hooks, command) {
2940
+ if (!hooks.has(command)) {
2941
+ hooks.set(command, []);
2942
+ }
2943
+ return hooks.get(command);
2944
+ }
2945
+ _createWrapper(command, callback, repeat) {
2946
+ return {
2947
+ callback,
2948
+ command,
2949
+ repeat
2950
+ };
2951
+ }
2952
+ _on(hooks, command, callback, repeat) {
2953
+ const wrappers = this._ensureCommand(hooks, command);
2954
+ const wrapper = this._createWrapper(command, callback, repeat);
2955
+ wrappers.unshift(wrapper);
2956
+ }
2957
+ /**
2958
+ * You register a preprocessing function, which is called before the callback function of the `trigger` method.
2959
+ * The value returned by this function is passed as a parameter to the `trigger` method's callback function.
2960
+ * If you register multiple preprocessing functions, they are executed in order, with each function receiving the value returned by the previous one as a parameter.
2961
+ * @param command Command to work.
2962
+ * @param callback Preprocessing function to register.
2963
+ */
2964
+ onBefore(command, callback) {
2965
+ this._on(this.beforeHooks, command, callback, -1);
2966
+ return this;
2967
+ }
2968
+ /**
2969
+ * Similar to the `onBefore` method, but it only runs once.
2970
+ * For more details, please refer to the `onBefore` method.
2971
+ * @param command Command to work.
2972
+ * @param callback Preprocessing function to register.
2973
+ */
2974
+ onceBefore(command, callback) {
2975
+ this._on(this.beforeHooks, command, callback, 1);
2976
+ return this;
2977
+ }
2978
+ /**
2979
+ * You register a post-processing function which is called after the callback function of the `trigger` method finishes.
2980
+ * This function receives the value returned by the `trigger` method's callback function as a parameter.
2981
+ * If you register multiple post-processing functions, they are executed in order, with each function receiving the value returned by the previous one as a parameter.
2982
+ * @param command Command to work.
2983
+ * @param callback Post-preprocessing function to register.
2984
+ */
2985
+ onAfter(command, callback) {
2986
+ this._on(this.afterHooks, command, callback, -1);
2987
+ return this;
2988
+ }
2989
+ /**
2990
+ * Similar to the `onAfter` method, but it only runs once.
2991
+ * For more details, please refer to the `onAfter` method.
2992
+ * @param command Command to work.
2993
+ * @param callback Post-preprocessing function to register.
2994
+ */
2995
+ onceAfter(command, callback) {
2996
+ this._on(this.afterHooks, command, callback, 1);
2997
+ return this;
2998
+ }
2999
+ _off(hooks, command, callback) {
3000
+ const wrappers = this._ensureCommand(hooks, command);
3001
+ if (callback) {
3002
+ const i = wrappers.findIndex((wrapper) => wrapper.callback === callback);
3003
+ if (i !== -1) {
3004
+ wrappers.splice(i, 1);
3005
+ }
3006
+ } else {
3007
+ wrappers.length = 0;
3008
+ }
3009
+ return this;
3010
+ }
3011
+ /**
3012
+ * You remove the preprocessing functions registered with `onBefore` or `onceBefore` methods.
3013
+ * If you don't specify a callback parameter, it removes all preprocessing functions registered for that command.
3014
+ * @param command Commands with preprocessing functions to be deleted.
3015
+ * @param callback Preprocessing function to be deleted.
3016
+ */
3017
+ offBefore(command, callback) {
3018
+ this._off(this.beforeHooks, command, callback);
3019
+ return this;
3020
+ }
3021
+ /**
3022
+ * You remove the post-preprocessing functions registered with `onAfter` or `onceAfter` methods.
3023
+ * If you don't specify a callback parameter, it removes all post-preprocessing functions registered for that command.
3024
+ * @param command Commands with post-preprocessing functions to be deleted.
3025
+ * @param callback post-Preprocessing function to be deleted.
3026
+ */
3027
+ offAfter(command, callback) {
3028
+ this._off(this.afterHooks, command, callback);
3029
+ return this;
3030
+ }
3031
+ async _hookWith(hooks, command, value, ...params) {
3032
+ let wrappers = this._ensureCommand(hooks, command);
3033
+ let i = wrappers.length;
3034
+ while (i--) {
3035
+ const wrapper = wrappers[i];
3036
+ value = await wrapper.callback(value, ...params);
3037
+ wrapper.repeat -= 1;
3038
+ if (wrapper.repeat === 0) {
3039
+ this._off(hooks, command, wrapper.callback);
3040
+ }
3041
+ }
3042
+ return value;
3043
+ }
3044
+ /**
3045
+ * You execute the callback function provided as a parameter. This callback function receives the 'initialValue' parameter.
3046
+ *
3047
+ * If preprocessing functions are registered, they run first, and the value returned by the preprocessing functions becomes the 'initialValue' parameter.
3048
+ * After the callback function finishes, post-processing functions are called.
3049
+ * These post-processing functions receive the value returned by the callback function as a parameter and run sequentially.
3050
+ *
3051
+ * The final value returned becomes the result of the `trigger` method.
3052
+ * @param command Command to work.
3053
+ * @param initialValue Initial value to be passed to the callback function.
3054
+ * @param callback The callback function to be executed.
3055
+ */
3056
+ async trigger(command, initialValue, callback, ...params) {
3057
+ let value;
3058
+ value = await this._hookWith(this.beforeHooks, command, initialValue, ...params);
3059
+ value = await callback(value, ...params);
3060
+ value = await this._hookWith(this.afterHooks, command, value, ...params);
3061
+ return value;
3062
+ }
3063
+ };
3064
+ function useHookall(target = Hookall.Global) {
3065
+ return new Hookall(target);
3066
+ }
3067
+ var HookallStore2 = class extends WeakMap {
3068
+ ensure(obj, key) {
3069
+ if (!this.has(obj)) {
3070
+ const scope2 = {};
3071
+ this.set(obj, scope2);
3072
+ }
3073
+ const scope = this.get(obj);
3074
+ if (!Object.prototype.hasOwnProperty.call(scope, key)) {
3075
+ scope[key] = /* @__PURE__ */ new Map();
3076
+ }
3077
+ return scope[key];
3078
+ }
3079
+ };
3080
+ var HookallSync = class _HookallSync {
3081
+ static Global = {};
3082
+ static _Store = new HookallStore2();
3083
+ beforeHooks;
3084
+ afterHooks;
3085
+ /**
3086
+ * Create hook system. you can pass a target object or undefined.
3087
+ * If you pass a object, the hook system will be work for object locally. You're going to want this kind of usage in general.
3088
+ * If not specified, will be work for global. This is useful when you want to share your work with multiple files.
3089
+ * @param target The object to work with locally. If not specified, will be work for global.
3090
+ */
3091
+ constructor(target) {
3092
+ this.beforeHooks = _HookallSync._Store.ensure(target, "before");
3093
+ this.afterHooks = _HookallSync._Store.ensure(target, "after");
3094
+ }
3095
+ _ensureCommand(hooks, command) {
3096
+ if (!hooks.has(command)) {
3097
+ hooks.set(command, []);
3098
+ }
3099
+ return hooks.get(command);
3100
+ }
3101
+ _createWrapper(command, callback, repeat) {
3102
+ return {
3103
+ callback,
3104
+ command,
3105
+ repeat
3106
+ };
3107
+ }
3108
+ _on(hooks, command, callback, repeat) {
3109
+ const wrappers = this._ensureCommand(hooks, command);
3110
+ const wrapper = this._createWrapper(command, callback, repeat);
3111
+ wrappers.unshift(wrapper);
3112
+ }
3113
+ /**
3114
+ * You register a preprocessing function, which is called before the callback function of the `trigger` method.
3115
+ * The value returned by this function is passed as a parameter to the `trigger` method's callback function.
3116
+ * If you register multiple preprocessing functions, they are executed in order, with each function receiving the value returned by the previous one as a parameter.
3117
+ * @param command Command to work.
3118
+ * @param callback Preprocessing function to register.
3119
+ */
3120
+ onBefore(command, callback) {
3121
+ this._on(this.beforeHooks, command, callback, -1);
3122
+ return this;
3123
+ }
3124
+ /**
3125
+ * Similar to the `onBefore` method, but it only runs once.
3126
+ * For more details, please refer to the `onBefore` method.
3127
+ * @param command Command to work.
3128
+ * @param callback Preprocessing function to register.
3129
+ */
3130
+ onceBefore(command, callback) {
3131
+ this._on(this.beforeHooks, command, callback, 1);
3132
+ return this;
3133
+ }
3134
+ /**
3135
+ * You register a post-processing function which is called after the callback function of the `trigger` method finishes.
3136
+ * This function receives the value returned by the `trigger` method's callback function as a parameter.
3137
+ * If you register multiple post-processing functions, they are executed in order, with each function receiving the value returned by the previous one as a parameter.
3138
+ * @param command Command to work.
3139
+ * @param callback Post-preprocessing function to register.
3140
+ */
3141
+ onAfter(command, callback) {
3142
+ this._on(this.afterHooks, command, callback, -1);
3143
+ return this;
3144
+ }
3145
+ /**
3146
+ * Similar to the `onAfter` method, but it only runs once.
3147
+ * For more details, please refer to the `onAfter` method.
3148
+ * @param command Command to work.
3149
+ * @param callback Post-preprocessing function to register.
3150
+ */
3151
+ onceAfter(command, callback) {
3152
+ this._on(this.afterHooks, command, callback, 1);
3153
+ return this;
3154
+ }
3155
+ _off(hooks, command, callback) {
3156
+ const wrappers = this._ensureCommand(hooks, command);
3157
+ if (callback) {
3158
+ const i = wrappers.findIndex((wrapper) => wrapper.callback === callback);
3159
+ if (i !== -1) {
3160
+ wrappers.splice(i, 1);
3161
+ }
3162
+ } else {
3163
+ wrappers.length = 0;
3164
+ }
3165
+ return this;
3166
+ }
3167
+ /**
3168
+ * You remove the preprocessing functions registered with `onBefore` or `onceBefore` methods.
3169
+ * If you don't specify a callback parameter, it removes all preprocessing functions registered for that command.
3170
+ * @param command Commands with preprocessing functions to be deleted.
3171
+ * @param callback Preprocessing function to be deleted.
3172
+ */
3173
+ offBefore(command, callback) {
3174
+ this._off(this.beforeHooks, command, callback);
3175
+ return this;
3176
+ }
3177
+ /**
3178
+ * You remove the post-preprocessing functions registered with `onAfter` or `onceAfter` methods.
3179
+ * If you don't specify a callback parameter, it removes all post-preprocessing functions registered for that command.
3180
+ * @param command Commands with post-preprocessing functions to be deleted.
3181
+ * @param callback post-Preprocessing function to be deleted.
3182
+ */
3183
+ offAfter(command, callback) {
3184
+ this._off(this.afterHooks, command, callback);
3185
+ return this;
3186
+ }
3187
+ _hookWith(hooks, command, value, ...params) {
3188
+ let wrappers = this._ensureCommand(hooks, command);
3189
+ let i = wrappers.length;
3190
+ while (i--) {
3191
+ const wrapper = wrappers[i];
3192
+ value = wrapper.callback(value, ...params);
3193
+ wrapper.repeat -= 1;
3194
+ if (wrapper.repeat === 0) {
3195
+ this._off(hooks, command, wrapper.callback);
3196
+ }
3197
+ }
3198
+ return value;
3199
+ }
3200
+ /**
3201
+ * You execute the callback function provided as a parameter. This callback function receives the 'initialValue' parameter.
3202
+ *
3203
+ * If preprocessing functions are registered, they run first, and the value returned by the preprocessing functions becomes the 'initialValue' parameter.
3204
+ * After the callback function finishes, post-processing functions are called.
3205
+ * These post-processing functions receive the value returned by the callback function as a parameter and run sequentially.
3206
+ *
3207
+ * The final value returned becomes the result of the `trigger` method.
3208
+ * @param command Command to work.
3209
+ * @param initialValue Initial value to be passed to the callback function.
3210
+ * @param callback The callback function to be executed.
3211
+ */
3212
+ trigger(command, initialValue, callback, ...params) {
3213
+ let value;
3214
+ value = this._hookWith(this.beforeHooks, command, initialValue, ...params);
3215
+ value = callback(value, ...params);
3216
+ value = this._hookWith(this.afterHooks, command, value, ...params);
3217
+ return value;
3218
+ }
3219
+ };
3220
+ function useHookallSync(target = HookallSync.Global) {
3221
+ return new HookallSync(target);
3222
+ }
3223
+
2909
3224
  // src/utils/numberToBytes.ts
2910
3225
  function numberToBytes(value, buffer, offset = 0, length = buffer.length) {
2911
3226
  if (length === 4) {
@@ -3776,11 +4091,16 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
3776
4091
  OFFSET_ROOT_INDEX_ORDER: 126,
3777
4092
  OFFSET_LAST_INSERT_PAGE_ID: 130,
3778
4093
  OFFSET_LAST_ROW_PK: 134,
4094
+ OFFSET_BITMAP_PAGE_ID: 140,
4095
+ OFFSET_FREE_PAGE_ID: 144,
3779
4096
  SIZE_PAGE_COUNT: 4,
3780
4097
  SIZE_PAGE_SIZE: 4,
3781
4098
  SIZE_ROOT_INDEX_PAGE_ID: 4,
3782
4099
  SIZE_ROOT_INDEX_ORDER: 4,
3783
- SIZE_LAST_INSERT_PAGE_ID: 4
4100
+ SIZE_LAST_INSERT_PAGE_ID: 4,
4101
+ SIZE_ROW_PK: 6,
4102
+ SIZE_BITMAP_PAGE_ID: 4,
4103
+ SIZE_FREE_PAGE_ID: 4
3784
4104
  };
3785
4105
  /**
3786
4106
  * Checks if the page type is `MetadataPage`.
@@ -3882,7 +4202,7 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
3882
4202
  return bytesToNumber(
3883
4203
  page,
3884
4204
  _MetadataPageManager.CONSTANT.OFFSET_LAST_ROW_PK,
3885
- Row.CONSTANT.SIZE_PK
4205
+ _MetadataPageManager.CONSTANT.SIZE_ROW_PK
3886
4206
  );
3887
4207
  }
3888
4208
  /**
@@ -3894,8 +4214,33 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
3894
4214
  return bytesToNumber(
3895
4215
  page,
3896
4216
  _MetadataPageManager.CONSTANT.OFFSET_ROW_COUNT,
3897
- Row.CONSTANT.SIZE_PK
4217
+ _MetadataPageManager.CONSTANT.SIZE_ROW_PK
4218
+ );
4219
+ }
4220
+ /**
4221
+ * Returns the ID of the bitmap page.
4222
+ * @param page Page data
4223
+ * @returns Bitmap page ID
4224
+ */
4225
+ getBitmapPageId(page) {
4226
+ return bytesToNumber(
4227
+ page,
4228
+ _MetadataPageManager.CONSTANT.OFFSET_BITMAP_PAGE_ID,
4229
+ _MetadataPageManager.CONSTANT.SIZE_BITMAP_PAGE_ID
4230
+ );
4231
+ }
4232
+ /**
4233
+ * Returns the ID of the free page.
4234
+ * @param page Page data
4235
+ * @returns Free page ID
4236
+ */
4237
+ getFreePageId(page) {
4238
+ const id = bytesToNumber(
4239
+ page,
4240
+ _MetadataPageManager.CONSTANT.OFFSET_FREE_PAGE_ID,
4241
+ _MetadataPageManager.CONSTANT.SIZE_FREE_PAGE_ID
3898
4242
  );
4243
+ return id === 4294967295 ? -1 : id;
3899
4244
  }
3900
4245
  /**
3901
4246
  * Sets the number of pages stored in the database.
@@ -3997,6 +4342,32 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
3997
4342
  Row.CONSTANT.SIZE_PK
3998
4343
  );
3999
4344
  }
4345
+ /**
4346
+ * Sets the ID of the bitmap page.
4347
+ * @param page Page data
4348
+ * @param bitmapPageId Bitmap page ID
4349
+ */
4350
+ setBitmapPageId(page, bitmapPageId) {
4351
+ numberToBytes(
4352
+ bitmapPageId,
4353
+ page,
4354
+ _MetadataPageManager.CONSTANT.OFFSET_BITMAP_PAGE_ID,
4355
+ _MetadataPageManager.CONSTANT.SIZE_BITMAP_PAGE_ID
4356
+ );
4357
+ }
4358
+ /**
4359
+ * Sets the ID of the free page.
4360
+ * @param page Page data
4361
+ * @param pageId Free page ID
4362
+ */
4363
+ setFreePageId(page, pageId) {
4364
+ numberToBytes(
4365
+ pageId,
4366
+ page,
4367
+ _MetadataPageManager.CONSTANT.OFFSET_FREE_PAGE_ID,
4368
+ _MetadataPageManager.CONSTANT.SIZE_FREE_PAGE_ID
4369
+ );
4370
+ }
4000
4371
  };
4001
4372
  var BitmapPageManager = class _BitmapPageManager extends PageManager {
4002
4373
  get pageType() {
@@ -4027,6 +4398,31 @@ var BitmapPageManager = class _BitmapPageManager extends PageManager {
4027
4398
  isEmptyPage(page, index) {
4028
4399
  return bytesToNumber(page, index, 1) === 0;
4029
4400
  }
4401
+ /**
4402
+ * Gets a bit from the bitmap page.
4403
+ * @param page Page data
4404
+ * @param index Bit index
4405
+ * @returns boolean indicating if the bit is set
4406
+ */
4407
+ getBit(page, index) {
4408
+ const bitOffset = Math.floor(index / 8);
4409
+ const offset = _BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER + bitOffset;
4410
+ const value = bytesToNumber(page, offset, 1);
4411
+ return getBit(value, index % 8);
4412
+ }
4413
+ /**
4414
+ * Sets a bit in the bitmap page.
4415
+ * @param page Page data
4416
+ * @param index Bit index
4417
+ * @param flag boolean indicating if the bit is set
4418
+ */
4419
+ setBit(page, index, flag) {
4420
+ const bitOffset = Math.floor(index / 8);
4421
+ const offset = _BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER + bitOffset;
4422
+ const value = bytesToNumber(page, offset, 1);
4423
+ const newValue = setBit(value, index % 8, flag);
4424
+ numberToBytes(newValue, page, offset, 1);
4425
+ }
4030
4426
  };
4031
4427
  var OverflowPageManager = class _OverflowPageManager extends PageManager {
4032
4428
  get pageType() {
@@ -4242,9 +4638,33 @@ var LogManager = class {
4242
4638
  });
4243
4639
  });
4244
4640
  }
4641
+ /**
4642
+ * Writes a commit marker to the log file.
4643
+ * This indicates that the preceding logs are part of a committed transaction.
4644
+ */
4645
+ async writeCommitMarker() {
4646
+ if (this.fd === null) {
4647
+ this.open();
4648
+ }
4649
+ this.view.setUint32(0, 4294967295, true);
4650
+ this.buffer.fill(0, 4);
4651
+ await new Promise((resolve, reject) => {
4652
+ import_node_fs.default.write(this.fd, this.buffer, 0, this.entrySize, null, (err) => {
4653
+ if (err) return reject(err);
4654
+ resolve();
4655
+ });
4656
+ });
4657
+ await new Promise((resolve, reject) => {
4658
+ import_node_fs.default.fsync(this.fd, (err) => {
4659
+ if (err) return reject(err);
4660
+ resolve();
4661
+ });
4662
+ });
4663
+ }
4245
4664
  /**
4246
4665
  * Reads the log file to recover the page map.
4247
4666
  * Runs synchronously as it is called by the VFS constructor.
4667
+ * Only returns pages from committed transactions (ended with a commit marker).
4248
4668
  * @returns Recovered page map
4249
4669
  */
4250
4670
  readAllSync() {
@@ -4254,11 +4674,19 @@ var LogManager = class {
4254
4674
  const restoredPages = /* @__PURE__ */ new Map();
4255
4675
  const currentFileSize = import_node_fs.default.fstatSync(this.fd).size;
4256
4676
  let offset = 0;
4677
+ let pendingPages = /* @__PURE__ */ new Map();
4257
4678
  while (offset + this.entrySize <= currentFileSize) {
4258
4679
  import_node_fs.default.readSync(this.fd, this.buffer, 0, this.entrySize, offset);
4259
4680
  const pageId = this.view.getUint32(0, true);
4260
- const pageData = this.buffer.slice(4, 4 + this.pageSize);
4261
- restoredPages.set(pageId, pageData);
4681
+ if (pageId === 4294967295) {
4682
+ for (const [pId, pData] of pendingPages) {
4683
+ restoredPages.set(pId, pData);
4684
+ }
4685
+ pendingPages.clear();
4686
+ } else {
4687
+ const pageData = this.buffer.slice(4, 4 + this.pageSize);
4688
+ pendingPages.set(pageId, pageData);
4689
+ }
4262
4690
  offset += this.entrySize;
4263
4691
  }
4264
4692
  return restoredPages;
@@ -4360,13 +4788,13 @@ var VirtualFileSystem = class {
4360
4788
  }
4361
4789
  }
4362
4790
  /**
4363
- * Commits the transaction.
4791
+ * Prepares the transaction for commit (Phase 1).
4792
+ * Writes dirty pages to WAL but does not update the main database file.
4364
4793
  * @param tx Transaction
4365
4794
  */
4366
- async commit(tx) {
4795
+ async prepareCommit(tx) {
4367
4796
  const dirtyPages = tx.__getDirtyPages();
4368
4797
  if (dirtyPages.size === 0) {
4369
- this.cleanupTransaction(tx);
4370
4798
  return;
4371
4799
  }
4372
4800
  const dirtyPageMap = /* @__PURE__ */ new Map();
@@ -4379,6 +4807,21 @@ var VirtualFileSystem = class {
4379
4807
  if (this.logManager && dirtyPageMap.size > 0) {
4380
4808
  await this.logManager.append(dirtyPageMap);
4381
4809
  }
4810
+ }
4811
+ /**
4812
+ * Finalizes the transaction commit (Phase 2).
4813
+ * Writes commit marker to WAL and updates the main database file (Checkpoint).
4814
+ * @param tx Transaction
4815
+ */
4816
+ async finalizeCommit(tx) {
4817
+ const dirtyPages = tx.__getDirtyPages();
4818
+ if (dirtyPages.size === 0) {
4819
+ this.cleanupTransaction(tx);
4820
+ return;
4821
+ }
4822
+ if (this.logManager) {
4823
+ await this.logManager.writeCommitMarker();
4824
+ }
4382
4825
  const sortedPages = Array.from(dirtyPages).sort((a, b) => a - b);
4383
4826
  const promises = [];
4384
4827
  for (const pageId of sortedPages) {
@@ -4401,6 +4844,15 @@ var VirtualFileSystem = class {
4401
4844
  }
4402
4845
  this.cleanupTransaction(tx);
4403
4846
  }
4847
+ /**
4848
+ * Commits the transaction (Single Phase).
4849
+ * Wrapper for prepare + finalize for backward compatibility.
4850
+ * @param tx Transaction
4851
+ */
4852
+ async commit(tx) {
4853
+ await this.prepareCommit(tx);
4854
+ await this.finalizeCommit(tx);
4855
+ }
4404
4856
  /**
4405
4857
  * Rolls back the transaction.
4406
4858
  * @param tx Transaction
@@ -4448,8 +4900,8 @@ var VirtualFileSystem = class {
4448
4900
  * @param position Start position in file
4449
4901
  */
4450
4902
  _writeAsync(handle, buffer, offset, length, position) {
4451
- if (position + length > 100 * 1024 * 1024) {
4452
- return Promise.reject(new Error(`[Safety Limit] File write exceeds 100MB limit at position ${position}`));
4903
+ if (position + length > 512 * 1024 * 1024) {
4904
+ return Promise.reject(new Error(`[Safety Limit] File write exceeds 512MB limit at position ${position}`));
4453
4905
  }
4454
4906
  return new Promise((resolve, reject) => {
4455
4907
  import_node_fs2.default.write(handle, buffer, offset, length, position, (err, bytesWritten) => {
@@ -4627,6 +5079,43 @@ var PageFileSystem = class {
4627
5079
  pageFactory = new PageManagerFactory();
4628
5080
  vfs;
4629
5081
  pageManagerFactory;
5082
+ /**
5083
+ * Updates the bitmap status for a specific page.
5084
+ * @param pageId The ID of the page to update
5085
+ * @param isFree True to mark as free, false to mark as used
5086
+ * @param tx Transaction
5087
+ */
5088
+ async updateBitmap(pageId, isFree, tx) {
5089
+ const metadata = await this.getMetadata(tx);
5090
+ const metadataManager = this.pageFactory.getManager(metadata);
5091
+ const bitmapPageId = metadataManager.getBitmapPageId(metadata);
5092
+ const headerSize = PageManager.CONSTANT.SIZE_PAGE_HEADER;
5093
+ const capacityPerBitmapPage = (this.pageSize - headerSize) * 8;
5094
+ let currentBitmapPageId = bitmapPageId;
5095
+ let targetBitIndex = pageId;
5096
+ while (targetBitIndex >= capacityPerBitmapPage) {
5097
+ const currentBitmapPage = await this.get(currentBitmapPageId, tx);
5098
+ const manager = this.pageFactory.getManager(currentBitmapPage);
5099
+ targetBitIndex -= capacityPerBitmapPage;
5100
+ const nextPageId = manager.getNextPageId(currentBitmapPage);
5101
+ if (nextPageId === -1) {
5102
+ if (!isFree) {
5103
+ throw new Error("Bitmap page not found for reused page");
5104
+ }
5105
+ const newBitmapPageId = await this.appendNewPage(PageManager.CONSTANT.PAGE_TYPE_BITMAP, tx);
5106
+ manager.setNextPageId(currentBitmapPage, newBitmapPageId);
5107
+ await this.setPage(currentBitmapPageId, currentBitmapPage, tx);
5108
+ currentBitmapPageId = newBitmapPageId;
5109
+ } else {
5110
+ currentBitmapPageId = nextPageId;
5111
+ }
5112
+ }
5113
+ await tx.__acquireWriteLock(currentBitmapPageId);
5114
+ const targetBitmapPage = await this.get(currentBitmapPageId, tx);
5115
+ const bitmapManager = this.pageFactory.getManager(targetBitmapPage);
5116
+ bitmapManager.setBit(targetBitmapPage, targetBitIndex, isFree);
5117
+ await this.setPage(currentBitmapPageId, targetBitmapPage, tx);
5118
+ }
4630
5119
  /**
4631
5120
  * VFS 인스턴스를 반환합니다.
4632
5121
  * Transaction 생성 시 사용됩니다.
@@ -4735,12 +5224,28 @@ var PageFileSystem = class {
4735
5224
  }
4736
5225
  /**
4737
5226
  * Appends and inserts a new page.
4738
- * @returns Created page ID
5227
+ * If a free page is available in the free list, it reuses it.
5228
+ * Otherwise, it appends a new page to the end of the file.
5229
+ * @returns Created or reused page ID
4739
5230
  */
4740
5231
  async appendNewPage(pageType = PageManager.CONSTANT.PAGE_TYPE_EMPTY, tx) {
4741
5232
  await tx.__acquireWriteLock(0);
4742
5233
  const metadata = await this.getMetadata(tx);
4743
5234
  const metadataManager = this.pageFactory.getManager(metadata);
5235
+ const freePageId = metadataManager.getFreePageId(metadata);
5236
+ if (freePageId !== -1) {
5237
+ const reusedPageId = freePageId;
5238
+ const reusedPage = await this.get(reusedPageId, tx);
5239
+ const reusedPageManager = this.pageFactory.getManager(reusedPage);
5240
+ const nextFreePageId = reusedPageManager.getNextPageId(reusedPage);
5241
+ metadataManager.setFreePageId(metadata, nextFreePageId);
5242
+ await this.setPage(0, metadata, tx);
5243
+ await this.updateBitmap(reusedPageId, false, tx);
5244
+ const manager2 = this.pageFactory.getManagerFromType(pageType);
5245
+ const newPage2 = manager2.create(this.pageSize, reusedPageId);
5246
+ await this.setPage(reusedPageId, newPage2, tx);
5247
+ return reusedPageId;
5248
+ }
4744
5249
  const pageCount = metadataManager.getPageCount(metadata);
4745
5250
  const newPageIndex = pageCount;
4746
5251
  const newTotalCount = pageCount + 1;
@@ -4805,6 +5310,26 @@ var PageFileSystem = class {
4805
5310
  }
4806
5311
  }
4807
5312
  }
5313
+ /**
5314
+ * Frees the page and marks it as available in the bitmap.
5315
+ * It also adds the page to the linked list of free pages in metadata.
5316
+ * @param pageId Page ID
5317
+ * @param tx Transaction
5318
+ */
5319
+ async setFreePage(pageId, tx) {
5320
+ await tx.__acquireWriteLock(0);
5321
+ await tx.__acquireWriteLock(pageId);
5322
+ const metadata = await this.getMetadata(tx);
5323
+ const metadataManager = this.pageFactory.getManager(metadata);
5324
+ const currentHeadFreePageId = metadataManager.getFreePageId(metadata);
5325
+ const emptyPageManager = this.pageFactory.getManagerFromType(PageManager.CONSTANT.PAGE_TYPE_EMPTY);
5326
+ const emptyPage = emptyPageManager.create(this.pageSize, pageId);
5327
+ emptyPageManager.setNextPageId(emptyPage, currentHeadFreePageId);
5328
+ await this.setPage(pageId, emptyPage, tx);
5329
+ await this.updateBitmap(pageId, true, tx);
5330
+ metadataManager.setFreePageId(metadata, pageId);
5331
+ await this.setPage(0, metadata, tx);
5332
+ }
4808
5333
  /**
4809
5334
  * Closes the page file system.
4810
5335
  */
@@ -4944,8 +5469,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
4944
5469
  while (true) {
4945
5470
  const page = await this.pfs.get(pageId, tx);
4946
5471
  const nextPageId = manager.getNextPageId(page);
4947
- manager.setNextPageId(page, -1);
4948
- manager.setRemainingCapacity(page, this.pfs.pageSize - PageManager.CONSTANT.SIZE_PAGE_HEADER);
5472
+ await this.pfs.setFreePage(pageId, tx);
4949
5473
  if (nextPageId === -1) {
4950
5474
  break;
4951
5475
  }
@@ -5337,13 +5861,41 @@ var RowTableEngine = class {
5337
5861
  if (this.rowManager.getDeletedFlag(row)) {
5338
5862
  return;
5339
5863
  }
5864
+ if (this.rowManager.getOverflowFlag(row)) {
5865
+ let overflowPageId = bytesToNumber(this.rowManager.getBody(row));
5866
+ while (overflowPageId !== -1) {
5867
+ const overflowPage = await this.pfs.get(overflowPageId, tx);
5868
+ const manager = this.factory.getManager(overflowPage);
5869
+ const nextPageId = manager.getNextPageId(overflowPage);
5870
+ await this.pfs.setFreePage(overflowPageId, tx);
5871
+ overflowPageId = nextPageId;
5872
+ }
5873
+ }
5340
5874
  this.rowManager.setDeletedFlag(row, true);
5341
5875
  await this.pfs.setPage(pageId, page, tx);
5342
5876
  if (decrementRowCount) {
5343
- const metadataPage = await this.pfs.getMetadata(tx);
5344
- const currentRowCount = this.metadataPageManager.getRowCount(metadataPage);
5345
- this.metadataPageManager.setRowCount(metadataPage, currentRowCount - 1);
5346
- await this.pfs.setMetadata(metadataPage, tx);
5877
+ const metadataPage2 = await this.pfs.getMetadata(tx);
5878
+ const currentRowCount = this.metadataPageManager.getRowCount(metadataPage2);
5879
+ this.metadataPageManager.setRowCount(metadataPage2, currentRowCount - 1);
5880
+ await this.pfs.setMetadata(metadataPage2, tx);
5881
+ }
5882
+ const insertedRowCount = this.dataPageManager.getInsertedRowCount(page);
5883
+ let allDeleted = true;
5884
+ const metadataPage = await this.pfs.getMetadata(tx);
5885
+ const lastInsertPageId = this.metadataPageManager.getLastInsertPageId(metadataPage);
5886
+ if (pageId === lastInsertPageId) {
5887
+ allDeleted = false;
5888
+ } else {
5889
+ for (let i = 0; i < insertedRowCount; i++) {
5890
+ const slotRow = this.dataPageManager.getRow(page, i);
5891
+ if (!this.rowManager.getDeletedFlag(slotRow)) {
5892
+ allDeleted = false;
5893
+ break;
5894
+ }
5895
+ }
5896
+ }
5897
+ if (allDeleted) {
5898
+ await this.pfs.setFreePage(pageId, tx);
5347
5899
  }
5348
5900
  }
5349
5901
  /**
@@ -5547,11 +6099,19 @@ var Transaction = class {
5547
6099
  this.heldLocks.add(lockId);
5548
6100
  this.pageLocks.set(pageId, lockId);
5549
6101
  }
6102
+ /**
6103
+ * Prepares the transaction for commit (Phase 1 of 2PC).
6104
+ * Writes dirty pages to WAL but does not update the database file yet.
6105
+ */
6106
+ async prepare() {
6107
+ await this.vfs.prepareCommit(this);
6108
+ }
5550
6109
  /**
5551
6110
  * Commits the transaction.
5552
6111
  */
5553
6112
  async commit() {
5554
- await this.vfs.commit(this);
6113
+ await this.vfs.prepareCommit(this);
6114
+ await this.vfs.finalizeCommit(this);
5555
6115
  await TxContext.run(this, async () => {
5556
6116
  for (const hook of this.commitHooks) {
5557
6117
  await hook();
@@ -5595,11 +6155,20 @@ var Transaction = class {
5595
6155
 
5596
6156
  // src/core/DataplyAPI.ts
5597
6157
  var DataplyAPI = class {
5598
- constructor(file, fileHandle, options) {
6158
+ constructor(file, options) {
5599
6159
  this.file = file;
5600
- this.fileHandle = fileHandle;
5601
- this.options = options;
5602
- this.pfs = new PageFileSystem(fileHandle, options.pageSize, options.pageCacheCapacity, options.wal);
6160
+ this.hook = {
6161
+ sync: useHookallSync(this),
6162
+ async: useHookall(this)
6163
+ };
6164
+ this.options = this.verboseOptions(options);
6165
+ this.fileHandle = this.createOrOpen(file, this.options);
6166
+ this.pfs = new PageFileSystem(
6167
+ this.fileHandle,
6168
+ this.options.pageSize,
6169
+ this.options.pageCacheCapacity,
6170
+ this.options.wal
6171
+ );
5603
6172
  this.textCodec = new TextCodec();
5604
6173
  this.lockManager = new LockManager();
5605
6174
  this.rowTableEngine = new RowTableEngine(this.pfs, this.options);
@@ -5607,10 +6176,12 @@ var DataplyAPI = class {
5607
6176
  this.txIdCounter = 0;
5608
6177
  }
5609
6178
  options;
6179
+ fileHandle;
5610
6180
  pfs;
5611
6181
  rowTableEngine;
5612
6182
  lockManager;
5613
6183
  textCodec;
6184
+ hook;
5614
6185
  initialized;
5615
6186
  txIdCounter;
5616
6187
  /**
@@ -5619,7 +6190,7 @@ var DataplyAPI = class {
5619
6190
  * @param fileHandle File handle
5620
6191
  * @returns Whether the page file is a valid Dataply file
5621
6192
  */
5622
- static VerifyFormat(fileHandle) {
6193
+ verifyFormat(fileHandle) {
5623
6194
  const size = MetadataPageManager.CONSTANT.OFFSET_MAGIC_STRING + MetadataPageManager.CONSTANT.MAGIC_STRING.length;
5624
6195
  const metadataPage = new Uint8Array(size);
5625
6196
  import_node_fs3.default.readSync(fileHandle, metadataPage, 0, size, 0);
@@ -5633,7 +6204,7 @@ var DataplyAPI = class {
5633
6204
  * @param options Options
5634
6205
  * @returns Options filled without omissions
5635
6206
  */
5636
- static VerboseOptions(options) {
6207
+ verboseOptions(options) {
5637
6208
  return Object.assign({
5638
6209
  pageSize: 8192,
5639
6210
  pageCacheCapacity: 1e4,
@@ -5644,53 +6215,71 @@ var DataplyAPI = class {
5644
6215
  * Initializes the database file.
5645
6216
  * The first page is initialized as the metadata page.
5646
6217
  * The second page is initialized as the first data page.
6218
+ * @param file Database file path
5647
6219
  * @param fileHandle File handle
5648
6220
  */
5649
- static InitializeFile(fileHandle, options) {
5650
- const metadataPageManager = new MetadataPageManager();
5651
- const dataPageManager = new DataPageManager();
5652
- const metadataPage = new Uint8Array(options.pageSize);
5653
- const dataPage = new Uint8Array(options.pageSize);
5654
- metadataPageManager.initial(
5655
- metadataPage,
5656
- MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA,
5657
- 0,
5658
- 0,
5659
- options.pageSize - MetadataPageManager.CONSTANT.SIZE_PAGE_HEADER
5660
- );
5661
- metadataPageManager.setMagicString(metadataPage);
5662
- metadataPageManager.setPageCount(metadataPage, 2);
5663
- metadataPageManager.setPageSize(metadataPage, options.pageSize);
5664
- metadataPageManager.setRootIndexPageId(metadataPage, -1);
5665
- metadataPageManager.setLastInsertPageId(metadataPage, 1);
5666
- dataPageManager.initial(
5667
- dataPage,
5668
- DataPageManager.CONSTANT.PAGE_TYPE_DATA,
5669
- 1,
5670
- -1,
5671
- options.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER
5672
- );
5673
- import_node_fs3.default.appendFileSync(fileHandle, metadataPage);
5674
- import_node_fs3.default.appendFileSync(fileHandle, dataPage);
6221
+ initializeFile(file, fileHandle, options) {
6222
+ const fileData = this.hook.sync.trigger("create", new Uint8Array(), (prepareFileData) => {
6223
+ const metadataPageManager = new MetadataPageManager();
6224
+ const bitmapPageManager = new BitmapPageManager();
6225
+ const dataPageManager = new DataPageManager();
6226
+ const metadataPage = new Uint8Array(options.pageSize);
6227
+ const dataPage = new Uint8Array(options.pageSize);
6228
+ metadataPageManager.initial(
6229
+ metadataPage,
6230
+ MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA,
6231
+ 0,
6232
+ 0,
6233
+ options.pageSize - MetadataPageManager.CONSTANT.SIZE_PAGE_HEADER
6234
+ );
6235
+ metadataPageManager.setMagicString(metadataPage);
6236
+ metadataPageManager.setPageSize(metadataPage, options.pageSize);
6237
+ metadataPageManager.setRootIndexPageId(metadataPage, -1);
6238
+ metadataPageManager.setBitmapPageId(metadataPage, 1);
6239
+ metadataPageManager.setLastInsertPageId(metadataPage, 2);
6240
+ metadataPageManager.setPageCount(metadataPage, 3);
6241
+ metadataPageManager.setFreePageId(metadataPage, -1);
6242
+ const bitmapPage = new Uint8Array(options.pageSize);
6243
+ bitmapPageManager.initial(
6244
+ bitmapPage,
6245
+ BitmapPageManager.CONSTANT.PAGE_TYPE_BITMAP,
6246
+ 1,
6247
+ -1,
6248
+ options.pageSize - BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER
6249
+ );
6250
+ dataPageManager.initial(
6251
+ dataPage,
6252
+ DataPageManager.CONSTANT.PAGE_TYPE_DATA,
6253
+ 2,
6254
+ -1,
6255
+ options.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER
6256
+ );
6257
+ return new Uint8Array([
6258
+ ...prepareFileData,
6259
+ ...metadataPage,
6260
+ ...bitmapPage,
6261
+ ...dataPage
6262
+ ]);
6263
+ }, file, fileHandle, options);
6264
+ import_node_fs3.default.appendFileSync(fileHandle, fileData);
5675
6265
  }
5676
6266
  /**
5677
6267
  * Opens the database file. If the file does not exist, it initializes it.
5678
6268
  * @param file Database file path
5679
6269
  * @param options Options
5680
- * @returns Dataply instance
6270
+ * @returns File handle
5681
6271
  */
5682
- static Use(file, options) {
5683
- const verboseOption = this.VerboseOptions(options);
6272
+ createOrOpen(file, options) {
5684
6273
  let fileHandle;
5685
- if (verboseOption.pageCacheCapacity < 100) {
6274
+ if (options.pageCacheCapacity < 100) {
5686
6275
  throw new Error("Page cache capacity must be at least 100");
5687
6276
  }
5688
6277
  if (!import_node_fs3.default.existsSync(file)) {
5689
- if (verboseOption.pageSize < 4096) {
6278
+ if (options.pageSize < 4096) {
5690
6279
  throw new Error("Page size must be at least 4096 bytes");
5691
6280
  }
5692
6281
  fileHandle = import_node_fs3.default.openSync(file, "w+");
5693
- this.InitializeFile(fileHandle, verboseOption);
6282
+ this.initializeFile(file, fileHandle, options);
5694
6283
  } else {
5695
6284
  fileHandle = import_node_fs3.default.openSync(file, "r+");
5696
6285
  const buffer = new Uint8Array(
@@ -5701,14 +6290,14 @@ var DataplyAPI = class {
5701
6290
  if (metadataManager.isMetadataPage(buffer)) {
5702
6291
  const storedPageSize = metadataManager.getPageSize(buffer);
5703
6292
  if (storedPageSize > 0) {
5704
- verboseOption.pageSize = storedPageSize;
6293
+ options.pageSize = storedPageSize;
5705
6294
  }
5706
6295
  }
5707
6296
  }
5708
- if (!this.VerifyFormat(fileHandle)) {
6297
+ if (!this.verifyFormat(fileHandle)) {
5709
6298
  throw new Error("Invalid dataply file");
5710
6299
  }
5711
- return new this(file, fileHandle, verboseOption);
6300
+ return fileHandle;
5712
6301
  }
5713
6302
  /**
5714
6303
  * Initializes the dataply instance.
@@ -5719,8 +6308,12 @@ var DataplyAPI = class {
5719
6308
  if (this.initialized) {
5720
6309
  return;
5721
6310
  }
5722
- await this.runWithDefault(() => this.rowTableEngine.init());
5723
- this.initialized = true;
6311
+ await this.runWithDefault(() => {
6312
+ return this.hook.async.trigger("init", void 0, async () => {
6313
+ await this.rowTableEngine.init();
6314
+ this.initialized = true;
6315
+ });
6316
+ });
5724
6317
  }
5725
6318
  /**
5726
6319
  * Creates a transaction.
@@ -5779,10 +6372,11 @@ var DataplyAPI = class {
5779
6372
  throw new Error("Dataply instance is not initialized");
5780
6373
  }
5781
6374
  return this.runWithDefault((tx2) => {
6375
+ incrementRowCount = incrementRowCount ?? true;
5782
6376
  if (typeof data === "string") {
5783
6377
  data = this.textCodec.encode(data);
5784
6378
  }
5785
- return this.rowTableEngine.insert(data, incrementRowCount ?? true, tx2);
6379
+ return this.rowTableEngine.insert(data, incrementRowCount, tx2);
5786
6380
  }, tx);
5787
6381
  }
5788
6382
  /**
@@ -5798,10 +6392,11 @@ var DataplyAPI = class {
5798
6392
  throw new Error("Dataply instance is not initialized");
5799
6393
  }
5800
6394
  return this.runWithDefault(async (tx2) => {
6395
+ incrementRowCount = incrementRowCount ?? true;
5801
6396
  const pks = [];
5802
6397
  for (const data of dataList) {
5803
6398
  const encoded = typeof data === "string" ? this.textCodec.encode(data) : data;
5804
- const pk = await this.rowTableEngine.insert(encoded, incrementRowCount ?? true, tx2);
6399
+ const pk = await this.rowTableEngine.insert(encoded, incrementRowCount, tx2);
5805
6400
  pks.push(pk);
5806
6401
  }
5807
6402
  return pks;
@@ -5835,7 +6430,8 @@ var DataplyAPI = class {
5835
6430
  throw new Error("Dataply instance is not initialized");
5836
6431
  }
5837
6432
  return this.runWithDefault(async (tx2) => {
5838
- await this.rowTableEngine.delete(pk, decrementRowCount ?? true, tx2);
6433
+ decrementRowCount = decrementRowCount ?? true;
6434
+ await this.rowTableEngine.delete(pk, decrementRowCount, tx2);
5839
6435
  }, tx);
5840
6436
  }
5841
6437
  async select(pk, asRaw = false, tx) {
@@ -5856,8 +6452,10 @@ var DataplyAPI = class {
5856
6452
  if (!this.initialized) {
5857
6453
  throw new Error("Dataply instance is not initialized");
5858
6454
  }
5859
- await this.pfs.close();
5860
- import_node_fs3.default.closeSync(this.fileHandle);
6455
+ return this.hook.async.trigger("close", void 0, async () => {
6456
+ await this.pfs.close();
6457
+ import_node_fs3.default.closeSync(this.fileHandle);
6458
+ });
5861
6459
  }
5862
6460
  };
5863
6461
 
@@ -5865,7 +6463,7 @@ var DataplyAPI = class {
5865
6463
  var Dataply = class {
5866
6464
  api;
5867
6465
  constructor(file, options) {
5868
- this.api = DataplyAPI.Use(file, options);
6466
+ this.api = new DataplyAPI(file, options ?? {});
5869
6467
  }
5870
6468
  /**
5871
6469
  * Gets the options used to open the dataply.
@@ -5944,6 +6542,52 @@ var Dataply = class {
5944
6542
  return this.api.close();
5945
6543
  }
5946
6544
  };
6545
+
6546
+ // src/core/transaction/GlobalTransaction.ts
6547
+ var GlobalTransaction = class {
6548
+ transactions = [];
6549
+ isCommitted = false;
6550
+ isRolledBack = false;
6551
+ /**
6552
+ * Adds a transaction to the global transaction.
6553
+ * @param tx Transaction to add
6554
+ */
6555
+ add(tx) {
6556
+ this.transactions.push(tx);
6557
+ }
6558
+ /**
6559
+ * Commits all transactions atomically.
6560
+ * Phase 1: Prepare (Write WAL)
6561
+ * Phase 2: Commit (Write Commit Marker & Checkpoint)
6562
+ */
6563
+ async commit() {
6564
+ if (this.isCommitted || this.isRolledBack) {
6565
+ throw new Error("Transaction is already finished");
6566
+ }
6567
+ try {
6568
+ await Promise.all(this.transactions.map((tx) => tx.prepare()));
6569
+ } catch (e) {
6570
+ await this.rollback();
6571
+ throw new Error(`Global commit failed during prepare phase: ${e}`);
6572
+ }
6573
+ try {
6574
+ await Promise.all(this.transactions.map((tx) => tx.commit()));
6575
+ this.isCommitted = true;
6576
+ } catch (e) {
6577
+ throw new Error(`Global commit failed during finalize phase: ${e}`);
6578
+ }
6579
+ }
6580
+ /**
6581
+ * Rolls back all transactions.
6582
+ */
6583
+ async rollback() {
6584
+ if (this.isCommitted || this.isRolledBack) {
6585
+ return;
6586
+ }
6587
+ await Promise.all(this.transactions.map((tx) => tx.rollback()));
6588
+ this.isRolledBack = true;
6589
+ }
6590
+ };
5947
6591
  // Annotate the CommonJS export names for ESM import in node:
5948
6592
  0 && (module.exports = {
5949
6593
  BPTreeAsync,
@@ -5952,6 +6596,7 @@ var Dataply = class {
5952
6596
  CacheEntanglementSync,
5953
6597
  Dataply,
5954
6598
  DataplyAPI,
6599
+ GlobalTransaction,
5955
6600
  InMemoryStoreStrategyAsync,
5956
6601
  InMemoryStoreStrategySync,
5957
6602
  InvertedWeakMap,