@anweather/game-plugin-ssdk 1.5.1 → 1.6.1

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.
@@ -0,0 +1,17 @@
1
+ import type { GamePluginService } from "../src/server/GamePluginService";
2
+ export interface ConformanceOptions {
3
+ /** Participant ids to seat. Defaults to metadata.minPlayers generated ids. */
4
+ participants?: string[];
5
+ /** InitializeGame config. Defaults to {}. */
6
+ config?: unknown;
7
+ }
8
+ export interface ConformanceReport {
9
+ passed: string[];
10
+ failed: {
11
+ check: string;
12
+ reason: string;
13
+ }[];
14
+ }
15
+ export declare function runConformance<Context>(service: GamePluginService<Context>, context: Context, options?: ConformanceOptions): Promise<ConformanceReport>;
16
+ /** runConformance, throwing one error naming every failed check. */
17
+ export declare function assertConformance<Context>(service: GamePluginService<Context>, context: Context, options?: ConformanceOptions): Promise<ConformanceReport>;
@@ -1,3 +1,10 @@
1
1
  export { mountSsdkHandler } from "./express";
2
2
  export { createGamePluginService } from "./defaultService";
3
3
  export type { RequiredGamePluginService, OptionalGamePluginService } from "./defaultService";
4
+ export { servePlugin, createPluginApp } from "./servePlugin";
5
+ export type { ServePluginOptions, PluginServiceDefinition, PluginApp, RunningPlugin } from "./servePlugin";
6
+ export { createRegistrationLoop } from "./registration";
7
+ export type { RegistrationOptions, RegistrationLoop } from "./registration";
8
+ export { runConformance, assertConformance } from "./conformance";
9
+ export type { ConformanceOptions, ConformanceReport } from "./conformance";
10
+ export { PLUGIN_PROTOCOL_VERSION, runtimeVersion } from "./version";
@@ -0,0 +1,25 @@
1
+ export interface RegistrationOptions {
2
+ gameType: string;
3
+ /** The platform's base URL, e.g. http://platform:8080. Absent means
4
+ * "never register" -- the backend runs standalone (local tests, the
5
+ * Jackson-bridge test). */
6
+ platformUrl: string | undefined;
7
+ /** How the platform reaches this backend, e.g. http://chess-remote:8091. */
8
+ backendBaseUrl: string;
9
+ /** The shared secret the platform will send back as X-Plugin-Auth. */
10
+ authToken: string | undefined;
11
+ /** Seconds between attempts while registration keeps failing. */
12
+ retryDelaySeconds?: number;
13
+ fetchImpl?: typeof fetch;
14
+ log?: Pick<Console, "log" | "error">;
15
+ }
16
+ export interface RegistrationLoop {
17
+ /** Start registering (returns immediately; the first attempt is async). */
18
+ start(): void;
19
+ /** Stop renewing and tell the platform this backend is going away. */
20
+ stop(): Promise<void>;
21
+ /** One attempt, resolving to the delay in seconds before the next one.
22
+ * Exposed for tests; start() drives this on a timer. */
23
+ attemptOnce(): Promise<number>;
24
+ }
25
+ export declare function createRegistrationLoop(options: RegistrationOptions): RegistrationLoop;
@@ -0,0 +1,65 @@
1
+ import { type Express, type Request } from "express";
2
+ import type { Server } from "node:http";
3
+ import type { ValidationCustomizer } from "@aws-smithy/server-common";
4
+ import type { GamePluginService, GamePluginServiceOperations } from "../src/server/GamePluginService";
5
+ import type { OptionalGamePluginService, RequiredGamePluginService } from "./defaultService";
6
+ import { type RegistrationLoop } from "./registration";
7
+ /** What a plugin supplies: every operation createGamePluginService requires,
8
+ * except Health, which servePlugin answers itself unless overridden. */
9
+ export type PluginServiceDefinition<Context> = Omit<RequiredGamePluginService<Context>, "Health"> & Partial<Pick<GamePluginService<Context>, "Health">> & OptionalGamePluginService<Context>;
10
+ export interface ServePluginOptions<Context = Record<string, never>> {
11
+ /** The wire GameType id, as GetMetadata reports it. */
12
+ gameType: string;
13
+ service: PluginServiceDefinition<Context>;
14
+ /** Port to listen on when the environment sets no PORT. 0 picks a free one. */
15
+ defaultPort?: number;
16
+ /** Directory served, unauthenticated, under /assets -- the plugin's own
17
+ * frontend bundle. Absent means no /assets route. */
18
+ staticDir?: string;
19
+ /** express.raw body limit. Every RPC carries the whole pluginState, and
20
+ * the platform's own ceiling is the games row (16 MB), so that is the
21
+ * default rather than Express's 100 KB. */
22
+ bodyLimit?: string;
23
+ /** Where PORT, PLUGIN_AUTH_TOKEN, PLATFORM_URL and
24
+ * PLUGIN_BACKEND_BASE_URL are read from. Defaults to process.env. */
25
+ env?: Record<string, string | undefined>;
26
+ /** Per-request context handed to every operation. Defaults to {}. */
27
+ makeContext?: (req: Request) => Context;
28
+ /** Smithy input validation policy. The default is permissive: the rules
29
+ * engine's own validateMove is the real gate for move-shape
30
+ * correctness, and a strict customizer would 400 a Document-typed
31
+ * payload the engine would have rejected with a readable reason. */
32
+ validationCustomizer?: ValidationCustomizer<GamePluginServiceOperations>;
33
+ /** Extra routes or middleware, mounted before the /v1 catch-all and
34
+ * after the auth gate. */
35
+ configure?: (app: Express) => void;
36
+ /** Install SIGTERM/SIGINT handlers that deregister and exit. Defaults
37
+ * to true; tests pass false. */
38
+ handleSignals?: boolean;
39
+ /** Seconds between registration attempts while the platform is
40
+ * unreachable. */
41
+ retryDelaySeconds?: number;
42
+ log?: Pick<Console, "log" | "error">;
43
+ fetchImpl?: typeof fetch;
44
+ }
45
+ export interface PluginApp {
46
+ app: Express;
47
+ registration: RegistrationLoop;
48
+ port: number;
49
+ authToken: string | undefined;
50
+ platformUrl: string | undefined;
51
+ backendBaseUrl: string;
52
+ }
53
+ export interface RunningPlugin extends PluginApp {
54
+ server: Server;
55
+ /** The port actually bound (differs from the configured one only for 0). */
56
+ port: number;
57
+ /** Stop renewing, deregister, close the listener. */
58
+ close(): Promise<void>;
59
+ }
60
+ /** Build the app without listening -- what servePlugin does first, exposed
61
+ * for tests and for a plugin that wants to hand the app to its own server. */
62
+ export declare function createPluginApp<Context = Record<string, never>>(options: ServePluginOptions<Context>): PluginApp;
63
+ /** Listen, register with the platform, and keep the registration alive
64
+ * until close() or SIGTERM. Resolves once the port is bound. */
65
+ export declare function servePlugin<Context = Record<string, never>>(options: ServePluginOptions<Context>): Promise<RunningPlugin>;
@@ -0,0 +1,9 @@
1
+ export declare const PLUGIN_PROTOCOL_VERSION = 1;
2
+ /** The version of this npm package as installed -- what servePlugin()
3
+ * reports as Health.runtimeVersion so the platform can see which SDK
4
+ * release a running plugin was built against. Read from the package's own
5
+ * package.json at runtime (which is why it is not a compile-time constant:
6
+ * the publish workflow stamps the version after the build). "unknown" only
7
+ * when the package layout is not the published one, e.g. a bundler that
8
+ * inlined this module. */
9
+ export declare function runtimeVersion(): string;
@@ -281,6 +281,17 @@ export interface HealthOutput {
281
281
  status: string | undefined;
282
282
  gameType: string | undefined;
283
283
  protocolVersion: number | undefined;
284
+ /**
285
+ * The version of the plugin runtime library this backend was built
286
+ * with (the Kotlin plugin-protocol-kotlin-runtime artifact or the
287
+ * @anweather/game-plugin-ssdk npm package -- both publish on one
288
+ * shared semver, see plugin-protocol/CLAUDE.md). Informational:
289
+ * the platform records it at registration so a plugin on a stale
290
+ * runtime is visible without shelling into its container. Absent
291
+ * from a backend built before this field existed.
292
+ * @public
293
+ */
294
+ runtimeVersion?: string | undefined;
284
295
  }
