@thatopen/services 0.9.0 → 0.10.0

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/cli.js CHANGED
@@ -6795,6 +6795,7 @@ Object.assign(lookup, {
6795
6795
  io: lookup,
6796
6796
  connect: lookup
6797
6797
  });
6798
+ const STORAGE_BATCH_MAX = 100;
6798
6799
  function parseErrorBody(body) {
6799
6800
  try {
6800
6801
  const json = JSON.parse(body);
@@ -7161,6 +7162,36 @@ class EngineServicesClient {
7161
7162
  `${ITEM_PATH}/${encodeURIComponent(fileId)}/version/${encodeURIComponent(versionTag)}/metadata`
7162
7163
  );
7163
7164
  }
7165
+ /**
7166
+ * Batch variant of {@link getFileVersionMetadata}. Reads the metadata of
7167
+ * many (file, version) pairs in one request instead of one request per pair,
7168
+ * which is what a file list needs when it hydrates a whole page at once.
7169
+ *
7170
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
7171
+ * requests automatically. Entries come back in the order they were asked
7172
+ * for; a pair the caller cannot read carries an `error` instead of
7173
+ * `metadata`, so one bad id does not lose the whole page.
7174
+ *
7175
+ * @param entries - The `{ itemId, versionTag }` pairs to read.
7176
+ * @param params - Optional flags such as `withDraft`.
7177
+ */
7178
+ async getFileVersionMetadataBatch(entries, params) {
7179
+ const { withDraft } = params || {};
7180
+ return await this.#requestBatches(
7181
+ entries,
7182
+ async (chunk) => (await this.#requestApi(
7183
+ "POST",
7184
+ `${ITEM_PATH}/batch/version-metadata`,
7185
+ {
7186
+ body: JSON.stringify({
7187
+ entries: chunk,
7188
+ ...withDraft !== void 0 && { withDraft }
7189
+ }),
7190
+ contentType: "application/json"
7191
+ }
7192
+ )).results
7193
+ );
7194
+ }
7164
7195
  // ─── Folders ─────────────────────────────────────────────────────
7165
7196
  /**
7166
7197
  * Lists all folders accessible by the current token.
@@ -7187,6 +7218,32 @@ class EngineServicesClient {
7187
7218
  `${FOLDER_PATH}/${folderId}`
7188
7219
  );
7189
7220
  }
7221
+ /**
7222
+ * Batch variant of {@link getFolder}. Resolves a known set of folder ids in
7223
+ * one request — for example the parents behind a file list's breadcrumbs.
7224
+ *
7225
+ * Reach for {@link listFolders} instead when the whole (project) tree is
7226
+ * wanted; this is for the case where the ids are already known.
7227
+ *
7228
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
7229
+ * requests automatically. A folder the caller cannot read carries an
7230
+ * `error` instead of `folder`.
7231
+ *
7232
+ * @param folderIds - The folder ids to resolve.
7233
+ */
7234
+ async getFoldersBatch(folderIds) {
7235
+ return await this.#requestBatches(
7236
+ folderIds,
7237
+ async (chunk) => (await this.#requestApi(
7238
+ "POST",
7239
+ `${ITEM_PATH}/batch/folders`,
7240
+ {
7241
+ body: JSON.stringify({ folderIds: chunk }),
7242
+ contentType: "application/json"
7243
+ }
7244
+ )).results
7245
+ );
7246
+ }
7190
7247
  /**
7191
7248
  * Creates a new folder.
7192
7249
  * @param name - Display name for the folder.
@@ -7529,15 +7586,12 @@ return main;`);
7529
7586
  */
7530
7587
  async downloadAppBundle(appId, params) {
7531
7588
  const { versionTag, withDraft } = params || {};
7532
- return await this.#requestFile(
7533
- `${ITEM_PATH}/${appId}/download/bundle`,
7534
- {
7535
- query: {
7536
- ...versionTag && { versionTag },
7537
- ...withDraft && { withDraft }
7538
- }
7589
+ return await this.#requestFile(`${ITEM_PATH}/${appId}/download/bundle`, {
7590
+ query: {
7591
+ ...versionTag && { versionTag },
7592
+ ...withDraft && { withDraft }
7539
7593
  }
7540
- );
7594
+ });
7541
7595
  }
