mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-23 06:10:32 +00:00
fix(extension-api): unify NodeEntityId/WidgetEntityId brand and tighten World stub
Resolves ~40 pre-existing typecheck failures on the foundation branch
caused by divergent entity-ID brand definitions:
- src/world/entityIds.ts brands as string + brand
- src/extension-api/{node,widget}.ts brands as number + brand
Both are now unified by re-exporting the world-layer brand from the
public API surface (string is canonical because Phase A entity IDs are
formatted like 'node:<graphUuid>:<localId>').
Also:
- World.getComponent / setComponent / removeComponent / entitiesWith are
now properly generic over <TData, TEntity>, so call sites get typed
data back instead of unknown.
- WidgetComponentSchema/Display/Value/Serialize/Container in
src/world/widgets/widgetComponents.ts get real data shapes
(type/options/label/hidden/disabled/value/serialize/widgetIds)
instead of opaque 'object'.
- getMode() casts the int return to NodeMode union.
- Hook result is typed as unknown so the runtime defensive Promise +
setupReturn checks don't trip TS2358 / TS1345.
- dynamicPrompts.v2.ts: widget.getValue<string>() (method-generic does
not exist) → widget.getValue() as string.
Result: 'pnpm typecheck' is now clean on ext-api/i-foundation.
This commit is contained in:
@@ -23,11 +23,14 @@ import type { WidgetHandle, WidgetOptions } from './widget'
|
||||
|
||||
/**
|
||||
* Branded entity ID for nodes. Prevents mixing node IDs with widget IDs
|
||||
* at compile time. The underlying value is always `number`.
|
||||
* at compile time. Re-exported from the world layer so the entire codebase
|
||||
* shares a single brand. The underlying value is `string` in Phase A
|
||||
* (e.g. `node:<graphUuid>:<localId>`).
|
||||
*
|
||||
* @stability stable
|
||||
*/
|
||||
export type NodeEntityId = number & { readonly __brand: 'NodeEntityId' }
|
||||
import type { NodeEntityId } from '@/world/entityIds'
|
||||
export type { NodeEntityId }
|
||||
|
||||
// ─── Geometry ────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -21,11 +21,13 @@ import type { AsyncHandler, Handler, Unsubscribe } from './events'
|
||||
|
||||
/**
|
||||
* Branded entity ID for widgets. Prevents mixing widget IDs with node IDs
|
||||
* at compile time. The underlying value is always `number`.
|
||||
* at compile time. Re-exported from the world layer so the entire codebase
|
||||
* shares a single brand. The underlying value is `string` in Phase A.
|
||||
*
|
||||
* @stability stable
|
||||
*/
|
||||
export type WidgetEntityId = number & { readonly __brand: 'WidgetEntityId' }
|
||||
import type { WidgetEntityId } from '@/world/entityIds'
|
||||
export type { WidgetEntityId }
|
||||
|
||||
// ─── Widget value ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ defineNodeExtension({
|
||||
if (widget.getOption('dynamicPrompts')) {
|
||||
widget.on('beforeSerialize', (e) => {
|
||||
if (e.context === 'prompt') {
|
||||
const value = widget.getValue<string>()
|
||||
const value = widget.getValue() as string
|
||||
e.setSerializedValue(
|
||||
typeof value === 'string' ? processDynamicPrompt(value) : value
|
||||
)
|
||||
|
||||
@@ -42,6 +42,7 @@ import { defineComponentKey } from '@/world/componentKey'
|
||||
|
||||
import type {
|
||||
NodeHandle,
|
||||
NodeMode,
|
||||
SlotInfo,
|
||||
SlotEntityId as PublicSlotEntityId,
|
||||
Point,
|
||||
@@ -287,7 +288,7 @@ function createNodeHandle(nodeId: NodeEntityId): NodeHandle {
|
||||
return world.getComponent(nodeId, NodeVisualKey)?.title ?? ''
|
||||
},
|
||||
getMode() {
|
||||
return world.getComponent(nodeId, ExecutionKey)?.mode ?? 0
|
||||
return (world.getComponent(nodeId, ExecutionKey)?.mode ?? 0) as NodeMode
|
||||
},
|
||||
|
||||
getProperty<T = unknown>(key: string): T | undefined {
|
||||
@@ -552,7 +553,7 @@ export function mountExtensionsForNode(nodeEntityId: NodeEntityId): void {
|
||||
_currentScope = record
|
||||
pauseTracking()
|
||||
try {
|
||||
const result = hook(createNodeHandle(nodeEntityId))
|
||||
const result: unknown = hook(createNodeHandle(nodeEntityId))
|
||||
if (result instanceof Promise) {
|
||||
// Async setup is not supported (D10c) — catch to prevent unhandled rejection
|
||||
result.catch((err) => {
|
||||
|
||||
@@ -2,9 +2,29 @@
|
||||
// Tests mock this module via vi.mock('@/world/widgets/widgetComponents').
|
||||
|
||||
import { defineComponentKey } from '../componentKey'
|
||||
import type { NodeEntityId, WidgetEntityId } from '../entityIds'
|
||||
|
||||
export const WidgetComponentContainer = defineComponentKey<object, unknown>('WidgetComponentContainer')
|
||||
export const WidgetComponentDisplay = defineComponentKey<object, unknown>('WidgetComponentDisplay')
|
||||
export const WidgetComponentSchema = defineComponentKey<object, unknown>('WidgetComponentSchema')
|
||||
export const WidgetComponentSerialize = defineComponentKey<object, unknown>('WidgetComponentSerialize')
|
||||
export const WidgetComponentValue = defineComponentKey<object, unknown>('WidgetComponentValue')
|
||||
interface WidgetContainerData {
|
||||
widgetIds: WidgetEntityId[]
|
||||
}
|
||||
interface WidgetDisplayData {
|
||||
label?: string
|
||||
hidden?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
interface WidgetSchemaData {
|
||||
type?: string
|
||||
options?: Record<string, unknown>
|
||||
}
|
||||
interface WidgetSerializeData {
|
||||
serialize?: boolean
|
||||
}
|
||||
interface WidgetValueData {
|
||||
value?: unknown
|
||||
}
|
||||
|
||||
export const WidgetComponentContainer = defineComponentKey<WidgetContainerData, NodeEntityId>('WidgetComponentContainer')
|
||||
export const WidgetComponentDisplay = defineComponentKey<WidgetDisplayData, WidgetEntityId>('WidgetComponentDisplay')
|
||||
export const WidgetComponentSchema = defineComponentKey<WidgetSchemaData, WidgetEntityId>('WidgetComponentSchema')
|
||||
export const WidgetComponentSerialize = defineComponentKey<WidgetSerializeData, WidgetEntityId>('WidgetComponentSerialize')
|
||||
export const WidgetComponentValue = defineComponentKey<WidgetValueData, WidgetEntityId>('WidgetComponentValue')
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
// Phase A stub — replaced by real ECS world when PR #11939 lands.
|
||||
// Tests mock this module via vi.mock('@/world/worldInstance').
|
||||
|
||||
import type { ComponentKey } from './componentKey'
|
||||
import type { EntityId } from './entityIds'
|
||||
|
||||
export interface World {
|
||||
getComponent(entityId: unknown, key: unknown): unknown
|
||||
setComponent(entityId: unknown, key: unknown, data: unknown): void
|
||||
removeComponent(entityId: unknown, key: unknown): void
|
||||
entitiesWith(key: unknown): unknown[]
|
||||
getComponent<TData, TEntity extends EntityId>(
|
||||
entityId: TEntity,
|
||||
key: ComponentKey<TData, TEntity>
|
||||
): TData | undefined
|
||||
setComponent<TData, TEntity extends EntityId>(
|
||||
entityId: TEntity,
|
||||
key: ComponentKey<TData, TEntity>,
|
||||
data: TData
|
||||
): void
|
||||
removeComponent<TData, TEntity extends EntityId>(
|
||||
entityId: TEntity,
|
||||
key: ComponentKey<TData, TEntity>
|
||||
): void
|
||||
entitiesWith<TData, TEntity extends EntityId>(
|
||||
key: ComponentKey<TData, TEntity>
|
||||
): TEntity[]
|
||||
}
|
||||
|
||||
export function getWorld(): World {
|
||||
|
||||
Reference in New Issue
Block a user