@memizy/protocol 1.0.0-rc.5 → 1.0.0-rc.7

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.
@@ -1,58 +1,74 @@
1
- import { z as e } from "zod";
1
+ import { SETTING_TYPES as e } from "./settings.js";
2
+ import { z as t } from "zod";
2
3
  //#region src/settingsSchema.ts
3
- var t = e.union([e.string().min(1, "Text must not be empty"), e.record(e.string().min(2), e.string().min(1)).refine((e) => Object.keys(e).length > 0, "Localized text needs at least one language")]), n = {
4
- id: e.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/, "Setting id must start with a letter and contain only letters, digits, \"_\" and \"-\""),
5
- label: t,
6
- description: t.optional(),
7
- modes: e.array(e.enum(["solo", "multiplayer"])).min(1, "modes needs at least one mode").optional()
8
- }, r = e.looseObject({
9
- ...n,
10
- type: e.literal("number"),
11
- default: e.number().finite(),
12
- min: e.number().finite().optional(),
13
- max: e.number().finite().optional(),
14
- step: e.number().positive().optional()
4
+ var n = t.union([t.string().min(1, "Text must not be empty"), t.record(t.string().min(2), t.string().min(1)).refine((e) => Object.keys(e).length > 0, "Localized text needs at least one language")]), r = {
5
+ id: t.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/, "Setting id must start with a letter and contain only letters, digits, \"_\" and \"-\""),
6
+ label: n,
7
+ description: n.optional(),
8
+ modes: t.array(t.enum(["solo", "multiplayer"])).min(1, "modes needs at least one mode").optional()
9
+ }, i = t.looseObject({
10
+ ...r,
11
+ type: t.literal("number"),
12
+ default: t.number().finite(),
13
+ min: t.number().finite().optional(),
14
+ max: t.number().finite().optional(),
15
+ step: t.number().positive().optional()
15
16
  }).refine((e) => e.min === void 0 || e.max === void 0 || e.min <= e.max, {
16
17
  message: "min must not be greater than max",
17
18
  path: ["min"]
18
19
  }).refine((e) => (e.min === void 0 || e.default >= e.min) && (e.max === void 0 || e.default <= e.max), {
19
20
  message: "default is outside min/max",
20
21
  path: ["default"]
21
- }), i = e.looseObject({
22
- ...n,
23
- type: e.literal("boolean"),
24
- default: e.boolean()
25
- }), a = e.union([
26
- e.string(),
27
- e.number().finite(),
28
- e.boolean()
29
- ]), o = e.looseObject({
30
- ...n,
31
- type: e.literal("select"),
32
- options: e.array(e.looseObject({
33
- value: a,
34
- label: t
22
+ }), a = t.looseObject({
23
+ ...r,
24
+ type: t.literal("boolean"),
25
+ default: t.boolean()
26
+ }), o = t.union([
27
+ t.string(),
28
+ t.number().finite(),
29
+ t.boolean()
30
+ ]), s = t.looseObject({
31
+ ...r,
32
+ type: t.literal("select"),
33
+ options: t.array(t.looseObject({
34
+ value: o,
35
+ label: n
35
36
  })).min(1, "A select setting needs at least one option"),
36
- default: a
37
+ default: o
37
38
  }).refine((e) => new Set(e.options.map((e) => e.value)).size === e.options.length, {
38
39
  message: "Option values must be unique",
39
40
  path: ["options"]
40
41
  }).refine((e) => e.options.some((t) => t.value === e.default), {
41
42
  message: "default must be one of the option values",
42
43
  path: ["default"]
43
- }), s = e.looseObject({
44
- ...n,
45
- type: e.literal("text"),
46
- default: e.string(),
47
- maxLength: e.number().int().positive().optional()
44
+ }), c = t.looseObject({
45
+ ...r,
46
+ type: t.literal("text"),
47
+ default: t.string(),
48
+ maxLength: t.number().int().positive().optional()
48
49
  }).refine((e) => e.maxLength === void 0 || e.default.length <= e.maxLength, {
49
50
  message: "default is longer than maxLength",
50
51
  path: ["default"]
51
- }), c = e.discriminatedUnion("type", [
52
- r,
52
+ }), l = t.discriminatedUnion("type", [
53
53
  i,
54
- o,
55
- s
56
- ]);
54
+ a,
55
+ s,
56
+ c
57
+ ]), u = t.looseObject({
58
+ ...r,
59
+ type: t.string().regex(/^[a-z][a-z0-9-]*$/, "A setting type is a lowercase name").refine((t) => !e.includes(t), "A known setting type"),
60
+ default: t.unknown().refine((e) => e !== void 0, "A setting needs a default")
61
+ }), d = t.unknown().transform((n, r) => {
62
+ let i = typeof n == "object" && n ? n.type : void 0, a = (typeof i == "string" && e.includes(i) ? l : u).safeParse(n);
63
+ if (!a.success) {
64
+ for (let e of a.error.issues) r.addIssue({
65
+ code: "custom",
66
+ message: e.message,
67
+ path: e.path
68
+ });
69
+ return t.NEVER;
70
+ }
71
+ return a.data;
72
+ });
57
73
  //#endregion
58
- export { t as LocalizedTextSchema, c as SettingDefinitionSchema };
74
+ export { n as LocalizedTextSchema, l as SettingDefinitionSchema, d as SettingEntrySchema, u as UnknownSettingSchema };
@@ -5,7 +5,7 @@
5
5
  * error `name` and `message`. The code is therefore also encoded in the
6
6
  * message as `[CODE] text`, and {@link toProtocolError} restores it.
7
7
  */
8
- export declare const PROTOCOL_ERROR_CODES: readonly ["UNSUPPORTED_PROTOCOL", "INVALID_ARGUMENT", "NOT_AUTHORITY", "NOT_ALLOWED_IN_VIEW", "AUTHORITY_UNAVAILABLE", "MESSAGE_TOO_LARGE", "SNAPSHOT_TOO_LARGE", "DATA_TOO_LARGE", "RATE_LIMITED", "ASSET_NOT_FOUND", "SESSION_ENDED", "INTERNAL_ERROR", "SERVICE_UNAVAILABLE"];
8
+ export declare const PROTOCOL_ERROR_CODES: readonly ["UNSUPPORTED_PROTOCOL", "INVALID_ARGUMENT", "NOT_AUTHORITY", "NOT_ALLOWED_IN_VIEW", "AUTHORITY_UNAVAILABLE", "MESSAGE_TOO_LARGE", "SNAPSHOT_TOO_LARGE", "DATA_TOO_LARGE", "RATE_LIMITED", "ASSET_NOT_FOUND", "SESSION_ENDED", "INTERNAL_ERROR", "SERVICE_UNAVAILABLE", "UNKNOWN_METHOD"];
9
9
  export type ProtocolErrorCode = (typeof PROTOCOL_ERROR_CODES)[number];
10
10
  export declare class ProtocolError extends Error {
11
11
  readonly code: ProtocolErrorCode;
@@ -1,17 +1,20 @@
1
1
  /**
2
2
  * @memizy/protocol – Memizy Plugin Protocol v1.
3
3
  * The normative specification is SPEC.md in this package.
4
+ *
5
+ * The public API is listed explicitly: it follows semver within 1.x. Helpers shared by the
6
+ * Memizy packages (rate limiting, call parsing, content policies…) are in
7
+ * `@memizy/protocol/internal` and may change in any release.
4
8
  */
5
- export * from './version';
6
- export * from './errors';
7
- export * from './limits';
8
- export * from './settingsSchema';
9
- export * from './settings';
10
- export * from './manifestSchema';
11
- export * from './pluginRuntime';
12
- export * from './types';
13
- export * from './schemas';
14
- export * from './relay';
15
- export * from './items';
16
- export * from './security';
17
- export * from './polyfills';
9
+ export { PROTOCOL_VERSION, parseProtocolVersion, compareProtocolVersions, negotiateProtocolVersion, isProtocolSupported, type ProtocolVersion, type HostProtocolVersions, } from './version.js';
10
+ export { PROTOCOL_ERROR_CODES, ProtocolError, isProtocolErrorCode, toProtocolError, type ProtocolErrorCode } from './errors.js';
11
+ export { LIMITS, type Limits } from './limits.js';
12
+ export { LocalizedTextSchema, SettingDefinitionSchema, UnknownSettingSchema, type LocalizedText, type SettingDefinition } from './settingsSchema.js';
13
+ export { validateSettingValue, resolveSettings, settingsForMode, localize, isKnownSetting, SETTING_TYPES, type SettingValue, type UnknownSettingDefinition } from './settings.js';
14
+ export { HostAsSchema, MultiplayerModeSchema, ServiceNameSchema, PluginPermissionsSchema, MemizyRuntimeSchema, PluginManifestError, safeParsePluginManifest, parsePluginManifest, readPluginManifestFromHtml, type MemizyRuntimeManifest, type PluginManifest, type PluginManifestResult, } from './manifestSchema.js';
15
+ export { extractManifestFromHtml, type HostAs, type PluginView, type PluginRuntime } from './pluginRuntime.js';
16
+ export * from './types.js';
17
+ export { AddressSchema, HandshakeSchema, OutgoingMessageSchema, AnswerRecordSchema, DataScopeSchema, SettingsUpdateSchema, SessionResultSchema, ErrorReportSchema, } from './schemas.js';
18
+ export { RELAY_PROTOCOL, type RelayBundle, type CreateRoomResponse, type RoomInfo, type RoomPlayer, type RelayClientMessage, type RelayErrorCode, type RelayServerMessage, } from './relay.js';
19
+ export { prepareDisplaySet, type DisplaySet, type PublicItemMark } from './items.js';
20
+ export { PLUGIN_DEVICES, type PluginDevice } from './security.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @memizy/protocol/internal – helpers shared by the Memizy packages (host-sdk, plugin-sdk,
3
+ * the relay server, Memizy Play). Not part of the public API: they may change in any
4
+ * release. Use `@memizy/protocol` instead.
5
+ */
6
+ export { findNonJson, jsonByteSize, assertJsonWithin, RateLimiter } from './limits.js';
7
+ export { toRuntime } from './pluginRuntime.js';
8
+ export { HOST_API_ARGS, parseHostCall } from './schemas.js';
9
+ export { RELAY_LIMITS, normalizePlayerName, isPin } from './relay.js';
10
+ export { seededRandom, displayItem, publicItem } from './items.js';
11
+ export { LIBRARY_ORIGINS, pluginContentPolicy, pluginAllowAttribute, injectContentPolicy } from './security.js';
12
+ export { ensureConnectionPolyfills } from './polyfills.js';
@@ -11,9 +11,12 @@ export declare function seededRandom(seed: string): () => number;
11
11
  /** The item in display order. Answers refer to IDs, so only the lists move. */
12
12
  export declare function displayItem(item: OQSEAnyItem, random: () => number): OQSEAnyItem;
13
13
  /**
14
- * The item without its answer. Custom (`x-`) types lose only `solution` (where OQSE
15
- * puts everything that gives their answer away) and the common fields. `hints` stay:
16
- * a game may show them on any device while the player answers.
14
+ * The item without its answer. An official type keeps only the fields its OQSE schema lists
15
+ * (fail closed: unknown fields, also nested, are removed, as are free-form `appSpecific` and
16
+ * `customData`), then loses the fields that give the answer away. Custom (`x-`) types keep
17
+ * their own fields and lose only `solution` (where OQSE puts everything that gives their
18
+ * answer away) and the common fields. `hints` stay: a game may show them on any device while
19
+ * the player answers.
17
20
  */
18
21
  export declare function publicItem(item: OQSEAnyItem): OQSEAnyItem;
19
22
  /**
@@ -25,10 +25,26 @@ export declare const LIMITS: {
25
25
  readonly answerBytes: number;
26
26
  /** Max serialized size of a generated item attached to `recordAnswer`. */
27
27
  readonly generatedItemBytes: number;
28
- /** Max serialized size of a service request (its result may be up to `messageBytes * 4`). */
28
+ /** `recordAnswer` calls per second and instance (sustained); the authority: per player. */
29
+ readonly answersPerSecond: 10;
30
+ /** Burst size of the `recordAnswer` rate limiter. */
31
+ readonly answerBurst: 20;
32
+ /** Max serialized size of a service request. */
29
33
  readonly serviceBytes: number;
34
+ /** Max serialized size of a service result. */
35
+ readonly serviceResultBytes: number;
30
36
  /** Service calls per minute and instance (sustained). */
31
37
  readonly servicesPerMinute: 30;
38
+ /** `getAsset` calls per second and instance (sustained). */
39
+ readonly assetsPerSecond: 10;
40
+ /** Burst size of the `getAsset` rate limiter. */
41
+ readonly assetBurst: 20;
42
+ /** Size of one asset every host can deliver through `getAsset`. */
43
+ readonly assetBytes: number;
44
+ /** `reportError` calls per second and instance the host keeps (more are dropped). */
45
+ readonly errorReportsPerSecond: 5;
46
+ /** Burst size of the `reportError` limiter. */
47
+ readonly errorReportBurst: 10;
32
48
  };
33
49
  export type Limits = {
34
50
  [K in keyof typeof LIMITS]: number;
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { OQSEManifest } from '@memizy/oqse';
3
- import { PluginRuntime } from './pluginRuntime';
3
+ import { PluginRuntime } from './pluginRuntime.js';
4
4
  export declare const HostAsSchema: z.ZodEnum<{
5
5
  presenter: "presenter";
6
6
  player: "player";
@@ -51,55 +51,48 @@ export declare const MemizyRuntimeSchema: z.ZodObject<{
51
51
  lateJoin: z.ZodOptional<z.ZodBoolean>;
52
52
  }, z.core.$loose>>;
53
53
  }, z.core.$loose>;
54
- settings: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
55
- type: z.ZodLiteral<"number">;
56
- default: z.ZodNumber;
57
- min: z.ZodOptional<z.ZodNumber>;
58
- max: z.ZodOptional<z.ZodNumber>;
59
- step: z.ZodOptional<z.ZodNumber>;
60
- id: z.ZodString;
61
- label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
62
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
63
- modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
64
- solo: "solo";
65
- multiplayer: "multiplayer";
66
- }>>>;
67
- }, z.core.$loose>, z.ZodObject<{
68
- type: z.ZodLiteral<"boolean">;
69
- default: z.ZodBoolean;
70
- id: z.ZodString;
71
- label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
72
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
73
- modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
74
- solo: "solo";
75
- multiplayer: "multiplayer";
76
- }>>>;
77
- }, z.core.$loose>, z.ZodObject<{
78
- type: z.ZodLiteral<"select">;
79
- options: z.ZodArray<z.ZodObject<{
80
- value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
81
- label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
82
- }, z.core.$loose>>;
83
- default: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
84
- id: z.ZodString;
85
- label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
86
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
87
- modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
88
- solo: "solo";
89
- multiplayer: "multiplayer";
90
- }>>>;
91
- }, z.core.$loose>, z.ZodObject<{
92
- type: z.ZodLiteral<"text">;
93
- default: z.ZodString;
94
- maxLength: z.ZodOptional<z.ZodNumber>;
95
- id: z.ZodString;
96
- label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
97
- description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
98
- modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
99
- solo: "solo";
100
- multiplayer: "multiplayer";
101
- }>>>;
102
- }, z.core.$loose>], "type">>>;
54
+ settings: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodUnknown, z.ZodTransform<import('./settings.js').UnknownSettingDefinition | {
55
+ [x: string]: unknown;
56
+ type: "number";
57
+ default: number;
58
+ id: string;
59
+ label: string | Record<string, string>;
60
+ min?: number | undefined;
61
+ max?: number | undefined;
62
+ step?: number | undefined;
63
+ description?: string | Record<string, string> | undefined;
64
+ modes?: ("solo" | "multiplayer")[] | undefined;
65
+ } | {
66
+ [x: string]: unknown;
67
+ type: "select";
68
+ options: {
69
+ [x: string]: unknown;
70
+ value: string | number | boolean;
71
+ label: string | Record<string, string>;
72
+ }[];
73
+ default: string | number | boolean;
74
+ id: string;
75
+ label: string | Record<string, string>;
76
+ description?: string | Record<string, string> | undefined;
77
+ modes?: ("solo" | "multiplayer")[] | undefined;
78
+ } | {
79
+ [x: string]: unknown;
80
+ type: "text";
81
+ default: string;
82
+ id: string;
83
+ label: string | Record<string, string>;
84
+ maxLength?: number | undefined;
85
+ description?: string | Record<string, string> | undefined;
86
+ modes?: ("solo" | "multiplayer")[] | undefined;
87
+ } | {
88
+ [x: string]: unknown;
89
+ type: "boolean";
90
+ default: boolean;
91
+ id: string;
92
+ label: string | Record<string, string>;
93
+ description?: string | Record<string, string> | undefined;
94
+ modes?: ("solo" | "multiplayer")[] | undefined;
95
+ }, unknown>>>>;
103
96
  settingsScreen: z.ZodOptional<z.ZodObject<{
104
97
  size: z.ZodEnum<{
105
98
  compact: "compact";
@@ -1,5 +1,6 @@
1
- import { MemizyRuntimeManifest } from './manifestSchema';
2
- import { SettingDefinition } from './settingsSchema';
1
+ import { MemizyRuntimeManifest } from './manifestSchema.js';
2
+ import { SettingDefinition } from './settingsSchema.js';
3
+ import { UnknownSettingDefinition } from './settings.js';
3
4
  export type HostAs = 'presenter' | 'player';
4
5
  export type PluginView = 'solo' | 'board' | 'controller' | 'settings';
5
6
  /** The runtime part with all defaults applied – what hosts work with. */
@@ -18,6 +19,11 @@ export interface PluginRuntime {
18
19
  lateJoin: boolean;
19
20
  };
20
21
  settings: SettingDefinition[];
22
+ /**
23
+ * Settings of types from a later protocol version (SPEC 2): not shown; the plugin gets
24
+ * their `default` (pass them to `resolveSettings`).
25
+ */
26
+ unknownSettings: UnknownSettingDefinition[];
21
27
  settingsScreen: null | {
22
28
  size: 'compact' | 'large';
23
29
  };
@@ -16,9 +16,15 @@
16
16
  * - `GET /api/health`
17
17
  * WebSocket: `/ws`.
18
18
  */
19
+ /**
20
+ * Version of the relay wire protocol (client <-> server). Within version 1 messages only
21
+ * change additively: receivers ignore unknown fields; the server answers an unknown `t`
22
+ * with a non-fatal `NOT_ALLOWED` error. The messages between the host app and the player
23
+ * apps (inside `data`) have their own version (`RELAY_APP_VERSION` in @memizy/host-sdk).
24
+ */
19
25
  export declare const RELAY_PROTOCOL = 1;
20
26
  export declare const RELAY_LIMITS: {
21
- /** Max size of one WebSocket frame (UTF-8 JSON). */
27
+ /** Max size of one WebSocket frame (UTF-8 JSON); fits an asset of `LIMITS.assetBytes` as base64. */
22
28
  readonly frameBytes: number;
23
29
  /** Max size of an uploaded bundle (plugin + set). */
24
30
  readonly bundleBytes: number;
@@ -32,6 +38,8 @@ export declare const RELAY_LIMITS: {
32
38
  readonly hostFrameBurst: 6000;
33
39
  /** Max player name length. */
34
40
  readonly nameLength: 32;
41
+ /** Max length of the `auth` a player sends when joining. */
42
+ readonly authLength: 4096;
35
43
  /** A room is closed when its host has been away this long. */
36
44
  readonly hostAwayMs: number;
37
45
  /** A room is closed after this time in any case. */
@@ -55,10 +63,15 @@ export interface RoomInfo {
55
63
  appName: string | null;
56
64
  players: number;
57
65
  }
58
- export interface RelayPlayer {
66
+ export interface RoomPlayer {
59
67
  id: string;
60
68
  name: string;
61
69
  connected: boolean;
70
+ /**
71
+ * What the player's app sent as `auth` when joining (e.g. a signed token of a signed-in
72
+ * student). Only the host gets it; the server does not check it – the host app does.
73
+ */
74
+ auth?: string;
62
75
  }
63
76
  export type RelayClientMessage =
64
77
  /** The host attaches to its room (also after a reconnect). */
@@ -68,13 +81,18 @@ export type RelayClientMessage =
68
81
  pin: string;
69
82
  hostToken: string;
70
83
  }
71
- /** A player joins (with `token`: rejoins as the same player). */
84
+ /**
85
+ * A player joins (with `token`: rejoins as the same player). `auth` (optional, at most
86
+ * `RELAY_LIMITS.authLength` characters) is passed to the host only, for apps with
87
+ * signed-in players; a rejoin with `auth` replaces the earlier one.
88
+ */
72
89
  | {
73
90
  t: 'join';
74
91
  protocol: number;
75
92
  pin: string;
76
93
  name: string;
77
94
  token?: string;
95
+ auth?: string;
78
96
  }
79
97
  /** Host → one player. */
80
98
  | {
@@ -114,7 +132,7 @@ export type RelayServerMessage =
114
132
  {
115
133
  t: 'hosting';
116
134
  pin: string;
117
- players: RelayPlayer[];
135
+ players: RoomPlayer[];
118
136
  }
119
137
  /** To a player after `join`. Keep `token` to rejoin as the same player. */
120
138
  | {
@@ -128,7 +146,7 @@ export type RelayServerMessage =
128
146
  /** To the host: a player joined, reconnected, disconnected or was renamed. */
129
147
  | {
130
148
  t: 'presence';
131
- player: RelayPlayer;
149
+ player: RoomPlayer;
132
150
  event: 'join' | 'connect' | 'disconnect' | 'rename' | 'leave';
133
151
  }
134
152
  /** To players: the host connection state. */
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { HostApi } from './types';
2
+ import { HostApi } from './types.js';
3
3
  export declare const AddressSchema: z.ZodString;
4
4
  export declare const HandshakeSchema: z.ZodObject<{
5
5
  protocol: z.ZodString;
@@ -25,13 +25,13 @@ export declare const AnswerRecordSchema: z.ZodObject<{
25
25
  timeSpentMs: z.ZodOptional<z.ZodNumber>;
26
26
  hintsUsed: z.ZodOptional<z.ZodNumber>;
27
27
  isSkipped: z.ZodOptional<z.ZodBoolean>;
28
+ score: z.ZodOptional<z.ZodNumber>;
28
29
  answer: z.ZodOptional<z.ZodUnknown>;
29
30
  item: z.ZodOptional<z.ZodObject<{
30
31
  id: z.ZodString;
31
32
  type: z.ZodString;
32
33
  }, z.core.$loose>>;
33
34
  }, z.core.$loose>;
34
- export declare const ProgressRecordsSchema: z.ZodRecord<z.ZodString, z.ZodType<import('@memizy/oqse').ProgressRecord, unknown, z.core.$ZodTypeInternals<import('@memizy/oqse').ProgressRecord, unknown>>>;
35
35
  export declare const DataScopeSchema: z.ZodEnum<{
36
36
  set: "set";
37
37
  plugin: "plugin";
@@ -45,9 +45,6 @@ export declare const SessionResultSchema: z.ZodObject<{
45
45
  scores: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
46
46
  summary: z.ZodOptional<z.ZodString>;
47
47
  }, z.core.$loose>;
48
- export declare const ResizeRequestSchema: z.ZodObject<{
49
- height: z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"auto">]>;
50
- }, z.core.$loose>;
51
48
  export declare const ErrorReportSchema: z.ZodObject<{
52
49
  code: z.ZodString;
53
50
  message: z.ZodString;
@@ -81,13 +78,13 @@ export declare const HOST_API_ARGS: {
81
78
  timeSpentMs: z.ZodOptional<z.ZodNumber>;
82
79
  hintsUsed: z.ZodOptional<z.ZodNumber>;
83
80
  isSkipped: z.ZodOptional<z.ZodBoolean>;
81
+ score: z.ZodOptional<z.ZodNumber>;
84
82
  answer: z.ZodOptional<z.ZodUnknown>;
85
83
  item: z.ZodOptional<z.ZodObject<{
86
84
  id: z.ZodString;
87
85
  type: z.ZodString;
88
86
  }, z.core.$loose>>;
89
87
  }, z.core.$loose>], null>;
90
- saveProgress: z.ZodTuple<[z.ZodRecord<z.ZodString, z.ZodType<import('@memizy/oqse').ProgressRecord, unknown, z.core.$ZodTypeInternals<import('@memizy/oqse').ProgressRecord, unknown>>>], null>;
91
88
  saveData: z.ZodTuple<[z.ZodEnum<{
92
89
  set: "set";
93
90
  plugin: "plugin";
@@ -102,15 +99,11 @@ export declare const HOST_API_ARGS: {
102
99
  scores: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
103
100
  summary: z.ZodOptional<z.ZodString>;
104
101
  }, z.core.$loose>], null>;
105
- resize: z.ZodTuple<[z.ZodObject<{
106
- height: z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"auto">]>;
107
- }, z.core.$loose>], null>;
108
102
  reportError: z.ZodTuple<[z.ZodObject<{
109
103
  code: z.ZodString;
110
104
  message: z.ZodString;
111
105
  context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
112
106
  }, z.core.$loose>], null>;
113
- exit: z.ZodTuple<[], null>;
114
107
  service: z.ZodTuple<[z.ZodString, z.ZodUnknown], null>;
115
108
  };
116
109
  /**
@@ -1,4 +1,4 @@
1
- import { PluginRuntime } from './pluginRuntime';
1
+ import { PluginRuntime } from './pluginRuntime.js';
2
2
  /** CDNs plugins load libraries from (scripts, styles, fonts, data files). */
3
3
  export declare const LIBRARY_ORIGINS: string[];
4
4
  /** Device permissions a plugin may declare, and their iframe `allow` names. */
@@ -1,5 +1,24 @@
1
- import { LocalizedText, SettingDefinition } from './settingsSchema';
2
- export type SettingValue = string | number | boolean;
1
+ import { LocalizedText, SettingDefinition } from './settingsSchema.js';
2
+ /** Setting types of this protocol version. */
3
+ export declare const SETTING_TYPES: readonly ["number", "boolean", "select", "text"];
4
+ /**
5
+ * A setting of a type from a later protocol version (SPEC 2): hosts do not show it and pass
6
+ * its `default` to the plugin.
7
+ */
8
+ export interface UnknownSettingDefinition {
9
+ id: string;
10
+ type: string;
11
+ default: unknown;
12
+ label: LocalizedText;
13
+ description?: LocalizedText;
14
+ modes?: ('solo' | 'multiplayer')[];
15
+ }
16
+ /** Whether a manifest setting is of a type this protocol version defines. */
17
+ export declare function isKnownSetting(def: SettingDefinition | UnknownSettingDefinition): def is SettingDefinition;
18
+ /** A setting value: a number, boolean or text, or any JSON (a setting type of a later version). */
19
+ export type SettingValue = string | number | boolean | null | SettingValue[] | {
20
+ [key: string]: SettingValue;
21
+ };
3
22
  /** Returns an error description if `value` is not valid for `def`, otherwise `null`. */
4
23
  export declare function validateSettingValue(def: SettingDefinition, value: unknown): string | null;
5
24
  /**
@@ -7,7 +26,9 @@ export declare function validateSettingValue(def: SettingDefinition, value: unkn
7
26
  * value or its default. Invalid values and unknown keys are reported in `errors`
8
27
  * (and replaced by defaults / dropped).
9
28
  */
10
- export declare function resolveSettings(definitions: SettingDefinition[], input?: Record<string, unknown>): {
29
+ export declare function resolveSettings(definitions: SettingDefinition[], input?: Record<string, unknown>,
30
+ /** Settings of types from a later version: always their default; input values are ignored. */
31
+ unknownSettings?: UnknownSettingDefinition[]): {
11
32
  values: Record<string, SettingValue>;
12
33
  errors: string[];
13
34
  };
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
+ import { UnknownSettingDefinition } from './settings.js';
2
3
  /** A string, or a map of BCP 47 language → string (e.g. `{ "cs": "Čas", "en": "Time" }`). */
3
4
  export declare const LocalizedTextSchema: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
4
5
  export type LocalizedText = z.infer<typeof LocalizedTextSchema>;
6
+ /** A setting of a type this protocol version defines (SPEC 2). */
5
7
  export declare const SettingDefinitionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
8
  type: z.ZodLiteral<"number">;
7
9
  default: z.ZodNumber;
@@ -52,3 +54,64 @@ export declare const SettingDefinitionSchema: z.ZodDiscriminatedUnion<[z.ZodObje
52
54
  }>>>;
53
55
  }, z.core.$loose>], "type">;
54
56
  export type SettingDefinition = z.infer<typeof SettingDefinitionSchema>;
57
+ /**
58
+ * A setting of a type from a later protocol version (SPEC 2): hosts do not show it and
59
+ * pass its `default` (any JSON value) to the plugin.
60
+ */
61
+ export declare const UnknownSettingSchema: z.ZodObject<{
62
+ type: z.ZodString;
63
+ default: z.ZodUnknown & z.ZodType<{} | null, unknown, z.core.$ZodTypeInternals<{} | null, unknown>>;
64
+ id: z.ZodString;
65
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
66
+ description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
67
+ modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
68
+ solo: "solo";
69
+ multiplayer: "multiplayer";
70
+ }>>>;
71
+ }, z.core.$loose>;
72
+ /**
73
+ * One entry of `settings` in a manifest: a known setting (validated by its type, with clear
74
+ * messages) or a setting of a later version (only `id`, `label`, `default`).
75
+ */
76
+ export declare const SettingEntrySchema: z.ZodPipe<z.ZodUnknown, z.ZodTransform<UnknownSettingDefinition | {
77
+ [x: string]: unknown;
78
+ type: "number";
79
+ default: number;
80
+ id: string;
81
+ label: string | Record<string, string>;
82
+ min?: number | undefined;
83
+ max?: number | undefined;
84
+ step?: number | undefined;
85
+ description?: string | Record<string, string> | undefined;
86
+ modes?: ("solo" | "multiplayer")[] | undefined;
87
+ } | {
88
+ [x: string]: unknown;
89
+ type: "select";
90
+ options: {
91
+ [x: string]: unknown;
92
+ value: string | number | boolean;
93
+ label: string | Record<string, string>;
94
+ }[];
95
+ default: string | number | boolean;
96
+ id: string;
97
+ label: string | Record<string, string>;
98
+ description?: string | Record<string, string> | undefined;
99
+ modes?: ("solo" | "multiplayer")[] | undefined;
100
+ } | {
101
+ [x: string]: unknown;
102
+ type: "text";
103
+ default: string;
104
+ id: string;
105
+ label: string | Record<string, string>;
106
+ maxLength?: number | undefined;
107
+ description?: string | Record<string, string> | undefined;
108
+ modes?: ("solo" | "multiplayer")[] | undefined;
109
+ } | {
110
+ [x: string]: unknown;
111
+ type: "boolean";
112
+ default: boolean;
113
+ id: string;
114
+ label: string | Record<string, string>;
115
+ description?: string | Record<string, string> | undefined;
116
+ modes?: ("solo" | "multiplayer")[] | undefined;
117
+ }, unknown>>;