@hyprune/schema 0.6.0 → 0.8.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/CHANGELOG.md +205 -0
- package/README.md +77 -4
- package/bin/validate.mjs +14 -4
- package/examples/library-releases.json +11 -0
- package/examples/library.json +18 -0
- package/examples/object-character.json +69 -0
- package/examples/object-machine.json +338 -0
- package/examples/object-seat.json +59 -0
- package/examples/object.json +148 -0
- package/examples/world-npcs.json +493 -0
- package/lib/validate.d.mts +34 -3
- package/lib/validate.mjs +604 -16
- package/package.json +1 -1
- package/v0.10/library.schema.json +93 -0
- package/v0.10/object.schema.json +1713 -0
- package/v0.21/focus-history.schema.json +82 -0
- package/v0.21/guidance.schema.json +118 -0
- package/v0.21/ipc.schema.json +15726 -0
- package/v0.21/quests-personal.schema.json +448 -0
- package/v0.22/ipc.schema.json +17466 -0
- package/v0.23/ipc.schema.json +19261 -0
- package/v0.9/common.schema.json +478 -0
- package/v0.9/library-releases.schema.json +53 -0
- package/v0.9/library.schema.json +93 -0
- package/v0.9/object.schema.json +739 -0
- package/v0.9/world.schema.json +2390 -0
package/CHANGELOG.md
CHANGED
|
@@ -185,3 +185,208 @@ definition exists in 0.16 and the graphics properties differ only by the new key
|
|
|
185
185
|
## @hyprune/schema 0.6.0 (2026-10-11)
|
|
186
186
|
|
|
187
187
|
Publishes IPC 0.20 and world format 0.8 to npm.
|
|
188
|
+
|
|
189
|
+
## Object manifests 0.9, libraries and the library release record (RFC-0011)
|
|
190
|
+
|
|
191
|
+
New contract files; no IPC or world format change (world formats stay 0.1–0.8).
|
|
192
|
+
Object manifests use the world format numbering: a world may use objects whose
|
|
193
|
+
`formatVersion` is at most its own.
|
|
194
|
+
|
|
195
|
+
- `v0.9/object.schema.json` (`object.json`): `id` (slug, equals the directory
|
|
196
|
+
name), SemVer `version`, `name`, `category`, `tags`, a directory-relative
|
|
197
|
+
`scene`, `license` + `attribution`, rest-pose `bounds`, optional `thumbnail`
|
|
198
|
+
and `lighting`, `traits` and optional `slots`. Traits: `decorative` (0.8 node
|
|
199
|
+
animations), `collidable` (0.8 proxy), `movable` (`surfaces`, `upright`,
|
|
200
|
+
`scale`), `character` (measured `clips` with `walk` and `idle`, `height`,
|
|
201
|
+
`radius`, `accent`, asset-level `say`/`panel` interactions) and `seat`
|
|
202
|
+
(anchors, `capacity`). `mover`, `machine`, `mount` and `interactive` are
|
|
203
|
+
reserved and rejected with a reason. Slots `states`, `labels`, `spawns` and
|
|
204
|
+
`effects` (at most 8 each) are validated and inert.
|
|
205
|
+
- `v0.9/library.schema.json` (`library.json`): reverse-domain `id`, `version`,
|
|
206
|
+
`name`, `license` + `attribution` and 1–256 listed `objects`.
|
|
207
|
+
- `v0.9/library-releases.schema.json`: the meta repository's
|
|
208
|
+
`libraries/releases.json`, one record per `id@version` (`id`, `version`,
|
|
209
|
+
`formatVersion`, `url`, `sha256`, `nixHash`, `published`).
|
|
210
|
+
- **Draft** `v0.10/object.schema.json` and `v0.10/library.schema.json`: 0.9 plus
|
|
211
|
+
the `mover` trait, RFC-0013's `machine` trait (typed `inputs` with `repeat`,
|
|
212
|
+
`drives` in the mapping language, `primary`, `positions`, `recipes`) and the
|
|
213
|
+
slot additions (`labels` font/align/colour/plate, `spawns` track/items/maxRate,
|
|
214
|
+
`effects` kind/colour/durationMs, new `tracks`, `stacks`, `toggles`, `ports`).
|
|
215
|
+
It may change until world format 0.10 is defined.
|
|
216
|
+
- `validate('object', manifest, undefined, format?, options)`: trait
|
|
217
|
+
combinations, origin conventions (floor, wall and ceiling within 1 cm; a
|
|
218
|
+
character's rest-pose feet within 5 cm), slot
|
|
219
|
+
ids, poses inside the bounds grown by 0.5 m, palettes, seat anchors, track
|
|
220
|
+
points and arcs, stack layouts, machine input references, drive targets and
|
|
221
|
+
output domains. With `options.gltf`: the format's glTF extension rules, node
|
|
222
|
+
names (unique, in the scene; the proxy may sit outside it), the 0.8 animation
|
|
223
|
+
and proxy rules, states-slot materials and nodes (one slot each),
|
|
224
|
+
`KHR_materials_variants` mappings (variant names of a states slot are palette
|
|
225
|
+
ids), character skins, clips and `extras.speed` (2%), triangle budgets
|
|
226
|
+
(50,000; characters 16,000; items 2,000; proxy 2,000) and bounds (exact for
|
|
227
|
+
axis-aligned meshes, else a containing box). `options.files`, `thumbnail`,
|
|
228
|
+
`measured` and `directory` add the directory limits, attribution coverage, the
|
|
229
|
+
256 x 256 thumbnail, the 1 cm bounds check and texture and GPU budgets.
|
|
230
|
+
Results carry `warnings`.
|
|
231
|
+
- `validate('library', manifest, undefined, format?, {objects, files})` and
|
|
232
|
+
`validate('library-releases', record)`. `objectFormats` and `objectLimits` are
|
|
233
|
+
exported. The CLI accepts `object` (with an optional glTF/GLB), `library` and
|
|
234
|
+
`library-releases`.
|
|
235
|
+
|
|
236
|
+
## IPC 0.21: markers, personal quests and focus sessions
|
|
237
|
+
|
|
238
|
+
- New grant `quest.control` (hello capabilities): every quest and focus mutation
|
|
239
|
+
and `guidance.set`.
|
|
240
|
+
- Required state keys: `markers {reveal, persistent, calm, coreDraw, list}`
|
|
241
|
+
(reveal `holdMs` 0 = toggle or 1000..15000, `fadeMs` 0..600; at most 64
|
|
242
|
+
markers with tier 1..4, labels up to 48 characters, one target of position,
|
|
243
|
+
zone, area, place or workspace), `quests {enabled, tracked, active[<=16],
|
|
244
|
+
counts, world: null}` and `focus {state, pausedState, sessionId,
|
|
245
|
+
phaseStartedAtMs, phaseEndsAtMs, phaseMs, remainingMs, creditedMs, crediting,
|
|
246
|
+
cycle, cycles, breakKind, countMode, target, calm}`. Times are monotonic ms.
|
|
247
|
+
- Methods: `markers.reveal {durationMs?}` (shell.control); `guidance.get`,
|
|
248
|
+
`guidance.set {patch}` returning `{config, defaults, overrides, ranges,
|
|
249
|
+
options, path, error}` over 21 flat dotted keys; `quests.list {states?,
|
|
250
|
+
sources?}`, `quests.get {questRef}`, `quests.track {questRef|null}`,
|
|
251
|
+
`quests.accept/abandon/resume/delete {questRef}`, `quests.create {quest}`
|
|
252
|
+
(`{revision, questRef}`), `quests.update {questRef, quest}`, `quests.mark
|
|
253
|
+
{questRef, stepId, objectiveId}`; `focus.start {target?, questRef?,
|
|
254
|
+
objectiveId?, overrides?}` (`{revision, sessionId}`),
|
|
255
|
+
`focus.pause/resume/skip/stop {}`, `focus.history {days?}` (at most 512
|
|
256
|
+
intervals). Personal quests: 1..16 steps, 1..8 `manual`, `reach` or `focus`
|
|
257
|
+
objectives per step.
|
|
258
|
+
- Events: `quest.progress` and `focus.phase` (`focusSessionId`; every event
|
|
259
|
+
carries the IPC `sessionId` and `revision`).
|
|
260
|
+
- `inputConfig` world mode gains `markers.reveal`; `overlayStyle` gains
|
|
261
|
+
required `questColor` (optional in the patch); `spatial.projection` markers
|
|
262
|
+
gain optional `markerId`.
|
|
263
|
+
- File schemas: `v0.21/quests-personal.schema.json`,
|
|
264
|
+
`v0.21/guidance.schema.json`, `v0.21/focus-history.schema.json`.
|
|
265
|
+
- Everything else is inherited from 0.20 unchanged; `session.hello` reports
|
|
266
|
+
`0.21`. Inheritance test included.
|
|
267
|
+
|
|
268
|
+
## IPC 0.22: object catalog, user placements and libraries (RFC-0011 P1)
|
|
269
|
+
|
|
270
|
+
- New grant `objects.control` (hello capabilities): every overlay change and
|
|
271
|
+
library install/remove.
|
|
272
|
+
- Required state key `objects {catalogRevision, instances[<=512], pocket[<=16],
|
|
273
|
+
usage, limits}`. An instance is `{id, source (authored|user), ref, name,
|
|
274
|
+
areaId, pose, scale, state (active|hidden|missing|blocked|loading|suspended),
|
|
275
|
+
movable, locked, rule, path, activity, seat}`; `path`, `activity` and `seat`
|
|
276
|
+
are always null until P2. `usage`/`limits` count this world's user
|
|
277
|
+
placements (placements, assets, triangles, gpuBytes, collisionTriangles).
|
|
278
|
+
- Methods: `objects.catalog {}` (`{revision, entries[<=1024]}`; an entry carries
|
|
279
|
+
ref, source, id, version, formatVersion, name, description, category, tags,
|
|
280
|
+
traits, surfaces, scale range, bounds, triangles, gpuBytes,
|
|
281
|
+
collisionTriangles, an absolute thumbnail path, license, library, other
|
|
282
|
+
versions, state, placeable, reason and up to 8 errors);
|
|
283
|
+
`objects.pocket {ref}` (`{revision, itemId}`); `objects.place {ref, pose?,
|
|
284
|
+
scale?}` (`{revision, id}`); `objects.transform {id, pose, scale?}`;
|
|
285
|
+
`objects.remove {id}|{all:true}`; `objects.hide {id, hidden}`;
|
|
286
|
+
`objects.reset {id}|{all:true}`; `objects.configure {id, patch:{locked}}`;
|
|
287
|
+
`libraries.list {}`; `libraries.install {archive, sha256}` (`{revision, id,
|
|
288
|
+
version}`); `libraries.remove {id, version}`. Refs are
|
|
289
|
+
`package/<asset>`, `library/<id>@<semver>/<object>` or `user/<object>`.
|
|
290
|
+
- `editor.selectedKind` gains `node`; `editor.submode` gains `universal`;
|
|
291
|
+
`editor.unplaced` gains `objects` (pocket items, `count` up to 32); new
|
|
292
|
+
required `editor.gizmo {space, pivot, snap}`.
|
|
293
|
+
- Error `data` may carry `rule` (the placement rule or limit that refused a
|
|
294
|
+
method).
|
|
295
|
+
- Everything else is inherited from 0.21 unchanged; `session.hello` reports
|
|
296
|
+
`0.22`. Inheritance test included.
|
|
297
|
+
|
|
298
|
+
## @hyprune/schema 0.7.0 (2026-10-12)
|
|
299
|
+
|
|
300
|
+
Publishes IPC 0.21 (markers, personal quests, focus sessions) and IPC 0.22 (object catalog, user placements, libraries), plus the object manifest (`object.json` 0.9 and the 0.10 draft), library and library release-record schemas.
|
|
301
|
+
|
|
302
|
+
## World 0.9: characters, paths, seats and keepClear (RFC-0011 P2)
|
|
303
|
+
|
|
304
|
+
- `objectAssets[]` (at most 32, ids unique) take exactly one source: inline
|
|
305
|
+
`{id, scene, animations?, collision?, lighting?, traits?, slots?}` (the 0.8
|
|
306
|
+
asset plus object.json 0.9 `traits` and `slots`; `decorative`, `collidable`,
|
|
307
|
+
`movable`, `character` and `seat` are allowed, `mover`, `mount`, `machine` and
|
|
308
|
+
`interactive` rejected), `dir {id, dir}` (a package directory holding
|
|
309
|
+
`object.json`, object format at most the world's) or `library {id, library,
|
|
310
|
+
object}` (`library` is `<reverse-domain id>@<semver>`, `object` a slug).
|
|
311
|
+
`animations` is shorthand for `traits.decorative.animations` and `collision`
|
|
312
|
+
for `traits.collidable`; giving both forms of one is invalid.
|
|
313
|
+
- `objects[]` gain `path` (a `route` path; replaces `pose`), `reverse` (only
|
|
314
|
+
with `path`), `seat` (a `seats[]` id, `<objectId>/<anchorId>` or
|
|
315
|
+
`<objectId>`; characters without a path), `accent` (linear RGB 0..1),
|
|
316
|
+
`interactions` (1..8, replacing the asset's list) and `locked` (movable
|
|
317
|
+
assets). `pose` is required unless `path` is set and forbidden with it. A
|
|
318
|
+
character's `scale` must be 1; at most 16 character instances and 8 per path.
|
|
319
|
+
- Instance interactions `{id, label (1..32), prompt? (1..64), action}` with
|
|
320
|
+
`say {lines[1..8]}`, `waypoint {target: {zoneId}|{position}|{anchor}|{object}}`,
|
|
321
|
+
`guide {path}` (a route) or `panel {panel}`. `travel` and `quest` are
|
|
322
|
+
reserved and rejected. Asset-level interactions stay `say`/`panel`.
|
|
323
|
+
- `paths[]` (at most 64, 1,024 points in total): `{id, name?, use?
|
|
324
|
+
(route|track|generic, default generic), closed?, points[2..256] {position,
|
|
325
|
+
type: line|curve|arc}, speed? (0.1..30), stops?[<=32]}`. A stop is `{point,
|
|
326
|
+
seconds? (0.5..600), clip? (look_around|wave|nod|idle_fidget), face?
|
|
327
|
+
({position}|{anchor}|{object}), seat?}` with at most one of clip, face and
|
|
328
|
+
seat, one stop per point and the point below the point count. Track stops
|
|
329
|
+
take only `point` and `seconds`; generic paths have no stops. An `arc` needs
|
|
330
|
+
a horizontal chord of at least 0.1 m.
|
|
331
|
+
- `seats[]` (at most 64): `{id, pose, height (0.2..1), use? (["sit"]),
|
|
332
|
+
approach? (0.3..2)}`. `keepClear[]` (at most 64): `{id, min, max}`, min
|
|
333
|
+
below max on every axis.
|
|
334
|
+
- `validate('world', manifest, undefined, '0.9', {assets})` adds unique ids,
|
|
335
|
+
asset, path, seat, waypoint and face references, route-only bindings,
|
|
336
|
+
character-only and movable-only fields, pose/path exclusivity, normalised
|
|
337
|
+
seat poses, arc chords, the point total, keepClear ordering and the
|
|
338
|
+
character limits. `assets` maps a dir or library asset id to its parsed
|
|
339
|
+
`object.json`, so its traits are checked like an inline asset's; without it
|
|
340
|
+
those checks skip the asset. Reserved traits and actions are reported with a
|
|
341
|
+
plain reason. `worldLimits` exports the limits.
|
|
342
|
+
- `validate('scene', gltf, undefined, '0.9', {world, asset})` reads an inline
|
|
343
|
+
asset's animations and proxy from either form and checks an inline
|
|
344
|
+
character's skin and clips; dir and library assets are checked as objects.
|
|
345
|
+
- `v0.9/common.schema.json` is the 0.8 common definitions under the 0.9 `$id`
|
|
346
|
+
(the directory's IPC, input, object and library schemas are untouched).
|
|
347
|
+
`examples/world-npcs.json` is a synthetic 0.9 manifest. Everything else is
|
|
348
|
+
inherited from 0.8 unchanged. Inheritance test included.
|
|
349
|
+
|
|
350
|
+
## IPC 0.23: characters, paths and interactions (RFC-0011 P2)
|
|
351
|
+
|
|
352
|
+
- Required state key `npcs {motion (auto|always|still|off), state
|
|
353
|
+
(none|animating|paused|still|off), reason (null or up to 64 characters, e.g.
|
|
354
|
+
battery), usage {authored, user}, limits {authored (<=16), user (<=8)},
|
|
355
|
+
drawing, aimed, interaction}`. `drawing` is null or `{pathId, use, spawn:
|
|
356
|
+
null|{ref}, bind: null|objectId}` (a `paths.draw` in progress); `aimed` is null
|
|
357
|
+
or `{objectId, name, prompt, interactions[<=8] {id, label, type}}`;
|
|
358
|
+
`interaction` is null or `{objectId, interactionId|null, type
|
|
359
|
+
(choose|say|waypoint|guide|panel), label, choices[<=8] {id, label},
|
|
360
|
+
line|null, lineIndex, lineCount, panel|null}`.
|
|
361
|
+
- `objects.instances[]` gain required `character` (boolean) and `accent`
|
|
362
|
+
(null or linear RGB); `path` is null or a qualified path ref
|
|
363
|
+
(`package/<id>`, `user/<id>`), `activity` null or
|
|
364
|
+
`idle|walking|running|stopped|sitting|guiding|interacting|suspended`, `seat`
|
|
365
|
+
null or a seat ref (up to 192 characters).
|
|
366
|
+
- `editor.paths[]` gain required `use` (route|track|generic), `name`
|
|
367
|
+
(string|null), `speed` (number|null) and `stops` (at most 32, the world 0.9
|
|
368
|
+
stop shape).
|
|
369
|
+
- `interaction.reticle.action` gains `interact`. `graphicsConfig` and
|
|
370
|
+
`graphicsPatch` gain `npc_motion` (`auto`, `always`, `still`, `off`).
|
|
371
|
+
- Methods: `npcs.spawn {ref, path?, pose?, phase?, speed? (0.25..2), accent?,
|
|
372
|
+
reverse?}` (`{revision, id}`; path and pose exclusive, reverse needs a path);
|
|
373
|
+
`npcs.bind {id, path?, pose?, phase?, speed?, accent?, reverse?}` (at least one
|
|
374
|
+
setting); `npcs.disable {id, disabled}`; `npcs.remove {id}|{all:true}`;
|
|
375
|
+
`paths.list {}` (`{paths[<=192]}`: `{ref, source (package|user), id, name,
|
|
376
|
+
use, closed, points, speed, stops, walkable (null for non-routes),
|
|
377
|
+
problems[<=8], bound[<=8]}`); `paths.set {path: {id?, name?, use, closed,
|
|
378
|
+
points[2..256], speed?, stops?}}` (`{revision, id}`); `paths.remove {id,
|
|
379
|
+
unbind?}`; `paths.draw {use?, name?, spawn? {ref, phase?, speed?, accent?},
|
|
380
|
+
bind?}` (`{revision, id}`; spawn and bind exclusive); `paths.finish {}`
|
|
381
|
+
(`{revision, id|null, bound|null}`); `interaction.choose {objectId,
|
|
382
|
+
interactionId|null}`. Path params take a qualified ref or a bare id
|
|
383
|
+
(`package/<id>`); `paths.set/remove/draw/finish` ids are user path ids.
|
|
384
|
+
Grants: `npcs.*` and `paths.set/remove/draw/finish` need `objects.control`,
|
|
385
|
+
`paths.list` needs `state.read`, `interaction.choose` needs `world.control`.
|
|
386
|
+
- Everything else is inherited from 0.22 unchanged (no new grant or event);
|
|
387
|
+
`session.hello` reports `0.23` and lists the ten methods. Inheritance test
|
|
388
|
+
included.
|
|
389
|
+
|
|
390
|
+
## @hyprune/schema 0.8.0 (2026-10-12)
|
|
391
|
+
|
|
392
|
+
Publishes IPC 0.23 and world format 0.9: characters and NPC instances, authored paths (route/track/generic) with stops, seats, keepClear volumes and NPC interactions.
|
package/README.md
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
> **Hyprune** is an explorable, world-driven desktop environment for Hyprland: your workspaces become places you can walk through, with live windows on the walls. This repository holds the versioned **schemas** for world packages, IPC and manifests, and their validator. Install Hyprune through the [hyprune](https://github.com/hyprune/hyprune) repository (starter kits, Nix, Arch); learn more at [hyprune.com](https://hyprune.com). Licence: MIT.
|
|
4
4
|
|
|
5
|
-
Versioned contracts for Hyprune, a world-driven Hyprland desktop: world packages (formats 0.1–0.
|
|
5
|
+
Versioned contracts for Hyprune, a world-driven Hyprland desktop: world packages (formats 0.1–0.9), the IPC protocol (0.1–0.23), object and library manifests (0.9, draft 0.10), extension and screen-type manifests and the input keymap. JSON Schema dialect: **2020-12**. Every version is frozen once published; core, the shell and the SDK pin these exact files. Readable reference: [hyprune.com/reference/schema](https://hyprune.com/reference/schema/).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
npm i -D @hyprune/schema
|
|
11
|
-
npx hyprune-validate world world.json - 0.
|
|
12
|
-
npx hyprune-validate scene scene.gltf - 0.
|
|
11
|
+
npx hyprune-validate world world.json - 0.9 # a world package manifest
|
|
12
|
+
npx hyprune-validate scene scene.gltf - 0.9 world.json # its glTF against the world format
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
16
|
import { validate, worldFormats } from '@hyprune/schema';
|
|
17
|
-
import schema from '@hyprune/schema/v0.
|
|
17
|
+
import schema from '@hyprune/schema/v0.9/world.schema.json' with { type: 'json' };
|
|
18
18
|
|
|
19
19
|
const result = validate('world', manifest, undefined, worldFormats.at(-1));
|
|
20
20
|
if (!result.valid) console.error(result.errors.join('\n'));
|
|
@@ -127,6 +127,17 @@ from the proxy. Check an asset's glTF with
|
|
|
127
127
|
`npm run validate -- scene objects/armillary.gltf - 0.8 world.json armillary`).
|
|
128
128
|
See RFC-0004 "world 0.8 placed world objects".
|
|
129
129
|
|
|
130
|
+
World format **0.9** adds characters, paths, seats and keep-clear volumes
|
|
131
|
+
(RFC-0011 P2). An object asset is an inline package glTF (now with optional
|
|
132
|
+
object.json `traits` and `slots`), a package object directory (`dir`) or a
|
|
133
|
+
library object (`library` + `object`). Instances gain `path` (a `route`; it
|
|
134
|
+
replaces `pose`), `reverse`, `seat`, `accent`, `interactions` (`say`,
|
|
135
|
+
`waypoint`, `guide`, `panel`) and `locked`. New top-level `paths` (route,
|
|
136
|
+
track or generic; stops with a clip, facing or seat), `seats` and `keepClear`.
|
|
137
|
+
Pass the object.json of dir and library assets to check their traits too:
|
|
138
|
+
`validate('world', manifest, undefined, '0.9', {assets: {resident: objectJson}})`.
|
|
139
|
+
`examples/world-npcs.json` is a synthetic example. See RFC-0011 "World format 0.9".
|
|
140
|
+
|
|
130
141
|
IPC **0.16** adds the `upscaler` graphics key (`"fsr1"`, `"bilinear"` or `null` to
|
|
131
142
|
inherit the preset) to `graphicsConfig`/`graphicsPatch`. Every 0.15 definition is
|
|
132
143
|
otherwise unchanged; earlier schemas stay frozen. See RFC-0003/0008 amendments.
|
|
@@ -156,3 +167,65 @@ stays `editor.unplaced.windows`. `spatial.map` gains optional `levels` (1-6
|
|
|
156
167
|
per-level blueprint SVGs). `extensions/blueprint.schema.json` defines the world
|
|
157
168
|
extension `com.hyprune.blueprint` 0.1 that supplies them. Every other 0.19
|
|
158
169
|
definition is unchanged. See the RFC-0004 and RFC-0005 amendments for IPC 0.20.
|
|
170
|
+
|
|
171
|
+
IPC **0.21** adds markers, personal quests and focus sessions (RFC-0012 P1). Three
|
|
172
|
+
new required state keys: `markers` (the marker reveal, the persistent-marker mode
|
|
173
|
+
and up to 64 resolved markers), `quests` (tracked and up to 16 active quests,
|
|
174
|
+
counts) and `focus` (the focus session phase, monotonic phase times, credited
|
|
175
|
+
time, cycles and target). New methods `markers.reveal` (shell.control),
|
|
176
|
+
`guidance.get`/`guidance.set` (marker, quest and focus settings, the `player.get`
|
|
177
|
+
pattern), `quests.list/get/track/accept/abandon/resume/create/update/delete/mark`
|
|
178
|
+
and `focus.start/pause/resume/skip/stop/history`; mutations need the new
|
|
179
|
+
`quest.control` grant. Events `quest.progress` and `focus.phase`. The world keymap
|
|
180
|
+
gains `markers.reveal`, the overlay style a required `questColor`, and
|
|
181
|
+
`spatial.projection` markers an optional `markerId`. File schemas in `v0.21/`:
|
|
182
|
+
`quests-personal.schema.json` (`quests/personal.json`), `guidance.schema.json`
|
|
183
|
+
(`guidance.json`) and `focus-history.schema.json` (one `focus/history.jsonl`
|
|
184
|
+
line). Every other 0.20 definition is unchanged. See RFC-0012.
|
|
185
|
+
|
|
186
|
+
IPC **0.22** adds the object catalog and user placements (RFC-0011 P1): the
|
|
187
|
+
`objects.control` grant, a required `objects` state key (this world's authored
|
|
188
|
+
and user instances, object pocket items, user-object usage and limits),
|
|
189
|
+
`objects.catalog/pocket/place/transform/remove/hide/reset/configure` and
|
|
190
|
+
`libraries.list/install/remove`. `editor.selectedKind` gains `node`,
|
|
191
|
+
`editor.submode` gains `universal`, `editor.unplaced` gains `objects` and the
|
|
192
|
+
new `editor.gizmo` carries the tool 4 axis space, pivot and snapping. Error
|
|
193
|
+
`data` may name the refusing placement `rule`. Every other 0.21 definition is
|
|
194
|
+
unchanged. See the RFC-0011 amendment "P1 in IPC 0.22".
|
|
195
|
+
|
|
196
|
+
IPC **0.23** adds characters, paths and interactions (RFC-0011 P2): a required
|
|
197
|
+
`npcs` state key (motion setting and effective state, character budgets, the
|
|
198
|
+
path being drawn, the aimed character and the running interaction),
|
|
199
|
+
`character` and `accent` on object instances with path, activity and seat
|
|
200
|
+
values, `use`/`name`/`speed`/`stops` on editor paths, the reticle action
|
|
201
|
+
`interact` and the graphics key `npc_motion`. New methods
|
|
202
|
+
`npcs.spawn/bind/disable/remove` and `paths.set/remove/draw/finish`
|
|
203
|
+
(objects.control), `paths.list` (state.read) and `interaction.choose`
|
|
204
|
+
(world.control). Every other 0.22 definition is unchanged.
|
|
205
|
+
|
|
206
|
+
## Objects and libraries (RFC-0011)
|
|
207
|
+
|
|
208
|
+
Object manifests (`object.json`) and library manifests (`library.json`) use the
|
|
209
|
+
world format numbering and start at **0.9** (`v0.9/object.schema.json`,
|
|
210
|
+
`v0.9/library.schema.json`); `v0.9/library-releases.schema.json` describes the
|
|
211
|
+
meta repository's `libraries/releases.json`. `v0.10/` holds a **draft** that adds
|
|
212
|
+
the `mover` trait, RFC-0013's `machine` trait and the extra slot kinds; it may
|
|
213
|
+
change until world format 0.10 is defined.
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
import { validate, objectFormats } from '@hyprune/schema';
|
|
217
|
+
const result = validate('object', manifest, undefined, undefined, { gltf, directory: 'street-lantern' });
|
|
218
|
+
// result.errors, result.warnings, result.triangles
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
```sh
|
|
222
|
+
npm run validate -- object examples/object.json
|
|
223
|
+
npm run validate -- object objects/bench/object.json - 0.9 objects/bench/object.glb
|
|
224
|
+
npm run validate -- library examples/library.json
|
|
225
|
+
npm run validate -- library-releases examples/library-releases.json
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The validator is pure: it never reads files. Pass `gltf` (the parsed glTF or a
|
|
229
|
+
GLB's JSON chunk) for the scene cross-checks, and `files`, `thumbnail` and
|
|
230
|
+
`measured` (exact rest bounds, largest texture, GPU bytes) when you have them;
|
|
231
|
+
`@hyprune/sdk`'s `hyprune object check <dir>` gathers all of them for a directory.
|
package/bin/validate.mjs
CHANGED
|
@@ -2,17 +2,27 @@
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { validate } from '../lib/validate.mjs';
|
|
4
4
|
const [kind, file, method, version, worldFile, asset, ...extra] = process.argv.slice(2);
|
|
5
|
-
|
|
5
|
+
// For kind `object` the fifth argument is the object's glTF (.gltf or .glb) for the cross-checks.
|
|
6
|
+
const gltfJson = path => {
|
|
7
|
+
const bytes = readFileSync(path);
|
|
8
|
+
if (!/\.glb$/i.test(path)) return JSON.parse(bytes.toString('utf8'));
|
|
9
|
+
if (bytes.length < 20 || bytes.readUInt32LE(0) !== 0x46546c67 || bytes.readUInt32LE(16) !== 0x4e4f534a) throw new Error('Invalid GLB header/JSON chunk');
|
|
10
|
+
return JSON.parse(bytes.subarray(20, 20 + bytes.readUInt32LE(12)).toString('utf8'));
|
|
11
|
+
};
|
|
12
|
+
if (!kind || !file || extra.length || ((worldFile || asset) && kind !== 'scene' && kind !== 'object') || (asset && kind === 'object')) {
|
|
6
13
|
console.error('Usage: hyprune-validate <world|extension|screen-type|ipc> <file.json|-> [response-method|-] [contract-version]\n' +
|
|
7
|
-
' hyprune-validate scene <scene.gltf> - <world-format> <world.json> [object-asset-id]\n
|
|
14
|
+
' hyprune-validate scene <scene.gltf> - <world-format> <world.json> [object-asset-id]\n' +
|
|
15
|
+
' hyprune-validate <object|library|library-releases> <file.json|-> [-] [format] [object scene.gltf|.glb]\n(in this repository: npm run validate -- ...)');
|
|
8
16
|
process.exitCode = 2;
|
|
9
17
|
} else {
|
|
10
18
|
try {
|
|
11
19
|
const bytes = readFileSync(file === '-' ? 0 : file);
|
|
12
20
|
if (bytes.length > 1_048_576) throw new Error('Document exceeds 1 MiB');
|
|
13
21
|
const value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
14
|
-
const
|
|
15
|
-
|
|
22
|
+
const options = kind === 'object' ? { gltf: worldFile ? gltfJson(worldFile) : undefined }
|
|
23
|
+
: { world: worldFile ? JSON.parse(readFileSync(worldFile, 'utf8')) : undefined, asset };
|
|
24
|
+
const result = validate(kind, value, method === '-' ? undefined : method, version || undefined, options);
|
|
25
|
+
for (const warning of result.warnings || []) console.error(`warning: ${warning}`);
|
|
16
26
|
if (result.valid) console.log(`VALID ${kind}: ${file}`);
|
|
17
27
|
else { console.error(result.errors.join('\n')); process.exitCode = 1; }
|
|
18
28
|
} catch (error) { console.error(error.message); process.exitCode = 2; }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"com.example.street@1.2.0": {
|
|
3
|
+
"id": "com.example.street",
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"formatVersion": "0.9",
|
|
6
|
+
"url": "https://example.com/releases/street-v1.2.0/com.example.street-1.2.0.tar.zst",
|
|
7
|
+
"sha256": "f06838d7b0725080016c84ae4f47aa01367ee665e3844637b14ed7f23706c894",
|
|
8
|
+
"nixHash": "sha256-8Gg417ByUIABbISuT0eqATZ+5mXjhEY3sU7X8jcGyJQ=",
|
|
9
|
+
"published": false
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion": "0.9",
|
|
3
|
+
"id": "com.example.street",
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"name": "Street kit",
|
|
6
|
+
"license": "CC-BY-4.0",
|
|
7
|
+
"attribution": [
|
|
8
|
+
{
|
|
9
|
+
"path": ".",
|
|
10
|
+
"author": "Example Author",
|
|
11
|
+
"license": "CC-BY-4.0"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"objects": [
|
|
15
|
+
"street-lantern",
|
|
16
|
+
"bench"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion": "0.9",
|
|
3
|
+
"id": "resident",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"name": "Resident",
|
|
6
|
+
"category": "characters",
|
|
7
|
+
"scene": "object.glb",
|
|
8
|
+
"license": "CC-BY-4.0",
|
|
9
|
+
"attribution": [
|
|
10
|
+
{
|
|
11
|
+
"path": ".",
|
|
12
|
+
"author": "Example Author",
|
|
13
|
+
"license": "CC-BY-4.0"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"bounds": {
|
|
17
|
+
"min": [
|
|
18
|
+
-0.45,
|
|
19
|
+
0,
|
|
20
|
+
-0.2
|
|
21
|
+
],
|
|
22
|
+
"max": [
|
|
23
|
+
0.45,
|
|
24
|
+
1.69,
|
|
25
|
+
0.22
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"thumbnail": "thumbnail.png",
|
|
29
|
+
"traits": {
|
|
30
|
+
"character": {
|
|
31
|
+
"clips": {
|
|
32
|
+
"walk": {
|
|
33
|
+
"speed": 1.12
|
|
34
|
+
},
|
|
35
|
+
"run": {
|
|
36
|
+
"speed": 2.9
|
|
37
|
+
},
|
|
38
|
+
"idle": {},
|
|
39
|
+
"sit": {
|
|
40
|
+
"seatHeight": 0.46
|
|
41
|
+
},
|
|
42
|
+
"sit_idle": {}
|
|
43
|
+
},
|
|
44
|
+
"height": 1.69,
|
|
45
|
+
"radius": 0.3,
|
|
46
|
+
"accent": false,
|
|
47
|
+
"interactions": [
|
|
48
|
+
{
|
|
49
|
+
"id": "talk",
|
|
50
|
+
"label": "Talk",
|
|
51
|
+
"action": {
|
|
52
|
+
"type": "say",
|
|
53
|
+
"lines": [
|
|
54
|
+
"The lifts run every few minutes."
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "where",
|
|
60
|
+
"label": "Where am I?",
|
|
61
|
+
"action": {
|
|
62
|
+
"type": "panel",
|
|
63
|
+
"panel": "map"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|