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
@@ -1,5 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ const { convertToModel, convertToModelList } = require('../models/convert');
4
+ const {
5
+ TaskSchema,
6
+ TaskChangeSchema,
7
+ TasksListResponseSchema,
8
+ TaskResponseSchema,
9
+ TaskChangesSchema,
10
+ TaskAttachmentsListResponseSchema,
11
+ } = require('../models/tasks');
12
+
3
13
  /**
4
14
  * Build query params for GET /5.2/projects/.../tasks (OData).
5
15
  * If params contains typeId and no $filter, expands to
@@ -39,8 +49,9 @@ class TasksApi {
39
49
  * OData $filter on task type, or pass $filter (and other OData options) directly.
40
50
  * @returns {Promise<object>}
41
51
  */
42
- getProjectTasks(projectId, params = {}) {
43
- return this._client.get(`/5.2/projects/${projectId}/tasks`, normalizeTaskParams(params));
52
+ async getProjectTasks(projectId, params = {}) {
53
+ const response = await this._client.get(`/5.2/projects/${projectId}/tasks`, normalizeTaskParams(params));
54
+ return convertToModel(response, TasksListResponseSchema, 'TasksListResponse');
44
55
  }
45
56
 
46
57
  /**
@@ -119,7 +130,7 @@ class TasksApi {
119
130
  if (verbose) {
120
131
  console.log(`Done. Total tasks retrieved: ${allItems.length}`);
121
132
  }
122
- return allItems;
133
+ return convertToModelList(allItems, TaskSchema, 'Task');
123
134
  }
124
135
 
125
136
  /**
@@ -129,8 +140,9 @@ class TasksApi {
129
140
  * @param {string} taskId
130
141
  * @returns {Promise<object>}
131
142
  */
132
- getTask(projectId, taskId) {
133
- return this._client.get(`/3.3/projects/${projectId}/tasks/${taskId}`);
143
+ async getTask(projectId, taskId) {
144
+ const response = await this._client.get(`/3.3/projects/${projectId}/tasks/${taskId}`);
145
+ return convertToModel(response, TaskResponseSchema, 'TaskResponse');
134
146
  }
135
147
 
136
148
  /**
@@ -140,8 +152,9 @@ class TasksApi {
140
152
  * @param {object} [params] - Optional filters (e.g. updatedAfter)
141
153
  * @returns {Promise<object>}
142
154
  */
143
- getProjectTaskChanges(projectId, params = {}) {
144
- return this._client.get(`/2.2/projects/${projectId}/tasks/changes`, params);
155
+ async getProjectTaskChanges(projectId, params = {}) {
156
+ const response = await this._client.get(`/2.2/projects/${projectId}/tasks/changes`, params);
157
+ return convertToModel(response, TaskChangesSchema, 'TaskChanges');
145
158
  }
146
159
 
147
160
  /**
@@ -151,8 +164,9 @@ class TasksApi {
151
164
  * @param {object} [params] - Optional filters (e.g. updatedAfter)
152
165
  * @returns {Promise<object>}
153
166
  */
154
- getProjectTaskAttachments(projectId, params = {}) {
155
- return this._client.get(`/1.1/projects/${projectId}/tasks/attachments`, params);
167
+ async getProjectTaskAttachments(projectId, params = {}) {
168
+ const response = await this._client.get(`/1.1/projects/${projectId}/tasks/attachments`, params);
169
+ return convertToModel(response, TaskAttachmentsListResponseSchema, 'TaskAttachmentsListResponse');
156
170
  }
157
171
 
158
172
  /**
@@ -164,12 +178,13 @@ class TasksApi {
164
178
  */
165
179
  async getAllProjectTaskChanges(projectId, params = {}, verbose = false) {
166
180
  const { paginate } = require('../utils/pagination');
167
- return paginate(
181
+ const raw = await paginate(
168
182
  `/2.2/projects/${projectId}/tasks/changes`,
169
183
  this._client,
170
184
  params,
171
185
  verbose,
172
186
  );
187
+ return convertToModelList(raw, TaskChangeSchema, 'TaskChange');
173
188
  }
174
189
  }
175
190
 
@@ -1,5 +1,18 @@
1
1
  'use strict';
2
2
 
3
+ const { convertToModel, convertToModelList } = require('../models/convert');
4
+ const { paginate } = require('../utils/pagination');
5
+ const {
6
+ TestPlanSchema,
7
+ TestPlanItemSchema,
8
+ TestPlanItemZoneSchema,
9
+ TestPlanRegistrationSchema,
10
+ TestPlansListResponseSchema,
11
+ TestPlanItemsListResponseSchema,
12
+ TestPlanItemZonesListResponseSchema,
13
+ TestPlanRegistrationsListResponseSchema,
14
+ } = require('../models/testPlans');
15
+
3
16
  /**
4
17
  * API methods for test plans.
5
18
  */
@@ -16,10 +29,27 @@ class TestPlansApi {
16
29
  * GET /1.2/projects/{projectId}/testPlans
17
30
  * @param {string} projectId
18
31
  * @param {object} [params]
19
- * @returns {Promise<object>}
32
+ * @param {boolean} [fullResponse=false]
33
+ * @returns {Promise<object[]|object>}
20
34
  */
21
- listTestPlans(projectId, params = {}) {
22
- return this._client.get(`/1.2/projects/${projectId}/testPlans`, params);
35
+ async listTestPlans(projectId, params = {}, fullResponse = false) {
36
+ const response = await this._client.get(`/1.2/projects/${projectId}/testPlans`, params);
37
+ const result = convertToModel(
38
+ response,
39
+ TestPlansListResponseSchema,
40
+ 'TestPlansListResponse',
41
+ );
42
+ return fullResponse ? result : (result?.items || []);
43
+ }
44
+
45
+ async getAllTestPlans(projectId, params = {}, verbose = false) {
46
+ const items = await paginate(
47
+ `/1.2/projects/${projectId}/testPlans`,
48
+ this._client,
49
+ params,
50
+ verbose,
51
+ );
52
+ return convertToModelList(items, TestPlanSchema, 'TestPlan');
23
53
  }
24
54
 
25
55
  /**
@@ -29,8 +59,24 @@ class TestPlansApi {
29
59
  * @param {object} [params]
30
60
  * @returns {Promise<object>}
31
61
  */
32
- listTestPlanItems(projectId, params = {}) {
33
- return this._client.get(`/1.1/projects/${projectId}/testPlanItems`, params);
62
+ async listTestPlanItems(projectId, params = {}, fullResponse = false) {
63
+ const response = await this._client.get(`/1.1/projects/${projectId}/testPlanItems`, params);
64
+ const result = convertToModel(
65
+ response,
66
+ TestPlanItemsListResponseSchema,
67
+ 'TestPlanItemsListResponse',
68
+ );
69
+ return fullResponse ? result : (result?.items || []);
70
+ }
71
+
72
+ async getAllTestPlanItems(projectId, params = {}, verbose = false) {
73
+ const items = await paginate(
74
+ `/1.1/projects/${projectId}/testPlanItems`,
75
+ this._client,
76
+ params,
77
+ verbose,
78
+ );
79
+ return convertToModelList(items, TestPlanItemSchema, 'TestPlanItem');
34
80
  }
35
81
 
36
82
  /**
@@ -40,8 +86,24 @@ class TestPlansApi {
40
86
  * @param {object} [params]
41
87
  * @returns {Promise<object>}
42
88
  */
43
- listTestPlanItemZones(projectId, params = {}) {
44
- return this._client.get(`/1.1/projects/${projectId}/testPlanItemZones`, params);
89
+ async listTestPlanItemZones(projectId, params = {}, fullResponse = false) {
90
+ const response = await this._client.get(`/1.1/projects/${projectId}/testPlanItemZones`, params);
91
+ const result = convertToModel(
92
+ response,
93
+ TestPlanItemZonesListResponseSchema,
94
+ 'TestPlanItemZonesListResponse',
95
+ );
96
+ return fullResponse ? result : (result?.items || []);
97
+ }
98
+
99
+ async getAllTestPlanItemZones(projectId, params = {}, verbose = false) {
100
+ const items = await paginate(
101
+ `/1.1/projects/${projectId}/testPlanItemZones`,
102
+ this._client,
103
+ params,
104
+ verbose,
105
+ );
106
+ return convertToModelList(items, TestPlanItemZoneSchema, 'TestPlanItemZone');
45
107
  }
46
108
 
47
109
  /**
@@ -51,8 +113,24 @@ class TestPlansApi {
51
113
  * @param {object} [params]
52
114
  * @returns {Promise<object>}
53
115
  */
54
- listTestPlanRegistrations(projectId, params = {}) {
55
- return this._client.get(`/1.1/projects/${projectId}/testPlanRegistrations`, params);
116
+ async listTestPlanRegistrations(projectId, params = {}, fullResponse = false) {
117
+ const response = await this._client.get(`/1.1/projects/${projectId}/testPlanRegistrations`, params);
118
+ const result = convertToModel(
119
+ response,
120
+ TestPlanRegistrationsListResponseSchema,
121
+ 'TestPlanRegistrationsListResponse',
122
+ );
123
+ return fullResponse ? result : (result?.items || []);
124
+ }
125
+
126
+ async getAllTestPlanRegistrations(projectId, params = {}, verbose = false) {
127
+ const items = await paginate(
128
+ `/1.1/projects/${projectId}/testPlanRegistrations`,
129
+ this._client,
130
+ params,
131
+ verbose,
132
+ );
133
+ return convertToModelList(items, TestPlanRegistrationSchema, 'TestPlanRegistration');
56
134
  }
57
135
  }
58
136
 
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ const { convertToModel } = require('../models/convert');
4
+ const { UserResponseSchema, UsersListResponseSchema } = require('../models/users');
5
+
3
6
  /**
4
7
  * API methods for users.
5
8
  */
@@ -15,10 +18,11 @@ class UsersApi {
15
18
  * Get a specific user.
16
19
  * GET /1.1/users/{userId}
17
20
  * @param {string} userId
18
- * @returns {Promise<object>}
21
+ * @returns {Promise<object>} UserResponse ({ data: User, links? })
19
22
  */
20
- getUser(userId) {
21
- return this._client.get(`/1.1/users/${userId}`);
23
+ async getUser(userId) {
24
+ const response = await this._client.get(`/1.1/users/${userId}`);
25
+ return convertToModel(response, UserResponseSchema, 'UserResponse');
22
26
  }
23
27
 
24
28
  /**
@@ -26,10 +30,11 @@ class UsersApi {
26
30
  * GET /1.2/projects/{projectId}/users
27
31
  * @param {string} projectId
28
32
  * @param {object} [params]
29
- * @returns {Promise<object>}
33
+ * @returns {Promise<object>} UsersListResponse ({ items: ProjectUser[], metadata?, links? })
30
34
  */
31
- listProjectUsers(projectId, params = {}) {
32
- return this._client.get(`/1.2/projects/${projectId}/users`, params);
35
+ async listProjectUsers(projectId, params = {}) {
36
+ const response = await this._client.get(`/1.2/projects/${projectId}/users`, params);
37
+ return convertToModel(response, UsersListResponseSchema, 'UsersListResponse');
33
38
  }
34
39
 
35
40
  /**
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ const { convertToModel } = require('../models/convert');
4
+ const { VersionSetsListResponseSchema, VersionSetResponseSchema } = require('../models/versionSets');
5
+ const { FilesListResponseSchema } = require('../models/files');
6
+
3
7
  /**
4
8
  * API methods for version sets.
5
9
  */
@@ -16,10 +20,11 @@ class VersionSetsApi {
16
20
  * GET /2.1/projects/{projectId}/version_sets
17
21
  * @param {string} projectId
18
22
  * @param {object} [params]
19
- * @returns {Promise<object>}
23
+ * @returns {Promise<object>} VersionSetsListResponse ({ items: VersionSet[], metadata?, links? })
20
24
  */
21
- getVersionSets(projectId, params = {}) {
22
- return this._client.get(`/2.1/projects/${projectId}/version_sets`, params);
25
+ async getVersionSets(projectId, params = {}) {
26
+ const response = await this._client.get(`/2.1/projects/${projectId}/version_sets`, params);
27
+ return convertToModel(response, VersionSetsListResponseSchema, 'VersionSetsListResponse');
23
28
  }
24
29
 
25
30
  /**
@@ -27,10 +32,11 @@ class VersionSetsApi {
27
32
  * GET /2.0/projects/{projectId}/version_sets/{versionSetId}
28
33
  * @param {string} projectId
29
34
  * @param {string} versionSetId
30
- * @returns {Promise<object>}
35
+ * @returns {Promise<object>} VersionSetResponse ({ data: VersionSet, links? })
31
36
  */
32
- getVersionSet(projectId, versionSetId) {
33
- return this._client.get(`/2.0/projects/${projectId}/version_sets/${versionSetId}`);
37
+ async getVersionSet(projectId, versionSetId) {
38
+ const response = await this._client.get(`/2.0/projects/${projectId}/version_sets/${versionSetId}`);
39
+ return convertToModel(response, VersionSetResponseSchema, 'VersionSetResponse');
34
40
  }
35
41
 
36
42
  /**
@@ -39,13 +45,14 @@ class VersionSetsApi {
39
45
  * @param {string} projectId
40
46
  * @param {string} fileAreaId
41
47
  * @param {object} [params]
42
- * @returns {Promise<object>}
48
+ * @returns {Promise<object>} VersionSetsListResponse
43
49
  */
44
- listFileAreaVersionSets(projectId, fileAreaId, params = {}) {
45
- return this._client.get(
50
+ async listFileAreaVersionSets(projectId, fileAreaId, params = {}) {
51
+ const response = await this._client.get(
46
52
  `/2.1/projects/${projectId}/file_areas/${fileAreaId}/version_sets`,
47
53
  params,
48
54
  );
55
+ return convertToModel(response, VersionSetsListResponseSchema, 'VersionSetsListResponse');
49
56
  }
50
57
 
51
58
  /**
@@ -54,13 +61,14 @@ class VersionSetsApi {
54
61
  * @param {string} projectId
55
62
  * @param {string} versionSetId
56
63
  * @param {object} [params]
57
- * @returns {Promise<object>}
64
+ * @returns {Promise<object>} FilesListResponse (cross-endpoint reuse of the Files model, matches Python)
58
65
  */
59
- listVersionSetFiles(projectId, versionSetId, params = {}) {
60
- return this._client.get(
66
+ async listVersionSetFiles(projectId, versionSetId, params = {}) {
67
+ const response = await this._client.get(
61
68
  `/3.0/projects/${projectId}/version_sets/${versionSetId}/files`,
62
69
  params,
63
70
  );
71
+ return convertToModel(response, FilesListResponseSchema, 'FilesListResponse');
64
72
  }
65
73
  }
66
74
 
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ const { convertToModel } = require('../models/convert');
4
+ const { WorkPackagesListResponseSchema } = require('../models/workPackages');
5
+
3
6
  /**
4
7
  * API methods for work packages on a project.
5
8
  */
@@ -16,10 +19,11 @@ class WorkPackagesApi {
16
19
  * GET /1.0/projects/{projectId}/workpackages
17
20
  * @param {string} projectId
18
21
  * @param {object} [params]
19
- * @returns {Promise<object>}
22
+ * @returns {Promise<object>} WorkPackagesListResponse ({ items: WorkPackage[], metadata?, links? })
20
23
  */
21
- listWorkPackages(projectId, params = {}) {
22
- return this._client.get(`/1.0/projects/${projectId}/workpackages`, params);
24
+ async listWorkPackages(projectId, params = {}) {
25
+ const response = await this._client.get(`/1.0/projects/${projectId}/workpackages`, params);
26
+ return convertToModel(response, WorkPackagesListResponseSchema, 'WorkPackagesListResponse');
23
27
  }
24
28
  }
25
29
 
package/src/browser.js ADDED
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ const RESOURCES = [
4
+ 'projects',
5
+ 'companies',
6
+ 'companyCatalog',
7
+ 'fileAreas',
8
+ 'fileRevisions',
9
+ 'fileUpload',
10
+ 'files',
11
+ 'folders',
12
+ 'forms',
13
+ 'inspectionPlans',
14
+ 'projectTemplates',
15
+ 'tasks',
16
+ 'testPlans',
17
+ 'users',
18
+ 'versionSets',
19
+ 'workPackages',
20
+ ];
21
+
22
+ class DaluxProxyError extends Error {
23
+ constructor(message, { name = 'DaluxProxyError', status, details } = {}) {
24
+ super(message);
25
+ this.name = name;
26
+ this.status = status;
27
+ this.details = details;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Create a browser-safe Dalux client backed by a same-origin server route.
33
+ * The Dalux API key remains on the server; this client never accepts one.
34
+ *
35
+ * @param {object} [options]
36
+ * @param {string} [options.url='/api/dalux']
37
+ * @param {typeof fetch} [options.fetch]
38
+ * @param {Record<string, string>} [options.headers]
39
+ */
40
+ function createBrowserClient({ url = '/api/dalux', fetch: fetchImpl, headers = {} } = {}) {
41
+ const request = fetchImpl || globalThis.fetch;
42
+ if (typeof request !== 'function') {
43
+ throw new Error('A fetch implementation is required');
44
+ }
45
+
46
+ const invoke = async (resource, method, args) => {
47
+ const response = await request(url, {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json', ...headers },
50
+ body: JSON.stringify({ resource, method, args }),
51
+ });
52
+
53
+ let payload;
54
+ try {
55
+ payload = await response.json();
56
+ } catch {
57
+ throw new DaluxProxyError(`Dalux proxy returned HTTP ${response.status} without JSON`, {
58
+ status: response.status,
59
+ });
60
+ }
61
+
62
+ if (!response.ok || !payload.ok) {
63
+ const error = payload.error || {};
64
+ throw new DaluxProxyError(error.message || `Dalux proxy returned HTTP ${response.status}`, {
65
+ name: error.name,
66
+ status: response.status,
67
+ details: error.details,
68
+ });
69
+ }
70
+ return payload.data;
71
+ };
72
+
73
+ return Object.fromEntries(RESOURCES.map((resource) => [
74
+ resource,
75
+ new Proxy({}, {
76
+ get(_target, method) {
77
+ if (typeof method !== 'string' || method === 'then') return undefined;
78
+ return (...args) => invoke(resource, method, args);
79
+ },
80
+ }),
81
+ ]));
82
+ }
83
+
84
+ module.exports = { createBrowserClient, DaluxProxyError };
package/src/index.js CHANGED
@@ -39,6 +39,8 @@ const {
39
39
  resolveFolderIdFromNamedPath,
40
40
  } = require('./utils');
41
41
 
42
+ const models = require('./models');
43
+
42
44
  /**
43
45
  * Create a fully configured Dalux Build API client.
44
46
  *
@@ -125,4 +127,7 @@ module.exports = {
125
127
  validateFolderId,
126
128
  resolveFileAreaByName,
127
129
  resolveFolderIdFromNamedPath,
130
+ // Data models (zod schemas) - both `models.FolderSchema` and top-level `FolderSchema` work
131
+ models,
132
+ ...models,
128
133
  };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+
5
+ /**
6
+ * API response link. Mirrors python/dalux_build/models/common.py::Link.
7
+ */
8
+ const LinkSchema = z.object({
9
+ rel: z.string(),
10
+ href: z.string(),
11
+ method: z.string().nullish(),
12
+ });
13
+
14
+ /**
15
+ * Response metadata with pagination info. Mirrors common.py::Metadata.
16
+ */
17
+ const MetadataSchema = z.object({
18
+ totalItems: z.number().nullish(),
19
+ totalRemainingItems: z.number().nullish(),
20
+ });
21
+
22
+ module.exports = { LinkSchema, MetadataSchema };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const { ProjectCompanySchema } = require('../projects');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /** Mirrors models/companies/models.py, which re-exports ProjectCompany. */
7
+ const CompaniesListResponseSchema = listResponseSchema(ProjectCompanySchema);
8
+ const CompanyResponseSchema = singleResponseSchema(ProjectCompanySchema);
9
+
10
+ module.exports = {
11
+ ProjectCompanySchema,
12
+ CompaniesListResponseSchema,
13
+ CompanyResponseSchema,
14
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /**
7
+ * Mirrors models/company_catalog/responses.py. These are exported for name
8
+ * parity but, like their Python counterparts, are NOT what the API layer
9
+ * actually uses — company_catalog.py's get_companies/get_company return
10
+ * CompaniesListResponse/CompanyResponse (typed ProjectCompany) instead.
11
+ * See src/api/CompanyCatalogApi.js.
12
+ */
13
+ const CompanyCatalogListResponseSchema = listResponseSchema(z.any());
14
+ const CompanyCatalogResponseSchema = singleResponseSchema(z.any());
15
+
16
+ module.exports = {
17
+ CompanyCatalogListResponseSchema,
18
+ CompanyCatalogResponseSchema,
19
+ };
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { ValidationError } = require('../utils/errors');
5
+ const { unwrapData } = require('./helpers');
6
+
7
+ /**
8
+ * Validate and convert a raw API response into a typed model, throwing a
9
+ * ValidationError if the response doesn't match `schema`. Mirrors
10
+ * response_converter.py::convert_to_model, without its legacy-compat
11
+ * fallback branches (the JS test suite ships schema-valid fixtures).
12
+ * @param {*} response
13
+ * @param {import('zod').ZodTypeAny} schema
14
+ * @param {string} [schemaName]
15
+ * @returns {*}
16
+ */
17
+ function convertToModel(response, schema, schemaName = 'model') {
18
+ if (response === null || response === undefined) return null;
19
+ try {
20
+ return schema.parse(response);
21
+ } catch (err) {
22
+ if (err instanceof z.ZodError) {
23
+ throw new ValidationError(`Failed to convert response to ${schemaName}: ${err.message}`);
24
+ }
25
+ throw err;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Validate and convert a list of raw items into typed models.
31
+ * Used by get_all_* pagination methods, which convert each item after
32
+ * pagination has already collected the raw list.
33
+ * @param {Array} items
34
+ * @param {import('zod').ZodTypeAny} itemSchema
35
+ * @param {string} [schemaName]
36
+ * @returns {Array}
37
+ */
38
+ function convertToModelList(items, itemSchema, schemaName = 'model') {
39
+ if (!Array.isArray(items)) return [];
40
+ const wrapped = unwrapData(itemSchema);
41
+ return items.map((item) => convertToModel(item, wrapped, schemaName));
42
+ }
43
+
44
+ module.exports = { convertToModel, convertToModelList };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /** Mirrors models/file_areas/models.py::FileArea (all fields required). */
7
+ const FileAreaSchema = z.object({
8
+ fileAreaId: z.string(),
9
+ fileAreaName: z.string(),
10
+ fileAreaType: z.string(),
11
+ });
12
+
13
+ const FileAreasListResponseSchema = listResponseSchema(FileAreaSchema);
14
+ const FileAreaResponseSchema = singleResponseSchema(FileAreaSchema);
15
+
16
+ module.exports = {
17
+ FileAreaSchema,
18
+ FileAreasListResponseSchema,
19
+ FileAreaResponseSchema,
20
+ };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+
5
+ /**
6
+ * Mirrors models/file_revisions/models.py::FileRevision — an empty
7
+ * placeholder in Python too (get_file_revision_content returns raw
8
+ * binary/content in both packages, so there's nothing to validate).
9
+ */
10
+ const FileRevisionSchema = z.object({}).passthrough();
11
+
12
+ module.exports = { FileRevisionSchema };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+
5
+ /**
6
+ * Mirrors models/file_upload/models.py::FileUpload — empty placeholder in
7
+ * Python too; upload/part/finalize responses are provider-specific and
8
+ * stay raw in both packages.
9
+ */
10
+ const FileUploadSchema = z.object({}).passthrough();
11
+
12
+ module.exports = { FileUploadSchema };