@ours.network/messenger-server 1.0.21 → 1.0.23
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/README.md +21 -6
- package/dist/chunks/{chunk-7KB5SIFJ.js → chunk-G4IVQ6I4.js} +1 -1
- package/dist/chunks/{server-QLWLPQHE.js → server-TX7WLV64.js} +408 -161
- package/dist/cli.js +2 -2
- package/dist/web/assets/{abnfDiagram-N423BO3Z-B6ZizsiF.js → abnfDiagram-N423BO3Z-DluufZNF.js} +2 -2
- package/dist/web/assets/{abnfDiagram-N423BO3Z-B6ZizsiF.js.map → abnfDiagram-N423BO3Z-DluufZNF.js.map} +1 -1
- package/dist/web/assets/{arc-5r9LRWcz.js → arc-CQ3ltbkj.js} +2 -2
- package/dist/web/assets/{arc-5r9LRWcz.js.map → arc-CQ3ltbkj.js.map} +1 -1
- package/dist/web/assets/{architectureDiagram-T3A2C74G-Cj4KI9n0.js → architectureDiagram-T3A2C74G-D_OLaJJP.js} +2 -2
- package/dist/web/assets/{architectureDiagram-T3A2C74G-Cj4KI9n0.js.map → architectureDiagram-T3A2C74G-D_OLaJJP.js.map} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-C-_bEmrE.js → blockDiagram-VBNYF7ZC-zWQJpXUI.js} +2 -2
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-C-_bEmrE.js.map → blockDiagram-VBNYF7ZC-zWQJpXUI.js.map} +1 -1
- package/dist/web/assets/{browser-PrseyNpO.js → browser-BtETXeFB.js} +2 -2
- package/dist/web/assets/{browser-PrseyNpO.js.map → browser-BtETXeFB.js.map} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-DE8rEM7l.js → c4Diagram-5PPSVZJV-Ccpd3-UB.js} +2 -2
- package/dist/web/assets/{c4Diagram-5PPSVZJV-DE8rEM7l.js.map → c4Diagram-5PPSVZJV-Ccpd3-UB.js.map} +1 -1
- package/dist/web/assets/channel-p16PN_NW.js +2 -0
- package/dist/web/assets/{channel-CP3HqW0T.js.map → channel-p16PN_NW.js.map} +1 -1
- package/dist/web/assets/{chunk-2GRJ4B5K-DXDdnzqB.js → chunk-2GRJ4B5K-BCgfd6Td.js} +2 -2
- package/dist/web/assets/{chunk-2GRJ4B5K-DXDdnzqB.js.map → chunk-2GRJ4B5K-BCgfd6Td.js.map} +1 -1
- package/dist/web/assets/{chunk-2Q5K7J3B-B2scPBcd.js → chunk-2Q5K7J3B-CQ48PhVz.js} +2 -2
- package/dist/web/assets/{chunk-2Q5K7J3B-B2scPBcd.js.map → chunk-2Q5K7J3B-CQ48PhVz.js.map} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-C8I-PBrV.js → chunk-5RXB4S5H-BTQ5wpp1.js} +2 -2
- package/dist/web/assets/{chunk-5RXB4S5H-C8I-PBrV.js.map → chunk-5RXB4S5H-BTQ5wpp1.js.map} +1 -1
- package/dist/web/assets/{chunk-5VM5RSS4-BXHXvnc8.js → chunk-5VM5RSS4-OmXNC4IB.js} +2 -2
- package/dist/web/assets/{chunk-5VM5RSS4-BXHXvnc8.js.map → chunk-5VM5RSS4-OmXNC4IB.js.map} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-08Ynset-.js → chunk-6Q2QTUOP-C8ooDwW4.js} +2 -2
- package/dist/web/assets/{chunk-6Q2QTUOP-08Ynset-.js.map → chunk-6Q2QTUOP-C8ooDwW4.js.map} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-Bb7CYAyC.js → chunk-GF5L2VYU-BsX1H_fv.js} +2 -2
- package/dist/web/assets/{chunk-GF5L2VYU-Bb7CYAyC.js.map → chunk-GF5L2VYU-BsX1H_fv.js.map} +1 -1
- package/dist/web/assets/{chunk-JWPE2WC7-DZWvONVd.js → chunk-JWPE2WC7-Df86oX4z.js} +2 -2
- package/dist/web/assets/{chunk-JWPE2WC7-DZWvONVd.js.map → chunk-JWPE2WC7-Df86oX4z.js.map} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-DLqf6yQb.js → chunk-KBJHAD2P-DHWP2W4_.js} +2 -2
- package/dist/web/assets/{chunk-KBJHAD2P-DLqf6yQb.js.map → chunk-KBJHAD2P-DHWP2W4_.js.map} +1 -1
- package/dist/web/assets/{chunk-RYQCIY6F-DTvnFCXh.js → chunk-RYQCIY6F-BxxxebdI.js} +2 -2
- package/dist/web/assets/{chunk-RYQCIY6F-DTvnFCXh.js.map → chunk-RYQCIY6F-BxxxebdI.js.map} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-C_kaUCOs.js → chunk-XXDRQBXY-VkYGSsGl.js} +2 -2
- package/dist/web/assets/{chunk-XXDRQBXY-C_kaUCOs.js.map → chunk-XXDRQBXY-VkYGSsGl.js.map} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-DniFmDq7.js +2 -0
- package/dist/web/assets/{classDiagram-JCYQIIEL-DPmFBQ5b.js.map → classDiagram-JCYQIIEL-DniFmDq7.js.map} +1 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-DniFmDq7.js +2 -0
- package/dist/web/assets/{classDiagram-v2-OCEON4UE-DPmFBQ5b.js.map → classDiagram-v2-OCEON4UE-DniFmDq7.js.map} +1 -1
- package/dist/web/assets/{cose-bilkent-JH36ORCC-GuA73T28.js → cose-bilkent-JH36ORCC-CwABkF0h.js} +2 -2
- package/dist/web/assets/{cose-bilkent-JH36ORCC-GuA73T28.js.map → cose-bilkent-JH36ORCC-CwABkF0h.js.map} +1 -1
- package/dist/web/assets/{cynefin-VYW2F7L2-ra9ViIw1.js → cynefin-VYW2F7L2-YRT0jK8r.js} +2 -2
- package/dist/web/assets/{cynefin-VYW2F7L2-ra9ViIw1.js.map → cynefin-VYW2F7L2-YRT0jK8r.js.map} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-SATlKd94.js → cynefinDiagram-MW4NZA55-HpASrz5x.js} +2 -2
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-SATlKd94.js.map → cynefinDiagram-MW4NZA55-HpASrz5x.js.map} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-VIOCx9FW.js → dagre-VZM6K2ZE-Bxqqu1Sj.js} +2 -2
- package/dist/web/assets/{dagre-VZM6K2ZE-VIOCx9FW.js.map → dagre-VZM6K2ZE-Bxqqu1Sj.js.map} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-CC5X5bh9.js → diagram-7IWD3JNH-CdOULw_j.js} +2 -2
- package/dist/web/assets/{diagram-7IWD3JNH-CC5X5bh9.js.map → diagram-7IWD3JNH-CdOULw_j.js.map} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-DiG4yo_i.js → diagram-B4RE2ZJO-BmAH_WZj.js} +2 -2
- package/dist/web/assets/{diagram-B4RE2ZJO-DiG4yo_i.js.map → diagram-B4RE2ZJO-BmAH_WZj.js.map} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-BlYoZBtY.js → diagram-LBJQPF4R-DhV2Gh-F.js} +2 -2
- package/dist/web/assets/{diagram-LBJQPF4R-BlYoZBtY.js.map → diagram-LBJQPF4R-DhV2Gh-F.js.map} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-CRFPxIVY.js → diagram-Q27KOJAE-C84KsWMY.js} +2 -2
- package/dist/web/assets/{diagram-Q27KOJAE-CRFPxIVY.js.map → diagram-Q27KOJAE-C84KsWMY.js.map} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-CZhtWIy0.js → diagram-UB23O5K3-DdedAaau.js} +2 -2
- package/dist/web/assets/{diagram-UB23O5K3-CZhtWIy0.js.map → diagram-UB23O5K3-DdedAaau.js.map} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-pY730TDb.js → ebnfDiagram-BXEA7PRR-DfNXtSHl.js} +2 -2
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-pY730TDb.js.map → ebnfDiagram-BXEA7PRR-DfNXtSHl.js.map} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-Bn32rfJy.js → erDiagram-JOGREHBK-DYuWNOEG.js} +2 -2
- package/dist/web/assets/{erDiagram-JOGREHBK-Bn32rfJy.js.map → erDiagram-JOGREHBK-DYuWNOEG.js.map} +1 -1
- package/dist/web/assets/{flowDiagram-UKHOOZJN-BvRucSDE.js → flowDiagram-UKHOOZJN-0vb0_3vx.js} +2 -2
- package/dist/web/assets/{flowDiagram-UKHOOZJN-BvRucSDE.js.map → flowDiagram-UKHOOZJN-0vb0_3vx.js.map} +1 -1
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-Nr1n-omI.js → ganttDiagram-PKOTCBZU-CK8tyfEC.js} +2 -2
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-Nr1n-omI.js.map → ganttDiagram-PKOTCBZU-CK8tyfEC.js.map} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-DynQOJUx.js → gitGraphDiagram-DS77QQ5N-DF6tegGR.js} +2 -2
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-DynQOJUx.js.map → gitGraphDiagram-DS77QQ5N-DF6tegGR.js.map} +1 -1
- package/dist/web/assets/index-C13P33S8.css +1 -0
- package/dist/web/assets/{index-DcBXzDIU.js → index-CaFh5gEt.js} +14 -14
- package/dist/web/assets/index-CaFh5gEt.js.map +1 -0
- package/dist/web/assets/{infoDiagram-6WML65LV-BlEoz4uC.js → infoDiagram-6WML65LV-CGrFzjRa.js} +2 -2
- package/dist/web/assets/{infoDiagram-6WML65LV-BlEoz4uC.js.map → infoDiagram-6WML65LV-CGrFzjRa.js.map} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-D7i55-jD.js → ishikawaDiagram-WSZJBQD7-CEo4pf_7.js} +2 -2
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-D7i55-jD.js.map → ishikawaDiagram-WSZJBQD7-CEo4pf_7.js.map} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-Ucz04wjf.js → journeyDiagram-NVQOT4AX-CspNWhHp.js} +2 -2
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-Ucz04wjf.js.map → journeyDiagram-NVQOT4AX-CspNWhHp.js.map} +1 -1
- package/dist/web/assets/{jsQR-BwlCAqNO.js → jsQR-DcKyKt8x.js} +2 -2
- package/dist/web/assets/{jsQR-BwlCAqNO.js.map → jsQR-DcKyKt8x.js.map} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DWsE9cHO.js → kanban-definition-27J2QSJJ-V17PR9V-.js} +2 -2
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DWsE9cHO.js.map → kanban-definition-27J2QSJJ-V17PR9V-.js.map} +1 -1
- package/dist/web/assets/{linear-C5Sn5WRj.js → linear-DJQlR9NW.js} +2 -2
- package/dist/web/assets/{linear-C5Sn5WRj.js.map → linear-DJQlR9NW.js.map} +1 -1
- package/dist/web/assets/{mermaid.core-fsJdVgy7.js → mermaid.core-BJxvfP_g.js} +5 -5
- package/dist/web/assets/{mermaid.core-fsJdVgy7.js.map → mermaid.core-BJxvfP_g.js.map} +1 -1
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-DaGgRc4d.js → mindmap-definition-FAOFIHXS-CRdQH63m.js} +2 -2
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-DaGgRc4d.js.map → mindmap-definition-FAOFIHXS-CRdQH63m.js.map} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-D8yGmXPL.js → pegDiagram-VL7TDLO6-Ba0KPauI.js} +2 -2
- package/dist/web/assets/{pegDiagram-VL7TDLO6-D8yGmXPL.js.map → pegDiagram-VL7TDLO6-Ba0KPauI.js.map} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-DwH7tSjj.js → pieDiagram-7S7Q4E2Y-B923JQAs.js} +2 -2
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-DwH7tSjj.js.map → pieDiagram-7S7Q4E2Y-B923JQAs.js.map} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-ChEMrl7C.js → quadrantDiagram-CIZ2JOQS-CRgKLy79.js} +2 -2
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-ChEMrl7C.js.map → quadrantDiagram-CIZ2JOQS-CRgKLy79.js.map} +1 -1
- package/dist/web/assets/{railroadDiagram-AXF67PYL-BEovXgwU.js → railroadDiagram-AXF67PYL-ox905hPN.js} +2 -2
- package/dist/web/assets/{railroadDiagram-AXF67PYL-BEovXgwU.js.map → railroadDiagram-AXF67PYL-ox905hPN.js.map} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-CmP1vUmt.js → requirementDiagram-LRYGKXZP-Duq__sqN.js} +2 -2
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-CmP1vUmt.js.map → requirementDiagram-LRYGKXZP-Duq__sqN.js.map} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-DxUz8yN6.js → sankeyDiagram-W5VNT64P-wHwQYnBB.js} +2 -2
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-DxUz8yN6.js.map → sankeyDiagram-W5VNT64P-wHwQYnBB.js.map} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-d4jgy5oa.js → sequenceDiagram-SI44F4Z6-DxIJk57u.js} +2 -2
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-d4jgy5oa.js.map → sequenceDiagram-SI44F4Z6-DxIJk57u.js.map} +1 -1
- package/dist/web/assets/{sizeCapture-X5ZJPWSS-HXVh3XKR.js → sizeCapture-X5ZJPWSS-DTR7sbJ5.js} +2 -2
- package/dist/web/assets/{sizeCapture-X5ZJPWSS-HXVh3XKR.js.map → sizeCapture-X5ZJPWSS-DTR7sbJ5.js.map} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-qUFT-p4T.js → stateDiagram-OKZ733FA-BVFtM2sL.js} +2 -2
- package/dist/web/assets/{stateDiagram-OKZ733FA-qUFT-p4T.js.map → stateDiagram-OKZ733FA-BVFtM2sL.js.map} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-cPQcuNzH.js +2 -0
- package/dist/web/assets/{stateDiagram-v2-UEYNNEHI-hxKmEhcU.js.map → stateDiagram-v2-UEYNNEHI-cPQcuNzH.js.map} +1 -1
- package/dist/web/assets/{swimlanes-SLNWSIFB-BqG_sSp3.js → swimlanes-SLNWSIFB-CD9G4YCZ.js} +3 -3
- package/dist/web/assets/{swimlanes-SLNWSIFB-BqG_sSp3.js.map → swimlanes-SLNWSIFB-CD9G4YCZ.js.map} +1 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CyR-kDwU.js +9 -0
- package/dist/web/assets/{swimlanesDiagram-ULZ7WXOC-DXjdKK-f.js.map → swimlanesDiagram-ULZ7WXOC-CyR-kDwU.js.map} +1 -1
- package/dist/web/assets/{timeline-definition-Z64GVDOM-Cv2XcEyA.js → timeline-definition-Z64GVDOM-Dvedkrz5.js} +2 -2
- package/dist/web/assets/{timeline-definition-Z64GVDOM-Cv2XcEyA.js.map → timeline-definition-Z64GVDOM-Dvedkrz5.js.map} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-CfDqT27B.js → vennDiagram-T6HMQDX7-D5CnNWtW.js} +2 -2
- package/dist/web/assets/{vennDiagram-T6HMQDX7-CfDqT27B.js.map → vennDiagram-T6HMQDX7-D5CnNWtW.js.map} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-B1NVAn0o.js → wardleyDiagram-T6FBY63Y-n746vsfx.js} +2 -2
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-B1NVAn0o.js.map → wardleyDiagram-T6FBY63Y-n746vsfx.js.map} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-B1-HStGS.js → xychartDiagram-ELKLHX3M-zEDnFO9e.js} +2 -2
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-B1-HStGS.js.map → xychartDiagram-ELKLHX3M-zEDnFO9e.js.map} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/dist/web/version.json +1 -1
- package/package.json +2 -2
- package/dist/web/assets/channel-CP3HqW0T.js +0 -2
- package/dist/web/assets/classDiagram-JCYQIIEL-DPmFBQ5b.js +0 -2
- package/dist/web/assets/classDiagram-v2-OCEON4UE-DPmFBQ5b.js +0 -2
- package/dist/web/assets/index-DcBXzDIU.js.map +0 -1
- package/dist/web/assets/index-DcCHCB0s.css +0 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-hxKmEhcU.js +0 -2
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DXjdKK-f.js +0 -9
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
publicEngineError,
|
|
8
8
|
publicInternalError,
|
|
9
9
|
reportFailure
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-G4IVQ6I4.js";
|
|
11
11
|
|
|
12
12
|
// node_modules/bn.js/lib/bn.js
|
|
13
13
|
var require_bn = __commonJS({
|
|
@@ -9676,7 +9676,7 @@ var require_websocket = __commonJS({
|
|
|
9676
9676
|
var http = __require("http");
|
|
9677
9677
|
var net = __require("net");
|
|
9678
9678
|
var tls = __require("tls");
|
|
9679
|
-
var { randomBytes: randomBytes2, createHash:
|
|
9679
|
+
var { randomBytes: randomBytes2, createHash: createHash5 } = __require("crypto");
|
|
9680
9680
|
var { Duplex, Readable } = __require("stream");
|
|
9681
9681
|
var { URL: URL2 } = __require("url");
|
|
9682
9682
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -10344,7 +10344,7 @@ var require_websocket = __commonJS({
|
|
|
10344
10344
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
10345
10345
|
return;
|
|
10346
10346
|
}
|
|
10347
|
-
const digest =
|
|
10347
|
+
const digest = createHash5("sha1").update(key + GUID).digest("base64");
|
|
10348
10348
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
10349
10349
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
10350
10350
|
return;
|
|
@@ -10716,7 +10716,7 @@ var require_websocket_server = __commonJS({
|
|
|
10716
10716
|
var EventEmitter = __require("events");
|
|
10717
10717
|
var http = __require("http");
|
|
10718
10718
|
var { Duplex } = __require("stream");
|
|
10719
|
-
var { createHash:
|
|
10719
|
+
var { createHash: createHash5 } = __require("crypto");
|
|
10720
10720
|
var extension2 = require_extension();
|
|
10721
10721
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
10722
10722
|
var subprotocol2 = require_subprotocol();
|
|
@@ -11023,7 +11023,7 @@ var require_websocket_server = __commonJS({
|
|
|
11023
11023
|
);
|
|
11024
11024
|
}
|
|
11025
11025
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
11026
|
-
const digest =
|
|
11026
|
+
const digest = createHash5("sha1").update(key + GUID).digest("base64");
|
|
11027
11027
|
const headers = [
|
|
11028
11028
|
"HTTP/1.1 101 Switching Protocols",
|
|
11029
11029
|
"Upgrade: websocket",
|
|
@@ -20621,11 +20621,24 @@ var ROUTES = {
|
|
|
20621
20621
|
const current = await within(client.currentIdentity(), deps.healthTimeoutMs ?? 1500);
|
|
20622
20622
|
const identity = publicIdentity(current);
|
|
20623
20623
|
if (identity.cid !== deps.identityCid) throw new Error("bound identity mismatch");
|
|
20624
|
+
const pushQueue = deps.push.queueStats();
|
|
20625
|
+
if (pushQueue.healthy === false) {
|
|
20626
|
+
sendJson(res, 503, {
|
|
20627
|
+
status: "degraded",
|
|
20628
|
+
message: "Push queue saturated",
|
|
20629
|
+
version: deps.buildInfo.version,
|
|
20630
|
+
sha: deps.buildInfo.sha,
|
|
20631
|
+
identityCid: deps.identityCid,
|
|
20632
|
+
pushQueue
|
|
20633
|
+
});
|
|
20634
|
+
return void 0;
|
|
20635
|
+
}
|
|
20624
20636
|
sendJson(res, 200, {
|
|
20625
20637
|
status: "ok",
|
|
20626
20638
|
version: deps.buildInfo.version,
|
|
20627
20639
|
sha: deps.buildInfo.sha,
|
|
20628
|
-
identityCid: deps.identityCid
|
|
20640
|
+
identityCid: deps.identityCid,
|
|
20641
|
+
pushQueue
|
|
20629
20642
|
});
|
|
20630
20643
|
} catch {
|
|
20631
20644
|
sendJson(res, 503, unavailable);
|
|
@@ -21195,6 +21208,34 @@ var require9 = createRequire8(import.meta.url);
|
|
|
21195
21208
|
var require10 = createRequire9(import.meta.url);
|
|
21196
21209
|
|
|
21197
21210
|
// src/daemon.ts
|
|
21211
|
+
function daemonNotificationReader(leaseToken) {
|
|
21212
|
+
let selected;
|
|
21213
|
+
return async (identity, since, signal) => {
|
|
21214
|
+
selected ??= _2();
|
|
21215
|
+
const url = `${selected.baseUrl.value}/identities/${encodeURIComponent(identity)}/notifications?since=${encodeURIComponent(String(since))}&kinds=inbound`;
|
|
21216
|
+
const response = await fetch(url, {
|
|
21217
|
+
headers: {
|
|
21218
|
+
"x-ours-lease-token": leaseToken,
|
|
21219
|
+
"x-ours-client-pid": String(process.pid),
|
|
21220
|
+
...selected.token ? { "x-ours-api-token": selected.token.value } : {}
|
|
21221
|
+
},
|
|
21222
|
+
signal
|
|
21223
|
+
});
|
|
21224
|
+
let body;
|
|
21225
|
+
try {
|
|
21226
|
+
body = await response.json();
|
|
21227
|
+
} catch {
|
|
21228
|
+
throw new Error(`daemon notification page returned HTTP ${response.status} with invalid JSON`);
|
|
21229
|
+
}
|
|
21230
|
+
if (!response.ok) throw new Error(`daemon notification page returned HTTP ${response.status}`);
|
|
21231
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) throw new Error("daemon notification page is malformed");
|
|
21232
|
+
const page = body;
|
|
21233
|
+
if (!Number.isSafeInteger(page.cursor) || page.cursor < 0 || !Array.isArray(page.events)) {
|
|
21234
|
+
throw new Error("daemon notification page is malformed");
|
|
21235
|
+
}
|
|
21236
|
+
return { cursor: page.cursor, events: page.events };
|
|
21237
|
+
};
|
|
21238
|
+
}
|
|
21198
21239
|
function hasCode(error, code) {
|
|
21199
21240
|
return error instanceof Error && "code" in error && error.code === code;
|
|
21200
21241
|
}
|
|
@@ -21219,6 +21260,7 @@ async function startRuntime(_cfg, buildInfo, attach = j) {
|
|
|
21219
21260
|
apiVisibility: "daemon-configured",
|
|
21220
21261
|
mcp: false
|
|
21221
21262
|
}),
|
|
21263
|
+
readNotificationPage: daemonNotificationReader(leaseToken),
|
|
21222
21264
|
async close() {
|
|
21223
21265
|
if (closed) return;
|
|
21224
21266
|
closed = true;
|
|
@@ -21247,10 +21289,23 @@ import { dirname as dirname2, join as join2 } from "node:path";
|
|
|
21247
21289
|
var MAX_BINDINGS = 32;
|
|
21248
21290
|
var MAX_ENDPOINT_LENGTH = 2048;
|
|
21249
21291
|
var MAX_LABEL_LENGTH = 160;
|
|
21250
|
-
var
|
|
21292
|
+
var MAX_RECORDS_PER_IDENTITY = 4096;
|
|
21293
|
+
var MAX_TOMBSTONES_PER_IDENTITY = 256;
|
|
21251
21294
|
var JOB_EXPIRY_MS = 24 * 60 * 60 * 1e3;
|
|
21252
|
-
var
|
|
21295
|
+
var TOMBSTONE_RETENTION_MS = 15 * 60 * 1e3;
|
|
21253
21296
|
var MAX_ATTEMPTS = 6;
|
|
21297
|
+
var emptyMetrics = () => ({
|
|
21298
|
+
admitted: 0,
|
|
21299
|
+
delivered: 0,
|
|
21300
|
+
suppressed: 0,
|
|
21301
|
+
retries: 0,
|
|
21302
|
+
dropped: 0,
|
|
21303
|
+
permanentFailures: 0,
|
|
21304
|
+
prunedBindings: 0,
|
|
21305
|
+
admissionFailures: 0,
|
|
21306
|
+
tombstonesPruned: 0,
|
|
21307
|
+
migratedTerminal: 0
|
|
21308
|
+
});
|
|
21254
21309
|
function nonEmpty2(value, max = Number.MAX_SAFE_INTEGER) {
|
|
21255
21310
|
return typeof value === "string" && value.length > 0 && value.length <= max;
|
|
21256
21311
|
}
|
|
@@ -21298,10 +21353,44 @@ function validBinding(value) {
|
|
|
21298
21353
|
}
|
|
21299
21354
|
return nonEmpty2(row.bindingId, 128) && nonEmpty2(row.createdAt, 64) && nonEmpty2(row.updatedAt, 64) && nonEmpty2(row.vapidFingerprint, 128) && Number.isSafeInteger(row.configEpoch) && row.configEpoch > 0 && (row.label === void 0 || nonEmpty2(row.label, MAX_LABEL_LENGTH));
|
|
21300
21355
|
}
|
|
21356
|
+
function validJobBase(row) {
|
|
21357
|
+
return nonEmpty2(row.id, 2048) && nonEmpty2(row.identityCid, 512) && nonEmpty2(row.wireId, 1024) && (row.kind === "message" || row.kind === "file" || row.kind === "photo" || row.kind === "voice") && nonEmpty2(row.contactId, 512) && (row.senderName === void 0 || nonEmpty2(row.senderName, 512)) && Number.isFinite(row.createdAt) && Number.isFinite(row.expiresAt) && Number.isSafeInteger(row.attempts) && row.attempts >= 0 && Number.isFinite(row.nextAttemptAt) && Array.isArray(row.targetBindingIds) && row.targetBindingIds.length <= MAX_BINDINGS && row.targetBindingIds.every((id) => nonEmpty2(id, 128)) && Array.isArray(row.deliveredBindingIds) && row.deliveredBindingIds.length <= MAX_BINDINGS && row.deliveredBindingIds.every((id) => nonEmpty2(id, 128)) && Number.isSafeInteger(row.sentCount) && row.sentCount >= 0;
|
|
21358
|
+
}
|
|
21301
21359
|
function validJob(value) {
|
|
21302
21360
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21303
21361
|
const row = value;
|
|
21304
|
-
return
|
|
21362
|
+
return validJobBase(row) && (row.status === "pending" || row.status === "in_flight") && (row.inFlightAt === void 0 || Number.isFinite(row.inFlightAt));
|
|
21363
|
+
}
|
|
21364
|
+
function validLegacyJob(value) {
|
|
21365
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21366
|
+
const row = value;
|
|
21367
|
+
return validJobBase(row) && (row.status === "pending" || row.status === "sent" || row.status === "dropped") && (row.completedAt === void 0 || Number.isFinite(row.completedAt));
|
|
21368
|
+
}
|
|
21369
|
+
function validTombstone(value) {
|
|
21370
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21371
|
+
const row = value;
|
|
21372
|
+
return nonEmpty2(row.id, 2048) && Number.isFinite(row.completedAt) && Number.isFinite(row.expiresAt);
|
|
21373
|
+
}
|
|
21374
|
+
function validMetrics(value) {
|
|
21375
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21376
|
+
const row = value;
|
|
21377
|
+
return [
|
|
21378
|
+
"admitted",
|
|
21379
|
+
"delivered",
|
|
21380
|
+
"suppressed",
|
|
21381
|
+
"retries",
|
|
21382
|
+
"dropped",
|
|
21383
|
+
"permanentFailures",
|
|
21384
|
+
"prunedBindings",
|
|
21385
|
+
"admissionFailures",
|
|
21386
|
+
"tombstonesPruned",
|
|
21387
|
+
"migratedTerminal"
|
|
21388
|
+
].every((key) => Number.isSafeInteger(row[key]) && Number(row[key]) >= 0) && (row.lastSaturationAt === void 0 || Number.isFinite(row.lastSaturationAt));
|
|
21389
|
+
}
|
|
21390
|
+
function validPageCheckpoint(value) {
|
|
21391
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21392
|
+
const row = value;
|
|
21393
|
+
return Number.isSafeInteger(row.fromCursor) && row.fromCursor >= 0 && Number.isSafeInteger(row.cursor) && row.cursor >= row.fromCursor && Number.isSafeInteger(row.eventCount) && row.eventCount >= 0 && Number.isSafeInteger(row.nextIndex) && row.nextIndex >= 0 && row.nextIndex <= row.eventCount && nonEmpty2(row.digest, 128) && /^[A-Za-z0-9_-]+$/.test(row.digest);
|
|
21305
21394
|
}
|
|
21306
21395
|
function validVapid(value) {
|
|
21307
21396
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -21316,11 +21405,17 @@ function validVapid(value) {
|
|
|
21316
21405
|
return false;
|
|
21317
21406
|
}
|
|
21318
21407
|
}
|
|
21408
|
+
function validV3(value) {
|
|
21409
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21410
|
+
const state = value;
|
|
21411
|
+
if (state.version !== 3 || !validVapid(state.vapid) || !nonEmpty2(state.vapidFingerprint, 128) || !Number.isSafeInteger(state.configEpoch) || state.configEpoch < 1 || !state.identities || typeof state.identities !== "object" || Array.isArray(state.identities)) return false;
|
|
21412
|
+
return Object.entries(state.identities).every(([cid, identity]) => nonEmpty2(cid, 512) && !!identity && typeof identity === "object" && Array.isArray(identity.bindings) && identity.bindings.length <= MAX_BINDINGS && identity.bindings.every(validBinding) && Array.isArray(identity.jobs) && Array.isArray(identity.tombstones) && identity.jobs.length + identity.tombstones.length <= MAX_RECORDS_PER_IDENTITY && identity.tombstones.length <= MAX_TOMBSTONES_PER_IDENTITY && identity.jobs.every((job) => validJob(job) && job.identityCid === cid) && identity.tombstones.every(validTombstone) && (identity.notificationCursor === null || Number.isSafeInteger(identity.notificationCursor) && identity.notificationCursor >= 0) && (identity.notificationPage === void 0 || validPageCheckpoint(identity.notificationPage) && identity.notificationCursor !== null && identity.notificationPage.fromCursor === identity.notificationCursor) && validMetrics(identity.metrics) && typeof identity.saturated === "boolean");
|
|
21413
|
+
}
|
|
21319
21414
|
function validV2(value) {
|
|
21320
21415
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21321
21416
|
const state = value;
|
|
21322
21417
|
if (state.version !== 2 || !validVapid(state.vapid) || !nonEmpty2(state.vapidFingerprint, 128) || !Number.isSafeInteger(state.configEpoch) || state.configEpoch < 1 || !state.identities || typeof state.identities !== "object" || Array.isArray(state.identities)) return false;
|
|
21323
|
-
return Object.entries(state.identities).every(([cid, identity]) => nonEmpty2(cid, 512) && !!identity && typeof identity === "object" && Array.isArray(identity.bindings) && identity.bindings.length <= MAX_BINDINGS && identity.bindings.every(validBinding) && Array.isArray(identity.jobs) && identity.jobs.length <=
|
|
21418
|
+
return Object.entries(state.identities).every(([cid, identity]) => nonEmpty2(cid, 512) && !!identity && typeof identity === "object" && Array.isArray(identity.bindings) && identity.bindings.length <= MAX_BINDINGS && identity.bindings.every(validBinding) && Array.isArray(identity.jobs) && identity.jobs.length <= MAX_RECORDS_PER_IDENTITY && identity.jobs.every((job) => validLegacyJob(job) && job.identityCid === cid));
|
|
21324
21419
|
}
|
|
21325
21420
|
function validLegacy(value) {
|
|
21326
21421
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -21341,13 +21436,22 @@ function preservedStateError(file, reason) {
|
|
|
21341
21436
|
`Existing push state ${file} is ${reason}; it was left unchanged. Restore valid push.json contents, or move the file aside explicitly before restarting.`
|
|
21342
21437
|
);
|
|
21343
21438
|
}
|
|
21439
|
+
function newIdentityState() {
|
|
21440
|
+
return { bindings: [], jobs: [], tombstones: [], notificationCursor: null, metrics: emptyMetrics(), saturated: false };
|
|
21441
|
+
}
|
|
21344
21442
|
function identityState(state, cid) {
|
|
21345
|
-
return state.identities[cid] ??=
|
|
21443
|
+
return state.identities[cid] ??= newIdentityState();
|
|
21346
21444
|
}
|
|
21347
21445
|
function privateEvent(event) {
|
|
21348
21446
|
const body = event.kind === "message" ? "New message" : event.kind === "voice" ? "New voice message" : event.kind === "photo" ? "New photo" : "New file";
|
|
21349
21447
|
return { ...event, title: "ours messenger", body };
|
|
21350
21448
|
}
|
|
21449
|
+
function invalidDeviceToken(error) {
|
|
21450
|
+
if (!error || typeof error !== "object") return false;
|
|
21451
|
+
const raw = error.reason ?? error.body;
|
|
21452
|
+
const body = typeof raw === "string" ? raw.slice(0, 2048) : Buffer.isBuffer(raw) ? raw.subarray(0, 2048).toString("utf8") : "";
|
|
21453
|
+
return /\b(?:BadDeviceToken|DeviceTokenNotForTopic|Unregistered)\b/.test(body);
|
|
21454
|
+
}
|
|
21351
21455
|
var LEGACY_LOCAL_VAPID_SUBJECT = "mailto:admin@localhost";
|
|
21352
21456
|
var DEFAULT_VAPID_SUBJECT = "https://ours.network";
|
|
21353
21457
|
function defaultVapidSubject(env) {
|
|
@@ -21389,7 +21493,7 @@ var PushStore = class _PushStore {
|
|
|
21389
21493
|
} catch (error) {
|
|
21390
21494
|
throw preservedStateError(file, `corrupt or unreadable (${error instanceof Error ? error.message : String(error)})`);
|
|
21391
21495
|
}
|
|
21392
|
-
if (validV2(value) || validLegacy(value)) parsed = value;
|
|
21496
|
+
if (validV3(value) || validV2(value) || validLegacy(value)) parsed = value;
|
|
21393
21497
|
else throw preservedStateError(file, "schema-invalid");
|
|
21394
21498
|
} catch (error) {
|
|
21395
21499
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -21407,7 +21511,7 @@ var PushStore = class _PushStore {
|
|
|
21407
21511
|
}
|
|
21408
21512
|
const selectedFingerprint = fingerprint(selected.publicKey);
|
|
21409
21513
|
let state;
|
|
21410
|
-
if (parsed &&
|
|
21514
|
+
if (parsed && validV3(parsed)) {
|
|
21411
21515
|
const rotated = parsed.vapidFingerprint !== selectedFingerprint;
|
|
21412
21516
|
state = {
|
|
21413
21517
|
...parsed,
|
|
@@ -21415,16 +21519,53 @@ var PushStore = class _PushStore {
|
|
|
21415
21519
|
vapidFingerprint: selectedFingerprint,
|
|
21416
21520
|
configEpoch: rotated ? parsed.configEpoch + 1 : parsed.configEpoch
|
|
21417
21521
|
};
|
|
21522
|
+
} else if (parsed && validV2(parsed)) {
|
|
21523
|
+
const rotated = parsed.vapidFingerprint !== selectedFingerprint;
|
|
21524
|
+
const now = Date.now();
|
|
21525
|
+
state = {
|
|
21526
|
+
version: 3,
|
|
21527
|
+
vapid: selected,
|
|
21528
|
+
vapidFingerprint: selectedFingerprint,
|
|
21529
|
+
configEpoch: rotated ? parsed.configEpoch + 1 : parsed.configEpoch,
|
|
21530
|
+
identities: Object.fromEntries(Object.entries(parsed.identities).map(([cid, old]) => {
|
|
21531
|
+
const pending = old.jobs.filter((row) => row.status === "pending").map((row) => ({
|
|
21532
|
+
id: row.id,
|
|
21533
|
+
identityCid: row.identityCid,
|
|
21534
|
+
wireId: row.wireId,
|
|
21535
|
+
kind: row.kind,
|
|
21536
|
+
contactId: row.contactId,
|
|
21537
|
+
senderName: row.senderName,
|
|
21538
|
+
createdAt: row.createdAt,
|
|
21539
|
+
expiresAt: row.expiresAt,
|
|
21540
|
+
attempts: row.attempts,
|
|
21541
|
+
nextAttemptAt: row.nextAttemptAt,
|
|
21542
|
+
targetBindingIds: [...row.targetBindingIds],
|
|
21543
|
+
deliveredBindingIds: [...row.deliveredBindingIds],
|
|
21544
|
+
status: "pending",
|
|
21545
|
+
sentCount: row.sentCount
|
|
21546
|
+
}));
|
|
21547
|
+
const terminal = old.jobs.filter((row) => row.status !== "pending");
|
|
21548
|
+
const tombstones = terminal.filter((row) => row.completedAt !== void 0 && now - row.completedAt <= TOMBSTONE_RETENTION_MS).sort((a4, b3) => (b3.completedAt ?? 0) - (a4.completedAt ?? 0)).slice(0, MAX_TOMBSTONES_PER_IDENTITY).map((row) => ({ id: row.id, completedAt: row.completedAt, expiresAt: row.completedAt + TOMBSTONE_RETENTION_MS }));
|
|
21549
|
+
const metrics = emptyMetrics();
|
|
21550
|
+
metrics.admitted = old.jobs.length;
|
|
21551
|
+
metrics.delivered = terminal.reduce((sum, row) => sum + (row.status === "sent" ? row.sentCount : 0), 0);
|
|
21552
|
+
metrics.dropped = terminal.filter((row) => row.status === "dropped").length;
|
|
21553
|
+
metrics.migratedTerminal = terminal.length;
|
|
21554
|
+
metrics.tombstonesPruned = terminal.length - tombstones.length;
|
|
21555
|
+
return [cid, { bindings: old.bindings, jobs: pending, tombstones, notificationCursor: null, metrics, saturated: false }];
|
|
21556
|
+
}))
|
|
21557
|
+
};
|
|
21418
21558
|
} else {
|
|
21419
21559
|
const oldFingerprint = parsed ? fingerprint(parsed.vapid.publicKey) : selectedFingerprint;
|
|
21420
21560
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21421
21561
|
state = {
|
|
21422
|
-
version:
|
|
21562
|
+
version: 3,
|
|
21423
21563
|
vapid: selected,
|
|
21424
21564
|
vapidFingerprint: selectedFingerprint,
|
|
21425
21565
|
configEpoch: 1,
|
|
21426
21566
|
identities: {
|
|
21427
21567
|
[identityCid]: {
|
|
21568
|
+
...newIdentityState(),
|
|
21428
21569
|
bindings: (parsed?.subscriptions ?? []).map((row) => ({
|
|
21429
21570
|
bindingId: randomUUID(),
|
|
21430
21571
|
endpoint: row.endpoint,
|
|
@@ -21435,14 +21576,13 @@ var PushStore = class _PushStore {
|
|
|
21435
21576
|
vapidFingerprint: oldFingerprint,
|
|
21436
21577
|
configEpoch: 1,
|
|
21437
21578
|
preview: "full"
|
|
21438
|
-
}))
|
|
21439
|
-
jobs: []
|
|
21579
|
+
}))
|
|
21440
21580
|
}
|
|
21441
21581
|
}
|
|
21442
21582
|
};
|
|
21443
21583
|
}
|
|
21444
21584
|
const store = new _PushStore(file, identityCid, state, deps);
|
|
21445
|
-
store.
|
|
21585
|
+
store.recoverAndPrune(Date.now());
|
|
21446
21586
|
store.persist();
|
|
21447
21587
|
return store;
|
|
21448
21588
|
}
|
|
@@ -21534,17 +21674,90 @@ var PushStore = class _PushStore {
|
|
|
21534
21674
|
if (removed) this.persist();
|
|
21535
21675
|
return removed;
|
|
21536
21676
|
}
|
|
21537
|
-
|
|
21677
|
+
get notificationCursor() {
|
|
21678
|
+
return identityState(this.state, this.identityCid).notificationCursor;
|
|
21679
|
+
}
|
|
21680
|
+
commitNotificationCursor(cursor) {
|
|
21681
|
+
if (!Number.isSafeInteger(cursor) || cursor < 0) throw new Error("push notification cursor must be a non-negative safe integer");
|
|
21682
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21683
|
+
if (owner.notificationCursor === cursor && owner.notificationPage === void 0) return;
|
|
21684
|
+
owner.notificationCursor = cursor;
|
|
21685
|
+
delete owner.notificationPage;
|
|
21686
|
+
this.persist();
|
|
21687
|
+
}
|
|
21688
|
+
get notificationPageProgress() {
|
|
21689
|
+
const page = identityState(this.state, this.identityCid).notificationPage;
|
|
21690
|
+
return page ? { ...page } : null;
|
|
21691
|
+
}
|
|
21692
|
+
beginNotificationPage(input) {
|
|
21693
|
+
if (!validPageCheckpoint({ ...input, nextIndex: 0 })) throw new Error("push notification page checkpoint is invalid");
|
|
21694
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21695
|
+
if (owner.notificationCursor !== input.fromCursor) {
|
|
21696
|
+
throw new Error("push notification page does not start at the committed cursor");
|
|
21697
|
+
}
|
|
21698
|
+
if (owner.notificationPage) return { ...owner.notificationPage };
|
|
21699
|
+
owner.notificationPage = { ...input, nextIndex: 0 };
|
|
21700
|
+
this.persist();
|
|
21701
|
+
return { ...owner.notificationPage };
|
|
21702
|
+
}
|
|
21703
|
+
advanceNotificationPage(nextIndex, durable = false) {
|
|
21704
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21705
|
+
const page = owner.notificationPage;
|
|
21706
|
+
if (!page || !Number.isSafeInteger(nextIndex) || nextIndex !== page.nextIndex + 1 || nextIndex > page.eventCount) {
|
|
21707
|
+
throw new Error("push notification page progress is invalid");
|
|
21708
|
+
}
|
|
21709
|
+
page.nextIndex = nextIndex;
|
|
21710
|
+
if (durable) this.persist();
|
|
21711
|
+
}
|
|
21712
|
+
checkpointNotificationPage() {
|
|
21713
|
+
if (!identityState(this.state, this.identityCid).notificationPage) {
|
|
21714
|
+
throw new Error("push notification page checkpoint is missing");
|
|
21715
|
+
}
|
|
21716
|
+
this.persist();
|
|
21717
|
+
}
|
|
21718
|
+
commitNotificationPage() {
|
|
21719
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21720
|
+
const page = owner.notificationPage;
|
|
21721
|
+
if (!page || page.nextIndex !== page.eventCount) throw new Error("push notification page is incomplete");
|
|
21722
|
+
owner.notificationCursor = page.cursor;
|
|
21723
|
+
delete owner.notificationPage;
|
|
21724
|
+
owner.saturated = false;
|
|
21725
|
+
this.persist();
|
|
21726
|
+
}
|
|
21727
|
+
admitJob(input, now = Date.now(), notificationPageNextIndex) {
|
|
21538
21728
|
if (!nonEmpty2(input.wireId, 1024) || !nonEmpty2(input.contactId, 512) || input.senderName !== void 0 && !nonEmpty2(input.senderName, 512)) {
|
|
21539
21729
|
throw new Error("push job correlation metadata is invalid or too long");
|
|
21540
21730
|
}
|
|
21541
21731
|
const targets = this.list().map((row) => row.bindingId);
|
|
21542
|
-
if (targets.length === 0) return
|
|
21732
|
+
if (targets.length === 0) return { status: "no_targets" };
|
|
21543
21733
|
const id = `${this.identityCid}:${input.wireId}:${input.kind}`;
|
|
21544
21734
|
const owner = identityState(this.state, this.identityCid);
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21735
|
+
const checkpointAdmission = () => {
|
|
21736
|
+
if (notificationPageNextIndex === void 0) return false;
|
|
21737
|
+
const page = owner.notificationPage;
|
|
21738
|
+
if (!page || notificationPageNextIndex !== page.nextIndex + 1 || notificationPageNextIndex > page.eventCount) {
|
|
21739
|
+
throw new Error("push notification page admission checkpoint is invalid");
|
|
21740
|
+
}
|
|
21741
|
+
page.nextIndex = notificationPageNextIndex;
|
|
21742
|
+
return true;
|
|
21743
|
+
};
|
|
21744
|
+
this.pruneTombstones(owner, now);
|
|
21745
|
+
if (owner.jobs.some((job) => job.id === id) || owner.tombstones.some((row) => row.id === id)) {
|
|
21746
|
+
const checkpointed2 = checkpointAdmission();
|
|
21747
|
+
if (checkpointed2) this.persist();
|
|
21748
|
+
return { status: "duplicate", jobId: id, ...checkpointed2 ? { checkpointed: true } : {} };
|
|
21749
|
+
}
|
|
21750
|
+
while (owner.jobs.length + owner.tombstones.length >= MAX_RECORDS_PER_IDENTITY && owner.tombstones.length > 0) {
|
|
21751
|
+
owner.tombstones.shift();
|
|
21752
|
+
owner.metrics.tombstonesPruned++;
|
|
21753
|
+
}
|
|
21754
|
+
if (owner.jobs.length + owner.tombstones.length >= MAX_RECORDS_PER_IDENTITY) {
|
|
21755
|
+
owner.saturated = true;
|
|
21756
|
+
owner.metrics.admissionFailures++;
|
|
21757
|
+
owner.metrics.lastSaturationAt = now;
|
|
21758
|
+
this.persist();
|
|
21759
|
+
return { status: "saturated", jobId: id };
|
|
21760
|
+
}
|
|
21548
21761
|
owner.jobs.push({
|
|
21549
21762
|
id,
|
|
21550
21763
|
identityCid: this.identityCid,
|
|
@@ -21561,41 +21774,55 @@ var PushStore = class _PushStore {
|
|
|
21561
21774
|
status: "pending",
|
|
21562
21775
|
sentCount: 0
|
|
21563
21776
|
});
|
|
21777
|
+
owner.metrics.admitted++;
|
|
21778
|
+
if (owner.notificationPage === void 0) owner.saturated = false;
|
|
21779
|
+
const checkpointed = checkpointAdmission();
|
|
21564
21780
|
this.persist();
|
|
21565
|
-
return true;
|
|
21781
|
+
return { status: "queued", jobId: id, ...checkpointed ? { checkpointed: true } : {} };
|
|
21782
|
+
}
|
|
21783
|
+
enqueueJob(input, now = Date.now()) {
|
|
21784
|
+
return this.admitJob(input, now).status === "queued";
|
|
21566
21785
|
}
|
|
21567
21786
|
dueJobs(now = Date.now()) {
|
|
21568
21787
|
return identityState(this.state, this.identityCid).jobs.filter((job) => job.status === "pending" && job.nextAttemptAt <= now).map((job) => ({ ...job, targetBindingIds: [...job.targetBindingIds], deliveredBindingIds: [...job.deliveredBindingIds] }));
|
|
21569
21788
|
}
|
|
21789
|
+
claimJob(jobId, now = Date.now()) {
|
|
21790
|
+
const job = identityState(this.state, this.identityCid).jobs.find((row) => row.id === jobId);
|
|
21791
|
+
if (!job || job.status !== "pending" || job.nextAttemptAt > now) return false;
|
|
21792
|
+
Object.assign(job, { status: "in_flight", inFlightAt: now });
|
|
21793
|
+
this.persist();
|
|
21794
|
+
return true;
|
|
21795
|
+
}
|
|
21570
21796
|
nextJobDelay(now = Date.now()) {
|
|
21571
21797
|
const pending = identityState(this.state, this.identityCid).jobs.filter((job) => job.status === "pending");
|
|
21572
21798
|
if (pending.length === 0) return null;
|
|
21573
21799
|
return Math.max(0, Math.min(...pending.map((job) => job.nextAttemptAt)) - now);
|
|
21574
21800
|
}
|
|
21575
21801
|
retryJob(jobId, now = Date.now(), random = Math.random) {
|
|
21576
|
-
const
|
|
21577
|
-
|
|
21802
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21803
|
+
const job = owner.jobs.find((row) => row.id === jobId);
|
|
21804
|
+
if (!job || job.status !== "pending" && job.status !== "in_flight") return false;
|
|
21578
21805
|
const attempts = job.attempts + 1;
|
|
21579
21806
|
if (attempts >= MAX_ATTEMPTS || now >= job.expiresAt) {
|
|
21580
|
-
|
|
21807
|
+
owner.metrics.dropped++;
|
|
21808
|
+
this.finishJob(owner, job, now);
|
|
21581
21809
|
this.persist();
|
|
21582
21810
|
return false;
|
|
21583
21811
|
}
|
|
21584
21812
|
const base = Math.min(6e4, 1e3 * 2 ** (attempts - 1));
|
|
21585
21813
|
const jitter = Math.floor(base * 0.25 * Math.max(0, Math.min(1, random())));
|
|
21586
|
-
Object.assign(job, { attempts, nextAttemptAt: Math.min(job.expiresAt, now + base + jitter) });
|
|
21814
|
+
Object.assign(job, { attempts, nextAttemptAt: Math.min(job.expiresAt, now + base + jitter), status: "pending" });
|
|
21815
|
+
delete job.inFlightAt;
|
|
21816
|
+
owner.metrics.retries++;
|
|
21587
21817
|
this.persist();
|
|
21588
21818
|
return true;
|
|
21589
21819
|
}
|
|
21590
21820
|
suppressJob(jobId, now = Date.now()) {
|
|
21591
|
-
const
|
|
21821
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21822
|
+
const job = owner.jobs.find((row) => row.id === jobId);
|
|
21592
21823
|
if (!job || job.status !== "pending") return false;
|
|
21593
|
-
|
|
21594
|
-
|
|
21595
|
-
completedAt: now,
|
|
21596
|
-
deliveredBindingIds: [...job.targetBindingIds],
|
|
21597
|
-
sentCount: 0
|
|
21598
|
-
});
|
|
21824
|
+
owner.metrics.suppressed++;
|
|
21825
|
+
this.finishJob(owner, job, now);
|
|
21599
21826
|
this.persist();
|
|
21600
21827
|
return true;
|
|
21601
21828
|
}
|
|
@@ -21614,14 +21841,17 @@ var PushStore = class _PushStore {
|
|
|
21614
21841
|
if (suppressed === 0) return 0;
|
|
21615
21842
|
Object.assign(job, { deliveredBindingIds: [...completed] });
|
|
21616
21843
|
const outstanding = job.targetBindingIds.some((id) => !completed.has(id) && owner.bindings.some((binding) => binding.bindingId === id));
|
|
21617
|
-
if (!outstanding)
|
|
21844
|
+
if (!outstanding) {
|
|
21845
|
+
owner.metrics.suppressed++;
|
|
21846
|
+
this.finishJob(owner, job, now);
|
|
21847
|
+
}
|
|
21618
21848
|
this.persist();
|
|
21619
21849
|
return suppressed;
|
|
21620
21850
|
}
|
|
21621
21851
|
async dispatchJob(jobId, event, now = Date.now(), random = Math.random) {
|
|
21622
21852
|
const owner = identityState(this.state, this.identityCid);
|
|
21623
21853
|
const job = owner.jobs.find((row) => row.id === jobId);
|
|
21624
|
-
if (!job || job.status !== "
|
|
21854
|
+
if (!job || job.status !== "in_flight") return { sent: 0, pruned: 0, retried: 0, dropped: 0 };
|
|
21625
21855
|
const completed = new Set(job.deliveredBindingIds);
|
|
21626
21856
|
const targets = job.targetBindingIds.map((id) => owner.bindings.find((binding) => binding.bindingId === id)).filter((binding) => !!binding && !completed.has(binding.bindingId) && binding.vapidFingerprint === this.state.vapidFingerprint && binding.configEpoch === this.state.configEpoch);
|
|
21627
21857
|
let sent = 0;
|
|
@@ -21641,7 +21871,7 @@ var PushStore = class _PushStore {
|
|
|
21641
21871
|
sent++;
|
|
21642
21872
|
} catch (error) {
|
|
21643
21873
|
const status = error.statusCode;
|
|
21644
|
-
if (status === 404 || status === 410) {
|
|
21874
|
+
if (status === 404 || status === 410 || invalidDeviceToken(error)) {
|
|
21645
21875
|
completed.add(binding.bindingId);
|
|
21646
21876
|
dead.add(binding.bindingId);
|
|
21647
21877
|
pruned++;
|
|
@@ -21655,28 +21885,66 @@ var PushStore = class _PushStore {
|
|
|
21655
21885
|
}));
|
|
21656
21886
|
if (dead.size) owner.bindings = owner.bindings.filter((binding) => !dead.has(binding.bindingId));
|
|
21657
21887
|
Object.assign(job, { deliveredBindingIds: [...completed], sentCount: job.sentCount + sent });
|
|
21888
|
+
owner.metrics.delivered += sent;
|
|
21889
|
+
owner.metrics.permanentFailures += dropped;
|
|
21890
|
+
owner.metrics.prunedBindings += pruned;
|
|
21658
21891
|
const outstanding = job.targetBindingIds.some((id) => !completed.has(id) && owner.bindings.some((binding) => binding.bindingId === id));
|
|
21659
21892
|
if (outstanding && transient) {
|
|
21660
21893
|
this.persist();
|
|
21661
21894
|
const retried = this.retryJob(job.id, now, random) ? transient : 0;
|
|
21662
21895
|
return { sent, pruned, retried, dropped: dropped + (retried ? 0 : transient) };
|
|
21663
21896
|
}
|
|
21664
|
-
|
|
21897
|
+
if (dropped > 0 && job.sentCount === 0) owner.metrics.dropped++;
|
|
21898
|
+
this.finishJob(owner, job, now);
|
|
21665
21899
|
this.persist();
|
|
21666
21900
|
return { sent, pruned, retried: 0, dropped };
|
|
21667
21901
|
}
|
|
21668
21902
|
queueStats() {
|
|
21669
|
-
const
|
|
21903
|
+
const owner = identityState(this.state, this.identityCid);
|
|
21904
|
+
const jobs = owner.jobs;
|
|
21670
21905
|
return {
|
|
21671
|
-
pending: jobs.
|
|
21672
|
-
|
|
21673
|
-
|
|
21674
|
-
|
|
21906
|
+
pending: jobs.length,
|
|
21907
|
+
inFlight: jobs.filter((job) => job.status === "in_flight").length,
|
|
21908
|
+
sent: owner.metrics.delivered + owner.metrics.suppressed,
|
|
21909
|
+
dropped: owner.metrics.dropped,
|
|
21910
|
+
depth: jobs.length + owner.tombstones.length,
|
|
21911
|
+
tombstones: owner.tombstones.length,
|
|
21912
|
+
capacity: MAX_RECORDS_PER_IDENTITY,
|
|
21913
|
+
saturated: owner.saturated,
|
|
21914
|
+
healthy: !owner.saturated,
|
|
21915
|
+
admissionFailures: owner.metrics.admissionFailures,
|
|
21916
|
+
delivered: owner.metrics.delivered,
|
|
21917
|
+
retries: owner.metrics.retries,
|
|
21918
|
+
permanentFailures: owner.metrics.permanentFailures,
|
|
21919
|
+
prunedBindings: owner.metrics.prunedBindings,
|
|
21920
|
+
...owner.metrics.lastSaturationAt === void 0 ? {} : { lastSaturationAt: owner.metrics.lastSaturationAt }
|
|
21675
21921
|
};
|
|
21676
21922
|
}
|
|
21677
|
-
|
|
21678
|
-
|
|
21679
|
-
|
|
21923
|
+
finishJob(owner, job, now) {
|
|
21924
|
+
owner.jobs = owner.jobs.filter((row) => row.id !== job.id);
|
|
21925
|
+
owner.tombstones.push({ id: job.id, completedAt: now, expiresAt: now + TOMBSTONE_RETENTION_MS });
|
|
21926
|
+
this.pruneTombstones(owner, now);
|
|
21927
|
+
}
|
|
21928
|
+
pruneTombstones(owner, now) {
|
|
21929
|
+
const before = owner.tombstones.length;
|
|
21930
|
+
owner.tombstones = owner.tombstones.filter((row) => row.expiresAt > now).sort((a4, b3) => a4.completedAt - b3.completedAt);
|
|
21931
|
+
if (owner.tombstones.length > MAX_TOMBSTONES_PER_IDENTITY) {
|
|
21932
|
+
owner.tombstones.splice(0, owner.tombstones.length - MAX_TOMBSTONES_PER_IDENTITY);
|
|
21933
|
+
}
|
|
21934
|
+
owner.metrics.tombstonesPruned += before - owner.tombstones.length;
|
|
21935
|
+
}
|
|
21936
|
+
recoverAndPrune(now) {
|
|
21937
|
+
for (const owner of Object.values(this.state.identities)) {
|
|
21938
|
+
for (const job of owner.jobs) {
|
|
21939
|
+
if (job.status === "in_flight") {
|
|
21940
|
+
Object.assign(job, { status: "pending", nextAttemptAt: Math.min(job.nextAttemptAt, now) });
|
|
21941
|
+
delete job.inFlightAt;
|
|
21942
|
+
}
|
|
21943
|
+
}
|
|
21944
|
+
this.pruneTombstones(owner, now);
|
|
21945
|
+
if (owner.jobs.length < MAX_RECORDS_PER_IDENTITY && owner.notificationPage === void 0) {
|
|
21946
|
+
owner.saturated = false;
|
|
21947
|
+
}
|
|
21680
21948
|
}
|
|
21681
21949
|
}
|
|
21682
21950
|
/** Compatibility for tests/old callers; durable production delivery uses jobs. */
|
|
@@ -21684,6 +21952,7 @@ var PushStore = class _PushStore {
|
|
|
21684
21952
|
const wire = `compat-${randomUUID()}`;
|
|
21685
21953
|
this.enqueueJob({ wireId: wire, kind: event.kind, contactId: event.contact_id });
|
|
21686
21954
|
const id = `${this.identityCid}:${wire}:${event.kind}`;
|
|
21955
|
+
this.claimJob(id);
|
|
21687
21956
|
const result = await this.dispatchJob(id, event);
|
|
21688
21957
|
return { sent: result.sent, pruned: result.pruned, failed: result.retried + result.dropped, errors: [] };
|
|
21689
21958
|
}
|
|
@@ -21692,6 +21961,7 @@ var PushStore = class _PushStore {
|
|
|
21692
21961
|
// src/push-delivery.ts
|
|
21693
21962
|
init_define_MESSENGER_BUILD_INFO();
|
|
21694
21963
|
var nonEmpty3 = (value) => typeof value === "string" && value.length > 0;
|
|
21964
|
+
var bounded = (value, max) => nonEmpty3(value) && value.length <= max;
|
|
21695
21965
|
function notificationKind(record) {
|
|
21696
21966
|
if (record.event === "message_received") return "message";
|
|
21697
21967
|
if (record.event !== "file_received") return null;
|
|
@@ -21699,7 +21969,7 @@ function notificationKind(record) {
|
|
|
21699
21969
|
if (record.kind === "photo") return "photo";
|
|
21700
21970
|
return "file";
|
|
21701
21971
|
}
|
|
21702
|
-
async function
|
|
21972
|
+
async function projectPushEvent(client, job) {
|
|
21703
21973
|
const url = `/chats/${encodeURIComponent(job.contactId)}#chat-message-${encodeURIComponent(job.wireId)}`;
|
|
21704
21974
|
if (job.kind === "message") {
|
|
21705
21975
|
if (!client.getHistoryItem) throw new Error("message projection is unavailable");
|
|
@@ -21736,7 +22006,15 @@ async function projectFromSdk(client, job) {
|
|
|
21736
22006
|
};
|
|
21737
22007
|
}
|
|
21738
22008
|
var PushDeliveryQueue = class {
|
|
21739
|
-
stats = {
|
|
22009
|
+
stats = {
|
|
22010
|
+
queued: 0,
|
|
22011
|
+
sent: 0,
|
|
22012
|
+
pruned: 0,
|
|
22013
|
+
retried: 0,
|
|
22014
|
+
dropped: 0,
|
|
22015
|
+
suppressed: 0,
|
|
22016
|
+
saturationEvents: 0
|
|
22017
|
+
};
|
|
21740
22018
|
store;
|
|
21741
22019
|
client;
|
|
21742
22020
|
identityCid;
|
|
@@ -21757,39 +22035,50 @@ var PushDeliveryQueue = class {
|
|
|
21757
22035
|
this.log = options.log;
|
|
21758
22036
|
this.now = options.now ?? Date.now;
|
|
21759
22037
|
this.random = options.random ?? Math.random;
|
|
21760
|
-
this.project = options.project ?? ((job) =>
|
|
22038
|
+
this.project = options.project ?? ((job) => projectPushEvent(this.client, job));
|
|
21761
22039
|
this.isForeground = options.isForeground ?? (() => false);
|
|
21762
22040
|
this.foregroundBindingIds = options.foregroundBindingIds ?? (() => /* @__PURE__ */ new Set());
|
|
21763
22041
|
this.automatic = options.autoStart !== false;
|
|
21764
22042
|
if (this.automatic) this.schedule(0);
|
|
21765
22043
|
}
|
|
21766
22044
|
enqueue(record) {
|
|
21767
|
-
|
|
22045
|
+
return this.admit(record).status === "queued";
|
|
22046
|
+
}
|
|
22047
|
+
admit(record, notificationPageNextIndex) {
|
|
22048
|
+
if (!bounded(record.sender_id, 512) || !bounded(record.wire_id, 1024)) return { status: "no_targets" };
|
|
21768
22049
|
const kind = notificationKind(record);
|
|
21769
|
-
if (!kind || !this.store.hasBindings()) return
|
|
21770
|
-
const
|
|
22050
|
+
if (!kind || !this.store.hasBindings()) return { status: "no_targets" };
|
|
22051
|
+
const admission = this.store.admitJob({
|
|
21771
22052
|
wireId: record.wire_id,
|
|
21772
22053
|
kind,
|
|
21773
22054
|
contactId: record.sender_id,
|
|
21774
|
-
senderName:
|
|
21775
|
-
}, this.now());
|
|
21776
|
-
if (
|
|
22055
|
+
senderName: bounded(record.sender_name, 512) ? record.sender_name : void 0
|
|
22056
|
+
}, this.now(), notificationPageNextIndex);
|
|
22057
|
+
if (admission.status === "saturated") {
|
|
22058
|
+
this.stats.saturationEvents++;
|
|
22059
|
+
const state = this.store.queueStats();
|
|
22060
|
+
this.log.warn(
|
|
22061
|
+
`event=push_queue_saturated status=saturated wire=${record.wire_id} kind=${kind} pending=${state.pending} depth=${state.depth} capacity=${state.capacity} admission_failures=${state.admissionFailures}`
|
|
22062
|
+
);
|
|
22063
|
+
return admission;
|
|
22064
|
+
}
|
|
22065
|
+
if (admission.status === "queued") {
|
|
21777
22066
|
this.stats.queued++;
|
|
21778
22067
|
const jobId = `${this.identityCid}:${record.wire_id}:${kind}`;
|
|
21779
22068
|
const suppressedBindings = this.store.suppressBindings(jobId, this.foregroundBindingIds(), this.now());
|
|
21780
22069
|
if (suppressedBindings > 0) {
|
|
21781
22070
|
this.stats.suppressed += suppressedBindings;
|
|
21782
22071
|
this.log.info(`push queue: suppressed ${suppressedBindings} foreground binding(s) kind=${kind} wire=${record.wire_id}`);
|
|
21783
|
-
if (this.store.queueStats().pending === 0) return
|
|
22072
|
+
if (this.store.queueStats().pending === 0) return admission;
|
|
21784
22073
|
} else if (this.isForeground() && this.store.suppressJob(jobId, this.now())) {
|
|
21785
22074
|
this.stats.suppressed++;
|
|
21786
22075
|
this.log.info(`push queue: suppressed foreground kind=${kind} wire=${record.wire_id}`);
|
|
21787
|
-
return
|
|
22076
|
+
return admission;
|
|
21788
22077
|
}
|
|
21789
22078
|
this.log.info(`push queue: queued kind=${kind} wire=${record.wire_id} depth=${this.store.queueStats().pending}`);
|
|
21790
22079
|
if (this.automatic) this.schedule(0);
|
|
21791
22080
|
}
|
|
21792
|
-
return
|
|
22081
|
+
return admission;
|
|
21793
22082
|
}
|
|
21794
22083
|
async drainDue() {
|
|
21795
22084
|
if (this.draining) return this.draining;
|
|
@@ -21805,6 +22094,7 @@ var PushDeliveryQueue = class {
|
|
|
21805
22094
|
async drainOnce() {
|
|
21806
22095
|
for (const job of this.store.dueJobs(this.now())) {
|
|
21807
22096
|
if (this.stopped) return;
|
|
22097
|
+
if (!this.store.claimJob(job.id, this.now())) continue;
|
|
21808
22098
|
let event;
|
|
21809
22099
|
try {
|
|
21810
22100
|
event = await this.project(job);
|
|
@@ -21851,77 +22141,51 @@ var PushDeliveryQueue = class {
|
|
|
21851
22141
|
|
|
21852
22142
|
// src/watch.ts
|
|
21853
22143
|
init_define_MESSENGER_BUILD_INFO();
|
|
21854
|
-
|
|
21855
|
-
|
|
21856
|
-
|
|
21857
|
-
|
|
21858
|
-
|
|
21859
|
-
|
|
21860
|
-
|
|
21861
|
-
|
|
21862
|
-
|
|
21863
|
-
|
|
21864
|
-
|
|
21865
|
-
|
|
21866
|
-
|
|
21867
|
-
|
|
21868
|
-
if (!message) throw new Error("canonical message was not available for push projection");
|
|
21869
|
-
const announcedSender = nonEmpty4(record.sender_name) ? record.sender_name : "New message";
|
|
21870
|
-
const sender = contactDisplayName(announcedSender);
|
|
21871
|
-
const body = contactMessagePreview(announcedSender, message.text);
|
|
21872
|
-
return { v: 1, kind: "message", title: sender, body, contact_id: contactId, wire_id: wireId, url };
|
|
22144
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
22145
|
+
function pageDigest(events) {
|
|
22146
|
+
return createHash4("sha256").update(JSON.stringify(events)).digest("base64url");
|
|
22147
|
+
}
|
|
22148
|
+
function applyNotificationPage(page, push, delivery, events, stats, log2) {
|
|
22149
|
+
if (!Number.isSafeInteger(page.cursor) || page.cursor < 0 || !Array.isArray(page.events)) {
|
|
22150
|
+
throw new Error("daemon notification page is malformed");
|
|
22151
|
+
}
|
|
22152
|
+
const fromCursor = push.notificationCursor;
|
|
22153
|
+
if (fromCursor === null) {
|
|
22154
|
+
if (page.events.length > 0) throw new Error("daemon notification tip page unexpectedly contained events");
|
|
22155
|
+
push.commitNotificationCursor(page.cursor);
|
|
22156
|
+
stats.cursorCommits++;
|
|
22157
|
+
return true;
|
|
21873
22158
|
}
|
|
21874
|
-
|
|
21875
|
-
|
|
21876
|
-
|
|
21877
|
-
|
|
21878
|
-
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
|
|
21882
|
-
|
|
21883
|
-
|
|
21884
|
-
|
|
21885
|
-
|
|
21886
|
-
|
|
21887
|
-
kind,
|
|
21888
|
-
title: contactDisplayName(file.from.name || label),
|
|
21889
|
-
body: `${label}: ${file.filename}`,
|
|
21890
|
-
contact_id: contactId,
|
|
21891
|
-
wire_id: wireId,
|
|
21892
|
-
url
|
|
21893
|
-
};
|
|
22159
|
+
let progress = push.notificationPageProgress;
|
|
22160
|
+
if (progress) {
|
|
22161
|
+
const prefix = page.events.slice(0, progress.eventCount);
|
|
22162
|
+
if (progress.fromCursor !== fromCursor || page.cursor < progress.cursor || page.events.length < progress.eventCount || pageDigest(prefix) !== progress.digest) {
|
|
22163
|
+
throw new Error("daemon notification page changed while a checkpoint was in progress");
|
|
22164
|
+
}
|
|
22165
|
+
} else {
|
|
22166
|
+
progress = push.beginNotificationPage({
|
|
22167
|
+
fromCursor,
|
|
22168
|
+
cursor: page.cursor,
|
|
22169
|
+
eventCount: page.events.length,
|
|
22170
|
+
digest: pageDigest(page.events)
|
|
22171
|
+
});
|
|
21894
22172
|
}
|
|
21895
|
-
|
|
21896
|
-
|
|
21897
|
-
|
|
21898
|
-
|
|
21899
|
-
|
|
21900
|
-
|
|
21901
|
-
|
|
21902
|
-
|
|
21903
|
-
|
|
21904
|
-
|
|
21905
|
-
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
|
|
21911
|
-
if (f4.status !== "unread") continue;
|
|
21912
|
-
if (delivery.enqueue({
|
|
21913
|
-
event: "file_received",
|
|
21914
|
-
sender_id: f4.from.id,
|
|
21915
|
-
sender_name: f4.from.name,
|
|
21916
|
-
wire_id: f4.wire_id,
|
|
21917
|
-
// notificationKind() reads `kind` to split voice from file; passing the
|
|
21918
|
-
// raw value keeps that decision in one place rather than duplicating it
|
|
21919
|
-
// here, and matches what the live daemon record carries.
|
|
21920
|
-
kind: f4.kind
|
|
21921
|
-
})) queued++;
|
|
21922
|
-
}
|
|
21923
|
-
if (queued > 0) log2.info(`watch reconcile: queued ${queued} push(es) for notifications missed while disconnected`);
|
|
21924
|
-
return queued;
|
|
22173
|
+
for (let index = progress.nextIndex; index < progress.eventCount; index++) {
|
|
22174
|
+
const record = page.events[index];
|
|
22175
|
+
const admission = delivery.admit(record, index + 1);
|
|
22176
|
+
if (admission.status === "saturated") {
|
|
22177
|
+
push.checkpointNotificationPage();
|
|
22178
|
+
stats.saturationEvents++;
|
|
22179
|
+
log2.warn(`event=push_cursor_blocked status=saturated source_cursor=${page.cursor}`);
|
|
22180
|
+
return false;
|
|
22181
|
+
}
|
|
22182
|
+
stats.events++;
|
|
22183
|
+
events.publish(normalizeNotification(record));
|
|
22184
|
+
if (!admission.checkpointed) push.advanceNotificationPage(index + 1);
|
|
22185
|
+
}
|
|
22186
|
+
push.commitNotificationPage();
|
|
22187
|
+
stats.cursorCommits++;
|
|
22188
|
+
return true;
|
|
21925
22189
|
}
|
|
21926
22190
|
function abortableWait(ms, signal) {
|
|
21927
22191
|
if (signal.aborted) return Promise.resolve();
|
|
@@ -21935,12 +22199,11 @@ function abortableWait(ms, signal) {
|
|
|
21935
22199
|
signal.addEventListener("abort", done, { once: true });
|
|
21936
22200
|
});
|
|
21937
22201
|
}
|
|
21938
|
-
function startWatcher(client, identity, push, log2, events, options
|
|
22202
|
+
function startWatcher(client, identity, push, log2, events, options) {
|
|
21939
22203
|
const controller = new AbortController();
|
|
21940
|
-
const stats = { pushes: 0, events: 0, reconnects: 0 };
|
|
22204
|
+
const stats = { pushes: 0, events: 0, reconnects: 0, cursorCommits: 0, saturationEvents: 0 };
|
|
21941
22205
|
let backoffMs = 500;
|
|
21942
22206
|
let reconnecting = false;
|
|
21943
|
-
const watch = options.watch ?? ((name, signal) => client.watchNotifications(name, { signal }));
|
|
21944
22207
|
const wait = options.wait ?? abortableWait;
|
|
21945
22208
|
const probe = options.probe ?? (() => client.version());
|
|
21946
22209
|
const loop = (async () => {
|
|
@@ -21952,35 +22215,16 @@ function startWatcher(client, identity, push, log2, events, options = {}) {
|
|
|
21952
22215
|
events.publish({ type: "sync_required", reason: "daemon_reconnected" });
|
|
21953
22216
|
reconnecting = false;
|
|
21954
22217
|
}
|
|
21955
|
-
const
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
21962
|
-
|
|
21963
|
-
for await (const record of upstream) {
|
|
21964
|
-
if (controller.signal.aborted) break;
|
|
21965
|
-
stats.events++;
|
|
21966
|
-
backoffMs = 500;
|
|
21967
|
-
events.publish(normalizeNotification(record));
|
|
21968
|
-
try {
|
|
21969
|
-
if (options.delivery) options.delivery.enqueue(record);
|
|
21970
|
-
if (options.delivery) continue;
|
|
21971
|
-
const event = await pushEventFor(client, record);
|
|
21972
|
-
if (!event) continue;
|
|
21973
|
-
const result = await push.send(event);
|
|
21974
|
-
stats.pushes += result.sent;
|
|
21975
|
-
if (result.pruned) log2.info(`push: pruned ${result.pruned} dead subscription(s)`);
|
|
21976
|
-
if (result.failed) {
|
|
21977
|
-
log2.warn(`push: ${result.failed} subscription(s) failed (kept for retry)`);
|
|
21978
|
-
}
|
|
21979
|
-
} catch (e) {
|
|
21980
|
-
reportFailure(log2.warn, "push send", e);
|
|
21981
|
-
}
|
|
22218
|
+
const since = push.notificationCursor ?? "tip";
|
|
22219
|
+
const page = await options.readPage(identity, since, controller.signal);
|
|
22220
|
+
if (controller.signal.aborted) break;
|
|
22221
|
+
const committed = applyNotificationPage(page, push, options.delivery, events, stats, log2);
|
|
22222
|
+
if (!committed) {
|
|
22223
|
+
await wait(backoffMs, controller.signal);
|
|
22224
|
+
backoffMs = Math.min(backoffMs * 2, 3e4);
|
|
22225
|
+
continue;
|
|
21982
22226
|
}
|
|
21983
|
-
|
|
22227
|
+
backoffMs = 500;
|
|
21984
22228
|
} catch (e) {
|
|
21985
22229
|
if (controller.signal.aborted) break;
|
|
21986
22230
|
stats.reconnects++;
|
|
@@ -22480,15 +22724,18 @@ async function start(cfg, buildInfo, dependencies = DEFAULT_START_DEPENDENCIES)
|
|
|
22480
22724
|
log,
|
|
22481
22725
|
foregroundBindingIds: () => presence.onlineBindings(bound.cid)
|
|
22482
22726
|
});
|
|
22483
|
-
watcher = startWatcher(runtime.client, cfg.identity, push, log, events, {
|
|
22727
|
+
watcher = startWatcher(runtime.client, cfg.identity, push, log, events, {
|
|
22728
|
+
delivery,
|
|
22729
|
+
readPage: runtime.readNotificationPage
|
|
22730
|
+
});
|
|
22484
22731
|
const readyDeps = {
|
|
22485
22732
|
runtime,
|
|
22486
22733
|
push,
|
|
22487
22734
|
config: cfg,
|
|
22488
22735
|
buildInfo,
|
|
22489
22736
|
watcherStats: () => ({
|
|
22490
|
-
...watcher?.stats ?? { pushes: 0, events: 0, reconnects: 0 },
|
|
22491
|
-
...delivery?.stats ?? { queued: 0, sent: 0, pruned: 0, retried: 0, dropped: 0 },
|
|
22737
|
+
...watcher?.stats ?? { pushes: 0, events: 0, reconnects: 0, cursorCommits: 0, saturationEvents: 0 },
|
|
22738
|
+
...delivery?.stats ?? { queued: 0, sent: 0, pruned: 0, retried: 0, dropped: 0, suppressed: 0, saturationEvents: 0 },
|
|
22492
22739
|
presenceIdentities: presence.onlineCount,
|
|
22493
22740
|
presenceSockets: presence.socketCount
|
|
22494
22741
|
}),
|