edinburgh 0.4.6 → 0.5.0

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.
Files changed (53) hide show
  1. package/README.md +263 -381
  2. package/build/src/datapack.js +1 -1
  3. package/build/src/datapack.js.map +1 -1
  4. package/build/src/edinburgh.d.ts +5 -5
  5. package/build/src/edinburgh.js +6 -7
  6. package/build/src/edinburgh.js.map +1 -1
  7. package/build/src/indexes.d.ts +44 -113
  8. package/build/src/indexes.js +145 -175
  9. package/build/src/indexes.js.map +1 -1
  10. package/build/src/migrate.js +11 -31
  11. package/build/src/migrate.js.map +1 -1
  12. package/build/src/models.d.ts +73 -54
  13. package/build/src/models.js +110 -171
  14. package/build/src/models.js.map +1 -1
  15. package/build/src/types.d.ts +29 -21
  16. package/build/src/types.js +16 -30
  17. package/build/src/types.js.map +1 -1
  18. package/package.json +1 -3
  19. package/skill/BaseIndex_batchProcess.md +1 -1
  20. package/skill/BaseIndex_find.md +2 -2
  21. package/skill/BaseIndex_find_2.md +7 -0
  22. package/skill/BaseIndex_find_3.md +7 -0
  23. package/skill/BaseIndex_find_4.md +7 -0
  24. package/skill/Model.md +5 -7
  25. package/skill/Model_batchProcess.md +8 -0
  26. package/skill/Model_delete.md +1 -1
  27. package/skill/Model_migrate.md +2 -4
  28. package/skill/Model_preCommit.md +2 -4
  29. package/skill/Model_preventPersist.md +1 -1
  30. package/skill/Model_replaceInto.md +2 -2
  31. package/skill/NonPrimaryIndex.md +10 -0
  32. package/skill/SKILL.md +140 -150
  33. package/skill/SecondaryIndex.md +2 -2
  34. package/skill/UniqueIndex.md +2 -2
  35. package/skill/defineModel.md +22 -0
  36. package/skill/field.md +2 -2
  37. package/skill/link.md +11 -9
  38. package/skill/transact.md +2 -2
  39. package/src/datapack.ts +1 -1
  40. package/src/edinburgh.ts +6 -9
  41. package/src/indexes.ts +155 -271
  42. package/src/migrate.ts +9 -30
  43. package/src/models.ts +186 -180
  44. package/src/types.ts +31 -26
  45. package/skill/Model_findAll.md +0 -12
  46. package/skill/PrimaryIndex.md +0 -8
  47. package/skill/PrimaryIndex_get.md +0 -17
  48. package/skill/PrimaryIndex_getLazy.md +0 -13
  49. package/skill/UniqueIndex_get.md +0 -17
  50. package/skill/index.md +0 -32
  51. package/skill/primary.md +0 -26
  52. package/skill/registerModel.md +0 -26
  53. package/skill/unique.md +0 -32
package/src/types.ts CHANGED
@@ -2,7 +2,6 @@ import DataPack from "./datapack.js";
2
2
  import { DatabaseError } from "olmdb/lowlevel";
3
3
  import { Model, modelRegistry, FieldConfig, currentTxn } from "./models.js";
4
4
  import { assert, addErrorPath, dbGet } from "./utils.js";
5
- import { PrimaryIndex, BaseIndex, IndexRangeIterator } from "./indexes.js";
6
5
 
7
6
 
8
7
  /**
@@ -71,7 +70,7 @@ export abstract class TypeWrapper<const T> {
71
70
  return value1 === value2;
72
71
  }
73
72
 
74
- getLinkedModel(): (typeof Model<unknown>) | undefined {
73
+ getLinkedModel(): undefined | typeof Model<unknown> {
75
74
  return;
76
75
  }
77
76
  }
@@ -355,7 +354,7 @@ class RecordType<T> extends TypeWrapper<Record<string | number, T>> {
355
354
 
356
355
  serialize(value: Record<string | number, T>, pack: DataPack) {
357
356
  pack.writeCollectionBoundary('object');
358
- for (const key of Object.keys(value)) {
357
+ for (const key in value) {
359
358
  pack.writeObjectKey(key);
360
359
  this.inner.serialize(value[key], pack);
361
360
  }
@@ -374,7 +373,7 @@ class RecordType<T> extends TypeWrapper<Record<string | number, T>> {
374
373
  }
375
374
 
376
375
  getError(value: Record<string | number, T>) {
377
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
376
+ if (typeof value !== 'object' || value === null || Array.isArray(value) || (typeof value.length === 'number' && typeof value[Symbol.iterator as any] === 'function')) {
378
377
  return new DatabaseError(`Expected object, got ${typeof value}`, 'INVALID_TYPE');
379
378
  }
380
379
  for (const key of Object.keys(value)) {
@@ -605,17 +604,22 @@ class IdentifierType extends TypeWrapper<string> {
605
604
  * @internal Type wrapper for model relationships (foreign keys).
606
605
  * @template T - The target model class type.
607
606
  */
