@table-games-engine/persistence 0.0.0-stage → 0.1.1
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.md +105 -0
- package/README.md +68 -2
- package/dist/checksum.d.ts +22 -0
- package/dist/core.d.ts +9 -0
- package/dist/core.js +239 -0
- package/dist/in-memory.d.ts +28 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1691 -0
- package/dist/persisted-engine.d.ts +135 -0
- package/dist/replay.d.ts +64 -0
- package/dist/snapshots.d.ts +80 -0
- package/dist/sqlite/adapter.d.ts +56 -0
- package/dist/sqlite/migrations.d.ts +31 -0
- package/package.json +46 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `PersistedEngine` — `Engine` + adapter writes.
|
|
3
|
+
*
|
|
4
|
+
* Wraps an in-process `Engine` (from `@table-games-engine/core`) so that every
|
|
5
|
+
* transition-producing call ALSO:
|
|
6
|
+
* 1. Appends the transition's events to the adapter's event log
|
|
7
|
+
* (audit trail / triggers / spectator catch-up).
|
|
8
|
+
* 2. Asks the configured `SnapshotPolicy` whether to snapshot, and if
|
|
9
|
+
* so writes the new `MatchState` (with checksum) to the adapter.
|
|
10
|
+
*
|
|
11
|
+
* # Sync vs async boundary
|
|
12
|
+
*
|
|
13
|
+
* The core `Engine` API is **synchronous**: `applyAction(input): Result<...>`
|
|
14
|
+
* returns immediately. The persisted wrapper exposes the same shape but
|
|
15
|
+
* each method returns a `Promise<Result<...>>` because adapter writes are
|
|
16
|
+
* async. Internally:
|
|
17
|
+
* 1. The underlying engine call runs synchronously and updates the
|
|
18
|
+
* in-memory state. (This is the source of truth — the wrapper does
|
|
19
|
+
* NOT undo it on adapter failure.)
|
|
20
|
+
* 2. The adapter writes are awaited.
|
|
21
|
+
* 3. Adapter errors propagate to the caller via the returned Promise.
|
|
22
|
+
* They do NOT roll back the in-memory state. Document this clearly:
|
|
23
|
+
* callers that require all-or-nothing semantics must layer their own
|
|
24
|
+
* retry/quarantine logic above the wrapper, or close the engine and
|
|
25
|
+
* restart from the last good snapshot.
|
|
26
|
+
*
|
|
27
|
+
* The trade-off is intentional. The alternative (rollback the in-memory
|
|
28
|
+
* state on failure) would require synthesising an inverse transition or
|
|
29
|
+
* snapshotting before each call, both of which are noticeably more
|
|
30
|
+
* expensive than letting the host runtime decide what to do on failure.
|
|
31
|
+
*
|
|
32
|
+
* # Ownership
|
|
33
|
+
*
|
|
34
|
+
* If `ownerNode` is provided to {@link createPersistedEngine}, the wrapper
|
|
35
|
+
* acquires an ownership lease on `matchId` before doing anything else.
|
|
36
|
+
* On failure (another node holds a non-expired lease) the factory throws.
|
|
37
|
+
* `release()` releases the lease; `close()` releases AND closes the
|
|
38
|
+
* adapter handle.
|
|
39
|
+
*
|
|
40
|
+
* # Initial snapshot
|
|
41
|
+
*
|
|
42
|
+
* The wrapper unconditionally persists the initial state as snapshot v0.
|
|
43
|
+
* This guarantees `replayMatch` returns a valid state even if zero
|
|
44
|
+
* transitions occur before a crash, and gives the snapshot policy a
|
|
45
|
+
* concrete "since-last" baseline to count from.
|
|
46
|
+
*/
|
|
47
|
+
import type { ActionHandler, EngineAdapter, GameEndCheck, GameManifest, MatchId, MatchState, ResolutionFailure, Result, StateTransition } from '@table-games-engine/core';
|
|
48
|
+
import type { ActionInput, ActionValidationResult } from '@table-games-engine/core';
|
|
49
|
+
import { type SnapshotPolicy } from './snapshots.js';
|
|
50
|
+
/**
|
|
51
|
+
* Arguments for {@link createPersistedEngine}.
|
|
52
|
+
*/
|
|
53
|
+
export interface CreatePersistedEngineArgs {
|
|
54
|
+
/** Game manifest passed straight through to {@link createEngine}. */
|
|
55
|
+
readonly manifest: GameManifest;
|
|
56
|
+
/** Initial state (will be persisted as snapshot v0). */
|
|
57
|
+
readonly initialState: MatchState;
|
|
58
|
+
/** Action handlers passed straight through to {@link createEngine}. */
|
|
59
|
+
readonly handlers: Record<string, ActionHandler>;
|
|
60
|
+
/** Persistence backend. The wrapper does NOT take ownership of close(). */
|
|
61
|
+
readonly adapter: EngineAdapter;
|
|
62
|
+
/** Match ID used for snapshot/event/ownership scoping. */
|
|
63
|
+
readonly matchId: MatchId;
|
|
64
|
+
/** Snapshot cadence; defaults to `everyNTransitions(50)` per the M2 plan. */
|
|
65
|
+
readonly policy?: SnapshotPolicy;
|
|
66
|
+
/**
|
|
67
|
+
* If provided, the wrapper acquires an ownership lease on `matchId` for
|
|
68
|
+
* `ownerNode` during construction. Construction throws if the lease
|
|
69
|
+
* cannot be acquired (another node owns it).
|
|
70
|
+
*/
|
|
71
|
+
readonly ownerNode?: string;
|
|
72
|
+
/** Lease duration in seconds. Defaults to 60. */
|
|
73
|
+
readonly leaseSeconds?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Persisted engine handle.
|
|
77
|
+
*
|
|
78
|
+
* Mirrors the shape of `Engine` but every transition method is async (because
|
|
79
|
+
* adapter writes are async) and returns a `Promise<Result<...>>`. Adds
|
|
80
|
+
* lifecycle methods for ownership and snapshot control.
|
|
81
|
+
*/
|
|
82
|
+
export interface PersistedEngine {
|
|
83
|
+
/** Synchronous read of the current `MatchState`. */
|
|
84
|
+
getState(): MatchState;
|
|
85
|
+
/** Match ID this wrapper is bound to. */
|
|
86
|
+
getMatchId(): MatchId;
|
|
87
|
+
/**
|
|
88
|
+
* Apply a player action and persist the resulting transition.
|
|
89
|
+
*
|
|
90
|
+
* On success: in-memory state is updated, events are appended, and a
|
|
91
|
+
* snapshot is taken if the policy fires.
|
|
92
|
+
*
|
|
93
|
+
* On engine validation failure: no adapter writes occur — the engine
|
|
94
|
+
* never produced a transition.
|
|
95
|
+
*
|
|
96
|
+
* On adapter failure: the in-memory state retains the new value (the
|
|
97
|
+
* engine has already applied it). The promise rejects with the adapter
|
|
98
|
+
* error. See module doc for rationale.
|
|
99
|
+
*/
|
|
100
|
+
applyAction(input: ActionInput): Promise<Result<StateTransition, ActionValidationResult>>;
|
|
101
|
+
resolveTopFrame(): Promise<Result<StateTransition, ResolutionFailure>>;
|
|
102
|
+
processTriggers(): Promise<Result<StateTransition, never>>;
|
|
103
|
+
checkStateBasedActions(): Promise<Result<StateTransition, never>>;
|
|
104
|
+
advancePhase(): Promise<Result<StateTransition, 'CANNOT_ADVANCE'>>;
|
|
105
|
+
advanceTurn(): Promise<Result<StateTransition, 'CANNOT_ADVANCE'>>;
|
|
106
|
+
/** Synchronous (the engine helper is sync and produces no transition). */
|
|
107
|
+
checkWinConditions(): GameEndCheck;
|
|
108
|
+
/**
|
|
109
|
+
* Force a snapshot at the current state, regardless of the policy.
|
|
110
|
+
*
|
|
111
|
+
* Useful for "checkpoint before risky operation", "snapshot on match
|
|
112
|
+
* end", or test determinism.
|
|
113
|
+
*/
|
|
114
|
+
forceSnapshot(): Promise<void>;
|
|
115
|
+
/** Release the ownership lease (if any). Idempotent. */
|
|
116
|
+
releaseOwnership(): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Release ownership AND close the underlying adapter. Idempotent for
|
|
119
|
+
* ownership; the adapter close behaviour is whatever the adapter
|
|
120
|
+
* documents (better-sqlite3 tolerates double-close).
|
|
121
|
+
*/
|
|
122
|
+
close(): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a persisted engine wrapper.
|
|
126
|
+
*
|
|
127
|
+
* Steps performed during construction (in order):
|
|
128
|
+
* 1. Acquire ownership lease (if `ownerNode` provided). Throws on failure.
|
|
129
|
+
* 2. Persist `initialState` as the v0 snapshot.
|
|
130
|
+
* 3. Initialise the in-memory `Engine` and return the wrapper.
|
|
131
|
+
*
|
|
132
|
+
* The default policy is `everyNTransitions(50)` per the M2 plan. Tests that
|
|
133
|
+
* want deterministic snapshot frequency should pass `always()` or `never()`.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createPersistedEngine(args: CreatePersistedEngineArgs): Promise<PersistedEngine>;
|
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Replay helper for `EngineAdapter`.
|
|
3
|
+
*
|
|
4
|
+
* # Replay contract (M2 design)
|
|
5
|
+
*
|
|
6
|
+
* The adapter stores TWO independent streams per match:
|
|
7
|
+
* - **Snapshots** — full `MatchState` blobs at specific versions.
|
|
8
|
+
* - **Events** — `GameEvent` records for triggers / audit / spectator
|
|
9
|
+
* catch-up.
|
|
10
|
+
*
|
|
11
|
+
* Crucially, events are NOT a derivation of `StateMutation`s — they are
|
|
12
|
+
* the game-domain things that happened (e.g. `damage-dealt`, `card-drawn`).
|
|
13
|
+
* That means we cannot reconstruct state by "replaying events" the way
|
|
14
|
+
* an event-sourced system might. To rebuild state at version V we have
|
|
15
|
+
* two options:
|
|
16
|
+
*
|
|
17
|
+
* 1. Load a snapshot whose version is `<= V`, and either accept its
|
|
18
|
+
* version (if there is no snapshot at exactly V) or re-execute the
|
|
19
|
+
* original action handlers from that snapshot up to V.
|
|
20
|
+
* 2. Persist a snapshot at every version (snapshot policy
|
|
21
|
+
* `always()`), which collapses (1) to "load the snapshot at V".
|
|
22
|
+
*
|
|
23
|
+
* In M2 we deliberately choose the simpler design: `replayMatch` returns
|
|
24
|
+
* the **latest snapshot at or before `toVersion`**. Callers control
|
|
25
|
+
* snapshot cadence via `SnapshotPolicy` (see `./snapshots.ts`); if a
|
|
26
|
+
* caller needs to recover to an exact version V, they should configure
|
|
27
|
+
* a policy that snapshots at V (e.g. `always()` or a turn-aligned
|
|
28
|
+
* cadence).
|
|
29
|
+
*
|
|
30
|
+
* Re-executing handlers between two snapshots to land on an arbitrary
|
|
31
|
+
* intermediate version is intentionally out of scope for M2. M3
|
|
32
|
+
* (Nakama adapter) will revisit this if cross-node failover requires
|
|
33
|
+
* sub-snapshot precision; for now, snapshot density is the knob.
|
|
34
|
+
*
|
|
35
|
+
* `loadEventsBetween` is a thin sugar wrapper around
|
|
36
|
+
* `EngineAdapter.loadEvents` for callers that want the audit trail
|
|
37
|
+
* across a version range.
|
|
38
|
+
*/
|
|
39
|
+
import type { EngineAdapter, MatchId, MatchState, StateVersion, GameEvent, ImmutableList } from '@table-games-engine/core';
|
|
40
|
+
/**
|
|
41
|
+
* Reconstruct the recorded `MatchState` for `matchId` at or before
|
|
42
|
+
* `toVersion`.
|
|
43
|
+
*
|
|
44
|
+
* Returns the latest snapshot whose `state.version` is `<= toVersion`,
|
|
45
|
+
* or the absolute latest snapshot when `toVersion` is omitted, or
|
|
46
|
+
* `null` when no snapshot has been persisted for the match.
|
|
47
|
+
*
|
|
48
|
+
* The returned state is the exact value that was passed to
|
|
49
|
+
* `persistSnapshot` at the time of capture (modulo JSON round-trip,
|
|
50
|
+
* which `MatchState` is designed to survive).
|
|
51
|
+
*
|
|
52
|
+
* Pure with respect to the caller: makes no engine calls, only adapter
|
|
53
|
+
* reads. The integrity check (checksum verification) happens inside
|
|
54
|
+
* `loadSnapshot` per the adapter contract.
|
|
55
|
+
*/
|
|
56
|
+
export declare function replayMatch(adapter: EngineAdapter, matchId: MatchId, toVersion?: StateVersion): Promise<MatchState | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Load events recorded in the half-open interval `(fromVersion, toVersion]`.
|
|
59
|
+
*
|
|
60
|
+
* Convenience alias for `EngineAdapter.loadEvents`; exposed from the
|
|
61
|
+
* replay module so callers that import "replay-related" helpers find it
|
|
62
|
+
* alongside `replayMatch`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function loadEventsBetween(adapter: EngineAdapter, matchId: MatchId, fromVersion: StateVersion, toVersion: StateVersion): Promise<ImmutableList<GameEvent>>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Snapshot cadence policies for the persisted engine.
|
|
3
|
+
*
|
|
4
|
+
* A `SnapshotPolicy` decides — given the current `MatchState` and a count of
|
|
5
|
+
* "things since the last snapshot" — whether the engine should snapshot
|
|
6
|
+
* RIGHT NOW. Policies are stateless predicates; the persisted engine is
|
|
7
|
+
* responsible for tracking `transitionsSince` and `ticksSince`.
|
|
8
|
+
*
|
|
9
|
+
* Policies are pluggable and composable: real deployments will combine a
|
|
10
|
+
* "every N transitions" rule with a "every T seconds of logical tick" rule
|
|
11
|
+
* via {@link combine}, while tests use {@link always} (snapshot every step)
|
|
12
|
+
* or {@link never} (snapshot never).
|
|
13
|
+
*
|
|
14
|
+
* # Why a separate "since-last" counter?
|
|
15
|
+
*
|
|
16
|
+
* `MatchState.version` could be inspected directly, but the wrapper does
|
|
17
|
+
* NOT have access to the version of the previous snapshot in a stateless
|
|
18
|
+
* predicate. We keep the policy pure and let the wrapper own the counters.
|
|
19
|
+
* This keeps the policy a single-line decision and makes `combine` trivial.
|
|
20
|
+
*
|
|
21
|
+
* # Defaults
|
|
22
|
+
*
|
|
23
|
+
* The persisted engine wrapper defaults to `everyNTransitions(50)` when
|
|
24
|
+
* the caller does not supply a policy — chosen to align with the M2 plan
|
|
25
|
+
* "snapshot every ~50 transitions" guidance.
|
|
26
|
+
*/
|
|
27
|
+
import type { MatchState } from '@table-games-engine/core';
|
|
28
|
+
/**
|
|
29
|
+
* A snapshot cadence policy.
|
|
30
|
+
*
|
|
31
|
+
* `shouldSnapshot` is invoked AFTER each engine transition. It receives:
|
|
32
|
+
* - `state`: the current `MatchState` AFTER the transition.
|
|
33
|
+
* - `sinceLast`: counters tracked by the wrapper since the last
|
|
34
|
+
* snapshot was taken. `transitionsSince` and `ticksSince` are both
|
|
35
|
+
* `0` immediately after a snapshot fires.
|
|
36
|
+
*
|
|
37
|
+
* Implementations MUST be pure — same arguments produce same answer.
|
|
38
|
+
*/
|
|
39
|
+
export interface SnapshotPolicy {
|
|
40
|
+
shouldSnapshot(state: MatchState, sinceLast: {
|
|
41
|
+
transitionsSince: number;
|
|
42
|
+
ticksSince: number;
|
|
43
|
+
}): boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Snapshot once every `n` transitions (`n >= 1`).
|
|
47
|
+
*
|
|
48
|
+
* `n=1` is equivalent to {@link always}. `n=0` would never trigger and is
|
|
49
|
+
* disallowed — use {@link never} explicitly when you mean "off".
|
|
50
|
+
*/
|
|
51
|
+
export declare function everyNTransitions(n: number): SnapshotPolicy;
|
|
52
|
+
/**
|
|
53
|
+
* Snapshot once every `t` logical ticks (`t >= 1`).
|
|
54
|
+
*
|
|
55
|
+
* Useful when "transitions" is too granular (e.g. lots of trigger
|
|
56
|
+
* processing per logical tick). `ticksSince` is the engine's logical
|
|
57
|
+
* `state.tick`, NOT wall-clock time.
|
|
58
|
+
*/
|
|
59
|
+
export declare function everyTTicks(t: number): SnapshotPolicy;
|
|
60
|
+
/**
|
|
61
|
+
* Snapshot every transition. Equivalent to `everyNTransitions(1)`.
|
|
62
|
+
*
|
|
63
|
+
* Recommended for tests (round-trip determinism) and for async-mode
|
|
64
|
+
* games where every action persists.
|
|
65
|
+
*/
|
|
66
|
+
export declare function always(): SnapshotPolicy;
|
|
67
|
+
/**
|
|
68
|
+
* Never snapshot. Useful for benchmarking the engine without persistence
|
|
69
|
+
* overhead, or for read-only "audit" engines that only need the event log.
|
|
70
|
+
*/
|
|
71
|
+
export declare function never(): SnapshotPolicy;
|
|
72
|
+
/**
|
|
73
|
+
* Disjunction of policies: snapshot if **any** sub-policy returns `true`.
|
|
74
|
+
*
|
|
75
|
+
* Empty input returns a policy equivalent to {@link never}.
|
|
76
|
+
*
|
|
77
|
+
* The first policy that returns `true` short-circuits evaluation, so cheap
|
|
78
|
+
* predicates should come first.
|
|
79
|
+
*/
|
|
80
|
+
export declare function combine(...policies: SnapshotPolicy[]): SnapshotPolicy;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview SQLite-backed `EngineAdapter` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Uses `better-sqlite3` (synchronous, native bindings). Each `EngineAdapter`
|
|
5
|
+
* method wraps its work in `Promise.resolve(...)` to satisfy the async
|
|
6
|
+
* contract — but no real async I/O is performed.
|
|
7
|
+
*
|
|
8
|
+
* Multi-row writes (event batches, ownership upserts, snapshot inserts) are
|
|
9
|
+
* wrapped in transactions for atomicity. Snapshots and loads run integrity
|
|
10
|
+
* checks via `checksumState`.
|
|
11
|
+
*
|
|
12
|
+
* NODE-ONLY: This module imports `better-sqlite3` and `node:fs` (via
|
|
13
|
+
* `migrations.ts`). It MUST NOT be imported from `@table-games-engine/core`.
|
|
14
|
+
*/
|
|
15
|
+
import type { EngineAdapter, GameEvent, ImmutableList, MatchId, MatchState, StateVersion } from '@table-games-engine/core';
|
|
16
|
+
export interface SqliteAdapterOptions {
|
|
17
|
+
/** Injectable clock for deterministic lease tests. Defaults to `Date.now`. */
|
|
18
|
+
now?: () => number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* SQLite-backed `EngineAdapter`.
|
|
22
|
+
*
|
|
23
|
+
* Construct with a filesystem path or `':memory:'`. Migrations are applied
|
|
24
|
+
* eagerly; the database handle is owned by this adapter and released by
|
|
25
|
+
* `close()`.
|
|
26
|
+
*/
|
|
27
|
+
export declare class SqliteAdapter implements EngineAdapter {
|
|
28
|
+
private readonly db;
|
|
29
|
+
private readonly now;
|
|
30
|
+
private readonly stmtInsertSnapshot;
|
|
31
|
+
private readonly stmtSelectSnapshotChecksum;
|
|
32
|
+
private readonly stmtSelectLatestSnapshot;
|
|
33
|
+
private readonly stmtSelectSnapshotAtVersion;
|
|
34
|
+
private readonly stmtMaxEventVersion;
|
|
35
|
+
private readonly stmtInsertEvent;
|
|
36
|
+
private readonly stmtSelectEvents;
|
|
37
|
+
private readonly stmtSelectOwnership;
|
|
38
|
+
private readonly stmtUpsertOwnership;
|
|
39
|
+
private readonly stmtDeleteOwnership;
|
|
40
|
+
constructor(path: string, opts?: SqliteAdapterOptions);
|
|
41
|
+
persistSnapshot(matchId: MatchId, state: MatchState, checksum: string): Promise<void>;
|
|
42
|
+
loadSnapshot(matchId: MatchId, atVersion?: StateVersion): Promise<MatchState | null>;
|
|
43
|
+
appendEvents(matchId: MatchId, transitionVersion: StateVersion, events: ImmutableList<GameEvent>): Promise<void>;
|
|
44
|
+
loadEvents(matchId: MatchId, fromVersion: StateVersion, toVersion: StateVersion): Promise<ImmutableList<GameEvent>>;
|
|
45
|
+
acquireOwnership(matchId: MatchId, ownerNode: string, leaseSeconds: number): Promise<boolean>;
|
|
46
|
+
releaseOwnership(matchId: MatchId, ownerNode: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Close the underlying database. Subsequent calls into adapter methods will
|
|
49
|
+
* throw with the better-sqlite3 native error ("The database connection is
|
|
50
|
+
* not open"); we do not wrap that to keep the contract minimal.
|
|
51
|
+
*
|
|
52
|
+
* Idempotent: calling `close()` twice is a no-op (better-sqlite3 tolerates
|
|
53
|
+
* the second close cleanly when the handle is already released).
|
|
54
|
+
*/
|
|
55
|
+
close(): Promise<void>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview SQLite migrations for the persistence package.
|
|
3
|
+
*
|
|
4
|
+
* `applyMigrations` reads the colocated `schema.sql` at runtime and applies
|
|
5
|
+
* its DDL inside a single transaction. Idempotent: every statement uses
|
|
6
|
+
* IF NOT EXISTS, so re-running on a populated database is safe.
|
|
7
|
+
*
|
|
8
|
+
* The `_meta` table tracks `schema_version` for future migrations. Initial
|
|
9
|
+
* version is `1`. Schema-version helpers (`getSchemaVersion`,
|
|
10
|
+
* `setSchemaVersion`) are exported for forward compatibility.
|
|
11
|
+
*
|
|
12
|
+
* NOTE: This file is Node-only — it uses `fs.readFileSync` and the
|
|
13
|
+
* `better-sqlite3` Database type. It MUST NOT be imported from
|
|
14
|
+
* `@table-games-engine/core`.
|
|
15
|
+
*/
|
|
16
|
+
import type Database from 'better-sqlite3';
|
|
17
|
+
/** Initial schema version — bump only when adding migrations. */
|
|
18
|
+
export declare const CURRENT_SCHEMA_VERSION = 1;
|
|
19
|
+
/**
|
|
20
|
+
* Apply the SQL schema to `db`. Idempotent: safe to run on every adapter
|
|
21
|
+
* construction.
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyMigrations(db: Database.Database): void;
|
|
24
|
+
/**
|
|
25
|
+
* Read the persisted schema version. Returns `0` when no version is recorded
|
|
26
|
+
* yet — including the case where the `_meta` table itself does not exist
|
|
27
|
+
* (i.e. brand-new database before `applyMigrations`).
|
|
28
|
+
*/
|
|
29
|
+
export declare function getSchemaVersion(db: Database.Database): number;
|
|
30
|
+
/** Persist the schema version (used by `applyMigrations` and future runners). */
|
|
31
|
+
export declare function setSchemaVersion(db: Database.Database, n: number): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@table-games-engine/persistence",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
}
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./core": {
|
|
16
|
+
"types": "./dist/core.d.ts",
|
|
17
|
+
"default": "./dist/core.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "esbuild src/index.ts src/core.ts --bundle --format=esm --platform=node --packages=external --outdir=dist && tsc -p tsconfig.build.json",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@table-games-engine/core": "^0.1.1",
|
|
30
|
+
"better-sqlite3": "^11.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
34
|
+
"typescript": "^5.4.0",
|
|
35
|
+
"vitest": "^2.0.0"
|
|
36
|
+
},
|
|
37
|
+
"license": "FSL-1.1-ALv2",
|
|
38
|
+
"description": "Snapshot, event and replay persistence (in-memory and SQLite) for table-games-engine matches.",
|
|
39
|
+
"keywords": [
|
|
40
|
+
"table-games",
|
|
41
|
+
"turn-based",
|
|
42
|
+
"multiplayer",
|
|
43
|
+
"game-engine",
|
|
44
|
+
"nakama",
|
|
45
|
+
"boardgame"
|
|
46
|
+
],
|
|
47
|
+
"homepage": "https://www.npmjs.com/package/@table-games-engine/persistence"
|
|
48
|
+
}
|