aispritejs 0.5.6 → 0.5.7

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 CHANGED
@@ -12,6 +12,8 @@
12
12
 
13
13
  Part of the **ai\*js** family: zero cross-package dependencies, framework-agnostic core, AI-readable docs.
14
14
 
15
+ > **Status: 0.5.7 — aligned with the ai\*js family version line.** Renderer-agnostic core plus `/pixi`, `/atlas`, `/schema` subpaths; hardened atlas/graph validation. See [CHANGELOG.md](CHANGELOG.md) for history.
16
+
15
17
  ## Why aispritejs
16
18
 
17
19
  - **Input-driven, not name-driven.** You set parameters (`speed=4`, `isGrounded=false`, `fireTrigger("jump")`), not animation names. Visual transitions live in data, decoupled from game code.
@@ -256,9 +258,7 @@ pnpm example:explosion # 6-frame play-once FX via the /pixi adapter
256
258
 
257
259
  ## Status
258
260
 
259
- **v0.1.3 — docs patch.** Adds a "When you DON'T need aispritejs" threshold and a complete, runnable 6-frame explosion (play-once FX) quickstart for the `/pixi` adapter; no source or API changes. v0.1.0 shipped all roadmap modules (1–4): the renderer-agnostic core (`.`), the PixiJS v8 adapter (`aispritejs/pixi`), the atlas parser (`aispritejs/atlas`), and the JSON Schema (`aispritejs/schema`); v0.1.1 added OIDC/SLSA publish provenance and v0.1.2 hardened validation — compile-time rejection of non-finite `speed` / `duration` / `defaultFrameDuration`, plus a runtime clamp of non-finite or non-positive `dt` (`dt <= 0`) to `0` in `update()`. See [CHANGELOG.md](CHANGELOG.md) for the full history. Zero runtime dependencies; the root import graph contains no `pixi.js`; `pixi.js` is an optional, type-only peer used only by the `/pixi` subpath.
260
-
261
- `aispritejs` is the newest package in the **ai\*js** family and follows its **own independent version line** — the `0.1.x` series reflects this package's own maturation, not alignment with any sibling's version number. Low usage in a given game (e.g. one built on static sprites) is expected, not a defect.
261
+ **Aligned with the ai\*js family version line.** The package version is synchronised with the shared family version (`0.5.x` — the banner above and [CHANGELOG.md](CHANGELOG.md) carry the exact release). All four roadmap modules are live: the renderer-agnostic core (`.`), the PixiJS v8 adapter (`aispritejs/pixi`), the atlas parser (`aispritejs/atlas`), and the JSON Schema (`aispritejs/schema`). Prior hardening waves delivered compile-time rejection of non-finite `speed` / `duration` / `defaultFrameDuration`, runtime clamping of non-finite or non-positive `dt` to `0` in `update()`, and named-error coverage for hostile atlas shapes. Zero runtime dependencies; the root import graph contains no `pixi.js`; `pixi.js` is an optional, type-only peer used only by the `/pixi` subpath.
262
262
 
263
263
  ## Roadmap
264
264
 
package/README_ZHTW.md CHANGED
@@ -12,6 +12,8 @@
12
12
 
13
13
  屬於 **ai\*js** 家族:零跨套件相依、核心與框架無關、AI 可讀的文件。
14
14
 
15
+ > **狀態:0.5.7 — 已對齊 ai\*js 家族版本線。** Renderer-agnostic core 加上 `/pixi`、`/atlas`、`/schema` subpath;atlas/graph 驗證已強化。歷史見 [CHANGELOG.md](CHANGELOG.md)。
16
+
15
17
  ## 為什麼用 aispritejs
16
18
 
17
19
  - **輸入驅動,而非名稱驅動。** 你設定參數(`speed=4`、`isGrounded=false`、`fireTrigger("jump")`),而不是動畫名稱。視覺轉移存在於資料中,與遊戲程式解耦。
@@ -253,9 +255,7 @@ pnpm example:platformer
253
255
 
254
256
  ## 狀態
255
257
 
