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: numbername: stringcreated_at: numberservices: numberA bit per service the account is entitled to.
suspended: booleanrequire_mfa: booleanEvery member signs in with 2FA.
personal: booleanMade 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: numberThe person's id (a number, as every permission call takes it).
email_verified: booleanexpires_at: numberWhen the token expires, in ms since the epoch.
impersonator?: numberThe operator acting as this person, when the session is an impersonation.
accounts: SubstrioCallerAccount[]The accounts the person belongs to.
holds?: booleanWhether 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 | nullThe 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 } | nullThe sandbox that called, from the signed Substrio-Caller stamp, or null.
readonly ip?: string | nullThe 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>): voidThe 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: stringfunction: stringtimestamp: numberUnix seconds.
rawBody: Uint8ArrayThe 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: stringdocumentId: stringtimestamp: numberUnix milliseconds.
rawBody: Uint8ArrayThe 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: stringThe send's idempotency id, else the message's number in its queue.
readonly body: BodyThe body as it was sent (JSON).
readonly timestamp: numberUnix milliseconds the message was sent.
readonly attempts: numberDeliveries so far, this one included (1 on the first).
ack(): voidDone: the message leaves the queue. Without any call, it is acked when the handler returns.
retry(options?: { delay?: number }): voidDeliver it again after
delayms (default: the queue's exponential backoff); past max_retries it is a dead letter.
SubstrioQueueBatch interface
interface SubstrioQueueBatch<Body = unknown>
readonly queue: stringreadonly messages: readonly SubstrioQueueMessage<Body>[]ackAll(): voidretryAll(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: numberduplicate: 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 }): SubstrioQueueSentsendBatch(items: readonly (unknown | { body: unknown; delay?: number; id?: string })[]): SubstrioQueueSent[]
SubstrioScheduledEvent interface
interface SubstrioScheduledEvent extends SubstrioCronEvent
type: 'scheduled'cron: stringThe cron entry's id (the same as cron_id).
scheduledTime: numberUnix 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.
readonly [name: string]: string | SubstrioKvNamespace | SubstrioD1Database | SubstrioServiceBinding | SubstrioAssets
SubstrioAssets interface
interface SubstrioAssets
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[]): SubstrioD1Statementfirst(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): SubstrioD1Statementbatch(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?: numbersliding?: 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: booleanremaining: numberresetAt: 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[]): thisDeclares 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): booleanDrops a unique space (its fields stay plain unique fields of their collections): true when it was declared.
kv(name: string): SubstrioKvA key-value store; values are bytes, one per character (see SubstrioKv).
secret(collection: string, id: string, field: string): string | nullThe plain value of a secret field (a rule of type "secret") of one document:
idis 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[] | SubstrioSqlResultRuns 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): SubstrioStatementA statement to run many times: all(...args) or first(...args).
arg(index: number): SubstrioChainArgThe 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) againstkeywhen it fits undermaxper window ofwindowMs, atomically (a fixed window;sliding: trueweighs 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 givescostback, never below zero.graph(label: string): SubstrioGraphA graph of edges between node ids.
graphLabels(): string[]The labels of the app's graphs.
transaction<R>(fn: (tx: SubstrioTransaction) => R): RRuns 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): numberThe declared permission
name's catalog bit (what requirePermission, holdsPermission and requirePersonOrPermission take aspermission). Throws a TypeError for a name the tenant never declared.requirePermission(permission: number, auth: SubstrioPermissionAuth, organization: string): voidThrows unless
auth(typically request.auth) holdspermission(db.permission(name)'s bit) inorganization(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 missingpermissionthrows a TypeError.holdsPermission(permission: number | bigint, auth: SubstrioPermissionAuth, organization: string): booleanrequirePermission's decision as a boolean, never a refusal: true when
authholdspermissioninorganization(nobody signed in or not held is false).permissionmay 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): bigintEvery permission
authholds inorganization, 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): voidrequirePermission(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): voidThe
verifiedroute 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): numberThe 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): Uint32ArrayuserIdOf 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: SubstrioLedgerThe double-entry ledger (LEDGER L3).
[collection: string]: any
SubstrioTransaction interface
interface SubstrioTransaction
sql(text: string, params?: SubstrioSqlParam[]): SubstrioSqlRow[] | SubstrioSqlResultprepare(text: string): SubstrioStatementcollection(name: string): anydb.collection(name): the same collection object.
[collection: string]: anytx.<name> is db.<name>.
db global
declare const db: SubstrioDb;
SubstrioStatement interface
interface SubstrioStatement
all(...args: SubstrioSqlParam[]): SubstrioSqlRow[]Every row.
first(...args: SubstrioSqlParam[]): SubstrioSqlRow | nullThe 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): SubstrioStatementcursor(): SubstrioCursorjson(...args: SubstrioChainArgument[]): SubstrioJsonBodyThe 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[]): SubstrioJsonBodyThe 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.
$eq?: SubstrioFilterEquality$ne?: SubstrioFilterEquality$gt?: number | string | Date$gte?: number | string | Date$lt?: number | string | Date$lte?: number | string | Date$in?: SubstrioFilterEquality[]$nin?: SubstrioFilterEquality[]$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.
$eq?: SubstrioFilterEquality$ne?: SubstrioFilterEquality$gt?: number | string | Date$gte?: number | string | Date$lt?: number | string | Date$lte?: number | string | Date$in?: SubstrioFilterEquality[]$nin?: SubstrioFilterEquality[]$exists?: boolean$startsWith?: string$not?: SubstrioFieldOperators$regex?: string$options?: '' | 'i'$all?: (string | number | boolean)[]The array field holds every one of the values (containsAll).
$size?: numberThe 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.
$and?: SubstrioFilterObject[]$or?: SubstrioFilterObject[]$nor?: SubstrioFilterObject[][field: string]: SubstrioFilterEquality | SubstrioFieldOperators | SubstrioFilterObject[] | undefined
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?: numberskip?: numberselect?: string[]Projection answers plain objects of these fields, rather than records.
after?: string | nullNaming 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: Rowsnext: string | nullprev: 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(): booleanWrites 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): booleanset(field: string, value: unknown): thismerge(fields: Record<string, unknown>): thisAssigns own fields in memory; save() writes them.
inc(field: string, delta?: number | bigint): thisExact integer addition (BigInt beyond 2^53); TypeError for nonnumbers, RangeError beyond int64.
keys(): string[]typeOf(field: string): string | undefinedThe stored type of a field ("string", "number", "boolean", "date", "null", "object", "array"); undefined when absent.
push(field: string, value: unknown): thispull(field: string, value: unknown): thisdirty(): booleanWhether this row has unsaved changes.
unset(field: string): thisdelete(): booleanDeletes the stored document.
reload(): thisRe-reads the stored row and drops unsaved changes.
revert(): thisAlias of reload.
toggle(field: string): thistoJSON(): 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): thistoArray(): SubstrioRecord<T>[]toJSON(): Record<string, unknown>[]project(fields: string[]): Record<string, unknown>[]first(): SubstrioRecord<T> | undefinedlast(): SubstrioRecord<T> | undefinedids(): string[]pluck(field: string): unknown[]reduce<U>(fn: (acc: U | undefined, record: SubstrioRecord<T>, index: number, result: this) => U): U | undefinedWithout 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): UsaveAll(): numberSaves rows with unsaved changes; reports the first conflict while other current rows may still save.
deleteAll(): numberDeletes each row's stored document; returns the number removed.
setAll(field: string, value: unknown): numberincAll(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>all(...args: SubstrioChainArgument[]): SubstrioQueryResult<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: SubstrioFindCursorOptions): SubstrioFindPage<SubstrioQueryResult<T> | Partial<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> | nullinsert(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,_updatedAtand_versionare 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> | nullA record, or null if no document has the id.
findById(id: SubstrioDocumentId): SubstrioRecord<T> | nullAlias of get.
live(id: SubstrioDocumentId | number | bigint): (T & { _id: string }) | nullThe 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
code0x4012 (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 }) | undefinedlive(id) for an integer id from 1 to 2^32 - 2 (an array index):
db.users[id]reads,id in db.usersasks,db.users[id] = docreplaces a stored document (a missing id throws a TypeError, nothing written) anddelete 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): CBinds
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> | nullSets plain fields by id.
update(id: SubstrioDocumentId, ops: SubstrioUpdate, options?: SubstrioWriteOptions): SubstrioRecord<T> | nullRow 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): numberNumber written; a filter object uses updateWhere. Missing ids are not counted.
deleteMany(ids: SubstrioDocumentId[] | SubstrioFilter): numberNumber removed; a filter object uses deleteWhere. Missing ids are not counted.
findOneAndUpdate(filter: SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): SubstrioRecord<T> | nullUpdates the first match; returns the row after the write or null.
findOneAndDelete(filter: SubstrioFilter): (T & { _id: string }) | nullRemoved document as a plain object, or null; it is no longer a record to save.
replace(id: SubstrioDocumentId, document: Partial<T>): SubstrioRecord<T> | nullReplaces fields, retaining system fields; null if absent.
delete(id: SubstrioDocumentId): booleanTrue when the document existed.
count(filter?: SubstrioFilter): numberexists(filter?: SubstrioFilter): booleanTrue 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
documentinserted;createdsays 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;createdsays which.put(document: SubstrioIdentifiedDocument<T>): SubstrioRecord<T>Stores the document under the
_idit 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 }): numberNumber removed.
updateWhere(filter: SubstrioFilter, ops: SubstrioUpdate, options?: SubstrioWriteOptions): numberNumber written, using the same operators as update.
text(field: string): thisDeclares a text field searched by match.
top(field: string, weight: string): thisDeclares a prefix top-k field used by startsWith(...).top(...).
keyword(field: string, termBytes?: number): thisDeclares 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: numbertoString(): 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?: numberorder?: 'value' | 'count'missing?: booleanafter?: 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: stringfield: stringvalue: stringThe 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: stringfield: string
SubstrioSchemaRule interface
interface SubstrioSchemaRule
A field rule of a collection schema (collection.schema).
field: stringtype?: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'datetime' | 'any'Leave it out, or 'any', for any type.
required?: booleannullable?: booleanmin?: numbermax?: numberpattern?: stringenum?: 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?: booleanrules?: SubstrioSchemaRule[]constraints?: SubstrioSchemaConstraint[]
SubstrioSchemaError interface
interface SubstrioSchemaError extends Error
What a write that breaks a collection's schema throws.
name: 'SchemaError'code: 'schema'collection: stringviolations: { 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: stringcount: numberHow many stored documents break a constraint.
first: stringThe id of the first of them.
SubstrioCollectionDeclarations interface
interface SubstrioCollectionDeclarations
Declarations a collection takes.
schema(): (SubstrioSchemaDefinition & { collection: string; strict: boolean }) | nullReads the collection's schema, or null when it has none.
schema(definition: SubstrioSchemaDefinition & { existing?: 'refuse' | 'allow' }): thisSets the collection's schema, replacing the old one (the same declaration as schema.update; returns the collection).
rulesandconstraints(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, unlessexistingis 'allow'.unique(field: string | string[], options?: { nulls?: 'skip' | 'count'; within?: 'collection' | 'document' | 'both' }): thisDeclares 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
fieldsandvalues).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 othernulls.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 withnullsskip; a compound or counted one is a TypeError, as is declaring the same fields with anotherwithin.expire(field: string, options?: { after?: number; existing?: 'refuse' | 'allow' }): thisDeclares that a document is gone
aftermilliseconds after the date its field (a name or dotted path) holds (after0: 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, unlessexistingis 'allow'. Declaring again replaces the period.dropExpiry(): thisTakes the expiry declaration away (the field's keys stay; a pending sweep is cancelled); returns the collection.
elements(field: string, members: string[]): thisKeys 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[]): booleanTakes 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): thisDeclares a compound index: an equality on every
leadfield and a range or order on theorderfield read one key range (plain top-level field names, none twice).dropCompound(lead: string[], order: string): booleanTakes 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
groupis 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): booleanTakes 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): thisDeclares a one-to-many relation:
keyis the field oftargetholding the id of a document of this collection.hasOne(name: string, target: unknown, key: string): thisDeclares a one-to-one relation:
keyis the field oftargetholding the id of a document of this collection.belongsTo(name: string, target: unknown, key: string): thisDeclares a relation to one document:
keyis the field of this collection holding the id of a document oftarget.belongsToMany(target: unknown, options: SubstrioManyToManyOptions): thisDeclares a many-to-many relation named
as, held by one document per link in thethroughcollection:keyis the field of those documents holding this collection's id andotherKeythe 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): thisDeclares a named scope:
Users.scope('active', c => c.eq('status', 'active')), thenUsers.active()starts a chain with the scope applied andchain.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: unknownas: stringkey?: stringotherKey?: string
SubstrioRecordLinks interface
interface SubstrioRecordLinks
The link methods of a record that has a many-to-many relation declared on its collection.
link(name: string, other: { _id: string } | string): thisLinks this record to
other(a record or its _id) through the relationname: one write on the through collection, none when the link exists. Answers this record.unlink(name: string, other: { _id: string } | string): thisRemoves the link to
otherthrough the relationname: one write on the through collection, none when there is no link. Answers this record.
SubstrioExplainCondition interface
interface SubstrioExplainCondition
One condition of a chain as explain() reports it: groups and elemMatch list their conditions.
op: stringfield?: stringother?: stringThe second field of a field comparison (gtField and the others).
conditions?: SubstrioExplainCondition[]
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' } | nullconditions: SubstrioExplainCondition[]residual: SubstrioExplainCondition[]sort: { field: string; direction: 'asc' | 'desc' }[]skip: numberlimit: number | nulldeclined?: SubstrioExplainDecline
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): thisA binary (an ArrayBuffer or a view such as a Uint8Array) is compared by its bytes: eq, neq, ne, in and nin take it.
neq(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): thisne(field: SubstrioFieldPath, value: SubstrioChainValue | SubstrioBinaryValue): thisAlias of neq.
gt(field: SubstrioFieldPath, value: SubstrioChainValue): thisgte(field: SubstrioFieldPath, value: SubstrioChainValue): thisge(field: SubstrioFieldPath, value: SubstrioChainValue): thisAlias of gte.
lt(field: SubstrioFieldPath, value: SubstrioChainValue): thislte(field: SubstrioFieldPath, value: SubstrioChainValue): thisle(field: SubstrioFieldPath, value: SubstrioChainValue): thisAlias of lte.
in(field: SubstrioFieldPath, values: (SubstrioChainValue | SubstrioBinaryValue)[]): thisnin(field: SubstrioFieldPath, values: (SubstrioChainValue | SubstrioBinaryValue)[]): thisexists(field: SubstrioFieldPath, present?: boolean): thisThe condition that the field is present (present = true) or absent (false).
exists(): booleanWith no argument, a terminal: whether any document matches.
startsWith(field: SubstrioFieldPath, value: SubstrioChainValue): thiscontainsAll(field: string, values: SubstrioChainValue[]): thisThe 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[]): thisThe array field holds at least one of the values as an element. An empty list matches nothing.
size(field: string, n: number): thisThe array field has exactly n elements.
sizeGt(field: string, n: number): thisThe array field has more than n elements.
sizeLt(field: string, n: number): thisThe array field has fewer than n elements.
elemMatch(field: string, filter: Record<string, unknown>): thisThe 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): thisThe 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): thisThe string field holds the text (case sensitive); keyed by trigrams, see endsWith.
icontains(field: SubstrioFieldPath, text: string): thisThe string field holds the text, ASCII letters compared without case; keyed by trigrams, see endsWith.
ieq(field: SubstrioFieldPath, text: string): thisThe 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[]): thisThe string field equals one of the texts, ASCII letters compared without case (see ieq).
istartsWith(field: SubstrioFieldPath, text: string): thisThe string field starts with the text, ASCII letters compared without case (see ieq).
regex(field: string, pattern: string | RegExp): thisThe string field matches the pattern somewhere (a string or RegExp, ECMAScript grammar; only the i flag).
eqField(field: string, other: string): thisField 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): thisgtField(field: string, other: string): thisgteField(field: string, other: string): thisltField(field: string, other: string): thislteField(field: string, other: string): thisonDay(field: string, date: Date | number, zone?: number): thisThe 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): thisThe field falls in the month (1 to 12) of the year, UTC or in the zone.
lastDays(field: string, n: number, zone?: number): thisThe 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> | nullOne document of the answer at random, or null when none match.
each(fn: (document: SubstrioChainRow<T, Shape>, index: number) => unknown, ...args: SubstrioChainArgument[]): numberfn(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[]): numberfn(documents, page) for each page of n documents of the answer, stopping when fn returns false. Returns the documents given.
explain(...args: SubstrioChainArgument[]): SubstrioExplainHow the chain is answered (runs it once).
match(field: string, text: SubstrioChainValue, options?: { mode?: 'all' | 'any' } | 'all' | 'any'): thisThe 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): thisThe 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): thisThe 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): thisThe 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[]): thisThe documents whose point lies inside the box:
lowandhighare 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): thisThe documents whose point lies within
radius(above 0) ofcenter, a non-empty array of numbers. The field has a geo or vector index.sparseKnn(field: SubstrioFieldPath, vector: { indices: number[]; values: number[] }, k: number): thisThe k documents of greatest dot product with a sparse vector (
indicesandvaluesof one length, not empty), ranked best first. The field has a sparse index.textNear(field: SubstrioFieldPath, text: string, fuzzy?: boolean | number): thisThe 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' }): thisThe 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[]): thisor(...conditions: SubstrioChainBuilder[]): thisnot(...conditions: SubstrioChainBuilder[]): thiswhere(filter: SubstrioFilter): thisThe 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): thisdirection: 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)?]>): thisSeveral sort keys (chained
sortcalls, 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): thisAlias of sort.
sort_by(keys: Array<string | [string, (1 | -1 | 'asc' | 'desc' | boolean)?]>): thisgroup_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): thisskip(count: number): thisclaim(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 appliesops(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[]): numberApplies
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[]): numberDeletes every document the chain answers (the same documents as ids()); returns the number removed.
increment(field: string, n?: number | bigint, ...args: SubstrioChainArgument[]): numberupdate({ $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[]): numberupdate({ $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] | undefinedThe 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[]): unknownmissing(): booleanThe 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).
nextandprevare opaque cursors bound to this query: passnextasafterfor the following page (null on the last page),prevasbeforefor the page ahead. A page at any depth costs what the first page costs on a keyed number sort.skipcannot 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
asnull.with(...relations: (string | string[] | ((q: SubstrioChain<T>) => SubstrioChain<T>))[]): thisEach 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): thisThe 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): thisThe documents whose number of related records of a hasMany or hasOne relation compares to
n('>=', '>', '=', '<', '<=';na 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): thisThe documents that have no related record of the relation (matching the function's chain when given).
withCount(...relations: (string | string[])[]): thisEach 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: truealso 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
ndocuments of each group ofgroupFieldinsortField's order (direction 1, -1, 'asc' or 'desc'; ascending by default), the groups in value order. Each group is an ordered key walk stopped atnrows, 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[]): numberHow 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): thisSQL 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,%abcendsWith,%abc%contains andabceq (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): thislike folding ASCII letters only (as icontains does):
Éandédiffer.between(field: SubstrioFieldPath, low: SubstrioChainValue, high: SubstrioChainValue): thislow <= 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): thisSQL 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): thisSQL semantics apply: NOT IN over another chain's field.
existsQuery(chain: SubstrioChain<any, any>): thisSQL 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> | nulljson(...args: SubstrioChainArgument[]): SubstrioJsonBodyThe 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[]): SubstrioJsonBodyThe one object first() answers, or the four bytes
null, as a SubstrioJsonBody. Same restrictions as json().last(...args: SubstrioChainArgument[]): SubstrioChainRow<T, Shape> | nullThe 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 : numberThe 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
aliasand 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 : numberThe 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
aliasand answers the chain; avg, min and max do the same (aliasesavg_<field>,min_<field>,max_<field>).avg(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | nullThe mean of a field's numbers, null when no matching document holds one.
min(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | nullThe smallest of a field's numbers, null when there is none.
max(field: string, ...args: SubstrioChainArgument[]): Grouped extends true ? this : number | nullThe largest of a field's numbers, null when there is none.
values(field: string, options?: SubstrioValuesOptions, ...args: SubstrioChainArgument[]): SubstrioValueCountsEach 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 | nullput(key: string, value: string | number | boolean | bigint | Uint8Array | ArrayBuffer): booleandelete(key: string): booleanpeek(key: string): numberThe stored length in bytes, or -1. Allocates nothing.
ref(key: string): numberThe 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): booleanWhether the key holds a value. A lookup only: the value is not read, nothing is allocated.
getBytes(key: string): Uint8Array | nullA fresh Uint8Array copy of the value, or null. One allocation; ref is the zero-allocation form.
increment(key: string, delta?: number): number | undefinedAtomically 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 | undefinedundefined when absent or not 8 bytes; throws a RangeError for a stored integer outside the safe range (never a BigInt).
getF64(key: string): number | undefinedundefined 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): booleanOnly an integral number within the safe range; anything else (a BigInt too) throws a TypeError.
putF64(key: string, value: number): booleanAny number; a non-number throws a TypeError.
cursor(): SubstrioCursor
SubstrioCursor interface
interface SubstrioCursor
An ordered cursor over a tree's keys.
readonly valid: booleanWhether the cursor stands on an entry: false before its first seek, past either end and once closed.
seekFirst(): booleanseekLast(): booleanseekPrefix(prefix: string): booleanseekGE(key: string): booleanseekLE(key: string): booleanseekLT(key: string): booleanseekPrefixLast(prefix: string): booleannext(): booleanprev(): booleankey(): string | nullvalue(): string | nullvalue(field: SubstrioFieldPath): unknownA collection cursor only: the one field of the document the cursor stands on (a nested
a.bpath reads down, as a chain's ownvalue/pluckdo), 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): numberclose(): 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): booleanremoveEdge(from: number, to: number): booleanhasEdge(from: number, to: number): booleanneighbors(node: number, direction?: string): number[]degree(node: number): numberstats(): 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?: stringAn idempotency key: posting the same key twice answers EXISTING, not a second entry.
kind: SubstrioLedgerIndexledger.kind(name)'s answer.
party: stringcurrency: stringamount: numberMinor units; an integer exact in a safe int64, never rounded.
type: SubstrioLedgerIndexledger.type.CREDIT, DEBIT, TRANSFER_IN or TRANSFER_OUT (REVERSAL is reached only through ledger.reverse).
status?: SubstrioLedgerIndexledger.status.PENDING (default) or CLEARED (VOIDED is reached only through ledger.void).
category?: SubstrioLedgerIndexledger.category(name)'s answer.
reference?: stringscope?: stringorg?: stringcounterparty?: stringclear_at?: numberEpoch ms; 0 takes a prior clear time away (ledger.clearAt's own rule).
external_ref?: stringdescription?: stringdata?: 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: numbercleared: numberpending_rows: numbercleared_rows: number
SubstrioLedgerTopBalance interface
interface SubstrioLedgerTopBalance
account_kind: SubstrioLedgerIndexparty: stringamount: number
SubstrioLedgerAccount interface
interface SubstrioLedgerAccount
party: stringamount: number
SubstrioLedgerTotalsRow interface
interface SubstrioLedgerTotalsRow
account_kind: SubstrioLedgerIndexcategory?: SubstrioLedgerIndexPresent only when the group's own entries named one.
type: SubstrioLedgerIndexstatus: SubstrioLedgerIndexamount: numberrows: number
SubstrioLedgerAccountTotalsRow interface
interface SubstrioLedgerAccountTotalsRow
party: stringcategory?: SubstrioLedgerIndextype: SubstrioLedgerIndexstatus: SubstrioLedgerIndexfirst?: booleanTrue on the groups of entries that hold their (kind, party, reference)'s first credit.
amount: numberrows: number
SubstrioLedgerClearDue interface
interface SubstrioLedgerClearDue
cleared: numbermore: boolean
SubstrioLedgerEntriesPage interface
interface SubstrioLedgerEntriesPage
entries: SubstrioDoc[]next_before: numbermore: 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): SubstrioLedgerIndexThe declared kind
name's catalog index, forkindelsewhere. Throws a TypeError for an undeclared name.category(name: string): SubstrioLedgerIndexThe declared category
name's catalog index, forcategoryelsewhere. Throws a TypeError for an undeclared name.post(entries: SubstrioLedgerPostEntry[]): SubstrioLedgerPostResultPosts 1 to 1000 entries as one call;
keymakes a repeat post idempotent (EXISTING, not a second entry). Throws LedgerRefused naming the first invalid entry (with itsindex), or LedgerWriteFailed.balance(kind: SubstrioLedgerIndex, party: string, currency: string, org?: string): SubstrioLedgerBalanceThe account's pending/cleared sums and row counts in
currency, over every organization or, givenorg, one.topBalances(currency: string, n: number): SubstrioLedgerTopBalance[]The
n(1 to 1000) largest account balances incurrency, largest first.accounts(kind: SubstrioLedgerIndex, currency: string): SubstrioLedgerAccount[]Every account of
kindincurrency(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
kindincurrency(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 (ms0). Same refusals as clear().clearDue(limit?: number): SubstrioLedgerClearDueClears every pending entry whose own clear_at is due (at most
limit, default 1000, 1 to 10000).moreis true when entries pastlimitare 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): SubstrioLedgerPostResultPosts 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 | nullThe plain entry object, or null (no such entry).
idOf(key: string): stringThe id text
keynames, 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): SubstrioLedgerEntriesPageA page of entries by
by(ledger.by.PARTY/REFERENCE/SCOPE/ORG; ALL walks the whole ledger, newest first, andvaluemust 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.orgnarrows a ledger.by.PARTY walk to that party's entries at one organization; any otherby, 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: stringsize: numberhash: stringtype: string
SubstrioFileStat interface
interface SubstrioFileStat extends SubstrioFileMeta
cacheControl: stringpublic: booleancreatedAt: numberupdatedAt: number
SubstrioFilePutOptions interface
interface SubstrioFilePutOptions
type?: stringcacheControl?: stringpublic?: boolean
SubstrioFileResponseInit interface
interface SubstrioFileResponseInit
status?: numberrange?: stringheaders?: 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): SubstrioFileMetaStores
dataatpath, replacing it if it exists.stat(path: string): SubstrioFileStat | nullThe full metadata of the stored file, or null (no such path).
get(path: string): Uint8Array | nullThe stored bytes, or null (no such path).
remove(path: string): booleanRemoves the file: true, or false (no such path).
response(path: string, init?: SubstrioFileResponseInit): Response | nullThe file as a Response (its body streamed with no native copy past the store's own read), or null (no such path).
init.rangeis 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 | nullBytes [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: stringport?: numbersecure?: booleanTLS from the first byte (port 465); otherwise STARTTLS when the server offers it.
user?: stringpass?: stringfrom: stringto: string | string[]One address, or an array of them (each its own RCPT).
message: stringThe whole message, headers and body (RFC 5322).
requireTLS?: booleanRefuse a server that does not offer TLS (default true).
verifyTLS?: booleanVerify the server's certificate (default true).
encode?: booleanDot-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: SubstrioCmdThe same tools as the global cmd.
api(method: string, path: string, body?: string | Uint8Array | JsonValue): anyCalls 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 } | nullThe 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 ifmethodandurl(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: SubstrioFilesThe 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 acode: 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): SubstrioKvgraph(label: string): SubstrioGraphgraphLabels(): 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): SubstrioQueueA 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?: numberThe 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): SubstrioLegacyCollectionCloudflare-shaped helpers over the same database.
batch(operations: unknown[], options?: unknown): anycollections(): anysearch(collection: string, query: string | Record<string, unknown>, options?: Record<string, unknown>): anyText, vector or hybrid search over a collection.
readonly objects: SubstrioObjectsDurable objects: every
export class Nameof 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): SubstrioObjectIdThe id the text always names (the first 16 bytes of sha256 of the class and the text).
newUniqueId(): SubstrioObjectIdAn id nobody named: random.
idFromString(hex: string): SubstrioObjectIdThe id of 32 hex characters (its toString).
get(id: SubstrioObjectId): SubstrioObjectStubThe stub of an object: fetch(request) calls its fetch, any other member calls that method with the arguments.
SubstrioObjectId interface
interface SubstrioObjectId
readonly cls: stringreadonly hex: stringreadonly name: string | undefinedtoString(): stringequals(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: SubstrioObjectStoragereadonly objects: SubstrioObjectsThe 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>): voidacceptWebSocket(socket: SubstrioObjectSocket): voidgetWebSockets(): 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: numbersend(data: string | ArrayBuffer | ArrayBufferView): voidclose(code?: number, reason?: string): void
SubstrioLegacyCollection interface
interface SubstrioLegacyCollection
add(doc: unknown): anyget(id: string): anyset(id: string, doc: unknown): anyupdate(id: string, ops: unknown): anydelete(id: string): anyfind(filter?: unknown, options?: unknown): { data: any[]; count: number }findOne(filter?: unknown): anycount(filter?: unknown): numberpipeline(stages: unknown[]): anystats(): anydrop(): anycursor(): SubstrioCursorfindById(id: string): anylive(id: string | number | bigint): anyThe 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]: anydb.<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: anyclass User extends db.<name>.Model: new User(doc) inserts and answers a live User; User.live, User.where(...).
use(klass: any): anyBinds a class that extends Model: every live row of the collection is then an instance of it.
exists(filter?: unknown): booleancreate(doc: unknown): anyupsert(filter: unknown, doc: unknown): anyput(doc: unknown): anyputMany(docs: unknown[]): string[]deleteWhere(filter?: unknown): numberupdateWhere(filter: unknown, fields: unknown): numberexpire(field: string, options?: { after?: number; existing?: 'refuse' | 'allow' }): thisA document is gone
afterms after the date its field holds (0: the date is the time); expired documents are hidden from every read and then deleted.dropExpiry(): thisTakes the expiry declaration away.
elements(field: string, members: string[]): thisKeys 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[]): booleanTakes 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): thisDeclares a compound index: an equality on every
leadfield and a range or order on theorderfield read one key range (plain top-level field names, none twice).dropCompound(lead: string[], order: string): booleanTakes 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
groupis 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): booleanTakes 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: numbersignal: numberstdout: Uint8Arraystderr: Uint8Arrayfiles: Array<{ path: string; size: number; hash: string }>cpuMs: numberwallMs: numbertext(): stringstdout 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[]): voidwarn(...args: unknown[]): voiderror(...args: unknown[]): voidinfo(...args: unknown[]): voiddebug(...args: unknown[]): voidtime(label?: string): voidtimeEnd(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?: stringcrv?: stringuse?: stringext?: booleankey_ops?: CryptoKeyUsage[]x?: stringy?: stringd?: stringn?: stringe?: stringp?: stringq?: stringdp?: stringdq?: stringqi?: stringk?: 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 & {})hash?: CryptoHashIdentifiernamedCurve?: CryptoCurveNamelength?: numberHMAC and AES key bits; the AES-CTR counter bits; none for deriveBits (that length is an argument).
modulusLength?: numberpublicExponent?: Uint8ArraysaltLength?: numberRSA-PSS salt bytes (required to sign and verify).
iv?: BufferSourceadditionalData?: BufferSourcetagLength?: numbercounter?: BufferSourcelabel?: BufferSourcesalt?: BufferSourceinfo?: BufferSourceiterations?: numberpublic?: CryptoKeyECDH and X25519: the peer's public key.
CryptoKey interface
interface CryptoKey
readonly type: 'secret' | 'public' | 'private'readonly extractable: booleanreadonly algorithm: { name: string; hash?: { name: CryptoHashName }; length?: number; namedCurve?: CryptoCurveName; modulusLength?: number; publicExponent?: Uint8Array; }readonly usages: CryptoKeyUsage[]
CryptoKeyPair interface
interface CryptoKeyPair
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): TrandomUUID(): stringsubtle: { 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): anyreadonly 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): Uint8ArrayencodeInto(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): stringInvalid 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 | nullset(name: string, value: string): voidhas(name: string): booleandelete(name: string): voidappend(name: string, value: string): voidforEach(callback: (value: string, name: string) => void): void
HeadersInit type
type HeadersInit = Headers | [string, string][] | Record<string, string>;
RequestInit interface
interface RequestInit
method?: stringheaders?: HeadersInitbody?: string | Uint8Arraysignal?: AbortSignal
Request class
declare class Request
constructor(input: string | Request, init?: { method?: string; headers?: HeadersInit; body?: string | Uint8Array; signal?: AbortSignal })readonly url: stringreadonly signal: AbortSignalinit.signal, else the copied request's, else a signal that never aborts. fetch(request) honours it.
readonly method: stringreadonly headers: Headersreadonly body: ReadableStream<Uint8Array> | string | nullThe 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: numberreadonly statusText: stringreadonly ok: booleanreadonly headers: Headersreadonly body: anyA ReadableStream for a streamed fetch response or one you built; the bytes or string given to the constructor otherwise.
readonly bodyUsed?: booleantrue 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: stringprotocol: stringhost: stringhostname: stringport: stringpathname: stringsearch: stringhash: stringreadonly origin: stringreadonly searchParams: URLSearchParamstoString(): string
URLSearchParams class
declare class URLSearchParams
constructor(init?: string | [string, string][] | Record<string, string>)get(name: string): string | nullhas(name: string): booleanset(name: string, value: string): voidappend(name: string, value: string): voiddelete(name: string): voidgetAll(name: string): string[]forEach(callback: (value: string, name: string) => void): voidentries(): 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: stringreadonly lastModified: number
FormData class
declare class FormData
Event class
declare class Event
constructor(type: string)preventDefault(): voidstopPropagation(): void
EventTarget class
declare class EventTarget
ReadableStreamController interface
interface ReadableStreamController<R = unknown>
enqueue(chunk: R): voidclose(): voiderror(reason?: unknown): voidreadonly desiredSize: number | null
ReadableStreamReader interface
interface ReadableStreamReader<R = unknown>
read(): Promise<{ done: boolean; value?: R }>cancel(reason?: unknown): Promise<void>releaseLock(): voidreadonly 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: booleangetReader(): ReadableStreamReader<R>cancel(reason?: unknown): Promise<void>tee(): [ReadableStream<R>, ReadableStream<R>]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: booleangetWriter(): { 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: ReadableStreamreadonly writable: WritableStream
TransformStreamController interface
interface TransformStreamController
enqueue(chunk: unknown): voiderror(reason?: unknown): voidterminate(): voidreadonly 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: stringreadonly 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: booleanreadonly reason: anyonabort: ((event: { type: 'abort'; target: AbortSignal }) => void) | nulladdEventListener(type: 'abort', listener: (event: { type: 'abort'; target: AbortSignal }) => void, options?: { once?: boolean }): voidremoveEventListener(type: 'abort', listener: (event: { type: 'abort'; target: AbortSignal }) => void): voidthrowIfAborted(): voidstatic abort(reason?: any): AbortSignalAn already aborted signal.
static timeout(delayMs: number): AbortSignalAborts with a DOMException named TimeoutError after delayMs (a timer of the call).
static any(signals: Iterable<AbortSignal>): AbortSignalAborts when any of the signals does, with its reason.
AbortController class
declare class AbortController
readonly signal: AbortSignalabort(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): SubstrioD1Statementbatch(statements: SubstrioD1Statement[]): Promise<unknown[]>exec(sql: string): Promise<unknown>
D1Statement class
declare class D1Statement implements SubstrioD1Statement
bind(...values: unknown[]): SubstrioD1Statementfirst(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: SubstrioEnvstdout: { 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): Buffernew (array: ArrayLike<number> | Iterable<number>): Buffernew (buffer: ArrayBufferLike, byteOffset?: number, length?: number): Bufferreadonly prototype: Bufferreadonly BYTES_PER_ELEMENT: numberof(...items: number[]): BufferInherited Uint8Array.of constructs this subclass.
from(input: string | ArrayBuffer | ArrayBufferView | number[], encoding?: 'hex' | 'base64' | 'utf8' | string): Bufferalloc(size: number): Bufferconcat(list: Uint8Array[], totalLength?: number): BufferisBuffer(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.
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: SubstrioEnvThe 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 function atob(data: string): stringexport function btoa(data: string): stringexport 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[]) => anyclass 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: Pathexport = 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 }): ParsedQsexport function stringify(obj: Record<string, any>, sep?: string, eq?: string, options?: { encodeURIComponent?: (s: string) => string }): stringexport function escape(str: string): stringexport function unescape(str: string): stringexport const decode: typeof parseexport 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): Urlexport function format(url: URL | Partial<Url> | string, options?: { auth?: boolean; fragment?: boolean; search?: boolean }): stringexport function resolve(from: string, to: string): stringexport function fileURLToPath(url: string | URL): stringexport function pathToFileURL(path: string): URLexport 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[]): stringexport function formatWithOptions(options: object, format?: any, ...args: any[]): stringexport function inspect(value: any, options?: { depth?: number | null; maxArrayLength?: number | null; breakLength?: number }): stringexport 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[]) => voidexport function inherits(constructor: Function, superConstructor: Function): voidexport function deprecate<T extends Function>(fn: T, message: string, code?: string): Texport function isDeepStrictEqual(a: unknown, b: unknown): booleanexport function stripVTControlCharacters(text: string): stringexport 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.isArrayexport function isBoolean(v: unknown): v is booleanexport function isNull(v: unknown): v is nullexport function isNullOrUndefined(v: unknown): v is null | undefinedexport function isNumber(v: unknown): v is numberexport function isString(v: unknown): v is stringexport function isSymbol(v: unknown): v is symbolexport function isUndefined(v: unknown): v is undefinedexport function isRegExp(v: unknown): v is RegExpexport function isObject(v: unknown): booleanexport function isDate(v: unknown): v is Dateexport function isError(v: unknown): v is Errorexport function isFunction(v: unknown): v is Functionexport function isPrimitive(v: unknown): booleanexport const TextEncoder: typeof globalThis.TextEncoderexport 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').typesexport = 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: Assertexport = 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.strictexport = 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 | ArrayBuffertype 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): Hashexport function createHmac(algorithm: DigestName, key: BinaryLike): Hmacexport function hash(algorithm: DigestName, data: BinaryLike, outputEncoding?: OutputEncoding | 'buffer'): string | Bufferexport function randomBytes(size: number): Bufferexport function randomBytes(size: number, callback: (err: Error | null, buf: Buffer) => void): voidexport const pseudoRandomBytes: typeof randomBytesexport function randomFillSync<T extends ArrayBufferView>(buffer: T, offset?: number, size?: number): Texport function randomInt(max: number): numberexport function randomInt(min: number, max: number): numberexport function randomInt(min: number, max: number, callback: (err: Error | null, value: number) => void): voidexport function randomUUID(): stringexport function getRandomValues<T extends ArrayBufferView>(array: T): Texport function timingSafeEqual(a: ArrayBufferView, b: ArrayBufferView): booleanexport function createSign(algorithm: DigestName): Signexport function createVerify(algorithm: DigestName): Verifyexport function sign(algorithm: DigestName | null, data: BinaryLike, key: PemKey): Bufferexport function verify(algorithm: DigestName | null, data: BinaryLike, key: PemKey, signature: BinaryLike): booleanexport function pbkdf2Sync(password: BinaryLike, salt: BinaryLike, iterations: number, keylen: number, digest?: DigestName): Bufferexport function pbkdf2(password: BinaryLike, salt: BinaryLike, iterations: number, keylen: number, digest: DigestName, callback: (err: Error | null, key?: Buffer) => void): voidexport 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 cryptoexport const subtle: typeof crypto.subtleexport { Hash, Hmac, Sign, Verify }
crypto module
declare module 'crypto'
export * from 'node:crypto'