@cipherstash/stack 0.3.0 → 0.5.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 (93) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +10 -37
  3. package/dist/bin/stash.js +261 -320
  4. package/dist/bin/stash.js.map +1 -1
  5. package/dist/{chunk-SUYMGQBY.js → chunk-4RNBI3UH.js} +1 -7
  6. package/dist/chunk-4RNBI3UH.js.map +1 -0
  7. package/dist/{chunk-OAPLZLR5.js → chunk-GXGEW6T4.js} +2 -76
  8. package/dist/chunk-GXGEW6T4.js.map +1 -0
  9. package/dist/{chunk-JLI27P46.js → chunk-HCL75LXQ.js} +1 -1
  10. package/dist/chunk-HCL75LXQ.js.map +1 -0
  11. package/dist/chunk-LBMC4D6D.js +19 -0
  12. package/dist/chunk-LBMC4D6D.js.map +1 -0
  13. package/dist/chunk-Q5FTQLYG.js +68 -0
  14. package/dist/chunk-Q5FTQLYG.js.map +1 -0
  15. package/dist/{chunk-MW6D52V2.js → chunk-TGUELLJS.js} +58 -150
  16. package/dist/chunk-TGUELLJS.js.map +1 -0
  17. package/dist/{chunk-TBAIVO6T.js → chunk-Y35W4WFN.js} +6 -6
  18. package/dist/chunk-Y35W4WFN.js.map +1 -0
  19. package/dist/chunk-YLISU7FD.js +357 -0
  20. package/dist/chunk-YLISU7FD.js.map +1 -0
  21. package/dist/{client-Bf0Xw2xo.d.cts → client-BFrHTgC6.d.ts} +50 -18
  22. package/dist/{client-Kfp8OsPB.d.ts → client-BVQvgvdY.d.cts} +50 -18
  23. package/dist/client.cjs +5 -5
  24. package/dist/client.cjs.map +1 -1
  25. package/dist/client.d.cts +4 -4
  26. package/dist/client.d.ts +4 -4
  27. package/dist/client.js +1 -1
  28. package/dist/drizzle/index.cjs +5 -5
  29. package/dist/drizzle/index.cjs.map +1 -1
  30. package/dist/drizzle/index.d.cts +14 -6
  31. package/dist/drizzle/index.d.ts +14 -6
  32. package/dist/drizzle/index.js +4 -4
  33. package/dist/drizzle/index.js.map +1 -1
  34. package/dist/dynamodb/index.cjs +68 -0
  35. package/dist/dynamodb/index.cjs.map +1 -1
  36. package/dist/dynamodb/index.d.cts +4 -4
  37. package/dist/dynamodb/index.d.ts +4 -4
  38. package/dist/dynamodb/index.js +9 -0
  39. package/dist/dynamodb/index.js.map +1 -1
  40. package/dist/encryption/index.cjs +2572 -0
  41. package/dist/encryption/index.cjs.map +1 -0
  42. package/dist/encryption/index.d.cts +7 -0
  43. package/dist/encryption/index.d.ts +7 -0
  44. package/dist/encryption/index.js +17 -0
  45. package/dist/encryption/index.js.map +1 -0
  46. package/dist/errors/index.cjs +44 -0
  47. package/dist/errors/index.cjs.map +1 -0
  48. package/dist/errors/index.d.cts +94 -0
  49. package/dist/errors/index.d.ts +94 -0
  50. package/dist/errors/index.js +9 -0
  51. package/dist/errors/index.js.map +1 -0
  52. package/dist/identity/index.cjs +21 -13
  53. package/dist/identity/index.cjs.map +1 -1
  54. package/dist/identity/index.d.cts +78 -2
  55. package/dist/identity/index.d.ts +78 -2
  56. package/dist/identity/index.js +7 -3
  57. package/dist/identity/index.js.map +1 -1
  58. package/dist/index.cjs +425 -182
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts +21 -19
  61. package/dist/index.d.ts +21 -19
  62. package/dist/index.js +15 -9
  63. package/dist/schema/index.cjs +5 -5
  64. package/dist/schema/index.cjs.map +1 -1
  65. package/dist/schema/index.d.cts +1 -2
  66. package/dist/schema/index.d.ts +1 -2
  67. package/dist/schema/index.js +1 -1
  68. package/dist/secrets/index.cjs +116 -180
  69. package/dist/secrets/index.cjs.map +1 -1
  70. package/dist/secrets/index.d.cts +4 -6
  71. package/dist/secrets/index.d.ts +4 -6
  72. package/dist/secrets/index.js +9 -318
  73. package/dist/secrets/index.js.map +1 -1
  74. package/dist/supabase/index.cjs +86 -3
  75. package/dist/supabase/index.cjs.map +1 -1
  76. package/dist/supabase/index.d.cts +4 -4
  77. package/dist/supabase/index.d.ts +4 -4
  78. package/dist/supabase/index.js +26 -3
  79. package/dist/supabase/index.js.map +1 -1
  80. package/dist/{types-public-0CzBV45X.d.cts → types-public-dKxDzTIL.d.cts} +47 -34
  81. package/dist/{types-public-0CzBV45X.d.ts → types-public-dKxDzTIL.d.ts} +47 -34
  82. package/dist/types-public.cjs.map +1 -1
  83. package/dist/types-public.d.cts +1 -2
  84. package/dist/types-public.d.ts +1 -2
  85. package/dist/types-public.js +1 -1
  86. package/package.json +30 -2
  87. package/dist/chunk-JLI27P46.js.map +0 -1
  88. package/dist/chunk-MW6D52V2.js.map +0 -1
  89. package/dist/chunk-OAPLZLR5.js.map +0 -1
  90. package/dist/chunk-SUYMGQBY.js.map +0 -1
  91. package/dist/chunk-TBAIVO6T.js.map +0 -1
  92. package/dist/index-9-Ya3fDK.d.cts +0 -169
  93. package/dist/index-9-Ya3fDK.d.ts +0 -169