256
- **v0.1.3 —— 文件修補。** 新增「何時你『不需要』aispritejs」門檻,以及一個完整、可執行、針對 `/pixi` 轉接器的 6 格爆炸(play-once FX)快速範例;原始碼與 API 無變動。v0.1.0 一併 ship 所有 roadmap 模組 1–4:與渲染器無關的核心(`.`)、PixiJS v8 轉接器(`aispritejs/pixi`)、atlas parser(`aispritejs/atlas`)、JSON Schema(`aispritejs/schema`);v0.1.1 加上 OIDC/SLSA 發佈 provenance,v0.1.2 強化驗證 —— 在編譯期拒絕非有限的 `speed` / `duration` / `defaultFrameDuration`,並在 `update()` 執行期把非有限或非正值(`dt <= 0`)的 `dt` 夾到 `0`。完整歷史請見 [CHANGELOG.md](CHANGELOG.md)。零執行期相依;根 import 圖不含 `pixi.js`;`pixi.js` 是選用、type-only 的 peer,僅 `/pixi` 子路徑用。
257
-
258
- `aispritejs` 是 **ai\*js** 家族中最新的套件,採用**自己獨立的版本線** —— `0.1.x` 系列反映的是這個套件自身的成熟度,而非與任何手足套件的版本號對齊。在某個遊戲裡使用率低(例如以靜態 sprite 為主的遊戲)是正常現象,並非缺陷。
258
+ **已對齊 ai\*js 家族版本線。** 套件版本與家族共享版本(`0.5.x`)同步——精確版次見上方 banner 與 [CHANGELOG.md](CHANGELOG.md)。所有四個 roadmap 模組均已上線:與渲染器無關的核心(`.`)、PixiJS v8 轉接器(`aispritejs/pixi`)、atlas parser(`aispritejs/atlas`)、JSON Schema(`aispritejs/schema`)。先前強化波次已完成:在編譯期拒絕非有限的 `speed` / `duration` / `defaultFrameDuration`、在 `update()` 執行期把非有限或非正值的 `dt` 夾到 `0`,以及惡意 atlas 格式的具名錯誤覆蓋。零執行期相依;根 import 圖不含 `pixi.js`;`pixi.js` 是選用、type-only 的 peer,僅 `/pixi` 子路徑使用。
259
259
 
260
260
  ## Roadmap
261
261
 
@@ -1,464 +1,6 @@
1
1
  'use strict';
2
2
 
