Runtime API reference

Every global, type and module a function can call, with its signature, read from the declarations the server serves.

Docs menu

The declarations below are the file the server serves at GET /runtime.d.ts. Point your editor at it for completion and type checks while you write a function:

curl -s "$SUBSTRIO_URL/runtime.d.ts" -o substrio-runtime.d.ts

Every global below is in scope in a function without an import:

export default {
  fetch(request, env, ctx) {
    const kv = db.kv('visits');
    const visits = Number(kv.get('count') ?? 0) + 1;
    kv.put('count', visits);
    return { visits };
  },
};

Handlers

JsonValue type

type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };

JSON-compatible values a handler may return (answered as application/json).

SubstrioCallerAccount interface

interface SubstrioCallerAccount

What a route function receives first: a Request plus the route's data.

An account the caller belongs to, with the caller's role in it.

id: number
name: string
created_at: number
services: number

A bit per service the account is entitled to.

suspended: boolean
require_mfa: boolean

Every member signs in with 2FA.

personal: boolean

Made for the person at sign-up.

role?: 'developer' | 'admin' | 'owner'

SubstrioRequestAuth interface

interface SubstrioRequestAuth

The caller the server verified from a tenant token.

user_id: number

The person's id (a number, as every permission call takes it).

email_verified: boolean
expires_at: number

When the token expires, in ms since the epoch.

impersonator?: number

The operator acting as this person, when the session is an impersonation.

accounts: SubstrioCallerAccount[]

The accounts the person belongs to.

holds?: boolean

Whether the caller holds every permission the route asks for; present only when it asks (or for an all-holding caller).

SubstrioRequest interface

interface SubstrioRequest extends Request
params: Record<string, string>

The captures of the declared route ("GET /emails/:id" gives { id }).

auth: SubstrioRequestAuth | null

The verified caller from a tenant token, or null (as the server writes it: session_auth_json).

readonly caller: { slug: string; app: string; sandbox: string | null } | null

The sandbox that called, from the signed Substrio-Caller stamp, or null.

readonly ip?: string | null

The caller's address ("203.0.113.7", "2001:db8::1") on a route declared with the last word "ip" ("GET /here public ip"): the socket's peer, or behind a --trusted-proxy peer that proxy's header. null when the socket gave none; undefined on every route without the word.

SubstrioExecutionContext interface

interface SubstrioExecutionContext

The third argument of a route call.

waitUntil(promise: Promise<unknown>): void

The promise is awaited before the response is sent (not a background task).

SubstrioFetchHandler type

type SubstrioFetchHandler = (
  request: SubstrioRequest,
  env: SubstrioEnv,
  ctx: SubstrioExecutionContext,
) => Response | JsonValue | Promise<Response | JsonValue>;

A route function: export default { fetch }, or the function itself.

SubstrioCronEvent interface

interface SubstrioCronEvent

The event of a cron entry (the function is called as handler(event)); fires on the server's local clock.

trigger: 'cron'
cron_id: string
function: string
timestamp: number

Unix seconds.

rawBody: Uint8Array

The bytes event was built from, before JSON parsing. Read once; cached on first read.

SubstrioDocumentEvent interface

interface SubstrioDocumentEvent

The event of a document trigger (INSERT, UPDATE or DELETE on a collection, or "*").

type: 'INSERT' | 'UPDATE' | 'DELETE'
collection: string
documentId: string
timestamp: number

Unix milliseconds.

rawBody: Uint8Array

The bytes event was built from, before JSON parsing. Read once; cached on first read.

SubstrioQueueMessage interface

interface SubstrioQueueMessage<Body = unknown>

The entry of each trigger kind. Cron, document and queue triggers exist; there is no email trigger, so no email() export is ever called. HTTP route call fetch(request, env, ctx) export default { fetch } or the function cron scheduled(event, env, ctx), else the default-export function called as handler(event) queue delivery queue(batch, env, ctx) the consumer a version's queues names document trigger the default-export function, handler(event) substrio.functions.<name>(...) the default-export function, handler(...args) substrio.functions.<name>.fetch(req) fetch(request, env, ctx) export default { fetch } is NOT called for anything but HTTP route calls. A cron, queue or trigger call on a module without the export it needs fails with an error that names the export.

One message a queue() handler receives.

readonly id: string

The send's idempotency id, else the message's number in its queue.

readonly body: Body

The body as it was sent (JSON).

readonly timestamp: number

Unix milliseconds the message was sent.

readonly attempts: number

Deliveries so far, this one included (1 on the first).

ack(): void

Done: the message leaves the queue. Without any call, it is acked when the handler returns.

retry(options?: { delay?: number }): void

