@cipherstash/stack 0.3.0 → 0.4.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 (57) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/dist/bin/stash.js +46 -141
  4. package/dist/bin/stash.js.map +1 -1
  5. package/dist/{chunk-SUYMGQBY.js → chunk-4RNBI3UH.js} +1 -7
  6. package/dist/chunk-4RNBI3UH.js.map +1 -0
  7. package/dist/{chunk-JLI27P46.js → chunk-KOU2MHXJ.js} +1 -1
  8. package/dist/chunk-KOU2MHXJ.js.map +1 -0
  9. package/dist/{chunk-MW6D52V2.js → chunk-WUZGKTMG.js} +45 -134
  10. package/dist/chunk-WUZGKTMG.js.map +1 -0
  11. package/dist/{chunk-TBAIVO6T.js → chunk-Y35W4WFN.js} +6 -6
  12. package/dist/chunk-Y35W4WFN.js.map +1 -0
  13. package/dist/{client-Bf0Xw2xo.d.cts → client-CUGXheRq.d.cts} +15 -16
  14. package/dist/{client-Kfp8OsPB.d.ts → client-CrGQnSlk.d.ts} +15 -16
  15. package/dist/client.cjs +5 -5
  16. package/dist/client.cjs.map +1 -1
  17. package/dist/client.d.cts +2 -2
  18. package/dist/client.d.ts +2 -2
  19. package/dist/client.js +1 -1
  20. package/dist/drizzle/index.cjs +5 -5
  21. package/dist/drizzle/index.cjs.map +1 -1
  22. package/dist/drizzle/index.d.cts +2 -2
  23. package/dist/drizzle/index.d.ts +2 -2
  24. package/dist/drizzle/index.js +2 -2
  25. package/dist/dynamodb/index.d.cts +2 -2
  26. package/dist/dynamodb/index.d.ts +2 -2
  27. package/dist/index.cjs +46 -141
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +3 -3
  30. package/dist/index.d.ts +3 -3
  31. package/dist/index.js +4 -4
  32. package/dist/schema/index.cjs +5 -5
  33. package/dist/schema/index.cjs.map +1 -1
  34. package/dist/schema/index.d.cts +1 -1
  35. package/dist/schema/index.d.ts +1 -1
  36. package/dist/schema/index.js +1 -1
  37. package/dist/secrets/index.cjs +46 -141
  38. package/dist/secrets/index.cjs.map +1 -1
  39. package/dist/secrets/index.d.cts +1 -1
  40. package/dist/secrets/index.d.ts +1 -1
  41. package/dist/secrets/index.js +4 -4
  42. package/dist/supabase/index.cjs +2 -2
  43. package/dist/supabase/index.cjs.map +1 -1
  44. package/dist/supabase/index.d.cts +2 -2
  45. package/dist/supabase/index.d.ts +2 -2
  46. package/dist/supabase/index.js +2 -2
  47. package/dist/{types-public-0CzBV45X.d.cts → types-public-Wj6tB_ux.d.cts} +47 -26
  48. package/dist/{types-public-0CzBV45X.d.ts → types-public-Wj6tB_ux.d.ts} +47 -26
  49. package/dist/types-public.cjs.map +1 -1
  50. package/dist/types-public.d.cts +1 -1
  51. package/dist/types-public.d.ts +1 -1
  52. package/dist/types-public.js +1 -1
  53. package/package.json +2 -2
  54. package/dist/chunk-JLI27P46.js.map +0 -1
  55. package/dist/chunk-MW6D52V2.js.map +0 -1
  56. package/dist/chunk-SUYMGQBY.js.map +0 -1
  57. package/dist/chunk-TBAIVO6T.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { E as EncryptionClient } from './client-Bf0Xw2xo.cjs';
2
- import { E as EncryptionClientConfig } from './types-public-0CzBV45X.cjs';
3
- export { d as EncryptedColumn, h as EncryptedField, i as EncryptedFromSchema, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, a as encryptedColumn, b as encryptedField, e as encryptedTable } from './types-public-0CzBV45X.cjs';
1
+ import { E as EncryptionClient } from './client-CUGXheRq.cjs';
2
+ import { E as EncryptionClientConfig } from './types-public-Wj6tB_ux.cjs';
3
+ export { d as EncryptedColumn, h as EncryptedField, i as EncryptedFromSchema, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, a as encryptedColumn, b as encryptedField, e as encryptedTable } from './types-public-Wj6tB_ux.cjs';
4
4
  export { C as ClientInitError, c as CtsTokenError, D as DecryptionOperationError, a as EncryptionError, E as EncryptionErrorTypes, b as EncryptionOperationError, L as LockContextError, S as StackError, g as getErrorMessage } from './index-9-Ya3fDK.cjs';
5
5
  import '@byteslice/result';
6
6
  import '@cipherstash/protect-ffi';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { E as EncryptionClient } from './client-Kfp8OsPB.js';
2
- import { E as EncryptionClientConfig } from './types-public-0CzBV45X.js';
3
- export { d as EncryptedColumn, h as EncryptedField, i as EncryptedFromSchema, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, a as encryptedColumn, b as encryptedField, e as encryptedTable } from './types-public-0CzBV45X.js';
1
+ import { E as EncryptionClient } from './client-CrGQnSlk.js';
2
+ import { E as EncryptionClientConfig } from './types-public-Wj6tB_ux.js';
3
+ export { d as EncryptedColumn, h as EncryptedField, i as EncryptedFromSchema, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, a as encryptedColumn, b as encryptedField, e as encryptedTable } from './types-public-Wj6tB_ux.js';
4
4
  export { C as ClientInitError, c as CtsTokenError, D as DecryptionOperationError, a as EncryptionError, E as EncryptionErrorTypes, b as EncryptionOperationError, L as LockContextError, S as StackError, g as getErrorMessage } from './index-9-Ya3fDK.js';
5
5
  import '@byteslice/result';
6
6
  import '@cipherstash/protect-ffi';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  Encryption
