albedo-node 0.6.3 โ 0.8.1
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 +92 -16
- package/dist/index.d.ts +144 -31
- package/dist/index.js +204 -59
- package/native/albedo.aarch64_linux_gnu.node +0 -0
- package/native/albedo.aarch64_linux_musl.node +0 -0
- package/native/albedo.aarch64_macos.node +0 -0
- package/native/albedo.x86_64_linux_gnu.node +0 -0
- package/native/albedo.x86_64_linux_musl.node +0 -0
- package/native/albedo.x86_64_macos.node +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ This package wraps the core database engine with a concise TypeScript API, ships
|
|
|
8
8
|
|
|
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
|
-
- ๐ Generator-based iterators for queries and
|
|
12
|
-
- ๐
|
|
11
|
+
- ๐ Generator-based iterators for queries, transforms, and live subscriptions
|
|
12
|
+
- ๐ WAL-based replication helpers with explicit cursors and batches
|
|
13
13
|
- ๐งช TypeScript typings and Bun-based test suite
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
@@ -86,28 +86,81 @@ while (!step.done) {
|
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
###
|
|
89
|
+
### Subscribing to change events
|
|
90
|
+
|
|
91
|
+
`Bucket.subscribe()` exposes the new oplog-backed subscription API as an async
|
|
92
|
+
generator. It yields individual change events rather than re-scanned documents.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const bucket = Bucket.open("./example.bucket", {
|
|
96
|
+
wal: true,
|
|
97
|
+
write_durability: "all",
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
for await (const event of bucket.subscribe<{ name: string }>(
|
|
101
|
+
where("name", { $eq: "Ada" }),
|
|
102
|
+
{ pollingTimeout: 50, batchSize: 64 },
|
|
103
|
+
)) {
|
|
104
|
+
console.log(event.op, event.seqno, event.doc);
|
|
105
|
+
|
|
106
|
+
if (event.op === "delete") {
|
|
107
|
+
console.log("deleted", event.doc_id.toString());
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Each yielded event has this shape:
|
|
90
113
|
|
|
91
114
|
```ts
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
type SubscriptionEvent<T = unknown> = {
|
|
116
|
+
seqno: bigint;
|
|
117
|
+
op: "insert" | "update" | "delete";
|
|
118
|
+
doc_id: ObjectId;
|
|
119
|
+
ts: bigint;
|
|
120
|
+
doc?: T;
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Notes:
|
|
94
125
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
126
|
+
- Subscriptions require WAL mode to be active.
|
|
127
|
+
- `doc` is present for inline insert and update payloads.
|
|
128
|
+
- Delete events still include `doc_id`, even when `doc` is absent.
|
|
129
|
+
- The generator closes the native subscription automatically when iteration stops.
|
|
130
|
+
|
|
131
|
+
### Replication
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const primary = Bucket.open("./primary.bucket", {
|
|
135
|
+
wal: true,
|
|
136
|
+
write_durability: "all",
|
|
137
|
+
});
|
|
138
|
+
const replica = Bucket.open("./replica.bucket", {
|
|
139
|
+
wal: true,
|
|
140
|
+
write_durability: "all",
|
|
98
141
|
});
|
|
99
142
|
|
|
143
|
+
const cursor = primary.replicationCursor();
|
|
144
|
+
|
|
100
145
|
primary.insert({ name: "Replica", version: 1 });
|
|
101
|
-
primary.close();
|
|
102
146
|
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
replica.applyReplicationBatch(
|
|
147
|
+
const batch = primary.readReplicationBatch(cursor);
|
|
148
|
+
if (batch) {
|
|
149
|
+
const nextCursor = replica.applyReplicationBatch(batch);
|
|
150
|
+
console.log(nextCursor.next_frame_index);
|
|
106
151
|
}
|
|
107
152
|
|
|
153
|
+
primary.close();
|
|
108
154
|
replica.close();
|
|
109
155
|
```
|
|
110
156
|
|
|
157
|
+
Notes:
|
|
158
|
+
|
|
159
|
+
- Replication requires WAL mode to be enabled on both buckets.
|
|
160
|
+
- `replicationCursor()` snapshots the current position in the WAL stream.
|
|
161
|
+
- `readReplicationBatch(cursor, maxBytes?)` returns `null` when there is nothing new to send.
|
|
162
|
+
- `applyReplicationBatch(batch)` applies the batch and returns the replica's new cursor.
|
|
163
|
+
|
|
111
164
|
## Query objects and BSON payloads
|
|
112
165
|
|
|
113
166
|
Most bucket operations accept either:
|
|
@@ -139,21 +192,44 @@ Serialized documents that contain `_id` fields with a BSON ObjectId will be revi
|
|
|
139
192
|
## API reference
|
|
140
193
|
|
|
141
194
|
- `default` export โ raw Albedo native module
|
|
142
|
-
- `ObjectId`, `serialize`, `deserialize`, `open`, `close`, `insert`, `delete`, `list`, `transform`, `
|
|
195
|
+
- `ObjectId`, `serialize`, `deserialize`, `open`, `open_with_options`, `close`, `insert`, `checkpoint`, `delete`, `list`, `transform`, `replicationCursor`, `readReplicationBatch`, `applyReplicationBatch`, etc.
|
|
143
196
|
- `BSON.serialize(value)` / `BSON.deserialize(bytes)` โ helper wrappers
|
|
144
197
|
- `Bucket`
|
|
145
|
-
- `static open(path: string): Bucket`
|
|
198
|
+
- `static open(path: string, options?: OpenBucketOptions): Bucket`
|
|
146
199
|
- `insert(doc: object | Uint8Array)`
|
|
200
|
+
- `checkpoint()`
|
|
147
201
|
- `delete(query?: object | Query)`
|
|
148
202
|
- `list<T>(query?: object | Query): Generator<T>`
|
|
203
|
+
- `subscribe<T>(query?: object | Query, options?: { pollingTimeout?: number; batchSize?: number }): AsyncGenerator<SubscriptionEvent<T>>`
|
|
149
204
|
- `transformIterator<T>(query?: object | Query): Generator<T, undefined, object | null>`
|
|
205
|
+
- `transform<T>(query, fn)` / `update<T>(query, fn)`
|
|
206
|
+
- `all<T>(query?)` / `one<T>(query?)`
|
|
207
|
+
- `beginTransaction()` / `tx(fn)`
|
|
150
208
|
- `ensureIndex(name: string, options: { unique: boolean; sparse: boolean; reverse: boolean })`
|
|
151
209
|
- `dropIndex(name: string)`
|
|
152
210
|
- `indexes: Record<string, { name: string; unique: boolean; sparse: boolean; reverse: boolean }>`
|
|
153
|
-
- `
|
|
154
|
-
- `
|
|
211
|
+
- `replicationCursor(): { generation: bigint; next_frame_index: bigint }`
|
|
212
|
+
- `readReplicationBatch(cursor, maxBytes?): Uint8Array | null`
|
|
213
|
+
- `applyReplicationBatch(batch: Uint8Array): { generation: bigint; next_frame_index: bigint }`
|
|
155
214
|
- accepts raw BSON `Uint8Array` payloads anywhere a query or document object is expected
|
|
156
215
|
|
|
216
|
+
- `OpenBucketOptions`
|
|
217
|
+
- `buildIdIndex?: boolean`
|
|
218
|
+
- `mode?: string`
|
|
219
|
+
- `auto_vaccuum?: boolean`
|
|
220
|
+
- `page_cache_capacity?: number`
|
|
221
|
+
- `wal?: boolean`
|
|
222
|
+
- `write_durability?: "all" | "manual" | { periodic: number }`
|
|
223
|
+
- `read_durability?: "shared" | "process"`
|
|
224
|
+
- `wal_auto_checkpoint?: (defaults to 1000 pages)`
|
|
225
|
+
|
|
226
|
+
- `SubscriptionEvent<T>`
|
|
227
|
+
- `seqno: bigint`
|
|
228
|
+
- `op: "insert" | "update" | "delete"`
|
|
229
|
+
- `doc_id: ObjectId`
|
|
230
|
+
- `ts: bigint`
|
|
231
|
+
- `doc?: T`
|
|
232
|
+
|
|
157
233
|
- `Query`
|
|
158
234
|
- `where(field, filter)` chains field predicates (e.g. `{ $eq: value }`, `{ $gt: 10 }`)
|
|
159
235
|
- `sortBy(field, direction?)` sets sort order
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
type ByteBuffer = Uint8Array;
|
|
2
|
+
type BucketHandle = object;
|
|
3
|
+
type TransactionHandle = object;
|
|
4
|
+
type ListIteratorHandle = object;
|
|
5
|
+
type TransformIteratorHandle = object;
|
|
6
|
+
type SubscriptionHandle = object;
|
|
7
|
+
export interface ReplicationCursor {
|
|
8
|
+
generation: bigint;
|
|
9
|
+
next_frame_index: bigint;
|
|
10
|
+
}
|
|
2
11
|
interface IndexOptions {
|
|
3
12
|
unique: boolean;
|
|
4
13
|
sparse: boolean;
|
|
5
14
|
reverse: boolean;
|
|
6
15
|
}
|
|
16
|
+
interface IndexInfo {
|
|
17
|
+
name: string;
|
|
18
|
+
unique: boolean;
|
|
19
|
+
sparse: boolean;
|
|
20
|
+
reverse: boolean;
|
|
21
|
+
}
|
|
7
22
|
interface ObjectIdInstance {
|
|
8
23
|
buffer: ByteBuffer;
|
|
9
24
|
toString(): string;
|
|
@@ -12,6 +27,20 @@ interface ObjectIdConstructor {
|
|
|
12
27
|
new (buffer?: ByteBuffer): ObjectIdInstance;
|
|
13
28
|
fromString(str: string): ObjectIdInstance;
|
|
14
29
|
}
|
|
30
|
+
export interface SubscriptionEvent<T = unknown> {
|
|
31
|
+
seqno: bigint;
|
|
32
|
+
op: "insert" | "update" | "delete";
|
|
33
|
+
doc_id: ObjectIdInstance;
|
|
34
|
+
ts: bigint;
|
|
35
|
+
doc?: T;
|
|
36
|
+
}
|
|
37
|
+
interface SubscriptionBatch<T = unknown> {
|
|
38
|
+
batch: Array<SubscriptionEvent<T>>;
|
|
39
|
+
}
|
|
40
|
+
export interface SubscribeOptions {
|
|
41
|
+
pollingTimeout?: number;
|
|
42
|
+
batchSize?: number;
|
|
43
|
+
}
|
|
15
44
|
/**
|
|
16
45
|
* Controls when fsync is called to guarantee write durability.
|
|
17
46
|
*/
|
|
@@ -34,11 +63,45 @@ interface OpenBucketOptions {
|
|
|
34
63
|
write_durability?: WriteDurability;
|
|
35
64
|
read_durability?: ReadDurability;
|
|
36
65
|
}
|
|
37
|
-
|
|
66
|
+
interface AlbedoModule {
|
|
67
|
+
ObjectId: ObjectIdConstructor;
|
|
68
|
+
serialize(value: unknown): Uint8Array;
|
|
69
|
+
deserialize<T = unknown>(data: ByteBuffer): T;
|
|
70
|
+
open(path: string): BucketHandle;
|
|
71
|
+
open_with_options(path: string, options: OpenBucketOptions): BucketHandle;
|
|
72
|
+
close(bucket: BucketHandle): void;
|
|
73
|
+
list(bucket: BucketHandle, query: object): ListIteratorHandle;
|
|
74
|
+
listClose(cursor: ListIteratorHandle): void;
|
|
75
|
+
listData(cursor: ListIteratorHandle): unknown | null;
|
|
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;
|
|
80
|
+
ensureIndex(bucket: BucketHandle, name: string, options: IndexOptions): void;
|
|
81
|
+
listIndexes(bucket: BucketHandle): Record<string, IndexInfo>;
|
|
82
|
+
dropIndex(bucket: BucketHandle, name: string): void;
|
|
83
|
+
delete(bucket: BucketHandle, query: object): void;
|
|
84
|
+
transactionDelete(tx: TransactionHandle, query: object): void;
|
|
85
|
+
transform(bucket: BucketHandle, query: object): TransformIteratorHandle;
|
|
86
|
+
transactionTransform(tx: TransactionHandle, query: object): TransformIteratorHandle;
|
|
87
|
+
transformClose(iter: TransformIteratorHandle): void;
|
|
88
|
+
transformData(iter: TransformIteratorHandle): unknown | null;
|
|
89
|
+
transformApply(iter: TransformIteratorHandle, replace: ByteBuffer | object | null): void;
|
|
90
|
+
transactionCommit(tx: TransactionHandle): void;
|
|
91
|
+
transactionRollback(tx: TransactionHandle): void;
|
|
92
|
+
transactionClose(tx: TransactionHandle): void;
|
|
93
|
+
subscribe(bucket: BucketHandle, query: object): SubscriptionHandle;
|
|
94
|
+
subscribePoll<T = unknown>(sub: SubscriptionHandle, maxEvents: number): SubscriptionBatch<T> | null;
|
|
95
|
+
subscribeClose(sub: SubscriptionHandle): void;
|
|
96
|
+
replicationCursor(bucket: BucketHandle): ReplicationCursor;
|
|
97
|
+
readReplicationBatch(bucket: BucketHandle, cursor: ReplicationCursor, maxBytes: number): ByteBuffer | null;
|
|
98
|
+
applyReplicationBatch(bucket: BucketHandle, data: ByteBuffer): ReplicationCursor;
|
|
99
|
+
}
|
|
100
|
+
export declare const albedo: AlbedoModule;
|
|
38
101
|
export default albedo;
|
|
39
102
|
export declare const BSON: {
|
|
40
|
-
serialize:
|
|
41
|
-
deserialize:
|
|
103
|
+
serialize: (value: unknown) => Uint8Array;
|
|
104
|
+
deserialize: <T = unknown>(data: ByteBuffer) => T;
|
|
42
105
|
};
|
|
43
106
|
/**
|
|
44
107
|
* Native ObjectId class constructor.
|
|
@@ -50,6 +113,50 @@ export declare const BSON: {
|
|
|
50
113
|
* ```
|
|
51
114
|
*/
|
|
52
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
|
+
}
|
|
53
160
|
/**
|
|
54
161
|
* Wrapper around a native Albedo bucket handle providing
|
|
55
162
|
* methods for CRUD operations, indexing, iteration, and
|
|
@@ -108,6 +215,21 @@ export declare class Bucket {
|
|
|
108
215
|
* ```
|
|
109
216
|
*/
|
|
110
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;
|
|
111
233
|
/**
|
|
112
234
|
* Delete documents matching the query. If no query is provided,
|
|
113
235
|
* all documents will be removed.
|
|
@@ -127,7 +249,7 @@ export declare class Bucket {
|
|
|
127
249
|
* console.log(bucket.indexes);
|
|
128
250
|
* ```
|
|
129
251
|
*/
|
|
130
|
-
get indexes():
|
|
252
|
+
get indexes(): Record<string, IndexInfo>;
|
|
131
253
|
/**
|
|
132
254
|
* Create or update an index on a field.
|
|
133
255
|
* @param name - index name (field path)
|
|
@@ -159,29 +281,26 @@ export declare class Bucket {
|
|
|
159
281
|
*/
|
|
160
282
|
list<T>(query?: object | Query): Generator<T>;
|
|
161
283
|
/**
|
|
162
|
-
* Async iterator that continuously polls
|
|
163
|
-
* optional query. Unlike `list`, when there are no more results the
|
|
164
|
-
* iterator waits for `pollingTimeout` milliseconds and retries,
|
|
165
|
-
* making it suitable for watching a bucket for new data.
|
|
284
|
+
* Async iterator that continuously polls a change subscription.
|
|
166
285
|
*
|
|
167
|
-
* The
|
|
168
|
-
*
|
|
286
|
+
* The generator yields individual oplog events rather than rescanned
|
|
287
|
+
* documents, and automatically closes the native subscription when the
|
|
288
|
+
* consumer stops iterating.
|
|
169
289
|
*
|
|
170
290
|
* @param query - filter or `Query` object
|
|
171
291
|
* @param options - polling configuration
|
|
172
|
-
* @param options.pollingTimeout - ms to wait before retrying when
|
|
173
|
-
*
|
|
174
|
-
* @
|
|
292
|
+
* @param options.pollingTimeout - ms to wait before retrying when the
|
|
293
|
+
* subscription is idle (default `50`)
|
|
294
|
+
* @param options.batchSize - maximum number of change events to pull per
|
|
295
|
+
* native poll (default `64`)
|
|
175
296
|
* @example
|
|
176
297
|
* ```ts
|
|
177
|
-
* for await (const
|
|
178
|
-
* console.log(
|
|
298
|
+
* for await (const event of bucket.subscribe<User>(where('active', { $eq: true }))) {
|
|
299
|
+
* console.log(event.op, event.doc);
|
|
179
300
|
* }
|
|
180
301
|
* ```
|
|
181
302
|
*/
|
|
182
|
-
|
|
183
|
-
pollingTimeout?: number;
|
|
184
|
-
}): AsyncGenerator<T>;
|
|
303
|
+
subscribe<T>(query?: object | Query, options?: SubscribeOptions): AsyncGenerator<SubscriptionEvent<T>>;
|
|
185
304
|
/**
|
|
186
305
|
* Collect all documents matching the optional query into an array.
|
|
187
306
|
* @param query - filter or `Query` object
|
|
@@ -229,7 +348,7 @@ export declare class Bucket {
|
|
|
229
348
|
* }
|
|
230
349
|
* ```
|
|
231
350
|
*/
|
|
232
|
-
transformIterator<T>(query?: object | Query): Generator<T, undefined,
|
|
351
|
+
transformIterator<T extends object>(query?: object | Query): Generator<T, undefined, TransformReplacement<T>>;
|
|
233
352
|
/**
|
|
234
353
|
* Apply a transformation function to each document matching the
|
|
235
354
|
* provided query. The predicate receives the current document and
|
|
@@ -248,19 +367,13 @@ export declare class Bucket {
|
|
|
248
367
|
* });
|
|
249
368
|
* ```
|
|
250
369
|
*/
|
|
251
|
-
transform<T extends object>(query: object | Query | undefined, fn: (doc: T) => T
|
|
370
|
+
transform<T extends object>(query: object | Query | undefined, fn: (doc: T) => TransformReplacement<T>): void;
|
|
252
371
|
/**
|
|
253
|
-
*
|
|
254
|
-
* bucket.
|
|
255
|
-
* @param callback - invoked with raw replication bytes
|
|
256
|
-
* @example
|
|
257
|
-
* ```ts
|
|
258
|
-
* bucket.setReplicationCallback(bytes => {
|
|
259
|
-
* console.log('got replication', bytes.length);
|
|
260
|
-
* });
|
|
261
|
-
* ```
|
|
372
|
+
* Alias for `transform` that reads more naturally for document updates.
|
|
262
373
|
*/
|
|
263
|
-
|
|
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;
|
|
264
377
|
/**
|
|
265
378
|
* Apply a batch of replication operations to this bucket.
|
|
266
379
|
* @param data - bytes produced by another bucket's replication
|
|
@@ -269,7 +382,7 @@ export declare class Bucket {
|
|
|
269
382
|
* bucket.applyReplicationBatch(remoteBytes);
|
|
270
383
|
* ```
|
|
271
384
|
*/
|
|
272
|
-
applyReplicationBatch(data: Uint8Array):
|
|
385
|
+
applyReplicationBatch(data: Uint8Array): ReplicationCursor;
|
|
273
386
|
}
|
|
274
387
|
type BSONValue = any;
|
|
275
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,
|
|
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,
|
|
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) {
|
|
@@ -184,34 +353,36 @@ class Bucket {
|
|
|
184
353
|
}
|
|
185
354
|
}
|
|
186
355
|
/**
|
|
187
|
-
* Async iterator that continuously polls
|
|
188
|
-
* optional query. Unlike `list`, when there are no more results the
|
|
189
|
-
* iterator waits for `pollingTimeout` milliseconds and retries,
|
|
190
|
-
* making it suitable for watching a bucket for new data.
|
|
356
|
+
* Async iterator that continuously polls a change subscription.
|
|
191
357
|
*
|
|
192
|
-
* The
|
|
193
|
-
*
|
|
358
|
+
* The generator yields individual oplog events rather than rescanned
|
|
359
|
+
* documents, and automatically closes the native subscription when the
|
|
360
|
+
* consumer stops iterating.
|
|
194
361
|
*
|
|
195
362
|
* @param query - filter or `Query` object
|
|
196
363
|
* @param options - polling configuration
|
|
197
|
-
* @param options.pollingTimeout - ms to wait before retrying when
|
|
198
|
-
*
|
|
199
|
-
* @
|
|
364
|
+
* @param options.pollingTimeout - ms to wait before retrying when the
|
|
365
|
+
* subscription is idle (default `50`)
|
|
366
|
+
* @param options.batchSize - maximum number of change events to pull per
|
|
367
|
+
* native poll (default `64`)
|
|
200
368
|
* @example
|
|
201
369
|
* ```ts
|
|
202
|
-
* for await (const
|
|
203
|
-
* console.log(
|
|
370
|
+
* for await (const event of bucket.subscribe<User>(where('active', { $eq: true }))) {
|
|
371
|
+
* console.log(event.op, event.doc);
|
|
204
372
|
* }
|
|
205
373
|
* ```
|
|
206
374
|
*/
|
|
207
|
-
async *
|
|
375
|
+
async *subscribe(query, options) {
|
|
208
376
|
const pollingTimeout = options?.pollingTimeout ?? 50;
|
|
209
|
-
const
|
|
377
|
+
const batchSize = options?.batchSize ?? 64;
|
|
378
|
+
const subscription = exports.albedo.subscribe(this.handle, convertToQuery(query));
|
|
210
379
|
try {
|
|
211
380
|
while (true) {
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
381
|
+
const batch = exports.albedo.subscribePoll(subscription, batchSize);
|
|
382
|
+
if (batch !== null) {
|
|
383
|
+
for (const event of batch.batch) {
|
|
384
|
+
yield event;
|
|
385
|
+
}
|
|
215
386
|
}
|
|
216
387
|
else {
|
|
217
388
|
await new Promise((r) => setTimeout(r, pollingTimeout));
|
|
@@ -219,7 +390,7 @@ class Bucket {
|
|
|
219
390
|
}
|
|
220
391
|
}
|
|
221
392
|
finally {
|
|
222
|
-
exports.albedo.
|
|
393
|
+
exports.albedo.subscribeClose(subscription);
|
|
223
394
|
}
|
|
224
395
|
}
|
|
225
396
|
/**
|
|
@@ -260,10 +431,7 @@ class Bucket {
|
|
|
260
431
|
* ```
|
|
261
432
|
*/
|
|
262
433
|
static convertToQuery(query) {
|
|
263
|
-
|
|
264
|
-
return query.query;
|
|
265
|
-
}
|
|
266
|
-
return query || {};
|
|
434
|
+
return convertToQuery(query);
|
|
267
435
|
}
|
|
268
436
|
/**
|
|
269
437
|
* Generator that allows reading and optionally modifying each
|
|
@@ -282,18 +450,7 @@ class Bucket {
|
|
|
282
450
|
* ```
|
|
283
451
|
*/
|
|
284
452
|
*transformIterator(query) {
|
|
285
|
-
|
|
286
|
-
const iter = exports.albedo.transform(this.handle, queryObj);
|
|
287
|
-
try {
|
|
288
|
-
let data;
|
|
289
|
-
while ((data = exports.albedo.transformData(iter)) !== undefined) {
|
|
290
|
-
const newDoc = yield data;
|
|
291
|
-
exports.albedo.transformApply(iter, newDoc);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
finally {
|
|
295
|
-
exports.albedo.transformClose(iter);
|
|
296
|
-
}
|
|
453
|
+
yield* iterateTransform(exports.albedo.transform(this.handle, convertToQuery(query)));
|
|
297
454
|
}
|
|
298
455
|
/**
|
|
299
456
|
* Apply a transformation function to each document matching the
|
|
@@ -314,31 +471,19 @@ class Bucket {
|
|
|
314
471
|
* ```
|
|
315
472
|
*/
|
|
316
473
|
transform(query, fn) {
|
|
317
|
-
|
|
318
|
-
const iter = exports.albedo.transform(this.handle, queryObj);
|
|
319
|
-
try {
|
|
320
|
-
let data;
|
|
321
|
-
while ((data = exports.albedo.transformData(iter)) !== undefined) {
|
|
322
|
-
exports.albedo.transformApply(iter, fn(data));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
finally {
|
|
326
|
-
exports.albedo.transformClose(iter);
|
|
327
|
-
}
|
|
474
|
+
applyTransform(exports.albedo.transform(this.handle, convertToQuery(query)), fn);
|
|
328
475
|
}
|
|
329
476
|
/**
|
|
330
|
-
*
|
|
331
|
-
* bucket.
|
|
332
|
-
* @param callback - invoked with raw replication bytes
|
|
333
|
-
* @example
|
|
334
|
-
* ```ts
|
|
335
|
-
* bucket.setReplicationCallback(bytes => {
|
|
336
|
-
* console.log('got replication', bytes.length);
|
|
337
|
-
* });
|
|
338
|
-
* ```
|
|
477
|
+
* Alias for `transform` that reads more naturally for document updates.
|
|
339
478
|
*/
|
|
340
|
-
|
|
341
|
-
|
|
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);
|
|
342
487
|
}
|
|
343
488
|
/**
|
|
344
489
|
* Apply a batch of replication operations to this bucket.
|
|
@@ -349,7 +494,7 @@ class Bucket {
|
|
|
349
494
|
* ```
|
|
350
495
|
*/
|
|
351
496
|
applyReplicationBatch(data) {
|
|
352
|
-
exports.albedo.applyReplicationBatch(this.handle, data);
|
|
497
|
+
return exports.albedo.applyReplicationBatch(this.handle, data);
|
|
353
498
|
}
|
|
354
499
|
}
|
|
355
500
|
exports.Bucket = Bucket;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|