dataply 0.0.8 → 0.0.10

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
@@ -4626,9 +4626,6 @@ var HookallSync = class _HookallSync {
4626
4626
  return value;
4627
4627
  }
4628
4628
  };
4629
- function useHookallSync(target = HookallSync.Global) {
4630
- return new HookallSync(target);
4631
- }
4632
4629
 
4633
4630
  // src/core/VirtualFileSystem.ts
4634
4631
  var import_node_fs2 = __toESM(require("node:fs"));
@@ -5425,24 +5422,13 @@ var TextCodec = class _TextCodec {
5425
5422
  }
5426
5423
  };
5427
5424
 
5428
- // src/core/transaction/TxContext.ts
5429
- var import_node_async_hooks = require("node:async_hooks");
5430
- var storage = new import_node_async_hooks.AsyncLocalStorage();
5431
- var TxContext = {
5432
- run: (tx, callback) => {
5433
- return storage.run(tx, callback);
5434
- },
5435
- get: () => {
5436
- return storage.getStore();
5437
- }
5438
- };
5439
-
5440
5425
  // src/core/RowIndexStrategy.ts
5441
5426
  var RowIdentifierStrategy = class extends SerializeStrategyAsync {
5442
- constructor(order, pfs) {
5427
+ constructor(order, pfs, txContext) {
5443
5428
  super(order);
5444
5429
  this.order = order;
5445
5430
  this.pfs = pfs;
5431
+ this.txContext = txContext;
5446
5432
  this.factory = new PageManagerFactory();
5447
5433
  this.indexPageManger = this.factory.getManagerFromType(PageManager.CONSTANT.PAGE_TYPE_INDEX);
5448
5434
  this.codec = new TextCodec();
@@ -5452,12 +5438,12 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
5452
5438
  indexPageManger;
5453
5439
  codec;
5454
5440
  async id(isLeaf) {
5455
- const tx = TxContext.get();
5441
+ const tx = this.txContext.get();
5456
5442
  const pageId = await this.pfs.appendNewPage(PageManager.CONSTANT.PAGE_TYPE_INDEX, tx);
5457
5443
  return pageId.toString();
5458
5444
  }
5459
5445
  async read(id) {
5460
- const tx = TxContext.get();
5446
+ const tx = this.txContext.get();
5461
5447
  const pageId = +id;
5462
5448
  const page = await this.pfs.get(pageId, tx);
5463
5449
  const indexId = this.indexPageManger.getIndexId(page);
@@ -5489,7 +5475,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
5489
5475
  return res;
5490
5476
  }
5491
5477
  async write(id, node) {
5492
- const tx = TxContext.get();
5478
+ const tx = this.txContext.get();
5493
5479
  const pageId = +id;
5494
5480
  const page = await this.pfs.get(pageId, tx);
5495
5481
  if (node.leaf) {
@@ -5524,7 +5510,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
5524
5510
  await this.pfs.setPage(pageId, page, tx);
5525
5511
  }
5526
5512
  async delete(id) {
5527
- const tx = TxContext.get();
5513
+ const tx = this.txContext.get();
5528
5514
  const manager = this.factory.getManagerFromType(PageManager.CONSTANT.PAGE_TYPE_INDEX);
5529
5515
  let pageId = +id;
5530
5516
  while (true) {
@@ -5538,7 +5524,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
5538
5524
  }
5539
5525
  }
5540
5526
  async readHead() {
5541
- const tx = TxContext.get();
5527
+ const tx = this.txContext.get();
5542
5528
  const metadataPage = await this.pfs.getMetadata(tx);
5543
5529
  const manager = this.factory.getManager(metadataPage);
5544
5530
  const rootIndexPageId = manager.getRootIndexPageId(metadataPage);
@@ -5552,7 +5538,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
5552
5538
  };
5553
5539
  }
5554
5540
  async writeHead(head) {
5555
- const tx = TxContext.get();
5541
+ const tx = this.txContext.get();
5556
5542
  const { root, order } = head;
5557
5543
  if (root === null) {
5558
5544
  throw new Error("");
@@ -5620,8 +5606,9 @@ var KeyManager = class {
5620
5606
 
5621
5607
  // src/core/RowTableEngine.ts
5622
5608
  var RowTableEngine = class {
5623
- constructor(pfs, options) {
5609
+ constructor(pfs, txContext, options) {
5624
5610
  this.pfs = pfs;
5611
+ this.txContext = txContext;
5625
5612
  this.options = options;
5626
5613
  this.factory = new PageManagerFactory();
5627
5614
  this.metadataPageManager = this.factory.getManagerFromType(MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA);
@@ -5634,7 +5621,7 @@ var RowTableEngine = class {
5634
5621
  this.maxBodySize = this.pfs.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER;
5635
5622
  this.order = this.getOptimalOrder(pfs.pageSize, IndexPageManager.CONSTANT.SIZE_KEY, IndexPageManager.CONSTANT.SIZE_VALUE);
5636
5623
  this.bptree = new BPTreeAsync(
5637
- new RowIdentifierStrategy(this.order, pfs),
5624
+ new RowIdentifierStrategy(this.order, pfs, txContext),
5638
5625
  new NumericComparator(),
5639
5626
  {
5640
5627
  capacity: this.options.pageCacheCapacity
@@ -6064,7 +6051,8 @@ var Transaction = class {
6064
6051
  * @param vfs VFS instance
6065
6052
  * @param lockManager LockManager instance
6066
6053
  */
6067
- constructor(id, vfs, lockManager) {
6054
+ constructor(id, context, vfs, lockManager) {
6055
+ this.context = context;
6068
6056
  this.vfs = vfs;
6069
6057
  this.lockManager = lockManager;
6070
6058
  this.id = id;
@@ -6173,7 +6161,7 @@ var Transaction = class {
6173
6161
  async commit() {
6174
6162
  await this.vfs.prepareCommit(this);
6175
6163
  await this.vfs.finalizeCommit(this);
6176
- await TxContext.run(this, async () => {
6164
+ await this.context.run(this, async () => {
6177
6165
  for (const hook of this.commitHooks) {
6178
6166
  await hook();
6179
6167
  }
@@ -6214,15 +6202,25 @@ var Transaction = class {
6214
6202
  }
6215
6203
  };
6216
6204
 
6205
+ // src/core/transaction/TxContext.ts
6206
+ var import_node_async_hooks = require("node:async_hooks");
6207
+ var TransactionContext = class {
6208
+ storage = new import_node_async_hooks.AsyncLocalStorage();
6209
+ run(tx, callback) {
6210
+ return this.storage.run(tx, callback);
6211
+ }
6212
+ get() {
6213
+ return this.storage.getStore();
6214
+ }
6215
+ };
6216
+
6217
6217
  // src/core/DataplyAPI.ts
6218
6218
  var DataplyAPI = class {
6219
6219
  constructor(file, options) {
6220
6220
  this.file = file;
6221
- this.hook = {
6222
- sync: useHookallSync(this),
6223
- async: useHookall(this)
6224
- };
6221
+ this.hook = useHookall(this);
6225
6222
  this.options = this.verboseOptions(options);
6223
+ this.isNewlyCreated = !import_node_fs3.default.existsSync(file);
6226
6224
  this.fileHandle = this.createOrOpen(file, this.options);
6227
6225
  this.pfs = new PageFileSystem(
6228
6226
  this.fileHandle,
@@ -6231,19 +6229,36 @@ var DataplyAPI = class {
6231
6229
  this.options.wal
6232
6230
  );
6233
6231
  this.textCodec = new TextCodec();
6232
+ this.txContext = new TransactionContext();
6234
6233
  this.lockManager = new LockManager();
6235
- this.rowTableEngine = new RowTableEngine(this.pfs, this.options);
6234
+ this.rowTableEngine = new RowTableEngine(this.pfs, this.txContext, this.options);
6236
6235
  this.initialized = false;
6237
6236
  this.txIdCounter = 0;
6238
6237
  }
6238
+ /**
6239
+ * These are not the same options that were used when the database was created.
6240
+ * They are simply the options received when the instance was created.
6241
+ * If you want to retrieve the options used during database creation, use `getMetadata()` instead.
6242
+ */
6239
6243
  options;
6244
+ /** File handle. Database file descriptor */
6240
6245
  fileHandle;
6246
+ /** Page file system. Used for managing pages. If you know what it is, you can skip this. */
6241
6247
  pfs;
6248
+ /** Row table engine. Used for managing rows. If you know what it is, you can skip this. */
6242
6249
  rowTableEngine;
6250
+ /** Lock manager. Used for managing transactions */
6243
6251
  lockManager;
6252
+ /** Text codec. Used for encoding and decoding text data */
6244
6253
  textCodec;
6254
+ /** Transaction context */
6255
+ txContext;
6256
+ /** Hook */
6245
6257
  hook;
6258
+ /** Whether the database was initialized via `init()` */
6246
6259
  initialized;
6260
+ /** Whether the database was created this time. */
6261
+ isNewlyCreated;
6247
6262
  txIdCounter;
6248
6263
  /**
6249
6264
  * Verifies if the page file is a valid Dataply file.
@@ -6280,47 +6295,45 @@ var DataplyAPI = class {
6280
6295
  * @param fileHandle File handle
6281
6296
  */
6282
6297
  initializeFile(file, fileHandle, options) {
6283
- this.hook.sync.trigger("create", void 0, () => {
6284
- const metadataPageManager = new MetadataPageManager();
6285
- const bitmapPageManager = new BitmapPageManager();
6286
- const dataPageManager = new DataPageManager();
6287
- const metadataPage = new Uint8Array(options.pageSize);
6288
- const dataPage = new Uint8Array(options.pageSize);
6289
- metadataPageManager.initial(
6290
- metadataPage,
6291
- MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA,
6292
- 0,
6293
- 0,
6294
- options.pageSize - MetadataPageManager.CONSTANT.SIZE_PAGE_HEADER
6295
- );
6296
- metadataPageManager.setMagicString(metadataPage);
6297
- metadataPageManager.setPageSize(metadataPage, options.pageSize);
6298
- metadataPageManager.setRootIndexPageId(metadataPage, -1);
6299
- metadataPageManager.setBitmapPageId(metadataPage, 1);
6300
- metadataPageManager.setLastInsertPageId(metadataPage, 2);
6301
- metadataPageManager.setPageCount(metadataPage, 3);
6302
- metadataPageManager.setFreePageId(metadataPage, -1);
6303
- const bitmapPage = new Uint8Array(options.pageSize);
6304
- bitmapPageManager.initial(
6305
- bitmapPage,
6306
- BitmapPageManager.CONSTANT.PAGE_TYPE_BITMAP,
6307
- 1,
6308
- -1,
6309
- options.pageSize - BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER
6310
- );
6311
- dataPageManager.initial(
6312
- dataPage,
6313
- DataPageManager.CONSTANT.PAGE_TYPE_DATA,
6314
- 2,
6315
- -1,
6316
- options.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER
6317
- );
6318
- import_node_fs3.default.appendFileSync(fileHandle, new Uint8Array([
6319
- ...metadataPage,
6320
- ...bitmapPage,
6321
- ...dataPage
6322
- ]));
6323
- }, file, fileHandle, options);
6298
+ const metadataPageManager = new MetadataPageManager();
6299
+ const bitmapPageManager = new BitmapPageManager();
6300
+ const dataPageManager = new DataPageManager();
6301
+ const metadataPage = new Uint8Array(options.pageSize);
6302
+ const dataPage = new Uint8Array(options.pageSize);
6303
+ metadataPageManager.initial(
6304
+ metadataPage,
6305
+ MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA,
6306
+ 0,
6307
+ 0,
6308
+ options.pageSize - MetadataPageManager.CONSTANT.SIZE_PAGE_HEADER
6309
+ );
6310
+ metadataPageManager.setMagicString(metadataPage);
6311
+ metadataPageManager.setPageSize(metadataPage, options.pageSize);
6312
+ metadataPageManager.setRootIndexPageId(metadataPage, -1);
6313
+ metadataPageManager.setBitmapPageId(metadataPage, 1);
6314
+ metadataPageManager.setLastInsertPageId(metadataPage, 2);
6315
+ metadataPageManager.setPageCount(metadataPage, 3);
6316
+ metadataPageManager.setFreePageId(metadataPage, -1);
6317
+ const bitmapPage = new Uint8Array(options.pageSize);
6318
+ bitmapPageManager.initial(
6319
+ bitmapPage,
6320
+ BitmapPageManager.CONSTANT.PAGE_TYPE_BITMAP,
6321
+ 1,
6322
+ -1,
6323
+ options.pageSize - BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER
6324
+ );
6325
+ dataPageManager.initial(
6326
+ dataPage,
6327
+ DataPageManager.CONSTANT.PAGE_TYPE_DATA,
6328
+ 2,
6329
+ -1,
6330
+ options.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER
6331
+ );
6332
+ import_node_fs3.default.appendFileSync(fileHandle, new Uint8Array([
6333
+ ...metadataPage,
6334
+ ...bitmapPage,
6335
+ ...dataPage
6336
+ ]));
6324
6337
  }
6325
6338
  /**
6326
6339
  * Opens the database file. If the file does not exist, it initializes it.
@@ -6367,11 +6380,12 @@ var DataplyAPI = class {
6367
6380
  if (this.initialized) {
6368
6381
  return;
6369
6382
  }
6370
- await this.runWithDefault(() => {
6371
- return this.hook.async.trigger("init", void 0, async () => {
6383
+ await this.runWithDefault(async (tx) => {
6384
+ await this.hook.trigger("init", tx, async (tx2) => {
6372
6385
  await this.rowTableEngine.init();
6373
6386
  this.initialized = true;
6374
- });
6387
+ return tx2;
6388
+ }, this.isNewlyCreated);
6375
6389
  });
6376
6390
  }
6377
6391
  /**
@@ -6381,7 +6395,12 @@ var DataplyAPI = class {
6381
6395
  * @returns Transaction object
6382
6396
  */
6383
6397
  createTransaction() {
6384
- return new Transaction(++this.txIdCounter, this.pfs.vfsInstance, this.lockManager);
6398
+ return new Transaction(
6399
+ ++this.txIdCounter,
6400
+ this.txContext,
6401
+ this.pfs.vfsInstance,
6402
+ this.lockManager
6403
+ );
6385
6404
  }
6386
6405
  /**
6387
6406
  * Runs a callback function within a transaction context.
@@ -6397,7 +6416,7 @@ var DataplyAPI = class {
6397
6416
  if (!tx) {
6398
6417
  tx = this.createTransaction();
6399
6418
  }
6400
- const [error, result] = await catchPromise(TxContext.run(tx, () => callback(tx)));
6419
+ const [error, result] = await catchPromise(this.txContext.run(tx, () => callback(tx)));
6401
6420
  if (error) {
6402
6421
  if (isInternalTx) {
6403
6422
  await tx.rollback();
@@ -6530,7 +6549,7 @@ var DataplyAPI = class {
6530
6549
  if (!this.initialized) {
6531
6550
  throw new Error("Dataply instance is not initialized");
6532
6551
  }
6533
- return this.hook.async.trigger("close", void 0, async () => {
6552
+ return this.hook.trigger("close", void 0, async () => {
6534
6553
  await this.pfs.close();
6535
6554
  import_node_fs3.default.closeSync(this.fileHandle);
6536
6555
  });
@@ -1,15 +1,13 @@
1
1
  import type { DataplyOptions, DataplyMetadata } from '../types';
2
- import { type IHookall, type IHookallSync } from 'hookall';
2
+ import { type IHookall } from 'hookall';
3
3
  import { PageFileSystem } from './PageFileSystem';
4
4
  import { RowTableEngine } from './RowTableEngine';
5
5
  import { TextCodec } from '../utils/TextCodec';
6
6
  import { LockManager } from './transaction/LockManager';
7
7
  import { Transaction } from './transaction/Transaction';
8
- interface DataplyAPISyncHook {
9
- create: (_: void, file: string, fileHandle: number, options: Required<DataplyOptions>) => void;
10
- }
8
+ import { TransactionContext } from './transaction/TxContext';
11
9
  interface DataplyAPIAsyncHook {
12
- init: () => Promise<void>;
10
+ init: (tx: Transaction, isNewlyCreated: boolean) => Promise<Transaction>;
13
11
  close: () => Promise<void>;
14
12
  }
15
13
  /**
@@ -17,17 +15,30 @@ interface DataplyAPIAsyncHook {
17
15
  */
18
16
  export declare class DataplyAPI {
19
17
  protected readonly file: string;
18
+ /**
19
+ * These are not the same options that were used when the database was created.
20
+ * They are simply the options received when the instance was created.
21
+ * If you want to retrieve the options used during database creation, use `getMetadata()` instead.
22
+ */
20
23
  readonly options: Required<DataplyOptions>;
24
+ /** File handle. Database file descriptor */
21
25
  protected readonly fileHandle: number;
26
+ /** Page file system. Used for managing pages. If you know what it is, you can skip this. */
22
27
  protected readonly pfs: PageFileSystem;
28
+ /** Row table engine. Used for managing rows. If you know what it is, you can skip this. */
23
29
  protected readonly rowTableEngine: RowTableEngine;
30
+ /** Lock manager. Used for managing transactions */
24
31
  protected readonly lockManager: LockManager;
32
+ /** Text codec. Used for encoding and decoding text data */
25
33
  protected readonly textCodec: TextCodec;
26
- protected readonly hook: {
27
- sync: IHookallSync<DataplyAPISyncHook>;
28
- async: IHookall<DataplyAPIAsyncHook>;
29
- };
34
+ /** Transaction context */
35
+ protected readonly txContext: TransactionContext;
36
+ /** Hook */
37
+ protected readonly hook: IHookall<DataplyAPIAsyncHook>;
38
+ /** Whether the database was initialized via `init()` */
30
39
  protected initialized: boolean;
40
+ /** Whether the database was created this time. */
41
+ private readonly isNewlyCreated;
31
42
  private txIdCounter;
32
43
  constructor(file: string, options: DataplyOptions);
33
44
  /**
@@ -3,14 +3,16 @@ import { SerializeStrategyAsync } from 'serializable-bptree';
3
3
  import { PageFileSystem } from './PageFileSystem';
4
4
  import { IndexPageManager, PageManagerFactory } from './Page';
5
5
  import { TextCodec } from '../utils/TextCodec';
6
+ import { TransactionContext } from './transaction/TxContext';
6
7
  export declare class RowIdentifierStrategy extends SerializeStrategyAsync<number, number> {
7
8
  readonly order: number;
8
9
  protected readonly pfs: PageFileSystem;
10
+ protected readonly txContext: TransactionContext;
9
11
  protected rootPageId: number;
10
12
  protected factory: PageManagerFactory;
11
13
  protected indexPageManger: IndexPageManager;
12
14
  protected codec: TextCodec;
13
- constructor(order: number, pfs: PageFileSystem);
15
+ constructor(order: number, pfs: PageFileSystem, txContext: TransactionContext);
14
16
  id(isLeaf: boolean): Promise<string>;
15
17
  read(id: string): Promise<BPTreeNode<number, number>>;
16
18
  write(id: string, node: BPTreeNode<number, number>): Promise<void>;
@@ -5,8 +5,10 @@ import { Row } from './Row';
5
5
  import { KeyManager } from './KeyManager';
6
6
  import { PageManagerFactory } from './Page';
7
7
  import { Transaction } from './transaction/Transaction';
8
+ import { TransactionContext } from './transaction/TxContext';
8
9
  export declare class RowTableEngine {
9
10
  protected readonly pfs: PageFileSystem;
11
+ protected readonly txContext: TransactionContext;
10
12
  protected readonly options: Required<DataplyOptions>;
11
13
  protected readonly bptree: BPTreeAsync<number, number>;
12
14
  protected readonly order: number;
@@ -20,7 +22,7 @@ export declare class RowTableEngine {
20
22
  private readonly ridBuffer;
21
23
  private readonly pageIdBuffer;
22
24
  private initialized;
23
- constructor(pfs: PageFileSystem, options: Required<DataplyOptions>);
25
+ constructor(pfs: PageFileSystem, txContext: TransactionContext, options: Required<DataplyOptions>);
24
26
  /**
25
27
  * Initializes the B+ Tree.
26
28
  */
@@ -1,12 +1,14 @@
1
1
  import { LockManager } from './LockManager';
2
2
  import { VirtualFileSystem } from '../VirtualFileSystem';
3
+ import { TransactionContext } from './TxContext';
3
4
  /**
4
5
  * Transaction class.
5
6
  * Manages the lifecycle and resources of a database transaction.
6
7
  */
7
8
  export declare class Transaction {
8
- private vfs;
9
- private lockManager;
9
+ readonly context: TransactionContext;
10
+ private readonly vfs;
11
+ private readonly lockManager;
10
12
  /** Transaction ID */
11
13
  readonly id: number;
12
14
  /** List of held lock IDs (LOCK_ID) */
@@ -26,7 +28,7 @@ export declare class Transaction {
26
28
  * @param vfs VFS instance
27
29
  * @param lockManager LockManager instance
28
30
  */
29
- constructor(id: number, vfs: VirtualFileSystem, lockManager: LockManager);
31
+ constructor(id: number, context: TransactionContext, vfs: VirtualFileSystem, lockManager: LockManager);
30
32
  /**
31
33
  * Registers a commit hook.
32
34
  * @param hook Function to execute
@@ -1,5 +1,6 @@
1
1
  import { Transaction } from './Transaction';
2
- export declare const TxContext: {
3
- run: <T>(tx: Transaction, callback: () => T) => T;
4
- get: () => Transaction | undefined;
5
- };
2
+ export declare class TransactionContext {
3
+ private readonly storage;
4
+ run<T>(tx: Transaction, callback: () => T): T;
5
+ get(): Transaction | undefined;
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
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>",
package/readme.md CHANGED
@@ -178,6 +178,8 @@ Rolls back all added transactions.
178
178
 
179
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.
180
180
 
181
+ For a detailed guide and examples on how to extend Dataply using Hooks, see [Extending Dataply Guide](docs/extension.md).
182
+
181
183
  ### Using DataplyAPI
182
184
 
183
185
  ```typescript
@@ -222,7 +224,7 @@ graph TD
222
224
  - **Fixed-size Pages**: All data is managed in fixed-size units (default 8KB) called pages.
223
225
  - **VFS Cache**: Minimizes disk I/O by caching frequently accessed pages in memory.
224
226
  - **Dirty Page Tracking**: Tracks modified pages (Dirty) to synchronize them with disk efficiently only at the time of commit.
225
- - **Detailed Structure**: For technical details on the physical layout, see [structure.md](structure.md).
227
+ - **Detailed Structure**: For technical details on the physical layout, see [structure.md](docs/structure.md).
226
228
 
227
229
  #### Page & Row Layout
228
230
  Dataply uses a **Slotted Page** architecture to manage records efficiently: