docs(ext-api): refresh @example blocks + deprecate notify (W17.C/D/E)

- W17.C: refresh 5 stale @example blocks (types.ts, lifecycle.ts, node.ts,
  imperatives.ts ×2) — replace deprecated `async setup()` with
  `setup() { onMounted(...) }`; remove A1-removed `node.getWidget` from
  defineNode + NodeHandle.on migration examples; align imperatives examples
  with W17.B notify-toast verdict.
- W17.D: author ~37 new @example blocks across 9 files for sparse exports
  surfaced by W17.A audit. Pure JSDoc — zero exported-type diff.
- W17.E: add @deprecated JSDoc to notify() + NotifyOptions per
  D-notify-toast-consolidation (W17.B verdict (a) soft-deprecate).

Pure JSDoc — no signature, type, or export changes.
Phase A surface remains FROZEN at ee0537fdb5 (foundation surface SHA
unchanged; only @example / @deprecated text differs).

See research/dashboard-snippet-audit-2026-05-21.md (W17.A) for full
per-edit rationale.
This commit is contained in:
Connor Byrne
2026-05-20 20:07:13 -07:00
parent dd5335df7c
commit 3d09f89251
9 changed files with 256 additions and 24 deletions

View File

@@ -22,6 +22,15 @@ export type Handler<E> = (event: E) => void
* support async handling (currently only `beforeSerialize`).
*
* @typeParam E - The event payload type.
* @example
* ```ts
* import type { AsyncHandler, WidgetBeforeSerializeEvent } from '@comfyorg/extension-api'
*
* const handler: AsyncHandler<WidgetBeforeSerializeEvent> = async (e) => {
* const frame = await captureFrame()
* e.setSerializedValue(frame)
* }
* ```
*/
export type AsyncHandler<E> = (event: E) => void | Promise<void>
@@ -76,12 +85,24 @@ import { getCurrentExtensionInstance } from '@/services/extension-api-service'
export interface GraphEventPayloads {
[event: string]: unknown
}
/**
* See {@link GraphEventPayloads | the augmentation example} —
* augment this interface the same way to narrow `execution.*` payloads.
*/
export interface ExecutionEventPayloads {
[event: string]: unknown
}
/**
* See {@link GraphEventPayloads | the augmentation example} —
* augment this interface the same way to narrow `server.*` payloads.
*/
export interface ServerEventPayloads {
[event: string]: unknown
}
/**
* See {@link GraphEventPayloads | the augmentation example} —
* augment this interface the same way to narrow `workbench.*` payloads.
*/
export interface WorkbenchEventPayloads {
[event: string]: unknown
}
@@ -179,6 +200,21 @@ export const graph: EventNamespace<GraphEventPayloads> = makeNamespace(
*
* @publicAPI
* @stability experimental
* @example
* ```ts
* import { defineExtension, onMounted, execution } from '@comfyorg/extension-api'
*
* defineExtension({
* name: 'my-ext',
* setup() {
* onMounted(() => {
* execution.on('start', (e) => console.log('run started', e))
* execution.on('progress', (e) => console.log('progress', e))
* execution.on('end', () => console.log('run done'))
* })
* }
* })
* ```
*/
export const execution: EventNamespace<ExecutionEventPayloads> = makeNamespace(
(evt) => `execution_${evt}`
@@ -194,6 +230,19 @@ export const execution: EventNamespace<ExecutionEventPayloads> = makeNamespace(
*
* @publicAPI
* @stability experimental
* @example
* ```ts
* import { defineExtension, onMounted, server } from '@comfyorg/extension-api'
*
* defineExtension({
* name: 'my-ext',
* setup() {
* onMounted(() => {
* server.on('reconnected', () => console.log('server back online'))
* })
* }
* })
* ```
*/
export const server: EventNamespace<ServerEventPayloads> = makeNamespace(
(evt) => evt
@@ -209,6 +258,19 @@ export const server: EventNamespace<ServerEventPayloads> = makeNamespace(
*
* @publicAPI
* @stability experimental
* @example
* ```ts
* import { defineExtension, onMounted, workbench } from '@comfyorg/extension-api'
*
* defineExtension({
* name: 'my-ext',
* setup() {
* onMounted(() => {
* workbench.on('notification', (e) => console.log('workbench notif', e))
* })
* }
* })
* ```
*/
export const workbench: EventNamespace<WorkbenchEventPayloads> = makeNamespace(
(evt) => `workbench:${evt}`

View File

@@ -11,6 +11,39 @@
export type { NodeLocatorId, NodeExecutionId } from '@/types/nodeIdentification'
/**
* Node identity round-trip helpers. Create/parse branded `NodeLocatorId` and
* `NodeExecutionId` values, or narrow an `unknown` to one with the type
* guards. Use these instead of raw string manipulation so future changes to
* the identity scheme stay transparent.
*
* @example
* ```ts
* import {
* createNodeLocatorId,
* parseNodeLocatorId,
* isNodeLocatorId,
* createNodeExecutionId,
* parseNodeExecutionId,
* isNodeExecutionId
* } from '@comfyorg/extension-api'
*
* // Construct
* const locator = createNodeLocatorId(graphUuid, localId)
* const execId = createNodeExecutionId(locator, runTag)
*
* // Narrow
* if (isNodeLocatorId(maybe)) {
* const parts = parseNodeLocatorId(maybe)
* console.log(parts.graphUuid, parts.localId)
* }
*
* if (isNodeExecutionId(maybe)) {
* const parts = parseNodeExecutionId(maybe)
* console.log(parts.locator, parts.runTag)
* }
* ```
*/
export {
isNodeLocatorId,
isNodeExecutionId,

View File

@@ -16,8 +16,7 @@
* import {
* defineExtension,
* onMounted,
* toast,
* notify
* toast
* } from '@comfyorg/extension-api'
*
* defineExtension({
@@ -25,7 +24,6 @@
* setup() {
* onMounted(() => {
* toast.show({ severity: 'info', summary: 'Ready' })
* notify({ kind: 'info', message: 'Loaded' })
* })
* }
* })
@@ -44,6 +42,8 @@ import type { ToastMessageOptions } from '@/types/extensionTypes'
* {@link toast}. `kind` maps onto PrimeVue toast severities; `message` maps
* to `summary`; `detail` is optional supplementary text.
*
* @deprecated Use {@link ToastMessageOptions} via `toast.show(...)`. See
* D-notify-toast-consolidation.
* @publicAPI
* @stability experimental
*/
@@ -100,11 +100,21 @@ export const toast: {
*
* Fire-and-forget.
*
* @deprecated Use {@link toast.show} — `notify` is a 1:1 wrapper sharing the
* same transport. See D-notify-toast-consolidation.
* @publicAPI
* @stability experimental
* @example
* ```ts
* notify({ kind: 'error', message: 'Workflow failed', detail: err.message })
* // `notify` is deprecated — prefer `toast.show` directly. See
* // D-notify-toast-consolidation.
* import { toast } from '@comfyorg/extension-api'
*
* toast.show({
* severity: 'error',
* summary: 'Workflow failed',
* detail: err.message
* })
* ```
*/
export function notify(opts: NotifyOptions): void {

View File

@@ -57,19 +57,17 @@ import type {
* ```ts
* import { defineNode } from '@comfyorg/extension-api'
*
* // Assumes the Python `PreviewAny` node declares a hidden, read-only
* // STRING input named `preview` in INPUT_TYPES. Runtime widget addition
* // is forbidden per AXIOMS.md A15 / D-ban-runtime-addwidget — declare in
* // INPUT_TYPES, then bind via getWidget().
* // Per AXIOMS.md §A1, nodes cannot enumerate widgets. To attach
* // per-widget behavior, register a widget type via `defineWidget` and
* // use the mount context's `ctx.widget` handle. This example reacts to
* // node-level execution only.
* export default defineNode({
* name: 'Comfy.PreviewAny',
* nodeTypes: ['PreviewAny'],
* name: 'my-org.executed-logger',
* nodeTypes: ['KSampler'],
*
* nodeCreated(node) {
* const preview = node.getWidget('preview')
* if (!preview) return
* node.on('executed', (e) => {
* preview.setValue(String(e.output['text'] ?? ''))
* console.log('node executed:', e.output)
* })
* }
* })
@@ -183,6 +181,30 @@ export declare function defineWidget(
* setup context) throws in development and silently no-ops in production.
*
* See {@link onMounted} for full usage examples.
*
* @example
* ```ts
* import {
* defineSidebarTab,
* onMounted,
* onUnmounted,
* onActivated,
* onDeactivated
* } from '@comfyorg/extension-api'
*
* defineSidebarTab({
* id: 'my-tab',
* title: 'My Tab',
* type: 'vue',
* component: MyTab,
* setup() {
* onMounted(() => console.log('tab mounted'))
* onActivated(() => console.log('tab shown'))
* onDeactivated(() => console.log('tab hidden'))
* onUnmounted(() => console.log('tab unmounted'))
* }
* })
* ```
*/
export {
onBeforeMount,

View File

@@ -29,6 +29,14 @@ export type { NodeEntityId }
* **Immutable tuple.** Attempts to
* mutate via `node.getPosition()[0] = X` raise a TypeScript error. Use
* {@link NodeHandle.setPosition} to move the node.
*
* @example
* ```ts
* import type { Point } from '@comfyorg/extension-api'
*
* // Per-pixel mouse coordinate from a canvas event
* const cursor: Point = [event.canvasX, event.canvasY]
* ```
*/
export type Point = readonly [x: number, y: number]
@@ -38,6 +46,13 @@ export type Point = readonly [x: number, y: number]
* **Immutable tuple.** Attempts to
* mutate via `node.getSize()[0] = X` raise a TypeScript error. Use
* {@link NodeHandle.setSize} to resize the node.
*
* @example
* ```ts
* import type { Size } from '@comfyorg/extension-api'
*
* const target: Size = [320, 240]
* ```
*/
export type Size = readonly [width: number, height: number]
@@ -368,15 +383,19 @@ export interface NodeHandle {
*
* // AFTER (recommended — widget-level, schema-declared)
* // Declare `_my_state` in the Python node's INPUT_TYPES as a hidden
* // STRING input; the widget will exist automatically. Then attach
* // the serialization transform to the widget:
* const stateWidget = node.getWidget('_my_state')!
* stateWidget.on('beforeSerialize', (e) => {
* e.setSerializedValue(JSON.stringify(computeState()))
* // STRING input. Then attach the serialization transform inside
* // `defineWidget({mount})` — `ctx.widget` is the only legal handle:
* import { defineWidget } from '@comfyorg/extension-api'
*
* defineWidget({
* name: 'my-org.state-shim',
* type: 'STRING',
* mount(_host, ctx) {
* ctx.widget.on('beforeSerialize', (e) => {
* e.setSerializedValue(JSON.stringify(computeState()))
* })
* }
* })
* // Note: runtime widget addition is forbidden per AXIOMS.md A15 /
* // D-ban-runtime-addwidget — declare in INPUT_TYPES, do not call
* // node.addWidget().
* ```
*
* @returns A cleanup function to remove the listener.

View File

@@ -59,6 +59,20 @@ import type {
*
* @publicAPI
* @stability experimental
* @example
* ```ts
* import { defineSidebarTab, type DisposableHandle } from '@comfyorg/extension-api'
*
* const handle: DisposableHandle = defineSidebarTab({
* id: 'my-tab',
* title: 'My Tab',
* type: 'vue',
* component: MyTab
* })
*
* // Later: tear down
* handle.dispose()
* ```
*/
export interface DisposableHandle {
dispose(): void

View File

@@ -17,14 +17,50 @@
// (ExtensionSlot.vue) import them directly from '@/types/extensionTypes'.
export type {
// Pre-existing types (unchanged shape)
/**
* Options bag for {@link defineSidebarTab}.
* @see {@link defineSidebarTab} for a usage example.
*/
SidebarTabExtension,
/**
* Options bag for {@link defineBottomPanelTab}.
* @see {@link defineBottomPanelTab} for a usage example.
*/
BottomPanelExtension,
/**
* Options bag for {@link toast.show} / {@link toast.remove}.
* @see {@link toast} for a usage example.
*/
ToastMessageOptions,
/**
* Manager interface backing the {@link toast} surface.
* @see {@link toast} for a usage example.
*/
ToastManager,
// Net-new shell-UI arg types
/**
* Options bag for {@link defineCommand}.
* @see {@link defineCommand} for a usage example.
*/
CommandDefinition,
/**
* Options bag for {@link defineHotkey}.
* @see {@link defineHotkey} for a usage example.
*/
HotkeyExtension,
/**
* Options bag for {@link defineAboutBadge}.
* @see {@link defineAboutBadge} for a usage example.
*/
AboutBadgeExtension,
/**
* Options bag for {@link defineSetting}.
* @see {@link defineSetting} for a usage example.
*/
SettingDefinition,
/**
* Options bag for {@link defineToolbarButton}.
* @see {@link defineToolbarButton} for a usage example.
*/
ToolbarButtonExtension
} from '@/types/extensionTypes'

View File

@@ -86,12 +86,14 @@ export interface NodeExtensionOptions {
*
* @example
* ```ts
* import { defineExtension } from '@comfyorg/extension-api'
* import { defineExtension, onMounted } from '@comfyorg/extension-api'
*
* export default defineExtension({
* name: 'my-org.my-extension',
* async setup() {
* // App is ready; register commands, sidebar tabs, etc.
* setup() {
* onMounted(() => {
* // App is ready; register commands, sidebar tabs, etc.
* })
* }
* })
* ```

View File

@@ -25,6 +25,15 @@ export type { WidgetEntityId }
/**
* The union of all legal widget scalar values. Complex widgets (DOM, canvas)
* may return their own serializable shapes.
*
* @example
* ```ts
* import type { WidgetValue } from '@comfyorg/extension-api'
*
* // `WidgetValue` is `string | number | boolean | null` — the four
* // primitive-widget value shapes.
* const val: WidgetValue = 42
* ```
*/
export type WidgetValue = string | number | boolean | null
@@ -512,6 +521,20 @@ export interface WidgetHandle<T = WidgetValue> {
* for those.
*
* @stability experimental
* @example
* ```ts
* import { defineWidget, type WidgetCleanup } from '@comfyorg/extension-api'
*
* defineWidget({
* name: 'my-ext',
* type: 'STRING',
* mount(host): WidgetCleanup {
* const input = document.createElement('input')
* host.appendChild(input)
* return () => input.remove()
* }
* })
* ```
*/
export type WidgetCleanup = () => void
@@ -571,6 +594,17 @@ export interface WidgetMountContext {
* remount fires `ctx.onBeforeRemount` / `ctx.onAfterRemount` instead.
*
* @stability experimental
* @example
* ```ts
* import type { WidgetMountFn } from '@comfyorg/extension-api'
*
* const mount: WidgetMountFn = (host, ctx) => {
* const el = document.createElement('div')
* el.textContent = String(ctx.widget.getValue() ?? '')
* host.appendChild(el)
* return () => el.remove()
* }
* ```
*/
export type WidgetMountFn = (
host: HTMLElement,