@selvajs/server 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/dist/access/guards.d.ts +213 -0
  2. package/dist/access/guards.d.ts.map +1 -0
  3. package/dist/access/guards.js +417 -0
  4. package/dist/access/guards.js.map +1 -0
  5. package/dist/access/index.d.ts +2 -0
  6. package/dist/access/index.d.ts.map +1 -1
  7. package/dist/access/index.js +6 -0
  8. package/dist/access/index.js.map +1 -1
  9. package/dist/access/permissions-scope.d.ts +15 -0
  10. package/dist/access/permissions-scope.d.ts.map +1 -0
  11. package/dist/access/permissions-scope.js +23 -0
  12. package/dist/access/permissions-scope.js.map +1 -0
  13. package/dist/api/bodies.d.ts +104 -0
  14. package/dist/api/bodies.d.ts.map +1 -0
  15. package/dist/api/bodies.js +87 -0
  16. package/dist/api/bodies.js.map +1 -0
  17. package/dist/api/deps.d.ts +135 -0
  18. package/dist/api/deps.d.ts.map +1 -0
  19. package/dist/api/deps.js +55 -0
  20. package/dist/api/deps.js.map +1 -0
  21. package/dist/api/errors.d.ts +35 -0
  22. package/dist/api/errors.d.ts.map +1 -0
  23. package/dist/api/errors.js +63 -0
  24. package/dist/api/errors.js.map +1 -0
  25. package/dist/api/index.d.ts +12 -0
  26. package/dist/api/index.d.ts.map +1 -0
  27. package/dist/api/index.js +15 -0
  28. package/dist/api/index.js.map +1 -0
  29. package/dist/api/map-core-error.d.ts +19 -0
  30. package/dist/api/map-core-error.d.ts.map +1 -0
  31. package/dist/api/map-core-error.js +49 -0
  32. package/dist/api/map-core-error.js.map +1 -0
  33. package/dist/api/pagination.d.ts +19 -0
  34. package/dist/api/pagination.d.ts.map +1 -0
  35. package/dist/api/pagination.js +40 -0
  36. package/dist/api/pagination.js.map +1 -0
  37. package/dist/api/request.d.ts +93 -0
  38. package/dist/api/request.d.ts.map +1 -0
  39. package/dist/api/request.js +150 -0
  40. package/dist/api/request.js.map +1 -0
  41. package/dist/api/respond.d.ts +29 -0
  42. package/dist/api/respond.d.ts.map +1 -0
  43. package/dist/api/respond.js +59 -0
  44. package/dist/api/respond.js.map +1 -0
  45. package/dist/api/responses-schema.d.ts +158 -0
  46. package/dist/api/responses-schema.d.ts.map +1 -0
  47. package/dist/api/responses-schema.js +104 -0
  48. package/dist/api/responses-schema.js.map +1 -0
  49. package/dist/api/responses.d.ts +19 -0
  50. package/dist/api/responses.d.ts.map +1 -0
  51. package/dist/api/responses.js +21 -0
  52. package/dist/api/responses.js.map +1 -0
  53. package/dist/api/shaped.d.ts +23 -0
  54. package/dist/api/shaped.d.ts.map +1 -0
  55. package/dist/api/shaped.js +29 -0
  56. package/dist/api/shaped.js.map +1 -0
  57. package/dist/api/types.d.ts +41 -0
  58. package/dist/api/types.d.ts.map +1 -0
  59. package/dist/api/types.js +12 -0
  60. package/dist/api/types.js.map +1 -0
  61. package/dist/compute/errors.d.ts +18 -0
  62. package/dist/compute/errors.d.ts.map +1 -0
  63. package/dist/compute/errors.js +21 -0
  64. package/dist/compute/errors.js.map +1 -0
  65. package/dist/compute/evict-changed-servers.d.ts +22 -0
  66. package/dist/compute/evict-changed-servers.d.ts.map +1 -0
  67. package/dist/compute/evict-changed-servers.js +31 -0
  68. package/dist/compute/evict-changed-servers.js.map +1 -0
  69. package/dist/compute/idempotency-http.d.ts +47 -0
  70. package/dist/compute/idempotency-http.d.ts.map +1 -0
  71. package/dist/compute/idempotency-http.js +51 -0
  72. package/dist/compute/idempotency-http.js.map +1 -0
  73. package/dist/compute/index.d.ts +5 -0
  74. package/dist/compute/index.d.ts.map +1 -1
  75. package/dist/compute/index.js +5 -0
  76. package/dist/compute/index.js.map +1 -1
  77. package/dist/compute/resolve-server.d.ts +25 -0
  78. package/dist/compute/resolve-server.d.ts.map +1 -0
  79. package/dist/compute/resolve-server.js +35 -0
  80. package/dist/compute/resolve-server.js.map +1 -0
  81. package/dist/compute/server-config-write.d.ts +52 -0
  82. package/dist/compute/server-config-write.d.ts.map +1 -0
  83. package/dist/compute/server-config-write.js +100 -0
  84. package/dist/compute/server-config-write.js.map +1 -0
  85. package/dist/definitions/index.d.ts +1 -0
  86. package/dist/definitions/index.d.ts.map +1 -1
  87. package/dist/definitions/index.js +1 -0
  88. package/dist/definitions/index.js.map +1 -1
  89. package/dist/definitions/visibility.d.ts +74 -0
  90. package/dist/definitions/visibility.d.ts.map +1 -0
  91. package/dist/definitions/visibility.js +117 -0
  92. package/dist/definitions/visibility.js.map +1 -0
  93. package/dist/handlers/definitionVersions.d.ts +30 -0
  94. package/dist/handlers/definitionVersions.d.ts.map +1 -0
  95. package/dist/handlers/definitionVersions.js +93 -0
  96. package/dist/handlers/definitionVersions.js.map +1 -0
  97. package/dist/handlers/definitions.d.ts +35 -0
  98. package/dist/handlers/definitions.d.ts.map +1 -0
  99. package/dist/handlers/definitions.js +207 -0
  100. package/dist/handlers/definitions.js.map +1 -0
  101. package/dist/handlers/index.d.ts +27 -0
  102. package/dist/handlers/index.d.ts.map +1 -0
  103. package/dist/handlers/index.js +29 -0
  104. package/dist/handlers/index.js.map +1 -0
  105. package/dist/handlers/invite-lookup.d.ts +11 -0
  106. package/dist/handlers/invite-lookup.d.ts.map +1 -0
  107. package/dist/handlers/invite-lookup.js +20 -0
  108. package/dist/handlers/invite-lookup.js.map +1 -0
  109. package/dist/handlers/invites.d.ts +31 -0
  110. package/dist/handlers/invites.d.ts.map +1 -0
  111. package/dist/handlers/invites.js +148 -0
  112. package/dist/handlers/invites.js.map +1 -0
  113. package/dist/handlers/me.d.ts +8 -0
  114. package/dist/handlers/me.d.ts.map +1 -0
  115. package/dist/handlers/me.js +21 -0
  116. package/dist/handlers/me.js.map +1 -0
  117. package/dist/handlers/me.starred.d.ts +11 -0
  118. package/dist/handlers/me.starred.d.ts.map +1 -0
  119. package/dist/handlers/me.starred.js +21 -0
  120. package/dist/handlers/me.starred.js.map +1 -0
  121. package/dist/handlers/orgAssets.d.ts +13 -0
  122. package/dist/handlers/orgAssets.d.ts.map +1 -0
  123. package/dist/handlers/orgAssets.js +54 -0
  124. package/dist/handlers/orgAssets.js.map +1 -0
  125. package/dist/handlers/orgCompute.d.ts +16 -0
  126. package/dist/handlers/orgCompute.d.ts.map +1 -0
  127. package/dist/handlers/orgCompute.js +96 -0
  128. package/dist/handlers/orgCompute.js.map +1 -0
  129. package/dist/handlers/orgMembers.d.ts +29 -0
  130. package/dist/handlers/orgMembers.d.ts.map +1 -0
  131. package/dist/handlers/orgMembers.js +200 -0
  132. package/dist/handlers/orgMembers.js.map +1 -0
  133. package/dist/handlers/orgs.d.ts +16 -0
  134. package/dist/handlers/orgs.d.ts.map +1 -0
  135. package/dist/handlers/orgs.js +26 -0
  136. package/dist/handlers/orgs.js.map +1 -0
  137. package/dist/handlers/projectMembers.d.ts +15 -0
  138. package/dist/handlers/projectMembers.d.ts.map +1 -0
  139. package/dist/handlers/projectMembers.js +95 -0
  140. package/dist/handlers/projectMembers.js.map +1 -0
  141. package/dist/handlers/projects.d.ts +21 -0
  142. package/dist/handlers/projects.d.ts.map +1 -0
  143. package/dist/handlers/projects.js +164 -0
  144. package/dist/handlers/projects.js.map +1 -0
  145. package/dist/handlers/reclaim.d.ts +10 -0
  146. package/dist/handlers/reclaim.d.ts.map +1 -0
  147. package/dist/handlers/reclaim.js +38 -0
  148. package/dist/handlers/reclaim.js.map +1 -0
  149. package/dist/handlers/services.d.ts +22 -0
  150. package/dist/handlers/services.d.ts.map +1 -0
  151. package/dist/handlers/services.js +37 -0
  152. package/dist/handlers/services.js.map +1 -0
  153. package/dist/handlers/shareLinks.d.ts +23 -0
  154. package/dist/handlers/shareLinks.d.ts.map +1 -0
  155. package/dist/handlers/shareLinks.js +85 -0
  156. package/dist/handlers/shareLinks.js.map +1 -0
  157. package/dist/http/index.d.ts +1 -0
  158. package/dist/http/index.d.ts.map +1 -1
  159. package/dist/http/index.js +1 -0
  160. package/dist/http/index.js.map +1 -1
  161. package/dist/http/login-rate-limit.d.ts +72 -0
  162. package/dist/http/login-rate-limit.d.ts.map +1 -0
  163. package/dist/http/login-rate-limit.js +84 -0
  164. package/dist/http/login-rate-limit.js.map +1 -0
  165. package/dist/notifications/deliver-invite.d.ts +27 -0
  166. package/dist/notifications/deliver-invite.d.ts.map +1 -0
  167. package/dist/notifications/deliver-invite.js +32 -0
  168. package/dist/notifications/deliver-invite.js.map +1 -0
  169. package/dist/notifications/index.d.ts +4 -0
  170. package/dist/notifications/index.d.ts.map +1 -0
  171. package/dist/notifications/index.js +4 -0
  172. package/dist/notifications/index.js.map +1 -0
  173. package/dist/notifications/smtp-config.d.ts +37 -0
  174. package/dist/notifications/smtp-config.d.ts.map +1 -0
  175. package/dist/notifications/smtp-config.js +41 -0
  176. package/dist/notifications/smtp-config.js.map +1 -0
  177. package/dist/notifications/smtp-provider.d.ts +18 -0
  178. package/dist/notifications/smtp-provider.d.ts.map +1 -0
  179. package/dist/notifications/smtp-provider.js +71 -0
  180. package/dist/notifications/smtp-provider.js.map +1 -0
  181. package/dist/ops/deploymentConfig.d.ts +73 -0
  182. package/dist/ops/deploymentConfig.d.ts.map +1 -0
  183. package/dist/ops/deploymentConfig.js +154 -0
  184. package/dist/ops/deploymentConfig.js.map +1 -0
  185. package/dist/ops/index.d.ts +1 -0
  186. package/dist/ops/index.d.ts.map +1 -1
  187. package/dist/ops/index.js +3 -2
  188. package/dist/ops/index.js.map +1 -1
  189. package/dist/organizations/index.d.ts +3 -0
  190. package/dist/organizations/index.d.ts.map +1 -0
  191. package/dist/organizations/index.js +5 -0
  192. package/dist/organizations/index.js.map +1 -0
  193. package/dist/organizations/list-all-members.d.ts +10 -0
  194. package/dist/organizations/list-all-members.d.ts.map +1 -0
  195. package/dist/organizations/list-all-members.js +36 -0
  196. package/dist/organizations/list-all-members.js.map +1 -0
  197. package/dist/organizations/org-asset-service.d.ts +39 -0
  198. package/dist/organizations/org-asset-service.d.ts.map +1 -0
  199. package/dist/organizations/org-asset-service.js +71 -0
  200. package/dist/organizations/org-asset-service.js.map +1 -0
  201. package/dist/projects/create-project.d.ts +30 -0
  202. package/dist/projects/create-project.d.ts.map +1 -0
  203. package/dist/projects/create-project.js +56 -0
  204. package/dist/projects/create-project.js.map +1 -0
  205. package/dist/projects/index.d.ts +2 -0
  206. package/dist/projects/index.d.ts.map +1 -0
  207. package/dist/projects/index.js +3 -0
  208. package/dist/projects/index.js.map +1 -0
  209. package/dist/providers/auth-lookup.d.ts +14 -0
  210. package/dist/providers/auth-lookup.d.ts.map +1 -0
  211. package/dist/providers/auth-lookup.js +34 -0
  212. package/dist/providers/auth-lookup.js.map +1 -0
  213. package/dist/providers/index.d.ts +1 -0
  214. package/dist/providers/index.d.ts.map +1 -1
  215. package/dist/providers/index.js +1 -0
  216. package/dist/providers/index.js.map +1 -1
  217. package/dist/testing/harness.d.ts +168 -0
  218. package/dist/testing/harness.d.ts.map +1 -0
  219. package/dist/testing/harness.js +314 -0
  220. package/dist/testing/harness.js.map +1 -0
  221. package/dist/testing/index.d.ts +18 -0
  222. package/dist/testing/index.d.ts.map +1 -0
  223. package/dist/testing/index.js +15 -0
  224. package/dist/testing/index.js.map +1 -0
  225. package/dist/testing/scenarios.d.ts +67 -0
  226. package/dist/testing/scenarios.d.ts.map +1 -0
  227. package/dist/testing/scenarios.js +104 -0
  228. package/dist/testing/scenarios.js.map +1 -0
  229. package/dist/testing/seed-adapter.d.ts +31 -0
  230. package/dist/testing/seed-adapter.d.ts.map +1 -0
  231. package/dist/testing/seed-adapter.js +19 -0
  232. package/dist/testing/seed-adapter.js.map +1 -0
  233. package/package.json +42 -3
