@kubun/plugin-p2p 0.9.0 → 0.10.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/lib/context/delegation.d.ts +4 -0
- package/lib/context/delegation.js +1 -0
- package/lib/context/group.js +1 -1
- package/lib/context/hub.js +1 -1
- package/lib/context/join.js +1 -1
- package/lib/context/types.d.ts +25 -2
- package/lib/groups/broadcast-service.d.ts +137 -4
- package/lib/groups/broadcast-service.js +1 -1
- package/lib/groups/broadcast.d.ts +120 -0
- package/lib/groups/broadcast.js +1 -1
- package/lib/groups/events.d.ts +16 -5
- package/lib/groups/events.js +1 -1
- package/lib/groups/group-handle-registry.d.ts +8 -0
- package/lib/groups/group-handle-registry.js +1 -1
- package/lib/groups/group-health-monitor.d.ts +45 -0
- package/lib/groups/group-health-monitor.js +1 -0
- package/lib/groups/invite-payload.d.ts +16 -0
- package/lib/groups/join-utils.d.ts +68 -2
- package/lib/groups/join-utils.js +1 -1
- package/lib/groups/manager.d.ts +15 -4
- package/lib/groups/manager.js +1 -1
- package/lib/groups/rejoin-codec.d.ts +14 -0
- package/lib/groups/rejoin-codec.js +1 -0
- package/lib/groups/store-received-grant.d.ts +47 -0
- package/lib/groups/store-received-grant.js +1 -0
- package/lib/groups/store-received-revocation.d.ts +46 -0
- package/lib/groups/store-received-revocation.js +1 -0
- package/lib/groups/wire-frame.d.ts +34 -0
- package/lib/groups/wire-frame.js +1 -0
- package/lib/hub/circle-catchup-requester.d.ts +58 -0
- package/lib/hub/circle-catchup-requester.js +1 -0
- package/lib/hub/circle-catchup-responder.d.ts +50 -0
- package/lib/hub/circle-catchup-responder.js +1 -0
- package/lib/hub/epoch-stale-detector.d.ts +18 -0
- package/lib/hub/epoch-stale-detector.js +1 -0
- package/lib/hub/errors.d.ts +7 -5
- package/lib/hub/errors.js +1 -1
- package/lib/hub/forward-remote-broadcast.d.ts +3 -3
- package/lib/hub/forward-remote-broadcast.js +1 -1
- package/lib/hub/group-channel.d.ts +30 -0
- package/lib/hub/group-channel.js +1 -1
- package/lib/hub/hub-connection.js +1 -1
- package/lib/hub/manager.d.ts +5 -4
- package/lib/hub/manager.js +1 -1
- package/lib/hub/receive-handler.d.ts +20 -2
- package/lib/hub/receive-handler.js +1 -1
- package/lib/hub/rejoin-manager.d.ts +78 -0
- package/lib/hub/rejoin-manager.js +1 -0
- package/lib/hub/rejoin-responder.d.ts +32 -0
- package/lib/hub/rejoin-responder.js +1 -0
- package/lib/hub/relay-manager.d.ts +23 -1
- package/lib/hub/relay-manager.js +1 -1
- package/lib/hub/send-handler.d.ts +16 -0
- package/lib/hub/send-handler.js +1 -1
- package/lib/hub/wiring.d.ts +33 -3
- package/lib/hub/wiring.js +1 -1
- package/lib/index.d.ts +19 -5
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +7 -0
- package/lib/protocol.js +1 -1
- package/lib/schema.d.ts +2 -2
- package/lib/schema.js +79 -3
- package/lib/sync/authorize.d.ts +25 -0
- package/lib/sync/authorize.js +1 -0
- package/lib/sync/broadcast-sender.js +1 -1
- package/lib/sync/errors.d.ts +11 -0
- package/lib/sync/errors.js +1 -0
- package/lib/sync/forwarder.d.ts +1 -0
- package/lib/sync/forwarder.js +1 -1
- package/lib/sync/handlers.d.ts +3 -18
- package/lib/sync/handlers.js +1 -1
- package/lib/sync/hub-tunnel-sync-listener.d.ts +2 -0
- package/lib/sync/hub-tunnel-sync-listener.js +1 -1
- package/lib/sync/merkle-apply.js +1 -1
- package/lib/sync/merkle-channel.d.ts +19 -0
- package/lib/sync/merkle-channel.js +1 -1
- package/lib/sync/peer-registry.d.ts +7 -0
- package/lib/sync/peer-registry.js +1 -1
- package/lib/sync/sync-client.d.ts +2 -0
- package/lib/sync/sync-client.js +1 -1
- package/lib/sync/sync-manager.js +1 -1
- package/lib/types.d.ts +122 -1
- package/lib/utils.d.ts +5 -0
- package/lib/utils.js +1 -0
- package/package.json +41 -41
- package/lib/sync/catalog-match.d.ts +0 -13
- package/lib/sync/catalog-match.js +0 -1
package/lib/types.d.ts
CHANGED
|
@@ -115,7 +115,15 @@ export type GroupRequestContext = {
|
|
|
115
115
|
filterCriteria: string;
|
|
116
116
|
}) => Promise<CatalogData>;
|
|
117
117
|
listCatalogs: (ownerDID?: string) => Promise<Array<CatalogData>>;
|
|
118
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Build an invite payload for a join request.
|
|
120
|
+
*
|
|
121
|
+
* `grants` are stringified write-capability JWTs the caller wants the joiner
|
|
122
|
+
* to hold immediately on join. `revocations` are signed revocation JWTs the
|
|
123
|
+
* caller wants the joiner to apply immediately — only verified ones travel;
|
|
124
|
+
* pending revocations (cap not yet known by the inviter) do not.
|
|
125
|
+
*/
|
|
126
|
+
invite: (groupID: string, joinRequest: string, grants?: Array<string>, revocations?: Array<string>) => Promise<InviteToGroupData>;
|
|
119
127
|
join: (invitePayload: string, joinRequestPayload: string) => Promise<JoinGroupData>;
|
|
120
128
|
leave: (groupID: string) => Promise<LeaveGroupData>;
|
|
121
129
|
removeMember: (groupID: string, memberDID: string) => Promise<RemoveGroupMemberData>;
|
|
@@ -129,6 +137,53 @@ export type GroupRequestContext = {
|
|
|
129
137
|
deleteCatalog: (catalogID: string) => Promise<DeleteCatalogData>;
|
|
130
138
|
deleteCircle: (circleID: string) => Promise<DeleteCircleData>;
|
|
131
139
|
removeCircleMember: (circleID: string, memberDID: string) => Promise<RemoveCircleMemberData>;
|
|
140
|
+
/**
|
|
141
|
+
* Mint a `document/write` capability granting `to` write access on resources
|
|
142
|
+
* matching `res`. Token is stored grantor-side, broadcast to the group, and
|
|
143
|
+
* returned for direct delivery.
|
|
144
|
+
*
|
|
145
|
+
* `res` is a URN. Most-specific wins; broader URNs grant strictly more:
|
|
146
|
+
* - `urn:kubun:document:<docID>` — single doc
|
|
147
|
+
* - `urn:kubun:model:<modelID>` — every doc of that model owned by the grantor
|
|
148
|
+
* - `urn:kubun:model:<interfaceID>` — every doc of every model implementing
|
|
149
|
+
* the interface (footgun: a grant on a widely-implemented interface like
|
|
150
|
+
* `Owned` or `Timestamped` becomes a near-wildcard)
|
|
151
|
+
* - `urn:kubun:user:<DID>` — every doc owned by that DID (the grantor)
|
|
152
|
+
* - `*` — every doc the grantor owns, present and future
|
|
153
|
+
*
|
|
154
|
+
* Prefer the narrowest URN that satisfies the use case. Broad grants combine
|
|
155
|
+
* with the long default `exp` (30 days) to expand the blast radius of a
|
|
156
|
+
* leaked or misdirected token.
|
|
157
|
+
*/
|
|
158
|
+
grantWriteCapability: (params: {
|
|
159
|
+
to: string;
|
|
160
|
+
res: string;
|
|
161
|
+
groupID: string;
|
|
162
|
+
expiresIn?: number;
|
|
163
|
+
}) => Promise<string>;
|
|
164
|
+
/**
|
|
165
|
+
* Revoke a previously-granted `document/write` capability by its `jti`.
|
|
166
|
+
*
|
|
167
|
+
* Revocation is binary: once a peer learns of the revocation, every
|
|
168
|
+
* mutation that authenticates via this `jti` is denied. The check is
|
|
169
|
+
* reactive, not retroactive — mutations already applied locally stay
|
|
170
|
+
* applied; only future writes carrying the revoked `jti` are gated.
|
|
171
|
+
*
|
|
172
|
+
* Only the original capability issuer may revoke it. The resolver looks up
|
|
173
|
+
* the capability locally and rejects:
|
|
174
|
+
* - `REVOCATION_CAP_UNKNOWN` when no row matches the `jti` on this peer
|
|
175
|
+
* (multi-device-grantor case, where the issuing device is not the one
|
|
176
|
+
* calling `revokeCapability`, is not yet supported).
|
|
177
|
+
* - `REVOCATION_UNAUTHORIZED` when the viewer is not the capability's
|
|
178
|
+
* issuer.
|
|
179
|
+
*
|
|
180
|
+
* Returns the boolean reported by the store: `true` when the revocation
|
|
181
|
+
* row was newly written (or materially changed), `false` on an idempotent
|
|
182
|
+
* no-op (already revoked at an equal or newer HLC).
|
|
183
|
+
*/
|
|
184
|
+
revokeCapability: (params: {
|
|
185
|
+
jti: string;
|
|
186
|
+
}) => Promise<boolean>;
|
|
132
187
|
};
|
|
133
188
|
export type SyncRequestContext = {
|
|
134
189
|
addPeer: (config: PeerConfig) => Promise<void>;
|
|
@@ -175,12 +230,56 @@ export type HubRequestContext = {
|
|
|
175
230
|
}) => Promise<boolean>;
|
|
176
231
|
listByGroup: (groupID: string) => Promise<Array<HubData>>;
|
|
177
232
|
listGroups: (hubID: string) => Promise<Array<GroupData>>;
|
|
233
|
+
/** Current health snapshot for a group (state + last transition time). */
|
|
234
|
+
getGroupHealth: (groupID: string) => GroupHealthSnapshot;
|
|
235
|
+
/**
|
|
236
|
+
* Kick off stale-device recovery for a group. Fire-and-forget at the resolver
|
|
237
|
+
* boundary; the terminal outcome arrives via the `groupHealthChanged`
|
|
238
|
+
* subscription. Resolves with the flow's final result for callers that await.
|
|
239
|
+
*/
|
|
240
|
+
rejoinGroup: (groupID: string) => Promise<RejoinResult>;
|
|
241
|
+
};
|
|
242
|
+
export type DelegationData = {
|
|
243
|
+
jwt: string;
|
|
244
|
+
jti: string;
|
|
245
|
+
grantor: string;
|
|
246
|
+
audience: string;
|
|
247
|
+
resource: string;
|
|
248
|
+
exp: number;
|
|
249
|
+
};
|
|
250
|
+
export type DelegationTokenCovers = {
|
|
251
|
+
modelID?: string | null;
|
|
252
|
+
docID?: string | null;
|
|
253
|
+
};
|
|
254
|
+
export type ListOwnDelegationTokensParams = {
|
|
255
|
+
viewerDID: string;
|
|
256
|
+
grantor?: string | null;
|
|
257
|
+
covers?: DelegationTokenCovers | null;
|
|
258
|
+
};
|
|
259
|
+
export type RevokedDelegationData = {
|
|
260
|
+
jti: string;
|
|
261
|
+
grantor: string;
|
|
262
|
+
audience: string;
|
|
263
|
+
revokerDID: string;
|
|
264
|
+
revokedAt: number;
|
|
265
|
+
verifiedAt: number;
|
|
266
|
+
capExp: number | null;
|
|
267
|
+
groupID: string;
|
|
268
|
+
};
|
|
269
|
+
export type ListOwnRevokedTokensParams = {
|
|
270
|
+
viewerDID: string;
|
|
271
|
+
grantor?: string | null;
|
|
272
|
+
};
|
|
273
|
+
export type DelegationRequestContext = {
|
|
274
|
+
listOwnTokens: (params: ListOwnDelegationTokensParams) => Promise<Array<DelegationData>>;
|
|
275
|
+
listOwnRevokedTokens: (params: ListOwnRevokedTokensParams) => Promise<Array<RevokedDelegationData>>;
|
|
178
276
|
};
|
|
179
277
|
export type P2PContext = {
|
|
180
278
|
group: GroupRequestContext;
|
|
181
279
|
sync: SyncRequestContext;
|
|
182
280
|
join: P2PJoinRequestContext;
|
|
183
281
|
hub: HubRequestContext;
|
|
282
|
+
delegation: DelegationRequestContext;
|
|
184
283
|
};
|
|
185
284
|
declare module '@kubun/graphql' {
|
|
186
285
|
interface PluginContextMap {
|
|
@@ -194,6 +293,28 @@ export type SyncSessionResult = {
|
|
|
194
293
|
messagesSent: number;
|
|
195
294
|
missingClusters?: Record<string, unknown>;
|
|
196
295
|
};
|
|
296
|
+
/** A group-level health condition the monitor tracks. */
|
|
297
|
+
export type GroupHealthCondition = 'epoch-stale';
|
|
298
|
+
/**
|
|
299
|
+
* Persisted health state for a group. `recovered` is a transient transition
|
|
300
|
+
* surfaced only on the emitted event; `getState` collapses it to `healthy`.
|
|
301
|
+
*/
|
|
302
|
+
export type GroupHealthState = 'healthy' | 'degraded' | 'recovering' | 'recovered' | 'recovery-failed';
|
|
303
|
+
export type GroupHealthData = {
|
|
304
|
+
groupID: string;
|
|
305
|
+
condition: GroupHealthCondition;
|
|
306
|
+
state: GroupHealthState;
|
|
307
|
+
};
|
|
308
|
+
/** A point-in-time health snapshot for a group (drives the GraphQL query). */
|
|
309
|
+
export type GroupHealthSnapshot = {
|
|
310
|
+
state: GroupHealthState;
|
|
311
|
+
/** Epoch ms of the last state transition, or null if never transitioned. */
|
|
312
|
+
lastTransitionAt: number | null;
|
|
313
|
+
};
|
|
314
|
+
/** Terminal outcome of a rejoin flow. */
|
|
315
|
+
export type RejoinResult = {
|
|
316
|
+
status: 'recovered' | 'recovery-failed';
|
|
317
|
+
};
|
|
197
318
|
export type SyncPluginAPI = {
|
|
198
319
|
/** Resolves when the HTTP sync transport is registered (only present when http option is enabled). */
|
|
199
320
|
syncReady?: Promise<void>;
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Node returns a Timeout object with `unref()`; browsers return a number. */
|
|
2
|
+
export declare function unref(timer: ReturnType<typeof setTimeout>): void;
|
|
3
|
+
/** Resolve after `ms`, without keeping the process alive. */
|
|
4
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
5
|
+
export declare function bytesEqual(a: Uint8Array, b: Uint8Array): boolean;
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function unref(e){e.unref?.()}export function sleep(e){return new Promise(n=>{unref(setTimeout(n,e))})}export function bytesEqual(e,n){if(e.length!==n.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==n[t])return!1;return!0}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/plugin-p2p",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"license": "see LICENSE.md",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -14,50 +14,50 @@
|
|
|
14
14
|
],
|
|
15
15
|
"sideEffects": false,
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@enkaku/async": "^0.
|
|
18
|
-
"@enkaku/capability": "^0.
|
|
19
|
-
"@enkaku/client": "^0.
|
|
20
|
-
"@enkaku/codec": "^0.
|
|
21
|
-
"@enkaku/event": "^0.
|
|
22
|
-
"@enkaku/generator": "^0.
|
|
23
|
-
"@enkaku/group": "^0.
|
|
24
|
-
"@enkaku/http-client-transport": "^0.
|
|
25
|
-
"@enkaku/http-server-transport": "^0.
|
|
26
|
-
"@enkaku/hub-protocol": "^0.
|
|
27
|
-
"@enkaku/hub-tunnel": "^0.
|
|
28
|
-
"@enkaku/protocol": "^0.
|
|
29
|
-
"@enkaku/runtime": "^0.
|
|
30
|
-
"@enkaku/schema": "^0.
|
|
31
|
-
"@enkaku/server": "^0.
|
|
32
|
-
"@enkaku/stream": "^0.
|
|
33
|
-
"@enkaku/token": "^0.
|
|
34
|
-
"@enkaku/transport": "^0.
|
|
17
|
+
"@enkaku/async": "^0.16.0",
|
|
18
|
+
"@enkaku/capability": "^0.16.0",
|
|
19
|
+
"@enkaku/client": "^0.16.0",
|
|
20
|
+
"@enkaku/codec": "^0.16.0",
|
|
21
|
+
"@enkaku/event": "^0.16.0",
|
|
22
|
+
"@enkaku/generator": "^0.16.0",
|
|
23
|
+
"@enkaku/group": "^0.16.2",
|
|
24
|
+
"@enkaku/http-client-transport": "^0.16.0",
|
|
25
|
+
"@enkaku/http-server-transport": "^0.16.0",
|
|
26
|
+
"@enkaku/hub-protocol": "^0.16.0",
|
|
27
|
+
"@enkaku/hub-tunnel": "^0.16.0",
|
|
28
|
+
"@enkaku/protocol": "^0.16.0",
|
|
29
|
+
"@enkaku/runtime": "^0.16.0",
|
|
30
|
+
"@enkaku/schema": "^0.16.0",
|
|
31
|
+
"@enkaku/server": "^0.16.0",
|
|
32
|
+
"@enkaku/stream": "^0.16.0",
|
|
33
|
+
"@enkaku/token": "^0.16.0",
|
|
34
|
+
"@enkaku/transport": "^0.16.0",
|
|
35
35
|
"@noble/hashes": "^2.2.0",
|
|
36
|
-
"graphql": "^16.
|
|
37
|
-
"kysely": "^0.
|
|
38
|
-
"@kubun/client": "^0.
|
|
39
|
-
"@kubun/db": "^0.
|
|
40
|
-
"@kubun/db
|
|
41
|
-
"@kubun/engine": "^0.
|
|
42
|
-
"@kubun/store-
|
|
43
|
-
"@kubun/store-
|
|
44
|
-
"@kubun/
|
|
45
|
-
"@kubun/
|
|
46
|
-
"@kubun/id": "^0.
|
|
47
|
-
"@kubun/
|
|
48
|
-
"@kubun/
|
|
49
|
-
"@kubun/protocol": "^0.
|
|
36
|
+
"graphql": "^16.14.2",
|
|
37
|
+
"kysely": "^0.29.2",
|
|
38
|
+
"@kubun/client": "^0.10.1",
|
|
39
|
+
"@kubun/db-adapter": "^0.10.0",
|
|
40
|
+
"@kubun/db": "^0.10.0",
|
|
41
|
+
"@kubun/engine": "^0.10.3",
|
|
42
|
+
"@kubun/store-graph": "^0.10.2",
|
|
43
|
+
"@kubun/store-p2p": "^0.10.1",
|
|
44
|
+
"@kubun/hlc": "^0.10.1",
|
|
45
|
+
"@kubun/graphql": "^0.10.1",
|
|
46
|
+
"@kubun/id": "^0.10.0",
|
|
47
|
+
"@kubun/mutation": "^0.10.1",
|
|
48
|
+
"@kubun/logger": "^0.10.0",
|
|
49
|
+
"@kubun/protocol": "^0.10.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@enkaku/hub-server": "^0.
|
|
52
|
+
"@enkaku/hub-server": "^0.16.0",
|
|
53
53
|
"get-port": "^7.2.0",
|
|
54
|
-
"@kubun/db-
|
|
55
|
-
"@kubun/
|
|
56
|
-
"@kubun/
|
|
57
|
-
"@kubun/
|
|
58
|
-
"@kubun/plugin-http": "^0.
|
|
59
|
-
"@kubun/plugin-rpc": "^0.
|
|
60
|
-
"@kubun/test-utils": "^0.
|
|
54
|
+
"@kubun/db-better-sqlite": "^0.10.0",
|
|
55
|
+
"@kubun/hub": "^0.10.0",
|
|
56
|
+
"@kubun/plugin-connector": "^0.10.0",
|
|
57
|
+
"@kubun/db-node-sqlite": "^0.10.0",
|
|
58
|
+
"@kubun/plugin-http": "^0.10.0",
|
|
59
|
+
"@kubun/plugin-rpc": "^0.10.0",
|
|
60
|
+
"@kubun/test-utils": "^0.10.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build:clean": "del lib",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { CatalogFilterCriteria, DocumentNode } from '@kubun/protocol';
|
|
2
|
-
import type { P2PStoreAPI } from '@kubun/store-p2p';
|
|
3
|
-
/**
|
|
4
|
-
* Determine whether a document matches a catalog's filter criteria.
|
|
5
|
-
*
|
|
6
|
-
* Mirrors the semantics of {@link resolveCatalogSyncScopes} (which collects
|
|
7
|
-
* broadly-applicable models + owner filters for sync): each catalog field is
|
|
8
|
-
* AND'd, values within a field are OR'd, and the `circles` field expands to a
|
|
9
|
-
* union of circle members and merges with the explicit `owners` filter.
|
|
10
|
-
*
|
|
11
|
-
* Returns `true` iff the document would be included by this catalog.
|
|
12
|
-
*/
|
|
13
|
-
export declare function catalogMatchesDoc(criteria: CatalogFilterCriteria, doc: Pick<DocumentNode, 'model' | 'owner'>, p2pStore: P2PStoreAPI): Promise<boolean>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export async function catalogMatchesDoc(e,l,n){if(null!=e.models&&e.models.length>0&&!e.models.includes(l.model))return!1;let c=null!=e.owners&&e.owners.length>0,r=null!=e.circles&&e.circles.length>0;return!!(!c&&!r||c&&e.owners?.includes(l.owner)||r&&null!=e.circles&&await n.isMemberOfAnyCircle(l.owner,e.circles))}
|