@bespokeagentics/microdots-host 0.1.2 → 0.2.0
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/package.json +6 -6
- package/src/anchorMount.test.ts +124 -0
- package/src/anchorMount.ts +153 -0
- package/src/fillComposition.ts +1 -1
- package/src/index.ts +56 -3
- package/src/mountedRegistry.test.ts +1 -0
- package/src/mounting.test.ts +23 -0
- package/src/mounting.ts +10 -1
- package/src/placementChecks.test.ts +180 -4
- package/src/placementChecks.ts +132 -7
- package/src/registry.test.ts +289 -0
- package/src/registry.ts +158 -0
- package/src/registryFromManifest.test.ts +126 -0
- package/src/registryFromManifest.ts +92 -0
- package/src/renderLayout.test.ts +263 -0
- package/src/renderLayout.ts +345 -0
- package/src/rules.test.ts +85 -0
- package/src/rules.ts +9 -0
- package/src/slotLayout.test.ts +127 -0
- package/src/slotLayout.ts +113 -0
- package/src/slots.test.ts +37 -3
- package/src/slots.ts +10 -1
- package/src/wire.test.ts +80 -0
- package/src/wire.ts +60 -7
- package/src/wireEngine.test.ts +1 -1
package/src/wire.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ManifestTag,
|
|
7
7
|
} from '@bespokeagentics/microdots-element'
|
|
8
8
|
|
|
9
|
+
import { TopologyRegistryEntry } from './registry.ts'
|
|
9
10
|
import { type RouteTable, makeRouteTable } from './routes.ts'
|
|
10
11
|
// Runtime import of the matcher only — `rules.ts` imports this module
|
|
11
12
|
// type-only, so the emitted module graph stays acyclic.
|
|
@@ -126,11 +127,21 @@ export type PlacementCondition = typeof PlacementCondition.Type
|
|
|
126
127
|
export const PlacementSpan = S.Literals([12, 8, 6, 4, 3])
|
|
127
128
|
export type PlacementSpan = typeof PlacementSpan.Type
|
|
128
129
|
|
|
130
|
+
/**
|
|
131
|
+
* The sentinel `slotId` of a placement pinned to a CSS selector in host-owned
|
|
132
|
+
* DOM instead of a declared slot. An anchor placement carries `selector`; the
|
|
133
|
+
* refinement below makes a half-anchor (either half without the other)
|
|
134
|
+
* unrepresentable, so a hand-edited file fails decode loudly and a mutator's
|
|
135
|
+
* `redecode()` refuses it as `undecodable` — never a silent half-record.
|
|
136
|
+
*/
|
|
137
|
+
export const ANCHOR_SLOT_ID = '@anchor'
|
|
138
|
+
|
|
129
139
|
/**
|
|
130
140
|
* Where one MicroDot element goes — widened in Phase 5 from `{tag, slotId}`
|
|
131
141
|
* only as far as a shipping check or the reading-B resolution needs, per the
|
|
132
|
-
* phase design's table (work item 2)
|
|
133
|
-
*
|
|
142
|
+
* phase design's table (work item 2), and again by the Pages write-mode plan
|
|
143
|
+
* (anchors: `'@anchor'` + `selector`). Every new field is `S.optionalKey`,
|
|
144
|
+
* because the existing `host-topology.json` files must decode unchanged —
|
|
134
145
|
* backward compatibility is a hard requirement, pinned by the Phase-4-shaped
|
|
135
146
|
* decode test in `./wire.test.ts`.
|
|
136
147
|
*
|
|
@@ -145,11 +156,14 @@ export type PlacementSpan = typeof PlacementSpan.Type
|
|
|
145
156
|
* three.
|
|
146
157
|
* - `span` — grid columns (grid slots only).
|
|
147
158
|
* - `order` — stacking order within the slot.
|
|
159
|
+
* - `selector` — the CSS selector an anchor placement pins to. Present
|
|
160
|
+
* exactly when `slotId === ANCHOR_SLOT_ID`.
|
|
148
161
|
*
|
|
149
|
-
* Deliberately NOT here, with their
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
162
|
+
* Deliberately NOT here, with their reasons: `resolved`/`resolvedAt` (check 5
|
|
163
|
+
* needs a crawl that does not exist — an anchor's resolution is reported
|
|
164
|
+
* UNVERIFIABLE, never stored as a guess) and `loading` (no check needs it and
|
|
165
|
+
* the runtime has one strategy — a stored `loading` the loader ignores would
|
|
166
|
+
* be a lie).
|
|
153
167
|
*/
|
|
154
168
|
export const TopologyPlacement = S.Struct({
|
|
155
169
|
id: S.optionalKey(S.String),
|
|
@@ -160,7 +174,17 @@ export const TopologyPlacement = S.Struct({
|
|
|
160
174
|
envs: S.optionalKey(S.Array(WireEnv)),
|
|
161
175
|
span: S.optionalKey(PlacementSpan),
|
|
162
176
|
order: S.optionalKey(S.Number),
|
|
163
|
-
|
|
177
|
+
selector: S.optionalKey(S.String),
|
|
178
|
+
}).pipe(
|
|
179
|
+
S.check(
|
|
180
|
+
S.makeFilter(
|
|
181
|
+
placement =>
|
|
182
|
+
(placement.slotId === ANCHOR_SLOT_ID) === ('selector' in placement) ||
|
|
183
|
+
`an anchor placement carries both slotId "${ANCHOR_SLOT_ID}" and a selector — never one half`,
|
|
184
|
+
{ expected: 'selector present exactly when slotId is "@anchor"' },
|
|
185
|
+
),
|
|
186
|
+
),
|
|
187
|
+
)
|
|
164
188
|
export type TopologyPlacement = typeof TopologyPlacement.Type
|
|
165
189
|
|
|
166
190
|
export const TopologyRoute = S.Struct({
|
|
@@ -244,9 +268,27 @@ export const TopologyHost = S.Struct({
|
|
|
244
268
|
id: S.String,
|
|
245
269
|
label: S.String,
|
|
246
270
|
ownedInputs: S.Array(S.Struct({ name: S.String, type: S.String })),
|
|
271
|
+
/**
|
|
272
|
+
* Who owns this host's layout DOM. Absent means `authored` — hand-written
|
|
273
|
+
* markup wins and `slotDom.ts` only backfills missing containers. Only an
|
|
274
|
+
* explicitly `generated` host renders its slot manifest as its page
|
|
275
|
+
* structure (`renderLayout.ts`) and accepts layout writes from Pages. The
|
|
276
|
+
* marker is HOST-level, not manifest-level, because the gate must be
|
|
277
|
+
* readable when NO manifest exists: `generateLayout` targets exactly that
|
|
278
|
+
* state, and `removeLayout` returns to it (plan ruling D4,
|
|
279
|
+
* `wiki/plans/active/microdots-platform-pages-generated-layout.md`).
|
|
280
|
+
*/
|
|
281
|
+
layout: S.optionalKey(S.Literals(['authored', 'generated'])),
|
|
247
282
|
})
|
|
248
283
|
export type TopologyHost = typeof TopologyHost.Type
|
|
249
284
|
|
|
285
|
+
/** The layout-ownership seam: absent means `authored`. Every gate — the
|
|
286
|
+
* proof host's renderer, the Pages service's layout mutators — reads the
|
|
287
|
+
* mode through here, never `topology.host.layout` directly. */
|
|
288
|
+
export const layoutModeOf = (topology: {
|
|
289
|
+
readonly host: TopologyHost
|
|
290
|
+
}): 'authored' | 'generated' => topology.host.layout ?? 'authored'
|
|
291
|
+
|
|
250
292
|
/**
|
|
251
293
|
* A whole host, as data: who it is, where its elements go, what is wired to
|
|
252
294
|
* what, and which events it merely watches (log-only taps — the demo host's
|
|
@@ -261,6 +303,16 @@ export type TopologyHost = typeof TopologyHost.Type
|
|
|
261
303
|
* - `rules` — route rules resolved by `./rules.ts`. Absent means no rules,
|
|
262
304
|
* and `resolvePlacements` degrades to the identity: exactly the route's own
|
|
263
305
|
* mounts, byte-identical to Phase 4 behaviour.
|
|
306
|
+
*
|
|
307
|
+
* 2026-09-03 adds a third, on the same terms:
|
|
308
|
+
*
|
|
309
|
+
* - `registry` — where to FETCH the bundle for a tag (`./registry.ts`).
|
|
310
|
+
* Absent means the host mounts from its compiled registry alone, exactly as
|
|
311
|
+
* before. Present, it wins per tag, which is what lets a placement for a dot
|
|
312
|
+
* the host was never built with actually mount: until this key existed, a
|
|
313
|
+
* fetched placement naming an unregistered tag reached `findEntry`, got
|
|
314
|
+
* `undefined`, and threw — so placing a MicroDot on a DEPLOYED host meant a
|
|
315
|
+
* human editing `registry.ts` and shipping a host build.
|
|
264
316
|
*/
|
|
265
317
|
export const HostTopology = S.Struct({
|
|
266
318
|
host: TopologyHost,
|
|
@@ -268,6 +320,7 @@ export const HostTopology = S.Struct({
|
|
|
268
320
|
overview: S.optionalKey(TopologyOverview),
|
|
269
321
|
slotManifest: S.optionalKey(HostSlotManifest),
|
|
270
322
|
rules: S.optionalKey(S.Array(RouteRule)),
|
|
323
|
+
registry: S.optionalKey(S.Array(TopologyRegistryEntry)),
|
|
271
324
|
wires: S.Array(Wire),
|
|
272
325
|
watch: S.Array(S.Struct({ event: S.String })),
|
|
273
326
|
})
|
package/src/wireEngine.test.ts
CHANGED
|
@@ -614,7 +614,7 @@ describe('attachWireEngine', () => {
|
|
|
614
614
|
* click sent paths from host A into panes rendering host B. All four wires
|
|
615
615
|
* derive `live`, so the Wiring screen showed nothing wrong.
|
|
616
616
|
*/
|
|
617
|
-
test('two
|
|
617
|
+
test('two MicroDots emitting the same event name drive only the wire whose `from` matches', () => {
|
|
618
618
|
const table = mount('wiring-table')
|
|
619
619
|
const canvas = mount('pages-canvas')
|
|
620
620
|
const inspector = mount('pages-inspector')
|