@hyprune/schema 0.5.2 → 0.7.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 +156 -0
- package/README.md +73 -4
- package/bin/validate.mjs +15 -5
- 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/extensions/blueprint.schema.json +29 -0
- package/lib/validate.d.mts +29 -2
- package/lib/validate.mjs +494 -8
- package/package.json +1 -1
- package/v0.10/library.schema.json +93 -0
- package/v0.10/object.schema.json +1713 -0
- package/v0.20/ipc.schema.json +11800 -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.8/common.schema.json +478 -0
- package/v0.8/world.schema.json +1261 -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/CHANGELOG.md
CHANGED
|
@@ -142,3 +142,159 @@ definition exists in 0.16 and the graphics properties differ only by the new key
|
|
|
142
142
|
action it would take (`""|click|grab|pocket|place|mount|select|path|drop`).
|
|
143
143
|
- Everything else is inherited from 0.18 unchanged, including the offload cap
|
|
144
144
|
and device keys; `session.hello` reports `0.19`.
|
|
145
|
+
|
|
146
|
+
## IPC 0.20: pocket actions and blueprint map levels
|
|
147
|
+
|
|
148
|
+
- `surface.pocket {surfaceId}` and `surface.unpocket {surfaceId}` (surface.control).
|
|
149
|
+
Pocket: the window leaves the world for the Placement queue, its placement kept;
|
|
150
|
+
idempotent. Unpocket: placed at a free spot in front of the player
|
|
151
|
+
(`placed: true`) or left in the pocket, selected for the Placement tool
|
|
152
|
+
(`placed: false`). Results `{revision}` / `{revision, placed}`.
|
|
153
|
+
- `map.levels` (optional, 1..6): `{id, name, minY, maxY, svg, sha256}` per
|
|
154
|
+
blueprint level, sorted by `minY`, in the frame of the map (`worldToMap`).
|
|
155
|
+
- New `extensions/blueprint.schema.json`: the `com.hyprune.blueprint` 0.1 world
|
|
156
|
+
extension (levels with package-relative SVG paths; byte budgets, ordering and
|
|
157
|
+
the `class` vocabulary are stated in its description and enforced by consumers).
|
|
158
|
+
- Everything else is inherited from 0.19 unchanged; `session.hello` reports
|
|
159
|
+
`0.20` and lists the two methods. Inheritance test included.
|
|
160
|
+
|
|
161
|
+
## World 0.8: placed world objects
|
|
162
|
+
|
|
163
|
+
- World format **0.8** adds `objectAssets` (at most 32) and `objects` (at most
|
|
164
|
+
256). An asset is a package-relative glTF (`scene`, same extension rules as the
|
|
165
|
+
world's scene.gltf; never lightmapped) with optional declarative node animation
|
|
166
|
+
(at most 16: `spin` or `orbit` about `pivot`/`axis` in asset space, or `bob`
|
|
167
|
+
along `axis` by `amplitude` metres; `period` 0.5–3600 s, optional `phase`), an
|
|
168
|
+
optional static `collision` proxy node and `lighting.mode` (`probe` default, or
|
|
169
|
+
`unlit`). Nodes are referenced by unique glTF node name. `library` (`id@version`)
|
|
170
|
+
is reserved for shared object libraries and rejected in 0.8. An object places an
|
|
171
|
+
asset with a `pose`, uniform `scale` (0.01–100), animation `speed` (0–10, default
|
|
172
|
+
1), `phase` (0–1) and an optional `ambient` RGB override (0–16).
|
|
173
|
+
- `validate('world')` (from 0.8): unique asset and object ids, known asset
|
|
174
|
+
references, normalised poses, unit axes, per-type fields (spin/orbit need a
|
|
175
|
+
pivot and take no amplitude; bob the reverse), one animation per node, collision
|
|
176
|
+
node not animated, library references rejected. `validate('scene', gltf,
|
|
177
|
+
undefined, '0.8', {world, asset: id})` checks an asset's glTF: extension rules,
|
|
178
|
+
referenced node names exist exactly once, no animated node inside another
|
|
179
|
+
animated subtree, the collision node has a mesh and is outside every animated
|
|
180
|
+
subtree.
|
|
181
|
+
- `v0.8/common.schema.json` is the 0.7 common definitions under the 0.8 `$id`
|
|
182
|
+
(the directory already held IPC 0.8, which keeps its own references).
|
|
183
|
+
Everything else is inherited from 0.7 unchanged. Inheritance test included.
|
|
184
|
+
|
|
185
|
+
## @hyprune/schema 0.6.0 (2026-10-11)
|
|
186
|
+
|
|
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.
|
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.8), the IPC protocol (0.1–0.22), 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.8 # a world package manifest
|
|
12
|
+
npx hyprune-validate scene scene.gltf - 0.8 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.8/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'));
|
|
@@ -118,6 +118,15 @@ optimised scene assets: `KHR_texture_basisu` (KTX2, material textures only),
|
|
|
118
118
|
`extensionsRequired` and lightmap textures against the world format. Version
|
|
119
119
|
gates use `atLeast()`; see RFC-0004 "world 0.7".
|
|
120
120
|
|
|
121
|
+
World format **0.8** adds placed world objects: `objectAssets` (package glTF
|
|
122
|
+
assets with optional `spin`/`orbit`/`bob` node animation, a static collision
|
|
123
|
+
proxy and `probe`/`unlit` lighting) and `objects` (instances with pose, scale,
|
|
124
|
+
speed, phase and an optional ambient override). Objects are render-only apart
|
|
125
|
+
from the proxy. Check an asset's glTF with
|
|
126
|
+
`validate('scene', gltf, undefined, '0.8', {world, asset: 'armillary'})` (CLI:
|
|
127
|
+
`npm run validate -- scene objects/armillary.gltf - 0.8 world.json armillary`).
|
|
128
|
+
See RFC-0004 "world 0.8 placed world objects".
|
|
129
|
+
|
|
121
130
|
IPC **0.16** adds the `upscaler` graphics key (`"fsr1"`, `"bilinear"` or `null` to
|
|
122
131
|
inherit the preset) to `graphicsConfig`/`graphicsPatch`. Every 0.15 definition is
|
|
123
132
|
otherwise unchanged; earlier schemas stay frozen. See RFC-0003/0008 amendments.
|
|
@@ -139,3 +148,63 @@ bob, reticle style/size/colour and reach limits), widens graphics `fov` to 30-12
|
|
|
139
148
|
adds `reticleColor`/`validColor`/`invalidColor` to the overlay style and the
|
|
140
149
|
required `interaction.reticle {state, action}`. Every other 0.18 definition is
|
|
141
150
|
unchanged. See the RFC-0006 amendment "IPC 0.19 player settings and reticle".
|
|
151
|
+
|
|
152
|
+
IPC **0.20** adds `surface.pocket`/`surface.unpocket` (surface.control): put a
|
|
153
|
+
world window into the pocket (the Placement queue) and take it out again, placed
|
|
154
|
+
in front of the player or selected for the Placement tool (`placed`). Pocket state
|
|
155
|
+
stays `editor.unplaced.windows`. `spatial.map` gains optional `levels` (1-6
|
|
156
|
+
per-level blueprint SVGs). `extensions/blueprint.schema.json` defines the world
|
|
157
|
+
extension `com.hyprune.blueprint` 0.1 that supplies them. Every other 0.19
|
|
158
|
+
definition is unchanged. See the RFC-0004 and RFC-0005 amendments for IPC 0.20.
|
|
159
|
+
|
|
160
|
+
IPC **0.21** adds markers, personal quests and focus sessions (RFC-0012 P1). Three
|
|
161
|
+
new required state keys: `markers` (the marker reveal, the persistent-marker mode
|
|
162
|
+
and up to 64 resolved markers), `quests` (tracked and up to 16 active quests,
|
|
163
|
+
counts) and `focus` (the focus session phase, monotonic phase times, credited
|
|
164
|
+
time, cycles and target). New methods `markers.reveal` (shell.control),
|
|
165
|
+
`guidance.get`/`guidance.set` (marker, quest and focus settings, the `player.get`
|
|
166
|
+
pattern), `quests.list/get/track/accept/abandon/resume/create/update/delete/mark`
|
|
167
|
+
and `focus.start/pause/resume/skip/stop/history`; mutations need the new
|
|
168
|
+
`quest.control` grant. Events `quest.progress` and `focus.phase`. The world keymap
|
|
169
|
+
gains `markers.reveal`, the overlay style a required `questColor`, and
|
|
170
|
+
`spatial.projection` markers an optional `markerId`. File schemas in `v0.21/`:
|
|
171
|
+
`quests-personal.schema.json` (`quests/personal.json`), `guidance.schema.json`
|
|
172
|
+
(`guidance.json`) and `focus-history.schema.json` (one `focus/history.jsonl`
|
|
173
|
+
line). Every other 0.20 definition is unchanged. See RFC-0012.
|
|
174
|
+
|
|
175
|
+
IPC **0.22** adds the object catalog and user placements (RFC-0011 P1): the
|
|
176
|
+
`objects.control` grant, a required `objects` state key (this world's authored
|
|
177
|
+
and user instances, object pocket items, user-object usage and limits),
|
|
178
|
+
`objects.catalog/pocket/place/transform/remove/hide/reset/configure` and
|
|
179
|
+
`libraries.list/install/remove`. `editor.selectedKind` gains `node`,
|
|
180
|
+
`editor.submode` gains `universal`, `editor.unplaced` gains `objects` and the
|
|
181
|
+
new `editor.gizmo` carries the tool 4 axis space, pivot and snapping. Error
|
|
182
|
+
`data` may name the refusing placement `rule`. Every other 0.21 definition is
|
|
183
|
+
unchanged. See the RFC-0011 amendment "P1 in IPC 0.22".
|
|
184
|
+
|
|
185
|
+
## Objects and libraries (RFC-0011)
|
|
186
|
+
|
|
187
|
+
Object manifests (`object.json`) and library manifests (`library.json`) use the
|
|
188
|
+
world format numbering and start at **0.9** (`v0.9/object.schema.json`,
|
|
189
|
+
`v0.9/library.schema.json`); `v0.9/library-releases.schema.json` describes the
|
|
190
|
+
meta repository's `libraries/releases.json`. `v0.10/` holds a **draft** that adds
|
|
191
|
+
the `mover` trait, RFC-0013's `machine` trait and the extra slot kinds; it may
|
|
192
|
+
change until world format 0.10 is defined.
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
import { validate, objectFormats } from '@hyprune/schema';
|
|
196
|
+
const result = validate('object', manifest, undefined, undefined, { gltf, directory: 'street-lantern' });
|
|
197
|
+
// result.errors, result.warnings, result.triangles
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
npm run validate -- object examples/object.json
|
|
202
|
+
npm run validate -- object objects/bench/object.json - 0.9 objects/bench/object.glb
|
|
203
|
+
npm run validate -- library examples/library.json
|
|
204
|
+
npm run validate -- library-releases examples/library-releases.json
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The validator is pure: it never reads files. Pass `gltf` (the parsed glTF or a
|
|
208
|
+
GLB's JSON chunk) for the scene cross-checks, and `files`, `thumbnail` and
|
|
209
|
+
`measured` (exact rest bounds, largest texture, GPU bytes) when you have them;
|
|
210
|
+
`@hyprune/sdk`'s `hyprune object check <dir>` gathers all of them for a directory.
|
package/bin/validate.mjs
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { validate } from '../lib/validate.mjs';
|
|
4
|
-
const [kind, file, method, version, worldFile, ...extra] = process.argv.slice(2);
|
|
5
|
-
|
|
4
|
+
const [kind, file, method, version, worldFile, asset, ...extra] = process.argv.slice(2);
|
|
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
|
|
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
|
+
}
|