@lacrous/omnicloud 0.2.32 → 0.2.34

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/index.cjs CHANGED
@@ -77,6 +77,51 @@ var OmniCloudError = class extends Error {
77
77
  details;
78
78
  };
79
79
  var DEFAULT_RETRY = { attempts: 3, baseDelayMs: 300 };
80
+ function randomId() {
81
+ return globalThis.crypto.randomUUID().replace(/-/g, "");
82
+ }
83
+ function multipartHead(boundary, fields, fileName) {
84
+ const parts = fields.map(
85
+ ([name, value]) => `--${boundary}\r
86
+ Content-Disposition: form-data; name="${name}"\r
87
+ \r
88
+ ${value}\r
89
+ `
90
+ );
91
+ const safeName = fileName.replace(/["\\\r\n]/g, "_");
92
+ parts.push(
93
+ `--${boundary}\r
94
+ Content-Disposition: form-data; name="file"; filename="${safeName}"\r
95
+ Content-Type: application/octet-stream\r
96
+ \r
97
+ `
98
+ );
99
+ return parts.join("");
100
+ }
101
+ function nodeMultipartStream(head, file, tail) {
102
+ const encoder = new TextEncoder();
103
+ const iterator = file[Symbol.asyncIterator]();
104
+ let sentHead = false;
105
+ return new ReadableStream({
106
+ async pull(controller) {
107
+ if (!sentHead) {
108
+ sentHead = true;
109
+ controller.enqueue(encoder.encode(head));
110
+ return;
111
+ }
112
+ const { done, value } = await iterator.next();
113
+ if (done) {
114
+ controller.enqueue(encoder.encode(tail));
115
+ controller.close();
116
+ return;
117
+ }
118
+ controller.enqueue(typeof value === "string" ? encoder.encode(value) : new Uint8Array(value));
119
+ },
120
+ async cancel(reason) {
121
+ await iterator.return?.(reason);
122
+ }
123
+ });
124
+ }
80
125
  function sleep(ms, signal) {
81
126
  return new Promise((resolve, reject) => {
82
127
  const timer = setTimeout(resolve, ms);
@@ -368,10 +413,13 @@ var FilesApi = class {
368
413
  async upload(input, options = {}) {
369
414
  const attempts = Math.max(1, (options.retry ?? 2) + 1);
370
415
  let lastError;
371
- const blob = await toBlob(input);
416
+ const source = input.path !== void 0 && this.canStreamFromDisk() ? { kind: "path", path: input.path } : { kind: "blob", blob: await toBlob(input) };
372
417
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
373
418
  try {
374
- return await this.uploadOnce(blob, input.name, options);
419
+ if (source.kind === "path") {
420
+ return await this.uploadPathOnce(source.path, input.name, options);
421
+ }
422
+ return await this.uploadOnce(source.blob, input.name, options);
375
423
  } catch (error) {
376
424
  lastError = error;
377
425
  if (options.signal?.aborted) throw error;
@@ -381,6 +429,46 @@ var FilesApi = class {
381
429
  }
382
430
  throw lastError;
383
431
  }
432
+ canStreamFromDisk() {
433
+ return typeof XMLHttpRequest !== "function" && typeof ReadableStream === "function";
434
+ }
435
+ /**
436
+ * Node: streams a file from disk as a multipart body. The file is re-opened on
437
+ * every attempt, so a retry sends the same bytes without holding them in memory.
438
+ */
439
+ async uploadPathOnce(path, name, options) {
440
+ const { createReadStream, statSync } = await import("fs");
441
+ const size = statSync(path).size;
442
+ const boundary = `omnicloud-${randomId()}`;
443
+ const fields = [];
444
+ if (options.folderId) fields.push(["folderId", options.folderId]);
445
+ const head = multipartHead(boundary, fields, name);
446
+ const tail = `\r
447
+ --${boundary}--\r
448
+ `;
449
+ const body = nodeMultipartStream(head, createReadStream(path), tail);
450
+ const response = await this.client.fetchImpl(`${this.client.baseUrl}/api/files`, {
451
+ method: "POST",
452
+ headers: {
453
+ ...this.client.defaultHeaders,
454
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
455
+ "Content-Length": String(Buffer.byteLength(head) + size + Buffer.byteLength(tail))
456
+ },
457
+ body,
458
+ credentials: "include",
459
+ duplex: "half"
460
+ });
461
+ const text = await response.text();
462
+ if (!response.ok) {
463
+ const error = safeJsonParse(text)?.error;
464
+ throw new OmniCloudError(
465
+ response.status,
466
+ error?.code ?? "UPLOAD_FAILED",
467
+ error?.message ?? `Upload failed (${response.status})`
468
+ );
469
+ }
470
+ return JSON.parse(text).file;
471
+ }
384
472
  async uploadOnce(blob, name, options) {
385
473
  const form = new FormData();
386
474
  form.append("file", blob, name);
@@ -1381,6 +1469,9 @@ async function resolveItemQuery(folders, userId, input) {
1381
1469
  // ../core/src/services/auth-session-service.ts
1382
1470
  var import_node_crypto3 = require("crypto");
1383
1471
 
1472
+ // ../core/src/services/api-token-service.ts
1473
+ var import_node_crypto4 = require("crypto");
1474
+
1384
1475
  // ../core/src/services/version-retention.ts
1385
1476
  function versionsToPrune(versions, currentVersionId, policy, now) {
1386
1477
  const historical = versions.filter((version) => version.id !== currentVersionId).sort((a, b) => b.versionNumber - a.versionNumber);
@@ -1502,7 +1593,7 @@ function sanitizeFileName(name) {
1502
1593
  }
1503
1594
 
1504
1595
  // ../core/src/utils/request-fingerprint.ts
1505
- var import_node_crypto4 = require("crypto");
1596
+ var import_node_crypto5 = require("crypto");
1506
1597
  function uploadRequestFingerprint(input) {
1507
1598
  const contentHash = input.spooled?.sha256 ?? sha256Hex(input.data ?? Buffer.alloc(0));
1508
1599
  const canonical = JSON.stringify({
@@ -1512,7 +1603,7 @@ function uploadRequestFingerprint(input) {
1512
1603
  contentHash,
1513
1604
  size: input.spooled?.size ?? input.data?.byteLength ?? 0
1514
1605
  });
1515
- return (0, import_node_crypto4.createHash)("sha256").update(canonical).digest("hex");
1606
+ return (0, import_node_crypto5.createHash)("sha256").update(canonical).digest("hex");
1516
1607
  }
1517
1608
 
1518
1609
  // ../core/src/services/file-service.ts
@@ -2747,7 +2838,7 @@ var RecentService = class {
2747
2838
  };
2748
2839
 
2749
2840
  // ../core/src/utils/spool.ts
2750
- var import_node_crypto5 = require("crypto");
2841
+ var import_node_crypto6 = require("crypto");
2751
2842
  var import_node_fs = require("fs");
2752
2843
  var import_promises = require("fs/promises");
2753
2844
  var import_node_os = require("os");
@@ -2756,16 +2847,16 @@ var import_node_stream2 = require("stream");
2756
2847
  var import_promises2 = require("stream/promises");
2757
2848
 
2758
2849
  // ../core/src/utils/secret-box.ts
2759
- var import_node_crypto6 = require("crypto");
2850
+ var import_node_crypto7 = require("crypto");
2760
2851
 
2761
2852
  // ../telegram/src/client.ts
2762
2853
  var import_telegram = require("telegram");
2763
2854
  var import_sessions = require("telegram/sessions/index.js");
2764
2855
 
2765
2856
  // ../telegram/src/errors.ts
2766
- var import_errors12 = require("telegram/errors/index.js");
2857
+ var import_errors13 = require("telegram/errors/index.js");
2767
2858
  function isTelegramRpcError(error) {
2768
- return error instanceof import_errors12.RPCError;
2859
+ return error instanceof import_errors13.RPCError;
2769
2860
  }
2770
2861
  function isPasswordRequiredError(error) {
2771
2862
  return isTelegramRpcError(error) && error.errorMessage === "SESSION_PASSWORD_NEEDED";
@@ -3390,7 +3481,7 @@ var TelegramStorageProvider = class {
3390
3481
  };
3391
3482
 
3392
3483
  // ../telegram/src/connection.ts
3393
- var import_node_crypto7 = require("crypto");
3484
+ var import_node_crypto8 = require("crypto");
3394
3485
  var import_telegram3 = require("telegram");
3395
3486
  var import_uploads2 = require("telegram/client/uploads.js");
3396
3487
  var import_Password = require("telegram/Password.js");
@@ -3478,7 +3569,7 @@ var TelegramConnectionService = class {
3478
3569
  needsPassword: false,
3479
3570
  failure: null
3480
3571
  };
3481
- const flowId = (0, import_node_crypto7.randomUUID)();
3572
+ const flowId = (0, import_node_crypto8.randomUUID)();
3482
3573
  const token = await this.exportQrToken(client);
3483
3574
  this.qrFlows.set(flowId, flow);
3484
3575
  this.watchQrApproval(flowId, flow);