285
296
  export declare namespace HealthOutput {
286
297
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@anweather/game-plugin-ssdk",
3
3
  "description": "Generated GamePlugin RPC types/server (plugin-protocol/model/game-plugin.smithy) plus a hand-written Express bridge and default-handler wrapper (./runtime) -- see plugin-protocol/CLAUDE.md's \"Publishing for cross-repo plugins\".",
4
- "version": "1.5.1",
4
+ "version": "1.6.1",
5
5
  "scripts": {
6
6
  "build": "concurrently 'npm:build:cjs' 'npm:build:es' 'npm:build:types'",
7
7
  "build:cjs": "tsc -p tsconfig.cjs.json",
@@ -9,7 +9,8 @@
9
9
  "build:types": "tsc -p tsconfig.types.json",
10
10
  "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
11
11
  "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo || exit 0",
12
- "prepack": "npm run clean && npm run build"
12
+ "prepack": "npm run clean && npm run build",
13
+ "test": "tsx --test test/*.test.ts"
13
14
  },
14
15
  "main": "./dist-cjs/src/index.js",
15
16
  "types": "./dist-types/src/index.d.ts",
@@ -69,7 +70,8 @@
69
70
  "express": "^4.21.2",
70
71
  "rimraf": "^3.0.0",
71
72
  "typescript": "~5.8.3",
72
- "@types/node": "^18.19.69"
73
+ "@types/node": "^18.19.69",
74
+ "tsx": "^4.19.2"
73
75
  },
74
76
  "engines": {
75
77
  "node": ">=18.0.0"