diff --git a/src/extension-api/node.ts b/src/extension-api/node.ts index eda59f3f0e..05e3b69d5e 100644 --- a/src/extension-api/node.ts +++ b/src/extension-api/node.ts @@ -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::`). * * @stability stable */ -export type NodeEntityId = number & { readonly __brand: 'NodeEntityId' } +import type { NodeEntityId } from '@/world/entityIds' +export type { NodeEntityId } // ─── Geometry ──────────────────────────────────────────────────────────────── diff --git a/src/extension-api/widget.ts b/src/extension-api/widget.ts index a9780a0ce2..b0ebe69161 100644 --- a/src/extension-api/widget.ts +++ b/src/extension-api/widget.ts @@ -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 ──────────────────────────────────────────────────────────── diff --git a/src/extensions/core/dynamicPrompts.v2.ts b/src/extensions/core/dynamicPrompts.v2.ts index d7dacc2ead..a028bb6f94 100644 --- a/src/extensions/core/dynamicPrompts.v2.ts +++ b/src/extensions/core/dynamicPrompts.v2.ts @@ -16,7 +16,7 @@ defineNodeExtension({ if (widget.getOption('dynamicPrompts')) { widget.on('beforeSerialize', (e) => { if (e.context === 'prompt') { - const value = widget.getValue() + const value = widget.getValue() as string e.setSerializedValue( typeof value === 'string' ? processDynamicPrompt(value) : value ) diff --git a/src/services/extension-api-service.ts b/src/services/extension-api-service.ts index 81e2f34d44..8a6fe2b4d3 100644 --- a/src/services/extension-api-service.ts +++ b/src/services/extension-api-service.ts @@ -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(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) => { diff --git a/src/world/widgets/widgetComponents.ts b/src/world/widgets/widgetComponents.ts index 2f270b2e76..f10dedd296 100644 --- a/src/world/widgets/widgetComponents.ts +++ b/src/world/widgets/widgetComponents.ts @@ -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('WidgetComponentContainer') -export const WidgetComponentDisplay = defineComponentKey('WidgetComponentDisplay') -export const WidgetComponentSchema = defineComponentKey('WidgetComponentSchema') -export const WidgetComponentSerialize = defineComponentKey('WidgetComponentSerialize') -export const WidgetComponentValue = defineComponentKey('WidgetComponentValue') +interface WidgetContainerData { + widgetIds: WidgetEntityId[] +} +interface WidgetDisplayData { + label?: string + hidden?: boolean + disabled?: boolean +} +interface WidgetSchemaData { + type?: string + options?: Record +} +interface WidgetSerializeData { + serialize?: boolean +} +interface WidgetValueData { + value?: unknown +} + +export const WidgetComponentContainer = defineComponentKey('WidgetComponentContainer') +export const WidgetComponentDisplay = defineComponentKey('WidgetComponentDisplay') +export const WidgetComponentSchema = defineComponentKey('WidgetComponentSchema') +export const WidgetComponentSerialize = defineComponentKey('WidgetComponentSerialize') +export const WidgetComponentValue = defineComponentKey('WidgetComponentValue') diff --git a/src/world/worldInstance.ts b/src/world/worldInstance.ts index 17b0d2f028..34f3a0c3bb 100644 --- a/src/world/worldInstance.ts +++ b/src/world/worldInstance.ts @@ -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( + entityId: TEntity, + key: ComponentKey + ): TData | undefined + setComponent( + entityId: TEntity, + key: ComponentKey, + data: TData + ): void + removeComponent( + entityId: TEntity, + key: ComponentKey + ): void + entitiesWith( + key: ComponentKey + ): TEntity[] } export function getWorld(): World {