@cosmicdrift/kumiko-server-runtime 0.252.0 → 0.252.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.252.
|
|
3
|
+
"version": "0.252.1",
|
|
4
4
|
"description": "Production server-boot runtime for Kumiko apps: connections, schema-drift-gate, seeds, lifecycle, graceful shutdown. Symmetric to kumiko-dev-server's runDevApp, without dev/scaffold/codegen tooling.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@cosmicdrift/kumiko-bundled-features": "0.252.
|
|
84
|
-
"@cosmicdrift/kumiko-framework": "0.252.
|
|
83
|
+
"@cosmicdrift/kumiko-bundled-features": "0.252.1",
|
|
84
|
+
"@cosmicdrift/kumiko-framework": "0.252.1",
|
|
85
85
|
"temporal-polyfill": "^0.3.2"
|
|
86
86
|
},
|
|
87
87
|
"publishConfig": {
|
|
@@ -895,6 +895,11 @@ describe("runProdApp: lokaler Event-Dispatcher (MSP-Anwendung im Single-Containe
|
|
|
895
895
|
// multiStreamProjection blieb in Prod unangewendet, kumiko_event_consumers
|
|
896
896
|
// blieb leer. Der Test schreibt über den ECHTEN Boot-Pfad und pollt auf
|
|
897
897
|
// die async projizierte Row.
|
|
898
|
+
// Both polls normally settle in well under a second; this only sizes the
|
|
899
|
+
// harness limit to the two 8s poll budgets so a slow retry is not cut short
|
|
900
|
+
// by bun's 5s default before pollFor can give up on its own terms.
|
|
901
|
+
const BOOT_AND_POLL_TIMEOUT_MS = 20_000;
|
|
902
|
+
|
|
898
903
|
async function pollFor<T>(probe: () => Promise<T | undefined>, timeoutMs = 8000): Promise<T> {
|
|
899
904
|
const deadline = Date.now() + timeoutMs;
|
|
900
905
|
for (;;) {
|
|
@@ -905,51 +910,60 @@ describe("runProdApp: lokaler Event-Dispatcher (MSP-Anwendung im Single-Containe
|
|
|
905
910
|
}
|
|
906
911
|
}
|
|
907
912
|
|
|
908
|
-
test(
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
expect(handle.entrypoint.eventDispatcher).toBeDefined();
|
|
913
|
+
test(
|
|
914
|
+
"Write → appendEvent → MSP wendet async an; Consumer-Cursor wandert",
|
|
915
|
+
async () => {
|
|
916
|
+
let dispatchSystemWrite: import("../extra-routes-deps").ExtraRoutesSystemDeps["dispatchSystemWrite"];
|
|
917
|
+
const handle = await boot(undefined, {
|
|
918
|
+
eventDispatcher: { pollIntervalMs: 50 },
|
|
919
|
+
extraRoutes: (_app, deps) => {
|
|
920
|
+
dispatchSystemWrite = deps.dispatchSystemWrite;
|
|
921
|
+
},
|
|
922
|
+
});
|
|
919
923
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
handlerQn: "prod-probe:write:probe-append",
|
|
923
|
-
payload: { aggregateId, note: "dispatched" },
|
|
924
|
-
tenantId: TENANT_ID as import("@cosmicdrift/kumiko-framework/engine").TenantId,
|
|
925
|
-
});
|
|
926
|
-
expect(result.isSuccess).toBe(true);
|
|
924
|
+
// Default-Boot baut den lokalen Dispatcher und start() hat ihn gestartet.
|
|
925
|
+
expect(handle.entrypoint.eventDispatcher).toBeDefined();
|
|
927
926
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
`SELECT note FROM prod_probe_pings WHERE aggregate_id = $1`,
|
|
934
|
-
[aggregateId],
|
|
935
|
-
)) as Array<{ note: string }>;
|
|
936
|
-
return rows[0];
|
|
927
|
+
const aggregateId = crypto.randomUUID();
|
|
928
|
+
const result = await dispatchSystemWrite!({
|
|
929
|
+
handlerQn: "prod-probe:write:probe-append",
|
|
930
|
+
payload: { aggregateId, note: "dispatched" },
|
|
931
|
+
tenantId: TENANT_ID as import("@cosmicdrift/kumiko-framework/engine").TenantId,
|
|
937
932
|
});
|
|
938
|
-
expect(
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
933
|
+
expect(result.isSuccess).toBe(true);
|
|
934
|
+
|
|
935
|
+
const url = ADMIN_URL.replace(/\/[^/]+$/, `/${TEST_DB}`);
|
|
936
|
+
const { db, close } = createDbConnection(url);
|
|
937
|
+
try {
|
|
938
|
+
const row = await pollFor(async () => {
|
|
939
|
+
const rows = (await asRawClient(db).unsafe(
|
|
940
|
+
`SELECT note FROM prod_probe_pings WHERE aggregate_id = $1`,
|
|
941
|
+
[aggregateId],
|
|
942
|
+
)) as Array<{ note: string }>;
|
|
943
|
+
return rows[0];
|
|
944
|
+
});
|
|
945
|
+
expect(row.note).toBe("dispatched");
|
|
946
|
+
|
|
947
|
+
// Consumer-Registrierung + Cursor-Fortschritt — in Prod war diese
|
|
948
|
+
// Tabelle komplett leer, DER Beweis dass nie ein Dispatcher lief.
|
|
949
|
+
// The cursor is committed after the projection row, so poll on the
|
|
950
|
+
// cursor value itself — a registered consumer still reads 0 for a moment.
|
|
951
|
+
const consumers = await pollFor(async () => {
|
|
952
|
+
const rows = (await asRawClient(db).unsafe(
|
|
953
|
+
`SELECT name, last_processed_event_id FROM kumiko_event_consumers
|
|
954
|
+
WHERE name = 'prod-probe:projection:probe-ping-projection'
|
|
955
|
+
OR name LIKE '%probe-ping-projection%'`,
|
|
956
|
+
)) as Array<{ name: string; last_processed_event_id: string | number }>;
|
|
957
|
+
return Number(rows[0]?.last_processed_event_id) > 0 ? rows : undefined;
|
|
958
|
+
});
|
|
959
|
+
expect(consumers.length).toBeGreaterThan(0);
|
|
960
|
+
expect(Number(consumers[0]?.last_processed_event_id)).toBeGreaterThan(0);
|
|
961
|
+
} finally {
|
|
962
|
+
await close();
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
BOOT_AND_POLL_TIMEOUT_MS,
|
|
966
|
+
);
|
|
953
967
|
|
|
954
968
|
test("eventDispatcher.disabled: kein lokaler Dispatcher gebaut", async () => {
|
|
955
969
|
const handle = await boot(undefined, {
|