@idosgames/module-sdk 0.10.1 → 0.11.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 b(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 M=["currency-bar","wallet","status","account","menu","modes"];function R(e){return typeof e=="string"&&M.includes(e)}function D(e){return e}exports.SHARED_UI_ROLES=M;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=b;
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;
package/dist/index.d.cts CHANGED
@@ -217,8 +217,12 @@ interface FeatureScreenProps {
217
217
  interface FeatureDefinition {
218
218
  /** Unique across the game — conventionally the owning module's id ("store"). */
219
219
  id: string;
220
- /** Menu label, already in the player's language. */
221
- label: string;
220
+ /**
221
+ * Menu label in the player's language. Prefer a function (`label: () => t("title")`): the lobby
222
+ * calls it on every render, so the tab follows the language switch (UI kit `setLanguage`) without
223
+ * a reload. A plain string stays as registered.
224
+ */
225
+ label: string | (() => string);
222
226
  /** Glyph name of @idosgames/react/ui ("shop", "hero"…), an emoji, or an image URL. */
223
227
  icon?: string;
224
228
  group?: FeatureGroup;
@@ -236,7 +240,9 @@ interface FeatureDefinition {
236
240
  available?: boolean;
237
241
  }
238
242
  /** A registered feature plus its live badge and availability. */
239
- interface FeatureEntry extends FeatureDefinition {
243
+ interface FeatureEntry extends Omit<FeatureDefinition, "label"> {
244
+ /** The label now — a function label is called on every read. */
245
+ readonly label: string;
240
246
  badge: number;
241
247
  /** The module that registered it. */
242
248
  moduleId: string;
@@ -357,6 +363,84 @@ interface MultiplayerRegistry {
357
363
  subscribe(listener: () => void): () => void;
358
364
  }
359
365
 
366
+ /** A button under the showcase or on the "Play" card. */
367
+ interface LobbyHeroAction {
368
+ id: string;
369
+ /** Already in the player's language. */
370
+ label: string;
371
+ /** Glyph name of @idosgames/react/ui ("edit", "star"…), an emoji, or an image URL. */
372
+ icon?: string;
373
+ /** Pressed. Omit when the button only opens `menu`. */
374
+ run?(): void;
375
+ /** A short list shown when the button is pressed (an emote picker) — each entry runs on its own. */
376
+ menu?: LobbyHeroAction[];
377
+ disabled?: boolean;
378
+ }
379
+ /** What the lobby lays out around the showcase. Read through `view()`, redrawn on `subscribe`. */
380
+ interface LobbyHeroView {
381
+ /** Big name under the showcase (the character's name). */
382
+ title?: string;
383
+ /** A line under it. */
384
+ subtitle?: string;
385
+ /** Buttons under the name ("Edit", "Emote"). */
386
+ actions?: LobbyHeroAction[];
387
+ /**
388
+ * The card next to "Play" — WHAT will be played: a caption ("World"), a name ("Island"), buttons
389
+ * to change it ("Change", "New"). Omitted — "Play" alone.
390
+ */
391
+ play?: {
392
+ caption?: string;
393
+ title?: string;
394
+ actions?: LobbyHeroAction[];
395
+ /** Overflow menu of the card ("⋯": download, upload…). */
396
+ more?: LobbyHeroAction[];
397
+ };
398
+ }
399
+ /** Handed to `mount`: where on the host the main figure should stand. */
400
+ interface LobbyHeroStage {
401
+ /**
402
+ * The free spot between the lobby's interface where the figure should stand, in CSS pixels of the
403
+ * host element (it moves with the layout — read it every frame). `null` — not laid out yet.
404
+ */
405
+ spot(): {
406
+ x: number;
407
+ y: number;
408
+ w: number;
409
+ h: number;
410
+ } | null;
411
+ }
412
+ interface LobbyHeroDefinition {
413
+ /** The game mode "Play" starts (a route id of the game). */
414
+ mode: string;
415
+ /**
416
+ * Put the showcase into `host` — an element filling the lobby's home behind its interface. Called
417
+ * when the home is shown, the returned function when it is left (stop the render loop there). The
418
+ * same hero may be mounted again later.
419
+ */
420
+ mount(host: HTMLElement, stage: LobbyHeroStage): () => void;
421
+ /** The current texts and buttons. Cheap — called on every redraw. */
422
+ view(): LobbyHeroView;
423
+ /** Called by the hero whenever `view()` would answer differently. Returns the unsubscribe. */
424
+ subscribe(listener: () => void): () => void;
425
+ /**
426
+ * "Play" was pressed: return false to stay in the lobby (nothing to play yet). Default: switch to
427
+ * `mode`.
428
+ */
429
+ beforePlay?(): boolean;
430
+ }
431
+ /** `ctx.lobby` — the app's lobby as seen by a module. */
432
+ interface LobbyRegistry {
433
+ /** Put this game's showcase on the lobby's home. Returns the unregister function. */
434
+ setHero(hero: LobbyHeroDefinition): () => void;
435
+ /** The hero on the home now (the lobby reads it). */
436
+ hero(): LobbyHeroDefinition | null;
437
+ subscribe(listener: () => void): () => void;
438
+ /** Whether the app has a lobby to return to (the base is installed). */
439
+ available(): boolean;
440
+ /** Go to the lobby — a game's "To the lobby" in its pause menu. False — there is no lobby. */
441
+ open(): boolean;
442
+ }
443
+
360
444
  /** One piece of work on the loading screen. Call `done()` exactly once, also on failure. */
361
445
  interface LoadingTask {
362
446
  /**
@@ -629,6 +713,12 @@ interface ModuleContext {
629
713
  * builds its "Play" buttons from it; a game can tell whether a lobby exists to return to.
630
714
  */
631
715
  modes: ModeRegistry;
716
+ /**
717
+ * The app's lobby: a game puts its showcase on the lobby's home (`setHero` — its character on a
718
+ * pedestal, "Play" next to it) and sends the player back to it from its pause menu (`open`). See
719
+ * `LobbyHeroDefinition`.
720
+ */
721
+ lobby: LobbyRegistry;
632
722
  /**
633
723
  * Switch the host to another mode (a route id registered by any module) — e.g. the lobby starts a
634
724
  * game, the Workshop opens a world and hands the screen to the game. Unknown ids are ignored. Called
@@ -790,9 +880,15 @@ interface RouteEntry {
790
880
  * A game that lives INSIDE the lobby: the base keeps the lobby's balances on top and its tabs at
791
881
  * the bottom around it, "Play" is this game, and the app starts in it after sign-in (idle-rpg).
792
882
  * Without it (the default) a game takes the whole screen, launched by "Play" in the lobby, with a
793
- * small button back (voxelcraft, board-game).
883
+ * small button back (board-game).
794
884
  */
795
885
  inLobby?: boolean;
886
+ /**
887
+ * A full-screen game with its OWN way back to the lobby (a pause menu with "To the lobby" that
888
+ * calls `ctx.lobby.open()` — voxelcraft): the base does not draw its round button back over it.
889
+ * The game must then offer that way on every device (a pause button on touch screens).
890
+ */
891
+ ownExit?: boolean;
796
892
  }
797
893
  /** A mode of the app as every module sees it (`ctx.modes`): a registered route plus its owner. */
798
894
  interface ModeEntry {
@@ -807,6 +903,8 @@ interface ModeEntry {
807
903
  moduleType: ModuleType;
808
904
  /** The route's `inLobby`: the game sits inside the lobby rather than taking the whole screen. */
809
905
  inLobby?: boolean;
906
+ /** The route's `ownExit`: the game leads back to the lobby itself — no button back over it. */
907
+ ownExit?: boolean;
810
908
  }
811
909
  /** `ctx.modes` — the modes of the app, live. Switch with `ctx.navigate(id)`. */
812
910
  interface ModeRegistry {
@@ -832,4 +930,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
832
930
  /** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
833
931
  declare function defineModule(module: Module): Module;
834
932
 
835
- 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 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 };
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 };
package/dist/index.d.ts CHANGED
@@ -217,8 +217,12 @@ interface FeatureScreenProps {
217
217
  interface FeatureDefinition {
218
218
  /** Unique across the game — conventionally the owning module's id ("store"). */
219
219
  id: string;
220
- /** Menu label, already in the player's language. */
221
- label: string;
220
+ /**
221
+ * Menu label in the player's language. Prefer a function (`label: () => t("title")`): the lobby
222
+ * calls it on every render, so the tab follows the language switch (UI kit `setLanguage`) without
223
+ * a reload. A plain string stays as registered.
224
+ */
225
+ label: string | (() => string);
222
226
  /** Glyph name of @idosgames/react/ui ("shop", "hero"…), an emoji, or an image URL. */
223
227
  icon?: string;
224
228
  group?: FeatureGroup;
@@ -236,7 +240,9 @@ interface FeatureDefinition {
236
240
  available?: boolean;
237
241
  }
238
242
  /** A registered feature plus its live badge and availability. */
239
- interface FeatureEntry extends FeatureDefinition {
243
+ interface FeatureEntry extends Omit<FeatureDefinition, "label"> {
244
+ /** The label now — a function label is called on every read. */
245
+ readonly label: string;
240
246
  badge: number;
241
247
  /** The module that registered it. */
242
248
  moduleId: string;
@@ -357,6 +363,84 @@ interface MultiplayerRegistry {
357
363
  subscribe(listener: () => void): () => void;
358
364
  }
359
365
 
366
+ /** A button under the showcase or on the "Play" card. */
367
+ interface LobbyHeroAction {
368
+ id: string;
369
+ /** Already in the player's language. */
370
+ label: string;
371
+ /** Glyph name of @idosgames/react/ui ("edit", "star"…), an emoji, or an image URL. */
372
+ icon?: string;
373
+ /** Pressed. Omit when the button only opens `menu`. */
374
+ run?(): void;
375
+ /** A short list shown when the button is pressed (an emote picker) — each entry runs on its own. */
376
+ menu?: LobbyHeroAction[];
377
+ disabled?: boolean;
378
+ }
379
+ /** What the lobby lays out around the showcase. Read through `view()`, redrawn on `subscribe`. */
380
+ interface LobbyHeroView {
381
+ /** Big name under the showcase (the character's name). */
382
+ title?: string;
383
+ /** A line under it. */
384
+ subtitle?: string;
385
+ /** Buttons under the name ("Edit", "Emote"). */
386
+ actions?: LobbyHeroAction[];
387
+ /**
388
+ * The card next to "Play" — WHAT will be played: a caption ("World"), a name ("Island"), buttons
389
+ * to change it ("Change", "New"). Omitted — "Play" alone.
390
+ */
391
+ play?: {
392
+ caption?: string;
393
+ title?: string;
394
+ actions?: LobbyHeroAction[];
395
+ /** Overflow menu of the card ("⋯": download, upload…). */
396
+ more?: LobbyHeroAction[];
397
+ };
398
+ }
399
+ /** Handed to `mount`: where on the host the main figure should stand. */
400
+ interface LobbyHeroStage {
401
+ /**
402
+ * The free spot between the lobby's interface where the figure should stand, in CSS pixels of the
403
+ * host element (it moves with the layout — read it every frame). `null` — not laid out yet.
404
+ */
405
+ spot(): {
406
+ x: number;
407
+ y: number;
408
+ w: number;
409
+ h: number;
410
+ } | null;
411
+ }
412
+ interface LobbyHeroDefinition {
413
+ /** The game mode "Play" starts (a route id of the game). */
414
+ mode: string;
415
+ /**
416
+ * Put the showcase into `host` — an element filling the lobby's home behind its interface. Called
417
+ * when the home is shown, the returned function when it is left (stop the render loop there). The
418
+ * same hero may be mounted again later.
419
+ */
420
+ mount(host: HTMLElement, stage: LobbyHeroStage): () => void;
421
+ /** The current texts and buttons. Cheap — called on every redraw. */
422
+ view(): LobbyHeroView;
423
+ /** Called by the hero whenever `view()` would answer differently. Returns the unsubscribe. */
424
+ subscribe(listener: () => void): () => void;
425
+ /**
426
+ * "Play" was pressed: return false to stay in the lobby (nothing to play yet). Default: switch to
427
+ * `mode`.
428
+ */
429
+ beforePlay?(): boolean;
430
+ }
431
+ /** `ctx.lobby` — the app's lobby as seen by a module. */
432
+ interface LobbyRegistry {
433
+ /** Put this game's showcase on the lobby's home. Returns the unregister function. */
434
+ setHero(hero: LobbyHeroDefinition): () => void;
435
+ /** The hero on the home now (the lobby reads it). */
436
+ hero(): LobbyHeroDefinition | null;
437
+ subscribe(listener: () => void): () => void;
438
+ /** Whether the app has a lobby to return to (the base is installed). */
439
+ available(): boolean;
440
+ /** Go to the lobby — a game's "To the lobby" in its pause menu. False — there is no lobby. */
441
+ open(): boolean;
442
+ }
443
+
360
444
  /** One piece of work on the loading screen. Call `done()` exactly once, also on failure. */
361
445
  interface LoadingTask {
362
446
  /**
@@ -629,6 +713,12 @@ interface ModuleContext {
629
713
  * builds its "Play" buttons from it; a game can tell whether a lobby exists to return to.
630
714
  */
631
715
  modes: ModeRegistry;
716
+ /**
717
+ * The app's lobby: a game puts its showcase on the lobby's home (`setHero` — its character on a
718
+ * pedestal, "Play" next to it) and sends the player back to it from its pause menu (`open`). See
719
+ * `LobbyHeroDefinition`.
720
+ */
721
+ lobby: LobbyRegistry;
632
722
  /**
633
723
  * Switch the host to another mode (a route id registered by any module) — e.g. the lobby starts a
634
724
  * game, the Workshop opens a world and hands the screen to the game. Unknown ids are ignored. Called
@@ -790,9 +880,15 @@ interface RouteEntry {
790
880
  * A game that lives INSIDE the lobby: the base keeps the lobby's balances on top and its tabs at
791
881
  * the bottom around it, "Play" is this game, and the app starts in it after sign-in (idle-rpg).
792
882
  * Without it (the default) a game takes the whole screen, launched by "Play" in the lobby, with a
793
- * small button back (voxelcraft, board-game).
883
+ * small button back (board-game).
794
884
  */
795
885
  inLobby?: boolean;
886
+ /**
887
+ * A full-screen game with its OWN way back to the lobby (a pause menu with "To the lobby" that
888
+ * calls `ctx.lobby.open()` — voxelcraft): the base does not draw its round button back over it.
889
+ * The game must then offer that way on every device (a pause button on touch screens).
890
+ */
891
+ ownExit?: boolean;
796
892
  }
797
893
  /** A mode of the app as every module sees it (`ctx.modes`): a registered route plus its owner. */
798
894
  interface ModeEntry {
@@ -807,6 +903,8 @@ interface ModeEntry {
807
903
  moduleType: ModuleType;
808
904
  /** The route's `inLobby`: the game sits inside the lobby rather than taking the whole screen. */
809
905
  inLobby?: boolean;
906
+ /** The route's `ownExit`: the game leads back to the lobby itself — no button back over it. */
907
+ ownExit?: boolean;
810
908
  }
811
909
  /** `ctx.modes` — the modes of the app, live. Switch with `ctx.navigate(id)`. */
812
910
  interface ModeRegistry {
@@ -832,4 +930,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
832
930
  /** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
833
931
  declare function defineModule(module: Module): Module;
834
932
 
835
- 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 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 };
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 };
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 b(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 M=["currency-bar","wallet","status","account","menu","modes"];function R(e){return typeof e=="string"&&M.includes(e)}function D(e){return e}export{M 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,b 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,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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idosgames/module-sdk",
3
- "version": "0.10.1",
3
+ "version": "0.11.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",