@routier/core 0.7.0 → 0.8.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.
Files changed (47) hide show
  1. package/dist/codegen/blocks.d.ts +17 -1
  2. package/dist/codegen/handlers/types.d.ts +13 -5
  3. package/dist/codegen/index.cjs +28 -0
  4. package/dist/codegen/index.cjs.map +1 -1
  5. package/dist/codegen/index.js +28 -0
  6. package/dist/codegen/index.js.map +1 -1
  7. package/dist/collections/MemoryDataCollection.d.ts +2 -4
  8. package/dist/collections/index.cjs +127 -15
  9. package/dist/collections/index.cjs.map +1 -1
  10. package/dist/collections/index.js +127 -15
  11. package/dist/collections/index.js.map +1 -1
  12. package/dist/expressions/index.cjs +226 -4
  13. package/dist/expressions/index.cjs.map +1 -1
  14. package/dist/expressions/index.js +228 -5
  15. package/dist/expressions/index.js.map +1 -1
  16. package/dist/expressions/parser.d.ts +42 -1
  17. package/dist/index.cjs +753 -345
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.js +756 -345
  20. package/dist/index.js.map +1 -1
  21. package/dist/plugins/EphemeralDataPlugin.d.ts +8 -0
  22. package/dist/plugins/index.cjs +566 -49
  23. package/dist/plugins/index.cjs.map +1 -1
  24. package/dist/plugins/index.js +566 -48
  25. package/dist/plugins/index.js.map +1 -1
  26. package/dist/plugins/query/QueryOptionsCollection.d.ts +15 -5
  27. package/dist/plugins/query/index.d.ts +1 -0
  28. package/dist/plugins/query/renames.d.ts +27 -0
  29. package/dist/plugins/query/types.d.ts +15 -1
  30. package/dist/plugins/translators/SqlTranslator.d.ts +15 -0
  31. package/dist/schema/SchemaDefinition.d.ts +8 -0
  32. package/dist/schema/changeTracker.d.ts +10 -0
  33. package/dist/schema/index.cjs +291 -274
  34. package/dist/schema/index.cjs.map +1 -1
  35. package/dist/schema/index.d.ts +1 -0
  36. package/dist/schema/index.js +294 -276
  37. package/dist/schema/index.js.map +1 -1
  38. package/dist/schema/types.d.ts +8 -7
  39. package/dist/schema/utils/storageDates.d.ts +25 -0
  40. package/dist/transfer/index.cjs.map +1 -1
  41. package/dist/transfer/index.js.map +1 -1
  42. package/dist/utilities/index.cjs +74 -29
  43. package/dist/utilities/index.cjs.map +1 -1
  44. package/dist/utilities/index.js +74 -29
  45. package/dist/utilities/index.js.map +1 -1
  46. package/package.json +2 -2
  47. package/dist/codegen/utils.d.ts +0 -22
