@idosgames/module-sdk 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- 'use strict';var x=/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*@[1-9]\d*$/;function S(e){return typeof e=="string"&&x.test(e)}function M(e){let t=e.indexOf(":");return t<0?"":e.slice(0,t)}var c=/^(string|number|boolean)(\[\])?(\?)?$/;function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,t){return e==="number"?typeof t=="number"&&Number.isFinite(t):typeof t===e}function f(e,t,r){if(!u(t))return `${r||"payload"} must be an object`;for(let[n,o]of Object.entries(e)){let i=r?`${r}.${n}`:n,a=t[n];if(typeof o!="string"){let p=f(o,a,i);if(p)return p;continue}let d=c.exec(o);if(!d)return `${i}: invalid descriptor "${o}"`;let[,s="",g,m]=d;if(a===void 0){if(m)continue;return `${i} is missing`}if(g){if(!Array.isArray(a))return `${i} must be ${s}[]`;if(!a.every(p=>l(s,p)))return `${i} must contain only ${s} values`;continue}if(!l(s,a))return `${i} must be a ${s}`}return null}function y(e,t){for(let[r,n]of Object.entries(e)){let o=t?`${t}.${r}`:r;if(typeof n=="string"){if(!c.test(n))throw new Error(`shape(): field "${o}" has invalid type "${n}" \u2014 use "string" | "number" | "boolean", optionally with "[]" and/or a trailing "?"`)}else if(u(n))y(n,o);else throw new Error(`shape(): field "${o}" must be a type string or an object`)}}function h(e){return y(e,""),{descriptor:e,check:t=>f(e,t,"")}}function v(e,t){if(!S(e))throw new Error(`defineTopic(): "${e}" is not a valid topic name \u2014 expected "<module-id>:<event>@<major>", e.g. "idle-rpg:character-upgraded@1"`);return {name:e,shape:t}}var T={modeChanged:v("host:mode-changed@1",h({from:"string?",to:"string"}))};var b=["currency-bar","wallet","status","account","menu","modes"];function R(e){return typeof e=="string"&&b.includes(e)}function D(e){return e}exports.SHARED_UI_ROLES=b;exports.TOPIC_NAME_RE=x;exports.defineModule=D;exports.defineTopic=v;exports.hostTopics=T;exports.isSharedUiRole=R;exports.isTopicName=S;exports.shape=h;exports.topicNamespace=M;
1
+ 'use strict';var x=/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*@[1-9]\d*$/;function S(e){return typeof e=="string"&&x.test(e)}function M(e){let t=e.indexOf(":");return t<0?"":e.slice(0,t)}var c=/^(string|number|boolean)(\[\])?(\?)?$/;function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,t){return e==="number"?typeof t=="number"&&Number.isFinite(t):typeof t===e}function f(e,t,o){if(!u(t))return `${o||"payload"} must be an object`;for(let[r,n]of Object.entries(e)){let i=o?`${o}.${r}`:r,a=t[r];if(typeof n!="string"){let p=f(n,a,i);if(p)return p;continue}let d=c.exec(n);if(!d)return `${i}: invalid descriptor "${n}"`;let[,s="",g,m]=d;if(a===void 0){if(m)continue;return `${i} is missing`}if(g){if(!Array.isArray(a))return `${i} must be ${s}[]`;if(!a.every(p=>l(s,p)))return `${i} must contain only ${s} values`;continue}if(!l(s,a))return `${i} must be a ${s}`}return null}function y(e,t){for(let[o,r]of Object.entries(e)){let n=t?`${t}.${o}`:o;if(typeof r=="string"){if(!c.test(r))throw new Error(`shape(): field "${n}" has invalid type "${r}" \u2014 use "string" | "number" | "boolean", optionally with "[]" and/or a trailing "?"`)}else if(u(r))y(r,n);else throw new Error(`shape(): field "${n}" must be a type string or an object`)}}function h(e){return y(e,""),{descriptor:e,check:t=>f(e,t,"")}}function v(e,t){if(!S(e))throw new Error(`defineTopic(): "${e}" is not a valid topic name \u2014 expected "<module-id>:<event>@<major>", e.g. "idle-rpg:character-upgraded@1"`);return {name:e,shape:t}}var T={modeChanged:v("host:mode-changed@1",h({from:"string?",to:"string"}))};var b=["currency-bar","wallet","status","account","menu","modes"];function R(e){return typeof e=="string"&&b.includes(e)}function D(e){return e}exports.SHARED_UI_ROLES=b;exports.TOPIC_NAME_RE=x;exports.defineModule=D;exports.defineTopic=v;exports.hostTopics=T;exports.isSharedUiRole=R;exports.isTopicName=S;exports.shape=h;exports.topicNamespace=M;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { RealtimeSession, IDosGamesClient } from '@idosgames/core';
1
+ import { RealtimeSession, VoiceController, IDosGamesClient } from '@idosgames/core';
2
2
  export { IDosGamesClient, SdkEvents } from '@idosgames/core';
