@chaos-overlords/storage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/NOTICE +21 -0
- package/README.md +35 -0
- package/dist/node/index.d.ts +29 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +60 -0
- package/dist/node/index.js.map +1 -0
- package/dist/postgres/index.d.ts +3 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +3 -0
- package/dist/postgres/index.js.map +1 -0
- package/dist/postgres/repositories.d.ts +11 -0
- package/dist/postgres/repositories.d.ts.map +1 -0
- package/dist/postgres/repositories.js +396 -0
- package/dist/postgres/repositories.js.map +1 -0
- package/dist/postgres/schema.d.ts +1002 -0
- package/dist/postgres/schema.d.ts.map +1 -0
- package/dist/postgres/schema.js +103 -0
- package/dist/postgres/schema.js.map +1 -0
- package/dist/shared/constraints.d.ts +6 -0
- package/dist/shared/constraints.d.ts.map +1 -0
- package/dist/shared/constraints.js +49 -0
- package/dist/shared/constraints.js.map +1 -0
- package/dist/shared/mappers.d.ts +105 -0
- package/dist/shared/mappers.d.ts.map +1 -0
- package/dist/shared/mappers.js +55 -0
- package/dist/shared/mappers.js.map +1 -0
- package/dist/sqlite/index.d.ts +3 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +3 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/repositories.d.ts +11 -0
- package/dist/sqlite/repositories.d.ts.map +1 -0
- package/dist/sqlite/repositories.js +392 -0
- package/dist/sqlite/repositories.js.map +1 -0
- package/dist/sqlite/schema.d.ts +1063 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +103 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/migrations/postgres/0000_glorious_zaladane.sql +94 -0
- package/migrations/postgres/meta/0000_snapshot.json +637 -0
- package/migrations/postgres/meta/_journal.json +13 -0
- package/migrations/sqlite/0000_exotic_warbird.sql +94 -0
- package/migrations/sqlite/meta/0000_snapshot.json +605 -0
- package/migrations/sqlite/meta/_journal.json +13 -0
- package/package.json +94 -0
package/NOTICE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Chaos Overlords: New Chrome
|
|
2
|
+
A clean-room MonoGame reimplementation of the 1996 turn-based strategy game.
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2026 kibertoad
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
7
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
8
|
+
Foundation, version 3 of the License.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
12
|
+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU General Public License along with
|
|
15
|
+
this program in the LICENSE file. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
The GPL covers the original code in this repository. It does not cover or grant
|
|
18
|
+
rights to the original Chaos Overlords assets. Those assets are not distributed
|
|
19
|
+
by this project and must be imported locally from a legally owned copy.
|
|
20
|
+
|
|
21
|
+
Project contact: https://github.com/kibertoad/chaos-overlords-new-chrome/issues
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @chaos-overlords/storage
|
|
2
|
+
|
|
3
|
+
Storage adapters for the [*Chaos Overlords: New Chrome*](https://github.com/kibertoad/chaos-overlords-new-chrome) multiplayer server.
|
|
4
|
+
|
|
5
|
+
Drizzle schemas and repository implementations of the kernel's storage ports. One SQLite
|
|
6
|
+
dialect serves both better-sqlite3 and Cloudflare D1 from a single migration lineage; Postgres has
|
|
7
|
+
its own.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { createSqliteStorage } from '@chaos-overlords/storage/sqlite'
|
|
11
|
+
import { createPostgresStorage } from '@chaos-overlords/storage/postgres'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The migration lineages ship in the package, under `migrations/sqlite` and `migrations/postgres`.
|
|
15
|
+
Point `wrangler d1 migrations apply` or the Node migrator at them rather than copying them.
|
|
16
|
+
|
|
17
|
+
`better-sqlite3` and `pg` are optional peers: install the one your dialect needs.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install @chaos-overlords/storage
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires Node.js 22 or newer.
|
|
26
|
+
|
|
27
|
+
## Documentation
|
|
28
|
+
|
|
29
|
+
The operator and contributor manual for the whole multiplayer workspace is in
|
|
30
|
+
[`multiplayer/README.md`](https://github.com/kibertoad/chaos-overlords-new-chrome/blob/main/multiplayer/README.md); the design notes are in
|
|
31
|
+
[`docs/MULTIPLAYER.md`](https://github.com/kibertoad/chaos-overlords-new-chrome/blob/main/docs/MULTIPLAYER.md).
|
|
32
|
+
|
|
33
|
+
## Licence
|
|
34
|
+
|
|
35
|
+
GPL-3.0-only. See [`LICENSE`](https://github.com/kibertoad/chaos-overlords-new-chrome/blob/main/LICENSE) and [`NOTICE`](https://github.com/kibertoad/chaos-overlords-new-chrome/blob/main/NOTICE).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MultiplayerStorage } from '@chaos-overlords/kernel';
|
|
2
|
+
/** Migration folders, resolved from the package so a runtime never guesses a relative path. */
|
|
3
|
+
export declare const SQLITE_MIGRATIONS_DIR: string;
|
|
4
|
+
export declare const POSTGRES_MIGRATIONS_DIR: string;
|
|
5
|
+
export interface OpenedStorage {
|
|
6
|
+
storage: MultiplayerStorage;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Opens a SQLite file (or `:memory:`), applies pending migrations, and returns the storage ports.
|
|
11
|
+
* WAL mode keeps a self-hosted server responsive while the event stream reads during writes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function openSqliteStorage(filename: string): OpenedStorage;
|
|
14
|
+
/** Opens a Postgres pool, applies pending migrations, and returns the storage ports. */
|
|
15
|
+
export declare function openPostgresStorage(connectionString: string): Promise<OpenedStorage>;
|
|
16
|
+
export type StorageTarget = {
|
|
17
|
+
kind: 'sqlite';
|
|
18
|
+
filename: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'postgres';
|
|
21
|
+
connectionString: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* `DATABASE_URL` grammar: `postgres://…` / `postgresql://…` for Postgres, `sqlite:<path>` or
|
|
25
|
+
* `sqlite::memory:` for SQLite. Anything else is refused rather than guessed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseStorageTarget(databaseUrl: string): StorageTarget;
|
|
28
|
+
export declare function openStorage(target: StorageTarget): Promise<OpenedStorage>;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAYjE,+FAA+F;AAC/F,eAAO,MAAM,qBAAqB,QAEjC,CAAA;AACD,eAAO,MAAM,uBAAuB,QAEnC,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,kBAAkB,CAAA;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAYjE;AAED,wFAAwF;AACxF,wBAAsB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAQ1F;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAA;AAElD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAUrE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAIzE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import BetterSqlite3 from 'better-sqlite3';
|
|
3
|
+
import { drizzle as drizzleSqlite } from 'drizzle-orm/better-sqlite3';
|
|
4
|
+
import { migrate as migrateSqlite } from 'drizzle-orm/better-sqlite3/migrator';
|
|
5
|
+
import { drizzle as drizzlePostgres } from 'drizzle-orm/node-postgres';
|
|
6
|
+
import { migrate as migratePostgres } from 'drizzle-orm/node-postgres/migrator';
|
|
7
|
+
import pg from 'pg';
|
|
8
|
+
import { createPostgresStorage } from '../postgres/repositories';
|
|
9
|
+
import * as postgresSchema from '../postgres/schema';
|
|
10
|
+
import { createSqliteStorage } from '../sqlite/repositories';
|
|
11
|
+
import * as sqliteSchema from '../sqlite/schema';
|
|
12
|
+
/** Migration folders, resolved from the package so a runtime never guesses a relative path. */
|
|
13
|
+
export const SQLITE_MIGRATIONS_DIR = fileURLToPath(new URL('../../migrations/sqlite', import.meta.url));
|
|
14
|
+
export const POSTGRES_MIGRATIONS_DIR = fileURLToPath(new URL('../../migrations/postgres', import.meta.url));
|
|
15
|
+
/**
|
|
16
|
+
* Opens a SQLite file (or `:memory:`), applies pending migrations, and returns the storage ports.
|
|
17
|
+
* WAL mode keeps a self-hosted server responsive while the event stream reads during writes.
|
|
18
|
+
*/
|
|
19
|
+
export function openSqliteStorage(filename) {
|
|
20
|
+
const connection = new BetterSqlite3(filename);
|
|
21
|
+
connection.pragma('journal_mode = WAL');
|
|
22
|
+
connection.pragma('foreign_keys = ON');
|
|
23
|
+
const db = drizzleSqlite(connection, { schema: sqliteSchema });
|
|
24
|
+
migrateSqlite(db, { migrationsFolder: SQLITE_MIGRATIONS_DIR });
|
|
25
|
+
return {
|
|
26
|
+
storage: createSqliteStorage(db),
|
|
27
|
+
close: async () => {
|
|
28
|
+
connection.close();
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Opens a Postgres pool, applies pending migrations, and returns the storage ports. */
|
|
33
|
+
export async function openPostgresStorage(connectionString) {
|
|
34
|
+
const pool = new pg.Pool({ connectionString });
|
|
35
|
+
const db = drizzlePostgres(pool, { schema: postgresSchema });
|
|
36
|
+
await migratePostgres(db, { migrationsFolder: POSTGRES_MIGRATIONS_DIR });
|
|
37
|
+
return {
|
|
38
|
+
storage: createPostgresStorage(db),
|
|
39
|
+
close: () => pool.end(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `DATABASE_URL` grammar: `postgres://…` / `postgresql://…` for Postgres, `sqlite:<path>` or
|
|
44
|
+
* `sqlite::memory:` for SQLite. Anything else is refused rather than guessed.
|
|
45
|
+
*/
|
|
46
|
+
export function parseStorageTarget(databaseUrl) {
|
|
47
|
+
if (/^postgres(ql)?:\/\//.test(databaseUrl)) {
|
|
48
|
+
return { kind: 'postgres', connectionString: databaseUrl };
|
|
49
|
+
}
|
|
50
|
+
if (databaseUrl.startsWith('sqlite:')) {
|
|
51
|
+
return { kind: 'sqlite', filename: databaseUrl.slice('sqlite:'.length) || ':memory:' };
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`Unsupported DATABASE_URL "${databaseUrl}": expected postgres://… or sqlite:<path>`);
|
|
54
|
+
}
|
|
55
|
+
export function openStorage(target) {
|
|
56
|
+
return target.kind === 'postgres'
|
|
57
|
+
? openPostgresStorage(target.connectionString)
|
|
58
|
+
: Promise.resolve(openSqliteStorage(target.filename));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAC7E,OAAO,KAAK,cAAc,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAyB,MAAM,gBAAgB,CAAA;AAC7E,OAAO,KAAK,cAAc,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MultiplayerStorage } from '@chaos-overlords/kernel';
|
|
2
|
+
import type { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
|
|
3
|
+
import * as schema from './schema';
|
|
4
|
+
/**
|
|
5
|
+
* The Postgres twin of `sqlite/repositories.ts`: the same statements over the pg-core schema.
|
|
6
|
+
* Any driver Drizzle wraps as a `PgDatabase` works (node-postgres in the Node runtime). The two
|
|
7
|
+
* files are kept in step by the conformance suite, not by sharing code across dialect types.
|
|
8
|
+
*/
|
|
9
|
+
export type PostgresDatabase = PgDatabase<PgQueryResultHKT, typeof schema>;
|
|
10
|
+
export declare function createPostgresStorage(db: PostgresDatabase): MultiplayerStorage;
|
|
11
|
+
//# sourceMappingURL=repositories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../src/postgres/repositories.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,kBAAkB,EAKnB,MAAM,yBAAyB,CAAA;AAgBhC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAavE,OAAO,KAAK,MAAM,MAAM,UAAU,CAAA;AAElC;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,EAAE,OAAO,MAAM,CAAC,CAAA;AAE1E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,gBAAgB,GAAG,kBAAkB,CAQ9E"}
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { and, asc, desc, eq, exists, inArray, isNotNull, isNull, lt, lte, ne, or, sql, } from 'drizzle-orm';
|
|
2
|
+
import { APPEND_ATTEMPTS, insertUnlessTaken, isUniqueViolation } from '../shared/constraints';
|
|
3
|
+
import { firstOrNull, toEvent, toMatch, toMatchInsert, toPlayer, toSnapshot, toTurn, toTurnOrders, toTurnReport, } from '../shared/mappers';
|
|
4
|
+
import * as schema from './schema';
|
|
5
|
+
export function createPostgresStorage(db) {
|
|
6
|
+
return {
|
|
7
|
+
matches: postgresMatchRepository(db),
|
|
8
|
+
players: postgresPlayerRepository(db),
|
|
9
|
+
turns: postgresTurnRepository(db),
|
|
10
|
+
snapshots: postgresSnapshotRepository(db),
|
|
11
|
+
events: postgresEventRepository(db),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function postgresMatchRepository(db) {
|
|
15
|
+
const { matches, players } = schema;
|
|
16
|
+
return {
|
|
17
|
+
async create(match) {
|
|
18
|
+
return insertUnlessTaken(() => db.insert(matches).values(toMatchInsert(match)));
|
|
19
|
+
},
|
|
20
|
+
async get(id) {
|
|
21
|
+
return firstOrNull((await db.select().from(matches).where(eq(matches.id, id))).map(toMatch));
|
|
22
|
+
},
|
|
23
|
+
async getByJoinCode(joinCode) {
|
|
24
|
+
return firstOrNull((await db.select().from(matches).where(eq(matches.joinCode, joinCode))).map(toMatch));
|
|
25
|
+
},
|
|
26
|
+
async listPublicLobbies(limit) {
|
|
27
|
+
const rows = await db
|
|
28
|
+
.select({
|
|
29
|
+
id: matches.id,
|
|
30
|
+
name: matches.name,
|
|
31
|
+
hostDisplayName: players.displayName,
|
|
32
|
+
playerCount: matches.seatCount,
|
|
33
|
+
maxPlayers: matches.maxPlayers,
|
|
34
|
+
passwordHash: matches.passwordHash,
|
|
35
|
+
createdAt: matches.createdAt,
|
|
36
|
+
})
|
|
37
|
+
.from(matches)
|
|
38
|
+
.innerJoin(players, eq(players.id, matches.hostPlayerId))
|
|
39
|
+
.where(and(eq(matches.status, 'lobby'), eq(matches.visibility, 'public')))
|
|
40
|
+
.orderBy(desc(matches.createdAt), asc(matches.id))
|
|
41
|
+
.limit(limit);
|
|
42
|
+
return rows.map(({ passwordHash, createdAt, ...rest }) => ({
|
|
43
|
+
...rest,
|
|
44
|
+
passwordProtected: passwordHash !== null,
|
|
45
|
+
createdAt: createdAt.toISOString(),
|
|
46
|
+
}));
|
|
47
|
+
},
|
|
48
|
+
async claimSeat(matchId) {
|
|
49
|
+
const rows = await db
|
|
50
|
+
.update(matches)
|
|
51
|
+
.set({
|
|
52
|
+
seatCount: sql `${matches.seatCount} + 1`,
|
|
53
|
+
joinCounter: sql `${matches.joinCounter} + 1`,
|
|
54
|
+
})
|
|
55
|
+
.where(and(eq(matches.id, matchId), eq(matches.status, 'lobby'), sql `${matches.seatCount} < ${matches.maxPlayers}`))
|
|
56
|
+
.returning({ joinCounter: matches.joinCounter });
|
|
57
|
+
const row = rows[0];
|
|
58
|
+
return row ? row.joinCounter - 1 : null;
|
|
59
|
+
},
|
|
60
|
+
async releaseSeat(matchId) {
|
|
61
|
+
await db
|
|
62
|
+
.update(matches)
|
|
63
|
+
.set({ seatCount: sql `${matches.seatCount} - 1` })
|
|
64
|
+
.where(and(eq(matches.id, matchId), sql `${matches.seatCount} > 0`));
|
|
65
|
+
},
|
|
66
|
+
async deleteInactive(statuses, before, limit) {
|
|
67
|
+
// `for update skip locked`: two sweeps (two server instances, or a cron overlapping itself)
|
|
68
|
+
// would otherwise pick overlapping batches and deadlock on each other's row locks. Skipping
|
|
69
|
+
// what a peer already holds means each pass simply collects a different batch.
|
|
70
|
+
const collectable = db
|
|
71
|
+
.select({ id: matches.id })
|
|
72
|
+
.from(matches)
|
|
73
|
+
.where(and(inArray(matches.status, [...statuses]), lt(matches.updatedAt, before)))
|
|
74
|
+
.limit(limit)
|
|
75
|
+
.for('update', { skipLocked: true });
|
|
76
|
+
// Children cascade from the match row, so one delete takes the whole match with it.
|
|
77
|
+
const rows = await db
|
|
78
|
+
.delete(matches)
|
|
79
|
+
.where(inArray(matches.id, collectable))
|
|
80
|
+
.returning({ id: matches.id });
|
|
81
|
+
return rows.length;
|
|
82
|
+
},
|
|
83
|
+
async transition(matchId, from, patch) {
|
|
84
|
+
const rows = await db
|
|
85
|
+
.update(matches)
|
|
86
|
+
.set(patch)
|
|
87
|
+
.where(and(eq(matches.id, matchId), inArray(matches.status, [...from])))
|
|
88
|
+
.returning({ id: matches.id });
|
|
89
|
+
return rows.length === 1;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function postgresPlayerRepository(db) {
|
|
94
|
+
const { matches, players } = schema;
|
|
95
|
+
return {
|
|
96
|
+
/**
|
|
97
|
+
* An insert fed by a select over the match row, so "the match is still in the lobby" is tested
|
|
98
|
+
* by the same statement that writes the player. The seat counter was claimed a moment earlier
|
|
99
|
+
* and the match may have started since; without this the player would land in a running match
|
|
100
|
+
* that had already seated its roster, holding a seat nobody can play.
|
|
101
|
+
*
|
|
102
|
+
* The select list is written out, which means it does NOT get Drizzle's column mapping: a
|
|
103
|
+
* column added to `players` later has to be added here too, in the storage form the column
|
|
104
|
+
* expects. The conformance suite compares a created player against its fixture field by field,
|
|
105
|
+
* so a dropped column fails there rather than going unnoticed.
|
|
106
|
+
*/
|
|
107
|
+
async create(player) {
|
|
108
|
+
const rows = await db
|
|
109
|
+
.insert(players)
|
|
110
|
+
.select(db
|
|
111
|
+
.select({
|
|
112
|
+
id: sql `${player.id}`.as('id'),
|
|
113
|
+
matchId: sql `${player.matchId}`.as('match_id'),
|
|
114
|
+
slot: sql `${player.slot}`.as('slot'),
|
|
115
|
+
joinOrder: sql `${player.joinOrder}`.as('join_order'),
|
|
116
|
+
displayName: sql `${player.displayName}`.as('display_name'),
|
|
117
|
+
tokenHash: sql `${player.tokenHash}`.as('token_hash'),
|
|
118
|
+
status: sql `${player.status}`.as('status'),
|
|
119
|
+
joinedAt: sql `${player.joinedAt}`.as('joined_at'),
|
|
120
|
+
})
|
|
121
|
+
.from(matches)
|
|
122
|
+
.where(and(eq(matches.id, player.matchId), eq(matches.status, 'lobby'))))
|
|
123
|
+
.returning({ id: players.id });
|
|
124
|
+
return rows.length === 1;
|
|
125
|
+
},
|
|
126
|
+
async get(id) {
|
|
127
|
+
return firstOrNull((await db.select().from(players).where(eq(players.id, id))).map(toPlayer));
|
|
128
|
+
},
|
|
129
|
+
async getByTokenHash(tokenHash) {
|
|
130
|
+
return firstOrNull((await db.select().from(players).where(eq(players.tokenHash, tokenHash))).map(toPlayer));
|
|
131
|
+
},
|
|
132
|
+
async listByMatch(matchId) {
|
|
133
|
+
const rows = await db
|
|
134
|
+
.select()
|
|
135
|
+
.from(players)
|
|
136
|
+
.where(eq(players.matchId, matchId))
|
|
137
|
+
.orderBy(asc(players.slot), asc(players.joinOrder), asc(players.id));
|
|
138
|
+
return rows.map(toPlayer);
|
|
139
|
+
},
|
|
140
|
+
async setStatus(playerId, status) {
|
|
141
|
+
await db.update(players).set({ status }).where(eq(players.id, playerId));
|
|
142
|
+
},
|
|
143
|
+
async transitionStatus(playerId, from, status) {
|
|
144
|
+
const rows = await db
|
|
145
|
+
.update(players)
|
|
146
|
+
.set({ status })
|
|
147
|
+
.where(and(eq(players.id, playerId), inArray(players.status, from)))
|
|
148
|
+
.returning({ id: players.id });
|
|
149
|
+
return rows.length === 1;
|
|
150
|
+
},
|
|
151
|
+
async revokeToken(playerId) {
|
|
152
|
+
await db.update(players).set({ tokenHash: null }).where(eq(players.id, playerId));
|
|
153
|
+
},
|
|
154
|
+
async assignSlots(assignments) {
|
|
155
|
+
if (assignments.length === 0)
|
|
156
|
+
return;
|
|
157
|
+
// One statement: a CASE that maps each id to its slot. A loop of updates could commit some
|
|
158
|
+
// seats and not others, and the transition that made the roster final has already landed.
|
|
159
|
+
const cases = assignments.map(({ playerId, slot }) => sql `when ${players.id} = ${playerId} then ${slot}`);
|
|
160
|
+
await db
|
|
161
|
+
.update(players)
|
|
162
|
+
.set({ slot: sql `case ${sql.join(cases, sql ` `)} else ${players.slot} end` })
|
|
163
|
+
.where(inArray(players.id, assignments.map(({ playerId }) => playerId)));
|
|
164
|
+
},
|
|
165
|
+
async delete(playerId) {
|
|
166
|
+
await db.delete(players).where(eq(players.id, playerId));
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function postgresTurnRepository(db) {
|
|
171
|
+
const { matches, turns, turnOrders, turnReports } = schema;
|
|
172
|
+
return {
|
|
173
|
+
async open(turn, playerIds) {
|
|
174
|
+
const created = await insertUnlessTaken(() => db.insert(turns).values(turn));
|
|
175
|
+
if (playerIds.length > 0) {
|
|
176
|
+
// Topped up rather than assumed: a re-run of the open step (a repaired seal) fills any row
|
|
177
|
+
// an interrupted one never wrote, and a player who already has a row keeps it untouched.
|
|
178
|
+
await db
|
|
179
|
+
.insert(turnOrders)
|
|
180
|
+
.values(playerIds.map((playerId) => ({ matchId: turn.matchId, turn: turn.number, playerId })))
|
|
181
|
+
.onConflictDoNothing();
|
|
182
|
+
}
|
|
183
|
+
return created;
|
|
184
|
+
},
|
|
185
|
+
async get(matchId, number) {
|
|
186
|
+
const rows = await db
|
|
187
|
+
.select()
|
|
188
|
+
.from(turns)
|
|
189
|
+
.where(and(eq(turns.matchId, matchId), eq(turns.number, number)));
|
|
190
|
+
return firstOrNull(rows.map(toTurn));
|
|
191
|
+
},
|
|
192
|
+
async submitOrders(matchId, number, playerId, submission) {
|
|
193
|
+
const turnIsOpen = exists(db
|
|
194
|
+
.select({ one: sql `1` })
|
|
195
|
+
.from(turns)
|
|
196
|
+
.where(and(eq(turns.matchId, matchId), eq(turns.number, number), eq(turns.status, 'open'))));
|
|
197
|
+
const rows = await db
|
|
198
|
+
.update(turnOrders)
|
|
199
|
+
.set(submission)
|
|
200
|
+
.where(and(eq(turnOrders.matchId, matchId), eq(turnOrders.turn, number), eq(turnOrders.playerId, playerId), turnIsOpen))
|
|
201
|
+
.returning({ playerId: turnOrders.playerId });
|
|
202
|
+
return rows.length === 1;
|
|
203
|
+
},
|
|
204
|
+
async getOrders(matchId, number, playerId) {
|
|
205
|
+
const rows = await db
|
|
206
|
+
.select()
|
|
207
|
+
.from(turnOrders)
|
|
208
|
+
.where(and(eq(turnOrders.matchId, matchId), eq(turnOrders.turn, number), eq(turnOrders.playerId, playerId)));
|
|
209
|
+
return firstOrNull(rows.map(toTurnOrders));
|
|
210
|
+
},
|
|
211
|
+
async listOrders(matchId, number) {
|
|
212
|
+
const rows = await db
|
|
213
|
+
.select()
|
|
214
|
+
.from(turnOrders)
|
|
215
|
+
.where(and(eq(turnOrders.matchId, matchId), eq(turnOrders.turn, number)))
|
|
216
|
+
.orderBy(asc(turnOrders.playerId));
|
|
217
|
+
return rows.map(toTurnOrders);
|
|
218
|
+
},
|
|
219
|
+
async transition(matchId, number, from, patch) {
|
|
220
|
+
const rows = await db
|
|
221
|
+
.update(turns)
|
|
222
|
+
.set(patch)
|
|
223
|
+
.where(and(eq(turns.matchId, matchId), eq(turns.number, number), inArray(turns.status, [...from])))
|
|
224
|
+
.returning({ number: turns.number });
|
|
225
|
+
return rows.length === 1;
|
|
226
|
+
},
|
|
227
|
+
async rescheduleDeadline(matchId, number, deadlineAt) {
|
|
228
|
+
const rows = await db
|
|
229
|
+
.update(turns)
|
|
230
|
+
.set({ deadlineAt })
|
|
231
|
+
.where(and(eq(turns.matchId, matchId), eq(turns.number, number), eq(turns.status, 'open')))
|
|
232
|
+
.returning({ number: turns.number });
|
|
233
|
+
return rows.length === 1;
|
|
234
|
+
},
|
|
235
|
+
async upsertReport(report) {
|
|
236
|
+
await db
|
|
237
|
+
.insert(turnReports)
|
|
238
|
+
.values(report)
|
|
239
|
+
.onConflictDoUpdate({
|
|
240
|
+
target: [turnReports.matchId, turnReports.turn, turnReports.playerId],
|
|
241
|
+
set: {
|
|
242
|
+
stateHash: report.stateHash,
|
|
243
|
+
finished: report.finished,
|
|
244
|
+
reportedAt: report.reportedAt,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
async listReports(matchId, number) {
|
|
249
|
+
const rows = await db
|
|
250
|
+
.select()
|
|
251
|
+
.from(turnReports)
|
|
252
|
+
.where(and(eq(turnReports.matchId, matchId), eq(turnReports.turn, number)))
|
|
253
|
+
.orderBy(asc(turnReports.playerId));
|
|
254
|
+
return rows.map(toTurnReport);
|
|
255
|
+
},
|
|
256
|
+
async listUnsettled(matchId) {
|
|
257
|
+
const rows = await db
|
|
258
|
+
.select()
|
|
259
|
+
.from(turns)
|
|
260
|
+
.where(and(eq(turns.matchId, matchId), inArray(turns.status, ['sealed', 'desynced'])))
|
|
261
|
+
.orderBy(asc(turns.number));
|
|
262
|
+
return rows.map(toTurn);
|
|
263
|
+
},
|
|
264
|
+
async listExpiredOpen(now, limit) {
|
|
265
|
+
return db
|
|
266
|
+
.select({ matchId: turns.matchId, number: turns.number })
|
|
267
|
+
.from(turns)
|
|
268
|
+
.where(and(eq(turns.status, 'open'), isNotNull(turns.deadlineAt), lte(turns.deadlineAt, now)))
|
|
269
|
+
.orderBy(asc(turns.deadlineAt))
|
|
270
|
+
.limit(limit);
|
|
271
|
+
},
|
|
272
|
+
/**
|
|
273
|
+
* Driven from `matches`, not from `turns`: a match whose current turn has no row at all (a
|
|
274
|
+
* `start` that died before opening turn 1) is as stalled as one whose seal stopped halfway,
|
|
275
|
+
* and an inner join would never see it. The repair opens the successor in both cases.
|
|
276
|
+
*/
|
|
277
|
+
async listStalledSeals(limit) {
|
|
278
|
+
return db
|
|
279
|
+
.select({ matchId: matches.id, number: matches.currentTurn })
|
|
280
|
+
.from(matches)
|
|
281
|
+
.leftJoin(turns, and(eq(turns.matchId, matches.id), eq(turns.number, matches.currentTurn)))
|
|
282
|
+
.where(and(inArray(matches.status, ['running', 'desynced']), or(isNull(turns.status), ne(turns.status, 'open'))))
|
|
283
|
+
.orderBy(asc(matches.id))
|
|
284
|
+
.limit(limit);
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function postgresSnapshotRepository(db) {
|
|
289
|
+
const { snapshots } = schema;
|
|
290
|
+
return {
|
|
291
|
+
async put(snapshot) {
|
|
292
|
+
await db
|
|
293
|
+
.insert(snapshots)
|
|
294
|
+
.values(snapshot)
|
|
295
|
+
.onConflictDoUpdate({
|
|
296
|
+
target: [snapshots.matchId, snapshots.turn],
|
|
297
|
+
set: {
|
|
298
|
+
formatVersion: snapshot.formatVersion,
|
|
299
|
+
stateHash: snapshot.stateHash,
|
|
300
|
+
uploadedByPlayerId: snapshot.uploadedByPlayerId,
|
|
301
|
+
uploadedAt: snapshot.uploadedAt,
|
|
302
|
+
body: snapshot.body,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
async get(matchId, turn) {
|
|
307
|
+
const rows = await db
|
|
308
|
+
.select()
|
|
309
|
+
.from(snapshots)
|
|
310
|
+
.where(and(eq(snapshots.matchId, matchId), eq(snapshots.turn, turn)));
|
|
311
|
+
return firstOrNull(rows.map(toSnapshot));
|
|
312
|
+
},
|
|
313
|
+
async getLatest(matchId) {
|
|
314
|
+
const rows = await db
|
|
315
|
+
.select()
|
|
316
|
+
.from(snapshots)
|
|
317
|
+
.where(eq(snapshots.matchId, matchId))
|
|
318
|
+
.orderBy(desc(snapshots.turn))
|
|
319
|
+
.limit(1);
|
|
320
|
+
return firstOrNull(rows.map(toSnapshot));
|
|
321
|
+
},
|
|
322
|
+
async prune(matchId, keep) {
|
|
323
|
+
// The turns to keep are the newest `keep`; everything strictly below the oldest of them goes.
|
|
324
|
+
const kept = await db
|
|
325
|
+
.select({ turn: snapshots.turn })
|
|
326
|
+
.from(snapshots)
|
|
327
|
+
.where(eq(snapshots.matchId, matchId))
|
|
328
|
+
.orderBy(desc(snapshots.turn))
|
|
329
|
+
.limit(keep);
|
|
330
|
+
const oldestKept = kept.at(-1)?.turn;
|
|
331
|
+
if (oldestKept === undefined || kept.length < keep)
|
|
332
|
+
return 0;
|
|
333
|
+
const rows = await db
|
|
334
|
+
.delete(snapshots)
|
|
335
|
+
.where(and(eq(snapshots.matchId, matchId), lt(snapshots.turn, oldestKept)))
|
|
336
|
+
.returning({ turn: snapshots.turn });
|
|
337
|
+
return rows.length;
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function postgresEventRepository(db) {
|
|
342
|
+
const { matchEvents } = schema;
|
|
343
|
+
return {
|
|
344
|
+
/**
|
|
345
|
+
* The sequence number comes from the log itself inside the insert, so the allocation cannot be
|
|
346
|
+
* separated from the write: a committed `seq` therefore implies every lower one is committed,
|
|
347
|
+
* which is the invariant a stream cursor relies on. Concurrent appends collide on the primary
|
|
348
|
+
* key and the loser simply re-reads the maximum.
|
|
349
|
+
*/
|
|
350
|
+
async append(event) {
|
|
351
|
+
const nextSeq = sql `(select coalesce(max(${matchEvents.seq}), 0) + 1 from ${matchEvents} where ${eq(matchEvents.matchId, event.matchId)})`;
|
|
352
|
+
for (let attempt = 1; attempt <= APPEND_ATTEMPTS; attempt += 1) {
|
|
353
|
+
try {
|
|
354
|
+
const rows = await db
|
|
355
|
+
.insert(matchEvents)
|
|
356
|
+
.values({
|
|
357
|
+
matchId: event.matchId,
|
|
358
|
+
seq: nextSeq,
|
|
359
|
+
type: event.type,
|
|
360
|
+
payload: event.payload,
|
|
361
|
+
createdAt: new Date(event.createdAt),
|
|
362
|
+
})
|
|
363
|
+
.returning({ seq: matchEvents.seq });
|
|
364
|
+
const row = rows[0];
|
|
365
|
+
if (!row)
|
|
366
|
+
throw new Error(`append to ${event.matchId} reported no row`);
|
|
367
|
+
return { ...event, seq: row.seq };
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
if (!isUniqueViolation(error) || attempt === APPEND_ATTEMPTS)
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
throw new Error(`could not append an event for ${event.matchId}`);
|
|
375
|
+
},
|
|
376
|
+
async listAfter(matchId, afterSeq, limit) {
|
|
377
|
+
const rows = await db
|
|
378
|
+
.select()
|
|
379
|
+
.from(matchEvents)
|
|
380
|
+
.where(and(eq(matchEvents.matchId, matchId), sql `${matchEvents.seq} > ${afterSeq}`))
|
|
381
|
+
.orderBy(asc(matchEvents.seq))
|
|
382
|
+
.limit(limit);
|
|
383
|
+
return rows.map(toEvent);
|
|
384
|
+
},
|
|
385
|
+
async lastSeq(matchId) {
|
|
386
|
+
const rows = await db
|
|
387
|
+
.select({ seq: matchEvents.seq })
|
|
388
|
+
.from(matchEvents)
|
|
389
|
+
.where(eq(matchEvents.matchId, matchId))
|
|
390
|
+
.orderBy(desc(matchEvents.seq))
|
|
391
|
+
.limit(1);
|
|
392
|
+
return rows[0]?.seq ?? 0;
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
//# sourceMappingURL=repositories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.js","sourceRoot":"","sources":["../../src/postgres/repositories.ts"],"names":[],"mappings":"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"}
|