@@ -1 +1 @@
1
- {"version":3,"file":"transfer/index.js","sources":["webpack://@routier/core/webpack/runtime/define_property_getters","webpack://@routier/core/webpack/runtime/has_own_property","webpack://@routier/core/./src/transfer/types.ts","webpack://@routier/core/./src/transfer/fillers.ts","webpack://@routier/core/./src/transfer/ChunkEncoder.ts","webpack://@routier/core/./src/transfer/decoder.ts","webpack://@routier/core/./src/schema/types.ts","webpack://@routier/core/./src/transfer/plan.ts","webpack://@routier/core/./src/transfer/index.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","/**\n * The wire format for query results crossing an in-process worker boundary.\n *\n * `postMessage` with no transfer list structured-clones everything, and a clone of a large row\n * array is paid twice — once serialising in the worker, once deserialising on the main thread,\n * where it blocks. Columnar typed arrays are transferred instead: the buffer changes owner and\n * nothing is copied.\n *\n * Nothing here knows about SQL, schemas, or workers. A column's values go in and\n * `{ payload, transferables }` comes out; the caller owns the transport. A transport with no\n * transfer list may ignore the array — the payload then clones correctly on its own.\n */\n\n/**\n * The COMPLETE layout: chunk size, bitmap semantics, JSON joining, and the framing above.\n *\n * Both sides reject a version they do not know rather than guessing. The worker ships as its own\n * bundle, so the two halves can be built from different sources.\n */\nexport const TRANSFER_VERSION = 1;\n\n/**\n * Rows per chunk.\n *\n * Chunking is what lets the main thread decode chunk *k* while the worker fills *k+1*, so the\n * first rows arrive in about 2ms at any result size instead of after the whole clone. Measured\n * best at 4,096: 8,192 is within noise, 25,000 is measurably worse.\n */\nexport const CHUNK_ROWS = 4096;\n\n/**\n * How one result column crosses the boundary.\n *\n * Each encoding names a VALUE shape, never an engine. An engine that hands back a date as ISO\n * text and one that hands back a `Date` both use `date-f64`; the filler accepts either. What an\n * engine cannot produce, it simply does not choose.\n */\nexport type TransferEncoding =\n /** `Float64Array` + null bitmap, transferred. */\n | 'float64'\n /**\n * Epoch ms in a `Float64Array` + null bitmap, transferred.\n *\n * Accepts a `Date`, an epoch number, or a parseable date string — whichever the engine\n * returns. Decode emits a `Date`.\n */\n | 'date-f64'\n /** `Uint8Array` of 0/1 + null bitmap, transferred; accepts 0/1 or a boolean; decode emits `true`/`false`. */\n | 'boolean-byte'\n /**\n * Values that are ALREADY JSON text, joined into ONE document per chunk; decode parses once.\n *\n * For an engine that stores a nested structure as text and returns it that way.\n */\n | 'json'\n /**\n * Values that are live objects, `JSON.stringify`d as they are collected, then joined and\n * parsed like `json`.\n *\n * For an engine that returns a nested structure already parsed — a document store, or a\n * driver with a JSON type parser. Crosses the wire as a `json` column, so it needs no\n * separate decoder.\n *\n * Whether this beats `clone` for a given payload is NOT measured. What was measured is that\n * once a value IS text, crossing it as text and parsing once per chunk beats parsing in the\n * worker and cloning the tree (25.3ms against 38.5ms at 20,000 rows). Deep, repetitive\n * structures are the promising case; a small flat object is likely a wash.\n */\n | 'json-stringify'\n /** Plain array of raw values, structured-cloned as they are. */\n | 'clone';\n\nexport type TransferColumn = {\n /** Exact name the engine returns, including projection and join aliases. */\n readonly name: string;\n readonly encoding: TransferEncoding;\n};\n\nexport type TransferPlan = {\n readonly version: typeof TRANSFER_VERSION;\n readonly columns: readonly TransferColumn[];\n};\n\nexport type EncodedColumn =\n | { readonly encoding: 'float64'; readonly data: Float64Array; readonly nulls: Uint8Array }\n | { readonly encoding: 'date-f64'; readonly data: Float64Array; readonly nulls: Uint8Array }\n | { readonly encoding: 'boolean-byte'; readonly data: Uint8Array; readonly nulls: Uint8Array }\n /** `'[' + rowTexts.join(',') + ']'`; a null row contributes the text `null`. */\n | { readonly encoding: 'json'; readonly doc: string }\n | { readonly encoding: 'clone'; readonly data: readonly unknown[] };\n\nexport type EncodedChunk = {\n readonly version: typeof TRANSFER_VERSION;\n /**\n * Rows in THIS chunk. The final chunk may be short, and is only 0 for the single chunk of a\n * zero-row result.\n */\n readonly rowCount: number;\n /** Keyed by column name. Exactly one entry per plan column. */\n readonly columns: Record<string, EncodedColumn>;\n};\n\n/**\n * One chunk and the buffers its transport may hand over rather than copy.\n *\n * The list is separate from the payload on purpose — it is an instruction to the transport, not\n * data, and embedding it would make the payload describe its own framing.\n */\nexport type EncodedTransfer = {\n readonly payload: EncodedChunk;\n readonly transferables: readonly ArrayBufferLike[];\n};\n\n/** Bytes of null bitmap for `rowCount` rows: one bit per row, LSB-first. */\nexport const nullByteLength = (rowCount: number): number => (rowCount + 7) >> 3;\n\n/**\n * Rejects a version this build cannot read.\n *\n * Never attempt to decode an unknown version. Every field's meaning is version-scoped, so a\n * layout that merely looks close would be read wrongly and silently.\n */\nexport const assertTransferVersion = (version: number): void => {\n if (version !== TRANSFER_VERSION) {\n throw new Error(`transfer codec version ${version} is not supported`);\n }\n};\n\n/**\n * Rejects a plan that does not describe the result the engine is about to produce.\n *\n * OPTIONAL, for an engine that can report the fields of a result before yielding rows. Encoding\n * against a wrong layout would put each field's values under another field's name and report\n * nothing, so a caller that CAN check should. An engine whose records are heterogeneous has\n * nothing to check against and skips it — the plan decides the result shape there.\n *\n * Checked once per result, never per row.\n */\nexport const assertColumnLayout = (plan: TransferPlan, names: readonly string[]): void => {\n const planned = plan.columns.map(column => column.name);\n\n const matches = planned.length === names.length\n && planned.every((name, index) => name === names[index]);\n\n if (matches === false) {\n throw new Error(\n `The transfer plan does not match the result columns. ` +\n `Plan: [${planned.join(', ')}]. Result: [${names.join(', ')}].`\n );\n }\n};\n","import { CHUNK_ROWS, EncodedColumn, nullByteLength, TransferEncoding } from './types';\n\n/**\n * One column being filled, one strategy per encoding.\n *\n * `set` answers `false` rather than coercing a value that does not belong in its encoding. A\n * schema type does not prove what an engine will actually return — custom serializers,\n * migrations and external writers can put anything in a field — so the encoder validates every\n * value it writes instead of trusting the plan. The caller turns a refusal into a fallback to\n * `clone`.\n */\nexport interface ColumnFiller {\n readonly encoding: TransferEncoding;\n set(index: number, value: unknown): boolean;\n /** Rows `0..count` as raw values, which is what a fallback to `clone` has to carry forward. */\n drain(count: number): unknown[];\n emit(rowCount: number, transferables: ArrayBufferLike[]): EncodedColumn;\n /** Readies the filler for the next chunk. Transfer detaches the buffers, so they are replaced. */\n reset(): void;\n}\n\nconst NULL_BYTES = nullByteLength(CHUNK_ROWS);\n\n/**\n * Epoch milliseconds for any shape an engine returns a date in, or `null` for anything else.\n *\n * An invalid `Date` and an unparseable string both answer `null` rather than writing `NaN`: a\n * date that cannot be represented is a value this encoding has no answer for, and the column\n * falls back so the raw value survives.\n */\nconst toEpoch = (value: unknown): number | null => {\n if (value instanceof Date) {\n const epoch = value.getTime();\n\n return Number.isNaN(epoch) ? null : epoch;\n }\n\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value : null;\n }\n\n if (typeof value !== 'string') {\n return null;\n }\n\n const parsed = Date.parse(value);\n\n return Number.isNaN(parsed) ? null : parsed;\n};\n\nabstract class TypedFiller<TData extends Float64Array | Uint8Array> implements ColumnFiller {\n\n abstract readonly encoding: TransferEncoding;\n\n protected data: TData;\n protected nulls: Uint8Array;\n\n constructor() {\n this.data = this.allocate();\n this.nulls = new Uint8Array(NULL_BYTES);\n }\n\n protected abstract allocate(): TData;\n\n /** Writes a non-null value, or answers false to send the column to `clone`. */\n protected abstract write(index: number, value: unknown): boolean;\n\n /** The value a `clone` fallback should carry for an already-written row. */\n protected abstract raw(index: number): unknown;\n\n set(index: number, value: unknown): boolean {\n if (value == null) {\n this.nulls[index >> 3] |= 1 << (index & 7);\n this.data[index] = 0;\n return true;\n }\n\n return this.write(index, value);\n }\n\n drain(count: number): unknown[] {\n const values: unknown[] = new Array(count);\n\n for (let i = 0; i < count; i++) {\n const isNull = (this.nulls[i >> 3] & (1 << (i & 7))) !== 0;\n\n values[i] = isNull ? null : this.raw(i);\n }\n\n return values;\n }\n\n emit(rowCount: number, transferables: ArrayBufferLike[]): EncodedColumn {\n // `subarray` would not do: transfer moves the whole underlying buffer, so a short final\n // chunk has to be copied to its exact size. A full chunk is already exact.\n const data = (rowCount === CHUNK_ROWS ? this.data : this.data.slice(0, rowCount)) as TData;\n const nulls = rowCount === CHUNK_ROWS ? this.nulls : this.nulls.slice(0, nullByteLength(rowCount));\n\n transferables.push(data.buffer, nulls.buffer);\n\n return { encoding: this.encoding, data, nulls } as EncodedColumn;\n }\n\n reset(): void {\n this.data = this.allocate();\n this.nulls = new Uint8Array(NULL_BYTES);\n }\n}\n\n/**\n * Numbers, including `NaN` and the infinities — those are legitimate JS numbers and pass through.\n *\n * No `BigInt64Array`. Routier only ever writes JS numbers, so `Float64Array` round-trips\n * everything it stored; a `bigint` or a wide integer type can only come from something else, and\n * that column falls back rather than losing precision silently.\n */\nclass Float64Filler extends TypedFiller<Float64Array> {\n\n readonly encoding = 'float64' as const;\n\n protected allocate(): Float64Array {\n return new Float64Array(CHUNK_ROWS);\n }\n\n protected write(index: number, value: unknown): boolean {\n if (typeof value !== 'number') {\n return false;\n }\n\n this.data[index] = value;\n\n return true;\n }\n\n protected raw(index: number): unknown {\n return this.data[index];\n }\n}\n\n/**\n * Epoch milliseconds, whatever shape the engine returned the date in.\n *\n * All three shapes are accepted because engines genuinely differ: one that stores a date as text\n * returns a string, one that parses before returning gives a `Date`, and one that stores a\n * timestamp gives a number. Requiring text would silently drop the whole encoding for every\n * engine of the other two kinds — they would fall back to `clone` on every row.\n *\n * The final entity shape needs a `Date` either way, so converting here means the wire carries\n * eight bytes and the main thread builds the `Date` from them.\n */\nclass DateFiller extends TypedFiller<Float64Array> {\n\n readonly encoding = 'date-f64' as const;\n\n protected allocate(): Float64Array {\n return new Float64Array(CHUNK_ROWS);\n }\n\n protected write(index: number, value: unknown): boolean {\n const epoch = toEpoch(value);\n\n if (epoch == null) {\n return false;\n }\n\n this.data[index] = epoch;\n\n return true;\n }\n\n /**\n * A `Date`, not the epoch number.\n *\n * A fallback column is decoded raw, and the rows already written can no longer produce their\n * ISO text. A `Date` is what the decoder would have emitted for them, and it is also what\n * the existing date deserializer passes through untouched — an epoch number would reach the\n * entity as a number.\n */\n protected raw(index: number): unknown {\n return new Date(this.data[index]);\n }\n}\n\n/**\n * 0/1 bytes.\n *\n * Both shapes are accepted: an engine with no boolean type returns 0 or 1, one with a boolean\n * type returns a boolean. Requiring either would drop the encoding entirely for engines of the\n * other kind. Both decode to `true`/`false`, so the result is identical.\n */\nclass BooleanFiller extends TypedFiller<Uint8Array> {\n\n readonly encoding = 'boolean-byte' as const;\n\n protected allocate(): Uint8Array {\n return new Uint8Array(CHUNK_ROWS);\n }\n\n protected write(index: number, value: unknown): boolean {\n if (value === 0 || value === 1) {\n this.data[index] = value;\n return true;\n }\n\n if (typeof value !== 'boolean') {\n return false;\n }\n\n this.data[index] = value ? 1 : 0;\n\n return true;\n }\n\n protected raw(index: number): unknown {\n return this.data[index];\n }\n}\n\n/**\n * JSON text, joined into one document per chunk so the main thread parses once per column\n * instead of once per row.\n *\n * The join is valid because each element is a complete JSON document and `null` is valid JSON.\n * The text is NOT validated here; that would be the second parse this exists to avoid. Text that\n * is not JSON poisons the chunk's document and the decoder reports it (see `decodeChunk`).\n */\nclass JsonFiller implements ColumnFiller {\n\n readonly encoding = 'json' as const;\n\n private texts: string[] = [];\n\n /**\n * Which rows were null, so a fallback to `clone` can carry `null` rather than the text\n * `'null'` the document holds. Sparse — only null rows are written.\n */\n private nulls: boolean[] = [];\n\n /**\n * @param toText The JSON text for one non-null value, or `null` to send the column to\n * `clone`. This is the whole difference between an engine that returns text and one that\n * returns a live object.\n * @param fromText Recovers the value the engine originally gave, for a fallback to `clone`.\n * The stored text is all that is kept, so a filler whose input was NOT text has to reverse\n * its own conversion — otherwise the rows written before the fallback would change type,\n * coming back as text while every row after it comes back as an object.\n */\n constructor(\n private readonly toText: (value: unknown) => string | null,\n private readonly fromText: (text: string) => unknown\n ) { }\n\n set(index: number, value: unknown): boolean {\n if (value == null) {\n this.texts[index] = 'null';\n this.nulls[index] = true;\n return true;\n }\n\n const text = this.toText(value);\n\n if (text == null) {\n return false;\n }\n\n this.texts[index] = text;\n\n return true;\n }\n\n drain(count: number): unknown[] {\n const values: unknown[] = new Array(count);\n\n for (let i = 0; i < count; i++) {\n values[i] = this.nulls[i] === true ? null : this.fromText(this.texts[i]);\n }\n\n return values;\n }\n\n emit(rowCount: number): EncodedColumn {\n // Joined with a single ',' and nothing else, which is the whole of the format.\n const doc = `[${this.texts.slice(0, rowCount).join(',')}]`;\n\n return { encoding: this.encoding, doc };\n }\n\n reset(): void {\n this.texts = [];\n this.nulls = [];\n }\n}\n\n/**\n * Raw values in a plain array, structured-cloned as they are.\n *\n * Strings live here and are never encoded. `TextEncoder` loses to clone by a wide margin —\n * 14.0ms against 8.3ms for 4,000 rows of 2KB text — because cloning a V8 string is a native\n * memcpy.\n */\nexport class CloneFiller implements ColumnFiller {\n\n readonly encoding = 'clone' as const;\n\n private data: unknown[];\n\n constructor(seed: unknown[] = []) {\n this.data = seed;\n }\n\n set(index: number, value: unknown): boolean {\n this.data[index] = value;\n\n return true;\n }\n\n drain(count: number): unknown[] {\n return this.data.slice(0, count);\n }\n\n emit(rowCount: number): EncodedColumn {\n return { encoding: this.encoding, data: this.data.slice(0, rowCount) };\n }\n\n reset(): void {\n this.data = [];\n }\n}\n\n/** A value that is already JSON text is passed straight through; anything else is not text. */\nconst asJsonText = (value: unknown): string | null =>\n typeof value === 'string' ? value : null;\n\n/**\n * A live value becomes text here.\n *\n * `JSON.stringify` throws on a circular structure and on a `bigint`, and returns `undefined` for\n * a value that is not representable at all — a function, or a lone `undefined`. All three send the\n * column to `clone`, which is the same answer every other filler gives a value it cannot encode.\n */\nconst asStringifiedJson = (value: unknown): string | null => {\n try {\n return JSON.stringify(value) ?? null;\n } catch {\n return null;\n }\n};\n\n/**\n * Back to a value, for a `json-stringify` column that fell back.\n *\n * The text came from `JSON.stringify` on this same value, so it parses. A `catch` returning the\n * text is there only so a fallback — already the unhappy path — cannot throw.\n */\nconst parseJsonText = (text: string): unknown => {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n};\n\nconst FILLERS: Record<TransferEncoding, () => ColumnFiller> = {\n 'float64': () => new Float64Filler(),\n 'date-f64': () => new DateFiller(),\n 'boolean-byte': () => new BooleanFiller(),\n // The engine gave text, so the text IS the raw value a fallback should carry.\n 'json': () => new JsonFiller(asJsonText, text => text),\n // The engine gave a live object, so a fallback has to parse the text back into one.\n 'json-stringify': () => new JsonFiller(asStringifiedJson, parseJsonText),\n 'clone': () => new CloneFiller(),\n};\n\nexport const createFiller = (encoding: TransferEncoding): ColumnFiller => {\n const create = FILLERS[encoding];\n\n if (create == null) {\n throw new Error(`transfer encoding '${encoding}' is not supported`);\n }\n\n return create();\n};\n","import { ColumnFiller, CloneFiller, createFiller } from './fillers';\nimport {\n assertTransferVersion,\n CHUNK_ROWS,\n EncodedColumn,\n EncodedTransfer,\n TransferColumn,\n TransferPlan,\n TRANSFER_VERSION,\n} from './types';\n\n/** Probe for names the prototype chain answers for — `__proto__`, `toString`, `constructor`. */\nconst EMPTY_OBJECT: Record<string, unknown> = {};\n\n/**\n * Fills one chunk at a time from row values, and emits it with the buffers its transport can hand\n * over.\n *\n * One encoder per result, not per chunk: a column that falls back to `clone` stays there for\n * every later chunk, and a forward-only cursor cannot rewind to re-encode what it already yielded.\n *\n * Usage is a loop — `appendRow` or `appendRecord` until `isFull`, `take`, repeat, then `take` once\n * more for the short final chunk. A zero-row result takes exactly one chunk with `rowCount: 0`.\n */\nexport class ChunkEncoder {\n\n private readonly columns: readonly TransferColumn[];\n private readonly fillers: ColumnFiller[];\n private readonly inheritedNames: boolean;\n private rows = 0;\n\n constructor(plan: TransferPlan) {\n assertTransferVersion(plan.version);\n\n if (plan.columns.length === 0) {\n throw new Error('A transfer plan needs at least one column; a result with no columns has no rows to encode.');\n }\n\n const names = new Set<string>();\n\n for (const column of plan.columns) {\n if (names.has(column.name)) {\n // One entry per column name in the emitted chunk, so two columns sharing a name\n // would silently keep only the second. The caller aliases them instead.\n throw new Error(`A transfer plan names the column '${column.name}' more than once.`);\n }\n\n names.add(column.name);\n }\n\n this.columns = plan.columns;\n this.fillers = plan.columns.map(column => createFiller(column.encoding));\n this.inheritedNames = plan.columns.some(column => column.name in EMPTY_OBJECT);\n }\n\n /** Rows in the chunk being filled. */\n get rowCount(): number {\n return this.rows;\n }\n\n get isFull(): boolean {\n return this.rows === CHUNK_ROWS;\n }\n\n /** The column names, in the order a row's values must arrive in. */\n get columnNames(): readonly string[] {\n return this.columns.map(column => column.name);\n }\n\n /**\n * Adds one row, its values in plan column order.\n *\n * A value that does not belong in its column's encoding sends that column to `clone` for the\n * rest of the result, carrying the rows already written with it. Nothing is coerced into a\n * typed array.\n */\n appendRow(values: readonly unknown[]): void {\n if (this.isFull) {\n throw new Error(`The chunk is full at ${CHUNK_ROWS} rows; take it before appending another.`);\n }\n\n if (values.length !== this.fillers.length) {\n throw new Error(\n `A row carried ${values.length} values for ${this.fillers.length} planned columns.`\n );\n }\n\n const index = this.rows;\n\n for (let i = 0; i < this.fillers.length; i++) {\n if (this.fillers[i].set(index, values[i]) === false) {\n this.fallBack(i, index, values[i]);\n }\n }\n\n this.rows = index + 1;\n }\n\n /**\n * Adds one row from a NAME-KEYED record, reading each planned column out of it.\n *\n * For an engine that yields records rather than positional tuples — a document store, a\n * key-value store, a driver that returns row objects. Projecting to an array is the caller's\n * alternative, and getting that order wrong is silent corruption rather than an error, so the\n * mapping belongs here once instead of in every plugin.\n *\n * A column the record does not carry is `null`, not an error. Records are legitimately\n * heterogeneous outside a fixed-schema table, and the plan is what decides the result shape.\n */\n appendRecord(record: Record<string, unknown>): void {\n const values: unknown[] = new Array(this.columns.length);\n\n for (let i = 0; i < this.columns.length; i++) {\n values[i] = this.readField(record, this.columns[i].name);\n }\n\n this.appendRow(values);\n }\n\n /**\n * A plain read, unless a planned name is one the prototype chain answers for.\n *\n * `record['__proto__']` on an object literal returns `Object.prototype` rather than\n * `undefined`, and `toString` returns a function — either would be encoded as a value. The\n * own-property test that avoids it costs a call per field, so it is only taken when a name in\n * this plan actually needs it.\n */\n private readField(record: Record<string, unknown>, name: string): unknown {\n if (this.inheritedNames) {\n return Object.prototype.hasOwnProperty.call(record, name) ? record[name] : null;\n }\n\n const value = record[name];\n\n // `null`, not `undefined`. An absent field and one holding `undefined` mean the same thing\n // here, and a `clone` column would otherwise carry `undefined` all the way to the entity —\n // a decoded row says `null` for an absent value in every other encoding.\n return value === undefined ? null : value;\n }\n\n /**\n * Emits the filled chunk and readies the encoder for the next one.\n *\n * Transfer DETACHES the emitted buffers, so the fillers allocate fresh arrays here rather\n * than reusing them. Reading a chunk's typed arrays after this is a use-after-transfer.\n */\n take(): EncodedTransfer {\n const transferables: ArrayBufferLike[] = [];\n // `Object.create(null)`, because a column named `__proto__` assigned onto a plain object\n // reaches `Object.prototype`'s setter and never becomes an own property — the column then\n // survives same-realm through the getter and vanishes the moment the chunk is cloned.\n const columns = Object.create(null) as Record<string, EncodedColumn>;\n\n for (let i = 0; i < this.columns.length; i++) {\n columns[this.columns[i].name] = this.fillers[i].emit(this.rows, transferables);\n }\n\n const payload = { version: TRANSFER_VERSION, rowCount: this.rows, columns } as const;\n\n this.rows = 0;\n\n for (const filler of this.fillers) {\n filler.reset();\n }\n\n return { payload, transferables };\n }\n\n private fallBack(column: number, index: number, value: unknown): void {\n const clone = new CloneFiller(this.fillers[column].drain(index));\n\n clone.set(index, value);\n\n this.fillers[column] = clone;\n }\n}\n","import {\n assertTransferVersion,\n EncodedChunk,\n TransferColumn,\n TransferEncoding,\n TransferPlan,\n} from './types';\n\n/**\n * Turning a chunk back into row objects, with a generated function rather than a loop over\n * column descriptors.\n *\n * A reflective loop assigning `row[column.name]` onto a fresh object builds dictionary-mode\n * objects and measured 2.3-4x slower — most of the codec's win is here. A generated function\n * emits one object literal per row, in plan column order, so every row of a result shares one\n * hidden class. This is the same technique `core/src/codegen/handlers` uses for `clone`, `hash`\n * and `compare`, and not the same registry: a chunk layout is a result shape, and joins,\n * projections and `RETURNING` layouts have no schema behind them.\n */\n\n/** A generated decoder. `json` carries one parsed array per JSON column, by column index. */\ntype ChunkDecoder = (chunk: EncodedChunk, json: readonly unknown[][]) => unknown[];\n\n/**\n * A chunk's JSON document did not parse.\n *\n * Reported as its own shape so a caller can retry the request WITHOUT a plan and get today's\n * clone path, which parses row by row and tolerates a field holding text that is not JSON.\n * Every other decode failure is a real error.\n *\n * Read structurally rather than with `instanceof`: an error constructed in another realm — Jest\n * gives each test file its own — fails the prototype test for exactly the cases this classifies.\n */\nexport type TransferJsonError = Error & { readonly transferJsonColumn: string };\n\nexport const isTransferJsonError = (error: unknown): error is TransferJsonError =>\n typeof (error as { transferJsonColumn?: unknown } | null)?.transferJsonColumn === 'string';\n\nconst jsonError = (column: string, cause: unknown): TransferJsonError =>\n Object.assign(\n new Error(\n `The transferred JSON document for column '${column}' did not parse, so this column holds ` +\n `text that is not JSON. Retry the request without a transfer plan. Cause: ` +\n `${(cause as Error)?.message ?? String(cause)}`\n ),\n { transferJsonColumn: column }\n );\n\n/**\n * What each column's encoding turned out to be, which is NOT always what the plan asked for.\n *\n * A column that met a value it could not type fell back to `clone` in the worker, and said so on\n * the chunk. The chunk's tag is the truth; the plan only fixes the order and the names.\n */\ntype EffectiveColumn = { readonly name: string; readonly encoding: TransferEncoding };\n\nconst effectiveColumns = (plan: TransferPlan, chunk: EncodedChunk): EffectiveColumn[] =>\n plan.columns.map((column: TransferColumn) => {\n // An own-property test, not a truthiness one: a name like `__proto__` or `toString`\n // resolves to something inherited on a plain object, which would pass a null check and\n // then be decoded as a column.\n if (Object.prototype.hasOwnProperty.call(chunk.columns, column.name) === false) {\n throw new Error(`The transferred chunk has no column '${column.name}', which the plan lists.`);\n }\n\n const encoded = chunk.columns[column.name];\n\n return { name: column.name, encoding: encoded.encoding };\n });\n\n/**\n * The serialized layout IS the key.\n *\n * Content-keyed, never by collection name: a migration changes the columns under one name, one\n * worker serves every database on the page, and joins and projections produce many shapes per\n * collection. Names are quoted so a name containing the separator cannot collide with a\n * different layout — a collision would hand a result the wrong decoder, silently.\n */\nconst cacheKey = (columns: readonly EffectiveColumn[]): string =>\n 'v1|' + columns.map(column => `${JSON.stringify(column.name)}:${column.encoding}`).join('|');\n\n/** Retains a compiled function per entry, so it is bounded. */\nconst CACHE_CAPACITY = 64;\n\nconst cache = new Map<string, ChunkDecoder>();\n\nconst cached = (key: string): ChunkDecoder | undefined => {\n const decoder = cache.get(key);\n\n if (decoder == null) {\n return undefined;\n }\n\n // Re-inserted so the eviction below drops the least recently USED entry, not the oldest.\n cache.delete(key);\n cache.set(key, decoder);\n\n return decoder;\n};\n\nconst remember = (key: string, decoder: ChunkDecoder): void => {\n cache.set(key, decoder);\n\n if (cache.size > CACHE_CAPACITY) {\n const oldest = cache.keys().next();\n\n if (oldest.done === false) {\n cache.delete(oldest.value);\n }\n }\n};\n\n/** Emptied between tests. Not part of the decoding contract. */\nexport const clearDecoderCache = (): void => cache.clear();\n\nconst rowValue = (column: EffectiveColumn, index: number): string => {\n const nulled = `(u${index}[b] & m) !== 0`;\n\n switch (column.encoding) {\n case 'float64':\n return `${nulled} ? null : d${index}[i]`;\n case 'date-f64':\n return `${nulled} ? null : new Date(d${index}[i])`;\n case 'boolean-byte':\n return `${nulled} ? null : d${index}[i] !== 0`;\n case 'json':\n return `j${index}[i]`;\n case 'clone':\n return `d${index}[i]`;\n }\n};\n\n/**\n * The key to write in the emitted object literal.\n *\n * A quoted `\"__proto__\"` in an object literal sets the row's prototype instead of defining a\n * property (Annex B.3.1), so that one name needs a computed key. Every other name keeps the\n * constant form: computed keys throughout measured 9% slower over a 4,096-row chunk, and decode\n * speed is what this whole module is for.\n */\nconst literalKey = (name: string): string =>\n name === '__proto__' ? `[${JSON.stringify(name)}]` : JSON.stringify(name);\n\nconst isTyped = (encoding: TransferEncoding): boolean =>\n encoding === 'float64' || encoding === 'date-f64' || encoding === 'boolean-byte';\n\nconst decoderSource = (columns: readonly EffectiveColumn[]): string => {\n const lines: string[] = ['\"use strict\";', 'var n = chunk.rowCount;', 'var rows = new Array(n);'];\n\n columns.forEach((column, index) => {\n if (column.encoding === 'json') {\n lines.push(`var j${index} = json[${index}];`);\n return;\n }\n\n lines.push(`var c${index} = chunk.columns[${JSON.stringify(column.name)}];`);\n lines.push(`var d${index} = c${index}.data;`);\n\n if (isTyped(column.encoding)) {\n lines.push(`var u${index} = c${index}.nulls;`);\n }\n });\n\n lines.push('for (var i = 0; i < n; i++) {');\n lines.push('var b = i >> 3, m = 1 << (i & 7);');\n // One object literal per row, properties in plan order, so all rows share a hidden class.\n lines.push('rows[i] = {');\n\n columns.forEach((column, index) => {\n lines.push(`${literalKey(column.name)}: ${rowValue(column, index)},`);\n });\n\n lines.push('};');\n lines.push('}');\n lines.push('return rows;');\n\n return lines.join('\\n');\n};\n\n/**\n * Whether this environment allows generated functions at all.\n *\n * `new Function` needs `unsafe-eval`, which a Content-Security-Policy can withhold. A caller\n * asks once at startup and stops sending plans if the answer is no, which falls back to the\n * transport's ordinary clone — a known-good path. There is deliberately no reflective decoder to\n * fall back to: one was measured and never beat the clone it would replace.\n */\nlet generationSupported: boolean | null = null;\n\nexport const isTransferCodecSupported = (): boolean => {\n if (generationSupported == null) {\n try {\n new Function('return 1')();\n generationSupported = true;\n } catch {\n generationSupported = false;\n }\n }\n\n return generationSupported;\n};\n\nconst buildDecoder = (columns: readonly EffectiveColumn[]): ChunkDecoder =>\n new Function('chunk', 'json', decoderSource(columns)) as ChunkDecoder;\n\nconst decoderFor = (columns: readonly EffectiveColumn[]): ChunkDecoder => {\n const key = cacheKey(columns);\n const hit = cached(key);\n\n if (hit != null) {\n return hit;\n }\n\n const decoder = buildDecoder(columns);\n\n remember(key, decoder);\n\n return decoder;\n};\n\n/**\n * Parsed outside the generated function, so a failure can name its column.\n *\n * One parse per JSON column per chunk replaces one per row — worth about 16% of the codec's\n * total win, and it stacks with chunking.\n */\nconst parseJsonColumns = (columns: readonly EffectiveColumn[], chunk: EncodedChunk): unknown[][] => {\n const parsed: unknown[][] = new Array(columns.length);\n\n columns.forEach((column, index) => {\n if (column.encoding !== 'json') {\n return;\n }\n\n const encoded = chunk.columns[column.name];\n const doc = encoded.encoding === 'json' ? encoded.doc : '[]';\n\n try {\n parsed[index] = JSON.parse(doc) as unknown[];\n } catch (error) {\n throw jsonError(column.name, error);\n }\n });\n\n return parsed;\n};\n\n/**\n * Decodes one chunk into final-shape row objects — real booleans, `Date` objects, parsed JSON.\n *\n * Not the raw storage shape. The entity needs the final shape either way, and decoding to raw and\n * re-shaping afterwards measured slower (156ms against 140ms at 100,000 rows). An absent or null\n * value becomes JavaScript `null`, never `undefined` and never an absent property.\n *\n * A column that fell back to `clone` in the worker comes back RAW, and the caller still owes it\n * whatever shaping that column would otherwise have had.\n */\nexport const decodeChunk = (plan: TransferPlan, chunk: EncodedChunk): unknown[] => {\n assertTransferVersion(plan.version);\n assertTransferVersion(chunk.version);\n\n const columns = effectiveColumns(plan, chunk);\n\n return decoderFor(columns)(chunk, parseJsonColumns(columns, chunk));\n};\n","import type { SchemaDefinition } from \"./SchemaDefinition\";\nimport type { SchemaBase } from \"./property/base/SchemaBase\";\nimport type { SchemaArray } from \"./property/types/SchemaArray\";\nimport type { SchemaVector } from \"./property/types/SchemaVector\";\nimport type { SchemaObject } from \"./property/types/SchemaObject\";\nimport type { PropertyInfo } from \"./PropertyInfo\";\nimport type { DeepPartial } from \"../types\";\nimport type { SchemaFunction } from \"./table\";\nimport type { SchemaOptional, SchemaTag } from \"./property/modifiers\";\nimport type { Branded } from \"../utilities/types\";\nimport type { SchemaSubscriptionOptions } from \"./communication/broadcast\";\n\nexport type DefaultValue<T, I = never> = T | ((injected: I) => T);\nexport type FunctionBody<TEntity, TResult> = (entity: TEntity, collectionName: CollectionName) => TResult;\nexport type IdType = string | number;\nexport type ForeignKey<T extends {}> = { \n schema: CompiledSchema<T>, \n property: PropertyInfo<T> \n};\n\nexport enum SchemaTypes {\n Array = \"Array\",\n Boolean = \"Boolean\",\n Date = \"Date\",\n Number = \"Number\",\n Object = \"Object\",\n String = \"String\",\n Definition = \"Definition\",\n Function = \"Function\",\n Computed = \"Computed\",\n /**\n * Content in, reference out. The only type whose write shape differs from its stored\n * shape, and a leaf on purpose — see `SchemaFile`.\n */\n File = \"File\",\n /**\n * A fixed-length list of numbers, carrying its dimension count — see `SchemaVector`.\n *\n * Value-shaped exactly like `s.array(s.number())`, which is why every array codegen\n * handler accepts it. It is a distinct type only so a backend can recognise it and store\n * it natively; nothing else needs to tell the two apart.\n */\n Vector = \"Vector\"\n}\n\nexport type ArrayShape = string | number | Date | {};\n\n\n/**\n * What a file property gives back: where the bytes are and what they are.\n *\n * Declared in core so `InferType` can name it. Core never reads or writes the bytes — it only\n * carries this shape — and `@routier/blob-plugin` is what puts one here.\n */\nexport type FileReferenceValue = {\n /** Where the bytes live, content-addressed by the blob plugin. */\n key: string;\n /** Byte length. */\n size: number;\n /** Media type as supplied at upload. */\n contentType: string;\n /** SHA-256 of the bytes, lowercase hex. */\n checksum: string;\n /** The name to show a user. Not part of the key. */\n fileName: string;\n};\n\n/**\n * What a file property ACCEPTS: content, or a reference you already have.\n *\n * `Blob` covers `File`, which is what an `<input type=\"file\">` yields. A reference is accepted\n * too, so re-saving an entity that was read from the database does not have to re-upload it.\n */\nexport type FileContentValue =\n | FileReferenceValue\n | Uint8Array\n | ArrayBuffer\n | Blob\n | string;\n\n/**\n * What a vector property holds, in and out: a plain list of numbers.\n *\n * Named rather than written inline because the inference rules have to recognise it after a\n * modifier has erased which class produced it — the same problem `FileReferenceValue` solves\n * above, and for the same reason.\n */\nexport type VectorValue = number[];\n\n/**\n * What `s.string({ ... })` accepts.\n *\n * Declarations only. Core stores them and never acts on them; a backend that can use one does.\n */\nexport type StringOptions = {\n /**\n * The longest value the property is declared to hold.\n *\n * MySQL uses it for `VARCHAR(maxLength)`; without it every string column is\n * `VARCHAR(255)`, which silently truncates longer values. Other backends ignore it. Core\n * never validates a value against it — see `SchemaBase.maxLength`.\n */\n maxLength?: number;\n};\n\nexport type ExpandedProperty = ExpandedChildProperty & {\n assignmentPath: string;\n selectorPath: string;\n properties: Map<string, ExpandedChildProperty>;\n childDegree: number;\n};\n\nexport type ExpandedChildProperty = {\n propertyName: string;\n type: SchemaTypes;\n isNullableOrOptional: boolean;\n isReadonly: boolean;\n isIdentity: boolean;\n isUnmapped: boolean;\n}\n\nexport enum HashType {\n Ids = \"Ids\",\n Object = \"Object\"\n}\n\nexport type HashFunction<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>, type: HashType.Object): string;\n (entity: InferType<TEntity>, type: HashType.Ids): string;\n}\n\nexport type GetHashTypeFunction<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>): HashType.Object;\n (entity: InferType<TEntity>): HashType.Ids;\n}\n\nexport type ChangeTrackingType = \"proxy\" | \"diff\" | \"immutable\";\n\nexport type IndexType = \"single\" | \"compound\" | \"unique\" | \"primary-key\"\nexport type Index = {\n properties: PropertyInfo<any>[],\n type: IndexType;\n name: string;\n}\n\n/**\n * Represents changes to subscriptions, categorizing them by modifications to\n * entities (additions, updates, removals) or query-driven removals.\n * @template T - The type of the entities in the subscription.\n */\nexport type SubscriptionChanges<T extends {}> = {\n /**\n * Entities that have been added to the subscription.\n */\n adds: InferType<T>[];\n /**\n * Entities that have been updated within the subscription.\n */\n updates: InferType<T>[];\n /**\n * Entities that have been removed from the subscription.\n */\n removals: InferType<T>[];\n /**\n * Entities that have been added/updated/removed from the subscription and it is unknown \n * if the entities have been added/updated/removed.\n */\n unknown: InferType<T>[];\n}\n\nexport interface ISchemaSubscription<T extends {}> extends Disposable {\n send(changes: SubscriptionChanges<T>): void;\n onMessage(callback: (changes: SubscriptionChanges<T>) => void): void;\n}\n\nexport type Enrich<TEntity extends {}> = {\n (entity: InferType<TEntity>, changeTrackingType: ChangeTrackingType): InferType<TEntity>;\n (entity: InferCreateType<TEntity>, changeTrackingType: ChangeTrackingType): InferCreateType<TEntity>;\n}\nexport type Prepare<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>): InferCreateType<TEntity>;\n (entity: InferType<TEntity>): InferType<TEntity>;\n}\nexport type Preprocess<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>): InferType<TEntity>;\n (entity: InferType<TEntity>): InferType<TEntity>;\n}\n\nexport type SetProperties<TEntity extends {}> = (destination: DeepPartial<InferType<TEntity> | InferCreateType<TEntity>>, source: DeepPartial<InferType<TEntity> | InferCreateType<TEntity>>) => void;\n\nexport type CompiledSchemaCore<TEntity extends {}> = Omit<CompiledSchema<TEntity>, \"createSubscription\">;\n\nexport type CompiledSchemaWithMetadata<TEntity extends {}, TMetadata> = {\n readonly metadata: TMetadata;\n} & CompiledSchema<TEntity>;\n\n/**\n * Represents a fully compiled schema with all utilities and metadata for an entity type.\n */\nexport type CompiledSchema<TEntity extends {}> = {\n\n deserializePartial: (item: Record<string, unknown>, properties: PropertyInfo<TEntity>[]) => DeepPartial<InferType<TEntity>>;\n\n createSubscription: (abortSignal?: AbortSignal, scope?: string, options?: SchemaSubscriptionOptions) => ISchemaSubscription<TEntity>;\n /** Returns the property info for a given id (full path) */\n getProperty: (id: string) => PropertyInfo<TEntity>;\n /** Returns the ID of the given entity. */\n getId: (entity: InferType<TEntity>) => IdType;\n /** Returns a deep clone of the given entity. */\n clone: (entity: InferType<TEntity>) => InferType<TEntity>;\n /**\n * Returns a deep clone of a record that is still in the STORAGE shape — renamed properties\n * under their `from` names rather than their in-memory names.\n *\n * `clone` reads in-memory names, so it returns `undefined` for every renamed property of a\n * stored record. Use this when copying rows a store holds before they have been deserialized.\n * Generated on first call; schemas that are never cloned in storage shape never build it.\n */\n cloneStorage: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Removes unmapped or extraneous properties from the entity. */\n strip: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Prepares a new entity for creation, applying defaults and transformations. */\n prepare: Prepare<TEntity>;\n /** Merges the source entity into the destination entity. */\n merge: (destination: InferType<TEntity> | InferCreateType<TEntity>, source: InferType<TEntity>) => InferType<TEntity>;\n /** Indicates if the schema has identity properties. */\n hasIdentities: boolean;\n /** List of properties that are identity keys. */\n idProperties: PropertyInfo<TEntity>[];\n /** All property metadata for the schema. */\n properties: PropertyInfo<TEntity>[],\n /** The hash type used for this schema. */\n hashType: HashType;\n /** Computes a hash for the given entity. */\n hash: HashFunction<TEntity>;\n /** Returns the hash type for the given entity. */\n getHashType: GetHashTypeFunction<TEntity>;\n /** Compares two entities for equality. */\n compare: (a: InferType<TEntity>, fromDb: InferType<TEntity>) => boolean;\n /** Deserializes an entity from storage format. */\n deserialize: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Sets 1 or many properties from the source object onto the destination object with change tracking. */\n set: SetProperties<TEntity>;\n /** Combines serializing and preparing an entity for saving. */\n preprocess: Preprocess<TEntity>;\n /** Combines deserializing and enriching an entity for selection. */\n postprocess: Enrich<TEntity>;\n\n /** Serializes an entity to storage format. */\n serialize: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Unique id for the schema. */\n id: SchemaId,\n /** The name of the collection for this schema. */\n collectionName: CollectionName;\n /** Returns all IDs for the given entity (usually a single-element tuple). */\n getIds: (entity: InferType<TEntity>) => [IdType];\n /** Enriches the entity with change tracking or other metadata. */\n enrich: Enrich<TEntity>;\n /** Indicates if the schema has identity keys. */\n hasIdentityKeys: boolean;\n /** Returns a deeply frozen (immutable) version of the entity. */\n freeze: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Enables change tracking on the entity. */\n enableChangeTracking: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** The schema definition object. */\n definition: SchemaDefinition<TEntity>;\n /** Returns all indexes defined for this schema. */\n getIndexes: () => Index[];\n /** Compares two entities for Id equality. */\n compareIds: (a: InferType<TEntity>, b: InferType<TEntity>) => boolean;\n}\n\nexport type PropertySerializer<T extends any> = (value: T) => string | number;\nexport type PropertyDeserializer<T extends any> = (value: string | number) => T;\n\n/**\n * A two-way transform between the application value and the stored value.\n *\n * Both directions may be async. Held as a live reference rather than stringified, so a\n * closure works and `injected` is a convenience rather than the only way in.\n */\nexport type PropertyTransform<T extends any> = {\n /**\n * Application value to stored value. Runs before the plugin sees it. May be async.\n *\n * `entity` is there for the one-way case: a transform with no `from` derives a value\n * rather than converting one, which is what `computed` does.\n */\n to: (value: T, entity: Record<string, unknown>) => unknown | Promise<unknown>;\n\n /**\n * Stored value back to application value. Runs after the plugin returns it.\n *\n * Optional. Leave it out and the transform is one-way: the stored value is the value.\n */\n from?: (value: unknown) => T | Promise<T>;\n\n /**\n * What the column becomes, when the stored form is not the property's own type.\n *\n * Defaults to the property's own type, so nothing changes unless you say it does. A\n * library that always produces text — a cipher, a compressor — sets this once, and the\n * caller who uses that library never writes it.\n */\n stores?: SchemaTypes;\n\n /**\n * Whether a filter on this property can still run in the database.\n *\n * Defaults to `none`, which rejects the filter rather than returning wrong rows. Set\n * `equality` only when `to` is deterministic.\n */\n comparable?: 'equality' | 'none';\n};\n\nexport type SchemaId = Branded<number, \"SchemaId\">;\nexport type CollectionName = Branded<string, \"CollectionName\">;\n\nexport type SchemaModifiers = \"default\" | \"deserialize\" |\n \"identity\" | \"key\" |\n \"nullable\" | \"optional\" |\n \"readonly\" | \"serialize\" |\n \"unmapped\" | \"computed\" |\n \"distinct\" | \"searchable\";\n\n/**\n * What a tagged property infers to.\n *\n * `tag()` is metadata and must not change a type, but `SchemaTag<T>` carries the same `T` as\n * whatever it wrapped without carrying which class that was. For a string `T` is already\n * `string`; for an object `T` is the map of child schemas, which only the `SchemaObject`\n * branch below knows how to unwrap. Falling through to the generic `SchemaBase` branch\n * therefore handed the raw map back, so `s.object({ key: s.string() }).tag('x')` typed\n * `key` as `SchemaString` instead of `string` — everything ran, and only the types lied.\n *\n * An array is distinguishable because `SchemaArray`'s parameter is the ELEMENT schema, so a\n * tagged array arrives here as a `SchemaBase` rather than a plain map.\n */\ntype InferTagged<C> = ResolveWrapped<C>;\n\n/**\n * What a wrapping modifier's inner type resolves to.\n *\n * `SchemaOptional`, `SchemaNullable` and `SchemaTag` all carry the same `C` as whatever they\n * wrapped, without carrying which class that was, so each has to work out what it is holding.\n * Three shapes are possible:\n *\n * - an already-resolved value (`string` from `s.string()`, a file reference from `s.file()`)\n * - an ELEMENT schema, which is what `SchemaArray` parameterises on\n * - a map of child schemas, which is what `SchemaObject` parameterises on\n *\n * Getting this wrong is silent. The map branch applied to an already-resolved object walks\n * its keys and infers `never` for each, so `s.file().optional()` typed as\n * `{ key: never, size: never, ... }` — which no value can satisfy and no test would catch at\n * runtime.\n */\ntype ResolveWrapped<C> =\n C extends string | number | boolean | Date | FileReferenceValue ? C :\n C extends VectorValue ? C :\n C extends SchemaBase<any, any> ? InferPrimitive<C>[] :\n { [K in keyof C]: InferPrimitive<C[K]> };\n\ntype InferPrimitive<T> =\n T extends SchemaOptional<infer C, infer __> ? ResolveWrapped<C> :\n T extends SchemaTag<infer C, infer __> ? InferTagged<C> :\n // Before the generic `SchemaBase` branch below, which would see `X = number[]` and map\n // the element through `InferPrimitive<number>` — no branch matches a bare `number`, so a\n // vector would type as `never[]`: assignable from nothing, and invisible at runtime.\n T extends SchemaVector<infer __, infer ___> ? VectorValue :\n T extends SchemaArray<infer Y, infer __> ? InferPrimitive<Y>[]\n : T extends SchemaObject<infer Obj, infer _> ?\n { [K in keyof Obj]: InferPrimitive<Obj[K]> } : // Process nested objects\n T extends SchemaFunction<infer F, infer __> ? F : T extends SchemaBase<infer X, infer _> ?\n X extends Array<infer A> ? InferPrimitive<A>[] : X : // Extract the primitive type\n never;\n\nexport type InferType<T> = T extends CompiledSchema<infer R> ? InferCompiledSchema<R> : T extends {} ? InferCompiledSchema<T> : T;\nexport type InferCreateType<T> = T extends CompiledSchema<infer R> ? InferCompiledCreateSchema<R> : T extends {} ? InferCompiledCreateSchema<T> : unknown;\nexport type InferMappedType<T> = T extends SchemaBase<infer K, infer __> ? InferType<K> : InferCompiledSchema<T>;\nexport type InferRoot<T> = T extends CompiledSchema<infer R> ? R : never;\n\ntype HasModifier<T, K extends keyof T, M extends SchemaModifiers> =\n T[K] extends SchemaBase<any, infer Mods> ?\n M extends Mods ? true : false :\n false;\n\ntype IsPlainProperty<T, K extends keyof T> =\n [\n HasModifier<T, K, \"readonly\">,\n HasModifier<T, K, \"optional\">,\n HasModifier<T, K, \"nullable\">\n ] extends [\n false,\n false,\n false\n ] ? true : false;\n\ntype IsCreateExcluded<T, K extends keyof T> =\n [\n HasModifier<T, K, \"identity\">,\n HasModifier<T, K, \"computed\">,\n HasModifier<T, K, \"unmapped\">\n ] extends [\n false,\n false,\n false\n ] ? false : true;\n\ntype IsCreateOptional<T, K extends keyof T> =\n [\n HasModifier<T, K, \"optional\">,\n HasModifier<T, K, \"default\">\n ] extends [\n false,\n false\n ] ? false : true;\n\ntype IsCreateNullable<T, K extends keyof T> =\n HasModifier<T, K, \"nullable\"> extends true ? true : false;\n\n/**\n * What a property ACCEPTS on the way in, which is not always what it gives back.\n *\n * Only a file differs today: you assign content and read a reference. Matching on the read\n * type rather than on `SchemaFile` itself is deliberate — it keeps working through every\n * modifier. `s.file().optional()` is a `SchemaOptional`, `s.file().tag('x')` is a\n * `SchemaTag`, and neither carries the original class, so a check against the class alone\n * would silently stop accepting content the moment anyone added a modifier.\n *\n * Assignability is required in BOTH directions, and the tuple wrappers are load-bearing.\n * One-way `extends` matches `never` — which is assignable to everything — so a generic\n * property over `Record<string, unknown>` resolved to file content and broke the Dexie\n * plugin's types. It also matched any object that merely happens to have these five fields\n * plus more. Mutual assignability admits the reference shape and nothing else, and the\n * tuples stop the conditional distributing over a union.\n */\ntype InferWritePrimitive<T> =\n [InferPrimitive<T>] extends [FileReferenceValue]\n ? [FileReferenceValue] extends [InferPrimitive<T>] ? FileContentValue : InferPrimitive<T>\n : InferPrimitive<T>;\n\ntype InferCreateProperty<T, K extends keyof T> =\n IsCreateNullable<T, K> extends true ? null | InferWritePrimitive<T[K]> : InferWritePrimitive<T[K]>;\n\ntype InferCompiledSchema<T> = CoalesceEmpty<{\n [K in keyof T as IsPlainProperty<T, K> extends true ? K : never]: InferPrimitive<T[K]>\n}, {\n readonly [K in keyof T as HasModifier<T, K, \"readonly\"> extends true ? K : never]: InferPrimitive<T[K]>\n }, {\n [K in keyof T as HasModifier<T, K, \"optional\"> extends true ? K : never]?: InferPrimitive<T[K]>\n }, {\n [K in keyof T as HasModifier<T, K, \"nullable\"> extends true ? K : never]: null | InferPrimitive<T[K]>\n}>;\n\ntype InferCompiledCreateSchema<T> = {\n [K in keyof T as IsCreateExcluded<T, K> extends true ? never\n : IsCreateOptional<T, K> extends true ? K : never]?: InferCreateProperty<T, K>\n} & {\n [K in keyof T as IsCreateExcluded<T, K> extends true ? never\n : IsCreateOptional<T, K> extends true ? never : K]: InferCreateProperty<T, K>\n};\n\ntype IsEmptyObject<T> = keyof T extends never ? true : false;\ntype CoalesceEmpty<T1 extends {}, T2 extends {}, T3 extends {}, T4 extends {}> = (IsEmptyObject<T1> extends true ? {} : T1) & (IsEmptyObject<T2> extends true ? {} : T2) & (IsEmptyObject<T3> extends true ? {} : T3) & (IsEmptyObject<T4> extends true ? {} : T4);\n","import { SchemaTypes } from '../schema/types';\nimport type { PropertyInfo } from '../schema/PropertyInfo';\nimport type { ResultColumn } from '../plugins/resultShape';\nimport { TransferEncoding, TransferPlan, TRANSFER_VERSION } from './types';\n\n/**\n * Deciding how each column of a result crosses the boundary.\n *\n * Separate from the codec beside it because of WHO imports it, not because of what it knows: the\n * encoder is bundled into a worker, and this runs on the main thread only. `SchemaTypes` is the\n * one runtime import — the enum's own module is type-only imports throughout — so a bundler that\n * does pull this in still does not pull in the schema machinery.\n *\n * Nothing here is SQL. A plan is a list of result columns and the property behind each one, and\n * both of those are data-model facts. What varies by engine is which values it hands back — one\n * that stores a date as ISO text needs `Date.parse`, one that parses before returning does not —\n * and that variation is a mapping the caller passes in rather than a table this module owns.\n *\n * The rule throughout is that uncertainty means `clone`. A wrong encoding is not slow, it is\n * wrong, and `clone` is exactly what happens without a plan — so the worst a conservative choice\n * costs is the speed-up it declines.\n */\n\n/**\n * Which encoding each schema type can take on one engine.\n *\n * A type with no entry gets `clone`. That is the safe direction: a missing entry is a type this\n * mapping has not considered, and guessing at one is how a column starts decoding wrongly.\n */\nexport type TransferTypeMapping = Readonly<Partial<Record<SchemaTypes, TransferEncoding>>>;\n\n/**\n * Full control over one column's encoding, for an engine whose answer is not a function of the\n * schema type alone.\n *\n * Returning `undefined` defers to the type mapping, so a resolver can decide the few columns it\n * cares about and leave the rest. It CANNOT override the serializer rule — a property that owns\n * its own storage shape stays on `clone` whatever a resolver says, because that rule is about the\n * schema's contract rather than about the engine.\n */\nexport type TransferEncodingResolver = (column: ResultColumn) => TransferEncoding | undefined;\n\n/**\n * How a caller says which encoding a column takes: a table by schema type, a resolver, or both.\n *\n * The table covers every engine measured so far. The resolver exists because \"the schema type\" is\n * this module's guess at what varies, and an engine is entitled to disagree — a store that keeps\n * one property in a different representation from its siblings has no way to say so in a table\n * keyed by type.\n */\nexport type TransferEncodingStrategy =\n | TransferTypeMapping\n | TransferEncodingResolver\n | { readonly resolve?: TransferEncodingResolver; readonly types?: TransferTypeMapping };\n\n/**\n * For an engine that returns a column as the raw text or number it stored.\n *\n * SQLite is the case this was measured against — a date is TEXT holding ISO-8601 and a boolean is\n * INTEGER holding 0 or 1 — but the mapping is about the STORED shape, not about SQL. Any engine\n * that keeps those encodings uses it; one that parses values before returning them (PGlite) needs\n * its own.\n *\n * `String` is deliberately absent, so strings cross in a plain array. Cloning a V8 string is a\n * native memcpy and encoding one measured slower: 14.0ms against 8.3ms for 4,000 rows of 2KB text.\n */\nexport const rawStorageTransferTypes: TransferTypeMapping = {\n [SchemaTypes.Number]: 'float64',\n [SchemaTypes.Boolean]: 'boolean-byte',\n [SchemaTypes.Date]: 'date-f64',\n [SchemaTypes.Object]: 'json',\n [SchemaTypes.Array]: 'json',\n [SchemaTypes.Vector]: 'json',\n};\n\n/**\n * For an engine that returns values already parsed — a document store, a key-value store holding\n * decoded records, or a driver with type parsers registered.\n *\n * Differs from {@link rawStorageTransferTypes} in ONE place: a nested structure arrives as a live\n * object, so it is stringified on the way out rather than passed through as text. Dates and\n * booleans need no separate entry, because those fillers accept either shape.\n *\n * Whether `json-stringify` beats `clone` for a given payload is unmeasured — see the encoding's\n * own note. An engine unsure of that should map its nested types to `clone` and keep today's\n * behaviour.\n */\nexport const parsedValueTransferTypes: TransferTypeMapping = {\n [SchemaTypes.Number]: 'float64',\n [SchemaTypes.Boolean]: 'boolean-byte',\n [SchemaTypes.Date]: 'date-f64',\n [SchemaTypes.Object]: 'json-stringify',\n [SchemaTypes.Array]: 'json-stringify',\n [SchemaTypes.Vector]: 'json-stringify',\n};\n\n/**\n * True when this layer must not touch the column, whatever its declared type says.\n *\n * A property that serializes, deserializes or transforms itself owns its storage shape, and this\n * has no way to know what that shape is. Handing an already-parsed value to a property carrying\n * `.deserialize(x => JSON.parse(String(x)))` throws, from a schema that was working.\n */\nconst ownsItsShape = (property: PropertyInfo<any>): boolean =>\n property.valueSerializer != null\n || property.valueDeserializer != null\n || property.transform != null\n || property.functionBody != null;\n\n/**\n * The encoding for one column.\n *\n * A schema type alone does not prove what the engine will return — a migration or an external\n * writer can put anything in a column — so this is a starting point the encoder still validates\n * per value.\n */\nexport const transferEncodingFor = (\n column: ResultColumn,\n strategy: TransferEncodingStrategy\n): TransferEncoding => {\n const property = column.property;\n\n // Checked BEFORE the resolver, and not overridable by it. A property carrying its own\n // serializer owns its storage shape; pre-shaping it throws from a schema that was working,\n // and that is true on every engine.\n if (property == null || ownsItsShape(property)) {\n return 'clone';\n }\n\n const { resolve, types } = normalize(strategy);\n\n return resolve?.(column) ?? types?.[property.type] ?? 'clone';\n};\n\nconst normalize = (strategy: TransferEncodingStrategy):\n { resolve?: TransferEncodingResolver; types?: TransferTypeMapping } => {\n\n if (typeof strategy === 'function') {\n return { resolve: strategy };\n }\n\n if ('resolve' in strategy || 'types' in strategy) {\n return strategy as { resolve?: TransferEncodingResolver; types?: TransferTypeMapping };\n }\n\n return { types: strategy as TransferTypeMapping };\n};\n\n/**\n * Builds the plan for an ordered result column list, or `undefined` when there is nothing to plan.\n *\n * `undefined` is not a failure — it means this result takes the ordinary clone path. Two shapes\n * get it:\n *\n * - **No columns.** There is no row to decode.\n * - **A repeated column name.** One chunk carries one entry per name, and a row object holds one\n * value per key, so a result naming a column twice cannot round-trip through a plan. That is a\n * legal result that works without the codec, so it keeps working rather than becoming an error.\n */\nexport const buildTransferPlan = (\n columns: readonly ResultColumn[],\n strategy: TransferEncodingStrategy\n): TransferPlan | undefined => {\n if (columns.length === 0) {\n return undefined;\n }\n\n const names = new Set(columns.map(column => column.name));\n\n if (names.size !== columns.length) {\n return undefined;\n }\n\n return {\n version: TRANSFER_VERSION,\n columns: columns.map(column => ({\n name: column.name,\n encoding: transferEncodingFor(column, strategy),\n })),\n };\n};\n","/**\n * The worker-boundary transfer codec.\n *\n * Not SQL-specific. Any plugin whose engine runs in a worker crosses this boundary for the same\n * reason — `FileSystemFileHandle.createSyncAccessHandle` is undefined on the main thread, so OPFS\n * persistence is only reachable from a worker — and pays the same structured clone for its records.\n * A document store, a key-value store, or anything else compiled to WASM over OPFS is the same\n * problem: many records, each a set of named values, crossing one `postMessage`.\n *\n * What an engine has to supply is small and deliberately so:\n *\n * - **An ordered list of the fields a result carries**, and the schema property behind each one\n * where there is one. Positional (`appendRow`) or name-keyed (`appendRecord`), whichever the\n * engine yields.\n * - **A `TransferEncodingStrategy`** — which encoding each field takes. A table by schema type\n * covers the common case; a resolver covers an engine whose answer is not a function of the type.\n *\n * Every encoding names a VALUE shape rather than an engine, and the fillers accept every shape a\n * value plausibly arrives in: a date as a `Date`, an epoch number, or text; a boolean as a boolean\n * or as 0/1; a nested structure as text (`json`) or as a live object (`json-stringify`). An engine\n * chooses; nothing here assumes.\n *\n * Three layers, and the first two are here:\n *\n * - **codec** (`types`, `ChunkEncoder`, `decoder`) — column values in, `{ payload, transferables }`\n * out. Knows nothing about schemas or workers.\n * - **plan building** (`plan`) — result columns become a `TransferPlan`. Needs `SchemaTypes` and a\n * property's serializers, which are data-model facts, so it belongs here too. What varies by\n * engine is only which values that engine hands back, and that is a `TransferTypeMapping` the\n * caller passes in.\n * - **wiring** — the worker protocol and the transport. Belongs to each plugin.\n *\n * What is NOT here is anything that knows a STORAGE LAYOUT. `entityResultColumns` lives in\n * `@routier/sql-plugin-core` because \"one JSON column per nested subtree, named for its root\" is a\n * fact about flat tables, not about the data model.\n *\n * Deliberately NOT in `core/src/plugins/wire/`: that module's contract is plain JSON for crossing\n * a trust boundary over HTTP, and a transferable only moves in-process.\n *\n * The encoder is import-light on purpose — a worker file ships as its own bundle, so everything it\n * pulls from core is bundled into it. `plan` adds one runtime import, the `SchemaTypes` enum, whose\n * own module is type-only imports throughout.\n */\nexport * from './types';\nexport * from './ChunkEncoder';\nexport * from './decoder';\nexport * from './plan';\n"],"names":["TRANSFER_VERSION","CHUNK_ROWS","nullByteLength","rowCount","assertTransferVersion","version","Error","assertColumnLayout","plan","names","planned","column","matches","name","index","NULL_BYTES","toEpoch","value","Date","epoch","Number","parsed","TypedFiller","Uint8Array","count","values","Array","i","isNull","transferables","data","nulls","Float64Filler","Float64Array","DateFiller","BooleanFiller","JsonFiller","toText","fromText","text","doc","CloneFiller","seed","asJsonText","asStringifiedJson","JSON","parseJsonText","FILLERS","createFiller","encoding","create","EMPTY_OBJECT","ChunkEncoder","Set","record","Object","undefined","columns","payload","filler","clone","isTransferJsonError","error","jsonError","cause","String","effectiveColumns","chunk","encoded","cacheKey","CACHE_CAPACITY","cache","Map","cached","key","decoder","remember","oldest","clearDecoderCache","rowValue","nulled","literalKey","isTyped","decoderSource","lines","generationSupported","isTransferCodecSupported","Function","buildDecoder","decoderFor","hit","parseJsonColumns","decodeChunk","SchemaTypes","HashType","rawStorageTransferTypes","parsedValueTransferTypes","ownsItsShape","property","transferEncodingFor","strategy","resolve","types","normalize","buildTransferPlan"],"mappings":"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"}
1
+ {"version":3,"file":"transfer/index.js","sources":["webpack://@routier/core/webpack/runtime/define_property_getters","webpack://@routier/core/webpack/runtime/has_own_property","webpack://@routier/core/./src/transfer/types.ts","webpack://@routier/core/./src/transfer/fillers.ts","webpack://@routier/core/./src/transfer/ChunkEncoder.ts","webpack://@routier/core/./src/transfer/decoder.ts","webpack://@routier/core/./src/schema/types.ts","webpack://@routier/core/./src/transfer/plan.ts","webpack://@routier/core/./src/transfer/index.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","/**\n * The wire format for query results crossing an in-process worker boundary.\n *\n * `postMessage` with no transfer list structured-clones everything, and a clone of a large row\n * array is paid twice — once serialising in the worker, once deserialising on the main thread,\n * where it blocks. Columnar typed arrays are transferred instead: the buffer changes owner and\n * nothing is copied.\n *\n * Nothing here knows about SQL, schemas, or workers. A column's values go in and\n * `{ payload, transferables }` comes out; the caller owns the transport. A transport with no\n * transfer list may ignore the array — the payload then clones correctly on its own.\n */\n\n/**\n * The COMPLETE layout: chunk size, bitmap semantics, JSON joining, and the framing above.\n *\n * Both sides reject a version they do not know rather than guessing. The worker ships as its own\n * bundle, so the two halves can be built from different sources.\n */\nexport const TRANSFER_VERSION = 1;\n\n/**\n * Rows per chunk.\n *\n * Chunking is what lets the main thread decode chunk *k* while the worker fills *k+1*, so the\n * first rows arrive in about 2ms at any result size instead of after the whole clone. Measured\n * best at 4,096: 8,192 is within noise, 25,000 is measurably worse.\n */\nexport const CHUNK_ROWS = 4096;\n\n/**\n * How one result column crosses the boundary.\n *\n * Each encoding names a VALUE shape, never an engine. An engine that hands back a date as ISO\n * text and one that hands back a `Date` both use `date-f64`; the filler accepts either. What an\n * engine cannot produce, it simply does not choose.\n */\nexport type TransferEncoding =\n /** `Float64Array` + null bitmap, transferred. */\n | 'float64'\n /**\n * Epoch ms in a `Float64Array` + null bitmap, transferred.\n *\n * Accepts a `Date`, an epoch number, or a parseable date string — whichever the engine\n * returns. Decode emits a `Date`.\n */\n | 'date-f64'\n /** `Uint8Array` of 0/1 + null bitmap, transferred; accepts 0/1 or a boolean; decode emits `true`/`false`. */\n | 'boolean-byte'\n /**\n * Values that are ALREADY JSON text, joined into ONE document per chunk; decode parses once.\n *\n * For an engine that stores a nested structure as text and returns it that way.\n */\n | 'json'\n /**\n * Values that are live objects, `JSON.stringify`d as they are collected, then joined and\n * parsed like `json`.\n *\n * For an engine that returns a nested structure already parsed — a document store, or a\n * driver with a JSON type parser. Crosses the wire as a `json` column, so it needs no\n * separate decoder.\n *\n * Whether this beats `clone` for a given payload is NOT measured. What was measured is that\n * once a value IS text, crossing it as text and parsing once per chunk beats parsing in the\n * worker and cloning the tree (25.3ms against 38.5ms at 20,000 rows). Deep, repetitive\n * structures are the promising case; a small flat object is likely a wash.\n */\n | 'json-stringify'\n /** Plain array of raw values, structured-cloned as they are. */\n | 'clone';\n\nexport type TransferColumn = {\n /** Exact name the engine returns, including projection and join aliases. */\n readonly name: string;\n readonly encoding: TransferEncoding;\n};\n\nexport type TransferPlan = {\n readonly version: typeof TRANSFER_VERSION;\n readonly columns: readonly TransferColumn[];\n};\n\nexport type EncodedColumn =\n | { readonly encoding: 'float64'; readonly data: Float64Array; readonly nulls: Uint8Array }\n | { readonly encoding: 'date-f64'; readonly data: Float64Array; readonly nulls: Uint8Array }\n | { readonly encoding: 'boolean-byte'; readonly data: Uint8Array; readonly nulls: Uint8Array }\n /** `'[' + rowTexts.join(',') + ']'`; a null row contributes the text `null`. */\n | { readonly encoding: 'json'; readonly doc: string }\n | { readonly encoding: 'clone'; readonly data: readonly unknown[] };\n\nexport type EncodedChunk = {\n readonly version: typeof TRANSFER_VERSION;\n /**\n * Rows in THIS chunk. The final chunk may be short, and is only 0 for the single chunk of a\n * zero-row result.\n */\n readonly rowCount: number;\n /** Keyed by column name. Exactly one entry per plan column. */\n readonly columns: Record<string, EncodedColumn>;\n};\n\n/**\n * One chunk and the buffers its transport may hand over rather than copy.\n *\n * The list is separate from the payload on purpose — it is an instruction to the transport, not\n * data, and embedding it would make the payload describe its own framing.\n */\nexport type EncodedTransfer = {\n readonly payload: EncodedChunk;\n readonly transferables: readonly ArrayBufferLike[];\n};\n\n/** Bytes of null bitmap for `rowCount` rows: one bit per row, LSB-first. */\nexport const nullByteLength = (rowCount: number): number => (rowCount + 7) >> 3;\n\n/**\n * Rejects a version this build cannot read.\n *\n * Never attempt to decode an unknown version. Every field's meaning is version-scoped, so a\n * layout that merely looks close would be read wrongly and silently.\n */\nexport const assertTransferVersion = (version: number): void => {\n if (version !== TRANSFER_VERSION) {\n throw new Error(`transfer codec version ${version} is not supported`);\n }\n};\n\n/**\n * Rejects a plan that does not describe the result the engine is about to produce.\n *\n * OPTIONAL, for an engine that can report the fields of a result before yielding rows. Encoding\n * against a wrong layout would put each field's values under another field's name and report\n * nothing, so a caller that CAN check should. An engine whose records are heterogeneous has\n * nothing to check against and skips it — the plan decides the result shape there.\n *\n * Checked once per result, never per row.\n */\nexport const assertColumnLayout = (plan: TransferPlan, names: readonly string[]): void => {\n const planned = plan.columns.map(column => column.name);\n\n const matches = planned.length === names.length\n && planned.every((name, index) => name === names[index]);\n\n if (matches === false) {\n throw new Error(\n `The transfer plan does not match the result columns. ` +\n `Plan: [${planned.join(', ')}]. Result: [${names.join(', ')}].`\n );\n }\n};\n","import { CHUNK_ROWS, EncodedColumn, nullByteLength, TransferEncoding } from './types';\n\n/**\n * One column being filled, one strategy per encoding.\n *\n * `set` answers `false` rather than coercing a value that does not belong in its encoding. A\n * schema type does not prove what an engine will actually return — custom serializers,\n * migrations and external writers can put anything in a field — so the encoder validates every\n * value it writes instead of trusting the plan. The caller turns a refusal into a fallback to\n * `clone`.\n */\nexport interface ColumnFiller {\n readonly encoding: TransferEncoding;\n set(index: number, value: unknown): boolean;\n /** Rows `0..count` as raw values, which is what a fallback to `clone` has to carry forward. */\n drain(count: number): unknown[];\n emit(rowCount: number, transferables: ArrayBufferLike[]): EncodedColumn;\n /** Readies the filler for the next chunk. Transfer detaches the buffers, so they are replaced. */\n reset(): void;\n}\n\nconst NULL_BYTES = nullByteLength(CHUNK_ROWS);\n\n/**\n * Epoch milliseconds for any shape an engine returns a date in, or `null` for anything else.\n *\n * An invalid `Date` and an unparseable string both answer `null` rather than writing `NaN`: a\n * date that cannot be represented is a value this encoding has no answer for, and the column\n * falls back so the raw value survives.\n */\nconst toEpoch = (value: unknown): number | null => {\n if (value instanceof Date) {\n const epoch = value.getTime();\n\n return Number.isNaN(epoch) ? null : epoch;\n }\n\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value : null;\n }\n\n if (typeof value !== 'string') {\n return null;\n }\n\n const parsed = Date.parse(value);\n\n return Number.isNaN(parsed) ? null : parsed;\n};\n\nabstract class TypedFiller<TData extends Float64Array | Uint8Array> implements ColumnFiller {\n\n abstract readonly encoding: TransferEncoding;\n\n protected data: TData;\n protected nulls: Uint8Array;\n\n constructor() {\n this.data = this.allocate();\n this.nulls = new Uint8Array(NULL_BYTES);\n }\n\n protected abstract allocate(): TData;\n\n /** Writes a non-null value, or answers false to send the column to `clone`. */\n protected abstract write(index: number, value: unknown): boolean;\n\n /** The value a `clone` fallback should carry for an already-written row. */\n protected abstract raw(index: number): unknown;\n\n set(index: number, value: unknown): boolean {\n if (value == null) {\n this.nulls[index >> 3] |= 1 << (index & 7);\n this.data[index] = 0;\n return true;\n }\n\n return this.write(index, value);\n }\n\n drain(count: number): unknown[] {\n const values: unknown[] = new Array(count);\n\n for (let i = 0; i < count; i++) {\n const isNull = (this.nulls[i >> 3] & (1 << (i & 7))) !== 0;\n\n values[i] = isNull ? null : this.raw(i);\n }\n\n return values;\n }\n\n emit(rowCount: number, transferables: ArrayBufferLike[]): EncodedColumn {\n // `subarray` would not do: transfer moves the whole underlying buffer, so a short final\n // chunk has to be copied to its exact size. A full chunk is already exact.\n const data = (rowCount === CHUNK_ROWS ? this.data : this.data.slice(0, rowCount)) as TData;\n const nulls = rowCount === CHUNK_ROWS ? this.nulls : this.nulls.slice(0, nullByteLength(rowCount));\n\n transferables.push(data.buffer, nulls.buffer);\n\n return { encoding: this.encoding, data, nulls } as EncodedColumn;\n }\n\n reset(): void {\n this.data = this.allocate();\n this.nulls = new Uint8Array(NULL_BYTES);\n }\n}\n\n/**\n * Numbers, including `NaN` and the infinities — those are legitimate JS numbers and pass through.\n *\n * No `BigInt64Array`. Routier only ever writes JS numbers, so `Float64Array` round-trips\n * everything it stored; a `bigint` or a wide integer type can only come from something else, and\n * that column falls back rather than losing precision silently.\n */\nclass Float64Filler extends TypedFiller<Float64Array> {\n\n readonly encoding = 'float64' as const;\n\n protected allocate(): Float64Array {\n return new Float64Array(CHUNK_ROWS);\n }\n\n protected write(index: number, value: unknown): boolean {\n if (typeof value !== 'number') {\n return false;\n }\n\n this.data[index] = value;\n\n return true;\n }\n\n protected raw(index: number): unknown {\n return this.data[index];\n }\n}\n\n/**\n * Epoch milliseconds, whatever shape the engine returned the date in.\n *\n * All three shapes are accepted because engines genuinely differ: one that stores a date as text\n * returns a string, one that parses before returning gives a `Date`, and one that stores a\n * timestamp gives a number. Requiring text would silently drop the whole encoding for every\n * engine of the other two kinds — they would fall back to `clone` on every row.\n *\n * The final entity shape needs a `Date` either way, so converting here means the wire carries\n * eight bytes and the main thread builds the `Date` from them.\n */\nclass DateFiller extends TypedFiller<Float64Array> {\n\n readonly encoding = 'date-f64' as const;\n\n protected allocate(): Float64Array {\n return new Float64Array(CHUNK_ROWS);\n }\n\n protected write(index: number, value: unknown): boolean {\n const epoch = toEpoch(value);\n\n if (epoch == null) {\n return false;\n }\n\n this.data[index] = epoch;\n\n return true;\n }\n\n /**\n * A `Date`, not the epoch number.\n *\n * A fallback column is decoded raw, and the rows already written can no longer produce their\n * ISO text. A `Date` is what the decoder would have emitted for them, and it is also what\n * the existing date deserializer passes through untouched — an epoch number would reach the\n * entity as a number.\n */\n protected raw(index: number): unknown {\n return new Date(this.data[index]);\n }\n}\n\n/**\n * 0/1 bytes.\n *\n * Both shapes are accepted: an engine with no boolean type returns 0 or 1, one with a boolean\n * type returns a boolean. Requiring either would drop the encoding entirely for engines of the\n * other kind. Both decode to `true`/`false`, so the result is identical.\n */\nclass BooleanFiller extends TypedFiller<Uint8Array> {\n\n readonly encoding = 'boolean-byte' as const;\n\n protected allocate(): Uint8Array {\n return new Uint8Array(CHUNK_ROWS);\n }\n\n protected write(index: number, value: unknown): boolean {\n if (value === 0 || value === 1) {\n this.data[index] = value;\n return true;\n }\n\n if (typeof value !== 'boolean') {\n return false;\n }\n\n this.data[index] = value ? 1 : 0;\n\n return true;\n }\n\n protected raw(index: number): unknown {\n return this.data[index];\n }\n}\n\n/**\n * JSON text, joined into one document per chunk so the main thread parses once per column\n * instead of once per row.\n *\n * The join is valid because each element is a complete JSON document and `null` is valid JSON.\n * The text is NOT validated here; that would be the second parse this exists to avoid. Text that\n * is not JSON poisons the chunk's document and the decoder reports it (see `decodeChunk`).\n */\nclass JsonFiller implements ColumnFiller {\n\n readonly encoding = 'json' as const;\n\n private texts: string[] = [];\n\n /**\n * Which rows were null, so a fallback to `clone` can carry `null` rather than the text\n * `'null'` the document holds. Sparse — only null rows are written.\n */\n private nulls: boolean[] = [];\n\n /**\n * @param toText The JSON text for one non-null value, or `null` to send the column to\n * `clone`. This is the whole difference between an engine that returns text and one that\n * returns a live object.\n * @param fromText Recovers the value the engine originally gave, for a fallback to `clone`.\n * The stored text is all that is kept, so a filler whose input was NOT text has to reverse\n * its own conversion — otherwise the rows written before the fallback would change type,\n * coming back as text while every row after it comes back as an object.\n */\n constructor(\n private readonly toText: (value: unknown) => string | null,\n private readonly fromText: (text: string) => unknown\n ) { }\n\n set(index: number, value: unknown): boolean {\n if (value == null) {\n this.texts[index] = 'null';\n this.nulls[index] = true;\n return true;\n }\n\n const text = this.toText(value);\n\n if (text == null) {\n return false;\n }\n\n this.texts[index] = text;\n\n return true;\n }\n\n drain(count: number): unknown[] {\n const values: unknown[] = new Array(count);\n\n for (let i = 0; i < count; i++) {\n values[i] = this.nulls[i] === true ? null : this.fromText(this.texts[i]);\n }\n\n return values;\n }\n\n emit(rowCount: number): EncodedColumn {\n // Joined with a single ',' and nothing else, which is the whole of the format.\n const doc = `[${this.texts.slice(0, rowCount).join(',')}]`;\n\n return { encoding: this.encoding, doc };\n }\n\n reset(): void {\n this.texts = [];\n this.nulls = [];\n }\n}\n\n/**\n * Raw values in a plain array, structured-cloned as they are.\n *\n * Strings live here and are never encoded. `TextEncoder` loses to clone by a wide margin —\n * 14.0ms against 8.3ms for 4,000 rows of 2KB text — because cloning a V8 string is a native\n * memcpy.\n */\nexport class CloneFiller implements ColumnFiller {\n\n readonly encoding = 'clone' as const;\n\n private data: unknown[];\n\n constructor(seed: unknown[] = []) {\n this.data = seed;\n }\n\n set(index: number, value: unknown): boolean {\n this.data[index] = value;\n\n return true;\n }\n\n drain(count: number): unknown[] {\n return this.data.slice(0, count);\n }\n\n emit(rowCount: number): EncodedColumn {\n return { encoding: this.encoding, data: this.data.slice(0, rowCount) };\n }\n\n reset(): void {\n this.data = [];\n }\n}\n\n/** A value that is already JSON text is passed straight through; anything else is not text. */\nconst asJsonText = (value: unknown): string | null =>\n typeof value === 'string' ? value : null;\n\n/**\n * A live value becomes text here.\n *\n * `JSON.stringify` throws on a circular structure and on a `bigint`, and returns `undefined` for\n * a value that is not representable at all — a function, or a lone `undefined`. All three send the\n * column to `clone`, which is the same answer every other filler gives a value it cannot encode.\n */\nconst asStringifiedJson = (value: unknown): string | null => {\n try {\n return JSON.stringify(value) ?? null;\n } catch {\n return null;\n }\n};\n\n/**\n * Back to a value, for a `json-stringify` column that fell back.\n *\n * The text came from `JSON.stringify` on this same value, so it parses. A `catch` returning the\n * text is there only so a fallback — already the unhappy path — cannot throw.\n */\nconst parseJsonText = (text: string): unknown => {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n};\n\nconst FILLERS: Record<TransferEncoding, () => ColumnFiller> = {\n 'float64': () => new Float64Filler(),\n 'date-f64': () => new DateFiller(),\n 'boolean-byte': () => new BooleanFiller(),\n // The engine gave text, so the text IS the raw value a fallback should carry.\n 'json': () => new JsonFiller(asJsonText, text => text),\n // The engine gave a live object, so a fallback has to parse the text back into one.\n 'json-stringify': () => new JsonFiller(asStringifiedJson, parseJsonText),\n 'clone': () => new CloneFiller(),\n};\n\nexport const createFiller = (encoding: TransferEncoding): ColumnFiller => {\n const create = FILLERS[encoding];\n\n if (create == null) {\n throw new Error(`transfer encoding '${encoding}' is not supported`);\n }\n\n return create();\n};\n","import { ColumnFiller, CloneFiller, createFiller } from './fillers';\nimport {\n assertTransferVersion,\n CHUNK_ROWS,\n EncodedColumn,\n EncodedTransfer,\n TransferColumn,\n TransferPlan,\n TRANSFER_VERSION,\n} from './types';\n\n/** Probe for names the prototype chain answers for — `__proto__`, `toString`, `constructor`. */\nconst EMPTY_OBJECT: Record<string, unknown> = {};\n\n/**\n * Fills one chunk at a time from row values, and emits it with the buffers its transport can hand\n * over.\n *\n * One encoder per result, not per chunk: a column that falls back to `clone` stays there for\n * every later chunk, and a forward-only cursor cannot rewind to re-encode what it already yielded.\n *\n * Usage is a loop — `appendRow` or `appendRecord` until `isFull`, `take`, repeat, then `take` once\n * more for the short final chunk. A zero-row result takes exactly one chunk with `rowCount: 0`.\n */\nexport class ChunkEncoder {\n\n private readonly columns: readonly TransferColumn[];\n private readonly fillers: ColumnFiller[];\n private readonly inheritedNames: boolean;\n private rows = 0;\n\n constructor(plan: TransferPlan) {\n assertTransferVersion(plan.version);\n\n if (plan.columns.length === 0) {\n throw new Error('A transfer plan needs at least one column; a result with no columns has no rows to encode.');\n }\n\n const names = new Set<string>();\n\n for (const column of plan.columns) {\n if (names.has(column.name)) {\n // One entry per column name in the emitted chunk, so two columns sharing a name\n // would silently keep only the second. The caller aliases them instead.\n throw new Error(`A transfer plan names the column '${column.name}' more than once.`);\n }\n\n names.add(column.name);\n }\n\n this.columns = plan.columns;\n this.fillers = plan.columns.map(column => createFiller(column.encoding));\n this.inheritedNames = plan.columns.some(column => column.name in EMPTY_OBJECT);\n }\n\n /** Rows in the chunk being filled. */\n get rowCount(): number {\n return this.rows;\n }\n\n get isFull(): boolean {\n return this.rows === CHUNK_ROWS;\n }\n\n /** The column names, in the order a row's values must arrive in. */\n get columnNames(): readonly string[] {\n return this.columns.map(column => column.name);\n }\n\n /**\n * Adds one row, its values in plan column order.\n *\n * A value that does not belong in its column's encoding sends that column to `clone` for the\n * rest of the result, carrying the rows already written with it. Nothing is coerced into a\n * typed array.\n */\n appendRow(values: readonly unknown[]): void {\n if (this.isFull) {\n throw new Error(`The chunk is full at ${CHUNK_ROWS} rows; take it before appending another.`);\n }\n\n if (values.length !== this.fillers.length) {\n throw new Error(\n `A row carried ${values.length} values for ${this.fillers.length} planned columns.`\n );\n }\n\n const index = this.rows;\n\n for (let i = 0; i < this.fillers.length; i++) {\n if (this.fillers[i].set(index, values[i]) === false) {\n this.fallBack(i, index, values[i]);\n }\n }\n\n this.rows = index + 1;\n }\n\n /**\n * Adds one row from a NAME-KEYED record, reading each planned column out of it.\n *\n * For an engine that yields records rather than positional tuples — a document store, a\n * key-value store, a driver that returns row objects. Projecting to an array is the caller's\n * alternative, and getting that order wrong is silent corruption rather than an error, so the\n * mapping belongs here once instead of in every plugin.\n *\n * A column the record does not carry is `null`, not an error. Records are legitimately\n * heterogeneous outside a fixed-schema table, and the plan is what decides the result shape.\n */\n appendRecord(record: Record<string, unknown>): void {\n const values: unknown[] = new Array(this.columns.length);\n\n for (let i = 0; i < this.columns.length; i++) {\n values[i] = this.readField(record, this.columns[i].name);\n }\n\n this.appendRow(values);\n }\n\n /**\n * A plain read, unless a planned name is one the prototype chain answers for.\n *\n * `record['__proto__']` on an object literal returns `Object.prototype` rather than\n * `undefined`, and `toString` returns a function — either would be encoded as a value. The\n * own-property test that avoids it costs a call per field, so it is only taken when a name in\n * this plan actually needs it.\n */\n private readField(record: Record<string, unknown>, name: string): unknown {\n if (this.inheritedNames) {\n return Object.prototype.hasOwnProperty.call(record, name) ? record[name] : null;\n }\n\n const value = record[name];\n\n // `null`, not `undefined`. An absent field and one holding `undefined` mean the same thing\n // here, and a `clone` column would otherwise carry `undefined` all the way to the entity —\n // a decoded row says `null` for an absent value in every other encoding.\n return value === undefined ? null : value;\n }\n\n /**\n * Emits the filled chunk and readies the encoder for the next one.\n *\n * Transfer DETACHES the emitted buffers, so the fillers allocate fresh arrays here rather\n * than reusing them. Reading a chunk's typed arrays after this is a use-after-transfer.\n */\n take(): EncodedTransfer {\n const transferables: ArrayBufferLike[] = [];\n // `Object.create(null)`, because a column named `__proto__` assigned onto a plain object\n // reaches `Object.prototype`'s setter and never becomes an own property — the column then\n // survives same-realm through the getter and vanishes the moment the chunk is cloned.\n const columns = Object.create(null) as Record<string, EncodedColumn>;\n\n for (let i = 0; i < this.columns.length; i++) {\n columns[this.columns[i].name] = this.fillers[i].emit(this.rows, transferables);\n }\n\n const payload = { version: TRANSFER_VERSION, rowCount: this.rows, columns } as const;\n\n this.rows = 0;\n\n for (const filler of this.fillers) {\n filler.reset();\n }\n\n return { payload, transferables };\n }\n\n private fallBack(column: number, index: number, value: unknown): void {\n const clone = new CloneFiller(this.fillers[column].drain(index));\n\n clone.set(index, value);\n\n this.fillers[column] = clone;\n }\n}\n","import {\n assertTransferVersion,\n EncodedChunk,\n TransferColumn,\n TransferEncoding,\n TransferPlan,\n} from './types';\n\n/**\n * Turning a chunk back into row objects, with a generated function rather than a loop over\n * column descriptors.\n *\n * A reflective loop assigning `row[column.name]` onto a fresh object builds dictionary-mode\n * objects and measured 2.3-4x slower — most of the codec's win is here. A generated function\n * emits one object literal per row, in plan column order, so every row of a result shares one\n * hidden class. This is the same technique `core/src/codegen/handlers` uses for `clone`, `hash`\n * and `compare`, and not the same registry: a chunk layout is a result shape, and joins,\n * projections and `RETURNING` layouts have no schema behind them.\n */\n\n/** A generated decoder. `json` carries one parsed array per JSON column, by column index. */\ntype ChunkDecoder = (chunk: EncodedChunk, json: readonly unknown[][]) => unknown[];\n\n/**\n * A chunk's JSON document did not parse.\n *\n * Reported as its own shape so a caller can retry the request WITHOUT a plan and get today's\n * clone path, which parses row by row and tolerates a field holding text that is not JSON.\n * Every other decode failure is a real error.\n *\n * Read structurally rather than with `instanceof`: an error constructed in another realm — Jest\n * gives each test file its own — fails the prototype test for exactly the cases this classifies.\n */\nexport type TransferJsonError = Error & { readonly transferJsonColumn: string };\n\nexport const isTransferJsonError = (error: unknown): error is TransferJsonError =>\n typeof (error as { transferJsonColumn?: unknown } | null)?.transferJsonColumn === 'string';\n\nconst jsonError = (column: string, cause: unknown): TransferJsonError =>\n Object.assign(\n new Error(\n `The transferred JSON document for column '${column}' did not parse, so this column holds ` +\n `text that is not JSON. Retry the request without a transfer plan. Cause: ` +\n `${(cause as Error)?.message ?? String(cause)}`\n ),\n { transferJsonColumn: column }\n );\n\n/**\n * What each column's encoding turned out to be, which is NOT always what the plan asked for.\n *\n * A column that met a value it could not type fell back to `clone` in the worker, and said so on\n * the chunk. The chunk's tag is the truth; the plan only fixes the order and the names.\n */\ntype EffectiveColumn = { readonly name: string; readonly encoding: TransferEncoding };\n\nconst effectiveColumns = (plan: TransferPlan, chunk: EncodedChunk): EffectiveColumn[] =>\n plan.columns.map((column: TransferColumn) => {\n // An own-property test, not a truthiness one: a name like `__proto__` or `toString`\n // resolves to something inherited on a plain object, which would pass a null check and\n // then be decoded as a column.\n if (Object.prototype.hasOwnProperty.call(chunk.columns, column.name) === false) {\n throw new Error(`The transferred chunk has no column '${column.name}', which the plan lists.`);\n }\n\n const encoded = chunk.columns[column.name];\n\n return { name: column.name, encoding: encoded.encoding };\n });\n\n/**\n * The serialized layout IS the key.\n *\n * Content-keyed, never by collection name: a migration changes the columns under one name, one\n * worker serves every database on the page, and joins and projections produce many shapes per\n * collection. Names are quoted so a name containing the separator cannot collide with a\n * different layout — a collision would hand a result the wrong decoder, silently.\n */\nconst cacheKey = (columns: readonly EffectiveColumn[]): string =>\n 'v1|' + columns.map(column => `${JSON.stringify(column.name)}:${column.encoding}`).join('|');\n\n/** Retains a compiled function per entry, so it is bounded. */\nconst CACHE_CAPACITY = 64;\n\nconst cache = new Map<string, ChunkDecoder>();\n\nconst cached = (key: string): ChunkDecoder | undefined => {\n const decoder = cache.get(key);\n\n if (decoder == null) {\n return undefined;\n }\n\n // Re-inserted so the eviction below drops the least recently USED entry, not the oldest.\n cache.delete(key);\n cache.set(key, decoder);\n\n return decoder;\n};\n\nconst remember = (key: string, decoder: ChunkDecoder): void => {\n cache.set(key, decoder);\n\n if (cache.size > CACHE_CAPACITY) {\n const oldest = cache.keys().next();\n\n if (oldest.done === false) {\n cache.delete(oldest.value);\n }\n }\n};\n\n/** Emptied between tests. Not part of the decoding contract. */\nexport const clearDecoderCache = (): void => cache.clear();\n\nconst rowValue = (column: EffectiveColumn, index: number): string => {\n const nulled = `(u${index}[b] & m) !== 0`;\n\n switch (column.encoding) {\n case 'float64':\n return `${nulled} ? null : d${index}[i]`;\n case 'date-f64':\n return `${nulled} ? null : new Date(d${index}[i])`;\n case 'boolean-byte':\n return `${nulled} ? null : d${index}[i] !== 0`;\n case 'json':\n return `j${index}[i]`;\n case 'clone':\n return `d${index}[i]`;\n }\n};\n\n/**\n * The key to write in the emitted object literal.\n *\n * A quoted `\"__proto__\"` in an object literal sets the row's prototype instead of defining a\n * property (Annex B.3.1), so that one name needs a computed key. Every other name keeps the\n * constant form: computed keys throughout measured 9% slower over a 4,096-row chunk, and decode\n * speed is what this whole module is for.\n */\nconst literalKey = (name: string): string =>\n name === '__proto__' ? `[${JSON.stringify(name)}]` : JSON.stringify(name);\n\nconst isTyped = (encoding: TransferEncoding): boolean =>\n encoding === 'float64' || encoding === 'date-f64' || encoding === 'boolean-byte';\n\nconst decoderSource = (columns: readonly EffectiveColumn[]): string => {\n const lines: string[] = ['\"use strict\";', 'var n = chunk.rowCount;', 'var rows = new Array(n);'];\n\n columns.forEach((column, index) => {\n if (column.encoding === 'json') {\n lines.push(`var j${index} = json[${index}];`);\n return;\n }\n\n lines.push(`var c${index} = chunk.columns[${JSON.stringify(column.name)}];`);\n lines.push(`var d${index} = c${index}.data;`);\n\n if (isTyped(column.encoding)) {\n lines.push(`var u${index} = c${index}.nulls;`);\n }\n });\n\n lines.push('for (var i = 0; i < n; i++) {');\n lines.push('var b = i >> 3, m = 1 << (i & 7);');\n // One object literal per row, properties in plan order, so all rows share a hidden class.\n lines.push('rows[i] = {');\n\n columns.forEach((column, index) => {\n lines.push(`${literalKey(column.name)}: ${rowValue(column, index)},`);\n });\n\n lines.push('};');\n lines.push('}');\n lines.push('return rows;');\n\n return lines.join('\\n');\n};\n\n/**\n * Whether this environment allows generated functions at all.\n *\n * `new Function` needs `unsafe-eval`, which a Content-Security-Policy can withhold. A caller\n * asks once at startup and stops sending plans if the answer is no, which falls back to the\n * transport's ordinary clone — a known-good path. There is deliberately no reflective decoder to\n * fall back to: one was measured and never beat the clone it would replace.\n */\nlet generationSupported: boolean | null = null;\n\nexport const isTransferCodecSupported = (): boolean => {\n if (generationSupported == null) {\n try {\n new Function('return 1')();\n generationSupported = true;\n } catch {\n generationSupported = false;\n }\n }\n\n return generationSupported;\n};\n\nconst buildDecoder = (columns: readonly EffectiveColumn[]): ChunkDecoder =>\n new Function('chunk', 'json', decoderSource(columns)) as ChunkDecoder;\n\nconst decoderFor = (columns: readonly EffectiveColumn[]): ChunkDecoder => {\n const key = cacheKey(columns);\n const hit = cached(key);\n\n if (hit != null) {\n return hit;\n }\n\n const decoder = buildDecoder(columns);\n\n remember(key, decoder);\n\n return decoder;\n};\n\n/**\n * Parsed outside the generated function, so a failure can name its column.\n *\n * One parse per JSON column per chunk replaces one per row — worth about 16% of the codec's\n * total win, and it stacks with chunking.\n */\nconst parseJsonColumns = (columns: readonly EffectiveColumn[], chunk: EncodedChunk): unknown[][] => {\n const parsed: unknown[][] = new Array(columns.length);\n\n columns.forEach((column, index) => {\n if (column.encoding !== 'json') {\n return;\n }\n\n const encoded = chunk.columns[column.name];\n const doc = encoded.encoding === 'json' ? encoded.doc : '[]';\n\n try {\n parsed[index] = JSON.parse(doc) as unknown[];\n } catch (error) {\n throw jsonError(column.name, error);\n }\n });\n\n return parsed;\n};\n\n/**\n * Decodes one chunk into final-shape row objects — real booleans, `Date` objects, parsed JSON.\n *\n * Not the raw storage shape. The entity needs the final shape either way, and decoding to raw and\n * re-shaping afterwards measured slower (156ms against 140ms at 100,000 rows). An absent or null\n * value becomes JavaScript `null`, never `undefined` and never an absent property.\n *\n * A column that fell back to `clone` in the worker comes back RAW, and the caller still owes it\n * whatever shaping that column would otherwise have had.\n */\nexport const decodeChunk = (plan: TransferPlan, chunk: EncodedChunk): unknown[] => {\n assertTransferVersion(plan.version);\n assertTransferVersion(chunk.version);\n\n const columns = effectiveColumns(plan, chunk);\n\n return decoderFor(columns)(chunk, parseJsonColumns(columns, chunk));\n};\n","import type { SchemaDefinition } from \"./SchemaDefinition\";\nimport type { SchemaBase } from \"./property/base/SchemaBase\";\nimport type { SchemaArray } from \"./property/types/SchemaArray\";\nimport type { SchemaVector } from \"./property/types/SchemaVector\";\nimport type { SchemaObject } from \"./property/types/SchemaObject\";\nimport type { PropertyInfo } from \"./PropertyInfo\";\nimport type { DeepPartial } from \"../types\";\nimport type { SchemaFunction } from \"./table\";\nimport type { SchemaOptional, SchemaTag, SchemaNullable } from \"./property/modifiers\";\nimport type { Branded } from \"../utilities/types\";\nimport type { SchemaSubscriptionOptions } from \"./communication/broadcast\";\n\nexport type DefaultValue<T, I = never> = T | ((injected: I) => T);\nexport type FunctionBody<TEntity, TResult> = (entity: TEntity, collectionName: CollectionName) => TResult;\nexport type IdType = string | number;\nexport type ForeignKey<T extends {}> = { \n schema: CompiledSchema<T>, \n property: PropertyInfo<T> \n};\n\nexport enum SchemaTypes {\n Array = \"Array\",\n Boolean = \"Boolean\",\n Date = \"Date\",\n Number = \"Number\",\n Object = \"Object\",\n String = \"String\",\n Definition = \"Definition\",\n Function = \"Function\",\n Computed = \"Computed\",\n /**\n * Content in, reference out. The only type whose write shape differs from its stored\n * shape, and a leaf on purpose — see `SchemaFile`.\n */\n File = \"File\",\n /**\n * A fixed-length list of numbers, carrying its dimension count — see `SchemaVector`.\n *\n * Value-shaped exactly like `s.array(s.number())`, which is why every array codegen\n * handler accepts it. It is a distinct type only so a backend can recognise it and store\n * it natively; nothing else needs to tell the two apart.\n */\n Vector = \"Vector\"\n}\n\nexport type ArrayShape = string | number | Date | {};\n\n\n/**\n * What a file property gives back: where the bytes are and what they are.\n *\n * Declared in core so `InferType` can name it. Core never reads or writes the bytes — it only\n * carries this shape — and `@routier/blob-plugin` is what puts one here.\n */\nexport type FileReferenceValue = {\n /** Where the bytes live, content-addressed by the blob plugin. */\n key: string;\n /** Byte length. */\n size: number;\n /** Media type as supplied at upload. */\n contentType: string;\n /** SHA-256 of the bytes, lowercase hex. */\n checksum: string;\n /** The name to show a user. Not part of the key. */\n fileName: string;\n};\n\n/**\n * What a file property ACCEPTS: content, or a reference you already have.\n *\n * `Blob` covers `File`, which is what an `<input type=\"file\">` yields. A reference is accepted\n * too, so re-saving an entity that was read from the database does not have to re-upload it.\n */\nexport type FileContentValue =\n | FileReferenceValue\n | Uint8Array\n | ArrayBuffer\n | Blob\n | string;\n\n/**\n * What a vector property holds, in and out: a plain list of numbers.\n *\n * Named rather than written inline because the inference rules have to recognise it after a\n * modifier has erased which class produced it — the same problem `FileReferenceValue` solves\n * above, and for the same reason.\n */\nexport type VectorValue = number[];\n\n/**\n * What `s.string({ ... })` accepts.\n *\n * Declarations only. Core stores them and never acts on them; a backend that can use one does.\n */\nexport type StringOptions = {\n /**\n * The longest value the property is declared to hold.\n *\n * MySQL uses it for `VARCHAR(maxLength)`; without it every string column is\n * `VARCHAR(255)`, which silently truncates longer values. Other backends ignore it. Core\n * never validates a value against it — see `SchemaBase.maxLength`.\n */\n maxLength?: number;\n};\n\nexport type ExpandedProperty = ExpandedChildProperty & {\n assignmentPath: string;\n selectorPath: string;\n properties: Map<string, ExpandedChildProperty>;\n childDegree: number;\n};\n\nexport type ExpandedChildProperty = {\n propertyName: string;\n type: SchemaTypes;\n isNullableOrOptional: boolean;\n isReadonly: boolean;\n isIdentity: boolean;\n isUnmapped: boolean;\n}\n\nexport enum HashType {\n Ids = \"Ids\",\n Object = \"Object\"\n}\n\nexport type HashFunction<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>, type: HashType.Object): string;\n (entity: InferType<TEntity>, type: HashType.Ids): string;\n}\n\nexport type GetHashTypeFunction<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>): HashType.Object;\n (entity: InferType<TEntity>): HashType.Ids;\n}\n\nexport type ChangeTrackingType = \"proxy\" | \"diff\" | \"immutable\";\n\nexport type IndexType = \"single\" | \"compound\" | \"unique\" | \"primary-key\"\nexport type Index = {\n properties: PropertyInfo<any>[],\n type: IndexType;\n name: string;\n}\n\n/**\n * Represents changes to subscriptions, categorizing them by modifications to\n * entities (additions, updates, removals) or query-driven removals.\n * @template T - The type of the entities in the subscription.\n */\nexport type SubscriptionChanges<T extends {}> = {\n /**\n * Entities that have been added to the subscription.\n */\n adds: InferType<T>[];\n /**\n * Entities that have been updated within the subscription.\n */\n updates: InferType<T>[];\n /**\n * Entities that have been removed from the subscription.\n */\n removals: InferType<T>[];\n /**\n * Entities that have been added/updated/removed from the subscription and it is unknown \n * if the entities have been added/updated/removed.\n */\n unknown: InferType<T>[];\n}\n\nexport interface ISchemaSubscription<T extends {}> extends Disposable {\n send(changes: SubscriptionChanges<T>): void;\n onMessage(callback: (changes: SubscriptionChanges<T>) => void): void;\n}\n\nexport type Enrich<TEntity extends {}> = {\n (entity: InferType<TEntity>, changeTrackingType: ChangeTrackingType): InferType<TEntity>;\n (entity: InferCreateType<TEntity>, changeTrackingType: ChangeTrackingType): InferCreateType<TEntity>;\n}\nexport type Prepare<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>): InferCreateType<TEntity>;\n (entity: InferType<TEntity>): InferType<TEntity>;\n}\nexport type Preprocess<TEntity extends {}> = {\n (entity: InferCreateType<TEntity>): InferType<TEntity>;\n (entity: InferType<TEntity>): InferType<TEntity>;\n}\n\nexport type SetProperties<TEntity extends {}> = (destination: DeepPartial<InferType<TEntity> | InferCreateType<TEntity>>, source: DeepPartial<InferType<TEntity> | InferCreateType<TEntity>>) => void;\n\nexport type CompiledSchemaCore<TEntity extends {}> = Omit<CompiledSchema<TEntity>, \"createSubscription\">;\n\nexport type CompiledSchemaWithMetadata<TEntity extends {}, TMetadata> = {\n readonly metadata: TMetadata;\n} & CompiledSchema<TEntity>;\n\n/**\n * Represents a fully compiled schema with all utilities and metadata for an entity type.\n */\nexport type CompiledSchema<TEntity extends {}> = {\n\n deserializePartial: (item: Record<string, unknown>, properties: PropertyInfo<TEntity>[]) => DeepPartial<InferType<TEntity>>;\n\n createSubscription: (abortSignal?: AbortSignal, scope?: string, options?: SchemaSubscriptionOptions) => ISchemaSubscription<TEntity>;\n /** Returns the property info for a given id (full path) */\n getProperty: (id: string) => PropertyInfo<TEntity>;\n /** Returns the ID of the given entity. */\n getId: (entity: InferType<TEntity>) => IdType;\n /** Returns a deep clone of the given entity. */\n clone: (entity: InferType<TEntity>) => InferType<TEntity>;\n /**\n * Returns a deep clone of a record that is still in the STORAGE shape — renamed properties\n * under their `from` names rather than their in-memory names.\n *\n * `clone` reads in-memory names, so it returns `undefined` for every renamed property of a\n * stored record. Use this when copying rows a store holds before they have been deserialized.\n * Generated on first call; schemas that are never cloned in storage shape never build it.\n */\n cloneStorage: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Removes unmapped or extraneous properties from the entity. */\n strip: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Prepares a new entity for creation, applying defaults and transformations. */\n prepare: Prepare<TEntity>;\n /** Merges the source entity into the destination entity. */\n merge: (destination: InferType<TEntity> | InferCreateType<TEntity>, source: InferType<TEntity>) => InferType<TEntity>;\n /** Indicates if the schema has identity properties. */\n hasIdentities: boolean;\n /** List of properties that are identity keys. */\n idProperties: PropertyInfo<TEntity>[];\n /** All property metadata for the schema. */\n properties: PropertyInfo<TEntity>[],\n /** The hash type used for this schema. */\n hashType: HashType;\n /** Computes a hash for the given entity. */\n hash: HashFunction<TEntity>;\n /** Returns the hash type for the given entity. */\n getHashType: GetHashTypeFunction<TEntity>;\n /** Compares two entities for equality. */\n compare: (a: InferType<TEntity>, fromDb: InferType<TEntity>) => boolean;\n /** Deserializes an entity from storage format. */\n deserialize: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Sets 1 or many properties from the source object onto the destination object with change tracking. */\n set: SetProperties<TEntity>;\n /** Combines serializing and preparing an entity for saving. */\n preprocess: Preprocess<TEntity>;\n /** Combines deserializing and enriching an entity for selection. */\n postprocess: Enrich<TEntity>;\n\n /** Serializes an entity to storage format. */\n serialize: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Unique id for the schema. */\n id: SchemaId,\n /** The name of the collection for this schema. */\n collectionName: CollectionName;\n /** Returns all IDs for the given entity (usually a single-element tuple). */\n getIds: (entity: InferType<TEntity>) => [IdType];\n /** Enriches the entity with change tracking or other metadata. */\n enrich: Enrich<TEntity>;\n /** Indicates if the schema has identity keys. */\n hasIdentityKeys: boolean;\n /** Returns a deeply frozen (immutable) version of the entity. */\n freeze: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** Enables change tracking on the entity. */\n enableChangeTracking: (entity: InferType<TEntity>) => InferType<TEntity>;\n /** The schema definition object. */\n definition: SchemaDefinition<TEntity>;\n /** Returns all indexes defined for this schema. */\n getIndexes: () => Index[];\n /** Compares two entities for Id equality. */\n compareIds: (a: InferType<TEntity>, b: InferType<TEntity>) => boolean;\n}\n\nexport type PropertySerializer<T extends any> = (value: T) => string | number;\nexport type PropertyDeserializer<T extends any> = (value: string | number) => T;\n\n/**\n * A two-way transform between the application value and the stored value.\n *\n * Both directions may be async. Held as a live reference rather than stringified, so a\n * closure works and `injected` is a convenience rather than the only way in.\n */\nexport type PropertyTransform<T extends any> = {\n /**\n * Application value to stored value. Runs before the plugin sees it. May be async.\n *\n * `entity` is there for the one-way case: a transform with no `from` derives a value\n * rather than converting one, which is what `computed` does.\n */\n to: (value: T, entity: Record<string, unknown>) => unknown | Promise<unknown>;\n\n /**\n * Stored value back to application value. Runs after the plugin returns it.\n *\n * Optional. Leave it out and the transform is one-way: the stored value is the value.\n */\n from?: (value: unknown) => T | Promise<T>;\n\n /**\n * What the column becomes, when the stored form is not the property's own type.\n *\n * Defaults to the property's own type, so nothing changes unless you say it does. A\n * library that always produces text — a cipher, a compressor — sets this once, and the\n * caller who uses that library never writes it.\n */\n stores?: SchemaTypes;\n\n /**\n * Whether a filter on this property can still run in the database.\n *\n * Defaults to `none`, which rejects the filter rather than returning wrong rows. Set\n * `equality` only when `to` is deterministic.\n */\n comparable?: 'equality' | 'none';\n};\n\nexport type SchemaId = Branded<number, \"SchemaId\">;\nexport type CollectionName = Branded<string, \"CollectionName\">;\n\nexport type SchemaModifiers = \"default\" | \"deserialize\" |\n \"identity\" | \"key\" |\n \"nullable\" | \"optional\" |\n \"readonly\" | \"serialize\" |\n \"unmapped\" | \"computed\" |\n \"distinct\" | \"searchable\";\n\n/**\n * What a tagged property infers to.\n *\n * `tag()` is metadata and must not change a type, but `SchemaTag<T>` carries the same `T` as\n * whatever it wrapped without carrying which class that was. For a string `T` is already\n * `string`; for an object `T` is the map of child schemas, which only the `SchemaObject`\n * branch below knows how to unwrap. Falling through to the generic `SchemaBase` branch\n * therefore handed the raw map back, so `s.object({ key: s.string() }).tag('x')` typed\n * `key` as `SchemaString` instead of `string` — everything ran, and only the types lied.\n *\n * An array is distinguishable because `SchemaArray`'s parameter is the ELEMENT schema, so a\n * tagged array arrives here as a `SchemaBase` rather than a plain map.\n */\ntype InferTagged<C> = ResolveWrapped<C>;\n\n/**\n * What a wrapping modifier's inner type resolves to.\n *\n * `SchemaOptional`, `SchemaNullable` and `SchemaTag` all carry the same `C` as whatever they\n * wrapped, without carrying which class that was, so each has to work out what it is holding.\n * Three shapes are possible:\n *\n * - an already-resolved value (`string` from `s.string()`, a file reference from `s.file()`)\n * - an ELEMENT schema, which is what `SchemaArray` parameterises on\n * - a map of child schemas, which is what `SchemaObject` parameterises on\n *\n * Getting this wrong is silent. The map branch applied to an already-resolved object walks\n * its keys and infers `never` for each, so `s.file().optional()` typed as\n * `{ key: never, size: never, ... }` — which no value can satisfy and no test would catch at\n * runtime.\n *\n * A map of child schemas goes through `InferCompiledSchema`, not a plain key map, so the\n * children's own `.nullable()`, `.optional()` and `.readonly()` survive the way they do at the\n * top level (#42). That branch cannot take an array — `SchemaObject.array().nullable()` wraps\n * `C = SchemaObject[]` — so an array is resolved element by element before it.\n */\ntype ResolveWrapped<C> =\n C extends string | number | boolean | Date | FileReferenceValue ? C :\n C extends VectorValue ? C :\n C extends SchemaBase<any, any> ? InferPrimitive<C>[] :\n C extends Array<infer E> ? InferPrimitive<E>[] :\n InferCompiledSchema<C>;\n\ntype InferPrimitive<T> =\n T extends SchemaOptional<infer C, infer __> ? ResolveWrapped<C> :\n // `null` itself comes from the parent's `nullable` key partition; this resolves what is\n // underneath. Without it a nullable object fell to the generic `SchemaBase` branch below\n // and typed its children as the raw builder classes.\n T extends SchemaNullable<infer C, infer __> ? ResolveWrapped<C> :\n T extends SchemaTag<infer C, infer __> ? InferTagged<C> :\n // Before the generic `SchemaBase` branch below, which would see `X = number[]` and map\n // the element through `InferPrimitive<number>` — no branch matches a bare `number`, so a\n // vector would type as `never[]`: assignable from nothing, and invisible at runtime.\n T extends SchemaVector<infer __, infer ___> ? VectorValue :\n T extends SchemaArray<infer Y, infer __> ? InferPrimitive<Y>[]\n : T extends SchemaObject<infer Obj, infer _> ?\n InferCompiledSchema<Obj> : // Nested objects keep their children's modifiers\n T extends SchemaFunction<infer F, infer __> ? F : T extends SchemaBase<infer X, infer _> ?\n X extends Array<infer A> ? InferPrimitive<A>[] : X : // Extract the primitive type\n never;\n\nexport type InferType<T> = T extends CompiledSchema<infer R> ? InferCompiledSchema<R> : T extends {} ? InferCompiledSchema<T> : T;\nexport type InferCreateType<T> = T extends CompiledSchema<infer R> ? InferCompiledCreateSchema<R> : T extends {} ? InferCompiledCreateSchema<T> : unknown;\nexport type InferMappedType<T> = T extends SchemaBase<infer K, infer __> ? InferType<K> : InferCompiledSchema<T>;\nexport type InferRoot<T> = T extends CompiledSchema<infer R> ? R : never;\n\ntype HasModifier<T, K extends keyof T, M extends SchemaModifiers> =\n T[K] extends SchemaBase<any, infer Mods> ?\n M extends Mods ? true : false :\n false;\n\ntype IsPlainProperty<T, K extends keyof T> =\n [\n HasModifier<T, K, \"readonly\">,\n HasModifier<T, K, \"optional\">,\n HasModifier<T, K, \"nullable\">\n ] extends [\n false,\n false,\n false\n ] ? true : false;\n\ntype IsCreateExcluded<T, K extends keyof T> =\n [\n HasModifier<T, K, \"identity\">,\n HasModifier<T, K, \"computed\">,\n HasModifier<T, K, \"unmapped\">\n ] extends [\n false,\n false,\n false\n ] ? false : true;\n\ntype IsCreateOptional<T, K extends keyof T> =\n [\n HasModifier<T, K, \"optional\">,\n HasModifier<T, K, \"default\">\n ] extends [\n false,\n false\n ] ? false : true;\n\ntype IsCreateNullable<T, K extends keyof T> =\n HasModifier<T, K, \"nullable\"> extends true ? true : false;\n\n/**\n * What a property ACCEPTS on the way in, which is not always what it gives back.\n *\n * Only a file differs today: you assign content and read a reference. Matching on the read\n * type rather than on `SchemaFile` itself is deliberate — it keeps working through every\n * modifier. `s.file().optional()` is a `SchemaOptional`, `s.file().tag('x')` is a\n * `SchemaTag`, and neither carries the original class, so a check against the class alone\n * would silently stop accepting content the moment anyone added a modifier.\n *\n * Assignability is required in BOTH directions, and the tuple wrappers are load-bearing.\n * One-way `extends` matches `never` — which is assignable to everything — so a generic\n * property over `Record<string, unknown>` resolved to file content and broke the Dexie\n * plugin's types. It also matched any object that merely happens to have these five fields\n * plus more. Mutual assignability admits the reference shape and nothing else, and the\n * tuples stop the conditional distributing over a union.\n */\ntype InferWritePrimitive<T> =\n [InferPrimitive<T>] extends [FileReferenceValue]\n ? [FileReferenceValue] extends [InferPrimitive<T>] ? FileContentValue : InferPrimitive<T>\n : InferPrimitive<T>;\n\ntype InferCreateProperty<T, K extends keyof T> =\n IsCreateNullable<T, K> extends true ? null | InferWritePrimitive<T[K]> : InferWritePrimitive<T[K]>;\n\ntype InferCompiledSchema<T> = CoalesceEmpty<{\n [K in keyof T as IsPlainProperty<T, K> extends true ? K : never]: InferPrimitive<T[K]>\n}, {\n readonly [K in keyof T as HasModifier<T, K, \"readonly\"> extends true ? K : never]: InferPrimitive<T[K]>\n }, {\n [K in keyof T as HasModifier<T, K, \"optional\"> extends true ? K : never]?: InferPrimitive<T[K]>\n }, {\n [K in keyof T as HasModifier<T, K, \"nullable\"> extends true ? K : never]: null | InferPrimitive<T[K]>\n}>;\n\ntype InferCompiledCreateSchema<T> = {\n [K in keyof T as IsCreateExcluded<T, K> extends true ? never\n : IsCreateOptional<T, K> extends true ? K : never]?: InferCreateProperty<T, K>\n} & {\n [K in keyof T as IsCreateExcluded<T, K> extends true ? never\n : IsCreateOptional<T, K> extends true ? never : K]: InferCreateProperty<T, K>\n};\n\ntype IsEmptyObject<T> = keyof T extends never ? true : false;\ntype CoalesceEmpty<T1 extends {}, T2 extends {}, T3 extends {}, T4 extends {}> = (IsEmptyObject<T1> extends true ? {} : T1) & (IsEmptyObject<T2> extends true ? {} : T2) & (IsEmptyObject<T3> extends true ? {} : T3) & (IsEmptyObject<T4> extends true ? {} : T4);\n","import { SchemaTypes } from '../schema/types';\nimport type { PropertyInfo } from '../schema/PropertyInfo';\nimport type { ResultColumn } from '../plugins/resultShape';\nimport { TransferEncoding, TransferPlan, TRANSFER_VERSION } from './types';\n\n/**\n * Deciding how each column of a result crosses the boundary.\n *\n * Separate from the codec beside it because of WHO imports it, not because of what it knows: the\n * encoder is bundled into a worker, and this runs on the main thread only. `SchemaTypes` is the\n * one runtime import — the enum's own module is type-only imports throughout — so a bundler that\n * does pull this in still does not pull in the schema machinery.\n *\n * Nothing here is SQL. A plan is a list of result columns and the property behind each one, and\n * both of those are data-model facts. What varies by engine is which values it hands back — one\n * that stores a date as ISO text needs `Date.parse`, one that parses before returning does not —\n * and that variation is a mapping the caller passes in rather than a table this module owns.\n *\n * The rule throughout is that uncertainty means `clone`. A wrong encoding is not slow, it is\n * wrong, and `clone` is exactly what happens without a plan — so the worst a conservative choice\n * costs is the speed-up it declines.\n */\n\n/**\n * Which encoding each schema type can take on one engine.\n *\n * A type with no entry gets `clone`. That is the safe direction: a missing entry is a type this\n * mapping has not considered, and guessing at one is how a column starts decoding wrongly.\n */\nexport type TransferTypeMapping = Readonly<Partial<Record<SchemaTypes, TransferEncoding>>>;\n\n/**\n * Full control over one column's encoding, for an engine whose answer is not a function of the\n * schema type alone.\n *\n * Returning `undefined` defers to the type mapping, so a resolver can decide the few columns it\n * cares about and leave the rest. It CANNOT override the serializer rule — a property that owns\n * its own storage shape stays on `clone` whatever a resolver says, because that rule is about the\n * schema's contract rather than about the engine.\n */\nexport type TransferEncodingResolver = (column: ResultColumn) => TransferEncoding | undefined;\n\n/**\n * How a caller says which encoding a column takes: a table by schema type, a resolver, or both.\n *\n * The table covers every engine measured so far. The resolver exists because \"the schema type\" is\n * this module's guess at what varies, and an engine is entitled to disagree — a store that keeps\n * one property in a different representation from its siblings has no way to say so in a table\n * keyed by type.\n */\nexport type TransferEncodingStrategy =\n | TransferTypeMapping\n | TransferEncodingResolver\n | { readonly resolve?: TransferEncodingResolver; readonly types?: TransferTypeMapping };\n\n/**\n * For an engine that returns a column as the raw text or number it stored.\n *\n * SQLite is the case this was measured against — a date is TEXT holding ISO-8601 and a boolean is\n * INTEGER holding 0 or 1 — but the mapping is about the STORED shape, not about SQL. Any engine\n * that keeps those encodings uses it; one that parses values before returning them (PGlite) needs\n * its own.\n *\n * `String` is deliberately absent, so strings cross in a plain array. Cloning a V8 string is a\n * native memcpy and encoding one measured slower: 14.0ms against 8.3ms for 4,000 rows of 2KB text.\n */\nexport const rawStorageTransferTypes: TransferTypeMapping = {\n [SchemaTypes.Number]: 'float64',\n [SchemaTypes.Boolean]: 'boolean-byte',\n [SchemaTypes.Date]: 'date-f64',\n [SchemaTypes.Object]: 'json',\n [SchemaTypes.Array]: 'json',\n [SchemaTypes.Vector]: 'json',\n};\n\n/**\n * For an engine that returns values already parsed — a document store, a key-value store holding\n * decoded records, or a driver with type parsers registered.\n *\n * Differs from {@link rawStorageTransferTypes} in ONE place: a nested structure arrives as a live\n * object, so it is stringified on the way out rather than passed through as text. Dates and\n * booleans need no separate entry, because those fillers accept either shape.\n *\n * Whether `json-stringify` beats `clone` for a given payload is unmeasured — see the encoding's\n * own note. An engine unsure of that should map its nested types to `clone` and keep today's\n * behaviour.\n */\nexport const parsedValueTransferTypes: TransferTypeMapping = {\n [SchemaTypes.Number]: 'float64',\n [SchemaTypes.Boolean]: 'boolean-byte',\n [SchemaTypes.Date]: 'date-f64',\n [SchemaTypes.Object]: 'json-stringify',\n [SchemaTypes.Array]: 'json-stringify',\n [SchemaTypes.Vector]: 'json-stringify',\n};\n\n/**\n * True when this layer must not touch the column, whatever its declared type says.\n *\n * A property that serializes, deserializes or transforms itself owns its storage shape, and this\n * has no way to know what that shape is. Handing an already-parsed value to a property carrying\n * `.deserialize(x => JSON.parse(String(x)))` throws, from a schema that was working.\n */\nconst ownsItsShape = (property: PropertyInfo<any>): boolean =>\n property.valueSerializer != null\n || property.valueDeserializer != null\n || property.transform != null\n || property.functionBody != null;\n\n/**\n * The encoding for one column.\n *\n * A schema type alone does not prove what the engine will return — a migration or an external\n * writer can put anything in a column — so this is a starting point the encoder still validates\n * per value.\n */\nexport const transferEncodingFor = (\n column: ResultColumn,\n strategy: TransferEncodingStrategy\n): TransferEncoding => {\n const property = column.property;\n\n // Checked BEFORE the resolver, and not overridable by it. A property carrying its own\n // serializer owns its storage shape; pre-shaping it throws from a schema that was working,\n // and that is true on every engine.\n if (property == null || ownsItsShape(property)) {\n return 'clone';\n }\n\n const { resolve, types } = normalize(strategy);\n\n return resolve?.(column) ?? types?.[property.type] ?? 'clone';\n};\n\nconst normalize = (strategy: TransferEncodingStrategy):\n { resolve?: TransferEncodingResolver; types?: TransferTypeMapping } => {\n\n if (typeof strategy === 'function') {\n return { resolve: strategy };\n }\n\n if ('resolve' in strategy || 'types' in strategy) {\n return strategy as { resolve?: TransferEncodingResolver; types?: TransferTypeMapping };\n }\n\n return { types: strategy as TransferTypeMapping };\n};\n\n/**\n * Builds the plan for an ordered result column list, or `undefined` when there is nothing to plan.\n *\n * `undefined` is not a failure — it means this result takes the ordinary clone path. Two shapes\n * get it:\n *\n * - **No columns.** There is no row to decode.\n * - **A repeated column name.** One chunk carries one entry per name, and a row object holds one\n * value per key, so a result naming a column twice cannot round-trip through a plan. That is a\n * legal result that works without the codec, so it keeps working rather than becoming an error.\n */\nexport const buildTransferPlan = (\n columns: readonly ResultColumn[],\n strategy: TransferEncodingStrategy\n): TransferPlan | undefined => {\n if (columns.length === 0) {\n return undefined;\n }\n\n const names = new Set(columns.map(column => column.name));\n\n if (names.size !== columns.length) {\n return undefined;\n }\n\n return {\n version: TRANSFER_VERSION,\n columns: columns.map(column => ({\n name: column.name,\n encoding: transferEncodingFor(column, strategy),\n })),\n };\n};\n","/**\n * The worker-boundary transfer codec.\n *\n * Not SQL-specific. Any plugin whose engine runs in a worker crosses this boundary for the same\n * reason — `FileSystemFileHandle.createSyncAccessHandle` is undefined on the main thread, so OPFS\n * persistence is only reachable from a worker — and pays the same structured clone for its records.\n * A document store, a key-value store, or anything else compiled to WASM over OPFS is the same\n * problem: many records, each a set of named values, crossing one `postMessage`.\n *\n * What an engine has to supply is small and deliberately so:\n *\n * - **An ordered list of the fields a result carries**, and the schema property behind each one\n * where there is one. Positional (`appendRow`) or name-keyed (`appendRecord`), whichever the\n * engine yields.\n * - **A `TransferEncodingStrategy`** — which encoding each field takes. A table by schema type\n * covers the common case; a resolver covers an engine whose answer is not a function of the type.\n *\n * Every encoding names a VALUE shape rather than an engine, and the fillers accept every shape a\n * value plausibly arrives in: a date as a `Date`, an epoch number, or text; a boolean as a boolean\n * or as 0/1; a nested structure as text (`json`) or as a live object (`json-stringify`). An engine\n * chooses; nothing here assumes.\n *\n * Three layers, and the first two are here:\n *\n * - **codec** (`types`, `ChunkEncoder`, `decoder`) — column values in, `{ payload, transferables }`\n * out. Knows nothing about schemas or workers.\n * - **plan building** (`plan`) — result columns become a `TransferPlan`. Needs `SchemaTypes` and a\n * property's serializers, which are data-model facts, so it belongs here too. What varies by\n * engine is only which values that engine hands back, and that is a `TransferTypeMapping` the\n * caller passes in.\n * - **wiring** — the worker protocol and the transport. Belongs to each plugin.\n *\n * What is NOT here is anything that knows a STORAGE LAYOUT. `entityResultColumns` lives in\n * `@routier/sql-plugin-core` because \"one JSON column per nested subtree, named for its root\" is a\n * fact about flat tables, not about the data model.\n *\n * Deliberately NOT in `core/src/plugins/wire/`: that module's contract is plain JSON for crossing\n * a trust boundary over HTTP, and a transferable only moves in-process.\n *\n * The encoder is import-light on purpose — a worker file ships as its own bundle, so everything it\n * pulls from core is bundled into it. `plan` adds one runtime import, the `SchemaTypes` enum, whose\n * own module is type-only imports throughout.\n */\nexport * from './types';\nexport * from './ChunkEncoder';\nexport * from './decoder';\nexport * from './plan';\n"],"names":["TRANSFER_VERSION","CHUNK_ROWS","nullByteLength","rowCount","assertTransferVersion","version","Error","assertColumnLayout","plan","names","planned","column","matches","name","index","NULL_BYTES","toEpoch","value","Date","epoch","Number","parsed","TypedFiller","Uint8Array","count","values","Array","i","isNull","transferables","data","nulls","Float64Filler","Float64Array","DateFiller","BooleanFiller","JsonFiller","toText","fromText","text","doc","CloneFiller","seed","asJsonText","asStringifiedJson","JSON","parseJsonText","FILLERS","createFiller","encoding","create","EMPTY_OBJECT","ChunkEncoder","Set","record","Object","undefined","columns","payload","filler","clone","isTransferJsonError","error","jsonError","cause","String","effectiveColumns","chunk","encoded","cacheKey","CACHE_CAPACITY","cache","Map","cached","key","decoder","remember","oldest","clearDecoderCache","rowValue","nulled","literalKey","isTyped","decoderSource","lines","generationSupported","isTransferCodecSupported","Function","buildDecoder","decoderFor","hit","parseJsonColumns","decodeChunk","SchemaTypes","HashType","rawStorageTransferTypes","parsedValueTransferTypes","ownsItsShape","property","transferEncodingFor","strategy","resolve","types","normalize","buildTransferPlan"],"mappings":";;;;;AAAA;AACA;AACA;AACA,kDAAkD,wCAAwC;AAC1F;AACA;AACA,E;;;;ACNA,wF;;;;;;;;;;;;;;;;;;;;;;;ACAA;;;;;;;;;;;CAWC,GAED;;;;;CAKC,GACM,MAAMA,mBAAmB,EAAE;AAElC;;;;;;CAMC,GACM,MAAMC,aAAa,KAAK;AAqF/B,0EAA0E,GACnE,MAAMC,iBAAiB,CAACC,WAA8BA,WAAW,KAAM,EAAE;AAEhF;;;;;CAKC,GACM,MAAMC,wBAAwB,CAACC;IAClC,IAAIA,YAAYL,kBAAkB;QAC9B,MAAM,IAAIM,MAAM,CAAC,uBAAuB,EAAED,QAAQ,iBAAiB,CAAC;IACxE;AACJ,EAAE;AAEF;;;;;;;;;CASC,GACM,MAAME,qBAAqB,CAACC,MAAoBC;IACnD,MAAMC,UAAUF,KAAK,OAAO,CAAC,GAAG,CAACG,CAAAA,SAAUA,OAAO,IAAI;IAEtD,MAAMC,UAAUF,QAAQ,MAAM,KAAKD,MAAM,MAAM,IACxCC,QAAQ,KAAK,CAAC,CAACG,MAAMC,QAAUD,SAASJ,KAAK,CAACK,MAAM;IAE3D,IAAIF,YAAY,OAAO;QACnB,MAAM,IAAIN,MACN,CAAC,qDAAqD,CAAC,GACvD,CAAC,OAAO,EAAEI,QAAQ,IAAI,CAAC,MAAM,YAAY,EAAED,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IAEvE;AACJ,EAAE;;;ACtJoF;AAqBtF,MAAMM,aAAab,cAAcA,CAACD,uCAAUA;AAE5C;;;;;;CAMC,GACD,MAAMe,UAAU,CAACC;IACb,IAAIA,iBAAiBC,MAAM;QACvB,MAAMC,QAAQF,MAAM,OAAO;QAE3B,OAAOG,OAAO,KAAK,CAACD,SAAS,OAAOA;IACxC;IAEA,IAAI,OAAOF,UAAU,UAAU;QAC3B,OAAOG,OAAO,QAAQ,CAACH,SAASA,QAAQ;IAC5C;IAEA,IAAI,OAAOA,UAAU,UAAU;QAC3B,OAAO;IACX;IAEA,MAAMI,SAASH,KAAK,KAAK,CAACD;IAE1B,OAAOG,OAAO,KAAK,CAACC,UAAU,OAAOA;AACzC;AAEA,MAAeC;IAID,KAAY;IACZ,MAAkB;IAE5B,aAAc;QACV,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ;QACzB,IAAI,CAAC,KAAK,GAAG,IAAIC,WAAWR;IAChC;IAUA,IAAID,KAAa,EAAEG,KAAc,EAAW;QACxC,IAAIA,SAAS,MAAM;YACf,IAAI,CAAC,KAAK,CAACH,SAAS,EAAE,IAAI,KAAMA,CAAAA,QAAQ;YACxC,IAAI,CAAC,IAAI,CAACA,MAAM,GAAG;YACnB,OAAO;QACX;QAEA,OAAO,IAAI,CAAC,KAAK,CAACA,OAAOG;IAC7B;IAEA,MAAMO,KAAa,EAAa;QAC5B,MAAMC,SAAoB,IAAIC,MAAMF;QAEpC,IAAK,IAAIG,IAAI,GAAGA,IAAIH,OAAOG,IAAK;YAC5B,MAAMC,SAAU,KAAI,CAAC,KAAK,CAACD,KAAK,EAAE,GAAI,KAAMA,CAAAA,IAAI,EAAE,MAAO;YAEzDF,MAAM,CAACE,EAAE,GAAGC,SAAS,OAAO,IAAI,CAAC,GAAG,CAACD;QACzC;QAEA,OAAOF;IACX;IAEA,KAAKtB,QAAgB,EAAE0B,aAAgC,EAAiB;QACpE,wFAAwF;QACxF,2EAA2E;QAC3E,MAAMC,OAAQ3B,aAAaF,uCAAUA,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAGE;QACvE,MAAM4B,QAAQ5B,aAAaF,uCAAUA,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAGC,cAAcA,CAACC;QAExF0B,cAAc,IAAI,CAACC,KAAK,MAAM,EAAEC,MAAM,MAAM;QAE5C,OAAO;YAAE,UAAU,IAAI,CAAC,QAAQ;YAAED;YAAMC;QAAM;IAClD;IAEA,QAAc;QACV,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ;QACzB,IAAI,CAAC,KAAK,GAAG,IAAIR,WAAWR;IAChC;AACJ;AAEA;;;;;;CAMC,GACD,MAAMiB,sBAAsBV;IAEf,WAAW,UAAmB;IAE7B,WAAyB;QAC/B,OAAO,IAAIW,aAAahC,uCAAUA;IACtC;IAEU,MAAMa,KAAa,EAAEG,KAAc,EAAW;QACpD,IAAI,OAAOA,UAAU,UAAU;YAC3B,OAAO;QACX;QAEA,IAAI,CAAC,IAAI,CAACH,MAAM,GAAGG;QAEnB,OAAO;IACX;IAEU,IAAIH,KAAa,EAAW;QAClC,OAAO,IAAI,CAAC,IAAI,CAACA,MAAM;IAC3B;AACJ;AAEA;;;;;;;;;;CAUC,GACD,MAAMoB,mBAAmBZ;IAEZ,WAAW,WAAoB;IAE9B,WAAyB;QAC/B,OAAO,IAAIW,aAAahC,uCAAUA;IACtC;IAEU,MAAMa,KAAa,EAAEG,KAAc,EAAW;QACpD,MAAME,QAAQH,QAAQC;QAEtB,IAAIE,SAAS,MAAM;YACf,OAAO;QACX;QAEA,IAAI,CAAC,IAAI,CAACL,MAAM,GAAGK;QAEnB,OAAO;IACX;IAEA;;;;;;;KAOC,GACS,IAAIL,KAAa,EAAW;QAClC,OAAO,IAAII,KAAK,IAAI,CAAC,IAAI,CAACJ,MAAM;IACpC;AACJ;AAEA;;;;;;CAMC,GACD,MAAMqB,sBAAsBb;IAEf,WAAW,eAAwB;IAElC,WAAuB;QAC7B,OAAO,IAAIC,WAAWtB,uCAAUA;IACpC;IAEU,MAAMa,KAAa,EAAEG,KAAc,EAAW;QACpD,IAAIA,UAAU,KAAKA,UAAU,GAAG;YAC5B,IAAI,CAAC,IAAI,CAACH,MAAM,GAAGG;YACnB,OAAO;QACX;QAEA,IAAI,OAAOA,UAAU,WAAW;YAC5B,OAAO;QACX;QAEA,IAAI,CAAC,IAAI,CAACH,MAAM,GAAGG,QAAQ,IAAI;QAE/B,OAAO;IACX;IAEU,IAAIH,KAAa,EAAW;QAClC,OAAO,IAAI,CAAC,IAAI,CAACA,MAAM;IAC3B;AACJ;AAEA;;;;;;;CAOC,GACD,MAAMsB;;;IAEO,WAAW,OAAgB;IAE5B,QAAkB,EAAE,CAAC;IAE7B;;;KAGC,GACO,QAAmB,EAAE,CAAC;IAE9B;;;;;;;;KAQC,GACD,YACqBC,MAAyC,EACzCC,QAAmC,CACtD;aAFmBD,SAAAA;aACAC,WAAAA;IACjB;IAEJ,IAAIxB,KAAa,EAAEG,KAAc,EAAW;QACxC,IAAIA,SAAS,MAAM;YACf,IAAI,CAAC,KAAK,CAACH,MAAM,GAAG;YACpB,IAAI,CAAC,KAAK,CAACA,MAAM,GAAG;YACpB,OAAO;QACX;QAEA,MAAMyB,OAAO,IAAI,CAAC,MAAM,CAACtB;QAEzB,IAAIsB,QAAQ,MAAM;YACd,OAAO;QACX;QAEA,IAAI,CAAC,KAAK,CAACzB,MAAM,GAAGyB;QAEpB,OAAO;IACX;IAEA,MAAMf,KAAa,EAAa;QAC5B,MAAMC,SAAoB,IAAIC,MAAMF;QAEpC,IAAK,IAAIG,IAAI,GAAGA,IAAIH,OAAOG,IAAK;YAC5BF,MAAM,CAACE,EAAE,GAAG,IAAI,CAAC,KAAK,CAACA,EAAE,KAAK,OAAO,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAACA,EAAE;QAC3E;QAEA,OAAOF;IACX;IAEA,KAAKtB,QAAgB,EAAiB;QAClC,+EAA+E;QAC/E,MAAMqC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAGrC,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC;QAE1D,OAAO;YAAE,UAAU,IAAI,CAAC,QAAQ;YAAEqC;QAAI;IAC1C;IAEA,QAAc;QACV,IAAI,CAAC,KAAK,GAAG,EAAE;QACf,IAAI,CAAC,KAAK,GAAG,EAAE;IACnB;AACJ;AAEA;;;;;;CAMC,GACM,MAAMC;IAEA,WAAW,QAAiB;IAE7B,KAAgB;IAExB,YAAYC,OAAkB,EAAE,CAAE;QAC9B,IAAI,CAAC,IAAI,GAAGA;IAChB;IAEA,IAAI5B,KAAa,EAAEG,KAAc,EAAW;QACxC,IAAI,CAAC,IAAI,CAACH,MAAM,GAAGG;QAEnB,OAAO;IACX;IAEA,MAAMO,KAAa,EAAa;QAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAGA;IAC9B;IAEA,KAAKrB,QAAgB,EAAiB;QAClC,OAAO;YAAE,UAAU,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAGA;QAAU;IACzE;IAEA,QAAc;QACV,IAAI,CAAC,IAAI,GAAG,EAAE;IAClB;AACJ;AAEA,6FAA6F,GAC7F,MAAMwC,aAAa,CAAC1B,QAChB,OAAOA,UAAU,WAAWA,QAAQ;AAExC;;;;;;CAMC,GACD,MAAM2B,oBAAoB,CAAC3B;IACvB,IAAI;QACA,OAAO4B,KAAK,SAAS,CAAC5B,UAAU;IACpC,EAAE,OAAM;QACJ,OAAO;IACX;AACJ;AAEA;;;;;CAKC,GACD,MAAM6B,gBAAgB,CAACP;IACnB,IAAI;QACA,OAAOM,KAAK,KAAK,CAACN;IACtB,EAAE,OAAM;QACJ,OAAOA;IACX;AACJ;AAEA,MAAMQ,UAAwD;IAC1D,WAAW,IAAM,IAAIf;IACrB,YAAY,IAAM,IAAIE;IACtB,gBAAgB,IAAM,IAAIC;IAC1B,8EAA8E;IAC9E,QAAQ,IAAM,IAAIC,WAAWO,YAAYJ,CAAAA,OAAQA;IACjD,oFAAoF;IACpF,kBAAkB,IAAM,IAAIH,WAAWQ,mBAAmBE;IAC1D,SAAS,IAAM,IAAIL;AACvB;AAEO,MAAMO,eAAe,CAACC;IACzB,MAAMC,SAASH,OAAO,CAACE,SAAS;IAEhC,IAAIC,UAAU,MAAM;QAChB,MAAM,IAAI5C,MAAM,CAAC,mBAAmB,EAAE2C,SAAS,kBAAkB,CAAC;IACtE;IAEA,OAAOC;AACX,EAAE;;;AC7XkE;AASnD;AAEjB,8FAA8F,GAC9F,MAAMC,eAAwC,CAAC;AAE/C;;;;;;;;;CASC,GACM,MAAMC;IAEQ,QAAmC;IACnC,QAAwB;IACxB,eAAwB;IACjC,OAAO,EAAE;IAEjB,YAAY5C,IAAkB,CAAE;QAC5BJ,qBAAqBA,CAACI,KAAK,OAAO;QAElC,IAAIA,KAAK,OAAO,CAAC,MAAM,KAAK,GAAG;YAC3B,MAAM,IAAIF,MAAM;QACpB;QAEA,MAAMG,QAAQ,IAAI4C;QAElB,KAAK,MAAM1C,UAAUH,KAAK,OAAO,CAAE;YAC/B,IAAIC,MAAM,GAAG,CAACE,OAAO,IAAI,GAAG;gBACxB,gFAAgF;gBAChF,wEAAwE;gBACxE,MAAM,IAAIL,MAAM,CAAC,kCAAkC,EAAEK,OAAO,IAAI,CAAC,iBAAiB,CAAC;YACvF;YAEAF,MAAM,GAAG,CAACE,OAAO,IAAI;QACzB;QAEA,IAAI,CAAC,OAAO,GAAGH,KAAK,OAAO;QAC3B,IAAI,CAAC,OAAO,GAAGA,KAAK,OAAO,CAAC,GAAG,CAACG,CAAAA,SAAUqC,YAAYA,CAACrC,OAAO,QAAQ;QACtE,IAAI,CAAC,cAAc,GAAGH,KAAK,OAAO,CAAC,IAAI,CAACG,CAAAA,SAAUA,OAAO,IAAI,IAAIwC;IACrE;IAEA,oCAAoC,GACpC,IAAI,WAAmB;QACnB,OAAO,IAAI,CAAC,IAAI;IACpB;IAEA,IAAI,SAAkB;QAClB,OAAO,IAAI,CAAC,IAAI,KAAKlD,uCAAUA;IACnC;IAEA,kEAAkE,GAClE,IAAI,cAAiC;QACjC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAACU,CAAAA,SAAUA,OAAO,IAAI;IACjD;IAEA;;;;;;KAMC,GACD,UAAUc,MAA0B,EAAQ;QACxC,IAAI,IAAI,CAAC,MAAM,EAAE;YACb,MAAM,IAAInB,MAAM,CAAC,qBAAqB,EAAEL,uCAAUA,CAAC,wCAAwC,CAAC;QAChG;QAEA,IAAIwB,OAAO,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YACvC,MAAM,IAAInB,MACN,CAAC,cAAc,EAAEmB,OAAO,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAE3F;QAEA,MAAMX,QAAQ,IAAI,CAAC,IAAI;QAEvB,IAAK,IAAIa,IAAI,GAAGA,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAEA,IAAK;YAC1C,IAAI,IAAI,CAAC,OAAO,CAACA,EAAE,CAAC,GAAG,CAACb,OAAOW,MAAM,CAACE,EAAE,MAAM,OAAO;gBACjD,IAAI,CAAC,QAAQ,CAACA,GAAGb,OAAOW,MAAM,CAACE,EAAE;YACrC;QACJ;QAEA,IAAI,CAAC,IAAI,GAAGb,QAAQ;IACxB;IAEA;;;;;;;;;;KAUC,GACD,aAAawC,MAA+B,EAAQ;QAChD,MAAM7B,SAAoB,IAAIC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM;QAEvD,IAAK,IAAIC,IAAI,GAAGA,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAEA,IAAK;YAC1CF,MAAM,CAACE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC2B,QAAQ,IAAI,CAAC,OAAO,CAAC3B,EAAE,CAAC,IAAI;QAC3D;QAEA,IAAI,CAAC,SAAS,CAACF;IACnB;IAEA;;;;;;;KAOC,GACO,UAAU6B,MAA+B,EAAEzC,IAAY,EAAW;QACtE,IAAI,IAAI,CAAC,cAAc,EAAE;YACrB,OAAO0C,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACD,QAAQzC,QAAQyC,MAAM,CAACzC,KAAK,GAAG;QAC/E;QAEA,MAAMI,QAAQqC,MAAM,CAACzC,KAAK;QAE1B,2FAA2F;QAC3F,2FAA2F;QAC3F,yEAAyE;QACzE,OAAOI,UAAUuC,YAAY,OAAOvC;IACxC;IAEA;;;;;KAKC,GACD,OAAwB;QACpB,MAAMY,gBAAmC,EAAE;QAC3C,yFAAyF;QACzF,0FAA0F;QAC1F,sFAAsF;QACtF,MAAM4B,UAAUF,OAAO,MAAM,CAAC;QAE9B,IAAK,IAAI5B,IAAI,GAAGA,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAEA,IAAK;YAC1C8B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC9B,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAACA,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAEE;QACpE;QAEA,MAAM6B,UAAU;YAAE,SAAS1D,0CAAgBA;YAAE,UAAU,IAAI,CAAC,IAAI;YAAEyD;QAAQ;QAE1E,IAAI,CAAC,IAAI,GAAG;QAEZ,KAAK,MAAME,UAAU,IAAI,CAAC,OAAO,CAAE;YAC/BA,OAAO,KAAK;QAChB;QAEA,OAAO;YAAED;YAAS7B;QAAc;IACpC;IAEQ,SAASlB,MAAc,EAAEG,KAAa,EAAEG,KAAc,EAAQ;QAClE,MAAM2C,QAAQ,IAAInB,WAAWA,CAAC,IAAI,CAAC,OAAO,CAAC9B,OAAO,CAAC,KAAK,CAACG;QAEzD8C,MAAM,GAAG,CAAC9C,OAAOG;QAEjB,IAAI,CAAC,OAAO,CAACN,OAAO,GAAGiD;IAC3B;AACJ;;;ACzKiB;AA6BV,MAAMC,sBAAsB,CAACC,QAChC,OAAQA,OAAmD,uBAAuB,SAAS;AAE/F,MAAMC,YAAY,CAACpD,QAAgBqD,QAC/BT,OAAO,MAAM,CACT,IAAIjD,MACA,CAAC,0CAA0C,EAAEK,OAAO,sCAAsC,CAAC,GAC3F,CAAC,yEAAyE,CAAC,GAC3E,GAAIqD,OAAiB,WAAWC,OAAOD,QAAQ,GAEnD;QAAE,oBAAoBrD;IAAO;AAWrC,MAAMuD,mBAAmB,CAAC1D,MAAoB2D,QAC1C3D,KAAK,OAAO,CAAC,GAAG,CAAC,CAACG;QACd,oFAAoF;QACpF,uFAAuF;QACvF,+BAA+B;QAC/B,IAAI4C,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACY,MAAM,OAAO,EAAExD,OAAO,IAAI,MAAM,OAAO;YAC5E,MAAM,IAAIL,MAAM,CAAC,qCAAqC,EAAEK,OAAO,IAAI,CAAC,wBAAwB,CAAC;QACjG;QAEA,MAAMyD,UAAUD,MAAM,OAAO,CAACxD,OAAO,IAAI,CAAC;QAE1C,OAAO;YAAE,MAAMA,OAAO,IAAI;YAAE,UAAUyD,QAAQ,QAAQ;QAAC;IAC3D;AAEJ;;;;;;;CAOC,GACD,MAAMC,WAAW,CAACZ,UACd,QAAQA,QAAQ,GAAG,CAAC9C,CAAAA,SAAU,GAAGkC,KAAK,SAAS,CAAClC,OAAO,IAAI,EAAE,CAAC,EAAEA,OAAO,QAAQ,EAAE,EAAE,IAAI,CAAC;AAE5F,6DAA6D,GAC7D,MAAM2D,iBAAiB;AAEvB,MAAMC,QAAQ,IAAIC;AAElB,MAAMC,SAAS,CAACC;IACZ,MAAMC,UAAUJ,MAAM,GAAG,CAACG;IAE1B,IAAIC,WAAW,MAAM;QACjB,OAAOnB;IACX;IAEA,yFAAyF;IACzFe,MAAM,MAAM,CAACG;IACbH,MAAM,GAAG,CAACG,KAAKC;IAEf,OAAOA;AACX;AAEA,MAAMC,WAAW,CAACF,KAAaC;IAC3BJ,MAAM,GAAG,CAACG,KAAKC;IAEf,IAAIJ,MAAM,IAAI,GAAGD,gBAAgB;QAC7B,MAAMO,SAASN,MAAM,IAAI,GAAG,IAAI;QAEhC,IAAIM,OAAO,IAAI,KAAK,OAAO;YACvBN,MAAM,MAAM,CAACM,OAAO,KAAK;QAC7B;IACJ;AACJ;AAEA,8DAA8D,GACvD,MAAMC,oBAAoB,IAAYP,MAAM,KAAK,GAAG;AAE3D,MAAMQ,WAAW,CAACpE,QAAyBG;IACvC,MAAMkE,SAAS,CAAC,EAAE,EAAElE,MAAM,cAAc,CAAC;IAEzC,OAAQH,OAAO,QAAQ;QACnB,KAAK;YACD,OAAO,GAAGqE,OAAO,WAAW,EAAElE,MAAM,GAAG,CAAC;QAC5C,KAAK;YACD,OAAO,GAAGkE,OAAO,oBAAoB,EAAElE,MAAM,IAAI,CAAC;QACtD,KAAK;YACD,OAAO,GAAGkE,OAAO,WAAW,EAAElE,MAAM,SAAS,CAAC;QAClD,KAAK;YACD,OAAO,CAAC,CAAC,EAAEA,MAAM,GAAG,CAAC;QACzB,KAAK;YACD,OAAO,CAAC,CAAC,EAAEA,MAAM,GAAG,CAAC;IAC7B;AACJ;AAEA;;;;;;;CAOC,GACD,MAAMmE,aAAa,CAACpE,OAChBA,SAAS,cAAc,CAAC,CAAC,EAAEgC,KAAK,SAAS,CAAChC,MAAM,CAAC,CAAC,GAAGgC,KAAK,SAAS,CAAChC;AAExE,MAAMqE,UAAU,CAACjC,WACbA,aAAa,aAAaA,aAAa,cAAcA,aAAa;AAEtE,MAAMkC,gBAAgB,CAAC1B;IACnB,MAAM2B,QAAkB;QAAC;QAAiB;QAA2B;KAA2B;IAEhG3B,QAAQ,OAAO,CAAC,CAAC9C,QAAQG;QACrB,IAAIH,OAAO,QAAQ,KAAK,QAAQ;YAC5ByE,MAAM,IAAI,CAAC,CAAC,KAAK,EAAEtE,MAAM,QAAQ,EAAEA,MAAM,EAAE,CAAC;YAC5C;QACJ;QAEAsE,MAAM,IAAI,CAAC,CAAC,KAAK,EAAEtE,MAAM,iBAAiB,EAAE+B,KAAK,SAAS,CAAClC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC3EyE,MAAM,IAAI,CAAC,CAAC,KAAK,EAAEtE,MAAM,IAAI,EAAEA,MAAM,MAAM,CAAC;QAE5C,IAAIoE,QAAQvE,OAAO,QAAQ,GAAG;YAC1ByE,MAAM,IAAI,CAAC,CAAC,KAAK,EAAEtE,MAAM,IAAI,EAAEA,MAAM,OAAO,CAAC;QACjD;IACJ;IAEAsE,MAAM,IAAI,CAAC;IACXA,MAAM,IAAI,CAAC;IACX,0FAA0F;IAC1FA,MAAM,IAAI,CAAC;IAEX3B,QAAQ,OAAO,CAAC,CAAC9C,QAAQG;QACrBsE,MAAM,IAAI,CAAC,GAAGH,WAAWtE,OAAO,IAAI,EAAE,EAAE,EAAEoE,SAASpE,QAAQG,OAAO,CAAC,CAAC;IACxE;IAEAsE,MAAM,IAAI,CAAC;IACXA,MAAM,IAAI,CAAC;IACXA,MAAM,IAAI,CAAC;IAEX,OAAOA,MAAM,IAAI,CAAC;AACtB;AAEA;;;;;;;CAOC,GACD,IAAIC,sBAAsC;AAEnC,MAAMC,2BAA2B;IACpC,IAAID,uBAAuB,MAAM;QAC7B,IAAI;YACA,IAAIE,SAAS;YACbF,sBAAsB;QAC1B,EAAE,OAAM;YACJA,sBAAsB;QAC1B;IACJ;IAEA,OAAOA;AACX,EAAE;AAEF,MAAMG,eAAe,CAAC/B,UAClB,IAAI8B,SAAS,SAAS,QAAQJ,cAAc1B;AAEhD,MAAMgC,aAAa,CAAChC;IAChB,MAAMiB,MAAML,SAASZ;IACrB,MAAMiC,MAAMjB,OAAOC;IAEnB,IAAIgB,OAAO,MAAM;QACb,OAAOA;IACX;IAEA,MAAMf,UAAUa,aAAa/B;IAE7BmB,SAASF,KAAKC;IAEd,OAAOA;AACX;AAEA;;;;;CAKC,GACD,MAAMgB,mBAAmB,CAAClC,SAAqCU;IAC3D,MAAM9C,SAAsB,IAAIK,MAAM+B,QAAQ,MAAM;IAEpDA,QAAQ,OAAO,CAAC,CAAC9C,QAAQG;QACrB,IAAIH,OAAO,QAAQ,KAAK,QAAQ;YAC5B;QACJ;QAEA,MAAMyD,UAAUD,MAAM,OAAO,CAACxD,OAAO,IAAI,CAAC;QAC1C,MAAM6B,MAAM4B,QAAQ,QAAQ,KAAK,SAASA,QAAQ,GAAG,GAAG;QAExD,IAAI;YACA/C,MAAM,CAACP,MAAM,GAAG+B,KAAK,KAAK,CAACL;QAC/B,EAAE,OAAOsB,OAAO;YACZ,MAAMC,UAAUpD,OAAO,IAAI,EAAEmD;QACjC;IACJ;IAEA,OAAOzC;AACX;AAEA;;;;;;;;;CASC,GACM,MAAMuE,cAAc,CAACpF,MAAoB2D;IAC5C/D,qBAAqBA,CAACI,KAAK,OAAO;IAClCJ,qBAAqBA,CAAC+D,MAAM,OAAO;IAEnC,MAAMV,UAAUS,iBAAiB1D,MAAM2D;IAEvC,OAAOsB,WAAWhC,SAASU,OAAOwB,iBAAiBlC,SAASU;AAChE,EAAE;;;ACpPK,IAAK0B,iBAAWA,0BAAXA;;;;;;;;;;IAUR;;;KAGC;IAED;;;;;;KAMC;WArBOA;MAuBX;AA8EM,IAAKC,cAAQA,iBAARA,gDAAAA,SAAAA;;;WAAAA;QAGX;;;AC5H6C;AAG6B;AAoD3E;;;;;;;;;;CAUC,GACM,MAAMC,0BAA+C;IACxD,CAACF,wBAAkB,CAAC,EAAE;IACtB,CAACA,yBAAmB,CAAC,EAAE;IACvB,CAACA,sBAAgB,CAAC,EAAE;IACpB,CAACA,wBAAkB,CAAC,EAAE;IACtB,CAACA,uBAAiB,CAAC,EAAE;IACrB,CAACA,wBAAkB,CAAC,EAAE;AAC1B,EAAE;AAEF;;;;;;;;;;;CAWC,GACM,MAAMG,2BAAgD;IACzD,CAACH,wBAAkB,CAAC,EAAE;IACtB,CAACA,yBAAmB,CAAC,EAAE;IACvB,CAACA,sBAAgB,CAAC,EAAE;IACpB,CAACA,wBAAkB,CAAC,EAAE;IACtB,CAACA,uBAAiB,CAAC,EAAE;IACrB,CAACA,wBAAkB,CAAC,EAAE;AAC1B,EAAE;AAEF;;;;;;CAMC,GACD,MAAMI,eAAe,CAACC,WAClBA,SAAS,eAAe,IAAI,QACzBA,SAAS,iBAAiB,IAAI,QAC9BA,SAAS,SAAS,IAAI,QACtBA,SAAS,YAAY,IAAI;AAEhC;;;;;;CAMC,GACM,MAAMC,sBAAsB,CAC/BxF,QACAyF;IAEA,MAAMF,WAAWvF,OAAO,QAAQ;IAEhC,sFAAsF;IACtF,2FAA2F;IAC3F,oCAAoC;IACpC,IAAIuF,YAAY,QAAQD,aAAaC,WAAW;QAC5C,OAAO;IACX;IAEA,MAAM,EAAEG,OAAO,EAAEC,KAAK,EAAE,GAAGC,UAAUH;IAErC,OAAOC,UAAU1F,WAAW2F,OAAO,CAACJ,SAAS,IAAI,CAAC,IAAI;AAC1D,EAAE;AAEF,MAAMK,YAAY,CAACH;IAGf,IAAI,OAAOA,aAAa,YAAY;QAChC,OAAO;YAAE,SAASA;QAAS;IAC/B;IAEA,IAAI,aAAaA,YAAY,WAAWA,UAAU;QAC9C,OAAOA;IACX;IAEA,OAAO;QAAE,OAAOA;IAAgC;AACpD;AAEA;;;;;;;;;;CAUC,GACM,MAAMI,oBAAoB,CAC7B/C,SACA2C;IAEA,IAAI3C,QAAQ,MAAM,KAAK,GAAG;QACtB,OAAOD;IACX;IAEA,MAAM/C,QAAQ,IAAI4C,IAAII,QAAQ,GAAG,CAAC9C,CAAAA,SAAUA,OAAO,IAAI;IAEvD,IAAIF,MAAM,IAAI,KAAKgD,QAAQ,MAAM,EAAE;QAC/B,OAAOD;IACX;IAEA,OAAO;QACH,SAASxD,0CAAgBA;QACzB,SAASyD,QAAQ,GAAG,CAAC9C,CAAAA,SAAW;gBAC5B,MAAMA,OAAO,IAAI;gBACjB,UAAUwF,oBAAoBxF,QAAQyF;YAC1C;IACJ;AACJ,EAAE;;;ACpLF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CC,GACuB;AACO;AACL;AACH"}
@@ -217,6 +217,30 @@ const mismatchWarning = (expression)=>{
217
217
  const outcome = expression.negated ? "every row matches" : "no row matches";
218
218
  return `Routier: '${side.property.property.getAssignmentPath()}' is a ${side.expected}, and this filter ` + `compares it against ${describeLiteral(side.value.value)}, which is a ${typeof side.value.value}. ` + `A strict comparison between them is the same answer for every row, so ${outcome} and the filter ` + `runs in memory. https://routier.dev/guides/strict-comparison-types`;
219
219
  };