3
- // src/sprite/errors.ts
4
- var SpriteAnimatorDisposedError = class extends Error {
5
- constructor() {
6
- super("aispritejs: animator has been disposed; this operation is not allowed");
7
- this.name = "SpriteAnimatorDisposedError";
8
- }
9
- };
10
- var InvalidGraphError = class extends Error {
11
- constructor(message) {
12
- super(`aispritejs: invalid graph \u2014 ${message}`);
13
- this.name = "InvalidGraphError";
14
- }
15
- };
16
- var UnknownInputError = class extends Error {
17
- input;
18
- constructor(input) {
19
- super(`aispritejs: unknown input "${input}"; declare it in the graph's inputs block`);
20
- this.name = "UnknownInputError";
21
- this.input = input;
22
- }
23
- };
24
- var InputTypeError = class extends Error {
25
- input;
26
- constructor(input, message) {
27
- super(`aispritejs: input "${input}" \u2014 ${message}`);
28
- this.name = "InputTypeError";
29
- this.input = input;
30
- }
31
- };
32
-
33
- // src/sprite/compile.ts
34
- var DEFAULT_FRAME_DURATION = 100;
35
- function isObject(v) {
36
- return typeof v === "object" && v !== null && !Array.isArray(v);
37
- }
38
- function compileGraph(graph) {
39
- const stateEntries = Object.entries(graph.states);
40
- if (stateEntries.length === 0) {
41
- throw new InvalidGraphError("states must declare at least one state");
42
- }
43
- for (const [name, def] of Object.entries(graph.inputs)) {
44
- if (def.type !== "number" && def.type !== "boolean" && def.type !== "trigger") {
45
- throw new InvalidGraphError(
46
- `input "${name}" has unknown type "${def.type}"`
47
- );
48
- }
49
- }
50
- const defaultDuration = graph.defaultFrameDuration ?? DEFAULT_FRAME_DURATION;
51
- if (!Number.isFinite(defaultDuration) || defaultDuration <= 0) {
52
- throw new InvalidGraphError(
53
- `defaultFrameDuration must be a finite number > 0, got ${defaultDuration}`
54
- );
55
- }
56
- if (graph.frames) {
57
- for (const [key, timing] of Object.entries(graph.frames)) {
58
- if (!isObject(timing)) {
59
- throw new InvalidGraphError(`frame "${key}" timing must be an object`);
60
- }
61
- if (timing.duration !== void 0 && (!Number.isFinite(timing.duration) || timing.duration <= 0)) {
62
- throw new InvalidGraphError(
63
- `frame "${key}" duration must be a finite number > 0, got ${timing.duration}`
64
- );
65
- }
66
- }
67
- }
68
- const initial = graph.initial ?? stateEntries[0][0];
69
- if (!Object.hasOwn(graph.states, initial)) {
70
- throw new InvalidGraphError(`initial state "${initial}" is not declared`);
71
- }
72
- const states = /* @__PURE__ */ new Map();
73
- for (const [name, st] of stateEntries) {
74
- const frameKeys = graph.animations[st.animation];
75
- if (frameKeys === void 0 || !Object.hasOwn(graph.animations, st.animation)) {
76
- throw new InvalidGraphError(`state "${name}" references unknown animation "${st.animation}"`);
77
- }
78
- if (frameKeys.length === 0) {
79
- throw new InvalidGraphError(`animation "${st.animation}" (state "${name}") has no frames`);
80
- }
81
- const speed = st.speed ?? 1;
82
- if (!Number.isFinite(speed) || speed <= 0) {
83
- throw new InvalidGraphError(
84
- `state "${name}" speed must be a finite number > 0, got ${speed}`
85
- );
86
- }
87
- const loop = st.loop === true;
88
- if (loop && st.onEnd !== void 0) {
89
- throw new InvalidGraphError(
90
- `state "${name}" loops, so onEnd "${st.onEnd}" would never fire; set loop:false or drop onEnd`
91
- );
92
- }
93
- if (st.onEnd !== void 0 && !Object.hasOwn(graph.states, st.onEnd)) {
94
- throw new InvalidGraphError(`state "${name}" onEnd target "${st.onEnd}" is not declared`);
95
- }
96
- const cumulative = [];
97
- let running = 0;
98
- for (const key of frameKeys) {
99
- running += graph.frames?.[key]?.duration ?? defaultDuration;
100
- cumulative.push(running);
101
- }
102
- states.set(name, {
103
- name,
104
- animation: st.animation,
105
- loop,
106
- speed,
107
- onEnd: st.onEnd,
108
- frameKeys,
109
- cumulative,
110
- total: running
111
- });
112
- }
113
- const compiled = [];
114
- graph.transitions.forEach((t, order) => {
115
- if (!isObject(t)) {
116
- throw new InvalidGraphError(`transition #${order} must be an object`);
117
- }
118
- if (t.when !== void 0 && !Array.isArray(t.when)) {
119
- throw new InvalidGraphError(`transition #${order} "when" must be an array`);
120
- }
121
- if (t.from !== "*" && !Object.hasOwn(graph.states, t.from)) {
122
- throw new InvalidGraphError(`transition #${order} from "${t.from}" is not a declared state`);
123
- }
124
- if (!Object.hasOwn(graph.states, t.to)) {
125
- throw new InvalidGraphError(`transition #${order} to "${t.to}" is not a declared state`);
126
- }
127
- const conditions = [];
128
- const triggers = [];
129
- for (const c of t.when ?? []) {
130
- const def = graph.inputs[c.input];
131
- if (def === void 0 || !Object.hasOwn(graph.inputs, c.input)) {
132
- throw new InvalidGraphError(
133
- `transition #${order} condition references unknown input "${c.input}"`
134
- );
135
- }
136
- compileCondition(order, c.input, c.op, c.value, def.type, conditions, triggers);
137
- }
138
- compiled.push({
139
- from: t.from,
140
- to: t.to,
141
- priority: t.priority ?? 0,
142
- order,
143
- conditions,
144
- triggers
145
- });
146
- });
147
- const candidatesByState = /* @__PURE__ */ new Map();
148
- for (const [name] of stateEntries) {
149
- const list = compiled.filter((t) => t.from === name || t.from === "*");
150
- list.sort((a, b) => b.priority - a.priority || a.order - b.order);
151
- candidatesByState.set(name, list);
152
- }
153
- return { initial, states, candidatesByState };
154
- }
155
- function compileCondition(order, input, op, value, kind, out, triggers) {
156
- switch (op) {
157
- case "Trigger": {
158
- if (kind !== "trigger") {
159
- throw new InvalidGraphError(
160
- `transition #${order}: op Trigger requires a trigger input, but "${input}" is ${kind}`
161
- );
162
- }
163
- if (value !== void 0) {
164
- throw new InvalidGraphError(
165
- `transition #${order}: Trigger condition on "${input}" must not carry a value`
166
- );
167
- }
168
- out.push((s) => s.isPending(input));
169
- triggers.push(input);
170
- return;
171
- }
172
- case "GreaterThan":
173
- case "LessThan": {
174
- if (kind !== "number") {
175
- throw new InvalidGraphError(
176
- `transition #${order}: op ${op} requires a number input, but "${input}" is ${kind}`
177
- );
178
- }
179
- if (typeof value !== "number") {
180
- throw new InvalidGraphError(
181
- `transition #${order}: op ${op} on "${input}" needs a numeric value`
182
- );
183
- }
184
- const v = value;
185
- out.push(
186
- op === "GreaterThan" ? (s) => s.readNumber(input) > v : (s) => s.readNumber(input) < v
187
- );
188
- return;
189
- }
190
- case "Equals":
191
- case "NotEquals": {
192
- if (kind === "trigger") {
193
- throw new InvalidGraphError(
194
- `transition #${order}: op ${op} cannot apply to trigger input "${input}"`
195
- );
196
- }
197
- if (kind === "number") {
198
- if (typeof value !== "number") {
199
- throw new InvalidGraphError(
200
- `transition #${order}: op ${op} on number input "${input}" needs a numeric value`
201
- );
202
- }
203
- const v2 = value;
204
- out.push(
205
- op === "Equals" ? (s) => s.readNumber(input) === v2 : (s) => s.readNumber(input) !== v2
206
- );
207
- return;
208
- }
209
- if (typeof value !== "boolean") {
210
- throw new InvalidGraphError(
211
- `transition #${order}: op ${op} on boolean input "${input}" needs a boolean value`
212
- );
213
- }
214
- const v = value;
215
- out.push(
216
- op === "Equals" ? (s) => s.readBoolean(input) === v : (s) => s.readBoolean(input) !== v
217
- );
218
- return;
219
- }
220
- default:
221
- throw new InvalidGraphError(`transition #${order}: unknown operator "${op}"`);
222
- }
223
- }
224
-
225
- // src/sprite/emitter.ts
226
- function createSignal() {
227
- const listeners = /* @__PURE__ */ new Set();
228
- function on(handler, options) {
229
- if (options?.signal?.aborted) return () => {
230
- };
231
- let detachAbort;
232
- const cleanup = () => {
233
- listeners.delete(wrapped);
234
- if (detachAbort) {
235
- detachAbort();
236
- detachAbort = void 0;
237
- }
238
- };
239
- let wrapped = handler;
240
- if (options?.once) {
241
- wrapped = (payload) => {
242
- cleanup();
243
- handler(payload);
244
- };
245
- }
246
- listeners.add(wrapped);
247
- const sig = options?.signal;
248
- if (sig) {
249
- const onAbort = () => cleanup();
250
- sig.addEventListener("abort", onAbort, { once: true });
251
- detachAbort = () => sig.removeEventListener("abort", onAbort);
252
- }
253
- return cleanup;
254
- }
255
- function emit(payload) {
256
- if (listeners.size === 0) return;
257
- for (const fn of [...listeners]) fn(payload);
258
- }
259
- function clear() {
260
- listeners.clear();
261
- }
262
- return { on, emit, clear };
263
- }
264
-
265
- // src/sprite/inputs.ts
266
- function createInputStore(inputs) {
267
- const kinds = /* @__PURE__ */ new Map();
268
- const numberDefaults = /* @__PURE__ */ new Map();
269
- const booleanDefaults = /* @__PURE__ */ new Map();
270
- const numbers = /* @__PURE__ */ new Map();
271
- const booleans = /* @__PURE__ */ new Map();
272
- const triggers = /* @__PURE__ */ new Map();
273
- for (const [name, def] of Object.entries(inputs)) {
274
- kinds.set(name, def.type);
275
- if (def.type === "number") {
276
- const d = def.default ?? 0;
277
- numberDefaults.set(name, d);
278
- numbers.set(name, d);
279
- } else if (def.type === "boolean") {
280
- const d = def.default ?? false;
281
- booleanDefaults.set(name, d);
282
- booleans.set(name, d);
283
- } else {
284
- triggers.set(name, false);
285
- }
286
- }
287
- function setInput(name, value) {
288
- const kind = kinds.get(name);
289
- if (kind === void 0) throw new UnknownInputError(name);
290
- if (kind === "trigger") {
291
- throw new InputTypeError(name, "is a Trigger; use fireTrigger()");
292
- }
293
- if (kind === "number") {
294
- if (typeof value !== "number") {
295
- throw new InputTypeError(name, `expects a number, received ${typeof value}`);
296
- }
297
- if (Number.isNaN(value)) {
298
- throw new InputTypeError(name, "cannot be set to NaN");
299
- }
300
- numbers.set(name, value);
301
- return;
302
- }
303
- if (typeof value !== "boolean") {
304
- throw new InputTypeError(name, `expects a boolean, received ${typeof value}`);
305
- }
306
- booleans.set(name, value);
307
- }
308
- function fireTrigger(name) {
309
- const kind = kinds.get(name);
310
- if (kind === void 0) throw new UnknownInputError(name);
311
- if (kind !== "trigger") {
312
- throw new InputTypeError(name, "is not a Trigger; use setInput()");
313
- }
314
- triggers.set(name, true);
315
- }
316
- return {
317
- setInput,
318
- fireTrigger,
319
- // `!` is sound: every declared input is seeded at construction and the
320
- // compiler validates that conditions only read declared inputs of the
321
- // matching kind, so these reads always hit a present entry.
322
- readNumber: (name) => numbers.get(name),
323
- readBoolean: (name) => booleans.get(name),
324
- isPending: (name) => triggers.get(name) === true,
325
- consume: (name) => {
326
- triggers.set(name, false);
327
- },
328
- reset: () => {
329
- for (const [name, d] of numberDefaults) numbers.set(name, d);
330
- for (const [name, d] of booleanDefaults) booleans.set(name, d);
331
- for (const name of triggers.keys()) triggers.set(name, false);
332
- }
333
- };
334
- }
335
-
336
- // src/sprite/machine.ts
337
- var NO_TRIGGERS = [];
338
- function createSpriteAnimator(graph) {
339
- const compiled = compileGraph(graph);
340
- const store = createInputStore(graph.inputs);
341
- const stateChange = createSignal();
342
- const complete = createSignal();
343
- let current = mustState(compiled.initial);
344
- let elapsed = 0;
345
- let completed = false;
346
- let activeFrameIndex = 0;
347
- let activeFrameKey = current.frameKeys[0];
348
- let disposed = false;
349
- function mustState(name) {
350
- return compiled.states.get(name);
351
- }
352
- function enter(to, consume) {
353
- const from = current.name;
354
- for (let i = 0; i < consume.length; i++) {
355
- store.consume(consume[i]);
356
- }
357
- current = mustState(to);
358
- elapsed = 0;
359
- completed = false;
360
- activeFrameIndex = 0;
361
- activeFrameKey = current.frameKeys[0];
362
- if (to !== from) stateChange.emit({ to, from });
363
- }
364
- function resolve() {
365
- const list = compiled.candidatesByState.get(current.name);
366
- for (let i = 0; i < list.length; i++) {
367
- const t = list[i];
368
- let ok = true;
369
- const conds = t.conditions;
370
- for (let j = 0; j < conds.length; j++) {
371
- if (!conds[j](store)) {
372
- ok = false;
373
- break;
374
- }
375
- }
376
- if (!ok) continue;
377
- if (t.to === current.name && t.triggers.length === 0) continue;
378
- return { to: t.to, triggers: t.triggers };
379
- }
380
- return void 0;
381
- }
382
- function update(deltaMs) {
383
- if (disposed) throw new SpriteAnimatorDisposedError();
384
- elapsed += (Number.isFinite(deltaMs) && deltaMs > 0 ? deltaMs : 0) * current.speed;
385
- const chosen = resolve();
386
- if (chosen) {
387
- enter(chosen.to, chosen.triggers);
388
- return;
389
- }
390
- const cs = current;
391
- const n = cs.frameKeys.length;
392
- let ended = false;
393
- if (!cs.loop && elapsed >= cs.total) {
394
- activeFrameIndex = n - 1;
395
- ended = true;
396
- } else {
397
- const t = cs.loop ? elapsed % cs.total : elapsed;
398
- let i = 0;
399
- while (i < n - 1 && t >= cs.cumulative[i]) i++;
400
- activeFrameIndex = i;
401
- }
402
- activeFrameKey = cs.frameKeys[activeFrameIndex];
403
- if (ended && !completed) {
404
- completed = true;
405
- complete.emit(cs.name);
406
- if (cs.onEnd !== void 0) enter(cs.onEnd, NO_TRIGGERS);
407
- }
408
- }
409
- function reset() {
410
- if (disposed) throw new SpriteAnimatorDisposedError();
411
- const from = current.name;
412
- store.reset();
413
- current = mustState(compiled.initial);
414
- elapsed = 0;
415
- completed = false;
416
- activeFrameIndex = 0;
417
- activeFrameKey = current.frameKeys[0];
418
- if (from !== compiled.initial) stateChange.emit({ to: compiled.initial, from });
419
- }
420
- function dispose() {
421
- if (disposed) return;
422
- disposed = true;
423
- stateChange.clear();
424
- complete.clear();
425
- }
426
- return {
427
- setInput(name, value) {
428
- if (disposed) throw new SpriteAnimatorDisposedError();
429
- store.setInput(name, value);
430
- },
431
- fireTrigger(name) {
432
- if (disposed) throw new SpriteAnimatorDisposedError();
433
- store.fireTrigger(name);
434
- },
435
- update,
436
- reset,
437
- dispose,
438
- onStateChange(handler, options) {
439
- if (disposed) return () => {
440
- };
441
- return stateChange.on((p) => handler(p.to, p.from), options);
442
- },
443
- onComplete(handler, options) {
444
- if (disposed) return () => {
445
- };
446
- return complete.on(handler, options);
447
- },
448
- get activeState() {
449
- return current.name;
450
- },
451
- get activeFrameKey() {
452
- return activeFrameKey;
453
- },
454
- get activeFrameIndex() {
455
- return activeFrameIndex;
456
- },
457
- get disposed() {
458
- return disposed;
459
- }
460
- };
461
- }
3
+ var chunkY663UAIF_cjs = require('../chunk-Y663UAIF.cjs');
462
4
 
