@ifc-lite/ids 1.14.11 → 1.15.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.
- package/dist/audit/coherence/index.d.ts +12 -0
- package/dist/audit/coherence/index.d.ts.map +1 -0
- package/dist/audit/coherence/index.js +392 -0
- package/dist/audit/coherence/index.js.map +1 -0
- package/dist/audit/coherence/regex.d.ts +57 -0
- package/dist/audit/coherence/regex.d.ts.map +1 -0
- package/dist/audit/coherence/regex.js +111 -0
- package/dist/audit/coherence/regex.js.map +1 -0
- package/dist/audit/ifc-schema/index.d.ts +4 -0
- package/dist/audit/ifc-schema/index.d.ts.map +1 -0
- package/dist/audit/ifc-schema/index.js +637 -0
- package/dist/audit/ifc-schema/index.js.map +1 -0
- package/dist/audit/index.d.ts +37 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +60 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/parser-shim.d.ts +13 -0
- package/dist/audit/parser-shim.d.ts.map +1 -0
- package/dist/audit/parser-shim.js +66 -0
- package/dist/audit/parser-shim.js.map +1 -0
- package/dist/audit/structural/index.d.ts +22 -0
- package/dist/audit/structural/index.d.ts.map +1 -0
- package/dist/audit/structural/index.js +441 -0
- package/dist/audit/structural/index.js.map +1 -0
- package/dist/audit/types.d.ts +91 -0
- package/dist/audit/types.d.ts.map +1 -0
- package/dist/audit/types.js +5 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/audit/xsd/index.d.ts +13 -0
- package/dist/audit/xsd/index.d.ts.map +1 -0
- package/dist/audit/xsd/index.js +203 -0
- package/dist/audit/xsd/index.js.map +1 -0
- package/dist/bridge/classifications.d.ts +15 -0
- package/dist/bridge/classifications.d.ts.map +1 -0
- package/dist/bridge/classifications.js +114 -0
- package/dist/bridge/classifications.js.map +1 -0
- package/dist/bridge/data-accessor.d.ts +16 -0
- package/dist/bridge/data-accessor.d.ts.map +1 -0
- package/dist/bridge/data-accessor.js +205 -0
- package/dist/bridge/data-accessor.js.map +1 -0
- package/dist/bridge/data-types.d.ts +11 -0
- package/dist/bridge/data-types.d.ts.map +1 -0
- package/dist/bridge/data-types.js +47 -0
- package/dist/bridge/data-types.js.map +1 -0
- package/dist/bridge/index.d.ts +17 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +20 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/materials.d.ts +11 -0
- package/dist/bridge/materials.d.ts.map +1 -0
- package/dist/bridge/materials.js +68 -0
- package/dist/bridge/materials.js.map +1 -0
- package/dist/bridge/predefined-types.d.ts +19 -0
- package/dist/bridge/predefined-types.d.ts.map +1 -0
- package/dist/bridge/predefined-types.js +84 -0
- package/dist/bridge/predefined-types.js.map +1 -0
- package/dist/bridge/properties.d.ts +20 -0
- package/dist/bridge/properties.d.ts.map +1 -0
- package/dist/bridge/properties.js +168 -0
- package/dist/bridge/properties.js.map +1 -0
- package/dist/bridge/schema-version.d.ts +10 -0
- package/dist/bridge/schema-version.d.ts.map +1 -0
- package/dist/bridge/schema-version.js +25 -0
- package/dist/bridge/schema-version.js.map +1 -0
- package/dist/bridge/units.d.ts +18 -0
- package/dist/bridge/units.d.ts.map +1 -0
- package/dist/bridge/units.js +55 -0
- package/dist/bridge/units.js.map +1 -0
- package/dist/constraints/comparators.d.ts +32 -0
- package/dist/constraints/comparators.d.ts.map +1 -0
- package/dist/constraints/comparators.js +94 -0
- package/dist/constraints/comparators.js.map +1 -0
- package/dist/constraints/index.d.ts.map +1 -1
- package/dist/constraints/index.js +68 -54
- package/dist/constraints/index.js.map +1 -1
- package/dist/constraints/xsd-cast.d.ts +20 -0
- package/dist/constraints/xsd-cast.d.ts.map +1 -0
- package/dist/constraints/xsd-cast.js +89 -0
- package/dist/constraints/xsd-cast.js.map +1 -0
- package/dist/facets/attribute-facet.d.ts.map +1 -1
- package/dist/facets/attribute-facet.js +51 -4
- package/dist/facets/attribute-facet.js.map +1 -1
- package/dist/facets/entity-facet.d.ts.map +1 -1
- package/dist/facets/entity-facet.js +55 -5
- package/dist/facets/entity-facet.js.map +1 -1
- package/dist/facets/partof-facet.d.ts.map +1 -1
- package/dist/facets/partof-facet.js +33 -3
- package/dist/facets/partof-facet.js.map +1 -1
- package/dist/facets/property-facet.d.ts.map +1 -1
- package/dist/facets/property-facet.js +119 -25
- package/dist/facets/property-facet.js.map +1 -1
- package/dist/facets/test-helpers.d.ts +7 -1
- package/dist/facets/test-helpers.d.ts.map +1 -1
- package/dist/facets/test-helpers.js +4 -0
- package/dist/facets/test-helpers.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/parser/xml-parser.d.ts.map +1 -1
- package/dist/parser/xml-parser.js +166 -64
- package/dist/parser/xml-parser.js.map +1 -1
- package/dist/translation/locales/de.d.ts +1 -0
- package/dist/translation/locales/de.d.ts.map +1 -1
- package/dist/translation/locales/de.js +1 -0
- package/dist/translation/locales/de.js.map +1 -1
- package/dist/translation/locales/en.d.ts +1 -0
- package/dist/translation/locales/en.d.ts.map +1 -1
- package/dist/translation/locales/en.js +1 -0
- package/dist/translation/locales/en.js.map +1 -1
- package/dist/translation/locales/fr.d.ts +1 -0
- package/dist/translation/locales/fr.d.ts.map +1 -1
- package/dist/translation/locales/fr.js +1 -0
- package/dist/translation/locales/fr.js.map +1 -1
- package/dist/types.d.ts +115 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/validation/validator.js +45 -3
- package/dist/validation/validator.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { extractPropertiesOnDemand, extractQuantitiesOnDemand, extractTypePropertiesOnDemand, extractTypeEntityOwnProperties, extractAllEntityAttributes, } from '@ifc-lite/parser';
|
|
5
|
+
import { RelationshipType } from '@ifc-lite/data';
|
|
6
|
+
import { idsDataTypeForProperty, idsDataTypeForQuantity } from './data-types.js';
|
|
7
|
+
import { applyUnitConversion } from './units.js';
|
|
8
|
+
/**
|
|
9
|
+
* IDS treats `IfcElementQuantity` (Qto_*) and `IfcPropertySet` (Pset_*)
|
|
10
|
+
* uniformly — both surface as "property sets" with named values. The
|
|
11
|
+
* parser stores them in separate columnar tables, so we merge here.
|
|
12
|
+
*
|
|
13
|
+
* Surfaces three sources:
|
|
14
|
+
* 1. The instance's own property sets (IfcPropertySet via
|
|
15
|
+
* IfcRelDefinesByProperties).
|
|
16
|
+
* 2. Predefined property sets (IfcDoorPanelProperties etc.) where
|
|
17
|
+
* the entity's schema-defined attributes act as property names.
|
|
18
|
+
* 3. Inherited property sets from `IfcRelDefinesByType`.
|
|
19
|
+
*
|
|
20
|
+
* Length-typed properties are converted to base SI units per the
|
|
21
|
+
* project's `lengthUnitScale` so IDS literals (always in metres)
|
|
22
|
+
* compare correctly.
|
|
23
|
+
*/
|
|
24
|
+
export function collectAllPropertySets(store, expressId) {
|
|
25
|
+
const out = [];
|
|
26
|
+
const scale = store.lengthUnitScale;
|
|
27
|
+
appendInstancePropertySets(store, expressId, scale, out);
|
|
28
|
+
appendQuantitySets(store, expressId, out);
|
|
29
|
+
appendPredefinedPropertySets(store, expressId, out);
|
|
30
|
+
appendInheritedPropertySets(store, expressId, scale, out);
|
|
31
|
+
if (out.length === 0) {
|
|
32
|
+
appendTypeEntityOwnProperties(store, expressId, out);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function appendInstancePropertySets(store, expressId, scale, out) {
|
|
37
|
+
let props = store.properties?.getForEntity?.(expressId);
|
|
38
|
+
if (!props || props.length === 0) {
|
|
39
|
+
props = extractPropertiesOnDemand(store, expressId);
|
|
40
|
+
}
|
|
41
|
+
if (!props || props.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
for (const pset of props) {
|
|
44
|
+
out.push({
|
|
45
|
+
name: pset.name,
|
|
46
|
+
properties: (pset.properties || []).map((p) => projectProperty(p, scale)),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function appendQuantitySets(store, expressId, out) {
|
|
51
|
+
let quantities = store.quantities?.getForEntity?.(expressId);
|
|
52
|
+
if (!quantities || quantities.length === 0) {
|
|
53
|
+
quantities = extractQuantitiesOnDemand(store, expressId);
|
|
54
|
+
}
|
|
55
|
+
if (!quantities || quantities.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
for (const qset of quantities) {
|
|
58
|
+
out.push({
|
|
59
|
+
name: qset.name,
|
|
60
|
+
properties: (qset.quantities || []).map((q) => ({
|
|
61
|
+
name: q.name,
|
|
62
|
+
value: q.value,
|
|
63
|
+
dataType: idsDataTypeForQuantity(q.type),
|
|
64
|
+
})),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Predefined property-set entities (`IfcDoorPanelProperties`, …) are
|
|
70
|
+
* connected to elements via `IfcRelDefinesByProperties` like a normal
|
|
71
|
+
* pset, but their properties live as schema-defined ATTRIBUTES on the
|
|
72
|
+
* entity itself. Surface them as a pset whose name is the entity's
|
|
73
|
+
* `Name` and whose properties are the schema-defined attribute slots
|
|
74
|
+
* beyond Name/Description.
|
|
75
|
+
*/
|
|
76
|
+
function appendPredefinedPropertySets(store, expressId, out) {
|
|
77
|
+
const psetIds = store.relationships?.getRelated?.(expressId, RelationshipType.DefinesByProperties, 'inverse') || [];
|
|
78
|
+
for (const psetId of psetIds) {
|
|
79
|
+
const ref = store.entityIndex?.byId?.get?.(psetId);
|
|
80
|
+
if (!ref)
|
|
81
|
+
continue;
|
|
82
|
+
const tu = String(ref.type).toUpperCase();
|
|
83
|
+
if (tu === 'IFCPROPERTYSET' || tu === 'IFCELEMENTQUANTITY')
|
|
84
|
+
continue;
|
|
85
|
+
if (!tu.endsWith('PROPERTIES'))
|
|
86
|
+
continue;
|
|
87
|
+
const allAttrs = extractAllEntityAttributes(store, psetId);
|
|
88
|
+
const psetNameAttr = allAttrs.find((a) => a.name === 'Name')?.value;
|
|
89
|
+
if (typeof psetNameAttr !== 'string' || !psetNameAttr)
|
|
90
|
+
continue;
|
|
91
|
+
if (out.some((p) => p.name === psetNameAttr))
|
|
92
|
+
continue;
|
|
93
|
+
const properties = allAttrs
|
|
94
|
+
.filter((a) => a.name !== 'GlobalId' &&
|
|
95
|
+
a.name !== 'Name' &&
|
|
96
|
+
a.name !== 'Description' &&
|
|
97
|
+
a.value !== undefined &&
|
|
98
|
+
a.value !== '')
|
|
99
|
+
.map((a) => ({
|
|
100
|
+
name: a.name,
|
|
101
|
+
value: a.value,
|
|
102
|
+
// dataType intentionally left empty: without a per-attribute
|
|
103
|
+
// schema lookup we can't know if PanelOperation is IFCDOORPANELOPERATIONENUM
|
|
104
|
+
// vs anything else. The IDS dataType gate then no-ops for these slots.
|
|
105
|
+
dataType: '',
|
|
106
|
+
}));
|
|
107
|
+
if (properties.length > 0)
|
|
108
|
+
out.push({ name: psetNameAttr, properties });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function appendInheritedPropertySets(store, expressId, scale, out) {
|
|
112
|
+
const inherited = extractTypePropertiesOnDemand(store, expressId);
|
|
113
|
+
if (!inherited || !inherited.properties || inherited.properties.length === 0)
|
|
114
|
+
return;
|
|
115
|
+
const seen = new Set(out.map((p) => p.name));
|
|
116
|
+
for (const pset of inherited.properties) {
|
|
117
|
+
if (seen.has(pset.name))
|
|
118
|
+
continue;
|
|
119
|
+
out.push({
|
|
120
|
+
name: pset.name,
|
|
121
|
+
properties: (pset.properties || []).map((p) => projectProperty(p, scale)),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function appendTypeEntityOwnProperties(store, expressId, out) {
|
|
126
|
+
const typePsets = extractTypeEntityOwnProperties(store, expressId);
|
|
127
|
+
if (typePsets.length === 0)
|
|
128
|
+
return;
|
|
129
|
+
for (const pset of typePsets) {
|
|
130
|
+
out.push({
|
|
131
|
+
name: pset.name,
|
|
132
|
+
properties: (pset.properties || []).map((p) => ({
|
|
133
|
+
name: p.name,
|
|
134
|
+
value: Array.isArray(p.value)
|
|
135
|
+
? JSON.stringify(p.value)
|
|
136
|
+
: p.value,
|
|
137
|
+
dataType: idsDataTypeForProperty(p.type),
|
|
138
|
+
...(Array.isArray(p.values) && p.values.length > 0
|
|
139
|
+
? { values: p.values }
|
|
140
|
+
: {}),
|
|
141
|
+
})),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Project a raw parser property record into the validator's
|
|
147
|
+
* `PropertySetInfo['properties'][number]` shape — applies unit
|
|
148
|
+
* conversion and resolves the IDS dataType. Multi-valued properties
|
|
149
|
+
* (lists, enumerations, table values) suppress dataType so the
|
|
150
|
+
* validator's dataType gate falls through to the value match.
|
|
151
|
+
*/
|
|
152
|
+
function projectProperty(p, scale) {
|
|
153
|
+
const hasMultiValue = Array.isArray(p.values) && p.values.length > 0;
|
|
154
|
+
const dataType = p.dataType ??
|
|
155
|
+
(hasMultiValue ? undefined : idsDataTypeForProperty(p.type));
|
|
156
|
+
const baseValue = Array.isArray(p.value)
|
|
157
|
+
? JSON.stringify(p.value)
|
|
158
|
+
: p.value;
|
|
159
|
+
const baseValues = hasMultiValue ? p.values : undefined;
|
|
160
|
+
const converted = applyUnitConversion(baseValue, baseValues, dataType, scale);
|
|
161
|
+
return {
|
|
162
|
+
name: p.name,
|
|
163
|
+
value: converted.value,
|
|
164
|
+
dataType: dataType ?? '',
|
|
165
|
+
...(converted.values ? { values: converted.values } : {}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=properties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"properties.js","sourceRoot":"","sources":["../../src/bridge/properties.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IfcSchemaVersion } from '@ifc-lite/data';
|
|
2
|
+
/**
|
|
3
|
+
* Narrow a parser schema-version string to the subset
|
|
4
|
+
* `@ifc-lite/data` carries lookup tables for. The parser surfaces a
|
|
5
|
+
* wider union (`'IFC2X3' | 'IFC4' | 'IFC4X3' | 'IFC5'`) than the
|
|
6
|
+
* schema package supports; IFC5 has no published EXPRESS schema yet so
|
|
7
|
+
* we treat it as IFC4X3 for type lookups.
|
|
8
|
+
*/
|
|
9
|
+
export declare function narrowSchemaVersion(raw: string | undefined): IfcSchemaVersion;
|
|
10
|
+
//# sourceMappingURL=schema-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-version.d.ts","sourceRoot":"","sources":["../../src/bridge/schema-version.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAa7E"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Narrow a parser schema-version string to the subset
|
|
6
|
+
* `@ifc-lite/data` carries lookup tables for. The parser surfaces a
|
|
7
|
+
* wider union (`'IFC2X3' | 'IFC4' | 'IFC4X3' | 'IFC5'`) than the
|
|
8
|
+
* schema package supports; IFC5 has no published EXPRESS schema yet so
|
|
9
|
+
* we treat it as IFC4X3 for type lookups.
|
|
10
|
+
*/
|
|
11
|
+
export function narrowSchemaVersion(raw) {
|
|
12
|
+
switch ((raw || '').toUpperCase()) {
|
|
13
|
+
case 'IFC2X3':
|
|
14
|
+
return 'IFC2X3';
|
|
15
|
+
case 'IFC4':
|
|
16
|
+
return 'IFC4';
|
|
17
|
+
case 'IFC4X3':
|
|
18
|
+
case 'IFC4X3_ADD2':
|
|
19
|
+
case 'IFC5':
|
|
20
|
+
return 'IFC4X3';
|
|
21
|
+
default:
|
|
22
|
+
return 'IFC4';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=schema-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-version.js","sourceRoot":"","sources":["../../src/bridge/schema-version.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAI/D;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAuB;IACzD,QAAQ,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAClC,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,QAAQ,CAAC;QACd,KAAK,aAAa,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,QAAQ,CAAC;QAClB;YACE,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per IDS spec, IDS literal values for IFC measure types are always in
|
|
3
|
+
* base SI units. The IFC store keeps the raw author value, so when the
|
|
4
|
+
* project's length unit is `MILLI`, a stored `1000` length means
|
|
5
|
+
* `1.0 metre` and the IDS check `1` should match. This helper applies
|
|
6
|
+
* the project's `lengthUnitScale` to numeric values for length
|
|
7
|
+
* measures.
|
|
8
|
+
*
|
|
9
|
+
* Properties without a declared dataType (notably `IfcPropertyTableValue`,
|
|
10
|
+
* where columns mix labels and measures) get a conservative double-up:
|
|
11
|
+
* every numeric candidate is surfaced both raw and scaled, so an IDS
|
|
12
|
+
* check using either unit space matches.
|
|
13
|
+
*/
|
|
14
|
+
export declare function applyUnitConversion(rawValue: string | number | boolean | null, rawValues: string[] | undefined, dataType: string | undefined, scale: number | undefined): {
|
|
15
|
+
value: string | number | boolean | null;
|
|
16
|
+
values: string[] | undefined;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=units.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/bridge/units.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,EAC1C,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB;IAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;CAAE,CAyC3E"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Per IDS spec, IDS literal values for IFC measure types are always in
|
|
6
|
+
* base SI units. The IFC store keeps the raw author value, so when the
|
|
7
|
+
* project's length unit is `MILLI`, a stored `1000` length means
|
|
8
|
+
* `1.0 metre` and the IDS check `1` should match. This helper applies
|
|
9
|
+
* the project's `lengthUnitScale` to numeric values for length
|
|
10
|
+
* measures.
|
|
11
|
+
*
|
|
12
|
+
* Properties without a declared dataType (notably `IfcPropertyTableValue`,
|
|
13
|
+
* where columns mix labels and measures) get a conservative double-up:
|
|
14
|
+
* every numeric candidate is surfaced both raw and scaled, so an IDS
|
|
15
|
+
* check using either unit space matches.
|
|
16
|
+
*/
|
|
17
|
+
export function applyUnitConversion(rawValue, rawValues, dataType, scale) {
|
|
18
|
+
if (!scale || scale === 1) {
|
|
19
|
+
return { value: rawValue, values: rawValues };
|
|
20
|
+
}
|
|
21
|
+
const upper = dataType ? dataType.toUpperCase() : '';
|
|
22
|
+
const isLength = upper === 'IFCLENGTHMEASURE' || upper === 'IFCPOSITIVELENGTHMEASURE';
|
|
23
|
+
const isUntypedTable = !dataType && Array.isArray(rawValues) && rawValues.length > 0;
|
|
24
|
+
if (!isLength && !isUntypedTable) {
|
|
25
|
+
return { value: rawValue, values: rawValues };
|
|
26
|
+
}
|
|
27
|
+
const convertNum = (v) => {
|
|
28
|
+
const n = typeof v === 'number' ? v : parseFloat(String(v));
|
|
29
|
+
return Number.isFinite(n) ? n * scale : null;
|
|
30
|
+
};
|
|
31
|
+
if (isLength) {
|
|
32
|
+
const converted = (() => {
|
|
33
|
+
const c = convertNum(rawValue);
|
|
34
|
+
return c == null ? rawValue : c;
|
|
35
|
+
})();
|
|
36
|
+
const values = Array.isArray(rawValues)
|
|
37
|
+
? rawValues.map((v) => {
|
|
38
|
+
const c = convertNum(v);
|
|
39
|
+
return c == null ? String(v) : String(c);
|
|
40
|
+
})
|
|
41
|
+
: rawValues;
|
|
42
|
+
return { value: converted, values };
|
|
43
|
+
}
|
|
44
|
+
// Untyped table — keep raw values and append scaled copies for every
|
|
45
|
+
// numeric candidate so either unit space matches.
|
|
46
|
+
const expanded = [];
|
|
47
|
+
for (const v of rawValues) {
|
|
48
|
+
expanded.push(String(v));
|
|
49
|
+
const c = convertNum(v);
|
|
50
|
+
if (c != null && String(c) !== String(v))
|
|
51
|
+
expanded.push(String(c));
|
|
52
|
+
}
|
|
53
|
+
return { value: rawValue, values: expanded };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=units.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/bridge/units.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA0C,EAC1C,SAA+B,EAC/B,QAA4B,EAC5B,KAAyB;IAEzB,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAChD,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,MAAM,QAAQ,GACZ,KAAK,KAAK,kBAAkB,IAAI,KAAK,KAAK,0BAA0B,CAAC;IACvE,MAAM,cAAc,GAClB,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,cAAc,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,CAAU,EAAiB,EAAE;QAC/C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC,CAAC;IAEF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE;YACtB,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC/B,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,EAAE,CAAC;QACL,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YACrC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAClB,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;gBACxB,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC3C,CAAC,CAAC;YACJ,CAAC,CAAC,SAAS,CAAC;QACd,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,qEAAqE;IACrE,kDAAkD;IAClD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,SAAU,EAAE,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare function numericEpsilon(castValue: number, actual?: number): number;
|
|
2
|
+
/**
|
|
3
|
+
* Compare an IDS literal `expected` (always a string from the XML) to
|
|
4
|
+
* `actual` as numbers. Returns:
|
|
5
|
+
* - `true` / `false` when both sides parse cleanly as numerics, OR
|
|
6
|
+
* - `undefined` when either side isn't a strict numeric literal,
|
|
7
|
+
* in which case the caller should fall back to other comparators
|
|
8
|
+
* (string equality, regex, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Strictness is the whole point: `parseFloat('2022-01-01')` returns
|
|
11
|
+
* `2022`, which would silently equate dates with their year. Requiring
|
|
12
|
+
* `NUMERIC_RE.test(...)` on both sides keeps date-like strings opaque.
|
|
13
|
+
*/
|
|
14
|
+
export declare function compareNumeric(expected: string, actual: string | number | boolean): boolean | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Compare an IDS boolean literal to a stored value. Per IDS 1.0 spec
|
|
17
|
+
* the literal MUST be lowercase `true` / `false`; uppercase or
|
|
18
|
+
* numeric forms (`1` / `0`) are malformed and never match.
|
|
19
|
+
*
|
|
20
|
+
* Returns:
|
|
21
|
+
* - `true` / `false` when both sides resolve to a boolean, OR
|
|
22
|
+
* - `undefined` when neither side is a recognised boolean — caller
|
|
23
|
+
* should fall through to other comparators.
|
|
24
|
+
*/
|
|
25
|
+
export declare function compareBoolean(expected: string, actual: string | number | boolean): boolean | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Plain string equality with optional case-insensitive shortcut.
|
|
28
|
+
* Returns `undefined` when no decision can be made so callers can
|
|
29
|
+
* try the numeric / boolean comparators next.
|
|
30
|
+
*/
|
|
31
|
+
export declare function compareString(expected: string, actual: string | number | boolean, caseInsensitive: boolean): boolean | undefined;
|
|
32
|
+
//# sourceMappingURL=comparators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparators.d.ts","sourceRoot":"","sources":["../../src/constraints/comparators.ts"],"names":[],"mappings":"AA6BA,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CASzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAChC,OAAO,GAAG,SAAS,CAYrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAChC,OAAO,GAAG,SAAS,CAgBrB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EACjC,eAAe,EAAE,OAAO,GACvB,OAAO,GAAG,SAAS,CAOrB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Shared comparators used by every IDS constraint matcher.
|
|
6
|
+
*
|
|
7
|
+
* These helpers encode IDS 1.0 cast-and-compare semantics in one
|
|
8
|
+
* place so `matchSimpleValue`, `matchEnumeration`, and `matchBounds`
|
|
9
|
+
* agree — the older code had `matchEnumeration` using a looser
|
|
10
|
+
* `parseFloat` path that silently equated date-shaped strings.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* A "clean" numeric literal: optional sign, digits, optional decimal,
|
|
14
|
+
* optional scientific exponent. Anchored end-to-end so trailing garbage
|
|
15
|
+
* (`'2022-01-01'`) doesn't smuggle through.
|
|
16
|
+
*/
|
|
17
|
+
const NUMERIC_RE = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
|
|
18
|
+
/**
|
|
19
|
+
* Numeric tolerance for floating-point comparisons.
|
|
20
|
+
*
|
|
21
|
+
* Mirrors upstream IfcOpenShell `ifctester`'s `is_x` rules: anchored
|
|
22
|
+
* on the IDS-side cast value with a ULP-scaled fudge to absorb the
|
|
23
|
+
* floating-point noise introduced when each side is decoded from text.
|
|
24
|
+
*/
|
|
25
|
+
const RELATIVE_TOLERANCE = 1e-6;
|
|
26
|
+
export function numericEpsilon(castValue, actual) {
|
|
27
|
+
const relative = RELATIVE_TOLERANCE * (1 + Math.abs(castValue));
|
|
28
|
+
const ulp = 16 * Number.EPSILON *
|
|
29
|
+
Math.max(Math.abs(castValue), typeof actual === 'number' ? Math.abs(actual) : 0);
|
|
30
|
+
return relative + ulp;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Compare an IDS literal `expected` (always a string from the XML) to
|
|
34
|
+
* `actual` as numbers. Returns:
|
|
35
|
+
* - `true` / `false` when both sides parse cleanly as numerics, OR
|
|
36
|
+
* - `undefined` when either side isn't a strict numeric literal,
|
|
37
|
+
* in which case the caller should fall back to other comparators
|
|
38
|
+
* (string equality, regex, etc.).
|
|
39
|
+
*
|
|
40
|
+
* Strictness is the whole point: `parseFloat('2022-01-01')` returns
|
|
41
|
+
* `2022`, which would silently equate dates with their year. Requiring
|
|
42
|
+
* `NUMERIC_RE.test(...)` on both sides keeps date-like strings opaque.
|
|
43
|
+
*/
|
|
44
|
+
export function compareNumeric(expected, actual) {
|
|
45
|
+
const actualStr = String(actual);
|
|
46
|
+
const expectedIsNumeric = NUMERIC_RE.test(expected);
|
|
47
|
+
const actualIsNumeric = typeof actual === 'number' || NUMERIC_RE.test(actualStr);
|
|
48
|
+
if (!expectedIsNumeric || !actualIsNumeric)
|
|
49
|
+
return undefined;
|
|
50
|
+
const expectedNum = parseFloat(expected);
|
|
51
|
+
const actualNum = typeof actual === 'number' ? actual : parseFloat(actualStr);
|
|
52
|
+
if (Number.isNaN(expectedNum) || Number.isNaN(actualNum))
|
|
53
|
+
return undefined;
|
|
54
|
+
return Math.abs(expectedNum - actualNum) <= numericEpsilon(expectedNum, actualNum);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Compare an IDS boolean literal to a stored value. Per IDS 1.0 spec
|
|
58
|
+
* the literal MUST be lowercase `true` / `false`; uppercase or
|
|
59
|
+
* numeric forms (`1` / `0`) are malformed and never match.
|
|
60
|
+
*
|
|
61
|
+
* Returns:
|
|
62
|
+
* - `true` / `false` when both sides resolve to a boolean, OR
|
|
63
|
+
* - `undefined` when neither side is a recognised boolean — caller
|
|
64
|
+
* should fall through to other comparators.
|
|
65
|
+
*/
|
|
66
|
+
export function compareBoolean(expected, actual) {
|
|
67
|
+
if (expected !== 'true' && expected !== 'false') {
|
|
68
|
+
// Expected isn't a valid boolean literal — caller falls through.
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
if (typeof actual === 'boolean') {
|
|
72
|
+
return expected === 'true' ? actual === true : actual === false;
|
|
73
|
+
}
|
|
74
|
+
if (actual === 'true' || actual === 'false') {
|
|
75
|
+
return actual === expected;
|
|
76
|
+
}
|
|
77
|
+
// Anything else against a boolean literal: malformed.
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Plain string equality with optional case-insensitive shortcut.
|
|
82
|
+
* Returns `undefined` when no decision can be made so callers can
|
|
83
|
+
* try the numeric / boolean comparators next.
|
|
84
|
+
*/
|
|
85
|
+
export function compareString(expected, actual, caseInsensitive) {
|
|
86
|
+
const actualStr = String(actual);
|
|
87
|
+
if (actualStr === expected)
|
|
88
|
+
return true;
|
|
89
|
+
if (caseInsensitive && actualStr.toUpperCase() === expected.toUpperCase()) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=comparators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparators.js","sourceRoot":"","sources":["../../src/constraints/comparators.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;GAOG;AAEH;;;;GAIG;AACH,MAAM,UAAU,GAAG,yCAAyC,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,MAAe;IAC/D,MAAM,QAAQ,GAAG,kBAAkB,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAChE,MAAM,GAAG,GACP,EAAE,GAAG,MAAM,CAAC,OAAO;QACnB,IAAI,CAAC,GAAG,CACN,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EACnB,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAClD,CAAC;IACJ,OAAO,QAAQ,GAAG,GAAG,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,MAAiC;IAEjC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,iBAAiB,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,eAAe,GACnB,OAAO,MAAM,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC3D,IAAI,CAAC,iBAAiB,IAAI,CAAC,eAAe;QAAE,OAAO,SAAS,CAAC;IAE7D,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC9E,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3E,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,SAAS,CAAC,IAAI,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,MAAiC;IAEjC,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChD,iEAAiE;QACjE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC;IAClE,CAAC;IAED,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QAC5C,OAAO,MAAM,KAAK,QAAQ,CAAC;IAC7B,CAAC;IAED,sDAAsD;IACtD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,MAAiC,EACjC,eAAwB;IAExB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,SAAS,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,eAAe,IAAI,SAAS,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constraints/index.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constraints/index.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,aAAa,CAAC;AAYrB,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,aAAa,EACzB,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EACzD,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAmBT;AAmKD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,aAAa,EACzB,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GACxD,MAAM,CAiBR;AA0CD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,CAgBlE"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
-
|
|
4
|
+
import { compareBoolean, compareNumeric, compareString, } from './comparators.js';
|
|
5
|
+
/** Tolerance for the bounds matcher's exclusive comparators. */
|
|
5
6
|
const NUMERIC_TOLERANCE = 1e-6;
|
|
6
7
|
/**
|
|
7
8
|
* Check if a value matches a constraint
|
|
@@ -15,7 +16,7 @@ export function matchConstraint(constraint, actualValue, options) {
|
|
|
15
16
|
case 'simpleValue':
|
|
16
17
|
return matchSimpleValue(constraint, actualValue, ci);
|
|
17
18
|
case 'pattern':
|
|
18
|
-
return matchPattern(constraint, actualValue);
|
|
19
|
+
return matchPattern(constraint, actualValue, ci);
|
|
19
20
|
case 'enumeration':
|
|
20
21
|
return matchEnumeration(constraint, actualValue, ci);
|
|
21
22
|
case 'bounds':
|
|
@@ -25,54 +26,44 @@ export function matchConstraint(constraint, actualValue, options) {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
|
-
* Match against a simple value
|
|
29
|
+
* Match against a simple value. Tries each comparator in order:
|
|
30
|
+
* string → numeric → boolean. The first decisive result wins;
|
|
31
|
+
* `undefined` lets the next strategy run.
|
|
29
32
|
*/
|
|
30
33
|
function matchSimpleValue(constraint, actualValue, caseInsensitive) {
|
|
31
34
|
const expected = constraint.value;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const actualNum = typeof actualValue === 'number' ? actualValue : parseFloat(actualStr);
|
|
42
|
-
if (!isNaN(expectedNum) && !isNaN(actualNum)) {
|
|
43
|
-
return Math.abs(expectedNum - actualNum) <= NUMERIC_TOLERANCE;
|
|
44
|
-
}
|
|
45
|
-
// Boolean comparison
|
|
46
|
-
if (typeof actualValue === 'boolean') {
|
|
47
|
-
const expectedLower = expected.toLowerCase();
|
|
48
|
-
if (expectedLower === 'true' || expectedLower === '1') {
|
|
49
|
-
return actualValue === true;
|
|
50
|
-
}
|
|
51
|
-
if (expectedLower === 'false' || expectedLower === '0') {
|
|
52
|
-
return actualValue === false;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Boolean string comparison
|
|
56
|
-
const actualLower = actualStr.toLowerCase();
|
|
57
|
-
const expectedLower = expected.toLowerCase();
|
|
58
|
-
if ((actualLower === 'true' || actualLower === 'false') &&
|
|
59
|
-
(expectedLower === 'true' || expectedLower === 'false')) {
|
|
60
|
-
return actualLower === expectedLower;
|
|
61
|
-
}
|
|
35
|
+
const stringResult = compareString(expected, actualValue, caseInsensitive);
|
|
36
|
+
if (stringResult !== undefined)
|
|
37
|
+
return stringResult;
|
|
38
|
+
const numericResult = compareNumeric(expected, actualValue);
|
|
39
|
+
if (numericResult !== undefined)
|
|
40
|
+
return numericResult;
|
|
41
|
+
const booleanResult = compareBoolean(expected, actualValue);
|
|
42
|
+
if (booleanResult !== undefined)
|
|
43
|
+
return booleanResult;
|
|
62
44
|
return false;
|
|
63
45
|
}
|
|
64
46
|
/**
|
|
65
47
|
* Match against a regex pattern
|
|
66
48
|
* IDS uses XSD regex syntax which is slightly different from JavaScript
|
|
67
49
|
*/
|
|
68
|
-
function matchPattern(constraint, actualValue) {
|
|
50
|
+
function matchPattern(constraint, actualValue, caseInsensitive = false) {
|
|
51
|
+
// Per IDS 1.0 spec patterns ONLY apply to string values. A pattern
|
|
52
|
+
// tested against a number / boolean fails outright — even if the
|
|
53
|
+
// textual representation would happen to match — so the validator
|
|
54
|
+
// can distinguish "wrong shape" from "wrong value".
|
|
55
|
+
if (typeof actualValue === 'number' || typeof actualValue === 'boolean') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
69
58
|
const actualStr = String(actualValue);
|
|
70
59
|
try {
|
|
71
60
|
// Convert XSD regex to JavaScript regex
|
|
72
61
|
const jsPattern = xsdToJsRegex(constraint.pattern);
|
|
73
|
-
// IDS patterns must match the entire string
|
|
74
|
-
//
|
|
75
|
-
|
|
62
|
+
// IDS patterns must match the entire string. Case-insensitive
|
|
63
|
+
// matching is opt-in per the call site (entity / predefined-type
|
|
64
|
+
// names use it; property and attribute values do not).
|
|
65
|
+
const flags = caseInsensitive ? 'i' : '';
|
|
66
|
+
const regex = new RegExp(`^${jsPattern}$`, flags);
|
|
76
67
|
return regex.test(actualStr);
|
|
77
68
|
}
|
|
78
69
|
catch {
|
|
@@ -95,24 +86,21 @@ function xsdToJsRegex(xsdPattern) {
|
|
|
95
86
|
.replace(/\[([^\]]+)-\[[^\]]+\]\]/g, '[$1]'));
|
|
96
87
|
}
|
|
97
88
|
/**
|
|
98
|
-
* Match against an enumeration
|
|
89
|
+
* Match against an enumeration. The actual value matches if ANY of the
|
|
90
|
+
* declared options matches under string / numeric / boolean comparison
|
|
91
|
+
* — same strategy table as `matchSimpleValue`, just iterated.
|
|
99
92
|
*/
|
|
100
93
|
function matchEnumeration(constraint, actualValue, caseInsensitive) {
|
|
101
|
-
const actualStr = String(actualValue);
|
|
102
|
-
const actualUpper = actualStr.toUpperCase();
|
|
103
94
|
return constraint.values.some((v) => {
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!isNaN(vNum) && !isNaN(actualNum)) {
|
|
114
|
-
return Math.abs(vNum - actualNum) <= NUMERIC_TOLERANCE;
|
|
115
|
-
}
|
|
95
|
+
const stringResult = compareString(v, actualValue, caseInsensitive);
|
|
96
|
+
if (stringResult !== undefined)
|
|
97
|
+
return stringResult;
|
|
98
|
+
const numericResult = compareNumeric(v, actualValue);
|
|
99
|
+
if (numericResult !== undefined)
|
|
100
|
+
return numericResult;
|
|
101
|
+
const booleanResult = compareBoolean(v, actualValue);
|
|
102
|
+
if (booleanResult !== undefined)
|
|
103
|
+
return booleanResult;
|
|
116
104
|
return false;
|
|
117
105
|
});
|
|
118
106
|
}
|
|
@@ -120,17 +108,43 @@ function matchEnumeration(constraint, actualValue, caseInsensitive) {
|
|
|
120
108
|
* Match against numeric bounds
|
|
121
109
|
*/
|
|
122
110
|
function matchBounds(constraint, actualValue) {
|
|
111
|
+
// String-length facets (xs:length / xs:minLength / xs:maxLength)
|
|
112
|
+
// operate on the textual length, not on numeric magnitude. When any
|
|
113
|
+
// of them are present, evaluate the length constraints first.
|
|
114
|
+
if (constraint.length !== undefined ||
|
|
115
|
+
constraint.minLength !== undefined ||
|
|
116
|
+
constraint.maxLength !== undefined) {
|
|
117
|
+
const str = String(actualValue);
|
|
118
|
+
if (constraint.length !== undefined && str.length !== constraint.length) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (constraint.minLength !== undefined && str.length < constraint.minLength) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (constraint.maxLength !== undefined && str.length > constraint.maxLength) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
// Length-only restrictions don't impose numeric bounds; if the
|
|
128
|
+
// constraint also carries min/max we fall through to the numeric
|
|
129
|
+
// check below (rare in practice).
|
|
130
|
+
if (constraint.minInclusive === undefined &&
|
|
131
|
+
constraint.maxInclusive === undefined &&
|
|
132
|
+
constraint.minExclusive === undefined &&
|
|
133
|
+
constraint.maxExclusive === undefined) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
123
137
|
const num = typeof actualValue === 'number'
|
|
124
138
|
? actualValue
|
|
125
139
|
: parseFloat(String(actualValue));
|
|
126
140
|
if (isNaN(num))
|
|
127
141
|
return false;
|
|
128
142
|
if (constraint.minInclusive !== undefined &&
|
|
129
|
-
num < constraint.minInclusive
|
|
143
|
+
num < constraint.minInclusive) {
|
|
130
144
|
return false;
|
|
131
145
|
}
|
|
132
146
|
if (constraint.maxInclusive !== undefined &&
|
|
133
|
-
num > constraint.maxInclusive
|
|
147
|
+
num > constraint.maxInclusive) {
|
|
134
148
|
return false;
|
|
135
149
|
}
|
|
136
150
|
if (constraint.minExclusive !== undefined && num <= constraint.minExclusive) {
|