hyperchess-store 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,211 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-store
3
+ // File: packages/store/src/adapters/memory.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ import { GameNotFoundError } from '../types/game-record';
7
+ /**
8
+ * In-memory game store for testing and development.
9
+ *
10
+ * Backed by a plain `Map`, so all data is lost when the process exits. It is the
11
+ * package default export and the reference implementation of {@link GameStore}:
12
+ * every other adapter is expected to match its observable behaviour.
13
+ *
14
+ * Records are deep-copied with `structuredClone` on the way out (and into
15
+ * watcher callbacks) so a caller mutating a returned object cannot corrupt the
16
+ * store — a hazard the database adapters don't have because they deserialise
17
+ * fresh rows on every read.
18
+ */
19
+ export class MemoryGameStore {
20
+ constructor() {
21
+ this.games = new Map();
22
+ this.watchers = new Map();
23
+ }
24
+ /**
25
+ * Insert or replace a game, stamping `updatedAt` with the current time.
26
+ *
27
+ * The record fully replaces any existing entry rather than merging into it,
28
+ * and watchers of this id are notified synchronously before returning.
29
+ *
30
+ * @param game - Record to store; if `id` is empty a new one is generated.
31
+ * @returns The id the game was stored under.
32
+ */
33
+ async saveGame(game) {
34
+ // Generate ID if not provided
35
+ const id = game.id || this.generateId();
36
+ const recordWithId = {
37
+ ...game,
38
+ id,
39
+ updatedAt: new Date().toISOString(),
40
+ };
41
+ this.games.set(id, recordWithId);
42
+ // Notify watchers
43
+ this.notifyWatchers(id, recordWithId);
44
+ return id;
45
+ }
46
+ /**
47
+ * Fetch a game by id.
48
+ *
49
+ * @param id - Game id to look up.
50
+ * @returns A deep copy of the stored record, safe for the caller to mutate.
51
+ * @throws {@link GameNotFoundError} if no game is stored under `id`.
52
+ */
53
+ async loadGame(id) {
54
+ const game = this.games.get(id);
55
+ if (!game) {
56
+ throw new GameNotFoundError(id);
57
+ }
58
+ return structuredClone(game); // Deep copy to prevent mutations
59
+ }
60
+ /**
61
+ * Remove a game and drop every watcher registered against it.
62
+ *
63
+ * Watchers are discarded silently — they receive no final notification, so a
64
+ * subscriber must treat "no further updates" as a possible deletion.
65
+ *
66
+ * @param id - Game id to remove.
67
+ * @throws {@link GameNotFoundError} if no game is stored under `id`.
68
+ */
69
+ async deleteGame(id) {
70
+ if (!this.games.has(id)) {
71
+ throw new GameNotFoundError(id);
72
+ }
73
+ this.games.delete(id);
74
+ // Notify watchers
75
+ const watchers = this.watchers.get(id);
76
+ if (watchers) {
77
+ this.watchers.delete(id);
78
+ }
79
+ }
80
+ /**
81
+ * List games, filtering, sorting and paginating entirely in memory.
82
+ *
83
+ * Sorting is by `createdAt`, treating a missing timestamp as the epoch so
84
+ * undated records sort oldest. Note the default order is ascending here,
85
+ * whereas the SQL-backed adapters default to descending.
86
+ *
87
+ * @param options - Filters (`userId`, `result`), `sort` direction and
88
+ * `offset`/`limit` window; omitting `limit` returns everything after
89
+ * `offset`.
90
+ * @returns Deep copies of the matching records.
91
+ */
92
+ async listGames(options) {
93
+ let games = Array.from(this.games.values());
94
+ // Filter by userId if provided
95
+ if (options?.userId) {
96
+ games = games.filter((g) => g.userId === options.userId);
97
+ }
98
+ // Filter by result if provided
99
+ if (options?.result) {
100
+ games = games.filter((g) => g.result === options.result);
101
+ }
102
+ // Sort by date
103
+ const sortOrder = options?.sort === 'desc' ? -1 : 1;
104
+ games.sort((a, b) => {
105
+ const dateA = new Date(a.createdAt || 0).getTime();
106
+ const dateB = new Date(b.createdAt || 0).getTime();
107
+ return sortOrder * (dateA - dateB);
108
+ });
109
+ // Apply pagination
110
+ const offset = options?.offset || 0;
111
+ const limit = options?.limit || games.length;
112
+ games = games.slice(offset, offset + limit);
113
+ return games.map((g) => structuredClone(g));
114
+ }
115
+ /**
116
+ * Count games matching the filters.
117
+ *
118
+ * `limit` and `offset` are deliberately ignored so the result is the total
119
+ * size of the match set, not the size of one page.
120
+ *
121
+ * @param options - Only `userId` and `result` are honoured.
122
+ * @returns Number of matching records.
123
+ */
124
+ async countGames(options) {
125
+ let games = Array.from(this.games.values());
126
+ if (options?.userId) {
127
+ games = games.filter((g) => g.userId === options.userId);
128
+ }
129
+ if (options?.result) {
130
+ games = games.filter((g) => g.result === options.result);
131
+ }
132
+ return games.length;
133
+ }
134
+ /**
135
+ * Subscribe to writes for a single game.
136
+ *
137
+ * Fires only on subsequent `saveGame()` calls — there is no initial emission
138
+ * of the current value, and deletions produce no event.
139
+ *
140
+ * @param id - Game id to observe. Watching an id that does not exist yet is
141
+ * valid; the callback fires when it is first saved.
142
+ * @param callback - Receives a deep copy of the saved record.
143
+ * @returns Unsubscribe handle; the id's watcher set is dropped once empty.
144
+ */
145
+ watch(id, callback) {
146
+ if (!this.watchers.has(id)) {
147
+ this.watchers.set(id, new Set());
148
+ }
149
+ this.watchers.get(id).add(callback);
150
+ // Return unsubscribe function
151
+ return () => {
152
+ const watchers = this.watchers.get(id);
153
+ if (watchers) {
154
+ watchers.delete(callback);
155
+ if (watchers.size === 0) {
156
+ this.watchers.delete(id);
157
+ }
158
+ }
159
+ };
160
+ }
161
+ /**
162
+ * Always resolves `true` — there is no backend that can be unreachable.
163
+ */
164
+ async isHealthy() {
165
+ return true; // In-memory store is always healthy
166
+ }
167
+ /**
168
+ * Dump every stored game, unfiltered and in insertion order.
169
+ *
170
+ * @returns Deep copies of all records.
171
+ */
172
+ async exportAll() {
173
+ return Array.from(this.games.values()).map((g) => structuredClone(g));
174
+ }
175
+ /**
176
+ * Bulk-load records, overwriting any existing game with the same id.
177
+ *
178
+ * Each import goes through `saveGame()`, so `updatedAt` is rewritten to now
179
+ * and watchers fire — importing a backup is not a silent restore.
180
+ *
181
+ * @param games - Records to load.
182
+ * @returns The number of records supplied.
183
+ */
184
+ async importGames(games) {
185
+ for (const game of games) {
186
+ await this.saveGame(game);
187
+ }
188
+ return games.length;
189
+ }
190
+ /**
191
+ * Drop every game and every registered watcher.
192
+ *
193
+ * @returns How many games were removed.
194
+ */
195
+ async clear() {
196
+ const count = this.games.size;
197
+ this.games.clear();
198
+ this.watchers.clear();
199
+ return count;
200
+ }
201
+ notifyWatchers(id, game) {
202
+ const watchers = this.watchers.get(id);
203
+ if (watchers) {
204
+ watchers.forEach((callback) => callback(structuredClone(game)));
205
+ }
206
+ }
207
+ generateId() {
208
+ return `game_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
209
+ }
210
+ }
211
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,161 @@
1
+ import { GameStore } from '../types/game-store';
2
+ import { GameRecord, GameQueryOptions, Unsubscribe } from '../types/game-record';
3
+ /**
4
+ * PostgreSQL adapter for production game storage
5
+ * Requires: npm install pg
6
+ *
7
+ * Features:
8
+ * - Connection pooling
9
+ * - LISTEN/NOTIFY for real-time updates
10
+ * - Full-text search ready
11
+ * - Automatic timestamp management
12
+ *
13
+ * Schema assumption: a `games` table must already exist — unlike the SQLite
14
+ * adapter this one never runs DDL, so migrations are the deployment's job. Its
15
+ * columns are `id` (primary key), `hpgn`, `hfen`, `players`, `metadata`,
16
+ * `result`, `user_id`, `synced`, `created_at`, `updated_at`. `players` and
17
+ * `metadata` are written as JSON strings and read back with `JSON.parse`, so
18
+ * they must be `text`/`json` rather than `jsonb` (a `jsonb` column would be
19
+ * returned pre-parsed by `pg` and fail to parse again). The timestamp columns
20
+ * are read as `Date` objects and converted to ISO strings.
21
+ *
22
+ * Real-time updates rely on a database-side trigger publishing
23
+ * `NOTIFY game_updates` with a `{ id, game }` JSON payload; without that trigger
24
+ * `watch()` still fires locally for writes made through this instance.
25
+ */
26
+ export declare class PostgresGameStore implements GameStore {
27
+ private pool;
28
+ private listeners;
29
+ private client;
30
+ /**
31
+ * Open a connection pool and begin listening for `game_updates` notifications.
32
+ *
33
+ * The pool is capped at 20 connections with a 2s connect timeout and 30s idle
34
+ * timeout. One extra connection is checked out and held for the lifetime of
35
+ * the store to service `LISTEN`; call {@link PostgresGameStore.close} to give
36
+ * it back.
37
+ *
38
+ * @param connectionString - Standard `postgres://` DSN passed to `pg.Pool`.
39
+ * @throws Error if the optional `pg` package is not installed.
40
+ */
41
+ constructor(connectionString: string);
42
+ /**
43
+ * Dedicate a pooled client to `LISTEN game_updates` and fan payloads out to
44
+ * local watchers. Failures are logged rather than thrown so a database without
45
+ * the notification trigger still yields a usable store.
46
+ */
47
+ private setupNotifications;
48
+ /**
49
+ * Upsert a game via `INSERT ... ON CONFLICT (id) DO UPDATE`.
50
+ *
51
+ * `updated_at` is always set to now. `created_at` is only meaningful on the
52
+ * insert path — the conflict branch deliberately leaves it untouched so the
53
+ * original creation time survives edits. `synced` defaults to `true` here,
54
+ * the opposite of the SQLite adapter, because a successful write to the
55
+ * central database *is* the synced state.
56
+ *
57
+ * @param game - Record to persist; a missing `id` is generated locally.
58
+ * @returns The id reported back by the `RETURNING` clause.
59
+ */
60
+ saveGame(game: GameRecord): Promise<string>;
61
+ /**
62
+ * Fetch one row by primary key.
63
+ *
64
+ * @param id - Game id.
65
+ * @returns The row mapped back to camelCase {@link GameRecord} shape.
66
+ * @throws {@link GameNotFoundError} if the query returns no rows.
67
+ */
68
+ loadGame(id: string): Promise<GameRecord>;
69
+ /**
70
+ * Delete one row and forget any watchers registered for it.
71
+ *
72
+ * @param id - Game id.
73
+ * @throws {@link GameNotFoundError} if the `DELETE` affected no rows.
74
+ */
75
+ deleteGame(id: string): Promise<void>;
76
+ /**
77
+ * List games with SQL-side filtering, ordering and pagination.
78
+ *
79
+ * Filters are appended as parameterised predicates, never string-interpolated.
80
+ * Ordering is by `created_at` and defaults to descending — the reverse of the
81
+ * memory adapter's default.
82
+ *
83
+ * @param options - `userId`/`result` filters, `sort` direction, `limit` and
84
+ * `offset`. An `offset` without a `limit` is passed through to Postgres,
85
+ * which permits it.
86
+ * @returns Matching rows in the requested order.
87
+ */
88
+ listGames(options?: GameQueryOptions): Promise<GameRecord[]>;
89
+ /**
90
+ * Count matching rows with `COUNT(*)`, ignoring `limit`/`offset`.
91
+ *
92
+ * Postgres returns `count` as a bigint string, so the result is parsed to a
93
+ * number before being handed back.
94
+ *
95
+ * @param options - Only `userId` and `result` are honoured.
96
+ * @returns Total number of matching rows.
97
+ */
98
+ countGames(options?: GameQueryOptions): Promise<number>;
99
+ /**
100
+ * Subscribe to changes for one game.
101
+ *
102
+ * Two paths feed the callback: writes made through this instance notify
103
+ * directly, and writes made by other processes arrive over the `game_updates`
104
+ * `LISTEN` channel — the latter only if the database publishes them. Watching
105
+ * is purely local bookkeeping and issues no query, so it is cheap.
106
+ *
107
+ * @param id - Game id to observe.
108
+ * @param callback - Receives the updated record.
109
+ * @returns Unsubscribe handle.
110
+ */
111
+ watch(id: string, callback: (game: GameRecord) => void): Unsubscribe;
112
+ /**
113
+ * Probe the pool with `SELECT 1`.
114
+ *
115
+ * Confirms connectivity only — it does not verify that the `games` table
116
+ * exists or is readable.
117
+ *
118
+ * @returns `false` instead of throwing if the query fails.
119
+ */
120
+ isHealthy(): Promise<boolean>;
121
+ /**
122
+ * Read every row, newest first, in a single unpaginated query.
123
+ *
124
+ * Materialises the whole table in memory — fine for backups, unsuitable for
125
+ * very large datasets.
126
+ *
127
+ * @returns All stored games.
128
+ */
129
+ exportAll(): Promise<GameRecord[]>;
130
+ /**
131
+ * Upsert a batch of games one statement at a time.
132
+ *
133
+ * Not wrapped in a transaction: a row that fails is logged and skipped, so a
134
+ * partial import is a possible outcome.
135
+ *
136
+ * @param games - Records to import.
137
+ * @returns How many were saved successfully.
138
+ */
139
+ importGames(games: GameRecord[]): Promise<number>;
140
+ /**
141
+ * Delete every row in the `games` table and drop all local watchers.
142
+ *
143
+ * @returns Number of rows deleted.
144
+ */
145
+ clear(): Promise<number>;
146
+ /**
147
+ * Map a snake_case database row to a {@link GameRecord}, parsing the JSON
148
+ * columns and rendering `timestamptz` values as ISO 8601 strings.
149
+ */
150
+ private rowToRecord;
151
+ private generateId;
152
+ /**
153
+ * Release the `LISTEN` client and drain the pool.
154
+ *
155
+ * Must be called for the process to exit cleanly — the held notification
156
+ * client keeps an open socket that would otherwise pin the event loop. The
157
+ * store is unusable afterwards.
158
+ */
159
+ close(): Promise<void>;
160
+ }
161
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/adapters/postgres.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAqB,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEpG;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,SAAS,CAA2D;IAC5E,OAAO,CAAC,MAAM,CAAM;IAEpB;;;;;;;;;;OAUG;gBACS,gBAAgB,EAAE,MAAM;IAkBpC;;;;OAIG;YACW,kBAAkB;IAqBhC;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAoCjD;;;;;;OAMG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAU/C;;;;;OAKG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU3C;;;;;;;;;;;OAWG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAkClE;;;;;;;;OAQG;IACG,UAAU,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB7D;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,WAAW;IAkBpE;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IASnC;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAKxC;;;;;;;;OAQG;IACG,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAevD;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAM9B;;;OAGG;IACH,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,UAAU;IAIlB;;;;;;OAMG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B"}