@selvajs/platform 0.21.0-beta.3 → 1.0.0-beta.5

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 +11 -8
  2. package/dist/access/index.d.ts +0 -1
  3. package/dist/access/index.js +0 -1
  4. package/dist/access/rules.d.ts +7 -14
  5. package/dist/access/rules.js +22 -26
  6. package/dist/apiTokens/index.d.ts +1 -2
  7. package/dist/apiTokens/index.js +1 -2
  8. package/dist/apiTokens/interface.d.ts +0 -1
  9. package/dist/apiTokens/interface.js +0 -1
  10. package/dist/apiTokens/scope.d.ts +0 -1
  11. package/dist/apiTokens/scope.js +0 -1
  12. package/dist/apiTokens/types.d.ts +0 -1
  13. package/dist/apiTokens/types.js +0 -1
  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 +11 -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 +0 -21
  51. package/dist/definitions/schemas.js +6 -19
  52. package/dist/definitions/types.d.ts +3 -4
  53. package/dist/definitions/types.js +0 -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 +21 -3
  93. package/dist/pagination.js +34 -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 +19 -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
@@ -1,19 +1,10 @@
1
1
  // Conformance suite for IAuthProvider: token round-trips, identity, password auth, admin management.
2
2
  import { describe, it, expect } from 'vitest';
3
3
  import { makeUuid } from './helpers.js';