Deliver it again after delay ms (default: the queue's exponential backoff); past max_retries it is a dead letter.

SubstrioQueueBatch interface

interface SubstrioQueueBatch<Body = unknown>
readonly queue: string
readonly messages: readonly SubstrioQueueMessage<Body>[]
ackAll(): void
retryAll(options?: { delay?: number }): void

SubstrioQueueHandler type

type SubstrioQueueHandler<Body = unknown> = (batch: SubstrioQueueBatch<Body>, env: SubstrioEnv, ctx: SubstrioExecutionContext) => unknown;

A queue consumer. A message the handler does not settle is acked when the handler returns and retried when it throws or rejects.

SubstrioQueueSent interface

interface SubstrioQueueSent

The result of a send: the message's number, and whether a known idempotency id made it a no-op.

seq: number
duplicate: boolean

SubstrioQueue interface

interface SubstrioQueue

substrio.queue(name): a durable queue the app's version declares in queues. Sending to a name it does not declare throws. Delays are milliseconds.

send(body: unknown, options?: { delay?: number; id?: string }): SubstrioQueueSent
sendBatch(items: readonly (unknown | { body: unknown; delay?: number; id?: string })[]): SubstrioQueueSent[]

SubstrioScheduledEvent interface

interface SubstrioScheduledEvent extends SubstrioCronEvent
type: 'scheduled'
cron: string

The cron entry's id (the same as cron_id).

scheduledTime: number

Unix milliseconds.

SubstrioScheduledHandler type

type SubstrioScheduledHandler = (event: SubstrioScheduledEvent, env: SubstrioEnv, ctx: SubstrioExecutionContext) => unknown;

SubstrioEventHandler type

type SubstrioEventHandler = (event: SubstrioCronEvent | SubstrioDocumentEvent | Record<string, JsonValue>) => unknown;

env

SubstrioEnv interface

interface SubstrioEnv

Secrets and vars (strings, read-only), then the function's typed bindings.

SubstrioAssets interface

interface SubstrioAssets
fetch(request: Request | string): Promise<Response>

Files of the function's deployment next to its source.

SubstrioKvNamespace interface

interface SubstrioKvNamespace

Cloudflare KVNamespace shim over db.kv; options are ignored. list() returns every key at once and is unreliable.

get(key: string, type?: 'text' | 'json' | { type: 'json' }): Promise<unknown>
put(key: string, value: string | JsonValue, options?: unknown): Promise<void>
delete(key: string): Promise<void>
list(options?: unknown): Promise<{ keys: { name: string }[]; list_complete: boolean }>

SubstrioD1Statement interface

interface SubstrioD1Statement

Cloudflare D1 shim over SQL; batch() runs the statements one after another, NOT as a transaction.

bind(...values: unknown[]): SubstrioD1Statement
first(column?: string): Promise<Record<string, unknown> | unknown | null>
all(): Promise<{ results: Record<string, unknown>[]; success: boolean; meta: { changes: number; last_row_id: number } }>
run(): Promise<{ results: Record<string, unknown>[]; success: boolean; meta: { changes: number; last_row_id: number } }>
raw(): Promise<unknown[][]>

SubstrioD1Database interface

interface SubstrioD1Database
prepare(sql: string): SubstrioD1Statement
batch(statements: SubstrioD1Statement[]): Promise<unknown[]>
exec(sql: string): Promise<unknown>

SubstrioServiceBinding interface

interface SubstrioServiceBinding

Calls another function of the tenant as an HTTP request.

fetch(request: Request | string, init?: RequestInit): Promise<Response>

env global

declare const env: SubstrioEnv;

db

SubstrioSqlParam type

type SubstrioSqlParam = null | undefined | boolean | number | bigint | string | Uint8Array | ArrayBuffer;

A value a chain condition or a SQL parameter takes.

SubstrioSqlRow type

type SubstrioSqlRow = Record<string, unknown>;

SubstrioSqlResult type

type SubstrioSqlResult = { changes: number; lastInsertId: number | bigint };

SubstrioLimitOptions interface

interface SubstrioLimitOptions

The database of the function's app. Synchronous: every call returns its value at once. A wrong argument throws a TypeError naming the call. db.<name> is the collection called name (any name not listed below).

cost?: number
sliding?: boolean

SubstrioLimitResult interface

interface SubstrioLimitResult

allowed: the cost fit under max; remaining: what is left in the window; resetAt: the window's end, in epoch ms.

allowed: boolean
remaining: number
resetAt: number

SubstrioPermissionAuth type

type SubstrioPermissionAuth = { user_id: number; email_verified?: boolean } | null;

The caller a permission call decides for, typically request.auth.

SubstrioDb interface

interface SubstrioDb
collection<T = SubstrioDoc>(name: string): SubstrioRuntimeCollection<T>

The collection name (the same as db[name], also for names this interface uses).

uniqueSpace(name: string, members: SubstrioUniqueSpaceMember[]): this

Declares a unique space: the members' fields (each a field, or dotted path, of a collection; a collection that does not exist is made) share one namespace, so no value may appear in two places of the space, two documents of one member or one document of each of two members (a document holding a value in two members of its own collection is allowed). A write that breaks it throws a UniqueError (collection, field, value) on insert, update, patch and batch; writers of one space are ordered, so of concurrent writes of one value exactly one passes. Each member becomes a unique field of its collection and is dropped with the space. Idempotent for the same members. Declaring over a value already held in two places throws a UniqueError naming the collection and declares nothing; a compound member, or a space already declared with other members, is a TypeError. A collection that is a member is not dropped until the space is. Answers db.

dropUniqueSpace(name: string): boolean

Drops a unique space (its fields stay plain unique fields of their collections): true when it was declared.

kv(name: string): SubstrioKv

A key-value store; values are bytes, one per character (see SubstrioKv).

secret(collection: string, id: string, field: string): string | null

The plain value of a secret field (a rule of type "secret") of one document: id is its _id. No other read returns a secret field: documents, queries, exports and events leave it out, and no HTTP, MCP or CLI route reads it. Every call is written to the audit trail (secret.field.read, naming the collection, field and document). Throws when the field is not a secret field, the document is missing, or the read cannot be recorded; null when the document holds no value for the field.

sql(text: string, params?: SubstrioSqlParam[]): SubstrioSqlRow[] | SubstrioSqlResult

Runs one SQL statement. A SELECT answers rows; anything else answers { changes, lastInsertId }. Integers past 2^53 come back as BigInt; DECIMAL, DATE, TIME and DATETIME as their SQL text; BLOB as Uint8Array. A TEXT or BLOB column holds 65535 bytes and longer values are cut: declare MEDIUMTEXT or LONGTEXT for more. Transaction text (BEGIN, COMMIT, ROLLBACK, SAVEPOINT) is refused: use db.transaction.

prepare(text: string): SubstrioStatement

A statement to run many times: all(...args) or first(...args).

arg(index: number): SubstrioChainArg

The run's indexth argument, in a chain kept and run again: notes.eq("n", db.arg(0)).count(5).

once<R>(key: string, options: { keep: number }, fn: () => R): { reply: R; replayed: boolean }

Runs fn once per key: a later call with the same key (within keep ms) answers the stored reply without running fn. fn throws: the key is released and the error rethrown. The reply must be JSON.

limit: { (key: string | number | Uint8Array, max: number, windowMs: number, options?: SubstrioLimitOptions): SubstrioLimitResult; peek(key: string | number | Uint8Array, max: number, windowMs: number, options?: SubstrioLimitOptions): SubstrioLimitResult; refund(key: string | number | Uint8Array, max: number, windowMs: number, options?: SubstrioLimitOptions): SubstrioLimitResult; }

A rate limit by key: counts cost (1) against key when it fits under max per window of windowMs, atomically (a fixed window; sliding: true weighs the previous window). Answers { allowed, remaining, resetAt }. A key is a string of characters up to U+00FF, a number or bytes, as a kv key. Refused inside db.transaction (peek counts nothing and is allowed). db.limit.peek counts nothing; db.limit.refund gives cost back, never below zero.

graph(label: string): SubstrioGraph

A graph of edges between node ids.

graphLabels(): string[]

The labels of the app's graphs.

transaction<R>(fn: (tx: SubstrioTransaction) => R): R

Runs fn(tx) as one SQL transaction and answers fn's return value. The engine's writer gate is held until it returns, so other callers wait behind it. A throw rolls back and rethrows the same error. fn must be synchronous: a returned promise rolls back and throws a TypeError. Inside a transaction tx.sql, tx.prepare and the collection writes (tx.<collection>, the same object as db.<collection>) commit with it and are taken back by a rollback; a kv or graph write throws "kv and graph writes are not transactional yet" (reads are fine). A nested db.transaction is a savepoint that rolls back alone. A call killed at its CPU limit inside the callback is rolled back too.

permission(name: string): number

The declared permission name's catalog bit (what requirePermission, holdsPermission and requirePersonOrPermission take as permission). Throws a TypeError for a name the tenant never declared.

requirePermission(permission: number, auth: SubstrioPermissionAuth, organization: string): void

Throws unless auth (typically request.auth) holds permission (db.permission(name)'s bit) in organization (a document id, e.g. event.host): a CredentialRequired refusal (401) for nobody signed in, a PermissionDenied refusal (403) naming the permission otherwise. These refusals are not thrown Errors: they are caught by the route layer and answered directly, so a handler never needs to catch them itself. A wrong or missing permission throws a TypeError.

holdsPermission(permission: number | bigint, auth: SubstrioPermissionAuth, organization: string): boolean

requirePermission's decision as a boolean, never a refusal: true when auth holds permission in organization (nobody signed in or not held is false). permission may instead be a BigInt mask of such bits (as heldPermissions answers them): true only when every bit is held. A wrong or missing argument still throws a TypeError.

heldPermissions(auth: SubstrioPermissionAuth, organization: string): bigint

Every permission auth holds in organization, as one BigInt mask (bit n is db.permission(name)'s answer n); 0n for nobody signed in, a value naming no person or organization, or no live membership. For reporting what a caller may do, never for deciding access (requirePermission/holdsPermission decide).

requirePersonOrPermission(person: number, permission: number, auth: SubstrioPermissionAuth, organization: string): void

requirePermission(permission, auth, organization), except person (request.auth.user_id's own number; 0 names no one) passes with no permission seek when it is the caller's own id.

requireVerified(auth: SubstrioPermissionAuth): void

The verified route gate as a call: passes a caller whose session carries a verified email, or an app key. Throws a CredentialRequired refusal (401) for nobody signed in, an EmailVerificationRequired refusal (403) otherwise (neither is a thrown Error: see requirePermission).

userIdOf(person: SubstrioDoc | SubstrioSqlRow): number

The platform user id (the number request.auth.user_id holds) of a person record or row of _users. Throws a TypeError for anything that is not a person.

userIdsOf(rows: unknown): Uint32Array

userIdOf of every person of rows (a chain's or query's answer over _users, whole rows, selected rows or lazy records), as a Uint32Array in the rows' order. Throws a TypeError for rows that are not an answer over _users.

readonly ledger: SubstrioLedger

The double-entry ledger (LEDGER L3).

[collection: string]: any

SubstrioTransaction interface

interface SubstrioTransaction
sql(text: string, params?: SubstrioSqlParam[]): SubstrioSqlRow[] | SubstrioSqlResult
prepare(text: string): SubstrioStatement
collection(name: string): any

db.collection(name): the same collection object.

[collection: string]: any

tx.<name> is db.<name>.

db global

declare const db: SubstrioDb;

SubstrioStatement interface

interface SubstrioStatement
all(...args: SubstrioSqlParam[]): SubstrioSqlRow[]

Every row.

first(...args: SubstrioSqlParam[]): SubstrioSqlRow | null

The first row, or null.

SubstrioDoc type

type SubstrioDoc = Record<string, unknown>;

SubstrioRuntimeCollection interface

interface SubstrioRuntimeCollection<T = SubstrioDoc> extends SubstrioCollection<T>

Runtime-only SQL/cursor hooks; embedded document contracts are injected from the SDK.

query(text: string, ...args: SubstrioSqlParam[]): SubstrioSqlRow[]
prepare(text: string): SubstrioStatement
cursor(): SubstrioCursor
json(...args: SubstrioChainArgument[]): SubstrioJsonBody

The JSON array of exactly the rows all(...args) answers, in the same order, as one SubstrioJsonBody: no JavaScript object is built per row. Not yet for a chain with select, a join, with, whereHas, whereNone, groupBy or a compound (union, intersect, except, distinct): throws naming the first one it finds.

jsonFirst(...args: SubstrioChainArgument[]): SubstrioJsonBody

The one object first(...args) answers, or the four bytes null, as a SubstrioJsonBody. Same restrictions as json().

Collections and chains

SubstrioDocumentId type

type SubstrioDocumentId = string | number | bigint;

Synchronous embedded collection contracts, separate from the HTTP client.

SubstrioFilterValue type

type SubstrioFilterValue = null | boolean | number | string | Date;

A value a filter member compares to: a string, number, boolean, Date or null (null names the documents without a value).

SubstrioBinaryValue type

type SubstrioBinaryValue = ArrayBuffer | ArrayBufferView;

A binary operand: an ArrayBuffer or any ArrayBufferView (a Uint8Array, another typed array, a DataView), compared by the bytes it views.

SubstrioBinaryWrapper interface

interface SubstrioBinaryWrapper

{$binary: base64}: binary written as its base64 text, the bytes a document stores for it.

$binary: string

SubstrioFilterEquality type

type SubstrioFilterEquality = SubstrioFilterValue | SubstrioBinaryValue | SubstrioBinaryWrapper;

What an equality compares to: a SubstrioFilterValue, or binary. A binary equals a stored binary of the same bytes only (never a string of those bytes), and an array holding one by any element. Only $eq, $ne, $in, $nin and a field's own value take it; an ordering or a text operator throws.

SubstrioElementOperators interface

interface SubstrioElementOperators

The operators of an element condition ($elemMatch): the ones a chain's elemMatch takes.

$gt?: number | string | Date
$gte?: number | string | Date
$lt?: number | string | Date
$lte?: number | string | Date
$exists?: boolean
$startsWith?: string
$endsWith?: string
$contains?: string
$icontains?: string

SubstrioFieldOperators interface

interface SubstrioFieldOperators

The operators of one field of a filter. $not takes a non-empty object of the operators before it (every document they do not select, one without the field included); $regex is a pattern string and $options "" or "i"; $all, $size and $elemMatch read an array held by a top-level field. null is the operand of $eq, $ne, $in and $nin only; _id takes $eq, $ne, $in and $nin over ids.

$gt?: number | string | Date
$gte?: number | string | Date
$lt?: number | string | Date
$lte?: number | string | Date
$exists?: boolean
$startsWith?: string
$regex?: string
$options?: '' | 'i'
$all?: (string | number | boolean)[]

The array field holds every one of the values (containsAll).

$size?: number

The array field has exactly this many elements (size).

$elemMatch?: Record<string, SubstrioFilterValue | SubstrioElementOperators>

An element satisfies every member: a value is an equality, an object takes SubstrioElementOperators (elemMatch).

SubstrioFilterObject interface

interface SubstrioFilterObject

A filter object. A member is a field (or a dotted path) holding a value (an equality) or an object of operators; $and, $or and $nor take non-empty arrays of filters. Every filter is a chain's plan: a filter the chain has no plan for throws a TypeError that says why (undefined, a BigInt, an integer from 2^53, a RegExp object, an unknown operator, $text, $fuzzy, $search, $knn, $sparseKnn, $near, $geoWithin, $box, $sphere and $whereJS). null or undefined means every document; any other non-object throws.

SubstrioFilter type

type SubstrioFilter = SubstrioFilterObject | null | undefined;

SubstrioWriteOptions interface

interface SubstrioWriteOptions

Every condition must hold on the stored row; otherwise the write returns null. {_version: v} is compare-and-set.

if?: Record<string, unknown>

SubstrioUpdate type

type SubstrioUpdate = Record<string, unknown> & {
  $set?: Record<string, unknown>;
  $unset?: Record<string, unknown>;
  $inc?: Record<string, number | bigint>;
  $mul?: Record<string, number | bigint>;
  $min?: Record<string, number | bigint>;
  $max?: Record<string, number | bigint>;
  $push?: Record<string, unknown>;
  $addToSet?: Record<string, unknown>;
  $pull?: Record<string, unknown>;
  $pullAll?: Record<string, unknown[]>;
  $pop?: Record<string, 1 | -1>;
  $rename?: Record<string, string>;
  $currentDate?: Record<string, true>;
  $setWhere?: Record<string, { where: Record<string, unknown>; set: Record<string, unknown> }>;
};

Plain fields are set, or use operators; mixing the two forms throws. $inc/$mul/$min/$max require finite numbers or BigInts inside int64, and a nonnumeric stored field throws without changing the row. Integer overflow throws RangeError. $push accepts a value or {$each, $position, $slice}; $pull accepts a value, scalar condition, or fields of an object element. $setWhere takes {arrayField: {where: {field: value}, set: {field: value}}}. A dotted key in $set, $unset, $inc, $mul, $min and $max ("address.city") writes the member of a document, creating the documents of a missing step; a step that is a scalar, null or an array throws and writes nothing. The array operators, $rename and $setWhere do not take a dotted key. $setWhere rebuilds each matching element (a document) with only the fields of set written (a dotted name in set writes a path); every other member of every element keeps its stored type and bytes. Values store as JSON writes them: -0 stores 0, NaN and the infinities null, a Date its ISO text, a hole of a sparse array null, undefined is no member of an object; the same at the top level, nested and in an array. An ArrayBuffer and every ArrayBufferView (a Uint8Array, any other typed array, a DataView) store as binary, the bytes they view in native byte order, and a stored binary value reads back as a Uint8Array (in a row, a pluck, a join, a group key or a distinct value); the same at the top level, nested and in an array. {$binary: base64} stores the same binary.

SubstrioFindOptions interface

interface SubstrioFindOptions
sort?: Record<string, 1 | -1> | Array<string | [string, (1 | -1)?]>

Keys in order, with numeric directions; a bare name in the array is ascending.

limit?: number
skip?: number
select?: string[]

Projection answers plain objects of these fields, rather than records.

after?: string | null

Naming either token (even null/undefined) requests a page; limit is required and skip must be zero.

before?: string | null

SubstrioFindPage interface

interface SubstrioFindPage<Rows>

Find pages never carry total. Rows retain the unprojected/projected result form.

rows: Rows
next: string | null
prev: string | null

SubstrioUnpagedFindOptions type

type SubstrioUnpagedFindOptions = Omit<SubstrioFindOptions, 'after' | 'before'> & { after?: never; before?: never };

SubstrioFindCursorOptions type

type SubstrioFindCursorOptions = Omit<SubstrioFindOptions, 'limit' | 'skip'> & { limit: number; skip?: 0 } &
  ({ after: string | null | undefined } | { before: string | null | undefined });

Two nonempty tokens, multiple sort keys, or a filter requiring fallback are refused at runtime.

SubstrioUpsertDocument type

type SubstrioUpsertDocument<T> = Partial<T> & { $setOnInsert?: Record<string, unknown> };

$setOnInsert applies only when the upsert inserts; ignored on a match.

SubstrioIdentifiedDocument type

type SubstrioIdentifiedDocument<T> = SubstrioUpsertDocument<T> & { _id: SubstrioDocumentId };

SubstrioRecordMethods interface

interface SubstrioRecordMethods extends SubstrioRecordLinks
save(): boolean

Writes changes and returns true. A detached row inserts and gains _id and _version. A stored row saves against its version; a concurrent change throws ConflictError and overwrites nothing. Reload, assign and save again.

has(field: string): boolean
set(field: string, value: unknown): this
merge(fields: Record<string, unknown>): this

Assigns own fields in memory; save() writes them.

inc(field: string, delta?: number | bigint): this

Exact integer addition (BigInt beyond 2^53); TypeError for nonnumbers, RangeError beyond int64.

keys(): string[]
typeOf(field: string): string | undefined

The stored type of a field ("string", "number", "boolean", "date", "null", "object", "array"); undefined when absent.

push(field: string, value: unknown): this
pull(field: string, value: unknown): this
dirty(): boolean

Whether this row has unsaved changes.

unset(field: string): this
delete(): boolean

Deletes the stored document.

reload(): this

Re-reads the stored row and drops unsaved changes.

revert(): this

Alias of reload.

toggle(field: string): this
toJSON(): Record<string, unknown>

JSON.stringify gives the stored document.

SubstrioRecord type

type SubstrioRecord<T = Record<string, unknown>> = Omit<T, '_id'> & SubstrioRecordMethods & { _id: string };

SubstrioConstructedRecord type

type SubstrioConstructedRecord<T = Record<string, unknown>, Fields extends object = {}> =
  Omit<Partial<T>, '_id'> & Fields & SubstrioRecordMethods & { _id?: unknown };

Constructing copies fields without storing them. No generated id is present yet; an input's own _id is copied as supplied. save() returns boolean and does not statically narrow this object's id after the call.

SubstrioQueryResult interface

interface SubstrioQueryResult<T = Record<string, unknown>> extends Omit<Array<SubstrioRecord<T>>, 'reduce'>

An unprojected result. Array map/filter produce ordinary arrays.

sort(compare?: (a: SubstrioRecord<T>, b: SubstrioRecord<T>) => number): this
toArray(): SubstrioRecord<T>[]
toJSON(): Record<string, unknown>[]
project(fields: string[]): Record<string, unknown>[]
first(): SubstrioRecord<T> | undefined
last(): SubstrioRecord<T> | undefined
ids(): string[]
pluck(field: string): unknown[]
reduce<U>(fn: (acc: U | undefined, record: SubstrioRecord<T>, index: number, result: this) => U): U | undefined

Without an initial value, acc starts undefined and every row is visited; an empty result returns undefined.

reduce<U>(fn: (acc: U, record: SubstrioRecord<T>, index: number, result: this) => U, initial: U): U
saveAll(): number

Saves rows with unsaved changes; reports the first conflict while other current rows may still save.

deleteAll(): number

Deletes each row's stored document; returns the number removed.

setAll(field: string, value: unknown): number
incAll(field: string, delta?: number | bigint): number

SubstrioCollection interface

interface SubstrioCollection<T = Record<string, unknown>> extends Omit<SubstrioChainStart<T>, 'all'>, SubstrioCollectionDeclarations

Documents are live records read in place. Dates store as ISO strings, undefined fields are dropped and BigInts inside int64 round trip.

new <Fields extends Partial<T> & Record<string, unknown>>(fields: Fields): SubstrioConstructedRecord<T, Fields>

Detached row: copies own fields; nothing is stored until save().

new (fields?: Partial<T>): SubstrioConstructedRecord<T>
<Fields extends Partial<T> & Record<string, unknown>>(fields: Fields): SubstrioConstructedRecord<T, Fields>

Same as new.

(fields?: Partial<T>): SubstrioConstructedRecord<T>
find(filter: SubstrioFilter, options: SubstrioFindOptions & { limit: 0 }): SubstrioQueryResult<T>

Zero limit exits before projection and paging, answering an empty record result.

find(filter: SubstrioFilter, options: SubstrioFindCursorOptions & { select: string[] }): SubstrioFindPage<Partial<T>[]> | SubstrioQueryResult<T>

A page, or the empty record result when limit is zero. No total is returned.

find(filter: SubstrioFilter, options: SubstrioFindCursorOptions & { select?: undefined }): SubstrioFindPage<SubstrioQueryResult<T>> | SubstrioQueryResult<T>
find(filter: SubstrioFilter, options: SubstrioUnpagedFindOptions & { select: string[]; limit?: undefined }): Partial<T>[]

Without limit or cursor, selection answers plain projected objects.

find(filter: SubstrioFilter, options: SubstrioUnpagedFindOptions & { select: string[] }): Partial<T>[] | SubstrioQueryResult<T>
find(filter?: SubstrioFilter, options?: SubstrioUnpagedFindOptions & { select?: undefined }): SubstrioQueryResult<T>
find(filter: SubstrioFilter, options: SubstrioFindOptions): SubstrioFindPage<SubstrioQueryResult<T> | Partial<T>[]> | SubstrioQueryResult<T> | Partial<T>[]

Optional token/selection properties can select either the paged or unpaged result form.

findOne(filter?: SubstrioFilter): SubstrioRecord<T> | null
insert(document: Partial<T>): { _id: string }

Inserts and returns only the id envelope. The collection assigns _id: a document that names one is refused (a TypeError), use put to choose an id. _createdAt, _updatedAt and _version are the system's and ignored.

add(document: Partial<T>): { _id: string }

Alias of insert.

create(document: Partial<T>): SubstrioRecord<T>

Inserts and returns the stored row; refuses a document that names _id, as insert does.

get(id: SubstrioDocumentId): SubstrioRecord<T> | null

A record, or null if no document has the id.

findById(id: SubstrioDocumentId): SubstrioRecord<T> | null

Alias of get.

live(id: SubstrioDocumentId | number | bigint): (T & { _id: string }) | null

The document as a live row: a field reads as on a plain row, and every assignment to a field is written at once (no save()). Null if no document has the id. Any id: a synced collection's wide time-ordered ids too. A held row is a snapshot whose writes are checked: after another writer changed the document, an assignment throws an Error with code 0x4012 (ROW_STALE), writes nothing, and the row then holds the document as it is (read the field again and retry). No update is lost.

[id: number]: (T & { _id: string }) | undefined

live(id) for an integer id from 1 to 2^32 - 2 (an array index): db.users[id] reads, id in db.users asks, db.users[id] = doc replaces a stored document (a missing id throws a TypeError, nothing written) and delete db.users[id] deletes. A larger id (a synced collection's) reads undefined here: use live(id).

[Symbol.iterator](): Iterator<T & { _id: string }>

for (const doc of db.users): every document as a live row, in id order, read 64 at a time.

readonly Model: new (doc?: Partial<T>) => T & { _id: string }

The base of a class on the collection's documents: class User extends db.users.Model { ... }. new User(doc) inserts once and answers the live row (an assignment after super(doc) is a write). The class's statics: live(id), use(Class), every chain start (User.where(...).all() answers User instances) and iteration.

use<C extends new (...args: any[]) => any>(klass: C): C

Binds klass (a class that extends Model): every live row of the collection is then an instance of it.

set(id: SubstrioDocumentId, fields: Partial<T>): SubstrioRecord<T> | null

Sets plain fields by id.

update(id: SubstrioDocumentId, ops: SubstrioUpdate, options?: SubstrioWriteOptions): SubstrioRecord<T> | null

Row after the write; null if absent or if the guard fails.

insertMany(documents: Partial<T>[], options: { onConflict?: 'error' | 'ignore'; returning: true }): SubstrioRecord<T>[]

One batch, ids in order; default error rolls back every insertion on a unique conflict. onConflict:'ignore' skips refused documents and returns only inserted ids. A non-object element throws TypeError naming its index. returning:true answers the inserted rows (records, in order) instead of ids.

insertMany(documents: Partial<T>[], options?: { onConflict?: 'error' | 'ignore'; returning?: false }): string[]
upsertMany(key: '_id', documents: SubstrioIdentifiedDocument<T>[]): string[]

Stores each document under its own _id (ids in input order; the same id twice gives one row, the last values win).

upsertMany<K extends string>(key: K extends '_id' ? never : K, documents: SubstrioUpsertDocument<T>[]): string[]

Matches on every key field; ids in input order, repeated keys share a row and later values win.

upsertMany(key: string[], documents: SubstrioUpsertDocument<T>[]): string[]
updateMany(ids: SubstrioDocumentId[] | SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): number

Number written; a filter object uses updateWhere. Missing ids are not counted.

deleteMany(ids: SubstrioDocumentId[] | SubstrioFilter): number

Number removed; a filter object uses deleteWhere. Missing ids are not counted.

findOneAndUpdate(filter: SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): SubstrioRecord<T> | null

Updates the first match; returns the row after the write or null.

findOneAndDelete(filter: SubstrioFilter): (T & { _id: string }) | null

Removed document as a plain object, or null; it is no longer a record to save.

replace(id: SubstrioDocumentId, document: Partial<T>): SubstrioRecord<T> | null

Replaces fields, retaining system fields; null if absent.

delete(id: SubstrioDocumentId): boolean

True when the document existed.

count(filter?: SubstrioFilter): number
exists(filter?: SubstrioFilter): boolean

True when a document matches; without a filter, whether the collection holds any document.

upsert(filter: SubstrioFilter, document: SubstrioUpsertDocument<T>): SubstrioRecord<T>

Updates the first match with plain document fields, else inserts.

upsert(key: '_id', document: SubstrioIdentifiedDocument<T>): SubstrioRecord<T>

Updates the stored document with this _id, else inserts it under that id.

findOrCreate(filter: SubstrioFilter, document: Partial<T>): { row: SubstrioRecord<T>; created: boolean }

The first match as it is (never written), else document inserted; created says which.

updateOrCreate(filter: SubstrioFilter, update: Partial<T>): { row: SubstrioRecord<T>; created: boolean }

The first match takes update's plain fields, else the filter's equality fields with them are inserted; created says which.

put(document: SubstrioIdentifiedDocument<T>): SubstrioRecord<T>

Stores the document under the _id it holds: a stored document takes its fields, else it is inserted under that id.

putMany(documents: SubstrioIdentifiedDocument<T>[]): string[]

put for each document: ids are returned as strings in input order.

deleteWhere(filter?: SubstrioFilter, options?: { limit?: number }): number

Number removed.

updateWhere(filter: SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): number

Number written, using the same operators as update.

text(field: string): this

Declares a text field searched by match.

top(field: string, weight: string): this

Declares a prefix top-k field used by startsWith(...).top(...).

keyword(field: string, termBytes?: number): this

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 terms (each distinct word keyed by its suffixes, each cut to termBytes, default 64), with no record read for a one-word icontains. Idempotent for the same termBytes; the field's keys are built before this returns. index.drop (kind collection, keyword true) takes it away.

SubstrioChainArg interface

interface SubstrioChainArg

SubstrioChainArgument type

type SubstrioChainArgument = null | boolean | number | string | Date | SubstrioBinaryValue;

SubstrioChainValue type

type SubstrioChainValue = SubstrioChainArgument | SubstrioChainArg;

SubstrioChainBuilder type

type SubstrioChainBuilder = (q: SubstrioChain) => unknown;

SubstrioFieldValue type

type SubstrioFieldValue = null | boolean | number | string | unknown[] | Record<string, unknown>;

A value of a field as the documents hold it (a nested value is the value itself).

SubstrioJsonBody interface

interface SubstrioJsonBody

The bytes json() or jsonFirst() answers: already a JSON document, not a value to read. toString() is the one way to get the text; length is its byte length. Returned from a function (or passed to new Response(body, init)) it is sent as the response body with no further encoding; used a second time that way, the call fails instead of sending an empty body.

readonly length: number
toString(): string

SubstrioSortOptions interface

interface SubstrioSortOptions

Options of values(), distinct(field) and facets(). top: the most values per field (facets: 10 by default; values and distinct: every value; 0 is every value, and past 65536 values the call throws: page with after). order: 'value' (numbers, then strings in byte order; the default of values and distinct) or 'count' (most first, ties by value; the default of facets). missing: true adds one last { value: null, count } entry for the documents with no value. after (value order only): the answer starts past this value.

sort's options: where the documents with no value (null or missing) go. 'last' is the default.

nulls: 'first' | 'last'

SubstrioValuesOptions interface

interface SubstrioValuesOptions
top?: number
order?: 'value' | 'count'
missing?: boolean
after?: null | boolean | number | string

SubstrioValueCounts interface

interface SubstrioValueCounts

A field's values and the number of matching documents holding each; more: top cut them.

values: { value: SubstrioFieldValue; count: number }[]
more: boolean

SubstrioUniqueError interface

interface SubstrioUniqueError extends Error

What a refused write of a unique field throws (collection.unique).

name: 'UniqueError'
code: 'unique'
collection: string
field: string
value: string

The held value, as text.

SubstrioUniqueSpaceMember interface

interface SubstrioUniqueSpaceMember

One member of a unique space (db.uniqueSpace): a field, or dotted path, of a collection.

collection: string
field: string

SubstrioSchemaRule interface

interface SubstrioSchemaRule

A field rule of a collection schema (collection.schema).

field: string
type?: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'datetime' | 'any'

Leave it out, or 'any', for any type.

required?: boolean
nullable?: boolean
min?: number
max?: number
pattern?: string
enum?: string[]

SubstrioSchemaConstraint type

type SubstrioSchemaConstraint =
  | { kind: 'exactly_one' | 'at_least_one' | 'at_most_one'; fields: string[] }
  | { kind: 'required_when'; field: string; equals: string; required?: string[]; forbidden?: string[] };

A rule across fields. A field is present when it holds a value other than null. Every field named has a rule of type string, number, boolean or datetime in the same schema.

SubstrioSchemaDefinition interface

interface SubstrioSchemaDefinition

A collection's schema (collection.schema).

strict?: boolean
constraints?: SubstrioSchemaConstraint[]

SubstrioSchemaError interface

interface SubstrioSchemaError extends Error

What a write that breaks a collection's schema throws.

name: 'SchemaError'
code: 'schema'
collection: string
violations: { field: string; message: string; constraint?: SubstrioSchemaConstraint['kind']; fields?: string[] }[]

SubstrioSchemaViolatorsError interface

interface SubstrioSchemaViolatorsError extends Error

What declaring a constraint over documents that already break it throws (collection.schema).

name: 'SchemaViolatorsError'
code: 'schema_violators'
collection: string
count: number

How many stored documents break a constraint.

first: string

The id of the first of them.

SubstrioCollectionDeclarations interface

interface SubstrioCollectionDeclarations

Declarations a collection takes.

schema(): (SubstrioSchemaDefinition & { collection: string; strict: boolean }) | null

Reads the collection's schema, or null when it has none.

schema(definition: SubstrioSchemaDefinition & { existing?: 'refuse' | 'allow' }): this

Sets the collection's schema, replacing the old one (the same declaration as schema.update; returns the collection). rules and constraints (at least one rule or constraint); a rule's type is a TypeError unless it is one of the listed words. Declaring a constraint over documents that already break it throws a SubstrioSchemaViolatorsError and declares nothing, unless existing is 'allow'.

unique(field: string | string[], options?: { nulls?: 'skip' | 'count'; within?: 'collection' | 'document' | 'both' }): this

Declares the field (a top-level name or a dotted path such as 'stripe.account_id') unique (idempotent; returns the collection): no two live documents hold the same value. An insert, upsert, put, update, updateWhere, row save, claim or setAll that would make two hold one value throws a SubstrioUniqueError and writes nothing for that document. A missing field, null and an empty string never conflict; rewriting a document's own value does not either. $inc, $mul, $min, $max and $push on the field are a TypeError; $unset is allowed. Declaring over documents that already share a value throws a SubstrioUniqueError and declares nothing. A field name or a dotted path; an array of them declares a compound (the tuple of their values is unique, and the error also carries fields and values). nulls ('skip', the default, or 'count') says how a null or missing member counts: skipped (a document with no value for a member is not checked) or as one value (two documents with the same other members and a null member conflict; top-level fields only). A member holding several values (an array) is a TypeError, as is declaring the same fields with the other nulls. within ('collection', the default, 'document' or 'both') says where the value must be unique: across the collection, inside each document (an array at the path may not hold it twice; documents may share it) or both. A document or both declaration is one field with nulls skip; a compound or counted one is a TypeError, as is declaring the same fields with another within.

expire(field: string, options?: { after?: number; existing?: 'refuse' | 'allow' }): this

Declares that a document is gone after milliseconds after the date its field (a name or dotted path) holds (after 0: the date is the time it expires; returns the collection). A document without a value in the field never expires. From the moment a document expires every read hides it (find, count, joins, sorted reads, get, findById, exists); the collection's sweeper then deletes it at its time, with its keys, counters and unique values, and publishes its delete. A unique value an expired document holds is free for a new write. The field is declared indexed. Declaring over documents that hold a value that is not a date throws a TypeError naming the first, unless existing is 'allow'. Declaring again replaces the period.

dropExpiry(): this

Takes the expiry declaration away (the field's keys stay; a pending sweep is cancelled); returns the collection.

elements(field: string, members: string[]): this

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.

dropElements(field: string, members: string[]): boolean

Takes an element declaration away (its keys, id and record); true when it was declared, false when it was not.

elementIndexes(): Array<{ kind: 'collection'; collection: string; field: string; elements: string[]; state: 'none' | 'building' | 'ready'; reason: string | null }>

The collection's element declarations, as index.list lists them.

compound(lead: string[], order: string): this

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).

dropCompound(lead: string[], order: string): boolean

Takes a compound declaration away (its keys, id and record); true when it was declared, false when it was not.

compoundIndexes(): Array<{ kind: 'collection'; collection: string; compound: string[]; state: 'none' | 'building' | 'ready'; reason: string | null }>

The collection's compound declarations, as index.list lists them.

aggregate(name: string, declaration: { group?: Array<string | { field: string; step?: number }>; metrics: Record<string, 'count' | { sum: string }>; where?: string }): 'none' | 'building' | 'ready'

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.

dropAggregate(name: string): boolean

Takes an aggregate declaration away (its group records with it); true when it was declared, false when it was not.

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 }>

The collection's aggregate declarations, as index.list lists them.

hasMany(name: string, target: unknown, key: string): this

Declares a one-to-many relation: key is the field of target holding the id of a document of this collection.

hasOne(name: string, target: unknown, key: string): this

Declares a one-to-one relation: key is the field of target holding the id of a document of this collection.

belongsTo(name: string, target: unknown, key: string): this

Declares a relation to one document: key is the field of this collection holding the id of a document of target.

belongsToMany(target: unknown, options: SubstrioManyToManyOptions): this

Declares a many-to-many relation named as, held by one document per link in the through collection: key is the field of those documents holding this collection's id and otherKey the one holding the target's id (each defaults to the collection's name in the singular with Id: 'users' is 'userId'). with(as) loads the related records, withCount(as) their number as <as>Count, whereHas(as, q => ...) and whereNone(as) filter by them; record.link(as, other) and record.unlink(as, other) write the links.

scope(name: string, define: (chain: SubstrioChain) => unknown): this

Declares a named scope: Users.scope('active', c => c.eq('status', 'active')), then Users.active() starts a chain with the scope applied and chain.active() adds it to any chain of the collection (also inside and, or and not). The function is called with the chain at that moment and may add any conditions, a sort or a limit; scopes compose in any order. Declaring a name again replaces the scope. A name that is a built-in method of a collection or chain is a TypeError, and so is calling a scope on a collection that has not declared it. Returns the collection.

SubstrioManyToManyOptions interface

interface SubstrioManyToManyOptions
through: unknown
as: string
key?: string
otherKey?: string

SubstrioExplainCondition interface

interface SubstrioExplainCondition

One condition of a chain as explain() reports it: groups and elemMatch list their conditions.

op: string
field?: string
other?: string

The second field of a field comparison (gtField and the others).

SubstrioExplain interface

interface SubstrioExplain

How a chain is answered (chain.explain()), from the route's own record of what it decided. route: direct (the document route read value keys), columns (the executor over the column mirror) or relational (the SQL engine, or a chain that runs no rows). driver: what named the candidate rows: id (the ids of an _id condition), postings (an eq or in condition's posting lists), keyedSets (row sets of keyed conditions combined), sortWalk (the sort field's keys in order) or counter (a count read from the counters of one equality's keys: no row read); null when no key drove. keys: 'trigram' says a substring leaf read its candidates from the trigram keys. residual: the top-level conditions tested on each candidate's stored record (all of them when no key proves any). aggregate: where a grouped or folded answer came from, keys (the field's value keys, no record read) or rows (each matched record read once); present on the direct route of an aggregate only. declined: why the direct route did not answer, when it did not (see SubstrioExplainDecline).

route: 'direct' | 'columns' | 'relational'
aggregate?: 'keys' | 'rows'
driver: { field: string | null; kind: 'id' | 'postings' | 'keyedSets' | 'sortWalk' | 'counter'; keys?: 'trigram' } | null
sort: { field: string; direction: 'asc' | 'desc' }[]
skip: number
limit: number | null

SubstrioExplainDecline type

type SubstrioExplainDecline =
  | 'Shape' | 'Bind' | 'NoKeyedLeaf' | 'SortField' | 'SortKinds' | 'SortFilterUnkeyed' | 'SortUnbounded'
  | 'Join' | 'Aggregate' | 'MixedIds' | 'Relation';

Why the direct route declined a chain (SubstrioExplain.declined); the columns route or the engine answered it.

SubstrioFieldPath type

type SubstrioFieldPath = string;

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 document held in a field ('address.city', 'stats.views'). A top-level field named exactly that text wins; a missing or null member, or a step that is not a document, reads as a missing value; an array anywhere on the path is never traversed (a document holding one never matches, and once an array is keyed under the path the chain throws 'a nested path does not traverse arrays'). A path is for eq, neq, gt, gte, lt, lte, in, nin, between, exists, startsWith, ieq, iin, istartsWith, like, ilike, contains, icontains and endsWith, and for sort keys; select, groupBy, aggregates, joins and the array leaves take top-level names only.

SubstrioDocument type

type SubstrioDocument<T> = Omit<T, '_id'> & { _id: string };

A stored document as a chain reads it: the type's own fields and its string _id.

SubstrioRowShape type

type SubstrioRowShape = 'records' | object;

What a chain's rows are. 'records' (the default) is the collection's own documents as records (save, set and inc work on them); a select, join, group, bucket or compound answers plain rows of the shape given.

SubstrioPlainRow type

type SubstrioPlainRow = Record<string, unknown>;

A plain row: the fields of a group (its keys and aggregate aliases), a compound's columns, a path's members.

SubstrioJoinedRow type

type SubstrioJoinedRow<T> = Partial<SubstrioDocument<T>> & Record<string, unknown>;

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).

SubstrioRows type

type SubstrioRows<T = Record<string, unknown>> = SubstrioQueryResult<T> & SubstrioRecord<T>[];

The result of a record chain's all(): an Array of records with the result methods (SubstrioQueryResult).

SubstrioChainRow type

type SubstrioChainRow<T, Shape> = Shape extends 'records' ? SubstrioRecord<T> : Shape;

One row of a chain of the given shape.

SubstrioChainRows type

type SubstrioChainRows<T, Shape> = Shape extends 'records' ? SubstrioRows<T> : Shape[];

The rows of a chain of the given shape, as all() answers them.

SubstrioColumn type

type SubstrioColumn<V> = [NonNullable<V>] extends [number | boolean]
  ? Float64Array | Array<NonNullable<V> | (number extends NonNullable<V> ? bigint : never) | null>
  : Array<NonNullable<V> | null>;

A column of columns(): see SubstrioChain.columns.

SubstrioColumns type

type SubstrioColumns<S> = { [K in keyof S]-?: SubstrioColumn<S[K]> };

One column per field of the shape.

SubstrioChainColumns type

type SubstrioChainColumns<T, Shape> = Shape extends 'records'
  ? { _id: string[] } & SubstrioColumns<Omit<T, '_id'>>
  : SubstrioColumns<Shape>;

What columns() answers: a record chain, every field and _id; a select, the selected fields.

SubstrioChain interface

interface SubstrioChain<T = Record<string, unknown>, Shape extends SubstrioRowShape = 'records', Grouped extends boolean = false>
eq(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): this

A binary (an ArrayBuffer or a view such as a Uint8Array) is compared by its bytes: eq, neq, ne, in and nin take it.

ne(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): this

Alias of neq.

gt(field: SubstrioFieldPath, value: SubstrioChainValue): this
gte(field: SubstrioFieldPath, value: SubstrioChainValue): this
ge(field: SubstrioFieldPath, value: SubstrioChainValue): this

Alias of gte.

lt(field: SubstrioFieldPath, value: SubstrioChainValue): this
lte(field: SubstrioFieldPath, value: SubstrioChainValue): this
le(field: SubstrioFieldPath, value: SubstrioChainValue): this

Alias of lte.

exists(field: SubstrioFieldPath, present?: boolean): this

The condition that the field is present (present = true) or absent (false).

exists(): boolean

With no argument, a terminal: whether any document matches.

startsWith(field: SubstrioFieldPath, value: SubstrioChainValue): this
containsAll(field: string, values: SubstrioChainValue[]): this

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.

containsAny(field: string, values: SubstrioChainValue[]): this

The array field holds at least one of the values as an element. An empty list matches nothing.

size(field: string, n: number): this

The array field has exactly n elements.

sizeGt(field: string, n: number): this

The array field has more than n elements.

sizeLt(field: string, n: number): this

The array field has fewer than n elements.

elemMatch(field: string, filter: Record<string, unknown>): this

The array of objects has an element satisfying every property of the filter: a value is an equality, an object of operators ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $startsWith, $endsWith, $contains, $icontains) one condition each.

endsWith(field: SubstrioFieldPath, text: string): this

The string field ends with the text (case sensitive). endsWith, contains, icontains and like / ilike read their candidates from the field's trigram keys (built by the first such query, kept by every write), each candidate then tested on its record. A text under 3 bytes (a like pattern's longest literal) has no key: beside another keyed condition it is a per-candidate test, alone it throws (never a walk over every document).

