@memizy/protocol 0.0.0-stage → 1.0.0-rc.6

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +22 -2
  4. package/SPEC.md +497 -0
  5. package/dist/errors.cjs +1 -0
  6. package/dist/errors.js +37 -0
  7. package/dist/index.cjs +1 -0
  8. package/dist/index.js +13 -0
  9. package/dist/internal.cjs +1 -0
  10. package/dist/internal.js +8 -0
  11. package/dist/items.cjs +1 -0
  12. package/dist/items.js +114 -0
  13. package/dist/limits.cjs +1 -0
  14. package/dist/limits.js +73 -0
  15. package/dist/manifestSchema.cjs +2 -0
  16. package/dist/manifestSchema.js +107 -0
  17. package/dist/pluginRuntime.cjs +1 -0
  18. package/dist/pluginRuntime.js +48 -0
  19. package/dist/polyfills.cjs +1 -0
  20. package/dist/polyfills.js +23 -0
  21. package/dist/relay.cjs +1 -0
  22. package/dist/relay.js +22 -0
  23. package/dist/schemas.cjs +1 -0
  24. package/dist/schemas.js +74 -0
  25. package/dist/security.cjs +1 -0
  26. package/dist/security.js +40 -0
  27. package/dist/settings.cjs +1 -0
  28. package/dist/settings.js +35 -0
  29. package/dist/settingsSchema.cjs +1 -0
  30. package/dist/settingsSchema.js +58 -0
  31. package/dist/src/errors.d.ts +21 -0
  32. package/dist/src/index.d.ts +20 -0
  33. package/dist/src/internal.d.ts +12 -0
  34. package/dist/src/items.d.ts +27 -0
  35. package/dist/src/limits.d.ts +78 -0
  36. package/dist/src/manifestSchema.d.ts +159 -0
  37. package/dist/src/pluginRuntime.d.ts +47 -0
  38. package/dist/src/polyfills.d.ts +6 -0
  39. package/dist/src/relay.d.ts +186 -0
  40. package/dist/src/schemas.d.ts +111 -0
  41. package/dist/src/security.d.ts +21 -0
  42. package/dist/src/settings.d.ts +22 -0
  43. package/dist/src/settingsSchema.d.ts +54 -0
  44. package/dist/src/types.d.ts +182 -0
  45. package/dist/src/version.d.ts +30 -0
  46. package/dist/types.cjs +1 -0
  47. package/dist/types.js +24 -0
  48. package/dist/version.cjs +1 -0
  49. package/dist/version.js +31 -0
  50. package/package.json +63 -4
