@vantis/data 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/verbs.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["/**\n * @vantis/data — branded atomic modifier helpers (VAN-866, ADR-0047).\n *\n * Each helper returns a Symbol-branded VerbEnvelope that .modify() serializes\n * into the __vantis_ops JSONB argument. JSON.stringify() ignores Symbol-keyed\n * properties, so the brand never appears on the wire.\n *\n * Security: the server-side __vantis_modify_<t> function is the actual\n * security boundary — it fully validates ops, match, and operand types.\n * These helpers provide ergonomics + compile-time safety only.\n *\n * Wire contract (engine-generated function signature):\n * api.__vantis_modify_<t>(\n * __vantis_match jsonb, -- PK columns object\n * __vantis_ops jsonb, -- { \"<col>\": { \"op\": \"<opname>\", \"v\"?: <operand>, \"path\"?: string[] } }\n * __vantis_row jsonb, -- optional insert row (upsert branch)\n * __vantis_guard jsonb -- optional single-column guard { \"<col>\": { \"<cmp>\": <value> } }\n * ) RETURNS SETOF api.<t>\n */\n\n/** Brand symbol for verb envelopes. Symbol-keyed, ignored by JSON.stringify. */\nexport const VERB_BRAND = Symbol.for('@vantis/data:verb');\n\n/**\n * A Symbol-branded atomic modifier envelope returned by the verb helpers.\n *\n * The VERB_BRAND property is non-enumerable and symbol-keyed, so\n * JSON.stringify() never includes it on the wire — the brand exists solely\n * to distinguish a modifier from a plain object (preventing a bare\n * `{op: \"merge\", v: {...}}` from being misread as a modifier envelope).\n *\n * String-keyed properties (op, v?, path?) are enumerable and are the\n * wire payload. .modify() serializes them directly into __vantis_ops.\n */\nexport interface VerbEnvelope {\n readonly [VERB_BRAND]: true;\n /** The operation name sent to the engine (e.g. \"increment\", \"toggle\"). */\n readonly op: string;\n /** Operand value — absent for zero-operand ops (toggle, serverTimestamp). */\n readonly v?: unknown;\n /**\n * JSON path for jsonSet — an array of path segments.\n * Only present on envelopes returned by jsonSet().\n * Sent as __vantis_ops->col->'path' alongside 'v'.\n */\n readonly path?: string[];\n}\n\n/**\n * Phantom-branded subtype of VerbEnvelope for numeric column ops\n * (increment / decrement / multiply / divide / min / max).\n * The `__verb` property is optional and never-valued at runtime\n * (TS-only phantom discriminant; JSON.stringify never emits it).\n */\nexport interface NumericVerb extends VerbEnvelope { readonly __verb?: 'numeric' }\n\n/**\n * Phantom-branded subtype of VerbEnvelope for boolean column ops (toggle).\n */\nexport interface BooleanVerb extends VerbEnvelope { readonly __verb?: 'boolean' }\n\n/**\n * Phantom-branded subtype of VerbEnvelope for timestamptz/date column ops\n * (serverTimestamp).\n */\nexport interface TemporalVerb extends VerbEnvelope { readonly __verb?: 'temporal' }\n\n/**\n * Phantom-branded subtype of VerbEnvelope for text/varchar column ops (concat).\n */\nexport interface TextVerb extends VerbEnvelope { readonly __verb?: 'text' }\n\n/**\n * Phantom-branded subtype of VerbEnvelope for jsonb column ops\n * (merge / jsonSet).\n */\nexport interface JsonbVerb extends VerbEnvelope { readonly __verb?: 'jsonb' }\n\n/**\n * Phantom-branded subtype of VerbEnvelope for the cross-type setOnInsert op.\n * Valid on ANY non-PK column in the upsert branch.\n */\nexport interface SetOnInsertVerb extends VerbEnvelope { readonly __verb?: 'setOnInsert' }\n\n/**\n * @internal Check if a value is a branded VerbEnvelope.\n * Used by .modify() to reject bare objects passed as ops values.\n */\nexport function isVerbEnvelope(v: unknown): v is VerbEnvelope {\n return (\n v !== null &&\n typeof v === 'object' &&\n (v as Record<symbol, unknown>)[VERB_BRAND] === true\n );\n}\n\n/**\n * Build a frozen verb envelope. The brand is stored as a non-enumerable\n * symbol property so JSON.stringify and Object.entries omit it.\n * The remaining enumerable string properties (op, v?, path?) are the wire payload.\n */\nfunction makeVerb(op: string, props?: Record<string, unknown>): VerbEnvelope {\n // Object.create(null): no prototype, no __proto__ setter attack vector.\n const env = Object.create(null) as Record<string | symbol, unknown>;\n // Non-enumerable symbol property: excluded from JSON.stringify + Object.entries.\n Object.defineProperty(env, VERB_BRAND, {\n value: true,\n enumerable: false,\n configurable: false,\n writable: false,\n });\n env['op'] = op;\n if (props) {\n for (const [k, vv] of Object.entries(props)) {\n env[k] = vv;\n }\n }\n return Object.freeze(env as unknown as VerbEnvelope);\n}\n\n/**\n * Serialize a numeric operand for precise wire representation.\n * bigint → string (prevents JS precision loss for values > 2^53 − 1).\n * number | string → unchanged.\n */\nfunction numOp(n: number | string | bigint): number | string {\n return typeof n === 'bigint' ? String(n) : n;\n}\n\n// ---------------------------------------------------------------------------\n// Numeric verbs — integer / bigint / numeric / real / double columns\n// ---------------------------------------------------------------------------\n\n/**\n * Increment a numeric column by n (atomic server-side, no lost-update risk).\n *\n * @param n - The increment amount. bigint is serialized as a string to\n * preserve precision; the server casts it to the column type.\n */\nexport function increment(n: number | string | bigint): NumericVerb {\n return makeVerb('increment', { v: numOp(n) }) as NumericVerb;\n}\n\n/**\n * Decrement a numeric column by n (atomic server-side).\n *\n * @param n - The decrement amount. bigint is serialized as a string.\n */\nexport function decrement(n: number | string | bigint): NumericVerb {\n return makeVerb('decrement', { v: numOp(n) }) as NumericVerb;\n}\n\n/**\n * Multiply a numeric column by n (atomic server-side).\n *\n * @param n - The multiplier. bigint is serialized as a string.\n */\nexport function multiply(n: number | string | bigint): NumericVerb {\n return makeVerb('multiply', { v: numOp(n) }) as NumericVerb;\n}\n\n/**\n * Divide a numeric column by n.\n *\n * Division by zero raises SQLSTATE 22012 and rolls the whole operation back\n * atomically — surfaced as a typed DataError. Integer/numeric overflow raises\n * 22003 and is also rolled back atomically.\n *\n * @param n - The divisor. bigint is serialized as a string.\n */\nexport function divide(n: number | string | bigint): NumericVerb {\n return makeVerb('divide', { v: numOp(n) }) as NumericVerb;\n}\n\n/**\n * Set a numeric field to v only if v < the current value — Mongo $min semantics\n * (running minimum / lower-clamp via LEAST(current, v)).\n * If the current value is NULL, the field is set to v.\n *\n * @param v - The candidate lower bound. bigint is serialized as a string.\n */\nexport function min(v: number | string | bigint): NumericVerb {\n return makeVerb('min', { v: numOp(v) }) as NumericVerb;\n}\n\n/**\n * Set a numeric field to v only if v > the current value — Mongo $max semantics\n * (high-water-mark / upper-clamp via GREATEST(current, v)).\n * If the current value is NULL, the field is set to v.\n *\n * @param v - The candidate upper bound. bigint is serialized as a string.\n */\nexport function max(v: number | string | bigint): NumericVerb {\n return makeVerb('max', { v: numOp(v) }) as NumericVerb;\n}\n\n// ---------------------------------------------------------------------------\n// Boolean verbs — boolean columns\n// ---------------------------------------------------------------------------\n\n/**\n * Flip a boolean column atomically (server reads the current value and negates it).\n *\n * Only valid on the UPDATE branch — toggle on the upsert INSERT seed raises a\n * server-side error (there is no \"current value\" to flip on a new row). Use\n * setOnInsert() to seed a boolean value on the upsert path.\n */\nexport function toggle(): BooleanVerb {\n return makeVerb('toggle') as BooleanVerb;\n}\n\n// ---------------------------------------------------------------------------\n// Timestamp verbs — timestamptz / date columns\n// ---------------------------------------------------------------------------\n\n/**\n * Set a timestamptz or date column to the server transaction clock\n * (PostgreSQL transaction_timestamp() — consistent within the same transaction,\n * independent of the client clock).\n *\n * Named `serverTimestamp` (not `now`) to avoid collision with the `now()`\n * builtin default (a column `default` in `vantis.schema.json`). No operand — the\n * server supplies the value.\n */\nexport function serverTimestamp(): TemporalVerb {\n return makeVerb('serverTimestamp') as TemporalVerb;\n}\n\n// ---------------------------------------------------------------------------\n// Text verbs — text / varchar columns\n// ---------------------------------------------------------------------------\n\n/**\n * Append s to a text or varchar column atomically (server-side col || s).\n *\n * @param s - The string to append. Concatenated at the server, not the client.\n */\nexport function concat(s: string): TextVerb {\n return makeVerb('concat', { v: s }) as TextVerb;\n}\n\n// ---------------------------------------------------------------------------\n// JSONB verbs — jsonb columns\n// ---------------------------------------------------------------------------\n\n/**\n * Atomically shallow-merge obj into a jsonb column\n * (PostgreSQL `col || obj` — union of top-level keys, obj wins on duplicates).\n * Does NOT recursively merge nested objects — for deep merges, use jsonSet().\n *\n * @param obj - The object to shallow-merge into the column.\n */\nexport function merge(obj: Record<string, unknown>): JsonbVerb {\n return makeVerb('merge', { v: obj }) as JsonbVerb;\n}\n\n/**\n * Atomically set a path within a jsonb column to v\n * (PostgreSQL `jsonb_set(col, path, v, create_if_missing := true)`).\n *\n * Creates the LEAF key at `path` if absent, but intermediate path segments\n * must already exist — if an intermediate key is missing, PostgreSQL returns\n * the original column value unchanged (it does not create nested structure).\n * Use `merge()` first to ensure the intermediate container exists if needed.\n *\n * @param path - Array of path segments (string keys; integer indices as strings).\n * @param v - The value to write at the path (any JSON-serializable value).\n */\nexport function jsonSet(path: string[], v: unknown): JsonbVerb {\n return makeVerb('jsonSet', { path, v }) as JsonbVerb;\n}\n\n// ---------------------------------------------------------------------------\n// Cross-type verbs — any non-PK column\n// ---------------------------------------------------------------------------\n\n/**\n * On the upsert INSERT branch: set this column to v (Mongo $setOnInsert semantics).\n * On the UPDATE branch: no-op — the column retains its current value.\n *\n * Only valid when `.modify()` is called with `opts.upsert` (the upsert branch);\n * using setOnInsert without `opts.upsert` raises a server-side error.\n *\n * @param v - The value to set on INSERT. Must be a non-null JSON-serializable value.\n * Passing `null` is a compile-time error; the engine rejects an explicit\n * JSON null operand for setOnInsert at the server-side preamble.\n */\nexport function setOnInsert<T>(v: T extends null ? never : T): SetOnInsertVerb {\n return makeVerb('setOnInsert', { v }) as SetOnInsertVerb;\n}\n","/**\n * @vantis/data — typed PostgREST error.\n *\n * Maps the PostgREST error body shape `{ code, message, details, hint }`.\n * The result discriminated union returned by every query builder:\n * Success → { data: T; error: null; count: number|null; status: number; statusText: string }\n * Failure → { data: null; error: DataError; count: null; status: number; statusText: string }\n *\n * Security (INV-01/INV-02): the token is NEVER included in any error field.\n * Full PostgREST error detail is returned to the tenant's own pod (server-tier —\n * acceptable per INV-10 / ADR-0039 §4). Public-edge redaction is VAN-844.\n */\n\n/**\n * A typed error returned from the Data API.\n *\n * Mirrors the PostgREST error body: code (PostgreSQL error code, e.g. \"42P01\"),\n * message (human-readable), and the optional details/hint fields.\n *\n * The `kind` field distinguishes config errors (missing env vars, browser\n * environment) from wire errors returned by PostgREST.\n */\nexport interface DataError {\n /** Discriminator: 'postgrest' for wire errors; 'config' for SDK config errors. */\n readonly kind: 'postgrest' | 'config';\n /** PostgreSQL error code (e.g. \"42P01\", \"PGRST301\") — or a short config key. */\n readonly code: string;\n /** Human-readable error message. NEVER contains the DATA_API_TOKEN. */\n readonly message: string;\n /** Additional error detail from PostgreSQL or PostgREST. May be null. */\n readonly details: string | null;\n /** Hint from PostgreSQL or PostgREST. May be null. */\n readonly hint: string | null;\n}\n\n/**\n * Discriminated result union for every Data API query.\n *\n * On success: `error` is null, `data` holds the typed payload.\n * On failure: `data` is null, `error` holds the typed DataError.\n * `count` carries the total row count (when requested) and is null on failure.\n * `status`/`statusText` mirror the HTTP response.\n */\nexport type DataResult<T> =\n | { data: T; error: null; count: number | null; status: number; statusText: string }\n | { data: null; error: DataError; count: null; status: number; statusText: string };\n\n// ---------------------------------------------------------------------------\n// Internal helpers — not exported; called from client.ts only.\n// ---------------------------------------------------------------------------\n\n/**\n * Build a config-class DataError (missing env, browser guard, etc.).\n * The token is NEVER passed here.\n */\nexport function makeConfigError(code: string, message: string): DataError {\n return { kind: 'config', code, message, details: null, hint: null };\n}\n\n/**\n * Parse the PostgREST error body from a failed response.\n * Falls back to a synthetic error when the body is not the expected JSON shape.\n * The token is never in the URL or body (it's a header only — INV-01/INV-02).\n */\nexport async function parsePostgRESTError(res: Response): Promise<DataError> {\n let code = 'PGRST_UNKNOWN';\n let message = `HTTP ${res.status} ${res.statusText}`;\n let details: string | null = null;\n let hint: string | null = null;\n\n try {\n const body: unknown = await res.json();\n if (body !== null && typeof body === 'object') {\n const b = body as Record<string, unknown>;\n if (typeof b['code'] === 'string') code = b['code'];\n if (typeof b['message'] === 'string') message = b['message'];\n if (typeof b['details'] === 'string') details = b['details'];\n else if (b['details'] == null) details = null;\n if (typeof b['hint'] === 'string') hint = b['hint'];\n else if (b['hint'] == null) hint = null;\n }\n } catch {\n // Body was not JSON — keep the fallback values.\n }\n\n return { kind: 'postgrest', code, message, details, hint };\n}\n","/**\n * @vantis/data — zero-config typed query client.\n *\n * Exports the ambient `data` object. Entry point: `data.from(tableName)`.\n *\n * Security contracts:\n * - INV-01/INV-02: DATA_API_TOKEN is NEVER logged, NEVER included in error\n * messages, NEVER placed in the URL (it is an Authorization header only).\n * - INV-11: env vars are read LAZILY on the FIRST QUERY (not at import/module\n * eval) so a framework build (next build) that imports server modules before\n * deploy-time injection does not throw.\n * - Browser guard: if a browser global is present at query time, fail closed\n * with a server-only error (secondary to the `exports` browser condition stub\n * in browser.ts — the runtime guard is defense-in-depth).\n * - ALL clients have explicit timeouts per CONVENTIONS.md.\n */\n\nimport type { Schema, SchemaInsert, Embeds, Rpc, Verbs } from './registry.js';\nimport { isVerbEnvelope, type VerbEnvelope } from './verbs.js';\nimport {\n type DataResult,\n type DataError,\n makeConfigError,\n parsePostgRESTError,\n} from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Type helpers\n// ---------------------------------------------------------------------------\n\n/** The row shape for a given table (pulled from the augmented Schema). */\ntype Row<T extends keyof Schema> = Schema[T];\n\n/**\n * Strict insert payload for a Schema table (VAN-869, ADR-0048).\n *\n * When the Go emitter (`vantis data build`) has registered the table in the\n * `SchemaInsert` interface, this resolves to `SchemaInsert[T]` — a shape\n * where NOT-NULL-no-default columns are required and nullable/defaulted columns\n * are optional. Tables absent from SchemaInsert (e.g. during initial authoring\n * before the first `vantis data build`) fall back to `Partial<Row<T>>`.\n *\n * Used by `.insert()`, `.upsert()`, and the `upsert` branch of `ModifyOptions`.\n */\ntype InsertPayload<T extends keyof Schema> = T extends keyof SchemaInsert\n ? SchemaInsert[T]\n : Partial<Row<T>>;\n\n/**\n * Partial update payload — always `Partial<Row<T>>`.\n *\n * `.update()` sends only the columns to change; requiring all NOT-NULL columns\n * would be wrong for a partial update. This is separate from InsertPayload so\n * insert/upsert can be strict while update remains permissive.\n */\ntype UpdatePayload<T extends keyof Schema> = Partial<Row<T>>;\n\n/**\n * Resolves the embed result type for a related table.\n *\n * Reads the `Embeds` cardinality registry populated by `vantis data build`:\n * - `'one'` (FK column is the referencing table's PK) → `Schema[TRelated]` (object)\n * - `'many'` (all other FKs — one-to-many inverse) → `Schema[TRelated][]` (array)\n * - `'one' | 'many'` (self-FK or ambiguous) → `Schema[TRelated] | Schema[TRelated][]` (union)\n *\n * Defaults to the array form when the table or relationship is absent from\n * `Embeds` (safe: PostgREST returns an array for unregistered embed shapes).\n *\n * EmbedByCardinality uses a bare type parameter `C` so TypeScript distributes\n * over union cardinalities: `EmbedByCardinality<R, 'one' | 'many'>` resolves\n * to `Schema[R] | Schema[R][]` rather than collapsing to the array branch\n * (the old `Embeds[TTable][TRelated] extends 'one'` pattern was NOT\n * distributive because the checked type was a property access, not a bare\n * parameter — VAN-869 fix D).\n */\ntype EmbedByCardinality<R extends keyof Schema, C> = C extends 'one'\n ? Schema[R]\n : Schema[R][];\n\ntype EmbedResult<\n TTable extends keyof Schema,\n TRelated extends keyof Schema,\n> = TTable extends keyof Embeds\n ? TRelated extends keyof Embeds[TTable]\n ? EmbedByCardinality<TRelated, Embeds[TTable][TRelated]>\n : Schema[TRelated][]\n : Schema[TRelated][];\n\n/**\n * The `ops` shape for `.modify()` on a given table — `VerbEnvelope` per\n * modifiable (non-PK, non-uuid) column, all optional.\n *\n * Resolves to `never` for tables absent from the `Verbs` registry (i.e.\n * tables with no `__vantis_modify_<t>` engine function — no PK, or all\n * non-PK columns are non-modifiable types like uuid). Calling `.modify()`\n * on such a table is a compile-time type error: the engine would 404 at\n * runtime, so it must be caught by the type system instead.\n *\n * Populated by `vantis data build` emitting a per-project `.vantis.d.ts`\n * that augments `interface Verbs { … }` — exactly as `Schema` is populated\n * for `data.from()` and `Rpc` is populated for `data.run.<name>()`.\n */\ntype VerbOpsFor<TTableName extends keyof Schema> =\n TTableName extends keyof Verbs\n ? 'ops' extends keyof Verbs[TTableName]\n ? Verbs[TTableName]['ops']\n : never\n : never;\n\n/**\n * The `match` shape for `.modify()` on a given table — PK column values.\n *\n * Resolves to `never` for tables absent from the `Verbs` registry (same\n * reasons as VerbOpsFor — engine-absent tables must fail at compile time).\n */\ntype VerbMatchFor<TTableName extends keyof Schema> =\n TTableName extends keyof Verbs\n ? 'match' extends keyof Verbs[TTableName]\n ? Verbs[TTableName]['match']\n : never\n : never;\n\n/**\n * The `guard` shape for `.modify()` on a given table — typed comparator\n * objects per column. Falls back to `Record<string, Record<string, unknown>>`\n * for tables absent from the `Verbs` registry.\n */\ntype VerbGuardFor<TTableName extends keyof Schema> =\n TTableName extends keyof Verbs\n ? 'guard' extends keyof Verbs[TTableName]\n ? Verbs[TTableName]['guard']\n : Record<string, Record<string, unknown>>\n : Record<string, Record<string, unknown>>;\n\n/**\n * Options for `.modify()`. Generic over the table name so `guard` is typed\n * to the column + comparator shape generated by `vantis data build`.\n *\n * `guard` and `upsert` are mutually exclusive at the type level (XOR):\n * providing both is a compile-time error. The engine enforces the same\n * constraint at runtime (sending both raises INVALID_ARGUMENT).\n *\n * @property upsert - When present, activates the upsert branch: if the row\n * identified by `match` does not exist, it is inserted with the values\n * from this object. Use setOnInsert() in `ops` to set additional fields\n * only on INSERT (Mongo $setOnInsert semantics).\n *\n * @property guard - Optional single-column optimistic-concurrency check.\n * Shape: `{ \"<col>\": { \"<comparator>\": <value> } }`.\n * Comparators: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`.\n * If the guard condition is NOT met, the engine returns an empty array\n * (not-applied signal) without modifying the row or raising an error.\n * Check for `result.data?.length === 0` in your application logic.\n */\nexport type ModifyOptions<T extends keyof Schema = keyof Schema> =\n | { upsert?: InsertPayload<T>; guard?: undefined }\n | { upsert?: undefined; guard?: VerbGuardFor<T> };\n\n// ---------------------------------------------------------------------------\n// PostgREST wire helpers\n// ---------------------------------------------------------------------------\n\n/** Default per-request timeout in milliseconds (30 seconds). */\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * RPC client-abort timeout (ms). Must exceed the maximum per-function\n * statement_timeout a customer may declare (60s, validated in internal/schema)\n * plus margin, so a within-spec long-running RPC is bounded by the DB, not\n * killed early by the client. Table queries keep DEFAULT_TIMEOUT_MS (they are\n * capped at 5s by the db-pre-request governor regardless). VAN-832.\n */\nconst DEFAULT_RPC_TIMEOUT_MS = 65_000;\n\n/** Resolved Data API environment, or a config error to return verbatim. */\ntype DataApiEnv = { apiUrl: string; apiToken: string } | { configError: DataError };\n\n/**\n * JSON.stringify replacer that serializes bigint values as decimal strings.\n * Prevents \"TypeError: Do not know how to serialize a BigInt\" when bigint\n * values appear in match/guard/upsert payloads (B3, VAN-866).\n * Numeric verb operands are already string-ified by numOp() in verbs.ts.\n */\nfunction bigintReplacer(_key: string, value: unknown): unknown {\n return typeof value === 'bigint' ? String(value) : value;\n}\n\n/**\n * Resolve the Data API environment with the single source of the server-only\n * browser guard + lazy env read (INV-01/INV-02/INV-11). Both QueryBuilder._execute\n * and executeRpc build on this so the security policy is defined once.\n */\nfunction resolveDataApiEnv(): DataApiEnv {\n // Browser guard (defense-in-depth; the browser export condition is primary).\n const _g = globalThis as Record<string, unknown>;\n if (_g['window'] !== undefined || _g['document'] !== undefined) {\n return {\n configError: makeConfigError(\n 'BROWSER_ENV',\n '@vantis/data is server-only. DATA_API_TOKEN must never run in a browser. ' +\n 'The client-side SDK is tracked as VAN-846.',\n ),\n };\n }\n // Lazy env read on call (INV-11).\n const apiUrl = (typeof process !== 'undefined' ? process.env['DATA_API_URL'] : undefined) ?? '';\n const apiToken = (typeof process !== 'undefined' ? process.env['DATA_API_TOKEN'] : undefined) ?? '';\n if (!apiUrl) {\n return {\n configError: makeConfigError(\n 'MISSING_DATA_API_URL',\n 'DATA_API_URL is not set. Ensure this service has declared a --uses edge ' +\n 'to a database block so Vantis injects the runtime env vars.',\n ),\n };\n }\n if (!apiToken) {\n return {\n configError: makeConfigError(\n 'MISSING_DATA_API_TOKEN',\n 'DATA_API_TOKEN is not set. Ensure this service has declared a --uses edge ' +\n 'to a database block so Vantis injects the runtime env vars.',\n ),\n };\n }\n return { apiUrl, apiToken };\n}\n\n/**\n * Translate a `%` wildcard (SQL LIKE) to the PostgREST `*` wildcard.\n * PostgREST uses `*` in the like/ilike operator — SQL `%` does not work on\n * the wire.\n */\nfunction translateWildcard(pattern: string): string {\n return pattern.replace(/%/g, '*');\n}\n\n/**\n * Quote a single element for inclusion in a PostgREST `in` list: `in.(a,b,c)`.\n *\n * The `in` operator uses a comma-separated list inside parentheses. A comma in\n * an element value WOULD split it into two elements — that is the real injection\n * vector. `(` `)` close/open the list; `\"` and `\\` are quoting metacharacters.\n *\n * Elements that contain any of `, ( ) \" \\` are wrapped in double-quotes with `\\`\n * escaped as `\\\\` and `\"` escaped as `\\\"`. URLSearchParams then percent-encodes\n * the literal double-quotes — PostgREST decodes and parses them correctly per the\n * v14 URL grammar (https://postgrest.org/en/v14/references/api/url_grammar.html).\n *\n * Elements that contain ONLY `.` `:` or whitespace are NOT quoted here — those\n * chars do not break the comma list. (Scalar filter methods do not call this\n * function at all — see the note on scalar operators below.)\n *\n * NOTE: Do NOT apply this to scalar filter operators (eq/neq/gt/gte/lt/lte/\n * like/ilike). Scalar filters are each their own query parameter; URLSearchParams\n * URL-encodes the value, so no injection is possible, and PostgREST does NOT strip\n * double-quotes for scalar operators — quoting them causes a literal string mismatch\n * (e.g. `email=eq.\"a@b.com\"` returns 0 rows even when `a@b.com` exists).\n *\n * NOTE: Do NOT apply this to the `is` operator values (null/true/false) — they\n * are fixed keywords, not user-supplied values.\n */\nfunction quoteInElement(v: unknown): string {\n const s = String(v);\n // Only quote when the value contains list-breaking chars: , ( ) \" \\\n if (/[,()\"\\\\]/.test(s)) {\n // Escape backslash first (order matters), then double-quote.\n return `\"${s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n }\n return s;\n}\n\n/**\n * Encode a value array for PostgREST's `in` operator: `(a,b,c)`.\n * Each element is quoted via quoteInElement (only list-breaking chars trigger quoting).\n */\nfunction encodeIn(values: unknown[]): string {\n return `(${values.map(quoteInElement).join(',')})`;\n}\n\n// ---------------------------------------------------------------------------\n// The builder state type\n// ---------------------------------------------------------------------------\n\ninterface BuilderState {\n table: string;\n filters: string[];\n selectCols: string;\n orderParts: string[];\n limitVal: number | null;\n limitReferencedTable: string | null;\n offsetVal: number | null;\n countMode: 'exact' | 'planned' | 'estimated' | null;\n headMode: boolean;\n singleMode: boolean;\n maybeSingleMode: boolean;\n // Write mode\n writeOp: 'insert' | 'update' | 'delete' | 'upsert' | null;\n writeBody: unknown;\n // Whether .select() was chained after a write (return=representation).\n writeSelect: boolean;\n // Column projection for write.select (null = '*').\n writeSelectCols: string;\n // Upsert options\n onConflict: string | null;\n ignoreDuplicates: boolean;\n // FK embedding state: list of embedded table names.\n embeds: string[];\n // Full-text search state (set by .search()).\n // When non-null, _buildRequest routes to /rpc/search_<table> and skips\n // table filters/select/order/limit (the RPC sorts + ranks server-side).\n searchQuery: string | null;\n searchLimit: number | null;\n}\n\nfunction defaultState(tableName: string): BuilderState {\n return {\n table: tableName,\n filters: [],\n selectCols: '*',\n orderParts: [],\n limitVal: null,\n limitReferencedTable: null,\n offsetVal: null,\n countMode: null,\n headMode: false,\n singleMode: false,\n maybeSingleMode: false,\n writeOp: null,\n writeBody: null,\n writeSelect: false,\n writeSelectCols: '*',\n onConflict: null,\n ignoreDuplicates: false,\n embeds: [],\n searchQuery: null,\n searchLimit: null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Query builder generics\n// ---------------------------------------------------------------------------\n\n/**\n * Options for `.select()` when `head` is false or absent (normal query).\n * - count: request a count total from PostgREST. Default none.\n */\nexport interface SelectOptions {\n head?: false;\n count?: 'exact' | 'planned' | 'estimated';\n}\n\n/**\n * Options for `.select()` when `head` is true (HEAD request — count-only).\n *\n * @note `head: true` returns `data: null` (HEAD has no body). The data\n * field will always be null regardless of the row type parameter.\n */\nexport interface SelectHeadOptions {\n head: true;\n count?: 'exact' | 'planned' | 'estimated';\n}\n\n/** Options for `.order()`. */\nexport interface OrderOptions {\n ascending?: boolean;\n nullsFirst?: boolean;\n referencedTable?: string;\n}\n\n/** Options for `.limit()`. */\nexport interface LimitOptions {\n referencedTable?: string;\n}\n\n/** Options for `.upsert()`. */\nexport interface UpsertOptions {\n onConflict?: string;\n ignoreDuplicates?: boolean;\n}\n\n/**\n * Options for `.search()`.\n *\n * The server pre-sorts results best-first (relevance-ordered). The `limit`\n * option caps results client-side via the PostgREST `/rpc` limit parameter\n * (also bounded by `PGRST_DB_MAX_ROWS=1000` on the server).\n */\nexport interface SearchOptions {\n /** Maximum number of rows to return (passed as PostgREST `?limit=n`). */\n limit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// The builder — PromiseLike thenable\n// ---------------------------------------------------------------------------\n\n/**\n * The QueryBuilder is a thenable — `await data.from('users')` resolves to\n * a DataResult without requiring an explicit `.execute()` call.\n *\n * Generic parameters:\n * TTableName — the table name (keyof Schema).\n * TRow — the current row shape (modified by embed).\n * TResult — the full DataResult type for this builder's current state.\n *\n * v0 notes:\n * - `.select(columns)` types as the FULL row for any column string (column-\n * subset Pick narrowing is a v1 follow-up).\n * - `.insert(values)` payload is typed as `SchemaInsert[T]` when the Go emitter\n * has registered the table (strict: required NOT-NULL-no-default columns\n * enforced). Falls back to `Partial<Row[T]>` for unregistered tables.\n * - `.embed(table)` cardinality is resolved from the `Embeds` registry:\n * `'one'` → single object; `'many'` → array. Unregistered → array default.\n */\nexport class QueryBuilder<\n TTableName extends keyof Schema,\n TRow = Row<TTableName>,\n TResult extends DataResult<unknown> = DataResult<TRow[]>,\n> implements PromiseLike<TResult>\n{\n private readonly _state: BuilderState;\n\n constructor(tableName: TTableName, state?: BuilderState) {\n this._state = state ?? defaultState(tableName as string);\n }\n\n // ─── Filters ────────────────────────────────────────────────────────────\n\n /**\n * Equality filter: column = value.\n *\n * The value is emitted raw (not quoted). URLSearchParams URL-encodes it, which\n * is correct and sufficient for scalar operators — PostgREST takes the whole\n * value after `eq.` and does NOT strip double-quotes for scalar ops (quoting\n * would cause a literal string mismatch, e.g. `email=eq.\"a@b.com\"` → 0 rows).\n */\n eq<K extends keyof TRow & string>(col: K, val: TRow[K]): this {\n this._assertNotSearch('eq');\n this._state.filters.push(`${col}=eq.${String(val)}`);\n return this;\n }\n\n /**\n * Inequality filter: column != value.\n *\n * Value emitted raw — see eq() for the scalar-quoting rationale.\n */\n neq<K extends keyof TRow & string>(col: K, val: TRow[K]): this {\n this._assertNotSearch('neq');\n this._state.filters.push(`${col}=neq.${String(val)}`);\n return this;\n }\n\n /**\n * Greater-than filter: column > value.\n *\n * Value emitted raw — see eq() for the scalar-quoting rationale.\n */\n gt<K extends keyof TRow & string>(col: K, val: TRow[K]): this {\n this._assertNotSearch('gt');\n this._state.filters.push(`${col}=gt.${String(val)}`);\n return this;\n }\n\n /**\n * Greater-than-or-equal filter: column >= value.\n *\n * Value emitted raw — see eq() for the scalar-quoting rationale.\n */\n gte<K extends keyof TRow & string>(col: K, val: TRow[K]): this {\n this._assertNotSearch('gte');\n this._state.filters.push(`${col}=gte.${String(val)}`);\n return this;\n }\n\n /**\n * Less-than filter: column < value.\n *\n * Value emitted raw — see eq() for the scalar-quoting rationale.\n */\n lt<K extends keyof TRow & string>(col: K, val: TRow[K]): this {\n this._assertNotSearch('lt');\n this._state.filters.push(`${col}=lt.${String(val)}`);\n return this;\n }\n\n /**\n * Less-than-or-equal filter: column <= value.\n *\n * Value emitted raw — see eq() for the scalar-quoting rationale.\n */\n lte<K extends keyof TRow & string>(col: K, val: TRow[K]): this {\n this._assertNotSearch('lte');\n this._state.filters.push(`${col}=lte.${String(val)}`);\n return this;\n }\n\n /**\n * LIKE filter. Pattern uses `%` (translated to PostgREST `*` on the wire)\n * or `*` directly. Example: `.like('email', '%@example.com')`.\n *\n * The wildcard-translated pattern is emitted raw. URLSearchParams URL-encodes\n * it, which is correct and sufficient — PostgREST takes the whole value after\n * `like.` as the pattern. Do NOT quote like patterns: PostgREST does NOT strip\n * double-quotes for scalar operators, so `like.\"a.b*\"` would match a literal\n * value containing `\"` rather than performing `LIKE 'a.b%'`.\n *\n * NOTE: a LITERAL `*` in a user-supplied substring cannot be escaped —\n * PostgREST has no like-escape mechanism and `*` is always the wildcard.\n * Strip or reject literal `*` from user input at the call site if needed.\n */\n like<K extends keyof TRow & string>(col: K, pattern: string): this {\n this._assertNotSearch('like');\n this._state.filters.push(`${col}=like.${translateWildcard(pattern)}`);\n return this;\n }\n\n /**\n * Case-insensitive LIKE filter. Pattern uses `%` or `*`.\n *\n * Pattern emitted raw after wildcard translation — see like() for the\n * scalar-quoting rationale.\n */\n ilike<K extends keyof TRow & string>(col: K, pattern: string): this {\n this._assertNotSearch('ilike');\n this._state.filters.push(`${col}=ilike.${translateWildcard(pattern)}`);\n return this;\n }\n\n /**\n * IN filter: column IN (values).\n * Each element is quoted per the PostgREST URL grammar.\n */\n in<K extends keyof TRow & string>(col: K, values: TRow[K][]): this {\n this._assertNotSearch('in');\n this._state.filters.push(`${col}=in.${encodeIn(values as unknown[])}`);\n return this;\n }\n\n /**\n * IS filter: column IS true/false/null.\n * PostgREST wire: `?col=is.true` / `?col=is.false` / `?col=is.null`.\n * Values are fixed keywords — NOT subject to quoteFilterValue.\n */\n is<K extends keyof TRow & string>(col: K, val: boolean | null): this {\n this._assertNotSearch('is');\n const wire = val === null ? 'null' : val ? 'true' : 'false';\n this._state.filters.push(`${col}=is.${wire}`);\n return this;\n }\n\n // ─── Select ─────────────────────────────────────────────────────────────\n\n /**\n * Set the column selection and optional count/head options.\n *\n * v0 type-narrowing: column-subset narrowing (Pick) is not implemented for v0.\n * `.select('id, name')` types as the full row (same as `.select('*')`).\n *\n * `head: true` returns `data: null` (HEAD has no body — it is a count-only\n * request). The overload narrows the return to `DataResult<null>` when\n * `head: true` is passed as an option.\n *\n * When chained after a write (.insert/.update/.delete/.upsert), sends\n * `Prefer: return=representation` and emits `?select=<columns>` if a non-default\n * column list is given (v0 fix: write-chained .select('id') correctly narrows\n * the response columns, not silently over-returning all columns).\n */\n select(\n columns?: string,\n options?: SelectHeadOptions,\n ): QueryBuilder<TTableName, TRow, DataResult<null>>;\n select(\n columns?: string,\n options?: SelectOptions,\n ): QueryBuilder<TTableName, TRow, DataResult<TRow[]>>;\n select(\n columns?: string,\n options?: SelectOptions | SelectHeadOptions,\n ): QueryBuilder<TTableName, TRow, DataResult<null> | DataResult<TRow[]>> {\n this._assertNotSearch('select');\n if (this._state.writeOp !== null) {\n // Chained after a write — return representation.\n this._state.writeSelect = true;\n this._state.writeSelectCols = columns ?? '*';\n } else {\n this._state.selectCols = columns ?? '*';\n }\n if (options?.head) this._state.headMode = true;\n if (options?.count) this._state.countMode = options.count;\n return this as unknown as QueryBuilder<TTableName, TRow, DataResult<null> | DataResult<TRow[]>>;\n }\n\n // ─── FK embedding ────────────────────────────────────────────────────────\n\n /**\n * Embed a related table by FK.\n *\n * Adds the related table to the PostgREST select: `?select=*,<relatedTable>(*)`.\n * PostgREST resolves the FK automatically (requires a schema-cache reload after\n * FK changes — https://postgrest.org/en/v14/references/api/resource_embedding.html).\n *\n * The result row type gains `{ <relatedTable>: EmbedResult<TTableName, TEmbedTable> }`.\n * The cardinality is resolved from the `Embeds` registry populated by `vantis data build`:\n * - `'one'` (FK column is the referencing table's PK) → single object `Schema[TEmbedTable]`.\n * - `'many'` (all other FKs — one-to-many inverse) → array `Schema[TEmbedTable][]`.\n * - Unregistered relationships default to the array form.\n *\n * @example\n * ```ts\n * // posts.user_id FK → users (many-to-one; Embeds says 'one')\n * const result = await data.from('posts').embed('users');\n * // result.data[0].users — Schema['users'] (object, not array)\n *\n * // users.id → posts (one-to-many; Embeds says 'many')\n * const result2 = await data.from('users').embed('posts');\n * // result2.data[0].posts — Schema['posts'][] (array)\n * ```\n */\n embed<TEmbedTable extends keyof Schema>(\n relatedTable: TEmbedTable,\n ): QueryBuilder<\n TTableName,\n TRow & { [K in TEmbedTable]: EmbedResult<TTableName, TEmbedTable> },\n DataResult<(TRow & { [K in TEmbedTable]: EmbedResult<TTableName, TEmbedTable> })[]>\n > {\n this._assertNotSearch('embed');\n const name = relatedTable as string;\n if (!this._state.embeds.includes(name)) {\n this._state.embeds.push(name);\n }\n return this as unknown as QueryBuilder<\n TTableName,\n TRow & { [K in TEmbedTable]: EmbedResult<TTableName, TEmbedTable> },\n DataResult<(TRow & { [K in TEmbedTable]: EmbedResult<TTableName, TEmbedTable> })[]>\n >;\n }\n\n // ─── Modifiers ───────────────────────────────────────────────────────────\n\n /**\n * Order results by a column.\n * @param col Column name.\n * @param options ascending (default true), nullsFirst (default undefined), referencedTable.\n */\n order<K extends keyof TRow & string>(col: K, options?: OrderOptions): this {\n this._assertNotSearch('order');\n const asc = options?.ascending !== false;\n const dir = asc ? 'asc' : 'desc';\n const nulls = options?.nullsFirst === true\n ? '.nullsfirst'\n : options?.nullsFirst === false\n ? '.nullslast'\n : '';\n const prefix = options?.referencedTable ? `${options.referencedTable}.` : '';\n this._state.orderParts.push(`${prefix}${col}.${dir}${nulls}`);\n return this;\n }\n\n /**\n * Limit the number of rows returned.\n *\n * When `referencedTable` is set, emits `?<referencedTable>.limit=n` to limit\n * an embedded resource (PostgREST embedded-resource limiting). Without\n * `referencedTable`, emits the top-level `?limit=n`.\n */\n limit(n: number, options?: LimitOptions): this {\n this._assertNotSearch('limit');\n this._state.limitVal = n;\n if (options?.referencedTable) {\n this._state.limitReferencedTable = options.referencedTable;\n }\n return this;\n }\n\n /**\n * Return a range of rows by offset.\n * @param from Start offset (0-indexed).\n * @param to End offset (inclusive).\n */\n range(from: number, to: number): this {\n this._assertNotSearch('range');\n this._state.offsetVal = from;\n this._state.limitVal = to - from + 1;\n return this;\n }\n\n // ─── Writes ──────────────────────────────────────────────────────────────\n\n /**\n * Insert one or more rows.\n *\n * By default returns `data: null` (PostgREST `return=minimal`).\n * Chain `.select()` to get the inserted rows back (`return=representation`).\n *\n * When the Go emitter (`vantis data build`) has registered the table in\n * `SchemaInsert`, the payload enforces required NOT-NULL-no-default columns\n * at compile time. Tables not yet registered fall back to `Partial<Row>`.\n */\n insert(\n values: InsertPayload<TTableName> | InsertPayload<TTableName>[],\n ): QueryBuilder<TTableName, TRow, DataResult<null>> {\n this._assertNotSearch('insert');\n this._state.writeOp = 'insert';\n this._state.writeBody = values;\n return this as unknown as QueryBuilder<TTableName, TRow, DataResult<null>>;\n }\n\n /**\n * Update rows matching the current filters.\n *\n * By default returns `data: null`. Chain `.select()` for the updated rows.\n *\n * `values` is always `Partial<Row>` — you send only the columns to change.\n * For insert/upsert, see `.insert()` and `.upsert()` (strict SchemaInsert typing).\n */\n update(\n values: UpdatePayload<TTableName>,\n ): QueryBuilder<TTableName, TRow, DataResult<null>> {\n this._assertNotSearch('update');\n this._state.writeOp = 'update';\n this._state.writeBody = values;\n return this as unknown as QueryBuilder<TTableName, TRow, DataResult<null>>;\n }\n\n /**\n * Delete rows matching the current filters.\n *\n * By default returns `data: null`. Chain `.select()` for the deleted rows.\n */\n delete(): QueryBuilder<TTableName, TRow, DataResult<null>> {\n this._assertNotSearch('delete');\n this._state.writeOp = 'delete';\n this._state.writeBody = null;\n return this as unknown as QueryBuilder<TTableName, TRow, DataResult<null>>;\n }\n\n /**\n * Upsert one or more rows.\n *\n * Uses `POST + Prefer: resolution=merge-duplicates` (or `ignore-duplicates`).\n * By default returns `data: null`. Chain `.select()` for the upserted rows.\n */\n upsert(\n values: InsertPayload<TTableName> | InsertPayload<TTableName>[],\n options?: UpsertOptions,\n ): QueryBuilder<TTableName, TRow, DataResult<null>> {\n this._assertNotSearch('upsert');\n this._state.writeOp = 'upsert';\n this._state.writeBody = values;\n if (options?.onConflict !== undefined) this._state.onConflict = options.onConflict;\n if (options?.ignoreDuplicates === true) this._state.ignoreDuplicates = true;\n return this as unknown as QueryBuilder<TTableName, TRow, DataResult<null>>;\n }\n\n // ─── Result narrowers ────────────────────────────────────────────────────\n\n /**\n * Expect exactly one row. Returns `data: TRow` (not an array).\n * If zero or more than one row is returned, PostgREST returns a 406 and this\n * resolves to an error.\n *\n * Sets `Accept: application/vnd.pgrst.object+json`.\n */\n single(): QueryBuilder<TTableName, TRow, DataResult<TRow>> {\n this._assertNotSearch('single');\n this._state.singleMode = true;\n return this as unknown as QueryBuilder<TTableName, TRow, DataResult<TRow>>;\n }\n\n /**\n * Expect zero or one row. Returns `data: TRow | null`.\n *\n * Client-side normalization (mirrors postgrest-js):\n * - 0 rows → `{ data: null, error: null }` (no error)\n * - 1 row → `{ data: <row>, error: null }`\n * - >1 rows → `{ data: null, error: { code: 'PGRST116', ... } }`\n *\n * Uses the normal array `Accept: application/json` — PostgREST returns the\n * array, and normalization happens client-side. This avoids the 406 that\n * `vnd.pgrst.object+json` would return for 0 rows.\n */\n maybeSingle(): QueryBuilder<TTableName, TRow, DataResult<TRow | null>> {\n this._assertNotSearch('maybeSingle');\n this._state.maybeSingleMode = true;\n return this as unknown as QueryBuilder<\n TTableName,\n TRow,\n DataResult<TRow | null>\n >;\n }\n\n // ─── Full-text search ────────────────────────────────────────────────────\n\n /**\n * Full-text search via the engine-provisioned rank RPC.\n *\n * Routes to `GET /rpc/search_<table>?query=<text>` (the PostgREST `/rpc`\n * path, not the table path). Results are pre-sorted most-relevant-first by\n * the server (`ts_rank_cd` ordering inside the function). Each result row\n * carries a `rank` field — an OPAQUE ordering-only relevance score.\n *\n * The four swap invariants (VAN-830 / ADR-0044):\n * (i) One entry point — `.search(query, opts)`. Engine specifics never exposed.\n * (ii) Text query in — engine-specific parsing is internal.\n * (iii) Opaque `rank` — ordering-only, never normalized. A future BM25/rerank\n * swap changes the scale, not the contract.\n * (iv) Pluggable transport — currently PostgREST `/rpc`; semantic search goes\n * to the AI gateway (opt-in, reserved).\n *\n * Security (INV-01/INV-02):\n * - The query text is passed via URLSearchParams (URL-encoded by the browser\n * URLSearchParams API) — NOT double-quoted (no PostgREST scalar-op quoting\n * applies to RPC function arguments).\n * - The DATA_API_TOKEN is in the Authorization header ONLY, never in the URL.\n * - Filters/select/order from any previously chained methods are NOT emitted\n * in search mode — the RPC returns the full row shape + rank, ordered by\n * the server.\n *\n * Requires the table to have at least one `searchable: true` column in\n * `vantis.schema.json` and a `vantis data apply` that provisions the search function.\n *\n * @example\n * ```ts\n * const result = await data.from('articles').search('hello world');\n * if (result.error) { ... }\n * for (const row of result.data) {\n * console.log(row.title, row.rank); // rank: opaque ordering-only score\n * }\n * ```\n *\n * With a limit:\n * ```ts\n * const result = await data.from('articles').search('typescript', { limit: 10 });\n * ```\n */\n search(\n query: string,\n opts?: SearchOptions,\n ): QueryBuilder<TTableName, TRow, DataResult<Array<TRow & { rank: number }>>> {\n // v0 guard (C7 + S-search-scope): .search() must be the first and only call\n // on a bare data.from('<table>') builder. Combining with filters, embeds,\n // ordering, limit, writes, or result-narrowers (single/head/count/maybeSingle)\n // would silently discard that state — the /rpc route ignores table-level params\n // entirely and returns unscoped rows. Fail loud at the call site (a programming\n // error, not a runtime data error) so the developer gets a stack trace pointing\n // at the bad chain, not a deferred rejection with no context.\n const _s = this._state;\n if (\n _s.filters.length > 0 ||\n _s.embeds.length > 0 ||\n _s.orderParts.length > 0 ||\n _s.limitVal !== null ||\n _s.offsetVal !== null ||\n _s.selectCols !== '*' ||\n _s.writeOp !== null ||\n _s.singleMode ||\n _s.headMode ||\n _s.countMode !== null ||\n _s.maybeSingleMode\n ) {\n throw new Error(\n `@vantis/data: .search() must be called directly on data.from('${_s.table}') in v0 — ` +\n `it cannot be combined with .eq()/.select()/.order()/.embed()/.limit() or a write. ` +\n `Apply those before search is not yet supported; remove them or filter the returned results in code.`,\n );\n }\n this._state.searchQuery = query;\n this._state.searchLimit = opts?.limit ?? null;\n return this as unknown as QueryBuilder<\n TTableName,\n TRow,\n DataResult<Array<TRow & { rank: number }>>\n >;\n }\n\n // ─── Atomic verb modifier ─────────────────────────────────────────────────\n\n /**\n * Atomically modify a single row via the server-side verb RPC\n * (`__vantis_modify_<table>`). Each value in `ops` must be a VerbEnvelope\n * returned by one of the verb helpers (increment, decrement, multiply, divide,\n * min, max, toggle, serverTimestamp, concat, merge, jsonSet, setOnInsert).\n * Bare objects or plain values are rejected synchronously.\n *\n * Wire: `POST /rpc/__vantis_modify_<table>` `Content-Profile: api`\n * ```json\n * { \"__vantis_match\": {...}, \"__vantis_ops\": {...},\n * \"__vantis_row\"?: {...}, \"__vantis_guard\"?: {...} }\n * ```\n *\n * Returns:\n * - `{ data: [row], error: null }` — applied (UPDATE path).\n * - `{ data: [], error: null }` — guard miss or row absent (not-applied).\n * Check `result.data?.length === 0` in your application logic.\n * - `{ data: null, error }` — a DataError from PostgREST or the engine.\n *\n * Security: token in Authorization header only (INV-01/INV-02). Lazy env\n * read (INV-11). Browser guard (secondary; browser.ts export condition is\n * primary).\n *\n * @example\n * ```ts\n * const result = await data.from('orders').modify(\n * { total: increment(10) },\n * { id: orderId },\n * { guard: { total: { gte: 0 } } },\n * );\n * if (!result.error && result.data?.length === 0) {\n * // guard miss — row unchanged\n * }\n * ```\n */\n async modify(\n ops: VerbOpsFor<TTableName>,\n match: VerbMatchFor<TTableName>,\n opts?: ModifyOptions<TTableName>,\n ): Promise<DataResult<TRow[]>> {\n // Fail-loud guard: .modify() must be called on a bare data.from('<table>').\n // The /rpc route ignores all table-level query params (filters, order, limit,\n // select, embeds); silently discarding them would produce wrong results.\n const _s = this._state;\n if (\n _s.filters.length > 0 ||\n _s.embeds.length > 0 ||\n _s.orderParts.length > 0 ||\n _s.limitVal !== null ||\n _s.offsetVal !== null ||\n _s.selectCols !== '*' ||\n _s.writeOp !== null ||\n _s.singleMode ||\n _s.headMode ||\n _s.countMode !== null ||\n _s.maybeSingleMode ||\n _s.searchQuery !== null\n ) {\n throw new Error(\n `@vantis/data: .modify() must be called directly on data.from('${_s.table}') — ` +\n `it cannot be combined with filters, .select(), .order(), .embed(), ` +\n `.limit(), writes, or .search().`,\n );\n }\n\n // Validate ops: each value must be a Symbol-branded VerbEnvelope.\n //\n // Defense-in-depth: build serializedOps with Object.create(null) so that\n // customer-controlled column names (flowing from ops keys) become plain own\n // properties and cannot shadow Object.prototype methods (__proto__, constructor,\n // prototype). Spec fix #5 (spec.go Validate) rejects these names at source;\n // this is a defense-in-depth layer in case a name slips through. JSON.stringify\n // serializes an Object.create(null) object's own properties correctly.\n const serializedOps = Object.create(null) as Record<string, { op: string; v?: unknown; path?: string[] }>;\n for (const [col, envelope] of Object.entries(\n ops as Record<string, unknown>,\n )) {\n if (!isVerbEnvelope(envelope)) {\n throw new Error(\n `@vantis/data: .modify() ops['${col}'] is not a VerbEnvelope. ` +\n `Use a verb helper: increment(), decrement(), toggle(), serverTimestamp(), etc.`,\n );\n }\n const entry: { op: string; v?: unknown; path?: string[] } = { op: envelope.op };\n if (envelope.v !== undefined) entry.v = envelope.v;\n if (envelope.path !== undefined) entry.path = envelope.path;\n serializedOps[col] = entry;\n }\n\n // Build the RPC body — omit optional keys when absent.\n // Use Object.create(null) for the same prototype-pollution defense as serializedOps\n // (the body keys here are engine-controlled constants, but keeping the same\n // posture avoids any future exposure if body keys become customer-influenced).\n const body = Object.create(null) as Record<string, unknown>;\n body['__vantis_match'] = match;\n body['__vantis_ops'] = serializedOps;\n if (opts?.upsert !== undefined) body['__vantis_row'] = opts.upsert;\n if (opts?.guard !== undefined) body['__vantis_guard'] = opts.guard;\n\n // POST /rpc/__vantis_modify_<table> via the shared RPC transport (S3).\n // executeRpc owns the security posture: browser guard, lazy env read (INV-11),\n // token in Authorization header only (INV-01/INV-02), explicit timeout.\n const rpcName = `__vantis_modify_${_s.table}`;\n const rpcResult = await executeRpc(rpcName, body);\n\n // Transport or PostgREST errors → pass through immediately.\n if (rpcResult.error) {\n return {\n data: null,\n error: rpcResult.error,\n count: null,\n status: rpcResult.status,\n statusText: rpcResult.statusText,\n };\n }\n\n // Adapt executeRpc's result to .modify() semantics (S4):\n // - data !== null && Array.isArray → valid JSON array ([] or [{...}]); return as TRow[].\n // An empty array [] IS the documented not-applied signal (guard miss or row absent).\n // - data !== null && !Array.isArray → non-array body (e.g. {} or \"ok\"): protocol\n // violation for SETOF api.<t>. Return INVALID_RESPONSE.\n // - data === null (any status, incl. 204) → unreadable/missing body.\n // __vantis_modify_<t> is SETOF api.<t> → PostgREST MUST return a JSON\n // array on success; null (incl. 204) is a protocol violation (B4, VAN-866).\n // Return INVALID_RESPONSE rather than masking as not-applied [].\n if (rpcResult.data !== null) {\n if (!Array.isArray(rpcResult.data)) {\n // Non-null, non-array 2xx body: protocol violation for SETOF api.<t>.\n return {\n data: null,\n error: makeConfigError(\n 'INVALID_RESPONSE',\n `__vantis_modify_${_s.table} returned a non-array response body. ` +\n `Expected a JSON array (SETOF api.${_s.table}).`,\n ),\n count: null,\n status: rpcResult.status,\n statusText: rpcResult.statusText,\n };\n }\n return {\n data: rpcResult.data as TRow[],\n error: null,\n count: null,\n status: rpcResult.status,\n statusText: rpcResult.statusText,\n };\n }\n // data null → unreadable response (parse failure / empty body / unexpected 204).\n return {\n data: null,\n error: makeConfigError(\n 'INVALID_RESPONSE',\n `__vantis_modify_${_s.table} returned an unreadable response. ` +\n `Expected a JSON array (SETOF api.${_s.table}).`,\n ),\n count: null,\n status: rpcResult.status,\n statusText: rpcResult.statusText,\n };\n }\n\n // ─── Private helpers ─────────────────────────────────────────────────────\n\n /**\n * Guard: throws synchronously if `.search()` has already been called on this\n * builder. `.search()` is terminal in v0 — no mutator may be chained after it.\n *\n * Without this guard, a trailing mutator (e.g. `.eq()`) would modify `_state`\n * fields that `_buildRequest`'s search branch silently ignores, producing an\n * unscoped query that returns rows the caller did not intend to receive.\n *\n * Called at the top of every state-mutating method.\n */\n private _assertNotSearch(method: string): void {\n if (this._state.searchQuery !== null) {\n throw new Error(\n `@vantis/data: .search() is terminal — ${method}() cannot be chained after .search() on '${this._state.table}'. ` +\n `Build filters/projection before .search() is not supported in v0.`,\n );\n }\n }\n\n // ─── Thenable ────────────────────────────────────────────────────────────\n\n then<TFulfilled = TResult, TRejected = never>(\n onfulfilled?: ((value: TResult) => TFulfilled | PromiseLike<TFulfilled>) | null | undefined,\n onrejected?: ((reason: unknown) => TRejected | PromiseLike<TRejected>) | null | undefined,\n ): Promise<TFulfilled | TRejected> {\n return this._execute().then(onfulfilled, onrejected);\n }\n\n // ─── Execution ───────────────────────────────────────────────────────────\n\n private async _execute(): Promise<TResult> {\n const env = resolveDataApiEnv();\n if ('configError' in env) {\n return { data: null, error: env.configError, count: null, status: 0, statusText: '' } as TResult;\n }\n const apiUrl = env.apiUrl;\n\n // ── Build the request ─────────────────────────────────────────────────\n const { url, method, headers, body } = this._buildRequest(apiUrl, env.apiToken);\n\n // ── Timeout ───────────────────────────────────────────────────────────\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);\n\n try {\n const res = await fetch(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body, bigintReplacer) : undefined,\n signal: controller.signal,\n });\n\n return await this._parseResponse(res) as TResult;\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n const dataErr = makeConfigError(\n 'TIMEOUT',\n `Data API request timed out after ${DEFAULT_TIMEOUT_MS}ms.`,\n );\n return { data: null, error: dataErr, count: null, status: 0, statusText: 'Timeout' } as TResult;\n }\n // Fix 4 (INV-02): do NOT echo err.message — it may contain request\n // context that includes the Authorization header. Use a generic message\n // and only include the error name (e.g. \"TypeError\"), never the full text.\n const errName = err instanceof Error ? err.name : 'UnknownError';\n const dataErr = makeConfigError(\n 'NETWORK_ERROR',\n `Data API request failed (${errName}). Check network connectivity and DATA_API_URL.`,\n );\n return { data: null, error: dataErr, count: null, status: 0, statusText: 'Network Error' } as TResult;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private _buildRequest(baseUrl: string, apiToken: string): {\n url: string;\n method: string;\n headers: Record<string, string>;\n body: unknown;\n } {\n const s = this._state;\n const isWrite = s.writeOp !== null;\n const isDelete = s.writeOp === 'delete';\n const isUpsert = s.writeOp === 'upsert';\n\n // ── Method ────────────────────────────────────────────────────────────\n let method: string;\n if (!isWrite) {\n method = s.headMode ? 'HEAD' : 'GET';\n } else {\n switch (s.writeOp) {\n case 'insert': method = 'POST'; break;\n case 'upsert': method = 'POST'; break;\n case 'update': method = 'PATCH'; break;\n case 'delete': method = 'DELETE'; break;\n default: method = 'GET';\n }\n }\n\n // ── Headers ───────────────────────────────────────────────────────────\n // NOTE: the Authorization header carries the token. It is NEVER logged\n // or placed in the URL. (INV-01/INV-02)\n const headers: Record<string, string> = {\n // token is in Authorization header ONLY — never in URL, never in errors.\n 'Authorization': `Bearer ${apiToken}`,\n };\n\n if (!isWrite) {\n // Reads: select the `api` schema via Accept-Profile.\n headers['Accept-Profile'] = 'api';\n headers['Accept'] = 'application/json';\n } else {\n // Writes: select the `api` schema via Content-Profile.\n headers['Content-Profile'] = 'api';\n if (!isDelete) {\n headers['Content-Type'] = 'application/json';\n }\n }\n\n // ── Prefer header ────────────────────────────────────────────────────\n const preferParts: string[] = [];\n\n if (isWrite) {\n if (s.writeSelect) {\n preferParts.push('return=representation');\n } else {\n preferParts.push('return=minimal');\n }\n }\n\n if (isUpsert) {\n if (s.ignoreDuplicates) {\n preferParts.push('resolution=ignore-duplicates');\n } else {\n preferParts.push('resolution=merge-duplicates');\n }\n }\n\n if (!isWrite && s.countMode !== null) {\n preferParts.push(`count=${s.countMode}`);\n }\n\n if (preferParts.length > 0) {\n headers['Prefer'] = preferParts.join(',');\n }\n\n // ── single() ──────────────────────────────────────────────────────────\n // PostgREST returns 406 on 0 or >1 rows — correct for .single().\n // maybeSingle() does NOT set this header — it uses normal array response\n // and normalises client-side.\n if (s.singleMode) {\n headers['Accept'] = 'application/vnd.pgrst.object+json';\n }\n\n // ── URL + query params ────────────────────────────────────────────────\n\n // Search mode: route to /rpc/search_<table>.\n // Only `query` (and optionally `limit`) are emitted — no table\n // filters/select/order/limit (the RPC pre-sorts + returns the full row\n // shape + rank). Headers are already set correctly for reads above\n // (Accept-Profile: api, Accept: application/json). Token is in\n // Authorization header only (never URL — INV-01/INV-02).\n //\n // Belt-and-braces: the .search() call-site guard ensures headMode=false and\n // writeOp=null before we reach this branch, so `method` is already 'GET' and\n // `headers` carries only read headers (no Prefer/Content-*/write headers).\n // We emit 'GET' explicitly here to make the invariant machine-readable and\n // to ensure no write method/header propagates if the guard were ever bypassed.\n if (s.searchQuery !== null) {\n const rpcUrl = `${baseUrl.replace(/\\/$/, '')}/rpc/search_${encodeURIComponent(s.table)}`;\n const rpcParams = new URLSearchParams();\n // Pass the query as a URLSearchParams entry — URL-encoded by the\n // URLSearchParams API. No PostgREST scalar-operator quoting applies here\n // (this is a function argument, not a filter operator value).\n rpcParams.set('query', s.searchQuery);\n if (s.searchLimit !== null) {\n rpcParams.set('limit', String(s.searchLimit));\n }\n const rpcQs = rpcParams.toString();\n return {\n url: rpcQs ? `${rpcUrl}?${rpcQs}` : rpcUrl,\n method: 'GET', // invariant: .search() guard ensures headMode=false + writeOp=null\n headers, // invariant: read-only headers only (Accept-Profile:api, Authorization)\n body: undefined,\n };\n }\n\n // Fix 5: encode the table name as a URL path segment.\n const tableUrl = `${baseUrl.replace(/\\/$/, '')}/${encodeURIComponent(s.table)}`;\n const params = new URLSearchParams();\n\n // Filters\n for (const f of s.filters) {\n const eqIdx = f.indexOf('=');\n if (eqIdx !== -1) {\n params.append(f.slice(0, eqIdx), f.slice(eqIdx + 1));\n }\n }\n\n // Select columns + FK embeds.\n // Fix 3: write-chained .select(columns) emits the exact columns string\n // (not forced '*') so callers can narrow the representation response.\n let selectStr: string;\n if (isWrite && s.writeSelect) {\n selectStr = s.writeSelectCols;\n } else {\n selectStr = s.selectCols;\n }\n if (s.embeds.length > 0) {\n const embedParts = s.embeds.map((e) => `${e}(*)`).join(',');\n selectStr = `${selectStr},${embedParts}`;\n }\n // Only emit select when it carries meaningful info (non-default or embeds).\n if (selectStr !== '*' || s.embeds.length > 0) {\n params.set('select', selectStr);\n }\n\n // Order\n if (s.orderParts.length > 0) {\n params.set('order', s.orderParts.join(','));\n }\n\n // Limit + offset\n // When limitReferencedTable is set, emit <table>.limit=n for embedded-resource limiting.\n if (s.limitVal !== null) {\n const limitKey = s.limitReferencedTable ? `${s.limitReferencedTable}.limit` : 'limit';\n params.set(limitKey, String(s.limitVal));\n }\n if (s.offsetVal !== null) params.set('offset', String(s.offsetVal));\n\n // Upsert on_conflict column\n if (isUpsert && s.onConflict !== null) {\n params.set('on_conflict', s.onConflict);\n }\n\n const qs = params.toString();\n const url = qs ? `${tableUrl}?${qs}` : tableUrl;\n\n const body = !isWrite || isDelete ? undefined : s.writeBody;\n\n return { url, method, headers, body };\n }\n\n private async _parseResponse(res: Response): Promise<DataResult<unknown>> {\n const s = this._state;\n\n if (!res.ok) {\n const error = await parsePostgRESTError(res);\n return { data: null, error, count: null, status: res.status, statusText: res.statusText };\n }\n\n // ── Count parsing ─────────────────────────────────────────────────────\n let count: number | null = null;\n if (s.countMode !== null) {\n const contentRange = res.headers.get('Content-Range');\n if (contentRange) {\n // Format: start-end/total (or start-end/* if unknown).\n const slash = contentRange.indexOf('/');\n if (slash !== -1) {\n const totalStr = contentRange.slice(slash + 1);\n if (totalStr !== '*') {\n const parsed = parseInt(totalStr, 10);\n if (!isNaN(parsed)) count = parsed;\n }\n }\n }\n }\n\n // ── Body parsing ──────────────────────────────────────────────────────\n let data: unknown = null;\n\n // HEAD requests and return=minimal writes have no body.\n const isMinimalWrite = s.writeOp !== null && !s.writeSelect;\n if (!s.headMode && !isMinimalWrite) {\n const text = await res.text();\n if (text) {\n try {\n data = JSON.parse(text) as unknown;\n } catch {\n // Non-JSON body — leave data as null.\n }\n }\n }\n\n // ── Result shaping ────────────────────────────────────────────────────\n\n // Writes without .select() → data: null (return=minimal).\n if (isMinimalWrite) {\n return { data: null, error: null, count, status: res.status, statusText: res.statusText };\n }\n\n // HEAD → data: null (no body in HEAD response).\n if (s.headMode) {\n return { data: null, error: null, count, status: res.status, statusText: res.statusText };\n }\n\n // Fix 1: maybeSingle — client-side normalization of the array response.\n // PostgREST returns a normal JSON array. We normalise here:\n // 0 rows → data: null, error: null\n // 1 row → data: <row>, error: null\n // >1 rows → data: null, error: PGRST116 (too many rows)\n if (s.maybeSingleMode) {\n if (!Array.isArray(data)) {\n // Unexpected non-array — treat as null.\n return { data: null, error: null, count, status: res.status, statusText: res.statusText };\n }\n if (data.length === 0) {\n return { data: null, error: null, count, status: res.status, statusText: res.statusText };\n }\n if (data.length > 1) {\n const err: DataError = {\n kind: 'postgrest',\n code: 'PGRST116',\n message: 'The result contains 0 or more than 1 rows.',\n details: null,\n hint: null,\n };\n return { data: null, error: err, count: null, status: res.status, statusText: res.statusText };\n }\n // Exactly 1 row.\n return { data: data[0] as unknown, error: null, count, status: res.status, statusText: res.statusText };\n }\n\n // single() — PostgREST already returned a single JSON object (not array)\n // because we set Accept: application/vnd.pgrst.object+json.\n if (s.singleMode) {\n return { data, error: null, count, status: res.status, statusText: res.statusText };\n }\n\n // Array responses (normal select).\n return { data, error: null, count, status: res.status, statusText: res.statusText };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Typed RPC execution (VAN-832)\n// ---------------------------------------------------------------------------\n\n/**\n * Execute a typed RPC: POST /rpc/<name> with the args as the JSON body.\n *\n * Mirrors QueryBuilder._execute's security posture:\n * - Browser guard (server-only — DATA_API_TOKEN must never reach a browser).\n * - Lazy env read on call (INV-11).\n * - Token in the Authorization header ONLY — never in the URL, never logged,\n * never in an error message (INV-01/INV-02).\n * - Explicit timeout (CONVENTIONS.md).\n * - RPC-aware parse: 204 / empty body → null; otherwise JSON (scalar/object/array).\n */\nasync function executeRpc(name: string, args: unknown): Promise<DataResult<unknown>> {\n const env = resolveDataApiEnv();\n if ('configError' in env) {\n return { data: null, error: env.configError, count: null, status: 0, statusText: '' };\n }\n const apiUrl = env.apiUrl;\n const apiToken = env.apiToken;\n\n // POST /rpc/<name> — token in Authorization header only (never URL — INV-01/INV-02).\n const url = `${apiUrl.replace(/\\/$/, '')}/rpc/${encodeURIComponent(name)}`;\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${apiToken}`,\n 'Content-Profile': 'api',\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n };\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), DEFAULT_RPC_TIMEOUT_MS);\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(args ?? {}, bigintReplacer),\n signal: controller.signal,\n });\n if (!res.ok) {\n const error = await parsePostgRESTError(res);\n return { data: null, error, count: null, status: res.status, statusText: res.statusText };\n }\n // RPC-aware parse: 204 No Content (void) / empty body → null; else JSON.\n let data: unknown = null;\n if (res.status !== 204) {\n const text = await res.text();\n if (text) {\n try {\n data = JSON.parse(text) as unknown;\n } catch {\n data = null;\n }\n }\n }\n return { data, error: null, count: null, status: res.status, statusText: res.statusText };\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n return {\n data: null,\n error: makeConfigError('TIMEOUT', `Data API request timed out after ${DEFAULT_RPC_TIMEOUT_MS}ms.`),\n count: null, status: 0, statusText: 'Timeout',\n };\n }\n // INV-02: never echo err.message (may carry the Authorization header).\n const errName = err instanceof Error ? err.name : 'UnknownError';\n return {\n data: null,\n error: makeConfigError('NETWORK_ERROR',\n `Data API request failed (${errName}). Check network connectivity and DATA_API_URL.`),\n count: null, status: 0, statusText: 'Network Error',\n };\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** The typed shape of data.run — one method per Rpc registry entry. */\ntype RunProxy = {\n [K in keyof Rpc]: (args: Rpc[K]['Args']) => Promise<DataResult<Rpc[K]['Returns']>>;\n};\n\n// ---------------------------------------------------------------------------\n// The ambient `data` export\n// ---------------------------------------------------------------------------\n\n/**\n * The typed data client. Import and use directly — no construction required.\n *\n * Reads `DATA_API_URL` and `DATA_API_TOKEN` from the environment on the first\n * query (lazy — so framework builds that import server modules before env\n * injection do not throw at import time).\n *\n * @example\n * ```ts\n * import { data } from '@vantis/data';\n *\n * const result = await data.from('users').eq('email', 'alice@example.com').single();\n * if (result.error) { ... }\n * const user = result.data; // typed as Schema['users']\n * ```\n */\nexport const data = {\n /**\n * Start a query against the given table.\n *\n * `tableName` is typed as `keyof Schema` — the augmented Schema interface\n * populated by `vantis data build`.\n *\n * Returns a `QueryBuilder` that is also PromiseLike — `await` it directly.\n */\n from<T extends keyof Schema>(tableName: T): QueryBuilder<T> {\n return new QueryBuilder<T>(tableName);\n },\n\n /**\n * Call a typed RPC (VAN-832). `data.run.<name>(args)` POSTs to /rpc/<name>\n * and returns the typed result. Names + arg/return types come from the\n * generated Rpc registry (`vantis data build`).\n *\n * @example\n * ```ts\n * const r = await data.run.place_order({ product_id: id, qty: 2 });\n * if (r.error) { ... }\n * const orderId = r.data; // typed from the fn() declaration\n * ```\n */\n run: new Proxy({} as RunProxy, {\n get(_target, prop) {\n if (typeof prop !== 'string') return undefined;\n // Never expose Promise/thenable probes as RPCs: otherwise `await data.run`\n // (a mistake) would fire POST /rpc/then. These names are reserved at\n // validation (spec.Validate + fn() reject them as function names).\n if (prop === 'then' || prop === 'catch' || prop === 'finally') return undefined;\n return (args: unknown) => executeRpc(prop, args);\n },\n }) as RunProxy,\n} as const;\n"],"mappings":"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","names":["dataErr","data"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@vantis/data",
3
+ "version": "0.0.1",
4
+ "description": "Vantis Data SDK — typed, server-only runtime client for the per-block Data API",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=20.17.0"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Vantis-Systems/platform.git",
16
+ "directory": "packages/data"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "vantis.schema.schema.json"
21
+ ],
22
+ "sideEffects": [
23
+ "./dist/browser.mjs",
24
+ "./dist/browser.cjs"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "browser": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/browser.mjs"
31
+ },
32
+ "import": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.mjs"
35
+ },
36
+ "require": {
37
+ "types": "./dist/index.d.cts",
38
+ "default": "./dist/index.cjs"
39
+ }
40
+ }
41
+ },
42
+ "main": "./dist/index.cjs",
43
+ "module": "./dist/index.mjs",
44
+ "types": "./dist/index.d.ts",
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "typecheck": "tsc --noEmit && tsc --project tsconfig.typetest.json",
48
+ "test": "vitest run --passWithNoTests",
49
+ "test:pack": "node scripts/pack-smoke.mjs"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^22",
53
+ "tsup": "^8",
54
+ "typescript": "^5.8",
55
+ "vitest": "^2"
56
+ }
57
+ }