@kubun/plugin-p2p 0.8.1 → 0.8.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.
Files changed (56) hide show
  1. package/lib/context/group.d.ts +4 -0
  2. package/lib/context/group.js +1 -0
  3. package/lib/context/join.js +1 -1
  4. package/lib/context/sync.js +1 -1
  5. package/lib/context/types.d.ts +5 -5
  6. package/lib/groups/broadcast-codec.d.ts +3 -0
  7. package/lib/{spaces → groups}/broadcast-service.d.ts +33 -15
  8. package/lib/groups/broadcast-service.js +1 -0
  9. package/lib/{spaces → groups}/broadcast.d.ts +6 -6
  10. package/lib/groups/broadcast.js +1 -0
  11. package/lib/groups/events.d.ts +38 -0
  12. package/lib/{spaces → groups}/events.js +1 -1
  13. package/lib/{spaces → groups}/invite-payload.d.ts +2 -2
  14. package/lib/groups/join-utils.d.ts +13 -0
  15. package/lib/groups/join-utils.js +1 -0
  16. package/lib/{spaces → groups}/manager.d.ts +41 -39
  17. package/lib/groups/manager.js +1 -0
  18. package/lib/groups/mls-group-handle.d.ts +4 -0
  19. package/lib/groups/mls-group-handle.js +1 -0
  20. package/lib/groups/mls-state.d.ts +42 -0
  21. package/lib/groups/mls-state.js +1 -0
  22. package/lib/hub/connection-pool.d.ts +19 -0
  23. package/lib/hub/connection-pool.js +1 -0
  24. package/lib/hub/group-channel.d.ts +51 -0
  25. package/lib/hub/group-channel.js +1 -0
  26. package/lib/hub/receive-handler.d.ts +36 -0
  27. package/lib/hub/receive-handler.js +1 -0
  28. package/lib/hub/relay-manager.d.ts +37 -0
  29. package/lib/hub/relay-manager.js +1 -0
  30. package/lib/hub/send-handler.d.ts +28 -0
  31. package/lib/hub/send-handler.js +1 -0
  32. package/lib/index.d.ts +12 -10
  33. package/lib/index.js +1 -1
  34. package/lib/schema.d.ts +2 -2
  35. package/lib/schema.js +42 -42
  36. package/lib/sync/catalog-scope.js +1 -1
  37. package/lib/sync/peer-registry.js +1 -1
  38. package/lib/types.d.ts +55 -45
  39. package/package.json +17 -15
  40. package/lib/context/space.d.ts +0 -4
  41. package/lib/context/space.js +0 -1
  42. package/lib/spaces/broadcast-codec.d.ts +0 -3
  43. package/lib/spaces/broadcast-service.js +0 -1
  44. package/lib/spaces/broadcast.js +0 -1
  45. package/lib/spaces/events.d.ts +0 -38
  46. package/lib/spaces/group-handle.d.ts +0 -3
  47. package/lib/spaces/group-handle.js +0 -1
  48. package/lib/spaces/join-utils.d.ts +0 -19
  49. package/lib/spaces/join-utils.js +0 -1
  50. package/lib/spaces/manager.js +0 -1
  51. package/lib/spaces/mls-state.d.ts +0 -27
  52. package/lib/spaces/mls-state.js +0 -1
  53. /package/lib/{spaces → groups}/broadcast-codec.js +0 -0
  54. /package/lib/{spaces → groups}/invite-payload.js +0 -0
  55. /package/lib/{spaces → groups}/mls-json.d.ts +0 -0
  56. /package/lib/{spaces → groups}/mls-json.js +0 -0
