@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
@@ -1,7 +1,7 @@
1
1
  import '../errors/index.cjs';
2
- import '../types-public-WADaA-TW.cjs';
2
+ import '../types-public-DX3mGqoi.cjs';
3
3
  import '@byteslice/result';
4
4
  import '@cipherstash/protect-ffi';
5
- export { E as Encryption, a as EncryptionClient, n as noClientError } from '../client-bW-5wG9Y.cjs';
5
+ export { E as Encryption, a as EncryptionClient, n as noClientError } from '../client-DQyqn0oT.cjs';
6
6
  import 'zod';
7
7
  import '../identity/index.cjs';
@@ -1,7 +1,7 @@
1
1
  import '../errors/index.js';
2
- import '../types-public-WADaA-TW.js';
2
+ import '../types-public-DX3mGqoi.js';
3
3
  import '@byteslice/result';
4
4
  import '@cipherstash/protect-ffi';
5
- export { E as Encryption, a as EncryptionClient, n as noClientError } from '../client-CZLBiToT.js';
5
+ export { E as Encryption, a as EncryptionClient, n as noClientError } from '../client-Dv60lAyy.js';
6
6
  import 'zod';
7
7
  import '../identity/index.js';
@@ -2,10 +2,10 @@ import {
2
2
  Encryption,
3
3
  EncryptionClient,
4
4
  noClientError
5
- } from "../chunk-O4MBO4DJ.js";
5
+ } from "../chunk-YPAPL3IC.js";
6
6
  import "../chunk-4RNBI3UH.js";
7
7
  import "../chunk-GNLU3I27.js";
8
- import "../chunk-46PDK2Z6.js";
8
+ import "../chunk-MD6742R6.js";
9
9
  import "../chunk-Q5FTQLYG.js";
10
10
  import "../chunk-LBMC4D6D.js";
11
11
  export {
@@ -1,6 +1,3 @@
1
- import {
2
- loadWorkSpaceId
3
- } from "../chunk-GXGEW6T4.js";
4
1
  import {
5
2
  logger
6
3
  } from "../chunk-Q5FTQLYG.js";
@@ -8,6 +5,69 @@ import {
8
5
  EncryptionErrorTypes
9
6
  } from "../chunk-LBMC4D6D.js";
10
7
 
8
+ // src/utils/config/index.ts
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ function getWorkspaceCrn(tomlString) {
12
+ let currentSection = "";
13
+ let workspaceCrn;
14
+ const lines = tomlString.split(/\r?\n/);
15
+ for (const line of lines) {
16
+ const trimmedLine = line.trim();
17
+ if (!trimmedLine || trimmedLine.startsWith("#")) {
18
+ continue;
19
+ }
20
+ const sectionMatch = trimmedLine.match(/^\[([^\]]+)\]$/);
21
+ if (sectionMatch) {
22
+ currentSection = sectionMatch[1];
23
+ continue;
24
+ }
25
+ const kvMatch = trimmedLine.match(/^(\w+)\s*=\s*"([^"]+)"$/);
26
+ if (kvMatch) {
27
+ const [_, key, value] = kvMatch;
28
+ if (currentSection === "auth" && key === "workspace_crn") {
29
+ workspaceCrn = value;
30
+ break;
31
+ }
32
+ }
33
+ }
34
+ return workspaceCrn;
35
+ }
36
+ function extractWorkspaceIdFromCrn(crn) {
37
+ const match = crn.match(/crn:[^:]+:([^:]+)$/);
38
+ if (!match) {
39
+ throw new Error("Invalid CRN format");
40
+ }
41
+ return match[1];
42
+ }
43
+ function loadWorkSpaceId(suppliedCrn) {
44
+ const configPath = path.join(process.cwd(), "cipherstash.toml");
45
+ if (suppliedCrn) {
46
+ return extractWorkspaceIdFromCrn(suppliedCrn);
47
+ }
48
+ if (!fs.existsSync(configPath) && !process.env.CS_WORKSPACE_CRN) {
49
+ throw new Error(
50
+ "You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable."
51
+ );
52
+ }
53
+ if (process.env.CS_WORKSPACE_CRN) {
54
+ return extractWorkspaceIdFromCrn(process.env.CS_WORKSPACE_CRN);
55
+ }
56
+ if (!fs.existsSync(configPath)) {
57
+ throw new Error(
58
+ "You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable."
59
+ );
60
+ }
61
+ const tomlString = fs.readFileSync(configPath, "utf8");
62
+ const workspaceCrn = getWorkspaceCrn(tomlString);
63
+ if (!workspaceCrn) {
64
+ throw new Error(
65
+ "You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable."
66
+ );
67
+ }
68
+ return extractWorkspaceIdFromCrn(workspaceCrn);
69
+ }
70
+
11
71
  // src/identity/index.ts
