albedo-node 0.7.2 โ†’ 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ This package wraps the core database engine with a concise TypeScript API, ships
9
9
  - ๐Ÿงฑ Access to the full Albedo bucket API from Node.js or Bun
10
10
  - ๐Ÿ“ฆ Precompiled native modules for Linux (glibc & musl), macOS, and arm64/x64
11
11
  - ๐Ÿ” Generator-based iterators for queries, transforms, and live subscriptions
12
- - ๐Ÿ”„ Built-in replication callback and apply mechanisms
12
+ - ๐Ÿ”„ WAL-based replication helpers with explicit cursors and batches
13
13
  - ๐Ÿงช TypeScript typings and Bun-based test suite
14
14
 
15
15
  ## Installation
@@ -75,6 +75,47 @@ bucket.close();
75
75
 
76
76
  You can chain multiple `where` calls or combine them with `sortBy` and `sector`. For quick one-off predicates, the standalone `where(field, filter)` helper returns a `Query` instance that slots into any method accepting a query.
77
77
 
78
+ Logical groups are available as both instance methods and static constructors:
79
+
80
+ ```ts
81
+ import { Query, where } from "albedo-node";
82
+
83
+ const visibleUsers = Query.or(
84
+ where("role", "admin"),
85
+ Query.and(
86
+ where("role", "user"),
87
+ where("verified", true),
88
+ ),
89
+ ).nor(
90
+ where("deleted", true),
91
+ );
92
+
93
+ for (const doc of bucket.list(visibleUsers)) {
94
+ console.log(doc);
95
+ }
96
+ ```
97
+
98
+ This produces a query equivalent to:
99
+
100
+ ```ts
101
+ {
102
+ query: {
103
+ $or: [
104
+ { role: "admin" },
105
+ {
106
+ $and: [
107
+ { role: "user" },
108
+ { verified: true },
109
+ ],
110
+ },
111
+ ],
112
+ $nor: [
113
+ { deleted: true },
114
+ ],
115
+ },
116
+ }
117
+ ```
118
+
78
119
  ### Transforming documents in place
79
120
 
80
121
  ```ts
@@ -131,25 +172,36 @@ Notes:
131
172
  ### Replication
132
173
 
133
174
  ```ts