contains(field: SubstrioFieldPath, text: string): this

The string field holds the text (case sensitive); keyed by trigrams, see endsWith.

icontains(field: SubstrioFieldPath, text: string): this

The string field holds the text, ASCII letters compared without case; keyed by trigrams, see endsWith.

ieq(field: SubstrioFieldPath, text: string): this

The string field equals the text, ASCII letters compared without case (É and é differ). A field that is not a string never matches. Answered from a folded key of the field, built when first asked.

iin(field: SubstrioFieldPath, texts: string[]): this

The string field equals one of the texts, ASCII letters compared without case (see ieq).

istartsWith(field: SubstrioFieldPath, text: string): this

The string field starts with the text, ASCII letters compared without case (see ieq).

regex(field: string, pattern: string | RegExp): this

The string field matches the pattern somewhere (a string or RegExp, ECMAScript grammar; only the i flag).

eqField(field: string, other: string): this

Field a equals field b of the same document (both hold a value; a number equals a number, a string a string).

neField(field: string, other: string): this
gtField(field: string, other: string): this
gteField(field: string, other: string): this
ltField(field: string, other: string): this
lteField(field: string, other: string): this
onDay(field: string, date: Date | number, zone?: number): this

The number (milliseconds) field falls on the day of the date, UTC or the zone given as minutes east of UTC.

