@pkcprotocol/pkc-js 0.0.45 → 0.0.47

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 (102) hide show
  1. package/dist/browser/clients/rpc-client/schema.d.ts +2 -2
  2. package/dist/browser/community/schema.d.ts +18 -18
  3. package/dist/browser/generated-version.d.ts +1 -1
  4. package/dist/browser/generated-version.js +1 -1
  5. package/dist/browser/index-with-compile-cache.d.ts +254 -0
  6. package/dist/browser/index-with-compile-cache.js +25 -0
  7. package/dist/browser/index-with-compile-cache.js.map +1 -0
  8. package/dist/browser/pages/schema.d.ts +5 -5
  9. package/dist/browser/pkc/pkc.d.ts +3 -4
  10. package/dist/browser/pkc/pkc.js +16 -19
  11. package/dist/browser/pkc/pkc.js.map +1 -1
  12. package/dist/browser/publications/comment/schema.d.ts +2 -2
  13. package/dist/browser/publications/comment/schema.js +7 -1
  14. package/dist/browser/publications/comment/schema.js.map +1 -1
  15. package/dist/browser/rpc/src/index.js +10 -1
  16. package/dist/browser/rpc/src/index.js.map +1 -1
  17. package/dist/browser/runtime/browser/compile-cache.d.ts +1 -0
  18. package/dist/browser/runtime/browser/compile-cache.js +7 -0
  19. package/dist/browser/runtime/browser/compile-cache.js.map +1 -0
  20. package/dist/browser/runtime/browser/setup-kubo-http-routers.d.ts +1 -0
  21. package/dist/browser/runtime/browser/setup-kubo-http-routers.js +4 -0
  22. package/dist/browser/runtime/browser/setup-kubo-http-routers.js.map +1 -0
  23. package/dist/browser/runtime/node/compile-cache.d.ts +1 -0
  24. package/dist/browser/runtime/node/compile-cache.js +29 -0
  25. package/dist/browser/runtime/node/compile-cache.js.map +1 -0
  26. package/dist/browser/runtime/node/setup-kubo-http-routers.d.ts +2 -0
  27. package/dist/browser/runtime/node/{setup-kubo-address-rewriter-and-http-router.js → setup-kubo-http-routers.js} +10 -79
  28. package/dist/browser/runtime/node/setup-kubo-http-routers.js.map +1 -0
  29. package/dist/bundled/bundle-manifest.json +1288 -0
  30. package/dist/bundled/chunks/helia-for-pkc-Kr0fvym5.js +281 -0
  31. package/dist/bundled/chunks/local-community-1YWTMINk.js +7218 -0
  32. package/dist/bundled/chunks/local-community-DyL61Q84.js +2 -0
  33. package/dist/bundled/chunks/node-7bvDHqBl.js +7567 -0
  34. package/dist/bundled/chunks/rpc-local-community-Xpx65cbz.js +128945 -0
  35. package/dist/bundled/chunks/schema-CijXm_fr.js +34342 -0
  36. package/dist/bundled/chunks/util-RR1J0iZB.js +200 -0
  37. package/dist/bundled/index-with-compile-cache.js +19 -0
  38. package/dist/bundled/index.js +2 -0
  39. package/dist/bundled/rpc/src/index.js +1473 -0
  40. package/dist/node/clients/rpc-client/schema.d.ts +2 -2
  41. package/dist/node/community/schema.d.ts +18 -18
  42. package/dist/node/generated-version.d.ts +1 -1
  43. package/dist/node/generated-version.js +1 -1
  44. package/dist/node/index-with-compile-cache.d.ts +254 -0
  45. package/dist/node/index-with-compile-cache.js +25 -0
  46. package/dist/node/index-with-compile-cache.js.map +1 -0
  47. package/dist/node/pages/schema.d.ts +5 -5
  48. package/dist/node/pkc/pkc.d.ts +3 -4
  49. package/dist/node/pkc/pkc.js +16 -19
  50. package/dist/node/pkc/pkc.js.map +1 -1
  51. package/dist/node/publications/comment/schema.d.ts +2 -2
  52. package/dist/node/publications/comment/schema.js +7 -1
  53. package/dist/node/publications/comment/schema.js.map +1 -1
  54. package/dist/node/rpc/src/index.js +10 -1
  55. package/dist/node/rpc/src/index.js.map +1 -1
  56. package/dist/node/runtime/browser/compile-cache.d.ts +1 -0
  57. package/dist/node/runtime/browser/compile-cache.js +7 -0
  58. package/dist/node/runtime/browser/compile-cache.js.map +1 -0
  59. package/dist/node/runtime/browser/setup-kubo-http-routers.d.ts +1 -0
  60. package/dist/node/runtime/browser/setup-kubo-http-routers.js +4 -0
  61. package/dist/node/runtime/browser/setup-kubo-http-routers.js.map +1 -0
  62. package/dist/node/runtime/node/compile-cache.d.ts +1 -0
  63. package/dist/node/runtime/node/compile-cache.js +29 -0
  64. package/dist/node/runtime/node/compile-cache.js.map +1 -0
  65. package/dist/node/runtime/node/setup-kubo-http-routers.d.ts +2 -0
  66. package/dist/node/runtime/node/{setup-kubo-address-rewriter-and-http-router.js → setup-kubo-http-routers.js} +10 -79
  67. package/dist/node/runtime/node/setup-kubo-http-routers.js.map +1 -0
  68. package/package.json +29 -10
  69. package/dist/browser/decorator-util.d.ts +0 -1
  70. package/dist/browser/decorator-util.js +0 -35
  71. package/dist/browser/decorator-util.js.map +0 -1
  72. package/dist/browser/rpc/src/lib/pkc-js/pkc-js-mock.js +0 -472
  73. package/dist/browser/rpc/src/lib/pkc-js/pkc-js-mock.js.map +0 -1
  74. package/dist/browser/runtime/browser/setup-kubo-address-rewriter-and-http-router.d.ts +0 -1
  75. package/dist/browser/runtime/browser/setup-kubo-address-rewriter-and-http-router.js +0 -4
  76. package/dist/browser/runtime/browser/setup-kubo-address-rewriter-and-http-router.js.map +0 -1
  77. package/dist/browser/runtime/node/address-rewriter-db.d.ts +0 -31
  78. package/dist/browser/runtime/node/address-rewriter-db.js +0 -156
  79. package/dist/browser/runtime/node/address-rewriter-db.js.map +0 -1
  80. package/dist/browser/runtime/node/addresses-rewriter-proxy-server.d.ts +0 -45
  81. package/dist/browser/runtime/node/addresses-rewriter-proxy-server.js +0 -493
  82. package/dist/browser/runtime/node/addresses-rewriter-proxy-server.js.map +0 -1
  83. package/dist/browser/runtime/node/setup-kubo-address-rewriter-and-http-router.d.ts +0 -4
  84. package/dist/browser/runtime/node/setup-kubo-address-rewriter-and-http-router.js.map +0 -1
  85. package/dist/node/decorator-util.d.ts +0 -1
  86. package/dist/node/decorator-util.js +0 -35
  87. package/dist/node/decorator-util.js.map +0 -1
  88. package/dist/node/rpc/src/lib/pkc-js/pkc-js-mock.d.ts +0 -1
  89. package/dist/node/rpc/src/lib/pkc-js/pkc-js-mock.js +0 -472
  90. package/dist/node/rpc/src/lib/pkc-js/pkc-js-mock.js.map +0 -1
  91. package/dist/node/runtime/browser/setup-kubo-address-rewriter-and-http-router.d.ts +0 -1
  92. package/dist/node/runtime/browser/setup-kubo-address-rewriter-and-http-router.js +0 -4
  93. package/dist/node/runtime/browser/setup-kubo-address-rewriter-and-http-router.js.map +0 -1
  94. package/dist/node/runtime/node/address-rewriter-db.d.ts +0 -31
  95. package/dist/node/runtime/node/address-rewriter-db.js +0 -156
  96. package/dist/node/runtime/node/address-rewriter-db.js.map +0 -1
  97. package/dist/node/runtime/node/addresses-rewriter-proxy-server.d.ts +0 -45
  98. package/dist/node/runtime/node/addresses-rewriter-proxy-server.js +0 -493
  99. package/dist/node/runtime/node/addresses-rewriter-proxy-server.js.map +0 -1
  100. package/dist/node/runtime/node/setup-kubo-address-rewriter-and-http-router.d.ts +0 -4
  101. package/dist/node/runtime/node/setup-kubo-address-rewriter-and-http-router.js.map +0 -1
  102. /package/dist/{browser/rpc/src/lib/pkc-js/pkc-js-mock.d.ts → bundled/challenges.js} +0 -0
