@selvajs/platform 0.21.0-beta.2 → 1.0.0-beta.4

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 (362) hide show
  1. package/README.md +10 -7
  2. package/dist/access/index.d.ts +1 -2
  3. package/dist/access/index.js +1 -2
  4. package/dist/access/rules.d.ts +11 -12
  5. package/dist/access/rules.js +27 -23
  6. package/dist/apiTokens/index.d.ts +2 -3
  7. package/dist/apiTokens/index.js +2 -3
  8. package/dist/apiTokens/interface.d.ts +0 -1
  9. package/dist/apiTokens/interface.js +0 -1
  10. package/dist/apiTokens/scope.d.ts +3 -2
  11. package/dist/apiTokens/scope.js +9 -5
  12. package/dist/apiTokens/types.d.ts +7 -2
  13. package/dist/apiTokens/types.js +10 -5
  14. package/dist/auth/index.d.ts +0 -1
  15. package/dist/auth/index.js +0 -1
  16. package/dist/auth/interface.d.ts +4 -23
  17. package/dist/auth/interface.js +0 -1
  18. package/dist/auth/types.d.ts +4 -2
  19. package/dist/auth/types.js +0 -1
  20. package/dist/bindings/index.d.ts +0 -1
  21. package/dist/bindings/index.js +0 -1
  22. package/dist/bindings/interface.d.ts +0 -1
  23. package/dist/bindings/interface.js +0 -1
  24. package/dist/computeServer/index.d.ts +1 -7
  25. package/dist/computeServer/index.js +2 -8
  26. package/dist/computeServer/interface.d.ts +9 -6
  27. package/dist/computeServer/interface.js +0 -1
  28. package/dist/computeServer/public.d.ts +6 -0
  29. package/dist/computeServer/public.js +2 -0
  30. package/dist/computeServer/secrets.d.ts +12 -6
  31. package/dist/computeServer/secrets.js +35 -15
  32. package/dist/computeServer/types.d.ts +5 -2
  33. package/dist/computeServer/types.js +4 -1
  34. package/dist/computeServer/utils.d.ts +17 -6
  35. package/dist/computeServer/utils.js +31 -21
  36. package/dist/config.d.ts +1 -2
  37. package/dist/config.js +0 -1
  38. package/dist/context.d.ts +12 -1
  39. package/dist/context.js +51 -2
  40. package/dist/data/index.d.ts +0 -1
  41. package/dist/data/index.js +0 -1
  42. package/dist/data/interface.d.ts +0 -1
  43. package/dist/data/interface.js +0 -1
  44. package/dist/definitions/index.d.ts +2 -3
  45. package/dist/definitions/index.js +2 -3
  46. package/dist/definitions/interface.d.ts +0 -1
  47. package/dist/definitions/interface.js +0 -1
  48. package/dist/definitions/paths.d.ts +0 -1
  49. package/dist/definitions/paths.js +1 -12
  50. package/dist/definitions/schemas.d.ts +2 -21
  51. package/dist/definitions/schemas.js +10 -19
  52. package/dist/definitions/types.d.ts +12 -4
  53. package/dist/definitions/types.js +1 -3
  54. package/dist/errors/interface.d.ts +1 -2
  55. package/dist/errors/interface.js +0 -1
  56. package/dist/errors.d.ts +8 -1
  57. package/dist/errors.js +11 -1
  58. package/dist/events/audit.d.ts +1 -2
  59. package/dist/events/audit.js +0 -1
  60. package/dist/events/index.d.ts +0 -1
  61. package/dist/events/index.js +0 -1
  62. package/dist/events/interface.d.ts +0 -1
  63. package/dist/events/interface.js +0 -1
  64. package/dist/index.d.ts +18 -61
  65. package/dist/index.js +18 -24
  66. package/dist/invites/index.d.ts +0 -1
  67. package/dist/invites/index.js +0 -1
  68. package/dist/invites/interface.d.ts +5 -6
  69. package/dist/invites/interface.js +0 -1
  70. package/dist/invites/types.d.ts +0 -1
  71. package/dist/invites/types.js +0 -1
  72. package/dist/logging/interface.d.ts +0 -1
  73. package/dist/logging/interface.js +0 -1
  74. package/dist/metrics/interface.d.ts +0 -1
  75. package/dist/metrics/interface.js +0 -1
  76. package/dist/notifications/index.d.ts +0 -1
  77. package/dist/notifications/index.js +0 -1
  78. package/dist/notifications/interface.d.ts +1 -3
  79. package/dist/notifications/interface.js +1 -3
  80. package/dist/notifications/types.d.ts +0 -1
  81. package/dist/notifications/types.js +0 -1
  82. package/dist/organizations/index.d.ts +2 -3
  83. package/dist/organizations/index.js +1 -2
  84. package/dist/organizations/interface.d.ts +3 -5
  85. package/dist/organizations/interface.js +0 -1
  86. package/dist/organizations/paths.d.ts +1 -6
  87. package/dist/organizations/paths.js +2 -20
  88. package/dist/organizations/schemas.d.ts +5 -7
  89. package/dist/organizations/schemas.js +20 -13
  90. package/dist/organizations/types.d.ts +0 -1
  91. package/dist/organizations/types.js +0 -1
  92. package/dist/pagination.d.ts +11 -3
  93. package/dist/pagination.js +15 -1
  94. package/dist/permissions/index.d.ts +2 -2
  95. package/dist/permissions/index.js +2 -2
  96. package/dist/permissions/interface.d.ts +4 -4
  97. package/dist/permissions/interface.js +0 -1
  98. package/dist/permissions/types.d.ts +0 -1
  99. package/dist/permissions/types.js +0 -1
  100. package/dist/platformProjects/index.d.ts +1 -3
  101. package/dist/platformProjects/index.js +1 -2
  102. package/dist/platformProjects/interface.d.ts +0 -1
  103. package/dist/platformProjects/interface.js +0 -1
  104. package/dist/platformProjects/schemas.d.ts +0 -1
  105. package/dist/platformProjects/schemas.js +0 -1
  106. package/dist/platformProjects/types.d.ts +3 -2
  107. package/dist/platformProjects/types.js +0 -1
  108. package/dist/projects/index.d.ts +0 -1
  109. package/dist/projects/index.js +0 -1
  110. package/dist/projects/interface.d.ts +0 -1
  111. package/dist/projects/interface.js +0 -1
  112. package/dist/projects/schemas.d.ts +0 -1
  113. package/dist/projects/schemas.js +0 -1
  114. package/dist/projects/types.d.ts +0 -1
  115. package/dist/projects/types.js +0 -1
  116. package/dist/shareLinks/index.d.ts +0 -1
  117. package/dist/shareLinks/index.js +0 -1
  118. package/dist/shareLinks/interface.d.ts +6 -3
  119. package/dist/shareLinks/interface.js +0 -1
  120. package/dist/shareLinks/schemas.d.ts +0 -1
  121. package/dist/shareLinks/schemas.js +4 -2
  122. package/dist/shareLinks/types.d.ts +0 -1
  123. package/dist/shareLinks/types.js +0 -1
  124. package/dist/solveCache/interface.d.ts +2 -3
  125. package/dist/solveCache/interface.js +0 -1
  126. package/dist/storage/assetClasses.d.ts +4 -8
  127. package/dist/storage/assetClasses.js +6 -21
  128. package/dist/storage/cacheBust.d.ts +0 -1
  129. package/dist/storage/cacheBust.js +5 -3
  130. package/dist/storage/image.d.ts +8 -5
  131. package/dist/storage/image.js +74 -12
  132. package/dist/storage/index.d.ts +2 -4
  133. package/dist/storage/index.js +2 -3
  134. package/dist/storage/interface.d.ts +11 -3
  135. package/dist/storage/interface.js +0 -1
  136. package/dist/storage/safeKey.d.ts +7 -0
  137. package/dist/storage/safeKey.js +12 -0
  138. package/dist/testing/index.d.ts +5 -2
  139. package/dist/testing/index.js +3 -2
  140. package/dist/testing/suites/apiTokenStoreSuite.d.ts +0 -1
  141. package/dist/testing/suites/apiTokenStoreSuite.js +0 -1
  142. package/dist/testing/suites/auditQuerySuite.d.ts +18 -0
  143. package/dist/testing/suites/auditQuerySuite.js +73 -0
  144. package/dist/testing/suites/authProviderSuite.d.ts +14 -3
  145. package/dist/testing/suites/authProviderSuite.js +57 -32
  146. package/dist/testing/suites/computeServerStoreSuite.d.ts +1 -2
  147. package/dist/testing/suites/computeServerStoreSuite.js +68 -4
  148. package/dist/testing/suites/definitionStoreSuite.d.ts +2 -1
  149. package/dist/testing/suites/definitionStoreSuite.js +48 -16
  150. package/dist/testing/suites/emailLinkAuthSuite.d.ts +2 -5
  151. package/dist/testing/suites/emailLinkAuthSuite.js +1 -9
  152. package/dist/testing/suites/eventSinkSuite.d.ts +0 -1
  153. package/dist/testing/suites/eventSinkSuite.js +9 -1
  154. package/dist/testing/suites/helpers.d.ts +5 -1
  155. package/dist/testing/suites/helpers.js +7 -1
  156. package/dist/testing/suites/inviteStoreSuite.d.ts +0 -1
  157. package/dist/testing/suites/inviteStoreSuite.js +17 -7
  158. package/dist/testing/suites/oauthAuthSuite.d.ts +18 -0
  159. package/dist/testing/suites/oauthAuthSuite.js +26 -0
  160. package/dist/testing/suites/orgStoreSuite.d.ts +5 -1
  161. package/dist/testing/suites/orgStoreSuite.js +26 -7
  162. package/dist/testing/suites/platformPermissionStoreSuite.d.ts +0 -5
  163. package/dist/testing/suites/platformPermissionStoreSuite.js +12 -63
  164. package/dist/testing/suites/platformProjectGrantStoreSuite.d.ts +0 -1
  165. package/dist/testing/suites/platformProjectGrantStoreSuite.js +0 -5
  166. package/dist/testing/suites/projectStoreSuite.d.ts +5 -1
  167. package/dist/testing/suites/projectStoreSuite.js +6 -6
  168. package/dist/testing/suites/shareLinkStoreSuite.d.ts +2 -1
  169. package/dist/testing/suites/shareLinkStoreSuite.js +22 -25
  170. package/dist/testing/suites/solveMetricSinkSuite.d.ts +0 -1
  171. package/dist/testing/suites/solveMetricSinkSuite.js +2 -2
  172. package/dist/testing/suites/storageProviderSuite.d.ts +0 -1
  173. package/dist/testing/suites/storageProviderSuite.js +15 -10
  174. package/dist/testing/suites/userProfileStoreSuite.d.ts +1 -2
  175. package/dist/testing/suites/userProfileStoreSuite.js +42 -7
  176. package/dist/userProfile/index.d.ts +2 -2
  177. package/dist/userProfile/index.js +2 -2
  178. package/dist/userProfile/interface.d.ts +5 -6
  179. package/dist/userProfile/interface.js +0 -1
  180. package/dist/userProfile/types.d.ts +2 -1
  181. package/dist/userProfile/types.js +2 -1
  182. package/dist/utils/audit.d.ts +0 -1
  183. package/dist/utils/audit.js +0 -1
  184. package/dist/utils/index.d.ts +0 -2
  185. package/dist/utils/index.js +0 -1
  186. package/package.json +7 -6
  187. package/dist/access/index.d.ts.map +0 -1
  188. package/dist/access/index.js.map +0 -1
  189. package/dist/access/rules.d.ts.map +0 -1
  190. package/dist/access/rules.js.map +0 -1
  191. package/dist/apiTokens/index.d.ts.map +0 -1
  192. package/dist/apiTokens/index.js.map +0 -1
  193. package/dist/apiTokens/interface.d.ts.map +0 -1
  194. package/dist/apiTokens/interface.js.map +0 -1
  195. package/dist/apiTokens/scope.d.ts.map +0 -1
  196. package/dist/apiTokens/scope.js.map +0 -1
  197. package/dist/apiTokens/types.d.ts.map +0 -1
  198. package/dist/apiTokens/types.js.map +0 -1
  199. package/dist/auth/index.d.ts.map +0 -1
  200. package/dist/auth/index.js.map +0 -1
  201. package/dist/auth/interface.d.ts.map +0 -1
  202. package/dist/auth/interface.js.map +0 -1
  203. package/dist/auth/types.d.ts.map +0 -1
  204. package/dist/auth/types.js.map +0 -1
  205. package/dist/bindings/index.d.ts.map +0 -1
  206. package/dist/bindings/index.js.map +0 -1
  207. package/dist/bindings/interface.d.ts.map +0 -1
  208. package/dist/bindings/interface.js.map +0 -1
  209. package/dist/computeServer/index.d.ts.map +0 -1
  210. package/dist/computeServer/index.js.map +0 -1
  211. package/dist/computeServer/interface.d.ts.map +0 -1
  212. package/dist/computeServer/interface.js.map +0 -1
  213. package/dist/computeServer/secrets.d.ts.map +0 -1
  214. package/dist/computeServer/secrets.js.map +0 -1
  215. package/dist/computeServer/types.d.ts.map +0 -1
  216. package/dist/computeServer/types.js.map +0 -1
  217. package/dist/computeServer/utils.d.ts.map +0 -1
  218. package/dist/computeServer/utils.js.map +0 -1
  219. package/dist/config.d.ts.map +0 -1
  220. package/dist/config.js.map +0 -1
  221. package/dist/context.d.ts.map +0 -1
  222. package/dist/context.js.map +0 -1
  223. package/dist/data/index.d.ts.map +0 -1
  224. package/dist/data/index.js.map +0 -1
  225. package/dist/data/interface.d.ts.map +0 -1
  226. package/dist/data/interface.js.map +0 -1
  227. package/dist/definitions/index.d.ts.map +0 -1
  228. package/dist/definitions/index.js.map +0 -1
  229. package/dist/definitions/interface.d.ts.map +0 -1
  230. package/dist/definitions/interface.js.map +0 -1
  231. package/dist/definitions/paths.d.ts.map +0 -1
  232. package/dist/definitions/paths.js.map +0 -1
  233. package/dist/definitions/schemas.d.ts.map +0 -1
  234. package/dist/definitions/schemas.js.map +0 -1
  235. package/dist/definitions/types.d.ts.map +0 -1
  236. package/dist/definitions/types.js.map +0 -1
  237. package/dist/errors/interface.d.ts.map +0 -1
  238. package/dist/errors/interface.js.map +0 -1
  239. package/dist/errors.d.ts.map +0 -1
  240. package/dist/errors.js.map +0 -1
  241. package/dist/events/audit.d.ts.map +0 -1
  242. package/dist/events/audit.js.map +0 -1
  243. package/dist/events/index.d.ts.map +0 -1
  244. package/dist/events/index.js.map +0 -1
  245. package/dist/events/interface.d.ts.map +0 -1
  246. package/dist/events/interface.js.map +0 -1
  247. package/dist/index.d.ts.map +0 -1
  248. package/dist/index.js.map +0 -1
  249. package/dist/invites/index.d.ts.map +0 -1
  250. package/dist/invites/index.js.map +0 -1
  251. package/dist/invites/interface.d.ts.map +0 -1
  252. package/dist/invites/interface.js.map +0 -1
  253. package/dist/invites/types.d.ts.map +0 -1
  254. package/dist/invites/types.js.map +0 -1
  255. package/dist/logging/interface.d.ts.map +0 -1
  256. package/dist/logging/interface.js.map +0 -1
  257. package/dist/metrics/interface.d.ts.map +0 -1
  258. package/dist/metrics/interface.js.map +0 -1
  259. package/dist/notifications/index.d.ts.map +0 -1
  260. package/dist/notifications/index.js.map +0 -1
  261. package/dist/notifications/interface.d.ts.map +0 -1
  262. package/dist/notifications/interface.js.map +0 -1
  263. package/dist/notifications/types.d.ts.map +0 -1
  264. package/dist/notifications/types.js.map +0 -1
  265. package/dist/organizations/index.d.ts.map +0 -1
  266. package/dist/organizations/index.js.map +0 -1
  267. package/dist/organizations/interface.d.ts.map +0 -1
  268. package/dist/organizations/interface.js.map +0 -1
  269. package/dist/organizations/paths.d.ts.map +0 -1
  270. package/dist/organizations/paths.js.map +0 -1
  271. package/dist/organizations/schemas.d.ts.map +0 -1
  272. package/dist/organizations/schemas.js.map +0 -1
  273. package/dist/organizations/types.d.ts.map +0 -1
  274. package/dist/organizations/types.js.map +0 -1
  275. package/dist/pagination.d.ts.map +0 -1
  276. package/dist/pagination.js.map +0 -1
  277. package/dist/permissions/index.d.ts.map +0 -1
  278. package/dist/permissions/index.js.map +0 -1
  279. package/dist/permissions/interface.d.ts.map +0 -1
  280. package/dist/permissions/interface.js.map +0 -1
  281. package/dist/permissions/types.d.ts.map +0 -1
  282. package/dist/permissions/types.js.map +0 -1
  283. package/dist/platformProjects/index.d.ts.map +0 -1
  284. package/dist/platformProjects/index.js.map +0 -1
  285. package/dist/platformProjects/interface.d.ts.map +0 -1
  286. package/dist/platformProjects/interface.js.map +0 -1
  287. package/dist/platformProjects/schemas.d.ts.map +0 -1
  288. package/dist/platformProjects/schemas.js.map +0 -1
  289. package/dist/platformProjects/types.d.ts.map +0 -1
  290. package/dist/platformProjects/types.js.map +0 -1
  291. package/dist/projects/index.d.ts.map +0 -1
  292. package/dist/projects/index.js.map +0 -1
  293. package/dist/projects/interface.d.ts.map +0 -1
  294. package/dist/projects/interface.js.map +0 -1
  295. package/dist/projects/schemas.d.ts.map +0 -1
  296. package/dist/projects/schemas.js.map +0 -1
  297. package/dist/projects/types.d.ts.map +0 -1
  298. package/dist/projects/types.js.map +0 -1
  299. package/dist/shareLinks/index.d.ts.map +0 -1
  300. package/dist/shareLinks/index.js.map +0 -1
  301. package/dist/shareLinks/interface.d.ts.map +0 -1
  302. package/dist/shareLinks/interface.js.map +0 -1
  303. package/dist/shareLinks/schemas.d.ts.map +0 -1
  304. package/dist/shareLinks/schemas.js.map +0 -1
  305. package/dist/shareLinks/types.d.ts.map +0 -1
  306. package/dist/shareLinks/types.js.map +0 -1
  307. package/dist/solveCache/interface.d.ts.map +0 -1
  308. package/dist/solveCache/interface.js.map +0 -1
  309. package/dist/storage/assetClasses.d.ts.map +0 -1
  310. package/dist/storage/assetClasses.js.map +0 -1
  311. package/dist/storage/cacheBust.d.ts.map +0 -1
  312. package/dist/storage/cacheBust.js.map +0 -1
  313. package/dist/storage/image.d.ts.map +0 -1
  314. package/dist/storage/image.js.map +0 -1
  315. package/dist/storage/index.d.ts.map +0 -1
  316. package/dist/storage/index.js.map +0 -1
  317. package/dist/storage/interface.d.ts.map +0 -1
  318. package/dist/storage/interface.js.map +0 -1
  319. package/dist/testing/index.d.ts.map +0 -1
  320. package/dist/testing/index.js.map +0 -1
  321. package/dist/testing/suites/apiTokenStoreSuite.d.ts.map +0 -1
  322. package/dist/testing/suites/apiTokenStoreSuite.js.map +0 -1
  323. package/dist/testing/suites/authProviderSuite.d.ts.map +0 -1
  324. package/dist/testing/suites/authProviderSuite.js.map +0 -1
  325. package/dist/testing/suites/computeServerStoreSuite.d.ts.map +0 -1
  326. package/dist/testing/suites/computeServerStoreSuite.js.map +0 -1
  327. package/dist/testing/suites/definitionStoreSuite.d.ts.map +0 -1
  328. package/dist/testing/suites/definitionStoreSuite.js.map +0 -1
  329. package/dist/testing/suites/emailLinkAuthSuite.d.ts.map +0 -1
  330. package/dist/testing/suites/emailLinkAuthSuite.js.map +0 -1
  331. package/dist/testing/suites/eventSinkSuite.d.ts.map +0 -1
  332. package/dist/testing/suites/eventSinkSuite.js.map +0 -1
  333. package/dist/testing/suites/helpers.d.ts.map +0 -1
  334. package/dist/testing/suites/helpers.js.map +0 -1
  335. package/dist/testing/suites/inviteStoreSuite.d.ts.map +0 -1
  336. package/dist/testing/suites/inviteStoreSuite.js.map +0 -1
  337. package/dist/testing/suites/orgStoreSuite.d.ts.map +0 -1
  338. package/dist/testing/suites/orgStoreSuite.js.map +0 -1
  339. package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +0 -1
  340. package/dist/testing/suites/platformPermissionStoreSuite.js.map +0 -1
  341. package/dist/testing/suites/platformProjectGrantStoreSuite.d.ts.map +0 -1
  342. package/dist/testing/suites/platformProjectGrantStoreSuite.js.map +0 -1
  343. package/dist/testing/suites/projectStoreSuite.d.ts.map +0 -1
  344. package/dist/testing/suites/projectStoreSuite.js.map +0 -1
  345. package/dist/testing/suites/shareLinkStoreSuite.d.ts.map +0 -1
  346. package/dist/testing/suites/shareLinkStoreSuite.js.map +0 -1
  347. package/dist/testing/suites/solveMetricSinkSuite.d.ts.map +0 -1
  348. package/dist/testing/suites/solveMetricSinkSuite.js.map +0 -1
  349. package/dist/testing/suites/storageProviderSuite.d.ts.map +0 -1
  350. package/dist/testing/suites/storageProviderSuite.js.map +0 -1
  351. package/dist/testing/suites/userProfileStoreSuite.d.ts.map +0 -1
  352. package/dist/testing/suites/userProfileStoreSuite.js.map +0 -1
  353. package/dist/userProfile/index.d.ts.map +0 -1
  354. package/dist/userProfile/index.js.map +0 -1
  355. package/dist/userProfile/interface.d.ts.map +0 -1
  356. package/dist/userProfile/interface.js.map +0 -1
  357. package/dist/userProfile/types.d.ts.map +0 -1
  358. package/dist/userProfile/types.js.map +0 -1
  359. package/dist/utils/audit.d.ts.map +0 -1
  360. package/dist/utils/audit.js.map +0 -1
  361. package/dist/utils/index.d.ts.map +0 -1
  362. package/dist/utils/index.js.map +0 -1