7542
7596
  /**
7543
7597
  * Archives (soft-deletes) an app.
@@ -7586,7 +7640,9 @@ return main;`);
7586
7640
  });
7587
7641
  if (!response.ok) {
7588
7642
  const text = await response.text().catch(() => "");
7589
- throw new Error(`Local server request failed: ${response.status} - ${text}`);
7643
+ throw new Error(
7644
+ `Local server request failed: ${response.status} - ${text}`
7645
+ );
7590
7646
  }
7591
7647
  return await response.json();
7592
7648
  }
@@ -7610,7 +7666,9 @@ return main;`);
7610
7666
  const response = await fetch(url2, { method: "POST" });
7611
7667
  if (!response.ok) {
7612
7668
  const text = await response.text().catch(() => "");
7613
- throw new Error(`Local server request failed: ${response.status} - ${text}`);
7669
+ throw new Error(
7670
+ `Local server request failed: ${response.status} - ${text}`
7671
+ );
7614
7672
  }
7615
7673
  return await response.json();
7616
7674
  }
@@ -7662,7 +7720,9 @@ return main;`);
7662
7720
  const response = await fetch(url2);
7663
7721
  if (!response.ok) {
7664
7722
  const text = await response.text().catch(() => "");
7665
- throw new Error(`Local server request failed: ${response.status} - ${text}`);
7723
+ throw new Error(
7724
+ `Local server request failed: ${response.status} - ${text}`
7725
+ );
7666
7726
  }
7667
7727
  return await response.json();
7668
7728
  }
@@ -7786,6 +7846,40 @@ return main;`);
7786
7846
  { ...expiresIn != null && { query: { expiresIn } } }
7787
7847
  );
7788
7848
  }
7849
+ /**
7850
+ * Batch variant of {@link getHiddenFileSignedUrl}. Mints presigned URLs for
7851
+ * many hidden files in one request.
7852
+ *
7853
+ * This is the right call for tile-based viewers (splats, point clouds),
7854
+ * which mint a URL per piece as the camera moves. One request here replaces
7855
+ * up to {@link STORAGE_BATCH_MAX} single calls, which keeps a busy session
7856
+ * well under the per-endpoint rate limit.
7857
+ *
7858
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
7859
+ * requests automatically. Entries come back in request order; a file that
7860
+ * is gone or not accessible carries an `error` instead of a `url`, so the
7861
+ * remaining tiles still load. Re-call to re-mint as `expiresAt` approaches.
7862
+ *
7863
+ * @param hiddenIds - The hidden files to sign.
7864
+ * @param expiresIn - Desired URL lifetime in seconds (60–3600). Defaults to
7865
+ * 900 (15 min) server-side; values are clamped to that range.
7866
+ */
7867
+ async getHiddenFileSignedUrlsBatch(hiddenIds, expiresIn) {
7868
+ return await this.#requestBatches(
7869
+ hiddenIds,
7870
+ async (chunk) => (await this.#requestApi(
7871
+ "POST",
7872
+ `${ITEM_PATH}/${HIDDEN_PATH}/signed-url/batch`,
7873
+ {
7874
+ body: JSON.stringify({
7875
+ hiddenFileIds: chunk,
7876
+ ...expiresIn != null && { expiresIn }
7877
+ }),
7878
+ contentType: "application/json"
7879
+ }
7880
+ )).results
7881
+ );
7882
+ }
7789
7883
  /**
7790
7884
  * Lists all hidden files attached to a parent item.
7791
7885
  * @param parentFileId - The parent item's unique identifier.
@@ -7818,11 +7912,9 @@ return main;`);
7818
7912
  async uploadItemIcon(itemId, icon) {
7819
7913
  const formData = new FormData();
7820
7914
  formData.append("icon", icon);
7821
- return await this.#requestApi(
7822
- "PUT",
7823
- `${ITEM_PATH}/${itemId}/icon`,
7824
- { body: formData }
7825
- );
7915
+ return await this.#requestApi("PUT", `${ITEM_PATH}/${itemId}/icon`, {
7916
+ body: formData
7917
+ });
7826
7918
  }
7827
7919
  /**
7828
7920
  * Downloads the icon for an item as a binary stream.
@@ -7892,6 +7984,38 @@ return main;`);
7892
7984
  { query: params }
7893
7985
  );
7894
7986
  }
7987
+ /**
7988
+ * Batch variant of {@link listVersions}. Lists the versions of many items in
7989
+ * one request, which is what a file panel needs to render a whole page.
7990
+ *
7991
+ * The returned versions are the full records, metadata included, so a list
7992
+ * that only needs version metadata does not need
7993
+ * {@link getFileVersionMetadataBatch} on top.
7994
+ *
7995
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
7996
+ * requests automatically. An item the caller cannot read carries an `error`
7997
+ * instead of `versions`.
7998
+ *
7999
+ * @param itemIds - The items whose versions to list.
8000
+ * @param params - Optional `{ archived }` filter, applied to every item.
8001
+ */
8002
+ async listVersionsBatch(itemIds, params = {}) {
8003
+ const { archived } = params;
8004
+ return await this.#requestBatches(
8005
+ itemIds,
8006
+ async (chunk) => (await this.#requestApi(
8007
+ "POST",
8008
+ `${ITEM_PATH}/batch/versions`,
8009
+ {
8010
+ body: JSON.stringify({
8011
+ itemIds: chunk,
8012
+ ...archived !== void 0 && { archived }
8013
+ }),
8014
+ contentType: "application/json"
8015
+ }
8016
+ )).results
8017
+ );
8018
+ }
7895
8019
  /**
7896
8020
  * Archives a version of an item. Archived versions remain available via
7897
8021
  * `listVersions({ archived: true })` and can be recovered or permanently
@@ -7943,6 +8067,21 @@ return main;`);
7943
8067
  // checkPermission, checkPermissionBatch) live on `PlatformClient` — they
7944
8068
  // cannot be called with an access token.
7945
8069
  // ─── Private Helpers ─────────────────────────────────────────────
8070
+ /**
8071
+ * Splits `inputs` into chunks the API accepts and concatenates the results,
8072
+ * so callers never have to think about the batch ceiling. Chunks go out
8073
+ * together; an empty input skips the network entirely, since the API
8074
+ * rejects an empty batch.
8075
+ */
8076
+ async #requestBatches(inputs, requestChunk) {
8077
+ if (!inputs.length) return [];
8078
+ const chunks = [];
8079
+ for (let start = 0; start < inputs.length; start += STORAGE_BATCH_MAX) {
8080
+ chunks.push(inputs.slice(start, start + STORAGE_BATCH_MAX));
8081
+ }
8082
+ const responses = await Promise.all(chunks.map(requestChunk));
8083
+ return responses.flat();
8084
+ }
7946
8085
  async #downloadItem(itemId, params) {
