@selvajs/platform 0.14.2 → 0.15.0-beta.1

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 (196) hide show
  1. package/LICENSE +1 -1
  2. package/dist/auth.d.ts +60 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +12 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/compute.d.ts +47 -0
  7. package/dist/compute.d.ts.map +1 -0
  8. package/dist/compute.js +11 -0
  9. package/dist/compute.js.map +1 -0
  10. package/dist/computeServer/index.d.ts +2 -0
  11. package/dist/computeServer/index.d.ts.map +1 -1
  12. package/dist/computeServer/index.js +6 -0
  13. package/dist/computeServer/index.js.map +1 -1
  14. package/dist/computeServer/interface.d.ts +10 -0
  15. package/dist/computeServer/interface.d.ts.map +1 -1
  16. package/dist/computeServer/secrets.d.ts +53 -0
  17. package/dist/computeServer/secrets.d.ts.map +1 -0
  18. package/dist/computeServer/secrets.js +67 -0
  19. package/dist/computeServer/secrets.js.map +1 -0
  20. package/dist/context.d.ts +7 -0
  21. package/dist/context.d.ts.map +1 -1
  22. package/dist/context.js +13 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/data/interface.d.ts +33 -0
  25. package/dist/data/interface.d.ts.map +1 -1
  26. package/dist/definitions/interface.d.ts +18 -0
  27. package/dist/definitions/interface.d.ts.map +1 -1
  28. package/dist/definitions/schemas.d.ts +1 -0
  29. package/dist/definitions/schemas.d.ts.map +1 -1
  30. package/dist/definitions/schemas.js +3 -0
  31. package/dist/definitions/schemas.js.map +1 -1
  32. package/dist/definitions/service.d.ts +62 -0
  33. package/dist/definitions/service.d.ts.map +1 -0
  34. package/dist/definitions/service.js +149 -0
  35. package/dist/definitions/service.js.map +1 -0
  36. package/dist/definitions/types.d.ts +24 -1
  37. package/dist/definitions/types.d.ts.map +1 -1
  38. package/dist/definitions.d.ts +146 -0
  39. package/dist/definitions.d.ts.map +1 -0
  40. package/dist/definitions.js +23 -0
  41. package/dist/definitions.js.map +1 -0
  42. package/dist/errors/interface.d.ts +44 -0
  43. package/dist/errors/interface.d.ts.map +1 -0
  44. package/dist/errors/interface.js +9 -0
  45. package/dist/errors/interface.js.map +1 -0
  46. package/dist/events/interface.d.ts +23 -0
  47. package/dist/events/interface.d.ts.map +1 -1
  48. package/dist/events/interface.js.map +1 -1
  49. package/dist/index.d.ts +14 -5
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +7 -2
  52. package/dist/index.js.map +1 -1
  53. package/dist/invites/schemas.d.ts +24 -0
  54. package/dist/invites/schemas.d.ts.map +1 -0
  55. package/dist/invites/schemas.js +17 -0
  56. package/dist/invites/schemas.js.map +1 -0
  57. package/dist/metrics/interface.d.ts +6 -0
  58. package/dist/metrics/interface.d.ts.map +1 -1
  59. package/dist/metrics/interface.js.map +1 -1
  60. package/dist/organizations/index.d.ts +4 -3
  61. package/dist/organizations/index.d.ts.map +1 -1
  62. package/dist/organizations/index.js +2 -1
  63. package/dist/organizations/index.js.map +1 -1
  64. package/dist/organizations/interface.d.ts +1 -1
  65. package/dist/organizations/interface.d.ts.map +1 -1
  66. package/dist/organizations/paths.d.ts +22 -0
  67. package/dist/organizations/paths.d.ts.map +1 -0
  68. package/dist/organizations/paths.js +43 -0
  69. package/dist/organizations/paths.js.map +1 -0
  70. package/dist/organizations/schemas.d.ts +10 -0
  71. package/dist/organizations/schemas.d.ts.map +1 -1
  72. package/dist/organizations/schemas.js +6 -0
  73. package/dist/organizations/schemas.js.map +1 -1
  74. package/dist/organizations/types.d.ts +10 -1
  75. package/dist/organizations/types.d.ts.map +1 -1
  76. package/dist/organizations.d.ts +93 -0
  77. package/dist/organizations.d.ts.map +1 -0
  78. package/dist/organizations.js +14 -0
  79. package/dist/organizations.js.map +1 -0
  80. package/dist/solveCache/interface.d.ts +86 -0
  81. package/dist/solveCache/interface.d.ts.map +1 -0
  82. package/dist/solveCache/interface.js +12 -0
  83. package/dist/solveCache/interface.js.map +1 -0
  84. package/dist/storage/assetClasses.d.ts +59 -0
  85. package/dist/storage/assetClasses.d.ts.map +1 -0
  86. package/dist/storage/assetClasses.js +86 -0
  87. package/dist/storage/assetClasses.js.map +1 -0
  88. package/dist/storage/cacheBust.d.ts +21 -0
  89. package/dist/storage/cacheBust.d.ts.map +1 -0
  90. package/dist/storage/cacheBust.js +29 -0
  91. package/dist/storage/cacheBust.js.map +1 -0
  92. package/dist/storage/image.d.ts +6 -1
  93. package/dist/storage/image.d.ts.map +1 -1
  94. package/dist/storage/image.js +8 -3
  95. package/dist/storage/image.js.map +1 -1
  96. package/dist/storage/index.d.ts +3 -0
  97. package/dist/storage/index.d.ts.map +1 -1
  98. package/dist/storage/index.js +2 -0
  99. package/dist/storage/index.js.map +1 -1
  100. package/dist/testing/adapterSuite.d.ts +33 -0
  101. package/dist/testing/adapterSuite.d.ts.map +1 -0
  102. package/dist/testing/adapterSuite.js +166 -0
  103. package/dist/testing/adapterSuite.js.map +1 -0
  104. package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
  105. package/dist/testing/suites/definitionStoreSuite.js +87 -0
  106. package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
  107. package/dist/testing/suites/eventSinkSuite.js +1 -0
  108. package/dist/testing/suites/eventSinkSuite.js.map +1 -1
  109. package/dist/testing/suites/orgStoreSuite.d.ts.map +1 -1
  110. package/dist/testing/suites/orgStoreSuite.js +12 -0
  111. package/dist/testing/suites/orgStoreSuite.js.map +1 -1
  112. package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
  113. package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
  114. package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
  115. package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
  116. package/dist/testing/suites/runner.d.ts +21 -0
  117. package/dist/testing/suites/runner.d.ts.map +1 -0
  118. package/dist/testing/suites/runner.js +7 -0
  119. package/dist/testing/suites/runner.js.map +1 -0
  120. package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
  121. package/dist/testing/suites/storageProviderSuite.js +18 -0
  122. package/dist/testing/suites/storageProviderSuite.js.map +1 -1
  123. package/package.json +9 -24
  124. package/src/access/index.ts +0 -23
  125. package/src/access/rules.ts +0 -276
  126. package/src/auth/index.ts +0 -8
  127. package/src/auth/interface.ts +0 -279
  128. package/src/auth/types.ts +0 -32
  129. package/src/bindings/index.ts +0 -2
  130. package/src/bindings/interface.ts +0 -61
  131. package/src/computeServer/index.ts +0 -17
  132. package/src/computeServer/interface.ts +0 -62
  133. package/src/computeServer/types.ts +0 -74
  134. package/src/computeServer/utils.ts +0 -112
  135. package/src/config.ts +0 -99
  136. package/src/context.ts +0 -66
  137. package/src/data/index.ts +0 -1
  138. package/src/data/interface.ts +0 -70
  139. package/src/definitions/index.ts +0 -26
  140. package/src/definitions/interface.ts +0 -78
  141. package/src/definitions/paths.ts +0 -31
  142. package/src/definitions/schemas.ts +0 -64
  143. package/src/definitions/types.ts +0 -111
  144. package/src/errors.ts +0 -16
  145. package/src/events/audit.ts +0 -67
  146. package/src/events/index.ts +0 -9
  147. package/src/events/interface.ts +0 -102
  148. package/src/index.ts +0 -219
  149. package/src/invites/index.ts +0 -2
  150. package/src/invites/interface.ts +0 -38
  151. package/src/invites/types.ts +0 -39
  152. package/src/metrics/interface.ts +0 -86
  153. package/src/organizations/index.ts +0 -14
  154. package/src/organizations/interface.ts +0 -68
  155. package/src/organizations/schemas.ts +0 -52
  156. package/src/organizations/types.ts +0 -31
  157. package/src/pagination.ts +0 -45
  158. package/src/permissions/index.ts +0 -3
  159. package/src/permissions/interface.ts +0 -60
  160. package/src/permissions/types.ts +0 -21
  161. package/src/platformProjects/index.ts +0 -3
  162. package/src/platformProjects/interface.ts +0 -27
  163. package/src/platformProjects/schemas.ts +0 -13
  164. package/src/platformProjects/types.ts +0 -22
  165. package/src/projects/index.ts +0 -4
  166. package/src/projects/interface.ts +0 -58
  167. package/src/projects/schemas.ts +0 -28
  168. package/src/projects/types.ts +0 -36
  169. package/src/shareLinks/index.ts +0 -5
  170. package/src/shareLinks/interface.ts +0 -35
  171. package/src/shareLinks/schemas.ts +0 -21
  172. package/src/shareLinks/types.ts +0 -34
  173. package/src/storage/image.ts +0 -77
  174. package/src/storage/index.ts +0 -9
  175. package/src/storage/interface.ts +0 -27
  176. package/src/testing/index.ts +0 -47
  177. package/src/testing/suites/authProviderSuite.ts +0 -207
  178. package/src/testing/suites/computeServerStoreSuite.ts +0 -195
  179. package/src/testing/suites/definitionStoreSuite.ts +0 -585
  180. package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
  181. package/src/testing/suites/eventSinkSuite.ts +0 -379
  182. package/src/testing/suites/helpers.ts +0 -87
  183. package/src/testing/suites/inviteStoreSuite.ts +0 -143
  184. package/src/testing/suites/orgStoreSuite.ts +0 -412
  185. package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
  186. package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
  187. package/src/testing/suites/projectStoreSuite.ts +0 -381
  188. package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
  189. package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
  190. package/src/testing/suites/storageProviderSuite.ts +0 -175
  191. package/src/testing/suites/userProfileStoreSuite.ts +0 -134
  192. package/src/userProfile/index.ts +0 -3
  193. package/src/userProfile/interface.ts +0 -48
  194. package/src/userProfile/types.ts +0 -24
  195. package/src/utils/audit.ts +0 -46
  196. package/src/utils/index.ts +0 -2
