@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.
- package/LICENSE +1 -1
- package/dist/auth.d.ts +60 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +12 -0
- package/dist/auth.js.map +1 -0
- package/dist/compute.d.ts +47 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +11 -0
- package/dist/compute.js.map +1 -0
- package/dist/computeServer/index.d.ts +2 -0
- package/dist/computeServer/index.d.ts.map +1 -1
- package/dist/computeServer/index.js +6 -0
- package/dist/computeServer/index.js.map +1 -1
- package/dist/computeServer/interface.d.ts +10 -0
- package/dist/computeServer/interface.d.ts.map +1 -1
- package/dist/computeServer/secrets.d.ts +53 -0
- package/dist/computeServer/secrets.d.ts.map +1 -0
- package/dist/computeServer/secrets.js +67 -0
- package/dist/computeServer/secrets.js.map +1 -0
- package/dist/context.d.ts +7 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +13 -0
- package/dist/context.js.map +1 -1
- package/dist/data/interface.d.ts +33 -0
- package/dist/data/interface.d.ts.map +1 -1
- package/dist/definitions/interface.d.ts +18 -0
- package/dist/definitions/interface.d.ts.map +1 -1
- package/dist/definitions/schemas.d.ts +1 -0
- package/dist/definitions/schemas.d.ts.map +1 -1
- package/dist/definitions/schemas.js +3 -0
- package/dist/definitions/schemas.js.map +1 -1
- package/dist/definitions/service.d.ts +62 -0
- package/dist/definitions/service.d.ts.map +1 -0
- package/dist/definitions/service.js +149 -0
- package/dist/definitions/service.js.map +1 -0
- package/dist/definitions/types.d.ts +24 -1
- package/dist/definitions/types.d.ts.map +1 -1
- package/dist/definitions.d.ts +146 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/definitions.js +23 -0
- package/dist/definitions.js.map +1 -0
- package/dist/errors/interface.d.ts +44 -0
- package/dist/errors/interface.d.ts.map +1 -0
- package/dist/errors/interface.js +9 -0
- package/dist/errors/interface.js.map +1 -0
- package/dist/events/interface.d.ts +23 -0
- package/dist/events/interface.d.ts.map +1 -1
- package/dist/events/interface.js.map +1 -1
- package/dist/index.d.ts +14 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/invites/schemas.d.ts +24 -0
- package/dist/invites/schemas.d.ts.map +1 -0
- package/dist/invites/schemas.js +17 -0
- package/dist/invites/schemas.js.map +1 -0
- package/dist/metrics/interface.d.ts +6 -0
- package/dist/metrics/interface.d.ts.map +1 -1
- package/dist/metrics/interface.js.map +1 -1
- package/dist/organizations/index.d.ts +4 -3
- package/dist/organizations/index.d.ts.map +1 -1
- package/dist/organizations/index.js +2 -1
- package/dist/organizations/index.js.map +1 -1
- package/dist/organizations/interface.d.ts +1 -1
- package/dist/organizations/interface.d.ts.map +1 -1
- package/dist/organizations/paths.d.ts +22 -0
- package/dist/organizations/paths.d.ts.map +1 -0
- package/dist/organizations/paths.js +43 -0
- package/dist/organizations/paths.js.map +1 -0
- package/dist/organizations/schemas.d.ts +10 -0
- package/dist/organizations/schemas.d.ts.map +1 -1
- package/dist/organizations/schemas.js +6 -0
- package/dist/organizations/schemas.js.map +1 -1
- package/dist/organizations/types.d.ts +10 -1
- package/dist/organizations/types.d.ts.map +1 -1
- package/dist/organizations.d.ts +93 -0
- package/dist/organizations.d.ts.map +1 -0
- package/dist/organizations.js +14 -0
- package/dist/organizations.js.map +1 -0
- package/dist/solveCache/interface.d.ts +86 -0
- package/dist/solveCache/interface.d.ts.map +1 -0
- package/dist/solveCache/interface.js +12 -0
- package/dist/solveCache/interface.js.map +1 -0
- package/dist/storage/assetClasses.d.ts +59 -0
- package/dist/storage/assetClasses.d.ts.map +1 -0
- package/dist/storage/assetClasses.js +86 -0
- package/dist/storage/assetClasses.js.map +1 -0
- package/dist/storage/cacheBust.d.ts +21 -0
- package/dist/storage/cacheBust.d.ts.map +1 -0
- package/dist/storage/cacheBust.js +29 -0
- package/dist/storage/cacheBust.js.map +1 -0
- package/dist/storage/image.d.ts +6 -1
- package/dist/storage/image.d.ts.map +1 -1
- package/dist/storage/image.js +8 -3
- package/dist/storage/image.js.map +1 -1
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/testing/adapterSuite.d.ts +33 -0
- package/dist/testing/adapterSuite.d.ts.map +1 -0
- package/dist/testing/adapterSuite.js +166 -0
- package/dist/testing/adapterSuite.js.map +1 -0
- package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/definitionStoreSuite.js +87 -0
- package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
- package/dist/testing/suites/eventSinkSuite.js +1 -0
- package/dist/testing/suites/eventSinkSuite.js.map +1 -1
- package/dist/testing/suites/orgStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/orgStoreSuite.js +12 -0
- package/dist/testing/suites/orgStoreSuite.js.map +1 -1
- package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
- package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
- package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
- package/dist/testing/suites/runner.d.ts +21 -0
- package/dist/testing/suites/runner.d.ts.map +1 -0
- package/dist/testing/suites/runner.js +7 -0
- package/dist/testing/suites/runner.js.map +1 -0
- package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
- package/dist/testing/suites/storageProviderSuite.js +18 -0
- package/dist/testing/suites/storageProviderSuite.js.map +1 -1
- package/package.json +9 -24
- package/src/access/index.ts +0 -23
- package/src/access/rules.ts +0 -276
- package/src/auth/index.ts +0 -8
- package/src/auth/interface.ts +0 -279
- package/src/auth/types.ts +0 -32
- package/src/bindings/index.ts +0 -2
- package/src/bindings/interface.ts +0 -61
- package/src/computeServer/index.ts +0 -17
- package/src/computeServer/interface.ts +0 -62
- package/src/computeServer/types.ts +0 -74
- package/src/computeServer/utils.ts +0 -112
- package/src/config.ts +0 -99
- package/src/context.ts +0 -66
- package/src/data/index.ts +0 -1
- package/src/data/interface.ts +0 -70
- package/src/definitions/index.ts +0 -26
- package/src/definitions/interface.ts +0 -78
- package/src/definitions/paths.ts +0 -31
- package/src/definitions/schemas.ts +0 -64
- package/src/definitions/types.ts +0 -111
- package/src/errors.ts +0 -16
- package/src/events/audit.ts +0 -67
- package/src/events/index.ts +0 -9
- package/src/events/interface.ts +0 -102
- package/src/index.ts +0 -219
- package/src/invites/index.ts +0 -2
- package/src/invites/interface.ts +0 -38
- package/src/invites/types.ts +0 -39
- package/src/metrics/interface.ts +0 -86
- package/src/organizations/index.ts +0 -14
- package/src/organizations/interface.ts +0 -68
- package/src/organizations/schemas.ts +0 -52
- package/src/organizations/types.ts +0 -31
- package/src/pagination.ts +0 -45
- package/src/permissions/index.ts +0 -3
- package/src/permissions/interface.ts +0 -60
- package/src/permissions/types.ts +0 -21
- package/src/platformProjects/index.ts +0 -3
- package/src/platformProjects/interface.ts +0 -27
- package/src/platformProjects/schemas.ts +0 -13
- package/src/platformProjects/types.ts +0 -22
- package/src/projects/index.ts +0 -4
- package/src/projects/interface.ts +0 -58
- package/src/projects/schemas.ts +0 -28
- package/src/projects/types.ts +0 -36
- package/src/shareLinks/index.ts +0 -5
- package/src/shareLinks/interface.ts +0 -35
- package/src/shareLinks/schemas.ts +0 -21
- package/src/shareLinks/types.ts +0 -34
- package/src/storage/image.ts +0 -77
- package/src/storage/index.ts +0 -9
- package/src/storage/interface.ts +0 -27
- package/src/testing/index.ts +0 -47
- package/src/testing/suites/authProviderSuite.ts +0 -207
- package/src/testing/suites/computeServerStoreSuite.ts +0 -195
- package/src/testing/suites/definitionStoreSuite.ts +0 -585
- package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
- package/src/testing/suites/eventSinkSuite.ts +0 -379
- package/src/testing/suites/helpers.ts +0 -87
- package/src/testing/suites/inviteStoreSuite.ts +0 -143
- package/src/testing/suites/orgStoreSuite.ts +0 -412
- package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
- package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
- package/src/testing/suites/projectStoreSuite.ts +0 -381
- package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
- package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
- package/src/testing/suites/storageProviderSuite.ts +0 -175
- package/src/testing/suites/userProfileStoreSuite.ts +0 -134
- package/src/userProfile/index.ts +0 -3
- package/src/userProfile/interface.ts +0 -48
- package/src/userProfile/types.ts +0 -24
- package/src/utils/audit.ts +0 -46
- package/src/utils/index.ts +0 -2
package/src/auth/interface.ts
DELETED
|
@@ -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' };
|
package/src/bindings/index.ts
DELETED
|
@@ -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
|
-
}
|