@drakkar.software/octospaces-sdk 0.1.0 → 0.4.3
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 +481 -274
- package/dist/index.js +1000 -493
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/types.ts +50 -83
- package/src/index.ts +62 -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/src/utils/invite-preview.test.ts +169 -0
- package/src/utils/invite-preview.ts +101 -0
- package/src/utils/live-sync-bus.test.ts +116 -0
- package/src/utils/live-sync-bus.ts +71 -0
- package/src/utils/search-match.test.ts +149 -0
- package/src/utils/search-match.ts +145 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Changelog — @drakkar.software/octospaces-sdk
|
|
2
|
+
|
|
3
|
+
## 0.4.1 (2026-06-12)
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- **Space-wide keyring restored.** `nodeKeyringName`, `nodeKeyringPull`, `nodeKeyringPush`
|
|
8
|
+
are removed. Replaced by `keyringName`, `keyringPull`, `keyringPush` (one keyring per
|
|
9
|
+
space, at `spaces/{spaceId}/_keyring`, collection `spacekeyring`).
|
|
10
|
+
- **`nodeMemberScope` no longer includes `'nodekeyring'`** — collections is now
|
|
11
|
+
`['objinv']` only. Use `spaceMemberScope` when the bearer also needs to decrypt enc
|
|
12
|
+
content (they need `spacekeyring` + `spaces/{spaceId}/**` path).
|
|
13
|
+
- **`OBJECT_COLLECTIONS` updated**: `'nodekeyring'` → `'spacekeyring'`.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`keyringName(spaceId)`** — base path for `addCollectionRecipient` calls.
|
|
18
|
+
- **`keyringPull(spaceId)` / `keyringPush(spaceId)`** — pull/push paths for the
|
|
19
|
+
space-wide keyring at `spaces/{spaceId}/_keyring`.
|
|
20
|
+
- **`addDeviceToSpaceKeyring(session, spaceId, device)`** — add a paired device's KEM
|
|
21
|
+
key to a space's keyring. Call after device pairing for each space the new device
|
|
22
|
+
should decrypt. ONE call per space unlocks all `enc` nodes.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **`inviteToSpace`** now adds the invitee to the space-wide keyring (if it exists) so
|
|
27
|
+
they can decrypt `enc` nodes from the start. Silently skips if the keyring doesn't
|
|
28
|
+
exist yet (no enc nodes in the space).
|
|
29
|
+
- **`createSpaceInviteLink`** adds the ephemeral KEM to the space keyring (if it exists).
|
|
30
|
+
- **`inviteToNode(enc=true)`** adds the invitee to the SPACE keyring (not a per-node
|
|
31
|
+
keyring) — granting decryption access to ALL enc nodes in the space.
|
|
32
|
+
- **`createNodeInviteLink(enc=true)`** uses `spaceMemberScope` for the cap (so the bearer
|
|
33
|
+
can reach the space keyring) and adds the ephemeral KEM to the space keyring.
|
|
34
|
+
- **`getNodeAccess`** / **`buildNodeAccess`** open the SPACE keyring (not a per-node
|
|
35
|
+
keyring) when `node.enc` is true.
|
|
36
|
+
- **`startDevicePairing`** comment updated — post-pairing call `addDeviceToSpaceKeyring`
|
|
37
|
+
rather than `inviteToNode` per enc node.
|
|
38
|
+
|
|
39
|
+
### Semantic note
|
|
40
|
+
|
|
41
|
+
The space keyring is coarse-grained: any keyring-holder with reach can decrypt ALL
|
|
42
|
+
`enc` nodes in the space. Inviting someone to one `enc` node grants them the space key
|
|
43
|
+
and thus access to all enc content. Per-node E2EE isolation requires per-node keyrings
|
|
44
|
+
(which are opt-in at the app level; the shared SDK no longer manages them).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 0.4.0 (2026-06-12)
|
|
49
|
+
|
|
50
|
+
### Breaking changes
|
|
51
|
+
|
|
52
|
+
- **`SpaceVisibility` removed.** The type `'private' | 'public'` no longer exists; spaces
|
|
53
|
+
are neutral containers. Visibility/encryption move to the node level (see below).
|
|
54
|
+
- **Space-level keyring removed.** `keyringName`, `keyringPull`, `keyringPush` are gone.
|
|
55
|
+
There is no per-space CEK. Encryption is per-node.
|
|
56
|
+
- **`addDeviceToSpaceKeyring` removed.** Pairing no longer auto-grants a linked device
|
|
57
|
+
access to E2EE nodes — the owner must call `inviteToNode` per node after pairing.
|
|
58
|
+
- **`getSpaceAccess` / `buildSpaceAccess` / `clearSpaceAccessCache` / `SpaceAccessHandle`
|
|
59
|
+
removed.** Replaced by `getNodeAccess` / `buildNodeAccess` / `clearNodeAccessCache` /
|
|
60
|
+
`NodeAccessHandle`.
|
|
61
|
+
- **`spaceIndexName` / `spaceIndexPull` removed.** Replaced by `objectDirName` /
|
|
62
|
+
`objectDirPull` (target `_index/objects/{shard}`).
|
|
63
|
+
- **`createSpace(session, name, opts?)` — `opts` removed.** Spaces no longer have a
|
|
64
|
+
`visibility` or `type`/`subtype`; call is now `createSpace(session, name)`.
|
|
65
|
+
- **`Space.visibility`, `Space.ownerId`, `Space.write` removed.** `Space` interface now
|
|
66
|
+
carries only `{ id, name, short, image?, members, unread? }`.
|
|
67
|
+
- **`SpaceMeta.type` / `SpaceMeta.subtype` removed.**
|
|
68
|
+
- **`joinSpaceByLink` no longer sets `visibility`/`ownerId`/`write` on the returned Space.**
|
|
69
|
+
- **`acceptSpaceInvite` no longer requires `cap.iss`.** Keyring verification is gone.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`NodeAccess = 'public' | 'space' | 'invite'`** — per-node access axis. Absent ⇒ `'space'`.
|
|
74
|
+
- **`ObjectNode.access?: NodeAccess`** and **`ObjectNode.enc?: boolean`** — two orthogonal
|
|
75
|
+
axes. The invalid combo `public+enc` is rejected at `createNode`/`setNodeAccess`.
|
|
76
|
+
- **Per-node keyrings**: `nodeKeyringName/Pull/Push(spaceId, nodeId)` at
|
|
77
|
+
`spaces/{spaceId}/objects/n/{nodeId}/_keyring` (collection `nodekeyring`).
|
|
78
|
+
- **Public node content**: `objPubName/Pull/Push(spaceId, nodeId)` at
|
|
79
|
+
`spaces/{spaceId}/objects/pub/{nodeId}` (collection `objpub`, `readRoles:['public']`).
|
|
80
|
+
- **Invite-only plaintext content**: `objInvName/Pull/Push(spaceId, nodeId)` at
|
|
81
|
+
`spaces/{spaceId}/objects/n/{nodeId}/content` (collection `objinv`, excluded from
|
|
82
|
+
`spaceMemberScope`).
|
|
83
|
+
- **Global object directory**: `objectDirName/Pull(shard?)` → `_index/objects/{shard}`.
|
|
84
|
+
- **`nodeMemberScope(spaceId, nodeId, canWrite)`** — narrow per-node cap covering
|
|
85
|
+
`['nodekeyring', 'objinv']`. Only this scope reaches invite-plaintext content.
|
|
86
|
+
- **`OBJECT_COLLECTIONS`** updated: `spacekeyring` → `nodekeyring`; `objpub` added;
|
|
87
|
+
`objinv` intentionally absent (excluded from broad scope by design).
|
|
88
|
+
- **`getSpaceClient(spaceId, session)`** — returns the member-gated client for index/access
|
|
89
|
+
docs; always plaintext.
|
|
90
|
+
- **`getNodeAccess(spaceId, nodeId, node, session, reg?)`** — resolves `{ encryptor, client,
|
|
91
|
+
isOwnerOpen }` for a specific node's content.
|
|
92
|
+
- **`buildNodeAccess(session, spaceId, nodeId, node)`** — soft variant (returns null instead
|
|
93
|
+
of throwing on missing access).
|
|
94
|
+
- **`clearNodeAccessCache()`** — drop cached handles on account switch.
|
|
95
|
+
- **`getNodeAccessEntry / saveNodeAccessEntry / removeNodeAccessEntry`** — per-node entries
|
|
96
|
+
in the unified space-access store (composite key `${spaceId}:${nodeId}`).
|
|
97
|
+
- **`readObjectTree(session, spaceId)`** — read-only pull of a space's node list.
|
|
98
|
+
- **`spaces/nodes.ts`** — new module:
|
|
99
|
+
- `createNode(session, spaceId, input, reg?)` — creates a node; mints per-node keyring
|
|
100
|
+
for `enc` nodes.
|
|
101
|
+
- `setNodeAccess(session, spaceId, nodeId, patch, reg?)` — flips `access`/`enc` flags.
|
|
102
|
+
- `inviteToNode(session, spaceId, nodeId, requestJson, node, nodeName?)` — owner-side
|
|
103
|
+
direct invite; adds to keyring (`enc`) or mints narrow cap (`invite+plaintext`).
|
|
104
|
+
- `acceptNodeInvite(session, bundleJson)` — invitee stores per-node access.
|
|
105
|
+
- `createNodeInviteLink(…)` — shareable link invite for a specific node.
|
|
106
|
+
- `joinNodeByLink(session, token)` — bearer stores per-node link entry.
|
|
107
|
+
- **`NewObjectInput.access?` / `NewObjectInput.enc?`** — `addObject` passes these through
|
|
108
|
+
to the node; `patchObject` now accepts them in its patch type.
|
|
109
|
+
- **`pushIndexSeed` API simplified**: `pushIndexSeed(client, spaceId, nodes?)` — no
|
|
110
|
+
encryptor argument (index is always plaintext).
|
|
111
|
+
|
|
112
|
+
### Changed
|
|
113
|
+
|
|
114
|
+
- Object index is **always plaintext**. `invite` node entries have `title` / `emoji`
|
|
115
|
+
stripped before storage (non-invited members see a placeholder row only).
|
|
116
|
+
- **`inviteToSpace`** no longer adds the invitee to a space keyring (there is none).
|
|
117
|
+
- **`acceptSpaceInvite`** no longer verifies keyring access.
|
|
118
|
+
- **`startDevicePairing`** no longer auto-grants linked devices to space keyrings.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 0.3.0 (2026-06-12)
|
|
123
|
+
|
|
124
|
+
### Added
|
|
125
|
+
|
|
126
|
+
- **`Space.type` / `Space.subtype`** — optional opaque string fields on the `Space` interface.
|
|
127
|
+
App-owned; no builtins defined in the SDK (`'chat'`, `'vault'`, `'chat-only'`, etc. are yours
|
|
128
|
+
to declare).
|
|
129
|
+
- **`SpaceMeta.type` / `SpaceMeta.subtype`** — same fields added to the write-side meta struct.
|
|
130
|
+
- **`writeSpaceAccess`** now persists `type` / `subtype` when truthy; `readSpaceAccess` reads them
|
|
131
|
+
back (string | null). Both are threaded through `addSpaceMember` / `removeSpaceMember` so roster
|
|
132
|
+
edits no longer drop them.
|
|
133
|
+
- **`createSpace`** accepts `opts.type` / `opts.subtype`; both are stored in `_access` and
|
|
134
|
+
mirrored on the returned `Space` object.
|
|
135
|
+
- **`spaceIndexName` exported** from the barrel (`src/index.ts`). `spaceIndexPull` / `spaceIndexName`
|
|
136
|
+
signatures generalised from `(shard: 'public')` to `(shard?: string)` — default `'public'`
|
|
137
|
+
keeps existing call sites unchanged. Clients read a typed catalog via `spaceIndexPull('chat')`.
|
|
138
|
+
|
|
139
|
+
### Notes
|
|
140
|
+
|
|
141
|
+
`type` / `subtype` may be set on private spaces (stored in the member-gated `_access` doc, not
|
|
142
|
+
publicly visible). Only public spaces reach the world-readable directory; the server projection
|
|
143
|
+
shards on `type`. Treat `type` as set-at-create — changing it after the fact leaves a stale row
|
|
144
|
+
in the old shard (same caveat as public → private flips today).
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 0.2.0 (2026-06-12)
|
|
149
|
+
|
|
150
|
+
### Breaking changes
|
|
151
|
+
|
|
152
|
+
- **Domain object types removed.** `BuiltinObjectType`, `BUILTIN_OBJECT_TYPES`, `RoomKind`,
|
|
153
|
+
`RoomSubtype`, `AutomationMeta`, `AutomationSchedule`, and `Room` are no longer exported.
|
|
154
|
+
`ObjectType` is now `string`. Apps must declare their own type constants.
|
|
155
|
+
- **`ObjectNode` fields removed.** `subtype` and `automation` fields are gone; store
|
|
156
|
+
app-specific node config in the existing `meta?: Record<string, unknown>` field instead.
|
|
157
|
+
- **Room/category bridge functions removed.** `categoryId`, `DEFAULT_CATEGORY`,
|
|
158
|
+
`objectsToRoomCategories`, `excludeAutomatedRooms`, `roomKindToSubtype`, `subtypeToRoomKind`,
|
|
159
|
+
`AdaptedCategory`, `SeedRoom`, `seedIndexNodes`, `normalizeCategories`, `CategoryError`.
|
|
160
|
+
- **Room projection functions removed.** `readIndexRooms`, `readSpaceIndexRooms`,
|
|
161
|
+
`readSpaceRooms`. Use `updateObjectIndex(session, spaceId, nodes => nodes, reg)` as a
|
|
162
|
+
read-only alternative.
|
|
163
|
+
- **Access-record helpers renamed.** `roomsRegistryPull`/`roomsRegistryPush` →
|
|
164
|
+
`spaceAccessPull`/`spaceAccessPush`; `readRooms`/`writeRooms` →
|
|
165
|
+
`readSpaceAccess`/`writeSpaceAccess`.
|
|
166
|
+
- **Storage leaf renamed.** Access records move from `spaces/{id}/_rooms` to
|
|
167
|
+
`spaces/{id}/_access`. Apps that haven't adopted this migration continue to work via the
|
|
168
|
+
Infra enricher's default `registry_path` param.
|
|
169
|
+
- **Collection names updated.** `rooms` → `spaceregistry`; `chatkeyring` → `spacekeyring`.
|
|
170
|
+
`accountScope` and `linkedDeviceScope` updated accordingly.
|
|
171
|
+
- **`createSpace` no longer seeds a general channel.** The index is seeded empty; apps add
|
|
172
|
+
their own initial nodes after creation.
|
|
173
|
+
|
|
174
|
+
### Migration
|
|
175
|
+
|
|
176
|
+
Adopting apps must declare their own type constants:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// e.g. in your own SDK package
|
|
180
|
+
export const MY_TYPES = { page: 'page', folder: 'folder' } as const;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Replace removed helpers:
|
|
184
|
+
- `readSpaceIndexRooms(session, spaceId, reg)` →
|
|
185
|
+
`updateObjectIndex(session, spaceId, nodes => nodes, reg)`
|
|
186
|
+
- `roomsRegistryPull/Push` → `spaceAccessPull`/`spaceAccessPush`
|
|
187
|
+
- `readRooms/writeRooms` → `readSpaceAccess`/`writeSpaceAccess`
|
|
188
|
+
- `ObjectNode.subtype` → `ObjectNode.meta.subtype` (or a first-class type string)
|
|
189
|
+
- `ObjectNode.automation` → `ObjectNode.meta.automation`
|
|
190
|
+
|
|
191
|
+
See `OCTOCHAT_MIGRATION.md` and `OCTOVAULT_MIGRATION.md` at the repo root for full
|
|
192
|
+
per-app migration guides.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 0.1.0 (initial)
|
|
197
|
+
|
|
198
|
+
Initial release — unified public/private spaces on one `spaces/{spaceId}/**` path family,
|
|
199
|
+
E2EE object index, link-based joins, cross-app keyring, and the generic `ObjectNode` tree
|
|
200
|
+
engine.
|