@glissade/cli 0.46.0 → 0.47.0-pre.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/dist/cli.js CHANGED
@@ -26,7 +26,10 @@ const KNOWN_BOOLEAN_FLAGS = new Set([
26
26
  "write",
27
27
  "keep-voice",
28
28
  "help",
29
- "lottie"
29
+ "lottie",
30
+ "lint",
31
+ "global",
32
+ "iife"
30
33
  ]);
31
34
  function parseArgs(argv) {
32
35
  const positional = [];
@@ -92,8 +95,8 @@ const USAGE = `usage:
92
95
  gs cache verify <scene-module> [--range a..b] [--sample <n>] assert cache hits == cold renders (§3.5)
93
96
  gs mcp <scene-module> start an MCP stdio server for this scene: describe / list_targets / apply_patch / undo / render_frame (the AI-native write layer)
94
97
  gs build [filter...] [--config <glissade.config.ts>] [--affected <git-ref>] [--explain] content-graph DAG runner: narrate→sfx→loudness→render per scene, runs ONLY the stale subtree. --affected <ref> pre-filters to scenes a git diff since <ref> touched (rebuild only what a change set touched; composed with the per-step content-hash staleness)
95
- gs describe [--out <api.json>] [--examples] snapshot THIS engine's describe() API manifest (stdout, or --out to a file) — the input to gs migrate
96
- gs types [--out <file.ts>] [--from <api.json>] [--check] codegen a type-checked track() SDK from the describe() manifest: only registered animatable paths + their value types compile, so a typo'd path or wrong value-type id is a COMPILE error (import track from the generated file). --check fails if --out is stale. Zero-runtime (types + a re-typed re-export of the real track)
98
+ gs describe [--out <api.json>] [--examples] [--lint] snapshot THIS engine's describe() API manifest (stdout, or --out to a file) — the input to gs migrate. --lint reconciles the manifest against the window.glissade runtime surface (every helper/node/surface member resolves, no type surfaced as a value, no arity drift) and exits non-zero on drift
99
+ gs types [--out <file.ts>] [--from <api.json>] [--check] [--global] codegen a type-checked track() SDK from the describe() manifest: only registered animatable paths + their value types compile, so a typo'd path or wrong value-type id is a COMPILE error (import track from the generated file). --check fails if --out is stale. Zero-runtime (types + a re-typed re-export of the real track). --global (alias --iife) instead emits a SELF-CONTAINED ambient window.glissade .d.ts for the no-build <script src> author (typed IIFE surface — a typo'd window.glissade member is a compile error)
97
100
  gs migrate <baseline-api.json> [--json] [--check] diff a saved API manifest against the current engine: moved imports / removed / added / changed, with a suggested fix per breaking item (advisory; --check exits non-zero on any breaking change for CI gating)
98
101
  gs repin <scene-module> --golden <dir> [--name <p>] [--frames a,b,..] [--fps <n>] [--since <ref>] [--write] [--only a,b] [--heatmap <dir>] [--floor <ssim>] [--force] narration-aware golden reviewer: render current vs committed goldens, report perceptual delta + the re-narration cause, re-pin only frames you allow (default dry-run; --floor refuses a bigger-than-expected drop)
99
102
  gs localize <scene-module> --to <locale> [--from <locale>] [--write] [--strict] [--keep-voice] [--json] fork a narration into a new locale (clone segment/pause structure, PRESERVING beat ids so .start() anchors survive) + stub messages.<locale>.json from the scene's t() ids, running the render path's parity + localize checks BEFORE any TTS. Default dry-run (exits non-zero on drift); --write emits <base>.<locale>.narration.json + messages.<locale>.json (re-localize CARRIES existing translations over — never clobbers); --strict refuses to write on a preflight failure
@@ -310,6 +313,20 @@ async function main() {
310
313
  const { describe } = await import("@glissade/scene/describe");
311
314
  if (df.has("examples")) await import("@glissade/scene/examples");
312
315
  const manifest = describe(df.has("examples") ? { examples: true } : {});
316
+ if (df.has("lint")) {
317
+ const { describeLint, collectRuntimeSurface, exemptFromUnreachable } = await import("./describeLint.js");
318
+ const { surface, unreachable } = await collectRuntimeSurface(manifest);
319
+ const exempt = exemptFromUnreachable(manifest, unreachable);
320
+ const violations = describeLint(manifest, surface, { exempt });
321
+ if (violations.length > 0) {
322
+ process.stderr.write(`gs describe --lint: ${violations.length} violation(s) — describe() drifted from the window.glissade surface:\n`);
323
+ for (const v of violations) process.stderr.write(` ✗ [${v.kind}] ${v.name}: ${v.detail}\n`);
324
+ process.exit(1);
325
+ }
326
+ const skipped = exempt.size > 0 ? ` (${exempt.size} browser-only helper(s) not verifiable headlessly — the check:describe CI gate covers them against the built @glissade/browser bundle)` : "";
327
+ process.stderr.write(`gs describe --lint: OK — every described node/helper/surface member resolves on the runtime bundle${skipped}\n`);
328
+ return;
329
+ }
313
330
  const json = `${JSON.stringify(manifest, null, 2)}\n`;
314
331
  const outPath = df.get("out");
315
332
  if (outPath) {
@@ -321,7 +338,8 @@ async function main() {
321
338
  }
322
339
  if (command === "types") {
323
340
  const { flags: tf } = parseArgs(rest);
324
- const { generateTypedSdk } = await import("./typedSdk.js");
341
+ const global = tf.has("global") || tf.has("iife");
342
+ const { generateTypedSdk, generateAmbientDts } = await import("./typedSdk.js");
325
343
  const { readFileSync, writeFileSync, existsSync } = await import("node:fs");
326
344
  const from = tf.get("from");
327
345
  let manifest;
@@ -336,12 +354,12 @@ async function main() {
336
354
  const { describe } = await import("@glissade/scene/describe");
337
355
  manifest = describe();
338
356
  }
339
- const src = generateTypedSdk(manifest);
357
+ const src = global ? generateAmbientDts(manifest) : generateTypedSdk(manifest);
340
358
  const outPath = tf.get("out");
341
359
  if (tf.has("check")) {
342
- if (!outPath) fail("gs types --check needs --out <file> (the committed typed-SDK file to verify)");
360
+ if (!outPath) fail(`gs types ${global ? "--global " : ""}--check needs --out <file> (the committed ${global ? "ambient .d.ts" : "typed-SDK"} file to verify)`);
343
361
  if ((existsSync(outPath) ? readFileSync(outPath, "utf8") : "") !== src) {
344
- process.stderr.write(`gs types: ${outPath} is STALE — run \`gs types --out ${outPath}\` to regenerate\n`);
362
+ process.stderr.write(`gs types: ${outPath} is STALE — run \`gs types ${global ? "--global " : ""}--out ${outPath}\` to regenerate\n`);
345
363
  process.exit(1);
