dataply 0.0.2 → 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) {
@@ -4323,9 +4638,33 @@ var LogManager = class {
4323
4638
  });
4324
4639
  });
4325
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
+ }
4326
4664
  /**
4327
4665
  * Reads the log file to recover the page map.
4328
4666
  * Runs synchronously as it is called by the VFS constructor.
4667
+ * Only returns pages from committed transactions (ended with a commit marker).
4329
4668
  * @returns Recovered page map
4330
4669
  */
4331
4670
  readAllSync() {
@@ -4335,11 +4674,19 @@ var LogManager = class {
4335
4674
  const restoredPages = /* @__PURE__ */ new Map();
4336
4675
  const currentFileSize = import_node_fs.default.fstatSync(this.fd).size;
4337
4676
  let offset = 0;
4677
+ let pendingPages = /* @__PURE__ */ new Map();
4338
4678
  while (offset + this.entrySize <= currentFileSize) {
4339
4679
  import_node_fs.default.readSync(this.fd, this.buffer, 0, this.entrySize, offset);
4340
4680
  const pageId = this.view.getUint32(0, true);
4341
- const pageData = this.buffer.slice(4, 4 + this.pageSize);
4342
- 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
+ }
4343
4690
  offset += this.entrySize;
4344
4691
  }
4345
4692
  return restoredPages;
@@ -4441,13 +4788,13 @@ var VirtualFileSystem = class {
4441
4788
  }
4442
4789
  }
4443
4790
  /**
4444
- * 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.
4445
4793
  * @param tx Transaction
4446
4794
  */