4
- export function runAuthProviderConformance(opts) {
5
- const { name, createProvider, userManagement = false } = opts;
6
- describe(`IAuthProvider conformance: ${name}`, () => {
7
- // Token round-trips
8
- it('verifyLogin returns a session token that verifyToken accepts', async () => {
9
- const { provider, adminPassword, adminEmail = '' } = await createProvider();
10
- const result = await provider.passwordAuth.verifyLogin(adminEmail, adminPassword);
11
- expect(result.kind).toBe('success');
12
- if (result.kind !== 'success')
13
- return;
14
- const verified = await provider.verifyToken(result.sessionToken);
15
- expect(verified?.id).toBe(result.user.id);
16
- });
4
+ /** Cases every auth provider must pass, password or not. */
5
+ export function runAuthCoreConformance(opts) {
6
+ const { name, createProvider } = opts;
7
+ describe(`IAuthProvider core conformance: ${name}`, () => {
17
8
  it('verifyToken returns null for a garbage token', async () => {
18
9
  const { provider } = await createProvider();
19
10
  const result = await provider.verifyToken('not-a-valid-token');
@@ -24,35 +15,57 @@ export function runAuthProviderConformance(opts) {
24
15
  const result = await provider.verifyToken('');
25
16
  expect(result).toBeNull();
26
17
  });
18
+ it('getUser returns null for unknown id', async () => {
19
+ const { provider } = await createProvider();
20
+ // Valid UUID shape but not an actual user — stricter backends (Postgres
21
+ // uuid columns) reject non-UUID ids outright, so we never send those.
22
+ const result = await provider.getUser(makeUuid());
23
+ expect(result).toBeNull();
24
+ });
25
+ });
26
+ }
27
+ /** Cases for a provider with `passwordAuth`. */
28
+ export function runPasswordAuthConformance(opts) {
29
+ const { name, createProvider, userManagement = false, selfRegistration = false } = opts;
30
+ describe(`IAuthProvider password conformance: ${name}`, () => {
31
+ // Token round-trips
32
+ it('verifyLogin returns a session token that verifyToken accepts', async () => {
33
+ const { provider, adminPassword, adminEmail = '' } = await createProvider();
34
+ const result = await provider.passwordAuth.verifyLogin(adminEmail, adminPassword);
35
+ expect(result.kind).toBe('success');
36
+ if (result.kind !== 'success')
37
+ return;
38
+ const verified = await provider.verifyToken(result.sessionToken);
39
+ expect(verified?.id).toBe(result.user.id);
40
+ });
27
41
  // Password auth (only runs when passwordAuth is present)
28
42
  it('verifyLogin returns success with correct password', async () => {
29
43
  const { provider, adminPassword, adminEmail = '' } = await createProvider();
30
- if (!provider.passwordAuth)
31
- return;
32
44
  const result = await provider.passwordAuth.verifyLogin(adminEmail, adminPassword);
33
45
  expect(result.kind).toBe('success');
34
46
  if (result.kind !== 'success')
35
47
  return;
36
- // Identity-only — platform permissions live on IPlatformPermissionStore now.
48
+ // Identity-only: platform permissions live on IPlatformPermissionStore.
37
49
  expect(result.user.id).toBeTruthy();
38
50
  expect(typeof result.sessionToken).toBe('string');
39
51
  expect(result.sessionToken.length).toBeGreaterThan(0);
40
52
  });
53
+ if (selfRegistration) {
54
+ it('registerUser creates an account that can sign in', async () => {
55
+ const { provider } = await createProvider();
56
+ const email = `register-${makeUuid()}@example.com`;
57
+ const user = await provider.passwordAuth.registerUser(email, 'pass12345678');
58
+ expect(user?.email).toBe(email);
59
+ const login = await provider.passwordAuth.verifyLogin(email, 'pass12345678');
60
+ expect(login.kind).toBe('success');
61
+ });
62
+ }
41
63
  it('verifyLogin returns failed for wrong password', async () => {
42
64
  const { provider, adminEmail = '' } = await createProvider();
43
- if (!provider.passwordAuth)
44
- return;
45
65
  const result = await provider.passwordAuth.verifyLogin(adminEmail, 'wrong-password-xyz');
46
66
  expect(result.kind).toBe('failed');
47
67
  });
48
68
  // getUser
49
- it('getUser returns null for unknown id', async () => {
50
- const { provider } = await createProvider();
51
- // Valid UUID shape but not an actual user — stricter backends (Postgres
52
- // uuid columns) reject non-UUID ids outright, so we never send those.
53
- const result = await provider.getUser(makeUuid());
54
- expect(result).toBeNull();
55
- });
56
69
  // ============================================================================
57
70
  // User management (opt-in)
58
71
  // ============================================================================
@@ -102,12 +115,8 @@ export function runAuthProviderConformance(opts) {
102
115
  const result = await provider.deleteUser(makeUuid());
103
116
  expect(result).toBe('not_found');
104
117
  });
105
- // Note: the §2 sole-`instance_admin` invariant is no longer enforced
106
- // by IAuthProvider — it lives on IPlatformPermissionStore now and
107
- // callers consult `countInstanceAdminsExcluding` before destructive
108
- // ops. Auth-side delete/disable just deletes the identity and
109
- // trusts the caller. See `runPlatformPermissionStoreConformance`
110
- // for the invariant tests.
118
+ // Auth-side delete/disable trusts the caller: the sole-`instance_admin`
119
+ // invariant is `runPlatformPermissionStoreConformance`'s to test.
111
120
  it('createUserWithPassword returns an identity record without profile fields', async () => {
112
121
  const { provider } = await createProvider();
113
122
  const user = await provider.passwordAuth.createUserWithPassword(`new-${makeUuid()}@example.com`, 'pass12345678');
@@ -127,6 +136,14 @@ export function runAuthProviderConformance(opts) {
127
136
  const fetched = await provider.getUser(created.id);
128
137
  expect(fetched?.disabled).toBe(true);
129
138
  });
139
+ it('verifyLogin refuses a disabled user', async () => {
140
+ const { provider } = await createProvider();
141
+ const email = `disabled-${makeUuid()}@example.com`;
142
+ const created = await provider.passwordAuth.createUserWithPassword(email, 'pass12345678');
143
+ await provider.disableUser(created.id);
144
+ const result = await provider.passwordAuth.verifyLogin(email, 'pass12345678');
145
+ expect(result).toMatchObject({ kind: 'failed' });
146
+ });
130
147
  it('disableUser returns not_found for unknown user', async () => {
131
148
  const { provider } = await createProvider();
132
149
  const result = await provider.disableUser(makeUuid());
@@ -156,4 +173,12 @@ export function runAuthProviderConformance(opts) {
156
173
  }
157
174
  });
158
175
  }
159
- //# sourceMappingURL=authProviderSuite.js.map
176
+ /**
177
+ * Both of the above, gated on what the provider has. New cases go behind an option
178
+ * so a third-party adapter does not turn red on upgrade.
179
+ */
180
+ export function runAuthProviderConformance(opts) {
181
+ runAuthCoreConformance(opts);
182
+ if (!opts.passwordless)
183
+ runPasswordAuthConformance(opts);
184
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Adapter conformance suite for IComputeServerStore.
3
3
  *
4
- * Architecture spec §4.8 — servers are scoped (platform vs. org-private), with a global
4
+ * Servers are scoped (platform vs. org-private), with a global
5
5
  * `defaultServerId` and per-org `orgDefaults`. The store surface exposes
6
6
  * scope-targeted mutations; visibility filtering is done by callers via
7
7
  * the pure helpers in `@selvajs/platform`.
@@ -18,4 +18,3 @@ export interface ComputeServerStoreConformanceOptions {
18
18
  seedOrg?: (orgId: string) => Promise<void>;
19
19
  }
20
20
  export declare function runComputeServerStoreConformance(opts: ComputeServerStoreConformanceOptions): void;
21
- //# sourceMappingURL=computeServerStoreSuite.d.ts.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Adapter conformance suite for IComputeServerStore.
3
3
  *
4
- * Architecture spec §4.8 — servers are scoped (platform vs. org-private), with a global
4
+ * Servers are scoped (platform vs. org-private), with a global
5
5
  * `defaultServerId` and per-org `orgDefaults`. The store surface exposes
6
6
  * scope-targeted mutations; visibility filtering is done by callers via
7
7
  * the pure helpers in `@selvajs/platform`.
@@ -65,11 +65,76 @@ export function runComputeServerStoreConformance(opts) {
65
65
  it('getConfig on empty returns an empty servers list', async () => {
66
66
  const store = await createStore();
67
67
  const got = await store.getConfig(SYSTEM_CONTEXT);
68
- expect(Array.isArray(got.servers)).toBe(true);
68
+ expect(got.servers).toEqual([]);
69
+ });
70
+ it('getConfig hides keys unless asked, and reports that one is stored', async () => {
71
+ const store = await createStore();
72
+ const id = makeUuid();
73
+ await store.savePlatformServers(SYSTEM_CONTEXT, [
74
+ {
75
+ id,
76
+ scope: 'platform',
77
+ sharedWith: 'all',
78
+ label: 'Keyed',
79
+ serverUrl: 'http://compute.test',
80
+ apiKey: 'rhino-key'
81
+ }
82
+ ], id);
83
+ const plain = await store.getConfig(SYSTEM_CONTEXT);
84
+ expect(plain.servers[0].apiKey).toBeUndefined();
85
+ expect(plain.servers[0].hasApiKey).toBe(true);
86
+ const keyed = await store.getConfig(SYSTEM_CONTEXT, { includeApiKeys: true });
87
+ expect(keyed.servers[0].apiKey).toBe('rhino-key');
88
+ });
89
+ it('getServerApiKey returns one key, and undefined for an unknown server', async () => {
90
+ const store = await createStore();
91
+ const id = makeUuid();
92
+ await store.savePlatformServers(SYSTEM_CONTEXT, [
93
+ {
94
+ id,
95
+ scope: 'platform',
96
+ sharedWith: 'all',
97
+ label: 'Keyed',
98
+ serverUrl: 'http://compute.test',
99
+ apiKey: 'rhino-key'
100
+ }
101
+ ], id);
102
+ expect(await store.getServerApiKey(SYSTEM_CONTEXT, id)).toBe('rhino-key');
103
+ expect(await store.getServerApiKey(SYSTEM_CONTEXT, makeUuid())).toBeUndefined();
104
+ });
105
+ it('verifySecrets passes on keys this store wrote', async () => {
106
+ const store = await createStore();
107
+ const id = makeUuid();
108
+ await store.savePlatformServers(SYSTEM_CONTEXT, [
109
+ {
110
+ id,
111
+ scope: 'platform',
112
+ sharedWith: 'all',
113
+ label: 'Keyed',
114
+ serverUrl: 'http://compute.test',
115
+ apiKey: 'rhino-key'
116
+ }
117
+ ], id);
118
+ const report = await store.verifySecrets?.();
119
+ if (report)
120
+ expect(report).toEqual({ ok: true, failures: [], plaintextFound: false });
69
121
  });
70
122
  // ============================================================================
71
- // Org-private servers + orgDefaults (Architecture spec §4.8)
123
+ // Org-private servers + orgDefaults
72
124
  // ============================================================================
125
+ it('saveOrgServers ignores an entry that is not an org server', async () => {
126
+ const store = await createStore();
127
+ const orgId = await seed();
128
+ const stray = {
129
+ id: makeUuid(),
130
+ scope: 'platform',
131
+ sharedWith: 'all',
132
+ label: 'Stray',
133
+ serverUrl: 'http://compute.test'
134
+ };
135
+ await store.saveOrgServers(SYSTEM_CONTEXT, orgId, [stray]);
136
+ expect((await store.getConfig(SYSTEM_CONTEXT)).servers).toEqual([]);
137
+ });
73
138
  it('saveOrgServers stores rows owned by the org', async () => {
74
139
  const store = await createStore();
75
140
  const orgId = await seed();
@@ -136,4 +201,3 @@ export function runComputeServerStoreConformance(opts) {
136
201
  });
137
202
  });
138
203
  }
139
- //# sourceMappingURL=computeServerStoreSuite.js.map
@@ -26,6 +26,8 @@ export interface DefinitionTestScope {
26
26
  * Must reference a real row for FK-enforcing adapters.
27
27
  */
28
28
  secondaryProjectId: string;
29
+ /** Used by the ctx-isolation cases when `projectId` is one anyone can see. */
30
+ privateProjectId?: string;
29
31
  /** Optional third user id for ctx-isolation tests. Required when ctxIsolation is set. */
30
32
  secondaryUserId?: string;
31
33
  /** Token for `secondaryUserId`, when used. */
@@ -49,4 +51,3 @@ export interface DefinitionStoreConformanceOptions {
49
51
  ctxIsolation?: boolean;
50
52
  }
51
53
  export declare function runDefinitionStoreConformance(opts: DefinitionStoreConformanceOptions): void;
52
- //# sourceMappingURL=definitionStoreSuite.d.ts.map
@@ -6,25 +6,21 @@
6
6
  * reference protection (Permissions spec §6), and error shapes.
7
7
  */
8
8
  import { describe, it, expect } from 'vitest';
9
- import { makeCtx, makeUuid } from './helpers.js';
9
+ import { makeSeedHelpers, makeUuid } from './helpers.js';
10
10
  const DEFAULT_SCOPE = {
11
11
  ownerId: 'user-1',
12
12
  projectId: 'project-1',
13
13
  secondaryProjectId: 'project-2',
14
14
  secondaryUserId: 'user-b'
15
15
  };
16
- // Suite-wide token map. `scopeFor` populates it from the scope; `ctx`
17
- // auto-attaches the matching token so adapters with DB-side auth (Supabase
18
- // RLS) run as the right user. Local providers ignore tokens entirely.
19
- const tokens = new Map();
20
- function ctx(userId, opts = {}) {
21
- return makeCtx(userId, { ...opts, sessionToken: tokens.get(userId) });
22
- }
16
+ // `scopeFor` registers the scope's tokens, so `ctx` runs as the right user on an
17
+ // adapter with DB-side auth (Supabase RLS). This suite seeds nobody itself.
18
+ const { ctx, registerToken } = makeSeedHelpers(async (userId) => ({ userId }));
23
19
  function recordTokensFromScope(scope) {
24
20
  if (scope.ownerSessionToken)
25
- tokens.set(scope.ownerId, scope.ownerSessionToken);
21
+ registerToken(scope.ownerId, scope.ownerSessionToken);
26
22
  if (scope.secondaryUserId && scope.secondaryUserSessionToken) {
27
- tokens.set(scope.secondaryUserId, scope.secondaryUserSessionToken);
23
+ registerToken(scope.secondaryUserId, scope.secondaryUserSessionToken);
28
24
  }
29
25
  }
30
26
  function record(scope, overrides = {}) {
@@ -163,6 +159,15 @@ export function runDefinitionStoreConformance(opts) {
163
159
  expect(got?.displayName).toBe('New');
164
160
  expect(got.updatedAt >= created.updatedAt).toBe(true);
165
161
  });
162
+ it('update with null tags clears them', async () => {
163
+ const store = await createStore();
164
+ const scope = await scopeFor();
165
+ const guid = makeUuid();
166
+ await store.create(ctx(scope.ownerId), record(scope, { guid, tags: ['roof'] }));
167
+ await store.update(ctx(scope.ownerId), guid, { tags: null });
168
+ const got = await store.get(ctx(scope.ownerId), guid);
169
+ expect(got?.tags ?? []).toEqual([]);
170
+ });
166
171
  it('solveCacheLimit round-trips: absent → inherit, set → value, null → cleared', async () => {
167
172
  const store = await createStore();
168
173
  const scope = await scopeFor();
@@ -269,9 +274,8 @@ export function runDefinitionStoreConformance(opts) {
269
274
  expect(got?.nextVersionNumber).toBe(4);
270
275
  });
271
276
  it('reserveNextVersionNumber mints a FRESH number after delete-latest (fileKeys never collide)', async () => {
272
- // The whole reason the counter exists: max(existing)+1 reused a number
273
- // after deleting the latest version, colliding the fileKey. The monotonic
274
- // counter must hand out a number BEYOND any deleted version's.
277
+ // The counter exists because max(existing)+1 would reuse a deleted number
278
+ // and collide the fileKey, so it must go beyond any deleted version's.
275
279
  const store = await createStore();
276
280
  const scope = await scopeFor();
277
281
  const guid = makeUuid();
@@ -574,7 +578,7 @@ export function runDefinitionStoreConformance(opts) {
574
578
  if (!otherUser)
575
579
  throw new Error('ctxIsolation requires scope.secondaryUserId');
576
580
  const guid = makeUuid();
577
- await store.create(ctx(scope.ownerId), record(scope, { guid }));
581
+ await store.create(ctx(scope.ownerId), record(scope, { guid, projectId: scope.privateProjectId ?? scope.projectId }));
578
582
  const page = await store.list(ctx(otherUser));
579
583
  expect(page.items.map((r) => r.guid)).not.toContain(guid);
580
584
  });
@@ -585,11 +589,10 @@ export function runDefinitionStoreConformance(opts) {
585
589
  if (!otherUser)
586
590
  throw new Error('ctxIsolation requires scope.secondaryUserId');
587
591
  const guid = makeUuid();
588
- await store.create(ctx(scope.ownerId), record(scope, { guid }));
592
+ await store.create(ctx(scope.ownerId), record(scope, { guid, projectId: scope.privateProjectId ?? scope.projectId }));
589
593
  const got = await store.get(ctx(otherUser), guid);
590
594
  expect(got).toBeNull();
591
595
  });
592
596
  }
593
597
  });
594
598
  }
595
- //# sourceMappingURL=definitionStoreSuite.js.map
@@ -18,15 +18,12 @@ export interface EmailLinkAuthConformanceOptions {
18
18
  * with self-signup enabled or disabled — the suite's tests are robust to
19
19
  * either, since they don't assume new users get created.
20
20
  *
21
- * `validEmail` is an address the adapter will accept (must pass adapter
22
- * validation; doesn't need to actually receive mail). `invalidEmail` is
23
- * one the adapter must reject as malformed.
21
+ * `invalidEmail` is an address the adapter must reject as malformed. A valid
22
+ * send isn't tested here: it needs real mail delivery.
24
23
  */
25
24
  createAdapter: () => Promise<{
26
25
  adapter: IEmailLinkAuth;
27
- validEmail: string;
28
26
  invalidEmail: string;
29
27
  }>;
30
28
  }
31
29
  export declare function runEmailLinkAuthConformance(opts: EmailLinkAuthConformanceOptions): void;
32
- //# sourceMappingURL=emailLinkAuthSuite.d.ts.map
@@ -36,16 +36,8 @@ export function runEmailLinkAuthConformance(opts) {
36
36
  const result = await adapter.sendMagicLink(invalidEmail, 'https://example.com/auth/email/callback');
37
37
  expect(result.ok).toBe(false);
38
38
  if (!result.ok) {
39
- expect(['invalid_email', 'rate_limited']).toContain(result.reason);
39
+ expect(result.reason).toBe('invalid_email');
40
40
  }
41
41
  });
42
- // `sendMagicLink` for a valid email is intentionally NOT tested here:
43
- // it requires real Supabase project credentials with email delivery
44
- // enabled, and we'd be asserting on side-effects (mail sent) we can't
45
- // observe. Adapter-specific integration tests cover that path.
46
- void '';
47
42
  });
48
- // keep `createAdapter` referenced for adapters that have nothing more to test
49
- void createAdapter;
50
43
  }
51
- //# sourceMappingURL=emailLinkAuthSuite.js.map
@@ -49,4 +49,3 @@ export interface EventSinkConformanceOptions {
49
49
  cleanup?: () => Promise<void> | void;
50
50
  }
51
51
  export declare function runEventSinkConformance(opts: EventSinkConformanceOptions): void;
52
- //# sourceMappingURL=eventSinkSuite.d.ts.map
@@ -151,6 +151,15 @@ export function runEventSinkConformance(opts) {
151
151
  await expect(provider.orgs.createOrg(ctx, makeOrg({ id: orgId, ownerId: actorId }))).rejects.toThrow();
152
152
  expect(sink.ofType('org.created')).toHaveLength(0);
153
153
  });
154
+ it('stamps tokenId on an event from an API-token request', async () => {
155
+ const tokenId = makeUuid();
156
+ const keyed = {
157
+ ...ctx,
158
+ apiScope: { tokenId, scopes: [], expiresAt: '2099-01-01T00:00:00.000Z' }
159
+ };
160
+ await provider.orgs.createOrg(keyed, makeOrg({ id: makeUuid(), ownerId: actorId }));
161
+ expect(sink.ofType('org.created')[0]).toMatchObject({ actorId, tokenId });
162
+ });
154
163
  it('system-context mutations emit with actorId="system"', async () => {
155
164
  const sysCtx = {
156
165
  userId: '',
@@ -261,4 +270,3 @@ function makeShareLink(overrides) {
261
270
  solveCount: 0
262
271
  };
263
272
  }
264
- //# sourceMappingURL=eventSinkSuite.js.map
@@ -14,6 +14,11 @@ export declare function makeCtx(userId: string, opts?: {
14
14
  orgPermissions?: RequestContext['orgPermissions'];
15
15
  sessionToken?: string;
16
16
  }): RequestContext;
17
+ /**
18
+ * A system context holding no permissions of its own, for proving a store leans on
19
+ * `system` rather than on a permission `SYSTEM_CONTEXT` happens to carry.
20
+ */
21
+ export declare function makeSystemCtx(userId?: string): RequestContext;
17
22
  export declare function makeUuid(): string;
18
23
  /**
19
24
  * Optional hook adapters pass when their backend enforces referential
@@ -52,4 +57,3 @@ export declare function makeSeedHelpers(seedUser: SeedUserFn): {
52
57
  registerToken: (userId: string, sessionToken: string) => void;
53
58
  tokens: Map<string, string>;
54
59
  };
55
- //# sourceMappingURL=helpers.d.ts.map
@@ -17,6 +17,13 @@ export function makeCtx(userId, opts = {}) {
17
17
  adapterContext: opts.sessionToken ? { sessionToken: opts.sessionToken } : undefined
18
18
  };
19
19
  }
20
+ /**
21
+ * A system context holding no permissions of its own, for proving a store leans on
22
+ * `system` rather than on a permission `SYSTEM_CONTEXT` happens to carry.
23
+ */
24
+ export function makeSystemCtx(userId = '') {
25
+ return { userId, platformPermissions: [], orgPermissions: [], system: true };
26
+ }
20
27
  export function makeUuid() {
21
28
  return crypto.randomUUID();
22
29
  }
@@ -44,4 +51,3 @@ export function makeSeedHelpers(seedUser) {
44
51
  }
45
52
  return { seed, ctx, registerToken, tokens };
46
53
  }
47
- //# sourceMappingURL=helpers.js.map
@@ -31,4 +31,3 @@ export interface InviteStoreConformanceOptions {
31
31
  seedUser?: SeedUserFn;
32
32
  }
33
33
  export declare function runInviteStoreConformance(opts: InviteStoreConformanceOptions): void;
34
- //# sourceMappingURL=inviteStoreSuite.d.ts.map
@@ -81,23 +81,34 @@ export function runInviteStoreConformance(opts) {
81
81
  await store.markAccepted(SYSTEM_CONTEXT, inv.id, acceptor);
82
82
  const got = await store.getByTokenHash(SYSTEM_CONTEXT, inv.tokenHash);
83
83
  expect(got).toBeNull();
84
+ const page = await store.listByOrg(ctx(scope.adminId), scope.orgId, { limit: 100 });
85
+ const row = page.items.find((i) => i.id === inv.id);
86
+ expect(row?.acceptedAt).toBeTruthy();
87
+ expect(row?.acceptedByUserId).toBe(acceptor);
84
88
  });
