Skip to content

Commit 8265a19

Browse files
Enable ESM compatibility (#19)
* Update tsconfig, package.json and file imports for ESM * Resolve compiler errors; add error handling util * Fix eslint config * Better error handling * Add changeset
1 parent 6b63379 commit 8265a19

22 files changed

Lines changed: 553 additions & 196 deletions

.changeset/spicy-lights-hunt.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@bentley/scenes-client": minor
3+
---
4+
5+
Enable ESM compatibility
6+
7+
**Breaking Changes:**
8+
9+
- Package now uses ESM module resolution and explicit file extensions
10+
- Import statements must use `.js` extensions when importing typescript files
11+
- Requires modern bundlers that support ESM
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ export default tseslint.config(
1010
files: ['src/**/*.ts', 'tests/**/*.ts', '*.d.ts'],
1111
languageOptions: {
1212
parser: tseslint.parser,
13-
sourceType: "module",
1413
parserOptions: {
14+
ecmaVersion: "latest",
1515
project: "tsconfig.eslint.json",
1616
},
1717
},
18+
settings: {
19+
"import/resolver": {
20+
typescript: {
21+
project: "tsconfig.eslint.json",
22+
},
23+
},
24+
},
1825
plugins: {
1926
prettier: prettierPlugin,
2027
header: headerPlugin,
@@ -31,6 +38,7 @@ export default tseslint.config(
3138
"curly": ["error", "all"],
3239
"eqeqeq": "warn",
3340
"no-alert": "warn",
41+
"no-console": "warn",
3442
"no-empty": ["warn", { allowEmptyCatch: true }],
3543
"no-eval": "error",
3644
"quotes": ["error", "double"],
@@ -39,6 +47,7 @@ export default tseslint.config(
3947
"@typescript-eslint/no-non-null-assertion": "warn",
4048
"@typescript-eslint/return-await": "warn",
4149
"@typescript-eslint/switch-exhaustiveness-check": "warn",
50+
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", caughtErrors: "none" }],
4251
},
4352
},
4453
{

packages/scenes-client/package.json

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
"name": "@bentley/scenes-client",
33
"version": "0.1.0",
44
"description": "Client-side code for managing scenes",
5-
"main": "index.js",
6-
"types": "./lib/index.d.ts",
75
"publishConfig": {
86
"registry": "https://pkgs.dev.azure.com/bentleycs/_packaging/Packages/npm/registry/",
97
"access": "restricted"
@@ -16,20 +14,30 @@
1614
"lint": "eslint src tests",
1715
"typecheck": "tsc --noEmit"
1816
},
19-
"keywords": [],
2017
"author": {
2118
"name": "Bentley Systems, Inc.",
2219
"url": "https://www.bentley.com"
2320
},
2421
"license": "MIT",
2522
"type": "module",
23+
"main": "./lib/index.js",
24+
"types": "./lib/index.d.ts",
25+
"exports": {
26+
".": {
27+
"types": "./lib/index.d.ts",
28+
"default": "./lib/index.js"
29+
}
30+
},
31+
"files": [
32+
"./lib"
33+
],
2634
"devDependencies": {
2735
"@types/node": "^24.0.7",
2836
"@typescript-eslint/eslint-plugin": "^8.36.0",
2937
"@typescript-eslint/parser": "^8.36.0",
3038
"eslint": "^9.30.1",
3139
"eslint-config-prettier": ">=10.1.8",
32-
"eslint-define-config": "^2.1.0",
40+
"eslint-import-resolver-typescript": "^4.4.4",
3341
"eslint-plugin-header": "^3.1.1",
3442
"eslint-plugin-import": "^2.32.0",
3543
"eslint-plugin-prettier": ">=5.5.3",

packages/scenes-client/src/api/sceneApi.ts

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {
33
SceneListResponse,
44
ScenesApiError,
5-
ScenesErrorResponse,
65
SceneResponse,
76
isSceneListResponse,
87
isSceneResponse,
@@ -13,9 +12,10 @@ import {
1312
PatchSceneParams,
1413
DeleteSceneParams,
1514
GetScenesParams,
16-
} from "../models/index";
17-
import { iteratePagedEndpoint } from "../utilities";
18-
import { callApi, AuthArgs } from "./apiFetch";
15+
handleErrorResponse,
16+
} from "../models/index.js";
17+
import { iteratePagedEndpoint } from "../utilities.js";
18+
import { callApi, AuthArgs } from "./apiFetch.js";
1919

2020
/**
2121
* Fetches a single scene by its ID.
@@ -34,13 +34,10 @@ export async function getScene({
3434
endpoint: `/${sceneId}?iTwinId=${iTwinId}&orderBy=${orderBy}`,
3535
getAccessToken,
3636
postProcess: async (response) => {
37-
const responseJson = await response.json();
3837
if (!response.ok) {
39-
throw new ScenesApiError(
40-
responseJson.error as ScenesErrorResponse,
41-
response.status,
42-
);
38+
await handleErrorResponse(response);
4339
}
40+
const responseJson = await response.json();
4441
if (!isSceneResponse(responseJson)) {
4542
throw new ScenesApiError(
4643
{
@@ -80,18 +77,18 @@ export async function getScenes({
8077
additionalHeaders: {
8178
Accept: "application/vnd.bentley.itwin-platform.v1+json",
8279
},
83-
postProcess: async (res) => {
84-
const json = await res.json();
85-
if (!res.ok) {
86-
throw new ScenesApiError(json.error as ScenesErrorResponse, res.status);
80+
postProcess: async (response) => {
81+
if (!response.ok) {
82+
await handleErrorResponse(response);
8783
}
88-
if (!isSceneListResponse(json)) {
84+
const responseJson = await response.json();
85+
if (!isSceneListResponse(responseJson)) {
8986
throw new ScenesApiError(
9087
{ code: "InvalidResponse", message: "Unexpected response format" },
91-
res.status,
88+
response.status,
9289
);
9390
}
94-
return json;
91+
return responseJson;
9592
},
9693
});
9794

@@ -124,14 +121,11 @@ export function getAllScenes(
124121
Accept: "application/vnd.bentley.itwin-platform.v1+json",
125122
},
126123
postProcess: async (response) => {
127-
const json = await response.json();
128124
if (!response.ok) {
129-
throw new ScenesApiError(
130-
json.error as ScenesErrorResponse,
131-
response.status,
132-
);
125+
await handleErrorResponse(response);
133126
}
134-
if (!isSceneListResponse(json)) {
127+
const responseJson = await response.json();
128+
if (!isSceneListResponse(responseJson)) {
135129
throw new ScenesApiError(
136130
{
137131
code: "InvalidResponse",
@@ -140,7 +134,7 @@ export function getAllScenes(
140134
response.status,
141135
);
142136
}
143-
return json;
137+
return responseJson;
144138
},
145139
});
146140
},
@@ -164,13 +158,10 @@ export async function postScene({
164158
getAccessToken,
165159
baseUrl,
166160
postProcess: async (response) => {
167-
const responseJson = await response.json();
168161
if (!response.ok) {
169-
throw new ScenesApiError(
170-
responseJson.error as ScenesErrorResponse,
171-
response.status,
172-
);
162+
await handleErrorResponse(response);
173163
}
164+
const responseJson = await response.json();
174165
if (!isSceneResponse(responseJson)) {
175166
throw new ScenesApiError(
176167
{
@@ -213,13 +204,10 @@ export async function patchScene({
213204
getAccessToken,
214205
baseUrl,
215206
postProcess: async (response) => {
216-
const responseJson = await response.json();
217207
if (!response.ok) {
218-
throw new ScenesApiError(
219-
responseJson.error as ScenesErrorResponse,
220-
response.status,
221-
);
208+
await handleErrorResponse(response);
222209
}
210+
const responseJson = await response.json();
223211
if (!isSceneResponse(responseJson)) {
224212
throw new ScenesApiError(
225213
{
@@ -265,13 +253,7 @@ export async function deleteScene({
265253
},
266254
postProcess: async (response) => {
267255
if (!response.ok) {
268-
const responseJson = await response
269-
.json()
270-
.catch(() => ({ error: {} as ScenesErrorResponse }));
271-
throw new ScenesApiError(
272-
responseJson.error as ScenesErrorResponse,
273-
response.status,
274-
);
256+
await handleErrorResponse(response);
275257
}
276258
return;
277259
},

packages/scenes-client/src/api/sceneObjectApi.ts

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import {
44
ScenesApiError,
5-
ScenesErrorResponse,
65
SceneObjectResponse,
76
SceneObjectListResponse,
87
isSceneObjectResponse,
@@ -18,9 +17,10 @@ import {
1817
DeleteObjectsParams,
1918
GetObjectsParams,
2019
PatchObjectParam,
21-
} from "../models/index";
22-
import { iteratePagedEndpoint, batched } from "../utilities";
23-
import { callApi, AuthArgs } from "./apiFetch";
20+
handleErrorResponse,
21+
} from "../models/index.js";
22+
import { iteratePagedEndpoint, batched } from "../utilities.js";
23+
import { callApi, AuthArgs } from "./apiFetch.js";
2424

2525
/**
2626
* Fetches a single scene object by its object ID.
@@ -40,11 +40,10 @@ export async function getObject({
4040
getAccessToken,
4141
baseUrl,
4242
postProcess: async (response) => {
43-
const responseJson = await response.json();
4443
if (!response.ok) {
45-
const err = responseJson.error as ScenesErrorResponse;
46-
throw new ScenesApiError(err, response.status);
44+
await handleErrorResponse(response);
4745
}
46+
const responseJson = await response.json();
4847
if (!isSceneObjectResponse(responseJson)) {
4948
throw new ScenesApiError(
5049
{
@@ -82,11 +81,10 @@ export async function getObjects({
8281
getAccessToken,
8382
baseUrl,
8483
postProcess: async (response) => {
85-
const responseJson = await response.json();
8684
if (!response.ok) {
87-
const err = responseJson.error as ScenesErrorResponse;
88-
throw new ScenesApiError(err, response.status);
85+
await handleErrorResponse(response);
8986
}
87+
const responseJson = await response.json();
9088
if (!isSceneObjectListResponse(responseJson)) {
9189
throw new ScenesApiError(
9290
{
@@ -127,11 +125,10 @@ export function getAllObjects(
127125
baseUrl: url,
128126
getAccessToken,
129127
postProcess: async (response) => {
130-
const responseJson = await response.json();
131128
if (!response.ok) {
132-
const err = responseJson.error as ScenesErrorResponse;
133-
throw new ScenesApiError(err, response.status);
129+
await handleErrorResponse(response);
134130
}
131+
const responseJson = await response.json();
135132
if (!isSceneObjectPagedResponse(responseJson)) {
136133
throw new ScenesApiError(
137134
{
@@ -173,11 +170,10 @@ export async function postObjects({
173170
getAccessToken,
174171
baseUrl,
175172
postProcess: async (response) => {
176-
const responseJson = await response.json();
177173
if (!response.ok) {
178-
const err = responseJson.error as ScenesErrorResponse;
179-
throw new ScenesApiError(err, response.status);
174+
await handleErrorResponse(response);
180175
}
176+
const responseJson = await response.json();
181177
if (!isSceneObjectListResponse(responseJson)) {
182178
throw new ScenesApiError(
183179
{
@@ -228,11 +224,10 @@ export async function patchObject({
228224
getAccessToken,
229225
baseUrl,
230226
postProcess: async (response) => {
231-
const responseJson = await response.json();
232227
if (!response.ok) {
233-
const err = responseJson.error as ScenesErrorResponse;
234-
throw new ScenesApiError(err, response.status);
228+
await handleErrorResponse(response);
235229
}
230+
const responseJson = await response.json();
236231
if (!isSceneObjectResponse(responseJson)) {
237232
throw new ScenesApiError(
238233
{
@@ -278,11 +273,10 @@ export async function patchObjects({
278273
getAccessToken,
279274
baseUrl,
280275
postProcess: async (response) => {
281-
const responseJson = await response.json();
282276
if (!response.ok) {
283-
const err = responseJson.error as ScenesErrorResponse;
284-
throw new ScenesApiError(err, response.status);
277+
await handleErrorResponse(response);
285278
}
279+
const responseJson = await response.json();
286280
if (!isSceneObjectListResponse(responseJson)) {
287281
throw new ScenesApiError(
288282
{
@@ -332,10 +326,7 @@ export async function deleteObject({
332326
baseUrl,
333327
postProcess: async (response) => {
334328
if (!response.ok) {
335-
const err = await response
336-
.json()
337-
.catch(() => ({}) as ScenesErrorResponse);
338-
throw new ScenesApiError(err, response.status);
329+
await handleErrorResponse(response);
339330
}
340331
return;
341332
},
@@ -369,10 +360,7 @@ export async function deleteObjects({
369360
baseUrl,
370361
postProcess: async (response) => {
371362
if (!response.ok) {
372-
const err = await response
373-
.json()
374-
.catch(() => ({}) as ScenesErrorResponse);
375-
throw new ScenesApiError(err, response.status);
363+
await handleErrorResponse(response);
376364
}
377365
return;
378366
},

packages/scenes-client/src/client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
patchScene,
88
deleteScene,
99
getScenes,
10-
} from "./api/sceneApi";
10+
} from "./api/sceneApi.js";
1111
import {
1212
postObjects,
1313
getObject,
@@ -17,7 +17,7 @@ import {
1717
getAllObjects,
1818
getObjects,
1919
patchObject,
20-
} from "./api/sceneObjectApi";
20+
} from "./api/sceneObjectApi.js";
2121
import {
2222
SceneListResponse,
2323
SceneObjectResponse,
@@ -41,7 +41,7 @@ import {
4141
GetScenesParams,
4242
GetObjectsParams,
4343
PatchObjectParam,
44-
} from "./models/index";
44+
} from "./models/index.js";
4545

4646
type AccessTokenFn = () => Promise<string>;
4747

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Copyright (c) Bentley Systems, Incorporated. All rights reserved.
22

3-
export { SceneClient } from "./client";
3+
export { SceneClient } from "./client.js";
44
export * from "./models";

0 commit comments

Comments
 (0)