134
- const primary = Bucket.open("./primary.bucket");
135
- const replica = Bucket.open("./replica.bucket");
136
-
137
- const batches: Uint8Array[] = [];
138
- primary.setReplicationCallback((data) => {
139
- batches.push(data);
175
+ const primary = Bucket.open("./primary.bucket", {
176
+ wal: true,
177
+ write_durability: "all",
140
178
  });
179
+ const replica = Bucket.open("./replica.bucket", {
180
+ wal: true,
181
+ write_durability: "all",
182
+ });
183
+
184
+ const cursor = primary.replicationCursor();
141
185
 
142
186
  primary.insert({ name: "Replica", version: 1 });
143
- primary.close();
144
187
 
145
- // Apply the first batch to the replica bucket
146
- if (batches.length > 0) {
147
- replica.applyReplicationBatch(batches[0]);
188
+ const batch = primary.readReplicationBatch(cursor);
189
+ if (batch) {
190
+ const nextCursor = replica.applyReplicationBatch(batch);
191
+ console.log(nextCursor.next_frame_index);
148
192
  }
149
193
 
194
+ primary.close();
150
195
  replica.close();
151
196
  ```
152
197
 
198
+ Notes:
199
+
200
+ - Replication requires WAL mode to be enabled on both buckets.
201
+ - `replicationCursor()` snapshots the current position in the WAL stream.
202
+ - `readReplicationBatch(cursor, maxBytes?)` returns `null` when there is nothing new to send.
203
+ - `applyReplicationBatch(batch)` applies the batch and returns the replica's new cursor.
204
+
153
205
  ## Query objects and BSON payloads
154
206
 
155
207
  Most bucket operations accept either:
@@ -162,8 +214,22 @@ Regardless of which form you use, the structure mirrors the underlying Albedo qu
162
214
 
163
215
  - `{ query: { field: value } }` โ€” equality filters
164
216
  - `{ query: { age: { "$gt": 40 } } }` โ€” comparison operators
217
+ - `{ query: { $or: [{ role: "admin" }, { public: true }], deleted: false } }` โ€” logical groups mixed with leaf filters
165
218
  - `{ query: { _id: someId }, sector: { limit: 10, offset: 0 } }` โ€” pagination controls
166
219
 
220
+ Supported field operators in this binding include:
221
+
222
+ - `$eq`, `$ne`, `$lt`, `$lte`, `$gt`, `$gte`
223
+ - `$in`, `$between`
224
+ - `$startsWith`, `$endsWith`
225
+ - `$exists`, `$notExists`
226
+
227
+ Logical operators supported inside `query` are:
228
+
229
+ - `$or`
230
+ - `$and`
231
+ - `$nor`
232
+
167
233
  For the exhaustive list of operators and document shapes, consult the [Albedo Query reference](https://github.com/klirix/albedo#readme). Whatever BSON document the core engine accepts can be provided here either as a plain object or as prebuilt BSON bytes.
168
234
 
169
235
  ### ObjectId support
@@ -181,22 +247,37 @@ Serialized documents that contain `_id` fields with a BSON ObjectId will be revi
181
247
  ## API reference
182
248
 
183
249
  - `default` export โ€” raw Albedo native module
184
- - `ObjectId`, `serialize`, `deserialize`, `open`, `close`, `insert`, `delete`, `list`, `transform`, `setReplicationCallback`, etc.
250
+ - `ObjectId`, `serialize`, `deserialize`, `open`, `open_with_options`, `close`, `insert`, `checkpoint`, `delete`, `list`, `transform`, `replicationCursor`, `readReplicationBatch`, `applyReplicationBatch`, etc.
185
251
  - `BSON.serialize(value)` / `BSON.deserialize(bytes)` โ€” helper wrappers
186
252
  - `Bucket`
187
- - `static open(path: string): Bucket`
253
+ - `static open(path: string, options?: OpenBucketOptions): Bucket`
188
254
  - `insert(doc: object | Uint8Array)`
255
+ - `checkpoint()`
189
256
  - `delete(query?: object | Query)`
190
257
  - `list<T>(query?: object | Query): Generator<T>`
191
258
  - `subscribe<T>(query?: object | Query, options?: { pollingTimeout?: number; batchSize?: number }): AsyncGenerator<SubscriptionEvent<T>>`
192
259
  - `transformIterator<T>(query?: object | Query): Generator<T, undefined, object | null>`
260
+ - `transform<T>(query, fn)` / `update<T>(query, fn)`
261
+ - `all<T>(query?)` / `one<T>(query?)`
262
+ - `beginTransaction()` / `tx(fn)`
193
263
  - `ensureIndex(name: string, options: { unique: boolean; sparse: boolean; reverse: boolean })`
194
264
  - `dropIndex(name: string)`
195
265
  - `indexes: Record<string, { name: string; unique: boolean; sparse: boolean; reverse: boolean }>`
196
- - `setReplicationCallback(cb: (data: Uint8Array) => void)`
197
- - `applyReplicationBatch(batch: Uint8Array)`
266
+ - `replicationCursor(): { generation: bigint; next_frame_index: bigint }`
267
+ - `readReplicationBatch(cursor, maxBytes?): Uint8Array | null`
268
+ - `applyReplicationBatch(batch: Uint8Array): { generation: bigint; next_frame_index: bigint }`
198
269
  - accepts raw BSON `Uint8Array` payloads anywhere a query or document object is expected
199
270
 
271
+ - `OpenBucketOptions`
272
+ - `buildIdIndex?: boolean`
273
+ - `mode?: string`
274
+ - `auto_vaccuum?: boolean`
275
+ - `page_cache_capacity?: number`
276
+ - `wal?: boolean`
277
+ - `write_durability?: "all" | "manual" | { periodic: number }`
278
+ - `read_durability?: "shared" | "process"`
279
+ - `wal_auto_checkpoint?: (defaults to 1000 pages)`
280
+
200
281
  - `SubscriptionEvent<T>`
201
282
  - `seqno: bigint`
202
283
  - `op: "insert" | "update" | "delete"`
@@ -206,8 +287,14 @@ Serialized documents that contain `_id` fields with a BSON ObjectId will be revi
206
287
 
207
288
  - `Query`
208
289
  - `where(field, filter)` chains field predicates (e.g. `{ $eq: value }`, `{ $gt: 10 }`)
290
+ - `or(...clauses)` adds an `$or` group
291
+ - `and(...clauses)` adds an `$and` group
292
+ - `nor(...clauses)` adds a `$nor` group
209
293
  - `sortBy(field, direction?)` sets sort order
210
294
  - `sector(offset?, limit?)` applies pagination window
295
+ - `static or(...clauses): Query` creates a query with an `$or` group
296
+ - `static and(...clauses): Query` creates a query with an `$and` group
297
+ - `static nor(...clauses): Query` creates a query with a `$nor` group
211
298
 
212
299
  - `where(field, filter): Query` โ€” convenience helper that creates a single-field `Query`
213
300
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  type ByteBuffer = Uint8Array;
2
2
  type BucketHandle = object;
3
+ type TransactionHandle = object;
3
4
  type ListIteratorHandle = object;
4
5
  type TransformIteratorHandle = object;
5
6
  type SubscriptionHandle = object;
7
+ export interface ReplicationCursor {
8
+ generation: bigint;
9
+ next_frame_index: bigint;
10
+ }
6
11
  interface IndexOptions {
7
12
  unique: boolean;
8
13
  sparse: boolean;
@@ -69,19 +74,28 @@ interface AlbedoModule {
69
74
  listClose(cursor: ListIteratorHandle): void;
70
75
  listData(cursor: ListIteratorHandle): unknown | null;
71
76
  insert(bucket: BucketHandle, doc: ByteBuffer | object): void;
77
+ checkpoint(bucket: BucketHandle): void;
78
+ transactionBegin(bucket: BucketHandle): TransactionHandle;
79
+ transactionInsert(tx: TransactionHandle, doc: ByteBuffer | object): void;
72
80
  ensureIndex(bucket: BucketHandle, name: string, options: IndexOptions): void;
73
81
  listIndexes(bucket: BucketHandle): Record<string, IndexInfo>;
74
82
  dropIndex(bucket: BucketHandle, name: string): void;
75
83
  delete(bucket: BucketHandle, query: object): void;
84
+ transactionDelete(tx: TransactionHandle, query: object): void;
76
85
  transform(bucket: BucketHandle, query: object): TransformIteratorHandle;
86
+ transactionTransform(tx: TransactionHandle, query: object): TransformIteratorHandle;
77
87
  transformClose(iter: TransformIteratorHandle): void;
78
88
  transformData(iter: TransformIteratorHandle): unknown | null;
79
89
  transformApply(iter: TransformIteratorHandle, replace: ByteBuffer | object | null): void;
90
+ transactionCommit(tx: TransactionHandle): void;
91
+ transactionRollback(tx: TransactionHandle): void;
92
+ transactionClose(tx: TransactionHandle): void;
80
93
  subscribe(bucket: BucketHandle, query: object): SubscriptionHandle;
81
94
  subscribePoll<T = unknown>(sub: SubscriptionHandle, maxEvents: number): SubscriptionBatch<T> | null;
82
95
  subscribeClose(sub: SubscriptionHandle): void;
83
- setReplicationCallback(bucket: BucketHandle, callback: (data: Uint8Array) => void): void;
84
- applyReplicationBatch(bucket: BucketHandle, data: ByteBuffer): void;
96
+ replicationCursor(bucket: BucketHandle): ReplicationCursor;
97
+ readReplicationBatch(bucket: BucketHandle, cursor: ReplicationCursor, maxBytes: number): ByteBuffer | null;
98
+ applyReplicationBatch(bucket: BucketHandle, data: ByteBuffer): ReplicationCursor;
85
99
  }
86
100
  export declare const albedo: AlbedoModule;
87
101
  export default albedo;
@@ -99,6 +113,50 @@ export declare const BSON: {
99
113
  * ```
100
114
  */
101
115
  export declare const ObjectId: ObjectIdConstructor;
116
+ type QueryInput = object | Query;
117
+ type TransformReplacement<T extends object> = T | ByteBuffer | null;
118
+ /**
119
+ * Wrapper around a native transaction handle providing
120
+ * write operations that are committed or rolled back together.
121
+ */
122
+ export declare class Transaction {
123
+ private handle;
124
+ constructor(handle: object);
125
+ private get nativeHandle();
126
+ /**
127
+ * Insert a document or raw byte buffer into the transaction.
128
+ */
129
+ insert(doc: object | ByteBuffer): void;
130
+ /**
131
+ * Delete documents matching the query from the transaction.
132
+ */
133
+ delete(query?: QueryInput): void;
134
+ /**
135
+ * Generator that allows reading and modifying matching documents
136
+ * within the transaction.
137
+ */
138
+ transformIterator<T extends object>(query?: QueryInput): Generator<T, undefined, TransformReplacement<T>>;
139
+ /**
140
+ * Apply a transformation function to matching documents in the transaction.
141
+ */
142
+ transform<T extends object>(query: QueryInput | undefined, fn: (doc: T) => TransformReplacement<T>): void;
143
+ /**
144
+ * Alias for `transform` that reads more naturally for document updates.
145
+ */
146
+ update<T extends object>(query: QueryInput | undefined, fn: (doc: T) => TransformReplacement<T>): void;
147
+ /**
148
+ * Commit the transaction.
149
+ */
150
+ commit(): void;
151
+ /**
152
+ * Roll back the transaction.
153
+ */
154
+ rollback(): void;
155
+ /**
156
+ * Close the transaction and release native resources.
157
+ */
158
+ close(): void;
159
+ }
102
160
  /**
103
161
  * Wrapper around a native Albedo bucket handle providing
104
162
  * methods for CRUD operations, indexing, iteration, and
@@ -157,6 +215,21 @@ export declare class Bucket {
157
215
  * ```
158
216
  */
159
217
  insert(doc: object | ByteBuffer): void;
218
+ /**
219
+ * Flush buffered bucket state through the native checkpoint mechanism.
220
+ */
221
+ checkpoint(): void;
222
+ /**
223
+ * Begin a manual transaction on this bucket.
224
+ */
225
+ beginTransaction(): Transaction;
226
+ /**
227
+ * Run a callback inside a transaction and commit it on success.
228
+ *
229
+ * If the callback throws, the transaction is rolled back before the
230
+ * original error is re-thrown.
231
+ */
232
+ tx<T>(fn: (tx: Transaction) => T): T;
160
233
  /**
161
234
  * Delete documents matching the query. If no query is provided,
162
235
  * all documents will be removed.
@@ -275,7 +348,7 @@ export declare class Bucket {
275
348
  * }
276
349
  * ```
277
350
  */
278
- transformIterator<T>(query?: object | Query): Generator<T, undefined, null | object>;
351
+ transformIterator<T extends object>(query?: object | Query): Generator<T, undefined, TransformReplacement<T>>;
279
352
  /**
280
353
  * Apply a transformation function to each document matching the
281
354
  * provided query. The predicate receives the current document and
@@ -294,19 +367,13 @@ export declare class Bucket {
294
367
  * });
295
368
  * ```
296
369
  */
297
- transform<T extends object>(query: object | Query | undefined, fn: (doc: T) => T | null): void;
370
+ transform<T extends object>(query: object | Query | undefined, fn: (doc: T) => TransformReplacement<T>): void;
298
371
  /**
299
- * Register a callback to receive replication data produced by the
300
- * bucket.
301
- * @param callback - invoked with raw replication bytes
302
- * @example
303
- * ```ts
304
- * bucket.setReplicationCallback(bytes => {
305
- * console.log('got replication', bytes.length);
306
- * });
307
- * ```
372
+ * Alias for `transform` that reads more naturally for document updates.
308
373
  */
309
- setReplicationCallback(callback: (data: Uint8Array) => void): void;
374
+ update<T extends object>(query: object | Query | undefined, fn: (doc: T) => TransformReplacement<T>): void;
375
+ replicationCursor(): ReplicationCursor;
376
+ readReplicationBatch(cursor: ReplicationCursor, maxBytes?: number): Uint8Array | null;
310
377
  /**
311
378
  * Apply a batch of replication operations to this bucket.
312
379
  * @param data - bytes produced by another bucket's replication
@@ -315,7 +382,7 @@ export declare class Bucket {
315
382
  * bucket.applyReplicationBatch(remoteBytes);
316
383
  * ```
317
384
  */
318
- applyReplicationBatch(data: Uint8Array): void;
385
+ applyReplicationBatch(data: Uint8Array): ReplicationCursor;
319
386
  }
320
387
  type BSONValue = any;
321
388
  type FilterOperators = {
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Query = exports.Bucket = exports.ObjectId = exports.BSON = exports.albedo = void 0;
3
+ exports.Query = exports.Bucket = exports.Transaction = exports.ObjectId = exports.BSON = exports.albedo = void 0;
4
4
  exports.where = where;
5
5
  const detect_libc_1 = require("detect-libc");
6
6
  function getNativeBinding() {
@@ -47,6 +47,106 @@ exports.BSON = {
47
47
  * ```
48
48
  */
49
49
  exports.ObjectId = exports.albedo.ObjectId;
50
+ function convertToQuery(query) {
51
+ if (query instanceof Query) {
52
+ return query.query;
53
+ }
54
+ return query || {};
55
+ }
56
+ function* iterateTransform(iter) {
57
+ try {
58
+ let data;
59
+ while ((data = exports.albedo.transformData(iter)) !== undefined) {
60
+ const newDoc = yield data;
61
+ exports.albedo.transformApply(iter, newDoc);
62
+ }
63
+ }
64
+ finally {
65
+ exports.albedo.transformClose(iter);
66
+ }
67
+ }
68
+ function applyTransform(iter, fn) {
69
+ try {
70
+ let data;
71
+ while ((data = exports.albedo.transformData(iter)) !== undefined) {
72
+ exports.albedo.transformApply(iter, fn(data));
73
+ }
74
+ }
75
+ finally {
76
+ exports.albedo.transformClose(iter);
77
+ }
78
+ }
79
+ function aggregateErrors(message, errors) {
80
+ return new AggregateError(errors, message);
81
+ }
82
+ /**
83
+ * Wrapper around a native transaction handle providing
84
+ * write operations that are committed or rolled back together.
85
+ */
86
+ class Transaction {
87
+ handle;
88
+ constructor(handle) {
89
+ this.handle = handle;
90
+ }
91
+ get nativeHandle() {
92
+ if (this.handle === null) {
93
+ throw new Error("Transaction is closed");
94
+ }
95
+ return this.handle;
96
+ }
97
+ /**
98
+ * Insert a document or raw byte buffer into the transaction.
99
+ */
100
+ insert(doc) {
101
+ exports.albedo.transactionInsert(this.nativeHandle, doc);
102
+ }
103
+ /**
104
+ * Delete documents matching the query from the transaction.
105
+ */
106
+ delete(query) {
107
+ exports.albedo.transactionDelete(this.nativeHandle, convertToQuery(query));
108
+ }
109
+ /**
110
+ * Generator that allows reading and modifying matching documents
111
+ * within the transaction.
112
+ */
113
+ transformIterator(query) {
114
+ return iterateTransform(exports.albedo.transactionTransform(this.nativeHandle, convertToQuery(query)));
115
+ }
116
+ /**
117
+ * Apply a transformation function to matching documents in the transaction.
118
+ */
119
+ transform(query, fn) {
120
+ applyTransform(exports.albedo.transactionTransform(this.nativeHandle, convertToQuery(query)), fn);
121
+ }
122
+ /**
123
+ * Alias for `transform` that reads more naturally for document updates.
124
+ */
125
+ update(query, fn) {
126
+ this.transform(query, fn);
127
+ }
128
+ /**
129
+ * Commit the transaction.
130
+ */
131
+ commit() {
132
+ exports.albedo.transactionCommit(this.nativeHandle);
133
+ }
134
+ /**
135
+ * Roll back the transaction.
136
+ */
137
+ rollback() {
138
+ exports.albedo.transactionRollback(this.nativeHandle);
139
+ }
140
+ /**
141
+ * Close the transaction and release native resources.
142
+ */
143
+ close() {
144
+ const handle = this.nativeHandle;
145
+ exports.albedo.transactionClose(handle);
146
+ this.handle = null;
147
+ }
148
+ }
149
+ exports.Transaction = Transaction;
50
150
  /**
51
151
  * Wrapper around a native Albedo bucket handle providing
52
152
  * methods for CRUD operations, indexing, iteration, and
@@ -114,6 +214,75 @@ class Bucket {
114
214
  insert(doc) {
115
215
  exports.albedo.insert(this.handle, doc);
116
216
  }
217
+ /**
218
+ * Flush buffered bucket state through the native checkpoint mechanism.
219
+ */
220
+ checkpoint() {
221
+ exports.albedo.checkpoint(this.handle);
222
+ }
223
+ /**
224
+ * Begin a manual transaction on this bucket.
225
+ */
226
+ beginTransaction() {
227
+ return new Transaction(exports.albedo.transactionBegin(this.handle));
228
+ }
229
+ /**
230
+ * Run a callback inside a transaction and commit it on success.
231
+ *
232
+ * If the callback throws, the transaction is rolled back before the
233
+ * original error is re-thrown.
234
+ */
235
+ tx(fn) {
236
+ const tx = this.beginTransaction();
237
+ let result;
238
+ try {
239
+ result = fn(tx);
240
+ }
241
+ catch (error) {
242
+ try {
243
+ tx.rollback();
244
+ }
245
+ catch (rollbackError) {
246
+ try {
247
+ tx.close();
248
+ }
249
+ catch (closeError) {
250
+ throw aggregateErrors("Transaction failed, rollback failed, and close failed", [error, rollbackError, closeError]);
251
+ }
252
+ throw aggregateErrors("Transaction failed and rollback failed", [
253
+ error,
254
+ rollbackError,
255
+ ]);
256
+ }
257
+ try {
258
+ tx.close();
259
+ }
260
+ catch (closeError) {
261
+ throw aggregateErrors("Transaction failed and close failed", [
262
+ error,
263
+ closeError,
264
+ ]);
265
+ }
266
+ throw error;
267
+ }
268
+ try {
269
+ tx.commit();
270
+ }
271
+ catch (commitError) {
272
+ try {
273
+ tx.close();
274
+ }
275
+ catch (closeError) {
276
+ throw aggregateErrors("Transaction commit and close both failed", [
277
+ commitError,
278
+ closeError,
279
+ ]);
280
+ }
281
+ throw commitError;
282
+ }
283
+ tx.close();
284
+ return result;
285
+ }
117
286
  /**
118
287
  * Delete documents matching the query. If no query is provided,
119
288
  * all documents will be removed.
@@ -126,7 +295,7 @@ class Bucket {
126
295
  * ```
127
296
  */
128
297
  delete(query) {
129
- exports.albedo.delete(this.handle, Bucket.convertToQuery(query));
298
+ exports.albedo.delete(this.handle, convertToQuery(query));
130
299
  }
131
300
  /**
132
301
  * Retrieve information about all indexes defined on the bucket.
@@ -172,7 +341,7 @@ class Bucket {
172
341
  * ```
173
342
  */
174
343
  *list(query) {
175
- const cursor = exports.albedo.list(this.handle, Bucket.convertToQuery(query));
344
+ const cursor = exports.albedo.list(this.handle, convertToQuery(query));
176
345
  try {
177
346
  let data;
178
347
  while ((data = exports.albedo.listData(cursor)) !== null) {
@@ -206,7 +375,7 @@ class Bucket {
206
375
  async *subscribe(query, options) {
207
376
  const pollingTimeout = options?.pollingTimeout ?? 50;
208
377
  const batchSize = options?.batchSize ?? 64;
209
- const subscription = exports.albedo.subscribe(this.handle, Bucket.convertToQuery(query));
378
+ const subscription = exports.albedo.subscribe(this.handle, convertToQuery(query));
210
379
  try {
211
380
  while (true) {
212
381
  const batch = exports.albedo.subscribePoll(subscription, batchSize);
@@ -262,10 +431,7 @@ class Bucket {
262
431
  * ```
263
432
  */
264
433
  static convertToQuery(query) {
265
- if (query instanceof Query) {
266
- return query.query;
267
- }
268
- return query || {};
434
+ return convertToQuery(query);
269
435
  }
270
436
  /**
271
437
  * Generator that allows reading and optionally modifying each
@@ -284,18 +450,7 @@ class Bucket {
284
450
  * ```
285
451
  */
286
452
  *transformIterator(query) {
287
- const queryObj = Bucket.convertToQuery(query);
288
- const iter = exports.albedo.transform(this.handle, queryObj);
289
- try {
290
- let data;
291
- while ((data = exports.albedo.transformData(iter)) !== undefined) {
292
- const newDoc = yield data;
293
- exports.albedo.transformApply(iter, newDoc);
294
- }
295
- }
296
- finally {
297
- exports.albedo.transformClose(iter);
298
- }
453
+ yield* iterateTransform(exports.albedo.transform(this.handle, convertToQuery(query)));
299
454
  }
300
455
  /**
301
456
  * Apply a transformation function to each document matching the
@@ -316,31 +471,19 @@ class Bucket {
316
471
  * ```
317
472
  */
318
473
  transform(query, fn) {
319
- const queryObj = Bucket.convertToQuery(query);
320
- const iter = exports.albedo.transform(this.handle, queryObj);
321
- try {
322
- let data;
323
- while ((data = exports.albedo.transformData(iter)) !== undefined) {
324
- exports.albedo.transformApply(iter, fn(data));
325
- }
326
- }
327
- finally {
328
- exports.albedo.transformClose(iter);
329
- }
474
+ applyTransform(exports.albedo.transform(this.handle, convertToQuery(query)), fn);
330
475
  }
331
476
  /**
332
- * Register a callback to receive replication data produced by the
333
- * bucket.
334
- * @param callback - invoked with raw replication bytes
335
- * @example
336
- * ```ts
337
- * bucket.setReplicationCallback(bytes => {
338
- * console.log('got replication', bytes.length);
339
- * });
340
- * ```
477
+ * Alias for `transform` that reads more naturally for document updates.
341
478
  */
342
- setReplicationCallback(callback) {
343
- exports.albedo.setReplicationCallback(this.handle, callback);
479
+ update(query, fn) {
480
+ this.transform(query, fn);
481
+ }
482
+ replicationCursor() {
483
+ return exports.albedo.replicationCursor(this.handle);
484
+ }
485
+ readReplicationBatch(cursor, maxBytes = 0) {
486
+ return exports.albedo.readReplicationBatch(this.handle, cursor, maxBytes);
344
487
  }
345
488
  /**
346
489
  * Apply a batch of replication operations to this bucket.
@@ -351,7 +494,7 @@ class Bucket {
351
494
  * ```
352
495
  */
353
496
  applyReplicationBatch(data) {
354
- exports.albedo.applyReplicationBatch(this.handle, data);
497
+ return exports.albedo.applyReplicationBatch(this.handle, data);
355
498
  }
356
499
  }
357
500
  exports.Bucket = Bucket;
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "albedo-node",
3
- "version": "0.7.2",
3
+ "version": "0.8.2",
4
4
  "description": "High-performance embedded database for Node.js with native bindings, BSON support, indexing, and replication",
5
5
  "keywords": [
6
6
  "albedo",