@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.
Files changed (46) hide show
  1. package/a2ui/catalog.json +5622 -0
  2. package/a2ui/demo.html +287 -0
  3. package/a2ui/generate-catalog.mjs +243 -0
  4. package/a2ui/index.d.ts +4 -0
  5. package/a2ui/index.js +2 -0
  6. package/a2ui/index.ts +12 -0
  7. package/a2ui/registry.d.ts +3 -0
  8. package/a2ui/registry.js +166 -0
  9. package/a2ui/registry.ts +182 -0
  10. package/a2ui/renderer.d.ts +7 -0
  11. package/a2ui/renderer.js +108 -0
  12. package/a2ui/renderer.ts +156 -0
  13. package/a2ui/smoke-test.mjs +238 -0
  14. package/a2ui/types.d.ts +75 -0
  15. package/a2ui/types.js +1 -0
  16. package/a2ui/types.ts +80 -0
  17. package/cdn/cre8-wc.esm.js +3363 -2861
  18. package/cdn/cre8-wc.esm.js.map +1 -1
  19. package/cdn/cre8-wc.min.js +765 -262
  20. package/cdn/cre8-wc.min.js.map +1 -1
  21. package/lib/a2ui/index.d.ts +5 -0
  22. package/lib/a2ui/index.d.ts.map +1 -0
  23. package/lib/a2ui/index.js +3 -0
  24. package/lib/a2ui/index.js.map +1 -0
  25. package/lib/a2ui/registry.d.ts +4 -0
  26. package/lib/a2ui/registry.d.ts.map +1 -0
  27. package/lib/a2ui/registry.js +167 -0
  28. package/lib/a2ui/registry.js.map +1 -0
  29. package/lib/a2ui/renderer.d.ts +8 -0
  30. package/lib/a2ui/renderer.d.ts.map +1 -0
  31. package/lib/a2ui/renderer.js +109 -0
  32. package/lib/a2ui/renderer.js.map +1 -0
  33. package/lib/a2ui/types.d.ts +76 -0
  34. package/lib/a2ui/types.d.ts.map +1 -0
  35. package/lib/a2ui/types.js +2 -0
  36. package/lib/a2ui/types.js.map +1 -0
  37. package/lib/components/icon/icon.d.ts +2 -1
  38. package/lib/components/icon/icon.d.ts.map +1 -1
  39. package/lib/components/icon/icon.js +56 -55
  40. package/lib/components/icon/icon.js.map +1 -1
  41. package/lib/vite.config.cdn.js +1 -1
  42. package/lib/vite.config.cdn.js.map +1 -1
  43. package/lib/vite.config.js +1 -1
  44. package/lib/vite.config.js.map +1 -1
  45. package/mcp-manifest.json +2 -2
  46. 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');
@@ -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
+ }