@arcadible/sdk 1.5.0-next.2 → 1.5.0-next.4
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/dist/context/client.js +2 -2
- package/dist/host.d.ts +2 -2
- package/dist/host.js +20 -3
- package/dist/lifecycle/holds.js +4 -1
- package/dist/system/client.d.ts +6 -1
- package/dist/system/rules.d.ts +14 -4
- package/dist/system/rules.js +42 -11
- package/dist/system/server.js +5 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/context/client.js
CHANGED
|
@@ -8,8 +8,8 @@ export function createContextClient(bus) {
|
|
|
8
8
|
let context = null;
|
|
9
9
|
let settled = false;
|
|
10
10
|
bus.on(Names.State, isObject, (data) => {
|
|
11
|
-
// fixed for the run: the first one stands
|
|
12
|
-
if (context)
|
|
11
|
+
// fixed for the run: the first one stands, and none comes after `ready`.
|
|
12
|
+
if (context || settled)
|
|
13
13
|
return;
|
|
14
14
|
const next = contextOf(data);
|
|
15
15
|
if (!next) {
|
package/dist/host.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Hello } from "./bus/protocol.js";
|
|
2
2
|
import { type Server } from "./bus/server.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ContextState } from "./context/rules.js";
|
|
4
4
|
import { type Holds } from "./lifecycle/holds.js";
|
|
5
5
|
import { type SystemState } from "./system/rules.js";
|
|
6
6
|
import { type System } from "./system/server.js";
|
package/dist/host.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// The page's side of the bus. The play page uses `createHost`;
|
|
2
2
|
// a game standing alone runs `serve` in its own page.
|
|
3
|
+
// [import]
|
|
4
|
+
import { log } from "./bus/protocol.js";
|
|
3
5
|
import { answerHellos, createServer } from "./bus/server.js";
|
|
4
6
|
import { contextServer } from "./context/server.js";
|
|
7
|
+
import { isTime } from "./context/rules.js";
|
|
5
8
|
import { createHolds } from "./lifecycle/holds.js";
|
|
6
|
-
import {
|
|
9
|
+
import { browserLanguage } from "./system/rules.js";
|
|
7
10
|
import { createSystem } from "./system/server.js";
|
|
8
11
|
import { lifecycleServer } from "./lifecycle/server.js";
|
|
9
12
|
import { savesServer } from "./saves/server.js";
|
|
@@ -31,6 +34,18 @@ export function serve(options) {
|
|
|
31
34
|
});
|
|
32
35
|
}
|
|
33
36
|
// [function]
|
|
37
|
+
// The launch's time: the page's, by Arcadible's clock, or the device's when
|
|
38
|
+
// none is given (as under `arc serve`). One that isn't an ISO time in UTC is
|
|
39
|
+
// a bug in the page, said in the console, and the device's time is used, so
|
|
40
|
+
// the game still gets one.
|
|
41
|
+
function launchedAt(given) {
|
|
42
|
+
if (isTime(given))
|
|
43
|
+
return given;
|
|
44
|
+
if (given !== undefined) {
|
|
45
|
+
log("error", `launchedAt isn't an ISO time in UTC: ${JSON.stringify(given)}; the device's time is used.`);
|
|
46
|
+
}
|
|
47
|
+
return new Date().toISOString();
|
|
48
|
+
}
|
|
34
49
|
// The play page's host: it answers its iframe's hello with a welcome and a
|
|
35
50
|
// private channel. Call it before inserting the iframe, so no hello is
|
|
36
51
|
// missed. It holds the game while the page is hidden; `hold` holds it for
|
|
@@ -40,7 +55,7 @@ export function createHost(options) {
|
|
|
40
55
|
const win = options.window ?? window;
|
|
41
56
|
const holds = createHolds();
|
|
42
57
|
const system = createSystem({
|
|
43
|
-
language: options.language ??
|
|
58
|
+
language: options.language ?? browserLanguage(win.navigator?.language),
|
|
44
59
|
});
|
|
45
60
|
const answering = answerHellos({
|
|
46
61
|
frame: options.frame,
|
|
@@ -50,7 +65,9 @@ export function createHost(options) {
|
|
|
50
65
|
holds,
|
|
51
66
|
system,
|
|
52
67
|
context: {
|
|
53
|
-
|
|
68
|
+
// Arcadible's time when given, else the device's: never a time the
|
|
69
|
+
// game would refuse.
|
|
70
|
+
launch: { at: launchedAt(options.launchedAt) },
|
|
54
71
|
},
|
|
55
72
|
}),
|
|
56
73
|
window: win,
|
package/dist/lifecycle/holds.js
CHANGED
|
@@ -58,7 +58,10 @@ export function createHolds() {
|
|
|
58
58
|
},
|
|
59
59
|
[Names.Paused]: (data) => {
|
|
60
60
|
const value = isObject(data) ? data["n"] : undefined;
|
|
61
|
-
|
|
61
|
+
// a pause the page sent: a whole number, from 1 up to the last.
|
|
62
|
+
if (!Number.isInteger(value) || value < 1)
|
|
63
|
+
return;
|
|
64
|
+
if (value > n)
|
|
62
65
|
return;
|
|
63
66
|
settled = Math.max(settled, value);
|
|
64
67
|
if (settled < n)
|
package/dist/system/client.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { Bus } from "../bus/client.js";
|
|
2
2
|
/** Arcadible's state for the player, which changes while the game runs. */
|
|
3
3
|
export interface System {
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The language Arcadible's store shows this player: a well-formed BCP 47
|
|
6
|
+
* tag in its canonical form ("en", "ja", later perhaps "zh-Hant" or
|
|
7
|
+
* "pt-BR"), ready for `Intl`. Match it against your game's languages by
|
|
8
|
+
* dropping parts from the end ("zh-Hant-TW", "zh-Hant", "zh").
|
|
9
|
+
*/
|
|
5
10
|
readonly language: string;
|
|
6
11
|
/**
|
|
7
12
|
* Called when something changes, with the new state (this same object)
|
package/dist/system/rules.d.ts
CHANGED
|
@@ -4,9 +4,19 @@ export declare const Names: {
|
|
|
4
4
|
export type SystemState = {
|
|
5
5
|
language: string;
|
|
6
6
|
};
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* A language tag (BCP 47, as the web, iOS, and Android use) in its canonical
|
|
9
|
+
* form, `zh-hant-tw` as `zh-Hant-TW`, or null if it isn't a well-formed one
|
|
10
|
+
* (`en_US`, `English`, empty). Only its shape is checked, never whether
|
|
11
|
+
* Arcadible uses it, so a language added later is never refused.
|
|
12
|
+
*/
|
|
13
|
+
export declare function languageOf(tag: unknown): string | null;
|
|
14
|
+
/** Whether a value is a language tag in its canonical form. */
|
|
8
15
|
export declare function isLanguage(value: unknown): value is string;
|
|
9
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* The state as the game keeps it: only fields it knows, each checked, the
|
|
18
|
+
* language in its canonical form.
|
|
19
|
+
*/
|
|
10
20
|
export declare function stateOf(value: unknown): SystemState | null;
|
|
11
|
-
/** A browser's language as
|
|
12
|
-
export declare function
|
|
21
|
+
/** A browser's language (`navigator.language`) as the page sends it, or `en`. */
|
|
22
|
+
export declare function browserLanguage(tag: string | undefined): string;
|
package/dist/system/rules.js
CHANGED
|
@@ -8,23 +8,54 @@ export const Names = {
|
|
|
8
8
|
// every change.
|
|
9
9
|
State: "system.state",
|
|
10
10
|
};
|
|
11
|
-
//
|
|
12
|
-
|
|
11
|
+
// The longest language tag kept: room for a language, script, region, and a
|
|
12
|
+
// variant or two (`zh-Hant-TW`, `sl-rozaj-biske`), not a paragraph.
|
|
13
|
+
const MAX_LANGUAGE = 35;
|
|
14
|
+
// A tag's shape, where the browser can't say (no `Intl.getCanonicalLocales`).
|
|
15
|
+
const SHAPE = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$/u;
|
|
16
|
+
// The language itself, first: an ISO 639 code, two or three letters. BCP 47
|
|
17
|
+
// also allows five to eight (`english` is well-formed), which nothing uses.
|
|
18
|
+
const PRIMARY = /^[A-Za-z]{2,3}(-|$)/u;
|
|
13
19
|
// [function]
|
|
14
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* A language tag (BCP 47, as the web, iOS, and Android use) in its canonical
|
|
22
|
+
* form, `zh-hant-tw` as `zh-Hant-TW`, or null if it isn't a well-formed one
|
|
23
|
+
* (`en_US`, `English`, empty). Only its shape is checked, never whether
|
|
24
|
+
* Arcadible uses it, so a language added later is never refused.
|
|
25
|
+
*/
|
|
26
|
+
export function languageOf(tag) {
|
|
27
|
+
if (typeof tag !== "string" || tag.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
if (tag.length > MAX_LANGUAGE || !PRIMARY.test(tag))
|
|
30
|
+
return null;
|
|
31
|
+
if (typeof Intl === "undefined" || !Intl.getCanonicalLocales) {
|
|
32
|
+
return SHAPE.test(tag) ? tag : null;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return Intl.getCanonicalLocales(tag)[0] ?? null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// [function]
|
|
42
|
+
/** Whether a value is a language tag in its canonical form. */
|
|
15
43
|
export function isLanguage(value) {
|
|
16
|
-
return typeof value === "string" &&
|
|
44
|
+
return typeof value === "string" && languageOf(value) === value;
|
|
17
45
|
}
|
|
18
46
|
// [function]
|
|
19
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The state as the game keeps it: only fields it knows, each checked, the
|
|
49
|
+
* language in its canonical form.
|
|
50
|
+
*/
|
|
20
51
|
export function stateOf(value) {
|
|
21
|
-
if (!isObject(value)
|
|
52
|
+
if (!isObject(value))
|
|
22
53
|
return null;
|
|
23
|
-
|
|
54
|
+
const language = languageOf(value["language"]);
|
|
55
|
+
return language ? { language } : null;
|
|
24
56
|
}
|
|
25
57
|
// [function]
|
|
26
|
-
/** A browser's language as
|
|
27
|
-
export function
|
|
28
|
-
|
|
29
|
-
return isLanguage(short) ? short : "en";
|
|
58
|
+
/** A browser's language (`navigator.language`) as the page sends it, or `en`. */
|
|
59
|
+
export function browserLanguage(tag) {
|
|
60
|
+
return languageOf(tag) ?? "en";
|
|
30
61
|
}
|
package/dist/system/server.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// The page's side of `system`: Arcadible's state for the player, sent as
|
|
2
2
|
// the game connects and again whenever it changes. Nothing waits on it.
|
|
3
|
-
import {
|
|
3
|
+
import { browserLanguage, languageOf, Names, } from "./rules.js";
|
|
4
4
|
// [function]
|
|
5
5
|
export function createSystem(initial) {
|
|
6
|
-
|
|
6
|
+
// only a well-formed tag goes to the game, in its canonical form.
|
|
7
|
+
let state = { ...initial, language: browserLanguage(initial.language) };
|
|
7
8
|
let context = null;
|
|
8
9
|
return {
|
|
9
10
|
set(next) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
: state.language;
|
|
11
|
+
// a tag that isn't a language changes nothing.
|
|
12
|
+
const language = languageOf(next.language) ?? state.language;
|
|
13
13
|
if (language === state.language)
|
|
14
14
|
return;
|
|
15
15
|
state = { ...state, language };
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.5.0-next.
|
|
1
|
+
export declare const VERSION = "1.5.0-next.4";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED