@cipherstash/stack 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +670 -0
  4. package/dist/bin/stash.js +5049 -0
  5. package/dist/bin/stash.js.map +1 -0
  6. package/dist/chunk-2GZMIJFO.js +2400 -0
  7. package/dist/chunk-2GZMIJFO.js.map +1 -0
  8. package/dist/chunk-5DCT6YU2.js +138 -0
  9. package/dist/chunk-5DCT6YU2.js.map +1 -0
  10. package/dist/chunk-7XRPN2KX.js +336 -0
  11. package/dist/chunk-7XRPN2KX.js.map +1 -0
  12. package/dist/chunk-SJ7JO4ME.js +28 -0
  13. package/dist/chunk-SJ7JO4ME.js.map +1 -0
  14. package/dist/chunk-SUYMGQBY.js +67 -0
  15. package/dist/chunk-SUYMGQBY.js.map +1 -0
  16. package/dist/client-BxJG56Ey.d.cts +647 -0
  17. package/dist/client-DtGq9dJp.d.ts +647 -0
  18. package/dist/client.cjs +347 -0
  19. package/dist/client.cjs.map +1 -0
  20. package/dist/client.d.cts +7 -0
  21. package/dist/client.d.ts +7 -0
  22. package/dist/client.js +11 -0
  23. package/dist/client.js.map +1 -0
  24. package/dist/drizzle/index.cjs +1528 -0
  25. package/dist/drizzle/index.cjs.map +1 -0
  26. package/dist/drizzle/index.d.cts +350 -0
  27. package/dist/drizzle/index.d.ts +350 -0
  28. package/dist/drizzle/index.js +1212 -0
  29. package/dist/drizzle/index.js.map +1 -0
  30. package/dist/dynamodb/index.cjs +382 -0
  31. package/dist/dynamodb/index.cjs.map +1 -0
  32. package/dist/dynamodb/index.d.cts +125 -0
  33. package/dist/dynamodb/index.d.ts +125 -0
  34. package/dist/dynamodb/index.js +355 -0
  35. package/dist/dynamodb/index.js.map +1 -0
  36. package/dist/identity/index.cjs +271 -0
  37. package/dist/identity/index.cjs.map +1 -0
  38. package/dist/identity/index.d.cts +3 -0
  39. package/dist/identity/index.d.ts +3 -0
  40. package/dist/identity/index.js +117 -0
  41. package/dist/identity/index.js.map +1 -0
  42. package/dist/index-9-Ya3fDK.d.cts +169 -0
  43. package/dist/index-9-Ya3fDK.d.ts +169 -0
  44. package/dist/index.cjs +2915 -0
  45. package/dist/index.cjs.map +1 -0
  46. package/dist/index.d.cts +22 -0
  47. package/dist/index.d.ts +22 -0
  48. package/dist/index.js +23 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/schema/index.cjs +368 -0
  51. package/dist/schema/index.cjs.map +1 -0
  52. package/dist/schema/index.d.cts +4 -0
  53. package/dist/schema/index.d.ts +4 -0
  54. package/dist/schema/index.js +23 -0
  55. package/dist/schema/index.js.map +1 -0
  56. package/dist/secrets/index.cjs +3207 -0
  57. package/dist/secrets/index.cjs.map +1 -0
  58. package/dist/secrets/index.d.cts +227 -0
  59. package/dist/secrets/index.d.ts +227 -0
  60. package/dist/secrets/index.js +323 -0
  61. package/dist/secrets/index.js.map +1 -0
  62. package/dist/supabase/index.cjs +1113 -0
  63. package/dist/supabase/index.cjs.map +1 -0
  64. package/dist/supabase/index.d.cts +144 -0
  65. package/dist/supabase/index.d.ts +144 -0
  66. package/dist/supabase/index.js +864 -0
  67. package/dist/supabase/index.js.map +1 -0
  68. package/dist/types-public-BCj1L4fi.d.cts +1013 -0
  69. package/dist/types-public-BCj1L4fi.d.ts +1013 -0
  70. package/dist/types-public.cjs +40 -0
  71. package/dist/types-public.cjs.map +1 -0
  72. package/dist/types-public.d.cts +4 -0
  73. package/dist/types-public.d.ts +4 -0
  74. package/dist/types-public.js +7 -0
  75. package/dist/types-public.js.map +1 -0
  76. package/package.json +202 -0
