dalux-build-api 1.1.5 → 2.0.1

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.
Files changed (39) hide show
  1. package/README.md +257 -187
  2. package/package.json +11 -4
  3. package/src/api/CompaniesApi.js +19 -12
  4. package/src/api/CompanyCatalogApi.js +22 -18
  5. package/src/api/FileAreasApi.js +14 -12
  6. package/src/api/FilesApi.js +11 -7
  7. package/src/api/FoldersApi.js +14 -12
  8. package/src/api/FormsApi.js +11 -6
  9. package/src/api/InspectionPlansApi.js +87 -9
  10. package/src/api/ProjectsApi.js +22 -18
  11. package/src/api/TasksApi.js +25 -10
  12. package/src/api/TestPlansApi.js +87 -9
  13. package/src/api/UsersApi.js +11 -6
  14. package/src/api/VersionSetsApi.js +20 -12
  15. package/src/api/WorkPackagesApi.js +7 -3
  16. package/src/browser.js +84 -0
  17. package/src/index.js +5 -0
  18. package/src/models/common.js +22 -0
  19. package/src/models/companies/index.js +14 -0
  20. package/src/models/companyCatalog/index.js +19 -0
  21. package/src/models/convert.js +44 -0
  22. package/src/models/fileAreas/index.js +20 -0
  23. package/src/models/fileRevisions/index.js +12 -0
  24. package/src/models/fileUpload/index.js +12 -0
  25. package/src/models/files/index.js +97 -0
  26. package/src/models/folders/index.js +20 -0
  27. package/src/models/forms/index.js +19 -0
  28. package/src/models/helpers.js +62 -0
  29. package/src/models/index.js +178 -0
  30. package/src/models/inspectionPlans/index.js +61 -0
  31. package/src/models/projectTemplates/index.js +11 -0
  32. package/src/models/projects/index.js +57 -0
  33. package/src/models/tasks/index.js +105 -0
  34. package/src/models/testPlans/index.js +61 -0
  35. package/src/models/users/index.js +29 -0
  36. package/src/models/versionSets/index.js +22 -0
  37. package/src/models/workPackages/index.js +19 -0
  38. package/src/next.js +89 -0
  39. package/LICENSE +0 -21
package/README.md CHANGED
@@ -1,17 +1,11 @@
1
- # Dalux Build API
1
+ # Dalux Build API – Node.js Client
2
2
 
