Skip to content
This repository was archived by the owner on Sep 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 0 additions & 49 deletions .changeset/opaque-pagination-cursors.md

This file was deleted.

2 changes: 1 addition & 1 deletion clients/dart/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This package is generated from the Eigen engine's OpenAPI specification and its
version tracks [`@eigeninteractive/server`](https://www.npmjs.com/package/@eigeninteractive/server)
exactly: 0.3.1 here is 0.3.1 there. It has no changes of its own.
exactly: 0.4.0 here is 0.4.0 there. It has no changes of its own.

See the engine's changelog:
<https://github.com/eigeninteractive/eigen-server/blob/main/packages/server/CHANGELOG.md>
Expand Down
2 changes: 1 addition & 1 deletion clients/dart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The server-authoritative game engine API. Client routes (`/api/engine/_*`) requi

This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:

- API version: 0.3.1
- API version: 0.4.0
- Generator version: 7.17.0
- Build package: org.openapitools.codegen.languages.DartDioClientCodegen

Expand Down
2 changes: 1 addition & 1 deletion clients/dart/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description: >-
# `version:` is stamped by the generation script from @eigeninteractive/server's
# package.json. The wire contract is the server's, so the client carries the
# server's version. Do not edit it by hand.
version: 0.3.1
version: 0.4.0
homepage: https://eigeninteractive.com
repository: https://github.com/eigeninteractive/eigen-server
issue_tracker: https://github.com/eigeninteractive/eigen-server/issues
Expand Down
7 changes: 7 additions & 0 deletions packages/kernel/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @eigeninteractive/kernel

## 0.4.0

### Patch Changes

- Updated dependencies []:
- @eigeninteractive/rules@0.4.0

## 0.3.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/kernel/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/kernel",
"version": "0.3.1",
"version": "0.4.0",
"license": "MIT",
"description": "The pure decision core of the EigenInteractive engine: given a game, state, roster and intent it returns a commit plan or a rejection. No I/O, no platform APIs.",
"main": "./dist/index.js",
Expand Down
2 changes: 2 additions & 0 deletions packages/rules/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# @eigeninteractive/rules

## 0.4.0

## 0.3.1

## 0.3.0
Expand Down
2 changes: 1 addition & 1 deletion packages/rules/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/rules",
"version": "0.3.1",
"version": "0.4.0",
"license": "MIT",
"description": "The game-rules contract for the EigenInteractive engine: GameRules, GameModule, the hooks, and the Envelope/Observation types. Pure types, zero engine dependencies.",
"main": "./dist/index.js",
Expand Down
56 changes: 56 additions & 0 deletions packages/server/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
# @eigeninteractive/server

## 0.4.0

### Minor Changes

- [#60](https://github.com/eigeninteractive/eigen-server/pull/60) [`4339dff`](https://github.com/eigeninteractive/eigen-server/commit/4339dff9c9f43bff9b9f482f6e9cd72f4f9476c7) Thanks [@seenu-k](https://github.com/seenu-k)! - Pagination cursors are now opaque tokens, and empty query parameters are treated as absent.

Every paged list could silently return nothing. A client that sent an optional
query parameter it had no value for produced `?cursor`, which arrives as the
empty string, and `z.coerce.number()` is `Number()`, so `""` became `0`. Zero is
a structurally valid integer cursor meaning "strictly older than the beginning of
time", so the request did not fail: the lobby, both `games/mine` buckets, the
friends list and every history screen returned `200` with an empty array. Nothing
was logged, because nothing had gone wrong as far as any layer could tell.

Three changes, each closing a different part of it:

- **`cursor` is now an opaque string** rather than a bare epoch-millisecond
integer, and paged responses carry a **`nextCursor`** that is null exactly when
the list is exhausted. There is no byte string that accidentally decodes to the
beginning of time, so the failure above cannot recur by construction. A cursor
that does not decode is a `400` with the new `invalidCursor` code.
- **The cursor carries the row id alongside the sort value**, so pages no longer
drop a row when two games share a timestamp - a limitation the previous
implementation documented but did not fix. The comparison is a SQLite row value
(`(sortKey, id) < (?, ?)`), which is planned against the same index.
- **Query parameters are parsed, never coerced.** This is the origin of the whole
failure, and it took four correct layers to become invisible. `Number(null)` is
`0`, so `z.coerce.number().int().min(0)` genuinely accepts null; the emitted
schema honestly reported `["integer", "null"]`; `openapi-generator` correctly
dropped its `if (x != null)` guard, because the API had declared null welcome;
dio rendered that null as a bare `?to=`; and the server coerced `""` back to
`0`. No library was misbehaving. Integer query parameters now parse from a
strict pattern, so they reject null and empty alike, which means the generated
client omits an absent parameter on its own and a malformed one is a loud `400`
rather than a plausible, wrong `200`. `?pool=` and `?to=` were failing the same
way and are fixed by the same change.

A contract test now asserts that no query parameter in the published document
is nullable, which is the tell that was invisible in review: `.min(0)` was
nullable and `.min(1)` was not.

Because `nextCursor` is now an answer rather than something a client infers from
a short page, callers no longer need to know how any list is sorted, and a final
page that happens to be exactly full no longer triggers one pointless request.

Breaking for direct API consumers: `cursor` is a string, and the four paged
responses (`Lobby`, `MyGames`, `PlayerGames`, `FriendsGames`) gained a required
`nextCursor` field. The generated Dart client and `eigen_flutter` are updated to
match.

### Patch Changes

- Updated dependencies []:
- @eigeninteractive/kernel@0.4.0
- @eigeninteractive/rules@0.4.0

## 0.3.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/server/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "EigenInteractive Engine API",
"version": "0.3.1",
"version": "0.4.0",
"description": "The server-authoritative game engine API. Client routes (`/api/engine/*`) require a Firebase ID token; the external-bot webhook (`/api/bot/action`) is HMAC-authenticated."
},
"tags": [
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/server",
"version": "0.3.1",
"version": "0.4.0",
"license": "MIT",
"description": "The deployable half of the EigenInteractive engine: the createEngine Worker factory, the BaseGameDO Durable Object base class, the D1 schema and appliers, auth, bots and push.",
"main": "./dist/index.js",
Expand Down
8 changes: 8 additions & 0 deletions packages/testkit/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @eigeninteractive/testkit

## 0.4.0

### Patch Changes

- Updated dependencies []:
- @eigeninteractive/kernel@0.4.0
- @eigeninteractive/rules@0.4.0

## 0.3.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/testkit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/testkit",
"version": "0.3.1",
"version": "0.4.0",
"license": "MIT",
"description": "Test helpers for games built on the EigenInteractive engine: drive rules through the real kernel and run shared twin fixtures, with no Worker, database or network.",
"main": "./dist/index.js",
Expand Down
Loading