463
5
  // src/atlas/parse.ts
464
6
  var InvalidAtlasError = class extends Error {
@@ -468,10 +10,10 @@ var InvalidAtlasError = class extends Error {
468
10
  }
469
11
  };
470
12
  function isForeignStates(states) {
471
- return isObject(states) && typeof states.initial === "string" && isObject(states.definitions);
13
+ return chunkY663UAIF_cjs.isObject(states) && typeof states.initial === "string" && chunkY663UAIF_cjs.isObject(states.definitions);
472
14
  }
473
15
  function assertAnimations(value) {
474
- if (!isObject(value)) {
16
+ if (!chunkY663UAIF_cjs.isObject(value)) {
475
17
  throw new InvalidAtlasError("`animations` must be an object of frame-key lists");
476
18
  }
477
19
  for (const [name, list] of Object.entries(value)) {
@@ -482,17 +24,17 @@ function assertAnimations(value) {
482
24
  return value;
483
25
  }
484
26
  function parseAtlas(atlas, control) {
485
- if (!isObject(atlas)) {
27
+ if (!chunkY663UAIF_cjs.isObject(atlas)) {
486
28
  throw new InvalidAtlasError("atlas must be an object");
487
29
  }
488
30
  const animations = assertAnimations(atlas.animations);
489
31
  const frames = atlas.frames;
490
- if (frames !== void 0 && !isObject(frames)) {
32
+ if (frames !== void 0 && !chunkY663UAIF_cjs.isObject(frames)) {
491
33
  throw new InvalidAtlasError("`frames`, if present, must be an object keyed by frame key");
492
34
  }
493
- if (isObject(frames)) {
35
+ if (chunkY663UAIF_cjs.isObject(frames)) {
494
36
  for (const [key, entry] of Object.entries(frames)) {
495
- if (!isObject(entry)) {
37
+ if (!chunkY663UAIF_cjs.isObject(entry)) {
496
38
  const actualType = entry === null ? "null" : Array.isArray(entry) ? "array" : typeof entry;
497
39
  throw new InvalidAtlasError(`frame entry "${key}" must be an object, got ${actualType}`);
498
40
  }
@@ -507,19 +49,19 @@ function parseAtlas(atlas, control) {
507
49
  "atlas `states` is event-driven (has `initial`/`definitions`); pass an aispritejs control block as the second argument"
508
50
  );
509
51
  }
510
- if (!isObject(atlas.inputs) || !isObject(atlas.states) || !Array.isArray(atlas.transitions)) {
52
+ if (!chunkY663UAIF_cjs.isObject(atlas.inputs) || !chunkY663UAIF_cjs.isObject(atlas.states) || !Array.isArray(atlas.transitions)) {
511
53
  throw new InvalidAtlasError(
512
54
  "atlas has no aispritejs control block (inputs/states/transitions); pass one as the second argument"
513
55
  );
514
56
  }
515
57
  for (const [key, val] of Object.entries(atlas.inputs)) {
516
- if (!isObject(val)) {
58
+ if (!chunkY663UAIF_cjs.isObject(val)) {
517
59
  const actualType = val === null ? "null" : Array.isArray(val) ? "array" : typeof val;
518
60
  throw new InvalidAtlasError(`input entry "${key}" must be an object, got ${actualType}`);
519
61
  }
520
62
  }
521
63
  for (const [key, val] of Object.entries(atlas.states)) {
522
- if (!isObject(val)) {
64
+ if (!chunkY663UAIF_cjs.isObject(val)) {
523
65
  const actualType = val === null ? "null" : Array.isArray(val) ? "array" : typeof val;
524
66
  throw new InvalidAtlasError(`state entry "${key}" must be an object, got ${actualType}`);
525
67
  }
@@ -527,10 +69,22 @@ function parseAtlas(atlas, control) {
527
69
  const rawTransitions = atlas.transitions;
528
70
  for (let i = 0; i < rawTransitions.length; i++) {
529
71
  const entry = rawTransitions[i];
530
- if (!isObject(entry)) {
72
+ if (!chunkY663UAIF_cjs.isObject(entry)) {
531
73
  const actualType = entry === null ? "null" : Array.isArray(entry) ? "array" : typeof entry;
532
74
  throw new InvalidAtlasError(`transitions[${i}] must be an object, got ${actualType}`);
533
75
  }
76
+ if (Array.isArray(entry.when)) {
77
+ const rawWhen = entry.when;
78
+ for (let j = 0; j < rawWhen.length; j++) {
79
+ const cond = rawWhen[j];
80
+ if (!chunkY663UAIF_cjs.isObject(cond)) {
81
+ const actualType = cond === null ? "null" : Array.isArray(cond) ? "array" : typeof cond;
82
+ throw new InvalidAtlasError(
83
+ `transitions[${i}].when[${j}] must be an object, got ${actualType}`
84
+ );
85
+ }
86
+ }
87
+ }
534
88
  }
535
89
  resolved = {
536
90
  inputs: atlas.inputs,
@@ -551,7 +105,7 @@ function parseAtlas(atlas, control) {
551
105
  };
552
106
  }
553
107
  function loadAtlas(atlas, control) {
554
- return createSpriteAnimator(parseAtlas(atlas, control));
108
+ return chunkY663UAIF_cjs.createSpriteAnimator(parseAtlas(atlas, control));
555
109
  }
556
110
 
557
111
  exports.InvalidAtlasError = InvalidAtlasError;