@cipherstash/stack 0.13.0 → 0.15.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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/{chunk-46PDK2Z6.js → chunk-MD6742R6.js} +1 -1
  4. package/dist/{chunk-46PDK2Z6.js.map → chunk-MD6742R6.js.map} +1 -1
  5. package/dist/{chunk-O4MBO4DJ.js → chunk-YPAPL3IC.js} +2 -2
  6. package/dist/{client-bW-5wG9Y.d.cts → client-DQyqn0oT.d.cts} +1 -1
  7. package/dist/{client-CZLBiToT.d.ts → client-Dv60lAyy.d.ts} +1 -1
  8. package/dist/client.d.cts +2 -2
  9. package/dist/client.d.ts +2 -2
  10. package/dist/drizzle/index.cjs +4 -2
  11. package/dist/drizzle/index.cjs.map +1 -1
  12. package/dist/drizzle/index.d.cts +2 -2
  13. package/dist/drizzle/index.d.ts +2 -2
  14. package/dist/drizzle/index.js +5 -3
  15. package/dist/drizzle/index.js.map +1 -1
  16. package/dist/dynamodb/index.d.cts +2 -2
  17. package/dist/dynamodb/index.d.ts +2 -2
  18. package/dist/encryption/index.cjs.map +1 -1
  19. package/dist/encryption/index.d.cts +2 -2
  20. package/dist/encryption/index.d.ts +2 -2
  21. package/dist/encryption/index.js +2 -2
  22. package/dist/identity/index.js +63 -3
  23. package/dist/identity/index.js.map +1 -1
  24. package/dist/index.cjs +6 -365
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +3 -4
  27. package/dist/index.d.ts +3 -4
  28. package/dist/index.js +2 -7
  29. package/dist/schema/index.d.cts +1 -1
  30. package/dist/schema/index.d.ts +1 -1
  31. package/dist/supabase/index.d.cts +2 -2
  32. package/dist/supabase/index.d.ts +2 -2
  33. package/dist/{types-public-WADaA-TW.d.ts → types-public-DX3mGqoi.d.cts} +3 -3
  34. package/dist/{types-public-WADaA-TW.d.cts → types-public-DX3mGqoi.d.ts} +3 -3
  35. package/dist/types-public.cjs.map +1 -1
  36. package/dist/types-public.d.cts +1 -1
  37. package/dist/types-public.d.ts +1 -1
  38. package/dist/types-public.js +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-EHDOOLWU.js +0 -357
  41. package/dist/chunk-EHDOOLWU.js.map +0 -1
  42. package/dist/chunk-GXGEW6T4.js +0 -68
  43. package/dist/chunk-GXGEW6T4.js.map +0 -1
  44. package/dist/secrets/index.cjs +0 -3143
  45. package/dist/secrets/index.cjs.map +0 -1
  46. package/dist/secrets/index.d.cts +0 -225
  47. package/dist/secrets/index.d.ts +0 -225
  48. package/dist/secrets/index.js +0 -14
  49. package/dist/secrets/index.js.map +0 -1
  50. /package/dist/{chunk-O4MBO4DJ.js.map → chunk-YPAPL3IC.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -1,7 +1,6 @@
1
- import { E as Encrypted } from './types-public-WADaA-TW.cjs';
2
- export { e as encryptedColumn, a as encryptedField, b as encryptedTable } from './types-public-WADaA-TW.cjs';
3
- export { E as Encryption } from './client-bW-5wG9Y.cjs';
4
- export { Secrets } from './secrets/index.cjs';
1
+ import { E as Encrypted } from './types-public-DX3mGqoi.cjs';
2
+ export { e as encryptedColumn, a as encryptedField, b as encryptedTable } from './types-public-DX3mGqoi.cjs';
3
+ export { E as Encryption } from './client-DQyqn0oT.cjs';
5
4
  import 'zod';
6
5
  import '@cipherstash/protect-ffi';
7
6
  import './errors/index.cjs';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { E as Encrypted } from './types-public-WADaA-TW.js';
2
- export { e as encryptedColumn, a as encryptedField, b as encryptedTable } from './types-public-WADaA-TW.js';
3
- export { E as Encryption } from './client-CZLBiToT.js';
4
- export { Secrets } from './secrets/index.js';
1
+ import { E as Encrypted } from './types-public-DX3mGqoi.js';
2
+ export { e as encryptedColumn, a as encryptedField, b as encryptedTable } from './types-public-DX3mGqoi.js';
3
+ export { E as Encryption } from './client-Dv60lAyy.js';
5
4
  import 'zod';
6
5
  import '@cipherstash/protect-ffi';
7
6
  import './errors/index.js';
package/dist/index.js CHANGED
@@ -1,9 +1,6 @@
1
- import {
2
- Secrets
3
- } from "./chunk-EHDOOLWU.js";
4
1
  import {
5
2
  Encryption
6
- } from "./chunk-O4MBO4DJ.js";
3
+ } from "./chunk-YPAPL3IC.js";
7
4
  import {
8
5
  encryptedToPgComposite,
9
6
  isEncryptedPayload
@@ -13,13 +10,11 @@ import {
13
10
  encryptedField,
14
11
  encryptedTable
15
12
  } from "./chunk-GNLU3I27.js";
16
- import "./chunk-46PDK2Z6.js";
17
- import "./chunk-GXGEW6T4.js";
13
+ import "./chunk-MD6742R6.js";
18
14
  import "./chunk-Q5FTQLYG.js";
19
15
  import "./chunk-LBMC4D6D.js";
20
16
  export {
21
17
  Encryption,
22
- Secrets,
23
18
  encryptedColumn,
24
19
  encryptedField,
25
20
  encryptedTable,
@@ -1,3 +1,3 @@
1
- export { C as CastAs, u as ColumnSchema, q as EncryptConfig, f as EncryptedColumn, n as EncryptedField, c as EncryptedTable, d as EncryptedTableColumn, v as EqlCastAs, I as InferEncrypted, t as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, x as buildEncryptConfig, y as castAsEnum, z as encryptConfigSchema, e as encryptedColumn, a as encryptedField, b as encryptedTable, A as eqlCastAsEnum, F as toEqlCastAs } from '../types-public-WADaA-TW.cjs';
1
+ export { C as CastAs, u as ColumnSchema, q as EncryptConfig, f as EncryptedColumn, n as EncryptedField, c as EncryptedTable, d as EncryptedTableColumn, v as EqlCastAs, I as InferEncrypted, t as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, x as buildEncryptConfig, y as castAsEnum, z as encryptConfigSchema, e as encryptedColumn, a as encryptedField, b as encryptedTable, A as eqlCastAsEnum, F as toEqlCastAs } from '../types-public-DX3mGqoi.cjs';
2
2
  import 'zod';
3
3
  import '@cipherstash/protect-ffi';
@@ -1,3 +1,3 @@
1
- export { C as CastAs, u as ColumnSchema, q as EncryptConfig, f as EncryptedColumn, n as EncryptedField, c as EncryptedTable, d as EncryptedTableColumn, v as EqlCastAs, I as InferEncrypted, t as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, x as buildEncryptConfig, y as castAsEnum, z as encryptConfigSchema, e as encryptedColumn, a as encryptedField, b as encryptedTable, A as eqlCastAsEnum, F as toEqlCastAs } from '../types-public-WADaA-TW.js';
1
+ export { C as CastAs, u as ColumnSchema, q as EncryptConfig, f as EncryptedColumn, n as EncryptedField, c as EncryptedTable, d as EncryptedTableColumn, v as EqlCastAs, I as InferEncrypted, t as InferPlaintext, M as MatchIndexOpts, O as OreIndexOpts, w as SteVecIndexOpts, T as TokenFilter, U as UniqueIndexOpts, x as buildEncryptConfig, y as castAsEnum, z as encryptConfigSchema, e as encryptedColumn, a as encryptedField, b as encryptedTable, A as eqlCastAsEnum, F as toEqlCastAs } from '../types-public-DX3mGqoi.js';
2
2
  import 'zod';
3
3
  import '@cipherstash/protect-ffi';
@@ -1,7 +1,7 @@
1
- import { A as AuditConfig, a as EncryptionClient } from '../client-bW-5wG9Y.cjs';
1
+ import { A as AuditConfig, a as EncryptionClient } from '../client-DQyqn0oT.cjs';
2
2
  import { EncryptionError } from '../errors/index.cjs';
3
3
  import { LockContext } from '../identity/index.cjs';
4
- import { c as EncryptedTable, d as EncryptedTableColumn } from '../types-public-WADaA-TW.cjs';
4
+ import { c as EncryptedTable, d as EncryptedTableColumn } from '../types-public-DX3mGqoi.cjs';
5
5
  import '@byteslice/result';
6
6
  import '@cipherstash/protect-ffi';
7
7
  import 'zod';
@@ -1,7 +1,7 @@
1
- import { A as AuditConfig, a as EncryptionClient } from '../client-CZLBiToT.js';
1
+ import { A as AuditConfig, a as EncryptionClient } from '../client-Dv60lAyy.js';
2
2
  import { EncryptionError } from '../errors/index.js';
3
3
  import { LockContext } from '../identity/index.js';
4
- import { c as EncryptedTable, d as EncryptedTableColumn } from '../types-public-WADaA-TW.js';
4
+ import { c as EncryptedTable, d as EncryptedTableColumn } from '../types-public-DX3mGqoi.js';
5
5
  import '@byteslice/result';
6
6
  import '@cipherstash/protect-ffi';
7
7
  import 'zod';
@@ -144,9 +144,9 @@ type DecryptionResult<T> = DecryptionSuccess<T> | DecryptionError<T>;
144
144
  /**
145
145
  * User-facing query type names for encrypting query values.
146
146
  *
147
- * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/exact)
148
- * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/match)
149
- * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/range)
147
+ * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)
148
+ * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)
149
+ * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)
150
150
  * - `'steVecSelector'`: JSONPath selector (e.g. `'$.user.email'`)
151
151
  * - `'steVecTerm'`: Containment (e.g. `{ role: 'admin' }`)
152
152
  * - `'searchableJson'`: Auto-infers selector or term from plaintext type (recommended)
@@ -144,9 +144,9 @@ type DecryptionResult<T> = DecryptionSuccess<T> | DecryptionError<T>;
144
144
  /**
145
145
  * User-facing query type names for encrypting query values.
146
146
  *
147
- * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/exact)
148
- * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/match)
149
- * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/range)
147
+ * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)
148
+ * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)
149
+ * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)
150
150
  * - `'steVecSelector'`: JSONPath selector (e.g. `'$.user.email'`)
151
151
  * - `'steVecTerm'`: Containment (e.g. `{ role: 'admin' }`)
152
152
  * - `'searchableJson'`: Auto-infers selector or term from plaintext type (recommended)
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types-public.ts","../src/types.ts"],"sourcesContent":["/**\n * Public type re-exports for `@cipherstash/stack/types`.\n *\n * This module exposes only the public types from the internal types module.\n * Internal helpers (`queryTypeToFfi`, `queryTypeToQueryOp`, `FfiIndexTypeName`,\n * `QueryTermBase`) are excluded.\n */\n\n// Core types\nexport type {\n Client,\n EncryptedValue,\n Encrypted,\n} from '@/types'\n\n// Client configuration\nexport type {\n KeysetIdentifier,\n ClientConfig,\n EncryptionClientConfig,\n} from '@/types'\n\n// Encrypt / decrypt operation options and results\nexport type {\n EncryptOptions,\n EncryptedReturnType,\n SearchTerm,\n EncryptedSearchTerm,\n EncryptedQueryResult,\n} from '@/types'\n\n// Model field types\nexport type {\n EncryptedFields,\n OtherFields,\n DecryptedFields,\n Decrypted,\n} from '@/types'\n\n// Bulk operations\nexport type {\n BulkEncryptPayload,\n BulkEncryptedData,\n BulkDecryptPayload,\n BulkDecryptedData,\n DecryptionResult,\n} from '@/types'\n\n// Query types (public only)\nexport type {\n QueryTypeName,\n EncryptQueryOptions,\n ScalarQueryTerm,\n} from '@/types'\n\n// Runtime values\nexport { queryTypes } from '@/types'\n","import type {\n EncryptedColumn,\n EncryptedField,\n EncryptedTable,\n EncryptedTableColumn,\n} from '@/schema'\nimport type {\n Encrypted as CipherStashEncrypted,\n JsPlaintext,\n QueryOpName,\n newClient,\n} from '@cipherstash/protect-ffi'\n\n// ---------------------------------------------------------------------------\n// Branded type utilities\n// ---------------------------------------------------------------------------\n\n/** Brand symbol for nominal typing */\ndeclare const __brand: unique symbol\n\n/** Creates a branded type that is structurally incompatible with the base type */\ntype Brand<T, B extends string> = T & { readonly [__brand]: B }\n\n// ---------------------------------------------------------------------------\n// Core types\n// ---------------------------------------------------------------------------\n\nexport type Client = Awaited<ReturnType<typeof newClient>> | undefined\n\n/** A branded type representing encrypted data. Cannot be accidentally used as plaintext. */\nexport type EncryptedValue = Brand<CipherStashEncrypted, 'encrypted'>\n\n/** Structural type representing encrypted data. See also `EncryptedValue` for branded nominal typing. */\nexport type Encrypted = CipherStashEncrypted\n\n// ---------------------------------------------------------------------------\n// Client configuration\n// ---------------------------------------------------------------------------\n\nexport type KeysetIdentifier = { name: string } | { id: string }\n\nexport type ClientConfig = {\n /**\n * The CipherStash workspace CRN (Cloud Resource Name).\n * Format: `crn:<region>.aws:<workspace-id>`.\n * Can also be set via the `CS_WORKSPACE_CRN` environment variable.\n */\n workspaceCrn?: string\n\n /**\n * The API access key used for authenticating with the CipherStash API.\n * Can also be set via the `CS_CLIENT_ACCESS_KEY` environment variable.\n * Obtain this from the CipherStash dashboard after creating a workspace.\n */\n accessKey?: string\n\n /**\n * The client identifier used to authenticate with CipherStash services.\n * Can also be set via the `CS_CLIENT_ID` environment variable.\n * Generated during workspace onboarding in the CipherStash dashboard.\n */\n clientId?: string\n\n /**\n * The client key material used in combination with ZeroKMS for encryption operations.\n * Can also be set via the `CS_CLIENT_KEY` environment variable.\n * Generated during workspace onboarding in the CipherStash dashboard.\n */\n clientKey?: string\n\n /**\n * An optional keyset identifier for multi-tenant encryption.\n * Each keyset provides cryptographic isolation, giving each tenant its own keyspace.\n * Specify by name (`{ name: \"tenant-a\" }`) or UUID (`{ id: \"...\" }`).\n * Keysets are created and managed in the CipherStash dashboard.\n */\n keyset?: KeysetIdentifier\n}\n\ntype AtLeastOneCsTable<T> = [T, ...T[]]\n\nexport type EncryptionClientConfig = {\n schemas: AtLeastOneCsTable<EncryptedTable<EncryptedTableColumn>>\n config?: ClientConfig\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt operation options and results\n// ---------------------------------------------------------------------------\n\n/**\n * Options for single-value encrypt operations.\n * Use a column from your table schema (from {@link encryptedColumn}) or a nested\n * field (from {@link encryptedField}) as the target for encryption.\n */\nexport type EncryptOptions = {\n /** The column or nested field to encrypt into. From {@link EncryptedColumn} or {@link EncryptedField}. */\n column: EncryptedColumn | EncryptedField\n table: EncryptedTable<EncryptedTableColumn>\n}\n\n/** Format for encrypted query/search term return values */\nexport type EncryptedReturnType =\n | 'eql'\n | 'composite-literal'\n | 'escaped-composite-literal'\n\nexport type SearchTerm = {\n value: JsPlaintext\n column: EncryptedColumn\n table: EncryptedTable<EncryptedTableColumn>\n returnType?: EncryptedReturnType\n}\n\n/** Encrypted search term result: EQL object or composite literal string */\nexport type EncryptedSearchTerm = Encrypted | string\n\n/** Result of encryptQuery (single or batch): EQL or composite literal string */\nexport type EncryptedQueryResult = Encrypted | string\n\n// ---------------------------------------------------------------------------\n// Model field types (encrypted vs decrypted views)\n// ---------------------------------------------------------------------------\n\nexport type EncryptedFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted ? K : never]: T[K]\n}\n\nexport type OtherFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted ? never : K]: T[K]\n}\n\nexport type DecryptedFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted\n ? K\n : never]: null extends T[K] ? string | null : string\n}\n\n/** Model with encrypted fields replaced by plaintext (decrypted) values */\nexport type Decrypted<T> = OtherFields<T> & DecryptedFields<T>\n\n/**\n * Maps a plaintext model type to its encrypted form using the table schema.\n *\n * Fields whose keys match columns defined in `S` become `Encrypted`;\n * all other fields retain their original types from `T`.\n *\n * When `S` is the widened `EncryptedTableColumn` (e.g. when a user passes an\n * explicit `<User>` type argument without specifying `S`), the type degrades\n * gracefully to `T` — preserving backward compatibility.\n *\n * @typeParam T - The plaintext model type (e.g. `{ id: string; email: string }`)\n * @typeParam S - The table schema column definition, inferred from the `table` argument\n *\n * @example\n * ```typescript\n * type User = { id: string; email: string }\n * // With a schema that defines `email`:\n * type Encrypted = EncryptedFromSchema<User, { email: EncryptedColumn }>\n * // => { id: string; email: Encrypted }\n * ```\n */\nexport type EncryptedFromSchema<T, S extends EncryptedTableColumn> = {\n [K in keyof T]: [K] extends [keyof S]\n ? [S[K & keyof S]] extends [EncryptedColumn | EncryptedField]\n ? null extends T[K]\n ? Encrypted | null\n : Encrypted\n : T[K]\n : T[K]\n}\n\n// ---------------------------------------------------------------------------\n// Bulk operations\n// ---------------------------------------------------------------------------\n\nexport type BulkEncryptPayload = Array<{\n id?: string\n plaintext: JsPlaintext\n}>\n\nexport type BulkEncryptedData = Array<{ id?: string; data: Encrypted }>\nexport type BulkDecryptPayload = Array<{ id?: string; data: Encrypted }>\nexport type BulkDecryptedData = Array<DecryptionResult<JsPlaintext>>\n\ntype DecryptionSuccess<T> = { error?: never; data: T; id?: string }\ntype DecryptionError<T> = { error: T; id?: string; data?: never }\n\n/**\n * Result type for individual items in bulk decrypt operations.\n * Uses `error`/`data` fields (not `failure`/`data`) since bulk operations\n * can have per-item failures.\n */\nexport type DecryptionResult<T> = DecryptionSuccess<T> | DecryptionError<T>\n\n// ---------------------------------------------------------------------------\n// Query types (for searchable encryption / encryptQuery)\n// ---------------------------------------------------------------------------\n\n/**\n * User-facing query type names for encrypting query values.\n *\n * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/exact)\n * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/match)\n * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/platform/searchable-encryption/supported-queries/range)\n * - `'steVecSelector'`: JSONPath selector (e.g. `'$.user.email'`)\n * - `'steVecTerm'`: Containment (e.g. `{ role: 'admin' }`)\n * - `'searchableJson'`: Auto-infers selector or term from plaintext type (recommended)\n */\nexport type QueryTypeName =\n | 'orderAndRange'\n | 'freeTextSearch'\n | 'equality'\n | 'steVecSelector'\n | 'steVecTerm'\n | 'searchableJson'\n\n/** @internal */\nexport type FfiIndexTypeName = 'ore' | 'match' | 'unique' | 'ste_vec'\n\nexport const queryTypes = {\n orderAndRange: 'orderAndRange',\n freeTextSearch: 'freeTextSearch',\n equality: 'equality',\n steVecSelector: 'steVecSelector',\n steVecTerm: 'steVecTerm',\n searchableJson: 'searchableJson',\n} as const satisfies Record<string, QueryTypeName>\n\n/** @internal */\nexport const queryTypeToFfi: Record<QueryTypeName, FfiIndexTypeName> = {\n orderAndRange: 'ore',\n freeTextSearch: 'match',\n equality: 'unique',\n steVecSelector: 'ste_vec',\n steVecTerm: 'ste_vec',\n searchableJson: 'ste_vec',\n}\n\n/** @internal */\nexport const queryTypeToQueryOp: Partial<Record<QueryTypeName, QueryOpName>> = {\n steVecSelector: 'ste_vec_selector',\n steVecTerm: 'ste_vec_term',\n}\n\n/** @internal */\nexport type QueryTermBase = {\n column: EncryptedColumn\n table: EncryptedTable<EncryptedTableColumn>\n queryType?: QueryTypeName\n returnType?: EncryptedReturnType\n}\n\nexport type EncryptQueryOptions = QueryTermBase\n\nexport type ScalarQueryTerm = QueryTermBase & {\n value: JsPlaintext\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4NO,IAAM,aAAa;AAAA,EACxB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,gBAAgB;AAClB;","names":[]}
1
+ {"version":3,"sources":["../src/types-public.ts","../src/types.ts"],"sourcesContent":["/**\n * Public type re-exports for `@cipherstash/stack/types`.\n *\n * This module exposes only the public types from the internal types module.\n * Internal helpers (`queryTypeToFfi`, `queryTypeToQueryOp`, `FfiIndexTypeName`,\n * `QueryTermBase`) are excluded.\n */\n\n// Core types\nexport type {\n Client,\n EncryptedValue,\n Encrypted,\n} from '@/types'\n\n// Client configuration\nexport type {\n KeysetIdentifier,\n ClientConfig,\n EncryptionClientConfig,\n} from '@/types'\n\n// Encrypt / decrypt operation options and results\nexport type {\n EncryptOptions,\n EncryptedReturnType,\n SearchTerm,\n EncryptedSearchTerm,\n EncryptedQueryResult,\n} from '@/types'\n\n// Model field types\nexport type {\n EncryptedFields,\n OtherFields,\n DecryptedFields,\n Decrypted,\n} from '@/types'\n\n// Bulk operations\nexport type {\n BulkEncryptPayload,\n BulkEncryptedData,\n BulkDecryptPayload,\n BulkDecryptedData,\n DecryptionResult,\n} from '@/types'\n\n// Query types (public only)\nexport type {\n QueryTypeName,\n EncryptQueryOptions,\n ScalarQueryTerm,\n} from '@/types'\n\n// Runtime values\nexport { queryTypes } from '@/types'\n","import type {\n EncryptedColumn,\n EncryptedField,\n EncryptedTable,\n EncryptedTableColumn,\n} from '@/schema'\nimport type {\n Encrypted as CipherStashEncrypted,\n JsPlaintext,\n QueryOpName,\n newClient,\n} from '@cipherstash/protect-ffi'\n\n// ---------------------------------------------------------------------------\n// Branded type utilities\n// ---------------------------------------------------------------------------\n\n/** Brand symbol for nominal typing */\ndeclare const __brand: unique symbol\n\n/** Creates a branded type that is structurally incompatible with the base type */\ntype Brand<T, B extends string> = T & { readonly [__brand]: B }\n\n// ---------------------------------------------------------------------------\n// Core types\n// ---------------------------------------------------------------------------\n\nexport type Client = Awaited<ReturnType<typeof newClient>> | undefined\n\n/** A branded type representing encrypted data. Cannot be accidentally used as plaintext. */\nexport type EncryptedValue = Brand<CipherStashEncrypted, 'encrypted'>\n\n/** Structural type representing encrypted data. See also `EncryptedValue` for branded nominal typing. */\nexport type Encrypted = CipherStashEncrypted\n\n// ---------------------------------------------------------------------------\n// Client configuration\n// ---------------------------------------------------------------------------\n\nexport type KeysetIdentifier = { name: string } | { id: string }\n\nexport type ClientConfig = {\n /**\n * The CipherStash workspace CRN (Cloud Resource Name).\n * Format: `crn:<region>.aws:<workspace-id>`.\n * Can also be set via the `CS_WORKSPACE_CRN` environment variable.\n */\n workspaceCrn?: string\n\n /**\n * The API access key used for authenticating with the CipherStash API.\n * Can also be set via the `CS_CLIENT_ACCESS_KEY` environment variable.\n * Obtain this from the CipherStash dashboard after creating a workspace.\n */\n accessKey?: string\n\n /**\n * The client identifier used to authenticate with CipherStash services.\n * Can also be set via the `CS_CLIENT_ID` environment variable.\n * Generated during workspace onboarding in the CipherStash dashboard.\n */\n clientId?: string\n\n /**\n * The client key material used in combination with ZeroKMS for encryption operations.\n * Can also be set via the `CS_CLIENT_KEY` environment variable.\n * Generated during workspace onboarding in the CipherStash dashboard.\n */\n clientKey?: string\n\n /**\n * An optional keyset identifier for multi-tenant encryption.\n * Each keyset provides cryptographic isolation, giving each tenant its own keyspace.\n * Specify by name (`{ name: \"tenant-a\" }`) or UUID (`{ id: \"...\" }`).\n * Keysets are created and managed in the CipherStash dashboard.\n */\n keyset?: KeysetIdentifier\n}\n\ntype AtLeastOneCsTable<T> = [T, ...T[]]\n\nexport type EncryptionClientConfig = {\n schemas: AtLeastOneCsTable<EncryptedTable<EncryptedTableColumn>>\n config?: ClientConfig\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt operation options and results\n// ---------------------------------------------------------------------------\n\n/**\n * Options for single-value encrypt operations.\n * Use a column from your table schema (from {@link encryptedColumn}) or a nested\n * field (from {@link encryptedField}) as the target for encryption.\n */\nexport type EncryptOptions = {\n /** The column or nested field to encrypt into. From {@link EncryptedColumn} or {@link EncryptedField}. */\n column: EncryptedColumn | EncryptedField\n table: EncryptedTable<EncryptedTableColumn>\n}\n\n/** Format for encrypted query/search term return values */\nexport type EncryptedReturnType =\n | 'eql'\n | 'composite-literal'\n | 'escaped-composite-literal'\n\nexport type SearchTerm = {\n value: JsPlaintext\n column: EncryptedColumn\n table: EncryptedTable<EncryptedTableColumn>\n returnType?: EncryptedReturnType\n}\n\n/** Encrypted search term result: EQL object or composite literal string */\nexport type EncryptedSearchTerm = Encrypted | string\n\n/** Result of encryptQuery (single or batch): EQL or composite literal string */\nexport type EncryptedQueryResult = Encrypted | string\n\n// ---------------------------------------------------------------------------\n// Model field types (encrypted vs decrypted views)\n// ---------------------------------------------------------------------------\n\nexport type EncryptedFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted ? K : never]: T[K]\n}\n\nexport type OtherFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted ? never : K]: T[K]\n}\n\nexport type DecryptedFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted\n ? K\n : never]: null extends T[K] ? string | null : string\n}\n\n/** Model with encrypted fields replaced by plaintext (decrypted) values */\nexport type Decrypted<T> = OtherFields<T> & DecryptedFields<T>\n\n/**\n * Maps a plaintext model type to its encrypted form using the table schema.\n *\n * Fields whose keys match columns defined in `S` become `Encrypted`;\n * all other fields retain their original types from `T`.\n *\n * When `S` is the widened `EncryptedTableColumn` (e.g. when a user passes an\n * explicit `<User>` type argument without specifying `S`), the type degrades\n * gracefully to `T` — preserving backward compatibility.\n *\n * @typeParam T - The plaintext model type (e.g. `{ id: string; email: string }`)\n * @typeParam S - The table schema column definition, inferred from the `table` argument\n *\n * @example\n * ```typescript\n * type User = { id: string; email: string }\n * // With a schema that defines `email`:\n * type Encrypted = EncryptedFromSchema<User, { email: EncryptedColumn }>\n * // => { id: string; email: Encrypted }\n * ```\n */\nexport type EncryptedFromSchema<T, S extends EncryptedTableColumn> = {\n [K in keyof T]: [K] extends [keyof S]\n ? [S[K & keyof S]] extends [EncryptedColumn | EncryptedField]\n ? null extends T[K]\n ? Encrypted | null\n : Encrypted\n : T[K]\n : T[K]\n}\n\n// ---------------------------------------------------------------------------\n// Bulk operations\n// ---------------------------------------------------------------------------\n\nexport type BulkEncryptPayload = Array<{\n id?: string\n plaintext: JsPlaintext\n}>\n\nexport type BulkEncryptedData = Array<{ id?: string; data: Encrypted }>\nexport type BulkDecryptPayload = Array<{ id?: string; data: Encrypted }>\nexport type BulkDecryptedData = Array<DecryptionResult<JsPlaintext>>\n\ntype DecryptionSuccess<T> = { error?: never; data: T; id?: string }\ntype DecryptionError<T> = { error: T; id?: string; data?: never }\n\n/**\n * Result type for individual items in bulk decrypt operations.\n * Uses `error`/`data` fields (not `failure`/`data`) since bulk operations\n * can have per-item failures.\n */\nexport type DecryptionResult<T> = DecryptionSuccess<T> | DecryptionError<T>\n\n// ---------------------------------------------------------------------------\n// Query types (for searchable encryption / encryptQuery)\n// ---------------------------------------------------------------------------\n\n/**\n * User-facing query type names for encrypting query values.\n *\n * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)\n * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)\n * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)\n * - `'steVecSelector'`: JSONPath selector (e.g. `'$.user.email'`)\n * - `'steVecTerm'`: Containment (e.g. `{ role: 'admin' }`)\n * - `'searchableJson'`: Auto-infers selector or term from plaintext type (recommended)\n */\nexport type QueryTypeName =\n | 'orderAndRange'\n | 'freeTextSearch'\n | 'equality'\n | 'steVecSelector'\n | 'steVecTerm'\n | 'searchableJson'\n\n/** @internal */\nexport type FfiIndexTypeName = 'ore' | 'match' | 'unique' | 'ste_vec'\n\nexport const queryTypes = {\n orderAndRange: 'orderAndRange',\n freeTextSearch: 'freeTextSearch',\n equality: 'equality',\n steVecSelector: 'steVecSelector',\n steVecTerm: 'steVecTerm',\n searchableJson: 'searchableJson',\n} as const satisfies Record<string, QueryTypeName>\n\n/** @internal */\nexport const queryTypeToFfi: Record<QueryTypeName, FfiIndexTypeName> = {\n orderAndRange: 'ore',\n freeTextSearch: 'match',\n equality: 'unique',\n steVecSelector: 'ste_vec',\n steVecTerm: 'ste_vec',\n searchableJson: 'ste_vec',\n}\n\n/** @internal */\nexport const queryTypeToQueryOp: Partial<Record<QueryTypeName, QueryOpName>> = {\n steVecSelector: 'ste_vec_selector',\n steVecTerm: 'ste_vec_term',\n}\n\n/** @internal */\nexport type QueryTermBase = {\n column: EncryptedColumn\n table: EncryptedTable<EncryptedTableColumn>\n queryType?: QueryTypeName\n returnType?: EncryptedReturnType\n}\n\nexport type EncryptQueryOptions = QueryTermBase\n\nexport type ScalarQueryTerm = QueryTermBase & {\n value: JsPlaintext\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4NO,IAAM,aAAa;AAAA,EACxB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,gBAAgB;AAClB;","names":[]}
@@ -1,3 +1,3 @@
1
- export { j as BulkDecryptPayload, B as BulkDecryptedData, l as BulkEncryptPayload, k as BulkEncryptedData, i as Client, G as ClientConfig, D as Decrypted, H as DecryptedFields, J as DecryptionResult, m as EncryptOptions, o as EncryptQueryOptions, E as Encrypted, L as EncryptedFields, h as EncryptedQueryResult, p as EncryptedReturnType, N as EncryptedSearchTerm, g as EncryptedValue, s as EncryptionClientConfig, K as KeysetIdentifier, P as OtherFields, Q as QueryTypeName, S as ScalarQueryTerm, R as SearchTerm, V as queryTypes } from './types-public-WADaA-TW.cjs';
1
+ export { j as BulkDecryptPayload, B as BulkDecryptedData, l as BulkEncryptPayload, k as BulkEncryptedData, i as Client, G as ClientConfig, D as Decrypted, H as DecryptedFields, J as DecryptionResult, m as EncryptOptions, o as EncryptQueryOptions, E as Encrypted, L as EncryptedFields, h as EncryptedQueryResult, p as EncryptedReturnType, N as EncryptedSearchTerm, g as EncryptedValue, s as EncryptionClientConfig, K as KeysetIdentifier, P as OtherFields, Q as QueryTypeName, S as ScalarQueryTerm, R as SearchTerm, V as queryTypes } from './types-public-DX3mGqoi.cjs';
2
2
  import 'zod';
3
3
  import '@cipherstash/protect-ffi';
@@ -1,3 +1,3 @@
1
- export { j as BulkDecryptPayload, B as BulkDecryptedData, l as BulkEncryptPayload, k as BulkEncryptedData, i as Client, G as ClientConfig, D as Decrypted, H as DecryptedFields, J as DecryptionResult, m as EncryptOptions, o as EncryptQueryOptions, E as Encrypted, L as EncryptedFields, h as EncryptedQueryResult, p as EncryptedReturnType, N as EncryptedSearchTerm, g as EncryptedValue, s as EncryptionClientConfig, K as KeysetIdentifier, P as OtherFields, Q as QueryTypeName, S as ScalarQueryTerm, R as SearchTerm, V as queryTypes } from './types-public-WADaA-TW.js';
1
+ export { j as BulkDecryptPayload, B as BulkDecryptedData, l as BulkEncryptPayload, k as BulkEncryptedData, i as Client, G as ClientConfig, D as Decrypted, H as DecryptedFields, J as DecryptionResult, m as EncryptOptions, o as EncryptQueryOptions, E as Encrypted, L as EncryptedFields, h as EncryptedQueryResult, p as EncryptedReturnType, N as EncryptedSearchTerm, g as EncryptedValue, s as EncryptionClientConfig, K as KeysetIdentifier, P as OtherFields, Q as QueryTypeName, S as ScalarQueryTerm, R as SearchTerm, V as queryTypes } from './types-public-DX3mGqoi.js';
2
2
  import 'zod';
3
3
  import '@cipherstash/protect-ffi';
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  queryTypes
3
- } from "./chunk-46PDK2Z6.js";
3
+ } from "./chunk-MD6742R6.js";
4
4
  export {
5
5
  queryTypes
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cipherstash/stack",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "CipherStash Stack for TypeScript and JavaScript",
5
5
  "keywords": [
6
6
  "encrypted",
@@ -1,357 +0,0 @@
1
- import {
2
- Encryption
3
- } from "./chunk-O4MBO4DJ.js";
4
- import {
5
- encryptedToPgComposite
6
- } from "./chunk-4RNBI3UH.js";
7
- import {
8
- encryptedColumn,
9
- encryptedTable
10
- } from "./chunk-GNLU3I27.js";
11
- import {
12
- extractWorkspaceIdFromCrn
13
- } from "./chunk-GXGEW6T4.js";
14
- import {
15
- logger
16
- } from "./chunk-Q5FTQLYG.js";
17
-
18
- // src/secrets/index.ts
19
- var Secrets = class {
20
- encryptionClient = null;
21
- config;
22
- apiBaseUrl = process.env.STASH_API_URL || "https://dashboard.cipherstash.com/api/secrets";
23
- secretsSchema = encryptedTable("secrets", {
24
- value: encryptedColumn("value")
25
- });
26
- constructor(config) {
27
- const workspaceCRN = config.workspaceCRN ?? process.env.CS_WORKSPACE_CRN;
28
- const clientId = config.clientId ?? process.env.CS_CLIENT_ID;
29
- const clientKey = config.clientKey ?? process.env.CS_CLIENT_KEY;
30
- const accessKey = config.accessKey ?? process.env.CS_CLIENT_ACCESS_KEY;
31
- if (!workspaceCRN || !clientId || !clientKey || !accessKey) {
32
- throw new Error(
33
- "Missing required configuration or environment variables."
34
- );
35
- }
36
- this.config = {
37
- environment: config.environment,
38
- workspaceCRN,
39
- clientId,
40
- clientKey,
41
- accessKey
42
- };
43
- }
44
- initPromise = null;
45
- /**
46
- * Initialize the Secrets client and underlying Encryption client
47
- */
48
- async ensureInitialized() {
49
- if (!this.initPromise) {
50
- this.initPromise = this._doInit();
51
- }
52
- return this.initPromise;
53
- }
54
- async _doInit() {
55
- logger.debug("Initializing the Secrets client.");
56
- this.encryptionClient = await Encryption({
57
- schemas: [this.secretsSchema],
58
- config: {
59
- workspaceCrn: this.config.workspaceCRN,
60
- clientId: this.config.clientId,
61
- clientKey: this.config.clientKey,
62
- accessKey: this.config.accessKey,
63
- keyset: { name: this.config.environment }
64
- }
65
- });
66
- logger.debug("Successfully initialized the Secrets client.");
67
- }
68
- /**
69
- * Get the authorization header for API requests
70
- */
71
- getAuthHeader() {
72
- return `Bearer ${this.config.accessKey}`;
73
- }
74
- /**
75
- * Make an API request with error handling.
76
- *
77
- * For GET requests, `params` are appended as URL query parameters.
78
- * For POST requests, `body` is sent as JSON in the request body.
79
- */
80
- async apiRequest(method, path, options) {
81
- try {
82
- let url = `${this.apiBaseUrl}${path}`;
83
- if (options?.params) {
84
- const searchParams = new URLSearchParams(options.params);
85
- url = `${url}?${searchParams.toString()}`;
86
- }
87
- logger.debug(`Secrets API request: ${method} ${path}`);
88
- const headers = {
89
- "Content-Type": "application/json",
90
- Authorization: this.getAuthHeader()
91
- };
92
- const response = await fetch(url, {
93
- method,
94
- headers,
95
- body: options?.body ? JSON.stringify(options.body) : void 0
96
- });
97
- if (!response.ok) {
98
- const errorText = await response.text();
99
- let errorMessage = `API request failed with status ${response.status}`;
100
- try {
101
- const errorJson = JSON.parse(errorText);
102
- errorMessage = errorJson.message || errorJson.error || errorMessage;
103
- } catch {
104
- errorMessage = errorText || errorMessage;
105
- }
106
- logger.error(`Secrets API error on ${method} ${path}: ${errorMessage}`);
107
- return {
108
- failure: {
109
- type: "ApiError",
110
- message: errorMessage
111
- }
112
- };
113
- }
114
- logger.debug(`Secrets API request successful: ${method} ${path}`);
115
- const data = await response.json();
116
- return { data };
117
- } catch (error) {
118
- const message = error instanceof Error ? error.message : "Unknown network error occurred";
119
- logger.error(`Secrets network error on ${method} ${path}: ${message}`);
120
- return {
121
- failure: {
122
- type: "NetworkError",
123
- message
124
- }
125
- };
126
- }
127
- }
128
- /**
129
- * Store an encrypted secret in the vault.
130
- * The value is encrypted locally before being sent to the API.
131
- *
132
- * API: POST /api/secrets/set
133
- *
134
- * @param name - The name of the secret
135
- * @param value - The plaintext value to encrypt and store
136
- * @returns A Result containing the API response or an error
137
- */
138
- async set(name, value) {
139
- logger.debug("Setting secret");
140
- await this.ensureInitialized();
141
- if (!this.encryptionClient) {
142
- return {
143
- failure: {
144
- type: "ClientError",
145
- message: "Failed to initialize Encryption client"
146
- }
147
- };
148
- }
149
- const encryptResult = await this.encryptionClient.encrypt(value, {
150
- column: this.secretsSchema.value,
151
- table: this.secretsSchema
152
- });
153
- if (encryptResult.failure) {
154
- logger.error("Failed to encrypt secret");
155
- return {
156
- failure: {
157
- type: "EncryptionError",
158
- message: encryptResult.failure.message
159
- }
160
- };
161
- }
162
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
163
- return await this.apiRequest("POST", "/set", {
164
- body: {
165
- workspaceId,
166
- environment: this.config.environment,
167
- name,
168
- encryptedValue: encryptedToPgComposite(encryptResult.data)
169
- }
170
- });
171
- }
172
- /**
173
- * Retrieve and decrypt a secret from the vault.
174
- * The secret is decrypted locally after retrieval.
175
- *
176
- * API: GET /api/secrets/get?workspaceId=...&environment=...&name=...
177
- *
178
- * @param name - The name of the secret to retrieve
179
- * @returns A Result containing the decrypted value or an error
180
- */
181
- async get(name) {
182
- logger.debug("Getting secret");
183
- await this.ensureInitialized();
184
- if (!this.encryptionClient) {
185
- return {
186
- failure: {
187
- type: "ClientError",
188
- message: "Failed to initialize Encryption client"
189
- }
190
- };
191
- }
192
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
193
- const apiResult = await this.apiRequest("GET", "/get", {
194
- params: {
195
- workspaceId,
196
- environment: this.config.environment,
197
- name
198
- }
199
- });
200
- if (apiResult.failure) {
201
- return apiResult;
202
- }
203
- const decryptResult = await this.encryptionClient.decrypt(
204
- apiResult.data.encryptedValue.data
205
- );
206
- if (decryptResult.failure) {
207
- logger.error("Failed to decrypt secret");
208
- return {
209
- failure: {
210
- type: "DecryptionError",
211
- message: decryptResult.failure.message
212
- }
213
- };
214
- }
215
- if (typeof decryptResult.data !== "string") {
216
- logger.error("Decrypted secret value is not a string");
217
- return {
218
- failure: {
219
- type: "DecryptionError",
220
- message: "Decrypted value is not a string"
221
- }
222
- };
223
- }
224
- return { data: decryptResult.data };
225
- }
226
- /**
227
- * Retrieve and decrypt many secrets from the vault.
228
- * The secrets are decrypted locally after retrieval.
229
- * This method only triggers a single network request to the ZeroKMS.
230
- *
231
- * API: GET /api/secrets/get-many?workspaceId=...&environment=...&names=name1,name2,...
232
- *
233
- * Constraints:
234
- * - Minimum 2 secret names required
235
- * - Maximum 100 secret names per request
236
- *
237
- * @param names - The names of the secrets to retrieve (min 2, max 100)
238
- * @returns A Result containing an object mapping secret names to their decrypted values
239
- */
240
- async getMany(names) {
241
- logger.debug(`Getting ${names.length} secrets.`);
242
- await this.ensureInitialized();
243
- if (!this.encryptionClient) {
244
- return {
245
- failure: {
246
- type: "ClientError",
247
- message: "Failed to initialize Encryption client"
248
- }
249
- };
250
- }
251
- if (names.length < 2) {
252
- return {
253
- failure: {
254
- type: "ClientError",
255
- message: "At least 2 secret names are required for getMany"
256
- }
257
- };
258
- }
259
- if (names.length > 100) {
260
- return {
261
- failure: {
262
- type: "ClientError",
263
- message: "Maximum 100 secret names per request"
264
- }
265
- };
266
- }
267
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
268
- const apiResult = await this.apiRequest(
269
- "GET",
270
- "/get-many",
271
- {
272
- params: {
273
- workspaceId,
274
- environment: this.config.environment,
275
- names: names.join(",")
276
- }
277
- }
278
- );
279
- if (apiResult.failure) {
280
- return apiResult;
281
- }
282
- const dataToDecrypt = apiResult.data.map((item) => ({
283
- name: item.name,
284
- value: item.encryptedValue.data
285
- }));
286
- const decryptResult = await this.encryptionClient.bulkDecryptModels(dataToDecrypt);
287
- if (decryptResult.failure) {
288
- logger.error(
289
- `Failed to decrypt secrets: ${decryptResult.failure.message}`
290
- );
291
- return {
292
- failure: {
293
- type: "DecryptionError",
294
- message: decryptResult.failure.message
295
- }
296
- };
297
- }
298
- const decryptedSecrets = decryptResult.data;
299
- const secretsMap = {};
300
- for (const secret of decryptedSecrets) {
301
- if (secret.name && secret.value) {
302
- secretsMap[secret.name] = secret.value;
303
- }
304
- }
305
- return { data: secretsMap };
306
- }
307
- /**
308
- * List all secrets in the environment.
309
- * Only names and metadata are returned; values remain encrypted.
310
- *
311
- * API: GET /api/secrets/list?workspaceId=...&environment=...
312
- *
313
- * @returns A Result containing the list of secrets or an error
314
- */
315
- async list() {
316
- logger.debug("Listing secrets.");
317
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
318
- const apiResult = await this.apiRequest(
319
- "GET",
320
- "/list",
321
- {
322
- params: {
323
- workspaceId,
324
- environment: this.config.environment
325
- }
326
- }
327
- );
328
- if (apiResult.failure) {
329
- return apiResult;
330
- }
331
- return { data: apiResult.data.secrets };
332
- }
333
- /**
334
- * Delete a secret from the vault.
335
- *
336
- * API: POST /api/secrets/delete
337
- *
338
- * @param name - The name of the secret to delete
339
- * @returns A Result containing the API response or an error
340
- */
341
- async delete(name) {
342
- logger.debug("Deleting secret");
343
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
344
- return await this.apiRequest("POST", "/delete", {
345
- body: {
346
- workspaceId,
347
- environment: this.config.environment,
348
- name
349
- }
350
- });
351
- }
352
- };
353
-
354
- export {
355
- Secrets
356
- };
357
- //# sourceMappingURL=chunk-EHDOOLWU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/secrets/index.ts"],"sourcesContent":["/**\n * Placeholder: Corrected Secrets client interface\n *\n * This file reflects the actual dashboard API endpoints as implemented in:\n * apps/dashboard/src/app/api/secrets/{get,set,list,get-many,delete}/route.ts\n *\n * Key corrections from the original interface:\n * 1. get, list, get-many are GET endpoints (not POST) with query params\n * 2. get-many takes a comma-separated `names` string (not a JSON array)\n * 3. set and delete return { success, message } (not void)\n * 4. SecretMetadata fields (id, createdAt, updatedAt) are non-optional\n * 5. GetSecretResponse fields (createdAt, updatedAt) are non-optional\n * 6. get-many enforces min 2 names (comma required) and max 100 names\n */\n\nimport { encryptedToPgComposite } from '@/encryption/helpers'\nimport type { EncryptionClient } from '@/encryption/index.js'\nimport { Encryption } from '@/index'\nimport { encryptedColumn, encryptedTable } from '@/schema'\nimport type { Encrypted } from '@/types'\nimport { logger } from '@/utils/logger'\nimport type { Result } from '@byteslice/result'\nimport { extractWorkspaceIdFromCrn } from '../utils/config/index.js'\n\nexport type SecretName = string\nexport type SecretValue = string\n\n/**\n * Discriminated error type for secrets operations.\n */\nexport type SecretsErrorType =\n | 'ApiError'\n | 'NetworkError'\n | 'ClientError'\n | 'EncryptionError'\n | 'DecryptionError'\n\n/**\n * Error returned by secrets operations.\n */\nexport interface SecretsError {\n type: SecretsErrorType\n message: string\n}\n\n/**\n * Configuration options for initializing the Stash client\n */\nexport interface SecretsConfig {\n environment: string\n workspaceCRN?: string\n clientId?: string\n clientKey?: string\n accessKey?: string\n}\n\n/**\n * Secret metadata returned from the API (list endpoint).\n * All fields are always present in API responses.\n */\nexport interface SecretMetadata {\n id: string\n name: string\n environment: string\n createdAt: string\n updatedAt: string\n}\n\n/**\n * API response for listing secrets.\n * GET /api/secrets/list?workspaceId=...&environment=...\n */\nexport interface ListSecretsResponse {\n environment: string\n secrets: SecretMetadata[]\n}\n\n/**\n * API response for getting a single secret.\n * GET /api/secrets/get?workspaceId=...&environment=...&name=...\n *\n * The `encryptedValue` is the raw value stored in the vault's `value` column,\n * which is the `{ data: Encrypted }` object that was passed to the set endpoint.\n */\nexport interface GetSecretResponse {\n name: string\n environment: string\n encryptedValue: {\n data: Encrypted\n }\n createdAt: string\n updatedAt: string\n}\n\n/**\n * API response for getting multiple secrets.\n * GET /api/secrets/get-many?workspaceId=...&environment=...&names=name1,name2,...\n *\n * Returns an array of GetSecretResponse objects.\n * Constraints:\n * - `names` must be comma-separated (minimum 2 names)\n * - Maximum 100 names per request\n */\nexport type GetManySecretsResponse = GetSecretResponse[]\n\n/**\n * API response for setting a secret.\n * POST /api/secrets/set\n */\nexport interface SetSecretResponse {\n success: true\n message: string\n}\n\n/**\n * API request body for setting a secret.\n * POST /api/secrets/set\n */\nexport interface SetSecretRequest {\n workspaceId: string\n environment: string\n name: string\n encryptedValue: {\n data: Encrypted\n }\n}\n\n/**\n * API response for deleting a secret.\n * POST /api/secrets/delete\n */\nexport interface DeleteSecretResponse {\n success: true\n message: string\n}\n\n/**\n * API request body for deleting a secret.\n * POST /api/secrets/delete\n */\nexport interface DeleteSecretRequest {\n workspaceId: string\n environment: string\n name: string\n}\n\n/**\n * API error response for plan limit violations (403).\n * Returned by POST /api/secrets/set when the workspace has reached its secret limit.\n */\nexport interface PlanLimitError {\n error: string\n code: 'PLAN_LIMIT_REACHED'\n}\n\nexport interface DecryptedSecretResponse {\n name: string\n environment: string\n value: string\n createdAt: string\n updatedAt: string\n}\n\n/**\n * The Secrets client provides a high-level API for managing encrypted secrets\n * stored in CipherStash. Secrets are encrypted locally before being sent to\n * the API, ensuring end-to-end encryption.\n */\nexport class Secrets {\n private encryptionClient: EncryptionClient | null = null\n private config: Required<SecretsConfig>\n private readonly apiBaseUrl =\n process.env.STASH_API_URL || 'https://dashboard.cipherstash.com/api/secrets'\n private readonly secretsSchema = encryptedTable('secrets', {\n value: encryptedColumn('value'),\n })\n\n constructor(config: SecretsConfig) {\n const workspaceCRN = config.workspaceCRN ?? process.env.CS_WORKSPACE_CRN\n const clientId = config.clientId ?? process.env.CS_CLIENT_ID\n const clientKey = config.clientKey ?? process.env.CS_CLIENT_KEY\n const accessKey = config.accessKey ?? process.env.CS_CLIENT_ACCESS_KEY\n\n if (!workspaceCRN || !clientId || !clientKey || !accessKey) {\n throw new Error(\n 'Missing required configuration or environment variables.',\n )\n }\n\n this.config = {\n environment: config.environment,\n workspaceCRN,\n clientId,\n clientKey,\n accessKey,\n }\n }\n\n private initPromise: Promise<void> | null = null\n\n /**\n * Initialize the Secrets client and underlying Encryption client\n */\n private async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this._doInit()\n }\n return this.initPromise\n }\n\n private async _doInit(): Promise<void> {\n logger.debug('Initializing the Secrets client.')\n\n this.encryptionClient = await Encryption({\n schemas: [this.secretsSchema],\n config: {\n workspaceCrn: this.config.workspaceCRN,\n clientId: this.config.clientId,\n clientKey: this.config.clientKey,\n accessKey: this.config.accessKey,\n keyset: { name: this.config.environment },\n },\n })\n\n logger.debug('Successfully initialized the Secrets client.')\n }\n\n /**\n * Get the authorization header for API requests\n */\n private getAuthHeader(): string {\n return `Bearer ${this.config.accessKey}`\n }\n\n /**\n * Make an API request with error handling.\n *\n * For GET requests, `params` are appended as URL query parameters.\n * For POST requests, `body` is sent as JSON in the request body.\n */\n private async apiRequest<T>(\n method: 'GET' | 'POST',\n path: string,\n options?: {\n body?: unknown\n params?: Record<string, string>\n },\n ): Promise<Result<T, SecretsError>> {\n try {\n let url = `${this.apiBaseUrl}${path}`\n\n if (options?.params) {\n const searchParams = new URLSearchParams(options.params)\n url = `${url}?${searchParams.toString()}`\n }\n\n logger.debug(`Secrets API request: ${method} ${path}`)\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: this.getAuthHeader(),\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n let errorMessage = `API request failed with status ${response.status}`\n try {\n const errorJson = JSON.parse(errorText)\n errorMessage = errorJson.message || errorJson.error || errorMessage\n } catch {\n errorMessage = errorText || errorMessage\n }\n\n logger.error(`Secrets API error on ${method} ${path}: ${errorMessage}`)\n\n return {\n failure: {\n type: 'ApiError',\n message: errorMessage,\n },\n }\n }\n\n logger.debug(`Secrets API request successful: ${method} ${path}`)\n\n const data = await response.json()\n return { data }\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : 'Unknown network error occurred'\n\n logger.error(`Secrets network error on ${method} ${path}: ${message}`)\n\n return {\n failure: {\n type: 'NetworkError',\n message,\n },\n }\n }\n }\n\n /**\n * Store an encrypted secret in the vault.\n * The value is encrypted locally before being sent to the API.\n *\n * API: POST /api/secrets/set\n *\n * @param name - The name of the secret\n * @param value - The plaintext value to encrypt and store\n * @returns A Result containing the API response or an error\n */\n async set(\n name: SecretName,\n value: SecretValue,\n ): Promise<Result<SetSecretResponse, SecretsError>> {\n logger.debug('Setting secret')\n\n await this.ensureInitialized()\n\n if (!this.encryptionClient) {\n return {\n failure: {\n type: 'ClientError',\n message: 'Failed to initialize Encryption client',\n },\n }\n }\n\n // Encrypt the value locally\n const encryptResult = await this.encryptionClient.encrypt(value, {\n column: this.secretsSchema.value,\n table: this.secretsSchema,\n })\n\n if (encryptResult.failure) {\n logger.error('Failed to encrypt secret')\n return {\n failure: {\n type: 'EncryptionError',\n message: encryptResult.failure.message,\n },\n }\n }\n\n // Extract workspaceId from CRN\n const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN)\n\n // Send encrypted value to API\n return await this.apiRequest<SetSecretResponse>('POST', '/set', {\n body: {\n workspaceId,\n environment: this.config.environment,\n name,\n encryptedValue: encryptedToPgComposite(encryptResult.data),\n },\n })\n }\n\n /**\n * Retrieve and decrypt a secret from the vault.\n * The secret is decrypted locally after retrieval.\n *\n * API: GET /api/secrets/get?workspaceId=...&environment=...&name=...\n *\n * @param name - The name of the secret to retrieve\n * @returns A Result containing the decrypted value or an error\n */\n async get(name: SecretName): Promise<Result<SecretValue, SecretsError>> {\n logger.debug('Getting secret')\n\n await this.ensureInitialized()\n\n if (!this.encryptionClient) {\n return {\n failure: {\n type: 'ClientError',\n message: 'Failed to initialize Encryption client',\n },\n }\n }\n\n // Extract workspaceId from CRN\n const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN)\n\n // Fetch encrypted value from API via GET with query params\n const apiResult = await this.apiRequest<GetSecretResponse>('GET', '/get', {\n params: {\n workspaceId,\n environment: this.config.environment,\n name,\n },\n })\n\n if (apiResult.failure) {\n return apiResult\n }\n\n // Decrypt the value locally\n const decryptResult = await this.encryptionClient.decrypt(\n apiResult.data.encryptedValue.data,\n )\n\n if (decryptResult.failure) {\n logger.error('Failed to decrypt secret')\n return {\n failure: {\n type: 'DecryptionError',\n message: decryptResult.failure.message,\n },\n }\n }\n\n if (typeof decryptResult.data !== 'string') {\n logger.error('Decrypted secret value is not a string')\n return {\n failure: {\n type: 'DecryptionError',\n message: 'Decrypted value is not a string',\n },\n }\n }\n\n return { data: decryptResult.data }\n }\n\n /**\n * Retrieve and decrypt many secrets from the vault.\n * The secrets are decrypted locally after retrieval.\n * This method only triggers a single network request to the ZeroKMS.\n *\n * API: GET /api/secrets/get-many?workspaceId=...&environment=...&names=name1,name2,...\n *\n * Constraints:\n * - Minimum 2 secret names required\n * - Maximum 100 secret names per request\n *\n * @param names - The names of the secrets to retrieve (min 2, max 100)\n * @returns A Result containing an object mapping secret names to their decrypted values\n */\n async getMany(\n names: SecretName[],\n ): Promise<Result<Record<SecretName, SecretValue>, SecretsError>> {\n logger.debug(`Getting ${names.length} secrets.`)\n\n await this.ensureInitialized()\n\n if (!this.encryptionClient) {\n return {\n failure: {\n type: 'ClientError',\n message: 'Failed to initialize Encryption client',\n },\n }\n }\n\n if (names.length < 2) {\n return {\n failure: {\n type: 'ClientError',\n message: 'At least 2 secret names are required for getMany',\n },\n }\n }\n\n if (names.length > 100) {\n return {\n failure: {\n type: 'ClientError',\n message: 'Maximum 100 secret names per request',\n },\n }\n }\n\n // Extract workspaceId from CRN\n const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN)\n\n // Fetch encrypted values from API via GET with comma-separated names\n const apiResult = await this.apiRequest<GetManySecretsResponse>(\n 'GET',\n '/get-many',\n {\n params: {\n workspaceId,\n environment: this.config.environment,\n names: names.join(','),\n },\n },\n )\n\n if (apiResult.failure) {\n return apiResult\n }\n\n const dataToDecrypt = apiResult.data.map((item) => ({\n name: item.name,\n value: item.encryptedValue.data,\n }))\n\n const decryptResult =\n await this.encryptionClient.bulkDecryptModels(dataToDecrypt)\n\n if (decryptResult.failure) {\n logger.error(\n `Failed to decrypt secrets: ${decryptResult.failure.message}`,\n )\n return {\n failure: {\n type: 'DecryptionError',\n message: decryptResult.failure.message,\n },\n }\n }\n\n // Transform array of decrypted secrets into an object keyed by secret name\n const decryptedSecrets =\n decryptResult.data as unknown as DecryptedSecretResponse[]\n const secretsMap: Record<SecretName, SecretValue> = {}\n\n for (const secret of decryptedSecrets) {\n if (secret.name && secret.value) {\n secretsMap[secret.name] = secret.value\n }\n }\n\n return { data: secretsMap }\n }\n\n /**\n * List all secrets in the environment.\n * Only names and metadata are returned; values remain encrypted.\n *\n * API: GET /api/secrets/list?workspaceId=...&environment=...\n *\n * @returns A Result containing the list of secrets or an error\n */\n async list(): Promise<Result<SecretMetadata[], SecretsError>> {\n logger.debug('Listing secrets.')\n\n // Extract workspaceId from CRN\n const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN)\n\n const apiResult = await this.apiRequest<ListSecretsResponse>(\n 'GET',\n '/list',\n {\n params: {\n workspaceId,\n environment: this.config.environment,\n },\n },\n )\n\n if (apiResult.failure) {\n return apiResult\n }\n\n return { data: apiResult.data.secrets }\n }\n\n /**\n * Delete a secret from the vault.\n *\n * API: POST /api/secrets/delete\n *\n * @param name - The name of the secret to delete\n * @returns A Result containing the API response or an error\n */\n async delete(\n name: SecretName,\n ): Promise<Result<DeleteSecretResponse, SecretsError>> {\n logger.debug('Deleting secret')\n\n // Extract workspaceId from CRN\n const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN)\n\n return await this.apiRequest<DeleteSecretResponse>('POST', '/delete', {\n body: {\n workspaceId,\n environment: this.config.environment,\n name,\n },\n })\n }\n}\n"],"mappings":"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","names":[]}