@antglobal/copilot-cards-core 1.0.4 → 1.0.6
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/README.md +57 -0
- package/dist/index.cjs +350 -129
- package/dist/index.d.ts +33 -7
- package/dist/index.js +350 -130
- package/dist/validation-text.cjs +1298 -0
- package/dist/validation-text.d.ts +215 -0
- package/dist/validation-text.js +1293 -0
- package/package.json +7 -1
package/dist/index.cjs
CHANGED
|
@@ -203,87 +203,106 @@ function evaluateExpression(expr, context) {
|
|
|
203
203
|
return t;
|
|
204
204
|
}
|
|
205
205
|
// Precedence (low → high): ternary → || → && → equality → relational → unary → atom
|
|
206
|
-
function parseTernary() {
|
|
207
|
-
const
|
|
206
|
+
function parseTernary(mode) {
|
|
207
|
+
const condition = parseOr(mode);
|
|
208
208
|
if (peek()?.type === 'question') {
|
|
209
209
|
consume(); // ?
|
|
210
|
-
const
|
|
210
|
+
const truthyMode = mode === 'evaluate' && Boolean(condition)
|
|
211
|
+
? 'evaluate'
|
|
212
|
+
: 'consume';
|
|
213
|
+
const truthy = parseTernary(truthyMode);
|
|
211
214
|
expect('colon');
|
|
212
|
-
const
|
|
213
|
-
|
|
215
|
+
const falsyMode = mode === 'evaluate' && !condition
|
|
216
|
+
? 'evaluate'
|
|
217
|
+
: 'consume';
|
|
218
|
+
const falsy = parseTernary(falsyMode);
|
|
219
|
+
if (mode === 'consume')
|
|
220
|
+
return undefined;
|
|
221
|
+
return condition ? truthy : falsy;
|
|
214
222
|
}
|
|
215
|
-
return
|
|
223
|
+
return condition;
|
|
216
224
|
}
|
|
217
|
-
function parseOr() {
|
|
218
|
-
let left = parseAnd();
|
|
225
|
+
function parseOr(mode) {
|
|
226
|
+
let left = parseAnd(mode);
|
|
219
227
|
while (peek()?.value === '||') {
|
|
220
228
|
consume();
|
|
221
|
-
|
|
229
|
+
const rightMode = mode === 'evaluate' && !left ? 'evaluate' : 'consume';
|
|
230
|
+
const right = parseAnd(rightMode);
|
|
231
|
+
if (rightMode === 'evaluate')
|
|
232
|
+
left = right;
|
|
222
233
|
}
|
|
223
234
|
return left;
|
|
224
235
|
}
|
|
225
|
-
function parseAnd() {
|
|
226
|
-
let left = parseEquality();
|
|
236
|
+
function parseAnd(mode) {
|
|
237
|
+
let left = parseEquality(mode);
|
|
227
238
|
while (peek()?.value === '&&') {
|
|
228
239
|
consume();
|
|
229
|
-
|
|
240
|
+
const rightMode = mode === 'evaluate' && Boolean(left) ? 'evaluate' : 'consume';
|
|
241
|
+
const right = parseEquality(rightMode);
|
|
242
|
+
if (rightMode === 'evaluate')
|
|
243
|
+
left = right;
|
|
230
244
|
}
|
|
231
245
|
return left;
|
|
232
246
|
}
|
|
233
|
-
function parseEquality() {
|
|
234
|
-
let left = parseRelational();
|
|
247
|
+
function parseEquality(mode) {
|
|
248
|
+
let left = parseRelational(mode);
|
|
235
249
|
while (peek()?.value === '===' || peek()?.value === '!==' || peek()?.value === '==' || peek()?.value === '!=') {
|
|
236
250
|
const op = consume().value;
|
|
237
|
-
const right = parseRelational();
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
251
|
+
const right = parseRelational(mode);
|
|
252
|
+
if (mode === 'evaluate') {
|
|
253
|
+
if (op === '===' || op === '==')
|
|
254
|
+
left = left === right;
|
|
255
|
+
else
|
|
256
|
+
left = left !== right;
|
|
257
|
+
}
|
|
242
258
|
}
|
|
243
259
|
return left;
|
|
244
260
|
}
|
|
245
|
-
function parseRelational() {
|
|
246
|
-
let left = parseUnary();
|
|
261
|
+
function parseRelational(mode) {
|
|
262
|
+
let left = parseUnary(mode);
|
|
247
263
|
while (peek()?.value === '>' || peek()?.value === '<' || peek()?.value === '>=' || peek()?.value === '<=') {
|
|
248
264
|
const op = consume().value;
|
|
249
|
-
const right = parseUnary();
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
265
|
+
const right = parseUnary(mode);
|
|
266
|
+
if (mode === 'evaluate') {
|
|
267
|
+
if (op === '>')
|
|
268
|
+
left = left > right;
|
|
269
|
+
else if (op === '<')
|
|
270
|
+
left = left < right;
|
|
271
|
+
else if (op === '>=')
|
|
272
|
+
left = left >= right;
|
|
273
|
+
else
|
|
274
|
+
left = left <= right;
|
|
275
|
+
}
|
|
258
276
|
}
|
|
259
277
|
return left;
|
|
260
278
|
}
|
|
261
|
-
function parseUnary() {
|
|
279
|
+
function parseUnary(mode) {
|
|
262
280
|
if (peek()?.value === '!') {
|
|
263
281
|
consume();
|
|
264
|
-
|
|
282
|
+
const value = parseUnary(mode);
|
|
283
|
+
return mode === 'evaluate' ? !value : undefined;
|
|
265
284
|
}
|
|
266
|
-
return parseAtom();
|
|
285
|
+
return parseAtom(mode);
|
|
267
286
|
}
|
|
268
|
-
function parseAtom() {
|
|
287
|
+
function parseAtom(mode) {
|
|
269
288
|
const t = peek();
|
|
270
289
|
if (!t)
|
|
271
290
|
return undefined;
|
|
272
291
|
if (t.type === 'string') {
|
|
273
292
|
consume();
|
|
274
|
-
return t.value;
|
|
293
|
+
return mode === 'evaluate' ? t.value : undefined;
|
|
275
294
|
}
|
|
276
295
|
if (t.type === 'number') {
|
|
277
296
|
consume();
|
|
278
|
-
return Number(t.value);
|
|
297
|
+
return mode === 'evaluate' ? Number(t.value) : undefined;
|
|
279
298
|
}
|
|
280
299
|
if (t.type === 'boolean') {
|
|
281
300
|
consume();
|
|
282
|
-
return t.value === 'true';
|
|
301
|
+
return mode === 'evaluate' ? t.value === 'true' : undefined;
|
|
283
302
|
}
|
|
284
303
|
if (t.type === 'null') {
|
|
285
304
|
consume();
|
|
286
|
-
return null;
|
|
305
|
+
return mode === 'evaluate' ? null : undefined;
|
|
287
306
|
}
|
|
288
307
|
if (t.type === 'undefined') {
|
|
289
308
|
consume();
|
|
@@ -291,19 +310,19 @@ function evaluateExpression(expr, context) {
|
|
|
291
310
|
}
|
|
292
311
|
if (t.type === 'ident') {
|
|
293
312
|
consume();
|
|
294
|
-
return getByPath$1(context, t.value);
|
|
313
|
+
return mode === 'evaluate' ? getByPath$1(context, t.value) : undefined;
|
|
295
314
|
}
|
|
296
315
|
if (t.type === 'paren' && t.value === '(') {
|
|
297
316
|
consume();
|
|
298
|
-
const
|
|
317
|
+
const value = parseTernary(mode);
|
|
299
318
|
expect('paren', ')');
|
|
300
|
-
return
|
|
319
|
+
return value;
|
|
301
320
|
}
|
|
302
321
|
// Fallback
|
|
303
322
|
consume();
|
|
304
323
|
return undefined;
|
|
305
324
|
}
|
|
306
|
-
const result = parseTernary();
|
|
325
|
+
const result = parseTernary('evaluate');
|
|
307
326
|
return result;
|
|
308
327
|
}
|
|
309
328
|
/**
|
|
@@ -388,7 +407,7 @@ async function parseResponseBody(response) {
|
|
|
388
407
|
* @param silent - If true, only write to ctx.variables without calling
|
|
389
408
|
* setVariable (avoids triggering re-render during polling iterations).
|
|
390
409
|
* When variableWriter is present it owns the write and must synchronously
|
|
391
|
-
* update the
|
|
410
|
+
* update the renderer-owned variable view while honoring this flag.
|
|
392
411
|
*/
|
|
393
412
|
function writeResponseVariable(ctx, responseKey, data, silent = false) {
|
|
394
413
|
if (ctx.variableWriter) {
|
|
@@ -906,7 +925,7 @@ function convertTextContent(content) {
|
|
|
906
925
|
* ```
|
|
907
926
|
*/
|
|
908
927
|
function convertButtonContent(content) {
|
|
909
|
-
const { label, text, url, actionType, style, ...rest } = content;
|
|
928
|
+
const { label, text, url, actionType: _actionType, style, ...rest } = content;
|
|
910
929
|
const props = { ...rest };
|
|
911
930
|
// Button display text
|
|
912
931
|
const displayText = label ?? text;
|
|
@@ -1846,97 +1865,215 @@ function parseSchema(input) {
|
|
|
1846
1865
|
}
|
|
1847
1866
|
return buildNode(rootID);
|
|
1848
1867
|
}
|
|
1849
|
-
|
|
1868
|
+
function encodePointerSegment(segment) {
|
|
1869
|
+
return String(segment).replace(/~/g, '~0').replace(/\//g, '~1');
|
|
1870
|
+
}
|
|
1871
|
+
function schemaPath(...segments) {
|
|
1872
|
+
return segments.length > 0
|
|
1873
|
+
? `/${segments.map(encodePointerSegment).join('/')}`
|
|
1874
|
+
: '';
|
|
1875
|
+
}
|
|
1876
|
+
function elementPath(id, ...segments) {
|
|
1877
|
+
return schemaPath('elements', id, ...segments);
|
|
1878
|
+
}
|
|
1879
|
+
function slotPath(id, slotName, ...segments) {
|
|
1880
|
+
return elementPath(id, 'props', 'slots', slotName, ...segments);
|
|
1881
|
+
}
|
|
1882
|
+
function addIssue(issues, code, path, message, params, anchorPath) {
|
|
1883
|
+
issues.push({
|
|
1884
|
+
code,
|
|
1885
|
+
path,
|
|
1886
|
+
...(anchorPath === undefined ? {} : { anchorPath }),
|
|
1887
|
+
message,
|
|
1888
|
+
...(params === undefined ? {} : { params }),
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
function displayValue(value) {
|
|
1892
|
+
try {
|
|
1893
|
+
return String(value);
|
|
1894
|
+
}
|
|
1895
|
+
catch {
|
|
1896
|
+
return '<unprintable>';
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
function displayError(error) {
|
|
1900
|
+
const message = readOwnData(error, 'message');
|
|
1901
|
+
return displayValue(message.kind === 'value' ? message.value : error);
|
|
1902
|
+
}
|
|
1850
1903
|
/**
|
|
1851
|
-
* Validate
|
|
1904
|
+
* Validate an unknown schema input and return structured diagnostics.
|
|
1905
|
+
*
|
|
1906
|
+
* Component names and component-specific props are deliberately not checked;
|
|
1907
|
+
* this validator only owns the shared card graph and binding protocol.
|
|
1852
1908
|
*/
|
|
1853
|
-
function
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1909
|
+
function validateSchemaDetailed(input) {
|
|
1910
|
+
const issues = [];
|
|
1911
|
+
if (!isPlainObject(input)) {
|
|
1912
|
+
addIssue(issues, 'SCHEMA_TYPE_MISMATCH', '', 'Schema must be a non-null plain object');
|
|
1913
|
+
return issues;
|
|
1914
|
+
}
|
|
1915
|
+
let schema;
|
|
1916
|
+
try {
|
|
1917
|
+
schema = normalizeSchema(input);
|
|
1858
1918
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1919
|
+
catch (error) {
|
|
1920
|
+
addIssue(issues, 'LEGACY_CONVERSION_FAILED', '', `Legacy schema conversion failed: ${displayError(error)}`);
|
|
1921
|
+
return issues;
|
|
1861
1922
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
1923
|
+
const version = readOwnData(schema, 'version');
|
|
1924
|
+
if (version.kind !== 'value' || !version.value) {
|
|
1925
|
+
addIssue(issues, 'SCHEMA_REQUIRED_FIELD', schemaPath('version'), 'Missing "version" field', undefined, '');
|
|
1864
1926
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1927
|
+
const rootID = readOwnData(schema, 'rootID');
|
|
1928
|
+
if (rootID.kind !== 'value' || typeof rootID.value !== 'string') {
|
|
1929
|
+
addIssue(issues, 'ROOT_TYPE_MISMATCH', schemaPath('rootID'), '"rootID" field must be a string', undefined, rootID.kind === 'missing' ? '' : undefined);
|
|
1867
1930
|
}
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1931
|
+
else if (!rootID.value) {
|
|
1932
|
+
addIssue(issues, 'SCHEMA_REQUIRED_FIELD', schemaPath('rootID'), 'Missing "rootID" field');
|
|
1933
|
+
}
|
|
1934
|
+
const elementsRead = readOwnData(schema, 'elements');
|
|
1935
|
+
if (elementsRead.kind === 'missing') {
|
|
1936
|
+
addIssue(issues, 'SCHEMA_REQUIRED_FIELD', schemaPath('elements'), 'Missing "elements" field', undefined, '');
|
|
1937
|
+
return issues;
|
|
1938
|
+
}
|
|
1939
|
+
const rawElements = elementsRead.kind === 'value'
|
|
1940
|
+
? elementsRead.value
|
|
1941
|
+
: undefined;
|
|
1942
|
+
if (!isPlainObject(rawElements)) {
|
|
1943
|
+
addIssue(issues, 'SCHEMA_TYPE_MISMATCH', schemaPath('elements'), '"elements" field must be an object');
|
|
1944
|
+
return issues;
|
|
1945
|
+
}
|
|
1946
|
+
const elements = rawElements;
|
|
1947
|
+
const elementEntries = ownStringDataEntries(elements);
|
|
1948
|
+
for (const id of elementEntries.accessors) {
|
|
1949
|
+
addIssue(issues, 'ELEMENT_ACCESSOR_NOT_ALLOWED', elementPath(id), `Element "${id}" must be an own data property`, { elementId: id });
|
|
1950
|
+
}
|
|
1951
|
+
if (elementEntries.opaque) {
|
|
1952
|
+
addIssue(issues, 'ELEMENTS_UNINSPECTABLE', schemaPath('elements'), 'The "elements" field could not be inspected safely');
|
|
1953
|
+
return issues;
|
|
1954
|
+
}
|
|
1955
|
+
const allIds = new Set([
|
|
1956
|
+
...elementEntries.entries.map(([id]) => id),
|
|
1957
|
+
...elementEntries.accessors,
|
|
1958
|
+
]);
|
|
1959
|
+
const validationElements = Object.create(null);
|
|
1960
|
+
for (const [id, element] of elementEntries.entries) {
|
|
1961
|
+
Object.defineProperty(validationElements, id, {
|
|
1962
|
+
configurable: true,
|
|
1963
|
+
enumerable: true,
|
|
1964
|
+
value: element,
|
|
1965
|
+
writable: true,
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
const validationSchema = {
|
|
1969
|
+
rootID: rootID.kind === 'value' && typeof rootID.value === 'string'
|
|
1970
|
+
? rootID.value
|
|
1971
|
+
: '',
|
|
1972
|
+
elements: validationElements};
|
|
1973
|
+
if (rootID.kind === 'value'
|
|
1974
|
+
&& typeof rootID.value === 'string'
|
|
1975
|
+
&& rootID.value
|
|
1976
|
+
&& !allIds.has(rootID.value)) {
|
|
1977
|
+
addIssue(issues, 'ROOT_REFERENCE_NOT_FOUND', schemaPath('rootID'), `Root element "${rootID.value}" not found in elements`, { rootID: rootID.value });
|
|
1978
|
+
}
|
|
1979
|
+
for (const [id, rawElement] of elementEntries.entries) {
|
|
1980
|
+
if (!isPlainObject(rawElement)) {
|
|
1981
|
+
addIssue(issues, 'ELEMENT_TYPE_MISMATCH', elementPath(id), `Element "${id}" must be an object`, { elementId: id });
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
const element = rawElement;
|
|
1985
|
+
const type = readOwnData(element, 'type');
|
|
1986
|
+
if (type.kind !== 'value' || !type.value) {
|
|
1987
|
+
addIssue(issues, 'ELEMENT_TYPE_MISSING', elementPath(id, 'type'), `Element "${id}" is missing a "type" field`, { elementId: id }, type.kind === 'missing' ? elementPath(id) : undefined);
|
|
1988
|
+
}
|
|
1989
|
+
const props = readOwnData(element, 'props');
|
|
1990
|
+
if (props.kind !== 'value' || !isPlainObject(props.value)) {
|
|
1991
|
+
addIssue(issues, 'ELEMENT_PROPS_TYPE_MISMATCH', elementPath(id, 'props'), `Element "${id}" "props" field must be an object`, { elementId: id }, props.kind === 'missing' ? elementPath(id) : undefined);
|
|
1992
|
+
continue;
|
|
1872
1993
|
}
|
|
1873
1994
|
// Validate slot children and groups references without invoking accessors.
|
|
1874
|
-
const
|
|
1995
|
+
const rawSlots = readOwnData(props.value, 'slots');
|
|
1996
|
+
if (rawSlots.kind === 'accessor' || rawSlots.kind === 'opaque') {
|
|
1997
|
+
addIssue(issues, 'SLOTS_UNINSPECTABLE', elementPath(id, 'props', 'slots'), `Element "${id}" slots could not be inspected safely`, { elementId: id });
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
if (rawSlots.kind === 'value' && !isPlainObject(rawSlots.value)) {
|
|
2001
|
+
addIssue(issues, 'SLOTS_TYPE_MISMATCH', elementPath(id, 'props', 'slots'), `Element "${id}" "slots" field must be an object`, { elementId: id });
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
const slots = rawSlots.kind === 'value'
|
|
2005
|
+
? ownStringDataEntries(rawSlots.value)
|
|
2006
|
+
: ownStringDataEntries(undefined);
|
|
1875
2007
|
for (const slotName of slots.accessors) {
|
|
1876
|
-
|
|
2008
|
+
addIssue(issues, 'SLOT_ACCESSOR_NOT_ALLOWED', slotPath(id, slotName), `Element "${id}" slot "${slotName}" must be an own data property`, { elementId: id, slotName });
|
|
1877
2009
|
}
|
|
1878
2010
|
if (slots.opaque) {
|
|
1879
|
-
|
|
2011
|
+
addIssue(issues, 'SLOTS_UNINSPECTABLE', elementPath(id, 'props', 'slots'), `Element "${id}" slots could not be inspected safely`, { elementId: id });
|
|
1880
2012
|
}
|
|
1881
2013
|
for (const [slotName, slot] of slots.entries) {
|
|
1882
|
-
if (!slot
|
|
1883
|
-
|
|
2014
|
+
if (!isPlainObject(slot)) {
|
|
2015
|
+
addIssue(issues, 'SLOT_TYPE_MISMATCH', slotPath(id, slotName), `Element "${id}" slot "${slotName}" must be an object`, { elementId: id, slotName });
|
|
1884
2016
|
continue;
|
|
1885
2017
|
}
|
|
1886
2018
|
const children = readOwnData(slot, 'children');
|
|
1887
2019
|
if (children.kind === 'value') {
|
|
1888
2020
|
const values = ownArrayDataValues(children.value);
|
|
1889
2021
|
if (!values) {
|
|
1890
|
-
|
|
2022
|
+
addIssue(issues, 'SLOT_CHILDREN_TYPE_MISMATCH', slotPath(id, slotName, 'children'), `Element "${id}" slot "${slotName}" children must be an array`, { elementId: id, slotName });
|
|
1891
2023
|
}
|
|
1892
2024
|
else {
|
|
1893
|
-
for (const childId of values) {
|
|
2025
|
+
for (const [index, childId] of values.entries()) {
|
|
1894
2026
|
if (typeof childId !== 'string' || childId.length === 0) {
|
|
1895
|
-
|
|
2027
|
+
addIssue(issues, 'SLOT_CHILD_ID_INVALID', slotPath(id, slotName, 'children', index), `Element "${id}" slot "${slotName}" child IDs must be non-empty strings; received "${displayValue(childId)}"`, { elementId: id, slotName, value: childId });
|
|
1896
2028
|
}
|
|
1897
2029
|
else if (!allIds.has(childId)) {
|
|
1898
|
-
|
|
2030
|
+
addIssue(issues, 'ELEMENT_REFERENCE_NOT_FOUND', slotPath(id, slotName, 'children', index), `Element "${id}" slot "${slotName}" references unknown child "${displayValue(childId)}"`, { elementId: id, slotName, reference: childId });
|
|
1899
2031
|
}
|
|
1900
2032
|
}
|
|
1901
2033
|
}
|
|
1902
2034
|
}
|
|
1903
2035
|
else if (children.kind === 'accessor' || children.kind === 'opaque') {
|
|
1904
|
-
|
|
2036
|
+
addIssue(issues, 'SLOT_ACCESSOR_NOT_ALLOWED', slotPath(id, slotName, 'children'), `Element "${id}" slot "${slotName}" children must be an own data property`, { elementId: id, slotName, field: 'children' });
|
|
1905
2037
|
}
|
|
1906
2038
|
const groups = readOwnData(slot, 'groups');
|
|
1907
2039
|
if (groups.kind === 'value') {
|
|
1908
2040
|
const groupValues = ownArrayDataValues(groups.value);
|
|
1909
2041
|
if (!groupValues) {
|
|
1910
|
-
|
|
2042
|
+
addIssue(issues, 'SLOT_GROUPS_TYPE_MISMATCH', slotPath(id, slotName, 'groups'), `Element "${id}" slot "${slotName}" groups must be an array`, { elementId: id, slotName });
|
|
1911
2043
|
}
|
|
1912
2044
|
else {
|
|
1913
|
-
for (const group of groupValues) {
|
|
2045
|
+
for (const [groupIndex, group] of groupValues.entries()) {
|
|
1914
2046
|
const childrenInGroup = ownArrayDataValues(group);
|
|
1915
2047
|
if (!childrenInGroup) {
|
|
1916
|
-
|
|
2048
|
+
addIssue(issues, 'SLOT_GROUP_TYPE_MISMATCH', slotPath(id, slotName, 'groups', groupIndex), `Element "${id}" slot "${slotName}" groups must contain arrays`, { elementId: id, slotName });
|
|
1917
2049
|
continue;
|
|
1918
2050
|
}
|
|
1919
|
-
for (const childId of childrenInGroup) {
|
|
2051
|
+
for (const [childIndex, childId] of childrenInGroup.entries()) {
|
|
1920
2052
|
if (typeof childId !== 'string' || childId.length === 0) {
|
|
1921
|
-
|
|
2053
|
+
addIssue(issues, 'SLOT_GROUP_CHILD_ID_INVALID', slotPath(id, slotName, 'groups', groupIndex, childIndex), `Element "${id}" slot "${slotName}" group child IDs must be non-empty strings; received "${displayValue(childId)}"`, { elementId: id, slotName, value: childId });
|
|
1922
2054
|
}
|
|
1923
2055
|
else if (!allIds.has(childId)) {
|
|
1924
|
-
|
|
2056
|
+
addIssue(issues, 'ELEMENT_REFERENCE_NOT_FOUND', slotPath(id, slotName, 'groups', groupIndex, childIndex), `Element "${id}" slot "${slotName}" group references unknown child "${displayValue(childId)}"`, { elementId: id, slotName, reference: childId });
|
|
1925
2057
|
}
|
|
1926
2058
|
}
|
|
1927
2059
|
}
|
|
1928
2060
|
}
|
|
1929
2061
|
}
|
|
1930
2062
|
else if (groups.kind === 'accessor' || groups.kind === 'opaque') {
|
|
1931
|
-
|
|
2063
|
+
addIssue(issues, 'SLOT_ACCESSOR_NOT_ALLOWED', slotPath(id, slotName, 'groups'), `Element "${id}" slot "${slotName}" groups must be an own data property`, { elementId: id, slotName, field: 'groups' });
|
|
1932
2064
|
}
|
|
1933
2065
|
}
|
|
1934
2066
|
}
|
|
1935
|
-
validateRepeatBindings(
|
|
1936
|
-
return
|
|
2067
|
+
validateRepeatBindings(validationSchema, issues);
|
|
2068
|
+
return issues;
|
|
2069
|
+
}
|
|
2070
|
+
/** Validate a CardSchema and return backwards-compatible error messages. */
|
|
2071
|
+
function validateSchema(input) {
|
|
2072
|
+
return validateSchemaDetailed(input).map(issue => issue.message);
|
|
1937
2073
|
}
|
|
1938
2074
|
const REPEAT_SLOT_LAYOUTS = new Set([
|
|
1939
2075
|
'default',
|
|
2076
|
+
'flex',
|
|
1940
2077
|
'list',
|
|
1941
2078
|
'grid',
|
|
1942
2079
|
'horizontalScroll',
|
|
@@ -2034,7 +2171,10 @@ function ownArrayDataValues(value) {
|
|
|
2034
2171
|
}
|
|
2035
2172
|
}
|
|
2036
2173
|
function elementSlotEntries(element) {
|
|
2037
|
-
const
|
|
2174
|
+
const props = readOwnData(element, 'props');
|
|
2175
|
+
const slots = props.kind === 'value'
|
|
2176
|
+
? readOwnData(props.value, 'slots')
|
|
2177
|
+
: props;
|
|
2038
2178
|
return slots.kind === 'value'
|
|
2039
2179
|
? ownStringDataEntries(slots.value)
|
|
2040
2180
|
: {
|
|
@@ -2050,11 +2190,15 @@ function readRepeatBindingFields(value) {
|
|
|
2050
2190
|
const template = readOwnData(value, 'template');
|
|
2051
2191
|
const item = readOwnData(value, 'item');
|
|
2052
2192
|
const index = readOwnData(value, 'index');
|
|
2193
|
+
const emptyTemplate = readOwnData(value, 'emptyTemplate');
|
|
2053
2194
|
return {
|
|
2054
2195
|
source: source.kind === 'value' ? source.value : undefined,
|
|
2055
2196
|
template: template.kind === 'value' ? template.value : undefined,
|
|
2056
|
-
item
|
|
2197
|
+
...(item.kind === 'value' ? { item: item.value } : {}),
|
|
2057
2198
|
...(index.kind === 'value' ? { index: index.value } : {}),
|
|
2199
|
+
...(emptyTemplate.kind === 'value'
|
|
2200
|
+
? { emptyTemplate: emptyTemplate.value }
|
|
2201
|
+
: {}),
|
|
2058
2202
|
};
|
|
2059
2203
|
}
|
|
2060
2204
|
function slotStaticReferences(slot) {
|
|
@@ -2179,6 +2323,10 @@ function elementReferences(element) {
|
|
|
2179
2323
|
if (typeof binding?.template === 'string' && binding.template) {
|
|
2180
2324
|
references.push(binding.template);
|
|
2181
2325
|
}
|
|
2326
|
+
if (typeof binding?.emptyTemplate === 'string'
|
|
2327
|
+
&& binding.emptyTemplate) {
|
|
2328
|
+
references.push(binding.emptyTemplate);
|
|
2329
|
+
}
|
|
2182
2330
|
const a2ui = getA2UIChildBinding(slot);
|
|
2183
2331
|
if (typeof a2ui?.templateId === 'string' && a2ui.templateId) {
|
|
2184
2332
|
references.push(a2ui.templateId);
|
|
@@ -2186,14 +2334,14 @@ function elementReferences(element) {
|
|
|
2186
2334
|
}
|
|
2187
2335
|
return references;
|
|
2188
2336
|
}
|
|
2189
|
-
function validateRepeatBindings(schema,
|
|
2337
|
+
function validateRepeatBindings(schema, issues) {
|
|
2190
2338
|
const dynamicBindings = [];
|
|
2191
2339
|
for (const [id, element] of Object.entries(schema.elements)) {
|
|
2192
2340
|
const elementRepeats = elementSlotEntries(element).entries
|
|
2193
2341
|
.filter((entry) => hasOwn(entry[1], 'repeat')
|
|
2194
2342
|
|| getA2UIChildBinding(entry[1]) !== undefined);
|
|
2195
2343
|
if (elementRepeats.length > 1) {
|
|
2196
|
-
|
|
2344
|
+
addIssue(issues, 'DYNAMIC_SLOT_MULTIPLE', elementPath(id, 'props', 'slots'), `Element "${id}" has more than one dynamic slot`, { elementId: id });
|
|
2197
2345
|
}
|
|
2198
2346
|
for (const [slotName, slot] of elementRepeats) {
|
|
2199
2347
|
const a2ui = getA2UIChildBinding(slot);
|
|
@@ -2207,10 +2355,10 @@ function validateRepeatBindings(schema, errors) {
|
|
|
2207
2355
|
});
|
|
2208
2356
|
if (typeof a2ui.templateId !== 'string'
|
|
2209
2357
|
|| a2ui.templateId.length === 0) {
|
|
2210
|
-
|
|
2358
|
+
addIssue(issues, 'DYNAMIC_TEMPLATE_INVALID', slotPath(id, slotName), `Element "${id}" slot "${slotName}" dynamic children template must be a non-empty string`, { elementId: id, slotName });
|
|
2211
2359
|
}
|
|
2212
2360
|
else if (!getOwnElement(schema, a2ui.templateId)) {
|
|
2213
|
-
|
|
2361
|
+
addIssue(issues, 'DYNAMIC_TEMPLATE_NOT_FOUND', slotPath(id, slotName), `Element "${id}" slot "${slotName}" dynamic children reference unknown template "${a2ui.templateId}"`, { elementId: id, slotName, reference: a2ui.templateId });
|
|
2214
2362
|
}
|
|
2215
2363
|
continue;
|
|
2216
2364
|
}
|
|
@@ -2218,7 +2366,7 @@ function validateRepeatBindings(schema, errors) {
|
|
|
2218
2366
|
? repeat.value
|
|
2219
2367
|
: undefined;
|
|
2220
2368
|
if (!isPlainObject(rawBinding)) {
|
|
2221
|
-
|
|
2369
|
+
addIssue(issues, 'REPEAT_TYPE_MISMATCH', slotPath(id, slotName, 'repeat'), `Element "${id}" slot "${slotName}" repeat must be a non-null plain object`, { elementId: id, slotName });
|
|
2222
2370
|
continue;
|
|
2223
2371
|
}
|
|
2224
2372
|
const binding = readRepeatBindingFields(rawBinding);
|
|
@@ -2230,7 +2378,7 @@ function validateRepeatBindings(schema, errors) {
|
|
|
2230
2378
|
binding,
|
|
2231
2379
|
});
|
|
2232
2380
|
if (!REPEAT_SLOT_LAYOUTS.has(slotName)) {
|
|
2233
|
-
|
|
2381
|
+
addIssue(issues, 'REPEAT_SLOT_UNSUPPORTED', slotPath(id, slotName, 'repeat'), `Element "${id}" slot "${slotName}" does not support repeat`, { elementId: id, slotName });
|
|
2234
2382
|
}
|
|
2235
2383
|
const children = readOwnData(slot, 'children');
|
|
2236
2384
|
const groups = readOwnData(slot, 'groups');
|
|
@@ -2240,49 +2388,78 @@ function validateRepeatBindings(schema, errors) {
|
|
|
2240
2388
|
|| (groups.kind === 'value' && !!groups.value)
|
|
2241
2389
|
|| groups.kind === 'accessor'
|
|
2242
2390
|
|| groups.kind === 'opaque') {
|
|
2243
|
-
|
|
2391
|
+
addIssue(issues, 'REPEAT_CONTENT_CONFLICT', slotPath(id, slotName, 'repeat'), `Element "${id}" slot "${slotName}" cannot combine repeat with children or groups`, { elementId: id, slotName });
|
|
2244
2392
|
}
|
|
2245
2393
|
if (typeof binding.source !== 'string') {
|
|
2246
|
-
|
|
2394
|
+
addIssue(issues, 'REPEAT_SOURCE_TYPE_MISMATCH', slotPath(id, slotName, 'repeat', 'source'), `Element "${id}" slot "${slotName}" repeat source must be a string`, { elementId: id, slotName }, hasOwn(rawBinding, 'source')
|
|
2395
|
+
? undefined
|
|
2396
|
+
: slotPath(id, slotName, 'repeat'));
|
|
2247
2397
|
}
|
|
2248
2398
|
else if (!isCompleteExpression(binding.source)) {
|
|
2249
|
-
|
|
2399
|
+
addIssue(issues, 'REPEAT_SOURCE_INVALID', slotPath(id, slotName, 'repeat', 'source'), `Element "${id}" slot "${slotName}" repeat source must be one complete non-empty \${...} expression`, { elementId: id, slotName });
|
|
2250
2400
|
}
|
|
2251
2401
|
if (typeof binding.template !== 'string') {
|
|
2252
|
-
|
|
2402
|
+
addIssue(issues, 'REPEAT_TEMPLATE_TYPE_MISMATCH', slotPath(id, slotName, 'repeat', 'template'), `Element "${id}" slot "${slotName}" repeat template must be a string`, { elementId: id, slotName }, hasOwn(rawBinding, 'template')
|
|
2403
|
+
? undefined
|
|
2404
|
+
: slotPath(id, slotName, 'repeat'));
|
|
2253
2405
|
}
|
|
2254
2406
|
else if (!binding.template) {
|
|
2255
|
-
|
|
2407
|
+
addIssue(issues, 'REPEAT_TEMPLATE_INVALID', slotPath(id, slotName, 'repeat', 'template'), `Element "${id}" slot "${slotName}" repeat template must be a non-empty string`, { elementId: id, slotName });
|
|
2256
2408
|
}
|
|
2257
2409
|
else if (!getOwnElement(schema, binding.template)) {
|
|
2258
|
-
|
|
2410
|
+
addIssue(issues, 'REPEAT_TEMPLATE_NOT_FOUND', slotPath(id, slotName, 'repeat', 'template'), `Element "${id}" slot "${slotName}" repeat references unknown template "${binding.template}"`, { elementId: id, slotName, reference: binding.template });
|
|
2411
|
+
}
|
|
2412
|
+
if (binding.item !== undefined) {
|
|
2413
|
+
validateAlias(id, slotName, 'item', binding.item, issues);
|
|
2259
2414
|
}
|
|
2260
|
-
validateAlias(id, slotName, 'item', binding.item, errors);
|
|
2261
2415
|
if (binding.index !== undefined) {
|
|
2262
|
-
validateAlias(id, slotName, 'index', binding.index,
|
|
2416
|
+
validateAlias(id, slotName, 'index', binding.index, issues);
|
|
2263
2417
|
}
|
|
2264
|
-
if (binding.index !== undefined
|
|
2265
|
-
|
|
2418
|
+
if (binding.index !== undefined
|
|
2419
|
+
&& (binding.item ?? '$item') === binding.index) {
|
|
2420
|
+
addIssue(issues, 'REPEAT_ALIAS_COLLISION', slotPath(id, slotName, 'repeat', 'index'), `Element "${id}" slot "${slotName}" repeat item and index aliases must be different`, { elementId: id, slotName, alias: binding.index });
|
|
2421
|
+
}
|
|
2422
|
+
if (binding.emptyTemplate !== undefined) {
|
|
2423
|
+
if (typeof binding.emptyTemplate !== 'string') {
|
|
2424
|
+
addIssue(issues, 'REPEAT_EMPTY_TEMPLATE_TYPE_MISMATCH', slotPath(id, slotName, 'repeat', 'emptyTemplate'), `Element "${id}" slot "${slotName}" repeat emptyTemplate must be a string`, { elementId: id, slotName });
|
|
2425
|
+
}
|
|
2426
|
+
else if (!binding.emptyTemplate) {
|
|
2427
|
+
addIssue(issues, 'REPEAT_EMPTY_TEMPLATE_INVALID', slotPath(id, slotName, 'repeat', 'emptyTemplate'), `Element "${id}" slot "${slotName}" repeat emptyTemplate must be a non-empty string`, { elementId: id, slotName });
|
|
2428
|
+
}
|
|
2429
|
+
else if (!getOwnElement(schema, binding.emptyTemplate)) {
|
|
2430
|
+
addIssue(issues, 'REPEAT_EMPTY_TEMPLATE_NOT_FOUND', slotPath(id, slotName, 'repeat', 'emptyTemplate'), `Element "${id}" slot "${slotName}" repeat references unknown empty template "${binding.emptyTemplate}"`, { elementId: id, slotName, reference: binding.emptyTemplate });
|
|
2431
|
+
}
|
|
2266
2432
|
}
|
|
2267
2433
|
}
|
|
2268
2434
|
}
|
|
2269
|
-
validateRepeatNesting(schema, dynamicBindings,
|
|
2270
|
-
validateDynamicClosures(schema, dynamicBindings,
|
|
2435
|
+
validateRepeatNesting(schema, dynamicBindings, issues);
|
|
2436
|
+
validateDynamicClosures(schema, dynamicBindings, issues);
|
|
2271
2437
|
}
|
|
2272
|
-
function validateAlias(owner, slotName, kind, alias,
|
|
2438
|
+
function validateAlias(owner, slotName, kind, alias, issues) {
|
|
2273
2439
|
if (typeof alias !== 'string') {
|
|
2274
|
-
|
|
2440
|
+
addIssue(issues, 'REPEAT_ALIAS_TYPE_MISMATCH', slotPath(owner, slotName, 'repeat', kind), `Element "${owner}" slot "${slotName}" repeat ${kind} must be a string`, { elementId: owner, slotName, field: kind });
|
|
2275
2441
|
}
|
|
2276
2442
|
else if (!ALIAS_PATTERN.test(alias)) {
|
|
2277
|
-
|
|
2443
|
+
addIssue(issues, 'REPEAT_ALIAS_INVALID', slotPath(owner, slotName, 'repeat', kind), `Element "${owner}" slot "${slotName}" repeat ${kind} alias "${alias}" is invalid`, { elementId: owner, slotName, field: kind, alias });
|
|
2278
2444
|
}
|
|
2279
2445
|
else if (RESERVED_ALIASES.has(alias)) {
|
|
2280
|
-
|
|
2446
|
+
addIssue(issues, 'REPEAT_ALIAS_RESERVED', slotPath(owner, slotName, 'repeat', kind), `Element "${owner}" slot "${slotName}" repeat ${kind} alias "${alias}" is reserved`, { elementId: owner, slotName, field: kind, alias });
|
|
2281
2447
|
}
|
|
2282
2448
|
}
|
|
2283
|
-
function validateRepeatNesting(schema, bindings,
|
|
2449
|
+
function validateRepeatNesting(schema, bindings, issues) {
|
|
2284
2450
|
const reportedCycles = new Set();
|
|
2285
2451
|
const reportedCollisions = new Set();
|
|
2452
|
+
function effectiveAliases(binding) {
|
|
2453
|
+
if (!binding)
|
|
2454
|
+
return [];
|
|
2455
|
+
if (binding.item !== undefined) {
|
|
2456
|
+
return [binding.item, binding.index].filter((alias) => typeof alias === 'string');
|
|
2457
|
+
}
|
|
2458
|
+
return [
|
|
2459
|
+
'$item',
|
|
2460
|
+
binding.index === undefined ? '$index' : binding.index,
|
|
2461
|
+
].filter((alias) => typeof alias === 'string');
|
|
2462
|
+
}
|
|
2286
2463
|
function visitElement(id, activeAliases, repeatPath, visitedStatic) {
|
|
2287
2464
|
const element = getOwnElement(schema, id);
|
|
2288
2465
|
if (!element || visitedStatic.has(id))
|
|
@@ -2297,26 +2474,31 @@ function validateRepeatNesting(schema, bindings, errors) {
|
|
|
2297
2474
|
? readRepeatBindingFields(repeat.value)
|
|
2298
2475
|
: undefined;
|
|
2299
2476
|
const a2uiBinding = getA2UIChildBinding(slot);
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2477
|
+
const itemTemplate = nativeBinding?.template ?? a2uiBinding?.templateId;
|
|
2478
|
+
const visitDynamicTemplate = (template, aliases) => {
|
|
2479
|
+
if (!template)
|
|
2480
|
+
return;
|
|
2302
2481
|
const cycleAt = repeatPath.indexOf(template);
|
|
2303
2482
|
if (cycleAt >= 0) {
|
|
2304
2483
|
const cycle = [...repeatPath.slice(cycleAt), template].join(' -> ');
|
|
2305
2484
|
if (!reportedCycles.has(cycle)) {
|
|
2306
2485
|
reportedCycles.add(cycle);
|
|
2307
|
-
|
|
2486
|
+
addIssue(issues, nativeBinding
|
|
2487
|
+
? 'REPEAT_TEMPLATE_CYCLE'
|
|
2488
|
+
: 'DYNAMIC_TEMPLATE_CYCLE', nativeBinding
|
|
2489
|
+
? slotPath(id, slotName, 'repeat', 'template')
|
|
2490
|
+
: slotPath(id, slotName), nativeBinding
|
|
2308
2491
|
? `Element "${id}" slot "${slotName}" has repeat template cycle: ${cycle}`
|
|
2309
|
-
: `Element "${id}" slot "${slotName}" has dynamic template cycle: ${cycle}
|
|
2492
|
+
: `Element "${id}" slot "${slotName}" has dynamic template cycle: ${cycle}`, { elementId: id, slotName, cycle });
|
|
2310
2493
|
}
|
|
2311
|
-
|
|
2494
|
+
return;
|
|
2312
2495
|
}
|
|
2313
|
-
const aliases = [nativeBinding?.item, nativeBinding?.index].filter((alias) => typeof alias === 'string');
|
|
2314
2496
|
for (const alias of aliases) {
|
|
2315
2497
|
if (activeAliases.has(alias)) {
|
|
2316
2498
|
const key = `${id}:${slotName}:${alias}`;
|
|
2317
2499
|
if (!reportedCollisions.has(key)) {
|
|
2318
2500
|
reportedCollisions.add(key);
|
|
2319
|
-
|
|
2501
|
+
addIssue(issues, 'REPEAT_ALIAS_COLLISION', slotPath(id, slotName, 'repeat'), `Element "${id}" slot "${slotName}" repeat alias "${alias}" conflicts with an active repeat alias`, { elementId: id, slotName, alias });
|
|
2320
2502
|
}
|
|
2321
2503
|
}
|
|
2322
2504
|
}
|
|
@@ -2324,7 +2506,9 @@ function validateRepeatNesting(schema, bindings, errors) {
|
|
|
2324
2506
|
for (const alias of aliases)
|
|
2325
2507
|
nestedAliases.add(alias);
|
|
2326
2508
|
visitElement(template, nestedAliases, [...repeatPath, template], new Set());
|
|
2327
|
-
}
|
|
2509
|
+
};
|
|
2510
|
+
visitDynamicTemplate(itemTemplate, effectiveAliases(nativeBinding));
|
|
2511
|
+
visitDynamicTemplate(nativeBinding?.emptyTemplate, []);
|
|
2328
2512
|
for (const child of slotStaticReferences(slot)) {
|
|
2329
2513
|
visitElement(child, activeAliases, repeatPath, nextVisitedStatic);
|
|
2330
2514
|
}
|
|
@@ -2332,13 +2516,17 @@ function validateRepeatNesting(schema, bindings, errors) {
|
|
|
2332
2516
|
}
|
|
2333
2517
|
for (const dynamic of bindings) {
|
|
2334
2518
|
const { owner, binding, template } = dynamic;
|
|
2335
|
-
const aliases = new Set(
|
|
2519
|
+
const aliases = new Set(effectiveAliases(binding));
|
|
2336
2520
|
if (typeof template === 'string' && template) {
|
|
2337
2521
|
visitElement(template, aliases, [owner, template], new Set());
|
|
2338
2522
|
}
|
|
2523
|
+
if (typeof binding?.emptyTemplate === 'string'
|
|
2524
|
+
&& binding.emptyTemplate) {
|
|
2525
|
+
visitElement(binding.emptyTemplate, new Set(), [owner, binding.emptyTemplate], new Set());
|
|
2526
|
+
}
|
|
2339
2527
|
}
|
|
2340
2528
|
}
|
|
2341
|
-
function validateDynamicClosures(schema, repeats,
|
|
2529
|
+
function validateDynamicClosures(schema, repeats, issues) {
|
|
2342
2530
|
for (const { owner } of repeats) {
|
|
2343
2531
|
const visited = new Set();
|
|
2344
2532
|
function visit(id) {
|
|
@@ -2352,10 +2540,11 @@ function validateDynamicClosures(schema, repeats, errors) {
|
|
|
2352
2540
|
if ((lifecycle.kind === 'value' && !!lifecycle.value)
|
|
2353
2541
|
|| lifecycle.kind === 'accessor'
|
|
2354
2542
|
|| lifecycle.kind === 'opaque') {
|
|
2355
|
-
|
|
2543
|
+
addIssue(issues, 'DYNAMIC_LIFECYCLE_UNSUPPORTED', elementPath(id, 'lifecycle'), `Element "${id}" in dynamic patch closure owned by "${owner}" cannot use lifecycle`, { elementId: id, owner });
|
|
2356
2544
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2545
|
+
const props = readOwnData(element, 'props');
|
|
2546
|
+
if (props.kind === 'value' && hasOwn(props.value, 'variableKey')) {
|
|
2547
|
+
addIssue(issues, 'DYNAMIC_VARIABLE_KEY_UNSUPPORTED', elementPath(id, 'props', 'variableKey'), `Element "${id}" in dynamic patch closure owned by "${owner}" cannot use props.variableKey`, { elementId: id, owner });
|
|
2359
2548
|
}
|
|
2360
2549
|
for (const reference of elementReferences(element))
|
|
2361
2550
|
visit(reference);
|
|
@@ -2882,6 +3071,7 @@ function materializeCard(input, variables, options = {}) {
|
|
|
2882
3071
|
template: native.template,
|
|
2883
3072
|
item: native.item,
|
|
2884
3073
|
index: native.index,
|
|
3074
|
+
emptyTemplate: native.emptyTemplate,
|
|
2885
3075
|
};
|
|
2886
3076
|
}
|
|
2887
3077
|
else {
|
|
@@ -2909,6 +3099,9 @@ function materializeCard(input, variables, options = {}) {
|
|
|
2909
3099
|
sourceOwnerId: sourceId,
|
|
2910
3100
|
slot: slotName,
|
|
2911
3101
|
templateId: binding.template,
|
|
3102
|
+
...(binding.emptyTemplate
|
|
3103
|
+
? { emptyTemplateId: binding.emptyTemplate }
|
|
3104
|
+
: {}),
|
|
2912
3105
|
...(sourcePath === undefined ? {} : { sourcePath }),
|
|
2913
3106
|
dataPath: activeScope.dataPath,
|
|
2914
3107
|
instancePath,
|
|
@@ -2920,6 +3113,9 @@ function materializeCard(input, variables, options = {}) {
|
|
|
2920
3113
|
repeatOwners.set(ownerKey, owner);
|
|
2921
3114
|
slotOwners.set(slotName, { owner, binding, sourcePath, depth });
|
|
2922
3115
|
addDependency('component', binding.template, ownerKey);
|
|
3116
|
+
if (binding.emptyTemplate) {
|
|
3117
|
+
addDependency('component', binding.emptyTemplate, ownerKey);
|
|
3118
|
+
}
|
|
2923
3119
|
const runtimeKeys = runtimeOwnerKeys.get(id) ?? [];
|
|
2924
3120
|
if (runtimeKeys.length > 0) {
|
|
2925
3121
|
for (const key of runtimeKeys)
|
|
@@ -3000,15 +3196,30 @@ function materializeCard(input, variables, options = {}) {
|
|
|
3000
3196
|
Reflect.deleteProperty(slot, 'repeat');
|
|
3001
3197
|
Reflect.deleteProperty(slot, A2UI_CHILD_BINDING);
|
|
3002
3198
|
slot.children = [];
|
|
3199
|
+
if (items.length === 0
|
|
3200
|
+
&& binding.dialect === 'native'
|
|
3201
|
+
&& binding.emptyTemplate) {
|
|
3202
|
+
const emptyPath = appendOccurrence(structuralPath, 'item', sourceId, slotName, `${binding.emptyTemplate}:empty`);
|
|
3203
|
+
const empty = buildNode(binding.emptyTemplate, activeScope, emptyPath, emptyPath, true, id, depth, owner.key, nextActiveDefinitions);
|
|
3204
|
+
slot.children.push(empty.id);
|
|
3205
|
+
node.children.push(empty);
|
|
3206
|
+
}
|
|
3003
3207
|
items.forEach((item, index) => {
|
|
3004
3208
|
const dataPath = sourcePath === undefined
|
|
3005
3209
|
? activeScope.dataPath
|
|
3006
3210
|
: joinPointer(sourcePath, [String(index)]);
|
|
3007
|
-
const locals = binding.dialect === 'native'
|
|
3008
|
-
?
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3211
|
+
const locals = binding.dialect === 'native'
|
|
3212
|
+
? binding.item !== undefined
|
|
3213
|
+
? {
|
|
3214
|
+
[binding.item]: item,
|
|
3215
|
+
...(binding.index === undefined
|
|
3216
|
+
? {}
|
|
3217
|
+
: { [binding.index]: index }),
|
|
3218
|
+
}
|
|
3219
|
+
: {
|
|
3220
|
+
$item: item,
|
|
3221
|
+
[binding.index ?? '$index']: index,
|
|
3222
|
+
}
|
|
3012
3223
|
: {};
|
|
3013
3224
|
const childScope = {
|
|
3014
3225
|
root: rootVariables,
|
|
@@ -3018,14 +3229,23 @@ function materializeCard(input, variables, options = {}) {
|
|
|
3018
3229
|
bindingDialect: binding.dialect,
|
|
3019
3230
|
};
|
|
3020
3231
|
const aliases = new Map();
|
|
3021
|
-
if (binding.dialect === 'native'
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3232
|
+
if (binding.dialect === 'native') {
|
|
3233
|
+
const itemAlias = binding.item ?? '$item';
|
|
3234
|
+
const indexAlias = binding.item === undefined
|
|
3235
|
+
? binding.index ?? '$index'
|
|
3236
|
+
: binding.index;
|
|
3237
|
+
aliases.set(itemAlias, sourcePath === undefined ? undefined : dataPath);
|
|
3238
|
+
if (indexAlias !== undefined) {
|
|
3239
|
+
aliases.set(indexAlias, sourcePath);
|
|
3240
|
+
}
|
|
3025
3241
|
}
|
|
3026
3242
|
scopeAliasPaths.set(childScope, aliases);
|
|
3027
|
-
scopeIndexAliases.set(childScope, new Set(binding.dialect === 'native'
|
|
3028
|
-
?
|
|
3243
|
+
scopeIndexAliases.set(childScope, new Set(binding.dialect === 'native'
|
|
3244
|
+
? binding.item === undefined
|
|
3245
|
+
? [binding.index ?? '$index']
|
|
3246
|
+
: binding.index === undefined
|
|
3247
|
+
? []
|
|
3248
|
+
: [binding.index]
|
|
3029
3249
|
: []));
|
|
3030
3250
|
const itemPath = appendOccurrence(structuralPath, 'item', sourceId, slotName, `${binding.template}:${index}`);
|
|
3031
3251
|
const child = buildNode(binding.template, childScope, itemPath, itemPath, true, id, depth, owner.key, nextActiveDefinitions);
|
|
@@ -4802,3 +5022,4 @@ exports.runActionSteps = runActionSteps;
|
|
|
4802
5022
|
exports.setByJsonPointer = setByJsonPointer;
|
|
4803
5023
|
exports.setByPath = setByPath;
|
|
4804
5024
|
exports.validateSchema = validateSchema;
|
|
5025
|
+
exports.validateSchemaDetailed = validateSchemaDetailed;
|