@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.
Files changed (46) hide show
  1. package/LICENSE +674 -0
  2. package/NOTICE +21 -0
  3. package/README.md +35 -0
  4. package/dist/node/index.d.ts +29 -0
  5. package/dist/node/index.d.ts.map +1 -0
  6. package/dist/node/index.js +60 -0
  7. package/dist/node/index.js.map +1 -0
  8. package/dist/postgres/index.d.ts +3 -0
  9. package/dist/postgres/index.d.ts.map +1 -0
  10. package/dist/postgres/index.js +3 -0
  11. package/dist/postgres/index.js.map +1 -0
  12. package/dist/postgres/repositories.d.ts +11 -0
  13. package/dist/postgres/repositories.d.ts.map +1 -0
  14. package/dist/postgres/repositories.js +396 -0
  15. package/dist/postgres/repositories.js.map +1 -0
  16. package/dist/postgres/schema.d.ts +1002 -0
  17. package/dist/postgres/schema.d.ts.map +1 -0
  18. package/dist/postgres/schema.js +103 -0
  19. package/dist/postgres/schema.js.map +1 -0
  20. package/dist/shared/constraints.d.ts +6 -0
  21. package/dist/shared/constraints.d.ts.map +1 -0
  22. package/dist/shared/constraints.js +49 -0
  23. package/dist/shared/constraints.js.map +1 -0
  24. package/dist/shared/mappers.d.ts +105 -0
  25. package/dist/shared/mappers.d.ts.map +1 -0
  26. package/dist/shared/mappers.js +55 -0
  27. package/dist/shared/mappers.js.map +1 -0
  28. package/dist/sqlite/index.d.ts +3 -0
  29. package/dist/sqlite/index.d.ts.map +1 -0
  30. package/dist/sqlite/index.js +3 -0
  31. package/dist/sqlite/index.js.map +1 -0
  32. package/dist/sqlite/repositories.d.ts +11 -0
  33. package/dist/sqlite/repositories.d.ts.map +1 -0
  34. package/dist/sqlite/repositories.js +392 -0
  35. package/dist/sqlite/repositories.js.map +1 -0
  36. package/dist/sqlite/schema.d.ts +1063 -0
  37. package/dist/sqlite/schema.d.ts.map +1 -0
  38. package/dist/sqlite/schema.js +103 -0
  39. package/dist/sqlite/schema.js.map +1 -0
  40. package/migrations/postgres/0000_glorious_zaladane.sql +94 -0
  41. package/migrations/postgres/meta/0000_snapshot.json +637 -0
  42. package/migrations/postgres/meta/_journal.json +13 -0
  43. package/migrations/sqlite/0000_exotic_warbird.sql +94 -0
  44. package/migrations/sqlite/meta/0000_snapshot.json +605 -0
  45. package/migrations/sqlite/meta/_journal.json +13 -0
  46. 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,3 @@
