@el-j/google-sheet-translations 3.0.0-beta.8 → 3.0.0-beta.9
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/dist/esm/index.js +174 -44
- package/dist/index.js +174 -44
- package/dist/migration/migrateV3.d.ts +3 -0
- package/dist/migration/migrateV3.d.ts.map +1 -1
- package/dist/providers/cryptpad/client.d.ts +2 -1
- package/dist/providers/cryptpad/client.d.ts.map +1 -1
- package/dist/providers/cryptpad/crypto.d.ts +8 -4
- package/dist/providers/cryptpad/crypto.d.ts.map +1 -1
- package/dist/providers/cryptpad/netflux.d.ts +1 -0
- package/dist/providers/cryptpad/netflux.d.ts.map +1 -1
- package/dist/providers/cryptpad/sheetOutputProvider.d.ts.map +1 -1
- package/dist/providers/cryptpad/sheetParser.d.ts +4 -2
- package/dist/providers/cryptpad/sheetParser.d.ts.map +1 -1
- package/dist/providers/google/providers.d.ts +1 -0
- package/dist/providers/google/providers.d.ts.map +1 -1
- package/dist/providers/syncEngine.d.ts +1 -0
- package/dist/providers/syncEngine.d.ts.map +1 -1
- package/dist-cli/cryptpad.mjs +190 -47
- package/dist-cli/run-provider.mjs +174 -44
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -3299,11 +3299,13 @@ function parsePadUrl(url) {
|
|
|
3299
3299
|
const match = urlObj.hash.match(/#\/(\d+)\/([^/]+)\/([^/]+)\/([^/]+)(\/p\/)?/);
|
|
3300
3300
|
if (!match) throw new Error(`Unsupported CryptPad URL format: "${url}". Expected "#/<version>/<app>/<mode>/<seed>[/p/]"`);
|
|
3301
3301
|
const [, versionStr, app, modeRaw, seed, passwordFlag] = match;
|
|
3302
|
+
const version = parseInt(versionStr, 10);
|
|
3303
|
+
if (version === 3) throw new Error(`CryptPad URL "${url}" is a version 3 "safe link" (hidden hash) which omits the encryption key to protect it in browser address bars. Please use the original pad share URL (version 2 format: "https://.../#/2/${app}/${modeRaw}/<secret>/..."), which can be copied via CryptPad's "Share" -> "Link" menu.`);
|
|
3302
3304
|
const mode = modeRaw === "edit" ? "edit" : "view";
|
|
3303
3305
|
const isPasswordProtected = Boolean(passwordFlag);
|
|
3304
3306
|
return {
|
|
3305
3307
|
origin: urlObj.origin,
|
|
3306
|
-
version
|
|
3308
|
+
version,
|
|
3307
3309
|
app,
|
|
3308
3310
|
mode,
|
|
3309
3311
|
seed,
|
|
@@ -3312,27 +3314,36 @@ function parsePadUrl(url) {
|
|
|
3312
3314
|
};
|
|
3313
3315
|
}
|
|
3314
3316
|
/**
|
|
3315
|
-
* Derives the Netflux channel ID (hex string)
|
|
3316
|
-
* from a CryptPad pad seed and optional password
|
|
3317
|
+
* Derives the Netflux channel ID (hex string), symmetric encryption key (32 bytes),
|
|
3318
|
+
* and Ed25519 signing keypair from a CryptPad pad seed and optional password
|
|
3319
|
+
* according to CryptPad's createEditCryptor2 / createViewCryptor2 protocol.
|
|
3317
3320
|
*/
|
|
3318
3321
|
function deriveCryptPadKeys(seedStr, password) {
|
|
3319
3322
|
const seed = b64Decode(seedStr);
|
|
3320
3323
|
if (!password) {
|
|
3321
|
-
const
|
|
3324
|
+
const hash = crypto.createHash("sha512").update(seed).digest();
|
|
3325
|
+
const signKp = nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
|
|
3326
|
+
const seed2 = hash.subarray(32, 64);
|
|
3322
3327
|
const hash2 = crypto.createHash("sha512").update(seed2).digest();
|
|
3323
3328
|
return {
|
|
3324
3329
|
channelHex: hash2.subarray(0, 16).toString("hex"),
|
|
3325
|
-
cryptKey: new Uint8Array(hash2.subarray(16, 48))
|
|
3330
|
+
cryptKey: new Uint8Array(hash2.subarray(16, 48)),
|
|
3331
|
+
signKey: signKp.secretKey,
|
|
3332
|
+
validateKey: signKp.publicKey
|
|
3326
3333
|
};
|
|
3327
3334
|
}
|
|
3328
3335
|
const pwBytes = decodeUTF8(password);
|
|
3329
3336
|
const superSeed1 = Buffer.concat([Buffer.from(pwBytes), Buffer.from(seed)]);
|
|
3330
|
-
const
|
|
3337
|
+
const hash1 = crypto.createHash("sha512").update(superSeed1).digest();
|
|
3338
|
+
const signKp = nacl.sign.keyPair.fromSeed(hash1.subarray(0, 32));
|
|
3339
|
+
const seed2 = hash1.subarray(32, 64);
|
|
3331
3340
|
const superSeed2 = Buffer.concat([Buffer.from(pwBytes), Buffer.from(seed2)]);
|
|
3332
3341
|
const hash2 = crypto.createHash("sha512").update(superSeed2).digest();
|
|
3333
3342
|
return {
|
|
3334
3343
|
channelHex: hash2.subarray(0, 16).toString("hex"),
|
|
3335
|
-
cryptKey: new Uint8Array(hash2.subarray(16, 48))
|
|
3344
|
+
cryptKey: new Uint8Array(hash2.subarray(16, 48)),
|
|
3345
|
+
signKey: signKp.secretKey,
|
|
3346
|
+
validateKey: signKp.publicKey
|
|
3336
3347
|
};
|
|
3337
3348
|
}
|
|
3338
3349
|
/**
|
|
@@ -3363,13 +3374,16 @@ function decryptCryptPadPayload(payload, cryptKey) {
|
|
|
3363
3374
|
}
|
|
3364
3375
|
/**
|
|
3365
3376
|
* Encrypts a plaintext message payload using TweetNaCl secretbox (XSalsa20-Poly1305).
|
|
3366
|
-
*
|
|
3377
|
+
* If signKey is provided, signs the `nonce|ciphertext` string using TweetNaCl Ed25519
|
|
3378
|
+
* matching OnlyOffice / CryptPad real-time channel requirements.
|
|
3367
3379
|
*/
|
|
3368
|
-
function encryptCryptPadPayload(plaintext, cryptKey) {
|
|
3380
|
+
function encryptCryptPadPayload(plaintext, cryptKey, signKey) {
|
|
3369
3381
|
const nonce = nacl.randomBytes(24);
|
|
3370
3382
|
const msgBytes = decodeUTF8(plaintext);
|
|
3371
3383
|
const cipher = nacl.secretbox(msgBytes, nonce, cryptKey);
|
|
3372
|
-
|
|
3384
|
+
const encStr = `${b64Encode(nonce)}|${b64Encode(cipher)}`;
|
|
3385
|
+
if (signKey) return b64Encode(nacl.sign(decodeUTF8(encStr), signKey));
|
|
3386
|
+
return encStr;
|
|
3373
3387
|
}
|
|
3374
3388
|
//#endregion
|
|
3375
3389
|
//#region src/providers/cryptpad/netflux.ts
|
|
@@ -3462,11 +3476,7 @@ function fetchChannelHistory(wsUrl, channelHex, cryptKey, options = {}) {
|
|
|
3462
3476
|
seq++,
|
|
3463
3477
|
"MSG",
|
|
3464
3478
|
peerId,
|
|
3465
|
-
JSON.stringify([
|
|
3466
|
-
"GET_HISTORY",
|
|
3467
|
-
channelHex,
|
|
3468
|
-
{}
|
|
3469
|
-
])
|
|
3479
|
+
JSON.stringify(["GET_FULL_HISTORY", channelHex])
|
|
3470
3480
|
]));
|
|
3471
3481
|
resetQuietTimer();
|
|
3472
3482
|
}
|
|
@@ -3476,7 +3486,11 @@ function fetchChannelHistory(wsUrl, channelHex, cryptKey, options = {}) {
|
|
|
3476
3486
|
if (!encStr && typeof msg[3] === "string" && msg[3].length > 50) encStr = msg[3];
|
|
3477
3487
|
try {
|
|
3478
3488
|
const parsed = JSON.parse(encStr);
|
|
3479
|
-
if (Array.isArray(parsed) && parsed[
|
|
3489
|
+
if (Array.isArray(parsed) && parsed[0] === "FULL_HISTORY" && Array.isArray(parsed[1])) encStr = parsed[1][4];
|
|
3490
|
+
else if (Array.isArray(parsed) && parsed[0] === "FULL_HISTORY_END") {
|
|
3491
|
+
finish(decryptedMessages);
|
|
3492
|
+
return;
|
|
3493
|
+
} else if (Array.isArray(parsed) && parsed[2] === "MSG" && typeof parsed[4] === "string") encStr = parsed[4];
|
|
3480
3494
|
} catch {}
|
|
3481
3495
|
if (typeof encStr === "string" && encStr.length > 30) {
|
|
3482
3496
|
const decrypted = decryptCryptPadPayload(encStr, cryptKey);
|
|
@@ -3490,7 +3504,9 @@ function fetchChannelHistory(wsUrl, channelHex, cryptKey, options = {}) {
|
|
|
3490
3504
|
};
|
|
3491
3505
|
ws.onerror = (err) => {
|
|
3492
3506
|
cleanup();
|
|
3493
|
-
|
|
3507
|
+
const errObj = err;
|
|
3508
|
+
const detail = errObj?.message || errObj?.error?.message || (errObj?.error ? String(errObj.error) : String(err));
|
|
3509
|
+
reject(/* @__PURE__ */ new Error(`CryptPad WebSocket error: ${detail}`));
|
|
3494
3510
|
};
|
|
3495
3511
|
});
|
|
3496
3512
|
}
|
|
@@ -3553,7 +3569,7 @@ function broadcastChannelMessage(wsUrl, channelHex, cryptKey, message, options =
|
|
|
3553
3569
|
const [, peerId, cmd] = msg;
|
|
3554
3570
|
if (cmd === "JOIN" && typeof peerId === "string" && !sent) {
|
|
3555
3571
|
sent = true;
|
|
3556
|
-
const encrypted = encryptCryptPadPayload(message, cryptKey);
|
|
3572
|
+
const encrypted = encryptCryptPadPayload(message, cryptKey, options.signKey);
|
|
3557
3573
|
ws.send(JSON.stringify([
|
|
3558
3574
|
seq++,
|
|
3559
3575
|
"MSG",
|
|
@@ -3567,7 +3583,9 @@ function broadcastChannelMessage(wsUrl, channelHex, cryptKey, message, options =
|
|
|
3567
3583
|
} catch {}
|
|
3568
3584
|
};
|
|
3569
3585
|
ws.onerror = (err) => {
|
|
3570
|
-
|
|
3586
|
+
const errObj = err;
|
|
3587
|
+
const detail = errObj?.message || errObj?.error?.message || (errObj?.error ? String(errObj.error) : String(err));
|
|
3588
|
+
rejectOnce(/* @__PURE__ */ new Error(`CryptPad WebSocket broadcast error: ${detail}`));
|
|
3571
3589
|
};
|
|
3572
3590
|
});
|
|
3573
3591
|
}
|
|
@@ -3583,6 +3601,12 @@ function colIndexToLetter(colIndex) {
|
|
|
3583
3601
|
}
|
|
3584
3602
|
return letter;
|
|
3585
3603
|
}
|
|
3604
|
+
/** Converts column letter to 0-based index: 'A' -> 0, 'Z' -> 25, 'AA' -> 26. */
|
|
3605
|
+
function letterToColIndex(letter) {
|
|
3606
|
+
let index = 0;
|
|
3607
|
+
for (let i = 0; i < letter.length; i++) index = index * 26 + (letter.charCodeAt(i) - 64);
|
|
3608
|
+
return index - 1;
|
|
3609
|
+
}
|
|
3586
3610
|
/**
|
|
3587
3611
|
* Parses cell coordinates from a reference like "A1", "Sheet1!B2", or "$C$5".
|
|
3588
3612
|
*/
|
|
@@ -3601,14 +3625,26 @@ function parseCellRef(ref) {
|
|
|
3601
3625
|
function extractOnlyOfficeChannelId(metadataMessages) {
|
|
3602
3626
|
for (const raw of metadataMessages) try {
|
|
3603
3627
|
const parsed = JSON.parse(raw);
|
|
3628
|
+
if (parsed?.content?.channel && typeof parsed.content.channel === "string") return parsed.content.channel;
|
|
3604
3629
|
if (Array.isArray(parsed) && Array.isArray(parsed[1])) {
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3630
|
+
const candidateContainers = [parsed[1], Array.isArray(parsed[1][0]) ? parsed[1][0] : null].filter(Boolean);
|
|
3631
|
+
for (const container of candidateContainers) for (const item of container) if (Array.isArray(item)) {
|
|
3632
|
+
for (const elem of item) if (typeof elem === "string" && elem.includes("channel")) try {
|
|
3633
|
+
const inner = JSON.parse(elem);
|
|
3634
|
+
if (inner?.content?.channel && typeof inner.content.channel === "string") return inner.content.channel;
|
|
3635
|
+
} catch {}
|
|
3636
|
+
}
|
|
3609
3637
|
}
|
|
3610
|
-
if (
|
|
3611
|
-
|
|
3638
|
+
if (typeof raw === "string") {
|
|
3639
|
+
const match = raw.match(/\\?"channel\\?"\s*:\s*\\?"([a-f0-9]{32})\\?"/i);
|
|
3640
|
+
if (match) return match[1];
|
|
3641
|
+
}
|
|
3642
|
+
} catch {
|
|
3643
|
+
if (typeof raw === "string") {
|
|
3644
|
+
const match = raw.match(/\\?"channel\\?"\s*:\s*\\?"([a-f0-9]{32})\\?"/i);
|
|
3645
|
+
if (match) return match[1];
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3612
3648
|
return null;
|
|
3613
3649
|
}
|
|
3614
3650
|
/**
|
|
@@ -3666,12 +3702,20 @@ function parseOnlyOfficeChanges(rtMessages) {
|
|
|
3666
3702
|
break;
|
|
3667
3703
|
}
|
|
3668
3704
|
}
|
|
3669
|
-
if (buf.length >=
|
|
3705
|
+
if (buf.length >= 31 && buf.readUInt32BE(4) === 19468289 && buf[14] === 1) try {
|
|
3706
|
+
const c1 = buf.readUInt32LE(15);
|
|
3707
|
+
const r1 = buf.readUInt32LE(19);
|
|
3708
|
+
if (r1 < 1e5 && c1 < 200) {
|
|
3709
|
+
const cellRef = `${colIndexToLetter(c1)}${r1 + 1}`;
|
|
3710
|
+
if (str.trim().length > 0 && !str.includes("!")) cells[cellRef] = str;
|
|
3711
|
+
}
|
|
3712
|
+
} catch {}
|
|
3713
|
+
else if (buf.length >= 30 && i >= 40 && buf.readUInt32BE(4) === 0) try {
|
|
3670
3714
|
const c1 = buf.readUInt32LE(14);
|
|
3671
3715
|
const r1 = buf.readUInt32LE(18);
|
|
3672
3716
|
if (r1 < 1e5 && c1 < 200) {
|
|
3673
3717
|
const cellRef = `${colIndexToLetter(c1)}${r1 + 1}`;
|
|
3674
|
-
if (
|
|
3718
|
+
if (str.trim().length > 0 && !str.includes("!")) cells[cellRef] = str;
|
|
3675
3719
|
}
|
|
3676
3720
|
} catch {}
|
|
3677
3721
|
}
|
|
@@ -3740,11 +3784,70 @@ function convertCellsToMultiSheetRows(cells, defaultSheet = "Sheet1") {
|
|
|
3740
3784
|
return result;
|
|
3741
3785
|
}
|
|
3742
3786
|
/**
|
|
3743
|
-
* Encodes a single cell update record into OnlyOffice binary format
|
|
3787
|
+
* Encodes a single cell update record into OnlyOffice binary format:
|
|
3788
|
+
* - Native AscCH.historyitem_Cell_ChangeValue (0x01291001) with 32-bit LE coordinates c1, r1.
|
|
3789
|
+
* - Explicit UTF-16LE cell reference tags for backward-compatibility and multi-sheet routing.
|
|
3744
3790
|
*/
|
|
3745
|
-
function encodeOnlyOfficeCellRecord(cellRef, value) {
|
|
3746
|
-
|
|
3791
|
+
function encodeOnlyOfficeCellRecord(cellRef, value, sheetId = 6) {
|
|
3792
|
+
let c1 = 0;
|
|
3793
|
+
let r1 = 0;
|
|
3794
|
+
const parsed = parseCellRef(cellRef);
|
|
3795
|
+
if (parsed) {
|
|
3796
|
+
c1 = letterToColIndex(parsed.col);
|
|
3797
|
+
r1 = Math.max(0, parsed.row - 1);
|
|
3798
|
+
}
|
|
3747
3799
|
const valBuf = Buffer.from(value, "utf16le");
|
|
3800
|
+
const L = valBuf.length;
|
|
3801
|
+
const tail = Buffer.from([
|
|
3802
|
+
1,
|
|
3803
|
+
0,
|
|
3804
|
+
2,
|
|
3805
|
+
0,
|
|
3806
|
+
3,
|
|
3807
|
+
2,
|
|
3808
|
+
1,
|
|
3809
|
+
2,
|
|
3810
|
+
0,
|
|
3811
|
+
3,
|
|
3812
|
+
1
|
|
3813
|
+
]);
|
|
3814
|
+
const body = Buffer.alloc(60 + L + tail.length);
|
|
3815
|
+
body.writeUInt32BE(19468289, 0);
|
|
3816
|
+
body.writeUInt32LE(2, 4);
|
|
3817
|
+
body[8] = 48 + sheetId % 10;
|
|
3818
|
+
body[9] = 0;
|
|
3819
|
+
body[10] = 1;
|
|
3820
|
+
body.writeUInt32LE(c1, 11);
|
|
3821
|
+
body.writeUInt32LE(r1, 15);
|
|
3822
|
+
body.writeUInt32LE(c1, 19);
|
|
3823
|
+
body.writeUInt32LE(r1, 23);
|
|
3824
|
+
body[27] = 0;
|
|
3825
|
+
body.writeUInt32LE(39 + L, 28);
|
|
3826
|
+
body[32] = 0;
|
|
3827
|
+
body[33] = 2;
|
|
3828
|
+
body[34] = r1 & 255;
|
|
3829
|
+
body[35] = 1;
|
|
3830
|
+
body[36] = 2;
|
|
3831
|
+
body[37] = c1 & 255;
|
|
3832
|
+
body[38] = 2;
|
|
3833
|
+
body[39] = 9;
|
|
3834
|
+
body[40] = 3;
|
|
3835
|
+
body.writeUInt32LE(26 + L, 41);
|
|
3836
|
+
body[45] = 0;
|
|
3837
|
+
body[46] = 0;
|
|
3838
|
+
body[47] = 1;
|
|
3839
|
+
body[48] = 9;
|
|
3840
|
+
body[49] = 1;
|
|
3841
|
+
body.writeUInt32LE(13 + L, 50);
|
|
3842
|
+
body[54] = 0;
|
|
3843
|
+
body[55] = 8;
|
|
3844
|
+
body.writeUInt32LE(L, 56);
|
|
3845
|
+
valBuf.copy(body, 60);
|
|
3846
|
+
tail.copy(body, 60 + L);
|
|
3847
|
+
const header = Buffer.alloc(4);
|
|
3848
|
+
header.writeUInt32LE(body.length, 0);
|
|
3849
|
+
const nativeBuf = Buffer.concat([header, body]);
|
|
3850
|
+
const refBuf = Buffer.from(cellRef, "utf16le");
|
|
3748
3851
|
const refHeader = Buffer.alloc(5);
|
|
3749
3852
|
refHeader[0] = 8;
|
|
3750
3853
|
refHeader.writeUInt32LE(refBuf.length, 1);
|
|
@@ -3752,6 +3855,7 @@ function encodeOnlyOfficeCellRecord(cellRef, value) {
|
|
|
3752
3855
|
valHeader[0] = 8;
|
|
3753
3856
|
valHeader.writeUInt32LE(valBuf.length, 1);
|
|
3754
3857
|
return Buffer.concat([
|
|
3858
|
+
nativeBuf,
|
|
3755
3859
|
refHeader,
|
|
3756
3860
|
refBuf,
|
|
3757
3861
|
valHeader,
|
|
@@ -3762,12 +3866,19 @@ function encodeOnlyOfficeCellRecord(cellRef, value) {
|
|
|
3762
3866
|
* Formats a list of cell updates into an OnlyOffice change transaction JSON string.
|
|
3763
3867
|
*/
|
|
3764
3868
|
function buildOnlyOfficeChangePayload(updates) {
|
|
3869
|
+
const txOpen = Buffer.from("0a0000000129000000ff00000000", "hex");
|
|
3765
3870
|
const records = updates.map((u) => {
|
|
3766
3871
|
const colStr = typeof u.col === "number" ? colIndexToLetter(u.col) : u.col.toUpperCase();
|
|
3767
3872
|
return encodeOnlyOfficeCellRecord(`${u.sheet && u.sheet.trim().length > 0 ? `${u.sheet.trim()}!` : ""}${colStr}${u.row}`, u.value);
|
|
3768
3873
|
});
|
|
3769
|
-
const changeEntry = `asc_1;${Buffer.concat(records).toString("base64")}`;
|
|
3770
|
-
return JSON.stringify({
|
|
3874
|
+
const changeEntry = `asc_1;${Buffer.concat([txOpen, ...records]).toString("base64")}`;
|
|
3875
|
+
return JSON.stringify({
|
|
3876
|
+
type: "saveChanges",
|
|
3877
|
+
changes: [{ change: changeEntry }],
|
|
3878
|
+
startSaveChanges: true,
|
|
3879
|
+
endSaveChanges: true,
|
|
3880
|
+
isExcel: true
|
|
3881
|
+
});
|
|
3771
3882
|
}
|
|
3772
3883
|
//#endregion
|
|
3773
3884
|
//#region src/providers/cryptpad/client.ts
|
|
@@ -3829,12 +3940,26 @@ var CryptPadClient = class {
|
|
|
3829
3940
|
const wsUrl = await this.getWebsocketUrl(signal);
|
|
3830
3941
|
const { channelHex, cryptKey } = this.getKeys();
|
|
3831
3942
|
const newRtChannel = crypto.randomBytes(16).toString("hex");
|
|
3832
|
-
const innerJson = JSON.stringify({
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3943
|
+
const innerJson = JSON.stringify({
|
|
3944
|
+
content: {
|
|
3945
|
+
hashes: {},
|
|
3946
|
+
ids: {},
|
|
3947
|
+
mediasSources: {},
|
|
3948
|
+
originalVersion: 9,
|
|
3949
|
+
version: 9,
|
|
3950
|
+
channel: newRtChannel
|
|
3951
|
+
},
|
|
3952
|
+
metadata: { type: "oo" }
|
|
3953
|
+
});
|
|
3954
|
+
await broadcastChannelMessage(wsUrl, channelHex, cryptKey, JSON.stringify([
|
|
3955
|
+
2,
|
|
3956
|
+
[[[
|
|
3957
|
+
0,
|
|
3958
|
+
0,
|
|
3959
|
+
innerJson
|
|
3960
|
+
]], "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"],
|
|
3961
|
+
"a4b411975be1d48a91f0ebfcd967add000f69564b2dc5ee644b87bf2bbfd786f"
|
|
3962
|
+
]), {
|
|
3838
3963
|
timeoutMs: this.timeoutMs,
|
|
3839
3964
|
signal
|
|
3840
3965
|
});
|
|
@@ -3903,10 +4028,12 @@ var CryptPadClient = class {
|
|
|
3903
4028
|
*
|
|
3904
4029
|
* @param updates - Array of cell updates containing coordinates and text values.
|
|
3905
4030
|
* @param signal - Optional AbortSignal to cancel the broadcast.
|
|
4031
|
+
* @param existingRtChannel - Optional pre-fetched RT channel ID to avoid refetching sheet data.
|
|
3906
4032
|
*/
|
|
3907
|
-
async sendCellUpdates(updates, signal) {
|
|
4033
|
+
async sendCellUpdates(updates, signal, existingRtChannel) {
|
|
3908
4034
|
if (updates.length === 0) return;
|
|
3909
|
-
let rtChannel =
|
|
4035
|
+
let rtChannel = existingRtChannel;
|
|
4036
|
+
if (!rtChannel) rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
|
|
3910
4037
|
if (!rtChannel) {
|
|
3911
4038
|
this.onProgress?.("No OnlyOffice RT channel found. Initializing headlessly (no browser required)...");
|
|
3912
4039
|
rtChannel = await this.initializeRtChannel(signal);
|
|
@@ -3914,11 +4041,12 @@ var CryptPadClient = class {
|
|
|
3914
4041
|
await new Promise((r) => setTimeout(r, 800));
|
|
3915
4042
|
}
|
|
3916
4043
|
const wsUrl = await this.getWebsocketUrl(signal);
|
|
3917
|
-
const { cryptKey } = this.getKeys();
|
|
4044
|
+
const { cryptKey, signKey } = this.getKeys();
|
|
3918
4045
|
const payload = buildOnlyOfficeChangePayload(updates);
|
|
3919
4046
|
await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, payload, {
|
|
3920
4047
|
timeoutMs: this.timeoutMs,
|
|
3921
|
-
signal
|
|
4048
|
+
signal,
|
|
4049
|
+
signKey
|
|
3922
4050
|
});
|
|
3923
4051
|
}
|
|
3924
4052
|
/**
|
|
@@ -3931,7 +4059,8 @@ var CryptPadClient = class {
|
|
|
3931
4059
|
*/
|
|
3932
4060
|
async writeSheetRows(sheetName, rows, options = {}) {
|
|
3933
4061
|
if (rows.length === 0) return 0;
|
|
3934
|
-
const
|
|
4062
|
+
const data = await this.fetchSheetData(options.signal);
|
|
4063
|
+
const existingRows = data.sheets[sheetName]?.rows ?? [];
|
|
3935
4064
|
let keyColName = "var";
|
|
3936
4065
|
const firstExisting = existingRows[0];
|
|
3937
4066
|
const firstIncoming = rows[0];
|
|
@@ -3980,7 +4109,7 @@ var CryptPadClient = class {
|
|
|
3980
4109
|
}
|
|
3981
4110
|
}
|
|
3982
4111
|
}
|
|
3983
|
-
await this.sendCellUpdates(updates, options.signal);
|
|
4112
|
+
await this.sendCellUpdates(updates, options.signal, data.metadata.rtChannelId);
|
|
3984
4113
|
return updates.length;
|
|
3985
4114
|
}
|
|
3986
4115
|
};
|
|
@@ -4113,6 +4242,7 @@ function createCryptPadSheetOutputProvider(options = {}) {
|
|
|
4113
4242
|
const count = await client.writeSheetRows(sheetName, rows, { override: options.override ?? false });
|
|
4114
4243
|
updatedSheets.push(sheetName);
|
|
4115
4244
|
totalUpdatedCells += count;
|
|
4245
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
4116
4246
|
}
|
|
4117
4247
|
return {
|
|
4118
4248
|
wroteFiles: [url],
|