608
- export class LinkType<T extends typeof Model<unknown>> extends TypeWrapper<InstanceType<T>> {
607
+ export class LinkType<T extends new (...args: any[]) => Model<any>> extends TypeWrapper<InstanceType<T>> {
609
608
  kind = 'link';
610
- tableName: string;
609
+ private TargetModel: T | (() => T);
611
610
 
612
611
  /**
613
612
  * Create a new LinkType.
614
- * @param TargetModel - The model class this link points to.
613
+ * @param TargetModel - The model class this link points to, or a thunk for forward references.
615
614
  */
616
- constructor(TargetModel: T) {
615
+ constructor(TargetModel: T | (() => T)) {
617
616
  super();
618
- this.tableName = (TargetModel as any).tableName || TargetModel.name;
617
+ this.TargetModel = TargetModel;
618
+ }
619
+
620
+ getLinkedModel(): typeof Model<unknown> {
621
+ if (!('getLazy' in this.TargetModel)) this.TargetModel = (this.TargetModel as unknown as () => T)();
622
+ return this.TargetModel as any;
619
623
  }
620
624
 
621
625
  serialize(model: InstanceType<T>, pack: DataPack) {
@@ -623,31 +627,28 @@ export class LinkType<T extends typeof Model<unknown>> extends TypeWrapper<Insta
623
627
  }
624
628
 
625
629
  deserialize(pack: DataPack) {
626
- return modelRegistry[this.tableName]._primary!._get(currentTxn(), pack.readUint8Array(), false);
630
+ return this.getLinkedModel()._primary._get(currentTxn(), pack.readUint8Array(), false);
627
631
  }
628
632
 
629
633
  getError(value: InstanceType<T>) {
630
- if (!(value instanceof modelRegistry[this.tableName])) {
631
- return new DatabaseError(`Expected instance of ${this.tableName}, got ${typeof value}`, 'VALUE_ERROR');
634
+ const TargetModel = this.getLinkedModel();
635
+ if (!((value as any) instanceof TargetModel)) {
636
+ return new DatabaseError(`Expected instance of ${TargetModel.tableName}, got ${typeof value}`, 'VALUE_ERROR');
632
637
  }
633
638
  }
634
639
 
635
640
  serializeType(pack: DataPack): void {
636
- pack.write(this.tableName);
641
+ pack.write(this.getLinkedModel().tableName);
637
642
  }
638
643
 
639
644
  static deserializeType(pack: DataPack, featureFlags: number): LinkType<any> {
640
645
  const tableName = pack.readString();
641
646
  const targetModel = modelRegistry[tableName];
642
647
  if (!targetModel) throw new DatabaseError(`Could not deserialize undefined model ${tableName}`, 'DESERIALIZATION_ERROR');
643
- return new LinkType(targetModel);
648
+ return new LinkType(targetModel as any);
644
649
  }
645
650
 
646
- toString() { return `link<${this.tableName}>`; }
647
-
648
- getLinkedModel(): T {
649
- return modelRegistry[this.tableName] as T;
650
- }
651
+ toString() { return `link<${this.getLinkedModel().tableName}>`; }
651
652
  }
652
653
 
653
654
  /** Type wrapper instance for the string type. */
@@ -781,16 +782,20 @@ export function record<const T>(inner: TypeWrapper<T>): TypeWrapper<Record<strin
781
782
  *
782
783
  * @example
783
784
  * ```typescript
784
- * class User extends E.Model<User> {
785
- * posts = E.field(E.array(E.link(Post, 'author')));
786
- * }
785
+ * const Author = E.defineModel(class {
786
+ * id = E.field(E.identifier);
787
+ * posts = E.field(E.array(E.link(() => Book)));
788
+ * }, { pk: "id" });
787
789
  *
788
- * class Post extends E.Model<Post> {
789
- * author = E.field(E.link(User));
790
- * }
790
+ * const Book = E.defineModel(class {
791
+ * id = E.field(E.identifier);
792
+ * author = E.field(E.link(Author));
793
+ * }, { pk: "id" });
791
794
  * ```
792
795
  */
793
- export function link<const T extends typeof Model<any>>(TargetModel: T): TypeWrapper<InstanceType<T>> {
796
+ export function link<const T extends new (...args: any[]) => Model<any>>(TargetModel: T): TypeWrapper<InstanceType<T>>;
797
+ export function link<const T extends new (...args: any[]) => Model<any>>(TargetModel: () => T): TypeWrapper<InstanceType<T>>;
798
+ export function link(TargetModel: any): TypeWrapper<any> {
794
799
  return new LinkType(TargetModel);
795
800
  }
796
801
 
@@ -1,12 +0,0 @@
1
- #### Model.findAll · static method
2
-
3
- Find all instances of this model in the database, ordered by primary key.
4
-
5
- **Signature:** `<T extends typeof Model<unknown>>(this: T, opts?: { reverse?: boolean; }) => IndexRangeIterator<T>`
6
-
7
- **Parameters:**
8
-
9
- - `this: T`
10
- - `opts?: {reverse?: boolean}` - - Optional parameters.
11
-
12
- **Returns:** An iterator.
@@ -1,8 +0,0 @@
1
- ### PrimaryIndex · class
2
-
3
- Primary index that stores the actual model data.
4
-
5
- **Type Parameters:**
6
-
7
- - `M extends typeof Model` - The model class this index belongs to.
8
- - `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
@@ -1,17 +0,0 @@
1
- #### primaryIndex.get · method
2
-
3
- Get a model instance by primary key values.
4
-
5
- **Signature:** `(...args: IndexArgTypes<M, F>) => InstanceType<M>`
6
-
7
- **Parameters:**
8
-
9
- - `args: IndexArgTypes<M, F>` - - The primary key values.
10
-
11
- **Returns:** The model instance if found, undefined otherwise.
12
-
13
- **Examples:**
14
-
15
- ```typescript
16
- const user = User.pk.get("john_doe");
17
- ```
@@ -1,13 +0,0 @@
1
- #### primaryIndex.getLazy · method
2
-
3
- Does the same as as `get()`, but will delay loading the instance from disk until the first
4
- property access. In case it turns out the instance doesn't exist, an error will be thrown
5
- at that time.
6
-
7
- **Signature:** `(...args: IndexArgTypes<M, F>) => InstanceType<M>`
8
-
9
- **Parameters:**
10
-
11
- - `args: IndexArgTypes<M, F>` - Primary key field values. (Or a single Uint8Array containing the key.)
12
-
13
- **Returns:** The (lazily loaded) model instance.
@@ -1,17 +0,0 @@
1
- #### uniqueIndex.get · method
2
-
3
- Get a model instance by unique index key values.
4
-
5
- **Signature:** `(...args: ARGS) => InstanceType<M>`
6
-
7
- **Parameters:**
8
-
9
- - `args: ARGS` - - The unique index key values.
10
-
11
- **Returns:** The model instance if found, undefined otherwise.
12
-
13
- **Examples:**
14
-
15
- ```typescript
16
- const userByEmail = User.byEmail.get("john@example.com");
17
- ```
package/skill/index.md DELETED
@@ -1,32 +0,0 @@
1
- ### index · function
2
-
3
- Create a secondary index on model fields, or a computed secondary index using a function.
4
-
5
- For field-based indexes, pass a field name or array of field names.
6
- For computed indexes, pass a function that takes a model instance and returns an array of
7
- index keys. Return `[]` to skip indexing for that instance. Each array element creates a
8
- separate index entry, enabling multi-value indexes (e.g., indexing by each word in a name).
9
-
10
- **Signature:** `{ <M extends typeof Model, V>(MyModel: M, fn: (instance: InstanceType<M>) => V[]): SecondaryIndex<M, [], [V]>; <M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): SecondaryIndex<...>; <M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(...`
11
-
12
- **Type Parameters:**
13
-
14
- - `M extends typeof Model` - The model class.
15
- - `V` - The computed index value type (for function-based indexes).
16
-
17
- **Parameters:**
18
-
19
- - `MyModel: M` - - The model class to create the index for.
20
- - `fn: (instance: InstanceType<M>) => V[]`
21
-
22
- **Returns:** A new SecondaryIndex instance.
23
-
24
- **Examples:**
25
-
26
- ```typescript
27
- class User extends E.Model<User> {
28
- static byAge = E.index(User, "age");
29
- static byTagsDate = E.index(User, ["tags", "createdAt"]);
30
- static byWord = E.index(User, (u: User) => u.name.split(" "));
31
- }
32
- ```
package/skill/primary.md DELETED
@@ -1,26 +0,0 @@
1
- ### primary · function
2
-
3
- Create a primary index on model fields.
4
-
5
- **Signature:** `{ <M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): PrimaryIndex<M, [F]>; <M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(MyModel: M, fields: FS): PrimaryIndex<...>; }`
6
-
7
- **Type Parameters:**
8
-
9
- - `M extends typeof Model` - The model class.
10
- - `F extends (keyof InstanceType<M> & string)` - The field name (for single field index).
11
-
12
- **Parameters:**
13
-
14
- - `MyModel: M` - - The model class to create the index for.
15
- - `field: F` - - Single field name for simple indexes.
16
-
17
- **Returns:** A new PrimaryIndex instance.
18
-
19
- **Examples:**
20
-
21
- ```typescript
22
- class User extends E.Model<User> {
23
- static pk = E.primary(User, ["id"]);
24
- static pkSingle = E.primary(User, "id");
25
- }
26
- ```
@@ -1,26 +0,0 @@
1
- ### registerModel · function
2
-
3
- Register a model class with the Edinburgh ORM system.
4
-
5
- **Signature:** `<T extends typeof Model<unknown>>(MyModel: T) => T`
6
-
7
- **Type Parameters:**
8
-
9
- - `T extends typeof Model<unknown>` - The model class type.
10
-
11
- **Parameters:**
12
-
13
- - `MyModel: T` - - The model class to register.
14
-
15
- **Returns:** The enhanced model class with ORM capabilities.
16
-
17
- **Examples:**
18
-
19
- ```typescript
20
- ⁣@E.registerModel
21
- class User extends E.Model<User> {
22
- static pk = E.index(User, ["id"], "primary");
23
- id = E.field(E.identifier);
24
- name = E.field(E.string);
25
- }
26
- ```
package/skill/unique.md DELETED
@@ -1,32 +0,0 @@
1
- ### unique · function
2
-
3
- Create a unique index on model fields, or a computed unique index using a function.
4
-
5
- For field-based indexes, pass a field name or array of field names.
6
- For computed indexes, pass a function that takes a model instance and returns an array of
7
- index keys. Return `[]` to skip indexing for that instance. Each array element creates a
8
- separate index entry, enabling multi-value indexes (e.g., indexing by each word in a name).
9
-
10
- **Signature:** `{ <M extends typeof Model, V>(MyModel: M, fn: (instance: InstanceType<M>) => V[]): UniqueIndex<M, [], [V]>; <M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): UniqueIndex<...>; <M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(MyMode...`
11
-
12
- **Type Parameters:**
13
-
14
- - `M extends typeof Model` - The model class.
15
- - `V` - The computed index value type (for function-based indexes).
16
-
17
- **Parameters:**
18
-
19
- - `MyModel: M` - - The model class to create the index for.
20
- - `fn: (instance: InstanceType<M>) => V[]`
21
-
22
- **Returns:** A new UniqueIndex instance.
23
-
24
- **Examples:**
25
-
26
- ```typescript
27
- class User extends E.Model<User> {
28
- static byEmail = E.unique(User, "email");
29
- static byNameAge = E.unique(User, ["name", "age"]);
30
- static byFullName = E.unique(User, (u: User) => [`${u.firstName} ${u.lastName}`]);
31
- }
32
- ```