@warlock.js/core 5.32.0 → 5.34.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 (173) hide show
  1. package/CHANGELOG.md +64 -46
  2. package/esm/application/application-config-types.d.mts +8 -0
  3. package/esm/application/application-config-types.d.mts.map +1 -1
  4. package/esm/application/boot-signal.d.mts +8 -8
  5. package/esm/application/boot-signal.d.mts.map +1 -1
  6. package/esm/application/boot-signal.mjs +15 -9
  7. package/esm/application/boot-signal.mjs.map +1 -1
  8. package/esm/cli/commands/ai/ai-command.action.mjs +140 -0
  9. package/esm/cli/commands/ai/ai-command.action.mjs.map +1 -0
  10. package/esm/cli/commands/ai.command.mjs +50 -0
  11. package/esm/cli/commands/ai.command.mjs.map +1 -0
  12. package/esm/cli/commands/check/check-command.action.mjs +4 -0
  13. package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
  14. package/esm/cli/commands/check.command.mjs +6 -1
  15. package/esm/cli/commands/check.command.mjs.map +1 -1
  16. package/esm/cli/commands/e2e.command.mjs +63 -0
  17. package/esm/cli/commands/e2e.command.mjs.map +1 -0
  18. package/esm/cli/commands/load-e2e-spec.mjs +47 -0
  19. package/esm/cli/commands/load-e2e-spec.mjs.map +1 -0
  20. package/esm/cli/commands/pm2/pm2-command.action.mjs +91 -0
  21. package/esm/cli/commands/pm2/pm2-command.action.mjs.map +1 -0
  22. package/esm/cli/commands/pm2/pm2-config.mjs +115 -0
  23. package/esm/cli/commands/pm2/pm2-config.mjs.map +1 -0
  24. package/esm/cli/commands/pm2.command.mjs +53 -0
  25. package/esm/cli/commands/pm2.command.mjs.map +1 -0
  26. package/esm/cli/framework-cli-commands.mjs +6 -0
  27. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  28. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  29. package/esm/connectors/connectors-manager.mjs +1 -4
  30. package/esm/connectors/connectors-manager.mjs.map +1 -1
  31. package/esm/connectors/shutdown-timeout.mjs +7 -0
  32. package/esm/connectors/shutdown-timeout.mjs.map +1 -0
  33. package/esm/connectors/socket-connector.d.mts.map +1 -1
  34. package/esm/connectors/socket-connector.mjs +8 -1
  35. package/esm/connectors/socket-connector.mjs.map +1 -1
  36. package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +1 -1
  37. package/esm/dev-server/health-checker/default-health-checkers.mjs +2 -16
  38. package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -1
  39. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -2
  40. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
  41. package/esm/dev-server/health-checker/file-health-result.d.mts +1 -1
  42. package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
  43. package/esm/e2e/browser.d.mts +4 -0
  44. package/esm/e2e/browser.d.mts.map +1 -0
  45. package/esm/e2e/browser.mjs +88 -0
  46. package/esm/e2e/browser.mjs.map +1 -0
  47. package/esm/e2e/cdp.d.mts +152 -0
  48. package/esm/e2e/cdp.d.mts.map +1 -0
  49. package/esm/e2e/cdp.mjs +106 -0
  50. package/esm/e2e/cdp.mjs.map +1 -0
  51. package/esm/e2e/fixture.mjs +195 -0
  52. package/esm/e2e/fixture.mjs.map +1 -0
  53. package/esm/e2e/index.d.mts +5 -0
  54. package/esm/e2e/index.mjs +6 -0
  55. package/esm/e2e/owned-process.mjs +193 -0
  56. package/esm/e2e/owned-process.mjs.map +1 -0
  57. package/esm/e2e/page.d.mts +64 -0
  58. package/esm/e2e/page.d.mts.map +1 -0
  59. package/esm/e2e/page.mjs +376 -0
  60. package/esm/e2e/page.mjs.map +1 -0
  61. package/esm/e2e/run-e2e.d.mts +17 -0
  62. package/esm/e2e/run-e2e.d.mts.map +1 -0
  63. package/esm/e2e/run-e2e.mjs +143 -0
  64. package/esm/e2e/run-e2e.mjs.map +1 -0
  65. package/esm/e2e/types.d.mts +123 -0
  66. package/esm/e2e/types.d.mts.map +1 -0
  67. package/esm/e2e/types.mjs +24 -0
  68. package/esm/e2e/types.mjs.map +1 -0
  69. package/esm/generations/features/index.mjs +2 -0
  70. package/esm/generations/features/index.mjs.map +1 -1
  71. package/esm/generations/features/process.feature.mjs +65 -0
  72. package/esm/generations/features/process.feature.mjs.map +1 -0
  73. package/esm/http/port-preflight.d.mts.map +1 -1
  74. package/esm/http/port-preflight.mjs +2 -0
  75. package/esm/http/port-preflight.mjs.map +1 -1
  76. package/esm/index.d.mts +7 -2
  77. package/esm/index.mjs +7 -2
  78. package/esm/secrets/errors.d.mts +9 -0
  79. package/esm/secrets/errors.d.mts.map +1 -0
  80. package/esm/secrets/errors.mjs +24 -0
  81. package/esm/secrets/errors.mjs.map +1 -0
  82. package/esm/secrets/file-backend.mjs +167 -0
  83. package/esm/secrets/file-backend.mjs.map +1 -0
  84. package/esm/secrets/index.d.mts +6 -0
  85. package/esm/secrets/index.mjs +6 -0
  86. package/esm/secrets/keyring-backend.mjs +50 -0
  87. package/esm/secrets/keyring-backend.mjs.map +1 -0
  88. package/esm/secrets/memory-backend.d.mts +12 -0
  89. package/esm/secrets/memory-backend.d.mts.map +1 -0
  90. package/esm/secrets/memory-backend.mjs +28 -0
  91. package/esm/secrets/memory-backend.mjs.map +1 -0
  92. package/esm/secrets/resolve-driver.mjs +14 -0
  93. package/esm/secrets/resolve-driver.mjs.map +1 -0
  94. package/esm/secrets/secret-store.d.mts +25 -0
  95. package/esm/secrets/secret-store.d.mts.map +1 -0
  96. package/esm/secrets/secret-store.mjs +120 -0
  97. package/esm/secrets/secret-store.mjs.map +1 -0
  98. package/esm/secrets/secrets.d.mts +11 -0
  99. package/esm/secrets/secrets.d.mts.map +1 -0
  100. package/esm/secrets/secrets.mjs +72 -0
  101. package/esm/secrets/secrets.mjs.map +1 -0
  102. package/esm/secrets/types.d.mts +26 -0
  103. package/esm/secrets/types.d.mts.map +1 -0
  104. package/esm/socket/index.d.mts +2 -2
  105. package/esm/socket/index.mjs +2 -2
  106. package/esm/socket/utils.d.mts +7 -0
  107. package/esm/socket/utils.d.mts.map +1 -1
  108. package/esm/socket/utils.mjs +12 -1
  109. package/esm/socket/utils.mjs.map +1 -1
  110. package/esm/tests/fakes/index.d.mts +2 -1
  111. package/esm/tests/fakes/index.mjs +2 -1
  112. package/esm/tests/fakes/secrets-fake.d.mts +13 -0
  113. package/esm/tests/fakes/secrets-fake.d.mts.map +1 -0
  114. package/esm/tests/fakes/secrets-fake.mjs +50 -0
  115. package/esm/tests/fakes/secrets-fake.mjs.map +1 -0
  116. package/esm/tests/fakes/test-fakes.d.mts +2 -2
  117. package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
  118. package/esm/tests/fakes/test-fakes.mjs +5 -2
  119. package/esm/tests/fakes/test-fakes.mjs.map +1 -1
  120. package/esm/tests/index.d.mts +2 -1
  121. package/esm/tests/index.mjs +2 -1
  122. package/esm/tests/vitest-setup.d.mts +1 -1
  123. package/esm/tests/vitest-setup.mjs.map +1 -1
  124. package/esm/tooling/check/lint-step.d.mts +2 -8
  125. package/esm/tooling/check/lint-step.d.mts.map +1 -1
  126. package/esm/tooling/check/lint-step.mjs +1 -42
  127. package/esm/tooling/check/lint-step.mjs.map +1 -1
  128. package/esm/tooling/check/run-check.d.mts +2 -0
  129. package/esm/tooling/check/run-check.d.mts.map +1 -1
  130. package/esm/tooling/check/run-check.mjs +3 -1
  131. package/esm/tooling/check/run-check.mjs.map +1 -1
  132. package/esm/tooling/problem.d.mts +1 -1
  133. package/esm/tooling/problem.d.mts.map +1 -1
  134. package/esm/tooling/problem.mjs.map +1 -1
  135. package/esm/updater/package-manager.mjs +5 -1
  136. package/esm/updater/package-manager.mjs.map +1 -1
  137. package/esm/upgrade/codemods/index.mjs +3 -0
  138. package/esm/upgrade/codemods/index.mjs.map +1 -1
  139. package/esm/upgrade/codemods/oxc-toolchain.mjs +191 -0
  140. package/esm/upgrade/codemods/oxc-toolchain.mjs.map +1 -0
  141. package/esm/upgrade/codemods/oxlint-rule-schemas.mjs +6 -0
  142. package/esm/upgrade/codemods/oxlint-rule-schemas.mjs.map +1 -0
  143. package/esm/upgrade/codemods/port-eslint-rules.mjs +343 -0
  144. package/esm/upgrade/codemods/port-eslint-rules.mjs.map +1 -0
  145. package/esm/upgrade/codemods/toolchain-templates.mjs +244 -0
  146. package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -0
  147. package/esm/upgrade/codemods/validate-oxlint-rule.mjs +60 -0
  148. package/esm/upgrade/codemods/validate-oxlint-rule.mjs.map +1 -0
  149. package/esm/upgrade/notes.mjs +14 -7
  150. package/esm/upgrade/notes.mjs.map +1 -1
  151. package/esm/upgrade/run-upgrade.mjs +25 -6
  152. package/esm/upgrade/run-upgrade.mjs.map +1 -1
  153. package/esm/warlock-config/types.d.mts +1 -1
  154. package/llms-full.txt +538 -29
  155. package/llms.txt +5 -3
  156. package/package.json +29 -20
  157. package/skills/README.md +2 -1
  158. package/skills/create-module/SKILL.md +2 -2
  159. package/skills/index.md +1 -1
  160. package/skills/run-app/SKILL.md +115 -6
  161. package/skills/store-secrets/SKILL.md +141 -0
  162. package/skills/test-browser/SKILL.md +119 -0
  163. package/skills/test-fakes/SKILL.md +13 -11
  164. package/skills/upgrade-from-5-29/SKILL.md +85 -7
  165. package/skills/warlock-check/SKILL.md +7 -3
  166. package/skills/warlock-doctor/SKILL.md +8 -0
  167. package/skills/wire-socket/SKILL.md +19 -0
  168. package/skills/write-cli-command/SKILL.md +21 -0
  169. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +0 -154
  170. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +0 -1
  171. package/esm/dev-server/health-checker/workers/eslint-health.worker.d.mts +0 -1
  172. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +0 -192
  173. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +0 -1