220
+ /**
221
+ * An item as one dispatch receives it: a new object, so a report written on it stays with that dispatch.
222
+ *
223
+ * A database option starts `executed` again, because a report is only an answer from the plugin that
224
+ * made it. A memory option keeps the reason core planned it with. A join's inner options are copied the
225
+ * same way, since a plugin can report on them too.
226
+ */ const toDispatchItem = (item)=>{
227
+ const option = item.option;
228
+ const value = option.name === "join" ? {
229
+ ...option.value,
230
+ innerOptions: option.value.innerOptions.forDispatch()
231
+ } : option.value;
232
+ return {
233
+ index: item.index,
234
+ option: option.target === "database" ? {
235
+ ...option,
236
+ value,
237
+ reason: "executed"
238
+ } : {
239
+ ...option,
240
+ value
241
+ }
242
+ };
243
+ };
220
244
  class QueryOptionsCollection {
221
245
  options = new Map();
222
246
  nextExecutionTarget = "database";
@@ -255,7 +279,7 @@ class QueryOptionsCollection {
255
279
  }
256
280
  }
257
281
  if (name === "filter") {
258
- // Need to check for unmapped and renamed properties
282
+ // Need to check for unmapped properties
259
283
  const filterValue = value;
260
284
  // A tautology (`x => true`) filters nothing — skip it entirely so
261
285
  // plugins never see it
@@ -272,14 +296,10 @@ class QueryOptionsCollection {
272
296
  this.cutOverToMemory("unmapped-property");
273
297
  return false;
274
298
  }
275
- if ((0,_assertions__rspack_import_1.isPropertyExpression)(expression) && expression.property.hasRenamedSegments) {
276
- // Cut over to memory execution: the plugin stores data under the
277
- // `from` (storage) names, but filter selectors reference the
278
- // in-memory names. Memory execution runs after deserialization,
279
- // where the in-memory names exist
280
- this.cutOverToMemory("renamed-property");
281
- return false;
282
- }
299
+ // A renamed property stays with the database. Whether the backend can read a
300
+ // `from` name is the plugin's to know, not this collection's: the property
301
+ // travels with the option, and a plugin that cannot resolve it reports it
302
+ // back see `reportRenamedProperties`
283
303
  if (comparesTypesThatCannotMatch(expression)) {
284
304
  _utilities__rspack_import_3/* .logger.warn */.vF.warn(mismatchWarning(expression));
285
305
  this.cutOverToMemory("predicate-error");
@@ -291,26 +311,19 @@ class QueryOptionsCollection {
291
311
  }
292
312
  if (name === "sort") {
293
313
  const sortValue = value;
294
- // Same rule as filters: sort selectors reference in-memory names, which
295
- // only exist after deserialization when the property is renamed or unmapped
314
+ // Same rule as filters: an unmapped property only exists after deserialization. A
315
+ // renamed one stays with the database, for the plugin to resolve or report
296
316
  if (sortValue.property != null && sortValue.property.isUnmapped) {
297
317
  this.cutOverToMemory("unmapped-property");
298
- } else if (sortValue.property != null && sortValue.property.hasRenamedSegments) {
299
- this.cutOverToMemory("renamed-property");
300
318
  }
301
319
  }
302
320
  if (name === "nearest") {
303
321
  const nearestValue = value;
304
- // Same rule as sort, and for the same reason: the plugin stores the vector under
305
- // the `from` name, and an unmapped property is not stored at all. Both are only
306
- // readable after deserialization, which is where memory execution runs.
307
- //
308
- // This is also what lets every translator's in-memory fallback read the column by
309
- // its resolved name — anything whose storage name differs never reaches them.
322
+ // Same rule as sort, and for the same reason: an unmapped property is not stored at
323
+ // all, so it is only readable after deserialization, which is where memory execution
324
+ // runs. A vector stored under a `from` name is the plugin's to resolve or report.
310
325
  if (nearestValue.property != null && nearestValue.property.isUnmapped) {
311
326
  this.cutOverToMemory("unmapped-property");
312
- } else if (nearestValue.property != null && nearestValue.property.hasRenamedSegments) {
313
- this.cutOverToMemory("renamed-property");
314
327
  }
315
328
  }
316
329
  if ((name === "filter" || name === "sort") && (this.options.has("skip") || this.options.has("take"))) {
@@ -417,6 +430,9 @@ class QueryOptionsCollection {
417
430
  * the shared collection before executing. Without restoring, a re-executed terminal —
418
431
  * the whole point of a subscribed queryable — stacks its option a second time and
419
432
  * runs it over the first execution's scalar result.
433
+ *
434
+ * The item objects are shared with the snapshot. Nothing reports on them, because every
435
+ * dispatch sends a `forDispatch` copy, so a restore brings back no reports.
420
436
  */ snapshot() {
421
437
  const options = new Map([
422
438
  ...this.options.entries()
@@ -494,19 +510,48 @@ class QueryOptionsCollection {
494
510
  }
495
511
  }
496
512
  /**
497
- * Forgets what any previous dispatch reported.
513
+ * A copy of the collection for one dispatch to a plugin, with nothing reported on it.
498
514
  *
499
515
  * Capability is answered per dispatch, so a report is only an answer for the execution that
500
- * produced it. The items are shared with any snapshot, so a report mutated in place otherwise
501
- * survives a restore and a second terminal on the same queryable replays options the plugin
502
- * did run a `skip` applied twice, over rows already windowed.
503
- */ forgetReports() {
516
+ * produced it. Reports are written onto items, and the items of a queryable's collection
517
+ * outlive any one execution: a snapshot shares them, and a subscription dispatches the same
518
+ * query on every change. A report left on them replays options the plugin did run on the
519
+ * next execution, such as a `skip` applied twice over rows already windowed, or hands a
520
+ * renamed filter to memory that the engine could have run.
521
+ *
522
+ * Each item keeps its index, name, value and target. A half from `split`/`splitAt` is copied
523
+ * with a copy of its origin, and its items are that copy's items, so a report on the half still
524
+ * cascades over the whole dispatch without reaching the collection it was copied from.
525
+ */ forDispatch() {
526
+ if (this.origin == null) {
527
+ return this.copyForDispatch().copy;
528
+ }
529
+ const { copy: root, copies } = this.origin.copyForDispatch();
530
+ const half = new QueryOptionsCollection();
504
531
  this.resolveEnumeration();
505
532
  for (const item of this.enumeratedItems){
506
- if (item.option.target === "database") {
507
- item.option.reason = "executed";
508
- }
533
+ // An item added to the half after it was split has no counterpart in the origin
534
+ half.adopt(copies.get(item) ?? toDispatchItem(item));
509
535
  }
536
+ half.origin = root;
537
+ return half;
538
+ }
539
+ copyForDispatch() {
540
+ const copy = new QueryOptionsCollection();
541
+ const copies = new Map();
542
+ this.resolveEnumeration();
543
+ for (const item of this.enumeratedItems){
544
+ const copied = toDispatchItem(item);
545
+ copies.set(item, copied);
546
+ copy.adopt(copied);
547
+ }
548
+ copy.nextExecutionTarget = this.nextExecutionTarget;
549
+ copy.nextExecutionReason = this.nextExecutionReason;
550
+ copy.nextIndex = this.nextIndex;
551
+ return {
552
+ copy,
553
+ copies
554
+ };
510
555
  }
511
556
  /** The options the database did not run, in the order they were written. */ notExecuted() {
512
557
  this.resolveEnumeration();