85
- it('markAccepted is idempotent (second call is a no-op)', async () => {
89
+ it('markAccepted is idempotent: the first acceptor stays', async () => {
86
90
  const store = await createStore();
87
91
  const scope = await scopeFor();
88
92
  const { userId: acceptor } = await seedUser(makeUuid());
89
93
  const inv = invite(scope);
90
94
  await store.create(ctx(scope.adminId), inv);
91
- await store.markAccepted(SYSTEM_CONTEXT, inv.id, acceptor);
92
- await store.markAccepted(SYSTEM_CONTEXT, inv.id, acceptor);
95
+ const { userId: second } = await seedUser(makeUuid());
96
+ expect(await store.markAccepted(SYSTEM_CONTEXT, inv.id, acceptor)).toBe(true);
97
+ expect(await store.markAccepted(SYSTEM_CONTEXT, inv.id, second)).toBe(false);
98
+ const page = await store.listByOrg(ctx(scope.adminId), scope.orgId, { limit: 100 });
99
+ expect(page.items.find((i) => i.id === inv.id)?.acceptedByUserId).toBe(acceptor);
93
100
  });
94
101
  it('listByOrg returns invites for the org', async () => {
95
102
  const store = await createStore();
96
103
  const scope = await scopeFor();
97
- for (let i = 0; i < 3; i++)
98
- await store.create(ctx(scope.adminId), invite(scope));
104
+ const ids = [];
105
+ for (let i = 0; i < 3; i++) {
106
+ const inv = invite(scope);
107
+ ids.push(inv.id);
108
+ await store.create(ctx(scope.adminId), inv);
109
+ }
99
110
  const page = await store.listByOrg(ctx(scope.adminId), scope.orgId, { limit: 100 });
100
- expect(page.items.length).toBeGreaterThanOrEqual(3);
111
+ expect(page.items.map((i) => i.id)).toEqual(expect.arrayContaining(ids));
101
112
  });
102
113
  it('revoke removes a pending invite', async () => {
103
114
  const store = await createStore();
@@ -157,4 +168,3 @@ export function runInviteStoreConformance(opts) {
157
168
  });
158
169
  });
159
170
  }
160
- //# sourceMappingURL=inviteStoreSuite.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Adapter conformance suite for IOAuthAuth and ISessionRefresh.
3
+ *
4
+ * Covers what can be checked in process: provider listing and the refusal of bad
5
+ * credentials. A real code exchange needs a browser at the IdP.
6
+ */
7
+ import type { IOAuthAuth, ISessionRefresh } from '../../auth/index.js';
8
+ export interface OAuthAuthConformanceOptions {
9
+ name: string;
10
+ createAdapter: () => Promise<{
11
+ oauth: IOAuthAuth;
12
+ sessionRefresh: ISessionRefresh;
13
+ }> | {
14
+ oauth: IOAuthAuth;
15
+ sessionRefresh: ISessionRefresh;
16
+ };
17
+ }
18
+ export declare function runOAuthAuthConformance(opts: OAuthAuthConformanceOptions): void;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Adapter conformance suite for IOAuthAuth and ISessionRefresh.
3
+ *
4
+ * Covers what can be checked in process: provider listing and the refusal of bad
5
+ * credentials. A real code exchange needs a browser at the IdP.
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ export function runOAuthAuthConformance(opts) {
9
+ const { name, createAdapter } = opts;
10
+ describe(`IOAuthAuth conformance: ${name}`, () => {
11
+ it('listProviders names providers in lowercase', async () => {
12
+ const { oauth } = await createAdapter();
13
+ for (const provider of oauth.listProviders()) {
14
+ expect(provider).toBe(provider.toLowerCase());
15
+ }
16
+ });
17
+ it('exchangeOAuthCode returns null for a code nobody issued', async () => {
18
+ const { oauth } = await createAdapter();
19
+ expect(await oauth.exchangeOAuthCode('not-a-real-code')).toBeNull();
20
+ });
21
+ it('refreshSession returns null for a token nobody issued', async () => {
22
+ const { sessionRefresh } = await createAdapter();
23
+ expect(await sessionRefresh.refreshSession('not-a-real-token')).toBeNull();
24
+ });
25
+ });
26
+ }
@@ -19,6 +19,11 @@ export interface OrgStoreConformanceOptions {
19
19
  * actually stored — callers use that, not the suggested one.
20
20
  */
21
21
  seedUser?: SeedUserFn;
22
+ /**
23
+ * Seeds the other party in the ctx-isolation cases. Needed where `seedUser` makes
24
+ * instance admins, who see everything by design.
25
+ */
26
+ seedOutsider?: SeedUserFn;
22
27
  /** If true, skip createOrg/deleteOrg tests (e.g. single-org providers). */
23
28
  singleOrgMode?: boolean;
24
29
  /** If true, run ctx-isolation tests (adapters with row-level security). */
@@ -39,4 +44,3 @@ export interface OrgStoreConformanceOptions {
39
44
  };
40
45
  }
