@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,51 @@
1
+ /**
2
+ * The HTTP layer over {@link createIdempotencyStore}: how a replayed response
3
+ * is keyed, snapshotted, and identified on the wire.
4
+ *
5
+ * The store itself is generic over `T` and knows nothing about requests. This
6
+ * is the part two hosts must agree on to implement the same `Idempotency-Key`
7
+ * contract, and the part each would otherwise reinvent:
8
+ *
9
+ * - **The key namespaces by caller.** `Idempotency-Key` is client-chosen, so
10
+ * two tenants can pick the same string. Without the caller in the key one
11
+ * would replay the other's result — that is the whole security property
12
+ * here, and a host that got it wrong would leak across tenants with
13
+ * nothing failing at build time.
14
+ * - **The response round-trips through a snapshot.** A `Response` body reads
15
+ * exactly once, so storing the object itself replays an empty body.
16
+ * - **A replay says so**, via `Idempotency-Replayed`. A client that cannot
17
+ * tell a replay from a fresh run cannot tell a successful retry from a
18
+ * second execution.
19
+ *
20
+ * How long a response stays replayable is *not* here: that trades a client's
21
+ * retry window against serving a stale result after the underlying resource
22
+ * moves, and only the host knows its own deadlines.
23
+ */
24
+ /** Header stamped on a replayed response, so a client can distinguish one. */
25
+ export const IDEMPOTENCY_REPLAYED_HEADER = 'Idempotency-Replayed';
26
+ /**
27
+ * Namespace a client-chosen key by its caller.
28
+ *
29
+ * `callerId` should be the narrowest identity available — a token id rather
30
+ * than a user id where the host issues per-token credentials, so two tokens
31
+ * held by one user do not share replays.
32
+ */
33
+ export function idempotencyKey(callerId, clientKey) {
34
+ return `${callerId} ${clientKey}`;
35
+ }
36
+ /** Flatten a response so it can be replayed. Consumes the body. */
37
+ export async function toStoredResponse(res) {
38
+ return {
39
+ status: res.status,
40
+ headers: [...res.headers.entries()],
41
+ body: await res.arrayBuffer()
42
+ };
43
+ }
44
+ /** Rebuild a live `Response` from a snapshot, marking it when it is a replay. */
45
+ export function fromStoredResponse(stored, replayed) {
46
+ const headers = new Headers(stored.headers);
47
+ if (replayed)
48
+ headers.set(IDEMPOTENCY_REPLAYED_HEADER, 'true');
49
+ return new Response(stored.body, { status: stored.status, headers });
50
+ }
51
+ //# sourceMappingURL=idempotency-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-http.js","sourceRoot":"","sources":["../../src/compute/idempotency-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAYlE;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,SAAiB;IACjE,OAAO,GAAG,QAAQ,IAAI,SAAS,EAAE,CAAC;AACnC,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAa;IACnD,OAAO;QACN,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,EAAE,MAAM,GAAG,CAAC,WAAW,EAAE;KAC7B,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,QAAiB;IAC3E,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC;IAC/D,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AACtE,CAAC"}
@@ -3,4 +3,9 @@ export { createComputeRateLimiter, DEFAULT_MAX_KEYS, type ComputeRateLimiter, ty
3
3
  export { createIdempotencyStore, DEFAULT_IDEMPOTENCY_MAX_KEYS, type IdempotencyStore, type IdempotencyStoreConfig, type IdempotencyOutcome } from './idempotency.js';
4
4
  export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl, isLinkLocalUrl } from './safe-url.js';
5
5
  export { createRemoteDefinitionFetcher, readBodyWithCap, type RemoteDefinitionFetcher, type RemoteDefinitionConfig } from './remote-definition.js';
6
+ export { resolveServerForOrg } from './resolve-server.js';
7
+ export { ComputeServerUnconfiguredError } from './errors.js';
8
+ export { evictChangedServers, type ServerConnection } from './evict-changed-servers.js';
9
+ export { validateIncomingServers, resolveApiKey, storedKeysById, type IncomingServerBase } from './server-config-write.js';
10
+ export { idempotencyKey, toStoredResponse, fromStoredResponse, IDEMPOTENCY_REPLAYED_HEADER, type StoredResponse } from './idempotency-http.js';
6
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,yBAAyB,EACzB,6BAA6B,EAC7B,cAAc,EACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,yBAAyB,EACzB,6BAA6B,EAC7B,cAAc,EACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EACN,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,KAAK,kBAAkB,EACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,2BAA2B,EAC3B,KAAK,cAAc,EACnB,MAAM,uBAAuB,CAAC"}
@@ -13,4 +13,9 @@ export { createComputeRateLimiter, DEFAULT_MAX_KEYS } from './rate-limit.js';
13
13
  export { createIdempotencyStore, DEFAULT_IDEMPOTENCY_MAX_KEYS } from './idempotency.js';
