@sovereignfs/sdk 1.25.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.
- package/LICENSE +661 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-NQLGPAL6.js +62 -0
- package/dist/e2ee-crypto.d.ts +84 -0
- package/dist/e2ee-crypto.js +162 -0
- package/dist/e2ee-device.d.ts +21 -0
- package/dist/e2ee-device.js +13 -0
- package/dist/e2ee-object.d.ts +50 -0
- package/dist/e2ee-object.js +75 -0
- package/dist/e2ee-state-U-GEJ7oL.d.ts +411 -0
- package/dist/e2ee-state.d.ts +1 -0
- package/dist/e2ee-state.js +36 -0
- package/dist/index.d.ts +543 -0
- package/dist/index.js +652 -0
- package/package.json +50 -0
- package/src/__tests__/e2ee-crypto.test.ts +156 -0
- package/src/__tests__/e2ee-object.test.ts +90 -0
- package/src/__tests__/e2ee-state.test.ts +83 -0
- package/src/__tests__/sdk.test.ts +427 -0
- package/src/__tests__/sessions.test.ts +63 -0
- package/src/activity.ts +19 -0
- package/src/auth.ts +134 -0
- package/src/connections.ts +106 -0
- package/src/data.ts +88 -0
- package/src/db.ts +34 -0
- package/src/directory.ts +31 -0
- package/src/e2ee-crypto.ts +260 -0
- package/src/e2ee-device.ts +75 -0
- package/src/e2ee-object.ts +118 -0
- package/src/e2ee-state.ts +72 -0
- package/src/e2ee.ts +65 -0
- package/src/env.ts +37 -0
- package/src/errors.ts +46 -0
- package/src/host.ts +251 -0
- package/src/index.ts +160 -0
- package/src/mailer.ts +7 -0
- package/src/notifications.ts +36 -0
- package/src/platform.ts +10 -0
- package/src/plugins.ts +146 -0
- package/src/portability.ts +176 -0
- package/src/secrets.ts +68 -0
- package/src/sessions.ts +36 -0
- package/src/storage.ts +49 -0
- package/src/types.ts +418 -0
- package/src/unimplemented.ts +48 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
interface SessionUser {
|
|
2
|
+
id: string;
|
|
3
|
+
tenantId: string;
|
|
4
|
+
email: string;
|
|
5
|
+
name: string | null;
|
|
6
|
+
image: string | null;
|
|
7
|
+
role: string;
|
|
8
|
+
/** Effective capability strings derived from the role preset (RFC 0021). */
|
|
9
|
+
capabilities: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
interface Session {
|
|
12
|
+
user: SessionUser;
|
|
13
|
+
/** Session expiry as a Unix timestamp (seconds). */
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
}
|
|
16
|
+
/** An authenticated session for the current user (SRS ACC-05). */
|
|
17
|
+
interface ActiveSession {
|
|
18
|
+
/** Opaque session token — pass to `sdk.auth.revokeSession` to end it. */
|
|
19
|
+
token: string;
|
|
20
|
+
/** Whether this is the session making the current request. */
|
|
21
|
+
current: boolean;
|
|
22
|
+
/** Raw User-Agent string of the device that created the session, if known. */
|
|
23
|
+
userAgent: string | null;
|
|
24
|
+
ipAddress: string | null;
|
|
25
|
+
/** Creation, last-active, and expiry as ISO 8601 strings. */
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
expiresAt: string;
|
|
29
|
+
}
|
|
30
|
+
interface ChangePasswordInput {
|
|
31
|
+
currentPassword: string;
|
|
32
|
+
newPassword: string;
|
|
33
|
+
}
|
|
34
|
+
/** Display-safe user shape returned by `sdk.directory` (RFC 0041). */
|
|
35
|
+
interface DirectoryUser {
|
|
36
|
+
id: string;
|
|
37
|
+
email: string;
|
|
38
|
+
name: string | null;
|
|
39
|
+
image: string | null;
|
|
40
|
+
}
|
|
41
|
+
interface SearchUsersInput {
|
|
42
|
+
query: string;
|
|
43
|
+
limit?: number;
|
|
44
|
+
}
|
|
45
|
+
interface ResolveUsersInput {
|
|
46
|
+
ids: string[];
|
|
47
|
+
}
|
|
48
|
+
interface MailOptions {
|
|
49
|
+
to: string | string[];
|
|
50
|
+
subject: string;
|
|
51
|
+
html?: string;
|
|
52
|
+
text?: string;
|
|
53
|
+
from?: string;
|
|
54
|
+
}
|
|
55
|
+
interface PlatformConfig {
|
|
56
|
+
/**
|
|
57
|
+
* Name of the default tenant row — set in Console › Settings › Instance name.
|
|
58
|
+
* In v1 (single-tenant) this is the platform-wide workspace name.
|
|
59
|
+
* Multi-tenant deployments (post-v1) will surface per-tenant names separately.
|
|
60
|
+
*/
|
|
61
|
+
tenantName: string;
|
|
62
|
+
inviteOnly: boolean;
|
|
63
|
+
version: string;
|
|
64
|
+
/** Instance name; falls back to tenantName when no white-label name is configured. */
|
|
65
|
+
instanceName: string;
|
|
66
|
+
/** Validated 6-digit hex colour overriding --sv-color-accent, or undefined when unset. */
|
|
67
|
+
instancePrimaryColor?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Stable UUID that uniquely identifies this Sovereign installation.
|
|
70
|
+
* Generated once at bootstrap and never changes. Use this to identify the
|
|
71
|
+
* instance in federated contexts, license checks, or analytics.
|
|
72
|
+
*/
|
|
73
|
+
instanceId: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* An activity event a plugin records via `sdk.activity.log()` (RFC 0005 —
|
|
77
|
+
* reserved surface, not yet implemented). The plugin supplies only the event
|
|
78
|
+
* shape; the runtime injects the actor, tenant, and emitting plugin and forces
|
|
79
|
+
* the event to be user-scoped, so a plugin cannot forge actor identity.
|
|
80
|
+
*/
|
|
81
|
+
interface ActivityLogEntry {
|
|
82
|
+
/** Dotted action verb, e.g. `"list.created"`. Namespaced by the runtime per plugin. */
|
|
83
|
+
action: string;
|
|
84
|
+
/** The user this event is about, if any — drives the user's personal-feed visibility. */
|
|
85
|
+
subjectUserId?: string;
|
|
86
|
+
/** Generic target kind, e.g. `"list"`. */
|
|
87
|
+
targetType?: string;
|
|
88
|
+
/** Generic target id, e.g. the list id. */
|
|
89
|
+
targetId?: string;
|
|
90
|
+
/** Human-readable one-line summary. */
|
|
91
|
+
summary?: string;
|
|
92
|
+
/** Structured detail (before/after, etc.). Avoid PII beyond what is necessary. */
|
|
93
|
+
metadata?: Record<string, unknown>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The Drizzle client returned by `sdk.db.getClient()` — the live platform
|
|
97
|
+
* Drizzle instance. Kept opaque (`unknown`) at the contract level so the
|
|
98
|
+
* published SDK takes no dependency on a specific dialect's Drizzle types;
|
|
99
|
+
* plugins type their own queries through their schema.
|
|
100
|
+
*/
|
|
101
|
+
type DrizzleClient = unknown;
|
|
102
|
+
/**
|
|
103
|
+
* Input to `sdk.notifications.send()` (RFC 0015).
|
|
104
|
+
* The runtime auto-populates `source`, `sourceType`, and `tenantId` from
|
|
105
|
+
* request context — plugins never set those.
|
|
106
|
+
*/
|
|
107
|
+
interface SendNotificationInput {
|
|
108
|
+
/** User ID of the intended recipient. */
|
|
109
|
+
recipientUserId: string;
|
|
110
|
+
/** Short notification headline. */
|
|
111
|
+
title: string;
|
|
112
|
+
/** Optional longer body text. */
|
|
113
|
+
body?: string;
|
|
114
|
+
/** In-app route to navigate to when the notification is clicked. */
|
|
115
|
+
url?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Category tag — drives mute preferences.
|
|
118
|
+
* Built-in categories: `'info'` (default), `'announcement'`, `'security'`.
|
|
119
|
+
* Custom plugin categories are allowed; `'security'` cannot be muted.
|
|
120
|
+
*/
|
|
121
|
+
category?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Optional URL to an image shown in the OS push notification (the Push
|
|
124
|
+
* API's `icon` option — not an `@sovereignfs/ui` `<Icon>` name; nothing
|
|
125
|
+
* in-app reads this field today). Defaults to the sending plugin's own
|
|
126
|
+
* `/plugin-icons/<pluginId>.svg` when unset — only set this to override
|
|
127
|
+
* with a notification-specific image instead of the plugin's own icon.
|
|
128
|
+
*/
|
|
129
|
+
icon?: string;
|
|
130
|
+
}
|
|
131
|
+
/** Plugin-scoped file storage object metadata (RFC 0044). */
|
|
132
|
+
interface StorageObject {
|
|
133
|
+
id: string;
|
|
134
|
+
pluginId: string;
|
|
135
|
+
ownerUserId: string | null;
|
|
136
|
+
key: string;
|
|
137
|
+
contentType: string;
|
|
138
|
+
size: number;
|
|
139
|
+
checksum: string;
|
|
140
|
+
/** The `metadata` passed to `put()`, round-tripped back on `get()`/`list()`. */
|
|
141
|
+
metadata: Record<string, unknown> | null;
|
|
142
|
+
createdAt: number;
|
|
143
|
+
updatedAt: number;
|
|
144
|
+
}
|
|
145
|
+
interface StoragePutInput {
|
|
146
|
+
key: string;
|
|
147
|
+
body: Blob | ArrayBuffer | Uint8Array;
|
|
148
|
+
contentType: string;
|
|
149
|
+
/** Owner of the object. Omit for a plugin-scoped (not per-user) object. */
|
|
150
|
+
ownerUserId?: string;
|
|
151
|
+
metadata?: Record<string, unknown> | null;
|
|
152
|
+
}
|
|
153
|
+
interface StorageContext {
|
|
154
|
+
tenantId: string;
|
|
155
|
+
pluginId: string;
|
|
156
|
+
userId: string | null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Client-side encryption profile shapes (RFC 0060, epic task 8.9). Persisted
|
|
160
|
+
* via `sdk.e2ee`; the browser-only crypto that produces/consumes the
|
|
161
|
+
* ciphertext these methods store lives in `e2ee-crypto`/`e2ee-device`/
|
|
162
|
+
* `e2ee-object`/`e2ee-state`. Distinct from server-side field crypto
|
|
163
|
+
* (`sdk.crypto.encryptField()`, RFC 0008): the runtime can decrypt a
|
|
164
|
+
* server-side field; it can never decrypt a client-side encrypted object.
|
|
165
|
+
*
|
|
166
|
+
* The Client Master Key (CMK) itself never exists server-side, plaintext or
|
|
167
|
+
* otherwise — only wrapped copies (`E2eeRecoveryWrapper`/
|
|
168
|
+
* `E2eeDeviceEnrollment`) and non-sensitive KDF/algorithm metadata do.
|
|
169
|
+
*/
|
|
170
|
+
type E2eeProfileStatus = 'active' | 'disabled';
|
|
171
|
+
/** Whether the current user has set up client-side encryption at all. */
|
|
172
|
+
interface E2eeProfile {
|
|
173
|
+
id: string;
|
|
174
|
+
userId: string;
|
|
175
|
+
status: E2eeProfileStatus;
|
|
176
|
+
/** e.g. `'AES-GCM-256'` — the algorithm used to encrypt objects under the CMK. */
|
|
177
|
+
cmkAlgorithm: string;
|
|
178
|
+
createdAt: number;
|
|
179
|
+
updatedAt: number;
|
|
180
|
+
}
|
|
181
|
+
/** The CMK wrapped by a key derived from the user's recovery secret. One per user. */
|
|
182
|
+
interface E2eeRecoveryWrapper {
|
|
183
|
+
id: string;
|
|
184
|
+
userId: string;
|
|
185
|
+
wrappedCmk: string;
|
|
186
|
+
kdfAlgorithm: string;
|
|
187
|
+
/** Opaque JSON-encoded KDF parameters (iterations, memory cost, etc.). */
|
|
188
|
+
kdfParams: string;
|
|
189
|
+
kdfSalt: string;
|
|
190
|
+
algorithmVersion: string;
|
|
191
|
+
createdAt: number;
|
|
192
|
+
updatedAt: number;
|
|
193
|
+
}
|
|
194
|
+
/** The CMK wrapped by one enrolled device's own key. Many per user. */
|
|
195
|
+
interface E2eeDeviceEnrollment {
|
|
196
|
+
id: string;
|
|
197
|
+
userId: string;
|
|
198
|
+
deviceId: string;
|
|
199
|
+
deviceLabel: string | null;
|
|
200
|
+
wrappedCmk: string;
|
|
201
|
+
algorithmVersion: string;
|
|
202
|
+
createdAt: number;
|
|
203
|
+
lastUsedAt: number | null;
|
|
204
|
+
/** `null` while the device is still enrolled. */
|
|
205
|
+
revokedAt: number | null;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Normalized state a plugin checks before touching client-side encrypted
|
|
209
|
+
* data — the SDK never hides the recovery model (RFC 0060 "SDK
|
|
210
|
+
* responsibilities"). `unsupported` means the browser lacks required
|
|
211
|
+
* WebCrypto primitives.
|
|
212
|
+
*/
|
|
213
|
+
type E2eeState = 'not-set-up' | 'locked' | 'unlocked' | 'unsupported';
|
|
214
|
+
/** No plugin scoping — one profile per user, regardless of caller. */
|
|
215
|
+
interface E2eeContext {
|
|
216
|
+
tenantId: string;
|
|
217
|
+
userId: string;
|
|
218
|
+
}
|
|
219
|
+
interface CreateE2eeProfileInput {
|
|
220
|
+
cmkAlgorithm: string;
|
|
221
|
+
}
|
|
222
|
+
interface SetE2eeRecoveryWrapperInput {
|
|
223
|
+
wrappedCmk: string;
|
|
224
|
+
kdfAlgorithm: string;
|
|
225
|
+
kdfParams: string;
|
|
226
|
+
kdfSalt: string;
|
|
227
|
+
algorithmVersion: string;
|
|
228
|
+
}
|
|
229
|
+
interface EnrollE2eeDeviceInput {
|
|
230
|
+
deviceId: string;
|
|
231
|
+
deviceLabel: string | null;
|
|
232
|
+
wrappedCmk: string;
|
|
233
|
+
algorithmVersion: string;
|
|
234
|
+
}
|
|
235
|
+
/** Runtime-created secret scope for the experimental plugin vault (RFC 0043). */
|
|
236
|
+
type SecretScope = 'user' | 'plugin' | 'instance';
|
|
237
|
+
/** Metadata returned for a stored secret. Plaintext values are never included. */
|
|
238
|
+
interface SecretRef {
|
|
239
|
+
id: string;
|
|
240
|
+
scope: SecretScope;
|
|
241
|
+
label: string;
|
|
242
|
+
metadata: Record<string, unknown> | null;
|
|
243
|
+
createdAt: number;
|
|
244
|
+
updatedAt: number;
|
|
245
|
+
lastUsedAt: number | null;
|
|
246
|
+
}
|
|
247
|
+
interface CreateSecretInput {
|
|
248
|
+
scope: SecretScope;
|
|
249
|
+
label: string;
|
|
250
|
+
value: string;
|
|
251
|
+
metadata?: Record<string, unknown> | null;
|
|
252
|
+
}
|
|
253
|
+
interface UpdateSecretInput {
|
|
254
|
+
id: string;
|
|
255
|
+
value: string;
|
|
256
|
+
}
|
|
257
|
+
interface SecretContext {
|
|
258
|
+
tenantId: string;
|
|
259
|
+
pluginId: string;
|
|
260
|
+
userId: string | null;
|
|
261
|
+
capabilities: readonly string[];
|
|
262
|
+
}
|
|
263
|
+
/** Runtime-created external provider connection scope (RFC 0049). */
|
|
264
|
+
type ConnectionScope = 'user' | 'plugin' | 'instance';
|
|
265
|
+
/** Metadata status for a plugin-owned external provider connection. */
|
|
266
|
+
type ConnectionStatus = 'connected' | 'needs_reauth' | 'paused' | 'disconnected' | 'error';
|
|
267
|
+
/** Metadata returned for an external connection. Secret values are never included. */
|
|
268
|
+
interface ConnectionRef {
|
|
269
|
+
id: string;
|
|
270
|
+
scope: ConnectionScope;
|
|
271
|
+
provider: string;
|
|
272
|
+
label: string;
|
|
273
|
+
status: ConnectionStatus;
|
|
274
|
+
secretRef: string | null;
|
|
275
|
+
metadata: Record<string, unknown> | null;
|
|
276
|
+
lastCheckedAt: number | null;
|
|
277
|
+
lastUsedAt: number | null;
|
|
278
|
+
lastError: SanitizedConnectionError | null;
|
|
279
|
+
createdAt: number;
|
|
280
|
+
updatedAt: number;
|
|
281
|
+
disconnectedAt: number | null;
|
|
282
|
+
}
|
|
283
|
+
interface CreateConnectionInput {
|
|
284
|
+
scope: ConnectionScope;
|
|
285
|
+
provider: string;
|
|
286
|
+
label: string;
|
|
287
|
+
secretRef?: string | null;
|
|
288
|
+
metadata?: Record<string, unknown> | null;
|
|
289
|
+
}
|
|
290
|
+
interface ConnectionListFilter {
|
|
291
|
+
provider?: string;
|
|
292
|
+
scope?: ConnectionScope;
|
|
293
|
+
includeDisconnected?: boolean;
|
|
294
|
+
}
|
|
295
|
+
interface UpdateConnectionInput {
|
|
296
|
+
label?: string;
|
|
297
|
+
status?: ConnectionStatus;
|
|
298
|
+
metadata?: Record<string, unknown> | null;
|
|
299
|
+
secretRef?: string | null;
|
|
300
|
+
lastCheckedAt?: number | null;
|
|
301
|
+
}
|
|
302
|
+
interface SanitizedConnectionError {
|
|
303
|
+
code?: string;
|
|
304
|
+
message: string;
|
|
305
|
+
retryable?: boolean;
|
|
306
|
+
status?: number;
|
|
307
|
+
}
|
|
308
|
+
interface MarkConnectionErrorInput {
|
|
309
|
+
error: SanitizedConnectionError;
|
|
310
|
+
status?: Extract<ConnectionStatus, 'error' | 'needs_reauth'>;
|
|
311
|
+
}
|
|
312
|
+
interface OAuthStateInput {
|
|
313
|
+
provider: string;
|
|
314
|
+
callbackPath: string;
|
|
315
|
+
nonce?: string;
|
|
316
|
+
metadata?: Record<string, unknown> | null;
|
|
317
|
+
expiresInSeconds?: number;
|
|
318
|
+
}
|
|
319
|
+
interface ConnectionOAuthState {
|
|
320
|
+
pluginId: string;
|
|
321
|
+
provider: string;
|
|
322
|
+
userId: string;
|
|
323
|
+
callbackPath: string;
|
|
324
|
+
nonce: string;
|
|
325
|
+
metadata: Record<string, unknown> | null;
|
|
326
|
+
expiresAt: number;
|
|
327
|
+
}
|
|
328
|
+
/** Source summary for effective external provider configuration (Task 3.27). */
|
|
329
|
+
type ProviderConfigSource = 'env' | 'console' | 'mixed' | 'missing';
|
|
330
|
+
/** Server-side effective external provider config for the calling plugin. */
|
|
331
|
+
interface ProviderConfig {
|
|
332
|
+
provider: string;
|
|
333
|
+
label: string;
|
|
334
|
+
configured: boolean;
|
|
335
|
+
source: ProviderConfigSource;
|
|
336
|
+
publicValues: Record<string, string>;
|
|
337
|
+
secretValues: Record<string, string>;
|
|
338
|
+
callbackUrl: string | null;
|
|
339
|
+
scopes: readonly string[];
|
|
340
|
+
missingRequired: readonly string[];
|
|
341
|
+
}
|
|
342
|
+
interface ConnectionContext {
|
|
343
|
+
tenantId: string;
|
|
344
|
+
pluginId: string;
|
|
345
|
+
userId: string | null;
|
|
346
|
+
capabilities: readonly string[];
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Context passed to a plugin's schedule handler (RFC 0046, Phase 1 subset).
|
|
350
|
+
* There is no originating HTTP request — the platform's in-process scheduler
|
|
351
|
+
* invokes handlers directly — so the runtime supplies synthetic `headers`
|
|
352
|
+
* pre-stamped with the calling plugin's identity. Pass them to SDK methods
|
|
353
|
+
* that attribute by request headers (e.g.
|
|
354
|
+
* `sdk.notifications.send(input, ctx.headers)`).
|
|
355
|
+
*/
|
|
356
|
+
interface ScheduleContext {
|
|
357
|
+
/** The invoking plugin's manifest id (e.g. `fs.sovereign.tasks`). */
|
|
358
|
+
pluginId: string;
|
|
359
|
+
/** The schedule's manifest-declared id (e.g. `due-reminders`). */
|
|
360
|
+
scheduleId: string;
|
|
361
|
+
/** Synthetic headers carrying `x-sovereign-plugin-id` for SDK attribution. */
|
|
362
|
+
headers: Headers;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* A plugin's schedule handler — the **default export** of a manifest-declared
|
|
366
|
+
* `schedules[].entry` module. Invoked server-side every `intervalMinutes`
|
|
367
|
+
* while the plugin is installed and enabled.
|
|
368
|
+
*
|
|
369
|
+
* Handlers must be idempotent: the interval is a floor, not an exact cadence,
|
|
370
|
+
* and restarts or multiple replicas can invoke a handler again sooner than
|
|
371
|
+
* the interval — claim work with conditional updates before acting on it.
|
|
372
|
+
* Thrown errors are caught and logged by the platform; there are no retries
|
|
373
|
+
* in Phase 1.
|
|
374
|
+
*/
|
|
375
|
+
type ScheduleHandler = (ctx: ScheduleContext) => Promise<void>;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Locked/unlocked state detection for client-side encryption (RFC 0060 SDK
|
|
379
|
+
* responsibility: "returning normalized unsupported/locked/error states").
|
|
380
|
+
* Consolidates the checks every consumer of `sdk.e2ee` needs before touching
|
|
381
|
+
* encrypted data — browser support, this device's local wrapping key, and
|
|
382
|
+
* whether this device has an active (non-revoked) enrollment — into one
|
|
383
|
+
* reusable helper instead of each plugin (Account today, Wallet later)
|
|
384
|
+
* reimplementing the same logic inline.
|
|
385
|
+
*/
|
|
386
|
+
|
|
387
|
+
interface E2eeLocalState {
|
|
388
|
+
state: E2eeState;
|
|
389
|
+
/** Null only when `state` is `'unsupported'` or `'not-set-up'`. */
|
|
390
|
+
deviceId: string | null;
|
|
391
|
+
/** This device's local wrapping key — present only when `state` is `'unlocked'`. */
|
|
392
|
+
deviceKey: CryptoKey | null;
|
|
393
|
+
/** This device's active enrollment record — present only when `state` is `'unlocked'`. */
|
|
394
|
+
activeEnrollment: E2eeDeviceEnrollment | null;
|
|
395
|
+
}
|
|
396
|
+
/** True when the current environment has the WebCrypto and IndexedDB primitives E2EE needs. */
|
|
397
|
+
declare function browserSupportsE2ee(): boolean;
|
|
398
|
+
/**
|
|
399
|
+
* Pure classification given already-resolved inputs — no IndexedDB access,
|
|
400
|
+
* so it's trivially unit-testable. `getE2eeLocalState` below is the
|
|
401
|
+
* convenience wrapper that actually reads this device's local key.
|
|
402
|
+
*/
|
|
403
|
+
declare function classifyE2eeLocalState(profile: E2eeProfile | null, devices: E2eeDeviceEnrollment[], deviceId: string, deviceKey: CryptoKey | null): E2eeLocalState;
|
|
404
|
+
/**
|
|
405
|
+
* Resolve this device's current E2EE state against the user's profile and
|
|
406
|
+
* enrollment list (typically loaded server-side and passed in by the
|
|
407
|
+
* caller). Reads this device's local wrapping key from IndexedDB.
|
|
408
|
+
*/
|
|
409
|
+
declare function getE2eeLocalState(profile: E2eeProfile | null, devices: E2eeDeviceEnrollment[]): Promise<E2eeLocalState>;
|
|
410
|
+
|
|
411
|
+
export { type ActiveSession as A, type E2eeLocalState as B, type ChangePasswordInput as C, type DrizzleClient as D, type E2eeContext as E, type E2eeProfileStatus as F, type E2eeState as G, type ProviderConfigSource as H, type SanitizedConnectionError as I, type ScheduleContext as J, type ScheduleHandler as K, type SessionUser as L, type MailOptions as M, type UpdateSecretInput as N, type OAuthStateInput as O, type PlatformConfig as P, browserSupportsE2ee as Q, type ResolveUsersInput as R, type Session as S, classifyE2eeLocalState as T, type UpdateConnectionInput as U, getE2eeLocalState as V, type SearchUsersInput as a, type DirectoryUser as b, type ActivityLogEntry as c, type SendNotificationInput as d, type StoragePutInput as e, type StorageContext as f, type StorageObject as g, type E2eeProfile as h, type CreateE2eeProfileInput as i, type E2eeRecoveryWrapper as j, type SetE2eeRecoveryWrapperInput as k, type EnrollE2eeDeviceInput as l, type E2eeDeviceEnrollment as m, type CreateSecretInput as n, type SecretContext as o, type SecretRef as p, type SecretScope as q, type CreateConnectionInput as r, type ConnectionContext as s, type ConnectionRef as t, type ConnectionListFilter as u, type MarkConnectionErrorInput as v, type ConnectionOAuthState as w, type ProviderConfig as x, type ConnectionScope as y, type ConnectionStatus as z };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { B as E2eeLocalState, Q as browserSupportsE2ee, T as classifyE2eeLocalState, V as getE2eeLocalState } from './e2ee-state-U-GEJ7oL.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDeviceKey,
|
|
3
|
+
getOrCreateDeviceId
|
|
4
|
+
} from "./chunk-NQLGPAL6.js";
|
|
5
|
+
import "./chunk-MLKGABMK.js";
|
|
6
|
+
|
|
7
|
+
// src/e2ee-state.ts
|
|
8
|
+
function browserSupportsE2ee() {
|
|
9
|
+
return typeof indexedDB !== "undefined" && typeof crypto !== "undefined" && typeof crypto.subtle !== "undefined";
|
|
10
|
+
}
|
|
11
|
+
function classifyE2eeLocalState(profile, devices, deviceId, deviceKey) {
|
|
12
|
+
if (!profile) {
|
|
13
|
+
return { state: "not-set-up", deviceId: null, deviceKey: null, activeEnrollment: null };
|
|
14
|
+
}
|
|
15
|
+
const activeEnrollment = devices.find((d) => d.deviceId === deviceId && d.revokedAt === null) ?? null;
|
|
16
|
+
if (deviceKey && activeEnrollment) {
|
|
17
|
+
return { state: "unlocked", deviceId, deviceKey, activeEnrollment };
|
|
18
|
+
}
|
|
19
|
+
return { state: "locked", deviceId, deviceKey: null, activeEnrollment: null };
|
|
20
|
+
}
|
|
21
|
+
async function getE2eeLocalState(profile, devices) {
|
|
22
|
+
if (!browserSupportsE2ee()) {
|
|
23
|
+
return { state: "unsupported", deviceId: null, deviceKey: null, activeEnrollment: null };
|
|
24
|
+
}
|
|
25
|
+
if (!profile) {
|
|
26
|
+
return { state: "not-set-up", deviceId: null, deviceKey: null, activeEnrollment: null };
|
|
27
|
+
}
|
|
28
|
+
const deviceId = getOrCreateDeviceId();
|
|
29
|
+
const deviceKey = await getDeviceKey(deviceId);
|
|
30
|
+
return classifyE2eeLocalState(profile, devices, deviceId, deviceKey);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
browserSupportsE2ee,
|
|
34
|
+
classifyE2eeLocalState,
|
|
35
|
+
getE2eeLocalState
|
|
36
|
+
};
|