@agentunion/fastaun 0.5.30 → 0.5.32
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/CHANGELOG.md +60 -0
- package/_packed_docs/CHANGELOG.md +60 -0
- package/_packed_docs/sdk/0.5.31-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +162 -0
- package/_packed_docs/sdk/0.5.31-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +167 -0
- package/_packed_docs/sdk/0.5.32-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +350 -0
- package/_packed_docs/sdk/0.5.32-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +319 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +12 -5
- package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +459 -0
- package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/274/200/345/217/221/347/247/273/346/244/215/346/214/207/345/215/227.md +554 -0
- package/_packed_docs/sdk/INDEX.md +27 -5
- package/_packed_docs/sdk//345/255/220AID/344/277/256/346/224/271agent.md/351/200/232/347/237/245Owner/345/234/272/346/231/257/346/214/207/345/215/227.md +234 -0
- package/dist/agent-md-schema.js +7 -6
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +2 -0
- package/dist/agent-md.js +20 -6
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +1 -0
- package/dist/aid-store.js +1 -0
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.js +1 -1
- package/dist/aid.js.map +1 -1
- package/dist/auth.js +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/client/control-messages.d.ts +22 -7
- package/dist/client/control-messages.js +220 -185
- package/dist/client/control-messages.js.map +1 -1
- package/dist/client/delivery.d.ts +3 -0
- package/dist/client/delivery.js +67 -3
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts +3 -0
- package/dist/client/group-state.js +48 -14
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +12 -3
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +9 -3
- package/dist/client/rpc-pipeline.js +61 -26
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +13 -1
- package/dist/client/v2-e2ee.js +32 -19
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +18 -1
- package/dist/client.js +396 -140
- package/dist/client.js.map +1 -1
- package/dist/discovery.d.ts +1 -0
- package/dist/discovery.js +3 -1
- package/dist/discovery.js.map +1 -1
- package/dist/group-fs.js +1 -1
- package/dist/group-fs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ownership-v21.d.ts +2 -2
- package/dist/ownership-v21.js +18 -21
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.js +6 -4
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/pubsub/binding.d.ts +54 -0
- package/dist/pubsub/binding.js +343 -0
- package/dist/pubsub/binding.js.map +1 -0
- package/dist/pubsub/channel.d.ts +45 -0
- package/dist/pubsub/channel.js +137 -0
- package/dist/pubsub/channel.js.map +1 -0
- package/dist/pubsub/client.d.ts +93 -0
- package/dist/pubsub/client.js +318 -0
- package/dist/pubsub/client.js.map +1 -0
- package/dist/pubsub/credentials.d.ts +51 -0
- package/dist/pubsub/credentials.js +360 -0
- package/dist/pubsub/credentials.js.map +1 -0
- package/dist/pubsub/errors.d.ts +5 -0
- package/dist/pubsub/errors.js +6 -0
- package/dist/pubsub/errors.js.map +1 -0
- package/dist/pubsub/index.d.ts +48 -0
- package/dist/pubsub/index.js +152 -0
- package/dist/pubsub/index.js.map +1 -0
- package/dist/pubsub/scheduler.d.ts +58 -0
- package/dist/pubsub/scheduler.js +249 -0
- package/dist/pubsub/scheduler.js.map +1 -0
- package/dist/pubsub/service.d.ts +100 -0
- package/dist/pubsub/service.js +650 -0
- package/dist/pubsub/service.js.map +1 -0
- package/dist/pubsub/types.d.ts +119 -0
- package/dist/pubsub/types.js +2 -0
- package/dist/pubsub/types.js.map +1 -0
- package/dist/register-flow.js +1 -1
- package/dist/register-flow.js.map +1 -1
- package/dist/storage/lowlevel.js +49 -8
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/storage/read-cache.d.ts +7 -3
- package/dist/storage/read-cache.js +137 -18
- package/dist/storage/read-cache.js.map +1 -1
- package/dist/storage/vfs.d.ts +1 -0
- package/dist/storage/vfs.js +12 -4
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +1 -1
- package/dist/tools/cross-sdk-agent.js +4 -2
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/tools/pubsub-actor.d.ts +27 -0
- package/dist/tools/pubsub-actor.js +260 -0
- package/dist/tools/pubsub-actor.js.map +1 -0
- package/dist/transport.d.ts +26 -2
- package/dist/transport.js +182 -17
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -5,15 +5,6 @@ export const CLASS_RECEIVE_METHODS = new Set([
|
|
|
5
5
|
'group.pull', 'group.v2.pull', 'group.history', 'group.ack', 'group.v2.ack', 'group.ack_messages',
|
|
6
6
|
]);
|
|
7
7
|
function object(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); }
|
|
8
|
-
function groupStreamNeedsPull(entry, messageClass, storage) {
|
|
9
|
-
if (!object(entry) || !object(entry.streams))
|
|
10
|
-
return true;
|
|
11
|
-
const classState = entry.streams[messageClass];
|
|
12
|
-
if (!object(classState))
|
|
13
|
-
return false;
|
|
14
|
-
const streamState = classState[storage];
|
|
15
|
-
return object(streamState) && streamState.needs_pull === true;
|
|
16
|
-
}
|
|
17
8
|
function protocolVersion(value) {
|
|
18
9
|
const version = typeof value === 'string' && /^[1-9][0-9]*$/.test(value) ? Number(value) : value;
|
|
19
10
|
return Number.isSafeInteger(version) && Number(version) > 0 ? Number(version) : null;
|
|
@@ -60,16 +51,156 @@ export function applicationResult(value) {
|
|
|
60
51
|
export class ControlMessageCoordinator {
|
|
61
52
|
client;
|
|
62
53
|
locks = new Map();
|
|
63
|
-
pending = new Map();
|
|
64
|
-
workers = new Map();
|
|
65
54
|
epoch = 0;
|
|
55
|
+
pubsubPages = 0;
|
|
56
|
+
pubsubPageWaiters = [];
|
|
57
|
+
pubsubReceivers = new Map();
|
|
58
|
+
pubsubCandidates = [];
|
|
59
|
+
pubsubBytes = 0;
|
|
60
|
+
blockedPubsubPulls = new Map();
|
|
61
|
+
pubsubWorkers = new Set();
|
|
66
62
|
constructor(client) {
|
|
67
63
|
this.client = client;
|
|
68
64
|
}
|
|
65
|
+
async acquirePubsubPage(identity) {
|
|
66
|
+
if (this.pubsubPages < 4)
|
|
67
|
+
this.pubsubPages++;
|
|
68
|
+
else
|
|
69
|
+
await new Promise((resolve, reject) => { this.pubsubPageWaiters.push({ resolve, reject }); });
|
|
70
|
+
try {
|
|
71
|
+
this.check(identity);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.releasePubsubPage();
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return () => this.releasePubsubPage();
|
|
78
|
+
}
|
|
79
|
+
releasePubsubPage() {
|
|
80
|
+
const waiter = this.pubsubPageWaiters.shift();
|
|
81
|
+
if (waiter)
|
|
82
|
+
waiter.resolve();
|
|
83
|
+
else
|
|
84
|
+
this.pubsubPages--;
|
|
85
|
+
}
|
|
69
86
|
supports(messageClass, group = false) {
|
|
70
|
-
|
|
87
|
+
if (group && messageClass === 'sdk_control')
|
|
88
|
+
return false;
|
|
89
|
+
const classes = this.client._negotiatedCapabilities?.message_classes_v2?.[group ? 'group' : 'p2p'];
|
|
71
90
|
return Array.isArray(classes) && classes.includes(messageClass);
|
|
72
91
|
}
|
|
92
|
+
registerPubsub(receiver, options) {
|
|
93
|
+
if (this.pubsubReceivers.size >= 64)
|
|
94
|
+
throw new StateError('control pubsub receivers are full');
|
|
95
|
+
const token = {};
|
|
96
|
+
this.pubsubReceivers.set(token, { receiver, ...options, identity: this.pubsubIdentity() });
|
|
97
|
+
return () => {
|
|
98
|
+
this.pubsubReceivers.delete(token);
|
|
99
|
+
if (!this.pubsubReceivers.size) {
|
|
100
|
+
this.pubsubCandidates = [];
|
|
101
|
+
this.pubsubBytes = 0;
|
|
102
|
+
this.blockedPubsubPulls.clear();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
pubsubIdentity() {
|
|
107
|
+
const c = this.client;
|
|
108
|
+
return JSON.stringify([c._aid, c._deviceId, c._slotId]);
|
|
109
|
+
}
|
|
110
|
+
queuePubsubCandidate(data) {
|
|
111
|
+
if (!this.supports('sdk_control') || ![...this.pubsubReceivers.values()].some(value => value.identity === this.pubsubIdentity()))
|
|
112
|
+
return;
|
|
113
|
+
const row = object(data.inline_message) ? { ...data, ...data.inline_message } : data;
|
|
114
|
+
if (!row.volatile_cursor)
|
|
115
|
+
return;
|
|
116
|
+
let envelope = row.envelope ?? row.payload;
|
|
117
|
+
if (typeof row.envelope_json === 'string') {
|
|
118
|
+
try {
|
|
119
|
+
envelope = JSON.parse(row.envelope_json);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!object(envelope) || envelope.type !== 'e2ee.p2p_encrypted' || envelope.version !== 'v2')
|
|
126
|
+
return;
|
|
127
|
+
const bytes = new TextEncoder().encode(JSON.stringify(row)).byteLength;
|
|
128
|
+
if (this.pubsubCandidates.length >= 64 || this.pubsubBytes + bytes > 1048576)
|
|
129
|
+
return;
|
|
130
|
+
const receivedAt = Date.now();
|
|
131
|
+
const deadline = Number.isSafeInteger(row.expires_at) ? row.expires_at : Number.isFinite(row.t_server) && Number.isFinite(row.ttl) ? row.t_server + row.ttl * 1000 : receivedAt + 60_000;
|
|
132
|
+
if (deadline <= receivedAt)
|
|
133
|
+
return;
|
|
134
|
+
this.pubsubCandidates.push({ row: structuredClone({ ...row, from_aid: row.from_aid ?? row.from ?? row.sender_aid, envelope_json: JSON.stringify(envelope) }), identity: this.identity(), tokens: [...this.pubsubReceivers.keys()], bytes, deadline });
|
|
135
|
+
this.pubsubBytes += bytes;
|
|
136
|
+
this.wakePubsubCandidates();
|
|
137
|
+
}
|
|
138
|
+
wakePubsubCandidates() {
|
|
139
|
+
while (this.pubsubWorkers.size < 2 && this.pubsubCandidates.length) {
|
|
140
|
+
const task = Promise.resolve().then(() => this.drainPubsubCandidates());
|
|
141
|
+
this.pubsubWorkers.add(task);
|
|
142
|
+
void task.finally(() => { this.pubsubWorkers.delete(task); this.wakePubsubCandidates(); });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async drainPubsubCandidates() {
|
|
146
|
+
while (this.pubsubCandidates.length) {
|
|
147
|
+
const candidate = this.pubsubCandidates.shift();
|
|
148
|
+
this.pubsubBytes -= candidate.bytes;
|
|
149
|
+
try {
|
|
150
|
+
this.check(candidate.identity);
|
|
151
|
+
if (candidate.deadline <= Date.now())
|
|
152
|
+
continue;
|
|
153
|
+
let timer;
|
|
154
|
+
let decoded;
|
|
155
|
+
try {
|
|
156
|
+
decoded = await Promise.race([
|
|
157
|
+
this.decode(candidate.row, 'sdk_control', false, {}, { background: 'event', deadline: candidate.deadline }),
|
|
158
|
+
new Promise((_, reject) => { timer = setTimeout(() => reject(new StateError('control pubsub candidate expired')), candidate.deadline - Date.now()); }),
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
if (timer !== undefined)
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
if (candidate.deadline <= Date.now())
|
|
166
|
+
continue;
|
|
167
|
+
this.check(candidate.identity);
|
|
168
|
+
this.dispatchPubsub(decoded, candidate.tokens);
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
dispatchPubsub(message, tokens = this.pubsubReceivers.keys()) {
|
|
174
|
+
const headers = message.protected_headers;
|
|
175
|
+
if (!object(headers) || headers.message_class !== 'sdk_control' || headers.sdk_control_protocol !== 'aun.pubsub' || protocolVersion(headers.sdk_control_version) !== 1)
|
|
176
|
+
return true;
|
|
177
|
+
const payload = message.payload, sender = message.from ?? message.from_aid ?? message.sender_aid;
|
|
178
|
+
if (message.encrypted !== true || message._decrypt_error || !object(payload) || payload.type !== 'app.pubsub.v1'
|
|
179
|
+
|| typeof sender !== 'string' || !sender || message.to && message.to !== this.client._aid
|
|
180
|
+
|| !Number.isSafeInteger(payload.expires_at) || typeof payload.op !== 'string')
|
|
181
|
+
return true;
|
|
182
|
+
let admitted = true;
|
|
183
|
+
for (const token of tokens) {
|
|
184
|
+
const registered = this.pubsubReceivers.get(token);
|
|
185
|
+
if (!registered || registered.identity !== this.pubsubIdentity() || registered.clock() >= payload.expires_at
|
|
186
|
+
|| (payload.service_aid ?? (payload.op === 'event' ? registered.serviceAid : undefined)) !== registered.serviceAid)
|
|
187
|
+
continue;
|
|
188
|
+
if (registered.publishReceiver) {
|
|
189
|
+
if (registered.serviceAid !== this.client._aid || !['authorize', 'publish', 'subscribe', 'renew', 'unsubscribe', 'catalog', 'stats', 'issue_grant', 'revoke_grant'].includes(payload.op))
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
else if (sender !== registered.serviceAid || payload.op !== 'event' && !['authorize', 'publish', 'subscribe', 'renew', 'unsubscribe', 'catalog', 'stats', 'issue_grant', 'revoke_grant'].some(op => payload.op === `${op}.result`))
|
|
193
|
+
continue;
|
|
194
|
+
if (registered.receiver(message) === false) {
|
|
195
|
+
admitted = false;
|
|
196
|
+
if (this.blockedPubsubPulls.size < 64 || this.blockedPubsubPulls.has(sender))
|
|
197
|
+
this.blockedPubsubPulls.set(sender, { message_class: 'sdk_control', sender_aid: sender });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return admitted;
|
|
201
|
+
}
|
|
202
|
+
resumePubsubPulls() { const pending = [...this.blockedPubsubPulls.values()]; this.blockedPubsubPulls.clear(); for (const request of pending)
|
|
203
|
+
this.client._delivery.queueMessageClassPull(request); }
|
|
73
204
|
identity() {
|
|
74
205
|
const c = this.client;
|
|
75
206
|
return JSON.stringify([c._aid, c._deviceId, c._slotId, c._serviceEventIdentityVersion, c._v2RuntimeGeneration,
|
|
@@ -99,13 +230,13 @@ export class ControlMessageCoordinator {
|
|
|
99
230
|
return { ...state, message_control_v1: streams };
|
|
100
231
|
});
|
|
101
232
|
}
|
|
102
|
-
async decode(row, messageClass, group, request) {
|
|
233
|
+
async decode(row, messageClass, group, request, rpcOptions) {
|
|
103
234
|
if (messageClassOf(row) !== messageClass)
|
|
104
235
|
throw new StateError('control message class mismatch');
|
|
105
236
|
let decoded;
|
|
106
237
|
const envelope = row.envelope_json ?? (object(row.payload) && String(row.payload.type ?? '').startsWith('e2ee.') ? JSON.stringify(row.payload) : null);
|
|
107
238
|
if (envelope) {
|
|
108
|
-
decoded = await this.client._decryptV2Message({ ...row, envelope_json: envelope }, false, false, true, true, undefined, false, 'control_pull');
|
|
239
|
+
decoded = await this.client._decryptV2Message({ ...row, envelope_json: envelope }, false, false, true, true, undefined, false, 'control_pull', ...(rpcOptions ? [undefined, rpcOptions] : []));
|
|
109
240
|
if (!decoded)
|
|
110
241
|
throw new StateError('control message decryption is not ready');
|
|
111
242
|
const headers = decoded.protected_headers;
|
|
@@ -129,22 +260,34 @@ export class ControlMessageCoordinator {
|
|
|
129
260
|
else {
|
|
130
261
|
decoded = { ...row, from: row.from_aid ?? row.from, encrypted: false };
|
|
131
262
|
}
|
|
263
|
+
for (const key of ['sender_device_id', 'sender_slot_id'])
|
|
264
|
+
if (typeof row[key] === 'string') {
|
|
265
|
+
if (key === 'sender_device_id' && decoded[key] && decoded[key] !== row[key])
|
|
266
|
+
throw new StateError('control sender device mismatch');
|
|
267
|
+
decoded[key] = row[key];
|
|
268
|
+
}
|
|
132
269
|
if (group) {
|
|
133
270
|
decoded.group_id = request.group_id ?? request.group_aid;
|
|
134
271
|
decoded.group_aid = row.group_aid ?? request.group_aid;
|
|
135
272
|
}
|
|
136
273
|
return { ...decoded, message_class: messageClass };
|
|
137
274
|
}
|
|
138
|
-
async consume(message) {
|
|
139
|
-
if (message.encrypted !== true || !object(message.protected_headers))
|
|
140
|
-
return;
|
|
275
|
+
async consume(message, _admission = false) {
|
|
276
|
+
if (message.encrypted !== true || !object(message.protected_headers) || message.protected_headers.message_class !== 'sdk_control')
|
|
277
|
+
return true;
|
|
141
278
|
const protocol = message.protected_headers.sdk_control_protocol;
|
|
142
279
|
const version = protocolVersion(message.protected_headers.sdk_control_version);
|
|
143
280
|
if (typeof protocol !== 'string' || version === null || !object(message.payload))
|
|
144
|
-
return;
|
|
281
|
+
return true;
|
|
282
|
+
if (protocol === 'aun.pubsub') {
|
|
283
|
+
if (version === 1)
|
|
284
|
+
return this.dispatchPubsub(message);
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
145
287
|
if (typeof this.client._onSdkControl === 'function') {
|
|
146
288
|
await this.client._onSdkControl(protocol, version, message.payload, String(message.from ?? message.from_aid ?? ''));
|
|
147
289
|
}
|
|
290
|
+
return true;
|
|
148
291
|
}
|
|
149
292
|
checkScope(result, request, group, volatileGroup) {
|
|
150
293
|
if (!object(result) || result.message_class !== request.message_class || !Array.isArray(result.messages))
|
|
@@ -160,35 +303,11 @@ export class ControlMessageCoordinator {
|
|
|
160
303
|
else if (result.cursor_scope !== 'peer' || result.sender_aid !== request.sender_aid)
|
|
161
304
|
throw new StateError('control Pull peer scope mismatch');
|
|
162
305
|
}
|
|
163
|
-
async
|
|
164
|
-
const c = this.client, ack = saved.pending_ack;
|
|
165
|
-
const target = ack.up_to_seq;
|
|
166
|
-
let after = saved.pending_ack_after_seq ?? 0;
|
|
167
|
-
if (!Number.isSafeInteger(target) || !Number.isSafeInteger(after) || after < 0 || after > target || target > saved.after_seq)
|
|
168
|
-
throw new StateError('invalid pending control ACK coverage');
|
|
169
|
-
const gateKey = `message_class/${ack.message_class}/${key}|forward|class=${ack.message_class}`;
|
|
170
|
-
let proof = '';
|
|
171
|
-
do {
|
|
172
|
-
this.check(identity);
|
|
173
|
-
const request = { message_class: ack.message_class, device_id: c._deviceId, slot_id: c._slotId, after_seq: after, head_seq: target, limit: 200,
|
|
174
|
-
...(group ? { group_id: ack.group_id } : { cursor_scope: 'peer', sender_aid: ack.sender_aid }) };
|
|
175
|
-
const result = await c._rpcPipeline.runPullSerialized(gateKey, () => c._rpcPipeline.rawCall(group ? 'group.v2.pull' : 'message.v2.pull', request, { pullGateKey: gateKey }), true);
|
|
176
|
-
this.check(identity);
|
|
177
|
-
this.checkScope(result, request, group, false);
|
|
178
|
-
const covered = result.scanned_through_seq ?? result.covered_through_seq;
|
|
179
|
-
if (result.head_seq !== target || !Number.isSafeInteger(covered) || covered < after || covered > target
|
|
180
|
-
|| covered === after && after < target || typeof result.scan_proof !== 'string' || !result.scan_proof)
|
|
181
|
-
throw new StateError('control ACK renewal coverage is invalid');
|
|
182
|
-
proof = result.scan_proof;
|
|
183
|
-
await c._rpcPipeline.rawCall(group ? 'group.v2.ack' : 'message.v2.ack', { ...ack, up_to_seq: covered, scan_proof: proof });
|
|
184
|
-
this.check(identity);
|
|
185
|
-
after = covered;
|
|
186
|
-
} while (after < target);
|
|
187
|
-
return proof;
|
|
188
|
-
}
|
|
189
|
-
async call(method, params) {
|
|
306
|
+
async call(method, params, background = false) {
|
|
190
307
|
const c = this.client;
|
|
191
308
|
const request = { ...params };
|
|
309
|
+
delete request._rpc_background;
|
|
310
|
+
const rpcOptions = background ? { background: true } : undefined;
|
|
192
311
|
const group = method.startsWith('group.');
|
|
193
312
|
const volatileGroup = group && request.volatile === true;
|
|
194
313
|
const messageClass = request.message_class ?? 'normal';
|
|
@@ -205,7 +324,7 @@ export class ControlMessageCoordinator {
|
|
|
205
324
|
if (!group)
|
|
206
325
|
request.cursor_scope = 'peer';
|
|
207
326
|
if (method === 'message.peers') {
|
|
208
|
-
const result = await c._rpcPipeline.rawCall(method, request);
|
|
327
|
+
const result = await c._rpcPipeline.rawCall(method, request, ...(rpcOptions ? [rpcOptions] : []));
|
|
209
328
|
this.check(identity);
|
|
210
329
|
if (!object(result) || result.message_class !== messageClass || !Array.isArray(result.peers))
|
|
211
330
|
throw new StateError('control peer directory scope mismatch');
|
|
@@ -219,45 +338,47 @@ export class ControlMessageCoordinator {
|
|
|
219
338
|
const lock = new Promise((resolve) => { unlock = resolve; });
|
|
220
339
|
this.locks.set(key, lock);
|
|
221
340
|
await preceding;
|
|
341
|
+
let releasePage;
|
|
222
342
|
try {
|
|
223
343
|
this.check(identity);
|
|
224
344
|
let saved = this.load(key);
|
|
225
345
|
const ackMethod = group ? 'group.v2.ack' : 'message.v2.ack';
|
|
226
346
|
if (!method.endsWith('.history') && object(saved.pending_ack)) {
|
|
227
|
-
|
|
347
|
+
delete saved.pending_ack.scan_proof;
|
|
228
348
|
try {
|
|
229
|
-
await c._rpcPipeline.rawCall(ackMethod, saved.pending_ack);
|
|
349
|
+
await c._rpcPipeline.rawCall(ackMethod, saved.pending_ack, ...(rpcOptions ? [rpcOptions] : []));
|
|
230
350
|
}
|
|
231
351
|
catch (error) {
|
|
232
352
|
this.check(identity);
|
|
233
353
|
if (volatileGroup && error?.data?.code === 'volatile_cursor_unavailable')
|
|
234
354
|
saved = { ...saved, volatile_cursor: '' };
|
|
235
|
-
else
|
|
236
|
-
|
|
237
|
-
throw error;
|
|
238
|
-
proof = await this.renewPendingAck(saved, group, key, identity);
|
|
239
|
-
}
|
|
355
|
+
else
|
|
356
|
+
throw error;
|
|
240
357
|
}
|
|
241
358
|
this.check(identity);
|
|
242
|
-
saved = { ...saved,
|
|
359
|
+
saved = { ...saved, pending_ack: undefined };
|
|
243
360
|
this.save(key, saved);
|
|
244
361
|
}
|
|
245
362
|
if (method.endsWith('.ack') || method === 'group.ack_messages') {
|
|
246
363
|
if (volatileGroup)
|
|
247
364
|
request.volatile_cursor = saved.volatile_cursor ?? '';
|
|
248
365
|
else {
|
|
249
|
-
const upto = request.up_to_seq ?? request.seq ?? 0;
|
|
366
|
+
const upto = request.up_to_seq ?? request.seq ?? request.msg_seq ?? 0;
|
|
250
367
|
if (!Number.isSafeInteger(upto) || upto < 0 || upto > Number(saved.after_seq ?? 0))
|
|
251
368
|
throw new ValidationError('control ACK exceeds durable cursor');
|
|
252
369
|
request.up_to_seq = upto;
|
|
253
|
-
request.scan_proof
|
|
370
|
+
delete request.scan_proof;
|
|
254
371
|
}
|
|
255
|
-
return await c._rpcPipeline.rawCall(ackMethod, request);
|
|
372
|
+
return await c._rpcPipeline.rawCall(ackMethod, request, ...(rpcOptions ? [rpcOptions] : []));
|
|
256
373
|
}
|
|
257
374
|
const history = method.endsWith('.history');
|
|
375
|
+
if (!history && messageClass === 'sdk_control')
|
|
376
|
+
releasePage = await this.acquirePubsubPage(identity);
|
|
377
|
+
if (!history && !group && messageClass === 'sdk_control' && this.blockedPubsubPulls.has(String(request.sender_aid)))
|
|
378
|
+
return { messages: [], _pubsub_backpressure: true, has_more: true };
|
|
258
379
|
delete request.window_mode;
|
|
259
380
|
delete request._rpc_background;
|
|
260
|
-
request.limit = Math.max(1, Math.min(Number(request.limit) || 50, 200));
|
|
381
|
+
request.limit = Math.max(1, Math.min(Number(request.limit) || 50, messageClass === 'sdk_control' ? 64 : 200));
|
|
261
382
|
const after = request.after_seq ?? saved.after_seq ?? 0;
|
|
262
383
|
if (!Number.isSafeInteger(after) || after < 0 || !history && after > Number(saved.after_seq ?? 0))
|
|
263
384
|
throw new ValidationError('control after_seq exceeds durable cursor');
|
|
@@ -267,8 +388,8 @@ export class ControlMessageCoordinator {
|
|
|
267
388
|
request.after_volatile_cursor ??= saved.volatile_cursor;
|
|
268
389
|
}
|
|
269
390
|
const wireMethod = history ? method : group ? 'group.v2.pull' : 'message.v2.pull';
|
|
270
|
-
const gateKey = `message_class/${messageClass}/${
|
|
271
|
-
const pull = () => c._rpcPipeline.runPullSerialized(gateKey, () => c._rpcPipeline.rawCall(wireMethod, request, { pullGateKey: gateKey }),
|
|
391
|
+
const gateKey = `message_class/${messageClass}/${group ? volatileGroup ? 'volatile_group' : 'group' : 'peer'}/${group ? request.group_id ?? request.group_aid : request.sender_aid}|forward|class=${messageClass}`;
|
|
392
|
+
const pull = () => c._rpcPipeline.runPullSerialized(gateKey, () => c._rpcPipeline.rawCall(wireMethod, request, { pullGateKey: gateKey, ...rpcOptions }), background);
|
|
272
393
|
let result;
|
|
273
394
|
try {
|
|
274
395
|
result = await pull();
|
|
@@ -289,10 +410,9 @@ export class ControlMessageCoordinator {
|
|
|
289
410
|
if (!volatileGroup && (!Number.isSafeInteger(head) || !Number.isSafeInteger(covered) || covered < 0 || covered > head
|
|
290
411
|
|| !history && covered < after || request.head_seq > 0 && head !== request.head_seq))
|
|
291
412
|
throw new StateError('control Pull coverage is invalid');
|
|
292
|
-
if (!history && !volatileGroup && covered > after && !(typeof result.scan_proof === 'string' && result.scan_proof))
|
|
293
|
-
throw new StateError('control Pull scan proof is missing');
|
|
294
413
|
const decoded = [], volatileCursors = [], volatileIds = [];
|
|
295
414
|
let previous = history ? 0 : after;
|
|
415
|
+
let backpressure = false, acceptedCovered = covered;
|
|
296
416
|
const rows = !group && !history
|
|
297
417
|
? [...result.messages].sort((left, right) => Number(object(right) && (right.storage === 'volatile' || Boolean(right.volatile_cursor)))
|
|
298
418
|
- Number(object(left) && (left.storage === 'volatile' || Boolean(left.volatile_cursor))))
|
|
@@ -306,8 +426,17 @@ export class ControlMessageCoordinator {
|
|
|
306
426
|
throw new StateError('control message outside verified coverage');
|
|
307
427
|
previous = row.seq;
|
|
308
428
|
}
|
|
309
|
-
|
|
429
|
+
const deadline = Number.isSafeInteger(row.expires_at) ? row.expires_at : Number.isFinite(row.t_server) && Number.isFinite(row.ttl) ? row.t_server + row.ttl * 1000 : Date.now() + 60_000;
|
|
430
|
+
const message = await this.decode(row, messageClass, group, request, ...(messageClass === 'sdk_control' ? [{ background: 'event', deadline }] : []));
|
|
431
|
+
if (messageClass !== 'sdk_control')
|
|
432
|
+
decoded.push(message);
|
|
310
433
|
this.check(identity);
|
|
434
|
+
if (!history && messageClass === 'sdk_control' && await this.consume(message, true) === false) {
|
|
435
|
+
backpressure = true;
|
|
436
|
+
if (!volatile)
|
|
437
|
+
acceptedCovered = Math.min(acceptedCovered, row.seq - 1);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
311
440
|
if (volatile && typeof row.volatile_cursor === 'string')
|
|
312
441
|
volatileCursors.push(row.volatile_cursor);
|
|
313
442
|
if (volatile && typeof row.volatile_id === 'string')
|
|
@@ -321,14 +450,12 @@ export class ControlMessageCoordinator {
|
|
|
321
450
|
}
|
|
322
451
|
}
|
|
323
452
|
for (const message of decoded) {
|
|
324
|
-
if (messageClass
|
|
325
|
-
await this.consume(message);
|
|
326
|
-
else
|
|
453
|
+
if (messageClass !== 'sdk_control')
|
|
327
454
|
await c._publishAppEvent(group ? 'group.message_created' : 'message.received', message, 'control_pull');
|
|
328
455
|
this.check(identity);
|
|
329
456
|
}
|
|
330
|
-
const next = { after_seq: Math.max(Number(saved.after_seq ?? 0),
|
|
331
|
-
volatile_cursor: result.volatile_cursor ?? result.next_volatile_cursor ?? volatileCursors.at(-1) ?? saved.volatile_cursor ?? '' };
|
|
457
|
+
const next = { after_seq: Math.max(Number(saved.after_seq ?? 0), acceptedCovered),
|
|
458
|
+
volatile_cursor: backpressure ? saved.volatile_cursor ?? '' : result.volatile_cursor ?? result.next_volatile_cursor ?? volatileCursors.at(-1) ?? saved.volatile_cursor ?? '' };
|
|
332
459
|
const ack = { message_class: messageClass, device_id: c._deviceId, slot_id: c._slotId };
|
|
333
460
|
if (group)
|
|
334
461
|
ack.group_id = request.group_id ?? request.group_aid;
|
|
@@ -337,73 +464,31 @@ export class ControlMessageCoordinator {
|
|
|
337
464
|
if (volatileGroup)
|
|
338
465
|
Object.assign(ack, { volatile: true, volatile_cursor: next.volatile_cursor });
|
|
339
466
|
else
|
|
340
|
-
Object.assign(ack, { up_to_seq: next.after_seq,
|
|
467
|
+
Object.assign(ack, { up_to_seq: next.after_seq,
|
|
341
468
|
...(volatileCursors.length ? { volatile_cursors: volatileCursors } : {}), ...(volatileIds.length ? { volatile_ids: volatileIds } : {}) });
|
|
342
469
|
const ackRequired = !volatileGroup || Boolean(next.volatile_cursor);
|
|
343
|
-
this.save(key, { ...next, ...(ackRequired ? { pending_ack: ack
|
|
470
|
+
this.save(key, { ...next, ...(ackRequired ? { pending_ack: ack } : {}) });
|
|
344
471
|
if (ackRequired)
|
|
345
|
-
await c._rpcPipeline.rawCall(ackMethod, ack);
|
|
472
|
+
await c._rpcPipeline.rawCall(ackMethod, ack, ...(rpcOptions ? [rpcOptions] : []));
|
|
346
473
|
this.check(identity);
|
|
347
474
|
if (ackRequired)
|
|
348
475
|
this.save(key, next);
|
|
349
476
|
}
|
|
350
|
-
return { ...result, messages: messageClass === 'sdk_control' ? [] : decoded, ack_seq:
|
|
477
|
+
return { ...result, messages: messageClass === 'sdk_control' ? [] : decoded, ack_seq: acceptedCovered, ...(backpressure ? { has_more: true, ...(this.blockedPubsubPulls.has(String(request.sender_aid)) ? { _pubsub_backpressure: true } : {}) } : {}) };
|
|
351
478
|
}
|
|
352
479
|
finally {
|
|
480
|
+
releasePage?.();
|
|
353
481
|
unlock();
|
|
354
482
|
if (this.locks.get(key) === lock)
|
|
355
483
|
this.locks.delete(key);
|
|
356
484
|
}
|
|
357
485
|
}
|
|
358
|
-
schedule(params) {
|
|
359
|
-
const group = Boolean(params.group_id ?? params.group_aid);
|
|
360
|
-
if (!this.supports(params.message_class, group))
|
|
361
|
-
return;
|
|
362
|
-
this.pending.set(this.key(params, group), { ...params });
|
|
363
|
-
const lane = params.message_class;
|
|
364
|
-
if (!this.workers.has(lane)) {
|
|
365
|
-
const identity = this.identity();
|
|
366
|
-
const worker = this.drain(identity, lane).finally(() => {
|
|
367
|
-
if (this.workers.get(lane) !== worker)
|
|
368
|
-
return;
|
|
369
|
-
this.workers.delete(lane);
|
|
370
|
-
const next = [...this.pending.values()].find((item) => item.message_class === lane);
|
|
371
|
-
if (next && !this.client._closing)
|
|
372
|
-
this.schedule(next);
|
|
373
|
-
});
|
|
374
|
-
this.workers.set(lane, worker);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
async drain(identity, lane) {
|
|
378
|
-
while (this.pending.size && identity === this.identity()) {
|
|
379
|
-
for (const higher of MESSAGE_CLASSES.slice(0, MESSAGE_CLASSES.indexOf(lane))) {
|
|
380
|
-
if (this.workers.has(higher)) {
|
|
381
|
-
await this.workers.get(higher);
|
|
382
|
-
if (identity !== this.identity() || this.client._closing)
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
const selected = [...this.pending.entries()].find(([, params]) => params.message_class === lane);
|
|
387
|
-
if (!selected)
|
|
388
|
-
break;
|
|
389
|
-
const [key, request] = selected;
|
|
390
|
-
this.pending.delete(key);
|
|
391
|
-
try {
|
|
392
|
-
const result = await this.call(request.group_id || request.group_aid ? 'group.v2.pull' : 'message.v2.pull', request);
|
|
393
|
-
this.check(identity);
|
|
394
|
-
if (result.has_more === true || result.volatile_has_more === true) {
|
|
395
|
-
if (result.head_seq > 0)
|
|
396
|
-
request.head_seq = result.head_seq;
|
|
397
|
-
this.pending.set(key, request);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
catch (error) {
|
|
401
|
-
this.client._clientLog.debug(`control pull deferred: ${String(error)}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
486
|
onPush(data, group = false) {
|
|
406
487
|
const messageClass = messageClassOf(data);
|
|
488
|
+
if (!group && object(data) && messageClass === 'sdk_control')
|
|
489
|
+
this.queuePubsubCandidate(data);
|
|
490
|
+
if (group && messageClass === 'sdk_control')
|
|
491
|
+
return true;
|
|
407
492
|
const volatile = group && object(data) && (data.storage === 'volatile' || data.volatile === true);
|
|
408
493
|
if (object(data) && data.delivery_class === 'normal' && messageClass !== 'sdk_control' && !(messageClass === 'normal' && volatile))
|
|
409
494
|
return false;
|
|
@@ -419,76 +504,26 @@ export class ControlMessageCoordinator {
|
|
|
419
504
|
}
|
|
420
505
|
else
|
|
421
506
|
params.sender_aid = data.sender_aid ?? data.from_aid ?? data.from ?? data.inline_message?.from_aid;
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
continue;
|
|
433
|
-
let cursor = '';
|
|
434
|
-
do {
|
|
435
|
-
const result = await this.call('message.peers', { message_class: messageClass, filter: 'pending', cursor });
|
|
436
|
-
this.check(identity);
|
|
437
|
-
for (const peer of result.peers) {
|
|
438
|
-
const sender = peer.sender_aid ?? peer.aid;
|
|
439
|
-
if (sender)
|
|
440
|
-
this.schedule({ message_class: messageClass, sender_aid: sender });
|
|
441
|
-
}
|
|
442
|
-
if (result.has_more !== true)
|
|
443
|
-
break;
|
|
444
|
-
if (typeof result.next_cursor !== 'string' || !result.next_cursor || result.next_cursor === cursor)
|
|
445
|
-
throw new StateError('control peer directory cursor did not advance');
|
|
446
|
-
cursor = result.next_cursor;
|
|
447
|
-
} while (cursor);
|
|
448
|
-
}
|
|
449
|
-
const classes = MESSAGE_CLASSES.filter((kind) => this.supports(kind, true));
|
|
450
|
-
if (classes.length) {
|
|
451
|
-
let cursor = '';
|
|
452
|
-
do {
|
|
453
|
-
const listing = await this.client._rpcPipeline.rawCall('group.list_my', { size: 200, cursor });
|
|
454
|
-
this.check(identity);
|
|
455
|
-
for (const entry of listing?.items ?? listing?.groups ?? []) {
|
|
456
|
-
if (object(entry) && entry.status && entry.status !== 'active')
|
|
457
|
-
continue;
|
|
458
|
-
const groupId = typeof entry === 'string' ? entry : entry.group_id ?? entry.group_aid;
|
|
459
|
-
if (!groupId)
|
|
460
|
-
continue;
|
|
461
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
462
|
-
this.check(identity);
|
|
463
|
-
for (const messageClass of classes) {
|
|
464
|
-
if (messageClass !== 'normal' && groupStreamNeedsPull(entry, messageClass, 'persistent'))
|
|
465
|
-
this.schedule({ message_class: messageClass, group_id: groupId });
|
|
466
|
-
if (groupStreamNeedsPull(entry, messageClass, 'volatile'))
|
|
467
|
-
this.schedule({ message_class: messageClass, group_id: groupId, volatile: true });
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
if (listing.has_more !== true)
|
|
471
|
-
break;
|
|
472
|
-
if (typeof listing.next_cursor !== 'string' || !listing.next_cursor || listing.next_cursor === cursor)
|
|
473
|
-
throw new StateError('control group directory cursor did not advance');
|
|
474
|
-
cursor = listing.next_cursor;
|
|
475
|
-
} while (cursor);
|
|
476
|
-
}
|
|
477
|
-
const c = this.client;
|
|
478
|
-
const saved = c._tokenStore.loadInstanceState?.(c._aid, c._deviceId, c._slotId)?.message_control_v1 ?? {};
|
|
479
|
-
for (const [key, value] of Object.entries(saved)) {
|
|
480
|
-
if (!object(value) || !value.pending_ack)
|
|
481
|
-
continue;
|
|
482
|
-
const [messageClass, kind, scope] = JSON.parse(key);
|
|
483
|
-
if (this.supports(messageClass, kind !== 'peer'))
|
|
484
|
-
this.schedule({ message_class: messageClass,
|
|
485
|
-
...(kind === 'peer' ? { sender_aid: scope } : { group_id: scope }), ...(kind === 'volatile_group' ? { volatile: true } : {}) });
|
|
507
|
+
if (!group && messageClass === 'sdk_control' && this.blockedPubsubPulls.has(String(params.sender_aid)))
|
|
508
|
+
return true;
|
|
509
|
+
if (params.group_id || params.sender_aid) {
|
|
510
|
+
const head = Math.max(...[data.seq, data.head_seq].filter((value) => Number.isSafeInteger(value) && value > 0), 0);
|
|
511
|
+
if (head > 0 && data.storage !== 'volatile' && data.volatile !== true && data.volatile_pending !== true && !data.volatile_cursor) {
|
|
512
|
+
const saved = this.load(this.key(params, group));
|
|
513
|
+
if (Number.isSafeInteger(saved.after_seq) && saved.after_seq >= head && !object(saved.pending_ack))
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
this.client._delivery.queueMessageClassPull(params);
|
|
486
517
|
}
|
|
518
|
+
return true;
|
|
487
519
|
}
|
|
488
520
|
close() {
|
|
489
521
|
this.epoch += 1;
|
|
490
|
-
this.
|
|
491
|
-
|
|
522
|
+
for (const waiter of this.pubsubPageWaiters.splice(0))
|
|
523
|
+
waiter.reject(new StateError('control pull invalidated'));
|
|
524
|
+
this.pubsubCandidates = [];
|
|
525
|
+
this.pubsubBytes = 0;
|
|
526
|
+
this.blockedPubsubPulls.clear();
|
|
492
527
|
}
|
|
493
528
|
}
|
|
494
529
|
//# sourceMappingURL=control-messages.js.map
|