@el-j/google-sheet-translations 3.0.0-beta.5 → 3.0.0-beta.6

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 CHANGED
@@ -3745,6 +3745,29 @@ var CryptPadClient = class {
3745
3745
  return resolveCryptPadWebsocketUrl(this.parsedUrl.origin, signal);
3746
3746
  }
3747
3747
  /**
3748
+ * Initializes the OnlyOffice RT channel for a brand-new CryptPad sheet that has never
3749
+ * been opened in a browser. Generates a random 32-hex channel ID, broadcasts it as an
3750
+ * encrypted metadata patch on the Netflux channel (the same thing the OnlyOffice browser
3751
+ * client does on first open), and returns the new RT channel ID.
3752
+ *
3753
+ * This removes the requirement to open the sheet in a browser before the CLI can write to it.
3754
+ */
3755
+ async initializeRtChannel(signal) {
3756
+ const wsUrl = await this.getWebsocketUrl(signal);
3757
+ const { channelHex, cryptKey } = this.getKeys();
3758
+ const newRtChannel = crypto.randomBytes(16).toString("hex");
3759
+ const innerJson = JSON.stringify({ content: { channel: newRtChannel } });
3760
+ await broadcastChannelMessage(wsUrl, channelHex, cryptKey, JSON.stringify([1, [[
3761
+ 0,
3762
+ 0,
3763
+ innerJson
3764
+ ]]]), {
3765
+ timeoutMs: this.timeoutMs,
3766
+ signal
3767
+ });
3768
+ return newRtChannel;
3769
+ }
3770
+ /**
3748
3771
  * Fetches the complete sheet data, including raw cell coordinate mappings,
3749
3772
  * multi-sheet tabs, and structured rows formatted for translation ingestion.
3750
3773
  */
@@ -3795,14 +3818,22 @@ var CryptPadClient = class {
3795
3818
  }
3796
3819
  /**
3797
3820
  * Broadcasts cell updates to the OnlyOffice real-time collaboration channel.
3821
+ * If the sheet has never been opened in a browser (RT channel is None), it is
3822
+ * automatically initialized headlessly — no browser required.
3798
3823
  */