@@ -0,0 +1,36 @@
1
+ import type { Logger } from '@kubun/logger';
2
+ import type { P2PStoreAPI } from '@kubun/store-p2p';
3
+ import { type BroadcastService } from '../groups/broadcast-service.js';
4
+ /**
5
+ * Shape of a message pushed by `hub/receive`. Matches `@enkaku/hub-protocol`
6
+ * server-to-client push payload.
7
+ */
8
+ export type ReceivedHubMessage = {
9
+ sequenceID: string;
10
+ senderDID: string;
11
+ groupID?: string;
12
+ /** base64-encoded encrypted MLS application message */
13
+ payload: string;
14
+ };
15
+ export type HandleReceivedMessageParams = {
16
+ groupID: string;
17
+ deviceID: string;
18
+ message: ReceivedHubMessage;
19
+ p2pStore: P2PStoreAPI;
20
+ broadcastService: BroadcastService;
21
+ logger?: Logger;
22
+ };
23
+ export type HandleReceivedMessageResult = {
24
+ ack: boolean;
25
+ };
26
+ /**
27
+ * Apply a single hub-received message for a group.
28
+ *
29
+ * Returns `{ ack: true }` when the message has been processed to a state where
30
+ * redelivery serves no purpose (applied successfully, or decrypt failed with
31
+ * no recovery path). Returns `{ ack: false }` when the hub should redeliver
32
+ * (transient apply or save failure, MLS state missing, unexpected error).
33
+ * Skips with `{ ack: false }` on groupID mismatch so the message stays queued
34
+ * for whichever recipient it was intended for.
35
+ */
36
+ export declare function handleReceivedMessage(params: HandleReceivedMessageParams): Promise<HandleReceivedMessageResult>;
@@ -0,0 +1 @@
1
+ import{fromB64 as e}from"@enkaku/codec";import{ApplyError as r,DecryptError as a}from"../groups/broadcast-service.js";import{fromMLSStateRow as s,toMLSStateInsert as t}from"../groups/mls-state.js";export async function handleReceivedMessage(c){let n,{groupID:u,deviceID:i,message:o,p2pStore:p,broadcastService:l,logger:g}=c;if(o.groupID!==u)return g?.debug("hub message groupID mismatch, skipping",{groupID:u,messageGroupID:o.groupID,sequenceID:o.sequenceID}),{ack:!1};let d=await p.getMLSState(u,i);if(null==d)return g?.warn("mls state missing for group, skipping message",{groupID:u,sequenceID:o.sequenceID}),{ack:!1};let m=s(d),D=e(o.payload);try{n=await l.processReceived({groupID:u,encrypted:D,groupState:m})}catch(e){if(e instanceof a)return g?.debug("decrypt failed, acking and skipping",{groupID:u,sequenceID:o.sequenceID,error:e}),{ack:!0};if(e instanceof r)return g?.error("broadcast apply failed, will redeliver",{groupID:u,sequenceID:o.sequenceID,error:e}),{ack:!1};return g?.error("unexpected error processing hub message",{groupID:u,sequenceID:o.sequenceID,error:e}),{ack:!1}}try{await p.saveMLSState(t(n.updatedGroupState,u,i))}catch(e){return g?.error("save mls state failed after apply, will redeliver",{groupID:u,sequenceID:o.sequenceID,error:e}),{ack:!1}}return{ack:!0}}
@@ -0,0 +1,37 @@
1
+ import type { Client } from '@enkaku/client';
2
+ import type { HubProtocol } from '@enkaku/hub-protocol';
3
+ import type { Logger } from '@kubun/logger';
4
+ import type { P2PStoreAPI } from '@kubun/store-p2p';
5
+ import type { GroupBroadcastMessage } from '../groups/broadcast.js';
6
+ import type { BroadcastService } from '../groups/broadcast-service.js';
7
+ import { GroupChannel, type GroupChannelParams } from './group-channel.js';
8
+ export type CreateHubClient = (hubURL: string) => Promise<Client<HubProtocol>>;
9
+ export type CreateGroupChannel = (params: GroupChannelParams) => GroupChannel;
10
+ export type HubRelayManagerParams = {
11
+ p2pStore: P2PStoreAPI;
12
+ broadcastService: BroadcastService;
13
+ deviceID: string;
14
+ createHubClient: CreateHubClient;
15
+ logger?: Logger;
16
+ ackFlushMs?: number;
17
+ ackFlushMax?: number;
18
+ backoffBaseMs?: number;
19
+ backoffMaxMs?: number;
20
+ backoffJitter?: number;
21
+ createGroupChannel?: CreateGroupChannel;
22
+ };
23
+ export declare class HubRelayManager {
24
+ #private;
25
+ constructor(params: HubRelayManagerParams);
26
+ /**
27
+ * Open channels for each group in parallel. Per-group failures are logged
28
+ * and swallowed: this method never rejects, so a single bad group does not
29
+ * block the others from starting. Callers who need per-group readiness
30
+ * signals must check `addGroup` / `broadcast` results individually.
31
+ */
32
+ start(groupIDs: Array<string>): Promise<void>;
33
+ addGroup(groupID: string): Promise<void>;
34
+ removeGroup(groupID: string): Promise<void>;
35
+ broadcast(groupID: string, message: GroupBroadcastMessage): Promise<void>;
36
+ stop(): Promise<void>;
37
+ }
@@ -0,0 +1 @@
1
+ import{HubConnectionPool as e}from"./connection-pool.js";import{GroupChannel as a}from"./group-channel.js";export class HubRelayManager{#e;#a;#t;#s;#r;#o;#i;#l;#c;#h;#u;#p=new Map;constructor(t){this.#e=t.p2pStore,this.#a=t.broadcastService,this.#t=t.deviceID,this.#r=t.logger,this.#o=t.ackFlushMs??500,this.#i=t.ackFlushMax??10,this.#l=t.backoffBaseMs??1e3,this.#c=t.backoffMaxMs??6e4,this.#h=t.backoffJitter??.25,this.#s=new e({createHubClient:t.createHubClient,logger:t.logger}),this.#u=t.createGroupChannel??(e=>new a(e))}async start(e){(await Promise.allSettled(e.map(e=>this.addGroup(e)))).forEach((a,t)=>{"rejected"===a.status&&this.#r?.error("addGroup failed during start",{groupID:e[t],error:a.reason})})}async addGroup(e){if(this.#p.has(e))return void this.#r?.debug("group already added",{groupID:e});let a=await this.#e.getGroup(e);if(null==a)throw Error(`Group ${e} not found`);let t=a.hub_urls;if(null==t||0===t.length)return void this.#r?.warn("group has no hub URLs, skipping",{groupID:e});t.length>1&&this.#r?.warn("multi-hub not yet supported, using first",{groupID:e,hubURLs:t});let s=this.#u({groupID:e,hubURL:t[0],deviceID:this.#t,pool:this.#s,broadcastService:this.#a,p2pStore:this.#e,logger:this.#r,ackFlushMs:this.#o,ackFlushMax:this.#i,backoffBaseMs:this.#l,backoffMaxMs:this.#c,backoffJitter:this.#h});await s.open(),this.#p.set(e,s)}async removeGroup(e){let a=this.#p.get(e);null!=a&&(this.#p.delete(e),await a.close())}async broadcast(e,a){let t=this.#p.get(e);if(null==t)throw Error(`Group ${e} is not active in HubRelayManager`);await t.broadcast(a)}async stop(){let e=[...this.#p.values()];this.#p.clear(),(await Promise.allSettled(e.map(e=>e.close()))).forEach(e=>{"rejected"===e.status&&this.#r?.warn("GroupChannel close failed",{error:e.reason})}),await this.#s.disposeAll()}}
@@ -0,0 +1,28 @@
1
+ import type { Client } from '@enkaku/client';
2
+ import type { HubProtocol } from '@enkaku/hub-protocol';
3
+ import type { Logger } from '@kubun/logger';
4
+ import type { P2PStoreAPI } from '@kubun/store-p2p';
5
+ import type { GroupBroadcastMessage } from '../groups/broadcast.js';
6
+ import type { BroadcastService } from '../groups/broadcast-service.js';
7
+ export type HandleSendBroadcastParams = {
8
+ groupID: string;
9
+ deviceID: string;
10
+ message: GroupBroadcastMessage;
11
+ p2pStore: P2PStoreAPI;
12
+ broadcastService: BroadcastService;
13
+ hubClient: Client<HubProtocol>;
14
+ logger?: Logger;
15
+ };
16
+ /**
17
+ * Encrypt + send a broadcast for a group, then persist the advanced MLS state.
18
+ *
19
+ * Failure semantics:
20
+ * - Missing MLS state for (group, device) → throw, caller cannot broadcast.
21
+ * - `prepareSend` failure → throw, no save (epoch unchanged on disk).
22
+ * - `hub/group/send` failure → throw, do NOT save advanced MLS state.
23
+ * Prevents an MLS epoch wedge where local state outpaces recipients.
24
+ * - `saveMLSState` failure → throw. State has advanced in memory; on next call
25
+ * the load-from-disk path will re-derive a fresh handle from the prior epoch
26
+ * and re-encrypt. (Wedge protection only applies pre-save by design.)
27
+ */
28
+ export declare function handleSendBroadcast(params: HandleSendBroadcastParams): Promise<void>;
@@ -0,0 +1 @@
1
+ import{toB64 as a}from"@enkaku/codec";import{fromMLSStateRow as r,toMLSStateInsert as t}from"../groups/mls-state.js";export async function handleSendBroadcast(e){let{groupID:o,deviceID:s,message:n,p2pStore:i,broadcastService:p,hubClient:d,logger:u}=e,c=await i.getMLSState(o,s);if(null==c)throw Error(`MLS state missing for group ${o}; cannot broadcast`);let l=r(c),{encrypted:m,updatedGroupState:S}=await p.prepareSend({groupID:o,message:n,groupState:l});try{await d.request("hub/group/send",{param:{groupID:o,payload:a(m)}})}catch(a){throw u?.error("hub/group/send failed",{groupID:o,error:a}),a}await i.saveMLSState(t(S,o,s))}
package/lib/index.d.ts CHANGED
@@ -1,22 +1,24 @@
1
1
  import type { KubunPlugin, PluginFactoryParams } from '@kubun/engine';
2
2
  export type { ContextDeps, PendingJoinRequest } from './context/types.js';
3
+ export { type GroupBroadcastMessage, type ProcessBroadcastParams, processBroadcast, } from './groups/broadcast.js';
4
+ export { deserializeBroadcast, serializeBroadcast } from './groups/broadcast-codec.js';
5
+ export { ApplyError, type BroadcastEvent, BroadcastService, type BroadcastServiceEvents, type BroadcastServiceParams, DecryptError, type ReceiveBroadcastParams, type ReceiveBroadcastResult, type SendBroadcastParams, type SendBroadcastResult, } from './groups/broadcast-service.js';
6
+ export { createFilteredGenerator, createGroupEventEmitter, type GroupEventEmitter, type GroupEventMap, } from './groups/events.js';
7
+ export { decodeFullJoinRequest, decodeInvitePayload, decodeJoinRequest, encodeFullJoinRequest, encodeInvitePayload, encodeJoinRequest, type FullJoinRequestPayload, type InvitePayload, type JoinRequestPayload, } from './groups/invite-payload.js';
8
+ export { type AddCircleMemberParams, type AddCircleMemberResult, type CreateCircleParams, type CreateCircleResult, type CreateGroupParams, type CreateGroupResult, type DeleteCircleParams, type DeleteCircleResult, GroupManager, type GroupManagerParams, type InviteToGroupParams, type InviteToGroupResult, type JoinGroupParams, type JoinGroupResult, type LeaveGroupParams, type LeaveGroupResult, type RemoveCircleMemberParams, type RemoveCircleMemberResult, type RemoveGroupMemberParams, type RemoveGroupMemberResult, type RemoveMemberParams, type RemoveMemberResult, type UpdateCircleParams, type UpdateCircleResult, type UpdateGroupParams, type UpdateGroupResult, } from './groups/manager.js';
9
+ export { type MLSGroupHandle, restoreMLSGroupHandle } from './groups/mls-group-handle.js';
10
+ export { replacer as mlsJSONReplacer, reviver as mlsJSONReviver } from './groups/mls-json.js';
11
+ export { deserializeMLSGroupState, type MLSGroupState, type SerializedMLSGroupState, serializeMLSGroupState, } from './groups/mls-state.js';
3
12
  export type { SyncClientMessage, SyncProtocol, SyncServerMessage } from './protocol.js';
4
13
  export { createP2PSchemaExtension } from './schema.js';
5
- export { type ProcessBroadcastParams, processBroadcast, type SpaceBroadcastMessage, } from './spaces/broadcast.js';
6
- export { deserializeBroadcast, serializeBroadcast } from './spaces/broadcast-codec.js';
7
- export { type BroadcastEvent, BroadcastService, type BroadcastServiceEvents, type BroadcastServiceParams, type ReceiveBroadcastParams, type ReceiveBroadcastResult, type SendBroadcastParams, type SendBroadcastResult, } from './spaces/broadcast-service.js';
8
- export { createFilteredGenerator, createSpaceEventEmitter, type SpaceEventEmitter, type SpaceEventMap, } from './spaces/events.js';
9
- export { restoreGroupHandle } from './spaces/group-handle.js';
10
- export { decodeFullJoinRequest, decodeInvitePayload, decodeJoinRequest, encodeFullJoinRequest, encodeInvitePayload, encodeJoinRequest, type FullJoinRequestPayload, type InvitePayload, type JoinRequestPayload, } from './spaces/invite-payload.js';
11
- export { type AddCircleMemberParams, type AddCircleMemberResult, type CreateCircleParams, type CreateCircleResult, type CreateSpaceParams, type CreateSpaceResult, type DeleteCircleParams, type DeleteCircleResult, type InviteToSpaceParams, type InviteToSpaceResult, type JoinSpaceParams, type JoinSpaceResult, type LeaveSpaceParams, type LeaveSpaceResult, type RemoveCircleMemberParams, type RemoveCircleMemberResult, type RemoveMemberParams, type RemoveMemberResult, type RemoveSpaceMemberParams, type RemoveSpaceMemberResult, SpaceManager, type SpaceManagerParams, type UpdateCircleParams, type UpdateCircleResult, type UpdateSpaceParams, type UpdateSpaceResult, } from './spaces/manager.js';
12
- export { replacer as mlsJSONReplacer, reviver as mlsJSONReviver } from './spaces/mls-json.js';
13
- export { deserializeGroupState, type GroupState, type SerializedGroupState, serializeGroupState, } from './spaces/mls-state.js';
14
14
  export { type CatalogSyncScope, resolveCatalogSyncScopes } from './sync/catalog-scope.js';
15
15
  export { checkSyncDelegation, negotiateDirection } from './sync/handlers.js';
16
16
  export { type PeerConfig, type PeerConfigWithID, PeerRegistry } from './sync/peer-registry.js';
17
17
  export { SyncClient, type SyncClientParams, type SyncScope, type SyncTransportProvider, } from './sync/sync-client.js';
18
18
  export { type SyncEvent, SyncManager, type SyncManagerParams, type SyncSessionInfo, type SyncStatus, } from './sync/sync-manager.js';
19
- export type { CatalogData, CircleData, CircleMemberData, DeleteCatalogData, DeleteCircleData, InviteToSpaceData, JoinSpaceData, LeaveSpaceData, P2PJoinRequestContext, RemoveCircleMemberData, RemoveSpaceMemberData, SpaceData, SpaceMemberData, SpaceRequestContext, SyncPluginAPI, SyncRequestContext, SyncSessionResult, UpdateCircleInput, UpdateSpaceInput, } from './types.js';
19
+ export type { GroupChannel, GroupChannelParams } from './hub/group-channel.js';
20
+ export { type CreateGroupChannel, type CreateHubClient, HubRelayManager, type HubRelayManagerParams, } from './hub/relay-manager.js';
21
+ export type { CatalogData, CircleData, CircleMemberData, DeleteCatalogData, DeleteCircleData, GroupData, GroupMemberData, GroupRequestContext, InviteToGroupData, JoinGroupData, LeaveGroupData, P2PContext, P2PJoinRequestContext, RemoveCircleMemberData, RemoveGroupMemberData, SyncPluginAPI, SyncRequestContext, SyncSessionResult, UpdateCircleInput, UpdateGroupInput, } from './types.js';
20
22
  export type P2PPluginOptions = {
21
23
  autoAcceptPeers?: Array<string>;
22
24
  http?: boolean | string;
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- import{ServerTransport as e}from"@enkaku/http-server-transport";import{Server as r}from"@enkaku/server";import{DirectTransports as t}from"@enkaku/transport";import{HLC as s}from"@kubun/hlc";import{spaceStoreDefinition as o}from"@kubun/store-space";import{createJoinContext as n}from"./context/join.js";import{createSpaceContext as a}from"./context/space.js";import{createSyncContext as c}from"./context/sync.js";import{createP2PSchemaExtension as i}from"./schema.js";import{createSpaceEventEmitter as p}from"./spaces/events.js";import{SpaceManager as m}from"./spaces/manager.js";import{createSyncHandlers as d}from"./sync/handlers.js";import{SyncManager as l}from"./sync/sync-manager.js";export{createP2PSchemaExtension}from"./schema.js";export{processBroadcast}from"./spaces/broadcast.js";export{deserializeBroadcast,serializeBroadcast}from"./spaces/broadcast-codec.js";export{BroadcastService}from"./spaces/broadcast-service.js";export{createFilteredGenerator,createSpaceEventEmitter}from"./spaces/events.js";export{restoreGroupHandle}from"./spaces/group-handle.js";export{decodeFullJoinRequest,decodeInvitePayload,decodeJoinRequest,encodeFullJoinRequest,encodeInvitePayload,encodeJoinRequest}from"./spaces/invite-payload.js";export{SpaceManager}from"./spaces/manager.js";export{replacer as mlsJSONReplacer,reviver as mlsJSONReviver}from"./spaces/mls-json.js";export{deserializeGroupState,serializeGroupState}from"./spaces/mls-state.js";export{resolveCatalogSyncScopes}from"./sync/catalog-scope.js";export{checkSyncDelegation,negotiateDirection}from"./sync/handlers.js";export{PeerRegistry}from"./sync/peer-registry.js";export{SyncClient}from"./sync/sync-client.js";export{SyncManager}from"./sync/sync-manager.js";export function createP2PPlugin(g){return y=>{let u,f;y.db.register(o);let j=y.db.adapter,h=y.identity,v=new s({nodeID:h.id}),x=new m({identity:h,getRandomID:y.runtime.getRandomID}),P=p(),S=new l({deployClusters:async e=>{let r=Object.values(e);await y.engine.deployGraph({clusters:r})},runtime:y.runtime,identity:y.identity,logger:y.getLogger("sync")}),b=d({db:y.db,logger:y.getLogger("sync-handlers"),peerRegistry:S.peerRegistry}),R=new Map,w=[],k={addPeer:e=>S.addPeer({config:e,stores:y.db}),removePeer:e=>S.removePeer({peerDID:e,stores:y.db}),listPeers:()=>S.listPeers({stores:y.db}),syncWithPeer:e=>S.merkleSyncWithPeer({...e,stores:y.db}),getStatus:e=>S.getStatus({peerDID:e,stores:y.db}),onSyncEvent:e=>S.onSyncEvent(e),createSyncTransport:function(e){let s=new t({signal:e}),o=new r({accessControl:!1,getRandomID:y.runtime.getRandomID,handlers:b,logger:y.getLogger("sync-server"),transports:[s.server],signal:e});return w.push(o),s.client}};if(g?.http){let t="string"==typeof g.http?g.http.replace(/^\//,""):"sync";k.syncReady=(async()=>{let s=await y.engine.getAPI("http");await s.listening,u=new e({allowedOrigin:["*"]}),f=new r({accessControl:!1,getRandomID:y.runtime.getRandomID,handlers:b,logger:y.getLogger("sync-http-server"),transports:[u]}),w.push(f),s.registerProtocol(t,u.fetch.bind(u))})()}return{name:"p2p",schemaExtension:e=>i(P),api:k,createContextFactory:()=>(e,r)=>{let t={identity:h,spaceManager:x,syncManager:S,stores:r,adapter:j,hlc:v,emitter:P,pendingJoinRequests:R,runtime:y.runtime,autoAcceptPeers:g?.autoAcceptPeers};return{...a(e,t),...c(e,t),...n(e,t)}},dispose:async()=>{for(let e of w)await e.dispose();await u?.dispose(),await S.dispose()}}}}
1
+ import{ServerTransport as e}from"@enkaku/http-server-transport";import{Server as r}from"@enkaku/server";import{DirectTransports as t}from"@enkaku/transport";import{HLC as o}from"@kubun/hlc";import{p2pStoreDefinition as s}from"@kubun/store-p2p";import{createGroupContext as n}from"./context/group.js";import{createJoinContext as a}from"./context/join.js";import{createSyncContext as i}from"./context/sync.js";import{createGroupEventEmitter as p}from"./groups/events.js";import{GroupManager as c}from"./groups/manager.js";import{createP2PSchemaExtension as m}from"./schema.js";import{createSyncHandlers as g}from"./sync/handlers.js";import{SyncManager as d}from"./sync/sync-manager.js";export{processBroadcast}from"./groups/broadcast.js";export{deserializeBroadcast,serializeBroadcast}from"./groups/broadcast-codec.js";export{ApplyError,BroadcastService,DecryptError}from"./groups/broadcast-service.js";export{createFilteredGenerator,createGroupEventEmitter}from"./groups/events.js";export{decodeFullJoinRequest,decodeInvitePayload,decodeJoinRequest,encodeFullJoinRequest,encodeInvitePayload,encodeJoinRequest}from"./groups/invite-payload.js";export{GroupManager}from"./groups/manager.js";export{restoreMLSGroupHandle}from"./groups/mls-group-handle.js";export{replacer as mlsJSONReplacer,reviver as mlsJSONReviver}from"./groups/mls-json.js";export{deserializeMLSGroupState,serializeMLSGroupState}from"./groups/mls-state.js";export{createP2PSchemaExtension}from"./schema.js";export{resolveCatalogSyncScopes}from"./sync/catalog-scope.js";export{checkSyncDelegation,negotiateDirection}from"./sync/handlers.js";export{PeerRegistry}from"./sync/peer-registry.js";export{SyncClient}from"./sync/sync-client.js";export{SyncManager}from"./sync/sync-manager.js";export{HubRelayManager}from"./hub/relay-manager.js";export function createP2PPlugin(l){return u=>{let y,f;u.db.register(s);let j=u.db.adapter,h=u.identity,v=new o({nodeID:h.id}),x=new c({identity:h,getRandomID:u.runtime.getRandomID}),P=p(),S=new d({deployClusters:async e=>{let r=Object.values(e);await u.engine.deployGraph({clusters:r})},runtime:u.runtime,identity:u.identity,logger:u.getLogger("sync")}),b=g({db:u.db,logger:u.getLogger("sync-handlers"),peerRegistry:S.peerRegistry}),R=new Map,w=[],k={addPeer:e=>S.addPeer({config:e,stores:u.db}),removePeer:e=>S.removePeer({peerDID:e,stores:u.db}),listPeers:()=>S.listPeers({stores:u.db}),syncWithPeer:e=>S.merkleSyncWithPeer({...e,stores:u.db}),getStatus:e=>S.getStatus({peerDID:e,stores:u.db}),onSyncEvent:e=>S.onSyncEvent(e),createSyncTransport:function(e){let o=new t({signal:e}),s=new r({accessControl:!1,getRandomID:u.runtime.getRandomID,handlers:b,logger:u.getLogger("sync-server"),transports:[o.server],signal:e});return w.push(s),o.client}};if(l?.http){let t="string"==typeof l.http?l.http.replace(/^\//,""):"sync";k.syncReady=(async()=>{let o=await u.engine.getAPI("http");await o.listening,y=new e({allowedOrigin:["*"]}),f=new r({accessControl:!1,getRandomID:u.runtime.getRandomID,handlers:b,logger:u.getLogger("sync-http-server"),transports:[y]}),w.push(f),o.registerProtocol(t,y.fetch.bind(y))})()}return{name:"p2p",schemaExtension:e=>m(P),api:k,createContextFactory:()=>(e,r)=>{let t={identity:h,groupManager:x,syncManager:S,stores:r,adapter:j,hlc:v,emitter:P,pendingJoinRequests:R,runtime:u.runtime,autoAcceptPeers:l?.autoAcceptPeers};return{group:n(e,t),sync:i(e,t),join:a(e,t)}},dispose:async()=>{for(let e of w)await e.dispose();await y?.dispose(),await S.dispose()}}}}
package/lib/schema.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import type { SchemaExtension } from '@kubun/engine';
2
- import { type SpaceEventEmitter } from './spaces/events.js';
3
- export declare function createP2PSchemaExtension(emitter: SpaceEventEmitter): SchemaExtension;
2
+ import { type GroupEventEmitter } from './groups/events.js';
3
+ export declare function createP2PSchemaExtension(emitter: GroupEventEmitter): SchemaExtension;
package/lib/schema.js CHANGED
@@ -1,5 +1,5 @@
1
- import{createFilteredGenerator as e}from"./spaces/events.js";let a=`
2
- type PeerSpace {
1
+ import{createFilteredGenerator as e}from"./groups/events.js";function r(e){if(null==e.p2p)throw Error("p2p plugin is not wired into this context");return e.p2p}let t=`
2
+ type PeerGroup {
3
3
  id: ID!
4
4
  name: String!
5
5
  description: String
@@ -7,8 +7,8 @@ type PeerSpace {
7
7
  createdAt: String!
8
8
  }
9
9
 
10
- type PeerSpaceMember {
11
- spaceID: ID!
10
+ type PeerGroupMember {
11
+ groupID: ID!
12
12
  memberDID: String!
13
13
  role: String!
14
14
  createdAt: String!
@@ -16,7 +16,7 @@ type PeerSpaceMember {
16
16
 
17
17
  type PeerCircle {
18
18
  id: ID!
19
- spaceID: ID!
19
+ groupID: ID!
20
20
  name: String!
21
21
  description: String
22
22
  createdAt: String!
@@ -38,23 +38,23 @@ type PeerCatalog {
38
38
  createdAt: String!
39
39
  }
40
40
 
41
- input CreateSpaceInput {
41
+ input CreateGroupInput {
42
42
  name: String!
43
43
  description: String
44
44
  }
45
45
 
46
- type CreateSpacePayload {
47
- space: PeerSpace!
46
+ type CreateGroupPayload {
47
+ group: PeerGroup!
48
48
  }
49
49
 
50
- type InviteToSpacePayload {
50
+ type InviteToGroupPayload {
51
51
  invitePayload: String!
52
- spaceID: ID!
53
- spaceName: String!
52
+ groupID: ID!
53
+ groupName: String!
54
54
  }
55
55
 
56
- type JoinSpacePayload {
57
- space: PeerSpace
56
+ type JoinGroupPayload {
57
+ group: PeerGroup
58
58
  }
59
59
 
60
60
  type PrepareJoinRequestPayload {
@@ -62,15 +62,15 @@ type PrepareJoinRequestPayload {
62
62
  }
63
63
 
64
64
  type CompleteJoinPayload {
65
- space: PeerSpace
65
+ group: PeerGroup
66
66
  }
67
67
 
68
- type LeaveSpacePayload {
69
- spaceID: ID!
68
+ type LeaveGroupPayload {
69
+ groupID: ID!
70
70
  }
71
71
 
72
- type RemoveSpaceMemberPayload {
73
- spaceID: ID!
72
+ type RemoveGroupMemberPayload {
73
+ groupID: ID!
74
74
  }
75
75
 
76
76
  input UpdateCatalogInput {
@@ -79,14 +79,14 @@ input UpdateCatalogInput {
79
79
  filterCriteria: String
80
80
  }
81
81
 
82
- input UpdateSpaceInput {
83
- spaceID: ID!
82
+ input UpdateGroupInput {
83
+ groupID: ID!
84
84
  name: String
85
85
  description: String
86
86
  }
87
87
 
88
- type UpdateSpacePayload {
89
- space: PeerSpace!
88
+ type UpdateGroupPayload {
89
+ group: PeerGroup!
90
90
  }
91
91
 
92
92
  input UpdateCircleInput {
@@ -141,28 +141,28 @@ type RemoveSyncPeerPayload {
141
141
  }
142
142
 
143
143
  extend type Query {
144
- spaces: [PeerSpace!]!
145
- space(id: ID!): PeerSpace
146
- spaceMembers(spaceID: ID!): [PeerSpaceMember!]!
147
- circles(spaceID: ID!): [PeerCircle!]!
144
+ groups: [PeerGroup!]!
145
+ group(id: ID!): PeerGroup
146
+ groupMembers(groupID: ID!): [PeerGroupMember!]!
147
+ circles(groupID: ID!): [PeerCircle!]!
148
148
  syncPeers: [SyncPeer!]!
149
149
  syncStatus: SyncStatus!
150
150
  }
151
151
 
152
152
  extend type Mutation {
153
- createSpace(input: CreateSpaceInput!): CreateSpacePayload!
154
- addSpaceMember(spaceID: ID!, memberDID: String!, role: String!): PeerSpaceMember!
155
- createCircle(spaceID: ID!, name: String!, description: String): PeerCircle!
153
+ createGroup(input: CreateGroupInput!): CreateGroupPayload!
154
+ addGroupMember(groupID: ID!, memberDID: String!, role: String!): PeerGroupMember!
155
+ createCircle(groupID: ID!, name: String!, description: String): PeerCircle!
156
156
  addCircleMember(circleID: ID!, memberDID: String!, role: String!): PeerCircleMember!
157
157
  createCatalog(name: String!, description: String, filterCriteria: String!): PeerCatalog!
158
- inviteToSpace(spaceID: ID!, joinRequest: String!): InviteToSpacePayload!
159
- joinSpace(invitePayload: String!, joinRequestPayload: String!): JoinSpacePayload!
158
+ inviteToGroup(groupID: ID!, joinRequest: String!): InviteToGroupPayload!
159
+ joinGroup(invitePayload: String!, joinRequestPayload: String!): JoinGroupPayload!
160
160
  prepareJoinRequest: PrepareJoinRequestPayload!
161
161
  completeJoin(invitePayload: String!): CompleteJoinPayload!
162
- leaveSpace(spaceID: ID!): LeaveSpacePayload!
163
- removeSpaceMember(spaceID: ID!, memberDID: String!): RemoveSpaceMemberPayload!
162
+ leaveGroup(groupID: ID!): LeaveGroupPayload!
163
+ removeGroupMember(groupID: ID!, memberDID: String!): RemoveGroupMemberPayload!
164
164
  updateCatalog(input: UpdateCatalogInput!): PeerCatalog!
165
- updateSpace(input: UpdateSpaceInput!): UpdateSpacePayload!
165
+ updateGroup(input: UpdateGroupInput!): UpdateGroupPayload!
166
166
  updateCircle(input: UpdateCircleInput!): UpdateCirclePayload!
167
167
  deleteCatalog(catalogID: ID!): DeleteCatalogPayload!
168
168
  deleteCircle(circleID: ID!): DeleteCirclePayload!
@@ -172,16 +172,16 @@ extend type Mutation {
172
172
  }
173
173
 
174
174
  extend type Subscription {
175
- spaceJoined: PeerSpace!
176
- spaceLeft: PeerSpace!
177
- spaceDataChanged(spaceID: ID!): PeerSpace!
178
- spaceMemberJoined(spaceID: ID!): PeerSpaceMember!
179
- spaceMemberLeft(spaceID: ID!): PeerSpaceMember!
180
- circleCreated(spaceID: ID!): PeerCircle!
181
- circleDeleted(spaceID: ID!): PeerCircle!
175
+ groupJoined: PeerGroup!
176
+ groupLeft: PeerGroup!
177
+ groupDataChanged(groupID: ID!): PeerGroup!
178
+ groupMemberJoined(groupID: ID!): PeerGroupMember!
179
+ groupMemberLeft(groupID: ID!): PeerGroupMember!
180
+ circleCreated(groupID: ID!): PeerCircle!
181
+ circleDeleted(groupID: ID!): PeerCircle!
182
182
  circleDataChanged(circleID: ID!): PeerCircle!
183
183
  circleMemberAdded(circleID: ID!): PeerCircleMember!
184
184
  circleMemberRemoved(circleID: ID!): PeerCircleMember!
185
185
  circleCatalogsChanged(circleID: ID!): PeerCircle!
186
186
  }
187
- `;export function createP2PSchemaExtension(r){return{sdl:a,resolvers:{queryFields:{spaces:async(e,a,r)=>await r.spaceGetSpaces(),space:async(e,a,r)=>await r.spaceGetSpace(a.id),spaceMembers:async(e,a,r)=>await r.spaceGetSpaceMembers(a.spaceID),circles:async(e,a,r)=>await r.spaceGetCircles(a.spaceID),syncPeers:async(e,a,r)=>await r.syncListPeers(),syncStatus:(e,a,r)=>{let c=r.syncGetStatus();return{...c,lastSyncByPeer:JSON.stringify(c.lastSyncByPeer)}}},mutationFields:{createSpace:async(e,a,r)=>{let c=a.input;return{space:await r.spaceCreateSpace(c)}},addSpaceMember:async(e,a,r)=>await r.spaceAddSpaceMember(a.spaceID,a.memberDID,a.role),createCircle:async(e,a,r)=>await r.spaceCreateCircle({spaceID:a.spaceID,name:a.name,description:a.description}),addCircleMember:async(e,a,r)=>await r.spaceAddCircleMember(a.circleID,a.memberDID,a.role),createCatalog:async(e,a,r)=>await r.spaceCreateCatalog({name:a.name,description:a.description,filterCriteria:a.filterCriteria}),inviteToSpace:async(e,a,r)=>await r.spaceInviteToSpace(a.spaceID,a.joinRequest),joinSpace:async(e,a,r)=>await r.spaceJoinSpace(a.invitePayload,a.joinRequestPayload),prepareJoinRequest:async(e,a,r)=>await r.p2pPrepareJoinRequest(),completeJoin:async(e,a,r)=>await r.p2pCompleteJoin(a.invitePayload),leaveSpace:async(e,a,r)=>await r.spaceLeaveSpace(a.spaceID),removeSpaceMember:async(e,a,r)=>await r.spaceRemoveSpaceMember(a.spaceID,a.memberDID),updateCatalog:async(e,a,r)=>{let c=a.input;return await r.spaceUpdateCatalog(c)},updateSpace:async(e,a,r)=>{let c=a.input;return{space:await r.spaceUpdateSpace(c)}},updateCircle:async(e,a,r)=>{let c=a.input;return{circle:await r.spaceUpdateCircle(c)}},deleteCatalog:async(e,a,r)=>await r.spaceDeleteCatalog(a.catalogID),deleteCircle:async(e,a,r)=>await r.spaceDeleteCircle(a.circleID),removeCircleMember:async(e,a,r)=>await r.spaceRemoveCircleMember(a.circleID,a.memberDID),addSyncPeer:async(e,a,r)=>(await r.syncAddPeer({peerDID:a.peerDID,endpoint:a.endpoint,mode:a.mode??"on-demand",allowedUsers:{all:!0},priority:a.priority??"0",trustLevel:a.trustLevel??"trusted"}),!0),removeSyncPeer:async(e,a,r)=>(await r.syncRemovePeer(a.peerDID),{success:!0})},subscriptionFields:{spaceJoined:{resolve:e=>e,subscribe:()=>e(r,"spaceJoined")},spaceLeft:{resolve:e=>e,subscribe:()=>e(r,"spaceLeft")},spaceDataChanged:{resolve:e=>e,subscribe:(a,c)=>e(r,"spaceDataChanged",e=>e.spaceID===c.spaceID)},spaceMemberJoined:{resolve:e=>e,subscribe:(a,c)=>e(r,"spaceMemberJoined",e=>e.spaceID===c.spaceID)},spaceMemberLeft:{resolve:e=>e,subscribe:(a,c)=>e(r,"spaceMemberLeft",e=>e.spaceID===c.spaceID)},circleCreated:{resolve:e=>e,subscribe:(a,c)=>e(r,"circleCreated",e=>e.spaceID===c.spaceID)},circleDeleted:{resolve:e=>e,subscribe:(a,c)=>e(r,"circleDeleted",e=>e.spaceID===c.spaceID)},circleDataChanged:{resolve:e=>e,subscribe:(a,c)=>e(r,"circleDataChanged",e=>e.circleID===c.circleID)},circleMemberAdded:{resolve:e=>e,subscribe:(a,c)=>e(r,"circleMemberAdded",e=>e.circleID===c.circleID)},circleMemberRemoved:{resolve:e=>e,subscribe:(a,c)=>e(r,"circleMemberRemoved",e=>e.circleID===c.circleID)},circleCatalogsChanged:{resolve:e=>e,subscribe:(a,c)=>e(r,"circleCatalogsChanged",e=>e.circleID===c.circleID)}}}}}
187
+ `;export function createP2PSchemaExtension(i){return{sdl:t,resolvers:{queryFields:{groups:async(e,t,i)=>await r(i).group.list(),group:async(e,t,i)=>await r(i).group.get(t.id),groupMembers:async(e,t,i)=>await r(i).group.listMembers(t.groupID),circles:async(e,t,i)=>await r(i).group.listCircles(t.groupID),syncPeers:async(e,t,i)=>await r(i).sync.listPeers(),syncStatus:(e,t,i)=>{let o=r(i).sync.getStatus();return{...o,lastSyncByPeer:JSON.stringify(o.lastSyncByPeer)}}},mutationFields:{createGroup:async(e,t,i)=>{let o=t.input;return{group:await r(i).group.create(o)}},addGroupMember:async(e,t,i)=>await r(i).group.addMember(t.groupID,t.memberDID,t.role),createCircle:async(e,t,i)=>await r(i).group.createCircle({groupID:t.groupID,name:t.name,description:t.description}),addCircleMember:async(e,t,i)=>await r(i).group.addCircleMember(t.circleID,t.memberDID,t.role),createCatalog:async(e,t,i)=>await r(i).group.createCatalog({name:t.name,description:t.description,filterCriteria:t.filterCriteria}),inviteToGroup:async(e,t,i)=>await r(i).group.invite(t.groupID,t.joinRequest),joinGroup:async(e,t,i)=>await r(i).group.join(t.invitePayload,t.joinRequestPayload),prepareJoinRequest:async(e,t,i)=>await r(i).join.prepareRequest(),completeJoin:async(e,t,i)=>await r(i).join.complete(t.invitePayload),leaveGroup:async(e,t,i)=>await r(i).group.leave(t.groupID),removeGroupMember:async(e,t,i)=>await r(i).group.removeMember(t.groupID,t.memberDID),updateCatalog:async(e,t,i)=>{let o=t.input;return await r(i).group.updateCatalog(o)},updateGroup:async(e,t,i)=>{let o=t.input;return{group:await r(i).group.update(o)}},updateCircle:async(e,t,i)=>{let o=t.input;return{circle:await r(i).group.updateCircle(o)}},deleteCatalog:async(e,t,i)=>await r(i).group.deleteCatalog(t.catalogID),deleteCircle:async(e,t,i)=>await r(i).group.deleteCircle(t.circleID),removeCircleMember:async(e,t,i)=>await r(i).group.removeCircleMember(t.circleID,t.memberDID),addSyncPeer:async(e,t,i)=>(await r(i).sync.addPeer({peerDID:t.peerDID,endpoint:t.endpoint,mode:t.mode??"on-demand",allowedUsers:{all:!0},priority:t.priority??"0",trustLevel:t.trustLevel??"trusted"}),!0),removeSyncPeer:async(e,t,i)=>(await r(i).sync.removePeer(t.peerDID),{success:!0})},subscriptionFields:{groupJoined:{resolve:e=>e,subscribe:()=>e(i,"groupJoined")},groupLeft:{resolve:e=>e,subscribe:()=>e(i,"groupLeft")},groupDataChanged:{resolve:e=>e,subscribe:(r,t)=>e(i,"groupDataChanged",e=>e.groupID===t.groupID)},groupMemberJoined:{resolve:e=>e,subscribe:(r,t)=>e(i,"groupMemberJoined",e=>e.groupID===t.groupID)},groupMemberLeft:{resolve:e=>e,subscribe:(r,t)=>e(i,"groupMemberLeft",e=>e.groupID===t.groupID)},circleCreated:{resolve:e=>e,subscribe:(r,t)=>e(i,"circleCreated",e=>e.groupID===t.groupID)},circleDeleted:{resolve:e=>e,subscribe:(r,t)=>e(i,"circleDeleted",e=>e.groupID===t.groupID)},circleDataChanged:{resolve:e=>e,subscribe:(r,t)=>e(i,"circleDataChanged",e=>e.circleID===t.circleID)},circleMemberAdded:{resolve:e=>e,subscribe:(r,t)=>e(i,"circleMemberAdded",e=>e.circleID===t.circleID)},circleMemberRemoved:{resolve:e=>e,subscribe:(r,t)=>e(i,"circleMemberRemoved",e=>e.circleID===t.circleID)},circleCatalogsChanged:{resolve:e=>e,subscribe:(r,t)=>e(i,"circleCatalogsChanged",e=>e.circleID===t.circleID)}}}}}
@@ -1 +1 @@
1
- import{getGraphStore as e}from"@kubun/store-graph";import{getSpaceStore as r}from"@kubun/store-space";export async function resolveCatalogSyncScopes(l,t,o){let[a,i]=await Promise.all([e(l),r(l)]),s=new Set,f=new Set,n=!1;for(let e of t){let r=await a.resolveCatalogScope(e);if(null!=r.models)for(let e of r.models)s.add(e);if(null!=r.owners)for(let e of(n=!0,r.owners))f.add(e);let l=await a.getCatalog(e);if(null!=l){let e=l.filter_criteria;if(null!=e.circles&&e.circles.length>0)for(let r of(n=!0,e.circles))for(let e of(await i.listCircleMembers(r)))f.add(e.member_did)}}let d=Array.from(s),m=n?Array.from(f):void 0,c=new Set(o??[]),u=new Set,w=new Set;return await Promise.all(d.map(async e=>{let r=await a.getClusterForModel(e);null!=r&&(u.add(r),c.has(e)||w.add(r))})),{modelIDs:d,owners:m,requiredClusterIDs:Array.from(u),missingClusterIDs:Array.from(w)}}
1
+ import{getGraphStore as e}from"@kubun/store-graph";import{getP2PStore as r}from"@kubun/store-p2p";export async function resolveCatalogSyncScopes(l,t,o){let[a,i]=await Promise.all([e(l),r(l)]),s=new Set,f=new Set,n=!1;for(let e of t){let r=await a.resolveCatalogScope(e);if(null!=r.models)for(let e of r.models)s.add(e);if(null!=r.owners)for(let e of(n=!0,r.owners))f.add(e);let l=await a.getCatalog(e);if(null!=l){let e=l.filter_criteria;if(null!=e.circles&&e.circles.length>0)for(let r of(n=!0,e.circles))for(let e of(await i.listCircleMembers(r)))f.add(e.member_did)}}let d=Array.from(s),m=n?Array.from(f):void 0,u=new Set(o??[]),c=new Set,w=new Set;return await Promise.all(d.map(async e=>{let r=await a.getClusterForModel(e);null!=r&&(c.add(r),u.has(e)||w.add(r))})),{modelIDs:d,owners:m,requiredClusterIDs:Array.from(c),missingClusterIDs:Array.from(w)}}
@@ -1 +1 @@
1
- import{getSpaceStore as e}from"@kubun/store-space";function t(e){var t,r;let i="string"==typeof(t=e.config)?JSON.parse(t):t??{};return{id:e.id,peerDID:e.peer_did,endpoint:e.endpoint,mode:e.mode,allowedUsers:"string"==typeof(r=e.allowed_users)?JSON.parse(r):r,priority:e.priority,trustLevel:e.trust_level,allowedDirection:i.allowedDirection,createdAt:e.created_at,updatedAt:e.updated_at}}export class PeerRegistry{#e;constructor(e){this.#e=e.runtime}async addPeer(t){let r=await e(t.stores);await r.addSyncPeer({id:this.#e.getRandomID(),peer_did:t.config.peerDID,endpoint:t.config.endpoint,mode:t.config.mode,allowed_users:t.config.allowedUsers,priority:t.config.priority,trust_level:t.config.trustLevel,config:{allowedDirection:t.config.allowedDirection},created_at:Date.now(),updated_at:Date.now()})}async getPeer(r){let i=await e(r.stores),o=await i.getSyncPeer(r.peerDID);if(o)return t(o)}async listPeers(r){let i=await e(r.stores);return(await i.listSyncPeers()).map(e=>t(e))}async updatePeer(t){let r=await this.getPeer({peerDID:t.peerDID,stores:t.stores});if(!r)throw Error(`Peer ${t.peerDID} not found`);let i={...r,...t.updates},o=await e(t.stores);await o.updateSyncPeer(t.peerDID,{endpoint:i.endpoint,mode:i.mode,allowed_users:i.allowedUsers,priority:i.priority,trust_level:i.trustLevel,config:{allowedDirection:i.allowedDirection}})}async removePeer(t){let r=await e(t.stores);await r.removeSyncPeer(t.peerDID)}async isPeerAllowed(e){return void 0!==await this.getPeer({peerDID:e.peerDID,stores:e.stores})}}
1
+ import{getP2PStore as e}from"@kubun/store-p2p";function t(e){var t,r;let i="string"==typeof(t=e.config)?JSON.parse(t):t??{};return{id:e.id,peerDID:e.peer_did,endpoint:e.endpoint,mode:e.mode,allowedUsers:"string"==typeof(r=e.allowed_users)?JSON.parse(r):r,priority:e.priority,trustLevel:e.trust_level,allowedDirection:i.allowedDirection,createdAt:e.created_at,updatedAt:e.updated_at}}export class PeerRegistry{#e;constructor(e){this.#e=e.runtime}async addPeer(t){let r=await e(t.stores);await r.addSyncPeer({id:this.#e.getRandomID(),peer_did:t.config.peerDID,endpoint:t.config.endpoint,mode:t.config.mode,allowed_users:t.config.allowedUsers,priority:t.config.priority,trust_level:t.config.trustLevel,config:{allowedDirection:t.config.allowedDirection},created_at:Date.now(),updated_at:Date.now()})}async getPeer(r){let i=await e(r.stores),o=await i.getSyncPeer(r.peerDID);if(o)return t(o)}async listPeers(r){let i=await e(r.stores);return(await i.listSyncPeers()).map(e=>t(e))}async updatePeer(t){let r=await this.getPeer({peerDID:t.peerDID,stores:t.stores});if(!r)throw Error(`Peer ${t.peerDID} not found`);let i={...r,...t.updates},o=await e(t.stores);await o.updateSyncPeer(t.peerDID,{endpoint:i.endpoint,mode:i.mode,allowed_users:i.allowedUsers,priority:i.priority,trust_level:i.trustLevel,config:{allowedDirection:i.allowedDirection}})}async removePeer(t){let r=await e(t.stores);await r.removeSyncPeer(t.peerDID)}async isPeerAllowed(e){return void 0!==await this.getPeer({peerDID:e.peerDID,stores:e.stores})}}
package/lib/types.d.ts CHANGED
@@ -3,22 +3,22 @@ import type { SyncDirection, SyncProtocol } from './protocol.js';
3
3
  import type { PeerConfig, PeerConfigWithID } from './sync/peer-registry.js';
4
4
  import type { SyncScope } from './sync/sync-client.js';
5
5
  import type { SyncEvent, SyncStatus } from './sync/sync-manager.js';
6
- export type SpaceData = {
6
+ export type GroupData = {
7
7
  id: string;
8
8
  name: string;
9
9
  description: string;
10
10
  createdBy: string;
11
11
  createdAt: string;
12
12
  };
13
- export type SpaceMemberData = {
14
- spaceID: string;
13
+ export type GroupMemberData = {
14
+ groupID: string;
15
15
  memberDID: string;
16
16
  role: string;
17
17
  createdAt: string;
18
18
  };
19
19
  export type CircleData = {
20
20
  id: string;
21
- spaceID: string;
21
+ groupID: string;
22
22
  name: string;
23
23
  description: string;
24
24
  createdAt: string;
@@ -37,19 +37,19 @@ export type CatalogData = {
37
37
  filterCriteria: string;
38
38
  createdAt: string;
39
39
  };
40
- export type InviteToSpaceData = {
40
+ export type InviteToGroupData = {
41
41
  invitePayload: string;
42
- spaceID: string;
43
- spaceName: string;
42
+ groupID: string;
43
+ groupName: string;
44
44
  };
45
- export type JoinSpaceData = {
46
- space: SpaceData | null;
45
+ export type JoinGroupData = {
46
+ group: GroupData | null;
47
47
  };
48
- export type LeaveSpaceData = {
49
- spaceID: string;
48
+ export type LeaveGroupData = {
49
+ groupID: string;
50
50
  };
51
- export type RemoveSpaceMemberData = {
52
- spaceID: string;
51
+ export type RemoveGroupMemberData = {
52
+ groupID: string;
53
53
  };
54
54
  export type DeleteCatalogData = {
55
55
  deletedID: string;
@@ -61,8 +61,8 @@ export type RemoveCircleMemberData = {
61
61
  circleID: string;
62
62
  memberDID: string;
63
63
  };
64
- export type UpdateSpaceInput = {
65
- spaceID: string;
64
+ export type UpdateGroupInput = {
65
+ groupID: string;
66
66
  name?: string;
67
67
  description?: string;
68
68
  };
@@ -72,57 +72,67 @@ export type UpdateCircleInput = {
72
72
  description?: string;
73
73
  catalogIDs?: Array<string>;
74
74
  };
75
- export type SpaceRequestContext = {
76
- spaceCreateSpace: (input: {
75
+ export type GroupRequestContext = {
76
+ create: (input: {
77
77
  name: string;
78
78
  description?: string;
79
- }) => Promise<SpaceData>;
80
- spaceGetSpaces: () => Promise<Array<SpaceData>>;
81
- spaceGetSpace: (id: string) => Promise<SpaceData | null>;
82
- spaceAddSpaceMember: (spaceID: string, memberDID: string, role: string) => Promise<SpaceMemberData>;
83
- spaceGetSpaceMembers: (spaceID: string) => Promise<Array<SpaceMemberData>>;
84
- spaceCreateCircle: (params: {
85
- spaceID: string;
79
+ }) => Promise<GroupData>;
80
+ list: () => Promise<Array<GroupData>>;
81
+ get: (id: string) => Promise<GroupData | null>;
82
+ addMember: (groupID: string, memberDID: string, role: string) => Promise<GroupMemberData>;
83
+ listMembers: (groupID: string) => Promise<Array<GroupMemberData>>;
84
+ createCircle: (params: {
85
+ groupID: string;
86
86
  name: string;
87
87
  description?: string;
88
88
  }) => Promise<CircleData>;
89
- spaceGetCircles: (spaceID: string) => Promise<Array<CircleData>>;
90
- spaceAddCircleMember: (circleID: string, memberDID: string, role: string) => Promise<CircleMemberData>;
91
- spaceCreateCatalog: (params: {
89
+ listCircles: (groupID: string) => Promise<Array<CircleData>>;
90
+ addCircleMember: (circleID: string, memberDID: string, role: string) => Promise<CircleMemberData>;
91
+ createCatalog: (params: {
92
92
  name: string;
93
93
  description?: string;
94
94
  filterCriteria: string;
95
95
  }) => Promise<CatalogData>;
96
- spaceGetCatalogs: (ownerDID?: string) => Promise<Array<CatalogData>>;
97
- spaceInviteToSpace: (spaceID: string, joinRequest: string) => Promise<InviteToSpaceData>;
98
- spaceJoinSpace: (invitePayload: string, joinRequestPayload: string) => Promise<JoinSpaceData>;
99
- spaceLeaveSpace: (spaceID: string) => Promise<LeaveSpaceData>;
100
- spaceRemoveSpaceMember: (spaceID: string, memberDID: string) => Promise<RemoveSpaceMemberData>;
101
- spaceUpdateSpace: (input: UpdateSpaceInput) => Promise<SpaceData>;
102
- spaceUpdateCircle: (input: UpdateCircleInput) => Promise<CircleData>;
103
- spaceUpdateCatalog: (params: {
96
+ listCatalogs: (ownerDID?: string) => Promise<Array<CatalogData>>;
97
+ invite: (groupID: string, joinRequest: string) => Promise<InviteToGroupData>;
98
+ join: (invitePayload: string, joinRequestPayload: string) => Promise<JoinGroupData>;
99
+ leave: (groupID: string) => Promise<LeaveGroupData>;
100
+ removeMember: (groupID: string, memberDID: string) => Promise<RemoveGroupMemberData>;
101
+ update: (input: UpdateGroupInput) => Promise<GroupData>;
102
+ updateCircle: (input: UpdateCircleInput) => Promise<CircleData>;
103
+ updateCatalog: (params: {
104
104
  catalogID: string;
105
105
  name?: string;
106
106
  filterCriteria?: string;
107
107
  }) => Promise<CatalogData>;
108
- spaceDeleteCatalog: (catalogID: string) => Promise<DeleteCatalogData>;
109
- spaceDeleteCircle: (circleID: string) => Promise<DeleteCircleData>;
110
- spaceRemoveCircleMember: (circleID: string, memberDID: string) => Promise<RemoveCircleMemberData>;
108
+ deleteCatalog: (catalogID: string) => Promise<DeleteCatalogData>;
109
+ deleteCircle: (circleID: string) => Promise<DeleteCircleData>;
110
+ removeCircleMember: (circleID: string, memberDID: string) => Promise<RemoveCircleMemberData>;
111
111
  };
112
112
  export type SyncRequestContext = {
113
- syncAddPeer: (config: PeerConfig) => Promise<void>;
114
- syncRemovePeer: (peerDID: string) => Promise<void>;
115
- syncListPeers: () => Promise<Array<PeerConfigWithID>>;
116
- syncGetStatus: (peerDID?: string) => SyncStatus;
113
+ addPeer: (config: PeerConfig) => Promise<void>;
114
+ removePeer: (peerDID: string) => Promise<void>;
115
+ listPeers: () => Promise<Array<PeerConfigWithID>>;
116
+ getStatus: (peerDID?: string) => SyncStatus;
117
117
  };
118
118
  export type P2PJoinRequestContext = {
119
- p2pPrepareJoinRequest: () => Promise<{
119
+ prepareRequest: () => Promise<{
120
120
  joinRequest: string;
121
121
  }>;
122
- p2pCompleteJoin: (invitePayload: string) => Promise<{
123
- space: SpaceData | null;
122
+ complete: (invitePayload: string) => Promise<{
123
+ group: GroupData | null;
124
124
  }>;
125
125
  };
126
+ export type P2PContext = {
127
+ group: GroupRequestContext;
128
+ sync: SyncRequestContext;
129
+ join: P2PJoinRequestContext;
130
+ };
131
+ declare module '@kubun/graphql' {
132
+ interface PluginContextMap {
133
+ p2p: P2PContext;
134
+ }
135
+ }
126
136
  export type SyncSessionResult = {
127
137
  sessionID: string;
128
138
  divergentBuckets: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/plugin-p2p",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "license": "see LICENSE.md",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -14,45 +14,47 @@
14
14
  ],
15
15
  "sideEffects": false,
16
16
  "dependencies": {
17
- "@enkaku/async": "^0.14.0",
17
+ "@enkaku/async": "^0.14.1",
18
18
  "@enkaku/capability": "^0.14.0",
19
- "@enkaku/client": "^0.14.3",
19
+ "@enkaku/client": "^0.14.4",
20
20
  "@enkaku/codec": "^0.14.0",
21
21
  "@enkaku/event": "^0.14.1",
22
22
  "@enkaku/generator": "^0.14.0",
23
23
  "@enkaku/group": "^0.14.6",
24
24
  "@enkaku/http-client-transport": "^0.14.3",
25
25
  "@enkaku/http-server-transport": "^0.14.2",
26
+ "@enkaku/hub-protocol": "^0.14.0",
26
27
  "@enkaku/protocol": "^0.14.0",
27
28
  "@enkaku/runtime": "^0.14.0",
28
29
  "@enkaku/schema": "^0.14.0",
29
- "@enkaku/server": "^0.14.3",
30
+ "@enkaku/server": "^0.14.4",
30
31
  "@enkaku/stream": "^0.14.1",
31
32
  "@enkaku/token": "0.14.1",
32
- "@enkaku/transport": "0.14.0",
33
+ "@enkaku/transport": "0.14.1",
33
34
  "@noble/hashes": "^2.2.0",
34
35
  "graphql": "^16.13.2",
35
36
  "kysely": "^0.28.16",
36
37
  "@kubun/client": "^0.8.0",
37
38
  "@kubun/db": "^0.8.1",
38
- "@kubun/engine": "^0.8.1",
39
- "@kubun/store-space": "^0.8.0",
39
+ "@kubun/engine": "^0.8.3",
40
40
  "@kubun/db-adapter": "^0.8.1",
41
- "@kubun/store-graph": "^0.8.0",
42
- "@kubun/graphql": "^0.8.1",
41
+ "@kubun/store-p2p": "^0.8.0",
42
+ "@kubun/store-graph": "^0.8.1",
43
+ "@kubun/graphql": "^0.8.2",
43
44
  "@kubun/hlc": "^0.8.0",
44
- "@kubun/id": "^0.8.0",
45
- "@kubun/logger": "^0.8.0",
46
45
  "@kubun/mutation": "^0.8.1",
47
- "@kubun/protocol": "^0.8.0"
46
+ "@kubun/protocol": "^0.8.1",
47
+ "@kubun/id": "^0.8.0",
48
+ "@kubun/logger": "^0.8.0"
48
49
  },
49
50
  "devDependencies": {
51
+ "@enkaku/hub-server": "^0.14.1",
50
52
  "get-port": "^7.2.0",
51
53
  "@kubun/db-better-sqlite": "^0.8.1",
52
- "@kubun/plugin-connector": "^0.8.1",
54
+ "@kubun/plugin-connector": "^0.8.2",
53
55
  "@kubun/plugin-http": "^0.8.1",
54
- "@kubun/test-utils": "^0.8.0",
55
- "@kubun/plugin-rpc": "^0.8.1"
56
+ "@kubun/plugin-rpc": "^0.8.1",
57
+ "@kubun/test-utils": "^0.8.0"
56
58
  },
57
59
  "scripts": {
58
60
  "build:clean": "del lib",