@adecore/intelligent-ui 0.25.0 → 0.25.2
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/catalog.d.ts +1 -1
- package/dist/catalog.js +2 -3
- package/dist/compiler.js +5 -0
- package/dist/event-time.d.ts +1 -0
- package/dist/event-time.js +14 -8
- package/dist/event-warning.d.ts +2 -0
- package/dist/event-warning.js +10 -0
- package/dist/protocol.d.ts +1 -0
- package/dist/protocol.js +2 -1
- package/dist/runtime.js +9 -0
- package/dist/syntax.d.ts +1 -0
- package/dist/syntax.js +4 -1
- package/package.json +1 -1
- package/src/catalog.ts +3 -3
- package/src/compiler.ts +5 -0
- package/src/event-time.ts +17 -8
- package/src/event-warning.ts +14 -0
- package/src/protocol.ts +2 -1
- package/src/runtime.ts +11 -0
- package/src/syntax.ts +7 -1
package/dist/catalog.d.ts
CHANGED
|
@@ -124,7 +124,7 @@ export declare const UI_CATALOG: {
|
|
|
124
124
|
};
|
|
125
125
|
readonly Event: {
|
|
126
126
|
readonly group: 'status';
|
|
127
|
-
readonly description: 'One event at a time or date; children are the title.';
|
|
127
|
+
readonly description: 'One event at a time or date; children are the title. at is HH:MM[:SS], YYYY-MM-DD, or YYYY-MM-DDTHH:MM[:SS] with an optional Z or ±HH:MM.';
|
|
128
128
|
readonly schema: z.ZodObject<{
|
|
129
129
|
at: z.ZodString;
|
|
130
130
|
tone: z.ZodOptional<z.ZodEnum<{
|
package/dist/catalog.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { parseUiEventTime } from './event-time.js';
|
|
3
2
|
import { uiPropList } from './prop-list.js';
|
|
4
3
|
export const UI_CATALOG_VERSION = 1;
|
|
5
4
|
export const UiToneSchema = z.enum(['neutral', 'info', 'success', 'warning', 'danger']);
|
|
@@ -51,9 +50,9 @@ export const UI_CATALOG = {
|
|
|
51
50
|
Timeline: { group: 'status', description: 'Events in time order.', schema: empty, children: ['Event'], count: { min: 1 } },
|
|
52
51
|
Event: {
|
|
53
52
|
group: 'status',
|
|
54
|
-
description: 'One event at a time or date; children are the title.',
|
|
53
|
+
description: 'One event at a time or date; children are the title. at is HH:MM[:SS], YYYY-MM-DD, or YYYY-MM-DDTHH:MM[:SS] with an optional Z or ±HH:MM.',
|
|
55
54
|
schema: z.strictObject({
|
|
56
|
-
at: text
|
|
55
|
+
at: text,
|
|
57
56
|
tone,
|
|
58
57
|
detail: text.optional()
|
|
59
58
|
}),
|
package/dist/compiler.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { safeKey, UI_LIMITS, UiBudget, UiFailure } from './budget.js';
|
|
2
2
|
import { isUiComponent, UI_CATALOG, UI_CATALOG_VERSION } from './catalog.js';
|
|
3
3
|
import { uiActionProp, uiActionTarget, uiLocalState } from './actions.js';
|
|
4
|
+
import { unreadableEventTime } from './event-warning.js';
|
|
4
5
|
import { uiNodeFallback, uiPlainText } from './fallback.js';
|
|
5
6
|
import { evaluateUiExpression, sameUiValue } from './expression.js';
|
|
6
7
|
import { parseUiSyntax, uiDiagnostic } from './syntax.js';
|
|
@@ -241,6 +242,10 @@ export function compileUiBlock(source, options) {
|
|
|
241
242
|
throw new UiFailure('invalid_props', parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join('; '));
|
|
242
243
|
}
|
|
243
244
|
}
|
|
245
|
+
const unreadable = !unresolved && syntax.complete && syntax.type === 'Event' ? unreadableEventTime(evaluated.at) : null;
|
|
246
|
+
if (unreadable !== null) {
|
|
247
|
+
diagnose(unreadable, syntax.start, syntax.end, syntax.id);
|
|
248
|
+
}
|
|
244
249
|
// The catalog promises six series, one per chart color; more still reach the values a person can open.
|
|
245
250
|
if (!unresolved && syntax.complete && syntax.type === 'Chart' && chartSeries(evaluated.data) > CHART_SERIES) {
|
|
246
251
|
diagnose(new UiFailure('too_many_series', `Chart draws up to ${CHART_SERIES} numeric series; the rest are only in its values.`), syntax.start, syntax.end, syntax.id);
|
package/dist/event-time.d.ts
CHANGED
package/dist/event-time.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const EVENT_TIME = /^(?:(\d{2}):(\d{2})
|
|
1
|
+
const EVENT_TIME = /^(?:(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?|(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})?)?)$/;
|
|
2
2
|
function validClock(hour, minute, second = 0) {
|
|
3
3
|
return hour <= 23 && minute <= 59 && second <= 59;
|
|
4
4
|
}
|
|
@@ -11,17 +11,23 @@ function zoneOffset(zone) {
|
|
|
11
11
|
const offset = (zone[0] === '-' ? -1 : 1) * (hours * 60 + minutes);
|
|
12
12
|
return minutes > 59 || offset < -12 * 60 || offset > 14 * 60 ? null : offset;
|
|
13
13
|
}
|
|
14
|
-
/* The moment an Event's `at` names, or null when it is no `HH:MM`, `YYYY-MM-DD` or ISO date-time with an optional offset. */
|
|
14
|
+
/* The moment an Event's `at` names, or null when it is no `HH:MM[:SS]`, `YYYY-MM-DD` or ISO date-time with an optional offset. */
|
|
15
15
|
export function parseUiEventTime(at) {
|
|
16
16
|
const match = EVENT_TIME.exec(at);
|
|
17
17
|
if (match === null) {
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
|
-
const [, clockHour, clockMinute, year, month, day, hour, minute, second, zone] = match.map((part) => part);
|
|
20
|
+
const [, clockHour, clockMinute, clockSecond, year, month, day, hour, minute, second, zone] = match.map((part) => part);
|
|
21
21
|
if (clockHour !== undefined) {
|
|
22
22
|
// The day is not shown for a time, so any day will do.
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const clockSeconds = Number(clockSecond ?? 0);
|
|
24
|
+
return validClock(Number(clockHour), Number(clockMinute), clockSeconds)
|
|
25
|
+
? {
|
|
26
|
+
kind: 'time',
|
|
27
|
+
date: new Date(Date.UTC(2000, 0, 1, Number(clockHour), Number(clockMinute), clockSeconds)),
|
|
28
|
+
written: true,
|
|
29
|
+
seconds: clockSecond !== undefined
|
|
30
|
+
}
|
|
25
31
|
: null;
|
|
26
32
|
}
|
|
27
33
|
const [y, m, d] = [Number(year), Number(month), Number(day)];
|
|
@@ -30,15 +36,15 @@ export function parseUiEventTime(at) {
|
|
|
30
36
|
return null;
|
|
31
37
|
}
|
|
32
38
|
if (hour === undefined) {
|
|
33
|
-
return { kind: 'date', date: calendar, written: true };
|
|
39
|
+
return { kind: 'date', date: calendar, written: true, seconds: false };
|
|
34
40
|
}
|
|
35
41
|
const [h, min, sec] = [Number(hour), Number(minute), Number(second ?? 0)];
|
|
36
42
|
if (!validClock(h, min, sec)) {
|
|
37
43
|
return null;
|
|
38
44
|
}
|
|
39
45
|
if (zone === undefined) {
|
|
40
|
-
return { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec)), written: true };
|
|
46
|
+
return { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec)), written: true, seconds: false };
|
|
41
47
|
}
|
|
42
48
|
const offset = zoneOffset(zone);
|
|
43
|
-
return offset === null ? null : { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec) - offset * 60000), written: false };
|
|
49
|
+
return offset === null ? null : { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec) - offset * 60000), written: false, seconds: false };
|
|
44
50
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { UiFailure } from './budget.js';
|
|
2
|
+
import { parseUiEventTime } from './event-time.js';
|
|
3
|
+
/* The warning for an Event whose `at` cannot be read, or null when it can. The Event still draws, with `at` as written. */
|
|
4
|
+
export function unreadableEventTime(at) {
|
|
5
|
+
if (typeof at !== 'string' || parseUiEventTime(at) !== null) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const shown = at.length > 40 ? `${at.slice(0, 40)}...` : at;
|
|
9
|
+
return new UiFailure('unreadable_time', `Event at="${shown}" is not HH:MM[:SS], YYYY-MM-DD or YYYY-MM-DDTHH:MM[:SS] with an optional Z or ±HH:MM, so it is shown as written.`);
|
|
10
|
+
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare const UiDiagnosticSchema: z.ZodObject<{
|
|
|
7
7
|
start: z.ZodNumber;
|
|
8
8
|
end: z.ZodNumber;
|
|
9
9
|
nodeId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
severity: z.ZodOptional<z.ZodLiteral<"warning">>;
|
|
10
11
|
}, z.core.$strip>;
|
|
11
12
|
export declare const UiBlockSchema: z.ZodType<UiBlock>;
|
|
12
13
|
export declare const UiBlocksSchema: z.ZodArray<z.ZodType<UiBlock, unknown, z.core.$ZodTypeInternals<UiBlock, unknown>>>;
|
package/dist/protocol.js
CHANGED
|
@@ -20,7 +20,8 @@ export const UiDiagnosticSchema = z.object({
|
|
|
20
20
|
message: z.string(),
|
|
21
21
|
start: position,
|
|
22
22
|
end: position,
|
|
23
|
-
nodeId: z.string().optional()
|
|
23
|
+
nodeId: z.string().optional(),
|
|
24
|
+
severity: z.literal('warning').optional()
|
|
24
25
|
});
|
|
25
26
|
export const UiBlockSchema = z.object({
|
|
26
27
|
id: z.string(),
|
package/dist/runtime.js
CHANGED
|
@@ -4,6 +4,7 @@ import { isUiComponent, UI_CATALOG, UI_CATALOG_VERSION } from './catalog.js';
|
|
|
4
4
|
import {} from './compiler.js';
|
|
5
5
|
import { uiActionProp, uiActionTarget, uiLocalState } from './actions.js';
|
|
6
6
|
import { uiCompiledNodes, uiVisibleInputs } from './inputs.js';
|
|
7
|
+
import { unreadableEventTime } from './event-warning.js';
|
|
7
8
|
import { uiDiagnostic } from './syntax.js';
|
|
8
9
|
export class UiState {
|
|
9
10
|
id = '';
|
|
@@ -150,6 +151,14 @@ export function evaluateUiBlock(block, state = new UiState(block), limits = {})
|
|
|
150
151
|
throw new UiFailure('invalid_props', parsed.error.issues.map((issue) => issue.message).join('; '));
|
|
151
152
|
}
|
|
152
153
|
view.props = parsed.data;
|
|
154
|
+
const unreadable = node.complete && node.type === 'Event' ? unreadableEventTime(props.at) : null;
|
|
155
|
+
// The compiler already warned about a time it could read from the source; this one waited on an Each or a query.
|
|
156
|
+
if (unreadable !== null &&
|
|
157
|
+
![...block.diagnostics, ...result.diagnostics].some((item) => item.code === 'unreadable_time' && item.nodeId === node.id)) {
|
|
158
|
+
if (result.diagnostics.length < budget.limits.diagnostics) {
|
|
159
|
+
result.diagnostics.push(uiDiagnostic(unreadable, node.start, node.end, node.id));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
153
162
|
if (node.type === 'Show') {
|
|
154
163
|
return props.when ? children(node.children, variables, depth + 1, suffix) : [];
|
|
155
164
|
}
|
package/dist/syntax.d.ts
CHANGED
package/dist/syntax.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { safeKey, UiBudget, UiFailure } from './budget.js';
|
|
2
2
|
import { parseUiExpression } from './expression.js';
|
|
3
|
+
const WARNING_CODES = new Set(['refused_prop', 'too_many_series', 'too_many_children', 'unreadable_time']);
|
|
3
4
|
export function uiDiagnostic(error, start, end, nodeId) {
|
|
5
|
+
const code = error instanceof UiFailure ? error.code : 'invalid_syntax';
|
|
4
6
|
return {
|
|
5
|
-
code
|
|
7
|
+
code,
|
|
8
|
+
...(WARNING_CODES.has(code) ? { severity: 'warning' } : {}),
|
|
6
9
|
message: error instanceof Error ? error.message : 'Invalid UI syntax.',
|
|
7
10
|
start,
|
|
8
11
|
end,
|
package/package.json
CHANGED
package/src/catalog.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { parseUiEventTime } from './event-time.ts';
|
|
3
2
|
import { uiPropList } from './prop-list.ts';
|
|
4
3
|
|
|
5
4
|
export const UI_CATALOG_VERSION = 1;
|
|
@@ -56,9 +55,10 @@ export const UI_CATALOG = {
|
|
|
56
55
|
Timeline: { group: 'status', description: 'Events in time order.', schema: empty, children: ['Event'], count: { min: 1 } },
|
|
57
56
|
Event: {
|
|
58
57
|
group: 'status',
|
|
59
|
-
description:
|
|
58
|
+
description:
|
|
59
|
+
'One event at a time or date; children are the title. at is HH:MM[:SS], YYYY-MM-DD, or YYYY-MM-DDTHH:MM[:SS] with an optional Z or ±HH:MM.',
|
|
60
60
|
schema: z.strictObject({
|
|
61
|
-
at: text
|
|
61
|
+
at: text,
|
|
62
62
|
tone,
|
|
63
63
|
detail: text.optional()
|
|
64
64
|
}),
|
package/src/compiler.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { safeKey, UI_LIMITS, UiBudget, UiFailure, type UiLimits } from './budget.ts';
|
|
2
2
|
import { isUiComponent, UI_CATALOG, UI_CATALOG_VERSION } from './catalog.ts';
|
|
3
3
|
import { uiActionProp, uiActionTarget, uiLocalState } from './actions.ts';
|
|
4
|
+
import { unreadableEventTime } from './event-warning.ts';
|
|
4
5
|
import { uiNodeFallback, uiPlainText } from './fallback.ts';
|
|
5
6
|
import { evaluateUiExpression, sameUiValue, type UiExpression, type UiValue } from './expression.ts';
|
|
6
7
|
import { parseUiSyntax, uiDiagnostic, type UiDiagnostic, type UiSyntaxNode } from './syntax.ts';
|
|
@@ -312,6 +313,10 @@ export function compileUiBlock(source: string, options: UiCompileOptions): UiBlo
|
|
|
312
313
|
throw new UiFailure('invalid_props', parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join('; '));
|
|
313
314
|
}
|
|
314
315
|
}
|
|
316
|
+
const unreadable = !unresolved && syntax.complete && syntax.type === 'Event' ? unreadableEventTime(evaluated.at) : null;
|
|
317
|
+
if (unreadable !== null) {
|
|
318
|
+
diagnose(unreadable, syntax.start, syntax.end, syntax.id);
|
|
319
|
+
}
|
|
315
320
|
// The catalog promises six series, one per chart color; more still reach the values a person can open.
|
|
316
321
|
if (!unresolved && syntax.complete && syntax.type === 'Chart' && chartSeries(evaluated.data) > CHART_SERIES) {
|
|
317
322
|
diagnose(
|
package/src/event-time.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
const EVENT_TIME =
|
|
1
|
+
const EVENT_TIME =
|
|
2
|
+
/^(?:(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?|(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})?)?)$/;
|
|
2
3
|
|
|
3
4
|
export interface UiEventTime {
|
|
4
5
|
kind: 'time' | 'date' | 'datetime';
|
|
5
6
|
date: Date;
|
|
6
7
|
// True when the text gave no offset: `date` then holds the parts as written in UTC, to be written in UTC, so no zone or clock change moves them. False is a real instant, to be written in the reader's zone.
|
|
7
8
|
written: boolean;
|
|
9
|
+
// True for a lone time that wrote seconds (`14:05:30`), which then shows them. A date-time shows its minutes only.
|
|
10
|
+
seconds: boolean;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
function validClock(hour: number, minute: number, second = 0): boolean {
|
|
@@ -21,17 +24,23 @@ function zoneOffset(zone: string): number | null {
|
|
|
21
24
|
return minutes > 59 || offset < -12 * 60 || offset > 14 * 60 ? null : offset;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
/* The moment an Event's `at` names, or null when it is no `HH:MM`, `YYYY-MM-DD` or ISO date-time with an optional offset. */
|
|
27
|
+
/* The moment an Event's `at` names, or null when it is no `HH:MM[:SS]`, `YYYY-MM-DD` or ISO date-time with an optional offset. */
|
|
25
28
|
export function parseUiEventTime(at: string): UiEventTime | null {
|
|
26
29
|
const match = EVENT_TIME.exec(at);
|
|
27
30
|
if (match === null) {
|
|
28
31
|
return null;
|
|
29
32
|
}
|
|
30
|
-
const [, clockHour, clockMinute, year, month, day, hour, minute, second, zone] = match.map((part) => part as string | undefined);
|
|
33
|
+
const [, clockHour, clockMinute, clockSecond, year, month, day, hour, minute, second, zone] = match.map((part) => part as string | undefined);
|
|
31
34
|
if (clockHour !== undefined) {
|
|
32
35
|
// The day is not shown for a time, so any day will do.
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
const clockSeconds = Number(clockSecond ?? 0);
|
|
37
|
+
return validClock(Number(clockHour), Number(clockMinute), clockSeconds)
|
|
38
|
+
? {
|
|
39
|
+
kind: 'time',
|
|
40
|
+
date: new Date(Date.UTC(2000, 0, 1, Number(clockHour), Number(clockMinute), clockSeconds)),
|
|
41
|
+
written: true,
|
|
42
|
+
seconds: clockSecond !== undefined
|
|
43
|
+
}
|
|
35
44
|
: null;
|
|
36
45
|
}
|
|
37
46
|
const [y, m, d] = [Number(year), Number(month), Number(day)];
|
|
@@ -40,15 +49,15 @@ export function parseUiEventTime(at: string): UiEventTime | null {
|
|
|
40
49
|
return null;
|
|
41
50
|
}
|
|
42
51
|
if (hour === undefined) {
|
|
43
|
-
return { kind: 'date', date: calendar, written: true };
|
|
52
|
+
return { kind: 'date', date: calendar, written: true, seconds: false };
|
|
44
53
|
}
|
|
45
54
|
const [h, min, sec] = [Number(hour), Number(minute), Number(second ?? 0)];
|
|
46
55
|
if (!validClock(h, min, sec)) {
|
|
47
56
|
return null;
|
|
48
57
|
}
|
|
49
58
|
if (zone === undefined) {
|
|
50
|
-
return { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec)), written: true };
|
|
59
|
+
return { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec)), written: true, seconds: false };
|
|
51
60
|
}
|
|
52
61
|
const offset = zoneOffset(zone);
|
|
53
|
-
return offset === null ? null : { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec) - offset * 60000), written: false };
|
|
62
|
+
return offset === null ? null : { kind: 'datetime', date: new Date(Date.UTC(y, m - 1, d, h, min, sec) - offset * 60000), written: false, seconds: false };
|
|
54
63
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { UiFailure } from './budget.ts';
|
|
2
|
+
import { parseUiEventTime } from './event-time.ts';
|
|
3
|
+
|
|
4
|
+
/* The warning for an Event whose `at` cannot be read, or null when it can. The Event still draws, with `at` as written. */
|
|
5
|
+
export function unreadableEventTime(at: unknown): UiFailure | null {
|
|
6
|
+
if (typeof at !== 'string' || parseUiEventTime(at) !== null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const shown = at.length > 40 ? `${at.slice(0, 40)}...` : at;
|
|
10
|
+
return new UiFailure(
|
|
11
|
+
'unreadable_time',
|
|
12
|
+
`Event at="${shown}" is not HH:MM[:SS], YYYY-MM-DD or YYYY-MM-DDTHH:MM[:SS] with an optional Z or ±HH:MM, so it is shown as written.`
|
|
13
|
+
);
|
|
14
|
+
}
|
package/src/protocol.ts
CHANGED
|
@@ -26,7 +26,8 @@ export const UiDiagnosticSchema = z.object({
|
|
|
26
26
|
message: z.string(),
|
|
27
27
|
start: position,
|
|
28
28
|
end: position,
|
|
29
|
-
nodeId: z.string().optional()
|
|
29
|
+
nodeId: z.string().optional(),
|
|
30
|
+
severity: z.literal('warning').optional()
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
export const UiBlockSchema: z.ZodType<UiBlock> = z.object({
|
package/src/runtime.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { isUiComponent, UI_CATALOG, UI_CATALOG_VERSION } from './catalog.ts';
|
|
|
4
4
|
import { type UiBlock, type UiNode } from './compiler.ts';
|
|
5
5
|
import { uiActionProp, uiActionTarget, uiLocalState } from './actions.ts';
|
|
6
6
|
import { uiCompiledNodes, uiVisibleInputs } from './inputs.ts';
|
|
7
|
+
import { unreadableEventTime } from './event-warning.ts';
|
|
7
8
|
import { uiDiagnostic, type UiDiagnostic } from './syntax.ts';
|
|
8
9
|
|
|
9
10
|
export interface UiBinding<Value> {
|
|
@@ -185,6 +186,16 @@ export function evaluateUiBlock(block: UiBlock, state = new UiState(block), limi
|
|
|
185
186
|
throw new UiFailure('invalid_props', parsed.error.issues.map((issue) => issue.message).join('; '));
|
|
186
187
|
}
|
|
187
188
|
view.props = parsed.data;
|
|
189
|
+
const unreadable = node.complete && node.type === 'Event' ? unreadableEventTime(props.at) : null;
|
|
190
|
+
// The compiler already warned about a time it could read from the source; this one waited on an Each or a query.
|
|
191
|
+
if (
|
|
192
|
+
unreadable !== null &&
|
|
193
|
+
![...block.diagnostics, ...result.diagnostics].some((item) => item.code === 'unreadable_time' && item.nodeId === node.id)
|
|
194
|
+
) {
|
|
195
|
+
if (result.diagnostics.length < budget.limits.diagnostics) {
|
|
196
|
+
result.diagnostics.push(uiDiagnostic(unreadable, node.start, node.end, node.id));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
188
199
|
if (node.type === 'Show') {
|
|
189
200
|
return props.when ? children(node.children, variables, depth + 1, suffix) : [];
|
|
190
201
|
}
|
package/src/syntax.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface UiDiagnostic {
|
|
|
7
7
|
start: number;
|
|
8
8
|
end: number;
|
|
9
9
|
nodeId?: string;
|
|
10
|
+
// Only a warning says so: the part it names still drew. Absent means an error, a part that could not be drawn.
|
|
11
|
+
severity?: 'warning';
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export interface UiSyntaxNode {
|
|
@@ -29,9 +31,13 @@ export interface UiSyntax {
|
|
|
29
31
|
diagnostics: UiDiagnostic[];
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
const WARNING_CODES = new Set(['refused_prop', 'too_many_series', 'too_many_children', 'unreadable_time']);
|
|
35
|
+
|
|
32
36
|
export function uiDiagnostic(error: unknown, start: number, end: number, nodeId?: string): UiDiagnostic {
|
|
37
|
+
const code = error instanceof UiFailure ? error.code : 'invalid_syntax';
|
|
33
38
|
return {
|
|
34
|
-
code
|
|
39
|
+
code,
|
|
40
|
+
...(WARNING_CODES.has(code) ? { severity: 'warning' as const } : {}),
|
|
35
41
|
message: error instanceof Error ? error.message : 'Invalid UI syntax.',
|
|
36
42
|
start,
|
|
37
43
|
end,
|