@cosmicdrift/kumiko-framework 0.16.0 → 0.19.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.
- package/package.json +5 -1
- package/src/api/routes.ts +9 -3
- package/src/bun-db/__tests__/query-guards.test.ts +53 -0
- package/src/bun-db/query.ts +162 -18
- package/src/db/assert-exists-in.ts +5 -1
- package/src/db/dialect.ts +11 -6
- package/src/db/entity-table-meta.ts +23 -8
- package/src/db/index.ts +25 -0
- package/src/db/migrate-runner.ts +35 -1
- package/src/db/money.ts +5 -0
- package/src/db/queries/test-stack.ts +3 -1
- package/src/engine/index.ts +1 -1
- package/src/engine/schema-builder.ts +1 -1
- package/src/event-store/__tests__/get-stream-version-perf.integration.test.ts +15 -12
- package/src/event-store/admin-api.ts +5 -4
- package/src/event-store/event-store.ts +3 -2
- package/src/event-store/snapshot.ts +2 -1
- package/src/migrations/__tests__/kumiko-drift.integration.test.ts +161 -0
- package/src/migrations/index.ts +11 -1
- package/src/migrations/kumiko-drift.ts +122 -0
- package/src/schema-cli.ts +228 -0
- package/src/stack/request-helper.ts +39 -4
- package/src/stack/table-helpers.ts +1 -1
- package/src/utils/index.ts +1 -1
- package/src/utils/safe-json.ts +19 -0
|
@@ -4,6 +4,44 @@ import type { SessionUser } from "../engine/types";
|
|
|
4
4
|
|
|
5
5
|
export type BatchCommand = { type: string; payload: unknown };
|
|
6
6
|
|
|
7
|
+
type WireErrorBody = {
|
|
8
|
+
readonly code?: string;
|
|
9
|
+
readonly details?: {
|
|
10
|
+
readonly causeName?: string;
|
|
11
|
+
readonly causeMessage?: string;
|
|
12
|
+
readonly causeStack?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function formatWriteFailure(type: string, body: unknown): string {
|
|
17
|
+
const parsed = body as {
|
|
18
|
+
isSuccess?: boolean;
|
|
19
|
+
error?: WireErrorBody | string;
|
|
20
|
+
};
|
|
21
|
+
const code =
|
|
22
|
+
(typeof parsed.error === "object" ? parsed.error?.code : undefined) ??
|
|
23
|
+
(typeof parsed.error === "string" ? parsed.error : "unknown");
|
|
24
|
+
const details =
|
|
25
|
+
typeof parsed.error === "object" && parsed.error?.details !== undefined
|
|
26
|
+
? parsed.error.details
|
|
27
|
+
: undefined;
|
|
28
|
+
const causeMessage =
|
|
29
|
+
details && typeof details === "object" && "causeMessage" in details
|
|
30
|
+
? String((details as { causeMessage?: unknown }).causeMessage ?? "")
|
|
31
|
+
: "";
|
|
32
|
+
const causeName =
|
|
33
|
+
details && typeof details === "object" && "causeName" in details
|
|
34
|
+
? String((details as { causeName?: unknown }).causeName ?? "")
|
|
35
|
+
: "";
|
|
36
|
+
if (code === "internal_error" && (causeMessage || causeName)) {
|
|
37
|
+
return `Expected write "${type}" to succeed but got error: ${code} (${causeName}: ${causeMessage})`;
|
|
38
|
+
}
|
|
39
|
+
if (details !== undefined) {
|
|
40
|
+
return `Expected write "${type}" to succeed but got error: ${code} — ${JSON.stringify(details)}`;
|
|
41
|
+
}
|
|
42
|
+
return `Expected write "${type}" to succeed but got error: ${code}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
7
45
|
export type RequestHelper = {
|
|
8
46
|
write: (
|
|
9
47
|
type: string,
|
|
@@ -123,10 +161,7 @@ export function createRequestHelper(app: Hono, jwt: JwtHelper): RequestHelper {
|
|
|
123
161
|
// follow the error-contract shape { error: { code, i18nKey, ... } } with
|
|
124
162
|
// a 4xx/5xx status — no isSuccess flag. Detect either.
|
|
125
163
|
if (body.isSuccess !== true) {
|
|
126
|
-
|
|
127
|
-
(typeof body.error === "object" ? body.error?.code : undefined) ??
|
|
128
|
-
(typeof body.error === "string" ? body.error : "unknown");
|
|
129
|
-
throw new Error(`Expected write "${type}" to succeed but got error: ${code}`);
|
|
164
|
+
throw new Error(formatWriteFailure(type, body));
|
|
130
165
|
}
|
|
131
166
|
return body.data as T; // @cast-boundary engine-bridge
|
|
132
167
|
},
|
|
@@ -107,7 +107,7 @@ export async function unsafePushTables(
|
|
|
107
107
|
if (!prevIdxNames.has(idx.name)) {
|
|
108
108
|
const kind = idx.unique ? "UNIQUE INDEX" : "INDEX";
|
|
109
109
|
const colList = idx.columns.map((c) => `"${c}"`).join(", ");
|
|
110
|
-
await createIndexIfNotExists(db, kind, idx.name, meta.tableName, colList);
|
|
110
|
+
await createIndexIfNotExists(db, kind, idx.name, meta.tableName, colList, idx.whereSql);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
} else {
|
package/src/utils/index.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { readPositiveIntEnv } from "./env-parse";
|
|
|
4
4
|
export { generateId } from "./ids";
|
|
5
5
|
export { isPlainObject } from "./is-plain-object";
|
|
6
6
|
export { parseStringArrayJson } from "./parse-string-array-json";
|
|
7
|
-
export { parseJsonOrThrow, parseJsonSafe } from "./safe-json";
|
|
7
|
+
export { parseJsonOrThrow, parseJsonSafe, stringifyJson } from "./safe-json";
|
|
8
8
|
export { parseRoles } from "./serialization";
|
package/src/utils/safe-json.ts
CHANGED
|
@@ -28,3 +28,22 @@ export function parseJsonOrThrow<T>(raw: string, context: string): T {
|
|
|
28
28
|
throw new Error(`Invalid JSON in ${context}: ${msg}`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
/** JSON.stringify that survives BigInt / Temporal values from DB rows. */
|
|
33
|
+
export function stringifyJson(value: unknown): string {
|
|
34
|
+
return JSON.stringify(value, (_key, v) => {
|
|
35
|
+
if (typeof v === "bigint") {
|
|
36
|
+
const asNumber = Number(v);
|
|
37
|
+
if (
|
|
38
|
+
asNumber <= Number.MAX_SAFE_INTEGER &&
|
|
39
|
+
asNumber >= Number.MIN_SAFE_INTEGER &&
|
|
40
|
+
BigInt(asNumber) === v
|
|
41
|
+
) {
|
|
42
|
+
return asNumber;
|
|
43
|
+
}
|
|
44
|
+
return v.toString();
|
|
45
|
+
}
|
|
46
|
+
if (v instanceof Temporal.Instant) return v.toString();
|
|
47
|
+
return v;
|
|
48
|
+
});
|
|
49
|
+
}
|