edinburgh 0.3.0 → 0.4.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/src/types.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { DataPack } from "./datapack.js";
2
- import * as olmdb from "olmdb";
3
- import { DatabaseError } from "olmdb";
4
- import { Model, modelRegistry, getMockModel } from "./models.js";
5
- import { assert, addErrorPath } from "./utils.js";
1
+ import DataPack from "./datapack.js";
2
+ import { DatabaseError } from "olmdb/lowlevel";
3
+ import { Model, modelRegistry, FieldConfig, currentTxn } from "./models.js";
4
+ import { assert, addErrorPath, dbGet } from "./utils.js";
5
+ import { PrimaryIndex, BaseIndex, IndexRangeIterator } from "./indexes.js";
6
6
 
7
7
 
8
8
  /**
@@ -70,6 +70,10 @@ export abstract class TypeWrapper<const T> {
70
70
  equals(value1: T, value2: T): boolean {
71
71
  return value1 === value2;
72
72
  }
73
+
74
+ getLinkedModel(): (typeof Model<unknown>) | undefined {
75
+ return;
76
+ }
73
77
  }
74
78
 
75
79
 
@@ -226,6 +230,10 @@ class ArrayType<T> extends TypeWrapper<T[]> {
226
230
  return new ArrayType(inner);
227
231
  }
228
232
 
233
+ default(): T[] {
234
+ return [];
235
+ }
236
+
229
237
  clone(value: T[]): T[] {
230
238
  return value.map(this.inner.clone.bind(this.inner));
231
239
  }
@@ -238,6 +246,11 @@ class ArrayType<T> extends TypeWrapper<T[]> {
238
246
  return true;
239
247
  }
240
248
 
249
+ toString() { return `array<${this.inner}>`; }
250
+
251
+ getLinkedModel() {
252
+ return this.inner.getLinkedModel();
253
+ }
241
254
  }
242
255
 
243
256
  /**
@@ -320,6 +333,12 @@ export class SetType<T> extends TypeWrapper<Set<T>> {
320
333
  }
321
334
  return true;
322
335
  }
336
+
337
+ toString() { return `set<${this.inner}>`; }
338
+
339
+ getLinkedModel() {
340
+ return this.inner.getLinkedModel();
341
+ }
323
342
  }
324
343
 
325
344
 
@@ -398,6 +417,20 @@ class OrType<const T> extends TypeWrapper<T> {
398
417
  const cb = this._getChoiceIndex(b);
399
418
  return ca === cb && this.choices[ca].equals(a, b);
400
419
  }
420
+
421
+ toString() { return `or<${this.choices.join('|')}>`; }
422
+
423
+ getLinkedModel() {
424
+ let model;
425
+ for (const choice of this.choices) {
426
+ const m = choice.getLinkedModel();
427
+ if (m) {
428
+ if (model && model !== m) throw new DatabaseError(`Union type has multiple linked models, unsupported by getLinkedModel()`, 'INVALID_TYPE');
429
+ model = m;
430
+ }
431
+ }
432
+ return model;
433
+ }
401
434
  }
402
435
 
403
436
  /**
@@ -443,6 +476,8 @@ class LiteralType<const T> extends TypeWrapper<T> {
443
476
  return new LiteralType(value);
444
477
  }
445
478
 
479
+ toString() { return `literal<${JSON.stringify(this.value)}>`; }
480
+
446
481
  default(): T {
447
482
  return this.value;
448
483
  }
@@ -481,7 +516,7 @@ class IdentifierType extends TypeWrapper<string> {
481
516
  let id: string;
482
517
  do {
483
518
  id = DataPack.generateIdentifier();
484
- } while (olmdb.get(new DataPack().write(model.constructor._primary!._cachedIndexId!).writeIdentifier(id).toUint8Array()));
519
+ } while (dbGet(model._txn.id, new DataPack().write(model.constructor._primary!._indexId!).writeIdentifier(id).toUint8Array()));
485
520
  return id;
486
521
  }
487
522
  }
@@ -492,33 +527,33 @@ class IdentifierType extends TypeWrapper<string> {
492
527
  */
