@snakechat/commskit-node 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/adapters-8ztjEYtz.d.ts +83 -0
- package/dist/call.d.ts +34 -0
- package/dist/call.js +51 -0
- package/dist/call.js.map +1 -0
- package/dist/chat.d.ts +64 -0
- package/dist/chat.js +30 -0
- package/dist/chat.js.map +1 -0
- package/dist/chunk-5BJ2SNM5.js +26 -0
- package/dist/chunk-5BJ2SNM5.js.map +1 -0
- package/dist/chunk-6I27CI32.js +586 -0
- package/dist/chunk-6I27CI32.js.map +1 -0
- package/dist/chunk-BBFVG7QU.js +52 -0
- package/dist/chunk-BBFVG7QU.js.map +1 -0
- package/dist/chunk-GR4PMGCS.js +82 -0
- package/dist/chunk-GR4PMGCS.js.map +1 -0
- package/dist/chunk-KL4WRZKZ.js +799 -0
- package/dist/chunk-KL4WRZKZ.js.map +1 -0
- package/dist/chunk-KLKRK6OG.js +107 -0
- package/dist/chunk-KLKRK6OG.js.map +1 -0
- package/dist/chunk-LY2GJWN7.js +708 -0
- package/dist/chunk-LY2GJWN7.js.map +1 -0
- package/dist/chunk-SEDRF62Q.js +39 -0
- package/dist/chunk-SEDRF62Q.js.map +1 -0
- package/dist/chunk-U4Z5INIW.js +1192 -0
- package/dist/chunk-U4Z5INIW.js.map +1 -0
- package/dist/chunk-XOXZZYSW.js +247 -0
- package/dist/chunk-XOXZZYSW.js.map +1 -0
- package/dist/chunk-XQ6FLRX7.js +179 -0
- package/dist/chunk-XQ6FLRX7.js.map +1 -0
- package/dist/chunk-XQFQN73U.js +38 -0
- package/dist/chunk-XQFQN73U.js.map +1 -0
- package/dist/connections-CGEPiZPF.d.ts +128 -0
- package/dist/core.d.ts +26 -0
- package/dist/core.js +89 -0
- package/dist/core.js.map +1 -0
- package/dist/devices-oLIB_M19.d.ts +97 -0
- package/dist/express.d.ts +19 -0
- package/dist/express.js +34 -0
- package/dist/express.js.map +1 -0
- package/dist/fastify.d.ts +22 -0
- package/dist/fastify.js +42 -0
- package/dist/fastify.js.map +1 -0
- package/dist/feature-B2bEXLZG.d.ts +233 -0
- package/dist/feature-DQUbnYE-.d.ts +284 -0
- package/dist/feature-DwKkuOBh.d.ts +195 -0
- package/dist/hub-CPrWmIvB.d.ts +174 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +97 -0
- package/dist/integration.js +151 -0
- package/dist/integration.js.map +1 -0
- package/dist/models-D1lehXIu.d.ts +31 -0
- package/dist/models-DTXEX1Q8.d.ts +34 -0
- package/dist/moderation.d.ts +9 -0
- package/dist/moderation.js +48 -0
- package/dist/moderation.js.map +1 -0
- package/dist/push.d.ts +198 -0
- package/dist/push.js +475 -0
- package/dist/push.js.map +1 -0
- package/dist/redis.d.ts +103 -0
- package/dist/redis.js +296 -0
- package/dist/redis.js.map +1 -0
- package/dist/s3.d.ts +75 -0
- package/dist/s3.js +315 -0
- package/dist/s3.js.map +1 -0
- package/dist/util-B32rIBVA.d.ts +22 -0
- package/package.json +146 -4
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { D as Db, a as Dialect, F as Feature, C as CommsHub } from './hub-CPrWmIvB.js';
|
|
2
|
+
import { R as RateLimit, S as SocketLike } from './connections-CGEPiZPF.js';
|
|
3
|
+
import { L as LRU } from './util-B32rIBVA.js';
|
|
4
|
+
|
|
5
|
+
/** CallConfig: timings (seconds), push channels and TURN settings (port of commskit.call.config). */
|
|
6
|
+
|
|
7
|
+
interface CallConfig {
|
|
8
|
+
ringTimeout: number;
|
|
9
|
+
ringingDisconnectGrace: number;
|
|
10
|
+
answeredDisconnectGrace: number;
|
|
11
|
+
maxCallHours: number;
|
|
12
|
+
ringWindowSlack: number;
|
|
13
|
+
ringtoneChannel: string;
|
|
14
|
+
missedChannel: string;
|
|
15
|
+
/** coturn REST-API credentials; the route answers 503 while turnSecret is unset. */
|
|
16
|
+
turnSecret: string | null;
|
|
17
|
+
turnUris: string[];
|
|
18
|
+
turnTtl: number;
|
|
19
|
+
/** call_initiate per caller (PROTOCOL.md section 9 "Limits"); null disables it. */
|
|
20
|
+
initiateRate: RateLimit | null;
|
|
21
|
+
}
|
|
22
|
+
declare const defaultCallConfig: () => CallConfig;
|
|
23
|
+
|
|
24
|
+
declare const ACTIVE_CALL_STATUSES: readonly ["ringing", "answered"];
|
|
25
|
+
declare class CallInfo {
|
|
26
|
+
callId: string;
|
|
27
|
+
chatId: number | null;
|
|
28
|
+
callerId: string;
|
|
29
|
+
receiverId: string;
|
|
30
|
+
isVideoCall: boolean;
|
|
31
|
+
status: string;
|
|
32
|
+
duration: number;
|
|
33
|
+
endReason: string | null;
|
|
34
|
+
/** Filled by a chat bridge during finalize: the call's chat entry. */
|
|
35
|
+
messageId: number | null;
|
|
36
|
+
logText: string | null;
|
|
37
|
+
constructor(callId: string, chatId: number | null, callerId: string, receiverId: string, isVideoCall: boolean, status: string, duration?: number, endReason?: string | null);
|
|
38
|
+
static fromRow(row: Record<string, any>): CallInfo;
|
|
39
|
+
get callType(): "video" | "audio";
|
|
40
|
+
otherParty(userId: string): string | null;
|
|
41
|
+
clone(patch?: Partial<CallInfo>): CallInfo;
|
|
42
|
+
}
|
|
43
|
+
/** "Voice call · 05:42", "Missed voice call", "Declined voice call" (caller-cancelled is logged as Missed). */
|
|
44
|
+
declare function callLogText(status: string, isVideoCall: boolean, duration: number, _endReason?: string | null): string;
|
|
45
|
+
declare class CallService {
|
|
46
|
+
private db;
|
|
47
|
+
private bridge;
|
|
48
|
+
private dialect;
|
|
49
|
+
config: CallConfig;
|
|
50
|
+
constructor(db: Db, config?: CallConfig, bridge?: CallChatBridge | null, dialect?: Dialect);
|
|
51
|
+
private ts;
|
|
52
|
+
createCall(chatId: number | null, callerId: string, receiverId: string, isVideoCall?: boolean): Promise<CallInfo>;
|
|
53
|
+
getCallById(callId: string): Promise<Record<string, any> | undefined>;
|
|
54
|
+
getCallInfo(callId: string): Promise<CallInfo | null>;
|
|
55
|
+
saveOfferSdp(callId: string, offerSdp: string): Promise<void>;
|
|
56
|
+
getOfferSdp(callId: string): Promise<string | null>;
|
|
57
|
+
private liveCallsQuery;
|
|
58
|
+
getActiveCallForUser(userId: string): Promise<CallInfo | null>;
|
|
59
|
+
getActiveCallsForUsers(userId: string): Promise<CallInfo[]>;
|
|
60
|
+
/** ringing -> answered. Only the receiver can answer, and only once. */
|
|
61
|
+
markAnswered(callId: string, receiverId: string): Promise<CallInfo | null>;
|
|
62
|
+
/** Move a live call to a terminal status. null if another path already did. */
|
|
63
|
+
finalizeCall(callId: string, status: string, endReason: string, onlyFrom?: readonly string[]): Promise<CallInfo | null>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Hook points a chat integration plugs into (`CallFeature.chatBridge`). The call feature never imports chat. */
|
|
67
|
+
|
|
68
|
+
interface CallChatBridge {
|
|
69
|
+
/** chat_id stored on the call (null = no chat). */
|
|
70
|
+
resolveChatId(db: Db, callerId: string, receiverId: string, requestedChatId: unknown): Promise<number | null>;
|
|
71
|
+
/** Call row committed; e.g. insert the "[Call Initiated]" chat entry. */
|
|
72
|
+
onCallCreated(db: Db, info: CallInfo): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Inside the finalize transaction (`trx` is the transaction: never use another connection, never commit).
|
|
75
|
+
* May set `info.messageId` / `info.logText`; they are committed atomically with the terminal status.
|
|
76
|
+
*/
|
|
77
|
+
onCallFinalizing(trx: Db, info: CallInfo): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Call WebSocket handlers (PROTOCOL.md section 4). Port of commskit.call.handlers. */
|
|
81
|
+
|
|
82
|
+
declare class CallHandlers {
|
|
83
|
+
private feature;
|
|
84
|
+
constructor(feature: CallFeature);
|
|
85
|
+
private get hub();
|
|
86
|
+
private get lifecycle();
|
|
87
|
+
register(): void;
|
|
88
|
+
/** Crash -> call-tagged error (label) or logged only (signalling relays). */
|
|
89
|
+
private guard;
|
|
90
|
+
private error;
|
|
91
|
+
callInitiate(userId: string, message: Record<string, any>, db: Db): Promise<void>;
|
|
92
|
+
answerCall(userId: string, message: Record<string, any>, db: Db): Promise<void>;
|
|
93
|
+
endCall(userId: string, message: Record<string, any>, db: Db): Promise<void>;
|
|
94
|
+
private relaySdp;
|
|
95
|
+
private iceCandidate;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Call lifecycle: finishing calls, ring timeout, disconnect grace, re-ring, pushes (port of commskit.call.lifecycle). */
|
|
99
|
+
|
|
100
|
+
/** (terminal status, end_reason) for a hangup/reject by `fromUser`. */
|
|
101
|
+
declare function endStatus(call: CallInfo, fromUser: string): [string, string];
|
|
102
|
+
declare class CallLifecycle {
|
|
103
|
+
readonly feature: CallFeature;
|
|
104
|
+
/** callId -> [callerId, receiverId]: participants never change, so this per-worker cache keeps ICE off the DB. */
|
|
105
|
+
readonly parties: LRU<string, [string, string]>;
|
|
106
|
+
private timers;
|
|
107
|
+
private stopped;
|
|
108
|
+
constructor(feature: CallFeature);
|
|
109
|
+
private get hub();
|
|
110
|
+
private get config();
|
|
111
|
+
service(db: Db): CallService;
|
|
112
|
+
remember(info: CallInfo): void;
|
|
113
|
+
/** Emit `call.finalized` (the chat integration sends `call_log` from it). */
|
|
114
|
+
finalized(info: CallInfo): Promise<void>;
|
|
115
|
+
/** The call, only if `userId` is a participant. */
|
|
116
|
+
loadCallFor(db: Db, callId: string, userId: string): Promise<CallInfo | null>;
|
|
117
|
+
/** Hangup / reject / cancel by `fromUser`; outcome decided from call state. */
|
|
118
|
+
finishCall(db: Db, callIn: CallInfo, fromUser: string): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* End every live call between two users with `endReason` (e.g. "blocked"). Answered calls become `ended`,
|
|
121
|
+
* ringing ones `missed`. Both parties get `call_ended`; `call.finalized` is emitted.
|
|
122
|
+
*/
|
|
123
|
+
endCallsBetween(userA: string, userB: string, endReason: string, endedBy?: string | null): Promise<CallInfo[]>;
|
|
124
|
+
rejectFromNotification(db: Db, callId: string, userId: string): Promise<boolean>;
|
|
125
|
+
private later;
|
|
126
|
+
/** Cancel every pending ring-timeout / disconnect-grace timer (hub shutdown). */
|
|
127
|
+
cancelAll(): void;
|
|
128
|
+
scheduleRingTimeout(callId: string): void;
|
|
129
|
+
/** Ring timeout: ringing -> missed (no_answer). */
|
|
130
|
+
expireUnanswered(callId: string): Promise<void>;
|
|
131
|
+
private endUserCalls;
|
|
132
|
+
/** Offline hook: after a grace period end calls the user is still in (answered ones get longer). */
|
|
133
|
+
endCallsAfterDisconnect(userId: string): void;
|
|
134
|
+
/** Re-ring a call still waiting for this user, on the new socket only, with the stored offer SDP. */
|
|
135
|
+
onConnect(userId: string, ws: SocketLike): Promise<void>;
|
|
136
|
+
incomingPayload(info: CallInfo): Promise<Record<string, any>>;
|
|
137
|
+
pushIncoming(info: CallInfo, callerName: string, callerAvatar: string | null): Promise<void>;
|
|
138
|
+
/** Receiver's app is not in the foreground: leave a missed-call notification (also stops a ringing one). */
|
|
139
|
+
pushMissedCall(info: CallInfo): Promise<void>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** CallPolicy: host gate for starting a call (blocking, credits...). */
|
|
143
|
+
|
|
144
|
+
interface CallDecision {
|
|
145
|
+
allowed: boolean;
|
|
146
|
+
/** On deny, sent as {"type":"error", ...error, "context":"call"}; e.g. {error, status: 402, message, info}. */
|
|
147
|
+
error?: Record<string, unknown> | null;
|
|
148
|
+
}
|
|
149
|
+
declare const allow: () => CallDecision;
|
|
150
|
+
declare const deny: (error: string, fields?: Record<string, unknown>) => CallDecision;
|
|
151
|
+
interface CallPolicy {
|
|
152
|
+
/**
|
|
153
|
+
* Runs after busy detection, right before the call row is created. `message` is the raw `call_initiate`
|
|
154
|
+
* frame (host fields such as `credit_acknowledged` are available). Charging here is safe: busy receivers
|
|
155
|
+
* never reach this point.
|
|
156
|
+
*/
|
|
157
|
+
authorize(db: Db, callerId: string, receiverId: string, message: Record<string, any>): Promise<CallDecision> | CallDecision;
|
|
158
|
+
}
|
|
159
|
+
declare class AllowAllCallPolicy implements CallPolicy {
|
|
160
|
+
authorize(): CallDecision;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** `hub.install(new CallFeature({...}))` (port of commskit.call.feature). */
|
|
164
|
+
|
|
165
|
+
interface CallFeatureOptions {
|
|
166
|
+
policy?: CallPolicy;
|
|
167
|
+
config?: Partial<CallConfig>;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* P2P WebRTC call signalling. Works standalone (chat_id null, no call_log); `linkChatAndCall` (integration)
|
|
171
|
+
* sets `chatBridge`.
|
|
172
|
+
*/
|
|
173
|
+
declare class CallFeature implements Feature {
|
|
174
|
+
readonly name = "call";
|
|
175
|
+
policy: CallPolicy;
|
|
176
|
+
config: CallConfig;
|
|
177
|
+
chatBridge: CallChatBridge | null;
|
|
178
|
+
lifecycle: CallLifecycle;
|
|
179
|
+
handlers: CallHandlers;
|
|
180
|
+
private _hub;
|
|
181
|
+
constructor(opts?: CallFeatureOptions);
|
|
182
|
+
get hub(): CommsHub;
|
|
183
|
+
install(hub: CommsHub): this;
|
|
184
|
+
/** End every live call between two users (e.g. on block). Both get `call_ended`; `call.finalized` is emitted. */
|
|
185
|
+
endCallsBetween(userA: string, userB: string, endReason: string, endedBy?: string | null): Promise<CallInfo[]>;
|
|
186
|
+
/**
|
|
187
|
+
* Account deletion: remove every calls row where the user is caller or receiver. Live calls are not
|
|
188
|
+
* signalled; disconnect the user first (or end their calls) if needed.
|
|
189
|
+
*/
|
|
190
|
+
deleteUserData(userId: string): Promise<{
|
|
191
|
+
calls: number;
|
|
192
|
+
}>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export { ACTIVE_CALL_STATUSES as A, CallInfo as C, AllowAllCallPolicy as a, type CallChatBridge as b, type CallConfig as c, type CallDecision as d, CallFeature as e, type CallFeatureOptions as f, CallHandlers as g, CallLifecycle as h, type CallPolicy as i, CallService as j, allow as k, callLogText as l, defaultCallConfig as m, deny as n, endStatus as o };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { IncomingMessage, Server } from 'node:http';
|
|
2
|
+
import { Duplex } from 'node:stream';
|
|
3
|
+
import { WebSocketServer } from 'ws';
|
|
4
|
+
import { A as AuthAdapter, d as UserDirectory, P as PushSender, e as UserProfile } from './adapters-8ztjEYtz.js';
|
|
5
|
+
import { C as ConnectionManager, L as Limiter, S as SocketLike } from './connections-CGEPiZPF.js';
|
|
6
|
+
import { Kysely } from 'kysely';
|
|
7
|
+
|
|
8
|
+
/** Database helpers shared by all features (Kysely; sqlite for tests/demo, postgres for production). */
|
|
9
|
+
|
|
10
|
+
type Dialect = "sqlite" | "postgres";
|
|
11
|
+
/** The per-frame "session": the hub's Kysely instance (queries autocommit; use `db.transaction()`). */
|
|
12
|
+
type Db = Kysely<any>;
|
|
13
|
+
declare function detectDialect(db: Db): Dialect;
|
|
14
|
+
/** A UTC timestamp value to write: "YYYY-MM-DD HH:MM:SS.ffffff" (sqlite, as SQLAlchemy stores it) or a Date (pg). */
|
|
15
|
+
declare function dbTimestamp(dialect: Dialect, d?: Date): string | Date;
|
|
16
|
+
/** Boolean column value: sqlite stores 0/1, postgres wants real booleans. */
|
|
17
|
+
declare const dbBool: (dialect: Dialect, v: boolean) => boolean | number;
|
|
18
|
+
/** Current time (the app clock; the Python kit reads the database clock). */
|
|
19
|
+
declare const dbNow: () => Date;
|
|
20
|
+
|
|
21
|
+
type Listener = (...args: any[]) => unknown | Promise<unknown>;
|
|
22
|
+
declare class EventBus {
|
|
23
|
+
private listeners;
|
|
24
|
+
on(name: string, listener: Listener): Listener;
|
|
25
|
+
off(name: string, listener: Listener): void;
|
|
26
|
+
/** Run listeners in registration order; a failing listener is logged, not thrown. */
|
|
27
|
+
emit(name: string, ...args: unknown[]): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface Logger {
|
|
31
|
+
info(msg: string, ...args: unknown[]): void;
|
|
32
|
+
warn(msg: string, ...args: unknown[]): void;
|
|
33
|
+
error(msg: string, ...args: unknown[]): void;
|
|
34
|
+
}
|
|
35
|
+
declare const silentLogger: Logger;
|
|
36
|
+
/** Replace the kit-wide logger (default: warnings and errors to the console). */
|
|
37
|
+
declare function setLogger(logger: Logger): void;
|
|
38
|
+
|
|
39
|
+
/** Framework-neutral REST routes (adapters: fastify, express, or the built-in node:http listener). */
|
|
40
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
41
|
+
interface RestContext {
|
|
42
|
+
method: string;
|
|
43
|
+
/** Route pattern with `:param` segments, e.g. `/chats/:chatId/messages`. */
|
|
44
|
+
path: string;
|
|
45
|
+
params: Record<string, string>;
|
|
46
|
+
query: Record<string, string | string[] | undefined>;
|
|
47
|
+
/** Lower-cased header names. */
|
|
48
|
+
headers: Record<string, string | string[] | undefined>;
|
|
49
|
+
/** Parsed JSON body (undefined when there is none). */
|
|
50
|
+
body: unknown;
|
|
51
|
+
/** Set by the hub for routes with `auth !== false` (Bearer token through the AuthAdapter). */
|
|
52
|
+
userId: string | null;
|
|
53
|
+
}
|
|
54
|
+
interface RestResult {
|
|
55
|
+
status: number;
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
body: unknown;
|
|
58
|
+
}
|
|
59
|
+
interface RestRoute {
|
|
60
|
+
method: HttpMethod;
|
|
61
|
+
path: string;
|
|
62
|
+
/** Require `Authorization: Bearer <token>` (default true). */
|
|
63
|
+
auth?: boolean;
|
|
64
|
+
handler(ctx: RestContext): Promise<RestResult> | RestResult;
|
|
65
|
+
}
|
|
66
|
+
/** Thrown by handlers to answer with FastAPI's error shape `{"detail": ...}`. */
|
|
67
|
+
declare class HttpError extends Error {
|
|
68
|
+
status: number;
|
|
69
|
+
detail: unknown;
|
|
70
|
+
headers?: Record<string, string> | undefined;
|
|
71
|
+
constructor(status: number, detail: unknown, headers?: Record<string, string> | undefined);
|
|
72
|
+
}
|
|
73
|
+
/** FastAPI-style 422 body for a bad parameter. */
|
|
74
|
+
declare function validationError(where: "query" | "path" | "body", name: string, type: string, msg: string, input?: unknown): HttpError;
|
|
75
|
+
declare const ok: (body: unknown, status?: number) => RestResult;
|
|
76
|
+
declare function firstQuery(q: RestContext["query"], name: string): string | undefined;
|
|
77
|
+
/** Match a request path against `:param` patterns. Returns the params or null. */
|
|
78
|
+
declare function matchPath(pattern: string, actual: string): Record<string, string> | null;
|
|
79
|
+
|
|
80
|
+
/** CommsHub: WS handler registry, lifecycle hooks, event bus, REST routes (port of commskit.core.hub). */
|
|
81
|
+
|
|
82
|
+
/** Handler(userId, message, db). Errors propagate to the socket loop, which replies `Message processing failed: ...`. */
|
|
83
|
+
type Handler = (userId: string, message: Record<string, any>, db: Db) => Promise<void> | void;
|
|
84
|
+
type ConnectHook = (userId: string, ws: SocketLike) => Promise<void> | void;
|
|
85
|
+
type DisconnectHook = (userId: string) => Promise<void> | void;
|
|
86
|
+
/** roleResolver(userId) -> "user" | any other role (sync or async). Non-user roles get no presence, delivery flush or user hooks. */
|
|
87
|
+
type RoleResolver = (userId: string) => string | Promise<string>;
|
|
88
|
+
/** onHandlerError(userId, frame, err): frame is the parsed message, or the raw text for bad JSON. */
|
|
89
|
+
type HandlerErrorHook = (userId: string, frame: unknown, err: unknown) => Promise<void> | void;
|
|
90
|
+
/** A feature installs itself on a hub: registers handlers, routes, hooks and tables' services. */
|
|
91
|
+
interface Feature {
|
|
92
|
+
readonly name: string;
|
|
93
|
+
install(hub: CommsHub): unknown;
|
|
94
|
+
}
|
|
95
|
+
interface CommsHubOptions {
|
|
96
|
+
auth: AuthAdapter;
|
|
97
|
+
/** Kysely instance (better-sqlite3 for tests/demo, pg for production). Create tables with `ensureSchema`. */
|
|
98
|
+
db: Db;
|
|
99
|
+
/** Override dialect detection. */
|
|
100
|
+
dialect?: Dialect;
|
|
101
|
+
users?: UserDirectory;
|
|
102
|
+
push?: PushSender;
|
|
103
|
+
connections?: ConnectionManager;
|
|
104
|
+
rateLimiter?: Limiter;
|
|
105
|
+
roleResolver?: RoleResolver;
|
|
106
|
+
onHandlerError?: HandlerErrorHook;
|
|
107
|
+
/** Seconds; at most one `user.came_online` per user per cooldown. */
|
|
108
|
+
cameOnlineCooldown?: number;
|
|
109
|
+
logger?: Logger;
|
|
110
|
+
}
|
|
111
|
+
declare class CommsHub {
|
|
112
|
+
readonly auth: AuthAdapter;
|
|
113
|
+
readonly db: Db;
|
|
114
|
+
readonly dialect: Dialect;
|
|
115
|
+
users: UserDirectory;
|
|
116
|
+
pushSender: PushSender;
|
|
117
|
+
connections: ConnectionManager;
|
|
118
|
+
rateLimiter: Limiter;
|
|
119
|
+
roleResolver?: RoleResolver;
|
|
120
|
+
onHandlerError?: HandlerErrorHook;
|
|
121
|
+
readonly events: EventBus;
|
|
122
|
+
readonly features: Record<string, unknown>;
|
|
123
|
+
private handlers;
|
|
124
|
+
private connectHooks;
|
|
125
|
+
private routes;
|
|
126
|
+
private tasks;
|
|
127
|
+
private ws;
|
|
128
|
+
constructor(opts: CommsHubOptions);
|
|
129
|
+
/** Install a feature (`hub.install(new ChatFeature({...}))`). Returns the feature. */
|
|
130
|
+
install<F extends Feature>(feature: F): F;
|
|
131
|
+
registerFeature(name: string, feature: unknown): void;
|
|
132
|
+
/** Register a WS handler for frames of `type`. Registering again replaces the previous handler. */
|
|
133
|
+
on(type: string, handler: Handler): Handler;
|
|
134
|
+
handles(type: string): boolean;
|
|
135
|
+
/** Run the handler for `message.type`. Unknown types are ignored (false). */
|
|
136
|
+
dispatch(userId: string, message: Record<string, any>): Promise<boolean>;
|
|
137
|
+
/** `hook(userId, ws)` after a socket is registered (runs before frames). Users only unless `allRoles`. */
|
|
138
|
+
onConnect(hook: ConnectHook, allRoles?: boolean): ConnectHook;
|
|
139
|
+
/** `hook(userId)` when the user's last socket on this worker closes (users only unless `allRoles`). */
|
|
140
|
+
onDisconnect(hook: DisconnectHook, allRoles?: boolean): DisconnectHook;
|
|
141
|
+
/** `roleResolver(userId)`; "user" without a resolver or when it fails. */
|
|
142
|
+
roleOf(userId: string): Promise<string>;
|
|
143
|
+
/** Call `onHandlerError`; never throws (hook failures are logged). */
|
|
144
|
+
reportHandlerError(userId: string, frame: unknown, err: unknown): Promise<void>;
|
|
145
|
+
joinRoom(userId: string, room: string): boolean;
|
|
146
|
+
leaveRoom(userId: string, room: string): void;
|
|
147
|
+
/** Send `frame` as given to every member (every worker, with a room-capable bridge). */
|
|
148
|
+
broadcastRoom(room: string, frame: unknown): Promise<number>;
|
|
149
|
+
/** Features call this from `install` to contribute routes. */
|
|
150
|
+
includeRoutes(routes: RestRoute[]): void;
|
|
151
|
+
/** `Authorization: Bearer <token>` -> user id (401 otherwise). */
|
|
152
|
+
authenticate(headers: RestContext["headers"]): Promise<string>;
|
|
153
|
+
/** All routes, ready for an adapter: auth is resolved and HttpError / crashes become `{status, body}`. */
|
|
154
|
+
restRoutes(): RestRoute[];
|
|
155
|
+
/** True when `url` is a `/ws/<token>` request. */
|
|
156
|
+
matchesWs(url: string | undefined): boolean;
|
|
157
|
+
/** Handle an HTTP upgrade for `/ws/:token` (accept first, then authenticate: 1008 on a bad token). */
|
|
158
|
+
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
159
|
+
/** Listen for `/ws/:token` upgrades on an http.Server (other upgrade paths are left to other listeners). */
|
|
160
|
+
attach(server: Server): void;
|
|
161
|
+
get wsServer(): WebSocketServer;
|
|
162
|
+
spawn(task: Promise<unknown> | (() => Promise<unknown>)): void;
|
|
163
|
+
/** Close sockets and wait briefly for background work. */
|
|
164
|
+
shutdown(): Promise<void>;
|
|
165
|
+
send(userId: string, payload: unknown): Promise<boolean>;
|
|
166
|
+
runConnectHooks(userId: string, ws: SocketLike, role?: string, cameOnline?: boolean): Promise<void>;
|
|
167
|
+
profiles(userIds: readonly string[]): Promise<Record<string, UserProfile>>;
|
|
168
|
+
profile(userId: string): Promise<UserProfile | undefined>;
|
|
169
|
+
displayName(userId: string, fallback?: string): Promise<string>;
|
|
170
|
+
/** Never throws: push failures are logged. */
|
|
171
|
+
push(userId: string, title: string, body: string, data: Record<string, unknown>, channel?: string, ttl?: number | null): Promise<void>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export { CommsHub as C, type Db as D, EventBus as E, type Feature as F, type Handler as H, type Listener as L, type RestContext as R, type Dialect as a, type CommsHubOptions as b, type ConnectHook as c, type DisconnectHook as d, type HandlerErrorHook as e, HttpError as f, type HttpMethod as g, type Logger as h, type RestResult as i, type RestRoute as j, type RoleResolver as k, dbBool as l, dbNow as m, dbTimestamp as n, detectDialect as o, firstQuery as p, matchPath as q, ok as r, setLogger as s, silentLogger as t, validationError as v };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { A as AuthAdapter, a as AuthError, D as DeviceRegistry, J as JwtAuth, L as LocalDiskStorage, M as MediaInfo, b as MediaStorage, N as NoopPush, c as NullUserDirectory, P as PushSender, S as StaticUserDirectory, U as UploadRejected, d as UserDirectory, e as UserProfile, f as deleteMedia, r as resolveUserId } from './adapters-8ztjEYtz.js';
|
|
2
|
+
export { MEDIA_TYPES, PONG_TYPE, detectMediaType, restListener } from './core.js';
|
|
3
|
+
export { C as ConnectionManager, L as Limiter, P as PresenceBridge, R as RateLimit, a as RateLimiter, S as SocketLike, U as USER_ROLE, b as UserConnection, r as retryAfterSeconds } from './connections-CGEPiZPF.js';
|
|
4
|
+
import { D as Db, a as Dialect } from './hub-CPrWmIvB.js';
|
|
5
|
+
export { C as CommsHub, b as CommsHubOptions, c as ConnectHook, d as DisconnectHook, E as EventBus, F as Feature, H as Handler, e as HandlerErrorHook, f as HttpError, g as HttpMethod, L as Listener, h as Logger, R as RestContext, i as RestResult, j as RestRoute, k as RoleResolver, l as dbBool, m as dbNow, n as dbTimestamp, o as detectDialect, p as firstQuery, q as matchPath, r as ok, s as setLogger, t as silentLogger, v as validationError } from './hub-CPrWmIvB.js';
|
|
6
|
+
import { D as DevicesTable } from './devices-oLIB_M19.js';
|
|
7
|
+
export { d as devicesDdl } from './devices-oLIB_M19.js';
|
|
8
|
+
export { f as formatIso, i as iso, n as nowIso, u as utcnow } from './util-B32rIBVA.js';
|
|
9
|
+
export { A as AllowAllChatPolicy, C as ChatActionError, a as ChatConfig, b as ChatFeature, c as ChatFeatureOptions, d as ChatPolicy, e as ChatRow, f as ChatService, D as DELETED_TYPE, M as MessageDto, g as MessageRow, R as RESERVED_TYPES, h as ReplyPreview, S as SYSTEM_TYPE, i as defaultChatConfig, m as messageDto } from './feature-DQUbnYE-.js';
|
|
10
|
+
import { ChatTables } from './chat.js';
|
|
11
|
+
export { ChatClearedTable, ChatsTable, MessageHiddenTable, MessagesTable, chatDdl } from './chat.js';
|
|
12
|
+
import { C as CallsTable } from './models-DTXEX1Q8.js';
|
|
13
|
+
import { B as BlocksTable, R as ReportsTable } from './models-D1lehXIu.js';
|
|
14
|
+
import 'node:http';
|
|
15
|
+
import 'node:stream';
|
|
16
|
+
import 'ws';
|
|
17
|
+
import 'kysely';
|
|
18
|
+
|
|
19
|
+
/** Kysely table map for every kit table. */
|
|
20
|
+
interface KitDatabase extends ChatTables {
|
|
21
|
+
calls: CallsTable;
|
|
22
|
+
comms_devices: DevicesTable;
|
|
23
|
+
comms_blocks: BlocksTable;
|
|
24
|
+
comms_reports: ReportsTable;
|
|
25
|
+
}
|
|
26
|
+
/** Tables created by `ensureSchema`, in dependency order. */
|
|
27
|
+
declare const KIT_TABLES: readonly ["chats", "messages", "calls", "comms_devices", "comms_blocks", "comms_reports", "comms_message_hidden", "comms_chat_cleared"];
|
|
28
|
+
/** Create every kit table (idempotent; same names/columns/constraints as the Python SQLAlchemy models). */
|
|
29
|
+
declare function ensureSchema(db: Db, opts?: {
|
|
30
|
+
dialect?: Dialect;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
|
|
33
|
+
export { BlocksTable, CallsTable, ChatTables, Db, DevicesTable, Dialect, KIT_TABLES, type KitDatabase, ReportsTable, ensureSchema };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommsHub,
|
|
3
|
+
ConnectionManager,
|
|
4
|
+
EventBus,
|
|
5
|
+
PONG_TYPE,
|
|
6
|
+
USER_ROLE,
|
|
7
|
+
restListener
|
|
8
|
+
} from "./chunk-LY2GJWN7.js";
|
|
9
|
+
import {
|
|
10
|
+
AllowAllChatPolicy,
|
|
11
|
+
ChatActionError,
|
|
12
|
+
ChatFeature,
|
|
13
|
+
ChatService,
|
|
14
|
+
DELETED_TYPE,
|
|
15
|
+
RESERVED_TYPES,
|
|
16
|
+
SYSTEM_TYPE,
|
|
17
|
+
chatDdl,
|
|
18
|
+
defaultChatConfig,
|
|
19
|
+
messageDto
|
|
20
|
+
} from "./chunk-U4Z5INIW.js";
|
|
21
|
+
import {
|
|
22
|
+
AuthError,
|
|
23
|
+
JwtAuth,
|
|
24
|
+
LocalDiskStorage,
|
|
25
|
+
MEDIA_TYPES,
|
|
26
|
+
NoopPush,
|
|
27
|
+
NullUserDirectory,
|
|
28
|
+
StaticUserDirectory,
|
|
29
|
+
UploadRejected,
|
|
30
|
+
deleteMedia,
|
|
31
|
+
detectMediaType,
|
|
32
|
+
resolveUserId
|
|
33
|
+
} from "./chunk-XQ6FLRX7.js";
|
|
34
|
+
import {
|
|
35
|
+
callDdl
|
|
36
|
+
} from "./chunk-XQFQN73U.js";
|
|
37
|
+
import {
|
|
38
|
+
moderationDdl
|
|
39
|
+
} from "./chunk-SEDRF62Q.js";
|
|
40
|
+
import {
|
|
41
|
+
devicesDdl
|
|
42
|
+
} from "./chunk-XOXZZYSW.js";
|
|
43
|
+
import {
|
|
44
|
+
HttpError,
|
|
45
|
+
dbBool,
|
|
46
|
+
dbNow,
|
|
47
|
+
dbTimestamp,
|
|
48
|
+
detectDialect,
|
|
49
|
+
firstQuery,
|
|
50
|
+
matchPath,
|
|
51
|
+
ok,
|
|
52
|
+
validationError
|
|
53
|
+
} from "./chunk-GR4PMGCS.js";
|
|
54
|
+
import {
|
|
55
|
+
RateLimit,
|
|
56
|
+
RateLimiter,
|
|
57
|
+
retryAfterSeconds
|
|
58
|
+
} from "./chunk-BBFVG7QU.js";
|
|
59
|
+
import {
|
|
60
|
+
formatIso,
|
|
61
|
+
iso,
|
|
62
|
+
nowIso,
|
|
63
|
+
utcnow
|
|
64
|
+
} from "./chunk-KLKRK6OG.js";
|
|
65
|
+
import {
|
|
66
|
+
setLogger,
|
|
67
|
+
silentLogger
|
|
68
|
+
} from "./chunk-5BJ2SNM5.js";
|
|
69
|
+
|
|
70
|
+
// src/schema.ts
|
|
71
|
+
import { sql } from "kysely";
|
|
72
|
+
var KIT_TABLES = [
|
|
73
|
+
"chats",
|
|
74
|
+
"messages",
|
|
75
|
+
"calls",
|
|
76
|
+
"comms_devices",
|
|
77
|
+
"comms_blocks",
|
|
78
|
+
"comms_reports",
|
|
79
|
+
"comms_message_hidden",
|
|
80
|
+
"comms_chat_cleared"
|
|
81
|
+
];
|
|
82
|
+
async function ensureSchema(db, opts = {}) {
|
|
83
|
+
const dialect = opts.dialect ?? detectDialect(db);
|
|
84
|
+
for (const stmt of [...chatDdl(dialect), ...callDdl(dialect), ...devicesDdl(dialect), ...moderationDdl(dialect)]) {
|
|
85
|
+
await sql.raw(stmt).execute(db);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
AllowAllChatPolicy,
|
|
90
|
+
AuthError,
|
|
91
|
+
ChatActionError,
|
|
92
|
+
ChatFeature,
|
|
93
|
+
ChatService,
|
|
94
|
+
CommsHub,
|
|
95
|
+
ConnectionManager,
|
|
96
|
+
DELETED_TYPE,
|
|
97
|
+
EventBus,
|
|
98
|
+
HttpError,
|
|
99
|
+
JwtAuth,
|
|
100
|
+
KIT_TABLES,
|
|
101
|
+
LocalDiskStorage,
|
|
102
|
+
MEDIA_TYPES,
|
|
103
|
+
NoopPush,
|
|
104
|
+
NullUserDirectory,
|
|
105
|
+
PONG_TYPE,
|
|
106
|
+
RESERVED_TYPES,
|
|
107
|
+
RateLimit,
|
|
108
|
+
RateLimiter,
|
|
109
|
+
SYSTEM_TYPE,
|
|
110
|
+
StaticUserDirectory,
|
|
111
|
+
USER_ROLE,
|
|
112
|
+
UploadRejected,
|
|
113
|
+
chatDdl,
|
|
114
|
+
dbBool,
|
|
115
|
+
dbNow,
|
|
116
|
+
dbTimestamp,
|
|
117
|
+
defaultChatConfig,
|
|
118
|
+
deleteMedia,
|
|
119
|
+
detectDialect,
|
|
120
|
+
detectMediaType,
|
|
121
|
+
devicesDdl,
|
|
122
|
+
ensureSchema,
|
|
123
|
+
firstQuery,
|
|
124
|
+
formatIso,
|
|
125
|
+
iso,
|
|
126
|
+
matchPath,
|
|
127
|
+
messageDto,
|
|
128
|
+
nowIso,
|
|
129
|
+
ok,
|
|
130
|
+
resolveUserId,
|
|
131
|
+
restListener,
|
|
132
|
+
retryAfterSeconds,
|
|
133
|
+
setLogger,
|
|
134
|
+
silentLogger,
|
|
135
|
+
utcnow,
|
|
136
|
+
validationError
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/** Aggregates every kit table (the only place besides `index.ts` allowed to import several features). */\nimport { sql } from \"kysely\";\nimport { callDdl } from \"./call/models.js\";\nimport type { CallsTable } from \"./call/models.js\";\nimport { chatDdl } from \"./chat/models.js\";\nimport type { ChatTables } from \"./chat/models.js\";\nimport { type Db, type Dialect, detectDialect } from \"./core/db.js\";\nimport { devicesDdl } from \"./core/devices.js\";\nimport type { DevicesTable } from \"./core/devices.js\";\nimport { moderationDdl } from \"./moderation/models.js\";\nimport type { BlocksTable, ReportsTable } from \"./moderation/models.js\";\n\n/** Kysely table map for every kit table. */\nexport interface KitDatabase extends ChatTables {\n calls: CallsTable;\n comms_devices: DevicesTable;\n comms_blocks: BlocksTable;\n comms_reports: ReportsTable;\n}\n\n/** Tables created by `ensureSchema`, in dependency order. */\nexport const KIT_TABLES = [\n \"chats\", \"messages\", \"calls\", \"comms_devices\", \"comms_blocks\", \"comms_reports\",\n \"comms_message_hidden\", \"comms_chat_cleared\",\n] as const;\n\n/** Create every kit table (idempotent; same names/columns/constraints as the Python SQLAlchemy models). */\nexport async function ensureSchema(db: Db, opts: { dialect?: Dialect } = {}): Promise<void> {\n const dialect = opts.dialect ?? detectDialect(db);\n for (const stmt of [...chatDdl(dialect), ...callDdl(dialect), ...devicesDdl(dialect), ...moderationDdl(dialect)]) {\n await sql.raw(stmt).execute(db);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,WAAW;AAoBb,IAAM,aAAa;AAAA,EACxB;AAAA,EAAS;AAAA,EAAY;AAAA,EAAS;AAAA,EAAiB;AAAA,EAAgB;AAAA,EAC/D;AAAA,EAAwB;AAC1B;AAGA,eAAsB,aAAa,IAAQ,OAA8B,CAAC,GAAkB;AAC1F,QAAM,UAAU,KAAK,WAAW,cAAc,EAAE;AAChD,aAAW,QAAQ,CAAC,GAAG,QAAQ,OAAO,GAAG,GAAG,QAAQ,OAAO,GAAG,GAAG,WAAW,OAAO,GAAG,GAAG,cAAc,OAAO,CAAC,GAAG;AAChH,UAAM,IAAI,IAAI,IAAI,EAAE,QAAQ,EAAE;AAAA,EAChC;AACF;","names":[]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { b as CallChatBridge, e as CallFeature, C as CallInfo } from './feature-DwKkuOBh.js';
|
|
2
|
+
import { D as Db, C as CommsHub } from './hub-CPrWmIvB.js';
|
|
3
|
+
import { b as ChatFeature } from './feature-DQUbnYE-.js';
|
|
4
|
+
import { c as CallPolicyLike, d as ChatPolicyLike, j as ModerationFeature } from './feature-B2bEXLZG.js';
|
|
5
|
+
import './connections-CGEPiZPF.js';
|
|
6
|
+
import './util-B32rIBVA.js';
|
|
7
|
+
import 'node:http';
|
|
8
|
+
import 'node:stream';
|
|
9
|
+
import 'ws';
|
|
10
|
+
import './adapters-8ztjEYtz.js';
|
|
11
|
+
import 'kysely';
|
|
12
|
+
|
|
13
|
+
/** Chat + call wiring: calls live in the chat as call-log entries. The only file importing both features. */
|
|
14
|
+
|
|
15
|
+
declare const CALL_PLACEHOLDER = "[Call Initiated]";
|
|
16
|
+
/** Implements `CallChatBridge` on top of the chat tables. */
|
|
17
|
+
declare class ChatCallBridge implements CallChatBridge {
|
|
18
|
+
readonly chat: ChatFeature;
|
|
19
|
+
readonly call: CallFeature;
|
|
20
|
+
constructor(chat: ChatFeature, call: CallFeature);
|
|
21
|
+
resolveChatId(db: Db, callerId: string, receiverId: string, _requestedChatId: unknown): Promise<number | null>;
|
|
22
|
+
onCallCreated(db: Db, info: CallInfo): Promise<void>;
|
|
23
|
+
onCallFinalizing(trx: Db, info: CallInfo): Promise<void>;
|
|
24
|
+
/** `call.finalized` listener: both parties' chat entry now reads e.g. "Voice call · 05:42". */
|
|
25
|
+
sendCallLog(info: CallInfo): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
/** Both features must already be installed on the same hub. */
|
|
28
|
+
declare function linkChatAndCall(chat: ChatFeature, call: CallFeature): ChatCallBridge;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Moderation wiring (port of commskit.integration.moderation): the block list in front of the chat / call policies, and
|
|
32
|
+
* live calls between a pair end when one blocks the other.
|
|
33
|
+
*
|
|
34
|
+
* `src/moderation` never imports chat or call; it emits `moderation.blocked` on the hub's event bus and exposes policy
|
|
35
|
+
* wrappers. This module connects the two sides. It only uses structural types for chat and call (so it compiles without the
|
|
36
|
+
* call module), and loads the chat module lazily, only when a chat feature is linked.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
declare const BLOCKED_END_REASON = "blocked";
|
|
40
|
+
/** The slice of `ChatFeature` the link needs. */
|
|
41
|
+
interface LinkableChat {
|
|
42
|
+
readonly hub: CommsHub;
|
|
43
|
+
policy: ChatPolicyLike;
|
|
44
|
+
}
|
|
45
|
+
/** The slice of `CallFeature` the link needs (`CallFeature.lifecycle.endCallsBetween`). */
|
|
46
|
+
interface CallLifecycleLike {
|
|
47
|
+
endCallsBetween(userA: string, userB: string, reason: string, endedBy?: string | null): Promise<unknown>;
|
|
48
|
+
}
|
|
49
|
+
interface LinkableCall {
|
|
50
|
+
readonly hub: CommsHub;
|
|
51
|
+
policy: CallPolicyLike;
|
|
52
|
+
readonly lifecycle: CallLifecycleLike;
|
|
53
|
+
}
|
|
54
|
+
declare class ModerationLink {
|
|
55
|
+
readonly moderation: ModerationFeature;
|
|
56
|
+
readonly chat: LinkableChat | null;
|
|
57
|
+
readonly call: LinkableCall | null;
|
|
58
|
+
constructor(moderation: ModerationFeature, chat?: LinkableChat | null, call?: LinkableCall | null);
|
|
59
|
+
/** `moderation.blocked` listener. */
|
|
60
|
+
endCallsOnBlock(blockerId: string, blockedId: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* `ModerationFeature.contextProvider`: MessageDtos (as the reporter sees them, newest first) around `messageId` in the
|
|
63
|
+
* pair's chat, or the latest ones. Hides / clears are ignored.
|
|
64
|
+
*/
|
|
65
|
+
reportMessages(reporterId: string | null, reportedId: string | null, messageId: number | null, before: number, after: number): Promise<Array<Record<string, any>>>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Install-time wiring; all given features must be installed on the same hub.
|
|
69
|
+
* - `chat.policy` becomes `moderation.chatPolicy(<previous policy>)`: a blocked pair cannot send_message / media_upload
|
|
70
|
+
* (error) or typing (dropped), and `res_users` / `res_messages.info` carry `is_blocked` / `blocked_by_other`.
|
|
71
|
+
* - `call.policy` becomes `moderation.callPolicy(<previous policy>)`: call_initiate to/from a blocked user is denied before
|
|
72
|
+
* the host policy runs (no credits charged).
|
|
73
|
+
* - on block, live calls between the pair end with `end_reason: "blocked"`.
|
|
74
|
+
* - with chat: `moderation.contextProvider` (unless the host set one) reads the chat tables for the admin
|
|
75
|
+
* `/moderation/admin/reports/:id/context` route.
|
|
76
|
+
* Policies already wrapped (e.g. `new ChatFeature({ policy: moderation.chatPolicy(...) })`) are not wrapped twice.
|
|
77
|
+
*/
|
|
78
|
+
declare function linkModeration(moderation: ModerationFeature, features?: {
|
|
79
|
+
chat?: LinkableChat | null;
|
|
80
|
+
call?: LinkableCall | null;
|
|
81
|
+
}): ModerationLink;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Account deletion across every installed feature (port of commskit.integration.privacy). Works through `hub.features` (any
|
|
85
|
+
* feature with an async `deleteUserData(userId)`), so it imports no feature package.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Call `deleteUserData(userId)` on every installed feature (chat, call, moderation, devices, then any others), then emit
|
|
90
|
+
* `user.deleted(userId)`. Returns `{featureName: result}`. A failing feature is logged and reported as `{error}` (or
|
|
91
|
+
* re-thrown with `raiseErrors`); the others still run.
|
|
92
|
+
*/
|
|
93
|
+
declare function deleteUserEverywhere(hub: CommsHub, userId: string, opts?: {
|
|
94
|
+
raiseErrors?: boolean;
|
|
95
|
+
}): Promise<Record<string, unknown>>;
|
|
96
|
+
|
|
97
|
+
export { BLOCKED_END_REASON, CALL_PLACEHOLDER, type CallLifecycleLike, ChatCallBridge, type LinkableCall, type LinkableChat, ModerationLink, deleteUserEverywhere, linkChatAndCall, linkModeration };
|