edinburgh 0.6.0 → 0.6.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 +75 -53
- package/build/src/edinburgh.d.ts +3 -0
- package/build/src/edinburgh.js +25 -18
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +4 -1
- package/build/src/indexes.js +10 -1
- package/build/src/indexes.js.map +1 -1
- package/build/src/models.d.ts +17 -8
- package/build/src/models.js +10 -2
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +17 -1
- package/build/src/types.js.map +1 -1
- package/package.json +2 -2
- package/skill/FindOptions.md +4 -1
- package/skill/SKILL.md +16 -0
- package/skill/field.md +4 -4
- package/skill/link.md +1 -1
- package/skill/setOnSaveCallback.md +3 -0
- package/src/edinburgh.ts +27 -18
- package/src/indexes.ts +16 -2
- package/src/models.ts +18 -9
- package/src/types.ts +33 -2
package/src/indexes.ts
CHANGED
|
@@ -22,6 +22,17 @@ type IndexArgTypes<ITEM, F extends readonly (keyof ITEM & string)[]> = {
|
|
|
22
22
|
[I in keyof F]: ITEM[F[I]]
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
function serializeIndexArg(fieldType: TypeWrapper<any>, arg: any, pack: DataPack) {
|
|
26
|
+
const linkedModel = fieldType.getLinkedModel();
|
|
27
|
+
if (!linkedModel || arg instanceof linkedModel) {
|
|
28
|
+
fieldType.serialize(arg, pack);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Link lookups may pass the target row's primary key instead of a loaded instance.
|
|
33
|
+
pack.write(linkedModel._argsToKeyBytes(Array.isArray(arg) ? arg : [arg], false).toUint8Array());
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
const MAX_INDEX_ID_PREFIX = -1;
|
|
26
37
|
const INDEX_ID_PREFIX = -2;
|
|
27
38
|
const VERSION_INFO_PREFIX = -3;
|
|
@@ -88,7 +99,10 @@ type ArrayOrOnlyItem<ARG_TYPES extends readonly any[]> = ARG_TYPES extends reado
|
|
|
88
99
|
* exclusive bounds via `after` / `before`, and reverse scans.
|
|
89
100
|
*
|
|
90
101
|
* For single-field indexes, values can be passed directly. For composite indexes,
|
|
91
|
-
* pass tuples or partial tuples for prefix matching.
|
|
102
|
+
* pass tuples or partial tuples for prefix matching. If an index field is a
|
|
103
|
+
* `link(...)`, you may pass either the linked model instance or the linked
|
|
104
|
+
* model's primary key. Composite linked primary keys are passed as tuples in
|
|
105
|
+
* that slot.
|
|
92
106
|
*
|
|
93
107
|
* @template ARG_TYPES - Tuple of index argument types.
|
|
94
108
|
* @template FETCH - Optional fetch mode used by overloads that return one row.
|
|
@@ -182,7 +196,7 @@ export abstract class BaseIndex<ITEM, const F extends readonly (keyof ITEM & str
|
|
|
182
196
|
let index = 0;
|
|
183
197
|
for (const fieldType of this._indexFields.values()) {
|
|
184
198
|
if (index >= args.length) break;
|
|
185
|
-
fieldType
|
|
199
|
+
serializeIndexArg(fieldType, args[index++], bytes);
|
|
186
200
|
}
|
|
187
201
|
}
|
|
188
202
|
return bytes;
|
package/src/models.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as lowlevel from "olmdb/lowlevel";
|
|
2
2
|
import { DatabaseError } from "olmdb/lowlevel";
|
|
3
3
|
import DataPack from "./datapack.js";
|
|
4
|
-
import { deserializeType, serializeType, TypeWrapper, identifier } from "./types.js";
|
|
4
|
+
import { deserializeType, serializeType, TypeWrapper, identifier, type FieldQueryArg, type FieldValue } from "./types.js";
|
|
5
5
|
import { transact, currentTxn, type Transaction } from "./edinburgh.js";
|
|
6
6
|
|
|
7
7
|
import { PrimaryKey, NonPrimaryIndex, IndexRangeIterator, UniqueIndex, SecondaryIndex, FindOptions, VersionInfo } from "./indexes.js";
|
|
@@ -49,7 +49,7 @@ export interface FieldConfig<T> {
|
|
|
49
49
|
* });
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
export function field<
|
|
52
|
+
export function field<TYPE extends TypeWrapper<any>>(type: TYPE, options: Partial<FieldConfig<FieldValue<TYPE>>> = {}): FieldValue<TYPE> {
|
|
53
53
|
// Return the config object, but TypeScript sees it as type T
|
|
54
54
|
options.type = type;
|
|
55
55
|
return options as any;
|
|
@@ -68,16 +68,16 @@ type FieldsOf<T> = T extends new () => infer I ? I : never;
|
|
|
68
68
|
|
|
69
69
|
type PKArgs<FIELDS, PK> =
|
|
70
70
|
PK extends readonly (keyof FIELDS & string)[]
|
|
71
|
-
? { [I in keyof PK]: PK[I] extends keyof FIELDS ? FIELDS[PK[I]] : never }
|
|
71
|
+
? { [I in keyof PK]: PK[I] extends keyof FIELDS ? FieldQueryArg<FIELDS[PK[I]]> : never }
|
|
72
72
|
: PK extends keyof FIELDS & string
|
|
73
|
-
? [FIELDS[PK]]
|
|
73
|
+
? [FieldQueryArg<FIELDS[PK]>]
|
|
74
74
|
: [string];
|
|
75
75
|
|
|
76
76
|
type IndexArgs<FIELDS, SPEC> =
|
|
77
77
|
SPEC extends readonly (keyof FIELDS & string)[]
|
|
78
|
-
? { [I in keyof SPEC]: SPEC[I] extends keyof FIELDS ? FIELDS[SPEC[I]] : never }
|
|
78
|
+
? { [I in keyof SPEC]: SPEC[I] extends keyof FIELDS ? FieldQueryArg<FIELDS[SPEC[I]]> : never }
|
|
79
79
|
: SPEC extends keyof FIELDS & string
|
|
80
|
-
? [FIELDS[SPEC]]
|
|
80
|
+
? [FieldQueryArg<FIELDS[SPEC]>]
|
|
81
81
|
: SPEC extends (instance: any) => infer R
|
|
82
82
|
? R extends (infer V)[] ? [V] : [R]
|
|
83
83
|
: never;
|
|
@@ -209,7 +209,7 @@ class ModelClassRuntime<FIELDS, PKA extends readonly any[], UNIQUE = {}, INDEX =
|
|
|
209
209
|
key = args;
|
|
210
210
|
} else {
|
|
211
211
|
key = this._argsToKeyBytes(args, false).toUint8Array();
|
|
212
|
-
keyParts =
|
|
212
|
+
keyParts = this._pkToArray(key);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
const keyHash = hashBytes(key);
|
|
@@ -269,6 +269,10 @@ class ModelClassRuntime<FIELDS, PKA extends readonly any[], UNIQUE = {}, INDEX =
|
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
271
|
* Load a model by primary key inside the current transaction.
|
|
272
|
+
*
|
|
273
|
+
* For `link(...)` primary-key fields, each argument may be the linked model
|
|
274
|
+
* instance or the linked model's primary key. Composite linked primary keys
|
|
275
|
+
* are passed as a tuple in that argument slot.
|
|
272
276
|
*
|
|
273
277
|
* @returns The matching model, or `undefined` if no row exists.
|
|
274
278
|
*/
|
|
@@ -278,6 +282,8 @@ class ModelClassRuntime<FIELDS, PKA extends readonly any[], UNIQUE = {}, INDEX =
|
|
|
278
282
|
|
|
279
283
|
/**
|
|
280
284
|
* Load a model by primary key without fetching its non-key fields immediately.
|
|
285
|
+
*
|
|
286
|
+
* Link-valued primary-key fields accept the same shorthand as `get()`.
|
|
281
287
|
*
|
|
282
288
|
* Accessing a lazy field later will load the remaining fields transparently.
|
|
283
289
|
*/
|
|
@@ -321,7 +327,9 @@ class ModelClassRuntime<FIELDS, PKA extends readonly any[], UNIQUE = {}, INDEX =
|
|
|
321
327
|
* Look up a model through a named unique index.
|
|
322
328
|
*
|
|
323
329
|
* @param name The name from the model's `unique` definition.
|
|
324
|
-
|
|
330
|
+
* @param args The unique-index key values. For `link(...)` fields, pass
|
|
331
|
+
* either the linked model instance or the linked model's primary key. If the
|
|
332
|
+
* linked model uses a composite primary key, pass the full tuple in that slot.
|
|
325
333
|
* @returns The matching model instance, if any.
|
|
326
334
|
*/
|
|
327
335
|
getBy<K extends string & keyof UNIQUE>(name: K, ...args: IndexArgs<FIELDS, UNIQUE[K]>): Model<FIELDS> | undefined {
|
|
@@ -332,7 +340,8 @@ class ModelClassRuntime<FIELDS, PKA extends readonly any[], UNIQUE = {}, INDEX =
|
|
|
332
340
|
* Query rows through a named unique or secondary index.
|
|
333
341
|
*
|
|
334
342
|
* This mirrors `find()`, but targets a named entry from the model's `unique`
|
|
335
|
-
|
|
343
|
+
* or `index` registration. Link-valued index fields accept either the linked
|
|
344
|
+
* model instance or the linked model's primary key tuple/value.
|
|
336
345
|
*/
|
|
337
346
|
findBy<K extends string & keyof (UNIQUE & INDEX)>(name: K, opts: FindOptions<IndexArgs<FIELDS, (UNIQUE & INDEX)[K]>, 'first'>): Model<FIELDS> | undefined;
|
|
338
347
|
findBy<K extends string & keyof (UNIQUE & INDEX)>(name: K, opts: FindOptions<IndexArgs<FIELDS, (UNIQUE & INDEX)[K]>, 'single'>): Model<FIELDS>;
|
package/src/types.ts
CHANGED
|
@@ -87,6 +87,35 @@ export interface TypeWrapper<T> {
|
|
|
87
87
|
default?(model: any): T;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// Hidden type-only metadata used to widen lookup arguments without widening assignment types.
|
|
91
|
+
export declare const QUERY_ARG: unique symbol;
|
|
92
|
+
|
|
93
|
+
export type LinkTargetPKArgs<T extends new (...args: any) => any> =
|
|
94
|
+
T extends { get(...args: infer PKA): any } ? PKA : never;
|
|
95
|
+
|
|
96
|
+
export type LinkPrimaryKeyInput<PKA extends readonly any[]> =
|
|
97
|
+
PKA extends readonly [infer ONLY]
|
|
98
|
+
? ONLY | PKA
|
|
99
|
+
: PKA;
|
|
100
|
+
|
|
101
|
+
type QueryArgCarrier<QUERY> = {
|
|
102
|
+
readonly [QUERY_ARG]?: QUERY;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type QueryAnnotated<T, QUERY> = T & QueryArgCarrier<QUERY>;
|
|
106
|
+
|
|
107
|
+
export type FieldValue<TYPE extends TypeWrapper<any>> =
|
|
108
|
+
TYPE extends TypeWrapper<infer T>
|
|
109
|
+
? TYPE extends { readonly [QUERY_ARG]?: infer QUERY }
|
|
110
|
+
? QueryAnnotated<T, Exclude<QUERY, undefined>>
|
|
111
|
+
: T
|
|
112
|
+
: never;
|
|
113
|
+
|
|
114
|
+
export type FieldQueryArg<T> =
|
|
115
|
+
T extends { readonly [QUERY_ARG]?: infer QUERY }
|
|
116
|
+
? Exclude<QUERY, undefined>
|
|
117
|
+
: T;
|
|
118
|
+
|
|
90
119
|
|
|
91
120
|
class StringType extends TypeWrapper<string> {
|
|
92
121
|
kind = 'string';
|
|
@@ -620,7 +649,7 @@ export class LinkType<T extends new (...args: any[]) => Model<any>> extends Type
|
|
|
620
649
|
}
|
|
621
650
|
|
|
622
651
|
getLinkedModel(): AnyModelClass {
|
|
623
|
-
if (!('getLazy' in this.TargetModel)) this.TargetModel = (this.TargetModel as unknown as () => T)();
|
|
652
|
+
if (!('getLazy' in (this.TargetModel as any))) this.TargetModel = (this.TargetModel as unknown as () => T)();
|
|
624
653
|
return this.TargetModel as any;
|
|
625
654
|
}
|
|
626
655
|
|
|
@@ -795,7 +824,9 @@ export function record<const T>(inner: TypeWrapper<T>): TypeWrapper<Record<strin
|
|
|
795
824
|
* }, { pk: "id" });
|
|
796
825
|
* ```
|
|
797
826
|
*/
|
|
798
|
-
export function link<const T extends new (...args: any[]) => Model<any>>(
|
|
827
|
+
export function link<const T extends new (...args: any[]) => Model<any>>(
|
|
828
|
+
TargetModel: T,
|
|
829
|
+
): TypeWrapper<InstanceType<T>> & QueryArgCarrier<InstanceType<T> | LinkPrimaryKeyInput<LinkTargetPKArgs<T>>>;
|
|
799
830
|
export function link<const T extends new (...args: any[]) => Model<any>>(TargetModel: () => T): TypeWrapper<InstanceType<T>>;
|
|
800
831
|
export function link(TargetModel: any): TypeWrapper<any> {
|
|
801
832
|
return new LinkType(TargetModel);
|