3
- API clients for the [Dalux Build REST API](https://app.swaggerhub.com/apis-docs/Dalux/DaluxBuild-api/4.14), available in two languages:
3
+ A lightweight Node.js client for the [Dalux Build REST API](https://app.swaggerhub.com/apis-docs/Dalux/DaluxBuild-api/4.14).
4
4
 
5
- | Language | Location | Documentation |
6
- |---|---|---|
7
- | Node.js | [`/`](.) | See below |
8
- | Python | [`/python`](python/) | [python/README.md](python/README.md) |
9
-
10
- ---
11
-
12
- # Node.js Client
13
-
14
- A lightweight Node.js client for the Dalux Build REST API.
5
+ See the [repo root README](../README.md) for the Python client
6
+ (`dalux-build`) and the [webhook server](../webhook-server/README.md) built
7
+ on it the two clients are versioned, tested, and released together; see
8
+ [../CONTRIBUTING.md](../CONTRIBUTING.md).
15
9
 
16
10
  ## Requirements
17
11
 
@@ -20,7 +14,7 @@ A lightweight Node.js client for the Dalux Build REST API.
20
14
  ## Installation
21
15
 
22
16
  ```bash
23
- npm install @bruadam/dalux-build-api
17
+ npm install dalux-build-api
24
18
  ```
25
19
 
26
20
  ## Getting Started
@@ -31,34 +25,84 @@ To access the API you need:
31
25
  2. An **API key** — managed via _Settings › Integrations › API Identities_ inside the Dalux Build UI.
32
26
 
33
27
  ```js
34
- const { createClient } = require('@bruadam/dalux-build-api');
28
+ const { createClient } = require("dalux-build-api");
35
29
 
36
30
  const dalux = createClient({
37
- baseUrl: 'https://<your-company>.dalux.com/api',
38
- apiKey: 'YOUR_API_KEY',
31
+ baseUrl: "https://<your-company>.dalux.com/api",
32
+ apiKey: "YOUR_API_KEY",
39
33
  });
40
34
  ```
41
35
 
42
36
  The returned object exposes one namespace per API group (see [API Reference](#api-reference) below).
43
37
 
38
+ ## Next.js (browser-safe, no CORS)
39
+
40
+ Dalux API keys must stay server-side, and CORS is controlled by the Dalux API rather than this SDK.
41
+ For Client Components, use the package's same-origin Next.js adapter: the browser calls your own
42
+ App Router endpoint, and that endpoint uses the regular Dalux client on the server.
43
+
44
+ ```js
45
+ // app/api/dalux/route.js
46
+ import { createClient } from "dalux-build-api";
47
+ import { createDaluxRouteHandler } from "dalux-build-api/next";
48
+ import { getCurrentUser } from "@/lib/auth";
49
+
50
+ const dalux = createClient({
51
+ baseUrl: process.env.DALUX_BASE_URL,
52
+ apiKey: process.env.DALUX_API_KEY,
53
+ });
54
+
55
+ export const POST = createDaluxRouteHandler({
56
+ client: dalux,
57
+ // Required: expose only the methods this application needs.
58
+ allowedMethods: {
59
+ projects: ["listProjects", "getProject"],
60
+ tasks: ["getProjectTasks", "getAllProjectTasks"],
61
+ },
62
+ // Recommended: connect this to your application's session/authorization.
63
+ authorize: async (request) => Boolean(await getCurrentUser(request)),
64
+ });
65
+ ```
66
+
67
+ ```js
68
+ // app/projects/Projects.jsx
69
+ "use client";
70
+
71
+ import { createBrowserClient } from "dalux-build-api/browser";
72
+
73
+ const dalux = createBrowserClient({ url: "/api/dalux" });
74
+
75
+ export async function loadProjects() {
76
+ return dalux.projects.listProjects();
77
+ }
78
+ ```
79
+
80
+ The browser entry contains no Axios, Node.js filesystem modules, base URL, or API key. Calls use
81
+ same-origin `fetch`, so the browser does not make a cross-origin Dalux request. Write methods are
82
+ supported only when you explicitly add them to `allowedMethods`; protect the route with your app's
83
+ authorization before exposing them.
84
+
44
85
  ### Examples
45
86
 
46
87
  **List all projects**
88
+
47
89
  ```js
48
90
  const projects = await dalux.projects.listProjects();
49
91
  console.log(projects);
50
92
  ```
51
93
 
52
94
  **Get a specific project**
95
+
53
96
  ```js
54
- const project = await dalux.projects.getProject('my-project-id');
97
+ const project = await dalux.projects.getProject("my-project-id");
55
98
  console.log(project);
56
99
  ```
57
100
 
58
101
  **List tasks on a project**
102
+
59
103
  ```js
60
- const tasks = await dalux.tasks.getProjectTasks('my-project-id', {
61
- updatedAfter: '2024-01-01',
104
+ const tasks = await dalux.tasks.getProjectTasks("my-project-id", {
105
+ updatedAfter: "2024-01-01",
62
106
  });
63
107
  console.log(tasks);
64
108
  ```
@@ -68,55 +112,86 @@ console.log(tasks);
68
112
  `getProjectTasks` forwards OData query options (for example `$filter`). You can also pass `typeId`; it is expanded to `$filter=data/type/typeId eq '<typeId>'` unless you already set `$filter`.
69
113
 
70
114
  ```js
71
- const byType = await dalux.tasks.getProjectTasks('my-project-id', { typeId: 'some-type-guid' });
115
+ const byType = await dalux.tasks.getProjectTasks("my-project-id", {
116
+ typeId: "some-type-guid",
117
+ });
72
118
 
73
119
  // All pages merged (bookmark pagination; matches Python client behaviour)
74
- const allTasks = await dalux.tasks.getAllProjectTasks('my-project-id', { typeId: 'some-type-guid' }, false);
120
+ const allTasks = await dalux.tasks.getAllProjectTasks(
121
+ "my-project-id",
122
+ { typeId: "some-type-guid" },
123
+ false,
124
+ );
75
125
  ```
76
126
 
77
127
  **Files: browse (6.1), fetch all pages, download**
78
128
 
79
- `listFiles` and `getAllFiles` call **GET `/6.1/projects/{projectId}/file_areas/{fileAreaId}/files`** (Dalux `listFiles`). Supported query parameters include `folderId`, `updatedAfter`, and `includeProperties` (see the official spec). Single-file metadata still uses **GET `/5.0/.../files/{fileId}`** (`getFile`).
129
+ `listFiles` and `getAllFiles` call **GET `/6.1/projects/{projectId}/file_areas/{fileAreaId}/files`** (Dalux `listFiles`). The documented optional query parameter is `includeProperties`; pagination uses Dalux's returned bookmark links. Single-file metadata still uses **GET `/5.0/.../files/{fileId}`** (`getFile`).
80
130
 
81
131
  ```js
82
- const allFiles = await dalux.files.getAllFiles('my-project-id', 'my-file-area-id', { folderId: 'folder-guid' });
132
+ const allFiles = await dalux.files.getAllFiles(
133
+ "my-project-id",
134
+ "my-file-area-id",
135
+ { folderId: "folder-guid" },
136
+ );
83
137
 
84
- const file = await dalux.files.getFile('my-project-id', 'my-file-area-id', 'file-guid', {
85
- download: true,
86
- savePath: './downloads',
87
- });
138
+ const file = await dalux.files.getFile(
139
+ "my-project-id",
140
+ "my-file-area-id",
141
+ "file-guid",
142
+ {
143
+ download: true,
144
+ savePath: "./downloads",
145
+ },
146
+ );
88
147
 
89
- const saved = await dalux.files.downloadFileFromLink(downloadUrl, 'model.ifc', './downloads');
148
+ const saved = await dalux.files.downloadFileFromLink(
149
+ downloadUrl,
150
+ "model.ifc",
151
+ "./downloads",
152
+ );
90
153
 
91
- await dalux.files.bulkDownloadFolder('my-project-id', 'my-file-area-id', 'folder-guid', './out', {
92
- filenameExtensions: ['.ifc'],
93
- verbose: true,
94
- });
154
+ await dalux.files.bulkDownloadFolder(
155
+ "my-project-id",
156
+ "my-file-area-id",
157
+ "folder-guid",
158
+ "./out",
159
+ {
160
+ filenameExtensions: [".ifc"],
161
+ verbose: true,
162
+ },
163
+ );
95
164
  ```
96
165
 
97
166
  **Upload a file (chunked)**
167
+
98
168
  ```js
99
- const fs = require('fs');
169
+ const fs = require("fs");
100
170
 
101
171
  // 1. Create an upload slot
102
172
  const { uploadGuid } = await dalux.fileUpload.createUpload(
103
- 'my-project-id',
104
- 'my-file-area-id',
105
- { fileName: 'drawing.pdf', mimeType: 'application/pdf' },
173
+ "my-project-id",
174
+ "my-file-area-id",
175
+ { fileName: "drawing.pdf", mimeType: "application/pdf" },
106
176
  );
107
177
 
108
178
  // 2. Upload the file in one chunk
109
- const content = fs.readFileSync('./drawing.pdf');
110
- await dalux.fileUpload.uploadFilePart('my-project-id', 'my-file-area-id', uploadGuid, content);
179
+ const content = fs.readFileSync("./drawing.pdf");
180
+ await dalux.fileUpload.uploadFilePart(
181
+ "my-project-id",
182
+ "my-file-area-id",
183
+ uploadGuid,
184
+ content,
185
+ );
111
186
 
112
187
  // 3. Finalize the upload
113
188
  const result = await dalux.fileUpload.finishUpload(
114
- 'my-project-id',
115
- 'my-file-area-id',
189
+ "my-project-id",
190
+ "my-file-area-id",
116
191
  uploadGuid,
117
- { folderId: 'target-folder-id' },
192
+ { folderId: "target-folder-id" },
118
193
  );
119
- console.log('New file ID:', result.fileId);
194
+ console.log("New file ID:", result.fileId);
120
195
  ```
121
196
 
122
197
  ## Authentication
@@ -129,7 +204,7 @@ All methods return Promises. Network or HTTP errors (4xx / 5xx) are thrown as Ax
129
204
 
130
205
  ```js
131
206
  try {
132
- const project = await dalux.projects.getProject('unknown-id');
207
+ const project = await dalux.projects.getProject("unknown-id");
133
208
  } catch (err) {
134
209
  console.error(err.response?.status, err.response?.data);
135
210
  }
@@ -139,173 +214,178 @@ try {
139
214
 
140
215
  All API namespaces are accessible on the object returned by `createClient`:
141
216
 
142
- | Namespace | Class | Description |
143
- |---|---|---|
144
- | `projects` | `ProjectsApi` | List, get, create and update projects; manage project metadata |
145
- | `companies` | `CompaniesApi` | Project companies (CRUD) |
146
- | `companyCatalog` | `CompanyCatalogApi` | Company catalog (CRUD + metadata) |
147
- | `tasks` | `TasksApi` | Tasks, approvals, safety issues, observations & good practices |
148
- | `fileAreas` | `FileAreasApi` | File areas on a project |
149
- | `files` | `FilesApi` | Files within a file area |
150
- | `folders` | `FoldersApi` | Folders within a file area |
151
- | `fileUpload` | `FileUploadApi` | Chunked file upload (create slot → upload parts → finalize) |
152
- | `fileRevisions` | `FileRevisionsApi` | Download file revision content |
153
- | `forms` | `FormsApi` | Forms and form attachments |
154
- | `users` | `UsersApi` | Company and project users |
155
- | `projectTemplates` | `ProjectTemplatesApi` | Available project templates |
156
- | `inspectionPlans` | `InspectionPlansApi` | Inspection plans, items, zones and registrations |
157
- | `testPlans` | `TestPlansApi` | Test plans, items, zones and registrations |
158
- | `versionSets` | `VersionSetsApi` | Version sets and version set files |
159
- | `workPackages` | `WorkPackagesApi` | Work packages on a project |
217
+ | Namespace | Class | Description |
218
+ | ------------------ | --------------------- | -------------------------------------------------------------- |
219
+ | `projects` | `ProjectsApi` | List, get, create and update projects; manage project metadata |
220
+ | `companies` | `CompaniesApi` | Project companies (CRUD) |
221
+ | `companyCatalog` | `CompanyCatalogApi` | Company catalog (CRUD + metadata) |
222
+ | `tasks` | `TasksApi` | Tasks, approvals, safety issues, observations & good practices |
223
+ | `fileAreas` | `FileAreasApi` | File areas on a project |
224
+ | `files` | `FilesApi` | Files within a file area |
225
+ | `folders` | `FoldersApi` | Folders within a file area |
226
+ | `fileUpload` | `FileUploadApi` | Chunked file upload (create slot → upload parts → finalize) |
227
+ | `fileRevisions` | `FileRevisionsApi` | Download file revision content |
228
+ | `forms` | `FormsApi` | Forms and form attachments |
229
+ | `users` | `UsersApi` | Company and project users |
230
+ | `projectTemplates` | `ProjectTemplatesApi` | Available project templates |
231
+ | `inspectionPlans` | `InspectionPlansApi` | Inspection plans, items, zones and registrations |
232
+ | `testPlans` | `TestPlansApi` | Test plans, items, zones and registrations |
233
+ | `versionSets` | `VersionSetsApi` | Version sets and version set files |
234
+ | `workPackages` | `WorkPackagesApi` | Work packages on a project |
235
+
236
+ `InspectionPlansApi` and `TestPlansApi` mirror the Python client's list behavior: their `list*`
237
+ methods return the typed `items` array by default and accept `fullResponse = true` as the third
238
+ argument to retain pagination metadata and links. Each collection also has a `getAll*` method that
239
+ follows Dalux bookmark pagination automatically.
160
240
 
161
241
  ### ProjectsApi
162
242
 
163
- | Method | HTTP | Path |
164
- |---|---|---|
165
- | `listProjects(params?)` | GET | `/5.1/projects` |
166
- | `getProject(projectId)` | GET | `/5.0/projects/{projectId}` |
167
- | `createProject(body)` | POST | `/5.0/projects` |
168
- | `updateProject(projectId, body)` | PATCH | `/5.0/projects/{projectId}` |
169
- | `listMetadataMappingsForProjects()` | GET | `/1.0/projects/metadata/1.0/mappings` |
170
- | `listMetadataValuesForProjects(key)` | GET | `/1.0/projects/metadata/1.0/mappings/{key}/values` |
171
- | `listProjectMetadata(projectId)` | GET | `/1.0/projects/{projectId}/metadata` |
172
- | `listProjectMetadataMappings(projectId)` | GET | `/1.0/projects/{projectId}/metadata/1.0/mappings` |
173
- | `listProjectMetadataValues(projectId, key)` | GET | `/1.0/projects/{projectId}/metadata/1.0/mappings/{key}/values` |
243
+ | Method | HTTP | Path |
244
+ | -------------------------------------------- | ----- | ---------------------------------------------------------------- |
245
+ | `listProjects(params?)` | GET | `/5.1/projects` |
246
+ | `getProject(projectId)` | GET | `/5.0/projects/{projectId}` |
247
+ | `createProject(body)` | POST | `/5.0/projects` |
248
+ | `updateProject(projectId, body)` | PATCH | `/5.0/projects/{projectId}` |
249
+ | `listMetadataMappingsForProjects()` | GET | `/1.0/projects/metadata/1.0/mappings` |
250
+ | `listMetadataValuesForProjects(key)` | GET | `/1.0/projects/metadata/1.0/mappings/{key}/values` |
251
+ | `listProjectMetadata(projectId)` | GET | `/1.0/projects/{projectId}/metadata` |
252
+ | `listProjectMetadataMappings(projectId)` | GET | `/1.0/projects/{projectId}/metadata/1.0/mappings` |
253
+ | `listProjectMetadataValues(projectId, key)` | GET | `/1.0/projects/{projectId}/metadata/1.0/mappings/{key}/values` |
174
254
 
175
255
  ### CompaniesApi
176
256
 
177
- | Method | HTTP | Path |
178
- |---|---|---|
179
- | `listProjectCompanies(projectId, params?)` | GET | `/3.1/projects/{projectId}/companies` |
180
- | `getProjectCompany(projectId, companyId)` | GET | `/3.0/projects/{projectId}/companies/{companyId}` |
181
- | `createProjectCompany(projectId, body)` | POST | `/3.1/projects/{projectId}/companies` |
257
+ | Method | HTTP | Path |
258
+ | -------------------------------------------------- | ----- | ------------------------------------------------- |
259
+ | `listProjectCompanies(projectId, params?)` | GET | `/3.1/projects/{projectId}/companies` |
260
+ | `getProjectCompany(projectId, companyId)` | GET | `/3.0/projects/{projectId}/companies/{companyId}` |
261
+ | `createProjectCompany(projectId, body)` | POST | `/3.1/projects/{projectId}/companies` |
182
262
  | `updateProjectCompany(projectId, companyId, body)` | PATCH | `/3.0/projects/{projectId}/companies/{companyId}` |
183
263
 
184
264
  ### CompanyCatalogApi
185
265
 
186
- | Method | HTTP | Path |
187
- |---|---|---|
188
- | `getCompanies(params?)` | GET | `/2.2/companyCatalog` |
189
- | `getCompany(catalogCompanyId)` | GET | `/1.2/companyCatalog/{catalogCompanyId}` |
190
- | `createCompany(body)` | POST | `/2.2/companyCatalog` |
191
- | `updateCompany(catalogCompanyId, body)` | PATCH | `/2.1/companyCatalog/{catalogCompanyId}` |
192
- | `listCompanyMetadata(catalogCompanyId)` | GET | `/1.0/companyCatalog/{catalogCompanyId}/metadata` |
193
- | `listCompanyMetadataMappings(catalogCompanyId)` | GET | `/1.0/companyCatalog/{catalogCompanyId}/metadata/1.0/mappings` |
194
- | `listCompanyMetadataValues(catalogCompanyId, key)` | GET | `/1.0/companyCatalog/{catalogCompanyId}/metadata/1.0/mappings/{key}/values` |
195
- | `listMetadataMappingsForCompanies()` | GET | `/1.0/companyCatalog/metadata/1.0/mappings` |
196
- | `listMetadataValuesForCompanies(key)` | GET | `/1.0/companyCatalog/metadata/1.0/mappings/{key}/values` |
266
+ | Method | HTTP | Path |
267
+ | -------------------------------------------------- | ----- | ----------------------------------------------------------------------------- |
268
+ | `getCompanies(params?)` | GET | `/2.2/companyCatalog` |
269
+ | `getCompany(catalogCompanyId)` | GET | `/1.2/companyCatalog/{catalogCompanyId}` |
270
+ | `createCompany(body)` | POST | `/2.2/companyCatalog` |
271
+ | `updateCompany(catalogCompanyId, body)` | PATCH | `/2.1/companyCatalog/{catalogCompanyId}` |
272
+ | `listCompanyMetadata(catalogCompanyId)` | GET | `/1.0/companyCatalog/{catalogCompanyId}/metadata` |
273
+ | `listCompanyMetadataMappings(catalogCompanyId)` | GET | `/1.0/companyCatalog/{catalogCompanyId}/metadata/1.0/mappings` |
274
+ | `listCompanyMetadataValues(catalogCompanyId, key)` | GET | `/1.0/companyCatalog/{catalogCompanyId}/metadata/1.0/mappings/{key}/values` |
275
+ | `listMetadataMappingsForCompanies()` | GET | `/1.0/companyCatalog/metadata/1.0/mappings` |
276
+ | `listMetadataValuesForCompanies(key)` | GET | `/1.0/companyCatalog/metadata/1.0/mappings/{key}/values` |
197
277
 
198
278
  ### TasksApi
199
279
 
200
- | Method | HTTP | Path |
201
- |---|---|---|
202
- | `getProjectTasks(projectId, params?)` | GET | `/5.2/projects/{projectId}/tasks` |
280
+ | Method | HTTP | Path |
281
+ | -------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
282
+ | `getProjectTasks(projectId, params?)` | GET | `/5.2/projects/{projectId}/tasks` |
203
283
  | `getAllProjectTasks(projectId, params?, verbose?)` | GET (paginated) | `/5.2/projects/{projectId}/tasks` — collects all `items` via `nextPage` / `bookmark`; respects `metadata.totalRemainingItems` or `metadata.totalItems` (Python parity) |
204
- | `getTask(projectId, taskId)` | GET | `/3.3/projects/{projectId}/tasks/{taskId}` |
205
- | `getProjectTaskChanges(projectId, params?)` | GET | `/2.2/projects/{projectId}/tasks/changes` |
206
- | `getProjectTaskAttachments(projectId, params?)` | GET | `/1.1/projects/{projectId}/tasks/attachments` |
284
+ | `getTask(projectId, taskId)` | GET | `/3.3/projects/{projectId}/tasks/{taskId}` |
285
+ | `getProjectTaskChanges(projectId, params?)` | GET | `/2.2/projects/{projectId}/tasks/changes` |
286
+ | `getProjectTaskAttachments(projectId, params?)` | GET | `/1.1/projects/{projectId}/tasks/attachments` |
207
287
 
208
288
  Query params: pass OData options on `getProjectTasks` / `getAllProjectTasks` (for example `$filter`). Shorthand: `typeId` is translated to `$filter=data/type/typeId eq '…'` when `$filter` is not set. `TasksApi.normalizeTaskParams` applies the same rules if you build requests manually.
209
289
 
210
290
  ### FileAreasApi
211
291
 
212
- | Method | HTTP | Path |
213
- |---|---|---|
214
- | `getFileAreas(projectId, params?)` | GET | `/5.1/projects/{projectId}/file_areas` |
215
- | `getFileArea(projectId, fileAreaId)` | GET | `/1.0/projects/{projectId}/file_areas/{fileAreaId}` |
292
+ | Method | HTTP | Path |
293
+ | ------------------------------------- | ---- | ----------------------------------------------------- |
294
+ | `getFileAreas(projectId, params?)` | GET | `/5.1/projects/{projectId}/file_areas` |
295
+ | `getFileArea(projectId, fileAreaId)` | GET | `/1.0/projects/{projectId}/file_areas/{fileAreaId}` |
216
296
 
217
297
  ### FilesApi
218
298
 
219
299
  Browse and paginated helpers use **route version 6.1** for the file collection. `getFile` (one file by id) uses **5.0**, as in the [Dalux Build API](https://app.swaggerhub.com/apis-docs/Dalux/DaluxBuild-api/4.14).
220
300
 
221
- | Method | HTTP | Path |
222
- |---|---|---|
223
- | `listFiles(projectId, fileAreaId, params?)` | GET | `/6.1/projects/{projectId}/file_areas/{fileAreaId}/files` |
224
- | `getAllFiles(projectId, fileAreaId, params?, verbose?)` | GET (paginated) | Same — all file `items` via bookmark / `metadata.totalRemainingItems` |
225
- | `getAllFilesInFolder(projectId, fileAreaId, folderId, params?, verbose?)` | — | Uses `getAllFiles` then filters by `data.folderId` |
226
- | `getFile(projectId, fileAreaId, fileId, options?)` | GET | `/5.0/.../files/{fileId}` — optional `{ download: true, savePath }` streams to disk (adds `downloadedFilePath`) |
227
- | `downloadFileFromLink(downloadLink, fileName, savePath?)` | GET | Direct download URL with `X-API-KEY` |
228
- | `bulkDownloadFolder(projectId, fileAreaId, folderId, savePath?, opts?)` | — | Optional `filenameKeywords`, `filenameKeywordsMatch` (`any` / `all`), `filenameExtensions`, `params`, `verbose` |
229
- | `bulkDownloadByIds(projectId, fileAreaId, fileIds, savePath?, options?)` | GET + download | Per-id `getFile` then stream from `downloadLink` |
230
- | `getFilePropertiesMapping(projectId, fileAreaId, fileId)` | GET | `/1.0/.../files/{fileId}/properties/1.0/mappings` |
231
- | `getFilePropertyMappingValues(projectId, fileAreaId, filePropertyId)` | GET | `/1.0/.../files/properties/1.0/mappings/{filePropertyId}/values` |
301
+ | Method | HTTP | Path |
302
+ | --------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------ |
303
+ | `listFiles(projectId, fileAreaId, params?)` | GET | `/6.1/projects/{projectId}/file_areas/{fileAreaId}/files` |
304
+ | `getAllFiles(projectId, fileAreaId, params?, verbose?)` | GET (paginated) | Same — all file `items` via bookmark / `metadata.totalRemainingItems` |
305
+ | `getAllFilesInFolder(projectId, fileAreaId, folderId, params?, verbose?)` | — | Uses `getAllFiles` then filters by `data.folderId` |
306
+ | `getFile(projectId, fileAreaId, fileId, options?)` | GET | `/5.0/.../files/{fileId}` — optional `{ download: true, savePath }` streams to disk (adds `downloadedFilePath`) |
307
+ | `downloadFileFromLink(downloadLink, fileName, savePath?)` | GET | Direct download URL with `X-API-KEY` |
308
+ | `bulkDownloadFolder(projectId, fileAreaId, folderId, savePath?, opts?)` | — | Optional `filenameKeywords`, `filenameKeywordsMatch` (`any` / `all`), `filenameExtensions`, `params`, `verbose` |
309
+ | `bulkDownloadByIds(projectId, fileAreaId, fileIds, savePath?, options?)` | GET + download | Per-id `getFile` then stream from `downloadLink` |
310
+ | `getFilePropertiesMapping(projectId, fileAreaId, fileId)` | GET | `/1.0/.../files/{fileId}/properties/1.0/mappings` |
311
+ | `getFilePropertyMappingValues(projectId, fileAreaId, filePropertyId)` | GET | `/1.0/.../files/properties/1.0/mappings/{filePropertyId}/values` |
232
312
 
233
313
  ### FoldersApi
234
314
 
235
- | Method | HTTP | Path |
236
- |---|---|---|
237
- | `listFolders(projectId, fileAreaId, params?)` | GET | `/5.1/projects/{projectId}/file_areas/{fileAreaId}/folders` |
238
- | `getFolder(projectId, fileAreaId, folderId)` | GET | `/5.0/projects/{projectId}/file_areas/{fileAreaId}/folders/{folderId}` |
239
- | `getFolderFilesProperties(projectId, fileAreaId, folderId)` | GET | `/1.0/.../folders/{folderId}/files/properties/1.0/mappings` |
315
+ | Method | HTTP | Path |
316
+ | ------------------------------------------------------------- | ---- | -------------------------------------------------------------------------- |
317
+ | `listFolders(projectId, fileAreaId, params?)` | GET | `/5.1/projects/{projectId}/file_areas/{fileAreaId}/folders` |
318
+ | `getFolder(projectId, fileAreaId, folderId)` | GET | `/5.0/projects/{projectId}/file_areas/{fileAreaId}/folders/{folderId}` |
319
+ | `getFolderFilesProperties(projectId, fileAreaId, folderId)` | GET | `/1.0/.../folders/{folderId}/files/properties/1.0/mappings` |
240
320
 
241
321
  ### FileUploadApi
242
322
 
243
- | Method | HTTP | Path |
244
- |---|---|---|
245
- | `createUpload(projectId, fileAreaId, body)` | POST | `/1.0/projects/{projectId}/file_areas/{fileAreaId}/upload` |
246
- | `uploadFilePart(projectId, fileAreaId, uploadGuid, chunk)` | POST | `/1.0/.../upload/{uploadGuid}` |
247
- | `finishUpload(projectId, fileAreaId, uploadGuid, body)` | POST | `/2.0/.../upload/{uploadGuid}/finalize` |
323
+ | Method | HTTP | Path |
324
+ | ------------------------------------------------------------ | ---- | -------------------------------------------------------------- |
325
+ | `createUpload(projectId, fileAreaId, body)` | POST | `/1.0/projects/{projectId}/file_areas/{fileAreaId}/upload` |
326
+ | `uploadFilePart(projectId, fileAreaId, uploadGuid, chunk)` | POST | `/1.0/.../upload/{uploadGuid}` |
327
+ | `finishUpload(projectId, fileAreaId, uploadGuid, body)` | POST | `/2.0/.../upload/{uploadGuid}/finalize` |
248
328
 
249
329
  ### FileRevisionsApi
250
330
 
251
- | Method | HTTP | Path |
252
- |---|---|---|
253
- | `getFileRevisionContent(projectId, fileAreaId, fileId, fileRevisionId)` | GET | `/2.0/.../files/{fileId}/revisions/{fileRevisionId}/content` |
331
+ | Method | HTTP | Path |
332
+ | --------------------------------------------------------------------------- | ---- | ------------------------------------------------------------ |
333
+ | `getFileRevisionContent(projectId, fileAreaId, fileId, fileRevisionId)` | GET | `/2.0/.../files/{fileId}/revisions/{fileRevisionId}/content` |
254
334
 
255
335
  ### FormsApi
256
336
 
257
- | Method | HTTP | Path |
258
- |---|---|---|
259
- | `getProjectForms(projectId, params?)` | GET | `/2.1/projects/{projectId}/forms` |
260
- | `getForm(projectId, formId)` | GET | `/1.2/projects/{projectId}/forms/{formId}` |
261
- | `getProjectFormAttachments(projectId, params?)` | GET | `/2.1/projects/{projectId}/forms/attachments` |
337
+ | Method | HTTP | Path |
338
+ | -------------------------------------------------- | ---- | ------------------------------------------------ |
339
+ | `getProjectForms(projectId, params?)` | GET | `/2.1/projects/{projectId}/forms` |
340
+ | `getForm(projectId, formId)` | GET | `/1.2/projects/{projectId}/forms/{formId}` |
341
+ | `getProjectFormAttachments(projectId, params?)` | GET | `/2.1/projects/{projectId}/forms/attachments` |
262
342
 
263
343
  ### UsersApi
264
344
 
265
- | Method | HTTP | Path |
266
- |---|---|---|
267
- | `getUser(userId)` | GET | `/1.1/users/{userId}` |
268
- | `listProjectUsers(projectId, params?)` | GET | `/1.2/projects/{projectId}/users` |
269
- | `getProjectUser(projectId, userId)` | GET | `/1.1/projects/{projectId}/users/{userId}` |
345
+ | Method | HTTP | Path |
346
+ | ----------------------------------------- | ---- | ---------------------------------------------- |
347
+ | `getUser(userId)` | GET | `/1.1/users/{userId}` |
348
+ | `listProjectUsers(projectId, params?)` | GET | `/1.2/projects/{projectId}/users` |
349
+ | `getProjectUser(projectId, userId)` | GET | `/1.1/projects/{projectId}/users/{userId}` |
270
350
 
271
351
  ### ProjectTemplatesApi
272
352
 
273
- | Method | HTTP | Path |
274
- |---|---|---|
275
- | `listProjectTemplates(params?)` | GET | `/1.1/projectTemplates` |
353
+ | Method | HTTP | Path |
354
+ | ---------------------------------- | ---- | --------------------------- |
355
+ | `listProjectTemplates(params?)` | GET | `/1.1/projectTemplates` |
276
356
 
277
357
  ### InspectionPlansApi
278
358
 
279
- | Method | HTTP | Path |
280
- |---|---|---|
281
- | `listInspectionPlans(projectId, params?)` | GET | `/1.2/projects/{projectId}/inspectionPlans` |
282
- | `listInspectionPlanItems(projectId, params?)` | GET | `/1.1/projects/{projectId}/inspectionPlanItems` |
283
- | `listInspectionPlanItemZones(projectId, params?)` | GET | `/1.1/projects/{projectId}/inspectionPlanItemZones` |
284
- | `listInspectionPlanRegistrations(projectId, params?)` | GET | `/2.1/projects/{projectId}/inspectionPlanRegistrations` |
359
+ | Method | HTTP | Path |
360
+ | -------------------------------------------------------- | ---- | ------------------------------------------------------------ |
361
+ | `listInspectionPlans(projectId, params?)` | GET | `/1.2/projects/{projectId}/inspectionPlans` |
362
+ | `listInspectionPlanItems(projectId, params?)` | GET | `/1.1/projects/{projectId}/inspectionPlanItems` |
363
+ | `listInspectionPlanItemZones(projectId, params?)` | GET | `/1.1/projects/{projectId}/inspectionPlanItemZones` |
364
+ | `listInspectionPlanRegistrations(projectId, params?)` | GET | `/2.1/projects/{projectId}/inspectionPlanRegistrations` |
285
365
 
286
366
  ### TestPlansApi
287
367
 
288
- | Method | HTTP | Path |
289
- |---|---|---|
290
- | `listTestPlans(projectId, params?)` | GET | `/1.2/projects/{projectId}/testPlans` |
291
- | `listTestPlanItems(projectId, params?)` | GET | `/1.1/projects/{projectId}/testPlanItems` |
292
- | `listTestPlanItemZones(projectId, params?)` | GET | `/1.1/projects/{projectId}/testPlanItemZones` |
293
- | `listTestPlanRegistrations(projectId, params?)` | GET | `/1.1/projects/{projectId}/testPlanRegistrations` |
368
+ | Method | HTTP | Path |
369
+ | ---------------------------------------------------- | ---- | -------------------------------------------------- |
370
+ | `listTestPlans(projectId, params?)` | GET | `/1.2/projects/{projectId}/testPlans` |
371
+ | `listTestPlanItems(projectId, params?)` | GET | `/1.1/projects/{projectId}/testPlanItems` |
372
+ | `listTestPlanItemZones(projectId, params?)` | GET | `/1.1/projects/{projectId}/testPlanItemZones` |
373
+ | `listTestPlanRegistrations(projectId, params?)` | GET | `/1.1/projects/{projectId}/testPlanRegistrations` |
294
374
 
295
375
  ### VersionSetsApi
296
376
 
297
- | Method | HTTP | Path |
298
- |---|---|---|
299
- | `getVersionSets(projectId, params?)` | GET | `/2.1/projects/{projectId}/version_sets` |
300
- | `getVersionSet(projectId, versionSetId)` | GET | `/2.0/projects/{projectId}/version_sets/{versionSetId}` |
301
- | `listFileAreaVersionSets(projectId, fileAreaId, params?)` | GET | `/2.1/projects/{projectId}/file_areas/{fileAreaId}/version_sets` |
302
- | `listVersionSetFiles(projectId, versionSetId, params?)` | GET | `/3.0/projects/{projectId}/version_sets/{versionSetId}/files` |
377
+ | Method | HTTP | Path |
378
+ | ------------------------------------------------------------- | ---- | ---------------------------------------------------------------- |
379
+ | `getVersionSets(projectId, params?)` | GET | `/2.1/projects/{projectId}/version_sets` |
380
+ | `getVersionSet(projectId, versionSetId)` | GET | `/2.0/projects/{projectId}/version_sets/{versionSetId}` |
381
+ | `listFileAreaVersionSets(projectId, fileAreaId, params?)` | GET | `/2.1/projects/{projectId}/file_areas/{fileAreaId}/version_sets` |
382
+ | `listVersionSetFiles(projectId, versionSetId, params?)` | GET | `/3.0/projects/{projectId}/version_sets/{versionSetId}/files` |
303
383
 
304
384
  ### WorkPackagesApi
305
385
 
306
- | Method | HTTP | Path |
307
- |---|---|---|
308
- | `listWorkPackages(projectId, params?)` | GET | `/1.0/projects/{projectId}/workpackages` |
386
+ | Method | HTTP | Path |
387
+ | ----------------------------------------- | ---- | --------------------------------------------- |
388
+ | `listWorkPackages(projectId, params?)` | GET | `/1.0/projects/{projectId}/workpackages` |
309
389
 
310
390
  ## Advanced Usage
311
391
 
@@ -314,51 +394,41 @@ Browse and paginated helpers use **route version 6.1** for the file collection.
314
394
  You can also instantiate API classes directly with a shared `ApiClient`:
315
395
 
316
396
  ```js
317
- const { Configuration, ApiClient, ProjectsApi, TasksApi } = require('@bruadam/dalux-build-api');
397
+ const {
398
+ Configuration,
399
+ ApiClient,
400
+ ProjectsApi,
401
+ TasksApi,
402
+ } = require("dalux-build-api");
318
403
 
319
404
  const config = new Configuration({
320
- baseUrl: 'https://<your-company>.dalux.com/api',
321
- apiKey: 'YOUR_API_KEY',
405
+ baseUrl: "https://<your-company>.dalux.com/api",
406
+ apiKey: "YOUR_API_KEY",
322
407
  });
323
408
  const apiClient = new ApiClient(config);
324
409
 
325
410
  const projects = new ProjectsApi(apiClient);
326
- const tasks = new TasksApi(apiClient);
411
+ const tasks = new TasksApi(apiClient);
327
412
  ```
328
413
 
329
- ## Development
414
+ ## Testing
330
415
 
331
416
  ```bash
332
- # Install dependencies
333
- npm install
334
-
335
- # Run tests with coverage
336
- npm test
417
+ npm install # from the repo root (this is an npm workspace)
418
+ npm test --workspace=javascript # Jest, with coverage
337
419
  ```
338
420
 
339
- ## Maintainer: npm releases
340
-
341
- ### Automatic (push to `main`)
342
-
343
- 1. Merge to `main` (or push directly). **CI** must pass.
344
- 2. After **both** the Node and Python test jobs succeed, **CI** calls **Publish npm Package** via `workflow_call` (same run as CI, so it does not depend on `workflow_run`).
345
- 3. The publish job only runs when that push changes `package.json`, `package-lock.json`, `src/`, or `test/`.
346
- 4. **Version bump** uses the same rules as the Python publisher ([commit message tokens](python/README.md#maintainer-pypi-releases)), but the **current** semver is **`max(python/pyproject.toml, package.json)`** so npm catches up when the Python package is ahead.
347
- 5. It runs tests again, publishes to npm, then pushes `chore: release vX.Y.Z [skip npm]`, tag `vX.Y.Z`, and a **GitHub Release** (unless the tag already exists). **`[skip npm]`** skips a second publish pass and skips **CI** on that sync commit.
348
-
349
- ### Manual
350
-
351
- - **Actions → Publish npm Package → Run workflow** for **patch / minor / major** (same test → publish → sync commit → tag → **GitHub Release**). The entry workflow is [npm-publish.yml](.github/workflows/npm-publish.yml); it delegates to [npm-publish-reusable.yml](.github/workflows/npm-publish-reusable.yml) (GitHub requires `workflow_call` in its own file).
352
- - **GitHub Release (published)** publishes the version already in `package.json` at that tag (no version bump in the workflow).
353
-
354
- Create a GitHub **environment** named `npm` with secret **`NPM_TOKEN`**. If pushes or releases from `GITHUB_TOKEN` are blocked, set **`RELEASE_PAT`** as described in [python/README.md](python/README.md#maintainer-pypi-releases).
355
-
356
- ### First publish and `E404` on `npm publish`
421
+ CI runs this on Node.js 20/22/24 — see
422
+ [`../.github/workflows/tests.yml`](../.github/workflows/tests.yml).
357
423
 
358
- If CI fails with **`404 Not Found - PUT …/@bruadam/dalux-build-api`** and **`is not in this registry`**, the package does not exist on npm yet **or** your token is not allowed to **create** it.
424
+ ## Releasing
359
425
 
360
- - **Classic [Automation](https://docs.npmjs.com/about-access-tokens#automation-tokens)** tokens only publish packages your account **already** maintains. They cannot create a **new** package name. Fix: use a **granular access token** with write access to the `@bruadam` scope (or all packages), or run **`npm publish --access public`** once locally while logged in as the owner, then switch CI back to an automation token.
361
- - Confirm the package is not taken by someone else: [https://www.npmjs.com/package/@bruadam/dalux-build-api](https://www.npmjs.com/package/@bruadam/dalux-build-api).
426
+ This package is versioned and published together with the Python client by
427
+ [Changesets](https://github.com/changesets/changesets) — there is no manual
428
+ edit of `version` in `package.json`, and nothing publishes to npm unless the
429
+ full test suite (Node.js, Python, webhook server) passes first. See
430
+ [../CONTRIBUTING.md](../CONTRIBUTING.md#how-releases-work) for the full flow
431
+ and [../README.md](../README.md#releasing) for the PyPI side of it.
362
432
 
363
433
  ## License
364
434