@@ -0,0 +1,1473 @@
1
+ import { Er as __toESM, Jn as require_commonjs, L as hideClassPrivateProps, ir as PKCError, or as logger_default, ot as replaceXWithY, yt as findStartedCommunity } from "../../chunks/schema-CijXm_fr.js";
2
+ import { _ as parseRpcUnsubscribeParam, c as parseRpcCancelExportParam, d as parseRpcCommunityIdentifierParam, f as parseRpcCommunityPageParam, g as parseRpcPublishChallengeAnswersParam, h as parseRpcExportCommunityParam, l as parseRpcCidParam, m as parseRpcExportCommunityModLogsParam, p as parseRpcEditCommunityParam, s as parseRpcAuthorNameParam, t as PKC, u as parseRpcCommentRepliesPageParam } from "../../chunks/node-7bvDHqBl.js";
3
+ import { Cn as parseVoteChallengeRequestToEncryptSchemaWithPKCErrorIfItFails, G as buildPageRuntimeFields, Jt as parseCommentChallengeRequestToEncryptSchemaWithPKCErrorIfItFails, K as buildPagesRuntimeFields, Qt as parseCommentModerationChallengeRequestToEncryptSchemaWithPKCErrorIfItFails, Sn as parseSetNewSettingsPKCWsServerSchemaWithPKCErrorIfItFails, Un as stringify, Yt as parseCommentEditChallengeRequestToEncryptSchemaWithPKCErrorIfItFails, dn as parseCreatePKCWsServerOptionsSchemaWithPKCErrorIfItFails, hn as parseDecryptedChallengeAnswerWithPKCErrorIfItFails, nn as parseCommunityEditChallengeRequestToEncryptSchemaWithPKCErrorIfItFails, rn as parseCommunityEditOptionsSchemaWithPKCErrorIfItFails, rt as require_lib, tt as pLimit, un as parseCreateNewLocalCommunityUserOptionsSchemaWithPKCErrorIfItFails } from "../../chunks/rpc-local-community-Xpx65cbz.js";
4
+ import { t as LocalCommunity } from "../../chunks/local-community-1YWTMINk.js";
5
+ import { fileURLToPath } from "node:url";
6
+ import path from "path";
7
+ import assert from "assert";
8
+ import { createReadStream, existsSync, mkdirSync, promises, statSync } from "fs";
9
+ import http from "http";
10
+ import { randomInt } from "crypto";
11
+ import { toString } from "uint8arrays/to-string";
12
+ import Database from "better-sqlite3";
13
+ import { Server } from "rpc-websockets";
14
+ //#region dist/node/rpc/src/lib/pkc-js/index.js
15
+ const log$1 = logger_default("pkc-react-hooks:pkc-js");
16
+ const PKCJs = { PKC };
17
+ /**
18
+ * replace PKCJs with a different implementation, for
19
+ * example to mock it during unit tests, to add mock content
20
+ * for developing the front-end or to add a PKCJs with
21
+ * desktop privileges in the Electron build.
22
+ */
23
+ function setPKCJs(_PKC) {
24
+ assert(typeof _PKC === "function", `setPKCJs invalid PKC argument '${_PKC}' not a function`);
25
+ if (_PKC.challenges === void 0) _PKC.challenges = PKC.challenges;
26
+ PKCJs.PKC = _PKC;
27
+ log$1("setPKCJs", _PKC?.constructor?.name);
28
+ }
29
+ //#endregion
30
+ //#region dist/node/rpc/src/utils.js
31
+ const maxRandomInt = 0xffffffffffff;
32
+ const generateSubscriptionId = () => randomInt(1, maxRandomInt);
33
+ function _encodeChallengeRequestId(id) {
34
+ return toString(id, "base58btc");
35
+ }
36
+ function _encodeEncrypted(encrypted) {
37
+ return {
38
+ tag: toString(encrypted.tag, "base64"),
39
+ iv: toString(encrypted.iv, "base64"),
40
+ ciphertext: toString(encrypted.ciphertext, "base64"),
41
+ type: encrypted.type
42
+ };
43
+ }
44
+ function _encodeSignature(signature) {
45
+ return {
46
+ ...signature,
47
+ publicKey: toString(signature.publicKey, "base64"),
48
+ signature: toString(signature.signature, "base64")
49
+ };
50
+ }
51
+ function encodeChallengeRequest(msg) {
52
+ return {
53
+ ...msg,
54
+ challengeRequestId: _encodeChallengeRequestId(msg.challengeRequestId),
55
+ encrypted: _encodeEncrypted(msg.encrypted),
56
+ signature: _encodeSignature(msg.signature)
57
+ };
58
+ }
59
+ function encodeChallengeMessage(msg) {
60
+ return {
61
+ ...msg,
62
+ challengeRequestId: _encodeChallengeRequestId(msg.challengeRequestId),
63
+ encrypted: _encodeEncrypted(msg.encrypted),
64
+ signature: _encodeSignature(msg.signature)
65
+ };
66
+ }
67
+ function encodeChallengeAnswerMessage(msg) {
68
+ return {
69
+ ...msg,
70
+ challengeRequestId: _encodeChallengeRequestId(msg.challengeRequestId),
71
+ encrypted: _encodeEncrypted(msg.encrypted),
72
+ signature: _encodeSignature(msg.signature)
73
+ };
74
+ }
75
+ function encodeChallengeVerificationMessage(msg) {
76
+ const encrypted = msg.encrypted ? _encodeEncrypted(msg.encrypted) : void 0;
77
+ return {
78
+ ...msg,
79
+ challengeRequestId: _encodeChallengeRequestId(msg.challengeRequestId),
80
+ encrypted,
81
+ signature: _encodeSignature(msg.signature)
82
+ };
83
+ }
84
+ //#endregion
85
+ //#region dist/node/rpc/src/json-rpc-util.js
86
+ var import_commonjs = /* @__PURE__ */ __toESM(require_commonjs(), 1);
87
+ var import_lib = require_lib();
88
+ function sanitizeRpcNotificationResult(event, result) {
89
+ if (event !== "error" || !result || typeof result !== "object") return result;
90
+ const sanitizedResult = { ...result };
91
+ if ("stack" in sanitizedResult) delete sanitizedResult.stack;
92
+ if ("details" in sanitizedResult && sanitizedResult.details && typeof sanitizedResult.details === "object") {
93
+ const details = { ...sanitizedResult.details };
94
+ const nestedError = details.error;
95
+ if (nestedError && typeof nestedError === "object") {
96
+ details.error = { ...nestedError };
97
+ if ("stack" in details.error) delete details.error.stack;
98
+ }
99
+ sanitizedResult.details = details;
100
+ }
101
+ return sanitizedResult;
102
+ }
103
+ //#endregion
104
+ //#region dist/node/rpc/src/index.js
105
+ const log = logger_default("pkc-js-rpc:pkc-ws-server");
106
+ var PKCWsServer = class extends import_lib.TypedEmitter {
107
+ constructor({ port, server, pkc, authKey, startStartedCommunitiesOnStartup, autoStartConcurrency, allowPrivateKeyExport, exportFileMaxAgeMs }) {
108
+ super();
109
+ this.connections = {};
110
+ this.subscriptionCleanups = {};
111
+ this.publishing = {};
112
+ this._setSettingsQueue = Promise.resolve();
113
+ this._trackedCommunityListeners = /* @__PURE__ */ new WeakMap();
114
+ this._getIpFromConnectionRequest = (req) => req.socket.remoteAddress;
115
+ this._onSettingsChange = {};
116
+ this._startedCommunities = {};
117
+ this._autoStartOnBoot = false;
118
+ this._exportCommunityInstances = /* @__PURE__ */ new Map();
119
+ this._ownsHttpServer = false;
120
+ const log = logger_default("pkc-js:PKCWsServer");
121
+ this.authKey = authKey;
122
+ this._autoStartOnBoot = startStartedCommunitiesOnStartup ?? true;
123
+ this._autoStartConcurrency = Math.max(1, autoStartConcurrency ?? 5);
124
+ this._allowPrivateKeyExport = allowPrivateKeyExport ?? true;
125
+ this._exportFileMaxAgeMs = exportFileMaxAgeMs ?? 1440 * 60 * 1e3;
126
+ this._initPKC(pkc);
127
+ if (server) this._httpServer = server;
128
+ else {
129
+ this._httpServer = http.createServer();
130
+ this._ownsHttpServer = true;
131
+ if (typeof port === "number") this._httpServer.listen(port);
132
+ }
133
+ this._httpServer.on("request", (req, res) => {
134
+ this._handleExportsHttpRequest(req, res).catch((e) => {
135
+ log.error("Unhandled error in /exports HTTP handler", e);
136
+ if (!res.headersSent) {
137
+ res.statusCode = 500;
138
+ res.end();
139
+ }
140
+ });
141
+ });
142
+ this.rpcWebsockets = new Server({
143
+ server: this._httpServer,
144
+ verifyClient: ({ req }, callback) => {
145
+ const requestOriginatorIp = this._getIpFromConnectionRequest(req);
146
+ log.trace("Received new connection request from", requestOriginatorIp, "with url", req.url);
147
+ const xForwardedFor = Boolean(req.rawHeaders.find((item, i) => item.toLowerCase() === "x-forwarded-for" && i % 2 === 0));
148
+ const isLocalhost = req.socket.localAddress && req.socket.localAddress === requestOriginatorIp && !xForwardedFor;
149
+ const hasAuth = this.authKey && `/${this.authKey}` === req.url;
150
+ if (!isLocalhost && !hasAuth) {
151
+ log(`Rejecting RPC connection request from`, requestOriginatorIp, `rejected because there is no auth key, url:`, req.url);
152
+ callback(false, 403, "You need to set the auth key to connect remotely");
153
+ } else callback(true);
154
+ }
155
+ });
156
+ this.ws = this.rpcWebsockets.wss;
157
+ this.rpcWebsockets.on("error", (error) => {
158
+ log.error("RPC server", "Received an error on rpc-websockets", error);
159
+ this._emitError(error);
160
+ });
161
+ this.ws.on("connection", (ws) => {
162
+ this.connections[ws._id] = ws;
163
+ this.subscriptionCleanups[ws._id] = {};
164
+ this._onSettingsChange[ws._id] = {};
165
+ log("Established connection with new RPC client", ws._id);
166
+ });
167
+ this.rpcWebsockets.on("disconnection", async (ws) => {
168
+ log("RPC client disconnected", ws._id, "number of rpc clients connected", this.rpcWebsockets.wss.clients.size);
169
+ const subscriptionCleanups = this.subscriptionCleanups[ws._id];
170
+ if (!subscriptionCleanups) {
171
+ delete this.subscriptionCleanups[ws._id];
172
+ delete this.connections[ws._id];
173
+ delete this._onSettingsChange[ws._id];
174
+ log("Disconnected from RPC client (no subscriptions to clean)", ws._id);
175
+ return;
176
+ }
177
+ for (const subscriptionId in subscriptionCleanups) {
178
+ await subscriptionCleanups[subscriptionId]();
179
+ delete subscriptionCleanups[subscriptionId];
180
+ }
181
+ delete this.subscriptionCleanups[ws._id];
182
+ delete this.connections[ws._id];
183
+ delete this._onSettingsChange[ws._id];
184
+ log("Disconnected from RPC client", ws._id);
185
+ });
186
+ this.rpcWebsocketsRegister("getComment", this.getComment.bind(this));
187
+ this.rpcWebsocketsRegister("getCommunityPage", this.getCommunityPage.bind(this));
188
+ this.rpcWebsocketsRegister("getCommentPage", this.getCommentPage.bind(this));
189
+ this.rpcWebsocketsRegister("createCommunity", this.createCommunity.bind(this));
190
+ this.rpcWebsocketsRegister("startCommunity", this.startCommunity.bind(this));
191
+ this.rpcWebsocketsRegister("stopCommunity", this.stopCommunity.bind(this));
192
+ this.rpcWebsocketsRegister("editCommunity", this.editCommunity.bind(this));
193
+ this.rpcWebsocketsRegister("deleteCommunity", this.deleteCommunity.bind(this));
194
+ this.rpcWebsocketsRegister("communitiesSubscribe", this.communitiesSubscribe.bind(this));
195
+ this.rpcWebsocketsRegister("settingsSubscribe", this.settingsSubscribe.bind(this));
196
+ this.rpcWebsocketsRegister("fetchCid", this.fetchCid.bind(this));
197
+ this.rpcWebsocketsRegister("resolveAuthorName", this.resolveAuthorName.bind(this));
198
+ this.rpcWebsocketsRegister("setSettings", this.setSettings.bind(this));
199
+ this.rpcWebsocketsRegister("commentUpdateSubscribe", this.commentUpdateSubscribe.bind(this));
200
+ this.rpcWebsocketsRegister("communityUpdateSubscribe", this.communityUpdateSubscribe.bind(this));
201
+ this.rpcWebsocketsRegister("publishComment", this.publishComment.bind(this));
202
+ this.rpcWebsocketsRegister("publishCommunityEdit", this.publishCommunityEdit.bind(this));
203
+ this.rpcWebsocketsRegister("publishVote", this.publishVote.bind(this));
204
+ this.rpcWebsocketsRegister("publishCommentEdit", this.publishCommentEdit.bind(this));
205
+ this.rpcWebsocketsRegister("publishCommentModeration", this.publishCommentModeration.bind(this));
206
+ this.rpcWebsocketsRegister("publishChallengeAnswers", this.publishChallengeAnswers.bind(this));
207
+ this.rpcWebsocketsRegister("unsubscribe", this.unsubscribe.bind(this));
208
+ this.rpcWebsocketsRegister("exportCommunity", this.exportCommunity.bind(this));
209
+ this.rpcWebsocketsRegister("exportsSubscribe", this.exportsSubscribe.bind(this));
210
+ this.rpcWebsocketsRegister("cancelExport", this.cancelExport.bind(this));
211
+ this.rpcWebsocketsRegister("exportCommunityModLogs", this.exportCommunityModLogs.bind(this));
212
+ hideClassPrivateProps(this);
213
+ }
214
+ async getStartedCommunity(address) {
215
+ if (!(address in this._startedCommunities)) throw Error("Can't call getStartedCommunity when the community hasn't been started");
216
+ while (this._startedCommunities[address] === "pending") await new Promise((r) => setTimeout(r, 20));
217
+ return this._startedCommunities[address];
218
+ }
219
+ _findCommunityAddress(identifier) {
220
+ const { name, publicKey } = identifier;
221
+ if (!name && !publicKey) throw new Error("At least one of name or publicKey must be provided");
222
+ if (name && name in this._startedCommunities) return name;
223
+ if (name && this.pkc.communities.includes(name)) return name;
224
+ if (publicKey && publicKey in this._startedCommunities) return publicKey;
225
+ if (publicKey && this.pkc.communities.includes(publicKey)) return publicKey;
226
+ }
227
+ _emitError(error) {
228
+ if (this.listeners("error").length === 0) log.error("Unhandled error. This may crash your process, you need to listen for error event on PKCRpcWsServer", error);
229
+ this.emit("error", error);
230
+ }
231
+ _getRpcStateDb() {
232
+ if (this._rpcStateDb) return this._rpcStateDb;
233
+ const dataPath = this.pkc.dataPath;
234
+ if (!dataPath) return void 0;
235
+ const rpcServerDir = path.join(dataPath, "rpc-server");
236
+ mkdirSync(rpcServerDir, { recursive: true });
237
+ const dbPath = path.join(rpcServerDir, "rpc-state.db");
238
+ this._rpcStateDb = new Database(dbPath);
239
+ this._rpcStateDb.pragma("journal_mode = WAL");
240
+ try {
241
+ this._rpcStateDb.exec("ALTER TABLE community_states RENAME TO community_states");
242
+ } catch (_) {}
243
+ this._rpcStateDb.exec(`
244
+ CREATE TABLE IF NOT EXISTS community_states (
245
+ address TEXT PRIMARY KEY,
246
+ wasStarted INTEGER NOT NULL DEFAULT 0,
247
+ wasExplicitlyStopped INTEGER NOT NULL DEFAULT 0
248
+ )
249
+ `);
250
+ return this._rpcStateDb;
251
+ }
252
+ _updateCommunityState(address, update) {
253
+ const db = this._getRpcStateDb();
254
+ if (!db) return;
255
+ db.prepare("INSERT OR IGNORE INTO community_states (address) VALUES (?)").run(address);
256
+ if (update.wasStarted !== void 0) db.prepare("UPDATE community_states SET wasStarted = ? WHERE address = ?").run(update.wasStarted ? 1 : 0, address);
257
+ if (update.wasExplicitlyStopped !== void 0) db.prepare("UPDATE community_states SET wasExplicitlyStopped = ? WHERE address = ?").run(update.wasExplicitlyStopped ? 1 : 0, address);
258
+ }
259
+ _removeCommunityState(address) {
260
+ const db = this._getRpcStateDb();
261
+ if (!db) return;
262
+ db.prepare("DELETE FROM community_states WHERE address = ?").run(address);
263
+ }
264
+ async _autoStartPreviousCommunities() {
265
+ if (!this._autoStartOnBoot) return;
266
+ const autoStartLog = logger_default("pkc-js-rpc:pkc-ws-server:auto-start");
267
+ autoStartLog("Checking for previously started communities to auto-start");
268
+ const db = this._getRpcStateDb();
269
+ if (!db) return;
270
+ const rows = db.prepare("SELECT address FROM community_states WHERE wasStarted = 1 AND wasExplicitlyStopped = 0").all();
271
+ const localCommunities = (await this._getPKCInstance()).communities;
272
+ const communitiesToStart = [];
273
+ for (const row of rows) {
274
+ if (!localCommunities.includes(row.address)) {
275
+ autoStartLog(`Skipping auto-start for ${row.address} - community no longer exists`);
276
+ this._removeCommunityState(row.address);
277
+ continue;
278
+ }
279
+ if (row.address in this._startedCommunities) {
280
+ autoStartLog(`Skipping auto-start for ${row.address} - already started`);
281
+ continue;
282
+ }
283
+ communitiesToStart.push(row.address);
284
+ }
285
+ if (communitiesToStart.length === 0) {
286
+ autoStartLog("No communities to auto-start");
287
+ return;
288
+ }
289
+ autoStartLog(`Auto-starting ${communitiesToStart.length} communities with concurrency limit of ${this._autoStartConcurrency}`);
290
+ const limit = pLimit(this._autoStartConcurrency);
291
+ await Promise.allSettled(communitiesToStart.map((address) => limit(async () => {
292
+ autoStartLog(`Auto-starting community: ${address}`);
293
+ try {
294
+ await this._internalStartCommunity(address);
295
+ autoStartLog(`Successfully auto-started: ${address}`);
296
+ } catch (e) {
297
+ autoStartLog.error(`Failed to auto-start community ${address}`, e);
298
+ this._emitError(e instanceof Error ? e : /* @__PURE__ */ new Error(`Failed to auto-start ${address}: ${String(e)}`));
299
+ }
300
+ })));
301
+ }
302
+ async _internalStartCommunity(address) {
303
+ const pkc = await this._getPKCInstance();
304
+ this._startedCommunities[address] = "pending";
305
+ try {
306
+ const community = await pkc.createCommunity({ address });
307
+ community.started = true;
308
+ await community.start();
309
+ this._startedCommunities[address] = community;
310
+ this._updateCommunityState(address, {
311
+ wasStarted: true,
312
+ wasExplicitlyStopped: false
313
+ });
314
+ return community;
315
+ } catch (e) {
316
+ delete this._startedCommunities[address];
317
+ throw e;
318
+ }
319
+ }
320
+ rpcWebsocketsRegister(method, callback) {
321
+ const callbackWithErrorHandled = async (params, connectionId) => {
322
+ try {
323
+ return await callback(params, connectionId);
324
+ } catch (e) {
325
+ const typedError = e;
326
+ log.error(`${callback.name} error`, {
327
+ params,
328
+ error: typedError
329
+ });
330
+ if (typedError instanceof PKCError) {
331
+ const seen = /* @__PURE__ */ new WeakSet();
332
+ const errorJson = JSON.parse(JSON.stringify(typedError, (_key, value) => {
333
+ if (typeof value === "object" && value !== null) {
334
+ if (seen.has(value)) return void 0;
335
+ seen.add(value);
336
+ }
337
+ return value;
338
+ }));
339
+ delete errorJson["stack"];
340
+ throw errorJson;
341
+ } else {
342
+ const errorJson = {
343
+ message: typedError.message,
344
+ name: typedError.name
345
+ };
346
+ if ("details" in typedError) errorJson.details = typedError.details;
347
+ if ("code" in typedError) errorJson.code = typedError.code;
348
+ throw errorJson;
349
+ }
350
+ }
351
+ };
352
+ this.rpcWebsockets.register(method, callbackWithErrorHandled);
353
+ if (this.authKey) this.rpcWebsockets.register(method, callbackWithErrorHandled, `/${this.authKey}`);
354
+ }
355
+ jsonRpcSendNotification({ method, result, subscription, event, connectionId }) {
356
+ const message = {
357
+ jsonrpc: "2.0",
358
+ method,
359
+ params: {
360
+ result: sanitizeRpcNotificationResult(event, result),
361
+ subscription,
362
+ event
363
+ }
364
+ };
365
+ this.connections[connectionId]?.send?.(JSON.stringify(message));
366
+ }
367
+ _registerPublishing(subscriptionId, publication, pkc, connectionId) {
368
+ this.publishing[subscriptionId] = {
369
+ publication,
370
+ pkc,
371
+ connectionId
372
+ };
373
+ }
374
+ _clearPublishing(subscriptionId) {
375
+ const record = this.publishing[subscriptionId];
376
+ if (record?.timeout) clearTimeout(record.timeout);
377
+ delete this.publishing[subscriptionId];
378
+ }
379
+ async _forceCleanupPublication(subscriptionId, reason) {
380
+ const record = this.publishing[subscriptionId];
381
+ if (!record) return;
382
+ const cleanup = await this.subscriptionCleanups?.[record.connectionId]?.[subscriptionId];
383
+ log(`Force-cleaning publication ${subscriptionId} after ${reason}`);
384
+ if (cleanup) {
385
+ await cleanup();
386
+ if (this.subscriptionCleanups?.[record.connectionId]) delete this.subscriptionCleanups[record.connectionId][subscriptionId];
387
+ }
388
+ this._clearPublishing(subscriptionId);
389
+ await this._retirePKCIfNeeded(record.pkc);
390
+ }
391
+ async _retirePKCIfNeeded(pkc) {
392
+ if (Object.values(this.publishing).filter(({ pkc: p }) => p === pkc).length === 0 && !pkc.destroyed) {
393
+ await pkc.destroy().catch((error) => log.error("Failed destroying old pkc immediately after setSettings", { error }));
394
+ return;
395
+ }
396
+ }
397
+ async _getPKCInstance() {
398
+ await this._setSettingsQueue;
399
+ return this.pkc;
400
+ }
401
+ async getComment(params) {
402
+ const getCommentArgs = parseRpcCidParam(params[0]);
403
+ return (await (await this._getPKCInstance()).getComment(getCommentArgs)).raw.comment;
404
+ }
405
+ async getCommunityPage(params) {
406
+ const { cid: pageCid, communityPublicKey, communityName, type, pageMaxSize } = parseRpcCommunityPageParam(params[0]);
407
+ if (!communityPublicKey && !communityName) throw Error("At least one of communityPublicKey or communityName must be provided");
408
+ const communityAddress = communityName || communityPublicKey;
409
+ const pkc = await this._getPKCInstance();
410
+ const community = communityAddress in this._startedCommunities ? await this.getStartedCommunity(communityAddress) : await pkc.createCommunity({
411
+ name: communityName,
412
+ publicKey: communityPublicKey
413
+ });
414
+ const { page } = type === "posts" ? await community.posts._fetchAndVerifyPage({
415
+ pageCid,
416
+ pageMaxSize
417
+ }) : await community.modQueue._fetchAndVerifyPage({
418
+ pageCid,
419
+ pageMaxSize
420
+ });
421
+ return {
422
+ page,
423
+ runtimeFields: buildPageRuntimeFields(page, pkc._memCaches.nameResolvedCache)
424
+ };
425
+ }
426
+ async getCommentPage(params) {
427
+ const { cid: pageCid, commentCid, communityPublicKey, communityName, pageMaxSize } = parseRpcCommentRepliesPageParam(params[0]);
428
+ const pkc = await this._getPKCInstance();
429
+ const { page } = await (await pkc.createComment({
430
+ cid: commentCid,
431
+ communityPublicKey,
432
+ communityName
433
+ })).replies._fetchAndVerifyPage({
434
+ pageCid,
435
+ pageMaxSize
436
+ });
437
+ return {
438
+ page,
439
+ runtimeFields: buildPageRuntimeFields(page, pkc._memCaches.nameResolvedCache)
440
+ };
441
+ }
442
+ async createCommunity(params) {
443
+ const createCommunityOptions = parseCreateNewLocalCommunityUserOptionsSchemaWithPKCErrorIfItFails(params[0]);
444
+ const community = await (await this._getPKCInstance()).createCommunity(createCommunityOptions);
445
+ if (!(community instanceof LocalCommunity)) throw Error("Failed to create a local community. This is a critical error");
446
+ return community.toJSONInternalRpcBeforeFirstUpdate();
447
+ }
448
+ _trackCommunityListener(community, event, listener) {
449
+ let listenersByEvent = this._trackedCommunityListeners.get(community);
450
+ if (!listenersByEvent) {
451
+ listenersByEvent = /* @__PURE__ */ new Map();
452
+ this._trackedCommunityListeners.set(community, listenersByEvent);
453
+ }
454
+ let listeners = listenersByEvent.get(event);
455
+ if (!listeners) {
456
+ listeners = /* @__PURE__ */ new Set();
457
+ listenersByEvent.set(event, listeners);
458
+ }
459
+ listeners.add(listener);
460
+ }
461
+ _untrackCommunityListener(community, event, listener) {
462
+ const listenersByEvent = this._trackedCommunityListeners.get(community);
463
+ if (!listenersByEvent) return;
464
+ const listeners = listenersByEvent.get(event);
465
+ if (!listeners) return;
466
+ listeners.delete(listener);
467
+ if (listeners.size === 0) listenersByEvent.delete(event);
468
+ if (listenersByEvent.size === 0) this._trackedCommunityListeners.delete(community);
469
+ }
470
+ _setupStartedEvents(community, connectionId, subscriptionId) {
471
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
472
+ method: "startCommunity",
473
+ subscription: subscriptionId,
474
+ event,
475
+ result,
476
+ connectionId
477
+ });
478
+ const getUpdateJson = () => typeof community.updatedAt === "number" ? community.toJSONInternalRpcAfterFirstUpdate() : community.toJSONInternalRpcBeforeFirstUpdate();
479
+ const updateListener = () => {
480
+ const json = getUpdateJson();
481
+ const communityIpfsRecord = community.raw.communityIpfs;
482
+ if (communityIpfsRecord?.posts?.pages && "runtimeFields" in json && json.runtimeFields) Object.assign(json.runtimeFields, { posts: { pages: buildPagesRuntimeFields(communityIpfsRecord.posts.pages, community._pkc._memCaches.nameResolvedCache) } });
483
+ sendEvent("update", json);
484
+ };
485
+ community.on("update", updateListener);
486
+ this._trackCommunityListener(community, "update", updateListener);
487
+ const startedStateListener = () => sendEvent("startedstatechange", { state: community.startedState });
488
+ community.on("startedstatechange", startedStateListener);
489
+ this._trackCommunityListener(community, "startedstatechange", startedStateListener);
490
+ const requestListener = (request) => sendEvent("challengerequest", encodeChallengeRequest(request));
491
+ community.on("challengerequest", requestListener);
492
+ this._trackCommunityListener(community, "challengerequest", requestListener);
493
+ const challengeListener = (challenge) => sendEvent("challenge", encodeChallengeMessage(challenge));
494
+ community.on("challenge", challengeListener);
495
+ this._trackCommunityListener(community, "challenge", challengeListener);
496
+ const challengeAnswerListener = (answer) => sendEvent("challengeanswer", encodeChallengeAnswerMessage(answer));
497
+ community.on("challengeanswer", challengeAnswerListener);
498
+ this._trackCommunityListener(community, "challengeanswer", challengeAnswerListener);
499
+ const challengeVerificationListener = (challengeVerification) => sendEvent("challengeverification", { challengeVerification: encodeChallengeVerificationMessage(challengeVerification) });
500
+ community.on("challengeverification", challengeVerificationListener);
501
+ this._trackCommunityListener(community, "challengeverification", challengeVerificationListener);
502
+ const errorListener = (error) => {
503
+ const rpcError = error;
504
+ if (community.state === "started") rpcError.details = {
505
+ ...rpcError.details,
506
+ newStartedState: community.startedState
507
+ };
508
+ else if (community.state === "updating") rpcError.details = {
509
+ ...rpcError.details,
510
+ newUpdatingState: community.updatingState
511
+ };
512
+ log("community rpc error", rpcError);
513
+ sendEvent("error", rpcError);
514
+ };
515
+ community.on("error", errorListener);
516
+ this._trackCommunityListener(community, "error", errorListener);
517
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
518
+ community.removeListener("update", updateListener);
519
+ this._untrackCommunityListener(community, "update", updateListener);
520
+ community.removeListener("startedstatechange", startedStateListener);
521
+ this._untrackCommunityListener(community, "startedstatechange", startedStateListener);
522
+ community.removeListener("challengerequest", requestListener);
523
+ this._untrackCommunityListener(community, "challengerequest", requestListener);
524
+ community.removeListener("challenge", challengeListener);
525
+ this._untrackCommunityListener(community, "challenge", challengeListener);
526
+ community.removeListener("challengeanswer", challengeAnswerListener);
527
+ this._untrackCommunityListener(community, "challengeanswer", challengeAnswerListener);
528
+ community.removeListener("challengeverification", challengeVerificationListener);
529
+ this._untrackCommunityListener(community, "challengeverification", challengeVerificationListener);
530
+ community.removeListener("error", errorListener);
531
+ this._untrackCommunityListener(community, "error", errorListener);
532
+ if (this._onSettingsChange[connectionId]) delete this._onSettingsChange[connectionId][subscriptionId];
533
+ };
534
+ }
535
+ async startCommunity(params, connectionId) {
536
+ const { name, publicKey } = parseRpcCommunityIdentifierParam(params[0]);
537
+ await this._getPKCInstance();
538
+ const address = this._findCommunityAddress({
539
+ name,
540
+ publicKey
541
+ });
542
+ if (!address) throw new PKCError("ERR_RPC_CLIENT_ATTEMPTING_TO_START_A_REMOTE_COMMUNITY", { communityAddress: name || publicKey });
543
+ const subscriptionId = generateSubscriptionId();
544
+ const startCommunityImpl = async () => {
545
+ const pkc = await this._getPKCInstance();
546
+ if (address in this._startedCommunities) {
547
+ const community = await this.getStartedCommunity(address);
548
+ this._setupStartedEvents(community, connectionId, subscriptionId);
549
+ } else try {
550
+ this._startedCommunities[address] = "pending";
551
+ const community = await pkc.createCommunity({ address });
552
+ this._setupStartedEvents(community, connectionId, subscriptionId);
553
+ community.started = true;
554
+ community.emit("update", community);
555
+ await community.start();
556
+ this._startedCommunities[address] = community;
557
+ this._updateCommunityState(address, {
558
+ wasStarted: true,
559
+ wasExplicitlyStopped: false
560
+ });
561
+ } catch (e) {
562
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
563
+ if (cleanup) await cleanup();
564
+ delete this._startedCommunities[address];
565
+ throw e;
566
+ }
567
+ };
568
+ this._onSettingsChange[connectionId][subscriptionId] = async ({ newPKC }) => {
569
+ const current = this._startedCommunities[address];
570
+ if (!current || current === "pending") return;
571
+ const community = await this.getStartedCommunity(address);
572
+ this._startedCommunities[address] = "pending";
573
+ try {
574
+ await community.stop();
575
+ community._pkc = newPKC;
576
+ await community.start();
577
+ this._startedCommunities[address] = community;
578
+ } catch (error) {
579
+ delete this._startedCommunities[address];
580
+ throw error;
581
+ }
582
+ };
583
+ await startCommunityImpl();
584
+ return { subscriptionId };
585
+ }
586
+ async stopCommunity(params) {
587
+ const { name, publicKey } = parseRpcCommunityIdentifierParam(params[0]);
588
+ await this._getPKCInstance();
589
+ const address = this._findCommunityAddress({
590
+ name,
591
+ publicKey
592
+ });
593
+ if (!address) throw new PKCError("ERR_RPC_CLIENT_TRYING_TO_STOP_REMOTE_COMMUNITY", { communityAddress: name || publicKey });
594
+ if (!(address in this._startedCommunities)) throw new PKCError("ERR_RPC_CLIENT_TRYING_TO_STOP_COMMUNITY_THAT_IS_NOT_RUNNING", { communityAddress: address });
595
+ const startedCommunity = await this.getStartedCommunity(address);
596
+ await startedCommunity.stop();
597
+ await this._postStoppingOrDeleting(startedCommunity);
598
+ delete this._startedCommunities[address];
599
+ this._updateCommunityState(address, { wasExplicitlyStopped: true });
600
+ return { success: true };
601
+ }
602
+ async _postStoppingOrDeleting(community) {
603
+ community.emit("update", community);
604
+ community.emit("startedstatechange", community.startedState);
605
+ const trackedListeners = this._trackedCommunityListeners.get(community);
606
+ if (trackedListeners) {
607
+ for (const [event, listeners] of trackedListeners) for (const listener of listeners) community.removeListener(event, listener);
608
+ this._trackedCommunityListeners.delete(community);
609
+ }
610
+ }
611
+ async editCommunity(params) {
612
+ const rawParam = params[0];
613
+ rawParam.editOptions = replaceXWithY(rawParam.editOptions, null, void 0);
614
+ const { name, publicKey, editOptions } = parseRpcEditCommunityParam(rawParam);
615
+ const editCommunityOptions = parseCommunityEditOptionsSchemaWithPKCErrorIfItFails(editOptions);
616
+ const pkc = await this._getPKCInstance();
617
+ const address = this._findCommunityAddress({
618
+ name,
619
+ publicKey
620
+ });
621
+ if (!address) throw new PKCError("ERR_RPC_CLIENT_TRYING_TO_EDIT_REMOTE_COMMUNITY", { communityAddress: name || publicKey });
622
+ let community;
623
+ if (this._startedCommunities[address] instanceof LocalCommunity) community = this._startedCommunities[address];
624
+ else {
625
+ community = await pkc.createCommunity({ address });
626
+ community.once("error", (error) => {
627
+ log.error("RPC server Received an error on community", community.address, "edit", error);
628
+ });
629
+ }
630
+ await community.edit(editCommunityOptions);
631
+ if (editCommunityOptions.address && this._startedCommunities[address]) {
632
+ this._startedCommunities[editCommunityOptions.address] = this._startedCommunities[address];
633
+ delete this._startedCommunities[address];
634
+ const db = this._getRpcStateDb();
635
+ if (db) db.prepare("UPDATE community_states SET address = @newAddress WHERE address = @oldAddress").run({
636
+ newAddress: editCommunityOptions.address,
637
+ oldAddress: address
638
+ });
639
+ }
640
+ if (typeof community.updatedAt === "number") return community.toJSONInternalRpcAfterFirstUpdate();
641
+ else return community.toJSONInternalRpcBeforeFirstUpdate();
642
+ }
643
+ async deleteCommunity(params) {
644
+ const { name, publicKey } = parseRpcCommunityIdentifierParam(params[0]);
645
+ const pkc = await this._getPKCInstance();
646
+ const address = this._findCommunityAddress({
647
+ name,
648
+ publicKey
649
+ });
650
+ if (!address) throw new PKCError("ERR_RPC_CLIENT_TRYING_TO_DELETE_REMOTE_COMMUNITY", { communityAddress: name || publicKey });
651
+ const community = address in this._startedCommunities ? await this.getStartedCommunity(address) : await pkc.createCommunity({ address });
652
+ await community.delete();
653
+ await this._postStoppingOrDeleting(community);
654
+ delete this._startedCommunities[address];
655
+ this._removeCommunityState(address);
656
+ return { success: true };
657
+ }
658
+ async communitiesSubscribe(params, connectionId) {
659
+ const subscriptionId = generateSubscriptionId();
660
+ const sendEvent = (event, result) => {
661
+ this.jsonRpcSendNotification({
662
+ method: "communitiesNotification",
663
+ subscription: Number(subscriptionId),
664
+ event,
665
+ result,
666
+ connectionId
667
+ });
668
+ };
669
+ const pkcSubscribeEvent = (newCommunities) => sendEvent("communitieschange", { communities: newCommunities });
670
+ const pkc = await this._getPKCInstance();
671
+ pkc.on("communitieschange", pkcSubscribeEvent);
672
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
673
+ pkc.removeListener("communitieschange", pkcSubscribeEvent);
674
+ };
675
+ sendEvent("communitieschange", { communities: pkc.communities });
676
+ return { subscriptionId };
677
+ }
678
+ async fetchCid(params) {
679
+ const parsedArgs = parseRpcCidParam(params[0]);
680
+ return (await this._getPKCInstance()).fetchCid(parsedArgs);
681
+ }
682
+ _serializeSettingsFromPKC(pkc) {
683
+ const pkcOptions = pkc.parsedPKCOptions;
684
+ const allChallengeFactories = {
685
+ ...PKCJs.PKC.challenges || {},
686
+ ...pkc.settings?.challenges || {}
687
+ };
688
+ return {
689
+ pkcOptions,
690
+ challenges: import_commonjs.mapValues(allChallengeFactories, (challengeFactory) => import_commonjs.omit(challengeFactory({ challengeSettings: {} }), ["getChallenge"]))
691
+ };
692
+ }
693
+ async settingsSubscribe(params, connectionId) {
694
+ const subscriptionId = generateSubscriptionId();
695
+ const sendEvent = (event, result) => {
696
+ this.jsonRpcSendNotification({
697
+ method: "settingsNotification",
698
+ subscription: Number(subscriptionId),
699
+ event,
700
+ result,
701
+ connectionId
702
+ });
703
+ };
704
+ const sendRpcSettings = async ({ newPKC }) => {
705
+ sendEvent("settingschange", this._serializeSettingsFromPKC(newPKC));
706
+ };
707
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
708
+ if (this._onSettingsChange[connectionId]) delete this._onSettingsChange[connectionId][subscriptionId];
709
+ };
710
+ this._onSettingsChange[connectionId][subscriptionId] = sendRpcSettings;
711
+ await sendRpcSettings({ newPKC: this.pkc });
712
+ return { subscriptionId };
713
+ }
714
+ _initPKC(pkc) {
715
+ this.pkc = pkc;
716
+ pkc.on("error", (error) => log.error("RPC server", "Received an error on pkc instance", error));
717
+ }
718
+ async _createPKCInstanceFromSetSettings(newOptions) {
719
+ return PKCJs.PKC(newOptions);
720
+ }
721
+ async setSettings(params) {
722
+ const runSetSettings = async () => {
723
+ const settings = parseSetNewSettingsPKCWsServerSchemaWithPKCErrorIfItFails(params[0]);
724
+ const currentSettings = this._serializeSettingsFromPKC(this.pkc);
725
+ if (stringify(settings.pkcOptions) === stringify(currentSettings.pkcOptions)) {
726
+ log("RPC client called setSettings with the same settings as the current one, aborting");
727
+ return;
728
+ }
729
+ log(`RPC client called setSettings, the clients need to call all subscription methods again`);
730
+ const oldPKC = this.pkc;
731
+ const { nameResolvers: _stripNr, ...pkcOptionsWithoutNameResolvers } = settings.pkcOptions;
732
+ const newPKC = await this._createPKCInstanceFromSetSettings({
733
+ ...pkcOptionsWithoutNameResolvers,
734
+ nameResolvers: this.pkc.parsedPKCOptions.nameResolvers
735
+ });
736
+ this._initPKC(newPKC);
737
+ for (const connectionId of import_commonjs.keys.strict(this._onSettingsChange)) {
738
+ const connectionHandlers = this._onSettingsChange[connectionId];
739
+ if (!connectionHandlers) continue;
740
+ for (const subscriptionId of import_commonjs.keys.strict(connectionHandlers)) {
741
+ const handler = connectionHandlers[subscriptionId];
742
+ if (!handler) continue;
743
+ try {
744
+ await handler({ newPKC });
745
+ } catch (error) {
746
+ log.error(`Failed to apply settings change to subscription ${subscriptionId} of connection ${connectionId}, continuing with remaining subscriptions`, error);
747
+ }
748
+ }
749
+ }
750
+ for (const [subscriptionId, pub] of Object.entries(this.publishing).filter((pub) => pub[1].pkc === oldPKC)) pub.timeout = setTimeout(async () => {
751
+ await this._forceCleanupPublication(Number(subscriptionId), "timeout");
752
+ }, 6e4);
753
+ setTimeout(async () => {
754
+ await this._retirePKCIfNeeded(oldPKC);
755
+ }, 6e4);
756
+ };
757
+ const setSettingsRun = this._setSettingsQueue.then(() => runSetSettings());
758
+ this._setSettingsQueue = setSettingsRun.catch(() => {});
759
+ await setSettingsRun;
760
+ return { success: true };
761
+ }
762
+ async commentUpdateSubscribe(params, connectionId) {
763
+ const logUpdate = logger_default("pkc-js-rpc:pkc-ws-server:commentUpdateSubscribe");
764
+ const parsedCommentUpdateArgs = parseRpcCidParam(params[0]);
765
+ const subscriptionId = generateSubscriptionId();
766
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
767
+ method: "commentUpdateNotification",
768
+ subscription: subscriptionId,
769
+ event,
770
+ result,
771
+ connectionId
772
+ });
773
+ let sentCommentIpfsUpdateEvent = false;
774
+ let lastSentNameResolved = void 0;
775
+ const pkc = await this._getPKCInstance();
776
+ const comment = await pkc.createComment(parsedCommentUpdateArgs);
777
+ const sendUpdate = () => {
778
+ if (!sentCommentIpfsUpdateEvent && comment.raw.comment) {
779
+ sendEvent("comment", {
780
+ comment: comment.raw.comment,
781
+ runtimeFields: { author: { nameResolved: comment.author.nameResolved } }
782
+ });
783
+ sentCommentIpfsUpdateEvent = true;
784
+ lastSentNameResolved = comment.author.nameResolved;
785
+ }
786
+ if (comment.raw.commentUpdate) {
787
+ const updateEvent = { commentUpdate: comment.raw.commentUpdate };
788
+ const runtimeFields = {};
789
+ if (comment.raw.commentUpdate.replies?.pages) runtimeFields.replies = { pages: buildPagesRuntimeFields(comment.raw.commentUpdate.replies.pages, pkc._memCaches.nameResolvedCache) };
790
+ if (typeof comment.author.nameResolved === "boolean") runtimeFields.author = { nameResolved: comment.author.nameResolved };
791
+ if (Object.keys(runtimeFields).length > 0) updateEvent.runtimeFields = runtimeFields;
792
+ sendEvent("update", updateEvent);
793
+ lastSentNameResolved = comment.author.nameResolved;
794
+ } else if (sentCommentIpfsUpdateEvent && typeof comment.author.nameResolved === "boolean" && comment.author.nameResolved !== lastSentNameResolved) {
795
+ sendEvent("runtimeupdate", { author: { nameResolved: comment.author.nameResolved } });
796
+ lastSentNameResolved = comment.author.nameResolved;
797
+ }
798
+ };
799
+ const updateListener = () => sendUpdate();
800
+ comment.on("update", updateListener);
801
+ const updatingStateListener = () => sendEvent("updatingstatechange", { state: comment.updatingState });
802
+ comment.on("updatingstatechange", updatingStateListener);
803
+ const stateListener = () => sendEvent("statechange", { state: comment.state });
804
+ comment.on("statechange", stateListener);
805
+ const errorListener = (error) => {
806
+ const errorWithNewUpdatingState = error;
807
+ if (comment.state === "publishing") errorWithNewUpdatingState.details = {
808
+ ...errorWithNewUpdatingState.details,
809
+ newPublishingState: comment.publishingState
810
+ };
811
+ else if (comment.state === "updating") errorWithNewUpdatingState.details = {
812
+ ...errorWithNewUpdatingState.details,
813
+ newUpdatingState: comment.updatingState
814
+ };
815
+ sendEvent("error", errorWithNewUpdatingState);
816
+ };
817
+ comment.on("error", errorListener);
818
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
819
+ logUpdate("Cleaning up commentUpdate subscription", {
820
+ subscriptionId,
821
+ connectionId,
822
+ cid: comment.cid
823
+ });
824
+ comment.removeListener("update", updateListener);
825
+ comment.removeListener("updatingstatechange", updatingStateListener);
826
+ comment.removeListener("statechange", stateListener);
827
+ comment.removeListener("error", errorListener);
828
+ await comment.stop();
829
+ if (this._onSettingsChange[connectionId]) delete this._onSettingsChange[connectionId][subscriptionId];
830
+ };
831
+ this._onSettingsChange[connectionId][subscriptionId] = async ({ newPKC }) => {
832
+ comment._pkc = newPKC;
833
+ await comment.update();
834
+ };
835
+ try {
836
+ sendUpdate();
837
+ await comment.update();
838
+ } catch (e) {
839
+ logUpdate.error("Cleaning up subscription to comment", comment.cid, "because comment.update threw an error", e);
840
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
841
+ if (cleanup) await cleanup();
842
+ throw e;
843
+ }
844
+ return { subscriptionId };
845
+ }
846
+ async communityUpdateSubscribe(params, connectionId) {
847
+ const parsedCommunityUpdateArgs = parseRpcCommunityIdentifierParam(params[0]);
848
+ const subscriptionId = generateSubscriptionId();
849
+ await this._bindCommunityUpdateSubscription(parsedCommunityUpdateArgs, connectionId, subscriptionId);
850
+ return { subscriptionId };
851
+ }
852
+ async _bindCommunityUpdateSubscription(parsedArgs, connectionId, subscriptionId) {
853
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
854
+ method: "communityUpdateNotification",
855
+ subscription: subscriptionId,
856
+ event,
857
+ result,
858
+ connectionId
859
+ });
860
+ const pkc = await this._getPKCInstance();
861
+ const startedCommunity = findStartedCommunity(pkc, parsedArgs);
862
+ const isStartedCommunity = Boolean(startedCommunity);
863
+ const community = startedCommunity || await pkc.createCommunity(parsedArgs);
864
+ const sendCommunityJson = () => {
865
+ let jsonToSend;
866
+ if (community instanceof LocalCommunity) jsonToSend = typeof community.updatedAt === "number" ? community.toJSONInternalRpcAfterFirstUpdate() : community.toJSONInternalRpcBeforeFirstUpdate();
867
+ else jsonToSend = community.toJSONRpcRemote();
868
+ const communityIpfsRecord = community.raw.communityIpfs;
869
+ if (communityIpfsRecord?.posts?.pages && "runtimeFields" in jsonToSend && jsonToSend.runtimeFields) Object.assign(jsonToSend.runtimeFields, { posts: { pages: buildPagesRuntimeFields(communityIpfsRecord.posts.pages, pkc._memCaches.nameResolvedCache) } });
870
+ sendEvent("update", jsonToSend);
871
+ };
872
+ const updateListener = () => sendCommunityJson();
873
+ community.on("update", updateListener);
874
+ const updatingStateListener = () => sendEvent("updatingstatechange", { state: community.updatingState });
875
+ community.on("updatingstatechange", updatingStateListener);
876
+ const startedStateListener = () => sendEvent("updatingstatechange", { state: community.startedState });
877
+ if (isStartedCommunity) community.on("startedstatechange", startedStateListener);
878
+ const errorListener = (error) => {
879
+ const rpcError = error;
880
+ if (community.state === "started") rpcError.details = {
881
+ ...rpcError.details,
882
+ newStartedState: community.startedState
883
+ };
884
+ else if (community.state === "updating") rpcError.details = {
885
+ ...rpcError.details,
886
+ newUpdatingState: community.updatingState
887
+ };
888
+ log("community rpc error", rpcError);
889
+ sendEvent("error", rpcError);
890
+ };
891
+ community.on("error", errorListener);
892
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
893
+ log("Cleaning up community", community.address, "client subscription");
894
+ community.removeListener("update", updateListener);
895
+ community.removeListener("updatingstatechange", updatingStateListener);
896
+ community.removeListener("error", errorListener);
897
+ community.removeListener("startedstatechange", startedStateListener);
898
+ if (this._onSettingsChange[connectionId]) delete this._onSettingsChange[connectionId][subscriptionId];
899
+ if (!isStartedCommunity && community.state !== "stopped") await community.stop();
900
+ };
901
+ this._onSettingsChange[connectionId][subscriptionId] = async ({ newPKC }) => {
902
+ if (!isStartedCommunity) {
903
+ community._pkc = newPKC;
904
+ await community.stop();
905
+ await community.update();
906
+ }
907
+ };
908
+ try {
909
+ if ("signer" in community || community.raw.communityIpfs) sendCommunityJson();
910
+ if (!isStartedCommunity) await community.update();
911
+ } catch (e) {
912
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
913
+ if (cleanup) await cleanup();
914
+ throw e;
915
+ }
916
+ }
917
+ async _createCommentInstanceFromPublishCommentParams(params) {
918
+ const comment = await (await this._getPKCInstance()).createComment(params.comment);
919
+ comment.challengeRequest = import_commonjs.omit(params, ["comment"]);
920
+ return comment;
921
+ }
922
+ async publishComment(params, connectionId) {
923
+ const publishOptions = parseCommentChallengeRequestToEncryptSchemaWithPKCErrorIfItFails(params[0]);
924
+ const subscriptionId = generateSubscriptionId();
925
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
926
+ method: "publishCommentNotification",
927
+ subscription: subscriptionId,
928
+ event,
929
+ result,
930
+ connectionId
931
+ });
932
+ const comment = await this._createCommentInstanceFromPublishCommentParams(publishOptions);
933
+ this._registerPublishing(subscriptionId, comment, comment._pkc, connectionId);
934
+ const challengeListener = (challenge) => sendEvent("challenge", encodeChallengeMessage(challenge));
935
+ comment.on("challenge", challengeListener);
936
+ const challengeAnswerListener = (answer) => sendEvent("challengeanswer", encodeChallengeAnswerMessage(answer));
937
+ comment.on("challengeanswer", challengeAnswerListener);
938
+ const challengeRequestListener = (request) => sendEvent("challengerequest", encodeChallengeRequest(request));
939
+ comment.on("challengerequest", challengeRequestListener);
940
+ const challengeVerificationListener = (challengeVerification) => sendEvent("challengeverification", {
941
+ challengeVerification: encodeChallengeVerificationMessage(challengeVerification),
942
+ runtimeFields: { author: { nameResolved: comment.author.nameResolved } }
943
+ });
944
+ comment.on("challengeverification", challengeVerificationListener);
945
+ const publishingStateListener = () => {
946
+ sendEvent("publishingstatechange", { state: comment.publishingState });
947
+ };
948
+ comment.on("publishingstatechange", publishingStateListener);
949
+ const stateListener = () => sendEvent("statechange", { state: comment.state });
950
+ comment.on("statechange", stateListener);
951
+ const errorListener = (error) => {
952
+ const commentRpcError = error;
953
+ commentRpcError.details = {
954
+ ...commentRpcError.details,
955
+ newPublishingState: comment.publishingState
956
+ };
957
+ sendEvent("error", commentRpcError);
958
+ };
959
+ comment.on("error", errorListener);
960
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
961
+ comment.removeListener("challenge", challengeListener);
962
+ comment.removeListener("challengeanswer", challengeAnswerListener);
963
+ comment.removeListener("challengerequest", challengeRequestListener);
964
+ comment.removeListener("challengeverification", challengeVerificationListener);
965
+ comment.removeListener("publishingstatechange", publishingStateListener);
966
+ comment.removeListener("statechange", stateListener);
967
+ comment.removeListener("error", errorListener);
968
+ await comment.stop();
969
+ this._clearPublishing(subscriptionId);
970
+ if (this._onSettingsChange[connectionId]) delete this._onSettingsChange[connectionId][subscriptionId];
971
+ };
972
+ try {
973
+ await comment.publish();
974
+ } catch (e) {
975
+ const error = e;
976
+ error.details = {
977
+ ...error.details,
978
+ publishThrowError: true
979
+ };
980
+ errorListener(error);
981
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
982
+ if (cleanup) await cleanup();
983
+ return { subscriptionId };
984
+ }
985
+ return { subscriptionId };
986
+ }
987
+ async _createVoteInstanceFromPublishVoteParams(params) {
988
+ const vote = await (await this._getPKCInstance()).createVote(params.vote);
989
+ vote.challengeRequest = import_commonjs.omit(params, ["vote"]);
990
+ return vote;
991
+ }
992
+ async publishVote(params, connectionId) {
993
+ const publishOptions = parseVoteChallengeRequestToEncryptSchemaWithPKCErrorIfItFails(params[0]);
994
+ const subscriptionId = generateSubscriptionId();
995
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
996
+ method: "publishVoteNotification",
997
+ subscription: subscriptionId,
998
+ event,
999
+ result,
1000
+ connectionId
1001
+ });
1002
+ const vote = await this._createVoteInstanceFromPublishVoteParams(publishOptions);
1003
+ this._registerPublishing(subscriptionId, vote, vote._pkc, connectionId);
1004
+ const challengeListener = (challenge) => sendEvent("challenge", encodeChallengeMessage(challenge));
1005
+ vote.on("challenge", challengeListener);
1006
+ const challengeAnswerListener = (answer) => sendEvent("challengeanswer", encodeChallengeAnswerMessage(answer));
1007
+ vote.on("challengeanswer", challengeAnswerListener);
1008
+ const challengeRequestListener = (request) => sendEvent("challengerequest", encodeChallengeRequest(request));
1009
+ vote.on("challengerequest", challengeRequestListener);
1010
+ const challengeVerificationListener = (challengeVerification) => sendEvent("challengeverification", { challengeVerification: encodeChallengeVerificationMessage(challengeVerification) });
1011
+ vote.on("challengeverification", challengeVerificationListener);
1012
+ const publishingStateListener = () => sendEvent("publishingstatechange", { state: vote.publishingState });
1013
+ vote.on("publishingstatechange", publishingStateListener);
1014
+ const errorListener = (error) => {
1015
+ const voteRpcError = error;
1016
+ voteRpcError.details = {
1017
+ ...voteRpcError.details,
1018
+ newPublishingState: vote.publishingState
1019
+ };
1020
+ sendEvent("error", voteRpcError);
1021
+ };
1022
+ vote.on("error", errorListener);
1023
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
1024
+ this._clearPublishing(subscriptionId);
1025
+ await vote.stop();
1026
+ vote.removeListener("challenge", challengeListener);
1027
+ vote.removeListener("challengeanswer", challengeAnswerListener);
1028
+ vote.removeListener("challengerequest", challengeRequestListener);
1029
+ vote.removeListener("challengeverification", challengeVerificationListener);
1030
+ vote.removeListener("publishingstatechange", publishingStateListener);
1031
+ vote.removeListener("error", errorListener);
1032
+ };
1033
+ try {
1034
+ await vote.publish();
1035
+ } catch (e) {
1036
+ const error = e;
1037
+ error.details = {
1038
+ ...error.details,
1039
+ publishThrowError: true
1040
+ };
1041
+ errorListener(error);
1042
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
1043
+ if (cleanup) await cleanup();
1044
+ }
1045
+ return { subscriptionId };
1046
+ }
1047
+ async _createCommunityEditInstanceFromPublishCommunityEditParams(params) {
1048
+ const communityEdit = await (await this._getPKCInstance()).createCommunityEdit(params.communityEdit);
1049
+ communityEdit.challengeRequest = import_commonjs.omit(params, ["communityEdit"]);
1050
+ return communityEdit;
1051
+ }
1052
+ async publishCommunityEdit(params, connectionId) {
1053
+ const publishOptions = parseCommunityEditChallengeRequestToEncryptSchemaWithPKCErrorIfItFails(params[0]);
1054
+ const subscriptionId = generateSubscriptionId();
1055
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
1056
+ method: "publishCommunityEditNotification",
1057
+ subscription: subscriptionId,
1058
+ event,
1059
+ result,
1060
+ connectionId
1061
+ });
1062
+ const communityEdit = await this._createCommunityEditInstanceFromPublishCommunityEditParams(publishOptions);
1063
+ this._registerPublishing(subscriptionId, communityEdit, communityEdit._pkc, connectionId);
1064
+ const challengeListener = (challenge) => sendEvent("challenge", encodeChallengeMessage(challenge));
1065
+ communityEdit.on("challenge", challengeListener);
1066
+ const challengeAnswerListener = (answer) => sendEvent("challengeanswer", encodeChallengeAnswerMessage(answer));
1067
+ communityEdit.on("challengeanswer", challengeAnswerListener);
1068
+ const challengeRequestListener = (request) => sendEvent("challengerequest", encodeChallengeRequest(request));
1069
+ communityEdit.on("challengerequest", challengeRequestListener);
1070
+ const challengeVerificationListener = (challengeVerification) => sendEvent("challengeverification", { challengeVerification: encodeChallengeVerificationMessage(challengeVerification) });
1071
+ communityEdit.on("challengeverification", challengeVerificationListener);
1072
+ const publishingStateListener = () => sendEvent("publishingstatechange", { state: communityEdit.publishingState });
1073
+ communityEdit.on("publishingstatechange", publishingStateListener);
1074
+ const errorListener = (error) => {
1075
+ const editRpcError = error;
1076
+ editRpcError.details = {
1077
+ ...editRpcError.details,
1078
+ newPublishingState: communityEdit.publishingState
1079
+ };
1080
+ sendEvent("error", editRpcError);
1081
+ };
1082
+ communityEdit.on("error", errorListener);
1083
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
1084
+ this._clearPublishing(subscriptionId);
1085
+ await communityEdit.stop();
1086
+ communityEdit.removeListener("challenge", challengeListener);
1087
+ communityEdit.removeListener("challengeanswer", challengeAnswerListener);
1088
+ communityEdit.removeListener("challengerequest", challengeRequestListener);
1089
+ communityEdit.removeListener("challengeverification", challengeVerificationListener);
1090
+ communityEdit.removeListener("publishingstatechange", publishingStateListener);
1091
+ communityEdit.removeListener("error", errorListener);
1092
+ };
1093
+ try {
1094
+ await communityEdit.publish();
1095
+ } catch (e) {
1096
+ const error = e;
1097
+ error.details = {
1098
+ ...error.details,
1099
+ publishThrowError: true
1100
+ };
1101
+ errorListener(error);
1102
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
1103
+ if (cleanup) await cleanup();
1104
+ }
1105
+ return { subscriptionId };
1106
+ }
1107
+ async _createCommentEditInstanceFromPublishCommentEditParams(params) {
1108
+ const commentEdit = await (await this._getPKCInstance()).createCommentEdit(params.commentEdit);
1109
+ commentEdit.challengeRequest = import_commonjs.omit(params, ["commentEdit"]);
1110
+ return commentEdit;
1111
+ }
1112
+ async publishCommentEdit(params, connectionId) {
1113
+ const publishOptions = parseCommentEditChallengeRequestToEncryptSchemaWithPKCErrorIfItFails(params[0]);
1114
+ const subscriptionId = generateSubscriptionId();
1115
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
1116
+ method: "publishCommentEditNotification",
1117
+ subscription: subscriptionId,
1118
+ event,
1119
+ result,
1120
+ connectionId
1121
+ });
1122
+ const commentEdit = await this._createCommentEditInstanceFromPublishCommentEditParams(publishOptions);
1123
+ this._registerPublishing(subscriptionId, commentEdit, commentEdit._pkc, connectionId);
1124
+ const challengeListener = (challenge) => sendEvent("challenge", encodeChallengeMessage(challenge));
1125
+ commentEdit.on("challenge", challengeListener);
1126
+ const challengeAnswerListener = (answer) => sendEvent("challengeanswer", encodeChallengeAnswerMessage(answer));
1127
+ commentEdit.on("challengeanswer", challengeAnswerListener);
1128
+ const challengeRequestListener = (request) => sendEvent("challengerequest", encodeChallengeRequest(request));
1129
+ commentEdit.on("challengerequest", challengeRequestListener);
1130
+ const challengeVerificationListener = (challengeVerification) => sendEvent("challengeverification", { challengeVerification: encodeChallengeVerificationMessage(challengeVerification) });
1131
+ commentEdit.on("challengeverification", challengeVerificationListener);
1132
+ const publishingStateListener = () => sendEvent("publishingstatechange", { state: commentEdit.publishingState });
1133
+ commentEdit.on("publishingstatechange", publishingStateListener);
1134
+ const errorListener = (error) => {
1135
+ const commentEditRpcError = error;
1136
+ commentEditRpcError.details = {
1137
+ ...commentEditRpcError.details,
1138
+ newPublishingState: commentEdit.publishingState
1139
+ };
1140
+ sendEvent("error", commentEditRpcError);
1141
+ };
1142
+ commentEdit.on("error", errorListener);
1143
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
1144
+ this._clearPublishing(subscriptionId);
1145
+ await commentEdit.stop();
1146
+ commentEdit.removeListener("challenge", challengeListener);
1147
+ commentEdit.removeListener("challengeanswer", challengeAnswerListener);
1148
+ commentEdit.removeListener("challengerequest", challengeRequestListener);
1149
+ commentEdit.removeListener("challengeverification", challengeVerificationListener);
1150
+ commentEdit.removeListener("publishingstatechange", publishingStateListener);
1151
+ commentEdit.removeListener("error", errorListener);
1152
+ };
1153
+ try {
1154
+ await commentEdit.publish();
1155
+ } catch (e) {
1156
+ const error = e;
1157
+ error.details = {
1158
+ ...error.details,
1159
+ publishThrowError: true
1160
+ };
1161
+ errorListener(error);
1162
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
1163
+ if (cleanup) await cleanup();
1164
+ }
1165
+ return { subscriptionId };
1166
+ }
1167
+ async _createCommentModerationInstanceFromPublishCommentModerationParams(params) {
1168
+ const commentModeration = await (await this._getPKCInstance()).createCommentModeration(params.commentModeration);
1169
+ commentModeration.challengeRequest = import_commonjs.omit(params, ["commentModeration"]);
1170
+ return commentModeration;
1171
+ }
1172
+ async publishCommentModeration(params, connectionId) {
1173
+ const publishOptions = parseCommentModerationChallengeRequestToEncryptSchemaWithPKCErrorIfItFails(params[0]);
1174
+ const subscriptionId = generateSubscriptionId();
1175
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
1176
+ method: "publishCommentModerationNotification",
1177
+ subscription: subscriptionId,
1178
+ event,
1179
+ result,
1180
+ connectionId
1181
+ });
1182
+ const commentMod = await this._createCommentModerationInstanceFromPublishCommentModerationParams(publishOptions);
1183
+ this._registerPublishing(subscriptionId, commentMod, commentMod._pkc, connectionId);
1184
+ const challengeListener = (challenge) => sendEvent("challenge", encodeChallengeMessage(challenge));
1185
+ commentMod.on("challenge", challengeListener);
1186
+ const challengeAnswerListener = (answer) => sendEvent("challengeanswer", encodeChallengeAnswerMessage(answer));
1187
+ commentMod.on("challengeanswer", challengeAnswerListener);
1188
+ const challengeRequestListener = (request) => sendEvent("challengerequest", encodeChallengeRequest(request));
1189
+ commentMod.on("challengerequest", challengeRequestListener);
1190
+ const challengeVerificationListener = (challengeVerification) => sendEvent("challengeverification", { challengeVerification: encodeChallengeVerificationMessage(challengeVerification) });
1191
+ commentMod.on("challengeverification", challengeVerificationListener);
1192
+ const publishingStateListener = () => sendEvent("publishingstatechange", { state: commentMod.publishingState });
1193
+ commentMod.on("publishingstatechange", publishingStateListener);
1194
+ const errorListener = (error) => {
1195
+ const commentModRpcError = error;
1196
+ commentModRpcError.details = {
1197
+ ...commentModRpcError.details,
1198
+ newPublishingState: commentMod.publishingState
1199
+ };
1200
+ sendEvent("error", commentModRpcError);
1201
+ };
1202
+ commentMod.on("error", errorListener);
1203
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
1204
+ commentMod.removeListener("challenge", challengeListener);
1205
+ commentMod.removeListener("challengeanswer", challengeAnswerListener);
1206
+ commentMod.removeListener("challengerequest", challengeRequestListener);
1207
+ commentMod.removeListener("challengeverification", challengeVerificationListener);
1208
+ commentMod.removeListener("publishingstatechange", publishingStateListener);
1209
+ commentMod.removeListener("error", errorListener);
1210
+ await commentMod.stop();
1211
+ this._clearPublishing(subscriptionId);
1212
+ };
1213
+ try {
1214
+ await commentMod.publish();
1215
+ } catch (e) {
1216
+ const error = e;
1217
+ error.details = {
1218
+ ...error.details,
1219
+ publishThrowError: true
1220
+ };
1221
+ errorListener(error);
1222
+ const cleanup = this.subscriptionCleanups?.[connectionId]?.[subscriptionId];
1223
+ if (cleanup) await cleanup();
1224
+ }
1225
+ return { subscriptionId };
1226
+ }
1227
+ async publishChallengeAnswers(params) {
1228
+ const parsed = parseRpcPublishChallengeAnswersParam(params[0]);
1229
+ const subscriptionId = parsed.subscriptionId;
1230
+ const decryptedChallengeAnswers = parseDecryptedChallengeAnswerWithPKCErrorIfItFails({ challengeAnswers: parsed.challengeAnswers });
1231
+ const record = this.publishing[subscriptionId];
1232
+ if (!record?.publication) throw Error(`no subscription with id '${subscriptionId}'`);
1233
+ const publication = record.publication;
1234
+ await this._getPKCInstance();
1235
+ await publication.publishChallengeAnswers({ challengeAnswers: decryptedChallengeAnswers.challengeAnswers });
1236
+ return { success: true };
1237
+ }
1238
+ async resolveAuthorName(params) {
1239
+ const parsedArgs = parseRpcAuthorNameParam(params[0]);
1240
+ return (await this._getPKCInstance()).resolveAuthorName(parsedArgs);
1241
+ }
1242
+ async _resolveLocalCommunityForExport(parsedArgs) {
1243
+ const address = this._findCommunityAddress(parsedArgs);
1244
+ if (!address) throw new PKCError("ERR_COMMUNITY_NOT_FOUND", {
1245
+ name: parsedArgs.name,
1246
+ publicKey: parsedArgs.publicKey
1247
+ });
1248
+ const cached = this._exportCommunityInstances.get(address);
1249
+ if (cached) return cached;
1250
+ const started = findStartedCommunity(this.pkc, parsedArgs);
1251
+ if (started instanceof LocalCommunity) {
1252
+ this._exportCommunityInstances.set(address, started);
1253
+ return started;
1254
+ }
1255
+ const community = await this.pkc.createCommunity({ address });
1256
+ if (!(community instanceof LocalCommunity)) throw new PKCError("ERR_COMMUNITY_NOT_LOCAL", { address });
1257
+ this._exportCommunityInstances.set(address, community);
1258
+ return community;
1259
+ }
1260
+ _toWireExportRecord(rec) {
1261
+ if (rec.progress === 1 && rec.url) return {
1262
+ ...rec,
1263
+ url: `/exports/${rec.exportId}`
1264
+ };
1265
+ return rec;
1266
+ }
1267
+ async exportCommunity(params, connectionId) {
1268
+ const parsedArgs = parseRpcExportCommunityParam(params[0]);
1269
+ if (parsedArgs.includePrivateKey === true && !this._allowPrivateKeyExport) throw new PKCError("ERR_PRIVATE_KEY_EXPORT_NOT_ALLOWED", {});
1270
+ return (await this._resolveLocalCommunityForExport(parsedArgs)).export({ includePrivateKey: parsedArgs.includePrivateKey });
1271
+ }
1272
+ async exportCommunityModLogs(params) {
1273
+ const { name, publicKey, startTimestamp, endTimestamp, commentCid, limit, order } = parseRpcExportCommunityModLogsParam(params[0]);
1274
+ const address = this._findCommunityAddress({
1275
+ name,
1276
+ publicKey
1277
+ });
1278
+ if (!address) throw new PKCError("ERR_COMMUNITY_NOT_FOUND", {
1279
+ name,
1280
+ publicKey
1281
+ });
1282
+ let community;
1283
+ if (this._startedCommunities[address] instanceof LocalCommunity) community = this._startedCommunities[address];
1284
+ else {
1285
+ const created = await (await this._getPKCInstance()).createCommunity({ address });
1286
+ if (!(created instanceof LocalCommunity)) throw new PKCError("ERR_COMMUNITY_NOT_LOCAL", { address });
1287
+ community = created;
1288
+ }
1289
+ return community.exportCommunityModLogs({
1290
+ startTimestamp,
1291
+ endTimestamp,
1292
+ commentCid,
1293
+ limit,
1294
+ order
1295
+ });
1296
+ }
1297
+ async exportsSubscribe(params, connectionId) {
1298
+ const parsedArgs = parseRpcCommunityIdentifierParam(params[0]);
1299
+ const subscriptionId = generateSubscriptionId();
1300
+ const community = await this._resolveLocalCommunityForExport(parsedArgs);
1301
+ const sendEvent = (event, result) => this.jsonRpcSendNotification({
1302
+ method: "exportschange",
1303
+ subscription: subscriptionId,
1304
+ event,
1305
+ result,
1306
+ connectionId
1307
+ });
1308
+ const exportschangeListener = (records) => sendEvent("exportschange", { records: records.map((r) => this._toWireExportRecord(r)) });
1309
+ community.on("exportschange", exportschangeListener);
1310
+ this.subscriptionCleanups[connectionId][subscriptionId] = async () => {
1311
+ community.removeListener("exportschange", exportschangeListener);
1312
+ };
1313
+ sendEvent("exportschange", { records: community.exports.map((r) => this._toWireExportRecord(r)) });
1314
+ return { subscriptionId };
1315
+ }
1316
+ async cancelExport(params, connectionId) {
1317
+ const { exportId } = parseRpcCancelExportParam(params[0]);
1318
+ for (const community of this._exportLoadedCommunities()) if (community._activeExports.has(exportId)) {
1319
+ await community._cancelExport(exportId);
1320
+ break;
1321
+ }
1322
+ return { success: true };
1323
+ }
1324
+ *_exportLoadedCommunities() {
1325
+ for (const value of Object.values(this._startedCommunities)) if (value instanceof LocalCommunity) yield value;
1326
+ for (const value of this._exportCommunityInstances.values()) yield value;
1327
+ }
1328
+ _findCommunityOwningExport(exportId) {
1329
+ for (const community of this._exportLoadedCommunities()) {
1330
+ const record = community._exports.find((r) => r.exportId === exportId);
1331
+ if (record) return {
1332
+ community,
1333
+ record
1334
+ };
1335
+ }
1336
+ }
1337
+ async _sweepOldExportFiles() {
1338
+ const MAX_AGE_MS = this._exportFileMaxAgeMs;
1339
+ if (MAX_AGE_MS === 0) return;
1340
+ if (!this.pkc.dataPath) return;
1341
+ const exportsDir = path.join(this.pkc.dataPath, "exports");
1342
+ if (!existsSync(exportsDir)) return;
1343
+ const now = Date.now();
1344
+ let entries = [];
1345
+ try {
1346
+ entries = await promises.readdir(exportsDir);
1347
+ } catch {
1348
+ return;
1349
+ }
1350
+ for (const entry of entries) {
1351
+ if (!entry.endsWith(".sqlite")) continue;
1352
+ const filePath = path.join(exportsDir, entry);
1353
+ try {
1354
+ if (now - (await promises.stat(filePath)).mtimeMs > MAX_AGE_MS) await promises.unlink(filePath);
1355
+ } catch {}
1356
+ }
1357
+ }
1358
+ async _handleExportsHttpRequest(req, res) {
1359
+ const httpLog = logger_default("pkc-js:PKCWsServer:exports-http");
1360
+ const match = new URL(req.url ?? "/", "http://localhost").pathname.match(/^\/exports\/([0-9a-fA-F-]{36})$/);
1361
+ if (!match) {
1362
+ if (this._ownsHttpServer) {
1363
+ res.statusCode = 404;
1364
+ res.end();
1365
+ }
1366
+ return;
1367
+ }
1368
+ if (req.method !== "GET") {
1369
+ res.statusCode = 405;
1370
+ res.setHeader("Allow", "GET");
1371
+ res.end();
1372
+ return;
1373
+ }
1374
+ const exportId = match[1];
1375
+ const owner = this._findCommunityOwningExport(exportId);
1376
+ const community = owner?.community;
1377
+ let candidatePath;
1378
+ if (owner) {
1379
+ const { record } = owner;
1380
+ if (record.progress === 1 && record.url) try {
1381
+ const parsed = new URL(record.url);
1382
+ if (parsed.protocol === "file:") candidatePath = fileURLToPath(parsed);
1383
+ } catch {
1384
+ candidatePath = void 0;
1385
+ }
1386
+ } else if (this.pkc.dataPath) candidatePath = path.join(this.pkc.dataPath, "exports", `${exportId}.sqlite`);
1387
+ if (!candidatePath) {
1388
+ httpLog("GET /exports — unknown exportId", exportId);
1389
+ res.statusCode = 404;
1390
+ res.end();
1391
+ return;
1392
+ }
1393
+ const filePath = candidatePath;
1394
+ let size;
1395
+ try {
1396
+ size = statSync(filePath).size;
1397
+ } catch {
1398
+ res.statusCode = 404;
1399
+ res.end();
1400
+ return;
1401
+ }
1402
+ res.statusCode = 200;
1403
+ res.setHeader("Content-Type", "application/vnd.sqlite3");
1404
+ res.setHeader("Content-Length", String(size));
1405
+ const stream = createReadStream(filePath);
1406
+ let streamEnded = false;
1407
+ let streamErrored = false;
1408
+ stream.on("end", () => {
1409
+ streamEnded = true;
1410
+ });
1411
+ stream.on("error", (err) => {
1412
+ streamErrored = true;
1413
+ httpLog.error("Error streaming export file", filePath, err);
1414
+ if (!res.headersSent) res.statusCode = 500;
1415
+ res.end();
1416
+ });
1417
+ res.on("close", () => {
1418
+ if (streamErrored || !streamEnded || !res.writableEnded) return;
1419
+ if (community) community._deleteExport(exportId).catch((e) => httpLog.error("Failed to cleanup downloaded export", exportId, e));
1420
+ else promises.unlink(filePath).catch((e) => httpLog.error("Failed to cleanup downloaded export file", exportId, e));
1421
+ });
1422
+ stream.pipe(res);
1423
+ }
1424
+ async unsubscribe(params, connectionId) {
1425
+ const { subscriptionId } = parseRpcUnsubscribeParam(params[0]);
1426
+ log("Received unsubscribe", {
1427
+ connectionId,
1428
+ subscriptionId
1429
+ });
1430
+ const connectionCleanups = this.subscriptionCleanups[connectionId];
1431
+ if (!connectionCleanups || !connectionCleanups[subscriptionId]) return { success: true };
1432
+ await connectionCleanups[subscriptionId]();
1433
+ delete connectionCleanups[subscriptionId];
1434
+ return { success: true };
1435
+ }
1436
+ async destroy() {
1437
+ for (const connectionId of import_commonjs.keys.strict(this.subscriptionCleanups)) for (const subscriptionId of import_commonjs.keys.strict(this.subscriptionCleanups[connectionId])) await this.unsubscribe([{ subscriptionId: Number(subscriptionId) }], connectionId);
1438
+ this.ws.close();
1439
+ if (this._ownsHttpServer && this._httpServer) await new Promise((r) => this._httpServer.close(() => r()));
1440
+ await (await this._getPKCInstance()).destroy();
1441
+ for (const communityAddress of import_commonjs.keys.strict(this._startedCommunities)) delete this._startedCommunities[communityAddress];
1442
+ this._exportCommunityInstances.clear();
1443
+ this._rpcStateDb?.close();
1444
+ this._rpcStateDb = void 0;
1445
+ this._onSettingsChange = {};
1446
+ }
1447
+ };
1448
+ const createPKCWsServer = async (options) => {
1449
+ const parsedOptions = parseCreatePKCWsServerOptionsSchemaWithPKCErrorIfItFails(options);
1450
+ const pkcWss = new PKCWsServer({
1451
+ pkc: await PKCJs.PKC(parsedOptions.pkcOptions),
1452
+ port: parsedOptions.port,
1453
+ server: parsedOptions.server,
1454
+ authKey: parsedOptions.authKey,
1455
+ startStartedCommunitiesOnStartup: parsedOptions.startStartedCommunitiesOnStartup,
1456
+ autoStartConcurrency: parsedOptions.autoStartConcurrency,
1457
+ allowPrivateKeyExport: parsedOptions.allowPrivateKeyExport,
1458
+ exportFileMaxAgeMs: parsedOptions.exportFileMaxAgeMs
1459
+ });
1460
+ pkcWss._autoStartPreviousCommunities().catch((e) => {
1461
+ log.error("Failed to auto-start previous communities", e);
1462
+ });
1463
+ pkcWss._sweepOldExportFiles().catch((e) => {
1464
+ log.error("Failed to sweep old export files", e);
1465
+ });
1466
+ return pkcWss;
1467
+ };
1468
+ const PKCRpc = {
1469
+ PKCWsServer: createPKCWsServer,
1470
+ setPKCJs
1471
+ };
1472
+ //#endregion
1473
+ export { PKCRpc as default };