refactor(ext-api): rename defineNodeExtension → defineNode, defineWidgetExtension → defineWidget

Shorter function names improve ergonomics while maintaining clarity:
- defineNode() - register node-scoped extensions
- defineWidget() - register widget type extensions

Old names kept as deprecated aliases for backwards compatibility.
Will be removed in v1.0.

Updates all docs, examples, tests, and internal references.

Addresses review discussion item #4 from design-review-12142.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Connor Byrne
2026-05-12 18:20:11 -07:00
parent e74250fd8a
commit 8da221b5db
10 changed files with 59 additions and 35 deletions

View File

@@ -7,14 +7,14 @@
* Import directly — no dependency on `window.app` at module evaluation time:
*
* ```ts
* import { defineNodeExtension, defineExtension } from '@comfyorg/extension-api'
* import { defineNode, defineExtension } from '@comfyorg/extension-api'
* ```
*
* ## API surface overview
*
* | Export | Purpose |
* |--------|---------|
* | `defineNodeExtension` | Register a node-scoped extension (the primary entry point) |
* | `defineNode` | Register a node-scoped extension (the primary entry point) |
* | `defineExtension` | Register an app-scoped extension (init, setup, shell UI) |
* | `onNodeMounted`, `onNodeRemoved` | Implicit-context lifecycle hooks (call inside nodeCreated) |
* | `NodeHandle` | Controlled access to node state and events |
@@ -57,10 +57,13 @@ export type {
// ── 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 { defineNodeExtension } from '@comfyorg/extension-api'` works
// so `import { defineNode } from '@comfyorg/extension-api'` works
// at both typecheck and runtime.
export {
defineExtension,
defineNode,
defineWidget,
// Deprecated aliases (remove in v1.0)
defineNodeExtension,
defineWidgetExtension,
startExtensionSystem

View File

@@ -1,5 +1,5 @@
/**
* Extension lifecycle — `defineExtension`, `defineNodeExtension`, and
* Extension lifecycle — `defineExtension`, `defineNode`, and
* the implicit-context lifecycle hooks (`onNodeMounted`, `onNodeRemoved`).
*
* Key behaviors:
@@ -58,9 +58,9 @@ import type {
* @publicAPI
* @example
* ```ts
* import { defineNodeExtension } from '@comfyorg/extension-api'
* import { defineNode } from '@comfyorg/extension-api'
*
* export default defineNodeExtension({
* export default defineNode({
* name: 'Comfy.PreviewAny',
* nodeTypes: ['PreviewAny'],
*
@@ -75,6 +75,11 @@ import type {
* })
* ```
*/
export declare function defineNode(
options: NodeExtensionOptions
): NodeExtensionOptions
/** @deprecated Use `defineNode` instead. Will be removed in v1.0. */
export declare function defineNodeExtension(
options: NodeExtensionOptions
): NodeExtensionOptions
@@ -82,7 +87,7 @@ export declare function defineNodeExtension(
/**
* Register an extension for app-wide lifecycle and shell UI contributions.
*
* Use `defineNodeExtension` for node/widget interactions. Use this for
* Use `defineNode` for node/widget interactions. Use this for
* `init`, `setup`, sidebar tabs, commands, and other app-level concerns.
*
* @publicAPI
@@ -109,6 +114,11 @@ export declare function defineExtension(
* @stability experimental
* @publicAPI
*/
export declare function defineWidget(
options: WidgetExtensionOptions
): WidgetExtensionOptions
/** @deprecated Use `defineWidget` instead. Will be removed in v1.0. */
export declare function defineWidgetExtension(
options: WidgetExtensionOptions
): WidgetExtensionOptions

View File

@@ -192,9 +192,9 @@ export interface NodeBeforeSerializeEvent {
*
* @example
* ```ts
* import { defineNodeExtension } from '@comfyorg/extension-api'
* import { defineNode } from '@comfyorg/extension-api'
*
* export default defineNodeExtension({
* export default defineNode({
* name: 'my-size-enforcer',
* nodeTypes: ['MyCustomNode'],
*

View File

@@ -1,6 +1,6 @@
/**
* Extension option interfaces — the type contracts for `defineNodeExtension`,
* `defineExtension`, and `defineWidgetExtension`.
* Extension option interfaces — the type contracts for `defineNode`,
* `defineExtension`, and `defineWidget`.
*
* Lives in its own module so the runtime service (`@/services/extension-api-service`)
* and the public lifecycle barrel (`@/extension-api/lifecycle`) can both depend on
@@ -15,14 +15,14 @@ import type { NodeHandle } from './node'
import type { WidgetHandle } from './widget'
/**
* Options for `defineNodeExtension`. Describes an extension that reacts to
* Options for `defineNode`. Describes an extension that reacts to
* node lifecycle events.
*
* @example
* ```ts
* import { defineNodeExtension } from '@comfyorg/extension-api'
* import { defineNode } from '@comfyorg/extension-api'
*
* export default defineNodeExtension({
* export default defineNode({
* name: 'my-org.my-extension',
* nodeTypes: ['KSampler'],
*
@@ -118,15 +118,15 @@ export interface ExtensionOptions {
}
/**
* Options for `defineWidgetExtension`. Describes an extension that provides a
* Options for `defineWidget`. Describes an extension that provides a
* custom widget type with its own DOM rendering.
*
* @stability experimental
* @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',
*

View File

@@ -232,9 +232,9 @@ export interface WidgetBeforeQueueEvent {
* @typeParam T - The type of `getValue()` / `setValue()`. Defaults to `WidgetValue`.
* @example
* ```ts
* import { defineNodeExtension } from '@comfyorg/extension-api'
* import { defineNode } from '@comfyorg/extension-api'
*
* export default defineNodeExtension({
* export default defineNode({
* name: 'my-extension',
* nodeCreated(node) {
* const steps = node.getWidget('steps')

View File

@@ -860,7 +860,7 @@ export class ComfyApp {
void useSubgraphStore().fetchSubgraphs()
await useExtensionService().loadExtensions()
// Start the v2 node-extension reactive mount watcher (I-SR.3 / MIG1.E5).
// Must run after loadExtensions() so all defineNodeExtension() calls have
// Must run after loadExtensions() so all defineNode() calls have
// pushed into nodeExtensions[] before the first watcher tick.
startExtensionSystem()

View File

@@ -49,7 +49,7 @@ vi.mock('@/extension-api/lifecycle', () => ({}))
// ── Import service (after mocks are in place) ────────────────────────────────
import {
_clearExtensionsForTesting,
defineNodeExtension,
defineNode,
getCurrentScope,
getScopeRegistry,
mountExtensionsForNode,
@@ -93,7 +93,7 @@ describe('scope-registry — D12 copy/paste reset semantics', () => {
// The extension stores a ref so we can mutate it after mount.
const counters = new Map<NodeEntityId, ReturnType<typeof ref>>()
defineNodeExtension({
defineNode({
name: 'z-counter',
nodeCreated(handle) {
const count = ref(0)
@@ -141,7 +141,7 @@ describe('scope-registry — D12 copy/paste reset semantics', () => {
let setupCallCount = 0
defineNodeExtension({
defineNode({
name: 'a-setup-counter',
nodeCreated() {
setupCallCount++
@@ -175,7 +175,7 @@ describe('scope-registry — D12 copy/paste reset semantics', () => {
const SOURCE_ID = makeNodeId(3)
const CLONE_ID = makeNodeId(4)
defineNodeExtension({
defineNode({
name: 'b-flag',
nodeCreated() {
return { flag: ref(true) }
@@ -220,7 +220,7 @@ describe('currentExtension global slot (D10a) + lifecycle hooks (I-SR.2.B3 / I-S
const NODE_ID = makeNodeId(10)
let scopeDuringSetup: ReturnType<typeof getCurrentScope> = null
defineNodeExtension({
defineNode({
name: 'c-slot-check',
nodeCreated() {
scopeDuringSetup = getCurrentScope()
@@ -238,7 +238,7 @@ describe('currentExtension global slot (D10a) + lifecycle hooks (I-SR.2.B3 / I-S
it('getCurrentScope() is restored to null after setup completes', () => {
const NODE_ID = makeNodeId(11)
defineNodeExtension({
defineNode({
name: 'd-slot-restore',
nodeCreated() {
/* no-op */
@@ -255,7 +255,7 @@ describe('currentExtension global slot (D10a) + lifecycle hooks (I-SR.2.B3 / I-S
const NODE_ID = makeNodeId(12)
const removedCb = vi.fn()
defineNodeExtension({
defineNode({
name: 'e-on-removed',
nodeCreated() {
onNodeRemoved(removedCb)
@@ -299,7 +299,7 @@ describe('I-SR.6 — scope lifecycle invariants', () => {
const NODE_ID = makeNodeId(30)
let setupCount = 0
defineNodeExtension({
defineNode({
name: 'h-once',
nodeCreated() {
setupCount++
@@ -325,7 +325,7 @@ describe('I-SR.6 — scope lifecycle invariants', () => {
const removedCb = vi.fn()
let setupCount = 0
defineNodeExtension({
defineNode({
name: 'i-promotion',
nodeCreated() {
setupCount++
@@ -377,7 +377,7 @@ describe('LoadedFromWorkflow tag routes to correct hook (I-SR.3)', () => {
const created = vi.fn()
const loaded = vi.fn()
defineNodeExtension({
defineNode({
name: 'f-routing',
nodeCreated: created,
loadedGraphNode: loaded
@@ -401,7 +401,7 @@ describe('LoadedFromWorkflow tag routes to correct hook (I-SR.3)', () => {
const created = vi.fn()
const loaded = vi.fn()
defineNodeExtension({
defineNode({
name: 'g-routing',
nodeCreated: created,
loadedGraphNode: loaded

View File

@@ -607,14 +607,20 @@ export function defineExtension(options: ExtensionOptions): void {
appExtensions.push(options)
}
export function defineNodeExtension(options: NodeExtensionOptions): void {
export function defineNode(options: NodeExtensionOptions): void {
nodeExtensions.push(options)
}
export function defineWidgetExtension(options: WidgetExtensionOptions): void {
export function defineWidget(options: WidgetExtensionOptions): void {
widgetExtensions.push(options)
}
/** @deprecated Use `defineNode` instead. Will be removed in v1.0. */
export const defineNodeExtension = defineNode
/** @deprecated Use `defineWidget` instead. Will be removed in v1.0. */
export const defineWidgetExtension = defineWidget
/** @internal Test-only: clear all registered extensions and reset state. */
export function _clearExtensionsForTesting(): void {
nodeExtensions.length = 0

View File

@@ -220,7 +220,7 @@ export const useExtensionService = () => {
_warnedBeforeRegisterNodeDef.add(ext.name)
console.warn(
`[ComfyUI] Extension "${ext.name}" uses deprecated hook "beforeRegisterNodeDef". ` +
'Use defineNodeExtension({ nodeCreated(handle) { ... } }) with a nodeTypes filter instead. ' +
'Use defineNode({ nodeCreated(handle) { ... } }) with a nodeTypes filter instead. ' +
'See https://docs.comfy.org/extensions/api for the v2 API.'
)
}

View File

@@ -21,4 +21,9 @@ export type {
export type { NodeExtensionOptions, ExtensionOptions } from '@/extension-api'
export { defineNodeExtension, defineExtension } from '@/extension-api'
export {
defineNode,
defineExtension,
// Deprecated aliases
defineNodeExtension
} from '@/extension-api'