@ai-matrx/vault 0.0.0 → 0.1.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/CHANGELOG.md +13 -0
- package/README.md +27 -1
- package/dist/index.cjs +8823 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1186 -0
- package/dist/index.d.ts +1186 -0
- package/dist/index.js +8790 -0
- package/dist/index.js.map +1 -0
- package/dist/io/bitwarden-json.worker.js +819 -0
- package/dist/io/bitwarden-json.worker.js.map +1 -0
- package/dist/io/csv-import.worker.js +289 -0
- package/dist/io/csv-import.worker.js.map +1 -0
- package/dist/io/keepass-xml.worker.js +2563 -0
- package/dist/io/keepass-xml.worker.js.map +1 -0
- package/dist/io/onepux.worker.js +8726 -0
- package/dist/io/onepux.worker.js.map +1 -0
- package/dist/io/proton-pass.worker.js +9058 -0
- package/dist/io/proton-pass.worker.js.map +1 -0
- package/package.json +86 -1
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1186 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { KnobClient } from '@ai-matrx/data/knobs';
|
|
3
|
+
import * as account from '@ai-matrx/api/account';
|
|
4
|
+
import { Api } from '@ai-matrx/api';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The Vault as the screens see it. Hosts map the wire shape into this once, in their VaultPort
|
|
8
|
+
* (`createApiVaultPort` does it for every host that talks to the server). Plaintext never lives
|
|
9
|
+
* here: an encrypted field carries a masked hint, and a revealed value is held only by
|
|
10
|
+
* `useTransientSecret` in the row showing it.
|
|
11
|
+
*/
|
|
12
|
+
/** Who may see a field's value. Mirrors the server contract: sealed has no human path, ever. */
|
|
13
|
+
type FieldHandling = "visible" | "revealable" | "sealed";
|
|
14
|
+
type FieldKind = "text" | "secret" | "url" | "multiline";
|
|
15
|
+
interface VaultField {
|
|
16
|
+
id: string;
|
|
17
|
+
/** Stable key inside the item (`username`, `password`). */
|
|
18
|
+
key: string;
|
|
19
|
+
label: string;
|
|
20
|
+
kind: FieldKind;
|
|
21
|
+
handling: FieldHandling;
|
|
22
|
+
editable: boolean;
|
|
23
|
+
envKey: string | null;
|
|
24
|
+
injectIntoSandbox: boolean;
|
|
25
|
+
/** Plain words about this field (`fields/` edits it). */
|
|
26
|
+
description: string;
|
|
27
|
+
/** Off = kept, but not used by anything. Inactive fields stay on the item so they can be turned back on. */
|
|
28
|
+
active: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** A plaintext custom field (the server's `non_secret_fields`): label + value, never encrypted. */
|
|
31
|
+
interface VaultCustomField {
|
|
32
|
+
key: string;
|
|
33
|
+
label: string;
|
|
34
|
+
value: string;
|
|
35
|
+
}
|
|
36
|
+
interface VaultOwner {
|
|
37
|
+
kind: "me" | "organization" | "shared";
|
|
38
|
+
label: string;
|
|
39
|
+
organizationId: string | null;
|
|
40
|
+
}
|
|
41
|
+
interface VaultItemCapabilities {
|
|
42
|
+
canUse: boolean;
|
|
43
|
+
canEdit: boolean;
|
|
44
|
+
canReveal: boolean;
|
|
45
|
+
canManage: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface VaultAuthenticator {
|
|
48
|
+
digits: number;
|
|
49
|
+
period: number;
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
}
|
|
52
|
+
interface VaultAttachment {
|
|
53
|
+
id: string;
|
|
54
|
+
label: string;
|
|
55
|
+
/** What the file is for ("Signs App Store requests"); "" when none. */
|
|
56
|
+
description: string;
|
|
57
|
+
fileName: string;
|
|
58
|
+
mediaType: string;
|
|
59
|
+
sizeBytes: number;
|
|
60
|
+
handling: FieldHandling;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
}
|
|
64
|
+
/** How the browser decides an item matches a page. */
|
|
65
|
+
type UriMatchMode = "host" | "domain" | "exact" | "never";
|
|
66
|
+
interface VaultItem {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
description: string;
|
|
70
|
+
definitionKey: string;
|
|
71
|
+
definitionLabel: string;
|
|
72
|
+
owner: VaultOwner;
|
|
73
|
+
tags: string[];
|
|
74
|
+
loginUrls: string[];
|
|
75
|
+
notes: string;
|
|
76
|
+
fields: VaultField[];
|
|
77
|
+
custom: VaultCustomField[];
|
|
78
|
+
authenticator: VaultAuthenticator | null;
|
|
79
|
+
capabilities: VaultItemCapabilities;
|
|
80
|
+
attachments: VaultAttachment[];
|
|
81
|
+
uriMatchMode: UriMatchMode;
|
|
82
|
+
browserFillEnabled: boolean;
|
|
83
|
+
/** `all_members` / `restricted` for an organization item. */
|
|
84
|
+
accessMode: string | null;
|
|
85
|
+
/** The person who owns the item (`user_id`). For the person's own items this is them. */
|
|
86
|
+
ownerUserId: string | null;
|
|
87
|
+
/** Set when the item is in the trash: what restoring it needs. */
|
|
88
|
+
deletion: {
|
|
89
|
+
deletionId: string | null;
|
|
90
|
+
deletedAt: string | null;
|
|
91
|
+
} | null;
|
|
92
|
+
updatedAt: string;
|
|
93
|
+
createdAt: string;
|
|
94
|
+
}
|
|
95
|
+
interface VaultOrganizationRef {
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
}
|
|
99
|
+
type VaultSort = "name" | "website" | "updated" | "created";
|
|
100
|
+
/** Which vault to look in. `all` is everything the person can see, shared with them included. */
|
|
101
|
+
type VaultSelection = "all" | "mine" | "shared" | (string & {});
|
|
102
|
+
interface VaultLocation {
|
|
103
|
+
vault: VaultSelection;
|
|
104
|
+
/** `all`, `favorites`, `authenticator`, `trash`, `category:<id>`, `type:<definitionKey>` or `tag:<tag>`. */
|
|
105
|
+
view: string;
|
|
106
|
+
q: string;
|
|
107
|
+
sort: VaultSort;
|
|
108
|
+
/** The open item; null = none. */
|
|
109
|
+
itemId: string | null;
|
|
110
|
+
mode: "view" | "edit" | "new";
|
|
111
|
+
/** New-item flow: the chosen definition, and the URL pasted at the start (kept across steps). */
|
|
112
|
+
kind: string | null;
|
|
113
|
+
url: string;
|
|
114
|
+
/** A panel open over the workspace: `share`, `history`, `activity`, `files`, `move`, `import`, … */
|
|
115
|
+
panel: string | null;
|
|
116
|
+
}
|
|
117
|
+
declare const DEFAULT_LOCATION: VaultLocation;
|
|
118
|
+
/** Reads a location from query params. The item id is the host's to place (a path or a param). */
|
|
119
|
+
declare function locationFromParams(params: URLSearchParams, itemId?: string | null): VaultLocation;
|
|
120
|
+
/** Writes everything that is not a default (and not the item id, which the host places). */
|
|
121
|
+
declare function locationToParams(loc: VaultLocation): URLSearchParams;
|
|
122
|
+
/** The one search every list shares: name, type, tags, URLs, then plaintext custom fields. */
|
|
123
|
+
declare function matchesQuery(item: VaultItem, query: string): boolean;
|
|
124
|
+
/** An item's website as people read it: `github.com` (no scheme, no `www.`), or "" when it has none. */
|
|
125
|
+
declare function siteOf(item: Pick<VaultItem, "loginUrls">): string;
|
|
126
|
+
declare function sortItems(items: readonly VaultItem[], sort: VaultSort): VaultItem[];
|
|
127
|
+
interface NewSecretField {
|
|
128
|
+
key: string;
|
|
129
|
+
label: string;
|
|
130
|
+
value: string;
|
|
131
|
+
}
|
|
132
|
+
interface ItemDraft {
|
|
133
|
+
name: string;
|
|
134
|
+
description: string;
|
|
135
|
+
tags: string[];
|
|
136
|
+
loginUrls: string[];
|
|
137
|
+
notes: string;
|
|
138
|
+
/** New values for encrypted fields the definition declares, by field key. Absent = unchanged. */
|
|
139
|
+
values: Record<string, string>;
|
|
140
|
+
/** Encrypted custom fields added in this edit. */
|
|
141
|
+
secrets: NewSecretField[];
|
|
142
|
+
/** The plaintext custom fields, complete. */
|
|
143
|
+
custom: VaultCustomField[];
|
|
144
|
+
/** Ids of encrypted fields to delete. */
|
|
145
|
+
removed: string[];
|
|
146
|
+
/** A seed or otpauth address to enrol as the item's one-time password; empty = leave alone. */
|
|
147
|
+
authenticator: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
type NoticeKind = "success" | "error" | "info";
|
|
151
|
+
type Notify = (message: string, kind?: NoticeKind) => void;
|
|
152
|
+
|
|
153
|
+
/** One recorded earlier value of a secret field. Value-free: the value comes only from `revealHistory`. */
|
|
154
|
+
interface HistoryEntry {
|
|
155
|
+
fieldId: string;
|
|
156
|
+
revision: number;
|
|
157
|
+
recordedAt: string;
|
|
158
|
+
/** False when the server kept the change but not its value (it can be neither shown nor restored). */
|
|
159
|
+
available: boolean;
|
|
160
|
+
}
|
|
161
|
+
/** One page of an item's history, newest first. */
|
|
162
|
+
interface HistoryPage {
|
|
163
|
+
entries: HistoryEntry[];
|
|
164
|
+
/** The chain head. A restore names it so a change made meanwhile is refused, never overwritten. */
|
|
165
|
+
historyRevision: number;
|
|
166
|
+
/** False when nothing on this item can be restored at all. */
|
|
167
|
+
restoreAvailable: boolean;
|
|
168
|
+
/** When capture began; earlier values were never recorded. */
|
|
169
|
+
captureStartedAt: string | null;
|
|
170
|
+
/** Pass to `history` for the next (older) page; null = no more. */
|
|
171
|
+
nextBefore: number | null;
|
|
172
|
+
/** Entries the server left out (unreadable), so the timeline is incomplete. */
|
|
173
|
+
omitted: number;
|
|
174
|
+
incomplete: boolean;
|
|
175
|
+
}
|
|
176
|
+
/** One audit row: what happened, who did it, when. Never a value. */
|
|
177
|
+
interface AuditEntry {
|
|
178
|
+
id: string;
|
|
179
|
+
action: string;
|
|
180
|
+
/** Null when the platform itself acted (automation, a scheduled job). */
|
|
181
|
+
actorId: string | null;
|
|
182
|
+
/** The encrypted field the event is about, when it is about one. */
|
|
183
|
+
fieldId: string | null;
|
|
184
|
+
metadata: Record<string, unknown>;
|
|
185
|
+
at: string;
|
|
186
|
+
}
|
|
187
|
+
/** New or changed file details. `description: null` clears it. */
|
|
188
|
+
interface FileDetails {
|
|
189
|
+
label?: string;
|
|
190
|
+
description?: string | null;
|
|
191
|
+
fileName?: string;
|
|
192
|
+
handling?: FieldHandling;
|
|
193
|
+
}
|
|
194
|
+
/** What happened to an item: password history, the audit trail, and its protected files. */
|
|
195
|
+
interface ActivityPort {
|
|
196
|
+
/** Personal items only (the server refuses others). `before` = `nextBefore` of the previous page. */
|
|
197
|
+
history(item: VaultItem, before?: number | null): Promise<HistoryPage>;
|
|
198
|
+
/** Audited; needs a recent sign-in. */
|
|
199
|
+
revealHistory(item: VaultItem, fieldId: string, revision: number): Promise<string>;
|
|
200
|
+
/** Makes an earlier value current again. Rejects with `HistoryChangedError` when the history moved. */
|
|
201
|
+
restoreHistory(item: VaultItem, fieldId: string, revision: number, expectedHistoryRevision: number): Promise<VaultItem>;
|
|
202
|
+
/** Newest first. The server has no cursor, only a limit (1–500). */
|
|
203
|
+
audit(item: VaultItem, limit: number): Promise<AuditEntry[]>;
|
|
204
|
+
/** Each returns the item as the server now has it. */
|
|
205
|
+
addFile(item: VaultItem, file: File, details: {
|
|
206
|
+
label: string;
|
|
207
|
+
description: string | null;
|
|
208
|
+
handling: FieldHandling;
|
|
209
|
+
}): Promise<VaultItem>;
|
|
210
|
+
updateFile(item: VaultItem, attachmentId: string, details: FileDetails): Promise<VaultItem>;
|
|
211
|
+
replaceFile(item: VaultItem, attachmentId: string, file: File): Promise<VaultItem>;
|
|
212
|
+
deleteFile(item: VaultItem, attachmentId: string): Promise<VaultItem>;
|
|
213
|
+
/** The decrypted bytes. Audited; sealed files are always refused. */
|
|
214
|
+
downloadFile(item: VaultItem, attachment: VaultAttachment): Promise<Uint8Array>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* CONFIRM IT'S YOU. The server shows or exports a secret only after a sign-in from the last few
|
|
219
|
+
* minutes (`recent_auth_required`). Every host signs people in with Supabase, so the one way to
|
|
220
|
+
* produce a fresh sign-in lives here, over the host's own auth client — whichever way the person
|
|
221
|
+
* signs in: Google (or any connected provider), a password, or a code sent to their email.
|
|
222
|
+
*/
|
|
223
|
+
/** How a person can prove it is them again. Ordered by what the account actually uses. */
|
|
224
|
+
type IdentityMethod = "provider" | "password" | "email_code" | "dev";
|
|
225
|
+
interface IdentityAccount {
|
|
226
|
+
email: string | null;
|
|
227
|
+
/** Connected sign-in providers on the account (`google`, `github`, …), never `email`. */
|
|
228
|
+
providers: readonly string[];
|
|
229
|
+
hasPassword: boolean;
|
|
230
|
+
}
|
|
231
|
+
interface VaultIdentity {
|
|
232
|
+
account(): Promise<IdentityAccount>;
|
|
233
|
+
/** Leaves the page for the provider and comes back to the same address (the URL carries everything). */
|
|
234
|
+
withProvider(provider: string): Promise<void>;
|
|
235
|
+
withPassword(password: string): Promise<void>;
|
|
236
|
+
sendEmailCode(): Promise<void>;
|
|
237
|
+
withEmailCode(code: string): Promise<void>;
|
|
238
|
+
/** A host-only path with no human step (the playground's dev server). */
|
|
239
|
+
dev?: () => Promise<void>;
|
|
240
|
+
}
|
|
241
|
+
/** The slice of supabase-js `auth` this needs, so the package never depends on supabase-js itself. */
|
|
242
|
+
interface SupabaseAuthLike {
|
|
243
|
+
getUser(): Promise<{
|
|
244
|
+
data: {
|
|
245
|
+
user: {
|
|
246
|
+
id: string;
|
|
247
|
+
email?: string | null;
|
|
248
|
+
identities?: {
|
|
249
|
+
provider: string;
|
|
250
|
+
}[] | null;
|
|
251
|
+
app_metadata?: {
|
|
252
|
+
providers?: string[];
|
|
253
|
+
};
|
|
254
|
+
} | null;
|
|
255
|
+
};
|
|
256
|
+
error: unknown;
|
|
257
|
+
}>;
|
|
258
|
+
signInWithPassword(c: {
|
|
259
|
+
email: string;
|
|
260
|
+
password: string;
|
|
261
|
+
}): Promise<{
|
|
262
|
+
data: {
|
|
263
|
+
user: {
|
|
264
|
+
id: string;
|
|
265
|
+
} | null;
|
|
266
|
+
};
|
|
267
|
+
error: {
|
|
268
|
+
message: string;
|
|
269
|
+
} | null;
|
|
270
|
+
}>;
|
|
271
|
+
signInWithOtp(c: {
|
|
272
|
+
email: string;
|
|
273
|
+
options?: {
|
|
274
|
+
shouldCreateUser?: boolean;
|
|
275
|
+
};
|
|
276
|
+
}): Promise<{
|
|
277
|
+
error: {
|
|
278
|
+
message: string;
|
|
279
|
+
} | null;
|
|
280
|
+
}>;
|
|
281
|
+
verifyOtp(c: {
|
|
282
|
+
email: string;
|
|
283
|
+
token: string;
|
|
284
|
+
type: "email";
|
|
285
|
+
}): Promise<{
|
|
286
|
+
data: {
|
|
287
|
+
user: {
|
|
288
|
+
id: string;
|
|
289
|
+
} | null;
|
|
290
|
+
};
|
|
291
|
+
error: {
|
|
292
|
+
message: string;
|
|
293
|
+
} | null;
|
|
294
|
+
}>;
|
|
295
|
+
signInWithOAuth(c: {
|
|
296
|
+
provider: never;
|
|
297
|
+
options?: {
|
|
298
|
+
redirectTo?: string;
|
|
299
|
+
};
|
|
300
|
+
}): Promise<{
|
|
301
|
+
error: {
|
|
302
|
+
message: string;
|
|
303
|
+
} | null;
|
|
304
|
+
}>;
|
|
305
|
+
}
|
|
306
|
+
/** THE identity confirmation for every host: give it the host's `supabase.auth`. */
|
|
307
|
+
declare function createSupabaseIdentity(auth: SupabaseAuthLike, options?: {
|
|
308
|
+
redirectTo?: () => string;
|
|
309
|
+
dev?: () => Promise<void>;
|
|
310
|
+
}): VaultIdentity;
|
|
311
|
+
/** True when the server refused because the sign-in is not recent enough. */
|
|
312
|
+
declare function needsRecentSignIn(e: unknown): boolean;
|
|
313
|
+
|
|
314
|
+
/** One account's one-time password as the server reports it. Metadata only: never a seed. */
|
|
315
|
+
interface AuthenticatorEntry {
|
|
316
|
+
itemId: string;
|
|
317
|
+
/** The login's name (the server has no separate authenticator name). */
|
|
318
|
+
name: string;
|
|
319
|
+
issuer: string | null;
|
|
320
|
+
/** The account the codes are for, from the setup address (`me@x.com`). */
|
|
321
|
+
account: string | null;
|
|
322
|
+
digits: number;
|
|
323
|
+
period: number;
|
|
324
|
+
algorithm: string;
|
|
325
|
+
enabled: boolean;
|
|
326
|
+
loginUrls: string[];
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* A two-factor method that is NOT an app code, recorded on the login as the plaintext custom field
|
|
330
|
+
* `mfa_method` so agents know to hand control back at the verification step.
|
|
331
|
+
*/
|
|
332
|
+
type MfaMethod = "sms" | "push_or_other";
|
|
333
|
+
type ItemRef = Pick<VaultItem, "id" | "owner">;
|
|
334
|
+
/**
|
|
335
|
+
* One-time passwords: every code, enrol by key / otpauth address / QR image, enable, disable,
|
|
336
|
+
* rename, remove, and the recorded non-app method. Every call may reject; enrol and remove may
|
|
337
|
+
* reject with "sign in again" — wrap them in `useConfirmed()`.
|
|
338
|
+
*/
|
|
339
|
+
interface AuthenticatorPort {
|
|
340
|
+
/** Every account the signed-in person holds a one-time password for. */
|
|
341
|
+
list(): Promise<AuthenticatorEntry[]>;
|
|
342
|
+
/** null when the item has none. */
|
|
343
|
+
get(item: ItemRef): Promise<AuthenticatorEntry | null>;
|
|
344
|
+
/** The current code and how long it lives. */
|
|
345
|
+
code(item: ItemRef & Pick<VaultItem, "authenticator">): Promise<TotpReading>;
|
|
346
|
+
/** A base32 setup key or a full `otpauth://totp/…` address. Needs a recent sign-in. */
|
|
347
|
+
enrol(item: ItemRef, input: string): Promise<AuthenticatorEntry>;
|
|
348
|
+
/** A QR image the server decodes in-request and drops. For hosts that cannot decode locally. */
|
|
349
|
+
enrolQrImage(item: ItemRef, image: Blob, fileName?: string): Promise<AuthenticatorEntry>;
|
|
350
|
+
/** On/off. Takes effect on the next code. */
|
|
351
|
+
setEnabled(item: ItemRef, enabled: boolean): Promise<AuthenticatorEntry>;
|
|
352
|
+
/** Deletes the seed. Two-factor stays on at the site; the phone app and backup codes still work. */
|
|
353
|
+
remove(item: ItemRef): Promise<void>;
|
|
354
|
+
/** Renames the login the code belongs to. */
|
|
355
|
+
rename(item: ItemRef, name: string): Promise<VaultItem>;
|
|
356
|
+
/** Records (or clears, with null) a non-app two-factor method on the login. */
|
|
357
|
+
setMfaMethod(item: ItemRef & Pick<VaultItem, "custom">, method: MfaMethod | null): Promise<VaultItem>;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** One field a credential type declares. */
|
|
361
|
+
interface DefinitionField {
|
|
362
|
+
key: string;
|
|
363
|
+
label: string;
|
|
364
|
+
kind: FieldKind;
|
|
365
|
+
handling: FieldHandling;
|
|
366
|
+
/** `metadata` fields are plaintext columns of the item; `encrypted` ones are secrets. */
|
|
367
|
+
storage: "metadata" | "encrypted";
|
|
368
|
+
required: boolean;
|
|
369
|
+
}
|
|
370
|
+
/** A credential type from the platform catalog (e.g. "GitHub personal access token"). */
|
|
371
|
+
interface VaultDefinition {
|
|
372
|
+
key: string;
|
|
373
|
+
label: string;
|
|
374
|
+
family: string;
|
|
375
|
+
providerKey: string | null;
|
|
376
|
+
description: string;
|
|
377
|
+
fields: readonly DefinitionField[];
|
|
378
|
+
sortOrder: number;
|
|
379
|
+
}
|
|
380
|
+
/** Catalog rows -> definitions. A preset's own fields replace its base's; with none it inherits them. */
|
|
381
|
+
declare function parseDefinitions(entries: readonly {
|
|
382
|
+
key: string;
|
|
383
|
+
payload: Record<string, unknown>;
|
|
384
|
+
sort_order?: number;
|
|
385
|
+
}[]): VaultDefinition[];
|
|
386
|
+
/** What a pasted address tells us: a name, the address itself, and the best-fitting type. */
|
|
387
|
+
interface UrlGuess {
|
|
388
|
+
name: string;
|
|
389
|
+
url: string;
|
|
390
|
+
definitionKey: string | null;
|
|
391
|
+
/** True when the address belongs to a provider the catalog knows. */
|
|
392
|
+
recognised: boolean;
|
|
393
|
+
/** That provider's own types (its tokens, its apps), offered beside the login. */
|
|
394
|
+
alternatives: readonly string[];
|
|
395
|
+
}
|
|
396
|
+
declare function inferFromUrl(raw: string, definitions: readonly VaultDefinition[]): UrlGuess | null;
|
|
397
|
+
/**
|
|
398
|
+
* THE CATEGORIES people browse by (1Password's left list), instead of the catalog's 130 raw types.
|
|
399
|
+
* Every type lands in exactly one, by its key and family; the raw type still shows on each item.
|
|
400
|
+
*/
|
|
401
|
+
declare const CATEGORIES: readonly [{
|
|
402
|
+
readonly id: "logins";
|
|
403
|
+
readonly label: "Logins";
|
|
404
|
+
}, {
|
|
405
|
+
readonly id: "passkeys";
|
|
406
|
+
readonly label: "Passkeys";
|
|
407
|
+
}, {
|
|
408
|
+
readonly id: "api";
|
|
409
|
+
readonly label: "API keys & tokens";
|
|
410
|
+
}, {
|
|
411
|
+
readonly id: "oauth";
|
|
412
|
+
readonly label: "Apps & OAuth";
|
|
413
|
+
}, {
|
|
414
|
+
readonly id: "infra";
|
|
415
|
+
readonly label: "Servers & databases";
|
|
416
|
+
}, {
|
|
417
|
+
readonly id: "env";
|
|
418
|
+
readonly label: "Environment values";
|
|
419
|
+
}, {
|
|
420
|
+
readonly id: "files";
|
|
421
|
+
readonly label: "Files & certificates";
|
|
422
|
+
}, {
|
|
423
|
+
readonly id: "other";
|
|
424
|
+
readonly label: "Other";
|
|
425
|
+
}];
|
|
426
|
+
type CategoryId = (typeof CATEGORIES)[number]["id"];
|
|
427
|
+
declare function categoryOf(key: string, family: string): CategoryId;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* BROWSERS THAT CAN FILL (access ladder T-30). Every AI Matrx extension install (or the macOS
|
|
431
|
+
* password app) that may fill a saved password has its own key, registered the first time it fills
|
|
432
|
+
* with a step-up: the person's password, or a passkey approval of that key from an aimatrx.com page.
|
|
433
|
+
* The server audits every registration, approval, fill and turn-off; turned-off devices stay listed.
|
|
434
|
+
*/
|
|
435
|
+
/** What is asking to fill: the browser extension, or the macOS password app. */
|
|
436
|
+
type FillDeviceKind = "browser" | "mac";
|
|
437
|
+
/** One browser (or Mac) that may fill saved passwords. Never carries key material. */
|
|
438
|
+
interface FillDevice {
|
|
439
|
+
id: string;
|
|
440
|
+
label: string;
|
|
441
|
+
/** The extension origin that registered it (`chrome-extension://…`), when known. */
|
|
442
|
+
origin: string | null;
|
|
443
|
+
addedAt: string | null;
|
|
444
|
+
lastUsedAt: string | null;
|
|
445
|
+
/** Set once it was turned off. A turned-off device stays in the list. */
|
|
446
|
+
revokedAt: string | null;
|
|
447
|
+
revokeReason: string | null;
|
|
448
|
+
keyThumbprint: string;
|
|
449
|
+
}
|
|
450
|
+
/** How this person can confirm it is them to turn filling on. */
|
|
451
|
+
interface FillStepUpMethods {
|
|
452
|
+
password: boolean;
|
|
453
|
+
passkey: boolean;
|
|
454
|
+
}
|
|
455
|
+
/** One key to approve: what the extension (or Mac app) put in the approval link. */
|
|
456
|
+
interface FillApprovalRequest {
|
|
457
|
+
/** SHA-256 JWK thumbprint, 64 lowercase hex characters. */
|
|
458
|
+
keyThumbprint: string;
|
|
459
|
+
label: string | null;
|
|
460
|
+
kind: FillDeviceKind;
|
|
461
|
+
}
|
|
462
|
+
/** The extension's own registration of its public key (the password step-up path). */
|
|
463
|
+
interface FillDeviceRegistration {
|
|
464
|
+
publicKeyJwk: Record<string, string>;
|
|
465
|
+
label: string;
|
|
466
|
+
origin: string | null;
|
|
467
|
+
/** The person's password as the step-up; omit when a fresh passkey approval of this key exists. */
|
|
468
|
+
password: string | null;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* The passkey half (ONE WebAuthn stack: the auth server's passkeys, relying party `rpId`). Present
|
|
472
|
+
* only when the host supplied its auth client; `available()` says whether a ceremony can run on
|
|
473
|
+
* this page at all (passkeys belong to one domain).
|
|
474
|
+
*/
|
|
475
|
+
interface FillPasskeys {
|
|
476
|
+
rpId: string;
|
|
477
|
+
available(): boolean;
|
|
478
|
+
/** Runs one passkey ceremony and approves the key. The approval is single-use and expires in minutes. */
|
|
479
|
+
approve(request: FillApprovalRequest): Promise<{
|
|
480
|
+
expiresAt: string;
|
|
481
|
+
}>;
|
|
482
|
+
/** Adds a passkey to the signed-in account (the auth server's own ceremony). */
|
|
483
|
+
add(): Promise<void>;
|
|
484
|
+
}
|
|
485
|
+
interface DevicesPort {
|
|
486
|
+
/** Newest first, turned-off ones included. */
|
|
487
|
+
list(): Promise<FillDevice[]>;
|
|
488
|
+
/** Turns filling off for one device; returns it as the server now has it. */
|
|
489
|
+
revoke(deviceId: string): Promise<FillDevice>;
|
|
490
|
+
stepUpMethods(): Promise<FillStepUpMethods>;
|
|
491
|
+
/** For the extension host: register its key with a step-up. */
|
|
492
|
+
register(registration: FillDeviceRegistration): Promise<FillDevice>;
|
|
493
|
+
passkeys?: FillPasskeys;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* The slice of supabase-js `auth` the passkey half needs, so the package never depends on
|
|
497
|
+
* supabase-js itself. Hosts pass their own `supabase.auth`.
|
|
498
|
+
*/
|
|
499
|
+
interface PasskeyAuthLike {
|
|
500
|
+
passkey: {
|
|
501
|
+
startAuthentication(): Promise<{
|
|
502
|
+
data: {
|
|
503
|
+
challenge_id: string;
|
|
504
|
+
options: unknown;
|
|
505
|
+
} | null;
|
|
506
|
+
error: unknown;
|
|
507
|
+
}>;
|
|
508
|
+
};
|
|
509
|
+
registerPasskey(): Promise<{
|
|
510
|
+
error: {
|
|
511
|
+
message?: string;
|
|
512
|
+
} | null;
|
|
513
|
+
}>;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* WHAT THE BROWSER EXTENSION DOES FOR THE VAULT — types only.
|
|
518
|
+
*
|
|
519
|
+
* The package never touches a web page, a frame or a tab. Everything that does (reading the tab,
|
|
520
|
+
* finding the focused login field, filling it, catching a submitted login, this browser's own fill
|
|
521
|
+
* key) is the extension's work, offered here as host abilities. A missing ability hides its screen;
|
|
522
|
+
* none shows a dead control.
|
|
523
|
+
*
|
|
524
|
+
* Words are the package's: every outcome is a code, and the package says it (`page-logic.ts`). The
|
|
525
|
+
* one exception is `message` on a server refusal the package cannot word for itself.
|
|
526
|
+
*
|
|
527
|
+
* Live state comes two ways:
|
|
528
|
+
* - `page` is a SNAPSHOT: the host passes a new object when the tab, its matches or its focused
|
|
529
|
+
* field change (the host re-renders the workspace).
|
|
530
|
+
* - `capture` and `fillSetup` are STORES (`get` + `subscribe`): service-worker messages arrive at
|
|
531
|
+
* any time and only their card redraws.
|
|
532
|
+
*/
|
|
533
|
+
/** A small external store: `get` returns the same object until it changes (useSyncExternalStore). */
|
|
534
|
+
interface HostStore<T> {
|
|
535
|
+
get(): T;
|
|
536
|
+
subscribe(onChange: () => void): () => void;
|
|
537
|
+
}
|
|
538
|
+
/** Why the server's match read failed. */
|
|
539
|
+
type PageMatchError = "sign_in_required" | "forbidden" | "fill_setup_required" | "server_error";
|
|
540
|
+
/** The server's "which logins may fill this page" read. */
|
|
541
|
+
type PageMatchState = {
|
|
542
|
+
status: "loading";
|
|
543
|
+
} | {
|
|
544
|
+
status: "ready";
|
|
545
|
+
} | {
|
|
546
|
+
status: "error";
|
|
547
|
+
reason: PageMatchError;
|
|
548
|
+
retry: () => void;
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Why Fill cannot run on this page right now. Absent = it can.
|
|
552
|
+
* - `restricted`: a browser page, or not https (filling never targets it)
|
|
553
|
+
* - `unavailable`: the extension cannot reach the page right now
|
|
554
|
+
* - `no_field`: no login box is focused yet ("click the username box, then Fill")
|
|
555
|
+
* - `matching_off`: saved-login matching is off in the extension's settings
|
|
556
|
+
* - `checking`: the extension is still looking at the page
|
|
557
|
+
*/
|
|
558
|
+
type PageBlock = "restricted" | "unavailable" | "no_field" | "matching_off" | "checking";
|
|
559
|
+
/** What a Fill did. */
|
|
560
|
+
type FillOutcome = "filled" | "partial" | "no_field" | "unavailable" | "not_started";
|
|
561
|
+
/** What "Sign in for me" did (the agent's own credential_login on this tab). */
|
|
562
|
+
type SignInOutcome = "signed_in" | "needs_code" | "challenge" | "rejected" | "several" | "no_match" | "unsafe" | "signed_out" | "unsupported" | "unknown";
|
|
563
|
+
/**
|
|
564
|
+
* The tab the side panel sits beside. A SUPERSET of today's `VaultHostCapabilities.page`
|
|
565
|
+
* (`url` + `matchingItemIds`), so `ItemList`'s "This page" keeps working unchanged.
|
|
566
|
+
*/
|
|
567
|
+
interface ExtensionPage {
|
|
568
|
+
/** The tab's address ("" when no tab). */
|
|
569
|
+
url: string;
|
|
570
|
+
/** The logins the SERVER says may fill this page, best first. */
|
|
571
|
+
matchingItemIds: readonly string[];
|
|
572
|
+
/** The match read itself. Absent = settled (`ready`). */
|
|
573
|
+
matchState?: PageMatchState;
|
|
574
|
+
/**
|
|
575
|
+
* Of `matchingItemIds`, the ones a Fill would put into the focused field right now (the focused
|
|
576
|
+
* frame's own offer). Absent = every match.
|
|
577
|
+
*/
|
|
578
|
+
fillableItemIds?: readonly string[];
|
|
579
|
+
/** Why Fill cannot run here. Absent = it can. */
|
|
580
|
+
blocked?: PageBlock;
|
|
581
|
+
/** "Sign in for me": fill AND submit, top frame only. Absent = not offered here. */
|
|
582
|
+
signIn?: (itemId: string) => Promise<SignInOutcome>;
|
|
583
|
+
/** Read the page and its matches again. */
|
|
584
|
+
refresh?: () => void;
|
|
585
|
+
}
|
|
586
|
+
/** Proposed replacement for `VaultHostCapabilities.fill` (today `Promise<void>`): it says what happened. */
|
|
587
|
+
type ExtensionFill = (itemId: string) => Promise<FillOutcome>;
|
|
588
|
+
/** A login the person just submitted on a page. Metadata only: the password stays in the host. */
|
|
589
|
+
interface CapturePrompt {
|
|
590
|
+
id: string;
|
|
591
|
+
/** The site, as people read it (`github.com`). */
|
|
592
|
+
host: string;
|
|
593
|
+
/** The username the page showed (not secret), so the person knows which account. */
|
|
594
|
+
username: string | null;
|
|
595
|
+
/** Saved logins for this site the server approved as update targets. */
|
|
596
|
+
existing: readonly {
|
|
597
|
+
itemId: string;
|
|
598
|
+
name: string;
|
|
599
|
+
}[];
|
|
600
|
+
/** The host could not keep the login in trusted memory: it can only be saved by hand. */
|
|
601
|
+
unavailable?: boolean;
|
|
602
|
+
}
|
|
603
|
+
type CaptureDecision = {
|
|
604
|
+
action: "save";
|
|
605
|
+
} | {
|
|
606
|
+
action: "update";
|
|
607
|
+
itemId: string;
|
|
608
|
+
} | {
|
|
609
|
+
action: "dismiss";
|
|
610
|
+
} | {
|
|
611
|
+
action: "never";
|
|
612
|
+
};
|
|
613
|
+
type CaptureResult = "saved" | "updated" | "dismissed" | "never" | "expired" | "sign_in_required" | "error";
|
|
614
|
+
interface CaptureHost extends HostStore<CapturePrompt | null> {
|
|
615
|
+
/** The host writes the password to the vault itself; the package only sends the choice. */
|
|
616
|
+
decide(promptId: string, decision: CaptureDecision): Promise<CaptureResult>;
|
|
617
|
+
}
|
|
618
|
+
interface FillSetupState {
|
|
619
|
+
/** `on`/`signed_out`/`loading` draw nothing; `off` draws the card. */
|
|
620
|
+
status: "loading" | "on" | "off" | "signed_out";
|
|
621
|
+
/** How this person can confirm it's them. null = not read yet (password is offered meanwhile). */
|
|
622
|
+
methods: {
|
|
623
|
+
password: boolean;
|
|
624
|
+
passkey: boolean;
|
|
625
|
+
} | null;
|
|
626
|
+
/** The web page that approves this browser with a passkey, and the code it shows. null = not ready. */
|
|
627
|
+
passkey: {
|
|
628
|
+
url: string;
|
|
629
|
+
code: string;
|
|
630
|
+
} | null;
|
|
631
|
+
/** The person went to approve with a passkey (outlives a remount of the card). */
|
|
632
|
+
awaitingPasskey: boolean;
|
|
633
|
+
}
|
|
634
|
+
type FillSetupResult = {
|
|
635
|
+
ok: true;
|
|
636
|
+
} | {
|
|
637
|
+
ok: false;
|
|
638
|
+
reason: "sign_in_required" | "not_approved" | "failed";
|
|
639
|
+
message?: string;
|
|
640
|
+
};
|
|
641
|
+
interface FillSetupHost extends HostStore<FillSetupState> {
|
|
642
|
+
/** The password travels once, straight from the box to the host; the package keeps no copy. */
|
|
643
|
+
turnOnWithPassword(password: string): Promise<FillSetupResult>;
|
|
644
|
+
/** Opens the passkey approval page (the host's own outbound door) and marks `awaitingPasskey`. */
|
|
645
|
+
openPasskey(): void;
|
|
646
|
+
/** Claims a passkey approval made on the web page. */
|
|
647
|
+
finishPasskey(): Promise<FillSetupResult>;
|
|
648
|
+
}
|
|
649
|
+
/** One set of new-password boxes on the page (one form in one frame). */
|
|
650
|
+
interface NewPasswordTarget {
|
|
651
|
+
id: string;
|
|
652
|
+
/** Where the boxes are (`https://github.com`). */
|
|
653
|
+
origin: string;
|
|
654
|
+
/** How many boxes it fills (a password and its confirmation = 2). */
|
|
655
|
+
fieldCount: number;
|
|
656
|
+
/** true when the boxes are in the page itself, not an embedded frame. */
|
|
657
|
+
topFrame: boolean;
|
|
658
|
+
}
|
|
659
|
+
type NewPasswordFind = {
|
|
660
|
+
status: "ready";
|
|
661
|
+
targets: readonly NewPasswordTarget[];
|
|
662
|
+
} | {
|
|
663
|
+
status: "none" | "stale" | "unavailable" | "unsafe";
|
|
664
|
+
};
|
|
665
|
+
type NewPasswordFillOutcome = "filled" | "partial" | "stale" | "unavailable" | "refused";
|
|
666
|
+
interface NewPasswordFieldsHost {
|
|
667
|
+
/** Finds new-password boxes on the page. */
|
|
668
|
+
find(): Promise<NewPasswordFind>;
|
|
669
|
+
/** Puts `value` into one target's boxes. The other targets are dropped by the host. */
|
|
670
|
+
use(targetId: string, value: string): Promise<NewPasswordFillOutcome>;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/** A metadata-only change to one field. The value never travels here. Absent = unchanged. */
|
|
674
|
+
interface FieldSettingsPatch {
|
|
675
|
+
/** Sealing is permanent: the server refuses any change away from `sealed`. */
|
|
676
|
+
handling?: FieldHandling;
|
|
677
|
+
/** null removes the env key. */
|
|
678
|
+
envKey?: string | null;
|
|
679
|
+
injectIntoSandbox?: boolean;
|
|
680
|
+
editable?: boolean;
|
|
681
|
+
/** "" clears it. */
|
|
682
|
+
description?: string;
|
|
683
|
+
active?: boolean;
|
|
684
|
+
}
|
|
685
|
+
/** Item-level settings that are not part of editing the item's content. Absent = unchanged. */
|
|
686
|
+
interface ItemSettingsPatch {
|
|
687
|
+
uriMatchMode?: UriMatchMode;
|
|
688
|
+
browserFillEnabled?: boolean;
|
|
689
|
+
/** The complete list of websites (used to add a promoted address). */
|
|
690
|
+
loginUrls?: string[];
|
|
691
|
+
}
|
|
692
|
+
interface NewFieldInput {
|
|
693
|
+
key: string;
|
|
694
|
+
value: string;
|
|
695
|
+
handling: FieldHandling;
|
|
696
|
+
envKey?: string | null;
|
|
697
|
+
injectIntoSandbox?: boolean;
|
|
698
|
+
description?: string;
|
|
699
|
+
}
|
|
700
|
+
/** The server's advisory look at a website. It never decides whether an item may be saved. */
|
|
701
|
+
interface DestinationResult {
|
|
702
|
+
normalizedUrl: string;
|
|
703
|
+
state: "reachable" | "not_found" | "responded" | "unreachable" | "invalid";
|
|
704
|
+
httpStatus: number | null;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Per-field and per-item settings: handling, sealing, env key, sandbox, description, active,
|
|
708
|
+
* values for recovery codes, website matching and browser fill. Every write answers with the item
|
|
709
|
+
* as the server now has it, so the screen never guesses.
|
|
710
|
+
*/
|
|
711
|
+
interface FieldsPort {
|
|
712
|
+
updateField(item: VaultItem, fieldId: string, patch: FieldSettingsPatch): Promise<VaultItem>;
|
|
713
|
+
/** Replaces one field's value (recovery codes after "Mark used", a replaced set). */
|
|
714
|
+
setValue(item: VaultItem, fieldId: string, value: string): Promise<VaultItem>;
|
|
715
|
+
addField(item: VaultItem, input: NewFieldInput): Promise<VaultItem>;
|
|
716
|
+
removeField(item: VaultItem, fieldId: string): Promise<VaultItem>;
|
|
717
|
+
/** Replaces several values at once, by field key, as one change (the server's rotate). */
|
|
718
|
+
rotate(item: VaultItem, values: Record<string, string>): Promise<VaultItem>;
|
|
719
|
+
updateItem(item: VaultItem, patch: ItemSettingsPatch): Promise<VaultItem>;
|
|
720
|
+
/** `item` decides the organization the check runs under; omit for the signed-in context. */
|
|
721
|
+
checkDestination(url: string, item?: VaultItem): Promise<DestinationResult>;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** What the server will take from a passkey exchange file. */
|
|
725
|
+
interface NativeImportLimits {
|
|
726
|
+
maxFileBytes: number;
|
|
727
|
+
maxItems: number;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* The desktop shell's passkey-file importer, as the web layer sees it. Only the desktop shell can
|
|
732
|
+
* open the file chooser, parse the exchange file and hold the keys; it talks to the server itself
|
|
733
|
+
* (`account.importPasskey`). The panel only ever receives counts and titles — never a value.
|
|
734
|
+
*/
|
|
735
|
+
type NativeImportRequest = {
|
|
736
|
+
action: "begin_file_import";
|
|
737
|
+
organization_id: string;
|
|
738
|
+
} | {
|
|
739
|
+
action: "recover";
|
|
740
|
+
} | {
|
|
741
|
+
action: "status" | "cancel";
|
|
742
|
+
operation_id: string;
|
|
743
|
+
} | {
|
|
744
|
+
action: "preview";
|
|
745
|
+
operation_id: string;
|
|
746
|
+
offset: number;
|
|
747
|
+
} | {
|
|
748
|
+
action: "choose_scope";
|
|
749
|
+
operation_id: string;
|
|
750
|
+
organization_id: string;
|
|
751
|
+
} | {
|
|
752
|
+
action: "confirm";
|
|
753
|
+
operation_id: string;
|
|
754
|
+
preview_digest: string;
|
|
755
|
+
};
|
|
756
|
+
type NativeImportPhase = "authorizing" | "preview" | "awaiting_confirmation" | "importing" | "partial" | "completed" | "cancelled" | "failed" | "unavailable";
|
|
757
|
+
type NativeImportDisposition = "eligible" | "unsupported" | "committed" | "failed" | "uncertain" | "not_attempted";
|
|
758
|
+
interface NativeImportStatus {
|
|
759
|
+
operation_id: string;
|
|
760
|
+
phase: NativeImportPhase;
|
|
761
|
+
total: number;
|
|
762
|
+
committed: number;
|
|
763
|
+
already_present: number;
|
|
764
|
+
unsupported: number;
|
|
765
|
+
failed: number;
|
|
766
|
+
uncertain: number;
|
|
767
|
+
not_attempted: number;
|
|
768
|
+
message: string;
|
|
769
|
+
}
|
|
770
|
+
interface NativeImportPreview {
|
|
771
|
+
operation_id: string;
|
|
772
|
+
preview_digest: string;
|
|
773
|
+
offset: number;
|
|
774
|
+
total: number;
|
|
775
|
+
slots: {
|
|
776
|
+
slot_id: string;
|
|
777
|
+
title: string | null;
|
|
778
|
+
disposition: NativeImportDisposition;
|
|
779
|
+
reason: string | null;
|
|
780
|
+
}[];
|
|
781
|
+
}
|
|
782
|
+
/** What the desktop shell supplies (Tauri `native_vault_file_import`). */
|
|
783
|
+
interface NativeImportBridge {
|
|
784
|
+
request(request: NativeImportRequest): Promise<NativeImportStatus | NativeImportPreview>;
|
|
785
|
+
/** Fires when the signed-in account or organization changes under a running import. Returns the unsubscribe. */
|
|
786
|
+
subscribe?(listener: () => void): () => void;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* THE organization settings the vault obeys (`vault.generator.*`, `vault.import.*`): every limit is
|
|
791
|
+
* a knob an organization sets, never a number in this package. Hosts build it once over their own
|
|
792
|
+
* Supabase client; one round trip, then lookups.
|
|
793
|
+
*/
|
|
794
|
+
type VaultKnobs = () => Promise<Readonly<Record<string, unknown>>>;
|
|
795
|
+
declare function createVaultKnobs(client: KnobClient, address: {
|
|
796
|
+
organizationId: string;
|
|
797
|
+
userId: string;
|
|
798
|
+
}): VaultKnobs;
|
|
799
|
+
|
|
800
|
+
/** Bringing items in and taking them out: imports, login CSV export, encrypted backup and restore. */
|
|
801
|
+
|
|
802
|
+
/** The formats the server writes a login CSV in. */
|
|
803
|
+
type LoginCsvProfile = account.VaultLoginCsvPreviewRequest["profile"];
|
|
804
|
+
type LoginCsvPreview = account.VaultLoginCsvPreviewResponse;
|
|
805
|
+
type BackupPreview = account.VaultBackupDownloadPreview;
|
|
806
|
+
type RestorePreview = account.VaultBackupRestorePreview;
|
|
807
|
+
type RestoreResult$1 = account.VaultBackupRestoreResult;
|
|
808
|
+
/** What the server created for one imported record. */
|
|
809
|
+
interface CreatedItem {
|
|
810
|
+
id: string;
|
|
811
|
+
name: string;
|
|
812
|
+
}
|
|
813
|
+
interface IoPort {
|
|
814
|
+
/** One imported record. The key makes a retry safe: the server answers a repeat with the first result. */
|
|
815
|
+
createImported(body: account.VaultItemCreateRequest, organizationId: string | null, idempotencyKey: string): Promise<CreatedItem>;
|
|
816
|
+
/** `KEY=value` text: one item per key; an existing key is updated in place. */
|
|
817
|
+
importEnv(envText: string, organizationId: string | null, injectIntoSandbox: boolean): Promise<CreatedItem[]>;
|
|
818
|
+
previewLoginCsv(profile: LoginCsvProfile, itemIds: readonly string[]): Promise<LoginCsvPreview>;
|
|
819
|
+
/** The CSV text. `revision` ties it to the preview the person just read. */
|
|
820
|
+
downloadLoginCsv(profile: LoginCsvProfile, itemIds: readonly string[], revision: string): Promise<string>;
|
|
821
|
+
previewBackup(itemIds: readonly string[]): Promise<BackupPreview>;
|
|
822
|
+
/** The encrypted backup file's bytes. */
|
|
823
|
+
downloadBackup(args: {
|
|
824
|
+
itemIds: readonly string[];
|
|
825
|
+
revision: string;
|
|
826
|
+
passphrase: string;
|
|
827
|
+
acceptOmissions: boolean;
|
|
828
|
+
}): Promise<Uint8Array<ArrayBuffer>>;
|
|
829
|
+
previewRestore(envelopeBase64: string, passphrase: string): Promise<RestorePreview>;
|
|
830
|
+
restoreBackup(args: {
|
|
831
|
+
envelopeBase64: string;
|
|
832
|
+
passphrase: string;
|
|
833
|
+
preview: RestorePreview;
|
|
834
|
+
restoreRunId: string;
|
|
835
|
+
acceptOmissions: boolean;
|
|
836
|
+
}): Promise<RestoreResult$1>;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/** What a restore brought back. The item returns disabled: sharing and automatic use stay off. */
|
|
840
|
+
interface RestoreResult {
|
|
841
|
+
item: VaultItem;
|
|
842
|
+
restoredFields: number;
|
|
843
|
+
restoredAttachments: number;
|
|
844
|
+
restoredPasskeys: number;
|
|
845
|
+
alreadyRestored: boolean;
|
|
846
|
+
}
|
|
847
|
+
/** Why a trashed item can't be brought back automatically. */
|
|
848
|
+
type RecoveryRefusal = "recovery_tracking_unavailable" | "recovery_manifest_unsupported" | "protected_component_requires_native_recovery" | "linked_component_requires_native_recovery" | "native_recovery_unavailable" | "native_components_missing" | "native_components_conflict" | "native_manifest_invalid";
|
|
849
|
+
/** What a restore would bring back. Counts only — the server never returns a value here. */
|
|
850
|
+
type RecoveryPreview = {
|
|
851
|
+
supported: true;
|
|
852
|
+
deletionId: string;
|
|
853
|
+
fields: number;
|
|
854
|
+
attachments: number;
|
|
855
|
+
passkeys: 0 | 1;
|
|
856
|
+
priorWasDisabled: boolean;
|
|
857
|
+
} | {
|
|
858
|
+
supported: false;
|
|
859
|
+
deletionId: string | null;
|
|
860
|
+
reason: RecoveryRefusal;
|
|
861
|
+
priorWasDisabled: boolean | null;
|
|
862
|
+
};
|
|
863
|
+
/** Favorites: the person's own star, across every host. */
|
|
864
|
+
interface FavoritesPort {
|
|
865
|
+
list(): Promise<string[]>;
|
|
866
|
+
set(itemId: string, on: boolean): Promise<void>;
|
|
867
|
+
}
|
|
868
|
+
/** Recently opened: newest first. */
|
|
869
|
+
interface RecentsPort {
|
|
870
|
+
list(): Promise<string[]>;
|
|
871
|
+
record(itemId: string): Promise<void>;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* The per-person state door (`platform.user_entity_state` through the public `ues_*` functions):
|
|
875
|
+
* anything with a supabase-js style `rpc`. The server API has no endpoint for it (see FEATURE.md),
|
|
876
|
+
* so the HOST supplies it; without it `favorites` and `recents` are absent and their controls hide.
|
|
877
|
+
*/
|
|
878
|
+
interface VaultUserStateDoor {
|
|
879
|
+
rpc(fn: string, args?: Record<string, unknown>): PromiseLike<{
|
|
880
|
+
data?: unknown;
|
|
881
|
+
error?: {
|
|
882
|
+
message?: string;
|
|
883
|
+
} | null;
|
|
884
|
+
}>;
|
|
885
|
+
}
|
|
886
|
+
/** List-level state: trash and restore, favorites, recently opened. */
|
|
887
|
+
interface ListsPort {
|
|
888
|
+
/** Brings a trashed item back (disabled). `deletionId` is the one the preview named; without it the port asks again. May reject with a recent-sign-in refusal. */
|
|
889
|
+
restore(item: VaultItem, deletionId?: string): Promise<RestoreResult>;
|
|
890
|
+
/** What a restore would bring back, before anything changes. Absent when the host gives no state door. */
|
|
891
|
+
preview?(item: VaultItem): Promise<RecoveryPreview>;
|
|
892
|
+
favorites?: FavoritesPort;
|
|
893
|
+
recents?: RecentsPort;
|
|
894
|
+
/** Everything of the vault's in Trash. The server's vault list leaves deleted items out, so this is the only way to see them. */
|
|
895
|
+
trash?: {
|
|
896
|
+
list(): Promise<VaultItem[]>;
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* What a person or an organization vault may do with a shared item — the server's two grant
|
|
902
|
+
* levels (`can_use`, `can_manage`). "use": sign in with it (and its agents), see the visible
|
|
903
|
+
* fields, never show the password. "edit": also show and change values. Only the owner (or an
|
|
904
|
+
* organization admin for an organization item) shares, moves or deletes — no grant gives that.
|
|
905
|
+
*/
|
|
906
|
+
type ShareLevel = "use" | "edit";
|
|
907
|
+
/** One entry on an item's access list: a person, or an organization vault it is shared into. */
|
|
908
|
+
interface ShareGrant {
|
|
909
|
+
id: string;
|
|
910
|
+
kind: "person" | "organization";
|
|
911
|
+
/** The person's email, or the organization's name. */
|
|
912
|
+
label: string;
|
|
913
|
+
userId: string | null;
|
|
914
|
+
organizationId: string | null;
|
|
915
|
+
level: ShareLevel;
|
|
916
|
+
createdAt: string;
|
|
917
|
+
}
|
|
918
|
+
/** A member's own access to an organization's vault. `null` follows the organization default. */
|
|
919
|
+
type MemberVaultAccess = "none" | "use" | "editor";
|
|
920
|
+
type EffectiveVaultAccess = MemberVaultAccess | "admin";
|
|
921
|
+
interface MemberAccessRow {
|
|
922
|
+
userId: string;
|
|
923
|
+
email: string;
|
|
924
|
+
role: string;
|
|
925
|
+
access: MemberVaultAccess | null;
|
|
926
|
+
effective: EffectiveVaultAccess;
|
|
927
|
+
}
|
|
928
|
+
interface MemberAccessList {
|
|
929
|
+
organizationId: string;
|
|
930
|
+
defaultAccess: MemberVaultAccess;
|
|
931
|
+
members: MemberAccessRow[];
|
|
932
|
+
}
|
|
933
|
+
/** Where an item can move or be copied to: a person's own vault (null) or one of their organizations. */
|
|
934
|
+
type VaultDestination = string | null;
|
|
935
|
+
interface MoveOutcome {
|
|
936
|
+
/** The item as the server has it now, in its new vault. */
|
|
937
|
+
item: VaultItem;
|
|
938
|
+
/** Field keys the server retired instead of moving (the one-time-password seed). */
|
|
939
|
+
notMoved: string[];
|
|
940
|
+
}
|
|
941
|
+
interface GiveOutcome {
|
|
942
|
+
recipientEmail: string;
|
|
943
|
+
notMoved: string[];
|
|
944
|
+
}
|
|
945
|
+
interface AssignInput {
|
|
946
|
+
recipientEmail: string;
|
|
947
|
+
definition: VaultDefinition;
|
|
948
|
+
draft: ItemDraft;
|
|
949
|
+
/** The server generates this field's value and never returns it (the creator never sees it). */
|
|
950
|
+
generateFieldKey: string | null;
|
|
951
|
+
}
|
|
952
|
+
interface AssignOutcome {
|
|
953
|
+
id: string;
|
|
954
|
+
name: string;
|
|
955
|
+
recipientEmail: string;
|
|
956
|
+
generated: boolean;
|
|
957
|
+
}
|
|
958
|
+
/** A protected field the server would let this sender include in a secure link. */
|
|
959
|
+
interface SecureLinkField {
|
|
960
|
+
key: string;
|
|
961
|
+
label: string;
|
|
962
|
+
available: boolean;
|
|
963
|
+
/** Why it cannot go (sealed, no reveal right…), in the server's words. */
|
|
964
|
+
reason: string | null;
|
|
965
|
+
}
|
|
966
|
+
/** What a secure link for this item could carry and how long its two halves live (organization knob). */
|
|
967
|
+
interface SecureLinkOptions {
|
|
968
|
+
fields: SecureLinkField[];
|
|
969
|
+
linkMinutes: number;
|
|
970
|
+
codeMinutes: number;
|
|
971
|
+
}
|
|
972
|
+
type SecureChannel = "email" | "sms";
|
|
973
|
+
interface SecureLinkInput {
|
|
974
|
+
email: string;
|
|
975
|
+
/** Their mobile, for the code. Without it the link and the code both go by email. */
|
|
976
|
+
phone: string | null;
|
|
977
|
+
name: string | null;
|
|
978
|
+
fieldKeys: readonly string[];
|
|
979
|
+
/** Which channel carries the link when both are given; the code takes the other. */
|
|
980
|
+
linkBy: SecureChannel | null;
|
|
981
|
+
note: string | null;
|
|
982
|
+
}
|
|
983
|
+
/** The server's receipt. Never the link and never a value: the link goes only to the recipient. */
|
|
984
|
+
interface SecureLinkReceipt {
|
|
985
|
+
id: string;
|
|
986
|
+
linkBy: SecureChannel;
|
|
987
|
+
codeBy: SecureChannel;
|
|
988
|
+
sameChannel: boolean;
|
|
989
|
+
expiresAt: string;
|
|
990
|
+
fieldKeys: string[];
|
|
991
|
+
sentence: string;
|
|
992
|
+
}
|
|
993
|
+
type SecureLinkStatus = "pending" | "viewed" | "revoked" | "expired" | "undeliverable" | "locked";
|
|
994
|
+
/** One link this person sent for this item (their own rows only). */
|
|
995
|
+
interface SentSecureLink {
|
|
996
|
+
id: string;
|
|
997
|
+
name: string | null;
|
|
998
|
+
email: string | null;
|
|
999
|
+
phone: string | null;
|
|
1000
|
+
linkBy: string;
|
|
1001
|
+
codeBy: string;
|
|
1002
|
+
fieldKeys: string[];
|
|
1003
|
+
status: SecureLinkStatus | string;
|
|
1004
|
+
createdAt: string;
|
|
1005
|
+
expiresAt: string;
|
|
1006
|
+
viewedAt: string | null;
|
|
1007
|
+
}
|
|
1008
|
+
/** Who can use an item: grants, organization access, member access, move, give ownership, assign, copy. */
|
|
1009
|
+
interface SharingPort {
|
|
1010
|
+
/** The item's current access list. Needs manage; load it before any change so nobody is dropped unseen. */
|
|
1011
|
+
grants(item: VaultItem): Promise<ShareGrant[]>;
|
|
1012
|
+
/** Share with ONE person by their exact email (the account must exist). Nobody else changes. */
|
|
1013
|
+
addPerson(item: VaultItem, email: string, level: ShareLevel): Promise<ShareGrant>;
|
|
1014
|
+
/** Share a personal item into one of the owner's organizations. It then lives in both vaults. */
|
|
1015
|
+
addOrganization(item: VaultItem, organizationId: string, level: ShareLevel): Promise<ShareGrant>;
|
|
1016
|
+
setLevel(item: VaultItem, grant: ShareGrant, level: ShareLevel): Promise<ShareGrant>;
|
|
1017
|
+
revoke(item: VaultItem, grant: ShareGrant): Promise<void>;
|
|
1018
|
+
/**
|
|
1019
|
+
* An organization item's mode: every member (at their own vault access) or only the people listed.
|
|
1020
|
+
* `all_members` clears every person on the list (server rule); `restricted` keeps `people`.
|
|
1021
|
+
*/
|
|
1022
|
+
setAccessMode(item: VaultItem, mode: "all_members" | "restricted", people: readonly ShareGrant[]): Promise<VaultItem>;
|
|
1023
|
+
/** Every member's access to an organization's vault. Owners and admins only. */
|
|
1024
|
+
memberAccess(organizationId: string): Promise<MemberAccessList>;
|
|
1025
|
+
setMemberAccess(organizationId: string, userId: string, access: MemberVaultAccess | null): Promise<EffectiveVaultAccess>;
|
|
1026
|
+
/** Move between the person's own vault and their organizations' vaults. Values are not copied. */
|
|
1027
|
+
move(item: VaultItem, to: VaultDestination): Promise<MoveOutcome>;
|
|
1028
|
+
/** Hand the item to another person. The sender loses all access; every share is removed. */
|
|
1029
|
+
giveOwnership(item: VaultItem, email: string): Promise<GiveOutcome>;
|
|
1030
|
+
/** An independent copy whose values can diverge. Needs edit on the item. */
|
|
1031
|
+
copy(item: VaultItem, to: VaultDestination): Promise<VaultItem>;
|
|
1032
|
+
/** Create a NEW item already owned by someone else (the new-item flow's "For someone else"). */
|
|
1033
|
+
assign(input: AssignInput): Promise<AssignOutcome>;
|
|
1034
|
+
/** Send by secure link: which protected fields may go, and the link's lifetimes. */
|
|
1035
|
+
secureLinkOptions(item: VaultItem): Promise<SecureLinkOptions>;
|
|
1036
|
+
/** Seals the chosen fields and sends the link to the recipient. May reject with a recent-sign-in refusal. */
|
|
1037
|
+
sendSecureLink(item: VaultItem, input: SecureLinkInput): Promise<SecureLinkReceipt>;
|
|
1038
|
+
/** Turns a pending link off at once; what it carried is deleted. Returns the server's sentence. */
|
|
1039
|
+
revokeSecureLink(item: VaultItem, deliveryId: string): Promise<string>;
|
|
1040
|
+
/** The links this person sent for the item. Present only when the host passes its database door. */
|
|
1041
|
+
sentSecureLinks?: (item: VaultItem) => Promise<SentSecureLink[]>;
|
|
1042
|
+
/** The texting-consented mobile on file for an existing user with this email, else null. Same door. */
|
|
1043
|
+
phoneOnFile?: (email: string) => Promise<string | null>;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/** A one-time-password reading: the code and where it sits in its period. */
|
|
1047
|
+
interface TotpReading {
|
|
1048
|
+
code: string;
|
|
1049
|
+
periodSeconds: number;
|
|
1050
|
+
remainingSeconds: number;
|
|
1051
|
+
}
|
|
1052
|
+
/** A save that landed. `notice` is set when part of it did not (e.g. the one-time password was refused). */
|
|
1053
|
+
interface SaveResult {
|
|
1054
|
+
item: VaultItem;
|
|
1055
|
+
notice: string | null;
|
|
1056
|
+
}
|
|
1057
|
+
interface VaultSnapshot {
|
|
1058
|
+
/** Parts that could not be read this time (an organization, shared items, one-time codes). */
|
|
1059
|
+
unavailable: readonly string[];
|
|
1060
|
+
items: readonly VaultItem[];
|
|
1061
|
+
/** Items in the trash (deleted, still recoverable). Never mixed into `items`. */
|
|
1062
|
+
deleted: readonly VaultItem[];
|
|
1063
|
+
organizations: readonly VaultOrganizationRef[];
|
|
1064
|
+
definitions: readonly VaultDefinition[];
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* The data seam. `createApiVaultPort` (this package) implements it over `@ai-matrx/api` for every
|
|
1068
|
+
* host — web app, extension, desktop — so there is ONE client. Every method may reject; the screens
|
|
1069
|
+
* show the failure and never swallow it. Each feature area owns its own slice (`authenticator`,
|
|
1070
|
+
* `sharing`, …) in its own folder.
|
|
1071
|
+
*/
|
|
1072
|
+
interface VaultPort {
|
|
1073
|
+
load(): Promise<VaultSnapshot>;
|
|
1074
|
+
/** The one path to a plaintext value. Audited by the server; handling `sealed` always refuses. */
|
|
1075
|
+
reveal(item: VaultItem, fieldKey: string): Promise<string>;
|
|
1076
|
+
/** `organizationId` null = the person's own vault. */
|
|
1077
|
+
create(definition: VaultDefinition, organizationId: string | null, draft: ItemDraft): Promise<SaveResult>;
|
|
1078
|
+
/** Applies the draft's changes; returns the item as the server now has it. */
|
|
1079
|
+
save(item: VaultItem, definition: VaultDefinition | null, draft: ItemDraft): Promise<SaveResult>;
|
|
1080
|
+
remove(item: VaultItem): Promise<void>;
|
|
1081
|
+
/** The item as the server has it now. */
|
|
1082
|
+
get(item: VaultItem): Promise<VaultItem>;
|
|
1083
|
+
authenticator: AuthenticatorPort;
|
|
1084
|
+
sharing: SharingPort;
|
|
1085
|
+
activity: ActivityPort;
|
|
1086
|
+
io: IoPort;
|
|
1087
|
+
fields: FieldsPort;
|
|
1088
|
+
lists: ListsPort;
|
|
1089
|
+
devices: DevicesPort;
|
|
1090
|
+
/** Desktop passkey import: the server's limits (and proof the organization may import). */
|
|
1091
|
+
native: {
|
|
1092
|
+
limits(organizationId: string): Promise<NativeImportLimits>;
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* What only some hosts can do. A missing capability hides its control; it never shows a dead one.
|
|
1097
|
+
* - Every host with a signed-in person supplies `identity` (confirm it's you before a secret shows).
|
|
1098
|
+
* - The browser extension supplies `page` (the tab's address, the logins that match it) and `fill`.
|
|
1099
|
+
* - The desktop app supplies `nativeProvider` (the system credential provider's state).
|
|
1100
|
+
*/
|
|
1101
|
+
interface VaultHostCapabilities {
|
|
1102
|
+
identity?: VaultIdentity;
|
|
1103
|
+
/** The organization's vault settings (`createVaultKnobs`): generator and import limits. Without it neither is offered. */
|
|
1104
|
+
knobs?: VaultKnobs;
|
|
1105
|
+
/** Extension: the tab beside the panel, its matches and state (`extension/host.ts`). */
|
|
1106
|
+
page?: ExtensionPage;
|
|
1107
|
+
fill?: ExtensionFill;
|
|
1108
|
+
/** Extension: the save-this-login prompt after a sign-in. */
|
|
1109
|
+
capture?: CaptureHost;
|
|
1110
|
+
/** Extension: whether filling is turned on in this browser, and turning it on. */
|
|
1111
|
+
fillSetup?: FillSetupHost;
|
|
1112
|
+
/** Extension: new-password boxes on the page, for the generator's "Fill page". */
|
|
1113
|
+
newPasswordFields?: NewPasswordFieldsHost;
|
|
1114
|
+
/** Desktop: the native passkey importer (file chooser, parsing and keys stay native). */
|
|
1115
|
+
nativeImport?: NativeImportBridge;
|
|
1116
|
+
openUrl?: (url: string) => void;
|
|
1117
|
+
nativeProvider?: {
|
|
1118
|
+
status: "ready" | "needs_setup" | "unavailable";
|
|
1119
|
+
openSetup: () => void;
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
type VaultLayout = "auto" | "full" | "split" | "stack";
|
|
1124
|
+
interface VaultAppProps {
|
|
1125
|
+
port: VaultPort;
|
|
1126
|
+
host?: VaultHostCapabilities;
|
|
1127
|
+
/** `auto` picks by the room the workspace is given: a side panel stacks, a window shows three panes. */
|
|
1128
|
+
layout?: VaultLayout;
|
|
1129
|
+
/**
|
|
1130
|
+
* Everything the address carries: vault, view, search, sort, the open item, edit/new, the new-item
|
|
1131
|
+
* address, the open panel. Controlled when given (the host writes it to the URL); the workspace
|
|
1132
|
+
* keeps its own copy when not. `push` = a step the Back button should undo; `replace` = typing.
|
|
1133
|
+
*/
|
|
1134
|
+
location?: VaultLocation;
|
|
1135
|
+
onLocationChange?: (next: VaultLocation, how: "push" | "replace") => void;
|
|
1136
|
+
/** The host's own toasts; without it the vault shows its own short notices. */
|
|
1137
|
+
notify?: Notify;
|
|
1138
|
+
}
|
|
1139
|
+
/** THE VAULT WORKSPACE: one component for the web app, the extension side panel and the desktop app. */
|
|
1140
|
+
declare function VaultApp(props: VaultAppProps): react.JSX.Element;
|
|
1141
|
+
|
|
1142
|
+
interface SharingPortOptions {
|
|
1143
|
+
/**
|
|
1144
|
+
* The host's database door on the `platform` schema (`supabase.schema("platform")`). It is the only
|
|
1145
|
+
* way to read the links a person sent (`secure_delivery_sent`, their own rows) and the recipient's
|
|
1146
|
+
* texting-consented mobile (`secure_delivery_recipient_phone`); the API has no route for either.
|
|
1147
|
+
* Without it the "Sent" list and the mobile pre-fill are absent; sending and turning off still work.
|
|
1148
|
+
*/
|
|
1149
|
+
platformDoor?: VaultUserStateDoor;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* THE vault client for every host: a VaultPort over the generated `@ai-matrx/api`. The web app, the
|
|
1154
|
+
* extension and the desktop app hand it their own `Api` (their own sign-in and organization) and get
|
|
1155
|
+
* identical behaviour; none of them keeps a hand-written vault client.
|
|
1156
|
+
*/
|
|
1157
|
+
|
|
1158
|
+
interface ApiVaultPortOptions {
|
|
1159
|
+
/** Overrides the organizations the vault lists (default: the signed-in person's own). */
|
|
1160
|
+
organizations?: readonly VaultOrganizationRef[];
|
|
1161
|
+
/** The host's per-person state door (its supabase client): turns on Favorites and Recently opened. */
|
|
1162
|
+
userState?: VaultUserStateDoor;
|
|
1163
|
+
/** The host's `supabase.schema("platform")`: turns on "Sent links" and the recipient's mobile in Send. */
|
|
1164
|
+
platformDoor?: SharingPortOptions["platformDoor"];
|
|
1165
|
+
/** The host's `supabase.auth`: turns on approving a browser with a passkey. */
|
|
1166
|
+
passkeys?: PasskeyAuthLike;
|
|
1167
|
+
}
|
|
1168
|
+
declare function createApiVaultPort(api: Api, options?: ApiVaultPortOptions): VaultPort;
|
|
1169
|
+
|
|
1170
|
+
/** Reads the organization's ceilings. Raw knob values are fine: they are validated here. */
|
|
1171
|
+
type GeneratorLimitsSource = () => Promise<{
|
|
1172
|
+
maxPasswordLength?: unknown;
|
|
1173
|
+
maxPassphraseWords?: unknown;
|
|
1174
|
+
} | null>;
|
|
1175
|
+
/**
|
|
1176
|
+
* An icon button beside a secret field that opens the generator. "Use" hands the value back and
|
|
1177
|
+
* closes it; closing without using drops the value.
|
|
1178
|
+
*/
|
|
1179
|
+
declare function GenerateButton({ onUse, label, limits, disabled }: {
|
|
1180
|
+
onUse: (value: string) => void;
|
|
1181
|
+
label?: string;
|
|
1182
|
+
limits?: GeneratorLimitsSource;
|
|
1183
|
+
disabled?: boolean;
|
|
1184
|
+
}): react.JSX.Element | null;
|
|
1185
|
+
|
|
1186
|
+
export { type ApiVaultPortOptions, CATEGORIES, type CategoryId, DEFAULT_LOCATION, type DefinitionField, type FieldHandling, type FieldKind, GenerateButton, type IdentityAccount, type IdentityMethod, type ItemDraft, type NoticeKind, type Notify, type SaveResult, type SupabaseAuthLike, type TotpReading, type UriMatchMode, type UrlGuess, VaultApp, type VaultAppProps, type VaultAttachment, type VaultAuthenticator, type VaultCustomField, type VaultDefinition, type VaultField, type VaultHostCapabilities, type VaultIdentity, type VaultItem, type VaultItemCapabilities, type VaultKnobs, type VaultLayout, type VaultLocation, type VaultOrganizationRef, type VaultOwner, type VaultPort, type VaultSelection, type VaultSnapshot, type VaultSort, type VaultUserStateDoor, categoryOf, createApiVaultPort, createSupabaseIdentity, createVaultKnobs, inferFromUrl, locationFromParams, locationToParams, matchesQuery, needsRecentSignIn, parseDefinitions, siteOf, sortItems };
|