493
528
  export class LinkType<T extends typeof Model<unknown>> extends TypeWrapper<InstanceType<T>> {
494
529
  kind = 'link';
495
- TargetModel: T;
496
-
530
+ tableName: string;
531
+
497
532
  /**
498
533
  * Create a new LinkType.
499
534
  * @param TargetModel - The model class this link points to.
500
535
  */
501
536
  constructor(TargetModel: T) {
502
537
  super();
503
- this.TargetModel = getMockModel(TargetModel);
538
+ this.tableName = (TargetModel as any).tableName || TargetModel.name;
504
539
  }
505
540
 
506
541
  serialize(model: InstanceType<T>, pack: DataPack) {
507
- pack.write(model._getCreatePrimaryKey());
542
+ pack.write(model.getPrimaryKey());
508
543
  }
509
544
 
510
545
  deserialize(pack: DataPack) {
511
- return this.TargetModel._primary!.getLazy(pack.readUint8Array());
546
+ return modelRegistry[this.tableName]._primary!._get(currentTxn(), pack.readUint8Array(), false);
512
547
  }
513
548
 
514
549
  getError(value: InstanceType<T>) {
515
- if (!(value instanceof this.TargetModel)) {
516
- return new DatabaseError(`Expected instance of ${this.TargetModel.tableName}, got ${typeof value}`, 'VALUE_ERROR');
550
+ if (!(value instanceof modelRegistry[this.tableName])) {
551
+ return new DatabaseError(`Expected instance of ${this.tableName}, got ${typeof value}`, 'VALUE_ERROR');
517
552
  }
518
553
  }
519
554
 
520
555
  serializeType(pack: DataPack): void {
521
- pack.write(this.TargetModel.tableName);
556
+ pack.write(this.tableName);
522
557
  }
523
558
 
524
559
  static deserializeType(pack: DataPack, featureFlags: number): LinkType<any> {
@@ -527,6 +562,12 @@ export class LinkType<T extends typeof Model<unknown>> extends TypeWrapper<Insta
527
562
  if (!targetModel) throw new DatabaseError(`Could not deserialize undefined model ${tableName}`, 'DESERIALIZATION_ERROR');
528
563
  return new LinkType(targetModel);
529
564
  }
565
+
566
+ toString() { return `link<${this.tableName}>`; }
567
+
568
+ getLinkedModel(): T {
569
+ return modelRegistry[this.tableName] as T;
570
+ }
530
571
  }
531
572
 
532
573
  /** Type wrapper instance for the string type. */
@@ -685,13 +726,14 @@ export function serializeType(arg: TypeWrapper<any>, pack: DataPack) {
685
726
  const TYPE_WRAPPERS: Record<string, TypeWrapper<any> | {deserializeType: (pack: DataPack, featureFlags: number) => TypeWrapper<any>}> = {
686
727
  string: string,
687
728
  number: number,
729
+ dateTime: dateTime,
730
+ boolean: boolean,
688
731
  array: ArrayType,
732
+ set: SetType,
689
733
  or: OrType,
690
734
  literal: LiteralType,
691
- boolean: boolean,
692
735
  id: identifier,
693
736
  link: LinkType,
694
- set: SetType
695
737
  };
696
738
 
697
739
  /**
@@ -703,6 +745,7 @@ const TYPE_WRAPPERS: Record<string, TypeWrapper<any> | {deserializeType: (pack:
703
745
  export function deserializeType(pack: DataPack, featureFlags: number): TypeWrapper<any> {
704
746
  const kind = pack.readString();
705
747
  const TypeWrapper = TYPE_WRAPPERS[kind];
748
+ if (!TypeWrapper) throw new DatabaseError(`Unknown field type in database: ${kind}`, 'CONSISTENCY_ERROR');
706
749
  if ('deserializeType' in TypeWrapper) {
707
750
  return TypeWrapper.deserializeType(pack, featureFlags);
708
751
  } else {
package/src/utils.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as lowlevel from "olmdb/lowlevel";
2
+
1
3
  /**
2
4
  * Assert function for runtime checks with TypeScript assertion support.
3
5
  * @param cond - Condition to check.
@@ -10,6 +12,33 @@ export function assert(cond: any, message?: string): asserts cond {
10
12
  }
11
13
  }
12
14
 
15
+ export function toBuffer(data: Uint8Array): ArrayBufferLike {
16
+ const b = data.buffer;
17
+ return b.byteLength === data.byteLength ? b : b.slice(data.byteOffset, data.byteOffset + data.byteLength);
18
+ }
19
+
20
+ export function hashBytes(data: Uint8Array): number {
21
+ let a = 0x811C9DC5, b = 0x811C9DC5;
22
+ for (const ch of data) {
23
+ a = Math.imul(a ^ ch, 0x517CC1B7) >>> 0;
24
+ b = Math.imul(b ^ ch, 0x27220A95) >>> 0;
25
+ }
26
+ return a + ((b & 0x1FFFFF) * 0x100000000);
27
+ }
28
+
29
+ export function dbGet(txnId: number, key: Uint8Array): Uint8Array | undefined {
30
+ const result = lowlevel.get(txnId, toBuffer(key));
31
+ return result ? new Uint8Array(result) : undefined;
32
+ }
33
+
34
+ export function dbPut(txnId: number, key: Uint8Array, value: Uint8Array): void {
35
+ lowlevel.put(txnId, toBuffer(key), toBuffer(value));
36
+ }
37
+
38
+ export function dbDel(txnId: number, key: Uint8Array): void {
39
+ lowlevel.del(txnId, toBuffer(key));
40
+ }
41
+
13
42
  /**
14
43
  * Regular expression for parsing error messages with paths.
15
44
  */
@@ -29,39 +58,10 @@ export function addErrorPath<T>(error: T, path: string | number): T {
29
58
  return error;
30
59
  }
31
60
 
32
- // Get initial log level from environment variable
33
- export let logLevel = parseInt(process.env.EDINBURGH_LOG_LEVEL || "0") || 0;
34
-
35
- /**
36
- * Global log level for debugging output.
37
- * 0 = no logging, 1 = model-level logs, 2 = update logs, 3 = read logs.
38
- */
39
- export function setLogLevel(level: number) {
40
- logLevel = level;
41
- }
61
+ // Get log level from environment variable
62
+ // 0: no logging (default), 1: model-level logs, 2: add update logs, 3: add read logs
63
+ export const logLevel = parseInt(process.env.EDINBURGH_LOG_LEVEL || "0") || 0;
42
64
 
43
65
  /** @internal Symbol used to access the underlying model from a proxy */
44
66
  export declare const TARGET_SYMBOL: unique symbol;
45
67
 
46
- export const delayedInits = new Set<{_delayedInit: () => boolean}>();
47
- let tryingDelayedInits = false;
48
-
49
- export function tryDelayedInits() {
50
- if (tryingDelayedInits) return;
51
- tryingDelayedInits = true;
52
- let progress = true;
53
- while(progress) {
54
- progress = false;
55
- for(const target of delayedInits) {
56
- try {
57
- if (target._delayedInit()) {
58
- delayedInits.delete(target);
59
- progress = true;
60
- }
61
- } catch(e) {
62
- console.error("Error during delayed init:", e);
63
- }
64
- }
65
- }
66
- tryingDelayedInits = false;
67
- }