@agentunion/fastaun-browser 0.5.16 → 0.5.17
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 +309 -274
- package/_packed_docs/CHANGELOG.md +309 -274
- package/_packed_docs/INDEX.md +8 -2
- package/_packed_docs/KITE_DOCS_GUIDE.md +2 -0
- package/_packed_docs/agent.md/SCHEMA.md +15 -15
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1178 -1178
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1213 -1213
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +58 -43
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +80 -17
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +27 -6
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +7 -4
- package/_packed_docs/sdk/09-group-rpc-manual.md +167 -35
- package/_packed_docs/sdk/09-message-rpc-manual.md +72 -42
- package/_packed_docs/sdk/09-storage-rpc-manual.md +23 -14
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +16 -12
- package/_packed_docs/sdk/INDEX.md +12 -12
- package/dist/agent-md.d.ts +2 -0
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +7 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/bundle.js +2274 -1789
- package/dist/client/delivery.d.ts +18 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +147 -5
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts +4 -0
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +63 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +37 -4
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +7 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +180 -25
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +60 -12
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +5 -0
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +15 -0
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.d.ts.map +1 -1
- package/dist/group-fs.js +8 -5
- package/dist/group-fs.js.map +1 -1
- package/dist/storage/vfs.d.ts +1 -0
- package/dist/storage/vfs.d.ts.map +1 -1
- package/dist/storage/vfs.js +68 -5
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +34 -3
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.js +34 -34
- package/dist/types.d.ts +16 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -10
package/dist/bundle.js
CHANGED
|
@@ -460,7 +460,7 @@ var init_indexeddb_store = __esm({
|
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
// src/version.ts
|
|
463
|
-
var VERSION = "0.5.
|
|
463
|
+
var VERSION = "0.5.17";
|
|
464
464
|
|
|
465
465
|
// src/types.ts
|
|
466
466
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
@@ -5139,6 +5139,7 @@ var PUSHED_SEQS_LIMIT = 5e4;
|
|
|
5139
5139
|
var PENDING_ORDERED_LIMIT = 5e4;
|
|
5140
5140
|
var MESSAGE_RECALL_SEEN_LIMIT = 1e4;
|
|
5141
5141
|
var GROUP_RECALL_SEEN_LIMIT = 1e4;
|
|
5142
|
+
var VOLATILE_SEEN_LIMIT = 1e4;
|
|
5142
5143
|
var SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
|
|
5143
5144
|
var P2P_GAP_FILL_RETRY_LIMIT = 12;
|
|
5144
5145
|
var P2P_GAP_FILL_RETRY_BASE_MS = 1e3;
|
|
@@ -5203,8 +5204,7 @@ var APP_GROUP_EVENT_ENVELOPE_KEYS = [
|
|
|
5203
5204
|
"timestamp",
|
|
5204
5205
|
"t_server",
|
|
5205
5206
|
"status",
|
|
5206
|
-
"device_id"
|
|
5207
|
-
"slot_id"
|
|
5207
|
+
"device_id"
|
|
5208
5208
|
];
|
|
5209
5209
|
var MAX_INLINE_PENDING_ACK_NAMESPACES = 4096;
|
|
5210
5210
|
function formatDeliveryError(error) {
|
|
@@ -5293,6 +5293,10 @@ var MessageDeliveryEngine = class {
|
|
|
5293
5293
|
__publicField(this, "pendingP2PInlineAcks", null);
|
|
5294
5294
|
__publicField(this, "pendingGroupInlineAcks", null);
|
|
5295
5295
|
__publicField(this, "pendingGroupInlineAckNamespaces", null);
|
|
5296
|
+
__publicField(this, "volatileContext", "");
|
|
5297
|
+
__publicField(this, "volatilePullCursorValue", "");
|
|
5298
|
+
__publicField(this, "volatileSeenMessages", /* @__PURE__ */ new Map());
|
|
5299
|
+
__publicField(this, "volatileInFlightMessages", /* @__PURE__ */ new Set());
|
|
5296
5300
|
// reset 后旧 inline Promise 只能自行结束,不能触碰新身份上下文的状态。
|
|
5297
5301
|
__publicField(this, "inlineGeneration", 0);
|
|
5298
5302
|
// 同一实例的 tracker 写入必须串行,避免旧的延迟 flush 覆盖新的 Forward 提交。
|
|
@@ -5970,6 +5974,10 @@ var MessageDeliveryEngine = class {
|
|
|
5970
5974
|
const client = this.runtime.client;
|
|
5971
5975
|
this.ensurePullOperationCurrent(ns);
|
|
5972
5976
|
const response = result;
|
|
5977
|
+
const volatileMessages = this.volatileMessages(response.messages);
|
|
5978
|
+
if (method === "message.pull" && volatileMessages.length > 0) {
|
|
5979
|
+
await this.publishPlainVolatileMessages(volatileMessages, "pull", true);
|
|
5980
|
+
}
|
|
5973
5981
|
const messages = deduplicatePlainForwardPageMessages(response.messages, afterSeq);
|
|
5974
5982
|
const coordinator = this.forwardCoordinator();
|
|
5975
5983
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
@@ -6169,14 +6177,19 @@ var MessageDeliveryEngine = class {
|
|
|
6169
6177
|
normalized = this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
6170
6178
|
} else if (this.isGroupScopedEvent(event)) {
|
|
6171
6179
|
payload = this.stripInlineInternalFields(payload);
|
|
6172
|
-
normalized = this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
|
|
6180
|
+
normalized = this.attachAppGroupEventEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
6173
6181
|
} else {
|
|
6174
6182
|
normalized = payload;
|
|
6175
6183
|
}
|
|
6184
|
+
if (this.isVolatileMessage(normalized) && isJsonObject(normalized)) {
|
|
6185
|
+
normalized = { ...normalized };
|
|
6186
|
+
delete normalized.seq;
|
|
6187
|
+
}
|
|
6176
6188
|
const canonicalSource = canonicalDeliverySource(source);
|
|
6177
6189
|
if (canonicalSource === "direct" || !isJsonObject(normalized)) return normalized;
|
|
6178
6190
|
if (!this.isInstanceScopedMessageEvent(event) && !this.isGroupScopedEvent(event)) return normalized;
|
|
6179
|
-
|
|
6191
|
+
normalized = { ...normalized, source: canonicalSource };
|
|
6192
|
+
return normalized;
|
|
6180
6193
|
}
|
|
6181
6194
|
stripInlineInternalFields(payload) {
|
|
6182
6195
|
if (!isJsonObject(payload)) return payload;
|
|
@@ -6317,10 +6330,13 @@ var MessageDeliveryEngine = class {
|
|
|
6317
6330
|
stripInternalSenderDeviceFields(payload) {
|
|
6318
6331
|
if (!isJsonObject(payload)) return payload;
|
|
6319
6332
|
const result = { ...payload };
|
|
6320
|
-
delete result.sender_device_id;
|
|
6321
6333
|
delete result._sender_device_id;
|
|
6322
6334
|
delete result.from_device_id;
|
|
6323
6335
|
delete result.from_device;
|
|
6336
|
+
delete result.slot_id;
|
|
6337
|
+
delete result.sender_slot_id;
|
|
6338
|
+
delete result.target_slot_id;
|
|
6339
|
+
delete result.owner_slot_id;
|
|
6324
6340
|
return result;
|
|
6325
6341
|
}
|
|
6326
6342
|
recallEventFromMessage(message) {
|
|
@@ -6373,6 +6389,112 @@ var MessageDeliveryEngine = class {
|
|
|
6373
6389
|
if (recall) return { event: "message.recalled", payload: recall };
|
|
6374
6390
|
return { event: "message.received", payload: message };
|
|
6375
6391
|
}
|
|
6392
|
+
isVolatileMessage(message) {
|
|
6393
|
+
return isJsonObject(message) && String(message.storage ?? "").trim() === "volatile";
|
|
6394
|
+
}
|
|
6395
|
+
volatileMessages(value) {
|
|
6396
|
+
if (!Array.isArray(value)) return [];
|
|
6397
|
+
return value.filter((item) => this.isVolatileMessage(item)).map((item) => ({ ...item }));
|
|
6398
|
+
}
|
|
6399
|
+
ensureVolatileContext() {
|
|
6400
|
+
const client = this.runtime.client;
|
|
6401
|
+
const context = JSON.stringify([client._aid ?? "", client._deviceId ?? "", client._slotId ?? ""]);
|
|
6402
|
+
if (context === this.volatileContext) return;
|
|
6403
|
+
this.volatileContext = context;
|
|
6404
|
+
this.volatilePullCursorValue = "";
|
|
6405
|
+
this.volatileSeenMessages.clear();
|
|
6406
|
+
this.volatileInFlightMessages.clear();
|
|
6407
|
+
}
|
|
6408
|
+
volatilePullCursor() {
|
|
6409
|
+
this.ensureVolatileContext();
|
|
6410
|
+
return this.volatilePullCursorValue;
|
|
6411
|
+
}
|
|
6412
|
+
resetVolatilePullCursor() {
|
|
6413
|
+
this.ensureVolatileContext();
|
|
6414
|
+
this.volatilePullCursorValue = "";
|
|
6415
|
+
}
|
|
6416
|
+
shouldResetVolatileCursor(cursor, error) {
|
|
6417
|
+
const text3 = error instanceof Error ? error.message : String(error);
|
|
6418
|
+
return Boolean(cursor) && cursor === this.volatilePullCursor() && text3.toLowerCase().includes("volatile cursor epoch mismatch");
|
|
6419
|
+
}
|
|
6420
|
+
volatileSeenKey(message) {
|
|
6421
|
+
const sender = String(message.from_aid ?? message.from ?? "").trim();
|
|
6422
|
+
const messageId = String(message.message_id ?? "").trim();
|
|
6423
|
+
const cursor = String(message.volatile_cursor ?? "").trim();
|
|
6424
|
+
return `${sender}|${messageId || cursor}`;
|
|
6425
|
+
}
|
|
6426
|
+
isVolatileSeen(message) {
|
|
6427
|
+
this.ensureVolatileContext();
|
|
6428
|
+
const now = Date.now();
|
|
6429
|
+
for (const [key, expiresAt] of this.volatileSeenMessages) {
|
|
6430
|
+
if (expiresAt <= now) this.volatileSeenMessages.delete(key);
|
|
6431
|
+
}
|
|
6432
|
+
return this.volatileSeenMessages.has(this.volatileSeenKey(message));
|
|
6433
|
+
}
|
|
6434
|
+
markVolatileSeen(message) {
|
|
6435
|
+
this.ensureVolatileContext();
|
|
6436
|
+
const now = Date.now();
|
|
6437
|
+
const expiresAt = Math.max(now + 6e4, Number(message.expires_at ?? now + 18e5) || now + 18e5);
|
|
6438
|
+
this.volatileSeenMessages.set(this.volatileSeenKey(message), expiresAt);
|
|
6439
|
+
while (this.volatileSeenMessages.size > VOLATILE_SEEN_LIMIT) {
|
|
6440
|
+
const oldest = this.volatileSeenMessages.keys().next().value;
|
|
6441
|
+
if (oldest === void 0) break;
|
|
6442
|
+
this.volatileSeenMessages.delete(oldest);
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
beginVolatileDelivery(message) {
|
|
6446
|
+
if (this.isVolatileSeen(message)) return "seen";
|
|
6447
|
+
const key = this.volatileSeenKey(message);
|
|
6448
|
+
if (this.volatileInFlightMessages.has(key)) return "inflight";
|
|
6449
|
+
this.volatileInFlightMessages.add(key);
|
|
6450
|
+
return "claimed";
|
|
6451
|
+
}
|
|
6452
|
+
finishVolatileDelivery(message) {
|
|
6453
|
+
this.ensureVolatileContext();
|
|
6454
|
+
this.volatileInFlightMessages.delete(this.volatileSeenKey(message));
|
|
6455
|
+
}
|
|
6456
|
+
publicVolatileMessage(message) {
|
|
6457
|
+
const result = this.stripInternalSenderDeviceFields(message);
|
|
6458
|
+
delete result.seq;
|
|
6459
|
+
return result;
|
|
6460
|
+
}
|
|
6461
|
+
async ackVolatileMessages(messages, v2, advanceCursor) {
|
|
6462
|
+
const cursors = [...new Set(messages.map((item) => String(item.volatile_cursor ?? "").trim()).filter(Boolean))];
|
|
6463
|
+
if (cursors.length === 0) return void 0;
|
|
6464
|
+
const method = v2 ? "message.v2.ack" : "message.ack";
|
|
6465
|
+
const result = await this.runtime.client._rpcPipeline.rawCall(
|
|
6466
|
+
method,
|
|
6467
|
+
{ volatile_cursors: cursors },
|
|
6468
|
+
{ background: true }
|
|
6469
|
+
);
|
|
6470
|
+
if (advanceCursor) {
|
|
6471
|
+
this.ensureVolatileContext();
|
|
6472
|
+
this.volatilePullCursorValue = cursors[cursors.length - 1];
|
|
6473
|
+
}
|
|
6474
|
+
return result;
|
|
6475
|
+
}
|
|
6476
|
+
async publishPlainVolatileMessages(messages, source, advanceCursor) {
|
|
6477
|
+
const processed = [];
|
|
6478
|
+
for (const rawMessage of messages) {
|
|
6479
|
+
const message = this.publicVolatileMessage(rawMessage);
|
|
6480
|
+
const claim = this.beginVolatileDelivery(message);
|
|
6481
|
+
if (claim === "seen") {
|
|
6482
|
+
processed.push(rawMessage);
|
|
6483
|
+
continue;
|
|
6484
|
+
}
|
|
6485
|
+
if (claim === "inflight") continue;
|
|
6486
|
+
try {
|
|
6487
|
+
const appEvent = this.p2pAppEventForMessage(message);
|
|
6488
|
+
await this.runtime.client._publishAppEvent(appEvent.event, appEvent.payload, source);
|
|
6489
|
+
this.markVolatileSeen(message);
|
|
6490
|
+
} finally {
|
|
6491
|
+
this.finishVolatileDelivery(message);
|
|
6492
|
+
}
|
|
6493
|
+
processed.push(rawMessage);
|
|
6494
|
+
}
|
|
6495
|
+
if (processed.length > 0) await this.ackVolatileMessages(processed, false, advanceCursor);
|
|
6496
|
+
return processed.map((item) => this.publicVolatileMessage(item));
|
|
6497
|
+
}
|
|
6376
6498
|
messageRecallDedupKey(payload) {
|
|
6377
6499
|
const ids = payload.message_ids;
|
|
6378
6500
|
const idPart = Array.isArray(ids) ? ids.map((i) => String(i ?? "").trim()).filter(Boolean).sort().join(",") : String(ids ?? "").trim();
|
|
@@ -6662,7 +6784,7 @@ var MessageDeliveryEngine = class {
|
|
|
6662
6784
|
client._clientLog.debug(`_onRawMessageReceived enter: from=${data?.from ?? "-"} mid=${data?.message_id ?? "-"} seq=${data?.seq ?? "-"}`);
|
|
6663
6785
|
const ns = isJsonObject(data) && client._aid && data.seq !== void 0 ? `p2p:${client._aid}` : "";
|
|
6664
6786
|
const seq2 = isJsonObject(data) ? positiveSafeSequenceHint(data.seq) : 0;
|
|
6665
|
-
if (client._v2Session && ns && seq2 > 0 && this.messageTargetsCurrentInstance(data)) {
|
|
6787
|
+
if (client._v2Session && ns && seq2 > 0 && !this.isVolatileMessage(data) && this.messageTargetsCurrentInstance(data)) {
|
|
6666
6788
|
client._seqTracker.updateMaxSeen(ns, seq2);
|
|
6667
6789
|
}
|
|
6668
6790
|
const hasInlineObject = isJsonObject(data) && hasNonEmptyInlineMessage(data.inline_message);
|
|
@@ -6696,6 +6818,10 @@ var MessageDeliveryEngine = class {
|
|
|
6696
6818
|
if (!this.messageTargetsCurrentInstance(msg)) {
|
|
6697
6819
|
return;
|
|
6698
6820
|
}
|
|
6821
|
+
if (this.isVolatileMessage(msg)) {
|
|
6822
|
+
await this.publishPlainVolatileMessages([msg], "push", false);
|
|
6823
|
+
return;
|
|
6824
|
+
}
|
|
6699
6825
|
const legacyPushSeq = positiveSafeSequenceHint(msg.seq);
|
|
6700
6826
|
const hasInlineField = hasNonEmptyInlineMessage(msg.inline_message);
|
|
6701
6827
|
if (client._v2Session && client._aid && (legacyPushSeq > 0 || hasInlineField)) {
|
|
@@ -7826,6 +7952,10 @@ var MessageDeliveryEngine = class {
|
|
|
7826
7952
|
const client = this.runtime.client;
|
|
7827
7953
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
7828
7954
|
if (!ns) return;
|
|
7955
|
+
if (this.isVolatileMessage(data)) {
|
|
7956
|
+
await client._v2E2EE.publishV2VolatileMessages([data], "push", false);
|
|
7957
|
+
return;
|
|
7958
|
+
}
|
|
7829
7959
|
if (isJsonObject(data) && String(data.kind ?? "").trim() === "message.online_unread_hint") {
|
|
7830
7960
|
if (client._sessionOptions?.background_sync === false) return;
|
|
7831
7961
|
this.enqueueOnlineUnreadP2pHint(data);
|
|
@@ -15912,6 +16042,9 @@ var SIGNED_METHODS = /* @__PURE__ */ new Set([
|
|
|
15912
16042
|
"group.commit_state",
|
|
15913
16043
|
"group.ban",
|
|
15914
16044
|
"group.unban",
|
|
16045
|
+
"group.block",
|
|
16046
|
+
"group.unblock",
|
|
16047
|
+
"group.report_status",
|
|
15915
16048
|
"group.dissolve",
|
|
15916
16049
|
"group.suspend",
|
|
15917
16050
|
"group.resume"
|
|
@@ -16084,12 +16217,22 @@ var RpcPipeline = class {
|
|
|
16084
16217
|
async call(method, params2) {
|
|
16085
16218
|
const client = this.runtime.client;
|
|
16086
16219
|
const tStart = Date.now();
|
|
16220
|
+
const volatileCursor = method === "message.pull" ? String(params2?.after_volatile_cursor ?? client._delivery.volatilePullCursor() ?? "").trim() : "";
|
|
16087
16221
|
client._clientLog.debug(`call enter: method=${method}`);
|
|
16088
16222
|
try {
|
|
16089
16223
|
const result = await this.callImpl(method, params2);
|
|
16090
16224
|
client._clientLog.debug(`call exit: elapsed=${Date.now() - tStart}ms method=${method}`);
|
|
16091
16225
|
return result;
|
|
16092
16226
|
} catch (err) {
|
|
16227
|
+
if (method === "message.pull" && client._delivery.shouldResetVolatileCursor(volatileCursor, err)) {
|
|
16228
|
+
client._delivery.resetVolatilePullCursor();
|
|
16229
|
+
const retryParams = { ...params2 ?? {} };
|
|
16230
|
+
delete retryParams.after_volatile_cursor;
|
|
16231
|
+
delete retryParams.client_signature;
|
|
16232
|
+
const result = await this.callImpl(method, retryParams);
|
|
16233
|
+
client._clientLog.debug(`call exit: elapsed=${Date.now() - tStart}ms method=${method}`);
|
|
16234
|
+
return result;
|
|
16235
|
+
}
|
|
16093
16236
|
client._clientLog.debug(`call exit (error): elapsed=${Date.now() - tStart}ms method=${method} err=${err instanceof Error ? err.message : String(err)}`);
|
|
16094
16237
|
throw err;
|
|
16095
16238
|
}
|
|
@@ -16119,6 +16262,8 @@ var RpcPipeline = class {
|
|
|
16119
16262
|
return await client._sendV2(String(p.to ?? ""), p.payload ?? {}, {
|
|
16120
16263
|
messageId: String(p.message_id ?? "") || void 0,
|
|
16121
16264
|
timestamp: p.timestamp,
|
|
16265
|
+
ttl: p.ttl,
|
|
16266
|
+
deviceId: String(p.device_id ?? "").trim() || String(p.to_device_id ?? "").trim() || void 0,
|
|
16122
16267
|
protectedHeaders: client._protectedHeadersFromParams(p),
|
|
16123
16268
|
context: isJsonObject(p.context) ? p.context : void 0
|
|
16124
16269
|
});
|
|
@@ -16193,7 +16338,8 @@ var RpcPipeline = class {
|
|
|
16193
16338
|
if (p.window_mode === "tail") return await client._v2E2EE.pullV2TailInternal(p);
|
|
16194
16339
|
const messages = await client._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, {
|
|
16195
16340
|
gateLocked: true,
|
|
16196
|
-
force: p.force === true
|
|
16341
|
+
force: p.force === true,
|
|
16342
|
+
afterVolatileCursor: String(p.after_volatile_cursor ?? "").trim() || void 0
|
|
16197
16343
|
});
|
|
16198
16344
|
return { messages };
|
|
16199
16345
|
} catch (exc) {
|
|
@@ -16210,7 +16356,17 @@ var RpcPipeline = class {
|
|
|
16210
16356
|
if (method === "message.v2.ack" || method === "message.ack" && hasMatchingV2Session(client)) {
|
|
16211
16357
|
await client._ensureV2SessionReady("message.ack");
|
|
16212
16358
|
client._clientLog.debug(`call route: ${method} -> V2 ack`);
|
|
16213
|
-
|
|
16359
|
+
const volatileCursors = Array.isArray(p.volatile_cursors) ? p.volatile_cursors.map((value) => String(value ?? "").trim()).filter(Boolean) : String(p.volatile_cursor ?? "").trim() ? [String(p.volatile_cursor).trim()] : [];
|
|
16360
|
+
const volatileIds = Array.isArray(p.volatile_ids) ? p.volatile_ids.map((value) => String(value ?? "").trim()).filter(Boolean) : [];
|
|
16361
|
+
if (volatileCursors.length === 0 && volatileIds.length === 0) {
|
|
16362
|
+
return await client._ackV2(p.seq ?? p.up_to_seq);
|
|
16363
|
+
}
|
|
16364
|
+
return await client._ackV2(
|
|
16365
|
+
p.seq ?? p.up_to_seq ?? 0,
|
|
16366
|
+
void 0,
|
|
16367
|
+
volatileCursors,
|
|
16368
|
+
volatileIds
|
|
16369
|
+
);
|
|
16214
16370
|
}
|
|
16215
16371
|
if ((method === "group.v2.pull" || method === "group.pull") && p.group_id && (method === "group.v2.pull" || hasMatchingV2Session(client))) {
|
|
16216
16372
|
await client._ensureV2SessionReady("group.pull");
|
|
@@ -16378,7 +16534,7 @@ var RpcPipeline = class {
|
|
|
16378
16534
|
}
|
|
16379
16535
|
}
|
|
16380
16536
|
injectMessageCursorContext(method, params2) {
|
|
16381
|
-
if (
|
|
16537
|
+
if (!["message.pull", "message.v2.pull", "message.ack", "message.v2.ack"].includes(method)) {
|
|
16382
16538
|
return;
|
|
16383
16539
|
}
|
|
16384
16540
|
const client = this.runtime.client;
|
|
@@ -16391,6 +16547,10 @@ var RpcPipeline = class {
|
|
|
16391
16547
|
}
|
|
16392
16548
|
params2.device_id = client._deviceId;
|
|
16393
16549
|
params2.slot_id = client._slotId;
|
|
16550
|
+
if ((method === "message.pull" || method === "message.v2.pull") && params2.after_volatile_cursor === void 0) {
|
|
16551
|
+
const volatileCursor = client._delivery.volatilePullCursor();
|
|
16552
|
+
if (volatileCursor) params2.after_volatile_cursor = volatileCursor;
|
|
16553
|
+
}
|
|
16394
16554
|
}
|
|
16395
16555
|
async applyClientSignature(method, params2) {
|
|
16396
16556
|
try {
|
|
@@ -16462,7 +16622,7 @@ var RpcPipeline = class {
|
|
|
16462
16622
|
const mode = method === "message.history" ? "history" : params2.window_mode === "tail" ? "tail" : "forward";
|
|
16463
16623
|
const cursor = mode === "history" ? `before=${String(params2.before_seq)}` : `after=${String(params2.after_seq ?? 0)}`;
|
|
16464
16624
|
const limit = mode === "forward" ? Number(params2.limit ?? 50) || 50 : params2.limit;
|
|
16465
|
-
return `p2p:${client._aid}|mode=${mode}|cursor=${cursor}|force=${String(Boolean(params2.force))}|limit=${String(limit)}`;
|
|
16625
|
+
return `p2p:${client._aid}|mode=${mode}|cursor=${cursor}|volatile=${String(params2.after_volatile_cursor ?? "")}|force=${String(Boolean(params2.force))}|limit=${String(limit)}`;
|
|
16466
16626
|
}
|
|
16467
16627
|
if (method === "group.pull" || method === "group.v2.pull" || method === "group.history") {
|
|
16468
16628
|
const gid = String(params2.group_id ?? params2.group_aid ?? "").trim();
|
|
@@ -17003,6 +17163,9 @@ var RpcPipeline = class {
|
|
|
17003
17163
|
this.postprocessGroupPull(params2, next);
|
|
17004
17164
|
}
|
|
17005
17165
|
}
|
|
17166
|
+
if ((method === "message.pull" || method === "group.pull") && isJsonObject(next) && Array.isArray(next.messages)) {
|
|
17167
|
+
next.messages = next.messages.map((message) => isJsonObject(message) && client._delivery ? client._delivery.stripInternalSenderDeviceFields(message) : message);
|
|
17168
|
+
}
|
|
17006
17169
|
if (!options.skipGroupState) {
|
|
17007
17170
|
next = await client._groupState.postprocessResult(method, params2, next);
|
|
17008
17171
|
}
|
|
@@ -17683,721 +17846,1335 @@ var StorageLowLevel = class {
|
|
|
17683
17846
|
}
|
|
17684
17847
|
};
|
|
17685
17848
|
|
|
17686
|
-
// src/
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
function isExplicitLocalPath(value) {
|
|
17691
|
-
return String(value || "").trim().toLowerCase().startsWith(LOCAL_PREFIX);
|
|
17849
|
+
// src/storage/types.ts
|
|
17850
|
+
function text(value, fallback = "") {
|
|
17851
|
+
if (value === null || value === void 0) return fallback;
|
|
17852
|
+
return String(value);
|
|
17692
17853
|
}
|
|
17693
|
-
function
|
|
17694
|
-
const
|
|
17695
|
-
return
|
|
17854
|
+
function intValue(value, fallback = 0) {
|
|
17855
|
+
const parsed = Number(value);
|
|
17856
|
+
return Number.isFinite(parsed) ? Math.trunc(parsed) : fallback;
|
|
17696
17857
|
}
|
|
17697
|
-
function
|
|
17698
|
-
if (
|
|
17699
|
-
return
|
|
17858
|
+
function boolValue(value, fallback = false) {
|
|
17859
|
+
if (value === null || value === void 0) return fallback;
|
|
17860
|
+
return Boolean(value);
|
|
17700
17861
|
}
|
|
17701
|
-
function
|
|
17702
|
-
const
|
|
17703
|
-
if (
|
|
17704
|
-
|
|
17705
|
-
if (
|
|
17706
|
-
|
|
17707
|
-
return GROUP_REF_RE.test(text3);
|
|
17862
|
+
function mountSourceFromRaw(raw) {
|
|
17863
|
+
const explicit = text(raw.mount_source);
|
|
17864
|
+
if (explicit) return explicit;
|
|
17865
|
+
const sourceAid = text(raw.source_aid);
|
|
17866
|
+
if (!sourceAid) return "";
|
|
17867
|
+
return `${sourceAid}:${keyToPath(raw.source_path)}`;
|
|
17708
17868
|
}
|
|
17709
|
-
function
|
|
17710
|
-
|
|
17711
|
-
|
|
17712
|
-
|
|
17869
|
+
function normalizePath(path) {
|
|
17870
|
+
let raw = String(path || "/").replace(/\\/g, "/").trim();
|
|
17871
|
+
if (!raw.startsWith("/")) raw = `/${raw}`;
|
|
17872
|
+
raw = raw.replace(/\/+/g, "/");
|
|
17873
|
+
const parts = [];
|
|
17874
|
+
for (const part of raw.split("/")) {
|
|
17875
|
+
if (!part || part === ".") continue;
|
|
17876
|
+
if (part === "..") {
|
|
17877
|
+
parts.pop();
|
|
17878
|
+
continue;
|
|
17879
|
+
}
|
|
17880
|
+
parts.push(part);
|
|
17713
17881
|
}
|
|
17714
|
-
return
|
|
17882
|
+
return parts.length ? `/${parts.join("/")}` : "/";
|
|
17715
17883
|
}
|
|
17716
|
-
|
|
17717
|
-
const
|
|
17718
|
-
|
|
17719
|
-
throw new Error(loaded?.error?.message || `signer identity not found: ${signAs}`);
|
|
17720
|
-
}
|
|
17721
|
-
if (!loaded.data.aid.privateKeyPem) {
|
|
17722
|
-
throw new Error(`signer identity missing private key: ${signAs}`);
|
|
17723
|
-
}
|
|
17724
|
-
return loaded.data.aid;
|
|
17884
|
+
function pathToKey(path) {
|
|
17885
|
+
const normalized = normalizePath(path);
|
|
17886
|
+
return normalized === "/" ? "" : normalized.slice(1);
|
|
17725
17887
|
}
|
|
17726
|
-
function
|
|
17727
|
-
return
|
|
17888
|
+
function keyToPath(key) {
|
|
17889
|
+
return normalizePath(text(key));
|
|
17728
17890
|
}
|
|
17729
|
-
function
|
|
17730
|
-
const
|
|
17731
|
-
if (
|
|
17732
|
-
|
|
17733
|
-
if (token) return token;
|
|
17734
|
-
}
|
|
17735
|
-
const directToken = text(authClient.accessToken ?? authClient.access_token);
|
|
17736
|
-
if (directToken) return directToken;
|
|
17737
|
-
const identityToken = text(authClient._identity?.access_token);
|
|
17738
|
-
if (identityToken) return identityToken;
|
|
17739
|
-
return text(authClient._sessionParams?.access_token);
|
|
17891
|
+
function nameFromPath(path) {
|
|
17892
|
+
const cleaned = normalizePath(path).replace(/\/+$/g, "");
|
|
17893
|
+
if (!cleaned || cleaned === "/") return "/";
|
|
17894
|
+
return cleaned.split("/").pop() || "/";
|
|
17740
17895
|
}
|
|
17741
|
-
function
|
|
17742
|
-
|
|
17743
|
-
|
|
17896
|
+
function baseNode(raw, type, path) {
|
|
17897
|
+
return {
|
|
17898
|
+
type,
|
|
17899
|
+
path,
|
|
17900
|
+
name: text(raw.name) || nameFromPath(path),
|
|
17901
|
+
owner: text(raw.owner ?? raw.owner_aid),
|
|
17902
|
+
bucket: text(raw.bucket, "default") || "default",
|
|
17903
|
+
size: intValue(raw.size ?? raw.size_bytes),
|
|
17904
|
+
mtime: intValue(raw.mtime ?? raw.updated_at),
|
|
17905
|
+
contentType: text(raw.content_type),
|
|
17906
|
+
version: intValue(raw.version),
|
|
17907
|
+
mode: text(raw.mode),
|
|
17908
|
+
isPublic: !boolValue(raw.is_private, true),
|
|
17909
|
+
objectId: text(raw.object_id),
|
|
17910
|
+
folderId: text(raw.folder_id),
|
|
17911
|
+
target: text(raw.target),
|
|
17912
|
+
mountSource: mountSourceFromRaw(raw),
|
|
17913
|
+
metadata: raw.metadata && typeof raw.metadata === "object" && !Array.isArray(raw.metadata) ? raw.metadata : {}
|
|
17914
|
+
};
|
|
17744
17915
|
}
|
|
17745
|
-
function
|
|
17746
|
-
|
|
17916
|
+
function nodeFromAny(input) {
|
|
17917
|
+
const raw = input && typeof input === "object" ? input : {};
|
|
17918
|
+
const nodeType = text(raw.type ?? raw.node_type).toLowerCase();
|
|
17919
|
+
if (["folder", "dir", "directory"].includes(nodeType)) return baseNode(raw, "dir", keyToPath(raw.path));
|
|
17920
|
+
if (["symlink", "link"].includes(nodeType)) return baseNode(raw, "symlink", keyToPath(raw.path));
|
|
17921
|
+
if (nodeType === "mount") return baseNode(raw, "mount", keyToPath(raw.path));
|
|
17922
|
+
const path = keyToPath(raw.path ?? raw.object_key);
|
|
17923
|
+
return {
|
|
17924
|
+
...baseNode(raw, "file", path),
|
|
17925
|
+
sha256: text(raw.sha256),
|
|
17926
|
+
etag: text(raw.etag)
|
|
17927
|
+
};
|
|
17928
|
+
}
|
|
17929
|
+
function usageFromAny(input, ownerFallback = "") {
|
|
17930
|
+
const raw = input && typeof input === "object" ? input : {};
|
|
17931
|
+
const quota = intValue(raw.quota_bytes ?? raw.quota_total_bytes);
|
|
17932
|
+
const used = intValue(raw.used_bytes ?? raw.quota_used_bytes);
|
|
17933
|
+
return {
|
|
17934
|
+
owner: text(raw.owner ?? raw.owner_aid, ownerFallback),
|
|
17935
|
+
quotaBytes: quota,
|
|
17936
|
+
usedBytes: used,
|
|
17937
|
+
availBytes: quota ? Math.max(0, quota - used) : 0,
|
|
17938
|
+
objectCount: intValue(raw.object_count)
|
|
17939
|
+
};
|
|
17747
17940
|
}
|
|
17941
|
+
function unmountFromAny(input, fallback) {
|
|
17942
|
+
const raw = input && typeof input === "object" ? input : {};
|
|
17943
|
+
const path = keyToPath(raw.mount_path ?? raw.path ?? fallback.mountPath);
|
|
17944
|
+
return {
|
|
17945
|
+
unmounted: boolValue(raw.unmounted),
|
|
17946
|
+
owner: text(raw.owner ?? raw.owner_aid, fallback.owner ?? ""),
|
|
17947
|
+
bucket: text(raw.bucket, fallback.bucket ?? "default") || (fallback.bucket ?? "default"),
|
|
17948
|
+
path,
|
|
17949
|
+
mountPath: path
|
|
17950
|
+
};
|
|
17951
|
+
}
|
|
17952
|
+
|
|
17953
|
+
// src/storage/vfs.ts
|
|
17748
17954
|
async function sha256Hex(data) {
|
|
17955
|
+
const copy = new Uint8Array(data.byteLength);
|
|
17956
|
+
copy.set(data);
|
|
17749
17957
|
const subtle = globalThis.crypto?.subtle;
|
|
17750
17958
|
if (!subtle?.digest) throw new StorageError("SHA-256 unavailable", "EUNSUPPORTED");
|
|
17751
|
-
const digest = await subtle.digest("SHA-256",
|
|
17959
|
+
const digest = await subtle.digest("SHA-256", copy.buffer);
|
|
17752
17960
|
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
17753
17961
|
}
|
|
17754
|
-
|
|
17755
|
-
if (typeof
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
if (typeof blob.stream === "function" && typeof Response !== "undefined") {
|
|
17759
|
-
return new Uint8Array(await new Response(blob).arrayBuffer());
|
|
17760
|
-
}
|
|
17761
|
-
if (typeof FileReader !== "undefined") {
|
|
17762
|
-
return await new Promise((resolve, reject) => {
|
|
17763
|
-
const reader = new FileReader();
|
|
17764
|
-
reader.onload = () => resolve(new Uint8Array(reader.result));
|
|
17765
|
-
reader.onerror = () => reject(reader.error ?? new StorageError("Blob read failed", "ESTORAGE"));
|
|
17766
|
-
reader.readAsArrayBuffer(blob);
|
|
17767
|
-
});
|
|
17768
|
-
}
|
|
17769
|
-
if (typeof blob.text === "function") {
|
|
17770
|
-
return new TextEncoder().encode(await blob.text());
|
|
17771
|
-
}
|
|
17772
|
-
throw new StorageError("Blob reads are unavailable in this JavaScript runtime", "EUNSUPPORTED");
|
|
17773
|
-
}
|
|
17774
|
-
async function toBytes(source) {
|
|
17775
|
-
if (typeof source === "string") return new TextEncoder().encode(source);
|
|
17776
|
-
if (source instanceof Uint8Array) return source;
|
|
17777
|
-
if (source instanceof ArrayBuffer) return new Uint8Array(source);
|
|
17778
|
-
if (typeof Blob !== "undefined" && source instanceof Blob) {
|
|
17779
|
-
return blobToBytes(source);
|
|
17780
|
-
}
|
|
17781
|
-
if (ArrayBuffer.isView(source)) {
|
|
17782
|
-
return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
17783
|
-
}
|
|
17784
|
-
throw new StorageError("unsupported group fs upload source", "EUNSUPPORTED");
|
|
17962
|
+
function toBytes(data) {
|
|
17963
|
+
if (typeof data === "string") return new TextEncoder().encode(data);
|
|
17964
|
+
if (data instanceof Uint8Array) return data;
|
|
17965
|
+
return new Uint8Array(data);
|
|
17785
17966
|
}
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
const
|
|
17789
|
-
|
|
17967
|
+
function contentTypeForPath(path) {
|
|
17968
|
+
const ext = path.toLowerCase().split(/[?#]/, 1)[0].match(/\.[^./\\]+$/)?.[0] ?? "";
|
|
17969
|
+
const known = {
|
|
17970
|
+
".txt": "text/plain",
|
|
17971
|
+
".json": "application/json",
|
|
17972
|
+
".md": "text/markdown",
|
|
17973
|
+
".html": "text/html",
|
|
17974
|
+
".csv": "text/csv",
|
|
17975
|
+
".png": "image/png",
|
|
17976
|
+
".jpg": "image/jpeg",
|
|
17977
|
+
".jpeg": "image/jpeg",
|
|
17978
|
+
".gif": "image/gif",
|
|
17979
|
+
".webp": "image/webp",
|
|
17980
|
+
".pdf": "application/pdf"
|
|
17981
|
+
};
|
|
17982
|
+
return known[ext] ?? "application/octet-stream";
|
|
17983
|
+
}
|
|
17984
|
+
function contentTypeForData(data, path = "") {
|
|
17985
|
+
const startsWith = (...bytes) => bytes.every((value, index) => data[index] === value);
|
|
17986
|
+
if (startsWith(137, 80, 78, 71, 13, 10, 26, 10)) return "image/png";
|
|
17987
|
+
if (startsWith(255, 216, 255)) return "image/jpeg";
|
|
17988
|
+
if (startsWith(71, 73, 70, 56)) return "image/gif";
|
|
17989
|
+
if (startsWith(66, 77)) return "image/bmp";
|
|
17990
|
+
if (startsWith(37, 80, 68, 70, 45)) return "application/pdf";
|
|
17991
|
+
if (startsWith(31, 139)) return "application/gzip";
|
|
17992
|
+
if (startsWith(80, 75, 3, 4) || startsWith(80, 75, 5, 6) || startsWith(80, 75, 7, 8)) return "application/zip";
|
|
17993
|
+
if (data.length >= 12 && startsWith(82, 73, 70, 70) && data[8] === 87 && data[9] === 69 && data[10] === 66 && data[11] === 80) return "image/webp";
|
|
17994
|
+
if (data.length >= 12 && data[4] === 102 && data[5] === 116 && data[6] === 121 && data[7] === 112) return "video/mp4";
|
|
17995
|
+
if (data.length === 0) return contentTypeForPath(path);
|
|
17996
|
+
if (!data.some((value) => value < 9 || value > 13 && value < 32 || value === 127)) {
|
|
17790
17997
|
try {
|
|
17791
|
-
|
|
17998
|
+
const text3 = new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
17999
|
+
const trimmed = text3.trimStart();
|
|
18000
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
18001
|
+
try {
|
|
18002
|
+
JSON.parse(text3);
|
|
18003
|
+
return "application/json";
|
|
18004
|
+
} catch {
|
|
18005
|
+
}
|
|
18006
|
+
}
|
|
18007
|
+
if (/^<!doctype\s+html\b|^<html\b/i.test(trimmed)) return "text/html";
|
|
18008
|
+
if (text3.trim()) return "text/plain";
|
|
17792
18009
|
} catch {
|
|
17793
18010
|
}
|
|
17794
18011
|
}
|
|
17795
|
-
|
|
17796
|
-
const requireFn = Function('return typeof require !== "undefined" ? require : undefined');
|
|
17797
|
-
const requireModule = requireFn();
|
|
17798
|
-
if (requireModule) return requireModule(specifier);
|
|
17799
|
-
} catch {
|
|
17800
|
-
}
|
|
17801
|
-
try {
|
|
17802
|
-
const dynamicImport = Function(`return import(${JSON.stringify(specifier)})`);
|
|
17803
|
-
return await dynamicImport();
|
|
17804
|
-
} catch {
|
|
17805
|
-
return null;
|
|
17806
|
-
}
|
|
18012
|
+
return contentTypeForPath(path);
|
|
17807
18013
|
}
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
return new Uint8Array(await fs.readFile(path));
|
|
18014
|
+
function textValue(value) {
|
|
18015
|
+
if (value === void 0 || value === null) return "";
|
|
18016
|
+
return String(value).trim();
|
|
17812
18017
|
}
|
|
17813
|
-
|
|
17814
|
-
const
|
|
17815
|
-
const
|
|
17816
|
-
|
|
17817
|
-
await fs.mkdir(pathMod.dirname(path), { recursive: true });
|
|
17818
|
-
try {
|
|
17819
|
-
await fs.writeFile(path, data, force ? void 0 : { flag: "wx" });
|
|
17820
|
-
return true;
|
|
17821
|
-
} catch (exc) {
|
|
17822
|
-
const code = text(exc.code);
|
|
17823
|
-
if (code === "EEXIST") throw new StorageExistsError(`local path already exists: ${path}`, "EEXIST", path);
|
|
17824
|
-
throw exc;
|
|
17825
|
-
}
|
|
18018
|
+
function aidIssuer(aid) {
|
|
18019
|
+
const value = textValue(aid).replace(/^\.+|\.+$/g, "").toLowerCase();
|
|
18020
|
+
const separator = value.indexOf(".");
|
|
18021
|
+
return separator >= 0 ? value.slice(separator + 1).replace(/^\.+|\.+$/g, "") : "";
|
|
17826
18022
|
}
|
|
17827
|
-
|
|
17828
|
-
const
|
|
17829
|
-
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
return info.isDirectory() ? "dir" : "file";
|
|
17833
|
-
} catch (exc) {
|
|
17834
|
-
const code = text(exc.code);
|
|
17835
|
-
if (code === "ENOENT") return "missing";
|
|
17836
|
-
throw exc;
|
|
17837
|
-
}
|
|
18023
|
+
function accessTokenFromClient(client) {
|
|
18024
|
+
const auth = client;
|
|
18025
|
+
return textValue(
|
|
18026
|
+
auth.accessToken ?? auth.access_token ?? auth._identity?.access_token ?? auth._sessionParams?.access_token ?? auth._session_params?.access_token
|
|
18027
|
+
);
|
|
17838
18028
|
}
|
|
17839
|
-
function
|
|
17840
|
-
const
|
|
17841
|
-
if (
|
|
17842
|
-
|
|
17843
|
-
if (lower.endsWith(".json")) return "application/json";
|
|
17844
|
-
if (lower.endsWith(".html") || lower.endsWith(".htm")) return "text/html";
|
|
17845
|
-
if (lower.endsWith(".png")) return "image/png";
|
|
17846
|
-
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
17847
|
-
return "application/octet-stream";
|
|
18029
|
+
function isInlineFallbackError(error) {
|
|
18030
|
+
const code = String(error.code).toUpperCase();
|
|
18031
|
+
if (["EINLINE", "EINLINE_LIMIT", "ERR_INLINE_LIMIT", "INLINE_LIMIT", "-32033"].includes(code)) return true;
|
|
18032
|
+
return error.code === -32602 && error.message.toLowerCase().includes("inline");
|
|
17848
18033
|
}
|
|
17849
|
-
function
|
|
17850
|
-
if (typeof
|
|
17851
|
-
|
|
17852
|
-
if (dst === "bytes:") return { mode: "bytes" };
|
|
17853
|
-
return { mode: "local", localPath: stripLocalPathPrefix(dst) };
|
|
18034
|
+
function parsePathLike(input, fallbackOwner) {
|
|
18035
|
+
if (typeof input !== "string") {
|
|
18036
|
+
return { owner: input.owner ?? fallbackOwner ?? null, path: input.path };
|
|
17854
18037
|
}
|
|
17855
|
-
const
|
|
17856
|
-
|
|
17857
|
-
|
|
18038
|
+
const match = input.match(/^([^:]+):(\/.*)$/);
|
|
18039
|
+
if (match) {
|
|
18040
|
+
return { owner: match[1], path: match[2] };
|
|
18041
|
+
}
|
|
18042
|
+
return { owner: fallbackOwner ?? null, path: input };
|
|
17858
18043
|
}
|
|
17859
|
-
var
|
|
18044
|
+
var StorageVFS = class {
|
|
17860
18045
|
constructor(client, options = {}) {
|
|
17861
18046
|
__publicField(this, "lowlevel");
|
|
18047
|
+
__publicField(this, "useFsRpc");
|
|
17862
18048
|
__publicField(this, "client");
|
|
17863
18049
|
this.client = client;
|
|
17864
18050
|
this.lowlevel = options.lowlevel ?? new StorageLowLevel(client);
|
|
18051
|
+
this.useFsRpc = options.useFsRpc ?? true;
|
|
17865
18052
|
}
|
|
17866
|
-
|
|
18053
|
+
get defaultOwner() {
|
|
18054
|
+
return this.client.aid ?? null;
|
|
18055
|
+
}
|
|
18056
|
+
owner(owner) {
|
|
18057
|
+
return owner || this.defaultOwner;
|
|
18058
|
+
}
|
|
18059
|
+
downloadUrlAndHeaders(ticket, owner) {
|
|
18060
|
+
const url = textValue(ticket.logical_url ?? ticket.download_url ?? ticket.url);
|
|
18061
|
+
const currentAid = textValue(this.defaultOwner);
|
|
18062
|
+
const targetAid = textValue(ticket.owner_aid ?? owner);
|
|
18063
|
+
const currentIssuer = aidIssuer(currentAid);
|
|
18064
|
+
const targetIssuer = aidIssuer(targetAid);
|
|
18065
|
+
const sameIssuer = Boolean(currentAid && targetAid && (currentAid.toLowerCase() === targetAid.toLowerCase() || currentIssuer && targetIssuer && currentIssuer === targetIssuer));
|
|
18066
|
+
const token = sameIssuer ? accessTokenFromClient(this.client) : "";
|
|
18067
|
+
return { url, headers: token ? { Authorization: `Bearer ${token}` } : void 0 };
|
|
18068
|
+
}
|
|
18069
|
+
async writeBytes(path, input, options = {}) {
|
|
18070
|
+
const data = toBytes(input);
|
|
18071
|
+
const contentType = options.contentType?.trim() || contentTypeForData(data, path);
|
|
18072
|
+
const owner = this.owner(options.owner);
|
|
18073
|
+
const bucket = options.bucket ?? "default";
|
|
18074
|
+
const objectKey = pathToKey(path);
|
|
18075
|
+
const sha2566 = await sha256Hex(data);
|
|
18076
|
+
const overwrite = options.overwrite ?? false;
|
|
17867
18077
|
try {
|
|
17868
|
-
|
|
18078
|
+
const check = await this.lowlevel.checkUpload({ owner, bucket, objectKey, size: data.length, sha256: sha2566 });
|
|
18079
|
+
if (check.within_limit === false) {
|
|
18080
|
+
throw new StorageError(`file size exceeds max_file_size_bytes: ${data.length}`, "E2BIG", path, check);
|
|
18081
|
+
}
|
|
18082
|
+
if (check.target_exists && !overwrite && options.expectedVersion === void 0) {
|
|
18083
|
+
throw new StorageExistsError(`remote path already exists: ${path}`, "EEXIST", path, check.target);
|
|
18084
|
+
}
|
|
18085
|
+
if (check.dedup_hit || check.skip_upload) {
|
|
18086
|
+
const completed = await this.lowlevel.completeUpload({
|
|
18087
|
+
owner,
|
|
18088
|
+
bucket,
|
|
18089
|
+
objectKey,
|
|
18090
|
+
size: data.length,
|
|
18091
|
+
sha256: sha2566,
|
|
18092
|
+
contentType,
|
|
18093
|
+
metadata: options.metadata,
|
|
18094
|
+
isPublic: options.public ?? false,
|
|
18095
|
+
expectedVersion: options.expectedVersion,
|
|
18096
|
+
skipBlob: true,
|
|
18097
|
+
overwrite
|
|
18098
|
+
});
|
|
18099
|
+
return nodeFromAny(completed);
|
|
18100
|
+
}
|
|
18101
|
+
if (check.inline === true) {
|
|
18102
|
+
return nodeFromAny(await this.lowlevel.putObject({
|
|
18103
|
+
owner,
|
|
18104
|
+
bucket,
|
|
18105
|
+
objectKey,
|
|
18106
|
+
content: data,
|
|
18107
|
+
contentType,
|
|
18108
|
+
metadata: options.metadata,
|
|
18109
|
+
isPublic: options.public ?? false,
|
|
18110
|
+
expectedVersion: options.expectedVersion,
|
|
18111
|
+
overwrite
|
|
18112
|
+
}));
|
|
18113
|
+
}
|
|
18114
|
+
const session = await this.lowlevel.createUploadSession({
|
|
18115
|
+
owner,
|
|
18116
|
+
bucket,
|
|
18117
|
+
objectKey,
|
|
18118
|
+
size: data.length,
|
|
18119
|
+
contentType,
|
|
18120
|
+
expectedVersion: options.expectedVersion,
|
|
18121
|
+
overwrite
|
|
18122
|
+
});
|
|
18123
|
+
const uploadUrl = String(session.upload_url ?? "");
|
|
18124
|
+
if (!uploadUrl) throw new StorageError(`create_upload_session did not return upload_url`, "ESTORAGE", path);
|
|
18125
|
+
const headers = {};
|
|
18126
|
+
if (session.headers && typeof session.headers === "object") {
|
|
18127
|
+
for (const [key, value] of Object.entries(session.headers)) {
|
|
18128
|
+
if (value !== void 0 && value !== null) headers[key] = String(value);
|
|
18129
|
+
}
|
|
18130
|
+
}
|
|
18131
|
+
for (const key of Object.keys(headers)) {
|
|
18132
|
+
if (key.toLowerCase() === "content-type") delete headers[key];
|
|
18133
|
+
}
|
|
18134
|
+
headers["Content-Type"] = contentType;
|
|
18135
|
+
await this.lowlevel.httpPut(uploadUrl, data, headers);
|
|
18136
|
+
return nodeFromAny(await this.lowlevel.completeUpload({
|
|
18137
|
+
owner,
|
|
18138
|
+
bucket,
|
|
18139
|
+
objectKey,
|
|
18140
|
+
sessionId: String(session.session_id ?? ""),
|
|
18141
|
+
size: data.length,
|
|
18142
|
+
sha256: sha2566,
|
|
18143
|
+
contentType,
|
|
18144
|
+
metadata: options.metadata,
|
|
18145
|
+
isPublic: options.public ?? false,
|
|
18146
|
+
expectedVersion: options.expectedVersion,
|
|
18147
|
+
overwrite
|
|
18148
|
+
}));
|
|
17869
18149
|
} catch (exc) {
|
|
17870
18150
|
throw mapStorageError(exc, path);
|
|
17871
18151
|
}
|
|
17872
18152
|
}
|
|
17873
|
-
async
|
|
17874
|
-
|
|
17875
|
-
|
|
17876
|
-
|
|
17877
|
-
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
|
|
17881
|
-
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
17886
|
-
|
|
17887
|
-
|
|
17888
|
-
|
|
18153
|
+
async readBytes(path, options = {}) {
|
|
18154
|
+
return (await this.readBytesWithMetadata(path, options)).data;
|
|
18155
|
+
}
|
|
18156
|
+
async readBytesWithMetadata(path, options = {}) {
|
|
18157
|
+
const owner = this.owner(options.owner);
|
|
18158
|
+
const bucket = options.bucket ?? "default";
|
|
18159
|
+
const objectKey = pathToKey(path);
|
|
18160
|
+
try {
|
|
18161
|
+
const result = await this.lowlevel.getObject({ owner, bucket, objectKey, token: options.token, offset: options.offset, limit: options.limit });
|
|
18162
|
+
return {
|
|
18163
|
+
data: base64ToBytes(String(result.content ?? "")),
|
|
18164
|
+
sha256: textValue(result.sha256)
|
|
18165
|
+
};
|
|
18166
|
+
} catch (exc) {
|
|
18167
|
+
const mapped = mapStorageError(exc, path);
|
|
18168
|
+
if (options.offset !== void 0 || options.limit !== void 0) throw mapped;
|
|
18169
|
+
if (!isInlineFallbackError(mapped)) throw mapped;
|
|
17889
18170
|
}
|
|
17890
|
-
|
|
18171
|
+
const ticket = await this.lowlevel.createDownloadTicket({ owner, bucket, objectKey, token: options.token });
|
|
18172
|
+
const { url: downloadUrl, headers } = this.downloadUrlAndHeaders(ticket, owner);
|
|
18173
|
+
if (!downloadUrl) throw new StorageError(`create_download_ticket did not return download_url`, "ESTORAGE", path);
|
|
18174
|
+
return {
|
|
18175
|
+
data: headers ? await this.lowlevel.httpGet(downloadUrl, headers) : await this.lowlevel.httpGet(downloadUrl),
|
|
18176
|
+
sha256: textValue(ticket.sha256)
|
|
18177
|
+
};
|
|
17891
18178
|
}
|
|
17892
|
-
|
|
17893
|
-
|
|
18179
|
+
async downloadFile(path, options = {}) {
|
|
18180
|
+
const { data, sha256: expectedSha } = await this.readBytesWithMetadata(path, options);
|
|
18181
|
+
const actualSha = await sha256Hex(data);
|
|
18182
|
+
const expected = expectedSha.toLowerCase();
|
|
18183
|
+
const verified = Boolean(expected) && actualSha.toLowerCase() === expected;
|
|
18184
|
+
if (!verified && expected && (options.verifyHash ?? true)) {
|
|
18185
|
+
throw new StorageError(
|
|
18186
|
+
`hash mismatch: expected=${expected} actual=${actualSha.toLowerCase()}`,
|
|
18187
|
+
"EHASH",
|
|
18188
|
+
path
|
|
18189
|
+
);
|
|
18190
|
+
}
|
|
18191
|
+
return {
|
|
18192
|
+
path,
|
|
18193
|
+
size: data.length,
|
|
18194
|
+
sha256: expectedSha || actualSha,
|
|
18195
|
+
verified,
|
|
18196
|
+
data
|
|
18197
|
+
};
|
|
17894
18198
|
}
|
|
17895
|
-
|
|
17896
|
-
|
|
18199
|
+
async list(path, options = {}) {
|
|
18200
|
+
if (options.recursive) {
|
|
18201
|
+
const result = [];
|
|
18202
|
+
const pending = [path];
|
|
18203
|
+
while (pending.length) {
|
|
18204
|
+
const current = pending.shift();
|
|
18205
|
+
const children = await this.list(current, { ...options, recursive: false });
|
|
18206
|
+
result.push(...children);
|
|
18207
|
+
pending.push(...children.filter((node) => node.type === "dir").map((node) => node.path));
|
|
18208
|
+
}
|
|
18209
|
+
return result;
|
|
18210
|
+
}
|
|
18211
|
+
const owner = this.owner(options.owner);
|
|
18212
|
+
const bucket = options.bucket ?? "default";
|
|
18213
|
+
const key = pathToKey(path);
|
|
18214
|
+
const raw = await this.lowlevel.fsList({
|
|
18215
|
+
owner,
|
|
18216
|
+
bucket,
|
|
18217
|
+
path: key,
|
|
18218
|
+
page: options.page,
|
|
18219
|
+
size: options.size,
|
|
18220
|
+
marker: options.marker,
|
|
18221
|
+
token: options.token
|
|
18222
|
+
});
|
|
18223
|
+
const items = raw.nodes ?? raw.items ?? [];
|
|
18224
|
+
return items.map((item) => nodeFromAny(item));
|
|
17897
18225
|
}
|
|
17898
|
-
stat(path, options = {}) {
|
|
17899
|
-
|
|
18226
|
+
async stat(path, options = {}) {
|
|
18227
|
+
const owner = this.owner(options.owner);
|
|
18228
|
+
const raw = await this.lowlevel.fsStat({ owner, bucket: options.bucket ?? "default", path: pathToKey(path), token: options.token });
|
|
18229
|
+
return nodeFromAny(raw);
|
|
17900
18230
|
}
|
|
17901
|
-
lstat(path, options = {}) {
|
|
17902
|
-
|
|
18231
|
+
async lstat(path, options = {}) {
|
|
18232
|
+
const owner = this.owner(options.owner);
|
|
18233
|
+
const raw = await this.lowlevel.fsLstat({ owner, bucket: options.bucket ?? "default", path: pathToKey(path), token: options.token });
|
|
18234
|
+
return nodeFromAny(raw);
|
|
17903
18235
|
}
|
|
17904
|
-
mkdir(path, options = {}) {
|
|
17905
|
-
|
|
18236
|
+
async mkdir(path, options = {}) {
|
|
18237
|
+
const raw = await this.lowlevel.fsMkdir({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), parents: options.parents });
|
|
18238
|
+
return nodeFromAny(raw.node ?? raw);
|
|
17906
18239
|
}
|
|
17907
|
-
|
|
17908
|
-
const
|
|
17909
|
-
|
|
17910
|
-
|
|
17911
|
-
|
|
18240
|
+
async touch(path, options = {}) {
|
|
18241
|
+
const raw = await this.lowlevel.fsTouch({
|
|
18242
|
+
owner: this.owner(options.owner),
|
|
18243
|
+
bucket: options.bucket ?? "default",
|
|
18244
|
+
path: pathToKey(path),
|
|
18245
|
+
parents: options.parents,
|
|
18246
|
+
noCreate: options.noCreate,
|
|
18247
|
+
mtime: options.mtime,
|
|
18248
|
+
followSymlinks: options.followSymlinks
|
|
18249
|
+
});
|
|
18250
|
+
return nodeFromAny(raw.node ?? raw);
|
|
17912
18251
|
}
|
|
17913
|
-
|
|
17914
|
-
const
|
|
17915
|
-
|
|
17916
|
-
delete params2.granteeAid;
|
|
17917
|
-
return this.call("group.fs.remove_acl", params2, path);
|
|
18252
|
+
async remove(path, options = {}) {
|
|
18253
|
+
const raw = await this.lowlevel.fsRemove({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), recursive: options.recursive });
|
|
18254
|
+
return { path, removedCount: Number(raw.removed_count ?? raw.deleted_count ?? 0) };
|
|
17918
18255
|
}
|
|
17919
|
-
|
|
17920
|
-
|
|
18256
|
+
async rename(src, dst, options = {}) {
|
|
18257
|
+
const raw = await this.lowlevel.fsRename({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", src: pathToKey(src), dst: pathToKey(dst), overwrite: options.overwrite, expectedVersion: options.expectedVersion });
|
|
18258
|
+
return nodeFromAny(raw.node ?? raw);
|
|
17921
18259
|
}
|
|
17922
|
-
|
|
17923
|
-
|
|
18260
|
+
async copy(src, dst, options = {}) {
|
|
18261
|
+
const raw = await this.lowlevel.fsCopy({
|
|
18262
|
+
owner: this.owner(options.owner),
|
|
18263
|
+
bucket: options.bucket ?? "default",
|
|
18264
|
+
src: pathToKey(src),
|
|
18265
|
+
dst: pathToKey(dst),
|
|
18266
|
+
overwrite: options.overwrite,
|
|
18267
|
+
followSymlinks: options.followSymlinks,
|
|
18268
|
+
recursive: options.recursive,
|
|
18269
|
+
dstOwner: options.dstOwner,
|
|
18270
|
+
dstBucket: options.dstBucket
|
|
18271
|
+
});
|
|
18272
|
+
return nodeFromAny(raw.node ?? raw);
|
|
17924
18273
|
}
|
|
17925
|
-
|
|
17926
|
-
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
17930
|
-
|
|
17931
|
-
|
|
18274
|
+
async find(path, options = {}) {
|
|
18275
|
+
const raw = await this.lowlevel.fsFind({
|
|
18276
|
+
owner: this.owner(options.owner),
|
|
18277
|
+
bucket: options.bucket ?? "default",
|
|
18278
|
+
path: pathToKey(path),
|
|
18279
|
+
name: options.name,
|
|
18280
|
+
nodeType: options.nodeType,
|
|
18281
|
+
size: options.size,
|
|
18282
|
+
mtime: options.mtime,
|
|
18283
|
+
page: options.page,
|
|
18284
|
+
pageSize: options.pageSize,
|
|
18285
|
+
token: options.token
|
|
18286
|
+
});
|
|
18287
|
+
const items = raw.nodes ?? raw.items ?? [];
|
|
18288
|
+
return items.map((item) => nodeFromAny(item));
|
|
17932
18289
|
}
|
|
17933
|
-
async
|
|
17934
|
-
const
|
|
17935
|
-
const
|
|
17936
|
-
|
|
17937
|
-
const srcRemote = typeof src === "string" && isGroupRemoteCopyPath(src, srcGroupRef, groupRef);
|
|
17938
|
-
const dstRemote = typeof dst === "string" && isGroupRemoteCopyPath(dst, dstGroupRef, groupRef);
|
|
17939
|
-
if (srcRemote && dstRemote) return this.copyRemoteToRemote(src, dst, options);
|
|
17940
|
-
if (!srcRemote && dstRemote) return this.uploadToGroup(src, dst, options);
|
|
17941
|
-
if (srcRemote && !dstRemote) return this.downloadFromGroup(src, dst, options);
|
|
17942
|
-
throw new StorageError("local-to-local copy is not handled by group.fs", "EINVAL", typeof src === "string" ? src : "");
|
|
18290
|
+
async df(options = {}) {
|
|
18291
|
+
const owner = this.owner(options.owner);
|
|
18292
|
+
const raw = await this.lowlevel.fsDf({ owner, bucket: options.bucket ?? "default" });
|
|
18293
|
+
return usageFromAny(raw, owner ?? void 0);
|
|
17943
18294
|
}
|
|
17944
|
-
async
|
|
17945
|
-
const
|
|
17946
|
-
|
|
17947
|
-
|
|
17948
|
-
|
|
17949
|
-
|
|
18295
|
+
async du(path, options = {}) {
|
|
18296
|
+
const nodes = await this.find(path, {
|
|
18297
|
+
owner: options.owner,
|
|
18298
|
+
bucket: options.bucket,
|
|
18299
|
+
page: 1,
|
|
18300
|
+
pageSize: options.pageSize ?? 1e3,
|
|
18301
|
+
token: options.token
|
|
18302
|
+
});
|
|
18303
|
+
const rootParts = pathToKey(path).split("/").filter(Boolean);
|
|
18304
|
+
let sizeBytes = 0;
|
|
18305
|
+
let fileCount = 0;
|
|
18306
|
+
let dirCount = 0;
|
|
18307
|
+
let symlinkCount = 0;
|
|
18308
|
+
let truncated = false;
|
|
18309
|
+
for (const node of nodes) {
|
|
18310
|
+
const nodeParts = pathToKey(node.path).split("/").filter(Boolean);
|
|
18311
|
+
const depth = Math.max(0, nodeParts.length - rootParts.length);
|
|
18312
|
+
if (options.maxDepth !== void 0 && depth > options.maxDepth) {
|
|
18313
|
+
truncated = true;
|
|
18314
|
+
continue;
|
|
18315
|
+
}
|
|
18316
|
+
if (node.type === "file") {
|
|
18317
|
+
fileCount += 1;
|
|
18318
|
+
sizeBytes += node.size;
|
|
18319
|
+
} else if (node.type === "dir") {
|
|
18320
|
+
dirCount += 1;
|
|
18321
|
+
} else if (node.type === "symlink") {
|
|
18322
|
+
symlinkCount += 1;
|
|
18323
|
+
}
|
|
17950
18324
|
}
|
|
17951
|
-
|
|
17952
|
-
|
|
18325
|
+
return {
|
|
18326
|
+
path,
|
|
18327
|
+
sizeBytes,
|
|
18328
|
+
fileCount,
|
|
18329
|
+
dirCount,
|
|
18330
|
+
symlinkCount,
|
|
18331
|
+
maxDepth: options.maxDepth,
|
|
18332
|
+
truncated
|
|
18333
|
+
};
|
|
17953
18334
|
}
|
|
17954
|
-
|
|
17955
|
-
const
|
|
17956
|
-
|
|
17957
|
-
|
|
18335
|
+
async mount(source, mountPath, options = {}) {
|
|
18336
|
+
const owner = this.owner(options.owner);
|
|
18337
|
+
const mountRef = parsePathLike(mountPath, owner);
|
|
18338
|
+
const sourceRef = parsePathLike(source, options.sourceOwner ?? this.defaultOwner);
|
|
18339
|
+
const raw = await this.lowlevel.fsMount({
|
|
18340
|
+
owner: mountRef.owner,
|
|
18341
|
+
bucket: options.bucket ?? "default",
|
|
18342
|
+
mountPath: pathToKey(mountRef.path),
|
|
18343
|
+
sourceAid: String(sourceRef.owner ?? ""),
|
|
18344
|
+
sourceBucket: options.sourceBucket,
|
|
18345
|
+
sourcePath: pathToKey(sourceRef.path),
|
|
18346
|
+
readonly: options.readonly,
|
|
18347
|
+
expiresAt: options.expiresAt ?? options.expires,
|
|
18348
|
+
requireApproval: options.requireApproval
|
|
18349
|
+
});
|
|
18350
|
+
return nodeFromAny(raw.node ?? raw.mount ?? raw);
|
|
17958
18351
|
}
|
|
17959
|
-
|
|
17960
|
-
|
|
18352
|
+
async mountVolume(volumeId, mountPath, options = {}) {
|
|
18353
|
+
const owner = this.owner(options.owner);
|
|
18354
|
+
const mountRef = parsePathLike(mountPath, owner);
|
|
18355
|
+
const raw = await this.lowlevel.fsMount({
|
|
18356
|
+
owner: mountRef.owner,
|
|
18357
|
+
bucket: options.bucket ?? "default",
|
|
18358
|
+
mountPath: pathToKey(mountRef.path),
|
|
18359
|
+
readonly: options.readonly,
|
|
18360
|
+
expiresAt: options.expiresAt ?? options.expires,
|
|
18361
|
+
requireApproval: options.requireApproval,
|
|
18362
|
+
volumeId
|
|
18363
|
+
});
|
|
18364
|
+
return nodeFromAny(raw.node ?? raw.mount ?? raw);
|
|
17961
18365
|
}
|
|
17962
|
-
|
|
17963
|
-
|
|
18366
|
+
approveMount(mountPath, options = {}) {
|
|
18367
|
+
const owner = this.owner(options.owner);
|
|
18368
|
+
const mountRef = parsePathLike(mountPath, owner);
|
|
18369
|
+
return this.lowlevel.fsApprove({
|
|
18370
|
+
owner: mountRef.owner,
|
|
18371
|
+
bucket: options.bucket ?? "default",
|
|
18372
|
+
mountPath: pathToKey(mountRef.path),
|
|
18373
|
+
mountId: options.mountId,
|
|
18374
|
+
requestId: options.requestId
|
|
18375
|
+
});
|
|
17964
18376
|
}
|
|
17965
|
-
|
|
17966
|
-
const
|
|
17967
|
-
|
|
17968
|
-
|
|
17969
|
-
|
|
17970
|
-
|
|
17971
|
-
|
|
17972
|
-
|
|
17973
|
-
|
|
17974
|
-
|
|
17975
|
-
delete params2.sourceType;
|
|
17976
|
-
delete params2.source_type;
|
|
17977
|
-
delete params2.localPath;
|
|
17978
|
-
delete params2.local_path;
|
|
17979
|
-
if (options.groupAid !== void 0) params2.group_aid = options.groupAid;
|
|
17980
|
-
if (options.groupId !== void 0) params2.group_id = options.groupId;
|
|
17981
|
-
if (options.srcGroupAid !== void 0) params2.src_group_aid = options.srcGroupAid;
|
|
17982
|
-
if (options.srcGroupId !== void 0) params2.src_group_id = options.srcGroupId;
|
|
17983
|
-
if (options.dstGroupAid !== void 0) params2.dst_group_aid = options.dstGroupAid;
|
|
17984
|
-
if (options.dstGroupId !== void 0) params2.dst_group_id = options.dstGroupId;
|
|
17985
|
-
if (options.followSymlinks !== void 0) params2.follow_symlinks = options.followSymlinks;
|
|
17986
|
-
delete params2.groupAid;
|
|
17987
|
-
delete params2.srcGroupAid;
|
|
17988
|
-
delete params2.dstGroupAid;
|
|
17989
|
-
delete params2.followSymlinks;
|
|
17990
|
-
if (!options.force) delete params2.force;
|
|
17991
|
-
if (!options.recursive) delete params2.recursive;
|
|
17992
|
-
return stripNil(params2);
|
|
18377
|
+
rejectMount(mountPath, options = {}) {
|
|
18378
|
+
const owner = this.owner(options.owner);
|
|
18379
|
+
const mountRef = parsePathLike(mountPath, owner);
|
|
18380
|
+
return this.lowlevel.fsReject({
|
|
18381
|
+
owner: mountRef.owner,
|
|
18382
|
+
bucket: options.bucket ?? "default",
|
|
18383
|
+
mountPath: pathToKey(mountRef.path),
|
|
18384
|
+
mountId: options.mountId,
|
|
18385
|
+
requestId: options.requestId
|
|
18386
|
+
});
|
|
17993
18387
|
}
|
|
17994
|
-
|
|
17995
|
-
|
|
18388
|
+
async unmount(mountPath, options = {}) {
|
|
18389
|
+
const owner = this.owner(options.owner);
|
|
18390
|
+
const mountRef = parsePathLike(mountPath, owner);
|
|
18391
|
+
const raw = await this.lowlevel.fsUnmount({
|
|
18392
|
+
owner: mountRef.owner,
|
|
18393
|
+
bucket: options.bucket ?? "default",
|
|
18394
|
+
mountPath: pathToKey(mountRef.path)
|
|
18395
|
+
});
|
|
18396
|
+
return unmountFromAny(raw, {
|
|
18397
|
+
owner: mountRef.owner,
|
|
18398
|
+
bucket: options.bucket ?? "default",
|
|
18399
|
+
mountPath: pathToKey(mountRef.path)
|
|
18400
|
+
});
|
|
17996
18401
|
}
|
|
17997
|
-
async
|
|
17998
|
-
|
|
17999
|
-
|
|
18000
|
-
if (isExplicitLocalPath(src) || sourceType === "path" || options.localPath === true || options.local_path === true) {
|
|
18001
|
-
const localPath = stripLocalPathPrefix(src);
|
|
18002
|
-
return { data: await readLocalFile(localPath), contentType: guessContentType(localPath) };
|
|
18003
|
-
}
|
|
18004
|
-
return { data: await toBytes(src), contentType: "text/plain;charset=utf-8" };
|
|
18005
|
-
}
|
|
18006
|
-
const data = await toBytes(src);
|
|
18007
|
-
const blobType = typeof Blob !== "undefined" && src instanceof Blob ? text(src.type) : "";
|
|
18008
|
-
const fileName = text(src.name);
|
|
18009
|
-
return { data, contentType: blobType || (fileName ? guessContentType(fileName) : "application/octet-stream") };
|
|
18402
|
+
async symlink(target, linkPath, options = {}) {
|
|
18403
|
+
const raw = await this.lowlevel.createSymlink({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(linkPath), target, overwrite: options.overwrite });
|
|
18404
|
+
return nodeFromAny(raw.symlink ?? raw);
|
|
18010
18405
|
}
|
|
18011
|
-
async
|
|
18012
|
-
const
|
|
18013
|
-
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
const
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
|
|
18021
|
-
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
|
|
18027
|
-
|
|
18406
|
+
async readlink(path, options = {}) {
|
|
18407
|
+
const raw = await this.lowlevel.readlink({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path) });
|
|
18408
|
+
return nodeFromAny(raw.symlink ?? raw);
|
|
18409
|
+
}
|
|
18410
|
+
async repoint(path, newTarget, options = {}) {
|
|
18411
|
+
const raw = await this.lowlevel.atomicRepoint({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), newTarget, expectedVersion: options.expectedVersion });
|
|
18412
|
+
if (raw.ok === false) throw new StorageConflictError("symlink version conflict", "ECONFLICT", path, raw);
|
|
18413
|
+
return nodeFromAny(raw.symlink ?? raw);
|
|
18414
|
+
}
|
|
18415
|
+
async renameSymlink(src, dst, options = {}) {
|
|
18416
|
+
const raw = await this.lowlevel.renameSymlink({
|
|
18417
|
+
owner: this.owner(options.owner),
|
|
18418
|
+
bucket: options.bucket ?? "default",
|
|
18419
|
+
path: pathToKey(src),
|
|
18420
|
+
newPath: pathToKey(dst),
|
|
18421
|
+
overwrite: options.overwrite,
|
|
18422
|
+
expectedVersion: options.expectedVersion
|
|
18028
18423
|
});
|
|
18029
|
-
|
|
18030
|
-
|
|
18031
|
-
delete baseParams.dstGroupId;
|
|
18032
|
-
delete baseParams.dstGroupAid;
|
|
18033
|
-
delete baseParams.dst_group_aid;
|
|
18034
|
-
delete baseParams.dst_group_id;
|
|
18035
|
-
delete baseParams.srcGroupId;
|
|
18036
|
-
delete baseParams.srcGroupAid;
|
|
18037
|
-
delete baseParams.src_group_aid;
|
|
18038
|
-
delete baseParams.src_group_id;
|
|
18039
|
-
delete baseParams.contentType;
|
|
18040
|
-
delete baseParams.expectedVersion;
|
|
18041
|
-
delete baseParams.verifyHash;
|
|
18042
|
-
delete baseParams.verify_hash;
|
|
18043
|
-
delete baseParams.recursive;
|
|
18044
|
-
delete baseParams.followSymlinks;
|
|
18045
|
-
delete baseParams.follow_symlinks;
|
|
18046
|
-
delete baseParams.sourceType;
|
|
18047
|
-
delete baseParams.source_type;
|
|
18048
|
-
delete baseParams.localPath;
|
|
18049
|
-
delete baseParams.local_path;
|
|
18050
|
-
const check = await this.call("group.fs.check_upload", baseParams, dst);
|
|
18051
|
-
if (check && typeof check === "object") {
|
|
18052
|
-
const raw = check;
|
|
18053
|
-
if (raw.within_limit === false) throw new StorageError("file size exceeds group fs upload limit", "E2BIG", dst, raw);
|
|
18054
|
-
if (raw.target_exists && !options.force && options.expected_version === void 0 && options.expectedVersion === void 0) {
|
|
18055
|
-
throw new StorageExistsError("group fs target already exists", "EEXIST", dst, raw.target);
|
|
18056
|
-
}
|
|
18057
|
-
if (raw.instant || raw.dedup_hit || raw.skip_upload) {
|
|
18058
|
-
return this.call("group.fs.complete_upload", {
|
|
18059
|
-
...baseParams,
|
|
18060
|
-
skip_blob: true,
|
|
18061
|
-
session_id: raw.session_id
|
|
18062
|
-
}, dst);
|
|
18063
|
-
}
|
|
18064
|
-
}
|
|
18065
|
-
const session = await this.call("group.fs.create_upload_session", baseParams, dst);
|
|
18066
|
-
if (!session || typeof session !== "object") {
|
|
18067
|
-
throw new StorageError(`group.fs.create_upload_session returned invalid response`, "ESTORAGE", dst, session);
|
|
18068
|
-
}
|
|
18069
|
-
const rawSession = session;
|
|
18070
|
-
const uploadUrl = text(rawSession.upload_url ?? rawSession.url);
|
|
18071
|
-
if (!uploadUrl) throw new StorageError("group.fs.create_upload_session did not return upload_url", "ESTORAGE", dst, session);
|
|
18072
|
-
const headers = {};
|
|
18073
|
-
if (rawSession.headers && typeof rawSession.headers === "object" && !Array.isArray(rawSession.headers)) {
|
|
18074
|
-
for (const [key, value] of Object.entries(rawSession.headers)) {
|
|
18075
|
-
if (value !== void 0 && value !== null) headers[key] = String(value);
|
|
18076
|
-
}
|
|
18077
|
-
}
|
|
18078
|
-
if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
|
|
18079
|
-
headers["Content-Type"] = text(baseParams.content_type) || "application/octet-stream";
|
|
18080
|
-
}
|
|
18081
|
-
await this.lowlevel.httpPut(uploadUrl, data, headers);
|
|
18082
|
-
return this.call("group.fs.complete_upload", {
|
|
18083
|
-
...baseParams,
|
|
18084
|
-
session_id: rawSession.session_id ?? rawSession.id
|
|
18085
|
-
}, dst);
|
|
18424
|
+
if (raw.ok === false) throw new StorageConflictError("symlink version conflict", "ECONFLICT", src, raw);
|
|
18425
|
+
return nodeFromAny(raw.symlink ?? raw);
|
|
18086
18426
|
}
|
|
18087
|
-
|
|
18088
|
-
|
|
18089
|
-
|
|
18090
|
-
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
}
|
|
18095
|
-
|
|
18096
|
-
|
|
18097
|
-
const
|
|
18098
|
-
|
|
18099
|
-
|
|
18100
|
-
|
|
18101
|
-
|
|
18427
|
+
setAcl(path, options) {
|
|
18428
|
+
return this.lowlevel.setAcl({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), granteeAid: options.granteeAid, perms: options.perms, expiresAt: options.expiresAt, maxUses: options.maxUses });
|
|
18429
|
+
}
|
|
18430
|
+
removeAcl(path, options) {
|
|
18431
|
+
return this.lowlevel.removeAcl({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), granteeAid: options.granteeAid });
|
|
18432
|
+
}
|
|
18433
|
+
listAcl(path, options = {}) {
|
|
18434
|
+
return this.lowlevel.listAcl({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path) });
|
|
18435
|
+
}
|
|
18436
|
+
async setVisibility(path, options) {
|
|
18437
|
+
const raw = await this.lowlevel.setVisibility({
|
|
18438
|
+
owner: this.owner(options.owner),
|
|
18439
|
+
bucket: options.bucket ?? "default",
|
|
18440
|
+
path: pathToKey(path),
|
|
18441
|
+
visibility: options.visibility,
|
|
18442
|
+
allowRoles: options.allowRoles
|
|
18102
18443
|
});
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
|
|
18115
|
-
|
|
18116
|
-
|
|
18117
|
-
|
|
18118
|
-
|
|
18119
|
-
|
|
18120
|
-
|
|
18121
|
-
|
|
18122
|
-
|
|
18123
|
-
|
|
18124
|
-
|
|
18125
|
-
|
|
18126
|
-
const
|
|
18127
|
-
|
|
18128
|
-
throw new StorageError("group.fs.create_download_ticket returned invalid response", "ESTORAGE", src, ticket);
|
|
18129
|
-
}
|
|
18130
|
-
const rawTicket = ticket;
|
|
18131
|
-
const downloadUrl = text(rawTicket.download_url ?? rawTicket.url);
|
|
18132
|
-
if (!downloadUrl) throw new StorageError("group.fs.create_download_ticket did not return download_url", "ESTORAGE", src, ticket);
|
|
18133
|
-
const data = await this.lowlevel.httpGet(downloadUrl, bearerHeaders(this.client));
|
|
18134
|
-
const expectedSha = text(rawTicket.sha256);
|
|
18135
|
-
const verifyHash = options.verify_hash ?? options.verifyHash ?? true;
|
|
18136
|
-
const verified = !verifyHash || !expectedSha || await sha256Hex(data) === expectedSha.toLowerCase();
|
|
18137
|
-
if (verifyHash && !verified) {
|
|
18138
|
-
throw new StorageConflictError("download hash verification failed", "ECONFLICT", src, ticket);
|
|
18139
|
-
}
|
|
18140
|
-
if (target.mode === "local" && target.localPath) {
|
|
18141
|
-
const status = await localFileStatus(target.localPath);
|
|
18142
|
-
if (status === "dir") {
|
|
18143
|
-
throw new StorageError("directory download target is not supported in this JavaScript runtime", "EISDIR", target.localPath);
|
|
18144
|
-
}
|
|
18145
|
-
if (status === "file" && !options.force) {
|
|
18146
|
-
throw new StorageExistsError(`local path already exists: ${target.localPath}`, "EEXIST", target.localPath);
|
|
18147
|
-
}
|
|
18148
|
-
}
|
|
18149
|
-
const result = {
|
|
18150
|
-
path: src,
|
|
18151
|
-
localPath: target.localPath,
|
|
18152
|
-
size: data.byteLength,
|
|
18153
|
-
sha256: expectedSha,
|
|
18154
|
-
verified,
|
|
18155
|
-
data
|
|
18156
|
-
};
|
|
18157
|
-
if (target.mode === "blob" && typeof Blob !== "undefined") {
|
|
18158
|
-
result.blob = new Blob([toExactArrayBuffer(data)], { type: text(rawTicket.content_type) || "application/octet-stream" });
|
|
18159
|
-
}
|
|
18160
|
-
if (target.mode === "local" && target.localPath) {
|
|
18161
|
-
result.wroteLocalFile = await writeLocalFile(target.localPath, data, Boolean(options.force));
|
|
18162
|
-
}
|
|
18163
|
-
return result;
|
|
18444
|
+
return nodeFromAny(raw.node ?? raw);
|
|
18445
|
+
}
|
|
18446
|
+
checkAccess(path, options = {}) {
|
|
18447
|
+
return this.lowlevel.checkAccess({
|
|
18448
|
+
owner: this.owner(options.owner),
|
|
18449
|
+
bucket: options.bucket ?? "default",
|
|
18450
|
+
path: pathToKey(path),
|
|
18451
|
+
operation: options.operation ?? "read",
|
|
18452
|
+
token: options.token,
|
|
18453
|
+
followSymlinks: options.followSymlinks ?? true
|
|
18454
|
+
});
|
|
18455
|
+
}
|
|
18456
|
+
issueToken(path, options = {}) {
|
|
18457
|
+
return this.lowlevel.issueToken({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), expiresAt: options.expiresAt, maxReads: options.maxReads });
|
|
18458
|
+
}
|
|
18459
|
+
revokeToken(path, options) {
|
|
18460
|
+
return this.lowlevel.revokeToken({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path), token: options.token });
|
|
18461
|
+
}
|
|
18462
|
+
listTokens(path, options = {}) {
|
|
18463
|
+
return this.lowlevel.listTokens({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(path) });
|
|
18464
|
+
}
|
|
18465
|
+
async getUsage(options = {}) {
|
|
18466
|
+
const owner = this.owner(options.owner);
|
|
18467
|
+
const raw = await this.lowlevel.getQuota({ owner, bucket: options.bucket ?? "default" });
|
|
18468
|
+
return usageFromAny(raw, owner ?? "");
|
|
18164
18469
|
}
|
|
18165
18470
|
};
|
|
18166
18471
|
|
|
18167
|
-
// src/
|
|
18168
|
-
var
|
|
18169
|
-
var
|
|
18170
|
-
|
|
18171
|
-
|
|
18472
|
+
// src/group-fs.ts
|
|
18473
|
+
var WINDOWS_DRIVE_RE = /^[A-Za-z]:[\\/]/;
|
|
18474
|
+
var GROUP_REF_RE = /^[^:/\\][^:]*:\//;
|
|
18475
|
+
var LOCAL_PREFIX = "local:";
|
|
18476
|
+
function isExplicitLocalPath(value) {
|
|
18477
|
+
return String(value || "").trim().toLowerCase().startsWith(LOCAL_PREFIX);
|
|
18172
18478
|
}
|
|
18173
|
-
function
|
|
18174
|
-
|
|
18175
|
-
|
|
18176
|
-
|
|
18177
|
-
|
|
18178
|
-
|
|
18479
|
+
function stripLocalPathPrefix(value) {
|
|
18480
|
+
const text3 = String(value || "").trim();
|
|
18481
|
+
return text3.toLowerCase().startsWith(LOCAL_PREFIX) ? text3.slice(LOCAL_PREFIX.length) : text3;
|
|
18482
|
+
}
|
|
18483
|
+
function isGroupRemoteCopyPath(value, ...groupHints) {
|
|
18484
|
+
if (isExplicitLocalPath(value)) return false;
|
|
18485
|
+
return isGroupRemotePath(value) || groupHints.some(Boolean);
|
|
18486
|
+
}
|
|
18487
|
+
function isGroupRemotePath(value) {
|
|
18488
|
+
const text3 = String(value || "").trim();
|
|
18489
|
+
if (!text3) return false;
|
|
18490
|
+
if (isExplicitLocalPath(text3)) return false;
|
|
18491
|
+
if (WINDOWS_DRIVE_RE.test(text3)) return false;
|
|
18492
|
+
if (text3.startsWith("http://") || text3.startsWith("https://")) return true;
|
|
18493
|
+
return GROUP_REF_RE.test(text3);
|
|
18494
|
+
}
|
|
18495
|
+
function stripNil(params2) {
|
|
18496
|
+
const out = {};
|
|
18497
|
+
for (const [key, value] of Object.entries(params2 ?? {})) {
|
|
18498
|
+
if (value !== void 0 && value !== null) out[key] = value;
|
|
18179
18499
|
}
|
|
18180
|
-
|
|
18181
|
-
|
|
18500
|
+
return out;
|
|
18501
|
+
}
|
|
18502
|
+
async function loadSigningAID(store, signAs) {
|
|
18503
|
+
const loaded = store.load ? await store.load(signAs) : store.loadAsync ? await store.loadAsync(signAs) : null;
|
|
18504
|
+
if (!loaded?.ok || !loaded.data?.aid) {
|
|
18505
|
+
throw new Error(loaded?.error?.message || `signer identity not found: ${signAs}`);
|
|
18182
18506
|
}
|
|
18183
|
-
if (
|
|
18184
|
-
|
|
18507
|
+
if (!loaded.data.aid.privateKeyPem) {
|
|
18508
|
+
throw new Error(`signer identity missing private key: ${signAs}`);
|
|
18185
18509
|
}
|
|
18186
|
-
|
|
18187
|
-
|
|
18510
|
+
return loaded.data.aid;
|
|
18511
|
+
}
|
|
18512
|
+
function text2(value) {
|
|
18513
|
+
return value === void 0 || value === null ? "" : String(value).trim();
|
|
18514
|
+
}
|
|
18515
|
+
function accessTokenFromClient2(client) {
|
|
18516
|
+
const authClient = client;
|
|
18517
|
+
if (typeof authClient.getAccessToken === "function") {
|
|
18518
|
+
const token = text2(authClient.getAccessToken());
|
|
18519
|
+
if (token) return token;
|
|
18188
18520
|
}
|
|
18189
|
-
|
|
18521
|
+
const directToken = text2(authClient.accessToken ?? authClient.access_token);
|
|
18522
|
+
if (directToken) return directToken;
|
|
18523
|
+
const identityToken = text2(authClient._identity?.access_token);
|
|
18524
|
+
if (identityToken) return identityToken;
|
|
18525
|
+
return text2(authClient._sessionParams?.access_token);
|
|
18190
18526
|
}
|
|
18191
|
-
function
|
|
18192
|
-
|
|
18193
|
-
|
|
18527
|
+
function bearerHeaders(client) {
|
|
18528
|
+
const token = accessTokenFromClient2(client);
|
|
18529
|
+
return token ? { Authorization: `Bearer ${token}` } : void 0;
|
|
18530
|
+
}
|
|
18531
|
+
function toExactArrayBuffer(bytes) {
|
|
18532
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
18533
|
+
}
|
|
18534
|
+
async function sha256Hex2(data) {
|
|
18535
|
+
const subtle = globalThis.crypto?.subtle;
|
|
18536
|
+
if (!subtle?.digest) throw new StorageError("SHA-256 unavailable", "EUNSUPPORTED");
|
|
18537
|
+
const digest = await subtle.digest("SHA-256", toExactArrayBuffer(data));
|
|
18538
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
18539
|
+
}
|
|
18540
|
+
async function blobToBytes(blob) {
|
|
18541
|
+
if (typeof blob.arrayBuffer === "function") {
|
|
18542
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
18194
18543
|
}
|
|
18195
|
-
if (
|
|
18196
|
-
|
|
18197
|
-
if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
|
|
18198
|
-
throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
|
|
18199
|
-
}
|
|
18200
|
-
return String(n);
|
|
18544
|
+
if (typeof blob.stream === "function" && typeof Response !== "undefined") {
|
|
18545
|
+
return new Uint8Array(await new Response(blob).arrayBuffer());
|
|
18201
18546
|
}
|
|
18202
|
-
|
|
18203
|
-
|
|
18204
|
-
|
|
18205
|
-
|
|
18206
|
-
|
|
18207
|
-
|
|
18208
|
-
|
|
18547
|
+
if (typeof FileReader !== "undefined") {
|
|
18548
|
+
return await new Promise((resolve, reject) => {
|
|
18549
|
+
const reader = new FileReader();
|
|
18550
|
+
reader.onload = () => resolve(new Uint8Array(reader.result));
|
|
18551
|
+
reader.onerror = () => reject(reader.error ?? new StorageError("Blob read failed", "ESTORAGE"));
|
|
18552
|
+
reader.readAsArrayBuffer(blob);
|
|
18553
|
+
});
|
|
18209
18554
|
}
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
const fracPart = match[3] ?? "";
|
|
18213
|
-
const exp = Number(match[4]);
|
|
18214
|
-
const digits = intPart + fracPart;
|
|
18215
|
-
const point = intPart.length + exp;
|
|
18216
|
-
if (point <= 0) {
|
|
18217
|
-
return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
18555
|
+
if (typeof blob.text === "function") {
|
|
18556
|
+
return new TextEncoder().encode(await blob.text());
|
|
18218
18557
|
}
|
|
18219
|
-
|
|
18220
|
-
|
|
18558
|
+
throw new StorageError("Blob reads are unavailable in this JavaScript runtime", "EUNSUPPORTED");
|
|
18559
|
+
}
|
|
18560
|
+
async function toBytes2(source) {
|
|
18561
|
+
if (typeof source === "string") return new TextEncoder().encode(source);
|
|
18562
|
+
if (source instanceof Uint8Array) return source;
|
|
18563
|
+
if (source instanceof ArrayBuffer) return new Uint8Array(source);
|
|
18564
|
+
if (typeof Blob !== "undefined" && source instanceof Blob) {
|
|
18565
|
+
return blobToBytes(source);
|
|
18221
18566
|
}
|
|
18222
|
-
|
|
18567
|
+
if (ArrayBuffer.isView(source)) {
|
|
18568
|
+
return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
18569
|
+
}
|
|
18570
|
+
throw new StorageError("unsupported group fs upload source", "EUNSUPPORTED");
|
|
18223
18571
|
}
|
|
18224
|
-
function
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
|
|
18231
|
-
} else if (ch === "\\") {
|
|
18232
|
-
result += "\\\\";
|
|
18233
|
-
} else if (ch === "\b") {
|
|
18234
|
-
result += "\\b";
|
|
18235
|
-
} else if (ch === "\f") {
|
|
18236
|
-
result += "\\f";
|
|
18237
|
-
} else if (ch === "\n") {
|
|
18238
|
-
result += "\\n";
|
|
18239
|
-
} else if (ch === "\r") {
|
|
18240
|
-
result += "\\r";
|
|
18241
|
-
} else if (ch === " ") {
|
|
18242
|
-
result += "\\t";
|
|
18243
|
-
} else if (code < 32) {
|
|
18244
|
-
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
18245
|
-
} else {
|
|
18246
|
-
result += ch;
|
|
18572
|
+
async function importNodeModule(specifier) {
|
|
18573
|
+
if (typeof document !== "undefined") return null;
|
|
18574
|
+
const getBuiltinModule = globalThis.process?.getBuiltinModule;
|
|
18575
|
+
if (typeof getBuiltinModule === "function" && specifier.startsWith("node:")) {
|
|
18576
|
+
try {
|
|
18577
|
+
return getBuiltinModule(specifier.slice("node:".length));
|
|
18578
|
+
} catch {
|
|
18247
18579
|
}
|
|
18248
18580
|
}
|
|
18249
|
-
|
|
18250
|
-
|
|
18581
|
+
try {
|
|
18582
|
+
const requireFn = Function('return typeof require !== "undefined" ? require : undefined');
|
|
18583
|
+
const requireModule = requireFn();
|
|
18584
|
+
if (requireModule) return requireModule(specifier);
|
|
18585
|
+
} catch {
|
|
18586
|
+
}
|
|
18587
|
+
try {
|
|
18588
|
+
const dynamicImport = Function(`return import(${JSON.stringify(specifier)})`);
|
|
18589
|
+
return await dynamicImport();
|
|
18590
|
+
} catch {
|
|
18591
|
+
return null;
|
|
18592
|
+
}
|
|
18251
18593
|
}
|
|
18252
|
-
function
|
|
18253
|
-
const
|
|
18254
|
-
|
|
18594
|
+
async function readLocalFile(path) {
|
|
18595
|
+
const fs = await importNodeModule("node:fs/promises");
|
|
18596
|
+
if (!fs?.readFile) throw new StorageError("local file reads are unavailable in this JavaScript runtime", "EUNSUPPORTED", path);
|
|
18597
|
+
return new Uint8Array(await fs.readFile(path));
|
|
18255
18598
|
}
|
|
18256
|
-
function
|
|
18257
|
-
const
|
|
18258
|
-
const
|
|
18259
|
-
|
|
18260
|
-
);
|
|
18261
|
-
|
|
18599
|
+
async function writeLocalFile(path, data, force) {
|
|
18600
|
+
const fs = await importNodeModule("node:fs/promises");
|
|
18601
|
+
const pathMod = await importNodeModule("node:path");
|
|
18602
|
+
if (!fs?.writeFile || !pathMod?.dirname) return false;
|
|
18603
|
+
await fs.mkdir(pathMod.dirname(path), { recursive: true });
|
|
18604
|
+
try {
|
|
18605
|
+
await fs.writeFile(path, data, force ? void 0 : { flag: "wx" });
|
|
18606
|
+
return true;
|
|
18607
|
+
} catch (exc) {
|
|
18608
|
+
const code = text2(exc.code);
|
|
18609
|
+
if (code === "EEXIST") throw new StorageExistsError(`local path already exists: ${path}`, "EEXIST", path);
|
|
18610
|
+
throw exc;
|
|
18611
|
+
}
|
|
18262
18612
|
}
|
|
18263
|
-
function
|
|
18264
|
-
const
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
|
|
18613
|
+
async function localFileStatus(path) {
|
|
18614
|
+
const fs = await importNodeModule("node:fs/promises");
|
|
18615
|
+
if (!fs?.stat) return "unavailable";
|
|
18616
|
+
try {
|
|
18617
|
+
const info = await fs.stat(path);
|
|
18618
|
+
return info.isDirectory() ? "dir" : "file";
|
|
18619
|
+
} catch (exc) {
|
|
18620
|
+
const code = text2(exc.code);
|
|
18621
|
+
if (code === "ENOENT") return "missing";
|
|
18622
|
+
throw exc;
|
|
18271
18623
|
}
|
|
18272
|
-
return ac.length - bc.length;
|
|
18273
18624
|
}
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
var GROUP_INDEX_CACHE_VERIFICATION_VERSION = 1;
|
|
18280
|
-
var GroupIndexMetaCache = class {
|
|
18281
|
-
constructor() {
|
|
18282
|
-
__publicField(this, "remote", /* @__PURE__ */ new Map());
|
|
18283
|
-
__publicField(this, "localEtags", /* @__PURE__ */ new Map());
|
|
18284
|
-
__publicField(this, "stale", /* @__PURE__ */ new Set());
|
|
18285
|
-
__publicField(this, "settings", /* @__PURE__ */ new Map());
|
|
18286
|
-
__publicField(this, "entryEtags", /* @__PURE__ */ new Map());
|
|
18287
|
-
__publicField(this, "verificationVersions", /* @__PURE__ */ new Map());
|
|
18625
|
+
function downloadTarget(dst) {
|
|
18626
|
+
if (typeof dst === "string") {
|
|
18627
|
+
if (dst === "blob:") return { mode: "blob" };
|
|
18628
|
+
if (dst === "bytes:") return { mode: "bytes" };
|
|
18629
|
+
return { mode: "local", localPath: stripLocalPathPrefix(dst) };
|
|
18288
18630
|
}
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
|
|
18293
|
-
|
|
18294
|
-
|
|
18295
|
-
|
|
18296
|
-
|
|
18297
|
-
|
|
18298
|
-
|
|
18299
|
-
|
|
18300
|
-
|
|
18301
|
-
|
|
18302
|
-
|
|
18303
|
-
|
|
18631
|
+
const mode = dst.type === "blob" ? "blob" : dst.type === "bytes" ? "bytes" : dst.path || dst.localPath ? "local" : "bytes";
|
|
18632
|
+
const localPath = dst.path ?? dst.localPath;
|
|
18633
|
+
return { mode, localPath: localPath ? stripLocalPathPrefix(localPath) : localPath };
|
|
18634
|
+
}
|
|
18635
|
+
var GroupFSVFS = class {
|
|
18636
|
+
constructor(client, options = {}) {
|
|
18637
|
+
__publicField(this, "lowlevel");
|
|
18638
|
+
__publicField(this, "client");
|
|
18639
|
+
this.client = client;
|
|
18640
|
+
this.lowlevel = options.lowlevel ?? new StorageLowLevel(client);
|
|
18641
|
+
}
|
|
18642
|
+
async call(method, params2, path = "") {
|
|
18643
|
+
try {
|
|
18644
|
+
return await this.client.call(method, await this.prepareParams(params2));
|
|
18645
|
+
} catch (exc) {
|
|
18646
|
+
throw mapStorageError(exc, path);
|
|
18304
18647
|
}
|
|
18305
18648
|
}
|
|
18306
|
-
|
|
18307
|
-
const
|
|
18308
|
-
const
|
|
18309
|
-
const
|
|
18310
|
-
|
|
18311
|
-
|
|
18312
|
-
|
|
18313
|
-
|
|
18649
|
+
async prepareParams(params2) {
|
|
18650
|
+
const next = { ...params2 ?? {} };
|
|
18651
|
+
const signAs = text2(next.sign_as ?? next.signAs);
|
|
18652
|
+
const aidStore = next.aid_store ?? next.aidStore;
|
|
18653
|
+
delete next.sign_as;
|
|
18654
|
+
delete next.signAs;
|
|
18655
|
+
delete next.aid_store;
|
|
18656
|
+
delete next.aidStore;
|
|
18657
|
+
if (signAs) {
|
|
18658
|
+
const currentAid = text2(this.client.aid ?? this.client._aid);
|
|
18659
|
+
if (currentAid.toLowerCase() !== signAs.toLowerCase()) {
|
|
18660
|
+
if (!aidStore) {
|
|
18661
|
+
throw new Error(`group.fs operation requires aidStore to sign as ${signAs}`);
|
|
18662
|
+
}
|
|
18663
|
+
next._client_signature_identity = await loadSigningAID(aidStore, signAs);
|
|
18664
|
+
}
|
|
18314
18665
|
}
|
|
18315
|
-
|
|
18316
|
-
this.stale.delete(key);
|
|
18317
|
-
return this.verificationVersions.get(key) ?? 0;
|
|
18666
|
+
return stripNil(next);
|
|
18318
18667
|
}
|
|
18319
|
-
|
|
18320
|
-
return this.
|
|
18668
|
+
ls(path, options = {}) {
|
|
18669
|
+
return this.call("group.fs.ls", { path, ...options }, path);
|
|
18321
18670
|
}
|
|
18322
|
-
|
|
18323
|
-
|
|
18324
|
-
return value ? { ...value } : null;
|
|
18671
|
+
find(path, options = {}) {
|
|
18672
|
+
return this.call("group.fs.find", { path, ...options }, path);
|
|
18325
18673
|
}
|
|
18326
|
-
|
|
18327
|
-
return this.
|
|
18674
|
+
stat(path, options = {}) {
|
|
18675
|
+
return this.call("group.fs.stat", { path, ...options }, path);
|
|
18328
18676
|
}
|
|
18329
|
-
|
|
18330
|
-
|
|
18331
|
-
const value = this.settings.get(this.key(localAid, groupAid)) ?? {};
|
|
18332
|
-
if (!keys.every((item) => item in value)) return null;
|
|
18333
|
-
return Object.fromEntries(keys.filter((item) => item in value).map((item) => [item, value[item]]));
|
|
18677
|
+
lstat(path, options = {}) {
|
|
18678
|
+
return this.call("group.fs.lstat", { path, ...options }, path);
|
|
18334
18679
|
}
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
if (this.verificationVersions.get(key) !== GROUP_INDEX_CACHE_VERIFICATION_VERSION) {
|
|
18338
|
-
return { cached: {}, missing: [...keys] };
|
|
18339
|
-
}
|
|
18340
|
-
const value = this.settings.get(key) ?? {};
|
|
18341
|
-
const localEntryEtags = this.entryEtags.get(key) ?? {};
|
|
18342
|
-
const remoteEntryEtags = Object.fromEntries(entries.map((item) => [String(item.key ?? ""), String(item.etag ?? "")]));
|
|
18343
|
-
const cached = {};
|
|
18344
|
-
const missing = [];
|
|
18345
|
-
for (const item of keys) {
|
|
18346
|
-
if (item in value && localEntryEtags[item] === remoteEntryEtags[item]) {
|
|
18347
|
-
cached[item] = value[item];
|
|
18348
|
-
} else {
|
|
18349
|
-
missing.push(item);
|
|
18350
|
-
}
|
|
18351
|
-
}
|
|
18352
|
-
return { cached, missing };
|
|
18680
|
+
mkdir(path, options = {}) {
|
|
18681
|
+
return this.call("group.fs.mkdir", { path, parents: options.parents ?? false, ...options }, path);
|
|
18353
18682
|
}
|
|
18354
|
-
|
|
18355
|
-
const
|
|
18356
|
-
const
|
|
18357
|
-
|
|
18358
|
-
this.
|
|
18359
|
-
this.entryEtags.set(key, Object.fromEntries(options.entries.map((item) => [String(item.key ?? ""), String(item.etag ?? "")]).filter(([entryKey]) => Boolean(entryKey))));
|
|
18360
|
-
this.localEtags.set(key, etag);
|
|
18361
|
-
this.verificationVersions.set(key, GROUP_INDEX_CACHE_VERIFICATION_VERSION);
|
|
18362
|
-
this.stale.delete(key);
|
|
18683
|
+
setAcl(path, options = {}) {
|
|
18684
|
+
const grantee = options.grantee_aid ?? options.granteeAid ?? "role:admin";
|
|
18685
|
+
const params2 = { ...options, path, grantee_aid: grantee, perms: options.perms ?? "rwx" };
|
|
18686
|
+
delete params2.granteeAid;
|
|
18687
|
+
return this.call("group.fs.set_acl", params2, path);
|
|
18363
18688
|
}
|
|
18364
|
-
|
|
18365
|
-
const
|
|
18366
|
-
|
|
18367
|
-
|
|
18368
|
-
|
|
18369
|
-
this.localEtags.set(key, String(record.local_etag ?? ""));
|
|
18370
|
-
this.settings.set(key, isRecord2(record.settings) ? { ...record.settings } : {});
|
|
18371
|
-
this.entryEtags.set(key, isRecord2(record.entry_etags) ? Object.fromEntries(Object.entries(record.entry_etags).map(([entryKey, entryEtag]) => [entryKey, String(entryEtag ?? "")])) : {});
|
|
18372
|
-
}
|
|
18373
|
-
const remoteEtag = String(this.remote.get(key)?.etag ?? "");
|
|
18374
|
-
if (remoteEtag && this.localEtags.get(key) !== remoteEtag) this.stale.add(key);
|
|
18375
|
-
if (remoteEtag && this.localEtags.get(key) === remoteEtag) this.stale.delete(key);
|
|
18689
|
+
removeAcl(path, options = {}) {
|
|
18690
|
+
const grantee = options.grantee_aid ?? options.granteeAid ?? "role:admin";
|
|
18691
|
+
const params2 = { ...options, path, grantee_aid: grantee };
|
|
18692
|
+
delete params2.granteeAid;
|
|
18693
|
+
return this.call("group.fs.remove_acl", params2, path);
|
|
18376
18694
|
}
|
|
18377
|
-
|
|
18378
|
-
return
|
|
18695
|
+
getAcl(path, options = {}) {
|
|
18696
|
+
return this.call("group.fs.get_acl", { path, ...options }, path);
|
|
18379
18697
|
}
|
|
18380
|
-
}
|
|
18381
|
-
|
|
18382
|
-
|
|
18383
|
-
}
|
|
18384
|
-
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
|
|
18388
|
-
|
|
18389
|
-
}
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
|
|
18698
|
+
listAcl(path, options = {}) {
|
|
18699
|
+
return this.call("group.fs.list_acl", { path, ...options }, path);
|
|
18700
|
+
}
|
|
18701
|
+
rm(path, options = {}) {
|
|
18702
|
+
return this.call("group.fs.rm", {
|
|
18703
|
+
path,
|
|
18704
|
+
recursive: options.recursive ?? false,
|
|
18705
|
+
force: options.force ?? false,
|
|
18706
|
+
...options
|
|
18707
|
+
}, path);
|
|
18708
|
+
}
|
|
18709
|
+
async cp(src, dst, options = {}) {
|
|
18710
|
+
const groupRef = options.group_aid ?? options.groupAid ?? options.group_id ?? options.groupId;
|
|
18711
|
+
const srcGroupRef = options.src_group_aid ?? options.srcGroupAid ?? options.src_group_id ?? options.srcGroupId;
|
|
18712
|
+
const dstGroupRef = options.dst_group_aid ?? options.dstGroupAid ?? options.dst_group_id ?? options.dstGroupId;
|
|
18713
|
+
const srcRemote = typeof src === "string" && isGroupRemoteCopyPath(src, srcGroupRef, groupRef);
|
|
18714
|
+
const dstRemote = typeof dst === "string" && isGroupRemoteCopyPath(dst, dstGroupRef, groupRef);
|
|
18715
|
+
if (srcRemote && dstRemote) return this.copyRemoteToRemote(src, dst, options);
|
|
18716
|
+
if (!srcRemote && dstRemote) return this.uploadToGroup(src, dst, options);
|
|
18717
|
+
if (srcRemote && !dstRemote) return this.downloadFromGroup(src, dst, options);
|
|
18718
|
+
throw new StorageError("local-to-local copy is not handled by group.fs", "EINVAL", typeof src === "string" ? src : "");
|
|
18719
|
+
}
|
|
18720
|
+
async mv(src, dst, options = {}) {
|
|
18721
|
+
const groupRef = options.group_aid ?? options.groupAid ?? options.group_id ?? options.groupId;
|
|
18722
|
+
const srcGroupRef = options.src_group_aid ?? options.srcGroupAid ?? options.src_group_id ?? options.srcGroupId;
|
|
18723
|
+
const dstGroupRef = options.dst_group_aid ?? options.dstGroupAid ?? options.dst_group_id ?? options.dstGroupId;
|
|
18724
|
+
if (!isGroupRemoteCopyPath(src, srcGroupRef, groupRef) || !isGroupRemoteCopyPath(dst, dstGroupRef, groupRef)) {
|
|
18725
|
+
throw new StorageError("group.fs.mv only supports group remote paths", "EINVAL", src);
|
|
18726
|
+
}
|
|
18727
|
+
const params2 = this.remoteCopyParams(src, dst, options);
|
|
18728
|
+
return this.call("group.fs.mv", params2, src);
|
|
18729
|
+
}
|
|
18730
|
+
df(pathOrGroup, options = {}) {
|
|
18731
|
+
const params2 = { ...options };
|
|
18732
|
+
if (pathOrGroup !== void 0 && pathOrGroup !== null) params2.path = pathOrGroup;
|
|
18733
|
+
return this.call("group.fs.df", params2, pathOrGroup ?? "");
|
|
18734
|
+
}
|
|
18735
|
+
mount(path, options = {}) {
|
|
18736
|
+
return this.call("group.fs.mount", { path, ...options }, path);
|
|
18737
|
+
}
|
|
18738
|
+
umount(path, options = {}) {
|
|
18739
|
+
return this.call("group.fs.umount", { path, ...options }, path);
|
|
18740
|
+
}
|
|
18741
|
+
remoteCopyParams(src, dst, options) {
|
|
18742
|
+
const params2 = { ...options, src, dst };
|
|
18743
|
+
delete params2.contentType;
|
|
18744
|
+
delete params2.content_type;
|
|
18745
|
+
delete params2.metadata;
|
|
18746
|
+
delete params2.expectedVersion;
|
|
18747
|
+
delete params2.expected_version;
|
|
18748
|
+
delete params2.parents;
|
|
18749
|
+
delete params2.verifyHash;
|
|
18750
|
+
delete params2.verify_hash;
|
|
18751
|
+
delete params2.sourceType;
|
|
18752
|
+
delete params2.source_type;
|
|
18753
|
+
delete params2.localPath;
|
|
18754
|
+
delete params2.local_path;
|
|
18755
|
+
if (options.groupAid !== void 0) params2.group_aid = options.groupAid;
|
|
18756
|
+
if (options.groupId !== void 0) params2.group_id = options.groupId;
|
|
18757
|
+
if (options.srcGroupAid !== void 0) params2.src_group_aid = options.srcGroupAid;
|
|
18758
|
+
if (options.srcGroupId !== void 0) params2.src_group_id = options.srcGroupId;
|
|
18759
|
+
if (options.dstGroupAid !== void 0) params2.dst_group_aid = options.dstGroupAid;
|
|
18760
|
+
if (options.dstGroupId !== void 0) params2.dst_group_id = options.dstGroupId;
|
|
18761
|
+
if (options.followSymlinks !== void 0) params2.follow_symlinks = options.followSymlinks;
|
|
18762
|
+
delete params2.groupAid;
|
|
18763
|
+
delete params2.srcGroupAid;
|
|
18764
|
+
delete params2.dstGroupAid;
|
|
18765
|
+
delete params2.followSymlinks;
|
|
18766
|
+
if (!options.force) delete params2.force;
|
|
18767
|
+
if (!options.recursive) delete params2.recursive;
|
|
18768
|
+
return stripNil(params2);
|
|
18769
|
+
}
|
|
18770
|
+
copyRemoteToRemote(src, dst, options) {
|
|
18771
|
+
return this.call("group.fs.cp", this.remoteCopyParams(src, dst, options), src);
|
|
18772
|
+
}
|
|
18773
|
+
async sourceBytes(src, options) {
|
|
18774
|
+
if (typeof src === "string") {
|
|
18775
|
+
const sourceType = text2(options.source_type ?? options.sourceType);
|
|
18776
|
+
if (isExplicitLocalPath(src) || sourceType === "path" || options.localPath === true || options.local_path === true) {
|
|
18777
|
+
const localPath = stripLocalPathPrefix(src);
|
|
18778
|
+
const data2 = await readLocalFile(localPath);
|
|
18779
|
+
return { data: data2, contentType: contentTypeForData(data2, localPath) };
|
|
18780
|
+
}
|
|
18781
|
+
return { data: await toBytes2(src), contentType: "text/plain;charset=utf-8" };
|
|
18782
|
+
}
|
|
18783
|
+
const data = await toBytes2(src);
|
|
18784
|
+
const fileName = text2(src.name);
|
|
18785
|
+
return { data, contentType: contentTypeForData(data, fileName) };
|
|
18786
|
+
}
|
|
18787
|
+
async uploadToGroup(src, dst, options) {
|
|
18788
|
+
const { data, contentType } = await this.sourceBytes(src, options);
|
|
18789
|
+
const digest = await sha256Hex2(data);
|
|
18790
|
+
const groupAid = options.group_aid ?? options.groupAid ?? options.dst_group_aid ?? options.dstGroupAid;
|
|
18791
|
+
const groupId = options.group_id ?? options.groupId ?? options.dst_group_id ?? options.dstGroupId;
|
|
18792
|
+
const baseParams = stripNil({
|
|
18793
|
+
...options,
|
|
18794
|
+
path: dst,
|
|
18795
|
+
group_aid: groupAid,
|
|
18796
|
+
group_id: groupAid ? void 0 : groupId,
|
|
18797
|
+
size_bytes: data.byteLength,
|
|
18798
|
+
sha256: digest,
|
|
18799
|
+
content_type: options.content_type ?? options.contentType ?? contentType,
|
|
18800
|
+
force: options.force ?? false,
|
|
18801
|
+
parents: options.parents ?? true,
|
|
18802
|
+
metadata: options.metadata,
|
|
18803
|
+
expected_version: options.expected_version ?? options.expectedVersion
|
|
18804
|
+
});
|
|
18805
|
+
delete baseParams.groupId;
|
|
18806
|
+
delete baseParams.groupAid;
|
|
18807
|
+
delete baseParams.dstGroupId;
|
|
18808
|
+
delete baseParams.dstGroupAid;
|
|
18809
|
+
delete baseParams.dst_group_aid;
|
|
18810
|
+
delete baseParams.dst_group_id;
|
|
18811
|
+
delete baseParams.srcGroupId;
|
|
18812
|
+
delete baseParams.srcGroupAid;
|
|
18813
|
+
delete baseParams.src_group_aid;
|
|
18814
|
+
delete baseParams.src_group_id;
|
|
18815
|
+
delete baseParams.contentType;
|
|
18816
|
+
delete baseParams.expectedVersion;
|
|
18817
|
+
delete baseParams.verifyHash;
|
|
18818
|
+
delete baseParams.verify_hash;
|
|
18819
|
+
delete baseParams.recursive;
|
|
18820
|
+
delete baseParams.followSymlinks;
|
|
18821
|
+
delete baseParams.follow_symlinks;
|
|
18822
|
+
delete baseParams.sourceType;
|
|
18823
|
+
delete baseParams.source_type;
|
|
18824
|
+
delete baseParams.localPath;
|
|
18825
|
+
delete baseParams.local_path;
|
|
18826
|
+
const check = await this.call("group.fs.check_upload", baseParams, dst);
|
|
18827
|
+
if (check && typeof check === "object") {
|
|
18828
|
+
const raw = check;
|
|
18829
|
+
if (raw.within_limit === false) throw new StorageError("file size exceeds group fs upload limit", "E2BIG", dst, raw);
|
|
18830
|
+
if (raw.target_exists && !options.force && options.expected_version === void 0 && options.expectedVersion === void 0) {
|
|
18831
|
+
throw new StorageExistsError("group fs target already exists", "EEXIST", dst, raw.target);
|
|
18832
|
+
}
|
|
18833
|
+
if (raw.instant || raw.dedup_hit || raw.skip_upload) {
|
|
18834
|
+
return this.call("group.fs.complete_upload", {
|
|
18835
|
+
...baseParams,
|
|
18836
|
+
skip_blob: true,
|
|
18837
|
+
session_id: raw.session_id
|
|
18838
|
+
}, dst);
|
|
18839
|
+
}
|
|
18840
|
+
}
|
|
18841
|
+
const session = await this.call("group.fs.create_upload_session", baseParams, dst);
|
|
18842
|
+
if (!session || typeof session !== "object") {
|
|
18843
|
+
throw new StorageError(`group.fs.create_upload_session returned invalid response`, "ESTORAGE", dst, session);
|
|
18844
|
+
}
|
|
18845
|
+
const rawSession = session;
|
|
18846
|
+
const uploadUrl = text2(rawSession.upload_url ?? rawSession.url);
|
|
18847
|
+
if (!uploadUrl) throw new StorageError("group.fs.create_upload_session did not return upload_url", "ESTORAGE", dst, session);
|
|
18848
|
+
const headers = {};
|
|
18849
|
+
if (rawSession.headers && typeof rawSession.headers === "object" && !Array.isArray(rawSession.headers)) {
|
|
18850
|
+
for (const [key, value] of Object.entries(rawSession.headers)) {
|
|
18851
|
+
if (value !== void 0 && value !== null) headers[key] = String(value);
|
|
18852
|
+
}
|
|
18853
|
+
}
|
|
18854
|
+
for (const key of Object.keys(headers)) {
|
|
18855
|
+
if (key.toLowerCase() === "content-type") delete headers[key];
|
|
18856
|
+
}
|
|
18857
|
+
headers["Content-Type"] = text2(baseParams.content_type) || "application/octet-stream";
|
|
18858
|
+
await this.lowlevel.httpPut(uploadUrl, data, headers);
|
|
18859
|
+
return this.call("group.fs.complete_upload", {
|
|
18860
|
+
...baseParams,
|
|
18861
|
+
session_id: rawSession.session_id ?? rawSession.id
|
|
18862
|
+
}, dst);
|
|
18863
|
+
}
|
|
18864
|
+
async downloadFromGroup(src, dst, options) {
|
|
18865
|
+
const target = downloadTarget(dst);
|
|
18866
|
+
if (target.mode === "local" && target.localPath) {
|
|
18867
|
+
const status = await localFileStatus(target.localPath);
|
|
18868
|
+
if (status === "file" && !options.force) {
|
|
18869
|
+
throw new StorageExistsError(`local path already exists: ${target.localPath}`, "EEXIST", target.localPath);
|
|
18870
|
+
}
|
|
18871
|
+
}
|
|
18872
|
+
const groupAid = options.group_aid ?? options.groupAid ?? options.src_group_aid ?? options.srcGroupAid;
|
|
18873
|
+
const groupId = options.group_id ?? options.groupId ?? options.src_group_id ?? options.srcGroupId;
|
|
18874
|
+
const params2 = stripNil({
|
|
18875
|
+
...options,
|
|
18876
|
+
path: src,
|
|
18877
|
+
group_aid: groupAid,
|
|
18878
|
+
group_id: groupAid ? void 0 : groupId
|
|
18879
|
+
});
|
|
18880
|
+
delete params2.groupId;
|
|
18881
|
+
delete params2.groupAid;
|
|
18882
|
+
delete params2.srcGroupId;
|
|
18883
|
+
delete params2.srcGroupAid;
|
|
18884
|
+
delete params2.src_group_aid;
|
|
18885
|
+
delete params2.src_group_id;
|
|
18886
|
+
delete params2.dstGroupId;
|
|
18887
|
+
delete params2.dstGroupAid;
|
|
18888
|
+
delete params2.dst_group_aid;
|
|
18889
|
+
delete params2.dst_group_id;
|
|
18890
|
+
delete params2.force;
|
|
18891
|
+
delete params2.recursive;
|
|
18892
|
+
delete params2.parents;
|
|
18893
|
+
delete params2.contentType;
|
|
18894
|
+
delete params2.content_type;
|
|
18895
|
+
delete params2.expectedVersion;
|
|
18896
|
+
delete params2.expected_version;
|
|
18897
|
+
delete params2.verifyHash;
|
|
18898
|
+
delete params2.verify_hash;
|
|
18899
|
+
delete params2.sourceType;
|
|
18900
|
+
delete params2.source_type;
|
|
18901
|
+
delete params2.localPath;
|
|
18902
|
+
delete params2.local_path;
|
|
18903
|
+
const ticket = await this.call("group.fs.create_download_ticket", params2, src);
|
|
18904
|
+
if (!ticket || typeof ticket !== "object") {
|
|
18905
|
+
throw new StorageError("group.fs.create_download_ticket returned invalid response", "ESTORAGE", src, ticket);
|
|
18906
|
+
}
|
|
18907
|
+
const rawTicket = ticket;
|
|
18908
|
+
const downloadUrl = text2(rawTicket.download_url ?? rawTicket.url);
|
|
18909
|
+
if (!downloadUrl) throw new StorageError("group.fs.create_download_ticket did not return download_url", "ESTORAGE", src, ticket);
|
|
18910
|
+
const data = await this.lowlevel.httpGet(downloadUrl, bearerHeaders(this.client));
|
|
18911
|
+
const expectedSha = text2(rawTicket.sha256);
|
|
18912
|
+
const verifyHash = options.verify_hash ?? options.verifyHash ?? true;
|
|
18913
|
+
const verified = !verifyHash || !expectedSha || await sha256Hex2(data) === expectedSha.toLowerCase();
|
|
18914
|
+
if (verifyHash && !verified) {
|
|
18915
|
+
throw new StorageConflictError("download hash verification failed", "ECONFLICT", src, ticket);
|
|
18916
|
+
}
|
|
18917
|
+
if (target.mode === "local" && target.localPath) {
|
|
18918
|
+
const status = await localFileStatus(target.localPath);
|
|
18919
|
+
if (status === "dir") {
|
|
18920
|
+
throw new StorageError("directory download target is not supported in this JavaScript runtime", "EISDIR", target.localPath);
|
|
18921
|
+
}
|
|
18922
|
+
if (status === "file" && !options.force) {
|
|
18923
|
+
throw new StorageExistsError(`local path already exists: ${target.localPath}`, "EEXIST", target.localPath);
|
|
18924
|
+
}
|
|
18925
|
+
}
|
|
18926
|
+
const result = {
|
|
18927
|
+
path: src,
|
|
18928
|
+
localPath: target.localPath,
|
|
18929
|
+
size: data.byteLength,
|
|
18930
|
+
sha256: expectedSha,
|
|
18931
|
+
verified,
|
|
18932
|
+
data
|
|
18933
|
+
};
|
|
18934
|
+
if (target.mode === "blob" && typeof Blob !== "undefined") {
|
|
18935
|
+
result.blob = new Blob([toExactArrayBuffer(data)], { type: text2(rawTicket.content_type) || "application/octet-stream" });
|
|
18936
|
+
}
|
|
18937
|
+
if (target.mode === "local" && target.localPath) {
|
|
18938
|
+
result.wroteLocalFile = await writeLocalFile(target.localPath, data, Boolean(options.force));
|
|
18939
|
+
}
|
|
18940
|
+
return result;
|
|
18941
|
+
}
|
|
18942
|
+
};
|
|
18943
|
+
|
|
18944
|
+
// src/v2/crypto/canonical.ts
|
|
18945
|
+
var encoder = new TextEncoder();
|
|
18946
|
+
var MAX_SAFE_JSON_INTEGER = 9007199254740991;
|
|
18947
|
+
function canonicalJson(obj) {
|
|
18948
|
+
return encoder.encode(serialize(obj));
|
|
18949
|
+
}
|
|
18950
|
+
function serialize(obj) {
|
|
18951
|
+
if (obj === null) return "null";
|
|
18952
|
+
if (obj === true) return "true";
|
|
18953
|
+
if (obj === false) return "false";
|
|
18954
|
+
if (typeof obj === "number") {
|
|
18955
|
+
return serializeNumber(obj);
|
|
18956
|
+
}
|
|
18957
|
+
if (typeof obj === "string") {
|
|
18958
|
+
return serializeString(obj);
|
|
18959
|
+
}
|
|
18960
|
+
if (Array.isArray(obj)) {
|
|
18961
|
+
return serializeArray(obj);
|
|
18962
|
+
}
|
|
18963
|
+
if (typeof obj === "object") {
|
|
18964
|
+
return serializeObject(obj);
|
|
18965
|
+
}
|
|
18966
|
+
throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
|
|
18967
|
+
}
|
|
18968
|
+
function serializeNumber(n) {
|
|
18969
|
+
if (!isFinite(n)) {
|
|
18970
|
+
throw new RangeError("canonicalJson: Infinity and NaN not allowed");
|
|
18971
|
+
}
|
|
18972
|
+
if (Object.is(n, -0)) return "0";
|
|
18973
|
+
if (Number.isInteger(n)) {
|
|
18974
|
+
if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
|
|
18975
|
+
throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
|
|
18976
|
+
}
|
|
18977
|
+
return String(n);
|
|
18978
|
+
}
|
|
18979
|
+
return expandExponent(String(n));
|
|
18980
|
+
}
|
|
18981
|
+
function expandExponent(s) {
|
|
18982
|
+
if (!/[eE]/.test(s)) return s;
|
|
18983
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
18984
|
+
if (!match) {
|
|
18985
|
+
throw new TypeError(`canonicalJson: invalid number ${s}`);
|
|
18986
|
+
}
|
|
18987
|
+
const sign = match[1] ?? "";
|
|
18988
|
+
const intPart = match[2] ?? "";
|
|
18989
|
+
const fracPart = match[3] ?? "";
|
|
18990
|
+
const exp = Number(match[4]);
|
|
18991
|
+
const digits = intPart + fracPart;
|
|
18992
|
+
const point = intPart.length + exp;
|
|
18993
|
+
if (point <= 0) {
|
|
18994
|
+
return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
18995
|
+
}
|
|
18996
|
+
if (point >= digits.length) {
|
|
18997
|
+
return `${sign}${digits}${"0".repeat(point - digits.length)}`;
|
|
18998
|
+
}
|
|
18999
|
+
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
19000
|
+
}
|
|
19001
|
+
function serializeString(s) {
|
|
19002
|
+
let result = '"';
|
|
19003
|
+
for (let i = 0; i < s.length; i++) {
|
|
19004
|
+
const ch = s[i];
|
|
19005
|
+
const code = s.charCodeAt(i);
|
|
19006
|
+
if (ch === '"') {
|
|
19007
|
+
result += '\\"';
|
|
19008
|
+
} else if (ch === "\\") {
|
|
19009
|
+
result += "\\\\";
|
|
19010
|
+
} else if (ch === "\b") {
|
|
19011
|
+
result += "\\b";
|
|
19012
|
+
} else if (ch === "\f") {
|
|
19013
|
+
result += "\\f";
|
|
19014
|
+
} else if (ch === "\n") {
|
|
19015
|
+
result += "\\n";
|
|
19016
|
+
} else if (ch === "\r") {
|
|
19017
|
+
result += "\\r";
|
|
19018
|
+
} else if (ch === " ") {
|
|
19019
|
+
result += "\\t";
|
|
19020
|
+
} else if (code < 32) {
|
|
19021
|
+
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
19022
|
+
} else {
|
|
19023
|
+
result += ch;
|
|
19024
|
+
}
|
|
19025
|
+
}
|
|
19026
|
+
result += '"';
|
|
19027
|
+
return result;
|
|
19028
|
+
}
|
|
19029
|
+
function serializeArray(arr) {
|
|
19030
|
+
const items = arr.map((item) => serialize(item));
|
|
19031
|
+
return "[" + items.join(",") + "]";
|
|
19032
|
+
}
|
|
19033
|
+
function serializeObject(obj) {
|
|
19034
|
+
const sortedKeys = Object.keys(obj).sort(compareCodePoints);
|
|
19035
|
+
const pairs2 = sortedKeys.map(
|
|
19036
|
+
(key) => serializeString(key) + ":" + serialize(obj[key])
|
|
19037
|
+
);
|
|
19038
|
+
return "{" + pairs2.join(",") + "}";
|
|
19039
|
+
}
|
|
19040
|
+
function compareCodePoints(a, b) {
|
|
19041
|
+
const ac = Array.from(a);
|
|
19042
|
+
const bc = Array.from(b);
|
|
19043
|
+
const n = Math.min(ac.length, bc.length);
|
|
19044
|
+
for (let i = 0; i < n; i++) {
|
|
19045
|
+
const av = ac[i].codePointAt(0) ?? 0;
|
|
19046
|
+
const bv = bc[i].codePointAt(0) ?? 0;
|
|
19047
|
+
if (av !== bv) return av - bv;
|
|
19048
|
+
}
|
|
19049
|
+
return ac.length - bc.length;
|
|
19050
|
+
}
|
|
19051
|
+
|
|
19052
|
+
// src/group-index.ts
|
|
19053
|
+
var GROUP_INDEX_SCHEMA = "aun.group.index.v1";
|
|
19054
|
+
var GROUP_INDEX_KEY = "group.index";
|
|
19055
|
+
var GROUP_INDEX_SIG_ALG = "ECDSA-P256-SHA256";
|
|
19056
|
+
var GROUP_INDEX_CACHE_VERIFICATION_VERSION = 1;
|
|
19057
|
+
var GroupIndexMetaCache = class {
|
|
19058
|
+
constructor() {
|
|
19059
|
+
__publicField(this, "remote", /* @__PURE__ */ new Map());
|
|
19060
|
+
__publicField(this, "localEtags", /* @__PURE__ */ new Map());
|
|
19061
|
+
__publicField(this, "stale", /* @__PURE__ */ new Set());
|
|
19062
|
+
__publicField(this, "settings", /* @__PURE__ */ new Map());
|
|
19063
|
+
__publicField(this, "entryEtags", /* @__PURE__ */ new Map());
|
|
19064
|
+
__publicField(this, "verificationVersions", /* @__PURE__ */ new Map());
|
|
19065
|
+
}
|
|
19066
|
+
observeRpcMeta(meta, options) {
|
|
19067
|
+
const groupIndexes = isRecord2(meta?.group_indexes) ? meta.group_indexes : null;
|
|
19068
|
+
if (!groupIndexes) return;
|
|
19069
|
+
const local = String(options.localAid ?? "");
|
|
19070
|
+
for (const [groupAid, value] of Object.entries(groupIndexes)) {
|
|
19071
|
+
if (!isRecord2(value)) continue;
|
|
19072
|
+
const key = this.key(local, groupAid);
|
|
19073
|
+
const remoteMeta = {};
|
|
19074
|
+
for (const name of ["etag", "last_modified", "schema"]) {
|
|
19075
|
+
if (value[name] !== void 0 && value[name] !== null) remoteMeta[name] = value[name];
|
|
19076
|
+
}
|
|
19077
|
+
this.remote.set(key, remoteMeta);
|
|
19078
|
+
const remoteEtag = String(remoteMeta.etag ?? "");
|
|
19079
|
+
if (remoteEtag && this.localEtags.get(key) !== remoteEtag) this.stale.add(key);
|
|
19080
|
+
else if (remoteEtag) this.stale.delete(key);
|
|
19081
|
+
}
|
|
19082
|
+
}
|
|
19083
|
+
markFresh(localAid, groupAid, options) {
|
|
19084
|
+
const key = this.key(localAid, groupAid);
|
|
19085
|
+
const etag = String(options.etag ?? "");
|
|
19086
|
+
const previous = this.localEtags.get(key) ?? "";
|
|
19087
|
+
if (this.verificationVersions.get(key) === GROUP_INDEX_CACHE_VERIFICATION_VERSION && previous !== etag) {
|
|
19088
|
+
this.verificationVersions.delete(key);
|
|
19089
|
+
this.settings.delete(key);
|
|
19090
|
+
this.entryEtags.delete(key);
|
|
19091
|
+
}
|
|
19092
|
+
this.localEtags.set(key, etag);
|
|
19093
|
+
this.stale.delete(key);
|
|
19094
|
+
return this.verificationVersions.get(key) ?? 0;
|
|
19095
|
+
}
|
|
19096
|
+
isStale(localAid, groupAid) {
|
|
19097
|
+
return this.stale.has(this.key(localAid, groupAid));
|
|
19098
|
+
}
|
|
19099
|
+
remoteMeta(localAid, groupAid) {
|
|
19100
|
+
const value = this.remote.get(this.key(localAid, groupAid));
|
|
19101
|
+
return value ? { ...value } : null;
|
|
19102
|
+
}
|
|
19103
|
+
localEtag(localAid, groupAid) {
|
|
19104
|
+
return this.localEtags.get(this.key(localAid, groupAid)) ?? "";
|
|
19105
|
+
}
|
|
19106
|
+
cachedSettings(localAid, groupAid, keys) {
|
|
19107
|
+
if (this.verificationVersions.get(this.key(localAid, groupAid)) !== GROUP_INDEX_CACHE_VERIFICATION_VERSION) return null;
|
|
19108
|
+
const value = this.settings.get(this.key(localAid, groupAid)) ?? {};
|
|
19109
|
+
if (!keys.every((item) => item in value)) return null;
|
|
19110
|
+
return Object.fromEntries(keys.filter((item) => item in value).map((item) => [item, value[item]]));
|
|
19111
|
+
}
|
|
19112
|
+
cachedSettingsByEntries(localAid, groupAid, keys, entries) {
|
|
19113
|
+
const key = this.key(localAid, groupAid);
|
|
19114
|
+
if (this.verificationVersions.get(key) !== GROUP_INDEX_CACHE_VERIFICATION_VERSION) {
|
|
19115
|
+
return { cached: {}, missing: [...keys] };
|
|
19116
|
+
}
|
|
19117
|
+
const value = this.settings.get(key) ?? {};
|
|
19118
|
+
const localEntryEtags = this.entryEtags.get(key) ?? {};
|
|
19119
|
+
const remoteEntryEtags = Object.fromEntries(entries.map((item) => [String(item.key ?? ""), String(item.etag ?? "")]));
|
|
19120
|
+
const cached = {};
|
|
19121
|
+
const missing = [];
|
|
19122
|
+
for (const item of keys) {
|
|
19123
|
+
if (item in value && localEntryEtags[item] === remoteEntryEtags[item]) {
|
|
19124
|
+
cached[item] = value[item];
|
|
19125
|
+
} else {
|
|
19126
|
+
missing.push(item);
|
|
19127
|
+
}
|
|
19128
|
+
}
|
|
19129
|
+
return { cached, missing };
|
|
19130
|
+
}
|
|
19131
|
+
cacheSettings(localAid, groupAid, settings, options) {
|
|
19132
|
+
const key = this.key(localAid, groupAid);
|
|
19133
|
+
const etag = String(options?.etag ?? "");
|
|
19134
|
+
if (!Array.isArray(options?.entries) || !etag) return;
|
|
19135
|
+
this.settings.set(key, { ...settings });
|
|
19136
|
+
this.entryEtags.set(key, Object.fromEntries(options.entries.map((item) => [String(item.key ?? ""), String(item.etag ?? "")]).filter(([entryKey]) => Boolean(entryKey))));
|
|
19137
|
+
this.localEtags.set(key, etag);
|
|
19138
|
+
this.verificationVersions.set(key, GROUP_INDEX_CACHE_VERIFICATION_VERSION);
|
|
19139
|
+
this.stale.delete(key);
|
|
19140
|
+
}
|
|
19141
|
+
restore(localAid, groupAid, record) {
|
|
19142
|
+
const key = this.key(localAid, groupAid);
|
|
19143
|
+
if (isRecord2(record.remote_meta)) this.remote.set(key, { ...record.remote_meta });
|
|
19144
|
+
if (Number(record.verification_version ?? 0) === GROUP_INDEX_CACHE_VERIFICATION_VERSION) {
|
|
19145
|
+
this.verificationVersions.set(key, GROUP_INDEX_CACHE_VERIFICATION_VERSION);
|
|
19146
|
+
this.localEtags.set(key, String(record.local_etag ?? ""));
|
|
19147
|
+
this.settings.set(key, isRecord2(record.settings) ? { ...record.settings } : {});
|
|
19148
|
+
this.entryEtags.set(key, isRecord2(record.entry_etags) ? Object.fromEntries(Object.entries(record.entry_etags).map(([entryKey, entryEtag]) => [entryKey, String(entryEtag ?? "")])) : {});
|
|
19149
|
+
}
|
|
19150
|
+
const remoteEtag = String(this.remote.get(key)?.etag ?? "");
|
|
19151
|
+
if (remoteEtag && this.localEtags.get(key) !== remoteEtag) this.stale.add(key);
|
|
19152
|
+
if (remoteEtag && this.localEtags.get(key) === remoteEtag) this.stale.delete(key);
|
|
19153
|
+
}
|
|
19154
|
+
key(localAid, groupAid) {
|
|
19155
|
+
return `${String(localAid ?? "")}\0${String(groupAid ?? "")}`;
|
|
19156
|
+
}
|
|
19157
|
+
};
|
|
19158
|
+
async function computeGroupIndexBodyHash(entries) {
|
|
19159
|
+
return `sha256:${await sha256Hex3(entriesBytes(entries))}`;
|
|
19160
|
+
}
|
|
19161
|
+
async function groupIndexEtag(entries) {
|
|
19162
|
+
return `"sha256:${await sha256Hex3(entriesBytes(entries))}"`;
|
|
19163
|
+
}
|
|
19164
|
+
async function groupIndexSettingEtag(value) {
|
|
19165
|
+
return `"sha256:${await sha256Hex3(encode(canonicalStringify(value)))}"`;
|
|
19166
|
+
}
|
|
19167
|
+
function groupIndexSigningPayload(meta, entries) {
|
|
19168
|
+
const metaWithoutSignature = { ...meta };
|
|
19169
|
+
delete metaWithoutSignature.signature;
|
|
19170
|
+
const lines = [canonicalStringify(metaWithoutSignature)];
|
|
19171
|
+
lines.push(...canonicalEntries(entries).map((item) => canonicalStringify(item)));
|
|
19172
|
+
return encode(`${lines.join("\n")}
|
|
19173
|
+
`);
|
|
19174
|
+
}
|
|
19175
|
+
async function buildSignedGroupIndex(options) {
|
|
19176
|
+
const entries = canonicalEntries(options.entries);
|
|
19177
|
+
const meta = {
|
|
18401
19178
|
type: "index_meta",
|
|
18402
19179
|
group_aid: String(options.groupAid),
|
|
18403
19180
|
etag: await groupIndexEtag(entries),
|
|
@@ -18489,7 +19266,7 @@ function canonicalStringify(value) {
|
|
|
18489
19266
|
function encode(text3) {
|
|
18490
19267
|
return new TextEncoder().encode(text3);
|
|
18491
19268
|
}
|
|
18492
|
-
async function
|
|
19269
|
+
async function sha256Hex3(data) {
|
|
18493
19270
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", exactArrayBuffer(data));
|
|
18494
19271
|
return bytesToHex(new Uint8Array(digest));
|
|
18495
19272
|
}
|
|
@@ -18563,1222 +19340,672 @@ function indexedDocumentKeyName(params2) {
|
|
|
18563
19340
|
var RpcFacade = class {
|
|
18564
19341
|
constructor(client) {
|
|
18565
19342
|
__publicField(this, "client");
|
|
18566
|
-
this.client = client;
|
|
18567
|
-
}
|
|
18568
|
-
call(method, params2) {
|
|
18569
|
-
return this.client.call(method, stripNil2(params2));
|
|
18570
|
-
}
|
|
18571
|
-
};
|
|
18572
|
-
var MessageThoughtFacade = class extends RpcFacade {
|
|
18573
|
-
put(params2) {
|
|
18574
|
-
return this.call("message.thought.put", params2);
|
|
18575
|
-
}
|
|
18576
|
-
get(params2) {
|
|
18577
|
-
return this.call("message.thought.get", params2);
|
|
18578
|
-
}
|
|
18579
|
-
};
|
|
18580
|
-
var MessageFacade = class extends RpcFacade {
|
|
18581
|
-
constructor() {
|
|
18582
|
-
super(...arguments);
|
|
18583
|
-
__publicField(this, "_thought");
|
|
18584
|
-
}
|
|
18585
|
-
get thought() {
|
|
18586
|
-
if (!this._thought) this._thought = new MessageThoughtFacade(this.client);
|
|
18587
|
-
return this._thought;
|
|
18588
|
-
}
|
|
18589
|
-
send(params2) {
|
|
18590
|
-
return this.call("message.send", params2);
|
|
18591
|
-
}
|
|
18592
|
-
pull(params2) {
|
|
18593
|
-
return this.call("message.pull", params2);
|
|
18594
|
-
}
|
|
18595
|
-
history(params2) {
|
|
18596
|
-
return this.call("message.history", params2);
|
|
18597
|
-
}
|
|
18598
|
-
ack(params2) {
|
|
18599
|
-
return this.call("message.ack", params2);
|
|
18600
|
-
}
|
|
18601
|
-
recall(params2) {
|
|
18602
|
-
return this.call("message.recall", params2);
|
|
18603
|
-
}
|
|
18604
|
-
queryOnline(params2) {
|
|
18605
|
-
return this.call("message.query_online", params2);
|
|
18606
|
-
}
|
|
18607
|
-
};
|
|
18608
|
-
function normalizeInviteParams(params2) {
|
|
18609
|
-
const out = stripNil2(params2);
|
|
18610
|
-
const rawGroupId = out.group_id ?? out.groupId ?? out.group_aid ?? out.groupAid;
|
|
18611
|
-
if (rawGroupId !== void 0) {
|
|
18612
|
-
out.group_id = normalizeGroupId(rawGroupId);
|
|
18613
|
-
delete out.groupId;
|
|
18614
|
-
delete out.group_aid;
|
|
18615
|
-
delete out.groupAid;
|
|
18616
|
-
}
|
|
18617
|
-
for (const [alias, canonical] of [["inviteId", "invite_id"], ["inviteeAid", "invitee_aid"]]) {
|
|
18618
|
-
if (out[canonical] === void 0 && out[alias] !== void 0) out[canonical] = out[alias];
|
|
18619
|
-
delete out[alias];
|
|
18620
|
-
}
|
|
18621
|
-
return out;
|
|
18622
|
-
}
|
|
18623
|
-
var MailFacade = class extends RpcFacade {
|
|
18624
|
-
send(params2) {
|
|
18625
|
-
return this.call("mail.send", params2);
|
|
18626
|
-
}
|
|
18627
|
-
get(params2) {
|
|
18628
|
-
return this.call("mail.get", params2);
|
|
18629
|
-
}
|
|
18630
|
-
list(params2) {
|
|
18631
|
-
return this.call("mail.list", params2);
|
|
18632
|
-
}
|
|
18633
|
-
delete(params2) {
|
|
18634
|
-
return this.call("mail.delete", params2);
|
|
18635
|
-
}
|
|
18636
|
-
move(params2) {
|
|
18637
|
-
return this.call("mail.move", params2);
|
|
18638
|
-
}
|
|
18639
|
-
mark(params2) {
|
|
18640
|
-
return this.call("mail.mark", params2);
|
|
18641
|
-
}
|
|
18642
|
-
search(params2) {
|
|
18643
|
-
return this.call("mail.search", params2);
|
|
18644
|
-
}
|
|
18645
|
-
getAttachment(params2) {
|
|
18646
|
-
return this.call("mail.get_attachment", params2);
|
|
18647
|
-
}
|
|
18648
|
-
getQuota(params2) {
|
|
18649
|
-
return this.call("mail.get_quota", params2);
|
|
18650
|
-
}
|
|
18651
|
-
health(params2) {
|
|
18652
|
-
return this.call("mail.health", params2);
|
|
18653
|
-
}
|
|
18654
|
-
status(params2) {
|
|
18655
|
-
return this.call("mail.status", params2);
|
|
18656
|
-
}
|
|
18657
|
-
};
|
|
18658
|
-
var GroupThoughtFacade = class extends RpcFacade {
|
|
18659
|
-
put(params2) {
|
|
18660
|
-
return this.call("group.thought.put", params2);
|
|
18661
|
-
}
|
|
18662
|
-
get(params2) {
|
|
18663
|
-
return this.call("group.thought.get", params2);
|
|
18664
|
-
}
|
|
18665
|
-
};
|
|
18666
|
-
var GroupFacade = class extends RpcFacade {
|
|
18667
|
-
constructor() {
|
|
18668
|
-
super(...arguments);
|
|
18669
|
-
__publicField(this, "_fs");
|
|
18670
|
-
__publicField(this, "_thought");
|
|
18671
|
-
}
|
|
18672
|
-
get fs() {
|
|
18673
|
-
if (!this._fs) this._fs = new GroupFSVFS(this.client);
|
|
18674
|
-
return this._fs;
|
|
18675
|
-
}
|
|
18676
|
-
get thought() {
|
|
18677
|
-
if (!this._thought) this._thought = new GroupThoughtFacade(this.client);
|
|
18678
|
-
return this._thought;
|
|
18679
|
-
}
|
|
18680
|
-
create(params2) {
|
|
18681
|
-
const split = splitAidStore(params2);
|
|
18682
|
-
if (split.params.group_name === void 0 && split.params.groupName !== void 0) {
|
|
18683
|
-
split.params.group_name = split.params.groupName;
|
|
18684
|
-
delete split.params.groupName;
|
|
18685
|
-
}
|
|
18686
|
-
if (split.aidStore && typeof this.client.createGroup === "function") {
|
|
18687
|
-
return this.client.createGroup(split.params, { aidStore: split.aidStore });
|
|
18688
|
-
}
|
|
18689
|
-
return this.call("group.create", split.params);
|
|
18690
|
-
}
|
|
18691
|
-
bindAid(params2) {
|
|
18692
|
-
return this.call("group.bind_aid", splitAidStore(params2).params);
|
|
18693
|
-
}
|
|
18694
|
-
bindGroupAid(params2) {
|
|
18695
|
-
const split = splitAidStore(params2);
|
|
18696
|
-
if (split.aidStore && typeof this.client.bindGroupAid === "function") {
|
|
18697
|
-
return this.client.bindGroupAid(split.params, { aidStore: split.aidStore });
|
|
18698
|
-
}
|
|
18699
|
-
return this.call("group.bind_group_aid", split.params);
|
|
18700
|
-
}
|
|
18701
|
-
renewGroupAid(params2) {
|
|
18702
|
-
const split = splitAidStore(params2);
|
|
18703
|
-
if (typeof this.client.renewGroupAid === "function") {
|
|
18704
|
-
return this.client.renewGroupAid(split.params, { aidStore: split.aidStore });
|
|
18705
|
-
}
|
|
18706
|
-
return this.call("group.renew_group_aid", split.params);
|
|
18707
|
-
}
|
|
18708
|
-
getInfo(params2) {
|
|
18709
|
-
return this.call("group.get_info", params2);
|
|
18710
|
-
}
|
|
18711
|
-
update(params2) {
|
|
18712
|
-
return this.call("group.update", params2);
|
|
18713
|
-
}
|
|
18714
|
-
list(params2) {
|
|
18715
|
-
return this.call("group.list", params2);
|
|
18716
|
-
}
|
|
18717
|
-
listMy(params2) {
|
|
18718
|
-
return this.call("group.list_my", params2);
|
|
18719
|
-
}
|
|
18720
|
-
search(params2) {
|
|
18721
|
-
return this.call("group.search", params2);
|
|
18722
|
-
}
|
|
18723
|
-
suspend(params2) {
|
|
18724
|
-
return this.call("group.suspend", params2);
|
|
18725
|
-
}
|
|
18726
|
-
resume(params2) {
|
|
18727
|
-
return this.call("group.resume", params2);
|
|
18728
|
-
}
|
|
18729
|
-
dissolve(params2) {
|
|
18730
|
-
return this.call("group.dissolve", params2);
|
|
18731
|
-
}
|
|
18732
|
-
addMember(params2) {
|
|
18733
|
-
return this.call("group.add_member", params2);
|
|
18734
|
-
}
|
|
18735
|
-
async inviteCall(method, params2) {
|
|
18736
|
-
const normalized = normalizeInviteParams(params2);
|
|
18737
|
-
try {
|
|
18738
|
-
const result = await this.call(method, normalized);
|
|
18739
|
-
await this.client._onGroupInviteActionResult?.(method, normalized, result);
|
|
18740
|
-
return result;
|
|
18741
|
-
} catch (error) {
|
|
18742
|
-
await this.client._onGroupInviteActionResult?.(method, normalized, void 0, error);
|
|
18743
|
-
throw error;
|
|
18744
|
-
}
|
|
18745
|
-
}
|
|
18746
|
-
inviteMember(params2) {
|
|
18747
|
-
return this.inviteCall("group.invite_member", params2);
|
|
18748
|
-
}
|
|
18749
|
-
listMyInvites(params2) {
|
|
18750
|
-
return this.inviteCall("group.list_my_invites", params2);
|
|
18751
|
-
}
|
|
18752
|
-
acceptInvite(params2) {
|
|
18753
|
-
return this.inviteCall("group.accept_invite", params2);
|
|
18754
|
-
}
|
|
18755
|
-
rejectInvite(params2) {
|
|
18756
|
-
return this.inviteCall("group.reject_invite", params2);
|
|
18757
|
-
}
|
|
18758
|
-
revokeInvite(params2) {
|
|
18759
|
-
return this.inviteCall("group.revoke_invite", params2);
|
|
18760
|
-
}
|
|
18761
|
-
getMembers(params2) {
|
|
18762
|
-
return this.call("group.get_members", params2);
|
|
18763
|
-
}
|
|
18764
|
-
kick(params2) {
|
|
18765
|
-
return this.call("group.kick", params2);
|
|
18766
|
-
}
|
|
18767
|
-
leave(params2) {
|
|
18768
|
-
return this.call("group.leave", params2);
|
|
18769
|
-
}
|
|
18770
|
-
setRole(params2) {
|
|
18771
|
-
return this.call("group.set_role", params2);
|
|
18772
|
-
}
|
|
18773
|
-
transferOwner(params2) {
|
|
18774
|
-
const split = splitAidStore(params2);
|
|
18775
|
-
if (split.aidStore && typeof this.client.startGroupTransfer === "function") {
|
|
18776
|
-
return this.client.startGroupTransfer(split.params, { aidStore: split.aidStore });
|
|
18777
|
-
}
|
|
18778
|
-
return this.call("group.transfer_owner", split.params);
|
|
18779
|
-
}
|
|
18780
|
-
completeTransfer(params2) {
|
|
18781
|
-
const split = splitAidStore(params2);
|
|
18782
|
-
if (split.aidStore && typeof this.client.completeGroupTransfer === "function") {
|
|
18783
|
-
return this.client.completeGroupTransfer(split.params, { aidStore: split.aidStore });
|
|
18784
|
-
}
|
|
18785
|
-
return this.call("group.complete_transfer", split.params);
|
|
19343
|
+
this.client = client;
|
|
18786
19344
|
}
|
|
18787
|
-
|
|
18788
|
-
return this.call(
|
|
19345
|
+
call(method, params2) {
|
|
19346
|
+
return this.client.call(method, stripNil2(params2));
|
|
18789
19347
|
}
|
|
18790
|
-
|
|
18791
|
-
|
|
19348
|
+
};
|
|
19349
|
+
var MessageThoughtFacade = class extends RpcFacade {
|
|
19350
|
+
put(params2) {
|
|
19351
|
+
return this.call("message.thought.put", params2);
|
|
18792
19352
|
}
|
|
18793
|
-
|
|
18794
|
-
return this.call("
|
|
19353
|
+
get(params2) {
|
|
19354
|
+
return this.call("message.thought.get", params2);
|
|
18795
19355
|
}
|
|
18796
|
-
|
|
18797
|
-
|
|
19356
|
+
};
|
|
19357
|
+
var MessageFacade = class extends RpcFacade {
|
|
19358
|
+
constructor() {
|
|
19359
|
+
super(...arguments);
|
|
19360
|
+
__publicField(this, "_thought");
|
|
18798
19361
|
}
|
|
18799
|
-
|
|
18800
|
-
|
|
19362
|
+
get thought() {
|
|
19363
|
+
if (!this._thought) this._thought = new MessageThoughtFacade(this.client);
|
|
19364
|
+
return this._thought;
|
|
18801
19365
|
}
|
|
18802
|
-
|
|
18803
|
-
return this.call("
|
|
19366
|
+
send(params2) {
|
|
19367
|
+
return this.call("message.send", params2);
|
|
18804
19368
|
}
|
|
18805
|
-
|
|
18806
|
-
return this.call("
|
|
19369
|
+
pull(params2) {
|
|
19370
|
+
return this.call("message.pull", params2);
|
|
18807
19371
|
}
|
|
18808
|
-
|
|
18809
|
-
return this.call("
|
|
19372
|
+
history(params2) {
|
|
19373
|
+
return this.call("message.history", params2);
|
|
18810
19374
|
}
|
|
18811
|
-
|
|
18812
|
-
return this.call("
|
|
19375
|
+
ack(params2) {
|
|
19376
|
+
return this.call("message.ack", params2);
|
|
18813
19377
|
}
|
|
18814
|
-
|
|
18815
|
-
return this.call("
|
|
19378
|
+
recall(params2) {
|
|
19379
|
+
return this.call("message.recall", params2);
|
|
18816
19380
|
}
|
|
18817
|
-
|
|
18818
|
-
return this.call("
|
|
19381
|
+
queryOnline(params2) {
|
|
19382
|
+
return this.call("message.query_online", params2);
|
|
18819
19383
|
}
|
|
18820
|
-
|
|
18821
|
-
|
|
19384
|
+
};
|
|
19385
|
+
function normalizeInviteParams(params2) {
|
|
19386
|
+
const out = stripNil2(params2);
|
|
19387
|
+
const rawGroupId = out.group_id ?? out.groupId ?? out.group_aid ?? out.groupAid;
|
|
19388
|
+
if (rawGroupId !== void 0) {
|
|
19389
|
+
out.group_id = normalizeGroupId(rawGroupId);
|
|
19390
|
+
delete out.groupId;
|
|
19391
|
+
delete out.group_aid;
|
|
19392
|
+
delete out.groupAid;
|
|
18822
19393
|
}
|
|
18823
|
-
|
|
18824
|
-
|
|
19394
|
+
for (const [alias, canonical] of [["inviteId", "invite_id"], ["inviteeAid", "invitee_aid"]]) {
|
|
19395
|
+
if (out[canonical] === void 0 && out[alias] !== void 0) out[canonical] = out[alias];
|
|
19396
|
+
delete out[alias];
|
|
18825
19397
|
}
|
|
18826
|
-
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
|
|
18831
|
-
const stale = typeof clientAny.isGroupIndexStale === "function" ? Boolean(clientAny.isGroupIndexStale(groupAid)) : false;
|
|
18832
|
-
const remoteMeta = typeof clientAny.getGroupIndexRemoteMeta === "function" ? clientAny.getGroupIndexRemoteMeta(groupAid) ?? {} : {};
|
|
18833
|
-
const localEtag = typeof clientAny.getGroupIndexLocalEtag === "function" ? String(clientAny.getGroupIndexLocalEtag(groupAid) ?? "") : "";
|
|
18834
|
-
const remoteEtag = String(remoteMeta?.etag ?? "");
|
|
18835
|
-
const localFound = Boolean(localEtag);
|
|
18836
|
-
const remoteFound = Boolean(remoteEtag);
|
|
18837
|
-
const inSync = localFound && remoteFound && localEtag === remoteEtag;
|
|
18838
|
-
return {
|
|
18839
|
-
group_aid: groupAid,
|
|
18840
|
-
local_found: localFound,
|
|
18841
|
-
remote_found: remoteFound,
|
|
18842
|
-
local_etag: localEtag,
|
|
18843
|
-
remote_etag: remoteEtag,
|
|
18844
|
-
in_sync: inSync,
|
|
18845
|
-
needs_update: Boolean(stale || remoteFound && !inSync),
|
|
18846
|
-
last_modified: remoteMeta?.last_modified,
|
|
18847
|
-
status: remoteFound ? 200 : 404,
|
|
18848
|
-
cached: true
|
|
18849
|
-
};
|
|
19398
|
+
return out;
|
|
19399
|
+
}
|
|
19400
|
+
var MailFacade = class extends RpcFacade {
|
|
19401
|
+
send(params2) {
|
|
19402
|
+
return this.call("mail.send", params2);
|
|
18850
19403
|
}
|
|
18851
|
-
|
|
18852
|
-
|
|
18853
|
-
const groupId = String(merged.group_id ?? "").trim();
|
|
18854
|
-
if (!groupId) throw new Error("group_id is required");
|
|
18855
|
-
const result = await this.getSettings({ group_id: groupId, keys: [GROUP_INDEX_KEY] });
|
|
18856
|
-
const resultMap = result;
|
|
18857
|
-
const groupAid = String(resultMap.group_aid ?? "").trim();
|
|
18858
|
-
if (!groupAid) throw new Error("group.get_settings response group_aid is required");
|
|
18859
|
-
let groupIndex = null;
|
|
18860
|
-
for (const item of result.settings ?? []) {
|
|
18861
|
-
if (item?.key !== GROUP_INDEX_KEY) continue;
|
|
18862
|
-
groupIndex = item.value;
|
|
18863
|
-
break;
|
|
18864
|
-
}
|
|
18865
|
-
if (!groupIndex) {
|
|
18866
|
-
return { group_id: resultMap.group_id, group_aid: groupAid, group_index: null, meta: {}, entries: [], settings: {} };
|
|
18867
|
-
}
|
|
18868
|
-
const parsed = parseGroupIndex(groupIndex);
|
|
18869
|
-
this.validateGroupIndexEntries(parsed.entries);
|
|
18870
|
-
await this.verifyPulledGroupIndex(groupId, groupAid, groupIndex, parsed);
|
|
18871
|
-
const etag = String(parsed.meta.etag ?? "");
|
|
18872
|
-
const settings = await this.hydrateGroupIndexSettings(groupId, groupAid, parsed.entries, etag, groupIndex);
|
|
18873
|
-
return {
|
|
18874
|
-
group_id: resultMap.group_id,
|
|
18875
|
-
group_aid: groupAid,
|
|
18876
|
-
group_index: groupIndex,
|
|
18877
|
-
meta: parsed.meta,
|
|
18878
|
-
entries: parsed.entries,
|
|
18879
|
-
settings
|
|
18880
|
-
};
|
|
19404
|
+
get(params2) {
|
|
19405
|
+
return this.call("mail.get", params2);
|
|
18881
19406
|
}
|
|
18882
|
-
|
|
18883
|
-
|
|
18884
|
-
const signedBy = String(parsed.meta?.signed_by ?? "").trim();
|
|
18885
|
-
if (!signedBy) throw new Error("group.index signed_by is required");
|
|
18886
|
-
const certFingerprint = String(parsed.meta?.cert_fingerprint ?? "").trim();
|
|
18887
|
-
const clientAny = this.client;
|
|
18888
|
-
const currentAid = clientAny.currentAid;
|
|
18889
|
-
let signer = currentAid?.aid === signedBy && !certFingerprint ? currentAid : null;
|
|
18890
|
-
if (currentAid?.aid === signedBy && certFingerprint && typeof currentAid.certPem === "string" && currentAid.certPem) {
|
|
18891
|
-
try {
|
|
18892
|
-
if (await certMatchesFingerprint(currentAid.certPem, certFingerprint)) signer = currentAid;
|
|
18893
|
-
} catch {
|
|
18894
|
-
signer = null;
|
|
18895
|
-
}
|
|
18896
|
-
}
|
|
18897
|
-
if (!signer && typeof clientAny.lookupPeer === "function") {
|
|
18898
|
-
signer = await clientAny.lookupPeer(signedBy, certFingerprint || void 0);
|
|
18899
|
-
}
|
|
18900
|
-
if (!signer) throw new Error(`group.index signer is unavailable: ${signedBy}`);
|
|
18901
|
-
const verified = await verifyGroupIndex(groupIndex, signer);
|
|
18902
|
-
if (!verified.ok) throw new Error(verified.error.message || "group.index verification failed");
|
|
18903
|
-
if (!verified.data.valid) throw new Error(`group.index verification failed: ${verified.data.reason || "invalid signature"}`);
|
|
18904
|
-
const adminsResult = await this.call("group.get_admins", { group_id: groupId });
|
|
18905
|
-
const adminsMap = adminsResult;
|
|
18906
|
-
if (String(adminsMap.group_aid ?? "").trim() !== groupAid) throw new Error("group.get_admins response group_aid does not match group.get_settings response");
|
|
18907
|
-
const authorized = Array.isArray(adminsMap.admins) && adminsMap.admins.some(
|
|
18908
|
-
(item) => String(item?.aid ?? "").trim() === signedBy && ["owner", "admin"].includes(String(item?.role ?? "").trim().toLowerCase())
|
|
18909
|
-
);
|
|
18910
|
-
if (!authorized) throw new Error(`group.index signer is not a current owner or admin: ${signedBy}`);
|
|
19407
|
+
list(params2) {
|
|
19408
|
+
return this.call("mail.list", params2);
|
|
18911
19409
|
}
|
|
18912
|
-
|
|
18913
|
-
|
|
18914
|
-
const groupId = String(merged.group_id ?? "").trim();
|
|
18915
|
-
const settings = merged.settings;
|
|
18916
|
-
if (!groupId) throw new Error("group_id is required");
|
|
18917
|
-
if (!settings || typeof settings !== "object" || Array.isArray(settings) || Object.keys(settings).length === 0) {
|
|
18918
|
-
throw new Error("settings must be a non-empty object");
|
|
18919
|
-
}
|
|
18920
|
-
const signer = merged.signer ?? this.client.currentAid;
|
|
18921
|
-
if (!signer) throw new Error("signer is required");
|
|
18922
|
-
const lastModified = Math.trunc(Number(merged.last_modified ?? Date.now()));
|
|
18923
|
-
const maxAttempts = Math.max(1, Math.trunc(Number(merged.max_attempts ?? 2)));
|
|
18924
|
-
let lastError = null;
|
|
18925
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
18926
|
-
const current = await this.getGroupIndex({ group_id: groupId });
|
|
18927
|
-
const groupAid = String(current.group_aid ?? "").trim();
|
|
18928
|
-
const currentIndex = current.group_index ?? null;
|
|
18929
|
-
const expectedEtag = currentIndex ? String(parseGroupIndex(currentIndex).meta.etag ?? "") : "";
|
|
18930
|
-
const baseSettings = current.settings && typeof current.settings === "object" && !Array.isArray(current.settings) ? current.settings : {};
|
|
18931
|
-
const signedSettings = await prepareGroupSettingsWithIndex({
|
|
18932
|
-
groupAid,
|
|
18933
|
-
settings,
|
|
18934
|
-
signer,
|
|
18935
|
-
lastModified,
|
|
18936
|
-
baseIndex: currentIndex
|
|
18937
|
-
});
|
|
18938
|
-
try {
|
|
18939
|
-
const result = await this.setSettings({
|
|
18940
|
-
group_id: groupId,
|
|
18941
|
-
settings: signedSettings,
|
|
18942
|
-
expected_index_etag: expectedEtag
|
|
18943
|
-
});
|
|
18944
|
-
const pushedEtag = String(parseGroupIndex(signedSettings[GROUP_INDEX_KEY]).meta.etag ?? "");
|
|
18945
|
-
const clientAny = this.client;
|
|
18946
|
-
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
18947
|
-
const parsed = parseGroupIndex(signedSettings[GROUP_INDEX_KEY]);
|
|
18948
|
-
await clientAny.cacheGroupIndexSettings(groupAid, { ...baseSettings, ...settings }, {
|
|
18949
|
-
entries: parsed.entries,
|
|
18950
|
-
etag: pushedEtag,
|
|
18951
|
-
groupIndex: signedSettings[GROUP_INDEX_KEY]
|
|
18952
|
-
});
|
|
18953
|
-
}
|
|
18954
|
-
return result;
|
|
18955
|
-
} catch (exc) {
|
|
18956
|
-
if (!String(exc?.message ?? exc).includes("etag conflict")) throw exc;
|
|
18957
|
-
lastError = exc;
|
|
18958
|
-
}
|
|
18959
|
-
}
|
|
18960
|
-
if (lastError) throw lastError;
|
|
18961
|
-
throw new Error("updateGroupIndex failed");
|
|
19410
|
+
delete(params2) {
|
|
19411
|
+
return this.call("mail.delete", params2);
|
|
18962
19412
|
}
|
|
18963
|
-
|
|
18964
|
-
|
|
18965
|
-
if (typeof clientAny.getGroupIndexCachedSettings === "function") {
|
|
18966
|
-
const cached = await clientAny.getGroupIndexCachedSettings(groupId, keys);
|
|
18967
|
-
if (cached && typeof cached === "object") return { groupId, settings: cached };
|
|
18968
|
-
}
|
|
18969
|
-
const result = await this.getGroupIndex({ group_id: groupId });
|
|
18970
|
-
const resultMap = result;
|
|
18971
|
-
const allSettings = resultMap.settings && typeof resultMap.settings === "object" && !Array.isArray(resultMap.settings) ? resultMap.settings : {};
|
|
18972
|
-
const settings = Object.fromEntries(keys.filter((key) => key in allSettings).map((key) => [key, allSettings[key]]));
|
|
18973
|
-
const groupAid = String(resultMap.group_aid ?? "").trim();
|
|
18974
|
-
if (groupAid && groupAid !== groupId && typeof clientAny.cacheGroupIndexSettings === "function" && resultMap.group_index) {
|
|
18975
|
-
await clientAny.cacheGroupIndexSettings(groupId, allSettings, { entries: resultMap.entries, etag: String(resultMap.meta?.etag ?? ""), groupIndex: resultMap.group_index });
|
|
18976
|
-
}
|
|
18977
|
-
return { groupId: String(resultMap.group_id ?? groupId), settings };
|
|
19413
|
+
move(params2) {
|
|
19414
|
+
return this.call("mail.move", params2);
|
|
18978
19415
|
}
|
|
18979
|
-
|
|
18980
|
-
|
|
18981
|
-
|
|
18982
|
-
|
|
18983
|
-
|
|
18984
|
-
const fetchedResult = await this.getSettings({ group_id: groupId, keys });
|
|
18985
|
-
const fetchedMap = fetchedResult;
|
|
18986
|
-
if (String(fetchedMap.group_aid ?? "").trim() !== groupAid) throw new Error("group settings response group_aid does not match verified group.index");
|
|
18987
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18988
|
-
for (const item of fetchedMap.settings ?? []) {
|
|
18989
|
-
const key = String(item?.key ?? "").trim();
|
|
18990
|
-
if (!key || seen.has(key)) throw new Error(`group settings response contains duplicate or empty key: ${key}`);
|
|
18991
|
-
seen.add(key);
|
|
18992
|
-
settings[key] = item?.value;
|
|
18993
|
-
}
|
|
18994
|
-
for (const entry of entries) {
|
|
18995
|
-
const key = String(entry.key);
|
|
18996
|
-
if (!seen.has(key)) throw new Error(`group.index setting is missing: ${key}`);
|
|
18997
|
-
if (await groupIndexSettingEtag(settings[key]) !== String(entry.etag)) throw new Error(`group.index setting etag mismatch: ${key}`);
|
|
18998
|
-
}
|
|
18999
|
-
}
|
|
19000
|
-
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
19001
|
-
await clientAny.cacheGroupIndexSettings(groupAid, settings, { entries, etag, groupIndex });
|
|
19002
|
-
}
|
|
19003
|
-
return settings;
|
|
19416
|
+
mark(params2) {
|
|
19417
|
+
return this.call("mail.mark", params2);
|
|
19418
|
+
}
|
|
19419
|
+
search(params2) {
|
|
19420
|
+
return this.call("mail.search", params2);
|
|
19004
19421
|
}
|
|
19005
|
-
|
|
19006
|
-
|
|
19007
|
-
for (const entry of entries) {
|
|
19008
|
-
const key = String(entry?.key ?? "").trim();
|
|
19009
|
-
const source = String(entry?.source ?? "").trim();
|
|
19010
|
-
const etag = String(entry?.etag ?? "").trim();
|
|
19011
|
-
if (!key) throw new Error("group.index entry key is required");
|
|
19012
|
-
if (seen.has(key)) throw new Error(`group.index contains duplicate key: ${key}`);
|
|
19013
|
-
if (source !== "db") throw new Error(`group.index entry source is unsupported: ${source || "<empty>"}`);
|
|
19014
|
-
if (!etag) throw new Error(`group.index entry etag is required: ${key}`);
|
|
19015
|
-
seen.add(key);
|
|
19016
|
-
}
|
|
19422
|
+
getAttachment(params2) {
|
|
19423
|
+
return this.call("mail.get_attachment", params2);
|
|
19017
19424
|
}
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
return this.call("group.send", params2);
|
|
19425
|
+
getQuota(params2) {
|
|
19426
|
+
return this.call("mail.get_quota", params2);
|
|
19021
19427
|
}
|
|
19022
|
-
|
|
19023
|
-
return this.call("
|
|
19428
|
+
health(params2) {
|
|
19429
|
+
return this.call("mail.health", params2);
|
|
19024
19430
|
}
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
return this.call("group.pull", params2);
|
|
19431
|
+
status(params2) {
|
|
19432
|
+
return this.call("mail.status", params2);
|
|
19028
19433
|
}
|
|
19029
|
-
|
|
19030
|
-
|
|
19434
|
+
};
|
|
19435
|
+
var GroupThoughtFacade = class extends RpcFacade {
|
|
19436
|
+
put(params2) {
|
|
19437
|
+
return this.call("group.thought.put", params2);
|
|
19031
19438
|
}
|
|
19032
|
-
|
|
19033
|
-
return this.call("group.
|
|
19439
|
+
get(params2) {
|
|
19440
|
+
return this.call("group.thought.get", params2);
|
|
19034
19441
|
}
|
|
19035
|
-
|
|
19036
|
-
|
|
19442
|
+
};
|
|
19443
|
+
var GroupFacade = class extends RpcFacade {
|
|
19444
|
+
constructor() {
|
|
19445
|
+
super(...arguments);
|
|
19446
|
+
__publicField(this, "_fs");
|
|
19447
|
+
__publicField(this, "_thought");
|
|
19037
19448
|
}
|
|
19038
|
-
|
|
19039
|
-
|
|
19449
|
+
get fs() {
|
|
19450
|
+
if (!this._fs) this._fs = new GroupFSVFS(this.client);
|
|
19451
|
+
return this._fs;
|
|
19040
19452
|
}
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
return {
|
|
19045
|
-
group_id: groupId,
|
|
19046
|
-
setting: {
|
|
19047
|
-
group_id: groupId,
|
|
19048
|
-
key_name: keyName,
|
|
19049
|
-
content: settings[contentKey] ?? "",
|
|
19050
|
-
attachments: settings[attachmentsKey] ?? [],
|
|
19051
|
-
updated_by: settings[`${contentKey}.updated_by`] ?? "",
|
|
19052
|
-
updated_at: settings[`${contentKey}.updated_at`] ?? 0
|
|
19053
|
-
}
|
|
19054
|
-
};
|
|
19453
|
+
get thought() {
|
|
19454
|
+
if (!this._thought) this._thought = new GroupThoughtFacade(this.client);
|
|
19455
|
+
return this._thought;
|
|
19055
19456
|
}
|
|
19056
|
-
|
|
19057
|
-
const
|
|
19058
|
-
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19062
|
-
|
|
19063
|
-
|
|
19064
|
-
|
|
19065
|
-
return this.
|
|
19457
|
+
create(params2) {
|
|
19458
|
+
const split = splitAidStore(params2);
|
|
19459
|
+
if (split.params.group_name === void 0 && split.params.groupName !== void 0) {
|
|
19460
|
+
split.params.group_name = split.params.groupName;
|
|
19461
|
+
delete split.params.groupName;
|
|
19462
|
+
}
|
|
19463
|
+
if (split.aidStore && typeof this.client.createGroup === "function") {
|
|
19464
|
+
return this.client.createGroup(split.params, { aidStore: split.aidStore });
|
|
19465
|
+
}
|
|
19466
|
+
return this.call("group.create", split.params);
|
|
19066
19467
|
}
|
|
19067
|
-
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
19071
|
-
const
|
|
19072
|
-
if (
|
|
19073
|
-
|
|
19074
|
-
const attachments = merged.attachments ?? [];
|
|
19075
|
-
const settingsUpdate = { [`${keyName}.content`]: content };
|
|
19076
|
-
if ("attachments" in merged) {
|
|
19077
|
-
settingsUpdate[`${keyName}.attachments`] = attachments;
|
|
19468
|
+
bindAid(params2) {
|
|
19469
|
+
return this.call("group.bind_aid", splitAidStore(params2).params);
|
|
19470
|
+
}
|
|
19471
|
+
bindGroupAid(params2) {
|
|
19472
|
+
const split = splitAidStore(params2);
|
|
19473
|
+
if (split.aidStore && typeof this.client.bindGroupAid === "function") {
|
|
19474
|
+
return this.client.bindGroupAid(split.params, { aidStore: split.aidStore });
|
|
19078
19475
|
}
|
|
19079
|
-
|
|
19080
|
-
const resultGroupId = String(result.group_id ?? groupId);
|
|
19081
|
-
return {
|
|
19082
|
-
group_id: resultGroupId,
|
|
19083
|
-
setting: {
|
|
19084
|
-
group_id: resultGroupId,
|
|
19085
|
-
key_name: keyName,
|
|
19086
|
-
content,
|
|
19087
|
-
attachments,
|
|
19088
|
-
updated_by: "",
|
|
19089
|
-
updated_at: 0
|
|
19090
|
-
}
|
|
19091
|
-
};
|
|
19476
|
+
return this.call("group.bind_group_aid", split.params);
|
|
19092
19477
|
}
|
|
19093
|
-
|
|
19094
|
-
const
|
|
19095
|
-
|
|
19096
|
-
|
|
19097
|
-
|
|
19098
|
-
return
|
|
19099
|
-
group_id: resultMap.group_id,
|
|
19100
|
-
announcement: {
|
|
19101
|
-
group_id: setting.group_id,
|
|
19102
|
-
content: setting.content,
|
|
19103
|
-
attachments: setting.attachments,
|
|
19104
|
-
updated_by: setting.updated_by,
|
|
19105
|
-
updated_at: setting.updated_at
|
|
19106
|
-
}
|
|
19107
|
-
};
|
|
19478
|
+
renewGroupAid(params2) {
|
|
19479
|
+
const split = splitAidStore(params2);
|
|
19480
|
+
if (typeof this.client.renewGroupAid === "function") {
|
|
19481
|
+
return this.client.renewGroupAid(split.params, { aidStore: split.aidStore });
|
|
19482
|
+
}
|
|
19483
|
+
return this.call("group.renew_group_aid", split.params);
|
|
19108
19484
|
}
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
const result = await this.updateSettingWithIndex({ ...merged, keyName: "announcement" });
|
|
19112
|
-
const resultMap = result;
|
|
19113
|
-
const setting = resultMap.setting;
|
|
19114
|
-
return {
|
|
19115
|
-
group_id: resultMap.group_id,
|
|
19116
|
-
announcement: {
|
|
19117
|
-
group_id: resultMap.group_id,
|
|
19118
|
-
content: setting.content,
|
|
19119
|
-
attachments: setting.attachments
|
|
19120
|
-
}
|
|
19121
|
-
};
|
|
19485
|
+
getInfo(params2) {
|
|
19486
|
+
return this.call("group.get_info", params2);
|
|
19122
19487
|
}
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
const result = await this.getSettingWithIndex({ ...merged, keyName: "rules" });
|
|
19126
|
-
const resultMap = result;
|
|
19127
|
-
const setting = resultMap.setting;
|
|
19128
|
-
return {
|
|
19129
|
-
group_id: resultMap.group_id,
|
|
19130
|
-
rules: {
|
|
19131
|
-
group_id: setting.group_id,
|
|
19132
|
-
content: setting.content,
|
|
19133
|
-
attachments: setting.attachments,
|
|
19134
|
-
updated_by: setting.updated_by,
|
|
19135
|
-
updated_at: setting.updated_at
|
|
19136
|
-
}
|
|
19137
|
-
};
|
|
19488
|
+
update(params2) {
|
|
19489
|
+
return this.call("group.update", params2);
|
|
19138
19490
|
}
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
const result = await this.updateSettingWithIndex({ ...merged, keyName: "rules" });
|
|
19142
|
-
const resultMap = result;
|
|
19143
|
-
const setting = resultMap.setting;
|
|
19144
|
-
return {
|
|
19145
|
-
group_id: resultMap.group_id,
|
|
19146
|
-
rules: {
|
|
19147
|
-
group_id: resultMap.group_id,
|
|
19148
|
-
content: setting.content,
|
|
19149
|
-
attachments: setting.attachments
|
|
19150
|
-
}
|
|
19151
|
-
};
|
|
19491
|
+
list(params2) {
|
|
19492
|
+
return this.call("group.list", params2);
|
|
19152
19493
|
}
|
|
19153
|
-
|
|
19154
|
-
|
|
19155
|
-
const groupId = merged.group_id;
|
|
19156
|
-
if (!groupId) throw new Error("group_id is required");
|
|
19157
|
-
const { groupId: resultGroupId, settings } = await this.getIndexedSettings(
|
|
19158
|
-
String(groupId),
|
|
19159
|
-
["join.mode", "join.question", "join.auto_approve_patterns", "join.max_pending", "join.attachments"]
|
|
19160
|
-
);
|
|
19161
|
-
return {
|
|
19162
|
-
group_id: resultGroupId,
|
|
19163
|
-
join_requirements: {
|
|
19164
|
-
group_id: resultGroupId,
|
|
19165
|
-
mode: settings["join.mode"] || "open",
|
|
19166
|
-
question: settings["join.question"] || "",
|
|
19167
|
-
auto_approve_patterns: settings["join.auto_approve_patterns"] || [],
|
|
19168
|
-
max_pending: settings["join.max_pending"] || 100,
|
|
19169
|
-
attachments: settings["join.attachments"] || [],
|
|
19170
|
-
updated_by: settings["join.mode.updated_by"] || "",
|
|
19171
|
-
updated_at: settings["join.mode.updated_at"] || 0
|
|
19172
|
-
}
|
|
19173
|
-
};
|
|
19494
|
+
listMy(params2) {
|
|
19495
|
+
return this.call("group.list_my", params2);
|
|
19174
19496
|
}
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
const groupId = merged.group_id;
|
|
19178
|
-
if (!groupId) throw new Error("group_id is required");
|
|
19179
|
-
const settingsUpdate = {};
|
|
19180
|
-
if ("mode" in merged) settingsUpdate["join.mode"] = merged.mode;
|
|
19181
|
-
if ("question" in merged) settingsUpdate["join.question"] = merged.question;
|
|
19182
|
-
if ("auto_approve_patterns" in merged) settingsUpdate["join.auto_approve_patterns"] = merged.auto_approve_patterns;
|
|
19183
|
-
if ("max_pending" in merged) settingsUpdate["join.max_pending"] = merged.max_pending;
|
|
19184
|
-
if ("attachments" in merged) settingsUpdate["join.attachments"] = merged.attachments;
|
|
19185
|
-
if (Object.keys(settingsUpdate).length === 0) {
|
|
19186
|
-
throw new Error("at least one field to update is required");
|
|
19187
|
-
}
|
|
19188
|
-
const result = await this.updateGroupIndex(indexUpdateParams(groupId, settingsUpdate, merged));
|
|
19189
|
-
return {
|
|
19190
|
-
group_id: result.group_id,
|
|
19191
|
-
join_requirements: {
|
|
19192
|
-
group_id: result.group_id,
|
|
19193
|
-
mode: merged.mode,
|
|
19194
|
-
question: merged.question,
|
|
19195
|
-
auto_approve_patterns: merged.auto_approve_patterns,
|
|
19196
|
-
max_pending: merged.max_pending,
|
|
19197
|
-
attachments: merged.attachments || []
|
|
19198
|
-
}
|
|
19199
|
-
};
|
|
19497
|
+
search(params2) {
|
|
19498
|
+
return this.call("group.search", params2);
|
|
19200
19499
|
}
|
|
19201
|
-
|
|
19202
|
-
return this.call("group.
|
|
19500
|
+
suspend(params2) {
|
|
19501
|
+
return this.call("group.suspend", params2);
|
|
19203
19502
|
}
|
|
19204
|
-
|
|
19205
|
-
|
|
19206
|
-
|
|
19207
|
-
|
|
19503
|
+
resume(params2) {
|
|
19504
|
+
return this.call("group.resume", params2);
|
|
19505
|
+
}
|
|
19506
|
+
dissolve(params2) {
|
|
19507
|
+
return this.call("group.dissolve", params2);
|
|
19508
|
+
}
|
|
19509
|
+
addMember(params2) {
|
|
19510
|
+
return this.call("group.add_member", params2);
|
|
19208
19511
|
}
|
|
19209
|
-
|
|
19210
|
-
|
|
19512
|
+
async inviteCall(method, params2) {
|
|
19513
|
+
const normalized = normalizeInviteParams(params2);
|
|
19514
|
+
try {
|
|
19515
|
+
const result = await this.call(method, normalized);
|
|
19516
|
+
await this.client._onGroupInviteActionResult?.(method, normalized, result);
|
|
19517
|
+
return result;
|
|
19518
|
+
} catch (error) {
|
|
19519
|
+
await this.client._onGroupInviteActionResult?.(method, normalized, void 0, error);
|
|
19520
|
+
throw error;
|
|
19521
|
+
}
|
|
19211
19522
|
}
|
|
19212
|
-
|
|
19213
|
-
return this.
|
|
19523
|
+
inviteMember(params2) {
|
|
19524
|
+
return this.inviteCall("group.invite_member", params2);
|
|
19214
19525
|
}
|
|
19215
|
-
|
|
19216
|
-
return this.
|
|
19526
|
+
listMyInvites(params2) {
|
|
19527
|
+
return this.inviteCall("group.list_my_invites", params2);
|
|
19217
19528
|
}
|
|
19218
|
-
|
|
19219
|
-
|
|
19220
|
-
// src/storage/types.ts
|
|
19221
|
-
function text2(value, fallback = "") {
|
|
19222
|
-
if (value === null || value === void 0) return fallback;
|
|
19223
|
-
return String(value);
|
|
19224
|
-
}
|
|
19225
|
-
function intValue(value, fallback = 0) {
|
|
19226
|
-
const parsed = Number(value);
|
|
19227
|
-
return Number.isFinite(parsed) ? Math.trunc(parsed) : fallback;
|
|
19228
|
-
}
|
|
19229
|
-
function boolValue(value, fallback = false) {
|
|
19230
|
-
if (value === null || value === void 0) return fallback;
|
|
19231
|
-
return Boolean(value);
|
|
19232
|
-
}
|
|
19233
|
-
function mountSourceFromRaw(raw) {
|
|
19234
|
-
const explicit = text2(raw.mount_source);
|
|
19235
|
-
if (explicit) return explicit;
|
|
19236
|
-
const sourceAid = text2(raw.source_aid);
|
|
19237
|
-
if (!sourceAid) return "";
|
|
19238
|
-
return `${sourceAid}:${keyToPath(raw.source_path)}`;
|
|
19239
|
-
}
|
|
19240
|
-
function normalizePath(path) {
|
|
19241
|
-
let raw = String(path || "/").replace(/\\/g, "/").trim();
|
|
19242
|
-
if (!raw.startsWith("/")) raw = `/${raw}`;
|
|
19243
|
-
raw = raw.replace(/\/+/g, "/");
|
|
19244
|
-
const parts = [];
|
|
19245
|
-
for (const part of raw.split("/")) {
|
|
19246
|
-
if (!part || part === ".") continue;
|
|
19247
|
-
if (part === "..") {
|
|
19248
|
-
parts.pop();
|
|
19249
|
-
continue;
|
|
19250
|
-
}
|
|
19251
|
-
parts.push(part);
|
|
19529
|
+
acceptInvite(params2) {
|
|
19530
|
+
return this.inviteCall("group.accept_invite", params2);
|
|
19252
19531
|
}
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
function pathToKey(path) {
|
|
19256
|
-
const normalized = normalizePath(path);
|
|
19257
|
-
return normalized === "/" ? "" : normalized.slice(1);
|
|
19258
|
-
}
|
|
19259
|
-
function keyToPath(key) {
|
|
19260
|
-
return normalizePath(text2(key));
|
|
19261
|
-
}
|
|
19262
|
-
function nameFromPath(path) {
|
|
19263
|
-
const cleaned = normalizePath(path).replace(/\/+$/g, "");
|
|
19264
|
-
if (!cleaned || cleaned === "/") return "/";
|
|
19265
|
-
return cleaned.split("/").pop() || "/";
|
|
19266
|
-
}
|
|
19267
|
-
function baseNode(raw, type, path) {
|
|
19268
|
-
return {
|
|
19269
|
-
type,
|
|
19270
|
-
path,
|
|
19271
|
-
name: text2(raw.name) || nameFromPath(path),
|
|
19272
|
-
owner: text2(raw.owner ?? raw.owner_aid),
|
|
19273
|
-
bucket: text2(raw.bucket, "default") || "default",
|
|
19274
|
-
size: intValue(raw.size ?? raw.size_bytes),
|
|
19275
|
-
mtime: intValue(raw.mtime ?? raw.updated_at),
|
|
19276
|
-
contentType: text2(raw.content_type),
|
|
19277
|
-
version: intValue(raw.version),
|
|
19278
|
-
mode: text2(raw.mode),
|
|
19279
|
-
isPublic: !boolValue(raw.is_private, true),
|
|
19280
|
-
objectId: text2(raw.object_id),
|
|
19281
|
-
folderId: text2(raw.folder_id),
|
|
19282
|
-
target: text2(raw.target),
|
|
19283
|
-
mountSource: mountSourceFromRaw(raw),
|
|
19284
|
-
metadata: raw.metadata && typeof raw.metadata === "object" && !Array.isArray(raw.metadata) ? raw.metadata : {}
|
|
19285
|
-
};
|
|
19286
|
-
}
|
|
19287
|
-
function nodeFromAny(input) {
|
|
19288
|
-
const raw = input && typeof input === "object" ? input : {};
|
|
19289
|
-
const nodeType = text2(raw.type ?? raw.node_type).toLowerCase();
|
|
19290
|
-
if (["folder", "dir", "directory"].includes(nodeType)) return baseNode(raw, "dir", keyToPath(raw.path));
|
|
19291
|
-
if (["symlink", "link"].includes(nodeType)) return baseNode(raw, "symlink", keyToPath(raw.path));
|
|
19292
|
-
if (nodeType === "mount") return baseNode(raw, "mount", keyToPath(raw.path));
|
|
19293
|
-
const path = keyToPath(raw.path ?? raw.object_key);
|
|
19294
|
-
return {
|
|
19295
|
-
...baseNode(raw, "file", path),
|
|
19296
|
-
sha256: text2(raw.sha256),
|
|
19297
|
-
etag: text2(raw.etag)
|
|
19298
|
-
};
|
|
19299
|
-
}
|
|
19300
|
-
function usageFromAny(input, ownerFallback = "") {
|
|
19301
|
-
const raw = input && typeof input === "object" ? input : {};
|
|
19302
|
-
const quota = intValue(raw.quota_bytes ?? raw.quota_total_bytes);
|
|
19303
|
-
const used = intValue(raw.used_bytes ?? raw.quota_used_bytes);
|
|
19304
|
-
return {
|
|
19305
|
-
owner: text2(raw.owner ?? raw.owner_aid, ownerFallback),
|
|
19306
|
-
quotaBytes: quota,
|
|
19307
|
-
usedBytes: used,
|
|
19308
|
-
availBytes: quota ? Math.max(0, quota - used) : 0,
|
|
19309
|
-
objectCount: intValue(raw.object_count)
|
|
19310
|
-
};
|
|
19311
|
-
}
|
|
19312
|
-
function unmountFromAny(input, fallback) {
|
|
19313
|
-
const raw = input && typeof input === "object" ? input : {};
|
|
19314
|
-
const path = keyToPath(raw.mount_path ?? raw.path ?? fallback.mountPath);
|
|
19315
|
-
return {
|
|
19316
|
-
unmounted: boolValue(raw.unmounted),
|
|
19317
|
-
owner: text2(raw.owner ?? raw.owner_aid, fallback.owner ?? ""),
|
|
19318
|
-
bucket: text2(raw.bucket, fallback.bucket ?? "default") || (fallback.bucket ?? "default"),
|
|
19319
|
-
path,
|
|
19320
|
-
mountPath: path
|
|
19321
|
-
};
|
|
19322
|
-
}
|
|
19323
|
-
|
|
19324
|
-
// src/storage/vfs.ts
|
|
19325
|
-
async function sha256Hex3(data) {
|
|
19326
|
-
const copy = new Uint8Array(data.byteLength);
|
|
19327
|
-
copy.set(data);
|
|
19328
|
-
const subtle = globalThis.crypto?.subtle;
|
|
19329
|
-
if (!subtle?.digest) throw new StorageError("SHA-256 unavailable", "EUNSUPPORTED");
|
|
19330
|
-
const digest = await subtle.digest("SHA-256", copy.buffer);
|
|
19331
|
-
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
19332
|
-
}
|
|
19333
|
-
function toBytes2(data) {
|
|
19334
|
-
if (typeof data === "string") return new TextEncoder().encode(data);
|
|
19335
|
-
if (data instanceof Uint8Array) return data;
|
|
19336
|
-
return new Uint8Array(data);
|
|
19337
|
-
}
|
|
19338
|
-
function textValue(value) {
|
|
19339
|
-
if (value === void 0 || value === null) return "";
|
|
19340
|
-
return String(value).trim();
|
|
19341
|
-
}
|
|
19342
|
-
function aidIssuer(aid) {
|
|
19343
|
-
const value = textValue(aid).replace(/^\.+|\.+$/g, "").toLowerCase();
|
|
19344
|
-
const separator = value.indexOf(".");
|
|
19345
|
-
return separator >= 0 ? value.slice(separator + 1).replace(/^\.+|\.+$/g, "") : "";
|
|
19346
|
-
}
|
|
19347
|
-
function accessTokenFromClient2(client) {
|
|
19348
|
-
const auth = client;
|
|
19349
|
-
return textValue(
|
|
19350
|
-
auth.accessToken ?? auth.access_token ?? auth._identity?.access_token ?? auth._sessionParams?.access_token ?? auth._session_params?.access_token
|
|
19351
|
-
);
|
|
19352
|
-
}
|
|
19353
|
-
function isInlineFallbackError(error) {
|
|
19354
|
-
const code = String(error.code).toUpperCase();
|
|
19355
|
-
if (["EINLINE", "EINLINE_LIMIT", "ERR_INLINE_LIMIT", "INLINE_LIMIT", "-32033"].includes(code)) return true;
|
|
19356
|
-
return error.code === -32602 && error.message.toLowerCase().includes("inline");
|
|
19357
|
-
}
|
|
19358
|
-
function parsePathLike(input, fallbackOwner) {
|
|
19359
|
-
if (typeof input !== "string") {
|
|
19360
|
-
return { owner: input.owner ?? fallbackOwner ?? null, path: input.path };
|
|
19532
|
+
rejectInvite(params2) {
|
|
19533
|
+
return this.inviteCall("group.reject_invite", params2);
|
|
19361
19534
|
}
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
return { owner: match[1], path: match[2] };
|
|
19535
|
+
revokeInvite(params2) {
|
|
19536
|
+
return this.inviteCall("group.revoke_invite", params2);
|
|
19365
19537
|
}
|
|
19366
|
-
|
|
19367
|
-
|
|
19368
|
-
var StorageVFS = class {
|
|
19369
|
-
constructor(client, options = {}) {
|
|
19370
|
-
__publicField(this, "lowlevel");
|
|
19371
|
-
__publicField(this, "useFsRpc");
|
|
19372
|
-
__publicField(this, "client");
|
|
19373
|
-
this.client = client;
|
|
19374
|
-
this.lowlevel = options.lowlevel ?? new StorageLowLevel(client);
|
|
19375
|
-
this.useFsRpc = options.useFsRpc ?? true;
|
|
19538
|
+
getMembers(params2) {
|
|
19539
|
+
return this.call("group.get_members", params2);
|
|
19376
19540
|
}
|
|
19377
|
-
|
|
19378
|
-
return this.
|
|
19541
|
+
kick(params2) {
|
|
19542
|
+
return this.call("group.kick", params2);
|
|
19379
19543
|
}
|
|
19380
|
-
|
|
19381
|
-
return
|
|
19544
|
+
leave(params2) {
|
|
19545
|
+
return this.call("group.leave", params2);
|
|
19382
19546
|
}
|
|
19383
|
-
|
|
19384
|
-
|
|
19385
|
-
const currentAid = textValue(this.defaultOwner);
|
|
19386
|
-
const targetAid = textValue(ticket.owner_aid ?? owner);
|
|
19387
|
-
const currentIssuer = aidIssuer(currentAid);
|
|
19388
|
-
const targetIssuer = aidIssuer(targetAid);
|
|
19389
|
-
const sameIssuer = Boolean(currentAid && targetAid && (currentAid.toLowerCase() === targetAid.toLowerCase() || currentIssuer && targetIssuer && currentIssuer === targetIssuer));
|
|
19390
|
-
const token = sameIssuer ? accessTokenFromClient2(this.client) : "";
|
|
19391
|
-
return { url, headers: token ? { Authorization: `Bearer ${token}` } : void 0 };
|
|
19547
|
+
setRole(params2) {
|
|
19548
|
+
return this.call("group.set_role", params2);
|
|
19392
19549
|
}
|
|
19393
|
-
|
|
19394
|
-
const
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
}
|
|
19405
|
-
if (check.target_exists && !overwrite && options.expectedVersion === void 0) {
|
|
19406
|
-
throw new StorageExistsError(`remote path already exists: ${path}`, "EEXIST", path, check.target);
|
|
19407
|
-
}
|
|
19408
|
-
if (check.dedup_hit || check.skip_upload) {
|
|
19409
|
-
const completed = await this.lowlevel.completeUpload({
|
|
19410
|
-
owner,
|
|
19411
|
-
bucket,
|
|
19412
|
-
objectKey,
|
|
19413
|
-
size: data.length,
|
|
19414
|
-
sha256: sha2566,
|
|
19415
|
-
contentType: options.contentType ?? "application/octet-stream",
|
|
19416
|
-
metadata: options.metadata,
|
|
19417
|
-
isPublic: options.public ?? false,
|
|
19418
|
-
expectedVersion: options.expectedVersion,
|
|
19419
|
-
skipBlob: true,
|
|
19420
|
-
overwrite
|
|
19421
|
-
});
|
|
19422
|
-
return nodeFromAny(completed);
|
|
19423
|
-
}
|
|
19424
|
-
if (check.inline === true) {
|
|
19425
|
-
return nodeFromAny(await this.lowlevel.putObject({
|
|
19426
|
-
owner,
|
|
19427
|
-
bucket,
|
|
19428
|
-
objectKey,
|
|
19429
|
-
content: data,
|
|
19430
|
-
contentType: options.contentType ?? "application/octet-stream",
|
|
19431
|
-
metadata: options.metadata,
|
|
19432
|
-
isPublic: options.public ?? false,
|
|
19433
|
-
expectedVersion: options.expectedVersion,
|
|
19434
|
-
overwrite
|
|
19435
|
-
}));
|
|
19436
|
-
}
|
|
19437
|
-
const session = await this.lowlevel.createUploadSession({
|
|
19438
|
-
owner,
|
|
19439
|
-
bucket,
|
|
19440
|
-
objectKey,
|
|
19441
|
-
size: data.length,
|
|
19442
|
-
contentType: options.contentType,
|
|
19443
|
-
expectedVersion: options.expectedVersion,
|
|
19444
|
-
overwrite
|
|
19445
|
-
});
|
|
19446
|
-
const uploadUrl = String(session.upload_url ?? "");
|
|
19447
|
-
if (!uploadUrl) throw new StorageError(`create_upload_session did not return upload_url`, "ESTORAGE", path);
|
|
19448
|
-
await this.lowlevel.httpPut(uploadUrl, data, session.headers);
|
|
19449
|
-
return nodeFromAny(await this.lowlevel.completeUpload({
|
|
19450
|
-
owner,
|
|
19451
|
-
bucket,
|
|
19452
|
-
objectKey,
|
|
19453
|
-
sessionId: String(session.session_id ?? ""),
|
|
19454
|
-
size: data.length,
|
|
19455
|
-
sha256: sha2566,
|
|
19456
|
-
contentType: options.contentType,
|
|
19457
|
-
metadata: options.metadata,
|
|
19458
|
-
isPublic: options.public ?? false,
|
|
19459
|
-
expectedVersion: options.expectedVersion,
|
|
19460
|
-
overwrite
|
|
19461
|
-
}));
|
|
19462
|
-
} catch (exc) {
|
|
19463
|
-
throw mapStorageError(exc, path);
|
|
19550
|
+
transferOwner(params2) {
|
|
19551
|
+
const split = splitAidStore(params2);
|
|
19552
|
+
if (split.aidStore && typeof this.client.startGroupTransfer === "function") {
|
|
19553
|
+
return this.client.startGroupTransfer(split.params, { aidStore: split.aidStore });
|
|
19554
|
+
}
|
|
19555
|
+
return this.call("group.transfer_owner", split.params);
|
|
19556
|
+
}
|
|
19557
|
+
completeTransfer(params2) {
|
|
19558
|
+
const split = splitAidStore(params2);
|
|
19559
|
+
if (split.aidStore && typeof this.client.completeGroupTransfer === "function") {
|
|
19560
|
+
return this.client.completeGroupTransfer(split.params, { aidStore: split.aidStore });
|
|
19464
19561
|
}
|
|
19562
|
+
return this.call("group.complete_transfer", split.params);
|
|
19465
19563
|
}
|
|
19466
|
-
|
|
19467
|
-
return
|
|
19564
|
+
ban(params2) {
|
|
19565
|
+
return this.call("group.ban", params2);
|
|
19468
19566
|
}
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19567
|
+
unban(params2) {
|
|
19568
|
+
return this.call("group.unban", params2);
|
|
19569
|
+
}
|
|
19570
|
+
getBanlist(params2) {
|
|
19571
|
+
return this.call("group.get_banlist", params2);
|
|
19572
|
+
}
|
|
19573
|
+
block(params2) {
|
|
19574
|
+
return this.call("group.block", params2);
|
|
19575
|
+
}
|
|
19576
|
+
unblock(params2) {
|
|
19577
|
+
return this.call("group.unblock", params2);
|
|
19578
|
+
}
|
|
19579
|
+
getBlocklist(params2) {
|
|
19580
|
+
return this.call("group.get_blocklist", params2);
|
|
19581
|
+
}
|
|
19582
|
+
reportStatus(params2) {
|
|
19583
|
+
return this.call("group.report_status", params2);
|
|
19584
|
+
}
|
|
19585
|
+
getMemberStatuses(params2) {
|
|
19586
|
+
return this.call("group.get_member_statuses", params2);
|
|
19587
|
+
}
|
|
19588
|
+
requestJoin(params2) {
|
|
19589
|
+
return this.call("group.request_join", params2);
|
|
19590
|
+
}
|
|
19591
|
+
listJoinRequests(params2) {
|
|
19592
|
+
return this.call("group.list_join_requests", params2);
|
|
19593
|
+
}
|
|
19594
|
+
reviewJoinRequest(params2) {
|
|
19595
|
+
return this.call("group.review_join_request", params2);
|
|
19596
|
+
}
|
|
19597
|
+
batchReviewJoinRequest(params2) {
|
|
19598
|
+
return this.call("group.batch_review_join_request", params2);
|
|
19599
|
+
}
|
|
19600
|
+
createInviteCode(params2) {
|
|
19601
|
+
return this.call("group.create_invite_code", params2);
|
|
19602
|
+
}
|
|
19603
|
+
listInviteCodes(params2) {
|
|
19604
|
+
return this.call("group.list_invite_codes", params2);
|
|
19605
|
+
}
|
|
19606
|
+
useInviteCode(params2) {
|
|
19607
|
+
return this.call("group.use_invite_code", params2);
|
|
19608
|
+
}
|
|
19609
|
+
revokeInviteCode(params2) {
|
|
19610
|
+
return this.call("group.revoke_invite_code", params2);
|
|
19611
|
+
}
|
|
19612
|
+
setSettings(params2) {
|
|
19613
|
+
return this.call("group.set_settings", params2);
|
|
19614
|
+
}
|
|
19615
|
+
getSettings(params2) {
|
|
19616
|
+
return this.call("group.get_settings", params2);
|
|
19617
|
+
}
|
|
19618
|
+
async checkGroupIndex(params2) {
|
|
19619
|
+
const merged = stripNil2(params2);
|
|
19620
|
+
const groupAid = String(merged.group_aid ?? merged.group_id ?? "").trim();
|
|
19621
|
+
if (!groupAid) throw new Error("group_aid is required");
|
|
19622
|
+
const clientAny = this.client;
|
|
19623
|
+
const stale = typeof clientAny.isGroupIndexStale === "function" ? Boolean(clientAny.isGroupIndexStale(groupAid)) : false;
|
|
19624
|
+
const remoteMeta = typeof clientAny.getGroupIndexRemoteMeta === "function" ? clientAny.getGroupIndexRemoteMeta(groupAid) ?? {} : {};
|
|
19625
|
+
const localEtag = typeof clientAny.getGroupIndexLocalEtag === "function" ? String(clientAny.getGroupIndexLocalEtag(groupAid) ?? "") : "";
|
|
19626
|
+
const remoteEtag = String(remoteMeta?.etag ?? "");
|
|
19627
|
+
const localFound = Boolean(localEtag);
|
|
19628
|
+
const remoteFound = Boolean(remoteEtag);
|
|
19629
|
+
const inSync = localFound && remoteFound && localEtag === remoteEtag;
|
|
19487
19630
|
return {
|
|
19488
|
-
|
|
19489
|
-
|
|
19631
|
+
group_aid: groupAid,
|
|
19632
|
+
local_found: localFound,
|
|
19633
|
+
remote_found: remoteFound,
|
|
19634
|
+
local_etag: localEtag,
|
|
19635
|
+
remote_etag: remoteEtag,
|
|
19636
|
+
in_sync: inSync,
|
|
19637
|
+
needs_update: Boolean(stale || remoteFound && !inSync),
|
|
19638
|
+
last_modified: remoteMeta?.last_modified,
|
|
19639
|
+
status: remoteFound ? 200 : 404,
|
|
19640
|
+
cached: true
|
|
19490
19641
|
};
|
|
19491
19642
|
}
|
|
19492
|
-
async
|
|
19493
|
-
const
|
|
19494
|
-
const
|
|
19495
|
-
|
|
19496
|
-
const
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
);
|
|
19643
|
+
async getGroupIndex(params2) {
|
|
19644
|
+
const merged = stripNil2(params2);
|
|
19645
|
+
const groupId = String(merged.group_id ?? "").trim();
|
|
19646
|
+
if (!groupId) throw new Error("group_id is required");
|
|
19647
|
+
const result = await this.getSettings({ group_id: groupId, keys: [GROUP_INDEX_KEY] });
|
|
19648
|
+
const resultMap = result;
|
|
19649
|
+
const groupAid = String(resultMap.group_aid ?? "").trim();
|
|
19650
|
+
if (!groupAid) throw new Error("group.get_settings response group_aid is required");
|
|
19651
|
+
let groupIndex = null;
|
|
19652
|
+
for (const item of result.settings ?? []) {
|
|
19653
|
+
if (item?.key !== GROUP_INDEX_KEY) continue;
|
|
19654
|
+
groupIndex = item.value;
|
|
19655
|
+
break;
|
|
19503
19656
|
}
|
|
19657
|
+
if (!groupIndex) {
|
|
19658
|
+
return { group_id: resultMap.group_id, group_aid: groupAid, group_index: null, meta: {}, entries: [], settings: {} };
|
|
19659
|
+
}
|
|
19660
|
+
const parsed = parseGroupIndex(groupIndex);
|
|
19661
|
+
this.validateGroupIndexEntries(parsed.entries);
|
|
19662
|
+
await this.verifyPulledGroupIndex(groupId, groupAid, groupIndex, parsed);
|
|
19663
|
+
const etag = String(parsed.meta.etag ?? "");
|
|
19664
|
+
const settings = await this.hydrateGroupIndexSettings(groupId, groupAid, parsed.entries, etag, groupIndex);
|
|
19504
19665
|
return {
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19666
|
+
group_id: resultMap.group_id,
|
|
19667
|
+
group_aid: groupAid,
|
|
19668
|
+
group_index: groupIndex,
|
|
19669
|
+
meta: parsed.meta,
|
|
19670
|
+
entries: parsed.entries,
|
|
19671
|
+
settings
|
|
19510
19672
|
};
|
|
19511
19673
|
}
|
|
19512
|
-
async
|
|
19513
|
-
if (
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19674
|
+
async verifyPulledGroupIndex(groupId, groupAid, groupIndex, parsed) {
|
|
19675
|
+
if (String(parsed.meta?.group_aid ?? "").trim() !== groupAid) throw new Error("group.index group_aid does not match group.get_settings response");
|
|
19676
|
+
const signedBy = String(parsed.meta?.signed_by ?? "").trim();
|
|
19677
|
+
if (!signedBy) throw new Error("group.index signed_by is required");
|
|
19678
|
+
const certFingerprint = String(parsed.meta?.cert_fingerprint ?? "").trim();
|
|
19679
|
+
const clientAny = this.client;
|
|
19680
|
+
const currentAid = clientAny.currentAid;
|
|
19681
|
+
let signer = currentAid?.aid === signedBy && !certFingerprint ? currentAid : null;
|
|
19682
|
+
if (currentAid?.aid === signedBy && certFingerprint && typeof currentAid.certPem === "string" && currentAid.certPem) {
|
|
19683
|
+
try {
|
|
19684
|
+
if (await certMatchesFingerprint(currentAid.certPem, certFingerprint)) signer = currentAid;
|
|
19685
|
+
} catch {
|
|
19686
|
+
signer = null;
|
|
19687
|
+
}
|
|
19688
|
+
}
|
|
19689
|
+
if (!signer && typeof clientAny.lookupPeer === "function") {
|
|
19690
|
+
signer = await clientAny.lookupPeer(signedBy, certFingerprint || void 0);
|
|
19691
|
+
}
|
|
19692
|
+
if (!signer) throw new Error(`group.index signer is unavailable: ${signedBy}`);
|
|
19693
|
+
const verified = await verifyGroupIndex(groupIndex, signer);
|
|
19694
|
+
if (!verified.ok) throw new Error(verified.error.message || "group.index verification failed");
|
|
19695
|
+
if (!verified.data.valid) throw new Error(`group.index verification failed: ${verified.data.reason || "invalid signature"}`);
|
|
19696
|
+
const adminsResult = await this.call("group.get_admins", { group_id: groupId });
|
|
19697
|
+
const adminsMap = adminsResult;
|
|
19698
|
+
if (String(adminsMap.group_aid ?? "").trim() !== groupAid) throw new Error("group.get_admins response group_aid does not match group.get_settings response");
|
|
19699
|
+
const authorized = Array.isArray(adminsMap.admins) && adminsMap.admins.some(
|
|
19700
|
+
(item) => String(item?.aid ?? "").trim() === signedBy && ["owner", "admin"].includes(String(item?.role ?? "").trim().toLowerCase())
|
|
19701
|
+
);
|
|
19702
|
+
if (!authorized) throw new Error(`group.index signer is not a current owner or admin: ${signedBy}`);
|
|
19703
|
+
}
|
|
19704
|
+
async updateGroupIndex(params2) {
|
|
19705
|
+
const merged = stripNil2(params2);
|
|
19706
|
+
const groupId = String(merged.group_id ?? "").trim();
|
|
19707
|
+
const settings = merged.settings;
|
|
19708
|
+
if (!groupId) throw new Error("group_id is required");
|
|
19709
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings) || Object.keys(settings).length === 0) {
|
|
19710
|
+
throw new Error("settings must be a non-empty object");
|
|
19711
|
+
}
|
|
19712
|
+
const signer = merged.signer ?? this.client.currentAid;
|
|
19713
|
+
if (!signer) throw new Error("signer is required");
|
|
19714
|
+
const lastModified = Math.trunc(Number(merged.last_modified ?? Date.now()));
|
|
19715
|
+
const maxAttempts = Math.max(1, Math.trunc(Number(merged.max_attempts ?? 2)));
|
|
19716
|
+
let lastError = null;
|
|
19717
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
19718
|
+
const current = await this.getGroupIndex({ group_id: groupId });
|
|
19719
|
+
const groupAid = String(current.group_aid ?? "").trim();
|
|
19720
|
+
const currentIndex = current.group_index ?? null;
|
|
19721
|
+
const expectedEtag = currentIndex ? String(parseGroupIndex(currentIndex).meta.etag ?? "") : "";
|
|
19722
|
+
const baseSettings = current.settings && typeof current.settings === "object" && !Array.isArray(current.settings) ? current.settings : {};
|
|
19723
|
+
const signedSettings = await prepareGroupSettingsWithIndex({
|
|
19724
|
+
groupAid,
|
|
19725
|
+
settings,
|
|
19726
|
+
signer,
|
|
19727
|
+
lastModified,
|
|
19728
|
+
baseIndex: currentIndex
|
|
19729
|
+
});
|
|
19730
|
+
try {
|
|
19731
|
+
const result = await this.setSettings({
|
|
19732
|
+
group_id: groupId,
|
|
19733
|
+
settings: signedSettings,
|
|
19734
|
+
expected_index_etag: expectedEtag
|
|
19735
|
+
});
|
|
19736
|
+
const pushedEtag = String(parseGroupIndex(signedSettings[GROUP_INDEX_KEY]).meta.etag ?? "");
|
|
19737
|
+
const clientAny = this.client;
|
|
19738
|
+
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
19739
|
+
const parsed = parseGroupIndex(signedSettings[GROUP_INDEX_KEY]);
|
|
19740
|
+
await clientAny.cacheGroupIndexSettings(groupAid, { ...baseSettings, ...settings }, {
|
|
19741
|
+
entries: parsed.entries,
|
|
19742
|
+
etag: pushedEtag,
|
|
19743
|
+
groupIndex: signedSettings[GROUP_INDEX_KEY]
|
|
19744
|
+
});
|
|
19745
|
+
}
|
|
19746
|
+
return result;
|
|
19747
|
+
} catch (exc) {
|
|
19748
|
+
if (!String(exc?.message ?? exc).includes("etag conflict")) throw exc;
|
|
19749
|
+
lastError = exc;
|
|
19750
|
+
}
|
|
19751
|
+
}
|
|
19752
|
+
if (lastError) throw lastError;
|
|
19753
|
+
throw new Error("updateGroupIndex failed");
|
|
19754
|
+
}
|
|
19755
|
+
async getIndexedSettings(groupId, keys) {
|
|
19756
|
+
const clientAny = this.client;
|
|
19757
|
+
if (typeof clientAny.getGroupIndexCachedSettings === "function") {
|
|
19758
|
+
const cached = await clientAny.getGroupIndexCachedSettings(groupId, keys);
|
|
19759
|
+
if (cached && typeof cached === "object") return { groupId, settings: cached };
|
|
19760
|
+
}
|
|
19761
|
+
const result = await this.getGroupIndex({ group_id: groupId });
|
|
19762
|
+
const resultMap = result;
|
|
19763
|
+
const allSettings = resultMap.settings && typeof resultMap.settings === "object" && !Array.isArray(resultMap.settings) ? resultMap.settings : {};
|
|
19764
|
+
const settings = Object.fromEntries(keys.filter((key) => key in allSettings).map((key) => [key, allSettings[key]]));
|
|
19765
|
+
const groupAid = String(resultMap.group_aid ?? "").trim();
|
|
19766
|
+
if (groupAid && groupAid !== groupId && typeof clientAny.cacheGroupIndexSettings === "function" && resultMap.group_index) {
|
|
19767
|
+
await clientAny.cacheGroupIndexSettings(groupId, allSettings, { entries: resultMap.entries, etag: String(resultMap.meta?.etag ?? ""), groupIndex: resultMap.group_index });
|
|
19768
|
+
}
|
|
19769
|
+
return { groupId: String(resultMap.group_id ?? groupId), settings };
|
|
19770
|
+
}
|
|
19771
|
+
async hydrateGroupIndexSettings(groupId, groupAid, entries, etag, groupIndex) {
|
|
19772
|
+
const keys = entries.map((item) => String(item.key));
|
|
19773
|
+
const clientAny = this.client;
|
|
19774
|
+
const settings = {};
|
|
19775
|
+
if (keys.length > 0) {
|
|
19776
|
+
const fetchedResult = await this.getSettings({ group_id: groupId, keys });
|
|
19777
|
+
const fetchedMap = fetchedResult;
|
|
19778
|
+
if (String(fetchedMap.group_aid ?? "").trim() !== groupAid) throw new Error("group settings response group_aid does not match verified group.index");
|
|
19779
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19780
|
+
for (const item of fetchedMap.settings ?? []) {
|
|
19781
|
+
const key = String(item?.key ?? "").trim();
|
|
19782
|
+
if (!key || seen.has(key)) throw new Error(`group settings response contains duplicate or empty key: ${key}`);
|
|
19783
|
+
seen.add(key);
|
|
19784
|
+
settings[key] = item?.value;
|
|
19785
|
+
}
|
|
19786
|
+
for (const entry of entries) {
|
|
19787
|
+
const key = String(entry.key);
|
|
19788
|
+
if (!seen.has(key)) throw new Error(`group.index setting is missing: ${key}`);
|
|
19789
|
+
if (await groupIndexSettingEtag(settings[key]) !== String(entry.etag)) throw new Error(`group.index setting etag mismatch: ${key}`);
|
|
19521
19790
|
}
|
|
19522
|
-
return result;
|
|
19523
19791
|
}
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
owner,
|
|
19529
|
-
bucket,
|
|
19530
|
-
path: key,
|
|
19531
|
-
page: options.page,
|
|
19532
|
-
size: options.size,
|
|
19533
|
-
marker: options.marker,
|
|
19534
|
-
token: options.token
|
|
19535
|
-
});
|
|
19536
|
-
const items = raw.nodes ?? raw.items ?? [];
|
|
19537
|
-
return items.map((item) => nodeFromAny(item));
|
|
19538
|
-
}
|
|
19539
|
-
async stat(path, options = {}) {
|
|
19540
|
-
const owner = this.owner(options.owner);
|
|
19541
|
-
const raw = await this.lowlevel.fsStat({ owner, bucket: options.bucket ?? "default", path: pathToKey(path), token: options.token });
|
|
19542
|
-
return nodeFromAny(raw);
|
|
19792
|
+
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
19793
|
+
await clientAny.cacheGroupIndexSettings(groupAid, settings, { entries, etag, groupIndex });
|
|
19794
|
+
}
|
|
19795
|
+
return settings;
|
|
19543
19796
|
}
|
|
19544
|
-
|
|
19545
|
-
const
|
|
19546
|
-
|
|
19547
|
-
|
|
19797
|
+
validateGroupIndexEntries(entries) {
|
|
19798
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19799
|
+
for (const entry of entries) {
|
|
19800
|
+
const key = String(entry?.key ?? "").trim();
|
|
19801
|
+
const source = String(entry?.source ?? "").trim();
|
|
19802
|
+
const etag = String(entry?.etag ?? "").trim();
|
|
19803
|
+
if (!key) throw new Error("group.index entry key is required");
|
|
19804
|
+
if (seen.has(key)) throw new Error(`group.index contains duplicate key: ${key}`);
|
|
19805
|
+
if (source !== "db") throw new Error(`group.index entry source is unsupported: ${source || "<empty>"}`);
|
|
19806
|
+
if (!etag) throw new Error(`group.index entry etag is required: ${key}`);
|
|
19807
|
+
seen.add(key);
|
|
19808
|
+
}
|
|
19548
19809
|
}
|
|
19549
|
-
|
|
19550
|
-
|
|
19551
|
-
return
|
|
19810
|
+
send(params2) {
|
|
19811
|
+
validateGroupIDFormat(params2?.group_id, "group_id");
|
|
19812
|
+
return this.call("group.send", params2);
|
|
19552
19813
|
}
|
|
19553
|
-
|
|
19554
|
-
|
|
19555
|
-
owner: this.owner(options.owner),
|
|
19556
|
-
bucket: options.bucket ?? "default",
|
|
19557
|
-
path: pathToKey(path),
|
|
19558
|
-
parents: options.parents,
|
|
19559
|
-
noCreate: options.noCreate,
|
|
19560
|
-
mtime: options.mtime,
|
|
19561
|
-
followSymlinks: options.followSymlinks
|
|
19562
|
-
});
|
|
19563
|
-
return nodeFromAny(raw.node ?? raw);
|
|
19814
|
+
recall(params2) {
|
|
19815
|
+
return this.call("group.recall", params2);
|
|
19564
19816
|
}
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
return
|
|
19817
|
+
pull(params2) {
|
|
19818
|
+
validateGroupIDFormat(params2?.group_id, "group_id");
|
|
19819
|
+
return this.call("group.pull", params2);
|
|
19568
19820
|
}
|
|
19569
|
-
|
|
19570
|
-
|
|
19571
|
-
return nodeFromAny(raw.node ?? raw);
|
|
19821
|
+
history(params2) {
|
|
19822
|
+
return this.call("group.history", params2);
|
|
19572
19823
|
}
|
|
19573
|
-
|
|
19574
|
-
|
|
19575
|
-
owner: this.owner(options.owner),
|
|
19576
|
-
bucket: options.bucket ?? "default",
|
|
19577
|
-
src: pathToKey(src),
|
|
19578
|
-
dst: pathToKey(dst),
|
|
19579
|
-
overwrite: options.overwrite,
|
|
19580
|
-
followSymlinks: options.followSymlinks,
|
|
19581
|
-
recursive: options.recursive,
|
|
19582
|
-
dstOwner: options.dstOwner,
|
|
19583
|
-
dstBucket: options.dstBucket
|
|
19584
|
-
});
|
|
19585
|
-
return nodeFromAny(raw.node ?? raw);
|
|
19824
|
+
pullEvents(params2) {
|
|
19825
|
+
return this.call("group.pull_events", params2);
|
|
19586
19826
|
}
|
|
19587
|
-
|
|
19588
|
-
|
|
19589
|
-
owner: this.owner(options.owner),
|
|
19590
|
-
bucket: options.bucket ?? "default",
|
|
19591
|
-
path: pathToKey(path),
|
|
19592
|
-
name: options.name,
|
|
19593
|
-
nodeType: options.nodeType,
|
|
19594
|
-
size: options.size,
|
|
19595
|
-
mtime: options.mtime,
|
|
19596
|
-
page: options.page,
|
|
19597
|
-
pageSize: options.pageSize,
|
|
19598
|
-
token: options.token
|
|
19599
|
-
});
|
|
19600
|
-
const items = raw.nodes ?? raw.items ?? [];
|
|
19601
|
-
return items.map((item) => nodeFromAny(item));
|
|
19827
|
+
ackMessages(params2) {
|
|
19828
|
+
return this.call("group.ack_messages", params2);
|
|
19602
19829
|
}
|
|
19603
|
-
|
|
19604
|
-
|
|
19605
|
-
const raw = await this.lowlevel.fsDf({ owner, bucket: options.bucket ?? "default" });
|
|
19606
|
-
return usageFromAny(raw, owner ?? void 0);
|
|
19830
|
+
ackEvents(params2) {
|
|
19831
|
+
return this.call("group.ack_events", params2);
|
|
19607
19832
|
}
|
|
19608
|
-
|
|
19609
|
-
const
|
|
19610
|
-
|
|
19611
|
-
bucket: options.bucket,
|
|
19612
|
-
page: 1,
|
|
19613
|
-
pageSize: options.pageSize ?? 1e3,
|
|
19614
|
-
token: options.token
|
|
19615
|
-
});
|
|
19616
|
-
const rootParts = pathToKey(path).split("/").filter(Boolean);
|
|
19617
|
-
let sizeBytes = 0;
|
|
19618
|
-
let fileCount = 0;
|
|
19619
|
-
let dirCount = 0;
|
|
19620
|
-
let symlinkCount = 0;
|
|
19621
|
-
let truncated = false;
|
|
19622
|
-
for (const node of nodes) {
|
|
19623
|
-
const nodeParts = pathToKey(node.path).split("/").filter(Boolean);
|
|
19624
|
-
const depth = Math.max(0, nodeParts.length - rootParts.length);
|
|
19625
|
-
if (options.maxDepth !== void 0 && depth > options.maxDepth) {
|
|
19626
|
-
truncated = true;
|
|
19627
|
-
continue;
|
|
19628
|
-
}
|
|
19629
|
-
if (node.type === "file") {
|
|
19630
|
-
fileCount += 1;
|
|
19631
|
-
sizeBytes += node.size;
|
|
19632
|
-
} else if (node.type === "dir") {
|
|
19633
|
-
dirCount += 1;
|
|
19634
|
-
} else if (node.type === "symlink") {
|
|
19635
|
-
symlinkCount += 1;
|
|
19636
|
-
}
|
|
19637
|
-
}
|
|
19833
|
+
documentSettingResult(groupId, keyName, settings) {
|
|
19834
|
+
const contentKey = `${keyName}.content`;
|
|
19835
|
+
const attachmentsKey = `${keyName}.attachments`;
|
|
19638
19836
|
return {
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19837
|
+
group_id: groupId,
|
|
19838
|
+
setting: {
|
|
19839
|
+
group_id: groupId,
|
|
19840
|
+
key_name: keyName,
|
|
19841
|
+
content: settings[contentKey] ?? "",
|
|
19842
|
+
attachments: settings[attachmentsKey] ?? [],
|
|
19843
|
+
updated_by: settings[`${contentKey}.updated_by`] ?? "",
|
|
19844
|
+
updated_at: settings[`${contentKey}.updated_at`] ?? 0
|
|
19845
|
+
}
|
|
19646
19846
|
};
|
|
19647
19847
|
}
|
|
19648
|
-
async
|
|
19649
|
-
const
|
|
19650
|
-
const
|
|
19651
|
-
|
|
19652
|
-
const
|
|
19653
|
-
|
|
19654
|
-
|
|
19655
|
-
|
|
19656
|
-
|
|
19657
|
-
|
|
19658
|
-
sourcePath: pathToKey(sourceRef.path),
|
|
19659
|
-
readonly: options.readonly,
|
|
19660
|
-
expiresAt: options.expiresAt ?? options.expires,
|
|
19661
|
-
requireApproval: options.requireApproval
|
|
19662
|
-
});
|
|
19663
|
-
return nodeFromAny(raw.node ?? raw.mount ?? raw);
|
|
19664
|
-
}
|
|
19665
|
-
async mountVolume(volumeId, mountPath, options = {}) {
|
|
19666
|
-
const owner = this.owner(options.owner);
|
|
19667
|
-
const mountRef = parsePathLike(mountPath, owner);
|
|
19668
|
-
const raw = await this.lowlevel.fsMount({
|
|
19669
|
-
owner: mountRef.owner,
|
|
19670
|
-
bucket: options.bucket ?? "default",
|
|
19671
|
-
mountPath: pathToKey(mountRef.path),
|
|
19672
|
-
readonly: options.readonly,
|
|
19673
|
-
expiresAt: options.expiresAt ?? options.expires,
|
|
19674
|
-
requireApproval: options.requireApproval,
|
|
19675
|
-
volumeId
|
|
19676
|
-
});
|
|
19677
|
-
return nodeFromAny(raw.node ?? raw.mount ?? raw);
|
|
19678
|
-
}
|
|
19679
|
-
approveMount(mountPath, options = {}) {
|
|
19680
|
-
const owner = this.owner(options.owner);
|
|
19681
|
-
const mountRef = parsePathLike(mountPath, owner);
|
|
19682
|
-
return this.lowlevel.fsApprove({
|
|
19683
|
-
owner: mountRef.owner,
|
|
19684
|
-
bucket: options.bucket ?? "default",
|
|
19685
|
-
mountPath: pathToKey(mountRef.path),
|
|
19686
|
-
mountId: options.mountId,
|
|
19687
|
-
requestId: options.requestId
|
|
19688
|
-
});
|
|
19689
|
-
}
|
|
19690
|
-
rejectMount(mountPath, options = {}) {
|
|
19691
|
-
const owner = this.owner(options.owner);
|
|
19692
|
-
const mountRef = parsePathLike(mountPath, owner);
|
|
19693
|
-
return this.lowlevel.fsReject({
|
|
19694
|
-
owner: mountRef.owner,
|
|
19695
|
-
bucket: options.bucket ?? "default",
|
|
19696
|
-
mountPath: pathToKey(mountRef.path),
|
|
19697
|
-
mountId: options.mountId,
|
|
19698
|
-
requestId: options.requestId
|
|
19699
|
-
});
|
|
19700
|
-
}
|
|
19701
|
-
async unmount(mountPath, options = {}) {
|
|
19702
|
-
const owner = this.owner(options.owner);
|
|
19703
|
-
const mountRef = parsePathLike(mountPath, owner);
|
|
19704
|
-
const raw = await this.lowlevel.fsUnmount({
|
|
19705
|
-
owner: mountRef.owner,
|
|
19706
|
-
bucket: options.bucket ?? "default",
|
|
19707
|
-
mountPath: pathToKey(mountRef.path)
|
|
19708
|
-
});
|
|
19709
|
-
return unmountFromAny(raw, {
|
|
19710
|
-
owner: mountRef.owner,
|
|
19711
|
-
bucket: options.bucket ?? "default",
|
|
19712
|
-
mountPath: pathToKey(mountRef.path)
|
|
19713
|
-
});
|
|
19714
|
-
}
|
|
19715
|
-
async symlink(target, linkPath, options = {}) {
|
|
19716
|
-
const raw = await this.lowlevel.createSymlink({ owner: this.owner(options.owner), bucket: options.bucket ?? "default", path: pathToKey(linkPath), target, overwrite: options.overwrite });
|
|
19717
|
-
return nodeFromAny(raw.symlink ?? raw);
|
|
19848
|
+
async getSettingWithIndex(params2) {
|
|
19849
|
+
const merged = params2 || {};
|
|
19850
|
+
const groupId = merged.group_id;
|
|
19851
|
+
if (!groupId) throw new Error("group_id is required");
|
|
19852
|
+
const keyName = indexedDocumentKeyName(merged);
|
|
19853
|
+
const { groupId: resultGroupId, settings } = await this.getIndexedSettings(
|
|
19854
|
+
String(groupId),
|
|
19855
|
+
[`${keyName}.content`, `${keyName}.attachments`]
|
|
19856
|
+
);
|
|
19857
|
+
return this.documentSettingResult(resultGroupId, keyName, settings);
|
|
19718
19858
|
}
|
|
19719
|
-
async
|
|
19720
|
-
const
|
|
19721
|
-
|
|
19859
|
+
async updateSettingWithIndex(params2) {
|
|
19860
|
+
const merged = params2 || {};
|
|
19861
|
+
const groupId = merged.group_id;
|
|
19862
|
+
if (!groupId) throw new Error("group_id is required");
|
|
19863
|
+
const keyName = indexedDocumentKeyName(merged);
|
|
19864
|
+
if (!("content" in merged)) throw new Error("content is required");
|
|
19865
|
+
const content = merged.content;
|
|
19866
|
+
const attachments = merged.attachments ?? [];
|
|
19867
|
+
const settingsUpdate = { [`${keyName}.content`]: content };
|
|
19868
|
+
if ("attachments" in merged) {
|
|
19869
|
+
settingsUpdate[`${keyName}.attachments`] = attachments;
|
|
19870
|
+
}
|
|
19871
|
+
const result = await this.updateGroupIndex(indexUpdateParams(groupId, settingsUpdate, merged));
|
|
19872
|
+
const resultGroupId = String(result.group_id ?? groupId);
|
|
19873
|
+
return {
|
|
19874
|
+
group_id: resultGroupId,
|
|
19875
|
+
setting: {
|
|
19876
|
+
group_id: resultGroupId,
|
|
19877
|
+
key_name: keyName,
|
|
19878
|
+
content,
|
|
19879
|
+
attachments,
|
|
19880
|
+
updated_by: "",
|
|
19881
|
+
updated_at: 0
|
|
19882
|
+
}
|
|
19883
|
+
};
|
|
19722
19884
|
}
|
|
19723
|
-
async
|
|
19724
|
-
const
|
|
19725
|
-
|
|
19726
|
-
|
|
19885
|
+
async getAnnouncement(params2) {
|
|
19886
|
+
const merged = params2 || {};
|
|
19887
|
+
const result = await this.getSettingWithIndex({ ...merged, keyName: "announcement" });
|
|
19888
|
+
const resultMap = result;
|
|
19889
|
+
const setting = resultMap.setting;
|
|
19890
|
+
return {
|
|
19891
|
+
group_id: resultMap.group_id,
|
|
19892
|
+
announcement: {
|
|
19893
|
+
group_id: setting.group_id,
|
|
19894
|
+
content: setting.content,
|
|
19895
|
+
attachments: setting.attachments,
|
|
19896
|
+
updated_by: setting.updated_by,
|
|
19897
|
+
updated_at: setting.updated_at
|
|
19898
|
+
}
|
|
19899
|
+
};
|
|
19727
19900
|
}
|
|
19728
|
-
async
|
|
19729
|
-
const
|
|
19730
|
-
|
|
19731
|
-
|
|
19732
|
-
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
|
|
19736
|
-
|
|
19737
|
-
|
|
19738
|
-
|
|
19901
|
+
async updateAnnouncement(params2) {
|
|
19902
|
+
const merged = params2 || {};
|
|
19903
|
+
const result = await this.updateSettingWithIndex({ ...merged, keyName: "announcement" });
|
|
19904
|
+
const resultMap = result;
|
|
19905
|
+
const setting = resultMap.setting;
|
|
19906
|
+
return {
|
|
19907
|
+
group_id: resultMap.group_id,
|
|
19908
|
+
announcement: {
|
|
19909
|
+
group_id: resultMap.group_id,
|
|
19910
|
+
content: setting.content,
|
|
19911
|
+
attachments: setting.attachments
|
|
19912
|
+
}
|
|
19913
|
+
};
|
|
19739
19914
|
}
|
|
19740
|
-
|
|
19741
|
-
|
|
19915
|
+
async getRules(params2) {
|
|
19916
|
+
const merged = params2 || {};
|
|
19917
|
+
const result = await this.getSettingWithIndex({ ...merged, keyName: "rules" });
|
|
19918
|
+
const resultMap = result;
|
|
19919
|
+
const setting = resultMap.setting;
|
|
19920
|
+
return {
|
|
19921
|
+
group_id: resultMap.group_id,
|
|
19922
|
+
rules: {
|
|
19923
|
+
group_id: setting.group_id,
|
|
19924
|
+
content: setting.content,
|
|
19925
|
+
attachments: setting.attachments,
|
|
19926
|
+
updated_by: setting.updated_by,
|
|
19927
|
+
updated_at: setting.updated_at
|
|
19928
|
+
}
|
|
19929
|
+
};
|
|
19742
19930
|
}
|
|
19743
|
-
|
|
19744
|
-
|
|
19931
|
+
async updateRules(params2) {
|
|
19932
|
+
const merged = params2 || {};
|
|
19933
|
+
const result = await this.updateSettingWithIndex({ ...merged, keyName: "rules" });
|
|
19934
|
+
const resultMap = result;
|
|
19935
|
+
const setting = resultMap.setting;
|
|
19936
|
+
return {
|
|
19937
|
+
group_id: resultMap.group_id,
|
|
19938
|
+
rules: {
|
|
19939
|
+
group_id: resultMap.group_id,
|
|
19940
|
+
content: setting.content,
|
|
19941
|
+
attachments: setting.attachments
|
|
19942
|
+
}
|
|
19943
|
+
};
|
|
19745
19944
|
}
|
|
19746
|
-
|
|
19747
|
-
|
|
19945
|
+
async getJoinRequirements(params2) {
|
|
19946
|
+
const merged = params2 || {};
|
|
19947
|
+
const groupId = merged.group_id;
|
|
19948
|
+
if (!groupId) throw new Error("group_id is required");
|
|
19949
|
+
const { groupId: resultGroupId, settings } = await this.getIndexedSettings(
|
|
19950
|
+
String(groupId),
|
|
19951
|
+
["join.mode", "join.question", "join.auto_approve_patterns", "join.max_pending", "join.attachments"]
|
|
19952
|
+
);
|
|
19953
|
+
return {
|
|
19954
|
+
group_id: resultGroupId,
|
|
19955
|
+
join_requirements: {
|
|
19956
|
+
group_id: resultGroupId,
|
|
19957
|
+
mode: settings["join.mode"] || "open",
|
|
19958
|
+
question: settings["join.question"] || "",
|
|
19959
|
+
auto_approve_patterns: settings["join.auto_approve_patterns"] || [],
|
|
19960
|
+
max_pending: settings["join.max_pending"] || 100,
|
|
19961
|
+
attachments: settings["join.attachments"] || [],
|
|
19962
|
+
updated_by: settings["join.mode.updated_by"] || "",
|
|
19963
|
+
updated_at: settings["join.mode.updated_at"] || 0
|
|
19964
|
+
}
|
|
19965
|
+
};
|
|
19748
19966
|
}
|
|
19749
|
-
async
|
|
19750
|
-
const
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
|
|
19756
|
-
|
|
19757
|
-
|
|
19967
|
+
async updateJoinRequirements(params2) {
|
|
19968
|
+
const merged = params2 || {};
|
|
19969
|
+
const groupId = merged.group_id;
|
|
19970
|
+
if (!groupId) throw new Error("group_id is required");
|
|
19971
|
+
const settingsUpdate = {};
|
|
19972
|
+
if ("mode" in merged) settingsUpdate["join.mode"] = merged.mode;
|
|
19973
|
+
if ("question" in merged) settingsUpdate["join.question"] = merged.question;
|
|
19974
|
+
if ("auto_approve_patterns" in merged) settingsUpdate["join.auto_approve_patterns"] = merged.auto_approve_patterns;
|
|
19975
|
+
if ("max_pending" in merged) settingsUpdate["join.max_pending"] = merged.max_pending;
|
|
19976
|
+
if ("attachments" in merged) settingsUpdate["join.attachments"] = merged.attachments;
|
|
19977
|
+
if (Object.keys(settingsUpdate).length === 0) {
|
|
19978
|
+
throw new Error("at least one field to update is required");
|
|
19979
|
+
}
|
|
19980
|
+
const result = await this.updateGroupIndex(indexUpdateParams(groupId, settingsUpdate, merged));
|
|
19981
|
+
return {
|
|
19982
|
+
group_id: result.group_id,
|
|
19983
|
+
join_requirements: {
|
|
19984
|
+
group_id: result.group_id,
|
|
19985
|
+
mode: merged.mode,
|
|
19986
|
+
question: merged.question,
|
|
19987
|
+
auto_approve_patterns: merged.auto_approve_patterns,
|
|
19988
|
+
max_pending: merged.max_pending,
|
|
19989
|
+
attachments: merged.attachments || []
|
|
19990
|
+
}
|
|
19991
|
+
};
|
|
19758
19992
|
}
|
|
19759
|
-
|
|
19760
|
-
return this.
|
|
19761
|
-
owner: this.owner(options.owner),
|
|
19762
|
-
bucket: options.bucket ?? "default",
|
|
19763
|
-
path: pathToKey(path),
|
|
19764
|
-
operation: options.operation ?? "read",
|
|
19765
|
-
token: options.token,
|
|
19766
|
-
followSymlinks: options.followSymlinks ?? true
|
|
19767
|
-
});
|
|
19993
|
+
getOnlineMembers(params2) {
|
|
19994
|
+
return this.call("group.get_online_members", params2);
|
|
19768
19995
|
}
|
|
19769
|
-
|
|
19770
|
-
|
|
19996
|
+
};
|
|
19997
|
+
var StreamFacade = class extends RpcFacade {
|
|
19998
|
+
create(params2) {
|
|
19999
|
+
return this.call("stream.create", params2);
|
|
19771
20000
|
}
|
|
19772
|
-
|
|
19773
|
-
return this.
|
|
20001
|
+
close(params2) {
|
|
20002
|
+
return this.call("stream.close", params2);
|
|
19774
20003
|
}
|
|
19775
|
-
|
|
19776
|
-
return this.
|
|
20004
|
+
getInfo(params2) {
|
|
20005
|
+
return this.call("stream.get_info", params2);
|
|
19777
20006
|
}
|
|
19778
|
-
|
|
19779
|
-
|
|
19780
|
-
const raw = await this.lowlevel.getQuota({ owner, bucket: options.bucket ?? "default" });
|
|
19781
|
-
return usageFromAny(raw, owner ?? "");
|
|
20007
|
+
listActive(params2) {
|
|
20008
|
+
return this.call("stream.list_active", params2);
|
|
19782
20009
|
}
|
|
19783
20010
|
};
|
|
19784
20011
|
|
|
@@ -24945,6 +25172,12 @@ var V2E2EECoordinator = class {
|
|
|
24945
25172
|
await client._publishAppEvent(eventName, safeEvent, "pending_retry");
|
|
24946
25173
|
continue;
|
|
24947
25174
|
}
|
|
25175
|
+
if (!entry.groupId && String(entry.msg.storage ?? "") === "volatile") {
|
|
25176
|
+
await this.publishPendingV2VolatileMessage(entry.msg, plaintext, "pending_retry");
|
|
25177
|
+
client._v2SenderIKPending.delete(key);
|
|
25178
|
+
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
25179
|
+
continue;
|
|
25180
|
+
}
|
|
24948
25181
|
client._v2SenderIKPending.delete(key);
|
|
24949
25182
|
const seq2 = Number(entry.msg.seq ?? 0);
|
|
24950
25183
|
if (entry.groupId) {
|
|
@@ -24961,6 +25194,27 @@ var V2E2EECoordinator = class {
|
|
|
24961
25194
|
client._v2SenderIKFetching.delete(fetchKey);
|
|
24962
25195
|
}
|
|
24963
25196
|
}
|
|
25197
|
+
async publishPendingV2VolatileMessage(rawMessage, plaintext, source) {
|
|
25198
|
+
const delivery = this.client._delivery;
|
|
25199
|
+
const claim = delivery.beginVolatileDelivery(rawMessage);
|
|
25200
|
+
if (claim === "inflight") return;
|
|
25201
|
+
if (claim === "seen") {
|
|
25202
|
+
await delivery.ackVolatileMessages([rawMessage], true, false);
|
|
25203
|
+
return;
|
|
25204
|
+
}
|
|
25205
|
+
try {
|
|
25206
|
+
const publicMessage = delivery.publicVolatileMessage(plaintext);
|
|
25207
|
+
for (const key of ["storage", "volatile_cursor", "created_at", "expires_at", "persist"]) {
|
|
25208
|
+
if (key in rawMessage) publicMessage[key] = rawMessage[key];
|
|
25209
|
+
}
|
|
25210
|
+
const appEvent = delivery.p2pAppEventForMessage(publicMessage);
|
|
25211
|
+
await this.client._publishAppEvent(appEvent.event, appEvent.payload, source);
|
|
25212
|
+
delivery.markVolatileSeen(rawMessage);
|
|
25213
|
+
} finally {
|
|
25214
|
+
delivery.finishVolatileDelivery(rawMessage);
|
|
25215
|
+
}
|
|
25216
|
+
await delivery.ackVolatileMessages([rawMessage], true, false);
|
|
25217
|
+
}
|
|
24964
25218
|
async sendV2(to, payload, opts) {
|
|
24965
25219
|
const client = this.client;
|
|
24966
25220
|
await client._ensureV2SessionReady(
|
|
@@ -24968,6 +25222,7 @@ var V2E2EECoordinator = class {
|
|
|
24968
25222
|
"V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)"
|
|
24969
25223
|
);
|
|
24970
25224
|
const toAid = String(to ?? "").trim();
|
|
25225
|
+
const targetDeviceId = String(opts?.deviceId ?? "").trim();
|
|
24971
25226
|
if (!toAid) throw new ValidationError("message.send requires 'to'");
|
|
24972
25227
|
if (!isJsonObject(payload)) throw new ValidationError("message.send payload must be a dict for V2 encryption");
|
|
24973
25228
|
const attempt = async (useCache) => {
|
|
@@ -24976,6 +25231,7 @@ var V2E2EECoordinator = class {
|
|
|
24976
25231
|
payload,
|
|
24977
25232
|
messageId: opts?.messageId,
|
|
24978
25233
|
timestamp: opts?.timestamp,
|
|
25234
|
+
deviceId: targetDeviceId || void 0,
|
|
24979
25235
|
protectedHeaders: opts?.protectedHeaders,
|
|
24980
25236
|
context: opts?.context,
|
|
24981
25237
|
useCache
|
|
@@ -24984,6 +25240,8 @@ var V2E2EECoordinator = class {
|
|
|
24984
25240
|
to: toAid,
|
|
24985
25241
|
payload: envelope,
|
|
24986
25242
|
message_id: opts?.messageId,
|
|
25243
|
+
...opts?.ttl !== void 0 ? { ttl: opts.ttl } : {},
|
|
25244
|
+
...targetDeviceId ? { device_id: targetDeviceId } : {},
|
|
24987
25245
|
encrypt: false,
|
|
24988
25246
|
_skip_send_result_envelope: true
|
|
24989
25247
|
});
|
|
@@ -25242,6 +25500,7 @@ var V2E2EECoordinator = class {
|
|
|
25242
25500
|
const client = this.client;
|
|
25243
25501
|
await client._ensureV2SessionReady("message.pull");
|
|
25244
25502
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
25503
|
+
let volatileCursor = String(opts?.afterVolatileCursor ?? client._delivery.volatilePullCursor() ?? "").trim();
|
|
25245
25504
|
if (ns) client._delivery.onPullStarted?.(ns);
|
|
25246
25505
|
if (opts?.windowMode === "tail") {
|
|
25247
25506
|
if (!opts.gateLocked) {
|
|
@@ -25261,6 +25520,7 @@ var V2E2EECoordinator = class {
|
|
|
25261
25520
|
if (ns && !opts?.gateLocked) {
|
|
25262
25521
|
const key = pullGateKeyForClient(client, "message.v2.pull", {
|
|
25263
25522
|
after_seq: afterSeq,
|
|
25523
|
+
after_volatile_cursor: volatileCursor,
|
|
25264
25524
|
force: opts?.force === true,
|
|
25265
25525
|
limit
|
|
25266
25526
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
@@ -25273,6 +25533,7 @@ var V2E2EECoordinator = class {
|
|
|
25273
25533
|
let nextAfterSeq = opts?.force ? afterSeq : afterSeq || (ns ? client._seqTracker.getContiguousSeq(ns) : 0);
|
|
25274
25534
|
let pullGateKey = ns ? pullGateKeyForClient(client, "message.v2.pull", {
|
|
25275
25535
|
after_seq: afterSeq,
|
|
25536
|
+
after_volatile_cursor: volatileCursor,
|
|
25276
25537
|
force: opts?.force === true,
|
|
25277
25538
|
limit
|
|
25278
25539
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`) : "";
|
|
@@ -25284,14 +25545,26 @@ var V2E2EECoordinator = class {
|
|
|
25284
25545
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
25285
25546
|
while (pageCount < maxPages) {
|
|
25286
25547
|
pageCount += 1;
|
|
25548
|
+
const volatileCursorBeforePage = volatileCursor;
|
|
25287
25549
|
const ackUpToSeq = pendingAckSeq;
|
|
25288
25550
|
const pendingInlineGeneration = ackUpToSeq > 0 ? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null : null;
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25551
|
+
let result;
|
|
25552
|
+
try {
|
|
25553
|
+
result = await client._callRawV2Rpc("message.v2.pull", {
|
|
25554
|
+
after_seq: nextAfterSeq,
|
|
25555
|
+
limit,
|
|
25556
|
+
...volatileCursor ? { after_volatile_cursor: volatileCursor } : {},
|
|
25557
|
+
...opts?.force ? { force: true } : {},
|
|
25558
|
+
...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
|
|
25559
|
+
}, pullGateKey);
|
|
25560
|
+
} catch (exc) {
|
|
25561
|
+
const text3 = exc instanceof Error ? exc.message : String(exc);
|
|
25562
|
+
if (!volatileCursor || !text3.toLowerCase().includes("volatile cursor epoch mismatch")) throw exc;
|
|
25563
|
+
client._delivery.resetVolatilePullCursor();
|
|
25564
|
+
volatileCursor = "";
|
|
25565
|
+
pageCount -= 1;
|
|
25566
|
+
continue;
|
|
25567
|
+
}
|
|
25295
25568
|
if (ackUpToSeq > 0) {
|
|
25296
25569
|
const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
|
|
25297
25570
|
if (actualAckSeq >= ackUpToSeq) {
|
|
@@ -25309,6 +25582,12 @@ var V2E2EECoordinator = class {
|
|
|
25309
25582
|
}
|
|
25310
25583
|
}
|
|
25311
25584
|
const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
|
|
25585
|
+
const volatileMessages = client._delivery.volatileMessages(pageMessages);
|
|
25586
|
+
if (volatileMessages.length > 0) {
|
|
25587
|
+
decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, "pull", true));
|
|
25588
|
+
volatileCursor = client._delivery.volatilePullCursor();
|
|
25589
|
+
}
|
|
25590
|
+
const volatileAdvanced = Boolean(volatileCursor) && volatileCursor !== volatileCursorBeforePage;
|
|
25312
25591
|
const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
|
|
25313
25592
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
25314
25593
|
const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
|
|
@@ -25485,7 +25764,7 @@ var V2E2EECoordinator = class {
|
|
|
25485
25764
|
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
25486
25765
|
const rawCount = messages.length;
|
|
25487
25766
|
if (blockedSeq > 0) break;
|
|
25488
|
-
|
|
25767
|
+
let shouldContinue = shouldContinueForwardPage({
|
|
25489
25768
|
rawCount,
|
|
25490
25769
|
nextAfterSeq,
|
|
25491
25770
|
nextAfter,
|
|
@@ -25496,10 +25775,16 @@ var V2E2EECoordinator = class {
|
|
|
25496
25775
|
pendingAckSeq,
|
|
25497
25776
|
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
25498
25777
|
});
|
|
25778
|
+
const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, "volatile_has_more");
|
|
25779
|
+
const volatileHasMore = result.volatile_has_more === true;
|
|
25780
|
+
if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || !volatileHasMoreField && limit > 0 && pageMessages.length >= limit)) {
|
|
25781
|
+
shouldContinue = true;
|
|
25782
|
+
}
|
|
25499
25783
|
if (!shouldContinue) break;
|
|
25500
25784
|
if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
25501
25785
|
const nextKey = pullGateKeyForClient(client, "message.v2.pull", {
|
|
25502
25786
|
after_seq: nextAfter,
|
|
25787
|
+
after_volatile_cursor: volatileCursor,
|
|
25503
25788
|
force: opts?.force === true,
|
|
25504
25789
|
limit
|
|
25505
25790
|
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
@@ -25520,6 +25805,69 @@ var V2E2EECoordinator = class {
|
|
|
25520
25805
|
client._delivery.onPullWorkSettled?.();
|
|
25521
25806
|
return deduplicatePulledResults(decrypted);
|
|
25522
25807
|
}
|
|
25808
|
+
async publishV2VolatileMessages(messages, source, advanceCursor) {
|
|
25809
|
+
const client = this.client;
|
|
25810
|
+
if (!client._v2Session) return [];
|
|
25811
|
+
const processed = [];
|
|
25812
|
+
const plaintextMessages = [];
|
|
25813
|
+
let complete = true;
|
|
25814
|
+
for (const rawMessage of messages) {
|
|
25815
|
+
const claim = client._delivery.beginVolatileDelivery(rawMessage);
|
|
25816
|
+
if (claim === "seen") {
|
|
25817
|
+
processed.push(rawMessage);
|
|
25818
|
+
continue;
|
|
25819
|
+
}
|
|
25820
|
+
if (claim === "inflight") {
|
|
25821
|
+
complete = false;
|
|
25822
|
+
continue;
|
|
25823
|
+
}
|
|
25824
|
+
let plaintext;
|
|
25825
|
+
try {
|
|
25826
|
+
if (client._isEncryptedPushMessage(rawMessage)) {
|
|
25827
|
+
plaintext = await client._decryptV2Message(rawMessage, true, true, true, true, void 0, false, source);
|
|
25828
|
+
} else {
|
|
25829
|
+
const legacy = isJsonObject(rawMessage.legacy_v1) ? rawMessage.legacy_v1 : {};
|
|
25830
|
+
plaintext = {
|
|
25831
|
+
message_id: String(rawMessage.message_id ?? ""),
|
|
25832
|
+
from: String(rawMessage.from_aid ?? rawMessage.from ?? ""),
|
|
25833
|
+
to: String(legacy.to ?? rawMessage.to ?? client._aid ?? ""),
|
|
25834
|
+
type: String(rawMessage.type ?? ""),
|
|
25835
|
+
timestamp: rawMessage.t_server ?? rawMessage.timestamp,
|
|
25836
|
+
payload: legacy.payload !== void 0 ? legacy.payload : rawMessage.payload,
|
|
25837
|
+
encrypted: false
|
|
25838
|
+
};
|
|
25839
|
+
for (const key of ["context", "protected_headers", "headers", "client_signature"]) {
|
|
25840
|
+
if (key in rawMessage) plaintext[key] = rawMessage[key];
|
|
25841
|
+
}
|
|
25842
|
+
if (rawMessage.sender_device_id !== void 0) plaintext.sender_device_id = rawMessage.sender_device_id;
|
|
25843
|
+
attachGatewayProximity(plaintext, rawMessage);
|
|
25844
|
+
}
|
|
25845
|
+
if (!plaintext) {
|
|
25846
|
+
complete = false;
|
|
25847
|
+
continue;
|
|
25848
|
+
}
|
|
25849
|
+
const publicMessage = client._delivery.publicVolatileMessage(plaintext);
|
|
25850
|
+
for (const key of ["storage", "volatile_cursor", "created_at", "expires_at", "persist"]) {
|
|
25851
|
+
if (key in rawMessage) publicMessage[key] = rawMessage[key];
|
|
25852
|
+
}
|
|
25853
|
+
const appEvent = client._delivery.p2pAppEventForMessage(publicMessage);
|
|
25854
|
+
await client._publishAppEvent(appEvent.event, appEvent.payload, source);
|
|
25855
|
+
client._delivery.markVolatileSeen(rawMessage);
|
|
25856
|
+
processed.push(rawMessage);
|
|
25857
|
+
plaintextMessages.push(publicMessage);
|
|
25858
|
+
} finally {
|
|
25859
|
+
client._delivery.finishVolatileDelivery(rawMessage);
|
|
25860
|
+
}
|
|
25861
|
+
}
|
|
25862
|
+
if (processed.length > 0) {
|
|
25863
|
+
await client._delivery.ackVolatileMessages(
|
|
25864
|
+
processed,
|
|
25865
|
+
true,
|
|
25866
|
+
advanceCursor && complete && processed.length === messages.length
|
|
25867
|
+
);
|
|
25868
|
+
}
|
|
25869
|
+
return plaintextMessages;
|
|
25870
|
+
}
|
|
25523
25871
|
async confirmForwardP2PAck(upToSeq, inlineGeneration) {
|
|
25524
25872
|
const ns = this.client._aid ? `p2p:${this.client._aid}` : "";
|
|
25525
25873
|
try {
|
|
@@ -25534,34 +25882,41 @@ var V2E2EECoordinator = class {
|
|
|
25534
25882
|
throw exc;
|
|
25535
25883
|
}
|
|
25536
25884
|
}
|
|
25537
|
-
async ackV2(upToSeq, inlineGeneration) {
|
|
25538
|
-
return this.ackV2WithConfirmation(upToSeq, inlineGeneration, false);
|
|
25885
|
+
async ackV2(upToSeq, inlineGeneration, volatileCursors = [], volatileIds = []) {
|
|
25886
|
+
return this.ackV2WithConfirmation(upToSeq, inlineGeneration, false, volatileCursors, volatileIds);
|
|
25539
25887
|
}
|
|
25540
25888
|
async ackV2ForForward(upToSeq, inlineGeneration) {
|
|
25541
25889
|
return this.ackV2WithConfirmation(upToSeq, inlineGeneration, true);
|
|
25542
25890
|
}
|
|
25543
|
-
async ackV2WithConfirmation(upToSeq, inlineGeneration, requireServerWatermark) {
|
|
25891
|
+
async ackV2WithConfirmation(upToSeq, inlineGeneration, requireServerWatermark, volatileCursors = [], volatileIds = []) {
|
|
25544
25892
|
const client = this.client;
|
|
25545
25893
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
25546
25894
|
let seq2 = upToSeq ?? (ns ? client._seqTracker.getContiguousSeq(ns) : 0);
|
|
25547
|
-
if (typeof seq2 !== "number" || !Number.isSafeInteger(seq2) || seq2 <= 0)
|
|
25548
|
-
seq2
|
|
25549
|
-
if (
|
|
25895
|
+
if (typeof seq2 !== "number" || !Number.isSafeInteger(seq2) || seq2 <= 0) seq2 = 0;
|
|
25896
|
+
if (seq2 <= 0 && volatileCursors.length === 0 && volatileIds.length === 0) return { acked: 0 };
|
|
25897
|
+
if (seq2 > 0) seq2 = client._clampAckSeq("message.v2.ack", "up_to_seq", ns, seq2);
|
|
25898
|
+
if ((!Number.isSafeInteger(seq2) || seq2 <= 0) && volatileCursors.length === 0 && volatileIds.length === 0) {
|
|
25899
|
+
return { acked: 0 };
|
|
25900
|
+
}
|
|
25550
25901
|
const ackGeneration = inlineGeneration ?? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null;
|
|
25551
25902
|
const v2SessionAtStart = client._v2Session;
|
|
25552
25903
|
const v2RuntimeGenerationAtStart = Number(client._v2RuntimeGeneration ?? 0);
|
|
25553
|
-
const raw = await client._callRawV2Rpc("message.v2.ack", {
|
|
25904
|
+
const raw = await client._callRawV2Rpc("message.v2.ack", {
|
|
25905
|
+
...seq2 > 0 ? { up_to_seq: seq2 } : {},
|
|
25906
|
+
...volatileCursors.length > 0 ? { volatile_cursors: volatileCursors } : {},
|
|
25907
|
+
...volatileIds.length > 0 ? { volatile_ids: volatileIds } : {}
|
|
25908
|
+
});
|
|
25554
25909
|
const result = isJsonObject(raw) ? { ...raw } : { result: raw };
|
|
25555
|
-
const actualAckSeq = requireServerWatermark ? client._delivery?.resolveForwardAckSeq?.(result, seq2) ?? 0 : client._delivery?.resolveActualAckSeq?.(result, seq2) ?? seq2;
|
|
25556
|
-
if (requireServerWatermark && actualAckSeq < seq2) {
|
|
25910
|
+
const actualAckSeq = seq2 > 0 ? requireServerWatermark ? client._delivery?.resolveForwardAckSeq?.(result, seq2) ?? 0 : client._delivery?.resolveActualAckSeq?.(result, seq2) ?? seq2 : 0;
|
|
25911
|
+
if (seq2 > 0 && requireServerWatermark && actualAckSeq < seq2) {
|
|
25557
25912
|
throw new E2EEError(`message.v2.ack server ACK watermark ${actualAckSeq} is below requested ${seq2}`);
|
|
25558
25913
|
}
|
|
25559
25914
|
result.ack_seq = actualAckSeq;
|
|
25560
25915
|
result.success = true;
|
|
25561
|
-
if (ackGeneration !== null) {
|
|
25916
|
+
if (seq2 > 0 && ackGeneration !== null) {
|
|
25562
25917
|
client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, ackGeneration);
|
|
25563
25918
|
}
|
|
25564
|
-
if (v2SessionAtStart && client._v2Session === v2SessionAtStart && Number(client._v2RuntimeGeneration ?? 0) === v2RuntimeGenerationAtStart) {
|
|
25919
|
+
if (seq2 > 0 && v2SessionAtStart && client._v2Session === v2SessionAtStart && Number(client._v2RuntimeGeneration ?? 0) === v2RuntimeGenerationAtStart) {
|
|
25565
25920
|
try {
|
|
25566
25921
|
const destroyed = await v2SessionAtStart.maybeDestroyOldSPKs(actualAckSeq);
|
|
25567
25922
|
if (client._v2Session === v2SessionAtStart && Number(client._v2RuntimeGeneration ?? 0) === v2RuntimeGenerationAtStart && destroyed.length > 0) {
|
|
@@ -26525,7 +26880,6 @@ var V2E2EECoordinator = class {
|
|
|
26525
26880
|
seq: msg.seq ?? null,
|
|
26526
26881
|
timestamp: msg.timestamp ?? msg.t_server ?? null,
|
|
26527
26882
|
device_id: msg.device_id ?? null,
|
|
26528
|
-
slot_id: msg.slot_id ?? null,
|
|
26529
26883
|
_decrypt_error: "encrypted push payload is not decryptable on raw push path",
|
|
26530
26884
|
_decrypt_stage: "push_envelope"
|
|
26531
26885
|
};
|
|
@@ -26566,10 +26920,11 @@ var V2E2EECoordinator = class {
|
|
|
26566
26920
|
encrypted: true,
|
|
26567
26921
|
e2ee: e2eeMeta
|
|
26568
26922
|
};
|
|
26923
|
+
const senderDeviceId = String(aad.from_device ?? "").trim();
|
|
26924
|
+
if (senderDeviceId) result.sender_device_id = senderDeviceId;
|
|
26569
26925
|
result.direction = fromAid && fromAid === client._aid ? "outbound_sync" : "inbound";
|
|
26570
26926
|
if (msg.t_server !== void 0) result.t_server = msg.t_server;
|
|
26571
26927
|
if (msg.device_id !== void 0) result.device_id = msg.device_id;
|
|
26572
|
-
if (msg.slot_id !== void 0) result.slot_id = msg.slot_id;
|
|
26573
26928
|
attachGatewayProximity(result, msg);
|
|
26574
26929
|
if (group) {
|
|
26575
26930
|
result.group_id = msg.group_id ?? aad.group_id ?? envelope.group_id ?? null;
|
|
@@ -26643,6 +26998,10 @@ var V2E2EECoordinator = class {
|
|
|
26643
26998
|
if (peerDevices.length === 0) {
|
|
26644
26999
|
throw new E2EEError(`V2 bootstrap: no devices found for ${to}`);
|
|
26645
27000
|
}
|
|
27001
|
+
const targetDeviceId = String(opts.deviceId ?? "").trim();
|
|
27002
|
+
if (targetDeviceId && !peerDevices.some((dev) => String(dev.device_id ?? "").trim() === targetDeviceId)) {
|
|
27003
|
+
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
27004
|
+
}
|
|
26646
27005
|
let targetSet = useCache ? this.getTargetSetCache(to) : void 0;
|
|
26647
27006
|
if (targetSet) {
|
|
26648
27007
|
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
@@ -26713,6 +27072,17 @@ var V2E2EECoordinator = class {
|
|
|
26713
27072
|
});
|
|
26714
27073
|
targetSet = this.cloneP2PTargetSet(targetSet);
|
|
26715
27074
|
}
|
|
27075
|
+
if (targetDeviceId) {
|
|
27076
|
+
let peerTargetFound = false;
|
|
27077
|
+
targetSet.targets = targetSet.targets.filter((target) => {
|
|
27078
|
+
const isPeerTarget = target.aid === to && (target.deviceId === targetDeviceId || wrapPolicy?.scope === "aid" && !target.deviceId) && target.role !== "self_sync";
|
|
27079
|
+
if (isPeerTarget) peerTargetFound = true;
|
|
27080
|
+
return isPeerTarget || target.role === "self_sync";
|
|
27081
|
+
});
|
|
27082
|
+
if (!peerTargetFound) {
|
|
27083
|
+
throw new E2EEError(`V2 bootstrap: target device ${targetDeviceId} not found for ${to}`);
|
|
27084
|
+
}
|
|
27085
|
+
}
|
|
26716
27086
|
const sender = await session.getSenderIdentity();
|
|
26717
27087
|
const envelope = await encryptP2PMessage(
|
|
26718
27088
|
sender,
|
|
@@ -27070,6 +27440,9 @@ function groupCacheIds(raw, groupId, explicitGroupAid) {
|
|
|
27070
27440
|
var GroupStateCoordinator = class {
|
|
27071
27441
|
constructor(runtime) {
|
|
27072
27442
|
__publicField(this, "runtime");
|
|
27443
|
+
__publicField(this, "ownerTransferInflight", /* @__PURE__ */ new Set());
|
|
27444
|
+
__publicField(this, "ownerTransferCompletedSeq", /* @__PURE__ */ new Map());
|
|
27445
|
+
__publicField(this, "ownerTransferCompletedLegacy", /* @__PURE__ */ new Set());
|
|
27073
27446
|
this.runtime = runtime;
|
|
27074
27447
|
}
|
|
27075
27448
|
get client() {
|
|
@@ -27102,6 +27475,7 @@ var GroupStateCoordinator = class {
|
|
|
27102
27475
|
if (!groupId) {
|
|
27103
27476
|
return;
|
|
27104
27477
|
}
|
|
27478
|
+
this.handleOwnerTransferEvent(data, groupId, action);
|
|
27105
27479
|
for (const cacheGroupId of groupCacheIds(rawGroupId, groupId, data.group_aid ?? data.groupAid)) {
|
|
27106
27480
|
client._v2E2EE?.deleteBootstrapCacheEntry?.(`group:${cacheGroupId}`);
|
|
27107
27481
|
}
|
|
@@ -27111,6 +27485,60 @@ var GroupStateCoordinator = class {
|
|
|
27111
27485
|
}
|
|
27112
27486
|
client._v2E2EE?.handleGroupChangedSpk?.(data, groupId, action);
|
|
27113
27487
|
}
|
|
27488
|
+
handleOwnerTransferEvent(data, groupId, action) {
|
|
27489
|
+
const status = String(data.status ?? "").trim();
|
|
27490
|
+
if (action !== "owner_transfer_rekey_pending" && action !== "owner_transferred") return;
|
|
27491
|
+
const eventSeq = Number(data.event_seq ?? 0);
|
|
27492
|
+
const normalizedEventSeq = Number.isSafeInteger(eventSeq) && eventSeq > 0 ? eventSeq : 0;
|
|
27493
|
+
const transferKey = [groupId, data.old_owner, data.new_owner].map((value) => String(value ?? "").trim().toLowerCase()).join("|");
|
|
27494
|
+
if (action === "owner_transferred" && status === "transferred") {
|
|
27495
|
+
if (normalizedEventSeq > 0) {
|
|
27496
|
+
this.ownerTransferCompletedSeq.set(
|
|
27497
|
+
groupId,
|
|
27498
|
+
Math.max(normalizedEventSeq, this.ownerTransferCompletedSeq.get(groupId) ?? 0)
|
|
27499
|
+
);
|
|
27500
|
+
} else {
|
|
27501
|
+
this.ownerTransferCompletedLegacy.add(transferKey);
|
|
27502
|
+
}
|
|
27503
|
+
return;
|
|
27504
|
+
}
|
|
27505
|
+
if (action !== "owner_transfer_rekey_pending" || status !== "pending_rekey") return;
|
|
27506
|
+
const client = this.client;
|
|
27507
|
+
const currentAid = String(client._aid ?? "").trim();
|
|
27508
|
+
const newOwner = String(data.new_owner ?? "").trim();
|
|
27509
|
+
const store = client._aidStore;
|
|
27510
|
+
if (!currentAid || currentAid.toLowerCase() !== newOwner.toLowerCase() || !store) return;
|
|
27511
|
+
if (this.ownerTransferInflight.has(groupId)) return;
|
|
27512
|
+
if (normalizedEventSeq > 0) {
|
|
27513
|
+
if ((this.ownerTransferCompletedSeq.get(groupId) ?? 0) >= normalizedEventSeq) return;
|
|
27514
|
+
} else if (this.ownerTransferCompletedLegacy.has(transferKey)) {
|
|
27515
|
+
return;
|
|
27516
|
+
}
|
|
27517
|
+
this.ownerTransferInflight.add(groupId);
|
|
27518
|
+
const params2 = { group_id: groupId };
|
|
27519
|
+
const groupAid = String(data.group_aid ?? "").trim();
|
|
27520
|
+
if (groupAid) params2.group_aid = groupAid;
|
|
27521
|
+
const task = (async () => {
|
|
27522
|
+
try {
|
|
27523
|
+
await client.completeGroupTransfer(params2, { aidStore: store });
|
|
27524
|
+
if (normalizedEventSeq > 0) {
|
|
27525
|
+
this.ownerTransferCompletedSeq.set(
|
|
27526
|
+
groupId,
|
|
27527
|
+
Math.max(normalizedEventSeq, this.ownerTransferCompletedSeq.get(groupId) ?? 0)
|
|
27528
|
+
);
|
|
27529
|
+
} else {
|
|
27530
|
+
this.ownerTransferCompletedLegacy.add(transferKey);
|
|
27531
|
+
}
|
|
27532
|
+
} catch (exc) {
|
|
27533
|
+
client._clientLog.debug(
|
|
27534
|
+
`automatic group owner transfer completion failed: group=${groupId} err=${String(formatCaughtError(exc))}`
|
|
27535
|
+
);
|
|
27536
|
+
} finally {
|
|
27537
|
+
this.ownerTransferInflight.delete(groupId);
|
|
27538
|
+
}
|
|
27539
|
+
})();
|
|
27540
|
+
client._safeAsync(task);
|
|
27541
|
+
}
|
|
27114
27542
|
async onV2StateProposed(data) {
|
|
27115
27543
|
const client = this.client;
|
|
27116
27544
|
if (!isJsonObject(data) || !client._v2Session) return;
|
|
@@ -28822,6 +29250,7 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
28822
29250
|
__publicField(this, "_uploadViaGatewayHost");
|
|
28823
29251
|
__publicField(this, "_tokenStore");
|
|
28824
29252
|
__publicField(this, "_log");
|
|
29253
|
+
__publicField(this, "_onChanged");
|
|
28825
29254
|
__publicField(this, "_ownerAidGetter");
|
|
28826
29255
|
__publicField(this, "_currentAidGetter");
|
|
28827
29256
|
__publicField(this, "_gatewayResolver");
|
|
@@ -28839,6 +29268,7 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
28839
29268
|
this._uploadViaGatewayHost = opts.uploadViaGatewayHost ?? false;
|
|
28840
29269
|
this._tokenStore = opts.tokenStore;
|
|
28841
29270
|
this._log = opts.logger ?? noopLogger;
|
|
29271
|
+
this._onChanged = opts.onChanged;
|
|
28842
29272
|
this._ownerAidGetter = opts.ownerAidGetter;
|
|
28843
29273
|
this._currentAidGetter = opts.currentAidGetter;
|
|
28844
29274
|
this._gatewayResolver = opts.gatewayResolver;
|
|
@@ -29042,6 +29472,9 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
29042
29472
|
let before = this._cache.get(target);
|
|
29043
29473
|
if (!before || typeof before !== "object") before = await this.loadRecord(target) ?? {};
|
|
29044
29474
|
const same = (!remoteEtag || String(before.remote_etag ?? "").trim() === remoteEtag) && (!remoteLastModified || String(before.last_modified ?? "").trim() === remoteLastModified);
|
|
29475
|
+
const hadRemoteVersion = Boolean(
|
|
29476
|
+
String(before.remote_etag ?? "").trim() || String(before.last_modified ?? "").trim()
|
|
29477
|
+
);
|
|
29045
29478
|
let record = { ...before };
|
|
29046
29479
|
if (!same || Object.keys(before).length === 0) {
|
|
29047
29480
|
const fields = {
|
|
@@ -29050,7 +29483,9 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
29050
29483
|
};
|
|
29051
29484
|
if (remoteEtag) fields.remote_etag = remoteEtag;
|
|
29052
29485
|
if (remoteLastModified) fields.last_modified = remoteLastModified;
|
|
29053
|
-
|
|
29486
|
+
const saved = await this.saveRecord(target, fields);
|
|
29487
|
+
record = saved || record;
|
|
29488
|
+
if (Object.keys(saved).length > 0 && hadRemoteVersion && !same) this._onChanged?.(target);
|
|
29054
29489
|
}
|
|
29055
29490
|
await this._scheduleFetchIfMissing(target, record, source);
|
|
29056
29491
|
this._log.debug(`agent.md meta observed: aid=${target} etag=${remoteEtag || "-"} last_modified=${remoteLastModified || "-"} source=${source || "-"}`);
|
|
@@ -29606,6 +30041,7 @@ function createAgentMdManagerForRuntime(opts) {
|
|
|
29606
30041
|
return new AgentMdManager({
|
|
29607
30042
|
aunPath: opts.config().aunPath,
|
|
29608
30043
|
tokenStore: opts.tokenStore(),
|
|
30044
|
+
onChanged: (aid) => opts.dispatcher().enqueue("agentmd.changed", aid),
|
|
29609
30045
|
uploadViaGatewayHost: true,
|
|
29610
30046
|
logger: opts.logger(),
|
|
29611
30047
|
ownerAidGetter: opts.ownerAid,
|
|
@@ -30018,6 +30454,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30018
30454
|
this._agentMdManager = createAgentMdManagerForRuntime({
|
|
30019
30455
|
config: () => this.configModel,
|
|
30020
30456
|
logger: () => this._logger.for("aun_core.agent_md"),
|
|
30457
|
+
dispatcher: () => this._dispatcher,
|
|
30021
30458
|
ownerAid: () => this._aid,
|
|
30022
30459
|
currentAid: () => this._currentAid,
|
|
30023
30460
|
gateway: {
|
|
@@ -30418,6 +30855,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30418
30855
|
this._agentMdManager = createAgentMdManagerForRuntime({
|
|
30419
30856
|
config: () => this.configModel,
|
|
30420
30857
|
logger: () => this._logger.for("aun_core.agent_md"),
|
|
30858
|
+
dispatcher: () => this._dispatcher,
|
|
30421
30859
|
ownerAid: () => this._aid,
|
|
30422
30860
|
currentAid: () => this._currentAid,
|
|
30423
30861
|
gateway: {
|
|
@@ -30602,12 +31040,18 @@ var _AUNClient = class _AUNClient {
|
|
|
30602
31040
|
for (const raw of groups) {
|
|
30603
31041
|
if (!isJsonObject(raw)) continue;
|
|
30604
31042
|
const role = String(raw.role ?? raw.my_role ?? "").trim().toLowerCase();
|
|
30605
|
-
if (role !== "owner") continue;
|
|
30606
31043
|
const groupId = String(raw.group_id ?? raw.groupId ?? raw.group_aid ?? raw.groupAid ?? "").trim();
|
|
30607
31044
|
const groupAid = String(raw.group_aid ?? raw.groupAid ?? groupId).trim();
|
|
30608
31045
|
if (!groupId || !groupAid) continue;
|
|
31046
|
+
const keystore = store._keystore;
|
|
31047
|
+
const pendingTransfer = keystore && typeof keystore.loadPendingGroupBind === "function" ? await keystore.loadPendingGroupBind(`complete:${groupId}`) : null;
|
|
31048
|
+
if (role !== "owner" && !pendingTransfer) continue;
|
|
30609
31049
|
try {
|
|
30610
31050
|
await this._runGroupIdentityOperation(groupId, async () => {
|
|
31051
|
+
if (pendingTransfer) {
|
|
31052
|
+
await this.completeGroupTransfer({ group_id: groupId, group_aid: groupAid }, { aidStore: store });
|
|
31053
|
+
return;
|
|
31054
|
+
}
|
|
30611
31055
|
const loaded = await store.load(groupAid);
|
|
30612
31056
|
if (!loaded.ok || !loaded.data?.aid?.isPrivateKeyValid()) {
|
|
30613
31057
|
await this.bindGroupAid({ group_id: groupId, group_aid: groupAid }, { aidStore: store });
|
|
@@ -30660,6 +31104,36 @@ var _AUNClient = class _AUNClient {
|
|
|
30660
31104
|
if (!isNamed) {
|
|
30661
31105
|
delete payload.public_key;
|
|
30662
31106
|
delete payload.curve;
|
|
31107
|
+
const keystore2 = store._keystore;
|
|
31108
|
+
const pendingName = String(payload.name ?? "").trim().toLowerCase();
|
|
31109
|
+
const pendingNameHash = bytesToHex8(new Uint8Array(await globalThis.crypto.subtle.digest(
|
|
31110
|
+
"SHA-256",
|
|
31111
|
+
new TextEncoder().encode(pendingName)
|
|
31112
|
+
)));
|
|
31113
|
+
const pendingKey2 = `create-anonymous:${pendingNameHash}`;
|
|
31114
|
+
let keyPair2 = null;
|
|
31115
|
+
if (keystore2 && typeof keystore2.loadPendingGroupBind === "function") {
|
|
31116
|
+
keyPair2 = await keystore2.loadPendingGroupBind(pendingKey2);
|
|
31117
|
+
}
|
|
31118
|
+
if (!keyPair2 || !keyPair2.public_key_der_b64 || !keyPair2.private_key_pem) {
|
|
31119
|
+
const generated = await new CryptoProvider().generateIdentity();
|
|
31120
|
+
keyPair2 = {
|
|
31121
|
+
private_key_pem: generated.private_key_pem,
|
|
31122
|
+
public_key_der_b64: generated.public_key_der_b64,
|
|
31123
|
+
curve: generated.curve
|
|
31124
|
+
};
|
|
31125
|
+
if (keystore2 && typeof keystore2.savePendingGroupBind === "function") {
|
|
31126
|
+
await keystore2.savePendingGroupBind(pendingKey2, keyPair2);
|
|
31127
|
+
}
|
|
31128
|
+
}
|
|
31129
|
+
if (!keyPair2?.public_key_der_b64 || !keyPair2.private_key_pem) {
|
|
31130
|
+
throw new ValidationError("createGroup: failed to generate or load key pair");
|
|
31131
|
+
}
|
|
31132
|
+
const requestHash = bytesToHex8(new Uint8Array(await globalThis.crypto.subtle.digest(
|
|
31133
|
+
"SHA-256",
|
|
31134
|
+
new TextEncoder().encode(keyPair2.public_key_der_b64)
|
|
31135
|
+
)));
|
|
31136
|
+
payload._create_request_id = `anon:${requestHash}`;
|
|
30663
31137
|
const created = await this.call("group.create", payload);
|
|
30664
31138
|
const createdMap = isJsonObject(created) ? created : {};
|
|
30665
31139
|
const createdGroup = isJsonObject(createdMap.group) ? createdMap.group : {};
|
|
@@ -30667,6 +31141,9 @@ var _AUNClient = class _AUNClient {
|
|
|
30667
31141
|
if (!groupId) {
|
|
30668
31142
|
throw new ValidationError("group.create response missing group.group_id");
|
|
30669
31143
|
}
|
|
31144
|
+
if (keystore2 && typeof keystore2.savePendingGroupBind === "function") {
|
|
31145
|
+
await keystore2.savePendingGroupBind(`bind:${groupId}`, keyPair2);
|
|
31146
|
+
}
|
|
30670
31147
|
const bindParams = { group_id: groupId };
|
|
30671
31148
|
for (const key of ["name", "description", "group_description", "avatar", "group_agent_md", "groupAgentMd", "content"]) {
|
|
30672
31149
|
if (key in payload) bindParams[key] = payload[key];
|
|
@@ -30680,15 +31157,26 @@ var _AUNClient = class _AUNClient {
|
|
|
30680
31157
|
const result2 = { ...createdMap, ...boundMap, group: { ...createdGroup, ...boundGroup } };
|
|
30681
31158
|
const postprocessParams2 = { ...payload };
|
|
30682
31159
|
delete postprocessParams2._defer_group_ready_postprocess;
|
|
31160
|
+
delete postprocessParams2._create_request_id;
|
|
30683
31161
|
const processed2 = await this._groupState.postprocessResult("group.create", postprocessParams2, result2);
|
|
30684
|
-
|
|
31162
|
+
const completed = await this._inviteInitialGroupMembers(processed2, groupId, initialMembers);
|
|
31163
|
+
if (keystore2 && typeof keystore2.clearPendingGroupBind === "function") {
|
|
31164
|
+
await keystore2.clearPendingGroupBind(pendingKey2);
|
|
31165
|
+
}
|
|
31166
|
+
return completed;
|
|
30685
31167
|
}
|
|
30686
|
-
const
|
|
31168
|
+
const groupName = String(payload.group_name).trim().toLowerCase();
|
|
31169
|
+
const pendingKey = `create:${groupName}`;
|
|
30687
31170
|
const keystore = store._keystore;
|
|
30688
31171
|
let keyPair = null;
|
|
30689
31172
|
if (keystore && typeof keystore.loadPendingGroupBind === "function") {
|
|
30690
31173
|
keyPair = await keystore.loadPendingGroupBind(pendingKey);
|
|
30691
31174
|
}
|
|
31175
|
+
if ((!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) && keystore && typeof keystore.loadIdentity === "function") {
|
|
31176
|
+
const ownerAid = String(this._aid ?? "").trim();
|
|
31177
|
+
const dot = ownerAid.indexOf(".");
|
|
31178
|
+
if (dot >= 0) keyPair = await keystore.loadIdentity(`${groupName}.${ownerAid.slice(dot + 1).toLowerCase()}`);
|
|
31179
|
+
}
|
|
30692
31180
|
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
30693
31181
|
const generated = await new CryptoProvider().generateIdentity();
|
|
30694
31182
|
keyPair = {
|
|
@@ -32785,8 +33273,8 @@ ${invitee}` : "";
|
|
|
32785
33273
|
*
|
|
32786
33274
|
* @param upToSeq 确认到此 seq;省略则用当前 contiguous
|
|
32787
33275
|
*/
|
|
32788
|
-
async _ackV2(upToSeq, inlineGeneration) {
|
|
32789
|
-
return await this._v2E2EE.ackV2(upToSeq, inlineGeneration);
|
|
33276
|
+
async _ackV2(upToSeq, inlineGeneration, volatileCursors = [], volatileIds = []) {
|
|
33277
|
+
return await this._v2E2EE.ackV2(upToSeq, inlineGeneration, volatileCursors, volatileIds);
|
|
32790
33278
|
}
|
|
32791
33279
|
/**
|
|
32792
33280
|
* 解密单条 V2 消息(与 Python `_decrypt_v2_message` 对齐)。
|
|
@@ -32878,7 +33366,6 @@ ${invitee}` : "";
|
|
|
32878
33366
|
seq: msg.seq,
|
|
32879
33367
|
timestamp: msg.t_server ?? msg.timestamp,
|
|
32880
33368
|
device_id: String(msg.device_id ?? ""),
|
|
32881
|
-
slot_id: String(msg.slot_id ?? ""),
|
|
32882
33369
|
_decrypt_error: String(exc),
|
|
32883
33370
|
_decrypt_stage: "spk_lookup",
|
|
32884
33371
|
_envelope_type: String(envelope.type ?? ""),
|
|
@@ -32921,7 +33408,6 @@ ${invitee}` : "";
|
|
|
32921
33408
|
seq: msg.seq,
|
|
32922
33409
|
timestamp: msg.t_server ?? msg.timestamp,
|
|
32923
33410
|
device_id: String(msg.device_id ?? ""),
|
|
32924
|
-
slot_id: String(msg.slot_id ?? ""),
|
|
32925
33411
|
_decrypt_error: "sender_ik_not_found",
|
|
32926
33412
|
_decrypt_stage: "sender_ik",
|
|
32927
33413
|
_envelope_type: String(envelope.type ?? ""),
|
|
@@ -32958,7 +33444,6 @@ ${invitee}` : "";
|
|
|
32958
33444
|
seq: msg.seq,
|
|
32959
33445
|
timestamp: msg.t_server ?? msg.timestamp,
|
|
32960
33446
|
device_id: String(msg.device_id ?? ""),
|
|
32961
|
-
slot_id: String(msg.slot_id ?? ""),
|
|
32962
33447
|
_decrypt_error: String(exc),
|
|
32963
33448
|
_decrypt_stage: "decrypt",
|
|
32964
33449
|
_envelope_type: String(envelope.type ?? ""),
|
|
@@ -32985,10 +33470,10 @@ ${invitee}` : "";
|
|
|
32985
33470
|
encrypted: true,
|
|
32986
33471
|
e2ee
|
|
32987
33472
|
};
|
|
33473
|
+
if (senderDeviceId) result.sender_device_id = senderDeviceId;
|
|
32988
33474
|
const explicitDirection = String(msg.direction ?? "").trim();
|
|
32989
33475
|
result.direction = explicitDirection || (fromAid && fromAid === this._aid ? "outbound_sync" : "inbound");
|
|
32990
33476
|
if (msg.device_id !== void 0) result.device_id = msg.device_id;
|
|
32991
|
-
if (msg.slot_id !== void 0) result.slot_id = msg.slot_id;
|
|
32992
33477
|
attachGatewayProximity2(result, msg);
|
|
32993
33478
|
attachV2EnvelopeMetadata2(result, e2ee);
|
|
32994
33479
|
if (groupIdForKeys) result = normalizeGroupMentionMode(result, msg, envelope);
|