12
72
  import { withResult } from "@byteslice/result";
13
73
  var LockContext = class {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/identity/index.ts"],"sourcesContent":["import { type EncryptionError, EncryptionErrorTypes } from '@/errors'\nimport { loadWorkSpaceId } from '@/utils/config'\nimport { logger } from '@/utils/logger'\nimport { type Result, withResult } from '@byteslice/result'\n\nexport type CtsRegions = 'ap-southeast-2'\n\nexport type IdentifyOptions = {\n fetchFromCts?: boolean\n}\n\nexport type CtsToken = {\n accessToken: string\n expiry: number\n}\n\nexport type Context = {\n identityClaim: string[]\n}\n\nexport type LockContextOptions = {\n context?: Context\n ctsToken?: CtsToken\n}\n\nexport type GetLockContextResponse = {\n ctsToken: CtsToken\n context: Context\n}\n\n/**\n * Manages CipherStash lock contexts for row-level access control.\n *\n * A `LockContext` ties encryption/decryption operations to an authenticated\n * user identity via CTS (CipherStash Token Service). Call {@link identify}\n * with a user's JWT to obtain a CTS token, then pass the `LockContext`\n * to `.withLockContext()` on any encrypt/decrypt operation.\n *\n * @example\n * ```typescript\n * import { LockContext } from \"@cipherstash/stack/identity\"\n *\n * const lc = new LockContext()\n * const identified = await lc.identify(userJwt)\n *\n * if (identified.failure) throw new Error(identified.failure.message)\n *\n * const result = await client\n * .encrypt(value, { column: users.email, table: users })\n * .withLockContext(identified.data)\n * ```\n */\nexport class LockContext {\n private ctsToken: CtsToken | undefined\n private workspaceId: string\n private context: Context\n\n constructor({\n context = { identityClaim: ['sub'] },\n ctsToken,\n }: LockContextOptions = {}) {\n const workspaceId = loadWorkSpaceId()\n\n if (!workspaceId) {\n throw new Error(\n 'You have not defined a workspace ID in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n if (ctsToken) {\n this.ctsToken = ctsToken\n }\n\n this.workspaceId = workspaceId\n this.context = context\n logger.debug('Successfully initialized the EQL lock context.')\n }\n\n /**\n * Exchange a user's JWT for a CTS token and bind it to this lock context.\n *\n * @param jwtToken - A valid OIDC / JWT token for the current user.\n * @returns A `Result` containing this `LockContext` (now authenticated) or an error.\n *\n * @example\n * ```typescript\n * const lc = new LockContext()\n * const result = await lc.identify(userJwt)\n * if (result.failure) {\n * console.error(\"Auth failed:\", result.failure.message)\n * }\n * ```\n */\n async identify(\n jwtToken: string,\n ): Promise<Result<LockContext, EncryptionError>> {\n const workspaceId = this.workspaceId\n\n const ctsEndpoint =\n process.env.CS_CTS_ENDPOINT ||\n 'https://ap-southeast-2.aws.auth.viturhosted.net'\n\n const ctsFetchResult = await withResult(\n () =>\n fetch(`${ctsEndpoint}/api/authorize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n workspaceId,\n oidcToken: jwtToken,\n }),\n }),\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n if (ctsFetchResult.failure) {\n return ctsFetchResult\n }\n\n const identifiedLockContext = await withResult(\n async () => {\n const ctsToken = (await ctsFetchResult.data.json()) as CtsToken\n\n if (!ctsToken.accessToken) {\n throw new Error(\n 'The response from the CipherStash API did not contain an access token. Please contact support.',\n )\n }\n\n this.ctsToken = ctsToken\n return this\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n return identifiedLockContext\n }\n\n /**\n * Retrieve the current CTS token and context for use with encryption operations.\n *\n * Must be called after {@link identify}. Returns the token/context pair that\n * `.withLockContext()` expects.\n *\n * @returns A `Result` containing the CTS token and identity context, or an error\n * if {@link identify} has not been called.\n */\n getLockContext(): Promise<Result<GetLockContextResponse, EncryptionError>> {\n return withResult(\n () => {\n if (!this.ctsToken?.accessToken || !this.ctsToken?.expiry) {\n throw new Error(\n 'The CTS token is not set. Please call identify() with a users JWT token, or pass an existing CTS token to the LockContext constructor before calling getLockContext().',\n )\n }\n\n return {\n context: this.context,\n ctsToken: this.ctsToken,\n }\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n }\n}\n"],"mappings":";;;;;;;;;;;AAGA,SAAsB,kBAAkB;AAiDjC,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY;AAAA,IACV,UAAU,EAAE,eAAe,CAAC,KAAK,EAAE;AAAA,IACnC;AAAA,EACF,IAAwB,CAAC,GAAG;AAC1B,UAAM,cAAc,gBAAgB;AAEpC,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,WAAK,WAAW;AAAA,IAClB;AAEA,SAAK,cAAc;AACnB,SAAK,UAAU;AACf,WAAO,MAAM,gDAAgD;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,SACJ,UAC+C;AAC/C,UAAM,cAAc,KAAK;AAEzB,UAAM,cACJ,QAAQ,IAAI,mBACZ;AAEF,UAAM,iBAAiB,MAAM;AAAA,MAC3B,MACE,MAAM,GAAG,WAAW,kBAAkB;AAAA,QACpC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH,CAAC;AAAA,MACH,CAAC,WAAW;AAAA,QACV,MAAM,qBAAqB;AAAA,QAC3B,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,QAAI,eAAe,SAAS;AAC1B,aAAO;AAAA,IACT;AAEA,UAAM,wBAAwB,MAAM;AAAA,MAClC,YAAY;AACV,cAAM,WAAY,MAAM,eAAe,KAAK,KAAK;AAEjD,YAAI,CAAC,SAAS,aAAa;AACzB,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,aAAK,WAAW;AAChB,eAAO;AAAA,MACT;AAAA,MACA,CAAC,WAAW;AAAA,QACV,MAAM,qBAAqB;AAAA,QAC3B,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,iBAA2E;AACzE,WAAO;AAAA,MACL,MAAM;AACJ,YAAI,CAAC,KAAK,UAAU,eAAe,CAAC,KAAK,UAAU,QAAQ;AACzD,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS,KAAK;AAAA,UACd,UAAU,KAAK;AAAA,QACjB;AAAA,MACF;AAAA,MACA,CAAC,WAAW;AAAA,QACV,MAAM,qBAAqB;AAAA,QAC3B,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/config/index.ts","../../src/identity/index.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\n\n/**\n * A lightweight function that parses a TOML-like string\n * and returns the `workspace_crn` value found under `[auth]`.\n *\n * @param tomlString The contents of the TOML file as a string.\n * @returns The workspace_crn if found, otherwise undefined.\n */\nfunction getWorkspaceCrn(tomlString: string): string | undefined {\n let currentSection = ''\n let workspaceCrn: string | undefined\n\n const lines = tomlString.split(/\\r?\\n/)\n\n for (const line of lines) {\n const trimmedLine = line.trim()\n\n if (!trimmedLine || trimmedLine.startsWith('#')) {\n continue\n }\n\n const sectionMatch = trimmedLine.match(/^\\[([^\\]]+)\\]$/)\n if (sectionMatch) {\n currentSection = sectionMatch[1]\n continue\n }\n\n const kvMatch = trimmedLine.match(/^(\\w+)\\s*=\\s*\"([^\"]+)\"$/)\n if (kvMatch) {\n const [_, key, value] = kvMatch\n\n if (currentSection === 'auth' && key === 'workspace_crn') {\n workspaceCrn = value\n break\n }\n }\n }\n\n return workspaceCrn\n}\n\n/**\n * Extracts the workspace ID from a CRN string.\n * CRN format: crn:region.aws:ID\n *\n * @param crn The CRN string to extract from\n * @returns The workspace ID portion of the CRN\n */\nexport function extractWorkspaceIdFromCrn(crn: string): string {\n const match = crn.match(/crn:[^:]+:([^:]+)$/)\n if (!match) {\n throw new Error('Invalid CRN format')\n }\n return match[1]\n}\n\nexport function loadWorkSpaceId(suppliedCrn?: string): string {\n const configPath = path.join(process.cwd(), 'cipherstash.toml')\n\n if (suppliedCrn) {\n return extractWorkspaceIdFromCrn(suppliedCrn)\n }\n\n if (!fs.existsSync(configPath) && !process.env.CS_WORKSPACE_CRN) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n // Environment variables take precedence over config files\n if (process.env.CS_WORKSPACE_CRN) {\n return extractWorkspaceIdFromCrn(process.env.CS_WORKSPACE_CRN)\n }\n\n if (!fs.existsSync(configPath)) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n const tomlString = fs.readFileSync(configPath, 'utf8')\n const workspaceCrn = getWorkspaceCrn(tomlString)\n\n if (!workspaceCrn) {\n throw new Error(\n 'You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n return extractWorkspaceIdFromCrn(workspaceCrn)\n}\n","import { type EncryptionError, EncryptionErrorTypes } from '@/errors'\nimport { loadWorkSpaceId } from '@/utils/config'\nimport { logger } from '@/utils/logger'\nimport { type Result, withResult } from '@byteslice/result'\n\nexport type CtsRegions = 'ap-southeast-2'\n\nexport type IdentifyOptions = {\n fetchFromCts?: boolean\n}\n\nexport type CtsToken = {\n accessToken: string\n expiry: number\n}\n\nexport type Context = {\n identityClaim: string[]\n}\n\nexport type LockContextOptions = {\n context?: Context\n ctsToken?: CtsToken\n}\n\nexport type GetLockContextResponse = {\n ctsToken: CtsToken\n context: Context\n}\n\n/**\n * Manages CipherStash lock contexts for row-level access control.\n *\n * A `LockContext` ties encryption/decryption operations to an authenticated\n * user identity via CTS (CipherStash Token Service). Call {@link identify}\n * with a user's JWT to obtain a CTS token, then pass the `LockContext`\n * to `.withLockContext()` on any encrypt/decrypt operation.\n *\n * @example\n * ```typescript\n * import { LockContext } from \"@cipherstash/stack/identity\"\n *\n * const lc = new LockContext()\n * const identified = await lc.identify(userJwt)\n *\n * if (identified.failure) throw new Error(identified.failure.message)\n *\n * const result = await client\n * .encrypt(value, { column: users.email, table: users })\n * .withLockContext(identified.data)\n * ```\n */\nexport class LockContext {\n private ctsToken: CtsToken | undefined\n private workspaceId: string\n private context: Context\n\n constructor({\n context = { identityClaim: ['sub'] },\n ctsToken,\n }: LockContextOptions = {}) {\n const workspaceId = loadWorkSpaceId()\n\n if (!workspaceId) {\n throw new Error(\n 'You have not defined a workspace ID in your config file, or the CS_WORKSPACE_CRN environment variable.',\n )\n }\n\n if (ctsToken) {\n this.ctsToken = ctsToken\n }\n\n this.workspaceId = workspaceId\n this.context = context\n logger.debug('Successfully initialized the EQL lock context.')\n }\n\n /**\n * Exchange a user's JWT for a CTS token and bind it to this lock context.\n *\n * @param jwtToken - A valid OIDC / JWT token for the current user.\n * @returns A `Result` containing this `LockContext` (now authenticated) or an error.\n *\n * @example\n * ```typescript\n * const lc = new LockContext()\n * const result = await lc.identify(userJwt)\n * if (result.failure) {\n * console.error(\"Auth failed:\", result.failure.message)\n * }\n * ```\n */\n async identify(\n jwtToken: string,\n ): Promise<Result<LockContext, EncryptionError>> {\n const workspaceId = this.workspaceId\n\n const ctsEndpoint =\n process.env.CS_CTS_ENDPOINT ||\n 'https://ap-southeast-2.aws.auth.viturhosted.net'\n\n const ctsFetchResult = await withResult(\n () =>\n fetch(`${ctsEndpoint}/api/authorize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n workspaceId,\n oidcToken: jwtToken,\n }),\n }),\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n if (ctsFetchResult.failure) {\n return ctsFetchResult\n }\n\n const identifiedLockContext = await withResult(\n async () => {\n const ctsToken = (await ctsFetchResult.data.json()) as CtsToken\n\n if (!ctsToken.accessToken) {\n throw new Error(\n 'The response from the CipherStash API did not contain an access token. Please contact support.',\n )\n }\n\n this.ctsToken = ctsToken\n return this\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n\n return identifiedLockContext\n }\n\n /**\n * Retrieve the current CTS token and context for use with encryption operations.\n *\n * Must be called after {@link identify}. Returns the token/context pair that\n * `.withLockContext()` expects.\n *\n * @returns A `Result` containing the CTS token and identity context, or an error\n * if {@link identify} has not been called.\n */\n getLockContext(): Promise<Result<GetLockContextResponse, EncryptionError>> {\n return withResult(\n () => {\n if (!this.ctsToken?.accessToken || !this.ctsToken?.expiry) {\n throw new Error(\n 'The CTS token is not set. Please call identify() with a users JWT token, or pass an existing CTS token to the LockContext constructor before calling getLockContext().',\n )\n }\n\n return {\n context: this.context,\n ctsToken: this.ctsToken,\n }\n },\n (error) => ({\n type: EncryptionErrorTypes.CtsTokenError,\n message: error.message,\n }),\n )\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,21 +15,12 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
31
21
  var index_exports = {};
32
22
  __export(index_exports, {
33
23
  Encryption: () => Encryption,
34
- Secrets: () => Secrets,
35
24
  encryptedColumn: () => encryptedColumn,
36
25
  encryptedField: () => encryptedField,
37
26
  encryptedTable: () => encryptedTable,
@@ -920,11 +909,11 @@ var import_result3 = require("@byteslice/result");
920
909
 
921
910
  // src/encryption/helpers/model-helpers.ts
922
911
  var import_protect_ffi4 = require("@cipherstash/protect-ffi");
923
- function setNestedValue(obj, path2, value) {
912
+ function setNestedValue(obj, path, value) {
924
913
  const FORBIDDEN_KEYS = ["__proto__", "prototype", "constructor"];
925
914
  let current = obj;
926
- for (let i = 0; i < path2.length - 1; i++) {
927
- const part = path2[i];
915
+ for (let i = 0; i < path.length - 1; i++) {
916
+ const part = path[i];
928
917
  if (FORBIDDEN_KEYS.includes(part)) {
929
918
  throw new Error(`[encryption]: Forbidden key "${part}" in field path`);
930
919
  }
@@ -933,7 +922,7 @@ function setNestedValue(obj, path2, value) {
933
922
  }
934
923
  current = current[part];
935
924
  }
936
- const lastKey = path2[path2.length - 1];
925
+ const lastKey = path[path.length - 1];
937
926
  if (FORBIDDEN_KEYS.includes(lastKey)) {
938
927
  throw new Error(`[encryption]: Forbidden key "${lastKey}" in field path`);
939
928
  }
@@ -1010,7 +999,7 @@ function prepareFieldsForEncryption(model, table) {
1010
999
  continue;
1011
1000
  }
1012
1001
  if (typeof value === "object" && !isEncryptedPayload(value) && !columnPaths2.includes(fullKey)) {
1013
- if (columnPaths2.some((path2) => path2.startsWith(fullKey))) {
1002
+ if (columnPaths2.some((path) => path.startsWith(fullKey))) {
1014
1003
  processNestedFields(
1015
1004
  value,
1016
1005
  fullKey,
@@ -1188,7 +1177,7 @@ function prepareBulkModelsForOperation(models, table) {
1188
1177
  continue;
1189
1178
  }
1190
1179
  if (typeof value === "object" && !isEncryptedPayload(value) && !columnPaths.includes(fullKey)) {
1191
- if (columnPaths.some((path2) => path2.startsWith(fullKey))) {
1180
+ if (columnPaths.some((path) => path.startsWith(fullKey))) {
1192
1181
  processNestedFields(
1193
1182
  value,
1194
1183
  fullKey,
@@ -2798,357 +2787,9 @@ var Encryption = async (config) => {
2798
2787
  }
2799
2788
  return result.data;
2800
2789
  };
2801
-
2802
- // src/utils/config/index.ts
2803
- var import_node_fs = __toESM(require("fs"), 1);
2804
- var import_node_path = __toESM(require("path"), 1);
2805
- function extractWorkspaceIdFromCrn(crn) {
2806
- const match = crn.match(/crn:[^:]+:([^:]+)$/);
2807
- if (!match) {
2808
- throw new Error("Invalid CRN format");
2809
- }
2810
- return match[1];
2811
- }
2812
-
2813
- // src/secrets/index.ts
2814
- var Secrets = class {
2815
- encryptionClient = null;
2816
- config;
2817
- apiBaseUrl = process.env.STASH_API_URL || "https://dashboard.cipherstash.com/api/secrets";
2818
- secretsSchema = encryptedTable("secrets", {
2819
- value: encryptedColumn("value")
2820
- });
2821
- constructor(config) {
2822
- const workspaceCRN = config.workspaceCRN ?? process.env.CS_WORKSPACE_CRN;
2823
- const clientId = config.clientId ?? process.env.CS_CLIENT_ID;
2824
- const clientKey = config.clientKey ?? process.env.CS_CLIENT_KEY;
2825
- const accessKey = config.accessKey ?? process.env.CS_CLIENT_ACCESS_KEY;
2826
- if (!workspaceCRN || !clientId || !clientKey || !accessKey) {
2827
- throw new Error(
2828
- "Missing required configuration or environment variables."
2829
- );
2830
- }
2831
- this.config = {
2832
- environment: config.environment,
2833
- workspaceCRN,
2834
- clientId,
2835
- clientKey,
2836
- accessKey
2837
- };
2838
- }
2839
- initPromise = null;
2840
- /**
2841
- * Initialize the Secrets client and underlying Encryption client
2842
- */
2843
- async ensureInitialized() {
2844
- if (!this.initPromise) {
2845
- this.initPromise = this._doInit();
2846
- }
2847
- return this.initPromise;
2848
- }
2849
- async _doInit() {
2850
- logger.debug("Initializing the Secrets client.");
2851
- this.encryptionClient = await Encryption({
2852
- schemas: [this.secretsSchema],
2853
- config: {
2854
- workspaceCrn: this.config.workspaceCRN,
2855
- clientId: this.config.clientId,
2856
- clientKey: this.config.clientKey,
2857
- accessKey: this.config.accessKey,
2858
- keyset: { name: this.config.environment }
2859
- }
2860
- });
2861
- logger.debug("Successfully initialized the Secrets client.");
2862
- }
2863
- /**
2864
- * Get the authorization header for API requests
2865
- */
2866
- getAuthHeader() {
2867
- return `Bearer ${this.config.accessKey}`;
2868
- }
2869
- /**
2870
- * Make an API request with error handling.
2871
- *
2872
- * For GET requests, `params` are appended as URL query parameters.
2873
- * For POST requests, `body` is sent as JSON in the request body.
2874
- */
2875
- async apiRequest(method, path2, options) {
2876
- try {
2877
- let url = `${this.apiBaseUrl}${path2}`;
2878
- if (options?.params) {
2879
- const searchParams = new URLSearchParams(options.params);
2880
- url = `${url}?${searchParams.toString()}`;
2881
- }
2882
- logger.debug(`Secrets API request: ${method} ${path2}`);
2883
- const headers = {
2884
- "Content-Type": "application/json",
2885
- Authorization: this.getAuthHeader()
2886
- };
2887
- const response = await fetch(url, {
2888
- method,
2889
- headers,
2890
- body: options?.body ? JSON.stringify(options.body) : void 0
2891
- });
2892
- if (!response.ok) {
2893
- const errorText = await response.text();
2894
- let errorMessage = `API request failed with status ${response.status}`;
2895
- try {
2896
- const errorJson = JSON.parse(errorText);
2897
- errorMessage = errorJson.message || errorJson.error || errorMessage;
2898
- } catch {
2899
- errorMessage = errorText || errorMessage;
2900
- }
2901
- logger.error(`Secrets API error on ${method} ${path2}: ${errorMessage}`);
2902
- return {
2903
- failure: {
2904
- type: "ApiError",
2905
- message: errorMessage
2906
- }
2907
- };
2908
- }
2909
- logger.debug(`Secrets API request successful: ${method} ${path2}`);
2910
- const data = await response.json();
2911
- return { data };
2912
- } catch (error) {
2913
- const message = error instanceof Error ? error.message : "Unknown network error occurred";
2914
- logger.error(`Secrets network error on ${method} ${path2}: ${message}`);
2915
- return {
2916
- failure: {
2917
- type: "NetworkError",
2918
- message
2919
- }
2920
- };
2921
- }
2922
- }
2923
- /**
2924
- * Store an encrypted secret in the vault.
2925
- * The value is encrypted locally before being sent to the API.
2926
- *
2927
- * API: POST /api/secrets/set
2928
- *
2929
- * @param name - The name of the secret
2930
- * @param value - The plaintext value to encrypt and store
2931
- * @returns A Result containing the API response or an error
2932
- */
2933
- async set(name, value) {
2934
- logger.debug("Setting secret");
2935
- await this.ensureInitialized();
2936
- if (!this.encryptionClient) {
2937
- return {
2938
- failure: {
2939
- type: "ClientError",
2940
- message: "Failed to initialize Encryption client"
2941
- }
2942
- };
2943
- }
2944
- const encryptResult = await this.encryptionClient.encrypt(value, {
2945
- column: this.secretsSchema.value,
2946
- table: this.secretsSchema
2947
- });
2948
- if (encryptResult.failure) {
2949
- logger.error("Failed to encrypt secret");
2950
- return {
2951
- failure: {
2952
- type: "EncryptionError",
2953
- message: encryptResult.failure.message
2954
- }
2955
- };
2956
- }
2957
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
2958
- return await this.apiRequest("POST", "/set", {
2959
- body: {
2960
- workspaceId,
2961
- environment: this.config.environment,
2962
- name,
2963
- encryptedValue: encryptedToPgComposite(encryptResult.data)
2964
- }
2965
- });
2966
- }
2967
- /**
2968
- * Retrieve and decrypt a secret from the vault.
2969
- * The secret is decrypted locally after retrieval.
2970
- *
2971
- * API: GET /api/secrets/get?workspaceId=...&environment=...&name=...
2972
- *
2973
- * @param name - The name of the secret to retrieve
2974
- * @returns A Result containing the decrypted value or an error
2975
- */
2976
- async get(name) {
2977
- logger.debug("Getting secret");
2978
- await this.ensureInitialized();
2979
- if (!this.encryptionClient) {
2980
- return {
2981
- failure: {
2982
- type: "ClientError",
2983
- message: "Failed to initialize Encryption client"
2984
- }
2985
- };
2986
- }
2987
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
2988
- const apiResult = await this.apiRequest("GET", "/get", {
2989
- params: {
2990
- workspaceId,
2991
- environment: this.config.environment,
2992
- name
2993
- }
2994
- });
2995
- if (apiResult.failure) {
2996
- return apiResult;
2997
- }
2998
- const decryptResult = await this.encryptionClient.decrypt(
2999
- apiResult.data.encryptedValue.data
3000
- );
3001
- if (decryptResult.failure) {
3002
- logger.error("Failed to decrypt secret");
3003
- return {
3004
- failure: {
3005
- type: "DecryptionError",
3006
- message: decryptResult.failure.message
3007
- }
3008
- };
3009
- }
3010
- if (typeof decryptResult.data !== "string") {
3011
- logger.error("Decrypted secret value is not a string");
3012
- return {
3013
- failure: {
3014
- type: "DecryptionError",
3015
- message: "Decrypted value is not a string"
3016
- }
3017
- };
3018
- }
3019
- return { data: decryptResult.data };
3020
- }
3021
- /**
3022
- * Retrieve and decrypt many secrets from the vault.
3023
- * The secrets are decrypted locally after retrieval.
3024
- * This method only triggers a single network request to the ZeroKMS.
3025
- *
3026
- * API: GET /api/secrets/get-many?workspaceId=...&environment=...&names=name1,name2,...
3027
- *
3028
- * Constraints:
3029
- * - Minimum 2 secret names required
3030
- * - Maximum 100 secret names per request
3031
- *
3032
- * @param names - The names of the secrets to retrieve (min 2, max 100)
3033
- * @returns A Result containing an object mapping secret names to their decrypted values
3034
- */
3035
- async getMany(names) {
3036
- logger.debug(`Getting ${names.length} secrets.`);
3037
- await this.ensureInitialized();
3038
- if (!this.encryptionClient) {
3039
- return {
3040
- failure: {
3041
- type: "ClientError",
3042
- message: "Failed to initialize Encryption client"
3043
- }
3044
- };
3045
- }
3046
- if (names.length < 2) {
3047
- return {
3048
- failure: {
3049
- type: "ClientError",
3050
- message: "At least 2 secret names are required for getMany"
3051
- }
3052
- };
3053
- }
3054
- if (names.length > 100) {
3055
- return {
3056
- failure: {
3057
- type: "ClientError",
3058
- message: "Maximum 100 secret names per request"
3059
- }
3060
- };
3061
- }
3062
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
3063
- const apiResult = await this.apiRequest(
3064
- "GET",
3065
- "/get-many",
3066
- {
3067
- params: {
3068
- workspaceId,
3069
- environment: this.config.environment,
3070
- names: names.join(",")
3071
- }
3072
- }
3073
- );
3074
- if (apiResult.failure) {
3075
- return apiResult;
3076
- }
3077
- const dataToDecrypt = apiResult.data.map((item) => ({
3078
- name: item.name,
3079
- value: item.encryptedValue.data
3080
- }));
3081
- const decryptResult = await this.encryptionClient.bulkDecryptModels(dataToDecrypt);
3082
- if (decryptResult.failure) {
3083
- logger.error(
3084
- `Failed to decrypt secrets: ${decryptResult.failure.message}`
3085
- );
3086
- return {
3087
- failure: {
3088
- type: "DecryptionError",
3089
- message: decryptResult.failure.message
3090
- }
3091
- };
3092
- }
3093
- const decryptedSecrets = decryptResult.data;
3094
- const secretsMap = {};
3095
- for (const secret of decryptedSecrets) {
3096
- if (secret.name && secret.value) {
3097
- secretsMap[secret.name] = secret.value;
3098
- }
3099
- }
3100
- return { data: secretsMap };
3101
- }
3102
- /**
3103
- * List all secrets in the environment.
3104
- * Only names and metadata are returned; values remain encrypted.
3105
- *
3106
- * API: GET /api/secrets/list?workspaceId=...&environment=...
3107
- *
3108
- * @returns A Result containing the list of secrets or an error
3109
- */
3110
- async list() {
3111
- logger.debug("Listing secrets.");
3112
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
3113
- const apiResult = await this.apiRequest(
3114
- "GET",
3115
- "/list",
3116
- {
3117
- params: {
3118
- workspaceId,
3119
- environment: this.config.environment
3120
- }
3121
- }
3122
- );
3123
- if (apiResult.failure) {
3124
- return apiResult;
3125
- }
3126
- return { data: apiResult.data.secrets };
3127
- }
3128
- /**
3129
- * Delete a secret from the vault.
3130
- *
3131
- * API: POST /api/secrets/delete
3132
- *
3133
- * @param name - The name of the secret to delete
3134
- * @returns A Result containing the API response or an error
3135
- */
3136
- async delete(name) {
3137
- logger.debug("Deleting secret");
3138
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
3139
- return await this.apiRequest("POST", "/delete", {
3140
- body: {
3141
- workspaceId,
3142
- environment: this.config.environment,
3143
- name
3144
- }
3145
- });
3146
- }
3147
- };
3148
2790
  // Annotate the CommonJS export names for ESM import in node:
3149
2791
  0 && (module.exports = {
3150
2792
  Encryption,
3151
- Secrets,
3152
2793
  encryptedColumn,
3153
2794
  encryptedField,
3154
2795
  encryptedTable,