14
14
  export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl, isLinkLocalUrl } from './safe-url.js';
15
15
  export { createRemoteDefinitionFetcher, readBodyWithCap } from './remote-definition.js';
16
+ export { resolveServerForOrg } from './resolve-server.js';
17
+ export { ComputeServerUnconfiguredError } from './errors.js';
18
+ export { evictChangedServers } from './evict-changed-servers.js';
19
+ export { validateIncomingServers, resolveApiKey, storedKeysById } from './server-config-write.js';
20
+ export { idempotencyKey, toStoredResponse, fromStoredResponse, IDEMPOTENCY_REPLAYED_HEADER } from './idempotency-http.js';
16
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,qCAAqC;AAErC,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAI5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,yBAAyB,EACzB,6BAA6B,EAC7B,cAAc,EACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,qCAAqC;AAErC,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAI5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,yBAAyB,EACzB,6BAA6B,EAC7B,cAAc,EACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAyB,MAAM,4BAA4B,CAAC;AACxF,OAAO,EACN,uBAAuB,EACvB,aAAa,EACb,cAAc,EAEd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,2BAA2B,EAE3B,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { type ComputeServerConfig, type IDataProvider, type RequestContext } from '@selvajs/platform';
2
+ import { ComputeServerUnconfiguredError } from './errors.js';
3
+ export { ComputeServerUnconfiguredError };
4
+ /** The one store this module reads — `SelvaDeps['computeServer']`. */
5
+ type ComputeServerStore = IDataProvider['computeServer'];
6
+ /**
7
+ * Pick the right Rhino.Compute server for an (org, definition) pair.
8
+ *
9
+ * Resolution order, narrowest wins:
10
+ * 1. `definitionPin` if it points at a server visible to `orgId`.
11
+ * 2. `orgDefaults[orgId]` if set and visible.
12
+ * 3. Global `defaultServerId`.
13
+ *
14
+ * The store returns the full config; the visibility predicate runs in the pure
15
+ * helper so callers don't reimplement it. Re-throws the pure helper's "nothing
16
+ * visible" failure as a typed `ComputeServerUnconfiguredError`.
17
+ *
18
+ * Reads a key-free config and fetches the `apiKey` only for the server that
19
+ * actually wins, instead of decrypting every configured server's key to use
20
+ * exactly one of them.
21
+ */
22
+ export declare function resolveServerForOrg(ctx: RequestContext, orgId: string | null | undefined, store: ComputeServerStore, opts?: {
23
+ definitionPin?: string | null;
24
+ }): Promise<ComputeServerConfig>;
25
+ //# sourceMappingURL=resolve-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-server.d.ts","sourceRoot":"","sources":["../../src/compute/resolve-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAI7D,OAAO,EAAE,8BAA8B,EAAE,CAAC;AAE1C,sEAAsE;AACtE,KAAK,kBAAkB,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,KAAK,EAAE,kBAAkB,EACzB,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GAC1C,OAAO,CAAC,mBAAmB,CAAC,CAU9B"}
@@ -0,0 +1,35 @@
1
+ import { resolveServerForOrg as resolvePure } from '@selvajs/platform';
2
+ import { ComputeServerUnconfiguredError } from './errors.js';
3
+ // Re-exported so the throw site and the class still read as one unit. Error
4
+ // mappers should import from `./errors.js` directly.
5
+ export { ComputeServerUnconfiguredError };
6
+ /**
7
+ * Pick the right Rhino.Compute server for an (org, definition) pair.
8
+ *
9
+ * Resolution order, narrowest wins:
10
+ * 1. `definitionPin` if it points at a server visible to `orgId`.
11
+ * 2. `orgDefaults[orgId]` if set and visible.
12
+ * 3. Global `defaultServerId`.
13
+ *
14
+ * The store returns the full config; the visibility predicate runs in the pure
15
+ * helper so callers don't reimplement it. Re-throws the pure helper's "nothing
16
+ * visible" failure as a typed `ComputeServerUnconfiguredError`.
17
+ *
18
+ * Reads a key-free config and fetches the `apiKey` only for the server that
19
+ * actually wins, instead of decrypting every configured server's key to use
20
+ * exactly one of them.
21
+ */
22
+ export async function resolveServerForOrg(ctx, orgId, store, opts = {}) {
23
+ const config = await store.getConfig(ctx);
24
+ let server;
25
+ try {
26
+ server = resolvePure(config, orgId, { definitionPin: opts.definitionPin });
27
+ }
28
+ catch (err) {
29
+ throw new ComputeServerUnconfiguredError(err instanceof Error ? err.message : undefined);
30
+ }
31
+ if (!server.hasApiKey)
32
+ return server;
33
+ return { ...server, apiKey: await store.getServerApiKey(ctx, server.id) };
34
+ }
35
+ //# sourceMappingURL=resolve-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-server.js","sourceRoot":"","sources":["../../src/compute/resolve-server.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,mBAAmB,IAAI,WAAW,EAIlC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAE7D,4EAA4E;AAC5E,qDAAqD;AACrD,OAAO,EAAE,8BAA8B,EAAE,CAAC;AAK1C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,GAAmB,EACnB,KAAgC,EAChC,KAAyB,EACzB,OAA0C,EAAE;IAE5C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,MAA2B,CAAC;IAChC,IAAI,CAAC;QACJ,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,8BAA8B,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC;IACrC,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Shared write-path rules for compute server config.
3
+ *
4
+ * Two routes accept a server set: `/api/admin/compute` (platform scope,
5
+ * `manage_compute`) and `/api/v1/orgs/{orgId}/compute` (org scope,
6
+ * `manage_org_compute`). They differ in who may call them, what extra fields
7
+ * they carry (`sharedWith` vs `ownerOrgId`), and how they validate the default
8
+ * selection — but the field validation and the apiKey merge below must behave
9
+ * identically in both, so they live here rather than in either handler.
10
+ *
11
+ * The apiKey merge is why this file exists: if the two copies disagree, a
12
+ * stored credential silently gets cleared or leaked, and nothing fails at
13
+ * build time.
14
+ */
15
+ /** The fields every incoming server carries, whatever its scope. */
16
+ export interface IncomingServerBase {
17
+ id: string;
18
+ label: string;
19
+ serverUrl: string;
20
+ /**
21
+ * `undefined` → preserve the stored key; `null` → clear it;
22
+ * non-empty string → replace it. See {@link resolveApiKey}.
23
+ */
24
+ apiKey?: string | null;
25
+ timeoutMs?: number;
26
+ retryCount?: number;
27
+ }
28
+ /**
29
+ * Validates the scope-independent fields of every submitted server. Throws the
30
+ * first failure as a 400; scope-specific fields (`sharedWith`, `ownerOrgId`)
31
+ * are the caller's business.
32
+ */
33
+ export declare function validateIncomingServers(servers: readonly IncomingServerBase[]): void;
34
+ /**
35
+ * The UI never receives stored keys — it sends back `hasApiKey` and omits the
36
+ * field for servers the operator didn't touch. So "omitted" cannot mean "clear":
37
+ * it has to preserve, or every save through the settings form would wipe the
38
+ * keys of every server the operator left alone. Clearing is explicit (`null`);
39
+ * an empty string is treated as "not provided" since that's what an untouched
40
+ * password input submits.
41
+ */
42
+ export declare function resolveApiKey(submitted: string | null | undefined, storedKey: string | undefined): string | undefined;
43
+ /**
44
+ * Builds the id → stored-key lookup the merge reads. The caller passes only the
45
+ * servers in its own scope, so one scope's write can never resolve a key from
46
+ * another's rows.
47
+ */
48
+ export declare function storedKeysById(scopedServers: readonly {
49
+ id: string;
50
+ apiKey?: string;
51
+ }[]): Map<string, string | undefined>;
52
+ //# sourceMappingURL=server-config-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-config-write.d.ts","sourceRoot":"","sources":["../../src/compute/server-config-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAqDH,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAYpF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,GAAG,SAAS,CAGpB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC7B,aAAa,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GACvD,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAEjC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Shared write-path rules for compute server config.
3
+ *
4
+ * Two routes accept a server set: `/api/admin/compute` (platform scope,
5
+ * `manage_compute`) and `/api/v1/orgs/{orgId}/compute` (org scope,
6
+ * `manage_org_compute`). They differ in who may call them, what extra fields
7
+ * they carry (`sharedWith` vs `ownerOrgId`), and how they validate the default
8
+ * selection — but the field validation and the apiKey merge below must behave
9
+ * identically in both, so they live here rather than in either handler.
10
+ *
11
+ * The apiKey merge is why this file exists: if the two copies disagree, a
12
+ * stored credential silently gets cleared or leaked, and nothing fails at
13
+ * build time.
14
+ */
15
+ import { isLinkLocalUrl } from './safe-url.js';
16
+ import { apiError, ApiErrorCode } from '../api/errors.js';
17
+ /**
18
+ * A stored `serverUrl` is fetched server-side on every status probe, every
19
+ * purge/shutdown action, and every solve, so an unfiltered one is an SSRF
20
+ * primitive handed to whoever holds `manage_compute`. Two rules:
21
+ *
22
+ * - Scheme allowlist: `http`/`https`. `file:`, `gopher:` and friends are never
23
+ * a Rhino.Compute endpoint.
24
+ * - Link-local (`169.254.0.0/16`) is refused. That range carries the cloud
25
+ * metadata service at `169.254.169.254`, which hands out the host's own IAM
26
+ * credentials to anything running on the box.
27
+ *
28
+ * **Loopback and RFC1918 are deliberately allowed.** Running compute on
29
+ * `localhost:6500` or a LAN box is the ordinary self-hosted layout, not an
30
+ * attack — most deployments reach their compute server over exactly those
31
+ * addresses. Blocking them by default would break those instances on upgrade
32
+ * and push every operator toward an opt-out flag, and a guard that everyone
33
+ * disables protects nobody. So the check is narrowed to the range where no
34
+ * legitimate compute server ever lives.
35
+ *
36
+ * What this does not stop: a privileged admin pointing the app at an internal
37
+ * host to probe it. That is a real but much weaker concern than credential
38
+ * theft, it needs `manage_compute` already, and it is inseparable from the
39
+ * legitimate case. Revisit if `manage_org_compute` is ever delegated widely on
40
+ * a multi-tenant instance.
41
+ *
42
+ * `isLinkLocalUrl` is reused rather than hand-rolled because a string compare
43
+ * against `169.254.169.254` misses the encodings that reach the same address:
44
+ * `http://2852039166/`, `http://0xa9fea9fe/`, `http://0251.0376.0251.0376/`.
45
+ */
46
+ function assertUsableServerUrl(serverUrl) {
47
+ let parsed;
48
+ try {
49
+ parsed = new URL(serverUrl);
50
+ }
51
+ catch {
52
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `Invalid serverUrl: ${serverUrl}`);
53
+ }
54
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
55
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `serverUrl must use http or https: ${serverUrl}`);
56
+ }
57
+ if (isLinkLocalUrl(serverUrl)) {
58
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `serverUrl points at the link-local range, which carries the cloud metadata service: ${serverUrl}`);
59
+ }
60
+ }
61
+ /**
62
+ * Validates the scope-independent fields of every submitted server. Throws the
63
+ * first failure as a 400; scope-specific fields (`sharedWith`, `ownerOrgId`)
64
+ * are the caller's business.
65
+ */
66
+ export function validateIncomingServers(servers) {
67
+ for (const s of servers) {
68
+ if (!s.id || typeof s.id !== 'string')
69
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, 'Each server needs an id');
70
+ if (!s.label || typeof s.label !== 'string')
71
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, 'Each server needs a label');
72
+ if (!s.serverUrl || typeof s.serverUrl !== 'string')
73
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, 'Each server needs a serverUrl');
74
+ assertUsableServerUrl(s.serverUrl);
75
+ if (s.apiKey !== undefined && s.apiKey !== null && typeof s.apiKey !== 'string')
76
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, 'apiKey must be a string, null, or omitted');
77
+ }
78
+ }
79
+ /**
80
+ * The UI never receives stored keys — it sends back `hasApiKey` and omits the
81
+ * field for servers the operator didn't touch. So "omitted" cannot mean "clear":
82
+ * it has to preserve, or every save through the settings form would wipe the
83
+ * keys of every server the operator left alone. Clearing is explicit (`null`);
84
+ * an empty string is treated as "not provided" since that's what an untouched
85
+ * password input submits.
86
+ */
87
+ export function resolveApiKey(submitted, storedKey) {
88
+ if (submitted === null)
89
+ return undefined;
90
+ return submitted ? submitted : storedKey;
91
+ }
92
+ /**
93
+ * Builds the id → stored-key lookup the merge reads. The caller passes only the
94
+ * servers in its own scope, so one scope's write can never resolve a key from
95
+ * another's rows.
96
+ */
97
+ export function storedKeysById(scopedServers) {
98
+ return new Map(scopedServers.map((s) => [s.id, s.apiKey]));
99
+ }
100
+ //# sourceMappingURL=server-config-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-config-write.js","sourceRoot":"","sources":["../../src/compute/server-config-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,SAAS,qBAAqB,CAAC,SAAiB;IAC/C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,sBAAsB,SAAS,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,qCAAqC,SAAS,EAAE,CAAC,CAAC;IACjG,CAAC;IACD,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,QAAQ,CACP,GAAG,EACH,YAAY,CAAC,iBAAiB,EAC9B,uFAAuF,SAAS,EAAE,CAClG,CAAC;IACH,CAAC;AACF,CAAC;AAgBD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAsC;IAC7E,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ;YACpC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,yBAAyB,CAAC,CAAC;QAC1E,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC1C,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,2BAA2B,CAAC,CAAC;QAC5E,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;YAClD,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,+BAA+B,CAAC,CAAC;QAChF,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;YAC9E,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,2CAA2C,CAAC,CAAC;IAC7F,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC5B,SAAoC,EACpC,SAA6B;IAE7B,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC7B,aAAyD;IAEzD,OAAO,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC"}
@@ -2,4 +2,5 @@ export { DefinitionService, type CreateDefinitionRecord } from './definition-ser
2
2
  export { fetchSchemaFromCompute, postSchemaFormData, assertSupportedSchemaVersion, assertCamelCaseSchema, readSchemaResults, SchemaExtractionError } from './schema-extraction.js';
