@adia-ai/web-components 0.0.2 → 0.0.3

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 (113) hide show
  1. package/a2ui/index.js +23 -17
  2. package/components/accordion/accordion.js +1 -1
  3. package/components/action-list/action-list.js +1 -1
  4. package/components/agent-artifact/agent-artifact.js +1 -1
  5. package/components/agent-feedback-bar/agent-feedback-bar.js +1 -1
  6. package/components/agent-questions/agent-questions.js +1 -1
  7. package/components/agent-reasoning/agent-reasoning.js +1 -1
  8. package/components/agent-suggestions/agent-suggestions.js +1 -1
  9. package/components/agent-trace/agent-trace.js +1 -1
  10. package/components/alert/alert.js +1 -1
  11. package/components/avatar/avatar.js +1 -1
  12. package/components/badge/badge.js +1 -1
  13. package/components/block/block.js +1 -1
  14. package/components/breadcrumb/breadcrumb.js +1 -1
  15. package/components/button/button.js +2 -2
  16. package/components/calendar-picker/calendar-picker.js +2 -2
  17. package/components/canvas/canvas.js +2 -2
  18. package/components/card/card.js +2 -2
  19. package/components/chart/chart.js +1 -1
  20. package/components/chat/chat-input.js +1 -1
  21. package/components/chat/chat.js +2 -2
  22. package/components/check/check.js +2 -2
  23. package/components/code/code.js +1 -1
  24. package/components/col/col.js +1 -1
  25. package/components/color-picker/color-picker.js +1 -1
  26. package/components/command/command.js +1 -1
  27. package/components/description-list/description-list.js +1 -1
  28. package/components/divider/divider.js +1 -1
  29. package/components/drawer/drawer.js +1 -1
  30. package/components/embed/embed.js +1 -1
  31. package/components/empty-state/empty-state.js +1 -1
  32. package/components/grid/grid.js +1 -1
  33. package/components/heatmap/heatmap.js +1 -1
  34. package/components/icon/icon.js +2 -2
  35. package/components/image/image.js +1 -1
  36. package/components/input/input.js +2 -2
  37. package/components/inspector/inspector.js +2 -2
  38. package/components/kbd/kbd.js +1 -1
  39. package/components/list/list.js +1 -1
  40. package/components/menu/menu.js +2 -2
  41. package/components/modal/modal.js +1 -1
  42. package/components/noodles/noodles.js +1 -1
  43. package/components/otp-input/otp-input.js +1 -1
  44. package/components/pagination/pagination.js +1 -1
  45. package/components/pane/pane.js +1 -1
  46. package/components/pipeline-status/pipeline-status.js +1 -1
  47. package/components/popover/popover.js +2 -2
  48. package/components/progress/progress.js +1 -1
  49. package/components/progress-row/progress-row.js +1 -1
  50. package/components/radio/radio.js +2 -2
  51. package/components/range/range.js +1 -1
  52. package/components/rating/rating.js +1 -1
  53. package/components/richtext/richtext.js +2 -2
  54. package/components/row/row.js +2 -2
  55. package/components/search/search.js +1 -1
  56. package/components/segment/segment.js +1 -1
  57. package/components/segmented/segmented.js +1 -1
  58. package/components/select/select.js +2 -2
  59. package/components/skeleton/skeleton.js +1 -1
  60. package/components/slider/slider.js +1 -1
  61. package/components/stack/stack.js +1 -1
  62. package/components/stat/stat.js +1 -1
  63. package/components/stepper/stepper.js +1 -1
  64. package/components/stream/stream.js +1 -1
  65. package/components/swiper/swiper.js +1 -1
  66. package/components/switch/switch.js +2 -2
  67. package/components/table/table.js +1 -1
  68. package/components/tabs/tab.js +1 -1
  69. package/components/tabs/tabs.js +1 -1
  70. package/components/tag/tag.js +1 -1
  71. package/components/text/text.js +1 -1
  72. package/components/textarea/textarea.js +1 -1
  73. package/components/timeline/timeline.js +1 -1
  74. package/components/toast/toast.js +1 -1
  75. package/components/toggle-group/toggle-group.js +1 -1
  76. package/components/toolbar/toolbar.js +2 -2
  77. package/components/tooltip/tooltip.js +2 -2
  78. package/components/tree/tree.js +1 -1
  79. package/components/upload/upload.js +1 -1
  80. package/core/markdown.js +1 -1
  81. package/core/provider.js +2 -2
  82. package/package.json +7 -3
  83. package/patterns/a2ui-root/a2ui-root.a2ui.json +118 -0
  84. package/{a2ui/root.js → patterns/a2ui-root/a2ui-root.js} +9 -4
  85. package/patterns/a2ui-root/a2ui-root.yaml +76 -0
  86. package/patterns/adia-chat/adia-chat.js +3 -3
  87. package/patterns/adia-editor/adia-editor.js +1 -1
  88. package/patterns/app-nav/app-nav.js +1 -1
  89. package/patterns/app-nav-group/app-nav-group.js +2 -2
  90. package/patterns/app-nav-item/app-nav-item.js +1 -1
  91. package/patterns/app-shell/app-shell.js +1 -1
  92. package/patterns/gen-ui/gen-ui.js +1 -1
  93. package/patterns/index.js +1 -0
  94. package/patterns/section-nav/section-nav.js +1 -1
  95. package/patterns/section-nav-group/section-nav-group.js +1 -1
  96. package/patterns/section-nav-item/section-nav-item.js +1 -1
  97. package/traits/define.js +1 -1
  98. package/a2ui/dockables/action.js +0 -152
  99. package/a2ui/dockables/base.js +0 -30
  100. package/a2ui/dockables/controller.js +0 -97
  101. package/a2ui/dockables/data-source.js +0 -103
  102. package/a2ui/dockables/index.js +0 -6
  103. package/a2ui/dockables/lifecycle.js +0 -84
  104. package/a2ui/dockables/provider.js +0 -59
  105. package/a2ui/manifest-runtime.js +0 -226
  106. package/a2ui/registry.js +0 -200
  107. package/a2ui/renderer.js +0 -361
  108. package/a2ui/stream.js +0 -243
  109. package/a2ui/surface-manifest.js +0 -294
  110. package/a2ui/surface.js +0 -222
  111. package/a2ui/wire-factory.js +0 -134
  112. package/a2ui/wiring-engine.js +0 -209
  113. package/a2ui/wiring-registry.js +0 -342