@@ -0,0 +1,357 @@
1
+ import {
2
+ Encryption
3
+ } from "./chunk-TGUELLJS.js";
4
+ import {
5
+ encryptedToPgComposite
6
+ } from "./chunk-4RNBI3UH.js";
7
+ import {
8
+ encryptedColumn,
9
+ encryptedTable
10
+ } from "./chunk-Y35W4WFN.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-YLISU7FD.js.map
@@ -0,0 +1 @@
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":[]}
@@ -1,7 +1,8 @@
1
- import { l as EncryptedQueryResult, m as Client, S as ScalarQueryTerm, B as BulkDecryptedData, n as BulkDecryptPayload, D as Decrypted, o as BulkEncryptedData, p as BulkEncryptPayload, q as EncryptOptions, d as EncryptedColumn, h as EncryptedField, f as EncryptedTable, g as EncryptedTableColumn, j as Encrypted, r as EncryptQueryOptions, Q as QueryTypeName, s as EncryptedReturnType, t as EncryptConfig, K as KeysetIdentifier, i as EncryptedFromSchema } from './types-public-0CzBV45X.cjs';
2
- import { a as EncryptionError, d as LockContext } from './index-9-Ya3fDK.cjs';
1
+ import { h as EncryptedQueryResult, i as Client, S as ScalarQueryTerm, B as BulkDecryptedData, j as BulkDecryptPayload, D as Decrypted, k as BulkEncryptedData, l as BulkEncryptPayload, m as EncryptOptions, f as EncryptedColumn, n as EncryptedField, c as EncryptedTable, d as EncryptedTableColumn, E as Encrypted, o as EncryptQueryOptions, Q as QueryTypeName, p as EncryptedReturnType, q as EncryptConfig, K as KeysetIdentifier, r as EncryptedFromSchema, s as EncryptionClientConfig } from './types-public-dKxDzTIL.js';
2
+ import { EncryptionError } from './errors/index.js';
3
3
  import { Result } from '@byteslice/result';
4
4
  import { JsPlaintext } from '@cipherstash/protect-ffi';
5
+ import { LockContext } from './identity/index.js';
5
6
 
