@drakkar.software/octospaces-sdk 0.1.0 → 0.4.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/CHANGELOG.md +200 -0
- package/dist/index.d.ts +337 -273
- package/dist/index.js +812 -469
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/types.ts +47 -81
- package/src/index.ts +42 -28
- package/src/objects/objects.test.ts +55 -95
- package/src/objects/objects.ts +23 -136
- package/src/spaces/members.test.ts +10 -3
- package/src/spaces/members.ts +86 -49
- package/src/spaces/nodes.test.ts +225 -0
- package/src/spaces/nodes.ts +427 -0
- package/src/spaces/object-index.test.ts +127 -71
- package/src/spaces/object-index.ts +61 -107
- package/src/spaces/registry.test.ts +59 -46
- package/src/spaces/registry.ts +28 -47
- package/src/sync/client.ts +20 -15
- package/src/sync/pairing.ts +10 -12
- package/src/sync/paths.test.ts +124 -16
- package/src/sync/paths.ts +73 -32
- package/src/sync/space-access-store.ts +17 -0
- package/src/sync/space-access.ts +112 -67
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WrappedKeyEntry } from '@drakkar.software/starfish-keyring';
|
|
2
|
+
import * as _drakkar_software_starfish_client from '@drakkar.software/starfish-client';
|
|
2
3
|
import { StarfishClient, Encryptor, StarfishCapProvider, PullCache } from '@drakkar.software/starfish-client';
|
|
3
4
|
import { CapCert, Base64Provider } from '@drakkar.software/starfish-protocol';
|
|
4
5
|
import { BootstrapOrigin, ScopePreset } from '@drakkar.software/starfish-identities';
|
|
@@ -89,19 +90,23 @@ declare function capProviderFor(cap: unknown, devEdPrivHex: string): StarfishCap
|
|
|
89
90
|
*/
|
|
90
91
|
declare function makeClient(cap: unknown, devEdPrivHex: string, namespaceOverride?: string): StarfishClient;
|
|
91
92
|
/**
|
|
92
|
-
* Open a
|
|
93
|
+
* Open a node's decryptor, throwing a descriptive error per failure mode
|
|
93
94
|
* (unreachable server / no keyring yet / not a recipient).
|
|
94
95
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
96
|
+
* `keyringPullPath` is the full `/pull/.../_keyring` path (e.g. from
|
|
97
|
+
* `keyringPull(spaceId)`). A `SpaceAccessError` is a hard access
|
|
98
|
+
* denial; any other thrown error is a transient offline state.
|
|
97
99
|
*/
|
|
98
|
-
declare function openEncryptor(client: StarfishClient, keys: DeviceKeys,
|
|
100
|
+
declare function openEncryptor(client: StarfishClient, keys: DeviceKeys, keyringPullPath: string, trustedAdders: string[]): Promise<Encryptor>;
|
|
99
101
|
/** Soft variant of {@link openEncryptor}: returns null instead of throwing. */
|
|
100
|
-
declare function buildEncryptor(client: StarfishClient, keys: DeviceKeys,
|
|
102
|
+
declare function buildEncryptor(client: StarfishClient, keys: DeviceKeys, keyringPullPath: string, trustedAdders: string[]): Promise<Encryptor | null>;
|
|
101
103
|
/**
|
|
102
|
-
* Owner-side: create
|
|
104
|
+
* Owner-side: create a per-node keyring if missing, return an encryptor.
|
|
105
|
+
*
|
|
106
|
+
* `keyringPullPath` / `keyringPushPath` are the full `/pull|push/.../_keyring`
|
|
107
|
+
* paths (e.g. from `keyringPull`/`keyringPush`).
|
|
103
108
|
*/
|
|
104
|
-
declare function ownerEnsureKeyring(client: StarfishClient, keys: DeviceKeys,
|
|
109
|
+
declare function ownerEnsureKeyring(client: StarfishClient, keys: DeviceKeys, keyringPullPath: string, keyringPushPath: string, trustedAdders?: string[]): Promise<Encryptor>;
|
|
105
110
|
/** A user's public profile: display pseudo + optional inline avatar + public identity keys. */
|
|
106
111
|
interface PublicProfile {
|
|
107
112
|
pseudo: string | null;
|
|
@@ -297,16 +302,14 @@ declare function sealToRecipient(session: Session, recipientKemPub: string, plai
|
|
|
297
302
|
declare function unsealFromRecipient(session: Session, blob: SealedBlob): Promise<string>;
|
|
298
303
|
|
|
299
304
|
type ID = string;
|
|
300
|
-
/** Maps a joined
|
|
301
|
-
*
|
|
302
|
-
*
|
|
305
|
+
/** Maps a joined space's id → its owner-issued member cap-cert (serialized JSON).
|
|
306
|
+
* Persisted both in device-local kv and, for durability, in the user's own synced
|
|
307
|
+
* `_spaces` doc so a fresh device re-hydrates it. */
|
|
303
308
|
type CapMap = Record<string, string>;
|
|
304
|
-
/** Maps a joined
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* doc. Recovered on any device with the same seed. See `account-seal.ts` and
|
|
309
|
-
* `space-access-store.ts`. */
|
|
309
|
+
/** Maps a joined link-access key → its sealed invitation credential (cap + ephemeral
|
|
310
|
+
* private key) SEALED to the account's own key. Keys are either `spaceId` (space-level
|
|
311
|
+
* link) or `${spaceId}:${nodeId}` (per-node invite link). Sealed because it embeds a
|
|
312
|
+
* bearer secret; recovered on any device with the same seed. */
|
|
310
313
|
type PubAccessMap = Record<string, SealedBlob>;
|
|
311
314
|
/** Maps a DM peer's userId → the private DM-space id shared with them. */
|
|
312
315
|
type DmMap = Record<string, string>;
|
|
@@ -325,8 +328,8 @@ type ReadValue = number;
|
|
|
325
328
|
interface ReadPrefs {
|
|
326
329
|
rooms: Record<string, ReadValue>;
|
|
327
330
|
}
|
|
328
|
-
/**
|
|
329
|
-
|
|
331
|
+
/** A joined or listed space. Visibility and encryption are per-node (see ObjectNode),
|
|
332
|
+
* not per-space — a space is a neutral container. */
|
|
330
333
|
interface Space {
|
|
331
334
|
id: ID;
|
|
332
335
|
name: string;
|
|
@@ -336,93 +339,51 @@ interface Space {
|
|
|
336
339
|
image?: string;
|
|
337
340
|
members: number;
|
|
338
341
|
unread?: number;
|
|
339
|
-
/** 'private' (E2EE keyring, the default) or 'public' (plaintext, joined via a
|
|
340
|
-
* space-wide invitation link). Absent ⇒ treat as 'private' (back-compat). */
|
|
341
|
-
visibility?: SpaceVisibility;
|
|
342
|
-
/** Public spaces only: the owner's userId (derived from the cap issuer). */
|
|
343
|
-
ownerId?: string;
|
|
344
|
-
/** Public spaces only (joiner side): whether this identity's invite link grants write. */
|
|
345
|
-
write?: boolean;
|
|
346
|
-
}
|
|
347
|
-
/** Legacy room kind — used while apps migrate their content onto objects.
|
|
348
|
-
* New code should use {@link RoomSubtype} directly. */
|
|
349
|
-
type RoomKind = 'channel' | 'dm' | 'automated';
|
|
350
|
-
/** Scheduled-fetch cadence for automated rooms. */
|
|
351
|
-
type AutomationSchedule = {
|
|
352
|
-
kind: 'interval';
|
|
353
|
-
everyMin: number;
|
|
354
|
-
} | {
|
|
355
|
-
kind: 'daily';
|
|
356
|
-
hour: number;
|
|
357
|
-
minute: number;
|
|
358
|
-
} | {
|
|
359
|
-
kind: 'weekly';
|
|
360
|
-
weekday: number;
|
|
361
|
-
hour: number;
|
|
362
|
-
minute: number;
|
|
363
|
-
} | {
|
|
364
|
-
kind: 'cron';
|
|
365
|
-
expression: string;
|
|
366
|
-
};
|
|
367
|
-
/** Stored, synced configuration of an `automated` room. */
|
|
368
|
-
interface AutomationMeta {
|
|
369
|
-
providerId: string;
|
|
370
|
-
params: Record<string, unknown>;
|
|
371
|
-
intervalMin: number;
|
|
372
|
-
schedule?: AutomationSchedule;
|
|
373
|
-
onOpen?: boolean;
|
|
374
|
-
enabled: boolean;
|
|
375
|
-
credential: SealedBlob;
|
|
376
|
-
botUserId?: string;
|
|
377
|
-
runOnDeviceId: string | null;
|
|
378
|
-
lastRunAt: number | null;
|
|
379
|
-
lastFetchHash?: string | null;
|
|
380
|
-
lastError: string | null;
|
|
381
342
|
}
|
|
382
|
-
/**
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
category: string;
|
|
387
|
-
name: string;
|
|
388
|
-
kind: RoomKind;
|
|
389
|
-
topic?: string;
|
|
390
|
-
unread?: number;
|
|
391
|
-
mention?: boolean;
|
|
392
|
-
avatar?: string;
|
|
393
|
-
automation?: AutomationMeta;
|
|
394
|
-
}
|
|
395
|
-
/** The builtin object types. Custom types ride the same `string` field. */
|
|
396
|
-
type BuiltinObjectType = 'room' | 'category' | 'automation' | 'doc' | 'project' | 'task';
|
|
397
|
-
type ObjectType = BuiltinObjectType | (string & {});
|
|
398
|
-
/** Runtime set of builtin type strings for renderer branching. */
|
|
399
|
-
declare const BUILTIN_OBJECT_TYPES: readonly BuiltinObjectType[];
|
|
400
|
-
/** How an object's content syncs. Builtins infer this; custom types set it explicitly. */
|
|
343
|
+
/** Any string an app assigns as an object's type. No builtins are defined here —
|
|
344
|
+
* each app declares its own type strings in its local SDK. */
|
|
345
|
+
type ObjectType = string;
|
|
346
|
+
/** How an object's content syncs. Apps may set this per-type explicitly. */
|
|
401
347
|
type ObjectContentKind = 'merge' | 'append' | 'none';
|
|
402
|
-
/**
|
|
403
|
-
|
|
348
|
+
/** Who may read a node's content (independent from whether it is E2EE):
|
|
349
|
+
* - `'public'` — world-readable; anonymous users may access the content. The node
|
|
350
|
+
* is listed in the global object directory (`_index/objects/{shard}`).
|
|
351
|
+
* - `'space'` — any member of the parent space. The default for new nodes.
|
|
352
|
+
* - `'invite'` — only members explicitly invited to this node (via its own per-node
|
|
353
|
+
* keyring for E2EE nodes, or via a per-node cap for plaintext nodes).
|
|
354
|
+
* Non-invited space members see a placeholder row (no title/emoji). */
|
|
355
|
+
type NodeAccess = 'public' | 'space' | 'invite';
|
|
404
356
|
/**
|
|
405
357
|
* One entry in a space's object index (`spaces/{spaceId}/objects/_index`).
|
|
406
358
|
* Identity + tree position + light metadata ONLY — heavy content (messages, doc
|
|
407
|
-
* blocks,
|
|
359
|
+
* blocks, event logs) lives in per-object content docs keyed by `id`.
|
|
360
|
+
*
|
|
361
|
+
* The index doc is always **plaintext** (member-gated). For `invite` nodes the
|
|
362
|
+
* `title` and `emoji` fields are stored empty in the index — only invited members
|
|
363
|
+
* can recover the real title from the node's content doc or encrypted keyring entry.
|
|
408
364
|
*/
|
|
409
365
|
interface ObjectNode {
|
|
410
366
|
id: ID;
|
|
411
367
|
type: ObjectType;
|
|
412
|
-
subtype?: RoomSubtype;
|
|
413
368
|
parentId: ID | null;
|
|
414
369
|
order: number;
|
|
415
370
|
title: string;
|
|
416
371
|
emoji?: string;
|
|
417
372
|
updatedAt: number;
|
|
418
373
|
archived?: boolean;
|
|
419
|
-
automation?: AutomationMeta;
|
|
420
374
|
contentKind?: ObjectContentKind;
|
|
375
|
+
/** Who may access this node's content. Absent ⇒ `'space'`. */
|
|
376
|
+
access?: NodeAccess;
|
|
377
|
+
/** True ⇒ this node's content is E2EE under the SPACE-WIDE keyring at
|
|
378
|
+
* `spaces/{spaceId}/_keyring`. All `enc` nodes in a space share one CEK.
|
|
379
|
+
* The combination `public + enc` is invalid. */
|
|
380
|
+
enc?: boolean;
|
|
381
|
+
/** App-specific fields. Apps store type-specific metadata here. */
|
|
421
382
|
meta?: Record<string, unknown>;
|
|
422
383
|
}
|
|
423
|
-
/** The object-index doc
|
|
384
|
+
/** The object-index doc stored at `spaces/{spaceId}/objects/_index`. */
|
|
424
385
|
interface ObjectsIndex {
|
|
425
|
-
v: 1;
|
|
386
|
+
v: 1 | 2;
|
|
426
387
|
objects: ObjectNode[];
|
|
427
388
|
updatedAt: number;
|
|
428
389
|
}
|
|
@@ -443,7 +404,7 @@ declare function randomId(): string;
|
|
|
443
404
|
declare function roomSlug(name: string): string;
|
|
444
405
|
|
|
445
406
|
/**
|
|
446
|
-
* Collection path + cap-scope helpers
|
|
407
|
+
* Collection path + cap-scope helpers for OctoSpaces.
|
|
447
408
|
*
|
|
448
409
|
* Paths are signed relative to SYNC_BASE; the server mounts the sync router at
|
|
449
410
|
* root, so they start with /pull or /push. Everything for a space is nested under
|
|
@@ -452,11 +413,17 @@ declare function roomSlug(name: string): string;
|
|
|
452
413
|
* covers a whole space.
|
|
453
414
|
*
|
|
454
415
|
* **Generic object collections** — scopes use the `obj*` collection names (the
|
|
455
|
-
* domain-neutral storage layer
|
|
456
|
-
*
|
|
457
|
-
*
|
|
416
|
+
* domain-neutral storage layer). The access record lives at
|
|
417
|
+
* `spaces/{spaceId}/_access` (collection `spaceregistry`); the space-wide keyring at
|
|
418
|
+
* `spaces/{spaceId}/_keyring` (collection `spacekeyring`). ONE keyring per space
|
|
419
|
+
* encrypts ALL the space's `enc` nodes.
|
|
420
|
+
*
|
|
421
|
+
* Note: `objinv` (invite-plaintext content) is intentionally EXCLUDED from
|
|
422
|
+
* OBJECT_COLLECTIONS / spaceMemberScope — only a per-node cap can reach it.
|
|
458
423
|
*/
|
|
459
424
|
|
|
425
|
+
/** Base name used as the `collectionName` arg to `addCollectionRecipient`.
|
|
426
|
+
* Appending `/_keyring` gives the full storage path. */
|
|
460
427
|
declare const keyringName: (spaceId: string) => string;
|
|
461
428
|
declare const keyringPull: (spaceId: string) => string;
|
|
462
429
|
declare const keyringPush: (spaceId: string) => string;
|
|
@@ -466,8 +433,8 @@ declare const profilePull: (userId: string) => string;
|
|
|
466
433
|
declare const profilePush: (userId: string) => string;
|
|
467
434
|
declare const spacesPull: (userId: string) => string;
|
|
468
435
|
declare const spacesPush: (userId: string) => string;
|
|
469
|
-
declare const
|
|
470
|
-
declare const
|
|
436
|
+
declare const spaceAccessPull: (spaceId: string) => string;
|
|
437
|
+
declare const spaceAccessPush: (spaceId: string) => string;
|
|
471
438
|
declare const objIndexPull: (spaceId: string) => string;
|
|
472
439
|
declare const objIndexPush: (spaceId: string) => string;
|
|
473
440
|
declare const objLogPull: (spaceId: string, objectId: string) => string;
|
|
@@ -476,18 +443,32 @@ declare const objDocPull: (spaceId: string, objectId: string) => string;
|
|
|
476
443
|
declare const objDocPush: (spaceId: string, objectId: string) => string;
|
|
477
444
|
declare const objectBlobPull: (spaceId: string, blobId: string) => string;
|
|
478
445
|
declare const objectBlobPush: (spaceId: string, blobId: string) => string;
|
|
446
|
+
declare const objPubName: (spaceId: string, nodeId: string) => string;
|
|
447
|
+
declare const objPubPull: (spaceId: string, nodeId: string) => string;
|
|
448
|
+
declare const objPubPush: (spaceId: string, nodeId: string) => string;
|
|
449
|
+
declare const objInvName: (spaceId: string, nodeId: string) => string;
|
|
450
|
+
declare const objInvPull: (spaceId: string, nodeId: string) => string;
|
|
451
|
+
declare const objInvPush: (spaceId: string, nodeId: string) => string;
|
|
479
452
|
declare const typesIndexPull: (spaceId: string) => string;
|
|
480
453
|
declare const typesIndexPush: (spaceId: string) => string;
|
|
481
|
-
declare const
|
|
454
|
+
declare const objectDirName: (shard?: string) => string;
|
|
455
|
+
declare const objectDirPull: (shard?: string) => string;
|
|
482
456
|
declare const OBJECT_COLLECTIONS: string[];
|
|
483
457
|
/** Full owner/device access to every space the identity owns. */
|
|
484
458
|
declare function ownerScope(): ScopePreset;
|
|
485
459
|
/**
|
|
486
|
-
* Member access to one SPACE —
|
|
487
|
-
* attachments
|
|
488
|
-
* covers current AND
|
|
460
|
+
* Member access to one SPACE — the space keyring, every node's content docs and
|
|
461
|
+
* attachments, all under `spaces/{spaceId}/**`. Does NOT cover `objinv` (invite-
|
|
462
|
+
* plaintext content) — use `nodeMemberScope` for that. One cap covers current AND
|
|
463
|
+
* future nodes.
|
|
489
464
|
*/
|
|
490
465
|
declare function spaceMemberScope(spaceId: string, canWrite: boolean): ScopePreset;
|
|
466
|
+
/**
|
|
467
|
+
* Narrow per-node cap for `invite+plaintext` nodes. Covers ONLY the node's `objinv`
|
|
468
|
+
* content path — the space keyring is space-wide and is covered by the broader space
|
|
469
|
+
* member scope. Use `spaceMemberScope` when the bearer also needs to decrypt enc content.
|
|
470
|
+
*/
|
|
471
|
+
declare function nodeMemberScope(spaceId: string, nodeId: string, canWrite: boolean): ScopePreset;
|
|
491
472
|
/**
|
|
492
473
|
* Personal cap: profile + space registry + device directory + all spaces.
|
|
493
474
|
* Note: app-specific collections like `'dminbox'` (chat) are NOT included here —
|
|
@@ -516,46 +497,62 @@ declare class SpaceAccessError extends Error {
|
|
|
516
497
|
}
|
|
517
498
|
|
|
518
499
|
/**
|
|
519
|
-
* Space access resolver
|
|
520
|
-
*
|
|
500
|
+
* Space and node access resolver.
|
|
501
|
+
*
|
|
502
|
+
* Encryption is per-node (each node has an `enc` flag) but keyed under ONE
|
|
503
|
+
* space-wide keyring at `spaces/{spaceId}/_keyring`. All `enc` nodes in a space
|
|
504
|
+
* share the same CEK; `access` gates *fetching*, the keyring gates *decryption*.
|
|
521
505
|
*
|
|
522
|
-
*
|
|
523
|
-
* `
|
|
506
|
+
* Two entry points:
|
|
507
|
+
* - `getSpaceClient` — returns the right StarfishClient for member-gated
|
|
508
|
+
* space docs (index, _access). No encryptor.
|
|
509
|
+
* - `getNodeAccess` — resolves the (client, encryptor) for a specific node's
|
|
510
|
+
* CONTENT. Encryptor is null for plaintext nodes; for enc nodes the encryptor
|
|
511
|
+
* opens the SPACE keyring (not a per-node keyring).
|
|
524
512
|
*
|
|
525
|
-
* Resolution order
|
|
526
|
-
* 1.
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
513
|
+
* Resolution order for `getNodeAccess`:
|
|
514
|
+
* 1. Per-node link entry → sign as ephemeral identity; encryptor from space keyring.
|
|
515
|
+
* 2. Per-node member entry → open space keyring as recipient.
|
|
516
|
+
* 3. Space-level link entry → same client; open space keyring if enc.
|
|
517
|
+
* 4. Space-level member entry → open space keyring if enc.
|
|
518
|
+
* 5. No entry, owner → mint space keyring if enc; plain client otherwise.
|
|
519
|
+
* 6. No entry, non-owner → SpaceAccessError if enc; plain client otherwise.
|
|
532
520
|
*/
|
|
533
521
|
|
|
534
|
-
interface
|
|
522
|
+
interface NodeAccessHandle {
|
|
535
523
|
encryptor: Encryptor | null;
|
|
536
524
|
client: StarfishClient;
|
|
537
|
-
/** True when opened as the space OWNER (
|
|
525
|
+
/** True when opened as the space OWNER (may seed / mint the space keyring). */
|
|
538
526
|
isOwnerOpen: boolean;
|
|
539
527
|
}
|
|
540
528
|
/** Drop every cached handle (on account switch — keys are per-identity). */
|
|
541
|
-
declare function
|
|
529
|
+
declare function clearNodeAccessCache(): void;
|
|
542
530
|
/**
|
|
543
|
-
*
|
|
531
|
+
* Return the right StarfishClient for reading/writing member-gated space docs
|
|
532
|
+
* (e.g. the `_index`, `_access`). Spaces are always plaintext — no encryptor.
|
|
533
|
+
*/
|
|
534
|
+
declare function getSpaceClient(spaceId: string, session: Session): StarfishClient;
|
|
535
|
+
/**
|
|
536
|
+
* Resolve the right (client, encryptor) for a node's CONTENT, opening and
|
|
537
|
+
* caching on first use.
|
|
544
538
|
*
|
|
545
|
-
* `
|
|
546
|
-
*
|
|
539
|
+
* `node` carries `{ access?, enc? }` — the plaintext flags from the index.
|
|
540
|
+
* `reg` is the space's access record if already known; used to determine
|
|
541
|
+
* ownership. Pass null if unknown.
|
|
547
542
|
*/
|
|
548
|
-
declare function
|
|
543
|
+
declare function getNodeAccess(spaceId: string, nodeId: string, node: {
|
|
544
|
+
access?: NodeAccess;
|
|
545
|
+
enc?: boolean;
|
|
546
|
+
}, session: Session, reg?: {
|
|
549
547
|
owner: string | null;
|
|
550
548
|
members: string[];
|
|
551
|
-
|
|
552
|
-
} | null): Promise<SpaceAccessHandle>;
|
|
549
|
+
} | null): Promise<NodeAccessHandle>;
|
|
553
550
|
/**
|
|
554
551
|
* SOFT resolve — never mints a keyring, never throws on missing access.
|
|
555
|
-
* Returns null when the identity has no usable access for the
|
|
552
|
+
* Returns null when the identity has no usable access for the node yet.
|
|
556
553
|
*/
|
|
557
|
-
declare function
|
|
558
|
-
|
|
554
|
+
declare function buildNodeAccess(session: Session, spaceId: string, nodeId: string, node: {
|
|
555
|
+
enc?: boolean;
|
|
559
556
|
}): Promise<{
|
|
560
557
|
client: StarfishClient;
|
|
561
558
|
encryptor: Encryptor | null;
|
|
@@ -604,6 +601,12 @@ declare function getSpaceAccessEntry(spaceId: string): SpaceAccessEntry | null;
|
|
|
604
601
|
declare function saveSpaceAccessEntry(spaceId: string, entry: SpaceAccessEntry): void;
|
|
605
602
|
/** Forget one space's access (on leaving that space). */
|
|
606
603
|
declare function removeSpaceAccessEntry(spaceId: string): void;
|
|
604
|
+
/** Look up a per-node invite access entry. Returns null if not invited or unknown. */
|
|
605
|
+
declare function getNodeAccessEntry(spaceId: string, nodeId: string): SpaceAccessEntry | null;
|
|
606
|
+
/** Persist an invite access entry for one node. */
|
|
607
|
+
declare function saveNodeAccessEntry(spaceId: string, nodeId: string, entry: SpaceAccessEntry): void;
|
|
608
|
+
/** Forget a node's invite access entry (e.g. on leaving the node). */
|
|
609
|
+
declare function removeNodeAccessEntry(spaceId: string, nodeId: string): void;
|
|
607
610
|
/** A snapshot of the in-memory cache — used by `recoverSpaceAccess` to find entries
|
|
608
611
|
* not yet on the server. */
|
|
609
612
|
declare function localSpaceAccessEntries(): SpaceAccessMap;
|
|
@@ -618,102 +621,11 @@ declare function linkAccessFromStore(): Record<string, {
|
|
|
618
621
|
/** Drop the in-memory cache (on account switch / sign-out). */
|
|
619
622
|
declare function clearSpaceAccessStore(): void;
|
|
620
623
|
|
|
621
|
-
/**
|
|
622
|
-
*
|
|
623
|
-
declare const DEFAULT_CATEGORY = "CHANNELS";
|
|
624
|
-
/** Deterministic category-node id from its name, so two devices that concurrently
|
|
625
|
-
* create the SAME category mint the SAME id → the union-merge dedupes them. */
|
|
626
|
-
declare const categoryId: (name: string) => ID;
|
|
627
|
-
/** A node plus its resolved children — the shape a tree view renders. */
|
|
628
|
-
interface ObjectTreeNode extends ObjectNode {
|
|
629
|
-
depth: number;
|
|
630
|
-
children: ObjectTreeNode[];
|
|
631
|
-
}
|
|
632
|
-
/** Map a legacy {@link Room} `kind` to the unified room {@link RoomSubtype}. */
|
|
633
|
-
declare function roomKindToSubtype(kind: Room['kind']): RoomSubtype;
|
|
634
|
-
/** Inverse of {@link roomKindToSubtype}. A legacy persisted `'stream'` subtype hits
|
|
635
|
-
* the `default` and reads back as a plain `'channel'` (normalization). */
|
|
636
|
-
declare function subtypeToRoomKind(subtype: RoomSubtype | undefined): Room['kind'];
|
|
637
|
-
/** The order value for a new node appended after `siblings`. */
|
|
638
|
-
declare function nextOrder(siblings: ObjectNode[]): number;
|
|
639
|
-
/**
|
|
640
|
-
* Build the render tree from a flat node list, repairing merge artifacts:
|
|
641
|
-
* - **archived** nodes (and their subtrees) are dropped.
|
|
642
|
-
* - **orphans** — a `parentId` that is missing or archived — reparent to root.
|
|
643
|
-
* - **cycles** — a node reachable from itself via `parentId` — reparent to root.
|
|
644
|
-
* - **siblings** sort by {@link compareSiblings} for cross-device determinism.
|
|
645
|
-
*/
|
|
646
|
-
declare function buildTree(nodes: ObjectNode[], includeArchived?: boolean): ObjectTreeNode[];
|
|
647
|
-
/** The root→node trail (inclusive) for breadcrumbs. Returns `[]` if unknown. */
|
|
648
|
-
declare function breadcrumbs(nodes: ObjectNode[], id: ID): ObjectNode[];
|
|
649
|
-
/** The root→parent trail (EXCLUSIVE of the node itself). */
|
|
650
|
-
declare function ancestors(nodes: ObjectNode[], id: ID): ObjectNode[];
|
|
651
|
-
/** The ids of a node and its whole subtree (for cascade-archive). */
|
|
652
|
-
declare function subtreeIds(nodes: ObjectNode[], rootId: ID): Set<ID>;
|
|
653
|
-
interface NewObjectInput {
|
|
654
|
-
type: ObjectType;
|
|
655
|
-
subtype?: RoomSubtype;
|
|
656
|
-
parentId?: ID | null;
|
|
657
|
-
title: string;
|
|
658
|
-
emoji?: string;
|
|
659
|
-
automation?: AutomationMeta;
|
|
660
|
-
/** Provide to reuse an id (e.g. a room id derived elsewhere); else minted. */
|
|
661
|
-
id?: ID;
|
|
662
|
-
}
|
|
663
|
-
/** Append a new node under `parentId` at the end of its sibling order. */
|
|
664
|
-
declare function addObject(nodes: ObjectNode[], input: NewObjectInput, now: number): {
|
|
665
|
-
nodes: ObjectNode[];
|
|
666
|
-
node: ObjectNode;
|
|
667
|
-
};
|
|
668
|
-
/** Patch a node's mutable metadata (title/emoji/automation), bumping `updatedAt`. */
|
|
669
|
-
declare function patchObject(nodes: ObjectNode[], id: ID, patch: Partial<Pick<ObjectNode, 'title' | 'emoji' | 'automation'>>, now: number): ObjectNode[];
|
|
670
|
-
/** Reparent a node (move in the tree). Rejects making a node its own descendant. */
|
|
671
|
-
declare function reparentObject(nodes: ObjectNode[], id: ID, parentId: ID | null, now: number): ObjectNode[];
|
|
672
|
-
/** Set explicit sibling order (drag-reorder). */
|
|
673
|
-
declare function reorderObjects(nodes: ObjectNode[], orderById: Record<ID, number>, now: number): ObjectNode[];
|
|
674
|
-
/** Cascade-archive a node and its whole subtree (soft delete). */
|
|
675
|
-
declare function archiveObject(nodes: ObjectNode[], id: ID, now: number): ObjectNode[];
|
|
676
|
-
/** The category→rooms grouping the legacy UI consumes. */
|
|
677
|
-
interface AdaptedCategory {
|
|
678
|
-
name: string;
|
|
679
|
-
rooms: Room[];
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Project the room/category nodes of an index into the legacy `{ name, rooms }[]`
|
|
683
|
-
* shape that app UIs still consume. Category nodes become buckets; room nodes become
|
|
684
|
-
* {@link Room}s grouped under their parent category (or `fallbackCategory` at root).
|
|
685
|
-
* Returns null when the index holds no room/category nodes yet.
|
|
686
|
-
*
|
|
687
|
-
* @deprecated Use the object tree directly once apps complete their migration.
|
|
688
|
-
*/
|
|
689
|
-
declare function objectsToRoomCategories(nodes: ObjectNode[], spaceId: string, fallbackCategory: string): AdaptedCategory[] | null;
|
|
690
|
-
/**
|
|
691
|
-
* Drop `kind: 'automated'` rooms from a category list (they belong to an Agents
|
|
692
|
-
* view, not the main room list). A category that held only agents is removed too.
|
|
693
|
-
*
|
|
694
|
-
* @deprecated Use the object tree directly once apps complete their migration.
|
|
695
|
-
*/
|
|
696
|
-
declare function excludeAutomatedRooms(categories: AdaptedCategory[]): AdaptedCategory[];
|
|
697
|
-
/** A minimal object descriptor the {@link seedIndexNodes} builder turns into nodes. */
|
|
698
|
-
interface SeedRoom {
|
|
699
|
-
id: ID;
|
|
700
|
-
name: string;
|
|
701
|
-
kind: Room['kind'];
|
|
702
|
-
category: string;
|
|
703
|
-
}
|
|
704
|
-
/**
|
|
705
|
-
* Build the initial `ObjectNode[]` for a brand-new space's index: a `category` node
|
|
706
|
-
* per distinct category and a `room` node per seed object parented under it. Pure +
|
|
707
|
-
* deterministic (category ids via {@link categoryId}).
|
|
708
|
-
*/
|
|
709
|
-
declare function seedIndexNodes(rooms: SeedRoom[], now: number): ObjectNode[];
|
|
710
|
-
|
|
711
|
-
/** Owner-set, SHARED space identity, persisted in the `_rooms` registry doc
|
|
712
|
-
* (plaintext — NOT E2EE). `image` is a data URI. Both optional for back-compat. */
|
|
624
|
+
/** Owner-set, SHARED space identity, persisted in the `_access` registry doc
|
|
625
|
+
* (plaintext — NOT E2EE). `image` is a data URI. All fields optional for back-compat. */
|
|
713
626
|
interface SpaceMeta {
|
|
714
627
|
name?: string | null;
|
|
715
628
|
image?: string | null;
|
|
716
|
-
visibility?: SpaceVisibility;
|
|
717
629
|
}
|
|
718
630
|
/** A resolved name/image update fanned out so the SpacesProvider adopts a
|
|
719
631
|
* freshly-reconciled value without waiting for its next navigation refresh. */
|
|
@@ -753,16 +665,14 @@ declare function updateArchivedDmsDoc(client: StarfishClient, userId: string, mu
|
|
|
753
665
|
declare function setDmMapping(client: StarfishClient, userId: string, peerUserId: string, spaceId: string): Promise<void>;
|
|
754
666
|
declare function writeSpaces(client: StarfishClient, userId: string, spaces: Space[], _hash: string | null): Promise<void>;
|
|
755
667
|
declare function reorderSpaces(client: StarfishClient, userId: string, order: string[]): Promise<void>;
|
|
756
|
-
declare function
|
|
757
|
-
declare function readRooms(client: StarfishClient, spaceId: string): Promise<{
|
|
668
|
+
declare function readSpaceAccess(client: StarfishClient, spaceId: string): Promise<{
|
|
758
669
|
owner: string | null;
|
|
759
670
|
members: string[];
|
|
760
|
-
visibility: SpaceVisibility | null;
|
|
761
671
|
name: string | null;
|
|
762
672
|
image: string | null;
|
|
763
673
|
hash: string | null;
|
|
764
674
|
}>;
|
|
765
|
-
declare function
|
|
675
|
+
declare function writeSpaceAccess(client: StarfishClient, spaceId: string, owner: string, members: string[], hash: string | null, meta?: SpaceMeta): Promise<void>;
|
|
766
676
|
declare function addSpaceMember(client: StarfishClient, spaceId: string, ownerUserId: string, memberUserId: string): Promise<void>;
|
|
767
677
|
/** Remove a member from the space roster (used for link revocation). */
|
|
768
678
|
declare function removeSpaceMember(client: StarfishClient, spaceId: string, memberUserId: string): Promise<void>;
|
|
@@ -770,16 +680,10 @@ declare function addJoinedSpace(client: StarfishClient, userId: string, space: S
|
|
|
770
680
|
declare function addJoinedSpaceWithCap(client: StarfishClient, userId: string, space: Space, capJson: string): Promise<void>;
|
|
771
681
|
declare function addJoinedSpaceWithLinkAccess(client: StarfishClient, userId: string, space: Space, sealed: SealedBlob): Promise<void>;
|
|
772
682
|
/**
|
|
773
|
-
* Create a new space owned by the identity. Seeds
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
* `opts.visibility` defaults to `'private'`.
|
|
683
|
+
* Create a new space owned by the identity. Seeds an empty plaintext object index.
|
|
684
|
+
* Apps populate the index with their own object types after creation using `createNode`.
|
|
777
685
|
*/
|
|
778
|
-
declare function createSpace(session: Session, name: string
|
|
779
|
-
visibility?: SpaceVisibility;
|
|
780
|
-
}): Promise<Space>;
|
|
781
|
-
declare class CategoryError extends Error {
|
|
782
|
-
}
|
|
686
|
+
declare function createSpace(session: Session, name: string): Promise<Space>;
|
|
783
687
|
declare function reconcileSpaceMeta(client: StarfishClient, userId: string, spaceId: string, shared: SpaceMeta, knownSpaces?: Space[]): Promise<void>;
|
|
784
688
|
|
|
785
689
|
interface JoinRequest {
|
|
@@ -789,22 +693,15 @@ interface JoinRequest {
|
|
|
789
693
|
}
|
|
790
694
|
declare function makeJoinRequest(session: Session): string;
|
|
791
695
|
/**
|
|
792
|
-
* Owner
|
|
793
|
-
*
|
|
794
|
-
|
|
795
|
-
declare function addDeviceToSpaceKeyring(session: Session, spaceId: string, recipient: {
|
|
796
|
-
kemPub: string;
|
|
797
|
-
userId: string;
|
|
798
|
-
}): Promise<void>;
|
|
799
|
-
/**
|
|
800
|
-
* Owner: invite an identity into a PRIVATE space. Adds them to the keyring, records
|
|
801
|
-
* them in the roster, and mints a single space-scoped member cap.
|
|
696
|
+
* Owner: invite an identity into a space. Records them in the roster, mints a
|
|
697
|
+
* space-scoped member cap, and adds them to the space-wide keyring if it exists
|
|
698
|
+
* (so they can decrypt `enc` nodes from the start).
|
|
802
699
|
* Returns the invite bundle JSON.
|
|
803
700
|
*/
|
|
804
701
|
declare function inviteToSpace(session: Session, spaceId: string, requestJson: string, canWrite?: boolean, spaceName?: string): Promise<string>;
|
|
805
702
|
/**
|
|
806
|
-
* Invitee: accept a
|
|
807
|
-
*
|
|
703
|
+
* Invitee: accept a space invite — store the cap and register the space.
|
|
704
|
+
* Returns the joined space.
|
|
808
705
|
*/
|
|
809
706
|
declare function acceptSpaceInvite(session: Session, inviteJson: string): Promise<Space>;
|
|
810
707
|
/** A space invite link token (v:1, no ownerId — derive from cap.iss instead). */
|
|
@@ -831,10 +728,23 @@ declare function createSpaceInviteLink(session: Session, spaceId: string, spaceN
|
|
|
831
728
|
link: string;
|
|
832
729
|
}>;
|
|
833
730
|
/**
|
|
834
|
-
* Any user: join a
|
|
731
|
+
* Any user: join a space by redeeming an invite link token.
|
|
835
732
|
* Stores the link credential locally and seals it into the synced `_spaces` doc.
|
|
836
733
|
*/
|
|
837
734
|
declare function joinSpaceByLink(session: Session, token: SpaceInviteLinkToken): Promise<Space>;
|
|
735
|
+
/**
|
|
736
|
+
* Add a device's KEM key as a recipient of a space's keyring.
|
|
737
|
+
*
|
|
738
|
+
* Call this after device pairing (for each space the new device should be able to
|
|
739
|
+
* decrypt). ONE space keyring encrypts ALL the space's `enc` nodes — adding the device
|
|
740
|
+
* once unlocks the whole space's E2EE content. Silently a no-op if the keyring doesn't
|
|
741
|
+
* exist yet.
|
|
742
|
+
*/
|
|
743
|
+
declare function addDeviceToSpaceKeyring(session: Session, spaceId: string, device: {
|
|
744
|
+
kemPub: string;
|
|
745
|
+
edPub: string;
|
|
746
|
+
userId: string;
|
|
747
|
+
}): Promise<void>;
|
|
838
748
|
/**
|
|
839
749
|
* Single sign-in hydration: merges server-side caps (plaintext member caps from
|
|
840
750
|
* `_spaces.caps`) and sealed link access (from `_spaces.pubAccess`) into the
|
|
@@ -846,60 +756,214 @@ declare function recoverSpaceAccess(session: Session, server: {
|
|
|
846
756
|
pubAccess: Record<string, SealedBlob>;
|
|
847
757
|
}): Promise<void>;
|
|
848
758
|
|
|
759
|
+
interface CreateNodeInput {
|
|
760
|
+
type: ObjectType;
|
|
761
|
+
title: string;
|
|
762
|
+
emoji?: string;
|
|
763
|
+
parentId?: string | null;
|
|
764
|
+
/** Who may reach this node. Default: `'space'`. */
|
|
765
|
+
access?: NodeAccess;
|
|
766
|
+
/** Whether node content is E2EE under the space-wide keyring. Default: `false`. */
|
|
767
|
+
enc?: boolean;
|
|
768
|
+
/** App-specific metadata. */
|
|
769
|
+
meta?: Record<string, unknown>;
|
|
770
|
+
}
|
|
849
771
|
/**
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
*
|
|
772
|
+
* Create a new node in a space's object index.
|
|
773
|
+
*
|
|
774
|
+
* - Rejects the invalid combo `public+enc`.
|
|
775
|
+
* - For `enc` nodes, ensures the space-wide keyring exists (minted once per space,
|
|
776
|
+
* idempotent on subsequent creates).
|
|
777
|
+
* - Returns the created node as it was inserted into the index.
|
|
853
778
|
*/
|
|
854
|
-
declare function
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
} | null>;
|
|
779
|
+
declare function createNode(session: Session, spaceId: string, input: CreateNodeInput, reg?: {
|
|
780
|
+
owner: string | null;
|
|
781
|
+
members: string[];
|
|
782
|
+
} | null): Promise<ObjectNode>;
|
|
858
783
|
/**
|
|
859
|
-
*
|
|
860
|
-
*
|
|
784
|
+
* Patch the `access`/`enc` axes of a node in the index.
|
|
785
|
+
*
|
|
786
|
+
* - Rejects `public+enc`.
|
|
787
|
+
* - For enabling `enc`, ensures the space keyring exists (idempotent).
|
|
788
|
+
* - Content migration (moving between `objpub`/`objdoc`/`objinv`) is the caller's
|
|
789
|
+
* responsibility — this only flips the metadata flags.
|
|
861
790
|
*/
|
|
862
|
-
declare function
|
|
791
|
+
declare function setNodeAccess(session: Session, spaceId: string, nodeId: string, patch: {
|
|
792
|
+
access?: NodeAccess;
|
|
793
|
+
enc?: boolean;
|
|
794
|
+
}, reg?: {
|
|
863
795
|
owner: string | null;
|
|
864
796
|
members: string[];
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
/**
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
797
|
+
} | null): Promise<void>;
|
|
798
|
+
interface NodeInviteBundle {
|
|
799
|
+
spaceId: string;
|
|
800
|
+
nodeId: string;
|
|
801
|
+
nodeName: string;
|
|
802
|
+
/** Space-level member cap (always present — grants index read access). */
|
|
803
|
+
cap: unknown;
|
|
804
|
+
/** Per-node narrow cap (only for `invite+plaintext` nodes). */
|
|
805
|
+
nodeCap?: unknown;
|
|
806
|
+
}
|
|
874
807
|
/**
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
808
|
+
* Owner: invite an identity to a specific node.
|
|
809
|
+
*
|
|
810
|
+
* - For `enc` nodes: adds the invitee to the space-wide keyring (granting decryption
|
|
811
|
+
* access to ALL enc nodes in the space) and mints a space-level member cap.
|
|
812
|
+
* - For `invite+plaintext` nodes: mints both a space-level cap (index) and a
|
|
813
|
+
* narrow per-node cap (`nodeMemberScope`, covers `objinv` content).
|
|
814
|
+
*
|
|
815
|
+
* Returns the invite bundle JSON; pass to the invitee who calls `acceptNodeInvite`.
|
|
878
816
|
*/
|
|
879
|
-
declare function
|
|
817
|
+
declare function inviteToNode(session: Session, spaceId: string, nodeId: string, requestJson: string, node: {
|
|
818
|
+
enc?: boolean;
|
|
819
|
+
}, nodeName?: string): Promise<string>;
|
|
880
820
|
/**
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
* For public spaces: pushes plaintext nodes.
|
|
821
|
+
* Invitee: accept a direct node invite — store the cap(s) and register access.
|
|
822
|
+
* Returns the nodeId.
|
|
884
823
|
*/
|
|
885
|
-
declare function
|
|
886
|
-
|
|
887
|
-
|
|
824
|
+
declare function acceptNodeInvite(session: Session, bundleJson: string): Promise<string>;
|
|
825
|
+
/** A node invite link token (v:1). */
|
|
826
|
+
interface NodeInviteLinkToken {
|
|
827
|
+
v: 1;
|
|
828
|
+
spaceId: string;
|
|
829
|
+
nodeId: string;
|
|
830
|
+
nodeName: string;
|
|
831
|
+
/** Cap scope depends on `enc`: spaceMemberScope for enc nodes, nodeMemberScope for plaintext. */
|
|
832
|
+
cap: unknown;
|
|
833
|
+
/** The ephemeral subject's Ed25519 private key (hex). */
|
|
834
|
+
key: string;
|
|
835
|
+
write: boolean;
|
|
836
|
+
}
|
|
837
|
+
declare function encodeNodeInviteLink(origin: string, token: NodeInviteLinkToken): string;
|
|
838
|
+
declare function decodeNodeInviteLink(fragment: string): NodeInviteLinkToken;
|
|
839
|
+
/**
|
|
840
|
+
* Owner: create a shareable invite link for a specific node.
|
|
841
|
+
*
|
|
842
|
+
* - For `enc` nodes: adds ephemeral KEM to the space-wide keyring; the link cap uses
|
|
843
|
+
* `spaceMemberScope` so the bearer can read the keyring and decrypt enc content.
|
|
844
|
+
* - For `invite+plaintext` nodes: narrow per-node cap (`nodeMemberScope`), no keyring.
|
|
845
|
+
*
|
|
846
|
+
* Anyone with the link can access the node; revoke by calling
|
|
847
|
+
* `removeSpaceMember(ephemeralUserId)` (and rotating the space keyring for enc nodes).
|
|
848
|
+
*/
|
|
849
|
+
declare function createNodeInviteLink(session: Session, spaceId: string, nodeId: string, nodeName: string, node: {
|
|
850
|
+
enc?: boolean;
|
|
851
|
+
}, write: boolean, origin: string): Promise<{
|
|
852
|
+
token: NodeInviteLinkToken;
|
|
853
|
+
link: string;
|
|
854
|
+
}>;
|
|
855
|
+
/**
|
|
856
|
+
* Any user: access a node by redeeming an invite link token.
|
|
857
|
+
* Stores the per-node link entry locally and seals it into the synced `_spaces` doc.
|
|
858
|
+
*/
|
|
859
|
+
declare function joinNodeByLink(session: Session, token: NodeInviteLinkToken): Promise<string>;
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Generic object-tree model — pure logic over a space's object index.
|
|
863
|
+
*
|
|
864
|
+
* A space's contents are {@link ObjectNode}s in one union-merged index doc at
|
|
865
|
+
* `spaces/{spaceId}/objects/_index`. This module is the pure, testable core:
|
|
866
|
+
* the tree builder + merge-artifact guards, breadcrumbs, ordering, and the node
|
|
867
|
+
* reducers a `store.set` applies.
|
|
868
|
+
*
|
|
869
|
+
* Because the index is union-merged (per-node last-write-wins keyed on `updatedAt`),
|
|
870
|
+
* the tree is eventually consistent — two devices can concurrently produce a cycle
|
|
871
|
+
* or an orphan. The builder below is the single place those are repaired so every
|
|
872
|
+
* consumer renders a well-formed tree.
|
|
873
|
+
*
|
|
874
|
+
* No domain types (room, category, task, …) are defined here. Apps define their own.
|
|
875
|
+
*/
|
|
876
|
+
|
|
877
|
+
/** A node plus its resolved children — the shape a tree view renders. */
|
|
878
|
+
interface ObjectTreeNode extends ObjectNode {
|
|
879
|
+
depth: number;
|
|
880
|
+
children: ObjectTreeNode[];
|
|
881
|
+
}
|
|
882
|
+
/** The order value for a new node appended after `siblings`. */
|
|
883
|
+
declare function nextOrder(siblings: ObjectNode[]): number;
|
|
888
884
|
/**
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
885
|
+
* Build the render tree from a flat node list, repairing merge artifacts:
|
|
886
|
+
* - **archived** nodes (and their subtrees) are dropped.
|
|
887
|
+
* - **orphans** — a `parentId` that is missing or archived — reparent to root.
|
|
888
|
+
* - **cycles** — a node reachable from itself via `parentId` — reparent to root.
|
|
889
|
+
* - **siblings** sort by {@link compareSiblings} for cross-device determinism.
|
|
890
|
+
*/
|
|
891
|
+
declare function buildTree(nodes: ObjectNode[], includeArchived?: boolean): ObjectTreeNode[];
|
|
892
|
+
/** The root→node trail (inclusive) for breadcrumbs. Returns `[]` if unknown. */
|
|
893
|
+
declare function breadcrumbs(nodes: ObjectNode[], id: ID): ObjectNode[];
|
|
894
|
+
/** The root→parent trail (EXCLUSIVE of the node itself). */
|
|
895
|
+
declare function ancestors(nodes: ObjectNode[], id: ID): ObjectNode[];
|
|
896
|
+
/** The ids of a node and its whole subtree (for cascade-archive). */
|
|
897
|
+
declare function subtreeIds(nodes: ObjectNode[], rootId: ID): Set<ID>;
|
|
898
|
+
interface NewObjectInput {
|
|
899
|
+
type: ObjectType;
|
|
900
|
+
parentId?: ID | null;
|
|
901
|
+
title: string;
|
|
902
|
+
emoji?: string;
|
|
903
|
+
/** App-specific metadata passed through to node.meta. */
|
|
904
|
+
meta?: Record<string, unknown>;
|
|
905
|
+
/** Provide to reuse an id (e.g. a node id derived elsewhere); else minted. */
|
|
906
|
+
id?: ID;
|
|
907
|
+
/** Who may reach this node. Absent ⇒ `'space'` (all space members). */
|
|
908
|
+
access?: NodeAccess;
|
|
909
|
+
/** Whether the node's content is E2EE under its own per-node keyring. Absent ⇒ false. */
|
|
910
|
+
enc?: boolean;
|
|
911
|
+
}
|
|
912
|
+
/** Append a new node under `parentId` at the end of its sibling order. */
|
|
913
|
+
declare function addObject(nodes: ObjectNode[], input: NewObjectInput, now: number): {
|
|
914
|
+
nodes: ObjectNode[];
|
|
915
|
+
node: ObjectNode;
|
|
916
|
+
};
|
|
917
|
+
/** Patch a node's mutable metadata (title/emoji/meta/access/enc), bumping `updatedAt`. */
|
|
918
|
+
declare function patchObject(nodes: ObjectNode[], id: ID, patch: Partial<Pick<ObjectNode, 'title' | 'emoji' | 'meta' | 'access' | 'enc'>>, now: number): ObjectNode[];
|
|
919
|
+
/** Reparent a node (move in the tree). Rejects making a node its own descendant. */
|
|
920
|
+
declare function reparentObject(nodes: ObjectNode[], id: ID, parentId: ID | null, now: number): ObjectNode[];
|
|
921
|
+
/** Set explicit sibling order (drag-reorder). */
|
|
922
|
+
declare function reorderObjects(nodes: ObjectNode[], orderById: Record<ID, number>, now: number): ObjectNode[];
|
|
923
|
+
/** Cascade-archive a node and its whole subtree (soft delete). */
|
|
924
|
+
declare function archiveObject(nodes: ObjectNode[], id: ID, now: number): ObjectNode[];
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Write the create-time seed into a space's index doc.
|
|
928
|
+
* Idempotent: a no-op if the index doc already exists.
|
|
929
|
+
* Pass `nodes` to seed with initial objects; defaults to an empty index.
|
|
930
|
+
*/
|
|
931
|
+
declare function pushIndexSeed(client: _drakkar_software_starfish_client.StarfishClient, spaceId: string, nodes?: ObjectNode[]): Promise<void>;
|
|
932
|
+
/**
|
|
933
|
+
* Seed a brand-new space's index as the OWNER. Always plaintext.
|
|
934
|
+
* Pass `nodes` to seed with initial objects; defaults to an empty index.
|
|
935
|
+
*/
|
|
936
|
+
declare function seedSpaceObjectIndex(session: Session, spaceId: string, nodes?: ObjectNode[]): Promise<void>;
|
|
937
|
+
/**
|
|
938
|
+
* Headless read-modify-write of a space's unified OBJECT INDEX.
|
|
939
|
+
* Always plaintext. Retries up to 3 times on ConflictError.
|
|
940
|
+
*
|
|
941
|
+
* The mutator receives the current nodes with real (or empty, for invite) titles.
|
|
942
|
+
* Before writing back, invite nodes have their title/emoji stripped again.
|
|
892
943
|
*/
|
|
893
944
|
declare function updateObjectIndex(session: Session, spaceId: string, mutator: (nodes: ObjectNode[], now: number) => ObjectNode[] | null, reg?: {
|
|
894
945
|
owner: string | null;
|
|
895
946
|
members: string[];
|
|
896
|
-
visibility?: SpaceVisibility;
|
|
897
947
|
} | null): Promise<void>;
|
|
948
|
+
/**
|
|
949
|
+
* Read the current object tree (read-only, no mutation). Returns the stored
|
|
950
|
+
* nodes (titles are empty for invite nodes the caller is not invited to).
|
|
951
|
+
*/
|
|
952
|
+
declare function readObjectTree(session: Session, spaceId: string): Promise<ObjectNode[]>;
|
|
898
953
|
|
|
899
954
|
/** The QR-payload prefix this SDK uses. Kept distinct from `octochat-pair:` so apps
|
|
900
955
|
* can route QR payloads to the correct handler during their migration window. */
|
|
901
956
|
declare const PAIR_PREFIX = "octospaces-pair:";
|
|
902
|
-
/**
|
|
957
|
+
/**
|
|
958
|
+
* Existing device: provision + PIN-seal a new device, publish to rendezvous, return
|
|
959
|
+
* the QR payload.
|
|
960
|
+
*
|
|
961
|
+
* After pairing, call `addDeviceToSpaceKeyring(session, spaceId, newDeviceKeys)` for
|
|
962
|
+
* each space whose E2EE content the new device should decrypt. ONE space keyring
|
|
963
|
+
* encrypts ALL `enc` nodes in a space — one call per space unlocks everything.
|
|
964
|
+
* Plaintext (`space` / `public`) nodes are immediately accessible via the linked-device
|
|
965
|
+
* cap-cert (no extra keyring step).
|
|
966
|
+
*/
|
|
903
967
|
declare function startDevicePairing(session: Session, pin: string): Promise<string>;
|
|
904
968
|
interface PairResult {
|
|
905
969
|
userId: string;
|
|
@@ -969,4 +1033,4 @@ declare const starfishBase64: Base64Provider;
|
|
|
969
1033
|
declare function toBase64Url(json: string): string;
|
|
970
1034
|
declare function fromBase64Url(b64url: string): string;
|
|
971
1035
|
|
|
972
|
-
export { type
|
|
1036
|
+
export { type ArchivedDms, CONNECT_TIMEOUT_MS, type CapMap, type CreateNodeInput, type DerivedIdentity, type DeviceKeys, type DmMap, type ID, type JoinRequest, type KvAdapter, type LinkedIdentity, type MutePrefs, type NewObjectInput, type NodeAccess, type NodeAccessHandle, type NodeInviteBundle, type NodeInviteLinkToken, OBJECT_COLLECTIONS, type ObjectContentKind, type ObjectNode, type ObjectTreeNode, type ObjectType, type ObjectsIndex, type OctoSpacesConfig, PAIR_PREFIX, PULL_CACHE_MAX_AGE_MS, type PairResult, type PasskeyEnrollment, type PersistedSession, type PubAccessMap, type PublicProfile, type ReadPrefs, type SealedBlob, type SeedLock, type Session, type Space, type SpaceAccessEntry, SpaceAccessError, type SpaceAccessMap, type SpaceInviteLinkToken, type SpaceMeta, type SpaceMetaUpdate, type UnlockMethod, type Vault, type VaultLoad, acceptNodeInvite, acceptSpaceInvite, accountScope, addDeviceToSpaceKeyring, addJoinedSpace, addJoinedSpaceWithCap, addJoinedSpaceWithLinkAccess, addObject, addSpaceMember, ancestors, archiveObject, attachmentPull, attachmentPush, breadcrumbs, broadcastSpaceMeta, buildAuthHeaders, buildEncryptor, buildLinkedSession, buildNodeAccess, buildSession, buildTree, bytesToHex, cacheProfile, capProviderFor, clearNodeAccessCache, clearSpaceAccessStore, completeDevicePairing, configureKv, configureOctoSpaces, createNode, createNodeInviteLink, createSpace, createSpaceInviteLink, decodeNodeInviteLink, decodeSpaceInviteLink, deriveSession, encodeNodeInviteLink, encodeSpaceInviteLink, ensureProfileKeys, ensurePseudo, fetchWithTimeout, fingerprintFromUserId, fromBase64Url, generateSeedWords, getNodeAccess, getNodeAccessEntry, getSharedSpacesNamespace, getSpaceAccessEntry, getSpaceClient, getSyncBase, getSyncNamespace, getSyncPrefix, hydrateSpaceAccessStore, inviteToNode, inviteToSpace, isValidSeed, joinNodeByLink, joinSpaceByLink, keyringName, keyringPull, keyringPush, kvGet, kvRemove, kvSet, linkAccessFromStore, linkedDeviceScope, loadCachedProfile, localSpaceAccessEntries, makeClient, makeJoinRequest, memberCapsFromStore, nextOrder, nodeMemberScope, objDocPull, objDocPush, objIndexPull, objIndexPush, objInvName, objInvPull, objInvPush, objLogPull, objLogPush, objPubName, objPubPull, objPubPush, objectBlobPull, objectBlobPush, objectDirName, objectDirPull, onSpaceMeta, openEncryptor, ownerEnsureKeyring, ownerScope, ownerTrustedAdders, patchObject, profilePull, profilePush, pullCache, pushIndexSeed, randomId, readObjectTree, readProfile, readProfiles, readPseudo, readSpaceAccess, readSpaces, reconcileSpaceMeta, recoverSpaceAccess, removeNodeAccessEntry, removeSpaceAccessEntry, removeSpaceMember, reorderObjects, reorderSpaces, reparentObject, roomSlug, rootIdentityOf, saveNodeAccessEntry, saveSpaceAccessEntry, sealToRecipient, sealToSelf, seedSpaceObjectIndex, setDmMapping, setNodeAccess, spaceAccessPull, spaceAccessPush, spaceMemberScope, spacesPull, spacesPush, starfishBase64, startDevicePairing, subtreeIds, toBase64Url, typesIndexPull, typesIndexPush, unsealFromRecipient, unsealFromSelf, updateArchivedDmsDoc, updateDmsDoc, updateMutesDoc, updateObjectIndex, updateQuickReactionsDoc, updateReadsDoc, updateSpacesDoc, userIdFromEdPub, writeProfile, writePseudo, writeSpaceAccess, writeSpaces };
|