@flare-engine/leaderboards 0.4.5 → 0.4.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/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/provider.d.ts
2
1
  /** Opaque leaderboard identifier — adapter-defined (e.g., "daily_high_score"). */
3
2
  type LeaderboardId = string;
4
3
  /** A single leaderboard row. */
@@ -24,20 +23,16 @@ interface LeaderboardsProvider {
24
23
  * Default no-op provider. Submissions resolve silently; queries return empty
25
24
  * arrays and `null` rank.
26
25
  */
27
- declare const NoopLeaderboardsProvider: LeaderboardsProvider;
28
- //#endregion
29
- //#region src/context.d.ts
26
+ export declare const NoopLeaderboardsProvider: LeaderboardsProvider;
30
27
  /**
31
28
  * React context carrying the active `LeaderboardsProvider`. Defaults to
32
29
  * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare
33
30
  * tree — consumers wrap their app in a `<LeaderboardsContext.Provider>` with
34
31
  * a real adapter when they need one.
35
32
  */
36
- declare const LeaderboardsContext: import("react").Context<LeaderboardsProvider>;
33
+ export declare const LeaderboardsContext: import("react").Context<LeaderboardsProvider>;
37
34
  /** Hook returning the current `LeaderboardsProvider`. */
38
- declare function useLeaderboards(): LeaderboardsProvider;
39
- //#endregion
40
- //#region src/memory.d.ts
35
+ export declare function useLeaderboards(): LeaderboardsProvider;
41
36
  /** A competitor row used to pre-populate an in-memory board (see {@link MemoryLeaderboardsOptions.seed}). */
42
37
  interface SeedScore {
43
38
  playerId: string;
@@ -75,7 +70,6 @@ interface MemoryLeaderboardsOptions {
75
70
  * - `getPlayerRank(id)` returns the local player's 1-based rank under the same
76
71
  * ordering, or `null` when the local player has no entry on that board.
77
72
  */
78
- declare function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider;
79
- //#endregion
80
- export { type LeaderboardId, LeaderboardsContext, type LeaderboardsProvider, type MemoryLeaderboardsOptions, NoopLeaderboardsProvider, type ScoreEntry, type SeedScore, createMemoryLeaderboards, useLeaderboards };
73
+ export declare function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider;
74
+ export type { LeaderboardId, LeaderboardsProvider, MemoryLeaderboardsOptions, ScoreEntry, SeedScore };
81
75
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"mappings":";;KACY;;UAGK;EACf;EACA;EACA;EACA;EACA,OAAO;;;;;;;;UASQ;EACf,YAAY,IAAI,eAAe,eAAe,OAAO,0BAA0B;EAC/E,QAAQ,IAAI,eAAe,YAAY,iBAAiB;EACxD,cAAc,IAAI,gBAAgB;;;;;;cAOvB,0BAA0B;;;;;;;;;cCnB1B,qCAAmB,QAAA;;iBAGhB,mBAAmB;;;;UCTlB;EACf;EACA;EACA;EACA,OAAO;;;UAIQ;;EAEf;;EAEA;;EAEA,OAAO,SAAS,OAAO,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;iBA+CjC,yBAAyB,OAAO,4BAA4B"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"mappings":";KACY;;UAGK;EACf;EACA;EACA;EACA;EACA,OAAO;;;;;;;;UASQ;EACf,YAAY,IAAI,eAAe,eAAe,OAAO,0BAA0B;EAC/E,QAAQ,IAAI,eAAe,YAAY,iBAAiB;EACxD,cAAc,IAAI,gBAAgB;;;;;;qBAOvB,0BAA0B;;;;;;;qBCnB1B,qCAAmB,QAAA;;wBAGhB,mBAAmB;;UCTlB;EACf;EACA;EACA;EACA,OAAO;;;UAIQ;;EAEf;;EAEA;;EAEA,OAAO,SAAS,OAAO,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;wBA+CjC,yBAAyB,OAAO,4BAA4B"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/provider.d.ts
2
1
  /** Opaque leaderboard identifier — adapter-defined (e.g., "daily_high_score"). */
3
2
  type LeaderboardId = string;
4
3
  /** A single leaderboard row. */
@@ -24,20 +23,16 @@ interface LeaderboardsProvider {
24
23
  * Default no-op provider. Submissions resolve silently; queries return empty
25
24
  * arrays and `null` rank.
26
25
  */
27
- declare const NoopLeaderboardsProvider: LeaderboardsProvider;
28
- //#endregion
29
- //#region src/context.d.ts
26
+ export declare const NoopLeaderboardsProvider: LeaderboardsProvider;
30
27
  /**
31
28
  * React context carrying the active `LeaderboardsProvider`. Defaults to
32
29
  * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare
33
30
  * tree — consumers wrap their app in a `<LeaderboardsContext.Provider>` with
34
31
  * a real adapter when they need one.
35
32
  */
36
- declare const LeaderboardsContext: import("react").Context<LeaderboardsProvider>;
33
+ export declare const LeaderboardsContext: import("react").Context<LeaderboardsProvider>;
37
34
  /** Hook returning the current `LeaderboardsProvider`. */
38
- declare function useLeaderboards(): LeaderboardsProvider;
39
- //#endregion
40
- //#region src/memory.d.ts
35
+ export declare function useLeaderboards(): LeaderboardsProvider;
41
36
  /** A competitor row used to pre-populate an in-memory board (see {@link MemoryLeaderboardsOptions.seed}). */
42
37
  interface SeedScore {
43
38
  playerId: string;
@@ -75,7 +70,6 @@ interface MemoryLeaderboardsOptions {
75
70
  * - `getPlayerRank(id)` returns the local player's 1-based rank under the same
76
71
  * ordering, or `null` when the local player has no entry on that board.
77
72
  */
78
- declare function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider;
79
- //#endregion
80
- export { type LeaderboardId, LeaderboardsContext, type LeaderboardsProvider, type MemoryLeaderboardsOptions, NoopLeaderboardsProvider, type ScoreEntry, type SeedScore, createMemoryLeaderboards, useLeaderboards };
73
+ export declare function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider;
74
+ export type { LeaderboardId, LeaderboardsProvider, MemoryLeaderboardsOptions, ScoreEntry, SeedScore };
81
75
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"mappings":";;KACY;;UAGK;EACf;EACA;EACA;EACA;EACA,OAAO;;;;;;;;UASQ;EACf,YAAY,IAAI,eAAe,eAAe,OAAO,0BAA0B;EAC/E,QAAQ,IAAI,eAAe,YAAY,iBAAiB;EACxD,cAAc,IAAI,gBAAgB;;;;;;cAOvB,0BAA0B;;;;;;;;;cCnB1B,qCAAmB,QAAA;;iBAGhB,mBAAmB;;;;UCTlB;EACf;EACA;EACA;EACA,OAAO;;;UAIQ;;EAEf;;EAEA;;EAEA,OAAO,SAAS,OAAO,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;iBA+CjC,yBAAyB,OAAO,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"mappings":";KACY;;UAGK;EACf;EACA;EACA;EACA;EACA,OAAO;;;;;;;;UASQ;EACf,YAAY,IAAI,eAAe,eAAe,OAAO,0BAA0B;EAC/E,QAAQ,IAAI,eAAe,YAAY,iBAAiB;EACxD,cAAc,IAAI,gBAAgB;;;;;;qBAOvB,0BAA0B;;;;;;;qBCnB1B,qCAAmB,QAAA;;wBAGhB,mBAAmB;;UCTlB;EACf;EACA;EACA;EACA,OAAO;;;UAIQ;;EAEf;;EAEA;;EAEA,OAAO,SAAS,OAAO,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;wBA+CjC,yBAAyB,OAAO,4BAA4B"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let react = require("react");
3
- //#region src/provider.ts
3
+ // src/provider.ts
4
4
  /**
5
5
  * Default no-op provider. Submissions resolve silently; queries return empty
6
6
  * arrays and `null` rank.
@@ -14,8 +14,7 @@ const NoopLeaderboardsProvider = {
14
14
  return null;
15
15
  }
16
16
  };
17
- //#endregion
18
- //#region src/context.ts
17
+ // src/context.ts
19
18
  /**
20
19
  * React context carrying the active `LeaderboardsProvider`. Defaults to
21
20
  * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare
@@ -27,8 +26,7 @@ const LeaderboardsContext = (0, react.createContext)(NoopLeaderboardsProvider);
27
26
  function useLeaderboards() {
28
27
  return (0, react.useContext)(LeaderboardsContext);
29
28
  }
30
- //#endregion
31
- //#region src/memory.ts
29
+ // src/memory.ts
32
30
  /** Sort a board DESCENDING by score (ties by insertion order) and assign sequential 1-based ranks. */
33
31
  function ranked(entries) {
34
32
  return entries.slice().sort((a, b) => b.score - a.score || a.order - b.order).map((e, i) => ({
@@ -109,7 +107,6 @@ function createMemoryLeaderboards(opts) {
109
107
  }
110
108
  };
111
109
  }
112
- //#endregion
113
110
  exports.LeaderboardsContext = LeaderboardsContext;
114
111
  exports.NoopLeaderboardsProvider = NoopLeaderboardsProvider;
115
112
  exports.createMemoryLeaderboards = createMemoryLeaderboards;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"sourcesContent":["/** Opaque leaderboard identifier — adapter-defined (e.g., \"daily_high_score\"). */\nexport type LeaderboardId = string;\n\n/** A single leaderboard row. */\nexport interface ScoreEntry {\n playerId: string;\n displayName: string;\n score: number;\n rank: number;\n meta?: Record<string, unknown>;\n}\n\n/**\n * Leaderboard provider interface. Consumers supply a concrete adapter (Game\n * Center, Google Play Games, Steam, custom backend) at the app boundary.\n * 0.1.0 ships the interface + a no-op default so downstream packages can\n * depend on it without forcing a platform choice.\n */\nexport interface LeaderboardsProvider {\n submitScore(id: LeaderboardId, score: number, meta?: Record<string, unknown>): Promise<void>;\n getTopN(id: LeaderboardId, n: number): Promise<readonly ScoreEntry[]>;\n getPlayerRank(id: LeaderboardId): Promise<number | null>;\n}\n\n/**\n * Default no-op provider. Submissions resolve silently; queries return empty\n * arrays and `null` rank.\n */\nexport const NoopLeaderboardsProvider: LeaderboardsProvider = {\n async submitScore(): Promise<void> {\n // no-op\n },\n async getTopN(): Promise<readonly ScoreEntry[]> {\n return [];\n },\n async getPlayerRank(): Promise<number | null> {\n return null;\n },\n};\n","import { createContext, useContext } from 'react';\nimport { type LeaderboardsProvider, NoopLeaderboardsProvider } from './provider.js';\n\n/**\n * React context carrying the active `LeaderboardsProvider`. Defaults to\n * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare\n * tree — consumers wrap their app in a `<LeaderboardsContext.Provider>` with\n * a real adapter when they need one.\n */\nexport const LeaderboardsContext = createContext<LeaderboardsProvider>(NoopLeaderboardsProvider);\n\n/** Hook returning the current `LeaderboardsProvider`. */\nexport function useLeaderboards(): LeaderboardsProvider {\n return useContext(LeaderboardsContext);\n}\n","import type { LeaderboardId, LeaderboardsProvider, ScoreEntry } from './provider.js';\n\n/** A competitor row used to pre-populate an in-memory board (see {@link MemoryLeaderboardsOptions.seed}). */\nexport interface SeedScore {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n}\n\n/** Options for {@link createMemoryLeaderboards}. */\nexport interface MemoryLeaderboardsOptions {\n /** Identity all local submissions record under. Defaults to `'local'`. */\n playerId?: string;\n /** Display name paired with `playerId`. Defaults to `'Player'`. */\n displayName?: string;\n /** Per-board competitor rows, pre-populated so queries are meaningful without a backend. */\n seed?: Readonly<Record<LeaderboardId, readonly SeedScore[]>>;\n}\n\ninterface InternalEntry {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n /** Monotonic insertion order; earlier submissions break score ties in their favour. */\n order: number;\n}\n\n/** Sort a board DESCENDING by score (ties by insertion order) and assign sequential 1-based ranks. */\nfunction ranked(entries: readonly InternalEntry[]): ScoreEntry[] {\n return entries\n .slice()\n .sort((a, b) => b.score - a.score || a.order - b.order)\n .map((e, i) => ({\n playerId: e.playerId,\n displayName: e.displayName,\n score: e.score,\n rank: i + 1,\n meta: e.meta,\n }));\n}\n\n/**\n * Create an in-memory {@link LeaderboardsProvider} — a backend-free adapter for\n * local development, tests, and single-player games.\n *\n * Semantics (the adapter-author contract):\n * - The provider interface's `submitScore(id, score, meta?)` carries no\n * playerId, so every submission records under one local identity:\n * `opts.playerId` (default `'local'`) / `opts.displayName` (default\n * `'Player'`).\n * - Boards are per-id in-memory lists. `opts.seed` pre-populates competitor\n * entries so `getTopN` / `getPlayerRank` are meaningful without a backend.\n * - `submitScore` keeps the local player's BEST (max) score per board: the\n * first submission inserts an entry; a later HIGHER score replaces both the\n * score AND the meta; a lower-or-equal score is ignored. Each entry carries a\n * monotonic insertion counter for stable tie-breaking.\n * - `getTopN(id, n)` ranks the board DESCENDING by score, ties broken by\n * insertion order (earlier submission ranks higher). Ranks are sequential\n * 1-based with no sharing. `n <= 0` and unknown boards return `[]`.\n * - `getPlayerRank(id)` returns the local player's 1-based rank under the same\n * ordering, or `null` when the local player has no entry on that board.\n */\nexport function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider {\n const localId = opts?.playerId ?? 'local';\n const localName = opts?.displayName ?? 'Player';\n const boards = new Map<LeaderboardId, InternalEntry[]>();\n let nextOrder = 0;\n\n if (opts?.seed !== undefined) {\n for (const [id, rows] of Object.entries(opts.seed)) {\n const board: InternalEntry[] = rows.map((row) => ({\n playerId: row.playerId,\n displayName: row.displayName,\n score: row.score,\n meta: row.meta,\n order: nextOrder++,\n }));\n boards.set(id, board);\n }\n }\n\n return {\n async submitScore(id, score, meta): Promise<void> {\n let board = boards.get(id);\n if (board === undefined) {\n board = [];\n boards.set(id, board);\n }\n const existing = board.find((e) => e.playerId === localId);\n if (existing === undefined) {\n board.push({\n playerId: localId,\n displayName: localName,\n score,\n meta,\n order: nextOrder++,\n });\n } else if (score > existing.score) {\n existing.score = score;\n existing.meta = meta;\n }\n },\n\n async getTopN(id, n): Promise<readonly ScoreEntry[]> {\n if (n <= 0) {\n return [];\n }\n const board = boards.get(id);\n if (board === undefined) {\n return [];\n }\n return ranked(board).slice(0, n);\n },\n\n async getPlayerRank(id): Promise<number | null> {\n const board = boards.get(id);\n if (board === undefined) {\n return null;\n }\n const found = ranked(board).find((e) => e.playerId === localId);\n return found === undefined ? null : found.rank;\n },\n };\n}\n"],"mappings":";;;;;;;AA4BA,MAAa,2BAAiD;CAC5D,MAAM,cAA6B,CAEnC;CACA,MAAM,UAA0C;EAC9C,OAAO,CAAC;CACV;CACA,MAAM,gBAAwC;EAC5C,OAAO;CACT;AACF;;;;;;;;;AC7BA,MAAa,uBAAA,GAAA,MAAA,cAAA,CAA0D,wBAAwB;;AAG/F,SAAgB,kBAAwC;CACtD,QAAA,GAAA,MAAA,WAAA,CAAkB,mBAAmB;AACvC;;;;ACgBA,SAAS,OAAO,SAAiD;CAC/D,OAAO,QACJ,MAAM,CAAC,CACP,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CACtD,KAAK,GAAG,OAAO;EACd,UAAU,EAAE;EACZ,aAAa,EAAE;EACf,OAAO,EAAE;EACT,MAAM,IAAI;EACV,MAAM,EAAE;CACV,EAAE;AACN;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,yBAAyB,MAAwD;CAC/F,MAAM,UAAU,MAAM,YAAY;CAClC,MAAM,YAAY,MAAM,eAAe;CACvC,MAAM,yBAAS,IAAI,IAAoC;CACvD,IAAI,YAAY;CAEhB,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,KAAK,IAAI,GAAG;EAClD,MAAM,QAAyB,KAAK,KAAK,SAAS;GAChD,UAAU,IAAI;GACd,aAAa,IAAI;GACjB,OAAO,IAAI;GACX,MAAM,IAAI;GACV,OAAO;EACT,EAAE;EACF,OAAO,IAAI,IAAI,KAAK;CACtB;CAGF,OAAO;EACL,MAAM,YAAY,IAAI,OAAO,MAAqB;GAChD,IAAI,QAAQ,OAAO,IAAI,EAAE;GACzB,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,CAAC;IACT,OAAO,IAAI,IAAI,KAAK;GACtB;GACA,MAAM,WAAW,MAAM,MAAM,MAAM,EAAE,aAAa,OAAO;GACzD,IAAI,aAAa,KAAA,GACf,MAAM,KAAK;IACT,UAAU;IACV,aAAa;IACb;IACA;IACA,OAAO;GACT,CAAC;QACI,IAAI,QAAQ,SAAS,OAAO;IACjC,SAAS,QAAQ;IACjB,SAAS,OAAO;GAClB;EACF;EAEA,MAAM,QAAQ,IAAI,GAAmC;GACnD,IAAI,KAAK,GACP,OAAO,CAAC;GAEV,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO,CAAC;GAEV,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;EACjC;EAEA,MAAM,cAAc,IAA4B;GAC9C,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO;GAET,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,MAAM,EAAE,aAAa,OAAO;GAC9D,OAAO,UAAU,KAAA,IAAY,OAAO,MAAM;EAC5C;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["createContext","useContext"],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"sourcesContent":["/** Opaque leaderboard identifier — adapter-defined (e.g., \"daily_high_score\"). */\nexport type LeaderboardId = string;\n\n/** A single leaderboard row. */\nexport interface ScoreEntry {\n playerId: string;\n displayName: string;\n score: number;\n rank: number;\n meta?: Record<string, unknown>;\n}\n\n/**\n * Leaderboard provider interface. Consumers supply a concrete adapter (Game\n * Center, Google Play Games, Steam, custom backend) at the app boundary.\n * 0.1.0 ships the interface + a no-op default so downstream packages can\n * depend on it without forcing a platform choice.\n */\nexport interface LeaderboardsProvider {\n submitScore(id: LeaderboardId, score: number, meta?: Record<string, unknown>): Promise<void>;\n getTopN(id: LeaderboardId, n: number): Promise<readonly ScoreEntry[]>;\n getPlayerRank(id: LeaderboardId): Promise<number | null>;\n}\n\n/**\n * Default no-op provider. Submissions resolve silently; queries return empty\n * arrays and `null` rank.\n */\nexport const NoopLeaderboardsProvider: LeaderboardsProvider = {\n async submitScore(): Promise<void> {\n // no-op\n },\n async getTopN(): Promise<readonly ScoreEntry[]> {\n return [];\n },\n async getPlayerRank(): Promise<number | null> {\n return null;\n },\n};\n","import { createContext, useContext } from 'react';\nimport { type LeaderboardsProvider, NoopLeaderboardsProvider } from './provider.js';\n\n/**\n * React context carrying the active `LeaderboardsProvider`. Defaults to\n * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare\n * tree — consumers wrap their app in a `<LeaderboardsContext.Provider>` with\n * a real adapter when they need one.\n */\nexport const LeaderboardsContext = createContext<LeaderboardsProvider>(NoopLeaderboardsProvider);\n\n/** Hook returning the current `LeaderboardsProvider`. */\nexport function useLeaderboards(): LeaderboardsProvider {\n return useContext(LeaderboardsContext);\n}\n","import type { LeaderboardId, LeaderboardsProvider, ScoreEntry } from './provider.js';\n\n/** A competitor row used to pre-populate an in-memory board (see {@link MemoryLeaderboardsOptions.seed}). */\nexport interface SeedScore {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n}\n\n/** Options for {@link createMemoryLeaderboards}. */\nexport interface MemoryLeaderboardsOptions {\n /** Identity all local submissions record under. Defaults to `'local'`. */\n playerId?: string;\n /** Display name paired with `playerId`. Defaults to `'Player'`. */\n displayName?: string;\n /** Per-board competitor rows, pre-populated so queries are meaningful without a backend. */\n seed?: Readonly<Record<LeaderboardId, readonly SeedScore[]>>;\n}\n\ninterface InternalEntry {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n /** Monotonic insertion order; earlier submissions break score ties in their favour. */\n order: number;\n}\n\n/** Sort a board DESCENDING by score (ties by insertion order) and assign sequential 1-based ranks. */\nfunction ranked(entries: readonly InternalEntry[]): ScoreEntry[] {\n return entries\n .slice()\n .sort((a, b) => b.score - a.score || a.order - b.order)\n .map((e, i) => ({\n playerId: e.playerId,\n displayName: e.displayName,\n score: e.score,\n rank: i + 1,\n meta: e.meta,\n }));\n}\n\n/**\n * Create an in-memory {@link LeaderboardsProvider} — a backend-free adapter for\n * local development, tests, and single-player games.\n *\n * Semantics (the adapter-author contract):\n * - The provider interface's `submitScore(id, score, meta?)` carries no\n * playerId, so every submission records under one local identity:\n * `opts.playerId` (default `'local'`) / `opts.displayName` (default\n * `'Player'`).\n * - Boards are per-id in-memory lists. `opts.seed` pre-populates competitor\n * entries so `getTopN` / `getPlayerRank` are meaningful without a backend.\n * - `submitScore` keeps the local player's BEST (max) score per board: the\n * first submission inserts an entry; a later HIGHER score replaces both the\n * score AND the meta; a lower-or-equal score is ignored. Each entry carries a\n * monotonic insertion counter for stable tie-breaking.\n * - `getTopN(id, n)` ranks the board DESCENDING by score, ties broken by\n * insertion order (earlier submission ranks higher). Ranks are sequential\n * 1-based with no sharing. `n <= 0` and unknown boards return `[]`.\n * - `getPlayerRank(id)` returns the local player's 1-based rank under the same\n * ordering, or `null` when the local player has no entry on that board.\n */\nexport function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider {\n const localId = opts?.playerId ?? 'local';\n const localName = opts?.displayName ?? 'Player';\n const boards = new Map<LeaderboardId, InternalEntry[]>();\n let nextOrder = 0;\n\n if (opts?.seed !== undefined) {\n for (const [id, rows] of Object.entries(opts.seed)) {\n const board: InternalEntry[] = rows.map((row) => ({\n playerId: row.playerId,\n displayName: row.displayName,\n score: row.score,\n meta: row.meta,\n order: nextOrder++,\n }));\n boards.set(id, board);\n }\n }\n\n return {\n async submitScore(id, score, meta): Promise<void> {\n let board = boards.get(id);\n if (board === undefined) {\n board = [];\n boards.set(id, board);\n }\n const existing = board.find((e) => e.playerId === localId);\n if (existing === undefined) {\n board.push({\n playerId: localId,\n displayName: localName,\n score,\n meta,\n order: nextOrder++,\n });\n } else if (score > existing.score) {\n existing.score = score;\n existing.meta = meta;\n }\n },\n\n async getTopN(id, n): Promise<readonly ScoreEntry[]> {\n if (n <= 0) {\n return [];\n }\n const board = boards.get(id);\n if (board === undefined) {\n return [];\n }\n return ranked(board).slice(0, n);\n },\n\n async getPlayerRank(id): Promise<number | null> {\n const board = boards.get(id);\n if (board === undefined) {\n return null;\n }\n const found = ranked(board).find((e) => e.playerId === localId);\n return found === undefined ? null : found.rank;\n },\n };\n}\n"],"mappings":";;;;;;;AA4BA,MAAa,2BAAiD;CAC5D,MAAM,cAA6B,CAEnC;CACA,MAAM,UAA0C;EAC9C,OAAO,CAAC;CACV;CACA,MAAM,gBAAwC;EAC5C,OAAO;CACT;AACF;;;;;;;;AC7BA,MAAa,uBAAA,GAAsBA,MAAAA,cAAAA,CAAoC,wBAAwB;;AAG/F,SAAgB,kBAAwC;CACtD,QAAA,GAAOC,MAAAA,WAAAA,CAAW,mBAAmB;AACvC;;;ACgBA,SAAS,OAAO,SAAiD;CAC/D,OAAO,QACJ,MAAM,CAAC,CACP,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CACtD,KAAK,GAAG,OAAO;EACd,UAAU,EAAE;EACZ,aAAa,EAAE;EACf,OAAO,EAAE;EACT,MAAM,IAAI;EACV,MAAM,EAAE;CACV,EAAE;AACN;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,yBAAyB,MAAwD;CAC/F,MAAM,UAAU,MAAM,YAAY;CAClC,MAAM,YAAY,MAAM,eAAe;CACvC,MAAM,yBAAS,IAAI,IAAoC;CACvD,IAAI,YAAY;CAEhB,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,KAAK,IAAI,GAAG;EAClD,MAAM,QAAyB,KAAK,KAAK,SAAS;GAChD,UAAU,IAAI;GACd,aAAa,IAAI;GACjB,OAAO,IAAI;GACX,MAAM,IAAI;GACV,OAAO;EACT,EAAE;EACF,OAAO,IAAI,IAAI,KAAK;CACtB;CAGF,OAAO;EACL,MAAM,YAAY,IAAI,OAAO,MAAqB;GAChD,IAAI,QAAQ,OAAO,IAAI,EAAE;GACzB,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,CAAC;IACT,OAAO,IAAI,IAAI,KAAK;GACtB;GACA,MAAM,WAAW,MAAM,MAAM,MAAM,EAAE,aAAa,OAAO;GACzD,IAAI,aAAa,KAAA,GACf,MAAM,KAAK;IACT,UAAU;IACV,aAAa;IACb;IACA;IACA,OAAO;GACT,CAAC;QACI,IAAI,QAAQ,SAAS,OAAO;IACjC,SAAS,QAAQ;IACjB,SAAS,OAAO;GAClB;EACF;EAEA,MAAM,QAAQ,IAAI,GAAmC;GACnD,IAAI,KAAK,GACP,OAAO,CAAC;GAEV,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO,CAAC;GAEV,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;EACjC;EAEA,MAAM,cAAc,IAA4B;GAC9C,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO;GAET,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,MAAM,EAAE,aAAa,OAAO;GAC9D,OAAO,UAAU,KAAA,IAAY,OAAO,MAAM;EAC5C;CACF;AACF"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createContext, useContext } from "react";
2
- //#region src/provider.ts
2
+ // src/provider.ts
3
3
  /**
4
4
  * Default no-op provider. Submissions resolve silently; queries return empty
5
5
  * arrays and `null` rank.
@@ -13,8 +13,7 @@ const NoopLeaderboardsProvider = {
13
13
  return null;
14
14
  }
15
15
  };
16
- //#endregion
17
- //#region src/context.ts
16
+ // src/context.ts
18
17
  /**
19
18
  * React context carrying the active `LeaderboardsProvider`. Defaults to
20
19
  * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare
@@ -26,8 +25,7 @@ const LeaderboardsContext = createContext(NoopLeaderboardsProvider);
26
25
  function useLeaderboards() {
27
26
  return useContext(LeaderboardsContext);
28
27
  }
29
- //#endregion
30
- //#region src/memory.ts
28
+ // src/memory.ts
31
29
  /** Sort a board DESCENDING by score (ties by insertion order) and assign sequential 1-based ranks. */
32
30
  function ranked(entries) {
33
31
  return entries.slice().sort((a, b) => b.score - a.score || a.order - b.order).map((e, i) => ({
@@ -108,7 +106,6 @@ function createMemoryLeaderboards(opts) {
108
106
  }
109
107
  };
110
108
  }
111
- //#endregion
112
109
  export { LeaderboardsContext, NoopLeaderboardsProvider, createMemoryLeaderboards, useLeaderboards };
113
110
 
114
111
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"sourcesContent":["/** Opaque leaderboard identifier — adapter-defined (e.g., \"daily_high_score\"). */\nexport type LeaderboardId = string;\n\n/** A single leaderboard row. */\nexport interface ScoreEntry {\n playerId: string;\n displayName: string;\n score: number;\n rank: number;\n meta?: Record<string, unknown>;\n}\n\n/**\n * Leaderboard provider interface. Consumers supply a concrete adapter (Game\n * Center, Google Play Games, Steam, custom backend) at the app boundary.\n * 0.1.0 ships the interface + a no-op default so downstream packages can\n * depend on it without forcing a platform choice.\n */\nexport interface LeaderboardsProvider {\n submitScore(id: LeaderboardId, score: number, meta?: Record<string, unknown>): Promise<void>;\n getTopN(id: LeaderboardId, n: number): Promise<readonly ScoreEntry[]>;\n getPlayerRank(id: LeaderboardId): Promise<number | null>;\n}\n\n/**\n * Default no-op provider. Submissions resolve silently; queries return empty\n * arrays and `null` rank.\n */\nexport const NoopLeaderboardsProvider: LeaderboardsProvider = {\n async submitScore(): Promise<void> {\n // no-op\n },\n async getTopN(): Promise<readonly ScoreEntry[]> {\n return [];\n },\n async getPlayerRank(): Promise<number | null> {\n return null;\n },\n};\n","import { createContext, useContext } from 'react';\nimport { type LeaderboardsProvider, NoopLeaderboardsProvider } from './provider.js';\n\n/**\n * React context carrying the active `LeaderboardsProvider`. Defaults to\n * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare\n * tree — consumers wrap their app in a `<LeaderboardsContext.Provider>` with\n * a real adapter when they need one.\n */\nexport const LeaderboardsContext = createContext<LeaderboardsProvider>(NoopLeaderboardsProvider);\n\n/** Hook returning the current `LeaderboardsProvider`. */\nexport function useLeaderboards(): LeaderboardsProvider {\n return useContext(LeaderboardsContext);\n}\n","import type { LeaderboardId, LeaderboardsProvider, ScoreEntry } from './provider.js';\n\n/** A competitor row used to pre-populate an in-memory board (see {@link MemoryLeaderboardsOptions.seed}). */\nexport interface SeedScore {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n}\n\n/** Options for {@link createMemoryLeaderboards}. */\nexport interface MemoryLeaderboardsOptions {\n /** Identity all local submissions record under. Defaults to `'local'`. */\n playerId?: string;\n /** Display name paired with `playerId`. Defaults to `'Player'`. */\n displayName?: string;\n /** Per-board competitor rows, pre-populated so queries are meaningful without a backend. */\n seed?: Readonly<Record<LeaderboardId, readonly SeedScore[]>>;\n}\n\ninterface InternalEntry {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n /** Monotonic insertion order; earlier submissions break score ties in their favour. */\n order: number;\n}\n\n/** Sort a board DESCENDING by score (ties by insertion order) and assign sequential 1-based ranks. */\nfunction ranked(entries: readonly InternalEntry[]): ScoreEntry[] {\n return entries\n .slice()\n .sort((a, b) => b.score - a.score || a.order - b.order)\n .map((e, i) => ({\n playerId: e.playerId,\n displayName: e.displayName,\n score: e.score,\n rank: i + 1,\n meta: e.meta,\n }));\n}\n\n/**\n * Create an in-memory {@link LeaderboardsProvider} — a backend-free adapter for\n * local development, tests, and single-player games.\n *\n * Semantics (the adapter-author contract):\n * - The provider interface's `submitScore(id, score, meta?)` carries no\n * playerId, so every submission records under one local identity:\n * `opts.playerId` (default `'local'`) / `opts.displayName` (default\n * `'Player'`).\n * - Boards are per-id in-memory lists. `opts.seed` pre-populates competitor\n * entries so `getTopN` / `getPlayerRank` are meaningful without a backend.\n * - `submitScore` keeps the local player's BEST (max) score per board: the\n * first submission inserts an entry; a later HIGHER score replaces both the\n * score AND the meta; a lower-or-equal score is ignored. Each entry carries a\n * monotonic insertion counter for stable tie-breaking.\n * - `getTopN(id, n)` ranks the board DESCENDING by score, ties broken by\n * insertion order (earlier submission ranks higher). Ranks are sequential\n * 1-based with no sharing. `n <= 0` and unknown boards return `[]`.\n * - `getPlayerRank(id)` returns the local player's 1-based rank under the same\n * ordering, or `null` when the local player has no entry on that board.\n */\nexport function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider {\n const localId = opts?.playerId ?? 'local';\n const localName = opts?.displayName ?? 'Player';\n const boards = new Map<LeaderboardId, InternalEntry[]>();\n let nextOrder = 0;\n\n if (opts?.seed !== undefined) {\n for (const [id, rows] of Object.entries(opts.seed)) {\n const board: InternalEntry[] = rows.map((row) => ({\n playerId: row.playerId,\n displayName: row.displayName,\n score: row.score,\n meta: row.meta,\n order: nextOrder++,\n }));\n boards.set(id, board);\n }\n }\n\n return {\n async submitScore(id, score, meta): Promise<void> {\n let board = boards.get(id);\n if (board === undefined) {\n board = [];\n boards.set(id, board);\n }\n const existing = board.find((e) => e.playerId === localId);\n if (existing === undefined) {\n board.push({\n playerId: localId,\n displayName: localName,\n score,\n meta,\n order: nextOrder++,\n });\n } else if (score > existing.score) {\n existing.score = score;\n existing.meta = meta;\n }\n },\n\n async getTopN(id, n): Promise<readonly ScoreEntry[]> {\n if (n <= 0) {\n return [];\n }\n const board = boards.get(id);\n if (board === undefined) {\n return [];\n }\n return ranked(board).slice(0, n);\n },\n\n async getPlayerRank(id): Promise<number | null> {\n const board = boards.get(id);\n if (board === undefined) {\n return null;\n }\n const found = ranked(board).find((e) => e.playerId === localId);\n return found === undefined ? null : found.rank;\n },\n };\n}\n"],"mappings":";;;;;;AA4BA,MAAa,2BAAiD;CAC5D,MAAM,cAA6B,CAEnC;CACA,MAAM,UAA0C;EAC9C,OAAO,CAAC;CACV;CACA,MAAM,gBAAwC;EAC5C,OAAO;CACT;AACF;;;;;;;;;AC7BA,MAAa,sBAAsB,cAAoC,wBAAwB;;AAG/F,SAAgB,kBAAwC;CACtD,OAAO,WAAW,mBAAmB;AACvC;;;;ACgBA,SAAS,OAAO,SAAiD;CAC/D,OAAO,QACJ,MAAM,CAAC,CACP,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CACtD,KAAK,GAAG,OAAO;EACd,UAAU,EAAE;EACZ,aAAa,EAAE;EACf,OAAO,EAAE;EACT,MAAM,IAAI;EACV,MAAM,EAAE;CACV,EAAE;AACN;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,yBAAyB,MAAwD;CAC/F,MAAM,UAAU,MAAM,YAAY;CAClC,MAAM,YAAY,MAAM,eAAe;CACvC,MAAM,yBAAS,IAAI,IAAoC;CACvD,IAAI,YAAY;CAEhB,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,KAAK,IAAI,GAAG;EAClD,MAAM,QAAyB,KAAK,KAAK,SAAS;GAChD,UAAU,IAAI;GACd,aAAa,IAAI;GACjB,OAAO,IAAI;GACX,MAAM,IAAI;GACV,OAAO;EACT,EAAE;EACF,OAAO,IAAI,IAAI,KAAK;CACtB;CAGF,OAAO;EACL,MAAM,YAAY,IAAI,OAAO,MAAqB;GAChD,IAAI,QAAQ,OAAO,IAAI,EAAE;GACzB,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,CAAC;IACT,OAAO,IAAI,IAAI,KAAK;GACtB;GACA,MAAM,WAAW,MAAM,MAAM,MAAM,EAAE,aAAa,OAAO;GACzD,IAAI,aAAa,KAAA,GACf,MAAM,KAAK;IACT,UAAU;IACV,aAAa;IACb;IACA;IACA,OAAO;GACT,CAAC;QACI,IAAI,QAAQ,SAAS,OAAO;IACjC,SAAS,QAAQ;IACjB,SAAS,OAAO;GAClB;EACF;EAEA,MAAM,QAAQ,IAAI,GAAmC;GACnD,IAAI,KAAK,GACP,OAAO,CAAC;GAEV,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO,CAAC;GAEV,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;EACjC;EAEA,MAAM,cAAc,IAA4B;GAC9C,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO;GAET,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,MAAM,EAAE,aAAa,OAAO;GAC9D,OAAO,UAAU,KAAA,IAAY,OAAO,MAAM;EAC5C;CACF;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/provider.ts","../src/context.ts","../src/memory.ts"],"sourcesContent":["/** Opaque leaderboard identifier — adapter-defined (e.g., \"daily_high_score\"). */\nexport type LeaderboardId = string;\n\n/** A single leaderboard row. */\nexport interface ScoreEntry {\n playerId: string;\n displayName: string;\n score: number;\n rank: number;\n meta?: Record<string, unknown>;\n}\n\n/**\n * Leaderboard provider interface. Consumers supply a concrete adapter (Game\n * Center, Google Play Games, Steam, custom backend) at the app boundary.\n * 0.1.0 ships the interface + a no-op default so downstream packages can\n * depend on it without forcing a platform choice.\n */\nexport interface LeaderboardsProvider {\n submitScore(id: LeaderboardId, score: number, meta?: Record<string, unknown>): Promise<void>;\n getTopN(id: LeaderboardId, n: number): Promise<readonly ScoreEntry[]>;\n getPlayerRank(id: LeaderboardId): Promise<number | null>;\n}\n\n/**\n * Default no-op provider. Submissions resolve silently; queries return empty\n * arrays and `null` rank.\n */\nexport const NoopLeaderboardsProvider: LeaderboardsProvider = {\n async submitScore(): Promise<void> {\n // no-op\n },\n async getTopN(): Promise<readonly ScoreEntry[]> {\n return [];\n },\n async getPlayerRank(): Promise<number | null> {\n return null;\n },\n};\n","import { createContext, useContext } from 'react';\nimport { type LeaderboardsProvider, NoopLeaderboardsProvider } from './provider.js';\n\n/**\n * React context carrying the active `LeaderboardsProvider`. Defaults to\n * `NoopLeaderboardsProvider` so `useLeaderboards()` never throws on a bare\n * tree — consumers wrap their app in a `<LeaderboardsContext.Provider>` with\n * a real adapter when they need one.\n */\nexport const LeaderboardsContext = createContext<LeaderboardsProvider>(NoopLeaderboardsProvider);\n\n/** Hook returning the current `LeaderboardsProvider`. */\nexport function useLeaderboards(): LeaderboardsProvider {\n return useContext(LeaderboardsContext);\n}\n","import type { LeaderboardId, LeaderboardsProvider, ScoreEntry } from './provider.js';\n\n/** A competitor row used to pre-populate an in-memory board (see {@link MemoryLeaderboardsOptions.seed}). */\nexport interface SeedScore {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n}\n\n/** Options for {@link createMemoryLeaderboards}. */\nexport interface MemoryLeaderboardsOptions {\n /** Identity all local submissions record under. Defaults to `'local'`. */\n playerId?: string;\n /** Display name paired with `playerId`. Defaults to `'Player'`. */\n displayName?: string;\n /** Per-board competitor rows, pre-populated so queries are meaningful without a backend. */\n seed?: Readonly<Record<LeaderboardId, readonly SeedScore[]>>;\n}\n\ninterface InternalEntry {\n playerId: string;\n displayName: string;\n score: number;\n meta?: Record<string, unknown>;\n /** Monotonic insertion order; earlier submissions break score ties in their favour. */\n order: number;\n}\n\n/** Sort a board DESCENDING by score (ties by insertion order) and assign sequential 1-based ranks. */\nfunction ranked(entries: readonly InternalEntry[]): ScoreEntry[] {\n return entries\n .slice()\n .sort((a, b) => b.score - a.score || a.order - b.order)\n .map((e, i) => ({\n playerId: e.playerId,\n displayName: e.displayName,\n score: e.score,\n rank: i + 1,\n meta: e.meta,\n }));\n}\n\n/**\n * Create an in-memory {@link LeaderboardsProvider} — a backend-free adapter for\n * local development, tests, and single-player games.\n *\n * Semantics (the adapter-author contract):\n * - The provider interface's `submitScore(id, score, meta?)` carries no\n * playerId, so every submission records under one local identity:\n * `opts.playerId` (default `'local'`) / `opts.displayName` (default\n * `'Player'`).\n * - Boards are per-id in-memory lists. `opts.seed` pre-populates competitor\n * entries so `getTopN` / `getPlayerRank` are meaningful without a backend.\n * - `submitScore` keeps the local player's BEST (max) score per board: the\n * first submission inserts an entry; a later HIGHER score replaces both the\n * score AND the meta; a lower-or-equal score is ignored. Each entry carries a\n * monotonic insertion counter for stable tie-breaking.\n * - `getTopN(id, n)` ranks the board DESCENDING by score, ties broken by\n * insertion order (earlier submission ranks higher). Ranks are sequential\n * 1-based with no sharing. `n <= 0` and unknown boards return `[]`.\n * - `getPlayerRank(id)` returns the local player's 1-based rank under the same\n * ordering, or `null` when the local player has no entry on that board.\n */\nexport function createMemoryLeaderboards(opts?: MemoryLeaderboardsOptions): LeaderboardsProvider {\n const localId = opts?.playerId ?? 'local';\n const localName = opts?.displayName ?? 'Player';\n const boards = new Map<LeaderboardId, InternalEntry[]>();\n let nextOrder = 0;\n\n if (opts?.seed !== undefined) {\n for (const [id, rows] of Object.entries(opts.seed)) {\n const board: InternalEntry[] = rows.map((row) => ({\n playerId: row.playerId,\n displayName: row.displayName,\n score: row.score,\n meta: row.meta,\n order: nextOrder++,\n }));\n boards.set(id, board);\n }\n }\n\n return {\n async submitScore(id, score, meta): Promise<void> {\n let board = boards.get(id);\n if (board === undefined) {\n board = [];\n boards.set(id, board);\n }\n const existing = board.find((e) => e.playerId === localId);\n if (existing === undefined) {\n board.push({\n playerId: localId,\n displayName: localName,\n score,\n meta,\n order: nextOrder++,\n });\n } else if (score > existing.score) {\n existing.score = score;\n existing.meta = meta;\n }\n },\n\n async getTopN(id, n): Promise<readonly ScoreEntry[]> {\n if (n <= 0) {\n return [];\n }\n const board = boards.get(id);\n if (board === undefined) {\n return [];\n }\n return ranked(board).slice(0, n);\n },\n\n async getPlayerRank(id): Promise<number | null> {\n const board = boards.get(id);\n if (board === undefined) {\n return null;\n }\n const found = ranked(board).find((e) => e.playerId === localId);\n return found === undefined ? null : found.rank;\n },\n };\n}\n"],"mappings":";;;;;;AA4BA,MAAa,2BAAiD;CAC5D,MAAM,cAA6B,CAEnC;CACA,MAAM,UAA0C;EAC9C,OAAO,CAAC;CACV;CACA,MAAM,gBAAwC;EAC5C,OAAO;CACT;AACF;;;;;;;;AC7BA,MAAa,sBAAsB,cAAoC,wBAAwB;;AAG/F,SAAgB,kBAAwC;CACtD,OAAO,WAAW,mBAAmB;AACvC;;;ACgBA,SAAS,OAAO,SAAiD;CAC/D,OAAO,QACJ,MAAM,CAAC,CACP,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CACtD,KAAK,GAAG,OAAO;EACd,UAAU,EAAE;EACZ,aAAa,EAAE;EACf,OAAO,EAAE;EACT,MAAM,IAAI;EACV,MAAM,EAAE;CACV,EAAE;AACN;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,yBAAyB,MAAwD;CAC/F,MAAM,UAAU,MAAM,YAAY;CAClC,MAAM,YAAY,MAAM,eAAe;CACvC,MAAM,yBAAS,IAAI,IAAoC;CACvD,IAAI,YAAY;CAEhB,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,KAAK,IAAI,GAAG;EAClD,MAAM,QAAyB,KAAK,KAAK,SAAS;GAChD,UAAU,IAAI;GACd,aAAa,IAAI;GACjB,OAAO,IAAI;GACX,MAAM,IAAI;GACV,OAAO;EACT,EAAE;EACF,OAAO,IAAI,IAAI,KAAK;CACtB;CAGF,OAAO;EACL,MAAM,YAAY,IAAI,OAAO,MAAqB;GAChD,IAAI,QAAQ,OAAO,IAAI,EAAE;GACzB,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,CAAC;IACT,OAAO,IAAI,IAAI,KAAK;GACtB;GACA,MAAM,WAAW,MAAM,MAAM,MAAM,EAAE,aAAa,OAAO;GACzD,IAAI,aAAa,KAAA,GACf,MAAM,KAAK;IACT,UAAU;IACV,aAAa;IACb;IACA;IACA,OAAO;GACT,CAAC;QACI,IAAI,QAAQ,SAAS,OAAO;IACjC,SAAS,QAAQ;IACjB,SAAS,OAAO;GAClB;EACF;EAEA,MAAM,QAAQ,IAAI,GAAmC;GACnD,IAAI,KAAK,GACP,OAAO,CAAC;GAEV,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO,CAAC;GAEV,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;EACjC;EAEA,MAAM,cAAc,IAA4B;GAC9C,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,KAAA,GACZ,OAAO;GAET,MAAM,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,MAAM,EAAE,aAAa,OAAO;GAC9D,OAAO,UAAU,KAAA,IAAY,OAAO,MAAM;EAC5C;CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flare-engine/leaderboards",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Leaderboard provider interface (consumer-supplied adapter)",
5
5
  "keywords": ["flare-engine", "leaderboards", "provider", "interface"],
6
6
  "license": "Apache-2.0",
@@ -37,7 +37,7 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "devDependencies": {
40
- "@types/react": "^19.0.0"
40
+ "@types/react": "^19.3.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react": ">=19.0.0"