@tmorrow/cre8-wc 2.0.2 → 2.0.4
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/a2ui/catalog.json +5622 -0
- package/a2ui/demo.html +287 -0
- package/a2ui/generate-catalog.mjs +243 -0
- package/a2ui/index.d.ts +4 -0
- package/a2ui/index.js +2 -0
- package/a2ui/index.ts +12 -0
- package/a2ui/registry.d.ts +3 -0
- package/a2ui/registry.js +166 -0
- package/a2ui/registry.ts +182 -0
- package/a2ui/renderer.d.ts +7 -0
- package/a2ui/renderer.js +108 -0
- package/a2ui/renderer.ts +156 -0
- package/a2ui/smoke-test.mjs +238 -0
- package/a2ui/types.d.ts +75 -0
- package/a2ui/types.js +1 -0
- package/a2ui/types.ts +80 -0
- package/cdn/cre8-wc.esm.js +3363 -2861
- package/cdn/cre8-wc.esm.js.map +1 -1
- package/cdn/cre8-wc.min.js +765 -262
- package/cdn/cre8-wc.min.js.map +1 -1
- package/lib/a2ui/index.d.ts +5 -0
- package/lib/a2ui/index.d.ts.map +1 -0
- package/lib/a2ui/index.js +3 -0
- package/lib/a2ui/index.js.map +1 -0
- package/lib/a2ui/registry.d.ts +4 -0
- package/lib/a2ui/registry.d.ts.map +1 -0
- package/lib/a2ui/registry.js +167 -0
- package/lib/a2ui/registry.js.map +1 -0
- package/lib/a2ui/renderer.d.ts +8 -0
- package/lib/a2ui/renderer.d.ts.map +1 -0
- package/lib/a2ui/renderer.js +109 -0
- package/lib/a2ui/renderer.js.map +1 -0
- package/lib/a2ui/types.d.ts +76 -0
- package/lib/a2ui/types.d.ts.map +1 -0
- package/lib/a2ui/types.js +2 -0
- package/lib/a2ui/types.js.map +1 -0
- package/lib/components/icon/icon.d.ts +2 -1
- package/lib/components/icon/icon.d.ts.map +1 -1
- package/lib/components/icon/icon.js +56 -55
- package/lib/components/icon/icon.js.map +1 -1
- package/lib/vite.config.cdn.js +1 -1
- package/lib/vite.config.cdn.js.map +1 -1
- package/lib/vite.config.js +1 -1
- package/lib/vite.config.js.map +1 -1
- package/mcp-manifest.json +2 -2
- package/package.json +15 -1
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { JSDOM } from 'jsdom';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const catalog = JSON.parse(readFileSync(resolve(__dirname, 'catalog.json'), 'utf8'));
|
|
8
|
+
|
|
9
|
+
const dom = new JSDOM('<!doctype html><html><body></body></html>');
|
|
10
|
+
globalThis.document = dom.window.document;
|
|
11
|
+
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
12
|
+
|
|
13
|
+
const { registerCatalog, render, validateSpec } = await import('./index.ts');
|
|
14
|
+
|
|
15
|
+
const cat = registerCatalog(catalog);
|
|
16
|
+
console.log(`loaded catalog "${cat.id}" (${cat.components.size} components)`);
|
|
17
|
+
|
|
18
|
+
const spec = {
|
|
19
|
+
component: 'cre8-card',
|
|
20
|
+
slots: {
|
|
21
|
+
header: [{ component: 'cre8-button', props: { text: 'Close', variant: 'tertiary' } }],
|
|
22
|
+
body: [
|
|
23
|
+
{ component: 'cre8-alert', props: { status: 'info' } },
|
|
24
|
+
{
|
|
25
|
+
component: 'cre8-button-group',
|
|
26
|
+
children: [
|
|
27
|
+
{ component: 'cre8-button', props: { text: 'Save', variant: 'primary' } },
|
|
28
|
+
{ component: 'cre8-button', props: { text: 'Cancel', variant: 'secondary', neutral: true } },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
validateSpec(spec, cat);
|
|
37
|
+
console.log('validation: OK');
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.error('validation failed:', e.message);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const el = render(spec, cat);
|
|
44
|
+
console.log('rendered:\n' + el.outerHTML);
|
|
45
|
+
|
|
46
|
+
const bad = { component: 'cre8-bogus' };
|
|
47
|
+
try {
|
|
48
|
+
validateSpec(bad, cat);
|
|
49
|
+
console.error('FAIL: expected rejection of cre8-bogus');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.log('allowlist reject:', e.message);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const badProp = { component: 'cre8-button', props: { notARealProp: 'x' } };
|
|
56
|
+
try {
|
|
57
|
+
validateSpec(badProp, cat);
|
|
58
|
+
console.error('FAIL: expected rejection of bad prop');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.log('prop reject:', e.message);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const badSlot = {
|
|
65
|
+
component: 'cre8-card',
|
|
66
|
+
slots: { footer: [], notASlot: [] },
|
|
67
|
+
};
|
|
68
|
+
try {
|
|
69
|
+
validateSpec(badSlot, cat);
|
|
70
|
+
console.error('FAIL: expected rejection of unknown slot');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.log('slot reject:', e.message);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const eventSpec = {
|
|
77
|
+
component: 'cre8-button',
|
|
78
|
+
props: { text: 'Save', variant: 'primary' },
|
|
79
|
+
events: {
|
|
80
|
+
click: { handler: 'save-record', stopPropagation: true },
|
|
81
|
+
'text-click': 'emit-telemetry',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const emitted = [];
|
|
86
|
+
const eventEl = render(eventSpec, cat, { onEvent: (e) => emitted.push(e) });
|
|
87
|
+
|
|
88
|
+
eventEl.dispatchEvent(new dom.window.Event('click', { bubbles: true }));
|
|
89
|
+
eventEl.dispatchEvent(new dom.window.CustomEvent('text-click', { detail: { source: 'kbd' } }));
|
|
90
|
+
|
|
91
|
+
if (emitted.length !== 2) {
|
|
92
|
+
console.error('FAIL: expected 2 emitted events, got', emitted.length);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
if (emitted[0].handler !== 'save-record' || emitted[0].event !== 'click') {
|
|
96
|
+
console.error('FAIL: click binding wrong:', emitted[0]);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
if (emitted[1].handler !== 'emit-telemetry' || emitted[1].detail?.source !== 'kbd') {
|
|
100
|
+
console.error('FAIL: custom event binding wrong:', emitted[1]);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
console.log('event binding:', emitted.map((e) => `${e.event}→${e.handler}`).join(', '));
|
|
104
|
+
|
|
105
|
+
const badEventSpec = { component: 'cre8-button', events: { click: { foo: 'bar' } } };
|
|
106
|
+
try {
|
|
107
|
+
validateSpec(badEventSpec, cat);
|
|
108
|
+
console.error('FAIL: expected rejection of missing handler');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.log('event reject:', e.message);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const badEnum = { component: 'cre8-button', props: { variant: 'bogus' } };
|
|
115
|
+
try {
|
|
116
|
+
validateSpec(badEnum, cat);
|
|
117
|
+
console.error('FAIL: expected enum rejection');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.log('enum reject:', e.message);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const badType = { component: 'cre8-button', props: { disabled: 'yes' } };
|
|
124
|
+
try {
|
|
125
|
+
validateSpec(badType, cat);
|
|
126
|
+
console.error('FAIL: expected type rejection');
|
|
127
|
+
process.exit(1);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.log('type reject:', e.message);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const badConst = {
|
|
133
|
+
component: 'cre8-button',
|
|
134
|
+
props: { variant: 'primary' },
|
|
135
|
+
// hand-crafted invalid: component field must match const
|
|
136
|
+
};
|
|
137
|
+
// component const is enforced via the registered component map, but verify
|
|
138
|
+
// explicit const checking works by abusing a spec path we can control.
|
|
139
|
+
// Instead, exercise the union number|string prop (cre8-field.max) with a bad type.
|
|
140
|
+
const badUnion = { component: 'cre8-field', props: { max: true } };
|
|
141
|
+
try {
|
|
142
|
+
validateSpec(badUnion, cat);
|
|
143
|
+
console.error('FAIL: expected union type rejection');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
console.log('union reject:', e.message);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const goodUnionNum = { component: 'cre8-field', props: { max: 10 } };
|
|
150
|
+
const goodUnionStr = { component: 'cre8-field', props: { max: '10' } };
|
|
151
|
+
validateSpec(goodUnionNum, cat);
|
|
152
|
+
validateSpec(goodUnionStr, cat);
|
|
153
|
+
console.log('union accepts string|number: OK');
|
|
154
|
+
|
|
155
|
+
// x-tsType resolver coverage
|
|
156
|
+
validateSpec({ component: 'cre8-chart', props: { type: 'doughnut' } }, cat);
|
|
157
|
+
try {
|
|
158
|
+
validateSpec({ component: 'cre8-chart', props: { type: 'donut' } }, cat);
|
|
159
|
+
console.error('FAIL: expected Cre8ChartType rejection');
|
|
160
|
+
process.exit(1);
|
|
161
|
+
} catch (e) {
|
|
162
|
+
console.log('Cre8ChartType reject:', e.message);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
validateSpec({ component: 'cre8-heading', props: { type: 'meta-small' } }, cat);
|
|
166
|
+
try {
|
|
167
|
+
validateSpec({ component: 'cre8-heading', props: { type: 'title-xxlarge' } }, cat);
|
|
168
|
+
console.error('FAIL: expected heading union rejection');
|
|
169
|
+
process.exit(1);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
console.log('heading-type reject:', e.message);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
validateSpec(
|
|
175
|
+
{
|
|
176
|
+
component: 'cre8-select',
|
|
177
|
+
props: {
|
|
178
|
+
items: [
|
|
179
|
+
{ label: 'One', value: 1 },
|
|
180
|
+
{ optGroupLabel: 'Group', options: [{ label: 'Sub', value: 'sub' }] },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
cat
|
|
185
|
+
);
|
|
186
|
+
console.log('Cre8SelectOption[] accepts mixed union: OK');
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
validateSpec(
|
|
190
|
+
{
|
|
191
|
+
component: 'cre8-select',
|
|
192
|
+
props: { items: [{ label: 'Missing value' }] },
|
|
193
|
+
},
|
|
194
|
+
cat
|
|
195
|
+
);
|
|
196
|
+
console.error('FAIL: expected required-value rejection');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
console.log('select-items required reject:', e.message);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
validateSpec(
|
|
204
|
+
{
|
|
205
|
+
component: 'cre8-select',
|
|
206
|
+
props: { items: [{ label: 'A', value: 1, extra: 'nope' }] },
|
|
207
|
+
},
|
|
208
|
+
cat
|
|
209
|
+
);
|
|
210
|
+
console.error('FAIL: expected additionalProperties rejection');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
console.log('select-items additionalProperties reject:', e.message);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// text-children + x-kind routing
|
|
217
|
+
const textSpec = {
|
|
218
|
+
component: 'cre8-heading',
|
|
219
|
+
props: { type: 'headline-default', tagVariant: 'h2' },
|
|
220
|
+
children: ['Hello world'],
|
|
221
|
+
};
|
|
222
|
+
validateSpec(textSpec, cat);
|
|
223
|
+
const hEl = render(textSpec, cat);
|
|
224
|
+
if (hEl.textContent !== 'Hello world') {
|
|
225
|
+
console.error('FAIL: text child not rendered; got:', hEl.textContent);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
if (hEl.getAttribute('tagVariant') !== null) {
|
|
229
|
+
console.error('FAIL: tagVariant should be set as property, not attribute');
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
if (hEl.tagVariant !== 'h2') {
|
|
233
|
+
console.error('FAIL: tagVariant property not set');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
console.log('text child + x-kind=property routing: OK');
|
|
237
|
+
|
|
238
|
+
console.log('all checks passed');
|
package/a2ui/types.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export type SpecChild = ComponentSpec | string;
|
|
2
|
+
export interface ComponentSpec {
|
|
3
|
+
component: string;
|
|
4
|
+
props?: Record<string, unknown>;
|
|
5
|
+
children?: SpecChild[];
|
|
6
|
+
slots?: Record<string, SpecChild[]>;
|
|
7
|
+
events?: Record<string, EventBinding>;
|
|
8
|
+
}
|
|
9
|
+
export type EventBinding = string | {
|
|
10
|
+
handler: string;
|
|
11
|
+
stopPropagation?: boolean;
|
|
12
|
+
preventDefault?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export interface EmittedEvent {
|
|
15
|
+
component: string;
|
|
16
|
+
path: string;
|
|
17
|
+
event: string;
|
|
18
|
+
handler: string;
|
|
19
|
+
detail: unknown;
|
|
20
|
+
nativeEvent: Event;
|
|
21
|
+
}
|
|
22
|
+
export interface CatalogSchema {
|
|
23
|
+
$id?: string;
|
|
24
|
+
$defs?: {
|
|
25
|
+
components?: Record<string, CatalogComponentDef>;
|
|
26
|
+
};
|
|
27
|
+
'x-a2ui'?: {
|
|
28
|
+
catalogId?: string;
|
|
29
|
+
library?: string;
|
|
30
|
+
libraryVersion?: string;
|
|
31
|
+
tagPrefix?: string;
|
|
32
|
+
framework?: string;
|
|
33
|
+
};
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface CatalogComponentDef {
|
|
37
|
+
title?: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
'x-category'?: string;
|
|
40
|
+
'x-slot-descriptions'?: Record<string, string>;
|
|
41
|
+
'x-events'?: Record<string, {
|
|
42
|
+
detail?: unknown;
|
|
43
|
+
}>;
|
|
44
|
+
properties?: {
|
|
45
|
+
component?: {
|
|
46
|
+
const?: string;
|
|
47
|
+
};
|
|
48
|
+
props?: {
|
|
49
|
+
properties?: Record<string, PropSchema>;
|
|
50
|
+
};
|
|
51
|
+
children?: unknown;
|
|
52
|
+
slots?: {
|
|
53
|
+
properties?: Record<string, unknown>;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface PropSchema {
|
|
58
|
+
type?: string | string[];
|
|
59
|
+
enum?: string[];
|
|
60
|
+
const?: unknown;
|
|
61
|
+
default?: unknown;
|
|
62
|
+
description?: string;
|
|
63
|
+
items?: PropSchema;
|
|
64
|
+
properties?: Record<string, PropSchema>;
|
|
65
|
+
required?: string[];
|
|
66
|
+
additionalProperties?: boolean;
|
|
67
|
+
oneOf?: PropSchema[];
|
|
68
|
+
'x-tsType'?: string;
|
|
69
|
+
'x-kind'?: 'attribute' | 'property';
|
|
70
|
+
}
|
|
71
|
+
export interface RegisteredCatalog {
|
|
72
|
+
id: string;
|
|
73
|
+
schema: CatalogSchema;
|
|
74
|
+
components: Map<string, CatalogComponentDef>;
|
|
75
|
+
}
|
package/a2ui/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/a2ui/types.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type SpecChild = ComponentSpec | string;
|
|
2
|
+
|
|
3
|
+
export interface ComponentSpec {
|
|
4
|
+
component: string;
|
|
5
|
+
props?: Record<string, unknown>;
|
|
6
|
+
children?: SpecChild[];
|
|
7
|
+
slots?: Record<string, SpecChild[]>;
|
|
8
|
+
events?: Record<string, EventBinding>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type EventBinding =
|
|
12
|
+
| string
|
|
13
|
+
| {
|
|
14
|
+
handler: string;
|
|
15
|
+
stopPropagation?: boolean;
|
|
16
|
+
preventDefault?: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface EmittedEvent {
|
|
20
|
+
component: string;
|
|
21
|
+
path: string;
|
|
22
|
+
event: string;
|
|
23
|
+
handler: string;
|
|
24
|
+
detail: unknown;
|
|
25
|
+
nativeEvent: Event;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CatalogSchema {
|
|
29
|
+
$id?: string;
|
|
30
|
+
$defs?: {
|
|
31
|
+
components?: Record<string, CatalogComponentDef>;
|
|
32
|
+
};
|
|
33
|
+
'x-a2ui'?: {
|
|
34
|
+
catalogId?: string;
|
|
35
|
+
library?: string;
|
|
36
|
+
libraryVersion?: string;
|
|
37
|
+
tagPrefix?: string;
|
|
38
|
+
framework?: string;
|
|
39
|
+
};
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CatalogComponentDef {
|
|
44
|
+
title?: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
'x-category'?: string;
|
|
47
|
+
'x-slot-descriptions'?: Record<string, string>;
|
|
48
|
+
'x-events'?: Record<string, { detail?: unknown }>;
|
|
49
|
+
properties?: {
|
|
50
|
+
component?: { const?: string };
|
|
51
|
+
props?: {
|
|
52
|
+
properties?: Record<string, PropSchema>;
|
|
53
|
+
};
|
|
54
|
+
children?: unknown;
|
|
55
|
+
slots?: {
|
|
56
|
+
properties?: Record<string, unknown>;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface PropSchema {
|
|
62
|
+
type?: string | string[];
|
|
63
|
+
enum?: string[];
|
|
64
|
+
const?: unknown;
|
|
65
|
+
default?: unknown;
|
|
66
|
+
description?: string;
|
|
67
|
+
items?: PropSchema;
|
|
68
|
+
properties?: Record<string, PropSchema>;
|
|
69
|
+
required?: string[];
|
|
70
|
+
additionalProperties?: boolean;
|
|
71
|
+
oneOf?: PropSchema[];
|
|
72
|
+
'x-tsType'?: string;
|
|
73
|
+
'x-kind'?: 'attribute' | 'property';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface RegisteredCatalog {
|
|
77
|
+
id: string;
|
|
78
|
+
schema: CatalogSchema;
|
|
79
|
+
components: Map<string, CatalogComponentDef>;
|
|
80
|
+
}
|