@ifc-lite/ids 1.15.50 → 1.15.52

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 (56) hide show
  1. package/dist/bridge/data-types.d.ts.map +1 -1
  2. package/dist/bridge/data-types.js +1 -0
  3. package/dist/bridge/data-types.js.map +1 -1
  4. package/dist/constraint-types.d.ts +110 -0
  5. package/dist/constraint-types.d.ts.map +1 -0
  6. package/dist/constraint-types.js +5 -0
  7. package/dist/constraint-types.js.map +1 -0
  8. package/dist/constraints/describe.d.ts +17 -0
  9. package/dist/constraints/describe.d.ts.map +1 -0
  10. package/dist/constraints/describe.js +140 -0
  11. package/dist/constraints/describe.js.map +1 -0
  12. package/dist/constraints/index.d.ts +1 -8
  13. package/dist/constraints/index.d.ts.map +1 -1
  14. package/dist/constraints/index.js +13 -361
  15. package/dist/constraints/index.js.map +1 -1
  16. package/dist/constraints/match-family.d.ts +20 -0
  17. package/dist/constraints/match-family.d.ts.map +1 -0
  18. package/dist/constraints/match-family.js +265 -0
  19. package/dist/constraints/match-family.js.map +1 -0
  20. package/dist/constraints/xsd-cast.d.ts +20 -1
  21. package/dist/constraints/xsd-cast.d.ts.map +1 -1
  22. package/dist/constraints/xsd-cast.js +99 -10
  23. package/dist/constraints/xsd-cast.js.map +1 -1
  24. package/dist/facets/property-facet.d.ts.map +1 -1
  25. package/dist/facets/property-facet.js +30 -33
  26. package/dist/facets/property-facet.js.map +1 -1
  27. package/dist/parser/dom.d.ts +11 -0
  28. package/dist/parser/dom.d.ts.map +1 -0
  29. package/dist/parser/dom.js +49 -0
  30. package/dist/parser/dom.js.map +1 -0
  31. package/dist/parser/xml-parser.d.ts.map +1 -1
  32. package/dist/parser/xml-parser.js +46 -58
  33. package/dist/parser/xml-parser.js.map +1 -1
  34. package/dist/translation/describe-constraint.d.ts +18 -0
  35. package/dist/translation/describe-constraint.d.ts.map +1 -0
  36. package/dist/translation/describe-constraint.js +77 -0
  37. package/dist/translation/describe-constraint.js.map +1 -0
  38. package/dist/translation/locales/de.d.ts +1 -0
  39. package/dist/translation/locales/de.d.ts.map +1 -1
  40. package/dist/translation/locales/de.js +1 -0
  41. package/dist/translation/locales/de.js.map +1 -1
  42. package/dist/translation/locales/en.d.ts +2 -0
  43. package/dist/translation/locales/en.d.ts.map +1 -1
  44. package/dist/translation/locales/en.js +2 -0
  45. package/dist/translation/locales/en.js.map +1 -1
  46. package/dist/translation/locales/fr.d.ts +1 -0
  47. package/dist/translation/locales/fr.d.ts.map +1 -1
  48. package/dist/translation/locales/fr.js +1 -0
  49. package/dist/translation/locales/fr.js.map +1 -1
  50. package/dist/translation/service.d.ts +4 -5
  51. package/dist/translation/service.d.ts.map +1 -1
  52. package/dist/translation/service.js +8 -58
  53. package/dist/translation/service.js.map +1 -1
  54. package/dist/types.d.ts +2 -59
  55. package/dist/types.d.ts.map +1 -1
  56. package/package.json +9 -8
