@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.
Files changed (119) hide show
  1. package/dist/audit/coherence/index.d.ts +12 -0
  2. package/dist/audit/coherence/index.d.ts.map +1 -0
  3. package/dist/audit/coherence/index.js +392 -0
  4. package/dist/audit/coherence/index.js.map +1 -0
  5. package/dist/audit/coherence/regex.d.ts +57 -0
  6. package/dist/audit/coherence/regex.d.ts.map +1 -0
  7. package/dist/audit/coherence/regex.js +111 -0
  8. package/dist/audit/coherence/regex.js.map +1 -0
  9. package/dist/audit/ifc-schema/index.d.ts +4 -0
  10. package/dist/audit/ifc-schema/index.d.ts.map +1 -0
  11. package/dist/audit/ifc-schema/index.js +637 -0
  12. package/dist/audit/ifc-schema/index.js.map +1 -0
  13. package/dist/audit/index.d.ts +37 -0
  14. package/dist/audit/index.d.ts.map +1 -0
  15. package/dist/audit/index.js +60 -0
  16. package/dist/audit/index.js.map +1 -0
  17. package/dist/audit/parser-shim.d.ts +13 -0
  18. package/dist/audit/parser-shim.d.ts.map +1 -0
  19. package/dist/audit/parser-shim.js +66 -0
  20. package/dist/audit/parser-shim.js.map +1 -0
  21. package/dist/audit/structural/index.d.ts +22 -0
  22. package/dist/audit/structural/index.d.ts.map +1 -0
  23. package/dist/audit/structural/index.js +441 -0
  24. package/dist/audit/structural/index.js.map +1 -0
  25. package/dist/audit/types.d.ts +91 -0
  26. package/dist/audit/types.d.ts.map +1 -0
  27. package/dist/audit/types.js +5 -0
  28. package/dist/audit/types.js.map +1 -0
  29. package/dist/audit/xsd/index.d.ts +13 -0
  30. package/dist/audit/xsd/index.d.ts.map +1 -0
  31. package/dist/audit/xsd/index.js +203 -0
  32. package/dist/audit/xsd/index.js.map +1 -0
  33. package/dist/bridge/classifications.d.ts +15 -0
  34. package/dist/bridge/classifications.d.ts.map +1 -0
  35. package/dist/bridge/classifications.js +114 -0
  36. package/dist/bridge/classifications.js.map +1 -0
  37. package/dist/bridge/data-accessor.d.ts +16 -0
  38. package/dist/bridge/data-accessor.d.ts.map +1 -0
  39. package/dist/bridge/data-accessor.js +205 -0
  40. package/dist/bridge/data-accessor.js.map +1 -0
  41. package/dist/bridge/data-types.d.ts +11 -0
  42. package/dist/bridge/data-types.d.ts.map +1 -0
  43. package/dist/bridge/data-types.js +47 -0
  44. package/dist/bridge/data-types.js.map +1 -0
  45. package/dist/bridge/index.d.ts +17 -0
  46. package/dist/bridge/index.d.ts.map +1 -0
  47. package/dist/bridge/index.js +20 -0
  48. package/dist/bridge/index.js.map +1 -0
  49. package/dist/bridge/materials.d.ts +11 -0
  50. package/dist/bridge/materials.d.ts.map +1 -0
  51. package/dist/bridge/materials.js +68 -0
  52. package/dist/bridge/materials.js.map +1 -0
  53. package/dist/bridge/predefined-types.d.ts +19 -0
  54. package/dist/bridge/predefined-types.d.ts.map +1 -0
  55. package/dist/bridge/predefined-types.js +84 -0
  56. package/dist/bridge/predefined-types.js.map +1 -0
  57. package/dist/bridge/properties.d.ts +20 -0
  58. package/dist/bridge/properties.d.ts.map +1 -0
  59. package/dist/bridge/properties.js +168 -0
  60. package/dist/bridge/properties.js.map +1 -0
  61. package/dist/bridge/schema-version.d.ts +10 -0
  62. package/dist/bridge/schema-version.d.ts.map +1 -0
  63. package/dist/bridge/schema-version.js +25 -0
  64. package/dist/bridge/schema-version.js.map +1 -0
  65. package/dist/bridge/units.d.ts +18 -0
  66. package/dist/bridge/units.d.ts.map +1 -0
  67. package/dist/bridge/units.js +55 -0
  68. package/dist/bridge/units.js.map +1 -0
  69. package/dist/constraints/comparators.d.ts +32 -0
  70. package/dist/constraints/comparators.d.ts.map +1 -0
  71. package/dist/constraints/comparators.js +94 -0
  72. package/dist/constraints/comparators.js.map +1 -0
  73. package/dist/constraints/index.d.ts.map +1 -1
  74. package/dist/constraints/index.js +68 -54
  75. package/dist/constraints/index.js.map +1 -1
  76. package/dist/constraints/xsd-cast.d.ts +20 -0
  77. package/dist/constraints/xsd-cast.d.ts.map +1 -0
  78. package/dist/constraints/xsd-cast.js +89 -0
  79. package/dist/constraints/xsd-cast.js.map +1 -0
  80. package/dist/facets/attribute-facet.d.ts.map +1 -1
  81. package/dist/facets/attribute-facet.js +51 -4
  82. package/dist/facets/attribute-facet.js.map +1 -1
  83. package/dist/facets/entity-facet.d.ts.map +1 -1
  84. package/dist/facets/entity-facet.js +55 -5
  85. package/dist/facets/entity-facet.js.map +1 -1
  86. package/dist/facets/partof-facet.d.ts.map +1 -1
  87. package/dist/facets/partof-facet.js +33 -3
  88. package/dist/facets/partof-facet.js.map +1 -1
  89. package/dist/facets/property-facet.d.ts.map +1 -1
  90. package/dist/facets/property-facet.js +119 -25
  91. package/dist/facets/property-facet.js.map +1 -1
  92. package/dist/facets/test-helpers.d.ts +7 -1
  93. package/dist/facets/test-helpers.d.ts.map +1 -1
  94. package/dist/facets/test-helpers.js +4 -0
  95. package/dist/facets/test-helpers.js.map +1 -1
  96. package/dist/index.d.ts +2 -0
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +4 -0
  99. package/dist/index.js.map +1 -1
  100. package/dist/parser/xml-parser.d.ts.map +1 -1
  101. package/dist/parser/xml-parser.js +166 -64
  102. package/dist/parser/xml-parser.js.map +1 -1
  103. package/dist/translation/locales/de.d.ts +1 -0
  104. package/dist/translation/locales/de.d.ts.map +1 -1
  105. package/dist/translation/locales/de.js +1 -0
  106. package/dist/translation/locales/de.js.map +1 -1
  107. package/dist/translation/locales/en.d.ts +1 -0
  108. package/dist/translation/locales/en.d.ts.map +1 -1
  109. package/dist/translation/locales/en.js +1 -0
  110. package/dist/translation/locales/en.js.map +1 -1
  111. package/dist/translation/locales/fr.d.ts +1 -0
  112. package/dist/translation/locales/fr.d.ts.map +1 -1
  113. package/dist/translation/locales/fr.js +1 -0
  114. package/dist/translation/locales/fr.js.map +1 -1
  115. package/dist/types.d.ts +115 -6
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/validation/validator.js +45 -3
  118. package/dist/validation/validator.js.map +1 -1
  119. 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":"AAAA;;+DAE+D;AAE/D,OAAO,EAEL,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAUjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAmB,EACnB,SAAiB;IAEjB,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,CAAC;IAEpC,0BAA0B,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IACzD,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IAC1C,4BAA4B,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACpD,2BAA2B,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAE1D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,6BAA6B,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,0BAA0B,CACjC,KAAmB,EACnB,SAAiB,EACjB,KAAyB,EACzB,GAAsB;IAEtB,IAAI,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,SAAS,CAEzC,CAAC;IACd,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,GAAG,yBAAyB,CAAC,KAAK,EAAE,SAAS,CAGhD,CAAC;IACL,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;SAC1E,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAmB,EACnB,SAAiB,EACjB,GAAsB;IAEtB,IAAI,UAAU,GAAG,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,SAAS,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,UAAU,GAAG,yBAAyB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEnD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9C,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC;aACzC,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,4BAA4B,CACnC,KAAmB,EACnB,SAAiB,EACjB,GAAsB;IAEtB,MAAM,OAAO,GACX,KAAK,CAAC,aAAa,EAAE,UAAU,EAAE,CAC/B,SAAS,EACT,gBAAgB,CAAC,mBAAmB,EACpC,SAAS,CACV,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,EAAE,GAAG,MAAM,CAAE,GAA0B,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClE,IAAI,EAAE,KAAK,gBAAgB,IAAI,EAAE,KAAK,oBAAoB;YAAE,SAAS;QACrE,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,SAAS;QAEzC,MAAM,QAAQ,GAAG,0BAA0B,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC;QACpE,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,CAAC,YAAY;YAAE,SAAS;QAChE,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC;YAAE,SAAS;QAEvD,MAAM,UAAU,GAAG,QAAQ;aACxB,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,KAAK,UAAU;YACrB,CAAC,CAAC,IAAI,KAAK,MAAM;YACjB,CAAC,CAAC,IAAI,KAAK,aAAa;YACxB,CAAC,CAAC,KAAK,KAAK,SAAS;YACrB,CAAC,CAAC,KAAK,KAAK,EAAE,CACjB;aACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACX,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,6DAA6D;YAC7D,6EAA6E;YAC7E,uEAAuE;YACvE,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC,CAAC;QACN,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,SAAS,2BAA2B,CAClC,KAAmB,EACnB,SAAiB,EACjB,KAAyB,EACzB,GAAsB;IAEtB,MAAM,SAAS,GAAG,6BAA6B,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAClE,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;QAC1E,OAAO;IAET,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5C,eAAe,CAAC,CAAY,EAAE,KAAK,CAAC,CACrC;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,6BAA6B,CACpC,KAAmB,EACnB,SAAiB,EACjB,GAAsB;IAEtB,MAAM,SAAS,GAAG,8BAA8B,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACnE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACnC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9C,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;oBAC3B,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;oBACzB,CAAC,CAAE,CAAC,CAAC,KAA0C;gBACjD,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,IAAmC,CAAC;gBACvE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;oBAChD,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;oBACtB,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CACtB,CAAU,EACV,KAAyB;IAEzB,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACrE,MAAM,QAAQ,GACZ,CAAC,CAAC,QAAQ;QACV,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAmC,CAAC,CAAC,CAAC;IAC9F,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QACtC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;QACzB,CAAC,CAAE,CAAC,CAAC,KAA0C,CAAC;IAClD,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAE,CAAC,CAAC,MAAmB,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,SAAS,GAAG,mBAAmB,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC9E,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,QAAQ,EAAE,QAAQ,IAAI,EAAE;QACxB,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1D,CAAC;AACJ,CAAC"}
