edinburgh 0.1.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.
@@ -0,0 +1,93 @@
1
+ import * as olmdb from "olmdb";
2
+ import { MODIFIED_INSTANCES_SYMBOL, resetModelCaches } from "./models.js";
3
+ // Re-export public API from models
4
+ export { Model, registerModel, field, setOnSaveCallback } from "./models.js";
5
+ // Re-export public API from types (only factory functions and instances)
6
+ export {
7
+ // Pre-defined type instances
8
+ string, number, boolean, identifier, undef,
9
+ // Type factory functions
10
+ opt, or, array, literal, link, } from "./types.js";
11
+ // Re-export public API from indexes
12
+ export { index, primary, unique, dump, } from "./indexes.js";
13
+ export { BaseIndex, UniqueIndex, PrimaryIndex } from './indexes.js';
14
+ // Re-export from OLMDB
15
+ export { init, onCommit, onRevert, getTransactionData, setTransactionData, DatabaseError } from "olmdb";
16
+ /**
17
+ * Executes a function within a database transaction context.
18
+ *
19
+ * Loading models (also through links in other models) and changing models can only be done from
20
+ * within a transaction.
21
+ *
22
+ * Transactions have a consistent view of the database, and changes made within a transaction are
23
+ * isolated from other transactions until they are committed. In case a commit clashes with changes
24
+ * made by another transaction, the transaction function will automatically be re-executed up to 10
25
+ * times.
26
+ *
27
+ * @template T - The return type of the transaction function.
28
+ * @param fn - The function to execute within the transaction context.
29
+ * @returns A promise that resolves with the function's return value.
30
+ * @throws {TypeError} If nested transactions are attempted.
31
+ * @throws {DatabaseError} With code "RACING_TRANSACTION" if the transaction fails after retries due to conflicts.
32
+ * @throws {DatabaseError} With code "TRANSACTION_FAILED" if the transaction fails for other reasons.
33
+ * @throws {DatabaseError} With code "TXN_LIMIT" if maximum number of transactions is reached.
34
+ * @throws {DatabaseError} With code "LMDB-{code}" for LMDB-specific errors.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const paid = await E.transact(() => {
39
+ * const user = User.load("john_doe");
40
+ * // This is concurrency-safe - the function will rerun if it is raced by another transaction
41
+ * if (user.credits > 0) {
42
+ * user.credits--;
43
+ * return true;
44
+ * }
45
+ * return false;
46
+ * });
47
+ * ```
48
+ * ```typescript
49
+ * // Transaction with automatic retry on conflicts
50
+ * await E.transact(() => {
51
+ * const counter = Counter.load("global") || new Counter({id: "global", value: 0});
52
+ * counter.value++;
53
+ * });
54
+ * ```
55
+ */
56
+ export function transact(fn) {
57
+ return olmdb.transact(() => {
58
+ const modifiedInstances = new Set();
59
+ olmdb.setTransactionData(MODIFIED_INSTANCES_SYMBOL, modifiedInstances);
60
+ const savedInstances = new Set();
61
+ try {
62
+ const result = fn();
63
+ // Save all modified instances before committing.
64
+ while (modifiedInstances.size > 0) {
65
+ // Back referencing can cause models to be scheduled for save() a second time,
66
+ // which is why we require the outer loop.
67
+ for (const instance of modifiedInstances) {
68
+ instance._save();
69
+ savedInstances.add(instance);
70
+ modifiedInstances.delete(instance);
71
+ }
72
+ }
73
+ return result;
74
+ }
75
+ catch (error) {
76
+ // Discard changes on all saved and still unsaved instances
77
+ for (const instance of savedInstances)
78
+ instance.preventPersist();
79
+ for (const instance of modifiedInstances)
80
+ instance.preventPersist();
81
+ throw error;
82
+ }
83
+ });
84
+ }
85
+ export async function deleteEverything() {
86
+ await olmdb.transact(() => {
87
+ for (const { key } of olmdb.scan()) {
88
+ olmdb.del(key);
89
+ }
90
+ });
91
+ await resetModelCaches();
92
+ }
93
+ //# sourceMappingURL=edinburgh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edinburgh.js","sourceRoot":"","sources":["../../src/edinburgh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAS,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEjF,mCAAmC;AACnC,OAAO,EACH,KAAK,EACL,aAAa,EACb,KAAK,EACL,iBAAiB,EACpB,MAAM,aAAa,CAAC;AAGrB,yEAAyE;AACzE,OAAO;AACH,6BAA6B;AAC7B,MAAM,EACN,MAAM,EACN,OAAO,EACP,UAAU,EACV,KAAK;AACL,yBAAyB;AACzB,GAAG,EACH,EAAE,EACF,KAAK,EACL,OAAO,EACP,IAAI,GACP,MAAM,YAAY,CAAC;AAEpB,oCAAoC;AACpC,OAAO,EACH,KAAK,EACL,OAAO,EACP,MAAM,EACN,IAAI,GACP,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEpE,uBAAuB;AACvB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAGxG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCE;AACF,MAAM,UAAU,QAAQ,CAAI,EAAW;IACnC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE;QACvB,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAc,CAAC;QAChD,KAAK,CAAC,kBAAkB,CAAC,yBAAyB,EAAE,iBAAiB,CAAC,CAAC;QAEvE,MAAM,cAAc,GAAoB,IAAI,GAAG,EAAE,CAAC;QAClD,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;YACpB,iDAAiD;YACjD,OAAM,iBAAiB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC/B,8EAA8E;gBAC9E,0CAA0C;gBAC1C,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;oBACvC,QAAQ,CAAC,KAAK,EAAE,CAAC;oBACjB,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBAC7B,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACvC,CAAC;YACL,CAAC;YAED,OAAO,MAAM,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,2DAA2D;YAC3D,KAAK,MAAM,QAAQ,IAAI,cAAc;gBAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;YACjE,KAAK,MAAM,QAAQ,IAAI,iBAAiB;gBAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;YACpE,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB;IAClC,MAAM,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE;QACtB,KAAK,MAAM,EAAC,GAAG,EAAC,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/B,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,gBAAgB,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,348 @@
1
+ import * as olmdb from "olmdb";
2
+ import { Bytes } from "./bytes.js";
3
+ import { Model } from "./models.js";
4
+ /** @internal Symbol used to access the underlying model from a proxy */
5
+ export declare const TARGET_SYMBOL: unique symbol;
6
+ type IndexArgTypes<M extends typeof Model<any>, F extends readonly (keyof InstanceType<M> & string)[]> = {
7
+ [I in keyof F]: InstanceType<M>[F[I]];
8
+ };
9
+ /**
10
+ * Iterator for range queries on indexes.
11
+ * Handles common iteration logic for both primary and unique indexes.
12
+ * Implements both Iterator and Iterable interfaces for efficiency.
13
+ */
14
+ declare class IndexRangeIterator<M extends typeof Model, F extends readonly (keyof InstanceType<M> & string)[]> implements Iterator<InstanceType<M>>, Iterable<InstanceType<M>> {
15
+ private iterator;
16
+ private indexId;
17
+ private parentIndex;
18
+ constructor(iterator: olmdb.DbIterator<any, any> | undefined, indexId: number, parentIndex: BaseIndex<M, F>);
19
+ [Symbol.iterator](): Iterator<InstanceType<M>>;
20
+ next(): IteratorResult<InstanceType<M>>;
21
+ count(): number;
22
+ fetch(): InstanceType<M> | undefined;
23
+ }
24
+ type ArrayOrOnlyItem<ARG_TYPES extends readonly any[]> = ARG_TYPES extends readonly [infer A] ? (A | Partial<ARG_TYPES>) : Partial<ARG_TYPES>;
25
+ type FindOptions<ARG_TYPES extends readonly any[]> = (({
26
+ is: ArrayOrOnlyItem<ARG_TYPES>;
27
+ } | (({
28
+ from: ArrayOrOnlyItem<ARG_TYPES>;
29
+ } | {
30
+ after: ArrayOrOnlyItem<ARG_TYPES>;
31
+ } | {}) & ({
32
+ to: ArrayOrOnlyItem<ARG_TYPES>;
33
+ } | {
34
+ before: ArrayOrOnlyItem<ARG_TYPES>;
35
+ } | {}))) & {
36
+ reverse?: boolean;
37
+ });
38
+ /**
39
+ * Base class for database indexes for efficient lookups on model fields.
40
+ *
41
+ * Indexes enable fast queries on specific field combinations and enforce uniqueness constraints.
42
+ *
43
+ * @template M - The model class this index belongs to.
44
+ * @template F - The field names that make up this index.
45
+ */
46
+ export declare abstract class BaseIndex<M extends typeof Model, const F extends readonly (keyof InstanceType<M> & string)[]> {
47
+ _fieldNames: F;
48
+ _MyModel: M;
49
+ /**
50
+ * Create a new index.
51
+ * @param MyModel - The model class this index belongs to.
52
+ * @param _fieldNames - Array of field names that make up this index.
53
+ */
54
+ constructor(MyModel: M, _fieldNames: F, isPrimary?: boolean);
55
+ _cachedIndexId?: number;
56
+ /**
57
+ * Deserialize index key bytes back to field values.
58
+ * @param bytes - Bytes to read from.
59
+ * @returns Array of field values.
60
+ */
61
+ _deserializeKey(bytes: Bytes): IndexArgTypes<M, F>;
62
+ /**
63
+ * Extract model from iterator entry - implemented differently by each index type.
64
+ * @param keyBytes - Key bytes with index ID already read.
65
+ * @param valueBytes - Value bytes from the entry.
66
+ * @returns Model instance or undefined.
67
+ * @internal
68
+ */
69
+ abstract _getModelFromEntry(keyBytes: Bytes, valueBytes: Bytes): InstanceType<M> | undefined;
70
+ /**
71
+ * Serialize field values to bytes for index key.
72
+ * @param args - Field values to serialize (can be partial for range queries).
73
+ * @param bytes - Bytes to write to.
74
+ * @internal
75
+ */
76
+ _serializeArgs(args: Partial<IndexArgTypes<M, F>> | readonly any[], bytes: Bytes): void;
77
+ /**
78
+ * Create database key from field values.
79
+ * @param args - Field values.
80
+ * @returns Database key bytes.
81
+ */
82
+ _getKeyFromArgs(args: IndexArgTypes<M, F>): Uint8Array;
83
+ /**
84
+ * Serialize model fields to bytes for index key.
85
+ * @param model - Model instance.
86
+ * @param bytes - Bytes to write to.
87
+ */
88
+ _serializeModel(model: InstanceType<M>, bytes: Bytes): void;
89
+ /**
90
+ * Create database key from model instance.
91
+ * @param model - Model instance.
92
+ * @param includeIndexId - Whether to include index ID in key.
93
+ * @returns Database key bytes or undefined if skipped.
94
+ * @internal
95
+ */
96
+ _getKeyFromModel(model: InstanceType<M>, includeIndexId: boolean): Uint8Array;
97
+ /**
98
+ * Extract field values from model for this index.
99
+ * @param model - Model instance.
100
+ * @returns Field values or undefined if should be skipped.
101
+ * @internal
102
+ */
103
+ _modelToArgs(model: InstanceType<M>): IndexArgTypes<M, F> | undefined;
104
+ /**
105
+ * Get or create unique index ID for this index.
106
+ * @returns Numeric index ID.
107
+ */
108
+ _getIndexId(): number;
109
+ /**
110
+ * Check if indexing should be skipped for a model instance.
111
+ * @param model - Model instance.
112
+ * @returns true if indexing should be skipped.
113
+ */
114
+ _checkSkip(model: InstanceType<M>): boolean;
115
+ /**
116
+ * Find model instances using flexible range query options.
117
+ *
118
+ * Supports exact matches, inclusive/exclusive range queries, and reverse iteration.
119
+ * For single-field indexes, you can pass values directly or in arrays.
120
+ * For multi-field indexes, pass arrays or partial arrays for prefix matching.
121
+ *
122
+ * @param opts - Query options object
123
+ * @param opts.is - Exact match (sets both `from` and `to` to same value)
124
+ * @param opts.from - Range start (inclusive)
125
+ * @param opts.after - Range start (exclusive)
126
+ * @param opts.to - Range end (inclusive)
127
+ * @param opts.before - Range end (exclusive)
128
+ * @param opts.reverse - Whether to iterate in reverse order
129
+ * @returns An iterable of model instances matching the query
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * // Exact match
134
+ * for (const user of User.byEmail.find({is: "john@example.com"})) {
135
+ * console.log(user.name);
136
+ * }
137
+ *
138
+ * // Range query (inclusive)
139
+ * for (const user of User.byEmail.find({from: "a@", to: "m@"})) {
140
+ * console.log(user.email);
141
+ * }
142
+ *
143
+ * // Range query (exclusive)
144
+ * for (const user of User.byEmail.find({after: "a@", before: "m@"})) {
145
+ * console.log(user.email);
146
+ * }
147
+ *
148
+ * // Open-ended ranges
149
+ * for (const user of User.byEmail.find({from: "m@"})) { // m@ and later
150
+ * console.log(user.email);
151
+ * }
152
+ *
153
+ * for (const user of User.byEmail.find({to: "m@"})) { // up to and including m@
154
+ * console.log(user.email);
155
+ * }
156
+ *
157
+ * // Reverse iteration
158
+ * for (const user of User.byEmail.find({reverse: true})) {
159
+ * console.log(user.email); // Z to A order
160
+ * }
161
+ *
162
+ * // Multi-field index prefix matching
163
+ * for (const item of CompositeModel.pk.find({from: ["electronics", "phones"]})) {
164
+ * console.log(item.name); // All electronics/phones items
165
+ * }
166
+ *
167
+ * // For single-field indexes, you can use the value directly
168
+ * for (const user of User.byEmail.find({is: "john@example.com"})) {
169
+ * console.log(user.name);
170
+ * }
171
+ * ```
172
+ */
173
+ find(opts?: FindOptions<IndexArgTypes<M, F>>): IndexRangeIterator<M, F>;
174
+ /**
175
+ * Save index entry for a model instance.
176
+ * @param model - Model instance to save.
177
+ * @param originalKey - Original key if updating.
178
+ */
179
+ abstract _save(model: InstanceType<M>, originalKey?: Uint8Array): Uint8Array | undefined;
180
+ abstract _getTypeName(): string;
181
+ }
182
+ /**
183
+ * Primary index that stores the actual model data.
184
+ *
185
+ * @template M - The model class this index belongs to.
186
+ * @template F - The field names that make up this index.
187
+ */
188
+ export declare class PrimaryIndex<M extends typeof Model, const F extends readonly (keyof InstanceType<M> & string)[]> extends BaseIndex<M, F> {
189
+ constructor(MyModel: M, fieldNames: F);
190
+ /**
191
+ * Get a model instance by primary key values.
192
+ * @param args - The primary key values.
193
+ * @returns The model instance if found, undefined otherwise.
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * const user = User.pk.get("john_doe");
198
+ * ```
199
+ */
200
+ get(...args: IndexArgTypes<M, F>): InstanceType<M> | undefined;
201
+ /**
202
+ * Extract model from iterator entry for primary index.
203
+ * @param keyBytes - Key bytes with index ID already read.
204
+ * @param valueBytes - Value bytes from the entry.
205
+ * @returns Model instance or undefined.
206
+ * @internal
207
+ */
208
+ _getModelFromEntry(keyBytes: Bytes, valueBytes: Bytes): InstanceType<M> | undefined;
209
+ /**
210
+ * Save primary index entry.
211
+ * @param model - Model instance.
212
+ * @param originalKey - Original key if updating.
213
+ */
214
+ _save(model: InstanceType<M>, originalKey?: Uint8Array): Uint8Array;
215
+ _getTypeName(): string;
216
+ }
217
+ /**
218
+ * Unique index that stores references to the primary key.
219
+ *
220
+ * @template M - The model class this index belongs to.
221
+ * @template F - The field names that make up this index.
222
+ */
223
+ export declare class UniqueIndex<M extends typeof Model, const F extends readonly (keyof InstanceType<M> & string)[]> extends BaseIndex<M, F> {
224
+ /**
225
+ * Get a model instance by unique index key values.
226
+ * @param args - The unique index key values.
227
+ * @returns The model instance if found, undefined otherwise.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * const userByEmail = User.byEmail.get("john@example.com");
232
+ * ```
233
+ */
234
+ get(...args: IndexArgTypes<M, F>): InstanceType<M> | undefined;
235
+ /**
236
+ * Extract model from iterator entry for unique index.
237
+ * @param keyBytes - Key bytes with index ID already read.
238
+ * @param valueBytes - Value bytes from the entry.
239
+ * @returns Model instance or undefined.
240
+ * @internal
241
+ */
242
+ _getModelFromEntry(keyBytes: Bytes, valueBytes: Bytes): InstanceType<M> | undefined;
243
+ /**
244
+ * Save unique index entry.
245
+ * @param model - Model instance.
246
+ * @param originalKey - Original key if updating.
247
+ */
248
+ _save(model: InstanceType<M>, originalKey?: Uint8Array): Uint8Array | undefined;
249
+ _getTypeName(): string;
250
+ }
251
+ /**
252
+ * Secondary index for non-unique lookups.
253
+ *
254
+ * @template M - The model class this index belongs to.
255
+ * @template F - The field names that make up this index.
256
+ */
257
+ export declare class SecondaryIndex<M extends typeof Model, const F extends readonly (keyof InstanceType<M> & string)[]> extends BaseIndex<M, F> {
258
+ /**
259
+ * Save secondary index entry.
260
+ * @param model - Model instance.
261
+ * @param originalKey - Original key if updating.
262
+ */
263
+ _save(model: InstanceType<M>, originalKey?: Uint8Array): Uint8Array | undefined;
264
+ /**
265
+ * Extract model from iterator entry for secondary index.
266
+ * @param keyBytes - Key bytes with index ID already read.
267
+ * @param valueBytes - Value bytes from the entry.
268
+ * @returns Model instance or undefined.
269
+ * @internal
270
+ */
271
+ _getModelFromEntry(keyBytes: Bytes, valueBytes: Bytes): InstanceType<M> | undefined;
272
+ /**
273
+ * Create secondary index key that includes both index fields and primary key.
274
+ * @param model - Model instance.
275
+ * @returns Database key bytes or undefined if skipped.
276
+ */
277
+ _getKeyFromModel(model: InstanceType<M>, includeIndexId: boolean): Uint8Array;
278
+ _getTypeName(): string;
279
+ }
280
+ export type Index<M extends typeof Model, F extends readonly (keyof InstanceType<M> & string)[]> = PrimaryIndex<M, F> | UniqueIndex<M, F> | SecondaryIndex<M, F>;
281
+ /**
282
+ * Create a primary index on model fields.
283
+ * @template M - The model class.
284
+ * @template F - The field name (for single field index).
285
+ * @template FS - The field names array (for composite index).
286
+ * @param MyModel - The model class to create the index for.
287
+ * @param field - Single field name for simple indexes.
288
+ * @param fields - Array of field names for composite indexes.
289
+ * @returns A new PrimaryIndex instance.
290
+ *
291
+ * @example
292
+ * ```typescript
293
+ * class User extends E.Model<User> {
294
+ * static pk = E.primary(User, ["id"]);
295
+ * static pkSingle = E.primary(User, "id");
296
+ * }
297
+ * ```
298
+ */
299
+ export declare function primary<M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): PrimaryIndex<M, [F]>;
300
+ export declare function primary<M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(MyModel: M, fields: FS): PrimaryIndex<M, FS>;
301
+ /**
302
+ * Create a unique index on model fields.
303
+ * @template M - The model class.
304
+ * @template F - The field name (for single field index).
305
+ * @template FS - The field names array (for composite index).
306
+ * @param MyModel - The model class to create the index for.
307
+ * @param field - Single field name for simple indexes.
308
+ * @param fields - Array of field names for composite indexes.
309
+ * @returns A new UniqueIndex instance.
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * class User extends E.Model<User> {
314
+ * static byEmail = E.unique(User, "email");
315
+ * static byNameAge = E.unique(User, ["name", "age"]);
316
+ * }
317
+ * ```
318
+ */
319
+ export declare function unique<M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): UniqueIndex<M, [F]>;
320
+ export declare function unique<M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(MyModel: M, fields: FS): UniqueIndex<M, FS>;
321
+ /**
322
+ * Create a secondary index on model fields.
323
+ * @template M - The model class.
324
+ * @template F - The field name (for single field index).
325
+ * @template FS - The field names array (for composite index).
326
+ * @param MyModel - The model class to create the index for.
327
+ * @param field - Single field name for simple indexes.
328
+ * @param fields - Array of field names for composite indexes.
329
+ * @returns A new SecondaryIndex instance.
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * class User extends E.Model<User> {
334
+ * static byAge = E.index(User, "age");
335
+ * static byTagsDate = E.index(User, ["tags", "createdAt"]);
336
+ * }
337
+ * ```
338
+ */
339
+ export declare function index<M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): SecondaryIndex<M, [F]>;
340
+ export declare function index<M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(MyModel: M, fields: FS): SecondaryIndex<M, FS>;
341
+ /**
342
+ * Dump database contents for debugging.
343
+ *
344
+ * Prints all indexes and their data to the console for inspection.
345
+ * This is primarily useful for development and debugging purposes.
346
+ */
347
+ export declare function dump(): void;
348
+ export {};