chore(ext-api): hygiene cleanup — D-design-review-hygiene-cleanup (wave-11)

Closes the (a)+(b) propagation legs for five design-review-12142 rows:

- Row #1 (Comments hygiene): strip 25 HR-style section dividers
  (`// ── XYZ ──`) across widget.ts/node.ts/registrations.ts/events.ts/
  index.ts; strip ~30 sites of decision archaeology refs (D5, D20,
  D-immutability-enforcement (Hybrid C), D-bootstrap-hooks (W6.P6.C),
  D-shell-ui-entrypoints (W6.P5.C), etc.) from per-export JSDoc;
  preserve PHASE_A_EXCLUDED axiom-ref blocks (functional — tells
  axiom-checker + future readers WHY surfaces are absent); preserve
  AXIOMS.md §A1/A14/A15/A16/A12 cross-refs; delete shell.ts
  "What changed in W6.P5" wave-history banner.
- Row #2 (@stability stable): already DONE in code (0 stable hits);
  this ADR closes leg (b).
- Row #4 (apiVersion drop): already DONE in code (0 hits); this ADR
  closes leg (b).
- Row #5 (defineNode/defineWidget short-form): D11 ACCEPTED 2026-05-12;
  one stale JSDoc @example in lifecycle.ts:113,115 fixed
  (defineWidgetExtension → defineWidget).
- Row #6 (Widget-prefixed methods): already DONE via D-bootstrap-hooks
  (unprefixed onMounted/onUnmounted etc.); this ADR closes leg (b).

Also drops two now-unused imports in node.ts (WidgetHandle, WidgetOptions
were only referenced in commented-out A14-deferred surfaces).

ADR: decisions/D-design-review-hygiene-cleanup.md
Net diff: -62 lines, zero behavior change.
Quality gates: lint + format:check + axiom-compliance all green.
This commit is contained in:
Connor Byrne
2026-05-20 16:09:45 -07:00
parent d5d5692928
commit ee0537fdb5
9 changed files with 48 additions and 110 deletions

View File

