@el-j/google-sheet-translations 3.0.0-beta.13 → 3.0.0-beta.14
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/core/rowTransformer.d.ts.map +1 -1
- package/dist/esm/index.js +405 -50
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +419 -60
- package/dist/providers/cryptpad/client.d.ts +23 -0
- package/dist/providers/cryptpad/client.d.ts.map +1 -1
- package/dist/providers/cryptpad/index.d.ts +1 -1
- package/dist/providers/cryptpad/index.d.ts.map +1 -1
- package/dist/providers/cryptpad/netflux.d.ts +4 -0
- package/dist/providers/cryptpad/netflux.d.ts.map +1 -1
- package/dist/providers/cryptpad/sheetOutputProvider.d.ts +4 -1
- package/dist/providers/cryptpad/sheetOutputProvider.d.ts.map +1 -1
- package/dist/providers/cryptpad/sheetParser.d.ts +27 -0
- package/dist/providers/cryptpad/sheetParser.d.ts.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/utils/dataConverter/findLocalChanges.d.ts.map +1 -1
- package/dist/utils/translationMerge.d.ts +18 -0
- package/dist/utils/translationMerge.d.ts.map +1 -0
- package/dist-cli/cryptpad.mjs +450 -57
- package/dist-cli/migrate-v3.mjs +0 -0
- package/dist-cli/run-provider.mjs +404 -49
- package/dist-cli/setup-wif.mjs +0 -0
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rowTransformer.d.ts","sourceRoot":"","sources":["../../src/core/rowTransformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,0BAA0B;IACzC,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACzE,mBAAmB,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,mBAAmB,CAAC;IAC3F,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,CAAC;CAClD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,QAAQ,EAAE,EAChB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,0BAA0B,GAC/B,qBAAqB,
|
|
1
|
+
{"version":3,"file":"rowTransformer.d.ts","sourceRoot":"","sources":["../../src/core/rowTransformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,0BAA0B;IACzC,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IACzE,mBAAmB,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,mBAAmB,CAAC;IAC3F,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,CAAC;CAClD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,QAAQ,EAAE,EAChB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,0BAA0B,GAC/B,qBAAqB,CAqFvB"}
|
package/dist/esm/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import fs$1 from "node:fs/promises";
|
|
|
12
12
|
import { parse } from "csv-parse/sync";
|
|
13
13
|
import crypto from "node:crypto";
|
|
14
14
|
import nacl from "tweetnacl";
|
|
15
|
+
import { WebSocket } from "ws";
|
|
15
16
|
//#region \0rolldown/runtime.js
|
|
16
17
|
var __create = Object.create;
|
|
17
18
|
var __defProp = Object.defineProperty;
|
|
@@ -224,9 +225,9 @@ function transformRowsToSheetData(rows, sheetTitle, deps) {
|
|
|
224
225
|
if (!originalHeader) continue;
|
|
225
226
|
const nonEmptyLanguageCells = rows.map((row) => {
|
|
226
227
|
const keyField = Object.keys(row).find((k) => k.toLowerCase() === keyColumn);
|
|
227
|
-
if (!keyField || !row[keyField]
|
|
228
|
+
if (!keyField || !row[keyField]) return {};
|
|
228
229
|
const rowLocal = {};
|
|
229
|
-
rowLocal[row[keyField].toString().toLowerCase()] = row[originalHeader];
|
|
230
|
+
rowLocal[row[keyField].toString().toLowerCase()] = row[originalHeader] ?? "";
|
|
230
231
|
return rowLocal;
|
|
231
232
|
}).filter((cell) => Object.keys(cell).length > 0);
|
|
232
233
|
const prepareObj = {};
|
|
@@ -737,7 +738,7 @@ function findLocalChanges(localData, spreadsheetData) {
|
|
|
737
738
|
for (const sheet of Object.keys(localData[locale])) {
|
|
738
739
|
if (!localData[locale][sheet]) continue;
|
|
739
740
|
if (sheet === "i18n") continue;
|
|
740
|
-
for (const key of Object.keys(localData[locale][sheet])) if (!resolvedLocale || !spreadsheetData[resolvedLocale]?.[sheet] ||
|
|
741
|
+
for (const key of Object.keys(localData[locale][sheet])) if (!resolvedLocale || !spreadsheetData[resolvedLocale]?.[sheet] || spreadsheetData[resolvedLocale][sheet][key] === void 0) {
|
|
741
742
|
if (!changes[locale]) changes[locale] = {};
|
|
742
743
|
if (!changes[locale][sheet]) changes[locale][sheet] = {};
|
|
743
744
|
changes[locale][sheet][key] = localData[locale][sheet][key];
|
|
@@ -3428,6 +3429,49 @@ function encryptCryptPadPayload(plaintext, cryptKey, signKey) {
|
|
|
3428
3429
|
}
|
|
3429
3430
|
//#endregion
|
|
3430
3431
|
//#region src/providers/cryptpad/netflux.ts
|
|
3432
|
+
function getWebSocketImpl() {
|
|
3433
|
+
if (typeof window !== "undefined" && window.WebSocket) return window.WebSocket;
|
|
3434
|
+
if (typeof process !== "undefined" && process.env.VITEST && typeof globalThis !== "undefined" && globalThis.WebSocket) return globalThis.WebSocket;
|
|
3435
|
+
return WebSocket;
|
|
3436
|
+
}
|
|
3437
|
+
function formatUnknownError(err) {
|
|
3438
|
+
const errObj = err;
|
|
3439
|
+
const details = [];
|
|
3440
|
+
if (errObj?.message) details.push(`message=${errObj.message}`);
|
|
3441
|
+
if (errObj?.error) {
|
|
3442
|
+
if (typeof errObj.error === "string") details.push(`inner=${errObj.error}`);
|
|
3443
|
+
else {
|
|
3444
|
+
const inner = errObj.error;
|
|
3445
|
+
if (inner.name) details.push(`innerName=${inner.name}`);
|
|
3446
|
+
if (inner.message) details.push(`innerMessage=${inner.message}`);
|
|
3447
|
+
if (inner.cause !== void 0) details.push(`innerCause=${String(inner.cause)}`);
|
|
3448
|
+
if (inner.stack) details.push(`innerStack=${inner.stack}`);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
if (err && typeof err === "object") {
|
|
3452
|
+
const e = err;
|
|
3453
|
+
if (e.type) details.push(`eventType=${e.type}`);
|
|
3454
|
+
const keys = Object.keys(e);
|
|
3455
|
+
if (keys.length > 0) details.push(`eventKeys=${keys.join(",")}`);
|
|
3456
|
+
}
|
|
3457
|
+
if (details.length > 0) return details.join(" | ");
|
|
3458
|
+
return String(err);
|
|
3459
|
+
}
|
|
3460
|
+
function shouldRetryBroadcastError(err) {
|
|
3461
|
+
const message = formatUnknownError(err).toLowerCase();
|
|
3462
|
+
return message.includes("unexpected server response: 429") || message.includes("server response: 429") || message.includes("too many requests");
|
|
3463
|
+
}
|
|
3464
|
+
function wait$1(ms) {
|
|
3465
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3466
|
+
}
|
|
3467
|
+
function withAttemptContext(err, attempt, maxAttempts) {
|
|
3468
|
+
if (err instanceof Error) {
|
|
3469
|
+
if (maxAttempts <= 1) return err;
|
|
3470
|
+
if (err.message.includes("Timeout") || err.message.includes("aborted") || err.message.includes("Operation aborted")) return err;
|
|
3471
|
+
}
|
|
3472
|
+
const detail = formatUnknownError(err);
|
|
3473
|
+
return /* @__PURE__ */ new Error(`Attempt ${attempt}/${maxAttempts} failed: ${detail}`);
|
|
3474
|
+
}
|
|
3431
3475
|
/**
|
|
3432
3476
|
* Discovers the real WebSocket URL for a CryptPad instance by querying its `/api/config` endpoint.
|
|
3433
3477
|
*/
|
|
@@ -3437,7 +3481,17 @@ async function resolveCryptPadWebsocketUrl(origin, signal) {
|
|
|
3437
3481
|
const res = await fetch(configUrl, { signal });
|
|
3438
3482
|
if (res.ok) {
|
|
3439
3483
|
const match = (await res.text()).match(/"websocketPath":\s*"([^"]+)"/);
|
|
3440
|
-
if (match && match[1])
|
|
3484
|
+
if (match && match[1]) {
|
|
3485
|
+
const candidate = match[1].trim();
|
|
3486
|
+
if (!candidate) throw new Error("Empty websocketPath");
|
|
3487
|
+
if (/^ws(s)?:\/\//i.test(candidate)) return candidate;
|
|
3488
|
+
if (candidate.startsWith("/") || !/^[a-z]+:\/\//i.test(candidate)) try {
|
|
3489
|
+
const urlObj = new URL(origin);
|
|
3490
|
+
const baseWsScheme = urlObj.protocol === "https:" ? "wss:" : "ws:";
|
|
3491
|
+
return candidate.startsWith("/") ? `${baseWsScheme}//${urlObj.host}${candidate}` : `${baseWsScheme}//${urlObj.host}/${candidate.replace(/^\//, "")}`;
|
|
3492
|
+
} catch {}
|
|
3493
|
+
return candidate;
|
|
3494
|
+
}
|
|
3441
3495
|
}
|
|
3442
3496
|
} catch {}
|
|
3443
3497
|
const urlObj = new URL(origin);
|
|
@@ -3450,9 +3504,9 @@ async function resolveCryptPadWebsocketUrl(origin, signal) {
|
|
|
3450
3504
|
*/
|
|
3451
3505
|
function fetchChannelHistory(wsUrl, channelHex, cryptKey, options = {}) {
|
|
3452
3506
|
const { timeoutMs = 8e3, quietPeriodMs = 1200, signal } = options;
|
|
3453
|
-
|
|
3507
|
+
const fetchOnce = () => new Promise((resolve, reject) => {
|
|
3454
3508
|
if (signal?.aborted) return reject(/* @__PURE__ */ new Error("Operation aborted"));
|
|
3455
|
-
const ws = new
|
|
3509
|
+
const ws = new (getWebSocketImpl())(wsUrl);
|
|
3456
3510
|
let seq = 1;
|
|
3457
3511
|
const decryptedMessages = [];
|
|
3458
3512
|
let quietTimer = null;
|
|
@@ -3545,11 +3599,24 @@ function fetchChannelHistory(wsUrl, channelHex, cryptKey, options = {}) {
|
|
|
3545
3599
|
};
|
|
3546
3600
|
ws.onerror = (err) => {
|
|
3547
3601
|
cleanup();
|
|
3548
|
-
|
|
3549
|
-
const detail = errObj?.message || errObj?.error?.message || (errObj?.error ? String(errObj.error) : String(err));
|
|
3550
|
-
reject(/* @__PURE__ */ new Error(`CryptPad WebSocket error: ${detail}`));
|
|
3602
|
+
reject(/* @__PURE__ */ new Error(`CryptPad WebSocket error: ${formatUnknownError(err)}`));
|
|
3551
3603
|
};
|
|
3552
3604
|
});
|
|
3605
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
|
|
3606
|
+
const retryDelayMs = Math.max(100, options.retryDelayMs ?? 1500);
|
|
3607
|
+
return (async () => {
|
|
3608
|
+
let lastError;
|
|
3609
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
|
|
3610
|
+
return await fetchOnce();
|
|
3611
|
+
} catch (error) {
|
|
3612
|
+
lastError = withAttemptContext(error, attempt, maxAttempts);
|
|
3613
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
3614
|
+
if (attempt >= maxAttempts || !shouldRetryBroadcastError(error)) throw lastError;
|
|
3615
|
+
const jitter = Math.floor(Math.random() * 250);
|
|
3616
|
+
await wait$1(retryDelayMs * attempt + jitter);
|
|
3617
|
+
}
|
|
3618
|
+
throw lastError ?? /* @__PURE__ */ new Error("CryptPad WebSocket history fetch failed.");
|
|
3619
|
+
})();
|
|
3553
3620
|
}
|
|
3554
3621
|
/**
|
|
3555
3622
|
* Connects to the CryptPad Netflux WebSocket server, joins a channel,
|
|
@@ -3558,9 +3625,9 @@ function fetchChannelHistory(wsUrl, channelHex, cryptKey, options = {}) {
|
|
|
3558
3625
|
*/
|
|
3559
3626
|
function broadcastChannelMessage(wsUrl, channelHex, cryptKey, message, options = {}) {
|
|
3560
3627
|
const { timeoutMs = 8e3, signal } = options;
|
|
3561
|
-
|
|
3628
|
+
const broadcastOnce = () => new Promise((resolve, reject) => {
|
|
3562
3629
|
if (signal?.aborted) return reject(/* @__PURE__ */ new Error("Operation aborted"));
|
|
3563
|
-
const ws = new
|
|
3630
|
+
const ws = new (getWebSocketImpl())(wsUrl);
|
|
3564
3631
|
let seq = 1;
|
|
3565
3632
|
let timeout = null;
|
|
3566
3633
|
let sent = false;
|
|
@@ -3596,19 +3663,28 @@ function broadcastChannelMessage(wsUrl, channelHex, cryptKey, message, options =
|
|
|
3596
3663
|
signal.addEventListener("abort", abortListener);
|
|
3597
3664
|
}
|
|
3598
3665
|
ws.onopen = () => {
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3666
|
+
try {
|
|
3667
|
+
ws.send(JSON.stringify([
|
|
3668
|
+
seq++,
|
|
3669
|
+
"JOIN",
|
|
3670
|
+
channelHex
|
|
3671
|
+
]));
|
|
3672
|
+
} catch (error) {
|
|
3673
|
+
rejectOnce(/* @__PURE__ */ new Error(`CryptPad WebSocket JOIN send failed: ${formatUnknownError(error)}`));
|
|
3674
|
+
}
|
|
3604
3675
|
};
|
|
3605
3676
|
ws.onmessage = (event) => {
|
|
3677
|
+
const raw = typeof event.data === "string" ? event.data : event.data.toString();
|
|
3678
|
+
let msg;
|
|
3606
3679
|
try {
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3680
|
+
msg = JSON.parse(raw);
|
|
3681
|
+
} catch {
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
if (!Array.isArray(msg)) return;
|
|
3685
|
+
const [, peerId, cmd] = msg;
|
|
3686
|
+
if (cmd === "JOIN" && typeof peerId === "string" && !sent) {
|
|
3687
|
+
try {
|
|
3612
3688
|
sent = true;
|
|
3613
3689
|
const encrypted = encryptCryptPadPayload(message, cryptKey, options.signKey);
|
|
3614
3690
|
ws.send(JSON.stringify([
|
|
@@ -3617,18 +3693,39 @@ function broadcastChannelMessage(wsUrl, channelHex, cryptKey, message, options =
|
|
|
3617
3693
|
channelHex,
|
|
3618
3694
|
encrypted
|
|
3619
3695
|
]));
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3696
|
+
} catch (error) {
|
|
3697
|
+
rejectOnce(/* @__PURE__ */ new Error(`CryptPad WebSocket MSG send failed: ${formatUnknownError(error)}`));
|
|
3698
|
+
return;
|
|
3623
3699
|
}
|
|
3624
|
-
|
|
3700
|
+
setTimeout(() => {
|
|
3701
|
+
resolveOnce();
|
|
3702
|
+
}, 350);
|
|
3703
|
+
}
|
|
3704
|
+
};
|
|
3705
|
+
ws.onclose = (event) => {
|
|
3706
|
+
if (settled) return;
|
|
3707
|
+
if (!sent) rejectOnce(/* @__PURE__ */ new Error(`CryptPad WebSocket closed before broadcast (code=${event.code}, reason=${event.reason || "none"}, clean=${event.wasClean}).`));
|
|
3625
3708
|
};
|
|
3626
3709
|
ws.onerror = (err) => {
|
|
3627
|
-
|
|
3628
|
-
const detail = errObj?.message || errObj?.error?.message || (errObj?.error ? String(errObj.error) : String(err));
|
|
3629
|
-
rejectOnce(/* @__PURE__ */ new Error(`CryptPad WebSocket broadcast error: ${detail}`));
|
|
3710
|
+
rejectOnce(/* @__PURE__ */ new Error(`CryptPad WebSocket broadcast error: ${formatUnknownError(err)}`));
|
|
3630
3711
|
};
|
|
3631
3712
|
});
|
|
3713
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
|
|
3714
|
+
const retryDelayMs = Math.max(100, options.retryDelayMs ?? 1500);
|
|
3715
|
+
return (async () => {
|
|
3716
|
+
let lastError;
|
|
3717
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
|
|
3718
|
+
await broadcastOnce();
|
|
3719
|
+
return;
|
|
3720
|
+
} catch (error) {
|
|
3721
|
+
lastError = withAttemptContext(error, attempt, maxAttempts);
|
|
3722
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
3723
|
+
if (attempt >= maxAttempts || !shouldRetryBroadcastError(error)) throw lastError;
|
|
3724
|
+
const jitter = Math.floor(Math.random() * 250);
|
|
3725
|
+
await wait$1(retryDelayMs * attempt + jitter);
|
|
3726
|
+
}
|
|
3727
|
+
throw lastError ?? /* @__PURE__ */ new Error("CryptPad WebSocket broadcast failed.");
|
|
3728
|
+
})();
|
|
3632
3729
|
}
|
|
3633
3730
|
/*!
|
|
3634
3731
|
* Copyright 2015-2017 Thomas Rosenau
|
|
@@ -7352,6 +7449,8 @@ function extractOnlyOfficeChannelId(metadataMessages) {
|
|
|
7352
7449
|
function parseOnlyOfficeChanges(rtMessages) {
|
|
7353
7450
|
const cells = {};
|
|
7354
7451
|
const sheetNames = /* @__PURE__ */ new Map();
|
|
7452
|
+
const preMapped = extractOnlyOfficeSheetIdMap(rtMessages);
|
|
7453
|
+
for (const [id, name] of Object.entries(preMapped.idToName)) sheetNames.set(id, name);
|
|
7355
7454
|
let defaultSheetId = "6";
|
|
7356
7455
|
for (const raw of rtMessages) try {
|
|
7357
7456
|
const data = JSON.parse(raw);
|
|
@@ -7375,7 +7474,14 @@ function parseOnlyOfficeChanges(rtMessages) {
|
|
|
7375
7474
|
const len = buf.readUInt32LE(j + 1);
|
|
7376
7475
|
if (len > 0 && j + 5 + len <= buf.length) strings.push(buf.subarray(j + 5, j + 5 + len).toString("utf16le"));
|
|
7377
7476
|
}
|
|
7378
|
-
if (strings.length >= 2)
|
|
7477
|
+
if (strings.length >= 2) {
|
|
7478
|
+
let sid = defaultSheetId;
|
|
7479
|
+
if (buf[7] === 1) {
|
|
7480
|
+
const sLen = buf.readUInt32LE(8);
|
|
7481
|
+
if (sLen > 0 && 12 + sLen <= buf.length) sid = buf.subarray(12, 12 + sLen).toString("utf16le");
|
|
7482
|
+
}
|
|
7483
|
+
sheetNames.set(sid, strings[1]);
|
|
7484
|
+
}
|
|
7379
7485
|
}
|
|
7380
7486
|
if (magic === 19595520) {
|
|
7381
7487
|
const strings = [];
|
|
@@ -7389,6 +7495,25 @@ function parseOnlyOfficeChanges(rtMessages) {
|
|
|
7389
7495
|
sheetNames.set(id, name);
|
|
7390
7496
|
}
|
|
7391
7497
|
}
|
|
7498
|
+
if (magic === 19595776) {
|
|
7499
|
+
const strings = [];
|
|
7500
|
+
for (let j = 0; j < buf.length - 4; j++) if (buf[j] === 8) {
|
|
7501
|
+
const len = buf.readUInt32LE(j + 1);
|
|
7502
|
+
if (len > 0 && j + 5 + len <= buf.length) strings.push(buf.subarray(j + 5, j + 5 + len).toString("utf16le"));
|
|
7503
|
+
}
|
|
7504
|
+
if (strings.length >= 1) {
|
|
7505
|
+
const deletedId = strings[0];
|
|
7506
|
+
const deletedName = sheetNames.get(deletedId);
|
|
7507
|
+
sheetNames.delete(deletedId);
|
|
7508
|
+
if (deletedName) {
|
|
7509
|
+
const prefix = `${deletedName}!`;
|
|
7510
|
+
for (const key of Object.keys(cells)) if (key.startsWith(prefix)) delete cells[key];
|
|
7511
|
+
}
|
|
7512
|
+
if (deletedId === "6") {
|
|
7513
|
+
for (const key of Object.keys(cells)) if (!key.includes("!") || key.startsWith("Sheet1!")) delete cells[key];
|
|
7514
|
+
}
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7392
7517
|
if (magic === 19468289) {
|
|
7393
7518
|
const sheetIdLen = buf.readUInt32LE(8);
|
|
7394
7519
|
if (sheetIdLen > 0 && sheetIdLen < 200 && 12 + sheetIdLen + 17 <= buf.length) {
|
|
@@ -7402,7 +7527,7 @@ function parseOnlyOfficeChanges(rtMessages) {
|
|
|
7402
7527
|
if (strLen >= 0 && strLen < 1e5 && k + 5 + strLen <= buf.length) {
|
|
7403
7528
|
const val = buf.subarray(k + 5, k + 5 + strLen).toString("utf16le");
|
|
7404
7529
|
const colLetter = colIndexToLetter(c1);
|
|
7405
|
-
const tabName = sheetNames.get(sheetIdStr)
|
|
7530
|
+
const tabName = sheetNames.get(sheetIdStr) && sheetNames.get(sheetIdStr) !== "Sheet1" ? sheetNames.get(sheetIdStr) : "";
|
|
7406
7531
|
const cellRef = `${tabName ? `${tabName}!` : ""}${colLetter}${r1 + 1}`;
|
|
7407
7532
|
cells[cellRef] = val;
|
|
7408
7533
|
}
|
|
@@ -7469,9 +7594,15 @@ function extractOnlyOfficeSheetIdMap(rtMessages) {
|
|
|
7469
7594
|
if (len > 0 && j + 5 + len <= buf.length) strings.push(buf.subarray(j + 5, j + 5 + len).toString("utf16le"));
|
|
7470
7595
|
}
|
|
7471
7596
|
if (strings.length >= 2) {
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7597
|
+
let sid = defaultSheetId;
|
|
7598
|
+
if (buf[7] === 1) {
|
|
7599
|
+
const sLen = buf.readUInt32LE(8);
|
|
7600
|
+
if (sLen > 0 && 12 + sLen <= buf.length) sid = buf.subarray(12, 12 + sLen).toString("utf16le");
|
|
7601
|
+
}
|
|
7602
|
+
const oldName = idToName[sid] || strings[0];
|
|
7603
|
+
delete nameToId[oldName];
|
|
7604
|
+
idToName[sid] = strings[1];
|
|
7605
|
+
nameToId[strings[1]] = sid;
|
|
7475
7606
|
}
|
|
7476
7607
|
}
|
|
7477
7608
|
if (magic === 19595520) {
|
|
@@ -7487,6 +7618,20 @@ function extractOnlyOfficeSheetIdMap(rtMessages) {
|
|
|
7487
7618
|
nameToId[name] = id;
|
|
7488
7619
|
}
|
|
7489
7620
|
}
|
|
7621
|
+
if (magic === 19595776) {
|
|
7622
|
+
const strings = [];
|
|
7623
|
+
for (let j = 0; j < buf.length - 4; j++) if (buf[j] === 8) {
|
|
7624
|
+
const len = buf.readUInt32LE(j + 1);
|
|
7625
|
+
if (len > 0 && j + 5 + len <= buf.length) strings.push(buf.subarray(j + 5, j + 5 + len).toString("utf16le"));
|
|
7626
|
+
}
|
|
7627
|
+
if (strings.length >= 1) {
|
|
7628
|
+
const id = strings[0];
|
|
7629
|
+
const name = idToName[id];
|
|
7630
|
+
delete idToName[id];
|
|
7631
|
+
if (name) delete nameToId[name];
|
|
7632
|
+
if (id === defaultSheetId) delete nameToId["Sheet1"];
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7490
7635
|
}
|
|
7491
7636
|
} catch {}
|
|
7492
7637
|
return {
|
|
@@ -7765,8 +7910,7 @@ function buildOnlyOfficeChangePayload(updates, defaultSheetId) {
|
|
|
7765
7910
|
time: now
|
|
7766
7911
|
});
|
|
7767
7912
|
for (const u of updates) {
|
|
7768
|
-
const
|
|
7769
|
-
const ref = `${u.sheet && u.sheet.trim().length > 0 ? `${u.sheet.trim()}!` : ""}${colStr}${u.row}`;
|
|
7913
|
+
const ref = `${typeof u.col === "number" ? colIndexToLetter(u.col) : u.col.toUpperCase()}${u.row}`;
|
|
7770
7914
|
const sid = u.sheetId ?? defaultSheetId ?? 6;
|
|
7771
7915
|
const rec = u.formula !== void 0 ? encodeOnlyOfficeFormulaCellRecord(ref, u.formula, sid) : encodeOnlyOfficeCellRecord(ref, u.value ?? "", sid);
|
|
7772
7916
|
changeItems.push({
|
|
@@ -7879,6 +8023,142 @@ function buildOnlyOfficeSheetAddPayload(name, sheetId, insertBeforeIndex) {
|
|
|
7879
8023
|
isExcel: true
|
|
7880
8024
|
});
|
|
7881
8025
|
}
|
|
8026
|
+
/**
|
|
8027
|
+
* Encodes a single "delete worksheet tab" record into the native OnlyOffice binary format:
|
|
8028
|
+
* AscCH.historyitem_Workbook_SheetDelete (magic 0x012b0200), UndoRedoDataTypes.SheetDelete (26, 0x1a).
|
|
8029
|
+
*
|
|
8030
|
+
* @param sheetId - Sheet identifier to delete.
|
|
8031
|
+
*/
|
|
8032
|
+
function encodeOnlyOfficeSheetDeleteRecord(sheetId) {
|
|
8033
|
+
const sheetIdStr = String(sheetId);
|
|
8034
|
+
const sheetIdBuf = Buffer.from(sheetIdStr, "utf16le");
|
|
8035
|
+
const idLen = sheetIdBuf.length;
|
|
8036
|
+
const propertyBlock = Buffer.concat([
|
|
8037
|
+
Buffer.from([
|
|
8038
|
+
0,
|
|
8039
|
+
2,
|
|
8040
|
+
1
|
|
8041
|
+
]),
|
|
8042
|
+
Buffer.from([1, 8]),
|
|
8043
|
+
Buffer.alloc(4),
|
|
8044
|
+
sheetIdBuf,
|
|
8045
|
+
Buffer.from([
|
|
8046
|
+
2,
|
|
8047
|
+
9,
|
|
8048
|
+
255,
|
|
8049
|
+
0,
|
|
8050
|
+
0
|
|
8051
|
+
])
|
|
8052
|
+
]);
|
|
8053
|
+
propertyBlock.writeUInt32LE(idLen, 5);
|
|
8054
|
+
const remainingLen = Buffer.alloc(4);
|
|
8055
|
+
remainingLen.writeUInt32LE(propertyBlock.length + 2, 0);
|
|
8056
|
+
const magic = Buffer.alloc(4);
|
|
8057
|
+
magic.writeUInt32BE(19595776, 0);
|
|
8058
|
+
const body = Buffer.concat([
|
|
8059
|
+
magic,
|
|
8060
|
+
Buffer.from([0, 26]),
|
|
8061
|
+
remainingLen,
|
|
8062
|
+
propertyBlock
|
|
8063
|
+
]);
|
|
8064
|
+
const header = Buffer.alloc(4);
|
|
8065
|
+
header.writeUInt32LE(body.length + 2, 0);
|
|
8066
|
+
return Buffer.concat([header, body]);
|
|
8067
|
+
}
|
|
8068
|
+
/**
|
|
8069
|
+
* Formats a single "delete worksheet tab" operation into an OnlyOffice `saveChanges`
|
|
8070
|
+
* message, structured the same way as {@link buildOnlyOfficeChangePayload}.
|
|
8071
|
+
*/
|
|
8072
|
+
function buildOnlyOfficeSheetDeletePayload(sheetId) {
|
|
8073
|
+
const txOpen = Buffer.from("0a0000000129000000ff00000000", "hex");
|
|
8074
|
+
const now = Date.now();
|
|
8075
|
+
const rec = encodeOnlyOfficeSheetDeleteRecord(sheetId);
|
|
8076
|
+
return JSON.stringify({
|
|
8077
|
+
type: "saveChanges",
|
|
8078
|
+
changes: [{
|
|
8079
|
+
change: JSON.stringify(`${txOpen.length};${txOpen.toString("base64")}`),
|
|
8080
|
+
time: now
|
|
8081
|
+
}, {
|
|
8082
|
+
change: JSON.stringify(`${rec.length};${rec.toString("base64")}`),
|
|
8083
|
+
time: now
|
|
8084
|
+
}],
|
|
8085
|
+
startSaveChanges: true,
|
|
8086
|
+
endSaveChanges: true,
|
|
8087
|
+
isExcel: true
|
|
8088
|
+
});
|
|
8089
|
+
}
|
|
8090
|
+
/**
|
|
8091
|
+
* Encodes a single "rename worksheet tab" record into the native OnlyOffice binary format:
|
|
8092
|
+
* AscCH.historyitem_Worksheet_Rename (magic 0x012a1201), UndoRedoDataTypes.FromTo (5),
|
|
8093
|
+
* matching `UndoRedoData_FromTo` (`from` = 0, `to` = 1, `copyRange` = 2, `sheetIdTo` = 3).
|
|
8094
|
+
*
|
|
8095
|
+
* @param sheetId - Internal sheet identifier to rename (e.g. '6').
|
|
8096
|
+
* @param oldName - Current name of the sheet tab (e.g. 'Sheet1').
|
|
8097
|
+
* @param newName - Desired new name for the tab (e.g. 'i18n').
|
|
8098
|
+
*/
|
|
8099
|
+
function encodeOnlyOfficeSheetRenameRecord(sheetId, oldName, newName) {
|
|
8100
|
+
const sheetIdBuf = Buffer.from(String(sheetId), "utf16le");
|
|
8101
|
+
const sheetIdLen = Buffer.alloc(4);
|
|
8102
|
+
sheetIdLen.writeUInt32LE(sheetIdBuf.length, 0);
|
|
8103
|
+
const oldNameBuf = Buffer.from(oldName, "utf16le");
|
|
8104
|
+
const oldNameLen = Buffer.alloc(4);
|
|
8105
|
+
oldNameLen.writeUInt32LE(oldNameBuf.length, 0);
|
|
8106
|
+
const newNameBuf = Buffer.from(newName, "utf16le");
|
|
8107
|
+
const newNameLen = Buffer.alloc(4);
|
|
8108
|
+
newNameLen.writeUInt32LE(newNameBuf.length, 0);
|
|
8109
|
+
const propertyBlock = Buffer.concat([
|
|
8110
|
+
Buffer.from([0, 8]),
|
|
8111
|
+
oldNameLen,
|
|
8112
|
+
oldNameBuf,
|
|
8113
|
+
Buffer.from([1, 8]),
|
|
8114
|
+
newNameLen,
|
|
8115
|
+
newNameBuf,
|
|
8116
|
+
Buffer.from([2, 0]),
|
|
8117
|
+
Buffer.from([3, 1])
|
|
8118
|
+
]);
|
|
8119
|
+
const propertyLen = Buffer.alloc(4);
|
|
8120
|
+
propertyLen.writeUInt32LE(propertyBlock.length, 0);
|
|
8121
|
+
const magic = Buffer.from([
|
|
8122
|
+
1,
|
|
8123
|
+
42,
|
|
8124
|
+
18,
|
|
8125
|
+
1
|
|
8126
|
+
]);
|
|
8127
|
+
const body = Buffer.concat([
|
|
8128
|
+
magic,
|
|
8129
|
+
sheetIdLen,
|
|
8130
|
+
sheetIdBuf,
|
|
8131
|
+
Buffer.from([0]),
|
|
8132
|
+
Buffer.from([5]),
|
|
8133
|
+
propertyLen,
|
|
8134
|
+
propertyBlock
|
|
8135
|
+
]);
|
|
8136
|
+
const header = Buffer.alloc(4);
|
|
8137
|
+
header.writeUInt32LE(body.length, 0);
|
|
8138
|
+
return Buffer.concat([header, body]);
|
|
8139
|
+
}
|
|
8140
|
+
/**
|
|
8141
|
+
* Formats a single "rename worksheet tab" operation into an OnlyOffice `saveChanges`
|
|
8142
|
+
* message, structured the same way as {@link buildOnlyOfficeChangePayload}.
|
|
8143
|
+
*/
|
|
8144
|
+
function buildOnlyOfficeSheetRenamePayload(sheetId, oldName, newName) {
|
|
8145
|
+
const txOpen = Buffer.from("0a0000000129000000ff00000000", "hex");
|
|
8146
|
+
const now = Date.now();
|
|
8147
|
+
const rec = encodeOnlyOfficeSheetRenameRecord(sheetId, oldName, newName);
|
|
8148
|
+
return JSON.stringify({
|
|
8149
|
+
type: "saveChanges",
|
|
8150
|
+
changes: [{
|
|
8151
|
+
change: JSON.stringify(`${txOpen.length};${txOpen.toString("base64")}`),
|
|
8152
|
+
time: now
|
|
8153
|
+
}, {
|
|
8154
|
+
change: JSON.stringify(`${rec.length};${rec.toString("base64")}`),
|
|
8155
|
+
time: now
|
|
8156
|
+
}],
|
|
8157
|
+
startSaveChanges: true,
|
|
8158
|
+
endSaveChanges: true,
|
|
8159
|
+
isExcel: true
|
|
8160
|
+
});
|
|
8161
|
+
}
|
|
7882
8162
|
//#endregion
|
|
7883
8163
|
//#region src/providers/cryptpad/client.ts
|
|
7884
8164
|
/**
|
|
@@ -7998,7 +8278,7 @@ var CryptPadClient = class {
|
|
|
7998
8278
|
await new Promise((r) => setTimeout(r, 800));
|
|
7999
8279
|
}
|
|
8000
8280
|
const sheetId = generateOnlyOfficeSheetId();
|
|
8001
|
-
const insertAt = insertBeforeIndex ?? data.sheetNames.length;
|
|
8281
|
+
const insertAt = Math.min(insertBeforeIndex ?? data.sheetNames.length, data.sheetNames.length);
|
|
8002
8282
|
await this.broadcastSheetAdd(name, sheetId, insertAt, rtChannel, signal);
|
|
8003
8283
|
return sheetId;
|
|
8004
8284
|
}
|
|
@@ -8020,6 +8300,61 @@ var CryptPadClient = class {
|
|
|
8020
8300
|
});
|
|
8021
8301
|
}
|
|
8022
8302
|
/**
|
|
8303
|
+
* Deletes a worksheet tab by name from the CryptPad workbook.
|
|
8304
|
+
*
|
|
8305
|
+
* @param sheetName - Name of the sheet tab to delete (e.g. 'Sheet1').
|
|
8306
|
+
* @param signal - Optional AbortSignal.
|
|
8307
|
+
*/
|
|
8308
|
+
async deleteSheet(sheetName, signal) {
|
|
8309
|
+
const data = await this.fetchSheetData(signal);
|
|
8310
|
+
const targetSheetId = data.sheetIds?.[sheetName];
|
|
8311
|
+
if (!targetSheetId) return;
|
|
8312
|
+
if (data.sheetNames.length <= 1) throw new Error(`Cannot delete sheet "${sheetName}": workbook must contain at least one worksheet.`);
|
|
8313
|
+
const rtChannel = data.metadata.rtChannelId;
|
|
8314
|
+
if (!rtChannel) throw new Error("Cannot delete sheet: pad has no active real-time collaboration channel.");
|
|
8315
|
+
await this.broadcastSheetDelete(targetSheetId, rtChannel, signal);
|
|
8316
|
+
}
|
|
8317
|
+
/**
|
|
8318
|
+
* Broadcasts a `Workbook_SheetDelete` record to an already-known RT channel.
|
|
8319
|
+
*/
|
|
8320
|
+
async broadcastSheetDelete(sheetId, rtChannel, signal) {
|
|
8321
|
+
const wsUrl = await this.getWebsocketUrl(signal);
|
|
8322
|
+
const { cryptKey, signKey } = this.getKeys();
|
|
8323
|
+
await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, buildOnlyOfficeSheetDeletePayload(sheetId), {
|
|
8324
|
+
timeoutMs: this.timeoutMs,
|
|
8325
|
+
signal,
|
|
8326
|
+
signKey
|
|
8327
|
+
});
|
|
8328
|
+
}
|
|
8329
|
+
/**
|
|
8330
|
+
* Broadcasts a `Worksheet_Rename` record to an already-known RT channel.
|
|
8331
|
+
*/
|
|
8332
|
+
async broadcastSheetRename(sheetId, oldName, newName, rtChannel, signal) {
|
|
8333
|
+
const wsUrl = await this.getWebsocketUrl(signal);
|
|
8334
|
+
const { cryptKey, signKey } = this.getKeys();
|
|
8335
|
+
await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, buildOnlyOfficeSheetRenamePayload(sheetId, oldName, newName), {
|
|
8336
|
+
timeoutMs: this.timeoutMs,
|
|
8337
|
+
signal,
|
|
8338
|
+
signKey
|
|
8339
|
+
});
|
|
8340
|
+
}
|
|
8341
|
+
/**
|
|
8342
|
+
* Renames an existing worksheet tab by name in the CryptPad workbook.
|
|
8343
|
+
*
|
|
8344
|
+
* @param oldSheetName - Current name of the sheet tab (e.g. 'Sheet1').
|
|
8345
|
+
* @param newSheetName - Desired new name for the tab (e.g. 'i18n').
|
|
8346
|
+
* @param signal - Optional AbortSignal.
|
|
8347
|
+
*/
|
|
8348
|
+
async renameSheet(oldSheetName, newSheetName, signal) {
|
|
8349
|
+
if (oldSheetName === newSheetName) return;
|
|
8350
|
+
const data = await this.fetchSheetData(signal);
|
|
8351
|
+
const targetSheetId = data.sheetIds?.[oldSheetName];
|
|
8352
|
+
if (!targetSheetId) throw new Error(`Cannot rename sheet "${oldSheetName}": sheet not found in the workbook.`);
|
|
8353
|
+
const rtChannel = data.metadata.rtChannelId;
|
|
8354
|
+
if (!rtChannel) throw new Error("Cannot rename sheet: pad has no active real-time collaboration channel.");
|
|
8355
|
+
await this.broadcastSheetRename(targetSheetId, oldSheetName, newSheetName, rtChannel, signal);
|
|
8356
|
+
}
|
|
8357
|
+
/**
|
|
8023
8358
|
* Writes a literal-text header row (row 1) into a sheet — the canonical, non-linked
|
|
8024
8359
|
* form used for the `i18n` sheet itself (there is nothing to link an `i18n` header to)
|
|
8025
8360
|
* and as the safe fallback whenever a new sheet's columns can't be aligned 1:1 with an
|
|
@@ -8138,7 +8473,12 @@ var CryptPadClient = class {
|
|
|
8138
8473
|
sheetIdMap = extractOnlyOfficeSheetIdMap(rtMessages);
|
|
8139
8474
|
}
|
|
8140
8475
|
const multiSheets = convertCellsToMultiSheetRows(cells);
|
|
8141
|
-
const
|
|
8476
|
+
const discoveredSheetNames = /* @__PURE__ */ new Set();
|
|
8477
|
+
for (const name of Object.keys(sheetIdMap.nameToId)) if (name && name.trim().length > 0) discoveredSheetNames.add(name);
|
|
8478
|
+
if (discoveredSheetNames.size === 0) {
|
|
8479
|
+
for (const name of Object.keys(multiSheets)) if (name && name.trim().length > 0) discoveredSheetNames.add(name);
|
|
8480
|
+
}
|
|
8481
|
+
const sheetNames = Array.from(discoveredSheetNames);
|
|
8142
8482
|
const defaultSheet = multiSheets["Sheet1"] ? "Sheet1" : sheetNames[0];
|
|
8143
8483
|
const defaultRows = defaultSheet && multiSheets[defaultSheet] ? multiSheets[defaultSheet] : convertCellsToSheetRows(cells);
|
|
8144
8484
|
const groupedCells = groupCellsBySheet(cells);
|
|
@@ -8215,16 +8555,26 @@ var CryptPadClient = class {
|
|
|
8215
8555
|
async writeSheetRows(sheetName, rows, options = {}) {
|
|
8216
8556
|
if (rows.length === 0) return 0;
|
|
8217
8557
|
const data = await this.fetchSheetData(options.signal);
|
|
8558
|
+
let rtChannel = data.metadata.rtChannelId;
|
|
8218
8559
|
let existingRows = data.sheets[sheetName]?.rows ?? [];
|
|
8219
8560
|
let targetSheetId = data.sheetIds?.[sheetName];
|
|
8220
8561
|
let justCreated = false;
|
|
8221
8562
|
if (!targetSheetId) {
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8563
|
+
if (data.sheetNames.includes("Sheet1") && (data.sheets["Sheet1"]?.rows.length ?? 0) === 0 && Object.keys(data.sheets["Sheet1"]?.cells ?? {}).length === 0 && sheetName !== "Sheet1" && (data.sheetNames.length === 1 || sheetName === "i18n")) {
|
|
8564
|
+
const sheet1Id = data.sheetIds?.["Sheet1"] ?? "6";
|
|
8565
|
+
this.onProgress?.(`Renaming fresh empty "Sheet1" tab to "${sheetName}".`);
|
|
8566
|
+
if (rtChannel) await this.broadcastSheetRename(sheet1Id, "Sheet1", sheetName, rtChannel, options.signal);
|
|
8567
|
+
else await this.renameSheet("Sheet1", sheetName, options.signal);
|
|
8568
|
+
targetSheetId = sheet1Id;
|
|
8569
|
+
existingRows = [];
|
|
8570
|
+
justCreated = true;
|
|
8571
|
+
} else {
|
|
8572
|
+
this.onProgress?.(`Sheet "${sheetName}" not found — creating it.`);
|
|
8573
|
+
targetSheetId = await this.createSheet(sheetName, data.sheetNames.length, options.signal);
|
|
8574
|
+
existingRows = [];
|
|
8575
|
+
justCreated = true;
|
|
8576
|
+
}
|
|
8226
8577
|
}
|
|
8227
|
-
let rtChannel = data.metadata.rtChannelId;
|
|
8228
8578
|
let keyColName = "key";
|
|
8229
8579
|
const firstExisting = existingRows[0];
|
|
8230
8580
|
const firstIncoming = rows[0];
|
|
@@ -8338,7 +8688,7 @@ function createCryptPadSheetInputProvider(options) {
|
|
|
8338
8688
|
}).fetchSheetData(request.signal);
|
|
8339
8689
|
const sourceTables = [];
|
|
8340
8690
|
if (Array.isArray(sheetData.sheetNames) && sheetData.sheetNames.length > 1) {
|
|
8341
|
-
for (const tabName of sheetData.sheetNames) if (requested.size === 0 || requested.has(tabName)
|
|
8691
|
+
for (const tabName of sheetData.sheetNames) if (requested.size === 0 || requested.has(tabName)) sourceTables.push({
|
|
8342
8692
|
tableId: `${source.tableId ?? url}#${tabName}`,
|
|
8343
8693
|
tableName: tabName,
|
|
8344
8694
|
rows: sheetData.sheets[tabName]?.rows ?? [],
|
|
@@ -8351,7 +8701,7 @@ function createCryptPadSheetInputProvider(options) {
|
|
|
8351
8701
|
}
|
|
8352
8702
|
});
|
|
8353
8703
|
}
|
|
8354
|
-
if (sourceTables.length === 0) sourceTables.push({
|
|
8704
|
+
if (sourceTables.length === 0 && (!Array.isArray(sheetData.sheetNames) || sheetData.sheetNames.length <= 1)) sourceTables.push({
|
|
8355
8705
|
tableId: source.tableId ?? url,
|
|
8356
8706
|
tableName: source.tableName,
|
|
8357
8707
|
rows: sheetData.rows,
|
|
@@ -8380,12 +8730,12 @@ const CRYPTPAD_SHEET_OUTPUT_CAPABILITIES = createCapabilitySet({ writeTables: tr
|
|
|
8380
8730
|
* Converts nested TranslationData `[locale][sheet][key] = value` into
|
|
8381
8731
|
* tabular rows `Record<sheetName, SheetRow[]>`.
|
|
8382
8732
|
*/
|
|
8383
|
-
function convertTranslationsToSheetRows(translations, localeMapping = {}, keyColumnName = "key") {
|
|
8733
|
+
function convertTranslationsToSheetRows(translations, localeMapping = {}, keyColumnName = "key", includeI18nSheet = false) {
|
|
8384
8734
|
const sheetRowsMap = {};
|
|
8385
8735
|
for (const [locale, sheets] of Object.entries(translations)) {
|
|
8386
8736
|
const colHeader = localeMapping[locale] ?? locale;
|
|
8387
8737
|
for (const [sheetName, keys] of Object.entries(sheets)) {
|
|
8388
|
-
if (sheetName === "i18n") continue;
|
|
8738
|
+
if (!includeI18nSheet && sheetName === "i18n") continue;
|
|
8389
8739
|
if (!sheetRowsMap[sheetName]) sheetRowsMap[sheetName] = /* @__PURE__ */ new Map();
|
|
8390
8740
|
for (const [key, value] of Object.entries(keys)) {
|
|
8391
8741
|
if (!sheetRowsMap[sheetName].has(key)) sheetRowsMap[sheetName].set(key, { [keyColumnName]: key });
|
|
@@ -8418,17 +8768,22 @@ function createCryptPadSheetOutputProvider(options = {}) {
|
|
|
8418
8768
|
timeoutMs: options.timeoutMs
|
|
8419
8769
|
});
|
|
8420
8770
|
const effectiveMapping = options.localeMapping ?? payload.localeMapping ?? {};
|
|
8421
|
-
const sheetRowsMap = convertTranslationsToSheetRows(payload.translations, effectiveMapping, options.keyColumnName ?? "key");
|
|
8771
|
+
const sheetRowsMap = convertTranslationsToSheetRows(payload.translations, effectiveMapping, options.keyColumnName ?? "key", options.includeI18nSheet ?? false);
|
|
8422
8772
|
const updatedSheets = [];
|
|
8423
8773
|
let totalUpdatedCells = 0;
|
|
8424
|
-
|
|
8774
|
+
const sheetEntries = Object.entries(sheetRowsMap).sort(([a], [b]) => {
|
|
8775
|
+
if (a === "i18n") return -1;
|
|
8776
|
+
if (b === "i18n") return 1;
|
|
8777
|
+
return 0;
|
|
8778
|
+
});
|
|
8779
|
+
for (const [sheetName, rows] of sheetEntries) {
|
|
8425
8780
|
const count = await client.writeSheetRows(sheetName, rows, {
|
|
8426
8781
|
override: options.override ?? false,
|
|
8427
8782
|
linkHeadersToI18nSheet: options.linkHeadersToI18nSheet ?? false
|
|
8428
8783
|
});
|
|
8429
8784
|
updatedSheets.push(sheetName);
|
|
8430
8785
|
totalUpdatedCells += count;
|
|
8431
|
-
await new Promise((r) => setTimeout(r,
|
|
8786
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
8432
8787
|
}
|
|
8433
8788
|
return {
|
|
8434
8789
|
wroteFiles: [url],
|
|
@@ -9738,4 +10093,4 @@ function migrateProjectToV3(options = {}) {
|
|
|
9738
10093
|
//#region src/index.ts
|
|
9739
10094
|
var src_default = getSpreadSheetData;
|
|
9740
10095
|
//#endregion
|
|
9741
|
-
export { CRYPTPAD_ASSET_SYNC_CAPABILITIES, CRYPTPAD_CSV_INPUT_CAPABILITIES, CRYPTPAD_SHEET_INPUT_CAPABILITIES, CRYPTPAD_WORKSPACE_OUTPUT_CAPABILITIES, CRYPTPAD_WORKSPACE_SYNC_CAPABILITIES, CryptPadClient, DEFAULT_IMAGE_EXTENSIONS, DEFAULT_WAIT_SECONDS, EMPTY_PROVIDER_CAPABILITIES, GOOGLE_SHEETS_PROVIDER_CAPABILITIES, GcpApiError, OPERATION_CAPABILITY_REQUIREMENTS, PROVIDER_CAPABILITIES, assertOperationCapabilities, assertRequiredCapabilities, assertValidProviderRuntimeConfig, broadcastChannelMessage, buildGoogleAuth, buildManifest, buildSyncPlan, columnIndexToLetter, convertCellsToSheetRows, convertFromDataJsonFormat, convertToDataJsonFormat, createAuthClient, createCapabilitySet, createCryptPadAssetSyncProvider, createCryptPadCsvInputProvider, createCryptPadSheetInputProvider, createCryptPadWorkspaceOutputProvider, createCryptPadWorkspaceSyncProvider, createDefaultProviderCatalog, createGoogleSheetsInputProvider, createGoogleSheetsOutputProvider, createGoogleSheetsSyncProvider, createInMemoryProviderCatalogProvider, createLocaleMapping, createProvidersFromRuntimeConfig, createSpreadsheet, decryptCryptPadPayload, src_default as default, deriveCryptPadKeys, encodeOnlyOfficeCellRecord, encryptCryptPadPayload, entriesToSeedKeys, entriesToTranslationData, exportDoc, extractOnlyOfficeChannelId, extractOnlyOfficeMetadata, extractOnlyOfficeSheetIdMap, fetchChannelHistory, filterValidLocales, findLocalChanges, getGoogleTranslateCode, getLanguagePrefix, getLocaleDisplayName, getMultipleSpreadSheetsData, getNormalizedLocaleForHeader, getOriginalHeaderForLocale, getSpreadSheetData, getTranslationSummary, grantDrivePermissions, handleBidirectionalSync, hasRequiredCapabilities, inferLocaleFromDocName, ingestDoc, isValidLocale, manageDriveTranslations, mapLegacyGoogleOptionsToProviderConfig, mergeMultipleTranslationData, mergeSheets, migrateProjectToV3, missingCapabilities, moveSpreadsheetToFolder, normalizeExtension, normalizeLocaleCode, normalizePrivateKey, parseDocContent, parseOnlyOfficeChanges, parsePadUrl, processRawRows, readManifest, readPublicSheet, requiresGoogleAuthForRuntimeConfig, resolveCryptPadWebsocketUrl, resolveLocaleWithFallback, resolveSyncPlan, runProviderPipeline, sanitizeFolderName, scanDriveFolderForDocs, scanDriveFolderForSpreadsheets, setupWIF, slugifyKey, syncDriveImages, updateSpreadsheetWithLocalChanges, validateCredentials, validateEnv, validateImageDirectory, validateProviderRuntimeConfig, wait, walkDirectory, withRetry, writeLanguageDataFile, writeLocalesFile, writeManifest, writeTranslationFiles };
|
|
10096
|
+
export { CRYPTPAD_ASSET_SYNC_CAPABILITIES, CRYPTPAD_CSV_INPUT_CAPABILITIES, CRYPTPAD_SHEET_INPUT_CAPABILITIES, CRYPTPAD_WORKSPACE_OUTPUT_CAPABILITIES, CRYPTPAD_WORKSPACE_SYNC_CAPABILITIES, CryptPadClient, DEFAULT_IMAGE_EXTENSIONS, DEFAULT_WAIT_SECONDS, EMPTY_PROVIDER_CAPABILITIES, GOOGLE_SHEETS_PROVIDER_CAPABILITIES, GcpApiError, OPERATION_CAPABILITY_REQUIREMENTS, PROVIDER_CAPABILITIES, assertOperationCapabilities, assertRequiredCapabilities, assertValidProviderRuntimeConfig, broadcastChannelMessage, buildGoogleAuth, buildManifest, buildOnlyOfficeSheetDeletePayload, buildOnlyOfficeSheetRenamePayload, buildSyncPlan, columnIndexToLetter, convertCellsToSheetRows, convertFromDataJsonFormat, convertToDataJsonFormat, createAuthClient, createCapabilitySet, createCryptPadAssetSyncProvider, createCryptPadCsvInputProvider, createCryptPadSheetInputProvider, createCryptPadWorkspaceOutputProvider, createCryptPadWorkspaceSyncProvider, createDefaultProviderCatalog, createGoogleSheetsInputProvider, createGoogleSheetsOutputProvider, createGoogleSheetsSyncProvider, createInMemoryProviderCatalogProvider, createLocaleMapping, createProvidersFromRuntimeConfig, createSpreadsheet, decryptCryptPadPayload, src_default as default, deriveCryptPadKeys, encodeOnlyOfficeCellRecord, encodeOnlyOfficeSheetDeleteRecord, encodeOnlyOfficeSheetRenameRecord, encryptCryptPadPayload, entriesToSeedKeys, entriesToTranslationData, exportDoc, extractOnlyOfficeChannelId, extractOnlyOfficeMetadata, extractOnlyOfficeSheetIdMap, fetchChannelHistory, filterValidLocales, findLocalChanges, getGoogleTranslateCode, getLanguagePrefix, getLocaleDisplayName, getMultipleSpreadSheetsData, getNormalizedLocaleForHeader, getOriginalHeaderForLocale, getSpreadSheetData, getTranslationSummary, grantDrivePermissions, handleBidirectionalSync, hasRequiredCapabilities, inferLocaleFromDocName, ingestDoc, isValidLocale, manageDriveTranslations, mapLegacyGoogleOptionsToProviderConfig, mergeMultipleTranslationData, mergeSheets, migrateProjectToV3, missingCapabilities, moveSpreadsheetToFolder, normalizeExtension, normalizeLocaleCode, normalizePrivateKey, parseDocContent, parseOnlyOfficeChanges, parsePadUrl, processRawRows, readManifest, readPublicSheet, requiresGoogleAuthForRuntimeConfig, resolveCryptPadWebsocketUrl, resolveLocaleWithFallback, resolveSyncPlan, runProviderPipeline, sanitizeFolderName, scanDriveFolderForDocs, scanDriveFolderForSpreadsheets, setupWIF, slugifyKey, syncDriveImages, updateSpreadsheetWithLocalChanges, validateCredentials, validateEnv, validateImageDirectory, validateProviderRuntimeConfig, wait, walkDirectory, withRetry, writeLanguageDataFile, writeLocalesFile, writeManifest, writeTranslationFiles };
|