@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.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +22 -2
- package/SPEC.md +497 -0
- package/dist/errors.cjs +1 -0
- package/dist/errors.js +37 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +13 -0
- package/dist/internal.cjs +1 -0
- package/dist/internal.js +8 -0
- package/dist/items.cjs +1 -0
- package/dist/items.js +114 -0
- package/dist/limits.cjs +1 -0
- package/dist/limits.js +73 -0
- package/dist/manifestSchema.cjs +2 -0
- package/dist/manifestSchema.js +107 -0
- package/dist/pluginRuntime.cjs +1 -0
- package/dist/pluginRuntime.js +48 -0
- package/dist/polyfills.cjs +1 -0
- package/dist/polyfills.js +23 -0
- package/dist/relay.cjs +1 -0
- package/dist/relay.js +22 -0
- package/dist/schemas.cjs +1 -0
- package/dist/schemas.js +74 -0
- package/dist/security.cjs +1 -0
- package/dist/security.js +40 -0
- package/dist/settings.cjs +1 -0
- package/dist/settings.js +35 -0
- package/dist/settingsSchema.cjs +1 -0
- package/dist/settingsSchema.js +58 -0
- package/dist/src/errors.d.ts +21 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/internal.d.ts +12 -0
- package/dist/src/items.d.ts +27 -0
- package/dist/src/limits.d.ts +78 -0
- package/dist/src/manifestSchema.d.ts +159 -0
- package/dist/src/pluginRuntime.d.ts +47 -0
- package/dist/src/polyfills.d.ts +6 -0
- package/dist/src/relay.d.ts +186 -0
- package/dist/src/schemas.d.ts +111 -0
- package/dist/src/security.d.ts +21 -0
- package/dist/src/settings.d.ts +22 -0
- package/dist/src/settingsSchema.d.ts +54 -0
- package/dist/src/types.d.ts +182 -0
- package/dist/src/version.d.ts +30 -0
- package/dist/types.cjs +1 -0
- package/dist/types.js +24 -0
- package/dist/version.cjs +1 -0
- package/dist/version.js +31 -0
- package/package.json +63 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./version.cjs"),t=require("./errors.cjs"),n=require("./limits.cjs"),r=require("./settingsSchema.cjs"),i=require("./settings.cjs"),a=require("./pluginRuntime.cjs"),o=require("./manifestSchema.cjs"),s=require("./types.cjs"),c=require("./schemas.cjs"),l=require("./relay.cjs"),u=require("./items.cjs"),d=require("./security.cjs");exports.AddressSchema=c.AddressSchema,exports.AnswerRecordSchema=c.AnswerRecordSchema,exports.BOARD_ADDRESS=s.BOARD_ADDRESS,exports.DataScopeSchema=c.DataScopeSchema,exports.ErrorReportSchema=c.ErrorReportSchema,exports.HOST_API_METHODS=s.HOST_API_METHODS,exports.HandshakeSchema=c.HandshakeSchema,exports.HostAsSchema=o.HostAsSchema,exports.LIMITS=n.LIMITS,exports.LocalizedTextSchema=r.LocalizedTextSchema,exports.MemizyRuntimeSchema=o.MemizyRuntimeSchema,exports.MultiplayerModeSchema=o.MultiplayerModeSchema,exports.OutgoingMessageSchema=c.OutgoingMessageSchema,exports.PLUGIN_API_METHODS=s.PLUGIN_API_METHODS,exports.PLUGIN_DEVICES=d.PLUGIN_DEVICES,exports.PROTOCOL_ERROR_CODES=t.PROTOCOL_ERROR_CODES,exports.PROTOCOL_VERSION=e.PROTOCOL_VERSION,exports.PluginManifestError=o.PluginManifestError,exports.PluginPermissionsSchema=o.PluginPermissionsSchema,exports.ProtocolError=t.ProtocolError,exports.RELAY_PROTOCOL=l.RELAY_PROTOCOL,exports.SERVER_ADDRESS=s.SERVER_ADDRESS,exports.SETTINGS_ADDRESS=s.SETTINGS_ADDRESS,exports.ServiceNameSchema=o.ServiceNameSchema,exports.SessionResultSchema=c.SessionResultSchema,exports.SettingDefinitionSchema=r.SettingDefinitionSchema,exports.SettingsUpdateSchema=c.SettingsUpdateSchema,exports.compareProtocolVersions=e.compareProtocolVersions,exports.extractManifestFromHtml=a.extractManifestFromHtml,exports.isProtocolErrorCode=t.isProtocolErrorCode,exports.isProtocolSupported=e.isProtocolSupported,exports.localize=i.localize,exports.negotiateProtocolVersion=e.negotiateProtocolVersion,exports.parsePluginManifest=o.parsePluginManifest,exports.parseProtocolVersion=e.parseProtocolVersion,exports.prepareDisplaySet=u.prepareDisplaySet,exports.readPluginManifestFromHtml=o.readPluginManifestFromHtml,exports.resolveSettings=i.resolveSettings,exports.safeParsePluginManifest=o.safeParsePluginManifest,exports.settingsForMode=i.settingsForMode,exports.toProtocolError=t.toProtocolError,exports.validateSettingValue=i.validateSettingValue;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PROTOCOL_VERSION as e, compareProtocolVersions as t, isProtocolSupported as n, negotiateProtocolVersion as r, parseProtocolVersion as i } from "./version.js";
|
|
2
|
+
import { PROTOCOL_ERROR_CODES as a, ProtocolError as o, isProtocolErrorCode as s, toProtocolError as c } from "./errors.js";
|
|
3
|
+
import { LIMITS as l } from "./limits.js";
|
|
4
|
+
import { LocalizedTextSchema as u, SettingDefinitionSchema as d } from "./settingsSchema.js";
|
|
5
|
+
import { localize as f, resolveSettings as p, settingsForMode as m, validateSettingValue as h } from "./settings.js";
|
|
6
|
+
import { extractManifestFromHtml as g } from "./pluginRuntime.js";
|
|
7
|
+
import { HostAsSchema as _, MemizyRuntimeSchema as v, MultiplayerModeSchema as y, PluginManifestError as b, PluginPermissionsSchema as x, ServiceNameSchema as S, parsePluginManifest as C, readPluginManifestFromHtml as w, safeParsePluginManifest as T } from "./manifestSchema.js";
|
|
8
|
+
import { BOARD_ADDRESS as E, HOST_API_METHODS as D, PLUGIN_API_METHODS as O, SERVER_ADDRESS as k, SETTINGS_ADDRESS as A } from "./types.js";
|
|
9
|
+
import { AddressSchema as j, AnswerRecordSchema as M, DataScopeSchema as N, ErrorReportSchema as P, HandshakeSchema as F, OutgoingMessageSchema as I, SessionResultSchema as L, SettingsUpdateSchema as R } from "./schemas.js";
|
|
10
|
+
import { RELAY_PROTOCOL as z } from "./relay.js";
|
|
11
|
+
import { prepareDisplaySet as B } from "./items.js";
|
|
12
|
+
import { PLUGIN_DEVICES as V } from "./security.js";
|
|
13
|
+
export { j as AddressSchema, M as AnswerRecordSchema, E as BOARD_ADDRESS, N as DataScopeSchema, P as ErrorReportSchema, D as HOST_API_METHODS, F as HandshakeSchema, _ as HostAsSchema, l as LIMITS, u as LocalizedTextSchema, v as MemizyRuntimeSchema, y as MultiplayerModeSchema, I as OutgoingMessageSchema, O as PLUGIN_API_METHODS, V as PLUGIN_DEVICES, a as PROTOCOL_ERROR_CODES, e as PROTOCOL_VERSION, b as PluginManifestError, x as PluginPermissionsSchema, o as ProtocolError, z as RELAY_PROTOCOL, k as SERVER_ADDRESS, A as SETTINGS_ADDRESS, S as ServiceNameSchema, L as SessionResultSchema, d as SettingDefinitionSchema, R as SettingsUpdateSchema, t as compareProtocolVersions, g as extractManifestFromHtml, s as isProtocolErrorCode, n as isProtocolSupported, f as localize, r as negotiateProtocolVersion, C as parsePluginManifest, i as parseProtocolVersion, B as prepareDisplaySet, w as readPluginManifestFromHtml, p as resolveSettings, T as safeParsePluginManifest, m as settingsForMode, c as toProtocolError, h as validateSettingValue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./limits.cjs"),t=require("./pluginRuntime.cjs"),n=require("./schemas.cjs"),r=require("./relay.cjs"),i=require("./items.cjs"),a=require("./security.cjs"),o=require("./polyfills.cjs");exports.HOST_API_ARGS=n.HOST_API_ARGS,exports.LIBRARY_ORIGINS=a.LIBRARY_ORIGINS,exports.RELAY_LIMITS=r.RELAY_LIMITS,exports.RateLimiter=e.RateLimiter,exports.assertJsonWithin=e.assertJsonWithin,exports.displayItem=i.displayItem,exports.ensureConnectionPolyfills=o.ensureConnectionPolyfills,exports.findNonJson=e.findNonJson,exports.injectContentPolicy=a.injectContentPolicy,exports.isPin=r.isPin,exports.jsonByteSize=e.jsonByteSize,exports.normalizePlayerName=r.normalizePlayerName,exports.parseHostCall=n.parseHostCall,exports.pluginAllowAttribute=a.pluginAllowAttribute,exports.pluginContentPolicy=a.pluginContentPolicy,exports.publicItem=i.publicItem,exports.seededRandom=i.seededRandom,exports.toRuntime=t.toRuntime;
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RateLimiter as e, assertJsonWithin as t, findNonJson as n, jsonByteSize as r } from "./limits.js";
|
|
2
|
+
import { toRuntime as i } from "./pluginRuntime.js";
|
|
3
|
+
import { HOST_API_ARGS as a, parseHostCall as o } from "./schemas.js";
|
|
4
|
+
import { RELAY_LIMITS as s, isPin as c, normalizePlayerName as l } from "./relay.js";
|
|
5
|
+
import { displayItem as u, publicItem as d, seededRandom as f } from "./items.js";
|
|
6
|
+
import { LIBRARY_ORIGINS as p, injectContentPolicy as m, pluginAllowAttribute as h, pluginContentPolicy as g } from "./security.js";
|
|
7
|
+
import { ensureConnectionPolyfills as _ } from "./polyfills.js";
|
|
8
|
+
export { a as HOST_API_ARGS, p as LIBRARY_ORIGINS, s as RELAY_LIMITS, e as RateLimiter, t as assertJsonWithin, u as displayItem, _ as ensureConnectionPolyfills, n as findNonJson, m as injectContentPolicy, c as isPin, r as jsonByteSize, l as normalizePlayerName, o as parseHostCall, h as pluginAllowAttribute, g as pluginContentPolicy, d as publicItem, f as seededRandom, i as toRuntime };
|
package/dist/items.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=require("@memizy/oqse");function t(e){let t=2166136261;for(let n=0;n<e.length;n++)t=Math.imul(t^e.charCodeAt(n),16777619);let n=t>>>0;return()=>{n=n+1831565813>>>0;let e=n;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}}function n(e,t,n){for(let r=0;r<6;r++){let r=[...e];for(let e=r.length-1;e>0;e--){let n=Math.floor(t()*(e+1));[r[e],r[n]]=[r[n],r[e]]}if(!n||r.length<2||!n(r))return r}return[...e.slice(1),e[0]]}var r=e=>t=>t.every((t,n)=>t.id===e[n]);function i(t,i){let a=structuredClone(t);switch(a.type){case`mcq-single`:case`mcq-multi`:return a.shuffle!==!1&&(a.options=n(a.options,i)),a;case`fill-in-select`:for(let e of Object.values(a.blanks??{}))e.shuffle!==!1&&(e.options=n(e.options,i));return a;case`sort-items`:return a.items=n(a.items,i,r(a.correctOrder)),a;case`match-pairs`:return a.prompts=n(a.prompts,i),a.matches=n(a.matches,i),a;case`match-complex`:return a.prompts=n(a.prompts,i),a.matches=n(a.matches,i),a;case`timeline`:return a.shuffle!==!1&&(a.events=n(a.events,i,t=>(0,e.isTimelineOrderCorrect)(t))),a;case`categorize`:return a.items=n(a.items,i),a;case`diagram-label`:return a.requireTyping||(a.labels=n(a.labels,i)),a;default:return a}}var a=[`explanation`,`incorrectFeedback`],o={note:[`hiddenContent`],flashcard:[`back`],"true-false":[`correctAnswer`],"mcq-single":[`correctId`],"mcq-multi":[`correctIds`],"short-answer":[`correctAnswers`],"sort-items":[`correctOrder`],"match-pairs":[`pairs`],"match-complex":[`connections`],slider:[`correctAnswer`],"numeric-input":[`correctAnswer`],"math-input":[`correctAnswers`],matrix:[`correctCells`],"open-ended":[`sampleAnswer`,`rubric`],"chess-puzzle":[`correctAnswers`]},s=[`options`,`items`,`prompts`,`matches`,`categories`,`rows`,`columns`,`labels`];function c(e){let t=e.type,n=structuredClone(e);for(let e of a)delete n[e];if(Array.isArray(n.sources))for(let e of n.sources)e&&typeof e==`object`&&delete e.quote;if(t.startsWith(`x-`))return delete n.solution,n.answerHidden=!0,n;for(let e of o[t]??[])delete n[e];for(let e of s)if(Array.isArray(n[e]))for(let t of n[e])t&&typeof t==`object`&&delete t.explanation;switch(t){case`fill-in-blanks`:n.blanks=Object.fromEntries(Object.keys(n.blanks??{}).map(e=>[e,[]]));break;case`fill-in-select`:for(let e of Object.values(n.blanks??{})){delete e.correctId;for(let t of e.options??[])delete t.explanation}break;case`categorize`:for(let e of n.items??[])delete e.correctCategoryId;break;case`timeline`:for(let e of n.events??[])delete e.date,delete e.precision;break;case`diagram-label`:for(let e of n.zones??[])delete e.correctLabelId;n.requireTyping&&(n.labels=[]);break;case`pin-on-image`:case`pin-on-model`:n.hotspots=[]}return n.answerHidden=!0,n}function l(e,n){let r=n===void 0?null:t(n),a=e.items.map(e=>r?i(e,r):structuredClone(e));return{set:{meta:e.meta,items:a},publicSet:{meta:e.meta,items:a.map(c)}}}exports.displayItem=i,exports.prepareDisplaySet=l,exports.publicItem=c,exports.seededRandom=t;
|
package/dist/items.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isTimelineOrderCorrect as e } from "@memizy/oqse";
|
|
2
|
+
//#region src/items.ts
|
|
3
|
+
function t(e) {
|
|
4
|
+
let t = 2166136261;
|
|
5
|
+
for (let n = 0; n < e.length; n++) t = Math.imul(t ^ e.charCodeAt(n), 16777619);
|
|
6
|
+
let n = t >>> 0;
|
|
7
|
+
return () => {
|
|
8
|
+
n = n + 1831565813 >>> 0;
|
|
9
|
+
let e = n;
|
|
10
|
+
return e = Math.imul(e ^ e >>> 15, e | 1), e ^= e + Math.imul(e ^ e >>> 7, e | 61), ((e ^ e >>> 14) >>> 0) / 4294967296;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function n(e, t, n) {
|
|
14
|
+
for (let r = 0; r < 6; r++) {
|
|
15
|
+
let r = [...e];
|
|
16
|
+
for (let e = r.length - 1; e > 0; e--) {
|
|
17
|
+
let n = Math.floor(t() * (e + 1));
|
|
18
|
+
[r[e], r[n]] = [r[n], r[e]];
|
|
19
|
+
}
|
|
20
|
+
if (!n || r.length < 2 || !n(r)) return r;
|
|
21
|
+
}
|
|
22
|
+
return [...e.slice(1), e[0]];
|
|
23
|
+
}
|
|
24
|
+
var r = (e) => (t) => t.every((t, n) => t.id === e[n]);
|
|
25
|
+
function i(t, i) {
|
|
26
|
+
let a = structuredClone(t);
|
|
27
|
+
switch (a.type) {
|
|
28
|
+
case "mcq-single":
|
|
29
|
+
case "mcq-multi": return a.shuffle !== !1 && (a.options = n(a.options, i)), a;
|
|
30
|
+
case "fill-in-select":
|
|
31
|
+
for (let e of Object.values(a.blanks ?? {})) e.shuffle !== !1 && (e.options = n(e.options, i));
|
|
32
|
+
return a;
|
|
33
|
+
case "sort-items": return a.items = n(a.items, i, r(a.correctOrder)), a;
|
|
34
|
+
case "match-pairs": return a.prompts = n(a.prompts, i), a.matches = n(a.matches, i), a;
|
|
35
|
+
case "match-complex": return a.prompts = n(a.prompts, i), a.matches = n(a.matches, i), a;
|
|
36
|
+
case "timeline": return a.shuffle !== !1 && (a.events = n(a.events, i, (t) => e(t))), a;
|
|
37
|
+
case "categorize": return a.items = n(a.items, i), a;
|
|
38
|
+
case "diagram-label": return a.requireTyping || (a.labels = n(a.labels, i)), a;
|
|
39
|
+
default: return a;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
var a = ["explanation", "incorrectFeedback"], o = {
|
|
43
|
+
note: ["hiddenContent"],
|
|
44
|
+
flashcard: ["back"],
|
|
45
|
+
"true-false": ["correctAnswer"],
|
|
46
|
+
"mcq-single": ["correctId"],
|
|
47
|
+
"mcq-multi": ["correctIds"],
|
|
48
|
+
"short-answer": ["correctAnswers"],
|
|
49
|
+
"sort-items": ["correctOrder"],
|
|
50
|
+
"match-pairs": ["pairs"],
|
|
51
|
+
"match-complex": ["connections"],
|
|
52
|
+
slider: ["correctAnswer"],
|
|
53
|
+
"numeric-input": ["correctAnswer"],
|
|
54
|
+
"math-input": ["correctAnswers"],
|
|
55
|
+
matrix: ["correctCells"],
|
|
56
|
+
"open-ended": ["sampleAnswer", "rubric"],
|
|
57
|
+
"chess-puzzle": ["correctAnswers"]
|
|
58
|
+
}, s = [
|
|
59
|
+
"options",
|
|
60
|
+
"items",
|
|
61
|
+
"prompts",
|
|
62
|
+
"matches",
|
|
63
|
+
"categories",
|
|
64
|
+
"rows",
|
|
65
|
+
"columns",
|
|
66
|
+
"labels"
|
|
67
|
+
];
|
|
68
|
+
function c(e) {
|
|
69
|
+
let t = e.type, n = structuredClone(e);
|
|
70
|
+
for (let e of a) delete n[e];
|
|
71
|
+
if (Array.isArray(n.sources)) for (let e of n.sources) e && typeof e == "object" && delete e.quote;
|
|
72
|
+
if (t.startsWith("x-")) return delete n.solution, n.answerHidden = !0, n;
|
|
73
|
+
for (let e of o[t] ?? []) delete n[e];
|
|
74
|
+
for (let e of s) if (Array.isArray(n[e])) for (let t of n[e]) t && typeof t == "object" && delete t.explanation;
|
|
75
|
+
switch (t) {
|
|
76
|
+
case "fill-in-blanks":
|
|
77
|
+
n.blanks = Object.fromEntries(Object.keys(n.blanks ?? {}).map((e) => [e, []]));
|
|
78
|
+
break;
|
|
79
|
+
case "fill-in-select":
|
|
80
|
+
for (let e of Object.values(n.blanks ?? {})) {
|
|
81
|
+
delete e.correctId;
|
|
82
|
+
for (let t of e.options ?? []) delete t.explanation;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case "categorize":
|
|
86
|
+
for (let e of n.items ?? []) delete e.correctCategoryId;
|
|
87
|
+
break;
|
|
88
|
+
case "timeline":
|
|
89
|
+
for (let e of n.events ?? []) delete e.date, delete e.precision;
|
|
90
|
+
break;
|
|
91
|
+
case "diagram-label":
|
|
92
|
+
for (let e of n.zones ?? []) delete e.correctLabelId;
|
|
93
|
+
n.requireTyping && (n.labels = []);
|
|
94
|
+
break;
|
|
95
|
+
case "pin-on-image":
|
|
96
|
+
case "pin-on-model": n.hotspots = [];
|
|
97
|
+
}
|
|
98
|
+
return n.answerHidden = !0, n;
|
|
99
|
+
}
|
|
100
|
+
function l(e, n) {
|
|
101
|
+
let r = n === void 0 ? null : t(n), a = e.items.map((e) => r ? i(e, r) : structuredClone(e));
|
|
102
|
+
return {
|
|
103
|
+
set: {
|
|
104
|
+
meta: e.meta,
|
|
105
|
+
items: a
|
|
106
|
+
},
|
|
107
|
+
publicSet: {
|
|
108
|
+
meta: e.meta,
|
|
109
|
+
items: a.map(c)
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { i as displayItem, l as prepareDisplaySet, c as publicItem, t as seededRandom };
|
package/dist/limits.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require("./errors.cjs");var t={messageBytes:65536,messagesPerSecond:30,messageBurst:60,snapshotBytes:1048576,snapshotsPerSecond:2,dataBytes:262144,dataWritesPerSecond:1,helloTimeoutMs:1e4,answerBytes:4096,generatedItemBytes:32768,answersPerSecond:10,answerBurst:20,serviceBytes:65536,serviceResultBytes:262144,servicesPerMinute:30,assetsPerSecond:10,assetBurst:20,assetBytes:2097152,errorReportsPerSecond:5,errorReportBurst:10},n=64;function r(e,t=`$`,i=0,a=new Set){if(e===null||typeof e==`string`||typeof e==`boolean`)return null;if(typeof e==`number`)return Number.isFinite(e)?null:`${t} is not a finite number`;if(typeof e!=`object`)return`${t} is a ${typeof e}`;if(i>=n)return`${t} is nested deeper than ${n} levels`;if(a.has(e))return`${t} contains a circular reference`;if(Array.isArray(e)){a.add(e);for(let n=0;n<e.length;n++){let o=r(e[n],`${t}[${n}]`,i+1,a);if(o)return o}return a.delete(e),null}let o=Object.getPrototypeOf(e);if(o!==Object.prototype&&o!==null)return`${t} is not a plain object`;a.add(e);for(let[n,o]of Object.entries(e)){let e=r(o,`${t}.${n}`,i+1,a);if(e)return e}return a.delete(e),null}function i(e){return new TextEncoder().encode(JSON.stringify(e)).length}function a(t,n,a,o){let s=r(t);if(s)throw new e.ProtocolError(`INVALID_ARGUMENT`,`${o} must be plain JSON: ${s}.`);let c=i(t);if(c>n)throw new e.ProtocolError(a,`${o} is ${c} bytes; the limit is ${n} bytes.`);return c}var o=class{rate;burst;now;tokens;last;constructor(e,t,n=()=>Date.now()){this.rate=e,this.burst=t,this.now=n,this.tokens=t,this.last=n()}tryTake(e=1){let t=this.now();return this.tokens=Math.min(this.burst,this.tokens+(t-this.last)/1e3*this.rate),this.last=t,this.tokens<e?!1:(this.tokens-=e,!0)}};exports.LIMITS=t,exports.RateLimiter=o,exports.assertJsonWithin=a,exports.findNonJson=r,exports.jsonByteSize=i;
|
package/dist/limits.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ProtocolError as e } from "./errors.js";
|
|
2
|
+
//#region src/limits.ts
|
|
3
|
+
var t = {
|
|
4
|
+
messageBytes: 65536,
|
|
5
|
+
messagesPerSecond: 30,
|
|
6
|
+
messageBurst: 60,
|
|
7
|
+
snapshotBytes: 1048576,
|
|
8
|
+
snapshotsPerSecond: 2,
|
|
9
|
+
dataBytes: 262144,
|
|
10
|
+
dataWritesPerSecond: 1,
|
|
11
|
+
helloTimeoutMs: 1e4,
|
|
12
|
+
answerBytes: 4096,
|
|
13
|
+
generatedItemBytes: 32768,
|
|
14
|
+
answersPerSecond: 10,
|
|
15
|
+
answerBurst: 20,
|
|
16
|
+
serviceBytes: 65536,
|
|
17
|
+
serviceResultBytes: 262144,
|
|
18
|
+
servicesPerMinute: 30,
|
|
19
|
+
assetsPerSecond: 10,
|
|
20
|
+
assetBurst: 20,
|
|
21
|
+
assetBytes: 2097152,
|
|
22
|
+
errorReportsPerSecond: 5,
|
|
23
|
+
errorReportBurst: 10
|
|
24
|
+
}, n = 64;
|
|
25
|
+
function r(e, t = "$", i = 0, a = /* @__PURE__ */ new Set()) {
|
|
26
|
+
if (e === null || typeof e == "string" || typeof e == "boolean") return null;
|
|
27
|
+
if (typeof e == "number") return Number.isFinite(e) ? null : `${t} is not a finite number`;
|
|
28
|
+
if (typeof e != "object") return `${t} is a ${typeof e}`;
|
|
29
|
+
if (i >= n) return `${t} is nested deeper than ${n} levels`;
|
|
30
|
+
if (a.has(e)) return `${t} contains a circular reference`;
|
|
31
|
+
if (Array.isArray(e)) {
|
|
32
|
+
a.add(e);
|
|
33
|
+
for (let n = 0; n < e.length; n++) {
|
|
34
|
+
let o = r(e[n], `${t}[${n}]`, i + 1, a);
|
|
35
|
+
if (o) return o;
|
|
36
|
+
}
|
|
37
|
+
return a.delete(e), null;
|
|
38
|
+
}
|
|
39
|
+
let o = Object.getPrototypeOf(e);
|
|
40
|
+
if (o !== Object.prototype && o !== null) return `${t} is not a plain object`;
|
|
41
|
+
a.add(e);
|
|
42
|
+
for (let [n, o] of Object.entries(e)) {
|
|
43
|
+
let e = r(o, `${t}.${n}`, i + 1, a);
|
|
44
|
+
if (e) return e;
|
|
45
|
+
}
|
|
46
|
+
return a.delete(e), null;
|
|
47
|
+
}
|
|
48
|
+
function i(e) {
|
|
49
|
+
return new TextEncoder().encode(JSON.stringify(e)).length;
|
|
50
|
+
}
|
|
51
|
+
function a(t, n, a, o) {
|
|
52
|
+
let s = r(t);
|
|
53
|
+
if (s) throw new e("INVALID_ARGUMENT", `${o} must be plain JSON: ${s}.`);
|
|
54
|
+
let c = i(t);
|
|
55
|
+
if (c > n) throw new e(a, `${o} is ${c} bytes; the limit is ${n} bytes.`);
|
|
56
|
+
return c;
|
|
57
|
+
}
|
|
58
|
+
var o = class {
|
|
59
|
+
rate;
|
|
60
|
+
burst;
|
|
61
|
+
now;
|
|
62
|
+
tokens;
|
|
63
|
+
last;
|
|
64
|
+
constructor(e, t, n = () => Date.now()) {
|
|
65
|
+
this.rate = e, this.burst = t, this.now = n, this.tokens = t, this.last = n();
|
|
66
|
+
}
|
|
67
|
+
tryTake(e = 1) {
|
|
68
|
+
let t = this.now();
|
|
69
|
+
return this.tokens = Math.min(this.burst, this.tokens + (t - this.last) / 1e3 * this.rate), this.last = t, this.tokens < e ? !1 : (this.tokens -= e, !0);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
export { t as LIMITS, o as RateLimiter, a as assertJsonWithin, r as findNonJson, i as jsonByteSize };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./settingsSchema.cjs"),t=require("./pluginRuntime.cjs");let n=require("zod"),r=require("@memizy/oqse");var i=n.z.string().regex(/^\d+\.\d+$/,`Protocol version must be in MAJOR.MINOR format (e.g. "1.0")`),a=n.z.enum([`presenter`,`player`]),o=n.z.looseObject({players:n.z.looseObject({min:n.z.number().int().min(1,`players.min must be at least 1`),max:n.z.number().int().max(200,`players.max must be at most 200`),recommended:n.z.number().int().positive().optional()}).refine(e=>e.min<=e.max,{message:`players.min must not be greater than players.max`,path:[`min`]}).refine(e=>e.recommended===void 0||e.recommended>=e.min&&e.recommended<=e.max,{message:`players.recommended must be between min and max`,path:[`recommended`]}),hostAs:n.z.array(a).min(1,`hostAs must contain "presenter" and/or "player"`).refine(e=>new Set(e).size===e.length,`hostAs must not contain duplicates`),lateJoin:n.z.boolean().optional()}),s=n.z.string().regex(/^(https|wss):\/\/[a-z0-9.-]+(:\d{1,5})?$/i,`A network origin is "https://host" or "wss://host" (no path)`),c=n.z.string().max(100).regex(/^[a-z][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$/,`A service name is lowercase segments joined by dots (e.g. "chess.puzzles")`),l=n.z.looseObject({network:n.z.array(s).max(20).optional(),devices:n.z.array(n.z.enum([`camera`,`microphone`,`geolocation`,`serial`,`bluetooth`])).max(5).optional()}),u=n.z.looseObject({protocol:i,set:n.z.enum([`required`,`optional`,`none`]).optional(),modes:n.z.looseObject({solo:n.z.looseObject({}).optional(),multiplayer:o.optional()}),settings:n.z.array(e.SettingDefinitionSchema).optional(),settingsScreen:n.z.looseObject({size:n.z.enum([`compact`,`large`])}).optional(),display:n.z.looseObject({orientation:n.z.enum([`any`,`portrait`,`landscape`]).optional()}).optional(),permissions:l.optional(),services:n.z.array(c).max(20).optional()}).refine(e=>e.modes.solo!==void 0||e.modes.multiplayer!==void 0,{message:`modes must declare "solo" and/or "multiplayer"`,path:[`modes`]}).refine(e=>e.settingsScreen===void 0||e.modes.multiplayer!==void 0,{message:`settingsScreen is only used in the multiplayer lobby; declare modes.multiplayer or remove it`,path:[`settingsScreen`]}).refine(e=>{let t=(e.settings??[]).map(e=>e.id);return new Set(t).size===t.length},{message:`Setting ids must be unique`,path:[`settings`]});function d(e){let n=[],i=r.OQSEManifestSchema.safeParse(e);i.success||n.push(...(0,r.formatOQSEErrors)(i.error));let a=e?.appSpecific?.memizy;if(a===void 0)n.push(`appSpecific.memizy: Missing the Memizy runtime block (protocol, modes, …)`);else{let e=u.safeParse(a);e.success||n.push(...(0,r.formatOQSEErrors)(e.error).map(e=>`appSpecific.memizy.${e}`))}if(n.length>0)return{success:!1,errors:n};let o=(0,r.withManifestDefaults)(e),s=t.toRuntime(o.appSpecific.memizy),c=o.capabilities.types;return s.set!==`none`&&(!Array.isArray(c)||c.length===0)?{success:!1,errors:[`capabilities.types: Declare the item types the game shows, or "set": "none" in appSpecific.memizy for a game without questions.`]}:{success:!0,manifest:o,runtime:s}}var f=class extends Error{errors;constructor(e){super(`Invalid plugin manifest:\n${e.join(`
|
|
2
|
+
`)}`),this.name=`PluginManifestError`,this.errors=e}};function p(e){let t=d(e);if(!t.success)throw new f(t.errors);return{manifest:t.manifest,runtime:t.runtime}}function m(e){let n=t.extractManifestFromHtml(e);return n.success?d(n.data):{success:!1,errors:[n.error]}}exports.HostAsSchema=a,exports.MemizyRuntimeSchema=u,exports.MultiplayerModeSchema=o,exports.PluginManifestError=f,exports.PluginPermissionsSchema=l,exports.ServiceNameSchema=c,exports.parsePluginManifest=p,exports.readPluginManifestFromHtml=m,exports.safeParsePluginManifest=d;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { SettingDefinitionSchema as e } from "./settingsSchema.js";
|
|
2
|
+
import { extractManifestFromHtml as t, toRuntime as n } from "./pluginRuntime.js";
|
|
3
|
+
import { z as r } from "zod";
|
|
4
|
+
import { OQSEManifestSchema as i, formatOQSEErrors as a, withManifestDefaults as o } from "@memizy/oqse";
|
|
5
|
+
//#region src/manifestSchema.ts
|
|
6
|
+
var s = r.string().regex(/^\d+\.\d+$/, "Protocol version must be in MAJOR.MINOR format (e.g. \"1.0\")"), c = r.enum(["presenter", "player"]), l = r.looseObject({
|
|
7
|
+
players: r.looseObject({
|
|
8
|
+
min: r.number().int().min(1, "players.min must be at least 1"),
|
|
9
|
+
max: r.number().int().max(200, "players.max must be at most 200"),
|
|
10
|
+
recommended: r.number().int().positive().optional()
|
|
11
|
+
}).refine((e) => e.min <= e.max, {
|
|
12
|
+
message: "players.min must not be greater than players.max",
|
|
13
|
+
path: ["min"]
|
|
14
|
+
}).refine((e) => e.recommended === void 0 || e.recommended >= e.min && e.recommended <= e.max, {
|
|
15
|
+
message: "players.recommended must be between min and max",
|
|
16
|
+
path: ["recommended"]
|
|
17
|
+
}),
|
|
18
|
+
hostAs: r.array(c).min(1, "hostAs must contain \"presenter\" and/or \"player\"").refine((e) => new Set(e).size === e.length, "hostAs must not contain duplicates"),
|
|
19
|
+
lateJoin: r.boolean().optional()
|
|
20
|
+
}), u = r.string().regex(/^(https|wss):\/\/[a-z0-9.-]+(:\d{1,5})?$/i, "A network origin is \"https://host\" or \"wss://host\" (no path)"), d = r.string().max(100).regex(/^[a-z][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$/, "A service name is lowercase segments joined by dots (e.g. \"chess.puzzles\")"), f = r.looseObject({
|
|
21
|
+
network: r.array(u).max(20).optional(),
|
|
22
|
+
devices: r.array(r.enum([
|
|
23
|
+
"camera",
|
|
24
|
+
"microphone",
|
|
25
|
+
"geolocation",
|
|
26
|
+
"serial",
|
|
27
|
+
"bluetooth"
|
|
28
|
+
])).max(5).optional()
|
|
29
|
+
}), p = r.looseObject({
|
|
30
|
+
protocol: s,
|
|
31
|
+
set: r.enum([
|
|
32
|
+
"required",
|
|
33
|
+
"optional",
|
|
34
|
+
"none"
|
|
35
|
+
]).optional(),
|
|
36
|
+
modes: r.looseObject({
|
|
37
|
+
solo: r.looseObject({}).optional(),
|
|
38
|
+
multiplayer: l.optional()
|
|
39
|
+
}),
|
|
40
|
+
settings: r.array(e).optional(),
|
|
41
|
+
settingsScreen: r.looseObject({ size: r.enum(["compact", "large"]) }).optional(),
|
|
42
|
+
display: r.looseObject({ orientation: r.enum([
|
|
43
|
+
"any",
|
|
44
|
+
"portrait",
|
|
45
|
+
"landscape"
|
|
46
|
+
]).optional() }).optional(),
|
|
47
|
+
permissions: f.optional(),
|
|
48
|
+
services: r.array(d).max(20).optional()
|
|
49
|
+
}).refine((e) => e.modes.solo !== void 0 || e.modes.multiplayer !== void 0, {
|
|
50
|
+
message: "modes must declare \"solo\" and/or \"multiplayer\"",
|
|
51
|
+
path: ["modes"]
|
|
52
|
+
}).refine((e) => e.settingsScreen === void 0 || e.modes.multiplayer !== void 0, {
|
|
53
|
+
message: "settingsScreen is only used in the multiplayer lobby; declare modes.multiplayer or remove it",
|
|
54
|
+
path: ["settingsScreen"]
|
|
55
|
+
}).refine((e) => {
|
|
56
|
+
let t = (e.settings ?? []).map((e) => e.id);
|
|
57
|
+
return new Set(t).size === t.length;
|
|
58
|
+
}, {
|
|
59
|
+
message: "Setting ids must be unique",
|
|
60
|
+
path: ["settings"]
|
|
61
|
+
});
|
|
62
|
+
function m(e) {
|
|
63
|
+
let t = [], r = i.safeParse(e);
|
|
64
|
+
r.success || t.push(...a(r.error));
|
|
65
|
+
let s = e?.appSpecific?.memizy;
|
|
66
|
+
if (s === void 0) t.push("appSpecific.memizy: Missing the Memizy runtime block (protocol, modes, …)");
|
|
67
|
+
else {
|
|
68
|
+
let e = p.safeParse(s);
|
|
69
|
+
e.success || t.push(...a(e.error).map((e) => `appSpecific.memizy.${e}`));
|
|
70
|
+
}
|
|
71
|
+
if (t.length > 0) return {
|
|
72
|
+
success: !1,
|
|
73
|
+
errors: t
|
|
74
|
+
};
|
|
75
|
+
let c = o(e), l = n(c.appSpecific.memizy), u = c.capabilities.types;
|
|
76
|
+
return l.set !== "none" && (!Array.isArray(u) || u.length === 0) ? {
|
|
77
|
+
success: !1,
|
|
78
|
+
errors: ["capabilities.types: Declare the item types the game shows, or \"set\": \"none\" in appSpecific.memizy for a game without questions."]
|
|
79
|
+
} : {
|
|
80
|
+
success: !0,
|
|
81
|
+
manifest: c,
|
|
82
|
+
runtime: l
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
var h = class extends Error {
|
|
86
|
+
errors;
|
|
87
|
+
constructor(e) {
|
|
88
|
+
super(`Invalid plugin manifest:\n${e.join("\n")}`), this.name = "PluginManifestError", this.errors = e;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
function g(e) {
|
|
92
|
+
let t = m(e);
|
|
93
|
+
if (!t.success) throw new h(t.errors);
|
|
94
|
+
return {
|
|
95
|
+
manifest: t.manifest,
|
|
96
|
+
runtime: t.runtime
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function _(e) {
|
|
100
|
+
let n = t(e);
|
|
101
|
+
return n.success ? m(n.data) : {
|
|
102
|
+
success: !1,
|
|
103
|
+
errors: [n.error]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { c as HostAsSchema, p as MemizyRuntimeSchema, l as MultiplayerModeSchema, h as PluginManifestError, f as PluginPermissionsSchema, d as ServiceNameSchema, g as parsePluginManifest, _ as readPluginManifestFromHtml, m as safeParsePluginManifest };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=/<script\b[^>]*\btype\s*=\s*["']application\/oqse-manifest\+json["'][^>]*>([\s\S]*?)<\/script\s*>/i;function t(t){let n=e.exec(t);if(!n)return{success:!1,error:`No <script type="application/oqse-manifest+json"> data island found.`};try{return{success:!0,data:JSON.parse(n[1])}}catch(e){return{success:!1,error:`The manifest data island is not valid JSON: ${e.message}`}}}function n(e){let t=e.modes.multiplayer,n=t?{players:{min:t.players.min,max:t.players.max,...t.players.recommended!==void 0&&{recommended:t.players.recommended}},hostAs:[...t.hostAs],lateJoin:t.lateJoin??!0}:null,r=[];return e.modes.solo&&r.push(`solo`),n?.hostAs.includes(`presenter`)&&r.push(`board`),n&&r.push(`controller`),n&&e.settingsScreen&&r.push(`settings`),{protocol:e.protocol,set:e.set??`required`,solo:e.modes.solo!==void 0,multiplayer:n,settings:e.settings??[],settingsScreen:e.settingsScreen?{size:e.settingsScreen.size}:null,orientation:e.display?.orientation??`any`,views:r,permissions:{network:[...new Set(e.permissions?.network??[])],devices:[...new Set(e.permissions?.devices??[])]},services:[...new Set(e.services??[])]}}exports.extractManifestFromHtml=t,exports.toRuntime=n;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/pluginRuntime.ts
|
|
2
|
+
var e = /<script\b[^>]*\btype\s*=\s*["']application\/oqse-manifest\+json["'][^>]*>([\s\S]*?)<\/script\s*>/i;
|
|
3
|
+
function t(t) {
|
|
4
|
+
let n = e.exec(t);
|
|
5
|
+
if (!n) return {
|
|
6
|
+
success: !1,
|
|
7
|
+
error: "No <script type=\"application/oqse-manifest+json\"> data island found."
|
|
8
|
+
};
|
|
9
|
+
try {
|
|
10
|
+
return {
|
|
11
|
+
success: !0,
|
|
12
|
+
data: JSON.parse(n[1])
|
|
13
|
+
};
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return {
|
|
16
|
+
success: !1,
|
|
17
|
+
error: `The manifest data island is not valid JSON: ${e.message}`
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function n(e) {
|
|
22
|
+
let t = e.modes.multiplayer, n = t ? {
|
|
23
|
+
players: {
|
|
24
|
+
min: t.players.min,
|
|
25
|
+
max: t.players.max,
|
|
26
|
+
...t.players.recommended !== void 0 && { recommended: t.players.recommended }
|
|
27
|
+
},
|
|
28
|
+
hostAs: [...t.hostAs],
|
|
29
|
+
lateJoin: t.lateJoin ?? !0
|
|
30
|
+
} : null, r = [];
|
|
31
|
+
return e.modes.solo && r.push("solo"), n?.hostAs.includes("presenter") && r.push("board"), n && r.push("controller"), n && e.settingsScreen && r.push("settings"), {
|
|
32
|
+
protocol: e.protocol,
|
|
33
|
+
set: e.set ?? "required",
|
|
34
|
+
solo: e.modes.solo !== void 0,
|
|
35
|
+
multiplayer: n,
|
|
36
|
+
settings: e.settings ?? [],
|
|
37
|
+
settingsScreen: e.settingsScreen ? { size: e.settingsScreen.size } : null,
|
|
38
|
+
orientation: e.display?.orientation ?? "any",
|
|
39
|
+
views: r,
|
|
40
|
+
permissions: {
|
|
41
|
+
network: [...new Set(e.permissions?.network ?? [])],
|
|
42
|
+
devices: [...new Set(e.permissions?.devices ?? [])]
|
|
43
|
+
},
|
|
44
|
+
services: [...new Set(e.services ?? [])]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { t as extractManifestFromHtml, n as toRuntime };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(){typeof Promise.withResolvers!=`function`&&Object.defineProperty(Promise,"withResolvers",{configurable:!0,writable:!0,value:function(){let e,t;return{promise:new this((n,r)=>{e=n,t=r}),resolve:e,reject:t}}}),typeof Object.hasOwn!=`function`&&Object.defineProperty(Object,"hasOwn",{configurable:!0,writable:!0,value:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})}exports.ensureConnectionPolyfills=e;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/polyfills.ts
|
|
2
|
+
function e() {
|
|
3
|
+
typeof Promise.withResolvers != "function" && Object.defineProperty(Promise, "withResolvers", {
|
|
4
|
+
configurable: !0,
|
|
5
|
+
writable: !0,
|
|
6
|
+
value: function() {
|
|
7
|
+
let e, t;
|
|
8
|
+
return {
|
|
9
|
+
promise: new this((n, r) => {
|
|
10
|
+
e = n, t = r;
|
|
11
|
+
}),
|
|
12
|
+
resolve: e,
|
|
13
|
+
reject: t
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}), typeof Object.hasOwn != "function" && Object.defineProperty(Object, "hasOwn", {
|
|
17
|
+
configurable: !0,
|
|
18
|
+
writable: !0,
|
|
19
|
+
value: (e, t) => Object.prototype.hasOwnProperty.call(e, t)
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { e as ensureConnectionPolyfills };
|
package/dist/relay.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=1,t={frameBytes:3145728,bundleBytes:8388608,playersPerRoom:100,playerFramesPerSecond:60,playerFrameBurst:120,hostFramesPerSecond:3e3,hostFrameBurst:6e3,nameLength:32,authLength:4096,hostAwayMs:6e5,roomTtlMs:216e5};function n(e){return typeof e==`string`?e.replace(/\s+/g,` `).trim().slice(0,t.nameLength):``}function r(e){return typeof e==`string`&&/^\d{6}$/.test(e)}exports.RELAY_LIMITS=t,exports.RELAY_PROTOCOL=e,exports.isPin=r,exports.normalizePlayerName=n;
|
package/dist/relay.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/relay.ts
|
|
2
|
+
var e = 1, t = {
|
|
3
|
+
frameBytes: 3145728,
|
|
4
|
+
bundleBytes: 8388608,
|
|
5
|
+
playersPerRoom: 100,
|
|
6
|
+
playerFramesPerSecond: 60,
|
|
7
|
+
playerFrameBurst: 120,
|
|
8
|
+
hostFramesPerSecond: 3e3,
|
|
9
|
+
hostFrameBurst: 6e3,
|
|
10
|
+
nameLength: 32,
|
|
11
|
+
authLength: 4096,
|
|
12
|
+
hostAwayMs: 6e5,
|
|
13
|
+
roomTtlMs: 216e5
|
|
14
|
+
};
|
|
15
|
+
function n(e) {
|
|
16
|
+
return typeof e == "string" ? e.replace(/\s+/g, " ").trim().slice(0, t.nameLength) : "";
|
|
17
|
+
}
|
|
18
|
+
function r(e) {
|
|
19
|
+
return typeof e == "string" && /^\d{6}$/.test(e);
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { t as RELAY_LIMITS, e as RELAY_PROTOCOL, r as isPin, n as normalizePlayerName };
|
package/dist/schemas.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require("./errors.cjs");let t=require("zod"),n=require("@memizy/oqse");var r=e=>t.z.string().min(1).max(e),i=t.z.string().min(1).max(128),a=t.z.looseObject({protocol:t.z.string().regex(/^\d+\.\d+$/,`protocol must be in MAJOR.MINOR format`),sdk:t.z.looseObject({name:r(200),version:r(100)}),plugin:t.z.looseObject({id:r(500),version:r(100)}),features:t.z.array(r(100)).max(100)}),o=t.z.looseObject({to:t.z.union([t.z.literal(`authority`),t.z.literal(`all`),t.z.array(i).min(1).max(500)]),data:t.z.unknown()}),s=t.z.looseObject({playerId:i.optional(),itemId:r(128),isCorrect:t.z.boolean(),confidence:t.z.union([t.z.literal(1),t.z.literal(2),t.z.literal(3),t.z.literal(4)]).optional(),timeSpentMs:t.z.number().int().nonnegative().optional(),hintsUsed:t.z.number().int().nonnegative().optional(),isSkipped:t.z.boolean().optional(),answer:t.z.unknown().optional(),item:t.z.looseObject({id:r(128),type:r(64)}).optional()}).refine(e=>!e.item||e.item.id===e.itemId,{message:`item.id must equal itemId`,path:[`item`,`id`]}),c=t.z.enum([`plugin`,`set`]),l=t.z.looseObject({values:t.z.record(t.z.string(),t.z.unknown()),valid:t.z.boolean(),message:t.z.string().max(500).optional()}),u=t.z.looseObject({scores:t.z.record(i,t.z.number().finite()).optional(),summary:t.z.string().max(1e3).optional()}),d=t.z.looseObject({code:r(100),message:t.z.string().max(2e3),context:t.z.record(t.z.string(),t.z.unknown()).optional()}),f={hello:t.z.tuple([a]),ready:t.z.tuple([]),send:t.z.tuple([o]),saveSnapshot:t.z.tuple([t.z.unknown()]),recordAnswer:t.z.tuple([s]),saveData:t.z.tuple([c,t.z.unknown()]),updateSettings:t.z.tuple([l]),getAsset:t.z.tuple([r(200),r(128).optional()]),end:t.z.tuple([u]),reportError:t.z.tuple([d]),service:t.z.tuple([r(100),t.z.unknown()])};function p(t,r){let i=f[t].safeParse(r);if(!i.success)throw new e.ProtocolError(`INVALID_ARGUMENT`,`${t}: ${(0,n.formatOQSEErrors)(i.error).join(`; `)}`);return i.data}exports.AddressSchema=i,exports.AnswerRecordSchema=s,exports.DataScopeSchema=c,exports.ErrorReportSchema=d,exports.HOST_API_ARGS=f,exports.HandshakeSchema=a,exports.OutgoingMessageSchema=o,exports.SessionResultSchema=u,exports.SettingsUpdateSchema=l,exports.parseHostCall=p;
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ProtocolError as e } from "./errors.js";
|
|
2
|
+
import { z as t } from "zod";
|
|
3
|
+
import { formatOQSEErrors as n } from "@memizy/oqse";
|
|
4
|
+
//#region src/schemas.ts
|
|
5
|
+
var r = (e) => t.string().min(1).max(e), i = t.string().min(1).max(128), a = t.looseObject({
|
|
6
|
+
protocol: t.string().regex(/^\d+\.\d+$/, "protocol must be in MAJOR.MINOR format"),
|
|
7
|
+
sdk: t.looseObject({
|
|
8
|
+
name: r(200),
|
|
9
|
+
version: r(100)
|
|
10
|
+
}),
|
|
11
|
+
plugin: t.looseObject({
|
|
12
|
+
id: r(500),
|
|
13
|
+
version: r(100)
|
|
14
|
+
}),
|
|
15
|
+
features: t.array(r(100)).max(100)
|
|
16
|
+
}), o = t.looseObject({
|
|
17
|
+
to: t.union([
|
|
18
|
+
t.literal("authority"),
|
|
19
|
+
t.literal("all"),
|
|
20
|
+
t.array(i).min(1).max(500)
|
|
21
|
+
]),
|
|
22
|
+
data: t.unknown()
|
|
23
|
+
}), s = t.looseObject({
|
|
24
|
+
playerId: i.optional(),
|
|
25
|
+
itemId: r(128),
|
|
26
|
+
isCorrect: t.boolean(),
|
|
27
|
+
confidence: t.union([
|
|
28
|
+
t.literal(1),
|
|
29
|
+
t.literal(2),
|
|
30
|
+
t.literal(3),
|
|
31
|
+
t.literal(4)
|
|
32
|
+
]).optional(),
|
|
33
|
+
timeSpentMs: t.number().int().nonnegative().optional(),
|
|
34
|
+
hintsUsed: t.number().int().nonnegative().optional(),
|
|
35
|
+
isSkipped: t.boolean().optional(),
|
|
36
|
+
answer: t.unknown().optional(),
|
|
37
|
+
item: t.looseObject({
|
|
38
|
+
id: r(128),
|
|
39
|
+
type: r(64)
|
|
40
|
+
}).optional()
|
|
41
|
+
}).refine((e) => !e.item || e.item.id === e.itemId, {
|
|
42
|
+
message: "item.id must equal itemId",
|
|
43
|
+
path: ["item", "id"]
|
|
44
|
+
}), c = t.enum(["plugin", "set"]), l = t.looseObject({
|
|
45
|
+
values: t.record(t.string(), t.unknown()),
|
|
46
|
+
valid: t.boolean(),
|
|
47
|
+
message: t.string().max(500).optional()
|
|
48
|
+
}), u = t.looseObject({
|
|
49
|
+
scores: t.record(i, t.number().finite()).optional(),
|
|
50
|
+
summary: t.string().max(1e3).optional()
|
|
51
|
+
}), d = t.looseObject({
|
|
52
|
+
code: r(100),
|
|
53
|
+
message: t.string().max(2e3),
|
|
54
|
+
context: t.record(t.string(), t.unknown()).optional()
|
|
55
|
+
}), f = {
|
|
56
|
+
hello: t.tuple([a]),
|
|
57
|
+
ready: t.tuple([]),
|
|
58
|
+
send: t.tuple([o]),
|
|
59
|
+
saveSnapshot: t.tuple([t.unknown()]),
|
|
60
|
+
recordAnswer: t.tuple([s]),
|
|
61
|
+
saveData: t.tuple([c, t.unknown()]),
|
|
62
|
+
updateSettings: t.tuple([l]),
|
|
63
|
+
getAsset: t.tuple([r(200), r(128).optional()]),
|
|
64
|
+
end: t.tuple([u]),
|
|
65
|
+
reportError: t.tuple([d]),
|
|
66
|
+
service: t.tuple([r(100), t.unknown()])
|
|
67
|
+
};
|
|
68
|
+
function p(t, r) {
|
|
69
|
+
let i = f[t].safeParse(r);
|
|
70
|
+
if (!i.success) throw new e("INVALID_ARGUMENT", `${t}: ${n(i.error).join("; ")}`);
|
|
71
|
+
return i.data;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { i as AddressSchema, s as AnswerRecordSchema, c as DataScopeSchema, d as ErrorReportSchema, f as HOST_API_ARGS, a as HandshakeSchema, o as OutgoingMessageSchema, u as SessionResultSchema, l as SettingsUpdateSchema, p as parseHostCall };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=[`https://cdn.jsdelivr.net`,`https://unpkg.com`,`https://cdnjs.cloudflare.com`,`https://esm.sh`],t=[`https://fonts.googleapis.com`,`https://fonts.gstatic.com`],n=[`camera`,`microphone`,`geolocation`,`serial`,`bluetooth`];function r(n,r){let i=r&&/^https?:\/\//.test(r)?[r]:[],a=n?.permissions.network??[],o=(...e)=>[...new Set(e.flat())].join(` `);return[`default-src 'none'`,`script-src 'unsafe-inline' 'wasm-unsafe-eval' blob: ${o(e,i)}`,`style-src 'unsafe-inline' ${o(e,t,i)}`,`font-src data: ${o(e,t,i)}`,`img-src data: blob: https:`,`media-src data: blob: https:`,`connect-src data: blob: ${o(e,i,a)}`,`worker-src blob: ${o(e,i)}`,`frame-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `)}function i(e){return(e?.permissions.devices??[]).map(e=>`${e} *`).join(`; `)}function a(e,t){let n=`<meta http-equiv="Content-Security-Policy" content="${t.replace(/"/g,`"`)}">`,r=/<head\b[^>]*>/i.exec(e);if(r)return e.slice(0,r.index+r[0].length)+n+e.slice(r.index+r[0].length);let i=/^\s*<!doctype[^>]*>/i.exec(e);return i?e.slice(0,i[0].length)+`<head>${n}</head>`+e.slice(i[0].length):`<head>${n}</head>${e}`}exports.LIBRARY_ORIGINS=e,exports.PLUGIN_DEVICES=n,exports.injectContentPolicy=a,exports.pluginAllowAttribute=i,exports.pluginContentPolicy=r;
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/security.ts
|
|
2
|
+
var e = [
|
|
3
|
+
"https://cdn.jsdelivr.net",
|
|
4
|
+
"https://unpkg.com",
|
|
5
|
+
"https://cdnjs.cloudflare.com",
|
|
6
|
+
"https://esm.sh"
|
|
7
|
+
], t = ["https://fonts.googleapis.com", "https://fonts.gstatic.com"], n = [
|
|
8
|
+
"camera",
|
|
9
|
+
"microphone",
|
|
10
|
+
"geolocation",
|
|
11
|
+
"serial",
|
|
12
|
+
"bluetooth"
|
|
13
|
+
];
|
|
14
|
+
function r(n, r) {
|
|
15
|
+
let i = r && /^https?:\/\//.test(r) ? [r] : [], a = n?.permissions.network ?? [], o = (...e) => [...new Set(e.flat())].join(" ");
|
|
16
|
+
return [
|
|
17
|
+
"default-src 'none'",
|
|
18
|
+
`script-src 'unsafe-inline' 'wasm-unsafe-eval' blob: ${o(e, i)}`,
|
|
19
|
+
`style-src 'unsafe-inline' ${o(e, t, i)}`,
|
|
20
|
+
`font-src data: ${o(e, t, i)}`,
|
|
21
|
+
"img-src data: blob: https:",
|
|
22
|
+
"media-src data: blob: https:",
|
|
23
|
+
`connect-src data: blob: ${o(e, i, a)}`,
|
|
24
|
+
`worker-src blob: ${o(e, i)}`,
|
|
25
|
+
"frame-src 'none'",
|
|
26
|
+
"form-action 'none'",
|
|
27
|
+
"base-uri 'none'"
|
|
28
|
+
].join("; ");
|
|
29
|
+
}
|
|
30
|
+
function i(e) {
|
|
31
|
+
return (e?.permissions.devices ?? []).map((e) => `${e} *`).join("; ");
|
|
32
|
+
}
|
|
33
|
+
function a(e, t) {
|
|
34
|
+
let n = `<meta http-equiv="Content-Security-Policy" content="${t.replace(/"/g, """)}">`, r = /<head\b[^>]*>/i.exec(e);
|
|
35
|
+
if (r) return e.slice(0, r.index + r[0].length) + n + e.slice(r.index + r[0].length);
|
|
36
|
+
let i = /^\s*<!doctype[^>]*>/i.exec(e);
|
|
37
|
+
return i ? e.slice(0, i[0].length) + `<head>${n}</head>` + e.slice(i[0].length) : `<head>${n}</head>${e}`;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { e as LIBRARY_ORIGINS, n as PLUGIN_DEVICES, a as injectContentPolicy, i as pluginAllowAttribute, r as pluginContentPolicy };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t){switch(e.type){case`number`:return typeof t!=`number`||!Number.isFinite(t)?`must be a number`:e.min!==void 0&&t<e.min?`must be at least ${e.min}`:e.max!==void 0&&t>e.max?`must be at most ${e.max}`:null;case`boolean`:return typeof t==`boolean`?null:`must be true or false`;case`select`:return e.options.some(e=>e.value===t)?null:`must be one of the options`;case`text`:return typeof t==`string`?e.maxLength!==void 0&&t.length>e.maxLength?`must be at most ${e.maxLength} characters`:null:`must be text`}}function t(t,n={}){let r={},i=[];for(let a of t){if(!(a.id in n)){r[a.id]=a.default;continue}let t=e(a,n[a.id]);t?(i.push(`${a.id}: ${t}`),r[a.id]=a.default):r[a.id]=n[a.id]}for(let e of Object.keys(n))t.some(t=>t.id===e)||i.push(`${e}: unknown setting`);return{values:r,errors:i}}function n(e,t){return e.filter(e=>!e.modes||e.modes.includes(t))}function r(e,t){if(typeof e==`string`)return e;let n=t.split(`-`)[0];return e[t]??e[n]??e.en??Object.values(e)[0]}exports.localize=r,exports.resolveSettings=t,exports.settingsForMode=n,exports.validateSettingValue=e;
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/settings.ts
|
|
2
|
+
function e(e, t) {
|
|
3
|
+
switch (e.type) {
|
|
4
|
+
case "number": return typeof t != "number" || !Number.isFinite(t) ? "must be a number" : e.min !== void 0 && t < e.min ? `must be at least ${e.min}` : e.max !== void 0 && t > e.max ? `must be at most ${e.max}` : null;
|
|
5
|
+
case "boolean": return typeof t == "boolean" ? null : "must be true or false";
|
|
6
|
+
case "select": return e.options.some((e) => e.value === t) ? null : "must be one of the options";
|
|
7
|
+
case "text": return typeof t == "string" ? e.maxLength !== void 0 && t.length > e.maxLength ? `must be at most ${e.maxLength} characters` : null : "must be text";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function t(t, n = {}) {
|
|
11
|
+
let r = {}, i = [];
|
|
12
|
+
for (let a of t) {
|
|
13
|
+
if (!(a.id in n)) {
|
|
14
|
+
r[a.id] = a.default;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
let t = e(a, n[a.id]);
|
|
18
|
+
t ? (i.push(`${a.id}: ${t}`), r[a.id] = a.default) : r[a.id] = n[a.id];
|
|
19
|
+
}
|
|
20
|
+
for (let e of Object.keys(n)) t.some((t) => t.id === e) || i.push(`${e}: unknown setting`);
|
|
21
|
+
return {
|
|
22
|
+
values: r,
|
|
23
|
+
errors: i
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function n(e, t) {
|
|
27
|
+
return e.filter((e) => !e.modes || e.modes.includes(t));
|
|
28
|
+
}
|
|
29
|
+
function r(e, t) {
|
|
30
|
+
if (typeof e == "string") return e;
|
|
31
|
+
let n = t.split("-")[0];
|
|
32
|
+
return e[t] ?? e[n] ?? e.en ?? Object.values(e)[0];
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { r as localize, t as resolveSettings, n as settingsForMode, e as validateSettingValue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=require("zod");var t=e.z.union([e.z.string().min(1,`Text must not be empty`),e.z.record(e.z.string().min(2),e.z.string().min(1)).refine(e=>Object.keys(e).length>0,`Localized text needs at least one language`)]),n={id:e.z.string().regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/,`Setting id must start with a letter and contain only letters, digits, "_" and "-"`),label:t,description:t.optional(),modes:e.z.array(e.z.enum([`solo`,`multiplayer`])).min(1,`modes needs at least one mode`).optional()},r=e.z.looseObject({...n,type:e.z.literal(`number`),default:e.z.number().finite(),min:e.z.number().finite().optional(),max:e.z.number().finite().optional(),step:e.z.number().positive().optional()}).refine(e=>e.min===void 0||e.max===void 0||e.min<=e.max,{message:`min must not be greater than max`,path:[`min`]}).refine(e=>(e.min===void 0||e.default>=e.min)&&(e.max===void 0||e.default<=e.max),{message:`default is outside min/max`,path:[`default`]}),i=e.z.looseObject({...n,type:e.z.literal(`boolean`),default:e.z.boolean()}),a=e.z.union([e.z.string(),e.z.number().finite(),e.z.boolean()]),o=e.z.looseObject({...n,type:e.z.literal(`select`),options:e.z.array(e.z.looseObject({value:a,label:t})).min(1,`A select setting needs at least one option`),default:a}).refine(e=>new Set(e.options.map(e=>e.value)).size===e.options.length,{message:`Option values must be unique`,path:[`options`]}).refine(e=>e.options.some(t=>t.value===e.default),{message:`default must be one of the option values`,path:[`default`]}),s=e.z.looseObject({...n,type:e.z.literal(`text`),default:e.z.string(),maxLength:e.z.number().int().positive().optional()}).refine(e=>e.maxLength===void 0||e.default.length<=e.maxLength,{message:`default is longer than maxLength`,path:[`default`]}),c=e.z.discriminatedUnion(`type`,[r,i,o,s]);exports.LocalizedTextSchema=t,exports.SettingDefinitionSchema=c;
|