@fabi-play/protocol 0.0.0-stage → 0.2.0
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/LICENSE +76 -0
- package/README.md +75 -2
- package/dist/index.d.ts +741 -0
- package/dist/index.js +605 -0
- package/fabi-play.schema.json +176 -0
- package/package.json +52 -3
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Fabi Play game manifest (fabi-play.json)",
|
|
4
|
+
"description": "What a game declares about itself, served as fabi-play.json at the game's address. Fabi Play checks it with parseManifest (@fabi-play/protocol); this schema describes the same rules for editors. See https://docs.fabiplay.app/sdk/manifest/.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "version", "title", "description", "ageRanges", "movements", "players", "minutes", "entry", "sdk"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "For editors only: the JSON Schema this file follows. Ignored by Fabi Play."
|
|
12
|
+
},
|
|
13
|
+
"id": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The game's id in the catalogue: lowercase letters, digits and single hyphens, up to 60 characters.",
|
|
16
|
+
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
|
|
17
|
+
"maxLength": 60,
|
|
18
|
+
"examples": ["star-catcher"]
|
|
19
|
+
},
|
|
20
|
+
"version": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "The game's version, e.g. 1.0 or 1.0.2.",
|
|
23
|
+
"pattern": "^\\d+\\.\\d+(\\.\\d+)?$",
|
|
24
|
+
"examples": ["1.0"]
|
|
25
|
+
},
|
|
26
|
+
"title": {
|
|
27
|
+
"description": "The game's name in Italian and English, up to 60 characters each.",
|
|
28
|
+
"$ref": "#/definitions/texts60"
|
|
29
|
+
},
|
|
30
|
+
"description": {
|
|
31
|
+
"description": "One or two sentences in Italian and English, up to 300 characters each.",
|
|
32
|
+
"$ref": "#/definitions/texts300"
|
|
33
|
+
},
|
|
34
|
+
"ageRanges": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"description": "The age ranges the game is made for: the catalogue shows it only to players in one of them.",
|
|
37
|
+
"minItems": 1,
|
|
38
|
+
"items": { "enum": ["3-5", "6-8", "9-12"] }
|
|
39
|
+
},
|
|
40
|
+
"movements": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"description": "The movements the game is played with.",
|
|
43
|
+
"minItems": 1,
|
|
44
|
+
"items": {
|
|
45
|
+
"enum": ["hands", "jump", "squat", "clap", "freeze", "lanes", "shapes"]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"players": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"description": "How many children can play together: from 1 to 4, min not above max.",
|
|
51
|
+
"required": ["min", "max"],
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"properties": {
|
|
54
|
+
"min": { "type": "integer", "minimum": 1, "maximum": 4 },
|
|
55
|
+
"max": { "type": "integer", "minimum": 1, "maximum": 4 }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"minutes": {
|
|
59
|
+
"type": "number",
|
|
60
|
+
"description": "Minutes of a typical game, from 1 to 20.",
|
|
61
|
+
"minimum": 1,
|
|
62
|
+
"maximum": 20
|
|
63
|
+
},
|
|
64
|
+
"entry": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "The page that starts the game: a path relative to the game's address (no scheme, no leading /, no \".\" or \"..\" segments, up to 200 characters) of letters, digits, _ and -, ending in lowercase .html.",
|
|
67
|
+
"pattern": "^(?:[A-Za-z0-9_][A-Za-z0-9_-]*(?:\\.[A-Za-z0-9_-]+)*/)*[A-Za-z0-9_][A-Za-z0-9_-]*(?:\\.[A-Za-z0-9_-]+)*\\.(?:html)$",
|
|
68
|
+
"maxLength": 200,
|
|
69
|
+
"examples": ["index.html"]
|
|
70
|
+
},
|
|
71
|
+
"sdk": {
|
|
72
|
+
"const": 1,
|
|
73
|
+
"description": "The protocol version of the SDK the game was built with: 1."
|
|
74
|
+
},
|
|
75
|
+
"backdrop": {
|
|
76
|
+
"type": "array",
|
|
77
|
+
"description": "Optional: the backdrops the game can draw over besides its own background. [\"video\"] opts in to mirror mode (paint no opaque background while backdrop is 'video').",
|
|
78
|
+
"uniqueItems": true,
|
|
79
|
+
"items": { "enum": ["video"] }
|
|
80
|
+
},
|
|
81
|
+
"intro": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"description": "Optional: Fabi explains the game on the TV before calibration, in 1 to 4 steps. Each step: one sentence (Italian and English, up to 90 characters; or one per age range) over an image of the game or a move shown with the children's avatars. The voice is generated by Fabi Play at publication from the text. Without intro the game goes straight to calibration; an invalid one is skipped by the TV.",
|
|
84
|
+
"required": ["steps"],
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"properties": {
|
|
87
|
+
"steps": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"minItems": 1,
|
|
90
|
+
"maxItems": 4,
|
|
91
|
+
"items": { "anyOf": [{ "$ref": "#/definitions/imageStep" }, { "$ref": "#/definitions/moveStep" }] }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"definitions": {
|
|
97
|
+
"imageStep": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"description": "A step over an image of the game.",
|
|
100
|
+
"required": ["say", "image"],
|
|
101
|
+
"additionalProperties": false,
|
|
102
|
+
"properties": {
|
|
103
|
+
"say": { "$ref": "#/definitions/say" },
|
|
104
|
+
"image": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"description": "An image of the game, 16:9, at least 1280×720: a path relative to the game's address, with the same rules as entry (no scheme, no leading /, no \".\" or \"..\" segments, up to 200 characters), ending in lowercase .jpg or .png (no WebP: old TV browsers may not decode it). JPG (quality about 80) for screenshots, PNG only for flat art.",
|
|
107
|
+
"pattern": "^(?:[A-Za-z0-9_][A-Za-z0-9_-]*(?:\\.[A-Za-z0-9_-]+)*/)*[A-Za-z0-9_][A-Za-z0-9_-]*(?:\\.[A-Za-z0-9_-]+)*\\.(?:jpg|png)$",
|
|
108
|
+
"maxLength": 200,
|
|
109
|
+
"examples": ["intro/track.jpg"]
|
|
110
|
+
},
|
|
111
|
+
"practice": { "const": false, "description": "Only with a move." }
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"moveStep": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"description": "A step showing a move with the children's avatars.",
|
|
117
|
+
"required": ["say", "move"],
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"properties": {
|
|
120
|
+
"say": { "$ref": "#/definitions/say" },
|
|
121
|
+
"move": {
|
|
122
|
+
"description": "The move Fabi shows, drawn with each child's avatar.",
|
|
123
|
+
"enum": ["jump", "squat", "clap", "lane-left", "lane-right", "arms-up", "arms-wide", "arms-v", "star"]
|
|
124
|
+
},
|
|
125
|
+
"practice": {
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"description": "true: the children try it and each gets a tick; the step moves on when all did, or after 8 s. Default false: Fabi only shows it."
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"say": {
|
|
132
|
+
"description": "What Fabi says: Italian and English, up to 90 characters each; or one such per age range (at least one; the closest range present is used for the others).",
|
|
133
|
+
"anyOf": [
|
|
134
|
+
{ "$ref": "#/definitions/texts90" },
|
|
135
|
+
{
|
|
136
|
+
"type": "object",
|
|
137
|
+
"minProperties": 1,
|
|
138
|
+
"additionalProperties": false,
|
|
139
|
+
"properties": {
|
|
140
|
+
"3-5": { "$ref": "#/definitions/texts90" },
|
|
141
|
+
"6-8": { "$ref": "#/definitions/texts90" },
|
|
142
|
+
"9-12": { "$ref": "#/definitions/texts90" }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"texts90": {
|
|
148
|
+
"type": "object",
|
|
149
|
+
"description": "One sentence in Italian and English: up to 90 characters (code points), no white space at either end, no control or bidirectional control characters. Fabi Play's check (parseIntro) also rejects lone surrogates and counts the characters of the NFC form.",
|
|
150
|
+
"required": ["it", "en"],
|
|
151
|
+
"additionalProperties": false,
|
|
152
|
+
"properties": {
|
|
153
|
+
"it": { "type": "string", "minLength": 1, "maxLength": 90, "pattern": "^[^\\s\\u0000-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069](?:[^\\u0000-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069]*[^\\s\\u0000-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069])?$" },
|
|
154
|
+
"en": { "type": "string", "minLength": 1, "maxLength": 90, "pattern": "^[^\\s\\u0000-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069](?:[^\\u0000-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069]*[^\\s\\u0000-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069])?$" }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"texts60": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"required": ["it", "en"],
|
|
160
|
+
"additionalProperties": false,
|
|
161
|
+
"properties": {
|
|
162
|
+
"it": { "type": "string", "minLength": 1, "maxLength": 60, "pattern": "\\S" },
|
|
163
|
+
"en": { "type": "string", "minLength": 1, "maxLength": 60, "pattern": "\\S" }
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"texts300": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"required": ["it", "en"],
|
|
169
|
+
"additionalProperties": false,
|
|
170
|
+
"properties": {
|
|
171
|
+
"it": { "type": "string", "minLength": 1, "maxLength": 300, "pattern": "\\S" },
|
|
172
|
+
"en": { "type": "string", "minLength": 1, "maxLength": 300, "pattern": "\\S" }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabi-play/protocol",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Messages between the phone, the screen and the games, with their checks.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fabi-play",
|
|
7
|
+
"protocol",
|
|
8
|
+
"pose",
|
|
9
|
+
"games"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://docs.fabiplay.app",
|
|
12
|
+
"bugs": "https://github.com/fabiplay/fabiplay-kit/issues",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/fabiplay/fabiplay-kit.git",
|
|
16
|
+
"directory": "packages/protocol"
|
|
17
|
+
},
|
|
18
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
19
|
+
"author": "Fabi Play (https://fabiplay.app)",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"fabi-play.schema.json"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"dts-bundle-generator": "9.5.1",
|
|
36
|
+
"esbuild": "0.28.2",
|
|
37
|
+
"tsx": "4.23.15",
|
|
38
|
+
"typescript": "5.7.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "tsx --test test/*.test.ts",
|
|
43
|
+
"build": "node ../../scripts/build-lib.mjs"
|
|
44
|
+
},
|
|
45
|
+
"module": "./dist/index.js",
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"types": "./dist/index.d.ts",
|
|
49
|
+
"import": "./dist/index.js",
|
|
50
|
+
"default": "./dist/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./fabi-play.schema.json": "./fabi-play.schema.json",
|
|
53
|
+
"./package.json": "./package.json"
|
|
54
|
+
}
|
|
6
55
|
}
|