@@ -0,0 +1,2 @@
1
+ export { createProjectWithUniqueSlug, type ProjectDraft, type CreateProjectOptions } from './create-project.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/projects/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,2BAA2B,EAC3B,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Project writes whose rules two routes must agree on.
2
+ export { createProjectWithUniqueSlug } from './create-project.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/projects/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EACN,2BAA2B,EAG3B,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { AuthUser, IAuthProvider } from '@selvajs/platform';
2
+ /**
3
+ * `IAuthProvider` has no by-email lookup and `listUsers` takes no filter, so
4
+ * this scans. Acceptable because the only caller is invite acceptance — once
5
+ * per invite, never on a hot path.
6
+ *
7
+ * A truncated scan would report an existing user as absent and send the flow
8
+ * down the create-account branch, where the provider rejects the duplicate. So
9
+ * exhausting the cursor matters: `null` must mean "not found", not "gave up".
10
+ * Returns `null` when the provider has no user store (`listUsers` → null) or
11
+ * when the cap is hit.
12
+ */
13
+ export declare function findAuthUserByEmail(auth: IAuthProvider, email: string): Promise<AuthUser | null>;
14
+ //# sourceMappingURL=auth-lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-lookup.d.ts","sourceRoot":"","sources":["../../src/providers/auth-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOjE;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAc1B"}
@@ -0,0 +1,34 @@
1
+ const PAGE_LIMIT = 200;
2
+ // Runaway guard against an adapter returning a non-advancing cursor.
3
+ // 100 pages = 20k users, far past any deployment this product targets.
4
+ const MAX_PAGES = 100;
5
+ /**
6
+ * `IAuthProvider` has no by-email lookup and `listUsers` takes no filter, so
7
+ * this scans. Acceptable because the only caller is invite acceptance — once
8
+ * per invite, never on a hot path.
9
+ *
10
+ * A truncated scan would report an existing user as absent and send the flow
11
+ * down the create-account branch, where the provider rejects the duplicate. So
12
+ * exhausting the cursor matters: `null` must mean "not found", not "gave up".
13
+ * Returns `null` when the provider has no user store (`listUsers` → null) or
14
+ * when the cap is hit.
15
+ */
16
+ export async function findAuthUserByEmail(auth, email) {
17
+ const needle = email.trim().toLowerCase();
18
+ if (!needle)
19
+ return null;
20
+ let cursor;
21
+ for (let page = 0; page < MAX_PAGES; page++) {
22
+ const result = await auth.listUsers({ limit: PAGE_LIMIT, cursor });
23
+ if (!result)
24
+ return null;
25
+ const hit = result.items.find((u) => u.email?.trim().toLowerCase() === needle);
26
+ if (hit)
27
+ return hit;
28
+ cursor = result.nextCursor;
29
+ if (!cursor)
30
+ return null;
31
+ }
32
+ return null;
33
+ }
34
+ //# sourceMappingURL=auth-lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-lookup.js","sourceRoot":"","sources":["../../src/providers/auth-lookup.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,qEAAqE;AACrE,uEAAuE;AACvE,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,IAAmB,EACnB,KAAa;IAEb,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,IAAI,MAA0B,CAAC;IAC/B,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;QAC/E,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { createSelvaProviders, type CreateSelvaProvidersOptions, type ProviderFactory, type ProviderRegistry, type SelvaProviderRuntime } from './create-selva-providers.js';
2
+ export { findAuthUserByEmail } from './auth-lookup.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  // Configurable provider wiring — env-driven selection over a caller-supplied
2
2
  // registry, with a `selva.config.js` override hook.