@@ -1,294 +0,0 @@
1
- /**
2
- * Surface Manifest — Multi-surface relationship document (A008).
3
- *
4
- * Manages a graph of surfaces and their associations:
5
- * routes-to, feeds, shares-context, depends-on, triggers, contains, slots-into
6
- *
7
- * The manifest is the design-time document that describes application topology.
8
- * The runtime reads it to set up navigation, pre-fetch data, and manage
9
- * cross-surface context.
10
- */
11
-
12
- /**
13
- * @typedef {object} SurfaceDescriptor
14
- * @property {string} name — Human-readable name
15
- * @property {string} [route] — URL pattern with :param placeholders
16
- * @property {boolean} [entryPoint] — Can user navigate here directly?
17
- * @property {string[]} [requiredParams] — Params needed to render
18
- * @property {Record<string, object>} [produces] — Data this surface outputs
19
- * @property {Record<string, { keys: string[] }>} [consumes] — Named contexts consumed
20
- * @property {string} [status] — generated | manual | template | placeholder
21
- * @property {string} [generatedBy] — Execution ID from gen-ui pipeline
22
- * @property {string[]} [tags] — Freeform tags
23
- */
24
-
25
- /**
26
- * @typedef {object} Association
27
- * @property {string} type — routes-to | feeds | shares-context | depends-on | triggers | contains | slots-into
28
- * @property {string} from — Source surface ID
29
- * @property {string} to — Target surface ID
30
- * @property {string} [trigger] — What activates this association
31
- * @property {Record<string, object>} [params] — Data passed from source to target
32
- * @property {object} [mapping] — Data flow mapping (for feeds)
33
- * @property {string} [context] — Shared context name (for shares-context)
34
- * @property {string} [condition] — Dependency condition (for depends-on)
35
- * @property {string} [fallback] — Fallback action (for depends-on)
36
- * @property {string} [effect] — Side effect (for triggers)
37
- * @property {string} [slot] — Composition slot (for contains/slots-into)
38
- * @property {number} [position] — Order within slot
39
- * @property {object} [meta] — Freeform metadata
40
- */
41
-
42
- export class SurfaceManifest {
43
- #id;
44
- #name;
45
- #version;
46
- /** @type {Map<string, SurfaceDescriptor>} */
47
- #surfaces = new Map();
48
- /** @type {Association[]} */
49
- #associations = [];
50
- /** @type {Map<string, object>} */
51
- #sharedContexts = new Map();
52
-
53
- /**
54
- * @param {object} opts
55
- * @param {string} opts.id — Manifest ID
56
- * @param {string} opts.name — Human-readable name
57
- * @param {string} [opts.version]
58
- */
59
- constructor({ id, name, version = '1.0.0' }) {
60
- this.#id = id;
61
- this.#name = name;
62
- this.#version = version;
63
- }
64
-
65
- // ── Surface CRUD ──
66
-
67
- /**
68
- * Add or update a surface descriptor.
69
- * @param {string} surfaceId
70
- * @param {SurfaceDescriptor} descriptor
71
- */
72
- addSurface(surfaceId, descriptor) {
73
- this.#surfaces.set(surfaceId, { ...descriptor });
74
- }
75
-
76
- /**
77
- * Remove a surface and all its associations.
78
- * @param {string} surfaceId
79
- */
80
- removeSurface(surfaceId) {
81
- this.#surfaces.delete(surfaceId);
82
- this.#associations = this.#associations.filter(
83
- a => a.from !== surfaceId && a.to !== surfaceId
84
- );
85
- }
86
-
87
- /**
88
- * Get a surface descriptor.
89
- * @param {string} surfaceId
90
- * @returns {SurfaceDescriptor | null}
91
- */
92
- getSurface(surfaceId) {
93
- return this.#surfaces.get(surfaceId) ?? null;
94
- }
95
-
96
- /** @returns {string[]} */
97
- get surfaceIds() {
98
- return [...this.#surfaces.keys()];
99
- }
100
-
101
- // ── Associations ──
102
-
103
- /**
104
- * Add an association between surfaces.
105
- * @param {Association} association
106
- */
107
- addAssociation(association) {
108
- // Validate surfaces exist
109
- if (!this.#surfaces.has(association.from)) {
110
- console.warn(`Manifest: surface "${association.from}" not found`);
111
- return;
112
- }
113
- if (!this.#surfaces.has(association.to)) {
114
- console.warn(`Manifest: surface "${association.to}" not found`);
115
- return;
116
- }
117
-
118
- // Deduplicate: same type + from + to replaces existing
119
- this.#associations = this.#associations.filter(
120
- a => !(a.type === association.type && a.from === association.from && a.to === association.to)
121
- );
122
- this.#associations.push({ ...association });
123
- }
124
-
125
- /**
126
- * Get associations for a surface (outgoing).
127
- * @param {string} surfaceId
128
- * @param {string} [type] — Filter by association type
129
- * @returns {Association[]}
130
- */
131
- getAssociationsFrom(surfaceId, type) {
132
- return this.#associations.filter(
133
- a => a.from === surfaceId && (!type || a.type === type)
134
- );
135
- }
136
-
137
- /**
138
- * Get associations targeting a surface (incoming).
139
- * @param {string} surfaceId
140
- * @param {string} [type]
141
- * @returns {Association[]}
142
- */
143
- getAssociationsTo(surfaceId, type) {
144
- return this.#associations.filter(
145
- a => a.to === surfaceId && (!type || a.type === type)
146
- );
147
- }
148
-
149
- /**
150
- * Get all surfaces sharing a context with the given surface.
151
- * @param {string} surfaceId
152
- * @returns {{ surfaceId: string, context: string }[]}
153
- */
154
- getSharedContextPeers(surfaceId) {
155
- const peers = [];
156
- for (const a of this.#associations) {
157
- if (a.type !== 'shares-context') continue;
158
- if (a.from === surfaceId) peers.push({ surfaceId: a.to, context: a.context });
159
- if (a.to === surfaceId) peers.push({ surfaceId: a.from, context: a.context });
160
- }
161
- return peers;
162
- }
163
-
164
- // ── Shared Contexts ──
165
-
166
- /**
167
- * Define a shared context.
168
- * @param {string} name
169
- * @param {object} config — { shape, source, params }
170
- */
171
- defineSharedContext(name, config) {
172
- this.#sharedContexts.set(name, { ...config });
173
- }
174
-
175
- /**
176
- * Get a shared context definition.
177
- * @param {string} name
178
- * @returns {object | null}
179
- */
180
- getSharedContext(name) {
181
- return this.#sharedContexts.get(name) ?? null;
182
- }
183
-
184
- // ── Validation ──
185
-
186
- /**
187
- * Validate the manifest for common issues.
188
- * @returns {{ valid: boolean, issues: { severity: string, message: string }[] }}
189
- */
190
- validate() {
191
- const issues = [];
192
-
193
- // Check for orphan surfaces (no associations)
194
- for (const id of this.#surfaces.keys()) {
195
- const hasAssoc = this.#associations.some(a => a.from === id || a.to === id);
196
- if (!hasAssoc && this.#surfaces.size > 1) {
197
- issues.push({ severity: 'warning', message: `Surface "${id}" has no associations` });
198
- }
199
- }
200
-
201
- // Check for missing entry points
202
- const entryPoints = [...this.#surfaces.entries()].filter(([, d]) => d.entryPoint);
203
- if (entryPoints.length === 0 && this.#surfaces.size > 0) {
204
- issues.push({ severity: 'warning', message: 'No surface marked as entryPoint' });
205
- }
206
-
207
- // Check depends-on has fallback
208
- for (const a of this.#associations) {
209
- if (a.type === 'depends-on' && !a.fallback) {
210
- issues.push({ severity: 'warning', message: `depends-on from "${a.from}" to "${a.to}" has no fallback` });
211
- }
212
- }
213
-
214
- // Check shares-context references defined contexts
215
- for (const a of this.#associations) {
216
- if (a.type === 'shares-context' && a.context && !this.#sharedContexts.has(a.context)) {
217
- issues.push({ severity: 'error', message: `Shared context "${a.context}" referenced but not defined` });
218
- }
219
- }
220
-
221
- // Check feeds associations have trigger
222
- for (const a of this.#associations) {
223
- if (a.type === 'feeds' && !a.trigger) {
224
- issues.push({ severity: 'warning', message: `feeds from "${a.from}" to "${a.to}" has no trigger` });
225
- }
226
- }
227
-
228
- // Check routes-to targets have routes
229
- for (const a of this.#associations) {
230
- if (a.type === 'routes-to') {
231
- const target = this.#surfaces.get(a.to);
232
- if (target && !target.route) {
233
- issues.push({ severity: 'error', message: `routes-to target "${a.to}" has no route defined` });
234
- }
235
- }
236
- }
237
-
238
- return {
239
- valid: !issues.some(i => i.severity === 'error'),
240
- issues,
241
- };
242
- }
243
-
244
- // ── Serialization ──
245
-
246
- /**
247
- * Export as a JSON-serializable object.
248
- * @returns {object}
249
- */
250
- toJSON() {
251
- return {
252
- $schema: 'https://a2ui.dev/schema/relationships/v1',
253
- id: this.#id,
254
- name: this.#name,
255
- version: this.#version,
256
- surfaces: Object.fromEntries(this.#surfaces),
257
- associations: [...this.#associations],
258
- sharedContexts: Object.fromEntries(this.#sharedContexts),
259
- };
260
- }
261
-
262
- /**
263
- * Import from a JSON object.
264
- * @param {object} json
265
- * @returns {SurfaceManifest}
266
- */
267
- static fromJSON(json) {
268
- const manifest = new SurfaceManifest({
269
- id: json.id,
270
- name: json.name,
271
- version: json.version,
272
- });
273
-
274
- if (json.surfaces) {
275
- for (const [id, desc] of Object.entries(json.surfaces)) {
276
- manifest.addSurface(id, desc);
277
- }
278
- }
279
-
280
- if (json.sharedContexts) {
281
- for (const [name, config] of Object.entries(json.sharedContexts)) {
282
- manifest.defineSharedContext(name, config);
283
- }
284
- }
285
-
286
- if (json.associations) {
287
- for (const assoc of json.associations) {
288
- manifest.addAssociation(assoc);
289
- }
290
- }
291
-
292
- return manifest;
293
- }
294
- }
package/a2ui/surface.js DELETED
@@ -1,222 +0,0 @@
1
- /**
2
- * Surface — A dock host where typed objects connect and disconnect cleanly.
3
- *
4
- * A Surface is the runtime representation of a rendered UI region.
5
- * It holds a data model, resolved params, and a registry of docked objects
6
- * (controllers, data sources, actions, providers, lifecycle hooks).
7
- *
8
- * Dockables attach via dock(surface) and clean up via undock().
9
- * The Surface doesn't know what dockables do internally — it just manages
10
- * their lifecycle and provides a shared context.
11
- */
12
-
13
- // ── Dock order (lowest docks first, undocks last) ──
14
- const DOCK_ORDER = { provider: 0, controller: 1, source: 2, action: 3, lifecycle: 4 };
15
-
16
- /**
17
- * @typedef {object} DockEntry
18
- * @property {import('./dockables/base.js').Dockable} dockable
19
- * @property {Function|null} cleanup — returned from dock()
20
- */
21
-
22
- export class Surface {
23
- /** @type {string} */
24
- surfaceId;
25
-
26
- /** @type {Map<string, DockEntry>} keyed by dockable.id */
27
- #docked = new Map();
28
-
29
- /** @type {object} reactive-ish data model */
30
- #model = {};
31
-
32
- /** @type {object} resolved params (route, store, literal) */
33
- #params = {};
34
-
35
- /** @type {Map<string, Set<Function>>} model watchers keyed by path */
36
- #watchers = new Map();
37
-
38
- /** @type {HTMLElement} surface root element */
39
- #rootElement;
40
-
41
- /** @type {Map<string, HTMLElement>} componentId → DOM element */
42
- #elements;
43
-
44
- /**
45
- * @param {string} surfaceId
46
- * @param {HTMLElement} rootElement — the surface's root DOM node
47
- * @param {Map<string, HTMLElement>} elements — componentId → element map
48
- */
49
- constructor(surfaceId, rootElement, elements) {
50
- this.surfaceId = surfaceId;
51
- this.#rootElement = rootElement;
52
- this.#elements = elements;
53
- }
54
-
55
- // ── Context (passed to dockables) ─────────────────────────
56
-
57
- /** @returns {SurfaceContext} */
58
- get context() {
59
- return {
60
- surfaceId: this.surfaceId,
61
- getElement: (id) => this.#elements.get(id) || null,
62
- getRootElement: () => this.#rootElement,
63
- getModel: (path) => path ? getPath(this.#model, path) : this.#model,
64
- setModel: (path, value) => this.#setModel(path, value),
65
- getDockable: (kind, id) => this.#getDockable(kind, id),
66
- listDockables: (kind) => this.#listDockables(kind),
67
- emit: (adiaEvent) => this.#emit(adiaEvent),
68
- getParam: (key) => this.#params[key],
69
- watchModel: (path, fn) => this.#watchModel(path, fn),
70
- };
71
- }
72
-
73
- // ── Dock Protocol ─────────────────────────────────────────
74
-
75
- /**
76
- * Dock a new object. Calls dockable.dock(context).
77
- * @param {import('./dockables/base.js').Dockable} dockable
78
- */
79
- dock(dockable) {
80
- // If same id already docked, redock (hot-swap)
81
- if (this.#docked.has(dockable.id)) {
82
- this.undock(dockable.id);
83
- }
84
-
85
- const cleanup = dockable.dock(this.context);
86
- this.#docked.set(dockable.id, {
87
- dockable,
88
- cleanup: typeof cleanup === 'function' ? cleanup : null,
89
- });
90
- }
91
-
92
- /**
93
- * Dock multiple objects in dependency order.
94
- * @param {import('./dockables/base.js').Dockable[]} dockables
95
- */
96
- dockAll(dockables) {
97
- const sorted = [...dockables].sort(
98
- (a, b) => (DOCK_ORDER[a.kind] ?? 9) - (DOCK_ORDER[b.kind] ?? 9)
99
- );
100
- for (const d of sorted) this.dock(d);
101
- }
102
-
103
- /**
104
- * Undock by id. Calls cleanup then dockable.undock().
105
- * @param {string} id
106
- */
107
- undock(id) {
108
- const entry = this.#docked.get(id);
109
- if (!entry) return;
110
- entry.cleanup?.();
111
- entry.dockable.undock();
112
- this.#docked.delete(id);
113
- }
114
-
115
- /**
116
- * Undock everything in reverse dependency order.
117
- */
118
- undockAll() {
119
- const entries = [...this.#docked.entries()].sort(
120
- (a, b) => (DOCK_ORDER[b[1].dockable.kind] ?? 9) - (DOCK_ORDER[a[1].dockable.kind] ?? 9)
121
- );
122
- for (const [id] of entries) this.undock(id);
123
- }
124
-
125
- /**
126
- * Undock specific ids.
127
- * @param {string[]} ids
128
- */
129
- undockMany(ids) {
130
- for (const id of ids) this.undock(id);
131
- }
132
-
133
- // ── Model ─────────────────────────────────────────────────
134
-
135
- /** Set initial model state (before docking). */
136
- setInitialModel(model) {
137
- Object.assign(this.#model, model);
138
- }
139
-
140
- /** Set resolved params. */
141
- setParams(params) {
142
- Object.assign(this.#params, params);
143
- }
144
-
145
- /** Update element map (after re-render). */
146
- updateElements(elements) {
147
- this.#elements = elements;
148
- }
149
-
150
- // ── Private ───────────────────────────────────────────────
151
-
152
- #setModel(path, value) {
153
- setPath(this.#model, path, value);
154
- // Notify watchers for this path and any parent paths
155
- for (const [watchPath, fns] of this.#watchers) {
156
- if (path === watchPath || path.startsWith(watchPath + '/')) {
157
- for (const fn of fns) fn(getPath(this.#model, watchPath));
158
- }
159
- }
160
- }
161
-
162
- #watchModel(path, fn) {
163
- if (!this.#watchers.has(path)) this.#watchers.set(path, new Set());
164
- this.#watchers.get(path).add(fn);
165
- return () => {
166
- const set = this.#watchers.get(path);
167
- set?.delete(fn);
168
- if (set?.size === 0) this.#watchers.delete(path);
169
- };
170
- }
171
-
172
- #getDockable(kind, id) {
173
- const entry = this.#docked.get(id);
174
- if (entry && entry.dockable.kind === kind) return entry.dockable;
175
- return null;
176
- }
177
-
178
- #listDockables(kind) {
179
- const result = [];
180
- for (const { dockable } of this.#docked.values()) {
181
- if (!kind || dockable.kind === kind) result.push(dockable);
182
- }
183
- return result;
184
- }
185
-
186
- #emit(adiaEvent) {
187
- const target = adiaEvent.target
188
- ? this.#elements.get(adiaEvent.target)
189
- : this.#rootElement;
190
- if (!target) return;
191
-
192
- target.dispatchEvent(new CustomEvent(adiaEvent.event, {
193
- bubbles: true,
194
- detail: adiaEvent,
195
- }));
196
- }
197
- }
198
-
199
- // ── JSON Pointer helpers (simplified, "/" delimited) ────────
200
-
201
- function getPath(obj, path) {
202
- if (!path || path === '/') return obj;
203
- const keys = path.replace(/^\//, '').split('/');
204
- let current = obj;
205
- for (const key of keys) {
206
- if (current == null) return undefined;
207
- current = current[key];
208
- }
209
- return current;
210
- }
211
-
212
- function setPath(obj, path, value) {
213
- if (!path || path === '/') return;
214
- const keys = path.replace(/^\//, '').split('/');
215
- let current = obj;
216
- for (let i = 0; i < keys.length - 1; i++) {
217
- const key = keys[i];
218
- if (current[key] == null) current[key] = {};
219
- current = current[key];
220
- }
221
- current[keys[keys.length - 1]] = value;
222
- }
@@ -1,134 +0,0 @@
1
- /**
2
- * Wire Factory — Translates wireComponents messages into typed Dockable objects.
3
- *
4
- * This is the bridge between the LLM's declarative JSON and the runtime dock system.
5
- * Each section of the wireComponents message maps to a Dockable type:
6
- *
7
- * data.sources → DataSourceDock[]
8
- * state.controllers → ControllerDock[]
9
- * actions → ActionDock[]
10
- * provides → ProviderDock
11
- * lifecycle → LifecycleDock
12
- * state.model → set directly on Surface
13
- */
14
- import { ControllerDock } from './dockables/controller.js';
15
- import { DataSourceDock } from './dockables/data-source.js';
16
- import { ActionDock } from './dockables/action.js';
17
- import { ProviderDock } from './dockables/provider.js';
18
- import { LifecycleDock } from './dockables/lifecycle.js';
19
- import { resolveController, resolveData, resolveHandler } from './wiring-registry.js';
20
-
21
- /**
22
- * Adapt a registry handler (old ctx shape) to the dock signature (config, surfaceCtx, domEvent).
23
- * The old handlers read from ctx.action.*, ctx.event, ctx.source, ctx.dataModel, ctx.updateModel, etc.
24
- * The new signature passes (config, surfaceCtx, domEventOrResult).
25
- */
26
- function adaptHandler(handlerFn) {
27
- return async (config, surfaceCtx, domEventOrResult) => {
28
- // Build a backward-compatible HandlerContext from the new args
29
- const ctx = {
30
- // Config props spread as "action" (old handlers read ctx.action.path, ctx.action.uri, etc.)
31
- action: config,
32
- // DOM event if present
33
- event: domEventOrResult instanceof Event ? domEventOrResult : null,
34
- source: domEventOrResult instanceof Event ? domEventOrResult.target : null,
35
- // Model access
36
- dataModel: surfaceCtx.getModel(),
37
- updateModel: (path, value) => surfaceCtx.setModel(path, value),
38
- // Controllers lookup
39
- controllers: Object.fromEntries(
40
- surfaceCtx.listDockables('controller').map(d => [d.id, d.controller])
41
- ),
42
- // Params
43
- params: {},
44
- // Data resolver
45
- resolveData,
46
- };
47
- return handlerFn(ctx);
48
- };
49
- }
50
-
51
- /**
52
- * Create a handler resolver function that looks up from the registry
53
- * and wraps with the adapter.
54
- * @returns {(name: string) => Function|null}
55
- */
56
- function createHandlerResolver() {
57
- const cache = new Map();
58
- return (name) => {
59
- if (cache.has(name)) return cache.get(name);
60
- const raw = resolveHandler(name);
61
- if (!raw) return null;
62
- const adapted = adaptHandler(raw);
63
- cache.set(name, adapted);
64
- return adapted;
65
- };
66
- }
67
-
68
- /**
69
- * Create a controller class resolver (async, lazy-loaded).
70
- * @returns {(type: string) => Promise<Function|null>}
71
- */
72
- function createControllerResolver() {
73
- return async (type) => resolveController(type);
74
- }
75
-
76
- /**
77
- * Create a data resolver that uses the wiring registry.
78
- * @returns {(uri: string, ctx: object) => Promise<*>}
79
- */
80
- function createDataResolver() {
81
- return async (uri, ctx) => resolveData(uri, {});
82
- }
83
-
84
- /**
85
- * Translate a wireComponents message into an array of Dockable objects.
86
- *
87
- * @param {object} msg — wireComponents message
88
- * @returns {{ dockables: import('./dockables/base.js').Dockable[], initialModel: object|null }}
89
- */
90
- export function createDockables(msg) {
91
- const dockables = [];
92
- const handlerResolver = createHandlerResolver();
93
- const controllerResolver = createControllerResolver();
94
- const dataResolver = createDataResolver();
95
-
96
- // Data sources
97
- if (msg.data?.sources) {
98
- for (const source of msg.data.sources) {
99
- dockables.push(new DataSourceDock(source, dataResolver));
100
- }
101
- }
102
-
103
- // Controllers
104
- if (msg.state?.controllers) {
105
- for (const ctrl of msg.state.controllers) {
106
- dockables.push(new ControllerDock(ctrl, controllerResolver));
107
- }
108
- }
109
-
110
- // Actions
111
- if (msg.actions) {
112
- for (const action of msg.actions) {
113
- dockables.push(new ActionDock(action, handlerResolver));
114
- }
115
- }
116
-
117
- // Provides
118
- if (msg.provides) {
119
- const provides = Array.isArray(msg.provides) ? msg.provides : [msg.provides];
120
- for (const p of provides) {
121
- dockables.push(new ProviderDock(p));
122
- }
123
- }
124
-
125
- // Lifecycle
126
- if (msg.lifecycle) {
127
- dockables.push(new LifecycleDock(msg.lifecycle, handlerResolver));
128
- }
129
-
130
- // Initial model (not a dockable — set directly on Surface)
131
- const initialModel = msg.state?.model || null;
132
-
133
- return { dockables, initialModel };
134
- }