@thatopen/services 0.9.1 → 0.11.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 +209 -26
- package/dist/core/client.d.ts +78 -2
- package/dist/core/examples/batch-reads.d.ts +1 -0
- package/dist/core/request-error.d.ts +17 -1
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +647 -501
- package/dist/types/batch.d.ts +36 -0
- package/docs/ai-quickstart.md +5 -0
- package/docs/client/paths.json +4 -0
- package/docs/rate-limits.md +123 -0
- package/package.json +1 -1
- package/resources/AGENTS.md +11 -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);
|
|
@@ -6810,12 +6811,22 @@ function parseErrorBody(body) {
|
|
|
6810
6811
|
}
|
|
6811
6812
|
return {};
|
|
6812
6813
|
}
|
|
6814
|
+
function retryAfterFromDetails(details) {
|
|
6815
|
+
if (!details || typeof details !== "object") return void 0;
|
|
6816
|
+
const value2 = details.retryAfter;
|
|
6817
|
+
return typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0 ? value2 : void 0;
|
|
6818
|
+
}
|
|
6813
6819
|
class RequestError extends Error {
|
|
6814
6820
|
status;
|
|
6815
6821
|
code;
|
|
6816
6822
|
details;
|
|
6817
6823
|
body;
|
|
6818
|
-
|
|
6824
|
+
/**
|
|
6825
|
+
* Seconds to wait before retrying, taken from the `Retry-After` header or
|
|
6826
|
+
* from `details.retryAfter`. Only present on rate-limited (429) responses.
|
|
6827
|
+
*/
|
|
6828
|
+
retryAfter;
|
|
6829
|
+
constructor(status, statusText, body, retryAfter) {
|
|
6819
6830
|
const parsed = parseErrorBody(body);
|
|
6820
6831
|
super(parsed.message ?? `${statusText || "Request failed"} (${status})`);
|
|
6821
6832
|
this.name = "RequestError";
|
|
@@ -6823,6 +6834,7 @@ class RequestError extends Error {
|
|
|
6823
6834
|
this.code = parsed.code;
|
|
6824
6835
|
this.details = parsed.details;
|
|
6825
6836
|
this.body = body;
|
|
6837
|
+
this.retryAfter = retryAfter ?? retryAfterFromDetails(parsed.details);
|
|
6826
6838
|
}
|
|
6827
6839
|
}
|
|
6828
6840
|
const FOLDER_PATH = "item/folder";
|
|
@@ -6833,6 +6845,35 @@ const HIDDEN_PATH = "hidden";
|
|
|
6833
6845
|
const ITEM_TYPE_FILE = "FILE";
|
|
6834
6846
|
const ITEM_TYPE_COMPONENT = "TOOL";
|
|
6835
6847
|
const ITEM_TYPE_APP = "APP";
|
|
6848
|
+
const RETRY_BASE_DELAY_MS = 500;
|
|
6849
|
+
const RETRY_MAX_DELAY_MS = 3e4;
|
|
6850
|
+
const RETRY_JITTER_RATIO = 0.25;
|
|
6851
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
6852
|
+
function parseRetryAfter(response) {
|
|
6853
|
+
const raw = response.headers?.get?.("Retry-After");
|
|
6854
|
+
if (!raw) return void 0;
|
|
6855
|
+
const seconds = Number(raw);
|
|
6856
|
+
if (Number.isFinite(seconds)) return Math.max(0, seconds);
|
|
6857
|
+
const timestamp = Date.parse(raw);
|
|
6858
|
+
if (Number.isNaN(timestamp)) return void 0;
|
|
6859
|
+
return Math.max(0, (timestamp - Date.now()) / 1e3);
|
|
6860
|
+
}
|
|
6861
|
+
function isRetryable(error2) {
|
|
6862
|
+
if (!(error2 instanceof RequestError)) return true;
|
|
6863
|
+
return error2.status === 429 || error2.status >= 500;
|
|
6864
|
+
}
|
|
6865
|
+
function retryDelayMs(error2, attempt) {
|
|
6866
|
+
const backoff = Math.min(
|
|
6867
|
+
RETRY_BASE_DELAY_MS * 2 ** attempt,
|
|
6868
|
+
RETRY_MAX_DELAY_MS
|
|
6869
|
+
);
|
|
6870
|
+
const jitter = backoff * RETRY_JITTER_RATIO * Math.random();
|
|
6871
|
+
const serverDelay = error2 instanceof RequestError && error2.retryAfter != null ? error2.retryAfter * 1e3 : void 0;
|
|
6872
|
+
if (serverDelay != null) {
|
|
6873
|
+
return Math.min(serverDelay, RETRY_MAX_DELAY_MS) + jitter;
|
|
6874
|
+
}
|
|
6875
|
+
return backoff + jitter;
|
|
6876
|
+
}
|
|
6836
6877
|
class EngineServicesClient {
|
|
6837
6878
|
apiUrl;
|
|
6838
6879
|
accessToken;
|
|
@@ -6905,7 +6946,9 @@ class EngineServicesClient {
|
|
|
6905
6946
|
}
|
|
6906
6947
|
}
|
|
6907
6948
|
/**
|
|
6908
|
-
* Sets the number of automatic retries for failed requests.
|
|
6949
|
+
* Sets the number of automatic retries for failed requests. Retries use
|
|
6950
|
+
* exponential backoff with jitter and honour `Retry-After`; only network
|
|
6951
|
+
* failures, 429s and 5xx are retried.
|
|
6909
6952
|
* @param retries - Number of retries (0 = no retries).
|
|
6910
6953
|
*/
|
|
6911
6954
|
setRetries(retries) {
|
|
@@ -6965,7 +7008,7 @@ class EngineServicesClient {
|
|
|
6965
7008
|
return this.accessToken;
|
|
6966
7009
|
}
|
|
6967
7010
|
async #requestApi(method, path2, requestData) {
|
|
6968
|
-
const { body, query, contentType, retries, responseType } = requestData || {};
|
|
7011
|
+
const { body, query, contentType, retries, responseType, retryAttempt } = requestData || {};
|
|
6969
7012
|
const url2 = this.#buildUrl(path2);
|
|
6970
7013
|
const cleanQuery = this.#cleanData(query);
|
|
6971
7014
|
const token = await this.resolveAccessToken();
|
|
@@ -6991,7 +7034,8 @@ class EngineServicesClient {
|
|
|
6991
7034
|
throw new RequestError(
|
|
6992
7035
|
response.status,
|
|
6993
7036
|
response.statusText,
|
|
6994
|
-
textResponse
|
|
7037
|
+
textResponse,
|
|
7038
|
+
parseRetryAfter(response)
|
|
6995
7039
|
);
|
|
6996
7040
|
}
|
|
6997
7041
|
if (responseType === "blob") {
|
|
@@ -6999,16 +7043,17 @@ class EngineServicesClient {
|
|
|
6999
7043
|
}
|
|
7000
7044
|
return response.json().then((data) => data).catch(() => void 0);
|
|
7001
7045
|
} catch (e) {
|
|
7002
|
-
|
|
7003
|
-
if (
|
|
7004
|
-
|
|
7046
|
+
const retriesLeft = retries != null ? retries : this.retries;
|
|
7047
|
+
if (retriesLeft > 0 && isRetryable(e)) {
|
|
7048
|
+
const attempt = retryAttempt ?? 0;
|
|
7049
|
+
await sleep(retryDelayMs(e, attempt));
|
|
7005
7050
|
return await this.#requestApi(method, path2, {
|
|
7006
7051
|
...requestData,
|
|
7007
|
-
retries:
|
|
7052
|
+
retries: retriesLeft - 1,
|
|
7053
|
+
retryAttempt: attempt + 1
|
|
7008
7054
|
});
|
|
7009
|
-
} else {
|
|
7010
|
-
throw e;
|
|
7011
7055
|
}
|
|
7056
|
+
throw e;
|
|
7012
7057
|
}
|
|
7013
7058
|
}
|
|
7014
7059
|
/**
|
|
@@ -7161,6 +7206,36 @@ class EngineServicesClient {
|
|
|
7161
7206
|
`${ITEM_PATH}/${encodeURIComponent(fileId)}/version/${encodeURIComponent(versionTag)}/metadata`
|
|
7162
7207
|
);
|
|
7163
7208
|
}
|
|
7209
|
+
/**
|
|
7210
|
+
* Batch variant of {@link getFileVersionMetadata}. Reads the metadata of
|
|
7211
|
+
* many (file, version) pairs in one request instead of one request per pair,
|
|
7212
|
+
* which is what a file list needs when it hydrates a whole page at once.
|
|
7213
|
+
*
|
|
7214
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
7215
|
+
* requests automatically. Entries come back in the order they were asked
|
|
7216
|
+
* for; a pair the caller cannot read carries an `error` instead of
|
|
7217
|
+
* `metadata`, so one bad id does not lose the whole page.
|
|
7218
|
+
*
|
|
7219
|
+
* @param entries - The `{ itemId, versionTag }` pairs to read.
|
|
7220
|
+
* @param params - Optional flags such as `withDraft`.
|
|
7221
|
+
*/
|
|
7222
|
+
async getFileVersionMetadataBatch(entries, params) {
|
|
7223
|
+
const { withDraft } = params || {};
|
|
7224
|
+
return await this.#requestBatches(
|
|
7225
|
+
entries,
|
|
7226
|
+
async (chunk) => (await this.#requestApi(
|
|
7227
|
+
"POST",
|
|
7228
|
+
`${ITEM_PATH}/batch/version-metadata`,
|
|
7229
|
+
{
|
|
7230
|
+
body: JSON.stringify({
|
|
7231
|
+
entries: chunk,
|
|
7232
|
+
...withDraft !== void 0 && { withDraft }
|
|
7233
|
+
}),
|
|
7234
|
+
contentType: "application/json"
|
|
7235
|
+
}
|
|
7236
|
+
)).results
|
|
7237
|
+
);
|
|
7238
|
+
}
|
|
7164
7239
|
// ─── Folders ─────────────────────────────────────────────────────
|
|
7165
7240
|
/**
|
|
7166
7241
|
* Lists all folders accessible by the current token.
|
|
@@ -7187,6 +7262,32 @@ class EngineServicesClient {
|
|
|
7187
7262
|
`${FOLDER_PATH}/${folderId}`
|
|
7188
7263
|
);
|
|
7189
7264
|
}
|
|
7265
|
+
/**
|
|
7266
|
+
* Batch variant of {@link getFolder}. Resolves a known set of folder ids in
|
|
7267
|
+
* one request — for example the parents behind a file list's breadcrumbs.
|
|
7268
|
+
*
|
|
7269
|
+
* Reach for {@link listFolders} instead when the whole (project) tree is
|
|
7270
|
+
* wanted; this is for the case where the ids are already known.
|
|
7271
|
+
*
|
|
7272
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
7273
|
+
* requests automatically. A folder the caller cannot read carries an
|
|
7274
|
+
* `error` instead of `folder`.
|
|
7275
|
+
*
|
|
7276
|
+
* @param folderIds - The folder ids to resolve.
|
|
7277
|
+
*/
|
|
7278
|
+
async getFoldersBatch(folderIds) {
|
|
7279
|
+
return await this.#requestBatches(
|
|
7280
|
+
folderIds,
|
|
7281
|
+
async (chunk) => (await this.#requestApi(
|
|
7282
|
+
"POST",
|
|
7283
|
+
`${ITEM_PATH}/batch/folders`,
|
|
7284
|
+
{
|
|
7285
|
+
body: JSON.stringify({ folderIds: chunk }),
|
|
7286
|
+
contentType: "application/json"
|
|
7287
|
+
}
|
|
7288
|
+
)).results
|
|
7289
|
+
);
|
|
7290
|
+
}
|
|
7190
7291
|
/**
|
|
7191
7292
|
* Creates a new folder.
|
|
7192
7293
|
* @param name - Display name for the folder.
|
|
@@ -7529,15 +7630,12 @@ return main;`);
|
|
|
7529
7630
|
*/
|
|
7530
7631
|
async downloadAppBundle(appId, params) {
|
|
7531
7632
|
const { versionTag, withDraft } = params || {};
|
|
7532
|
-
return await this.#requestFile(
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
...versionTag && { versionTag },
|
|
7537
|
-
...withDraft && { withDraft }
|
|
7538
|
-
}
|
|
7633
|
+
return await this.#requestFile(`${ITEM_PATH}/${appId}/download/bundle`, {
|
|
7634
|
+
query: {
|
|
7635
|
+
...versionTag && { versionTag },
|
|
7636
|
+
...withDraft && { withDraft }
|
|
7539
7637
|
}
|
|
7540
|
-
);
|
|
7638
|
+
});
|
|
7541
7639
|
}
|
|
7542
7640
|
/**
|
|
7543
7641
|
* Archives (soft-deletes) an app.
|
|
@@ -7586,7 +7684,9 @@ return main;`);
|
|
|
7586
7684
|
});
|
|
7587
7685
|
if (!response.ok) {
|
|
7588
7686
|
const text = await response.text().catch(() => "");
|
|
7589
|
-
throw new Error(
|
|
7687
|
+
throw new Error(
|
|
7688
|
+
`Local server request failed: ${response.status} - ${text}`
|
|
7689
|
+
);
|
|
7590
7690
|
}
|
|
7591
7691
|
return await response.json();
|
|
7592
7692
|
}
|
|
@@ -7610,7 +7710,9 @@ return main;`);
|
|
|
7610
7710
|
const response = await fetch(url2, { method: "POST" });
|
|
7611
7711
|
if (!response.ok) {
|
|
7612
7712
|
const text = await response.text().catch(() => "");
|
|
7613
|
-
throw new Error(
|
|
7713
|
+
throw new Error(
|
|
7714
|
+
`Local server request failed: ${response.status} - ${text}`
|
|
7715
|
+
);
|
|
7614
7716
|
}
|
|
7615
7717
|
return await response.json();
|
|
7616
7718
|
}
|
|
@@ -7662,7 +7764,9 @@ return main;`);
|
|
|
7662
7764
|
const response = await fetch(url2);
|
|
7663
7765
|
if (!response.ok) {
|
|
7664
7766
|
const text = await response.text().catch(() => "");
|
|
7665
|
-
throw new Error(
|
|
7767
|
+
throw new Error(
|
|
7768
|
+
`Local server request failed: ${response.status} - ${text}`
|
|
7769
|
+
);
|
|
7666
7770
|
}
|
|
7667
7771
|
return await response.json();
|
|
7668
7772
|
}
|
|
@@ -7786,6 +7890,40 @@ return main;`);
|
|
|
7786
7890
|
{ ...expiresIn != null && { query: { expiresIn } } }
|
|
7787
7891
|
);
|
|
7788
7892
|
}
|
|
7893
|
+
/**
|
|
7894
|
+
* Batch variant of {@link getHiddenFileSignedUrl}. Mints presigned URLs for
|
|
7895
|
+
* many hidden files in one request.
|
|
7896
|
+
*
|
|
7897
|
+
* This is the right call for tile-based viewers (splats, point clouds),
|
|
7898
|
+
* which mint a URL per piece as the camera moves. One request here replaces
|
|
7899
|
+
* up to {@link STORAGE_BATCH_MAX} single calls, which keeps a busy session
|
|
7900
|
+
* well under the per-endpoint rate limit.
|
|
7901
|
+
*
|
|
7902
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
7903
|
+
* requests automatically. Entries come back in request order; a file that
|
|
7904
|
+
* is gone or not accessible carries an `error` instead of a `url`, so the
|
|
7905
|
+
* remaining tiles still load. Re-call to re-mint as `expiresAt` approaches.
|
|
7906
|
+
*
|
|
7907
|
+
* @param hiddenIds - The hidden files to sign.
|
|
7908
|
+
* @param expiresIn - Desired URL lifetime in seconds (60–3600). Defaults to
|
|
7909
|
+
* 900 (15 min) server-side; values are clamped to that range.
|
|
7910
|
+
*/
|
|
7911
|
+
async getHiddenFileSignedUrlsBatch(hiddenIds, expiresIn) {
|
|
7912
|
+
return await this.#requestBatches(
|
|
7913
|
+
hiddenIds,
|
|
7914
|
+
async (chunk) => (await this.#requestApi(
|
|
7915
|
+
"POST",
|
|
7916
|
+
`${ITEM_PATH}/${HIDDEN_PATH}/signed-url/batch`,
|
|
7917
|
+
{
|
|
7918
|
+
body: JSON.stringify({
|
|
7919
|
+
hiddenFileIds: chunk,
|
|
7920
|
+
...expiresIn != null && { expiresIn }
|
|
7921
|
+
}),
|
|
7922
|
+
contentType: "application/json"
|
|
7923
|
+
}
|
|
7924
|
+
)).results
|
|
7925
|
+
);
|
|
7926
|
+
}
|
|
7789
7927
|
/**
|
|
7790
7928
|
* Lists all hidden files attached to a parent item.
|
|
7791
7929
|
* @param parentFileId - The parent item's unique identifier.
|
|
@@ -7818,11 +7956,9 @@ return main;`);
|
|
|
7818
7956
|
async uploadItemIcon(itemId, icon) {
|
|
7819
7957
|
const formData = new FormData();
|
|
7820
7958
|
formData.append("icon", icon);
|
|
7821
|
-
return await this.#requestApi(
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
{ body: formData }
|
|
7825
|
-
);
|
|
7959
|
+
return await this.#requestApi("PUT", `${ITEM_PATH}/${itemId}/icon`, {
|
|
7960
|
+
body: formData
|
|
7961
|
+
});
|
|
7826
7962
|
}
|
|
7827
7963
|
/**
|
|
7828
7964
|
* Downloads the icon for an item as a binary stream.
|
|
@@ -7892,6 +8028,38 @@ return main;`);
|
|
|
7892
8028
|
{ query: params }
|
|
7893
8029
|
);
|
|
7894
8030
|
}
|
|
8031
|
+
/**
|
|
8032
|
+
* Batch variant of {@link listVersions}. Lists the versions of many items in
|
|
8033
|
+
* one request, which is what a file panel needs to render a whole page.
|
|
8034
|
+
*
|
|
8035
|
+
* The returned versions are the full records, metadata included, so a list
|
|
8036
|
+
* that only needs version metadata does not need
|
|
8037
|
+
* {@link getFileVersionMetadataBatch} on top.
|
|
8038
|
+
*
|
|
8039
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
8040
|
+
* requests automatically. An item the caller cannot read carries an `error`
|
|
8041
|
+
* instead of `versions`.
|
|
8042
|
+
*
|
|
8043
|
+
* @param itemIds - The items whose versions to list.
|
|
8044
|
+
* @param params - Optional `{ archived }` filter, applied to every item.
|
|
8045
|
+
*/
|
|
8046
|
+
async listVersionsBatch(itemIds, params = {}) {
|
|
8047
|
+
const { archived } = params;
|
|
8048
|
+
return await this.#requestBatches(
|
|
8049
|
+
itemIds,
|
|
8050
|
+
async (chunk) => (await this.#requestApi(
|
|
8051
|
+
"POST",
|
|
8052
|
+
`${ITEM_PATH}/batch/versions`,
|
|
8053
|
+
{
|
|
8054
|
+
body: JSON.stringify({
|
|
8055
|
+
itemIds: chunk,
|
|
8056
|
+
...archived !== void 0 && { archived }
|
|
8057
|
+
}),
|
|
8058
|
+
contentType: "application/json"
|
|
8059
|
+
}
|
|
8060
|
+
)).results
|
|
8061
|
+
);
|
|
8062
|
+
}
|
|
7895
8063
|
/**
|
|
7896
8064
|
* Archives a version of an item. Archived versions remain available via
|
|
7897
8065
|
* `listVersions({ archived: true })` and can be recovered or permanently
|
|
@@ -7943,6 +8111,21 @@ return main;`);
|
|
|
7943
8111
|
// checkPermission, checkPermissionBatch) live on `PlatformClient` — they
|
|
7944
8112
|
// cannot be called with an access token.
|
|
7945
8113
|
// ─── Private Helpers ─────────────────────────────────────────────
|
|
8114
|
+
/**
|
|
8115
|
+
* Splits `inputs` into chunks the API accepts and concatenates the results,
|
|
8116
|
+
* so callers never have to think about the batch ceiling. Chunks go out
|
|
8117
|
+
* together; an empty input skips the network entirely, since the API
|
|
8118
|
+
* rejects an empty batch.
|
|
8119
|
+
*/
|
|
8120
|
+
async #requestBatches(inputs, requestChunk) {
|
|
8121
|
+
if (!inputs.length) return [];
|
|
8122
|
+
const chunks = [];
|
|
8123
|
+
for (let start = 0; start < inputs.length; start += STORAGE_BATCH_MAX) {
|
|
8124
|
+
chunks.push(inputs.slice(start, start + STORAGE_BATCH_MAX));
|
|
8125
|
+
}
|
|
8126
|
+
const responses = await Promise.all(chunks.map(requestChunk));
|
|
8127
|
+
return responses.flat();
|
|
8128
|
+
}
|
|
7946
8129
|
async #downloadItem(itemId, params) {
|
|
7947
8130
|
const { versionTag, withDraft } = params || {};
|
|
7948
8131
|
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
|
|
|
@@ -83,7 +84,13 @@ export type DownloadItemFileParams = {
|
|
|
83
84
|
};
|
|
84
85
|
/** Configuration options for the {@link EngineServicesClient} constructor. */
|
|
85
86
|
export type EngineServicesClientProps = {
|
|
86
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Number of automatic retries on request failure. Default: 0.
|
|
89
|
+
*
|
|
90
|
+
* Retries are spaced with exponential backoff plus jitter, and honour the
|
|
91
|
+
* server's `Retry-After` when it sends one. Only network failures, 429s and
|
|
92
|
+
* 5xx are retried — other 4xx fail immediately.
|
|
93
|
+
*/
|
|
87
94
|
retries?: number;
|
|
88
95
|
/**
|
|
89
96
|
* If true, sends the token as an `Authorization: Bearer` header instead of
|
|
@@ -166,7 +173,9 @@ export declare class EngineServicesClient {
|
|
|
166
173
|
*/
|
|
167
174
|
constructor(accessToken: string, apiUrl: string, props?: EngineServicesClientProps);
|
|
168
175
|
/**
|
|
169
|
-
* Sets the number of automatic retries for failed requests.
|
|
176
|
+
* Sets the number of automatic retries for failed requests. Retries use
|
|
177
|
+
* exponential backoff with jitter and honour `Retry-After`; only network
|
|
178
|
+
* failures, 429s and 5xx are retried.
|
|
170
179
|
* @param retries - Number of retries (0 = no retries).
|
|
171
180
|
*/
|
|
172
181
|
setRetries(retries: number): void;
|
|
@@ -305,6 +314,22 @@ export declare class EngineServicesClient {
|
|
|
305
314
|
deleteFileVersionMetadata(fileId: string, versionTag: string): Promise<{
|
|
306
315
|
success: boolean;
|
|
307
316
|
}>;
|
|
317
|
+
/**
|
|
318
|
+
* Batch variant of {@link getFileVersionMetadata}. Reads the metadata of
|
|
319
|
+
* many (file, version) pairs in one request instead of one request per pair,
|
|
320
|
+
* which is what a file list needs when it hydrates a whole page at once.
|
|
321
|
+
*
|
|
322
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
323
|
+
* requests automatically. Entries come back in the order they were asked
|
|
324
|
+
* for; a pair the caller cannot read carries an `error` instead of
|
|
325
|
+
* `metadata`, so one bad id does not lose the whole page.
|
|
326
|
+
*
|
|
327
|
+
* @param entries - The `{ itemId, versionTag }` pairs to read.
|
|
328
|
+
* @param params - Optional flags such as `withDraft`.
|
|
329
|
+
*/
|
|
330
|
+
getFileVersionMetadataBatch(entries: VersionMetadataBatchRequest[], params?: {
|
|
331
|
+
withDraft?: boolean;
|
|
332
|
+
}): Promise<VersionMetadataBatchEntry[]>;
|
|
308
333
|
/**
|
|
309
334
|
* Lists all folders accessible by the current token.
|
|
310
335
|
* @param params - Optional filters for parent folder and archive status.
|
|
@@ -324,6 +349,20 @@ export declare class EngineServicesClient {
|
|
|
324
349
|
* @param folderId - The folder's unique identifier.
|
|
325
350
|
*/
|
|
326
351
|
getFolder(folderId: string): Promise<ItemFolder>;
|
|
352
|
+
/**
|
|
353
|
+
* Batch variant of {@link getFolder}. Resolves a known set of folder ids in
|
|
354
|
+
* one request — for example the parents behind a file list's breadcrumbs.
|
|
355
|
+
*
|
|
356
|
+
* Reach for {@link listFolders} instead when the whole (project) tree is
|
|
357
|
+
* wanted; this is for the case where the ids are already known.
|
|
358
|
+
*
|
|
359
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
360
|
+
* requests automatically. A folder the caller cannot read carries an
|
|
361
|
+
* `error` instead of `folder`.
|
|
362
|
+
*
|
|
363
|
+
* @param folderIds - The folder ids to resolve.
|
|
364
|
+
*/
|
|
365
|
+
getFoldersBatch(folderIds: string[]): Promise<ItemFoldersBatchEntry[]>;
|
|
327
366
|
/**
|
|
328
367
|
* Creates a new folder.
|
|
329
368
|
* @param name - Display name for the folder.
|
|
@@ -638,6 +677,25 @@ export declare class EngineServicesClient {
|
|
|
638
677
|
url: string;
|
|
639
678
|
expiresAt: string;
|
|
640
679
|
}>;
|
|
680
|
+
/**
|
|
681
|
+
* Batch variant of {@link getHiddenFileSignedUrl}. Mints presigned URLs for
|
|
682
|
+
* many hidden files in one request.
|
|
683
|
+
*
|
|
684
|
+
* This is the right call for tile-based viewers (splats, point clouds),
|
|
685
|
+
* which mint a URL per piece as the camera moves. One request here replaces
|
|
686
|
+
* up to {@link STORAGE_BATCH_MAX} single calls, which keeps a busy session
|
|
687
|
+
* well under the per-endpoint rate limit.
|
|
688
|
+
*
|
|
689
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
690
|
+
* requests automatically. Entries come back in request order; a file that
|
|
691
|
+
* is gone or not accessible carries an `error` instead of a `url`, so the
|
|
692
|
+
* remaining tiles still load. Re-call to re-mint as `expiresAt` approaches.
|
|
693
|
+
*
|
|
694
|
+
* @param hiddenIds - The hidden files to sign.
|
|
695
|
+
* @param expiresIn - Desired URL lifetime in seconds (60–3600). Defaults to
|
|
696
|
+
* 900 (15 min) server-side; values are clamped to that range.
|
|
697
|
+
*/
|
|
698
|
+
getHiddenFileSignedUrlsBatch(hiddenIds: string[], expiresIn?: number): Promise<HiddenFileSignedUrlBatchEntry[]>;
|
|
641
699
|
/**
|
|
642
700
|
* Lists all hidden files attached to a parent item.
|
|
643
701
|
* @param parentFileId - The parent item's unique identifier.
|
|
@@ -700,6 +758,24 @@ export declare class EngineServicesClient {
|
|
|
700
758
|
listVersions(itemId: string, params?: {
|
|
701
759
|
archived?: boolean;
|
|
702
760
|
}): Promise<ItemVersion[]>;
|
|
761
|
+
/**
|
|
762
|
+
* Batch variant of {@link listVersions}. Lists the versions of many items in
|
|
763
|
+
* one request, which is what a file panel needs to render a whole page.
|
|
764
|
+
*
|
|
765
|
+
* The returned versions are the full records, metadata included, so a list
|
|
766
|
+
* that only needs version metadata does not need
|
|
767
|
+
* {@link getFileVersionMetadataBatch} on top.
|
|
768
|
+
*
|
|
769
|
+
* Inputs longer than {@link STORAGE_BATCH_MAX} are split into several
|
|
770
|
+
* requests automatically. An item the caller cannot read carries an `error`
|
|
771
|
+
* instead of `versions`.
|
|
772
|
+
*
|
|
773
|
+
* @param itemIds - The items whose versions to list.
|
|
774
|
+
* @param params - Optional `{ archived }` filter, applied to every item.
|
|
775
|
+
*/
|
|
776
|
+
listVersionsBatch(itemIds: string[], params?: {
|
|
777
|
+
archived?: boolean;
|
|
778
|
+
}): Promise<ItemVersionsBatchEntry[]>;
|
|
703
779
|
/**
|
|
704
780
|
* Archives a version of an item. Archived versions remain available via
|
|
705
781
|
* `listVersions({ archived: true })` and can be recovered or permanently
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -15,11 +15,27 @@
|
|
|
15
15
|
* }
|
|
16
16
|
* }
|
|
17
17
|
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example Rate limiting
|
|
20
|
+
* ```ts
|
|
21
|
+
* catch (err) {
|
|
22
|
+
* if (err instanceof RequestError && err.status === 429) {
|
|
23
|
+
* console.log(err.code); // "RATE_LIMITED"
|
|
24
|
+
* console.log(err.details); // { limit, windowSeconds, retryAfter, scope }
|
|
25
|
+
* console.log(err.retryAfter); // seconds to wait before trying again
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
18
29
|
*/
|
|
19
30
|
export declare class RequestError extends Error {
|
|
20
31
|
readonly status: number;
|
|
21
32
|
readonly code?: string;
|
|
22
33
|
readonly details?: unknown;
|
|
23
34
|
readonly body: string;
|
|
24
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Seconds to wait before retrying, taken from the `Retry-After` header or
|
|
37
|
+
* from `details.retryAfter`. Only present on rate-limited (429) responses.
|
|
38
|
+
*/
|
|
39
|
+
readonly retryAfter?: number;
|
|
40
|
+
constructor(status: number, statusText: string, body: string, retryAfter?: number);
|
|
25
41
|
}
|