@thatopen/services 0.10.0 → 0.12.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/built-in/index.d.ts +232 -0
- package/dist/cli.js +54 -10
- package/dist/core/client.d.ts +10 -2
- package/dist/core/request-error.d.ts +17 -1
- package/dist/index.cjs.js +2 -2
- package/dist/index.es.js +582 -545
- package/docs/ai-quickstart.md +5 -0
- package/docs/builtin/paths.json +12 -0
- package/docs/rate-limits.md +123 -0
- package/package.json +1 -1
- package/resources/AGENTS.md +11 -1
- package/src/built-in/ProjectManager/example.ts +163 -0
- package/src/built-in/UIManager/src/viewer/example.ts +182 -0
- package/src/built-in/UIManager/src/viewer-toolbar/example.ts +252 -0
- package/src/built-in/index.ts +233 -0
package/dist/built-in/index.d.ts
CHANGED
|
@@ -1567,6 +1567,238 @@ export type HelloWorld = InstanceType<typeof _HelloWorld>;
|
|
|
1567
1567
|
export declare const HelloWorld: typeof _HelloWorld & {
|
|
1568
1568
|
uuid: "2c4ae432-fc24-43e9-9783-0c960c674e96";
|
|
1569
1569
|
};
|
|
1570
|
+
export type ProjectNamespaceKey = "origin" | "georeferencing" | "appSettings";
|
|
1571
|
+
export interface ProjectNamespace {
|
|
1572
|
+
label: string;
|
|
1573
|
+
description?: string;
|
|
1574
|
+
}
|
|
1575
|
+
export interface ProjectEntity<T = unknown> {
|
|
1576
|
+
namespaceId: ProjectNamespaceKey;
|
|
1577
|
+
label: string;
|
|
1578
|
+
description?: string;
|
|
1579
|
+
data: T;
|
|
1580
|
+
}
|
|
1581
|
+
/** GIS origin point that fixes the scene's (0,0,0) to a real-world coordinate. */
|
|
1582
|
+
export interface OriginData {
|
|
1583
|
+
lat: number;
|
|
1584
|
+
lon: number;
|
|
1585
|
+
height: number;
|
|
1586
|
+
rotation: number;
|
|
1587
|
+
}
|
|
1588
|
+
export interface BimSite {
|
|
1589
|
+
label: string;
|
|
1590
|
+
description?: string;
|
|
1591
|
+
lat: number;
|
|
1592
|
+
lon: number;
|
|
1593
|
+
height: number;
|
|
1594
|
+
rotation: number;
|
|
1595
|
+
/** Column-major 4x4 matrix (16 values) mapping BIM local space to the site's georeferenced frame. */
|
|
1596
|
+
baseMatrix: number[];
|
|
1597
|
+
}
|
|
1598
|
+
export interface BimCoordinatesData {
|
|
1599
|
+
defaultSiteId?: string;
|
|
1600
|
+
sites: Record<string, BimSite>;
|
|
1601
|
+
}
|
|
1602
|
+
export type AssetCoordinateType = "splat" | "ifc" | "point-cloud" | (string & {});
|
|
1603
|
+
export interface AssetCoordinate {
|
|
1604
|
+
fileId: string;
|
|
1605
|
+
type: AssetCoordinateType;
|
|
1606
|
+
lat: number;
|
|
1607
|
+
lon: number;
|
|
1608
|
+
height: number;
|
|
1609
|
+
rotation: number;
|
|
1610
|
+
}
|
|
1611
|
+
export interface GraphicsSettingsData {
|
|
1612
|
+
stylePreset: string;
|
|
1613
|
+
postproduction: boolean;
|
|
1614
|
+
edges: {
|
|
1615
|
+
enabled: boolean;
|
|
1616
|
+
color: string;
|
|
1617
|
+
opacity: number;
|
|
1618
|
+
};
|
|
1619
|
+
ambientOcclusion: {
|
|
1620
|
+
enabled: boolean;
|
|
1621
|
+
value: number;
|
|
1622
|
+
};
|
|
1623
|
+
tonalShading: {
|
|
1624
|
+
enabled: boolean;
|
|
1625
|
+
value: number;
|
|
1626
|
+
};
|
|
1627
|
+
surfaceColor: {
|
|
1628
|
+
enabled: boolean;
|
|
1629
|
+
};
|
|
1630
|
+
transparentBackground: {
|
|
1631
|
+
enabled: boolean;
|
|
1632
|
+
color: string;
|
|
1633
|
+
};
|
|
1634
|
+
grid: {
|
|
1635
|
+
enabled: boolean;
|
|
1636
|
+
color: string;
|
|
1637
|
+
};
|
|
1638
|
+
gridHeight: {
|
|
1639
|
+
auto: boolean;
|
|
1640
|
+
value: number;
|
|
1641
|
+
};
|
|
1642
|
+
antiAliasing: boolean;
|
|
1643
|
+
nativeResolution: boolean;
|
|
1644
|
+
adaptiveResolution: {
|
|
1645
|
+
enabled: boolean;
|
|
1646
|
+
value: number;
|
|
1647
|
+
};
|
|
1648
|
+
minResolution: number;
|
|
1649
|
+
renderScale: number;
|
|
1650
|
+
}
|
|
1651
|
+
export interface ProjectManagerData {
|
|
1652
|
+
schemaVersion: 1;
|
|
1653
|
+
namespaces: Record<ProjectNamespaceKey, ProjectNamespace>;
|
|
1654
|
+
entities: {
|
|
1655
|
+
origin: ProjectEntity<OriginData>;
|
|
1656
|
+
bimCoordinates: ProjectEntity<BimCoordinatesData>;
|
|
1657
|
+
assetCoordinates: ProjectEntity<AssetCoordinate[]>;
|
|
1658
|
+
graphicsSettings: ProjectEntity<GraphicsSettingsData>;
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
export interface SingletonEntity<T> {
|
|
1662
|
+
get(): T | undefined;
|
|
1663
|
+
set(data: T): Promise<void>;
|
|
1664
|
+
update(partial: Partial<T>): Promise<void>;
|
|
1665
|
+
}
|
|
1666
|
+
export interface BimCoordinatesEntity {
|
|
1667
|
+
get(): BimCoordinatesData;
|
|
1668
|
+
getSites(): Record<string, BimSite>;
|
|
1669
|
+
getSite(siteId: string): BimSite | undefined;
|
|
1670
|
+
getDefaultSite(): BimSite | undefined;
|
|
1671
|
+
/** Creates a new site. `ProjectManager` generates the opaque `siteId` — callers
|
|
1672
|
+
* never invent their own (see the class-level JSDoc on `ProjectManager` for why). */
|
|
1673
|
+
createSite(site: BimSite): Promise<string>;
|
|
1674
|
+
/** Updates a site that already exists (from `createSite`). Throws if `siteId`
|
|
1675
|
+
* isn't in `sites` — this is not an upsert; use `createSite` to add one. */
|
|
1676
|
+
setSite(siteId: string, site: BimSite): Promise<void>;
|
|
1677
|
+
setDefaultSite(siteId: string): Promise<void>;
|
|
1678
|
+
removeSite(siteId: string): Promise<void>;
|
|
1679
|
+
}
|
|
1680
|
+
export interface AssetCoordinatesEntity {
|
|
1681
|
+
getAll(): AssetCoordinate[];
|
|
1682
|
+
get(fileId: string): AssetCoordinate | undefined;
|
|
1683
|
+
getByType(type: AssetCoordinateType): AssetCoordinate[];
|
|
1684
|
+
set(fileId: string, data: Omit<AssetCoordinate, "fileId">): Promise<void>;
|
|
1685
|
+
remove(fileId: string): Promise<void>;
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* ProjectManager is the platform's canonical store for project-level
|
|
1689
|
+
* configuration and data — origin/georeferencing/app-settings today, with
|
|
1690
|
+
* room to grow. It persists a single JSON file (`${ProjectManager.uuid}.json`)
|
|
1691
|
+
* loose directly inside `__project_data` (same folder convention `GISManager`
|
|
1692
|
+
* already used) — NOT in a per-app subfolder like `collider`/`cde`, which are
|
|
1693
|
+
* for secondary, per-builtin data. Sharing that root folder with other
|
|
1694
|
+
* builtins doesn't change its role: it's still the one canonical project
|
|
1695
|
+
* record, meant to stop each builtin from inventing its own ad-hoc
|
|
1696
|
+
* project-persistence pattern.
|
|
1697
|
+
*
|
|
1698
|
+
* Schema shape (see `src/types.ts` for the full rationale):
|
|
1699
|
+
* - `namespaces`/`entities` are maps keyed by a fixed, readable slug — a
|
|
1700
|
+
* closed set this code defines, never created at runtime.
|
|
1701
|
+
* - `bimCoordinates.data.sites` is the one open, runtime-created collection
|
|
1702
|
+
* today, so its keys are opaque generated ids instead of slugs.
|
|
1703
|
+
* - `origin`/`graphicsSettings` are singletons; `sites`/`assetCoordinates`
|
|
1704
|
+
* are collections.
|
|
1705
|
+
* - `entities[id]` is a uniform envelope: `namespaceId`, mandatory `label`,
|
|
1706
|
+
* optional `description` (omitted, not stored empty), free-form `data`.
|
|
1707
|
+
*
|
|
1708
|
+
* Id generation rule: whenever a collection needs an opaque id (today just
|
|
1709
|
+
* `sites`, potentially more collections later), `ProjectManager` generates it
|
|
1710
|
+
* internally (`crypto.randomUUID()`, no prefix) — callers never invent their
|
|
1711
|
+
* own. `createSite` is the current example; follow the same pattern for any
|
|
1712
|
+
* future `create*` method.
|
|
1713
|
+
*/
|
|
1714
|
+
declare class _ProjectManager extends OBC.Component {
|
|
1715
|
+
static readonly uuid: "6f3a9c2e-8b1d-4e6a-9c3f-1d7b5a8e2c4f";
|
|
1716
|
+
enabled: boolean;
|
|
1717
|
+
/** True once init(client) has been called — safe to use the entity accessors. */
|
|
1718
|
+
get ready(): boolean;
|
|
1719
|
+
/** Suppress auto-save while bulk-loading data. */
|
|
1720
|
+
private _loadingData;
|
|
1721
|
+
readonly onSaveStart: OBC.Event<void>;
|
|
1722
|
+
readonly onSaveComplete: OBC.Event<boolean>;
|
|
1723
|
+
private _client?;
|
|
1724
|
+
private _fileId?;
|
|
1725
|
+
private _data;
|
|
1726
|
+
private _saveTimer?;
|
|
1727
|
+
private _savePromise?;
|
|
1728
|
+
private _scheduleSave;
|
|
1729
|
+
private _queueSave;
|
|
1730
|
+
private _doSave;
|
|
1731
|
+
/** Cancels the pending debounce and runs the save immediately. Awaits completion. */
|
|
1732
|
+
flush(): Promise<void>;
|
|
1733
|
+
init(client: PlatformClient): Promise<void>;
|
|
1734
|
+
/** Opaque id generator for any collection needing one (currently just
|
|
1735
|
+
* `bimCoordinates.sites`) — see the class-level JSDoc's id-generation rule. */
|
|
1736
|
+
private _generateId;
|
|
1737
|
+
origin: SingletonEntity<OriginData>;
|
|
1738
|
+
graphicsSettings: SingletonEntity<GraphicsSettingsData>;
|
|
1739
|
+
bimCoordinates: BimCoordinatesEntity;
|
|
1740
|
+
assetCoordinates: AssetCoordinatesEntity;
|
|
1741
|
+
getNamespaces(): Record<ProjectNamespaceKey, ProjectNamespace>;
|
|
1742
|
+
getNamespace(key: ProjectNamespaceKey): Record<string, unknown>;
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* ProjectManager is the platform's canonical store for project-level
|
|
1746
|
+
* configuration and data — origin/georeferencing/app-settings today, with
|
|
1747
|
+
* room to grow. It persists a single JSON file (`${ProjectManager.uuid}.json`)
|
|
1748
|
+
* loose directly inside `__project_data` (same folder convention `GISManager`
|
|
1749
|
+
* already used) — NOT in a per-app subfolder like `collider`/`cde`, which are
|
|
1750
|
+
* for secondary, per-builtin data. Sharing that root folder with other
|
|
1751
|
+
* builtins doesn't change its role: it's still the one canonical project
|
|
1752
|
+
* record, meant to stop each builtin from inventing its own ad-hoc
|
|
1753
|
+
* project-persistence pattern.
|
|
1754
|
+
*
|
|
1755
|
+
* Schema shape (see `src/types.ts` for the full rationale):
|
|
1756
|
+
* - `namespaces`/`entities` are maps keyed by a fixed, readable slug — a
|
|
1757
|
+
* closed set this code defines, never created at runtime.
|
|
1758
|
+
* - `bimCoordinates.data.sites` is the one open, runtime-created collection
|
|
1759
|
+
* today, so its keys are opaque generated ids instead of slugs.
|
|
1760
|
+
* - `origin`/`graphicsSettings` are singletons; `sites`/`assetCoordinates`
|
|
1761
|
+
* are collections.
|
|
1762
|
+
* - `entities[id]` is a uniform envelope: `namespaceId`, mandatory `label`,
|
|
1763
|
+
* optional `description` (omitted, not stored empty), free-form `data`.
|
|
1764
|
+
*
|
|
1765
|
+
* Id generation rule: whenever a collection needs an opaque id (today just
|
|
1766
|
+
* `sites`, potentially more collections later), `ProjectManager` generates it
|
|
1767
|
+
* internally (`crypto.randomUUID()`, no prefix) — callers never invent their
|
|
1768
|
+
* own. `createSite` is the current example; follow the same pattern for any
|
|
1769
|
+
* future `create*` method.
|
|
1770
|
+
*/
|
|
1771
|
+
export type ProjectManager = InstanceType<typeof _ProjectManager>;
|
|
1772
|
+
/**
|
|
1773
|
+
* ProjectManager is the platform's canonical store for project-level
|
|
1774
|
+
* configuration and data — origin/georeferencing/app-settings today, with
|
|
1775
|
+
* room to grow. It persists a single JSON file (`${ProjectManager.uuid}.json`)
|
|
1776
|
+
* loose directly inside `__project_data` (same folder convention `GISManager`
|
|
1777
|
+
* already used) — NOT in a per-app subfolder like `collider`/`cde`, which are
|
|
1778
|
+
* for secondary, per-builtin data. Sharing that root folder with other
|
|
1779
|
+
* builtins doesn't change its role: it's still the one canonical project
|
|
1780
|
+
* record, meant to stop each builtin from inventing its own ad-hoc
|
|
1781
|
+
* project-persistence pattern.
|
|
1782
|
+
*
|
|
1783
|
+
* Schema shape (see `src/types.ts` for the full rationale):
|
|
1784
|
+
* - `namespaces`/`entities` are maps keyed by a fixed, readable slug — a
|
|
1785
|
+
* closed set this code defines, never created at runtime.
|
|
1786
|
+
* - `bimCoordinates.data.sites` is the one open, runtime-created collection
|
|
1787
|
+
* today, so its keys are opaque generated ids instead of slugs.
|
|
1788
|
+
* - `origin`/`graphicsSettings` are singletons; `sites`/`assetCoordinates`
|
|
1789
|
+
* are collections.
|
|
1790
|
+
* - `entities[id]` is a uniform envelope: `namespaceId`, mandatory `label`,
|
|
1791
|
+
* optional `description` (omitted, not stored empty), free-form `data`.
|
|
1792
|
+
*
|
|
1793
|
+
* Id generation rule: whenever a collection needs an opaque id (today just
|
|
1794
|
+
* `sites`, potentially more collections later), `ProjectManager` generates it
|
|
1795
|
+
* internally (`crypto.randomUUID()`, no prefix) — callers never invent their
|
|
1796
|
+
* own. `createSite` is the current example; follow the same pattern for any
|
|
1797
|
+
* future `create*` method.
|
|
1798
|
+
*/
|
|
1799
|
+
export declare const ProjectManager: typeof _ProjectManager & {
|
|
1800
|
+
uuid: "6f3a9c2e-8b1d-4e6a-9c3f-1d7b5a8e2c4f";
|
|
1801
|
+
};
|
|
1570
1802
|
/** `main.bcf`'s path relative to `__project_data/` — shared with
|
|
1571
1803
|
* `requestBcfSave` (`UIManager/src/utils/bcf-storage.ts`) so both sides of
|
|
1572
1804
|
* the save/load round-trip agree on where the file lives. */
|
package/dist/cli.js
CHANGED
|
@@ -6811,12 +6811,22 @@ function parseErrorBody(body) {
|
|
|
6811
6811
|
}
|
|
6812
6812
|
return {};
|
|
6813
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
|
+
}
|
|
6814
6819
|
class RequestError extends Error {
|
|
6815
6820
|
status;
|
|
6816
6821
|
code;
|
|
6817
6822
|
details;
|
|
6818
6823
|
body;
|
|
6819
|
-
|
|
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) {
|
|
6820
6830
|
const parsed = parseErrorBody(body);
|
|
6821
6831
|
super(parsed.message ?? `${statusText || "Request failed"} (${status})`);
|
|
6822
6832
|
this.name = "RequestError";
|
|
@@ -6824,6 +6834,7 @@ class RequestError extends Error {
|
|
|
6824
6834
|
this.code = parsed.code;
|
|
6825
6835
|
this.details = parsed.details;
|
|
6826
6836
|
this.body = body;
|
|
6837
|
+
this.retryAfter = retryAfter ?? retryAfterFromDetails(parsed.details);
|
|
6827
6838
|
}
|
|
6828
6839
|
}
|
|
6829
6840
|
const FOLDER_PATH = "item/folder";
|
|
@@ -6834,6 +6845,35 @@ const HIDDEN_PATH = "hidden";
|
|
|
6834
6845
|
const ITEM_TYPE_FILE = "FILE";
|
|
6835
6846
|
const ITEM_TYPE_COMPONENT = "TOOL";
|
|
6836
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
|
+
}
|
|
6837
6877
|
class EngineServicesClient {
|
|
6838
6878
|
apiUrl;
|
|
6839
6879
|
accessToken;
|
|
@@ -6906,7 +6946,9 @@ class EngineServicesClient {
|
|
|
6906
6946
|
}
|
|
6907
6947
|
}
|
|
6908
6948
|
/**
|
|
6909
|
-
* 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.
|
|
6910
6952
|
* @param retries - Number of retries (0 = no retries).
|
|
6911
6953
|
*/
|
|
6912
6954
|
setRetries(retries) {
|
|
@@ -6966,7 +7008,7 @@ class EngineServicesClient {
|
|
|
6966
7008
|
return this.accessToken;
|
|
6967
7009
|
}
|
|
6968
7010
|
async #requestApi(method, path2, requestData) {
|
|
6969
|
-
const { body, query, contentType, retries, responseType } = requestData || {};
|
|
7011
|
+
const { body, query, contentType, retries, responseType, retryAttempt } = requestData || {};
|
|
6970
7012
|
const url2 = this.#buildUrl(path2);
|
|
6971
7013
|
const cleanQuery = this.#cleanData(query);
|
|
6972
7014
|
const token = await this.resolveAccessToken();
|
|
@@ -6992,7 +7034,8 @@ class EngineServicesClient {
|
|
|
6992
7034
|
throw new RequestError(
|
|
6993
7035
|
response.status,
|
|
6994
7036
|
response.statusText,
|
|
6995
|
-
textResponse
|
|
7037
|
+
textResponse,
|
|
7038
|
+
parseRetryAfter(response)
|
|
6996
7039
|
);
|
|
6997
7040
|
}
|
|
6998
7041
|
if (responseType === "blob") {
|
|
@@ -7000,16 +7043,17 @@ class EngineServicesClient {
|
|
|
7000
7043
|
}
|
|
7001
7044
|
return response.json().then((data) => data).catch(() => void 0);
|
|
7002
7045
|
} catch (e) {
|
|
7003
|
-
|
|
7004
|
-
if (
|
|
7005
|
-
|
|
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));
|
|
7006
7050
|
return await this.#requestApi(method, path2, {
|
|
7007
7051
|
...requestData,
|
|
7008
|
-
retries:
|
|
7052
|
+
retries: retriesLeft - 1,
|
|
7053
|
+
retryAttempt: attempt + 1
|
|
7009
7054
|
});
|
|
7010
|
-
} else {
|
|
7011
|
-
throw e;
|
|
7012
7055
|
}
|
|
7056
|
+
throw e;
|
|
7013
7057
|
}
|
|
7014
7058
|
}
|
|
7015
7059
|
/**
|
package/dist/core/client.d.ts
CHANGED
|
@@ -84,7 +84,13 @@ export type DownloadItemFileParams = {
|
|
|
84
84
|
};
|
|
85
85
|
/** Configuration options for the {@link EngineServicesClient} constructor. */
|
|
86
86
|
export type EngineServicesClientProps = {
|
|
87
|
-
/**
|
|
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
|
+
*/
|
|
88
94
|
retries?: number;
|
|
89
95
|
/**
|
|
90
96
|
* If true, sends the token as an `Authorization: Bearer` header instead of
|
|
@@ -167,7 +173,9 @@ export declare class EngineServicesClient {
|
|
|
167
173
|
*/
|
|
168
174
|
constructor(accessToken: string, apiUrl: string, props?: EngineServicesClientProps);
|
|
169
175
|
/**
|
|
170
|
-
* 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.
|
|
171
179
|
* @param retries - Number of retries (0 = no retries).
|
|
172
180
|
*/
|
|
173
181
|
setRetries(retries: number): void;
|
|
@@ -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
|
}
|