@@ -0,0 +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":"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","names":["FfiProtectError","import_result","import_result","import_result"]}
@@ -0,0 +1,125 @@
1
+ import { E as EncryptionClient } from '../client-BxJG56Ey.cjs';
2
+ import { D as Decrypted, i as EncryptedValue, d as ProtectTable, f as ProtectTableColumn } from '../types-public-BCj1L4fi.cjs';
3
+ import { ProtectErrorCode } from '@cipherstash/protect-ffi';
4
+ import { Result } from '@byteslice/result';
5
+ import '../index-9-Ya3fDK.cjs';
6
+ import 'zod';
7
+ import 'evlog';
8
+
9
+ type AuditConfig = {
10
+ metadata?: Record<string, unknown>;
11
+ };
12
+ type AuditData = {
13
+ metadata?: Record<string, unknown>;
14
+ };
15
+ type DynamoDBOperationOptions = {
16
+ logger?: {
17
+ error: (message: string, error: Error) => void;
18
+ };
19
+ errorHandler?: (error: EncryptedDynamoDBError) => void;
20
+ };
21
+ declare abstract class DynamoDBOperation<T> {
22
+ protected auditMetadata?: Record<string, unknown>;
23
+ protected logger?: DynamoDBOperationOptions['logger'];
24
+ protected errorHandler?: DynamoDBOperationOptions['errorHandler'];
25
+ constructor(options?: DynamoDBOperationOptions);
26
+ /**
27
+ * Attach audit metadata to this operation. Can be chained.
28
+ */
29
+ audit(config: AuditConfig): this;
30
+ /**
31
+ * Get the audit metadata for this operation.
32
+ */
33
+ protected getAuditData(): AuditData;
34
+ /**
35
+ * Execute the operation and return a Result
36
+ */
37
+ abstract execute(): Promise<Result<T, EncryptedDynamoDBError>>;
38
+ /**
39
+ * Make the operation thenable
40
+ */
41
+ then<TResult1 = Result<T, EncryptedDynamoDBError>, TResult2 = never>(onfulfilled?: ((value: Result<T, EncryptedDynamoDBError>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
42
+ }
43
+
44
+ declare class BulkDecryptModelsOperation<T extends Record<string, unknown>> extends DynamoDBOperation<Decrypted<T>[]> {
45
+ private encryptionClient;
46
+ private items;
47
+ private table;
48
+ constructor(encryptionClient: EncryptionClient, items: Record<string, EncryptedValue | unknown>[], table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
49
+ execute(): Promise<Result<Decrypted<T>[], EncryptedDynamoDBError>>;
50
+ }
51
+
52
+ declare class BulkEncryptModelsOperation<T extends Record<string, unknown>> extends DynamoDBOperation<T[]> {
53
+ private encryptionClient;
54
+ private items;
55
+ private table;
56
+ constructor(encryptionClient: EncryptionClient, items: T[], table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
57
+ execute(): Promise<Result<T[], EncryptedDynamoDBError>>;
58
+ }
59
+
60
+ declare class DecryptModelOperation<T extends Record<string, unknown>> extends DynamoDBOperation<Decrypted<T>> {
61
+ private encryptionClient;
62
+ private item;
63
+ private table;
64
+ constructor(encryptionClient: EncryptionClient, item: Record<string, EncryptedValue | unknown>, table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
65
+ execute(): Promise<Result<Decrypted<T>, EncryptedDynamoDBError>>;
66
+ }
67
+
68
+ declare class EncryptModelOperation<T extends Record<string, unknown>> extends DynamoDBOperation<T> {
69
+ private encryptionClient;
70
+ private item;
71
+ private table;
72
+ constructor(encryptionClient: EncryptionClient, item: T, table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
73
+ execute(): Promise<Result<T, EncryptedDynamoDBError>>;
74
+ }
75
+
76
+ interface EncryptedDynamoDBConfig {
77
+ encryptionClient: EncryptionClient;
78
+ options?: {
79
+ logger?: {
80
+ error: (message: string, error: Error) => void;
81
+ };
82
+ errorHandler?: (error: EncryptedDynamoDBError) => void;
83
+ };
84
+ }
85
+ interface EncryptedDynamoDBError extends Error {
86
+ code: ProtectErrorCode | 'DYNAMODB_ENCRYPTION_ERROR';
87
+ details?: Record<string, unknown>;
88
+ }
89
+ interface EncryptedDynamoDBInstance {
90
+ encryptModel<T extends Record<string, unknown>>(item: T, table: ProtectTable<ProtectTableColumn>): EncryptModelOperation<T>;
91
+ bulkEncryptModels<T extends Record<string, unknown>>(items: T[], table: ProtectTable<ProtectTableColumn>): BulkEncryptModelsOperation<T>;
92
+ decryptModel<T extends Record<string, unknown>>(item: Record<string, EncryptedValue | unknown>, table: ProtectTable<ProtectTableColumn>): DecryptModelOperation<T>;
93
+ bulkDecryptModels<T extends Record<string, unknown>>(items: Record<string, EncryptedValue | unknown>[], table: ProtectTable<ProtectTableColumn>): BulkDecryptModelsOperation<T>;
94
+ }
95
+
96
+ /**
97
+ * Create an encrypted DynamoDB helper bound to an `EncryptionClient`.
98
+ *
99
+ * Returns an object with `encryptModel`, `decryptModel`, `bulkEncryptModels`,
100
+ * and `bulkDecryptModels` methods that transparently encrypt/decrypt DynamoDB
101
+ * items according to the provided table schema.
102
+ *
103
+ * @param config - Configuration containing the `encryptionClient` and optional
104
+ * logging / error-handling callbacks.
105
+ * @returns An {@link EncryptedDynamoDBInstance} with encrypt/decrypt operations.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * import { Encryption } from "@cipherstash/stack"
110
+ * import { encryptedDynamoDB } from "@cipherstash/stack/dynamodb"
111
+ * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
112
+ *
113
+ * const users = encryptedTable("users", {
114
+ * email: encryptedColumn("email").equality(),
115
+ * })
116
+ *
117
+ * const client = await Encryption({ schemas: [users] })
118
+ * const dynamo = encryptedDynamoDB({ encryptionClient: client })
119
+ *
120
+ * const encrypted = await dynamo.encryptModel({ email: "a@b.com" }, users)
121
+ * ```
122
+ */
123
+ declare function encryptedDynamoDB(config: EncryptedDynamoDBConfig): EncryptedDynamoDBInstance;
124
+
125
+ export { type EncryptedDynamoDBConfig, type EncryptedDynamoDBError, type EncryptedDynamoDBInstance, encryptedDynamoDB };
@@ -0,0 +1,125 @@
1
+ import { E as EncryptionClient } from '../client-DtGq9dJp.js';
2
+ import { D as Decrypted, i as EncryptedValue, d as ProtectTable, f as ProtectTableColumn } from '../types-public-BCj1L4fi.js';
3
+ import { ProtectErrorCode } from '@cipherstash/protect-ffi';
4
+ import { Result } from '@byteslice/result';
5
+ import '../index-9-Ya3fDK.js';
6
+ import 'zod';
7
+ import 'evlog';
8
+
9
+ type AuditConfig = {
10
+ metadata?: Record<string, unknown>;
11
+ };
12
+ type AuditData = {
13
+ metadata?: Record<string, unknown>;
14
+ };
15
+ type DynamoDBOperationOptions = {
16
+ logger?: {
17
+ error: (message: string, error: Error) => void;
18
+ };
19
+ errorHandler?: (error: EncryptedDynamoDBError) => void;
20
+ };
21
+ declare abstract class DynamoDBOperation<T> {
22
+ protected auditMetadata?: Record<string, unknown>;
23
+ protected logger?: DynamoDBOperationOptions['logger'];
24
+ protected errorHandler?: DynamoDBOperationOptions['errorHandler'];
25
+ constructor(options?: DynamoDBOperationOptions);
26
+ /**
27
+ * Attach audit metadata to this operation. Can be chained.
28
+ */
29
+ audit(config: AuditConfig): this;
30
+ /**
31
+ * Get the audit metadata for this operation.
32
+ */
33
+ protected getAuditData(): AuditData;
34
+ /**
35
+ * Execute the operation and return a Result
36
+ */
37
+ abstract execute(): Promise<Result<T, EncryptedDynamoDBError>>;
38
+ /**
39
+ * Make the operation thenable
40
+ */
41
+ then<TResult1 = Result<T, EncryptedDynamoDBError>, TResult2 = never>(onfulfilled?: ((value: Result<T, EncryptedDynamoDBError>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
42
+ }
43
+
44
+ declare class BulkDecryptModelsOperation<T extends Record<string, unknown>> extends DynamoDBOperation<Decrypted<T>[]> {
45
+ private encryptionClient;
46
+ private items;
47
+ private table;
48
+ constructor(encryptionClient: EncryptionClient, items: Record<string, EncryptedValue | unknown>[], table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
49
+ execute(): Promise<Result<Decrypted<T>[], EncryptedDynamoDBError>>;
50
+ }
51
+
52
+ declare class BulkEncryptModelsOperation<T extends Record<string, unknown>> extends DynamoDBOperation<T[]> {
53
+ private encryptionClient;
54
+ private items;
55
+ private table;
56
+ constructor(encryptionClient: EncryptionClient, items: T[], table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
57
+ execute(): Promise<Result<T[], EncryptedDynamoDBError>>;
58
+ }
59
+
60
+ declare class DecryptModelOperation<T extends Record<string, unknown>> extends DynamoDBOperation<Decrypted<T>> {
61
+ private encryptionClient;
62
+ private item;
63
+ private table;
64
+ constructor(encryptionClient: EncryptionClient, item: Record<string, EncryptedValue | unknown>, table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
65
+ execute(): Promise<Result<Decrypted<T>, EncryptedDynamoDBError>>;
66
+ }
67
+
68
+ declare class EncryptModelOperation<T extends Record<string, unknown>> extends DynamoDBOperation<T> {
69
+ private encryptionClient;
70
+ private item;
71
+ private table;
72
+ constructor(encryptionClient: EncryptionClient, item: T, table: ProtectTable<ProtectTableColumn>, options?: DynamoDBOperationOptions);
73
+ execute(): Promise<Result<T, EncryptedDynamoDBError>>;
74
+ }
75
+
76
+ interface EncryptedDynamoDBConfig {
77
+ encryptionClient: EncryptionClient;
78
+ options?: {
79
+ logger?: {
80
+ error: (message: string, error: Error) => void;
81
+ };
82
+ errorHandler?: (error: EncryptedDynamoDBError) => void;
83
+ };
84
+ }
85
+ interface EncryptedDynamoDBError extends Error {
86
+ code: ProtectErrorCode | 'DYNAMODB_ENCRYPTION_ERROR';
87
+ details?: Record<string, unknown>;
88
+ }
89
+ interface EncryptedDynamoDBInstance {
90
+ encryptModel<T extends Record<string, unknown>>(item: T, table: ProtectTable<ProtectTableColumn>): EncryptModelOperation<T>;
91
+ bulkEncryptModels<T extends Record<string, unknown>>(items: T[], table: ProtectTable<ProtectTableColumn>): BulkEncryptModelsOperation<T>;
92
+ decryptModel<T extends Record<string, unknown>>(item: Record<string, EncryptedValue | unknown>, table: ProtectTable<ProtectTableColumn>): DecryptModelOperation<T>;
93
+ bulkDecryptModels<T extends Record<string, unknown>>(items: Record<string, EncryptedValue | unknown>[], table: ProtectTable<ProtectTableColumn>): BulkDecryptModelsOperation<T>;
94
+ }
95
+
96
+ /**
97
+ * Create an encrypted DynamoDB helper bound to an `EncryptionClient`.
98
+ *
99
+ * Returns an object with `encryptModel`, `decryptModel`, `bulkEncryptModels`,
100
+ * and `bulkDecryptModels` methods that transparently encrypt/decrypt DynamoDB
101
+ * items according to the provided table schema.
102
+ *
103
+ * @param config - Configuration containing the `encryptionClient` and optional
104
+ * logging / error-handling callbacks.
105
+ * @returns An {@link EncryptedDynamoDBInstance} with encrypt/decrypt operations.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * import { Encryption } from "@cipherstash/stack"
110
+ * import { encryptedDynamoDB } from "@cipherstash/stack/dynamodb"
111
+ * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
112
+ *
113
+ * const users = encryptedTable("users", {
114
+ * email: encryptedColumn("email").equality(),
115
+ * })
116
+ *
117
+ * const client = await Encryption({ schemas: [users] })
118
+ * const dynamo = encryptedDynamoDB({ encryptionClient: client })
119
+ *
120
+ * const encrypted = await dynamo.encryptModel({ email: "a@b.com" }, users)
121
+ * ```
122
+ */
123
+ declare function encryptedDynamoDB(config: EncryptedDynamoDBConfig): EncryptedDynamoDBInstance;
124
+
125
+ export { type EncryptedDynamoDBConfig, type EncryptedDynamoDBError, type EncryptedDynamoDBInstance, encryptedDynamoDB };
@@ -0,0 +1,355 @@
1
+ // src/dynamodb/operations/bulk-decrypt-models.ts
2
+ import { withResult } from "@byteslice/result";
3
+
4
+ // src/dynamodb/helpers.ts
5
+ import { ProtectError as FfiProtectError } from "@cipherstash/protect-ffi";
6
+ var ciphertextAttrSuffix = "__source";
7
+ var searchTermAttrSuffix = "__hmac";
8
+ var EncryptedDynamoDBErrorImpl = class extends Error {
9
+ constructor(message, code, details) {
10
+ super(message);
11
+ this.code = code;
12
+ this.details = details;
13
+ this.name = "EncryptedDynamoDBError";
14
+ }
15
+ };
16
+ function handleError(error, context, options) {
17
+ const errorObj = error;
18
+ const errorCode = error instanceof FfiProtectError ? error.code : errorObj && typeof errorObj === "object" && "code" in errorObj && typeof errorObj.code === "string" ? errorObj.code : "DYNAMODB_ENCRYPTION_ERROR";
19
+ const errorMessage = error instanceof Error ? error.message : errorObj && typeof errorObj.message === "string" ? errorObj.message : String(error);
20
+ const dynamoError = new EncryptedDynamoDBErrorImpl(errorMessage, errorCode, {
21
+ context
22
+ });
23
+ if (options?.errorHandler) {
24
+ options.errorHandler(dynamoError);
25
+ }
26
+ if (options?.logger) {
27
+ options.logger.error(`Error in ${context}`, dynamoError);
28
+ }
29
+ return dynamoError;
30
+ }
31
+ function deepClone(obj) {
32
+ if (obj === null || typeof obj !== "object") {
33
+ return obj;
34
+ }
35
+ if (Array.isArray(obj)) {
36
+ return obj.map((item) => deepClone(item));
37
+ }
38
+ return Object.entries(obj).reduce(
39
+ (acc, [key, value]) => ({
40
+ // biome-ignore lint/performance/noAccumulatingSpread: TODO later
41
+ ...acc,
42
+ [key]: deepClone(value)
43
+ }),
44
+ {}
45
+ );
46
+ }
47
+ function toEncryptedDynamoItem(encrypted, encryptedAttrs) {
48
+ function processValue(attrName, attrValue, isNested) {
49
+ if (attrValue === null || attrValue === void 0) {
50
+ return { [attrName]: attrValue };
51
+ }
52
+ if (encryptedAttrs.includes(attrName) || isNested && typeof attrValue === "object" && "c" in attrValue) {
53
+ const encryptPayload = attrValue;
54
+ if (encryptPayload?.c) {
55
+ const result = {};
56
+ if (encryptPayload.hm) {
57
+ result[`${attrName}${searchTermAttrSuffix}`] = encryptPayload.hm;
58
+ }
59
+ result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.c;
60
+ return result;
61
+ }
62
+ if (encryptPayload?.sv) {
63
+ const result = {};
64
+ result[`${attrName}${ciphertextAttrSuffix}`] = encryptPayload.sv;
65
+ return result;
66
+ }
67
+ }
68
+ if (typeof attrValue === "object" && !Array.isArray(attrValue)) {
69
+ const nestedResult = Object.entries(
70
+ attrValue
71
+ ).reduce(
72
+ (acc, [key, val]) => {
73
+ const processed = processValue(key, val, true);
74
+ return Object.assign({}, acc, processed);
75
+ },
76
+ {}
77
+ );
78
+ return { [attrName]: nestedResult };
79
+ }
80
+ return { [attrName]: attrValue };
81
+ }
82
+ return Object.entries(encrypted).reduce(
83
+ (putItem, [attrName, attrValue]) => {
84
+ const processed = processValue(attrName, attrValue, false);
85
+ return Object.assign({}, putItem, processed);
86
+ },
87
+ {}
88
+ );
89
+ }
90
+ function toItemWithEqlPayloads(decrypted, encryptionSchema) {
91
+ function processValue(attrName, attrValue, isNested) {
92
+ if (attrValue === null || attrValue === void 0) {
93
+ return { [attrName]: attrValue };
94
+ }
95
+ if (attrName.endsWith(searchTermAttrSuffix)) {
96
+ return {};
97
+ }
98
+ const encryptConfig = encryptionSchema.build();
99
+ const encryptedAttrs = Object.keys(encryptConfig.columns);
100
+ const columnName = attrName.slice(0, -ciphertextAttrSuffix.length);
101
+ if (attrName.endsWith(ciphertextAttrSuffix) && (encryptedAttrs.includes(columnName) || isNested)) {
102
+ const i = { c: columnName, t: encryptConfig.tableName };
103
+ const v = 2;
104
+ if (!isNested && encryptConfig.columns[columnName].cast_as === "json" && encryptConfig.columns[columnName].indexes.ste_vec) {
105
+ return {
106
+ [columnName]: {
107
+ i,
108
+ v,
109
+ k: "sv",
110
+ sv: attrValue
111
+ }
112
+ };
113
+ }
114
+ return {
115
+ [columnName]: {
116
+ i,
117
+ v,
118
+ k: "ct",
119
+ c: attrValue
120
+ }
121
+ };
122
+ }
123
+ if (typeof attrValue === "object" && !Array.isArray(attrValue)) {
124
+ const nestedResult = Object.entries(
125
+ attrValue
126
+ ).reduce(
127
+ (acc, [key, val]) => {
128
+ const processed = processValue(key, val, true);
129
+ return Object.assign({}, acc, processed);
130
+ },
131
+ {}
132
+ );
133
+ return { [attrName]: nestedResult };
134
+ }
135
+ return { [attrName]: attrValue };
136
+ }
137
+ return Object.entries(decrypted).reduce(
138
+ (formattedItem, [attrName, attrValue]) => {
139
+ const processed = processValue(attrName, attrValue, false);
140
+ return Object.assign({}, formattedItem, processed);
141
+ },
142
+ {}
143
+ );
144
+ }
145
+
146
+ // src/dynamodb/operations/base-operation.ts
147
+ var DynamoDBOperation = class {
148
+ auditMetadata;
149
+ logger;
150
+ errorHandler;
151
+ constructor(options) {
152
+ this.logger = options?.logger;
153
+ this.errorHandler = options?.errorHandler;
154
+ }
155
+ /**
156
+ * Attach audit metadata to this operation. Can be chained.
157
+ */
158
+ audit(config) {
159
+ this.auditMetadata = config.metadata;
160
+ return this;
161
+ }
162
+ /**
163
+ * Get the audit metadata for this operation.
164
+ */
165
+ getAuditData() {
166
+ return {
167
+ metadata: this.auditMetadata
168
+ };
169
+ }
170
+ /**
171
+ * Make the operation thenable
172
+ */
173
+ then(onfulfilled, onrejected) {
174
+ return this.execute().then(onfulfilled, onrejected);
175
+ }
176
+ };
177
+
178
+ // src/dynamodb/operations/bulk-decrypt-models.ts
179
+ var BulkDecryptModelsOperation = class extends DynamoDBOperation {
180
+ encryptionClient;
181
+ items;
182
+ table;
183
+ constructor(encryptionClient, items, table, options) {
184
+ super(options);
185
+ this.encryptionClient = encryptionClient;
186
+ this.items = items;
187
+ this.table = table;
188
+ }
189
+ async execute() {
190
+ return await withResult(
191
+ async () => {
192
+ const itemsWithEqlPayloads = this.items.map(
193
+ (item) => toItemWithEqlPayloads(item, this.table)
194
+ );
195
+ const decryptResult = await this.encryptionClient.bulkDecryptModels(itemsWithEqlPayloads).audit(this.getAuditData());
196
+ if (decryptResult.failure) {
197
+ const error = new Error(decryptResult.failure.message);
198
+ error.code = decryptResult.failure.code;
199
+ throw error;
200
+ }
201
+ return decryptResult.data;
202
+ },
203
+ (error) => handleError(error, "bulkDecryptModels", {
204
+ logger: this.logger,
205
+ errorHandler: this.errorHandler
206
+ })
207
+ );
208
+ }
209
+ };
210
+
211
+ // src/dynamodb/operations/bulk-encrypt-models.ts
212
+ import { withResult as withResult2 } from "@byteslice/result";
213
+ var BulkEncryptModelsOperation = class extends DynamoDBOperation {
214
+ encryptionClient;
215
+ items;
216
+ table;
217
+ constructor(encryptionClient, items, table, options) {
218
+ super(options);
219
+ this.encryptionClient = encryptionClient;
220
+ this.items = items;
221
+ this.table = table;
222
+ }
223
+ async execute() {
224
+ return await withResult2(
225
+ async () => {
226
+ const encryptResult = await this.encryptionClient.bulkEncryptModels(
227
+ this.items.map((item) => deepClone(item)),
228
+ this.table
229
+ ).audit(this.getAuditData());
230
+ if (encryptResult.failure) {
231
+ const error = new Error(encryptResult.failure.message);
232
+ error.code = encryptResult.failure.code;
233
+ throw error;
234
+ }
235
+ const data = encryptResult.data.map((item) => deepClone(item));
236
+ const encryptedAttrs = Object.keys(this.table.build().columns);
237
+ return data.map(
238
+ (encrypted) => toEncryptedDynamoItem(encrypted, encryptedAttrs)
239
+ );
240
+ },
241
+ (error) => handleError(error, "bulkEncryptModels", {
242
+ logger: this.logger,
243
+ errorHandler: this.errorHandler
244
+ })
245
+ );
246
+ }
247
+ };
248
+
249
+ // src/dynamodb/operations/decrypt-model.ts
250
+ import { withResult as withResult3 } from "@byteslice/result";
251
+ var DecryptModelOperation = class extends DynamoDBOperation {
252
+ encryptionClient;
253
+ item;
254
+ table;
255
+ constructor(encryptionClient, item, table, options) {
256
+ super(options);
257
+ this.encryptionClient = encryptionClient;
258
+ this.item = item;
259
+ this.table = table;
260
+ }
261
+ async execute() {
262
+ return await withResult3(
263
+ async () => {
264
+ const withEqlPayloads = toItemWithEqlPayloads(this.item, this.table);
265
+ const decryptResult = await this.encryptionClient.decryptModel(withEqlPayloads).audit(this.getAuditData());
266
+ if (decryptResult.failure) {
267
+ const error = new Error(decryptResult.failure.message);
268
+ error.code = decryptResult.failure.code;
269
+ throw error;
270
+ }
271
+ return decryptResult.data;
272
+ },
273
+ (error) => handleError(error, "decryptModel", {
274
+ logger: this.logger,
275
+ errorHandler: this.errorHandler
276
+ })
277
+ );
278
+ }
279
+ };
280
+
281
+ // src/dynamodb/operations/encrypt-model.ts
282
+ import { withResult as withResult4 } from "@byteslice/result";
283
+ var EncryptModelOperation = class extends DynamoDBOperation {
284
+ encryptionClient;
285
+ item;
286
+ table;
287
+ constructor(encryptionClient, item, table, options) {
288
+ super(options);
289
+ this.encryptionClient = encryptionClient;
290
+ this.item = item;
291
+ this.table = table;
292
+ }
293
+ async execute() {
294
+ return await withResult4(
295
+ async () => {
296
+ const encryptResult = await this.encryptionClient.encryptModel(deepClone(this.item), this.table).audit(this.getAuditData());
297
+ if (encryptResult.failure) {
298
+ const error = new Error(encryptResult.failure.message);
299
+ error.code = encryptResult.failure.code;
300
+ throw error;
301
+ }
302
+ const data = deepClone(encryptResult.data);
303
+ const encryptedAttrs = Object.keys(this.table.build().columns);
304
+ return toEncryptedDynamoItem(data, encryptedAttrs);
305
+ },
306
+ (error) => handleError(error, "encryptModel", {
307
+ logger: this.logger,
308
+ errorHandler: this.errorHandler
309
+ })
310
+ );
311
+ }
312
+ };
313
+
314
+ // src/dynamodb/index.ts
315
+ function encryptedDynamoDB(config) {
316
+ const { encryptionClient, options } = config;
317
+ return {
318
+ encryptModel(item, table) {
319
+ return new EncryptModelOperation(
320
+ encryptionClient,
321
+ item,
322
+ table,
323
+ options
324
+ );
325
+ },
326
+ bulkEncryptModels(items, table) {
327
+ return new BulkEncryptModelsOperation(
328
+ encryptionClient,
329
+ items,
330
+ table,
331
+ options
332
+ );
333
+ },
334
+ decryptModel(item, table) {
335
+ return new DecryptModelOperation(
336
+ encryptionClient,
337
+ item,
338
+ table,
339
+ options
340
+ );
341
+ },
342
+ bulkDecryptModels(items, table) {
343
+ return new BulkDecryptModelsOperation(
344
+ encryptionClient,
345
+ items,
346
+ table,
347
+ options
348
+ );
349
+ }
350
+ };
351
+ }
352
+ export {
353
+ encryptedDynamoDB
354
+ };
355
+ //# sourceMappingURL=index.js.map