inMonth(field: string, year: number, month: number, zone?: number): this

The field falls in the month (1 to 12) of the year, UTC or in the zone.

lastDays(field: string, n: number, zone?: number): this

The field falls in the last n calendar days (today and the n - 1 before it) up to now, when the chain is built.

sample(n: number, ...args: SubstrioChainArgument[]): SubstrioChainRows<T, Shape>

n distinct documents of the answer picked at random, in the answer's order (all of them when fewer match).

random(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | null

One document of the answer at random, or null when none match.

each(fn: (document: SubstrioChainRow<T, Shape>, index: number) => unknown, ...args: SubstrioChainArgument[]): number

fn(document, index) for each document of the answer in its order, stopping when fn returns false; fn may write to the collection. Returns how many documents fn was given.

chunk(n: number, fn: (documents: SubstrioChainRow<T, Shape>[], page: number) => unknown, ...args: SubstrioChainArgument[]): number

fn(documents, page) for each page of n documents of the answer, stopping when fn returns false. Returns the documents given.

explain(...args: SubstrioChainArgument[]): SubstrioExplain

How the chain is answered (runs it once).

match(field: string, text: SubstrioChainValue, options?: { mode?: 'all' | 'any' } | 'all' | 'any'): this

The documents whose text field holds every word of text (mode 'all', the default) or at least one ('any'; with no sort they come ranked by distinct words matched, ties in row order). Words are runs of letters, digits and '_', ASCII case folded. The field is declared with collection.text(field); any other field is an error.

top(count: number, weight: string): this

The count documents of greatest numeric weight, ties in row order: sets the chain's sort and limit, and leaves out documents whose weight is not a number. After startsWith(field, prefix) on a field declared with collection.top(field, weight) a prefix of 1 to 3 bytes reads count keys.

knn(field: SubstrioFieldPath, vector: number[], k: number): this

The k nearest documents to the vector, ranked nearest first beside the other conditions. The field has a vector index; the vector is a non-empty array of numbers and k a count from 1.

near(field: SubstrioFieldPath, latitude: number, longitude: number, k?: number): this

The k (default 10) documents nearest to the point (latitude, longitude), ranked nearest first. The field has a geo index.

withinBox(field: SubstrioFieldPath, low: number[], high: number[]): this

The documents whose point lies inside the box: low and high are its two corners, non-empty arrays of numbers of one length (one number per dimension). The field has a geo or vector index.

withinSphere(field: SubstrioFieldPath, center: number[], radius: number): this

The documents whose point lies within radius (above 0) of center, a non-empty array of numbers. The field has a geo or vector index.

sparseKnn(field: SubstrioFieldPath, vector: { indices: number[]; values: number[] }, k: number): this

The k documents of greatest dot product with a sparse vector (indices and values of one length, not empty), ranked best first. The field has a sparse index.

textNear(field: SubstrioFieldPath, text: string, fuzzy?: boolean | number): this

The documents whose text field holds the text's words as a phrase, or with fuzzy (true, or a nonzero number) the documents a fuzzy match of the text reaches. The field has a trigram index. Words are runs of ASCII letters and digits, without case; any other character (punctuation, space, non-ASCII) separates words, and stop words and 1-letter words are skipped on both sides. The phrase's words must stand adjacent and in order: the first ends a word, the last begins one, middle words are whole, a single word lies inside a word, and a word under 3 letters matches only an equal word. A text of only stop words matches nothing.

search(text: string, options?: { k?: number; mode?: 'vector' }): this

The collection's $search in its vector mode: the text is embedded by the collection's RAG configuration and the k (default 50) documents nearest to the embedding in the RAG vector field are kept, ranked nearest first. The hybrid mode, decay, boost_feedback and strict_model are options of the HTTP $search, not of a chain (a chain that names them throws).

and(...conditions: SubstrioChainBuilder[]): this
or(...conditions: SubstrioChainBuilder[]): this
not(...conditions: SubstrioChainBuilder[]): this
where(filter: SubstrioFilter): this

The conditions of a filter object (the operators find takes), ANDed with the chain's: where({ n: { $lt: 5 } }).

sort(field: SubstrioFieldPath, direction?: 1 | -1 | 'asc' | 'desc' | boolean, options?: SubstrioSortOptions): this

direction: 1, 'asc' or false ascending (the default); -1, 'desc' or true descending. Repeated calls (or an array of [field, direction]) add keys in order; several keys make the chain relational (SQL semantics apply). A field may be 'as.field' of a join. The documents with no value (null or missing) come last in either direction; { nulls: 'first' } puts them first (in id order), on a plain single-key sort of the collection's own field only: it is refused with several keys, a cursor page, last() or an aggregate.

sort(keys: Array<string | [string, (1 | -1 | 'asc' | 'desc' | boolean)?]>): this

Several sort keys (chained sort calls, or the array form) of the collection's own fields: the first key's order, each run of equal values ordered by the next key, a document with no value (null or missing) after those with one on every key in either direction, ties in id order. They run on the keyed executor, not SQL; a cursor page (page, pages) refuses them.

sort_by(field: string, direction?: 1 | -1 | 'asc' | 'desc' | boolean, options?: SubstrioSortOptions): this

Alias of sort.

sort_by(keys: Array<string | [string, (1 | -1 | 'asc' | 'desc' | boolean)?]>): this
group_by(field: string): SubstrioGroupedChain<T>

Groups the matching documents by a field's value: sum, avg, min, max and count then answer per group (see SubstrioGroupedChain).

limit(count: number): this
skip(count: number): this
claim(n: number, ops: Record<string, unknown>, ...args: SubstrioChainValue[]): SubstrioChainRows<T, Shape>

The queue terminal: takes up to n (an integer from 1) documents the chain selects, in its sort order, and applies ops (what update(id, ops) takes: $set, $inc, $mul, $min, $max, $unset) to each one, guarded by the chain's own conditions so two claimers never both claim a document. Resolves to the claimed rows as all() answers them, after the update, in claim order; [] when none. Refused: a join, select, groupBy, skip, limit, and a condition a guard cannot re-test (or, not, in, between, like, ...).

update(ops: Record<string, unknown>, ...args: SubstrioChainArgument[]): number

Applies ops (what updateWhere takes: update operators, or plain fields to set) to every document the chain answers; its sort, skip and limit choose the documents as they do for ids(). Returns the number written. Refused: a join, with, groupBy, distinct or compound chain. A trailing { returning: true } answers the rows written (records, in order) instead of the count.

update(ops: Record<string, unknown>, ...args: [...SubstrioChainArgument[], { returning: true }]): SubstrioRecord<T>[]
delete(...args: SubstrioChainArgument[]): number

Deletes every document the chain answers (the same documents as ids()); returns the number removed.

increment(field: string, n?: number | bigint, ...args: SubstrioChainArgument[]): number

update({ $inc: { [field]: n } }) with n = 1 by default (a finite number, or a BigInt inside int64); returns the number written.

increment(field: string, n: number | bigint, ...args: [...SubstrioChainArgument[], { returning: true }]): SubstrioRecord<T>[]

With a trailing { returning: true }: the rows written (records, in order).

decrement(field: string, n?: number | bigint, ...args: SubstrioChainArgument[]): number

update({ $inc: { [field]: -n } }) with n = 1 by default; returns the number written.

decrement(field: string, n: number | bigint, ...args: [...SubstrioChainArgument[], { returning: true }]): SubstrioRecord<T>[]

With a trailing { returning: true }: the rows written (records, in order).

firstOrFail(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape>

first(), but a chain that matches nothing throws an Error named NotFoundError.

value<K extends keyof T & string>(field: K, ...args: SubstrioChainArgument[]): T[K] | undefined

The field of the first row the chain answers; undefined when it matches nothing or the row has no such field.

value(field: SubstrioFieldPath, ...args: SubstrioChainArgument[]): unknown
missing(): boolean

The negation of exists(): true when no document matches.

page(options: { size: number; after?: string | null; before?: string | null; total?: boolean }, ...args: SubstrioChainValue[]): { rows: SubstrioChainRows<T, Shape>; next: string | null; prev: string | null; total?: number }

One page of the chain in its sort order (or id order when unsorted). next and prev are opaque cursors bound to this query: pass next as after for the following page (null on the last page), prev as before for the page ahead. A page at any depth costs what the first page costs on a keyed number sort. skip cannot be combined with a page.

pages(options: { size: number }, ...args: SubstrioChainValue[]): IterableIterator<SubstrioChainRows<T, Shape>>

Every page of the chain in turn, as an iterator of row arrays.

select<K extends keyof SubstrioDocument<T> & string>(...fields: K[] | [K[]]): SubstrioChain<T, Pick<SubstrioDocument<T>, K>>

The fields each row carries. A name with a dot that no join alias spells is a nested path: select('name', 'address.city') answers {name, address: {city}}, the member read in place from each document; a member the document lacks (a missing step, a scalar or an array on the path) is absent from the row, a stored null is null. Only a plain chain answers a path (not with join, groupBy, distinct or columns()).

select(...fields: string[] | [string[]]): SubstrioChain<T, SubstrioPlainRow>
join(collection: string, localField: string, foreignField: string, as: string): SubstrioChain<T, SubstrioJoinedRow<T>>
innerJoin(collection: string, localField: string, foreignField: string, as: string): SubstrioChain<T, SubstrioJoinedRow<T>>

SQL semantics apply: every match of the foreign collection; a document with none is dropped.

leftJoin(collection: string, localField: string, foreignField: string, as: string): SubstrioChain<T, SubstrioJoinedRow<T>>

SQL semantics apply: every match of the foreign collection; a document with none stays, its as null.

with(...relations: (string | string[] | ((q: SubstrioChain<T>) => SubstrioChain<T>))[]): this

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.

whereHas(relation: string, constraint?: (q: SubstrioChain) => SubstrioChain): this

The documents that have a related record of the relation (matching the function's chain when given); the related side is read when called.

whereHas(relation: string, op: '>=' | '>' | '=' | '<' | '<=', n: number): this

The documents whose number of related records of a hasMany or hasOne relation compares to n ('>=', '>', '=', '<', '<='; n a whole number from 0): one count lookup per candidate document, no related key set is built. 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.

whereNone(relation: string, constraint?: (q: SubstrioChain) => SubstrioChain): this

The documents that have no related record of the relation (matching the function's chain when given).

withCount(...relations: (string | string[])[]): this

Each row carries the number of related records of a hasMany relation as <name>Count, with no record read.

distinct(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioFieldValue[]

The distinct values of a field among the matching documents (values() without the counts).

distinct(): SubstrioChain<T, Shape extends 'records' ? SubstrioJoinedRow<T> : Shape, Grouped>

Each row of select() once; answers plain rows. select(fields).distinct() of the collection's own fields is the groups of those fields (no group or aggregate), read from the rows the filter yields by the aggregate stage (not a relational chain: explain().route is 'direct'); whole rows and joined fields keep SQL semantics. An integer from 2^53 up in a distinct or grouped key is returned exact, as a BigInt.

groupBy(...fields: string[] | [string[]]): SubstrioChain<T, SubstrioPlainRow, true>

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.

bucket(field: string, step: number | string, options?: { fill?: boolean }): SubstrioChain<T, SubstrioPlainRow, true>

A group key that cuts a field's numbers (or dates, in ms) into buckets of step: a number, or text such as '1h', '1d', '1w', '30m', '15s', '250ms'. The group's value is the bucket's first value (floor(v / step) * step); rows with no number are one null group. fill: true also answers the empty buckets between the first and the last. Like groupBy it takes count, sum, avg, min and max after it.

histogram(field: string, step: number | string, options?: { fill?: boolean }): SubstrioChain<T, SubstrioPlainRow, true>

bucket(field, step, options) with a count: rows are { [field]: bucket start, count }.

topPerGroup(groupField: string, n: number, sortField: string, direction?: 1 | -1 | 'asc' | 'desc', ...args: SubstrioChainArgument[]): SubstrioChainRows<T, Shape>

The first n documents of each group of groupField in sortField's order (direction 1, -1, 'asc' or 'desc'; ascending by default), the groups in value order. Each group is an ordered key walk stopped at n rows, never a sort of all the rows. Documents with no group value are in no group. The chain holds conditions only (no sort, skip, limit or select).

countDistinct(field: string, ...args: SubstrioChainArgument[]): number

How many different values the field holds among the matching documents (null and missing do not count).

having(field: string, op: '=' | '==' | '!=' | '<>' | '>' | '>=' | '<' | '<=' | 'eq' | 'ne' | 'neq' | 'gt' | 'gte' | 'ge' | 'lt' | 'lte' | 'le', value: SubstrioChainValue): this

SQL semantics apply: a condition over a group key or an aggregate alias; op is '=', '!=', '>', '>=', '<' or '<='.

like(field: SubstrioFieldPath, pattern: SubstrioChainValue): this

% any run of characters, _ any one character, a backslash makes the next character literal. Case-sensitive; a document whose field is not a string never matches. Run by the executor: abc% is startsWith, %abc endsWith, %abc% contains and abc eq (keyed); any other pattern tests each candidate, behind a startsWith of its literal 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.

ilike(field: SubstrioFieldPath, pattern: SubstrioChainValue): this

like folding ASCII letters only (as icontains does): É and é differ.

between(field: SubstrioFieldPath, low: SubstrioChainValue, high: SubstrioChainValue): this

low <= field <= high, both bounds included, run by the executor as gte(low) and lte(high) of one field; a document with a null or missing field never matches, and a null bound is refused. (SQL semantics apply to a joined field's between and to having.)

inQuery(field: SubstrioFieldPath, chain: SubstrioChain<any, any>, chainField: string): this

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.

notInQuery(field: string, chain: SubstrioChain<any, any>, chainField: string): this

SQL semantics apply: NOT IN over another chain's field.

existsQuery(chain: SubstrioChain<any, any>): this

SQL semantics apply: every document when the chain has one, none otherwise.

union(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>

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.

unionAll(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>

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.

intersect(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>

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.

except(chain: SubstrioChain<any, any>): SubstrioChain<T, SubstrioPlainRow>

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.

all(...args: SubstrioChainArgument[]): SubstrioChainRows<T, Shape>
first(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | null
json(...args: SubstrioChainArgument[]): SubstrioJsonBody

The JSON array of exactly the rows all() answers, in the same order, as one SubstrioJsonBody: no JavaScript object is built per row. Not yet for a chain with select, a join, with, whereHas, whereNone, groupBy or a compound (union, intersect, except, distinct) or window (bucket, histogram): throws naming the first one it finds.

jsonFirst(...args: SubstrioChainArgument[]): SubstrioJsonBody

The one object first() answers, or the four bytes null, as a SubstrioJsonBody. Same restrictions as json().

last(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | null

The last row in the chain's order, or null. A sorted chain reads its reversed walk (no walk of all rows); a chain with no condition, sort or window reads the end of the collection.

count(...args: SubstrioChainArgument[]): Grouped extends true ? this : number

The number of matching documents (before skip and limit). Once groupBy, bucket or histogram was called, count(alias = 'count') adds the group's row count as column alias and answers the chain (SQL semantics apply; end it with all(), first() or having()).

ids(...args: SubstrioChainArgument[]): string[]
pluck<K extends keyof T & string>(field: K, ...args: SubstrioChainArgument[]): (T[K] | undefined)[]

One field's value of every match, in the chain's order (no row is built). A nested path ('address.city') is read in place from each document: undefined for a document that lacks it (a missing step, a scalar or an array on the path), null for a stored null.

pluck(field: SubstrioFieldPath, ...args: SubstrioChainArgument[]): unknown[]
columns(...args: SubstrioChainArgument[]): SubstrioChainColumns<T, Shape>

One array per selected field (every field of the collection, and _id, when nothing is selected), in the chain's order. A column whose every cell is present and a number or boolean is a Float64Array (a boolean reads 0 or 1); any other is an Array of the cells' own values (strings, Dates, BigInts past 2^53), null where a document lacks the field. Throws a TypeError when a selected field is a nested path (a.b).

sum(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number

The sum of a field's numbers over the matching documents (sort, skip and limit do not apply); 0 when none holds a number. Integers add exactly (int64) and the result is the nearest double, so it is exact up to 2^53; past int64 the sum continues as a double and never wraps. Once groupBy, bucket or histogram was called, sum(field, alias = 'sum_<field>') adds the group's sum as column alias and answers the chain; avg, min and max do the same (aliases avg_<field>, min_<field>, max_<field>).

avg(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | null

The mean of a field's numbers, null when no matching document holds one.

min(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | null

The smallest of a field's numbers, null when there is none.

max(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | null

The largest of a field's numbers, null when there is none.

values(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioValueCounts

Each value of a field among the matching documents with the number of documents holding it, read from the field's value keys where it has them (sort, skip and limit do not apply). See SubstrioValuesOptions.

facets(fields: string | string[], options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): Record<string, SubstrioValueCounts>

values() of several fields over the chain's one filter, in one call: one property per field.

SubstrioChainStart type

type SubstrioChainStart<T = Record<string, unknown>> = Pick<SubstrioChain<T>,
  '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'
> & {
  /** Every document of the collection: the chain with no condition, run. */
  all(...args: SubstrioChainArgument[]): SubstrioRows<T>;
  /** The terminals over the ids of every document (see SubstrioChain). */
  sample(n: number, ...args: SubstrioChainArgument[]): SubstrioRows<T>;
  random(...args: SubstrioChainArgument[]): SubstrioRecord<T> | null;
  each(fn: (document: SubstrioRecord<T>, index: number) => unknown, ...args: SubstrioChainArgument[]): number;
  chunk(n: number, fn: (documents: SubstrioRecord<T>[], page: number) => unknown, ...args: SubstrioChainArgument[]): number;
  explain(...args: SubstrioChainArgument[]): SubstrioExplain;
  /** The first and the last document (the ends of the collection), or null when it is empty. */
  first(): SubstrioRecord<T> | null;
  last(): SubstrioRecord<T> | null;
  /** The first document, or a NotFoundError when the collection is empty; one field of it; whether the collection is empty. */
  firstOrFail(): SubstrioRecord<T>;
  value<K extends keyof T & string>(field: K): T[K] | undefined;
  value(field: SubstrioFieldPath): unknown;
  missing(): boolean;
  /** The ids and one field of every document (count() is the collection's own, from its counter). */
  ids(): string[];
  pluck<K extends keyof T & string>(field: K): (T[K] | undefined)[];
  pluck(field: SubstrioFieldPath): unknown[];
  /** The aggregates over every document of the collection (see SubstrioChain). */
  sum(field: string, ...args: SubstrioChainArgument[]): number;
  avg(field: string, ...args: SubstrioChainArgument[]): number | null;
  min(field: string, ...args: SubstrioChainArgument[]): number | null;
  max(field: string, ...args: SubstrioChainArgument[]): number | null;
  countDistinct(field: string, ...args: SubstrioChainArgument[]): number;
  /** The first `n` documents of each group in `sortField`'s order (see SubstrioChain). */
  topPerGroup(groupField: string, n: number, sortField: string, direction?: 1 | -1 | 'asc' | 'desc', ...args: SubstrioChainArgument[]): SubstrioRows<T>;
  /** The values, distinct values and facets over every document of the collection (see SubstrioChain). */
  values(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioValueCounts;
  distinct(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioFieldValue[];
  facets(fields: string | string[], options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): Record<string, SubstrioValueCounts>;
};

SubstrioGroupedChain type

type SubstrioGroupedChain<T = Record<string, unknown>> = {
  [K in 'eq' | 'neq' | 'ne' | 'gt' | 'gte' | 'ge' | 'lt' | 'lte' | 'le' | 'in' | 'nin' | 'exists' | 'startsWith' | 'and' | 'or' | 'not' | 'group_by']:
    (...args: Parameters<SubstrioChain<T>[K]>) => SubstrioGroupedChain<T>;
} & {
  sum(field: string, ...args: SubstrioChainArgument[]): Record<string, number>;
  avg(field: string, ...args: SubstrioChainArgument[]): Record<string, number | null>;
  min(field: string, ...args: SubstrioChainArgument[]): Record<string, number | null>;
  max(field: string, ...args: SubstrioChainArgument[]): Record<string, number | null>;
  /** The matching documents of each group. */
  count(...args: SubstrioChainArgument[]): Record<string, number>;
};

A chain after group_by(field). Its terminals return a plain object with one property per group: the key is the group value as JS spells it (a string as is, String(number), "true", "false", "null" for null or a missing field, an object's or an array's JSON), and values spelled the same (1 and "1") share a property. Integer-like keys enumerate first, as in any object; the others in first-seen order. No match gives an empty object. A group with no number in the field has sum 0 and null for avg, min and max. A Date written from script is stored as its ISO string and groups under that string. The group value "__proto__" is refused.

SubstrioKv interface

interface SubstrioKv

A key-value store of bytes, one per character: a value is a string of characters up to U+00FF, a number, a boolean, a BigInt or binary data, and get answers a string of those bytes (put("n", 5) then get("n") is "5"). Text with other characters throws: store new TextEncoder().encode(text) and decode with new TextDecoder().decode(Uint8Array.from(s, c => c.charCodeAt(0))).

get(key: string): string | null
put(key: string, value: string | number | boolean | bigint | Uint8Array | ArrayBuffer): boolean
delete(key: string): boolean
peek(key: string): number

The stored length in bytes, or -1. Allocates nothing.

ref(key: string): number

The stored length in bytes, or -1; the value is copied into the store's shared buffer kv._v (read it right after the call: the next ref overwrites it, and it is replaced by a larger one when a value outgrows it). Allocates nothing once the buffer has grown.

has(key: string): boolean

Whether the key holds a value. A lookup only: the value is not read, nothing is allocated.

getBytes(key: string): Uint8Array | null

A fresh Uint8Array copy of the value, or null. One allocation; ref is the zero-allocation form.

increment(key: string, delta?: number): number | undefined

Atomically adds delta (an integer, default 1) to the 8-byte integer under key (an absent key starts at 0) and answers the new value; undefined when the stored value is not 8 bytes.

getInt(key: string): number | undefined

undefined when absent or not 8 bytes; throws a RangeError for a stored integer outside the safe range (never a BigInt).

getF64(key: string): number | undefined

undefined when absent or not 8 bytes. Reads the same 8 bytes as getInt: read a value with the kind that wrote it.

putInt(key: string, value: number): boolean

Only an integral number within the safe range; anything else (a BigInt too) throws a TypeError.

putF64(key: string, value: number): boolean

Any number; a non-number throws a TypeError.

cursor(): SubstrioCursor

SubstrioCursor interface

interface SubstrioCursor

An ordered cursor over a tree's keys.

readonly valid: boolean

Whether the cursor stands on an entry: false before its first seek, past either end and once closed.

seekFirst(): boolean
seekLast(): boolean
seekPrefix(prefix: string): boolean
seekGE(key: string): boolean
seekLE(key: string): boolean
seekLT(key: string): boolean
seekPrefixLast(prefix: string): boolean
next(): boolean
prev(): boolean
key(): string | null
value(): string | null
value(field: SubstrioFieldPath): unknown

A collection cursor only: the one field of the document the cursor stands on (a nested a.b path reads down, as a chain's own value/pluck do), with no document built. undefined where the record lacks the field. Cost: 0 descents when nothing waited since the cursor's last step or seek, 1 by the cursor's kept key after a wait. A key-value cursor's value has no fields and throws a TypeError.

scanCount(prefix?: string): number
close(): void

SubstrioGraph interface

interface SubstrioGraph

A directed graph; see the engine's graph block (jsc_db_direct.cpp) for the full analytics list.

addEdge(from: number, to: number): boolean
removeEdge(from: number, to: number): boolean
hasEdge(from: number, to: number): boolean
neighbors(node: number, direction?: string): number[]
degree(node: number): number
stats(): Record<string, unknown>
[analytic: string]: any

SubstrioLedgerIndex type

type SubstrioLedgerIndex = number;

A tenant's declared ledger kind, category, type or entries-by index (what ledger.kind/category/type.<NAME>/status.<NAME>/by.<NAME> answer).

SubstrioLedgerPostEntry interface

interface SubstrioLedgerPostEntry
key?: string

An idempotency key: posting the same key twice answers EXISTING, not a second entry.

kind: SubstrioLedgerIndex

ledger.kind(name)'s answer.

party: string
currency: string
amount: number

Minor units; an integer exact in a safe int64, never rounded.

type: SubstrioLedgerIndex

ledger.type.CREDIT, DEBIT, TRANSFER_IN or TRANSFER_OUT (REVERSAL is reached only through ledger.reverse).

status?: SubstrioLedgerIndex

ledger.status.PENDING (default) or CLEARED (VOIDED is reached only through ledger.void).

category?: SubstrioLedgerIndex

ledger.category(name)'s answer.

reference?: string
scope?: string
org?: string
counterparty?: string
clear_at?: number

Epoch ms; 0 takes a prior clear time away (ledger.clearAt's own rule).

external_ref?: string
description?: string
data?: Record<string, unknown>

A plain object, serialized once into the stored entry; never an array.

SubstrioLedgerPostResult interface

interface SubstrioLedgerPostResult
outcome: 'POSTED' | 'EXISTING'
ids: string[]

SubstrioLedgerBalance interface

interface SubstrioLedgerBalance
pending: number
cleared: number
pending_rows: number
cleared_rows: number

SubstrioLedgerTopBalance interface

interface SubstrioLedgerTopBalance
account_kind: SubstrioLedgerIndex
party: string
amount: number

SubstrioLedgerAccount interface

interface SubstrioLedgerAccount
party: string
amount: number

SubstrioLedgerTotalsRow interface

interface SubstrioLedgerTotalsRow
account_kind: SubstrioLedgerIndex
category?: SubstrioLedgerIndex

Present only when the group's own entries named one.

amount: number
rows: number

SubstrioLedgerAccountTotalsRow interface

interface SubstrioLedgerAccountTotalsRow
party: string
first?: boolean

True on the groups of entries that hold their (kind, party, reference)'s first credit.

amount: number
rows: number

SubstrioLedgerClearDue interface

interface SubstrioLedgerClearDue
cleared: number
more: boolean

SubstrioLedgerEntriesPage interface

interface SubstrioLedgerEntriesPage
entries: SubstrioDoc[]
next_before: number
more: boolean

SubstrioLedger interface

interface SubstrioLedger

The double-entry ledger (LEDGER L3), reached as db.ledger. Every call is one native decision over tenant/substrio_ledger.h: no HTTP, no JSON text between the call and it. A refusal (a validated call the store itself declined: an unknown kind/category index, an invalid id, an out-of-range limit, declaring totals twice a different way) throws an Error named LedgerRefused with an error code naming it, and post()'s own refusal also carries the refusing entry's index. A write that the store accepted but could not commit throws an Error named LedgerWriteFailed.

kind(name: string): SubstrioLedgerIndex

The declared kind name's catalog index, for kind elsewhere. Throws a TypeError for an undeclared name.

category(name: string): SubstrioLedgerIndex

The declared category name's catalog index, for category elsewhere. Throws a TypeError for an undeclared name.

post(entries: SubstrioLedgerPostEntry[]): SubstrioLedgerPostResult

Posts 1 to 1000 entries as one call; key makes a repeat post idempotent (EXISTING, not a second entry). Throws LedgerRefused naming the first invalid entry (with its index), or LedgerWriteFailed.

balance(kind: SubstrioLedgerIndex, party: string, currency: string, org?: string): SubstrioLedgerBalance

The account's pending/cleared sums and row counts in currency, over every organization or, given org, one.

topBalances(currency: string, n: number): SubstrioLedgerTopBalance[]

The n (1 to 1000) largest account balances in currency, largest first.

accounts(kind: SubstrioLedgerIndex, currency: string): SubstrioLedgerAccount[]

Every account of kind in currency (pending plus cleared sum), parties in the balance aggregate's own order.

totals(value: string | null, currency: string, by?: SubstrioLedgerIndex): SubstrioLedgerTotalsRow[]

Scope, reference or ledger-wide totals (grouped by by, default ledger.by.SCOPE). Throws LedgerRefused TOTALS_NOT_DECLARED naming declareTotals() when the tenant never declared that grouping.

declareTotals(by?: SubstrioLedgerIndex): 'DECLARED' | 'EXISTING'

Declares a totals grouping (default ledger.by.SCOPE; also REFERENCE, ALL or PARTY) so totals()/accountTotals() can answer it. Idempotent: 'DECLARED' the first time, 'EXISTING' after.

accountTotals(kind: SubstrioLedgerIndex, currency: string): SubstrioLedgerAccountTotalsRow[]

Per-party totals of kind in currency (PLAT-LEDGER-ACCOUNT-SPLIT). Throws LedgerRefused TOTALS_NOT_DECLARED naming declareTotals(ledger.by.PARTY) when never declared.

clear(id: string): 'CHANGED' | 'UNCHANGED'

Moves the entry to CLEARED, or VOIDED. 'CHANGED' or 'UNCHANGED'; throws LedgerRefused for an invalid id or wrong status, or LedgerWriteFailed.

void(id: string): 'CHANGED' | 'UNCHANGED'
clearAt(id: string, ms: number): 'CHANGED' | 'UNCHANGED'

Sets the entry's own clear time (ms > 0), or takes it away (ms 0). Same refusals as clear().

clearDue(limit?: number): SubstrioLedgerClearDue

Clears every pending entry whose own clear_at is due (at most limit, default 1000, 1 to 10000). more is true when entries past limit are still due.

annotate(id: string, notes: string | null | undefined, tags: string[] | null | undefined, by: number): 'CHANGED' | 'NOT_FOUND'

Replaces notes and/or tags (an array of strings; absent/null/undefined each leaves it unchanged) recorded by (a platform user id). 'CHANGED' or 'NOT_FOUND'; throws LedgerWriteFailed.

reverse(id: string): SubstrioLedgerPostResult

Posts the entry's reversal (ledger.type.REVERSAL); idempotent like post(). Throws LedgerRefused for an invalid id or the store's own refusal.

get(id: string): SubstrioDoc | null

The plain entry object, or null (no such entry).

idOf(key: string): string

The id text key names, exactly as post() derives a non-reversal entry's own id (no read). Throws a TypeError for an empty or missing key.

entries(by: SubstrioLedgerIndex, value: string | null, before?: number, limit?: number, org?: string): SubstrioLedgerEntriesPage

A page of entries by by (ledger.by.PARTY/REFERENCE/SCOPE/ORG; ALL walks the whole ledger, newest first, and value must then be null/undefined), newest first. before: 0 or absent starts from the newest. limit: 1 to 1000, default 50; out of range throws LedgerRefused LIMIT_INVALID. org narrows a ledger.by.PARTY walk to that party's entries at one organization; any other by, or a non-string, throws a TypeError.

readonly type: { readonly CREDIT: SubstrioLedgerIndex; readonly DEBIT: SubstrioLedgerIndex; readonly TRANSFER_IN: SubstrioLedgerIndex; readonly TRANSFER_OUT: SubstrioLedgerIndex; readonly REVERSAL: SubstrioLedgerIndex }

The postable entry types: CREDIT, DEBIT, TRANSFER_IN, TRANSFER_OUT, REVERSAL (post() only takes the first four).

readonly status: { readonly PENDING: SubstrioLedgerIndex; readonly CLEARED: SubstrioLedgerIndex; readonly VOIDED: SubstrioLedgerIndex }

The entry statuses: PENDING, CLEARED, VOIDED (post() only takes the first two).

readonly by: { readonly PARTY: SubstrioLedgerIndex; readonly REFERENCE: SubstrioLedgerIndex; readonly SCOPE: SubstrioLedgerIndex; readonly ORG: SubstrioLedgerIndex; readonly ALL: SubstrioLedgerIndex }

entries()/totals()/declareTotals()'s own grouping: PARTY, REFERENCE, SCOPE, ORG, ALL.

substrio

SubstrioFileMeta interface

interface SubstrioFileMeta
path: string
size: number
hash: string
type: string

SubstrioFileStat interface

interface SubstrioFileStat extends SubstrioFileMeta
cacheControl: string
public: boolean
createdAt: number
updatedAt: number

SubstrioFilePutOptions interface

interface SubstrioFilePutOptions
type?: string
cacheControl?: string
public?: boolean

SubstrioFileResponseInit interface

interface SubstrioFileResponseInit
status?: number
range?: string
headers?: HeadersInit

SubstrioFiles interface

interface SubstrioFiles

The tenant's stored files (PLAT-FILES-JS), the same store the /v1/_files routes use: no HTTP, no base64, no JSON between a call and it. path is a FilePath (no empty or '.'/'..' segment, no leading/trailing '/', at most 65535 bytes, valid UTF-8, no control byte): any other path string throws the Error /v1/_files answers for it (KEY_TOO_LARGE or INVALID_ARGUMENT), its message naming FILE_PATH_INVALID; a path that is not a string, a TypeError. A write the store declined (quota, most likely) throws an Error named after the refusal /v1/_files answers for it.

put(path: string, data: ArrayBuffer | ArrayBufferView | string | Blob, options?: SubstrioFilePutOptions): SubstrioFileMeta

Stores data at path, replacing it if it exists.

stat(path: string): SubstrioFileStat | null

The full metadata of the stored file, or null (no such path).

get(path: string): Uint8Array | null

The stored bytes, or null (no such path).

remove(path: string): boolean

Removes the file: true, or false (no such path).

response(path: string, init?: SubstrioFileResponseInit): Response | null

The file as a Response (its body streamed with no native copy past the store's own read), or null (no such path). init.range is one HTTP Range header value ("bytes=A-B"/"bytes=A-"/"bytes=-N"); unsatisfiable answers 416 with no body.

slice(path: string, offset: number, length: number, init?: { status?: number; headers?: HeadersInit }): Response | null

Bytes [offset, offset + length) of the file as the whole resource: init.status (200 by default), the stored type and cache, no Content-Range (one tile of one archive). Past the end or of length 0 answers 416 with no body; a bound that is not a whole number from 0 throws a TypeError; null (no such path).

SubstrioSmtpOptions interface

interface SubstrioSmtpOptions

The platform services of the function's app.

substrio.mail.smtp's options: one message over SMTP (jsc_fetch.cpp), behind the app's outbound fence.

host: string
port?: number
secure?: boolean

TLS from the first byte (port 465); otherwise STARTTLS when the server offers it.

user?: string
pass?: string
from: string
to: string | string[]

One address, or an array of them (each its own RCPT).

message: string

The whole message, headers and body (RFC 5322).

requireTLS?: boolean

Refuse a server that does not offer TLS (default true).

verifyTLS?: boolean

Verify the server's certificate (default true).

encode?: boolean

Dot-stuff and normalise line ends (default true).

timeoutMs?: number

SubstrioServices interface

interface SubstrioServices
readonly mail: { smtp(options: SubstrioSmtpOptions): Promise<{ ok: boolean; code: number; message: string }> }

Mail sent by the function itself: substrio.mail.smtp(options) settles with { ok, code, message }.

readonly cmd: SubstrioCmd

The same tools as the global cmd.

api(method: string, path: string, body?: string | Uint8Array | JsonValue): any

Calls the app's own API like an HTTP request, as the app: substrio.api("GET", "/v1/_meta/collections"). Answers the parsed JSON body (the error envelope on failure). Email, realtime, queues-like and vector operations are reached this way, through the operation registry (GET /v1/_operation_registry). A body is a string, an object (serialised) or a Uint8Array.

route(method: string, url: string, authorization?: string): { params: Record<string, string>; route: string; auth: SubstrioPermissionAuth } | { status: number; body: string } | null

The gate of one of the app's own declared routes, judged for the caller named by authorization (a bearer token, as the Authorization header carries it) as if method and url (the path, with its query) were an HTTP request to it: { params, route, auth } on a pass, for the app to then call its own handler with; otherwise { status, body }, the refusal's own HTTP shape. Null when there is no route callback for this call.

files: SubstrioFiles

The tenant's stored files (PLAT-FILES-JS).

functions: { [name: string]: ((...args: any[]) => any) & { fetch(request: Request): any } }

The app's other functions as plain JS functions: await substrio.functions.worker({ job: 1 }). Called in the caller's own context with live values in and out (no serialisation, no new VM). The callee sees its own env; the caller's is back after. Shares the caller's CPU budget and deadline; nests at most 64 deep (jsc.limit_call_depth); refused inside db.transaction. A refused call throws an Error with a code: function_not_found, function_not_loaded, function_no_plain_entry, call_depth_exceeded, call_in_transaction, call_tenant_mismatch, function_compile_failed. .fetch(request) calls the callee's fetch entry.

kv(name: string): SubstrioKv
graph(label: string): SubstrioGraph
graphLabels(): string[]
db: { query(collection: string, query?: unknown): any; get(collection: string, id: string): any; insert(collection: string, doc: unknown): any; update(collection: string, id: string, ops: unknown): any; delete(collection: string, id: string): any; count(collection: string, filter?: unknown): number; insertMany(collection: string, docs: unknown[]): any; aggregate(collection: string, pipeline: unknown[]): any; collections(): any; }

The older object API; prefer db.

auth: { /** The caller who started this run, exactly as request.auth answers it * (null for a cron, a queue job, a trigger, or a call with an app key). */ getCurrentUser(): { user_id: string; email_verified: boolean; expires_at: number } | null; /** True exactly when this run's own caller holds the tenant's declared * permission `permission` (false for an unknown name, or no caller). */ checkPermission(permission: string): boolean; }
webhooks: { create(config: unknown): any; list(): any; remove(id: string): any; emit(event: string, payload: unknown): any; }
queue(name: string): SubstrioQueue

A durable queue (docs/runtime/queues.md).

plans: { get(...args: unknown[]): any; set(...args: unknown[]): any }
usage: { consume(...args: unknown[]): any; get(...args: unknown[]): any }
subscriptions: { entitlement(): { app: string; plan: string; default: boolean; since: number; limits: Record<string, { day: number; month: number }>; features: Record<string, boolean> } | null; }

The caller's plan in force at this app (docs/subscriptions.md): the highest of every subscription they hold here; null with no caller or no plans.

lua: { eval(script: string, keys?: string[], argv?: string[]): any }

Runs a Lua script against the app's default KV.

request: { path: string; method: string; body: string }

The request of this call as text: { path, method, body }.

readonly tenant: { readonly slug: string; readonly app: string; readonly sandbox: string | null }

The app this function runs in; a sandbox is "<app>--<name>". Frozen.

readonly runtimeLevel?: number

The runtime compatibility level this app runs under (PLATFORM-UPGRADES): which behaviour of the function runtime a release gives it. Undefined for an app still on level 1, the behaviour every app had before the level existed; from level 2 on it is the number the app's record carries (a platform upgrade moves it, never above the running release's).

collection(name: string): SubstrioLegacyCollection

Cloudflare-shaped helpers over the same database.

batch(operations: unknown[], options?: unknown): any
collections(): any
search(collection: string, query: string | Record<string, unknown>, options?: Record<string, unknown>): any

Text, vector or hybrid search over a collection.

readonly objects: SubstrioObjects

Durable objects: every export class Name of a function is the class substrio.objects.Name. One live instance per id on the server; its calls run one at a time. See docs/runtime/durable-objects.md.

SubstrioObjects interface

interface SubstrioObjects

The classes of the app's functions by name: substrio.objects.Room.

[className: string]: SubstrioObjectNamespace

SubstrioObjectNamespace interface

interface SubstrioObjectNamespace
idFromName(name: string): SubstrioObjectId

The id the text always names (the first 16 bytes of sha256 of the class and the text).

newUniqueId(): SubstrioObjectId

An id nobody named: random.

idFromString(hex: string): SubstrioObjectId

The id of 32 hex characters (its toString).

get(id: SubstrioObjectId): SubstrioObjectStub

The stub of an object: fetch(request) calls its fetch, any other member calls that method with the arguments.

SubstrioObjectId interface

interface SubstrioObjectId
readonly cls: string
readonly hex: string
readonly name: string | undefined
toString(): string
equals(other: SubstrioObjectId): boolean

SubstrioObjectStub type

type SubstrioObjectStub = {
  readonly id: SubstrioObjectId;
  fetch(input: string | Request, init?: RequestInit): Promise<Response>;
} & { [method: string]: (...args: any[]) => Promise<any> };

SubstrioObjectState interface

interface SubstrioObjectState

What an object's constructor receives: its state.

readonly id: { toString(): string }
readonly storage: SubstrioObjectStorage
readonly objects: SubstrioObjects

The same as substrio.objects, but a call of the object to itself is refused at once.

blockConcurrencyWhile<T>(callback: () => Promise<T> | T): Promise<T>
waitUntil(promise: Promise<unknown>): void
acceptWebSocket(socket: SubstrioObjectSocket): void
getWebSockets(): SubstrioObjectSocket[]

SubstrioObjectStorage interface

interface SubstrioObjectStorage
get(key: string | string[]): Promise<any>
put(key: string | Record<string, unknown>, value?: unknown): Promise<void>
delete(key: string | string[]): Promise<boolean | number>
deleteAll(): Promise<void>
list(options?: { start?: string; end?: string; prefix?: string; limit?: number; reverse?: boolean }): Promise<Map<string, any>>
transaction<T>(callback: (txn: SubstrioObjectStorage & { rollback(): void }) => Promise<T>): Promise<T>
getAlarm(): Promise<number | null>
setAlarm(time: number | Date): Promise<void>
deleteAlarm(): Promise<void>

SubstrioObjectSocket interface

interface SubstrioObjectSocket
readonly id: number
send(data: string | ArrayBuffer | ArrayBufferView): void
close(code?: number, reason?: string): void

SubstrioLegacyCollection interface

interface SubstrioLegacyCollection
add(doc: unknown): any
get(id: string): any
set(id: string, doc: unknown): any
update(id: string, ops: unknown): any
delete(id: string): any
find(filter?: unknown, options?: unknown): { data: any[]; count: number }
findOne(filter?: unknown): any
count(filter?: unknown): number
pipeline(stages: unknown[]): any
stats(): any
drop(): any
cursor(): SubstrioCursor
findById(id: string): any
live(id: string | number | bigint): any

The document as a live row: every assignment to a field is written at once (null if absent). A held row is a snapshot: after another writer's change an assignment throws ROW_STALE (code 0x4012), writes nothing, and the row holds the document as it is.

[id: number]: any

db.<name>[id]: live(id) for an integer id up to 2^32 - 2; id in db.<name>, db.<name>[id] = doc, delete db.<name>[id].

[Symbol.iterator](): Iterator<any>

for (const doc of db.<name>): every document as a live row, in id order.

readonly Model: any

class User extends db.<name>.Model: new User(doc) inserts and answers a live User; User.live, User.where(...).

use(klass: any): any

Binds a class that extends Model: every live row of the collection is then an instance of it.

exists(filter?: unknown): boolean
create(doc: unknown): any
upsert(filter: unknown, doc: unknown): any
put(doc: unknown): any
putMany(docs: unknown[]): string[]
deleteWhere(filter?: unknown): number
updateWhere(filter: unknown, fields: unknown): number
expire(field: string, options?: { after?: number; existing?: 'refuse' | 'allow' }): this

A document is gone after ms after the date its field holds (0: the date is the time); expired documents are hidden from every read and then deleted.

dropExpiry(): this

Takes the expiry declaration away.

elements(field: string, members: string[]): this

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.

dropElements(field: string, members: string[]): boolean

Takes an element declaration away (its keys, id and record); true when it was declared, false when it was not.

elementIndexes(): Array<{ kind: 'collection'; collection: string; field: string; elements: string[]; state: 'none' | 'building' | 'ready'; reason: string | null }>

The collection's element declarations, as index.list lists them.

compound(lead: string[], order: string): this

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).

dropCompound(lead: string[], order: string): boolean

Takes a compound declaration away (its keys, id and record); true when it was declared, false when it was not.

compoundIndexes(): Array<{ kind: 'collection'; collection: string; compound: string[]; state: 'none' | 'building' | 'ready'; reason: string | null }>

The collection's compound declarations, as index.list lists them.

aggregate(name: string, declaration: { group?: Array<string | { field: string; step?: number }>; metrics: Record<string, 'count' | { sum: string }>; where?: string }): 'none' | 'building' | 'ready'

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.

dropAggregate(name: string): boolean

Takes an aggregate declaration away (its group records with it); true when it was declared, false when it was not.

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 }>

The collection's aggregate declarations, as index.list lists them.

substrio global

declare const substrio: SubstrioServices;

SubstrioCmdResult interface

interface SubstrioCmdResult

One finished cmd.<tool> run (jsc_cmd.inc). stdout and stderr are the tool's bytes; files are the outputs it wrote (files.out), stored on a clean exit.

code: number
signal: number
stdout: Uint8Array
stderr: Uint8Array
files: Array<{ path: string; size: number; hash: string }>
cpuMs: number
wallMs: number
text(): string

stdout decoded as UTF-8.

SubstrioCmdTool interface

interface SubstrioCmdTool

One approved tool: a template (each value exactly one argument, no shell), an argument array with options, or one argument. A refusal rejects with a SubstrioToolError whose code names the reason.

(strings: TemplateStringsArray, ...values: unknown[]): Promise<SubstrioCmdResult>
(args: string[] | string, options?: { stdin?: string | Uint8Array; timeoutMs?: number }): Promise<SubstrioCmdResult>

SubstrioCmd type

type SubstrioCmd = { readonly [tool: string]: SubstrioCmdTool };

The command line tools this host has from the approved catalog (cmd.ffmpeg, cmd.magick, ...).

cmd global

declare const cmd: SubstrioCmd;

Web platform

fetch function

declare function fetch(input: string | Request, init?: { method?: string; headers?: HeadersInit; body?: string; signal?: AbortSignal }): Promise<Response>;

fetch buffers a response known to be 1 MiB or less and streams a larger or chunked one (see Response below): the Response it answers has status, ok, url, headers (get, has, forEach, entries, keys, values), text(), json(), arrayBuffer() and bytes() (exact for binary bodies), and body (a ReadableStream) on a streamed one; no blob() or clone(). init takes method, headers (Headers, array or object), a string body and a signal only (or the one a Request input carries; an abort cancels the transfer and rejects with the signal's reason): no redirect option or non-string body. 30 s timeout per request. Loopback, private, link-local and reserved addresses are refused (also after a redirect) unless the tenant has an allowance for the host.

setTimeout function

declare function setTimeout(callback: (...args: any[]) => void, delayMs?: number, ...args: any[]): number;

Timers run inside the call, on its budget; the call's timers are cleared when it ends.

setInterval function

declare function setInterval(callback: (...args: any[]) => void, delayMs?: number, ...args: any[]): number;

clearTimeout function

declare function clearTimeout(id?: number): void;

clearInterval function

declare function clearInterval(id?: number): void;

queueMicrotask function

declare function queueMicrotask(callback: () => void): void;

Runs the callback on the engine's microtask queue (the one promise reactions use): after the current synchronous code, before timers. A throw is an unhandled rejection, reported once at the end of the call.

structuredClone function

declare function structuredClone<T>(value: T): T;

The standard structured clone: primitives (BigInt, undefined too), Date, RegExp, Map, Set, ArrayBuffer, typed arrays, DataView, Error types (name, message, stack, cause), arrays and plain objects (own enumerable string keys). Cycles and repeated references are kept. A function or a Symbol throws a DOMException named DataCloneError; so does nesting past 4096 levels. Class instances become plain objects; a RegExp's lastIndex is not kept.

performance global

declare const performance

Milliseconds with sub-microsecond precision; the only member.

now(): number

atob function

declare function atob(data: string): string;

btoa function

declare function btoa(data: string): string;

_hrtime function

declare function _hrtime(): number;

Nanosecond monotonic clock, for benchmarks.

debugPrint function

declare function debugPrint(value: unknown): void;

SubstrioConsole interface

interface SubstrioConsole
log(...args: unknown[]): void
warn(...args: unknown[]): void
error(...args: unknown[]): void
info(...args: unknown[]): void
debug(...args: unknown[]): void
time(label?: string): void
timeEnd(label?: string): void

console global

declare const console: SubstrioConsole;

CryptoHashName type

type CryptoHashName = 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512';

CryptoCurveName type

type CryptoCurveName = 'P-256' | 'P-384' | 'P-521';

CryptoKeyUsage type

type CryptoKeyUsage = 'encrypt' | 'decrypt' | 'sign' | 'verify' | 'deriveKey' | 'deriveBits' | 'wrapKey' | 'unwrapKey';

CryptoKeyFormat type

type CryptoKeyFormat = 'raw' | 'pkcs8' | 'spki' | 'jwk';

CryptoHashIdentifier type

type CryptoHashIdentifier = CryptoHashName | { name: CryptoHashName };

CryptoJsonWebKey interface

interface CryptoJsonWebKey

A JSON Web Key (RFC 7517) with the members Web Crypto reads and writes.

kty?: 'oct' | 'RSA' | 'EC' | 'OKP'
alg?: string
crv?: string
use?: string
ext?: boolean
key_ops?: CryptoKeyUsage[]
x?: string
y?: string
d?: string
n?: string
e?: string
p?: string
q?: string
dp?: string
dq?: string
qi?: string
k?: string

CryptoAlgorithm interface

interface CryptoAlgorithm

The algorithm dictionaries of every operation (the members an operation does not read are ignored).

name: 'HMAC' | 'RSASSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA' | 'Ed25519' | 'AES-GCM' | 'AES-CBC' | 'AES-CTR' | 'AES-KW' | 'RSA-OAEP' | 'PBKDF2' | 'HKDF' | 'ECDH' | 'X25519' | (string & {})
namedCurve?: CryptoCurveName
length?: number

HMAC and AES key bits; the AES-CTR counter bits; none for deriveBits (that length is an argument).

modulusLength?: number
publicExponent?: Uint8Array
saltLength?: number

RSA-PSS salt bytes (required to sign and verify).

additionalData?: BufferSource
tagLength?: number
counter?: BufferSource
label?: BufferSource
iterations?: number
public?: CryptoKey

ECDH and X25519: the peer's public key.

CryptoKey interface

interface CryptoKey
readonly type: 'secret' | 'public' | 'private'
readonly extractable: boolean
readonly algorithm: { name: string; hash?: { name: CryptoHashName }; length?: number; namedCurve?: CryptoCurveName; modulusLength?: number; publicExponent?: Uint8Array; }
readonly usages: CryptoKeyUsage[]

CryptoKeyPair interface

interface CryptoKeyPair
publicKey: CryptoKey
privateKey: CryptoKey

crypto global

declare const crypto

Web Crypto on OpenSSL 3, complete for: digest SHA-1/256/384/512; sign and verify with HMAC, RSASSA-PKCS1-v1_5, RSA-PSS, ECDSA (P-256, P-384, P-521; the signature is r||s) and Ed25519; encrypt and decrypt with AES-GCM, AES-CBC, AES-CTR and RSA-OAEP; generateKey; importKey and exportKey in raw, pkcs8, spki and jwk; deriveBits and deriveKey with PBKDF2, HKDF, ECDH and X25519; wrapKey and unwrapKey with AES-GCM, AES-CBC, AES-CTR, AES-KW and RSA-OAEP. Every subtle method answers a settled promise; a failure rejects with an Error named as Web Crypto names it (TypeError, NotSupportedError, InvalidAccessError, DataError, OperationError, SyntaxError). The work is synchronous native CPU inside the call, and counts against its CPU budget: generating an RSA key takes tens to hundreds of milliseconds. getRandomValues takes at most 65536 bytes per call.

getRandomValues<T extends ArrayBufferView>(array: T): T
randomUUID(): string
subtle: { digest(algorithm: CryptoHashIdentifier, data: BufferSource): Promise<ArrayBuffer>; generateKey(algorithm: CryptoAlgorithm | string, extractable: boolean, usages: CryptoKeyUsage[]): Promise<CryptoKey | CryptoKeyPair>; importKey(format: 'jwk', keyData: CryptoJsonWebKey, algorithm: CryptoAlgorithm | string, extractable: boolean, usages: CryptoKeyUsage[]): Promise<CryptoKey>; importKey(format: 'raw' | 'pkcs8' | 'spki', keyData: BufferSource, algorithm: CryptoAlgorithm | string, extractable: boolean, usages: CryptoKeyUsage[]): Promise<CryptoKey>; exportKey(format: 'jwk', key: CryptoKey): Promise<CryptoJsonWebKey>; exportKey(format: 'raw' | 'pkcs8' | 'spki', key: CryptoKey): Promise<ArrayBuffer>; sign(algorithm: CryptoAlgorithm | string, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>; verify(algorithm: CryptoAlgorithm | string, key: CryptoKey, signature: BufferSource, data: BufferSource): Promise<boolean>; encrypt(algorithm: CryptoAlgorithm, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>; decrypt(algorithm: CryptoAlgorithm, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>; deriveBits(algorithm: CryptoAlgorithm, baseKey: CryptoKey, length?: number | null): Promise<ArrayBuffer>; deriveKey(algorithm: CryptoAlgorithm, baseKey: CryptoKey, derivedKeyType: CryptoAlgorithm | string, extractable: boolean, usages: CryptoKeyUsage[]): Promise<CryptoKey>; wrapKey(format: CryptoKeyFormat, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: CryptoAlgorithm): Promise<ArrayBuffer>; unwrapKey(format: CryptoKeyFormat, wrappedKey: BufferSource, unwrappingKey: CryptoKey, unwrapAlgorithm: CryptoAlgorithm, unwrappedKeyAlgorithm: CryptoAlgorithm | string, extractable: boolean, usages: CryptoKeyUsage[]): Promise<CryptoKey>; }

BufferSource type

type BufferSource = ArrayBuffer | ArrayBufferView;

require global

declare const require

CommonJS require for the builtin modules only (the ones declared at the end of this file, by their node: or bare name, and substrio:env). Any other id throws an Error with code MODULE_NOT_FOUND naming it. The same module, the same exports object, as the matching import. cache holds the exports of the modules loaded so far, indexed by module.

(id: string): any
readonly cache: any[]

TextEncoder class

declare class TextEncoder

UTF-8 only: TextDecoder throws "Unsupported encoding" for any other label.

readonly encoding: 'utf-8'
encode(text?: string): Uint8Array
encodeInto(text: string, into: Uint8Array): { read: number; written: number }

TextDecoder class

declare class TextDecoder
constructor(label?: 'utf-8' | 'utf8' | 'UTF-8', options?: { fatal?: boolean })
readonly encoding: 'utf-8'
decode(input?: BufferSource): string

Invalid UTF-8 becomes U+FFFD; with { fatal: true } it throws instead.

Headers class

declare class Headers

Headers, Request and Response are the engine's JS implementations, not the browser's: no clone(). A fetch response whose body is over 1 MiB, or of unknown length, is streamed: fetch() resolves at the headers, body is a ReadableStream paced by backpressure (at most 1 MiB undelivered), and text(), json() and arrayBuffer() read the rest (16 MiB limit, over it they throw an error naming it). A smaller body is buffered. Returning a fetch response, or new Response(response.body, init), without reading its body sends the upstream bytes to the client as they arrive, with no JS reading them. Returning a Response built on a ReadableStream you made (or transformed) from an HTTP route streams it chunk by chunk: the call stays running (wall and CPU limits apply) and the stream is pulled only as fast as the client reads. A body you read yourself and return as text or bytes is a buffered response (64 MiB limit). A fetch body you never read is cancelled when the call ends.

constructor(init?: HeadersInit)
get(name: string): string | null
set(name: string, value: string): void
has(name: string): boolean
delete(name: string): void
append(name: string, value: string): void
forEach(callback: (value: string, name: string) => void): void

HeadersInit type

type HeadersInit = Headers | [string, string][] | Record<string, string>;

RequestInit interface

interface RequestInit
method?: string
headers?: HeadersInit
body?: string | Uint8Array
signal?: AbortSignal

Request class

declare class Request
constructor(input: string | Request, init?: { method?: string; headers?: HeadersInit; body?: string | Uint8Array; signal?: AbortSignal })
readonly url: string
readonly signal: AbortSignal

init.signal, else the copied request's, else a signal that never aborts. fetch(request) honours it.

readonly method: string
readonly headers: Headers
readonly body: ReadableStream<Uint8Array> | string | null

The body of the request a function receives: its text (or null) when the upload is buffered (Content-Length up to 1 MiB), a ReadableStream of Uint8Array when it is larger or chunked (read once; the client is paced by your reads). text(), json(), arrayBuffer() and bytes() work on both and are limited to 16 MiB.

text(): Promise<string>
json(): Promise<any>
arrayBuffer(): Promise<ArrayBuffer>
bytes(): Promise<Uint8Array>
formData(): Promise<FormData>

Response class

declare class Response
constructor(body?: string | Uint8Array | ArrayBuffer | ReadableStream | null, init?: { status?: number; statusText?: string; headers?: HeadersInit })
readonly status: number
readonly statusText: string
readonly ok: boolean
readonly headers: Headers
readonly body: any

A ReadableStream for a streamed fetch response or one you built; the bytes or string given to the constructor otherwise.

readonly bodyUsed?: boolean

true once a streamed fetch body was read or locked.

text(): Promise<string>
json(): Promise<any>
arrayBuffer(): Promise<ArrayBuffer>
bytes?(): Promise<Uint8Array>

Fetched responses only (buffered and streamed).

blob(): Promise<Blob>
static json(data: unknown, init?: { status?: number; statusText?: string; headers?: HeadersInit }): Response

URL class

declare class URL

A native URL wrapped by a JS class.

constructor(url: string, base?: string)
href: string
protocol: string
host: string
hostname: string
port: string
pathname: string
search: string
hash: string
readonly origin: string
readonly searchParams: URLSearchParams
toString(): string

URLSearchParams class

declare class URLSearchParams
constructor(init?: string | [string, string][] | Record<string, string>)
get(name: string): string | null
has(name: string): boolean
set(name: string, value: string): void
append(name: string, value: string): void
delete(name: string): void
getAll(name: string): string[]
forEach(callback: (value: string, name: string) => void): void
entries(): IterableIterator<[string, string]>
keys(): IterableIterator<string>
values(): IterableIterator<string>
toString(): string

Blob class

declare class Blob
constructor(parts?: unknown[], options?: { type?: string })
text(): Promise<string>
arrayBuffer(): Promise<ArrayBuffer>
slice(start?: number, end?: number, type?: string): Blob

File class

declare class File extends Blob
constructor(parts: unknown[], name: string, options?: { type?: string; lastModified?: number })
readonly name: string
readonly lastModified: number

FormData class

declare class FormData
append(name: string, value: string | Blob): void
get(name: string): string | Blob | null
has(name: string): boolean
delete(name: string): void

Event class

declare class Event
constructor(type: string)
preventDefault(): void
stopPropagation(): void

EventTarget class

declare class EventTarget
addEventListener(type: string, listener: (event: Event) => void): void
removeEventListener(type: string, listener: (event: Event) => void): void
dispatchEvent(event: Event): boolean

ReadableStreamController interface

interface ReadableStreamController<R = unknown>
enqueue(chunk: R): void
close(): void
error(reason?: unknown): void
readonly desiredSize: number | null

ReadableStreamReader interface

interface ReadableStreamReader<R = unknown>
read(): Promise<{ done: boolean; value?: R }>
cancel(reason?: unknown): Promise<void>
releaseLock(): void
readonly closed: Promise<void>

ReadableStream class

declare class ReadableStream<R = unknown>

Web streams with pull, cancel and backpressure (highWaterMark counts chunks, or size(chunk)): a pull runs only while the queue is below it or a read waits. Read with getReader() or for await (const chunk of stream). A fetch response body gives Uint8Array chunks. No byte streams (BYOB), CompressionStream or TextDecoderStream.

constructor(source?: { start?(controller: ReadableStreamController<R>): unknown; pull?(controller: ReadableStreamController<R>): unknown; cancel?(reason?: unknown): unknown }, strategy?: { highWaterMark?: number; size?(chunk: R): number })
readonly locked: boolean
getReader(): ReadableStreamReader<R>
cancel(reason?: unknown): Promise<void>
pipeTo(destination: WritableStream, options?: { preventClose?: boolean; preventAbort?: boolean; preventCancel?: boolean }): Promise<void>
pipeThrough<T extends { readable: ReadableStream; writable: WritableStream }>(transform: T, options?: { preventClose?: boolean; preventAbort?: boolean; preventCancel?: boolean }): T['readable']
values(): AsyncIterableIterator<R>
[Symbol.asyncIterator](): AsyncIterableIterator<R>

WritableStream class

declare class WritableStream

Writes run one after the other; close() waits for them.

constructor(sink?: { start?(controller: { error(reason?: unknown): void }): unknown; write?(chunk: any, controller: { error(reason?: unknown): void }): unknown; close?(): unknown; abort?(reason?: unknown): unknown }, strategy?: { highWaterMark?: number })
readonly locked: boolean
getWriter(): { write(chunk: any): Promise<void>; close(): Promise<void>; abort(reason?: unknown): Promise<void>; releaseLock(): void; readonly ready: Promise<void>; readonly closed: Promise<void>; readonly desiredSize: number | null }
close(): Promise<void>
abort(reason?: unknown): Promise<void>

TransformStream class

declare class TransformStream

transform() may return a promise; a write waits while the readable side has a chunk nobody has read.

constructor(transformer?: { start?(controller: TransformStreamController): unknown; transform?(chunk: any, controller: TransformStreamController): unknown; flush?(controller: TransformStreamController): unknown }, writableStrategy?: { highWaterMark?: number }, readableStrategy?: { highWaterMark?: number })
readonly readable: ReadableStream
readonly writable: WritableStream

TransformStreamController interface

interface TransformStreamController
enqueue(chunk: unknown): void
error(reason?: unknown): void
terminate(): void
readonly desiredSize: number | null

DOMException class

declare class DOMException extends Error

An Error with a standard name: AbortError, TimeoutError, DataCloneError.

constructor(message?: string, name?: string)
readonly name: string
readonly code: number

AbortSignal class

declare class AbortSignal

aborted and reason are read-only. abort() sets reason to its argument, else a DOMException named AbortError. A fetch given the signal is cancelled in flight and rejects with the reason.

readonly aborted: boolean
readonly reason: any
onabort: ((event: { type: 'abort'; target: AbortSignal }) => void) | null
addEventListener(type: 'abort', listener: (event: { type: 'abort'; target: AbortSignal }) => void, options?: { once?: boolean }): void
removeEventListener(type: 'abort', listener: (event: { type: 'abort'; target: AbortSignal }) => void): void
throwIfAborted(): void
static abort(reason?: any): AbortSignal

An already aborted signal.

static timeout(delayMs: number): AbortSignal

Aborts with a DOMException named TimeoutError after delayMs (a timer of the call).

static any(signals: Iterable<AbortSignal>): AbortSignal

Aborts when any of the signals does, with its reason.

AbortController class

declare class AbortController
readonly signal: AbortSignal
abort(reason?: any): void

KVNamespace class

declare class KVNamespace extends Object implements SubstrioKvNamespace

Constructors of the Cloudflare shims env bindings are made of.

constructor(name: string)
get(key: string, type?: 'text' | 'json' | { type: 'json' }): Promise<unknown>
put(key: string, value: string | JsonValue, options?: unknown): Promise<void>
delete(key: string): Promise<void>
list(options?: unknown): Promise<{ keys: { name: string }[]; list_complete: boolean }>

D1Database class

declare class D1Database implements SubstrioD1Database
constructor(name: string)
prepare(sql: string): SubstrioD1Statement
batch(statements: SubstrioD1Statement[]): Promise<unknown[]>
exec(sql: string): Promise<unknown>

D1Statement class

declare class D1Statement implements SubstrioD1Statement
bind(...values: unknown[]): SubstrioD1Statement
first(column?: string): Promise<Record<string, unknown> | unknown | null>
all(): Promise<{ results: Record<string, unknown>[]; success: boolean; meta: { changes: number; last_row_id: number } }>
run(): Promise<{ results: Record<string, unknown>[]; success: boolean; meta: { changes: number; last_row_id: number } }>
raw(): Promise<unknown[][]>

ServiceBinding class

declare class ServiceBinding implements SubstrioServiceBinding
constructor(serviceName: string)
fetch(request: Request | string, init?: RequestInit): Promise<Response>

Node and Cloudflare shims the engine installs

process global

declare const process

Node's process, only env (the function's env, the same object as the global env) and the two stream writers, which log.

readonly env: SubstrioEnv
stdout: { write(message: string): true; isTTY: false }
stderr: { write(message: string): true; isTTY: false }

Buffer interface

interface Buffer extends Uint8Array

A Uint8Array with a few Node statics. No readUInt*, slice-to-Buffer, equals or copy. Encodings: 'hex', 'base64', else UTF-8.

toString(encoding?: 'hex' | 'base64' | 'utf8' | string): string

Buffer global

declare const Buffer
new (length?: number): Buffer
new (array: ArrayLike<number> | Iterable<number>): Buffer
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Buffer
readonly prototype: Buffer
readonly BYTES_PER_ELEMENT: number
of(...items: number[]): Buffer

Inherited Uint8Array.of constructs this subclass.

from(input: string | ArrayBuffer | ArrayBufferView | number[], encoding?: 'hex' | 'base64' | 'utf8' | string): Buffer
alloc(size: number): Buffer
concat(list: Uint8Array[], totalLength?: number): Buffer
isBuffer(value: unknown): value is Buffer

caches global

declare const caches

Cloudflare's cache, only default, best effort: it lives in the isolate's memory (not shared between worker threads, gone when the isolate is retired) and holds at most 16 MiB (FunctionSizeLimits::CACHE_BYTES) of bodies and headers, evicting the least recently used. An entry expires at its Cache-Control s-maxage or max-age, else its Expires (checked on match); no-store and private are not stored; with none of them it stays until evicted. A response larger than the whole cache is not stored.

default: { match(request: Request | string): Promise<Response | null>; put(request: Request | string, response: Response): Promise<void>; /** True when a live entry existed. */ delete(request: Request | string): Promise<boolean>; }

setImmediate function

declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): number;

Runs the callback as a promise microtask (not after timers as in Node); answers an id, counted per isolate.

clearImmediate function

declare function clearImmediate(id?: number): void;

Cancels a setImmediate that has not run yet.

Builtin modules a function can import or require

substrio:env module

declare module 'substrio:env'
export const env: SubstrioEnv

The importing function's own env (refreshed in place, read-only); from require, the running call's env.

node:buffer module

declare module 'node:buffer'
export { Buffer }
export const Blob: typeof globalThis.Blob
export function atob(data: string): string
export function btoa(data: string): string
export const INSPECT_MAX_BYTES: number

buffer module

declare module 'buffer'
export * from 'node:buffer'

node:events module

declare module 'node:events'
type Listener = (...args: any[]) => any
class EventEmitter { constructor(options?: { captureRejections?: boolean }); static defaultMaxListeners: number; static readonly errorMonitor: unique symbol; static readonly captureRejectionSymbol: unique symbol; static EventEmitter: typeof EventEmitter; static once(emitter: EventEmitter, name: string | symbol, options?: { signal?: AbortSignal }): Promise<any[]>; static listenerCount(emitter: EventEmitter, type: string | symbol): number; static getEventListeners(emitter: EventEmitter, type: string | symbol): Listener[]; static setMaxListeners(n: number, ...targets: EventEmitter[]): void; on(type: string | symbol, listener: Listener): this; addListener(type: string | symbol, listener: Listener): this; prependListener(type: string | symbol, listener: Listener): this; once(type: string | symbol, listener: Listener): this; prependOnceListener(type: string | symbol, listener: Listener): this; off(type: string | symbol, listener: Listener): this; removeListener(type: string | symbol, listener: Listener): this; removeAllListeners(type?: string | symbol): this; emit(type: string | symbol, ...args: any[]): boolean; listeners(type: string | symbol): Listener[]; rawListeners(type: string | symbol): Listener[]; listenerCount(type: string | symbol, listener?: Listener): number; eventNames(): (string | symbol)[]; setMaxListeners(n: number): this; getMaxListeners(): number; }
export = EventEmitter

events module

declare module 'events'
import EventEmitter = require('node:events')
export = EventEmitter

node:path module

declare module 'node:path'
interface ParsedPath { root: string; dir: string; base: string; ext: string; name: string }
interface Path { sep: '/'; delimiter: ':'; posix: Path; resolve(...paths: string[]): string; normalize(path: string): string; isAbsolute(path: string): boolean; join(...paths: string[]): string; relative(from: string, to: string): string; dirname(path: string): string; basename(path: string, suffix?: string): string; extname(path: string): string; format(pathObject: Partial<ParsedPath>): string; parse(path: string): ParsedPath; toNamespacedPath(path: string): string; }
const path: Path
export = path

path module

declare module 'path'
import path = require('node:path')
export = path

node:path/posix module

declare module 'node:path/posix'
import path = require('node:path')
export = path

path/posix module

declare module 'path/posix'
import path = require('node:path')
export = path

node:querystring module

declare module 'node:querystring'
type ParsedQs = Record<string, string | string[]>
export function parse(str: string, sep?: string | null, eq?: string | null, options?: { maxKeys?: number; decodeURIComponent?: (s: string) => string }): ParsedQs
export function stringify(obj: Record<string, any>, sep?: string, eq?: string, options?: { encodeURIComponent?: (s: string) => string }): string
export function escape(str: string): string
export function unescape(str: string): string
export const decode: typeof parse
export const encode: typeof stringify

querystring module

declare module 'querystring'
export * from 'node:querystring'

node:string_decoder module

declare module 'node:string_decoder'
type StringDecoderEncoding = 'utf8' | 'utf-8' | 'utf16le' | 'utf-16le' | 'ucs2' | 'ucs-2' | 'latin1' | 'binary' | 'ascii' | 'hex' | 'base64' | 'base64url'
export class StringDecoder { constructor(encoding?: StringDecoderEncoding); readonly encoding: string; write(buffer: string | ArrayBufferView): string; end(buffer?: ArrayBufferView): string; }

string_decoder module

declare module 'string_decoder'
export * from 'node:string_decoder'

node:url module

declare module 'node:url'
export { URL, URLSearchParams }
export class Url { protocol: string | null; slashes: boolean | null; auth: string | null; host: string | null; port: string | null; hostname: string | null; hash: string | null; search: string | null; query: string | Record<string, string | string[]> | null; pathname: string | null; path: string | null; href: string | null; }
export function parse(urlString: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url
export function format(url: URL | Partial<Url> | string, options?: { auth?: boolean; fragment?: boolean; search?: boolean }): string
export function resolve(from: string, to: string): string
export function fileURLToPath(url: string | URL): string
export function pathToFileURL(path: string): URL
export function urlToHttpOptions(url: URL): Record<string, any>
export function domainToASCII(domain: string): string

url module

declare module 'url'
export * from 'node:url'

node:util module

declare module 'node:util'
export function format(format?: any, ...args: any[]): string
export function formatWithOptions(options: object, format?: any, ...args: any[]): string
export function inspect(value: any, options?: { depth?: number | null; maxArrayLength?: number | null; breakLength?: number }): string
export namespace inspect { const custom: unique symbol; const defaultOptions: { depth: number; maxArrayLength: number; breakLength: number } }
export function promisify<T extends (...args: any[]) => any>(fn: T): (...args: any[]) => Promise<any>
export namespace promisify { const custom: unique symbol }
export function callbackify(fn: (...args: any[]) => Promise<any>): (...args: any[]) => void
export function inherits(constructor: Function, superConstructor: Function): void
export function deprecate<T extends Function>(fn: T, message: string, code?: string): T
export function isDeepStrictEqual(a: unknown, b: unknown): boolean
export function stripVTControlCharacters(text: string): string
export const types: { isDate(v: unknown): v is Date; isRegExp(v: unknown): v is RegExp; isMap(v: unknown): v is Map<any, any>; isSet(v: unknown): v is Set<any>; isWeakMap(v: unknown): boolean; isWeakSet(v: unknown): boolean; isPromise(v: unknown): v is Promise<any>; isNativeError(v: unknown): v is Error; isArrayBuffer(v: unknown): v is ArrayBuffer; isAnyArrayBuffer(v: unknown): boolean; isTypedArray(v: unknown): boolean; isUint8Array(v: unknown): v is Uint8Array; isDataView(v: unknown): v is DataView; isArrayBufferView(v: unknown): v is ArrayBufferView; isAsyncFunction(v: unknown): boolean; isGeneratorFunction(v: unknown): boolean; isGeneratorObject(v: unknown): boolean; isSymbolObject(v: unknown): boolean; isBoxedPrimitive(v: unknown): boolean; isProxy(v: unknown): boolean; }
export const isArray: typeof Array.isArray
export function isBoolean(v: unknown): v is boolean
export function isNull(v: unknown): v is null
export function isNullOrUndefined(v: unknown): v is null | undefined
export function isNumber(v: unknown): v is number
export function isString(v: unknown): v is string
export function isSymbol(v: unknown): v is symbol
export function isUndefined(v: unknown): v is undefined
export function isRegExp(v: unknown): v is RegExp
export function isObject(v: unknown): boolean
export function isDate(v: unknown): v is Date
export function isError(v: unknown): v is Error
export function isFunction(v: unknown): v is Function
export function isPrimitive(v: unknown): boolean
export const TextEncoder: typeof globalThis.TextEncoder
export const TextDecoder: typeof globalThis.TextDecoder

util module

declare module 'util'
export * from 'node:util'

node:util/types module

declare module 'node:util/types'
export const types: typeof import('node:util').types
export = types

util/types module

declare module 'util/types'
import types = require('node:util/types')
export = types

node:assert module

declare module 'node:assert'
class AssertionError extends Error { constructor(options: { message?: string; actual?: unknown; expected?: unknown; operator?: string }); generatedMessage: boolean; code: 'ERR_ASSERTION'; actual: unknown; expected: unknown; operator: string | undefined; }
type ErrorMatcher = Function | RegExp | Record<string, unknown> | ((error: any) => boolean)
interface Assert { (value: unknown, message?: string | Error): asserts value; ok(value: unknown, message?: string | Error): asserts value; fail(message?: string | Error): never; equal(actual: unknown, expected: unknown, message?: string | Error): void; notEqual(actual: unknown, expected: unknown, message?: string | Error): void; strictEqual<T>(actual: unknown, expected: T, message?: string | Error): asserts actual is T; notStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void; deepEqual(actual: unknown, expected: unknown, message?: string | Error): void; notDeepEqual(actual: unknown, expected: unknown, message?: string | Error): void; deepStrictEqual<T>(actual: unknown, expected: T, message?: string | Error): asserts actual is T; notDeepStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void; throws(fn: () => unknown, expected?: ErrorMatcher | string, message?: string): void; doesNotThrow(fn: () => unknown, expected?: ErrorMatcher | string, message?: string): void; rejects(promiseOrFn: Promise<unknown> | (() => Promise<unknown>), expected?: ErrorMatcher | string, message?: string): Promise<void>; doesNotReject(promiseOrFn: Promise<unknown> | (() => Promise<unknown>), expected?: ErrorMatcher | string, message?: string): Promise<void>; match(value: string, regexp: RegExp, message?: string | Error): void; doesNotMatch(value: string, regexp: RegExp, message?: string | Error): void; ifError(value: unknown): asserts value is null | undefined; AssertionError: typeof AssertionError; strict: Assert; }
const assert: Assert
export = assert

assert module

declare module 'assert'
import assert = require('node:assert')
export = assert

node:assert/strict module

declare module 'node:assert/strict'
import assert = require('node:assert')
const strict: typeof assert.strict
export = strict

assert/strict module

declare module 'assert/strict'
import strict = require('node:assert/strict')
export = strict

node:crypto module

declare module 'node:crypto'
type DigestName = 'sha1' | 'sha256' | 'sha384' | 'sha512' | 'sha-1' | 'sha-256' | 'sha-384' | 'sha-512' | (string & {})
type BinaryLike = string | ArrayBufferView | ArrayBuffer
type OutputEncoding = 'hex' | 'base64' | 'base64url'
class Hash { update(data: BinaryLike, inputEncoding?: 'utf8' | 'hex' | 'base64'): this; digest(): Buffer; digest(encoding: OutputEncoding): string; copy(): Hash; }
class Hmac { update(data: BinaryLike, inputEncoding?: 'utf8' | 'hex' | 'base64'): this; digest(): Buffer; digest(encoding: OutputEncoding): string; }
type PemKey = string | Uint8Array | { key: string | Uint8Array; /** constants.RSA_PKCS1_PSS_PADDING for RSA-PSS (the maximum salt to sign, any salt to verify). */ padding?: number; /** "ieee-p1363" for the r||s signature of ECDSA; the default is DER. */ dsaEncoding?: 'der' | 'ieee-p1363'; }

A PEM key (PKCS#8, SPKI, PKCS#1 or SEC1), or one with the options that choose the padding and the EC signature encoding.

class Sign { update(data: BinaryLike, inputEncoding?: 'utf8' | 'hex' | 'base64'): this; sign(privateKey: PemKey): Buffer; sign(privateKey: PemKey, outputEncoding: OutputEncoding): string; }
class Verify { update(data: BinaryLike, inputEncoding?: 'utf8' | 'hex' | 'base64'): this; verify(publicKey: PemKey, signature: BinaryLike, signatureEncoding?: 'hex' | 'base64'): boolean; }
export function createHash(algorithm: DigestName): Hash
export function createHmac(algorithm: DigestName, key: BinaryLike): Hmac
export function hash(algorithm: DigestName, data: BinaryLike, outputEncoding?: OutputEncoding | 'buffer'): string | Buffer
export function randomBytes(size: number): Buffer
export function randomBytes(size: number, callback: (err: Error | null, buf: Buffer) => void): void
export const pseudoRandomBytes: typeof randomBytes
export function randomFillSync<T extends ArrayBufferView>(buffer: T, offset?: number, size?: number): T
export function randomInt(max: number): number
export function randomInt(min: number, max: number): number
export function randomInt(min: number, max: number, callback: (err: Error | null, value: number) => void): void
export function randomUUID(): string
export function getRandomValues<T extends ArrayBufferView>(array: T): T
export function timingSafeEqual(a: ArrayBufferView, b: ArrayBufferView): boolean
export function createSign(algorithm: DigestName): Sign
export function createVerify(algorithm: DigestName): Verify
export function sign(algorithm: DigestName | null, data: BinaryLike, key: PemKey): Buffer
export function verify(algorithm: DigestName | null, data: BinaryLike, key: PemKey, signature: BinaryLike): boolean
export function pbkdf2Sync(password: BinaryLike, salt: BinaryLike, iterations: number, keylen: number, digest?: DigestName): Buffer
export function pbkdf2(password: BinaryLike, salt: BinaryLike, iterations: number, keylen: number, digest: DigestName, callback: (err: Error | null, key?: Buffer) => void): void
export function getHashes(): string[]
export const constants: { RSA_PKCS1_PADDING: 1; RSA_PKCS1_OAEP_PADDING: 4; RSA_PKCS1_PSS_PADDING: 6; RSA_PSS_SALTLEN_DIGEST: -1; RSA_PSS_SALTLEN_MAX_SIGN: -2; RSA_PSS_SALTLEN_AUTO: -2; }
export const webcrypto: typeof crypto
export const subtle: typeof crypto.subtle
export { Hash, Hmac, Sign, Verify }

crypto module

declare module 'crypto'
export * from 'node:crypto'