@fabi-play/sdk 0.0.0-stage → 0.1.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/index.js ADDED
@@ -0,0 +1,490 @@
1
+ import {
2
+ BodyInterpolator,
3
+ NS,
4
+ PROTOCOL_VERSION,
5
+ __publicField,
6
+ defaultAvatarFor,
7
+ drawAvatar,
8
+ isAvatarId,
9
+ parseDeviceHint,
10
+ parseGameStats,
11
+ parseView,
12
+ smoothBody,
13
+ toScreen,
14
+ wire
15
+ } from "./chunks/chunk-QJPCNRFI.js";
16
+
17
+ // src/adapters.ts
18
+ function muteDuringBreaks(play, win = window) {
19
+ const contexts = /* @__PURE__ */ new Set();
20
+ const Original = win.AudioContext;
21
+ if (Original) {
22
+ win.AudioContext = class extends Original {
23
+ constructor(...args) {
24
+ super(...args);
25
+ contexts.add(this);
26
+ }
27
+ };
28
+ }
29
+ let paused = [];
30
+ let suspended = [];
31
+ void Promise.resolve(play).then((p) => {
32
+ p.on("break-start", () => {
33
+ paused = [...win.document.querySelectorAll("audio, video")].filter((m) => !m.paused);
34
+ paused.forEach((m) => m.pause());
35
+ suspended = [...contexts].filter((c) => c.state === "running");
36
+ suspended.forEach((c) => void c.suspend());
37
+ });
38
+ p.on("break-done", () => {
39
+ paused.forEach((m) => void m.play().catch(() => void 0));
40
+ suspended.forEach((c) => void c.resume());
41
+ paused = [];
42
+ suspended = [];
43
+ });
44
+ });
45
+ }
46
+ var DEFAULT_KEYS = { jump: "Space", squat: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", clap: "Enter" };
47
+ function parseKeys(value) {
48
+ if (value.trim() === "default") return void 0;
49
+ const map = {};
50
+ for (const pair of value.split(",")) {
51
+ const [action, code] = pair.split(":").map((s) => s.trim());
52
+ if (action && code) map[action] = code;
53
+ }
54
+ return map;
55
+ }
56
+ var NAMED = {
57
+ Space: [" ", 32],
58
+ Enter: ["Enter", 13],
59
+ Escape: ["Escape", 27],
60
+ ShiftLeft: ["Shift", 16],
61
+ ArrowLeft: ["ArrowLeft", 37],
62
+ ArrowUp: ["ArrowUp", 38],
63
+ ArrowRight: ["ArrowRight", 39],
64
+ ArrowDown: ["ArrowDown", 40]
65
+ };
66
+ function keyInfo(code) {
67
+ const named = NAMED[code];
68
+ if (named) return { key: named[0], code, keyCode: named[1] };
69
+ const letter = /^Key([A-Z])$/.exec(code);
70
+ if (letter) return { key: letter[1].toLowerCase(), code, keyCode: letter[1].charCodeAt(0) };
71
+ const digit = /^Digit([0-9])$/.exec(code);
72
+ if (digit) return { key: digit[1], code, keyCode: 48 + Number(digit[1]) };
73
+ throw new Error(`Fabi Play: unknown key "${code}" (use Space, Enter, Arrow\u2026, KeyA\u2026, Digit0\u2026)`);
74
+ }
75
+ var domKeys = (doc = document) => ({
76
+ dispatch(type, k) {
77
+ const e = new KeyboardEvent(type, { key: k.key, code: k.code, bubbles: true, cancelable: true });
78
+ for (const name of ["keyCode", "which"]) Object.defineProperty(e, name, { get: () => k.keyCode });
79
+ (doc.activeElement ?? doc.body ?? doc).dispatchEvent(e);
80
+ }
81
+ });
82
+ var TAP_MS = 80;
83
+ var MAX_HOLD_MS = 800;
84
+ function keyboard(play, map = DEFAULT_KEYS, options = {}) {
85
+ const player = options.player ?? 1;
86
+ const target = options.target ?? domKeys();
87
+ const later = options.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
88
+ const held = /* @__PURE__ */ new Set();
89
+ let lane = 0;
90
+ const down = (a) => {
91
+ const code = map[a];
92
+ if (!code || held.has(a)) return;
93
+ held.add(a);
94
+ target.dispatch("keydown", keyInfo(code));
95
+ };
96
+ const up = (a) => {
97
+ const code = map[a];
98
+ if (!code || !held.has(a)) return;
99
+ held.delete(a);
100
+ target.dispatch("keyup", keyInfo(code));
101
+ };
102
+ const tap = (a) => {
103
+ down(a);
104
+ later(() => up(a), TAP_MS);
105
+ };
106
+ const onGesture = (g) => {
107
+ if (g.player !== player) return;
108
+ const lanes = { "lane-left": -1, "lane-center": 0, "lane-right": 1 };
109
+ switch (g.gesture) {
110
+ case "jump":
111
+ down("jump");
112
+ later(() => up("jump"), MAX_HOLD_MS);
113
+ break;
114
+ case "land":
115
+ up("jump");
116
+ break;
117
+ case "squat":
118
+ down("squat");
119
+ break;
120
+ case "stand":
121
+ up("squat");
122
+ break;
123
+ case "clap":
124
+ tap("clap");
125
+ break;
126
+ default: {
127
+ const next = lanes[g.gesture];
128
+ if (next === void 0) break;
129
+ const dir = next > lane ? "right" : "left";
130
+ for (let i = 0; i < Math.abs(next - lane); i++) later(() => tap(dir), i * (TAP_MS * 2));
131
+ lane = next;
132
+ }
133
+ }
134
+ };
135
+ const onFrame = (f) => {
136
+ const p = f.players.find((x) => x.id === player);
137
+ if (!p) return;
138
+ if (p.shape === "arms-up") down("arms-up");
139
+ else up("arms-up");
140
+ };
141
+ const offs = [play.on("gesture", onGesture), play.on("frame", onFrame), play.on("break-start", () => [...held].forEach(up))];
142
+ return () => {
143
+ offs.forEach((off) => off());
144
+ [...held].forEach(up);
145
+ };
146
+ }
147
+ function domPointer(doc = document) {
148
+ const ring = doc.createElement("div");
149
+ ring.setAttribute("aria-hidden", "true");
150
+ ring.style.cssText = "position:fixed;left:0;top:0;width:56px;height:56px;margin:-28px 0 0 -28px;border-radius:50%;border:6px solid #FBE7B8;background:rgba(251,231,184,.25);pointer-events:none;z-index:2147483647;box-sizing:border-box;transition:transform 60ms linear";
151
+ doc.body.appendChild(ring);
152
+ const fire = (x, y, ...types) => {
153
+ const el = doc.elementFromPoint(x, y) ?? doc.body;
154
+ for (const type of types) {
155
+ const init = { bubbles: true, cancelable: true, clientX: x, clientY: y, pointerId: 1, pointerType: "touch", isPrimary: true };
156
+ el.dispatchEvent(type.startsWith("pointer") ? new PointerEvent(type, init) : new MouseEvent(type, init));
157
+ }
158
+ };
159
+ return {
160
+ size: () => ({ width: doc.documentElement.clientWidth, height: doc.documentElement.clientHeight }),
161
+ move: (x, y) => fire(x, y, "pointermove", "mousemove"),
162
+ click: (x, y) => fire(x, y, "pointerdown", "mousedown", "pointerup", "mouseup", "click"),
163
+ cursor: (x, y, progress) => {
164
+ ring.style.transform = `translate(${x}px, ${y}px) scale(${1 - progress * 0.35})`;
165
+ ring.style.borderColor = progress > 0 ? "#EE6A43" : "#FBE7B8";
166
+ }
167
+ };
168
+ }
169
+ function pointer(play, options = {}) {
170
+ const player = options.player ?? 1;
171
+ const hand = options.hand ?? "any";
172
+ const dwell = options.dwellMs ?? 800;
173
+ const radius = options.radius ?? 0.04;
174
+ const target = options.target ?? domPointer();
175
+ let anchor = null;
176
+ let clicked = false;
177
+ return play.on("frame", (f) => {
178
+ const p = f.players.find((x2) => x2.id === player);
179
+ if (!p) return;
180
+ const pick = (a, b) => a && b ? a.y < b.y ? a : b : a ?? b;
181
+ const h = hand === "left" ? p.leftHand : hand === "right" ? p.rightHand : pick(p.leftHand, p.rightHand);
182
+ if (!h) return;
183
+ const { width, height } = target.size();
184
+ const x = h.x * width;
185
+ const y = h.y * height;
186
+ target.move(x, y);
187
+ const far = anchor ? Math.hypot(x - anchor.x, y - anchor.y) > radius * Math.hypot(width, height) : true;
188
+ if (far) {
189
+ anchor = { x, y, t: f.t };
190
+ clicked = false;
191
+ }
192
+ const progress = clicked ? 0 : Math.min((f.t - anchor.t) / dwell, 1);
193
+ if (progress >= 1) {
194
+ clicked = true;
195
+ target.click(anchor.x, anchor.y);
196
+ }
197
+ target.cursor(x, y, clicked ? 0 : progress);
198
+ });
199
+ }
200
+
201
+ // src/avatars.ts
202
+ var frameTime = () => {
203
+ const t = typeof document !== "undefined" ? document.timeline?.currentTime : null;
204
+ return typeof t === "number" && Number.isFinite(t) ? t : globalThis.performance?.now() ?? Date.now();
205
+ };
206
+ var clockNow = () => globalThis.performance?.now() ?? Date.now();
207
+ var AvatarBook = class {
208
+ /** now: this frame's time (tests). */
209
+ constructor(now = frameTime) {
210
+ __publicField(this, "now", now);
211
+ __publicField(this, "known", /* @__PURE__ */ new Map());
212
+ __publicField(this, "smoothed", /* @__PURE__ */ new Map());
213
+ /** When each body of the host's frames was taken (the frame's t) and arrived. */
214
+ __publicField(this, "times", /* @__PURE__ */ new WeakMap());
215
+ __publicField(this, "lerp", new BodyInterpolator());
216
+ /** Animation frames something was drawn in, for stats. */
217
+ __publicField(this, "drawnAt", NaN);
218
+ __publicField(this, "drawn", 0);
219
+ /**
220
+ * A player's smoothed body (see Play.smoothedBody): a frame's body smoothed for its player and interpolated for
221
+ * this animation frame, or a player id's (null if none yet).
222
+ */
223
+ __publicField(this, "smoothedBody", (who, options = {}) => {
224
+ const interpolate = options.interpolate !== false;
225
+ if (typeof who === "number") {
226
+ const last = this.smoothed.get(who);
227
+ if (!last) return null;
228
+ return interpolate ? this.lerp.at(who, this.tick()) ?? last.body : last.body;
229
+ }
230
+ if (typeof who !== "object" || who === null || !Array.isArray(who.body)) return null;
231
+ return this.posed(who.id, who.body, smoothAmount(options.smooth), interpolate);
232
+ });
233
+ }
234
+ /** Every frame from the host: the latest avatar of each player, and when their bodies were taken (t). */
235
+ note(players, t, arrival = clockNow()) {
236
+ for (const p of players) {
237
+ this.known.set(p.id, this.resolve(p.id, p.avatar));
238
+ if (t !== void 0 && Number.isFinite(t) && Array.isArray(p.body)) this.times.set(p.body, { t, arrival });
239
+ }
240
+ }
241
+ /**
242
+ * The avatar id of a player (an id, 1-based, or a frame): the one the child picked, or the default of the slot
243
+ * (fox, lion, dragon, unicorn: the 6–8 band's, as no age is known here) before the first frame, from an older host, or for an id this SDK doesn't know.
244
+ */
245
+ of(player) {
246
+ if (typeof player === "object" && player !== null) return player.avatar !== void 0 ? this.resolve(player.id, player.avatar) : this.of(player.id);
247
+ return this.known.get(player) ?? defaultAvatarFor(player - 1);
248
+ }
249
+ /**
250
+ * The body to draw for this player now: body smoothed (amount below 1) against the player's previous one, then,
251
+ * with interpolate, at this animation frame's time between the last two.
252
+ */
253
+ posed(player, body, amount, interpolate = true) {
254
+ const sample = this.smooth(player, body, amount);
255
+ return interpolate ? this.lerp.at(player, this.tick()) ?? sample : sample;
256
+ }
257
+ /** body smoothed against this player's previous one; the same body twice in a row is smoothed once. */
258
+ smooth(player, body, amount) {
259
+ const last = this.smoothed.get(player);
260
+ if (last && last.raw === body && last.amount === amount) return last.body;
261
+ if (last && last.amount !== amount) this.lerp.forget(player);
262
+ const next = amount < 1 ? smoothBody(last?.body, body, amount) : body;
263
+ this.smoothed.set(player, { raw: body, body: next, amount });
264
+ const at = this.times.get(body);
265
+ const arrival = at?.arrival ?? clockNow();
266
+ this.lerp.push(player, at?.t ?? arrival, next, arrival);
267
+ return next;
268
+ }
269
+ /**
270
+ * Animation frames the avatars were drawn in, since the last call; for stats. Frames are
271
+ * told apart by their time, so any number of avatars or calls in one frame count once.
272
+ */
273
+ takeDrawn() {
274
+ const n = this.drawn;
275
+ this.drawn = 0;
276
+ return n;
277
+ }
278
+ tick() {
279
+ const t = this.now();
280
+ if (t !== this.drawnAt) {
281
+ this.drawnAt = t;
282
+ this.drawn++;
283
+ }
284
+ return t;
285
+ }
286
+ resolve(id, avatar) {
287
+ return isAvatarId(avatar) ? avatar : defaultAvatarFor(id - 1);
288
+ }
289
+ };
290
+ function defaultBox(ctx, view) {
291
+ const t = typeof ctx.getTransform === "function" ? ctx.getTransform() : null;
292
+ const sx = t ? Math.hypot(t.a, t.b) || 1 : 1;
293
+ const sy = t ? Math.hypot(t.c, t.d) || 1 : 1;
294
+ const w = (ctx.canvas?.width ?? 0) / sx;
295
+ const h = (ctx.canvas?.height ?? 0) / sy;
296
+ return view ? { x: view.x * w, y: view.y * h, w: view.w * w, h: view.h * h } : { x: 0, y: 0, w, h };
297
+ }
298
+ var smoothAmount = (smooth) => smooth === false ? 1 : typeof smooth === "number" ? Math.min(Math.max(smooth, 0.05), 1) : 0.5;
299
+ function avatarDrawer(book, scene) {
300
+ return (ctx, who, player, box, options = {}) => {
301
+ const frame = Array.isArray(who) ? null : who;
302
+ const body = frame ? frame.body : who;
303
+ if (!Array.isArray(body)) return null;
304
+ const avatar = typeof player === "string" ? player : book.of(typeof player === "number" ? player : frame ?? 1);
305
+ const id = typeof player === "number" ? player : frame?.id;
306
+ const posed = id !== void 0 ? book.posed(id, body, smoothAmount(options.smooth), options.interpolate !== false) : body;
307
+ return drawAvatar(ctx, posed, avatar, box ?? defaultBox(ctx, scene().view), options.alpha !== void 0 ? { alpha: options.alpha } : {}) ? posed : null;
308
+ };
309
+ }
310
+
311
+ // src/easy.ts
312
+ function easy(connecting) {
313
+ const listeners = /* @__PURE__ */ new Map();
314
+ const emit = (event, data) => listeners.get(event)?.forEach((l) => l(data));
315
+ let play = null;
316
+ const queue = [];
317
+ const withPlay = (fn) => play ? fn(play) : queue.push(fn);
318
+ const early = new AvatarBook();
319
+ const drawEarly = avatarDrawer(early, () => ({ view: null }));
320
+ void connecting.then((p) => {
321
+ play = p;
322
+ const shapes = /* @__PURE__ */ new Map();
323
+ p.on("gesture", (g) => emit(g.gesture, { player: g.player }));
324
+ p.on("pause", (m) => emit("pause", { reason: m.reason === "distance" ? "distance" : "user" }));
325
+ p.on("resume", () => emit("resume", {}));
326
+ p.on("break-start", () => emit("break-start", {}));
327
+ p.on("break-done", (m) => emit("break-end", { shown: m.shown }));
328
+ p.onBackdrop((backdrop, view) => emit("backdrop", { backdrop, view }));
329
+ p.on("frame", (f) => {
330
+ emit("players", { players: f.players });
331
+ for (const pl of f.players) {
332
+ emit("hands", { player: pl.id, left: pl.leftHand, right: pl.rightHand });
333
+ if (shapes.get(pl.id) !== pl.shape) {
334
+ shapes.set(pl.id, pl.shape);
335
+ emit("shape", { player: pl.id, shape: pl.shape });
336
+ }
337
+ }
338
+ });
339
+ emit("start", startInfo(p));
340
+ queue.splice(0).forEach((fn) => fn(p));
341
+ });
342
+ return {
343
+ on(event, listener) {
344
+ const set = listeners.get(event) ?? /* @__PURE__ */ new Set();
345
+ listeners.set(event, set);
346
+ set.add(listener);
347
+ if (event === "start" && play) listener(startInfo(play));
348
+ return () => void set.delete(listener);
349
+ },
350
+ ready: () => withPlay((p) => p.ready()),
351
+ score: (score, player = 1) => withPlay((p) => p.score(player, score)),
352
+ break: (reason) => new Promise((resolve) => withPlay((p) => void p.break(reason).then(resolve))),
353
+ end: (score) => withPlay(
354
+ (p) => p.end(typeof score === "number" ? [{ player: 1, score, stars: 2 }] : score.map((s) => ({ player: s.player, score: s.score, stars: s.stars ?? 2 })))
355
+ ),
356
+ keys: (map, options) => withPlay((p) => void keyboard(p, map, options)),
357
+ pointer: (options) => withPlay((p) => void pointer(p, options)),
358
+ get view() {
359
+ return play?.view ?? null;
360
+ },
361
+ get device() {
362
+ return play?.device ?? null;
363
+ },
364
+ reportStats: (s) => play?.reportStats(s),
365
+ toScreen: (p) => play ? play.toScreen(p) : toScreen(null, p),
366
+ avatarOf: (player) => play ? play.avatarOf(player) : early.of(player),
367
+ drawAvatar: (...args) => play ? play.drawAvatar(...args) : drawEarly(...args),
368
+ smoothedBody: (...args) => play ? play.smoothedBody(...args) : early.smoothedBody(...args)
369
+ };
370
+ }
371
+ var startInfo = (p) => ({ ageRange: p.ageRange, players: p.players, locale: p.locale, mode: p.mode, backdrop: p.backdrop, view: p.view, device: p.device });
372
+
373
+ // src/index.ts
374
+ var windowChannel = (win = window) => ({
375
+ post: (m) => win.parent.postMessage(m, "*"),
376
+ // Only the screen that embeds the game speaks to it.
377
+ listen: (l) => win.addEventListener("message", (e) => e.source === win.parent && l(e.data))
378
+ });
379
+ var INIT_TIMEOUT_MS = 1e4;
380
+ var STATS_MS = 1e3;
381
+ function connect(channel = windowChannel()) {
382
+ return new Promise((resolve, reject) => {
383
+ const listeners = /* @__PURE__ */ new Map();
384
+ const breaks = [];
385
+ let started = false;
386
+ let backdrop = "none";
387
+ let view = null;
388
+ const backdropListeners = /* @__PURE__ */ new Set();
389
+ const avatars = new AvatarBook();
390
+ const drawAvatar2 = avatarDrawer(avatars, () => ({ view }));
391
+ const smoothedBody = avatars.smoothedBody;
392
+ const timer = setTimeout(() => reject(new Error("Fabi Play: no host, is the game opened from Fabi Play?")), INIT_TIMEOUT_MS);
393
+ channel.listen((data) => {
394
+ if (typeof data !== "object" || data === null || data.ns !== NS) return;
395
+ const m = data;
396
+ if (m.type === "init" && !started) {
397
+ if (m.v !== PROTOCOL_VERSION) {
398
+ clearTimeout(timer);
399
+ return reject(new Error(`Fabi Play: protocol ${m.v}, this SDK speaks ${PROTOCOL_VERSION}`));
400
+ }
401
+ started = true;
402
+ clearTimeout(timer);
403
+ backdrop = m.backdrop === "video" ? "video" : "none";
404
+ view = parseView(m.view) ?? null;
405
+ const device = parseDeviceHint(m.device) ?? null;
406
+ let reported = {};
407
+ if (m.stats === true) {
408
+ let since = performance.now();
409
+ const stats = setInterval(() => {
410
+ const now = performance.now();
411
+ channel.post(wire({ type: "stats", renderFps: Math.round(avatars.takeDrawn() * 1e3 / Math.max(now - since, 1)), ...reported }));
412
+ since = now;
413
+ }, STATS_MS);
414
+ stats.unref?.();
415
+ }
416
+ resolve({
417
+ locale: m.locale,
418
+ mode: m.mode,
419
+ players: m.players,
420
+ muted: m.muted,
421
+ ageRange: m.ageRange ?? "6-8",
422
+ get backdrop() {
423
+ return backdrop;
424
+ },
425
+ get view() {
426
+ return view;
427
+ },
428
+ device,
429
+ reportStats: (s) => {
430
+ reported = parseGameStats(s);
431
+ },
432
+ toScreen: (p) => toScreen(view, p),
433
+ avatarOf: (player) => avatars.of(player),
434
+ drawAvatar: drawAvatar2,
435
+ smoothedBody,
436
+ onBackdrop(listener) {
437
+ backdropListeners.add(listener);
438
+ return () => void backdropListeners.delete(listener);
439
+ },
440
+ on(event, listener) {
441
+ const set = listeners.get(event) ?? /* @__PURE__ */ new Set();
442
+ listeners.set(event, set);
443
+ set.add(listener);
444
+ return () => void set.delete(listener);
445
+ },
446
+ ready: () => channel.post(wire({ type: "ready" })),
447
+ score: (player, score) => channel.post(wire({ type: "score", player, score })),
448
+ break: (reason = "level") => new Promise((done) => {
449
+ breaks.push(done);
450
+ channel.post(wire({ type: "break", reason }));
451
+ }),
452
+ end: (scores) => channel.post(wire({ type: "end", scores }))
453
+ });
454
+ } else if (!started) return;
455
+ else {
456
+ let message = m;
457
+ if (m.type === "frame" && Array.isArray(m.players)) avatars.note(m.players, m.t);
458
+ if (m.type === "backdrop") {
459
+ if (m.backdrop !== "video" && m.backdrop !== "none") return;
460
+ const next = parseView(m.view) ?? null;
461
+ if (m.backdrop === backdrop && sameView(next, view)) return;
462
+ backdrop = m.backdrop;
463
+ view = next;
464
+ message = { type: "backdrop", backdrop, ...view ? { view } : {} };
465
+ for (const l of [...backdropListeners]) l(backdrop, view);
466
+ }
467
+ for (const l of listeners.get(m.type) ?? []) l(message);
468
+ if (m.type === "break-done") breaks.shift()?.(m.shown);
469
+ }
470
+ });
471
+ });
472
+ }
473
+ var sameView = (a, b) => a === b || a !== null && b !== null && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
474
+ export {
475
+ AvatarBook,
476
+ DEFAULT_KEYS,
477
+ avatarDrawer,
478
+ connect,
479
+ defaultBox,
480
+ domKeys,
481
+ domPointer,
482
+ easy,
483
+ keyInfo,
484
+ keyboard,
485
+ muteDuringBreaks,
486
+ parseKeys,
487
+ pointer,
488
+ smoothBody,
489
+ windowChannel
490
+ };
@@ -0,0 +1,12 @@
1
+ {
2
+ "package": "@fabi-play/sdk",
3
+ "version": "0.1.0",
4
+ "algorithm": "sha384",
5
+ "files": {
6
+ "fabi-play.js": "sha384-vwHpIsx7Fav+S6/jNTY6qYfWT1zq3p2NIq9zD4T56qXw+/ogk6jfdwJIs0/gFNy3",
7
+ "fabi-play.umd.js": "sha384-YDcTw2S3gcyQYGCn1cRcqui3DaqF1ByJ4cvS4HBP++xY7bSuLmSc6yhRH1RQ0b9G",
8
+ "fabi-play.umd.min.js": "sha384-Hhlc2ZXMa3NvLOfsHXMTXlWxSMzw8GwJgMHID4I++red4Xmg2a8n3c67alpHgPlD",
9
+ "fabi-play.d.ts": "sha384-NjFh/5P1gigt+yi1P58WplUgGh2CQUSJd77W+4puFk+r1/hDB7mkXK5tYoRKTYWB",
10
+ "fabi-play.schema.json": "sha384-tHySTbbJnqrjp7H93Y/NxsyIqU1QUHnMqCbx4N7hOzfUzgvXpDYOpzZLXgWTOHAU"
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,71 @@
1
1
  {
2
2
  "name": "@fabi-play/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "What a game imports: players, movements and the end of the game, from inside its sandboxed frame.",
5
+ "keywords": [
6
+ "fabi-play",
7
+ "sdk",
8
+ "games",
9
+ "motion",
10
+ "kids"
11
+ ],
12
+ "homepage": "https://docs.fabiplay.app",
13
+ "bugs": "https://github.com/fabiplay/fabiplay-kit/issues",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/fabiplay/fabiplay-kit.git",
17
+ "directory": "packages/sdk"
18
+ },
19
+ "license": "SEE LICENSE IN LICENSE",
20
+ "author": "Fabi Play (https://fabiplay.app)",
21
+ "type": "module",
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "sideEffects": [
28
+ "./dist/fabi-play.js",
29
+ "./dist/fabi-play.umd.js",
30
+ "./dist/fabi-play.umd.min.js"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "devDependencies": {
39
+ "@fabi-play/avatars": "^0.1.0",
40
+ "@fabi-play/protocol": "^0.1.0",
41
+ "dts-bundle-generator": "9.5.1",
42
+ "esbuild": "0.28.2",
43
+ "tsx": "4.23.15",
44
+ "typescript": "5.7.3"
45
+ },
46
+ "scripts": {
47
+ "typecheck": "tsc --noEmit",
48
+ "test": "tsx --test test/*.test.ts",
49
+ "build": "node scripts/build.mjs"
50
+ },
51
+ "module": "./dist/index.js",
52
+ "exports": {
53
+ ".": {
54
+ "types": "./dist/index.d.ts",
55
+ "import": "./dist/index.js",
56
+ "default": "./dist/index.js"
57
+ },
58
+ "./avatars": {
59
+ "types": "./dist/avatars.d.ts",
60
+ "import": "./dist/avatars.js",
61
+ "default": "./dist/avatars.js"
62
+ },
63
+ "./fabi-play.js": "./dist/fabi-play.js",
64
+ "./fabi-play.umd.js": "./dist/fabi-play.umd.js",
65
+ "./fabi-play.umd.min.js": "./dist/fabi-play.umd.min.js",
66
+ "./fabi-play.d.ts": "./dist/fabi-play.d.ts",
67
+ "./fabi-play.schema.json": "./dist/fabi-play.schema.json",
68
+ "./integrity.json": "./dist/integrity.json",
69
+ "./package.json": "./package.json"
70
+ }
6
71
  }