@cynodia/axiom-core 0.6.0-alpha.1 → 0.6.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/diagnostics.d.ts +1 -0
- package/dist/diagnostics.js +1 -0
- package/dist/graph.js +1 -1
- package/dist/resolve-presentation.js +5 -2
- package/dist/ui.d.ts +12 -0
- package/dist/ui.js +18 -0
- package/dist/validate-presentation.js +6 -3
- package/dist/validate.js +30 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,13 +21,13 @@ builders, `Presentation`, `Theme`, `resolvePresentationMap`, `validateGraph`,
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npm install @cynodia/axiom-core
|
|
24
|
+
npm install @cynodia/axiom-core
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Most applications should install the facade package instead, which re-exports this one:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install @cynodia/axiom
|
|
30
|
+
npm install @cynodia/axiom
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
|
package/dist/diagnostics.d.ts
CHANGED
|
@@ -71,5 +71,6 @@ export declare const VALIDATION_CODES: {
|
|
|
71
71
|
readonly principalReferenceOnClient: "PRINCIPAL_REFERENCE_ON_CLIENT";
|
|
72
72
|
readonly serverOnlyStateObserved: "SERVER_ONLY_STATE_OBSERVED";
|
|
73
73
|
readonly invalidPrincipalEntity: "INVALID_PRINCIPAL_ENTITY";
|
|
74
|
+
readonly missingActionArgument: "MISSING_ACTION_ARGUMENT";
|
|
74
75
|
};
|
|
75
76
|
//# sourceMappingURL=diagnostics.d.ts.map
|
package/dist/diagnostics.js
CHANGED
|
@@ -58,4 +58,5 @@ export const VALIDATION_CODES = {
|
|
|
58
58
|
principalReferenceOnClient: 'PRINCIPAL_REFERENCE_ON_CLIENT',
|
|
59
59
|
serverOnlyStateObserved: 'SERVER_ONLY_STATE_OBSERVED',
|
|
60
60
|
invalidPrincipalEntity: 'INVALID_PRINCIPAL_ENTITY',
|
|
61
|
+
missingActionArgument: 'MISSING_ACTION_ARGUMENT',
|
|
61
62
|
};
|
package/dist/graph.js
CHANGED
|
@@ -23,7 +23,7 @@ export class ApplicationGraph {
|
|
|
23
23
|
/** Bumped by every change, so the derived edge index can never serve stale data. */
|
|
24
24
|
revision = 0;
|
|
25
25
|
semanticIndex;
|
|
26
|
-
constructor(id, name, version = '0.6.
|
|
26
|
+
constructor(id, name, version = '0.6.2') {
|
|
27
27
|
this.data = { id, name, version, nodes: {}, edges: {} };
|
|
28
28
|
}
|
|
29
29
|
get id() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isUINode, uiChildIds } from './ui.js';
|
|
1
|
+
import { formSubmitActionId, isUINode, uiChildIds } from './ui.js';
|
|
2
2
|
import { TEXT_ROLE_HEADING_LEVELS, normalizeDensity, normalizeLayout, normalizePadding, normalizeRole, } from './presentation.js';
|
|
3
3
|
import { DEFAULT_THEME } from './theme.js';
|
|
4
4
|
/**
|
|
@@ -249,7 +249,10 @@ function semanticLayer(node, index) {
|
|
|
249
249
|
const layer = { origin: 'inferred' };
|
|
250
250
|
const formId = index.formOf.get(node.id);
|
|
251
251
|
const form = formId ? index.nodes.get(formId) : undefined;
|
|
252
|
-
|
|
252
|
+
const submitActionId = form && form.kind === 'form'
|
|
253
|
+
? formSubmitActionId(form, (id) => index.nodes.get(id))
|
|
254
|
+
: undefined;
|
|
255
|
+
if (submitActionId !== undefined && submitActionId === node.actionId) {
|
|
253
256
|
layer.role = 'primary';
|
|
254
257
|
layer.emphasis = 'strong';
|
|
255
258
|
layer.uxRole = 'primary-action';
|
package/dist/ui.d.ts
CHANGED
|
@@ -198,6 +198,18 @@ export declare function isUINode(node: {
|
|
|
198
198
|
* content that never appears at the same time.
|
|
199
199
|
*/
|
|
200
200
|
export declare function primaryChildIds(node: UINode): NodeId[];
|
|
201
|
+
/**
|
|
202
|
+
* The action a form submits, however it was declared.
|
|
203
|
+
*
|
|
204
|
+
* A form may name the action directly (`submitActionId`) or name a declared button that
|
|
205
|
+
* invokes it (`submitButtonId`). Every layer — execution, validation, presentation
|
|
206
|
+
* inference, `AgentAPI` — must resolve it the same way, or a form's primary action means
|
|
207
|
+
* one thing to the renderer and another to an agent.
|
|
208
|
+
*/
|
|
209
|
+
export declare function formSubmitActionId(form: FormNode, lookup: (id: NodeId) => {
|
|
210
|
+
kind: string;
|
|
211
|
+
actionId?: NodeId;
|
|
212
|
+
} | undefined): NodeId | undefined;
|
|
201
213
|
/** Child ids declared by a UI node, in render order. */
|
|
202
214
|
export declare function uiChildIds(node: UINode): NodeId[];
|
|
203
215
|
//# sourceMappingURL=ui.d.ts.map
|
package/dist/ui.js
CHANGED
|
@@ -32,6 +32,24 @@ export function primaryChildIds(node) {
|
|
|
32
32
|
return uiChildIds(node);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The action a form submits, however it was declared.
|
|
37
|
+
*
|
|
38
|
+
* A form may name the action directly (`submitActionId`) or name a declared button that
|
|
39
|
+
* invokes it (`submitButtonId`). Every layer — execution, validation, presentation
|
|
40
|
+
* inference, `AgentAPI` — must resolve it the same way, or a form's primary action means
|
|
41
|
+
* one thing to the renderer and another to an agent.
|
|
42
|
+
*/
|
|
43
|
+
export function formSubmitActionId(form, lookup) {
|
|
44
|
+
if (form.submitActionId) {
|
|
45
|
+
return form.submitActionId;
|
|
46
|
+
}
|
|
47
|
+
if (!form.submitButtonId) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const button = lookup(form.submitButtonId);
|
|
51
|
+
return button?.kind === 'button' ? button.actionId : undefined;
|
|
52
|
+
}
|
|
35
53
|
/** Child ids declared by a UI node, in render order. */
|
|
36
54
|
export function uiChildIds(node) {
|
|
37
55
|
switch (node.kind) {
|
|
@@ -2,7 +2,7 @@ import { VALIDATION_CODES } from './diagnostics.js';
|
|
|
2
2
|
import { ACTION_UX_ROLES, ALIGNMENTS, BOUNDED_SIZES, CONTROL_VARIANTS, DENSITIES, DEVICE_CLASSES, EMPHASIS_LEVELS, ICON_NAMES, JUSTIFICATIONS, LAYOUT_KINDS, PRESENTATION_ROLES, SIZING_VALUES, SPACING_TOKENS, SURFACE_ROLES, TEXT_ROLES, TREATMENTS, HEADING_LEVELS, UX_ROLES, VALUE_FORMAT_KINDS, } from './presentation.js';
|
|
3
3
|
import { isDestructiveAction } from './resolve-presentation.js';
|
|
4
4
|
import { APPEARANCES, RADIUS_TOKENS, SEMANTIC_COLOR_ROLES } from './theme.js';
|
|
5
|
-
import { isUINode, primaryChildIds, uiChildIds } from './ui.js';
|
|
5
|
+
import { formSubmitActionId, isUINode, primaryChildIds, uiChildIds } from './ui.js';
|
|
6
6
|
/** More than this many controls side by side stops being a group and becomes a wall. */
|
|
7
7
|
const HORIZONTAL_ACTION_LIMIT = 5;
|
|
8
8
|
/** Below this a non-wrapping row is not yet a responsive problem. */
|
|
@@ -666,8 +666,11 @@ function checkPrimaryActions(bag, index, resolved) {
|
|
|
666
666
|
continue;
|
|
667
667
|
}
|
|
668
668
|
const primary = new Set();
|
|
669
|
-
if (isForm && node.kind === 'form'
|
|
670
|
-
|
|
669
|
+
if (isForm && node.kind === 'form') {
|
|
670
|
+
const submitActionId = formSubmitActionId(node, (id) => index.nodes.get(id));
|
|
671
|
+
if (submitActionId) {
|
|
672
|
+
primary.add(submitActionId);
|
|
673
|
+
}
|
|
671
674
|
}
|
|
672
675
|
for (const child of descendants(index, node.id)) {
|
|
673
676
|
if (child.kind === 'button' && resolved[child.id]?.uxRole === 'primary-action') {
|
package/dist/validate.js
CHANGED
|
@@ -449,6 +449,13 @@ function validateUiNode(node, context) {
|
|
|
449
449
|
if (node.submitActionId) {
|
|
450
450
|
requireKind(node.submitActionId, 'action', node.id, context, VALIDATION_CODES.invalidActionRef);
|
|
451
451
|
}
|
|
452
|
+
if (!node.submitButtonId && node.submitActionId) {
|
|
453
|
+
// A generated submit button carries no arguments, so an action needing any of them
|
|
454
|
+
// could never be satisfied. Silent omission is what produced ARGUMENT_TYPE_MISMATCH
|
|
455
|
+
// at run time; it is rejected here instead.
|
|
456
|
+
requireArguments(node.submitActionId, {}, node.id, context, `Form ${node.id} submits ${node.submitActionId}, whose generated button cannot supply arguments;` +
|
|
457
|
+
' declare a submit button with submitButtonId and give it');
|
|
458
|
+
}
|
|
452
459
|
if (node.submitButtonId) {
|
|
453
460
|
const button = context.nodes.get(node.submitButtonId);
|
|
454
461
|
if (button?.kind !== 'button') {
|
|
@@ -497,6 +504,7 @@ function validateUiNode(node, context) {
|
|
|
497
504
|
return;
|
|
498
505
|
case 'button': {
|
|
499
506
|
requireKind(node.actionId, 'action', node.id, context, VALIDATION_CODES.invalidActionRef);
|
|
507
|
+
requireArguments(node.actionId, node.arguments ?? {}, node.id, context, `Button ${node.id}`);
|
|
500
508
|
if (typeof node.label !== 'string') {
|
|
501
509
|
validateExpression(node.label, node.id, context, new Set());
|
|
502
510
|
}
|
|
@@ -522,6 +530,28 @@ function validateUiNode(node, context) {
|
|
|
522
530
|
default:
|
|
523
531
|
}
|
|
524
532
|
}
|
|
533
|
+
/**
|
|
534
|
+
* Every required parameter of an action must have an argument. A missing one is statically
|
|
535
|
+
* knowable, and would otherwise surface as a refusal at invocation time — on the authority,
|
|
536
|
+
* for a remote action.
|
|
537
|
+
*/
|
|
538
|
+
function requireArguments(actionId, args, ownerId, context, where) {
|
|
539
|
+
const action = context.nodes.get(actionId);
|
|
540
|
+
if (action?.kind !== 'action') {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const missing = (action.parameters ?? [])
|
|
544
|
+
.filter((parameter) => parameter.required && !(String(parameter.id) in args))
|
|
545
|
+
.map((parameter) => String(parameter.id));
|
|
546
|
+
if (missing.length > 0) {
|
|
547
|
+
context.errors.push({
|
|
548
|
+
code: VALIDATION_CODES.missingActionArgument,
|
|
549
|
+
message: `${where} ${missing.length === 1 ? 'no argument for' : 'no arguments for'} ${missing.join(', ')}`,
|
|
550
|
+
nodeId: ownerId,
|
|
551
|
+
details: { actionId, missing },
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
525
555
|
/** Every UI node beneath this one, for checks that must know what a subtree contains. */
|
|
526
556
|
function uiDescendants(id, context) {
|
|
527
557
|
const found = new Set();
|