3
3
  import { ComponentType } from 'react';
4
4
 
@@ -232,6 +232,13 @@ interface FeatureDefinition {
232
232
  primary?: boolean;
233
233
  /** The screen. Rendered by the presenter, inside the kit provider and the client provider. */
234
234
  Screen: ComponentType<FeatureScreenProps>;
235
+ /**
236
+ * The screen paints edge to edge (its own background, a stage like the Heroes screen): the lobby
237
+ * gives it the whole area between its top row and the menu — no side padding, no width limit — and
238
+ * stretches it to that height. Default `false`: a page on the lobby's background, padded and kept
239
+ * to a readable width on a wide window.
240
+ */
241
+ fullBleed?: boolean;
235
242
  /**
236
243
  * Whether the title has something for this screen yet. Default `true`. A screen that depends on
237
244
  * title data (a shop without offers is an empty tab) registers with `false` and calls
@@ -345,6 +352,13 @@ interface MultiplayerRoom {
345
352
  readonly lateJoin: boolean;
346
353
  /** Leave the room for good: closes the session and the rooms screen forgets it. */
347
354
  leave(): Promise<void>;
355
+ /**
356
+ * The room's voice chat, or `null` when the room has none. The rooms system owns it and draws its
357
+ * controls (the mic button over the game included); a game reads it for its own touches — who is
358
+ * speaking (`isSpeaking`, `on("change")`), a talking player's marker, proximity volume
359
+ * (`setPeerVolume` by distance).
360
+ */
361
+ readonly voice: VoiceController | null;
348
362
  }
349
363
  /** What `ctx.features.open("multiplayer", args)` understands. */
350
364
  interface MultiplayerScreenArgs {
@@ -525,6 +539,76 @@ interface AppNotifications {
525
539
  dismiss(id: string): void;
526
540
  }
527
541
 
542
+ /** The room's voices, as the recording takes them (the multiplayer system provides this). */
543
+ interface RecordingVoiceSource {
544
+ /** This player's microphone track (silent while not transmitting); null — mic off. */
545
+ mic(): MediaStreamTrack | null;
546
+ /** Other players' voice tracks. */
547
+ others(): MediaStreamTrack[];
548
+ /** The publisher allows other players' voices in videos (`Multiplayer.RecordVoices`). */
549
+ recordOthers(): boolean;
550
+ /** Called when tracks come or go (a player joined, the mic went on). */
551
+ subscribe(listener: () => void): () => void;
552
+ }
553
+ /** `ctx.clips`. */
554
+ interface ClipsRegistry {
555
+ /**
556
+ * A bright moment right now. `weight` 1–10 (default 5): how much it is worth showing — a boss
557
+ * kill 8, a level-up 4. Close marks merge into one clip (their weights add up).
558
+ */
559
+ mark(name: string, weight?: number): void;
560
+ /** The multiplayer system registers the room's voices here; `null` when the room is gone. */
561
+ setVoiceSource(source: RecordingVoiceSource | null): void;
562
+ /** This player is making a long recording right now. */
563
+ isRecording(): boolean;
564
+ /** Called when `isRecording` changes. */
565
+ subscribe(listener: () => void): () => void;
566
+ }
567
+
568
+ /** The hero to draw, as the engine's Character system describes it. */
569
+ interface CharacterArtSubject {
570
+ /** The character's id (`CharacterID` of its definition). */
571
+ characterId: string;
572
+ /** Its class (`Classification.ClassID`), if any. */
573
+ classId?: string | null;
574
+ /** What it wears: equipment slot id → the worn item's `ItemID`. */
575
+ equipment: Readonly<Record<string, string>>;
576
+ /** The worn skin (`WornSkinID`), if any. */
577
+ skinId?: string | null;
578
+ /** Not the player's yet (shown dimmed by the screen) — the art may draw it in a calmer pose. */
579
+ locked?: boolean;
580
+ }
581
+ interface CharacterArtOptions {
582
+ /**
583
+ * "live" — the figure moving (breathing, blinking), for a big view; "still" — one frame, for a list
584
+ * of many heroes. Default "live".
585
+ */
586
+ mode?: "live" | "still";
587
+ }
588
+ /** One mounted figure. */
589
+ interface CharacterArtView {
590
+ /** The hero changed (equipped, switched): redraw it. A changed weapon may play a short move. */
591
+ update(subject: CharacterArtSubject): void;
592
+ /** Stop drawing and free what was made (the screen unmounts). */
593
+ destroy(): void;
594
+ }
595
+ interface CharacterArt {
596
+ /**
597
+ * Draw the hero into `host` — an element the screen sizes and positions; fill it, the figure
598
+ * standing on its bottom edge and centred. The returned view is updated and destroyed by the screen.
599
+ */
600
+ mount(host: HTMLElement, subject: CharacterArtSubject, options?: CharacterArtOptions): CharacterArtView;
601
+ }
602
+ /** `ctx.characterArt` — the game's character art, shared by every module of the session. */
603
+ interface CharacterArtRegistry {
604
+ /** Make this the art every screen draws heroes with. Returns the unregister function. */
605
+ set(art: CharacterArt): () => void;
606
+ /** The art now, or null — no game set one (the screen draws its own picture). */
607
+ get(): CharacterArt | null;
608
+ /** Called when the art is set or removed (a screen remounts its figures). */
609
+ subscribe(listener: () => void): () => void;
610
+ }
611
+
528
612
  /** What a module is, for catalog/UI purposes. Not every module is a game. */
529
613
  type ModuleType = "game" | "app" | "ai-app";
530
614
  /**
@@ -719,6 +803,12 @@ interface ModuleContext {
719
803
  * `LobbyHeroDefinition`.
720
804
  */
721
805
  lobby: LobbyRegistry;
806
+ /**
807
+ * The game's character art (see `CharacterArt`): a game sets how its hero is drawn (`set`), a
808
+ * screen of another module (the Heroes screen) draws the hero with it (`get()?.mount(...)`) — or its
809
+ * own picture when no game set one.
810
+ */
811
+ characterArt: CharacterArtRegistry;
722
812
  /**
723
813
  * Switch the host to another mode (a route id registered by any module) — e.g. the lobby starts a
724
814
  * game, the Workshop opens a world and hands the screen to the game. Unknown ids are ignored. Called
@@ -750,6 +840,12 @@ interface ModuleContext {
750
840
  * one look, one queue, one sound for the whole app. The module's own id is the default `source`.
751
841
  */
752
842
  notifications: AppNotifications;
843
+ /**
844
+ * The platform's video recording (see `ClipsRegistry`): a game marks bright moments
845
+ * (`ctx.clips.mark("boss_down", 8)`) — they become the player's "Best moments" clips on
846
+ * idosgames.com. A no-op anywhere else.
847
+ */
848
+ clips: ClipsRegistry;
753
849
  }
754
850
  /**
755
851
  * A module's debug surface for the agent: read its state, drive it with named actions.
@@ -842,6 +938,21 @@ interface EngineScene {
842
938
  * host falls back to reading the canvas itself, which is correct for 2D canvases.
843
939
  */
844
940
  capture?(): Promise<Blob | null>;
941
+ /**
942
+ * Where the action is right now, 0–1 on both axes of the canvas (the player, the explosion) — for
943
+ * the platform's video recording: a vertical (TikTok) cut of a landscape game follows this point
944
+ * instead of cutting the middle. Called a few times a second; keep it cheap. Omit — the centre.
945
+ */
946
+ focus?(): {
947
+ x: number;
948
+ y: number;
949
+ } | null;
950
+ /**
951
+ * The game's own sound as a MediaStream, for the platform's video recording — e.g. a
952
+ * `MediaStreamAudioDestinationNode` your audio graph also feeds (Three.js: `listener.context`).
953
+ * Omit — videos carry the voices but not the game's sound.
954
+ */
955
+ audio?(): MediaStream | null;
845
956
  /** Permanent teardown — dispose GPU resources, remove listeners. */