3799
3824
  async sendCellUpdates(updates, signal) {
3800
3825
  if (updates.length === 0) return;
3801
- const rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
3802
- if (!rtChannel) throw new Error(`CryptPad sheet "${this.parsedUrl.cleanUrl}" does not have an active OnlyOffice RT channel.`);
3826
+ let rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
3827
+ if (!rtChannel) {
3828
+ console.log("No OnlyOffice RT channel found. Initializing headlessly (no browser required)...");
3829
+ rtChannel = await this.initializeRtChannel(signal);
3830
+ console.log(`RT channel initialized: ${rtChannel}`);
3831
+ await new Promise((r) => setTimeout(r, 800));
3832
+ }
3803
3833
  const wsUrl = await this.getWebsocketUrl(signal);
3804
3834
  const { cryptKey } = this.getKeys();
3805
- await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, buildOnlyOfficeChangePayload(updates), {
3835
+ const payload = buildOnlyOfficeChangePayload(updates);
3836
+ await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, payload, {
3806
3837
  timeoutMs: this.timeoutMs,
3807
3838
  signal
3808
3839
  });
package/dist/index.js CHANGED
@@ -3778,6 +3778,29 @@ var CryptPadClient = class {
3778
3778
  return resolveCryptPadWebsocketUrl(this.parsedUrl.origin, signal);
3779
3779
  }
3780
3780
  /**
3781
+ * Initializes the OnlyOffice RT channel for a brand-new CryptPad sheet that has never
3782
+ * been opened in a browser. Generates a random 32-hex channel ID, broadcasts it as an
3783
+ * encrypted metadata patch on the Netflux channel (the same thing the OnlyOffice browser
3784
+ * client does on first open), and returns the new RT channel ID.
3785
+ *
3786
+ * This removes the requirement to open the sheet in a browser before the CLI can write to it.
3787
+ */
3788
+ async initializeRtChannel(signal) {
3789
+ const wsUrl = await this.getWebsocketUrl(signal);
3790
+ const { channelHex, cryptKey } = this.getKeys();
3791
+ const newRtChannel = node_crypto.default.randomBytes(16).toString("hex");
3792
+ const innerJson = JSON.stringify({ content: { channel: newRtChannel } });
3793
+ await broadcastChannelMessage(wsUrl, channelHex, cryptKey, JSON.stringify([1, [[
3794
+ 0,
3795
+ 0,
3796
+ innerJson
3797
+ ]]]), {
3798
+ timeoutMs: this.timeoutMs,
3799
+ signal
3800
+ });
3801
+ return newRtChannel;
3802
+ }
3803
+ /**
3781
3804
  * Fetches the complete sheet data, including raw cell coordinate mappings,
3782
3805
  * multi-sheet tabs, and structured rows formatted for translation ingestion.
3783
3806
  */
@@ -3828,14 +3851,22 @@ var CryptPadClient = class {
3828
3851
  }
3829
3852
  /**
3830
3853
  * Broadcasts cell updates to the OnlyOffice real-time collaboration channel.
3854
+ * If the sheet has never been opened in a browser (RT channel is None), it is
3855
+ * automatically initialized headlessly — no browser required.
3831
3856
  */
3832
3857
  async sendCellUpdates(updates, signal) {
3833
3858
  if (updates.length === 0) return;
3834
- const rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
3835
- if (!rtChannel) throw new Error(`CryptPad sheet "${this.parsedUrl.cleanUrl}" does not have an active OnlyOffice RT channel.`);
3859
+ let rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
3860
+ if (!rtChannel) {
3861
+ console.log("No OnlyOffice RT channel found. Initializing headlessly (no browser required)...");
3862
+ rtChannel = await this.initializeRtChannel(signal);
3863
+ console.log(`RT channel initialized: ${rtChannel}`);
3864
+ await new Promise((r) => setTimeout(r, 800));
3865
+ }
3836
3866
  const wsUrl = await this.getWebsocketUrl(signal);
3837
3867
  const { cryptKey } = this.getKeys();
3838
- await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, buildOnlyOfficeChangePayload(updates), {
3868
+ const payload = buildOnlyOfficeChangePayload(updates);
3869
+ await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, payload, {
3839
3870
  timeoutMs: this.timeoutMs,
3840
3871
  signal
3841
3872
  });
@@ -42,6 +42,15 @@ export declare class CryptPadClient {
42
42
  getKeys(): DerivedCryptPadKeys;
43
43
  /** Resolves the Netflux WebSocket endpoint to connect to. */
44
44
  getWebsocketUrl(signal?: AbortSignal): Promise<string>;
45
+ /**
46
+ * Initializes the OnlyOffice RT channel for a brand-new CryptPad sheet that has never
47
+ * been opened in a browser. Generates a random 32-hex channel ID, broadcasts it as an
48
+ * encrypted metadata patch on the Netflux channel (the same thing the OnlyOffice browser
49
+ * client does on first open), and returns the new RT channel ID.
50
+ *
51
+ * This removes the requirement to open the sheet in a browser before the CLI can write to it.
52
+ */
53
+ initializeRtChannel(signal?: AbortSignal): Promise<string>;
45
54
  /**
46
55
  * Fetches the complete sheet data, including raw cell coordinate mappings,
47
56
  * multi-sheet tabs, and structured rows formatted for translation ingestion.
@@ -54,6 +63,8 @@ export declare class CryptPadClient {
54
63
  fetchSheetRows(sheetName?: string, signal?: AbortSignal): Promise<SheetRow[]>;
55
64
  /**
56
65
  * Broadcasts cell updates to the OnlyOffice real-time collaboration channel.
66
+ * If the sheet has never been opened in a browser (RT channel is None), it is
67
+ * automatically initialized headlessly — no browser required.
57
68
  */
58
69
  sendCellUpdates(updates: OnlyOfficeCellUpdate[], signal?: AbortSignal): Promise<void>;
59
70
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/providers/cryptpad/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACzB,MAAM,UAAU,CAAC;AAMlB,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2GAA2G;IAC3G,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CACZ,MAAM,EACN;QACE,KAAK,EAAE,iBAAiB,CAAC;QACzB,IAAI,EAAE,QAAQ,EAAE,CAAC;KAClB,CACF,CAAC;IACF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAsB;IAE1C,YAAY,OAAO,EAAE,qBAAqB,EAezC;IAED,wEAAwE;IACxE,OAAO,IAAI,mBAAmB,CAK7B;IAED,6DAA6D;IACvD,eAAe,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAK3D;IAED;;;OAGG;IACG,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoDvE;IAED;;;OAGG;IACG,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAMlF;IAED;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB1F;IAED;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,QAAQ,EAAE,EAChB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACzD,OAAO,CAAC,MAAM,CAAC,CA+EjB;CACF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/providers/cryptpad/client.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACzB,MAAM,UAAU,CAAC;AAMlB,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2GAA2G;IAC3G,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CACZ,MAAM,EACN;QACE,KAAK,EAAE,iBAAiB,CAAC;QACzB,IAAI,EAAE,QAAQ,EAAE,CAAC;KAClB,CACF,CAAC;IACF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAsB;IAE1C,YAAY,OAAO,EAAE,qBAAqB,EAezC;IAED,wEAAwE;IACxE,OAAO,IAAI,mBAAmB,CAK7B;IAED,6DAA6D;IACvD,eAAe,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAK3D;IAED;;;;;;;OAOG;IACG,mBAAmB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAmB/D;IAED;;;OAGG;IACG,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoDvE;IAED;;;OAGG;IACG,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAMlF;IAED;;;;OAIG;IACG,eAAe,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B1F;IAED;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,QAAQ,EAAE,EAChB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACzD,OAAO,CAAC,MAAM,CAAC,CA+EjB;CACF"}
@@ -649,6 +649,29 @@ var CryptPadClient = class {
649
649
  return resolveCryptPadWebsocketUrl(this.parsedUrl.origin, signal);
650
650
  }
651
651
  /**
652
+ * Initializes the OnlyOffice RT channel for a brand-new CryptPad sheet that has never
653
+ * been opened in a browser. Generates a random 32-hex channel ID, broadcasts it as an
654
+ * encrypted metadata patch on the Netflux channel (the same thing the OnlyOffice browser
655
+ * client does on first open), and returns the new RT channel ID.
656
+ *
657
+ * This removes the requirement to open the sheet in a browser before the CLI can write to it.
658
+ */
659
+ async initializeRtChannel(signal) {
660
+ const wsUrl = await this.getWebsocketUrl(signal);
661
+ const { channelHex, cryptKey } = this.getKeys();
662
+ const newRtChannel = crypto.randomBytes(16).toString("hex");
663
+ const innerJson = JSON.stringify({ content: { channel: newRtChannel } });
664
+ await broadcastChannelMessage(wsUrl, channelHex, cryptKey, JSON.stringify([1, [[
665
+ 0,
666
+ 0,
667
+ innerJson
668
+ ]]]), {
669
+ timeoutMs: this.timeoutMs,
670
+ signal
671
+ });
672
+ return newRtChannel;
673
+ }
674
+ /**
652
675
  * Fetches the complete sheet data, including raw cell coordinate mappings,
653
676
  * multi-sheet tabs, and structured rows formatted for translation ingestion.
654
677
  */
@@ -699,14 +722,22 @@ var CryptPadClient = class {
699
722
  }
700
723
  /**
701
724
  * Broadcasts cell updates to the OnlyOffice real-time collaboration channel.
725
+ * If the sheet has never been opened in a browser (RT channel is None), it is
726
+ * automatically initialized headlessly — no browser required.
702
727
  */
703
728
  async sendCellUpdates(updates, signal) {
704
729
  if (updates.length === 0) return;
705
- const rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
706
- if (!rtChannel) throw new Error(`CryptPad sheet "${this.parsedUrl.cleanUrl}" does not have an active OnlyOffice RT channel.`);
730
+ let rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
731
+ if (!rtChannel) {
732
+ console.log("No OnlyOffice RT channel found. Initializing headlessly (no browser required)...");
733
+ rtChannel = await this.initializeRtChannel(signal);
734
+ console.log(`RT channel initialized: ${rtChannel}`);
735
+ await new Promise((r) => setTimeout(r, 800));
736
+ }
707
737
  const wsUrl = await this.getWebsocketUrl(signal);
708
738
  const { cryptKey } = this.getKeys();
709
- await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, buildOnlyOfficeChangePayload(updates), {
739
+ const payload = buildOnlyOfficeChangePayload(updates);
740
+ await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, payload, {
710
741
  timeoutMs: this.timeoutMs,
711
742
  signal
712
743
  });
@@ -1660,7 +1691,7 @@ async function main() {
1660
1691
  console.log(` App: ${data.metadata.app}`);
1661
1692
  console.log(` Mode: ${data.metadata.mode}`);
1662
1693
  console.log(` Channel ID: ${data.metadata.channelId}`);
1663
- console.log(` RT Channel: ${data.metadata.rtChannelId ?? "None"}`);
1694
+ console.log(` RT Channel: ${data.metadata.rtChannelId ?? "None (will auto-initialize on first push)"}`);
1664
1695
  console.log(` Total Cells: ${Object.keys(data.cells).length}`);
1665
1696
  console.log(` Sheet Tabs (${data.sheetNames.length}):`);
1666
1697
  for (const name of data.sheetNames) {
@@ -1481,6 +1481,29 @@ var CryptPadClient = class {
1481
1481
  return resolveCryptPadWebsocketUrl(this.parsedUrl.origin, signal);
1482
1482
  }
1483
1483
  /**
1484
+ * Initializes the OnlyOffice RT channel for a brand-new CryptPad sheet that has never
1485
+ * been opened in a browser. Generates a random 32-hex channel ID, broadcasts it as an
1486
+ * encrypted metadata patch on the Netflux channel (the same thing the OnlyOffice browser
1487
+ * client does on first open), and returns the new RT channel ID.
1488
+ *
1489
+ * This removes the requirement to open the sheet in a browser before the CLI can write to it.
1490
+ */
1491
+ async initializeRtChannel(signal) {
1492
+ const wsUrl = await this.getWebsocketUrl(signal);
1493
+ const { channelHex, cryptKey } = this.getKeys();
1494
+ const newRtChannel = crypto.randomBytes(16).toString("hex");
1495
+ const innerJson = JSON.stringify({ content: { channel: newRtChannel } });
1496
+ await broadcastChannelMessage(wsUrl, channelHex, cryptKey, JSON.stringify([1, [[
1497
+ 0,
1498
+ 0,
1499
+ innerJson
1500
+ ]]]), {
1501
+ timeoutMs: this.timeoutMs,
1502
+ signal
1503
+ });
1504
+ return newRtChannel;
1505
+ }
1506
+ /**
1484
1507
  * Fetches the complete sheet data, including raw cell coordinate mappings,
1485
1508
  * multi-sheet tabs, and structured rows formatted for translation ingestion.
1486
1509
  */
@@ -1531,14 +1554,22 @@ var CryptPadClient = class {
1531
1554
  }
1532
1555
  /**
1533
1556
  * Broadcasts cell updates to the OnlyOffice real-time collaboration channel.
1557
+ * If the sheet has never been opened in a browser (RT channel is None), it is
1558
+ * automatically initialized headlessly — no browser required.
1534
1559
  */
1535
1560
  async sendCellUpdates(updates, signal) {
1536
1561
  if (updates.length === 0) return;
1537
- const rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
1538
- if (!rtChannel) throw new Error(`CryptPad sheet "${this.parsedUrl.cleanUrl}" does not have an active OnlyOffice RT channel.`);
1562
+ let rtChannel = (await this.fetchSheetData(signal)).metadata.rtChannelId;
1563
+ if (!rtChannel) {
1564
+ console.log("No OnlyOffice RT channel found. Initializing headlessly (no browser required)...");
1565
+ rtChannel = await this.initializeRtChannel(signal);
1566
+ console.log(`RT channel initialized: ${rtChannel}`);
1567
+ await new Promise((r) => setTimeout(r, 800));
1568
+ }
1539
1569
  const wsUrl = await this.getWebsocketUrl(signal);
1540
1570
  const { cryptKey } = this.getKeys();
1541
- await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, buildOnlyOfficeChangePayload(updates), {
1571
+ const payload = buildOnlyOfficeChangePayload(updates);
1572
+ await broadcastChannelMessage(wsUrl, rtChannel, cryptKey, payload, {
1542
1573
  timeoutMs: this.timeoutMs,
1543
1574
  signal
1544
1575
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@el-j/google-sheet-translations",
3
- "version": "3.0.0-beta.5",
3
+ "version": "3.0.0-beta.6",
4
4
  "description": "A package to manage translations stored in Google Spreadsheets",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",