@shipfox/api-secrets 2.0.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 (288) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +116 -0
  5. package/LICENSE +21 -0
  6. package/README.md +207 -0
  7. package/dist/config.d.ts +8 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +38 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/core/crypto.d.ts +25 -0
  12. package/dist/core/crypto.d.ts.map +1 -0
  13. package/dist/core/crypto.js +85 -0
  14. package/dist/core/crypto.js.map +1 -0
  15. package/dist/core/dek-manager.d.ts +16 -0
  16. package/dist/core/dek-manager.d.ts.map +1 -0
  17. package/dist/core/dek-manager.js +85 -0
  18. package/dist/core/dek-manager.js.map +1 -0
  19. package/dist/core/errors.d.ts +53 -0
  20. package/dist/core/errors.d.ts.map +1 -0
  21. package/dist/core/errors.js +86 -0
  22. package/dist/core/errors.js.map +1 -0
  23. package/dist/core/fingerprint.d.ts +2 -0
  24. package/dist/core/fingerprint.d.ts.map +1 -0
  25. package/dist/core/fingerprint.js +9 -0
  26. package/dist/core/fingerprint.js.map +1 -0
  27. package/dist/core/index.d.ts +30 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +97 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/key-provider.d.ts +17 -0
  32. package/dist/core/key-provider.d.ts.map +1 -0
  33. package/dist/core/key-provider.js +45 -0
  34. package/dist/core/key-provider.js.map +1 -0
  35. package/dist/core/local-secret-store.d.ts +19 -0
  36. package/dist/core/local-secret-store.d.ts.map +1 -0
  37. package/dist/core/local-secret-store.js +40 -0
  38. package/dist/core/local-secret-store.js.map +1 -0
  39. package/dist/core/management.d.ts +37 -0
  40. package/dist/core/management.d.ts.map +1 -0
  41. package/dist/core/management.js +403 -0
  42. package/dist/core/management.js.map +1 -0
  43. package/dist/core/rotate-kek.d.ts +10 -0
  44. package/dist/core/rotate-kek.d.ts.map +1 -0
  45. package/dist/core/rotate-kek.js +94 -0
  46. package/dist/core/rotate-kek.js.map +1 -0
  47. package/dist/core/secret-store.d.ts +33 -0
  48. package/dist/core/secret-store.d.ts.map +1 -0
  49. package/dist/core/secret-store.js +209 -0
  50. package/dist/core/secret-store.js.map +1 -0
  51. package/dist/core/store-resolver.d.ts +18 -0
  52. package/dist/core/store-resolver.d.ts.map +1 -0
  53. package/dist/core/store-resolver.js +10 -0
  54. package/dist/core/store-resolver.js.map +1 -0
  55. package/dist/core/store-validation.d.ts +11 -0
  56. package/dist/core/store-validation.d.ts.map +1 -0
  57. package/dist/core/store-validation.js +30 -0
  58. package/dist/core/store-validation.js.map +1 -0
  59. package/dist/core/value-cipher.d.ts +10 -0
  60. package/dist/core/value-cipher.d.ts.map +1 -0
  61. package/dist/core/value-cipher.js +22 -0
  62. package/dist/core/value-cipher.js.map +1 -0
  63. package/dist/core/variable-store.d.ts +24 -0
  64. package/dist/core/variable-store.d.ts.map +1 -0
  65. package/dist/core/variable-store.js +194 -0
  66. package/dist/core/variable-store.js.map +1 -0
  67. package/dist/db/cap.d.ts +8 -0
  68. package/dist/db/cap.d.ts.map +1 -0
  69. package/dist/db/cap.js +39 -0
  70. package/dist/db/cap.js.map +1 -0
  71. package/dist/db/data-keys.d.ts +24 -0
  72. package/dist/db/data-keys.d.ts.map +1 -0
  73. package/dist/db/data-keys.js +61 -0
  74. package/dist/db/data-keys.js.map +1 -0
  75. package/dist/db/db.d.ts +1254 -0
  76. package/dist/db/db.d.ts.map +1 -0
  77. package/dist/db/db.js +24 -0
  78. package/dist/db/db.js.map +1 -0
  79. package/dist/db/index.d.ts +13 -0
  80. package/dist/db/index.d.ts.map +1 -0
  81. package/dist/db/index.js +15 -0
  82. package/dist/db/index.js.map +1 -0
  83. package/dist/db/management.d.ts +50 -0
  84. package/dist/db/management.d.ts.map +1 -0
  85. package/dist/db/management.js +127 -0
  86. package/dist/db/management.js.map +1 -0
  87. package/dist/db/schema/common.d.ts +3 -0
  88. package/dist/db/schema/common.d.ts.map +1 -0
  89. package/dist/db/schema/common.js +8 -0
  90. package/dist/db/schema/common.js.map +1 -0
  91. package/dist/db/schema/data-keys.d.ts +103 -0
  92. package/dist/db/schema/data-keys.d.ts.map +1 -0
  93. package/dist/db/schema/data-keys.js +24 -0
  94. package/dist/db/schema/data-keys.js.map +1 -0
  95. package/dist/db/schema/outbox.d.ts +195 -0
  96. package/dist/db/schema/outbox.d.ts.map +1 -0
  97. package/dist/db/schema/outbox.js +5 -0
  98. package/dist/db/schema/outbox.js.map +1 -0
  99. package/dist/db/schema/values.d.ts +193 -0
  100. package/dist/db/schema/values.d.ts.map +1 -0
  101. package/dist/db/schema/values.js +43 -0
  102. package/dist/db/schema/values.js.map +1 -0
  103. package/dist/db/schema/variables.d.ts +175 -0
  104. package/dist/db/schema/variables.d.ts.map +1 -0
  105. package/dist/db/schema/variables.js +41 -0
  106. package/dist/db/schema/variables.js.map +1 -0
  107. package/dist/db/scope.d.ts +20 -0
  108. package/dist/db/scope.d.ts.map +1 -0
  109. package/dist/db/scope.js +19 -0
  110. package/dist/db/scope.js.map +1 -0
  111. package/dist/db/values.d.ts +33 -0
  112. package/dist/db/values.d.ts.map +1 -0
  113. package/dist/db/values.js +119 -0
  114. package/dist/db/values.js.map +1 -0
  115. package/dist/db/variables.d.ts +32 -0
  116. package/dist/db/variables.d.ts.map +1 -0
  117. package/dist/db/variables.js +117 -0
  118. package/dist/db/variables.js.map +1 -0
  119. package/dist/index.d.ts +4 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +28 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/metrics/index.d.ts +3 -0
  124. package/dist/metrics/index.d.ts.map +1 -0
  125. package/dist/metrics/index.js +4 -0
  126. package/dist/metrics/index.js.map +1 -0
  127. package/dist/metrics/instance.d.ts +39 -0
  128. package/dist/metrics/instance.d.ts.map +1 -0
  129. package/dist/metrics/instance.js +151 -0
  130. package/dist/metrics/instance.js.map +1 -0
  131. package/dist/metrics/service.d.ts +2 -0
  132. package/dist/metrics/service.d.ts.map +1 -0
  133. package/dist/metrics/service.js +21 -0
  134. package/dist/metrics/service.js.map +1 -0
  135. package/dist/presentation/dto/index.d.ts +2 -0
  136. package/dist/presentation/dto/index.d.ts.map +1 -0
  137. package/dist/presentation/dto/index.js +3 -0
  138. package/dist/presentation/dto/index.js.map +1 -0
  139. package/dist/presentation/dto/management.d.ts +7 -0
  140. package/dist/presentation/dto/management.d.ts.map +1 -0
  141. package/dist/presentation/dto/management.js +32 -0
  142. package/dist/presentation/dto/management.js.map +1 -0
  143. package/dist/presentation/e2eRoutes/create-secret.d.ts +2 -0
  144. package/dist/presentation/e2eRoutes/create-secret.d.ts.map +1 -0
  145. package/dist/presentation/e2eRoutes/create-secret.js +34 -0
  146. package/dist/presentation/e2eRoutes/create-secret.js.map +1 -0
  147. package/dist/presentation/e2eRoutes/create-variable.d.ts +2 -0
  148. package/dist/presentation/e2eRoutes/create-variable.d.ts.map +1 -0
  149. package/dist/presentation/e2eRoutes/create-variable.js +34 -0
  150. package/dist/presentation/e2eRoutes/create-variable.js.map +1 -0
  151. package/dist/presentation/e2eRoutes/index.d.ts +3 -0
  152. package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
  153. package/dist/presentation/e2eRoutes/index.js +11 -0
  154. package/dist/presentation/e2eRoutes/index.js.map +1 -0
  155. package/dist/presentation/routes/auth.d.ts +16 -0
  156. package/dist/presentation/routes/auth.d.ts.map +1 -0
  157. package/dist/presentation/routes/auth.js +42 -0
  158. package/dist/presentation/routes/auth.js.map +1 -0
  159. package/dist/presentation/routes/batch-secrets.d.ts +2 -0
  160. package/dist/presentation/routes/batch-secrets.d.ts.map +1 -0
  161. package/dist/presentation/routes/batch-secrets.js +44 -0
  162. package/dist/presentation/routes/batch-secrets.js.map +1 -0
  163. package/dist/presentation/routes/batch-variables.d.ts +2 -0
  164. package/dist/presentation/routes/batch-variables.d.ts.map +1 -0
  165. package/dist/presentation/routes/batch-variables.js +44 -0
  166. package/dist/presentation/routes/batch-variables.js.map +1 -0
  167. package/dist/presentation/routes/cursor.d.ts +3 -0
  168. package/dist/presentation/routes/cursor.d.ts.map +1 -0
  169. package/dist/presentation/routes/cursor.js +19 -0
  170. package/dist/presentation/routes/cursor.js.map +1 -0
  171. package/dist/presentation/routes/delete-secret.d.ts +2 -0
  172. package/dist/presentation/routes/delete-secret.d.ts.map +1 -0
  173. package/dist/presentation/routes/delete-secret.js +40 -0
  174. package/dist/presentation/routes/delete-secret.js.map +1 -0
  175. package/dist/presentation/routes/delete-variable.d.ts +2 -0
  176. package/dist/presentation/routes/delete-variable.d.ts.map +1 -0
  177. package/dist/presentation/routes/delete-variable.js +40 -0
  178. package/dist/presentation/routes/delete-variable.js.map +1 -0
  179. package/dist/presentation/routes/errors.d.ts +2 -0
  180. package/dist/presentation/routes/errors.d.ts.map +1 -0
  181. package/dist/presentation/routes/errors.js +73 -0
  182. package/dist/presentation/routes/errors.js.map +1 -0
  183. package/dist/presentation/routes/get-variable.d.ts +2 -0
  184. package/dist/presentation/routes/get-variable.d.ts.map +1 -0
  185. package/dist/presentation/routes/get-variable.js +42 -0
  186. package/dist/presentation/routes/get-variable.js.map +1 -0
  187. package/dist/presentation/routes/index.d.ts +3 -0
  188. package/dist/presentation/routes/index.d.ts.map +1 -0
  189. package/dist/presentation/routes/index.js +29 -0
  190. package/dist/presentation/routes/index.js.map +1 -0
  191. package/dist/presentation/routes/list-secrets.d.ts +2 -0
  192. package/dist/presentation/routes/list-secrets.d.ts.map +1 -0
  193. package/dist/presentation/routes/list-secrets.js +50 -0
  194. package/dist/presentation/routes/list-secrets.js.map +1 -0
  195. package/dist/presentation/routes/list-variables.d.ts +2 -0
  196. package/dist/presentation/routes/list-variables.d.ts.map +1 -0
  197. package/dist/presentation/routes/list-variables.js +50 -0
  198. package/dist/presentation/routes/list-variables.js.map +1 -0
  199. package/dist/presentation/routes/put-secret.d.ts +2 -0
  200. package/dist/presentation/routes/put-secret.d.ts.map +1 -0
  201. package/dist/presentation/routes/put-secret.js +52 -0
  202. package/dist/presentation/routes/put-secret.js.map +1 -0
  203. package/dist/presentation/routes/put-variable.d.ts +2 -0
  204. package/dist/presentation/routes/put-variable.d.ts.map +1 -0
  205. package/dist/presentation/routes/put-variable.js +52 -0
  206. package/dist/presentation/routes/put-variable.js.map +1 -0
  207. package/dist/presentation/routes/warnings.d.ts +5 -0
  208. package/dist/presentation/routes/warnings.d.ts.map +1 -0
  209. package/dist/presentation/routes/warnings.js +16 -0
  210. package/dist/presentation/routes/warnings.js.map +1 -0
  211. package/dist/tsconfig.test.tsbuildinfo +1 -0
  212. package/drizzle/0000_lowly_ogun.sql +59 -0
  213. package/drizzle/meta/0000_snapshot.json +547 -0
  214. package/drizzle/meta/_journal.json +13 -0
  215. package/drizzle.config.ts +7 -0
  216. package/package.json +77 -0
  217. package/src/config.test.ts +41 -0
  218. package/src/config.ts +41 -0
  219. package/src/core/crypto.test.ts +115 -0
  220. package/src/core/crypto.ts +105 -0
  221. package/src/core/dek-manager.test.ts +120 -0
  222. package/src/core/dek-manager.ts +98 -0
  223. package/src/core/errors.ts +100 -0
  224. package/src/core/fingerprint.test.ts +27 -0
  225. package/src/core/fingerprint.ts +14 -0
  226. package/src/core/index.ts +153 -0
  227. package/src/core/key-provider.test.ts +44 -0
  228. package/src/core/key-provider.ts +70 -0
  229. package/src/core/local-secret-store.ts +49 -0
  230. package/src/core/management.test.ts +37 -0
  231. package/src/core/management.ts +503 -0
  232. package/src/core/rotate-kek.test.ts +183 -0
  233. package/src/core/rotate-kek.ts +100 -0
  234. package/src/core/secret-store.test.ts +301 -0
  235. package/src/core/secret-store.ts +251 -0
  236. package/src/core/store-resolver.test.ts +20 -0
  237. package/src/core/store-resolver.ts +28 -0
  238. package/src/core/store-validation.ts +43 -0
  239. package/src/core/value-cipher.ts +26 -0
  240. package/src/core/variable-store.test.ts +106 -0
  241. package/src/core/variable-store.ts +232 -0
  242. package/src/db/cap.ts +58 -0
  243. package/src/db/data-keys.ts +99 -0
  244. package/src/db/db.ts +26 -0
  245. package/src/db/index.ts +45 -0
  246. package/src/db/management.ts +252 -0
  247. package/src/db/schema/common.ts +8 -0
  248. package/src/db/schema/data-keys.ts +31 -0
  249. package/src/db/schema/outbox.ts +4 -0
  250. package/src/db/schema/values.ts +74 -0
  251. package/src/db/schema/variables.ts +71 -0
  252. package/src/db/scope.ts +42 -0
  253. package/src/db/values.ts +191 -0
  254. package/src/db/variables.ts +187 -0
  255. package/src/index.test.ts +11 -0
  256. package/src/index.ts +50 -0
  257. package/src/metrics/index.ts +19 -0
  258. package/src/metrics/instance.ts +219 -0
  259. package/src/metrics/service.ts +19 -0
  260. package/src/presentation/dto/index.ts +1 -0
  261. package/src/presentation/dto/management.ts +36 -0
  262. package/src/presentation/e2eRoutes/create-secret.ts +31 -0
  263. package/src/presentation/e2eRoutes/create-variable.ts +34 -0
  264. package/src/presentation/e2eRoutes/index.test.ts +94 -0
  265. package/src/presentation/e2eRoutes/index.ts +8 -0
  266. package/src/presentation/routes/auth.ts +50 -0
  267. package/src/presentation/routes/batch-secrets.ts +33 -0
  268. package/src/presentation/routes/batch-variables.ts +33 -0
  269. package/src/presentation/routes/cursor.ts +23 -0
  270. package/src/presentation/routes/delete-secret.ts +26 -0
  271. package/src/presentation/routes/delete-variable.ts +26 -0
  272. package/src/presentation/routes/errors.ts +63 -0
  273. package/src/presentation/routes/get-variable.ts +32 -0
  274. package/src/presentation/routes/index.ts +29 -0
  275. package/src/presentation/routes/list-secrets.ts +41 -0
  276. package/src/presentation/routes/list-variables.ts +41 -0
  277. package/src/presentation/routes/management.test.ts +452 -0
  278. package/src/presentation/routes/put-secret.ts +39 -0
  279. package/src/presentation/routes/put-variable.ts +39 -0
  280. package/src/presentation/routes/warnings.ts +23 -0
  281. package/test/env.ts +8 -0
  282. package/test/globalSetup.ts +18 -0
  283. package/test/setup.ts +17 -0
  284. package/tsconfig.build.json +10 -0
  285. package/tsconfig.build.tsbuildinfo +1 -0
  286. package/tsconfig.json +7 -0
  287. package/tsconfig.test.json +8 -0
  288. package/vitest.config.ts +12 -0
