diff --git a/docs/examples/item-type-sample/README.md b/docs/examples/item-type-sample/README.md index 631608c..1955cc9 100644 --- a/docs/examples/item-type-sample/README.md +++ b/docs/examples/item-type-sample/README.md @@ -7,7 +7,6 @@ This is a reference layout for adding a new server item type plugin. - `definition.py`: static metadata/defaults/schema constants. - `validator.py`: `validate_update(item, next_params)` normalization and validation. - `actions.py`: `use_item(item, nickname, clock_formatter)` runtime behavior. -- `module.py`: thin exported surface combining the three files. - `plugin.py`: registration payload consumed by plugin auto-discovery. Use this folder as a copy template when creating a real item under: diff --git a/docs/examples/item-type-sample/module.py b/docs/examples/item-type-sample/module.py deleted file mode 100644 index 9b9895e..0000000 --- a/docs/examples/item-type-sample/module.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Counter item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - DEFAULT_PARAMS, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EMIT_RANGE, - EMIT_SOUND, - LABEL, - PROPERTY_METADATA, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, -) -from .validator import validate_update diff --git a/docs/examples/item-type-sample/plugin.py b/docs/examples/item-type-sample/plugin.py index 246d0eb..3f1ca0f 100644 --- a/docs/examples/item-type-sample/plugin.py +++ b/docs/examples/item-type-sample/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "counter", "order": 25, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/docs/item-schema.md b/docs/item-schema.md index 149b1ef..d874637 100644 --- a/docs/item-schema.md +++ b/docs/item-schema.md @@ -49,7 +49,7 @@ - Persisted state stores only instance data. - Global/type-level properties are loaded from server registry in `server/app/item_catalog.py`. -- Per-type use/update validation and message behavior are implemented in per-item modules under `server/app/items/types/*/module.py`, discovered via plugins in `server/app/items/types/*/plugin.py`. +- Per-type use/update validation and message behavior are implemented in per-item modules under `server/app/items/types/*/definition.py`, `validator.py`, and `actions.py`, discovered via plugins in `server/app/items/types/*/plugin.py`. - Client-side add/edit metadata is consumed from `welcome.uiDefinitions` via `client/src/items/itemRegistry.ts` (no local fallback definitions). - End-to-end add-item template: `docs/item-type-template.md`. diff --git a/docs/item-type-template.md b/docs/item-type-template.md index 8c48211..c12b503 100644 --- a/docs/item-type-template.md +++ b/docs/item-type-template.md @@ -11,7 +11,6 @@ When adding a new item type: - `definition.py` for metadata/constants - `validator.py` for `validate_update(item, next_params)` - `actions.py` for `use_item(item, nickname, clock_formatter)` - - `module.py` as thin exported surface - `plugin.py` for registration 2. Server plugin file @@ -20,11 +19,9 @@ When adding a new item type: - `order` - `module` -3. Shared item-type unions -- Add the type in: - - `server/app/models.py` - - `client/src/network/protocol.ts` - - `client/src/state/gameState.ts` +3. Shared type acceptance +- Item type ids are string-based in protocol/state models. +- For generic new types, no enum/union list updates are required. 4. Client runtime behavior (optional) - Default: no item-specific client module needed. @@ -34,14 +31,19 @@ That is enough for a first working item type. ## Reference Sample Folder -See `docs/examples/item-type-sample/` for a complete copyable folder with all five files. +See `docs/examples/item-type-sample/` for a complete copyable folder. -## Minimal `module.py` Example +## Minimal `plugin.py` Example ```py -from .actions import use_item -from .definition import LABEL, TOOLTIP, EDITABLE_PROPERTIES, CAPABILITIES, USE_SOUND, EMIT_SOUND, USE_COOLDOWN_MS, EMIT_RANGE, DIRECTIONAL, DEFAULT_TITLE, DEFAULT_PARAMS, PROPERTY_METADATA -from .validator import validate_update +from ..plugin_helpers import build_item_module +from . import actions, definition, validator + +ITEM_TYPE_PLUGIN = { + "type": "counter", + "order": 25, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), +} ``` ## Checklist Before Commit diff --git a/docs/item-types.md b/docs/item-types.md index 488b009..b46b4a0 100644 --- a/docs/item-types.md +++ b/docs/item-types.md @@ -197,14 +197,12 @@ For a full copy/paste example with plain-English explanation, see `docs/item-typ - `definition.py` (defaults/capabilities/metadata/options) - `validator.py` (`validate_update`) - `actions.py` (`use_item`) - - `module.py` (thin exported surface) 2. Server plugin: add `server/app/items/types//plugin.py` exporting `ITEM_TYPE_PLUGIN` with: - `type` - `order` - `module` The server auto-discovers plugins at boot, so no central registry edit is needed. -3. Server models: extend `ItemType` literals in `server/app/models.py` and any packet enums that list item types. -4. Client protocol/state types: update item-type unions in `client/src/network/protocol.ts` and `client/src/state/gameState.ts`. +3. Server/client protocol/state models are now string-based for item type ids; for generic types no enum/union list updates are required. 5. Client runtime behavior: add `client/src/items/types//behavior.ts` only if custom client runtime is needed (for example piano mode). 6. Tests: add or update server tests under `server/tests/` for use/update validation, unknown-key stripping, and `uiDefinitions` completeness. diff --git a/plans/item-architecture-refactor-plan.md b/plans/item-architecture-refactor-plan.md index cf2d59d..261faf6 100644 --- a/plans/item-architecture-refactor-plan.md +++ b/plans/item-architecture-refactor-plan.md @@ -285,7 +285,8 @@ When adding a new item type: - Missing/invalid schema now disables item menus with explicit status. - Phase 4: - Property editor behavior is metadata-driven by `valueType/range/options/maxLength`. - - `visibleWhen` is supported and item property rows recompute live after updates. + - `visibleWhen` now survives protocol parsing and item property rows recompute live after updates. + - List options are now carried in `propertyMetadata[key].options` (no separate `propertyOptions` map). - Phase 5: - Client runtime behavior remains modular per item via behavior registry; `main.ts` orchestration no longer carries item-specific business branches. - Phase 6: @@ -294,11 +295,16 @@ When adding a new item type: - Save timing now configurable via: - `storage.state_save_debounce_ms` - `storage.state_save_max_delay_ms` + - Additional: + - Added plugin contract tests to validate required item package files/exports. + - Added unknown-item-type rejection for `item_add`. + - Added `capabilities` in `welcome.uiDefinitions.itemTypes`. + - Removed hardcoded client/server item-type enum restrictions (string-based ids + runtime known-type checks). ### Notes - Client item-specific runtime is now reduced to only `piano`; simple items (`dice`, `wheel`, `clock`, `radio_station`, `widget`) run through generic client flows with no custom behavior module. -- Server item implementations now live inside per-type folders (`server/app/items/types/*/module.py`) and plugins point directly to those modules. -- Server type packages are now split into `definition.py` / `validator.py` / `actions.py` plus a thin `module.py` export surface. +- Server item implementations now live inside per-type folders (`server/app/items/types/*/definition.py`, `validator.py`, `actions.py`). +- Plugins now compose module surfaces via a shared helper (`server/app/items/types/plugin_helpers.py`), so per-type `module.py` is no longer required. - Added docs sample folder at `docs/examples/item-type-sample/` and updated template docs to reflect the package layout. ## Completion Review (2026-02-24) @@ -306,64 +312,11 @@ When adding a new item type: - **Phase 0 (docs + guardrails):** ✅ Complete. `docs/item-schema.md` exists and server tests cover UI schema completeness + unknown-key stripping behavior. - **Phase 1 (server package standardization + auto-discovery):** ✅ Complete. Item types are standardized under `server/app/items/types/*` with `plugin.py` entrypoints and discovered by `server/app/items/registry.py`. - **Phase 2 (strict validation + unknown-key stripping):** ✅ Complete. Type validators enforce allowed keys and normalize values; tests verify unknown params are stripped. -- **Phase 3 (client removes fallback authority):** ✅ Mostly complete. Client consumes `welcome.uiDefinitions` and disables item menus when schema is missing. -- **Phase 4 (metadata-driven editor + visibility dependencies):** ⚠️ Partially complete. Generic metadata-driven editing is in place, but `visibleWhen` is not included in the client protocol schema, so dependency visibility rules from server metadata are dropped during message validation. +- **Phase 3 (client removes fallback authority):** ✅ Complete. Client consumes `welcome.uiDefinitions` and disables item menus when schema is missing. +- **Phase 4 (metadata-driven editor + visibility dependencies):** ✅ Complete. Generic metadata-driven editing is in place and `visibleWhen` survives client protocol parsing. - **Phase 5 (behavior registry completion):** ✅ Complete. Runtime behavior is registry-driven with generic path + optional piano module. - **Phase 6 (coalesced persistence):** ✅ Complete. Debounced/max-delay state save and flush-on-shutdown are implemented in server lifecycle. -### Recommendations / Cleanup (for simplest new-item creation) -1. **Unblock `visibleWhen` end-to-end (high priority).** -Scope: -- Carry `visibleWhen` from server metadata through client protocol validation into `itemRegistry`. -Problem today: -- Server emits `visibleWhen`, but client protocol schema drops unknown metadata fields, so dependency visibility can silently fail. -Implementation: -- Update `client/src/network/protocol.ts` `welcome.uiDefinitions.itemTypes[].propertyMetadata` schema to allow `visibleWhen` object values of `string | number | boolean`. -- Keep `client/src/items/itemRegistry.ts` normalization strict (ignore invalid `visibleWhen` entries). -- Add a client test/fixture (or lightweight runtime assertion) that confirms `visibleWhen` survives parse. -Acceptance criteria: -- For a property like `facing` with `visibleWhen: { directional: true }`, toggling `directional` updates property visibility without hardcoded client logic. - -2. **Remove hardcoded item-type literals (high priority).** -Scope: -- Reduce manual edits needed when adding a new item type. -Problem today: -- Type additions still touch multiple literal lists (`Literal[...]`, unions, enums) across server/client protocol/state models. -Implementation: -- Server: - - Keep runtime source of truth in plugin registry. - - Limit literal usage to protocol boundary where needed; validate item type membership against discovered registry set. -- Client: - - Replace rigid item-type enums in parse paths with string + runtime membership checks from server-provided definitions where feasible. - - Keep compile-time unions only where they materially improve safety and can be generated/centralized. -Acceptance criteria: -- Adding a new item plugin does not require editing multiple type-literal lists in unrelated files. -- New type appears in add/edit flows after server metadata update with minimal client changes. - -3. **Include `capabilities` in `welcome.uiDefinitions.itemTypes` (medium).** -Scope: -- Complete server metadata contract for item UI/runtime decisions. -Problem today: -- `capabilities` exist on `WorldItem`, but not consistently in type definition metadata payload for menu/rules decisions. -Implementation: -- Add `capabilities` per item type in server `uiDefinitions.itemTypes[]` payload. -- Parse/store on client registry model. -- Use this metadata for UI gating where applicable (for example, show/hide unsupported actions for a type). -Acceptance criteria: -- Client can decide type-level action affordances from `uiDefinitions` alone, without extra hardcoded assumptions. - -4. **Move list options into per-property metadata (medium).** -Scope: -- Consolidate split item property config sources. -Problem today: -- Options are split between `propertyOptions` and `propertyMetadata`, causing parallel maintenance. -Implementation: -- Server emits list options at `propertyMetadata[key].options`. -- Client reads options from metadata first. -- Remove separate `propertyOptions` map. -Acceptance criteria: -- One canonical place (`propertyMetadata`) defines type, range, tooltip, options, and visibility for each property. -- New list property requires server-only metadata changes for options. - -5. **Eliminate manual property label mapping in client (medium):** either include labels in server metadata or auto-humanize keys so new properties are readable without code changes. -6. **Add a script/check for “new item completeness” (low):** one CI check that verifies plugin discovery, protocol acceptance, docs presence, and test coverage for each discovered type. +### Recommendations / Cleanup (remaining) +1. **Eliminate manual property label mapping in client (medium):** either include labels for all properties in server metadata or auto-humanize keys so new properties are readable without code changes. +2. **Strengthen new-item completeness checks (low):** extend the plugin contract tests to optionally assert docs coverage and richer protocol examples per discovered type. diff --git a/server/app/items/types/clock/__init__.py b/server/app/items/types/clock/__init__.py index 597716d..32cd77b 100644 --- a/server/app/items/types/clock/__init__.py +++ b/server/app/items/types/clock/__init__.py @@ -1,3 +1 @@ -"""Item type package exposing plugin module surface.""" - -from .module import * # noqa: F401,F403 +"""Clock item type plugin package.""" diff --git a/server/app/items/types/clock/module.py b/server/app/items/types/clock/module.py deleted file mode 100644 index f28fbc8..0000000 --- a/server/app/items/types/clock/module.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Clock item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - DEFAULT_PARAMS, - DEFAULT_TIME_ZONE, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EMIT_RANGE, - EMIT_SOUND, - LABEL, - PROPERTY_METADATA, - TIME_ZONE_OPTIONS, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, -) -from .validator import validate_update - -__all__ = [ - "LABEL", - "TOOLTIP", - "EDITABLE_PROPERTIES", - "CAPABILITIES", - "USE_SOUND", - "EMIT_SOUND", - "USE_COOLDOWN_MS", - "EMIT_RANGE", - "DIRECTIONAL", - "DEFAULT_TITLE", - "DEFAULT_TIME_ZONE", - "TIME_ZONE_OPTIONS", - "DEFAULT_PARAMS", - "PROPERTY_METADATA", - "validate_update", - "use_item", -] diff --git a/server/app/items/types/clock/plugin.py b/server/app/items/types/clock/plugin.py index 52d5b6b..267cdbb 100644 --- a/server/app/items/types/clock/plugin.py +++ b/server/app/items/types/clock/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "clock", "order": 10, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/server/app/items/types/dice/__init__.py b/server/app/items/types/dice/__init__.py index 597716d..2e649fd 100644 --- a/server/app/items/types/dice/__init__.py +++ b/server/app/items/types/dice/__init__.py @@ -1,3 +1 @@ -"""Item type package exposing plugin module surface.""" - -from .module import * # noqa: F401,F403 +"""Dice item type plugin package.""" diff --git a/server/app/items/types/dice/module.py b/server/app/items/types/dice/module.py deleted file mode 100644 index 349fb6b..0000000 --- a/server/app/items/types/dice/module.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Dice item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - DEFAULT_PARAMS, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EMIT_RANGE, - EMIT_SOUND, - LABEL, - PROPERTY_METADATA, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, -) -from .validator import validate_update - -__all__ = [ - "LABEL", - "TOOLTIP", - "EDITABLE_PROPERTIES", - "CAPABILITIES", - "USE_SOUND", - "EMIT_SOUND", - "USE_COOLDOWN_MS", - "EMIT_RANGE", - "DIRECTIONAL", - "DEFAULT_TITLE", - "DEFAULT_PARAMS", - "PROPERTY_METADATA", - "validate_update", - "use_item", -] diff --git a/server/app/items/types/dice/plugin.py b/server/app/items/types/dice/plugin.py index 241bddc..1940d27 100644 --- a/server/app/items/types/dice/plugin.py +++ b/server/app/items/types/dice/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "dice", "order": 20, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/server/app/items/types/piano/__init__.py b/server/app/items/types/piano/__init__.py index 597716d..0e1a591 100644 --- a/server/app/items/types/piano/__init__.py +++ b/server/app/items/types/piano/__init__.py @@ -1,3 +1 @@ -"""Item type package exposing plugin module surface.""" - -from .module import * # noqa: F401,F403 +"""Piano item type plugin package.""" diff --git a/server/app/items/types/piano/module.py b/server/app/items/types/piano/module.py deleted file mode 100644 index 29f5251..0000000 --- a/server/app/items/types/piano/module.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Piano item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - DEFAULT_PARAMS, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EMIT_RANGE, - EMIT_SOUND, - INSTRUMENT_OPTIONS, - LABEL, - PROPERTY_METADATA, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, - VOICE_MODE_OPTIONS, -) -from .validator import validate_update - -__all__ = [ - "LABEL", - "TOOLTIP", - "EDITABLE_PROPERTIES", - "CAPABILITIES", - "USE_SOUND", - "EMIT_SOUND", - "USE_COOLDOWN_MS", - "EMIT_RANGE", - "DIRECTIONAL", - "DEFAULT_TITLE", - "DEFAULT_PARAMS", - "PROPERTY_METADATA", - "INSTRUMENT_OPTIONS", - "VOICE_MODE_OPTIONS", - "validate_update", - "use_item", -] diff --git a/server/app/items/types/piano/plugin.py b/server/app/items/types/piano/plugin.py index cd829d2..53ea7fc 100644 --- a/server/app/items/types/piano/plugin.py +++ b/server/app/items/types/piano/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "piano", "order": 30, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/server/app/items/types/plugin_helpers.py b/server/app/items/types/plugin_helpers.py new file mode 100644 index 0000000..fa01d17 --- /dev/null +++ b/server/app/items/types/plugin_helpers.py @@ -0,0 +1,19 @@ +"""Helpers for composing item plugin module surfaces.""" + +from __future__ import annotations + +from types import SimpleNamespace +from typing import Any + + +def build_item_module(definition: Any, *, validate_update: Any, use_item: Any) -> Any: + """Compose a plugin module-like object from split definition/validator/actions files.""" + + exports: dict[str, Any] = { + name: getattr(definition, name) + for name in dir(definition) + if name.isupper() + } + exports["validate_update"] = validate_update + exports["use_item"] = use_item + return SimpleNamespace(**exports) diff --git a/server/app/items/types/radio_station/__init__.py b/server/app/items/types/radio_station/__init__.py index 597716d..a2e9b75 100644 --- a/server/app/items/types/radio_station/__init__.py +++ b/server/app/items/types/radio_station/__init__.py @@ -1,3 +1 @@ -"""Item type package exposing plugin module surface.""" - -from .module import * # noqa: F401,F403 +"""Radio station item type plugin package.""" diff --git a/server/app/items/types/radio_station/module.py b/server/app/items/types/radio_station/module.py deleted file mode 100644 index 9b57e04..0000000 --- a/server/app/items/types/radio_station/module.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Radio item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - CHANNEL_OPTIONS, - DEFAULT_PARAMS, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EFFECT_OPTIONS, - EMIT_RANGE, - EMIT_SOUND, - LABEL, - PROPERTY_METADATA, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, -) -from .validator import validate_update - -__all__ = [ - "LABEL", - "TOOLTIP", - "EDITABLE_PROPERTIES", - "CAPABILITIES", - "USE_SOUND", - "EMIT_SOUND", - "USE_COOLDOWN_MS", - "EMIT_RANGE", - "DIRECTIONAL", - "DEFAULT_TITLE", - "DEFAULT_PARAMS", - "PROPERTY_METADATA", - "CHANNEL_OPTIONS", - "EFFECT_OPTIONS", - "validate_update", - "use_item", -] diff --git a/server/app/items/types/radio_station/plugin.py b/server/app/items/types/radio_station/plugin.py index 487a012..24f714d 100644 --- a/server/app/items/types/radio_station/plugin.py +++ b/server/app/items/types/radio_station/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "radio_station", "order": 40, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/server/app/items/types/wheel/__init__.py b/server/app/items/types/wheel/__init__.py index 597716d..e685937 100644 --- a/server/app/items/types/wheel/__init__.py +++ b/server/app/items/types/wheel/__init__.py @@ -1,3 +1 @@ -"""Item type package exposing plugin module surface.""" - -from .module import * # noqa: F401,F403 +"""Wheel item type plugin package.""" diff --git a/server/app/items/types/wheel/module.py b/server/app/items/types/wheel/module.py deleted file mode 100644 index cd199d3..0000000 --- a/server/app/items/types/wheel/module.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Wheel item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - DEFAULT_PARAMS, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EMIT_RANGE, - EMIT_SOUND, - LABEL, - PROPERTY_METADATA, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, -) -from .validator import validate_update - -__all__ = [ - "LABEL", - "TOOLTIP", - "EDITABLE_PROPERTIES", - "CAPABILITIES", - "USE_SOUND", - "EMIT_SOUND", - "USE_COOLDOWN_MS", - "EMIT_RANGE", - "DIRECTIONAL", - "DEFAULT_TITLE", - "DEFAULT_PARAMS", - "PROPERTY_METADATA", - "validate_update", - "use_item", -] diff --git a/server/app/items/types/wheel/plugin.py b/server/app/items/types/wheel/plugin.py index 8fbc863..15e899d 100644 --- a/server/app/items/types/wheel/plugin.py +++ b/server/app/items/types/wheel/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "wheel", "order": 50, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/server/app/items/types/widget/__init__.py b/server/app/items/types/widget/__init__.py index 597716d..adb3ace 100644 --- a/server/app/items/types/widget/__init__.py +++ b/server/app/items/types/widget/__init__.py @@ -1,3 +1 @@ -"""Item type package exposing plugin module surface.""" - -from .module import * # noqa: F401,F403 +"""Widget item type plugin package.""" diff --git a/server/app/items/types/widget/module.py b/server/app/items/types/widget/module.py deleted file mode 100644 index f52ab8d..0000000 --- a/server/app/items/types/widget/module.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Widget item plugin module surface.""" - -from __future__ import annotations - -from .actions import use_item -from .definition import ( - CAPABILITIES, - DEFAULT_PARAMS, - DEFAULT_TITLE, - DIRECTIONAL, - EDITABLE_PROPERTIES, - EFFECT_OPTIONS, - EMIT_RANGE, - EMIT_SOUND, - LABEL, - PROPERTY_METADATA, - TOOLTIP, - USE_COOLDOWN_MS, - USE_SOUND, -) -from .validator import validate_update - -__all__ = [ - "LABEL", - "TOOLTIP", - "EDITABLE_PROPERTIES", - "CAPABILITIES", - "USE_SOUND", - "EMIT_SOUND", - "USE_COOLDOWN_MS", - "EMIT_RANGE", - "DIRECTIONAL", - "DEFAULT_TITLE", - "DEFAULT_PARAMS", - "PROPERTY_METADATA", - "EFFECT_OPTIONS", - "validate_update", - "use_item", -] diff --git a/server/app/items/types/widget/plugin.py b/server/app/items/types/widget/plugin.py index f0f7fb3..76865af 100644 --- a/server/app/items/types/widget/plugin.py +++ b/server/app/items/types/widget/plugin.py @@ -2,10 +2,11 @@ from __future__ import annotations -from . import module +from ..plugin_helpers import build_item_module +from . import actions, definition, validator ITEM_TYPE_PLUGIN = { "type": "widget", "order": 60, - "module": module, + "module": build_item_module(definition, validate_update=validator.validate_update, use_item=actions.use_item), } diff --git a/server/tests/test_item_plugin_contract.py b/server/tests/test_item_plugin_contract.py index 9bc3f4a..5c68738 100644 --- a/server/tests/test_item_plugin_contract.py +++ b/server/tests/test_item_plugin_contract.py @@ -28,5 +28,4 @@ def test_item_plugin_folders_have_required_files() -> None: assert (type_dir / "definition.py").is_file() assert (type_dir / "validator.py").is_file() assert (type_dir / "actions.py").is_file() - assert (type_dir / "module.py").is_file() assert (type_dir / "plugin.py").is_file()