@digicatapult/dtdl-parser 0.0.3

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.
@@ -0,0 +1,26 @@
1
+ export type ModelingException = ParsingException | ResolutionException
2
+
3
+ export interface ParsingException {
4
+ ExceptionKind: 'Parsing'
5
+ Errors: ParsingError[]
6
+ }
7
+
8
+ export interface ParsingError {
9
+ PrimaryID?: string
10
+ SecondaryID?: string
11
+ Property?: string
12
+ AuxProperty?: string
13
+ Type?: string
14
+ Value?: string
15
+ Restriction?: string
16
+ Transformation?: string
17
+ Violations?: string[]
18
+ Cause: string
19
+ Action: string
20
+ ValidationID: string
21
+ }
22
+
23
+ export interface ResolutionException {
24
+ ExceptionKind: 'Resolution'
25
+ UndefinedIdentifiers: string[]
26
+ }
@@ -0,0 +1,372 @@
1
+ export type DtdlObjectModel = { [entityId: string]: EntityType }
2
+
3
+ export interface ArrayInfo extends ComplexSchemaInfo {
4
+ EntityKind: 'Array'
5
+ elementSchema: string
6
+ }
7
+
8
+ export type ArrayType = ArrayInfo
9
+
10
+ export interface BooleanInfo extends PrimitiveSchemaInfo {
11
+ EntityKind: 'Boolean'
12
+ }
13
+
14
+ export type BooleanType = BooleanInfo
15
+
16
+ export interface CommandInfo extends ContentInfo {
17
+ EntityKind: 'Command'
18
+ commandType?: string
19
+ request?: string
20
+ response?: string
21
+ }
22
+
23
+ export type CommandType = CommandInfo
24
+
25
+ export interface CommandPayloadInfo extends SchemaFieldInfo {
26
+ EntityKind: 'CommandPayload' | 'CommandRequest' | 'CommandResponse'
27
+ }
28
+
29
+ export type CommandPayloadType = CommandPayloadInfo | CommandRequestType | CommandResponseType
30
+
31
+ export interface CommandRequestInfo extends CommandPayloadInfo {
32
+ EntityKind: 'CommandRequest'
33
+ }
34
+
35
+ export type CommandRequestType = CommandRequestInfo
36
+
37
+ export interface CommandResponseInfo extends CommandPayloadInfo {
38
+ EntityKind: 'CommandResponse'
39
+ }
40
+
41
+ export type CommandResponseType = CommandResponseInfo
42
+
43
+ export interface CommandTypeInfo extends EntityInfo {
44
+ EntityKind: 'CommandType'
45
+ }
46
+
47
+ export type CommandTypeType = CommandTypeInfo
48
+
49
+ export interface ComplexSchemaInfo extends SchemaInfo {
50
+ EntityKind: 'Array' | 'Enum' | 'Map' | 'Object'
51
+ }
52
+
53
+ export type ComplexSchemaType = ComplexSchemaInfo | ArrayType | EnumType | MapType | ObjectType
54
+
55
+ export interface ComponentInfo extends ContentInfo {
56
+ EntityKind: 'Component'
57
+ schema: string
58
+ }
59
+
60
+ export type ComponentType = ComponentInfo
61
+
62
+ export interface ContentInfo extends NamedEntityInfo {
63
+ EntityKind: 'Command' | 'Component' | 'Property' | 'Relationship' | 'Telemetry'
64
+ }
65
+
66
+ export type ContentType = ContentInfo | CommandType | ComponentType | PropertyType | RelationshipType | TelemetryType
67
+
68
+ export interface DateInfo extends TemporalSchemaInfo {
69
+ EntityKind: 'Date'
70
+ }
71
+
72
+ export type DateType = DateInfo
73
+
74
+ export interface DateTimeInfo extends TemporalSchemaInfo {
75
+ EntityKind: 'DateTime'
76
+ }
77
+
78
+ export type DateTimeType = DateTimeInfo
79
+
80
+ export interface DoubleInfo extends NumericSchemaInfo {
81
+ EntityKind: 'Double'
82
+ }
83
+
84
+ export type DoubleType = DoubleInfo
85
+
86
+ export interface DurationInfo extends TemporalSchemaInfo {
87
+ EntityKind: 'Duration'
88
+ }
89
+
90
+ export type DurationType = DurationInfo
91
+
92
+ export interface EntityInfo {
93
+ EntityKind:
94
+ | 'Array'
95
+ | 'Boolean'
96
+ | 'Command'
97
+ | 'CommandPayload'
98
+ | 'CommandType'
99
+ | 'Component'
100
+ | 'Date'
101
+ | 'DateTime'
102
+ | 'Double'
103
+ | 'Duration'
104
+ | 'Enum'
105
+ | 'EnumValue'
106
+ | 'Field'
107
+ | 'Float'
108
+ | 'Integer'
109
+ | 'Interface'
110
+ | 'Long'
111
+ | 'Map'
112
+ | 'MapKey'
113
+ | 'MapValue'
114
+ | 'Object'
115
+ | 'Property'
116
+ | 'Relationship'
117
+ | 'String'
118
+ | 'Telemetry'
119
+ | 'Time'
120
+ | 'CommandRequest'
121
+ | 'CommandResponse'
122
+ | 'Unit'
123
+ | 'UnitAttribute'
124
+ | 'LatentType'
125
+ | 'NamedLatentType'
126
+ SupplementalTypes: string[]
127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
+ SupplementalProperties: { [property: string]: any }
129
+ UndefinedTypes: string[]
130
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
+ UndefinedProperties: { [property: string]: any }
132
+ ClassId: string
133
+ comment?: string
134
+ description: { [languageCode: string]: string }
135
+ displayName: { [languageCode: string]: string }
136
+ languageMajorVersion: number
137
+ Id: string
138
+ ChildOf?: string
139
+ DefinedIn?: string
140
+ }
141
+
142
+ export type EntityType =
143
+ | EntityInfo
144
+ | CommandTypeType
145
+ | InterfaceType
146
+ | LatentTypeType
147
+ | NamedEntityType
148
+ | SchemaType
149
+ | UnitType
150
+
151
+ export interface EnumInfo extends ComplexSchemaInfo {
152
+ EntityKind: 'Enum'
153
+ enumValues: string[]
154
+ valueSchema: string
155
+ }
156
+
157
+ export type EnumType = EnumInfo
158
+
159
+ export interface EnumValueInfo extends NamedEntityInfo {
160
+ EntityKind: 'EnumValue'
161
+ enumValue: string | number | boolean
162
+ }
163
+
164
+ export type EnumValueType = EnumValueInfo
165
+
166
+ export interface FieldInfo extends SchemaFieldInfo {
167
+ EntityKind: 'Field'
168
+ }
169
+
170
+ export type FieldType = FieldInfo
171
+
172
+ export interface FloatInfo extends NumericSchemaInfo {
173
+ EntityKind: 'Float'
174
+ }
175
+
176
+ export type FloatType = FloatInfo
177
+
178
+ export interface IntegerInfo extends NumericSchemaInfo {
179
+ EntityKind: 'Integer'
180
+ }
181
+
182
+ export type IntegerType = IntegerInfo
183
+
184
+ export interface InterfaceInfo extends EntityInfo {
185
+ EntityKind: 'Interface'
186
+ contents: { [name: string]: string }
187
+ commands: { [name: string]: string }
188
+ components: { [name: string]: string }
189
+ properties: { [name: string]: string }
190
+ relationships: { [name: string]: string }
191
+ telemetries: { [name: string]: string }
192
+ extends: string[]
193
+ extendedBy: string[]
194
+ schemas: string[]
195
+ }
196
+
197
+ export type InterfaceType = InterfaceInfo
198
+
199
+ export interface LatentTypeInfo extends EntityInfo {
200
+ EntityKind: 'LatentType'
201
+ }
202
+
203
+ export type LatentTypeType = LatentTypeInfo
204
+
205
+ export interface LongInfo extends NumericSchemaInfo {
206
+ EntityKind: 'Long'
207
+ }
208
+
209
+ export type LongType = LongInfo
210
+
211
+ export interface MapInfo extends ComplexSchemaInfo {
212
+ EntityKind: 'Map'
213
+ mapKey: string
214
+ mapValue: string
215
+ }
216
+
217
+ export type MapType = MapInfo
218
+
219
+ export interface MapKeyInfo extends NamedEntityInfo {
220
+ EntityKind: 'MapKey'
221
+ schema: string
222
+ }
223
+
224
+ export type MapKeyType = MapKeyInfo
225
+
226
+ export interface MapValueInfo extends SchemaFieldInfo {
227
+ EntityKind: 'MapValue'
228
+ }
229
+
230
+ export type MapValueType = MapValueInfo
231
+
232
+ export interface NamedEntityInfo extends EntityInfo {
233
+ EntityKind:
234
+ | 'Command'
235
+ | 'CommandPayload'
236
+ | 'Component'
237
+ | 'EnumValue'
238
+ | 'Field'
239
+ | 'MapKey'
240
+ | 'MapValue'
241
+ | 'Property'
242
+ | 'Relationship'
243
+ | 'Telemetry'
244
+ | 'CommandRequest'
245
+ | 'CommandResponse'
246
+ | 'UnitAttribute'
247
+ | 'NamedLatentType'
248
+ name: string
249
+ }
250
+
251
+ export type NamedEntityType =
252
+ | NamedEntityInfo
253
+ | ContentType
254
+ | EnumValueType
255
+ | MapKeyType
256
+ | NamedLatentTypeType
257
+ | SchemaFieldType
258
+ | UnitAttributeType
259
+
260
+ export interface NamedLatentTypeInfo extends NamedEntityInfo {
261
+ EntityKind: 'NamedLatentType'
262
+ }
263
+
264
+ export type NamedLatentTypeType = NamedLatentTypeInfo
265
+
266
+ export interface NumericSchemaInfo extends PrimitiveSchemaInfo {
267
+ EntityKind: 'Double' | 'Float' | 'Integer' | 'Long'
268
+ }
269
+
270
+ export type NumericSchemaType = NumericSchemaInfo | DoubleType | FloatType | IntegerType | LongType
271
+
272
+ export interface ObjectInfo extends ComplexSchemaInfo {
273
+ EntityKind: 'Object'
274
+ fields: string[]
275
+ }
276
+
277
+ export type ObjectType = ObjectInfo
278
+
279
+ export interface PrimitiveSchemaInfo extends SchemaInfo {
280
+ EntityKind: 'Boolean' | 'Date' | 'DateTime' | 'Double' | 'Duration' | 'Float' | 'Integer' | 'Long' | 'String' | 'Time'
281
+ }
282
+
283
+ export type PrimitiveSchemaType =
284
+ | PrimitiveSchemaInfo
285
+ | BooleanType
286
+ | NumericSchemaType
287
+ | StringType
288
+ | TemporalSchemaType
289
+
290
+ export interface PropertyInfo extends ContentInfo {
291
+ EntityKind: 'Property'
292
+ schema: string
293
+ writable: boolean
294
+ }
295
+
296
+ export type PropertyType = PropertyInfo
297
+
298
+ export interface RelationshipInfo extends ContentInfo {
299
+ EntityKind: 'Relationship'
300
+ maxMultiplicity?: number
301
+ minMultiplicity?: number
302
+ properties: string[]
303
+ target?: string
304
+ writable: boolean
305
+ }
306
+
307
+ export type RelationshipType = RelationshipInfo
308
+
309
+ export interface SchemaInfo extends EntityInfo {
310
+ EntityKind:
311
+ | 'Array'
312
+ | 'Boolean'
313
+ | 'Date'
314
+ | 'DateTime'
315
+ | 'Double'
316
+ | 'Duration'
317
+ | 'Enum'
318
+ | 'Float'
319
+ | 'Integer'
320
+ | 'Long'
321
+ | 'Map'
322
+ | 'Object'
323
+ | 'String'
324
+ | 'Time'
325
+ }
326
+
327
+ export type SchemaType = SchemaInfo | ComplexSchemaType | PrimitiveSchemaType
328
+
329
+ export interface SchemaFieldInfo extends NamedEntityInfo {
330
+ EntityKind: 'CommandPayload' | 'Field' | 'MapValue' | 'CommandRequest' | 'CommandResponse'
331
+ schema: string
332
+ }
333
+
334
+ export type SchemaFieldType = SchemaFieldInfo | CommandPayloadType | FieldType | MapValueType
335
+
336
+ export interface StringInfo extends PrimitiveSchemaInfo {
337
+ EntityKind: 'String'
338
+ }
339
+
340
+ export type StringType = StringInfo
341
+
342
+ export interface TelemetryInfo extends ContentInfo {
343
+ EntityKind: 'Telemetry'
344
+ schema: string
345
+ }
346
+
347
+ export type TelemetryType = TelemetryInfo
348
+
349
+ export interface TemporalSchemaInfo extends PrimitiveSchemaInfo {
350
+ EntityKind: 'Date' | 'DateTime' | 'Duration' | 'Time'
351
+ }
352
+
353
+ export type TemporalSchemaType = TemporalSchemaInfo | DateType | DateTimeType | DurationType | TimeType
354
+
355
+ export interface TimeInfo extends TemporalSchemaInfo {
356
+ EntityKind: 'Time'
357
+ }
358
+
359
+ export type TimeType = TimeInfo
360
+
361
+ export interface UnitInfo extends EntityInfo {
362
+ EntityKind: 'Unit'
363
+ symbol?: string
364
+ }
365
+
366
+ export type UnitType = UnitInfo
367
+
368
+ export interface UnitAttributeInfo extends NamedEntityInfo {
369
+ EntityKind: 'UnitAttribute'
370
+ }
371
+
372
+ export type UnitAttributeType = UnitAttributeInfo
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Digital Twin Consortium and contributors.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
@@ -0,0 +1,19 @@
1
+ using DTDLParser;
2
+ using System;
3
+ using System.Runtime.InteropServices.JavaScript;
4
+ using System.Runtime.Versioning;
5
+
6
+ namespace DtdlParserJSInterop;
7
+
8
+ [SupportedOSPlatform("browser")]
9
+ public partial class ModelParserInterop
10
+ {
11
+ public static void Main() => Console.WriteLine("dotnet loaded");
12
+
13
+ [JSExport]
14
+ public static string ParserVersion() => typeof(ModelParser).Assembly.FullName;
15
+
16
+ [JSExport]
17
+ public static string Parse(string dtdl) => new ModelParser().ParseToJson(dtdl);
18
+
19
+ }
@@ -0,0 +1 @@
1
+ This directory modifies code from `DTDLParser for .NET`, licensed under an [MIT License](./LICENSE). You can find the original project [here](https://github.com/digitaltwinconsortium/DTDLParser).
@@ -0,0 +1,11 @@
1
+ import { dotnet } from './_framework/dotnet.js'
2
+
3
+ const { getAssemblyExports, getConfig } = await dotnet.withDiagnosticTracing(false).create()
4
+
5
+ const config = getConfig()
6
+ const assemblyExports = await getAssemblyExports(config.mainAssemblyName)
7
+
8
+ const parserVersion = () => assemblyExports.DtdlParserJSInterop.ModelParserInterop.ParserVersion()
9
+ const parse = (dtdl) => assemblyExports.DtdlParserJSInterop.ModelParserInterop.Parse(dtdl)
10
+
11
+ export { parse, parserVersion }
@@ -0,0 +1,11 @@
1
+ {
2
+ "wasmHostProperties": {
3
+ "perHostConfig": [
4
+ {
5
+ "name": "node",
6
+ "js-path": "modelParser.js",
7
+ "host": "nodejs"
8
+ }
9
+ ]
10
+ }
11
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@digicatapult/dtdl-parser",
3
+ "version": "0.0.3",
4
+ "description": "JS tool to parse DTDL defined Ontologies",
5
+ "main": "src/index.ts",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "NODE_ENV=test ./node_modules/.bin/mocha --config ./test/mocharc.json ./src/**/*.test.ts",
9
+ "build": "npx swc ./src ./package.json -d ./build --copy-files",
10
+ "clean": "rimraf -rf ./build",
11
+ "lint": "eslint .",
12
+ "depcheck": "depcheck"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/digicatapult/dtdl-parser.git"
17
+ },
18
+ "keywords": [
19
+ "dtdl",
20
+ "digital",
21
+ "twins",
22
+ "parsing",
23
+ "tool"
24
+ ],
25
+ "author": "Digital Catapult",
26
+ "license": "Apache-2.0",
27
+ "bugs": {
28
+ "url": "https://github.com/digicatapult/dtdl-parser/issues"
29
+ },
30
+ "homepage": "https://github.com/digicatapult/dtdl-parser#readme",
31
+ "devDependencies": {
32
+ "@eslint/eslintrc": "^3.1.0",
33
+ "@eslint/js": "^9.12.0",
34
+ "@swc-node/register": "^1.10.9",
35
+ "@swc/cli": "^0.4.1-nightly.20240914",
36
+ "@swc/core": "^1.7.35",
37
+ "@types/chai": "^5.0.0",
38
+ "@types/mocha": "^10.0.9",
39
+ "@types/node": "^22.7.5",
40
+ "@typescript-eslint/eslint-plugin": "^8.8.1",
41
+ "@typescript-eslint/parser": "^8.8.1",
42
+ "chai": "^5.1.1",
43
+ "depcheck": "^1.4.7",
44
+ "eslint": "^9.12.0",
45
+ "eslint-config-prettier": "^9.1.0",
46
+ "eslint-plugin-prettier": "^5.2.1",
47
+ "globals": "^15.11.0",
48
+ "mocha": "^10.7.3",
49
+ "prettier": "^3.3.3",
50
+ "prettier-plugin-organize-imports": "^4.1.0",
51
+ "rimraf": "^6.0.1",
52
+ "typescript": "^5.6.3"
53
+ },
54
+ "engines": {
55
+ "node": ">= 20"
56
+ }
57
+ }
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,119 @@
1
+ import { expect } from 'chai'
2
+ import { describe, it } from 'mocha'
3
+ import path from 'path'
4
+ import { parseDirectories, searchForJsonFiles, validateDirectories } from '../index'
5
+ import { Parser } from '../interop'
6
+
7
+ const fixturesFilepath = path.resolve('src/__tests__/fixtures')
8
+
9
+ const exampleModel = {
10
+ 'dtmi:com:example:base;1': {
11
+ languageMajorVersion: 3,
12
+ Id: 'dtmi:com:example:base;1',
13
+ ChildOf: 'dtmi:com:example;1',
14
+ DefinedIn: 'dtmi:com:example;1',
15
+ EntityKind: 'Interface',
16
+ ClassId: 'dtmi:dtdl:class:Interface;3',
17
+ extendedBy: ['dtmi:com:example;1'],
18
+ },
19
+ 'dtmi:com:example;1': {
20
+ languageMajorVersion: 3,
21
+ Id: 'dtmi:com:example;1',
22
+ EntityKind: 'Interface',
23
+ ClassId: 'dtmi:dtdl:class:Interface;3',
24
+ extends: ['dtmi:com:example:base;1'],
25
+ },
26
+ }
27
+
28
+ const mockParser: Parser = {
29
+ parse: () => JSON.stringify(exampleModel),
30
+ parserVersion: () => '1.0.0',
31
+ }
32
+
33
+ const mockParserWithParsingException: Parser = {
34
+ parse: () => {
35
+ throw new Error(
36
+ JSON.stringify({
37
+ ExceptionKind: 'Parsing',
38
+ Errors: [{ Cause: '', Action: '', ValidationID: '' }],
39
+ })
40
+ )
41
+ },
42
+ parserVersion: () => '1.0.0',
43
+ }
44
+
45
+ const mockParserWithResolutionException: Parser = {
46
+ parse: () => {
47
+ throw new Error(
48
+ JSON.stringify({
49
+ ExceptionKind: 'Resolution',
50
+ })
51
+ )
52
+ },
53
+ parserVersion: () => '1.0.0',
54
+ }
55
+
56
+ describe('parse', () => {
57
+ describe('search for files', () => {
58
+ it('should return nested json filepaths', async () => {
59
+ const filepaths = searchForJsonFiles(fixturesFilepath)
60
+ expect(filepaths.map((fp) => path.basename(fp))).to.deep.equal(['empty.json', 'nested.json'])
61
+ })
62
+ })
63
+
64
+ describe('valid parse', () => {
65
+ it('should return model', async () => {
66
+ const model = parseDirectories(fixturesFilepath, mockParser)
67
+ expect(model).to.deep.equal(exampleModel)
68
+ })
69
+ })
70
+
71
+ describe('invalid directory path', () => {
72
+ it('should return null', async () => {
73
+ const model = parseDirectories('invalid', mockParser)
74
+ expect(model).to.equal(null)
75
+ })
76
+ })
77
+
78
+ describe('parsing exception thrown by interop parser', () => {
79
+ it('should return null', async () => {
80
+ const model = parseDirectories(fixturesFilepath, mockParserWithParsingException)
81
+ expect(model).to.equal(null)
82
+ })
83
+ })
84
+ })
85
+
86
+ describe('parse', () => {
87
+ describe('valid validate', () => {
88
+ it('should returned validated true', async () => {
89
+ const isValid = validateDirectories(fixturesFilepath, mockParser, false)
90
+ expect(isValid).to.equal(true)
91
+ })
92
+ })
93
+
94
+ describe('invalid directory path', () => {
95
+ it('should return null', async () => {
96
+ const isValid = validateDirectories('invalid', mockParser, false)
97
+ expect(isValid).to.equal(false)
98
+ })
99
+ })
100
+
101
+ describe('parsing exception thrown by interop validate', () => {
102
+ it('should return false', async () => {
103
+ const isValid = validateDirectories(fixturesFilepath, mockParserWithParsingException, false)
104
+ expect(isValid).to.equal(false)
105
+ })
106
+ })
107
+
108
+ describe('resolution exception thrown by interop validate', () => {
109
+ it('should return false if including resolution check', async () => {
110
+ const isValid = validateDirectories(fixturesFilepath, mockParserWithResolutionException, true)
111
+ expect(isValid).to.equal(false)
112
+ })
113
+
114
+ it('should return true if NOT including resolution check', async () => {
115
+ const isValid = validateDirectories(fixturesFilepath, mockParserWithResolutionException, false)
116
+ expect(isValid).to.equal(true)
117
+ })
118
+ })
119
+ })
package/src/error.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { ModelingException, ParsingException, ResolutionException } from '../interop/DtdlErr.js'
2
+
3
+ const { error } = console
4
+
5
+ const isParsingEx = (exception: ModelingException): exception is ParsingException => {
6
+ return exception.ExceptionKind === 'Parsing'
7
+ }
8
+
9
+ const isResolutionEx = (exception: ModelingException): exception is ResolutionException => {
10
+ return exception.ExceptionKind === 'Resolution'
11
+ }
12
+
13
+ export const isResolutionException = (err: unknown) => {
14
+ if (!(err instanceof Error)) return false
15
+ return isResolutionEx(JSON.parse(err.message))
16
+ }
17
+
18
+ export const errorHandler = (err: unknown) => {
19
+ if (!(err instanceof Error)) return error(`Unexpected error: ${err}`)
20
+
21
+ const exception = JSON.parse(err.message) as ModelingException
22
+
23
+ if (!(isParsingEx(exception) || isResolutionEx(exception))) error('Unknown exception type')
24
+ error(exception)
25
+ }