@@ -1,279 +0,0 @@
1
- import type { AuthUser, LoginResult, UserManagementResult } from './types.js';
2
- import type { ListOptions, Page } from '../pagination.js';
3
-
4
- /**
5
- * Optional password-based authentication surface. Implemented by providers
6
- * that own credentials (Local, Supabase Auth). OIDC-only providers leave
7
- * `IAuthProvider.passwordAuth` undefined.
8
- */
9
- export interface IPasswordAuth {
10
- /**
11
- * Verify credentials. Discriminated union — callers MUST `switch (result.kind)`,
12
- * never treat truthiness as authenticated.
13
- */
14
- verifyLogin(email: string, password: string): Promise<LoginResult>;
15
-
16
- /**
17
- * Admin-initiated user creation. Pure identity — callers grant platform
18
- * permissions separately via `IPlatformPermissionStore.set`.
19
- */
20
- createUserWithPassword(email: string, password: string): Promise<AuthUser>;
21
-
22
- /** Self-service registration. Return null when self-registration is disabled. */
23
- registerUser?(email: string, password: string): Promise<AuthUser | null>;
24
- }
25
-
26
- /**
27
- * Optional OAuth surface. Implemented by providers that broker OAuth flows
28
- * (Supabase Auth, future Eterna/Auth0/etc.). Local credential-only providers
29
- * leave `IAuthProvider.oauth` undefined.
30
- *
31
- * Lifecycle is the standard authorization-code flow:
32
- *
33
- * 1. Browser hits `/auth/{provider}/start?provider=google` → server calls
34
- * `getOAuthAuthorizationUrl` and 303s to the result.
35
- * 2. IdP redirects back to `/auth/{provider}/callback?code=...` →
36
- * server calls `exchangeOAuthCode` to mint the session pair.
37
- * 3. When the access token expires, the session-refresh middleware in
38
- * `hooks.server.ts` calls `refreshSession` with the stored refresh
39
- * token to swap for a fresh pair without bouncing the user.
40
- *
41
- * Each method returns `null` (rather than throwing) on irrecoverable
42
- * credential failures — the route layer translates `null` into the right
43
- * HTTP shape (401 for refresh, login redirect for callback).
44
- */
45
- export interface IOAuthAuth {
46
- /**
47
- * The OAuth providers this adapter is configured to broker. Returned as
48
- * lowercased identifiers ("google", "github", …) — the adapter reads its
49
- * own configuration source (env var, dashboard, hard-coded list) and
50
- * decides what's available. Empty array when none are wired.
51
- *
52
- * Driving adapters (HTTP routes, UI) call this instead of reading
53
- * provider-specific env vars directly — keeps the provider name out of
54
- * `selva`.
55
- */
56
- listProviders(): readonly string[];
57
-
58
- /**
59
- * Build the IdP authorization URL the browser should be redirected to.
60
- * `redirectTo` is the full callback URL — must be the route that handles
61
- * the `?code=...` exchange. Provider-name validation is the caller's job;
62
- * passing an unconfigured provider may surface as an upstream error.
63
- */
64
- getOAuthAuthorizationUrl(
65
- provider: 'google' | 'github' | 'azure' | 'gitlab',
66
- redirectTo: string
67
- ): Promise<string>;
68
-
69
- /**
70
- * Exchange an authorization code for a session. Returns the resolved
71
- * identity and an access/refresh token pair. `null` when the code is
72
- * invalid, expired, or already redeemed — callers MUST treat null as
73
- * "send the user back to /login".
74
- */
75
- exchangeOAuthCode(code: string): Promise<{
76
- user: AuthUser;
77
- sessionToken: string;
78
- refreshToken: string;
79
- } | null>;
80
-
81
- /**
82
- * Swap a refresh token for a fresh access/refresh pair. Returns null on
83
- * irrecoverable failure (revoked, expired, signed by a different secret);
84
- * the session-refresh middleware treats null as "force re-login" by
85
- * clearing the refresh cookie.
86
- */
87
- refreshSession(refreshToken: string): Promise<{
88
- sessionToken: string;
89
- refreshToken: string;
90
- } | null>;
91
- }
92
-
93
- /**
94
- * Optional passwordless email surface — "type your email, click the link in
95
- * your inbox, you're in." Implemented by providers that broker email delivery
96
- * + token verification (Supabase Auth, future Eterna ID, …). Adapters that
97
- * can't send mail or don't model this flow leave `IAuthProvider.emailLink`
98
- * undefined.
99
- *
100
- * Lifecycle is two phases mirroring OAuth's start/callback split:
101
- *
102
- * 1. User submits an email → server calls `sendMagicLink(email,
103
- * callbackUrl)`. The adapter delivers a link to that callback URL with
104
- * whatever verification token shape it uses (Supabase: `?token_hash=…
105
- * &type=magiclink`).
106
- * 2. User clicks the link, browser hits the callback route → server calls
107
- * `verifyMagicLink(rawTokenFromUrl)` to mint a session.
108
- *
109
- * The driving adapter never inspects the token itself — it forwards the raw
110
- * URL string. Adapter-specific token shapes stay inside the adapter.
111
- */
112
- export interface IEmailLinkAuth {
113
- /**
114
- * Send a magic-link email. `callbackUrl` is the absolute URL the
115
- * adapter should embed in the link — must point at the route that
116
- * handles `verifyMagicLink`.
117
- *
118
- * Returns `{ ok: true }` on success. On adapter-classified failures
119
- * (rate limit, signup disabled, invalid email) returns
120
- * `{ ok: false, reason }` so the route can render a user-friendly
121
- * message without exposing provider internals. Network/unknown errors
122
- * still throw.
123
- */
124
- sendMagicLink(
125
- email: string,
126
- callbackUrl: string
127
- ): Promise<
128
- { ok: true } | { ok: false; reason: 'rate_limited' | 'signup_disabled' | 'invalid_email' }
129
- >;
130
-
131
- /**
132
- * Verify the token the IdP sent the user. `rawCallbackUrl` is the full
133
- * URL the user landed on (or an opaque adapter-specific token string —
134
- * adapters document which); the adapter pulls whatever fields it needs.
135
- *
136
- * Returns `null` for invalid/expired/already-redeemed tokens — callers
137
- * MUST treat null as "send the user back to /login". `refreshToken` is
138
- * present only for adapters that issue refresh tokens for email-link
139
- * sessions (Supabase does); omit otherwise.
140
- */
141
- verifyMagicLink(rawCallbackUrl: string): Promise<{
142
- user: AuthUser;
143
- sessionToken: string;
144
- refreshToken?: string;
145
- } | null>;
146
- }
147
-
148
- /**
149
- * Optional forward-proxy authentication surface. Implemented by providers
150
- * that derive identity from signals injected by a trusted upstream reverse
151
- * proxy (HTTP headers, mTLS, etc.) — e.g. Caddy `forward_auth`,
152
- * oauth2-proxy, Authelia, Pomerium, Traefik forward-auth.
153
- *
154
- * ⚠ TRUST BOUNDARY — READ BEFORE IMPLEMENTING ⚠
155
- *
156
- * The signals this surface consumes are NOT verified cryptographically by
157
- * Selva. They are trusted because the deployment runs the app behind a
158
- * proxy that:
159
- *
160
- * 1. Authenticates the user against an upstream IdP, AND
161
- * 2. STRIPS any client-supplied copies of the trusted headers from
162
- * inbound requests before adding its own, AND
163
- * 3. Is the ONLY network path that can reach the app process (bind to
164
- * 127.0.0.1, firewall the app port, or a private socket).
165
- *
166
- * If any condition fails, an attacker can spoof the headers and become
167
- * anyone. **Each implementer of this interface MUST ship a README that
168
- * states, loudly, the exact proxy configuration required.** The platform
169
- * layer cannot verify this — it is the provider's contract with its
170
- * operator.
171
- *
172
- * Lifecycle: `identifyFromHeaders` is called from `hooks.server.ts` on
173
- * every authed request whose session cookie is missing or invalid. It MUST
174
- * be cheap — a header read plus at most one indexed lookup. No network
175
- * calls, no synchronous disk scans of unbounded size.
176
- */
177
- export interface IProxyAuth {
178
- /**
179
- * Identify the caller from trusted upstream-proxy signals on the
180
- * incoming request. Return `null` for "not identified" (header missing,
181
- * UPN not allowlisted, account disabled). MUST NOT throw for absent
182
- * headers — those are the normal anonymous case.
183
- *
184
- * On first identification of a previously-allowlisted user, the
185
- * implementation MAY materialize stored fields (display name, email)
186
- * from the same headers. Auto-creating arbitrary users from headers
187
- * is forbidden: the allowlist IS the security boundary.
188
- */
189
- identifyFromHeaders(headers: Headers): Promise<AuthUser | null>;
190
-
191
- /**
192
- * Diagnostic helper. Returns true iff none of the identity headers this
193
- * provider trusts arrived on the request. The hook layer uses this on
194
- * `/login` misses to distinguish "proxy never reached us" from "proxy is
195
- * here but didn't recognise the user" so operators can tell silent
196
- * forward-auth misconfigurations apart from real allowlist gaps without
197
- * leaking the trusted header names to end users.
198
- */
199
- hasNoIdentityHeaders(headers: Headers): boolean;
200
-
201
- /**
202
- * Diagnostic helper. Returns the identity-header names this provider is
203
- * configured to read. Used purely for log output — never surfaced to end
204
- * users.
205
- */
206
- readonly configuredHeaderNames: readonly string[];
207
- }
208
-
209
- /**
210
- * Authentication provider — identity verification only. Profile state lives
211
- * in `IUserProfileStore`, platform permissions in `IPlatformPermissionStore`,
212
- * password ops in optional `passwordAuth`, OAuth in optional `oauth`.
213
- *
214
- * Methods here do NOT take a `RequestContext` — the provider produces the
215
- * identity used to *build* the context.
216
- */
217
- export interface IAuthProvider {
218
- /** Display name shown in admin UI (e.g. "Local", "Microsoft Entra ID"). */
219
- readonly name: string;
220
-
221
- /** Present for credential-owning providers; undefined for OIDC-only. */
222
- readonly passwordAuth?: IPasswordAuth;
223
-
224
- /** Present for providers that broker OAuth flows; undefined otherwise. */
225
- readonly oauth?: IOAuthAuth;
226
-
227
- /**
228
- * Present for providers that send sign-in links over email (Supabase Auth,
229
- * future Eterna ID). Undefined otherwise.
230
- */
231
- readonly emailLink?: IEmailLinkAuth;
232
-
233
- /**
234
- * Present for providers that derive identity from a trusted upstream
235
- * proxy (forward-auth headers, mTLS, etc.). Undefined for credential
236
- * or OIDC-broker providers. See `IProxyAuth` for the trust contract.
237
- *
238
- * When set, the platform treats sign-out as the proxy/IdP's job and
239
- * does not render a logout button — Selva has no session to destroy.
240
- */
241
- readonly proxyAuth?: IProxyAuth;
242
-
243
- /**
244
- * Verify a token (session cookie, JWT, ID token, etc.). Returns the user
245
- * or null when invalid/expired.
246
- */
247
- verifyToken(token: string): Promise<AuthUser | null>;
248
-
249
- getUser(id: string): Promise<AuthUser | null>;
250
-
251
- /** Returns null if the provider does not support user management. */
252
- listUsers(opts?: ListOptions): Promise<Page<AuthUser> | null>;
253
-
254
- /**
255
- * Optional: allowlist a user without a password (OAuth providers). Pure
256
- * identity — callers grant permissions via `IPlatformPermissionStore.set`.
257
- */
258
- createUser?(email: string): Promise<AuthUser>;
259
-
260
- /**
261
- * Delete a user identity. The sole-`instance_admin` invariant is NOT
262
- * enforced here — callers MUST consult
263
- * `IPlatformPermissionStore.countInstanceAdminsExcluding(id)` first and
264
- * surface `'last_admin'` themselves.
265
- */
266
- deleteUser(id: string): Promise<UserManagementResult>;
267
-
268
- /**
269
- * Disable a user. Sessions become invalid; identity is preserved (preferred
270
- * over deletion). Same invariant rules as `deleteUser`.
271
- */
272
- disableUser(id: string): Promise<UserManagementResult>;
273
-
274
- /**
275
- * Stamp `lastLoginAt`. Best-effort — failure MUST NOT block auth.
276
- * Adapters MAY debounce (e.g. skip if existing timestamp < 60s old).
277
- */
278
- touchLastLogin?(id: string): Promise<void>;
279
- }
package/src/auth/types.ts DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * Identity record from an auth provider. Pure identity — no Selva-specific
3
- * authorization or profile state. Platform permissions live in
4
- * `IPlatformPermissionStore`; profile state lives in `IUserProfileStore`.
5
- */
6
- export interface AuthUser {
7
- id: string;
8
- email?: string;
9
- /** Provider-specific only — never PII, credentials, or tokens. */
10
- metadata?: Record<string, unknown>;
11
- createdAt?: string;
12
- /** Provider-updated only; never client-writable. */
13
- lastLoginAt?: string;
14
- /** When true, `verifyToken` and `verifyLogin` MUST fail. Prefer over deletion. */
15
- disabled?: boolean;
16
- }
17
-
18
- /**
19
- * - `ok` — succeeded
20
- * - `not_found` — target user doesn't exist
21
- * - `not_supported` — provider doesn't implement user management
22
- * - `last_admin` — refused; would leave zero `instance_admin` users
23
- */
24
- export type UserManagementResult = 'ok' | 'not_found' | 'not_supported' | 'last_admin';
25
-
26
- /**
27
- * - `success` — credentials valid; includes session token the provider minted.
28
- * - `failed` — credentials invalid, user disabled, or rate-limited.
29
- */
30
- export type LoginResult =
31
- | { kind: 'success'; user: AuthUser; sessionToken: string }
32
- | { kind: 'failed'; reason?: 'invalid_credentials' | 'disabled' | 'rate_limited' };
@@ -1,2 +0,0 @@
1
- export type { IBindingResolver } from './interface.js';
2
- export { NoopBindingResolver } from './interface.js';
@@ -1,61 +0,0 @@
1
- /**
2
- * Server-side resolver for inputs marked as `source: { kind: 'server', key }`
3
- * in the schema. The host (selva app or a fork) supplies an implementation;
4
- * the platform stays domain-agnostic.
5
- *
6
- * Keys are opaque strings. The host defines the namespace
7
- * (e.g. 'capture.geometry', 'parcel.boundary') and the resolver interprets it
8
- * however it likes — typically as an address into the host's own domain store.
9
- *
10
- * Status: preliminary. The interface is expected to refine once we wire a
11
- * real binding end-to-end (see docs/upstream-binding-prep PR plan). Current
12
- * shape is the minimum needed to slot a host implementation behind
13
- * `SelvaConfig.bindingResolver`.
14
- */
15
-
16
- import type { RequestContext } from '../context.js';
17
-
18
- export interface IBindingResolver {
19
- /**
20
- * Resolve a batch of bound keys in one call.
21
- *
22
- * - `keys` is what the schema author wrote (`source.key`) — opaque to the
23
- * platform, interpreted by the resolver. The author writes keys at
24
- * design time, so the key should describe WHAT attribute to read, not
25
- * WHICH entity to read it from.
26
- * - `scope` is set by the calling route at solve time — opaque to the
27
- * platform, supplies the "which entity" anchor that varies per
28
- * request (capture id, parcel id, custom struct). Optional because
29
- * not every host needs it.
30
- *
31
- * Implementations should return ONLY the keys they successfully
32
- * resolved — absent entries in the returned map signal "missing" to the
33
- * caller, who then decides what to do (e.g. error the solve, or fall back
34
- * to the input's `default`).
35
- *
36
- * Do not throw for individual missing keys. Throw only for resolver-wide
37
- * failures (DB unreachable, configuration error). Per-key "not found" is
38
- * normal and is signalled by omitting the entry.
39
- *
40
- * Batching is intentional: a single solve often binds multiple inputs
41
- * from one domain object, and the resolver should be able to do that
42
- * with a single DB round-trip.
43
- */
44
- resolve(
45
- ctx: RequestContext,
46
- keys: readonly string[],
47
- scope?: unknown
48
- ): Promise<Map<string, unknown>>;
49
- }
50
-
51
- /**
52
- * Default resolver used when the host hasn't configured one. Returns an
53
- * empty map for any request, which makes every bound input fail at solve
54
- * time — a loud signal that a resolver needs to be wired up. Drop-in replace
55
- * via `SelvaConfig.bindingResolver` once the host has a real implementation.
56
- */
57
- export class NoopBindingResolver implements IBindingResolver {
58
- async resolve(): Promise<Map<string, unknown>> {
59
- return new Map();
60
- }
61
- }
@@ -1,17 +0,0 @@
1
- export type {
2
- ComputeServerConfig,
3
- ComputeConfig,
4
- PlatformComputeServer,
5
- OrgComputeServer
6
- } from './types.js';
7
- export { isPlatformServer, isOrgServer } from './types.js';
8
- export type { IComputeServerStore } from './interface.js';
9
- export type { ResolveOptions } from './utils.js';
10
- export {
11
- serversVisibleTo,
12
- defaultServerIdFor,
13
- resolveServerForOrg,
14
- findServerById,
15
- platformServers,
16
- orgServersFor
17
- } from './utils.js';
@@ -1,62 +0,0 @@
1
- import type { RequestContext } from '../context.js';
2
- import type { ComputeConfig, ComputeServerConfig } from './types.js';
3
-
4
- /**
5
- * Compute-server configuration. Architecture spec §4.8.
6
- *
7
- * One config doc holds *all* servers (platform + org-private) plus the
8
- * global `defaultServerId` and per-org `orgDefaults` map. Authorization is
9
- * the route layer's job — `manage_compute` for platform-server mutations,
10
- * `manage_org_compute` (gated by the `ALLOW_ORG_COMPUTE_OVERRIDE` flag) for
11
- * org-private mutations and `orgDefaults[orgId]`.
12
- */
13
- export interface IComputeServerStore {
14
- /**
15
- * Read the full config. Callers (resolver, page loaders, route handlers)
16
- * apply the visibility predicate from `serversVisibleTo` themselves —
17
- * the store does not pre-filter, because the same row set is needed at
18
- * different scopes (admin manage vs. org manage vs. solve).
19
- */
20
- getConfig(ctx: RequestContext): Promise<ComputeConfig>;
21
-
22
- /**
23
- * Replace the entire platform-server set + global `defaultServerId`.
24
- * Org-private rows and `orgDefaults` are left untouched.
25
- */
26
- savePlatformServers(
27
- ctx: RequestContext,
28
- servers: ComputeServerConfig[],
29
- defaultServerId: string | undefined
30
- ): Promise<void>;
31
-
32
- /**
33
- * Replace the org-private server set for `orgId` and (optionally) update
34
- * `orgDefaults[orgId]`. Platform rows are left untouched.
35
- *
36
- * Pass `defaultServerId: null` to clear the org's override; `undefined`
37
- * leaves the existing value alone.
38
- */
39
- saveOrgServers(
40
- ctx: RequestContext,
41
- orgId: string,
42
- servers: ComputeServerConfig[],
43
- defaultServerId?: string | null
44
- ): Promise<void>;
45
-
46
- /**
47
- * Set or clear `orgDefaults[orgId]`. The route layer is responsible for
48
- * verifying the chosen server is visible to the org before calling this.
49
- *
50
- * `serverId === null` clears the override.
51
- */
52
- setOrgDefault(ctx: RequestContext, orgId: string, serverId: string | null): Promise<void>;
53
-
54
- /**
55
- * Hard-delete this org's compute rows: org-private servers, the
56
- * `orgDefaults[orgId]` entry, and any references to this org inside
57
- * platform servers' `sharedWith` allowlists. No-op when none exist.
58
- * Called from `deleteOrg` so soft-deleting an org does not leave its
59
- * compute config behind.
60
- */
61
- deleteByOrg(ctx: RequestContext, orgId: string): Promise<void>;
62
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * Compute servers belong to one of two scopes — never both.
3
- *
4
- * - **Platform server** (`scope: 'platform'`) — created by `manage_compute`.
5
- * `sharedWith` controls which orgs see it. The single global
6
- * `defaultServerId` (in {@link ComputeConfig}) must reference a platform
7
- * server and is *always* visible to every org regardless of `sharedWith`.
8
- *
9
- * - **Org-private server** (`scope: 'org'`) — created by an org owner/admin
10
- * with `manage_org_compute`. Visible only to its `ownerOrgId`. Gated by
11
- * the `ALLOW_ORG_COMPUTE_OVERRIDE` platform flag.
12
- *
13
- * Architecture spec §4.8.
14
- */
15
- export type ComputeServerConfig = PlatformComputeServer | OrgComputeServer;
16
-
17
- interface ComputeServerCommon {
18
- id: string;
19
- label: string;
20
- /** Base URL of the Rhino.Compute instance. */
21
- serverUrl: string;
22
- /** Sent as `RhinoComputeKey` header. */
23
- apiKey?: string;
24
- /** Default: 30000. */
25
- timeoutMs?: number;
26
- /** Default: 0. */
27
- retryCount?: number;
28
- }
29
-
30
- export interface PlatformComputeServer extends ComputeServerCommon {
31
- scope: 'platform';
32
- /**
33
- * Which orgs can see this server in pickers / use it for solves.
34
- * - `'all'` — every org. Default in `tenancy: 'single'`.
35
- * - `string[]` — explicit org-id allowlist. `[]` = dormant
36
- * (admin-only) unless this server is the global `defaultServerId`,
37
- * which is always usable everywhere.
38
- */
39
- sharedWith: 'all' | string[];
40
- }
41
-
42
- export interface OrgComputeServer extends ComputeServerCommon {
43
- scope: 'org';
44
- /** The org that owns this server. Visible only to members of this org. */
45
- ownerOrgId: string;
46
- }
47
-
48
- export interface ComputeConfig {
49
- servers: ComputeServerConfig[];
50
- /**
51
- * Global default. Must reference a platform server. Always usable by
52
- * every org regardless of that server's `sharedWith` — this is the
53
- * "baseline server any user uses" floor.
54
- */
55
- defaultServerId?: string;
56
- /**
57
- * Per-org default override. `orgDefaults[orgId]` must reference a server
58
- * that is visible to `orgId` (a platform server with `orgId` in
59
- * `sharedWith` or `'all'`, or an org server owned by `orgId`).
60
- */
61
- orgDefaults?: Record<string, string>;
62
- }
63
-
64
- // ============================================================================
65
- // Type guards
66
- // ============================================================================
67
-
68
- export function isPlatformServer(s: ComputeServerConfig): s is PlatformComputeServer {
69
- return s.scope === 'platform';
70
- }
71
-
72
- export function isOrgServer(s: ComputeServerConfig): s is OrgComputeServer {
73
- return s.scope === 'org';
74
- }
@@ -1,112 +0,0 @@
1
- import {
2
- isOrgServer,
3
- isPlatformServer,
4
- type ComputeConfig,
5
- type ComputeServerConfig
6
- } from './types.js';
7
-
8
- /**
9
- * Servers an org can see in pickers and use for solves. Architecture spec §4.8.
10
- *
11
- * = platform servers shared with this org (or `'all'`, or the global default)
12
- * ∪ org-private servers owned by this org
13
- *
14
- * The global default is always included — it's the floor of the system.
15
- */
16
- export function serversVisibleTo(
17
- config: ComputeConfig,
18
- orgId: string | null | undefined
19
- ): ComputeServerConfig[] {
20
- return config.servers.filter((s) => {
21
- if (isPlatformServer(s)) {
22
- if (s.id === config.defaultServerId) return true;
23
- if (s.sharedWith === 'all') return true;
24
- if (orgId && s.sharedWith.includes(orgId)) return true;
25
- return false;
26
- }
27
- // Org-private — visible only to its owner org.
28
- return !!orgId && s.ownerOrgId === orgId;
29
- });
30
- }
31
-
32
- /**
33
- * Resolve the default server id for an org. Per-org override → global default.
34
- * Returns `undefined` if neither is set or the chosen id is not visible.
35
- */
36
- export function defaultServerIdFor(
37
- config: ComputeConfig,
38
- orgId: string | null | undefined
39
- ): string | undefined {
40
- const visible = serversVisibleTo(config, orgId);
41
- const visibleIds = new Set(visible.map((s) => s.id));
42
-
43
- if (orgId) {
44
- const orgChoice = config.orgDefaults?.[orgId];
45
- if (orgChoice && visibleIds.has(orgChoice)) return orgChoice;
46
- }
47
- if (config.defaultServerId && visibleIds.has(config.defaultServerId)) {
48
- return config.defaultServerId;
49
- }
50
- return undefined;
51
- }
52
-
53
- /**
54
- * Resolve a definition's compute server. Architecture spec §4.8 resolution order:
55
- * 1. Definition pin (`computeServerId`) if visible to the project's org.
56
- * 2. `orgDefaults[orgId]` if set.
57
- * 3. Global `defaultServerId`.
58
- *
59
- * Pure — throws if no usable server exists.
60
- */
61
- export interface ResolveOptions {
62
- /** Per-definition pin. Falls through silently if not visible. */
63
- definitionPin?: string | null;
64
- }
65
-
66
- export function resolveServerForOrg(
67
- config: ComputeConfig,
68
- orgId: string | null | undefined,
69
- opts: ResolveOptions = {}
70
- ): ComputeServerConfig {
71
- const visible = serversVisibleTo(config, orgId);
72
- if (visible.length === 0) {
73
- throw new Error('No compute servers are configured or visible for this org.');
74
- }
75
-
76
- if (opts.definitionPin) {
77
- const pinned = visible.find((s) => s.id === opts.definitionPin);
78
- if (pinned) return pinned;
79
- // Fall through — admin may have un-shared the server since the pin
80
- // was set.
81
- }
82
-
83
- const defaultId = defaultServerIdFor(config, orgId);
84
- if (defaultId) {
85
- const found = visible.find((s) => s.id === defaultId);
86
- if (found) return found;
87
- }
88
-
89
- // Last resort — return the first visible server. Mostly applies in tests
90
- // and during initial setup before a default is chosen.
91
- return visible[0];
92
- }
93
-
94
- /**
95
- * Lookup by id, regardless of scope or visibility. Useful in admin contexts
96
- * that need to display a server independently of the visibility filter.
97
- */
98
- export function findServerById(config: ComputeConfig, id: string): ComputeServerConfig | undefined {
99
- return config.servers.find((s) => s.id === id);
100
- }
101
-
102
- // ============================================================================
103
- // Filters
104
- // ============================================================================
105
-
106
- export function platformServers(config: ComputeConfig) {
107
- return config.servers.filter(isPlatformServer);
108
- }
109
-
110
- export function orgServersFor(config: ComputeConfig, orgId: string) {
111
- return config.servers.filter((s) => isOrgServer(s) && s.ownerOrgId === orgId);
112
- }