@twinfinity/twin 0.0.0-feature.31596

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 (245) hide show
  1. package/README.md +277 -0
  2. package/dist/BuildInfo.d.ts +25 -0
  3. package/dist/BuildInfo.d.ts.map +1 -0
  4. package/dist/BuildInfo.js +15 -0
  5. package/dist/BuildInfo.js.map +1 -0
  6. package/dist/TwinClient.d.ts +823 -0
  7. package/dist/TwinClient.d.ts.map +1 -0
  8. package/dist/TwinClient.js +1487 -0
  9. package/dist/TwinClient.js.map +1 -0
  10. package/dist/errors.d.ts +289 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +357 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +4 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/tsconfig.tsbuildinfo +1 -0
  19. package/dist/types/Twin.d.ts +7 -0
  20. package/dist/types/Twin.d.ts.map +1 -0
  21. package/dist/types/Twin.js +6 -0
  22. package/dist/types/Twin.js.map +1 -0
  23. package/dist/types/TwinGeometryInfo.d.ts +7 -0
  24. package/dist/types/TwinGeometryInfo.d.ts.map +1 -0
  25. package/dist/types/TwinGeometryInfo.js +2 -0
  26. package/dist/types/TwinGeometryInfo.js.map +1 -0
  27. package/dist/types/asset-api.d.ts +66 -0
  28. package/dist/types/asset-api.d.ts.map +1 -0
  29. package/dist/types/asset-api.js +2 -0
  30. package/dist/types/asset-api.js.map +1 -0
  31. package/dist/types/asset-attachment-api.d.ts +51 -0
  32. package/dist/types/asset-attachment-api.d.ts.map +1 -0
  33. package/dist/types/asset-attachment-api.js +8 -0
  34. package/dist/types/asset-attachment-api.js.map +1 -0
  35. package/dist/types/asset-batch-api.d.ts +73 -0
  36. package/dist/types/asset-batch-api.d.ts.map +1 -0
  37. package/dist/types/asset-batch-api.js +2 -0
  38. package/dist/types/asset-batch-api.js.map +1 -0
  39. package/dist/types/asset-types-api.d.ts +76 -0
  40. package/dist/types/asset-types-api.d.ts.map +1 -0
  41. package/dist/types/asset-types-api.js +5 -0
  42. package/dist/types/asset-types-api.js.map +1 -0
  43. package/dist/types/common.d.ts +10 -0
  44. package/dist/types/common.d.ts.map +1 -0
  45. package/dist/types/common.js +2 -0
  46. package/dist/types/common.js.map +1 -0
  47. package/dist/types/generated.d.ts +458 -0
  48. package/dist/types/generated.d.ts.map +1 -0
  49. package/dist/types/generated.js +2 -0
  50. package/dist/types/generated.js.map +1 -0
  51. package/dist/types/index.d.ts +10 -0
  52. package/dist/types/index.d.ts.map +1 -0
  53. package/dist/types/index.js +10 -0
  54. package/dist/types/index.js.map +1 -0
  55. package/dist/types/twin-api.d.ts +141 -0
  56. package/dist/types/twin-api.d.ts.map +1 -0
  57. package/dist/types/twin-api.js +2 -0
  58. package/dist/types/twin-api.js.map +1 -0
  59. package/dist/types/twin-file-api.d.ts +61 -0
  60. package/dist/types/twin-file-api.d.ts.map +1 -0
  61. package/dist/types/twin-file-api.js +2 -0
  62. package/dist/types/twin-file-api.js.map +1 -0
  63. package/dist/types/twin-permissions-api.d.ts +240 -0
  64. package/dist/types/twin-permissions-api.d.ts.map +1 -0
  65. package/dist/types/twin-permissions-api.js +2 -0
  66. package/dist/types/twin-permissions-api.js.map +1 -0
  67. package/docs/.nojekyll +1 -0
  68. package/docs/assets/hierarchy.js +1 -0
  69. package/docs/assets/highlight.css +99 -0
  70. package/docs/assets/icons.js +18 -0
  71. package/docs/assets/icons.svg +1 -0
  72. package/docs/assets/main.js +60 -0
  73. package/docs/assets/navigation.js +1 -0
  74. package/docs/assets/search.js +1 -0
  75. package/docs/assets/style.css +1633 -0
  76. package/docs/classes/AssetAttachmentConflictError.html +3 -0
  77. package/docs/classes/AssetAttachmentNotFoundError.html +2 -0
  78. package/docs/classes/AssetBatchError.html +3 -0
  79. package/docs/classes/AssetCatalogTooLargeError.html +16 -0
  80. package/docs/classes/AssetCategoryConflictError.html +3 -0
  81. package/docs/classes/AssetCategoryForbiddenError.html +2 -0
  82. package/docs/classes/AssetCategoryNotFoundError.html +2 -0
  83. package/docs/classes/AssetCategoryValidationError.html +3 -0
  84. package/docs/classes/AssetConflictError.html +3 -0
  85. package/docs/classes/AssetForbiddenError.html +2 -0
  86. package/docs/classes/AssetNotFoundError.html +2 -0
  87. package/docs/classes/AssetPermissionsConflictError.html +16 -0
  88. package/docs/classes/AssetTypeConflictError.html +3 -0
  89. package/docs/classes/AssetTypeForbiddenError.html +2 -0
  90. package/docs/classes/AssetTypeNotFoundError.html +2 -0
  91. package/docs/classes/AssetTypeValidationError.html +3 -0
  92. package/docs/classes/AssetValidationError.html +3 -0
  93. package/docs/classes/ProcessorNotFoundError.html +2 -0
  94. package/docs/classes/TwinClient.html +409 -0
  95. package/docs/classes/TwinConflictError.html +3 -0
  96. package/docs/classes/TwinFileConflictError.html +3 -0
  97. package/docs/classes/TwinFileNotFoundError.html +2 -0
  98. package/docs/classes/TwinFileValidationError.html +3 -0
  99. package/docs/classes/TwinNotFoundError.html +4 -0
  100. package/docs/classes/TwinPermissionsConflictError.html +19 -0
  101. package/docs/classes/TwinPermissionsForbiddenError.html +12 -0
  102. package/docs/classes/TwinPrincipalNotFoundError.html +5 -0
  103. package/docs/classes/TwinTemporarilyUnavailableError.html +10 -0
  104. package/docs/classes/TwinUnauthenticatedError.html +6 -0
  105. package/docs/classes/TwinValidationError.html +3 -0
  106. package/docs/functions/isAssetCatalogTooLargeErrorBody.html +6 -0
  107. package/docs/functions/readErrorBody.html +10 -0
  108. package/docs/hierarchy.html +1 -0
  109. package/docs/index.html +168 -0
  110. package/docs/interfaces/ApiCreateUploadSessionResponse.html +7 -0
  111. package/docs/interfaces/AssetAttachmentPut.html +6 -0
  112. package/docs/interfaces/AssetBatchDeleteOperation.html +4 -0
  113. package/docs/interfaces/AssetBatchGetRequest.html +2 -0
  114. package/docs/interfaces/AssetBatchGetResponse.html +3 -0
  115. package/docs/interfaces/AssetBatchRequest.html +2 -0
  116. package/docs/interfaces/AssetBatchResponse.html +3 -0
  117. package/docs/interfaces/AssetBatchRestoreOperation.html +4 -0
  118. package/docs/interfaces/AssetBatchUpsertItem.html +12 -0
  119. package/docs/interfaces/AssetBatchUpsertOperation.html +4 -0
  120. package/docs/interfaces/AssetCatalogCategory.html +7 -0
  121. package/docs/interfaces/AssetCatalogResponse.html +6 -0
  122. package/docs/interfaces/AssetCatalogTooLargeErrorBody.html +15 -0
  123. package/docs/interfaces/AssetCategoryDelete.html +2 -0
  124. package/docs/interfaces/AssetCategoryGet.html +14 -0
  125. package/docs/interfaces/AssetCategoryPut.html +7 -0
  126. package/docs/interfaces/AssetGet.html +18 -0
  127. package/docs/interfaces/AssetGroupGet.html +9 -0
  128. package/docs/interfaces/AssetGroupsGet.html +3 -0
  129. package/docs/interfaces/AssetListItem.html +14 -0
  130. package/docs/interfaces/AssetPermissionsConflictErrorBody.html +12 -0
  131. package/docs/interfaces/AssetPermissionsPut.html +9 -0
  132. package/docs/interfaces/AssetPosition.html +4 -0
  133. package/docs/interfaces/AssetPost.html +12 -0
  134. package/docs/interfaces/AssetPrincipalPermissionsGet.html +23 -0
  135. package/docs/interfaces/AssetPut.html +12 -0
  136. package/docs/interfaces/AssetTypeDelete.html +2 -0
  137. package/docs/interfaces/AssetTypeGet.html +16 -0
  138. package/docs/interfaces/AssetTypePut.html +9 -0
  139. package/docs/interfaces/AttachmentGet.html +20 -0
  140. package/docs/interfaces/AttachmentPost.html +4 -0
  141. package/docs/interfaces/BatchItemError.html +4 -0
  142. package/docs/interfaces/BatchItemResult.html +6 -0
  143. package/docs/interfaces/BatchSummary.html +4 -0
  144. package/docs/interfaces/Coordinates.html +6 -0
  145. package/docs/interfaces/Diagnostics.html +4 -0
  146. package/docs/interfaces/DiagnosticsSummary.html +4 -0
  147. package/docs/interfaces/ElementQuantities.html +3 -0
  148. package/docs/interfaces/EntityCollection.html +15 -0
  149. package/docs/interfaces/EntityRepresentation.html +12 -0
  150. package/docs/interfaces/Facility.html +22 -0
  151. package/docs/interfaces/FacilityPart.html +18 -0
  152. package/docs/interfaces/FilePatch.html +7 -0
  153. package/docs/interfaces/FilePatchSet.html +5 -0
  154. package/docs/interfaces/GenericDiagnosticEntry.html +4 -0
  155. package/docs/interfaces/ITwinClient.html +404 -0
  156. package/docs/interfaces/IfcDiagnosticEntry.html +5 -0
  157. package/docs/interfaces/IfcDiagnosticNode.html +6 -0
  158. package/docs/interfaces/IfcDiagnosticNodeData.html +6 -0
  159. package/docs/interfaces/InheritedPermissions.html +8 -0
  160. package/docs/interfaces/ListTwinItem.html +30 -0
  161. package/docs/interfaces/Location.html +13 -0
  162. package/docs/interfaces/MyPermissions.html +7 -0
  163. package/docs/interfaces/MyTwinPermissions.html +5 -0
  164. package/docs/interfaces/PaginatedResponse.html +3 -0
  165. package/docs/interfaces/Pagination.html +4 -0
  166. package/docs/interfaces/Property.html +4 -0
  167. package/docs/interfaces/PropertySet.html +6 -0
  168. package/docs/interfaces/SimpleQuantity.html +6 -0
  169. package/docs/interfaces/SinglePropertyValue.html +5 -0
  170. package/docs/interfaces/Site.html +27 -0
  171. package/docs/interfaces/Space.html +16 -0
  172. package/docs/interfaces/TwinComputedPropertySetsResponse.html +4 -0
  173. package/docs/interfaces/TwinContentResponse.html +6 -0
  174. package/docs/interfaces/TwinDelete.html +2 -0
  175. package/docs/interfaces/TwinDiagnosticEntry.html +5 -0
  176. package/docs/interfaces/TwinDiagnosticNode.html +4 -0
  177. package/docs/interfaces/TwinDiagnosticNodeData.html +9 -0
  178. package/docs/interfaces/TwinDiagnosticsResponse.html +5 -0
  179. package/docs/interfaces/TwinErrorBody.html +4 -0
  180. package/docs/interfaces/TwinFileDelete.html +2 -0
  181. package/docs/interfaces/TwinFileGet.html +15 -0
  182. package/docs/interfaces/TwinFileListOptions.html +6 -0
  183. package/docs/interfaces/TwinFilePut.html +8 -0
  184. package/docs/interfaces/TwinFileReference.html +4 -0
  185. package/docs/interfaces/TwinFileRestore.html +2 -0
  186. package/docs/interfaces/TwinFileSource.html +4 -0
  187. package/docs/interfaces/TwinFileTransform.html +11 -0
  188. package/docs/interfaces/TwinFileVersionListOptions.html +4 -0
  189. package/docs/interfaces/TwinGet.html +48 -0
  190. package/docs/interfaces/TwinGroupGet.html +10 -0
  191. package/docs/interfaces/TwinGroupsGet.html +3 -0
  192. package/docs/interfaces/TwinPermissionsConflictErrorBody.html +8 -0
  193. package/docs/interfaces/TwinPermissionsPut.html +11 -0
  194. package/docs/interfaces/TwinPrincipalPermissionsGet.html +34 -0
  195. package/docs/interfaces/TwinProcessorEvent.html +7 -0
  196. package/docs/interfaces/TwinProcessorResponse.html +5 -0
  197. package/docs/interfaces/TwinPut.html +21 -0
  198. package/docs/interfaces/TwinReference.html +4 -0
  199. package/docs/interfaces/TwinSpecification.html +18 -0
  200. package/docs/interfaces/TwinVersionContentResponse.html +7 -0
  201. package/docs/interfaces/UploadStatusGet.html +3 -0
  202. package/docs/media/twin-management.md +399 -0
  203. package/docs/modules.html +1 -0
  204. package/docs/types/AssetBatchGetStatus.html +1 -0
  205. package/docs/types/AssetBatchOperation.html +1 -0
  206. package/docs/types/AssetBatchOperationStatus.html +1 -0
  207. package/docs/types/DiagnosticCategory.html +2 -0
  208. package/docs/types/DiagnosticLevel.html +2 -0
  209. package/docs/types/EpsgType.html +1 -0
  210. package/docs/types/FacilityClass.html +3 -0
  211. package/docs/types/FacilityPartClass.html +3 -0
  212. package/docs/types/IfcDiagnosticCode.html +1 -0
  213. package/docs/types/Op.html +2 -0
  214. package/docs/types/SinglePropertyValueType.html +3 -0
  215. package/docs/types/SingleQuantityValueType.html +3 -0
  216. package/docs/types/SiteType.html +3 -0
  217. package/docs/types/SpaceType.html +2 -0
  218. package/docs/types/TwinBoundCapabilityName.html +8 -0
  219. package/docs/types/TwinCapability.html +12 -0
  220. package/docs/types/TwinCapabilityName.html +8 -0
  221. package/docs/types/TwinDiagnosticCode.html +1 -0
  222. package/docs/types/TwinErrorCause.html +8 -0
  223. package/docs/types/TwinErrorItemKind.html +4 -0
  224. package/docs/types/TwinPermissionsConflictCause.html +6 -0
  225. package/docs/types/TwinScopedCapability.html +9 -0
  226. package/docs/types/TwinScopedCapabilityName.html +8 -0
  227. package/docs/types/UploadStatus.html +1 -0
  228. package/package.json +52 -0
  229. package/src/BuildInfo.ts +41 -0
  230. package/src/TwinClient.ts +3113 -0
  231. package/src/errors.ts +567 -0
  232. package/src/index.ts +3 -0
  233. package/src/tsconfig.json +13 -0
  234. package/src/types/Twin.ts +7 -0
  235. package/src/types/TwinGeometryInfo.ts +7 -0
  236. package/src/types/asset-api.ts +69 -0
  237. package/src/types/asset-attachment-api.ts +56 -0
  238. package/src/types/asset-batch-api.ts +90 -0
  239. package/src/types/asset-types-api.ts +86 -0
  240. package/src/types/common.ts +10 -0
  241. package/src/types/generated.ts +655 -0
  242. package/src/types/index.ts +9 -0
  243. package/src/types/twin-api.ts +153 -0
  244. package/src/types/twin-file-api.ts +67 -0
  245. package/src/types/twin-permissions-api.ts +257 -0