@@ -0,0 +1,209 @@
1
+ import { countSecretValueRows, db, deleteSecretValueRows, lockWorkspaceEntries, upsertSecretValueRows } from '#db/index.js';
2
+ import { normalizedProjectId } from '#db/scope.js';
3
+ import { classifySecretsOperationError, operationScope, recordSecretsEntriesMutated, recordSecretsOperation } from '#metrics/instance.js';
4
+ import { fingerprintSecretValue } from './fingerprint.js';
5
+ import { assertWorkspaceCap, validateNamespace, validateSecretKeys, validateValueBytes } from './store-validation.js';
6
+ import { encryptSecretValue } from './value-cipher.js';
7
+ export function createSecretStoreApi(params) {
8
+ return {
9
+ async getSecret (input) {
10
+ const startedAt = Date.now();
11
+ const scope = operationScope(input);
12
+ try {
13
+ const namespace = input.namespace ?? '';
14
+ validateNamespace(namespace);
15
+ validateSecretKeys([
16
+ input.key
17
+ ]);
18
+ const value = await params.resolveSecretStore(input.store).getSecret({
19
+ ...input,
20
+ namespace
21
+ });
22
+ recordSecretsOperation({
23
+ resource: 'secret',
24
+ operation: 'get',
25
+ surface: 'internal',
26
+ scope,
27
+ outcome: value === null ? 'not_found' : 'success',
28
+ durationMs: Date.now() - startedAt
29
+ });
30
+ return value;
31
+ } catch (error) {
32
+ recordSecretsOperation({
33
+ resource: 'secret',
34
+ operation: 'get',
35
+ surface: 'internal',
36
+ scope,
37
+ outcome: classifySecretsOperationError(error),
38
+ durationMs: Date.now() - startedAt
39
+ });
40
+ throw error;
41
+ }
42
+ },
43
+ async getSecretsByNamespace (input) {
44
+ const startedAt = Date.now();
45
+ const scope = operationScope(input);
46
+ try {
47
+ const namespace = input.namespace ?? '';
48
+ validateNamespace(namespace);
49
+ const values = await params.resolveSecretStore(input.store).getSecretsByNamespace({
50
+ ...input,
51
+ namespace
52
+ });
53
+ recordSecretsOperation({
54
+ resource: 'secret',
55
+ operation: 'get_namespace',
56
+ surface: 'internal',
57
+ scope,
58
+ outcome: 'success',
59
+ durationMs: Date.now() - startedAt
60
+ });
61
+ return values;
62
+ } catch (error) {
63
+ recordSecretsOperation({
64
+ resource: 'secret',
65
+ operation: 'get_namespace',
66
+ surface: 'internal',
67
+ scope,
68
+ outcome: classifySecretsOperationError(error),
69
+ durationMs: Date.now() - startedAt
70
+ });
71
+ throw error;
72
+ }
73
+ },
74
+ async setSecrets (input) {
75
+ const startedAt = Date.now();
76
+ const scope = operationScope(input);
77
+ try {
78
+ const namespace = input.namespace ?? '';
79
+ const entries = Object.entries(input.values);
80
+ validateNamespace(namespace);
81
+ validateSecretKeys(entries.map(([key])=>key));
82
+ validateValueBytes(entries.map(([, value])=>value));
83
+ if (entries.length === 0) {
84
+ recordSecretsOperation({
85
+ resource: 'secret',
86
+ operation: 'set',
87
+ surface: 'internal',
88
+ scope,
89
+ outcome: 'success',
90
+ durationMs: Date.now() - startedAt
91
+ });
92
+ return;
93
+ }
94
+ const dek = await params.dekManager.getPlaintextDek(input.workspaceId);
95
+ const projectId = normalizedProjectId(input);
96
+ const rows = entries.map(([key, value])=>({
97
+ workspaceId: input.workspaceId,
98
+ projectId,
99
+ namespace,
100
+ key,
101
+ ciphertext: encryptSecretValue({
102
+ dek,
103
+ workspaceId: input.workspaceId,
104
+ scope: {
105
+ projectId
106
+ },
107
+ namespace,
108
+ key,
109
+ value
110
+ }),
111
+ fingerprint: fingerprintSecretValue(value, dek),
112
+ lastEditedBy: input.editedBy ?? null
113
+ }));
114
+ let existingEntries = 0;
115
+ await db().transaction(async (tx)=>{
116
+ await lockWorkspaceEntries(input.workspaceId, tx);
117
+ existingEntries = await countSecretValueRows({
118
+ workspaceId: input.workspaceId,
119
+ projectId,
120
+ namespace,
121
+ keys: entries.map(([key])=>key)
122
+ }, tx);
123
+ await assertWorkspaceCap({
124
+ workspaceId: input.workspaceId,
125
+ namespace,
126
+ incomingEntries: entries.length - existingEntries,
127
+ tx
128
+ });
129
+ await upsertSecretValueRows(rows, tx);
130
+ });
131
+ recordSecretsEntriesMutated({
132
+ resource: 'secret',
133
+ operation: 'set',
134
+ effect: 'created',
135
+ surface: 'internal',
136
+ count: entries.length - existingEntries
137
+ });
138
+ recordSecretsEntriesMutated({
139
+ resource: 'secret',
140
+ operation: 'set',
141
+ effect: 'updated',
142
+ surface: 'internal',
143
+ count: existingEntries
144
+ });
145
+ recordSecretsOperation({
146
+ resource: 'secret',
147
+ operation: 'set',
148
+ surface: 'internal',
149
+ scope,
150
+ outcome: 'success',
151
+ durationMs: Date.now() - startedAt
152
+ });
153
+ } catch (error) {
154
+ recordSecretsOperation({
155
+ resource: 'secret',
156
+ operation: 'set',
157
+ surface: 'internal',
158
+ scope,
159
+ outcome: classifySecretsOperationError(error),
160
+ durationMs: Date.now() - startedAt
161
+ });
162
+ throw error;
163
+ }
164
+ },
165
+ async deleteSecrets (input) {
166
+ const startedAt = Date.now();
167
+ const scope = operationScope(input);
168
+ try {
169
+ const namespace = input.namespace ?? '';
170
+ validateNamespace(namespace);
171
+ if (input.keys) validateSecretKeys(input.keys);
172
+ const deleted = await deleteSecretValueRows({
173
+ workspaceId: input.workspaceId,
174
+ projectId: normalizedProjectId(input),
175
+ namespace,
176
+ keys: input.keys
177
+ });
178
+ recordSecretsEntriesMutated({
179
+ resource: 'secret',
180
+ operation: 'delete',
181
+ effect: 'deleted',
182
+ surface: 'internal',
183
+ count: deleted
184
+ });
185
+ recordSecretsOperation({
186
+ resource: 'secret',
187
+ operation: 'delete',
188
+ surface: 'internal',
189
+ scope,
190
+ outcome: 'success',
191
+ durationMs: Date.now() - startedAt
192
+ });
193
+ return deleted;
194
+ } catch (error) {
195
+ recordSecretsOperation({
196
+ resource: 'secret',
197
+ operation: 'delete',
198
+ surface: 'internal',
199
+ scope,
200
+ outcome: classifySecretsOperationError(error),
201
+ durationMs: Date.now() - startedAt
202
+ });
203
+ throw error;
204
+ }
205
+ }
206
+ };
207
+ }
208
+
209
+ //# sourceMappingURL=secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/secret-store.ts"],"sourcesContent":["import {\n countSecretValueRows,\n db,\n deleteSecretValueRows,\n lockWorkspaceEntries,\n type StoreScope,\n upsertSecretValueRows,\n} from '#db/index.js';\nimport {normalizedProjectId} from '#db/scope.js';\nimport {\n classifySecretsOperationError,\n operationScope,\n recordSecretsEntriesMutated,\n recordSecretsOperation,\n} from '#metrics/instance.js';\nimport type {DekManager} from './dek-manager.js';\nimport {fingerprintSecretValue} from './fingerprint.js';\nimport type {SecretStoreProvider} from './store-resolver.js';\nimport {\n assertWorkspaceCap,\n validateNamespace,\n validateSecretKeys,\n validateValueBytes,\n} from './store-validation.js';\nimport {encryptSecretValue} from './value-cipher.js';\n\nexport interface SetSecretsParams extends StoreScope {\n workspaceId: string;\n namespace?: string | undefined;\n values: Record<string, string>;\n editedBy?: string | null | undefined;\n}\n\nexport interface DeleteSecretsParams extends StoreScope {\n workspaceId: string;\n namespace?: string | undefined;\n keys?: string[] | undefined;\n}\n\nexport function createSecretStoreApi(params: {\n dekManager: DekManager;\n resolveSecretStore: (name?: string | undefined) => SecretStoreProvider;\n}) {\n return {\n async getSecret(\n input: StoreScope & {\n workspaceId: string;\n namespace?: string | undefined;\n key: string;\n store?: string;\n },\n ) {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n validateNamespace(namespace);\n validateSecretKeys([input.key]);\n const value = await params.resolveSecretStore(input.store).getSecret({...input, namespace});\n recordSecretsOperation({\n resource: 'secret',\n operation: 'get',\n surface: 'internal',\n scope,\n outcome: value === null ? 'not_found' : 'success',\n durationMs: Date.now() - startedAt,\n });\n return value;\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'get',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async getSecretsByNamespace(\n input: StoreScope & {workspaceId: string; namespace?: string | undefined; store?: string},\n ) {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n validateNamespace(namespace);\n const values = await params\n .resolveSecretStore(input.store)\n .getSecretsByNamespace({...input, namespace});\n recordSecretsOperation({\n resource: 'secret',\n operation: 'get_namespace',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return values;\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'get_namespace',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async setSecrets(input: SetSecretsParams): Promise<void> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n const entries = Object.entries(input.values);\n validateNamespace(namespace);\n validateSecretKeys(entries.map(([key]) => key));\n validateValueBytes(entries.map(([, value]) => value));\n if (entries.length === 0) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'set',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return;\n }\n\n const dek = await params.dekManager.getPlaintextDek(input.workspaceId);\n const projectId = normalizedProjectId(input);\n const rows = entries.map(([key, value]) => ({\n workspaceId: input.workspaceId,\n projectId,\n namespace,\n key,\n ciphertext: encryptSecretValue({\n dek,\n workspaceId: input.workspaceId,\n scope: {projectId},\n namespace,\n key,\n value,\n }),\n fingerprint: fingerprintSecretValue(value, dek),\n lastEditedBy: input.editedBy ?? null,\n }));\n\n let existingEntries = 0;\n await db().transaction(async (tx) => {\n await lockWorkspaceEntries(input.workspaceId, tx);\n existingEntries = await countSecretValueRows(\n {\n workspaceId: input.workspaceId,\n projectId,\n namespace,\n keys: entries.map(([key]) => key),\n },\n tx,\n );\n await assertWorkspaceCap({\n workspaceId: input.workspaceId,\n namespace,\n incomingEntries: entries.length - existingEntries,\n tx,\n });\n await upsertSecretValueRows(rows, tx);\n });\n\n recordSecretsEntriesMutated({\n resource: 'secret',\n operation: 'set',\n effect: 'created',\n surface: 'internal',\n count: entries.length - existingEntries,\n });\n recordSecretsEntriesMutated({\n resource: 'secret',\n operation: 'set',\n effect: 'updated',\n surface: 'internal',\n count: existingEntries,\n });\n recordSecretsOperation({\n resource: 'secret',\n operation: 'set',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'set',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async deleteSecrets(input: DeleteSecretsParams): Promise<number> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n validateNamespace(namespace);\n if (input.keys) validateSecretKeys(input.keys);\n\n const deleted = await deleteSecretValueRows({\n workspaceId: input.workspaceId,\n projectId: normalizedProjectId(input),\n namespace,\n keys: input.keys,\n });\n recordSecretsEntriesMutated({\n resource: 'secret',\n operation: 'delete',\n effect: 'deleted',\n surface: 'internal',\n count: deleted,\n });\n recordSecretsOperation({\n resource: 'secret',\n operation: 'delete',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return deleted;\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'delete',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n };\n}\n"],"names":["countSecretValueRows","db","deleteSecretValueRows","lockWorkspaceEntries","upsertSecretValueRows","normalizedProjectId","classifySecretsOperationError","operationScope","recordSecretsEntriesMutated","recordSecretsOperation","fingerprintSecretValue","assertWorkspaceCap","validateNamespace","validateSecretKeys","validateValueBytes","encryptSecretValue","createSecretStoreApi","params","getSecret","input","startedAt","Date","now","scope","namespace","key","value","resolveSecretStore","store","resource","operation","surface","outcome","durationMs","error","getSecretsByNamespace","values","setSecrets","entries","Object","map","length","dek","dekManager","getPlaintextDek","workspaceId","projectId","rows","ciphertext","fingerprint","lastEditedBy","editedBy","existingEntries","transaction","tx","keys","incomingEntries","effect","count","deleteSecrets","deleted"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export declare const BUILTIN_LOCAL_STORE = "local";
2
+ export interface GetSecretParams {
3
+ workspaceId: string;
4
+ projectId?: string | null | undefined;
5
+ namespace: string;
6
+ key: string;
7
+ }
8
+ export interface GetSecretsByNamespaceParams {
9
+ workspaceId: string;
10
+ projectId?: string | null | undefined;
11
+ namespace: string;
12
+ }
13
+ export interface SecretStoreProvider {
14
+ getSecret(params: GetSecretParams): Promise<string | null>;
15
+ getSecretsByNamespace(params: GetSecretsByNamespaceParams): Promise<Record<string, string>>;
16
+ }
17
+ export declare function createSecretStoreResolver(localProvider: SecretStoreProvider): (name?: string) => SecretStoreProvider;
18
+ //# sourceMappingURL=store-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-resolver.d.ts","sourceRoot":"","sources":["../../src/core/store-resolver.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3D,qBAAqB,CAAC,MAAM,EAAE,2BAA2B,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7F;AAED,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,mBAAmB,IACvC,aAA0B,KAAG,mBAAmB,CAIpF"}
@@ -0,0 +1,10 @@
1
+ import { UnknownSecretStoreError } from './errors.js';
2
+ export const BUILTIN_LOCAL_STORE = 'local';
3
+ export function createSecretStoreResolver(localProvider) {
4
+ return function resolveSecretStore(name = BUILTIN_LOCAL_STORE) {
5
+ if (name === BUILTIN_LOCAL_STORE) return localProvider;
6
+ throw new UnknownSecretStoreError(name);
7
+ };
8
+ }
9
+
10
+ //# sourceMappingURL=store-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/store-resolver.ts"],"sourcesContent":["import {UnknownSecretStoreError} from './errors.js';\n\nexport const BUILTIN_LOCAL_STORE = 'local';\n\nexport interface GetSecretParams {\n workspaceId: string;\n projectId?: string | null | undefined;\n namespace: string;\n key: string;\n}\n\nexport interface GetSecretsByNamespaceParams {\n workspaceId: string;\n projectId?: string | null | undefined;\n namespace: string;\n}\n\nexport interface SecretStoreProvider {\n getSecret(params: GetSecretParams): Promise<string | null>;\n getSecretsByNamespace(params: GetSecretsByNamespaceParams): Promise<Record<string, string>>;\n}\n\nexport function createSecretStoreResolver(localProvider: SecretStoreProvider) {\n return function resolveSecretStore(name = BUILTIN_LOCAL_STORE): SecretStoreProvider {\n if (name === BUILTIN_LOCAL_STORE) return localProvider;\n throw new UnknownSecretStoreError(name);\n };\n}\n"],"names":["UnknownSecretStoreError","BUILTIN_LOCAL_STORE","createSecretStoreResolver","localProvider","resolveSecretStore","name"],"mappings":"AAAA,SAAQA,uBAAuB,QAAO,cAAc;AAEpD,OAAO,MAAMC,sBAAsB,QAAQ;AAoB3C,OAAO,SAASC,0BAA0BC,aAAkC;IAC1E,OAAO,SAASC,mBAAmBC,OAAOJ,mBAAmB;QAC3D,IAAII,SAASJ,qBAAqB,OAAOE;QACzC,MAAM,IAAIH,wBAAwBK;IACpC;AACF"}
@@ -0,0 +1,11 @@
1
+ import { type Tx } from '#db/index.js';
2
+ export declare function validateNamespace(namespace: string): void;
3
+ export declare function validateSecretKeys(keys: Iterable<string>): void;
4
+ export declare function validateValueBytes(values: Iterable<string>): void;
5
+ export declare function assertWorkspaceCap(params: {
6
+ workspaceId: string;
7
+ namespace: string;
8
+ incomingEntries: number;
9
+ tx: Tx;
10
+ }): Promise<void>;
11
+ //# sourceMappingURL=store-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-validation.d.ts","sourceRoot":"","sources":["../../src/core/store-validation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,EAAE,EAAC,MAAM,cAAc,CAAC;AAQ5D,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAI/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAMjE;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE,EAAE,CAAC;CACR,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB"}
@@ -0,0 +1,30 @@
1
+ import { isSystemNamespace, namespaceSchema, secretKeySchema } from '@shipfox/api-secrets-dto';
2
+ import { config, MAX_VALUE_BYTES } from '#config.js';
3
+ import { countWorkspaceEntries } from '#db/index.js';
4
+ import { NamespaceValidationError, SecretKeyValidationError, SecretValueTooLargeError, WorkspaceSecretCapExceededError } from './errors.js';
5
+ export function validateNamespace(namespace) {
6
+ if (namespaceSchema.safeParse(namespace).success) return;
7
+ throw new NamespaceValidationError(namespace);
8
+ }
9
+ export function validateSecretKeys(keys) {
10
+ for (const key of keys){
11
+ if (!secretKeySchema.safeParse(key).success) throw new SecretKeyValidationError(key);
12
+ }
13
+ }
14
+ export function validateValueBytes(values) {
15
+ for (const value of values){
16
+ if (Buffer.byteLength(value, 'utf8') > MAX_VALUE_BYTES) {
17
+ throw new SecretValueTooLargeError(MAX_VALUE_BYTES);
18
+ }
19
+ }
20
+ }
21
+ export async function assertWorkspaceCap(params) {
22
+ // System namespaces are internal-only; public management routes do not accept a namespace.
23
+ if (isSystemNamespace(params.namespace)) return;
24
+ const count = await countWorkspaceEntries(params.workspaceId, params.tx);
25
+ if (count + params.incomingEntries > config.SECRETS_MAX_PER_WORKSPACE) {
26
+ throw new WorkspaceSecretCapExceededError(params.workspaceId, config.SECRETS_MAX_PER_WORKSPACE);
27
+ }
28
+ }
29
+
30
+ //# sourceMappingURL=store-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/store-validation.ts"],"sourcesContent":["import {isSystemNamespace, namespaceSchema, secretKeySchema} from '@shipfox/api-secrets-dto';\nimport {config, MAX_VALUE_BYTES} from '#config.js';\nimport {countWorkspaceEntries, type Tx} from '#db/index.js';\nimport {\n NamespaceValidationError,\n SecretKeyValidationError,\n SecretValueTooLargeError,\n WorkspaceSecretCapExceededError,\n} from './errors.js';\n\nexport function validateNamespace(namespace: string): void {\n if (namespaceSchema.safeParse(namespace).success) return;\n throw new NamespaceValidationError(namespace);\n}\n\nexport function validateSecretKeys(keys: Iterable<string>): void {\n for (const key of keys) {\n if (!secretKeySchema.safeParse(key).success) throw new SecretKeyValidationError(key);\n }\n}\n\nexport function validateValueBytes(values: Iterable<string>): void {\n for (const value of values) {\n if (Buffer.byteLength(value, 'utf8') > MAX_VALUE_BYTES) {\n throw new SecretValueTooLargeError(MAX_VALUE_BYTES);\n }\n }\n}\n\nexport async function assertWorkspaceCap(params: {\n workspaceId: string;\n namespace: string;\n incomingEntries: number;\n tx: Tx;\n}): Promise<void> {\n // System namespaces are internal-only; public management routes do not accept a namespace.\n if (isSystemNamespace(params.namespace)) return;\n\n const count = await countWorkspaceEntries(params.workspaceId, params.tx);\n if (count + params.incomingEntries > config.SECRETS_MAX_PER_WORKSPACE) {\n throw new WorkspaceSecretCapExceededError(params.workspaceId, config.SECRETS_MAX_PER_WORKSPACE);\n }\n}\n"],"names":["isSystemNamespace","namespaceSchema","secretKeySchema","config","MAX_VALUE_BYTES","countWorkspaceEntries","NamespaceValidationError","SecretKeyValidationError","SecretValueTooLargeError","WorkspaceSecretCapExceededError","validateNamespace","namespace","safeParse","success","validateSecretKeys","keys","key","validateValueBytes","values","value","Buffer","byteLength","assertWorkspaceCap","params","count","workspaceId","tx","incomingEntries","SECRETS_MAX_PER_WORKSPACE"],"mappings":"AAAA,SAAQA,iBAAiB,EAAEC,eAAe,EAAEC,eAAe,QAAO,2BAA2B;AAC7F,SAAQC,MAAM,EAAEC,eAAe,QAAO,aAAa;AACnD,SAAQC,qBAAqB,QAAgB,eAAe;AAC5D,SACEC,wBAAwB,EACxBC,wBAAwB,EACxBC,wBAAwB,EACxBC,+BAA+B,QAC1B,cAAc;AAErB,OAAO,SAASC,kBAAkBC,SAAiB;IACjD,IAAIV,gBAAgBW,SAAS,CAACD,WAAWE,OAAO,EAAE;IAClD,MAAM,IAAIP,yBAAyBK;AACrC;AAEA,OAAO,SAASG,mBAAmBC,IAAsB;IACvD,KAAK,MAAMC,OAAOD,KAAM;QACtB,IAAI,CAACb,gBAAgBU,SAAS,CAACI,KAAKH,OAAO,EAAE,MAAM,IAAIN,yBAAyBS;IAClF;AACF;AAEA,OAAO,SAASC,mBAAmBC,MAAwB;IACzD,KAAK,MAAMC,SAASD,OAAQ;QAC1B,IAAIE,OAAOC,UAAU,CAACF,OAAO,UAAUf,iBAAiB;YACtD,MAAM,IAAII,yBAAyBJ;QACrC;IACF;AACF;AAEA,OAAO,eAAekB,mBAAmBC,MAKxC;IACC,2FAA2F;IAC3F,IAAIvB,kBAAkBuB,OAAOZ,SAAS,GAAG;IAEzC,MAAMa,QAAQ,MAAMnB,sBAAsBkB,OAAOE,WAAW,EAAEF,OAAOG,EAAE;IACvE,IAAIF,QAAQD,OAAOI,eAAe,GAAGxB,OAAOyB,yBAAyB,EAAE;QACrE,MAAM,IAAInB,gCAAgCc,OAAOE,WAAW,EAAEtB,OAAOyB,yBAAyB;IAChG;AACF"}
@@ -0,0 +1,10 @@
1
+ import { type SecretValueAadParams } from './crypto.js';
2
+ export declare function encryptSecretValue(params: SecretValueAadParams & {
3
+ dek: Buffer;
4
+ value: string;
5
+ }): string;
6
+ export declare function decryptSecretValue(params: SecretValueAadParams & {
7
+ dek: Buffer;
8
+ ciphertext: string;
9
+ }): string;
10
+ //# sourceMappingURL=value-cipher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"value-cipher.d.ts","sourceRoot":"","sources":["../../src/core/value-cipher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,oBAAoB,EAAC,MAAM,aAAa,CAAC;AAG3F,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,oBAAoB,GAAG;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GAC1D,MAAM,CAMR;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,oBAAoB,GAAG;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,GAC/D,MAAM,CAUR"}
@@ -0,0 +1,22 @@
1
+ import { aadForValue, aesGcmOpen, aesGcmSeal } from './crypto.js';
2
+ import { SecretDecryptionError } from './errors.js';
3
+ export function encryptSecretValue(params) {
4
+ return aesGcmSeal({
5
+ key: params.dek,
6
+ plaintext: Buffer.from(params.value, 'utf8'),
7
+ aad: aadForValue(params)
8
+ });
9
+ }
10
+ export function decryptSecretValue(params) {
11
+ try {
12
+ return aesGcmOpen({
13
+ key: params.dek,
14
+ encoded: params.ciphertext,
15
+ aad: aadForValue(params)
16
+ }).toString('utf8');
17
+ } catch {
18
+ throw new SecretDecryptionError();
19
+ }
20
+ }
21
+
22
+ //# sourceMappingURL=value-cipher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/value-cipher.ts"],"sourcesContent":["import {aadForValue, aesGcmOpen, aesGcmSeal, type SecretValueAadParams} from './crypto.js';\nimport {SecretDecryptionError} from './errors.js';\n\nexport function encryptSecretValue(\n params: SecretValueAadParams & {dek: Buffer; value: string},\n): string {\n return aesGcmSeal({\n key: params.dek,\n plaintext: Buffer.from(params.value, 'utf8'),\n aad: aadForValue(params),\n });\n}\n\nexport function decryptSecretValue(\n params: SecretValueAadParams & {dek: Buffer; ciphertext: string},\n): string {\n try {\n return aesGcmOpen({\n key: params.dek,\n encoded: params.ciphertext,\n aad: aadForValue(params),\n }).toString('utf8');\n } catch {\n throw new SecretDecryptionError();\n }\n}\n"],"names":["aadForValue","aesGcmOpen","aesGcmSeal","SecretDecryptionError","encryptSecretValue","params","key","dek","plaintext","Buffer","from","value","aad","decryptSecretValue","encoded","ciphertext","toString"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,UAAU,EAAEC,UAAU,QAAkC,cAAc;AAC3F,SAAQC,qBAAqB,QAAO,cAAc;AAElD,OAAO,SAASC,mBACdC,MAA2D;IAE3D,OAAOH,WAAW;QAChBI,KAAKD,OAAOE,GAAG;QACfC,WAAWC,OAAOC,IAAI,CAACL,OAAOM,KAAK,EAAE;QACrCC,KAAKZ,YAAYK;IACnB;AACF;AAEA,OAAO,SAASQ,mBACdR,MAAgE;IAEhE,IAAI;QACF,OAAOJ,WAAW;YAChBK,KAAKD,OAAOE,GAAG;YACfO,SAAST,OAAOU,UAAU;YAC1BH,KAAKZ,YAAYK;QACnB,GAAGW,QAAQ,CAAC;IACd,EAAE,OAAM;QACN,MAAM,IAAIb;IACZ;AACF"}
@@ -0,0 +1,24 @@
1
+ import { type StoreScope } from '#db/index.js';
2
+ export interface SetVariablesParams extends StoreScope {
3
+ workspaceId: string;
4
+ namespace?: string | undefined;
5
+ values: Record<string, string>;
6
+ editedBy?: string | null | undefined;
7
+ }
8
+ export interface DeleteVariablesParams extends StoreScope {
9
+ workspaceId: string;
10
+ namespace?: string | undefined;
11
+ keys?: string[] | undefined;
12
+ }
13
+ export declare function getVariable(input: StoreScope & {
14
+ workspaceId: string;
15
+ namespace?: string | undefined;
16
+ key: string;
17
+ }): Promise<string | null>;
18
+ export declare function getVariablesByNamespace(input: StoreScope & {
19
+ workspaceId: string;
20
+ namespace?: string | undefined;
21
+ }): Promise<Record<string, string>>;
22
+ export declare function setVariables(input: SetVariablesParams): Promise<void>;
23
+ export declare function deleteVariables(input: DeleteVariablesParams): Promise<number>;
24
+ //# sourceMappingURL=variable-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variable-store.d.ts","sourceRoot":"","sources":["../../src/core/variable-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EAEhB,MAAM,cAAc,CAAC;AAetB,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,qBAAsB,SAAQ,UAAU;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC7B;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,UAAU,GAAG;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,GACrF,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,UAAU,GAAG;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GACxE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA4BjC;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsF3E;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCnF"}
@@ -0,0 +1,194 @@
1
+ import { countSecretVariableRows, db, deleteSecretVariableRows, getSecretVariableRowWithPrecedence, listSecretVariableRowsByNamespace, lockWorkspaceEntries, upsertSecretVariableRows } from '#db/index.js';
2
+ import { normalizedProjectId } from '#db/scope.js';
3
+ import { classifySecretsOperationError, operationScope, recordSecretsEntriesMutated, recordSecretsOperation } from '#metrics/instance.js';
4
+ import { assertWorkspaceCap, validateNamespace, validateSecretKeys, validateValueBytes } from './store-validation.js';
5
+ export async function getVariable(input) {
6
+ const startedAt = Date.now();
7
+ const scope = operationScope(input);
8
+ try {
9
+ const namespace = input.namespace ?? '';
10
+ validateNamespace(namespace);
11
+ validateSecretKeys([
12
+ input.key
13
+ ]);
14
+ const row = await getSecretVariableRowWithPrecedence({
15
+ ...input,
16
+ namespace
17
+ });
18
+ recordSecretsOperation({
19
+ resource: 'variable',
20
+ operation: 'get',
21
+ surface: 'internal',
22
+ scope,
23
+ outcome: row ? 'success' : 'not_found',
24
+ durationMs: Date.now() - startedAt
25
+ });
26
+ return row?.value ?? null;
27
+ } catch (error) {
28
+ recordSecretsOperation({
29
+ resource: 'variable',
30
+ operation: 'get',
31
+ surface: 'internal',
32
+ scope,
33
+ outcome: classifySecretsOperationError(error),
34
+ durationMs: Date.now() - startedAt
35
+ });
36
+ throw error;
37
+ }
38
+ }
39
+ export async function getVariablesByNamespace(input) {
40
+ const startedAt = Date.now();
41
+ const scope = operationScope(input);
42
+ try {
43
+ const namespace = input.namespace ?? '';
44
+ validateNamespace(namespace);
45
+ const rows = await listSecretVariableRowsByNamespace({
46
+ ...input,
47
+ namespace
48
+ });
49
+ recordSecretsOperation({
50
+ resource: 'variable',
51
+ operation: 'get_namespace',
52
+ surface: 'internal',
53
+ scope,
54
+ outcome: 'success',
55
+ durationMs: Date.now() - startedAt
56
+ });
57
+ return Object.fromEntries(rows.map((row)=>[
58
+ row.key,
59
+ row.value
60
+ ]));
61
+ } catch (error) {
62
+ recordSecretsOperation({
63
+ resource: 'variable',
64
+ operation: 'get_namespace',
65
+ surface: 'internal',
66
+ scope,
67
+ outcome: classifySecretsOperationError(error),
68
+ durationMs: Date.now() - startedAt
69
+ });
70
+ throw error;
71
+ }
72
+ }
73
+ export async function setVariables(input) {
74
+ const startedAt = Date.now();
75
+ const scope = operationScope(input);
76
+ try {
77
+ const namespace = input.namespace ?? '';
78
+ const entries = Object.entries(input.values);
79
+ validateNamespace(namespace);
80
+ validateSecretKeys(entries.map(([key])=>key));
81
+ validateValueBytes(entries.map(([, value])=>value));
82
+ if (entries.length === 0) {
83
+ recordSecretsOperation({
84
+ resource: 'variable',
85
+ operation: 'set',
86
+ surface: 'internal',
87
+ scope,
88
+ outcome: 'success',
89
+ durationMs: Date.now() - startedAt
90
+ });
91
+ return;
92
+ }
93
+ const projectId = normalizedProjectId(input);
94
+ let existingEntries = 0;
95
+ await db().transaction(async (tx)=>{
96
+ await lockWorkspaceEntries(input.workspaceId, tx);
97
+ existingEntries = await countSecretVariableRows({
98
+ workspaceId: input.workspaceId,
99
+ projectId,
100
+ namespace,
101
+ keys: entries.map(([key])=>key)
102
+ }, tx);
103
+ await assertWorkspaceCap({
104
+ workspaceId: input.workspaceId,
105
+ namespace,
106
+ incomingEntries: entries.length - existingEntries,
107
+ tx
108
+ });
109
+ await upsertSecretVariableRows(entries.map(([key, value])=>({
110
+ workspaceId: input.workspaceId,
111
+ projectId,
112
+ namespace,
113
+ key,
114
+ value,
115
+ lastEditedBy: input.editedBy ?? null
116
+ })), tx);
117
+ });
118
+ recordSecretsEntriesMutated({
119
+ resource: 'variable',
120
+ operation: 'set',
121
+ effect: 'created',
122
+ surface: 'internal',
123
+ count: entries.length - existingEntries
124
+ });
125
+ recordSecretsEntriesMutated({
126
+ resource: 'variable',
127
+ operation: 'set',
128
+ effect: 'updated',
129
+ surface: 'internal',
130
+ count: existingEntries
131
+ });
132
+ recordSecretsOperation({
133
+ resource: 'variable',
134
+ operation: 'set',
135
+ surface: 'internal',
136
+ scope,
137
+ outcome: 'success',
138
+ durationMs: Date.now() - startedAt
139
+ });
140
+ } catch (error) {
141
+ recordSecretsOperation({
142
+ resource: 'variable',
143
+ operation: 'set',
144
+ surface: 'internal',
145
+ scope,
146
+ outcome: classifySecretsOperationError(error),
147
+ durationMs: Date.now() - startedAt
148
+ });
149
+ throw error;
150
+ }
151
+ }
152
+ export async function deleteVariables(input) {
153
+ const startedAt = Date.now();
154
+ const scope = operationScope(input);
155
+ try {
156
+ const namespace = input.namespace ?? '';
157
+ validateNamespace(namespace);
158
+ if (input.keys) validateSecretKeys(input.keys);
159
+ const deleted = await deleteSecretVariableRows({
160
+ workspaceId: input.workspaceId,
161
+ projectId: normalizedProjectId(input),
162
+ namespace,
163
+ keys: input.keys
164
+ });
165
+ recordSecretsEntriesMutated({
166
+ resource: 'variable',
167
+ operation: 'delete',
168
+ effect: 'deleted',
169
+ surface: 'internal',
170
+ count: deleted
171
+ });
172
+ recordSecretsOperation({
173
+ resource: 'variable',
174
+ operation: 'delete',
175
+ surface: 'internal',
176
+ scope,
177
+ outcome: 'success',
178
+ durationMs: Date.now() - startedAt
179
+ });
180
+ return deleted;
181
+ } catch (error) {
182
+ recordSecretsOperation({
183
+ resource: 'variable',
184
+ operation: 'delete',
185
+ surface: 'internal',
186
+ scope,
187
+ outcome: classifySecretsOperationError(error),
188
+ durationMs: Date.now() - startedAt
189
+ });
190
+ throw error;
191
+ }
192
+ }
193
+
194
+ //# sourceMappingURL=variable-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/variable-store.ts"],"sourcesContent":["import {\n countSecretVariableRows,\n db,\n deleteSecretVariableRows,\n getSecretVariableRowWithPrecedence,\n listSecretVariableRowsByNamespace,\n lockWorkspaceEntries,\n type StoreScope,\n upsertSecretVariableRows,\n} from '#db/index.js';\nimport {normalizedProjectId} from '#db/scope.js';\nimport {\n classifySecretsOperationError,\n operationScope,\n recordSecretsEntriesMutated,\n recordSecretsOperation,\n} from '#metrics/instance.js';\nimport {\n assertWorkspaceCap,\n validateNamespace,\n validateSecretKeys,\n validateValueBytes,\n} from './store-validation.js';\n\nexport interface SetVariablesParams extends StoreScope {\n workspaceId: string;\n namespace?: string | undefined;\n values: Record<string, string>;\n editedBy?: string | null | undefined;\n}\n\nexport interface DeleteVariablesParams extends StoreScope {\n workspaceId: string;\n namespace?: string | undefined;\n keys?: string[] | undefined;\n}\n\nexport async function getVariable(\n input: StoreScope & {workspaceId: string; namespace?: string | undefined; key: string},\n): Promise<string | null> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n validateNamespace(namespace);\n validateSecretKeys([input.key]);\n\n const row = await getSecretVariableRowWithPrecedence({...input, namespace});\n recordSecretsOperation({\n resource: 'variable',\n operation: 'get',\n surface: 'internal',\n scope,\n outcome: row ? 'success' : 'not_found',\n durationMs: Date.now() - startedAt,\n });\n return row?.value ?? null;\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'get',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n}\n\nexport async function getVariablesByNamespace(\n input: StoreScope & {workspaceId: string; namespace?: string | undefined},\n): Promise<Record<string, string>> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n validateNamespace(namespace);\n\n const rows = await listSecretVariableRowsByNamespace({...input, namespace});\n recordSecretsOperation({\n resource: 'variable',\n operation: 'get_namespace',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return Object.fromEntries(rows.map((row) => [row.key, row.value]));\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'get_namespace',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n}\n\nexport async function setVariables(input: SetVariablesParams): Promise<void> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n const entries = Object.entries(input.values);\n validateNamespace(namespace);\n validateSecretKeys(entries.map(([key]) => key));\n validateValueBytes(entries.map(([, value]) => value));\n if (entries.length === 0) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'set',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return;\n }\n\n const projectId = normalizedProjectId(input);\n let existingEntries = 0;\n await db().transaction(async (tx) => {\n await lockWorkspaceEntries(input.workspaceId, tx);\n existingEntries = await countSecretVariableRows(\n {\n workspaceId: input.workspaceId,\n projectId,\n namespace,\n keys: entries.map(([key]) => key),\n },\n tx,\n );\n await assertWorkspaceCap({\n workspaceId: input.workspaceId,\n namespace,\n incomingEntries: entries.length - existingEntries,\n tx,\n });\n await upsertSecretVariableRows(\n entries.map(([key, value]) => ({\n workspaceId: input.workspaceId,\n projectId,\n namespace,\n key,\n value,\n lastEditedBy: input.editedBy ?? null,\n })),\n tx,\n );\n });\n\n recordSecretsEntriesMutated({\n resource: 'variable',\n operation: 'set',\n effect: 'created',\n surface: 'internal',\n count: entries.length - existingEntries,\n });\n recordSecretsEntriesMutated({\n resource: 'variable',\n operation: 'set',\n effect: 'updated',\n surface: 'internal',\n count: existingEntries,\n });\n recordSecretsOperation({\n resource: 'variable',\n operation: 'set',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'set',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n}\n\nexport async function deleteVariables(input: DeleteVariablesParams): Promise<number> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const namespace = input.namespace ?? '';\n validateNamespace(namespace);\n if (input.keys) validateSecretKeys(input.keys);\n\n const deleted = await deleteSecretVariableRows({\n workspaceId: input.workspaceId,\n projectId: normalizedProjectId(input),\n namespace,\n keys: input.keys,\n });\n recordSecretsEntriesMutated({\n resource: 'variable',\n operation: 'delete',\n effect: 'deleted',\n surface: 'internal',\n count: deleted,\n });\n recordSecretsOperation({\n resource: 'variable',\n operation: 'delete',\n surface: 'internal',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return deleted;\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'delete',\n surface: 'internal',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n}\n"],"names":["countSecretVariableRows","db","deleteSecretVariableRows","getSecretVariableRowWithPrecedence","listSecretVariableRowsByNamespace","lockWorkspaceEntries","upsertSecretVariableRows","normalizedProjectId","classifySecretsOperationError","operationScope","recordSecretsEntriesMutated","recordSecretsOperation","assertWorkspaceCap","validateNamespace","validateSecretKeys","validateValueBytes","getVariable","input","startedAt","Date","now","scope","namespace","key","row","resource","operation","surface","outcome","durationMs","value","error","getVariablesByNamespace","rows","Object","fromEntries","map","setVariables","entries","values","length","projectId","existingEntries","transaction","tx","workspaceId","keys","incomingEntries","lastEditedBy","editedBy","effect","count","deleteVariables","deleted"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type Tx } from './db.js';
2
+ export declare function countWorkspaceEntries(workspaceId: string, tx?: Tx): Promise<number>;
3
+ export declare function countPublicEntriesByResource(tx?: Tx): Promise<{
4
+ secrets: number;
5
+ variables: number;
6
+ }>;
7
+ export declare function lockWorkspaceEntries(workspaceId: string, tx: Tx): Promise<void>;
8
+ //# sourceMappingURL=cap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cap.d.ts","sourceRoot":"","sources":["../../src/db/cap.ts"],"names":[],"mappings":"AACA,OAAO,EAAK,KAAK,EAAE,EAAC,MAAM,SAAS,CAAC;AAIpC,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBzF;AAED,wBAAsB,4BAA4B,CAChD,EAAE,CAAC,EAAE,EAAE,GACN,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,CAAC,CAiB/C;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrF"}