@@ -0,0 +1,167 @@
1
+ import { SecretStoreError } from "./errors.mjs";
2
+ import { registerLogSecret } from "@warlock.js/logger";
3
+ import { createCipheriv, createDecipheriv, createHash, hkdfSync, randomBytes } from "node:crypto";
4
+ import path from "node:path";
5
+ import { constants } from "node:fs";
6
+ import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
7
+
8
+ //#region ../../packages/core/src/secrets/file-backend.ts
9
+ function digest(value) {
10
+ return createHash("sha256").update(value).digest("hex");
11
+ }
12
+ function isMissing(error) {
13
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
14
+ }
15
+ /** One authenticated file per secret: concurrent writes to different keys cannot clobber each other. */
16
+ var FileSecretBackend = class {
17
+ constructor(directory, encryptionKey) {
18
+ this.directory = directory;
19
+ this.encryptionKey = encryptionKey;
20
+ }
21
+ key(namespace) {
22
+ const secret = this.encryptionKey();
23
+ if (typeof secret !== "string" || !/^[a-f\d]{64}$/i.test(secret)) throw new SecretStoreError("KEY_REQUIRED");
24
+ registerLogSecret(secret, ["encryptionKey"]);
25
+ return Buffer.from(hkdfSync("sha256", Buffer.from(secret, "hex"), namespace, "warlock:secrets:v1", 32));
26
+ }
27
+ validateOwner(stats, directory) {
28
+ if ((directory ? !stats.isDirectory() : !stats.isFile() || stats.nlink !== 1) || process.getuid && stats.uid !== process.getuid()) throw new SecretStoreError("UNSAFE_FILE");
29
+ }
30
+ async folder(namespace, create) {
31
+ const root = path.resolve(this.directory);
32
+ const folder = path.join(root, digest(namespace));
33
+ for (const directory of [root, folder]) {
34
+ if (!create) try {
35
+ await lstat(directory);
36
+ } catch (error) {
37
+ if (isMissing(error)) return void 0;
38
+ throw error;
39
+ }
40
+ if (process.platform === "win32") throw new SecretStoreError("UNSAFE_FILE");
41
+ if (create) await mkdir(directory, {
42
+ recursive: true,
43
+ mode: 448
44
+ });
45
+ this.validateOwner(await lstat(directory), true);
46
+ const handle = await open(directory, constants.O_RDONLY | constants.O_NOFOLLOW);
47
+ try {
48
+ this.validateOwner(await handle.stat(), true);
49
+ await handle.chmod(448);
50
+ if (((await handle.stat()).mode & 511) !== 448) throw new SecretStoreError("UNSAFE_FILE");
51
+ } finally {
52
+ await handle.close();
53
+ }
54
+ }
55
+ return folder;
56
+ }
57
+ async location(namespace, name, create) {
58
+ const folder = await this.folder(namespace, create);
59
+ return folder ? path.join(folder, `${digest(name)}.secret`) : void 0;
60
+ }
61
+ aad(namespace, name) {
62
+ return Buffer.from(JSON.stringify([
63
+ "warlock:secrets:v1",
64
+ namespace,
65
+ name
66
+ ]));
67
+ }
68
+ async safe(operation) {
69
+ try {
70
+ return await operation();
71
+ } catch (error) {
72
+ if (error instanceof SecretStoreError) throw error;
73
+ if (error instanceof Error && "code" in error && error.code === "ELOOP") throw new SecretStoreError("UNSAFE_FILE");
74
+ throw new SecretStoreError("UNAVAILABLE");
75
+ }
76
+ }
77
+ async get(namespace, name) {
78
+ return this.safe(async () => {
79
+ const file = await this.location(namespace, name, false);
80
+ if (!file) return void 0;
81
+ let handle;
82
+ try {
83
+ handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
84
+ } catch (error) {
85
+ if (isMissing(error)) return void 0;
86
+ throw error;
87
+ }
88
+ let payload;
89
+ try {
90
+ this.validateOwner(await handle.stat(), false);
91
+ await handle.chmod(384);
92
+ if (((await handle.stat()).mode & 511) !== 384) throw new SecretStoreError("UNSAFE_FILE");
93
+ payload = await handle.readFile();
94
+ } finally {
95
+ await handle.close();
96
+ }
97
+ const key = this.key(namespace);
98
+ try {
99
+ if (payload.length < 29 || payload[0] !== 1) throw new Error();
100
+ const decipher = createDecipheriv("aes-256-gcm", key, payload.subarray(1, 13));
101
+ decipher.setAAD(this.aad(namespace, name));
102
+ decipher.setAuthTag(payload.subarray(13, 29));
103
+ return Buffer.concat([decipher.update(payload.subarray(29)), decipher.final()]).toString("utf8");
104
+ } catch {
105
+ throw new SecretStoreError("CORRUPT_FILE");
106
+ }
107
+ });
108
+ }
109
+ async set(namespace, name, value) {
110
+ return this.safe(async () => {
111
+ const key = this.key(namespace);
112
+ const file = await this.location(namespace, name, true);
113
+ if (!file) throw new SecretStoreError("UNAVAILABLE");
114
+ try {
115
+ this.validateOwner(await lstat(file), false);
116
+ } catch (error) {
117
+ if (!isMissing(error)) throw error;
118
+ }
119
+ const iv = randomBytes(12);
120
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
121
+ cipher.setAAD(this.aad(namespace, name));
122
+ const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
123
+ const payload = Buffer.concat([
124
+ Buffer.from([1]),
125
+ iv,
126
+ cipher.getAuthTag(),
127
+ encrypted
128
+ ]);
129
+ const temporary = `${file}.${randomBytes(16).toString("hex")}.tmp`;
130
+ const handle = await open(temporary, "wx", 384);
131
+ try {
132
+ try {
133
+ await handle.chmod(384);
134
+ this.validateOwner(await handle.stat(), false);
135
+ if (((await handle.stat()).mode & 511) !== 384) throw new SecretStoreError("UNSAFE_FILE");
136
+ await handle.writeFile(payload);
137
+ await handle.sync();
138
+ } finally {
139
+ await handle.close();
140
+ }
141
+ await rename(temporary, file);
142
+ } finally {
143
+ await unlink(temporary).catch((error) => {
144
+ if (!isMissing(error)) throw error;
145
+ });
146
+ }
147
+ });
148
+ }
149
+ async remove(namespace, name) {
150
+ return this.safe(async () => {
151
+ const file = await this.location(namespace, name, false);
152
+ if (!file) return false;
153
+ try {
154
+ this.validateOwner(await lstat(file), false);
155
+ await unlink(file);
156
+ return true;
157
+ } catch (error) {
158
+ if (isMissing(error)) return false;
159
+ throw error;
160
+ }
161
+ });
162
+ }
163
+ };
164
+
165
+ //#endregion
166
+ export { FileSecretBackend };
167
+ //# sourceMappingURL=file-backend.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-backend.mjs","names":[],"sources":["../../../../../../../../packages/core/src/secrets/file-backend.ts"],"sourcesContent":["import { createCipheriv, createDecipheriv, createHash, hkdfSync, randomBytes } from \"node:crypto\";\nimport { registerLogSecret } from \"@warlock.js/logger\";\nimport { constants, type Stats } from \"node:fs\";\nimport { lstat, mkdir, open, rename, unlink, type FileHandle } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { SecretStoreError } from \"./errors\";\nimport type { SecretBackend } from \"./types\";\n\nfunction digest(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction isMissing(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\n/** One authenticated file per secret: concurrent writes to different keys cannot clobber each other. */\nexport class FileSecretBackend implements SecretBackend {\n public constructor(\n private readonly directory: string,\n private readonly encryptionKey: () => string | undefined,\n ) {}\n\n private key(namespace: string): Buffer {\n const secret = this.encryptionKey();\n if (typeof secret !== \"string\" || !/^[a-f\\d]{64}$/i.test(secret)) {\n throw new SecretStoreError(\"KEY_REQUIRED\");\n }\n registerLogSecret(secret, [\"encryptionKey\"]);\n // Core's encrypt/decrypt helpers use global config directly. Derive a\n // separate AES-256-GCM key without changing that global config.\n // https://nodejs.org/docs/latest-v22.x/api/crypto.html#cryptohkdfsyncdigest-ikm-salt-info-keylen\n return Buffer.from(\n hkdfSync(\"sha256\", Buffer.from(secret, \"hex\"), namespace, \"warlock:secrets:v1\", 32),\n );\n }\n\n private validateOwner(stats: Stats, directory: boolean): void {\n if (\n (directory ? !stats.isDirectory() : !stats.isFile() || stats.nlink !== 1) ||\n (process.getuid && stats.uid !== process.getuid())\n ) {\n throw new SecretStoreError(\"UNSAFE_FILE\");\n }\n }\n\n private async folder(namespace: string, create: boolean): Promise<string | undefined> {\n // Node's Windows modes cannot enforce owner-only ACLs. Fail closed there.\n // https://nodejs.org/docs/latest-v22.x/api/fs.html#file-modes\n const root = path.resolve(this.directory);\n const folder = path.join(root, digest(namespace));\n for (const directory of [root, folder]) {\n if (!create) {\n try {\n await lstat(directory);\n } catch (error) {\n if (isMissing(error)) return undefined;\n throw error;\n }\n }\n if (process.platform === \"win32\") throw new SecretStoreError(\"UNSAFE_FILE\");\n if (create) await mkdir(directory, { recursive: true, mode: 0o700 });\n this.validateOwner(await lstat(directory), true);\n const handle = await open(directory, constants.O_RDONLY | constants.O_NOFOLLOW);\n try {\n this.validateOwner(await handle.stat(), true);\n await handle.chmod(0o700);\n if (((await handle.stat()).mode & 0o777) !== 0o700) {\n throw new SecretStoreError(\"UNSAFE_FILE\");\n }\n } finally {\n await handle.close();\n }\n }\n return folder;\n }\n\n private async location(\n namespace: string,\n name: string,\n create: boolean,\n ): Promise<string | undefined> {\n const folder = await this.folder(namespace, create);\n return folder ? path.join(folder, `${digest(name)}.secret`) : undefined;\n }\n\n private aad(namespace: string, name: string): Buffer {\n return Buffer.from(JSON.stringify([\"warlock:secrets:v1\", namespace, name]));\n }\n\n private async safe<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof SecretStoreError) throw error;\n if (error instanceof Error && \"code\" in error && error.code === \"ELOOP\") {\n throw new SecretStoreError(\"UNSAFE_FILE\");\n }\n throw new SecretStoreError(\"UNAVAILABLE\");\n }\n }\n\n public async get(namespace: string, name: string): Promise<string | undefined> {\n return this.safe(async () => {\n const file = await this.location(namespace, name, false);\n if (!file) return undefined;\n let handle: FileHandle;\n try {\n handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);\n } catch (error) {\n if (isMissing(error)) return undefined;\n throw error;\n }\n let payload: Buffer;\n try {\n this.validateOwner(await handle.stat(), false);\n await handle.chmod(0o600);\n if (((await handle.stat()).mode & 0o777) !== 0o600) {\n throw new SecretStoreError(\"UNSAFE_FILE\");\n }\n payload = await handle.readFile();\n } finally {\n await handle.close();\n }\n const key = this.key(namespace);\n try {\n // v1 (one byte), 12-byte IV, 16-byte tag, then ciphertext (possibly empty).\n if (payload.length < 29 || payload[0] !== 1) throw new Error();\n const decipher = createDecipheriv(\"aes-256-gcm\", key, payload.subarray(1, 13));\n decipher.setAAD(this.aad(namespace, name));\n decipher.setAuthTag(payload.subarray(13, 29));\n return Buffer.concat([decipher.update(payload.subarray(29)), decipher.final()]).toString(\n \"utf8\",\n );\n } catch {\n throw new SecretStoreError(\"CORRUPT_FILE\");\n }\n });\n }\n\n public async set(namespace: string, name: string, value: string): Promise<void> {\n return this.safe(async () => {\n const key = this.key(namespace);\n const file = await this.location(namespace, name, true);\n if (!file) throw new SecretStoreError(\"UNAVAILABLE\");\n try {\n this.validateOwner(await lstat(file), false);\n } catch (error) {\n if (!isMissing(error)) throw error;\n }\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv);\n cipher.setAAD(this.aad(namespace, name));\n const encrypted = Buffer.concat([cipher.update(value, \"utf8\"), cipher.final()]);\n const payload = Buffer.concat([Buffer.from([1]), iv, cipher.getAuthTag(), encrypted]);\n const temporary = `${file}.${randomBytes(16).toString(\"hex\")}.tmp`;\n const handle = await open(temporary, \"wx\", 0o600);\n try {\n try {\n await handle.chmod(0o600);\n this.validateOwner(await handle.stat(), false);\n if (((await handle.stat()).mode & 0o777) !== 0o600) {\n throw new SecretStoreError(\"UNSAFE_FILE\");\n }\n await handle.writeFile(payload);\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(temporary, file);\n } finally {\n await unlink(temporary).catch((error: unknown) => {\n if (!isMissing(error)) throw error;\n });\n }\n });\n }\n\n public async remove(namespace: string, name: string): Promise<boolean> {\n return this.safe(async () => {\n const file = await this.location(namespace, name, false);\n if (!file) return false;\n try {\n this.validateOwner(await lstat(file), false);\n await unlink(file);\n return true;\n } catch (error) {\n if (isMissing(error)) return false;\n throw error;\n }\n });\n }\n}\n"],"mappings":";;;;;;;;AAQA,SAAS,OAAO,OAAuB;CACrC,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACxD;AAEA,SAAS,UAAU,OAAyB;CAC1C,OAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;AACrE;;AAGA,IAAa,oBAAb,MAAwD;CACtD,AAAO,YACL,AAAiB,WACjB,AAAiB,eACjB;EAFiB;EACA;CAChB;CAEH,AAAQ,IAAI,WAA2B;EACrC,MAAM,SAAS,KAAK,cAAc;EAClC,IAAI,OAAO,WAAW,YAAY,CAAC,iBAAiB,KAAK,MAAM,GAC7D,MAAM,IAAI,iBAAiB,cAAc;EAE3C,kBAAkB,QAAQ,CAAC,eAAe,CAAC;EAI3C,OAAO,OAAO,KACZ,SAAS,UAAU,OAAO,KAAK,QAAQ,KAAK,GAAG,WAAW,sBAAsB,EAAE,CACpF;CACF;CAEA,AAAQ,cAAc,OAAc,WAA0B;EAC5D,KACG,YAAY,CAAC,MAAM,YAAY,IAAI,CAAC,MAAM,OAAO,KAAK,MAAM,UAAU,MACtE,QAAQ,UAAU,MAAM,QAAQ,QAAQ,OAAO,GAEhD,MAAM,IAAI,iBAAiB,aAAa;CAE5C;CAEA,MAAc,OAAO,WAAmB,QAA8C;EAGpF,MAAM,OAAO,KAAK,QAAQ,KAAK,SAAS;EACxC,MAAM,SAAS,KAAK,KAAK,MAAM,OAAO,SAAS,CAAC;EAChD,KAAK,MAAM,aAAa,CAAC,MAAM,MAAM,GAAG;GACtC,IAAI,CAAC,QACH,IAAI;IACF,MAAM,MAAM,SAAS;GACvB,SAAS,OAAO;IACd,IAAI,UAAU,KAAK,GAAG,OAAO;IAC7B,MAAM;GACR;GAEF,IAAI,QAAQ,aAAa,SAAS,MAAM,IAAI,iBAAiB,aAAa;GAC1E,IAAI,QAAQ,MAAM,MAAM,WAAW;IAAE,WAAW;IAAM,MAAM;GAAM,CAAC;GACnE,KAAK,cAAc,MAAM,MAAM,SAAS,GAAG,IAAI;GAC/C,MAAM,SAAS,MAAM,KAAK,WAAW,UAAU,WAAW,UAAU,UAAU;GAC9E,IAAI;IACF,KAAK,cAAc,MAAM,OAAO,KAAK,GAAG,IAAI;IAC5C,MAAM,OAAO,MAAM,GAAK;IACxB,MAAM,MAAM,OAAO,KAAK,EAAC,CAAE,OAAO,SAAW,KAC3C,MAAM,IAAI,iBAAiB,aAAa;GAE5C,UAAU;IACR,MAAM,OAAO,MAAM;GACrB;EACF;EACA,OAAO;CACT;CAEA,MAAc,SACZ,WACA,MACA,QAC6B;EAC7B,MAAM,SAAS,MAAM,KAAK,OAAO,WAAW,MAAM;EAClD,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG,OAAO,IAAI,EAAE,QAAQ,IAAI;CAChE;CAEA,AAAQ,IAAI,WAAmB,MAAsB;EACnD,OAAO,OAAO,KAAK,KAAK,UAAU;GAAC;GAAsB;GAAW;EAAI,CAAC,CAAC;CAC5E;CAEA,MAAc,KAAQ,WAAyC;EAC7D,IAAI;GACF,OAAO,MAAM,UAAU;EACzB,SAAS,OAAO;GACd,IAAI,iBAAiB,kBAAkB,MAAM;GAC7C,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,SAC9D,MAAM,IAAI,iBAAiB,aAAa;GAE1C,MAAM,IAAI,iBAAiB,aAAa;EAC1C;CACF;CAEA,MAAa,IAAI,WAAmB,MAA2C;EAC7E,OAAO,KAAK,KAAK,YAAY;GAC3B,MAAM,OAAO,MAAM,KAAK,SAAS,WAAW,MAAM,KAAK;GACvD,IAAI,CAAC,MAAM,OAAO;GAClB,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,KAAK,MAAM,UAAU,WAAW,UAAU,aAAa,UAAU,UAAU;GAC5F,SAAS,OAAO;IACd,IAAI,UAAU,KAAK,GAAG,OAAO;IAC7B,MAAM;GACR;GACA,IAAI;GACJ,IAAI;IACF,KAAK,cAAc,MAAM,OAAO,KAAK,GAAG,KAAK;IAC7C,MAAM,OAAO,MAAM,GAAK;IACxB,MAAM,MAAM,OAAO,KAAK,EAAC,CAAE,OAAO,SAAW,KAC3C,MAAM,IAAI,iBAAiB,aAAa;IAE1C,UAAU,MAAM,OAAO,SAAS;GAClC,UAAU;IACR,MAAM,OAAO,MAAM;GACrB;GACA,MAAM,MAAM,KAAK,IAAI,SAAS;GAC9B,IAAI;IAEF,IAAI,QAAQ,SAAS,MAAM,QAAQ,OAAO,GAAG,MAAM,IAAI,MAAM;IAC7D,MAAM,WAAW,iBAAiB,eAAe,KAAK,QAAQ,SAAS,GAAG,EAAE,CAAC;IAC7E,SAAS,OAAO,KAAK,IAAI,WAAW,IAAI,CAAC;IACzC,SAAS,WAAW,QAAQ,SAAS,IAAI,EAAE,CAAC;IAC5C,OAAO,OAAO,OAAO,CAAC,SAAS,OAAO,QAAQ,SAAS,EAAE,CAAC,GAAG,SAAS,MAAM,CAAC,CAAC,CAAC,CAAC,SAC9E,MACF;GACF,QAAQ;IACN,MAAM,IAAI,iBAAiB,cAAc;GAC3C;EACF,CAAC;CACH;CAEA,MAAa,IAAI,WAAmB,MAAc,OAA8B;EAC9E,OAAO,KAAK,KAAK,YAAY;GAC3B,MAAM,MAAM,KAAK,IAAI,SAAS;GAC9B,MAAM,OAAO,MAAM,KAAK,SAAS,WAAW,MAAM,IAAI;GACtD,IAAI,CAAC,MAAM,MAAM,IAAI,iBAAiB,aAAa;GACnD,IAAI;IACF,KAAK,cAAc,MAAM,MAAM,IAAI,GAAG,KAAK;GAC7C,SAAS,OAAO;IACd,IAAI,CAAC,UAAU,KAAK,GAAG,MAAM;GAC/B;GACA,MAAM,KAAK,YAAY,EAAE;GACzB,MAAM,SAAS,eAAe,eAAe,KAAK,EAAE;GACpD,OAAO,OAAO,KAAK,IAAI,WAAW,IAAI,CAAC;GACvC,MAAM,YAAY,OAAO,OAAO,CAAC,OAAO,OAAO,OAAO,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;GAC9E,MAAM,UAAU,OAAO,OAAO;IAAC,OAAO,KAAK,CAAC,CAAC,CAAC;IAAG;IAAI,OAAO,WAAW;IAAG;GAAS,CAAC;GACpF,MAAM,YAAY,GAAG,KAAK,GAAG,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK,EAAE;GAC7D,MAAM,SAAS,MAAM,KAAK,WAAW,MAAM,GAAK;GAChD,IAAI;IACF,IAAI;KACF,MAAM,OAAO,MAAM,GAAK;KACxB,KAAK,cAAc,MAAM,OAAO,KAAK,GAAG,KAAK;KAC7C,MAAM,MAAM,OAAO,KAAK,EAAC,CAAE,OAAO,SAAW,KAC3C,MAAM,IAAI,iBAAiB,aAAa;KAE1C,MAAM,OAAO,UAAU,OAAO;KAC9B,MAAM,OAAO,KAAK;IACpB,UAAU;KACR,MAAM,OAAO,MAAM;IACrB;IACA,MAAM,OAAO,WAAW,IAAI;GAC9B,UAAU;IACR,MAAM,OAAO,SAAS,CAAC,CAAC,OAAO,UAAmB;KAChD,IAAI,CAAC,UAAU,KAAK,GAAG,MAAM;IAC/B,CAAC;GACH;EACF,CAAC;CACH;CAEA,MAAa,OAAO,WAAmB,MAAgC;EACrE,OAAO,KAAK,KAAK,YAAY;GAC3B,MAAM,OAAO,MAAM,KAAK,SAAS,WAAW,MAAM,KAAK;GACvD,IAAI,CAAC,MAAM,OAAO;GAClB,IAAI;IACF,KAAK,cAAc,MAAM,MAAM,IAAI,GAAG,KAAK;IAC3C,MAAM,OAAO,IAAI;IACjB,OAAO;GACT,SAAS,OAAO;IACd,IAAI,UAAU,KAAK,GAAG,OAAO;IAC7B,MAAM;GACR;EACF,CAAC;CACH;AACF"}
@@ -0,0 +1,6 @@
1
+ import { SecretStoreError, SecretStoreErrorCode } from "./errors.mjs";
2
+ import { SecretBackend, SecretStoreOptions, SecretsConfigurations } from "./types.mjs";
3
+ import { MemorySecretBackend } from "./memory-backend.mjs";
4
+ import { SecretStore } from "./secret-store.mjs";
5
+ import { secrets } from "./secrets.mjs";
6
+ export { MemorySecretBackend, type SecretBackend, SecretStore, SecretStoreError, SecretStoreErrorCode, type SecretStoreOptions, type SecretsConfigurations, secrets };
@@ -0,0 +1,6 @@
1
+ import { SecretStoreError } from "./errors.mjs";
2
+ import { MemorySecretBackend } from "./memory-backend.mjs";
3
+ import { SecretStore } from "./secret-store.mjs";
4
+ import { secrets } from "./secrets.mjs";
5
+
6
+ export { MemorySecretBackend, SecretStore, SecretStoreError, secrets };
@@ -0,0 +1,50 @@
1
+ import { SecretStoreError } from "./errors.mjs";
2
+
3
+ //#region ../../packages/core/src/secrets/keyring-backend.ts
4
+ var KeyringSecretBackend = class KeyringSecretBackend {
5
+ constructor(keyring) {
6
+ this.keyring = keyring;
7
+ }
8
+ /** Read-only availability probe; never writes a setup credential. */
9
+ static async load(namespace, importer = importKeyring) {
10
+ try {
11
+ const keyring = await importer();
12
+ const backend = new KeyringSecretBackend(keyring);
13
+ await backend.entry(namespace, "").getPassword();
14
+ return backend;
15
+ } catch {
16
+ return;
17
+ }
18
+ }
19
+ entry(namespace, name) {
20
+ return new this.keyring.AsyncEntry(`warlock:${namespace}`, name || "__availability__", { linux: { store: "secret-service" } });
21
+ }
22
+ async get(namespace, name) {
23
+ try {
24
+ return await this.entry(namespace, name).getPassword() ?? void 0;
25
+ } catch {
26
+ throw new SecretStoreError("UNAVAILABLE");
27
+ }
28
+ }
29
+ async set(namespace, name, value) {
30
+ try {
31
+ await this.entry(namespace, name).setPassword(value);
32
+ } catch {
33
+ throw new SecretStoreError("UNAVAILABLE");
34
+ }
35
+ }
36
+ async remove(namespace, name) {
37
+ try {
38
+ return await this.entry(namespace, name).deletePassword();
39
+ } catch {
40
+ throw new SecretStoreError("UNAVAILABLE");
41
+ }
42
+ }
43
+ };
44
+ async function importKeyring() {
45
+ return import("@napi-rs/keyring");
46
+ }
47
+
48
+ //#endregion
49
+ export { KeyringSecretBackend };
50
+ //# sourceMappingURL=keyring-backend.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyring-backend.mjs","names":[],"sources":["../../../../../../../../packages/core/src/secrets/keyring-backend.ts"],"sourcesContent":["import { SecretStoreError } from \"./errors\";\nimport type { SecretBackend } from \"./types\";\n\n// Verified against @napi-rs/keyring 2.1.1's published index.d.ts and README:\n// https://github.com/Brooooooklyn/keyring-node (2026-10-19).\n// Structural types keep the optional peer unnecessary for TypeScript consumers.\ninterface KeyringEntry {\n getPassword(): Promise<string | null>;\n setPassword(value: string): Promise<void>;\n deletePassword(): Promise<boolean>;\n}\n\ntype KeyringModule = {\n AsyncEntry: new (\n service: string,\n username: string,\n options: { linux: { store: \"secret-service\" } },\n ) => KeyringEntry;\n};\n\nexport class KeyringSecretBackend implements SecretBackend {\n private constructor(private readonly keyring: KeyringModule) {}\n\n /** Read-only availability probe; never writes a setup credential. */\n public static async load(\n namespace: string,\n importer: () => Promise<unknown> = importKeyring,\n ): Promise<KeyringSecretBackend | undefined> {\n try {\n const keyring = (await importer()) as KeyringModule;\n const backend = new KeyringSecretBackend(keyring);\n await backend.entry(namespace, \"\").getPassword();\n return backend;\n } catch {\n return undefined;\n }\n }\n\n private entry(namespace: string, name: string): KeyringEntry {\n // Require persistence on Linux: the library's automatic keyutils fallback\n // is memory-only and loses credentials on reboot. Ignored on other OSes.\n return new this.keyring.AsyncEntry(`warlock:${namespace}`, name || \"__availability__\", {\n linux: { store: \"secret-service\" },\n });\n }\n\n public async get(namespace: string, name: string): Promise<string | undefined> {\n try {\n return (await this.entry(namespace, name).getPassword()) ?? undefined;\n } catch {\n throw new SecretStoreError(\"UNAVAILABLE\");\n }\n }\n\n public async set(namespace: string, name: string, value: string): Promise<void> {\n try {\n await this.entry(namespace, name).setPassword(value);\n } catch {\n throw new SecretStoreError(\"UNAVAILABLE\");\n }\n }\n\n public async remove(namespace: string, name: string): Promise<boolean> {\n try {\n return await this.entry(namespace, name).deletePassword();\n } catch {\n throw new SecretStoreError(\"UNAVAILABLE\");\n }\n }\n}\n\nasync function importKeyring(): Promise<unknown> {\n const peer = \"@napi-rs/keyring\";\n return import(peer);\n}\n"],"mappings":";;;AAoBA,IAAa,uBAAb,MAAa,qBAA8C;CACzD,AAAQ,YAAY,AAAiB,SAAwB;EAAxB;CAAyB;;CAG9D,aAAoB,KAClB,WACA,WAAmC,eACQ;EAC3C,IAAI;GACF,MAAM,UAAW,MAAM,SAAS;GAChC,MAAM,UAAU,IAAI,qBAAqB,OAAO;GAChD,MAAM,QAAQ,MAAM,WAAW,EAAE,CAAC,CAAC,YAAY;GAC/C,OAAO;EACT,QAAQ;GACN;EACF;CACF;CAEA,AAAQ,MAAM,WAAmB,MAA4B;EAG3D,OAAO,IAAI,KAAK,QAAQ,WAAW,WAAW,aAAa,QAAQ,oBAAoB,EACrF,OAAO,EAAE,OAAO,iBAAiB,EACnC,CAAC;CACH;CAEA,MAAa,IAAI,WAAmB,MAA2C;EAC7E,IAAI;GACF,OAAQ,MAAM,KAAK,MAAM,WAAW,IAAI,CAAC,CAAC,YAAY,KAAM;EAC9D,QAAQ;GACN,MAAM,IAAI,iBAAiB,aAAa;EAC1C;CACF;CAEA,MAAa,IAAI,WAAmB,MAAc,OAA8B;EAC9E,IAAI;GACF,MAAM,KAAK,MAAM,WAAW,IAAI,CAAC,CAAC,YAAY,KAAK;EACrD,QAAQ;GACN,MAAM,IAAI,iBAAiB,aAAa;EAC1C;CACF;CAEA,MAAa,OAAO,WAAmB,MAAgC;EACrE,IAAI;GACF,OAAO,MAAM,KAAK,MAAM,WAAW,IAAI,CAAC,CAAC,eAAe;EAC1D,QAAQ;GACN,MAAM,IAAI,iBAAiB,aAAa;EAC1C;CACF;AACF;AAEA,eAAe,gBAAkC;CAE/C,OAAO,OAAO;AAChB"}
@@ -0,0 +1,12 @@
1
+ import { SecretBackend } from "./types.mjs";
2
+ //#region ../../packages/core/src/secrets/memory-backend.d.ts
3
+ /** Isolated, namespace-aware storage. No OS, environment or filesystem access. */
4
+ export declare class MemorySecretBackend implements SecretBackend {
5
+ private readonly values;
6
+ get(namespace: string, name: string): Promise<string | undefined>;
7
+ set(namespace: string, name: string, value: string): Promise<void>;
8
+ remove(namespace: string, name: string): Promise<boolean>;
9
+ clear(): void;
10
+ }
11
+ //#endregion
12
+ //# sourceMappingURL=memory-backend.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-backend.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/secrets/memory-backend.ts"],"mappings":";;;qBAGa,+BAA+B;mBACzB;EAEJ,IAAI,mBAAmB,eAAe;EAItC,IAAI,mBAAmB,cAAc,gBAAgB;EASrD,OAAO,mBAAmB,eAAe;EAI/C"}
@@ -0,0 +1,28 @@
1
+ //#region ../../packages/core/src/secrets/memory-backend.ts
2
+ /** Isolated, namespace-aware storage. No OS, environment or filesystem access. */
3
+ var MemorySecretBackend = class {
4
+ constructor() {
5
+ this.values = /* @__PURE__ */ new Map();
6
+ }
7
+ async get(namespace, name) {
8
+ return this.values.get(namespace)?.get(name);
9
+ }
10
+ async set(namespace, name, value) {
11
+ let entries = this.values.get(namespace);
12
+ if (!entries) {
13
+ entries = /* @__PURE__ */ new Map();
14
+ this.values.set(namespace, entries);
15
+ }
16
+ entries.set(name, value);
17
+ }
18
+ async remove(namespace, name) {
19
+ return this.values.get(namespace)?.delete(name) ?? false;
20
+ }
21
+ clear() {
22
+ this.values.clear();
23
+ }
24
+ };
25
+
26
+ //#endregion
27
+ export { MemorySecretBackend };
28
+ //# sourceMappingURL=memory-backend.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-backend.mjs","names":[],"sources":["../../../../../../../../packages/core/src/secrets/memory-backend.ts"],"sourcesContent":["import type { SecretBackend } from \"./types\";\n\n/** Isolated, namespace-aware storage. No OS, environment or filesystem access. */\nexport class MemorySecretBackend implements SecretBackend {\n private readonly values = new Map<string, Map<string, string>>();\n\n public async get(namespace: string, name: string): Promise<string | undefined> {\n return this.values.get(namespace)?.get(name);\n }\n\n public async set(namespace: string, name: string, value: string): Promise<void> {\n let entries = this.values.get(namespace);\n if (!entries) {\n entries = new Map();\n this.values.set(namespace, entries);\n }\n entries.set(name, value);\n }\n\n public async remove(namespace: string, name: string): Promise<boolean> {\n return this.values.get(namespace)?.delete(name) ?? false;\n }\n\n public clear(): void {\n this.values.clear();\n }\n}\n"],"mappings":";;AAGA,IAAa,sBAAb,MAA0D;;gCAC9B,IAAI,IAAiC;;CAE/D,MAAa,IAAI,WAAmB,MAA2C;EAC7E,OAAO,KAAK,OAAO,IAAI,SAAS,CAAC,EAAE,IAAI,IAAI;CAC7C;CAEA,MAAa,IAAI,WAAmB,MAAc,OAA8B;EAC9E,IAAI,UAAU,KAAK,OAAO,IAAI,SAAS;EACvC,IAAI,CAAC,SAAS;GACZ,0BAAU,IAAI,IAAI;GAClB,KAAK,OAAO,IAAI,WAAW,OAAO;EACpC;EACA,QAAQ,IAAI,MAAM,KAAK;CACzB;CAEA,MAAa,OAAO,WAAmB,MAAgC;EACrE,OAAO,KAAK,OAAO,IAAI,SAAS,CAAC,EAAE,OAAO,IAAI,KAAK;CACrD;CAEA,AAAO,QAAc;EACnB,KAAK,OAAO,MAAM;CACpB;AACF"}
@@ -0,0 +1,14 @@
1
+ import { SecretStoreError } from "./errors.mjs";
2
+
3
+ //#region ../../packages/core/src/secrets/resolve-driver.ts
4
+ /** Resolve and enforce policy before looking up even an already cached store. */
5
+ function resolveSecretsDriver(options) {
6
+ const driver = options.driver ?? process.env.SECRETS_DRIVER ?? "auto";
7
+ if (driver !== "auto" && driver !== "memory") throw new SecretStoreError("INVALID_DRIVER");
8
+ if (driver === "memory" && process.env.NODE_ENV === "production" && options.allowMemoryInProduction !== true) throw new SecretStoreError("MEMORY_IN_PRODUCTION");
9
+ return driver;
10
+ }
11
+
12
+ //#endregion
13
+ export { resolveSecretsDriver };
14
+ //# sourceMappingURL=resolve-driver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-driver.mjs","names":[],"sources":["../../../../../../../../packages/core/src/secrets/resolve-driver.ts"],"sourcesContent":["import { SecretStoreError } from \"./errors\";\nimport type { SecretsConfigurations } from \"./types\";\n\n/** Resolve and enforce policy before looking up even an already cached store. */\nexport function resolveSecretsDriver(options: SecretsConfigurations): \"auto\" | \"memory\" {\n const driver = options.driver ?? process.env.SECRETS_DRIVER ?? \"auto\";\n if (driver !== \"auto\" && driver !== \"memory\") throw new SecretStoreError(\"INVALID_DRIVER\");\n if (\n driver === \"memory\" &&\n process.env.NODE_ENV === \"production\" &&\n options.allowMemoryInProduction !== true\n ) {\n throw new SecretStoreError(\"MEMORY_IN_PRODUCTION\");\n }\n return driver;\n}\n"],"mappings":";;;;AAIA,SAAgB,qBAAqB,SAAmD;CACtF,MAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI,kBAAkB;CAC/D,IAAI,WAAW,UAAU,WAAW,UAAU,MAAM,IAAI,iBAAiB,gBAAgB;CACzF,IACE,WAAW,YACX,QAAQ,IAAI,aAAa,gBACzB,QAAQ,4BAA4B,MAEpC,MAAM,IAAI,iBAAiB,sBAAsB;CAEnD,OAAO;AACT"}
@@ -0,0 +1,25 @@
1
+ import { SecretStoreOptions } from "./types.mjs";
2
+ //#region ../../packages/core/src/secrets/secret-store.d.ts
3
+ /** Lazily prepared, application-scoped credentials. All operations are asynchronous. */
4
+ export declare class SecretStore {
5
+ private primary;
6
+ private readonly file;
7
+ private readonly backend;
8
+ readonly namespace: string;
9
+ constructor(options: SecretStoreOptions);
10
+ private validate;
11
+ /** Exact, collision-free environment variable name (UTF-8 encoded as uppercase hex). */
12
+ environmentName(name: string): string;
13
+ private keyring;
14
+ private register;
15
+ private environment;
16
+ private assertWritable;
17
+ private safe;
18
+ get(name: string): Promise<string | undefined>;
19
+ has(name: string): Promise<boolean>;
20
+ set(name: string, value: string): Promise<void>;
21
+ /** Remove every writable layer so a fallback value cannot reappear. */
22
+ remove(name: string): Promise<boolean>;
23
+ }
24
+ //#endregion
25
+ //# sourceMappingURL=secret-store.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/secrets/secret-store.ts"],"mappings":";;;qBAWa;UACH;mBACS;mBACA;WACD;EAEhB,YAAmB,SAAS;UAoBpB;;EAYD,gBAAgB;UAMf;UAKA;UAIA;UAIA;UAQM;EAUD,IAAI,eAAe;EAkBnB,IAAI,eAAe;EAInB,IAAI,cAAc,gBAAgB;;EA2BlC,OAAO,eAAe"}
@@ -0,0 +1,120 @@
1
+ import { config } from "../config/config-getter.mjs";
2
+ import { storagePath } from "../utils/paths.mjs";
3
+ import { SecretStoreError } from "./errors.mjs";
4
+ import { MemorySecretBackend } from "./memory-backend.mjs";
5
+ import { FileSecretBackend } from "./file-backend.mjs";
6
+ import { resolveSecretsDriver } from "./resolve-driver.mjs";
7
+ import { KeyringSecretBackend } from "./keyring-backend.mjs";
8
+ import { registerLogSecret } from "@warlock.js/logger";
9
+
10
+ //#region ../../packages/core/src/secrets/secret-store.ts
11
+ /** Lazily prepared, application-scoped credentials. All operations are asynchronous. */
12
+ var SecretStore = class {
13
+ constructor(options) {
14
+ if (typeof options.namespace !== "string" || !options.namespace.trim() || Buffer.from(options.namespace).toString("utf8") !== options.namespace) throw new SecretStoreError("INVALID_NAMESPACE");
15
+ this.namespace = options.namespace;
16
+ const driver = options.backend ? "auto" : resolveSecretsDriver(options);
17
+ this.backend = options.backend ?? (driver === "memory" ? new MemorySecretBackend() : void 0);
18
+ const encryptionKey = options.encryptionKey;
19
+ this.file = new FileSecretBackend(options.directory ?? storagePath("secrets"), typeof encryptionKey === "function" ? encryptionKey : () => encryptionKey ?? config.key("encryption.key"));
20
+ }
21
+ validate(name) {
22
+ if (typeof name !== "string" || !name.length || name.length > 256 || Buffer.from(name).toString("utf8") !== name) throw new SecretStoreError("INVALID_NAME");
23
+ }
24
+ /** Exact, collision-free environment variable name (UTF-8 encoded as uppercase hex). */
25
+ environmentName(name) {
26
+ this.validate(name);
27
+ const hex = (value) => Buffer.from(value).toString("hex").toUpperCase();
28
+ return `WARLOCK_SECRET_${hex(this.namespace)}_${hex(name)}`;
29
+ }
30
+ keyring() {
31
+ this.primary ??= KeyringSecretBackend.load(this.namespace);
32
+ return this.primary;
33
+ }
34
+ register(name, value) {
35
+ registerLogSecret(value ?? "", [name, this.environmentName(name)]);
36
+ }
37
+ environment(name) {
38
+ return process.env[this.environmentName(name)];
39
+ }
40
+ assertWritable(name) {
41
+ const value = this.environment(name);
42
+ if (value !== void 0) {
43
+ this.register(name, value);
44
+ throw new SecretStoreError("READ_ONLY");
45
+ }
46
+ }
47
+ async safe(operation) {
48
+ try {
49
+ return await operation();
50
+ } catch (error) {
51
+ if (error instanceof SecretStoreError) throw error;
52
+ throw new SecretStoreError("UNAVAILABLE");
53
+ }
54
+ }
55
+ async get(name) {
56
+ this.validate(name);
57
+ this.register(name);
58
+ return this.safe(async () => {
59
+ let value;
60
+ if (this.backend) value = await this.backend.get(this.namespace, name);
61
+ else {
62
+ value = await (await this.keyring())?.get(this.namespace, name);
63
+ value ??= this.environment(name);
64
+ value ??= await this.file.get(this.namespace, name);
65
+ }
66
+ if (value !== void 0) this.register(name, value);
67
+ return value;
68
+ });
69
+ }
70
+ async has(name) {
71
+ return await this.get(name) !== void 0;
72
+ }
73
+ async set(name, value) {
74
+ this.validate(name);
75
+ if (typeof value !== "string") throw new SecretStoreError("INVALID_VALUE");
76
+ this.register(name, value);
77
+ return this.safe(async () => {
78
+ if (this.backend) {
79
+ const previous = await this.backend.get(this.namespace, name);
80
+ if (previous !== void 0) this.register(name, previous);
81
+ return this.backend.set(this.namespace, name, value);
82
+ }
83
+ this.assertWritable(name);
84
+ const primary = await this.keyring();
85
+ const backend = primary ?? this.file;
86
+ if (primary) {
87
+ const fallback = await this.file.get(this.namespace, name);
88
+ if (fallback !== void 0) this.register(name, fallback);
89
+ }
90
+ const previous = await backend.get(this.namespace, name);
91
+ if (previous !== void 0) this.register(name, previous);
92
+ await backend.set(this.namespace, name, value);
93
+ if (primary) await this.file.remove(this.namespace, name);
94
+ });
95
+ }
96
+ /** Remove every writable layer so a fallback value cannot reappear. */
97
+ async remove(name) {
98
+ this.validate(name);
99
+ this.register(name);
100
+ return this.safe(async () => {
101
+ if (this.backend) {
102
+ const previous = await this.backend.get(this.namespace, name);
103
+ if (previous !== void 0) this.register(name, previous);
104
+ return this.backend.remove(this.namespace, name);
105
+ }
106
+ this.assertWritable(name);
107
+ const primary = await this.keyring();
108
+ const previous = await primary?.get(this.namespace, name);
109
+ if (previous !== void 0) this.register(name, previous);
110
+ const fromFile = await this.file.get(this.namespace, name);
111
+ if (fromFile !== void 0) this.register(name, fromFile);
112
+ const removed = await primary?.remove(this.namespace, name) ?? false;
113
+ return await this.file.remove(this.namespace, name) || removed;
114
+ });
115
+ }
116
+ };
117
+
118
+ //#endregion
119
+ export { SecretStore };
120
+ //# sourceMappingURL=secret-store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.mjs","names":[],"sources":["../../../../../../../../packages/core/src/secrets/secret-store.ts"],"sourcesContent":["import { registerLogSecret } from \"@warlock.js/logger\";\nimport { config } from \"../config/config-getter\";\nimport { storagePath } from \"../utils/paths\";\nimport { SecretStoreError } from \"./errors\";\nimport { FileSecretBackend } from \"./file-backend\";\nimport { MemorySecretBackend } from \"./memory-backend\";\nimport { resolveSecretsDriver } from \"./resolve-driver\";\nimport { KeyringSecretBackend } from \"./keyring-backend\";\nimport type { SecretBackend, SecretStoreOptions } from \"./types\";\n\n/** Lazily prepared, application-scoped credentials. All operations are asynchronous. */\nexport class SecretStore {\n private primary: Promise<KeyringSecretBackend | undefined> | undefined;\n private readonly file: FileSecretBackend;\n private readonly backend: SecretBackend | undefined;\n public readonly namespace: string;\n\n public constructor(options: SecretStoreOptions) {\n if (\n typeof options.namespace !== \"string\" ||\n !options.namespace.trim() ||\n Buffer.from(options.namespace).toString(\"utf8\") !== options.namespace\n ) {\n throw new SecretStoreError(\"INVALID_NAMESPACE\");\n }\n this.namespace = options.namespace;\n const driver = options.backend ? \"auto\" : resolveSecretsDriver(options);\n this.backend = options.backend ?? (driver === \"memory\" ? new MemorySecretBackend() : undefined);\n const encryptionKey = options.encryptionKey;\n this.file = new FileSecretBackend(\n options.directory ?? storagePath(\"secrets\"),\n typeof encryptionKey === \"function\"\n ? encryptionKey\n : () => encryptionKey ?? config.key<string | undefined>(\"encryption.key\"),\n );\n }\n\n private validate(name: string): void {\n if (\n typeof name !== \"string\" ||\n !name.length ||\n name.length > 256 ||\n Buffer.from(name).toString(\"utf8\") !== name\n ) {\n throw new SecretStoreError(\"INVALID_NAME\");\n }\n }\n\n /** Exact, collision-free environment variable name (UTF-8 encoded as uppercase hex). */\n public environmentName(name: string): string {\n this.validate(name);\n const hex = (value: string) => Buffer.from(value).toString(\"hex\").toUpperCase();\n return `WARLOCK_SECRET_${hex(this.namespace)}_${hex(name)}`;\n }\n\n private keyring(): Promise<KeyringSecretBackend | undefined> {\n this.primary ??= KeyringSecretBackend.load(this.namespace);\n return this.primary;\n }\n\n private register(name: string, value?: string): void {\n registerLogSecret(value ?? \"\", [name, this.environmentName(name)]);\n }\n\n private environment(name: string): string | undefined {\n return process.env[this.environmentName(name)];\n }\n\n private assertWritable(name: string): void {\n const value = this.environment(name);\n if (value !== undefined) {\n this.register(name, value);\n throw new SecretStoreError(\"READ_ONLY\");\n }\n }\n\n private async safe<T>(operation: () => Promise<T>): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n if (error instanceof SecretStoreError) throw error;\n // Custom backends may throw messages containing values. Do not propagate them.\n throw new SecretStoreError(\"UNAVAILABLE\");\n }\n }\n\n public async get(name: string): Promise<string | undefined> {\n this.validate(name);\n this.register(name);\n return this.safe(async () => {\n let value: string | undefined;\n if (this.backend) {\n value = await this.backend.get(this.namespace, name);\n } else {\n const primary = await this.keyring();\n value = await primary?.get(this.namespace, name);\n value ??= this.environment(name);\n value ??= await this.file.get(this.namespace, name);\n }\n if (value !== undefined) this.register(name, value);\n return value;\n });\n }\n\n public async has(name: string): Promise<boolean> {\n return (await this.get(name)) !== undefined;\n }\n\n public async set(name: string, value: string): Promise<void> {\n this.validate(name);\n if (typeof value !== \"string\") throw new SecretStoreError(\"INVALID_VALUE\");\n // Register before any backend call, including failed writes.\n this.register(name, value);\n return this.safe(async () => {\n if (this.backend) {\n const previous = await this.backend.get(this.namespace, name);\n if (previous !== undefined) this.register(name, previous);\n return this.backend.set(this.namespace, name, value);\n }\n this.assertWritable(name);\n const primary = await this.keyring();\n const backend = primary ?? this.file;\n if (primary) {\n const fallback = await this.file.get(this.namespace, name);\n if (fallback !== undefined) this.register(name, fallback);\n }\n // Retain masking for the old value after replacement as well.\n const previous = await backend.get(this.namespace, name);\n if (previous !== undefined) this.register(name, previous);\n await backend.set(this.namespace, name, value);\n if (primary) await this.file.remove(this.namespace, name);\n });\n }\n\n /** Remove every writable layer so a fallback value cannot reappear. */\n public async remove(name: string): Promise<boolean> {\n this.validate(name);\n this.register(name);\n return this.safe(async () => {\n if (this.backend) {\n const previous = await this.backend.get(this.namespace, name);\n if (previous !== undefined) this.register(name, previous);\n return this.backend.remove(this.namespace, name);\n }\n this.assertWritable(name);\n const primary = await this.keyring();\n const previous = await primary?.get(this.namespace, name);\n if (previous !== undefined) this.register(name, previous);\n const fromFile = await this.file.get(this.namespace, name);\n if (fromFile !== undefined) this.register(name, fromFile);\n const removed = (await primary?.remove(this.namespace, name)) ?? false;\n return (await this.file.remove(this.namespace, name)) || removed;\n });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ //#region ../../packages/core/src/secrets/secrets.d.ts
2
+ /** App identity and configuration are resolved on first use, after config has loaded. */
3
+ export declare const secrets: {
4
+ get(name: string): Promise<string | undefined>;
5
+ set(name: string, value: string): Promise<void>;
6
+ remove(name: string): Promise<boolean>;
7
+ has(name: string): Promise<boolean>;
8
+ environmentName(name: string): string;
9
+ };
10
+ //#endregion
11
+ //# sourceMappingURL=secrets.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/secrets/secrets.ts"],"mappings":";;qBA6Ca;EACL,IAAG,eAAgB;EAGnB,IAAG,cAAa,gBAAkB;EAGlC,OAAM,eAAgB;EAGtB,IAAG,eAAgB;EAGzB,gBAAe"}
@@ -0,0 +1,72 @@
1
+ import { config } from "../config/config-getter.mjs";
2
+ import { rootPath, storagePath } from "../utils/paths.mjs";
3
+ import { resolveSecretsDriver } from "./resolve-driver.mjs";
4
+ import { SecretStore } from "./secret-store.mjs";
5
+ import { createHash } from "node:crypto";
6
+ import path from "node:path";
7
+
8
+ //#region ../../packages/core/src/secrets/secrets.ts
9
+ let testBackend;
10
+ let stores = /* @__PURE__ */ new Map();
11
+ /** @internal The test kit swaps only the facade, never explicitly constructed stores. */
12
+ function replaceSecretsBackend(backend) {
13
+ const previous = testBackend;
14
+ const previousStores = stores;
15
+ testBackend = backend;
16
+ stores = /* @__PURE__ */ new Map();
17
+ return () => {
18
+ testBackend = previous;
19
+ stores = previousStores;
20
+ };
21
+ }
22
+ function current() {
23
+ const root = rootPath();
24
+ const appName = config.key("app.appName") || path.basename(root);
25
+ const namespace = config.key("secrets.namespace") ?? `${appName}:${createHash("sha256").update(root).digest("hex")}`;
26
+ const directory = config.key("secrets.directory") ?? storagePath("secrets");
27
+ const options = {
28
+ driver: config.key("secrets.driver"),
29
+ allowMemoryInProduction: config.key("secrets.allowMemoryInProduction")
30
+ };
31
+ const driver = testBackend ? "auto" : resolveSecretsDriver(options);
32
+ const identity = JSON.stringify([
33
+ root,
34
+ namespace,
35
+ directory,
36
+ driver
37
+ ]);
38
+ let store = stores.get(identity);
39
+ if (!store) {
40
+ store = new SecretStore({
41
+ ...options,
42
+ driver,
43
+ namespace,
44
+ directory,
45
+ backend: testBackend
46
+ });
47
+ stores.set(identity, store);
48
+ }
49
+ return store;
50
+ }
51
+ /** App identity and configuration are resolved on first use, after config has loaded. */
52
+ const secrets = {
53
+ async get(name) {
54
+ return current().get(name);
55
+ },
56
+ async set(name, value) {
57
+ return current().set(name, value);
58
+ },
59
+ async remove(name) {
60
+ return current().remove(name);
61
+ },
62
+ async has(name) {
63
+ return current().has(name);
64
+ },
65
+ environmentName(name) {
66
+ return current().environmentName(name);
67
+ }
68
+ };
69
+
70
+ //#endregion
71
+ export { replaceSecretsBackend, secrets };
72
+ //# sourceMappingURL=secrets.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.mjs","names":[],"sources":["../../../../../../../../packages/core/src/secrets/secrets.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport { config } from \"../config/config-getter\";\nimport { rootPath, storagePath } from \"../utils/paths\";\nimport { resolveSecretsDriver } from \"./resolve-driver\";\nimport { SecretStore } from \"./secret-store\";\nimport type { SecretBackend, SecretsConfigurations } from \"./types\";\n\nlet testBackend: SecretBackend | undefined;\nlet stores = new Map<string, SecretStore>();\n\n/** @internal The test kit swaps only the facade, never explicitly constructed stores. */\nexport function replaceSecretsBackend(backend: SecretBackend): () => void {\n const previous = testBackend;\n const previousStores = stores;\n testBackend = backend;\n stores = new Map();\n return () => {\n testBackend = previous;\n stores = previousStores;\n };\n}\n\nfunction current(): SecretStore {\n const root = rootPath();\n const appName = config.key<string | undefined>(\"app.appName\") || path.basename(root);\n const namespace =\n config.key<string | undefined>(\"secrets.namespace\") ??\n `${appName}:${createHash(\"sha256\").update(root).digest(\"hex\")}`;\n const directory = config.key<string | undefined>(\"secrets.directory\") ?? storagePath(\"secrets\");\n const options: SecretsConfigurations = {\n driver: config.key<SecretsConfigurations[\"driver\"]>(\"secrets.driver\"),\n allowMemoryInProduction: config.key<boolean | undefined>(\"secrets.allowMemoryInProduction\"),\n };\n const driver = testBackend ? \"auto\" : resolveSecretsDriver(options);\n const identity = JSON.stringify([root, namespace, directory, driver]);\n let store = stores.get(identity);\n if (!store) {\n store = new SecretStore({ ...options, driver, namespace, directory, backend: testBackend });\n stores.set(identity, store);\n }\n return store;\n}\n\n/** App identity and configuration are resolved on first use, after config has loaded. */\nexport const secrets = {\n async get(name: string): Promise<string | undefined> {\n return current().get(name);\n },\n async set(name: string, value: string): Promise<void> {\n return current().set(name, value);\n },\n async remove(name: string): Promise<boolean> {\n return current().remove(name);\n },\n async has(name: string): Promise<boolean> {\n return current().has(name);\n },\n environmentName(name: string): string {\n return current().environmentName(name);\n },\n};\n"],"mappings":";;;;;;;;AAQA,IAAI;AACJ,IAAI,yBAAS,IAAI,IAAyB;;AAG1C,SAAgB,sBAAsB,SAAoC;CACxE,MAAM,WAAW;CACjB,MAAM,iBAAiB;CACvB,cAAc;CACd,yBAAS,IAAI,IAAI;CACjB,aAAa;EACX,cAAc;EACd,SAAS;CACX;AACF;AAEA,SAAS,UAAuB;CAC9B,MAAM,OAAO,SAAS;CACtB,MAAM,UAAU,OAAO,IAAwB,aAAa,KAAK,KAAK,SAAS,IAAI;CACnF,MAAM,YACJ,OAAO,IAAwB,mBAAmB,KAClD,GAAG,QAAQ,GAAG,WAAW,QAAQ,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK;CAC9D,MAAM,YAAY,OAAO,IAAwB,mBAAmB,KAAK,YAAY,SAAS;CAC9F,MAAM,UAAiC;EACrC,QAAQ,OAAO,IAAqC,gBAAgB;EACpE,yBAAyB,OAAO,IAAyB,iCAAiC;CAC5F;CACA,MAAM,SAAS,cAAc,SAAS,qBAAqB,OAAO;CAClE,MAAM,WAAW,KAAK,UAAU;EAAC;EAAM;EAAW;EAAW;CAAM,CAAC;CACpE,IAAI,QAAQ,OAAO,IAAI,QAAQ;CAC/B,IAAI,CAAC,OAAO;EACV,QAAQ,IAAI,YAAY;GAAE,GAAG;GAAS;GAAQ;GAAW;GAAW,SAAS;EAAY,CAAC;EAC1F,OAAO,IAAI,UAAU,KAAK;CAC5B;CACA,OAAO;AACT;;AAGA,MAAa,UAAU;CACrB,MAAM,IAAI,MAA2C;EACnD,OAAO,QAAQ,CAAC,CAAC,IAAI,IAAI;CAC3B;CACA,MAAM,IAAI,MAAc,OAA8B;EACpD,OAAO,QAAQ,CAAC,CAAC,IAAI,MAAM,KAAK;CAClC;CACA,MAAM,OAAO,MAAgC;EAC3C,OAAO,QAAQ,CAAC,CAAC,OAAO,IAAI;CAC9B;CACA,MAAM,IAAI,MAAgC;EACxC,OAAO,QAAQ,CAAC,CAAC,IAAI,IAAI;CAC3B;CACA,gBAAgB,MAAsB;EACpC,OAAO,QAAQ,CAAC,CAAC,gBAAgB,IAAI;CACvC;AACF"}
@@ -0,0 +1,26 @@
1
+ //#region ../../packages/core/src/secrets/types.d.ts
2
+ /** Backend implementations must never log credentials or attach them to errors. */
3
+ export interface SecretBackend {
4
+ get(namespace: string, name: string): Promise<string | undefined>;
5
+ set(namespace: string, name: string, value: string): Promise<void>;
6
+ remove(namespace: string, name: string): Promise<boolean>;
7
+ }
8
+ export type SecretsConfigurations = {
9
+ /** auto selects keychain/environment/files; memory is isolated to this process. */
10
+ driver?: "auto" | "memory";
11
+ /** Explicitly permit the volatile memory driver under NODE_ENV=production. */
12
+ allowMemoryInProduction?: boolean;
13
+ /** Stable identity shared only by processes belonging to this app installation. */
14
+ namespace?: string;
15
+ /** Trusted parent location; defaults to storage/secrets. */
16
+ directory?: string;
17
+ };
18
+ export type SecretStoreOptions = SecretsConfigurations & {
19
+ namespace: string;
20
+ /** Bypasses keychain, environment and files, for isolated tests. */
21
+ backend?: SecretBackend;
22
+ /** Defaults to the current encryption.key config, read only when files are used. */
23
+ encryptionKey?: string | (() => string | undefined);
24
+ };
25
+ //#endregion
26
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/secrets/types.ts"],"mappings":";;iBACiB;EACf,IAAI,mBAAmB,eAAe;EACtC,IAAI,mBAAmB,cAAc,gBAAgB;EACrD,OAAO,mBAAmB,eAAe;;YAG/B;;EAEV;;EAEA;;EAEA;;EAEA;;YAGU,qBAAqB;EAC/B;;EAEA,UAAU;;EAEV"}