@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.
- package/dist/bridge/data-types.d.ts.map +1 -1
- package/dist/bridge/data-types.js +1 -0
- package/dist/bridge/data-types.js.map +1 -1
- package/dist/constraint-types.d.ts +110 -0
- package/dist/constraint-types.d.ts.map +1 -0
- package/dist/constraint-types.js +5 -0
- package/dist/constraint-types.js.map +1 -0
- package/dist/constraints/describe.d.ts +17 -0
- package/dist/constraints/describe.d.ts.map +1 -0
- package/dist/constraints/describe.js +140 -0
- package/dist/constraints/describe.js.map +1 -0
- package/dist/constraints/index.d.ts +1 -8
- package/dist/constraints/index.d.ts.map +1 -1
- package/dist/constraints/index.js +13 -361
- package/dist/constraints/index.js.map +1 -1
- package/dist/constraints/match-family.d.ts +20 -0
- package/dist/constraints/match-family.d.ts.map +1 -0
- package/dist/constraints/match-family.js +265 -0
- package/dist/constraints/match-family.js.map +1 -0
- package/dist/constraints/xsd-cast.d.ts +20 -1
- package/dist/constraints/xsd-cast.d.ts.map +1 -1
- package/dist/constraints/xsd-cast.js +99 -10
- package/dist/constraints/xsd-cast.js.map +1 -1
- package/dist/facets/property-facet.d.ts.map +1 -1
- package/dist/facets/property-facet.js +30 -33
- package/dist/facets/property-facet.js.map +1 -1
- package/dist/parser/dom.d.ts +11 -0
- package/dist/parser/dom.d.ts.map +1 -0
- package/dist/parser/dom.js +49 -0
- package/dist/parser/dom.js.map +1 -0
- package/dist/parser/xml-parser.d.ts.map +1 -1
- package/dist/parser/xml-parser.js +46 -58
- package/dist/parser/xml-parser.js.map +1 -1
- package/dist/translation/describe-constraint.d.ts +18 -0
- package/dist/translation/describe-constraint.d.ts.map +1 -0
- package/dist/translation/describe-constraint.js +77 -0
- package/dist/translation/describe-constraint.js.map +1 -0
- 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 +2 -0
- package/dist/translation/locales/en.d.ts.map +1 -1
- package/dist/translation/locales/en.js +2 -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/translation/service.d.ts +4 -5
- package/dist/translation/service.d.ts.map +1 -1
- package/dist/translation/service.js +8 -58
- package/dist/translation/service.js.map +1 -1
- package/dist/types.d.ts +2 -59
- package/dist/types.d.ts.map +1 -1
- 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
|
-
|
|
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":"
|
|
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
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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'
|
|
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":"AAAA;;+DAE+D;AAE/D
|
|
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;
|
|
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
|
-
|
|
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'
|
|
79
|
-
|
|
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
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|