6
7
  type AuditConfig = {
7
8
  metadata?: Record<string, unknown>;
@@ -134,23 +135,23 @@ declare class BulkEncryptModelsOperationWithLockContext<T extends Record<string,
134
135
  * Decrypts an encrypted payload using the provided client.
135
136
  * This is the type returned by the {@link EncryptionClient.decrypt | decrypt} method of the {@link EncryptionClient}.
136
137
  */
137
- declare class DecryptOperation extends EncryptionOperation<JsPlaintext | null> {
138
+ declare class DecryptOperation extends EncryptionOperation<JsPlaintext> {
138
139
  private client;
139
140
  private encryptedData;
140
141
  constructor(client: Client, encryptedData: Encrypted);
141
142
  withLockContext(lockContext: LockContext): DecryptOperationWithLockContext;
142
- execute(): Promise<Result<JsPlaintext | null, EncryptionError>>;
143
+ execute(): Promise<Result<JsPlaintext, EncryptionError>>;
143
144
  getOperation(): {
144
145
  client: Client;
145
146
  encryptedData: Encrypted;
146
147
  auditData?: Record<string, unknown>;
147
148
  };
148
149
  }
149
- declare class DecryptOperationWithLockContext extends EncryptionOperation<JsPlaintext | null> {
150
+ declare class DecryptOperationWithLockContext extends EncryptionOperation<JsPlaintext> {
150
151
  private operation;
151
152
  private lockContext;
152
153
  constructor(operation: DecryptOperation, lockContext: LockContext);
153
- execute(): Promise<Result<JsPlaintext | null, EncryptionError>>;
154
+ execute(): Promise<Result<JsPlaintext, EncryptionError>>;
154
155
  }
155
156
 
156
157
  declare class DecryptModelOperation<T extends Record<string, unknown>> extends EncryptionOperation<Decrypted<T>> {
@@ -176,12 +177,12 @@ declare class EncryptOperation extends EncryptionOperation<Encrypted> {
176
177
  private plaintext;
177
178
  private column;
178
179
  private table;
179
- constructor(client: Client, plaintext: JsPlaintext | null, opts: EncryptOptions);
180
+ constructor(client: Client, plaintext: JsPlaintext, opts: EncryptOptions);
180
181
  withLockContext(lockContext: LockContext): EncryptOperationWithLockContext;
181
182
  execute(): Promise<Result<Encrypted, EncryptionError>>;
182
183
  getOperation(): {
183
184
  client: Client;
184
- plaintext: JsPlaintext | null;
185
+ plaintext: JsPlaintext;
185
186
  column: EncryptedColumn | EncryptedField;
186
187
  table: EncryptedTable<EncryptedTableColumn>;
187
188
  };
@@ -220,7 +221,7 @@ declare class EncryptQueryOperation extends EncryptionOperation<EncryptedQueryRe
220
221
  private client;
221
222
  private plaintext;
222
223
  private opts;
223
- constructor(client: Client, plaintext: JsPlaintext | null, opts: EncryptQueryOptions);
224
+ constructor(client: Client, plaintext: JsPlaintext, opts: EncryptQueryOptions);
224
225
  withLockContext(lockContext: LockContext): EncryptQueryOperationWithLockContext;
225
226
  execute(): Promise<Result<EncryptedQueryResult, EncryptionError>>;
226
227
  getOperation(): {
@@ -229,7 +230,7 @@ declare class EncryptQueryOperation extends EncryptionOperation<EncryptedQueryRe
229
230
  queryType?: QueryTypeName;
230
231
  returnType?: EncryptedReturnType;
231
232
  client: Client;
232
- plaintext: JsPlaintext | null;
233
+ plaintext: JsPlaintext;
233
234
  };
234
235
  }
235
236
  /**
@@ -240,10 +241,11 @@ declare class EncryptQueryOperationWithLockContext extends EncryptionOperation<E
240
241
  private plaintext;
241
242
  private opts;
242
243
  private lockContext;
243
- constructor(client: Client, plaintext: JsPlaintext | null, opts: EncryptQueryOptions, lockContext: LockContext, auditMetadata?: Record<string, unknown>);
244
+ constructor(client: Client, plaintext: JsPlaintext, opts: EncryptQueryOptions, lockContext: LockContext, auditMetadata?: Record<string, unknown>);
244
245
  execute(): Promise<Result<EncryptedQueryResult, EncryptionError>>;
245
246
  }
246
247
 
248
+ declare const noClientError: () => Error;
247
249
  /** The EncryptionClient is the main entry point for interacting with the CipherStash Encryption library.
248
250
  * It provides methods for encrypting and decrypting individual values, as well as models (objects) and bulk operations.
249
251
  *
@@ -271,7 +273,7 @@ declare class EncryptionClient {
271
273
  /**
272
274
  * Encrypt a value - returns a promise which resolves to an encrypted value.
273
275
  *
274
- * @param plaintext - The plaintext value to be encrypted. Can be null.
276
+ * @param plaintext - The plaintext value to be encrypted.
275
277
  * @param opts - Options specifying the column (or nested field) and table for encryption. See {@link EncryptOptions}.
276
278
  * @returns An EncryptOperation that can be awaited or chained with additional methods.
277
279
  *
@@ -343,11 +345,11 @@ declare class EncryptionClient {
343
345
  * @see {@link LockContext}
344
346
  * @see {@link EncryptOperation}
345
347
  */
346
- encrypt(plaintext: JsPlaintext | null, opts: EncryptOptions): EncryptOperation;
348
+ encrypt(plaintext: JsPlaintext, opts: EncryptOptions): EncryptOperation;
347
349
  /**
348
350
  * Encrypt a query value - returns a promise which resolves to an encrypted query value.
349
351
  *
350
- * @param plaintext - The plaintext value to be encrypted for querying. Can be null.
352
+ * @param plaintext - The plaintext value to be encrypted for querying.
351
353
  * @param opts - Options specifying the column, table, and optional queryType for encryption.
352
354
  * @returns An EncryptQueryOperation that can be awaited or chained with additional methods.
353
355
  *
@@ -397,7 +399,7 @@ declare class EncryptionClient {
397
399
  * - String plaintext → `steVecSelector` (JSONPath queries like `'$.user.email'`)
398
400
  * - Object/Array plaintext → `steVecTerm` (containment queries like `{ role: 'admin' }`)
399
401
  */
400
- encryptQuery(plaintext: JsPlaintext | null, opts: EncryptQueryOptions): EncryptQueryOperation;
402
+ encryptQuery(plaintext: JsPlaintext, opts: EncryptQueryOptions): EncryptQueryOperation;
401
403
  /**
402
404
  * Encrypt multiple values for use in queries (batch operation).
403
405
  * @param terms - Array of query terms to encrypt
@@ -592,7 +594,7 @@ declare class EncryptionClient {
592
594
  *
593
595
  * Each value is encrypted with its own unique key via a single call to ZeroKMS.
594
596
  * Values can include optional `id` fields for correlating results back to
595
- * your application data. Null plaintext values are preserved as null.
597
+ * your application data.
596
598
  *
597
599
  * @param plaintexts - An array of objects with `plaintext` (and optional `id`) fields.
598
600
  * @param opts - Options specifying the target column (or nested {@link encryptedField}) and table. See {@link EncryptOptions}.
@@ -613,7 +615,6 @@ declare class EncryptionClient {
613
615
  * [
614
616
  * { id: "u1", plaintext: "alice@example.com" },
615
617
  * { id: "u2", plaintext: "bob@example.com" },
616
- * { id: "u3", plaintext: null },
617
618
  * ],
618
619
  * { column: users.email, table: users },
619
620
  * )
@@ -661,5 +662,36 @@ declare class EncryptionClient {
661
662
  workspaceId: string | undefined;
662
663
  };
663
664
  }
665
+ /**
666
+ * Creates and initializes an Encryption client for encrypting and decrypting data with CipherStash.
667
+ *
668
+ * Provide at least one schema (from {@link encryptedTable}) so the client knows which tables and
669
+ * columns to use. Credentials are read from the optional `config` or from the environment
670
+ * (`CS_WORKSPACE_CRN`, `CS_CLIENT_ID`, `CS_CLIENT_KEY`, `CS_CLIENT_ACCESS_KEY`).
671
+ *
672
+ * @param config - Initialization options. Must include `schemas`; optionally include `config` for
673
+ * workspace/keys. Logging is configured via the `STASH_STACK_LOG` environment variable
674
+ * (`debug | info | error`, default: `error`).
675
+ * @returns A Promise that resolves to an initialized {@link EncryptionClient} ready for
676
+ * {@link EncryptionClient.encrypt}, {@link EncryptionClient.decrypt}, and related operations.
677
+ *
678
+ * @throws Throws if `schemas` is empty, or if a keyset `id` is supplied but is not a valid UUID.
679
+ * Also throws if {@link EncryptionClient.init} fails (e.g. invalid credentials or config).
680
+ *
681
+ * @example
682
+ * ```typescript
683
+ * import { Encryption, encryptedTable, encryptedColumn } from "@cipherstash/stack"
684
+ *
685
+ * const users = encryptedTable("users", {
686
+ * email: encryptedColumn("email"),
687
+ * })
688
+ * const client = await Encryption({ schemas: [users] })
689
+ * const result = await client.encrypt("alice@example.com", { column: users.email, table: users })
690
+ * ```
691
+ *
692
+ * @see {@link EncryptionClientConfig} for full config options.
693
+ * @see {@link EncryptionClient} for available methods after initialization.
694
+ */
695
+ declare const Encryption: (config: EncryptionClientConfig) => Promise<EncryptionClient>;
664
696
 
665
- export { type AuditConfig as A, EncryptionClient as E };
697
+ export { type AuditConfig as A, Encryption as E, EncryptionClient as a, noClientError as n };