@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.
- package/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +6 -4
- package/SPEC.md +90 -54
- package/dist/errors.cjs +1 -1
- package/dist/errors.js +5 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +11 -12
- package/dist/internal.cjs +1 -0
- package/dist/internal.js +8 -0
- package/dist/items.cjs +1 -1
- package/dist/items.js +107 -44
- package/dist/limits.cjs +1 -1
- package/dist/limits.js +9 -1
- package/dist/manifestSchema.cjs +1 -1
- package/dist/manifestSchema.js +1 -1
- package/dist/pluginRuntime.cjs +1 -1
- package/dist/pluginRuntime.js +26 -24
- package/dist/relay.cjs +1 -1
- package/dist/relay.js +2 -1
- package/dist/schemas.cjs +1 -1
- package/dist/schemas.js +34 -36
- package/dist/settings.cjs +1 -1
- package/dist/settings.js +24 -14
- package/dist/settingsSchema.cjs +1 -1
- package/dist/settingsSchema.js +55 -39
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/index.d.ts +16 -13
- package/dist/src/internal.d.ts +12 -0
- package/dist/src/items.d.ts +6 -3
- package/dist/src/limits.d.ts +17 -1
- package/dist/src/manifestSchema.d.ts +43 -50
- package/dist/src/pluginRuntime.d.ts +8 -2
- package/dist/src/relay.d.ts +23 -5
- package/dist/src/schemas.d.ts +3 -10
- package/dist/src/security.d.ts +1 -1
- package/dist/src/settings.d.ts +24 -3
- package/dist/src/settingsSchema.d.ts +63 -0
- package/dist/src/types.d.ts +33 -31
- package/dist/types.cjs +1 -1
- package/dist/types.js +3 -7
- package/package.json +13 -5
package/dist/settingsSchema.js
CHANGED
|
@@ -1,58 +1,74 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SETTING_TYPES as e } from "./settings.js";
|
|
2
|
+
import { z as t } from "zod";
|
|
2
3
|
//#region src/settingsSchema.ts
|
|
3
|
-
var
|
|
4
|
-
id:
|
|
5
|
-
label:
|
|
6
|
-
description:
|
|
7
|
-
modes:
|
|
8
|
-
},
|
|
9
|
-
...
|
|
10
|
-
type:
|
|
11
|
-
default:
|
|
12
|
-
min:
|
|
13
|
-
max:
|
|
14
|
-
step:
|
|
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
|
-
}),
|
|
22
|
-
...
|
|
23
|
-
type:
|
|
24
|
-
default:
|
|
25
|
-
}),
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
]),
|
|
30
|
-
...
|
|
31
|
-
type:
|
|
32
|
-
options:
|
|
33
|
-
value:
|
|
34
|
-
label:
|
|
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:
|
|
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
|
-
}),
|
|
44
|
-
...
|
|
45
|
-
type:
|
|
46
|
-
default:
|
|
47
|
-
maxLength:
|
|
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
|
-
}),
|
|
52
|
-
r,
|
|
52
|
+
}), l = t.discriminatedUnion("type", [
|
|
53
53
|
i,
|
|
54
|
-
|
|
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 {
|
|
74
|
+
export { n as LocalizedTextSchema, l as SettingDefinitionSchema, d as SettingEntrySchema, u as UnknownSettingSchema };
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -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;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
11
|
-
export
|
|
12
|
-
export * from './types';
|
|
13
|
-
export
|
|
14
|
-
export
|
|
15
|
-
export
|
|
16
|
-
export
|
|
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';
|
package/dist/src/items.d.ts
CHANGED
|
@@ -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.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
/**
|
package/dist/src/limits.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
};
|
package/dist/src/relay.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
/**
|
|
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:
|
|
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:
|
|
149
|
+
player: RoomPlayer;
|
|
132
150
|
event: 'join' | 'connect' | 'disconnect' | 'rename' | 'leave';
|
|
133
151
|
}
|
|
134
152
|
/** To players: the host connection state. */
|
package/dist/src/schemas.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/src/security.d.ts
CHANGED
|
@@ -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. */
|
package/dist/src/settings.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
import { LocalizedText, SettingDefinition } from './settingsSchema';
|
|
2
|
-
|
|
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>>;
|