41
46
  export declare function runOrgStoreConformance(opts: OrgStoreConformanceOptions): void;
42
- //# sourceMappingURL=orgStoreSuite.d.ts.map
@@ -37,8 +37,9 @@ function member(orgId, userId, role = 'member') {
37
37
  };
38
38
  }
39
39
  export function runOrgStoreConformance(opts) {
40
- const { name, createStore, seedUser = noopSeedUser, singleOrgMode = false, ctxIsolation = false, createCompanionStores } = opts;
40
+ const { name, createStore, seedUser = noopSeedUser, seedOutsider = seedUser, singleOrgMode = false, ctxIsolation = false, createCompanionStores } = opts;
41
41
  const { seed, ctx } = makeSeedHelpers(seedUser);
42
+ const outsider = makeSeedHelpers(seedOutsider);
42
43
  describe(`IOrgStore conformance: ${name}`, () => {
43
44
  // ============================================================================
44
45
  // Organizations
@@ -116,7 +117,7 @@ export function runOrgStoreConformance(opts) {
116
117
  });
117
118
  }
118
119
  else {
119
- it('getOrg returns the default organization', async () => {
120
+ it('listOrgs returns the default organization', async () => {
120
121
  const store = await createStore();
121
122
  const u1 = await seed();
122
123
  const orgs = await store.listOrgs(ctx(u1));
@@ -190,6 +191,25 @@ export function runOrgStoreConformance(opts) {
190
191
  const got = await store.getOrgMember(ctx(u1), orgId, u2);
191
192
  expect(got?.role).toBe('admin');
192
193
  });
194
+ it('updateOrgMemberPermissions replaces the set and leaves the role', async () => {
195
+ const store = await createStore();
196
+ const { u1, orgId } = await setupOrg(store);
197
+ const u2 = await seed();
198
+ await store.addOrgMember(ctx(u1), member(orgId, u2));
199
+ const before = await store.getOrgMember(ctx(u1), orgId, u2);
200
+ await store.updateOrgMemberPermissions(ctx(u1), orgId, u2, ['manage_projects']);
201
+ const after = await store.getOrgMember(ctx(u1), orgId, u2);
202
+ expect(after?.permissions).toEqual(['manage_projects']);
203
+ expect(after?.role).toBe(before?.role);
204
+ });
205
+ it('removeOrgMember throws 404 for someone who is not a member', async () => {
206
+ const store = await createStore();
207
+ const { u1, orgId } = await setupOrg(store);
208
+ const stranger = await seed();
209
+ await expect(store.removeOrgMember(ctx(u1), orgId, stranger)).rejects.toMatchObject({
210
+ statusCode: 404
211
+ });
212
+ });
193
213
  it('removeOrgMember deletes the member', async () => {
194
214
  const store = await createStore();
195
215
  const { u1, orgId } = await setupOrg(store);
@@ -347,22 +367,21 @@ export function runOrgStoreConformance(opts) {
347
367
  it('ctx isolation: orgs created by one user are not visible to another', async () => {
348
368
  const store = await createStore();
349
369
  const userA = await seed();
350
- const userB = await seed();
370
+ const userB = await outsider.seed();
351
371
  const orgA = makeUuid();
352
372
  await store.createOrg(ctx(userA), org(userA, { id: orgA, slug: `org-a-${orgA.slice(0, 8)}` }));
353
- const page = await store.listOrgs(ctx(userB));
373
+ const page = await store.listOrgs(outsider.ctx(userB));
354
374
  expect(page.items.map((o) => o.id)).not.toContain(orgA);
355
375
  });
356
376
  it('ctx isolation: user cannot get an org they do not own', async () => {
357
377
  const store = await createStore();
358
378
  const userA = await seed();
359
- const userB = await seed();
379
+ const userB = await outsider.seed();
360
380
  const orgA = makeUuid();
361
381
  await store.createOrg(ctx(userA), org(userA, { id: orgA, slug: `org-a-${orgA.slice(0, 8)}` }));
362
- const got = await store.getOrg(ctx(userB), orgA);
382
+ const got = await store.getOrg(outsider.ctx(userB), orgA);
363
383
  expect(got).toBeNull();
364
384
  });
365
385
  }
366
386
  });
367
387
  }
368
- //# sourceMappingURL=orgStoreSuite.js.map
@@ -5,10 +5,6 @@
5
5
  * `countInstanceAdminsExcluding`, plus the §2 sole-`instance_admin`
6
6
  * invariant — `set` must return `'last_admin'` when dropping the only admin,
7
7
  * and `countInstanceAdminsExcluding` must exclude the named user.
8
- *
9
- * These tests used to live on the auth-provider conformance suite when the
10
- * invariant was enforced there; the move from `IAuthProvider` to
11
- * `IPlatformPermissionStore` brought them with it.
12
8
  */
13
9
  import type { IPlatformPermissionStore } from '../../index.js';
14
10
  export interface PlatformPermissionStoreConformanceOptions {
@@ -34,4 +30,3 @@ export interface PlatformPermissionStoreConformanceOptions {
34
30
  cleanup?: () => Promise<void> | void;
35
31
  }
36
32
  export declare function runPlatformPermissionStoreConformance(opts: PlatformPermissionStoreConformanceOptions): void;
37
- //# sourceMappingURL=platformPermissionStoreSuite.d.ts.map