846
957
  destroy(): void;
847
958
  }
@@ -930,4 +1041,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
930
1041
  /** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
931
1042
  declare function defineModule(module: Module): Module;
932
1043
 
933
- export { type AppNotifications, type CapturedContent, type ContentFileData, type ContentRegistry, type ContentTypeHandler, type DownloadedContent, type EngineScene, type FeatureDefinition, type FeatureEntry, type FeatureGroup, type FeaturePresenter, type FeatureRegistry, type FeatureScreenProps, type InferShape, type LoadingReporter, type LoadingTask, type LobbyHeroAction, type LobbyHeroDefinition, type LobbyHeroStage, type LobbyHeroView, type LobbyRegistry, type LocalContentItem, type ModeEntry, type ModeRegistry, type Module, type ModuleAgentApi, type ModuleAuthor, type ModuleContext, type ModuleEngine, type ModuleEventEmit, type ModuleEventListen, type ModuleEvents, type ModuleEventsManifest, type ModuleManifest, type ModuleMedia, type ModuleMeta, type ModuleSharedUi, type ModuleType, type MultiplayerGame, type MultiplayerRegistry, type MultiplayerRoom, type MultiplayerScreenArgs, type MultiplayerSetting, type NotificationAction, type NotificationInput, type NotificationLevel, type PanelSlot, type RouteEntry, SHARED_UI_ROLES, type SceneMountContext, type Shape, type ShapeDescriptor, type ShapeLeaf, type ShapeLeafSpec, type SharedEventBus, type SharedUiContext, type SharedUiRole, type SurfaceAllocator, type SurfaceHandle, type SurfaceKind, TOPIC_NAME_RE, type Topic, type TopicName, type TopicPayload, type UiPanel, defineModule, defineTopic, hostTopics, isSharedUiRole, isTopicName, shape, topicNamespace };
1044
+ export { type AppNotifications, type CapturedContent, type CharacterArt, type CharacterArtOptions, type CharacterArtRegistry, type CharacterArtSubject, type CharacterArtView, type ClipsRegistry, type ContentFileData, type ContentRegistry, type ContentTypeHandler, type DownloadedContent, type EngineScene, type FeatureDefinition, type FeatureEntry, type FeatureGroup, type FeaturePresenter, type FeatureRegistry, type FeatureScreenProps, type InferShape, type LoadingReporter, type LoadingTask, type LobbyHeroAction, type LobbyHeroDefinition, type LobbyHeroStage, type LobbyHeroView, type LobbyRegistry, type LocalContentItem, type ModeEntry, type ModeRegistry, type Module, type ModuleAgentApi, type ModuleAuthor, type ModuleContext, type ModuleEngine, type ModuleEventEmit, type ModuleEventListen, type ModuleEvents, type ModuleEventsManifest, type ModuleManifest, type ModuleMedia, type ModuleMeta, type ModuleSharedUi, type ModuleType, type MultiplayerGame, type MultiplayerRegistry, type MultiplayerRoom, type MultiplayerScreenArgs, type MultiplayerSetting, type NotificationAction, type NotificationInput, type NotificationLevel, type PanelSlot, type RecordingVoiceSource, type RouteEntry, SHARED_UI_ROLES, type SceneMountContext, type Shape, type ShapeDescriptor, type ShapeLeaf, type ShapeLeafSpec, type SharedEventBus, type SharedUiContext, type SharedUiRole, type SurfaceAllocator, type SurfaceHandle, type SurfaceKind, TOPIC_NAME_RE, type Topic, type TopicName, type TopicPayload, type UiPanel, defineModule, defineTopic, hostTopics, isSharedUiRole, isTopicName, shape, topicNamespace };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RealtimeSession, IDosGamesClient } from '@idosgames/core';
1
+ import { RealtimeSession, VoiceController, IDosGamesClient } from '@idosgames/core';
2
2
  export { IDosGamesClient, SdkEvents } from '@idosgames/core';
3
3
  import { ComponentType } from 'react';
4
4
 
@@ -232,6 +232,13 @@ interface FeatureDefinition {
232
232
  primary?: boolean;
233
233
  /** The screen. Rendered by the presenter, inside the kit provider and the client provider. */
234
234
  Screen: ComponentType<FeatureScreenProps>;
235
+ /**
236
+ * The screen paints edge to edge (its own background, a stage like the Heroes screen): the lobby
237
+ * gives it the whole area between its top row and the menu — no side padding, no width limit — and
238
+ * stretches it to that height. Default `false`: a page on the lobby's background, padded and kept
239
+ * to a readable width on a wide window.
240
+ */
241
+ fullBleed?: boolean;
235
242
  /**
236
243
  * Whether the title has something for this screen yet. Default `true`. A screen that depends on
237
244
  * title data (a shop without offers is an empty tab) registers with `false` and calls
@@ -345,6 +352,13 @@ interface MultiplayerRoom {
345
352
  readonly lateJoin: boolean;
346
353
  /** Leave the room for good: closes the session and the rooms screen forgets it. */
347
354
  leave(): Promise<void>;
355
+ /**
356
+ * The room's voice chat, or `null` when the room has none. The rooms system owns it and draws its
357
+ * controls (the mic button over the game included); a game reads it for its own touches — who is
358
+ * speaking (`isSpeaking`, `on("change")`), a talking player's marker, proximity volume
359
+ * (`setPeerVolume` by distance).
360
+ */
361
+ readonly voice: VoiceController | null;
348
362
  }
349
363
  /** What `ctx.features.open("multiplayer", args)` understands. */
350
364
  interface MultiplayerScreenArgs {
@@ -525,6 +539,76 @@ interface AppNotifications {
525
539
  dismiss(id: string): void;
526
540
  }
527
541
 
542
+ /** The room's voices, as the recording takes them (the multiplayer system provides this). */
543
+ interface RecordingVoiceSource {
544
+ /** This player's microphone track (silent while not transmitting); null — mic off. */
545
+ mic(): MediaStreamTrack | null;
546
+ /** Other players' voice tracks. */
547
+ others(): MediaStreamTrack[];
548
+ /** The publisher allows other players' voices in videos (`Multiplayer.RecordVoices`). */
549
+ recordOthers(): boolean;
550
+ /** Called when tracks come or go (a player joined, the mic went on). */
551
+ subscribe(listener: () => void): () => void;
552
+ }
553
+ /** `ctx.clips`. */
554
+ interface ClipsRegistry {
555
+ /**
556
+ * A bright moment right now. `weight` 1–10 (default 5): how much it is worth showing — a boss
557
+ * kill 8, a level-up 4. Close marks merge into one clip (their weights add up).
558
+ */
559
+ mark(name: string, weight?: number): void;
560
+ /** The multiplayer system registers the room's voices here; `null` when the room is gone. */
561
+ setVoiceSource(source: RecordingVoiceSource | null): void;
562
+ /** This player is making a long recording right now. */
563
+ isRecording(): boolean;
564
+ /** Called when `isRecording` changes. */
565
+ subscribe(listener: () => void): () => void;
566
+ }
567
+
568
+ /** The hero to draw, as the engine's Character system describes it. */
569
+ interface CharacterArtSubject {
570
+ /** The character's id (`CharacterID` of its definition). */
571
+ characterId: string;
572
+ /** Its class (`Classification.ClassID`), if any. */
573
+ classId?: string | null;
574
+ /** What it wears: equipment slot id → the worn item's `ItemID`. */
575
+ equipment: Readonly<Record<string, string>>;
576
+ /** The worn skin (`WornSkinID`), if any. */
577
+ skinId?: string | null;
578
+ /** Not the player's yet (shown dimmed by the screen) — the art may draw it in a calmer pose. */
579
+ locked?: boolean;
580
+ }
581
+ interface CharacterArtOptions {
582
+ /**
583
+ * "live" — the figure moving (breathing, blinking), for a big view; "still" — one frame, for a list
584
+ * of many heroes. Default "live".
585
+ */
586
+ mode?: "live" | "still";
587
+ }
588
+ /** One mounted figure. */
589
+ interface CharacterArtView {
590
+ /** The hero changed (equipped, switched): redraw it. A changed weapon may play a short move. */
591
+ update(subject: CharacterArtSubject): void;
592
+ /** Stop drawing and free what was made (the screen unmounts). */
593
+ destroy(): void;
594
+ }
595
+ interface CharacterArt {
596
+ /**
597
+ * Draw the hero into `host` — an element the screen sizes and positions; fill it, the figure
598
+ * standing on its bottom edge and centred. The returned view is updated and destroyed by the screen.
599
+ */
600
+ mount(host: HTMLElement, subject: CharacterArtSubject, options?: CharacterArtOptions): CharacterArtView;
601
+ }
602
+ /** `ctx.characterArt` — the game's character art, shared by every module of the session. */
603
+ interface CharacterArtRegistry {
604
+ /** Make this the art every screen draws heroes with. Returns the unregister function. */
605
+ set(art: CharacterArt): () => void;
606
+ /** The art now, or null — no game set one (the screen draws its own picture). */
607
+ get(): CharacterArt | null;
608
+ /** Called when the art is set or removed (a screen remounts its figures). */
609
+ subscribe(listener: () => void): () => void;
610
+ }
611
+
528
612
  /** What a module is, for catalog/UI purposes. Not every module is a game. */
529
613
  type ModuleType = "game" | "app" | "ai-app";
530
614
  /**
@@ -719,6 +803,12 @@ interface ModuleContext {
719
803
  * `LobbyHeroDefinition`.
720
804
  */
721
805
  lobby: LobbyRegistry;
806
+ /**
807
+ * The game's character art (see `CharacterArt`): a game sets how its hero is drawn (`set`), a
808
+ * screen of another module (the Heroes screen) draws the hero with it (`get()?.mount(...)`) — or its
809
+ * own picture when no game set one.
810
+ */
811
+ characterArt: CharacterArtRegistry;
722
812
  /**
723
813
  * Switch the host to another mode (a route id registered by any module) — e.g. the lobby starts a
724
814
  * game, the Workshop opens a world and hands the screen to the game. Unknown ids are ignored. Called
@@ -750,6 +840,12 @@ interface ModuleContext {
750
840
  * one look, one queue, one sound for the whole app. The module's own id is the default `source`.
751
841
  */
752
842
  notifications: AppNotifications;
843
+ /**
844
+ * The platform's video recording (see `ClipsRegistry`): a game marks bright moments
845
+ * (`ctx.clips.mark("boss_down", 8)`) — they become the player's "Best moments" clips on
846
+ * idosgames.com. A no-op anywhere else.
847
+ */
848
+ clips: ClipsRegistry;
753
849
  }
754
850
  /**
755
851
  * A module's debug surface for the agent: read its state, drive it with named actions.
@@ -842,6 +938,21 @@ interface EngineScene {
842
938
  * host falls back to reading the canvas itself, which is correct for 2D canvases.
843
939
  */
844
940
  capture?(): Promise<Blob | null>;
941
+ /**
942
+ * Where the action is right now, 0–1 on both axes of the canvas (the player, the explosion) — for
943
+ * the platform's video recording: a vertical (TikTok) cut of a landscape game follows this point
944
+ * instead of cutting the middle. Called a few times a second; keep it cheap. Omit — the centre.
945
+ */
946
+ focus?(): {
947
+ x: number;
948
+ y: number;
949
+ } | null;
950
+ /**
951
+ * The game's own sound as a MediaStream, for the platform's video recording — e.g. a
952
+ * `MediaStreamAudioDestinationNode` your audio graph also feeds (Three.js: `listener.context`).
953
+ * Omit — videos carry the voices but not the game's sound.
954
+ */
955
+ audio?(): MediaStream | null;
845
956
  /** Permanent teardown — dispose GPU resources, remove listeners. */
846
957
  destroy(): void;
847
958
  }
@@ -930,4 +1041,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
930
1041
  /** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
931
1042
  declare function defineModule(module: Module): Module;
932
1043
 
933
- export { type AppNotifications, type CapturedContent, type ContentFileData, type ContentRegistry, type ContentTypeHandler, type DownloadedContent, type EngineScene, type FeatureDefinition, type FeatureEntry, type FeatureGroup, type FeaturePresenter, type FeatureRegistry, type FeatureScreenProps, type InferShape, type LoadingReporter, type LoadingTask, type LobbyHeroAction, type LobbyHeroDefinition, type LobbyHeroStage, type LobbyHeroView, type LobbyRegistry, type LocalContentItem, type ModeEntry, type ModeRegistry, type Module, type ModuleAgentApi, type ModuleAuthor, type ModuleContext, type ModuleEngine, type ModuleEventEmit, type ModuleEventListen, type ModuleEvents, type ModuleEventsManifest, type ModuleManifest, type ModuleMedia, type ModuleMeta, type ModuleSharedUi, type ModuleType, type MultiplayerGame, type MultiplayerRegistry, type MultiplayerRoom, type MultiplayerScreenArgs, type MultiplayerSetting, type NotificationAction, type NotificationInput, type NotificationLevel, type PanelSlot, type RouteEntry, SHARED_UI_ROLES, type SceneMountContext, type Shape, type ShapeDescriptor, type ShapeLeaf, type ShapeLeafSpec, type SharedEventBus, type SharedUiContext, type SharedUiRole, type SurfaceAllocator, type SurfaceHandle, type SurfaceKind, TOPIC_NAME_RE, type Topic, type TopicName, type TopicPayload, type UiPanel, defineModule, defineTopic, hostTopics, isSharedUiRole, isTopicName, shape, topicNamespace };
1044
+ export { type AppNotifications, type CapturedContent, type CharacterArt, type CharacterArtOptions, type CharacterArtRegistry, type CharacterArtSubject, type CharacterArtView, type ClipsRegistry, type ContentFileData, type ContentRegistry, type ContentTypeHandler, type DownloadedContent, type EngineScene, type FeatureDefinition, type FeatureEntry, type FeatureGroup, type FeaturePresenter, type FeatureRegistry, type FeatureScreenProps, type InferShape, type LoadingReporter, type LoadingTask, type LobbyHeroAction, type LobbyHeroDefinition, type LobbyHeroStage, type LobbyHeroView, type LobbyRegistry, type LocalContentItem, type ModeEntry, type ModeRegistry, type Module, type ModuleAgentApi, type ModuleAuthor, type ModuleContext, type ModuleEngine, type ModuleEventEmit, type ModuleEventListen, type ModuleEvents, type ModuleEventsManifest, type ModuleManifest, type ModuleMedia, type ModuleMeta, type ModuleSharedUi, type ModuleType, type MultiplayerGame, type MultiplayerRegistry, type MultiplayerRoom, type MultiplayerScreenArgs, type MultiplayerSetting, type NotificationAction, type NotificationInput, type NotificationLevel, type PanelSlot, type RecordingVoiceSource, type RouteEntry, SHARED_UI_ROLES, type SceneMountContext, type Shape, type ShapeDescriptor, type ShapeLeaf, type ShapeLeafSpec, type SharedEventBus, type SharedUiContext, type SharedUiRole, type SurfaceAllocator, type SurfaceHandle, type SurfaceKind, TOPIC_NAME_RE, type Topic, type TopicName, type TopicPayload, type UiPanel, defineModule, defineTopic, hostTopics, isSharedUiRole, isTopicName, shape, topicNamespace };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var x=/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*@[1-9]\d*$/;function S(e){return typeof e=="string"&&x.test(e)}function M(e){let t=e.indexOf(":");return t<0?"":e.slice(0,t)}var c=/^(string|number|boolean)(\[\])?(\?)?$/;function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,t){return e==="number"?typeof t=="number"&&Number.isFinite(t):typeof t===e}function f(e,t,r){if(!u(t))return `${r||"payload"} must be an object`;for(let[n,o]of Object.entries(e)){let i=r?`${r}.${n}`:n,a=t[n];if(typeof o!="string"){let p=f(o,a,i);if(p)return p;continue}let d=c.exec(o);if(!d)return `${i}: invalid descriptor "${o}"`;let[,s="",g,m]=d;if(a===void 0){if(m)continue;return `${i} is missing`}if(g){if(!Array.isArray(a))return `${i} must be ${s}[]`;if(!a.every(p=>l(s,p)))return `${i} must contain only ${s} values`;continue}if(!l(s,a))return `${i} must be a ${s}`}return null}function y(e,t){for(let[r,n]of Object.entries(e)){let o=t?`${t}.${r}`:r;if(typeof n=="string"){if(!c.test(n))throw new Error(`shape(): field "${o}" has invalid type "${n}" \u2014 use "string" | "number" | "boolean", optionally with "[]" and/or a trailing "?"`)}else if(u(n))y(n,o);else throw new Error(`shape(): field "${o}" must be a type string or an object`)}}function h(e){return y(e,""),{descriptor:e,check:t=>f(e,t,"")}}function v(e,t){if(!S(e))throw new Error(`defineTopic(): "${e}" is not a valid topic name \u2014 expected "<module-id>:<event>@<major>", e.g. "idle-rpg:character-upgraded@1"`);return {name:e,shape:t}}var T={modeChanged:v("host:mode-changed@1",h({from:"string?",to:"string"}))};var b=["currency-bar","wallet","status","account","menu","modes"];function R(e){return typeof e=="string"&&b.includes(e)}function D(e){return e}export{b as SHARED_UI_ROLES,x as TOPIC_NAME_RE,D as defineModule,v as defineTopic,T as hostTopics,R as isSharedUiRole,S as isTopicName,h as shape,M as topicNamespace};
1
+ var x=/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*@[1-9]\d*$/;function S(e){return typeof e=="string"&&x.test(e)}function M(e){let t=e.indexOf(":");return t<0?"":e.slice(0,t)}var c=/^(string|number|boolean)(\[\])?(\?)?$/;function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,t){return e==="number"?typeof t=="number"&&Number.isFinite(t):typeof t===e}function f(e,t,o){if(!u(t))return `${o||"payload"} must be an object`;for(let[r,n]of Object.entries(e)){let i=o?`${o}.${r}`:r,a=t[r];if(typeof n!="string"){let p=f(n,a,i);if(p)return p;continue}let d=c.exec(n);if(!d)return `${i}: invalid descriptor "${n}"`;let[,s="",g,m]=d;if(a===void 0){if(m)continue;return `${i} is missing`}if(g){if(!Array.isArray(a))return `${i} must be ${s}[]`;if(!a.every(p=>l(s,p)))return `${i} must contain only ${s} values`;continue}if(!l(s,a))return `${i} must be a ${s}`}return null}function y(e,t){for(let[o,r]of Object.entries(e)){let n=t?`${t}.${o}`:o;if(typeof r=="string"){if(!c.test(r))throw new Error(`shape(): field "${n}" has invalid type "${r}" \u2014 use "string" | "number" | "boolean", optionally with "[]" and/or a trailing "?"`)}else if(u(r))y(r,n);else throw new Error(`shape(): field "${n}" must be a type string or an object`)}}function h(e){return y(e,""),{descriptor:e,check:t=>f(e,t,"")}}function v(e,t){if(!S(e))throw new Error(`defineTopic(): "${e}" is not a valid topic name \u2014 expected "<module-id>:<event>@<major>", e.g. "idle-rpg:character-upgraded@1"`);return {name:e,shape:t}}var T={modeChanged:v("host:mode-changed@1",h({from:"string?",to:"string"}))};var b=["currency-bar","wallet","status","account","menu","modes"];function R(e){return typeof e=="string"&&b.includes(e)}function D(e){return e}export{b as SHARED_UI_ROLES,x as TOPIC_NAME_RE,D as defineModule,v as defineTopic,T as hostTopics,R as isSharedUiRole,S as isTopicName,h as shape,M as topicNamespace};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idosgames/module-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "The composable-module contract for the iDosGames host shell: Module / ModuleContext types plus tiny authoring helpers. Framework-neutral at runtime — a module can be a game (Three/Phaser), a plain app, or an AI app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "//": "@idosgames/core is a real range, not '*': a published contract must name the version of core whose IDosGamesClient/SdkEvents shapes it references. Bump in step with core (see RELEASING.md). react is an OPTIONAL peer — the contract references React's ComponentType type only (a UI panel is a React component), which is erased at build, so a runtime-only module needs no React.",
48
48
  "dependencies": {
49
- "@idosgames/core": "^0.21.0"
49
+ "@idosgames/core": "^0.23.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": "^18.3.1 || ^19.0.0"