7947
8086
  const { versionTag, withDraft } = params || {};
7948
8087
  return await this.#requestFile(`${ITEM_PATH}/${itemId}/download`, {
@@ -2,6 +2,7 @@ import { ExecutionEntity, ExecutionSuscriptionReturnType } from '../types/execut
2
2
  import { AppVersionProps, ComponentVersionProps, ComponentItem, AppItem, Item, ItemFolder, ItemVersion, ItemWithVersions } from '../types/items';
3
3
  import { CreateItemResponse, UpdateItemResponse } from '../types/response';
4
4
  import { CreateHiddenItemResult, CreateHiddenItemsBatchResult, HiddenFileEntity, Metadata } from '../types/files';
5
+ import { HiddenFileSignedUrlBatchEntry, ItemFoldersBatchEntry, ItemVersionsBatchEntry, VersionMetadataBatchEntry, VersionMetadataBatchRequest } from '../types/batch';
5
6
  import { ThatOpenContext } from '../types/context';
6
7
  import { NpmCredentials } from '../types/npm';
7
8
 
@@ -305,6 +306,22 @@ export declare class EngineServicesClient {
305
306
  deleteFileVersionMetadata(fileId: string, versionTag: string): Promise<{
306
307
  success: boolean;
307
308
  }>;
309
+ /**
310
+ * Batch variant of {@link getFileVersionMetadata}. Reads the metadata of
311
+ * many (file, version) pairs in one request instead of one request per pair,
312
+ * which is what a file list needs when it hydrates a whole page at once.
313
+ *
314
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
315
+ * requests automatically. Entries come back in the order they were asked
316
+ * for; a pair the caller cannot read carries an `error` instead of
317
+ * `metadata`, so one bad id does not lose the whole page.
318
+ *
319
+ * @param entries - The `{ itemId, versionTag }` pairs to read.
320
+ * @param params - Optional flags such as `withDraft`.
321
+ */
322
+ getFileVersionMetadataBatch(entries: VersionMetadataBatchRequest[], params?: {
323
+ withDraft?: boolean;
324
+ }): Promise<VersionMetadataBatchEntry[]>;
308
325
  /**
309
326
  * Lists all folders accessible by the current token.
310
327
  * @param params - Optional filters for parent folder and archive status.
@@ -324,6 +341,20 @@ export declare class EngineServicesClient {
324
341
  * @param folderId - The folder's unique identifier.
325
342
  */
326
343
  getFolder(folderId: string): Promise<ItemFolder>;
344
+ /**
345
+ * Batch variant of {@link getFolder}. Resolves a known set of folder ids in
346
+ * one request — for example the parents behind a file list's breadcrumbs.
347
+ *
348
+ * Reach for {@link listFolders} instead when the whole (project) tree is
349
+ * wanted; this is for the case where the ids are already known.
350
+ *
351
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
352
+ * requests automatically. A folder the caller cannot read carries an
353
+ * `error` instead of `folder`.
354
+ *
355
+ * @param folderIds - The folder ids to resolve.
356
+ */
357
+ getFoldersBatch(folderIds: string[]): Promise<ItemFoldersBatchEntry[]>;
327
358
  /**
328
359
  * Creates a new folder.
329
360
  * @param name - Display name for the folder.
@@ -638,6 +669,25 @@ export declare class EngineServicesClient {
638
669
  url: string;
639
670
  expiresAt: string;
640
671
  }>;
672
+ /**
673
+ * Batch variant of {@link getHiddenFileSignedUrl}. Mints presigned URLs for
674
+ * many hidden files in one request.
675
+ *
676
+ * This is the right call for tile-based viewers (splats, point clouds),
677
+ * which mint a URL per piece as the camera moves. One request here replaces
678
+ * up to {@link STORAGE_BATCH_MAX} single calls, which keeps a busy session
679
+ * well under the per-endpoint rate limit.
680
+ *
681
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
682
+ * requests automatically. Entries come back in request order; a file that
683
+ * is gone or not accessible carries an `error` instead of a `url`, so the
684
+ * remaining tiles still load. Re-call to re-mint as `expiresAt` approaches.
685
+ *
686
+ * @param hiddenIds - The hidden files to sign.
687
+ * @param expiresIn - Desired URL lifetime in seconds (60–3600). Defaults to
688
+ * 900 (15 min) server-side; values are clamped to that range.
689
+ */
690
+ getHiddenFileSignedUrlsBatch(hiddenIds: string[], expiresIn?: number): Promise<HiddenFileSignedUrlBatchEntry[]>;
641
691
  /**
642
692
  * Lists all hidden files attached to a parent item.
643
693
  * @param parentFileId - The parent item's unique identifier.
@@ -700,6 +750,24 @@ export declare class EngineServicesClient {
700
750
  listVersions(itemId: string, params?: {
701
751
  archived?: boolean;
702
752
  }): Promise<ItemVersion[]>;
753
+ /**
754
+ * Batch variant of {@link listVersions}. Lists the versions of many items in
755
+ * one request, which is what a file panel needs to render a whole page.
756
+ *
757
+ * The returned versions are the full records, metadata included, so a list
758
+ * that only needs version metadata does not need
759
+ * {@link getFileVersionMetadataBatch} on top.
760
+ *
761
+ * Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
762
+ * requests automatically. An item the caller cannot read carries an `error`
763
+ * instead of `versions`.
764
+ *
765
+ * @param itemIds - The items whose versions to list.
766
+ * @param params - Optional `{ archived }` filter, applied to every item.
767
+ */
768
+ listVersionsBatch(itemIds: string[], params?: {
769
+ archived?: boolean;
770
+ }): Promise<ItemVersionsBatchEntry[]>;
703
771
  /**
704
772
  * Archives a version of an item. Archived versions remain available via
705
773
  * `listVersions({ archived: true })` and can be recovered or permanently
@@ -0,0 +1 @@
1
+ export {};