3
3
  export type { SchemaExtractionResult } from './schema-extraction.js';
4
4
  export { createDefinitionLoader, DefinitionLoadError, type DefinitionChannel, type DefinitionLoader, type DefinitionLoaderDeps, type DefinitionLoadErrorKind, type DefinitionLoadOptions, type LoadedDefinition } from './load-for-render.js';
5
+ export { resolveAccessibleProjects, listVisibleDefinitions, getVisibleDefinition, loadVisibleVersion, type VisibilityDeps, type AccessibleProjectSet, type ListVisibleDefinitionsResult } from './visibility.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/definitions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEzF,OAAO,EACN,sBAAsB,EACtB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAErE,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/definitions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEzF,OAAO,EACN,sBAAsB,EACtB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAErE,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EACjC,MAAM,iBAAiB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { DefinitionService } from './definition-service.js';
2
2
  export { fetchSchemaFromCompute, postSchemaFormData, assertSupportedSchemaVersion, assertCamelCaseSchema, readSchemaResults, SchemaExtractionError } from './schema-extraction.js';
3
3
  export { createDefinitionLoader, DefinitionLoadError } from './load-for-render.js';
4
+ export { resolveAccessibleProjects, listVisibleDefinitions, getVisibleDefinition, loadVisibleVersion } from './visibility.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/definitions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA+B,MAAM,yBAAyB,CAAC;AAEzF,OAAO,EACN,sBAAsB,EACtB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EAOnB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/definitions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA+B,MAAM,yBAAyB,CAAC;AAEzF,OAAO,EACN,sBAAsB,EACtB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EAOnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAIlB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Visibility-filtered definition reads, shared by the library page load and
3
+ * `GET /api/v1/definitions`.
4
+ *
5
+ * **This is a tenancy boundary.** The project scan runs as `SYSTEM_CONTEXT` so
6
+ * cross-org `public` projects are reachable when `ALLOW_CROSS_ORG_PUBLIC` is on;
7
+ * `canView` over the fetched membership rows is the only thing keeping another
8
+ * org's projects out of the result. The definition list itself is then filtered
9
+ * *in the query* via `projectIds`, so the cursor is applied after visibility
10
+ * rather than over a page that gets thinned afterwards.
11
+ *
12
+ * Cost is constant in the number of orgs and projects: three bulk membership
13
+ * reads regardless of N. Anything added here must keep that property — a
14
+ * per-project `await` inside a loop reintroduces the N+1 this replaced.
15
+ */
16
+ import type { DefinitionListOptions, DefinitionRecord, DefinitionVersion, OrgMember, Page, Project, ProjectMember, RequestContext } from '@selvajs/platform';
17
+ import type { SelvaDeps } from '../api/deps.js';
18
+ /**
19
+ * The stores this module reads. Required at every call site.
20
+ *
21
+ * These used to fall back to a host's composition root, which welded this
22
+ * tenancy boundary to one app's boot — anything importing it started the whole
23
+ * application. Handlers pass `req.deps`; page loads pass their own equivalent.
24
+ * That is what lets a second host on a different provider set reuse this
25
+ * boundary instead of rebuilding it.
26
+ */
27
+ export type VisibilityDeps = Pick<SelvaDeps, 'orgs' | 'projects' | 'platformProjectGrants' | 'definitionMeta' | 'flag'>;
28
+ /**
29
+ * The caller's accessible project set plus the membership rows it was derived
30
+ * from, so a caller needing a second rule (`canEdit` on the projects page)
31
+ * can evaluate it with no further I/O.
32
+ */
33
+ export interface AccessibleProjectSet {
34
+ projects: Project[];
35
+ /** Every project the scan saw, keyed by id — for name lookups on rows. */
36
+ allById: Map<string, Project>;
37
+ /** The caller's project-membership row per scanned project (`null` = not a member). */
38
+ memberByProjectId: Map<string, ProjectMember | null>;
39
+ /** The caller's org-membership row per scanned org (`null` = not a member). */
40
+ orgMemberByOrgId: Map<string, OrgMember | null>;
41
+ }
42
+ /**
43
+ * Resolve the caller's accessible project set in a constant number of queries.
44
+ * Platform grants are fetched only for `platform`-visibility projects —
45
+ * `canView` short-circuits before reading them for every other visibility.
46
+ */
47
+ export declare function resolveAccessibleProjects(ctx: RequestContext, deps: VisibilityDeps): Promise<AccessibleProjectSet>;
48
+ export interface ListVisibleDefinitionsResult extends Page<DefinitionRecord> {
49
+ /** For resolving a definition's `projectId` to a name. */
50
+ projects: Project[];
51
+ }
52
+ /**
53
+ * `opts.projectId` narrows to one project; a caller who cannot view it gets an
54
+ * empty page rather than a 403, so this never reveals that a project exists.
55
+ */
56
+ export declare function listVisibleDefinitions(ctx: RequestContext, opts: (DefinitionListOptions & {
57
+ projectId?: string;
58
+ }) | undefined, deps: VisibilityDeps): Promise<ListVisibleDefinitionsResult>;
59
+ /**
60
+ * One definition, or `null` when the caller cannot view its project.
61
+ *
62
+ * Returns `null` rather than throwing 403 so callers surface a 404: a public
63
+ * API that answers "forbidden" for a guid the caller cannot see turns
64
+ * `/definitions/{guid}` into a cross-tenant existence oracle.
65
+ */
66
+ export declare function getVisibleDefinition(ctx: RequestContext, guid: string, deps: VisibilityDeps): Promise<DefinitionRecord | null>;
67
+ /**
68
+ * One version of a definition the caller can view, including its cached
69
+ * `schema`. `null` when the definition is invisible, the version is missing, or
70
+ * the version belongs to a different definition — a caller must not be able to
71
+ * read an arbitrary version by pairing it with a guid they can see.
72
+ */
73
+ export declare function loadVisibleVersion(ctx: RequestContext, guid: string, versionId: string, deps: VisibilityDeps): Promise<DefinitionVersion | null>;
74
+ //# sourceMappingURL=visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../src/definitions/visibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACX,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,IAAI,EAEJ,OAAO,EACP,aAAa,EACb,cAAc,EACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAChC,SAAS,EACT,MAAM,GAAG,UAAU,GAAG,uBAAuB,GAAG,gBAAgB,GAAG,MAAM,CACzE,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,0EAA0E;IAC1E,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,uFAAuF;IACvF,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC,CAAC;IACrD,+EAA+E;IAC/E,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;CAChD;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC9C,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,cAAc,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAyC/B;AAED,MAAM,WAAW,4BAA6B,SAAQ,IAAI,CAAC,gBAAgB,CAAC;IAC3E,0DAA0D;IAC1D,QAAQ,EAAE,OAAO,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC3C,GAAG,EAAE,cAAc,EACnB,IAAI,GAAE,qBAAqB,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,aAAK,EACzD,IAAI,EAAE,cAAc,GAClB,OAAO,CAAC,4BAA4B,CAAC,CAavC;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,GAClB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0BlC;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,cAAc,GAClB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAOnC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Visibility-filtered definition reads, shared by the library page load and
3
+ * `GET /api/v1/definitions`.
4
+ *
5
+ * **This is a tenancy boundary.** The project scan runs as `SYSTEM_CONTEXT` so
6
+ * cross-org `public` projects are reachable when `ALLOW_CROSS_ORG_PUBLIC` is on;
7
+ * `canView` over the fetched membership rows is the only thing keeping another
8
+ * org's projects out of the result. The definition list itself is then filtered
9
+ * *in the query* via `projectIds`, so the cursor is applied after visibility
10
+ * rather than over a page that gets thinned afterwards.
11
+ *
12
+ * Cost is constant in the number of orgs and projects: three bulk membership
13
+ * reads regardless of N. Anything added here must keep that property — a
14
+ * per-project `await` inside a loop reintroduces the N+1 this replaced.
15
+ */
16
+ import { SYSTEM_CONTEXT, canView } from '@selvajs/platform';
17
+ import { projectAccessInputFromRowsWith } from '../access/index.js';
18
+ /** Upper bound on the org/project scan. Visibility needs the whole set. */
19
+ const SCAN_LIMIT = 200;
20
+ /**
21
+ * Resolve the caller's accessible project set in a constant number of queries.
22
+ * Platform grants are fetched only for `platform`-visibility projects —
23
+ * `canView` short-circuits before reading them for every other visibility.
24
+ */
25
+ export async function resolveAccessibleProjects(ctx, deps) {
26
+ const orgs = deps.orgs;
27
+ const projectStore = deps.projects;
28
+ const grantStore = deps.platformProjectGrants;
29
+ const orgsPage = await orgs.listOrgs(SYSTEM_CONTEXT, { limit: SCAN_LIMIT });
30
+ const orgIds = orgsPage.items.map((o) => o.id);
31
+ const projectPages = await Promise.all(orgIds.map((orgId) => projectStore.listProjects(SYSTEM_CONTEXT, orgId, { limit: SCAN_LIMIT })));
32
+ const allProjects = projectPages.flatMap((p) => p.items);
33
+ const projectIds = allProjects.map((p) => p.id);
34
+ const platformProjectIds = allProjects
35
+ .filter((p) => p.visibility === 'platform')
36
+ .map((p) => p.id);
37
+ const [orgMemberByOrgId, memberByProjectId, grantsByProjectId] = await Promise.all([
38
+ orgs.getOrgMembersFor(SYSTEM_CONTEXT, orgIds, ctx.userId),
39
+ projectStore.getProjectMembersFor(SYSTEM_CONTEXT, projectIds, ctx.userId),
40
+ platformProjectIds.length
41
+ ? grantStore.listByProjects(SYSTEM_CONTEXT, platformProjectIds)
42
+ : Promise.resolve(new Map())
43
+ ]);
44
+ const projects = allProjects.filter((project) => canView(projectAccessInputFromRowsWith({ deps }, ctx, project, {
45
+ member: memberByProjectId.get(project.id) ?? null,
46
+ orgMember: orgMemberByOrgId.get(project.orgId) ?? null,
47
+ platformGrants: grantsByProjectId.get(project.id) ?? []
48
+ })));
49
+ return {
50
+ projects,
51
+ allById: new Map(allProjects.map((p) => [p.id, p])),
52
+ memberByProjectId,
53
+ orgMemberByOrgId
54
+ };
55
+ }
56
+ /**
57
+ * `opts.projectId` narrows to one project; a caller who cannot view it gets an
58
+ * empty page rather than a 403, so this never reveals that a project exists.
59
+ */
60
+ export async function listVisibleDefinitions(ctx, opts = {}, deps) {
61
+ const { projectId, ...listOpts } = opts;
62
+ const { projects } = await resolveAccessibleProjects(ctx, deps);
63
+ const scoped = projectId ? projects.filter((p) => p.id === projectId) : projects;
64
+ // An empty id set matches nothing, which is what a caller with no visible
65
+ // projects should get — not an unfiltered listing.
66
+ const page = await deps.definitionMeta.list(ctx, {
67
+ ...listOpts,
68
+ projectIds: scoped.map((p) => p.id)
69
+ });
70
+ return { items: page.items, nextCursor: page.nextCursor, projects: scoped };
71
+ }
72
+ /**
73
+ * One definition, or `null` when the caller cannot view its project.
74
+ *
75
+ * Returns `null` rather than throwing 403 so callers surface a 404: a public
76
+ * API that answers "forbidden" for a guid the caller cannot see turns
77
+ * `/definitions/{guid}` into a cross-tenant existence oracle.
78
+ */
79
+ export async function getVisibleDefinition(ctx, guid, deps) {
80
+ const meta = deps.definitionMeta;
81
+ const projectStore = deps.projects;
82
+ const record = await meta.get(ctx, guid);
83
+ if (!record)
84
+ return null;
85
+ const project = await projectStore.getProject(SYSTEM_CONTEXT, record.projectId);
86
+ if (!project)
87
+ return null;
88
+ const [orgMembers, projectMembers, grants] = await Promise.all([
89
+ deps.orgs.getOrgMembersFor(SYSTEM_CONTEXT, [project.orgId], ctx.userId),
90
+ projectStore.getProjectMembersFor(SYSTEM_CONTEXT, [project.id], ctx.userId),
91
+ project.visibility === 'platform'
92
+ ? deps.platformProjectGrants.listByProject(SYSTEM_CONTEXT, project.id)
93
+ : Promise.resolve([])
94
+ ]);
95
+ const allowed = canView(projectAccessInputFromRowsWith({ deps }, ctx, project, {
96
+ member: projectMembers.get(project.id) ?? null,
97
+ orgMember: orgMembers.get(project.orgId) ?? null,
98
+ platformGrants: grants
99
+ }));
100
+ return allowed ? record : null;
101
+ }
102
+ /**
103
+ * One version of a definition the caller can view, including its cached
104
+ * `schema`. `null` when the definition is invisible, the version is missing, or
105
+ * the version belongs to a different definition — a caller must not be able to
106
+ * read an arbitrary version by pairing it with a guid they can see.
107
+ */
108
+ export async function loadVisibleVersion(ctx, guid, versionId, deps) {
109
+ const record = await getVisibleDefinition(ctx, guid, deps);
110
+ if (!record)
111
+ return null;
112
+ const version = await deps.definitionMeta.getVersion(ctx, versionId);
113
+ if (!version || version.definitionId !== record.guid)
114
+ return null;
115
+ return version;
116
+ }
117
+ //# sourceMappingURL=visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.js","sourceRoot":"","sources":["../../src/definitions/visibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Version handlers: list, upload, read, delete, and a version's cached schema.
3
+ *
4
+ * Reads resolve through `getVisibleDefinition` / `loadVisibleVersion`, which
5
+ * return `null` rather than throwing, so an invisible definition answers 404.
6
+ * A 403 anywhere here would confirm a guid exists across a tenant boundary.
7
+ */
8
+ import type { ApiHandler } from '../api/index.js';
9
+ export declare const listVersions: ApiHandler;
10
+ /** Upload a new version. Advances `draft`; `live` is unchanged until publish. */
11
+ export declare const uploadVersion: ApiHandler;
12
+ /**
13
+ * Version metadata. `schema` is excluded and served by the `/schema`
14
+ * sub-resource, so a version read never carries a several-hundred-KB blob.
15
+ */
16
+ export declare const getVersion: ApiHandler;
17
+ /**
18
+ * Delete an old version. The store throws 409 if the version is currently
19
+ * referenced by `liveVersionId` or `draftVersionId` — repoint first.
20
+ */
21
+ export declare const deleteVersion: ApiHandler;
22
+ /**
23
+ * The UI schema cached on this version at upload. Reads what is stored — no
24
+ * Rhino.Compute round-trip, unlike the pre-upload `POST /api/v1/compute/schema`.
25
+ *
26
+ * Schemas belong to a version, so this is the canonical location;
27
+ * `/definitions/{guid}/schema` is an alias for the live one.
28
+ */
29
+ export declare const getVersionSchema: ApiHandler;
30
+ //# sourceMappingURL=definitionVersions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitionVersions.d.ts","sourceRoot":"","sources":["../../src/handlers/definitionVersions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAYlD,eAAO,MAAM,YAAY,EAAE,UAa1B,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,aAAa,EAAE,UAgC3B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,UASxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,UAO3B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAW9B,CAAC"}