@@ -0,0 +1,111 @@
1
+ import { z } from 'zod';
2
+ import { HostApi } from './types.js';
3
+ export declare const AddressSchema: z.ZodString;
4
+ export declare const HandshakeSchema: z.ZodObject<{
5
+ protocol: z.ZodString;
6
+ sdk: z.ZodObject<{
7
+ name: z.ZodString;
8
+ version: z.ZodString;
9
+ }, z.core.$loose>;
10
+ plugin: z.ZodObject<{
11
+ id: z.ZodString;
12
+ version: z.ZodString;
13
+ }, z.core.$loose>;
14
+ features: z.ZodArray<z.ZodString>;
15
+ }, z.core.$loose>;
16
+ export declare const OutgoingMessageSchema: z.ZodObject<{
17
+ to: z.ZodUnion<readonly [z.ZodLiteral<"authority">, z.ZodLiteral<"all">, z.ZodArray<z.ZodString>]>;
18
+ data: z.ZodUnknown;
19
+ }, z.core.$loose>;
20
+ export declare const AnswerRecordSchema: z.ZodObject<{
21
+ playerId: z.ZodOptional<z.ZodString>;
22
+ itemId: z.ZodString;
23
+ isCorrect: z.ZodBoolean;
24
+ confidence: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
25
+ timeSpentMs: z.ZodOptional<z.ZodNumber>;
26
+ hintsUsed: z.ZodOptional<z.ZodNumber>;
27
+ isSkipped: z.ZodOptional<z.ZodBoolean>;
28
+ answer: z.ZodOptional<z.ZodUnknown>;
29
+ item: z.ZodOptional<z.ZodObject<{
30
+ id: z.ZodString;
31
+ type: z.ZodString;
32
+ }, z.core.$loose>>;
33
+ }, z.core.$loose>;
34
+ export declare const DataScopeSchema: z.ZodEnum<{
35
+ set: "set";
36
+ plugin: "plugin";
37
+ }>;
38
+ export declare const SettingsUpdateSchema: z.ZodObject<{
39
+ values: z.ZodRecord<z.ZodString, z.ZodUnknown>;
40
+ valid: z.ZodBoolean;
41
+ message: z.ZodOptional<z.ZodString>;
42
+ }, z.core.$loose>;
43
+ export declare const SessionResultSchema: z.ZodObject<{
44
+ scores: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
45
+ summary: z.ZodOptional<z.ZodString>;
46
+ }, z.core.$loose>;
47
+ export declare const ErrorReportSchema: z.ZodObject<{
48
+ code: z.ZodString;
49
+ message: z.ZodString;
50
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
51
+ }, z.core.$loose>;
52
+ /** Argument schemas of every Host API method. */
53
+ export declare const HOST_API_ARGS: {
54
+ hello: z.ZodTuple<[z.ZodObject<{
55
+ protocol: z.ZodString;
56
+ sdk: z.ZodObject<{
57
+ name: z.ZodString;
58
+ version: z.ZodString;
59
+ }, z.core.$loose>;
60
+ plugin: z.ZodObject<{
61
+ id: z.ZodString;
62
+ version: z.ZodString;
63
+ }, z.core.$loose>;
64
+ features: z.ZodArray<z.ZodString>;
65
+ }, z.core.$loose>], null>;
66
+ ready: z.ZodTuple<[], null>;
67
+ send: z.ZodTuple<[z.ZodObject<{
68
+ to: z.ZodUnion<readonly [z.ZodLiteral<"authority">, z.ZodLiteral<"all">, z.ZodArray<z.ZodString>]>;
69
+ data: z.ZodUnknown;
70
+ }, z.core.$loose>], null>;
71
+ saveSnapshot: z.ZodTuple<[z.ZodUnknown], null>;
72
+ recordAnswer: z.ZodTuple<[z.ZodObject<{
73
+ playerId: z.ZodOptional<z.ZodString>;
74
+ itemId: z.ZodString;
75
+ isCorrect: z.ZodBoolean;
76
+ confidence: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
77
+ timeSpentMs: z.ZodOptional<z.ZodNumber>;
78
+ hintsUsed: z.ZodOptional<z.ZodNumber>;
79
+ isSkipped: z.ZodOptional<z.ZodBoolean>;
80
+ answer: z.ZodOptional<z.ZodUnknown>;
81
+ item: z.ZodOptional<z.ZodObject<{
82
+ id: z.ZodString;
83
+ type: z.ZodString;
84
+ }, z.core.$loose>>;
85
+ }, z.core.$loose>], null>;
86
+ saveData: z.ZodTuple<[z.ZodEnum<{
87
+ set: "set";
88
+ plugin: "plugin";
89
+ }>, z.ZodUnknown], null>;
90
+ updateSettings: z.ZodTuple<[z.ZodObject<{
91
+ values: z.ZodRecord<z.ZodString, z.ZodUnknown>;
92
+ valid: z.ZodBoolean;
93
+ message: z.ZodOptional<z.ZodString>;
94
+ }, z.core.$loose>], null>;
95
+ getAsset: z.ZodTuple<[z.ZodString, z.ZodOptional<z.ZodString>], null>;
96
+ end: z.ZodTuple<[z.ZodObject<{
97
+ scores: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
98
+ summary: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$loose>], null>;
100
+ reportError: z.ZodTuple<[z.ZodObject<{
101
+ code: z.ZodString;
102
+ message: z.ZodString;
103
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
104
+ }, z.core.$loose>], null>;
105
+ service: z.ZodTuple<[z.ZodString, z.ZodUnknown], null>;
106
+ };
107
+ /**
108
+ * Validates the arguments of a Host API call.
109
+ * @throws {ProtocolError} `INVALID_ARGUMENT` with readable details.
110
+ */
111
+ export declare function parseHostCall<M extends keyof HostApi>(method: M, args: unknown[]): Parameters<HostApi[M]>;
@@ -0,0 +1,21 @@
1
+ import { PluginRuntime } from './pluginRuntime.js';
2
+ /** CDNs plugins load libraries from (scripts, styles, fonts, data files). */
3
+ export declare const LIBRARY_ORIGINS: string[];
4
+ /** Device permissions a plugin may declare, and their iframe `allow` names. */
5
+ export declare const PLUGIN_DEVICES: readonly ["camera", "microphone", "geolocation", "serial", "bluetooth"];
6
+ export type PluginDevice = (typeof PLUGIN_DEVICES)[number];
7
+ /**
8
+ * The Content Security Policy of a plugin iframe.
9
+ *
10
+ * - Scripts only inline (the plugin itself), from the library CDNs and from the host
11
+ * (`hostOrigin`, which serves the SDK in development and in Memizy Play).
12
+ * - Network requests (`fetch`, WebSocket…) only to the CDNs and the declared
13
+ * `permissions.network` origins.
14
+ * - Images and media may come from any https URL (study sets link them), so an image
15
+ * URL can still carry data out: the policy blocks data channels, not every leak.
16
+ */
17
+ export declare function pluginContentPolicy(runtime: Pick<PluginRuntime, 'permissions'> | null, hostOrigin: string | null): string;
18
+ /** The iframe `allow` attribute for the declared devices (empty when none). */
19
+ export declare function pluginAllowAttribute(runtime: Pick<PluginRuntime, 'permissions'> | null): string;
20
+ /** Puts the policy into the plugin HTML as the first element of `<head>` (before any script). */
21
+ export declare function injectContentPolicy(html: string, policy: string): string;
@@ -0,0 +1,22 @@
1
+ import { LocalizedText, SettingDefinition } from './settingsSchema.js';
2
+ export type SettingValue = string | number | boolean;
3
+ /** Returns an error description if `value` is not valid for `def`, otherwise `null`. */
4
+ export declare function validateSettingValue(def: SettingDefinition, value: unknown): string | null;
5
+ /**
6
+ * Builds the full settings object: every declared setting gets the valid input
7
+ * value or its default. Invalid values and unknown keys are reported in `errors`
8
+ * (and replaced by defaults / dropped).
9
+ */
10
+ export declare function resolveSettings(definitions: SettingDefinition[], input?: Record<string, unknown>): {
11
+ values: Record<string, SettingValue>;
12
+ errors: string[];
13
+ };
14
+ /** The settings a host shows for a mode (`modes` absent = both modes). */
15
+ export declare function settingsForMode<T extends {
16
+ modes?: readonly string[];
17
+ }>(definitions: readonly T[], mode: 'solo' | 'multiplayer'): T[];
18
+ /**
19
+ * Picks the best text for a locale: exact match (`cs-CZ`), language (`cs`),
20
+ * English, then the first available.
21
+ */
22
+ export declare function localize(text: LocalizedText, locale: string): string;
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ /** A string, or a map of BCP 47 language → string (e.g. `{ "cs": "Čas", "en": "Time" }`). */
3
+ export declare const LocalizedTextSchema: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
4
+ export type LocalizedText = z.infer<typeof LocalizedTextSchema>;
5
+ export declare const SettingDefinitionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
+ type: z.ZodLiteral<"number">;
7
+ default: z.ZodNumber;
8
+ min: z.ZodOptional<z.ZodNumber>;
9
+ max: z.ZodOptional<z.ZodNumber>;
10
+ step: z.ZodOptional<z.ZodNumber>;
11
+ id: z.ZodString;
12
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
13
+ description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
14
+ modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
15
+ solo: "solo";
16
+ multiplayer: "multiplayer";
17
+ }>>>;
18
+ }, z.core.$loose>, z.ZodObject<{
19
+ type: z.ZodLiteral<"boolean">;
20
+ default: z.ZodBoolean;
21
+ id: z.ZodString;
22
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
23
+ description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
24
+ modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
25
+ solo: "solo";
26
+ multiplayer: "multiplayer";
27
+ }>>>;
28
+ }, z.core.$loose>, z.ZodObject<{
29
+ type: z.ZodLiteral<"select">;
30
+ options: z.ZodArray<z.ZodObject<{
31
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
32
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
33
+ }, z.core.$loose>>;
34
+ default: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
35
+ id: z.ZodString;
36
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
37
+ description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
38
+ modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
39
+ solo: "solo";
40
+ multiplayer: "multiplayer";
41
+ }>>>;
42
+ }, z.core.$loose>, z.ZodObject<{
43
+ type: z.ZodLiteral<"text">;
44
+ default: z.ZodString;
45
+ maxLength: z.ZodOptional<z.ZodNumber>;
46
+ id: z.ZodString;
47
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
48
+ description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
49
+ modes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
50
+ solo: "solo";
51
+ multiplayer: "multiplayer";
52
+ }>>>;
53
+ }, z.core.$loose>], "type">;
54
+ export type SettingDefinition = z.infer<typeof SettingDefinitionSchema>;
@@ -0,0 +1,182 @@
1
+ import { OQSEAnyItem, OQSEMeta, ProgressRecord } from '@memizy/oqse';
2
+ import { HostAs, PluginView } from './pluginRuntime.js';
3
+ /** Address of an instance: a player ID, `"board"`, `"settings"` (`SETTINGS_ADDRESS`); `"server"` is reserved. */
4
+ export type Address = string;
5
+ /** Address of the presenter's board. */
6
+ export declare const BOARD_ADDRESS = "board";
7
+ /** Reserved for a future server-side authority (SPEC 3.1). */
8
+ export declare const SERVER_ADDRESS = "server";
9
+ /** Address of the plugin's own settings screen in the lobby (view `settings`). */
10
+ export declare const SETTINGS_ADDRESS = "settings";
11
+ export type SessionMode = 'solo' | 'multiplayer';
12
+ export interface Player {
13
+ id: string;
14
+ name: string;
15
+ isHost: boolean;
16
+ connected: boolean;
17
+ }
18
+ export interface SessionInfo {
19
+ id: string;
20
+ mode: SessionMode;
21
+ /** `null` in solo. */
22
+ hostAs: HostAs | null;
23
+ view: PluginView;
24
+ /** This instance. */
25
+ self: Address;
26
+ /** Who owns the game state. */
27
+ authority: Address;
28
+ /** This instance joined a running game. */
29
+ lateJoin: boolean;
30
+ }
31
+ export interface Handshake {
32
+ /** Protocol version the SDK speaks, e.g. `"1.0"`. */
33
+ protocol: string;
34
+ sdk: {
35
+ name: string;
36
+ version: string;
37
+ };
38
+ plugin: {
39
+ id: string;
40
+ version: string;
41
+ };
42
+ features: string[];
43
+ }
44
+ /** Language and theme of the app (`InitPayload.config`, `configChanged`). */
45
+ export interface HostConfig {
46
+ locale: string;
47
+ theme: 'light' | 'dark';
48
+ }
49
+ export type DataScope = 'plugin' | 'set';
50
+ export interface PluginData {
51
+ /** Data across all sets (`null` = nothing saved yet). */
52
+ plugin: unknown;
53
+ /** Data for this plugin and the current set (`null` = nothing saved yet). */
54
+ set: unknown;
55
+ }
56
+ export interface InitPayload {
57
+ /** Negotiated version: same MAJOR, the lower MINOR. */
58
+ protocol: string;
59
+ host: {
60
+ name: string;
61
+ version: string;
62
+ };
63
+ oqseVersion: string;
64
+ features: string[];
65
+ session: SessionInfo;
66
+ /** Current roster (the presenter is not a player). */
67
+ players: Player[];
68
+ set: {
69
+ meta: OQSEMeta;
70
+ items: OQSEAnyItem[];
71
+ };
72
+ /** Values for every declared setting (defaults applied). */
73
+ settings: Record<string, unknown>;
74
+ config: HostConfig;
75
+ /** The session clock and its pauses (see {@link SessionClock}). */
76
+ clock: SessionClock;
77
+ /** Memizy services this host offers to the plugin (declared in its manifest and available here); call them with `service()`. */
78
+ services: string[];
79
+ /** Learning progress of `self` (empty for board/settings). */
80
+ progress: Record<string, ProgressRecord>;
81
+ /** Plugin data of `self` (`null` values for board/settings). */
82
+ data: PluginData;
83
+ /** Last saved snapshot, only for the authority (resume). */
84
+ snapshot: unknown | null;
85
+ }
86
+ /**
87
+ * The session clock. `Date.now() + offsetMs` is the session time. Pauses: the
88
+ * game time is `(pausedAt ?? sessionTime) - pausedMs` – it stands still while the host
89
+ * has paused the game (`pausedAt` = session time of the pause) and continues afterwards
90
+ * without the paused time (`pausedMs` = total of finished pauses).
91
+ */
92
+ export interface SessionClock {
93
+ offsetMs: number;
94
+ /** Total length of finished pauses (ms). */
95
+ pausedMs: number;
96
+ /** Session time when the current pause started, or `null` while the game runs. */
97
+ pausedAt: number | null;
98
+ }
99
+ export interface AnswerRecord {
100
+ /** Default: self. The authority may record for any player. */
101
+ playerId?: string;
102
+ itemId: string;
103
+ isCorrect: boolean;
104
+ confidence?: 1 | 2 | 3 | 4;
105
+ timeSpentMs?: number;
106
+ hintsUsed?: number;
107
+ isSkipped?: boolean;
108
+ /**
109
+ * What the player answered, in the `checkAnswer` format of the item type
110
+ * (choice IDs, text, numbers…). Lets teachers see which wrong answers were chosen.
111
+ */
112
+ answer?: unknown;
113
+ /**
114
+ * The item itself, required when `itemId` is not in the set (a generated item, e.g.
115
+ * from a service). It needs at least `id` (= `itemId`) and `type`; the answer fields may
116
+ * be left out. Such answers count for statistics (`skills`, `tags`), not for repetition.
117
+ */
118
+ item?: {
119
+ id: string;
120
+ type: string;
121
+ [key: string]: unknown;
122
+ };
123
+ }
124
+ export interface SessionResult {
125
+ /** Final score per player ID (in solo: the single player). */
126
+ scores?: Record<string, number>;
127
+ /** Short plain-text summary shown by the host. */
128
+ summary?: string;
129
+ }
130
+ export interface SettingsUpdate {
131
+ values: Record<string, unknown>;
132
+ valid: boolean;
133
+ message?: string;
134
+ }
135
+ export interface ErrorReport {
136
+ code: string;
137
+ message: string;
138
+ context?: Record<string, unknown>;
139
+ }
140
+ export interface OutgoingMessage {
141
+ /** `'all'` = every other instance. */
142
+ to: 'authority' | 'all' | Address[];
143
+ /** JSON-serializable. */
144
+ data: unknown;
145
+ }
146
+ export interface IncomingMessage {
147
+ from: Address;
148
+ data: unknown;
149
+ /** Session clock (ms). */
150
+ sentAt: number;
151
+ }
152
+ export type SessionEndReason = 'finished' | 'host-left' | 'kicked' | 'closed' | 'error';
153
+ /** Methods the host exposes to the plugin (plugin → host). SPEC section 5. */
154
+ export interface HostApi {
155
+ hello(handshake: Handshake): Promise<InitPayload>;
156
+ ready(): Promise<void>;
157
+ send(message: OutgoingMessage): Promise<void>;
158
+ saveSnapshot(snapshot: unknown): Promise<void>;
159
+ recordAnswer(answer: AnswerRecord): Promise<void>;
160
+ saveData(scope: DataScope, value: unknown): Promise<void>;
161
+ updateSettings(update: SettingsUpdate): Promise<void>;
162
+ getAsset(key: string, itemId?: string): Promise<Blob>;
163
+ end(result: SessionResult): Promise<void>;
164
+ reportError(error: ErrorReport): Promise<void>;
165
+ /** Calls a Memizy service (one of `InitPayload.services`); returns its JSON result. */
166
+ service(name: string, payload: unknown): Promise<unknown>;
167
+ }
168
+ /** Methods the plugin exposes to the host (host → plugin). SPEC section 7. */
169
+ export interface PluginApi {
170
+ start(): Promise<void>;
171
+ deliver(message: IncomingMessage): Promise<void>;
172
+ playersChanged(players: Player[]): Promise<void>;
173
+ authorityChanged(status: {
174
+ connected: boolean;
175
+ }): Promise<void>;
176
+ configChanged(config: HostConfig): Promise<void>;
177
+ /** The clock was corrected, or the game was paused / resumed. */
178
+ clockChanged(clock: SessionClock): Promise<void>;
179
+ sessionEnded(reason: SessionEndReason): Promise<void>;
180
+ }
181
+ export declare const HOST_API_METHODS: readonly ["hello", "ready", "send", "saveSnapshot", "recordAnswer", "saveData", "updateSettings", "getAsset", "end", "reportError", "service"];
182
+ export declare const PLUGIN_API_METHODS: readonly ["start", "deliver", "playersChanged", "authorityChanged", "configChanged", "clockChanged", "sessionEnded"];
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Protocol version handling (SPEC section 4.3).
3
+ */
4
+ /** The protocol version implemented by this package. */
5
+ export declare const PROTOCOL_VERSION = "1.0";
6
+ export interface ProtocolVersion {
7
+ major: number;
8
+ minor: number;
9
+ }
10
+ /** Parses a `MAJOR.MINOR` version. Returns `null` for anything else. */
11
+ export declare function parseProtocolVersion(version: string): ProtocolVersion | null;
12
+ /** Compares two `MAJOR.MINOR` versions (negative: a < b, 0: equal, positive: a > b). */
13
+ export declare function compareProtocolVersions(a: string, b: string): number;
14
+ /**
15
+ * The protocol versions a host supports: one version, or one per MAJOR it supports
16
+ * (e.g. `['1.4', '2.1']`, SPEC 4.3).
17
+ */
18
+ export type HostProtocolVersions = string | readonly string[];
19
+ /**
20
+ * Negotiates the version both sides use: the plugin's MAJOR (which the host must
21
+ * support), the lower MINOR. Returns `null` when the host does not support that MAJOR
22
+ * or a version is invalid.
23
+ */
24
+ export declare function negotiateProtocolVersion(plugin: string, host: HostProtocolVersions): string | null;
25
+ /**
26
+ * Checks before loading the iframe whether a host can run a plugin whose
27
+ * manifest requires `required` (SPEC 4.3): the host supports its MAJOR with a MINOR ≥
28
+ * the required one.
29
+ */
30
+ export declare function isProtocolSupported(required: string, host?: HostProtocolVersions): boolean;
package/dist/types.cjs ADDED
@@ -0,0 +1 @@
1
+ var e=`board`,t=`server`,n=`settings`,r=[`hello`,`ready`,`send`,`saveSnapshot`,`recordAnswer`,`saveData`,`updateSettings`,`getAsset`,`end`,`reportError`,`service`],i=[`start`,`deliver`,`playersChanged`,`authorityChanged`,`configChanged`,`clockChanged`,`sessionEnded`];exports.BOARD_ADDRESS=e,exports.HOST_API_METHODS=r,exports.PLUGIN_API_METHODS=i,exports.SERVER_ADDRESS=t,exports.SETTINGS_ADDRESS=n;
package/dist/types.js ADDED
@@ -0,0 +1,24 @@
1
+ //#region src/types.ts
2
+ var e = "board", t = "server", n = "settings", r = [
3
+ "hello",
4
+ "ready",
5
+ "send",
6
+ "saveSnapshot",
7
+ "recordAnswer",
8
+ "saveData",
9
+ "updateSettings",
10
+ "getAsset",
11
+ "end",
12
+ "reportError",
13
+ "service"
14
+ ], i = [
15
+ "start",
16
+ "deliver",
17
+ "playersChanged",
18
+ "authorityChanged",
19
+ "configChanged",
20
+ "clockChanged",
21
+ "sessionEnded"
22
+ ];
23
+ //#endregion
24
+ export { e as BOARD_ADDRESS, r as HOST_API_METHODS, i as PLUGIN_API_METHODS, t as SERVER_ADDRESS, n as SETTINGS_ADDRESS };
@@ -0,0 +1 @@
1
+ var e=`1.0`,t=/^(\d+)\.(\d+)$/;function n(e){let n=t.exec(e);return n?{major:Number(n[1]),minor:Number(n[2])}:null}function r(e,t){let r=n(e),i=n(t);if(!r||!i)throw Error(`Invalid protocol version: ${r?t:e}`);return r.major===i.major?r.minor-i.minor:r.major-i.major}function i(e,t){for(let r of typeof t==`string`?[t]:t){let t=n(r);if(t?.major===e)return t}return null}function a(e,t){let r=n(e),a=r&&i(r.major,t);return!r||!a?null:`${r.major}.${Math.min(r.minor,a.minor)}`}function o(e,t=`1.0`){let r=n(e),a=r&&i(r.major,t);return!!r&&!!a&&a.minor>=r.minor}exports.PROTOCOL_VERSION=e,exports.compareProtocolVersions=r,exports.isProtocolSupported=o,exports.negotiateProtocolVersion=a,exports.parseProtocolVersion=n;
@@ -0,0 +1,31 @@
1
+ //#region src/version.ts
2
+ var e = "1.0", t = /^(\d+)\.(\d+)$/;
3
+ function n(e) {
4
+ let n = t.exec(e);
5
+ return n ? {
6
+ major: Number(n[1]),
7
+ minor: Number(n[2])
8
+ } : null;
9
+ }
10
+ function r(e, t) {
11
+ let r = n(e), i = n(t);
12
+ if (!r || !i) throw Error(`Invalid protocol version: ${r ? t : e}`);
13
+ return r.major === i.major ? r.minor - i.minor : r.major - i.major;
14
+ }
15
+ function i(e, t) {
16
+ for (let r of typeof t == "string" ? [t] : t) {
17
+ let t = n(r);
18
+ if (t?.major === e) return t;
19
+ }
20
+ return null;
21
+ }
22
+ function a(e, t) {
23
+ let r = n(e), a = r && i(r.major, t);
24
+ return !r || !a ? null : `${r.major}.${Math.min(r.minor, a.minor)}`;
25
+ }
26
+ function o(e, t = "1.0") {
27
+ let r = n(e), a = r && i(r.major, t);
28
+ return !!r && !!a && a.minor >= r.minor;
29
+ }
30
+ //#endregion
31
+ export { e as PROTOCOL_VERSION, r as compareProtocolVersions, o as isProtocolSupported, a as negotiateProtocolVersion, n as parseProtocolVersion };
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@memizy/protocol",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0-rc.6",
4
+ "description": "Memizy Plugin Protocol: types, Zod schemas and helpers shared by @memizy/plugin-sdk and @memizy/host-sdk.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/src/index.d.ts",
9
+ "sideEffects": false,
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/src/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./internal": {
17
+ "types": "./dist/src/internal.d.ts",
18
+ "import": "./dist/internal.js",
19
+ "require": "./dist/internal.cjs"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "SPEC.md",
25
+ "README.md",
26
+ "CHANGELOG.md",
27
+ "LICENSE"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc --noEmit && vite build && node ../../scripts/dts-extensions.mjs dist",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "typecheck": "tsc --noEmit",
34
+ "prepublishOnly": "npm run build"
35
+ },
36
+ "dependencies": {
37
+ "@memizy/oqse": "^1.0.0-rc.6",
38
+ "zod": "^4.3.6"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.6.0",
42
+ "typescript": "~6.0.2",
43
+ "vite": "^8.0.4",
44
+ "vite-plugin-dts": "^4.5.4",
45
+ "vitest": "^4.1.4"
46
+ },
47
+ "license": "MIT",
48
+ "author": "Memizy Team",
49
+ "keywords": [
50
+ "memizy",
51
+ "plugin",
52
+ "protocol",
53
+ "oqse",
54
+ "education"
55
+ ],
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/memizy/memizy.git",
59
+ "directory": "packages/protocol"
60
+ },
61
+ "bugs": {
62
+ "url": "https://github.com/memizy/memizy/issues"
63
+ },
64
+ "homepage": "https://github.com/memizy/memizy/tree/main/packages/protocol#readme"
65
+ }