@cosmicdrift/kumiko-framework 0.21.1 → 0.23.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 +1 -1
- package/src/__tests__/schema-cli-status.integration.test.ts +95 -0
- package/src/__tests__/schema-cli.integration.test.ts +233 -0
- package/src/db/__tests__/rebuild-marker.test.ts +49 -1
- package/src/db/__tests__/sql-inventory.test.ts +1 -1
- package/src/db/rebuild-marker.ts +11 -5
- package/src/db/schema-inspection.ts +14 -1
- package/src/engine/boot-validator/screens-nav.ts +12 -1
- package/src/engine/index.ts +3 -1
- package/src/engine/types/index.ts +3 -1
- package/src/engine/types/screen.ts +20 -1
- package/src/files/__tests__/file-field-pipeline.integration.test.ts +2 -1
- package/src/files/__tests__/files.integration.test.ts +10 -9
- package/src/files/__tests__/storage-tracking.integration.test.ts +2 -1
- package/src/files/feature.ts +21 -0
- package/src/files/file-ref-entity.ts +30 -0
- package/src/files/file-ref-table.ts +12 -21
- package/src/files/file-routes.ts +47 -81
- package/src/files/index.ts +3 -7
- package/src/files/storage-tracking.ts +36 -6
- package/src/migrations/__tests__/kumiko-drift.integration.test.ts +33 -0
- package/src/migrations/__tests__/kumiko-drift.report.test.ts +92 -0
- package/src/migrations/kumiko-drift.ts +27 -1
- package/src/schema-cli.ts +14 -12
- package/src/seeding/__tests__/entity-seed.test.ts +14 -3
- package/src/seeding/entity-seed.ts +12 -8
- package/src/testing/e2e-generator.ts +4 -0
- package/src/ui-types/index.ts +7 -1
|
@@ -6,7 +6,7 @@ describe("runEventStoreSeed", () => {
|
|
|
6
6
|
let updateCalls = 0;
|
|
7
7
|
let createCalls = 0;
|
|
8
8
|
|
|
9
|
-
const result = await runEventStoreSeed({
|
|
9
|
+
const result = await runEventStoreSeed<string>({
|
|
10
10
|
existing: { id: "agg-1", version: 3 },
|
|
11
11
|
create: async () => {
|
|
12
12
|
createCalls++;
|
|
@@ -26,7 +26,7 @@ describe("runEventStoreSeed", () => {
|
|
|
26
26
|
test('ifExists="update" calls update when row exists', async () => {
|
|
27
27
|
let updateCalls = 0;
|
|
28
28
|
|
|
29
|
-
const result = await runEventStoreSeed({
|
|
29
|
+
const result = await runEventStoreSeed<string>({
|
|
30
30
|
existing: { id: "agg-2", version: 1 },
|
|
31
31
|
ifExists: "update",
|
|
32
32
|
create: async () => ({ id: "new" }),
|
|
@@ -44,7 +44,7 @@ describe("runEventStoreSeed", () => {
|
|
|
44
44
|
test("missing row calls create", async () => {
|
|
45
45
|
let createCalls = 0;
|
|
46
46
|
|
|
47
|
-
const result = await runEventStoreSeed({
|
|
47
|
+
const result = await runEventStoreSeed<string>({
|
|
48
48
|
existing: null,
|
|
49
49
|
create: async () => {
|
|
50
50
|
createCalls++;
|
|
@@ -56,4 +56,15 @@ describe("runEventStoreSeed", () => {
|
|
|
56
56
|
expect(result.id).toBe("created");
|
|
57
57
|
expect(createCalls).toBe(1);
|
|
58
58
|
});
|
|
59
|
+
|
|
60
|
+
test("supports numeric id type via explicit generic", async () => {
|
|
61
|
+
const result = await runEventStoreSeed<number>({
|
|
62
|
+
existing: { id: 42, version: 1 },
|
|
63
|
+
ifExists: "update",
|
|
64
|
+
create: async () => ({ id: 99 }),
|
|
65
|
+
update: async (row) => ({ id: row.id }),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(result.id).toBe(42);
|
|
69
|
+
});
|
|
59
70
|
});
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { DEFAULT_SEED_IF_EXISTS, type SeedIfExists } from "./types";
|
|
2
2
|
|
|
3
|
-
export type EventStoreSeedExisting = {
|
|
4
|
-
readonly id:
|
|
3
|
+
export type EventStoreSeedExisting<TId extends string | number = string> = {
|
|
4
|
+
readonly id: TId;
|
|
5
5
|
readonly version: number;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export type RunEventStoreSeedOptions<
|
|
8
|
+
export type RunEventStoreSeedOptions<
|
|
9
|
+
TId extends string | number = string,
|
|
10
|
+
TExisting extends EventStoreSeedExisting<TId> = EventStoreSeedExisting<TId>,
|
|
11
|
+
> = {
|
|
9
12
|
readonly existing: TExisting | null | undefined;
|
|
10
13
|
readonly ifExists?: SeedIfExists;
|
|
11
|
-
readonly create: () => Promise<{ id:
|
|
12
|
-
readonly update: (existing: TExisting) => Promise<{ id:
|
|
14
|
+
readonly create: () => Promise<{ id: TId }>;
|
|
15
|
+
readonly update: (existing: TExisting) => Promise<{ id: TId }>;
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
/** Shared create-or-skip/update path for event-store boot-seed helpers. */
|
|
16
|
-
export async function runEventStoreSeed<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
export async function runEventStoreSeed<
|
|
20
|
+
TId extends string | number = string,
|
|
21
|
+
TExisting extends EventStoreSeedExisting<TId> = EventStoreSeedExisting<TId>,
|
|
22
|
+
>(opts: RunEventStoreSeedOptions<TId, TExisting>): Promise<{ id: TId }> {
|
|
19
23
|
const ifExists = opts.ifExists ?? DEFAULT_SEED_IF_EXISTS;
|
|
20
24
|
if (opts.existing != null) {
|
|
21
25
|
if (ifExists === "skip") {
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type EntityEditScreenDefinition,
|
|
31
31
|
type EntityListScreenDefinition,
|
|
32
32
|
type FieldDefinition,
|
|
33
|
+
isExtensionEditSection,
|
|
33
34
|
normalizeEditField,
|
|
34
35
|
normalizeListColumn,
|
|
35
36
|
parseQn,
|
|
@@ -234,6 +235,7 @@ function collectRequiredEditFields(
|
|
|
234
235
|
): string[] {
|
|
235
236
|
const out: string[] = [];
|
|
236
237
|
for (const section of screen.layout.sections) {
|
|
238
|
+
if (isExtensionEditSection(section)) continue;
|
|
237
239
|
for (const rawField of section.fields) {
|
|
238
240
|
const { field } = normalizeEditField(rawField);
|
|
239
241
|
const def = entity.fields[field];
|
|
@@ -271,6 +273,7 @@ function buildEditFillOps(
|
|
|
271
273
|
// form; Felder ohne Fixture-Wert (file/image/…) werden übersprungen.
|
|
272
274
|
const ops: EditFillOp[] = [];
|
|
273
275
|
for (const section of screen.layout.sections) {
|
|
276
|
+
if (isExtensionEditSection(section)) continue;
|
|
274
277
|
for (const raw of section.fields) {
|
|
275
278
|
const { field } = normalizeEditField(raw);
|
|
276
279
|
const def = entity.fields[field];
|
|
@@ -317,6 +320,7 @@ function pickIdentifyingForEdit(
|
|
|
317
320
|
: undefined;
|
|
318
321
|
|
|
319
322
|
for (const section of editScreen.layout.sections) {
|
|
323
|
+
if (isExtensionEditSection(section)) continue;
|
|
320
324
|
for (const rawField of section.fields) {
|
|
321
325
|
const { field } = normalizeEditField(rawField);
|
|
322
326
|
if (entity.fields[field]?.type !== "text") continue;
|
package/src/ui-types/index.ts
CHANGED
|
@@ -46,7 +46,9 @@ export type {
|
|
|
46
46
|
ConfigEditScreenDefinition,
|
|
47
47
|
CustomScreenDefinition,
|
|
48
48
|
CustomScreenRoute,
|
|
49
|
+
EditExtensionSection,
|
|
49
50
|
EditFieldSpec,
|
|
51
|
+
EditFieldsSection,
|
|
50
52
|
EditLayout,
|
|
51
53
|
EditSectionSpec,
|
|
52
54
|
EntityEditScreenDefinition,
|
|
@@ -64,6 +66,10 @@ export type {
|
|
|
64
66
|
ScreenSlots,
|
|
65
67
|
ToolbarAction,
|
|
66
68
|
} from "../engine/types/screen";
|
|
67
|
-
export {
|
|
69
|
+
export {
|
|
70
|
+
isExtensionEditSection,
|
|
71
|
+
normalizeEditField,
|
|
72
|
+
normalizeListColumn,
|
|
73
|
+
} from "../engine/types/screen";
|
|
68
74
|
export type { WorkspaceDefinition } from "../engine/types/workspace";
|
|
69
75
|
export type { AppSchema, FeatureSchema, WorkspaceSchema } from "./app-schema";
|