@@ -2,12 +2,19 @@ import { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';
2
2
  const ALGO = 'aes-256-gcm';
3
3
  const IV_BYTES = 12;
4
4
  const TAG_BYTES = 16;
5
- const PREFIX = 'enc:v1:';
5
+ const PREFIX_V1 = 'enc:v1:';
6
+ const PREFIX_V2 = 'enc:v2:';
7
+ export function isEncryptedSecret(value) {
8
+ return value.startsWith(PREFIX_V1) || value.startsWith(PREFIX_V2);
9
+ }
6
10
  /**
7
11
  * AES-256-GCM envelope for at-rest secrets (e.g. compute API keys).
8
12
  *
9
- * Wire format: `enc:v1:<base64(iv|tag|ciphertext)>`. The version prefix lets
10
- * a future migration to a new algorithm tell old and new envelopes apart.
13
+ * Wire format: `enc:v2:<base64(iv|tag|ciphertext)>`. `v2` authenticates the
14
+ * record the secret belongs to as AAD, so ciphertext copied onto another record
15
+ * fails to decrypt instead of handing the plaintext to that record's URL.
16
+ * `enc:v1:` envelopes have no AAD and still read: a store rewrites them as `v2`
17
+ * on its next save.
11
18
  *
12
19
  * Defends against backup leaks, accidental file/DB sharing, and read-only
13
20
  * storage access — not against an attacker who also has the master key
@@ -16,11 +23,11 @@ const PREFIX = 'enc:v1:';
16
23
  *
17
24
  * Pure `node:crypto`, no fs or DB coupling, so the local and Supabase
18
25
  * compute-server stores share this one implementation.
26
+ *
27
+ * Pass `aad` to bind the secret to its record. {@link decryptSecret} then needs the
28
+ * same value.
19
29
  */
20
- export function isEncryptedSecret(value) {
21
- return value.startsWith(PREFIX);
22
- }
23
- export function encryptSecret(plaintext, key) {
30
+ export function encryptSecret(plaintext, key, aad) {
24
31
  if (key.length !== 32) {
25
32
  throw new Error(`Secret key must be 32 bytes; got ${key.length}`);
26
33
  }
@@ -29,23 +36,33 @@ export function encryptSecret(plaintext, key) {
29
36
  }
30
37
  const iv = randomBytes(IV_BYTES);
31
38
  const cipher = createCipheriv(ALGO, key, iv);
39
+ if (aad !== undefined)
40
+ cipher.setAAD(Buffer.from(aad, 'utf8'));
32
41
  const ciphertext = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
33
42
  const tag = cipher.getAuthTag();
34
- return PREFIX + Buffer.concat([iv, tag, ciphertext]).toString('base64');
43
+ const prefix = aad === undefined ? PREFIX_V1 : PREFIX_V2;
44
+ return prefix + Buffer.concat([iv, tag, ciphertext]).toString('base64');
35
45
  }
36
- export function decryptSecret(envelope, key) {
46
+ /** `aad` is ignored for an `enc:v1:` envelope and required for an `enc:v2:` one. */
47
+ export function decryptSecret(envelope, key, aad) {
37
48
  if (key.length !== 32) {
38
49
  throw new Error(`Secret key must be 32 bytes; got ${key.length}`);
39
50
  }
40
51
  if (!isEncryptedSecret(envelope)) {
41
52
  throw new Error('Value is not an encrypted secret envelope');
42
53
  }
43
- const buf = Buffer.from(envelope.slice(PREFIX.length), 'base64');
54
+ const bound = envelope.startsWith(PREFIX_V2);
55
+ if (bound && aad === undefined) {
56
+ throw new Error('This secret is bound to a record: pass its id');
57
+ }
58
+ const buf = Buffer.from(envelope.slice(PREFIX_V2.length), 'base64');
44
59
  const iv = buf.subarray(0, IV_BYTES);
45
60
  const tag = buf.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
46
61
  const ciphertext = buf.subarray(IV_BYTES + TAG_BYTES);
47
62
  const decipher = createDecipheriv(ALGO, key, iv);
48
63
  decipher.setAuthTag(tag);
64
+ if (bound)
65
+ decipher.setAAD(Buffer.from(aad, 'utf8'));
49
66
  return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
50
67
  }
51
68
  /**
@@ -55,12 +72,15 @@ export function decryptSecret(envelope, key) {
55
72
  * ciphertext nothing can decrypt.
56
73
  */
57
74
  export function decodeSecretKey(raw) {
58
- if (/^[0-9a-fA-F]{64}$/.test(raw))
59
- return Buffer.from(raw, 'hex');
60
- const buf = Buffer.from(raw, 'base64');
61
- if (buf.length === 32)
75
+ const key = raw.trim();
76
+ if (/^[0-9a-fA-F]{64}$/.test(key))
77
+ return Buffer.from(key, 'hex');
78
+ // `Buffer.from` skips what it can't decode, so only a round trip proves the
79
+ // whole string was base64 rather than 32 bytes of it.
80
+ const buf = Buffer.from(key, 'base64');
81
+ if (buf.length === 32 && buf.toString('base64').replace(/=+$/, '') === key.replace(/=+$/, '')) {
62
82
  return buf;
83
+ }
63
84
  throw new Error('SELVA_AT_REST_KEY must be 32 bytes encoded as 64-char hex or base64. ' +
64
85
  "Generate one with: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"");
65
86
  }
66
- //# sourceMappingURL=secrets.js.map
@@ -1,3 +1,7 @@
1
+ export declare const COMPUTE_TIMEOUT_DEFAULT_MS = 30000;
2
+ export declare const COMPUTE_TIMEOUT_MIN_MS = 1000;
3
+ export declare const COMPUTE_TIMEOUT_MAX_MS = 300000;
4
+ export declare const COMPUTE_RETRY_MAX = 5;
1
5
  /**
2
6
  * Compute servers belong to one of two scopes — never both.
3
7
  *
@@ -29,7 +33,7 @@ interface ComputeServerCommon {
29
33
  * they'd only throw away.
30
34
  */
31
35
  hasApiKey?: boolean;
32
- /** Default: 30000. */
36
+ /** Default: {@link COMPUTE_TIMEOUT_DEFAULT_MS}. */
33
37
  timeoutMs?: number;
34
38
  /** Default: 0. */
35
39
  retryCount?: number;
@@ -68,4 +72,3 @@ export interface ComputeConfig {
68
72
  export declare function isPlatformServer(s: ComputeServerConfig): s is PlatformComputeServer;
69
73
  export declare function isOrgServer(s: ComputeServerConfig): s is OrgComputeServer;
70
74
  export {};
71
- //# sourceMappingURL=types.d.ts.map
@@ -1,3 +1,7 @@
1
+ export const COMPUTE_TIMEOUT_DEFAULT_MS = 30000;
2
+ export const COMPUTE_TIMEOUT_MIN_MS = 1000;
3
+ export const COMPUTE_TIMEOUT_MAX_MS = 300000;
4
+ export const COMPUTE_RETRY_MAX = 5;
1
5
  // ============================================================================
2
6
  // Type guards
3
7
  // ============================================================================
@@ -7,4 +11,3 @@ export function isPlatformServer(s) {
7
11
  export function isOrgServer(s) {
8
12
  return s.scope === 'org';
9
13
  }
10
- //# sourceMappingURL=types.js.map
@@ -10,18 +10,27 @@ export declare function serversVisibleTo(config: ComputeConfig, orgId: string |
10
10
  * default. Returns `undefined` if neither is set or visible.
11
11
  */
12
12
  export declare function defaultServerIdFor(config: ComputeConfig, orgId: string | null | undefined): string | undefined;
13
+ export interface ResolveOptions {
14
+ /** Per-definition pin. Falls through silently if not visible. */
15
+ definitionPin?: string | null;
16
+ }
17
+ /**
18
+ * From servers already filtered for visibility, the pin if it is among them, then
19
+ * the default, then the first. Only for a caller holding the filtered list: use
20
+ * {@link resolveServerForOrg} when you have the config.
21
+ */
22
+ export declare function pickServer<T extends {
23
+ id: string;
24
+ }>(visible: readonly T[], defaultId: string | null | undefined, pin: string | null | undefined): T | undefined;
13
25
  /**
14
26
  * Resolves a definition's compute server, in order:
15
27
  * 1. Definition pin (`computeServerId`) if visible to the project's org.
16
28
  * 2. `orgDefaults[orgId]` if set.
17
29
  * 3. Global `defaultServerId`.
30
+ * 4. The first visible server, mainly before a default is chosen.
18
31
  *
19
32
  * Throws if no usable server exists.
20
33
  */
21
- export interface ResolveOptions {
22
- /** Per-definition pin. Falls through silently if not visible. */
23
- definitionPin?: string | null;
24
- }
25
34
  export declare function resolveServerForOrg(config: ComputeConfig, orgId: string | null | undefined, opts?: ResolveOptions): ComputeServerConfig;
26
35
  /**
27
36
  * The config as one org may see it: visible servers only, `orgDefaults` reduced
@@ -36,5 +45,7 @@ export declare function scopeConfigToOrg(config: ComputeConfig, orgId: string):
36
45
  /** Lookup by id, ignoring scope or visibility — for admin contexts that display a server outside the visibility filter. */
37
46
  export declare function findServerById(config: ComputeConfig, id: string): ComputeServerConfig | undefined;
38
47
  export declare function platformServers(config: ComputeConfig): import("./types.js").PlatformComputeServer[];
39
- export declare function orgServersFor(config: ComputeConfig, orgId: string): ComputeServerConfig[];
40
- //# sourceMappingURL=utils.d.ts.map
48
+ /** What a compute settings form should send for a typed timeout. */
49
+ export declare function clampTimeoutMs(value: unknown): number;
50
+ /** What a compute settings form should send for a typed retry count. */
51
+ export declare function clampRetryCount(value: unknown): number;
@@ -1,4 +1,4 @@
1
- import { isOrgServer, isPlatformServer } from './types.js';
1
+ import { COMPUTE_RETRY_MAX, COMPUTE_TIMEOUT_DEFAULT_MS, COMPUTE_TIMEOUT_MAX_MS, COMPUTE_TIMEOUT_MIN_MS, isPlatformServer } from './types.js';
2
2
  /**
3
3
  * Servers an org can see in pickers and use for solves:
4
4
  * platform servers shared with this org (or `'all'`, or the global default)
@@ -36,25 +36,30 @@ export function defaultServerIdFor(config, orgId) {
36
36
  }
37
37
  return undefined;
38
38
  }
39
+ /**
40
+ * From servers already filtered for visibility, the pin if it is among them, then
41
+ * the default, then the first. Only for a caller holding the filtered list: use
42
+ * {@link resolveServerForOrg} when you have the config.
43
+ */
44
+ export function pickServer(visible, defaultId, pin) {
45
+ // A pin can go stale when an admin un-shares the server, so it falls through.
46
+ return visible.find((s) => s.id === pin) ?? visible.find((s) => s.id === defaultId) ?? visible[0];
47
+ }
48
+ /**
49
+ * Resolves a definition's compute server, in order:
50
+ * 1. Definition pin (`computeServerId`) if visible to the project's org.
51
+ * 2. `orgDefaults[orgId]` if set.
52
+ * 3. Global `defaultServerId`.
53
+ * 4. The first visible server, mainly before a default is chosen.
54
+ *
55
+ * Throws if no usable server exists.
56
+ */
39
57
  export function resolveServerForOrg(config, orgId, opts = {}) {
40
58
  const visible = serversVisibleTo(config, orgId);
41
- if (visible.length === 0) {
59
+ const server = pickServer(visible, defaultServerIdFor(config, orgId), opts.definitionPin);
60
+ if (!server)
42
61
  throw new Error('No compute servers are configured or visible for this org.');
43
- }
44
- if (opts.definitionPin) {
45
- const pinned = visible.find((s) => s.id === opts.definitionPin);
46
- if (pinned)
47
- return pinned;
48
- // Not visible — admin may have un-shared the server since the pin was set. Fall through.
49
- }
50
- const defaultId = defaultServerIdFor(config, orgId);
51
- if (defaultId) {
52
- const found = visible.find((s) => s.id === defaultId);
53
- if (found)
54
- return found;
55
- }
56
- // Last resort: first visible server. Mainly hit in tests and initial setup before a default is chosen.
57
- return visible[0];
62
+ return server;
58
63
  }
59
64
  /**
60
65
  * The config as one org may see it: visible servers only, `orgDefaults` reduced
@@ -74,7 +79,7 @@ export function scopeConfigToOrg(config, orgId) {
74
79
  defaultServerId: config.defaultServerId && visibleIds.has(config.defaultServerId)
75
80
  ? config.defaultServerId
76
81
  : undefined,
77
- orgDefaults: orgDefault ? { [orgId]: orgDefault } : {}
82
+ orgDefaults: orgDefault && visibleIds.has(orgDefault) ? { [orgId]: orgDefault } : {}
78
83
  };
79
84
  }
80
85
  /** Lookup by id, ignoring scope or visibility — for admin contexts that display a server outside the visibility filter. */
@@ -87,7 +92,12 @@ export function findServerById(config, id) {
87
92
  export function platformServers(config) {
88
93
  return config.servers.filter(isPlatformServer);
89
94
  }
90
- export function orgServersFor(config, orgId) {
91
- return config.servers.filter((s) => isOrgServer(s) && s.ownerOrgId === orgId);
95
+ /** What a compute settings form should send for a typed timeout. */
96
+ export function clampTimeoutMs(value) {
97
+ const ms = Number(value) || COMPUTE_TIMEOUT_DEFAULT_MS;
98
+ return Math.min(COMPUTE_TIMEOUT_MAX_MS, Math.max(COMPUTE_TIMEOUT_MIN_MS, ms));
99
+ }
100
+ /** What a compute settings form should send for a typed retry count. */
101
+ export function clampRetryCount(value) {
102
+ return Math.min(COMPUTE_RETRY_MAX, Math.max(0, Number(value) || 0));
92
103
  }
93
- //# sourceMappingURL=utils.js.map
package/dist/config.d.ts CHANGED
@@ -60,5 +60,4 @@ export interface SelvaConfig {
60
60
  export type SelvaConfigFactory = (env: Record<string, string | undefined>) => SelvaConfig;
61
61
  export declare function isFlagEnabled(config: SelvaConfig, flag: keyof SelvaFlags): boolean;
62
62
  /** Identity function for type inference and IDE autocomplete. */
63
- export declare function defineConfig(config: SelvaConfig | SelvaConfigFactory): SelvaConfig | SelvaConfigFactory;
64
- //# sourceMappingURL=config.d.ts.map
63
+ export declare function defineConfig<T extends SelvaConfig | SelvaConfigFactory>(config: T): T;
package/dist/config.js CHANGED
@@ -5,4 +5,3 @@ export function isFlagEnabled(config, flag) {
5
5
  export function defineConfig(config) {
6
6
  return config;
7
7
  }
8
- //# sourceMappingURL=config.js.map
package/dist/context.d.ts CHANGED
@@ -37,6 +37,7 @@ export interface RequestContext {
37
37
  apiScope?: {
38
38
  tokenId: string;
39
39
  scopes: readonly ApiScope[];
40
+ expiresAt: string;
40
41
  };
41
42
  /** Opaque adapter payload — the Supabase adapter passes the user JWT through for RLS; local ignores it. */
42
43
  adapterContext?: unknown;
@@ -61,10 +62,20 @@ export declare function isShareContext(ctx: RequestContext): boolean;
61
62
  * definition on one channel, never instance authority.
62
63
  */
63
64
  export declare function assertNotShareContext(ctx: RequestContext, what: string): void;
65
+ /** May `ctx` read `userId`'s platform permissions? Throws `ProviderError(403)` if not. */
66
+ export declare function assertCanReadPermissions(ctx: RequestContext, userId: string): void;
67
+ /** May `ctx` read several users' platform permissions? Throws `ProviderError(403)` if not. */
68
+ export declare function assertCanReadPermissionsBatch(ctx: RequestContext): void;
69
+ /** May `ctx` change platform permissions? Throws `ProviderError(403)` if not. */
70
+ export declare function assertCanManagePermissions(ctx: RequestContext): void;
71
+ /**
72
+ * For provider authors: may `ctx` read or change `userId`'s profile? Throws
73
+ * `ProviderError(403)` if not.
74
+ */
75
+ export declare function assertCanAccessProfile(ctx: RequestContext, userId: string): void;
64
76
  /**
65
77
  * Returns the org the caller is acting as, or throws `ProviderError(403)`.
66
78
  * Centralizes this guard so every store that scopes rows to an acting org fails
67
79
  * the same consistent way, instead of leaking `org_id = undefined` into a query.
68
80
  */
69
81
  export declare function requireActingOrg(ctx: RequestContext): string;
70
- //# sourceMappingURL=context.d.ts.map
package/dist/context.js CHANGED
@@ -14,7 +14,7 @@ export const SYSTEM_CONTEXT = {
14
14
  * `actingOrgId` returns false unless `instance_admin` or `system`.
15
15
  */
16
16
  export function hasPermission(ctx, permission) {
17
- if (ctx.system)
17
+ if (ctx.system && !isShareContext(ctx))
18
18
  return true;
19
19
  if (ctx.platformPermissions.includes('instance_admin'))
20
20
  return true;
@@ -41,6 +41,56 @@ export function assertNotShareContext(ctx, what) {
41
41
  throw new ProviderError(`Forbidden: a share link cannot ${what}`, 403);
42
42
  }
43
43
  }
44
+ // For provider authors: `IPlatformPermissionStore` adapters whose backing store is
45
+ // too coarse to enforce this themselves. Reading one row and reading many agree, and
46
+ // read access is not authority: `set` stays `instance_admin`-only.
47
+ /** May `ctx` read `userId`'s platform permissions? Throws `ProviderError(403)` if not. */
48
+ export function assertCanReadPermissions(ctx, userId) {
49
+ assertNotShareContext(ctx, 'read permissions');
50
+ if (ctx.system)
51
+ return;
52
+ if (ctx.userId === userId)
53
+ return;
54
+ if (hasPermission(ctx, 'instance_admin'))
55
+ return;
56
+ if (hasPermission(ctx, 'manage_instance_users'))
57
+ return;
58
+ throw new ProviderError('Forbidden: cannot read another user’s permissions', 403);
59
+ }
60
+ /** May `ctx` read several users' platform permissions? Throws `ProviderError(403)` if not. */
61
+ export function assertCanReadPermissionsBatch(ctx) {
62
+ assertNotShareContext(ctx, 'read permissions');
63
+ if (ctx.system)
64
+ return;
65
+ if (hasPermission(ctx, 'instance_admin'))
66
+ return;
67
+ if (hasPermission(ctx, 'manage_instance_users'))
68
+ return;
69
+ throw new ProviderError('Forbidden: instance admin or manage_instance_users required', 403);
70
+ }
71
+ /** May `ctx` change platform permissions? Throws `ProviderError(403)` if not. */
72
+ export function assertCanManagePermissions(ctx) {
73
+ assertNotShareContext(ctx, 'manage permissions');
74
+ if (ctx.system)
75
+ return;
76
+ if (hasPermission(ctx, 'instance_admin'))
77
+ return;
78
+ throw new ProviderError('Forbidden: instance admin required', 403);
79
+ }
80
+ /**
81
+ * For provider authors: may `ctx` read or change `userId`'s profile? Throws
82
+ * `ProviderError(403)` if not.
83
+ */
84
+ export function assertCanAccessProfile(ctx, userId) {
85
+ assertNotShareContext(ctx, 'access user profiles');
86
+ if (ctx.system)
87
+ return;
88
+ if (ctx.userId === userId)
89
+ return;
90
+ if (hasPermission(ctx, 'instance_admin'))
91
+ return;
92
+ throw new ProviderError('Forbidden: cannot access another user’s profile', 403);
93
+ }
44
94
  const PLATFORM_PERMISSION_VALUES = new Set(ALL_PLATFORM_PERMISSIONS);
45
95
  function isPlatformPermission(p) {
46
96
  return PLATFORM_PERMISSION_VALUES.has(p);
@@ -56,4 +106,3 @@ export function requireActingOrg(ctx) {
56
106
  }
57
107
  return ctx.actingOrgId;
58
108
  }
59
- //# sourceMappingURL=context.js.map
@@ -1,3 +1,2 @@
1
1
  export type { IDataProvider, SchemaVersionReport, UserErasureOptions } from './interface.js';
2
2
  export { ERASED_ACTOR_ID } from './interface.js';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1 @@
1
1
  export { ERASED_ACTOR_ID } from './interface.js';
2
- //# sourceMappingURL=index.js.map
@@ -117,4 +117,3 @@ export interface IDataProvider {
117
117
  */
118
118
  onUserDeleted(ctx: RequestContext, userId: string, opts?: UserErasureOptions): Promise<void>;
119
119
  }
120
- //# sourceMappingURL=interface.d.ts.map
@@ -15,4 +15,3 @@
15
15
  * which marks solves never attributed to a user (share-link / server flows).
16
16
  */
17
17
  export const ERASED_ACTOR_ID = 'deleted';
18
- //# sourceMappingURL=interface.js.map
@@ -2,8 +2,7 @@ export type { DefinitionFileExt, DefinitionListItem, DefinitionRecord, Definitio
2
2
  export { toDefinitionListItem } from './types.js';
3
3
  export type { IDefinitionStore } from './interface.js';
4
4
  export type { UISchema } from '@selvajs/schemas';
5
- export { GH_EXTENSIONS, COVER_IMAGE_EXTENSIONS, ALLOWED_UPLOAD_EXTENSIONS, COVER_IMAGE_CONTENT_TYPES } from './types.js';
5
+ export { GH_EXTENSIONS, COVER_IMAGE_CONTENT_TYPES } from './types.js';
6
6
  export { definitionPaths } from './paths.js';
7
7
  export type { UpdateMetadataInput } from './schemas.js';
8
- export { DefinitionChannelSchema, PublishVersionInputSchema, CreateDefinitionInputSchema, UpdateMetadataInputSchema, GuidSchema, UUID_REGEX } from './schemas.js';
9
- //# sourceMappingURL=index.d.ts.map
8
+ export { PublishVersionInputSchema, CreateDefinitionInputSchema, UpdateMetadataInputSchema, GuidSchema, toSourceDocumentId } from './schemas.js';
@@ -1,5 +1,4 @@
1
1
  export { toDefinitionListItem } from './types.js';
2
- export { GH_EXTENSIONS, COVER_IMAGE_EXTENSIONS, ALLOWED_UPLOAD_EXTENSIONS, COVER_IMAGE_CONTENT_TYPES } from './types.js';
2
+ export { GH_EXTENSIONS, COVER_IMAGE_CONTENT_TYPES } from './types.js';
3
3
  export { definitionPaths } from './paths.js';
4
- export { DefinitionChannelSchema, PublishVersionInputSchema, CreateDefinitionInputSchema, UpdateMetadataInputSchema, GuidSchema, UUID_REGEX } from './schemas.js';
5
- //# sourceMappingURL=index.js.map
4
+ export { PublishVersionInputSchema, CreateDefinitionInputSchema, UpdateMetadataInputSchema, GuidSchema, toSourceDocumentId } from './schemas.js';
@@ -75,4 +75,3 @@ export interface IDefinitionStore {
75
75
  */
76
76
  attachInitialVersion(ctx: RequestContext, definitionId: string, versionId: string): Promise<void>;
77
77
  }
78
- //# sourceMappingURL=interface.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=interface.js.map
@@ -4,4 +4,3 @@ export declare const definitionPaths: {
4
4
  readonly image: (guid: string) => string;
5
5
  readonly prefix: (guid: string) => string;
6
6
  };
7
- //# sourceMappingURL=paths.d.ts.map
@@ -1,14 +1,4 @@
1
- /**
2
- * Path-segment safety check. Blocks traversal, separators, NUL, and empty
3
- * segments. Allowed alphabet `[A-Za-z0-9._-]+` covers GUIDs and sanitized
4
- * refs. Called by every helper so a malicious `guid` can't escape the
5
- * `definitions/` prefix.
6
- */
7
- function assertSafeKey(value, label) {
8
- if (!value || !/^[A-Za-z0-9._-]+$/.test(value) || value === '.' || value === '..') {
9
- throw new Error(`Unsafe ${label}: ${JSON.stringify(value)}`);
10
- }
11
- }
1
+ import { assertSafeKey } from '../storage/safeKey.js';
12
2
  export const definitionPaths = {
13
3
  version: (guid, versionNumber, ext) => {
14
4
  assertSafeKey(guid, 'guid');
@@ -26,4 +16,3 @@ export const definitionPaths = {
26
16
  return `definitions/${guid}/`;
27
17
  }
28
18
  };
29
- //# sourceMappingURL=paths.js.map
@@ -1,31 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  export declare const UUID_REGEX: RegExp;
3
3
  export declare const GuidSchema: z.ZodString;
4
+ /** A Grasshopper document GUID as stores keep and compare it (lowercase), or null for anything else. */
5
+ export declare function toSourceDocumentId(value: unknown): string | null;
4
6
  export declare const DefinitionStatusSchema: z.ZodEnum<{
5
7
  draft: "draft";
6
8
  published: "published";
7
9
  archived: "archived";
8
10
  }>;
9
- export declare const DefinitionFileExtSchema: z.ZodEnum<{
10
- gh: "gh";
11
- ghx: "ghx";
12
- }>;
13
- export declare const DefinitionVersionSchema: z.ZodObject<{
14
- id: z.ZodString;
15
- definitionId: z.ZodString;
16
- versionNumber: z.ZodNumber;
17
- fileExt: z.ZodEnum<{
18
- gh: "gh";
19
- ghx: "ghx";
20
- }>;
21
- fileKey: z.ZodString;
22
- originalFilename: z.ZodOptional<z.ZodString>;
23
- uploadedBy: z.ZodString;
24
- uploadedAt: z.ZodString;
25
- changeNote: z.ZodOptional<z.ZodString>;
26
- schema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
27
- schemaExtractedAt: z.ZodOptional<z.ZodString>;
28
- }, z.core.$strip>;
29
11
  export declare const DefinitionChannelSchema: z.ZodEnum<{
30
12
  draft: "draft";
31
13
  live: "live";
@@ -66,4 +48,3 @@ export type UpdateMetadataInput = z.infer<typeof UpdateMetadataInputSchema>;
66
48
  export declare const PublishVersionInputSchema: z.ZodDefault<z.ZodObject<{
67
49
  versionId: z.ZodOptional<z.ZodString>;
68
50
  }, z.core.$strip>>;
69
- //# sourceMappingURL=schemas.d.ts.map
@@ -4,30 +4,22 @@ export const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9
4
4
  // regex is what keeps `__proto__` and `constructor` out of a key position.
5
5
  // Relaxing it to a looser string re-opens prototype pollution in every store
6
6
  // that does `config.definitions[guid]`.
7
- export const GuidSchema = z.string().regex(UUID_REGEX, 'Invalid GUID format');
7
+ //
8
+ // Lowercases because local keys by raw string while Postgres compares `uuid`
9
+ // columns case-insensitively, so one GUID in two cases is two records on local.
10
+ export const GuidSchema = z.string().toLowerCase().regex(UUID_REGEX, 'Invalid GUID format');
11
+ /** A Grasshopper document GUID as stores keep and compare it (lowercase), or null for anything else. */
12
+ export function toSourceDocumentId(value) {
13
+ return typeof value === 'string' && UUID_REGEX.test(value) ? value.toLowerCase() : null;
14
+ }
8
15
  // Intentionally omits `pending` (see `DefinitionStatus` in ./types.ts): it's a
9
16
  // create-internal state `DefinitionService.create` writes during the
10
17
  // metadata-first window, and a client must never hand-set it through an update.
11
18
  export const DefinitionStatusSchema = z.enum(['draft', 'published', 'archived']);
12
- export const DefinitionFileExtSchema = z.enum(['gh', 'ghx']);
13
- export const DefinitionVersionSchema = z.object({
14
- id: GuidSchema,
15
- definitionId: GuidSchema,
16
- versionNumber: z.number().int().min(1),
17
- fileExt: DefinitionFileExtSchema,
18
- fileKey: z.string().min(1),
19
- originalFilename: z.string().max(256).optional(),
20
- uploadedBy: z.string().min(1),
21
- uploadedAt: z.string(),
22
- changeNote: z.string().max(1000).optional(),
23
- // Rhino.Compute validates structural correctness at upload; this only asserts it's an object.
24
- schema: z.record(z.string(), z.unknown()).optional(),
25
- schemaExtractedAt: z.string().optional()
26
- });
27
19
  export const DefinitionChannelSchema = z.enum(['live', 'draft']);
28
20
  /** HTTP-body validator for new-definition input. */
29
21
  export const CreateDefinitionInputSchema = z.object({
30
- displayName: z.string().min(1, 'Display name is required').max(256),
22
+ displayName: z.string().trim().min(1, 'Display name is required').max(256),
31
23
  description: z.string().max(2000).optional(),
32
24
  coverImage: z.string().max(2048).optional(),
33
25
  category: z.string().max(128).optional(),
@@ -42,7 +34,7 @@ export const CreateDefinitionInputSchema = z.object({
42
34
  * - value — set
43
35
  */
44
36
  export const UpdateMetadataInputSchema = z.object({
45
- displayName: z.string().min(1, 'Display name is required').max(256).optional(),
37
+ displayName: z.string().trim().min(1, 'Display name is required').max(256).optional(),
46
38
  description: z.string().max(2000).nullable().optional(),
47
39
  coverImage: z.string().max(2048).nullable().optional(),
48
40
  category: z.string().max(128).nullable().optional(),
@@ -60,4 +52,3 @@ export const PublishVersionInputSchema = z
60
52
  versionId: GuidSchema.optional()
61
53
  })
62
54
  .default({});
63
- //# sourceMappingURL=schemas.js.map
@@ -1,8 +1,8 @@
1
1
  import type { UISchema } from '@selvajs/schemas';
2
+ import type { z } from 'zod';
3
+ import type { DefinitionChannelSchema } from './schemas.js';
2
4
  export type DefinitionFileExt = 'gh' | 'ghx';
3
5
  export declare const GH_EXTENSIONS: string[];
4
- export declare const COVER_IMAGE_EXTENSIONS: string[];
5
- export declare const ALLOWED_UPLOAD_EXTENSIONS: string[];
6
6
  export declare const COVER_IMAGE_CONTENT_TYPES: Record<string, string>;
7
7
  /**
8
8
  * - `pending` — internal: metadata written, blob upload may be in flight.
@@ -81,6 +81,12 @@ export interface DefinitionRecord {
81
81
  */
82
82
  liveVersionId: string | null;
83
83
  draftVersionId: string | null;
84
+ /**
85
+ * The Grasshopper document GUID (lowercase) from the schema Compute
86
+ * extracted at the last upload that carried one. A copied `.gh` keeps its
87
+ * GUID, so this suggests a match and never proves one.
88
+ */
89
+ sourceDocumentId?: string;
84
90
  createdAt: string;
85
91
  updatedAt: string;
86
92
  /** Soft-delete marker, independent of `status`: a store filters this out regardless of `archived`. */
@@ -107,6 +113,8 @@ export interface DefinitionListItem {
107
113
  status: DefinitionStatus;
108
114
  solveCount: number;
109
115
  liveVersionId: string | null;
116
+ /** See {@link DefinitionRecord.sourceDocumentId}. */
117
+ sourceDocumentId: string | null;
110
118
  updatedAt: string;
111
119
  }
112
120
  export declare function toDefinitionListItem(r: DefinitionRecord): DefinitionListItem;
@@ -132,6 +140,6 @@ export interface DefinitionRecordPatch {
132
140
  solveCacheLimit?: number | null;
133
141
  status?: DefinitionStatus;
134
142
  ownerId?: string;
143
+ sourceDocumentId?: string | null;
135
144
  }
136
- export type DefinitionChannel = 'live' | 'draft';
137
- //# sourceMappingURL=types.d.ts.map
145
+ export type DefinitionChannel = z.infer<typeof DefinitionChannelSchema>;
@@ -1,6 +1,4 @@
1
1
  export const GH_EXTENSIONS = ['.gh', '.ghx'];
2
- export const COVER_IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp', '.gif'];
3
- export const ALLOWED_UPLOAD_EXTENSIONS = [...GH_EXTENSIONS, ...COVER_IMAGE_EXTENSIONS];
4
2
  export const COVER_IMAGE_CONTENT_TYPES = {
5
3
  '.jpg': 'image/jpeg',
6
4
  '.jpeg': 'image/jpeg',
@@ -20,7 +18,7 @@ export function toDefinitionListItem(r) {
20
18
  status: r.status,
21
19
  solveCount: r.solveCount,
22
20
  liveVersionId: r.liveVersionId,
21
+ sourceDocumentId: r.sourceDocumentId ?? null,
23
22
  updatedAt: r.updatedAt
24
23
  };
25
24
  }
26
- //# sourceMappingURL=types.js.map
@@ -2,7 +2,7 @@
2
2
  * Structured context attached to a reported error. All fields optional — a
3
3
  * report can fire before any request context is built (e.g. an
4
4
  * `unhandledRejection` with no active request). Deliberately NOT a
5
- * `RequestContext`: reporters must never receive the opaque `authToken`, so
5
+ * `RequestContext`: reporters must never receive the opaque `adapterContext`, so
6
6
  * only non-sensitive routing/identity fields are surfaced here.
7
7
  */
8
8
  export interface ErrorContext {
@@ -37,4 +37,3 @@ export interface IErrorReporter {
37
37
  export declare class NoopErrorReporter implements IErrorReporter {
38
38
  capture(_error: unknown, _context?: ErrorContext): void;
39
39
  }
40
- //# sourceMappingURL=interface.d.ts.map
@@ -6,4 +6,3 @@
6
6
  export class NoopErrorReporter {
7
7
  capture(_error, _context) { }
8
8
  }
9
- //# sourceMappingURL=interface.js.map
package/dist/errors.d.ts CHANGED
@@ -2,8 +2,15 @@
2
2
  * Thrown by providers to signal an expected failure the API layer forwards with
3
3
  * a specific status. Unknown errors should propagate as plain `Error` and become 500s.
4
4
  */
5
+ declare const BRAND: unique symbol;
5
6
  export declare class ProviderError extends Error {
6
7
  readonly statusCode: number;
8
+ readonly [BRAND] = true;
9
+ /**
10
+ * Providers depend on this package, so a version mismatch installs two copies
11
+ * and plain `instanceof` would turn a 403 from one into a 500 in the other.
12
+ */
13
+ static [Symbol.hasInstance](value: unknown): boolean;
7
14
  constructor(message: string, statusCode?: number);
8
15
  }
9
- //# sourceMappingURL=errors.d.ts.map
16
+ export {};