marcidb-client 0.11.4 → 0.13.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.
- package/README.md +4 -5
- package/bin/marci-generate-darwin-arm64 +0 -0
- package/bin/marci-generate-linux-x64 +0 -0
- package/bin/marci-generate-win32-x64.exe +0 -0
- package/bin/marci-migrate-darwin-arm64 +0 -0
- package/bin/marci-migrate-linux-x64 +0 -0
- package/bin/marci-migrate-win32-x64.exe +0 -0
- package/package.json +1 -1
- package/runtime/index.d.ts +346 -7
- package/runtime/index.js +117 -0
package/README.md
CHANGED
|
@@ -49,11 +49,10 @@ import { marcidb } from "marcidb-client";
|
|
|
49
49
|
|
|
50
50
|
const db = marcidb("http://localhost:3000");
|
|
51
51
|
|
|
52
|
-
// Find
|
|
53
|
-
const users = await db.user.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
52
|
+
// Find — a chainable query; no select = id + every scalar
|
|
53
|
+
const users = await db.user.where({ name: { $startsWith: "A" } }).order("id", "desc").limit(20);
|
|
54
|
+
const named = await db.user.select({ id: true, name: true });
|
|
55
|
+
const one = await db.user.where({ id: 1 }).first();
|
|
57
56
|
|
|
58
57
|
// Insert
|
|
59
58
|
const id = await db.user.insert({
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/runtime/index.d.ts
CHANGED
|
@@ -1,3 +1,348 @@
|
|
|
1
|
+
type ServiceKeys = "$where" | "$order" | "$limit" | "$skip" | "$cursor";
|
|
2
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
type AggregateKeys = {
|
|
6
|
+
$count: true;
|
|
7
|
+
} | {
|
|
8
|
+
$sum: string;
|
|
9
|
+
} | {
|
|
10
|
+
$avg: string;
|
|
11
|
+
} | {
|
|
12
|
+
$min: string;
|
|
13
|
+
} | {
|
|
14
|
+
$max: string;
|
|
15
|
+
};
|
|
16
|
+
type GetResult<
|
|
17
|
+
TModel,
|
|
18
|
+
TSelect
|
|
19
|
+
> = TModel extends any ? { -readonly [K in keyof Omit<TSelect, ServiceKeys> as TSelect[K] extends false | undefined ? never : K extends keyof TModel ? K : never] : K extends keyof TModel ? TSelect[K] extends true ? TModel[K] : TSelect[K] extends Record<string, any> ? TModel[K] extends readonly object[] ? TSelect[K] extends AggregateKeys ? AggregateResult<NonNullable<TModel[K][number]>, TSelect[K]> : GetResult<NonNullable<TModel[K][number]>, TSelect[K]>[] : GetResult<NonNullable<TModel[K]>, TSelect[K]> | Extract<TModel[K], null> : TModel[K] : never } : never;
|
|
20
|
+
type RefUpdate<I> = {
|
|
21
|
+
"$connect"?: I;
|
|
22
|
+
};
|
|
23
|
+
type RefUpdateStruct<
|
|
24
|
+
I,
|
|
25
|
+
U
|
|
26
|
+
> = {
|
|
27
|
+
"$update"?: U;
|
|
28
|
+
"$ensure"?: I;
|
|
29
|
+
"$set"?: I;
|
|
30
|
+
};
|
|
31
|
+
/** Relation to independent rows — link operations only, the rows themselves are never created
|
|
32
|
+
* or deleted. `$set` replaces link membership with exactly the given set (missing links are
|
|
33
|
+
* disconnected, new ones connected); `$connect` links (idempotent); `$remove` unlinks. */
|
|
34
|
+
type RefListUpdate<I> = {
|
|
35
|
+
"$set"?: I[];
|
|
36
|
+
"$connect"?: I | I[];
|
|
37
|
+
"$remove"?: I | I[];
|
|
38
|
+
};
|
|
39
|
+
/** `@list` relation: an ordered inline id array — a sequence, so the same id may appear several
|
|
40
|
+
* times. `$set` replaces the whole array (also the reorder operation); `$connect` appends at the
|
|
41
|
+
* end (an already-present id gains another occurrence); `$connectUnique` appends only ids not
|
|
42
|
+
* already present; `$remove` removes every occurrence. */
|
|
43
|
+
type RefListUpdateOrdered<I> = {
|
|
44
|
+
"$set"?: I[];
|
|
45
|
+
"$connect"?: I | I[];
|
|
46
|
+
"$connectUnique"?: I | I[];
|
|
47
|
+
"$remove"?: I | I[];
|
|
48
|
+
};
|
|
49
|
+
/** Owned (struct) list: the children live and die with the parent. `$push` creates children,
|
|
50
|
+
* `$update` edits single children in place (each item is the child's id fields — the shape
|
|
51
|
+
* query results return — plus the changes under `data`), `$remove` deletes children by id,
|
|
52
|
+
* `$set` replaces all children (deletes the current ones, creates the new). */
|
|
53
|
+
type RefListUpdateStruct<
|
|
54
|
+
I,
|
|
55
|
+
U,
|
|
56
|
+
Id
|
|
57
|
+
> = {
|
|
58
|
+
"$push"?: I | I[];
|
|
59
|
+
"$update"?: (Id & {
|
|
60
|
+
data: U;
|
|
61
|
+
}) | (Id & {
|
|
62
|
+
data: U;
|
|
63
|
+
})[];
|
|
64
|
+
"$remove"?: Id | Id[];
|
|
65
|
+
"$set"?: I[];
|
|
66
|
+
};
|
|
67
|
+
/** Variable-length primitive array — a sequence, so the same value may appear several times.
|
|
68
|
+
* `$push` appends at the end (duplicates kept); `$pushUnique` appends only values not already
|
|
69
|
+
* present; `$remove` removes every occurrence; `$set` replaces the whole array (also the
|
|
70
|
+
* positional-edit path — send the full new array). One operator per update. */
|
|
71
|
+
type PrimitiveListUpdate<T> = {
|
|
72
|
+
"$set"?: T[];
|
|
73
|
+
"$push"?: T | T[];
|
|
74
|
+
"$pushUnique"?: T | T[];
|
|
75
|
+
"$remove"?: T | T[];
|
|
76
|
+
};
|
|
77
|
+
type Never<T> = { [K in keyof T]? : never };
|
|
78
|
+
type FieldOps = {
|
|
79
|
+
"$eq": unknown;
|
|
80
|
+
"$ne": unknown;
|
|
81
|
+
"$not": unknown;
|
|
82
|
+
"$in": unknown;
|
|
83
|
+
"$notIn": unknown;
|
|
84
|
+
"$gt": unknown;
|
|
85
|
+
"$gte": unknown;
|
|
86
|
+
"$lt": unknown;
|
|
87
|
+
"$lte": unknown;
|
|
88
|
+
"$startsWith": unknown;
|
|
89
|
+
"$includes": unknown;
|
|
90
|
+
"$every": unknown;
|
|
91
|
+
"$some": unknown;
|
|
92
|
+
"$none": unknown;
|
|
93
|
+
"$near": unknown;
|
|
94
|
+
"$search": unknown;
|
|
95
|
+
};
|
|
96
|
+
type Only<
|
|
97
|
+
K extends keyof FieldOps,
|
|
98
|
+
V
|
|
99
|
+
> = { [P in K] : V } & Never<Omit<FieldOps, K>>;
|
|
100
|
+
type WhereValue<T> = (T & Never<{
|
|
101
|
+
"$and": unknown;
|
|
102
|
+
"$or": unknown;
|
|
103
|
+
"$not": unknown;
|
|
104
|
+
}>) | ({
|
|
105
|
+
"$and": WhereValue<T>[];
|
|
106
|
+
} & Never<T> & Never<{
|
|
107
|
+
"$or": unknown;
|
|
108
|
+
"$not": unknown;
|
|
109
|
+
}>) | ({
|
|
110
|
+
"$or": WhereValue<T>[];
|
|
111
|
+
} & Never<T> & Never<{
|
|
112
|
+
"$and": unknown;
|
|
113
|
+
"$not": unknown;
|
|
114
|
+
}>) | ({
|
|
115
|
+
"$not": WhereValue<T>;
|
|
116
|
+
} & Never<T> & Never<{
|
|
117
|
+
"$and": unknown;
|
|
118
|
+
"$or": unknown;
|
|
119
|
+
}>);
|
|
120
|
+
type ValueOps<T> = {
|
|
121
|
+
"$eq"?: T;
|
|
122
|
+
"$ne"?: T;
|
|
123
|
+
"$not"?: T;
|
|
124
|
+
"$in"?: T[];
|
|
125
|
+
"$notIn"?: T[];
|
|
126
|
+
};
|
|
127
|
+
type NumOps<T> = {
|
|
128
|
+
"$gt"?: T;
|
|
129
|
+
"$gte"?: T;
|
|
130
|
+
"$lt"?: T;
|
|
131
|
+
"$lte"?: T;
|
|
132
|
+
};
|
|
133
|
+
type StrOps = {
|
|
134
|
+
"$includes"?: string;
|
|
135
|
+
"$startsWith"?: string;
|
|
136
|
+
};
|
|
137
|
+
type CompareValue<T> = T | (ValueOps<T> & Never<Omit<FieldOps, keyof ValueOps<T>>>);
|
|
138
|
+
type CompareNumValue<T> = ValueOps<T> & NumOps<T> & Never<Omit<FieldOps, keyof ValueOps<T> | keyof NumOps<T>>>;
|
|
139
|
+
type CompareStrValue<T> = ValueOps<T> & StrOps & Never<Omit<FieldOps, keyof ValueOps<T> | keyof StrOps>>;
|
|
140
|
+
type UpdateNumValue = {
|
|
141
|
+
"$increment": number;
|
|
142
|
+
};
|
|
143
|
+
type JsonType = "string" | "number" | "boolean" | "object" | "array" | "null";
|
|
144
|
+
type JsonCondition = JsonValue | {
|
|
145
|
+
"$eq"?: JsonValue;
|
|
146
|
+
"$ne"?: JsonValue;
|
|
147
|
+
"$not"?: JsonValue;
|
|
148
|
+
"$gt"?: number | string;
|
|
149
|
+
"$gte"?: number | string;
|
|
150
|
+
"$lt"?: number | string;
|
|
151
|
+
"$lte"?: number | string;
|
|
152
|
+
"$in"?: JsonValue[];
|
|
153
|
+
"$notIn"?: JsonValue[];
|
|
154
|
+
"$startsWith"?: string;
|
|
155
|
+
"$includes"?: string;
|
|
156
|
+
"$contains"?: JsonValue;
|
|
157
|
+
"$exists"?: boolean;
|
|
158
|
+
"$type"?: JsonType;
|
|
159
|
+
};
|
|
160
|
+
type JsonPathWhere = {
|
|
161
|
+
[path: string]: JsonCondition;
|
|
162
|
+
};
|
|
163
|
+
type CompareRefValue<T> = T | Only<"$not", T>;
|
|
164
|
+
type CompareRefListValue<T> = Only<"$every", T> | Only<"$some", T> | Only<"$none", T>;
|
|
165
|
+
type VectorSearch = {
|
|
166
|
+
vector: number[];
|
|
167
|
+
k?: number;
|
|
168
|
+
threshold?: number;
|
|
169
|
+
};
|
|
170
|
+
type FullTextSearch = string | {
|
|
171
|
+
query: string;
|
|
172
|
+
limit?: number;
|
|
173
|
+
};
|
|
174
|
+
type CustomSearch = Record<string, any>;
|
|
175
|
+
type CustomSearchValue<P> = Only<"$near", P> | Only<"$search", P>;
|
|
176
|
+
type AggregateResult<
|
|
177
|
+
TModel,
|
|
178
|
+
T
|
|
179
|
+
> = (T extends {
|
|
180
|
+
$count: true;
|
|
181
|
+
} ? {
|
|
182
|
+
count: number;
|
|
183
|
+
} : {}) & (T extends {
|
|
184
|
+
$sum: string;
|
|
185
|
+
} ? {
|
|
186
|
+
sum: number | null;
|
|
187
|
+
} : {}) & (T extends {
|
|
188
|
+
$avg: string;
|
|
189
|
+
} ? {
|
|
190
|
+
avg: number | null;
|
|
191
|
+
} : {}) & (T extends {
|
|
192
|
+
$min: infer F;
|
|
193
|
+
} ? {
|
|
194
|
+
min: (F extends keyof TModel ? TModel[F] : never) | null;
|
|
195
|
+
} : {}) & (T extends {
|
|
196
|
+
$max: infer F;
|
|
197
|
+
} ? {
|
|
198
|
+
max: (F extends keyof TModel ? TModel[F] : never) | null;
|
|
199
|
+
} : {});
|
|
200
|
+
declare const __op: unique symbol;
|
|
201
|
+
type Op<T> = PromiseLike<T> & {
|
|
202
|
+
readonly [__op]: T;
|
|
203
|
+
};
|
|
204
|
+
type MarciOp = {
|
|
205
|
+
model: string;
|
|
206
|
+
action: string;
|
|
207
|
+
query?: any;
|
|
208
|
+
data?: any;
|
|
209
|
+
id?: any;
|
|
210
|
+
};
|
|
211
|
+
type MarciTransport = {
|
|
212
|
+
exec(op: MarciOp): Promise<any>;
|
|
213
|
+
batch(ops: MarciOp[]): Promise<any[]>;
|
|
214
|
+
};
|
|
215
|
+
type ModelTypes = {
|
|
216
|
+
name: string;
|
|
217
|
+
model: any;
|
|
218
|
+
id: Record<string, any>;
|
|
219
|
+
/** What an empty select returns: id + every scalar field, as a `{ field: true }` shape. */
|
|
220
|
+
scalars: Record<string, true>;
|
|
221
|
+
select: Record<string, any>;
|
|
222
|
+
query: Record<string, any>;
|
|
223
|
+
where: any;
|
|
224
|
+
order: Record<string, any>;
|
|
225
|
+
insert: any;
|
|
226
|
+
update: any;
|
|
227
|
+
aggregate: Record<string, any>;
|
|
228
|
+
reindex: boolean;
|
|
229
|
+
};
|
|
230
|
+
declare const __sub: unique symbol;
|
|
231
|
+
declare const __sel: unique symbol;
|
|
232
|
+
type Sub<
|
|
233
|
+
Name extends string,
|
|
234
|
+
Shape = any
|
|
235
|
+
> = {
|
|
236
|
+
readonly [__sub]: Name;
|
|
237
|
+
readonly [__sel]: Shape;
|
|
238
|
+
};
|
|
239
|
+
type Effective<
|
|
240
|
+
T extends ModelTypes,
|
|
241
|
+
S
|
|
242
|
+
> = [Exclude<keyof S, ServiceKeys>] extends [never] ? T["scalars"] : S;
|
|
243
|
+
type Resolve<S> = S extends {
|
|
244
|
+
readonly [__sel]: infer X;
|
|
245
|
+
} ? Resolve<X> : S extends object ? { [K in keyof S] : K extends `$${string}` ? S[K] : Resolve<S[K]> } : S;
|
|
246
|
+
type Rows<
|
|
247
|
+
T extends ModelTypes,
|
|
248
|
+
Sel
|
|
249
|
+
> = GetResult<T["model"], Resolve<Effective<T, Sel>>>;
|
|
250
|
+
type BareId<I> = I extends {
|
|
251
|
+
id: infer V;
|
|
252
|
+
} ? V : never;
|
|
253
|
+
type IdArg<I> = I | BareId<I>;
|
|
254
|
+
/**
|
|
255
|
+
* A lazy, immutable query over one model — `db.user` itself is one. Every clause returns a new query, so
|
|
256
|
+
* queries compose (`const active = db.user.where({ active: true })`). `await` runs it as `findMany`; passing
|
|
257
|
+
* it to `$transaction` batches it; placing it in another query's select makes it a sub-select. `Sel` is the
|
|
258
|
+
* projection: id + scalars until `select(...)` sets a shape.
|
|
259
|
+
*/
|
|
260
|
+
interface Query<
|
|
261
|
+
T extends ModelTypes,
|
|
262
|
+
Sel = T["scalars"]
|
|
263
|
+
> extends PromiseLike<Rows<T, Sel>[]> {
|
|
264
|
+
readonly [__op]: Rows<T, Sel>[];
|
|
265
|
+
readonly [__sub]: T["name"];
|
|
266
|
+
readonly [__sel]: Effective<T, Sel>;
|
|
267
|
+
/** Filter (marcidb `$where`). Repeated calls are ANDed. */
|
|
268
|
+
where(where: T["where"]): Query<T, Sel>;
|
|
269
|
+
/** Sort by one field: `order("age", "desc")` or `order({ age: "desc" })`. */
|
|
270
|
+
order(field: keyof T["order"] & string, direction?: "asc" | "desc"): Query<T, Sel>;
|
|
271
|
+
order(order: T["order"]): Query<T, Sel>;
|
|
272
|
+
limit(n: number): Query<T, Sel>;
|
|
273
|
+
skip(n: number): Query<T, Sel>;
|
|
274
|
+
/** Keyset cursor: rows strictly after this id in the current order (`$cursor`). */
|
|
275
|
+
after(id: IdArg<T["id"]>): Query<T, Sel>;
|
|
276
|
+
/**
|
|
277
|
+
* The projection. Values are `true`, a nested shape, or a sub-query of the relation's model
|
|
278
|
+
* (`posts: db.post.limit(5)`, `posts: db.post.where({ published: true }).count()`). No argument, or no
|
|
279
|
+
* field keys, selects id + every scalar. `$`-clauses are accepted here too (the object form).
|
|
280
|
+
*/
|
|
281
|
+
select<S extends T["query"] = T["scalars"]>(shape?: S): Query<T, S>;
|
|
282
|
+
/** The first matching row or `null`. */
|
|
283
|
+
first(): Op<Rows<T, Sel> | null>;
|
|
284
|
+
/** Row count; inside a select it becomes `{ count }` for the relation. */
|
|
285
|
+
count(): Op<number> & Sub<T["name"], {
|
|
286
|
+
$count: true;
|
|
287
|
+
}>;
|
|
288
|
+
/** @deprecated pass the filter through the chain: `db.user.where(w).count()` (removed in the next minor). */
|
|
289
|
+
count(query: {
|
|
290
|
+
$where?: T["where"];
|
|
291
|
+
}): Op<number> & Sub<T["name"], {
|
|
292
|
+
$count: true;
|
|
293
|
+
}>;
|
|
294
|
+
aggregate<A extends T["aggregate"]>(query: A): Op<AggregateResult<T["model"], A>> & Sub<T["name"], NoInfer<A>>;
|
|
295
|
+
insert(data: T["insert"]): Op<T["id"]>;
|
|
296
|
+
update(id: IdArg<T["id"]>, data: T["update"]): Op<void>;
|
|
297
|
+
/** Applies `data` to every row the chain's `where` matches (all rows without one); resolves to the number of rows. */
|
|
298
|
+
updateMany(data: T["update"]): Op<number>;
|
|
299
|
+
/** @deprecated pass the filter through the chain: `db.user.where(w).updateMany(data)` (removed in the next minor). */
|
|
300
|
+
updateMany(query: {
|
|
301
|
+
$where?: T["where"];
|
|
302
|
+
}, data: T["update"]): Op<number>;
|
|
303
|
+
delete(id: IdArg<T["id"]>): Op<void>;
|
|
304
|
+
/**
|
|
305
|
+
* Deletes every row the chain's `where` matches (cascades apply, as for `delete`); resolves to the number
|
|
306
|
+
* deleted. Refuses to run without a `where` — write `.where({})` to mean "every row".
|
|
307
|
+
*/
|
|
308
|
+
deleteMany(): Op<number>;
|
|
309
|
+
/** @deprecated use `.select(query)` — it takes the same object, merged with the chain (removed in the next minor). */
|
|
310
|
+
findMany<Q extends T["query"] = {}>(query?: Q): Op<Rows<T, Q>[]>;
|
|
311
|
+
/** @deprecated use `.select(query).first()` (removed in the next minor). */
|
|
312
|
+
findFirst<Q extends T["query"] = {}>(query?: Q): Op<Rows<T, Q> | null>;
|
|
313
|
+
}
|
|
314
|
+
/** `db.<model>`: the root query, plus `reindex()` for models with a `@custom` (vector / full-text) index. */
|
|
315
|
+
type Collection<T extends ModelTypes> = Query<T> & (T["reindex"] extends true ? {
|
|
316
|
+
reindex(): Op<{
|
|
317
|
+
ok: boolean;
|
|
318
|
+
indexed: number;
|
|
319
|
+
}>;
|
|
320
|
+
} : {});
|
|
321
|
+
/** One field of a model, in slot order: `n` name, `k` key | body | one | many, `m` the relation's target model. */
|
|
322
|
+
type FieldDesc = {
|
|
323
|
+
n: string;
|
|
324
|
+
k: "key" | "body" | "one" | "many";
|
|
325
|
+
t?: string | null;
|
|
326
|
+
m?: string;
|
|
327
|
+
};
|
|
328
|
+
/** Model name → its field descriptors (structs included, as relation targets). */
|
|
329
|
+
type ModelsMeta = Record<string, readonly FieldDesc[]>;
|
|
330
|
+
type QueryLayerOptions = {
|
|
331
|
+
models: ModelsMeta;
|
|
332
|
+
/** Runs one operation (a query, or a write) and resolves its result. */
|
|
333
|
+
run(op: MarciOp): Promise<any>;
|
|
334
|
+
/** Hook for `insert` payloads (the lecodes SDK generates uuid ids here). */
|
|
335
|
+
prepareInsert?(model: string, data: any): any;
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* The query builder over a set of models and a `run` function. Returns `op(descriptor)` — a lazy operation
|
|
339
|
+
* (`await` runs it, `$transaction` takes its `__op`) — and `collection(model)`, the immutable `db.<model>`
|
|
340
|
+
* root query. Transport-agnostic: the generated client and the embedded/HTTP transports supply `run`.
|
|
341
|
+
*/
|
|
342
|
+
declare function createQueryLayer(options: QueryLayerOptions): {
|
|
343
|
+
op: (descriptor: MarciOp) => any;
|
|
344
|
+
collection: (model: string) => any;
|
|
345
|
+
};
|
|
1
346
|
declare function request(method: string, url: string, body?: any): Promise<any>;
|
|
2
347
|
/**
|
|
3
348
|
* A binary-eligible read over HTTP. Advertises `Accept: <binary>, json` plus the client's schema
|
|
@@ -10,12 +355,6 @@ declare function request(method: string, url: string, body?: any): Promise<any>;
|
|
|
10
355
|
declare function requestBinary(url: string, body: any, schemaHash: string): Promise<Uint8Array | {
|
|
11
356
|
json: any;
|
|
12
357
|
}>;
|
|
13
|
-
type FieldDesc = {
|
|
14
|
-
n: string;
|
|
15
|
-
k: "key" | "body" | "one" | "many";
|
|
16
|
-
t?: string | null;
|
|
17
|
-
m?: string;
|
|
18
|
-
};
|
|
19
358
|
/**
|
|
20
359
|
* Builds the binary decoder registry for a set of models. `MODELS` maps each model name to its ordered
|
|
21
360
|
* field descriptors (emitted by codegen). Returns `getDecoder` (shape → cached decoder, or `null` when the
|
|
@@ -26,4 +365,4 @@ declare function createDecoderRegistry(MODELS: Record<string, FieldDesc[]>): {
|
|
|
26
365
|
decodeBuffer(decoder: (reader: any) => any, bytes: Uint8Array, many: boolean): any;
|
|
27
366
|
};
|
|
28
367
|
declare function encodeId(id: any): string;
|
|
29
|
-
export { requestBinary, request, encodeId, createDecoderRegistry };
|
|
368
|
+
export { requestBinary, request, encodeId, createQueryLayer, createDecoderRegistry, WhereValue, VectorSearch, ValueOps, UpdateNumValue, Sub, StrOps, ServiceKeys, Rows, Resolve, RefUpdateStruct, RefUpdate, RefListUpdateStruct, RefListUpdateOrdered, RefListUpdate, QueryLayerOptions, Query, PrimitiveListUpdate, Op, Only, NumOps, Never, ModelsMeta, ModelTypes, MarciTransport, MarciOp, JsonValue, JsonType, JsonPathWhere, JsonCondition, IdArg, GetResult, FullTextSearch, FieldOps, FieldDesc, Effective, CustomSearchValue, CustomSearch, CompareValue, CompareStrValue, CompareRefValue, CompareRefListValue, CompareNumValue, Collection, BareId, AggregateResult, AggregateKeys };
|
package/runtime/index.js
CHANGED
|
@@ -1,3 +1,119 @@
|
|
|
1
|
+
// src/query.ts
|
|
2
|
+
var AGGREGATE_KEYS = ["$count", "$sum", "$avg", "$min", "$max"];
|
|
3
|
+
var and = (a, b) => a ? b ? { $and: [a, b] } : a : b;
|
|
4
|
+
function createQueryLayer(options) {
|
|
5
|
+
const { models, run } = options;
|
|
6
|
+
const op = (descriptor) => ({
|
|
7
|
+
__op: descriptor,
|
|
8
|
+
then: (onFulfilled, onRejected) => run(descriptor).then(onFulfilled, onRejected),
|
|
9
|
+
catch: (onRejected) => run(descriptor).catch(onRejected),
|
|
10
|
+
finally: (onFinally) => run(descriptor).finally(onFinally)
|
|
11
|
+
});
|
|
12
|
+
const scalarSelect = (model) => {
|
|
13
|
+
const out = {};
|
|
14
|
+
for (const f of models[model] ?? [])
|
|
15
|
+
if (f.k === "key" || f.k === "body")
|
|
16
|
+
out[f.n] = true;
|
|
17
|
+
return out;
|
|
18
|
+
};
|
|
19
|
+
const keyField = (model) => (models[model] ?? []).find((f) => f.k === "key")?.n ?? "id";
|
|
20
|
+
const idObject = (model, id) => id !== null && typeof id === "object" ? id : { [keyField(model)]: id };
|
|
21
|
+
const resolveShape = (model, shape) => {
|
|
22
|
+
const out = {};
|
|
23
|
+
let fields = 0;
|
|
24
|
+
for (const k in shape) {
|
|
25
|
+
const v = shape[k];
|
|
26
|
+
if (k.charCodeAt(0) === 36) {
|
|
27
|
+
out[k] = v;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (v !== undefined && v !== false)
|
|
31
|
+
fields++;
|
|
32
|
+
if (v !== null && typeof v === "object") {
|
|
33
|
+
if ("__select" in v) {
|
|
34
|
+
out[k] = v.__select;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const desc = (models[model] ?? []).find((f) => f.n === k);
|
|
38
|
+
out[k] = desc && desc.m && !AGGREGATE_KEYS.some((a) => (a in v)) ? resolveShape(desc.m, v) : v;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
out[k] = v;
|
|
42
|
+
}
|
|
43
|
+
if (fields === 0)
|
|
44
|
+
Object.assign(out, scalarSelect(model));
|
|
45
|
+
return out;
|
|
46
|
+
};
|
|
47
|
+
const collection = (model) => {
|
|
48
|
+
const make = (st) => {
|
|
49
|
+
const build = (query) => {
|
|
50
|
+
const q = resolveShape(model, { ...st.shape ?? {}, ...query ?? {} });
|
|
51
|
+
const where = and(st.where, q.$where);
|
|
52
|
+
if (where)
|
|
53
|
+
q.$where = where;
|
|
54
|
+
else
|
|
55
|
+
delete q.$where;
|
|
56
|
+
if (st.order && !q.$order)
|
|
57
|
+
q.$order = st.order;
|
|
58
|
+
if (st.limit !== undefined && q.$limit === undefined)
|
|
59
|
+
q.$limit = st.limit;
|
|
60
|
+
if (st.skip !== undefined && q.$skip === undefined)
|
|
61
|
+
q.$skip = st.skip;
|
|
62
|
+
if (st.cursor !== undefined && q.$cursor === undefined)
|
|
63
|
+
q.$cursor = st.cursor;
|
|
64
|
+
return q;
|
|
65
|
+
};
|
|
66
|
+
const whereOnly = (query) => {
|
|
67
|
+
const w = and(st.where, query && query.$where);
|
|
68
|
+
return w ? { $where: w } : {};
|
|
69
|
+
};
|
|
70
|
+
const findMany = () => ({ model, action: "findMany", query: build() });
|
|
71
|
+
return {
|
|
72
|
+
get __op() {
|
|
73
|
+
return findMany();
|
|
74
|
+
},
|
|
75
|
+
get __select() {
|
|
76
|
+
return build();
|
|
77
|
+
},
|
|
78
|
+
then: (onFulfilled, onRejected) => run(findMany()).then(onFulfilled, onRejected),
|
|
79
|
+
catch: (onRejected) => run(findMany()).catch(onRejected),
|
|
80
|
+
finally: (onFinally) => run(findMany()).finally(onFinally),
|
|
81
|
+
where: (where) => make({ ...st, where: and(st.where, where) }),
|
|
82
|
+
order: (field, direction) => make({ ...st, order: typeof field === "string" ? { [field]: direction ?? "asc" } : field }),
|
|
83
|
+
limit: (n) => make({ ...st, limit: n }),
|
|
84
|
+
skip: (n) => make({ ...st, skip: n }),
|
|
85
|
+
after: (id) => make({ ...st, cursor: idObject(model, id) }),
|
|
86
|
+
select: (shape) => make({ ...st, shape: shape ?? {} }),
|
|
87
|
+
first: () => op({ model, action: "findFirst", query: build() }),
|
|
88
|
+
count: (query) => {
|
|
89
|
+
const q = whereOnly(query);
|
|
90
|
+
return Object.assign(op({ model, action: "count", query: q }), { __select: { $count: true, ...q } });
|
|
91
|
+
},
|
|
92
|
+
aggregate: (query) => {
|
|
93
|
+
const q = { ...query ?? {}, ...whereOnly(query) };
|
|
94
|
+
if (!q.$where)
|
|
95
|
+
delete q.$where;
|
|
96
|
+
return Object.assign(op({ model, action: "aggregate", query: q }), { __select: q });
|
|
97
|
+
},
|
|
98
|
+
insert: (data) => op({ model, action: "insert", data: options.prepareInsert ? options.prepareInsert(model, data) : data }),
|
|
99
|
+
update: (id, data) => op({ model, action: "update", id: idObject(model, id), data }),
|
|
100
|
+
updateMany: (a, b) => b === undefined ? op({ model, action: "updateMany", query: whereOnly(), data: a }) : op({ model, action: "updateMany", query: whereOnly(a), data: b }),
|
|
101
|
+
delete: (id) => op({ model, action: "delete", id: idObject(model, id) }),
|
|
102
|
+
deleteMany: () => {
|
|
103
|
+
if (st.where === undefined)
|
|
104
|
+
throw new Error(`marcidb: ${model}.deleteMany() needs a where — use .where({}) to delete every row`);
|
|
105
|
+
return op({ model, action: "deleteMany", query: whereOnly() });
|
|
106
|
+
},
|
|
107
|
+
reindex: () => op({ model, action: "$reindex" }),
|
|
108
|
+
findMany: (query) => op({ model, action: "findMany", query: build(query) }),
|
|
109
|
+
findFirst: (query) => op({ model, action: "findFirst", query: build(query) })
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
return make({});
|
|
113
|
+
};
|
|
114
|
+
return { op, collection };
|
|
115
|
+
}
|
|
116
|
+
|
|
1
117
|
// src/index.ts
|
|
2
118
|
async function request(method, url, body) {
|
|
3
119
|
const res = await fetch(url, {
|
|
@@ -230,5 +346,6 @@ export {
|
|
|
230
346
|
requestBinary,
|
|
231
347
|
request,
|
|
232
348
|
encodeId,
|
|
349
|
+
createQueryLayer,
|
|
233
350
|
createDecoderRegistry
|
|
234
351
|
};
|