4447
- async commit(tx) {
4795
+ async prepareCommit(tx) {
4448
4796
  const dirtyPages = tx.__getDirtyPages();
4449
4797
  if (dirtyPages.size === 0) {
4450
- this.cleanupTransaction(tx);
4451
4798
  return;
4452
4799
  }
4453
4800
  const dirtyPageMap = /* @__PURE__ */ new Map();
@@ -4460,6 +4807,21 @@ var VirtualFileSystem = class {
4460
4807
  if (this.logManager && dirtyPageMap.size > 0) {
4461
4808
  await this.logManager.append(dirtyPageMap);
4462
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
+ }
4463
4825
  const sortedPages = Array.from(dirtyPages).sort((a, b) => a - b);
4464
4826
  const promises = [];
4465
4827
  for (const pageId of sortedPages) {
@@ -4482,6 +4844,15 @@ var VirtualFileSystem = class {
4482
4844
  }
4483
4845
  this.cleanupTransaction(tx);
4484
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
+ }
4485
4856
  /**
4486
4857
  * Rolls back the transaction.
4487
4858
  * @param tx Transaction
@@ -5728,11 +6099,19 @@ var Transaction = class {
5728
6099
  this.heldLocks.add(lockId);
5729
6100
  this.pageLocks.set(pageId, lockId);
5730
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
+ }
5731
6109
  /**
5732
6110
  * Commits the transaction.
5733
6111
  */
5734
6112
  async commit() {
5735
- await this.vfs.commit(this);
6113
+ await this.vfs.prepareCommit(this);
6114
+ await this.vfs.finalizeCommit(this);
5736
6115
  await TxContext.run(this, async () => {
5737
6116
  for (const hook of this.commitHooks) {
5738
6117
  await hook();
@@ -5776,11 +6155,20 @@ var Transaction = class {
5776
6155
 
5777
6156
  // src/core/DataplyAPI.ts
5778
6157
  var DataplyAPI = class {
5779
- constructor(file, fileHandle, options) {
6158
+ constructor(file, options) {
5780
6159
  this.file = file;
5781
- this.fileHandle = fileHandle;
5782
- this.options = options;
5783
- 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
+ );
5784
6172
  this.textCodec = new TextCodec();
5785
6173
  this.lockManager = new LockManager();
5786
6174
  this.rowTableEngine = new RowTableEngine(this.pfs, this.options);
@@ -5788,10 +6176,12 @@ var DataplyAPI = class {
5788
6176
  this.txIdCounter = 0;
5789
6177
  }
5790
6178
  options;
6179
+ fileHandle;
5791
6180
  pfs;
5792
6181
  rowTableEngine;
5793
6182
  lockManager;
5794
6183
  textCodec;
6184
+ hook;
5795
6185
  initialized;
5796
6186
  txIdCounter;
5797
6187
  /**
@@ -5800,7 +6190,7 @@ var DataplyAPI = class {
5800
6190
  * @param fileHandle File handle
5801
6191
  * @returns Whether the page file is a valid Dataply file
5802
6192
  */
5803
- static VerifyFormat(fileHandle) {
6193
+ verifyFormat(fileHandle) {
5804
6194
  const size = MetadataPageManager.CONSTANT.OFFSET_MAGIC_STRING + MetadataPageManager.CONSTANT.MAGIC_STRING.length;
5805
6195
  const metadataPage = new Uint8Array(size);
5806
6196
  import_node_fs3.default.readSync(fileHandle, metadataPage, 0, size, 0);
@@ -5814,7 +6204,7 @@ var DataplyAPI = class {
5814
6204
  * @param options Options
5815
6205
  * @returns Options filled without omissions
5816
6206
  */
5817
- static VerboseOptions(options) {
6207
+ verboseOptions(options) {
5818
6208
  return Object.assign({
5819
6209
  pageSize: 8192,
5820
6210
  pageCacheCapacity: 1e4,
@@ -5825,67 +6215,71 @@ var DataplyAPI = class {
5825
6215
  * Initializes the database file.
5826
6216
  * The first page is initialized as the metadata page.
5827
6217
  * The second page is initialized as the first data page.
6218
+ * @param file Database file path
5828
6219
  * @param fileHandle File handle
5829
6220
  */
5830
- static InitializeFile(fileHandle, options) {
5831
- const metadataPageManager = new MetadataPageManager();
5832
- const bitmapPageManager = new BitmapPageManager();
5833
- const dataPageManager = new DataPageManager();
5834
- const metadataPage = new Uint8Array(options.pageSize);
5835
- const dataPage = new Uint8Array(options.pageSize);
5836
- metadataPageManager.initial(
5837
- metadataPage,
5838
- MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA,
5839
- 0,
5840
- 0,
5841
- options.pageSize - MetadataPageManager.CONSTANT.SIZE_PAGE_HEADER
5842
- );
5843
- metadataPageManager.setMagicString(metadataPage);
5844
- metadataPageManager.setPageSize(metadataPage, options.pageSize);
5845
- metadataPageManager.setRootIndexPageId(metadataPage, -1);
5846
- metadataPageManager.setBitmapPageId(metadataPage, 1);
5847
- metadataPageManager.setLastInsertPageId(metadataPage, 2);
5848
- metadataPageManager.setPageCount(metadataPage, 3);
5849
- metadataPageManager.setFreePageId(metadataPage, -1);
5850
- const bitmapPage = new Uint8Array(options.pageSize);
5851
- bitmapPageManager.initial(
5852
- bitmapPage,
5853
- BitmapPageManager.CONSTANT.PAGE_TYPE_BITMAP,
5854
- 1,
5855
- -1,
5856
- options.pageSize - BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER
5857
- );
5858
- dataPageManager.initial(
5859
- dataPage,
5860
- DataPageManager.CONSTANT.PAGE_TYPE_DATA,
5861
- 2,
5862
- -1,
5863
- options.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER
5864
- );
5865
- import_node_fs3.default.appendFileSync(fileHandle, new Uint8Array([
5866
- ...metadataPage,
5867
- ...bitmapPage,
5868
- ...dataPage
5869
- ]));
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);
5870
6265
  }
5871
6266
  /**
5872
6267
  * Opens the database file. If the file does not exist, it initializes it.
5873
6268
  * @param file Database file path
5874
6269
  * @param options Options
5875
- * @returns Dataply instance
6270
+ * @returns File handle
5876
6271
  */
5877
- static Use(file, options) {
5878
- const verboseOption = this.VerboseOptions(options);
6272
+ createOrOpen(file, options) {
5879
6273
  let fileHandle;
5880
- if (verboseOption.pageCacheCapacity < 100) {
6274
+ if (options.pageCacheCapacity < 100) {
5881
6275
  throw new Error("Page cache capacity must be at least 100");
5882
6276
  }
5883
6277
  if (!import_node_fs3.default.existsSync(file)) {
5884
- if (verboseOption.pageSize < 4096) {
6278
+ if (options.pageSize < 4096) {
5885
6279
  throw new Error("Page size must be at least 4096 bytes");
5886
6280
  }
5887
6281
  fileHandle = import_node_fs3.default.openSync(file, "w+");
5888
- this.InitializeFile(fileHandle, verboseOption);
6282
+ this.initializeFile(file, fileHandle, options);
5889
6283
  } else {
5890
6284
  fileHandle = import_node_fs3.default.openSync(file, "r+");
5891
6285
  const buffer = new Uint8Array(
@@ -5896,14 +6290,14 @@ var DataplyAPI = class {
5896
6290
  if (metadataManager.isMetadataPage(buffer)) {
5897
6291
  const storedPageSize = metadataManager.getPageSize(buffer);
5898
6292
  if (storedPageSize > 0) {
5899
- verboseOption.pageSize = storedPageSize;
6293
+ options.pageSize = storedPageSize;
5900
6294
  }
5901
6295
  }
5902
6296
  }
5903
- if (!this.VerifyFormat(fileHandle)) {
6297
+ if (!this.verifyFormat(fileHandle)) {
5904
6298
  throw new Error("Invalid dataply file");
5905
6299
  }
5906
- return new this(file, fileHandle, verboseOption);
6300
+ return fileHandle;
5907
6301
  }
5908
6302
  /**
5909
6303
  * Initializes the dataply instance.
@@ -5914,8 +6308,12 @@ var DataplyAPI = class {
5914
6308
  if (this.initialized) {
5915
6309
  return;
5916
6310
  }
5917
- await this.runWithDefault(() => this.rowTableEngine.init());
5918
- 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
+ });
5919
6317
  }
5920
6318
  /**
5921
6319
  * Creates a transaction.
@@ -5974,10 +6372,11 @@ var DataplyAPI = class {
5974
6372
  throw new Error("Dataply instance is not initialized");
5975
6373
  }
5976
6374
  return this.runWithDefault((tx2) => {
6375
+ incrementRowCount = incrementRowCount ?? true;
5977
6376
  if (typeof data === "string") {
5978
6377
  data = this.textCodec.encode(data);
5979
6378
  }
5980
- return this.rowTableEngine.insert(data, incrementRowCount ?? true, tx2);
6379
+ return this.rowTableEngine.insert(data, incrementRowCount, tx2);
5981
6380
  }, tx);
5982
6381
  }
5983
6382
  /**
@@ -5993,10 +6392,11 @@ var DataplyAPI = class {
5993
6392
  throw new Error("Dataply instance is not initialized");
5994
6393
  }
5995
6394
  return this.runWithDefault(async (tx2) => {
6395
+ incrementRowCount = incrementRowCount ?? true;
5996
6396
  const pks = [];
5997
6397
  for (const data of dataList) {
5998
6398
  const encoded = typeof data === "string" ? this.textCodec.encode(data) : data;
5999
- const pk = await this.rowTableEngine.insert(encoded, incrementRowCount ?? true, tx2);
6399
+ const pk = await this.rowTableEngine.insert(encoded, incrementRowCount, tx2);
6000
6400
  pks.push(pk);
6001
6401
  }
6002
6402
  return pks;
@@ -6030,7 +6430,8 @@ var DataplyAPI = class {
6030
6430
  throw new Error("Dataply instance is not initialized");
6031
6431
  }
6032
6432
  return this.runWithDefault(async (tx2) => {
6033
- await this.rowTableEngine.delete(pk, decrementRowCount ?? true, tx2);
6433
+ decrementRowCount = decrementRowCount ?? true;
6434
+ await this.rowTableEngine.delete(pk, decrementRowCount, tx2);
6034
6435
  }, tx);
6035
6436
  }
6036
6437
  async select(pk, asRaw = false, tx) {
@@ -6051,8 +6452,10 @@ var DataplyAPI = class {
6051
6452
  if (!this.initialized) {
6052
6453
  throw new Error("Dataply instance is not initialized");
6053
6454
  }
6054
- await this.pfs.close();
6055
- 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
+ });
6056
6459
  }
6057
6460
  };
6058
6461
 
@@ -6060,7 +6463,7 @@ var DataplyAPI = class {
6060
6463
  var Dataply = class {
6061
6464
  api;
6062
6465
  constructor(file, options) {
6063
- this.api = DataplyAPI.Use(file, options);
6466
+ this.api = new DataplyAPI(file, options ?? {});
6064
6467
  }
6065
6468
  /**
6066
6469
  * Gets the options used to open the dataply.
@@ -6139,6 +6542,52 @@ var Dataply = class {
6139
6542
  return this.api.close();
6140
6543
  }
6141
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
+ };
6142
6591
  // Annotate the CommonJS export names for ESM import in node:
6143
6592
  0 && (module.exports = {
6144
6593
  BPTreeAsync,
@@ -6147,6 +6596,7 @@ var Dataply = class {
6147
6596
  CacheEntanglementSync,
6148
6597
  Dataply,
6149
6598
  DataplyAPI,
6599
+ GlobalTransaction,
6150
6600
  InMemoryStoreStrategyAsync,
6151
6601
  InMemoryStoreStrategySync,
6152
6602
  InvertedWeakMap,
@@ -1,50 +1,63 @@
1
1
  import type { DataplyOptions, DataplyMetadata } from '../types';
2
+ import { type IHookall, type IHookallSync } from 'hookall';
2
3
  import { PageFileSystem } from './PageFileSystem';
3
4
  import { RowTableEngine } from './RowTableEngine';
4
5
  import { TextCodec } from '../utils/TextCodec';
5
6
  import { LockManager } from './transaction/LockManager';
6
7
  import { Transaction } from './transaction/Transaction';
8
+ interface DataplyAPISyncHook {
9
+ create: (fileData: Uint8Array, file: string, fileHandle: number, options: Required<DataplyOptions>) => Uint8Array;
10
+ }
11
+ interface DataplyAPIAsyncHook {
12
+ init: () => Promise<void>;
13
+ close: () => Promise<void>;
14
+ }
7
15
  /**
8
16
  * Class for managing Dataply files.
9
17
  */
10
18
  export declare class DataplyAPI {
11
- protected file: string;
12
- protected fileHandle: number;
19
+ protected readonly file: string;
13
20
  readonly options: Required<DataplyOptions>;
21
+ protected readonly fileHandle: number;
14
22
  protected readonly pfs: PageFileSystem;
15
23
  protected readonly rowTableEngine: RowTableEngine;
16
24
  protected readonly lockManager: LockManager;
17
25
  protected readonly textCodec: TextCodec;
26
+ protected readonly hook: {
27
+ sync: IHookallSync<DataplyAPISyncHook>;
28
+ async: IHookall<DataplyAPIAsyncHook>;
29
+ };
18
30
  protected initialized: boolean;
19
31
  private txIdCounter;
20
- protected constructor(file: string, fileHandle: number, options: Required<DataplyOptions>);
32
+ constructor(file: string, options: DataplyOptions);
21
33
  /**
22
34
  * Verifies if the page file is a valid Dataply file.
23
35
  * The metadata page must be located at the beginning of the Dataply file.
24
36
  * @param fileHandle File handle
25
37
  * @returns Whether the page file is a valid Dataply file
26
38
  */
27
- static VerifyFormat(fileHandle: number): boolean;
39
+ private verifyFormat;
28
40
  /**
29
41
  * Fills missing options with default values.
30
42
  * @param options Options
31
43
  * @returns Options filled without omissions
32
44
  */
33
- static VerboseOptions(options?: DataplyOptions): Required<DataplyOptions>;
45
+ private verboseOptions;
34
46
  /**
35
47
  * Initializes the database file.
36
48
  * The first page is initialized as the metadata page.
37
49
  * The second page is initialized as the first data page.
50
+ * @param file Database file path
38
51
  * @param fileHandle File handle
39
52
  */
40
- static InitializeFile(fileHandle: number, options: Required<DataplyOptions>): void;
53
+ private initializeFile;
41
54
  /**
42
55
  * Opens the database file. If the file does not exist, it initializes it.
43
56
  * @param file Database file path
44
57
  * @param options Options
45
- * @returns Dataply instance
58
+ * @returns File handle
46
59
  */
47
- static Use(file: string, options?: DataplyOptions): DataplyAPI;
60
+ private createOrOpen;
48
61
  /**
49
62
  * Initializes the dataply instance.
50
63
  * Must be called before using the dataply instance.
@@ -119,3 +132,4 @@ export declare class DataplyAPI {
119
132
  */
120
133
  close(): Promise<void>;
121
134
  }
135
+ export {};
@@ -25,9 +25,15 @@ export declare class LogManager {
25
25
  * @param pages Map of changed pages (Page ID -> Data)
26
26
  */
27
27
  append(pages: Map<number, Uint8Array>): Promise<void>;
28
+ /**
29
+ * Writes a commit marker to the log file.
30
+ * This indicates that the preceding logs are part of a committed transaction.
31
+ */
32
+ writeCommitMarker(): Promise<void>;
28
33
  /**
29
34
  * Reads the log file to recover the page map.
30
35
  * Runs synchronously as it is called by the VFS constructor.
36
+ * Only returns pages from committed transactions (ended with a commit marker).
31
37
  * @returns Recovered page map
32
38
  */
33
39
  readAllSync(): Map<number, Uint8Array>;
@@ -29,7 +29,20 @@ export declare class VirtualFileSystem {
29
29
  */
30
30
  private recover;
31
31
  /**
32
- * Commits the transaction.
32
+ * Prepares the transaction for commit (Phase 1).
33
+ * Writes dirty pages to WAL but does not update the main database file.
34
+ * @param tx Transaction
35
+ */
36
+ prepareCommit(tx: Transaction): Promise<void>;
37
+ /**
38
+ * Finalizes the transaction commit (Phase 2).
39
+ * Writes commit marker to WAL and updates the main database file (Checkpoint).
40
+ * @param tx Transaction
41
+ */
42
+ finalizeCommit(tx: Transaction): Promise<void>;
43
+ /**
44
+ * Commits the transaction (Single Phase).
45
+ * Wrapper for prepare + finalize for backward compatibility.
33
46
  * @param tx Transaction
34
47
  */
35
48
  commit(tx: Transaction): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import { Transaction } from './Transaction';
2
+ /**
3
+ * Global Transaction Manager.
4
+ * Coordinates transactions across multiple instances (shards) using 2-Phase Commit (2PC).
5
+ */
6
+ export declare class GlobalTransaction {
7
+ private transactions;
8
+ private isCommitted;
9
+ private isRolledBack;
10
+ /**
11
+ * Adds a transaction to the global transaction.
12
+ * @param tx Transaction to add
13
+ */
14
+ add(tx: Transaction): void;
15
+ /**
16
+ * Commits all transactions atomically.
17
+ * Phase 1: Prepare (Write WAL)
18
+ * Phase 2: Commit (Write Commit Marker & Checkpoint)
19
+ */
20
+ commit(): Promise<void>;
21
+ /**
22
+ * Rolls back all transactions.
23
+ */
24
+ rollback(): Promise<void>;
25
+ }
@@ -86,6 +86,11 @@ export declare class Transaction {
86
86
  * @param pageId Page ID
87
87
  */
88
88
  __acquireWriteLock(pageId: number): Promise<void>;
89
+ /**
90
+ * Prepares the transaction for commit (Phase 1 of 2PC).
91
+ * Writes dirty pages to WAL but does not update the database file yet.
92
+ */
93
+ prepare(): Promise<void>;
89
94
  /**
90
95
  * Commits the transaction.
91
96
  */
@@ -4,3 +4,4 @@ export * from 'cache-entanglement';
4
4
  export type { DataplyOptions } from './types';
5
5
  export { Dataply } from './core/Dataply';
6
6
  export { DataplyAPI } from './core/DataplyAPI';
7
+ export { GlobalTransaction } from './core/transaction/GlobalTransaction';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A lightweight storage engine for Node.js with support for MVCC, WAL.",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",
@@ -45,7 +45,8 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "cache-entanglement": "^1.7.1",
48
+ "hookall": "^2.2.0",
48
49
  "ryoiki": "^1.2.0",
49
50
  "serializable-bptree": "^5.2.1"
50
51
  }
51
- }
52
+ }
package/readme.md CHANGED
@@ -82,6 +82,36 @@ try {
82
82
  }
83
83
  ```
84
84
 
85
+ ### Global Transactions
86
+ You can perform atomic operations across multiple `Dataply` instances using the `GlobalTransaction` class. This uses a **2-Phase Commit (2PC)** mechanism to ensure that either all instances commit successfully or all are rolled back.
87
+
88
+ ```typescript
89
+ import { Dataply, GlobalTransaction } from 'dataply'
90
+
91
+ const db1 = new Dataply('./db1.db', { wal: './db1.wal' })
92
+ const db2 = new Dataply('./db2.db', { wal: './db2.wal' })
93
+
94
+ await db1.init()
95
+ await db2.init()
96
+
97
+ const tx1 = db1.createTransaction()
98
+ const tx2 = db2.createTransaction()
99
+
100
+ const globalTx = new GlobalTransaction()
101
+ globalTx.add(tx1)
102
+ globalTx.add(tx2)
103
+
104
+ try {
105
+ await db1.insert('Data for DB1', tx1)
106
+ await db2.insert('Data for DB2', tx2)
107
+
108
+ // Phase 1: Prepare (WAL write) -> Phase 2: Commit (Marker write)
109
+ await globalTx.commit()
110
+ } catch (error) {
111
+ await globalTx.rollback()
112
+ }
113
+ ```
114
+
85
115
  ### Auto-Transaction
86
116
  If you omit the `tx` argument when calling methods like `insert`, `update`, or `delete`, Dataply internally **creates an individual transaction automatically**.
87
117
 
@@ -133,6 +163,17 @@ Permanently reflects all changes made during the transaction to disk and release
133
163
  #### `async rollback(): Promise<void>`
134
164
  Cancels all changes made during the transaction and restores the original state.
135
165
 
166
+ ### GlobalTransaction Class
167
+
168
+ #### `add(tx: Transaction): void`
169
+ Adds an individual transaction from a `Dataply` instance to the global transaction.
170
+
171
+ #### `async commit(): Promise<void>`
172
+ Atomically commits all added transactions using a 2-Phase Commit (2PC) process.
173
+
174
+ #### `async rollback(): Promise<void>`
175
+ Rolls back all added transactions.
176
+
136
177
  ## Extending Dataply
137
178
 
138
179
  If you want to extend Dataply's functionality, use the `DataplyAPI` class. Unlike the standard `Dataply` class, `DataplyAPI` provides direct access to internal components like `PageFileSystem` or `RowTableEngine`, offering much more flexibility for custom implementations.
@@ -152,7 +193,7 @@ class CustomDataply extends DataplyAPI {
152
193
  }
153
194
  }
154
195
 
155
- const custom = CustomDataply.Use('./data.db')
196
+ const custom = new CustomDataply('./data.db')
156
197
  await custom.init()
157
198
 
158
199
  const stats = await custom.getInternalStats()
@@ -1,19 +0,0 @@
1
- import { type BPTreeNode, type SerializeStrategyHead, SerializeStrategyAsync } from 'serializable-bptree';
2
- import { PageFileSystem } from './PageFileSystem';
3
- import { IndexPageManager, PageManagerFactory } from './Page';
4
- import { TextCodec } from '../utils/TextCodec';
5
- export declare class RowIdentifierStrategy extends SerializeStrategyAsync<number, number> {
6
- readonly order: number;
7
- protected readonly pfs: PageFileSystem;
8
- protected rootPageId: number;
9
- protected factory: PageManagerFactory;
10
- protected indexPageManger: IndexPageManager;
11
- protected codec: TextCodec;
12
- constructor(order: number, pfs: PageFileSystem);
13
- id(isLeaf: boolean): Promise<string>;
14
- read(id: string): Promise<BPTreeNode<number, number>>;
15
- write(id: string, node: BPTreeNode<number, number>): Promise<void>;
16
- delete(id: string): Promise<void>;
17
- readHead(): Promise<SerializeStrategyHead | null>;
18
- writeHead(head: SerializeStrategyHead): Promise<void>;
19
- }
@@ -1,75 +0,0 @@
1
- import type { ShardOptions, ShardMetadata } from '../types';
2
- import { ShardAPI } from './ShareAPI';
3
- import { Transaction } from './transaction/Transaction';
4
- /**
5
- * Class for managing Shard files.
6
- */
7
- export declare class Shard {
8
- protected readonly api: ShardAPI;
9
- constructor(file: string, options?: ShardOptions);
10
- /**
11
- * Gets the options used to open the shard.
12
- * @returns Options used to open the shard.
13
- */
14
- get options(): Required<ShardOptions>;
15
- /**
16
- * Creates a transaction.
17
- * The created transaction object can be used to add or modify data.
18
- * A transaction must be terminated by calling either `commit` or `rollback`.
19
- * @returns Transaction object
20
- */
21
- createTransaction(): Transaction;
22
- /**
23
- * Initializes the shard instance.
24
- * Must be called before using the shard instance.
25
- * If not called, the shard instance cannot be used.
26
- */
27
- init(): Promise<void>;
28
- /**
29
- * Retrieves metadata from the shard.
30
- * @returns Metadata of the shard.
31
- */
32
- getMetadata(): Promise<ShardMetadata>;
33
- /**
34
- * Inserts data. Returns the PK of the added row.
35
- * @param data Data to add
36
- * @param tx Transaction
37
- * @returns PK of the added data
38
- */
39
- insert(data: string | Uint8Array, tx?: Transaction): Promise<number>;
40
- /**
41
- * Inserts multiple data in batch.
42
- * If a transaction is not provided, it internally creates a single transaction to process.
43
- * @param dataList Array of data to add
44
- * @param tx Transaction
45
- * @returns Array of PKs of the added data
46
- */
47
- insertBatch(dataList: (string | Uint8Array)[], tx?: Transaction): Promise<number[]>;
48
- /**
49
- * Updates data.
50
- * @param pk PK of the data to update
51
- * @param data Data to update
52
- * @param tx Transaction
53
- */
54
- update(pk: number, data: string | Uint8Array, tx?: Transaction): Promise<void>;
55
- /**
56
- * Deletes data.
57
- * @param pk PK of the data to delete
58
- * @param tx Transaction
59
- */
60
- delete(pk: number, tx?: Transaction): Promise<void>;
61
- /**
62
- * Selects data.
63
- * @param pk PK of the data to select
64
- * @param asRaw Whether to return the selected data as raw
65
- * @param tx Transaction
66
- * @returns Selected data
67
- */
68
- select(pk: number, asRaw: true, tx?: Transaction): Promise<Uint8Array | null>;
69
- select(pk: number, asRaw: false, tx?: Transaction): Promise<string | null>;
70
- select(pk: number, asRaw?: boolean, tx?: Transaction): Promise<string | null>;
71
- /**
72
- * Closes the shard file.
73
- */
74
- close(): Promise<void>;
75
- }
@@ -1,121 +0,0 @@
1
- import type { ShardOptions, ShardMetadata } from '../types';
2
- import { PageFileSystem } from './PageFileSystem';
3
- import { RowTableEngine } from './RowTableEngine';
4
- import { TextCodec } from '../utils/TextCodec';
5
- import { LockManager } from './transaction/LockManager';
6
- import { Transaction } from './transaction/Transaction';
7
- /**
8
- * Class for managing Shard files.
9
- */
10
- export declare class ShardAPI {
11
- protected file: string;
12
- protected fileHandle: number;
13
- readonly options: Required<ShardOptions>;
14
- protected readonly pfs: PageFileSystem;
15
- protected readonly rowTableEngine: RowTableEngine;
16
- protected readonly lockManager: LockManager;
17
- protected readonly textCodec: TextCodec;
18
- protected initialized: boolean;
19
- private txIdCounter;
20
- protected constructor(file: string, fileHandle: number, options: Required<ShardOptions>);
21
- /**
22
- * Verifies if the page file is a valid Shard file.
23
- * The metadata page must be located at the beginning of the Shard file.
24
- * @param fileHandle File handle
25
- * @returns Whether the page file is a valid Shard file
26
- */
27
- static VerifyFormat(fileHandle: number): boolean;
28
- /**
29
- * Fills missing options with default values.
30
- * @param options Options
31
- * @returns Options filled without omissions
32
- */
33
- static VerboseOptions(options?: ShardOptions): Required<ShardOptions>;
34
- /**
35
- * Initializes the database file.
36
- * The first page is initialized as the metadata page.
37
- * The second page is initialized as the first data page.
38
- * @param fileHandle File handle
39
- */
40
- static InitializeFile(fileHandle: number, options: Required<ShardOptions>): void;
41
- /**
42
- * Opens the database file. If the file does not exist, it initializes it.
43
- * @param file Database file path
44
- * @param options Options
45
- * @returns Shard instance
46
- */
47
- static Use(file: string, options?: ShardOptions): ShardAPI;
48
- /**
49
- * Initializes the shard instance.
50
- * Must be called before using the shard instance.
51
- * If not called, the shard instance cannot be used.
52
- */
53
- init(): Promise<void>;
54
- /**
55
- * Creates a transaction.
56
- * The created transaction object can be used to add or modify data.
57
- * A transaction must be terminated by calling either `commit` or `rollback`.
58
- * @returns Transaction object
59
- */
60
- createTransaction(): Transaction;
61
- /**
62
- * Runs a callback function within a transaction context.
63
- * If no transaction is provided, a new transaction is created.
64
- * The transaction is committed if the callback completes successfully,
65
- * or rolled back if an error occurs.
66
- * @param callback The callback function to run within the transaction context.
67
- * @param tx The transaction to use. If not provided, a new transaction is created.
68
- * @returns The result of the callback function.
69
- */
70
- private runWithDefault;
71
- /**
72
- * Retrieves metadata from the shard.
73
- * @returns Metadata of the shard.
74
- */
75
- getMetadata(): Promise<ShardMetadata>;
76
- /**
77
- * Inserts data. Returns the PK of the added row.
78
- * @param data Data to add
79
- * @param incrementRowCount Whether to increment the row count to metadata
80
- * @param tx Transaction
81
- * @returns PK of the added data
82
- */
83
- insert(data: string | Uint8Array, incrementRowCount?: boolean, tx?: Transaction): Promise<number>;
84
- /**
85
- * Inserts multiple data in batch.
86
- * If a transaction is not provided, it internally creates a single transaction to process.
87
- * @param dataList Array of data to add
88
- * @param incrementRowCount Whether to increment the row count to metadata
89
- * @param tx Transaction
90
- * @returns Array of PKs of the added data
91
- */
92
- insertBatch(dataList: (string | Uint8Array)[], incrementRowCount?: boolean, tx?: Transaction): Promise<number[]>;
93
- /**
94
- * Updates data.
95
- * @param pk PK of the data to update
96
- * @param data Data to update
97
- * @param tx Transaction
98
- */
99
- update(pk: number, data: string | Uint8Array, tx?: Transaction): Promise<void>;
100
- /**
101
- * Deletes data.
102
- * @param pk PK of the data to delete
103
- * @param decrementRowCount Whether to decrement the row count to metadata
104
- * @param tx Transaction
105
- */
106
- delete(pk: number, decrementRowCount?: boolean, tx?: Transaction): Promise<void>;
107
- /**
108
- * Selects data.
109
- * @param pk PK of the data to select
110
- * @param asRaw Whether to return the selected data as raw
111
- * @param tx Transaction
112
- * @returns Selected data
113
- */
114
- select(pk: number, asRaw: true, tx?: Transaction): Promise<Uint8Array | null>;
115
- select(pk: number, asRaw: false, tx?: Transaction): Promise<string | null>;
116
- select(pk: number, asRaw?: boolean, tx?: Transaction): Promise<string | null>;
117
- /**
118
- * Closes the shard file.
119
- */
120
- close(): Promise<void>;
121
- }