346
364
  }
347
365
  process.stderr.write(`gs types: ${outPath} is up to date\n`);
@@ -349,7 +367,7 @@ async function main() {
349
367
  }
350
368
  if (outPath) {
351
369
  writeFileSync(outPath, src);
352
- process.stderr.write(`gs types: wrote a typed track() SDK (${Object.keys(manifest.nodes).length} node types) → ${outPath}\n`);
370
+ process.stderr.write(global ? `gs types --global: wrote the self-contained ambient window.glissade .d.ts → ${outPath}\n` : `gs types: wrote a typed track() SDK (${Object.keys(manifest.nodes).length} node types) → ${outPath}\n`);
353
371
  } else process.stdout.write(src);
354
372
  return;
355
373
  }
@@ -0,0 +1,105 @@
1
+ import { usageArity } from "@glissade/scene/describe";
2
+ //#region src/describeLint.ts
3
+ /**
4
+ * Type-only surface names that ARE legitimately a runtime class/value on the
5
+ * bundle (a driver class exported under the same name as its type). Empty today —
6
+ * the current type-only surface (Paint/PathValue/FontAxes) erases entirely — but a
7
+ * seam so a future `FollowPath`-style class-and-type name doesn't false-positive.
8
+ */
9
+ const RUNTIME_TYPE_ALLOWLIST = /* @__PURE__ */ new Set([]);
10
+ /**
11
+ * Reconcile a describe() manifest against a runtime surface record. Returns every
12
+ * violation (empty = clean); the CLI prints them and exits non-zero.
13
+ *
14
+ * Assertions:
15
+ * (a) every callable the manifest advertises (node constructor, helper, surface
16
+ * value function/constructor) resolves to a `function` on the bundle;
17
+ * (b) every surface `kind:'type'` name is truly type-only (erases at runtime) —
18
+ * a type surfaced as a runtime value is the drift this catches;
19
+ * (c) arity: a documented callable must not REQUIRE materially more positional
20
+ * args than its usage advertises (`Function.length > documented total + 1`;
21
+ * tolerant by one — a trailing optional like `measurer` legitimately lifts
22
+ * `Function.length`).
23
+ */
24
+ function describeLint(manifest, surface, opts = {}) {
25
+ const exempt = opts.exempt ?? /* @__PURE__ */ new Set();
26
+ const out = [];
27
+ const isFn = (n) => typeof surface[n] === "function";
28
+ const present = (n) => surface[n] !== void 0;
29
+ const callables = /* @__PURE__ */ new Set();
30
+ for (const name of Object.keys(manifest.nodes)) callables.add(name);
31
+ for (const h of manifest.helpers) callables.add(h.name);
32
+ for (const e of manifest.surface ?? []) if (e.kind === "value" && (e.form === "constructor" || e.form === "function")) callables.add(e.name);
33
+ for (const name of [...callables].sort()) {
34
+ if (exempt.has(name) || isFn(name)) continue;
35
+ out.push(present(name) ? {
36
+ kind: "not-callable",
37
+ name,
38
+ detail: `window.glissade.${name} is a ${typeof surface[name]}, expected a function`
39
+ } : {
40
+ kind: "missing",
41
+ name,
42
+ detail: `window.glissade.${name} is documented but MISSING from the runtime bundle`
43
+ });
44
+ }
45
+ for (const e of manifest.surface ?? []) if (e.kind === "value" && e.form === "object" && !exempt.has(e.name) && !present(e.name)) out.push({
46
+ kind: "missing",
47
+ name: e.name,
48
+ detail: `window.glissade.${e.name} (value export) is MISSING from the runtime bundle`
49
+ });
50
+ for (const e of manifest.surface ?? []) if (e.kind === "type" && !exempt.has(e.name) && present(e.name) && !RUNTIME_TYPE_ALLOWLIST.has(e.name)) out.push({
51
+ kind: "type-as-value",
52
+ name: e.name,
53
+ detail: `'${e.name}' is tagged kind:'type' but resolves to a runtime ${typeof surface[e.name]} — surface it as kind:'value' or remove it`
54
+ });
55
+ for (const h of manifest.helpers) {
56
+ if (exempt.has(h.name)) continue;
57
+ const fn = surface[h.name];
58
+ if (typeof fn !== "function") continue;
59
+ const total = usageArity(h.usage);
60
+ if (total !== void 0 && fn.length > total + 1) out.push({
61
+ kind: "arity",
62
+ name: h.name,
63
+ detail: `window.glissade.${h.name} takes ${fn.length} params but its usage documents ${total} — update describe()'s usage or the signature`
64
+ });
65
+ }
66
+ return out;
67
+ }
68
+ /**
69
+ * Assemble the runtime surface a HEADLESS `gs describe --lint` can reach: the
70
+ * embed authoring packages the CLI legitimately depends on (core + scene + their
71
+ * tree-shakeable subpaths + player), merged into one record. Browser-only modules
72
+ * (`@glissade/backend-canvas2d/snapshot`, `@glissade/element`) are NOT CLI deps —
73
+ * importing them throws, so those specifiers are collected into `unreachable` and
74
+ * the caller exempts the helpers they'd have provided. (The `check:describe` CI
75
+ * gate injects the built `@glissade/browser` bundle instead, so it verifies those
76
+ * too.)
77
+ */
78
+ async function collectRuntimeSurface(manifest) {
79
+ const modules = new Set([
80
+ "@glissade/core",
81
+ "@glissade/scene",
82
+ "@glissade/scene/describe",
83
+ "@glissade/scene/layout-ctors"
84
+ ]);
85
+ for (const h of manifest.helpers) modules.add(h.import);
86
+ const surface = {};
87
+ const unreachable = /* @__PURE__ */ new Set();
88
+ for (const spec of modules) try {
89
+ Object.assign(surface, await import(spec));
90
+ } catch {
91
+ unreachable.add(spec);
92
+ }
93
+ return {
94
+ surface,
95
+ unreachable
96
+ };
97
+ }
98
+ /** The exempt name-set for a headless lint: helper names whose import module was unreachable. */
99
+ function exemptFromUnreachable(manifest, unreachable) {
100
+ const exempt = /* @__PURE__ */ new Set();
101
+ for (const h of manifest.helpers) if (unreachable.has(h.import)) exempt.add(h.name);
102
+ return exempt;
103
+ }
104
+ //#endregion
105
+ export { collectRuntimeSurface, describeLint, exemptFromUnreachable };
package/dist/mcp.js CHANGED
@@ -6,12 +6,12 @@ import { join } from "node:path";
6
6
  import { validateTrack } from "@glissade/core";
