@substrio/sdk 0.0.0-stage → 0.1.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/substrio.d.ts ADDED
@@ -0,0 +1,1087 @@
1
+ // Types of the SDK's entry point. The operation surface (client.ops, every
2
+ // input, result and the error envelope) is generated: substrio_operations.d.mts.
3
+ import type { SubstrioOperations, SubstrioErrorBody, SubstrioErrorCode } from './substrio_operations.mjs';
4
+
5
+ export * from './substrio_operations.mjs';
6
+
7
+ export interface SubstrioErrorInfo {
8
+ readonly hex: number;
9
+ readonly name: string;
10
+ readonly message: string;
11
+ readonly status: number;
12
+ }
13
+
14
+ export declare const SUBSTRIO_ERRORS: Readonly<Record<string, SubstrioErrorInfo>>;
15
+
16
+ /** A failed call: the HTTP status, the error code and the server's envelope. */
17
+ export declare class SubstrioError extends Error {
18
+ constructor(status: number, code: SubstrioErrorCode | '', message: string, body: unknown);
19
+ readonly status: number;
20
+ readonly code: SubstrioErrorCode | '';
21
+ readonly body: SubstrioErrorBody | unknown;
22
+ readonly isRetryable: boolean;
23
+ readonly isAuth: boolean;
24
+ readonly isQuota: boolean;
25
+ readonly isNotFound: boolean;
26
+ static fromResponse(status: number, parsed: unknown): SubstrioError;
27
+ }
28
+
29
+ export interface SubstrioConfig {
30
+ url: string;
31
+ key?: string;
32
+ token?: string;
33
+ tenant?: string;
34
+ }
35
+
36
+ export default class Substrio {
37
+ constructor(config: SubstrioConfig);
38
+ /** Every registry operation at the words of its name: ops.kv.get(input). */
39
+ readonly ops: SubstrioOperations;
40
+ // The HTTP surface (collections, queries, realtime, KV wire) is not typed yet.
41
+ [member: string]: any;
42
+ }
43
+
44
+ // @substrio-chain-begin (the single owner of embedded types; src/substrio/CMakeLists.txt copies this block into jsc/substrio_runtime.d.ts)
45
+ /** Synchronous embedded collection contracts, separate from the HTTP client. */
46
+ export type SubstrioDocumentId = string | number | bigint;
47
+ /** A value a filter member compares to: a string, number, boolean, Date or null (null names the documents without a value). */
48
+ export type SubstrioFilterValue = null | boolean | number | string | Date;
49
+ /** A binary operand: an ArrayBuffer or any ArrayBufferView (a Uint8Array, another typed array, a DataView), compared by the bytes it views. */
50
+ export type SubstrioBinaryValue = ArrayBuffer | ArrayBufferView;
51
+ /** {$binary: base64}: binary written as its base64 text, the bytes a document stores for it. */
52
+ export interface SubstrioBinaryWrapper { $binary: string }
53
+ /**
54
+ * What an equality compares to: a SubstrioFilterValue, or binary. A binary equals a stored binary of the same bytes
55
+ * only (never a string of those bytes), and an array holding one by any element. Only $eq, $ne, $in, $nin and
56
+ * a field's own value take it; an ordering or a text operator throws.
57
+ */
58
+ export type SubstrioFilterEquality = SubstrioFilterValue | SubstrioBinaryValue | SubstrioBinaryWrapper;
59
+ /** The operators of an element condition ($elemMatch): the ones a chain's elemMatch takes. */
60
+ export interface SubstrioElementOperators {
61
+ $eq?: SubstrioFilterEquality;
62
+ $ne?: SubstrioFilterEquality;
63
+ $gt?: number | string | Date;
64
+ $gte?: number | string | Date;
65
+ $lt?: number | string | Date;
66
+ $lte?: number | string | Date;
67
+ $in?: SubstrioFilterEquality[];
68
+ $nin?: SubstrioFilterEquality[];
69
+ $exists?: boolean;
70
+ $startsWith?: string;
71
+ $endsWith?: string;
72
+ $contains?: string;
73
+ $icontains?: string;
74
+ }
75
+ /**
76
+ * The operators of one field of a filter. $not takes a non-empty object of the
77
+ * operators before it (every document they do not select, one without the field
78
+ * included); $regex is a pattern string and $options "" or "i"; $all, $size and
79
+ * $elemMatch read an array held by a top-level field. null is the operand of $eq,
80
+ * $ne, $in and $nin only; _id takes $eq, $ne, $in and $nin over ids.
81
+ */
82
+ export interface SubstrioFieldOperators {
83
+ $eq?: SubstrioFilterEquality;
84
+ $ne?: SubstrioFilterEquality;
85
+ $gt?: number | string | Date;
86
+ $gte?: number | string | Date;
87
+ $lt?: number | string | Date;
88
+ $lte?: number | string | Date;
89
+ $in?: SubstrioFilterEquality[];
90
+ $nin?: SubstrioFilterEquality[];
91
+ $exists?: boolean;
92
+ $startsWith?: string;
93
+ $not?: SubstrioFieldOperators;
94
+ $regex?: string;
95
+ $options?: '' | 'i';
96
+ /** The array field holds every one of the values (containsAll). */
97
+ $all?: (string | number | boolean)[];
98
+ /** The array field has exactly this many elements (size). */
99
+ $size?: number;
100
+ /** An element satisfies every member: a value is an equality, an object takes SubstrioElementOperators (elemMatch). */
101
+ $elemMatch?: Record<string, SubstrioFilterValue | SubstrioElementOperators>;
102
+ }
103
+ /**
104
+ * A filter object. A member is a field (or a dotted path) holding a value (an equality) or an object of
105
+ * operators; $and, $or and $nor take non-empty arrays of filters. Every filter is a chain's plan: a filter
106
+ * the chain has no plan for throws a TypeError that says why (undefined, a BigInt, an integer from 2^53,
107
+ * a RegExp object, an unknown operator, $text, $fuzzy, $search, $knn, $sparseKnn, $near, $geoWithin, $box,
108
+ * $sphere and $whereJS). null or undefined means every document; any other non-object throws.
109
+ */
110
+ export interface SubstrioFilterObject {
111
+ $and?: SubstrioFilterObject[];
112
+ $or?: SubstrioFilterObject[];
113
+ $nor?: SubstrioFilterObject[];
114
+ [field: string]: SubstrioFilterEquality | SubstrioFieldOperators | SubstrioFilterObject[] | undefined;
115
+ }
116
+ export type SubstrioFilter = SubstrioFilterObject | null | undefined;
117
+ /** Every condition must hold on the stored row; otherwise the write returns null. {_version: v} is compare-and-set. */
118
+ export interface SubstrioWriteOptions { if?: Record<string, unknown>; }
119
+ /**
120
+ * Plain fields are set, or use operators; mixing the two forms throws.
121
+ * $inc/$mul/$min/$max require finite numbers or BigInts inside int64, and a
122
+ * nonnumeric stored field throws without changing the row. Integer overflow
123
+ * throws RangeError. $push accepts a value or {$each, $position, $slice};
124
+ * $pull accepts a value, scalar condition, or fields of an object element.
125
+ * $setWhere takes {arrayField: {where: {field: value}, set: {field: value}}}.
126
+ * A dotted key in $set, $unset, $inc, $mul, $min and $max ("address.city") writes the member of a document,
127
+ * creating the documents of a missing step; a step that is a scalar, null or an array throws and writes nothing.
128
+ * The array operators, $rename and $setWhere do not take a dotted key.
129
+ * $setWhere rebuilds each matching element (a document) with only the fields of `set` written (a dotted name in
130
+ * `set` writes a path); every other member of every element keeps its stored type and bytes.
131
+ * Values store as JSON writes them: -0 stores 0, NaN and the infinities null, a Date its ISO text, a hole of a
132
+ * sparse array null, undefined is no member of an object; the same at the top level, nested and in an array.
133
+ * An ArrayBuffer and every ArrayBufferView (a Uint8Array, any other typed array, a DataView) store as binary, the
134
+ * bytes they view in native byte order, and a stored binary value reads back as a Uint8Array (in a row, a pluck,
135
+ * a join, a group key or a distinct value); the same at the top level, nested and in an array. {$binary: base64}
136
+ * stores the same binary.
137
+ */
138
+ export type SubstrioUpdate = Record<string, unknown> & {
139
+ $set?: Record<string, unknown>;
140
+ $unset?: Record<string, unknown>;
141
+ $inc?: Record<string, number | bigint>;
142
+ $mul?: Record<string, number | bigint>;
143
+ $min?: Record<string, number | bigint>;
144
+ $max?: Record<string, number | bigint>;
145
+ $push?: Record<string, unknown>;
146
+ $addToSet?: Record<string, unknown>;
147
+ $pull?: Record<string, unknown>;
148
+ $pullAll?: Record<string, unknown[]>;
149
+ $pop?: Record<string, 1 | -1>;
150
+ $rename?: Record<string, string>;
151
+ $currentDate?: Record<string, true>;
152
+ $setWhere?: Record<string, { where: Record<string, unknown>; set: Record<string, unknown> }>;
153
+ };
154
+ export interface SubstrioFindOptions {
155
+ /** Keys in order, with numeric directions; a bare name in the array is ascending. */
156
+ sort?: Record<string, 1 | -1> | Array<string | [string, (1 | -1)?]>;
157
+ limit?: number;
158
+ skip?: number;
159
+ /** Projection answers plain objects of these fields, rather than records. */
160
+ select?: string[];
161
+ /** Naming either token (even null/undefined) requests a page; limit is required and skip must be zero. */
162
+ after?: string | null;
163
+ before?: string | null;
164
+ }
165
+ /** Find pages never carry total. Rows retain the unprojected/projected result form. */
166
+ export interface SubstrioFindPage<Rows> {
167
+ rows: Rows;
168
+ next: string | null;
169
+ prev: string | null;
170
+ }
171
+ export type SubstrioUnpagedFindOptions = Omit<SubstrioFindOptions, 'after' | 'before'> & { after?: never; before?: never };
172
+ /** Two nonempty tokens, multiple sort keys, or a filter requiring fallback are refused at runtime. */
173
+ export type SubstrioFindCursorOptions = Omit<SubstrioFindOptions, 'limit' | 'skip'> & { limit: number; skip?: 0 } &
174
+ ({ after: string | null | undefined } | { before: string | null | undefined });
175
+ /** $setOnInsert applies only when the upsert inserts; ignored on a match. */
176
+ export type SubstrioUpsertDocument<T> = Partial<T> & { $setOnInsert?: Record<string, unknown> };
177
+ export type SubstrioIdentifiedDocument<T> = SubstrioUpsertDocument<T> & { _id: SubstrioDocumentId };
178
+ export interface SubstrioRecordMethods extends SubstrioRecordLinks {
179
+ /**
180
+ * Writes changes and returns true. A detached row inserts and gains _id and
181
+ * _version. A stored row saves against its version; a concurrent change
182
+ * throws ConflictError and overwrites nothing. Reload, assign and save again.
183
+ */
184
+ save(): boolean;
185
+ has(field: string): boolean;
186
+ set(field: string, value: unknown): this;
187
+ /** Assigns own fields in memory; save() writes them. */
188
+ merge(fields: Record<string, unknown>): this;
189
+ /** Exact integer addition (BigInt beyond 2^53); TypeError for nonnumbers, RangeError beyond int64. */
190
+ inc(field: string, delta?: number | bigint): this;
191
+ keys(): string[];
192
+ /** The stored type of a field ("string", "number", "boolean", "date", "null", "object", "array"); undefined when absent. */
193
+ typeOf(field: string): string | undefined;
194
+ push(field: string, value: unknown): this;
195
+ pull(field: string, value: unknown): this;
196
+ /** Whether this row has unsaved changes. */
197
+ dirty(): boolean;
198
+ unset(field: string): this;
199
+ /** Deletes the stored document. */
200
+ delete(): boolean;
201
+ /** Re-reads the stored row and drops unsaved changes. */
202
+ reload(): this;
203
+ /** Alias of reload. */
204
+ revert(): this;
205
+ toggle(field: string): this;
206
+ /** JSON.stringify gives the stored document. */
207
+ toJSON(): Record<string, unknown>;
208
+ }
209
+ export type SubstrioRecord<T = Record<string, unknown>> = Omit<T, '_id'> & SubstrioRecordMethods & { _id: string };
210
+ /**
211
+ * Constructing copies fields without storing them. No generated id is present
212
+ * yet; an input's own _id is copied as supplied. save() returns boolean and
213
+ * does not statically narrow this object's id after the call.
214
+ */
215
+ export type SubstrioConstructedRecord<T = Record<string, unknown>, Fields extends object = {}> =
216
+ Omit<Partial<T>, '_id'> & Fields & SubstrioRecordMethods & { _id?: unknown };
217
+ /** An unprojected result. Array map/filter produce ordinary arrays. */
218
+ export interface SubstrioQueryResult<T = Record<string, unknown>> extends Omit<Array<SubstrioRecord<T>>, 'reduce'> {
219
+ sort(compare?: (a: SubstrioRecord<T>, b: SubstrioRecord<T>) => number): this;
220
+ toArray(): SubstrioRecord<T>[];
221
+ toJSON(): Record<string, unknown>[];
222
+ project(fields: string[]): Record<string, unknown>[];
223
+ first(): SubstrioRecord<T> | undefined;
224
+ last(): SubstrioRecord<T> | undefined;
225
+ ids(): string[];
226
+ pluck(field: string): unknown[];
227
+ /** Without an initial value, acc starts undefined and every row is visited; an empty result returns undefined. */
228
+ reduce<U>(fn: (acc: U | undefined, record: SubstrioRecord<T>, index: number, result: this) => U): U | undefined;
229
+ reduce<U>(fn: (acc: U, record: SubstrioRecord<T>, index: number, result: this) => U, initial: U): U;
230
+ /** Saves rows with unsaved changes; reports the first conflict while other current rows may still save. */
231
+ saveAll(): number;
232
+ /** Deletes each row's stored document; returns the number removed. */
233
+ deleteAll(): number;
234
+ setAll(field: string, value: unknown): number;
235
+ incAll(field: string, delta?: number | bigint): number;
236
+ }
237
+ /**
238
+ * Documents are live records read in place. Dates store as ISO strings,
239
+ * undefined fields are dropped and BigInts inside int64 round trip.
240
+ */
241
+ export interface SubstrioCollection<T = Record<string, unknown>> extends Omit<SubstrioChainStart<T>, 'all'>, SubstrioCollectionDeclarations {
242
+ /** Detached row: copies own fields; nothing is stored until save(). */
243
+ new <Fields extends Partial<T> & Record<string, unknown>>(fields: Fields): SubstrioConstructedRecord<T, Fields>;
244
+ new (fields?: Partial<T>): SubstrioConstructedRecord<T>;
245
+ /** Same as new. */
246
+ <Fields extends Partial<T> & Record<string, unknown>>(fields: Fields): SubstrioConstructedRecord<T, Fields>;
247
+ (fields?: Partial<T>): SubstrioConstructedRecord<T>;
248
+ all(...args: SubstrioChainArgument[]): SubstrioQueryResult<T>;
249
+ /** Zero limit exits before projection and paging, answering an empty record result. */
250
+ find(filter: SubstrioFilter, options: SubstrioFindOptions & { limit: 0 }): SubstrioQueryResult<T>;
251
+ /** A page, or the empty record result when limit is zero. No total is returned. */
252
+ find(filter: SubstrioFilter, options: SubstrioFindCursorOptions & { select: string[] }): SubstrioFindPage<Partial<T>[]> | SubstrioQueryResult<T>;
253
+ find(filter: SubstrioFilter, options: SubstrioFindCursorOptions & { select?: undefined }): SubstrioFindPage<SubstrioQueryResult<T>> | SubstrioQueryResult<T>;
254
+ find(filter: SubstrioFilter, options: SubstrioFindCursorOptions): SubstrioFindPage<SubstrioQueryResult<T> | Partial<T>[]> | SubstrioQueryResult<T>;
255
+ /** Without limit or cursor, selection answers plain projected objects. */
256
+ find(filter: SubstrioFilter, options: SubstrioUnpagedFindOptions & { select: string[]; limit?: undefined }): Partial<T>[];
257
+ find(filter: SubstrioFilter, options: SubstrioUnpagedFindOptions & { select: string[] }): Partial<T>[] | SubstrioQueryResult<T>;
258
+ find(filter?: SubstrioFilter, options?: SubstrioUnpagedFindOptions & { select?: undefined }): SubstrioQueryResult<T>;
259
+ /** Optional token/selection properties can select either the paged or unpaged result form. */
260
+ find(filter: SubstrioFilter, options: SubstrioFindOptions): SubstrioFindPage<SubstrioQueryResult<T> | Partial<T>[]> | SubstrioQueryResult<T> | Partial<T>[];
261
+ findOne(filter?: SubstrioFilter): SubstrioRecord<T> | null;
262
+ /**
263
+ * Inserts and returns only the id envelope. The collection assigns `_id`: a document that
264
+ * names one is refused (a TypeError), use put to choose an id. `_createdAt`, `_updatedAt`
265
+ * and `_version` are the system's and ignored.
266
+ */
267
+ insert(document: Partial<T>): { _id: string };
268
+ /** Alias of insert. */
269
+ add(document: Partial<T>): { _id: string };
270
+ /** Inserts and returns the stored row; refuses a document that names `_id`, as insert does. */
271
+ create(document: Partial<T>): SubstrioRecord<T>;
272
+ /** A record, or null if no document has the id. */
273
+ get(id: SubstrioDocumentId): SubstrioRecord<T> | null;
274
+ /** Alias of get. */
275
+ findById(id: SubstrioDocumentId): SubstrioRecord<T> | null;
276
+ /**
277
+ * The document as a live row: a field reads as on a plain row, and every assignment to a field is written at
278
+ * once (no save()). Null if no document has the id. Any id: a synced collection's wide time-ordered ids too.
279
+ * A held row is a snapshot whose writes are checked: after another writer changed the document, an assignment
280
+ * throws an Error with `code` 0x4012 (ROW_STALE), writes nothing, and the row then holds the document as it is
281
+ * (read the field again and retry). No update is lost.
282
+ */
283
+ live(id: SubstrioDocumentId | number | bigint): (T & { _id: string }) | null;
284
+ /**
285
+ * live(id) for an integer id from 1 to 2^32 - 2 (an array index): `db.users[id]` reads, `id in db.users`
286
+ * asks, `db.users[id] = doc` replaces a stored document (a missing id throws a TypeError, nothing written)
287
+ * and `delete db.users[id]` deletes. A larger id (a synced collection's) reads undefined here: use live(id).
288
+ */
289
+ [id: number]: (T & { _id: string }) | undefined;
290
+ /** for (const doc of db.users): every document as a live row, in id order, read 64 at a time. */
291
+ [Symbol.iterator](): Iterator<T & { _id: string }>;
292
+ /**
293
+ * The base of a class on the collection's documents: `class User extends db.users.Model { ... }`. `new User(doc)`
294
+ * inserts once and answers the live row (an assignment after super(doc) is a write). The class's statics:
295
+ * live(id), use(Class), every chain start (User.where(...).all() answers User instances) and iteration.
296
+ */
297
+ readonly Model: new (doc?: Partial<T>) => T & { _id: string };
298
+ /** Binds `klass` (a class that extends Model): every live row of the collection is then an instance of it. */
299
+ use<C extends new (...args: any[]) => any>(klass: C): C;
300
+ /** Sets plain fields by id. */
301
+ set(id: SubstrioDocumentId, fields: Partial<T>): SubstrioRecord<T> | null;
302
+ /** Row after the write; null if absent or if the guard fails. */
303
+ update(id: SubstrioDocumentId, ops: SubstrioUpdate, options?: SubstrioWriteOptions): SubstrioRecord<T> | null;
304
+ /**
305
+ * One batch, ids in order; default error rolls back every insertion on a
306
+ * unique conflict. onConflict:'ignore' skips refused documents and returns
307
+ * only inserted ids. A non-object element throws TypeError naming its index.
308
+ * returning:true answers the inserted rows (records, in order) instead of ids.
309
+ */
310
+ insertMany(documents: Partial<T>[], options: { onConflict?: 'error' | 'ignore'; returning: true }): SubstrioRecord<T>[];
311
+ insertMany(documents: Partial<T>[], options?: { onConflict?: 'error' | 'ignore'; returning?: false }): string[];
312
+ /** Stores each document under its own `_id` (ids in input order; the same id twice gives one row, the last values win). */
313
+ upsertMany(key: '_id', documents: SubstrioIdentifiedDocument<T>[]): string[];
314
+ /** Matches on every key field; ids in input order, repeated keys share a row and later values win. */
315
+ upsertMany<K extends string>(key: K extends '_id' ? never : K, documents: SubstrioUpsertDocument<T>[]): string[];
316
+ upsertMany(key: string[], documents: SubstrioUpsertDocument<T>[]): string[];
317
+ /** Number written; a filter object uses updateWhere. Missing ids are not counted. */
318
+ updateMany(ids: SubstrioDocumentId[] | SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): number;
319
+ /** Number removed; a filter object uses deleteWhere. Missing ids are not counted. */
320
+ deleteMany(ids: SubstrioDocumentId[] | SubstrioFilter): number;
321
+ /** Updates the first match; returns the row after the write or null. */
322
+ findOneAndUpdate(filter: SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): SubstrioRecord<T> | null;
323
+ /** Removed document as a plain object, or null; it is no longer a record to save. */
324
+ findOneAndDelete(filter: SubstrioFilter): (T & { _id: string }) | null;
325
+ /** Replaces fields, retaining system fields; null if absent. */
326
+ replace(id: SubstrioDocumentId, document: Partial<T>): SubstrioRecord<T> | null;
327
+ /** True when the document existed. */
328
+ delete(id: SubstrioDocumentId): boolean;
329
+ count(filter?: SubstrioFilter): number;
330
+ /** True when a document matches; without a filter, whether the collection holds any document. */
331
+ exists(filter?: SubstrioFilter): boolean;
332
+ /** Updates the first match with plain document fields, else inserts. */
333
+ upsert(filter: SubstrioFilter, document: SubstrioUpsertDocument<T>): SubstrioRecord<T>;
334
+ /** Updates the stored document with this `_id`, else inserts it under that id. */
335
+ upsert(key: '_id', document: SubstrioIdentifiedDocument<T>): SubstrioRecord<T>;
336
+ /** The first match as it is (never written), else `document` inserted; `created` says which. */
337
+ findOrCreate(filter: SubstrioFilter, document: Partial<T>): { row: SubstrioRecord<T>; created: boolean };
338
+ /** The first match takes `update`'s plain fields, else the filter's equality fields with them are inserted; `created` says which. */
339
+ updateOrCreate(filter: SubstrioFilter, update: Partial<T>): { row: SubstrioRecord<T>; created: boolean };
340
+ /** Stores the document under the `_id` it holds: a stored document takes its fields, else it is inserted under that id. */
341
+ put(document: SubstrioIdentifiedDocument<T>): SubstrioRecord<T>;
342
+ /** put for each document: ids are returned as strings in input order. */
343
+ putMany(documents: SubstrioIdentifiedDocument<T>[]): string[];
344
+ /** Number removed. */
345
+ deleteWhere(filter?: SubstrioFilter, options?: { limit?: number }): number;
346
+ /** Number written, using the same operators as update. */
347
+ updateWhere(filter: SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): number;
348
+ /** Declares a text field searched by match. */
349
+ text(field: string): this;
350
+ /** Declares a prefix top-k field used by startsWith(...).top(...). */
351
+ top(field: string, weight: string): this;
352
+ /**
353
+ * Declares a string field (a top-level name or a dotted path such as 'profile.bio') keyword: contains/icontains of a word read the field's suffix
354
+ * terms (each distinct word keyed by its suffixes, each cut to termBytes, default 64), with no
355
+ * record read for a one-word icontains. Idempotent for the same termBytes; the field's keys are
356
+ * built before this returns. index.drop (kind collection, keyword true) takes it away.
357
+ */
358
+ keyword(field: string, termBytes?: number): this;
359
+ }
360
+
361
+ // The object chain: db.<collection>.eq(...).gt(...).sort(...).all(...args).
362
+ // SubstrioChain lists exactly the methods of the engine's chain table
363
+ // (kChainMethods, src/substrio/jsc/jsc_db_direct.cpp) and SubstrioChainStart the ones
364
+ // a collection answers to as a chain's first call (not exists: a collection's
365
+ // exists takes a filter and refuses a field name; the terminals first, last,
366
+ // ids, pluck and count also run on the bare collection, see SubstrioChainStart);
367
+ // jsc_chain_conformance_test fails when the names differ.
368
+ export interface SubstrioChainArg {}
369
+ export type SubstrioChainArgument = null | boolean | number | string | Date | SubstrioBinaryValue;
370
+ export type SubstrioChainValue = SubstrioChainArgument | SubstrioChainArg;
371
+ export type SubstrioChainBuilder = (q: SubstrioChain) => unknown;
372
+ /** A value of a field as the documents hold it (a nested value is the value itself). */
373
+ export type SubstrioFieldValue = null | boolean | number | string | unknown[] | Record<string, unknown>;
374
+ /**
375
+ * The bytes json() or jsonFirst() answers: already a JSON document, not a value to read. `toString()`
376
+ * is the one way to get the text; `length` is its byte length. Returned from a function (or passed to
377
+ * `new Response(body, init)`) it is sent as the response body with no further encoding; used a second
378
+ * time that way, the call fails instead of sending an empty body.
379
+ */
380
+ export interface SubstrioJsonBody {
381
+ readonly length: number;
382
+ toString(): string;
383
+ }
384
+ /**
385
+ * Options of values(), distinct(field) and facets(). `top`: the most values
386
+ * per field (facets: 10 by default; values and distinct: every value; 0 is
387
+ * every value, and past 65536 values the call throws: page with `after`).
388
+ * `order`: 'value' (numbers, then strings in byte order; the default of values
389
+ * and distinct) or 'count' (most first, ties by value; the default of facets).
390
+ * `missing`: true adds one last { value: null, count } entry for the documents
391
+ * with no value. `after` (value order only): the answer starts past this value.
392
+ */
393
+ /** sort's options: where the documents with no value (null or missing) go. 'last' is the default. */
394
+ export interface SubstrioSortOptions {
395
+ nulls: 'first' | 'last';
396
+ }
397
+ export interface SubstrioValuesOptions {
398
+ top?: number;
399
+ order?: 'value' | 'count';
400
+ missing?: boolean;
401
+ after?: null | boolean | number | string;
402
+ }
403
+ /** A field's values and the number of matching documents holding each; `more`: `top` cut them. */
404
+ export interface SubstrioValueCounts {
405
+ values: { value: SubstrioFieldValue; count: number }[];
406
+ more: boolean;
407
+ }
408
+ /** What a refused write of a unique field throws (collection.unique). */
409
+ export interface SubstrioUniqueError extends Error {
410
+ name: 'UniqueError';
411
+ code: 'unique';
412
+ collection: string;
413
+ field: string;
414
+ /** The held value, as text. */
415
+ value: string;
416
+ }
417
+
418
+ /** One member of a unique space (db.uniqueSpace): a field, or dotted path, of a collection. */
419
+ export interface SubstrioUniqueSpaceMember {
420
+ collection: string;
421
+ field: string;
422
+ }
423
+
424
+ /** A field rule of a collection schema (collection.schema). */
425
+ export interface SubstrioSchemaRule {
426
+ field: string;
427
+ /** Leave it out, or 'any', for any type. */
428
+ type?: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'datetime' | 'any';
429
+ required?: boolean;
430
+ nullable?: boolean;
431
+ min?: number;
432
+ max?: number;
433
+ pattern?: string;
434
+ enum?: string[];
435
+ }
436
+ /**
437
+ * A rule across fields. A field is present when it holds a value other than null. Every field named has a
438
+ * rule of type string, number, boolean or datetime in the same schema.
439
+ */
440
+ export type SubstrioSchemaConstraint =
441
+ | { kind: 'exactly_one' | 'at_least_one' | 'at_most_one'; fields: string[] }
442
+ | { kind: 'required_when'; field: string; equals: string; required?: string[]; forbidden?: string[] };
443
+ /** A collection's schema (collection.schema). */
444
+ export interface SubstrioSchemaDefinition {
445
+ strict?: boolean;
446
+ rules?: SubstrioSchemaRule[];
447
+ constraints?: SubstrioSchemaConstraint[];
448
+ }
449
+ /** What a write that breaks a collection's schema throws. */
450
+ export interface SubstrioSchemaError extends Error {
451
+ name: 'SchemaError';
452
+ code: 'schema';
453
+ collection: string;
454
+ violations: { field: string; message: string; constraint?: SubstrioSchemaConstraint['kind']; fields?: string[] }[];
455
+ }
456
+ /** What declaring a constraint over documents that already break it throws (collection.schema). */
457
+ export interface SubstrioSchemaViolatorsError extends Error {
458
+ name: 'SchemaViolatorsError';
459
+ code: 'schema_violators';
460
+ collection: string;
461
+ /** How many stored documents break a constraint. */
462
+ count: number;
463
+ /** The id of the first of them. */
464
+ first: string;
465
+ }
466
+
467
+ /** Declarations a collection takes. */
468
+ export interface SubstrioCollectionDeclarations {
469
+ /**
470
+ * Reads the collection's schema, or null when it has none.
471
+ */
472
+ schema(): (SubstrioSchemaDefinition & { collection: string; strict: boolean }) | null;
473
+ /**
474
+ * Sets the collection's schema, replacing the old one (the same declaration as schema.update; returns the
475
+ * collection). `rules` and `constraints` (at least one rule or constraint); a rule's type is a TypeError
476
+ * unless it is one of the listed words. Declaring a constraint over documents that already break it throws
477
+ * a SubstrioSchemaViolatorsError and declares nothing, unless `existing` is 'allow'.
478
+ */
479
+ schema(definition: SubstrioSchemaDefinition & { existing?: 'refuse' | 'allow' }): this;
480
+ /**
481
+ * Declares the field (a top-level name or a dotted path such as 'stripe.account_id') unique (idempotent; returns the collection):
482
+ * no two live documents hold the same value. An insert, upsert, put, update,
483
+ * updateWhere, row save, claim or setAll that would make two hold one value
484
+ * throws a SubstrioUniqueError and writes nothing for that document. A missing
485
+ * field, null and an empty string never conflict; rewriting a document's own
486
+ * value does not either. $inc, $mul, $min, $max and $push on the field are a
487
+ * TypeError; $unset is allowed. Declaring over documents that already share
488
+ * a value throws a SubstrioUniqueError and declares nothing. A field name or a
489
+ * dotted path; an array of them declares a compound (the tuple of their values
490
+ * is unique, and the error also carries `fields` and `values`). `nulls`
491
+ * ('skip', the default, or 'count') says how a null or missing member counts:
492
+ * skipped (a document with no value for a member is not checked) or as one
493
+ * value (two documents with the same other members and a null member conflict;
494
+ * top-level fields only). A member holding several values (an array) is a
495
+ * TypeError, as is declaring the same fields with the other `nulls`.
496
+ * `within` ('collection', the default, 'document' or 'both') says where the
497
+ * value must be unique: across the collection, inside each document (an array
498
+ * at the path may not hold it twice; documents may share it) or both. A
499
+ * document or both declaration is one field with `nulls` skip; a compound or
500
+ * counted one is a TypeError, as is declaring the same fields with another
501
+ * `within`.
502
+ */
503
+ unique(field: string | string[], options?: { nulls?: 'skip' | 'count'; within?: 'collection' | 'document' | 'both' }): this;
504
+ /**
505
+ * Declares that a document is gone `after` milliseconds after the date its field (a name or dotted path)
506
+ * holds (`after` 0: the date is the time it expires; returns the collection). A document without a value in
507
+ * the field never expires. From the moment a document expires every read hides it (find, count, joins,
508
+ * sorted reads, get, findById, exists); the collection's sweeper then deletes it at its time, with its
509
+ * keys, counters and unique values, and publishes its delete. A unique value an expired document holds is
510
+ * free for a new write. The field is declared indexed. Declaring over documents that hold a value that is
511
+ * not a date throws a TypeError naming the first, unless `existing` is 'allow'. Declaring again replaces
512
+ * the period.
513
+ */
514
+ expire(field: string, options?: { after?: number; existing?: 'refuse' | 'allow' }): this;
515
+ /** Takes the expiry declaration away (the field's keys stay; a pending sweep is cancelled); returns the collection. */
516
+ dropExpiry(): this;
517
+ /** Keys each element object of the array field by the values of `members` (in that order): an elemMatch of equalities on the first members and one range on the next reads one key range. */
518
+ elements(field: string, members: string[]): this;
519
+ /** Takes an element declaration away (its keys, id and record); true when it was declared, false when it was not. */
520
+ dropElements(field: string, members: string[]): boolean;
521
+ /** The collection's element declarations, as index.list lists them. */
522
+ elementIndexes(): Array<{ kind: 'collection'; collection: string; field: string; elements: string[]; state: 'none' | 'building' | 'ready'; reason: string | null }>;
523
+ /** Declares a compound index: an equality on every `lead` field and a range or order on the `order` field read one key range (plain top-level field names, none twice). */
524
+ compound(lead: string[], order: string): this;
525
+ /** Takes a compound declaration away (its keys, id and record); true when it was declared, false when it was not. */
526
+ dropCompound(lead: string[], order: string): boolean;
527
+ /** The collection's compound declarations, as index.list lists them. */
528
+ compoundIndexes(): Array<{ kind: 'collection'; collection: string; compound: string[]; state: 'none' | 'building' | 'ready'; reason: string | null }>;
529
+ /** Declares a live count and sum per group (an absent or empty `group` is the whole collection as one group), kept current by every write; idempotent for the same name and shape, a TypeError naming the refusal for a different one; returns the declaration's state. */
530
+ aggregate(name: string, declaration: { group?: Array<string | { field: string; step?: number }>; metrics: Record<string, 'count' | { sum: string }>; where?: string }): 'none' | 'building' | 'ready';
531
+ /** Takes an aggregate declaration away (its group records with it); true when it was declared, false when it was not. */
532
+ dropAggregate(name: string): boolean;
533
+ /** The collection's aggregate declarations, as index.list lists them. */
534
+ aggregates(): Array<{ kind: 'aggregate'; collection: string; name: string; group: Array<string | { field: string; step: number }>; metrics: Record<string, 'count' | { sum: string }>; where?: string; state: 'none' | 'building' | 'ready'; reason: string | null }>;
535
+ /** Declares a one-to-many relation: `key` is the field of `target` holding the id of a document of this collection. */
536
+ hasMany(name: string, target: unknown, key: string): this;
537
+ /** Declares a one-to-one relation: `key` is the field of `target` holding the id of a document of this collection. */
538
+ hasOne(name: string, target: unknown, key: string): this;
539
+ /** Declares a relation to one document: `key` is the field of this collection holding the id of a document of `target`. */
540
+ belongsTo(name: string, target: unknown, key: string): this;
541
+ /**
542
+ * Declares a many-to-many relation named `as`, held by one document per link in the `through`
543
+ * collection: `key` is the field of those documents holding this collection's id and `otherKey` the one
544
+ * holding the target's id (each defaults to the collection's name in the singular with Id: 'users' is
545
+ * 'userId'). with(as) loads the related records, withCount(as) their number as `<as>Count`,
546
+ * whereHas(as, q => ...) and whereNone(as) filter by them; record.link(as, other) and
547
+ * record.unlink(as, other) write the links.
548
+ */
549
+ belongsToMany(target: unknown, options: SubstrioManyToManyOptions): this;
550
+ /**
551
+ * Declares a named scope: `Users.scope('active', c => c.eq('status', 'active'))`, then
552
+ * `Users.active()` starts a chain with the scope applied and `chain.active()` adds it to
553
+ * any chain of the collection (also inside and, or and not). The function is called
554
+ * with the chain at that moment and may add any conditions, a sort or a limit; scopes
555
+ * compose in any order. Declaring a name again replaces the scope. A name that is a
556
+ * built-in method of a collection or chain is a TypeError, and so is calling a scope
557
+ * on a collection that has not declared it. Returns the collection.
558
+ */
559
+ scope(name: string, define: (chain: SubstrioChain) => unknown): this;
560
+ }
561
+
562
+ export interface SubstrioManyToManyOptions {
563
+ through: unknown;
564
+ as: string;
565
+ key?: string;
566
+ otherKey?: string;
567
+ }
568
+
569
+ /** The link methods of a record that has a many-to-many relation declared on its collection. */
570
+ export interface SubstrioRecordLinks {
571
+ /** Links this record to `other` (a record or its _id) through the relation `name`: one write on the through collection, none when the link exists. Answers this record. */
572
+ link(name: string, other: { _id: string } | string): this;
573
+ /** Removes the link to `other` through the relation `name`: one write on the through collection, none when there is no link. Answers this record. */
574
+ unlink(name: string, other: { _id: string } | string): this;
575
+ }
576
+
577
+ /** One condition of a chain as explain() reports it: groups and elemMatch list their conditions. */
578
+ export interface SubstrioExplainCondition {
579
+ op: string;
580
+ field?: string;
581
+ /** The second field of a field comparison (gtField and the others). */
582
+ other?: string;
583
+ conditions?: SubstrioExplainCondition[];
584
+ }
585
+ /**
586
+ * How a chain is answered (chain.explain()), from the route's own record of what it
587
+ * decided. route: `direct` (the document route read value keys), `columns` (the
588
+ * executor over the column mirror) or `relational` (the SQL engine, or a chain that
589
+ * runs no rows). driver: what named the candidate rows: `id` (the ids of an _id
590
+ * condition), `postings` (an eq or in condition's posting lists), `keyedSets` (row sets
591
+ * of keyed conditions combined), `sortWalk` (the sort field's keys in order) or
592
+ * `counter` (a count read from the counters of one equality's keys: no row read); null
593
+ * when no key drove. `keys: 'trigram'` says a substring leaf read its candidates from
594
+ * the trigram keys. residual: the top-level conditions tested on each candidate's
595
+ * stored record (all of them when no key proves any). aggregate: where a grouped or
596
+ * folded answer came from, `keys` (the field's value keys, no record read) or `rows`
597
+ * (each matched record read once); present on the direct route of an aggregate only.
598
+ * declined: why the direct route did not answer, when it did not (see SubstrioExplainDecline).
599
+ */
600
+ export interface SubstrioExplain {
601
+ route: 'direct' | 'columns' | 'relational';
602
+ aggregate?: 'keys' | 'rows';
603
+ driver: { field: string | null; kind: 'id' | 'postings' | 'keyedSets' | 'sortWalk' | 'counter'; keys?: 'trigram' } | null;
604
+ conditions: SubstrioExplainCondition[];
605
+ residual: SubstrioExplainCondition[];
606
+ sort: { field: string; direction: 'asc' | 'desc' }[];
607
+ skip: number;
608
+ limit: number | null;
609
+ declined?: SubstrioExplainDecline;
610
+ }
611
+ /** Why the direct route declined a chain (SubstrioExplain.declined); the columns route or the engine answered it. */
612
+ export type SubstrioExplainDecline =
613
+ | 'Shape' | 'Bind' | 'NoKeyedLeaf' | 'SortField' | 'SortKinds' | 'SortFilterUnkeyed' | 'SortUnbounded'
614
+ | 'Join' | 'Aggregate' | 'MixedIds' | 'Relation';
615
+
616
+ /**
617
+ * A field name of a leaf or a sort key. Besides a top-level field it may be a nested path, the member of a
618
+ * document held in a field ('address.city', 'stats.views'). A top-level field named exactly that text wins;
619
+ * a missing or null member, or a step that is not a document, reads as a missing value; an array anywhere
620
+ * on the path is never traversed (a document holding one never matches, and once an array is keyed under
621
+ * the path the chain throws 'a nested path does not traverse arrays'). A path is for eq, neq, gt, gte, lt,
622
+ * lte, in, nin, between, exists, startsWith, ieq, iin, istartsWith, like, ilike, contains, icontains and
623
+ * endsWith, and for sort keys; select, groupBy, aggregates, joins and the array leaves take top-level
624
+ * names only.
625
+ */
626
+ export type SubstrioFieldPath = string;
627
+
628
+ /** A stored document as a chain reads it: the type's own fields and its string `_id`. */
629
+ export type SubstrioDocument<T> = Omit<T, '_id'> & { _id: string };
630
+ /**
631
+ * What a chain's rows are. 'records' (the default) is the collection's own documents as records (save, set
632
+ * and inc work on them); a select, join, group, bucket or compound answers plain rows of the shape given.
633
+ */
634
+ export type SubstrioRowShape = 'records' | object;
635
+ /** A plain row: the fields of a group (its keys and aggregate aliases), a compound's columns, a path's members. */
636
+ export type SubstrioPlainRow = Record<string, unknown>;
637
+ /** A row of a join: the document's fields and the joined document under its `as` name (null for a left join with no match). */
638
+ export type SubstrioJoinedRow<T> = Partial<SubstrioDocument<T>> & Record<string, unknown>;
639
+ /** The result of a record chain's all(): an Array of records with the result methods (SubstrioQueryResult). */
640
+ export type SubstrioRows<T = Record<string, unknown>> = SubstrioQueryResult<T> & SubstrioRecord<T>[];
641
+ /** One row of a chain of the given shape. */
642
+ export type SubstrioChainRow<T, Shape> = Shape extends 'records' ? SubstrioRecord<T> : Shape;
643
+ /** The rows of a chain of the given shape, as all() answers them. */
644
+ export type SubstrioChainRows<T, Shape> = Shape extends 'records' ? SubstrioRows<T> : Shape[];
645
+ /** A column of columns(): see SubstrioChain.columns. */
646
+ export type SubstrioColumn<V> = [NonNullable<V>] extends [number | boolean]
647
+ ? Float64Array | Array<NonNullable<V> | (number extends NonNullable<V> ? bigint : never) | null>
648
+ : Array<NonNullable<V> | null>;
649
+ /** One column per field of the shape. */
650
+ export type SubstrioColumns<S> = { [K in keyof S]-?: SubstrioColumn<S[K]> };
651
+ /** What columns() answers: a record chain, every field and `_id`; a select, the selected fields. */
652
+ export type SubstrioChainColumns<T, Shape> = Shape extends 'records'
653
+ ? { _id: string[] } & SubstrioColumns<Omit<T, '_id'>>
654
+ : SubstrioColumns<Shape>;
655
+
656
+ export interface SubstrioChain<T = Record<string, unknown>, Shape extends SubstrioRowShape = 'records', Grouped extends boolean = false> {
657
+ /** A binary (an ArrayBuffer or a view such as a Uint8Array) is compared by its bytes: eq, neq, ne, in and nin take it. */
658
+ eq(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): this;
659
+ neq(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): this;
660
+ /** Alias of neq. */
661
+ ne(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): this;
662
+ gt(field: SubstrioFieldPath, value: SubstrioChainValue): this;
663
+ gte(field: SubstrioFieldPath, value: SubstrioChainValue): this;
664
+ /** Alias of gte. */
665
+ ge(field: SubstrioFieldPath, value: SubstrioChainValue): this;
666
+ lt(field: SubstrioFieldPath, value: SubstrioChainValue): this;
667
+ lte(field: SubstrioFieldPath, value: SubstrioChainValue): this;
668
+ /** Alias of lte. */
669
+ le(field: SubstrioFieldPath, value: SubstrioChainValue): this;
670
+ in(field: SubstrioFieldPath, values: (SubstrioChainValue | SubstrioBinaryValue)[]): this;
671
+ nin(field: SubstrioFieldPath, values: (SubstrioChainValue | SubstrioBinaryValue)[]): this;
672
+ /** The condition that the field is present (present = true) or absent (false). */
673
+ exists(field: SubstrioFieldPath, present?: boolean): this;
674
+ /** With no argument, a terminal: whether any document matches. */
675
+ exists(): boolean;
676
+ startsWith(field: SubstrioFieldPath, value: SubstrioChainValue): this;
677
+ /** The array field holds every one of the values as an element (the same kind: 1 is not '1'). An empty list matches every array. */
678
+ containsAll(field: string, values: SubstrioChainValue[]): this;
679
+ /** The array field holds at least one of the values as an element. An empty list matches nothing. */
680
+ containsAny(field: string, values: SubstrioChainValue[]): this;
681
+ /** The array field has exactly n elements. */
682
+ size(field: string, n: number): this;
683
+ /** The array field has more than n elements. */
684
+ sizeGt(field: string, n: number): this;
685
+ /** The array field has fewer than n elements. */
686
+ sizeLt(field: string, n: number): this;
687
+ /**
688
+ * The array of objects has an element satisfying every property of the filter: a value is an
689
+ * equality, an object of operators ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $startsWith,
690
+ * $endsWith, $contains, $icontains) one condition each.
691
+ */
692
+ elemMatch(field: string, filter: Record<string, unknown>): this;
693
+ /**
694
+ * The string field ends with the text (case sensitive). endsWith, contains, icontains and like / ilike read their
695
+ * candidates from the field's trigram keys (built by the first such query, kept by every write), each candidate
696
+ * then tested on its record. A text under 3 bytes (a like pattern's longest literal) has no key: beside another
697
+ * keyed condition it is a per-candidate test, alone it throws (never a walk over every document).
698
+ */
699
+ endsWith(field: SubstrioFieldPath, text: string): this;
700
+ /** The string field holds the text (case sensitive); keyed by trigrams, see endsWith. */
701
+ contains(field: SubstrioFieldPath, text: string): this;
702
+ /** The string field holds the text, ASCII letters compared without case; keyed by trigrams, see endsWith. */
703
+ icontains(field: SubstrioFieldPath, text: string): this;
704
+ /**
705
+ * The string field equals the text, ASCII letters compared without case (`É` and `é` differ). A field
706
+ * that is not a string never matches. Answered from a folded key of the field, built when first asked.
707
+ */
708
+ ieq(field: SubstrioFieldPath, text: string): this;
709
+ /** The string field equals one of the texts, ASCII letters compared without case (see ieq). */
710
+ iin(field: SubstrioFieldPath, texts: string[]): this;
711
+ /** The string field starts with the text, ASCII letters compared without case (see ieq). */
712
+ istartsWith(field: SubstrioFieldPath, text: string): this;
713
+ /** The string field matches the pattern somewhere (a string or RegExp, ECMAScript grammar; only the i flag). */
714
+ regex(field: string, pattern: string | RegExp): this;
715
+ /** Field a equals field b of the same document (both hold a value; a number equals a number, a string a string). */
716
+ eqField(field: string, other: string): this;
717
+ neField(field: string, other: string): this;
718
+ gtField(field: string, other: string): this;
719
+ gteField(field: string, other: string): this;
720
+ ltField(field: string, other: string): this;
721
+ lteField(field: string, other: string): this;
722
+ /** The number (milliseconds) field falls on the day of the date, UTC or the zone given as minutes east of UTC. */
723
+ onDay(field: string, date: Date | number, zone?: number): this;
724
+ /** The field falls in the month (1 to 12) of the year, UTC or in the zone. */
725
+ inMonth(field: string, year: number, month: number, zone?: number): this;
726
+ /** The field falls in the last n calendar days (today and the n - 1 before it) up to now, when the chain is built. */
727
+ lastDays(field: string, n: number, zone?: number): this;
728
+ /** n distinct documents of the answer picked at random, in the answer's order (all of them when fewer match). */
729
+ sample(n: number, ...args: SubstrioChainArgument[]): SubstrioChainRows<T, Shape>;
730
+ /** One document of the answer at random, or null when none match. */
731
+ random(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | null;
732
+ /**
733
+ * fn(document, index) for each document of the answer in its order, stopping when fn returns
734
+ * false; fn may write to the collection. Returns how many documents fn was given.
735
+ */
736
+ each(fn: (document: SubstrioChainRow<T, Shape>, index: number) => unknown, ...args: SubstrioChainArgument[]): number;
737
+ /** fn(documents, page) for each page of n documents of the answer, stopping when fn returns false. Returns the documents given. */
738
+ chunk(n: number, fn: (documents: SubstrioChainRow<T, Shape>[], page: number) => unknown, ...args: SubstrioChainArgument[]): number;
739
+ /** How the chain is answered (runs it once). */
740
+ explain(...args: SubstrioChainArgument[]): SubstrioExplain;
741
+ /**
742
+ * The documents whose text field holds every word of text (mode 'all', the
743
+ * default) or at least one ('any'; with no sort they come ranked by distinct
744
+ * words matched, ties in row order). Words are runs of letters, digits and
745
+ * '_', ASCII case folded. The field is declared with collection.text(field);
746
+ * any other field is an error.
747
+ */
748
+ match(field: string, text: SubstrioChainValue, options?: { mode?: 'all' | 'any' } | 'all' | 'any'): this;
749
+ /**
750
+ * The count documents of greatest numeric weight, ties in row order: sets
751
+ * the chain's sort and limit, and leaves out documents whose weight is not a
752
+ * number. After startsWith(field, prefix) on a field declared with
753
+ * collection.top(field, weight) a prefix of 1 to 3 bytes reads count keys.
754
+ */
755
+ top(count: number, weight: string): this;
756
+ /**
757
+ * The k nearest documents to the vector, ranked nearest first beside the other conditions. The field has a
758
+ * vector index; the vector is a non-empty array of numbers and k a count from 1.
759
+ */
760
+ knn(field: SubstrioFieldPath, vector: number[], k: number): this;
761
+ /**
762
+ * The k (default 10) documents nearest to the point (latitude, longitude), ranked nearest first. The field has a
763
+ * geo index.
764
+ */
765
+ near(field: SubstrioFieldPath, latitude: number, longitude: number, k?: number): this;
766
+ /**
767
+ * The documents whose point lies inside the box: `low` and `high` are its two corners, non-empty arrays of
768
+ * numbers of one length (one number per dimension). The field has a geo or vector index.
769
+ */
770
+ withinBox(field: SubstrioFieldPath, low: number[], high: number[]): this;
771
+ /**
772
+ * The documents whose point lies within `radius` (above 0) of `center`, a non-empty array of numbers. The field
773
+ * has a geo or vector index.
774
+ */
775
+ withinSphere(field: SubstrioFieldPath, center: number[], radius: number): this;
776
+ /**
777
+ * The k documents of greatest dot product with a sparse vector (`indices` and `values` of one length, not empty),
778
+ * ranked best first. The field has a sparse index.
779
+ */
780
+ sparseKnn(field: SubstrioFieldPath, vector: { indices: number[]; values: number[] }, k: number): this;
781
+ /**
782
+ * The documents whose text field holds the text's words as a phrase, or with `fuzzy` (true, or a nonzero number)
783
+ * the documents a fuzzy match of the text reaches. The field has a trigram index. Words are runs of ASCII letters
784
+ * and digits, without case; any other character (punctuation, space, non-ASCII) separates words, and stop words and
785
+ * 1-letter words are skipped on both sides. The phrase's words must stand adjacent and in order: the first ends a
786
+ * word, the last begins one, middle words are whole, a single word lies inside a word, and a word under 3 letters
787
+ * matches only an equal word. A text of only stop words matches nothing.
788
+ */
789
+ textNear(field: SubstrioFieldPath, text: string, fuzzy?: boolean | number): this;
790
+ /**
791
+ * The collection's $search in its vector mode: the text is embedded by the collection's RAG configuration and the
792
+ * k (default 50) documents nearest to the embedding in the RAG vector field are kept, ranked nearest first. The
793
+ * hybrid mode, decay, boost_feedback and strict_model are options of the HTTP $search, not of a chain (a chain
794
+ * that names them throws).
795
+ */
796
+ search(text: string, options?: { k?: number; mode?: 'vector' }): this;
797
+ and(...conditions: SubstrioChainBuilder[]): this;
798
+ or(...conditions: SubstrioChainBuilder[]): this;
799
+ not(...conditions: SubstrioChainBuilder[]): this;
800
+ /** The conditions of a filter object (the operators find takes), ANDed with the chain's: where({ n: { $lt: 5 } }). */
801
+ where(filter: SubstrioFilter): this;
802
+ /**
803
+ * direction: 1, 'asc' or false ascending (the default); -1, 'desc' or true
804
+ * descending. Repeated calls (or an array of [field, direction]) add keys in
805
+ * order; several keys make the chain relational (SQL semantics apply). A
806
+ * field may be 'as.field' of a join. The documents with no value (null or
807
+ * missing) come last in either direction; { nulls: 'first' } puts them first
808
+ * (in id order), on a plain single-key sort of the collection's own field
809
+ * only: it is refused with several keys, a cursor page, last() or an aggregate.
810
+ */
811
+ sort(field: SubstrioFieldPath, direction?: 1 | -1 | 'asc' | 'desc' | boolean, options?: SubstrioSortOptions): this;
812
+ /**
813
+ * Several sort keys (chained `sort` calls, or the array form) of the collection's own fields: the first
814
+ * key's order, each run of equal values ordered by the next key, a document with no value (null or
815
+ * missing) after those with one on every key in either direction, ties in id order. They run on the
816
+ * keyed executor, not SQL; a cursor page (`page`, `pages`) refuses them.
817
+ */
818
+ sort(keys: Array<string | [string, (1 | -1 | 'asc' | 'desc' | boolean)?]>): this;
819
+ /** Alias of sort. */
820
+ sort_by(field: string, direction?: 1 | -1 | 'asc' | 'desc' | boolean, options?: SubstrioSortOptions): this;
821
+ sort_by(keys: Array<string | [string, (1 | -1 | 'asc' | 'desc' | boolean)?]>): this;
822
+ /**
823
+ * Groups the matching documents by a field's value: sum, avg, min, max and
824
+ * count then answer per group (see SubstrioGroupedChain).
825
+ */
826
+ group_by(field: string): SubstrioGroupedChain<T>;
827
+ limit(count: number): this;
828
+ skip(count: number): this;
829
+ /**
830
+ * The queue terminal: takes up to `n` (an integer from 1) documents the
831
+ * chain selects, in its sort order, and applies `ops` (what update(id, ops)
832
+ * takes: $set, $inc, $mul, $min, $max, $unset) to each one, guarded by the
833
+ * chain's own conditions so two claimers never both claim a document.
834
+ * Resolves to the claimed rows as all() answers them, after the update, in
835
+ * claim order; [] when none. Refused: a join, select, groupBy, skip, limit,
836
+ * and a condition a guard cannot re-test (or, not, in, between, like, ...).
837
+ */
838
+ claim(n: number, ops: Record<string, unknown>, ...args: SubstrioChainValue[]): SubstrioChainRows<T, Shape>;
839
+ /**
840
+ * Applies `ops` (what updateWhere takes: update operators, or plain fields to
841
+ * set) to every document the chain answers; its sort, skip and limit choose
842
+ * the documents as they do for ids(). Returns the number written. Refused:
843
+ * a join, with, groupBy, distinct or compound chain. A trailing { returning: true }
844
+ * answers the rows written (records, in order) instead of the count.
845
+ */
846
+ update(ops: Record<string, unknown>, ...args: SubstrioChainArgument[]): number;
847
+ update(ops: Record<string, unknown>, ...args: [...SubstrioChainArgument[], { returning: true }]): SubstrioRecord<T>[];
848
+ /** Deletes every document the chain answers (the same documents as ids()); returns the number removed. */
849
+ delete(...args: SubstrioChainArgument[]): number;
850
+ /** update({ $inc: { [field]: n } }) with n = 1 by default (a finite number, or a BigInt inside int64); returns the number written. */
851
+ increment(field: string, n?: number | bigint, ...args: SubstrioChainArgument[]): number;
852
+ /** With a trailing { returning: true }: the rows written (records, in order). */
853
+ increment(field: string, n: number | bigint, ...args: [...SubstrioChainArgument[], { returning: true }]): SubstrioRecord<T>[];
854
+ /** update({ $inc: { [field]: -n } }) with n = 1 by default; returns the number written. */
855
+ decrement(field: string, n?: number | bigint, ...args: SubstrioChainArgument[]): number;
856
+ /** With a trailing { returning: true }: the rows written (records, in order). */
857
+ decrement(field: string, n: number | bigint, ...args: [...SubstrioChainArgument[], { returning: true }]): SubstrioRecord<T>[];
858
+ /** first(), but a chain that matches nothing throws an Error named NotFoundError. */
859
+ firstOrFail(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape>;
860
+ /** The field of the first row the chain answers; undefined when it matches nothing or the row has no such field. */
861
+ value<K extends keyof T & string>(field: K, ...args: SubstrioChainArgument[]): T[K] | undefined;
862
+ value(field: SubstrioFieldPath, ...args: SubstrioChainArgument[]): unknown;
863
+ /** The negation of exists(): true when no document matches. */
864
+ missing(): boolean;
865
+ /**
866
+ * One page of the chain in its sort order (or id order when unsorted).
867
+ * `next` and `prev` are opaque cursors bound to this query: pass `next` as
868
+ * `after` for the following page (null on the last page), `prev` as `before`
869
+ * for the page ahead. A page at any depth costs what the first page costs on
870
+ * a keyed number sort. `skip` cannot be combined with a page.
871
+ */
872
+ page(options: { size: number; after?: string | null; before?: string | null; total?: boolean }, ...args: SubstrioChainValue[]):
873
+ { rows: SubstrioChainRows<T, Shape>; next: string | null; prev: string | null; total?: number };
874
+ /** Every page of the chain in turn, as an iterator of row arrays. */
875
+ pages(options: { size: number }, ...args: SubstrioChainValue[]): IterableIterator<SubstrioChainRows<T, Shape>>;
876
+ /**
877
+ * The fields each row carries. A name with a dot that no join alias spells is a nested path: select('name', 'address.city')
878
+ * answers `{name, address: {city}}`, the member read in place from each document; a member the document lacks (a
879
+ * missing step, a scalar or an array on the path) is absent from the row, a stored null is null. Only a plain chain
880
+ * answers a path (not with join, groupBy, distinct or columns()).
881
+ */
882
+ select<K extends keyof SubstrioDocument<T> & string>(...fields: K[] | [K[]]): SubstrioChain<T, Pick<SubstrioDocument<T>, K>>;
883
+ select(...fields: string[] | [string[]]): SubstrioChain<T, SubstrioPlainRow>;
884
+ join(collection: string, localField: string, foreignField: string, as: string): SubstrioChain<T, SubstrioJoinedRow<T>>;
885
+ // Relational chains: a chain that uses any of the methods below runs through
886
+ // the SQL engine and SQL semantics apply to it (a null never compares equal,
887
+ // a root document
888
+ // appears once per joined match). A chain that uses none keeps the document
889
+ // rules of the methods above.
890
+ /** SQL semantics apply: every match of the foreign collection; a document with none is dropped. */
891
+ innerJoin(collection: string, localField: string, foreignField: string, as: string): SubstrioChain<T, SubstrioJoinedRow<T>>;
892
+ /** SQL semantics apply: every match of the foreign collection; a document with none stays, its `as` null. */
893
+ leftJoin(collection: string, localField: string, foreignField: string, as: string): SubstrioChain<T, SubstrioJoinedRow<T>>;
894
+ /** Each row carries the related records declared on the collection (hasMany: an array; hasOne and belongsTo: a record or null). Without projection these remain collection records. */
895
+ with(...relations: (string | string[] | ((q: SubstrioChain<T>) => SubstrioChain<T>))[]): this;
896
+ /** The documents that have a related record of the relation (matching the function's chain when given); the related side is read when called. */
897
+ whereHas(relation: string, constraint?: (q: SubstrioChain) => SubstrioChain): this;
898
+ /**
899
+ * The documents whose number of related records of a hasMany or hasOne relation compares to `n` ('>=', '>', '=',
900
+ * '<', '<='; `n` a whole number from 0): one count lookup per candidate document, no related key set is built.
901
+ * An owner with no related record has 0. Not with sort, nor inside and, or, not; a plain run (all, first, count, ids, pluck) answers it.
902
+ */
903
+ whereHas(relation: string, op: '>=' | '>' | '=' | '<' | '<=', n: number): this;
904
+ /** The documents that have no related record of the relation (matching the function's chain when given). */
905
+ whereNone(relation: string, constraint?: (q: SubstrioChain) => SubstrioChain): this;
906
+ /** Each row carries the number of related records of a hasMany relation as `<name>Count`, with no record read. */
907
+ withCount(...relations: (string | string[])[]): this;
908
+ /** The distinct values of a field among the matching documents (values() without the counts). */
909
+ distinct(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioFieldValue[];
910
+ /**
911
+ * Each row of select() once; answers plain rows. select(fields).distinct() of the collection's own fields
912
+ * is the groups of those fields (no group or aggregate), read from the rows the filter yields by the
913
+ * aggregate stage (not a relational chain: explain().route is 'direct'); whole rows and joined fields keep
914
+ * SQL semantics. An integer from 2^53 up in a distinct or grouped key is returned exact, as a BigInt.
915
+ */
916
+ distinct(): SubstrioChain<T, Shape extends 'records' ? SubstrioJoinedRow<T> : Shape, Grouped>;
917
+ /** SQL semantics apply: groups by fields ('as.field' names a join's); with count/sum/avg/min/max selectors all() answers one plain row per group. */
918
+ groupBy(...fields: string[] | [string[]]): SubstrioChain<T, SubstrioPlainRow, true>;
919
+ /**
920
+ * A group key that cuts a field's numbers (or dates, in ms) into buckets of `step`: a number, or text such as
921
+ * '1h', '1d', '1w', '30m', '15s', '250ms'. The group's value is the bucket's first value (floor(v / step) * step);
922
+ * rows with no number are one null group. `fill: true` also answers the empty buckets between the first and
923
+ * the last. Like groupBy it takes count, sum, avg, min and max after it.
924
+ */
925
+ bucket(field: string, step: number | string, options?: { fill?: boolean }): SubstrioChain<T, SubstrioPlainRow, true>;
926
+ /** bucket(field, step, options) with a count: rows are { [field]: bucket start, count }. */
927
+ histogram(field: string, step: number | string, options?: { fill?: boolean }): SubstrioChain<T, SubstrioPlainRow, true>;
928
+ /**
929
+ * The first `n` documents of each group of `groupField` in `sortField`'s order (direction 1, -1, 'asc' or
930
+ * 'desc'; ascending by default), the groups in value order. Each group is an ordered key walk stopped at `n`
931
+ * rows, never a sort of all the rows. Documents with no group value are in no group. The chain holds
932
+ * conditions only (no sort, skip, limit or select).
933
+ */
934
+ topPerGroup(groupField: string, n: number, sortField: string, direction?: 1 | -1 | 'asc' | 'desc', ...args: SubstrioChainArgument[]): SubstrioChainRows<T, Shape>;
935
+ /** How many different values the field holds among the matching documents (null and missing do not count). */
936
+ countDistinct(field: string, ...args: SubstrioChainArgument[]): number;
937
+ /** SQL semantics apply: a condition over a group key or an aggregate alias; op is '=', '!=', '>', '>=', '<' or '<='. */
938
+ having(field: string, op: '=' | '==' | '!=' | '<>' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne' | 'neq' | 'gt' | 'gte' | 'ge' | 'lt' | 'lte' | 'le', value: SubstrioChainValue): this;
939
+ /**
940
+ * `%` any run of characters, `_` any one character, a backslash makes the next character literal. Case-sensitive;
941
+ * a document whose field is not a string never matches. Run by the executor: `abc%` is startsWith, `%abc` endsWith,
942
+ * `%abc%` contains and `abc` eq (keyed); any other pattern tests each candidate, behind a startsWith of its literal
943
+ * prefix. A pattern is a string (or an argument). Wherever the chain runs through SQL (a subquery, a compound, a join the document route declines) it is refused, as contains and regex are.
944
+ */
945
+ like(field: SubstrioFieldPath, pattern: SubstrioChainValue): this;
946
+ /** like folding ASCII letters only (as icontains does): `É` and `é` differ. */
947
+ ilike(field: SubstrioFieldPath, pattern: SubstrioChainValue): this;
948
+ /**
949
+ * low <= field <= high, both bounds included, run by the executor as gte(low) and lte(high) of one field; a
950
+ * document with a null or missing field never matches, and a null bound is refused. (SQL semantics apply
951
+ * to a joined field's between and to having.)
952
+ */
953
+ between(field: SubstrioFieldPath, low: SubstrioChainValue, high: SubstrioChainValue): this;
954
+ /** SQL semantics apply: IN over another chain's field (a null in it makes notInQuery match nothing). The chain is read as it is now. */
955
+ inQuery(field: SubstrioFieldPath, chain: SubstrioChain<any, any>, chainField: string): this;
956
+ /** SQL semantics apply: NOT IN over another chain's field. */
957
+ notInQuery(field: string, chain: SubstrioChain<any, any>, chainField: string): this;
958
+ /** SQL semantics apply: every document when the chain has one, none otherwise. */
959
+ existsQuery(chain: SubstrioChain<any, any>): this;
960
+ /** SQL semantics apply: the rows of both chains' select() fields, duplicates removed; answers plain rows. The other chain cannot itself be a compound yet (flatten it: a.union(b).union(c)) and cannot carry its own sort, skip or limit yet: this chain's apply to the whole compound. */
961
+ union(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>;
962
+ /** SQL semantics apply: the rows of both chains' select() fields, duplicates kept. The other chain cannot itself be a compound yet or carry its own sort, skip or limit yet. */
963
+ unionAll(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>;
964
+ /** SQL semantics apply: the rows both chains have. The other chain cannot itself be a compound yet or carry its own sort, skip or limit yet. */
965
+ intersect(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>;
966
+ /** SQL semantics apply: the rows this chain has and the other does not. The other chain cannot itself be a compound yet or carry its own sort, skip or limit yet. */
967
+ except(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>;
968
+ all(...args: SubstrioChainArgument[]): SubstrioChainRows<T, Shape>;
969
+ first(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | null;
970
+ /**
971
+ * The JSON array of exactly the rows all() answers, in the same order, as one SubstrioJsonBody: no
972
+ * JavaScript object is built per row. Not yet for a chain with select, a join, with, whereHas,
973
+ * whereNone, groupBy or a compound (union, intersect, except, distinct) or window (bucket,
974
+ * histogram): throws naming the first one it finds.
975
+ */
976
+ json(...args: SubstrioChainArgument[]): SubstrioJsonBody;
977
+ /** The one object first() answers, or the four bytes `null`, as a SubstrioJsonBody. Same restrictions as json(). */
978
+ jsonFirst(...args: SubstrioChainArgument[]): SubstrioJsonBody;
979
+ /**
980
+ * The last row in the chain's order, or null. A sorted chain reads its reversed walk (no walk of
981
+ * all rows); a chain with no condition, sort or window reads the end of the collection.
982
+ */
983
+ last(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | null;
984
+ /**
985
+ * The number of matching documents (before skip and limit). Once groupBy, bucket or histogram was
986
+ * called, count(alias = 'count') adds the group's row count as column `alias` and answers the chain
987
+ * (SQL semantics apply; end it with all(), first() or having()).
988
+ */
989
+ count(...args: SubstrioChainArgument[]): Grouped extends true ? this : number;
990
+ ids(...args: SubstrioChainArgument[]): string[];
991
+ /**
992
+ * One field's value of every match, in the chain's order (no row is built). A nested path ('address.city') is read in
993
+ * place from each document: undefined for a document that lacks it (a missing step, a scalar or an array on the
994
+ * path), null for a stored null.
995
+ */
996
+ pluck<K extends keyof T & string>(field: K, ...args: SubstrioChainArgument[]): (T[K] | undefined)[];
997
+ pluck(field: SubstrioFieldPath, ...args: SubstrioChainArgument[]): unknown[];
998
+ /**
999
+ * One array per selected field (every field of the collection, and `_id`, when nothing is selected), in the
1000
+ * chain's order. A column whose every cell is present and a number or boolean is a Float64Array (a boolean
1001
+ * reads 0 or 1); any other is an Array of the cells' own values (strings, Dates, BigInts past 2^53), null
1002
+ * where a document lacks the field. Throws a TypeError when a selected field is a nested path (a.b).
1003
+ */
1004
+ columns(...args: SubstrioChainArgument[]): SubstrioChainColumns<T, Shape>;
1005
+ /**
1006
+ * The sum of a field's numbers over the matching documents (sort, skip and
1007
+ * limit do not apply); 0 when none holds a number. Integers add exactly
1008
+ * (int64) and the result is the nearest double, so it is exact up to 2^53;
1009
+ * past int64 the sum continues as a double and never wraps. Once groupBy, bucket or histogram was
1010
+ * called, sum(field, alias = 'sum_<field>') adds the group's sum as column `alias` and answers the
1011
+ * chain; avg, min and max do the same (aliases `avg_<field>`, `min_<field>`, `max_<field>`).
1012
+ */
1013
+ sum(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number;
1014
+ /** The mean of a field's numbers, null when no matching document holds one. */
1015
+ avg(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | null;
1016
+ /** The smallest of a field's numbers, null when there is none. */
1017
+ min(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | null;
1018
+ /** The largest of a field's numbers, null when there is none. */
1019
+ max(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | null;
1020
+ /**
1021
+ * Each value of a field among the matching documents with the number of
1022
+ * documents holding it, read from the field's value keys where it has them
1023
+ * (sort, skip and limit do not apply). See SubstrioValuesOptions.
1024
+ */
1025
+ values(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioValueCounts;
1026
+ /** values() of several fields over the chain's one filter, in one call: one property per field. */
1027
+ facets(fields: string | string[], options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): Record<string, SubstrioValueCounts>;
1028
+ }
1029
+ export type SubstrioChainStart<T = Record<string, unknown>> = Pick<SubstrioChain<T>,
1030
+ 'eq' | 'neq' | 'ne' | 'gt' | 'gte' | 'ge' | 'lt' | 'lte' | 'le' | 'in' | 'nin' | 'startsWith' | 'containsAll' | 'containsAny' | 'size' | 'sizeGt' | 'sizeLt' | 'elemMatch' | 'endsWith' | 'contains' | 'icontains' | 'ieq' | 'iin' | 'istartsWith' | 'regex' | 'eqField' | 'neField' | 'gtField' | 'gteField' | 'ltField' | 'lteField' | 'onDay' | 'inMonth' | 'lastDays' | 'match' | 'knn' | 'near' | 'withinBox' | 'withinSphere' | 'sparseKnn' | 'textNear' | 'search' | 'and' | 'or' | 'not' | 'where' | 'sort' | 'sort_by' | 'group_by' | 'limit' | 'skip' | 'select' | 'join' | 'innerJoin' | 'leftJoin' | 'distinct' | 'groupBy' | 'bucket' | 'histogram' | 'like' | 'ilike' | 'between' | 'inQuery' | 'notInQuery' | 'existsQuery' | 'with' | 'withCount' | 'whereHas' | 'whereNone' | 'page' | 'pages'
1031
+ > & {
1032
+ /** Every document of the collection: the chain with no condition, run. */
1033
+ all(...args: SubstrioChainArgument[]): SubstrioRows<T>;
1034
+ /** The terminals over the ids of every document (see SubstrioChain). */
1035
+ sample(n: number, ...args: SubstrioChainArgument[]): SubstrioRows<T>;
1036
+ random(...args: SubstrioChainArgument[]): SubstrioRecord<T> | null;
1037
+ each(fn: (document: SubstrioRecord<T>, index: number) => unknown, ...args: SubstrioChainArgument[]): number;
1038
+ chunk(n: number, fn: (documents: SubstrioRecord<T>[], page: number) => unknown, ...args: SubstrioChainArgument[]): number;
1039
+ explain(...args: SubstrioChainArgument[]): SubstrioExplain;
1040
+ /** The first and the last document (the ends of the collection), or null when it is empty. */
1041
+ first(): SubstrioRecord<T> | null;
1042
+ last(): SubstrioRecord<T> | null;
1043
+ /** The first document, or a NotFoundError when the collection is empty; one field of it; whether the collection is empty. */
1044
+ firstOrFail(): SubstrioRecord<T>;
1045
+ value<K extends keyof T & string>(field: K): T[K] | undefined;
1046
+ value(field: SubstrioFieldPath): unknown;
1047
+ missing(): boolean;
1048
+ /** The ids and one field of every document (count() is the collection's own, from its counter). */
1049
+ ids(): string[];
1050
+ pluck<K extends keyof T & string>(field: K): (T[K] | undefined)[];
1051
+ pluck(field: SubstrioFieldPath): unknown[];
1052
+ /** The aggregates over every document of the collection (see SubstrioChain). */
1053
+ sum(field: string, ...args: SubstrioChainArgument[]): number;
1054
+ avg(field: string, ...args: SubstrioChainArgument[]): number | null;
1055
+ min(field: string, ...args: SubstrioChainArgument[]): number | null;
1056
+ max(field: string, ...args: SubstrioChainArgument[]): number | null;
1057
+ countDistinct(field: string, ...args: SubstrioChainArgument[]): number;
1058
+ /** The first `n` documents of each group in `sortField`'s order (see SubstrioChain). */
1059
+ topPerGroup(groupField: string, n: number, sortField: string, direction?: 1 | -1 | 'asc' | 'desc', ...args: SubstrioChainArgument[]): SubstrioRows<T>;
1060
+ /** The values, distinct values and facets over every document of the collection (see SubstrioChain). */
1061
+ values(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioValueCounts;
1062
+ distinct(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioFieldValue[];
1063
+ facets(fields: string | string[], options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): Record<string, SubstrioValueCounts>;
1064
+ };
1065
+ /**
1066
+ * A chain after group_by(field). Its terminals return a plain object with one
1067
+ * property per group: the key is the group value as JS spells it (a string as
1068
+ * is, String(number), "true", "false", "null" for null or a missing field,
1069
+ * an object's or an array's JSON), and values spelled the same (1 and "1")
1070
+ * share a property. Integer-like keys enumerate first, as in any object; the
1071
+ * others in first-seen order. No match gives an empty object. A group with no
1072
+ * number in the field has sum 0 and null for avg, min and max. A Date written
1073
+ * from script is stored as its ISO string and groups under that string. The
1074
+ * group value "__proto__" is refused.
1075
+ */
1076
+ export type SubstrioGroupedChain<T = Record<string, unknown>> = {
1077
+ [K in 'eq' | 'neq' | 'ne' | 'gt' | 'gte' | 'ge' | 'lt' | 'lte' | 'le' | 'in' | 'nin' | 'exists' | 'startsWith' | 'and' | 'or' | 'not' | 'group_by']:
1078
+ (...args: Parameters<SubstrioChain<T>[K]>) => SubstrioGroupedChain<T>;
1079
+ } & {
1080
+ sum(field: string, ...args: SubstrioChainArgument[]): Record<string, number>;
1081
+ avg(field: string, ...args: SubstrioChainArgument[]): Record<string, number | null>;
1082
+ min(field: string, ...args: SubstrioChainArgument[]): Record<string, number | null>;
1083
+ max(field: string, ...args: SubstrioChainArgument[]): Record<string, number | null>;
1084
+ /** The matching documents of each group. */
1085
+ count(...args: SubstrioChainArgument[]): Record<string, number>;
1086
+ };
1087
+ // @substrio-chain-end