@cipherstash/stack 0.1.0 → 0.3.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.
- package/CHANGELOG.md +12 -0
- package/README.md +11 -14
- package/dist/bin/stash.js +90 -57
- package/dist/bin/stash.js.map +1 -1
- package/dist/{chunk-SJ7JO4ME.js → chunk-JLI27P46.js} +1 -1
- package/dist/chunk-JLI27P46.js.map +1 -0
- package/dist/{chunk-2GZMIJFO.js → chunk-MW6D52V2.js} +69 -43
- package/dist/chunk-MW6D52V2.js.map +1 -0
- package/dist/{chunk-5DCT6YU2.js → chunk-OAPLZLR5.js} +7 -3
- package/dist/{chunk-5DCT6YU2.js.map → chunk-OAPLZLR5.js.map} +1 -1
- package/dist/{chunk-7XRPN2KX.js → chunk-TBAIVO6T.js} +26 -23
- package/dist/chunk-TBAIVO6T.js.map +1 -0
- package/dist/{client-BxJG56Ey.d.cts → client-Bf0Xw2xo.d.cts} +44 -26
- package/dist/{client-DtGq9dJp.d.ts → client-Kfp8OsPB.d.ts} +44 -26
- package/dist/client.cjs +25 -22
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +5 -5
- package/dist/drizzle/index.cjs +19 -16
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.d.cts +5 -5
- package/dist/drizzle/index.d.ts +5 -5
- package/dist/drizzle/index.js +2 -2
- package/dist/drizzle/index.js.map +1 -1
- package/dist/dynamodb/index.cjs.map +1 -1
- package/dist/dynamodb/index.d.cts +10 -10
- package/dist/dynamodb/index.d.ts +10 -10
- package/dist/dynamodb/index.js.map +1 -1
- package/dist/identity/index.cjs +6 -2
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.js +1 -1
- package/dist/index.cjs +94 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -7
- package/dist/schema/index.cjs +31 -28
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +11 -11
- package/dist/secrets/index.cjs +90 -57
- package/dist/secrets/index.cjs.map +1 -1
- package/dist/secrets/index.d.cts +1 -1
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/index.js +4 -4
- package/dist/secrets/index.js.map +1 -1
- package/dist/supabase/index.cjs +7 -7
- package/dist/supabase/index.cjs.map +1 -1
- package/dist/supabase/index.d.cts +3 -3
- package/dist/supabase/index.d.ts +3 -3
- package/dist/supabase/index.js +3 -3
- package/dist/supabase/index.js.map +1 -1
- package/dist/{types-public-BCj1L4fi.d.ts → types-public-0CzBV45X.d.cts} +100 -58
- package/dist/{types-public-BCj1L4fi.d.cts → types-public-0CzBV45X.d.ts} +100 -58
- package/dist/types-public.cjs.map +1 -1
- package/dist/types-public.d.cts +1 -1
- package/dist/types-public.d.ts +1 -1
- package/dist/types-public.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-2GZMIJFO.js.map +0 -1
- package/dist/chunk-7XRPN2KX.js.map +0 -1
- package/dist/chunk-SJ7JO4ME.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/dynamodb/index.ts","../../src/dynamodb/operations/bulk-decrypt-models.ts","../../src/dynamodb/helpers.ts","../../src/dynamodb/operations/base-operation.ts","../../src/dynamodb/operations/bulk-encrypt-models.ts","../../src/dynamodb/operations/decrypt-model.ts","../../src/dynamodb/operations/encrypt-model.ts"],"sourcesContent":["import type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport { BulkDecryptModelsOperation } from './operations/bulk-decrypt-models'\nimport { BulkEncryptModelsOperation } from './operations/bulk-encrypt-models'\nimport { DecryptModelOperation } from './operations/decrypt-model'\nimport { EncryptModelOperation } from './operations/encrypt-model'\nimport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBInstance,\n} from './types'\n\n/**\n * Create an encrypted DynamoDB helper bound to an `EncryptionClient`.\n *\n * Returns an object with `encryptModel`, `decryptModel`, `bulkEncryptModels`,\n * and `bulkDecryptModels` methods that transparently encrypt/decrypt DynamoDB\n * items according to the provided table schema.\n *\n * @param config - Configuration containing the `encryptionClient` and optional\n * logging / error-handling callbacks.\n * @returns An {@link EncryptedDynamoDBInstance} with encrypt/decrypt operations.\n *\n * @example\n * ```typescript\n * import { Encryption } from \"@cipherstash/stack\"\n * import { encryptedDynamoDB } from \"@cipherstash/stack/dynamodb\"\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const client = await Encryption({ schemas: [users] })\n * const dynamo = encryptedDynamoDB({ encryptionClient: client })\n *\n * const encrypted = await dynamo.encryptModel({ email: \"a@b.com\" }, users)\n * ```\n */\nexport function encryptedDynamoDB(\n config: EncryptedDynamoDBConfig,\n): EncryptedDynamoDBInstance {\n const { encryptionClient, options } = config\n\n return {\n encryptModel<T extends Record<string, unknown>>(\n item: T,\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new EncryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkEncryptModels<T extends Record<string, unknown>>(\n items: T[],\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new BulkEncryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n\n decryptModel<T extends Record<string, unknown>>(\n item: Record<string, EncryptedValue | unknown>,\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new DecryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkDecryptModels<T extends Record<string, unknown>>(\n items: Record<string, EncryptedValue | unknown>[],\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new BulkDecryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n }\n}\n\nexport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBError,\n EncryptedDynamoDBInstance,\n} from './types'\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkDecryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>[]> {\n private encryptionClient: EncryptionClient\n private items: Record<string, EncryptedValue | unknown>[]\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: Record<string, EncryptedValue | unknown>[],\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>[], EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const itemsWithEqlPayloads = this.items.map((item) =>\n toItemWithEqlPayloads(item, this.table),\n )\n\n const decryptResult = await this.encryptionClient\n .bulkDecryptModels<T>(itemsWithEqlPayloads as T[])\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'bulkDecryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport type { ProtectErrorCode } from '@cipherstash/protect-ffi'\nimport { ProtectError as FfiProtectError } from '@cipherstash/protect-ffi'\nimport type { EncryptedDynamoDBError } from './types'\n\nexport const ciphertextAttrSuffix = '__source'\nexport const searchTermAttrSuffix = '__hmac'\n\nexport class EncryptedDynamoDBErrorImpl\n extends Error\n implements EncryptedDynamoDBError\n{\n constructor(\n message: string,\n public code: ProtectErrorCode | 'DYNAMODB_ENCRYPTION_ERROR',\n public details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'EncryptedDynamoDBError'\n }\n}\n\nexport function handleError(\n error: unknown,\n context: string,\n options?: {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n },\n): EncryptedDynamoDBError {\n // Preserve FFI error code if available, otherwise use generic DynamoDB error code\n // Check for FfiProtectError instance or plain error objects with code property\n const errorObj = error as Record<string, unknown>\n const errorCode =\n error instanceof FfiProtectError\n ? error.code\n : errorObj &&\n typeof errorObj === 'object' &&\n 'code' in errorObj &&\n typeof errorObj.code === 'string'\n ? (errorObj.code as ProtectErrorCode)\n : 'DYNAMODB_ENCRYPTION_ERROR'\n\n const errorMessage =\n error instanceof Error\n ? error.message\n : errorObj && typeof errorObj.message === 'string'\n ? errorObj.message\n : String(error)\n\n const dynamoError = new EncryptedDynamoDBErrorImpl(errorMessage, errorCode, {\n context,\n })\n\n if (options?.errorHandler) {\n options.errorHandler(dynamoError)\n }\n\n if (options?.logger) {\n options.logger.error(`Error in ${context}`, dynamoError)\n }\n\n return dynamoError\n}\n\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => deepClone(item)) as unknown as T\n }\n\n return Object.entries(obj as Record<string, unknown>).reduce(\n (acc, [key, value]) => ({\n // biome-ignore lint/performance/noAccumulatingSpread: TODO later\n ...acc,\n [key]: deepClone(value),\n }),\n {} as T,\n )\n}\n\nexport function toEncryptedDynamoItem(\n encrypted: Record<string, unknown>,\n encryptedAttrs: string[],\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Handle encrypted payload\n if (\n encryptedAttrs.includes(attrName) ||\n (isNested &&\n typeof attrValue === 'object' &&\n 'c' in (attrValue as object))\n ) {\n const encryptPayload = attrValue as EncryptedValue\n if (encryptPayload?.c) {\n const result: Record<string, unknown> = {}\n if (encryptPayload.hm) {\n result[`${attrName}${searchTermAttrSuffix}`] = encryptPayload.hm\n }\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.c\n return result\n }\n\n if (encryptPayload?.sv) {\n const result: Record<string, unknown> = {}\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.sv\n return result\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(encrypted).reduce(\n (putItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, putItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n\nexport function toItemWithEqlPayloads(\n decrypted: Record<string, EncryptedValue | unknown>,\n encryptionSchema: ProtectTable<ProtectTableColumn>,\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Skip HMAC fields\n if (attrName.endsWith(searchTermAttrSuffix)) {\n return {}\n }\n\n const encryptConfig = encryptionSchema.build()\n const encryptedAttrs = Object.keys(encryptConfig.columns)\n const columnName = attrName.slice(0, -ciphertextAttrSuffix.length)\n\n // Handle encrypted payload\n if (\n attrName.endsWith(ciphertextAttrSuffix) &&\n (encryptedAttrs.includes(columnName) || isNested)\n ) {\n const i = { c: columnName, t: encryptConfig.tableName }\n const v = 2\n\n // Nested values are not searchable, so we can just return the standard EQL payload.\n // Worth noting, that encryptConfig.columns[columnName] will be undefined if isNested is true.\n if (\n !isNested &&\n encryptConfig.columns[columnName].cast_as === 'json' &&\n encryptConfig.columns[columnName].indexes.ste_vec\n ) {\n return {\n [columnName]: {\n i,\n v,\n k: 'sv',\n sv: attrValue,\n },\n }\n }\n\n return {\n [columnName]: {\n i,\n v,\n k: 'ct',\n c: attrValue,\n },\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(decrypted).reduce(\n (formattedItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, formattedItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n","import type { Result } from '@byteslice/result'\nimport type { EncryptedDynamoDBError } from '../types'\n\nexport type AuditConfig = {\n metadata?: Record<string, unknown>\n}\n\nexport type AuditData = {\n metadata?: Record<string, unknown>\n}\n\nexport type DynamoDBOperationOptions = {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n}\n\nexport abstract class DynamoDBOperation<T> {\n protected auditMetadata?: Record<string, unknown>\n protected logger?: DynamoDBOperationOptions['logger']\n protected errorHandler?: DynamoDBOperationOptions['errorHandler']\n\n constructor(options?: DynamoDBOperationOptions) {\n this.logger = options?.logger\n this.errorHandler = options?.errorHandler\n }\n\n /**\n * Attach audit metadata to this operation. Can be chained.\n */\n audit(config: AuditConfig): this {\n this.auditMetadata = config.metadata\n return this\n }\n\n /**\n * Get the audit metadata for this operation.\n */\n protected getAuditData(): AuditData {\n return {\n metadata: this.auditMetadata,\n }\n }\n\n /**\n * Execute the operation and return a Result\n */\n abstract execute(): Promise<Result<T, EncryptedDynamoDBError>>\n\n /**\n * Make the operation thenable\n */\n public then<TResult1 = Result<T, EncryptedDynamoDBError>, TResult2 = never>(\n onfulfilled?:\n | ((\n value: Result<T, EncryptedDynamoDBError>,\n ) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkEncryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T[]> {\n private encryptionClient: EncryptionClient\n private items: T[]\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: T[],\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<Result<T[], EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .bulkEncryptModels(\n this.items.map((item) => deepClone(item)),\n this.table,\n )\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = encryptResult.data.map((item) => deepClone(item))\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return data.map(\n (encrypted) => toEncryptedDynamoItem(encrypted, encryptedAttrs) as T,\n )\n },\n (error) =>\n handleError(error, 'bulkEncryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class DecryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>> {\n private encryptionClient: EncryptionClient\n private item: Record<string, EncryptedValue | unknown>\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: Record<string, EncryptedValue | unknown>,\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>, EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const withEqlPayloads = toItemWithEqlPayloads(this.item, this.table)\n\n const decryptResult = await this.encryptionClient\n .decryptModel<T>(withEqlPayloads as T)\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'decryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class EncryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T> {\n private encryptionClient: EncryptionClient\n private item: T\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: T,\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<Result<T, EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .encryptModel(deepClone(this.item), this.table)\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = deepClone(encryptResult.data)\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return toEncryptedDynamoItem(data, encryptedAttrs) as T\n },\n (error) =>\n handleError(error, 'encryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,oBAAwC;;;ACAxC,yBAAgD;AAGzC,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;AAE7B,IAAM,6BAAN,cACG,MAEV;AAAA,EACE,YACE,SACO,MACA,SACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,YACd,OACA,SACA,SAMwB;AAGxB,QAAM,WAAW;AACjB,QAAM,YACJ,iBAAiB,mBAAAA,eACb,MAAM,OACN,YACE,OAAO,aAAa,YACpB,UAAU,YACV,OAAO,SAAS,SAAS,WACxB,SAAS,OACV;AAER,QAAM,eACJ,iBAAiB,QACb,MAAM,UACN,YAAY,OAAO,SAAS,YAAY,WACtC,SAAS,UACT,OAAO,KAAK;AAEpB,QAAM,cAAc,IAAI,2BAA2B,cAAc,WAAW;AAAA,IAC1E;AAAA,EACF,CAAC;AAED,MAAI,SAAS,cAAc;AACzB,YAAQ,aAAa,WAAW;AAAA,EAClC;AAEA,MAAI,SAAS,QAAQ;AACnB,YAAQ,OAAO,MAAM,YAAY,OAAO,IAAI,WAAW;AAAA,EACzD;AAEA,SAAO;AACT;AAEO,SAAS,UAAa,KAAW;AACtC,MAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC;AAAA,EAC1C;AAEA,SAAO,OAAO,QAAQ,GAA8B,EAAE;AAAA,IACpD,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO;AAAA;AAAA,MAEtB,GAAG;AAAA,MACH,CAAC,GAAG,GAAG,UAAU,KAAK;AAAA,IACxB;AAAA,IACA,CAAC;AAAA,EACH;AACF;AAEO,SAAS,sBACd,WACA,gBACyB;AACzB,WAAS,aACP,UACA,WACA,UACyB;AACzB,QAAI,cAAc,QAAQ,cAAc,QAAW;AACjD,aAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,IACjC;AAGA,QACE,eAAe,SAAS,QAAQ,KAC/B,YACC,OAAO,cAAc,YACrB,OAAQ,WACV;AACA,YAAM,iBAAiB;AACvB,UAAI,gBAAgB,GAAG;AACrB,cAAM,SAAkC,CAAC;AACzC,YAAI,eAAe,IAAI;AACrB,iBAAO,GAAG,QAAQ,GAAG,oBAAoB,EAAE,IAAI,eAAe;AAAA,QAChE;AACA,eAAO,GAAG,QAAQ,GAAG,oBAAoB,EAAE,IAAI,eAAe;AAC9D,eAAO;AAAA,MACT;AAEA,UAAI,gBAAgB,IAAI;AACtB,cAAM,SAAkC,CAAC;AACzC,eAAO,GAAG,QAAQ,GAAG,oBAAoB,EAAE,IAAI,eAAe;AAC9D,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC9D,YAAM,eAAe,OAAO;AAAA,QAC1B;AAAA,MACF,EAAE;AAAA,QACA,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM;AACnB,gBAAM,YAAY,aAAa,KAAK,KAAK,IAAI;AAC7C,iBAAO,OAAO,OAAO,CAAC,GAAG,KAAK,SAAS;AAAA,QACzC;AAAA,QACA,CAAC;AAAA,MACH;AACA,aAAO,EAAE,CAAC,QAAQ,GAAG,aAAa;AAAA,IACpC;AAGA,WAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,EACjC;AAEA,SAAO,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC/B,CAAC,SAAS,CAAC,UAAU,SAAS,MAAM;AAClC,YAAM,YAAY,aAAa,UAAU,WAAW,KAAK;AACzD,aAAO,OAAO,OAAO,CAAC,GAAG,SAAS,SAAS;AAAA,IAC7C;AAAA,IACA,CAAC;AAAA,EACH;AACF;AAEO,SAAS,sBACd,WACA,kBACyB;AACzB,WAAS,aACP,UACA,WACA,UACyB;AACzB,QAAI,cAAc,QAAQ,cAAc,QAAW;AACjD,aAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,IACjC;AAGA,QAAI,SAAS,SAAS,oBAAoB,GAAG;AAC3C,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,gBAAgB,iBAAiB,MAAM;AAC7C,UAAM,iBAAiB,OAAO,KAAK,cAAc,OAAO;AACxD,UAAM,aAAa,SAAS,MAAM,GAAG,CAAC,qBAAqB,MAAM;AAGjE,QACE,SAAS,SAAS,oBAAoB,MACrC,eAAe,SAAS,UAAU,KAAK,WACxC;AACA,YAAM,IAAI,EAAE,GAAG,YAAY,GAAG,cAAc,UAAU;AACtD,YAAM,IAAI;AAIV,UACE,CAAC,YACD,cAAc,QAAQ,UAAU,EAAE,YAAY,UAC9C,cAAc,QAAQ,UAAU,EAAE,QAAQ,SAC1C;AACA,eAAO;AAAA,UACL,CAAC,UAAU,GAAG;AAAA,YACZ;AAAA,YACA;AAAA,YACA,GAAG;AAAA,YACH,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,CAAC,UAAU,GAAG;AAAA,UACZ;AAAA,UACA;AAAA,UACA,GAAG;AAAA,UACH,GAAG;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC9D,YAAM,eAAe,OAAO;AAAA,QAC1B;AAAA,MACF,EAAE;AAAA,QACA,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM;AACnB,gBAAM,YAAY,aAAa,KAAK,KAAK,IAAI;AAC7C,iBAAO,OAAO,OAAO,CAAC,GAAG,KAAK,SAAS;AAAA,QACzC;AAAA,QACA,CAAC;AAAA,MACH;AACA,aAAO,EAAE,CAAC,QAAQ,GAAG,aAAa;AAAA,IACpC;AAGA,WAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,EACjC;AAEA,SAAO,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC/B,CAAC,eAAe,CAAC,UAAU,SAAS,MAAM;AACxC,YAAM,YAAY,aAAa,UAAU,WAAW,KAAK;AACzD,aAAO,OAAO,OAAO,CAAC,GAAG,eAAe,SAAS;AAAA,IACnD;AAAA,IACA,CAAC;AAAA,EACH;AACF;;;ACvNO,IAAe,oBAAf,MAAoC;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EAEV,YAAY,SAAoC;AAC9C,SAAK,SAAS,SAAS;AACvB,SAAK,eAAe,SAAS;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAA2B;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKU,eAA0B;AAClC,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAUO,KACL,aAKA,YAC8B;AAC9B,WAAO,KAAK,QAAQ,EAAE,KAAK,aAAa,UAAU;AAAA,EACpD;AACF;;;AFpDO,IAAM,6BAAN,cAEG,kBAAkC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,OACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAEX;AACA,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,uBAAuB,KAAK,MAAM;AAAA,UAAI,CAAC,SAC3C,sBAAsB,MAAM,KAAK,KAAK;AAAA,QACxC;AAEA,cAAM,gBAAgB,MAAM,KAAK,iBAC9B,kBAAqB,oBAA2B,EAChD,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,eAAO,cAAc;AAAA,MACvB;AAAA,MACA,CAAC,UACC,YAAY,OAAO,qBAAqB;AAAA,QACtC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AG5DA,IAAAC,iBAAwC;AAQjC,IAAM,6BAAN,cAEG,kBAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,OACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAAwD;AACnE,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,gBAAgB,MAAM,KAAK,iBAC9B;AAAA,UACC,KAAK,MAAM,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC;AAAA,UACxC,KAAK;AAAA,QACP,EACC,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,cAAM,OAAO,cAAc,KAAK,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC;AAC7D,cAAM,iBAAiB,OAAO,KAAK,KAAK,MAAM,MAAM,EAAE,OAAO;AAE7D,eAAO,KAAK;AAAA,UACV,CAAC,cAAc,sBAAsB,WAAW,cAAc;AAAA,QAChE;AAAA,MACF;AAAA,MACA,CAAC,UACC,YAAY,OAAO,qBAAqB;AAAA,QACtC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AC5DA,IAAAC,iBAAwC;AAQjC,IAAM,wBAAN,cAEG,kBAAgC;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,MACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAEX;AACA,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,kBAAkB,sBAAsB,KAAK,MAAM,KAAK,KAAK;AAEnE,cAAM,gBAAgB,MAAM,KAAK,iBAC9B,aAAgB,eAAoB,EACpC,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,eAAO,cAAc;AAAA,MACvB;AAAA,MACA,CAAC,UACC,YAAY,OAAO,gBAAgB;AAAA,QACjC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AC1DA,IAAAC,iBAAwC;AAQjC,IAAM,wBAAN,cAEG,kBAAqB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,MACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAAsD;AACjE,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,gBAAgB,MAAM,KAAK,iBAC9B,aAAa,UAAU,KAAK,IAAI,GAAG,KAAK,KAAK,EAC7C,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,cAAM,OAAO,UAAU,cAAc,IAAI;AACzC,cAAM,iBAAiB,OAAO,KAAK,KAAK,MAAM,MAAM,EAAE,OAAO;AAE7D,eAAO,sBAAsB,MAAM,cAAc;AAAA,MACnD;AAAA,MACA,CAAC,UACC,YAAY,OAAO,gBAAgB;AAAA,QACjC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;ANpBO,SAAS,kBACd,QAC2B;AAC3B,QAAM,EAAE,kBAAkB,QAAQ,IAAI;AAEtC,SAAO;AAAA,IACL,aACE,MACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,kBACE,OACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,aACE,MACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,kBACE,OACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["FfiProtectError","import_result","import_result","import_result"]}
|
|
1
|
+
{"version":3,"sources":["../../src/dynamodb/index.ts","../../src/dynamodb/operations/bulk-decrypt-models.ts","../../src/dynamodb/helpers.ts","../../src/dynamodb/operations/base-operation.ts","../../src/dynamodb/operations/bulk-encrypt-models.ts","../../src/dynamodb/operations/decrypt-model.ts","../../src/dynamodb/operations/encrypt-model.ts"],"sourcesContent":["import type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport { BulkDecryptModelsOperation } from './operations/bulk-decrypt-models'\nimport { BulkEncryptModelsOperation } from './operations/bulk-encrypt-models'\nimport { DecryptModelOperation } from './operations/decrypt-model'\nimport { EncryptModelOperation } from './operations/encrypt-model'\nimport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBInstance,\n} from './types'\n\n/**\n * Create an encrypted DynamoDB helper bound to an `EncryptionClient`.\n *\n * Returns an object with `encryptModel`, `decryptModel`, `bulkEncryptModels`,\n * and `bulkDecryptModels` methods that transparently encrypt/decrypt DynamoDB\n * items according to the provided table schema.\n *\n * @param config - Configuration containing the `encryptionClient` and optional\n * logging / error-handling callbacks.\n * @returns An {@link EncryptedDynamoDBInstance} with encrypt/decrypt operations.\n *\n * @example\n * ```typescript\n * import { Encryption } from \"@cipherstash/stack\"\n * import { encryptedDynamoDB } from \"@cipherstash/stack/dynamodb\"\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const client = await Encryption({ schemas: [users] })\n * const dynamo = encryptedDynamoDB({ encryptionClient: client })\n *\n * const encrypted = await dynamo.encryptModel({ email: \"a@b.com\" }, users)\n * ```\n */\nexport function encryptedDynamoDB(\n config: EncryptedDynamoDBConfig,\n): EncryptedDynamoDBInstance {\n const { encryptionClient, options } = config\n\n return {\n encryptModel<T extends Record<string, unknown>>(\n item: T,\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new EncryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkEncryptModels<T extends Record<string, unknown>>(\n items: T[],\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new BulkEncryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n\n decryptModel<T extends Record<string, unknown>>(\n item: Record<string, EncryptedValue | unknown>,\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new DecryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkDecryptModels<T extends Record<string, unknown>>(\n items: Record<string, EncryptedValue | unknown>[],\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new BulkDecryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n }\n}\n\nexport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBError,\n EncryptedDynamoDBInstance,\n} from './types'\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkDecryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>[]> {\n private encryptionClient: EncryptionClient\n private items: Record<string, EncryptedValue | unknown>[]\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: Record<string, EncryptedValue | unknown>[],\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>[], EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const itemsWithEqlPayloads = this.items.map((item) =>\n toItemWithEqlPayloads(item, this.table),\n )\n\n const decryptResult = await this.encryptionClient\n .bulkDecryptModels<T>(itemsWithEqlPayloads as T[])\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'bulkDecryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport type { ProtectErrorCode } from '@cipherstash/protect-ffi'\nimport { ProtectError as FfiProtectError } from '@cipherstash/protect-ffi'\nimport type { EncryptedDynamoDBError } from './types'\n\nexport const ciphertextAttrSuffix = '__source'\nexport const searchTermAttrSuffix = '__hmac'\n\nexport class EncryptedDynamoDBErrorImpl\n extends Error\n implements EncryptedDynamoDBError\n{\n constructor(\n message: string,\n public code: ProtectErrorCode | 'DYNAMODB_ENCRYPTION_ERROR',\n public details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'EncryptedDynamoDBError'\n }\n}\n\nexport function handleError(\n error: unknown,\n context: string,\n options?: {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n },\n): EncryptedDynamoDBError {\n // Preserve FFI error code if available, otherwise use generic DynamoDB error code\n // Check for FfiProtectError instance or plain error objects with code property\n const errorObj = error as Record<string, unknown>\n const errorCode =\n error instanceof FfiProtectError\n ? error.code\n : errorObj &&\n typeof errorObj === 'object' &&\n 'code' in errorObj &&\n typeof errorObj.code === 'string'\n ? (errorObj.code as ProtectErrorCode)\n : 'DYNAMODB_ENCRYPTION_ERROR'\n\n const errorMessage =\n error instanceof Error\n ? error.message\n : errorObj && typeof errorObj.message === 'string'\n ? errorObj.message\n : String(error)\n\n const dynamoError = new EncryptedDynamoDBErrorImpl(errorMessage, errorCode, {\n context,\n })\n\n if (options?.errorHandler) {\n options.errorHandler(dynamoError)\n }\n\n if (options?.logger) {\n options.logger.error(`Error in ${context}`, dynamoError)\n }\n\n return dynamoError\n}\n\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => deepClone(item)) as unknown as T\n }\n\n return Object.entries(obj as Record<string, unknown>).reduce(\n (acc, [key, value]) => ({\n // biome-ignore lint/performance/noAccumulatingSpread: TODO later\n ...acc,\n [key]: deepClone(value),\n }),\n {} as T,\n )\n}\n\nexport function toEncryptedDynamoItem(\n encrypted: Record<string, unknown>,\n encryptedAttrs: string[],\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Handle encrypted payload\n if (\n encryptedAttrs.includes(attrName) ||\n (isNested &&\n typeof attrValue === 'object' &&\n 'c' in (attrValue as object))\n ) {\n const encryptPayload = attrValue as EncryptedValue\n if (encryptPayload?.c) {\n const result: Record<string, unknown> = {}\n if (encryptPayload.hm) {\n result[`${attrName}${searchTermAttrSuffix}`] = encryptPayload.hm\n }\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.c\n return result\n }\n\n if (encryptPayload?.sv) {\n const result: Record<string, unknown> = {}\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.sv\n return result\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(encrypted).reduce(\n (putItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, putItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n\nexport function toItemWithEqlPayloads(\n decrypted: Record<string, EncryptedValue | unknown>,\n encryptionSchema: EncryptedTable<EncryptedTableColumn>,\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Skip HMAC fields\n if (attrName.endsWith(searchTermAttrSuffix)) {\n return {}\n }\n\n const encryptConfig = encryptionSchema.build()\n const encryptedAttrs = Object.keys(encryptConfig.columns)\n const columnName = attrName.slice(0, -ciphertextAttrSuffix.length)\n\n // Handle encrypted payload\n if (\n attrName.endsWith(ciphertextAttrSuffix) &&\n (encryptedAttrs.includes(columnName) || isNested)\n ) {\n const i = { c: columnName, t: encryptConfig.tableName }\n const v = 2\n\n // Nested values are not searchable, so we can just return the standard EQL payload.\n // Worth noting, that encryptConfig.columns[columnName] will be undefined if isNested is true.\n if (\n !isNested &&\n encryptConfig.columns[columnName].cast_as === 'json' &&\n encryptConfig.columns[columnName].indexes.ste_vec\n ) {\n return {\n [columnName]: {\n i,\n v,\n k: 'sv',\n sv: attrValue,\n },\n }\n }\n\n return {\n [columnName]: {\n i,\n v,\n k: 'ct',\n c: attrValue,\n },\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(decrypted).reduce(\n (formattedItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, formattedItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n","import type { Result } from '@byteslice/result'\nimport type { EncryptedDynamoDBError } from '../types'\n\nexport type AuditConfig = {\n metadata?: Record<string, unknown>\n}\n\nexport type AuditData = {\n metadata?: Record<string, unknown>\n}\n\nexport type DynamoDBOperationOptions = {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n}\n\nexport abstract class DynamoDBOperation<T> {\n protected auditMetadata?: Record<string, unknown>\n protected logger?: DynamoDBOperationOptions['logger']\n protected errorHandler?: DynamoDBOperationOptions['errorHandler']\n\n constructor(options?: DynamoDBOperationOptions) {\n this.logger = options?.logger\n this.errorHandler = options?.errorHandler\n }\n\n /**\n * Attach audit metadata to this operation. Can be chained.\n */\n audit(config: AuditConfig): this {\n this.auditMetadata = config.metadata\n return this\n }\n\n /**\n * Get the audit metadata for this operation.\n */\n protected getAuditData(): AuditData {\n return {\n metadata: this.auditMetadata,\n }\n }\n\n /**\n * Execute the operation and return a Result\n */\n abstract execute(): Promise<Result<T, EncryptedDynamoDBError>>\n\n /**\n * Make the operation thenable\n */\n public then<TResult1 = Result<T, EncryptedDynamoDBError>, TResult2 = never>(\n onfulfilled?:\n | ((\n value: Result<T, EncryptedDynamoDBError>,\n ) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkEncryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T[]> {\n private encryptionClient: EncryptionClient\n private items: T[]\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: T[],\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<Result<T[], EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .bulkEncryptModels(\n this.items.map((item) => deepClone(item)),\n this.table,\n )\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = encryptResult.data.map((item) => deepClone(item))\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return data.map(\n (encrypted) => toEncryptedDynamoItem(encrypted, encryptedAttrs) as T,\n )\n },\n (error) =>\n handleError(error, 'bulkEncryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class DecryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>> {\n private encryptionClient: EncryptionClient\n private item: Record<string, EncryptedValue | unknown>\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: Record<string, EncryptedValue | unknown>,\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>, EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const withEqlPayloads = toItemWithEqlPayloads(this.item, this.table)\n\n const decryptResult = await this.encryptionClient\n .decryptModel<T>(withEqlPayloads as T)\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'decryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class EncryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T> {\n private encryptionClient: EncryptionClient\n private item: T\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: T,\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<Result<T, EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .encryptModel(deepClone(this.item), this.table)\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = deepClone(encryptResult.data)\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return toEncryptedDynamoItem(data, encryptedAttrs) as T\n },\n (error) =>\n handleError(error, 'encryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,oBAAwC;;;ACAxC,yBAAgD;AAGzC,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;AAE7B,IAAM,6BAAN,cACG,MAEV;AAAA,EACE,YACE,SACO,MACA,SACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,YACd,OACA,SACA,SAMwB;AAGxB,QAAM,WAAW;AACjB,QAAM,YACJ,iBAAiB,mBAAAA,eACb,MAAM,OACN,YACE,OAAO,aAAa,YACpB,UAAU,YACV,OAAO,SAAS,SAAS,WACxB,SAAS,OACV;AAER,QAAM,eACJ,iBAAiB,QACb,MAAM,UACN,YAAY,OAAO,SAAS,YAAY,WACtC,SAAS,UACT,OAAO,KAAK;AAEpB,QAAM,cAAc,IAAI,2BAA2B,cAAc,WAAW;AAAA,IAC1E;AAAA,EACF,CAAC;AAED,MAAI,SAAS,cAAc;AACzB,YAAQ,aAAa,WAAW;AAAA,EAClC;AAEA,MAAI,SAAS,QAAQ;AACnB,YAAQ,OAAO,MAAM,YAAY,OAAO,IAAI,WAAW;AAAA,EACzD;AAEA,SAAO;AACT;AAEO,SAAS,UAAa,KAAW;AACtC,MAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC;AAAA,EAC1C;AAEA,SAAO,OAAO,QAAQ,GAA8B,EAAE;AAAA,IACpD,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO;AAAA;AAAA,MAEtB,GAAG;AAAA,MACH,CAAC,GAAG,GAAG,UAAU,KAAK;AAAA,IACxB;AAAA,IACA,CAAC;AAAA,EACH;AACF;AAEO,SAAS,sBACd,WACA,gBACyB;AACzB,WAAS,aACP,UACA,WACA,UACyB;AACzB,QAAI,cAAc,QAAQ,cAAc,QAAW;AACjD,aAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,IACjC;AAGA,QACE,eAAe,SAAS,QAAQ,KAC/B,YACC,OAAO,cAAc,YACrB,OAAQ,WACV;AACA,YAAM,iBAAiB;AACvB,UAAI,gBAAgB,GAAG;AACrB,cAAM,SAAkC,CAAC;AACzC,YAAI,eAAe,IAAI;AACrB,iBAAO,GAAG,QAAQ,GAAG,oBAAoB,EAAE,IAAI,eAAe;AAAA,QAChE;AACA,eAAO,GAAG,QAAQ,GAAG,oBAAoB,EAAE,IAAI,eAAe;AAC9D,eAAO;AAAA,MACT;AAEA,UAAI,gBAAgB,IAAI;AACtB,cAAM,SAAkC,CAAC;AACzC,eAAO,GAAG,QAAQ,GAAG,oBAAoB,EAAE,IAAI,eAAe;AAC9D,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC9D,YAAM,eAAe,OAAO;AAAA,QAC1B;AAAA,MACF,EAAE;AAAA,QACA,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM;AACnB,gBAAM,YAAY,aAAa,KAAK,KAAK,IAAI;AAC7C,iBAAO,OAAO,OAAO,CAAC,GAAG,KAAK,SAAS;AAAA,QACzC;AAAA,QACA,CAAC;AAAA,MACH;AACA,aAAO,EAAE,CAAC,QAAQ,GAAG,aAAa;AAAA,IACpC;AAGA,WAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,EACjC;AAEA,SAAO,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC/B,CAAC,SAAS,CAAC,UAAU,SAAS,MAAM;AAClC,YAAM,YAAY,aAAa,UAAU,WAAW,KAAK;AACzD,aAAO,OAAO,OAAO,CAAC,GAAG,SAAS,SAAS;AAAA,IAC7C;AAAA,IACA,CAAC;AAAA,EACH;AACF;AAEO,SAAS,sBACd,WACA,kBACyB;AACzB,WAAS,aACP,UACA,WACA,UACyB;AACzB,QAAI,cAAc,QAAQ,cAAc,QAAW;AACjD,aAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,IACjC;AAGA,QAAI,SAAS,SAAS,oBAAoB,GAAG;AAC3C,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,gBAAgB,iBAAiB,MAAM;AAC7C,UAAM,iBAAiB,OAAO,KAAK,cAAc,OAAO;AACxD,UAAM,aAAa,SAAS,MAAM,GAAG,CAAC,qBAAqB,MAAM;AAGjE,QACE,SAAS,SAAS,oBAAoB,MACrC,eAAe,SAAS,UAAU,KAAK,WACxC;AACA,YAAM,IAAI,EAAE,GAAG,YAAY,GAAG,cAAc,UAAU;AACtD,YAAM,IAAI;AAIV,UACE,CAAC,YACD,cAAc,QAAQ,UAAU,EAAE,YAAY,UAC9C,cAAc,QAAQ,UAAU,EAAE,QAAQ,SAC1C;AACA,eAAO;AAAA,UACL,CAAC,UAAU,GAAG;AAAA,YACZ;AAAA,YACA;AAAA,YACA,GAAG;AAAA,YACH,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,CAAC,UAAU,GAAG;AAAA,UACZ;AAAA,UACA;AAAA,UACA,GAAG;AAAA,UACH,GAAG;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC9D,YAAM,eAAe,OAAO;AAAA,QAC1B;AAAA,MACF,EAAE;AAAA,QACA,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM;AACnB,gBAAM,YAAY,aAAa,KAAK,KAAK,IAAI;AAC7C,iBAAO,OAAO,OAAO,CAAC,GAAG,KAAK,SAAS;AAAA,QACzC;AAAA,QACA,CAAC;AAAA,MACH;AACA,aAAO,EAAE,CAAC,QAAQ,GAAG,aAAa;AAAA,IACpC;AAGA,WAAO,EAAE,CAAC,QAAQ,GAAG,UAAU;AAAA,EACjC;AAEA,SAAO,OAAO,QAAQ,SAAS,EAAE;AAAA,IAC/B,CAAC,eAAe,CAAC,UAAU,SAAS,MAAM;AACxC,YAAM,YAAY,aAAa,UAAU,WAAW,KAAK;AACzD,aAAO,OAAO,OAAO,CAAC,GAAG,eAAe,SAAS;AAAA,IACnD;AAAA,IACA,CAAC;AAAA,EACH;AACF;;;ACvNO,IAAe,oBAAf,MAAoC;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EAEV,YAAY,SAAoC;AAC9C,SAAK,SAAS,SAAS;AACvB,SAAK,eAAe,SAAS;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAA2B;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKU,eAA0B;AAClC,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAUO,KACL,aAKA,YAC8B;AAC9B,WAAO,KAAK,QAAQ,EAAE,KAAK,aAAa,UAAU;AAAA,EACpD;AACF;;;AFpDO,IAAM,6BAAN,cAEG,kBAAkC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,OACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAEX;AACA,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,uBAAuB,KAAK,MAAM;AAAA,UAAI,CAAC,SAC3C,sBAAsB,MAAM,KAAK,KAAK;AAAA,QACxC;AAEA,cAAM,gBAAgB,MAAM,KAAK,iBAC9B,kBAAqB,oBAA2B,EAChD,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,eAAO,cAAc;AAAA,MACvB;AAAA,MACA,CAAC,UACC,YAAY,OAAO,qBAAqB;AAAA,QACtC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AG5DA,IAAAC,iBAAwC;AAQjC,IAAM,6BAAN,cAEG,kBAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,OACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAAwD;AACnE,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,gBAAgB,MAAM,KAAK,iBAC9B;AAAA,UACC,KAAK,MAAM,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC;AAAA,UACxC,KAAK;AAAA,QACP,EACC,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,cAAM,OAAO,cAAc,KAAK,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC;AAC7D,cAAM,iBAAiB,OAAO,KAAK,KAAK,MAAM,MAAM,EAAE,OAAO;AAE7D,eAAO,KAAK;AAAA,UACV,CAAC,cAAc,sBAAsB,WAAW,cAAc;AAAA,QAChE;AAAA,MACF;AAAA,MACA,CAAC,UACC,YAAY,OAAO,qBAAqB;AAAA,QACtC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AC5DA,IAAAC,iBAAwC;AAQjC,IAAM,wBAAN,cAEG,kBAAgC;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,MACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAEX;AACA,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,kBAAkB,sBAAsB,KAAK,MAAM,KAAK,KAAK;AAEnE,cAAM,gBAAgB,MAAM,KAAK,iBAC9B,aAAgB,eAAoB,EACpC,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,eAAO,cAAc;AAAA,MACvB;AAAA,MACA,CAAC,UACC,YAAY,OAAO,gBAAgB;AAAA,QACjC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AC1DA,IAAAC,iBAAwC;AAQjC,IAAM,wBAAN,cAEG,kBAAqB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACE,kBACA,MACA,OACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,mBAAmB;AACxB,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAa,UAAsD;AACjE,WAAO,UAAM;AAAA,MACX,YAAY;AACV,cAAM,gBAAgB,MAAM,KAAK,iBAC9B,aAAa,UAAU,KAAK,IAAI,GAAG,KAAK,KAAK,EAC7C,MAAM,KAAK,aAAa,CAAC;AAE5B,YAAI,cAAc,SAAS;AAGzB,gBAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,OAAO;AAGrD,gBAAM,OAAO,cAAc,QAAQ;AACnC,gBAAM;AAAA,QACR;AAEA,cAAM,OAAO,UAAU,cAAc,IAAI;AACzC,cAAM,iBAAiB,OAAO,KAAK,KAAK,MAAM,MAAM,EAAE,OAAO;AAE7D,eAAO,sBAAsB,MAAM,cAAc;AAAA,MACnD;AAAA,MACA,CAAC,UACC,YAAY,OAAO,gBAAgB;AAAA,QACjC,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;ANpBO,SAAS,kBACd,QAC2B;AAC3B,QAAM,EAAE,kBAAkB,QAAQ,IAAI;AAEtC,SAAO;AAAA,IACL,aACE,MACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,kBACE,OACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,aACE,MACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,kBACE,OACA,OACA;AACA,aAAO,IAAI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["FfiProtectError","import_result","import_result","import_result"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as EncryptionClient } from '../client-
|
|
2
|
-
import { D as Decrypted,
|
|
1
|
+
import { E as EncryptionClient } from '../client-Bf0Xw2xo.cjs';
|
|
2
|
+
import { D as Decrypted, k as EncryptedValue, f as EncryptedTable, g as EncryptedTableColumn } from '../types-public-0CzBV45X.cjs';
|
|
3
3
|
import { ProtectErrorCode } from '@cipherstash/protect-ffi';
|
|
4
4
|
import { Result } from '@byteslice/result';
|
|
5
5
|
import '../index-9-Ya3fDK.cjs';
|
|
@@ -45,7 +45,7 @@ declare class BulkDecryptModelsOperation<T extends Record<string, unknown>> exte
|
|
|
45
45
|
private encryptionClient;
|
|
46
46
|
private items;
|
|
47
47
|
private table;
|
|
48
|
-
constructor(encryptionClient: EncryptionClient, items: Record<string, EncryptedValue | unknown>[], table:
|
|
48
|
+
constructor(encryptionClient: EncryptionClient, items: Record<string, EncryptedValue | unknown>[], table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
49
49
|
execute(): Promise<Result<Decrypted<T>[], EncryptedDynamoDBError>>;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ declare class BulkEncryptModelsOperation<T extends Record<string, unknown>> exte
|
|
|
53
53
|
private encryptionClient;
|
|
54
54
|
private items;
|
|
55
55
|
private table;
|
|
56
|
-
constructor(encryptionClient: EncryptionClient, items: T[], table:
|
|
56
|
+
constructor(encryptionClient: EncryptionClient, items: T[], table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
57
57
|
execute(): Promise<Result<T[], EncryptedDynamoDBError>>;
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -61,7 +61,7 @@ declare class DecryptModelOperation<T extends Record<string, unknown>> extends D
|
|
|
61
61
|
private encryptionClient;
|
|
62
62
|
private item;
|
|
63
63
|
private table;
|
|
64
|
-
constructor(encryptionClient: EncryptionClient, item: Record<string, EncryptedValue | unknown>, table:
|
|
64
|
+
constructor(encryptionClient: EncryptionClient, item: Record<string, EncryptedValue | unknown>, table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
65
65
|
execute(): Promise<Result<Decrypted<T>, EncryptedDynamoDBError>>;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -69,7 +69,7 @@ declare class EncryptModelOperation<T extends Record<string, unknown>> extends D
|
|
|
69
69
|
private encryptionClient;
|
|
70
70
|
private item;
|
|
71
71
|
private table;
|
|
72
|
-
constructor(encryptionClient: EncryptionClient, item: T, table:
|
|
72
|
+
constructor(encryptionClient: EncryptionClient, item: T, table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
73
73
|
execute(): Promise<Result<T, EncryptedDynamoDBError>>;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -87,10 +87,10 @@ interface EncryptedDynamoDBError extends Error {
|
|
|
87
87
|
details?: Record<string, unknown>;
|
|
88
88
|
}
|
|
89
89
|
interface EncryptedDynamoDBInstance {
|
|
90
|
-
encryptModel<T extends Record<string, unknown>>(item: T, table:
|
|
91
|
-
bulkEncryptModels<T extends Record<string, unknown>>(items: T[], table:
|
|
92
|
-
decryptModel<T extends Record<string, unknown>>(item: Record<string, EncryptedValue | unknown>, table:
|
|
93
|
-
bulkDecryptModels<T extends Record<string, unknown>>(items: Record<string, EncryptedValue | unknown>[], table:
|
|
90
|
+
encryptModel<T extends Record<string, unknown>>(item: T, table: EncryptedTable<EncryptedTableColumn>): EncryptModelOperation<T>;
|
|
91
|
+
bulkEncryptModels<T extends Record<string, unknown>>(items: T[], table: EncryptedTable<EncryptedTableColumn>): BulkEncryptModelsOperation<T>;
|
|
92
|
+
decryptModel<T extends Record<string, unknown>>(item: Record<string, EncryptedValue | unknown>, table: EncryptedTable<EncryptedTableColumn>): DecryptModelOperation<T>;
|
|
93
|
+
bulkDecryptModels<T extends Record<string, unknown>>(items: Record<string, EncryptedValue | unknown>[], table: EncryptedTable<EncryptedTableColumn>): BulkDecryptModelsOperation<T>;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
package/dist/dynamodb/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as EncryptionClient } from '../client-
|
|
2
|
-
import { D as Decrypted,
|
|
1
|
+
import { E as EncryptionClient } from '../client-Kfp8OsPB.js';
|
|
2
|
+
import { D as Decrypted, k as EncryptedValue, f as EncryptedTable, g as EncryptedTableColumn } from '../types-public-0CzBV45X.js';
|
|
3
3
|
import { ProtectErrorCode } from '@cipherstash/protect-ffi';
|
|
4
4
|
import { Result } from '@byteslice/result';
|
|
5
5
|
import '../index-9-Ya3fDK.js';
|
|
@@ -45,7 +45,7 @@ declare class BulkDecryptModelsOperation<T extends Record<string, unknown>> exte
|
|
|
45
45
|
private encryptionClient;
|
|
46
46
|
private items;
|
|
47
47
|
private table;
|
|
48
|
-
constructor(encryptionClient: EncryptionClient, items: Record<string, EncryptedValue | unknown>[], table:
|
|
48
|
+
constructor(encryptionClient: EncryptionClient, items: Record<string, EncryptedValue | unknown>[], table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
49
49
|
execute(): Promise<Result<Decrypted<T>[], EncryptedDynamoDBError>>;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ declare class BulkEncryptModelsOperation<T extends Record<string, unknown>> exte
|
|
|
53
53
|
private encryptionClient;
|
|
54
54
|
private items;
|
|
55
55
|
private table;
|
|
56
|
-
constructor(encryptionClient: EncryptionClient, items: T[], table:
|
|
56
|
+
constructor(encryptionClient: EncryptionClient, items: T[], table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
57
57
|
execute(): Promise<Result<T[], EncryptedDynamoDBError>>;
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -61,7 +61,7 @@ declare class DecryptModelOperation<T extends Record<string, unknown>> extends D
|
|
|
61
61
|
private encryptionClient;
|
|
62
62
|
private item;
|
|
63
63
|
private table;
|
|
64
|
-
constructor(encryptionClient: EncryptionClient, item: Record<string, EncryptedValue | unknown>, table:
|
|
64
|
+
constructor(encryptionClient: EncryptionClient, item: Record<string, EncryptedValue | unknown>, table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
65
65
|
execute(): Promise<Result<Decrypted<T>, EncryptedDynamoDBError>>;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -69,7 +69,7 @@ declare class EncryptModelOperation<T extends Record<string, unknown>> extends D
|
|
|
69
69
|
private encryptionClient;
|
|
70
70
|
private item;
|
|
71
71
|
private table;
|
|
72
|
-
constructor(encryptionClient: EncryptionClient, item: T, table:
|
|
72
|
+
constructor(encryptionClient: EncryptionClient, item: T, table: EncryptedTable<EncryptedTableColumn>, options?: DynamoDBOperationOptions);
|
|
73
73
|
execute(): Promise<Result<T, EncryptedDynamoDBError>>;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -87,10 +87,10 @@ interface EncryptedDynamoDBError extends Error {
|
|
|
87
87
|
details?: Record<string, unknown>;
|
|
88
88
|
}
|
|
89
89
|
interface EncryptedDynamoDBInstance {
|
|
90
|
-
encryptModel<T extends Record<string, unknown>>(item: T, table:
|
|
91
|
-
bulkEncryptModels<T extends Record<string, unknown>>(items: T[], table:
|
|
92
|
-
decryptModel<T extends Record<string, unknown>>(item: Record<string, EncryptedValue | unknown>, table:
|
|
93
|
-
bulkDecryptModels<T extends Record<string, unknown>>(items: Record<string, EncryptedValue | unknown>[], table:
|
|
90
|
+
encryptModel<T extends Record<string, unknown>>(item: T, table: EncryptedTable<EncryptedTableColumn>): EncryptModelOperation<T>;
|
|
91
|
+
bulkEncryptModels<T extends Record<string, unknown>>(items: T[], table: EncryptedTable<EncryptedTableColumn>): BulkEncryptModelsOperation<T>;
|
|
92
|
+
decryptModel<T extends Record<string, unknown>>(item: Record<string, EncryptedValue | unknown>, table: EncryptedTable<EncryptedTableColumn>): DecryptModelOperation<T>;
|
|
93
|
+
bulkDecryptModels<T extends Record<string, unknown>>(items: Record<string, EncryptedValue | unknown>[], table: EncryptedTable<EncryptedTableColumn>): BulkDecryptModelsOperation<T>;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/dynamodb/operations/bulk-decrypt-models.ts","../../src/dynamodb/helpers.ts","../../src/dynamodb/operations/base-operation.ts","../../src/dynamodb/operations/bulk-encrypt-models.ts","../../src/dynamodb/operations/decrypt-model.ts","../../src/dynamodb/operations/encrypt-model.ts","../../src/dynamodb/index.ts"],"sourcesContent":["import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkDecryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>[]> {\n private encryptionClient: EncryptionClient\n private items: Record<string, EncryptedValue | unknown>[]\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: Record<string, EncryptedValue | unknown>[],\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>[], EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const itemsWithEqlPayloads = this.items.map((item) =>\n toItemWithEqlPayloads(item, this.table),\n )\n\n const decryptResult = await this.encryptionClient\n .bulkDecryptModels<T>(itemsWithEqlPayloads as T[])\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'bulkDecryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport type { ProtectErrorCode } from '@cipherstash/protect-ffi'\nimport { ProtectError as FfiProtectError } from '@cipherstash/protect-ffi'\nimport type { EncryptedDynamoDBError } from './types'\n\nexport const ciphertextAttrSuffix = '__source'\nexport const searchTermAttrSuffix = '__hmac'\n\nexport class EncryptedDynamoDBErrorImpl\n extends Error\n implements EncryptedDynamoDBError\n{\n constructor(\n message: string,\n public code: ProtectErrorCode | 'DYNAMODB_ENCRYPTION_ERROR',\n public details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'EncryptedDynamoDBError'\n }\n}\n\nexport function handleError(\n error: unknown,\n context: string,\n options?: {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n },\n): EncryptedDynamoDBError {\n // Preserve FFI error code if available, otherwise use generic DynamoDB error code\n // Check for FfiProtectError instance or plain error objects with code property\n const errorObj = error as Record<string, unknown>\n const errorCode =\n error instanceof FfiProtectError\n ? error.code\n : errorObj &&\n typeof errorObj === 'object' &&\n 'code' in errorObj &&\n typeof errorObj.code === 'string'\n ? (errorObj.code as ProtectErrorCode)\n : 'DYNAMODB_ENCRYPTION_ERROR'\n\n const errorMessage =\n error instanceof Error\n ? error.message\n : errorObj && typeof errorObj.message === 'string'\n ? errorObj.message\n : String(error)\n\n const dynamoError = new EncryptedDynamoDBErrorImpl(errorMessage, errorCode, {\n context,\n })\n\n if (options?.errorHandler) {\n options.errorHandler(dynamoError)\n }\n\n if (options?.logger) {\n options.logger.error(`Error in ${context}`, dynamoError)\n }\n\n return dynamoError\n}\n\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => deepClone(item)) as unknown as T\n }\n\n return Object.entries(obj as Record<string, unknown>).reduce(\n (acc, [key, value]) => ({\n // biome-ignore lint/performance/noAccumulatingSpread: TODO later\n ...acc,\n [key]: deepClone(value),\n }),\n {} as T,\n )\n}\n\nexport function toEncryptedDynamoItem(\n encrypted: Record<string, unknown>,\n encryptedAttrs: string[],\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Handle encrypted payload\n if (\n encryptedAttrs.includes(attrName) ||\n (isNested &&\n typeof attrValue === 'object' &&\n 'c' in (attrValue as object))\n ) {\n const encryptPayload = attrValue as EncryptedValue\n if (encryptPayload?.c) {\n const result: Record<string, unknown> = {}\n if (encryptPayload.hm) {\n result[`${attrName}${searchTermAttrSuffix}`] = encryptPayload.hm\n }\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.c\n return result\n }\n\n if (encryptPayload?.sv) {\n const result: Record<string, unknown> = {}\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.sv\n return result\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(encrypted).reduce(\n (putItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, putItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n\nexport function toItemWithEqlPayloads(\n decrypted: Record<string, EncryptedValue | unknown>,\n encryptionSchema: ProtectTable<ProtectTableColumn>,\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Skip HMAC fields\n if (attrName.endsWith(searchTermAttrSuffix)) {\n return {}\n }\n\n const encryptConfig = encryptionSchema.build()\n const encryptedAttrs = Object.keys(encryptConfig.columns)\n const columnName = attrName.slice(0, -ciphertextAttrSuffix.length)\n\n // Handle encrypted payload\n if (\n attrName.endsWith(ciphertextAttrSuffix) &&\n (encryptedAttrs.includes(columnName) || isNested)\n ) {\n const i = { c: columnName, t: encryptConfig.tableName }\n const v = 2\n\n // Nested values are not searchable, so we can just return the standard EQL payload.\n // Worth noting, that encryptConfig.columns[columnName] will be undefined if isNested is true.\n if (\n !isNested &&\n encryptConfig.columns[columnName].cast_as === 'json' &&\n encryptConfig.columns[columnName].indexes.ste_vec\n ) {\n return {\n [columnName]: {\n i,\n v,\n k: 'sv',\n sv: attrValue,\n },\n }\n }\n\n return {\n [columnName]: {\n i,\n v,\n k: 'ct',\n c: attrValue,\n },\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(decrypted).reduce(\n (formattedItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, formattedItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n","import type { Result } from '@byteslice/result'\nimport type { EncryptedDynamoDBError } from '../types'\n\nexport type AuditConfig = {\n metadata?: Record<string, unknown>\n}\n\nexport type AuditData = {\n metadata?: Record<string, unknown>\n}\n\nexport type DynamoDBOperationOptions = {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n}\n\nexport abstract class DynamoDBOperation<T> {\n protected auditMetadata?: Record<string, unknown>\n protected logger?: DynamoDBOperationOptions['logger']\n protected errorHandler?: DynamoDBOperationOptions['errorHandler']\n\n constructor(options?: DynamoDBOperationOptions) {\n this.logger = options?.logger\n this.errorHandler = options?.errorHandler\n }\n\n /**\n * Attach audit metadata to this operation. Can be chained.\n */\n audit(config: AuditConfig): this {\n this.auditMetadata = config.metadata\n return this\n }\n\n /**\n * Get the audit metadata for this operation.\n */\n protected getAuditData(): AuditData {\n return {\n metadata: this.auditMetadata,\n }\n }\n\n /**\n * Execute the operation and return a Result\n */\n abstract execute(): Promise<Result<T, EncryptedDynamoDBError>>\n\n /**\n * Make the operation thenable\n */\n public then<TResult1 = Result<T, EncryptedDynamoDBError>, TResult2 = never>(\n onfulfilled?:\n | ((\n value: Result<T, EncryptedDynamoDBError>,\n ) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkEncryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T[]> {\n private encryptionClient: EncryptionClient\n private items: T[]\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: T[],\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<Result<T[], EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .bulkEncryptModels(\n this.items.map((item) => deepClone(item)),\n this.table,\n )\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = encryptResult.data.map((item) => deepClone(item))\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return data.map(\n (encrypted) => toEncryptedDynamoItem(encrypted, encryptedAttrs) as T,\n )\n },\n (error) =>\n handleError(error, 'bulkEncryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class DecryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>> {\n private encryptionClient: EncryptionClient\n private item: Record<string, EncryptedValue | unknown>\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: Record<string, EncryptedValue | unknown>,\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>, EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const withEqlPayloads = toItemWithEqlPayloads(this.item, this.table)\n\n const decryptResult = await this.encryptionClient\n .decryptModel<T>(withEqlPayloads as T)\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'decryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption/ffi'\nimport type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class EncryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T> {\n private encryptionClient: EncryptionClient\n private item: T\n private table: ProtectTable<ProtectTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: T,\n table: ProtectTable<ProtectTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<Result<T, EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .encryptModel(deepClone(this.item), this.table)\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = deepClone(encryptResult.data)\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return toEncryptedDynamoItem(data, encryptedAttrs) as T\n },\n (error) =>\n handleError(error, 'encryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { ProtectTable, ProtectTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport { BulkDecryptModelsOperation } from './operations/bulk-decrypt-models'\nimport { BulkEncryptModelsOperation } from './operations/bulk-encrypt-models'\nimport { DecryptModelOperation } from './operations/decrypt-model'\nimport { EncryptModelOperation } from './operations/encrypt-model'\nimport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBInstance,\n} from './types'\n\n/**\n * Create an encrypted DynamoDB helper bound to an `EncryptionClient`.\n *\n * Returns an object with `encryptModel`, `decryptModel`, `bulkEncryptModels`,\n * and `bulkDecryptModels` methods that transparently encrypt/decrypt DynamoDB\n * items according to the provided table schema.\n *\n * @param config - Configuration containing the `encryptionClient` and optional\n * logging / error-handling callbacks.\n * @returns An {@link EncryptedDynamoDBInstance} with encrypt/decrypt operations.\n *\n * @example\n * ```typescript\n * import { Encryption } from \"@cipherstash/stack\"\n * import { encryptedDynamoDB } from \"@cipherstash/stack/dynamodb\"\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const client = await Encryption({ schemas: [users] })\n * const dynamo = encryptedDynamoDB({ encryptionClient: client })\n *\n * const encrypted = await dynamo.encryptModel({ email: \"a@b.com\" }, users)\n * ```\n */\nexport function encryptedDynamoDB(\n config: EncryptedDynamoDBConfig,\n): EncryptedDynamoDBInstance {\n const { encryptionClient, options } = config\n\n return {\n encryptModel<T extends Record<string, unknown>>(\n item: T,\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new EncryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkEncryptModels<T extends Record<string, unknown>>(\n items: T[],\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new BulkEncryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n\n decryptModel<T extends Record<string, unknown>>(\n item: Record<string, EncryptedValue | unknown>,\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new DecryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkDecryptModels<T extends Record<string, unknown>>(\n items: Record<string, EncryptedValue | unknown>[],\n table: ProtectTable<ProtectTableColumn>,\n ) {\n return new BulkDecryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n }\n}\n\nexport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBError,\n EncryptedDynamoDBInstance,\n} from './types'\n"],"mappings":"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","names":["withResult","withResult","withResult","withResult","withResult","withResult"]}
|
|
1
|
+
{"version":3,"sources":["../../src/dynamodb/operations/bulk-decrypt-models.ts","../../src/dynamodb/helpers.ts","../../src/dynamodb/operations/base-operation.ts","../../src/dynamodb/operations/bulk-encrypt-models.ts","../../src/dynamodb/operations/decrypt-model.ts","../../src/dynamodb/operations/encrypt-model.ts","../../src/dynamodb/index.ts"],"sourcesContent":["import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkDecryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>[]> {\n private encryptionClient: EncryptionClient\n private items: Record<string, EncryptedValue | unknown>[]\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: Record<string, EncryptedValue | unknown>[],\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>[], EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const itemsWithEqlPayloads = this.items.map((item) =>\n toItemWithEqlPayloads(item, this.table),\n )\n\n const decryptResult = await this.encryptionClient\n .bulkDecryptModels<T>(itemsWithEqlPayloads as T[])\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'bulkDecryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport type { ProtectErrorCode } from '@cipherstash/protect-ffi'\nimport { ProtectError as FfiProtectError } from '@cipherstash/protect-ffi'\nimport type { EncryptedDynamoDBError } from './types'\n\nexport const ciphertextAttrSuffix = '__source'\nexport const searchTermAttrSuffix = '__hmac'\n\nexport class EncryptedDynamoDBErrorImpl\n extends Error\n implements EncryptedDynamoDBError\n{\n constructor(\n message: string,\n public code: ProtectErrorCode | 'DYNAMODB_ENCRYPTION_ERROR',\n public details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'EncryptedDynamoDBError'\n }\n}\n\nexport function handleError(\n error: unknown,\n context: string,\n options?: {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n },\n): EncryptedDynamoDBError {\n // Preserve FFI error code if available, otherwise use generic DynamoDB error code\n // Check for FfiProtectError instance or plain error objects with code property\n const errorObj = error as Record<string, unknown>\n const errorCode =\n error instanceof FfiProtectError\n ? error.code\n : errorObj &&\n typeof errorObj === 'object' &&\n 'code' in errorObj &&\n typeof errorObj.code === 'string'\n ? (errorObj.code as ProtectErrorCode)\n : 'DYNAMODB_ENCRYPTION_ERROR'\n\n const errorMessage =\n error instanceof Error\n ? error.message\n : errorObj && typeof errorObj.message === 'string'\n ? errorObj.message\n : String(error)\n\n const dynamoError = new EncryptedDynamoDBErrorImpl(errorMessage, errorCode, {\n context,\n })\n\n if (options?.errorHandler) {\n options.errorHandler(dynamoError)\n }\n\n if (options?.logger) {\n options.logger.error(`Error in ${context}`, dynamoError)\n }\n\n return dynamoError\n}\n\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => deepClone(item)) as unknown as T\n }\n\n return Object.entries(obj as Record<string, unknown>).reduce(\n (acc, [key, value]) => ({\n // biome-ignore lint/performance/noAccumulatingSpread: TODO later\n ...acc,\n [key]: deepClone(value),\n }),\n {} as T,\n )\n}\n\nexport function toEncryptedDynamoItem(\n encrypted: Record<string, unknown>,\n encryptedAttrs: string[],\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Handle encrypted payload\n if (\n encryptedAttrs.includes(attrName) ||\n (isNested &&\n typeof attrValue === 'object' &&\n 'c' in (attrValue as object))\n ) {\n const encryptPayload = attrValue as EncryptedValue\n if (encryptPayload?.c) {\n const result: Record<string, unknown> = {}\n if (encryptPayload.hm) {\n result[`${attrName}${searchTermAttrSuffix}`] = encryptPayload.hm\n }\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.c\n return result\n }\n\n if (encryptPayload?.sv) {\n const result: Record<string, unknown> = {}\n result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.sv\n return result\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(encrypted).reduce(\n (putItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, putItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n\nexport function toItemWithEqlPayloads(\n decrypted: Record<string, EncryptedValue | unknown>,\n encryptionSchema: EncryptedTable<EncryptedTableColumn>,\n): Record<string, unknown> {\n function processValue(\n attrName: string,\n attrValue: unknown,\n isNested: boolean,\n ): Record<string, unknown> {\n if (attrValue === null || attrValue === undefined) {\n return { [attrName]: attrValue }\n }\n\n // Skip HMAC fields\n if (attrName.endsWith(searchTermAttrSuffix)) {\n return {}\n }\n\n const encryptConfig = encryptionSchema.build()\n const encryptedAttrs = Object.keys(encryptConfig.columns)\n const columnName = attrName.slice(0, -ciphertextAttrSuffix.length)\n\n // Handle encrypted payload\n if (\n attrName.endsWith(ciphertextAttrSuffix) &&\n (encryptedAttrs.includes(columnName) || isNested)\n ) {\n const i = { c: columnName, t: encryptConfig.tableName }\n const v = 2\n\n // Nested values are not searchable, so we can just return the standard EQL payload.\n // Worth noting, that encryptConfig.columns[columnName] will be undefined if isNested is true.\n if (\n !isNested &&\n encryptConfig.columns[columnName].cast_as === 'json' &&\n encryptConfig.columns[columnName].indexes.ste_vec\n ) {\n return {\n [columnName]: {\n i,\n v,\n k: 'sv',\n sv: attrValue,\n },\n }\n }\n\n return {\n [columnName]: {\n i,\n v,\n k: 'ct',\n c: attrValue,\n },\n }\n }\n\n // Handle nested objects recursively\n if (typeof attrValue === 'object' && !Array.isArray(attrValue)) {\n const nestedResult = Object.entries(\n attrValue as Record<string, unknown>,\n ).reduce(\n (acc, [key, val]) => {\n const processed = processValue(key, val, true)\n return Object.assign({}, acc, processed)\n },\n {} as Record<string, unknown>,\n )\n return { [attrName]: nestedResult }\n }\n\n // Handle non-encrypted values\n return { [attrName]: attrValue }\n }\n\n return Object.entries(decrypted).reduce(\n (formattedItem, [attrName, attrValue]) => {\n const processed = processValue(attrName, attrValue, false)\n return Object.assign({}, formattedItem, processed)\n },\n {} as Record<string, unknown>,\n )\n}\n","import type { Result } from '@byteslice/result'\nimport type { EncryptedDynamoDBError } from '../types'\n\nexport type AuditConfig = {\n metadata?: Record<string, unknown>\n}\n\nexport type AuditData = {\n metadata?: Record<string, unknown>\n}\n\nexport type DynamoDBOperationOptions = {\n logger?: {\n error: (message: string, error: Error) => void\n }\n errorHandler?: (error: EncryptedDynamoDBError) => void\n}\n\nexport abstract class DynamoDBOperation<T> {\n protected auditMetadata?: Record<string, unknown>\n protected logger?: DynamoDBOperationOptions['logger']\n protected errorHandler?: DynamoDBOperationOptions['errorHandler']\n\n constructor(options?: DynamoDBOperationOptions) {\n this.logger = options?.logger\n this.errorHandler = options?.errorHandler\n }\n\n /**\n * Attach audit metadata to this operation. Can be chained.\n */\n audit(config: AuditConfig): this {\n this.auditMetadata = config.metadata\n return this\n }\n\n /**\n * Get the audit metadata for this operation.\n */\n protected getAuditData(): AuditData {\n return {\n metadata: this.auditMetadata,\n }\n }\n\n /**\n * Execute the operation and return a Result\n */\n abstract execute(): Promise<Result<T, EncryptedDynamoDBError>>\n\n /**\n * Make the operation thenable\n */\n public then<TResult1 = Result<T, EncryptedDynamoDBError>, TResult2 = never>(\n onfulfilled?:\n | ((\n value: Result<T, EncryptedDynamoDBError>,\n ) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class BulkEncryptModelsOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T[]> {\n private encryptionClient: EncryptionClient\n private items: T[]\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n items: T[],\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.items = items\n this.table = table\n }\n\n public async execute(): Promise<Result<T[], EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .bulkEncryptModels(\n this.items.map((item) => deepClone(item)),\n this.table,\n )\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = encryptResult.data.map((item) => deepClone(item))\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return data.map(\n (encrypted) => toEncryptedDynamoItem(encrypted, encryptedAttrs) as T,\n )\n },\n (error) =>\n handleError(error, 'bulkEncryptModels', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { Decrypted, EncryptedValue } from '@/types'\nimport { type Result, withResult } from '@byteslice/result'\nimport { handleError, toItemWithEqlPayloads } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class DecryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<Decrypted<T>> {\n private encryptionClient: EncryptionClient\n private item: Record<string, EncryptedValue | unknown>\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: Record<string, EncryptedValue | unknown>,\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<\n Result<Decrypted<T>, EncryptedDynamoDBError>\n > {\n return await withResult(\n async () => {\n const withEqlPayloads = toItemWithEqlPayloads(this.item, this.table)\n\n const decryptResult = await this.encryptionClient\n .decryptModel<T>(withEqlPayloads as T)\n .audit(this.getAuditData())\n\n if (decryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(decryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = decryptResult.failure.code\n throw error\n }\n\n return decryptResult.data\n },\n (error) =>\n handleError(error, 'decryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptionClient } from '@/encryption'\nimport type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport { type Result, withResult } from '@byteslice/result'\nimport { deepClone, handleError, toEncryptedDynamoItem } from '../helpers'\nimport type { EncryptedDynamoDBError } from '../types'\nimport {\n DynamoDBOperation,\n type DynamoDBOperationOptions,\n} from './base-operation'\n\nexport class EncryptModelOperation<\n T extends Record<string, unknown>,\n> extends DynamoDBOperation<T> {\n private encryptionClient: EncryptionClient\n private item: T\n private table: EncryptedTable<EncryptedTableColumn>\n\n constructor(\n encryptionClient: EncryptionClient,\n item: T,\n table: EncryptedTable<EncryptedTableColumn>,\n options?: DynamoDBOperationOptions,\n ) {\n super(options)\n this.encryptionClient = encryptionClient\n this.item = item\n this.table = table\n }\n\n public async execute(): Promise<Result<T, EncryptedDynamoDBError>> {\n return await withResult(\n async () => {\n const encryptResult = await this.encryptionClient\n .encryptModel(deepClone(this.item), this.table)\n .audit(this.getAuditData())\n\n if (encryptResult.failure) {\n // Create an Error object that preserves the FFI error code\n // This is necessary because withResult's ensureError wraps non-Error objects\n const error = new Error(encryptResult.failure.message) as Error & {\n code?: string\n }\n error.code = encryptResult.failure.code\n throw error\n }\n\n const data = deepClone(encryptResult.data)\n const encryptedAttrs = Object.keys(this.table.build().columns)\n\n return toEncryptedDynamoItem(data, encryptedAttrs) as T\n },\n (error) =>\n handleError(error, 'encryptModel', {\n logger: this.logger,\n errorHandler: this.errorHandler,\n }),\n )\n }\n}\n","import type { EncryptedTable, EncryptedTableColumn } from '@/schema'\nimport type { EncryptedValue } from '@/types'\nimport { BulkDecryptModelsOperation } from './operations/bulk-decrypt-models'\nimport { BulkEncryptModelsOperation } from './operations/bulk-encrypt-models'\nimport { DecryptModelOperation } from './operations/decrypt-model'\nimport { EncryptModelOperation } from './operations/encrypt-model'\nimport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBInstance,\n} from './types'\n\n/**\n * Create an encrypted DynamoDB helper bound to an `EncryptionClient`.\n *\n * Returns an object with `encryptModel`, `decryptModel`, `bulkEncryptModels`,\n * and `bulkDecryptModels` methods that transparently encrypt/decrypt DynamoDB\n * items according to the provided table schema.\n *\n * @param config - Configuration containing the `encryptionClient` and optional\n * logging / error-handling callbacks.\n * @returns An {@link EncryptedDynamoDBInstance} with encrypt/decrypt operations.\n *\n * @example\n * ```typescript\n * import { Encryption } from \"@cipherstash/stack\"\n * import { encryptedDynamoDB } from \"@cipherstash/stack/dynamodb\"\n * import { encryptedTable, encryptedColumn } from \"@cipherstash/stack/schema\"\n *\n * const users = encryptedTable(\"users\", {\n * email: encryptedColumn(\"email\").equality(),\n * })\n *\n * const client = await Encryption({ schemas: [users] })\n * const dynamo = encryptedDynamoDB({ encryptionClient: client })\n *\n * const encrypted = await dynamo.encryptModel({ email: \"a@b.com\" }, users)\n * ```\n */\nexport function encryptedDynamoDB(\n config: EncryptedDynamoDBConfig,\n): EncryptedDynamoDBInstance {\n const { encryptionClient, options } = config\n\n return {\n encryptModel<T extends Record<string, unknown>>(\n item: T,\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new EncryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkEncryptModels<T extends Record<string, unknown>>(\n items: T[],\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new BulkEncryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n\n decryptModel<T extends Record<string, unknown>>(\n item: Record<string, EncryptedValue | unknown>,\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new DecryptModelOperation<T>(\n encryptionClient,\n item,\n table,\n options,\n )\n },\n\n bulkDecryptModels<T extends Record<string, unknown>>(\n items: Record<string, EncryptedValue | unknown>[],\n table: EncryptedTable<EncryptedTableColumn>,\n ) {\n return new BulkDecryptModelsOperation<T>(\n encryptionClient,\n items,\n table,\n options,\n )\n },\n }\n}\n\nexport type {\n EncryptedDynamoDBConfig,\n EncryptedDynamoDBError,\n EncryptedDynamoDBInstance,\n} from './types'\n"],"mappings":"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","names":["withResult","withResult","withResult","withResult","withResult","withResult"]}
|
package/dist/identity/index.cjs
CHANGED
|
@@ -123,7 +123,7 @@ function initStackLogger(config) {
|
|
|
123
123
|
const rates = samplingFromEnv();
|
|
124
124
|
(0, import_evlog.initLogger)({
|
|
125
125
|
env: { service: "@cipherstash/stack" },
|
|
126
|
-
enabled: config?.enabled ??
|
|
126
|
+
enabled: config?.enabled ?? !!rates,
|
|
127
127
|
pretty: config?.pretty,
|
|
128
128
|
...rates && { sampling: { rates } },
|
|
129
129
|
...config?.drain && { drain: config.drain }
|
|
@@ -136,7 +136,11 @@ function safeMessage(args) {
|
|
|
136
136
|
var logger = {
|
|
137
137
|
debug(...args) {
|
|
138
138
|
const log = (0, import_evlog.createRequestLogger)();
|
|
139
|
-
log.set({
|
|
139
|
+
log.set({
|
|
140
|
+
level: "debug",
|
|
141
|
+
source: "@cipherstash/stack",
|
|
142
|
+
message: safeMessage(args)
|
|
143
|
+
});
|
|
140
144
|
log.emit();
|
|
141
145
|
},
|
|
142
146
|
info(...args) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/identity/index.ts","../../src/errors/index.ts","../../src/utils/config/index.ts","../../src/utils/logger/index.ts"],"sourcesContent":["import { type EncryptionError, EncryptionErrorTypes } from '@/errors'\nimport { loadWorkSpaceId } from '@/utils/config'\nimport { logger } from '@/utils/logger'\nimport { type Result, withResult } from '@byteslice/result'\n\nexport type CtsRegions = 'ap-southeast-2'\n\nexport type IdentifyOptions = {\n fetchFromCts?: boolean\n}\n\nexport type CtsToken = {\n accessToken: string\n expiry: number\n}\n\nexport type Context = {\n identityClaim: string[]\n}\n\nexport type LockContextOptions = {\n context?: Context\n ctsToken?: CtsToken\n}\n\nexport type GetLockContextResponse = {\n ctsToken: CtsToken\n context: Context\n}\n\n/**\n * Manages CipherStash lock contexts for row-level access control.\n *\n * A `LockContext` ties encryption/decryption operations to an authenticated\n * user identity via CTS (CipherStash Token Service). Call {@link identify}\n * with a user's JWT to obtain a CTS token, then pass the `LockContext`\n * to `.withLockContext()` on any encrypt/decrypt operation.\n *\n * @example\n * ```typescript\n * import { LockContext } from \"@cipherstash/stack/identity\"\n *\n * const lc = new LockContext()\n * const identified = await lc.identify(userJwt)\n *\n * if (identified.failure) throw new Error(identified.failure.message)\n *\n * const result = await client\n * .encrypt(value, { column: users.email, table: users })\n * .withLockContext(identified.data)\n * ```\n */\nexport class LockContext {\n private ctsToken: CtsToken | undefined\n private workspaceId: string\n private context: Context\n\n constructor({\n context = { identityClaim: ['sub'] },\n ctsToken,\n }: LockContextOptions = {}) {\n const workspaceId = loadWorkSpaceId()\n\n if (!workspaceId) {\n throw new Error(\n 'You have not defined a workspace ID in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n if (ctsToken) {\n this.ctsToken = ctsToken\n }\n\n this.workspaceId = workspaceId\n this.context = context\n logger.debug('Successfully initialized the EQL lock context.')\n }\n\n /**\n * Exchange a user's JWT for a CTS token and bind it to this lock context.\n *\n * @param jwtToken - A valid OIDC / JWT token for the current user.\n * @returns A `Result` containing this `LockContext` (now authenticated) or an error.\n *\n * @example\n * ```typescript\n * const lc = new LockContext()\n * const result = await lc.identify(userJwt)\n * if (result.failure) {\n * console.error(\"Auth failed:\", result.failure.message)\n * }\n * ```\n */\n async identify(\n jwtToken: string,\n ): Promise<Result<LockContext, EncryptionError>> {\n const workspaceId = this.workspaceId\n\n const ctsEndpoint =\n process.env.CS_CTS_ENDPOINT ||\n 'https://ap-southeast-2.aws.auth.viturhosted.net'\n\n const ctsFetchResult = await withResult(\n () =>\n fetch(`${ctsEndpoint}/api/authorize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n workspaceId,\n oidcToken: jwtToken,\n }),\n }),\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n if (ctsFetchResult.failure) {\n return ctsFetchResult\n }\n\n const identifiedLockContext = await withResult(\n async () => {\n const ctsToken = (await ctsFetchResult.data.json()) as CtsToken\n\n if (!ctsToken.accessToken) {\n throw new Error(\n 'The response from the CipherStash API did not contain an access token. Please contact support.',\n )\n }\n\n this.ctsToken = ctsToken\n return this\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n return identifiedLockContext\n }\n\n /**\n * Retrieve the current CTS token and context for use with encryption operations.\n *\n * Must be called after {@link identify}. Returns the token/context pair that\n * `.withLockContext()` expects.\n *\n * @returns A `Result` containing the CTS token and identity context, or an error\n * if {@link identify} has not been called.\n */\n getLockContext(): Promise<Result<GetLockContextResponse, EncryptionError>> {\n return withResult(\n () => {\n if (!this.ctsToken?.accessToken || !this.ctsToken?.expiry) {\n throw new Error(\n 'The CTS token is not set. Please call identify() with a users JWT token, or pass an existing CTS token to the LockContext constructor before calling getLockContext().',\n )\n }\n\n return {\n context: this.context,\n ctsToken: this.ctsToken,\n }\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n }\n}\n","import type { ProtectErrorCode } from '@cipherstash/protect-ffi'\n\nexport const EncryptionErrorTypes = {\n ClientInitError: 'ClientInitError',\n EncryptionError: 'EncryptionError',\n DecryptionError: 'DecryptionError',\n LockContextError: 'LockContextError',\n CtsTokenError: 'CtsTokenError',\n}\n\n/**\n * Base error interface returned by all encryption operations.\n *\n * Every operation that can fail returns `Result<T, EncryptionError>`.\n * Use the `type` field to narrow to a specific error kind, or use\n * {@link StackError} for an exhaustive discriminated union.\n *\n * @example\n * ```typescript\n * const result = await client.encrypt(value, opts)\n * if (result.failure) {\n * switch (result.failure.type) {\n * case 'EncryptionError':\n * console.error('Encryption failed:', result.failure.message)\n * break\n * case 'LockContextError':\n * console.error('Lock context issue:', result.failure.message)\n * break\n * }\n * }\n * ```\n */\nexport interface EncryptionError {\n type: (typeof EncryptionErrorTypes)[keyof typeof EncryptionErrorTypes]\n message: string\n code?: ProtectErrorCode\n}\n\n// ---------------------------------------------------------------------------\n// Specific error types (discriminated union members)\n// ---------------------------------------------------------------------------\n\nexport interface ClientInitError {\n type: typeof EncryptionErrorTypes.ClientInitError\n message: string\n}\n\nexport interface EncryptionOperationError {\n type: typeof EncryptionErrorTypes.EncryptionError\n message: string\n code?: ProtectErrorCode\n}\n\nexport interface DecryptionOperationError {\n type: typeof EncryptionErrorTypes.DecryptionError\n message: string\n code?: ProtectErrorCode\n}\n\nexport interface LockContextError {\n type: typeof EncryptionErrorTypes.LockContextError\n message: string\n}\n\nexport interface CtsTokenError {\n type: typeof EncryptionErrorTypes.CtsTokenError\n message: string\n}\n\n/**\n * Discriminated union of all specific error types.\n *\n * Use `StackError` when you need exhaustive error handling via `switch` on the `type` field.\n *\n * @example\n * ```typescript\n * function handleError(error: StackError) {\n * switch (error.type) {\n * case 'ClientInitError':\n * // re-initialize client\n * break\n * case 'EncryptionError':\n * case 'DecryptionError':\n * // log and retry\n * break\n * case 'LockContextError':\n * // re-authenticate\n * break\n * case 'CtsTokenError':\n * // refresh token\n * break\n * default:\n * error satisfies never\n * }\n * }\n * ```\n */\nexport type StackError =\n | ClientInitError\n | EncryptionOperationError\n | DecryptionOperationError\n | LockContextError\n | CtsTokenError\n\n// ---------------------------------------------------------------------------\n// Error utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Safely extract an error message from an unknown thrown value.\n * Unlike `(error as Error).message`, this handles non-Error values gracefully.\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message\n if (typeof error === 'string') return error\n return String(error)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\n/**\n * A lightweight function that parses a TOML-like string\n * and returns the `workspace_crn` value found under `[auth]`.\n *\n * @param tomlString The contents of the TOML file as a string.\n * @returns The workspace_crn if found, otherwise undefined.\n */\nfunction getWorkspaceCrn(tomlString: string): string | undefined {\n let currentSection = ''\n let workspaceCrn: string | undefined\n\n const lines = tomlString.split(/\\r?\\n/)\n\n for (const line of lines) {\n const trimmedLine = line.trim()\n\n if (!trimmedLine || trimmedLine.startsWith('#')) {\n continue\n }\n\n const sectionMatch = trimmedLine.match(/^\\[([^\\]]+)\\]$/)\n if (sectionMatch) {\n currentSection = sectionMatch[1]\n continue\n }\n\n const kvMatch = trimmedLine.match(/^(\\w+)\\s*=\\s*\"([^\"]+)\"$/)\n if (kvMatch) {\n const [_, key, value] = kvMatch\n\n if (currentSection === 'auth' && key === 'workspace_crn') {\n workspaceCrn = value\n break\n }\n }\n }\n\n return workspaceCrn\n}\n\n/**\n * Extracts the workspace ID from a CRN string.\n * CRN format: crn:region.aws:ID\n *\n * @param crn The CRN string to extract from\n * @returns The workspace ID portion of the CRN\n */\nexport function extractWorkspaceIdFromCrn(crn: string): string {\n const match = crn.match(/crn:[^:]+:([^:]+)$/)\n if (!match) {\n throw new Error('Invalid CRN format')\n }\n return match[1]\n}\n\nexport function loadWorkSpaceId(suppliedCrn?: string): string {\n const configPath = path.join(process.cwd(), 'cipherstash.toml')\n\n if (suppliedCrn) {\n return extractWorkspaceIdFromCrn(suppliedCrn)\n }\n\n if (!fs.existsSync(configPath) && !process.env.CS_WORKSPACE_CRN) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n // Environment variables take precedence over config files\n if (process.env.CS_WORKSPACE_CRN) {\n return extractWorkspaceIdFromCrn(process.env.CS_WORKSPACE_CRN)\n }\n\n if (!fs.existsSync(configPath)) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n const tomlString = fs.readFileSync(configPath, 'utf8')\n const workspaceCrn = getWorkspaceCrn(tomlString)\n\n if (!workspaceCrn) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n return extractWorkspaceIdFromCrn(workspaceCrn)\n}\n","import { initLogger, createRequestLogger } from 'evlog'\nimport type { LoggerConfig } from 'evlog'\n\nexport type LoggingConfig = {\n enabled?: boolean\n pretty?: boolean\n drain?: LoggerConfig['drain']\n}\n\nfunction samplingFromEnv() {\n const env = process.env.STASH_LOG_LEVEL\n if (!env) return undefined\n const levels = ['debug', 'info', 'warn', 'error'] as const\n const idx = levels.indexOf(env as (typeof levels)[number])\n if (idx === -1) return undefined\n return Object.fromEntries(levels.map((l, i) => [l, i >= idx ? 100 : 0]))\n}\n\nlet initialized = false\n\nexport function initStackLogger(config?: LoggingConfig): void {\n if (initialized) return\n initialized = true\n const rates = samplingFromEnv()\n initLogger({\n env: { service: '@cipherstash/stack' },\n enabled: config?.enabled ?? true,\n pretty: config?.pretty,\n ...(rates && { sampling: { rates } }),\n ...(config?.drain && { drain: config.drain }),\n })\n}\n\n// Auto-init with defaults on first import\ninitStackLogger()\n\nexport { createRequestLogger }\n\n// Stringify only the first arg (the message string); drop subsequent args\n// which may contain sensitive objects (e.g. encryptConfig, plaintext).\nfunction safeMessage(args: unknown[]): string {\n return typeof args[0] === 'string' ? args[0] : ''\n}\n\n// Legacy logger for simple one-off logs (used by encryption/ffi/index.ts + identity/index.ts)\nexport const logger = {\n debug(...args: unknown[]) {\n const log = createRequestLogger()\n log.set({ level: 'debug', source: '@cipherstash/stack', message: safeMessage(args) })\n log.emit()\n },\n info(...args: unknown[]) {\n const log = createRequestLogger()\n log.set({ source: '@cipherstash/stack' })\n log.info(safeMessage(args))\n log.emit()\n },\n warn(...args: unknown[]) {\n const log = createRequestLogger()\n log.warn(safeMessage(args))\n log.emit()\n },\n error(...args: unknown[]) {\n const log = createRequestLogger()\n log.error(safeMessage(args))\n log.emit()\n },\n}\n"],"mappings":"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","names":["path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/identity/index.ts","../../src/errors/index.ts","../../src/utils/config/index.ts","../../src/utils/logger/index.ts"],"sourcesContent":["import { type EncryptionError, EncryptionErrorTypes } from '@/errors'\nimport { loadWorkSpaceId } from '@/utils/config'\nimport { logger } from '@/utils/logger'\nimport { type Result, withResult } from '@byteslice/result'\n\nexport type CtsRegions = 'ap-southeast-2'\n\nexport type IdentifyOptions = {\n fetchFromCts?: boolean\n}\n\nexport type CtsToken = {\n accessToken: string\n expiry: number\n}\n\nexport type Context = {\n identityClaim: string[]\n}\n\nexport type LockContextOptions = {\n context?: Context\n ctsToken?: CtsToken\n}\n\nexport type GetLockContextResponse = {\n ctsToken: CtsToken\n context: Context\n}\n\n/**\n * Manages CipherStash lock contexts for row-level access control.\n *\n * A `LockContext` ties encryption/decryption operations to an authenticated\n * user identity via CTS (CipherStash Token Service). Call {@link identify}\n * with a user's JWT to obtain a CTS token, then pass the `LockContext`\n * to `.withLockContext()` on any encrypt/decrypt operation.\n *\n * @example\n * ```typescript\n * import { LockContext } from \"@cipherstash/stack/identity\"\n *\n * const lc = new LockContext()\n * const identified = await lc.identify(userJwt)\n *\n * if (identified.failure) throw new Error(identified.failure.message)\n *\n * const result = await client\n * .encrypt(value, { column: users.email, table: users })\n * .withLockContext(identified.data)\n * ```\n */\nexport class LockContext {\n private ctsToken: CtsToken | undefined\n private workspaceId: string\n private context: Context\n\n constructor({\n context = { identityClaim: ['sub'] },\n ctsToken,\n }: LockContextOptions = {}) {\n const workspaceId = loadWorkSpaceId()\n\n if (!workspaceId) {\n throw new Error(\n 'You have not defined a workspace ID in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n if (ctsToken) {\n this.ctsToken = ctsToken\n }\n\n this.workspaceId = workspaceId\n this.context = context\n logger.debug('Successfully initialized the EQL lock context.')\n }\n\n /**\n * Exchange a user's JWT for a CTS token and bind it to this lock context.\n *\n * @param jwtToken - A valid OIDC / JWT token for the current user.\n * @returns A `Result` containing this `LockContext` (now authenticated) or an error.\n *\n * @example\n * ```typescript\n * const lc = new LockContext()\n * const result = await lc.identify(userJwt)\n * if (result.failure) {\n * console.error(\"Auth failed:\", result.failure.message)\n * }\n * ```\n */\n async identify(\n jwtToken: string,\n ): Promise<Result<LockContext, EncryptionError>> {\n const workspaceId = this.workspaceId\n\n const ctsEndpoint =\n process.env.CS_CTS_ENDPOINT ||\n 'https://ap-southeast-2.aws.auth.viturhosted.net'\n\n const ctsFetchResult = await withResult(\n () =>\n fetch(`${ctsEndpoint}/api/authorize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n workspaceId,\n oidcToken: jwtToken,\n }),\n }),\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n if (ctsFetchResult.failure) {\n return ctsFetchResult\n }\n\n const identifiedLockContext = await withResult(\n async () => {\n const ctsToken = (await ctsFetchResult.data.json()) as CtsToken\n\n if (!ctsToken.accessToken) {\n throw new Error(\n 'The response from the CipherStash API did not contain an access token. Please contact support.',\n )\n }\n\n this.ctsToken = ctsToken\n return this\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n return identifiedLockContext\n }\n\n /**\n * Retrieve the current CTS token and context for use with encryption operations.\n *\n * Must be called after {@link identify}. Returns the token/context pair that\n * `.withLockContext()` expects.\n *\n * @returns A `Result` containing the CTS token and identity context, or an error\n * if {@link identify} has not been called.\n */\n getLockContext(): Promise<Result<GetLockContextResponse, EncryptionError>> {\n return withResult(\n () => {\n if (!this.ctsToken?.accessToken || !this.ctsToken?.expiry) {\n throw new Error(\n 'The CTS token is not set. Please call identify() with a users JWT token, or pass an existing CTS token to the LockContext constructor before calling getLockContext().',\n )\n }\n\n return {\n context: this.context,\n ctsToken: this.ctsToken,\n }\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n }\n}\n","import type { ProtectErrorCode } from '@cipherstash/protect-ffi'\n\nexport const EncryptionErrorTypes = {\n ClientInitError: 'ClientInitError',\n EncryptionError: 'EncryptionError',\n DecryptionError: 'DecryptionError',\n LockContextError: 'LockContextError',\n CtsTokenError: 'CtsTokenError',\n}\n\n/**\n * Base error interface returned by all encryption operations.\n *\n * Every operation that can fail returns `Result<T, EncryptionError>`.\n * Use the `type` field to narrow to a specific error kind, or use\n * {@link StackError} for an exhaustive discriminated union.\n *\n * @example\n * ```typescript\n * const result = await client.encrypt(value, opts)\n * if (result.failure) {\n * switch (result.failure.type) {\n * case 'EncryptionError':\n * console.error('Encryption failed:', result.failure.message)\n * break\n * case 'LockContextError':\n * console.error('Lock context issue:', result.failure.message)\n * break\n * }\n * }\n * ```\n */\nexport interface EncryptionError {\n type: (typeof EncryptionErrorTypes)[keyof typeof EncryptionErrorTypes]\n message: string\n code?: ProtectErrorCode\n}\n\n// ---------------------------------------------------------------------------\n// Specific error types (discriminated union members)\n// ---------------------------------------------------------------------------\n\nexport interface ClientInitError {\n type: typeof EncryptionErrorTypes.ClientInitError\n message: string\n}\n\nexport interface EncryptionOperationError {\n type: typeof EncryptionErrorTypes.EncryptionError\n message: string\n code?: ProtectErrorCode\n}\n\nexport interface DecryptionOperationError {\n type: typeof EncryptionErrorTypes.DecryptionError\n message: string\n code?: ProtectErrorCode\n}\n\nexport interface LockContextError {\n type: typeof EncryptionErrorTypes.LockContextError\n message: string\n}\n\nexport interface CtsTokenError {\n type: typeof EncryptionErrorTypes.CtsTokenError\n message: string\n}\n\n/**\n * Discriminated union of all specific error types.\n *\n * Use `StackError` when you need exhaustive error handling via `switch` on the `type` field.\n *\n * @example\n * ```typescript\n * function handleError(error: StackError) {\n * switch (error.type) {\n * case 'ClientInitError':\n * // re-initialize client\n * break\n * case 'EncryptionError':\n * case 'DecryptionError':\n * // log and retry\n * break\n * case 'LockContextError':\n * // re-authenticate\n * break\n * case 'CtsTokenError':\n * // refresh token\n * break\n * default:\n * error satisfies never\n * }\n * }\n * ```\n */\nexport type StackError =\n | ClientInitError\n | EncryptionOperationError\n | DecryptionOperationError\n | LockContextError\n | CtsTokenError\n\n// ---------------------------------------------------------------------------\n// Error utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Safely extract an error message from an unknown thrown value.\n * Unlike `(error as Error).message`, this handles non-Error values gracefully.\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message\n if (typeof error === 'string') return error\n return String(error)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\n/**\n * A lightweight function that parses a TOML-like string\n * and returns the `workspace_crn` value found under `[auth]`.\n *\n * @param tomlString The contents of the TOML file as a string.\n * @returns The workspace_crn if found, otherwise undefined.\n */\nfunction getWorkspaceCrn(tomlString: string): string | undefined {\n let currentSection = ''\n let workspaceCrn: string | undefined\n\n const lines = tomlString.split(/\\r?\\n/)\n\n for (const line of lines) {\n const trimmedLine = line.trim()\n\n if (!trimmedLine || trimmedLine.startsWith('#')) {\n continue\n }\n\n const sectionMatch = trimmedLine.match(/^\\[([^\\]]+)\\]$/)\n if (sectionMatch) {\n currentSection = sectionMatch[1]\n continue\n }\n\n const kvMatch = trimmedLine.match(/^(\\w+)\\s*=\\s*\"([^\"]+)\"$/)\n if (kvMatch) {\n const [_, key, value] = kvMatch\n\n if (currentSection === 'auth' && key === 'workspace_crn') {\n workspaceCrn = value\n break\n }\n }\n }\n\n return workspaceCrn\n}\n\n/**\n * Extracts the workspace ID from a CRN string.\n * CRN format: crn:region.aws:ID\n *\n * @param crn The CRN string to extract from\n * @returns The workspace ID portion of the CRN\n */\nexport function extractWorkspaceIdFromCrn(crn: string): string {\n const match = crn.match(/crn:[^:]+:([^:]+)$/)\n if (!match) {\n throw new Error('Invalid CRN format')\n }\n return match[1]\n}\n\nexport function loadWorkSpaceId(suppliedCrn?: string): string {\n const configPath = path.join(process.cwd(), 'cipherstash.toml')\n\n if (suppliedCrn) {\n return extractWorkspaceIdFromCrn(suppliedCrn)\n }\n\n if (!fs.existsSync(configPath) && !process.env.CS_WORKSPACE_CRN) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n // Environment variables take precedence over config files\n if (process.env.CS_WORKSPACE_CRN) {\n return extractWorkspaceIdFromCrn(process.env.CS_WORKSPACE_CRN)\n }\n\n if (!fs.existsSync(configPath)) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n const tomlString = fs.readFileSync(configPath, 'utf8')\n const workspaceCrn = getWorkspaceCrn(tomlString)\n\n if (!workspaceCrn) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n return extractWorkspaceIdFromCrn(workspaceCrn)\n}\n","import { initLogger, createRequestLogger } from 'evlog'\nimport type { LoggerConfig } from 'evlog'\n\nexport type LoggingConfig = {\n enabled?: boolean\n pretty?: boolean\n drain?: LoggerConfig['drain']\n}\n\nfunction samplingFromEnv() {\n const env = process.env.STASH_LOG_LEVEL\n if (!env) return undefined\n const levels = ['debug', 'info', 'warn', 'error'] as const\n const idx = levels.indexOf(env as (typeof levels)[number])\n if (idx === -1) return undefined\n return Object.fromEntries(levels.map((l, i) => [l, i >= idx ? 100 : 0]))\n}\n\nlet initialized = false\n\nexport function initStackLogger(config?: LoggingConfig): void {\n if (initialized) return\n initialized = true\n const rates = samplingFromEnv()\n initLogger({\n env: { service: '@cipherstash/stack' },\n enabled: config?.enabled ?? !!rates,\n pretty: config?.pretty,\n ...(rates && { sampling: { rates } }),\n ...(config?.drain && { drain: config.drain }),\n })\n}\n\n// Auto-init with defaults on first import\ninitStackLogger()\n\nexport { createRequestLogger }\n\n// Stringify only the first arg (the message string); drop subsequent args\n// which may contain sensitive objects (e.g. encryptConfig, plaintext).\nfunction safeMessage(args: unknown[]): string {\n return typeof args[0] === 'string' ? args[0] : ''\n}\n\n// Legacy logger for simple one-off logs (used by encryption/ffi/index.ts + identity/index.ts)\nexport const logger = {\n debug(...args: unknown[]) {\n const log = createRequestLogger()\n log.set({\n level: 'debug',\n source: '@cipherstash/stack',\n message: safeMessage(args),\n })\n log.emit()\n },\n info(...args: unknown[]) {\n const log = createRequestLogger()\n log.set({ source: '@cipherstash/stack' })\n log.info(safeMessage(args))\n log.emit()\n },\n warn(...args: unknown[]) {\n const log = createRequestLogger()\n log.warn(safeMessage(args))\n log.emit()\n },\n error(...args: unknown[]) {\n const log = createRequestLogger()\n log.error(safeMessage(args))\n log.emit()\n },\n}\n"],"mappings":"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","names":["path","fs"]}
|