7
7
  import { evaluate } from "@glissade/scene";
8
8
  import { SkiaBackend } from "@glissade/backend-skia";
9
+ import { describe } from "@glissade/scene/describe";
9
10
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
10
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
12
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
12
13
  import { emptySidecar, mergeSidecar } from "@glissade/core/sidecar";
13
14
  import { applyPatches } from "@glissade/core/studio-host";
14
- import { describe } from "@glissade/scene/describe";
15
15
  //#region src/mcpSession.ts
16
16
  /**
17
17
  * gs mcp session (0.28): the stateful core behind the AI-native WRITE layer. It
package/dist/typedSdk.js CHANGED
@@ -100,5 +100,186 @@ function generateTypedSdk(manifest) {
100
100
  L.push("");
101
101
  return L.join("\n");
102
102
  }
103
+ /**
104
+ * value-type id → the SELF-CONTAINED TS type used in the ambient d.ts. Opaque core
105
+ * types (`paint`/`fontAxes`/`path`) map to local `Gs*` aliases instead of the real
106
+ * `@glissade/core` names, so the emitted file needs no imports (the no-build author
107
+ * has no node_modules to resolve `Paint` from).
108
+ */
109
+ const AMBIENT_VALUE_TYPE = {
110
+ number: "number",
111
+ vec2: "readonly [number, number]",
112
+ "vec2-arc": "readonly [number, number]",
113
+ color: "string",
114
+ string: "string",
115
+ boolean: "boolean",
116
+ paint: "GsPaint",
117
+ fontAxes: "GsFontAxes",
118
+ path: "GsPathValue"
119
+ };
120
+ /** The node constructors that get a real per-node props interface (form:'constructor'). */
121
+ const CONSTRUCTOR_NODES = new Set([
122
+ "Group",
123
+ "Rect",
124
+ "Circle",
125
+ "Path",
126
+ "Text",
127
+ "Image",
128
+ "Video",
129
+ "Layout"
130
+ ]);
131
+ /**
132
+ * Map a manifest prop `type` string to a SELF-CONTAINED TS type for the ambient
133
+ * d.ts. Precise for the known value-type ids (incl. a `|`-union of them, e.g.
134
+ * `color|paint` → `string | GsPaint`); best-effort `unknown` for opaque construction
135
+ * types (`SketchStyle`, `BlendMode`, the `anchor` preset union, …) whose real shapes
136
+ * aren't self-containedly expressible — permissive, so authoring never fights it.
137
+ */
138
+ function ambientPropType(manifestType) {
139
+ const members = manifestType.split("|").map((m) => m.trim());
140
+ if (members.every((m) => m in AMBIENT_VALUE_TYPE)) return [...new Set(members.map((m) => AMBIENT_VALUE_TYPE[m]))].join(" | ");
141
+ if (manifestType === "Node[]") return "GsNode[]";
142
+ return "unknown";
143
+ }
144
+ /**
145
+ * Derive a surface taxonomy when the manifest predates 0.47 (no `surface`): node
146
+ * names that are known constructors → constructors, helpers + core callables →
147
+ * functions, `easings` → object, plus the opaque type-only names. Keeps
148
+ * `gs types --global --from <old-api.json>` working (best-effort).
149
+ */
150
+ function surfaceOf(manifest) {
151
+ if (manifest.surface && manifest.surface.length > 0) return manifest.surface;
152
+ const out = [];
153
+ for (const name of Object.keys(manifest.nodes)) if (CONSTRUCTOR_NODES.has(name)) out.push({
154
+ name,
155
+ kind: "value",
156
+ iife: true,
157
+ form: "constructor"
158
+ });
159
+ else out.push({
160
+ name,
161
+ kind: "value",
162
+ iife: true,
163
+ form: "function"
164
+ });
165
+ for (const h of manifest.helpers) out.push({
166
+ name: h.name,
167
+ kind: "value",
168
+ iife: true,
169
+ form: "function"
170
+ });
171
+ for (const name of [
172
+ "timeline",
173
+ "createScene",
174
+ "track",
175
+ "evaluate",
176
+ "stagger",
177
+ "describe"
178
+ ]) out.push({
179
+ name,
180
+ kind: "value",
181
+ iife: true,
182
+ form: "function"
183
+ });
184
+ out.push({
185
+ name: "easings",
186
+ kind: "value",
187
+ iife: true,
188
+ form: "object"
189
+ });
190
+ for (const name of [
191
+ "Paint",
192
+ "PathValue",
193
+ "FontAxes"
194
+ ]) out.push({
195
+ name,
196
+ kind: "type",
197
+ iife: false,
198
+ form: "type"
199
+ });
200
+ const seen = /* @__PURE__ */ new Set();
201
+ return out.filter((e) => seen.has(e.name) ? false : (seen.add(e.name), true)).sort((a, b) => a.name.localeCompare(b.name));
202
+ }
203
+ /**
204
+ * Generate the SELF-CONTAINED ambient `window.glissade` `.d.ts` from the manifest's
205
+ * `surface` taxonomy. Emits opaque `Gs*` aliases for the complex types, a real
206
+ * per-node construction-props interface for each node constructor (value types
207
+ * reused from the manifest), a best-effort signature per callable, and the global
208
+ * augmentation (`declare global { interface Window { glissade }; const glissade }`).
209
+ * Deterministic (everything sorted) + pure, so it's `--check`-gateable.
210
+ *
211
+ * HONEST about approximation: node instances / Paint / FontAxes / PathValue / the
212
+ * timeline document are OPAQUE brands (the real shapes need the published types);
213
+ * helper signatures beyond the headline authoring calls are `(...args) => any`.
214
+ * The value it delivers is member-name + node-construction-prop type-checking — a
215
+ * typo'd `window.glissade.Reet` or an unknown surface member is a compile error.
216
+ */
217
+ function generateAmbientDts(manifest) {
218
+ const surface = surfaceOf(manifest);
219
+ new Map(manifest.surface ? manifest.surface.map((e) => [e.name, e]) : []);
220
+ const builderMethods = [...manifest.builder.methods.map((m) => m.name)].sort();
221
+ const L = [];
222
+ L.push(`// GENERATED by \`gs types --global\` from the describe() API manifest v${manifest.version} — DO NOT EDIT.`);
223
+ L.push("// Self-contained ambient types for the NO-BUILD `window.glissade` IIFE (<script src>).");
224
+ L.push("// Regenerate after a glissade upgrade: gs types --global --out glissade.d.ts");
225
+ L.push("// Reference it from your tsconfig `include` (or a /// <reference path> ) — no imports needed.");
226
+ L.push("//");
227
+ L.push("// APPROXIMATE: node instances, Paint, FontAxes, PathValue and the Timeline document are");
228
+ L.push("// OPAQUE brands, and helper signatures beyond the headline authoring calls are `(...args) => any`.");
229
+ L.push("// What IS checked: every surface member name + each node constructor's construction props.");
230
+ L.push("");
231
+ L.push("export {};");
232
+ L.push("");
233
+ L.push("/** Opaque best-effort types — the real shapes live in @glissade/core|scene. */");
234
+ L.push("type GsPaint = string | { readonly [key: string]: unknown };");
235
+ L.push("type GsFontAxes = { readonly [axis: string]: number };");
236
+ L.push("type GsPathValue = readonly unknown[];");
237
+ L.push("type GsNode = { readonly __glissadeNode: 'node' };");
238
+ L.push("type GsScene = { readonly __glissadeScene: 'scene' };");
239
+ L.push("type GsTimeline = { readonly __glissadeTimeline: 'timeline' };");
240
+ L.push("");
241
+ L.push("/** The `timeline(tl => …)` builder surface (methods from describe().builder). */");
242
+ L.push("interface GsTimelineBuilder {");
243
+ for (const name of builderMethods) L.push(` ${name}(...args: any[]): GsTimelineBuilder;`);
244
+ L.push("}");
245
+ L.push("");
246
+ const ctorNodes = surface.filter((e) => e.form === "constructor" && manifest.nodes[e.name]).map((e) => e.name).sort();
247
+ const propsInterfaceName = (node) => `Gs${node}Props`;
248
+ for (const node of ctorNodes) {
249
+ const props = manifest.nodes[node].props;
250
+ L.push(`/** Construction props for \`new glissade.${node}({ … })\` (best-effort; opaque prop types → \`unknown\`). */`);
251
+ L.push(`interface ${propsInterfaceName(node)} {`);
252
+ for (const prop of Object.keys(props).sort()) L.push(` ${JSON.stringify(prop)}?: ${ambientPropType(props[prop].type)};`);
253
+ L.push("}");
254
+ L.push("");
255
+ }
256
+ L.push("/** Every `window.glissade.<name>` export (a typo'd member is a compile error). */");
257
+ L.push("interface GlissadeGlobal {");
258
+ for (const e of surface) {
259
+ if (e.kind !== "value" || !e.iife) continue;
260
+ L.push(` ${JSON.stringify(e.name)}: ${ambientMemberType(e, ctorNodes)};`);
261
+ }
262
+ L.push("}");
263
+ L.push("");
264
+ L.push("declare global {");
265
+ L.push(" interface Window {");
266
+ L.push(" glissade: GlissadeGlobal;");
267
+ L.push(" }");
268
+ L.push(" const glissade: GlissadeGlobal;");
269
+ L.push("}");
270
+ L.push("");
271
+ return L.join("\n");
272
+ }
273
+ /** The TS type for one window.glissade value member (constructor / function / object). */
274
+ function ambientMemberType(e, ctorNodes) {
275
+ if (e.form === "object") return "Record<string, unknown>";
276
+ if (e.form === "constructor") return `new (props?: ${ctorNodes.includes(e.name) ? `Gs${e.name}Props` : "Record<string, unknown>"}) => GsNode`;
277
+ switch (e.name) {
278
+ case "timeline": return "(build: (tl: GsTimelineBuilder) => void) => GsTimeline";
279
+ case "createScene": return "(spec: { size: { w: number; h: number }; children: GsNode[] }) => GsScene";
280
+ case "track": return "(target: string, type: string, keys: readonly unknown[], opts?: { editable?: boolean }) => unknown";
281
+ default: return "(...args: any[]) => any";
282
+ }
283
+ }
103
284
  //#endregion
