Skip to content

Feature/storage - #41

Open
mattvb91 wants to merge 7 commits into
devfrom
feature/storage
Open

mattvb91 wants to merge 7 commits into
devfrom
feature/storage

Conversation

@mattvb91

Copy link
Copy Markdown
Member

No description provided.

Plugins get ctx.db, a Kysely instance from the project's database
plugin, and ctx.table() to name their own tables (chat_mutes for
@mavonengine/chat). A plugin's migrations run in name order before
setup(), each once and in a transaction, recorded in
<prefix>__migrations. A plugin whose migration fails is turned off.

A database plugin returns its Kysely instance from database(). With
more than one, options.database or "database" in mavonengine.json picks
it. Without one, ctx.db is undefined and plugins with migrations are
warned about. Core only imports kysely's types, so it is an optional
peer: a game without a database plugin doesn't install it.

stop() now waits for promises returned by destroy() before closing the
database, and returns a promise itself.
It gives the server and its plugins a Kysely instance on a SQLite file,
.mavonengine/data/mavonengine.sqlite by default, or MAVONENGINE_SQLITE_PATH.
node:sqlite is built into Node, so there is nothing native to compile; a
small adapter gives it the shape Kysely's SqliteDialect expects, and
stores booleans as 0/1 and dates as ISO strings. The database runs in
WAL mode with foreign keys on. A .gitignore is written into the data
directory when the plugin creates it.

tsup drops the node: prefix of imports by default, which turned
node:sqlite into a package that doesn't exist, so the prefix is kept.
The server serves GET /api/game/database (the tables with the plugin
each belongs to by its prefix, their columns and rows, and the
migrations that ran) and GET /api/game/database/tables/:name for a page
of rows. Both only read, through Kysely's query builder so they work on
any database plugin, and only with the admin token, since the database
holds what players did. Table names are checked against the database's
own list.

The dashboard's Database tab lists the tables, shows the rows of the
selected one, paged by what fits on screen, and the migrations. It is
only fetched while the tab shows, and the tab only shows up when GET
/api/game/plugins marks a database plugin. Without the admin token it
says so and the other tabs keep working.
resolve() puts the current drive in front of a path like /game on
Windows, so the expected paths are built with resolve() too instead of
being written out with forward slashes.
A unit test builds the plugin with its own tsup config and checks that
the output imports node:sqlite and runs queries, so a build that drops
the node: prefix again fails npm test instead of the plugin being
skipped in projects.

e2e/sqlite runs the template's server with the built plugin and a plugin
that migrates and writes: the database file, ctx.db, the database
plugin marked in GET /api/game/plugins, GET /api/game/database, the data
after the server is killed and started again, and the server without
the plugin. CI runs it on ubuntu and windows, and typechecks the plugin.
Nothing handled them, so Ctrl+C, a deploy or the dev watcher restarting
the server ended the process without stop(): plugins' destroy() never
ran and the database was never closed. Now a started server frees its
HTTP port, stops (plugins save, then the database closes) and exits
with 0, or with 1 when that takes over 2.5s, below the 3s mavonengine
dev waits. A second Ctrl+C exits right away.

One listener serves every running server and goes away with the last
one, so tests that start many servers add no listeners. stopOnSignals:
false leaves the signals to the game. Windows has no SIGTERM, so a
process killed there still stops without this.

The sqlite e2e's plugin now also saves in destroy(), and the test
checks that row is there after a restart, except on Windows.
tsup takes entries as globs, where a backslash escapes, so the resolved
Windows path matched nothing: the entry is now a relative path with
forward slashes. tsup also names its output .mjs when it runs where the
package.json isn't "type": "module", like the repo root, so the test
sets the extension.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant