@idosgames/module-sdk 0.5.0 → 0.6.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 S=/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*@[1-9]\d*$/;function x(e){return typeof e=="string"&&S.test(e)}function M(e){let t=e.indexOf(":");return t<0?"":e.slice(0,t)}var l=/^(string|number|boolean)(\[\])?(\?)?$/;function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function c(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=l.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=>c(s,p)))return `${i} must contain only ${s} values`;continue}if(!c(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(!l.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(!x(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 E={modeChanged:v("host:mode-changed@1",h({from:"string?",to:"string"}))};var T=["currency-bar","wallet","status","account","menu"];function w(e){return typeof e=="string"&&T.includes(e)}function R(e){return e}exports.SHARED_UI_ROLES=T;exports.TOPIC_NAME_RE=S;exports.defineModule=R;exports.defineTopic=v;exports.hostTopics=E;exports.isSharedUiRole=w;exports.isTopicName=x;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 T(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[n,r]of Object.entries(e)){let i=o?`${o}.${n}`:n,a=t[n];if(typeof r!="string"){let p=f(r,a,i);if(p)return p;continue}let d=c.exec(r);if(!d)return `${i}: invalid descriptor "${r}"`;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,n]of Object.entries(e)){let r=t?`${t}.${o}`:o;if(typeof n=="string"){if(!c.test(n))throw new Error(`shape(): field "${r}" has invalid type "${n}" \u2014 use "string" | "number" | "boolean", optionally with "[]" and/or a trailing "?"`)}else if(u(n))y(n,r);else throw new Error(`shape(): field "${r}" 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 b={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=b;exports.isSharedUiRole=R;exports.isTopicName=S;exports.shape=h;exports.topicNamespace=T;
package/dist/index.d.cts CHANGED
@@ -111,7 +111,7 @@ interface ModuleEventsManifest {
111
111
  * each one has to mean the same thing across the whole catalog. A new role is a minor release of
112
112
  * this package; anything that does not fit a role is plain events + SDK data.
113
113
  */
114
- declare const SHARED_UI_ROLES: readonly ["currency-bar", "wallet", "status", "account", "menu"];
114
+ declare const SHARED_UI_ROLES: readonly ["currency-bar", "wallet", "status", "account", "menu", "modes"];
115
115
  type SharedUiRole = (typeof SHARED_UI_ROLES)[number];
116
116
  /** Static declaration on a module (`Module.sharedUi`). */
117
117
  interface ModuleSharedUi {
@@ -125,8 +125,8 @@ interface ModuleSharedUi {
125
125
  /**
126
126
  * Roles this module draws INSIDE ITS OWN MODE while nobody `provides` them for everybody — a
127
127
  * template with a full-screen UI (bottom tab bar) puts "Log out" and the player's ID into its own
128
- * header instead of having the host float them over its layout. Only "account" and "menu" are
129
- * host-drawn, so those are the roles this matters for: the host hides its floating copy while this
128
+ * header instead of having the host float them over its layout. Only "account", "menu" and "modes"
129
+ * are host-drawn, so those are the roles this matters for: the host hides its floating copy while this
130
130
  * module's mode is on screen and keeps drawing it in every other mode. `shouldDraw` of every module
131
131
  * is unchanged.
132
132
  */
@@ -215,7 +215,7 @@ interface FeatureScreenProps {
215
215
  close(): void;
216
216
  }
217
217
  interface FeatureDefinition {
218
- /** Unique across the game — conventionally the owning module's id without "-ui" ("store"). */
218
+ /** Unique across the game — conventionally the owning module's id ("store"). */
219
219
  id: string;
220
220
  /** Menu label, already in the player's language. */
221
221
  label: string;
@@ -228,17 +228,27 @@ interface FeatureDefinition {
228
228
  primary?: boolean;
229
229
  /** The screen. Rendered by the presenter, inside the kit provider and the client provider. */
230
230
  Screen: ComponentType<FeatureScreenProps>;
231
+ /**
232
+ * Whether the title has something for this screen yet. Default `true`. A screen that depends on
233
+ * title data (a shop without offers is an empty tab) registers with `false` and calls
234
+ * `setAvailable(id, true)` once it has checked — so the tab never flashes in and out.
235
+ */
236
+ available?: boolean;
231
237
  }
232
- /** A registered feature plus its live badge. */
238
+ /** A registered feature plus its live badge and availability. */
233
239
  interface FeatureEntry extends FeatureDefinition {
234
240
  badge: number;
235
241
  /** The module that registered it. */
236
242
  moduleId: string;
243
+ /** Menus list only available features; `open()` of an unavailable one is refused. */
244
+ available: boolean;
245
+ /** Why it is hidden, in plain words — shown to the AI Coder's agent, never to players. */
246
+ unavailableReason?: string;
237
247
  }
238
248
  /**
239
249
  * Shows a feature's screen. The menu owner registers one; the most recent presenter wins, and
240
250
  * unregistering hands the job back to the previous one (the lobby unmounts when the player switches
241
- * to another mode → the HUD's ☰ or the host's button presents again).
251
+ * to a game → the host's window presents again).
242
252
  */
243
253
  type FeaturePresenter = (id: string, args?: Record<string, unknown>) => boolean;
244
254
  /** `ctx.features` — the registry of the game's features, shared by every module of the session. */
@@ -248,10 +258,18 @@ interface FeatureRegistry {
248
258
  /** Features in registration order (menus sort them themselves). */
249
259
  list(): FeatureEntry[];
250
260
  get(id: string): FeatureEntry | undefined;
251
- /** Opens a feature's screen through the current presenter. `false` — no such feature or no presenter. */
261
+ /**
262
+ * Opens a feature's screen through the current presenter. `false` — no such feature, it is not
263
+ * available on this title, or no presenter.
264
+ */
252
265
  open(id: string, args?: Record<string, unknown>): boolean;
253
266
  /** The "something to claim" counter of a feature; 0 hides it. */
254
267
  setBadge(id: string, count: number): void;
268
+ /**
269
+ * Show or hide a feature by what the title has (see `FeatureDefinition.available`). `reason` says
270
+ * why it is hidden ("no store offers configured on the title") — the agent reads it.
271
+ */
272
+ setAvailable(id: string, available: boolean, reason?: string): void;
255
273
  /** Called on every registration, removal and badge change. */
256
274
  subscribe(listener: () => void): () => void;
257
275
  /** For menu owners: become the one who shows screens. Returns an unregister function. */
@@ -309,6 +327,11 @@ interface ModuleManifest {
309
327
  * `"feature"`.
310
328
  */
311
329
  type?: "template" | "feature";
330
+ /**
331
+ * Part of the standard set: the platform installs this module into EVERY new project together with
332
+ * the base (login + lobby) — the shop, heroes, leaderboards… A creator removes it like any module.
333
+ */
334
+ inBase?: boolean;
312
335
  /** Overrides the catalog display name; defaults to `ModuleMeta.name` from `module.ts`. */
313
336
  name?: string;
314
337
  /** One-line pitch shown on the catalog card. */
@@ -409,8 +432,14 @@ interface ModuleContext {
409
432
  */
410
433
  features: FeatureRegistry;
411
434
  /**
412
- * Switch the host to another mode (a route id registered by any module) — e.g. the Workshop opens a
413
- * world and hands the screen to the game. Unknown ids are ignored.
435
+ * The app's modes — every route any module registered, and which one is on screen. The lobby
436
+ * builds its "Play" buttons from it; a game can tell whether a lobby exists to return to.
437
+ */
438
+ modes: ModeRegistry;
439
+ /**
440
+ * Switch the host to another mode (a route id registered by any module) — e.g. the lobby starts a
441
+ * game, the Workshop opens a world and hands the screen to the game. Unknown ids are ignored. Called
442
+ * inside `setup()` it picks the mode the app starts in.
414
443
  */
415
444
  navigate(modeId: string): void;
416
445
  /**
@@ -535,6 +564,30 @@ interface RouteEntry {
535
564
  icon?: string;
536
565
  order?: number;
537
566
  }
567
+ /** A mode of the app as every module sees it (`ctx.modes`): a registered route plus its owner. */
568
+ interface ModeEntry {
569
+ id: string;
570
+ label: string;
571
+ icon?: string;
572
+ /** Resolved order (0 when the route gave none); the list is sorted by it. */
573
+ order: number;
574
+ /** The module that registered the route. */
575
+ moduleId: string;
576
+ /** That module's `meta.type` — a lobby offers "Play" for `game` modes. */
577
+ moduleType: ModuleType;
578
+ }
579
+ /** `ctx.modes` — the modes of the app, live. Switch with `ctx.navigate(id)`. */
580
+ interface ModeRegistry {
581
+ /**
582
+ * Every mode, sorted by `order`. Filled once every module's `setup()` has run — read it at render
583
+ * time or subscribe, not inside `setup()`.
584
+ */
585
+ list(): ModeEntry[];
586
+ /** The mode on screen; `null` until the app is shown. */
587
+ current(): string | null;
588
+ /** Called when the list or the current mode changes. Returns the unsubscribe function. */
589
+ subscribe(listener: () => void): () => void;
590
+ }
538
591
  /**
539
592
  * @deprecated The pre-0.2 string-topic bus. `ctx.events` is now `ModuleEvents` (typed topic tokens,
540
593
  * see `defineTopic`); this type stays only so older code that named it still compiles.
@@ -547,4 +600,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
547
600
  /** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
548
601
  declare function defineModule(module: Module): Module;
549
602
 
550
- export { 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 LocalContentItem, 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 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 };
603
+ export { 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 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 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
@@ -111,7 +111,7 @@ interface ModuleEventsManifest {
111
111
  * each one has to mean the same thing across the whole catalog. A new role is a minor release of
112
112
  * this package; anything that does not fit a role is plain events + SDK data.
113
113
  */
114
- declare const SHARED_UI_ROLES: readonly ["currency-bar", "wallet", "status", "account", "menu"];
114
+ declare const SHARED_UI_ROLES: readonly ["currency-bar", "wallet", "status", "account", "menu", "modes"];
115
115
  type SharedUiRole = (typeof SHARED_UI_ROLES)[number];
116
116
  /** Static declaration on a module (`Module.sharedUi`). */
117
117
  interface ModuleSharedUi {
@@ -125,8 +125,8 @@ interface ModuleSharedUi {
125
125
  /**
126
126
  * Roles this module draws INSIDE ITS OWN MODE while nobody `provides` them for everybody — a
127
127
  * template with a full-screen UI (bottom tab bar) puts "Log out" and the player's ID into its own
128
- * header instead of having the host float them over its layout. Only "account" and "menu" are
129
- * host-drawn, so those are the roles this matters for: the host hides its floating copy while this
128
+ * header instead of having the host float them over its layout. Only "account", "menu" and "modes"
129
+ * are host-drawn, so those are the roles this matters for: the host hides its floating copy while this
130
130
  * module's mode is on screen and keeps drawing it in every other mode. `shouldDraw` of every module
131
131
  * is unchanged.
132
132
  */
@@ -215,7 +215,7 @@ interface FeatureScreenProps {
215
215
  close(): void;
216
216
  }
217
217
  interface FeatureDefinition {
218
- /** Unique across the game — conventionally the owning module's id without "-ui" ("store"). */
218
+ /** Unique across the game — conventionally the owning module's id ("store"). */
219
219
  id: string;
220
220
  /** Menu label, already in the player's language. */
221
221
  label: string;
@@ -228,17 +228,27 @@ interface FeatureDefinition {
228
228
  primary?: boolean;
229
229
  /** The screen. Rendered by the presenter, inside the kit provider and the client provider. */
230
230
  Screen: ComponentType<FeatureScreenProps>;
231
+ /**
232
+ * Whether the title has something for this screen yet. Default `true`. A screen that depends on
233
+ * title data (a shop without offers is an empty tab) registers with `false` and calls
234
+ * `setAvailable(id, true)` once it has checked — so the tab never flashes in and out.
235
+ */
236
+ available?: boolean;
231
237
  }
232
- /** A registered feature plus its live badge. */
238
+ /** A registered feature plus its live badge and availability. */
233
239
  interface FeatureEntry extends FeatureDefinition {
234
240
  badge: number;
235
241
  /** The module that registered it. */
236
242
  moduleId: string;
243
+ /** Menus list only available features; `open()` of an unavailable one is refused. */
244
+ available: boolean;
245
+ /** Why it is hidden, in plain words — shown to the AI Coder's agent, never to players. */
246
+ unavailableReason?: string;
237
247
  }
238
248
  /**
239
249
  * Shows a feature's screen. The menu owner registers one; the most recent presenter wins, and
240
250
  * unregistering hands the job back to the previous one (the lobby unmounts when the player switches
241
- * to another mode → the HUD's ☰ or the host's button presents again).
251
+ * to a game → the host's window presents again).
242
252
  */
243
253
  type FeaturePresenter = (id: string, args?: Record<string, unknown>) => boolean;
244
254
  /** `ctx.features` — the registry of the game's features, shared by every module of the session. */
@@ -248,10 +258,18 @@ interface FeatureRegistry {
248
258
  /** Features in registration order (menus sort them themselves). */
249
259
  list(): FeatureEntry[];
250
260
  get(id: string): FeatureEntry | undefined;
251
- /** Opens a feature's screen through the current presenter. `false` — no such feature or no presenter. */
261
+ /**
262
+ * Opens a feature's screen through the current presenter. `false` — no such feature, it is not
263
+ * available on this title, or no presenter.
264
+ */
252
265
  open(id: string, args?: Record<string, unknown>): boolean;
253
266
  /** The "something to claim" counter of a feature; 0 hides it. */
254
267
  setBadge(id: string, count: number): void;
268
+ /**
269
+ * Show or hide a feature by what the title has (see `FeatureDefinition.available`). `reason` says
270
+ * why it is hidden ("no store offers configured on the title") — the agent reads it.
271
+ */
272
+ setAvailable(id: string, available: boolean, reason?: string): void;
255
273
  /** Called on every registration, removal and badge change. */
256
274
  subscribe(listener: () => void): () => void;
257
275
  /** For menu owners: become the one who shows screens. Returns an unregister function. */
@@ -309,6 +327,11 @@ interface ModuleManifest {
309
327
  * `"feature"`.
310
328
  */
311
329
  type?: "template" | "feature";
330
+ /**
331
+ * Part of the standard set: the platform installs this module into EVERY new project together with
332
+ * the base (login + lobby) — the shop, heroes, leaderboards… A creator removes it like any module.
333
+ */
334
+ inBase?: boolean;
312
335
  /** Overrides the catalog display name; defaults to `ModuleMeta.name` from `module.ts`. */
313
336
  name?: string;
314
337
  /** One-line pitch shown on the catalog card. */
@@ -409,8 +432,14 @@ interface ModuleContext {
409
432
  */
410
433
  features: FeatureRegistry;
411
434
  /**
412
- * Switch the host to another mode (a route id registered by any module) — e.g. the Workshop opens a
413
- * world and hands the screen to the game. Unknown ids are ignored.
435
+ * The app's modes — every route any module registered, and which one is on screen. The lobby
436
+ * builds its "Play" buttons from it; a game can tell whether a lobby exists to return to.
437
+ */
438
+ modes: ModeRegistry;
439
+ /**
440
+ * Switch the host to another mode (a route id registered by any module) — e.g. the lobby starts a
441
+ * game, the Workshop opens a world and hands the screen to the game. Unknown ids are ignored. Called
442
+ * inside `setup()` it picks the mode the app starts in.
414
443
  */
415
444
  navigate(modeId: string): void;
416
445
  /**
@@ -535,6 +564,30 @@ interface RouteEntry {
535
564
  icon?: string;
536
565
  order?: number;
537
566
  }
567
+ /** A mode of the app as every module sees it (`ctx.modes`): a registered route plus its owner. */
568
+ interface ModeEntry {
569
+ id: string;
570
+ label: string;
571
+ icon?: string;
572
+ /** Resolved order (0 when the route gave none); the list is sorted by it. */
573
+ order: number;
574
+ /** The module that registered the route. */
575
+ moduleId: string;
576
+ /** That module's `meta.type` — a lobby offers "Play" for `game` modes. */
577
+ moduleType: ModuleType;
578
+ }
579
+ /** `ctx.modes` — the modes of the app, live. Switch with `ctx.navigate(id)`. */
580
+ interface ModeRegistry {
581
+ /**
582
+ * Every mode, sorted by `order`. Filled once every module's `setup()` has run — read it at render
583
+ * time or subscribe, not inside `setup()`.
584
+ */
585
+ list(): ModeEntry[];
586
+ /** The mode on screen; `null` until the app is shown. */
587
+ current(): string | null;
588
+ /** Called when the list or the current mode changes. Returns the unsubscribe function. */
589
+ subscribe(listener: () => void): () => void;
590
+ }
538
591
  /**
539
592
  * @deprecated The pre-0.2 string-topic bus. `ctx.events` is now `ModuleEvents` (typed topic tokens,
540
593
  * see `defineTopic`); this type stays only so older code that named it still compiles.
@@ -547,4 +600,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
547
600
  /** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
548
601
  declare function defineModule(module: Module): Module;
549
602
 
550
- export { 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 LocalContentItem, 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 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 };
603
+ export { 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 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 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 S=/^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*@[1-9]\d*$/;function x(e){return typeof e=="string"&&S.test(e)}function M(e){let t=e.indexOf(":");return t<0?"":e.slice(0,t)}var l=/^(string|number|boolean)(\[\])?(\?)?$/;function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function c(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=l.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=>c(s,p)))return `${i} must contain only ${s} values`;continue}if(!c(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(!l.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(!x(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 E={modeChanged:v("host:mode-changed@1",h({from:"string?",to:"string"}))};var T=["currency-bar","wallet","status","account","menu"];function w(e){return typeof e=="string"&&T.includes(e)}function R(e){return e}export{T as SHARED_UI_ROLES,S as TOPIC_NAME_RE,R as defineModule,v as defineTopic,E as hostTopics,w as isSharedUiRole,x 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 T(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[n,r]of Object.entries(e)){let i=o?`${o}.${n}`:n,a=t[n];if(typeof r!="string"){let p=f(r,a,i);if(p)return p;continue}let d=c.exec(r);if(!d)return `${i}: invalid descriptor "${r}"`;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,n]of Object.entries(e)){let r=t?`${t}.${o}`:o;if(typeof n=="string"){if(!c.test(n))throw new Error(`shape(): field "${r}" has invalid type "${n}" \u2014 use "string" | "number" | "boolean", optionally with "[]" and/or a trailing "?"`)}else if(u(n))y(n,r);else throw new Error(`shape(): field "${r}" 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 b={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,b as hostTopics,R as isSharedUiRole,S as isTopicName,h as shape,T as topicNamespace};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@idosgames/module-sdk",
3
- "version": "0.5.0",
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.",
3
+ "version": "0.6.0",
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",
7
7
  "author": "iDos Games",
@@ -44,7 +44,7 @@
44
44
  "build": "tsup",
45
45
  "typecheck": "tsc --noEmit -p tsconfig.json"
46
46
  },
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.",
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
49
  "@idosgames/core": "^0.19.0"
50
50
  },