@@ -37,9 +37,7 @@ export type AsyncHandler<E> = (event: E) => void | Promise<void>
*/
export type Unsubscribe = () => void
// ─────────────────────────────────────────────────────────────────────────────
// D-bootstrap-hooks (W6.P6.C) — Event-namespace facades
// ─────────────────────────────────────────────────────────────────────────────
// Event-namespace facades
//
// Four typed event-namespace handles (`graph` / `execution` / `server` /
// `workbench`) replace the ad-hoc `api.addEventListener('execution_start', ...)`
@@ -206,7 +204,7 @@ export const server: EventNamespace<ServerEventPayloads> = makeNamespace(
*
* Canonical events today: `'notification'`. Future: `'themeChanged'`,
* `'panelToggled'`, `'commandInvoked'`. NOT a DI container — see
* D-bootstrap-hooks §Decision for the "thin event-namespace handle only"
* the bootstrap-hooks design for the "thin event-namespace handle only"
* scope-back.
*
* @publicAPI

View File

@@ -1,6 +1,6 @@
/**
* Inline imperative shell APIs — `toast` + `notify`
* (D-shell-ui-entrypoints, W6.P5.C carve-out).
*.
*
* Per the ACCEPTED PICK (option (ii) "separate entries" with an
* inline-imperative carve-out), `toast` and `notify` are NOT exposed as

View File

@@ -59,7 +59,6 @@ export type {
WidgetExtensionOptions
} from './types'
// ── Registration function implementations ────────────────────────────────────
// Runtime implementations live in the service; the types above are the
// public contract. The barrel re-exports the concrete fns from the service
// so `import { defineNode } from '@comfyorg/extension-api'` works
@@ -76,7 +75,7 @@ export {
export { onNodeMounted, onNodeRemoved } from './lifecycle'
// D-bootstrap-hooks (W6.P6.C) — context-scoped Vue-idiomatic lifecycle hooks
// context-scoped Vue-idiomatic lifecycle hooks
// usable inside `defineExtension.setup` / `defineSidebarTab.setup` /
// `defineBottomPanelTab.setup` bodies.
export {
@@ -87,7 +86,7 @@ export {
onDeactivated
} from './lifecycle'
// D-bootstrap-hooks (W6.P6.C) — four typed event-namespace handles.
// four typed event-namespace handles.
// Payload types default to `unknown` and are tightened via D5 module
// augmentation in a follow-on PR. Custom-node events ride `server.on(...)`.
export { graph, execution, server, workbench } from './events'
@@ -135,7 +134,7 @@ export type { Handler, AsyncHandler, Unsubscribe } from './events'
// ingredients of SidebarTabExtension / BottomPanelExtension and are NOT
// part of the public surface.
//
// Per W6.P5.B (D-shell-ui-entrypoints): ExtensionManager + CommandManager are
// Note: ExtensionManager + CommandManager are
// DROPPED from the public surface — the v2 model uses per-surface defineX
// entries (defineSidebarTab, defineCommand, …) each returning a disposable,
// not a centralized umbrella handle. Internal callers continue importing
@@ -146,7 +145,7 @@ export type {
BottomPanelExtension,
ToastMessageOptions,
ToastManager,
// Net-new W6.P5 arg types
// Shell-UI arg types
CommandDefinition,
HotkeyExtension,
AboutBadgeExtension,
@@ -154,7 +153,7 @@ export type {
ToolbarButtonExtension
} from './shell'
// D-shell-ui-entrypoints (W6.P5.C) — per-surface defineX entries. Each
// per-surface defineX entries. Each
// returns a DisposableHandle; carve-out: toast + notify remain inline
// imperative (exported below from ./imperatives), NOT as defineX wrappers.
export {
@@ -168,7 +167,7 @@ export {
} from './registrations'
export type { DisposableHandle } from './registrations'
// D-shell-ui-entrypoints (W6.P5.C) — inline imperative carve-out. Fire-and-forget;
// inline imperative carve-out. Fire-and-forget;
// no defineX wrapper, no DisposableHandle. Call from any setup() body or
// hook closure.
export { toast, notify } from './imperatives'

View File

@@ -110,9 +110,9 @@ export declare function defineExtension(
* @publicAPI
* @example
* ```ts
* import { defineWidgetExtension } from '@comfyorg/extension-api'
* import { defineWidget } from '@comfyorg/extension-api'
*
* export default defineWidgetExtension({
* export default defineWidget({
* name: 'my-org.color-picker',
* type: 'COLOR_PICKER'
* })
@@ -165,7 +165,7 @@ export declare function defineWidget(
*/
/**
* ## Context-Scoped Bootstrap Hooks (D-bootstrap-hooks, W6.P6.C)
* ## Context-Scoped Bootstrap Hooks
*
* In addition to the node-level `onNodeMounted` / `onNodeRemoved` hooks above,
* the v2 API exposes Vue-idiomatic context-scoped hooks for

View File

@@ -9,7 +9,6 @@
*/
import type { AsyncHandler, Handler, Unsubscribe } from './events'
import type { WidgetHandle, WidgetOptions } from './widget'
import type { NodeEntityId } from '@/world/entityIds'
/**
@@ -27,7 +26,7 @@ export type { NodeEntityId }
/**
* A 2D point as `[x, y]`.
*
* **Immutable tuple per D-immutability-enforcement (Hybrid C).** Attempts to
* **Immutable tuple.** Attempts to
* mutate via `node.getPosition()[0] = X` raise a TypeScript error. Use
* {@link NodeHandle.setPosition} to move the node.
*/
@@ -36,7 +35,7 @@ export type Point = readonly [x: number, y: number]
/**
* A 2D size as `[width, height]`.
*
* **Immutable tuple per D-immutability-enforcement (Hybrid C).** Attempts to
* **Immutable tuple.** Attempts to
* mutate via `node.getSize()[0] = X` raise a TypeScript error. Use
* {@link NodeHandle.setSize} to resize the node.
*/
@@ -138,8 +137,6 @@ export interface NodeBeforeSerializeEvent {
* ```
*/
export interface NodeHandle {
// ── IDENTITY ──────────────────────────────────────────────────────────────
/**
* Opaque identifier for this node. Stable for the lifetime of the node
* entity. Treat as a string token: do not parse, slice, or compare its
@@ -147,7 +144,7 @@ export interface NodeHandle {
* another handle.
*
* @remarks
* Per D20, the underlying value is a branded `NodeEntityId` at runtime
* The underlying value is a branded `NodeEntityId` at runtime
* but is narrowed to `string` on the public surface so authors never
* need to import a brand to type a local variable.
*/
@@ -176,26 +173,21 @@ export interface NodeHandle {
*/
readonly comfyClass: string
// ── SPATIAL STATE ─────────────────────────────────────────────────────────
// PHASE_A_EXCLUDED per AXIOMS.md A14: Deferred pending A13 coord-space stabilization.
// getPosition(): Point
// setPosition(pos: Point): void
// getSize(): Size
// setSize(size: Size): void
// ── VISUAL STATE ──────────────────────────────────────────────────────────
// PHASE_A_EXCLUDED per AXIOMS.md A14: Uncertain use case.
// getTitle(): string
// setTitle(title: string): void
// isSelected(): boolean
// ── EXECUTION MODE ────────────────────────────────────────────────────────
// PHASE_A_EXCLUDED per AXIOMS.md A14: nodeMode has "egregious" use patterns.
// getMode(): NodeMode
// setMode(mode: NodeMode): void
// ── PROPERTIES (migration shim) ───────────────────────────────────────────
/**
* Returns a per-node-instance property by key.
*
@@ -221,9 +213,7 @@ export interface NodeHandle {
*/
setProperty(key: string, value: unknown): void
// ── WIDGETS ───────────────────────────────────────────────────────────────
// COMMENTED OUT per AXIOMS.md A1 + A2 (D-no-node-widget-access, 2026-05-19):
// COMMENTED OUT per AXIOMS.md A1 + A2:
// Nodes cannot reference or enumerate their widgets. Bilateral (node→widget)
// direction is closed; the widget→node direction (`widget.parentNode`)
// remains the sole channel. Extensions needing per-widget coordination
@@ -246,7 +236,7 @@ export interface NodeHandle {
// /**
// * Returns all widgets on this node as `WidgetHandle` instances.
// *
// * **Immutable view per D-immutability-enforcement (Hybrid C).** The returned
// * **Immutable view.** The returned
// * array cannot be mutated (`push`, `splice`, `length =`, index assignment
// * all raise TS errors). Each `WidgetHandle` is also surface-frozen — use
// * the `WidgetHandle` setter methods (`setValue`, `setHidden`, etc.) to
@@ -276,18 +266,15 @@ export interface NodeHandle {
// hook against a per-widget host `<div>` it owns. See `WidgetMountFn` and
// `WidgetMountContext` in `./widget` for the lifecycle contract.
// ── SLOTS ─────────────────────────────────────────────────────────────────
/**
* Returns all input slots on this node.
*
* **Immutable view per D-immutability-enforcement (Hybrid C).** The returned
* **Immutable view.** The returned
* array and each slot are `Readonly` — `node.getInputs().push(...)`,
* `node.getInputs()[i] = X`, and `node.getInputs()[i].name = "x"` all raise
* TypeScript errors at compile time. Per-slot mutators (`setInputName`,
* `replaceInput`, bulk field setters) are tracked under
* W6.P8.UNMIGRATABLE / D-input-output-shape.
*
* *
* @example
* ```ts
* // ❌ TS-ERR — readonly array; v1 patterns no longer compile
@@ -303,28 +290,26 @@ export interface NodeHandle {
/**
* Returns all output slots on this node.
*
* **Immutable view per D-immutability-enforcement (Hybrid C).** Same
* **Immutable view.** Same
* read-only semantics as {@link NodeHandle.getInputs}. Per-slot mutators
* tracked under W6.P8.UNMIGRATABLE / D-input-output-shape.
* tracked separately.
*/
getOutputs(): ReadonlyArray<Readonly<SlotInfo>>
/**
* @deprecated Use {@link NodeHandle.getInputs} instead. Renamed to align
* with the `getX()` accessor convention (D11/D-immutability-enforcement).
* with the `getX()` accessor convention.
* Will be removed in v1.0.
*/
inputs(): ReadonlyArray<Readonly<SlotInfo>>
/**
* @deprecated Use {@link NodeHandle.getOutputs} instead. Renamed to align
* with the `getX()` accessor convention (D11/D-immutability-enforcement).
* with the `getX()` accessor convention.
* Will be removed in v1.0.
*/
outputs(): ReadonlyArray<Readonly<SlotInfo>>
// ── EVENTS ────────────────────────────────────────────────────────────────
/**
* Subscribe to node removal (graph deletion, not subgraph promotion).
*

View File

@@ -1,6 +1,6 @@
/**
* Per-surface shell UI registration entry points
* (D-shell-ui-entrypoints, W6.P5.C).
*.
*
* Each `defineX` function in this module is the v2 replacement for one slot
* of the v1 `app.registerExtension({ commands, keybindings, settings, … })`
@@ -64,10 +64,7 @@ export interface DisposableHandle {
dispose(): void
}
// ─────────────────────────────────────────────────────────────────────────────
// Internal mount-queue infrastructure
// ─────────────────────────────────────────────────────────────────────────────
/**
* Registration side effect: registers something into a store and returns a
* cleanup. May be async (most registrations dynamic-import their store at
@@ -198,10 +195,7 @@ export function _clearShellRegistrationsForTesting(): void {
_systemStarted = false
}
// ─────────────────────────────────────────────────────────────────────────────
// Public defineX entry points
// ─────────────────────────────────────────────────────────────────────────────
/**
* Register a sidebar tab. Returns a {@link DisposableHandle} — call
* `handle.dispose()` to remove the tab.

View File

@@ -2,44 +2,26 @@
* Shell UI extension types — sidebar tabs, bottom panels, commands, hotkeys,
* settings, about badges, toolbar buttons, toasts.
*
* Re-exported from `src/types/extensionTypes.ts` with no shape changes. The
* original module remains the source of truth; this barrel makes the shell
* types available from the single `@comfyorg/extension-api` package entry
* point.
*
* ## What changed in W6.P5 (D-shell-ui-entrypoints)
*
* The v2 public surface follows option (ii) "separate entries" — every
* registerable shell UI surface gets its own `defineX` entry point in
* `@/extension-api/registrations`, and the **arg types** for those entries
* are re-exported here. `ExtensionManager` and `CommandManager` are no
* longer part of the public API (they were v1 umbrella handles); per-surface
* disposable handles returned by each `defineX` replace them.
*
* Toast + notification surfaces remain inline-imperative (see
* `@/extension-api/imperatives`) — the carve-out per the ACCEPTED ADR.
* Re-exported from `src/types/extensionTypes.ts` with no shape changes. Each
* registerable shell UI surface has its own `defineX` entry point in
* `@/extension-api/registrations`; the arg types for those entries are
* re-exported here. Toast + notification surfaces remain inline-imperative
* (see `@/extension-api/imperatives`).
*
* @packageDocumentation
*/
// VueExtension and CustomExtension are deliberately NOT re-exported per D19
// they are discriminated-union ingredients of SidebarTabExtension /
// VueExtension and CustomExtension are intentionally NOT re-exported — they
// are discriminated-union ingredients of SidebarTabExtension /
// BottomPanelExtension, not author-facing entry points. Internal callers
// (ExtensionSlot.vue) import them directly from '@/types/extensionTypes'.
//
// Per W6.P5.B (D-shell-ui-entrypoints) the public surface adds 5 net-new
// argument types (HotkeyExtension, AboutBadgeExtension, SettingDefinition,
// ToolbarButtonExtension, CommandDefinition) and DROPS ExtensionManager +
// CommandManager from the v2 public re-export — the legacy umbrella handles
// have no role under option (ii) since each defineX returns its own
// per-surface disposable.
export type {
// Pre-existing types (unchanged shape)
SidebarTabExtension,
BottomPanelExtension,
ToastMessageOptions,
ToastManager,
// Net-new W6.P5 arg types
// Net-new shell-UI arg types
CommandDefinition,
HotkeyExtension,
AboutBadgeExtension,

View File

@@ -107,7 +107,7 @@ export interface ExtensionOptions {
* Runs once during app initialization (after the app is mounted but before
* the first workflow is loaded). Equivalent to the v1 `ComfyExtension.init`.
*
* @deprecated Per D-bootstrap-hooks (W6.P6.C, ACCEPTED 2026-05-14): move the
* @deprecated move the
* `init` body into `setup()`. The body of `setup()` runs at the same point
* `init` used to run (early lifecycle); use `onMounted(() => ...)` inside
* `setup()` for what `init` did via late-lifecycle assumptions. A codemod
@@ -121,7 +121,7 @@ export interface ExtensionOptions {
* to the v1 `ComfyExtension.setup`. Safe to call shell UI registration APIs
* (`ExtensionManager`, `CommandManager`) here.
*
* @deprecated Per D-bootstrap-hooks (W6.P6.C, ACCEPTED 2026-05-14): the
* @deprecated the
* `setup` property name is retained, but the v1 semantic "fires after all
* core extensions ready" now lives in `onMounted(() => ...)` *inside* the
* `setup()` body. The `setup()` body itself now runs at the earlier

View File

@@ -74,7 +74,7 @@ export interface WidgetOptionChangeEvent {
readonly newValue: unknown
}
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16 (D-widget-serialization-simplification, wave-9):
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16:
// `WidgetPropertyChangeEvent` is vacuous after `'serialize'` was removed from
// the property union (A16: authors cannot disable serialization). The other
// historical members ('hidden', 'disabled') were already A14-deferred.
@@ -126,7 +126,7 @@ export interface WidgetOptionChangeEvent {
* ```
*/
export interface WidgetBeforeSerializeEvent<T = WidgetValue> {
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16 (D-widget-serialization-simplification, wave-9):
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16:
// The 4-way transport discriminator inverted the direction of knowledge
// flow — framework owns transport, extensions own value. Workflow JSON
// and API prompt converge to a single serialized payload; clone and
@@ -151,7 +151,7 @@ export interface WidgetBeforeSerializeEvent<T = WidgetValue> {
*/
setSerializedValue(v: unknown): void
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16 (D-widget-serialization-simplification, wave-9):
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16:
// `skip()` IS a per-call disable — authors cannot disable serialization
// (A16). If a widget should not contribute to the payload, it should not
// be a widget (A15). Restoration requires axiom amendments to A15 + A16.
@@ -215,7 +215,7 @@ export interface WidgetBeforeQueueEvent {
* ```ts
* import { defineWidget } from '@comfyorg/extension-api'
*
* // Per AXIOMS.md A1 (D-no-node-widget-access, 2026-05-19), nodes cannot
* // Per AXIOMS.md A1, nodes cannot
* // enumerate or reference widgets — `node.getWidget(name)` was removed.
* // To react to a specific widget's lifecycle and value changes, register
* // a widget type and use the `mount` context's `ctx.widget` handle:
@@ -232,8 +232,6 @@ export interface WidgetBeforeQueueEvent {
* ```
*/
export interface WidgetHandle<T = WidgetValue> {
// ── IDENTITY ───────────────────────────────────────────────────────────────
/**
* Opaque identifier for this widget. Stable for the lifetime of the
* widget entity. Treat as a string token: do not parse, slice, or compare
@@ -241,7 +239,7 @@ export interface WidgetHandle<T = WidgetValue> {
* another handle.
*
* @remarks
* Per D20, the underlying value is a branded `WidgetEntityId` at runtime
* The underlying value is a branded `WidgetEntityId` at runtime
* but is narrowed to `string` on the public surface so authors never need
* to import a brand to type a local variable.
*/
@@ -269,8 +267,6 @@ export interface WidgetHandle<T = WidgetValue> {
*/
readonly widgetType: string
// ── VALUE — first-class, every-widget ─────────────────────────────────────
/**
* Returns the widget's current user-edited value.
*
@@ -278,8 +274,7 @@ export interface WidgetHandle<T = WidgetValue> {
* @example
* ```ts
* // Inside `defineWidget({mount})` — `ctx.widget` is the only legal
* // path to a `WidgetHandle` (D-no-node-widget-access removed
* // `node.getWidget(name)`).
* // path to a `WidgetHandle` (nodes cannot enumerate widgets per A1).
* const value = widget.getValue<number>()
* ```
*/
@@ -292,18 +287,14 @@ export interface WidgetHandle<T = WidgetValue> {
*/
setValue(value: T): void
// ── VISIBILITY — first-class, every-widget ────────────────────────────────
// PHASE_A_EXCLUDED per AXIOMS.md A14: Deferred pending serialization convergence.
// isHidden(): boolean
// setHidden(hidden: boolean): void
// ── DISABLED — first-class, every-widget ─────────────────────────────────
// PHASE_A_EXCLUDED per AXIOMS.md A14: Deferred pending serialization convergence.
// isDisabled(): boolean
// setDisabled(disabled: boolean): void
// ── LABEL — first-class, every-widget ────────────────────────────────────
/**
* The widget's display label shown to the user. Defaults to the widget name.
* Read-only invariant (set at creation, never changes after).
@@ -313,8 +304,6 @@ export interface WidgetHandle<T = WidgetValue> {
*/
readonly label: string
// ── HEIGHT — reserved layout slot for mount-lifecycle widgets ────────────
/**
* Updates the reserved height for this widget and triggers a node relayout.
*
@@ -332,7 +321,7 @@ export interface WidgetHandle<T = WidgetValue> {
*/
setHeight(px: number): void
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16 (D-widget-serialization-simplification, wave-9):
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16:
// Authors cannot disable serialization at the widget level (A16). If a
// widget should not contribute to the serialized payload, it should not
// be a widget (A15) — use a boxed/composed widget (BBOX-style), a
@@ -344,12 +333,10 @@ export interface WidgetHandle<T = WidgetValue> {
// isSerializeEnabled(): boolean
// setSerializeEnabled(enabled: boolean): void
// ── OPTIONS BAG — type-specific overrides ─────────────────────────────────
/**
* Read-only snapshot of the full options bag for this widget.
*
* **Immutable per D-immutability-enforcement (Hybrid C).** The returned
* **Immutable.** The returned
* object is `Readonly<WidgetOptions>` — `widget.options.min = 0`,
* `widget.options = {...}`, and `widget.options.values = [...]` all raise
* TypeScript errors at compile time. To mutate, use
@@ -360,7 +347,7 @@ export interface WidgetHandle<T = WidgetValue> {
* §A16, `serialize` is no longer a writable option (and no longer a key
* on this bag) — there is no widget-level serialization disable.
* `widget.options.values = [...]` (combo refresh) migrates to a future
* `setValues` mutator (tracked under W6.P8.UNMIGRATABLE).
* `setValues` mutator.
*
* @example
* ```ts
@@ -408,17 +395,15 @@ export interface WidgetHandle<T = WidgetValue> {
*/
setOption(key: string, value: unknown): void
// ── SERIALIZE VALUE — read-only accessor; D5 is the write path ───────────
/**
* The widget's current `serializeValue` function (or `undefined` if none is
* registered).
*
* **Accessor-only per D-immutability-enforcement (Hybrid C).** The setter
* **Accessor-only.** The setter
* intentionally does not exist on the public type — assignment
* (`widget.serializeValue = fn`) raises a TypeScript error. The v2
* migration target is the {@link WidgetHandle.on | `on('beforeSerialize', fn)`}
* event (per D5), which is typed, async-capable, and composable across
* event, which is typed, async-capable, and composable across
* multiple extensions on the same widget.
*
* @deprecated v1 callers reading `widget.serializeValue` to invoke the
@@ -443,8 +428,6 @@ export interface WidgetHandle<T = WidgetValue> {
*/
readonly serializeValue: ((...args: unknown[]) => unknown) | undefined
// ── EVENTS ────────────────────────────────────────────────────────────────
/**
* Subscribe to the widget's value changes.
*
@@ -472,7 +455,7 @@ export interface WidgetHandle<T = WidgetValue> {
handler: Handler<WidgetOptionChangeEvent>
): Unsubscribe
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16 (D-widget-serialization-simplification, wave-9):
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16:
// `WidgetPropertyChangeEvent` is vacuous — the only property the event
// ever surfaced was `'serialize'`, which is gone per A16. `setHidden` /
// `setDisabled` were already A14-deferred. Restoration requires a new
@@ -521,8 +504,6 @@ export interface WidgetHandle<T = WidgetValue> {
): Unsubscribe
}
// ── MOUNT LIFECYCLE — the sole DOM seam per D-widget-converge / Axiom A12 ──
/**
* Cleanup function returned from a widget's `mount()`. Fires exactly once,
* when the widget entity is destroyed. **Does NOT fire on host remount**
@@ -612,10 +593,9 @@ export interface WidgetOptions {
hidden?: boolean
/** If `true`, the widget is rendered read-only (no user editing). */
readonly?: boolean
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16 (D-widget-serialization-simplification, wave-9):
// PHASE_A_EXCLUDED per AXIOMS.md A14 + A16:
// `serialize` contradicted A16 even as a read-only key — there is no
// widget-level serialization disable. Already write-blocked by
// D-immutability-enforcement; now removed from the type entirely.
// widget-level serialization disable. Removed from the type entirely.
//
// serialize?: boolean
/** Display label override. Defaults to the widget `name`. */