@hopple/sdk 1.0.0-next.4 → 1.0.0-next.9
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/README.md +78 -19
- package/index.d.ts +348 -106
- package/index.js +1 -1
- package/manifest.d.ts +47 -3
- package/manifest.js +15 -5
- package/manifest.md +32 -16
- package/package.json +1 -2
- package/config.schema.json +0 -1004
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hopple/sdk
|
|
1
|
+
/*! @hopple/sdk | Copyright (c) 2026 The Hopple authors | MIT License */
|
|
2
2
|
// The SDK runs from the runtime the page loads as its first script, which
|
|
3
3
|
// defines the global `Hopple`; this forwards each export to it.
|
|
4
4
|
const runtime = globalThis.Hopple;
|
package/manifest.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
1
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-BSfTrJ/manifest/src/manifest.d.ts
|
|
2
2
|
/**
|
|
3
3
|
* The API a game is written against: `M.m`, a major and the least minor it
|
|
4
4
|
* needs, or `next`, the pre-releases of the next stable release.
|
|
@@ -217,6 +217,26 @@ type ScoreBoard = {
|
|
|
217
217
|
direction: ScoreDirection;
|
|
218
218
|
/** See `ScoreFormat`. Absent means `integer`. */
|
|
219
219
|
format?: ScoreFormat;
|
|
220
|
+
/**
|
|
221
|
+
* Which board, of the ones this id has named. Absent is `1`.
|
|
222
|
+
*
|
|
223
|
+
* **Raising it starts the board again from nobody.** The runs under the
|
|
224
|
+
* version before are kept and no longer counted, shown or ranked, so a board
|
|
225
|
+
* whose scores stopped being comparable — new levels, a new rule — begins
|
|
226
|
+
* again under the id the game already submits to.
|
|
227
|
+
*
|
|
228
|
+
* **Only a change to what a stored score means needs it.** The title changes
|
|
229
|
+
* freely, and so does the format between `integer` and `decimal`. Once the
|
|
230
|
+
* board holds scores, turning it around (`direction`) or writing it in
|
|
231
|
+
* another unit (to or from `duration-ms`) is refused at the same version.
|
|
232
|
+
* Going back to a version declared before brings its runs back, so it is
|
|
233
|
+
* held to what that version was.
|
|
234
|
+
*
|
|
235
|
+
* @asType integer
|
|
236
|
+
* @minimum 1
|
|
237
|
+
* @maximum 1000
|
|
238
|
+
*/
|
|
239
|
+
version?: number;
|
|
220
240
|
};
|
|
221
241
|
/**
|
|
222
242
|
* The boards this game keeps a score on.
|
|
@@ -380,6 +400,28 @@ type Achievement = {
|
|
|
380
400
|
secret?: boolean;
|
|
381
401
|
/** Present makes this measured rather than flagged. See `AchievementProgress`. */
|
|
382
402
|
progress?: AchievementProgress;
|
|
403
|
+
/**
|
|
404
|
+
* Which achievement, of the ones this id has named. Absent is `1`.
|
|
405
|
+
*
|
|
406
|
+
* **Raising it starts the achievement again for everybody.** What players
|
|
407
|
+
* reported and unlocked under the version before is kept and no longer
|
|
408
|
+
* counted, so it is earned afresh under the id the game already reports to.
|
|
409
|
+
*
|
|
410
|
+
* **Only a change to what a stored value means needs it.** The words change
|
|
411
|
+
* freely — title, detail, icon, secret — and so does the target, and a tier
|
|
412
|
+
* added after the last one: progress is kept as the number reported, which
|
|
413
|
+
* stays true when the goal moves, and a player who unlocked it keeps it. At
|
|
414
|
+
* the same version, a published achievement may not turn from a flag into a
|
|
415
|
+
* measure or back, turn its direction around, write its value in another
|
|
416
|
+
* unit (to or from `duration-ms`), or move or drop a tier it already has.
|
|
417
|
+
* Going back to a version declared before brings back what was earned under
|
|
418
|
+
* it, so it is held to what that version was.
|
|
419
|
+
*
|
|
420
|
+
* @asType integer
|
|
421
|
+
* @minimum 1
|
|
422
|
+
* @maximum 1000
|
|
423
|
+
*/
|
|
424
|
+
version?: number;
|
|
383
425
|
};
|
|
384
426
|
/**
|
|
385
427
|
* The achievements this game keeps for a player.
|
|
@@ -445,7 +487,7 @@ type InputDeviceSet = InputDevice[];
|
|
|
445
487
|
* doubled. A thumbstick is not one of these; see `InputStick`.
|
|
446
488
|
*
|
|
447
489
|
* @group Input
|
|
448
|
-
* @pattern ^(?:key\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|pointer\.(?:primary|secondary)|mouse\.(?:left|middle|right)|touch\.tap|controls\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\.(?:north|south|east|west|start|select)|pad\.(?:left|right)\.(?:north|south|east|west|bumper|trigger))$
|
|
490
|
+
* @pattern ^(?:key\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|pointer\.(?:primary|secondary)|mouse\.(?:left|middle|right|wheelup|wheeldown)|touch\.tap|controls\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\.(?:north|south|east|west|start|select)|pad\.(?:left|right)\.(?:north|south|east|west|bumper|trigger))$
|
|
449
491
|
* @errorMessage is not a binding this SDK knows (a thumbstick is read rather than bound, and Escape is the host's key)
|
|
450
492
|
*/
|
|
451
493
|
type InputBinding = string;
|
|
@@ -521,7 +563,7 @@ type PlayerBinding = string;
|
|
|
521
563
|
* mouse button.
|
|
522
564
|
*
|
|
523
565
|
* @group Input
|
|
524
|
-
* @pattern ^(?:key\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|mouse\.(?:left|middle|right))$
|
|
566
|
+
* @pattern ^(?:key\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|mouse\.(?:left|middle|right|wheelup|wheeldown))$
|
|
525
567
|
* @errorMessage must be a key or a mouse button (and Escape is the host's key)
|
|
526
568
|
*/
|
|
527
569
|
type KeyboardBinding = string;
|
|
@@ -902,6 +944,8 @@ type GameManifest = {
|
|
|
902
944
|
publishable?: boolean;
|
|
903
945
|
/**
|
|
904
946
|
* The game's own version, `MAJOR.MINOR.PATCH`. Ours to read, never to parse for meaning.
|
|
947
|
+
* People see it beside the build the server numbers each upload with, and
|
|
948
|
+
* each published version needs one no other published version of the game has.
|
|
905
949
|
*
|
|
906
950
|
* @category Important
|
|
907
951
|
* @pattern ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
|
package/manifest.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hopple/sdk 1.0.0-next.
|
|
1
|
+
/*! @hopple/sdk 1.0.0-next.9 | Copyright (c) 2026 The Hopple authors | MIT License */
|
|
2
2
|
//#region ../manifest/src/manifest.schema.ts
|
|
3
3
|
const GameManifest = {
|
|
4
4
|
"type": "object",
|
|
@@ -186,7 +186,12 @@ const ScoreBoard = {
|
|
|
186
186
|
"errorMessage": "must not be blank"
|
|
187
187
|
},
|
|
188
188
|
"direction": { "$ref": "#/definitions/ScoreDirection" },
|
|
189
|
-
"format": { "$ref": "#/definitions/ScoreFormat" }
|
|
189
|
+
"format": { "$ref": "#/definitions/ScoreFormat" },
|
|
190
|
+
"version": {
|
|
191
|
+
"type": "integer",
|
|
192
|
+
"minimum": 1,
|
|
193
|
+
"maximum": 1e3
|
|
194
|
+
}
|
|
190
195
|
},
|
|
191
196
|
"required": ["title", "direction"],
|
|
192
197
|
"additionalProperties": false
|
|
@@ -241,7 +246,12 @@ const Achievement = {
|
|
|
241
246
|
"errorMessage": "must not be blank"
|
|
242
247
|
},
|
|
243
248
|
"secret": { "type": "boolean" },
|
|
244
|
-
"progress": { "$ref": "#/definitions/AchievementProgress" }
|
|
249
|
+
"progress": { "$ref": "#/definitions/AchievementProgress" },
|
|
250
|
+
"version": {
|
|
251
|
+
"type": "integer",
|
|
252
|
+
"minimum": 1,
|
|
253
|
+
"maximum": 1e3
|
|
254
|
+
}
|
|
245
255
|
},
|
|
246
256
|
"required": ["title"],
|
|
247
257
|
"additionalProperties": false
|
|
@@ -365,7 +375,7 @@ const InputBindings = {
|
|
|
365
375
|
};
|
|
366
376
|
const InputBinding = {
|
|
367
377
|
"type": "string",
|
|
368
|
-
"pattern": "^(?:key\\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|pointer\\.(?:primary|secondary)|mouse\\.(?:left|middle|right)|touch\\.tap|controls\\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\\.(?:north|south|east|west|start|select)|pad\\.(?:left|right)\\.(?:north|south|east|west|bumper|trigger))$",
|
|
378
|
+
"pattern": "^(?:key\\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|pointer\\.(?:primary|secondary)|mouse\\.(?:left|middle|right|wheelup|wheeldown)|touch\\.tap|controls\\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\\.(?:north|south|east|west|start|select)|pad\\.(?:left|right)\\.(?:north|south|east|west|bumper|trigger))$",
|
|
369
379
|
"errorMessage": "is not a binding this SDK knows (a thumbstick is read rather than bound, and Escape is the host's key)"
|
|
370
380
|
};
|
|
371
381
|
const ControlLayout = {
|
|
@@ -562,7 +572,7 @@ const definitions = {
|
|
|
562
572
|
},
|
|
563
573
|
KeyboardBinding: {
|
|
564
574
|
"type": "string",
|
|
565
|
-
"pattern": "^(?:key\\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|mouse\\.(?:left|middle|right))$",
|
|
575
|
+
"pattern": "^(?:key\\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|mouse\\.(?:left|middle|right|wheelup|wheeldown))$",
|
|
566
576
|
"errorMessage": "must be a key or a mouse button (and Escape is the host's key)"
|
|
567
577
|
},
|
|
568
578
|
PlayersDeclaration: {
|
package/manifest.md
CHANGED
|
@@ -55,15 +55,17 @@ same function, so they agree on whether a manifest is valid.
|
|
|
55
55
|
|
|
56
56
|
### In an editor
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
as `config.schema.json`, for completion, hover docs and errors as
|
|
60
|
-
JSON config can point at it:
|
|
58
|
+
The `hopple` CLI ships the hopple config's JSON Schema, the manifest's
|
|
59
|
+
included, as `config.schema.json`, for completion, hover docs and errors as
|
|
60
|
+
you type. A JSON config in a project with `hopple` installed can point at it:
|
|
61
61
|
|
|
62
62
|
```json
|
|
63
|
-
{ "$schema": "./node_modules
|
|
63
|
+
{ "$schema": "./node_modules/hopple/config.schema.json", "page": "dist/index.html", "manifest": { ... } }
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
A TypeScript config gets the same checks from its type,
|
|
66
|
+
A TypeScript config gets the same checks from its type,
|
|
67
|
+
`import type { HoppleConfig } from 'hopple'`, whose manifest is the type of
|
|
68
|
+
the installed `@hopple/sdk`.
|
|
67
69
|
|
|
68
70
|
A few rules relate one part of a manifest to another, which JSON Schema can't
|
|
69
71
|
express: buttons must name declared actions, a control's `as` must name its
|
|
@@ -142,6 +144,7 @@ A game without this key can still use `sdk.save`.
|
|
|
142
144
|
| `title` | yes | What players see. At most 40 characters. |
|
|
143
145
|
| `direction` | yes | `desc`: higher is better. `asc`: lower is better (times, strokes). |
|
|
144
146
|
| `format` | no | `integer` (default), `decimal`, or `duration-ms` (so `224310` shows as `3:44.31`). |
|
|
147
|
+
| `version` | no | 1 by default. Raising it starts the board again; see [Changing the manifest in a new version](#changing-the-manifest-in-a-new-version). |
|
|
145
148
|
|
|
146
149
|
`boards` is a map even for one board, so a game can add a second one later
|
|
147
150
|
without changing the meaning of the first. At most 16 boards.
|
|
@@ -180,6 +183,9 @@ There are two kinds:
|
|
|
180
183
|
`secret: true` hides the title and detail from players until it is earned.
|
|
181
184
|
The manifest is public, so this only stops spoilers.
|
|
182
185
|
|
|
186
|
+
`version` is 1 by default. Raising it starts the achievement again for
|
|
187
|
+
everybody; see [Changing the manifest in a new version](#changing-the-manifest-in-a-new-version).
|
|
188
|
+
|
|
183
189
|
Limits: 128 achievements, titles up to 40 characters, details up to 120, icons
|
|
184
190
|
up to 8 characters, 8 tiers.
|
|
185
191
|
|
|
@@ -354,17 +360,27 @@ approval and links, and an upload declaring it is refused.
|
|
|
354
360
|
|
|
355
361
|
## Changing the manifest in a new version
|
|
356
362
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
363
|
+
Every board and every achievement has a `version`, 1 when left out. The
|
|
364
|
+
server refuses a new version of the game whose manifest would change what data
|
|
365
|
+
players already have means, and the way out is always to raise that board's or
|
|
366
|
+
achievement's `version`: it starts again from nobody under the same id, and
|
|
367
|
+
what was stored under the version before is kept but no longer counted.
|
|
368
|
+
|
|
369
|
+
- **Boards and achievements can be added and dropped freely.** What players
|
|
370
|
+
stored under one that is dropped is kept, and comes back if it is declared
|
|
371
|
+
again.
|
|
372
|
+
- **A board's title can change, and so can its format between `integer` and
|
|
373
|
+
`decimal`.** Once it has scores, flipping its `direction` or moving its
|
|
374
|
+
format to or from `duration-ms` needs a new `version`.
|
|
375
|
+
- **An achievement's title, detail, icon and secret flag can change, and so can
|
|
376
|
+
its target; tiers can be added after the last one.** Progress is kept as the
|
|
377
|
+
number reported, so a player at 12 of 12 is at 12 of 25 when the target is
|
|
378
|
+
raised, and keeps the unlock; when it is lowered past them, they have it.
|
|
379
|
+
Turning a flag into a measured achievement or back, flipping `direction`,
|
|
380
|
+
moving its format to or from `duration-ms`, or moving or dropping a tier it
|
|
381
|
+
already has needs a new `version`, whether or not anybody holds it yet.
|
|
382
|
+
- **A version declared again is held to what it was**, since what was stored
|
|
383
|
+
under it comes back.
|
|
368
384
|
|
|
369
385
|
Input action ids and audio channel ids may change freely. A player's remapped
|
|
370
386
|
keys or volume settings for an id that no longer exists are simply dropped.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hopple/sdk",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.9",
|
|
4
4
|
"description": "The Hopple SDK: what a game imports to talk to the host that runs it, forwarded to the runtime its page loads.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://hopple.app",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"types": "./manifest.d.ts",
|
|
30
30
|
"default": "./manifest.js"
|
|
31
31
|
},
|
|
32
|
-
"./config.schema.json": "./config.schema.json",
|
|
33
32
|
"./package.json": "./package.json"
|
|
34
33
|
},
|
|
35
34
|
"main": "./index.js",
|