digital-boardgame-framework 0.4.0 → 0.6.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.
Files changed (45) hide show
  1. package/dist/client/index.d.ts +4 -0
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +2 -0
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/client/realtime.d.ts +11 -0
  6. package/dist/client/realtime.d.ts.map +1 -0
  7. package/dist/client/realtime.js +32 -0
  8. package/dist/client/realtime.js.map +1 -0
  9. package/dist/client/update-banner.d.ts +20 -0
  10. package/dist/client/update-banner.d.ts.map +1 -0
  11. package/dist/client/update-banner.js +23 -0
  12. package/dist/client/update-banner.js.map +1 -0
  13. package/dist/client/use-game.d.ts +8 -0
  14. package/dist/client/use-game.d.ts.map +1 -1
  15. package/dist/client/use-game.js +12 -2
  16. package/dist/client/use-game.js.map +1 -1
  17. package/dist/client/use-version-check.d.ts +40 -0
  18. package/dist/client/use-version-check.d.ts.map +1 -0
  19. package/dist/client/use-version-check.js +63 -0
  20. package/dist/client/use-version-check.js.map +1 -0
  21. package/dist/server/game-server.d.ts +5 -0
  22. package/dist/server/game-server.d.ts.map +1 -1
  23. package/dist/server/game-server.js +11 -0
  24. package/dist/server/game-server.js.map +1 -1
  25. package/dist/server/identity/jwt.d.ts +46 -0
  26. package/dist/server/identity/jwt.d.ts.map +1 -0
  27. package/dist/server/identity/jwt.js +134 -0
  28. package/dist/server/identity/jwt.js.map +1 -0
  29. package/dist/server/index.d.ts +4 -0
  30. package/dist/server/index.d.ts.map +1 -1
  31. package/dist/server/index.js +2 -0
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/server/realtime.d.ts +28 -0
  34. package/dist/server/realtime.d.ts.map +1 -0
  35. package/dist/server/realtime.js +35 -0
  36. package/dist/server/realtime.js.map +1 -0
  37. package/dist/vite/index.d.ts +3 -0
  38. package/dist/vite/index.d.ts.map +1 -0
  39. package/dist/vite/index.js +2 -0
  40. package/dist/vite/index.js.map +1 -0
  41. package/dist/vite/version-stamp.d.ts +26 -0
  42. package/dist/vite/version-stamp.d.ts.map +1 -0
  43. package/dist/vite/version-stamp.js +46 -0
  44. package/dist/vite/version-stamp.js.map +1 -0
  45. package/package.json +9 -1
@@ -1,3 +1,7 @@
1
1
  export { useGame } from './use-game.js';
2
2
  export type { UseGameOpts, UseGameResult, GameClientApi } from './use-game.js';
3
+ export { useVersionCheck } from './use-version-check.js';
4
+ export type { UseVersionCheckOpts, UseVersionCheckResult } from './use-version-check.js';
5
+ export { UpdateBanner } from './update-banner.js';
6
+ export type { UpdateBannerProps } from './update-banner.js';
3
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1,2 +1,4 @@
1
1
  export { useGame } from './use-game.js';
