@arbidocs/sdk 0.3.46 → 0.3.48

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/browser.cjs CHANGED
@@ -3637,6 +3637,23 @@ async function generateEncryptedWorkspaceKey(arbi, wrappedKey, serverSessionKey,
3637
3637
  const workspaceKey = client.sealedBoxDecrypt(wrappedKey, encryptionKeyPair.secretKey);
3638
3638
  return client.sealKeyForSession(workspaceKey, serverSessionKey);
3639
3639
  }
3640
+ async function getRawWorkspaceKey(arbi, workspaceId, signingPrivateKeyBase64) {
3641
+ const { data: workspaces, error } = await arbi.fetch.GET("/v1/user/workspaces");
3642
+ if (error || !workspaces) {
3643
+ throw new ArbiError("Failed to fetch workspaces");
3644
+ }
3645
+ const ws = workspaces.find((w2) => w2.external_id === workspaceId);
3646
+ if (!ws || !ws.wrapped_key) {
3647
+ throw new ArbiError(`Workspace ${workspaceId} not found or has no encryption key`);
3648
+ }
3649
+ const signingPrivateKey = client.base64ToBytes(signingPrivateKeyBase64);
3650
+ const ed25519PublicKey = signingPrivateKey.slice(32, 64);
3651
+ const encryptionKeyPair = client.deriveEncryptionKeypairFromSigning({
3652
+ publicKey: ed25519PublicKey,
3653
+ secretKey: signingPrivateKey
3654
+ });
3655
+ return client.sealedBoxDecrypt(ws.wrapped_key, encryptionKeyPair.secretKey);
3656
+ }
3640
3657
  async function selectWorkspaceById(arbi, workspaceId, serverSessionKey, signingPrivateKeyBase64) {
3641
3658
  const { data: workspaces, error } = await arbi.fetch.GET("/v1/user/workspaces");
3642
3659
  if (error || !workspaces) {
@@ -4106,6 +4123,7 @@ __export(documents_exports, {
4106
4123
  listAll: () => listAll,
4107
4124
  listDocuments: () => listDocuments,
4108
4125
  listPaginated: () => listPaginated,
4126
+ mimeFromName: () => mimeFromName,
4109
4127
  sanitizeFolderPath: () => sanitizeFolderPath,
4110
4128
  updateDocuments: () => updateDocuments,
4111
4129
  uploadDocumentsDirect: () => uploadDocumentsDirect,
@@ -4160,10 +4178,17 @@ async function listDocuments(arbi) {
4160
4178
  return requireData(await arbi.fetch.GET("/v1/document/list"), "Failed to fetch documents");
4161
4179
  }
4162
4180
  async function* listPaginated(arbi, options = {}) {
4163
- const { pageSize = 5e3, order = "id_asc", fields, signal, lookahead = 3 } = options;
4164
- const fetchPage = async (pageOffset) => {
4181
+ const {
4182
+ pageSize = 5e3,
4183
+ firstPageSize,
4184
+ order = "id_asc",
4185
+ fields,
4186
+ signal,
4187
+ lookahead = 1
4188
+ } = options;
4189
+ const fetchPage = async (pageOffset, limit) => {
4165
4190
  const query = {
4166
- limit: pageSize,
4191
+ limit,
4167
4192
  offset: pageOffset,
4168
4193
  order
4169
4194
  };
@@ -4186,19 +4211,21 @@ async function* listPaginated(arbi, options = {}) {
4186
4211
  const queue = [];
4187
4212
  const tryEnqueue = () => {
4188
4213
  while (!done && queue.length < depth && issued < MAX_PAGES) {
4189
- const p2 = fetchPage(nextOffsetToIssue);
4190
- p2.catch(() => {
4214
+ const limit = issued === 0 && firstPageSize !== void 0 ? firstPageSize : pageSize;
4215
+ const promise = fetchPage(nextOffsetToIssue, limit);
4216
+ promise.catch(() => {
4191
4217
  });
4192
- queue.push(p2);
4193
- nextOffsetToIssue += pageSize;
4218
+ queue.push({ limit, promise });
4219
+ nextOffsetToIssue += limit;
4194
4220
  issued++;
4195
4221
  }
4196
4222
  };
4197
4223
  tryEnqueue();
4198
4224
  while (queue.length > 0 && !signal?.aborted) {
4199
- const page = await queue.shift();
4225
+ const { limit, promise } = queue.shift();
4226
+ const page = await promise;
4200
4227
  if (signal?.aborted) return;
4201
- const isShort = page.length < pageSize;
4228
+ const isShort = page.length < limit;
4202
4229
  if (isShort) done = true;
4203
4230
  tryEnqueue();
4204
4231
  if (page.length > 0) {
@@ -4246,11 +4273,11 @@ async function updateDocuments(arbi, documents) {
4246
4273
  "Failed to update documents"
4247
4274
  );
4248
4275
  }
4249
- async function uploadUrl(arbi, urls, workspaceId, shared = false) {
4276
+ async function uploadUrl(arbi, urls, shared = false) {
4250
4277
  return requireData(
4251
4278
  await arbi.fetch.POST("/v1/document/upload-url", {
4252
4279
  params: {
4253
- query: { urls, workspace_ext_id: workspaceId, shared }
4280
+ query: { urls, shared }
4254
4281
  }
4255
4282
  }),
4256
4283
  "Failed to upload from URLs"
@@ -4263,29 +4290,31 @@ async function getParsedContent(auth, docId, stage) {
4263
4290
  });
4264
4291
  return res.json();
4265
4292
  }
4266
- async function uploadFile(auth, workspaceId, fileData, fileName, options) {
4293
+ async function uploadFile(auth, fileData, fileName, options) {
4267
4294
  const formData = new FormData();
4268
4295
  formData.append("files", fileData, fileName);
4269
- const params = new URLSearchParams({ workspace_ext_id: workspaceId });
4296
+ const params = new URLSearchParams();
4270
4297
  if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
4271
4298
  if (options?.configExtId) params.set("config_ext_id", options.configExtId);
4299
+ const qs = params.toString();
4272
4300
  const res = await authenticatedFetch({
4273
4301
  ...auth,
4274
- path: `/v1/document/upload?${params.toString()}`,
4302
+ path: qs ? `/v1/document/upload?${qs}` : `/v1/document/upload`,
4275
4303
  method: "POST",
4276
4304
  body: formData
4277
4305
  });
4278
4306
  return res.json();
4279
4307
  }
4280
- async function uploadFiles(auth, workspaceId, files, options) {
4308
+ async function uploadFiles(auth, files, options) {
4281
4309
  const formData = new FormData();
4282
4310
  for (const f2 of files) formData.append("files", f2.data, f2.name);
4283
- const params = new URLSearchParams({ workspace_ext_id: workspaceId });
4311
+ const params = new URLSearchParams();
4284
4312
  if (options?.folder) params.set("folder", sanitizeFolderPath(options.folder));
4285
4313
  if (options?.configExtId) params.set("config_ext_id", options.configExtId);
4314
+ const qs = params.toString();
4286
4315
  const res = await authenticatedFetch({
4287
4316
  ...auth,
4288
- path: `/v1/document/upload?${params.toString()}`,
4317
+ path: qs ? `/v1/document/upload?${qs}` : `/v1/document/upload`,
4289
4318
  method: "POST",
4290
4319
  body: formData
4291
4320
  });
@@ -4297,6 +4326,68 @@ async function downloadDocument(auth, docId) {
4297
4326
  path: `/v1/document/${docId}/download`
4298
4327
  });
4299
4328
  }
4329
+ function mimeFromName(name) {
4330
+ const dot = name.lastIndexOf(".");
4331
+ const ext = dot === -1 ? "" : name.slice(dot).toLowerCase();
4332
+ switch (ext) {
4333
+ case ".pdf":
4334
+ return "application/pdf";
4335
+ case ".txt":
4336
+ return "text/plain";
4337
+ case ".md":
4338
+ return "text/markdown";
4339
+ case ".html":
4340
+ case ".htm":
4341
+ return "text/html";
4342
+ case ".doc":
4343
+ return "application/msword";
4344
+ case ".docx":
4345
+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
4346
+ case ".xls":
4347
+ return "application/vnd.ms-excel";
4348
+ case ".xlsx":
4349
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
4350
+ case ".ppt":
4351
+ return "application/vnd.ms-powerpoint";
4352
+ case ".pptx":
4353
+ return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
4354
+ case ".eml":
4355
+ return "message/rfc822";
4356
+ default:
4357
+ return void 0;
4358
+ }
4359
+ }
4360
+ async function presignedPut(url, body, onProgress) {
4361
+ const XhrGlobal = globalThis.XMLHttpRequest;
4362
+ if (XhrGlobal) {
4363
+ await new Promise((resolve, reject) => {
4364
+ const xhr = new XhrGlobal();
4365
+ xhr.open("PUT", url);
4366
+ if (onProgress) {
4367
+ xhr.upload.addEventListener("progress", (e) => {
4368
+ if (e.lengthComputable) onProgress(e.loaded, e.total);
4369
+ });
4370
+ }
4371
+ xhr.addEventListener("load", () => {
4372
+ if (xhr.status >= 200 && xhr.status < 300) {
4373
+ resolve();
4374
+ } else {
4375
+ const snippet = (xhr.responseText || "").slice(0, 200);
4376
+ reject(new Error(`Presigned PUT failed: ${xhr.status} ${snippet}`));
4377
+ }
4378
+ });
4379
+ xhr.addEventListener("error", () => reject(new Error("Presigned PUT network error")));
4380
+ xhr.addEventListener("abort", () => reject(new Error("Presigned PUT aborted")));
4381
+ xhr.send(body);
4382
+ });
4383
+ return;
4384
+ }
4385
+ const putRes = await fetch(url, { method: "PUT", body });
4386
+ if (!putRes.ok) {
4387
+ const snippet = await putRes.text().catch(() => "");
4388
+ throw new Error(`Presigned PUT failed: ${putRes.status} ${snippet.slice(0, 200)}`);
4389
+ }
4390
+ }
4300
4391
  async function uploadDocumentsDirect(arbi, workspaceKey, files, options) {
4301
4392
  if (files.length === 0) return { doc_ext_ids: [], skipped: [] };
4302
4393
  const SECRETBOX_ENVELOPE_OVERHEAD = 40;
@@ -4329,6 +4420,11 @@ async function uploadDocumentsDirect(arbi, workspaceKey, files, options) {
4329
4420
  }
4330
4421
  const skipped = [];
4331
4422
  const toCommit = [];
4423
+ const indexByName = /* @__PURE__ */ new Map();
4424
+ files.forEach((f2, i2) => {
4425
+ if (!indexByName.has(f2.name)) indexByName.set(f2.name, []);
4426
+ indexByName.get(f2.name).push(i2);
4427
+ });
4332
4428
  for (const item of items) {
4333
4429
  if (item.status !== "uploading" || !item.upload_url || !item.doc_ext_id) {
4334
4430
  skipped.push({
@@ -4338,21 +4434,34 @@ async function uploadDocumentsDirect(arbi, workspaceKey, files, options) {
4338
4434
  continue;
4339
4435
  }
4340
4436
  const queue = bytesByName.get(item.file_name);
4341
- if (!queue || queue.length === 0) {
4437
+ const idxQueue = indexByName.get(item.file_name);
4438
+ if (!queue || queue.length === 0 || !idxQueue || idxQueue.length === 0) {
4342
4439
  throw new Error(`upload-init returned slot for unknown file: ${item.file_name}`);
4343
4440
  }
4344
4441
  const raw = queue.shift();
4442
+ const fileIndex = idxQueue.shift();
4345
4443
  const ciphertext = client.encryptFile(raw, workspaceKey);
4346
- const putRes = await fetch(item.upload_url, {
4347
- method: "PUT",
4348
- body: ciphertext
4349
- });
4350
- if (!putRes.ok) {
4351
- const body = await putRes.text().catch(() => "");
4444
+ const total = ciphertext.length;
4445
+ try {
4446
+ await presignedPut(item.upload_url, ciphertext, (loaded, putTotal) => {
4447
+ options?.onBytesProgress?.({
4448
+ fileIndex,
4449
+ fileName: item.file_name,
4450
+ loaded,
4451
+ total: putTotal
4452
+ });
4453
+ });
4454
+ } catch (err) {
4352
4455
  throw new Error(
4353
- `Presigned PUT for ${item.doc_ext_id} failed: ${putRes.status} ${body.slice(0, 200)}`
4456
+ `Presigned PUT for ${item.doc_ext_id} failed: ${err instanceof Error ? err.message : String(err)}`
4354
4457
  );
4355
4458
  }
4459
+ options?.onBytesProgress?.({
4460
+ fileIndex,
4461
+ fileName: item.file_name,
4462
+ loaded: total,
4463
+ total
4464
+ });
4356
4465
  toCommit.push(item.doc_ext_id);
4357
4466
  }
4358
4467
  if (toCommit.length === 0) {
@@ -5193,19 +5302,19 @@ var Arbi = class {
5193
5302
  get: (externalIds) => getDocuments(this.requireClient(), externalIds),
5194
5303
  delete: (externalIds) => deleteDocuments(this.requireClient(), externalIds),
5195
5304
  update: (documents) => updateDocuments(this.requireClient(), documents),
5196
- uploadUrl: (urls, shared, workspaceId) => uploadUrl(
5197
- this.requireClient(),
5198
- urls,
5199
- workspaceId ?? this.requireWorkspace(),
5200
- shared
5201
- ),
5202
- uploadFile: (fileData, fileName, options) => uploadFile(
5203
- this.getAuthHeaders(),
5204
- options?.workspaceId ?? this.requireWorkspace(),
5205
- fileData,
5206
- fileName,
5207
- options?.folder ? { folder: options.folder } : void 0
5208
- ),
5305
+ uploadUrl: (urls, shared) => {
5306
+ this.requireWorkspace();
5307
+ return uploadUrl(this.requireClient(), urls, shared);
5308
+ },
5309
+ uploadFile: (fileData, fileName, options) => {
5310
+ this.requireWorkspace();
5311
+ return uploadFile(
5312
+ this.getAuthHeaders(),
5313
+ fileData,
5314
+ fileName,
5315
+ options?.folder ? { folder: options.folder } : void 0
5316
+ );
5317
+ },
5209
5318
  download: (docId) => downloadDocument(this.getAuthHeaders(), docId),
5210
5319
  getParsedContent: (docId, stage) => getParsedContent(this.getAuthHeaders(), docId, stage)
5211
5320
  };
@@ -5443,6 +5552,7 @@ exports.formatFileSize = formatFileSize;
5443
5552
  exports.formatUserName = formatUserName;
5444
5553
  exports.formatWorkspaceChoices = formatWorkspaceChoices;
5445
5554
  exports.getErrorMessage = getErrorMessage;
5555
+ exports.getRawWorkspaceKey = getRawWorkspaceKey;
5446
5556
  exports.health = health_exports;
5447
5557
  exports.parseSSEEvents = parseSSEEvents;
5448
5558
  exports.performPasswordLogin = performPasswordLogin;