1
+ export { createPostgresStorage, type PostgresDatabase } from './repositories';
2
+ export * as postgresSchema from './schema';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,3 @@
1
+ export { createPostgresStorage } from './repositories';
2
+ export * as postgresSchema from './schema';
3
+ //# sourceMappingURL=index.js.map
@@ -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":"AAUA,OAAO,EACL,GAAG,EACH,GAAG,EACH,IAAI,EACJ,EAAE,EACF,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,EAAE,EACF,GAAG,EACH,EAAE,EACF,EAAE,EACF,GAAG,GACJ,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC7F,OAAO,EACL,WAAW,EACX,OAAO,EACP,OAAO,EACP,aAAa,EACb,QAAQ,EACR,UAAU,EACV,MAAM,EACN,YAAY,EACZ,YAAY,GACb,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,MAAM,MAAM,UAAU,CAAA;AASlC,MAAM,UAAU,qBAAqB,CAAC,EAAoB;IACxD,OAAO;QACL,OAAO,EAAE,uBAAuB,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,wBAAwB,CAAC,EAAE,CAAC;QACrC,KAAK,EAAE,sBAAsB,CAAC,EAAE,CAAC;QACjC,SAAS,EAAE,0BAA0B,CAAC,EAAE,CAAC;QACzC,MAAM,EAAE,uBAAuB,CAAC,EAAE,CAAC;KACpC,CAAA;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,EAAoB;IACnD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;IACnC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAK;YAChB,OAAO,iBAAiB,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACjF,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,OAAO,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;QAC9F,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,QAAQ;YAC1B,OAAO,WAAW,CAChB,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CACrF,CAAA;QACH,CAAC;QACD,KAAK,CAAC,iBAAiB,CAAC,KAAK;YAC3B,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC;gBACN,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,eAAe,EAAE,OAAO,CAAC,WAAW;gBACpC,WAAW,EAAE,OAAO,CAAC,SAAS;gBAC9B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,SAAS,EAAE,OAAO,CAAC,SAAS;aAC7B,CAAC;iBACD,IAAI,CAAC,OAAO,CAAC;iBACb,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;iBACxD,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;iBACzE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;iBACjD,KAAK,CAAC,KAAK,CAAC,CAAA;YACf,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;gBACzD,GAAG,IAAI;gBACP,iBAAiB,EAAE,YAAY,KAAK,IAAI;gBACxC,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;aACnC,CAAC,CAAC,CAAA;QACL,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,OAAO;YACrB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC;gBACH,SAAS,EAAE,GAAG,CAAA,GAAG,OAAO,CAAC,SAAS,MAAM;gBACxC,WAAW,EAAE,GAAG,CAAA,GAAG,OAAO,CAAC,WAAW,MAAM;aAC7C,CAAC;iBACD,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,EACvB,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3B,GAAG,CAAA,GAAG,OAAO,CAAC,SAAS,MAAM,OAAO,CAAC,UAAU,EAAE,CAClD,CACF;iBACA,SAAS,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;YAClD,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACnB,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACzC,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,OAAO;YACvB,MAAM,EAAE;iBACL,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,CAAA,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC;iBACjD,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,GAAG,CAAA,GAAG,OAAO,CAAC,SAAS,MAAM,CAAC,CAAC,CAAA;QACvE,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK;YAC1C,4FAA4F;YAC5F,4FAA4F;YAC5F,+EAA+E;YAC/E,MAAM,WAAW,GAAG,EAAE;iBACnB,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;iBAC1B,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;iBACjF,KAAK,CAAC,KAAK,CAAC;iBACZ,GAAG,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;YACtC,oFAAoF;YACpF,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,OAAO,CAAC;iBACf,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;iBACvC,SAAS,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;YAChC,OAAO,IAAI,CAAC,MAAM,CAAA;QACpB,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK;YACnC,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC,KAAK,CAAC;iBACV,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;iBACvE,SAAS,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;YAChC,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,wBAAwB,CAAC,EAAoB;IACpD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;IACnC,OAAO;QACL;;;;;;;;;;WAUG;QACH,KAAK,CAAC,MAAM,CAAC,MAAM;YACjB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,OAAO,CAAC;iBACf,MAAM,CACL,EAAE;iBACC,MAAM,CAAC;gBACN,EAAE,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;gBAC9B,OAAO,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC;gBAC9C,IAAI,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC;gBACpC,SAAS,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC;gBACpD,WAAW,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC;gBAC1D,SAAS,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC;gBACpD,MAAM,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC;gBAC1C,QAAQ,EAAE,GAAG,CAAA,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC;aAClD,CAAC;iBACD,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAC3E;iBACA,SAAS,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;YAChC,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,OAAO,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC/F,CAAC;QACD,KAAK,CAAC,cAAc,CAAC,SAAS;YAC5B,OAAO,WAAW,CAChB,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CACxF,CAAA;QACH,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,OAAO;YACvB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,OAAO,CAAC;iBACb,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;iBACnC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;YACtE,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAC3B,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM;YAC9B,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC1E,CAAC;QACD,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM;YAC3C,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;iBACf,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;iBACnE,SAAS,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;YAChC,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,QAAQ;YACxB,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;QACnF,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,WAAW;YAC3B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YACpC,2FAA2F;YAC3F,0FAA0F;YAC1F,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAC3B,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,CAAA,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,SAAS,IAAI,EAAE,CAC3E,CAAA;YACD,MAAM,EAAE;iBACL,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,CAAA,QAAQ,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAA,GAAG,CAAC,SAAS,OAAO,CAAC,IAAI,MAAM,EAAE,CAAC;iBAC5E,KAAK,CACJ,OAAO,CACL,OAAO,CAAC,EAAE,EACV,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAC5C,CACF,CAAA;QACL,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,QAAQ;YACnB,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC1D,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,EAAoB;IAClD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;IAC1D,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS;YACxB,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YAC5E,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,2FAA2F;gBAC3F,yFAAyF;gBACzF,MAAM,EAAE;qBACL,MAAM,CAAC,UAAU,CAAC;qBAClB,MAAM,CACL,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CACtF;qBACA,mBAAmB,EAAE,CAAA;YAC1B,CAAC;YACD,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM;YACvB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,KAAK,CAAC;iBACX,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;YACnE,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;QACtC,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU;YACtD,MAAM,UAAU,GAAG,MAAM,CACvB,EAAE;iBACC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAA,GAAG,EAAE,CAAC;iBACvB,IAAI,CAAC,KAAK,CAAC;iBACX,KAAK,CACJ,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CACpF,CACJ,CAAA;YACD,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,UAAU,CAAC;iBAClB,GAAG,CAAC,UAAU,CAAC;iBACf,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,EAC/B,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAC3B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,EACjC,UAAU,CACX,CACF;iBACA,SAAS,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAA;YAC/C,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ;YACvC,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,UAAU,CAAC;iBAChB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,EAC/B,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAC3B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAClC,CACF,CAAA;YACH,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAA;QAC5C,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM;YAC9B,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,UAAU,CAAC;iBAChB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;iBACxE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAA;YACpC,OAAO,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QAC/B,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK;YAC3C,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,KAAK,CAAC;iBACb,GAAG,CAAC,KAAK,CAAC;iBACV,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAC1B,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,EACxB,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CACjC,CACF;iBACA,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;YACtC,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU;YAClD,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,KAAK,CAAC;iBACb,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC;iBACnB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;iBAC1F,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;YACtC,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,MAAM;YACvB,MAAM,EAAE;iBACL,MAAM,CAAC,WAAW,CAAC;iBACnB,MAAM,CAAC,MAAM,CAAC;iBACd,kBAAkB,CAAC;gBAClB,MAAM,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,QAAQ,CAAC;gBACrE,GAAG,EAAE;oBACH,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,UAAU,EAAE,MAAM,CAAC,UAAU;iBAC9B;aACF,CAAC,CAAA;QACN,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM;YAC/B,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,WAAW,CAAC;iBACjB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;iBAC1E,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAA;YACrC,OAAO,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QAC/B,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,OAAO;YACzB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,KAAK,CAAC;iBACX,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;iBACrF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;YAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACzB,CAAC;QACD,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,KAAK;YAC9B,OAAO,EAAE;iBACN,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;iBACxD,IAAI,CAAC,KAAK,CAAC;iBACX,KAAK,CACJ,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CACvF;iBACA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;iBAC9B,KAAK,CAAC,KAAK,CAAC,CAAA;QACjB,CAAC;QACD;;;;WAIG;QACH,KAAK,CAAC,gBAAgB,CAAC,KAAK;YAC1B,OAAO,EAAE;iBACN,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;iBAC5D,IAAI,CAAC,OAAO,CAAC;iBACb,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;iBAC1F,KAAK,CACJ,GAAG,CACD,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,EAChD,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CACnD,CACF;iBACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;iBACxB,KAAK,CAAC,KAAK,CAAC,CAAA;QACjB,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,0BAA0B,CAAC,EAAoB;IACtD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAA;IAC5B,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,QAAQ;YAChB,MAAM,EAAE;iBACL,MAAM,CAAC,SAAS,CAAC;iBACjB,MAAM,CAAC,QAAQ,CAAC;iBAChB,kBAAkB,CAAC;gBAClB,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC;gBAC3C,GAAG,EAAE;oBACH,aAAa,EAAE,QAAQ,CAAC,aAAa;oBACrC,SAAS,EAAE,QAAQ,CAAC,SAAS;oBAC7B,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;oBAC/C,UAAU,EAAE,QAAQ,CAAC,UAAU;oBAC/B,IAAI,EAAE,QAAQ,CAAC,IAAI;iBACpB;aACF,CAAC,CAAA;QACN,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI;YACrB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,SAAS,CAAC;iBACf,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;YACvE,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAA;QAC1C,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,OAAO;YACrB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,SAAS,CAAC;iBACf,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;iBACrC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;iBAC7B,KAAK,CAAC,CAAC,CAAC,CAAA;YACX,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAA;QAC1C,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI;YACvB,8FAA8F;YAC9F,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;iBAChC,IAAI,CAAC,SAAS,CAAC;iBACf,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;iBACrC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;iBAC7B,KAAK,CAAC,IAAI,CAAC,CAAA;YACd,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAA;YACpC,IAAI,UAAU,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI;gBAAE,OAAO,CAAC,CAAA;YAC5D,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,SAAS,CAAC;iBACjB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;iBAC1E,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,CAAA;YACtC,OAAO,IAAI,CAAC,MAAM,CAAA;QACpB,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,EAAoB;IACnD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;IAC9B,OAAO;QACL;;;;;WAKG;QACH,KAAK,CAAC,MAAM,CAAC,KAAK;YAChB,MAAM,OAAO,GAAG,GAAG,CAAQ,wBAAwB,WAAW,CAAC,GAAG,kBAAkB,WAAW,UAAU,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAA;YAClJ,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,eAAe,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;gBAC/D,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,EAAE;yBAClB,MAAM,CAAC,WAAW,CAAC;yBACnB,MAAM,CAAC;wBACN,OAAO,EAAE,KAAK,CAAC,OAAO;wBACtB,GAAG,EAAE,OAAO;wBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;wBACtB,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;qBACrC,CAAC;yBACD,SAAS,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,CAAC,CAAA;oBACtC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;oBACnB,IAAI,CAAC,GAAG;wBAAE,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;oBACvE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAoB,CAAA;gBACrD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,eAAe;wBAAE,MAAM,KAAK,CAAA;gBAC3E,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QACnE,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK;YACtC,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,EAAE;iBACR,IAAI,CAAC,WAAW,CAAC;iBACjB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,CAAA,GAAG,WAAW,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC,CAAC;iBACnF,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;iBAC7B,KAAK,CAAC,KAAK,CAAC,CAAA;YACf,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,OAAO;YACnB,MAAM,IAAI,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,CAAC;iBAChC,IAAI,CAAC,WAAW,CAAC;iBACjB,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;iBACvC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;iBAC9B,KAAK,CAAC,CAAC,CAAC,CAAA;YACX,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC"}