2
+ export { useVersionCheck } from './use-version-check.js';
3
+ export { UpdateBanner } from './update-banner.js';
2
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface SupabaseRealtimeOpts {
2
+ supabaseUrl: string;
3
+ /** The PUBLIC anon/publishable key (safe in the client bundle). */
4
+ anonKey: string;
5
+ gameId: string;
6
+ /** Must match the server broadcaster's topicPrefix. Default 'game:'. */
7
+ topicPrefix?: string;
8
+ }
9
+ /** Build a `subscribe` fn for `useGame({ subscribe })`. */
10
+ export declare function subscribeSupabaseRealtime(opts: SupabaseRealtimeOpts): (onChange: () => void) => () => void;
11
+ //# sourceMappingURL=realtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/client/realtime.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,2DAA2D;AAC3D,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,oBAAoB,GACzB,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAetC"}
@@ -0,0 +1,32 @@
1
+ // Client helper: a `subscribe` function for useGame backed by Supabase Realtime
2
+ // broadcast. Subscribes to the game's "moved" channel and fires onChange — which
3
+ // makes useGame re-fetch the REDACTED view through the server. The broadcast
4
+ // carries only {turn}; it never carries game state (see server/realtime.ts).
5
+ //
6
+ // Imported via the /client/realtime subpath so games that don't use realtime
7
+ // don't pull @supabase/supabase-js into their bundle.
8
+ import { createClient } from '@supabase/supabase-js';
9
+ /** Build a `subscribe` fn for `useGame({ subscribe })`. */
10
+ export function subscribeSupabaseRealtime(opts) {
11
+ return (onChange) => {
12
+ const client = createClient(opts.supabaseUrl, opts.anonKey, {
13
+ auth: { persistSession: false },
14
+ });
15
+ const topic = `${opts.topicPrefix ?? 'game:'}${opts.gameId}`;
16
+ const channel = client
17
+ .channel(topic)
18
+ .on('broadcast', { event: 'moved' }, () => onChange())
19
+ .subscribe();
20
+ return () => {
21
+ try {
22
+ channel.unsubscribe();
23
+ }
24
+ catch { /* ignore */ }
25
+ try {
26
+ client.removeAllChannels();
27
+ }
28
+ catch { /* ignore */ }
29
+ };
30
+ };
31
+ }
32
+ //# sourceMappingURL=realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../src/client/realtime.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,6EAA6E;AAC7E,6EAA6E;AAC7E,EAAE;AACF,6EAA6E;AAC7E,sDAAsD;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAWrD,2DAA2D;AAC3D,MAAM,UAAU,yBAAyB,CACvC,IAA0B;IAE1B,OAAO,CAAC,QAAQ,EAAE,EAAE;QAClB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,EAAE;YAC1D,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE;SAChC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,WAAW,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAG,MAAM;aACnB,OAAO,CAAC,KAAK,CAAC;aACd,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;aACrD,SAAS,EAAE,CAAC;QACf,OAAO,GAAG,EAAE;YACV,IAAI,CAAC;gBAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YACrD,IAAI,CAAC;gBAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC5D,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { CSSProperties } from 'react';
2
+ import { type UseVersionCheckOpts } from './use-version-check.js';
3
+ /**
4
+ * Drop-in "a new version is available — Reload" banner. Renders nothing until a
5
+ * newer build is deployed, then shows a dismissible bar. Built on
6
+ * `useVersionCheck`; pass the same options. Restyle via `className`/`style`, or
7
+ * skip this component entirely and build your own UI on the hook.
8
+ *
9
+ * <UpdateBanner currentBuild={__DBF_BUILD_ID__} />
10
+ */
11
+ export interface UpdateBannerProps extends UseVersionCheckOpts {
12
+ message?: string;
13
+ reloadLabel?: string;
14
+ /** Hide the default styling (so `className` fully controls appearance). */
15
+ unstyled?: boolean;
16
+ className?: string;
17
+ style?: CSSProperties;
18
+ }
19
+ export declare function UpdateBanner({ message, reloadLabel, unstyled, className, style, ...opts }: UpdateBannerProps): import("react/jsx-runtime").JSX.Element | null;
20
+ //# sourceMappingURL=update-banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-banner.d.ts","sourceRoot":"","sources":["../../src/client/update-banner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEnF;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAiBD,wBAAgB,YAAY,CAAC,EAC3B,OAAuC,EACvC,WAAsB,EACtB,QAAgB,EAChB,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,iBAAiB,kDAcnB"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useVersionCheck } from './use-version-check.js';
3
+ const barStyle = {
4
+ position: 'fixed', left: 0, right: 0, bottom: 0, zIndex: 9999,
5
+ display: 'flex', gap: 12, alignItems: 'center', justifyContent: 'center',
6
+ padding: '10px 14px', background: '#1d1830', color: '#e8e6f2',
7
+ borderTop: '1px solid #ffffff22', fontFamily: 'system-ui, sans-serif', fontSize: 14,
8
+ };
9
+ const reloadBtn = {
10
+ background: '#5a3380', color: 'white', border: 'none', borderRadius: 6,
11
+ padding: '6px 14px', fontSize: 14, cursor: 'pointer',
12
+ };
13
+ const closeBtn = {
14
+ background: 'transparent', color: '#aab', border: 'none', fontSize: 18,
15
+ cursor: 'pointer', lineHeight: 1,
16
+ };
17
+ export function UpdateBanner({ message = 'A new version is available.', reloadLabel = 'Reload', unstyled = false, className, style, ...opts }) {
18
+ const { updateAvailable, reload, dismiss } = useVersionCheck(opts);
19
+ if (!updateAvailable)
20
+ return null;
21
+ return (_jsxs("div", { role: "status", className: className, style: unstyled ? style : { ...barStyle, ...style }, children: [_jsx("span", { children: message }), _jsx("button", { type: "button", onClick: reload, style: unstyled ? undefined : reloadBtn, children: reloadLabel }), _jsx("button", { type: "button", onClick: dismiss, "aria-label": "Dismiss", style: unstyled ? undefined : closeBtn, children: "\u00D7" })] }));
22
+ }
23
+ //# sourceMappingURL=update-banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-banner.js","sourceRoot":"","sources":["../../src/client/update-banner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAA4B,MAAM,wBAAwB,CAAC;AAmBnF,MAAM,QAAQ,GAAkB;IAC9B,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI;IAC7D,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ;IACxE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS;IAC7D,SAAS,EAAE,qBAAqB,EAAE,UAAU,EAAE,uBAAuB,EAAE,QAAQ,EAAE,EAAE;CACpF,CAAC;AACF,MAAM,SAAS,GAAkB;IAC/B,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACtE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS;CACrD,CAAC;AACF,MAAM,QAAQ,GAAkB;IAC9B,UAAU,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;IACtE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,EAC3B,OAAO,GAAG,6BAA6B,EACvC,WAAW,GAAG,QAAQ,EACtB,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACW;IAClB,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACnE,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAClC,OAAO,CACL,eAAK,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,aAC1F,yBAAO,OAAO,GAAQ,EACtB,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,YAC3E,WAAW,GACL,EACT,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,OAAO,gBAAa,SAAS,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,uBAE1F,IACL,CACP,CAAC;AACJ,CAAC"}
@@ -29,6 +29,14 @@ export interface GameClientApi<S, A> {
29
29
  export interface UseGameOpts {
30
30
  pollMs?: number;
31
31
  pauseWhenHidden?: boolean;
32
+ /** Optional realtime push. Given a callback, set up a subscription that
33
+ * invokes it whenever the game changes (e.g. Supabase Realtime "moved"
34
+ * broadcast); return an unsubscribe fn. When provided, the hook refreshes on
35
+ * every push and polling drops to a slow self-healing fallback. Transport-
36
+ * agnostic: the framework only needs the "something changed" signal — it
37
+ * always re-fetches the redacted view through the server (push must NOT carry
38
+ * state). See `subscribeSupabaseRealtime` in /client/realtime. */
39
+ subscribe?: (onChange: () => void) => () => void;
32
40
  }
33
41
  export interface UseGameResult<S, A> {
34
42
  view: S | null;
@@ -1 +1 @@
1
- {"version":3,"file":"use-game.d.ts","sourceRoot":"","sources":["../../src/client/use-game.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1G,YAAY,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,CAAC,UAAU,EAAE;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,KAAK,GAAG,gBAAgB,GAAG,UAAU,CAAC;QACjD;qFAC6E;QAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,YAAY,EAAE,CAAC,EAAE,CAAC;IAClB,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,GAAG,gBAAgB,GAAG,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClG,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,GAAE,WAAgB,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CA2GtG"}
1
+ {"version":3,"file":"use-game.d.ts","sourceRoot":"","sources":["../../src/client/use-game.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1G,YAAY,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,CAAC,UAAU,EAAE;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,KAAK,GAAG,gBAAgB,GAAG,UAAU,CAAC;QACjD;qFAC6E;QAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;uEAMmE;IACnE,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,YAAY,EAAE,CAAC,EAAE,CAAC;IAClB,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,GAAG,gBAAgB,GAAG,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClG,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,GAAE,WAAgB,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAqHtG"}
@@ -50,7 +50,9 @@ export function useGame(client, opts = {}) {
50
50
  useEffect(() => {
51
51
  mounted.current = true;
52
52
  refresh();
53
- const ms = opts.pollMs ?? 10_000;
53
+ // With a realtime subscription, polling becomes a slow self-healing fallback
54
+ // (covers a dropped socket); push drives the fast updates.
55
+ const ms = opts.pollMs ?? (opts.subscribe ? 30_000 : 10_000);
54
56
  let handle = null;
55
57
  const start = () => {
56
58
  if (handle)
@@ -80,13 +82,21 @@ export function useGame(client, opts = {}) {
80
82
  if (pauseWhenHidden && typeof document !== 'undefined') {
81
83
  document.addEventListener('visibilitychange', onVis);
82
84
  }
85
+ // Realtime push: refresh on every "something changed" signal. Always
86
+ // re-fetch through the server (redacted) — the signal carries no state.
87
+ let unsubscribe;
88
+ if (opts.subscribe) {
89
+ unsubscribe = opts.subscribe(() => { refresh(); });
90
+ }
83
91
  return () => {
84
92
  mounted.current = false;
85
93
  stop();
86
94
  if (typeof document !== 'undefined')
87
95
  document.removeEventListener('visibilitychange', onVis);
96
+ if (unsubscribe)
97
+ unsubscribe();
88
98
  };
89
- }, [refresh, opts.pollMs, opts.pauseWhenHidden]);
99
+ }, [refresh, opts.pollMs, opts.pauseWhenHidden, opts.subscribe]);
90
100
  const submit = useCallback(async (action) => {
91
101
  setLoading(true);
92
102
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"use-game.js","sourceRoot":"","sources":["../../src/client/use-game.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-game.js","sourceRoot":"","sources":["../../src/client/use-game.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Detects when a newer build of the app has been deployed while a tab is still
3
+ * running an old one, so the app can prompt the user to reload. Pairs with the
4
+ * `versionStamp` Vite plugin (digital-boardgame-framework/vite), which injects
5
+ * the running build id and emits the `version.json` this hook polls.
6
+ *
7
+ * Why: a long-lived open tab never re-requests the page, so it keeps running
8
+ * stale JS after a deploy — users then report already-fixed bugs. This polls a
9
+ * small uncached `version.json` on tab-refocus (the high-value trigger) and on
10
+ * an interval, and flips `updateAvailable` when the deployed build differs from
11
+ * the one this tab is running.
12
+ */
13
+ export interface UseVersionCheckOpts {
14
+ /** The build id this running bundle was built with — pass the value the
15
+ * `versionStamp` plugin injected (default global `__DBF_BUILD_ID__`). */
16
+ currentBuild: string;
17
+ /** URL of the published version manifest. Default `/version.json`. */
18
+ url?: string;
19
+ /** Poll interval in ms. Default 5 min. `<= 0` disables interval polling
20
+ * (refocus checks still run). */
21
+ intervalMs?: number;
22
+ /** Also check whenever the tab becomes visible again. Default true. */
23
+ checkOnFocus?: boolean;
24
+ /** Auto-reload on Vite's `vite:preloadError` — a lazy import() hitting a
25
+ * now-404'd old chunk after a deploy. Default true. */
26
+ reloadOnChunkError?: boolean;
27
+ }
28
+ export interface UseVersionCheckResult {
29
+ /** True when a different build is live and the user hasn't dismissed it. */
30
+ updateAvailable: boolean;
31
+ currentBuild: string;
32
+ /** The latest deployed build id seen, or null before the first successful check. */
33
+ latestBuild: string | null;
34
+ /** Hard-reload the page onto the new build. */
35
+ reload: () => void;
36
+ /** Hide the prompt for the current `latestBuild` (re-appears if a newer one ships). */
37
+ dismiss: () => void;
38
+ }
39
+ export declare function useVersionCheck(opts: UseVersionCheckOpts): UseVersionCheckResult;
40
+ //# sourceMappingURL=use-version-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-version-check.d.ts","sourceRoot":"","sources":["../../src/client/use-version-check.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,mBAAmB;IAClC;8EAC0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;sCACkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;4DACwD;IACxD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,uFAAuF;IACvF,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,qBAAqB,CAiEhF"}
@@ -0,0 +1,63 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ export function useVersionCheck(opts) {
3
+ const { currentBuild, url = '/version.json', intervalMs = 5 * 60 * 1000, checkOnFocus = true, reloadOnChunkError = true, } = opts;
4
+ const [latestBuild, setLatestBuild] = useState(null);
5
+ const [dismissed, setDismissed] = useState(null);
6
+ const currentRef = useRef(currentBuild);
7
+ currentRef.current = currentBuild;
8
+ useEffect(() => {
9
+ if (typeof document === 'undefined')
10
+ return;
11
+ let cancelled = false;
12
+ async function check() {
13
+ try {
14
+ // Cache-bust the query AND request no-store so neither the browser nor
15
+ // an edge CDN serves a stale manifest.
16
+ const r = await fetch(`${url}?_=${Date.now()}`, { cache: 'no-store' });
17
+ if (!r.ok)
18
+ return;
19
+ const data = (await r.json().catch(() => null));
20
+ if (!cancelled && data && typeof data.build === 'string')
21
+ setLatestBuild(data.build);
22
+ }
23
+ catch {
24
+ // offline / transient — ignore; we'll try again next tick.
25
+ }
26
+ }
27
+ check();
28
+ let id;
29
+ if (intervalMs > 0)
30
+ id = setInterval(check, intervalMs);
31
+ const onVisible = () => { if (document.visibilityState === 'visible')
32
+ check(); };
33
+ if (checkOnFocus)
34
+ document.addEventListener('visibilitychange', onVisible);
35
+ const onPreloadError = () => { if (typeof location !== 'undefined')
36
+ location.reload(); };
37
+ if (reloadOnChunkError && typeof window !== 'undefined') {
38
+ window.addEventListener('vite:preloadError', onPreloadError);
39
+ }
40
+ return () => {
41
+ cancelled = true;
42
+ if (id)
43
+ clearInterval(id);
44
+ if (checkOnFocus)
45
+ document.removeEventListener('visibilitychange', onVisible);
46
+ if (reloadOnChunkError && typeof window !== 'undefined') {
47
+ window.removeEventListener('vite:preloadError', onPreloadError);
48
+ }
49
+ };
50
+ }, [url, intervalMs, checkOnFocus, reloadOnChunkError]);
51
+ const updateAvailable = latestBuild !== null &&
52
+ latestBuild !== currentRef.current &&
53
+ latestBuild !== dismissed;
54
+ return {
55
+ updateAvailable,
56
+ currentBuild,
57
+ latestBuild,
58
+ reload: () => { if (typeof location !== 'undefined')
59
+ location.reload(); },
60
+ dismiss: () => setDismissed(latestBuild),
61
+ };
62
+ }
63
+ //# sourceMappingURL=use-version-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-version-check.js","sourceRoot":"","sources":["../../src/client/use-version-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AA0CpD,MAAM,UAAU,eAAe,CAAC,IAAyB;IACvD,MAAM,EACJ,YAAY,EACZ,GAAG,GAAG,eAAe,EACrB,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,EAC1B,YAAY,GAAG,IAAI,EACnB,kBAAkB,GAAG,IAAI,GAC1B,GAAG,IAAI,CAAC;IAET,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACxC,UAAU,CAAC,OAAO,GAAG,YAAY,CAAC;IAElC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,OAAO;QAC5C,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,KAAK,UAAU,KAAK;YAClB,IAAI,CAAC;gBACH,uEAAuE;gBACvE,uCAAuC;gBACvC,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;gBACvE,IAAI,CAAC,CAAC,CAAC,EAAE;oBAAE,OAAO;gBAClB,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAA8B,CAAC;gBAC7E,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;oBAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvF,CAAC;YAAC,MAAM,CAAC;gBACP,2DAA2D;YAC7D,CAAC;QACH,CAAC;QAED,KAAK,EAAE,CAAC;QACR,IAAI,EAA8C,CAAC;QACnD,IAAI,UAAU,GAAG,CAAC;YAAE,EAAE,GAAG,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAExD,MAAM,SAAS,GAAG,GAAG,EAAE,GAAG,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS;YAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACjF,IAAI,YAAY;YAAE,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;QAE3E,MAAM,cAAc,GAAG,GAAG,EAAE,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACzF,IAAI,kBAAkB,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YACxD,MAAM,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;QAC/D,CAAC;QAED,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;YACjB,IAAI,EAAE;gBAAE,aAAa,CAAC,EAAE,CAAC,CAAC;YAC1B,IAAI,YAAY;gBAAE,QAAQ,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;YAC9E,IAAI,kBAAkB,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;gBACxD,MAAM,CAAC,mBAAmB,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;YAClE,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAExD,MAAM,eAAe,GACnB,WAAW,KAAK,IAAI;QACpB,WAAW,KAAK,UAAU,CAAC,OAAO;QAClC,WAAW,KAAK,SAAS,CAAC;IAE5B,OAAO;QACL,eAAe;QACf,YAAY;QACZ,WAAW;QACX,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACzE,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC;KACzC,CAAC;AACJ,CAAC"}
@@ -4,6 +4,7 @@ import type { LogEntry } from '../core/log.js';
4
4
  import type { SnapshotStore, BugReportRow, ReportFilter } from './store.js';
5
5
  import type { Notifier } from './notify.js';
6
6
  import type { ReportForwarder } from './forward.js';
7
+ import type { GameBroadcaster } from './realtime.js';
7
8
  export interface GameServerOpts<S, A, P extends string> {
8
9
  adapter: GameAdapter<S, A, P>;
9
10
  codec: Codec<S>;
@@ -12,6 +13,10 @@ export interface GameServerOpts<S, A, P extends string> {
12
13
  /** Optional sink for stored bug reports (GitHub/Slack/email/webhook).
13
14
  * Called best-effort after the report is persisted; see {@link ReportForwarder}. */
14
15
  forwarder?: ReportForwarder;
16
+ /** Optional realtime push: a tiny "game moved" ping after each move so
17
+ * waiting clients refresh instantly instead of polling. Called best-effort.
18
+ * NEVER carries state — see {@link GameBroadcaster}. */
19
+ broadcaster?: GameBroadcaster;
15
20
  gameUrl: (gameId: string, token: string) => string;
16
21
  idGen?: () => string;
17
22
  actionEquals?: (a: A, b: A) => boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"game-server.d.ts","sourceRoot":"","sources":["../../src/server/game-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"game-server.d.ts","sourceRoot":"","sources":["../../src/server/game-server.ts"],"names":[],"mappings":"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"}
@@ -91,6 +91,17 @@ export class GameServer {
91
91
  turn: nextTurn,
92
92
  state: this.encodeSnapshot(next),
93
93
  });
94
+ // Best-effort realtime ping (turn only — never state) so waiting clients
95
+ // refresh instantly. A failure must not fail the move (clients still poll).
96
+ if (this.opts.broadcaster) {
97
+ try {
98
+ await this.opts.broadcaster.gameMoved(gameId, { turn: nextTurn });
99
+ }
100
+ catch (e) {
101
+ // eslint-disable-next-line no-console
102
+ console.error('[dbf] broadcast failed:', e);
103
+ }
104
+ }
94
105
  // Best-effort notify the new actor (if different and we have an email).
95
106
  const newActor = this.opts.adapter.currentActor(next);
96
107
  if (newActor && newActor !== actor && this.opts.notifier && meta.emails?.[newActor]) {
@@ -1 +1 @@
1
- {"version":3,"file":"game-server.js","sourceRoot":"","sources":["../../src/server/game-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"game-server.js","sourceRoot":"","sources":["../../src/server/game-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export type IdentityKind = 'anon' | 'registered';
2
+ /** Claims carried in an identity token. */
3
+ export interface IdentityClaims {
4
+ /** Stable player id (JWT `sub`). */
5
+ playerId: string;
6
+ displayName: string;
7
+ kind: IdentityKind;
8
+ }
9
+ /** JsonWebKey plus the `kid` (key id) field, which the DOM lib type omits. */
10
+ export type Jwk = JsonWebKey & {
11
+ kid?: string;
12
+ };
13
+ /** A JWK key set as published at the hub's /id/jwks endpoint. */
14
+ export interface Jwks {
15
+ keys: Jwk[];
16
+ }
17
+ /** Generate a fresh signing keypair. Run ONCE; store the private JWK as a hub
18
+ * secret, publish the public JWK via /id/jwks. Returns the kid too. */
19
+ export declare function generateIdentityKeypair(): Promise<{
20
+ privateJwk: Jwk;
21
+ publicJwk: Jwk;
22
+ kid: string;
23
+ }>;
24
+ /** Public JWK → a JWKS document for /id/jwks. */
25
+ export declare function toJwks(publicJwks: Jwk[]): Jwks;
26
+ /** Sign an identity token (hub side). `nowSeconds`/`ttlSeconds` injectable for
27
+ * testing; default ttl 1h. */
28
+ export declare function signIdentityToken(privateJwk: Jwk, claims: IdentityClaims, opts?: {
29
+ issuer?: string;
30
+ audience?: string;
31
+ ttlSeconds?: number;
32
+ nowSeconds?: number;
33
+ }): Promise<string>;
34
+ export interface VerifiedIdentity extends IdentityClaims {
35
+ issuer?: string;
36
+ audience?: string;
37
+ expiresAt: number;
38
+ }
39
+ /** Verify an identity token (game side) against the hub JWKS. Returns the
40
+ * identity, or null on any failure (bad signature, expired, unknown key,
41
+ * malformed). Never throws on untrusted input. `nowSeconds` injectable. */
42
+ export declare function verifyIdentityToken(token: string, jwks: Jwks, opts?: {
43
+ nowSeconds?: number;
44
+ audience?: string;
45
+ }): Promise<VerifiedIdentity | null>;
46
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/server/identity/jwt.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,YAAY,CAAC;AAEjD,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,MAAM,GAAG,GAAG,UAAU,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD,iEAAiE;AACjE,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAsCD;wEACwE;AACxE,wBAAsB,uBAAuB,IAAI,OAAO,CAAC;IACvD,UAAU,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;CAC9C,CAAC,CAQD;AAED,iDAAiD;AACjD,wBAAgB,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAE9C;AAED;+BAC+B;AAC/B,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,GAAG,EACf,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1F,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;4EAE4E;AAC5E,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,EACV,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAyClC"}
@@ -0,0 +1,134 @@
1
+ // Identity token (JWT) core — the trust mechanism for cross-game identity.
2
+ //
3
+ // The hub signs a short-lived identity token with its private key; every game
4
+ // VERIFIES it offline against the hub's published public key (JWKS) — no
5
+ // per-request call to the hub. This is what makes "one identity across separate
6
+ // origins" cheap and stateless. See docs/identity-design.md.
7
+ //
8
+ // ES256 (ECDSA P-256 + SHA-256) via WebCrypto only — no dependencies, and it
9
+ // runs unchanged in Cloudflare Workers/Pages Functions, Node 18+, and browsers
10
+ // (the verify side ships to game Functions via this package's /server entry).
11
+ const ALG = { name: 'ECDSA', namedCurve: 'P-256' };
12
+ const SIGN_ALG = { name: 'ECDSA', hash: 'SHA-256' };
13
+ function subtle() {
14
+ const c = globalThis.crypto;
15
+ if (!c?.subtle)
16
+ throw new Error('WebCrypto unavailable (need Workers, Node 18+, or a browser)');
17
+ return c.subtle;
18
+ }
19
+ // ---- base64url ----
20
+ function b64uFromBytes(bytes) {
21
+ let bin = '';
22
+ for (const b of bytes)
23
+ bin += String.fromCharCode(b);
24
+ return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
25
+ }
26
+ function bytesFromB64u(s) {
27
+ const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/'));
28
+ const out = new Uint8Array(bin.length);
29
+ for (let i = 0; i < bin.length; i++)
30
+ out[i] = bin.charCodeAt(i);
31
+ return out;
32
+ }
33
+ function b64uFromString(s) {
34
+ return b64uFromBytes(new TextEncoder().encode(s));
35
+ }
36
+ function jsonFromB64u(s) {
37
+ return JSON.parse(new TextDecoder().decode(bytesFromB64u(s)));
38
+ }
39
+ /** Stable key id from the public JWK (so JWKS rotation works). */
40
+ async function thumbprint(publicJwk) {
41
+ // RFC 7638 thumbprint over the required EC members, lexicographically ordered.
42
+ const canon = JSON.stringify({ crv: publicJwk.crv, kty: publicJwk.kty, x: publicJwk.x, y: publicJwk.y });
43
+ const hash = await subtle().digest('SHA-256', new TextEncoder().encode(canon));
44
+ return b64uFromBytes(new Uint8Array(hash));
45
+ }
46
+ /** Generate a fresh signing keypair. Run ONCE; store the private JWK as a hub
47
+ * secret, publish the public JWK via /id/jwks. Returns the kid too. */
48
+ export async function generateIdentityKeypair() {
49
+ const pair = await subtle().generateKey(ALG, true, ['sign', 'verify']);
50
+ const privateJwk = await subtle().exportKey('jwk', pair.privateKey);
51
+ const publicJwk = await subtle().exportKey('jwk', pair.publicKey);
52
+ const kid = await thumbprint(publicJwk);
53
+ privateJwk.kid = kid;
54
+ publicJwk.kid = kid;
55
+ return { privateJwk, publicJwk, kid };
56
+ }
57
+ /** Public JWK → a JWKS document for /id/jwks. */
58
+ export function toJwks(publicJwks) {
59
+ return { keys: publicJwks.map((k) => ({ ...k, use: 'sig', alg: 'ES256' })) };
60
+ }
61
+ /** Sign an identity token (hub side). `nowSeconds`/`ttlSeconds` injectable for
62
+ * testing; default ttl 1h. */
63
+ export async function signIdentityToken(privateJwk, claims, opts = {}) {
64
+ const now = opts.nowSeconds ?? Math.floor(Date.now() / 1000);
65
+ const kid = privateJwk.kid ?? (await thumbprint(privateJwk));
66
+ const header = { alg: 'ES256', typ: 'JWT', kid };
67
+ const payload = {
68
+ sub: claims.playerId,
69
+ name: claims.displayName,
70
+ kind: claims.kind,
71
+ iat: now,
72
+ exp: now + (opts.ttlSeconds ?? 3600),
73
+ };
74
+ if (opts.issuer)
75
+ payload.iss = opts.issuer;
76
+ if (opts.audience)
77
+ payload.aud = opts.audience;
78
+ const key = await subtle().importKey('jwk', privateJwk, ALG, false, ['sign']);
79
+ const signingInput = `${b64uFromString(JSON.stringify(header))}.${b64uFromString(JSON.stringify(payload))}`;
80
+ const sig = await subtle().sign(SIGN_ALG, key, new TextEncoder().encode(signingInput));
81
+ return `${signingInput}.${b64uFromBytes(new Uint8Array(sig))}`;
82
+ }
83
+ /** Verify an identity token (game side) against the hub JWKS. Returns the
84
+ * identity, or null on any failure (bad signature, expired, unknown key,
85
+ * malformed). Never throws on untrusted input. `nowSeconds` injectable. */
86
+ export async function verifyIdentityToken(token, jwks, opts = {}) {
87
+ try {
88
+ const parts = token.split('.');
89
+ if (parts.length !== 3)
90
+ return null;
91
+ const [h, p, s] = parts;
92
+ const header = jsonFromB64u(h);
93
+ if (header.alg !== 'ES256')
94
+ return null;
95
+ const candidates = jwks.keys.filter((k) => !header.kid || k.kid === header.kid);
96
+ if (candidates.length === 0)
97
+ return null;
98
+ const data = new TextEncoder().encode(`${h}.${p}`);
99
+ const sig = bytesFromB64u(s);
100
+ let ok = false;
101
+ for (const jwk of candidates) {
102
+ try {
103
+ const key = await subtle().importKey('jwk', jwk, ALG, false, ['verify']);
104
+ if (await subtle().verify(SIGN_ALG, key, sig, data)) {
105
+ ok = true;
106
+ break;
107
+ }
108
+ }
109
+ catch { /* try next key */ }
110
+ }
111
+ if (!ok)
112
+ return null;
113
+ const payload = jsonFromB64u(p);
114
+ const now = opts.nowSeconds ?? Math.floor(Date.now() / 1000);
115
+ if (typeof payload.exp !== 'number' || payload.exp < now)
116
+ return null;
117
+ if (opts.audience && payload.aud !== opts.audience)
118
+ return null;
119
+ if (!payload.sub || (payload.kind !== 'anon' && payload.kind !== 'registered'))
120
+ return null;
121
+ return {
122
+ playerId: payload.sub,
123
+ displayName: payload.name ?? '',
124
+ kind: payload.kind,
125
+ issuer: payload.iss,
126
+ audience: payload.aud,
127
+ expiresAt: payload.exp,
128
+ };
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../src/server/identity/jwt.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,6DAA6D;AAC7D,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAoB9E,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,CAAC;AAC5D,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAW,CAAC;AAE7D,SAAS,MAAM;IACb,MAAM,CAAC,GAAI,UAAkC,CAAC,MAAM,CAAC;IACrD,IAAI,CAAC,CAAC,EAAE,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAChG,OAAO,CAAC,CAAC,MAAM,CAAC;AAClB,CAAC;AAED,sBAAsB;AACtB,SAAS,aAAa,CAAC,KAAiB;IACtC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC;AACD,SAAS,aAAa,CAAC,CAAS;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,GAAG,CAAC;AACb,CAAC;AACD,SAAS,cAAc,CAAC,CAAS;IAC/B,OAAO,aAAa,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AACD,SAAS,YAAY,CAAI,CAAS;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAM,CAAC;AACrE,CAAC;AAED,kEAAkE;AAClE,KAAK,UAAU,UAAU,CAAC,SAAc;IACtC,+EAA+E;IAC/E,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;IACzG,MAAM,IAAI,GAAG,MAAM,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAiB,CAAC,CAAC;IAC/F,OAAO,aAAa,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;wEACwE;AACxE,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAG3C,MAAM,IAAI,GAAG,MAAM,MAAM,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACvE,MAAM,UAAU,GAAQ,MAAM,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,MAAM,SAAS,GAAQ,MAAM,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC;IACrB,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC;IACpB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;AACxC,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,MAAM,CAAC,UAAiB;IACtC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED;+BAC+B;AAC/B,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAe,EACf,MAAsB,EACtB,OAAyF,EAAE;IAE3F,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACjD,MAAM,OAAO,GAA4B;QACvC,GAAG,EAAE,MAAM,CAAC,QAAQ;QACpB,IAAI,EAAE,MAAM,CAAC,WAAW;QACxB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;KACrC,CAAC;IACF,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3C,IAAI,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;IAE/C,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5G,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAiB,CAAC,CAAC;IACvG,OAAO,GAAG,YAAY,IAAI,aAAa,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;AACjE,CAAC;AAQD;;4EAE4E;AAC5E,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAa,EACb,IAAU,EACV,OAAmD,EAAE;IAErD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;QACxB,MAAM,MAAM,GAAG,YAAY,CAAiC,CAAC,CAAC,CAAC;QAC/D,IAAI,MAAM,CAAC,GAAG,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAExC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;QAChF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEzC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAiB,CAAC;QACnE,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAiB,CAAC;QAC7C,IAAI,EAAE,GAAG,KAAK,CAAC;QACf,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACzE,IAAI,MAAM,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;oBAAC,EAAE,GAAG,IAAI,CAAC;oBAAC,MAAM;gBAAC,CAAC;YAC5E,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAErB,MAAM,OAAO,GAAG,YAAY,CAEzB,CAAC,CAAC,CAAC;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC7D,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,CAAC,GAAG,GAAG,GAAG;YAAE,OAAO,IAAI,CAAC;QACtE,IAAI,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAChE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;QAE5F,OAAO;YACL,QAAQ,EAAE,OAAO,CAAC,GAAG;YACrB,WAAW,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;YAC/B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM,EAAE,OAAO,CAAC,GAAG;YACnB,QAAQ,EAAE,OAAO,CAAC,GAAG;YACrB,SAAS,EAAE,OAAO,CAAC,GAAG;SACvB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -7,6 +7,10 @@ export type { Notifier, NotifyTurnArgs } from './notify.js';
7
7
  export { NoopNotifier } from './notifiers/noop-notifier.js';
8
8
  export { ResendNotifier } from './notifiers/resend-notifier.js';
9
9
  export type { ResendNotifierOpts } from './notifiers/resend-notifier.js';
10
+ export { generateIdentityKeypair, signIdentityToken, verifyIdentityToken, toJwks, } from './identity/jwt.js';
11
+ export type { IdentityClaims, IdentityKind, VerifiedIdentity, Jwks, Jwk, } from './identity/jwt.js';
10
12
  export type { ReportForwarder } from './forward.js';
11
13
  export { NoopForwarder } from './forwarders/noop-forwarder.js';
14
+ export type { GameBroadcaster, SupabaseBroadcasterOpts } from './realtime.js';
15
+ export { NoopBroadcaster, SupabaseBroadcaster } from './realtime.js';
12
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACV,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,GAC/E,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,GACjE,MAAM,YAAY,CAAC;AAOpB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACV,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,GAC/E,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,GACjE,MAAM,YAAY,CAAC;AAOpB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,OAAO,EACL,uBAAuB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,GACxE,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,IAAI,EAAE,GAAG,GAC1D,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
@@ -8,5 +8,7 @@ export { ConflictError } from './store.js';
8
8
  export { SupabaseStore } from './stores/supabase-store.js';
9
9
  export { NoopNotifier } from './notifiers/noop-notifier.js';
10
10
  export { ResendNotifier } from './notifiers/resend-notifier.js';
11
+ export { generateIdentityKeypair, signIdentityToken, verifyIdentityToken, toJwks, } from './identity/jwt.js';
11
12
  export { NoopForwarder } from './forwarders/noop-forwarder.js';
13
+ export { NoopBroadcaster, SupabaseBroadcaster } from './realtime.js';
12
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAS9C,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,+EAA+E;AAC/E,gDAAgD;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAIhE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAS9C,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,+EAA+E;AAC/E,gDAAgD;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGhE,OAAO,EACL,uBAAuB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,GACxE,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,28 @@
1
+ /** Fan a move notification out to subscribers of a game. Best-effort: the
2
+ * caller swallows failures (clients still have polling as a fallback). */
3
+ export interface GameBroadcaster {
4
+ gameMoved(gameId: string, info: {
5
+ turn: number;
6
+ }): Promise<void>;
7
+ }
8
+ /** Default: broadcasts nothing (clients fall back to polling). */
9
+ export declare class NoopBroadcaster implements GameBroadcaster {
10
+ gameMoved(): Promise<void>;
11
+ }
12
+ export interface SupabaseBroadcasterOpts {
13
+ supabaseUrl: string;
14
+ /** service-role/secret key — server-side only. */
15
+ serviceKey: string;
16
+ /** Channel topic prefix; clients subscribe to `${prefix}${gameId}`. Default 'game:'. */
17
+ topicPrefix?: string;
18
+ }
19
+ /** Broadcasts via Supabase Realtime's server broadcast endpoint. The payload is
20
+ * only the turn number — never state. */
21
+ export declare class SupabaseBroadcaster implements GameBroadcaster {
22
+ private opts;
23
+ constructor(opts: SupabaseBroadcasterOpts);
24
+ gameMoved(gameId: string, info: {
25
+ turn: number;
26
+ }): Promise<void>;
27
+ }
28
+ //# sourceMappingURL=realtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/server/realtime.ts"],"names":[],"mappings":"AASA;2EAC2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,kEAAkE;AAClE,qBAAa,eAAgB,YAAW,eAAe;IAC/C,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;0CAC0C;AAC1C,qBAAa,mBAAoB,YAAW,eAAe;IAC7C,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,uBAAuB;IAE3C,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAavE"}
@@ -0,0 +1,35 @@
1
+ // Realtime push — the SIGNAL half of low-latency online play.
2
+ //
3
+ // SECURITY-CRITICAL: this broadcasts only a tiny "game moved → turn N" ping. It
4
+ // must NEVER carry game state. Snapshot rows hold the UNREDACTED state (hidden
5
+ // hands, deck order); the only safe way for a client to learn the new state is
6
+ // to re-fetch through the server endpoint, which applies `viewFor` redaction.
7
+ // So: server broadcasts {turn} → client re-fetches the redacted view. Do NOT
8
+ // subscribe clients to the snapshots table directly (that would leak).
9
+ /** Default: broadcasts nothing (clients fall back to polling). */
10
+ export class NoopBroadcaster {
11
+ async gameMoved() { }
12
+ }
13
+ /** Broadcasts via Supabase Realtime's server broadcast endpoint. The payload is
14
+ * only the turn number — never state. */
15
+ export class SupabaseBroadcaster {
16
+ opts;
17
+ constructor(opts) {
18
+ this.opts = opts;
19
+ }
20
+ async gameMoved(gameId, info) {
21
+ const topic = `${this.opts.topicPrefix ?? 'game:'}${gameId}`;
22
+ const res = await fetch(`${this.opts.supabaseUrl}/realtime/v1/api/broadcast`, {
23
+ method: 'POST',
24
+ headers: {
25
+ apikey: this.opts.serviceKey,
26
+ Authorization: `Bearer ${this.opts.serviceKey}`,
27
+ 'Content-Type': 'application/json',
28
+ },
29
+ body: JSON.stringify({ messages: [{ topic, event: 'moved', payload: { turn: info.turn } }] }),
30
+ });
31
+ if (!res.ok)
32
+ throw new Error(`Supabase broadcast failed: ${res.status} ${await res.text()}`);
33
+ }
34
+ }
35
+ //# sourceMappingURL=realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../src/server/realtime.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,6EAA6E;AAC7E,uEAAuE;AAQvE,kEAAkE;AAClE,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAC,SAAS,KAAgC,CAAC;CACjD;AAUD;0CAC0C;AAC1C,MAAM,OAAO,mBAAmB;IACV;IAApB,YAAoB,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAErD,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,IAAsB;QACpD,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,OAAO,GAAG,MAAM,EAAE,CAAC;QAC7D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,4BAA4B,EAAE;YAC5E,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU;gBAC5B,aAAa,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;gBAC/C,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;SAC9F,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export { versionStamp } from './version-stamp.js';
2
+ export type { VersionStampOpts, VersionStampPlugin } from './version-stamp.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { versionStamp } from './version-stamp.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,26 @@
1
+ /** Minimal structural subset of Vite's `Plugin` — avoids a `vite` dependency.
2
+ * The returned object is structurally a valid Vite plugin. */
3
+ export interface VersionStampPlugin {
4
+ name: string;
5
+ config(): {
6
+ define: Record<string, string>;
7
+ };
8
+ configResolved(resolved: {
9
+ build?: {
10
+ outDir?: string;
11
+ };
12
+ }): void;
13
+ closeBundle(): void;
14
+ }
15
+ export interface VersionStampOpts {
16
+ /** Build id to stamp. Default: `git rev-parse --short HEAD`, or `dev-<rand>`. */
17
+ buildId?: string;
18
+ /** Global name to define the build id under. Default `__DBF_BUILD_ID__`. */
19
+ define?: string;
20
+ /** Manifest filename written into the build output dir. Default `version.json`. */
21
+ fileName?: string;
22
+ /** Override the output dir (otherwise taken from Vite's resolved config). */
23
+ outDir?: string;
24
+ }
25
+ export declare function versionStamp(opts?: VersionStampOpts): VersionStampPlugin;
26
+ //# sourceMappingURL=version-stamp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-stamp.d.ts","sourceRoot":"","sources":["../../src/vite/version-stamp.ts"],"names":[],"mappings":"AAaA;+DAC+D;AAC/D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC7C,cAAc,CAAC,QAAQ,EAAE;QAAE,KAAK,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAChE,WAAW,IAAI,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAUD,wBAAgB,YAAY,CAAC,IAAI,GAAE,gBAAqB,GAAG,kBAAkB,CAwB5E"}
@@ -0,0 +1,46 @@
1
+ // Build-time half of the update-banner feature. Node-only (used in
2
+ // vite.config); never imported by app/runtime code.
3
+ //
4
+ // `versionStamp()` does two things:
5
+ // 1. injects a build id as a global define (default `__DBF_BUILD_ID__`) so the
6
+ // running bundle knows which build it is, and
7
+ // 2. writes `<outDir>/version.json` = { build, time } at the end of the build,
8
+ // which `useVersionCheck` polls to detect that a newer build is live.
9
+ import { execSync } from 'node:child_process';
10
+ import { writeFileSync, mkdirSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ function defaultBuildId() {
13
+ try {
14
+ return execSync('git rev-parse --short HEAD', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
15
+ }
16
+ catch {
17
+ return 'dev-' + Math.random().toString(36).slice(2, 9);
18
+ }
19
+ }
20
+ export function versionStamp(opts = {}) {
21
+ const buildId = opts.buildId ?? defaultBuildId();
22
+ const defineName = opts.define ?? '__DBF_BUILD_ID__';
23
+ const fileName = opts.fileName ?? 'version.json';
24
+ let outDir = opts.outDir ?? 'dist';
25
+ const builtAt = new Date().toISOString();
26
+ return {
27
+ name: 'dbf:version-stamp',
28
+ config() {
29
+ return { define: { [defineName]: JSON.stringify(buildId) } };
30
+ },
31
+ configResolved(resolved) {
32
+ if (!opts.outDir && resolved.build?.outDir)
33
+ outDir = resolved.build.outDir;
34
+ },
35
+ closeBundle() {
36
+ try {
37
+ mkdirSync(outDir, { recursive: true });
38
+ writeFileSync(join(outDir, fileName), JSON.stringify({ build: buildId, time: builtAt }));
39
+ }
40
+ catch {
41
+ // best-effort — a failed stamp shouldn't fail the build
42
+ }
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=version-stamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-stamp.js","sourceRoot":"","sources":["../../src/vite/version-stamp.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,oDAAoD;AACpD,EAAE;AACF,oCAAoC;AACpC,iFAAiF;AACjF,mDAAmD;AACnD,iFAAiF;AACjF,2EAA2E;AAE3E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAsBjC,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAyB,EAAE;IACtD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,kBAAkB,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,cAAc,CAAC;IACjD,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEzC,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,MAAM;YACJ,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAC/D,CAAC;QACD,cAAc,CAAC,QAAQ;YACrB,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAE,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7E,CAAC;QACD,WAAW;YACT,IAAI,CAAC;gBACH,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;YAC3F,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digital-boardgame-framework",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Foundation library for turn-based digital boardgames: deterministic engine plumbing, async multiplayer, agent-friendly bug triage.",
5
5
  "keywords": ["boardgame", "board-game", "multiplayer", "async-multiplayer", "turn-based", "game-framework", "supabase", "cloudflare-pages"],
6
6
  "author": "John Champaign",
@@ -27,6 +27,14 @@
27
27
  "./client": {
28
28
  "types": "./dist/client/index.d.ts",
29
29
  "import": "./dist/client/index.js"
30
+ },
31
+ "./client/realtime": {
32
+ "types": "./dist/client/realtime.d.ts",
33
+ "import": "./dist/client/realtime.js"
34
+ },
35
+ "./vite": {
36
+ "types": "./dist/vite/index.d.ts",
37
+ "import": "./dist/vite/index.js"
30
38
  }
31
39
  },
32
40
  "files": ["dist", "supabase/schema.sql"],