@@ -0,0 +1,265 @@
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 { compareBoolean, compareNumeric, compareString, isStrictNumericLiteral, isBooleanLiteral, } from './comparators.js';
5
+ import { isNumericXsdBase, isBooleanXsdBase } from './xsd-cast.js';
6
+ import { translateXsdRegex } from './xsd-regex.js';
7
+ /** Tolerance for the bounds matcher's exclusive comparators. */
8
+ export const NUMERIC_TOLERANCE = 1e-6;
9
+ /**
10
+ * The conjunctive facets `parseRestriction` attached to this constraint
11
+ * from the same `<xs:restriction>`, or `undefined` when it declared one
12
+ * facet family (the common case). `simpleValue` never carries any: it
13
+ * does not come from a restriction.
14
+ */
15
+ export function conjunctiveFacetsOf(constraint) {
16
+ return constraint.type === 'simpleValue' ? undefined : constraint.and;
17
+ }
18
+ export function matchOneFamily(constraint, actualValue, ci) {
19
+ switch (constraint.type) {
20
+ case 'simpleValue':
21
+ return matchSimpleValue(constraint, actualValue, ci);
22
+ case 'pattern':
23
+ return matchPattern(constraint, actualValue, ci);
24
+ case 'enumeration':
25
+ return matchEnumeration(constraint, actualValue, ci);
26
+ case 'bounds':
27
+ return matchBounds(constraint, actualValue);
28
+ default:
29
+ return false;
30
+ }
31
+ }
32
+ /**
33
+ * Per-constraint comparator applicability. `compareNumeric` runs two
34
+ * regex tests and `compareBoolean` two equality checks per call — and
35
+ * simple-value matching sits inside per-entity × per-specification hot
36
+ * loops (name matching against every pset/property). Whether the IDS
37
+ * literal could EVER match numerically or boolean-ly depends only on
38
+ * the constraint, so decide it once.
39
+ */
40
+ const SIMPLE_VALUE_COERCIBLE = new WeakMap();
41
+ function isCoercibleSimpleValue(constraint) {
42
+ let coercible = SIMPLE_VALUE_COERCIBLE.get(constraint);
43
+ if (coercible === undefined) {
44
+ coercible =
45
+ isStrictNumericLiteral(constraint.value) ||
46
+ isBooleanLiteral(constraint.value);
47
+ SIMPLE_VALUE_COERCIBLE.set(constraint, coercible);
48
+ }
49
+ return coercible;
50
+ }
51
+ /**
52
+ * Match against a simple value. Tries each comparator in order:
53
+ * string → numeric → boolean. The first decisive result wins;
54
+ * `undefined` lets the next strategy run.
55
+ */
56
+ function matchSimpleValue(constraint, actualValue, caseInsensitive) {
57
+ const expected = constraint.value;
58
+ const stringResult = compareString(expected, actualValue, caseInsensitive);
59
+ if (stringResult !== undefined)
60
+ return stringResult;
61
+ // A non-numeric, non-boolean literal can only match through string
62
+ // equality — skip the comparators that would return undefined anyway.
63
+ if (!isCoercibleSimpleValue(constraint))
64
+ return false;
65
+ const numericResult = compareNumeric(expected, actualValue);
66
+ if (numericResult !== undefined)
67
+ return numericResult;
68
+ const booleanResult = compareBoolean(expected, actualValue);
69
+ if (booleanResult !== undefined)
70
+ return booleanResult;
71
+ return false;
72
+ }
73
+ /**
74
+ * Match against a regex pattern
75
+ * IDS uses XSD regex syntax which is slightly different from JavaScript
76
+ */
77
+ function matchPattern(constraint, actualValue, caseInsensitive = false) {
78
+ // An XSD `xs:pattern` facet constrains the *lexical* space of its base
79
+ // datatype, so it matches the textual representation of the value — the
80
+ // official IDS reference (ifctester) does `re.fullmatch(pattern,
81
+ // str(value))`. A numeric value therefore satisfies e.g.
82
+ // `<restriction base="xs:decimal"><pattern value="^.*$"/>` ("any
83
+ // decimal value present"), which the previous blanket number/boolean
84
+ // bail-out wrongly failed on every numeric property.
85
+ //
86
+ // But the runtime value must first be type-compatible with the declared
87
+ // base: a number only matches a numeric base and a boolean only a
88
+ // boolean base. A number tested against `base="xs:string"` is a type
89
+ // mismatch — buildingSMART's corpus encodes exactly this as
90
+ // `patterns_always_fail_on_any_number`. (A string actual is already the
91
+ // lexical form, so it is matched directly regardless of base.)
92
+ if (typeof actualValue === 'number') {
93
+ if (!isNumericXsdBase(constraint.base))
94
+ return false;
95
+ }
96
+ else if (typeof actualValue === 'boolean') {
97
+ if (!isBooleanXsdBase(constraint.base))
98
+ return false;
99
+ }
100
+ const actualStr = String(actualValue);
101
+ const regex = compilePatternRegex(constraint, caseInsensitive);
102
+ // An un-compilable pattern can't match anything (e.g. an unbalanced
103
+ // `[`); treat it as a non-match rather than throwing.
104
+ return regex ? regex.test(actualStr) : false;
105
+ }
106
+ /**
107
+ * Compiled-pattern cache. Translating XSD → JS regex and building the
108
+ * `RegExp` on every value check dominated pattern-heavy validation; the
109
+ * compiled form depends only on (constraint, caseInsensitive), and the
110
+ * constraint object is stable per parsed document.
111
+ */
112
+ const PATTERN_REGEX_CACHE = new WeakMap();
113
+ function compilePatternRegex(constraint, caseInsensitive) {
114
+ let entry = PATTERN_REGEX_CACHE.get(constraint);
115
+ if (!entry) {
116
+ entry = {};
117
+ PATTERN_REGEX_CACHE.set(constraint, entry);
118
+ }
119
+ const slot = caseInsensitive ? 'ci' : 'cs';
120
+ let regex = entry[slot];
121
+ if (regex === undefined) {
122
+ regex = buildPatternRegex(constraint.pattern, caseInsensitive);
123
+ entry[slot] = regex;
124
+ }
125
+ return regex;
126
+ }
127
+ function buildPatternRegex(xsdPattern, caseInsensitive) {
128
+ // XSD char-class subtraction `[a-z-[aeiou]]` has no JS equivalent;
129
+ // approximate as the positive class (drop the exclusion) so the rest
130
+ // of the pattern still evaluates, matching long-standing behaviour.
131
+ const desubtracted = xsdPattern.replace(/\[([^\]]+)-\[[^\]]+\]\]/g, '[$1]');
132
+ // Shared XSD → JS translation: `\i`/`\c`/`\d`/`\w` (and their
133
+ // negations) map to Unicode property escapes, and verbatim `\p{…}`
134
+ // classes pass through — both require the `u` flag for full fidelity.
135
+ const { pattern } = translateXsdRegex(desubtracted);
136
+ // IDS patterns must match the entire lexical value. Wrapping in a
137
+ // non-capturing group anchors top-level alternation correctly
138
+ // (`a|b` → `^(?:a|b)$`, not `^a|b$`). Case-insensitive matching is
139
+ // opt-in per the call site (entity / predefined-type names use it;
140
+ // property and attribute values do not).
141
+ const anchored = `^(?:${pattern})$`;
142
+ try {
143
+ return new RegExp(anchored, caseInsensitive ? 'iu' : 'u');
144
+ }
145
+ catch {
146
+ // Some patterns are valid under JS's lenient (Annex-B) dialect but
147
+ // rejected under `u`. Retry without it so plain patterns keep
148
+ // matching; this loses `\p{…}` fidelity for that one pattern only.
149
+ try {
150
+ return new RegExp(anchored, caseInsensitive ? 'i' : '');
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ }
157
+ /**
158
+ * Compiled exact-match sets per enumeration constraint. Real-world IDS
159
+ * code lists carry hundreds of values and are matched against every
160
+ * candidate entity, so the linear comparator walk dominated validation
161
+ * time. Constraint objects are stable per parsed document, making a
162
+ * WeakMap cache safe.
163
+ */
164
+ const ENUM_VALUE_SETS = new WeakMap();
165
+ function getEnumValueSets(constraint) {
166
+ let sets = ENUM_VALUE_SETS.get(constraint);
167
+ if (!sets) {
168
+ const exact = new Set(constraint.values);
169
+ const upper = new Set();
170
+ let anyCoercible = false;
171
+ for (const v of constraint.values) {
172
+ upper.add(v.toUpperCase());
173
+ if (isStrictNumericLiteral(v) || isBooleanLiteral(v))
174
+ anyCoercible = true;
175
+ }
176
+ sets = { exact, upper, anyCoercible };
177
+ ENUM_VALUE_SETS.set(constraint, sets);
178
+ }
179
+ return sets;
180
+ }
181
+ /**
182
+ * Match against an enumeration. The actual value matches if ANY of the
183
+ * declared options matches under string / numeric / boolean comparison
184
+ * — same strategy table as `matchSimpleValue`, just iterated.
185
+ */
186
+ function matchEnumeration(constraint, actualValue, caseInsensitive) {
187
+ // O(1) fast path: a set hit is exactly the condition under which
188
+ // `compareString` would have returned true for some value, so this
189
+ // never changes the outcome — misses fall through to the full
190
+ // comparator walk for numeric / boolean semantics.
191
+ const sets = getEnumValueSets(constraint);
192
+ const actualStr = String(actualValue);
193
+ if (sets.exact.has(actualStr))
194
+ return true;
195
+ if (caseInsensitive && sets.upper.has(actualStr.toUpperCase()))
196
+ return true;
197
+ // Pure-string enumerations are fully decided by the set lookups —
198
+ // only numeric/boolean literals can still match in the slow walk.
199
+ if (!sets.anyCoercible)
200
+ return false;
201
+ return constraint.values.some((v) => {
202
+ const stringResult = compareString(v, actualValue, caseInsensitive);
203
+ if (stringResult !== undefined)
204
+ return stringResult;
205
+ const numericResult = compareNumeric(v, actualValue);
206
+ if (numericResult !== undefined)
207
+ return numericResult;
208
+ const booleanResult = compareBoolean(v, actualValue);
209
+ if (booleanResult !== undefined)
210
+ return booleanResult;
211
+ return false;
212
+ });
213
+ }
214
+ /**
215
+ * Match against numeric bounds
216
+ */
217
+ function matchBounds(constraint, actualValue) {
218
+ // String-length facets (xs:length / xs:minLength / xs:maxLength)
219
+ // operate on the textual length, not on numeric magnitude. When any
220
+ // of them are present, evaluate the length constraints first.
221
+ if (constraint.length !== undefined ||
222
+ constraint.minLength !== undefined ||
223
+ constraint.maxLength !== undefined) {
224
+ const str = String(actualValue);
225
+ if (constraint.length !== undefined && str.length !== constraint.length) {
226
+ return false;
227
+ }
228
+ if (constraint.minLength !== undefined && str.length < constraint.minLength) {
229
+ return false;
230
+ }
231
+ if (constraint.maxLength !== undefined && str.length > constraint.maxLength) {
232
+ return false;
233
+ }
234
+ // Length-only restrictions don't impose numeric bounds; if the
235
+ // constraint also carries min/max we fall through to the numeric
236
+ // check below (rare in practice).
237
+ if (constraint.minInclusive === undefined &&
238
+ constraint.maxInclusive === undefined &&
239
+ constraint.minExclusive === undefined &&
240
+ constraint.maxExclusive === undefined) {
241
+ return true;
242
+ }
243
+ }
244
+ const num = typeof actualValue === 'number'
245
+ ? actualValue
246
+ : parseFloat(String(actualValue));
247
+ if (isNaN(num))
248
+ return false;
249
+ if (constraint.minInclusive !== undefined &&
250
+ num < constraint.minInclusive) {
251
+ return false;
252
+ }
253
+ if (constraint.maxInclusive !== undefined &&
254
+ num > constraint.maxInclusive) {
255
+ return false;
256
+ }
257
+ if (constraint.minExclusive !== undefined && num <= constraint.minExclusive) {
258
+ return false;
259
+ }
260
+ if (constraint.maxExclusive !== undefined && num >= constraint.maxExclusive) {
261
+ return false;
262
+ }
263
+ return true;
264
+ }
265
+ //# sourceMappingURL=match-family.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match-family.js","sourceRoot":"","sources":["../../src/constraints/match-family.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,13 @@
1
+ /**
2
+ * The specials upstream's xs:double pattern accepts, spelled exactly as it
3
+ * spells them:
4
+ *
5
+ * ^([-+]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?|NaN|\+INF|-INF)$
6
+ *
7
+ * Bare `INF` is absent because that pattern has `\+INF`, not `\+?INF`; and
8
+ * `Infinity` because it appears in neither upstream nor XSD.
9
+ */
10
+ export declare const XSD_NUMERIC_SPECIALS: Set<string>;
1
11
  /**
2
12
  * Returns true iff the IDS literal `value` casts successfully under at
3
13
  * least one of `xsdTypes`. Empty / undefined `xsdTypes` returns `true`
@@ -10,5 +20,14 @@ export declare function literalCastsUnder(value: string, xsdType: string): boole
10
20
  export declare function isNumericXsdBase(base: string | undefined): boolean;
11
21
  /** True iff the restriction `@base` declares the boolean value space. */
12
22
  export declare function isBooleanXsdBase(base: string | undefined): boolean;
13
- export declare function ifcMeasureToXsdTypes(measure: string | undefined): readonly string[];
23
+ /**
24
+ * `IfcTimeStamp`'s XSD types are the one answer here that depends on the
25
+ * schema version, so the mapper takes it rather than returning a union that
26
+ * is wrong somewhere. See the `IFCTIMESTAMP` branch below.
27
+ *
28
+ * Callers that genuinely have no version pass `undefined` and get the union
29
+ * across versions — permissive, which for a cast GATE means it defers rather
30
+ * than rejecting a value some schema allows.
31
+ */
32
+ export declare function ifcMeasureToXsdTypes(measure: string | undefined, schemaVersion?: string | undefined): readonly string[];
14
33
  //# sourceMappingURL=xsd-cast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"xsd-cast.d.ts","sourceRoot":"","sources":["../../src/constraints/xsd-cast.ts"],"names":[],"mappings":"AAuBA;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACtC,OAAO,CAGT;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAyBzE;AA4CD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAElE;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAElE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,CAuBnF"}
1
+ {"version":3,"file":"xsd-cast.d.ts","sourceRoot":"","sources":["../../src/constraints/xsd-cast.ts"],"names":[],"mappings":"AAsBA;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAAmC,CAAC;AASrE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACtC,OAAO,CAGT;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkCzE;AA4CD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAElE;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAElE;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,GACjC,SAAS,MAAM,EAAE,CAkDnB"}
@@ -4,13 +4,29 @@
4
4
  /**
5
5
  * XSD strict-cast helpers shared by the attribute and property facets.
6
6
  *
7
- * Mirrors the `int.TryParse` / `double.TryParse` / `DateTime.TryParse`
8
- * rules upstream `IDS-Audit-tool` applies before doing the value
9
- * comparison. An IDS literal must cast successfully under at least one
7
+ * Mirrors what upstream `IDS-Audit-tool` accepts before doing the value
8
+ * comparison.
9
+ *
10
+ * NOT `TryParse`, which this said until #3336: upstream decides these with
11
+ * GENERATED REGEXES (`ids-lib.codegen/XmlSchema_XsTypesGenerator.cs`), and its
12
+ * xs:double pattern is neither .NET nor XSD. It takes `+INF` (an XSD 1.1
13
+ * spelling) while rejecting bare `INF` (the 1.0 one), and rejects `Infinity`
14
+ * (the .NET one). Parity with upstream is the contract, so that is what these
15
+ * arms implement. An IDS literal must cast successfully under at least one
10
16
  * of the slot's declared XSD types — `xs:integer` rejects `42.0`,
11
17
  * `xs:double` accepts either, etc.
12
18
  */
13
19
  import { isWhollyNumeric } from '@ifc-lite/encoding';
20
+ /**
21
+ * The specials upstream's xs:double pattern accepts, spelled exactly as it
22
+ * spells them:
23
+ *
24
+ * ^([-+]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?|NaN|\+INF|-INF)$
25
+ *
26
+ * Bare `INF` is absent because that pattern has `\+INF`, not `\+?INF`; and
27
+ * `Infinity` because it appears in neither upstream nor XSD.
28
+ */
29
+ export const XSD_NUMERIC_SPECIALS = new Set(['NaN', '+INF', '-INF']);
14
30
  const INTEGER_RE = /^[+-]?\d+$/;
15
31
  const DATE_RE = /^\d{4}-\d{2}-\d{2}(Z|[+-]\d{2}:\d{2})?$/;
16
32
  const DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/;
@@ -31,12 +47,21 @@ export function literalCastsUnder(value, xsdType) {
31
47
  case 'xs:integer':
32
48
  return INTEGER_RE.test(value);
33
49
  case 'xs:double':
34
- // Same language the local `DOUBLE_RE` decided, via the shared
35
- // linear scan. That regex was
36
- // `/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/` — the #3113 shape,
37
- // quadratic on a failing match — and an IDS literal is as
50
+ // The finite part goes through the shared linear scan, not a regex: the
51
+ // natural pattern `/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/` is the
52
+ // #3113 shape, quadratic on a failing match, and an IDS literal is as
38
53
  // untrusted as the file it came out of.
39
- return isWhollyNumeric(value);
54
+ //
55
+ // Deliberate deviation from upstream (#3336). Every part of upstream's
56
+ // pattern is optional, so it accepts a family of digitless and
57
+ // mantissa-less forms that are not numbers: "", "+", ".", "-", "+.",
58
+ // "-.", and the exponent-only class "e5" / "+e5" / ".e5". Those fall out
59
+ // of how the regex is written rather than being a contract, and an empty
60
+ // string is not a double, so the finite scan keeps rejecting them.
61
+ //
62
+ // NOT an exhaustive list -- the family is larger than the rows pinned in
63
+ // xsd-cast-specials.test.ts, which are representatives.
64
+ return isWhollyNumeric(value) || XSD_NUMERIC_SPECIALS.has(value);
40
65
  case 'xs:boolean':
41
66
  return value === 'true' || value === 'false';
42
67
  case 'xs:date':
@@ -101,10 +126,48 @@ export function isNumericXsdBase(base) {
101
126
  export function isBooleanXsdBase(base) {
102
127
  return baseLocalName(base) === 'boolean';
103
128
  }
104
- export function ifcMeasureToXsdTypes(measure) {
129
+ /**
130
+ * Measures whose EXPRESS base contradicts the `*MEASURE` / `*RATIO`
131
+ * suffix heuristic below, or that the heuristic does not reach at all.
132
+ * Derived from `TYPE <name> = <base>;` in
133
+ * `packages/codegen/schemas/IFC4_ADD2_TC1.exp` and `IFC4X3.exp`, over
134
+ * the closure of the `IfcValue` SELECT (the value space an IFC property
135
+ * can actually carry); `xsd-cast-express.test.ts` re-derives that diff
136
+ * and fails if this table drifts from the schemas.
137
+ *
138
+ * - `IfcDescriptiveMeasure` is `STRING`, not a number, despite the name.
139
+ * - `IfcIntegerCountRateMeasure` is `INTEGER`, not `REAL`.
140
+ * - `IfcParameterValue` (`REAL`) and `IfcPositiveInteger` (`INTEGER`)
141
+ * end in neither suffix, so they previously got no cast gate at all.
142
+ * - `IfcTime` and `IfcUriReference` are `STRING`. `xs:string` accepts
143
+ * every literal, so naming them changes no verdict; they are listed
144
+ * so the re-derivation covers the whole reachable value space rather
145
+ * than carrying a second exception list of its own.
146
+ */
147
+ const MEASURE_XSD_OVERRIDES = new Map([
148
+ ['IFCDESCRIPTIVEMEASURE', ['xs:string']],
149
+ ['IFCINTEGERCOUNTRATEMEASURE', ['xs:integer']],
150
+ ['IFCPARAMETERVALUE', ['xs:double']],
151
+ ['IFCPOSITIVEINTEGER', ['xs:integer']],
152
+ ['IFCTIME', ['xs:string']],
153
+ ['IFCURIREFERENCE', ['xs:string']],
154
+ ]);
155
+ /**
156
+ * `IfcTimeStamp`'s XSD types are the one answer here that depends on the
157
+ * schema version, so the mapper takes it rather than returning a union that
158
+ * is wrong somewhere. See the `IFCTIMESTAMP` branch below.
159
+ *
160
+ * Callers that genuinely have no version pass `undefined` and get the union
161
+ * across versions — permissive, which for a cast GATE means it defers rather
162
+ * than rejecting a value some schema allows.
163
+ */
164
+ export function ifcMeasureToXsdTypes(measure, schemaVersion) {
105
165
  if (!measure)
106
166
  return [];
107
167
  const m = measure.toUpperCase();
168
+ const override = MEASURE_XSD_OVERRIDES.get(m);
169
+ if (override)
170
+ return override;
108
171
  if (m === 'IFCINTEGER' || m === 'IFCCOUNTMEASURE')
109
172
  return ['xs:integer'];
110
173
  if (m === 'IFCBOOLEAN')
@@ -115,8 +178,34 @@ export function ifcMeasureToXsdTypes(measure) {
115
178
  return ['xs:date'];
116
179
  if (m === 'IFCDATETIME')
117
180
  return ['xs:dateTime'];
118
- if (m === 'IFCDURATION' || m === 'IFCTIMESTAMP')
181
+ if (m === 'IFCDURATION')
119
182
  return ['xs:duration'];
183
+ // `TYPE IfcTimeStamp = INTEGER;` — a UNIX epoch second, not an ISO-8601
184
+ // duration. It was bundled onto the `IFCDURATION` row above on the strength
185
+ // of the name, which made this gate reject every value a timestamp property
186
+ // can legally hold. The generated `xsdTypesByEntity` table — the SAME
187
+ // question, answered from upstream `SchemaInfo.Attributes.g.cs`, and what
188
+ // the attribute facet gates on — answers PER SCHEMA VERSION, and it splits:
189
+ // `IfcOwnerHistory.CreationDate` carries `["xs:integer"]` under IFC2X3 and
190
+ // `["xs:dateTime","xs:integer"]` under IFC4 and IFC4X3.
191
+ //
192
+ // So there is no single correct answer to give, and taking the union ACROSS
193
+ // versions would recreate the disagreement in the other direction: under
194
+ // IFC2X3 an ISO-8601 date-time literal would pass this gate and be rejected
195
+ // by the attribute facet, on the same file. Swapping a total false-REJECT
196
+ // for a narrower false-ACCEPT is not a fix for "the two gates must agree",
197
+ // so answer per version instead and let the caller supply it.
198
+ //
199
+ // With no version the union is returned: a caller that cannot say which
200
+ // schema it is reading gets the permissive answer, which for a gate means
201
+ // deferring rather than rejecting a value some schema allows.
202
+ if (m === 'IFCTIMESTAMP') {
203
+ if (schemaVersion === undefined)
204
+ return ['xs:integer', 'xs:dateTime'];
205
+ return schemaVersion.toUpperCase() === 'IFC2X3'
206
+ ? ['xs:integer']
207
+ : ['xs:integer', 'xs:dateTime'];
208
+ }
120
209
  // All numeric measures (REAL, *MEASURE, *RATIO) accept doubles.
121
210
  if (m === 'IFCREAL' || m.endsWith('MEASURE') || m.endsWith('RATIO')) {
122
211
  return ['xs:double'];
@@ -1 +1 @@
1
- {"version":3,"file":"xsd-cast.js","sourceRoot":"","sources":["../../src/constraints/xsd-cast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"xsd-cast.js","sourceRoot":"","sources":["../../src/constraints/xsd-cast.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"property-facet.d.ts","sourceRoot":"","sources":["../../src/facets/property-facet.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAEhB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAsGnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAoBT;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,GACxB,gBAAgB,CAmGlB"}
1
+ {"version":3,"file":"property-facet.d.ts","sourceRoot":"","sources":["../../src/facets/property-facet.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAEhB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAyGnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAqBT;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,GACxB,gBAAgB,CAmGlB"}
@@ -45,16 +45,17 @@ function qualifiedPropertyNames(pset) {
45
45
  * check; the outcome depends only on (literal, measure name).
46
46
  */
47
47
  const CAST_GATE_CACHE = new WeakMap();
48
- function passesCastGate(value, dataType) {
48
+ function passesCastGate(value, dataType, schemaVersion) {
49
49
  let byType = CAST_GATE_CACHE.get(value);
50
50
  if (!byType) {
51
51
  byType = new Map();
52
52
  CAST_GATE_CACHE.set(value, byType);
53
53
  }
54
- const key = dataType ?? '';
54
+ // `IFCTIMESTAMP` casts differently under IFC2X3, so the version is in the key.
55
+ const key = `${dataType ?? ''}|${schemaVersion ?? ''}`;
55
56
  let passes = byType.get(key);
56
57
  if (passes === undefined) {
57
- const xsdTypes = ifcMeasureToXsdTypes(dataType);
58
+ const xsdTypes = ifcMeasureToXsdTypes(dataType, schemaVersion);
58
59
  passes = xsdTypes.length === 0 || literalCastsUnderAnyType(value.value, xsdTypes);
59
60
  byType.set(key, passes);
60
61
  }
@@ -64,7 +65,7 @@ function passesCastGate(value, dataType) {
64
65
  * Diagnostics-free verdict for a single property — the exact `passed`
65
66
  * boolean `checkSingleProperty` would compute.
66
67
  */
67
- function singlePropertyPasses(facet, prop) {
68
+ function singlePropertyPasses(facet, prop, schemaVersion) {
68
69
  // "No value" fails any check, including existence-only ones.
69
70
  if (prop.value === null || prop.value === undefined || prop.value === '') {
70
71
  return false;
@@ -75,8 +76,9 @@ function singlePropertyPasses(facet, prop) {
75
76
  }
76
77
  }
77
78
  if (facet.value) {
78
- if (facet.value.type === 'simpleValue' && !passesCastGate(facet.value, prop.dataType)) {
79
- return false;
79
+ if (facet.value.type === 'simpleValue') {
80
+ if (!passesCastGate(facet.value, prop.dataType, schemaVersion))
81
+ return false;
80
82
  }
81
83
  const candidateValues = prop.values && prop.values.length > 0 ? prop.values : [prop.value];
82
84
  return candidateValues.some((v) => matchConstraint(facet.value, v));
@@ -101,6 +103,7 @@ export function propertyFacetPasses(facet, expressId, accessor) {
101
103
  const propertySets = accessor.getPropertySets(expressId);
102
104
  if (propertySets.length === 0)
103
105
  return false;
106
+ const schemaVersion = accessor.getSchemaVersion?.();
104
107
  let anyMatchingPset = false;
105
108
  for (const pset of propertySets) {
106
109
  if (!matchConstraint(facet.propertySet, pset.name))
@@ -111,7 +114,7 @@ export function propertyFacetPasses(facet, expressId, accessor) {
111
114
  if (!matchConstraint(facet.baseName, prop.name))
112
115
  continue;
113
116
  anyMatchingProp = true;
114
- if (!singlePropertyPasses(facet, prop))
117
+ if (!singlePropertyPasses(facet, prop, schemaVersion))
115
118
  return false;
116
119
  }
117
120
  if (!anyMatchingProp)
@@ -160,7 +163,7 @@ export function checkPropertyFacet(facet, expressId, accessor) {
160
163
  let lastPass;
161
164
  let firstFailure;
162
165
  for (const pset of matchingPsets) {
163
- const result = checkPropertyInPset(facet, pset);
166
+ const result = checkPropertyInPset(facet, pset, accessor.getSchemaVersion?.());
164
167
  if (result.passed) {
165
168
  lastPass = result;
166
169
  continue;
@@ -218,7 +221,7 @@ export function checkPropertyFacet(facet, expressId, accessor) {
218
221
  * Tries ALL matching properties and returns on first pass.
219
222
  * If none pass, returns the most specific failure.
220
223
  */
221
- function checkPropertyInPset(facet, pset) {
224
+ function checkPropertyInPset(facet, pset, schemaVersion) {
222
225
  // Find matching properties
223
226
  const matchingProps = pset.properties.filter((prop) => matchConstraint(facet.baseName, prop.name));
224
227
  if (matchingProps.length === 0) {
@@ -242,7 +245,7 @@ function checkPropertyInPset(facet, pset) {
242
245
  let lastPass;
243
246
  let firstFailure;
244
247
  for (const prop of matchingProps) {
245
- const result = checkSingleProperty(facet, pset, prop);
248
+ const result = checkSingleProperty(facet, pset, prop, schemaVersion);
246
249
  if (result.passed) {
247
250
  lastPass = result;
248
251
  continue;
@@ -263,7 +266,7 @@ function checkPropertyInPset(facet, pset) {
263
266
  /**
264
267
  * Check a single property against the facet's dataType and value constraints.
265
268
  */
266
- function checkSingleProperty(facet, pset, prop) {
269
+ function checkSingleProperty(facet, pset, prop, schemaVersion) {
267
270
  // Per IDS spec, a property whose stored value is "no value" (null,
268
271
  // undefined, empty string, or IfcLogical UNKNOWN) fails ANY check —
269
272
  // including a name-only existence check. Detect those up front so
@@ -326,28 +329,22 @@ function checkSingleProperty(facet, pset, prop) {
326
329
  },
327
330
  };
328
331
  }
329
- // Strict XSD-cast gate: the IDS literal MUST cast successfully
330
- // under at least one of the IFC measure's XSD types. Mirrors the
331
- // attribute facet's check an `IFCINTEGER` slot rejects `42.0`,
332
- // an `IFCBOOLEAN` slot rejects numeric literals, etc. The shared
333
- // `ifcMeasureToXsdTypes` mapping turns the parser-side measure
334
- // name into the XSD types the cast helper understands.
335
- if (facet.value.type === 'simpleValue') {
336
- const xsdTypes = ifcMeasureToXsdTypes(prop.dataType);
337
- if (xsdTypes.length > 0 &&
338
- !literalCastsUnderAnyType(facet.value.value, xsdTypes)) {
339
- return {
340
- passed: false,
341
- actualValue: String(propValue),
342
- expectedValue: formatConstraint(facet.value),
343
- failure: {
344
- type: 'PROPERTY_VALUE_MISMATCH',
345
- field: `${pset.name}.${prop.name}`,
346
- actual: String(propValue),
347
- expected: formatConstraint(facet.value),
348
- },
349
- };
350
- }
332
+ // Strict XSD-cast gate, mirroring the attribute facet. Shared with
333
+ // `singlePropertyPasses` rather than repeated: the two must return the
334
+ // same verdict, and a second copy is what lets them drift.
335
+ if (facet.value.type === 'simpleValue' &&
336
+ !passesCastGate(facet.value, prop.dataType, schemaVersion)) {
337
+ return {
338
+ passed: false,
339
+ actualValue: String(propValue),
340
+ expectedValue: formatConstraint(facet.value),
341
+ failure: {
342
+ type: 'PROPERTY_VALUE_MISMATCH',
343
+ field: `${pset.name}.${prop.name}`,
344
+ actual: String(propValue),
345
+ expected: formatConstraint(facet.value),
346
+ },
347
+ };
351
348
  }
352
349
  // Multi-valued IFC properties (IfcPropertyEnumeratedValue,
353
350
  // IfcPropertyListValue) pass if ANY individual value satisfies the