104
- export { generateTypedSdk };
285
+ export { generateAmbientDts, generateTypedSdk };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/cli",
3
- "version": "0.46.0",
3
+ "version": "0.47.0-pre.0",
4
4
  "description": "glissade CLI: headless rendering via backend-skia (+ FFmpeg mux).",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -28,15 +28,15 @@
28
28
  "@napi-rs/canvas": "^0.1.65",
29
29
  "esbuild": "0.28.0",
30
30
  "jiti": "^2.4.2",
31
- "@glissade/backend-skia": "0.46.0",
32
- "@glissade/core": "0.46.0",
33
- "@glissade/interact": "0.46.0",
34
- "@glissade/lottie": "0.46.0",
35
- "@glissade/narrate": "0.46.0",
36
- "@glissade/player": "0.46.0",
37
- "@glissade/scene": "0.46.0",
38
- "@glissade/sfx": "0.46.0",
39
- "@glissade/svg": "0.46.0"
31
+ "@glissade/backend-skia": "0.47.0-pre.0",
32
+ "@glissade/core": "0.47.0-pre.0",
33
+ "@glissade/interact": "0.47.0-pre.0",
34
+ "@glissade/lottie": "0.47.0-pre.0",
35
+ "@glissade/narrate": "0.47.0-pre.0",
36
+ "@glissade/player": "0.47.0-pre.0",
37
+ "@glissade/scene": "0.47.0-pre.0",
38
+ "@glissade/sfx": "0.47.0-pre.0",
39
+ "@glissade/svg": "0.47.0-pre.0"
40
40
  },
41
41
  "repository": {
42
42
  "type": "git",