@idosgames/module-sdk 0.4.1 → 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 +1 -1
- package/dist/index.d.cts +125 -4
- package/dist/index.d.ts +125 -4
- package/dist/index.js +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var
|
|
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"];
|
|
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 {
|
|
@@ -122,6 +122,15 @@ interface ModuleSharedUi {
|
|
|
122
122
|
* With no provider installed the host warns in dev, and the catalog tells the agent to install one.
|
|
123
123
|
*/
|
|
124
124
|
requires?: readonly SharedUiRole[];
|
|
125
|
+
/**
|
|
126
|
+
* Roles this module draws INSIDE ITS OWN MODE while nobody `provides` them for everybody — a
|
|
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", "menu" and "modes"
|
|
129
|
+
* are host-drawn, so those are the roles this matters for: the host hides its floating copy while this
|
|
130
|
+
* module's mode is on screen and keeps drawing it in every other mode. `shouldDraw` of every module
|
|
131
|
+
* is unchanged.
|
|
132
|
+
*/
|
|
133
|
+
drawsOwn?: readonly SharedUiRole[];
|
|
125
134
|
}
|
|
126
135
|
/** `ctx.sharedUi` — the host's answer, fixed for the whole session. */
|
|
127
136
|
interface SharedUiContext {
|
|
@@ -196,6 +205,77 @@ interface ContentRegistry {
|
|
|
196
205
|
subscribe(listener: () => void): () => void;
|
|
197
206
|
}
|
|
198
207
|
|
|
208
|
+
/** Menu sections, in this order. */
|
|
209
|
+
type FeatureGroup = "economy" | "events" | "social" | "profile" | "other";
|
|
210
|
+
/** What the presenter hands to a feature's screen. */
|
|
211
|
+
interface FeatureScreenProps {
|
|
212
|
+
/** What `open(id, args)` passed — e.g. `{ itemInstanceID }` to open straight on one item. */
|
|
213
|
+
args?: Record<string, unknown>;
|
|
214
|
+
/** Close the screen (go back to the menu / the lobby's first tab). */
|
|
215
|
+
close(): void;
|
|
216
|
+
}
|
|
217
|
+
interface FeatureDefinition {
|
|
218
|
+
/** Unique across the game — conventionally the owning module's id ("store"). */
|
|
219
|
+
id: string;
|
|
220
|
+
/** Menu label, already in the player's language. */
|
|
221
|
+
label: string;
|
|
222
|
+
/** Glyph name of @idosgames/react/ui ("shop", "hero"…), an emoji, or an image URL. */
|
|
223
|
+
icon?: string;
|
|
224
|
+
group?: FeatureGroup;
|
|
225
|
+
/** Sort order within the group (and among the lobby's main tabs); lower first. */
|
|
226
|
+
order?: number;
|
|
227
|
+
/** A main tab of the lobby rather than an entry under "More". */
|
|
228
|
+
primary?: boolean;
|
|
229
|
+
/** The screen. Rendered by the presenter, inside the kit provider and the client provider. */
|
|
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;
|
|
237
|
+
}
|
|
238
|
+
/** A registered feature plus its live badge and availability. */
|
|
239
|
+
interface FeatureEntry extends FeatureDefinition {
|
|
240
|
+
badge: number;
|
|
241
|
+
/** The module that registered it. */
|
|
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;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Shows a feature's screen. The menu owner registers one; the most recent presenter wins, and
|
|
250
|
+
* unregistering hands the job back to the previous one (the lobby unmounts when the player switches
|
|
251
|
+
* to a game → the host's window presents again).
|
|
252
|
+
*/
|
|
253
|
+
type FeaturePresenter = (id: string, args?: Record<string, unknown>) => boolean;
|
|
254
|
+
/** `ctx.features` — the registry of the game's features, shared by every module of the session. */
|
|
255
|
+
interface FeatureRegistry {
|
|
256
|
+
/** Register (or replace) a feature. Returns an unregister function. */
|
|
257
|
+
register(feature: FeatureDefinition): () => void;
|
|
258
|
+
/** Features in registration order (menus sort them themselves). */
|
|
259
|
+
list(): FeatureEntry[];
|
|
260
|
+
get(id: string): FeatureEntry | undefined;
|
|
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
|
+
*/
|
|
265
|
+
open(id: string, args?: Record<string, unknown>): boolean;
|
|
266
|
+
/** The "something to claim" counter of a feature; 0 hides it. */
|
|
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;
|
|
273
|
+
/** Called on every registration, removal and badge change. */
|
|
274
|
+
subscribe(listener: () => void): () => void;
|
|
275
|
+
/** For menu owners: become the one who shows screens. Returns an unregister function. */
|
|
276
|
+
setPresenter(presenter: FeaturePresenter): () => void;
|
|
277
|
+
}
|
|
278
|
+
|
|
199
279
|
/** What a module is, for catalog/UI purposes. Not every module is a game. */
|
|
200
280
|
type ModuleType = "game" | "app" | "ai-app";
|
|
201
281
|
/**
|
|
@@ -247,6 +327,11 @@ interface ModuleManifest {
|
|
|
247
327
|
* `"feature"`.
|
|
248
328
|
*/
|
|
249
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;
|
|
250
335
|
/** Overrides the catalog display name; defaults to `ModuleMeta.name` from `module.ts`. */
|
|
251
336
|
name?: string;
|
|
252
337
|
/** One-line pitch shown on the catalog card. */
|
|
@@ -341,8 +426,20 @@ interface ModuleContext {
|
|
|
341
426
|
*/
|
|
342
427
|
content: ContentRegistry;
|
|
343
428
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
429
|
+
* The game's features — screens shown in the menu (the "menu" shared-UI role draws it). Register
|
|
430
|
+
* your screen here instead of a nav route when it is a SECTION of the game (a shop, a friends list)
|
|
431
|
+
* rather than a mode of its own; `open(id)` opens another module's screen. See `FeatureDefinition`.
|
|
432
|
+
*/
|
|
433
|
+
features: FeatureRegistry;
|
|
434
|
+
/**
|
|
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.
|
|
346
443
|
*/
|
|
347
444
|
navigate(modeId: string): void;
|
|
348
445
|
/**
|
|
@@ -467,6 +564,30 @@ interface RouteEntry {
|
|
|
467
564
|
icon?: string;
|
|
468
565
|
order?: number;
|
|
469
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
|
+
}
|
|
470
591
|
/**
|
|
471
592
|
* @deprecated The pre-0.2 string-topic bus. `ctx.events` is now `ModuleEvents` (typed topic tokens,
|
|
472
593
|
* see `defineTopic`); this type stays only so older code that named it still compiles.
|
|
@@ -479,4 +600,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
|
|
|
479
600
|
/** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
|
|
480
601
|
declare function defineModule(module: Module): Module;
|
|
481
602
|
|
|
482
|
-
export { type CapturedContent, type ContentFileData, type ContentRegistry, type ContentTypeHandler, type DownloadedContent, type EngineScene, 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"];
|
|
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 {
|
|
@@ -122,6 +122,15 @@ interface ModuleSharedUi {
|
|
|
122
122
|
* With no provider installed the host warns in dev, and the catalog tells the agent to install one.
|
|
123
123
|
*/
|
|
124
124
|
requires?: readonly SharedUiRole[];
|
|
125
|
+
/**
|
|
126
|
+
* Roles this module draws INSIDE ITS OWN MODE while nobody `provides` them for everybody — a
|
|
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", "menu" and "modes"
|
|
129
|
+
* are host-drawn, so those are the roles this matters for: the host hides its floating copy while this
|
|
130
|
+
* module's mode is on screen and keeps drawing it in every other mode. `shouldDraw` of every module
|
|
131
|
+
* is unchanged.
|
|
132
|
+
*/
|
|
133
|
+
drawsOwn?: readonly SharedUiRole[];
|
|
125
134
|
}
|
|
126
135
|
/** `ctx.sharedUi` — the host's answer, fixed for the whole session. */
|
|
127
136
|
interface SharedUiContext {
|
|
@@ -196,6 +205,77 @@ interface ContentRegistry {
|
|
|
196
205
|
subscribe(listener: () => void): () => void;
|
|
197
206
|
}
|
|
198
207
|
|
|
208
|
+
/** Menu sections, in this order. */
|
|
209
|
+
type FeatureGroup = "economy" | "events" | "social" | "profile" | "other";
|
|
210
|
+
/** What the presenter hands to a feature's screen. */
|
|
211
|
+
interface FeatureScreenProps {
|
|
212
|
+
/** What `open(id, args)` passed — e.g. `{ itemInstanceID }` to open straight on one item. */
|
|
213
|
+
args?: Record<string, unknown>;
|
|
214
|
+
/** Close the screen (go back to the menu / the lobby's first tab). */
|
|
215
|
+
close(): void;
|
|
216
|
+
}
|
|
217
|
+
interface FeatureDefinition {
|
|
218
|
+
/** Unique across the game — conventionally the owning module's id ("store"). */
|
|
219
|
+
id: string;
|
|
220
|
+
/** Menu label, already in the player's language. */
|
|
221
|
+
label: string;
|
|
222
|
+
/** Glyph name of @idosgames/react/ui ("shop", "hero"…), an emoji, or an image URL. */
|
|
223
|
+
icon?: string;
|
|
224
|
+
group?: FeatureGroup;
|
|
225
|
+
/** Sort order within the group (and among the lobby's main tabs); lower first. */
|
|
226
|
+
order?: number;
|
|
227
|
+
/** A main tab of the lobby rather than an entry under "More". */
|
|
228
|
+
primary?: boolean;
|
|
229
|
+
/** The screen. Rendered by the presenter, inside the kit provider and the client provider. */
|
|
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;
|
|
237
|
+
}
|
|
238
|
+
/** A registered feature plus its live badge and availability. */
|
|
239
|
+
interface FeatureEntry extends FeatureDefinition {
|
|
240
|
+
badge: number;
|
|
241
|
+
/** The module that registered it. */
|
|
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;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Shows a feature's screen. The menu owner registers one; the most recent presenter wins, and
|
|
250
|
+
* unregistering hands the job back to the previous one (the lobby unmounts when the player switches
|
|
251
|
+
* to a game → the host's window presents again).
|
|
252
|
+
*/
|
|
253
|
+
type FeaturePresenter = (id: string, args?: Record<string, unknown>) => boolean;
|
|
254
|
+
/** `ctx.features` — the registry of the game's features, shared by every module of the session. */
|
|
255
|
+
interface FeatureRegistry {
|
|
256
|
+
/** Register (or replace) a feature. Returns an unregister function. */
|
|
257
|
+
register(feature: FeatureDefinition): () => void;
|
|
258
|
+
/** Features in registration order (menus sort them themselves). */
|
|
259
|
+
list(): FeatureEntry[];
|
|
260
|
+
get(id: string): FeatureEntry | undefined;
|
|
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
|
+
*/
|
|
265
|
+
open(id: string, args?: Record<string, unknown>): boolean;
|
|
266
|
+
/** The "something to claim" counter of a feature; 0 hides it. */
|
|
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;
|
|
273
|
+
/** Called on every registration, removal and badge change. */
|
|
274
|
+
subscribe(listener: () => void): () => void;
|
|
275
|
+
/** For menu owners: become the one who shows screens. Returns an unregister function. */
|
|
276
|
+
setPresenter(presenter: FeaturePresenter): () => void;
|
|
277
|
+
}
|
|
278
|
+
|
|
199
279
|
/** What a module is, for catalog/UI purposes. Not every module is a game. */
|
|
200
280
|
type ModuleType = "game" | "app" | "ai-app";
|
|
201
281
|
/**
|
|
@@ -247,6 +327,11 @@ interface ModuleManifest {
|
|
|
247
327
|
* `"feature"`.
|
|
248
328
|
*/
|
|
249
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;
|
|
250
335
|
/** Overrides the catalog display name; defaults to `ModuleMeta.name` from `module.ts`. */
|
|
251
336
|
name?: string;
|
|
252
337
|
/** One-line pitch shown on the catalog card. */
|
|
@@ -341,8 +426,20 @@ interface ModuleContext {
|
|
|
341
426
|
*/
|
|
342
427
|
content: ContentRegistry;
|
|
343
428
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
429
|
+
* The game's features — screens shown in the menu (the "menu" shared-UI role draws it). Register
|
|
430
|
+
* your screen here instead of a nav route when it is a SECTION of the game (a shop, a friends list)
|
|
431
|
+
* rather than a mode of its own; `open(id)` opens another module's screen. See `FeatureDefinition`.
|
|
432
|
+
*/
|
|
433
|
+
features: FeatureRegistry;
|
|
434
|
+
/**
|
|
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.
|
|
346
443
|
*/
|
|
347
444
|
navigate(modeId: string): void;
|
|
348
445
|
/**
|
|
@@ -467,6 +564,30 @@ interface RouteEntry {
|
|
|
467
564
|
icon?: string;
|
|
468
565
|
order?: number;
|
|
469
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
|
+
}
|
|
470
591
|
/**
|
|
471
592
|
* @deprecated The pre-0.2 string-topic bus. `ctx.events` is now `ModuleEvents` (typed topic tokens,
|
|
472
593
|
* see `defineTopic`); this type stays only so older code that named it still compiles.
|
|
@@ -479,4 +600,4 @@ interface SharedEventBus<Events extends Record<string, unknown> = Record<string,
|
|
|
479
600
|
/** Identity helper that pins a module literal to the `Module` type for editor help and errors. */
|
|
480
601
|
declare function defineModule(module: Module): Module;
|
|
481
602
|
|
|
482
|
-
export { type CapturedContent, type ContentFileData, type ContentRegistry, type ContentTypeHandler, type DownloadedContent, type EngineScene, 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
|
|
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.
|
|
4
|
-
"description": "The composable-module contract for the iDosGames host shell: Module / ModuleContext types plus tiny authoring helpers. Framework-neutral at runtime
|
|
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,9 +44,9 @@
|
|
|
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
|
|
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.
|
|
49
|
+
"@idosgames/core": "^0.19.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": "^18.3.1 || ^19.0.0"
|