{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { 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 */\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 arrayIndexModeSchema = z.union([\n  z.literal('all'),\n  z.literal('none'),\n  z.object({\n    item: z.boolean().optional(),\n    wildcard: z.boolean().optional(),\n    position: z.boolean().optional(),\n  }),\n])\n\nconst steVecIndexOptsSchema = z.object({\n  prefix: z.string(),\n  array_index_mode: arrayIndexModeSchema.optional(),\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\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\nexport type ProtectTableColumn = {\n  [key: string]:\n    | ProtectColumn\n    | {\n        [key: string]:\n          | ProtectValue\n          | {\n              [key: string]:\n                | ProtectValue\n                | {\n                    [key: string]: ProtectValue\n                  }\n            }\n      }\n}\nexport type EncryptConfig = z.infer<typeof encryptConfigSchema>\n\n// ------------------------\n// Interface definitions\n// ------------------------\nexport class ProtectValue {\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 cast_as value.\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 ProtectColumn {\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 cast_as value.\n   */\n  dataType(castAs: CastAs) {\n    this.castAsValue = castAs\n    return this\n  }\n\n  /**\n   * Enable ORE indexing (Order-Revealing Encryption).\n   */\n  orderAndRange() {\n    this.indexesValue.ore = {}\n    return this\n  }\n\n  /**\n   * Enable an Exact index. Optionally pass tokenFilters.\n   */\n  equality(tokenFilters?: TokenFilter[]) {\n    this.indexesValue.unique = {\n      token_filters: tokenFilters ?? [],\n    }\n    return this\n  }\n\n  /**\n   * Enable a Match index. Allows passing of custom match options.\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.\n   * Enables path queries ($.user.email) and containment queries ({ role: 'admin' }).\n   * Automatically sets cast_as to 'json'.\n   */\n  searchableJson() {\n    this.castAsValue = 'json'\n    this.indexesValue.ste_vec = { prefix: 'enabled', array_index_mode: 'all' }\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 ProtectTable<T extends ProtectTableColumn> {\n  constructor(\n    public readonly tableName: string,\n    private readonly columnBuilders: T,\n  ) {}\n\n  /**\n   * Build a TableDefinition object: tableName + built column configs.\n   */\n  build(): TableDefinition {\n    const builtColumns: Record<string, ColumnSchema> = {}\n\n    const processColumn = (\n      builder:\n        | ProtectColumn\n        | Record<\n            string,\n            | ProtectValue\n            | Record<\n                string,\n                | ProtectValue\n                | Record<string, ProtectValue | Record<string, ProtectValue>>\n              >\n          >,\n      colName: string,\n    ) => {\n      if (builder instanceof ProtectColumn) {\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                ...builtColumn.indexes.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 ProtectValue) {\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// User facing functions\n// ------------------------\nexport function csTable<T extends ProtectTableColumn>(\n  tableName: string,\n  columns: T,\n): ProtectTable<T> & T {\n  const tableBuilder = new ProtectTable(tableName, columns) as ProtectTable<T> &\n    T\n\n  for (const [colName, colBuilder] of Object.entries(columns)) {\n    ;(tableBuilder as ProtectTableColumn)[colName] = colBuilder\n  }\n\n  return tableBuilder\n}\n\nexport function csColumn(columnName: string) {\n  return new ProtectColumn(columnName)\n}\n\nexport function csValue(valueName: string) {\n  return new ProtectValue(valueName)\n}\n\n// ------------------------\n// Internal functions\n// ------------------------\nexport function buildEncryptConfig(\n  ...protectTables: Array<ProtectTable<ProtectTableColumn>>\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":[]}