@prisma-next/utils 0.11.0 → 0.12.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.
@@ -1 +1 @@
1
- {"version":3,"file":"abortable.d.mts","names":[],"sources":["../src/abortable.ts"],"mappings":";;AAuBA;;;;;;;;;;;;;;;;;;;;;;iBAAgB,SAAA,CAAU,MAAA,EAAQ,WAAA,OAAkB,OAAA,EAAS,OAAA,CAAQ,CAAA,MAAO,OAAA,CAAQ,CAAA"}
1
+ {"version":3,"file":"abortable.d.mts","names":[],"sources":["../src/abortable.ts"],"mappings":";;AAuBA;;;;;;;;;;;;;;;;;;;AAAqF;;;iBAArE,SAAA,CAAU,MAAA,EAAQ,WAAA,OAAkB,OAAA,EAAS,OAAA,CAAQ,CAAA,MAAO,OAAA,CAAQ,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"abortable.mjs","names":[],"sources":["../src/abortable.ts"],"sourcesContent":["/**\n * Allows aborting an async operation by returning a Promise which rejects if\n * the provided AbortSignal aborts and otherwise resolves with the result of\n * the async operation.\n *\n * Throws immediately if the signal is already aborted.\n * When the signal is aborted later, any wrapped promise will reject with\n * the signal's reason (or a default DOMException).\n *\n * @example\n * ```typescript\n * const { signal = new AbortController().signal } = options;\n * const unlessAborted = abortable(signal);\n *\n * // Any of these will reject if signal is aborted\n * await unlessAborted(asyncOperation());\n * await unlessAborted(fetch(url));\n * ```\n *\n * @param signal - The AbortSignal to race against\n * @returns A function that wraps promises to be cancelable\n * @throws If signal is already aborted\n */\nexport function abortable(signal: AbortSignal): <T>(promise: Promise<T>) => Promise<T> {\n signal.throwIfAborted();\n\n const abortPromise = new Promise<never>((_resolve, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n reject(signal.reason ?? new DOMException('Operation cancelled'));\n },\n { once: true },\n );\n });\n\n return <T>(operation: Promise<T>): Promise<T> => {\n return Promise.race([operation, abortPromise]);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,UAAU,QAA6D;CACrF,OAAO,gBAAgB;CAEvB,MAAM,eAAe,IAAI,SAAgB,UAAU,WAAW;EAC5D,OAAO,iBACL,eACM;GACJ,OAAO,OAAO,UAAU,IAAI,aAAa,sBAAsB,CAAC;KAElE,EAAE,MAAM,MAAM,CACf;GACD;CAEF,QAAW,cAAsC;EAC/C,OAAO,QAAQ,KAAK,CAAC,WAAW,aAAa,CAAC"}
1
+ {"version":3,"file":"abortable.mjs","names":[],"sources":["../src/abortable.ts"],"sourcesContent":["/**\n * Allows aborting an async operation by returning a Promise which rejects if\n * the provided AbortSignal aborts and otherwise resolves with the result of\n * the async operation.\n *\n * Throws immediately if the signal is already aborted.\n * When the signal is aborted later, any wrapped promise will reject with\n * the signal's reason (or a default DOMException).\n *\n * @example\n * ```typescript\n * const { signal = new AbortController().signal } = options;\n * const unlessAborted = abortable(signal);\n *\n * // Any of these will reject if signal is aborted\n * await unlessAborted(asyncOperation());\n * await unlessAborted(fetch(url));\n * ```\n *\n * @param signal - The AbortSignal to race against\n * @returns A function that wraps promises to be cancelable\n * @throws If signal is already aborted\n */\nexport function abortable(signal: AbortSignal): <T>(promise: Promise<T>) => Promise<T> {\n signal.throwIfAborted();\n\n const abortPromise = new Promise<never>((_resolve, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n reject(signal.reason ?? new DOMException('Operation cancelled'));\n },\n { once: true },\n );\n });\n\n return <T>(operation: Promise<T>): Promise<T> => {\n return Promise.race([operation, abortPromise]);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,UAAU,QAA6D;CACrF,OAAO,eAAe;CAEtB,MAAM,eAAe,IAAI,SAAgB,UAAU,WAAW;EAC5D,OAAO,iBACL,eACM;GACJ,OAAO,OAAO,UAAU,IAAI,aAAa,qBAAqB,CAAC;EACjE,GACA,EAAE,MAAM,KAAK,CACf;CACF,CAAC;CAED,QAAW,cAAsC;EAC/C,OAAO,QAAQ,KAAK,CAAC,WAAW,YAAY,CAAC;CAC/C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"array-equal.d.mts","names":[],"sources":["../src/array-equal.ts"],"mappings":";;AAgBA;;;;;;;;;;;;;;;iBAAgB,YAAA,GAAA,CAAgB,CAAA,WAAY,CAAA,IAAK,CAAA,WAAY,CAAA"}
1
+ {"version":3,"file":"array-equal.d.mts","names":[],"sources":["../src/array-equal.ts"],"mappings":";;AAgBA;;;;;;;;;;AAAgE;;;;;iBAAhD,YAAA,GAAA,CAAgB,CAAA,WAAY,CAAA,IAAK,CAAA,WAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"array-equal.mjs","names":[],"sources":["../src/array-equal.ts"],"sourcesContent":["/**\n * Checks if two arrays are equal using Object.is() for element comparison.\n * Arrays are considered equal if they have the same length and each element\n * at corresponding indices is equal according to Object.is().\n *\n * @param a - First array to compare\n * @param b - Second array to compare\n * @returns true if arrays are equal, false otherwise\n *\n * @example\n * ```typescript\n * isArrayEqual(['a', 'b'], ['a', 'b']); // true\n * isArrayEqual(['a'], ['a', 'b']); // false\n * isArrayEqual([0], [-0]); // false (Object.is distinguishes +0 and -0)\n * ```\n */\nexport function isArrayEqual<T>(a: readonly T[], b: readonly T[]): boolean {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) {\n return false;\n }\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,aAAgB,GAAiB,GAA0B;CACzE,IAAI,EAAE,WAAW,EAAE,QACjB,OAAO;CAET,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,IAAI,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,GAAG,EACxB,OAAO;CAGX,OAAO"}
1
+ {"version":3,"file":"array-equal.mjs","names":[],"sources":["../src/array-equal.ts"],"sourcesContent":["/**\n * Checks if two arrays are equal using Object.is() for element comparison.\n * Arrays are considered equal if they have the same length and each element\n * at corresponding indices is equal according to Object.is().\n *\n * @param a - First array to compare\n * @param b - Second array to compare\n * @returns true if arrays are equal, false otherwise\n *\n * @example\n * ```typescript\n * isArrayEqual(['a', 'b'], ['a', 'b']); // true\n * isArrayEqual(['a'], ['a', 'b']); // false\n * isArrayEqual([0], [-0]); // false (Object.is distinguishes +0 and -0)\n * ```\n */\nexport function isArrayEqual<T>(a: readonly T[], b: readonly T[]): boolean {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) {\n return false;\n }\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,aAAgB,GAAiB,GAA0B;CACzE,IAAI,EAAE,WAAW,EAAE,QACjB,OAAO;CAET,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,IAAI,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,EAAE,GACvB,OAAO;CAGX,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"assertions.d.mts","names":[],"sources":["../src/assertions.ts"],"mappings":";;AAaA;;;;;;;;;;;;iBAAgB,aAAA,GAAA,CAAiB,KAAA,EAAO,CAAA,qBAAsB,OAAA,mBAA0B,KAAA,IAAS,CAAA;;;;;;;;;;;;iBAiBjF,SAAA,CAAU,SAAA,WAAoB,OAAA,mBAA0B,SAAA"}
1
+ {"version":3,"file":"assertions.d.mts","names":[],"sources":["../src/assertions.ts"],"mappings":";;AAaA;;;;;;;;;;;AAAkG;iBAAlF,aAAA,GAAA,CAAiB,KAAA,EAAO,CAAA,qBAAsB,OAAA,mBAA0B,KAAA,IAAS,CAAC;;;;;;;;AAiBjB;;;;iBAAjE,SAAA,CAAU,SAAA,WAAoB,OAAA,mBAA0B,SAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"assertions.mjs","names":[],"sources":["../src/assertions.ts"],"sourcesContent":["/**\n * Asserts that a value is defined (not null or undefined).\n * Use for invariants where the value should always exist at runtime.\n *\n * @throws Error if value is null or undefined\n *\n * @example\n * ```typescript\n * const table = storage.namespaces[namespaceId].tables[tableName];\n * assertDefined(table, `Table \"${tableName}\" not found`);\n * // table is now narrowed to non-nullable\n * ```\n */\nexport function assertDefined<T>(value: T | null | undefined, message: string): asserts value is T {\n if (value === null || value === undefined) {\n throw new Error(message);\n }\n}\n\n/**\n * Asserts that a condition is true.\n * Use for invariants that should always hold at runtime.\n *\n * @throws Error if condition is false\n *\n * @example\n * ```typescript\n * invariant(columns.length > 0, 'Primary key must have at least one column');\n * ```\n */\nexport function invariant(condition: boolean, message: string): asserts condition {\n if (!condition) {\n throw new Error(message);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,cAAiB,OAA6B,SAAqC;CACjG,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,MAAM,IAAI,MAAM,QAAQ;;;;;;;;;;;;;AAe5B,SAAgB,UAAU,WAAoB,SAAoC;CAChF,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,QAAQ"}
1
+ {"version":3,"file":"assertions.mjs","names":[],"sources":["../src/assertions.ts"],"sourcesContent":["/**\n * Asserts that a value is defined (not null or undefined).\n * Use for invariants where the value should always exist at runtime.\n *\n * @throws Error if value is null or undefined\n *\n * @example\n * ```typescript\n * const table = storage.namespaces[namespaceId].tables[tableName];\n * assertDefined(table, `Table \"${tableName}\" not found`);\n * // table is now narrowed to non-nullable\n * ```\n */\nexport function assertDefined<T>(value: T | null | undefined, message: string): asserts value is T {\n if (value === null || value === undefined) {\n throw new Error(message);\n }\n}\n\n/**\n * Asserts that a condition is true.\n * Use for invariants that should always hold at runtime.\n *\n * @throws Error if condition is false\n *\n * @example\n * ```typescript\n * invariant(columns.length > 0, 'Primary key must have at least one column');\n * ```\n */\nexport function invariant(condition: boolean, message: string): asserts condition {\n if (!condition) {\n throw new Error(message);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,cAAiB,OAA6B,SAAqC;CACjG,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,MAAM,IAAI,MAAM,OAAO;AAE3B;;;;;;;;;;;;AAaA,SAAgB,UAAU,WAAoB,SAAoC;CAChF,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,OAAO;AAE3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"canonical-stringify.d.mts","names":[],"sources":["../src/canonical-stringify.ts"],"mappings":";;AA6CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,kBAAA,CAAmB,KAAA"}
1
+ {"version":3,"file":"canonical-stringify.d.mts","names":[],"sources":["../src/canonical-stringify.ts"],"mappings":";;AA6CA;;;;AAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAjC,kBAAA,CAAmB,KAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"canonical-stringify.mjs","names":[],"sources":["../src/canonical-stringify.ts"],"sourcesContent":["/**\n * Produces a deterministic, JSON-like string representation of a value.\n *\n * Designed for use as a stable identity / cache key. Two values that are\n * structurally equivalent — regardless of object key insertion order —\n * produce the same string. Two values that differ in any meaningful way\n * (including types that JSON would conflate, like `BigInt(1)` vs `1`)\n * produce different strings.\n *\n * Supported inputs:\n * - `null`, `undefined` (distinguishable: `null` → `\"null\"`, `undefined` → `\"undefined\"`)\n * - `boolean`, `string`, `number` (including `NaN`, `Infinity`, `-Infinity`)\n * - `bigint` (suffixed with `n` to disambiguate from `number`)\n * - `Date` (tagged + ISO string)\n * - `Buffer` / `Uint8Array` (tagged + hex-encoded as `Bytes(<hex>)`)\n * - Other `ArrayBuffer` views — `Int8Array`, `Uint16Array`, `Float64Array`,\n * `DataView`, etc. (tagged with the constructor name + hex-encoded over\n * the underlying bytes, e.g. `Uint16Array(<hex>)`). Note that the bytes\n * are read in host byte order, so callers that need cross-platform\n * stability for multi-byte typed arrays should normalize endianness\n * before passing the value in.\n * - Arrays (order-preserving)\n * - Plain objects (key-sorted) — only objects whose prototype is\n * `Object.prototype` or `null`. Non-plain objects (`Map`, `Set`,\n * `RegExp`, class instances, etc.) are rejected so they cannot silently\n * collapse to `{}` and collide with each other.\n *\n * Throws on `function`, `symbol`, circular references, non-plain objects,\n * and objects with symbol-keyed properties (which `Object.keys` would\n * silently drop). Callers that need to canonicalize any of these must\n * convert them to a supported representation first.\n *\n * The output format is intentionally not JSON: the type tags and BigInt\n * suffix mean it cannot be round-tripped via `JSON.parse`. The goal is\n * keying, not serialization.\n *\n * @example\n * ```typescript\n * canonicalStringify({ a: 1, b: 2 }) === canonicalStringify({ b: 2, a: 1 })\n * // → true\n *\n * canonicalStringify(1n) !== canonicalStringify(1)\n * // → true\n * ```\n */\nexport function canonicalStringify(value: unknown): string {\n return write(value, new Set());\n}\n\nfunction write(value: unknown, seen: Set<object>): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n\n switch (typeof value) {\n case 'boolean':\n return value ? 'true' : 'false';\n case 'number':\n return writeNumber(value);\n case 'bigint':\n return `${value.toString()}n`;\n case 'string':\n return JSON.stringify(value);\n case 'function':\n throw new TypeError('canonicalStringify: functions are not supported');\n case 'symbol':\n throw new TypeError('canonicalStringify: symbols are not supported');\n }\n\n // From here, value is a non-null object.\n const obj = value as object;\n\n // Leaf object types are handled before touching `seen`: they can never\n // contain back-references, so cycle tracking is wasted work for them.\n if (value instanceof Date) {\n return `Date(${value.toISOString()})`;\n }\n\n // `Buffer` is a `Uint8Array` subclass; this branch covers both, and\n // emits the legacy `Bytes(<hex>)` tag so a `Buffer` and a same-content\n // `Uint8Array` digest identically.\n if (value instanceof Uint8Array) {\n return `Bytes(${bytesToHex(value)})`;\n }\n\n // Any other `ArrayBuffer` view — typed arrays (`Int8Array`,\n // `Uint16Array`, `Float64Array`, …) and `DataView`. Without this\n // branch they would fall through to the plain-object writer and\n // canonicalize as `{\"0\":1,\"1\":2,...}`, which would silently collide\n // with a same-keyed plain object. Tagging by constructor name keeps\n // distinct view families distinct.\n if (ArrayBuffer.isView(value)) {\n const tag = value.constructor.name;\n const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n return `${tag}(${bytesToHex(bytes)})`;\n }\n\n if (seen.has(obj)) {\n throw new TypeError('canonicalStringify: circular reference detected');\n }\n seen.add(obj);\n try {\n if (Array.isArray(value)) {\n const parts = value.map((item) => write(item, seen));\n return `[${parts.join(',')}]`;\n }\n\n return writePlainObject(obj as Record<string, unknown>, seen);\n } finally {\n seen.delete(obj);\n }\n}\n\nfunction writeNumber(value: number): string {\n if (Number.isNaN(value)) return 'NaN';\n if (value === Number.POSITIVE_INFINITY) return 'Infinity';\n if (value === Number.NEGATIVE_INFINITY) return '-Infinity';\n // Distinguish `+0` from `-0` so they hash differently.\n if (value === 0 && 1 / value === Number.NEGATIVE_INFINITY) return '-0';\n return String(value);\n}\n\nfunction writePlainObject(obj: Record<string, unknown>, seen: Set<object>): string {\n // Only true plain objects are accepted here. Without this guard, anything\n // that fell through the type-tagged branches above (`Map`, `Set`,\n // `RegExp`, class instances, …) would canonicalize to `{}` because\n // `Object.keys` returns no enumerable string keys for them — silently\n // colliding with each other and with the literal `{}`.\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) {\n const tag = proto?.constructor?.name ?? 'unknown';\n throw new TypeError(`canonicalStringify: non-plain objects are not supported (got ${tag})`);\n }\n\n // `Object.keys` ignores symbol-keyed properties, so they would be\n // silently dropped from the canonical form. Force callers to handle\n // them explicitly instead of producing a key that omits real data.\n if (Object.getOwnPropertySymbols(obj).length > 0) {\n throw new TypeError(\n 'canonicalStringify: objects with symbol-keyed properties are not supported',\n );\n }\n\n const keys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const key of keys) {\n parts.push(`${JSON.stringify(key)}:${write(obj[key], seen)}`);\n }\n return `{${parts.join(',')}}`;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i] as number;\n out += byte.toString(16).padStart(2, '0');\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,mBAAmB,OAAwB;CACzD,OAAO,MAAM,uBAAO,IAAI,KAAK,CAAC;;AAGhC,SAAS,MAAM,OAAgB,MAA2B;CACxD,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,UAAU,KAAA,GAAW,OAAO;CAEhC,QAAQ,OAAO,OAAf;EACE,KAAK,WACH,OAAO,QAAQ,SAAS;EAC1B,KAAK,UACH,OAAO,YAAY,MAAM;EAC3B,KAAK,UACH,OAAO,GAAG,MAAM,UAAU,CAAC;EAC7B,KAAK,UACH,OAAO,KAAK,UAAU,MAAM;EAC9B,KAAK,YACH,MAAM,IAAI,UAAU,kDAAkD;EACxE,KAAK,UACH,MAAM,IAAI,UAAU,gDAAgD;;CAIxE,MAAM,MAAM;CAIZ,IAAI,iBAAiB,MACnB,OAAO,QAAQ,MAAM,aAAa,CAAC;CAMrC,IAAI,iBAAiB,YACnB,OAAO,SAAS,WAAW,MAAM,CAAC;CASpC,IAAI,YAAY,OAAO,MAAM,EAG3B,OAAO,GAFK,MAAM,YAAY,KAEhB,GAAG,WAAW,IADV,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,WAClC,CAAC,CAAC;CAGrC,IAAI,KAAK,IAAI,IAAI,EACf,MAAM,IAAI,UAAU,kDAAkD;CAExE,KAAK,IAAI,IAAI;CACb,IAAI;EACF,IAAI,MAAM,QAAQ,MAAM,EAEtB,OAAO,IADO,MAAM,KAAK,SAAS,MAAM,MAAM,KAAK,CACnC,CAAC,KAAK,IAAI,CAAC;EAG7B,OAAO,iBAAiB,KAAgC,KAAK;WACrD;EACR,KAAK,OAAO,IAAI;;;AAIpB,SAAS,YAAY,OAAuB;CAC1C,IAAI,OAAO,MAAM,MAAM,EAAE,OAAO;CAChC,IAAI,UAAU,OAAO,mBAAmB,OAAO;CAC/C,IAAI,UAAU,OAAO,mBAAmB,OAAO;CAE/C,IAAI,UAAU,KAAK,IAAI,UAAU,OAAO,mBAAmB,OAAO;CAClE,OAAO,OAAO,MAAM;;AAGtB,SAAS,iBAAiB,KAA8B,MAA2B;CAMjF,MAAM,QAAQ,OAAO,eAAe,IAAI;CACxC,IAAI,UAAU,OAAO,aAAa,UAAU,MAAM;EAChD,MAAM,MAAM,OAAO,aAAa,QAAQ;EACxC,MAAM,IAAI,UAAU,gEAAgE,IAAI,GAAG;;CAM7F,IAAI,OAAO,sBAAsB,IAAI,CAAC,SAAS,GAC7C,MAAM,IAAI,UACR,6EACD;CAGH,MAAM,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM;CACpC,MAAM,QAAkB,EAAE;CAC1B,KAAK,MAAM,OAAO,MAChB,MAAM,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,KAAK,GAAG;CAE/D,OAAO,IAAI,MAAM,KAAK,IAAI,CAAC;;AAG7B,SAAS,WAAW,OAA2B;CAC7C,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,OAAO,KAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;;CAE3C,OAAO"}
1
+ {"version":3,"file":"canonical-stringify.mjs","names":[],"sources":["../src/canonical-stringify.ts"],"sourcesContent":["/**\n * Produces a deterministic, JSON-like string representation of a value.\n *\n * Designed for use as a stable identity / cache key. Two values that are\n * structurally equivalent — regardless of object key insertion order —\n * produce the same string. Two values that differ in any meaningful way\n * (including types that JSON would conflate, like `BigInt(1)` vs `1`)\n * produce different strings.\n *\n * Supported inputs:\n * - `null`, `undefined` (distinguishable: `null` → `\"null\"`, `undefined` → `\"undefined\"`)\n * - `boolean`, `string`, `number` (including `NaN`, `Infinity`, `-Infinity`)\n * - `bigint` (suffixed with `n` to disambiguate from `number`)\n * - `Date` (tagged + ISO string)\n * - `Buffer` / `Uint8Array` (tagged + hex-encoded as `Bytes(<hex>)`)\n * - Other `ArrayBuffer` views — `Int8Array`, `Uint16Array`, `Float64Array`,\n * `DataView`, etc. (tagged with the constructor name + hex-encoded over\n * the underlying bytes, e.g. `Uint16Array(<hex>)`). Note that the bytes\n * are read in host byte order, so callers that need cross-platform\n * stability for multi-byte typed arrays should normalize endianness\n * before passing the value in.\n * - Arrays (order-preserving)\n * - Plain objects (key-sorted) — only objects whose prototype is\n * `Object.prototype` or `null`. Non-plain objects (`Map`, `Set`,\n * `RegExp`, class instances, etc.) are rejected so they cannot silently\n * collapse to `{}` and collide with each other.\n *\n * Throws on `function`, `symbol`, circular references, non-plain objects,\n * and objects with symbol-keyed properties (which `Object.keys` would\n * silently drop). Callers that need to canonicalize any of these must\n * convert them to a supported representation first.\n *\n * The output format is intentionally not JSON: the type tags and BigInt\n * suffix mean it cannot be round-tripped via `JSON.parse`. The goal is\n * keying, not serialization.\n *\n * @example\n * ```typescript\n * canonicalStringify({ a: 1, b: 2 }) === canonicalStringify({ b: 2, a: 1 })\n * // → true\n *\n * canonicalStringify(1n) !== canonicalStringify(1)\n * // → true\n * ```\n */\nexport function canonicalStringify(value: unknown): string {\n return write(value, new Set());\n}\n\nfunction write(value: unknown, seen: Set<object>): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n\n switch (typeof value) {\n case 'boolean':\n return value ? 'true' : 'false';\n case 'number':\n return writeNumber(value);\n case 'bigint':\n return `${value.toString()}n`;\n case 'string':\n return JSON.stringify(value);\n case 'function':\n throw new TypeError('canonicalStringify: functions are not supported');\n case 'symbol':\n throw new TypeError('canonicalStringify: symbols are not supported');\n }\n\n // From here, value is a non-null object.\n const obj = value as object;\n\n // Leaf object types are handled before touching `seen`: they can never\n // contain back-references, so cycle tracking is wasted work for them.\n if (value instanceof Date) {\n return `Date(${value.toISOString()})`;\n }\n\n // `Buffer` is a `Uint8Array` subclass; this branch covers both, and\n // emits the legacy `Bytes(<hex>)` tag so a `Buffer` and a same-content\n // `Uint8Array` digest identically.\n if (value instanceof Uint8Array) {\n return `Bytes(${bytesToHex(value)})`;\n }\n\n // Any other `ArrayBuffer` view — typed arrays (`Int8Array`,\n // `Uint16Array`, `Float64Array`, …) and `DataView`. Without this\n // branch they would fall through to the plain-object writer and\n // canonicalize as `{\"0\":1,\"1\":2,...}`, which would silently collide\n // with a same-keyed plain object. Tagging by constructor name keeps\n // distinct view families distinct.\n if (ArrayBuffer.isView(value)) {\n const tag = value.constructor.name;\n const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n return `${tag}(${bytesToHex(bytes)})`;\n }\n\n if (seen.has(obj)) {\n throw new TypeError('canonicalStringify: circular reference detected');\n }\n seen.add(obj);\n try {\n if (Array.isArray(value)) {\n const parts = value.map((item) => write(item, seen));\n return `[${parts.join(',')}]`;\n }\n\n return writePlainObject(obj as Record<string, unknown>, seen);\n } finally {\n seen.delete(obj);\n }\n}\n\nfunction writeNumber(value: number): string {\n if (Number.isNaN(value)) return 'NaN';\n if (value === Number.POSITIVE_INFINITY) return 'Infinity';\n if (value === Number.NEGATIVE_INFINITY) return '-Infinity';\n // Distinguish `+0` from `-0` so they hash differently.\n if (value === 0 && 1 / value === Number.NEGATIVE_INFINITY) return '-0';\n return String(value);\n}\n\nfunction writePlainObject(obj: Record<string, unknown>, seen: Set<object>): string {\n // Only true plain objects are accepted here. Without this guard, anything\n // that fell through the type-tagged branches above (`Map`, `Set`,\n // `RegExp`, class instances, …) would canonicalize to `{}` because\n // `Object.keys` returns no enumerable string keys for them — silently\n // colliding with each other and with the literal `{}`.\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) {\n const tag = proto?.constructor?.name ?? 'unknown';\n throw new TypeError(`canonicalStringify: non-plain objects are not supported (got ${tag})`);\n }\n\n // `Object.keys` ignores symbol-keyed properties, so they would be\n // silently dropped from the canonical form. Force callers to handle\n // them explicitly instead of producing a key that omits real data.\n if (Object.getOwnPropertySymbols(obj).length > 0) {\n throw new TypeError(\n 'canonicalStringify: objects with symbol-keyed properties are not supported',\n );\n }\n\n const keys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const key of keys) {\n parts.push(`${JSON.stringify(key)}:${write(obj[key], seen)}`);\n }\n return `{${parts.join(',')}}`;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i] as number;\n out += byte.toString(16).padStart(2, '0');\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,mBAAmB,OAAwB;CACzD,OAAO,MAAM,uBAAO,IAAI,IAAI,CAAC;AAC/B;AAEA,SAAS,MAAM,OAAgB,MAA2B;CACxD,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,UAAU,KAAA,GAAW,OAAO;CAEhC,QAAQ,OAAO,OAAf;EACE,KAAK,WACH,OAAO,QAAQ,SAAS;EAC1B,KAAK,UACH,OAAO,YAAY,KAAK;EAC1B,KAAK,UACH,OAAO,GAAG,MAAM,SAAS,EAAE;EAC7B,KAAK,UACH,OAAO,KAAK,UAAU,KAAK;EAC7B,KAAK,YACH,MAAM,IAAI,UAAU,iDAAiD;EACvE,KAAK,UACH,MAAM,IAAI,UAAU,+CAA+C;CACvE;CAGA,MAAM,MAAM;CAIZ,IAAI,iBAAiB,MACnB,OAAO,QAAQ,MAAM,YAAY,EAAE;CAMrC,IAAI,iBAAiB,YACnB,OAAO,SAAS,WAAW,KAAK,EAAE;CASpC,IAAI,YAAY,OAAO,KAAK,GAG1B,OAAO,GAFK,MAAM,YAAY,KAEhB,GAAG,WAAW,IADV,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,UACnC,CAAC,EAAE;CAGrC,IAAI,KAAK,IAAI,GAAG,GACd,MAAM,IAAI,UAAU,iDAAiD;CAEvE,KAAK,IAAI,GAAG;CACZ,IAAI;EACF,IAAI,MAAM,QAAQ,KAAK,GAErB,OAAO,IADO,MAAM,KAAK,SAAS,MAAM,MAAM,IAAI,CACnC,EAAE,KAAK,GAAG,EAAE;EAG7B,OAAO,iBAAiB,KAAgC,IAAI;CAC9D,UAAU;EACR,KAAK,OAAO,GAAG;CACjB;AACF;AAEA,SAAS,YAAY,OAAuB;CAC1C,IAAI,OAAO,MAAM,KAAK,GAAG,OAAO;CAChC,IAAI,UAAU,OAAO,mBAAmB,OAAO;CAC/C,IAAI,UAAU,OAAO,mBAAmB,OAAO;CAE/C,IAAI,UAAU,KAAK,IAAI,UAAU,OAAO,mBAAmB,OAAO;CAClE,OAAO,OAAO,KAAK;AACrB;AAEA,SAAS,iBAAiB,KAA8B,MAA2B;CAMjF,MAAM,QAAQ,OAAO,eAAe,GAAG;CACvC,IAAI,UAAU,OAAO,aAAa,UAAU,MAAM;EAChD,MAAM,MAAM,OAAO,aAAa,QAAQ;EACxC,MAAM,IAAI,UAAU,gEAAgE,IAAI,EAAE;CAC5F;CAKA,IAAI,OAAO,sBAAsB,GAAG,EAAE,SAAS,GAC7C,MAAM,IAAI,UACR,4EACF;CAGF,MAAM,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK;CACnC,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,OAAO,MAChB,MAAM,KAAK,GAAG,KAAK,UAAU,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,IAAI,GAAG;CAE9D,OAAO,IAAI,MAAM,KAAK,GAAG,EAAE;AAC7B;AAEA,SAAS,WAAW,OAA2B;CAC7C,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,OAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;CAC1C;CACA,OAAO;AACT"}
@@ -0,0 +1,82 @@
1
+ //#region src/casts.ts
2
+ /**
3
+ * **Last-resort escape hatch for unsafe type assertions. Not a sanctioned tool to reach for.**
4
+ *
5
+ * Before reaching for `blindCast`, **rewrite the surrounding code so the cast becomes
6
+ * unnecessary**: tighten an input type, add a runtime check that narrows via a type
7
+ * predicate, restructure a generic so the compiler can see the relationship you're
8
+ * asserting, or use {@link castAs} when the value already satisfies the target type.
9
+ * Only when no rewrite is feasible does `blindCast` become the right answer — and at
10
+ * that point, the `Reason` literal you supply must articulate the compromise in
11
+ * language a reviewer can evaluate.
12
+ *
13
+ * The reviewer **will** validate the `Reason`. If it doesn't hold up under scrutiny,
14
+ * that is not a signal to soften the reason; it is a signal to go back and solve the
15
+ * underlying type-system problem properly. An unconvincing justification is rework,
16
+ * not a free pass.
17
+ *
18
+ * `blindCast` is the auditable form of `as Foo` / `as unknown as Foo`: it bypasses
19
+ * the compiler's checks (the input type is `unknown`, the output type is whatever the
20
+ * caller asks for), but it forces the unsafety to be named at the call site instead of
21
+ * smuggled in via a bare `as`. The `Reason` type parameter exists only at compile
22
+ * time — it is not present in the emitted JavaScript — but it is grep-able and
23
+ * visible to future readers.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const stringValue = blindCast<
28
+ * string,
29
+ * "JSON.parse returns `unknown`; this field is documented to be a string in the API contract"
30
+ * >(parsed[key]);
31
+ * ```
32
+ *
33
+ * @typeParam TargetType - The type the caller is asserting the input has.
34
+ * @typeParam _Reason - A string literal describing why bypassing the type system is necessary here.
35
+ * Only meaningful at compile time. The reviewer evaluates whether it justifies the unsafety.
36
+ */
37
+ function blindCast(input) {
38
+ return input;
39
+ }
40
+ /**
41
+ * Type-checked, runtime pass-through alternative to a bare `as Type` cast.
42
+ *
43
+ * Use `castAs` when the value already satisfies the target type but you want to make
44
+ * the type assertion explicit at the call site — for example, when an inferred type is
45
+ * wider than the type you want to publish, or when a literal object should be tagged
46
+ * with its nominal interface. Unlike {@link blindCast}, the compiler still checks that
47
+ * the value is assignable to the target type, so this helper cannot smuggle in an
48
+ * unsafe assertion.
49
+ *
50
+ * `castAs` exists alongside `blindCast` so authors pick the right name at the call
51
+ * site: a `castAs` is type-checked and benign; a `blindCast` is the unsafe escape
52
+ * hatch. The split makes review faster — readers know which casts to scrutinize and
53
+ * which are pure annotations.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * interface FancyObject {
58
+ * key: string;
59
+ * keyTwo: {
60
+ * subKey: string;
61
+ * subKeyTwo: number;
62
+ * };
63
+ * }
64
+ *
65
+ * const typedObject = castAs<FancyObject>({
66
+ * key: 'Chookede',
67
+ * keyTwo: {
68
+ * subKey: 'Choookeeeee',
69
+ * subKeyTwo: 2,
70
+ * },
71
+ * });
72
+ * ```
73
+ *
74
+ * @typeParam Type - The type to constrain and tag the value with. The value must be assignable to `Type`.
75
+ */
76
+ function castAs(value) {
77
+ return value;
78
+ }
79
+ //#endregion
80
+ export { castAs as n, blindCast as t };
81
+
82
+ //# sourceMappingURL=casts-D2-X_DNw.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"casts-D2-X_DNw.mjs","names":["x"],"sources":["../src/casts.ts"],"sourcesContent":["/**\n * **Last-resort escape hatch for unsafe type assertions. Not a sanctioned tool to reach for.**\n *\n * Before reaching for `blindCast`, **rewrite the surrounding code so the cast becomes\n * unnecessary**: tighten an input type, add a runtime check that narrows via a type\n * predicate, restructure a generic so the compiler can see the relationship you're\n * asserting, or use {@link castAs} when the value already satisfies the target type.\n * Only when no rewrite is feasible does `blindCast` become the right answer — and at\n * that point, the `Reason` literal you supply must articulate the compromise in\n * language a reviewer can evaluate.\n *\n * The reviewer **will** validate the `Reason`. If it doesn't hold up under scrutiny,\n * that is not a signal to soften the reason; it is a signal to go back and solve the\n * underlying type-system problem properly. An unconvincing justification is rework,\n * not a free pass.\n *\n * `blindCast` is the auditable form of `as Foo` / `as unknown as Foo`: it bypasses\n * the compiler's checks (the input type is `unknown`, the output type is whatever the\n * caller asks for), but it forces the unsafety to be named at the call site instead of\n * smuggled in via a bare `as`. The `Reason` type parameter exists only at compile\n * time — it is not present in the emitted JavaScript — but it is grep-able and\n * visible to future readers.\n *\n * @example\n * ```typescript\n * const stringValue = blindCast<\n * string,\n * \"JSON.parse returns `unknown`; this field is documented to be a string in the API contract\"\n * >(parsed[key]);\n * ```\n *\n * @typeParam TargetType - The type the caller is asserting the input has.\n * @typeParam _Reason - A string literal describing why bypassing the type system is necessary here.\n * Only meaningful at compile time. The reviewer evaluates whether it justifies the unsafety.\n */\nexport function blindCast<TargetType, _Reason extends string>(input: unknown): TargetType {\n // biome-ignore lint/suspicious/noExplicitAny: this helper is the single canonical escape hatch for type-unsafe casts in the codebase; the `any` is hyper-local, the unsafety is made explicit at every call site via the call's own `Reason` literal, and the reviewer evaluates whether that justification holds\n const x: any = input;\n return x;\n}\n\n/**\n * Type-checked, runtime pass-through alternative to a bare `as Type` cast.\n *\n * Use `castAs` when the value already satisfies the target type but you want to make\n * the type assertion explicit at the call site — for example, when an inferred type is\n * wider than the type you want to publish, or when a literal object should be tagged\n * with its nominal interface. Unlike {@link blindCast}, the compiler still checks that\n * the value is assignable to the target type, so this helper cannot smuggle in an\n * unsafe assertion.\n *\n * `castAs` exists alongside `blindCast` so authors pick the right name at the call\n * site: a `castAs` is type-checked and benign; a `blindCast` is the unsafe escape\n * hatch. The split makes review faster — readers know which casts to scrutinize and\n * which are pure annotations.\n *\n * @example\n * ```typescript\n * interface FancyObject {\n * key: string;\n * keyTwo: {\n * subKey: string;\n * subKeyTwo: number;\n * };\n * }\n *\n * const typedObject = castAs<FancyObject>({\n * key: 'Chookede',\n * keyTwo: {\n * subKey: 'Choookeeeee',\n * subKeyTwo: 2,\n * },\n * });\n * ```\n *\n * @typeParam Type - The type to constrain and tag the value with. The value must be assignable to `Type`.\n */\nexport function castAs<Type>(value: Type): Type {\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,UAA8C,OAA4B;CAGxF,OAAOA;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,OAAa,OAAmB;CAC9C,OAAO;AACT"}
@@ -0,0 +1,77 @@
1
+ //#region src/casts.d.ts
2
+ /**
3
+ * **Last-resort escape hatch for unsafe type assertions. Not a sanctioned tool to reach for.**
4
+ *
5
+ * Before reaching for `blindCast`, **rewrite the surrounding code so the cast becomes
6
+ * unnecessary**: tighten an input type, add a runtime check that narrows via a type
7
+ * predicate, restructure a generic so the compiler can see the relationship you're
8
+ * asserting, or use {@link castAs} when the value already satisfies the target type.
9
+ * Only when no rewrite is feasible does `blindCast` become the right answer — and at
10
+ * that point, the `Reason` literal you supply must articulate the compromise in
11
+ * language a reviewer can evaluate.
12
+ *
13
+ * The reviewer **will** validate the `Reason`. If it doesn't hold up under scrutiny,
14
+ * that is not a signal to soften the reason; it is a signal to go back and solve the
15
+ * underlying type-system problem properly. An unconvincing justification is rework,
16
+ * not a free pass.
17
+ *
18
+ * `blindCast` is the auditable form of `as Foo` / `as unknown as Foo`: it bypasses
19
+ * the compiler's checks (the input type is `unknown`, the output type is whatever the
20
+ * caller asks for), but it forces the unsafety to be named at the call site instead of
21
+ * smuggled in via a bare `as`. The `Reason` type parameter exists only at compile
22
+ * time — it is not present in the emitted JavaScript — but it is grep-able and
23
+ * visible to future readers.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const stringValue = blindCast<
28
+ * string,
29
+ * "JSON.parse returns `unknown`; this field is documented to be a string in the API contract"
30
+ * >(parsed[key]);
31
+ * ```
32
+ *
33
+ * @typeParam TargetType - The type the caller is asserting the input has.
34
+ * @typeParam _Reason - A string literal describing why bypassing the type system is necessary here.
35
+ * Only meaningful at compile time. The reviewer evaluates whether it justifies the unsafety.
36
+ */
37
+ declare function blindCast<TargetType, _Reason extends string>(input: unknown): TargetType;
38
+ /**
39
+ * Type-checked, runtime pass-through alternative to a bare `as Type` cast.
40
+ *
41
+ * Use `castAs` when the value already satisfies the target type but you want to make
42
+ * the type assertion explicit at the call site — for example, when an inferred type is
43
+ * wider than the type you want to publish, or when a literal object should be tagged
44
+ * with its nominal interface. Unlike {@link blindCast}, the compiler still checks that
45
+ * the value is assignable to the target type, so this helper cannot smuggle in an
46
+ * unsafe assertion.
47
+ *
48
+ * `castAs` exists alongside `blindCast` so authors pick the right name at the call
49
+ * site: a `castAs` is type-checked and benign; a `blindCast` is the unsafe escape
50
+ * hatch. The split makes review faster — readers know which casts to scrutinize and
51
+ * which are pure annotations.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * interface FancyObject {
56
+ * key: string;
57
+ * keyTwo: {
58
+ * subKey: string;
59
+ * subKeyTwo: number;
60
+ * };
61
+ * }
62
+ *
63
+ * const typedObject = castAs<FancyObject>({
64
+ * key: 'Chookede',
65
+ * keyTwo: {
66
+ * subKey: 'Choookeeeee',
67
+ * subKeyTwo: 2,
68
+ * },
69
+ * });
70
+ * ```
71
+ *
72
+ * @typeParam Type - The type to constrain and tag the value with. The value must be assignable to `Type`.
73
+ */
74
+ declare function castAs<Type>(value: Type): Type;
75
+ //#endregion
76
+ export { blindCast, castAs };
77
+ //# sourceMappingURL=casts.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"casts.d.mts","names":[],"sources":["../src/casts.ts"],"mappings":";;AAmCA;;;;;;;;;AAAyF;AA0CzF;;;;;;;;;AAA+C;;;;;;;;;;;;;;;iBA1C/B,SAAA,oCAAA,CAA8C,KAAA,YAAiB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0CzE,MAAA,MAAA,CAAa,KAAA,EAAO,IAAA,GAAO,IAAI"}
package/dist/casts.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { n as castAs, t as blindCast } from "./casts-D2-X_DNw.mjs";
2
+ export { blindCast, castAs };
@@ -27,4 +27,4 @@ function ifDefined(key, value) {
27
27
  //#endregion
28
28
  export { ifDefined as t };
29
29
 
30
- //# sourceMappingURL=defined-BYcWqtXq.mjs.map
30
+ //# sourceMappingURL=defined-BnXRnErx.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"defined-BYcWqtXq.mjs","names":[],"sources":["../src/defined.ts"],"sourcesContent":["/**\n * Returns an object with the key/value if value is defined, otherwise an empty object.\n *\n * Use with spread to conditionally include optional properties while satisfying\n * exactOptionalPropertyTypes. This is explicit about which properties are optional\n * and won't inadvertently strip other undefined values.\n *\n * @example\n * ```typescript\n * // Instead of:\n * const obj = {\n * required: 'value',\n * ...(optional ? { optional } : {}),\n * };\n *\n * // Use:\n * const obj = {\n * required: 'value',\n * ...ifDefined('optional', optional),\n * };\n * ```\n */\nexport function ifDefined<K extends string, V>(\n key: K,\n value: V | undefined,\n): Record<never, never> | { [P in K]: V } {\n return value !== undefined ? ({ [key]: value } as { [P in K]: V }) : {};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,UACd,KACA,OACwC;CACxC,OAAO,UAAU,KAAA,IAAa,GAAG,MAAM,OAAO,GAAuB,EAAE"}
1
+ {"version":3,"file":"defined-BnXRnErx.mjs","names":[],"sources":["../src/defined.ts"],"sourcesContent":["/**\n * Returns an object with the key/value if value is defined, otherwise an empty object.\n *\n * Use with spread to conditionally include optional properties while satisfying\n * exactOptionalPropertyTypes. This is explicit about which properties are optional\n * and won't inadvertently strip other undefined values.\n *\n * @example\n * ```typescript\n * // Instead of:\n * const obj = {\n * required: 'value',\n * ...(optional ? { optional } : {}),\n * };\n *\n * // Use:\n * const obj = {\n * required: 'value',\n * ...ifDefined('optional', optional),\n * };\n * ```\n */\nexport function ifDefined<K extends string, V>(\n key: K,\n value: V | undefined,\n): Record<never, never> | { [P in K]: V } {\n return value !== undefined ? ({ [key]: value } as { [P in K]: V }) : {};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,UACd,KACA,OACwC;CACxC,OAAO,UAAU,KAAA,IAAa,GAAG,MAAM,MAAM,IAAwB,CAAC;AACxE"}
package/dist/defined.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as ifDefined } from "./defined-BYcWqtXq.mjs";
1
+ import { t as ifDefined } from "./defined-BnXRnErx.mjs";
2
2
  export { ifDefined };
@@ -1 +1 @@
1
- {"version":3,"file":"hash-content.d.mts","names":[],"sources":["../src/hash-content.ts"],"mappings":";;AAqDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAsB,WAAA,CAAY,KAAA,WAAgB,OAAA"}
1
+ {"version":3,"file":"hash-content.d.mts","names":[],"sources":["../src/hash-content.ts"],"mappings":";;AAqDA;;;;AAAyD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAnC,WAAA,CAAY,KAAA,WAAgB,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"hash-content.mjs","names":[],"sources":["../src/hash-content.ts"],"sourcesContent":["/**\n * Hashes a canonical-string representation of an execution into a bounded,\n * opaque cache-key digest.\n *\n * Designed for use as the final step of `RuntimeMiddlewareContext.contentHash`\n * implementations: family runtimes compose a canonical string from\n * `meta.storageHash`, the rendered statement (or wire command), and\n * canonicalized parameters via `canonicalStringify`, then pipe the result\n * through this helper.\n *\n * Why hash the canonical string instead of using it directly as a `Map` key:\n *\n * 1. **Bounded memory.** A raw canonical key includes concrete parameter\n * values, so a query bound to a 10 MB JSON column or binary blob produces\n * a 10 MB cache key. With `maxEntries = 1000`, that scales to gigabytes\n * of cache keys alone. SHA-512 pins per-key cost at a fixed digest\n * length regardless of input size.\n *\n * 2. **Sensitive-data isolation.** The canonical string contains parameter\n * values verbatim. Cache keys flow into debug logs, Redis `KEYS`/`MONITOR`\n * output, persistence dumps, monitoring tools, and any user-supplied\n * `CacheStore` implementation. Hashing prevents PII / credentials /\n * tokens that appear in query parameters from showing up in any of those\n * surfaces.\n *\n * Algorithm choice — SHA-512 (`SHA-512` via the WebCrypto API):\n *\n * - **Portability.** WebCrypto (`globalThis.crypto.subtle`) is available in\n * every modern JavaScript runtime — Node, Deno, Bun, browsers, edge\n * workers — without importing a Node-specific module. This keeps the\n * helper usable in non-Node hosts where `node:crypto` is not available.\n * - **Collision space.** 512 bits of output makes accidental collisions\n * astronomically improbable — far beyond what a cache needs, but the\n * incremental cost over 256-bit output is negligible and the headroom\n * is free.\n * - **No additional dependency.** SHA-512 is part of the WebCrypto standard\n * set of digest algorithms; no third-party package needed.\n *\n * The function is `async` because the WebCrypto digest API is async by\n * design. Callers must await the result.\n *\n * Output format: `sha512:HEXDIGEST` (128-char hex with the algorithm tag\n * prefix). Self-describing so a future migration to a different hash\n * produces visibly distinct keys, and consistent with the\n * `sha256:HEXDIGEST` shape already used by `meta.storageHash`.\n *\n * @example\n * ```typescript\n * const canonical = `${exec.meta.storageHash}|${exec.sql}|${canonicalStringify(exec.params)}`;\n * return await hashContent(canonical);\n * // → 'sha512:8f3...e1c' (always 135 chars: 'sha512:' + 128 hex chars)\n * ```\n */\nexport async function hashContent(value: string): Promise<string> {\n const bytes = new TextEncoder().encode(value);\n const digest = await crypto.subtle.digest('SHA-512', bytes);\n const hex = Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');\n return `sha512:${hex}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,eAAsB,YAAY,OAAgC;CAChE,MAAM,QAAQ,IAAI,aAAa,CAAC,OAAO,MAAM;CAC7C,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,MAAM;CAE3D,OAAO,UADK,MAAM,KAAK,IAAI,WAAW,OAAO,GAAG,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,GACxE"}
1
+ {"version":3,"file":"hash-content.mjs","names":[],"sources":["../src/hash-content.ts"],"sourcesContent":["/**\n * Hashes a canonical-string representation of an execution into a bounded,\n * opaque cache-key digest.\n *\n * Designed for use as the final step of `RuntimeMiddlewareContext.contentHash`\n * implementations: family runtimes compose a canonical string from\n * `meta.storageHash`, the rendered statement (or wire command), and\n * canonicalized parameters via `canonicalStringify`, then pipe the result\n * through this helper.\n *\n * Why hash the canonical string instead of using it directly as a `Map` key:\n *\n * 1. **Bounded memory.** A raw canonical key includes concrete parameter\n * values, so a query bound to a 10 MB JSON column or binary blob produces\n * a 10 MB cache key. With `maxEntries = 1000`, that scales to gigabytes\n * of cache keys alone. SHA-512 pins per-key cost at a fixed digest\n * length regardless of input size.\n *\n * 2. **Sensitive-data isolation.** The canonical string contains parameter\n * values verbatim. Cache keys flow into debug logs, Redis `KEYS`/`MONITOR`\n * output, persistence dumps, monitoring tools, and any user-supplied\n * `CacheStore` implementation. Hashing prevents PII / credentials /\n * tokens that appear in query parameters from showing up in any of those\n * surfaces.\n *\n * Algorithm choice — SHA-512 (`SHA-512` via the WebCrypto API):\n *\n * - **Portability.** WebCrypto (`globalThis.crypto.subtle`) is available in\n * every modern JavaScript runtime — Node, Deno, Bun, browsers, edge\n * workers — without importing a Node-specific module. This keeps the\n * helper usable in non-Node hosts where `node:crypto` is not available.\n * - **Collision space.** 512 bits of output makes accidental collisions\n * astronomically improbable — far beyond what a cache needs, but the\n * incremental cost over 256-bit output is negligible and the headroom\n * is free.\n * - **No additional dependency.** SHA-512 is part of the WebCrypto standard\n * set of digest algorithms; no third-party package needed.\n *\n * The function is `async` because the WebCrypto digest API is async by\n * design. Callers must await the result.\n *\n * Output format: `sha512:HEXDIGEST` (128-char hex with the algorithm tag\n * prefix). Self-describing so a future migration to a different hash\n * produces visibly distinct keys, and consistent with the\n * `sha256:HEXDIGEST` shape already used by `meta.storageHash`.\n *\n * @example\n * ```typescript\n * const canonical = `${exec.meta.storageHash}|${exec.sql}|${canonicalStringify(exec.params)}`;\n * return await hashContent(canonical);\n * // → 'sha512:8f3...e1c' (always 135 chars: 'sha512:' + 128 hex chars)\n * ```\n */\nexport async function hashContent(value: string): Promise<string> {\n const bytes = new TextEncoder().encode(value);\n const digest = await crypto.subtle.digest('SHA-512', bytes);\n const hex = Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');\n return `sha512:${hex}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,eAAsB,YAAY,OAAgC;CAChE,MAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,KAAK;CAC5C,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK;CAE1D,OAAO,UADK,MAAM,KAAK,IAAI,WAAW,MAAM,IAAI,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EACzE;AACrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.mts","names":[],"sources":["../src/json.ts"],"mappings":";;AAgBA;;;;;AAEA;;;;;AAEA;;;;KAJY,aAAA;AAAA,KAEA,SAAA,YAAqB,SAAA;AAAA,KAErB,UAAA;EAAA,UAAyB,GAAA,WAAc,SAAA;AAAA;AAAA,KAEvC,SAAA,GAAY,aAAA,GAAgB,SAAA,GAAY,UAAA"}
1
+ {"version":3,"file":"json.d.mts","names":[],"sources":["../src/json.ts"],"mappings":";;AAgBA;;;;AAAyB;AAEzB;;;;AAA0C;AAE1C;;;;KAJY,aAAA;AAAA,KAEA,SAAA,YAAqB,SAAS;AAAA,KAE9B,UAAA;EAAA,UAAyB,GAAA,WAAc,SAAS;AAAA;AAAA,KAEhD,SAAA,GAAY,aAAA,GAAgB,SAAA,GAAY,UAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"redact-db-url.d.mts","names":[],"sources":["../src/redact-db-url.ts"],"mappings":";;AAMA;;;UAAiB,mBAAA;EAAA,SACN,IAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;AAAA;;AASX;;;;;iBAAgB,iBAAA,CAAkB,GAAA,WAAc,mBAAA"}
1
+ {"version":3,"file":"redact-db-url.d.mts","names":[],"sources":["../src/redact-db-url.ts"],"mappings":";;AAMA;;;UAAiB,mBAAA;EAAA,SACN,IAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;AAAA;AAAQ;AASnB;;;;AAAmE;AAThD,iBASH,iBAAA,CAAkB,GAAA,WAAc,mBAAmB"}
@@ -1,4 +1,4 @@
1
- import { t as ifDefined } from "./defined-BYcWqtXq.mjs";
1
+ import { t as ifDefined } from "./defined-BnXRnErx.mjs";
2
2
  //#region src/redact-db-url.ts
3
3
  /**
4
4
  * Redacts a database connection URL to a minimal metadata object.
@@ -1 +1 @@
1
- {"version":3,"file":"redact-db-url.mjs","names":[],"sources":["../src/redact-db-url.ts"],"sourcesContent":["import { ifDefined } from './defined';\n\n/**\n * Minimal metadata extracted from a database URL for logging or error output.\n * Sensitive fields (password, full URL) are never returned.\n */\nexport interface RedactedDatabaseUrl {\n readonly host?: string;\n readonly port?: string;\n readonly database?: string;\n readonly username?: string;\n}\n\n/**\n * Redacts a database connection URL to a minimal metadata object.\n *\n * Parsing errors are ignored and result in an empty object so callers never\n * leak raw URLs when the input is malformed.\n */\nexport function redactDatabaseUrl(url: string): RedactedDatabaseUrl {\n try {\n const parsed = new URL(url);\n const database = parsed.pathname?.replace(/^\\//, '') || undefined;\n return {\n ...ifDefined('host', parsed.hostname || undefined),\n ...ifDefined('port', parsed.port || undefined),\n ...ifDefined('database', database),\n ...ifDefined('username', parsed.username || undefined),\n };\n } catch {\n // Ignore parsing errors; return empty metadata\n return {};\n }\n}\n"],"mappings":";;;;;;;;AAmBA,SAAgB,kBAAkB,KAAkC;CAClE,IAAI;EACF,MAAM,SAAS,IAAI,IAAI,IAAI;EAC3B,MAAM,WAAW,OAAO,UAAU,QAAQ,OAAO,GAAG,IAAI,KAAA;EACxD,OAAO;GACL,GAAG,UAAU,QAAQ,OAAO,YAAY,KAAA,EAAU;GAClD,GAAG,UAAU,QAAQ,OAAO,QAAQ,KAAA,EAAU;GAC9C,GAAG,UAAU,YAAY,SAAS;GAClC,GAAG,UAAU,YAAY,OAAO,YAAY,KAAA,EAAU;GACvD;SACK;EAEN,OAAO,EAAE"}
1
+ {"version":3,"file":"redact-db-url.mjs","names":[],"sources":["../src/redact-db-url.ts"],"sourcesContent":["import { ifDefined } from './defined';\n\n/**\n * Minimal metadata extracted from a database URL for logging or error output.\n * Sensitive fields (password, full URL) are never returned.\n */\nexport interface RedactedDatabaseUrl {\n readonly host?: string;\n readonly port?: string;\n readonly database?: string;\n readonly username?: string;\n}\n\n/**\n * Redacts a database connection URL to a minimal metadata object.\n *\n * Parsing errors are ignored and result in an empty object so callers never\n * leak raw URLs when the input is malformed.\n */\nexport function redactDatabaseUrl(url: string): RedactedDatabaseUrl {\n try {\n const parsed = new URL(url);\n const database = parsed.pathname?.replace(/^\\//, '') || undefined;\n return {\n ...ifDefined('host', parsed.hostname || undefined),\n ...ifDefined('port', parsed.port || undefined),\n ...ifDefined('database', database),\n ...ifDefined('username', parsed.username || undefined),\n };\n } catch {\n // Ignore parsing errors; return empty metadata\n return {};\n }\n}\n"],"mappings":";;;;;;;;AAmBA,SAAgB,kBAAkB,KAAkC;CAClE,IAAI;EACF,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,MAAM,WAAW,OAAO,UAAU,QAAQ,OAAO,EAAE,KAAK,KAAA;EACxD,OAAO;GACL,GAAG,UAAU,QAAQ,OAAO,YAAY,KAAA,CAAS;GACjD,GAAG,UAAU,QAAQ,OAAO,QAAQ,KAAA,CAAS;GAC7C,GAAG,UAAU,YAAY,QAAQ;GACjC,GAAG,UAAU,YAAY,OAAO,YAAY,KAAA,CAAS;EACvD;CACF,QAAQ;EAEN,OAAO,CAAC;CACV;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.mts","names":[],"sources":["../src/result.ts"],"mappings":";;AAeA;;;;;;;;;;;;;UAAiB,EAAA;EAAA,SACN,EAAA;EAAA,SACA,KAAA,EAAO,CAAA;EAChB,QAAA,IAAY,CAAA;EACZ,WAAA;AAAA;;;;UAMe,KAAA;EAAA,SACN,EAAA;EAAA,SACA,OAAA,EAAS,CAAA;EAClB,QAAA;EACA,WAAA,IAAe,CAAA;AAAA;;;;;;;KASL,MAAA,SAAe,EAAA,CAAG,CAAA,IAAK,KAAA,CAAM,CAAA;;;;iBAgFzB,EAAA,GAAA,CAAM,KAAA,EAAO,CAAA,GAAI,EAAA,CAAG,CAAA;;;;iBAOpB,KAAA,GAAA,CAAS,OAAA,EAAS,CAAA,GAAI,KAAA,CAAM,CAAA;AAP5C;;;;AAAA,iBAqBgB,MAAA,CAAA,GAAU,EAAA"}
1
+ {"version":3,"file":"result.d.mts","names":[],"sources":["../src/result.ts"],"mappings":";;AAiBA;;;;;;;;;;;;AAIa;UAJI,EAAA;EAAA,SACN,EAAA;EAAA,SACA,KAAA,EAAO,CAAA;EAChB,QAAA,IAAY,CAAC;EACb,WAAA;AAAA;;;;UAMe,KAAA;EAAA,SACN,EAAA;EAAA,SACA,OAAA,EAAS,CAAA;EAClB,QAAA;EACA,WAAA,IAAe,CAAC;AAAA;;;;;;;KASN,MAAA,SAAe,EAAA,CAAG,CAAA,IAAK,KAAA,CAAM,CAAA;;;;iBAkFzB,EAAA,GAAA,CAAM,KAAA,EAAO,CAAA,GAAI,EAAA,CAAG,CAAA;;;;iBAOpB,KAAA,GAAA,CAAS,OAAA,EAAS,CAAA,GAAI,KAAA,CAAM,CAAA;AAP5C;;;;AAAA,iBAqBgB,MAAA,CAAA,GAAU,EAAE"}
package/dist/result.mjs CHANGED
@@ -1,5 +1,17 @@
1
+ import { t as blindCast } from "./casts-D2-X_DNw.mjs";
1
2
  //#region src/result.ts
2
3
  /**
4
+ * Generic Result type for representing success or failure outcomes.
5
+ *
6
+ * This is the standard way to return "expected failures" as values rather than
7
+ * throwing exceptions. See docs/Error Handling.md for the full taxonomy.
8
+ *
9
+ * Naming rationale:
10
+ * - `Ok<T>` / `NotOk<F>` mirror the `ok: true/false` discriminator
11
+ * - `NotOk` avoids collision with domain types like "Failure" or "Error"
12
+ * - `failure` property distinguishes from JS Error semantics
13
+ */
14
+ /**
3
15
  * Result class that implements both Ok and NotOk variants.
4
16
  */
5
17
  var ResultImpl = class ResultImpl {
@@ -24,13 +36,13 @@ var ResultImpl = class ResultImpl {
24
36
  * Creates a successful result.
25
37
  */
26
38
  static ok(value) {
27
- return new ResultImpl(true, value);
39
+ return blindCast(new ResultImpl(true, value));
28
40
  }
29
41
  /**
30
42
  * Creates an unsuccessful result.
31
43
  */
32
44
  static notOk(failure) {
33
- return new ResultImpl(false, failure);
45
+ return blindCast(new ResultImpl(false, failure));
34
46
  }
35
47
  /**
36
48
  * Asserts that this result is Ok and returns the value.
@@ -1 +1 @@
1
- {"version":3,"file":"result.mjs","names":[],"sources":["../src/result.ts"],"sourcesContent":["/**\n * Generic Result type for representing success or failure outcomes.\n *\n * This is the standard way to return \"expected failures\" as values rather than\n * throwing exceptions. See docs/Error Handling.md for the full taxonomy.\n *\n * Naming rationale:\n * - `Ok<T>` / `NotOk<F>` mirror the `ok: true/false` discriminator\n * - `NotOk` avoids collision with domain types like \"Failure\" or \"Error\"\n * - `failure` property distinguishes from JS Error semantics\n */\n\n/**\n * Represents a successful result containing a value.\n */\nexport interface Ok<T> {\n readonly ok: true;\n readonly value: T;\n assertOk(): T;\n assertNotOk(): never;\n}\n\n/**\n * Represents an unsuccessful result containing failure details.\n */\nexport interface NotOk<F> {\n readonly ok: false;\n readonly failure: F;\n assertOk(): never;\n assertNotOk(): F;\n}\n\n/**\n * A discriminated union representing either success (Ok) or failure (NotOk).\n *\n * @typeParam T - The success value type\n * @typeParam F - The failure details type\n */\nexport type Result<T, F> = Ok<T> | NotOk<F>;\n\n/**\n * Result class that implements both Ok and NotOk variants.\n */\nclass ResultImpl<T, F> {\n readonly ok: boolean;\n private readonly _value?: T;\n private readonly _failure?: F;\n\n private constructor(ok: boolean, valueOrFailure: T | F) {\n this.ok = ok;\n if (ok) {\n this._value = valueOrFailure as T;\n } else {\n this._failure = valueOrFailure as F;\n }\n Object.freeze(this);\n }\n\n get value(): T {\n if (!this.ok) {\n throw new Error('Cannot access value on NotOk result');\n }\n // biome-ignore lint/style/noNonNullAssertion: must be present if ok is true\n return this._value!;\n }\n\n get failure(): F {\n if (this.ok) {\n throw new Error('Cannot access failure on Ok result');\n }\n // biome-ignore lint/style/noNonNullAssertion: must be present if ok is false\n return this._failure!;\n }\n\n /**\n * Creates a successful result.\n */\n static ok<T, F = never>(value: T): Ok<T> {\n // TypeScript cannot express discriminated return types for a single implementation.\n // Cast is safe: ok=true guarantees this is an Ok<T> at runtime.\n return new ResultImpl<T, F>(true, value) as unknown as Ok<T>;\n }\n\n /**\n * Creates an unsuccessful result.\n */\n static notOk<T = never, F = unknown>(failure: F): NotOk<F> {\n // TypeScript cannot express discriminated return types for a single implementation.\n // Cast is safe: ok=false guarantees this is a NotOk<F> at runtime.\n return new ResultImpl<T, F>(false, failure) as unknown as NotOk<F>;\n }\n\n /**\n * Asserts that this result is Ok and returns the value.\n * Throws if the result is NotOk.\n */\n assertOk(this: Result<T, F>): T {\n if (!this.ok) {\n throw new Error('Expected Ok result but got NotOk');\n }\n return this.value;\n }\n\n /**\n * Asserts that this result is NotOk and returns the failure.\n * Throws if the result is Ok.\n */\n assertNotOk(this: Result<T, F>): F {\n if (this.ok) {\n throw new Error('Expected NotOk result but got Ok');\n }\n return this.failure;\n }\n}\n\n/**\n * Creates a successful result.\n */\nexport function ok<T>(value: T): Ok<T> {\n return ResultImpl.ok(value);\n}\n\n/**\n * Creates an unsuccessful result.\n */\nexport function notOk<F>(failure: F): NotOk<F> {\n return ResultImpl.notOk(failure);\n}\n\n/**\n * Singleton for void success results.\n * Use this for validation checks that don't produce a value.\n */\nconst OK_VOID: Ok<void> = ResultImpl.ok<void>(undefined);\n\n/**\n * Returns a successful void result.\n * Use this for validation checks that don't produce a value.\n */\nexport function okVoid(): Ok<void> {\n return OK_VOID;\n}\n"],"mappings":";;;;AA2CA,IAAM,aAAN,MAAM,WAAiB;CACrB;CACA;CACA;CAEA,YAAoB,IAAa,gBAAuB;EACtD,KAAK,KAAK;EACV,IAAI,IACF,KAAK,SAAS;OAEd,KAAK,WAAW;EAElB,OAAO,OAAO,KAAK;;CAGrB,IAAI,QAAW;EACb,IAAI,CAAC,KAAK,IACR,MAAM,IAAI,MAAM,sCAAsC;EAGxD,OAAO,KAAK;;CAGd,IAAI,UAAa;EACf,IAAI,KAAK,IACP,MAAM,IAAI,MAAM,qCAAqC;EAGvD,OAAO,KAAK;;;;;CAMd,OAAO,GAAiB,OAAiB;EAGvC,OAAO,IAAI,WAAiB,MAAM,MAAM;;;;;CAM1C,OAAO,MAA8B,SAAsB;EAGzD,OAAO,IAAI,WAAiB,OAAO,QAAQ;;;;;;CAO7C,WAAgC;EAC9B,IAAI,CAAC,KAAK,IACR,MAAM,IAAI,MAAM,mCAAmC;EAErD,OAAO,KAAK;;;;;;CAOd,cAAmC;EACjC,IAAI,KAAK,IACP,MAAM,IAAI,MAAM,mCAAmC;EAErD,OAAO,KAAK;;;;;;AAOhB,SAAgB,GAAM,OAAiB;CACrC,OAAO,WAAW,GAAG,MAAM;;;;;AAM7B,SAAgB,MAAS,SAAsB;CAC7C,OAAO,WAAW,MAAM,QAAQ;;;;;;AAOlC,MAAM,UAAoB,WAAW,GAAS,KAAA,EAAU;;;;;AAMxD,SAAgB,SAAmB;CACjC,OAAO"}
1
+ {"version":3,"file":"result.mjs","names":[],"sources":["../src/result.ts"],"sourcesContent":["/**\n * Generic Result type for representing success or failure outcomes.\n *\n * This is the standard way to return \"expected failures\" as values rather than\n * throwing exceptions. See docs/Error Handling.md for the full taxonomy.\n *\n * Naming rationale:\n * - `Ok<T>` / `NotOk<F>` mirror the `ok: true/false` discriminator\n * - `NotOk` avoids collision with domain types like \"Failure\" or \"Error\"\n * - `failure` property distinguishes from JS Error semantics\n */\n\nimport { blindCast } from './casts';\n\n/**\n * Represents a successful result containing a value.\n */\nexport interface Ok<T> {\n readonly ok: true;\n readonly value: T;\n assertOk(): T;\n assertNotOk(): never;\n}\n\n/**\n * Represents an unsuccessful result containing failure details.\n */\nexport interface NotOk<F> {\n readonly ok: false;\n readonly failure: F;\n assertOk(): never;\n assertNotOk(): F;\n}\n\n/**\n * A discriminated union representing either success (Ok) or failure (NotOk).\n *\n * @typeParam T - The success value type\n * @typeParam F - The failure details type\n */\nexport type Result<T, F> = Ok<T> | NotOk<F>;\n\n/**\n * Result class that implements both Ok and NotOk variants.\n */\nclass ResultImpl<T, F> {\n readonly ok: boolean;\n private readonly _value?: T;\n private readonly _failure?: F;\n\n private constructor(ok: boolean, valueOrFailure: T | F) {\n this.ok = ok;\n if (ok) {\n this._value = valueOrFailure as T;\n } else {\n this._failure = valueOrFailure as F;\n }\n Object.freeze(this);\n }\n\n get value(): T {\n if (!this.ok) {\n throw new Error('Cannot access value on NotOk result');\n }\n // biome-ignore lint/style/noNonNullAssertion: must be present if ok is true\n return this._value!;\n }\n\n get failure(): F {\n if (this.ok) {\n throw new Error('Cannot access failure on Ok result');\n }\n // biome-ignore lint/style/noNonNullAssertion: must be present if ok is false\n return this._failure!;\n }\n\n /**\n * Creates a successful result.\n */\n static ok<T, F = never>(value: T): Ok<T> {\n return blindCast<\n Ok<T>,\n 'ResultImpl is the single implementation of the Result discriminated union; TypeScript cannot express discriminated return types for a single class. ok=true guarantees this is an Ok<T> at runtime.'\n >(new ResultImpl<T, F>(true, value));\n }\n\n /**\n * Creates an unsuccessful result.\n */\n static notOk<T = never, F = unknown>(failure: F): NotOk<F> {\n return blindCast<\n NotOk<F>,\n 'ResultImpl is the single implementation of the Result discriminated union; TypeScript cannot express discriminated return types for a single class. ok=false guarantees this is a NotOk<F> at runtime.'\n >(new ResultImpl<T, F>(false, failure));\n }\n\n /**\n * Asserts that this result is Ok and returns the value.\n * Throws if the result is NotOk.\n */\n assertOk(this: Result<T, F>): T {\n if (!this.ok) {\n throw new Error('Expected Ok result but got NotOk');\n }\n return this.value;\n }\n\n /**\n * Asserts that this result is NotOk and returns the failure.\n * Throws if the result is Ok.\n */\n assertNotOk(this: Result<T, F>): F {\n if (this.ok) {\n throw new Error('Expected NotOk result but got Ok');\n }\n return this.failure;\n }\n}\n\n/**\n * Creates a successful result.\n */\nexport function ok<T>(value: T): Ok<T> {\n return ResultImpl.ok(value);\n}\n\n/**\n * Creates an unsuccessful result.\n */\nexport function notOk<F>(failure: F): NotOk<F> {\n return ResultImpl.notOk(failure);\n}\n\n/**\n * Singleton for void success results.\n * Use this for validation checks that don't produce a value.\n */\nconst OK_VOID: Ok<void> = ResultImpl.ok<void>(undefined);\n\n/**\n * Returns a successful void result.\n * Use this for validation checks that don't produce a value.\n */\nexport function okVoid(): Ok<void> {\n return OK_VOID;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA6CA,IAAM,aAAN,MAAM,WAAiB;CACrB;CACA;CACA;CAEA,YAAoB,IAAa,gBAAuB;EACtD,KAAK,KAAK;EACV,IAAI,IACF,KAAK,SAAS;OAEd,KAAK,WAAW;EAElB,OAAO,OAAO,IAAI;CACpB;CAEA,IAAI,QAAW;EACb,IAAI,CAAC,KAAK,IACR,MAAM,IAAI,MAAM,qCAAqC;EAGvD,OAAO,KAAK;CACd;CAEA,IAAI,UAAa;EACf,IAAI,KAAK,IACP,MAAM,IAAI,MAAM,oCAAoC;EAGtD,OAAO,KAAK;CACd;;;;CAKA,OAAO,GAAiB,OAAiB;EACvC,OAAO,UAGL,IAAI,WAAiB,MAAM,KAAK,CAAC;CACrC;;;;CAKA,OAAO,MAA8B,SAAsB;EACzD,OAAO,UAGL,IAAI,WAAiB,OAAO,OAAO,CAAC;CACxC;;;;;CAMA,WAAgC;EAC9B,IAAI,CAAC,KAAK,IACR,MAAM,IAAI,MAAM,kCAAkC;EAEpD,OAAO,KAAK;CACd;;;;;CAMA,cAAmC;EACjC,IAAI,KAAK,IACP,MAAM,IAAI,MAAM,kCAAkC;EAEpD,OAAO,KAAK;CACd;AACF;;;;AAKA,SAAgB,GAAM,OAAiB;CACrC,OAAO,WAAW,GAAG,KAAK;AAC5B;;;;AAKA,SAAgB,MAAS,SAAsB;CAC7C,OAAO,WAAW,MAAM,OAAO;AACjC;;;;;AAMA,MAAM,UAAoB,WAAW,GAAS,KAAA,CAAS;;;;;AAMvD,SAAgB,SAAmB;CACjC,OAAO;AACT"}
package/package.json CHANGED
@@ -1,29 +1,35 @@
1
1
  {
2
2
  "name": "@prisma-next/utils",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Shared utility functions for Prisma Next",
8
8
  "devDependencies": {
9
- "@prisma-next/tsconfig": "0.11.0",
10
- "@prisma-next/tsdown": "0.11.0",
9
+ "@prisma-next/tsconfig": "0.12.0",
10
+ "@prisma-next/tsdown": "0.12.0",
11
11
  "tsdown": "0.22.0",
12
12
  "typescript": "5.9.3",
13
13
  "vitest": "4.1.6"
14
14
  },
15
+ "peerDependencies": {
16
+ "typescript": ">=5.9"
17
+ },
18
+ "peerDependenciesMeta": {
19
+ "typescript": {
20
+ "optional": true
21
+ }
22
+ },
15
23
  "files": [
16
24
  "dist",
17
25
  "src"
18
26
  ],
19
- "engines": {
20
- "node": ">=20"
21
- },
22
27
  "exports": {
23
28
  "./abortable": "./dist/abortable.mjs",
24
29
  "./array-equal": "./dist/array-equal.mjs",
25
30
  "./assertions": "./dist/assertions.mjs",
26
31
  "./canonical-stringify": "./dist/canonical-stringify.mjs",
32
+ "./casts": "./dist/casts.mjs",
27
33
  "./defined": "./dist/defined.mjs",
28
34
  "./hash-content": "./dist/hash-content.mjs",
29
35
  "./json": "./dist/json.mjs",
@@ -32,6 +38,9 @@
32
38
  "./simplify-deep": "./dist/simplify-deep.mjs",
33
39
  "./package.json": "./package.json"
34
40
  },
41
+ "engines": {
42
+ "node": ">=24"
43
+ },
35
44
  "repository": {
36
45
  "type": "git",
37
46
  "url": "https://github.com/prisma/prisma-next.git",
package/src/casts.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * **Last-resort escape hatch for unsafe type assertions. Not a sanctioned tool to reach for.**
3
+ *
4
+ * Before reaching for `blindCast`, **rewrite the surrounding code so the cast becomes
5
+ * unnecessary**: tighten an input type, add a runtime check that narrows via a type
6
+ * predicate, restructure a generic so the compiler can see the relationship you're
7
+ * asserting, or use {@link castAs} when the value already satisfies the target type.
8
+ * Only when no rewrite is feasible does `blindCast` become the right answer — and at
9
+ * that point, the `Reason` literal you supply must articulate the compromise in
10
+ * language a reviewer can evaluate.
11
+ *
12
+ * The reviewer **will** validate the `Reason`. If it doesn't hold up under scrutiny,
13
+ * that is not a signal to soften the reason; it is a signal to go back and solve the
14
+ * underlying type-system problem properly. An unconvincing justification is rework,
15
+ * not a free pass.
16
+ *
17
+ * `blindCast` is the auditable form of `as Foo` / `as unknown as Foo`: it bypasses
18
+ * the compiler's checks (the input type is `unknown`, the output type is whatever the
19
+ * caller asks for), but it forces the unsafety to be named at the call site instead of
20
+ * smuggled in via a bare `as`. The `Reason` type parameter exists only at compile
21
+ * time — it is not present in the emitted JavaScript — but it is grep-able and
22
+ * visible to future readers.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const stringValue = blindCast<
27
+ * string,
28
+ * "JSON.parse returns `unknown`; this field is documented to be a string in the API contract"
29
+ * >(parsed[key]);
30
+ * ```
31
+ *
32
+ * @typeParam TargetType - The type the caller is asserting the input has.
33
+ * @typeParam _Reason - A string literal describing why bypassing the type system is necessary here.
34
+ * Only meaningful at compile time. The reviewer evaluates whether it justifies the unsafety.
35
+ */
36
+ export function blindCast<TargetType, _Reason extends string>(input: unknown): TargetType {
37
+ // biome-ignore lint/suspicious/noExplicitAny: this helper is the single canonical escape hatch for type-unsafe casts in the codebase; the `any` is hyper-local, the unsafety is made explicit at every call site via the call's own `Reason` literal, and the reviewer evaluates whether that justification holds
38
+ const x: any = input;
39
+ return x;
40
+ }
41
+
42
+ /**
43
+ * Type-checked, runtime pass-through alternative to a bare `as Type` cast.
44
+ *
45
+ * Use `castAs` when the value already satisfies the target type but you want to make
46
+ * the type assertion explicit at the call site — for example, when an inferred type is
47
+ * wider than the type you want to publish, or when a literal object should be tagged
48
+ * with its nominal interface. Unlike {@link blindCast}, the compiler still checks that
49
+ * the value is assignable to the target type, so this helper cannot smuggle in an
50
+ * unsafe assertion.
51
+ *
52
+ * `castAs` exists alongside `blindCast` so authors pick the right name at the call
53
+ * site: a `castAs` is type-checked and benign; a `blindCast` is the unsafe escape
54
+ * hatch. The split makes review faster — readers know which casts to scrutinize and
55
+ * which are pure annotations.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * interface FancyObject {
60
+ * key: string;
61
+ * keyTwo: {
62
+ * subKey: string;
63
+ * subKeyTwo: number;
64
+ * };
65
+ * }
66
+ *
67
+ * const typedObject = castAs<FancyObject>({
68
+ * key: 'Chookede',
69
+ * keyTwo: {
70
+ * subKey: 'Choookeeeee',
71
+ * subKeyTwo: 2,
72
+ * },
73
+ * });
74
+ * ```
75
+ *
76
+ * @typeParam Type - The type to constrain and tag the value with. The value must be assignable to `Type`.
77
+ */
78
+ export function castAs<Type>(value: Type): Type {
79
+ return value;
80
+ }
@@ -0,0 +1 @@
1
+ export { blindCast, castAs } from '../casts';
package/src/result.ts CHANGED
@@ -10,6 +10,8 @@
10
10
  * - `failure` property distinguishes from JS Error semantics
11
11
  */
12
12
 
13
+ import { blindCast } from './casts';
14
+
13
15
  /**
14
16
  * Represents a successful result containing a value.
15
17
  */
@@ -76,18 +78,20 @@ class ResultImpl<T, F> {
76
78
  * Creates a successful result.
77
79
  */
78
80
  static ok<T, F = never>(value: T): Ok<T> {
79
- // TypeScript cannot express discriminated return types for a single implementation.
80
- // Cast is safe: ok=true guarantees this is an Ok<T> at runtime.
81
- return new ResultImpl<T, F>(true, value) as unknown as Ok<T>;
81
+ return blindCast<
82
+ Ok<T>,
83
+ 'ResultImpl is the single implementation of the Result discriminated union; TypeScript cannot express discriminated return types for a single class. ok=true guarantees this is an Ok<T> at runtime.'
84
+ >(new ResultImpl<T, F>(true, value));
82
85
  }
83
86
 
84
87
  /**
85
88
  * Creates an unsuccessful result.
86
89
  */
87
90
  static notOk<T = never, F = unknown>(failure: F): NotOk<F> {
88
- // TypeScript cannot express discriminated return types for a single implementation.
89
- // Cast is safe: ok=false guarantees this is a NotOk<F> at runtime.
90
- return new ResultImpl<T, F>(false, failure) as unknown as NotOk<F>;
91
+ return blindCast<
92
+ NotOk<F>,
93
+ 'ResultImpl is the single implementation of the Result discriminated union; TypeScript cannot express discriminated return types for a single class. ok=false guarantees this is a NotOk<F> at runtime.'
94
+ >(new ResultImpl<T, F>(false, failure));
91
95
  }
92
96
 
93
97
  /**