marcidb-client 0.12.0 → 0.14.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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "marcidb-client",
3
3
  "type": "module",
4
- "version": "0.12.0",
4
+ "version": "0.14.0",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "den59k"
@@ -1,3 +1,391 @@
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
+ journal?: JournalArgs;
211
+ };
212
+ /** The arguments of a journal action (`$journalOpen` / `$journalRead` / `$journalDrop`). `wait` is in seconds. */
213
+ type JournalArgs = {
214
+ name: string;
215
+ on?: readonly JournalOp[];
216
+ after?: number;
217
+ limit?: number;
218
+ wait?: number;
219
+ };
220
+ type MarciTransport = {
221
+ exec(op: MarciOp): Promise<any>;
222
+ batch(ops: MarciOp[]): Promise<any[]>;
223
+ };
224
+ type ModelTypes = {
225
+ name: string;
226
+ model: any;
227
+ id: Record<string, any>;
228
+ /** What an empty select returns: id + every scalar field, as a `{ field: true }` shape. */
229
+ scalars: Record<string, true>;
230
+ select: Record<string, any>;
231
+ query: Record<string, any>;
232
+ where: any;
233
+ order: Record<string, any>;
234
+ insert: any;
235
+ update: any;
236
+ aggregate: Record<string, any>;
237
+ reindex: boolean;
238
+ };
239
+ declare const __sub: unique symbol;
240
+ declare const __sel: unique symbol;
241
+ type Sub<
242
+ Name extends string,
243
+ Shape = any
244
+ > = {
245
+ readonly [__sub]: Name;
246
+ readonly [__sel]: Shape;
247
+ };
248
+ type Effective<
249
+ T extends ModelTypes,
250
+ S
251
+ > = [Exclude<keyof S, ServiceKeys>] extends [never] ? T["scalars"] : S;
252
+ type Resolve<S> = S extends {
253
+ readonly [__sel]: infer X;
254
+ } ? Resolve<X> : S extends object ? { [K in keyof S] : K extends `$${string}` ? S[K] : Resolve<S[K]> } : S;
255
+ type Rows<
256
+ T extends ModelTypes,
257
+ Sel
258
+ > = GetResult<T["model"], Resolve<Effective<T, Sel>>>;
259
+ type BareId<I> = I extends {
260
+ id: infer V;
261
+ } ? V : never;
262
+ type IdArg<I> = I | BareId<I>;
263
+ /**
264
+ * A lazy, immutable query over one model — `db.user` itself is one. Every clause returns a new query, so
265
+ * queries compose (`const active = db.user.where({ active: true })`). `await` runs it as `findMany`; passing
266
+ * it to `$transaction` batches it; placing it in another query's select makes it a sub-select. `Sel` is the
267
+ * projection: id + scalars until `select(...)` sets a shape.
268
+ */
269
+ interface Query<
270
+ T extends ModelTypes,
271
+ Sel = T["scalars"]
272
+ > extends PromiseLike<Rows<T, Sel>[]> {
273
+ readonly [__op]: Rows<T, Sel>[];
274
+ readonly [__sub]: T["name"];
275
+ readonly [__sel]: Effective<T, Sel>;
276
+ /** Filter (marcidb `$where`). Repeated calls are ANDed. */
277
+ where(where: T["where"]): Query<T, Sel>;
278
+ /** Sort by one field: `order("age", "desc")` or `order({ age: "desc" })`. */
279
+ order(field: keyof T["order"] & string, direction?: "asc" | "desc"): Query<T, Sel>;
280
+ order(order: T["order"]): Query<T, Sel>;
281
+ limit(n: number): Query<T, Sel>;
282
+ skip(n: number): Query<T, Sel>;
283
+ /** Keyset cursor: rows strictly after this id in the current order (`$cursor`). */
284
+ after(id: IdArg<T["id"]>): Query<T, Sel>;
285
+ /**
286
+ * The projection. Values are `true`, a nested shape, or a sub-query of the relation's model
287
+ * (`posts: db.post.limit(5)`, `posts: db.post.where({ published: true }).count()`). No argument, or no
288
+ * field keys, selects id + every scalar. `$`-clauses are accepted here too (the object form).
289
+ */
290
+ select<S extends T["query"] = T["scalars"]>(shape?: S): Query<T, S>;
291
+ /** The first matching row or `null`. */
292
+ first(): Op<Rows<T, Sel> | null>;
293
+ /** Row count; inside a select it becomes `{ count }` for the relation. */
294
+ count(): Op<number> & Sub<T["name"], {
295
+ $count: true;
296
+ }>;
297
+ /** @deprecated pass the filter through the chain: `db.user.where(w).count()` (removed in the next minor). */
298
+ count(query: {
299
+ $where?: T["where"];
300
+ }): Op<number> & Sub<T["name"], {
301
+ $count: true;
302
+ }>;
303
+ aggregate<A extends T["aggregate"]>(query: A): Op<AggregateResult<T["model"], A>> & Sub<T["name"], NoInfer<A>>;
304
+ insert(data: T["insert"]): Op<T["id"]>;
305
+ update(id: IdArg<T["id"]>, data: T["update"]): Op<void>;
306
+ /** Applies `data` to every row the chain's `where` matches (all rows without one); resolves to the number of rows. */
307
+ updateMany(data: T["update"]): Op<number>;
308
+ /** @deprecated pass the filter through the chain: `db.user.where(w).updateMany(data)` (removed in the next minor). */
309
+ updateMany(query: {
310
+ $where?: T["where"];
311
+ }, data: T["update"]): Op<number>;
312
+ delete(id: IdArg<T["id"]>): Op<void>;
313
+ /**
314
+ * Deletes every row the chain's `where` matches (cascades apply, as for `delete`); resolves to the number
315
+ * deleted. Refuses to run without a `where` — write `.where({})` to mean "every row".
316
+ */
317
+ deleteMany(): Op<number>;
318
+ /** @deprecated use `.select(query)` — it takes the same object, merged with the chain (removed in the next minor). */
319
+ findMany<Q extends T["query"] = {}>(query?: Q): Op<Rows<T, Q>[]>;
320
+ /** @deprecated use `.select(query).first()` (removed in the next minor). */
321
+ findFirst<Q extends T["query"] = {}>(query?: Q): Op<Rows<T, Q> | null>;
322
+ }
323
+ /** What a journal can record. Only deletes so far. */
324
+ type JournalOp = "delete";
325
+ /** One recorded change. `row` is the row as it last was: its id and every scalar field. */
326
+ type JournalEntry<
327
+ T extends ModelTypes,
328
+ O extends JournalOp = JournalOp
329
+ > = {
330
+ seq: number;
331
+ op: O;
332
+ row: Rows<T, T["scalars"]>;
333
+ };
334
+ type JournalOptions<O extends JournalOp> = {
335
+ /** The operations to record. A journal that exists with other ones is an error, not a redefinition. */
336
+ on: O | readonly O[];
337
+ /** `false`: the loop ends when the journal is read through. By default it waits for the next entry. */
338
+ wait?: boolean;
339
+ };
340
+ /**
341
+ * A named, durable log of a model's changes. It starts recording when it is first asked for and keeps
342
+ * every entry until the loop that reads it has moved past it — an entry whose loop body threw, or that
343
+ * was never reached, is delivered again to the next reader of the same name.
344
+ */
345
+ interface Journal<
346
+ T extends ModelTypes,
347
+ O extends JournalOp = JournalOp
348
+ > extends AsyncIterable<JournalEntry<T, O>> {
349
+ /** Drops the journal with whatever it still holds; the model's writes stop paying for it. */
350
+ drop(): Promise<void>;
351
+ }
352
+ /**
353
+ * `db.<model>`: the root query, plus `reindex()` for models with a `@custom` (vector / full-text) index and
354
+ * `$journal(name, { on })` — the journal of this model's changes under that name, created on first use.
355
+ */
356
+ type Collection<T extends ModelTypes> = Query<T> & (T["reindex"] extends true ? {
357
+ reindex(): Op<{
358
+ ok: boolean;
359
+ indexed: number;
360
+ }>;
361
+ } : {}) & {
362
+ $journal<O extends JournalOp>(name: string, options: JournalOptions<O>): Journal<T, O>;
363
+ };
364
+ /** One field of a model, in slot order: `n` name, `k` key | body | one | many, `m` the relation's target model. */
365
+ type FieldDesc = {
366
+ n: string;
367
+ k: "key" | "body" | "one" | "many";
368
+ t?: string | null;
369
+ m?: string;
370
+ };
371
+ /** Model name → its field descriptors (structs included, as relation targets). */
372
+ type ModelsMeta = Record<string, readonly FieldDesc[]>;
373
+ type QueryLayerOptions = {
374
+ models: ModelsMeta;
375
+ /** Runs one operation (a query, or a write) and resolves its result. */
376
+ run(op: MarciOp): Promise<any>;
377
+ /** Hook for `insert` payloads (the lecodes SDK generates uuid ids here). */
378
+ prepareInsert?(model: string, data: any): any;
379
+ };
380
+ /**
381
+ * The query builder over a set of models and a `run` function. Returns `op(descriptor)` — a lazy operation
382
+ * (`await` runs it, `$transaction` takes its `__op`) — and `collection(model)`, the immutable `db.<model>`
383
+ * root query. Transport-agnostic: the generated client and the embedded/HTTP transports supply `run`.
384
+ */
385
+ declare function createQueryLayer(options: QueryLayerOptions): {
386
+ op: (descriptor: MarciOp) => any;
387
+ collection: (model: string) => any;
388
+ };
1
389
  declare function request(method: string, url: string, body?: any): Promise<any>;
