@formancy/data-server 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +156 -0
- package/NOTICE +9 -0
- package/README.md +619 -2
- package/dist/drivers-DtJsZ_HW.mjs +3328 -0
- package/dist/drivers-DtJsZ_HW.mjs.map +1 -0
- package/dist/identity-keys-cli.d.mts +1 -0
- package/dist/identity-keys-cli.mjs +69 -0
- package/dist/identity-keys-cli.mjs.map +1 -0
- package/dist/identity-settings-BlQY9gHy.mjs +165 -0
- package/dist/identity-settings-BlQY9gHy.mjs.map +1 -0
- package/dist/index.d.mts +733 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/main.d.mts +1 -0
- package/dist/main.mjs +189 -0
- package/dist/main.mjs.map +1 -0
- package/dist/secrets-DhKHG-Y9.mjs +689 -0
- package/dist/secrets-DhKHG-Y9.mjs.map +1 -0
- package/package.json +52 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drivers-DtJsZ_HW.mjs","names":["FORM_ID","routeKey","isRecord","FORM_ID","isRecord","isRecord","isRecord","isRecord","isRecord","isRecord","isRecord","isRecord","isRecord"],"sources":["../src/config-store.ts","../src/audit.ts","../src/lookup-deadline.ts","../src/generation.ts","../src/served-schema.ts","../src/bundle-format2.ts","../src/bundle.ts","../src/published.ts","../src/routes/runtime-errors.ts","../src/routes/write-once.ts","../src/routes/runtime-deletes.ts","../src/routes/runtime-lists.ts","../src/routes/abandoned.ts","../src/routes/runtime-lookups.ts","../src/routes/runtime-menu.ts","../src/routes/runtime.ts","../src/cors.ts","../src/paths.ts","../src/rate-limit.ts","../src/reassigned.ts","../src/publishing.ts","../src/routes/admin-audit.ts","../src/routes/admin-evolution.ts","../src/routes/admin-promotion.ts","../src/routes/admin.ts","../src/app.ts","../src/connections.ts","../src/drivers.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { link, mkdir, open, readdir, readFile, rm, stat } from 'node:fs/promises'\nimport { join } from 'node:path'\n\nexport type PublishOutcome =\n | { ok: true; version: number }\n /** Somebody published first. `current` is what they published, so the caller can rebase and retry. */\n | { ok: false; reason: 'conflict'; current: number | null }\n\n/**\n * Where published form bundles live: outside every customer's business tables.\n *\n * A port because a second implementation is already planned — a shared store\n * with compare-and-swap for more than one server instance (plan section 5) —\n * and `config-store.test.ts` is written against this interface so that store\n * can run the same cases. Today there is one, on a directory.\n *\n * Versions are immutable once published, as formancy's are\n * (formancy.ai 0025): publishing never edits, it adds the next number.\n */\nexport interface ConfigurationStore {\n /** The newest published version, or `null` before the first. */\n latest(id: string): Promise<number | null>\n /** Every published version, ascending; `[]` for an id never published (0030). */\n versions(id: string): Promise<number[]>\n /**\n * One published version, or `undefined` if it does not exist. A version\n * that exists and does not parse -- edited by hand, or damaged -- throws\n * `UnparsableVersionError`, so a caller can tell it from a read that\n * failed.\n */\n read(id: string, version: number): Promise<unknown>\n /**\n * Publish the next version, only if the newest is still `expectedBase`.\n * Two administrators publishing from the same base: one wins, the other is\n * told, and nothing is overwritten.\n */\n publish(id: string, expectedBase: number | null, value: unknown): Promise<PublishOutcome>\n /** Every id with at least one version, in codepoint order. */\n list(): Promise<string[]>\n}\n\n/** What `read` throws for a version that exists and does not parse. Its message names the file. */\nexport class UnparsableVersionError extends Error {\n override name = 'UnparsableVersionError'\n}\n\n/**\n * An id is a directory name, so it is checked before it touches a path: a\n * lower-case letter or digit first, then those, dot, hyphen, underscore. `..`\n * cannot start with a letter or digit, and nothing here has a slash.\n *\n * **Lower case only**, because NTFS and APFS are case-insensitive: there,\n * `Order` and `order` are one directory, and reading one id would silently\n * return the other's bundle. Found by this suite on Windows; Linux CI would\n * never have shown it.\n */\nexport const CONFIGURATION_ID_MAX_LENGTH = 128\nexport const CONFIGURATION_ID = new RegExp(`^[a-z0-9][a-z0-9._-]{0,${String(CONFIGURATION_ID_MAX_LENGTH - 1)}}$`)\nconst VERSION_FILE = /^([1-9][0-9]*)\\.json$/\n\nfunction assertId(id: string): void {\n if (!CONFIGURATION_ID.test(id)) throw new Error(`${JSON.stringify(id)} is not a configuration id`)\n}\n\nfunction isCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code\n}\n\n/**\n * A configuration store on a directory — a persistent volume in a container.\n *\n * Layout: `<root>/<id>/<version>.json`, pretty-printed, one file per version,\n * so an export is a copy of the directory and a review is a diff.\n *\n * **Compare-and-swap without a lock.** A version is written to a temporary file,\n * flushed, and then hard-linked to its final name. `link` fails if the name\n * exists, atomically, so of two writers that both read version 3 as the newest,\n * exactly one creates 4.json and the other gets a conflict. Nobody ever sees a\n * half-written version, because the final name only appears once the bytes are\n * on disk. A crash leaves a temporary file that nothing reads.\n *\n * On a local filesystem — ext4, xfs, NTFS — `link` is atomic. Over NFS it is\n * atomic on the server but the error a client sees after a retransmission can\n * be wrong, which is why the plan limits this store to a single-instance pilot.\n */\nexport function createFileConfigurationStore(root: string): ConfigurationStore {\n /**\n * Whether the root does not exist yet — a fresh volume, an empty store — and\n * a loud failure if it exists and is not a directory.\n *\n * Asked whenever something under the root is missing, because \"missing\"\n * means two different things. On Windows a path through a FILE reports\n * ENOENT, not ENOTDIR, so a volume path that names a file would otherwise\n * make every published form look unpublished.\n */\n async function rootIsMissing(): Promise<boolean> {\n try {\n if (!(await stat(root)).isDirectory()) throw new Error(`${root} is not a directory; the configuration store needs one`)\n return false\n } catch (error) {\n if (isCode(error, 'ENOENT')) return true\n throw error\n }\n }\n\n async function versions(id: string): Promise<number[]> {\n assertId(id)\n let names: string[]\n try {\n names = await readdir(join(root, id))\n } catch (error) {\n if (!isCode(error, 'ENOENT') && !isCode(error, 'ENOTDIR')) throw error\n await rootIsMissing()\n return []\n }\n // Only the final names: a `.publishing-*` leftover of a crashed publish is never a version.\n const found: number[] = []\n for (const name of names) {\n const match = VERSION_FILE.exec(name)\n if (match?.[1] !== undefined) found.push(Number(match[1]))\n }\n return found.sort((a, b) => a - b)\n }\n\n async function latest(id: string): Promise<number | null> {\n return (await versions(id)).at(-1) ?? null\n }\n\n return {\n latest,\n versions,\n\n async read(id, version) {\n assertId(id)\n if (!Number.isSafeInteger(version) || version < 1) throw new Error(`${String(version)} is not a version`)\n const path = join(root, id, `${String(version)}.json`)\n let text: string\n try {\n text = await readFile(path, 'utf8')\n } catch (error) {\n if (!isCode(error, 'ENOENT') && !isCode(error, 'ENOTDIR')) throw error\n await rootIsMissing()\n return undefined\n }\n try {\n return JSON.parse(text) as unknown\n } catch {\n // A published version that does not parse was edited by hand or\n // damaged. Serving something in its place would be guessing.\n throw new UnparsableVersionError(`${path} is not valid JSON; a published version must never be edited`)\n }\n },\n\n async publish(id, expectedBase, value) {\n const current = await latest(id)\n if (current !== expectedBase) return { ok: false, reason: 'conflict', current }\n\n const directory = join(root, id)\n await mkdir(directory, { recursive: true })\n const next = (current ?? 0) + 1\n const temporary = join(directory, `.publishing-${randomUUID()}`)\n const handle = await open(temporary, 'wx')\n try {\n await handle.writeFile(`${JSON.stringify(value, null, 2)}\\n`, 'utf8')\n // On disk before it has a name: a version that exists must be whole.\n await handle.sync()\n } finally {\n await handle.close()\n }\n\n try {\n await link(temporary, join(directory, `${String(next)}.json`))\n return { ok: true, version: next }\n } catch (error) {\n if (isCode(error, 'EEXIST')) return { ok: false, reason: 'conflict', current: await latest(id) }\n throw error\n } finally {\n await rm(temporary, { force: true })\n }\n },\n\n async list() {\n if (await rootIsMissing()) return []\n const names = await readdir(root)\n const ids: string[] = []\n for (const name of names) {\n if (CONFIGURATION_ID.test(name) && (await latest(name)) !== null) ids.push(name)\n }\n return ids.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n },\n }\n}\n","import { createHmac } from 'node:crypto'\nimport type { FastifyBaseLogger, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport { CONFIGURATION_ID as FORM_ID } from './config-store.js'\n\n/** What every event says, on either plane. */\ninterface AuditEventBase {\n at: string\n /**\n * The verified host actor, or `null` for a request refused before its token\n * was read: no token or a token refused, over the rate limit, or a body\n * Fastify refused (400, 413, 415, a client gone mid-body), whatever token it\n * carried, since each plane authenticates after the body is read (0033).\n */\n actor: string | null\n status: number\n /** `ok` below 400; otherwise the stable code the response carried, or `http-NNN` when it carried none. */\n outcome: string\n}\n\nexport type RuntimeOperation = 'form-list' | 'form' | 'read' | 'list' | 'create' | 'update' | 'delete' | 'lookup-query' | 'lookup-resolve'\n\nexport type AdminOperation =\n | 'connection-list'\n | 'connection-test'\n | 'discovery'\n | 'proposal'\n | 'publish'\n | 'version-list'\n | 'version-latest'\n | 'version-read'\n | 'drift'\n | 'regeneration'\n | 'restore'\n | 'import'\n\n/**\n * One operational audit event of the runtime plane: who asked for what,\n * against which published version, and how it ended (plan section 12, 0023).\n *\n * Never a value. Not an answer, not a field, not a record: an audit log that\n * copied business data would be a second copy of the customer's database with\n * none of its permissions. The record is named by a keyed hash, or not at all.\n */\nexport interface RuntimeAuditEvent extends AuditEventBase {\n plane: 'runtime'\n operation: RuntimeOperation\n /** The form addressed, when it has a form id's shape (`auditedForm`); otherwise `null`. */\n form: string | null\n /** The published version the request was served from, when it got that far. */\n formVersion: number | null\n /**\n * A keyed hash of the record token, or `null`. Keyed, because a record token\n * spells its key and a key is often small — customer 7 — so a plain hash\n * could be reversed by trying every number. Without an audit key the server\n * cannot make one that resists that, so it records nothing rather than a\n * reference that only looks redacted.\n */\n record: string | null\n}\n\n/**\n * One operational audit event of the administrator's plane (0033): who\n * connected, discovered, proposed, published, read, compared, regenerated,\n * restored or imported, against which connection and which version, and how\n * it ended.\n *\n * Names and numbers only. Never the bundle, the policy or a role it names,\n * a snapshot, a generation request, a drift report or any message.\n */\nexport interface AdminAuditEvent extends AuditEventBase {\n plane: 'admin'\n operation: AdminOperation\n /** An allowlisted connection's name, never an address (0019): one the allowlist does not know is `null`. */\n connection: string | null\n /** The form addressed, when it has a form id's shape (`auditedForm`); otherwise `null`. */\n form: string | null\n /** The version read, compared, regenerated from, or written. */\n formVersion: number | null\n /** Publish, restore and import: the base the administrator said they edited, or saw newest. */\n expectedBase: number | null\n /** Restore only: the version the new one copies, which nothing on disk records. */\n restoredFrom: number | null\n}\n\n/** Both planes, told apart by `plane`. A typed sink narrows on it before reading a plane's own fields. */\nexport type AuditEvent = RuntimeAuditEvent | AdminAuditEvent\n\n/**\n * Where events go. A port: the default writes a structured log line; a\n * deployment that needs a durable trail supplies its own.\n *\n * A sink that throws does not fail the request — by the time the event exists,\n * the write it describes has committed or not, and refusing to answer would\n * only hide which. The failure is logged instead. That is the limitation 0023\n * writes down: this is an operational trail, not evidence.\n */\nexport type AuditSink = (event: AuditEvent) => void | Promise<void>\n\n/** Events as structured log lines, one per request. */\nexport function logAuditSink(log: FastifyBaseLogger): AuditSink {\n return (event) => {\n log.info({ audit: event }, 'audit')\n }\n}\n\n/**\n * A form id as the trail records it, on either plane: the text addressed when\n * it has the store's shape for a form id, otherwise `null`. A path parameter\n * is anything a caller types, token or no token, up to the router's limit; a\n * form id's shape holds no `:`, `;`, `@`, `=` or `/`, so no connection\n * string, and names something the store could hold (0033). It does hold an\n * IP address, a host name, or a lower-case word that is somebody's password:\n * those are form ids, and are recorded as typed.\n */\nexport function auditedForm(id: unknown): string | null {\n return typeof id === 'string' && FORM_ID.test(id) ? id : null\n}\n\n/** The keyed hash a record is named by: the first 32 hex characters of HMAC-SHA-256. */\nexport function recordReference(key: string | undefined, token: string | undefined): string | null {\n if (key === undefined || token === undefined || token === '') return null\n return createHmac('sha256', key).update(token, 'utf8').digest('hex').slice(0, 32)\n}\n\n/** What the shared hooks establish for every event, before a plane adds its own fields. */\nexport interface AuditBase<Operation extends string> extends AuditEventBase {\n operation: Operation\n}\n\nexport interface AuditRequestsOptions<Operation extends string> {\n /** The audit name of every route the plane registers, keyed `METHOD url`, HEAD as the GET it answers for. */\n names: Readonly<Record<string, Operation>>\n /** Absent, nothing is emitted; a route without a name is refused all the same. */\n sink?: AuditSink | undefined\n now?: (() => string) | undefined\n /** The plane's event: the base, and the fields only that plane has. */\n describe: (request: FastifyRequest, base: AuditBase<Operation>) => AuditEvent\n}\n\n/** `METHOD url`, with HEAD as the GET it answers for. */\nfunction routeKey(method: string, url: string): string {\n const verb = method.toUpperCase()\n return `${verb === 'HEAD' ? 'GET' : verb} ${url}`\n}\n\n/**\n * One event per request, for every route of the plane it is called on, from\n * one set of hooks both planes share — so the two cannot disagree on what an\n * outcome is, or on whether a refusal is audited (0023, 0033).\n *\n * Called before the plane registers a route: from then on a route in that\n * context with no audit name stops the registration, whether or not a sink is\n * configured. `onRoute` sees only the routes registered after it, so a route\n * registered in the context before this call is refused on every request\n * instead, before its handler runs, and the log names it: either way a route\n * without a name never answers unaudited.\n *\n * The event is built once the reply is decided, from the response itself:\n * every ending — success, refusal, a database out of reach — without each\n * branch remembering to say so.\n *\n * Exactly once for a request that reaches a route, whichever way it ends,\n * measured against Fastify 5.12.5 on Node 22. `onResponse` runs only when the\n * response finishes. One whose client left while the route still ran never\n * finishes: the handler completes and its write commits, `onSend` runs with\n * the response already destroyed, and neither `onResponse` nor\n * `onRequestAbort` runs, the latter because the body had been read. One\n * pipelined behind another answer on its connection has no socket of its own\n * until that answer is written; if the connection goes first it emits\n * neither `finish` nor `close`, and is never destroyed. So the event is also\n * written from `onSend` when the response or its connection is already gone,\n * and otherwise from the connection's `close` if it comes before the event\n * is written: a response whose socket goes after `onSend` goes with its\n * connection, and a pipelined one has nothing else to say so. Whichever\n * comes first writes it, and the others find it written. A connection holds\n * one listener while any answer waits on it, however many requests a client\n * pipelined, and none once the last is written.\n */\nexport function auditRequests<Operation extends string>(app: FastifyInstance, options: AuditRequestsOptions<Operation>): void {\n const { names, sink, describe } = options\n\n app.addHook('onRoute', (route) => {\n for (const method of [route.method].flat()) {\n const key = routeKey(method, route.url)\n if (!Object.hasOwn(names, key)) throw new Error(`${key} has no audit name (0023, 0033)`)\n }\n })\n\n // A route `onRoute` never saw: registered in this context before the call.\n app.addHook('onRequest', async (request, reply) => {\n const key = routeKey(request.method, request.routeOptions.url ?? '')\n if (Object.hasOwn(names, key)) return undefined\n request.log.error(`${key} has no audit name (0023, 0033)`)\n return reply.code(500).send({ code: 'unaudited-route', message: 'This route has no audit name, and does not answer.' })\n })\n\n if (sink === undefined) return\n const now = options.now ?? (() => new Date().toISOString())\n const codes = new WeakMap<FastifyRequest, string>()\n const written = new WeakSet<FastifyRequest>()\n /** Per connection, the requests whose answers wait on it, and its one `close` listener for all of them. */\n const waiting = new WeakMap<object, { requests: Map<FastifyRequest, FastifyReply>; close: () => void }>()\n\n /** Until its event is written, a request's connection going writes it. */\n const wait = (request: FastifyRequest, reply: FastifyReply): void => {\n const connection = request.raw.socket\n let entry = waiting.get(connection)\n if (entry === undefined) {\n const requests = new Map<FastifyRequest, FastifyReply>()\n entry = {\n requests,\n close: () => {\n for (const [each, answer] of requests) void emit(each, answer)\n },\n }\n waiting.set(connection, entry)\n connection.once('close', entry.close)\n }\n entry.requests.set(request, reply)\n }\n\n /** Written: the connection need not say so, and holds no listener once nothing on it waits. */\n const settle = (request: FastifyRequest): void => {\n const connection = request.raw.socket\n const entry = waiting.get(connection)\n if (entry === undefined || !entry.requests.delete(request) || entry.requests.size > 0) return\n connection.off('close', entry.close)\n waiting.delete(connection)\n }\n\n const emit = async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {\n if (written.has(request)) return\n written.add(request)\n settle(request)\n // A route without a name was refused in onRequest, and the log says so; there is no event to name it by.\n const operation = names[routeKey(request.method, request.routeOptions.url ?? '')]\n if (operation === undefined) return\n const status = reply.statusCode\n try {\n const base = { at: now(), actor: request.identity?.actor.id ?? null, operation, status, outcome: status < 400 ? 'ok' : (codes.get(request) ?? `http-${String(status)}`) }\n await sink(describe(request, base))\n } catch (error) {\n request.log.error({ error: (error as Error).message }, 'an audit event could not be written')\n }\n }\n\n app.addHook('onSend', async (request, reply, payload) => {\n // The stable code of a refusal, read from the response itself.\n if (reply.statusCode >= 400 && typeof payload === 'string') {\n try {\n const code = (JSON.parse(payload) as { code?: unknown }).code\n if (typeof code === 'string') codes.set(request, code)\n } catch {\n // A body that is not JSON carries no code; the status says enough.\n }\n }\n if (reply.raw.destroyed || request.raw.socket.destroyed) {\n await emit(request, reply)\n return payload\n }\n wait(request, reply)\n return payload\n })\n\n app.addHook('onResponse', async (request, reply) => {\n await emit(request, reply)\n })\n}\n","import { DEFAULT_LOOKUP_DEADLINE_MS, MAX_LOOKUP_DEADLINE_MS } from '@formancy/data-core'\n\n/**\n * How long a lookup statement may run, and the setting that changes it\n * (0061). `FORMANCY_DATA_LOOKUP_DEADLINE_MS` is read by the composition root\n * (main.ts) and parsed here, so the rule is tested without starting a\n * process; `createDataServer` holds its `runtime.lookupDeadlineMs` to the\n * same rule. One value for the server, not one per lookup: what it should be\n * depends on the deployment's hardware and data, not on a form.\n */\n\nexport type LookupDeadlineSetting = { ok: true; ms: number } | { ok: false; problem: string }\n\n/** What is wrong with `ms` as a deadline, said of `name`, or undefined: a whole number from 1 to the longest a Node timer holds. */\nexport function lookupDeadlineProblem(name: string, ms: number, written: string = String(ms)): string | undefined {\n if (Number.isSafeInteger(ms) && ms >= 1 && ms <= MAX_LOOKUP_DEADLINE_MS) return undefined\n return `${name} is a whole number of milliseconds from 1 to ${String(MAX_LOOKUP_DEADLINE_MS)}; it is ${written}.`\n}\n\n/**\n * `FORMANCY_DATA_LOOKUP_DEADLINE_MS` as written: unset is the measured\n * default; otherwise decimal digits only, no sign, fraction, exponent or\n * space, from 1 to the longest a Node timer holds. The refusal quotes what\n * was written, which is a number of milliseconds and never a secret.\n */\nexport function lookupDeadlineSetting(text: string | undefined): LookupDeadlineSetting {\n if (text === undefined) return { ok: true, ms: DEFAULT_LOOKUP_DEADLINE_MS }\n const ms = /^[0-9]+$/.test(text) ? Number(text) : Number.NaN\n const problem = lookupDeadlineProblem('FORMANCY_DATA_LOOKUP_DEADLINE_MS', ms, JSON.stringify(text))\n return problem === undefined ? { ok: true, ms } : { ok: false, problem }\n}\n\n/** One lookup statement's signal, and the call that stops its timer and its listening once the statement is done. */\nexport interface LookupDeadline {\n readonly signal: AbortSignal\n clear(): void\n}\n\n/**\n * A signal that aborts when `ms` have passed, with a TimeoutError, or when\n * `within` aborts, with `within`'s own reason (0061).\n *\n * A timer of its own rather than `AbortSignal.any` over\n * `AbortSignal.timeout`: a timeout signal cannot be cleared, and the\n * composite stayed reachable from it until it fired -- about 730 bytes a\n * request, for up to 24.8 days at the longest deadline (2026-10-15 review,\n * Node 24.21.0). `clear()` stops the timer and the listening, so a lookup\n * that answered holds nothing. The timer is unreferenced, as a timeout\n * signal's is: a deadline never keeps the process alive.\n */\nexport function lookupDeadline(ms: number, within?: AbortSignal): LookupDeadline {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(new DOMException(`The lookup passed its deadline of ${String(ms)} ms.`, 'TimeoutError')), ms)\n // A browser-like global's setTimeout, as the host page's suites run the\n // server under jsdom, returns a number, which keeps nothing alive.\n if (typeof timer === 'object') timer.unref()\n const left = (): void => controller.abort(within?.reason)\n if (within?.aborted === true) controller.abort(within.reason)\n else within?.addEventListener('abort', left, { once: true })\n return {\n signal: controller.signal,\n clear() {\n clearTimeout(timer)\n within?.removeEventListener('abort', left)\n },\n }\n}\n","import type { GenerationRequest, LookupChoice } from '@formancy/data-core'\nimport { CONFIGURATION_ID as FORM_ID } from './config-store.js'\n\nexport type GenerationReading = { ok: true; generation: GenerationRequest } | { ok: false; problems: string[] }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction lookupChoices(value: unknown): LookupChoice[] | undefined {\n if (value === undefined) return []\n if (!Array.isArray(value)) return undefined\n const choices: LookupChoice[] = []\n for (const entry of value) {\n if (!isRecord(entry) || typeof entry['foreignKey'] !== 'string') return undefined\n const display = entry['display']\n if (!Array.isArray(display) || !display.every((column) => typeof column === 'string')) return undefined\n choices.push({ foreignKey: entry['foreignKey'], display: [...(display as string[])] })\n }\n return choices\n}\n\n/**\n * One reading of a generation request, for the proposal route and for the\n * bundle check (0030), so what a proposal was generated from and what a\n * bundle says it was generated from are read by the same rules.\n *\n * Normalised: `lookups` and `pinned` are always lists, and `versionColumn` is\n * present only when it is a string. Other properties are not carried, so a\n * stored request that is not equal to its own reading holds something this\n * reading would not have produced.\n */\nexport function readGeneration(value: unknown): GenerationReading {\n if (!isRecord(value)) return { ok: false, problems: ['a generation request is an object'] }\n const problems: string[] = []\n const { connection, root, formId, title, versionColumn, pinned } = value\n if (typeof connection !== 'string') problems.push('connection must name a connection')\n if (!isRecord(root) || typeof root['schema'] !== 'string' || typeof root['name'] !== 'string') problems.push('root must be { schema, name }')\n if (typeof formId !== 'string' || !FORM_ID.test(formId)) problems.push('formId must be a lower-case form id')\n if (typeof title !== 'string') problems.push('title must be text')\n const lookups = lookupChoices(value['lookups'])\n if (lookups === undefined) problems.push('lookups must be a list of { foreignKey, display: [columns] }')\n const pinnedColumns = pinned === undefined ? [] : Array.isArray(pinned) && pinned.every((name) => typeof name === 'string') ? [...(pinned as string[])] : undefined\n if (pinnedColumns === undefined) problems.push('pinned must be a list of columns')\n if (versionColumn !== undefined && typeof versionColumn !== 'string') problems.push('versionColumn must name a column')\n if (problems.length > 0 || lookups === undefined || pinnedColumns === undefined || !isRecord(root)) return { ok: false, problems }\n return {\n ok: true,\n generation: {\n connection: connection as string,\n root: { schema: root['schema'] as string, name: root['name'] as string },\n formId: formId as string,\n title: title as string,\n lookups,\n pinned: pinnedColumns,\n ...(typeof versionColumn === 'string' ? { versionColumn } : {}),\n },\n }\n}\n","import { GENERATED_SPEC_VERSION } from '@formancy/data-core'\nimport type { FormSchema } from '@formancy/spec'\nimport { validateSchema } from '@formancy/spec/validate'\n\nexport type ServedSchema = { valid: true; schema: FormSchema } | { valid: false; problems: string[] }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * A stored form or base as this server serves one: a document formancy's\n * validator accepts, in the spec version the generator writes (0042), or\n * every reason it is not, each beginning with `label`.\n *\n * The version is this server's question as well as the validator's, because\n * the validator accepts every version its release speaks, and that grows\n * with the release: from `@formancy/spec` 0.4.0 on a spec 4 document is valid,\n * and a host page whose renderer is at 0.3.0 refuses it outright. Another\n * version is refused whole, since what the validator would add is about a\n * version this server does not serve. A construct of a later version in a\n * document that says the generator's is refused with the same reason, by\n * the validator's code, rather than with its advice to move the document to\n * that version, which this server would then refuse.\n */\nexport function servedSchema(label: 'form' | 'base', document: unknown): ServedSchema {\n const only = `and this server publishes and serves only spec ${JSON.stringify(GENERATED_SPEC_VERSION)}, the version it generates (0042)`\n const declared = isRecord(document) ? document['specVersion'] : undefined\n if (typeof declared === 'string' && declared !== GENERATED_SPEC_VERSION) return { valid: false, problems: [`${label}: is spec ${JSON.stringify(declared)}, ${only}`] }\n const checked = validateSchema(document)\n if (checked.valid) return checked\n return {\n valid: false,\n problems: checked.errors.map((error) =>\n error.code.startsWith('version.') ? `${label}: ${error.path} needs spec ${JSON.stringify(String(error.values['version']))}, ${only}` : `${label}: ${error.message}`,\n ),\n }\n}\n","import { applyPresentation, findObject, generateForm, presentationShapeProblems } from '@formancy/data-core'\nimport type { FormBindings, GenerationRequest, MetadataSnapshot, ObjectRef, PresentationOverrides } from '@formancy/data-core'\nimport type { FormSchema } from '@formancy/spec'\nimport { canonicalize } from '@formancy/spec'\nimport type { BundleV2 } from './bundle.js'\nimport { readGeneration } from './generation.js'\nimport { servedSchema } from './served-schema.js'\n\n/*\n * What a format-2 bundle carries beyond format 1 (0030), and what holds it:\n * the generation request, the generated base and the presentation chosen\n * over it. `validateBundle` asks `format2Problems` on publish and on every\n * read; the publish route alone asks `generatedProblems`, the one check that\n * runs this release's generator.\n */\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * What format 2 adds (0030): a generation request in the form the proposal\n * route reads one, for this bundle's connection and form; a base formancy\n * accepts, in the generator's spec version (`servedSchema`); and a\n * presentation that applies to that base and gives exactly the stored form.\n * Not whether the generator would write that base — that is\n * `generatedProblems`, at publish only.\n */\nexport function format2Problems(document: Record<string, unknown>, form: FormSchema, bindings: FormBindings, snapshot: MetadataSnapshot | null): string[] {\n const problems: string[] = []\n const read = readGeneration(document['generation'])\n const base = servedSchema('base', document['base'])\n if (!read.ok) problems.push(...read.problems.map((problem) => `generation: ${problem}`))\n else if (canonicalize(read.generation) !== canonicalize(document['generation'])) {\n problems.push('generation: is not in its normalised form (lookups and pinned as lists, versionColumn only when given, nothing else)')\n } else {\n if (read.generation.connection !== document['connection']) problems.push(`generation: names connection ${read.generation.connection}, and the bundle is bound to ${String(document['connection'])}`)\n if (base.valid && read.generation.formId !== base.schema.id) problems.push(`generation: names form ${read.generation.formId}, and the base is ${base.schema.id}`)\n if (base.valid) problems.push(...requestProblems(read.generation, base.schema, bindings, snapshot))\n }\n if (!base.valid) problems.push(...base.problems)\n\n const shape = presentationShapeProblems(document['presentation'])\n if (shape.length > 0) return [...problems, ...shape.map((problem) => `presentation: ${problem}`)]\n if (!base.valid) return problems\n const applied = applyPresentation(base.schema, document['presentation'] as PresentationOverrides, bindings)\n if (!applied.ok) problems.push(...applied.problems.map((problem) => `presentation: ${problem}`))\n else if (canonicalize(applied.form) !== canonicalize(form)) {\n problems.push('form: is not the base with its presentation applied; the form, the base or the presentation was edited')\n }\n return problems\n}\n\n/**\n * What the stored request alone decides, against the base and bindings it is\n * said to have produced (0030): the title, the root, a confirmed version\n * column, the pins and the lookups. A regeneration generates from this\n * request and its draft is checked at publish against the same request, so\n * an edit to it alone would reach the next version unannounced. None of\n * these asks the generator, so a later release cannot fail a stored version\n * on them.\n *\n * Not caught: a pin taken out by hand. A field written by no operation could\n * be pinned, generated, unwritable by the account or from a view, and only\n * the generator tells them apart. The policy still decides what is written\n * and which rows are reached; what such an edit changes is the next draft's\n * form.\n */\nfunction requestProblems(generation: GenerationRequest, base: FormSchema, bindings: FormBindings, snapshot: MetadataSnapshot | null): string[] {\n const problems: string[] = []\n const tableOf = (ref: ObjectRef) => `${ref.schema}.${ref.name}`\n if (generation.title !== base.title) problems.push(`generation: is titled ${JSON.stringify(generation.title)}, and the base ${JSON.stringify(base.title)}`)\n if (!isRecord(bindings.root) || generation.root.schema !== bindings.root.schema || generation.root.name !== bindings.root.name) {\n problems.push(`generation: names root ${tableOf(generation.root)}, and the bindings ${isRecord(bindings.root) ? tableOf(bindings.root) : 'none'}`)\n }\n\n // A table's rowversion is its strategy whatever the request confirms, and\n // one the account may not read leaves it none (0027); otherwise a confirmed\n // version column is in the bindings exactly when the request gives it.\n const concurrency = isRecord(bindings.concurrency) ? bindings.concurrency : null\n const confirmed = concurrency?.kind === 'version-column' && concurrency.confirmed === true ? concurrency.column : undefined\n const rowversion = concurrency?.kind === 'rowversion' || hasRowversion(snapshot, bindings.root)\n if (generation.versionColumn !== undefined && !rowversion && generation.versionColumn !== confirmed) {\n problems.push(`generation: confirms version column ${generation.versionColumn}, and the bindings use ${confirmed ?? 'none'}`)\n }\n if (generation.versionColumn === undefined && confirmed !== undefined) {\n problems.push(`generation: confirms no version column, and the bindings use ${confirmed} as a confirmed one`)\n }\n\n for (const column of generation.pinned ?? []) {\n const field = bindings.fields.find((binding) => binding.kind === 'column' && binding.column === column)\n if (field !== undefined && (field.writes.create || field.writes.update)) problems.push(`generation: pins ${column}, and its field ${field.field} is written by the form`)\n }\n\n // Compared as foreign key and display columns, the whole of a lookup choice.\n type Choice = { foreignKey: string; display: readonly string[] }\n const same = (left: Choice) => (right: Choice) => left.foreignKey === right.foreignKey && canonicalize(left.display) === canonicalize(right.display)\n const shown = (lookup: Choice) => `${lookup.foreignKey} showing ${lookup.display.join(', ')}`\n const bound = bindings.fields.flatMap((binding) => (binding.kind === 'lookup' ? [binding] : []))\n for (const lookup of generation.lookups) if (!bound.some(same(lookup))) problems.push(`generation: chooses a lookup over ${shown(lookup)}, which the bindings do not have`)\n for (const lookup of bound) if (!generation.lookups.some(same(lookup))) problems.push(`generation: the bindings have a lookup over ${shown(lookup)}, which it does not choose`)\n return problems\n}\n\n/** Whether the snapshot's root has a rowversion column; false for a snapshot no catalog could produce. */\nfunction hasRowversion(snapshot: MetadataSnapshot | null, root: ObjectRef): boolean {\n if (snapshot === null || !isRecord(root)) return false\n return findObject(snapshot, root)?.columns.some((column) => column.type.kind === 'rowversion') ?? false\n}\n\n/**\n * Whether this server's generator writes the bundle's base and bindings from\n * its snapshot and generation request (0030). Asked at publish, so a base\n * somebody edited — a relaxed maxLength, a dropped `required` — is never\n * stored as if the database had said so. Not asked on read: a later release\n * whose generator writes something else must not make every version stored\n * before it \"corrupt\".\n */\nexport function generatedProblems(bundle: BundleV2): string[] {\n let generated\n try {\n generated = generateForm(bundle.snapshot, bundle.generation)\n } catch (error) {\n return [`generation: ${(error as Error).message}`]\n }\n const problems: string[] = []\n if (canonicalize(generated.form) !== canonicalize(bundle.base)) problems.push('base: is not what this server generates from the stored snapshot and generation request')\n if (canonicalize(generated.bindings) !== canonicalize(bundle.bindings)) problems.push('bindings: are not what this server generates from the stored snapshot and generation request')\n return problems\n}\n","import { bindingsVersionProblem, createSnapshot, deleteProblems, findObject, rowFilterColumnProblem, throughProblems, validatePolicy } from '@formancy/data-core'\nimport type { FormBindings, FormPolicy, GenerationRequest, MetadataSnapshot, ObjectRef, PresentationOverrides } from '@formancy/data-core'\nimport type { FormSchema } from '@formancy/spec'\nimport { format2Problems } from './bundle-format2.js'\nimport { servedSchema } from './served-schema.js'\n\n/**\n * Everything one published version of a form needs at runtime, as one\n * immutable document in the configuration store (0013).\n *\n * The four concerns plan section 9 keeps apart travel together here and stay\n * separate inside: the portable formancy document, the database bindings, the\n * access policy, and the snapshot the bindings were generated from — which the\n * request planner reads column types from and drift review compares against.\n */\ninterface BundleParts {\n /** The connection the form is bound to, by the name the deployment's allowlist gives it. */\n connection: string\n form: FormSchema\n bindings: FormBindings\n policy: FormPolicy\n snapshot: MetadataSnapshot\n}\n\n/** Published before 0030: served as it is; it kept no base, so a regeneration cannot carry its presentation. */\nexport interface BundleV1 extends BundleParts {\n format: 1\n}\n\n/**\n * Since 0030. `form` is `applyPresentation(base, presentation, bindings)`;\n * `base` and `bindings` are `generateForm(snapshot, generation)`'s, which is\n * checked at publish (`generatedProblems`) and not on read.\n */\nexport interface BundleV2 extends BundleParts {\n format: 2\n generation: GenerationRequest\n base: FormSchema\n presentation: PresentationOverrides\n}\n\nexport type PublishedBundle = BundleV1 | BundleV2\n\nexport type BundleValidation = { ok: true; bundle: PublishedBundle } | { ok: false; problems: string[] }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Whether a document can be served as a published form, with every reason it\n * cannot.\n *\n * Run on publish and again on every read from the store, because the store is\n * files on a volume and a file can be edited by hand (0013). Each check is one\n * a hand edit could break silently:\n *\n * - the form must be one formancy's own validator accepts, in the spec\n * version the generator writes (`servedSchema`, 0042) -- the validator\n * alone accepts every version its release speaks;\n * - the snapshot must still hash to its own fingerprint — an edited column\n * type would otherwise reach the planner, which trusts it;\n * - the bindings must have been generated from that snapshot;\n * - the bindings must be a version this release reads — one published before\n * 0027 cannot say which operation a field is written on, and is republished;\n * - every bound field must exist in the form, and the policy must fit the\n * bindings and the snapshot (`policyProblems`), so a rule nobody enforces\n * cannot be published;\n * - in format 2 (0030), the form must be its base with its presentation\n * applied, and the generation request the one this server would store and\n * the one its base and bindings say they came from (`bundle-format2.ts`).\n *\n * Format 1 is still read, so every version published before 0030 is served.\n *\n * It never throws. A part of another shape than a check reads -- a hand\n * edit's `null` where the bindings' fields belong -- is one more reason the\n * version is not served: thrown, it would reach the server's default 500,\n * which answers with the exception's message, and the menu of forms (0045)\n * would have to tell it from a store that cannot be read.\n */\nexport function validateBundle(document: unknown): BundleValidation {\n try {\n return checkedBundle(document)\n } catch (error) {\n return { ok: false, problems: [`the bundle has a part of another shape than it should: ${(error as Error).message}`] }\n }\n}\n\nfunction checkedBundle(document: unknown): BundleValidation {\n if (!isRecord(document)) return { ok: false, problems: ['a bundle is an object'] }\n const problems: string[] = []\n const format = document['format']\n if (format !== 1 && format !== 2) problems.push('format must be 1 or 2')\n if (typeof document['connection'] !== 'string' || document['connection'] === '') problems.push('connection must name a connection')\n\n const form = servedSchema('form', document['form'])\n if (!form.valid) problems.push(...form.problems)\n\n const snapshot = document['snapshot'] as MetadataSnapshot | undefined\n const bindings = document['bindings'] as FormBindings | undefined\n const policy = document['policy'] as FormPolicy | undefined\n if (!isRecord(snapshot) || !isRecord(bindings) || !isRecord(policy)) {\n problems.push('snapshot, bindings and policy must all be present')\n return { ok: false, problems }\n }\n\n let wellFormed = false\n try {\n const { fingerprint, ...rest } = snapshot\n if (createSnapshot(rest).fingerprint !== fingerprint) {\n problems.push('the snapshot does not hash to its own fingerprint: it was edited after it was taken')\n }\n wellFormed = true\n } catch (error) {\n problems.push(`the snapshot is not one a catalog could produce: ${(error as Error).message}`)\n }\n\n // Before the policy: fitting it reads what each field writes per operation,\n // which a version-1 file does not say.\n const version = bindingsVersionProblem(bindings.version)\n if (version !== null) {\n problems.push(version)\n return { ok: false, problems }\n }\n\n if (bindings.snapshotFingerprint !== snapshot.fingerprint) {\n problems.push('the bindings were not generated from this snapshot')\n }\n\n if (form.valid && Array.isArray(bindings.fields)) {\n const keys = new Set(form.schema.model.fields.map((field) => field.key))\n for (const binding of bindings.fields) {\n if (!keys.has(binding.field)) problems.push(`the bindings name ${binding.field}, which the form does not have`)\n }\n }\n\n // A snapshot no catalog could produce cannot be searched for filter columns.\n if (wellFormed) problems.push(...policyProblems(snapshot, bindings, policy))\n else problems.push(...fitProblems(policy, bindings))\n if (format === 2 && form.valid && Array.isArray(bindings.fields)) problems.push(...format2Problems(document, form.schema, bindings, wellFormed ? snapshot : null))\n\n return problems.length === 0 ? { ok: true, bundle: document as unknown as PublishedBundle } : { ok: false, problems }\n}\n\n/**\n * Everything a policy does not fit: the bindings (`validatePolicy`), the\n * snapshot's columns a row filter compares (`unfilterableColumns`), what\n * the snapshot says of each lookup a through names (`throughProblems`, 0043),\n * and what a delete it grants would also do to other tables\n * (`deleteProblems`, 0050) -- each of which the planner asks too. One\n * function for the bundle check, on publish and on every read, and for the\n * regeneration route's report, so the three cannot disagree about one policy.\n */\nexport function policyProblems(snapshot: MetadataSnapshot, bindings: FormBindings, policy: FormPolicy): string[] {\n return [\n ...fitProblems(policy, bindings),\n ...unfilterableColumns(snapshot, bindings, policy),\n ...throughProblems(snapshot, bindings, policy).map((problem) => `policy: ${problem}`),\n ...(isRecord(bindings.root) ? deleteProblems(snapshot, bindings, policy).map((problem) => `policy: ${problem}`) : []),\n ]\n}\n\nfunction fitProblems(policy: FormPolicy, bindings: FormBindings): string[] {\n const fitted = validatePolicy(policy, bindings)\n return fitted.ok ? [] : fitted.problems.map((problem) => `policy: ${problem}`)\n}\n\n/** The filter rules of a policy as stored, whatever shape a hand edit left them in; `validatePolicy` reports a bad shape. */\nfunction rulesOf(value: unknown): Array<{ column: string }> {\n return Array.isArray(value) ? value.filter((rule): rule is { column: string } => isRecord(rule) && typeof rule['column'] === 'string') : []\n}\n\n/**\n * Row filter columns a filter cannot compare, as `rowFilterColumnProblem`\n * names them: absent, not readable by the snapshot's account (0027), or of a\n * kind with no spelling both engines compare alike — a boolean, a float, a\n * time or a timestamp (0028). The root's filter is the WHERE of every read\n * and update and is written on every create; a lookup's is the WHERE of every\n * search on its target. Either way the planner refuses every request it\n * scopes, so saying so at publish is cheaper than at every request. A root\n * filter column the account may not INSERT refuses every create in the same\n * way, so it is a problem while the form offers create.\n */\nfunction unfilterableColumns(snapshot: MetadataSnapshot, bindings: FormBindings, policy: FormPolicy): string[] {\n const problems: string[] = []\n const unfilterable = (where: string, ref: ObjectRef, rules: Array<{ column: string }>) => {\n const object = findObject(snapshot, ref)\n // A table outside the snapshot is the bindings' problem, reported as such.\n if (object === undefined) return\n for (const { column } of rules) {\n const problem = rowFilterColumnProblem(object, column)\n if (problem !== null) problems.push(`policy: ${where} ${problem}`)\n }\n }\n unfilterable('rowFilters', bindings.root, rulesOf(policy.rowFilters))\n // The planner writes every root filter column from the context on create.\n if (isRecord(bindings.operations) && bindings.operations.create === true) {\n const root = findObject(snapshot, bindings.root)\n for (const { column } of rulesOf(policy.rowFilters)) {\n if (root?.columns.find((candidate) => candidate.name === column)?.access.insert === false) {\n problems.push(`policy: rowFilters ${column} is a column this connection's account may not insert, and every create writes it`)\n }\n }\n }\n const lookups: Record<string, unknown> = isRecord(policy.lookups) ? policy.lookups : {}\n for (const binding of Array.isArray(bindings.fields) ? bindings.fields : []) {\n if (binding.kind === 'lookup') unfilterable(`lookups.${binding.field}`, binding.target.table, rulesOf(lookups[binding.field]))\n }\n return problems\n}\n","import type { FastifyBaseLogger } from 'fastify'\nimport { validateBundle } from './bundle.js'\nimport type { PublishedBundle } from './bundle.js'\nimport type { ConfigurationStore } from './config-store.js'\nimport { CONFIGURATION_ID } from './config-store.js'\n\n/** A form id is a configuration id: lower case, so it means one thing on every filesystem (0013). */\nexport const FORM_ID = CONFIGURATION_ID\n\nexport type Published =\n | { ok: true; version: number; bundle: PublishedBundle }\n | { ok: false; status: 404; code: 'unknown-form' | 'unknown-version'; message: string }\n | { ok: false; status: 500; code: 'corrupt-bundle'; message: string }\n\n/**\n * The newest published version of a form, validated again on the way out of\n * the store (0019). One function for both planes, because \"a hand-edited\n * version is never served\" is one decision and two copies of it could drift.\n *\n * The problems go to the operator's log; the caller learns only that the\n * version is not served.\n */\nexport async function loadPublished(store: ConfigurationStore, id: string, log: FastifyBaseLogger): Promise<Published> {\n const unknown = { ok: false, status: 404, code: 'unknown-form', message: `No form is called ${id}.` } as const\n if (!FORM_ID.test(id)) return unknown\n const version = await store.latest(id)\n if (version === null) return unknown\n return checked(store, id, version, log)\n}\n\n/**\n * One published version of a form, by number, validated as `loadPublished`\n * validates the newest (0030): the versions list and a restore read through\n * here, so an older version edited by hand is never served or restored.\n */\nexport async function loadVersion(store: ConfigurationStore, id: string, version: number, log: FastifyBaseLogger): Promise<Published> {\n if (!FORM_ID.test(id) || (await store.latest(id)) === null) return { ok: false, status: 404, code: 'unknown-form', message: `No form is called ${id}.` }\n return checked(store, id, version, log)\n}\n\nasync function checked(store: ConfigurationStore, id: string, version: number, log: FastifyBaseLogger): Promise<Published> {\n const document = await store.read(id, version)\n if (document === undefined) return { ok: false, status: 404, code: 'unknown-version', message: `${id} has no version ${String(version)}.` }\n const validation = validateBundle(document)\n if (!validation.ok) {\n log.error({ form: id, version, problems: validation.problems }, 'a published bundle failed validation')\n return { ok: false, status: 500, code: 'corrupt-bundle', message: `Version ${String(version)} of ${id} no longer validates and is not served.` }\n }\n return { ok: true, version, bundle: validation.bundle }\n}\n","import type { FormBindings, PlanRefusalCode, RecordFailure, RuntimeRefusal, UnknownOutcome } from '@formancy/data-core'\n\nexport interface HttpRefusal {\n status: number\n /** The body every runtime refusal has, declared once in data-core so the client reads the shape sent here. */\n body: RuntimeRefusal\n}\n\n/**\n * A planner or policy refusal as an HTTP answer.\n *\n * Access refusals are 403 and say which rule, never which value: the policy's\n * own messages name keys, columns and attributes only (0011).\n */\nexport function planRefusal(code: PlanRefusalCode, message: string): HttpRefusal {\n switch (code) {\n case 'operation-denied':\n case 'field-denied':\n case 'missing-attribute':\n case 'over-posting':\n case 'unknown-lookup':\n case 'operation-unavailable':\n return { status: 403, body: { code, message } }\n case 'drift':\n // The bundle's snapshot no longer matches what it was published with, or\n // the database as it is now stops this operation for the form as\n // published (0041): an administrator reviews drift, a person filling in\n // a form cannot. The message is the planner's sentence, which names no\n // column; what changed is in the server's log and in drift review.\n return { status: 409, body: { code, message } }\n case 'invalid-context':\n // The host's token verified, and still did not produce a context the\n // policy can read: a deployment problem, not the person's.\n return { status: 403, body: { code, message } }\n case 'invalid-policy':\n case 'invalid-bindings':\n return { status: 500, body: { code, message: 'This form is published with a configuration the server cannot apply.' } }\n case 'invalid-request':\n case 'invalid-record-token':\n case 'invalid-version':\n case 'nothing-to-update':\n case 'not-sortable':\n case 'not-searchable':\n return { status: 400, body: { code, message } }\n case 'record-not-read':\n // The update route plans with the record it read and answers a read\n // that failed itself (0040), so only a read that came back as another\n // record's token reaches this -- a key spelled otherwise than the\n // database holds it, under a collation that matched it anyway. Nothing\n // was sent, and a 500 would tell the client it may have been saved.\n return { status: 409, body: { code, message: 'This update could not be compared with the record as read. Nothing was saved.' } }\n }\n}\n\n/** The form field a column is bound to, directly or through a lookup, for a field error. */\nfunction fieldOf(bindings: FormBindings, column: string | undefined): string | undefined {\n if (column === undefined) return undefined\n for (const binding of bindings.fields) {\n if (binding.kind === 'column' && binding.column === column) return binding.field\n if (binding.kind === 'lookup' && binding.columns.includes(column)) return binding.field\n }\n return undefined\n}\n\n/**\n * A database's refusal as an HTTP answer (0015).\n *\n * Constraint failures become a field error where the engine names a column the\n * form binds, so a person sees the message beside the field. `refused` is\n * 422, because sending it again will be refused the same way; `unavailable`\n * is 503, because it may not be. `unknown-outcome` is not answered here: only\n * a write has one, and the write routes answer it with `unknownOutcome`, which\n * names what was addressed. A read that reported one would be an adapter's\n * bug, and is thrown -- a 500 -- rather than told to a person as \"may have\n * been saved\" (0031).\n */\nexport function recordFailure(bindings: FormBindings, failure: RecordFailure): HttpRefusal {\n const field = fieldOf(bindings, failure.column)\n const withField = (status: number, code: string, message: string): HttpRefusal => ({\n status,\n body: field === undefined ? { code, message } : { code, message, fieldErrors: [{ field, code, message }] },\n })\n switch (failure.code) {\n case 'not-found':\n return { status: 404, body: { code: 'not-found', message: 'No such record.' } }\n case 'stale':\n return { status: 409, body: { code: 'stale', message: 'The record changed since it was read. Reload it, review the changes, and save again.' } }\n case 'unique-violation':\n return withField(422, 'unique-violation', 'Another record already has this value.')\n case 'foreign-key-violation':\n return withField(422, 'foreign-key-violation', 'This refers to a record that does not exist, or is still referred to.')\n case 'not-null-violation':\n return withField(422, 'not-null-violation', 'A value is required.')\n case 'check-violation':\n return withField(422, 'check-violation', 'The database does not accept this value.')\n case 'too-long':\n return withField(422, 'too-long', 'This value is longer than the database stores.')\n case 'out-of-range':\n return withField(422, 'out-of-range', 'This value is outside the range the database stores.')\n case 'permission-denied':\n return { status: 403, body: { code: 'permission-denied', message: 'The database connection is not allowed to do this.' } }\n case 'schema-changed':\n return { status: 409, body: { code: 'schema-changed', message: 'The database changed since this form was published.' } }\n case 'refused':\n // A trigger's own error, a write declined, an error the adapter does not\n // know: it will be refused again, so the person is not invited to retry (0028).\n return withField(422, 'refused', 'The database refused this request, and nothing was saved. Sending it again will be refused the same way.')\n case 'unavailable':\n // Unreachable, or a refusal the engine documents as passing: a deadlock, a lock timeout.\n return { status: 503, body: { code: 'unavailable', message: 'The database could not complete this now. Nothing was saved.' } }\n case 'unknown-outcome':\n throw new Error('only a write has an unknown outcome; the write routes answer it with unknownOutcome')\n }\n}\n\n/**\n * A delete's refusal by the database as an HTTP answer (0050), in a\n * delete's words: nothing was deleted, never \"nothing was saved\". Never a\n * field error: a delete writes no field, and the column a constraint names\n * is another table's. A key that still refers to the record is\n * `referenced`, said without the table, the key or how many rows -- the\n * route logs the key's name (runtime-deletes.ts) -- though the answer\n * itself tells the person that something refers to the record, which any\n * refusal would.\n * `unknown-outcome` is the route's to answer with `unknownOutcome`, which\n * names what was addressed; here it is a bug, as for a read.\n */\nexport function deleteFailure(failure: RecordFailure): HttpRefusal {\n const refused: HttpRefusal = { status: 422, body: { code: 'refused', message: 'The database refused this request, and nothing was deleted. Sending it again will be refused the same way.' } }\n switch (failure.code) {\n case 'not-found':\n return { status: 404, body: { code: 'not-found', message: 'No such record.' } }\n case 'stale':\n return { status: 409, body: { code: 'stale', message: 'The record changed since it was read. Reload it and review the changes before deleting it.' } }\n case 'foreign-key-violation':\n return { status: 422, body: { code: 'referenced', message: 'Other records still refer to this one, so the database keeps it. Nothing was deleted.' } }\n case 'unique-violation':\n case 'not-null-violation':\n case 'check-violation':\n case 'too-long':\n case 'out-of-range':\n case 'refused':\n // A trigger's or a constraint's refusal of what the delete did: it\n // will be refused again, so the person is not invited to retry (0028).\n return refused\n case 'permission-denied':\n return { status: 403, body: { code: 'permission-denied', message: 'The database connection is not allowed to do this.' } }\n case 'schema-changed':\n return { status: 409, body: { code: 'schema-changed', message: 'The database changed since this form was published.' } }\n case 'unavailable':\n return { status: 503, body: { code: 'unavailable', message: 'The database could not complete this now. Nothing was deleted.' } }\n case 'unknown-outcome':\n throw new Error('only a write has an unknown outcome; the delete route answers it with unknownOutcome')\n }\n}\n\n/**\n * A write that was sent and whose answer was lost, as an HTTP answer (0031):\n * 502, because something between this server and the database failed, and in\n * words that say it may have been saved. Nothing here sends it again.\n *\n * The sentence never names a value (0011): what was addressed travels in\n * `record` and `version`, for the host to reconcile with. Each sentence says\n * what is safe next. An update and a delete are protected by their version\n * (0015, 0050). A create\n * whose key the insert named can be read by it. A create whose key the\n * database numbers cannot be found by anything here.\n */\nexport function unknownOutcome(operation: 'create' | 'update' | 'delete', record: string | null, version: string | null): { status: 502; body: UnknownOutcome } {\n let message: string\n if (operation === 'delete') {\n // A delete is protected by its version as an update is, and a key a\n // version proves a record by is never another record's (0050).\n message = 'The connection to the database failed after the deletion was sent. It may have been deleted. Deleting again with the same version deletes nothing else: if it was deleted, that is answered as not found.'\n } else if (operation === 'update') {\n message = 'The connection to the database failed after the change was sent. It may have been saved. Saving again with the same version is safe: it is stored at most once.'\n } else if (record !== null) {\n message = 'The connection to the database failed after the record was sent. It may have been saved: read it before entering it again.'\n } else {\n message = 'The connection to the database failed after the record was sent. It may have been saved, and the database numbers new records, so only a search of your own can tell.'\n }\n return { status: 502, body: { code: 'unknown-outcome', message, operation, record, version } }\n}\n","import { createHash } from 'node:crypto'\nimport { WRITE_ID_HEADER } from '@formancy/data-core'\nimport type { FastifyRequest } from 'fastify'\n\n/*\n * A write applied at most once per sending (0031).\n *\n * Nothing in this repository sends a write again, but the browser does:\n * Chromium resends a request -- a POST too -- when the connection it reused\n * closed before any byte of the answer came back. Measured with Playwright's\n * Chromium on 2026-10-09: a create whose answer was lost after the server\n * stored it reached the server twice, and the page saw only the second\n * answer, a 201, over two stored records. The host's page reuses its\n * connections for nearly every write, because the form, the record and the\n * lookups come first.\n *\n * So the client names each sending with a random id, and a request that\n * arrives with an id this server has already acted on is not applied: it is\n * answered with the first sending's answer -- awaited, if that is still with\n * the database -- which is the answer the page should have had. The id is\n * the person's, the form's and the operation's, so another of each is\n * another write -- a delete's included (0050), whose answer is a token and\n * `deleted`, so nothing of a deleted record is kept; and the same id with\n * another body is refused, because\n * answering it with the first write's answer would say \"Created\" about\n * something never sent.\n *\n * What it does not do: it holds in this process only. A resend that reaches\n * another replica behind a balancer, or this one after a restart, is applied\n * again; and a request without an id -- a client of the deployment's own --\n * is a write of its own every time. 0031 writes both down.\n */\n\n/** What a write route answers: the status, the body, and the record the audit names. */\nexport interface WriteAnswer {\n status: number\n body: unknown\n record?: string | undefined\n}\n\n/**\n * How long a sending's answer is kept for a resend, and how much is kept.\n *\n * Measured on 2026-10-09 by the host page's browser gate\n * (apps/host/scripts/browser-test.mjs, \"Create resent by Chromium\"), with\n * Playwright's Chromium against the real server on PostgreSQL, six runs: the\n * resend reached the server 5 to 6 ms after the connection closed -- the\n * gate polls every 5 ms, so that is its resolution -- and the order form's\n * create answer was 234 to 236 bytes as JSON. Ten minutes is a margin over\n * those milliseconds for a slower client, not a measurement of one; ten\n * thousand such answers are about 2.4 MB, and the byte cap is there for a\n * form whose records are large. Oldest dropped first, so a person sending\n * large records under fresh ids cannot make this server hold more: a resend\n * that arrives after its answer was dropped is applied again.\n */\nexport const KEEP_MS = 10 * 60_000\nexport const MAX_KEPT = 10_000\nexport const MAX_KEPT_BYTES = 32 * 1024 * 1024\n\n/** What a write id must look like: the client's are 32 hex characters; anything of 16 to 64 URL-safe characters is taken. */\nconst WRITE_ID = /^[A-Za-z0-9_-]{16,64}$/\n\n/** The request's write id; undefined when it has none; null when it has one that is not one. */\nexport function writeId(request: FastifyRequest): string | undefined | null {\n const value = request.headers[WRITE_ID_HEADER]\n if (value === undefined) return undefined\n return typeof value === 'string' && WRITE_ID.test(value) ? value : null\n}\n\n/** The refusal of a malformed id, or of an id that came back with another write. Nothing was sent. */\nexport const INVALID_WRITE_ID: WriteAnswer = {\n status: 400,\n body: { code: 'invalid-request', message: `The ${WRITE_ID_HEADER} header is not one write's id. Nothing was saved.` },\n}\nconst REUSED_WRITE_ID: WriteAnswer = {\n status: 400,\n body: { code: 'invalid-request', message: 'This write id was sent before with a different write. Nothing was saved.' },\n}\n\ninterface Kept {\n digest: string\n until: number\n answer: Promise<WriteAnswer>\n /** The answer's size as UTF-8 JSON once it is in; nothing while it is with the database. */\n bytes: number\n}\n\nexport function createWriteOnce(now: () => number = Date.now) {\n const kept = new Map<string, Kept>()\n let bytes = 0\n\n /** Drops what has expired, and the oldest past either bound. Insertion order is expiry order. */\n function prune(): void {\n for (const [key, entry] of kept) {\n if (entry.until > now() && kept.size <= MAX_KEPT && bytes <= MAX_KEPT_BYTES) break\n kept.delete(key)\n bytes -= entry.bytes\n }\n }\n\n return {\n /**\n * `perform`'s answer, the first time this id is sent for this write; the\n * same answer, with `repeated`, every later time within KEEP_MS. The\n * check and the claim are one synchronous step, so two sendings that\n * arrive together cannot both perform.\n */\n async once(scope: { actor: string; form: string; operation: 'create' | 'update' | 'delete'; id: string; body: unknown }, perform: () => Promise<WriteAnswer>): Promise<WriteAnswer & { repeated: boolean }> {\n prune()\n const key = JSON.stringify([scope.actor, scope.form, scope.operation, scope.id])\n const digest = createHash('sha256').update(JSON.stringify(scope.body) ?? '').digest('hex')\n const earlier = kept.get(key)\n if (earlier !== undefined) {\n if (earlier.digest !== digest) return { ...REUSED_WRITE_ID, repeated: false }\n return { ...(await earlier.answer), repeated: true }\n }\n const answer = perform()\n const entry: Kept = { digest, until: now() + KEEP_MS, answer, bytes: 0 }\n kept.set(key, entry)\n answer.then(\n (settled) => {\n // Counted only while it is still kept: an entry pruned in flight holds nothing.\n if (kept.get(key) !== entry) return\n entry.bytes = Buffer.byteLength(JSON.stringify(settled.body) ?? '', 'utf8')\n bytes += entry.bytes\n },\n // A rejection is the first sending's to report; a resend that awaits it reports it too.\n () => {},\n )\n return { ...(await answer), repeated: false }\n },\n }\n}\n","import { authorizeOperation, planDelete, recordToken } from '@formancy/data-core'\nimport type { PlanRefusal, PolicyContext, RecordDeleted } from '@formancy/data-core'\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport type { PublishedBundle } from '../bundle.js'\nimport type { OpenConnection } from '../connections.js'\nimport { deleteFailure, planRefusal } from './runtime-errors.js'\nimport { INVALID_WRITE_ID, writeId } from './write-once.js'\nimport type { WriteAnswer } from './write-once.js'\n\n/** What the delete route borrows from the runtime plane, so it decides each step as the update route does. */\nexport interface DeleteRouteSteps {\n /** The published bundle, or a reply already sent. */\n published(id: string, reply: FastifyReply): Promise<PublishedBundle | undefined>\n /** The form's connection, or a reply already sent. */\n connection(bundle: PublishedBundle, reply: FastifyReply): Promise<OpenConnection | undefined>\n context(request: FastifyRequest): PolicyContext\n /** Runs `perform` once per sending of a write id, and answers a later sending with its answer (0031). */\n sendOnce(request: FastifyRequest<{ Params: { id: string } }>, operation: 'delete', id: string | undefined, perform: () => Promise<WriteAnswer>): Promise<WriteAnswer & { repeated?: boolean }>\n /** A write sent whose answer was lost: said in the log, answered 502 with what was addressed, never sent again. */\n lost(request: FastifyRequest<{ Params: { id: string } }>, operation: 'delete', record: string, version: string, failure: string): WriteAnswer\n /** A planner's refusal as the answer, its drift said in the log first. */\n refusal(request: FastifyRequest<{ Params: { id: string } }>, operation: 'delete', plan: PlanRefusal): WriteAnswer\n /** A refusal, sent, with what the audit names. */\n answered(reply: FastifyReply, answer: WriteAnswer & { repeated?: boolean }): Promise<FastifyReply>\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * One delete, from the description it is decided over to the statement,\n * answered rather than sent: the route runs it once per sending, so a write\n * id that arrives again is answered with this answer and asks the database\n * nothing (0031).\n *\n * The policy is asked first, before any statement (0041); then the root is\n * described, and the delete is planned over that description and sent with\n * its definition. The answer names the row the statement deleted, by the\n * token its key makes as the database held it -- which under a\n * case-insensitive key may be spelled otherwise than the token sent (0028)\n * -- and holds no value of it, so a resend's kept answer holds none either.\n */\nasync function deleted(steps: DeleteRouteSteps, request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, open: OpenConnection, record: string, version: string): Promise<WriteAnswer> {\n const actor = steps.context(request)\n const authorized = authorizeOperation(bundle.policy, actor, 'delete')\n if (!authorized.ok) return planRefusal(authorized.code, authorized.message)\n const root = await open.records.describe(bundle.bindings.root)\n if (!root.ok) return deleteFailure(root)\n const plan = planDelete(bundle.snapshot, bundle.bindings, bundle.policy, actor, record, version, root.described)\n if (!plan.ok) return steps.refusal(request, 'delete', plan)\n const outcome = await open.records.delete(plan.request)\n if (!outcome.ok && outcome.code === 'unknown-outcome') return steps.lost(request, 'delete', record, version, outcome.message)\n if (!outcome.ok && outcome.code === 'foreign-key-violation') {\n // The person is told only that something refers to the record; the\n // operator, which key. Its name alone: the adapter's sentence can carry\n // the key's values.\n request.log.info({ form: request.params.id, constraint: outcome.constraint ?? null }, 'a delete was refused: other records still refer to this one')\n }\n if (!outcome.ok) return deleteFailure(outcome)\n const token = recordToken(plan.request.target.identity.map((column) => column.name), outcome.deleted.values)\n // A key the planner decoded from a token makes one again; the token sent is the fallback, never a guess.\n const removed = token.ok ? token.token : record\n return { status: 200, body: { record: removed, deleted: true } satisfies RecordDeleted, record: removed }\n}\n\n/**\n * `POST /v1/forms/:id/records/delete` (0050): one record, by its token and\n * the version its person read, under the form's policy. A POST with a\n * body, as every runtime write is: a token in a DELETE's URL would land in\n * every proxy's log (0044), and RFC 9110 gives a DELETE's body no meaning.\n *\n * The body is checked first and the audit trail names the record; then the\n * write id, as an update's (0031). A 200 with `{ record, deleted: true }`,\n * never a 204: a client counts a write done only when the answer says what\n * happened to it. Every refusal is a delete's (`deleteFailure`), and none\n * carries a field error. The audit names the operation `delete` and the\n * record by its keyed hash, the only trace of which record went (0023).\n */\nexport function registerDeleteRoute(app: FastifyInstance, steps: DeleteRouteSteps): void {\n app.post<{ Params: { id: string } }>('/v1/forms/:id/records/delete', async (request, reply) => {\n const body = request.body\n if (!isRecord(body) || typeof body['record'] !== 'string' || typeof body['version'] !== 'string') {\n return reply.code(400).send({ code: 'invalid-request', message: 'Expected { record, version }.' })\n }\n if (request.auditTrail !== undefined) request.auditTrail.record = body['record']\n const id = writeId(request)\n if (id === null) return steps.answered(reply, INVALID_WRITE_ID)\n const bundle = await steps.published(request.params.id, reply)\n if (bundle === undefined) return reply\n const open = await steps.connection(bundle, reply)\n if (open === undefined) return reply\n const { record, version } = body\n return steps.answered(reply, await steps.sendOnce(request, 'delete', id, () => deleted(steps, request, bundle, open, record, version)))\n })\n}\n","import { planList, recordPage, validateListQuery } from '@formancy/data-core'\nimport type { DescribedTable, PolicyContext, RecordFailure, RecordList } from '@formancy/data-core'\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport type { PublishedBundle } from '../bundle.js'\nimport type { OpenConnection } from '../connections.js'\nimport { planRefusal } from './runtime-errors.js'\nimport type { WriteAnswer } from './write-once.js'\n\n/** What the list route borrows from the runtime plane, so it decides each step as the read route does. */\nexport interface ListRouteSteps {\n /** The published bundle, or a reply already sent. */\n published(id: string, reply: FastifyReply): Promise<PublishedBundle | undefined>\n /** The form's connection, or a reply already sent. */\n connection(bundle: PublishedBundle, reply: FastifyReply): Promise<OpenConnection | undefined>\n context(request: FastifyRequest): PolicyContext\n /** A read the database refused, as the read route answers it: a table that moved is decided as drift, over a description asked once more -- for a page, as a list. */\n unread(request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, open: OpenConnection, failure: RecordFailure, operation: 'list'): Promise<WriteAnswer>\n /** A read decided over the description its own statement carried, as the read route decides one -- for a page, as a list: a refusal when drift stops reading the form, or the table no longer holds its key to a total order. */\n undecided(request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, described: DescribedTable, operation: 'list'): WriteAnswer | undefined\n /** A refusal, sent, with what the audit names. */\n answered(reply: FastifyReply, answer: WriteAnswer): Promise<FastifyReply>\n}\n\n/**\n * `POST /v1/forms/:id/records/list` (0044): one page of the records this\n * person may read, each exactly what `records/read` returns for its token,\n * in a total order, with whether more follow and no total.\n *\n * A POST, so the search never appears in a URL a proxy logs. The body is\n * checked first, as the read route checks its token; then the planner asks\n * the policy -- a list is a read, under the read filter and every through,\n * with no grant of its own -- before any statement. The page and the table\n * it was read from come from one statement, and the page is decided over\n * that description after it ran, as a read is (0041), and also refused\n * when the table no longer holds the key to a total order: refused, its\n * rows are discarded and never reach the body, and the log says `list`, as\n * the audit does. The audit names the operation `list` and no record, never\n * the search, the sort or the parent (0023).\n *\n * An adapter that answered `unknown-outcome` for a list -- which writes\n * nothing -- is a bug, and is answered 500, as for a read (0031).\n */\nexport function registerListRoute(app: FastifyInstance, steps: ListRouteSteps): void {\n app.post<{ Params: { id: string } }>('/v1/forms/:id/records/list', async (request, reply) => {\n const query = validateListQuery(request.body === undefined ? {} : request.body)\n if (!query.ok) return reply.code(400).send({ code: query.code, message: query.message })\n const bundle = await steps.published(request.params.id, reply)\n if (bundle === undefined) return reply\n const plan = planList(bundle.snapshot, bundle.bindings, bundle.policy, steps.context(request), query.query)\n if (!plan.ok) return steps.answered(reply, planRefusal(plan.code, plan.message))\n const open = await steps.connection(bundle, reply)\n if (open === undefined) return reply\n const outcome = await open.records.list(plan.request)\n if (!outcome.ok) return steps.answered(reply, await steps.unread(request, bundle, open, outcome, 'list'))\n const drifted = steps.undecided(request, bundle, outcome.described, 'list')\n if (drifted !== undefined) return steps.answered(reply, drifted)\n return recordPage(bundle.bindings, plan.fields, outcome.rows, query.query.limit) satisfies RecordList\n })\n}\n","import type { FastifyReply, FastifyRequest } from 'fastify'\n\n/** A lookup request's signal, aborted when its client leaves before the answer, and the call that stops listening. */\nexport interface Abandonment {\n readonly signal: AbortSignal\n /** Removes the listeners: called once the route has its answer, whichever way. */\n settle(): void\n}\n\n/** What the signal is aborted with: an AbortError, so a route tells it from a database's failure by identity. */\nconst LEFT = 'The client closed the connection before the answer.'\n\n/**\n * Whether the browser gave up on this request before it was answered (0061).\n *\n * Two events say so, measured on Fastify 5.12.5 with Node 24 and Node 22 on\n * 2026-10-14: the response's `close` before it finished writing, and the\n * socket's `close`. The socket is needed for a request pipelined behind\n * another, whose response has no socket of its own until the answer before\n * it is written: on Node 22 such a response emits no `close` when the\n * connection goes (on Node 24 it does), as audit.ts found for its event. Two\n * events that look as if they say so do not: the request's `close` fires as\n * soon as its body is read, and Fastify's `onRequestAbort` never runs once\n * it was. A socket already destroyed has gone already.\n *\n * `settle()` removes both listeners: a keep-alive socket carries request\n * after request, and would otherwise gather one per request.\n */\nexport function abandonment(request: FastifyRequest, reply: FastifyReply): Abandonment {\n const controller = new AbortController()\n const response = reply.raw\n const socket = request.raw.socket\n const gone = (): void => {\n if (!response.writableFinished) controller.abort(new DOMException(LEFT, 'AbortError'))\n }\n response.once('close', gone)\n socket.once('close', gone)\n if (socket.destroyed) gone()\n return {\n signal: controller.signal,\n settle() {\n response.off('close', gone)\n socket.off('close', gone)\n },\n }\n}\n","import { buildLookupConfig, findObject, lookupRowFilter, scopeRowFilters, validateLookupQuery } from '@formancy/data-core'\nimport type { LookupConfig, ObjectMeta, PolicyContext, PolicyOperation, ResolvedLookup, RowFilters, RuntimeRefusal } from '@formancy/data-core'\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport type { PublishedBundle } from '../bundle.js'\nimport type { OpenConnection } from '../connections.js'\nimport { lookupDeadline } from '../lookup-deadline.js'\nimport { abandonment } from './abandoned.js'\nimport { planRefusal } from './runtime-errors.js'\n\n/**\n * At most this many tokens are resolved in one request: a form holds one per\n * lookup field, not thousands. A list page's tokens for one lookup field fit\n * one resolve: `LIST_PAGE_SIZE` is at most this, which a test holds (0044).\n */\nexport const MAX_RESOLVE = 100\n\nconst OPERATIONS = new Set<PolicyOperation>(['read', 'create', 'update'])\n\n/** What the lookup routes borrow from the runtime plane, so they decide each step as the record routes do. */\nexport interface LookupRouteSteps {\n /** The published bundle, or a reply already sent. */\n published(id: string, reply: FastifyReply): Promise<PublishedBundle | undefined>\n /** The form's connection, or a reply already sent. */\n connection(bundle: PublishedBundle, reply: FastifyReply): Promise<OpenConnection | undefined>\n context(request: FastifyRequest): PolicyContext\n /** How long a lookup statement may run, from the moment the adapter is asked for it (0061). */\n lookupDeadlineMs: number\n}\n\ntype LookupRequest = FastifyRequest<{ Params: { id: string; source: string } }>\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** The lookup field a source name stands for, its config and the actor's filter for it — or a reply already sent. */\nasync function lookupFor(steps: LookupRouteSteps, request: LookupRequest, reply: FastifyReply, operation: unknown): Promise<{ open: OpenConnection; config: LookupConfig; filters: RowFilters } | undefined> {\n if (typeof operation !== 'string' || !OPERATIONS.has(operation as PolicyOperation)) {\n await reply.code(400).send({ code: 'invalid-request', message: 'Say which operation the form is open for: read, create or update.' })\n return undefined\n }\n const bundle = await steps.published(request.params.id, reply)\n if (bundle === undefined) return undefined\n const binding = bundle.bindings.fields.find((entry) => entry.kind === 'lookup' && entry.source === request.params.source)\n if (binding === undefined) {\n await reply.code(404).send({ code: 'unknown-lookup', message: `This form has no lookup called ${request.params.source}.` })\n return undefined\n }\n const filter = lookupRowFilter(bundle.policy, steps.context(request), bundle.bindings, operation as PolicyOperation, binding.field)\n if (!filter.ok) {\n const refusal = planRefusal(filter.code, filter.message)\n await reply.code(refusal.status).send(refusal.body)\n return undefined\n }\n // Scoped as a record request's filter is (0028), before a connection is\n // opened: a trusted value the column does not hold in that spelling is\n // refused here, never left to the engine to convert.\n const config = buildLookupConfig(bundle.bindings, binding.field, { snapshot: bundle.snapshot })\n // buildLookupConfig found the target in this snapshot, or it would have thrown.\n const scoped = scopeRowFilters(findObject(bundle.snapshot, config.target) as ObjectMeta, filter.filter, `lookups.${binding.field}`)\n if (!scoped.ok) {\n const refusal = planRefusal(scoped.code, scoped.message)\n await reply.code(refusal.status).send(refusal.body)\n return undefined\n }\n const open = await steps.connection(bundle, reply)\n if (open === undefined) return undefined\n return { open, config, filters: scoped.filters }\n}\n\n/** What each lookup says when it fails, or its resolve passes its deadline: `unavailable`, which invites another try. */\nconst UNAVAILABLE: Record<'search' | 'resolve', string> = {\n search: 'The options could not be loaded.',\n resolve: 'The selected options could not be loaded.',\n}\n\n/**\n * One lookup statement under the request's signal, answered (0061).\n *\n * The signal aborts when the client leaves before the answer, or when the\n * deadline passes. The deadline starts when the adapter is asked, so a wait\n * for a pooled connection, inside the driver, counts, and reading the form\n * and opening its connection do not: a deadline that passed there would be\n * answered as a statement stopped when none was sent. The adapter stops the\n * statement and rejects with the signal's reason, untranslated; anything\n * else it throws is the database's.\n *\n * A search past the deadline is `503 lookup-timeout`, not `unavailable`,\n * because 0028 says a refusal says whether trying again helps, and a\n * contains search that timed out reads the same rows again. A resolve reads\n * by key, at most a row a token, so one past the deadline waited rather\n * than read too much, and trying again helps: it is `unavailable`, and the\n * log names the deadline. A client that left is answered `499 abandoned`:\n * nobody reads it, and it exists so the audit trail's event for the request\n * says what happened (0023, 0033). The person reads the renderer's own\n * sentence, \"could not be loaded\"; at formancy 0.5.0 a source's message has\n * nowhere else to go.\n */\nasync function answer<T>(steps: LookupRouteSteps, request: LookupRequest, reply: FastifyReply, kind: 'search' | 'resolve', run: (open: OpenConnection, config: LookupConfig, filters: RowFilters, signal: AbortSignal) => Promise<T>, operation: unknown): Promise<T | FastifyReply> {\n const abandoned = abandonment(request, reply)\n try {\n const found = await lookupFor(steps, request, reply, operation)\n if (found === undefined) return reply\n const deadline = lookupDeadline(steps.lookupDeadlineMs, abandoned.signal)\n const { signal } = deadline\n try {\n return await run(found.open, found.config, found.filters, signal)\n } catch (error) {\n if (signal.aborted && error === signal.reason) return stopped(steps, request, reply, kind, signal.reason === abandoned.signal.reason)\n request.log.warn({ error: (error as Error).message }, kind === 'search' ? 'a lookup search failed' : 'a lookup resolve failed')\n return reply.code(503).send({ code: 'unavailable', message: UNAVAILABLE[kind] } satisfies RuntimeRefusal)\n } finally {\n deadline.clear()\n }\n } finally {\n abandoned.settle()\n }\n}\n\n/** A lookup the adapter stopped at the signal: its client left, or its deadline passed. */\nfunction stopped(steps: LookupRouteSteps, request: LookupRequest, reply: FastifyReply, kind: 'search' | 'resolve', left: boolean): FastifyReply {\n if (left) {\n request.log.info({ lookup: kind }, 'a lookup was abandoned by its client; the database was told to stop it')\n return reply.code(499).send({ code: 'abandoned', message: 'The client closed the connection before the answer; the database was told to stop the statement.' } satisfies RuntimeRefusal)\n }\n const ms = steps.lookupDeadlineMs\n request.log.warn({ lookup: kind, deadlineMs: ms }, 'a lookup passed its deadline; the database was told to stop it')\n if (kind === 'resolve') return reply.code(503).send({ code: 'unavailable', message: UNAVAILABLE.resolve } satisfies RuntimeRefusal)\n return reply.code(503).send({ code: 'lookup-timeout', message: `The search took longer than this server allows (${String(ms)} ms), and the database was told to stop it. The same search will take as long again.` } satisfies RuntimeRefusal)\n}\n\n/**\n * `POST /v1/forms/:id/lookups/:source/query` and `…/resolve`: one page of a\n * lookup's options, or the labels of tokens a form holds, for the operation\n * the form is open for and under the actor's filter for that lookup (0012).\n * Each runs one statement that stops when its request is abandoned or its\n * deadline passes (0061); one that completes is answered exactly as before.\n */\nexport function registerLookupRoutes(app: FastifyInstance, steps: LookupRouteSteps): void {\n app.post<{ Params: { id: string; source: string } }>('/v1/forms/:id/lookups/:source/query', async (request, reply) => {\n const body = isRecord(request.body) ? request.body : {}\n return answer(\n steps,\n request,\n reply,\n 'search',\n async (open, config, filters, signal) => {\n const query = validateLookupQuery(config, { search: body['search'] ?? '', offset: body['offset'] ?? 0, limit: body['limit'] ?? config.maxPageSize })\n if (!query.ok) return reply.code(400).send({ code: query.code, message: query.message })\n return open.lookups.search(config, query.query, filters, signal)\n },\n body['operation'],\n )\n })\n\n app.post<{ Params: { id: string; source: string } }>('/v1/forms/:id/lookups/:source/resolve', async (request, reply) => {\n const body = isRecord(request.body) ? request.body : {}\n const tokens = body['tokens']\n if (!Array.isArray(tokens) || tokens.length > MAX_RESOLVE || !tokens.every((token) => typeof token === 'string')) {\n return reply.code(400).send({ code: 'invalid-request', message: `Expected { operation, tokens } with at most ${String(MAX_RESOLVE)} tokens.` })\n }\n return answer(steps, request, reply, 'resolve', async (open, config, filters, signal) => ({ rows: await open.lookups.resolve(config, tokens as string[], filters, signal) }) satisfies ResolvedLookup, body['operation'])\n })\n}\n","import { formMenu, menuForm } from '@formancy/data-core'\nimport type { FormMenu, MenuForm, PolicyContext } from '@formancy/data-core'\nimport type { FastifyBaseLogger, FastifyInstance, FastifyRequest } from 'fastify'\nimport type { ConfigurationStore } from '../config-store.js'\nimport { loadPublished } from '../published.js'\n\n/** What the menu route borrows from the runtime plane. */\nexport interface MenuRouteSteps {\n store: ConfigurationStore\n /** The connections the deployment's allowlist has: the registry's `ids()`, which asks no database. */\n connections(): readonly string[]\n context(request: FastifyRequest): PolicyContext\n}\n\n/** What the menu answers when the store cannot list its forms: nothing it was told, no path. */\nconst STORE_UNAVAILABLE = { code: 'store-unavailable', message: 'The forms cannot be listed now.' } as const\n\n/**\n * The newest version of `id` as this person's menu reads it, or `undefined`\n * when it is not served -- validated as every read is (0019), bound to a\n * connection the allowlist has, and read alone (`menuForm`). Whatever stops\n * one form -- a version that no longer validates, does not parse or cannot\n * be read, a connection the allowlist does not have, bindings a hand edit\n * left in a shape the check passes and the menu cannot read -- leaves that\n * form out, and the log says which and why; never a 500, which would let one\n * damaged file hide every form from everybody, and answer with what the\n * error said.\n */\nasync function entry(store: ConfigurationStore, allowed: readonly string[], id: string, context: PolicyContext, log: FastifyBaseLogger): Promise<MenuForm | undefined> {\n try {\n const loaded = await loadPublished(store, id, log)\n if (!loaded.ok) return undefined\n const { bundle } = loaded\n if (!allowed.includes(bundle.connection)) {\n log.error({ form: id, connection: bundle.connection }, 'a published form names a connection the allowlist does not have, and is not served')\n return undefined\n }\n return menuForm({ id, title: bundle.form.title, connection: bundle.connection, snapshot: bundle.snapshot, bindings: bundle.bindings, policy: bundle.policy }, context)\n } catch (error) {\n log.error({ form: id, error: (error as Error).message }, 'a published form cannot be read, and is not served')\n return undefined\n }\n}\n\n/**\n * `GET /v1/forms` (0045): the published forms this person may read, in\n * codepoint order of their ids, each with its title, the operations the\n * policy grants them of what it offers as published, and the children a\n * page may show under one of its records -- and nothing about any other\n * form, so the answer is the same to the byte whatever else is published.\n *\n * Every request reads and validates every published form's newest version,\n * and asks the policy of each (0019, 0022): nothing is cached. It asks no\n * database. Opening a form asks what the database as it is now allows\n * (`GET /v1/forms/:id`, 0041); the menu, over every form, would otherwise\n * be a statement per form, and one database out of reach would hide forms\n * on every other. A store that cannot list its forms is a 500 that says\n * only that. The audit names the operation `form-list`, and no form.\n */\nexport function registerMenuRoute(app: FastifyInstance, steps: MenuRouteSteps): void {\n app.get('/v1/forms', async (request, reply) => {\n let ids: string[]\n try {\n ids = await steps.store.list()\n } catch (error) {\n request.log.error({ error: (error as Error).message }, 'the configuration store cannot list its forms')\n return reply.code(500).send(STORE_UNAVAILABLE)\n }\n const context = steps.context(request)\n const allowed = steps.connections()\n const forms: MenuForm[] = []\n for (const id of ids) {\n const read = await entry(steps.store, allowed, id, context, request.log)\n if (read !== undefined) forms.push(read)\n }\n return formMenu(forms) satisfies FormMenu\n })\n}\n","import {\n authorizeOperation,\n checkSubmittedFields,\n deleteEffects,\n driftRefusal,\n grantedOperations,\n intendedRecord,\n listable,\n listColumns,\n NOTHING_LEFT,\n planCreate,\n planRead,\n planUpdate,\n READ_REFUSED,\n readableFields,\n rejectedSelection,\n runtimeOperations,\n toFormAnswers,\n} from '@formancy/data-core'\nimport type {\n DescribedTable,\n DriftChange,\n FieldError,\n FormRecord,\n MembershipCheck,\n PlanRefusal,\n PolicyContext,\n PolicyOperation,\n PublishedForm,\n RecordFailure,\n} from '@formancy/data-core'\nimport { canonicalize } from '@formancy/spec'\nimport type { FastifyBaseLogger, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport type { PublishedBundle } from '../bundle.js'\nimport type { ConfigurationStore } from '../config-store.js'\nimport type { ConnectionRegistry, OpenConnection } from '../connections.js'\nimport type { HostIdentity } from '../identity.js'\nimport type { AuditSink, RuntimeAuditEvent, RuntimeOperation } from '../audit.js'\nimport { auditedForm, auditRequests, recordReference } from '../audit.js'\nimport { lookupDeadline } from '../lookup-deadline.js'\nimport { loadPublished } from '../published.js'\nimport { registerDeleteRoute } from './runtime-deletes.js'\nimport { planRefusal, recordFailure, unknownOutcome } from './runtime-errors.js'\nimport { registerListRoute } from './runtime-lists.js'\nimport { registerLookupRoutes } from './runtime-lookups.js'\nimport { registerMenuRoute } from './runtime-menu.js'\nimport { createWriteOnce, INVALID_WRITE_ID, writeId } from './write-once.js'\nimport type { WriteAnswer } from './write-once.js'\n\nexport interface RuntimeOptions {\n registry: ConnectionRegistry\n store: ConfigurationStore\n authenticate: (request: FastifyRequest) => Promise<HostIdentity | undefined>\n /**\n * The operational trail (0023): one event per request, outcomes included.\n * `key` names records by a keyed hash; without it, no record is named.\n */\n audit?: { sink: AuditSink; key?: string; now?: () => string }\n /**\n * How long a lookup statement may run before the server stops it, the\n * membership checks inside a create or an update included (0061).\n */\n lookupDeadlineMs: number\n}\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n /** `repeated`: answered with an earlier sending's answer, and nothing asked of the database (0031). */\n auditTrail?: { formVersion: number | null; record?: string; repeated?: boolean }\n }\n}\n\n/**\n * The audit name of each runtime route, keyed `METHOD url`; a route without\n * one stops the server, or never answers (0033). `admin-audit.test.ts` sends\n * every route the router holds and fails on an entry no route has. Exported\n * from the package: the host page's suite reads the runtime plane's routes\n * here rather than keeping a list of its own (0047).\n */\nexport const ROUTE_OPERATIONS: Readonly<Record<string, RuntimeOperation>> = {\n 'GET /v1/forms': 'form-list',\n 'GET /v1/forms/:id': 'form',\n 'POST /v1/forms/:id/records/read': 'read',\n 'POST /v1/forms/:id/records/list': 'list',\n 'POST /v1/forms/:id/records/create': 'create',\n 'POST /v1/forms/:id/records/update': 'update',\n 'POST /v1/forms/:id/records/delete': 'delete',\n 'POST /v1/forms/:id/lookups/:source/query': 'lookup-query',\n 'POST /v1/forms/:id/lookups/:source/resolve': 'lookup-resolve',\n}\n\nexport { MAX_RESOLVE } from './runtime-lookups.js'\n\n/** A read that found no record inside the filters, as the port's failure, for the answers that need one. */\nconst NOT_FOUND: RecordFailure = { ok: false, code: 'not-found', message: 'No such record exists inside the filters.' }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction contextOf(identity: HostIdentity): PolicyContext {\n return { actor: { id: identity.actor.id, roles: [...identity.actor.roles] }, attributes: { ...identity.attributes } }\n}\n\n/**\n * The answers a renderer submits, minus the fields it echoed back that this\n * actor may not write.\n *\n * A formancy renderer submits every field, disabled ones included, and the\n * policy calls a submitted field the actor may not write over-posting (0011).\n * Whether the actor may write a field is the policy's decision, so it is asked\n * — one key at a time, through `checkSubmittedFields` — rather than restated\n * here: a defaulted column the generator left writable can still be one the\n * policy grants nobody. The echo is removed on create when it is empty, and on\n * update when it equals what the record holds. Anything else in such a field\n * still reaches the planner and is refused: a changed value there is tampering\n * or a stale client, and either way is not saved (0022). An unedited instant\n * or time the actor may write is the planner's to remove, against the same\n * read (0040).\n */\nfunction withoutEchoes(\n bundle: PublishedBundle,\n actor: PolicyContext,\n operation: 'create' | 'update',\n answers: unknown,\n current?: Readonly<Record<string, unknown>>,\n): unknown {\n if (!isRecord(answers)) return answers\n const kept: Record<string, unknown> = { ...answers }\n for (const binding of bundle.bindings.fields) {\n if (!Object.hasOwn(kept, binding.field)) continue\n if (checkSubmittedFields(bundle.policy, actor, bundle.bindings, operation, [binding.field]).ok) continue\n const value = kept[binding.field]\n const echo = current === undefined\n ? value === null || value === undefined || value === ''\n : Object.hasOwn(current, binding.field) && canonicalize(value ?? null) === canonicalize(current[binding.field] ?? null)\n if (echo) delete kept[binding.field]\n }\n return kept\n}\n\n/**\n * The runtime plane: a published form, its records and its lookups, for the\n * host application's people (plan section 13). Every route needs a verified\n * host token; every decision about what that person may do is the published\n * policy's, through the planner (0018), and every database answer is the\n * adapter's, through the ports (0015).\n */\nexport async function runtimeRoutes(app: FastifyInstance, options: RuntimeOptions): Promise<void> {\n const { registry, store } = options\n const writes = createWriteOnce()\n\n app.addHook('onRequest', async (request) => {\n request.auditTrail = { formVersion: null }\n })\n\n // Before any route, so a route without an audit name stops the server,\n // with a sink or without one (0033).\n const key = options.audit?.key\n auditRequests(app, {\n names: ROUTE_OPERATIONS,\n sink: options.audit?.sink,\n now: options.audit?.now,\n describe: (request, base): RuntimeAuditEvent => {\n const trail = request.auditTrail ?? { formVersion: null }\n return {\n at: base.at,\n plane: 'runtime',\n actor: base.actor,\n operation: base.operation,\n form: auditedForm((request.params as { id?: unknown }).id),\n formVersion: trail.formVersion,\n status: base.status,\n outcome: trail.repeated === true ? 'repeated' : base.outcome,\n record: recordReference(key, trail.record),\n }\n },\n })\n\n app.addHook('preHandler', async (request, reply) => {\n const identity = await options.authenticate(request)\n if (identity === undefined) {\n return reply.code(401).header('www-authenticate', 'Bearer').send({ code: 'unauthenticated', message: 'A valid host token is required.' })\n }\n request.identity = identity\n return undefined\n })\n\n const context = (request: FastifyRequest): PolicyContext => {\n if (request.identity === undefined) throw new Error('the preHandler sets the identity before any route runs')\n return contextOf(request.identity)\n }\n\n async function published(id: string, reply: FastifyReply): Promise<PublishedBundle | undefined> {\n const loaded = await loadPublished(store, id, reply.log)\n if (loaded.ok) {\n if (reply.request.auditTrail !== undefined) reply.request.auditTrail.formVersion = loaded.version\n return loaded.bundle\n }\n await reply.code(loaded.status).send({ code: loaded.code, message: loaded.message })\n return undefined\n }\n\n async function connection(bundle: PublishedBundle, reply: FastifyReply): Promise<OpenConnection | undefined> {\n try {\n const open = await registry.open(bundle.connection)\n if (open !== undefined) return open\n reply.log.error({ connection: bundle.connection }, 'a published form names a connection the allowlist does not have')\n await reply.code(500).send({ code: 'unknown-connection', message: 'This form is published against a connection this server does not have.' })\n } catch (error) {\n reply.log.warn({ connection: bundle.connection, error: (error as Error).message }, 'connection could not be opened')\n await reply.code(503).send({ code: 'unavailable', message: 'The database cannot be reached. Nothing was saved.' })\n }\n return undefined\n }\n\n /**\n * A write that was sent and whose answer was lost (0031): said once in the\n * log, with the adapter's sentence, which names no value; answered with what\n * was addressed; and never sent again -- each route asks the adapter once.\n */\n function lost(request: FastifyRequest<{ Params: { id: string } }>, operation: 'create' | 'update' | 'delete', record: string | null, version: string | null, failure: string): WriteAnswer {\n request.log.warn({ form: request.params.id, operation, failure }, 'a write was sent and its answer lost; it is not retried')\n return { ...unknownOutcome(operation, record, version), record: record ?? undefined }\n }\n\n /**\n * Sends a write to the database once per sending (write-once.ts): `perform`\n * runs the first time its write id arrives, and a later arrival is answered\n * with that answer. A request without an id is performed every time.\n */\n async function sendOnce(\n request: FastifyRequest<{ Params: { id: string } }>,\n operation: 'create' | 'update' | 'delete',\n id: string | undefined,\n perform: () => Promise<WriteAnswer>,\n ): Promise<WriteAnswer & { repeated?: boolean }> {\n const actor = request.identity?.actor.id\n if (id === undefined || actor === undefined) return perform()\n return writes.once({ actor, form: request.params.id, operation, id, body: request.body }, perform)\n }\n\n /**\n * A drift refusal of the database as it is now (0041), said once in the\n * log with what blocks, by kind and the fields it affects -- never a value,\n * and never the change's message, which names columns and types the person\n * is not told -- and answered with the planner's sentence.\n */\n function driftAnswer(request: FastifyRequest<{ Params: { id: string } }>, operation: PolicyOperation | 'list' | 'form', changes: readonly DriftChange[] | undefined, message: string): WriteAnswer {\n request.log.warn(\n { form: request.params.id, version: request.auditTrail?.formVersion ?? null, operation, changes: (changes ?? []).map((change) => ({ kind: change.kind, affects: change.affects })) },\n 'refused: the database no longer fits this form as published',\n )\n return planRefusal('drift', message)\n }\n\n /** A planner's refusal as the answer, its drift said in the log first, under the operation the audit names. */\n function refusal(request: FastifyRequest<{ Params: { id: string } }>, operation: PolicyOperation | 'list', plan: PlanRefusal): WriteAnswer {\n return plan.code === 'drift' && plan.drift !== undefined ? driftAnswer(request, operation, plan.drift, plan.message) : planRefusal(plan.code, plan.message)\n }\n\n /** The root as the catalog describes it now, or the answer when it cannot be described: 503 when the database cannot say, 409 when the table is not one it describes. */\n async function describedRoot(bundle: PublishedBundle, open: OpenConnection): Promise<{ ok: true; described: DescribedTable } | { ok: false; answer: WriteAnswer }> {\n const found = await open.records.describe(bundle.bindings.root)\n return found.ok ? { ok: true, described: found.described } : { ok: false, answer: recordFailure(bundle.bindings, found) }\n }\n\n /** A write's answer, sent, with what the audit names: its record, and whether it was a repeat. */\n async function answered(reply: FastifyReply, answer: WriteAnswer & { repeated?: boolean }): Promise<FastifyReply> {\n const trail = reply.request.auditTrail\n if (trail !== undefined && answer.record !== undefined) trail.record = answer.record\n if (trail !== undefined && answer.repeated === true) trail.repeated = true\n return reply.code(answer.status).send(answer.body)\n }\n\n /**\n * Runs every membership check: undefined when every selection is a member,\n * otherwise the refusal to answer. Answered rather than sent, so an\n * update's checks run inside the sending they belong to (0031).\n *\n * Each check has a lookup deadline of its own, counted from when the\n * adapter is asked for it, and nothing else (0061): a write goes on after\n * its client leaves (0031), so no abandonment stops it. A check\n * past its deadline keeps the answer every failed check has, nothing\n * saved, and the log says the deadline passed.\n */\n async function memberships(open: OpenConnection, checks: readonly MembershipCheck[], log: FastifyBaseLogger): Promise<WriteAnswer | undefined> {\n const errors: FieldError[] = []\n for (const check of checks) {\n const deadline = lookupDeadline(options.lookupDeadlineMs)\n try {\n const rejected = await open.lookups.rejects(check.config, check.tokens, check.filters, deadline.signal)\n if (rejected.length > 0) errors.push(rejectedSelection(check.field))\n } catch (error) {\n // Fails closed, as formancy's own membership port does (formancy.ai 0077).\n if (deadline.signal.aborted && error === deadline.signal.reason) log.warn({ field: check.field, deadlineMs: options.lookupDeadlineMs }, 'a lookup could not vouch for a selection: it passed its deadline, and the database was told to stop it')\n else log.warn({ field: check.field, error: (error as Error).message }, 'a lookup could not vouch for a selection')\n return { status: 503, body: { code: 'unavailable', message: 'A selection could not be checked. Nothing was saved.' } }\n } finally {\n deadline.clear()\n }\n }\n return errors.length === 0 ? undefined : { status: 422, body: { code: 'invalid-values', message: 'A selection is not one of the options.', fieldErrors: errors } }\n }\n\n /**\n * One create, from the description it is decided over to the insert,\n * answered rather than sent: the create route runs it once per sending, so\n * a write id that arrives again is answered with this answer and asks the\n * database nothing (0031). The mirror of `updated`.\n *\n * The policy is asked before any statement, so an actor it refuses reaches\n * none (0041). Then the root is described, the one statement before the\n * planner: a create the description stops is refused there, with no\n * membership check and no insert sent, and the insert carries the\n * definition it was decided over.\n */\n async function created(request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, open: OpenConnection, answers: Record<string, unknown>): Promise<WriteAnswer> {\n const actor = context(request)\n const authorized = authorizeOperation(bundle.policy, actor, 'create')\n if (!authorized.ok) return planRefusal(authorized.code, authorized.message)\n const root = await describedRoot(bundle, open)\n if (!root.ok) return root.answer\n const plan = planCreate(bundle.snapshot, bundle.bindings, bundle.policy, actor, withoutEchoes(bundle, actor, 'create', answers), root.described)\n if (!plan.ok) {\n if (plan.code === 'invalid-values') return { status: 422, body: { code: plan.code, message: plan.message, fieldErrors: plan.fieldErrors } }\n return refusal(request, 'create', plan)\n }\n const refused = await memberships(open, plan.memberships, request.log)\n if (refused !== undefined) return refused\n const outcome = await open.records.insert(plan.request)\n if (!outcome.ok && outcome.code === 'unknown-outcome') return lost(request, 'create', intendedRecord(plan.request), null, outcome.message)\n if (!outcome.ok) return recordFailure(bundle.bindings, outcome)\n const done = toFormAnswers(bundle.bindings, plan.fields, outcome)\n return { status: 201, body: done, record: done.record ?? undefined }\n }\n\n /**\n * One update, from the read that tells an echo from a change to the write,\n * answered rather than sent: the update route runs it once per sending, so\n * a write id that arrives again is answered with this answer and asks the\n * database nothing, its read included (0031).\n *\n * The policy is asked first, before any statement (0041). The record as it\n * stands is read next, and the read carries the table's description, which\n * the update is decided over. Against the record 0022 removes an unchanged\n * echo of a field the actor may not write, and the planner one of an\n * instant or a time they may (0040). An actor who may update and not read\n * gets no echo removed, and the planner needs no read for them: the table\n * is described for them in a statement of its own, as it is after a read\n * that failed. When the planner cannot tell an echo from a change because\n * the read failed, whatever the failure, that failure is the answer: sent\n * anyway, a read that failed and a write that did not would store the cut\n * value.\n */\n async function updated(request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, open: OpenConnection, record: string, version: string, answers: Record<string, unknown>): Promise<WriteAnswer> {\n const actor = context(request)\n const authorized = authorizeOperation(bundle.policy, actor, 'update')\n if (!authorized.ok) return planRefusal(authorized.code, authorized.message)\n let current: FormRecord | undefined\n let unread: RecordFailure | undefined\n let described: DescribedTable | undefined\n const reading = planRead(bundle.snapshot, bundle.bindings, bundle.policy, actor, record)\n if (reading.ok) {\n const stored = await open.records.read(reading.request)\n if (!stored.ok) unread = stored\n else {\n described = stored.described\n if (stored.record === null) unread = NOT_FOUND\n else current = toFormAnswers(bundle.bindings, reading.fields, { ok: true, ...stored.record })\n }\n }\n if (described === undefined) {\n const root = await describedRoot(bundle, open)\n if (!root.ok) return root.answer\n described = root.described\n }\n const plan = planUpdate(bundle.snapshot, bundle.bindings, bundle.policy, actor, record, version, withoutEchoes(bundle, actor, 'update', answers, current?.answers), current, described)\n if (!plan.ok) {\n if (plan.code === 'invalid-values') return { status: 422, body: { code: plan.code, message: plan.message, fieldErrors: plan.fieldErrors } }\n if (plan.code === 'record-not-read' && unread !== undefined) return recordFailure(bundle.bindings, unread)\n return refusal(request, 'update', plan)\n }\n const refused = await memberships(open, plan.memberships, request.log)\n if (refused !== undefined) return refused\n const outcome = await open.records.update(plan.request)\n if (!outcome.ok && outcome.code === 'unknown-outcome') return lost(request, 'update', record, version, outcome.message)\n if (!outcome.ok) return recordFailure(bundle.bindings, outcome)\n return { status: 200, body: toFormAnswers(bundle.bindings, plan.fields, outcome) }\n }\n\n /**\n * A read the database refused, a record's or a page's: one that names a\n * column that is gone -- `schema-changed` -- returned no description, so\n * the table is described once more and the read decided over that, and a\n * form drift stops is answered as drift whichever statement found it out\n * (0041). Any other failure is the database's. A page is decided as a\n * list (0044), and said in the log as one.\n */\n async function unread(request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, open: OpenConnection, failure: RecordFailure, operation: 'read' | 'list'): Promise<WriteAnswer> {\n const root = failure.code === 'schema-changed' ? await describedRoot(bundle, open) : undefined\n const refused = root?.ok === true ? driftRefusal(bundle.snapshot, bundle.bindings, bundle.policy, root.described, operation) : undefined\n return refused === undefined ? recordFailure(bundle.bindings, failure) : refusal(request, operation, refused)\n }\n\n /**\n * A read decided over the description its own statement carried (0041):\n * the refusal when drift stops reading this form -- or, for a page, when\n * the table no longer holds its key to a total order (0044) -- or\n * `undefined`.\n */\n function undecided(request: FastifyRequest<{ Params: { id: string } }>, bundle: PublishedBundle, described: DescribedTable, operation: 'read' | 'list'): WriteAnswer | undefined {\n const refused = driftRefusal(bundle.snapshot, bundle.bindings, bundle.policy, described, operation)\n return refused === undefined ? undefined : refusal(request, operation, refused)\n }\n\n // The forms this person may read (0045): from the published bundles and the policy, asking no database.\n registerMenuRoute(app, { store, connections: () => registry.ids(), context })\n\n /**\n * The form, what this person may do with it now, and which fields they may\n * see. The policy first, before any statement; then the root is described,\n * and only what the database as it is now still allows is offered (0041):\n * a form whose records it can no longer show faithfully is refused, and so\n * is one that leaves this person nothing. Opening a form therefore asks the\n * database, and one that cannot be reached is 503.\n */\n app.get<{ Params: { id: string } }>('/v1/forms/:id', async (request, reply) => {\n const bundle = await published(request.params.id, reply)\n if (bundle === undefined) return reply\n const actor = context(request)\n // What the policy grants of what the form offers, as the menu says it (0045); then what the database allows now.\n const allowed = grantedOperations(bundle.bindings, bundle.policy, actor)\n if (allowed.length === 0) return reply.code(403).send({ code: 'operation-denied', message: 'This form is not available to you.' })\n const open = await connection(bundle, reply)\n if (open === undefined) return reply\n const root = await describedRoot(bundle, open)\n if (!root.ok) return answered(reply, root.answer)\n const decided = runtimeOperations(bundle.snapshot, bundle.bindings, bundle.policy, root.described)\n if (!decided.ok) return answered(reply, planRefusal(decided.code, decided.message))\n const { verdict } = decided\n if (!verdict.readable) return answered(reply, driftAnswer(request, 'form', decided.drift, READ_REFUSED))\n const operations = allowed.filter((operation) => operation === 'read' || verdict.writable[operation])\n if (operations.length === 0) return answered(reply, driftAnswer(request, 'form', decided.drift, NOTHING_LEFT))\n const readable = readableFields(bundle.policy, actor, bundle.bindings)\n // What `planList` will accept, from the function it asks, and what a page\n // over this description would be decided as, so the page never offers a\n // grid it would be refused (0044).\n const listed = listable(bundle.snapshot, bundle.bindings, bundle.policy, actor)\n const list = listed.ok && driftRefusal(bundle.snapshot, bundle.bindings, bundle.policy, root.described, 'list') === undefined ? listColumns(bundle.bindings, listed.fields) : null\n // What a delete also does, for a page to word its confirmation by: only where one is offered (0050).\n const deleting = operations.includes('delete') ? deleteEffects(bundle.snapshot, bundle.bindings.root, bundle.policy) : null\n return { form: bundle.form, operations, readable: readable.ok ? readable.fields : [], list, deleting } satisfies PublishedForm\n })\n\n /**\n * One record. The policy and the token are decided before any statement;\n * the read statement returns the table's description beside the record,\n * and the read is decided over it after it ran (0041): refused, its values\n * are discarded and never reach the body. A read the database refused as\n * `schema-changed` -- it names a column that is gone -- returned no\n * description, so the table is described once more and the read decided\n * over that: a form drift stops is answered as drift, whichever statement\n * found it out.\n */\n app.post<{ Params: { id: string } }>('/v1/forms/:id/records/read', async (request, reply) => {\n const body = request.body\n if (!isRecord(body) || typeof body['record'] !== 'string') return reply.code(400).send({ code: 'invalid-request', message: 'Expected { record }.' })\n if (request.auditTrail !== undefined) request.auditTrail.record = body['record']\n const bundle = await published(request.params.id, reply)\n if (bundle === undefined) return reply\n const plan = planRead(bundle.snapshot, bundle.bindings, bundle.policy, context(request), body['record'])\n if (!plan.ok) return answered(reply, planRefusal(plan.code, plan.message))\n const open = await connection(bundle, reply)\n if (open === undefined) return reply\n const outcome = await open.records.read(plan.request)\n if (!outcome.ok) return answered(reply, await unread(request, bundle, open, outcome, 'read'))\n const drifted = undecided(request, bundle, outcome.described, 'read')\n if (drifted !== undefined) return answered(reply, drifted)\n if (outcome.record === null) return answered(reply, recordFailure(bundle.bindings, NOT_FOUND))\n return toFormAnswers(bundle.bindings, plan.fields, { ok: true, ...outcome.record })\n })\n\n // A page of records is a read of many (0044): its steps are the read's, and so is every decision between them.\n registerListRoute(app, { published, connection, context, unread, undecided, answered })\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/records/create', async (request, reply) => {\n const body = request.body\n if (!isRecord(body) || !isRecord(body['answers'])) return reply.code(400).send({ code: 'invalid-request', message: 'Expected { answers }.' })\n const id = writeId(request)\n if (id === null) return answered(reply, INVALID_WRITE_ID)\n const bundle = await published(request.params.id, reply)\n if (bundle === undefined) return reply\n const open = await connection(bundle, reply)\n if (open === undefined) return reply\n const answers = body['answers']\n return answered(reply, await sendOnce(request, 'create', id, () => created(request, bundle, open, answers)))\n })\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/records/update', async (request, reply) => {\n const body = request.body\n if (!isRecord(body) || typeof body['record'] !== 'string' || typeof body['version'] !== 'string' || !isRecord(body['answers'])) {\n return reply.code(400).send({ code: 'invalid-request', message: 'Expected { record, version, answers }.' })\n }\n if (request.auditTrail !== undefined) request.auditTrail.record = body['record']\n const id = writeId(request)\n if (id === null) return answered(reply, INVALID_WRITE_ID)\n const bundle = await published(request.params.id, reply)\n if (bundle === undefined) return reply\n const open = await connection(bundle, reply)\n if (open === undefined) return reply\n const { record, version, answers } = body\n return answered(reply, await sendOnce(request, 'update', id, () => updated(request, bundle, open, record, version, answers)))\n })\n\n // One record, by its token and the version read (0050): the update's steps, a delete's answers.\n registerDeleteRoute(app, { published, connection, context, sendOnce, lost, refusal, answered })\n\n // A lookup's options and labels, each one statement that stops when its request is abandoned or its deadline passes (0061).\n registerLookupRoutes(app, { published, connection, context, lookupDeadlineMs: options.lookupDeadlineMs })\n}\n","/**\n * Across origins (0060): the runtime plane answers a page on another origin\n * when the operator lists that origin exactly, and nothing else does.\n *\n * - `FORMANCY_DATA_CORS_ORIGINS`, read by main.ts and parsed here, and\n * `runtime.origins`, which createDataServer checks with the same rule.\n * - `crossOrigin`, which createDataServer calls on every server: with a\n * list, an `OPTIONS` route at each runtime path that grants a preflight\n * only where the method asked reaches the runtime route at that path, the\n * origin carried back on every answer of a runtime route, and a check at\n * ready that refuses a route another plane could answer at a runtime\n * path; with or without one, a token-bearing request a browser marks\n * cross-site or same-site refused before its token is read unless its\n * origin is listed and its route is the runtime plane's.\n *\n * Never a wildcard, a pattern, `null` or credentials, and never HEAD.\n */\nimport { CROSS_ORIGIN_REQUEST_HEADERS } from '@formancy/data-core'\nimport type { FastifyInstance, FastifyRequest } from 'fastify'\nimport { ROUTE_OPERATIONS } from './routes/runtime.js'\n\n/**\n * How long a browser may keep a granted preflight, in seconds. Each engine\n * caps what it keeps, read on main on 2026-10-13: WebKit at 600\n * (`maxPreflightCacheTimeout`, CrossOriginPreflightResultCache.cpp:43),\n * Chromium at two hours (`kMaxTimeout`, services/network/cors/\n * preflight_result.cc:40) and Firefox at 86400 (nsCORSListenerProxy.cpp:\n * 1404), so 600 is the longest every engine honours as written. It is also\n * the longest a browser that cached a grant keeps sending without asking\n * after its origin leaves the list, which the refusal below closes for\n * every browser that sends Sec-Fetch-Site. A choice within those caps, not\n * a measurement: nothing here measured a cost that would choose another.\n */\nexport const PREFLIGHT_MAX_AGE_SECONDS = 600\n\n/** The answer to a refused preflight, and to a token-bearing request refused across origins: a code and a sentence, and nothing of the request. */\nexport const CROSS_ORIGIN_REFUSED = {\n code: 'cross-origin-refused',\n message: 'Across origins this server answers only the runtime plane’s routes, from an origin its operator lists, and this request is not one of those, so it was refused before any work.',\n} as const\n\nconst SETTING = 'FORMANCY_DATA_CORS_ORIGINS'\n\n/** Plain HTTP is accepted on these hosts only, where a developer's page is: never across a network (0060). */\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]'])\n\n/** One label of a host name as a browser's URL parser leaves it: lower case, digits, `-` and `_`. Anchored, one class, so linear (CodeQL). */\nconst LABEL = /^[a-z0-9_-]+$/\n\n/**\n * Why `entry` is not an origin this server lists, as the end of a sentence\n * that never repeats it, or `undefined`. An origin is accepted only when\n * the URL parser gives it back unchanged as its origin -- so lower case, no\n * path, no trailing slash, no default port, no user information, a punycode\n * name -- with a host that is a DNS name or an IP address, over `https:`, or\n * `http:` on loopback.\n */\nfunction originProblem(entry: unknown): string | undefined {\n if (typeof entry !== 'string') return 'is not a string'\n if (entry === '*' || entry === 'null') return 'is a wildcard or the opaque origin, which is never listed: each origin is named exactly'\n let url: URL\n try {\n url = new URL(entry)\n } catch {\n return 'is not an origin: a scheme, a host and, where it is not the default, a port'\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') return 'is not an https: origin'\n if (url.origin !== entry) return 'is not written as a browser sends Origin: lower case, a punycode name, no path or trailing slash, no default port and no user information'\n const host = url.hostname\n if (!(host.startsWith('[') || host.split('.').every((label) => LABEL.test(label)))) return 'names a pattern or a host that is no DNS name or IP address'\n if (url.protocol === 'http:' && !LOOPBACK.has(host)) return 'is plain HTTP on a host other than localhost, 127.0.0.1 or [::1]: anything else is listed as https:'\n return undefined\n}\n\nexport type CorsOriginsSetting = { ok: true; origins: readonly string[] } | { ok: false; problem: string }\n\n/**\n * `FORMANCY_DATA_CORS_ORIGINS` as written: origins separated by commas,\n * space around each ignored. Unset, empty, or nothing but commas and space\n * is no origin -- Compose passes `\"\"` for a variable it was not given. Any\n * entry that is not an origin refuses the whole setting, named by its place\n * and never quoted; and a list needs the runtime plane, whose routes are all\n * it grants.\n */\nexport function corsOriginsSetting(text: string | undefined, plane: { runtime: boolean }): CorsOriginsSetting {\n const entries = (text ?? '').split(',').map((entry) => entry.trim()).filter((entry) => entry !== '')\n for (const [index, entry] of entries.entries()) {\n const problem = originProblem(entry)\n if (problem !== undefined) return { ok: false, problem: `${SETTING}: entry ${String(index + 1)} ${problem}.` }\n }\n if (entries.length > 0 && !plane.runtime) {\n return { ok: false, problem: `${SETTING} lists origins, and the runtime plane is off: set FORMANCY_DATA_STORE_DIR and FORMANCY_DATA_CONNECTIONS, or leave it empty.` }\n }\n return { ok: true, origins: [...new Set(entries)] }\n}\n\n/** What is wrong with `runtime.origins` as an embedder passed it, by place, or `undefined`: the setting's rule, with no trimming. */\nexport function originsProblem(origins: unknown): string | undefined {\n if (!Array.isArray(origins)) return 'runtime.origins is not a list of origins.'\n for (const [index, entry] of origins.entries()) {\n const problem = originProblem(entry)\n if (problem !== undefined) return `runtime.origins[${String(index)}] ${problem}.`\n }\n return undefined\n}\n\n/** `existing` Vary with `name` added once, by its token case-insensitively; `*` stays `*`. */\nexport function varyWith(existing: number | string | readonly string[] | undefined, name: string): string {\n const tokens = [existing ?? []].flat().flatMap((value) => String(value).split(',')).map((token) => token.trim()).filter((token) => token !== '')\n if (tokens.includes('*')) return '*'\n return tokens.some((token) => token.toLowerCase() === name.toLowerCase()) ? tokens.join(', ') : [...tokens, name].join(', ')\n}\n\n/**\n * The origin as a log line may name it: only when the URL parser gives it\n * back unchanged and it is no longer than an origin can be (`https://`, a\n * 253-character name and a port). Such an origin holds no user information,\n * path or query, so nothing a caller pasted into a URL rides into the log\n * with it, and it is what the operator would add to the list (0060).\n * Anything else is logged with no origin at all.\n */\nfunction loggable(origin: unknown): { origin?: string } {\n if (typeof origin !== 'string' || origin.length > 267) return {}\n try {\n return new URL(origin).origin === origin ? { origin } : {}\n } catch {\n return {}\n }\n}\n\n/** `METHOD url` as the runtime table keys it: HEAD is HEAD, and the table names none, so it is never offered across origins. */\nconst routeKey = (request: FastifyRequest) => `${request.method} ${request.routeOptions.url ?? ''}`\n\nconst OFFERED = new Set(Object.keys(ROUTE_OPERATIONS))\n\n/** A route as the check at ready sees it. */\ninterface RegisteredRoute {\n method: string\n url: string\n constraints?: unknown\n}\n\n/** A segment of a pattern: a plain `:name`, static text, or something the overlap rule does not model. */\nfunction segmentKind(segment: string): 'parameter' | 'static' | 'unmodelled' {\n if (/^:[A-Za-z0-9_]+$/.test(segment)) return 'parameter'\n return segment.includes(':') || segment.includes('*') ? 'unmodelled' : 'static'\n}\n\n/** Whether a path exists that both patterns, each of plain segments, match: the same number of segments, each equal static text or a parameter on either side. */\nfunction overlap(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((segment, index) => segment === b[index] || segmentKind(segment) === 'parameter' || segmentKind(String(b[index])) === 'parameter')\n}\n\n/**\n * The methods a browser sends on a URL's grant without asking again, whatever\n * method the grant named: the Fetch standard asks again only for another\n * method or for a header the grant does not name, and a grant names its\n * headers by URL. Measured in Chromium and WebKit, not Firefox (0060).\n */\nconst SAFELISTED = new Set(['GET', 'HEAD', 'POST'])\n\n/**\n * What stops a server with origins at ready, as sentences: a route outside\n * the runtime table whose pattern could match a path a runtime pattern\n * matches -- any runtime pattern for a GET, HEAD or POST, which ride the\n * grant of whatever method was granted at that URL, and one of the same\n * method otherwise. Those are the requests a grant for the runtime route\n * would let a browser send to the other, and the case Fastify's parameters\n * cannot tell apart. OPTIONS routes are this file's, and a HEAD at a\n * runtime GET's own pattern is the one Fastify adds for it: no other can\n * be registered there. Any route whose pattern holds a regex parameter, a\n * wildcard or two parameters in one segment, or that has a constraint,\n * stops it too: the rule does not model those, and refuses rather than\n * guesses.\n */\nfunction routeProblems(routes: readonly RegisteredRoute[]): string[] {\n const problems: string[] = []\n const runtime = [...OFFERED].map((key) => ({ key, method: key.slice(0, key.indexOf(' ')), segments: key.slice(key.indexOf(' ') + 1).split('/') }))\n for (const route of routes) {\n const segments = route.url.split('/')\n const constrained = typeof route.constraints === 'object' && route.constraints !== null && Object.keys(route.constraints).length > 0\n if (constrained || segments.some((segment) => segmentKind(segment) === 'unmodelled')) {\n problems.push(`${route.method} ${route.url} has a pattern or a constraint the check that keeps the planes apart across origins does not model`)\n continue\n }\n const key = `${route.method} ${route.url}`\n if (route.method === 'OPTIONS' || OFFERED.has(key) || (route.method === 'HEAD' && OFFERED.has(`GET ${route.url}`))) continue\n for (const pattern of runtime) {\n const rides = SAFELISTED.has(route.method) || pattern.method === route.method\n if (rides && overlap(segments, pattern.segments)) problems.push(`${key} could answer a path the runtime route ${pattern.key} answers`)\n }\n }\n return problems\n}\n\n/** Whether two routers' parameters are the same names with the same values, in any order. */\nfunction sameParameters(a: Readonly<Record<string, unknown>>, b: Readonly<Record<string, unknown>>): boolean {\n const names = Object.keys(a)\n return names.length === Object.keys(b).length && names.every((name) => Object.hasOwn(b, name) && a[name] === b[name])\n}\n\ntype Preflight = { granted: true; origin: string; method: string } | { granted: false; reason: string }\n\nexport interface CrossOriginOptions {\n /** Origins exactly as a browser sends them, already checked (`originsProblem`); empty, nothing is offered across origins. */\n origins: readonly string[]\n}\n\n/**\n * Called by createDataServer on every server, after the path-not-plain\n * refusal and before any route, so its hooks cover every route registered\n * after it.\n */\nexport function crossOrigin(app: FastifyInstance, options: CrossOriginOptions): void {\n const listed = new Set(options.origins)\n\n // A browser that cached a grant keeps sending without asking for up to\n // Max-Age; after its origin left the list its requests, writes included,\n // would run and only their answers go unread. So a request carrying a\n // token whose Sec-Fetch-Site says the browser sent it from another site\n // is refused here unless its origin is listed and its route the runtime\n // plane's: after every onRequest, the limiter's included, so it is\n // counted wherever the limiter counts, which is every route but /health;\n // before each plane's preHandler, which reads the token, so no token is\n // read and no work done; and a plane's own hooks still write its one\n // event, where the route is a plane's, which /v1/whoami and /health are\n // not. With no route, the router's 404 is left as it is. Only ever\n // narrows: no browser sends a token across origins without a grant.\n app.addHook('preHandler', async (request, reply) => {\n if (request.routeOptions.url === undefined || request.headers.authorization === undefined) return undefined\n const site = request.headers['sec-fetch-site']\n if (site !== 'cross-site' && site !== 'same-site') return undefined\n const origin = request.headers.origin\n const fromListed = typeof origin === 'string' && listed.has(origin)\n if (fromListed && OFFERED.has(routeKey(request))) return undefined\n request.log.info({ reason: fromListed ? 'route not offered across origins' : 'origin not listed', ...loggable(origin) }, 'cross-origin request refused')\n return reply.code(403).send(CROSS_ORIGIN_REFUSED)\n })\n\n if (listed.size === 0) return\n\n // Every route, as it is registered, for the check at ready: Fastify's\n // parameters cannot tell two routes apart whose parameters come out equal.\n const routes: RegisteredRoute[] = []\n app.addHook('onRoute', (route) => {\n for (const method of [route.method].flat()) routes.push({ method, url: route.url, constraints: route.constraints })\n })\n app.addHook('onReady', async () => {\n const problems = routeProblems(routes)\n if (problems.length > 0) throw new Error(`With origins listed, the server does not start beside a route another plane could answer at a runtime path (0060): ${problems.join('; ')}.`)\n })\n\n // Every answer of a runtime route, refusals included -- path-not-plain,\n // 401, 404, 409, the limiter's 429, the plane's 503 -- so a page on a\n // listed origin reads the server's sentence; Vary on all of them, since\n // the same route answers another origin without it. What is written\n // before any hook, the router's 414 and the 503 while closing among it,\n // carries neither (app.ts says which). Never credentials, exposed headers\n // or the private network.\n app.addHook('onSend', async (request, reply, payload) => {\n if (!OFFERED.has(routeKey(request))) return payload\n reply.header('vary', varyWith(reply.getHeader('vary'), 'Origin'))\n const origin = request.headers.origin\n if (typeof origin === 'string' && listed.has(origin)) reply.header('access-control-allow-origin', origin)\n return payload\n })\n\n /** A preflight at `pattern`, decided: the origin, the method the runtime table names here and the router takes to the same parameters, and only the headers the server reads. */\n const decide = (request: FastifyRequest, pattern: string): Preflight => {\n const origin = request.headers.origin\n const method = request.headers['access-control-request-method']\n if (typeof origin !== 'string' || typeof method !== 'string') return { granted: false, reason: 'not a preflight' }\n if (!listed.has(origin)) return { granted: false, reason: 'origin not listed' }\n if (!OFFERED.has(`${method} ${pattern}`)) return { granted: false, reason: 'method not offered on this route' }\n // Where the method takes this path, as far as Fastify says: its\n // parameters. A route of another plane at this path gives others. The\n // router leaves a query out, as it does routing the request. No route\n // at all cannot be, the runtime route being here; it is refused all the\n // same. Behind the check at ready, which refuses every such route\n // registered after this call, it holds only a route registered before\n // it, which createDataServer has none of; and it asks for the method\n // asked alone: another riding the grant is the check at ready's.\n const reached = app.findRoute({ method: method as 'GET', url: request.url })\n if (reached === null || !sameParameters(reached.params, request.params as Record<string, unknown>)) return { granted: false, reason: 'method reaches another route' }\n const asked = String(request.headers['access-control-request-headers'] ?? '').split(',').map((name) => name.trim().toLowerCase()).filter((name) => name !== '')\n if (!asked.every((name) => (CROSS_ORIGIN_REQUEST_HEADERS as readonly string[]).includes(name))) return { granted: false, reason: 'header not allowed' }\n return { granted: true, origin, method }\n }\n\n // One OPTIONS route per runtime path, from the table the plane registers\n // from, outside both planes: a preflight reads no token and does no work,\n // so it is no audit event, and it is not counted, so the limiter's 429,\n // which the page can read, never becomes a refused preflight, which it\n // cannot. An OPTIONS anywhere else has no route: the router's 404.\n const vary = 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers'\n for (const pattern of new Set([...OFFERED].map((key) => key.slice(key.indexOf(' ') + 1)))) {\n app.options(pattern, { config: { rateLimit: false } }, async (request, reply) => {\n const decided = decide(request, pattern)\n if (!decided.granted) {\n request.log.info({ reason: decided.reason, ...loggable(request.headers.origin) }, 'preflight refused')\n return reply.code(403).header('vary', vary).send(CROSS_ORIGIN_REFUSED)\n }\n return reply\n .code(204)\n .headers({\n 'access-control-allow-origin': decided.origin,\n 'access-control-allow-methods': decided.method,\n 'access-control-allow-headers': CROSS_ORIGIN_REQUEST_HEADERS.join(', '),\n 'access-control-max-age': String(PREFLIGHT_MAX_AGE_SECONDS),\n vary,\n })\n .send()\n })\n }\n}\n","/**\n * The paths this server routes (0037).\n *\n * A proxy in front of the server may choose where to forward a request by its\n * path after decoding it, resolving its `.` and `..` segments and collapsing\n * its slashes. cloudflared 2026.10.0 was measured doing all three, so that\n * `/v1/forms/a/versions/..%2F..%2Fb` matched a door meant for the runtime\n * route `/v1/forms/b`, while Fastify, which routes the path as sent, ran the\n * administrator's version-read for it. Rather than model anybody's\n * normalisation, the server routes only a path in which there is nothing to\n * normalise, and refuses every other before any route's work.\n *\n * No name the server accepts needs more. A form id and a connection id start\n * with a lower-case letter or digit and hold only those, `.`, `-` and `_`; a\n * version is digits or `latest`; a lookup source is formancy's option-source\n * name, lower case, digits and hyphens. None can be `.` or `..`, and\n * `encodeURIComponent`, which the studio and data-client send names through,\n * leaves each as it is. paths.test.ts derives that for form and connection\n * ids from the store's and the allowlist's own checks; for versions and\n * sources it holds a sample.\n */\n\n/** One segment: RFC 3986's unreserved characters, its sub-delims, `:` and `@`. No `%`, no `\\`, nothing outside ASCII. */\nconst SEGMENT = /^[A-Za-z0-9\\-._~!$&'()*+,;=:@]+$/\n\n/**\n * Whether a request target is a plain path: before any `?`, a `/` and then\n * one or more segments, each non-empty, neither `.` nor `..`, and made only of\n * characters a path carries as themselves. So no escape, no backslash, no\n * empty segment (a doubled or trailing slash, or `/` alone), no dot segment\n * and no `#`. The query is not looked at: neither the router nor a door\n * routes by it.\n */\nexport function plainPath(target: string): boolean {\n const query = target.indexOf('?')\n const path = query === -1 ? target : target.slice(0, query)\n if (!path.startsWith('/')) return false\n return path\n .slice(1)\n .split('/')\n .every((segment) => segment !== '.' && segment !== '..' && SEGMENT.test(segment))\n}\n\n/** The answer to a path that is not plain, with 400: a code and a sentence, and nothing of the path, which is whatever the caller typed. */\nexport const PATH_NOT_PLAIN = {\n code: 'path-not-plain',\n message:\n \"This path holds an encoded character, an empty or a dot segment, or a character a path does not carry as itself. No name here is written that way, and a proxy in front of this server may read such a path differently, so it was not routed to any route's work.\",\n} as const\n","/**\n * The per-address request limit, and the setting that changes it.\n *\n * `FORMANCY_DATA_RATE_LIMIT` is read by the composition root (main.ts) and\n * parsed here, so the rule is tested without starting a process.\n */\n\n/** Requests per client address per window, unless the operator says otherwise: 600 a minute, as every earlier release had. */\nexport const DEFAULT_RATE_LIMIT = { max: 600, timeWindowMs: 60_000 } as const\n\n/** What main.ts prints, and stops, when the setting is anything but a whole number of at least one. */\nexport const RATE_LIMIT_REFUSAL = 'FORMANCY_DATA_RATE_LIMIT is a whole number of requests a minute per client address, at least 1.'\n\nexport type RateLimitSetting = { ok: true; rateLimit: { max: number; timeWindowMs: number } } | { ok: false; problem: string }\n\n/**\n * `FORMANCY_DATA_RATE_LIMIT` as written: unset is the default; otherwise\n * decimal digits only, no sign, no fraction, no exponent, no space, and a\n * value a JavaScript number holds exactly. The window is always a minute, so\n * the number an operator writes is the number of requests.\n */\nexport function rateLimitSetting(text: string | undefined): RateLimitSetting {\n if (text === undefined) return { ok: true, rateLimit: { ...DEFAULT_RATE_LIMIT } }\n const max = /^[0-9]+$/.test(text) ? Number(text) : Number.NaN\n if (!Number.isSafeInteger(max) || max < 1) return { ok: false, problem: RATE_LIMIT_REFUSAL }\n return { ok: true, rateLimit: { max, timeWindowMs: DEFAULT_RATE_LIMIT.timeWindowMs } }\n}\n","import { grantsOnKey, reassignedKeys } from '@formancy/data-core'\nimport type { FieldAnchor, FormBindings, FormPolicy, ReassignedKey } from '@formancy/data-core'\n\n/*\n * What a publish must say about keys that now stand for something else\n * (0039). A grant names a key, and a key renumbers when a colliding column\n * comes or goes (0030), so a grant written for what a key stood for applies\n * afterwards to whatever it stands for now. The policy shows a removal; only\n * the publish can say a key was kept.\n */\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction anchorOf(value: unknown): FieldAnchor | undefined {\n if (!isRecord(value)) return undefined\n if (value['kind'] === 'column' && typeof value['column'] === 'string') return { kind: 'column', column: value['column'] }\n if (value['kind'] === 'lookup' && typeof value['foreignKey'] === 'string') return { kind: 'lookup', foreignKey: value['foreignKey'] }\n return undefined\n}\n\n/**\n * A publish's `keysConfirmed`: absent is none, and anything but a list of\n * `{ field, was, now }`, each anchor a column or a lookup's foreign key, is\n * `undefined`. Each entry is rebuilt from what it names, so nothing else a\n * client sent is carried.\n */\nexport function readKeysConfirmed(value: unknown): ReassignedKey[] | undefined {\n if (value === undefined) return []\n if (!Array.isArray(value)) return undefined\n const keys: ReassignedKey[] = []\n for (const entry of value) {\n if (!isRecord(entry) || typeof entry['field'] !== 'string') return undefined\n const was = anchorOf(entry['was'])\n const now = anchorOf(entry['now'])\n if (was === undefined || now === undefined) return undefined\n keys.push({ field: entry['field'], was, now })\n }\n return keys\n}\n\nconst sameAnchor = (left: FieldAnchor, right: FieldAnchor): boolean =>\n left.kind === 'column' ? right.kind === 'column' && left.column === right.column : right.kind === 'lookup' && left.foreignKey === right.foreignKey\n\n/**\n * The keys `replaced` bound to another column or lookup than `published`\n * does, that `published`'s policy grants on -- a role on the key's field or\n * a through that names it (0043), `grantsOnKey`, the function the studio\n * asks with -- and that `confirmed` does not name exactly as reassigned: the\n * same key, the column or lookup it stood for, and the one it stands for\n * now. A confirmation matched by key\n * alone would carry a decision to a column nobody was shown -- a studio\n * draft generated again after its keys were decided can renumber one again.\n * One for a key that was not reassigned grants nothing, and is ignored.\n *\n * An anchor is a column's or a foreign key's name, not its table's: a\n * publish that moves the form to another root or connection is compared by\n * name, and its grants follow their keys unasked (0039's costs).\n */\nexport function unconfirmedKeys(replaced: FormBindings, published: { bindings: FormBindings; policy: FormPolicy }, confirmed: readonly ReassignedKey[]): ReassignedKey[] {\n return reassignedKeys(replaced, published.bindings).filter(\n (key) => grantsOnKey(published.policy, key.field) && !confirmed.some((entry) => entry.field === key.field && sameAnchor(entry.was, key.was) && sameAnchor(entry.now, key.now)),\n )\n}\n","import { describeReassigned, diffSnapshots } from '@formancy/data-core'\nimport type { DriftChange, DriftReport, FormBindings, FormPolicy, MetadataSnapshot, ReassignedKey } from '@formancy/data-core'\nimport type { FastifyBaseLogger } from 'fastify'\nimport { generatedProblems } from './bundle-format2.js'\nimport { validateBundle } from './bundle.js'\nimport type { BundleV2, PublishedBundle } from './bundle.js'\nimport { UnparsableVersionError } from './config-store.js'\nimport type { ConfigurationStore } from './config-store.js'\nimport { loadVersion } from './published.js'\nimport { unconfirmedKeys } from './reassigned.js'\n\n/*\n * What a version must be to be written, and what the database must be to\n * serve it: the checks the publish route, the restore route and the import\n * route (0062) share, so that \"a version this server would write\" and \"a\n * version this database can serve\" are each one decision. An import is a\n * publish whose bundle came from another store, gated as a restore is.\n */\n\n/** A refusal for the route to send: a status, a stable code, a sentence, and what else the caller can act on. */\nexport interface Refusal {\n status: number\n code: string\n message: string\n extra?: Record<string, unknown>\n}\n\nexport type Storable =\n | { ok: true; bundle: BundleV2 }\n /** `connection` once the bundle validated, so the trail can name it whatever refused it after. */\n | { ok: false; refusal: Refusal; connection?: string }\n\nconst INVALID = 'The bundle cannot be published.'\n\n/**\n * Whether `document` may be written as a version of form `id`, in the order\n * the publish route has always asked: it validates (0019); it is format 2,\n * which keeps its generation request (0030); its form is `id`; its\n * connection is one `allowlisted` knows; and this server's generator writes\n * its base and bindings from its snapshot and request (0030) -- asked here,\n * at the write, and never on read, so a later generator cannot make a stored\n * version corrupt.\n *\n * `unknown` is the sentence for a connection the allowlist does not know:\n * the import says why a name has to match across deployments.\n */\nexport function storable(document: unknown, id: string, allowlisted: (connection: string) => boolean, unknown: (connection: string) => string = (connection) => `No connection is called ${connection}.`): Storable {\n const checked = validateBundle(document)\n if (!checked.ok) return { ok: false, refusal: { status: 422, code: 'invalid-bundle', message: INVALID, extra: { problems: checked.problems } } }\n const bundle = checked.bundle\n const refused = (message: string, extra?: Record<string, unknown>): Storable => ({ ok: false, refusal: { status: 422, code: 'invalid-bundle', message, ...(extra === undefined ? {} : { extra }) }, connection: bundle.connection })\n if (bundle.format === 1) return refused(INVALID, { problems: ['publish format 2: since 0030 a version keeps its generation request, generated base and presentation'] })\n if (bundle.form.id !== id) return refused(`The form's id is ${bundle.form.id}, not ${id}.`)\n if (!allowlisted(bundle.connection)) return refused(unknown(bundle.connection))\n const generated = generatedProblems(bundle)\n if (generated.length > 0) return refused(INVALID, { problems: generated })\n return { ok: true, bundle }\n}\n\n/**\n * The keys a write over `expectedBase` grants on that the version it\n * replaces bound to something else, and that it does not confirm (0039).\n * Nothing to compare for a first version, or for a base that is not there\n * -- below 1, or past the newest -- which the store's compare-and-swap\n * refuses; nor for a version that does not validate, or whose file does\n * not parse, which is not served (0019), so none of its grants is in\n * effect -- and publishing over it is how it is replaced. Any other\n * failure to read the base throws: a check that cannot answer refuses.\n */\nexport async function unconfirmedAgainst(store: ConfigurationStore, id: string, expectedBase: number | null, bundle: { bindings: FormBindings; policy: FormPolicy }, confirmed: readonly ReassignedKey[], log: FastifyBaseLogger): Promise<ReassignedKey[]> {\n if (expectedBase === null || expectedBase < 1) return []\n let replaced: Awaited<ReturnType<typeof loadVersion>>\n try {\n replaced = await loadVersion(store, id, expectedBase, log)\n } catch (error) {\n if (!(error instanceof UnparsableVersionError)) throw error\n log.error({ form: id, version: expectedBase }, 'a published version does not parse, and is not compared')\n return []\n }\n return replaced.ok ? unconfirmedKeys(replaced.bundle.bindings, bundle, confirmed) : []\n}\n\n/** The conflict a compare-and-swap answers with: somebody wrote `current` first. */\nexport function conflictOver(current: number | null): Refusal {\n return { status: 409, code: 'conflict', message: 'Somebody published first. Rebase on the current version and try again.', extra: { current } }\n}\n\n/**\n * The refusal for a write over `expectedBase` whose keys are not all\n * confirmed (0039), or `undefined` when there is none. Compared with a\n * version somebody has replaced, the keys may say something else, so the\n * conflict is the answer then.\n */\nexport async function keysRefusal(store: ConfigurationStore, id: string, expectedBase: number | null, bundle: { bindings: FormBindings; policy: FormPolicy }, confirmed: readonly ReassignedKey[], log: FastifyBaseLogger): Promise<Refusal | undefined> {\n const keys = await unconfirmedAgainst(store, id, expectedBase, bundle, confirmed, log)\n if (keys.length === 0) return undefined\n const current = await store.latest(id)\n if (current !== expectedBase) return conflictOver(current)\n return {\n status: 422,\n code: 'keys-reassigned',\n message: `Version ${String(expectedBase)} bound ${keys.map((key) => key.field).join(', ')} to other columns or lookups: confirm the grants on each for what it stands for now, or remove them. A grant is a role on the key's field, or a through that scopes this form's rows by it.`,\n extra: { keys, problems: keys.map(describeReassigned) },\n }\n}\n\nexport type Servable =\n | { ok: true; drift: DriftReport }\n /** The database is another engine's than the one the bundle's bindings were made for. */\n | { ok: false; code: 'other-engine'; bound: MetadataSnapshot['kind']; database: MetadataSnapshot['kind'] }\n /** Drift blocks: only the blocking changes, which are the reason. */\n | { ok: false; code: 'incompatible'; changes: DriftChange[] }\n\n/**\n * Whether the database as `current` describes it can serve `bundle`, by\n * drift's rules (0010): a blocking change refuses, and a change to review or\n * note is reported and allowed. One gate for a restore and an import, which\n * both put a version already reviewed in front of a database it was not\n * generated from.\n *\n * Another engine is asked first, as its own answer: drift does not compare\n * across engines, and a connection pointed at another engine is the\n * administrator's to put right, not the server's failure. `diffSnapshots`\n * throws for nothing else a validated bundle can hold; anything it does\n * throw is the caller's 500.\n */\nexport function servable(bundle: PublishedBundle, current: MetadataSnapshot): Servable {\n if (bundle.snapshot.kind !== current.kind) return { ok: false, code: 'other-engine', bound: bundle.snapshot.kind, database: current.kind }\n const drift = diffSnapshots(bundle.snapshot, current, bundle.bindings, bundle.policy)\n if (drift.blocking) return { ok: false, code: 'incompatible', changes: drift.changes.filter((change) => change.severity === 'blocking') }\n return { ok: true, drift }\n}\n","import type { FastifyRequest } from 'fastify'\nimport { auditedForm } from '../audit.js'\nimport type { AdminAuditEvent, AdminOperation, AuditBase } from '../audit.js'\n\n/**\n * The audit name of every administrator route, keyed `METHOD url` (0033).\n * A route registered on the plane without an entry here stops the server\n * from starting; `admin-audit.test.ts` sends every route the router holds\n * and fails on an entry no route has.\n */\nexport const ADMIN_OPERATIONS: Readonly<Record<string, AdminOperation>> = {\n 'GET /v1/connections': 'connection-list',\n 'POST /v1/connections/:id/test': 'connection-test',\n 'GET /v1/connections/:id/metadata': 'discovery',\n 'POST /v1/form-proposals': 'proposal',\n 'POST /v1/forms/:id/versions': 'publish',\n 'GET /v1/forms/:id/versions': 'version-list',\n 'GET /v1/forms/:id/versions/latest': 'version-latest',\n 'GET /v1/forms/:id/versions/:version': 'version-read',\n 'POST /v1/forms/:id/drift': 'drift',\n 'POST /v1/forms/:id/regenerations': 'regeneration',\n 'POST /v1/forms/:id/restorations': 'restore',\n 'POST /v1/forms/:id/imports': 'import',\n}\n\n/**\n * What a route has established for the event beyond its path, set at the\n * point it is established: a name from a body once the body is read, a\n * version once it is loaded or written. Never what was sent. A connection\n * set here is still recorded only if the allowlist knows it (`describeAdmin`).\n */\nexport interface AdminTrail {\n connection?: string\n form?: string\n formVersion?: number\n expectedBase?: number | null\n restoredFrom?: number\n}\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n adminAudit?: AdminTrail\n }\n}\n\n/** The request's trail, begun on first use: a refusal before any route ran has none, and needs none. */\nexport function adminTrail(request: FastifyRequest): AdminTrail {\n request.adminAudit ??= {}\n return request.adminAudit\n}\n\n/**\n * The administrator's event: the shared base, the trail, and the path's own\n * names, each recorded only once it is known to be a name.\n *\n * A connection, from the path, a body or a stored bundle, is recorded only\n * when `known` -- the allowlist -- has it: a connection id is a name, never an\n * address (0019), and a connection string pasted where a name goes, with a\n * token or without, would otherwise put a host and a password in the trail.\n * Checked here, where every event is made, so no route can forget it.\n */\nexport function describeAdmin(known: (connection: string) => boolean) {\n return (request: FastifyRequest, base: AuditBase<AdminOperation>): AdminAuditEvent => {\n const trail = request.adminAudit ?? {}\n const url = request.routeOptions.url ?? ''\n const id = (request.params as { id?: unknown }).id\n const path = typeof id === 'string' ? id : undefined\n const connection = trail.connection ?? (url.startsWith('/v1/connections/:id') ? path : undefined)\n return {\n at: base.at,\n plane: 'admin',\n actor: base.actor,\n operation: base.operation,\n connection: connection !== undefined && known(connection) ? connection : null,\n form: auditedForm(trail.form ?? (url.startsWith('/v1/forms/:id') ? path : undefined)),\n formVersion: trail.formVersion ?? null,\n expectedBase: trail.expectedBase ?? null,\n restoredFrom: trail.restoredFrom ?? null,\n status: base.status,\n outcome: base.outcome,\n }\n }\n}\n","import { buildLookupConfig, diffSnapshots, generateForm, reassignedKeys, rebasePresentation } from '@formancy/data-core'\nimport type {\n DriftReport,\n FormBindings,\n FormPolicy,\n GeneratedForm,\n GenerationNote,\n GenerationRequest,\n LookupChoice,\n MetadataSnapshot,\n PresentationConflict,\n PresentationOverrides,\n ReassignedKey,\n} from '@formancy/data-core'\nimport type { FormSchema } from '@formancy/spec'\nimport { validateSchema } from '@formancy/spec/validate'\nimport type { FastifyInstance, FastifyReply } from 'fastify'\nimport { policyProblems } from '../bundle.js'\nimport type { BundleV2 } from '../bundle.js'\nimport type { ConfigurationStore } from '../config-store.js'\nimport { FORM_ID, loadPublished, loadVersion } from '../published.js'\nimport { servable } from '../publishing.js'\nimport type { Servable } from '../publishing.js'\nimport { adminTrail } from './admin-audit.js'\nimport type { Discover, Refuse } from './admin.js'\n\n/**\n * What `POST /v1/forms/:id/regenerations` answers: the newest version\n * regenerated from the database as it is now, with its presentation carried\n * (0030). Nothing is written; this is a draft to review and publish.\n */\nexport interface Regeneration {\n /** The version regenerated from; publish with it as `expectedBase`. */\n version: number\n /** `diffSnapshots(published snapshot, now, published bindings, published policy)`. */\n drift: DriftReport\n /** The request as used: lookups the database no longer allows are left out. */\n generation: GenerationRequest\n base: FormSchema\n bindings: FormBindings\n notes: GenerationNote[]\n snapshot: MetadataSnapshot\n /** The published policy, unchanged. */\n policy: FormPolicy\n /** What that policy does not fit in the new bindings and snapshot. */\n policyProblems: string[]\n /** The published presentation, carried to the new base. */\n presentation: PresentationOverrides\n /** The new base with it applied. */\n form: FormSchema\n conflicts: PresentationConflict[]\n /** Lookups the runtime would refuse now, each with the sentence that refuses it. */\n lookupsDropped: Array<{ foreignKey: string; message: string }>\n /**\n * Keys that now stand for another column or lookup: the grants written for\n * them need confirming. A publish whose policy grants on one is refused,\n * 422 `keys-reassigned`, unless its body lists it, as given here, in\n * `keysConfirmed` (0039).\n */\n keysReassigned: ReassignedKey[]\n}\n\n/** What `POST /v1/forms/:id/restorations` answers: the version written, the one it copies, and the drift that allowed it. */\nexport interface Restoration {\n version: number\n restoredFrom: number\n drift: DriftReport\n}\n\ninterface EvolutionOptions {\n store: ConfigurationStore\n discover: Discover\n refuse: Refuse\n}\n\n/** A version in a path or a body: a whole number from 1, at most sixteen digits, as the store names its files. */\nconst VERSION = /^[1-9][0-9]{0,15}$/\n\nfunction versionOf(text: string): number | undefined {\n if (!VERSION.test(text)) return undefined\n const version = Number(text)\n return Number.isSafeInteger(version) ? version : undefined\n}\n\nfunction isVersion(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 1\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\ntype Regenerated = { ok: true; generated: GeneratedForm; generation: GenerationRequest; dropped: Regeneration['lookupsDropped'] } | { ok: false; message: string }\n\n/**\n * The form generated again from the stored request against the database as\n * it is now, keeping each lookup the runtime would still build.\n *\n * First without lookups: a failure there is not a lookup's — the root, a\n * pinned or version column — and nothing can be generated. Then each lookup\n * alone, put through `buildLookupConfig` exactly as the runtime builds it\n * (0028), which refuses more than the generator does: a display column with\n * no text form, a float key. A lookup either refuses is left out with the\n * sentence that refused it, which names only approved metadata.\n */\nfunction regenerate(bundle: BundleV2, current: MetadataSnapshot): Regenerated {\n const attempt = (lookups: LookupChoice[]) => generateForm(current, { ...bundle.generation, lookups })\n try {\n attempt([])\n } catch (error) {\n return { ok: false, message: (error as Error).message }\n }\n const kept: LookupChoice[] = []\n const dropped: Regeneration['lookupsDropped'] = []\n for (const choice of bundle.generation.lookups) {\n try {\n const trial = attempt([choice])\n const field = trial.bindings.fields.find((binding) => binding.kind === 'lookup' && binding.foreignKey === choice.foreignKey)\n if (field === undefined) throw new Error(`${choice.foreignKey} was not generated as a lookup`)\n buildLookupConfig(trial.bindings, field.field, { snapshot: current })\n kept.push(choice)\n } catch (error) {\n dropped.push({ foreignKey: choice.foreignKey, message: (error as Error).message })\n }\n }\n const generation = { ...bundle.generation, lookups: kept }\n try {\n return { ok: true, generated: generateForm(current, generation), generation, dropped }\n } catch (error) {\n return { ok: false, message: (error as Error).message }\n }\n}\n\n/**\n * Versions, regeneration and restore (0030), on the administrator plane:\n * registered by `adminRoutes` after its role check, so the check covers them.\n */\nexport function evolutionRoutes(app: FastifyInstance, { store, discover, refuse }: EvolutionOptions): void {\n /** Something that cannot happen for a stored, validated bundle: logged for the operator, 500 for the caller. */\n function internal(reply: FastifyReply, id: string, error: unknown) {\n reply.log.error({ form: id, error: (error as Error).message }, 'a regeneration or restore failed where it cannot')\n return refuse(reply, 500, 'internal', \"This could not be completed; the operator's log says why.\")\n }\n\n app.get<{ Params: { id: string } }>('/v1/forms/:id/versions', async (request, reply) => {\n const id = request.params.id\n const versions = FORM_ID.test(id) ? await store.versions(id) : []\n if (versions.length === 0) return refuse(reply, 404, 'unknown-form', `No form is called ${id}.`)\n return { versions }\n })\n\n // `/versions/latest` is a static route, which the router prefers to this one.\n app.get<{ Params: { id: string; version: string } }>('/v1/forms/:id/versions/:version', async (request, reply) => {\n const version = versionOf(request.params.version)\n if (version === undefined) return refuse(reply, 400, 'invalid-request', 'A version is a whole number from 1.')\n const loaded = await loadVersion(store, request.params.id, version, reply.log)\n if (!loaded.ok) return refuse(reply, loaded.status, loaded.code, loaded.message)\n const trail = adminTrail(request)\n trail.formVersion = version\n trail.connection = loaded.bundle.connection\n return { version, bundle: loaded.bundle }\n })\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/regenerations', async (request, reply) => {\n const id = request.params.id\n const loaded = await loadPublished(store, id, reply.log)\n if (!loaded.ok) return refuse(reply, loaded.status, loaded.code, loaded.message)\n const bundle = loaded.bundle\n const trail = adminTrail(request)\n trail.formVersion = loaded.version\n trail.connection = bundle.connection\n if (bundle.format === 1) {\n return refuse(reply, 409, 'published-before-0030', `Version ${String(loaded.version)} was published before 0030 and kept no generation request: propose the form again.`)\n }\n const current = await discover(bundle.connection, reply)\n if (current === undefined) return reply\n\n let drift: DriftReport\n try {\n drift = diffSnapshots(bundle.snapshot, current, bundle.bindings, bundle.policy)\n } catch (error) {\n return internal(reply, id, error)\n }\n const outcome = regenerate(bundle, current)\n if (!outcome.ok) return refuse(reply, 422, 'cannot-generate', outcome.message, { drift })\n const { generated, generation, dropped } = outcome\n\n let carried\n try {\n carried = rebasePresentation({ base: bundle.base, presentation: bundle.presentation, bindings: bundle.bindings }, { base: generated.form, bindings: generated.bindings })\n } catch (error) {\n return internal(reply, id, error)\n }\n const valid = validateSchema(carried.form)\n if (!valid.valid) return internal(reply, id, new Error(`the carried form is not one formancy accepts: ${valid.errors.map((entry) => entry.message).join('; ')}`))\n\n const regeneration: Regeneration = {\n version: loaded.version,\n drift,\n generation,\n base: generated.form,\n bindings: generated.bindings,\n notes: generated.notes,\n snapshot: current,\n policy: bundle.policy,\n policyProblems: policyProblems(current, generated.bindings, bundle.policy),\n presentation: carried.presentation,\n form: carried.form,\n conflicts: carried.conflicts,\n lookupsDropped: dropped,\n keysReassigned: reassignedKeys(bundle.bindings, generated.bindings),\n }\n return regeneration\n })\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/restorations', async (request, reply) => {\n const id = request.params.id\n const body = request.body\n if (!isRecord(body) || !isVersion(body['version']) || !isVersion(body['expectedBase'])) {\n return refuse(reply, 400, 'invalid-request', 'Expected { version: the version to restore, expectedBase: the newest version you saw }.')\n }\n const version = body['version']\n const trail = adminTrail(request)\n trail.restoredFrom = version\n trail.expectedBase = body['expectedBase']\n const loaded = await loadVersion(store, id, version, reply.log)\n if (!loaded.ok) return refuse(reply, loaded.status, loaded.code, loaded.message)\n trail.connection = loaded.bundle.connection\n const current = await discover(loaded.bundle.connection, reply)\n if (current === undefined) return reply\n\n let served: Servable\n try {\n served = servable(loaded.bundle, current)\n } catch (error) {\n return internal(reply, id, error)\n }\n if (!served.ok && served.code === 'other-engine') {\n return refuse(reply, 422, 'other-engine', `Version ${String(version)} cannot be restored: connection ${loaded.bundle.connection} is a ${served.database} database now, and the version is bound to ${served.bound}. A form's bindings belong to one engine.`)\n }\n if (!served.ok) {\n return refuse(reply, 409, 'incompatible', `Version ${String(version)} cannot be restored: the database has changed in a way it cannot serve.`, { changes: served.changes })\n }\n const drift = served.drift\n // The document as it was read and validated, written as the store writes every version: a copy, never a re-generation.\n // The same bytes only for a version this store wrote; one reformatted on the volume comes back in the store's spelling.\n const outcome = await store.publish(id, body['expectedBase'], loaded.bundle)\n if (!outcome.ok) return refuse(reply, 409, 'conflict', 'Somebody published first. Look at the current version and try again.', { current: outcome.current })\n trail.formVersion = outcome.version\n const restoration: Restoration = { version: outcome.version, restoredFrom: version, drift }\n return reply.code(201).send(restoration)\n })\n}\n","import type { DriftReport } from '@formancy/data-core'\nimport type { FastifyInstance, FastifyReply } from 'fastify'\nimport type { ConfigurationStore } from '../config-store.js'\nimport { FORM_ID } from '../published.js'\nimport { conflictOver, keysRefusal, servable, storable } from '../publishing.js'\nimport type { Refusal, Servable } from '../publishing.js'\nimport { readKeysConfirmed } from '../reassigned.js'\nimport { adminTrail } from './admin-audit.js'\nimport type { Discover, Refuse } from './admin.js'\n\n/**\n * What `POST /v1/forms/:id/imports` answers (0062): the version written, and\n * the drift between the snapshot the bundle carries and this deployment's\n * database that allowed it -- changes to review or note, never one that\n * blocks.\n */\nexport interface Importation {\n version: number\n drift: DriftReport\n}\n\ninterface PromotionOptions {\n store: ConfigurationStore\n /** Whether this deployment's allowlist has a connection by that name. */\n allowlisted: (connection: string) => boolean\n discover: Discover\n refuse: Refuse\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * A published version moved between deployments (0062): the version file\n * another deployment's store wrote -- read there as `GET\n * /v1/forms/:id/versions/:version`, the export -- written here as a new\n * version, after the checks a publish makes and the gate a restore passes.\n * Registered by `adminRoutes` after its role check, so the check covers it.\n *\n * The bundle is written as it came, snapshot and all: what was reviewed\n * there is what runs here, and the runtime's root check (0041) and this\n * deployment's drift compare against that snapshot until a regeneration\n * replaces it. Nothing is renamed: the connection and the form keep their\n * names, because a lookup's source name is made from the connection's, so\n * a renamed connection is another generated base.\n */\nexport function promotionRoutes(app: FastifyInstance, { store, allowlisted, discover, refuse }: PromotionOptions): void {\n const send = (reply: FastifyReply, refusal: Refusal) => refuse(reply, refusal.status, refusal.code, refusal.message, refusal.extra)\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/imports', async (request, reply) => {\n const id = request.params.id\n const body = request.body\n // 1. The body's shape, before anything is checked or a database asked.\n if (!FORM_ID.test(id)) return refuse(reply, 400, 'invalid-request', 'A form id is lower-case letters, digits, dot, hyphen or underscore.')\n const shape = 'Expected { expectedBase: the newest version here, or null for the first, bundle: the version file, keysConfirmed? }.'\n if (!isRecord(body) || !('expectedBase' in body) || !(body['expectedBase'] === null || Number.isSafeInteger(body['expectedBase'])) || !isRecord(body['bundle'])) {\n return refuse(reply, 400, 'invalid-request', shape)\n }\n const expectedBase = body['expectedBase'] as number | null\n const trail = adminTrail(request)\n trail.expectedBase = expectedBase\n const confirmed = readKeysConfirmed(body['keysConfirmed'])\n if (confirmed === undefined) {\n return refuse(reply, 400, 'invalid-request', 'Expected keysConfirmed, when sent, to list { field, was, now } as an import refused 422 keys-reassigned lists them in keys.')\n }\n\n // 2 to 4. What a publish checks: it validates, in format 2, for this form; its connection is\n // a name this allowlist has; and this server's generator writes its base and bindings.\n const checked = storable(body['bundle'], id, allowlisted, (name) => `No connection is called ${name} in this deployment: an import keeps the connection's name, which each deployment's allowlist resolves to its own database.`)\n if (!checked.ok) {\n if (checked.connection !== undefined) trail.connection = checked.connection\n return send(reply, checked.refusal)\n }\n const bundle = checked.bundle\n trail.connection = bundle.connection\n\n // 5. This deployment's database as it is now.\n const current = await discover(bundle.connection, reply)\n if (current === undefined) return reply\n\n // 6. Restore's gate: refused for another engine, or a change that blocks.\n let served: Servable\n try {\n served = servable(bundle, current)\n } catch (error) {\n reply.log.error({ form: id, error: (error as Error).message }, 'an import failed where it cannot')\n return refuse(reply, 500, 'internal', \"This could not be completed; the operator's log says why.\")\n }\n if (!served.ok && served.code === 'other-engine') {\n return refuse(reply, 422, 'other-engine', `This version cannot be imported: connection ${bundle.connection} is a ${served.database} database here, and the version is bound to ${served.bound}. A form's bindings belong to one engine.`)\n }\n if (!served.ok) {\n return refuse(reply, 409, 'incompatible', \"This version cannot be imported: this deployment's database differs from the one it was published from in a way it cannot serve.\", { changes: served.changes })\n }\n\n // 7. 0039 against this deployment's base; a stale base is the conflict first.\n const keys = await keysRefusal(store, id, expectedBase, bundle, confirmed, reply.log)\n if (keys !== undefined) return send(reply, keys)\n\n // 8. Compare-and-swap, as every version is written.\n const outcome = await store.publish(id, expectedBase, bundle)\n if (!outcome.ok) return send(reply, conflictOver(outcome.current))\n trail.formVersion = outcome.version\n const importation: Importation = { version: outcome.version, drift: served.drift }\n return reply.code(201).send(importation)\n })\n}\n","import { diffSnapshots, generateForm } from '@formancy/data-core'\nimport type { MetadataSnapshot } from '@formancy/data-core'\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\nimport { auditRequests } from '../audit.js'\nimport type { AuditSink } from '../audit.js'\nimport type { PublishedBundle } from '../bundle.js'\nimport type { ConfigurationStore } from '../config-store.js'\nimport type { ConnectionRegistry, OpenConnection } from '../connections.js'\nimport { readGeneration } from '../generation.js'\nimport type { HostIdentity } from '../identity.js'\nimport { FORM_ID, loadPublished } from '../published.js'\nimport { conflictOver, keysRefusal, storable } from '../publishing.js'\nimport type { Refusal } from '../publishing.js'\nimport { readKeysConfirmed } from '../reassigned.js'\nimport { ADMIN_OPERATIONS, adminTrail, describeAdmin } from './admin-audit.js'\nimport { evolutionRoutes } from './admin-evolution.js'\nimport { promotionRoutes } from './admin-promotion.js'\n\nexport interface AdminOptions {\n registry: ConnectionRegistry\n store: ConfigurationStore\n /** Roles in the host's token that may connect, discover, propose and publish. */\n adminRoles: readonly string[]\n authenticate: (request: FastifyRequest) => Promise<HostIdentity | undefined>\n /**\n * The operational trail (0023, 0033): one event per request, refusals\n * included. Required, unlike the runtime's: a plane that publishes and\n * restores forms does not run without saying who did.\n */\n audit: { sink: AuditSink; now?: () => string }\n}\n\n/** Sends a refusal: a stable code, a sentence, and whatever else the caller can act on. */\nexport type Refuse = (reply: FastifyReply, status: number, code: string, message: string, extra?: Record<string, unknown>) => FastifyReply\n\n/** Discovers a connection's database as it is now, or sends the refusal and gives `undefined`. */\nexport type Discover = (connection: string, reply: FastifyReply) => Promise<MetadataSnapshot | undefined>\n\nfunction refuse(reply: FastifyReply, status: number, code: string, message: string, extra: Record<string, unknown> = {}) {\n return reply.code(status).send({ code, message, ...extra })\n}\n\n/** Sends a refusal `publishing.ts` decided. */\nfunction send(reply: FastifyReply, refusal: Refusal): FastifyReply {\n return refuse(reply, refusal.status, refusal.code, refusal.message, refusal.extra)\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * The administrator's plane: connections, discovery, proposals, publication,\n * drift, and since 0030 versions, regeneration and restore (plan section 13).\n * Every route requires a host identity holding one of `adminRoles` —\n * publishing a form and writing a business record are\n * different permissions, and nothing here touches a record.\n *\n * A database that cannot be reached is 503 with no detail: a driver's message\n * names hosts and ports, which is the operator's business and not the caller's.\n *\n * Every request is audited, one event each (0033), by hooks installed before\n * any route, so a route added here without an audit name stops the server.\n */\nexport async function adminRoutes(app: FastifyInstance, options: AdminOptions): Promise<void> {\n const { registry, store } = options\n const admins = new Set(options.adminRoles)\n\n // The type requires it; this is for a caller the type does not reach.\n if (typeof (options.audit as Partial<AdminOptions['audit']> | undefined)?.sink !== 'function') {\n throw new Error(\"The administrator's plane needs an audit sink: audit: { sink } (0033).\")\n }\n auditRequests(app, { names: ADMIN_OPERATIONS, sink: options.audit.sink, now: options.audit.now, describe: describeAdmin((id) => registry.scope(id) !== undefined) })\n\n app.addHook('preHandler', async (request, reply) => {\n const identity = await options.authenticate(request)\n if (identity === undefined) {\n return reply.code(401).header('www-authenticate', 'Bearer').send({ code: 'unauthenticated', message: 'A valid host token is required.' })\n }\n // Before the role check, so a refused attempt names who made it.\n request.identity = identity\n if (!identity.actor.roles.some((role) => admins.has(role))) {\n return refuse(reply, 403, 'forbidden', 'This action needs an administrator role.')\n }\n return undefined\n })\n\n /** The connection's ports, or a reply already sent. */\n async function connection(id: string, reply: FastifyReply): Promise<OpenConnection | undefined> {\n let open: OpenConnection | undefined\n try {\n open = await registry.open(id)\n } catch (error) {\n reply.log.warn({ connection: id, error: (error as Error).message }, 'connection could not be opened')\n await refuse(reply, 503, 'unavailable', `Connection ${id} cannot be reached.`)\n return undefined\n }\n if (open === undefined) await refuse(reply, 404, 'unknown-connection', `No connection is called ${id}.`)\n return open\n }\n\n async function discover(id: string, reply: FastifyReply): Promise<MetadataSnapshot | undefined> {\n const scope = registry.scope(id)\n const open = await connection(id, reply)\n if (open === undefined || scope === undefined) return undefined\n try {\n return await open.adapter.discover(scope)\n } catch (error) {\n reply.log.warn({ connection: id, error: (error as Error).message }, 'discovery failed')\n await refuse(reply, 503, 'unavailable', `Connection ${id} could not be read.`)\n return undefined\n }\n }\n\n app.get('/v1/connections', async () => ({ connections: registry.ids() }))\n\n app.post<{ Params: { id: string } }>('/v1/connections/:id/test', async (request, reply) => {\n const open = await connection(request.params.id, reply)\n if (open === undefined) return reply\n try {\n return await open.adapter.ping()\n } catch {\n return refuse(reply, 503, 'unavailable', `Connection ${request.params.id} did not answer.`)\n }\n })\n\n app.get<{ Params: { id: string } }>('/v1/connections/:id/metadata', async (request, reply) => {\n const snapshot = await discover(request.params.id, reply)\n return snapshot ?? reply\n })\n\n app.post('/v1/form-proposals', async (request, reply) => {\n const body = request.body\n if (!isRecord(body)) return refuse(reply, 400, 'invalid-request', 'Expected a JSON object.')\n const read = readGeneration(body)\n if (!read.ok) {\n return refuse(reply, 400, 'invalid-request', 'Expected connection, root { schema, name }, a lower-case formId, title, and optional lookups, versionColumn and pinned columns.')\n }\n const trail = adminTrail(request)\n trail.form = read.generation.formId\n trail.connection = read.generation.connection\n const snapshot = await discover(read.generation.connection, reply)\n if (snapshot === undefined) return reply\n try {\n // The request as it was read is what a format-2 bundle keeps (0030), so a regeneration asks for the same form.\n return { ...generateForm(snapshot, read.generation), snapshot, generation: read.generation }\n } catch (error) {\n // generateForm's messages name only approved metadata and the request's own words.\n return refuse(reply, 422, 'cannot-generate', (error as Error).message)\n }\n })\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/versions', async (request, reply) => {\n const id = request.params.id\n const body = request.body\n if (!FORM_ID.test(id)) return refuse(reply, 400, 'invalid-request', 'A form id is lower-case letters, digits, dot, hyphen or underscore.')\n if (!isRecord(body) || !('expectedBase' in body) || !(body['expectedBase'] === null || Number.isSafeInteger(body['expectedBase']))) {\n return refuse(reply, 400, 'invalid-request', 'Expected { expectedBase: the version you edited, or null for the first, bundle }.')\n }\n const expectedBase = body['expectedBase'] as number | null\n const trail = adminTrail(request)\n trail.expectedBase = expectedBase\n const confirmed = readKeysConfirmed(body['keysConfirmed'])\n if (confirmed === undefined) {\n return refuse(reply, 400, 'invalid-request', 'Expected keysConfirmed, when sent, to list { field, was, now } as a regeneration reports them in keysReassigned.')\n }\n const checked = storable(body['bundle'], id, (name) => registry.scope(name) !== undefined)\n if (!checked.ok) {\n if (checked.connection !== undefined) trail.connection = checked.connection\n return send(reply, checked.refusal)\n }\n const bundle = checked.bundle\n trail.connection = bundle.connection\n const keys = await keysRefusal(store, id, expectedBase, bundle, confirmed, reply.log)\n if (keys !== undefined) return send(reply, keys)\n const outcome = await store.publish(id, expectedBase, bundle)\n if (!outcome.ok) return send(reply, conflictOver(outcome.current))\n trail.formVersion = outcome.version\n return reply.code(201).send({ version: outcome.version })\n })\n\n /** The newest published bundle, re-validated, or a reply already sent. */\n async function latest(id: string, reply: FastifyReply): Promise<{ version: number; bundle: PublishedBundle } | undefined> {\n const loaded = await loadPublished(store, id, reply.log)\n if (loaded.ok) {\n const trail = adminTrail(reply.request)\n trail.formVersion = loaded.version\n trail.connection = loaded.bundle.connection\n return { version: loaded.version, bundle: loaded.bundle }\n }\n await refuse(reply, loaded.status, loaded.code, loaded.message)\n return undefined\n }\n\n app.get<{ Params: { id: string } }>('/v1/forms/:id/versions/latest', async (request, reply) => {\n return (await latest(request.params.id, reply)) ?? reply\n })\n\n app.post<{ Params: { id: string } }>('/v1/forms/:id/drift', async (request, reply) => {\n const published = await latest(request.params.id, reply)\n if (published === undefined) return reply\n const current = await discover(published.bundle.connection, reply)\n if (current === undefined) return reply\n return { version: published.version, ...diffSnapshots(published.bundle.snapshot, current, published.bundle.bindings, published.bundle.policy) }\n })\n\n // After the role check above, which covers every route registered on this instance.\n evolutionRoutes(app, { store, discover, refuse })\n promotionRoutes(app, { store, allowlisted: (name) => registry.scope(name) !== undefined, discover, refuse })\n}\n","import rateLimit from '@fastify/rate-limit'\nimport { DEFAULT_LOOKUP_DEADLINE_MS } from '@formancy/data-core'\nimport Fastify, { LogController } from 'fastify'\nimport type { FastifyInstance, FastifyRequest } from 'fastify'\nimport { CONFIGURATION_ID_MAX_LENGTH } from './config-store.js'\nimport type { ConfigurationStore } from './config-store.js'\nimport type { ConnectionRegistry } from './connections.js'\nimport { crossOrigin, originsProblem } from './cors.js'\nimport { lookupDeadlineProblem } from './lookup-deadline.js'\nimport type { HostIdentity, IdentityVerifier } from './identity.js'\nimport { PATH_NOT_PLAIN, plainPath } from './paths.js'\nimport { DEFAULT_RATE_LIMIT } from './rate-limit.js'\nimport { adminRoutes } from './routes/admin.js'\nimport type { AdminOptions } from './routes/admin.js'\nimport { runtimeRoutes } from './routes/runtime.js'\nimport type { RuntimeOptions } from './routes/runtime.js'\n\nexport interface DataServerOptions {\n verifyIdentity: IdentityVerifier\n /**\n * Fastify's logger: `true` for standard output, or a stream to write its\n * lines to. Off by default in tests; the composition root turns it on.\n */\n logger?: boolean | { stream: { write: (line: string) => void } }\n /**\n * Requests per client address per window, on every route but `/health`.\n *\n * Default 600 a minute (`DEFAULT_RATE_LIMIT`). Generous on purpose: a lookup\n * is a request per pause in typing. What it is for is the other case:\n * somebody replaying tokens at the verifier as fast as the network allows.\n * The runnable server reads `FORMANCY_DATA_RATE_LIMIT` for it (main.ts).\n * Fastify's `trustProxy` is not offered, so behind a reverse proxy every\n * client shares the proxy's address and one bucket: the setting raises that\n * bucket, it does not split it.\n */\n rateLimit?: { max: number; timeWindowMs: number }\n /**\n * The administrator's plane: connections, discovery, proposals, publication\n * and drift. Absent, none of those routes exist — a server without a store\n * answers 404 for them rather than pretending. Its audit sink is required:\n * every request that reaches one of its routes is an event (0033).\n */\n admin?: { registry: ConnectionRegistry; store: ConfigurationStore; adminRoles: readonly string[]; audit: AdminOptions['audit'] }\n /**\n * The runtime plane: published forms, their records and their lookups, for\n * the host application's people. Absent, none of those routes exist.\n *\n * `origins`: the origins a page may call these routes from, each exactly\n * as a browser sends `Origin` -- `https://app.example`, or plain HTTP on\n * localhost, 127.0.0.1 or [::1] -- checked as `FORMANCY_DATA_CORS_ORIGINS`\n * is, so a wildcard, a pattern or `null` stops the server here (0060).\n * Empty or absent, nothing is offered across origins. The administrator's\n * plane has no such list: it answers no other origin.\n *\n * `lookupDeadlineMs`: how long one lookup statement may run, counted from\n * when the adapter is asked for it -- so a wait for a pooled connection\n * counts, and reading the form and opening its connection do not -- before\n * the server stops it: a search is then answered `503 lookup-timeout`, and\n * a resolve, or a membership check inside a write, `503 unavailable`\n * (0061). A whole number from 1 to 2147483647, or the server is not built.\n * Default `DEFAULT_LOOKUP_DEADLINE_MS`, derived from the published\n * performance figures; the runnable server reads\n * `FORMANCY_DATA_LOOKUP_DEADLINE_MS` for it (main.ts).\n */\n runtime?: { registry: ConnectionRegistry; store: ConfigurationStore; audit?: NonNullable<RuntimeOptions['audit']>; origins?: readonly string[]; lookupDeadlineMs?: number }\n}\n\n/**\n * The largest request body accepted. A record is a form's worth of fields; a\n * megabyte is generous for that and small enough that a request cannot be used\n * to make the server buffer something large.\n */\nconst BODY_LIMIT = 1024 * 1024\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n identity?: HostIdentity\n }\n}\n\n/**\n * A request as the log names it: its method and the route that answered it,\n * never the path, the query or the Host header it was sent with. Those are\n * whatever a caller typed, token or none -- a connection string pasted where\n * a connection's name goes, a password in a query -- and the log is the\n * stream the audit trail is written to, read by whoever runs the collector\n * (0033). Fastify's default writes the URL and the Host as sent.\n */\nfunction requestForLog(request: FastifyRequest) {\n const port = request.socket.remotePort\n return { method: request.method, route: request.routeOptions.url ?? null, remoteAddress: request.ip, ...(port === undefined ? {} : { remotePort: port }) }\n}\n\n/** Fastify's request lines, but for its 404 line, which names the path it was sent: here, a 404 is named by its method. */\nclass RouteOnlyLog extends LogController {\n override routeNotFound(request: FastifyRequest): void {\n if (this.isLogDisabled(request)) return\n request.log.info({ req: request }, 'no route matches')\n }\n}\n\nfunction bearer(request: FastifyRequest): string | undefined {\n const header = request.headers.authorization\n if (header === undefined) return undefined\n const match = /^Bearer ([A-Za-z0-9._~+/=-]+)$/.exec(header)\n return match?.[1]\n}\n\n/**\n * The HTTP surface, as far as it exists.\n *\n * The administrator's plane is registered when the server is given a store,\n * a connection registry and the roles that administer. The record and lookup\n * routes arrive with the operations they call: a route that answered before\n * the adapters behind it could would be a documented-but-inert endpoint, which\n * is the failure this repository refuses.\n *\n * `GET /v1/whoami` is for the person wiring a host application in: it returns\n * exactly the identity the server derived from the host's token, so a wrong\n * issuer, audience or claim mapping is visible before any form depends on it.\n */\nexport async function createDataServer(options: DataServerOptions): Promise<FastifyInstance> {\n // Checked before anything is built, as main.ts checks the setting: a list\n // nobody could have meant does not start a server that grants it. A copy\n // is checked and granted, since building awaits before the grants are\n // made, and an embedder's array changed meanwhile would be granted\n // unchecked.\n const given = options.runtime?.origins ?? []\n const origins = Array.isArray(given) ? Object.freeze([...given]) : given\n const refused = originsProblem(origins)\n if (refused !== undefined) throw new Error(refused)\n // The deadline by the variable's rule (lookup-deadline.ts): 0 or a value\n // past what a timer holds refuses every lookup at once, and a fraction or\n // NaN makes Node's timer throw on every lookup and every write with a\n // lookup field, so either stops the server here instead.\n const lookupDeadlineMs = options.runtime?.lookupDeadlineMs ?? DEFAULT_LOOKUP_DEADLINE_MS\n const deadlineRefused = lookupDeadlineProblem('runtime.lookupDeadlineMs', lookupDeadlineMs)\n if (deadlineRefused !== undefined) throw new Error(deadlineRefused)\n\n const logger = options.logger ?? false\n const app = Fastify({\n logger: logger === false ? false : { serializers: { req: requestForLog }, ...(logger === true ? {} : { stream: logger.stream }) },\n logController: new RouteOnlyLog(),\n bodyLimit: BODY_LIMIT,\n // The longest path parameter is a form id, and the router's default of\n // 100 refused the store's longer ones with 414 before any route ran: a\n // form could be proposed and never published. Lookup sources and\n // connection ids are shorter.\n routerOptions: { maxParamLength: CONFIGURATION_ID_MAX_LENGTH },\n })\n\n // Nothing this server answers may be kept by a cache (0037). A form id may\n // end in `.js` or `.css`, which a CDN caches by extension, and a cache that\n // does not treat Authorization as private would hand one person's form or\n // record to the next. Set on every answer that runs the hooks, refusals\n // and errors included, and over anything a route set: a route that wanted\n // to be cached would be a decision, not a header. What is written before\n // any hook -- the router's 400 for an escape that does not decode and its\n // 414, the 503 while closing, what Node's parser refuses -- carries none,\n // and holds no form or record.\n app.addHook('onSend', async (_request, reply, payload) => {\n reply.header('cache-control', 'no-store')\n return payload\n })\n\n // Global, unlike formancy's server, where the management plane is\n // authenticated by its own sessions: every route here except /health is\n // reachable by anybody holding a browser and does work before refusing.\n const limit = options.rateLimit ?? DEFAULT_RATE_LIMIT\n await app.register(rateLimit, { global: true, max: limit.max, timeWindow: limit.timeWindowMs })\n\n // A path a proxy could read differently from the router is answered here,\n // before any route's work (paths.ts, 0037): before a plane's hooks, before\n // the token is read, before the audit's own onRequest. A route that\n // matched still writes its plane's one event, by this status, since the\n // plane's onSend and onResponse run for any answer its route gives (0033).\n // The limiter puts its hook on each route, and a route's own hooks run\n // after every hook added here, so it would never see this refusal: it is\n // asked here instead, and a request it has counted is not counted again.\n // `request.url` is the target as sent: Fastify reads it from the raw\n // request, and nothing here rewrites URLs.\n const counted = app.rateLimit()\n app.addHook('onRequest', async (request, reply) => {\n if (plainPath(request.url)) return undefined\n await counted.call(app, request, reply)\n return reply.code(400).send(PATH_NOT_PLAIN)\n })\n\n // Across origins (cors.ts, 0060): before every route, so its hooks cover\n // /health, /v1/whoami and both planes. With no origin listed it adds no\n // route and no header, and still refuses a token-bearing request a\n // browser marks cross-site, which only a grant cached under an earlier\n // list, or added by some other component, would let a browser send.\n crossOrigin(app, { origins })\n\n const authenticate = async (request: FastifyRequest): Promise<HostIdentity | undefined> => {\n const token = bearer(request)\n if (token === undefined) return undefined\n const outcome = await options.verifyIdentity(token)\n if (!outcome.ok) {\n // Logged without the token: the reason is jose's code or a sentence.\n request.log.info({ reason: outcome.reason }, 'host identity refused')\n return undefined\n }\n return outcome.identity\n }\n\n // An orchestrator polls this every few seconds; limiting it would mark a\n // healthy server unhealthy exactly when it is busiest.\n app.get('/health', { config: { rateLimit: false } }, async () => ({ status: 'ok' }))\n\n app.get('/v1/whoami', async (request, reply) => {\n const identity = await authenticate(request)\n if (identity === undefined) {\n return reply.code(401).header('www-authenticate', 'Bearer').send({ code: 'unauthenticated', message: 'A valid host token is required.' })\n }\n return identity\n })\n\n if (options.admin !== undefined) {\n await app.register(adminRoutes, { ...options.admin, authenticate })\n }\n\n if (options.runtime !== undefined) {\n const { origins: _offered, lookupDeadlineMs: _checked, ...runtime } = options.runtime\n await app.register(runtimeRoutes, { ...runtime, authenticate, lookupDeadlineMs })\n }\n\n return app\n}\n","import { isDatabaseKind } from '@formancy/data-core'\nimport type { DatabaseAdapter, DatabaseKind, DiscoveryScope, LookupAdapter, RecordAdapter } from '@formancy/data-core'\nimport { resolveSecret } from './secrets.js'\nimport type { SecretSource } from './secrets.js'\n\n/**\n * One database the deployment allows forms to bind to, as the operator wrote\n * it. The password is a secret reference, never a password (0014): this file is\n * reviewable configuration, and a secret in it would be a secret in review.\n */\nexport interface ConnectionConfig {\n /** The name forms use. Lower case, as configuration ids are (0013). */\n id: string\n kind: DatabaseKind\n host: string\n port: number\n database: string\n user: string\n /** `env:NAME` or `file:/absolute/path`. */\n password: string\n /** The schemas an administrator approved. Discovery reads nothing outside them. */\n schemas: string[]\n /**\n * TLS. On by default, and certificate checking with it: a deployment that\n * turns either off says so here, in a file somebody reviews.\n */\n tls?: { enabled?: boolean; trustServerCertificate?: boolean }\n}\n\n/** What the server holds for one open connection: the three ports. */\nexport interface OpenConnection {\n adapter: DatabaseAdapter\n lookups: LookupAdapter\n records: RecordAdapter\n}\n\n/**\n * Opens a connection of one kind. Supplied by the composition root, which\n * imports the drivers; nothing here does, so the registry is tested with a\n * fake and the server's core depends on no driver.\n */\nexport type ConnectionFactory = (config: ConnectionConfig, password: string) => Promise<OpenConnection>\n\nconst ID = /^[a-z0-9][a-z0-9._-]{0,63}$/\n\n/**\n * The allowlist, checked: every problem at once, so an operator fixes the file\n * in one pass rather than one restart per mistake.\n */\nexport function parseConnections(document: unknown): { ok: true; connections: ConnectionConfig[] } | { ok: false; problems: string[] } {\n if (!Array.isArray(document)) return { ok: false, problems: ['the connections file is a list'] }\n const problems: string[] = []\n const seen = new Set<string>()\n const connections: ConnectionConfig[] = []\n document.forEach((entry: unknown, index) => {\n const where = `connections[${String(index)}]`\n if (typeof entry !== 'object' || entry === null) {\n problems.push(`${where} is not an object`)\n return\n }\n const value = entry as Record<string, unknown>\n const id = value['id']\n const before = problems.length\n if (typeof id !== 'string' || !ID.test(id)) problems.push(`${where}.id must be lower-case letters, digits, dot, hyphen or underscore`)\n else if (seen.has(id)) problems.push(`${where}.id ${id} is used twice`)\n if (!isDatabaseKind(value['kind'])) problems.push(`${where}.kind must be postgres or sqlserver`)\n for (const field of ['host', 'database', 'user'] as const) {\n if (typeof value[field] !== 'string' || value[field] === '') problems.push(`${where}.${field} is required`)\n }\n const port = value['port']\n if (typeof port !== 'number' || !Number.isInteger(port) || port < 1 || port > 65535) problems.push(`${where}.port must be a port number`)\n const password = value['password']\n if (typeof password !== 'string' || !(password.startsWith('env:') || password.startsWith('file:'))) {\n // Never echoed: a value here that is not a reference is most likely the password itself.\n problems.push(`${where}.password must be a secret reference, env:NAME or file:/path`)\n }\n const schemas = value['schemas']\n if (!Array.isArray(schemas) || schemas.length === 0 || !schemas.every((schema) => typeof schema === 'string' && schema !== '')) {\n problems.push(`${where}.schemas must list at least one approved schema`)\n }\n if (problems.length === before && typeof id === 'string') {\n seen.add(id)\n connections.push(entry as ConnectionConfig)\n }\n })\n return problems.length === 0 ? { ok: true, connections } : { ok: false, problems }\n}\n\nexport interface ConnectionRegistry {\n /** The ids a form may bind to. */\n ids(): string[]\n /** The approved scope of one connection, or `undefined` if the deployment does not allow it. */\n scope(id: string): DiscoveryScope | undefined\n /**\n * The connection's ports, opened on first use and shared after. A connection\n * that failed to open is tried again on the next call, not cached as broken:\n * a database restarting is not a reason to restart the server.\n */\n open(id: string): Promise<OpenConnection | undefined>\n /** Closes every open connection. Safe to call twice. */\n close(): Promise<void>\n}\n\n/**\n * The allowlisted connections, opened lazily through the composition root's\n * factories. A form names a connection; this is the only place that name\n * becomes a database, so a form cannot reach a database the operator did not\n * list.\n */\nexport function createConnectionRegistry(\n connections: readonly ConnectionConfig[],\n factories: Readonly<Partial<Record<DatabaseKind, ConnectionFactory>>>,\n secrets?: SecretSource,\n): ConnectionRegistry {\n const byId = new Map(connections.map((connection) => [connection.id, connection]))\n const opening = new Map<string, Promise<OpenConnection>>()\n\n return {\n ids: () => [...byId.keys()].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)),\n\n scope(id) {\n const config = byId.get(id)\n return config === undefined ? undefined : { schemas: [...config.schemas] }\n },\n\n async open(id) {\n const config = byId.get(id)\n if (config === undefined) return undefined\n let pending = opening.get(id)\n if (pending === undefined) {\n const factory = factories[config.kind]\n if (factory === undefined) throw new Error(`no driver is configured for ${config.kind} connections`)\n pending = resolveSecret(config.password, secrets).then((password) => factory(config, password))\n opening.set(id, pending)\n // Forgotten on failure, so the next request tries again.\n pending.catch(() => opening.delete(id))\n }\n return pending\n },\n\n async close() {\n const open = [...opening.values()]\n opening.clear()\n await Promise.allSettled(open.map(async (pending) => (await pending).adapter.close()))\n },\n }\n}\n","import type { DatabaseAdapter, DatabaseKind } from '@formancy/data-core'\nimport { connectPostgres, createPostgresAdapter, createPostgresLookups, createPostgresRecords } from '@formancy/data-postgres'\nimport { connectSqlServer, createSqlServerAdapter, createSqlServerLookups, createSqlServerRecords } from '@formancy/data-sqlserver'\nimport type { ConnectionConfig, ConnectionFactory } from './connections.js'\n\n/** TLS unless the operator turned it off, and certificates checked unless the operator trusts the server's. */\nfunction tls(config: ConnectionConfig): { enabled: boolean; trust: boolean } {\n return { enabled: config.tls?.enabled ?? true, trust: config.tls?.trustServerCertificate ?? false }\n}\n\n/** The application name the lookups' client gives PostgreSQL, so a DBA tells it from the records' in pg_stat_activity (0061). */\nexport const POSTGRES_LOOKUPS_APPLICATION = 'formancy-data lookups'\n\n/**\n * PostgreSQL. Pinged before it is handed out, so an allowlisted connection that\n * cannot be reached fails here, where the registry retries it next time,\n * rather than on the first record a person saves.\n *\n * Two clients (0061). Lookups run on one of their own, because PostgreSQL\n * delivers a cancel on a connection of its own, and a cancel that arrives\n * after its statement finished stops whatever that backend runs next: in the\n * 2026-10-14 probe a lookup's late cancel stopped the statement pipelined\n * behind it on a shared pool of one. On their own client a late cancel can\n * stop only another lookup -- which the adapter runs once more -- and never a\n * record read or write, which 0015 never retries. The cost is up to twice the\n * connections per allowlisted database, each pool at the driver's default.\n * The ping runs on the records' client; a failure ends both. Closing the\n * adapter closes both, each as the adapter closes its own.\n */\nconst openPostgres: ConnectionFactory = async (config, password) => {\n const { enabled, trust } = tls(config)\n const options = {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password,\n tls: { enabled, rejectUnauthorized: !trust },\n }\n const sql = connectPostgres(options)\n const lookupSql = connectPostgres({ ...options, applicationName: POSTGRES_LOOKUPS_APPLICATION })\n const records = createPostgresAdapter(sql)\n const lookups = createPostgresAdapter(lookupSql)\n const adapter: DatabaseAdapter = {\n ...records,\n close: async () => {\n await Promise.all([records.close(), lookups.close()])\n },\n }\n try {\n await adapter.ping()\n } catch (error) {\n await Promise.all([sql.end({ timeout: 1 }), lookupSql.end({ timeout: 1 })])\n throw error\n }\n return { adapter, lookups: createPostgresLookups(lookupSql), records: createPostgresRecords(sql) }\n}\n\n/** SQL Server: no native module, no ODBC. */\nconst openSqlServer: ConnectionFactory = async (config, password) => {\n const { enabled, trust } = tls(config)\n const pool = await connectSqlServer({\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password,\n encrypt: enabled,\n trustServerCertificate: trust,\n })\n return { adapter: createSqlServerAdapter(pool), lookups: createSqlServerLookups(pool), records: createSqlServerRecords(pool) }\n}\n\n/**\n * The drivers, one factory per engine. Each adapter package opens its own\n * connections (`connectPostgres`, `connectSqlServer`), so a connection and the\n * code that binds values on it always come from one copy of the driver — the\n * server imports no driver at all, and the end-to-end suite is what found why\n * that matters.\n */\nexport const DRIVER_FACTORIES: Readonly<Record<DatabaseKind, ConnectionFactory>> = {\n postgres: openPostgres,\n sqlserver: openSqlServer,\n}\n"],"mappings":"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"}
|