@thatopen/services 0.9.1 → 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 +155 -16
- package/dist/core/client.d.ts +68 -0
- package/dist/core/examples/batch-reads.d.ts +1 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +470 -360
- package/dist/types/batch.d.ts +36 -0
- package/docs/client/paths.json +4 -0
- package/package.json +1 -1
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
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
7823
|
-
|
|
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`, {
|
package/dist/core/client.d.ts
CHANGED
|
@@ -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 {};
|