package/src/errors.ts ADDED
@@ -0,0 +1,567 @@
1
+ import type {
2
+ TwinGet,
3
+ AssetGet,
4
+ TwinFileGet,
5
+ AssetBatchResponse,
6
+ AssetCategoryGet,
7
+ AssetTypeGet,
8
+ TwinPrincipalPermissionsGet,
9
+ AssetPrincipalPermissionsGet
10
+ } from './types';
11
+ import type { AttachmentGet } from './types/asset-attachment-api';
12
+
13
+ /**
14
+ * Closed union of `cause` values the Twin service emits on a 400 response. Mirrors
15
+ * `TwinBadRequestCause` in `backend/services/Twinfinity.Twin/Controllers/Model/TwinStatus.cs`.
16
+ * The trailing `(string & {})` keeps autocomplete on the known values while still
17
+ * accepting unknown causes at the type level — a forward-compat pattern: a new
18
+ * cause emitted by a newer server doesn't break older clients that pattern-match
19
+ * on the union. Matches the shape of `TwinCapability` in
20
+ * `types/twin-permissions-api.ts`.
21
+ */
22
+ export type TwinErrorCause =
23
+ | 'NotAllowedToEditDeletedItem'
24
+ | 'NotAllowedToDeleteItem'
25
+ | 'ImmutableField'
26
+ | 'ReferencedItemNotFound'
27
+ | 'ReferencedItemDeleted'
28
+ | 'FileNotOwnedByTwin'
29
+ | 'DuplicateFileReference'
30
+ | 'MultipleVersionsOfSameFileOnSameNode'
31
+ | 'LimitOutOfRange'
32
+ | 'OffsetOutOfRange'
33
+ | 'IdMismatchBetweenUrlAndBody'
34
+ | 'BatchSizeEmpty'
35
+ | 'BatchSizeExceedsMaximum'
36
+ | 'InvalidParameterCombination'
37
+ | 'DocumentChangeNotFound'
38
+ | 'DeletePermissionRequired'
39
+ | 'CannotEditDeletedFile'
40
+ | 'CannotMoveFileBetweenTwins'
41
+ | 'ContainerDoesNotExist'
42
+ | 'DuplicateContainerIds'
43
+ | 'InsufficientPermissionsOnContainer'
44
+ | 'DuplicateId'
45
+ | 'MetadataTooLarge'
46
+ | 'InvalidTransform'
47
+ | 'InvalidFilePatches'
48
+ | 'MetadataValidationFailed'
49
+ | 'MetadataSchemaInvalid'
50
+ | 'AssetTypeNotFound'
51
+ | 'AssetCategoryAndTypeRequired'
52
+ | 'AssetDescriptionTooLong'
53
+ | 'StringIdInUse'
54
+ | 'CatalogTooLarge'
55
+ | 'EtagRequired'
56
+ | 'InvalidPaginationToken'
57
+ | 'SystemPrincipalImmutable'
58
+ | 'ManagePermissionsNotGrantableToAllAuthenticated'
59
+ | 'ValidationFailed'
60
+ // Anything the server emits that this client doesn't know about yet.
61
+ // (string & {}) keeps the autocomplete on the listed values above.
62
+ // eslint-disable-next-line @typescript-eslint/ban-types
63
+ | (string & {});
64
+
65
+ /**
66
+ * Closed union of `item.kind` values the server emits. Mirrors `ItemKind` in
67
+ * `backend/services/Twinfinity.Twin/Model/ItemReference.cs`. Same forward-compat
68
+ * shape as {@link TwinErrorCause}.
69
+ */
70
+ export type TwinErrorItemKind =
71
+ | 'Twin'
72
+ | 'Asset'
73
+ | 'File'
74
+ | 'DocumentChange'
75
+ | 'AssetCategory'
76
+ | 'AssetType'
77
+ | 'Attachment'
78
+ | 'Document'
79
+ | 'Principal'
80
+ | 'Request'
81
+ // eslint-disable-next-line @typescript-eslint/ban-types
82
+ | (string & {});
83
+
84
+ export interface TwinErrorBody {
85
+ detail: string;
86
+ cause: TwinErrorCause;
87
+ item?: {
88
+ kind: TwinErrorItemKind;
89
+ // Absent when the reference names something that has no id — a `Request` kind
90
+ // carries only `field`, the request member that failed validation.
91
+ id?: string;
92
+ version?: number;
93
+ field?: string;
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Parse a non-OK response body as a {@link TwinErrorBody}. Falls back to a
99
+ * synthetic body carrying the response text (or statusText) when the body
100
+ * isn't valid JSON — for example when a WAF or intermediate proxy injects
101
+ * an HTML 4xx page. Callers can therefore always pattern-match on
102
+ * `errorBody.cause` without an outer try/catch around a bare
103
+ * `response.json()`.
104
+ *
105
+ * The synthetic body uses `cause: 'Unknown'`, which is permitted by the
106
+ * `(string & {})` forward-compat tail on {@link TwinErrorCause}. Consumers
107
+ * that switch on known causes will fall through to a default arm.
108
+ */
109
+ export async function readErrorBody(response: Response): Promise<TwinErrorBody> {
110
+ // Parse a clone, so that a body which turns out not to be JSON can still be read as text below.
111
+ // `json()` consumes the stream: on a real Response, reading the original here would leave the
112
+ // fallback unreachable and every non-JSON diagnostic would degrade to `statusText`.
113
+ const parseFrom = typeof response.clone === 'function' ? response.clone() : response;
114
+ try {
115
+ const parsed = (await parseFrom.json()) as TwinErrorBody;
116
+ // Defensive shape check — JSON-but-not-our-shape (e.g. an upstream
117
+ // ProblemDetails payload from a non-Twinfinity component) should still
118
+ // surface usefully. `detail` is the only field every consumer reads.
119
+ if (parsed && typeof parsed.detail === 'string') return parsed;
120
+ return { detail: JSON.stringify(parsed), cause: 'Unknown' };
121
+ } catch {
122
+ const text = await response.text().catch(() => '');
123
+ return { detail: text || response.statusText || `HTTP ${response.status}`, cause: 'Unknown' };
124
+ }
125
+ }
126
+
127
+ export class TwinValidationError extends Error {
128
+ public readonly errorBody?: TwinErrorBody;
129
+
130
+ constructor(message: string, errorBody?: TwinErrorBody) {
131
+ super(message);
132
+ this.name = 'TwinValidationError';
133
+ this.errorBody = errorBody;
134
+ }
135
+ }
136
+
137
+ export class TwinConflictError extends Error {
138
+ public readonly currentTwin: TwinGet;
139
+
140
+ constructor(message: string, currentTwin: TwinGet) {
141
+ super(message);
142
+ this.name = 'TwinConflictError';
143
+ this.currentTwin = currentTwin;
144
+ }
145
+ }
146
+
147
+ export class TwinNotFoundError extends Error {
148
+ /** The server's envelope, when there was one — `item.kind` names what was not found. */
149
+ public readonly errorBody?: TwinErrorBody;
150
+
151
+ constructor(message: string, errorBody?: TwinErrorBody) {
152
+ super(message);
153
+ this.name = 'TwinNotFoundError';
154
+ this.errorBody = errorBody;
155
+ }
156
+ }
157
+
158
+ export class ProcessorNotFoundError extends Error {
159
+ constructor(message: string) {
160
+ super(message);
161
+ this.name = 'ProcessorNotFoundError';
162
+ }
163
+ }
164
+
165
+ export class AssetValidationError extends Error {
166
+ public readonly errorBody?: TwinErrorBody;
167
+
168
+ constructor(message: string, errorBody?: TwinErrorBody) {
169
+ super(message);
170
+ this.name = 'AssetValidationError';
171
+ this.errorBody = errorBody;
172
+ }
173
+ }
174
+
175
+ export class AssetForbiddenError extends Error {
176
+ constructor(message: string) {
177
+ super(message);
178
+ this.name = 'AssetForbiddenError';
179
+ }
180
+ }
181
+
182
+ export class AssetConflictError extends Error {
183
+ public readonly currentAsset: AssetGet;
184
+
185
+ constructor(message: string, currentAsset: AssetGet) {
186
+ super(message);
187
+ this.name = 'AssetConflictError';
188
+ this.currentAsset = currentAsset;
189
+ }
190
+ }
191
+
192
+ export class AssetNotFoundError extends Error {
193
+ constructor(message: string) {
194
+ super(message);
195
+ this.name = 'AssetNotFoundError';
196
+ }
197
+ }
198
+
199
+ export class AssetAttachmentConflictError extends Error {
200
+ public readonly currentAttachment: AttachmentGet;
201
+
202
+ constructor(message: string, currentAttachment: AttachmentGet) {
203
+ super(message);
204
+ this.name = 'AssetAttachmentConflictError';
205
+ this.currentAttachment = currentAttachment;
206
+ }
207
+ }
208
+
209
+ export class AssetAttachmentNotFoundError extends Error {
210
+ constructor(message: string) {
211
+ super(message);
212
+ this.name = 'AssetAttachmentNotFoundError';
213
+ }
214
+ }
215
+
216
+ export class AssetBatchError extends Error {
217
+ public readonly response: AssetBatchResponse;
218
+
219
+ constructor(message: string, response: AssetBatchResponse) {
220
+ super(message);
221
+ this.name = 'AssetBatchError';
222
+ this.response = response;
223
+ }
224
+ }
225
+
226
+ export class TwinFileValidationError extends Error {
227
+ public readonly errorBody?: TwinErrorBody;
228
+
229
+ constructor(message: string, errorBody?: TwinErrorBody) {
230
+ super(message);
231
+ this.name = 'TwinFileValidationError';
232
+ this.errorBody = errorBody;
233
+ }
234
+ }
235
+
236
+ export class TwinFileConflictError extends Error {
237
+ public readonly currentTwinFile: TwinFileGet;
238
+
239
+ constructor(message: string, currentTwinFile: TwinFileGet) {
240
+ super(message);
241
+ this.name = 'TwinFileConflictError';
242
+ this.currentTwinFile = currentTwinFile;
243
+ }
244
+ }
245
+
246
+ export class TwinFileNotFoundError extends Error {
247
+ constructor(message: string) {
248
+ super(message);
249
+ this.name = 'TwinFileNotFoundError';
250
+ }
251
+ }
252
+
253
+ // ---------- Asset categories ----------
254
+
255
+ export class AssetCategoryValidationError extends Error {
256
+ public readonly errorBody?: TwinErrorBody;
257
+
258
+ constructor(message: string, errorBody?: TwinErrorBody) {
259
+ super(message);
260
+ this.name = 'AssetCategoryValidationError';
261
+ this.errorBody = errorBody;
262
+ }
263
+ }
264
+
265
+ export class AssetCategoryConflictError extends Error {
266
+ public readonly currentCategory: AssetCategoryGet;
267
+
268
+ constructor(message: string, currentCategory: AssetCategoryGet) {
269
+ super(message);
270
+ this.name = 'AssetCategoryConflictError';
271
+ this.currentCategory = currentCategory;
272
+ }
273
+ }
274
+
275
+ export class AssetCategoryNotFoundError extends Error {
276
+ constructor(message: string) {
277
+ super(message);
278
+ this.name = 'AssetCategoryNotFoundError';
279
+ }
280
+ }
281
+
282
+ export class AssetCategoryForbiddenError extends Error {
283
+ constructor(message: string) {
284
+ super(message);
285
+ this.name = 'AssetCategoryForbiddenError';
286
+ }
287
+ }
288
+
289
+ // ---------- Asset types ----------
290
+
291
+ export class AssetTypeValidationError extends Error {
292
+ public readonly errorBody?: TwinErrorBody;
293
+
294
+ constructor(message: string, errorBody?: TwinErrorBody) {
295
+ super(message);
296
+ this.name = 'AssetTypeValidationError';
297
+ this.errorBody = errorBody;
298
+ }
299
+ }
300
+
301
+ export class AssetTypeConflictError extends Error {
302
+ public readonly currentType: AssetTypeGet;
303
+
304
+ constructor(message: string, currentType: AssetTypeGet) {
305
+ super(message);
306
+ this.name = 'AssetTypeConflictError';
307
+ this.currentType = currentType;
308
+ }
309
+ }
310
+
311
+ export class AssetTypeNotFoundError extends Error {
312
+ constructor(message: string) {
313
+ super(message);
314
+ this.name = 'AssetTypeNotFoundError';
315
+ }
316
+ }
317
+
318
+ export class AssetTypeForbiddenError extends Error {
319
+ constructor(message: string) {
320
+ super(message);
321
+ this.name = 'AssetTypeForbiddenError';
322
+ }
323
+ }
324
+
325
+ // ---------- Asset catalog ----------
326
+
327
+ /**
328
+ * Wire shape returned by `GET /asset-type-catalog` with a 400 when the
329
+ * tenant's catalog can't be served as a single round-trip — same shape as
330
+ * {@link TwinErrorBody} plus structured counts so picker UIs can render a
331
+ * precise message without parsing `detail`.
332
+ *
333
+ * Mirrors `AssetCatalogTooLargeResponse` in
334
+ * `backend/services/Twinfinity.Twin/Controllers/Model/AssetCatalogDtos.cs`.
335
+ */
336
+ export interface AssetCatalogTooLargeErrorBody extends TwinErrorBody {
337
+ cause: 'CatalogTooLarge';
338
+ categoriesCount: number;
339
+ typesCount: number;
340
+ maxCatalogSize: number;
341
+ categoriesOverflowed: boolean;
342
+ typesOverflowed: boolean;
343
+ }
344
+
345
+ /**
346
+ * Thrown by `getAssetTypeCatalog` when the customer's catalog exceeds the
347
+ * per-customer cap. The catalog endpoint is a single-round-trip view; tenants
348
+ * over the cap must switch to a paginated endpoint (not yet shipped). Surfaced
349
+ * as a hard failure rather than silent truncation so picker UIs can render
350
+ * "too many rows, use search" instead of "out of types."
351
+ *
352
+ * `categoriesOverflowed` / `typesOverflowed` identify which side(s) breached
353
+ * the cap; both can be true. `categoriesCount` / `typesCount` are the observed
354
+ * counts (≥ `maxCatalogSize` for the overflowing side(s)).
355
+ */
356
+ export class AssetCatalogTooLargeError extends Error {
357
+ public readonly errorBody: AssetCatalogTooLargeErrorBody;
358
+ public readonly categoriesCount: number;
359
+ public readonly typesCount: number;
360
+ public readonly maxCatalogSize: number;
361
+ public readonly categoriesOverflowed: boolean;
362
+ public readonly typesOverflowed: boolean;
363
+
364
+ constructor(message: string, errorBody: AssetCatalogTooLargeErrorBody) {
365
+ super(message);
366
+ this.name = 'AssetCatalogTooLargeError';
367
+ this.errorBody = errorBody;
368
+ this.categoriesCount = errorBody.categoriesCount;
369
+ this.typesCount = errorBody.typesCount;
370
+ this.maxCatalogSize = errorBody.maxCatalogSize;
371
+ this.categoriesOverflowed = errorBody.categoriesOverflowed;
372
+ this.typesOverflowed = errorBody.typesOverflowed;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Type guard for a {@link TwinErrorBody} that carries `CatalogTooLarge`
378
+ * with the structured count fields the dedicated wire shape adds on top of
379
+ * the standard envelope. Used by the client to decide whether to throw a
380
+ * typed {@link AssetCatalogTooLargeError} or fall through to a generic
381
+ * validation error when the server sends a malformed payload.
382
+ */
383
+ export function isAssetCatalogTooLargeErrorBody(body: TwinErrorBody): body is AssetCatalogTooLargeErrorBody {
384
+ return (
385
+ body.cause === 'CatalogTooLarge' &&
386
+ typeof (body as Partial<AssetCatalogTooLargeErrorBody>).categoriesCount === 'number' &&
387
+ typeof (body as Partial<AssetCatalogTooLargeErrorBody>).typesCount === 'number' &&
388
+ typeof (body as Partial<AssetCatalogTooLargeErrorBody>).maxCatalogSize === 'number' &&
389
+ typeof (body as Partial<AssetCatalogTooLargeErrorBody>).categoriesOverflowed === 'boolean' &&
390
+ typeof (body as Partial<AssetCatalogTooLargeErrorBody>).typesOverflowed === 'boolean'
391
+ );
392
+ }
393
+
394
+ // ---------- Authentication ----------
395
+
396
+ /**
397
+ * Thrown when the server returns 401 (Unauthorized). Distinct from forbidden
398
+ * errors: 401 means "no valid auth context" (the caller should redirect to
399
+ * login / refresh tokens), while 403 means "authenticated but not granted"
400
+ * (the UI should surface a permissions hint or render disabled affordances).
401
+ */
402
+ export class TwinUnauthenticatedError extends Error {
403
+ constructor(message: string) {
404
+ super(message);
405
+ this.name = 'TwinUnauthenticatedError';
406
+ }
407
+ }
408
+
409
+ // ---------- Twin-scoped permissions ----------
410
+
411
+ /**
412
+ * The `cause` values the twin-scoped permission routes emit on a 409. A subset of
413
+ * `TwinConflictCause` in `backend/services/Twinfinity.Twin/Controllers/Model/TwinStatus.cs`:
414
+ * that enum's `GroupIdAlreadyInUse` and `ScopeIdAlreadyInUse` are raised by twin *creation*,
415
+ * which answers through {@link TwinConflictError}, never through this surface. Carries the same
416
+ * forward-compat tail as {@link TwinErrorCause}.
417
+ */
418
+ export type TwinPermissionsConflictCause =
419
+ | 'EtagMismatch'
420
+ | 'EtagRequired'
421
+ | 'ScopesNotProvisioned'
422
+ // eslint-disable-next-line @typescript-eslint/ban-types
423
+ | (string & {});
424
+
425
+ /**
426
+ * The 409 envelope of the twin-scoped permission surface: the standard
427
+ * {@link TwinErrorBody} plus the server's view of the entry the caller lost a race on,
428
+ * whenever the service could report it.
429
+ */
430
+ export interface TwinPermissionsConflictErrorBody extends TwinErrorBody {
431
+ cause: TwinPermissionsConflictCause;
432
+ currentState?: TwinPrincipalPermissionsGet;
433
+ }
434
+
435
+ /**
436
+ * Thrown by the twin permission mutation and listing methods on a 409.
437
+ *
438
+ * The three causes need different handling, which is why they are distinguishable
439
+ * rather than collapsed into one "modified by another user":
440
+ * - `EtagMismatch` — the etag is stale; re-read (or use {@link currentState}) and retry.
441
+ * - `EtagRequired` — the write omitted an etag but the principal already held an entry,
442
+ * so it would have replaced state the caller never read. Retried by reading at all.
443
+ * - `ScopesNotProvisioned` — the twin owns no Twinfinity scope, so there is no permission
444
+ * state to read or write. Nothing provisions on read, so retrying will not help.
445
+ */
446
+ export class TwinPermissionsConflictError extends Error {
447
+ /**
448
+ * Which of the three conflicts this is. Named `conflictCause` rather than `cause` so the
449
+ * standard `Error.cause` — whose consumers expect a wrapped error, not a string — is left
450
+ * alone; the same value is on {@link errorBody}`.cause`.
451
+ */
452
+ public readonly conflictCause: TwinPermissionsConflictCause;
453
+ /** The entry as the server holds it, including the etag to retry with. */
454
+ public readonly currentState?: TwinPrincipalPermissionsGet;
455
+ public readonly errorBody: TwinPermissionsConflictErrorBody;
456
+
457
+ constructor(message: string, errorBody: TwinPermissionsConflictErrorBody) {
458
+ super(message);
459
+ this.name = 'TwinPermissionsConflictError';
460
+ this.conflictCause = errorBody.cause;
461
+ this.currentState = errorBody.currentState;
462
+ this.errorBody = errorBody;
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Thrown on a 403 from the twin-scoped permission surface: the caller can see the twin
468
+ * but does not hold `ManageTwinPermissions` on it. Distinct from
469
+ * {@link TwinNotFoundError}, which is what a twin the caller cannot see at all answers —
470
+ * the surface refuses to confirm such a twin exists.
471
+ */
472
+ export class TwinPermissionsForbiddenError extends Error {
473
+ /**
474
+ * The server's envelope, when there was one. Its presence is load-bearing: a genuine refusal
475
+ * carries `cause: 'InsufficientPermissions'`, while a Twin service that could not evaluate the
476
+ * request at all (a customer-database lookup that threw, a missing twin feature) falls through
477
+ * to the framework's default forbid handler and answers a 403 with no body. Without this a
478
+ * caller cannot tell "you may not" from "we could not tell", and would report the first.
479
+ */
480
+ public readonly errorBody?: TwinErrorBody;
481
+
482
+ constructor(message: string, errorBody?: TwinErrorBody) {
483
+ super(message);
484
+ this.name = 'TwinPermissionsForbiddenError';
485
+ this.errorBody = errorBody;
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Thrown when a 404 names the principal rather than the twin — an id that no principal
491
+ * in the tenant carries, or one holding nothing on the twin for a `DELETE` to revoke.
492
+ */
493
+ export class TwinPrincipalNotFoundError extends Error {
494
+ public readonly errorBody?: TwinErrorBody;
495
+
496
+ constructor(message: string, errorBody?: TwinErrorBody) {
497
+ super(message);
498
+ this.name = 'TwinPrincipalNotFoundError';
499
+ this.errorBody = errorBody;
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Thrown on a 503: the Twin service could neither carry out the request nor answer it
505
+ * truthfully. The request is unchanged and safe to retry as-is — unlike
506
+ * {@link TwinPermissionsConflictError}, which never clears on its own.
507
+ */
508
+ export class TwinTemporarilyUnavailableError extends Error {
509
+ public readonly errorBody?: TwinErrorBody;
510
+ /**
511
+ * The `Retry-After` the service sent, in seconds, when it sent one. The Twin service pairs the
512
+ * status with this header deliberately, so that "retrying is worthwhile" does not rest on the
513
+ * status alone — pass it to whatever backoff the caller uses instead of inventing a delay.
514
+ */
515
+ public readonly retryAfterSeconds?: number;
516
+
517
+ constructor(message: string, errorBody?: TwinErrorBody, retryAfterSeconds?: number) {
518
+ super(message);
519
+ this.name = 'TwinTemporarilyUnavailableError';
520
+ this.errorBody = errorBody;
521
+ this.retryAfterSeconds = retryAfterSeconds;
522
+ }
523
+ }
524
+
525
+ // ---------- Asset-domain permissions ----------
526
+
527
+ /**
528
+ * The 409 envelope of the customer-wide asset permission surface. The same shape as
529
+ * {@link TwinPermissionsConflictErrorBody}, but `currentState` is a domain entry rather than a
530
+ * twin's: the two carry different identity fields (`id` versus `principalId`) and different
531
+ * capability vocabularies, so a caller that acts on the state it was handed has to know which it
532
+ * has.
533
+ *
534
+ * `ScopesNotProvisioned` never appears here — the asset management scope is seeded once per
535
+ * customer rather than provisioned per resource.
536
+ */
537
+ export interface AssetPermissionsConflictErrorBody extends TwinErrorBody {
538
+ cause: TwinPermissionsConflictCause;
539
+ currentState?: AssetPrincipalPermissionsGet;
540
+ }
541
+
542
+ /**
543
+ * Thrown by the asset permission mutation methods on a 409, which on this surface is always an
544
+ * etag failure:
545
+ * - `EtagMismatch` — the etag is stale; re-read (or use {@link currentState}) and retry. Expect
546
+ * this routinely: the scope's etag is customer-wide, so any administrator's write invalidates it.
547
+ * - `EtagRequired` — the write omitted an etag but the principal already held capabilities, so it
548
+ * would have replaced state the caller never read.
549
+ */
550
+ export class AssetPermissionsConflictError extends Error {
551
+ /**
552
+ * Which of the two conflicts this is. Named `conflictCause` rather than `cause` so the standard
553
+ * `Error.cause` — whose consumers expect a wrapped error, not a string — is left alone.
554
+ */
555
+ public readonly conflictCause: TwinPermissionsConflictCause;
556
+ /** The entry as the server holds it, including the etag to retry with. */
557
+ public readonly currentState?: AssetPrincipalPermissionsGet;
558
+ public readonly errorBody: AssetPermissionsConflictErrorBody;
559
+
560
+ constructor(message: string, errorBody: AssetPermissionsConflictErrorBody) {
561
+ super(message);
562
+ this.name = 'AssetPermissionsConflictError';
563
+ this.conflictCause = errorBody.cause;
564
+ this.currentState = errorBody.currentState;
565
+ this.errorBody = errorBody;
566
+ }
567
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { type ITwinClient, TwinClient } from './TwinClient';
2
+ export * from './types';
3
+ export * from './errors';
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.common.json",
3
+ "compilerOptions": {
4
+ "experimentalDecorators": true,
5
+ "noEmit": false,
6
+ "outDir": "../dist"
7
+ },
8
+ "include": ["**/*.ts"],
9
+ "references": [
10
+ { "path": "../../twinfinity-authentication/src" },
11
+ { "path": "../../twinfinity-t8-client-model/src" }
12
+ ]
13
+ }
@@ -0,0 +1,7 @@
1
+ import { T8ClientModel } from '@twinfinity/t8-client-model';
2
+
3
+ export class Twin<TProject, TProduct, TEntityType, TUnit> {
4
+ id: string;
5
+ version: number;
6
+ model?: T8ClientModel<TProject, TProduct, TEntityType, TUnit>;
7
+ }
@@ -0,0 +1,7 @@
1
+ import type { T8GIModelGeneric } from '@twinfinity/t8-client-model';
2
+
3
+ export interface TwinGeometryInfo<TMeshDescriptor, TStyle, TRegion> {
4
+ id: string;
5
+ version: number;
6
+ model?: T8GIModelGeneric<TMeshDescriptor, TStyle, TRegion>;
7
+ }
@@ -0,0 +1,69 @@
1
+ export interface AssetListItem {
2
+ id: string;
3
+ name: string;
4
+ /** Free-text description, common to all asset categories and types. Not part of `metadata`. */
5
+ description?: string;
6
+ category?: string;
7
+ type?: string;
8
+ twinReference?: string;
9
+ parentIfcGid?: string;
10
+ ifcGid?: string;
11
+ position?: AssetPosition;
12
+ etag: string;
13
+ createdAt: string;
14
+ isDeleted?: boolean;
15
+ }
16
+
17
+ export interface AssetPosition {
18
+ x?: number;
19
+ y?: number;
20
+ z?: number;
21
+ }
22
+
23
+ export interface AssetGet {
24
+ id: string;
25
+ name: string;
26
+ /** Free-text description, common to all asset categories and types. Not part of `metadata`. */
27
+ description?: string;
28
+ category?: string;
29
+ type?: string;
30
+ ifcGid?: string;
31
+ twinReference?: string;
32
+ position?: AssetPosition;
33
+ parentIfcGid?: string;
34
+ metadata?: Record<string, unknown>;
35
+ etag: string;
36
+ createdUtc: string;
37
+ createdBy: string;
38
+ modifiedUtc: string;
39
+ modifiedBy: string;
40
+ isDeleted?: boolean;
41
+ }
42
+
43
+ export interface AssetPost {
44
+ id?: string;
45
+ name: string;
46
+ /** Free-text description, common to all asset categories and types. Not part of `metadata`. */
47
+ description?: string;
48
+ category?: string;
49
+ type?: string;
50
+ ifcGid?: string;
51
+ position?: AssetPosition;
52
+ parentIfcGid?: string;
53
+ metadata?: Record<string, unknown>;
54
+ twinReference?: string;
55
+ }
56
+
57
+ export interface AssetPut {
58
+ etag: string;
59
+ name: string;
60
+ /** Free-text description, common to all asset categories and types. Not part of `metadata`. */
61
+ description?: string;
62
+ category?: string;
63
+ type?: string;
64
+ ifcGid?: string;
65
+ position?: AssetPosition;
66
+ parentIfcGid?: string;
67
+ metadata?: Record<string, unknown>;
68
+ twinReference?: string;
69
+ }