@@ -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;AAKrB,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;AA8JD;;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
+ {"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
- /** Numeric tolerance for floating point comparisons (per IDS spec) */
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 (exact match)
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 actualStr = String(actualValue);
33
- // Exact string match
34
- if (actualStr === expected)
35
- return true;
36
- // Case-insensitive match only when explicitly requested (IFC entity/predefined type names)
37
- if (caseInsensitive && actualStr.toUpperCase() === expected.toUpperCase())
38
- return true;
39
- // Numeric comparison with tolerance
40
- const expectedNum = parseFloat(expected);
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
- // Per XSD/IDS spec, patterns are case-sensitive (no 'i' flag)
75
- const regex = new RegExp(`^${jsPattern}$`);
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 (one of a list)
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
- // Try exact match first
105
- if (v === actualStr)
106
- return true;
107
- // Case-insensitive match only when explicitly requested
108
- if (caseInsensitive && v.toUpperCase() === actualUpper)
109
- return true;
110
- // Numeric comparison
111
- const vNum = parseFloat(v);
112
- const actualNum = typeof actualValue === 'number' ? actualValue : parseFloat(actualStr);
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 - NUMERIC_TOLERANCE) {
143
+ num < constraint.minInclusive) {
130
144
  return false;
131
145
  }
132
146
  if (constraint.maxInclusive !== undefined &&
133
- num > constraint.maxInclusive + NUMERIC_TOLERANCE) {
147
+ num > constraint.maxInclusive) {
134
148
  return false;
135
149
  }
136
150
  if (constraint.minExclusive !== undefined && num <= constraint.minExclusive) {