2
390
  /**
3
391
  * A binary-eligible read over HTTP. Advertises `Accept: <binary>, json` plus the client's schema
@@ -10,12 +398,6 @@ declare function request(method: string, url: string, body?: any): Promise<any>;
10
398
  declare function requestBinary(url: string, body: any, schemaHash: string): Promise<Uint8Array | {
11
399
  json: any;
12
400
  }>;
13
- type FieldDesc = {
14
- n: string;
15
- k: "key" | "body" | "one" | "many";
16
- t?: string | null;
17
- m?: string;
18
- };
19
401
  /**
20
402
  * Builds the binary decoder registry for a set of models. `MODELS` maps each model name to its ordered
21
403
  * field descriptors (emitted by codegen). Returns `getDecoder` (shape → cached decoder, or `null` when the
@@ -26,4 +408,4 @@ declare function createDecoderRegistry(MODELS: Record<string, FieldDesc[]>): {
26
408
  decodeBuffer(decoder: (reader: any) => any, bytes: Uint8Array, many: boolean): any;
27
409
  };
28
410
  declare function encodeId(id: any): string;
29
- export { requestBinary, request, encodeId, createDecoderRegistry };
411
+ export { AggregateKeys, AggregateResult, BareId, Collection, CompareNumValue, CompareRefListValue, CompareRefValue, CompareStrValue, CompareValue, CustomSearch, CustomSearchValue, Effective, FieldDesc, FieldOps, FullTextSearch, GetResult, IdArg, Journal, JournalArgs, JournalEntry, JournalOp, JournalOptions, JsonCondition, JsonPathWhere, JsonType, JsonValue, MarciOp, MarciTransport, ModelTypes, ModelsMeta, Never, NumOps, Only, Op, PrimitiveListUpdate, Query, QueryLayerOptions, RefListUpdate, RefListUpdateOrdered, RefListUpdateStruct, RefUpdate, RefUpdateStruct, Resolve, Rows, ServiceKeys, StrOps, Sub, UpdateNumValue, ValueOps, VectorSearch, WhereValue, createDecoderRegistry, createQueryLayer, encodeId, request, requestBinary };
package/runtime/index.js CHANGED
@@ -1,3 +1,153 @@
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 journal = (model, name, options) => {
48
+ const on = Array.isArray(options.on) ? options.on : [options.on];
49
+ const wait = options.wait === false ? 0 : 30;
50
+ const opened = run({ model, action: "$journalOpen", journal: { name, on } });
51
+ opened.catch(() => {});
52
+ const read = (after, limit, wait) => run({ model, action: "$journalRead", journal: { name, after, limit, wait } });
53
+ return {
54
+ async* [Symbol.asyncIterator]() {
55
+ await opened;
56
+ let done;
57
+ let confirmed;
58
+ try {
59
+ for (;; ) {
60
+ const entries = await read(done, 100, wait);
61
+ confirmed = done;
62
+ if (entries.length === 0) {
63
+ if (wait === 0)
64
+ return;
65
+ continue;
66
+ }
67
+ for (const entry of entries) {
68
+ yield entry;
69
+ done = entry.seq;
70
+ }
71
+ }
72
+ } finally {
73
+ if (done !== undefined && done !== confirmed)
74
+ await read(done, 1, 0).catch(() => {});
75
+ }
76
+ },
77
+ drop: () => opened.catch(() => {}).then(() => run({ model, action: "$journalDrop", journal: { name } })).then(() => {})
78
+ };
79
+ };
80
+ const collection = (model) => {
81
+ const make = (st) => {
82
+ const build = (query) => {
83
+ const q = resolveShape(model, { ...st.shape ?? {}, ...query ?? {} });
84
+ const where = and(st.where, q.$where);
85
+ if (where)
86
+ q.$where = where;
87
+ else
88
+ delete q.$where;
89
+ if (st.order && !q.$order)
90
+ q.$order = st.order;
91
+ if (st.limit !== undefined && q.$limit === undefined)
92
+ q.$limit = st.limit;
93
+ if (st.skip !== undefined && q.$skip === undefined)
94
+ q.$skip = st.skip;
95
+ if (st.cursor !== undefined && q.$cursor === undefined)
96
+ q.$cursor = st.cursor;
97
+ return q;
98
+ };
99
+ const whereOnly = (query) => {
100
+ const w = and(st.where, query && query.$where);
101
+ return w ? { $where: w } : {};
102
+ };
103
+ const findMany = () => ({ model, action: "findMany", query: build() });
104
+ return {
105
+ get __op() {
106
+ return findMany();
107
+ },
108
+ get __select() {
109
+ return build();
110
+ },
111
+ then: (onFulfilled, onRejected) => run(findMany()).then(onFulfilled, onRejected),
112
+ catch: (onRejected) => run(findMany()).catch(onRejected),
113
+ finally: (onFinally) => run(findMany()).finally(onFinally),
114
+ where: (where) => make({ ...st, where: and(st.where, where) }),
115
+ order: (field, direction) => make({ ...st, order: typeof field === "string" ? { [field]: direction ?? "asc" } : field }),
116
+ limit: (n) => make({ ...st, limit: n }),
117
+ skip: (n) => make({ ...st, skip: n }),
118
+ after: (id) => make({ ...st, cursor: idObject(model, id) }),
119
+ select: (shape) => make({ ...st, shape: shape ?? {} }),
120
+ first: () => op({ model, action: "findFirst", query: build() }),
121
+ count: (query) => {
122
+ const q = whereOnly(query);
123
+ return Object.assign(op({ model, action: "count", query: q }), { __select: { $count: true, ...q } });
124
+ },
125
+ aggregate: (query) => {
126
+ const q = { ...query ?? {}, ...whereOnly(query) };
127
+ if (!q.$where)
128
+ delete q.$where;
129
+ return Object.assign(op({ model, action: "aggregate", query: q }), { __select: q });
130
+ },
131
+ insert: (data) => op({ model, action: "insert", data: options.prepareInsert ? options.prepareInsert(model, data) : data }),
132
+ update: (id, data) => op({ model, action: "update", id: idObject(model, id), data }),
133
+ updateMany: (a, b) => b === undefined ? op({ model, action: "updateMany", query: whereOnly(), data: a }) : op({ model, action: "updateMany", query: whereOnly(a), data: b }),
134
+ delete: (id) => op({ model, action: "delete", id: idObject(model, id) }),
135
+ deleteMany: () => {
136
+ if (st.where === undefined)
137
+ throw new Error(`marcidb: ${model}.deleteMany() needs a where — use .where({}) to delete every row`);
138
+ return op({ model, action: "deleteMany", query: whereOnly() });
139
+ },
140
+ reindex: () => op({ model, action: "$reindex" }),
141
+ $journal: (name, options) => journal(model, name, options),
142
+ findMany: (query) => op({ model, action: "findMany", query: build(query) }),
143
+ findFirst: (query) => op({ model, action: "findFirst", query: build(query) })
144
+ };
145
+ };
146
+ return make({});
147
+ };
148
+ return { op, collection };
149
+ }
150
+
1
151
  // src/index.ts
2
152
  async function request(method, url, body) {
3
153
  const res = await fetch(url, {
@@ -227,8 +377,9 @@ function encodeId(id) {
227
377
  return String(id);
228
378
  }
229
379
  export {
230
- requestBinary,
231
- request,
380
+ createDecoderRegistry,
381
+ createQueryLayer,
232
382
  encodeId,
233
- createDecoderRegistry
383
+ request,
384
+ requestBinary
234
385
  };