3
3
  export { createSelvaProviders } from './create-selva-providers.js';
4
+ export { findAuthUserByEmail } from './auth-lookup.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,oDAAoD;AAEpD,OAAO,EACN,oBAAoB,EAKpB,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,oDAAoD;AAEpD,OAAO,EACN,oBAAoB,EAKpB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * A host-agnostic harness for testing API handlers.
3
+ *
4
+ * Handlers are transport-free, but the tests around them were not: they built
5
+ * a SvelteKit `RequestEvent` and imported the Selva app's fixtures, so a second
6
+ * host could run the handlers and inherit none of the tests proving they work.
7
+ * That is the duplication this removes — a host wires a `TestHarness` once and
8
+ * gets the shared suites.
9
+ *
10
+ * Two things stay injected because no interface supplies them:
11
+ * - {@link SeedAuthAdapter}, since identity has no portable creation method.
12
+ * - `mapError`, since folding a host's own domain errors into the envelope is
13
+ * by definition host-specific. Omitting it is not neutral: a host whose
14
+ * guards throw something `runHandler` does not recognize gets a 500 where
15
+ * production returns 403, and every status assertion silently weakens.
16
+ */
17
+ import { emptyProfile, type AuthUser, type DefinitionRecord, type DefinitionVersion, type ILogger, type OrgMember, type OrgPermission, type OrgRole, type Organization, type PlatformPermission, type Project, type ProjectMember, type ProjectRole, type ProjectVisibility, type RequestContext, type SelvaConfig, type ShareLink } from '@selvajs/platform';
18
+ import type { ApiError, ApiHandler, SelvaDeps } from '../api/index.js';
19
+ import type { SeedAuthAdapter } from './seed-adapter.js';
20
+ export interface TestHarness {
21
+ config: SelvaConfig;
22
+ auth: SeedAuthAdapter;
23
+ /** Folds host domain errors into the envelope, exactly as the host's adapter does. */
24
+ mapError?: (err: unknown) => ApiError | undefined;
25
+ /** Overrides merged onto the deps `callHandler` builds — token codecs, services, upload caps. */
26
+ deps?: Partial<Pick<SelvaDeps, 'tokens' | 'uploadLimits' | 'services'>>;
27
+ log?: ILogger;
28
+ }
29
+ /**
30
+ * Swallows output unless `LOUD=1`.
31
+ *
32
+ * `runHandler` logs through `req.log` on the 500 path. Without a logger a test
33
+ * that trips an unexpected 500 dies inside the error handler itself, which
34
+ * hides the error that caused it.
35
+ */
36
+ export declare const silentLog: ILogger;
37
+ export interface SeededUser {
38
+ id: string;
39
+ email: string;
40
+ }
41
+ export declare function seedUser(h: TestHarness, email: string, platformPermissions?: PlatformPermission[]): Promise<SeededUser>;
42
+ export declare function seedOrg(h: TestHarness, opts: {
43
+ name: string;
44
+ slug: string;
45
+ ownerId: string;
46
+ }): Promise<Organization>;
47
+ export declare function seedOrgMember(h: TestHarness, opts: {
48
+ orgId: string;
49
+ userId: string;
50
+ role: OrgRole;
51
+ permissions?: OrgPermission[];
52
+ }): Promise<OrgMember>;
53
+ export declare function seedProject(h: TestHarness, opts: {
54
+ orgId: string;
55
+ name: string;
56
+ slug: string;
57
+ ownerId: string;
58
+ visibility?: ProjectVisibility;
59
+ autoJoinOnUpload?: boolean;
60
+ }): Promise<Project>;
61
+ export declare function seedProjectMember(h: TestHarness, opts: {
62
+ projectId: string;
63
+ userId: string;
64
+ role: ProjectRole;
65
+ }): Promise<ProjectMember>;
66
+ /**
67
+ * Grant platform-wide permissions to a seeded user.
68
+ *
69
+ * Replaces the whole set rather than adding to it, matching
70
+ * `IPermissionStore.set` — a test that means to add has to pass the existing
71
+ * permissions back in.
72
+ */
73
+ export declare function grantPlatformPermissions(h: TestHarness, userId: string, perms: PlatformPermission[]): Promise<void>;
74
+ export interface SeededDefinition {
75
+ record: DefinitionRecord;
76
+ version: DefinitionVersion;
77
+ }
78
+ /**
79
+ * A definition with one version, written through the host's `DefinitionService`
80
+ * rather than straight into the store.
81
+ *
82
+ * Going through the service is what makes the fixture match production: it is
83
+ * what allocates the version, writes the blob, and moves the channel pointer,
84
+ * and a test that seeded the record alone would exercise a definition no solve
85
+ * path can load.
86
+ */
87
+ export declare function seedDefinition(h: TestHarness, opts: {
88
+ projectId: string;
89
+ ownerId: string;
90
+ displayName?: string;
91
+ guid?: string;
92
+ }): Promise<SeededDefinition>;
93
+ export interface SeededShareLink {
94
+ link: ShareLink;
95
+ rawToken: string;
96
+ }
97
+ /**
98
+ * A share link plus the plaintext token behind it.
99
+ *
100
+ * Minted through the harness's own codec, so the stored hash verifies against
101
+ * the same secret a handler resolves from `deps.tokens.shareLinks` — a second
102
+ * codec would hash to something no handler could ever match.
103
+ */
104
+ export declare function seedShareLink(h: TestHarness, opts: {
105
+ definitionId: string;
106
+ channel?: 'live' | 'draft';
107
+ createdBy: string;
108
+ allowSolve?: boolean;
109
+ maxSolves?: number | null;
110
+ expiresAt?: string | null;
111
+ }): Promise<SeededShareLink>;
112
+ /**
113
+ * The harness a `callHandler` call should build its deps from, carried on the
114
+ * locals rather than passed alongside them.
115
+ *
116
+ * A test already threads `locals` from `actAs` into every call; making the
117
+ * harness a second argument would mean passing the same two values everywhere
118
+ * and would let them disagree — locals from one stack, deps from another, which
119
+ * type-checks and silently tests nothing.
120
+ */
121
+ declare const HARNESS: unique symbol;
122
+ export interface ActingLocals {
123
+ user: AuthUser;
124
+ ctx: RequestContext;
125
+ profile: ReturnType<typeof emptyProfile>;
126
+ providers: SelvaConfig;
127
+ log: ILogger;
128
+ [HARNESS]?: TestHarness;
129
+ }
130
+ /**
131
+ * Build the per-request identity a handler sees.
132
+ *
133
+ * Mirrors what a host's auth hook produces: one membership lookup, plus the
134
+ * instance-admin fallback to the first listed org for an admin who belongs to
135
+ * none. Keeping this aligned with the host is the point of testing against real
136
+ * stores rather than mocks — a fixture that invented its own `ctx` would prove
137
+ * the handler works under scope no request ever has.
138
+ */
139
+ export declare function actAs(h: TestHarness, userId: string): Promise<ActingLocals>;
140
+ export declare function anon(h: TestHarness): {
141
+ providers: SelvaConfig;
142
+ [HARNESS]?: TestHarness;
143
+ };
144
+ export interface CallHandlerOpts {
145
+ locals: unknown;
146
+ params?: Record<string, string>;
147
+ url?: string;
148
+ body?: unknown;
149
+ headers?: Record<string, string>;
150
+ }
151
+ export interface CallResult {
152
+ status: number;
153
+ headers: Headers;
154
+ json?: unknown;
155
+ text?: string;
156
+ location?: string;
157
+ }
158
+ /**
159
+ * Invoke an `ApiHandler` with no host framework in the loop.
160
+ *
161
+ * Still goes through `runHandler`, because that is the contract: it is what
162
+ * turns a thrown `ApiError` — or a host error `mapError` recognizes — into a
163
+ * status. Calling the handler bare would make every failure an exception and
164
+ * every status assertion untestable.
165
+ */
166
+ export declare function callHandler(handler: ApiHandler, opts: CallHandlerOpts): Promise<CallResult>;
167
+ export {};
168
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/testing/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * A host-agnostic harness for testing API handlers.
3
+ *
4
+ * Handlers are transport-free, but the tests around them were not: they built
5
+ * a SvelteKit `RequestEvent` and imported the Selva app's fixtures, so a second
6
+ * host could run the handlers and inherit none of the tests proving they work.
7
+ * That is the duplication this removes — a host wires a `TestHarness` once and
8
+ * gets the shared suites.
9
+ *
10
+ * Two things stay injected because no interface supplies them:
11
+ * - {@link SeedAuthAdapter}, since identity has no portable creation method.
12
+ * - `mapError`, since folding a host's own domain errors into the envelope is
13
+ * by definition host-specific. Omitting it is not neutral: a host whose
14
+ * guards throw something `runHandler` does not recognize gets a 500 where
15
+ * production returns 403, and every status assertion silently weakens.
16
+ */
17
+ import { randomUUID } from 'node:crypto';
18
+ import { SYSTEM_CONTEXT, DEFAULT_ORG_PERMISSIONS, DEFAULT_SHARE_LINK_MAX_SOLVES, emptyProfile } from '@selvajs/platform';
19
+ import { depsFromConfig, runHandler } from '../api/index.js';
20
+ /**
21
+ * Swallows output unless `LOUD=1`.
22
+ *
23
+ * `runHandler` logs through `req.log` on the 500 path. Without a logger a test
24
+ * that trips an unexpected 500 dies inside the error handler itself, which
25
+ * hides the error that caused it.
26
+ */
27
+ export const silentLog = {
28
+ error: (...args) => {
29
+ if (process.env.LOUD)
30
+ console.error('[test log]', ...args);
31
+ },
32
+ warn: () => { },
33
+ info: () => { },
34
+ debug: () => { },
35
+ child: () => silentLog
36
+ };
37
+ export async function seedUser(h, email, platformPermissions = []) {
38
+ // Identity row then data-layer row, in that order — the sequence production
39
+ // runs through its auth hook. Reversing it leaves a data row keyed to an id
40
+ // no identity claims.
41
+ const u = await h.auth.createUser(email);
42
+ await h.config.data.ensureUser(SYSTEM_CONTEXT, u.id);
43
+ if (platformPermissions.length > 0) {
44
+ await h.config.data.permissions.set(SYSTEM_CONTEXT, u.id, platformPermissions);
45
+ }
46
+ // `AuthUser.email` is optional — a header-auth provider need not report one.
47
+ // The seeded email is the caller's own input, so hand that back rather than
48
+ // widening every test's `email` to `string | undefined`.
49
+ return { id: u.id, email };
50
+ }
51
+ export async function seedOrg(h, opts) {
52
+ const now = new Date().toISOString();
53
+ const org = {
54
+ id: randomUUID(),
55
+ name: opts.name,
56
+ slug: opts.slug,
57
+ ownerId: opts.ownerId,
58
+ createdBy: opts.ownerId,
59
+ updatedBy: opts.ownerId,
60
+ createdAt: now,
61
+ updatedAt: now,
62
+ deletedAt: null
63
+ };
64
+ await h.config.data.orgs.createOrg(SYSTEM_CONTEXT, org);
65
+ return org;
66
+ }
67
+ export async function seedOrgMember(h, opts) {
68
+ const now = new Date().toISOString();
69
+ const member = {
70
+ orgId: opts.orgId,
71
+ userId: opts.userId,
72
+ role: opts.role,
73
+ permissions: opts.permissions ?? [...DEFAULT_ORG_PERMISSIONS[opts.role]],
74
+ joinedAt: now,
75
+ updatedAt: now,
76
+ updatedBy: opts.userId,
77
+ deletedAt: null
78
+ };
79
+ await h.config.data.orgs.addOrgMember(SYSTEM_CONTEXT, member);
80
+ return member;
81
+ }
82
+ export async function seedProject(h, opts) {
83
+ const now = new Date().toISOString();
84
+ const project = {
85
+ id: randomUUID(),
86
+ orgId: opts.orgId,
87
+ name: opts.name,
88
+ slug: opts.slug,
89
+ visibility: opts.visibility ?? 'private',
90
+ ownerId: opts.ownerId,
91
+ createdBy: opts.ownerId,
92
+ updatedBy: opts.ownerId,
93
+ autoJoinOnUpload: opts.autoJoinOnUpload ?? false,
94
+ createdAt: now,
95
+ updatedAt: now,
96
+ deletedAt: null
97
+ };
98
+ await h.config.data.projects.createProject(SYSTEM_CONTEXT, project);
99
+ await h.config.data.projects.addProjectMember(SYSTEM_CONTEXT, {
100
+ projectId: project.id,
101
+ userId: opts.ownerId,
102
+ role: 'owner',
103
+ joinedAt: now,
104
+ updatedAt: now,
105
+ updatedBy: opts.ownerId,
106
+ deletedAt: null
107
+ });
108
+ return project;
109
+ }
110
+ export async function seedProjectMember(h, opts) {
111
+ const now = new Date().toISOString();
112
+ const member = {
113
+ projectId: opts.projectId,
114
+ userId: opts.userId,
115
+ role: opts.role,
116
+ joinedAt: now,
117
+ updatedAt: now,
118
+ updatedBy: opts.userId,
119
+ deletedAt: null
120
+ };
121
+ await h.config.data.projects.addProjectMember(SYSTEM_CONTEXT, member);
122
+ return member;
123
+ }
124
+ /**
125
+ * Grant platform-wide permissions to a seeded user.
126
+ *
127
+ * Replaces the whole set rather than adding to it, matching
128
+ * `IPermissionStore.set` — a test that means to add has to pass the existing
129
+ * permissions back in.
130
+ */
131
+ export async function grantPlatformPermissions(h, userId, perms) {
132
+ await h.config.data.permissions.set(SYSTEM_CONTEXT, userId, perms);
133
+ }
134
+ /**
135
+ * A definition with one version, written through the host's `DefinitionService`
136
+ * rather than straight into the store.
137
+ *
138
+ * Going through the service is what makes the fixture match production: it is
139
+ * what allocates the version, writes the blob, and moves the channel pointer,
140
+ * and a test that seeded the record alone would exercise a definition no solve
141
+ * path can load.
142
+ */
143
+ export async function seedDefinition(h, opts) {
144
+ const service = h.deps?.services?.definitions;
145
+ if (!service)
146
+ throw new Error('TestHarness.deps.services.definitions is required to seed one');
147
+ const input = {
148
+ guid: opts.guid ?? randomUUID(),
149
+ projectId: opts.projectId,
150
+ ownerId: opts.ownerId,
151
+ fileExt: 'gh',
152
+ displayName: opts.displayName ?? 'Test Definition',
153
+ originalFilename: 'test.gh'
154
+ };
155
+ const file = new TextEncoder().encode('FAKE_GH_BYTES');
156
+ // Admin-shaped ctx so the service's own tenancy checks don't refuse a seed.
157
+ const ctx = {
158
+ userId: opts.ownerId,
159
+ actingOrgId: undefined,
160
+ platformPermissions: ['instance_admin'],
161
+ orgPermissions: []
162
+ };
163
+ const schema = { name: 'Test', inputs: [], outputs: [] };
164
+ return service.create(ctx, input, file, schema);
165
+ }
166
+ /**
167
+ * A share link plus the plaintext token behind it.
168
+ *
169
+ * Minted through the harness's own codec, so the stored hash verifies against
170
+ * the same secret a handler resolves from `deps.tokens.shareLinks` — a second
171
+ * codec would hash to something no handler could ever match.
172
+ */
173
+ export async function seedShareLink(h, opts) {
174
+ const codec = h.deps?.tokens?.shareLinks;
175
+ if (!codec)
176
+ throw new Error('TestHarness.deps.tokens.shareLinks is required to seed a link');
177
+ const rawToken = codec.mintRawToken();
178
+ const link = {
179
+ id: randomUUID(),
180
+ definitionId: opts.definitionId,
181
+ channel: opts.channel ?? 'live',
182
+ tokenHash: codec.hashToken(rawToken),
183
+ createdBy: opts.createdBy,
184
+ createdAt: new Date().toISOString(),
185
+ expiresAt: opts.expiresAt ?? null,
186
+ revokedAt: null,
187
+ allowSolve: opts.allowSolve ?? true,
188
+ maxSolves: opts.maxSolves === undefined ? DEFAULT_SHARE_LINK_MAX_SOLVES : opts.maxSolves,
189
+ solveCount: 0
190
+ };
191
+ await h.config.data.shareLinks.create({
192
+ userId: opts.createdBy,
193
+ actingOrgId: undefined,
194
+ platformPermissions: ['instance_admin'],
195
+ orgPermissions: []
196
+ }, link);
197
+ return { link, rawToken };
198
+ }
199
+ // ============================================================================
200
+ // Acting identity
201
+ // ============================================================================
202
+ /**
203
+ * The harness a `callHandler` call should build its deps from, carried on the
204
+ * locals rather than passed alongside them.
205
+ *
206
+ * A test already threads `locals` from `actAs` into every call; making the
207
+ * harness a second argument would mean passing the same two values everywhere
208
+ * and would let them disagree — locals from one stack, deps from another, which
209
+ * type-checks and silently tests nothing.
210
+ */
211
+ const HARNESS = Symbol.for('@selvajs/server/testing.harness');
212
+ /**
213
+ * Build the per-request identity a handler sees.
214
+ *
215
+ * Mirrors what a host's auth hook produces: one membership lookup, plus the
216
+ * instance-admin fallback to the first listed org for an admin who belongs to
217
+ * none. Keeping this aligned with the host is the point of testing against real
218
+ * stores rather than mocks — a fixture that invented its own `ctx` would prove
219
+ * the handler works under scope no request ever has.
220
+ */
221
+ export async function actAs(h, userId) {
222
+ const user = await h.auth.findById(userId);
223
+ if (!user)
224
+ throw new Error(`actAs: user not found: ${userId}`);
225
+ const platformPermissions = await h.config.data.permissions.getFor(SYSTEM_CONTEXT, user.id);
226
+ const membership = await h.config.data.orgs.findUserMembership(SYSTEM_CONTEXT, user.id);
227
+ let actingOrgId = membership?.org.id;
228
+ const orgPermissions = membership ? [...membership.member.permissions] : [];
229
+ if (!actingOrgId && platformPermissions.includes('instance_admin')) {
230
+ const firstOrgPage = await h.config.data.orgs.listOrgs(SYSTEM_CONTEXT, { limit: 1 });
231
+ actingOrgId = firstOrgPage.items[0]?.id;
232
+ }
233
+ const profile = (await h.config.data.userProfile.getProfile(SYSTEM_CONTEXT, user.id)) ?? emptyProfile(user.id);
234
+ return {
235
+ user,
236
+ ctx: { userId: user.id, actingOrgId, platformPermissions, orgPermissions },
237
+ profile,
238
+ providers: h.config,
239
+ log: h.log ?? silentLog,
240
+ [HARNESS]: h
241
+ };
242
+ }
243
+ export function anon(h) {
244
+ return { providers: h.config, [HARNESS]: h };
245
+ }
246
+ /**
247
+ * Invoke an `ApiHandler` with no host framework in the loop.
248
+ *
249
+ * Still goes through `runHandler`, because that is the contract: it is what
250
+ * turns a thrown `ApiError` — or a host error `mapError` recognizes — into a
251
+ * status. Calling the handler bare would make every failure an exception and
252
+ * every status assertion untestable.
253
+ */
254
+ export async function callHandler(handler, opts) {
255
+ const url = new URL(opts.url ?? 'http://test.local/');
256
+ const init = {
257
+ method: opts.body !== undefined ? 'POST' : 'GET',
258
+ headers: opts.headers ?? {}
259
+ };
260
+ if (opts.body !== undefined) {
261
+ // FormData carries its own multipart content-type with a generated
262
+ // boundary; forcing JSON onto it makes `request.formData()` throw.
263
+ if (opts.body instanceof FormData) {
264
+ init.body = opts.body;
265
+ }
266
+ else {
267
+ init.headers['content-type'] ??= 'application/json';
268
+ init.body = typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body);
269
+ }
270
+ }
271
+ const locals = opts.locals;
272
+ const h = locals[HARNESS];
273
+ if (!h) {
274
+ // Without the harness there is no `mapError`, so every guard rejection
275
+ // would fall through to a 500 and each expected 403 would read as a
276
+ // passing-but-meaningless assertion. Fail loudly instead.
277
+ throw new Error('callHandler: locals carry no harness — build them with `actAs()` or `anon()`.');
278
+ }
279
+ const req = {
280
+ ctx: locals.ctx,
281
+ user: locals.user,
282
+ profile: locals.profile,
283
+ log: locals.log ?? h.log ?? silentLog,
284
+ params: opts.params ?? {},
285
+ url,
286
+ request: new Request(url.toString(), init),
287
+ deps: depsFromConfig(locals.providers, h.deps?.services ?? {}, {
288
+ tokens: h.deps?.tokens,
289
+ uploadLimits: h.deps?.uploadLimits
290
+ })
291
+ };
292
+ return readResponse(await runHandler(handler, req, { fallback: 'Test', mapError: h.mapError }));
293
+ }
294
+ async function readResponse(res) {
295
+ const contentType = res.headers.get('content-type') ?? '';
296
+ const out = { status: res.status, headers: res.headers };
297
+ if (res.headers.has('location'))
298
+ out.location = res.headers.get('location');
299
+ if (contentType.includes('application/json')) {
300
+ const text = await res.text();
301
+ out.text = text;
302
+ try {
303
+ out.json = text ? JSON.parse(text) : undefined;
304
+ }
305
+ catch {
306
+ // Leave it as text — a malformed body is the assertion's problem.
307
+ }
308
+ }
309
+ else {
310
+ out.text = await res.text();
311
+ }
312
+ return out;
313
+ }
314
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/testing/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Test harness for the transport-free API handlers.
3
+ *
4
+ * A host builds a {@link TestHarness} around its own provider stack and gets
5
+ * the seeders, `actAs`, and `callHandler` that the handler tests are written
6
+ * against — so the tests travel with the handlers instead of being rewritten
7
+ * per host.
8
+ *
9
+ * This entry point constructs no provider. Which stack a test runs on is the
10
+ * host's decision, and a package that picked one would drag that provider into
11
+ * every consumer's dependency tree.
12
+ */
13
+ export type { SeedAuthAdapter } from './seed-adapter.js';
14
+ export { seedUser, seedOrg, seedOrgMember, seedProject, seedProjectMember, seedDefinition, seedShareLink, grantPlatformPermissions, actAs, anon, callHandler, silentLog } from './harness.js';
15
+ export type { TestHarness, SeededUser, SeededDefinition, SeededShareLink, ActingLocals, CallHandlerOpts, CallResult } from './harness.js';
16
+ export { seedAcme, seedBigClient, seedThirdOrg, seedCommons } from './scenarios.js';
17
+ export type { AcmeFixture, BigClientFixture, ThirdOrgFixture, CommonsFixture } from './scenarios.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AACtB,YAAY,EACX,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpF,YAAY,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Test harness for the transport-free API handlers.
3
+ *
4
+ * A host builds a {@link TestHarness} around its own provider stack and gets
5
+ * the seeders, `actAs`, and `callHandler` that the handler tests are written
6
+ * against — so the tests travel with the handlers instead of being rewritten
7
+ * per host.
8
+ *
9
+ * This entry point constructs no provider. Which stack a test runs on is the
10
+ * host's decision, and a package that picked one would drag that provider into
11
+ * every consumer's dependency tree.
12
+ */
13
+ export { seedUser, seedOrg, seedOrgMember, seedProject, seedProjectMember, seedDefinition, seedShareLink, grantPlatformPermissions, actAs, anon, callHandler, silentLog } from './harness.js';
14
+ export { seedAcme, seedBigClient, seedThirdOrg, seedCommons } from './scenarios.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}