3
- } from "./chunk-MW6D52V2.js";
4
- import "./chunk-SUYMGQBY.js";
5
- import "./chunk-JLI27P46.js";
3
+ } from "./chunk-WUZGKTMG.js";
4
+ import "./chunk-4RNBI3UH.js";
5
+ import "./chunk-KOU2MHXJ.js";
6
6
  import {
7
7
  EncryptionErrorTypes,
8
8
  getErrorMessage
@@ -11,7 +11,7 @@ import {
11
11
  encryptedColumn,
12
12
  encryptedField,
13
13
  encryptedTable
14
- } from "./chunk-TBAIVO6T.js";
14
+ } from "./chunk-Y35W4WFN.js";
15
15
  export {
16
16
  Encryption,
17
17
  EncryptionErrorTypes,
@@ -32,7 +32,7 @@ __export(schema_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(schema_exports);
34
34
  var import_zod = require("zod");
35
- var castAsEnum = import_zod.z.enum(["bigint", "boolean", "date", "number", "string", "json"]).default("string");
35
+ var castAsEnum = import_zod.z.enum(["bigint", "boolean", "date", "number", "string", "json", "text"]).default("text");
36
36
  var tokenFilterSchema = import_zod.z.object({
37
37
  kind: import_zod.z.literal("downcase")
38
38
  });
@@ -80,7 +80,7 @@ var EncryptedField = class {
80
80
  castAsValue;
81
81
  constructor(valueName) {
82
82
  this.valueName = valueName;
83
- this.castAsValue = "string";
83
+ this.castAsValue = "text";
84
84
  }
85
85
  /**
86
86
  * Set or override the plaintext data type for this field.
@@ -89,7 +89,7 @@ var EncryptedField = class {
89
89
  * a different type so the encryption layer knows how to encode the plaintext
90
90
  * before encrypting.
91
91
  *
92
- * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'bigint'`, or `'json'`.
92
+ * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'text'`, `'bigint'`, or `'json'`.
93
93
  * @returns This `EncryptedField` instance for method chaining.
94
94
  *
95
95
  * @example
@@ -105,7 +105,7 @@ var EncryptedField = class {
105
105
  }
106
106
  build() {
107
107
  return {
108
- cast_as: this.castAsValue,
108
+ cast_as: this.castAsValue === "string" ? "text" : this.castAsValue,
109
109
  indexes: {}
110
110
  };
111
111
  }
@@ -259,7 +259,7 @@ var EncryptedColumn = class {
259
259
  }
260
260
  build() {
261
261
  return {
262
- cast_as: this.castAsValue,
262
+ cast_as: this.castAsValue === "string" ? "text" : this.castAsValue,
263
263
  indexes: this.indexesValue
264
264
  };
265
265
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/schema/index.ts"],"sourcesContent":["import type { Encrypted } from '@/types'\nimport { z } from 'zod'\n\n// ------------------------\n// Zod schemas\n// ------------------------\n\n/**\n * Allowed cast types for CipherStash schema fields.\n *\n * **Possible values:**\n * - `\"bigint\"`\n * - `\"boolean\"`\n * - `\"date\"`\n * - `\"number\"`\n * - `\"string\"`\n * - `\"json\"`\n *\n * @remarks\n * This is a Zod enum used at runtime to validate schema definitions.\n * Use {@link CastAs} when typing your own code.\n *\n * @internal\n */\nexport const castAsEnum = z\n .enum(['bigint', 'boolean', 'date', 'number', 'string', 'json'])\n .default('string')\n\nconst tokenFilterSchema = z.object({\n kind: z.literal('downcase'),\n})\n\nconst tokenizerSchema = z\n .union([\n z.object({\n kind: z.literal('standard'),\n }),\n z.object({\n kind: z.literal('ngram'),\n token_length: z.number(),\n }),\n ])\n .default({ kind: 'ngram', token_length: 3 })\n .optional()\n\nconst oreIndexOptsSchema = z.object({})\n\nconst uniqueIndexOptsSchema = z.object({\n token_filters: z.array(tokenFilterSchema).default([]).optional(),\n})\n\nconst matchIndexOptsSchema = z.object({\n tokenizer: tokenizerSchema,\n token_filters: z.array(tokenFilterSchema).default([]).optional(),\n k: z.number().default(6).optional(),\n m: z.number().default(2048).optional(),\n include_original: z.boolean().default(false).optional(),\n})\n\nconst steVecIndexOptsSchema = z.object({\n prefix: z.string(),\n})\n\nconst indexesSchema = z\n .object({\n ore: oreIndexOptsSchema.optional(),\n unique: uniqueIndexOptsSchema.optional(),\n match: matchIndexOptsSchema.optional(),\n ste_vec: steVecIndexOptsSchema.optional(),\n })\n .default({})\n\nconst columnSchema = z\n .object({\n cast_as: castAsEnum,\n indexes: indexesSchema,\n })\n .default({})\n\nconst tableSchema = z.record(columnSchema).default({})\n\nconst tablesSchema = z.record(tableSchema).default({})\n\n/** @internal */\nexport const encryptConfigSchema = z.object({\n v: z.number(),\n tables: tablesSchema,\n})\n\n// ------------------------\n// Type definitions\n// ------------------------\n\n/**\n * Type-safe alias for {@link castAsEnum} used to specify the *unencrypted* data type of a column or value.\n * This is important because once encrypted, all data is stored as binary blobs.\n *\n * @see {@link castAsEnum} for possible values.\n */\nexport type CastAs = z.infer<typeof castAsEnum>\nexport type TokenFilter = z.infer<typeof tokenFilterSchema>\nexport type MatchIndexOpts = z.infer<typeof matchIndexOptsSchema>\nexport type SteVecIndexOpts = z.infer<typeof steVecIndexOptsSchema>\nexport type UniqueIndexOpts = z.infer<typeof uniqueIndexOptsSchema>\nexport type OreIndexOpts = z.infer<typeof oreIndexOptsSchema>\nexport type ColumnSchema = z.infer<typeof columnSchema>\n\n/**\n * Shape of table columns: either top-level {@link EncryptedColumn} or nested\n * objects whose leaves are {@link EncryptedField}. Used with {@link encryptedTable}.\n */\nexport type EncryptedTableColumn = {\n [key: string]:\n | EncryptedColumn\n | {\n [key: string]:\n | EncryptedField\n | {\n [key: string]:\n | EncryptedField\n | {\n [key: string]: EncryptedField\n }\n }\n }\n}\nexport type EncryptConfig = z.infer<typeof encryptConfigSchema>\n\n// ------------------------\n// Interface definitions\n// ------------------------\n\n/**\n * Builder for a nested encrypted field (encrypted but not searchable).\n * Create with {@link encryptedField}. Use inside nested objects in {@link encryptedTable};\n * supports `.dataType()` for plaintext type. No index methods (equality, orderAndRange, etc.).\n */\nexport class EncryptedField {\n private valueName: string\n private castAsValue: CastAs\n\n constructor(valueName: string) {\n this.valueName = valueName\n this.castAsValue = 'string'\n }\n\n /**\n * Set or override the plaintext data type for this field.\n *\n * By default all values are treated as `'string'`. Use this method to specify\n * a different type so the encryption layer knows how to encode the plaintext\n * before encrypting.\n *\n * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'bigint'`, or `'json'`.\n * @returns This `EncryptedField` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedField } from \"@cipherstash/stack/schema\"\n *\n * const age = encryptedField(\"age\").dataType(\"number\")\n * ```\n */\n dataType(castAs: CastAs) {\n this.castAsValue = castAs\n return this\n }\n\n build() {\n return {\n cast_as: this.castAsValue,\n indexes: {},\n }\n }\n\n getName() {\n return this.valueName\n }\n}\n\nexport class EncryptedColumn {\n private columnName: string\n private castAsValue: CastAs\n private indexesValue: {\n ore?: OreIndexOpts\n unique?: UniqueIndexOpts\n match?: Required<MatchIndexOpts>\n ste_vec?: SteVecIndexOpts\n } = {}\n\n constructor(columnName: string) {\n this.columnName = columnName\n this.castAsValue = 'string'\n }\n\n /**\n * Set or override the plaintext data type for this column.\n *\n * By default all columns are treated as `'string'`. Use this method to specify\n * a different type so the encryption layer knows how to encode the plaintext\n * before encrypting.\n *\n * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'bigint'`, or `'json'`.\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const dateOfBirth = encryptedColumn(\"date_of_birth\").dataType(\"date\")\n * ```\n */\n dataType(castAs: CastAs) {\n this.castAsValue = castAs\n return this\n }\n\n /**\n * Enable Order-Revealing Encryption (ORE) indexing on this column.\n *\n * ORE allows sorting, comparison, and range queries on encrypted data.\n * Use with `encryptQuery` and `queryType: 'orderAndRange'`.\n *\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").orderAndRange(),\n * })\n * ```\n */\n orderAndRange() {\n this.indexesValue.ore = {}\n return this\n }\n\n /**\n * Enable an exact-match (unique) index on this column.\n *\n * Allows equality queries on encrypted data. Use with `encryptQuery`\n * and `queryType: 'equality'`.\n *\n * @param tokenFilters - Optional array of token filters (e.g. `[{ kind: 'downcase' }]`).\n * When omitted, no token filters are applied.\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n * ```\n */\n equality(tokenFilters?: TokenFilter[]) {\n this.indexesValue.unique = {\n token_filters: tokenFilters ?? [],\n }\n return this\n }\n\n /**\n * Enable a full-text / fuzzy search (match) index on this column.\n *\n * Uses n-gram tokenization by default for substring and fuzzy matching.\n * Use with `encryptQuery` and `queryType: 'freeTextSearch'`.\n *\n * @param opts - Optional match index configuration. Defaults to 3-character ngram\n * tokenization with a downcase filter, `k=6`, `m=2048`, and `include_original=true`.\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").freeTextSearch(),\n * })\n *\n * // With custom options\n * const posts = encryptedTable(\"posts\", {\n * body: encryptedColumn(\"body\").freeTextSearch({\n * tokenizer: { kind: \"ngram\", token_length: 4 },\n * k: 8,\n * m: 4096,\n * }),\n * })\n * ```\n */\n freeTextSearch(opts?: MatchIndexOpts) {\n // Provide defaults\n this.indexesValue.match = {\n tokenizer: opts?.tokenizer ?? { kind: 'ngram', token_length: 3 },\n token_filters: opts?.token_filters ?? [\n {\n kind: 'downcase',\n },\n ],\n k: opts?.k ?? 6,\n m: opts?.m ?? 2048,\n include_original: opts?.include_original ?? true,\n }\n return this\n }\n\n /**\n * Configure this column for searchable encrypted JSON (STE-Vec).\n *\n * Enables encrypted JSONPath selector queries (e.g. `'$.user.email'`) and\n * containment queries (e.g. `{ role: 'admin' }`). Automatically sets the\n * data type to `'json'`.\n *\n * When used with `encryptQuery`, the query operation is auto-inferred from\n * the plaintext type: strings become selector queries, objects/arrays become\n * containment queries.\n *\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const documents = encryptedTable(\"documents\", {\n * metadata: encryptedColumn(\"metadata\").searchableJson(),\n * })\n * ```\n */\n searchableJson() {\n this.castAsValue = 'json'\n this.indexesValue.ste_vec = { prefix: 'enabled' }\n return this\n }\n\n build() {\n return {\n cast_as: this.castAsValue,\n indexes: this.indexesValue,\n }\n }\n\n getName() {\n return this.columnName\n }\n}\n\ninterface TableDefinition {\n tableName: string\n columns: Record<string, ColumnSchema>\n}\n\nexport class EncryptedTable<T extends EncryptedTableColumn> {\n /** @internal Type-level brand so TypeScript can infer `T` from `EncryptedTable<T>`. */\n declare readonly _columnType: T\n\n constructor(\n public readonly tableName: string,\n private readonly columnBuilders: T,\n ) {}\n\n /**\n * Compile this table schema into a `TableDefinition` used internally by the encryption client.\n *\n * Iterates over all column builders, calls `.build()` on each, and assembles\n * the final `{ tableName, columns }` structure. For `searchableJson()` columns,\n * the STE-Vec prefix is automatically set to `\"<tableName>/<columnName>\"`.\n *\n * @returns A `TableDefinition` containing the table name and built column configs.\n *\n * @example\n * ```typescript\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const definition = users.build()\n * // { tableName: \"users\", columns: { email: { cast_as: \"string\", indexes: { unique: ... } } } }\n * ```\n */\n build(): TableDefinition {\n const builtColumns: Record<string, ColumnSchema> = {}\n\n const processColumn = (\n builder:\n | EncryptedColumn\n | Record<\n string,\n | EncryptedField\n | Record<\n string,\n | EncryptedField\n | Record<\n string,\n EncryptedField | Record<string, EncryptedField>\n >\n >\n >,\n colName: string,\n ) => {\n if (builder instanceof EncryptedColumn) {\n const builtColumn = builder.build()\n\n // Hanlde building the ste_vec index for JSON columns so users don't have to pass the prefix.\n if (\n builtColumn.cast_as === 'json' &&\n builtColumn.indexes.ste_vec?.prefix === 'enabled'\n ) {\n builtColumns[colName] = {\n ...builtColumn,\n indexes: {\n ...builtColumn.indexes,\n ste_vec: {\n prefix: `${this.tableName}/${colName}`,\n },\n },\n }\n } else {\n builtColumns[colName] = builtColumn\n }\n } else {\n for (const [key, value] of Object.entries(builder)) {\n if (value instanceof EncryptedField) {\n builtColumns[value.getName()] = value.build()\n } else {\n processColumn(value, key)\n }\n }\n }\n }\n\n for (const [colName, builder] of Object.entries(this.columnBuilders)) {\n processColumn(builder, colName)\n }\n\n return {\n tableName: this.tableName,\n columns: builtColumns,\n }\n }\n}\n\n// ------------------------\n// Schema type inference helpers\n// ------------------------\n\n/**\n * Infer the plaintext (decrypted) type from a EncryptedTable schema.\n *\n * @example\n * ```typescript\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * name: encryptedColumn(\"name\"),\n * })\n *\n * type UserPlaintext = InferPlaintext<typeof users>\n * // => { email: string; name: string }\n * ```\n */\nexport type InferPlaintext<T extends EncryptedTable<any>> =\n T extends EncryptedTable<infer C>\n ? {\n [K in keyof C as C[K] extends EncryptedColumn | EncryptedField\n ? K\n : never]: string\n }\n : never\n\n/**\n * Infer the encrypted type from a EncryptedTable schema.\n *\n * @example\n * ```typescript\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * type UserEncrypted = InferEncrypted<typeof users>\n * // => { email: Encrypted }\n * ```\n */\nexport type InferEncrypted<T extends EncryptedTable<any>> =\n T extends EncryptedTable<infer C>\n ? {\n [K in keyof C as C[K] extends EncryptedColumn | EncryptedField\n ? K\n : never]: Encrypted\n }\n : never\n\n// ------------------------\n// User facing functions\n// ------------------------\n\n/**\n * Define an encrypted table schema.\n *\n * Creates a `EncryptedTable` that maps a database table name to a set of encrypted\n * column definitions. Pass the resulting object to `Encryption({ schemas: [...] })`\n * when initializing the client.\n *\n * The returned object is also a proxy that exposes each column builder directly,\n * so you can reference columns as `users.email` when calling `encrypt`, `decrypt`,\n * and `encryptQuery`.\n *\n * @param tableName - The name of the database table this schema represents.\n * @param columns - An object whose keys are logical column names and values are\n * {@link EncryptedColumn} from {@link encryptedColumn}, or nested objects whose\n * leaves are {@link EncryptedField} from {@link encryptedField}.\n * @returns A `EncryptedTable<T> & T` that can be used as both a schema definition\n * and a column accessor.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality().freeTextSearch(),\n * address: encryptedColumn(\"address\"),\n * })\n *\n * // Use as schema\n * const client = await Encryption({ schemas: [users] })\n *\n * // Use as column accessor\n * await client.encrypt(\"hello@example.com\", { column: users.email, table: users })\n * ```\n */\nexport function encryptedTable<T extends EncryptedTableColumn>(\n tableName: string,\n columns: T,\n): EncryptedTable<T> & T {\n const tableBuilder = new EncryptedTable(\n tableName,\n columns,\n ) as EncryptedTable<T> & T\n\n for (const [colName, colBuilder] of Object.entries(columns)) {\n ;(tableBuilder as EncryptedTableColumn)[colName] = colBuilder\n }\n\n return tableBuilder\n}\n\n/**\n * Define an encrypted column within a table schema.\n *\n * Creates a `EncryptedColumn` builder for the given column name. Chain index\n * methods (`.equality()`, `.freeTextSearch()`, `.orderAndRange()`,\n * `.searchableJson()`) and/or `.dataType()` to configure searchable encryption\n * and the plaintext data type.\n *\n * @param columnName - The name of the database column to encrypt.\n * @returns A new `EncryptedColumn` builder.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality().freeTextSearch().orderAndRange(),\n * })\n * ```\n */\nexport function encryptedColumn(columnName: string) {\n return new EncryptedColumn(columnName)\n}\n\n/**\n * Define an encrypted field for use in nested or structured schemas.\n *\n * `encryptedField` is similar to {@link encryptedColumn} but creates an {@link EncryptedField}\n * for nested fields that are encrypted but not searchable (no indexes). Use `.dataType()`\n * to specify the plaintext type.\n *\n * @param valueName - The name of the value field.\n * @returns A new `EncryptedField` builder.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedField } from \"@cipherstash/stack/schema\"\n *\n * const orders = encryptedTable(\"orders\", {\n * details: {\n * amount: encryptedField(\"amount\").dataType(\"number\"),\n * currency: encryptedField(\"currency\"),\n * },\n * })\n * ```\n */\nexport function encryptedField(valueName: string) {\n return new EncryptedField(valueName)\n}\n\n// ------------------------\n// Internal functions\n// ------------------------\n\n/** @internal */\nexport function buildEncryptConfig(\n ...protectTables: Array<EncryptedTable<EncryptedTableColumn>>\n): EncryptConfig {\n const config: EncryptConfig = {\n v: 2,\n tables: {},\n }\n\n for (const tb of protectTables) {\n const tableDef = tb.build()\n config.tables[tableDef.tableName] = tableDef.columns\n }\n\n return config\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/schema/index.ts"],"sourcesContent":["import type { Encrypted } from '@/types'\nimport { z } from 'zod'\n\n// ------------------------\n// Zod schemas\n// ------------------------\n\n/**\n * Allowed cast types for CipherStash schema fields.\n *\n * **Possible values:**\n * - `\"bigint\"`\n * - `\"boolean\"`\n * - `\"date\"`\n * - `\"number\"`\n * - `\"string\"`\n * - `\"json\"`\n * - `\"text\"`\n *\n * @remarks\n * This is a Zod enum used at runtime to validate schema definitions.\n * Use {@link CastAs} when typing your own code.\n *\n * @internal\n */\nexport const castAsEnum = z\n .enum(['bigint', 'boolean', 'date', 'number', 'string', 'json', 'text'])\n .default('text')\n\nconst tokenFilterSchema = z.object({\n kind: z.literal('downcase'),\n})\n\nconst tokenizerSchema = z\n .union([\n z.object({\n kind: z.literal('standard'),\n }),\n z.object({\n kind: z.literal('ngram'),\n token_length: z.number(),\n }),\n ])\n .default({ kind: 'ngram', token_length: 3 })\n .optional()\n\nconst oreIndexOptsSchema = z.object({})\n\nconst uniqueIndexOptsSchema = z.object({\n token_filters: z.array(tokenFilterSchema).default([]).optional(),\n})\n\nconst matchIndexOptsSchema = z.object({\n tokenizer: tokenizerSchema,\n token_filters: z.array(tokenFilterSchema).default([]).optional(),\n k: z.number().default(6).optional(),\n m: z.number().default(2048).optional(),\n include_original: z.boolean().default(false).optional(),\n})\n\nconst steVecIndexOptsSchema = z.object({\n prefix: z.string(),\n})\n\nconst indexesSchema = z\n .object({\n ore: oreIndexOptsSchema.optional(),\n unique: uniqueIndexOptsSchema.optional(),\n match: matchIndexOptsSchema.optional(),\n ste_vec: steVecIndexOptsSchema.optional(),\n })\n .default({})\n\nconst columnSchema = z\n .object({\n cast_as: castAsEnum,\n indexes: indexesSchema,\n })\n .default({})\n\nconst tableSchema = z.record(columnSchema).default({})\n\nconst tablesSchema = z.record(tableSchema).default({})\n\n/** @internal */\nexport const encryptConfigSchema = z.object({\n v: z.number(),\n tables: tablesSchema,\n})\n\n// ------------------------\n// Type definitions\n// ------------------------\n\n/**\n * Type-safe alias for {@link castAsEnum} used to specify the *unencrypted* data type of a column or value.\n * This is important because once encrypted, all data is stored as binary blobs.\n *\n * @see {@link castAsEnum} for possible values.\n */\nexport type CastAs = z.infer<typeof castAsEnum>\nexport type TokenFilter = z.infer<typeof tokenFilterSchema>\nexport type MatchIndexOpts = z.infer<typeof matchIndexOptsSchema>\nexport type SteVecIndexOpts = z.infer<typeof steVecIndexOptsSchema>\nexport type UniqueIndexOpts = z.infer<typeof uniqueIndexOptsSchema>\nexport type OreIndexOpts = z.infer<typeof oreIndexOptsSchema>\nexport type ColumnSchema = z.infer<typeof columnSchema>\n\n/**\n * Shape of table columns: either top-level {@link EncryptedColumn} or nested\n * objects whose leaves are {@link EncryptedField}. Used with {@link encryptedTable}.\n */\nexport type EncryptedTableColumn = {\n [key: string]:\n | EncryptedColumn\n | {\n [key: string]:\n | EncryptedField\n | {\n [key: string]:\n | EncryptedField\n | {\n [key: string]: EncryptedField\n }\n }\n }\n}\nexport type EncryptConfig = z.infer<typeof encryptConfigSchema>\n\n// ------------------------\n// Interface definitions\n// ------------------------\n\n/**\n * Builder for a nested encrypted field (encrypted but not searchable).\n * Create with {@link encryptedField}. Use inside nested objects in {@link encryptedTable};\n * supports `.dataType()` for plaintext type. No index methods (equality, orderAndRange, etc.).\n */\nexport class EncryptedField {\n private valueName: string\n private castAsValue: CastAs\n\n constructor(valueName: string) {\n this.valueName = valueName\n this.castAsValue = 'text'\n }\n\n /**\n * Set or override the plaintext data type for this field.\n *\n * By default all values are treated as `'string'`. Use this method to specify\n * a different type so the encryption layer knows how to encode the plaintext\n * before encrypting.\n *\n * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'text'`, `'bigint'`, or `'json'`.\n * @returns This `EncryptedField` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedField } from \"@cipherstash/stack/schema\"\n *\n * const age = encryptedField(\"age\").dataType(\"number\")\n * ```\n */\n dataType(castAs: CastAs) {\n this.castAsValue = castAs\n return this\n }\n\n build() {\n return {\n cast_as: this.castAsValue === 'string' ? 'text' : this.castAsValue,\n indexes: {},\n }\n }\n\n getName() {\n return this.valueName\n }\n}\n\nexport class EncryptedColumn {\n private columnName: string\n private castAsValue: CastAs\n private indexesValue: {\n ore?: OreIndexOpts\n unique?: UniqueIndexOpts\n match?: Required<MatchIndexOpts>\n ste_vec?: SteVecIndexOpts\n } = {}\n\n constructor(columnName: string) {\n this.columnName = columnName\n this.castAsValue = 'string'\n }\n\n /**\n * Set or override the plaintext data type for this column.\n *\n * By default all columns are treated as `'string'`. Use this method to specify\n * a different type so the encryption layer knows how to encode the plaintext\n * before encrypting.\n *\n * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'bigint'`, or `'json'`.\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const dateOfBirth = encryptedColumn(\"date_of_birth\").dataType(\"date\")\n * ```\n */\n dataType(castAs: CastAs) {\n this.castAsValue = castAs\n return this\n }\n\n /**\n * Enable Order-Revealing Encryption (ORE) indexing on this column.\n *\n * ORE allows sorting, comparison, and range queries on encrypted data.\n * Use with `encryptQuery` and `queryType: 'orderAndRange'`.\n *\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").orderAndRange(),\n * })\n * ```\n */\n orderAndRange() {\n this.indexesValue.ore = {}\n return this\n }\n\n /**\n * Enable an exact-match (unique) index on this column.\n *\n * Allows equality queries on encrypted data. Use with `encryptQuery`\n * and `queryType: 'equality'`.\n *\n * @param tokenFilters - Optional array of token filters (e.g. `[{ kind: 'downcase' }]`).\n * When omitted, no token filters are applied.\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n * ```\n */\n equality(tokenFilters?: TokenFilter[]) {\n this.indexesValue.unique = {\n token_filters: tokenFilters ?? [],\n }\n return this\n }\n\n /**\n * Enable a full-text / fuzzy search (match) index on this column.\n *\n * Uses n-gram tokenization by default for substring and fuzzy matching.\n * Use with `encryptQuery` and `queryType: 'freeTextSearch'`.\n *\n * @param opts - Optional match index configuration. Defaults to 3-character ngram\n * tokenization with a downcase filter, `k=6`, `m=2048`, and `include_original=true`.\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").freeTextSearch(),\n * })\n *\n * // With custom options\n * const posts = encryptedTable(\"posts\", {\n * body: encryptedColumn(\"body\").freeTextSearch({\n * tokenizer: { kind: \"ngram\", token_length: 4 },\n * k: 8,\n * m: 4096,\n * }),\n * })\n * ```\n */\n freeTextSearch(opts?: MatchIndexOpts) {\n // Provide defaults\n this.indexesValue.match = {\n tokenizer: opts?.tokenizer ?? { kind: 'ngram', token_length: 3 },\n token_filters: opts?.token_filters ?? [\n {\n kind: 'downcase',\n },\n ],\n k: opts?.k ?? 6,\n m: opts?.m ?? 2048,\n include_original: opts?.include_original ?? true,\n }\n return this\n }\n\n /**\n * Configure this column for searchable encrypted JSON (STE-Vec).\n *\n * Enables encrypted JSONPath selector queries (e.g. `'$.user.email'`) and\n * containment queries (e.g. `{ role: 'admin' }`). Automatically sets the\n * data type to `'json'`.\n *\n * When used with `encryptQuery`, the query operation is auto-inferred from\n * the plaintext type: strings become selector queries, objects/arrays become\n * containment queries.\n *\n * @returns This `EncryptedColumn` instance for method chaining.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const documents = encryptedTable(\"documents\", {\n * metadata: encryptedColumn(\"metadata\").searchableJson(),\n * })\n * ```\n */\n searchableJson() {\n this.castAsValue = 'json'\n this.indexesValue.ste_vec = { prefix: 'enabled' }\n return this\n }\n\n build() {\n return {\n cast_as: this.castAsValue === 'string' ? 'text' : this.castAsValue,\n indexes: this.indexesValue,\n }\n }\n\n getName() {\n return this.columnName\n }\n}\n\ninterface TableDefinition {\n tableName: string\n columns: Record<string, ColumnSchema>\n}\n\nexport class EncryptedTable<T extends EncryptedTableColumn> {\n /** @internal Type-level brand so TypeScript can infer `T` from `EncryptedTable<T>`. */\n declare readonly _columnType: T\n\n constructor(\n public readonly tableName: string,\n private readonly columnBuilders: T,\n ) {}\n\n /**\n * Compile this table schema into a `TableDefinition` used internally by the encryption client.\n *\n * Iterates over all column builders, calls `.build()` on each, and assembles\n * the final `{ tableName, columns }` structure. For `searchableJson()` columns,\n * the STE-Vec prefix is automatically set to `\"<tableName>/<columnName>\"`.\n *\n * @returns A `TableDefinition` containing the table name and built column configs.\n *\n * @example\n * ```typescript\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const definition = users.build()\n * // { tableName: \"users\", columns: { email: { cast_as: \"string\", indexes: { unique: ... } } } }\n * ```\n */\n build(): TableDefinition {\n const builtColumns: Record<string, ColumnSchema> = {}\n\n const processColumn = (\n builder:\n | EncryptedColumn\n | Record<\n string,\n | EncryptedField\n | Record<\n string,\n | EncryptedField\n | Record<\n string,\n EncryptedField | Record<string, EncryptedField>\n >\n >\n >,\n colName: string,\n ) => {\n if (builder instanceof EncryptedColumn) {\n const builtColumn = builder.build()\n\n // Hanlde building the ste_vec index for JSON columns so users don't have to pass the prefix.\n if (\n builtColumn.cast_as === 'json' &&\n builtColumn.indexes.ste_vec?.prefix === 'enabled'\n ) {\n builtColumns[colName] = {\n ...builtColumn,\n indexes: {\n ...builtColumn.indexes,\n ste_vec: {\n prefix: `${this.tableName}/${colName}`,\n },\n },\n }\n } else {\n builtColumns[colName] = builtColumn\n }\n } else {\n for (const [key, value] of Object.entries(builder)) {\n if (value instanceof EncryptedField) {\n builtColumns[value.getName()] = value.build()\n } else {\n processColumn(value, key)\n }\n }\n }\n }\n\n for (const [colName, builder] of Object.entries(this.columnBuilders)) {\n processColumn(builder, colName)\n }\n\n return {\n tableName: this.tableName,\n columns: builtColumns,\n }\n }\n}\n\n// ------------------------\n// Schema type inference helpers\n// ------------------------\n\n/**\n * Infer the plaintext (decrypted) type from a EncryptedTable schema.\n *\n * @example\n * ```typescript\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * name: encryptedColumn(\"name\"),\n * })\n *\n * type UserPlaintext = InferPlaintext<typeof users>\n * // => { email: string; name: string }\n * ```\n */\nexport type InferPlaintext<T extends EncryptedTable<any>> =\n T extends EncryptedTable<infer C>\n ? {\n [K in keyof C as C[K] extends EncryptedColumn | EncryptedField\n ? K\n : never]: string\n }\n : never\n\n/**\n * Infer the encrypted type from a EncryptedTable schema.\n *\n * @example\n * ```typescript\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * type UserEncrypted = InferEncrypted<typeof users>\n * // => { email: Encrypted }\n * ```\n */\nexport type InferEncrypted<T extends EncryptedTable<any>> =\n T extends EncryptedTable<infer C>\n ? {\n [K in keyof C as C[K] extends EncryptedColumn | EncryptedField\n ? K\n : never]: Encrypted\n }\n : never\n\n// ------------------------\n// User facing functions\n// ------------------------\n\n/**\n * Define an encrypted table schema.\n *\n * Creates a `EncryptedTable` that maps a database table name to a set of encrypted\n * column definitions. Pass the resulting object to `Encryption({ schemas: [...] })`\n * when initializing the client.\n *\n * The returned object is also a proxy that exposes each column builder directly,\n * so you can reference columns as `users.email` when calling `encrypt`, `decrypt`,\n * and `encryptQuery`.\n *\n * @param tableName - The name of the database table this schema represents.\n * @param columns - An object whose keys are logical column names and values are\n * {@link EncryptedColumn} from {@link encryptedColumn}, or nested objects whose\n * leaves are {@link EncryptedField} from {@link encryptedField}.\n * @returns A `EncryptedTable<T> & T` that can be used as both a schema definition\n * and a column accessor.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality().freeTextSearch(),\n * address: encryptedColumn(\"address\"),\n * })\n *\n * // Use as schema\n * const client = await Encryption({ schemas: [users] })\n *\n * // Use as column accessor\n * await client.encrypt(\"hello@example.com\", { column: users.email, table: users })\n * ```\n */\nexport function encryptedTable<T extends EncryptedTableColumn>(\n tableName: string,\n columns: T,\n): EncryptedTable<T> & T {\n const tableBuilder = new EncryptedTable(\n tableName,\n columns,\n ) as EncryptedTable<T> & T\n\n for (const [colName, colBuilder] of Object.entries(columns)) {\n ;(tableBuilder as EncryptedTableColumn)[colName] = colBuilder\n }\n\n return tableBuilder\n}\n\n/**\n * Define an encrypted column within a table schema.\n *\n * Creates a `EncryptedColumn` builder for the given column name. Chain index\n * methods (`.equality()`, `.freeTextSearch()`, `.orderAndRange()`,\n * `.searchableJson()`) and/or `.dataType()` to configure searchable encryption\n * and the plaintext data type.\n *\n * @param columnName - The name of the database column to encrypt.\n * @returns A new `EncryptedColumn` builder.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality().freeTextSearch().orderAndRange(),\n * })\n * ```\n */\nexport function encryptedColumn(columnName: string) {\n return new EncryptedColumn(columnName)\n}\n\n/**\n * Define an encrypted field for use in nested or structured schemas.\n *\n * `encryptedField` is similar to {@link encryptedColumn} but creates an {@link EncryptedField}\n * for nested fields that are encrypted but not searchable (no indexes). Use `.dataType()`\n * to specify the plaintext type.\n *\n * @param valueName - The name of the value field.\n * @returns A new `EncryptedField` builder.\n *\n * @example\n * ```typescript\n * import { encryptedTable, encryptedField } from \"@cipherstash/stack/schema\"\n *\n * const orders = encryptedTable(\"orders\", {\n * details: {\n * amount: encryptedField(\"amount\").dataType(\"number\"),\n * currency: encryptedField(\"currency\"),\n * },\n * })\n * ```\n */\nexport function encryptedField(valueName: string) {\n return new EncryptedField(valueName)\n}\n\n/**\n * Build an encrypt config from a list of encrypted tables.\n *\n * @param ...tables - The list of encrypted tables to build the config from.\n * @returns An encrypt config object.\n *\n * @example\n * ```typescript\n * import { buildEncryptConfig } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const orders = encryptedTable(\"orders\", {\n * amount: encryptedColumn(\"amount\").dataType(\"number\"),\n * })\n *\n * const config = buildEncryptConfig(users, orders)\n * console.log(config)\n * ```\n */\nexport function buildEncryptConfig(\n ...protectTables: Array<EncryptedTable<EncryptedTableColumn>>\n): EncryptConfig {\n const config: EncryptConfig = {\n v: 2,\n tables: {},\n }\n\n for (const tb of protectTables) {\n const tableDef = tb.build()\n config.tables[tableDef.tableName] = tableDef.columns\n }\n\n return config\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,4 @@
1
- export { C as CastAs, x as ColumnSchema, t as EncryptConfig, d as EncryptedColumn, h as EncryptedField, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, y as buildEncryptConfig, u as castAsEnum, v as encryptConfigSchema, a as encryptedColumn, b as encryptedField, e as encryptedTable } from '../types-public-0CzBV45X.cjs';
1
+ export { C as CastAs, x as ColumnSchema, t as EncryptConfig, d as EncryptedColumn, h as EncryptedField, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, y as buildEncryptConfig, u as castAsEnum, v as encryptConfigSchema, a as encryptedColumn, b as encryptedField, e as encryptedTable } from '../types-public-Wj6tB_ux.cjs';
2
2
  import 'zod';
3
3
  import 'evlog';
4
4
  import '@cipherstash/protect-ffi';
@@ -1,4 +1,4 @@
1
- export { C as CastAs, x as ColumnSchema, t as EncryptConfig, d as EncryptedColumn, h as EncryptedField, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, y as buildEncryptConfig, u as castAsEnum, v as encryptConfigSchema, a as encryptedColumn, b as encryptedField, e as encryptedTable } from '../types-public-0CzBV45X.js';
1
+ export { C as CastAs, x as ColumnSchema, t as EncryptConfig, d as EncryptedColumn, h as EncryptedField, f as EncryptedTable, g as EncryptedTableColumn, c as InferEncrypted, I as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, y as buildEncryptConfig, u as castAsEnum, v as encryptConfigSchema, a as encryptedColumn, b as encryptedField, e as encryptedTable } from '../types-public-Wj6tB_ux.js';
2
2
  import 'zod';
3
3
  import 'evlog';
4
4
  import '@cipherstash/protect-ffi';
@@ -8,7 +8,7 @@ import {
8
8
  encryptedColumn,
9
9
  encryptedField,
10
10
  encryptedTable
11
- } from "../chunk-TBAIVO6T.js";
11
+ } from "../chunk-Y35W4WFN.js";
12
12
  export {
13
13
  EncryptedColumn,
14
14
  EncryptedField,
@@ -41,15 +41,9 @@ function encryptedToPgComposite(obj) {
41
41
  };
42
42
  }
43
43
  function encryptedToCompositeLiteral(obj) {
44
- if (obj === null) {
45
- throw new Error("encryptedToCompositeLiteral: obj cannot be null");
46
- }
47
44
  return `(${JSON.stringify(JSON.stringify(obj))})`;
48
45
  }
49
46
  function encryptedToEscapedCompositeLiteral(obj) {
50
- if (obj === null) {
51
- throw new Error("encryptedToEscapedCompositeLiteral: obj cannot be null");
52
- }
53
47
  return JSON.stringify(encryptedToCompositeLiteral(obj));
54
48
  }
55
49
  function formatEncryptedResult(encrypted, returnType) {
@@ -89,7 +83,7 @@ var EncryptionErrorTypes = {
89
83
 
90
84
  // src/schema/index.ts
91
85
  var import_zod = require("zod");
92
- var castAsEnum = import_zod.z.enum(["bigint", "boolean", "date", "number", "string", "json"]).default("string");
86
+ var castAsEnum = import_zod.z.enum(["bigint", "boolean", "date", "number", "string", "json", "text"]).default("text");
93
87
  var tokenFilterSchema = import_zod.z.object({
94
88
  kind: import_zod.z.literal("downcase")
95
89
  });
@@ -137,7 +131,7 @@ var EncryptedField = class {
137
131
  castAsValue;
138
132
  constructor(valueName) {
139
133
  this.valueName = valueName;
140
- this.castAsValue = "string";
134
+ this.castAsValue = "text";
141
135
  }
142
136
  /**
143
137
  * Set or override the plaintext data type for this field.
@@ -146,7 +140,7 @@ var EncryptedField = class {
146
140
  * a different type so the encryption layer knows how to encode the plaintext
147
141
  * before encrypting.
148
142
  *
149
- * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'bigint'`, or `'json'`.
143
+ * @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'text'`, `'bigint'`, or `'json'`.
150
144
  * @returns This `EncryptedField` instance for method chaining.
151
145
  *
152
146
  * @example
@@ -162,7 +156,7 @@ var EncryptedField = class {
162
156
  }
163
157
  build() {
164
158
  return {
165
- cast_as: this.castAsValue,
159
+ cast_as: this.castAsValue === "string" ? "text" : this.castAsValue,
166
160
  indexes: {}
167
161
  };
168
162
  }
@@ -316,7 +310,7 @@ var EncryptedColumn = class {
316
310
  }
317
311
  build() {
318
312
  return {
319
- cast_as: this.castAsValue,
313
+ cast_as: this.castAsValue === "string" ? "text" : this.castAsValue,
320
314
  indexes: this.indexesValue
321
315
  };
322
316
  }
@@ -687,18 +681,6 @@ var EncryptionOperation = class {
687
681
  };
688
682
 
689
683
  // src/encryption/operations/batch-encrypt-query.ts
690
- function filterNullTerms(terms) {
691
- const nullIndices = /* @__PURE__ */ new Set();
692
- const nonNullTerms = [];
693
- terms.forEach((term, index) => {
694
- if (term.value === null || term.value === void 0) {
695
- nullIndices.add(index);
696
- } else {
697
- nonNullTerms.push({ term, originalIndex: index });
698
- }
699
- });
700
- return { nullIndices, nonNullTerms };
701
- }
702
684
  function buildQueryPayload(term, lockContext) {
703
685
  assertValidNumericValue(term.value);
704
686
  const { indexType, queryOp } = resolveIndexType(
@@ -719,13 +701,10 @@ function buildQueryPayload(term, lockContext) {
719
701
  }
720
702
  return payload;
721
703
  }
722
- function assembleResults(totalLength, encryptedValues, nonNullTerms) {
723
- const results = new Array(totalLength).fill(null);
724
- nonNullTerms.forEach(({ term, originalIndex }, i) => {
725
- const encrypted = encryptedValues[i];
726
- results[originalIndex] = formatEncryptedResult(encrypted, term.returnType);
727
- });
728
- return results;
704
+ function assembleResults(terms, encryptedValues) {
705
+ return terms.map(
706
+ (term, i) => formatEncryptedResult(encryptedValues[i], term.returnType)
707
+ );
729
708
  }
730
709
  var BatchEncryptQueryOperation = class extends EncryptionOperation {
731
710
  constructor(client, terms) {
@@ -752,23 +731,18 @@ var BatchEncryptQueryOperation = class extends EncryptionOperation {
752
731
  log.emit();
753
732
  return { data: [] };
754
733
  }
755
- const { nullIndices, nonNullTerms } = filterNullTerms(this.terms);
756
- if (nonNullTerms.length === 0) {
757
- log.emit();
758
- return { data: this.terms.map(() => null) };
759
- }
760
734
  const result = await (0, import_result.withResult)(
761
735
  async () => {
762
736
  if (!this.client) throw noClientError();
763
737
  const { metadata } = this.getAuditData();
764
- const queries = nonNullTerms.map(
765
- ({ term }) => buildQueryPayload(term)
738
+ const queries = this.terms.map(
739
+ (term) => buildQueryPayload(term)
766
740
  );
767
741
  const encrypted = await (0, import_protect_ffi2.encryptQueryBulk)(this.client, {
768
742
  queries,
769
743
  unverifiedContext: metadata
770
744
  });
771
- return assembleResults(this.terms.length, encrypted, nonNullTerms);
745
+ return assembleResults(this.terms, encrypted);
772
746
  },
773
747
  (error) => {
774
748
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -802,11 +776,6 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
802
776
  log.emit();
803
777
  return { data: [] };
804
778
  }
805
- const { nullIndices, nonNullTerms } = filterNullTerms(this.terms);
806
- if (nonNullTerms.length === 0) {
807
- log.emit();
808
- return { data: this.terms.map(() => null) };
809
- }
810
779
  const lockContextResult = await this.lockContext.getLockContext();
811
780
  if (lockContextResult.failure) {
812
781
  log.emit();
@@ -817,15 +786,15 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
817
786
  async () => {
818
787
  if (!this.client) throw noClientError();
819
788
  const { metadata } = this.getAuditData();
820
- const queries = nonNullTerms.map(
821
- ({ term }) => buildQueryPayload(term, context)
789
+ const queries = this.terms.map(
790
+ (term) => buildQueryPayload(term, context)
822
791
  );
823
792
  const encrypted = await (0, import_protect_ffi2.encryptQueryBulk)(this.client, {
824
793
  queries,
825
794
  serviceToken: ctsToken,
826
795
  unverifiedContext: metadata
827
796
  });
828
- return assembleResults(this.terms.length, encrypted, nonNullTerms);
797
+ return assembleResults(this.terms, encrypted);
829
798
  },
830
799
  (error) => {
831
800
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -845,42 +814,25 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
845
814
  var import_result2 = require("@byteslice/result");
846
815
  var import_protect_ffi3 = require("@cipherstash/protect-ffi");
847
816
  var createDecryptPayloads = (encryptedPayloads, lockContext) => {
848
- return encryptedPayloads.map((item, index) => ({ ...item, originalIndex: index })).filter(({ data }) => data !== null).map(({ id, data, originalIndex }) => ({
817
+ return encryptedPayloads.map(({ id, data }) => ({
849
818
  id,
850
819
  ciphertext: data,
851
- originalIndex,
852
820
  ...lockContext && { lockContext }
853
821
  }));
854
822
  };
855
- var createNullResult = (encryptedPayloads) => {
856
- return encryptedPayloads.map(({ id }) => ({
857
- id,
858
- data: null
859
- }));
860
- };
861
823
  var mapDecryptedDataToResult = (encryptedPayloads, decryptedData) => {
862
- const result = new Array(encryptedPayloads.length);
863
- let decryptedIndex = 0;
864
- for (let i = 0; i < encryptedPayloads.length; i++) {
865
- if (encryptedPayloads[i].data === null) {
866
- result[i] = { id: encryptedPayloads[i].id, data: null };
867
- } else {
868
- const decryptResult = decryptedData[decryptedIndex];
869
- if ("error" in decryptResult) {
870
- result[i] = {
871
- id: encryptedPayloads[i].id,
872
- error: decryptResult.error
873
- };
874
- } else {
875
- result[i] = {
876
- id: encryptedPayloads[i].id,
877
- data: decryptResult.data
878
- };
879
- }
880
- decryptedIndex++;
824
+ return decryptedData.map((decryptResult, i) => {
825
+ if ("error" in decryptResult) {
826
+ return {
827
+ id: encryptedPayloads[i].id,
828
+ error: decryptResult.error
829
+ };
881
830
  }
882
- }
883
- return result;
831
+ return {
832
+ id: encryptedPayloads[i].id,
833
+ data: decryptResult.data
834
+ };
835
+ });
884
836
  };
885
837
  var BulkDecryptOperation = class extends EncryptionOperation {
886
838
  client;
@@ -905,13 +857,10 @@ var BulkDecryptOperation = class extends EncryptionOperation {
905
857
  if (!this.client) throw noClientError();
906
858
  if (!this.encryptedPayloads || this.encryptedPayloads.length === 0)
907
859
  return [];
908
- const nonNullPayloads = createDecryptPayloads(this.encryptedPayloads);
909
- if (nonNullPayloads.length === 0) {
910
- return createNullResult(this.encryptedPayloads);
911
- }
860
+ const payloads = createDecryptPayloads(this.encryptedPayloads);
912
861
  const { metadata } = this.getAuditData();
913
862
  const decryptedData = await (0, import_protect_ffi3.decryptBulkFallible)(this.client, {
914
- ciphertexts: nonNullPayloads,
863
+ ciphertexts: payloads,
915
864
  unverifiedContext: metadata
916
865
  });
917
866
  return mapDecryptedDataToResult(this.encryptedPayloads, decryptedData);
@@ -963,16 +912,13 @@ var BulkDecryptOperationWithLockContext = class extends EncryptionOperation {
963
912
  if (context.failure) {
964
913
  throw new Error(`[encryption]: ${context.failure.message}`);
965
914
  }
966
- const nonNullPayloads = createDecryptPayloads(
915
+ const payloads = createDecryptPayloads(
967
916
  encryptedPayloads,
968
917
  context.data.context
969
918
  );
970
- if (nonNullPayloads.length === 0) {
971
- return createNullResult(encryptedPayloads);
972
- }
973
919
  const { metadata } = this.getAuditData();
974
920
  const decryptedData = await (0, import_protect_ffi3.decryptBulkFallible)(client, {
975
- ciphertexts: nonNullPayloads,
921
+ ciphertexts: payloads,
976
922
  serviceToken: context.data.ctsToken,
977
923
  unverifiedContext: metadata
978
924
  });
@@ -1611,34 +1557,14 @@ var BulkDecryptModelsOperationWithLockContext = class extends EncryptionOperatio
1611
1557
  var import_result4 = require("@byteslice/result");
1612
1558
  var import_protect_ffi5 = require("@cipherstash/protect-ffi");
1613
1559
  var createEncryptPayloads = (plaintexts, column, table, lockContext) => {
1614
- return plaintexts.map((item, index) => ({ ...item, originalIndex: index })).filter(({ plaintext }) => plaintext !== null).map(({ id, plaintext, originalIndex }) => ({
1560
+ return plaintexts.map(({ id, plaintext }) => ({
1615
1561
  id,
1616
1562
  plaintext,
1617
1563
  column: column.getName(),
1618
1564
  table: table.tableName,
1619
- originalIndex,
1620
1565
  ...lockContext && { lockContext }
1621
1566
  }));
1622
1567
  };
1623
- var createNullResult2 = (plaintexts) => {
1624
- return plaintexts.map(({ id }) => ({ id, data: null }));
1625
- };
1626
- var mapEncryptedDataToResult = (plaintexts, encryptedData) => {
1627
- const result = new Array(plaintexts.length);
1628
- let encryptedIndex = 0;
1629
- for (let i = 0; i < plaintexts.length; i++) {
1630
- if (plaintexts[i].plaintext === null) {
1631
- result[i] = { id: plaintexts[i].id, data: null };
1632
- } else {
1633
- result[i] = {
1634
- id: plaintexts[i].id,
1635
- data: encryptedData[encryptedIndex]
1636
- };
1637
- encryptedIndex++;
1638
- }
1639
- }
1640
- return result;
1641
- };
1642
1568
  var BulkEncryptOperation = class extends EncryptionOperation {
1643
1569
  client;
1644
1570
  plaintexts;
@@ -1671,20 +1597,20 @@ var BulkEncryptOperation = class extends EncryptionOperation {
1671
1597
  if (!this.plaintexts || this.plaintexts.length === 0) {
1672
1598
  return [];
1673
1599
  }
1674
- const nonNullPayloads = createEncryptPayloads(
1600
+ const payloads = createEncryptPayloads(
1675
1601
  this.plaintexts,
1676
1602
  this.column,
1677
1603
  this.table
1678
1604
  );
1679
- if (nonNullPayloads.length === 0) {
1680
- return createNullResult2(this.plaintexts);
1681
- }
1682
1605
  const { metadata } = this.getAuditData();
1683
1606
  const encryptedData = await (0, import_protect_ffi5.encryptBulk)(this.client, {
1684
- plaintexts: nonNullPayloads,
1607
+ plaintexts: payloads,
1685
1608
  unverifiedContext: metadata
1686
1609
  });
1687
- return mapEncryptedDataToResult(this.plaintexts, encryptedData);
1610
+ return encryptedData.map((data, i) => ({
1611
+ id: this.plaintexts[i].id,
1612
+ data
1613
+ }));
1688
1614
  },
1689
1615
  (error) => {
1690
1616
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -1741,22 +1667,22 @@ var BulkEncryptOperationWithLockContext = class extends EncryptionOperation {
1741
1667
  if (context.failure) {
1742
1668
  throw new Error(`[encryption]: ${context.failure.message}`);
1743
1669
  }
1744
- const nonNullPayloads = createEncryptPayloads(
1670
+ const payloads = createEncryptPayloads(
1745
1671
  plaintexts,
1746
1672
  column,
1747
1673
  table,
1748
1674
  context.data.context
1749
1675
  );
1750
- if (nonNullPayloads.length === 0) {
1751
- return createNullResult2(plaintexts);
1752
- }
1753
1676
  const { metadata } = this.getAuditData();
1754
1677
  const encryptedData = await (0, import_protect_ffi5.encryptBulk)(client, {
1755
- plaintexts: nonNullPayloads,
1678
+ plaintexts: payloads,
1756
1679
  serviceToken: context.data.ctsToken,
1757
1680
  unverifiedContext: metadata
1758
1681
  });
1759
- return mapEncryptedDataToResult(plaintexts, encryptedData);
1682
+ return encryptedData.map((data, i) => ({
1683
+ id: plaintexts[i].id,
1684
+ data
1685
+ }));
1760
1686
  },
1761
1687
  (error) => {
1762
1688
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -1906,9 +1832,6 @@ var DecryptOperation = class extends EncryptionOperation {
1906
1832
  if (!this.client) {
1907
1833
  throw noClientError();
1908
1834
  }
1909
- if (this.encryptedData === null) {
1910
- return null;
1911
- }
1912
1835
  const { metadata } = this.getAuditData();
1913
1836
  return await (0, import_protect_ffi6.decrypt)(this.client, {
1914
1837
  ciphertext: this.encryptedData,
@@ -1959,9 +1882,6 @@ var DecryptOperationWithLockContext = class extends EncryptionOperation {
1959
1882
  if (!client) {
1960
1883
  throw noClientError();
1961
1884
  }
1962
- if (encryptedData === null) {
1963
- return null;
1964
- }
1965
1885
  const { metadata } = this.getAuditData();
1966
1886
  const context = await this.lockContext.getLockContext();
1967
1887
  if (context.failure) {
@@ -2115,9 +2035,6 @@ var EncryptOperation = class extends EncryptionOperation {
2115
2035
  if (!this.client) {
2116
2036
  throw noClientError();
2117
2037
  }
2118
- if (this.plaintext === null) {
2119
- return null;
2120
- }
2121
2038
  if (typeof this.plaintext === "number" && Number.isNaN(this.plaintext)) {
2122
2039
  throw new Error("[encryption]: Cannot encrypt NaN value");
2123
2040
  }
@@ -2179,9 +2096,6 @@ var EncryptOperationWithLockContext = class extends EncryptionOperation {
2179
2096
  if (!client) {
2180
2097
  throw noClientError();
2181
2098
  }
2182
- if (plaintext === null) {
2183
- return null;
2184
- }
2185
2099
  const { metadata } = this.getAuditData();
2186
2100
  const context = await this.lockContext.getLockContext();
2187
2101
  if (context.failure) {
@@ -2345,10 +2259,6 @@ var EncryptQueryOperation = class extends EncryptionOperation {
2345
2259
  queryType: this.opts.queryType,
2346
2260
  lockContext: false
2347
2261
  });
2348
- if (this.plaintext === null || this.plaintext === void 0) {
2349
- log.emit();
2350
- return { data: null };
2351
- }
2352
2262
  const validationError = validateNumericValue(this.plaintext);
2353
2263
  if (validationError?.failure) {
2354
2264
  log.emit();
@@ -2412,10 +2322,6 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
2412
2322
  queryType: this.opts.queryType,
2413
2323
  lockContext: true
2414
2324
  });
2415
- if (this.plaintext === null || this.plaintext === void 0) {
2416
- log.emit();
2417
- return { data: null };
2418
- }
2419
2325
  const validationError = validateNumericValue(this.plaintext);
2420
2326
  if (validationError?.failure) {
2421
2327
  log.emit();
@@ -2520,7 +2426,7 @@ var EncryptionClient = class {
2520
2426
  /**
2521
2427
  * Encrypt a value - returns a promise which resolves to an encrypted value.
2522
2428
  *
2523
- * @param plaintext - The plaintext value to be encrypted. Can be null.
2429
+ * @param plaintext - The plaintext value to be encrypted.
2524
2430
  * @param opts - Options specifying the column (or nested field) and table for encryption. See {@link EncryptOptions}.
2525
2431
  * @returns An EncryptOperation that can be awaited or chained with additional methods.
2526
2432
  *
@@ -2821,7 +2727,7 @@ var EncryptionClient = class {
2821
2727
  *
2822
2728
  * Each value is encrypted with its own unique key via a single call to ZeroKMS.
2823
2729
  * Values can include optional `id` fields for correlating results back to
2824
- * your application data. Null plaintext values are preserved as null.
2730
+ * your application data.
2825
2731
  *
2826
2732
  * @param plaintexts - An array of objects with `plaintext` (and optional `id`) fields.
2827
2733
  * @param opts - Options specifying the target column (or nested {@link encryptedField}) and table. See {@link EncryptOptions}.
@@ -2842,7 +2748,6 @@ var EncryptionClient = class {
2842
2748
  * [
2843
2749
  * { id: "u1", plaintext: "alice@example.com" },
2844
2750
  * { id: "u2", plaintext: "bob@example.com" },
2845
- * { id: "u3", plaintext: null },
2846
2751
  * ],
2847
2752
  * { column: users.email, table: users },
2848
2753
  * )