@cosmicdrift/kumiko-framework 0.24.1 → 0.26.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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/bun-db/__tests__/extract-table-info.test.ts +22 -1
  3. package/src/bun-db/query.ts +5 -2
  4. package/src/db/__tests__/source-shadow-create.integration.test.ts +54 -0
  5. package/src/db/__tests__/sql-inventory.test.ts +26 -1
  6. package/src/db/__tests__/table-builder-indexes.test.ts +15 -0
  7. package/src/db/__tests__/tenant-db-where-merge.test.ts +81 -0
  8. package/src/db/entity-table-meta.ts +1 -1
  9. package/src/db/queries/event-store.ts +2 -7
  10. package/src/db/sql-inventory.ts +9 -0
  11. package/src/db/tenant-db.ts +5 -2
  12. package/src/engine/__tests__/boot-validator.test.ts +79 -0
  13. package/src/engine/__tests__/post-query-hook.test.ts +6 -6
  14. package/src/engine/__tests__/registry.test.ts +58 -0
  15. package/src/engine/__tests__/search-payload-extension.test.ts +49 -3
  16. package/src/engine/__tests__/unmanaged-table.test.ts +30 -1
  17. package/src/engine/boot-validator/api-ext.ts +1 -5
  18. package/src/engine/boot-validator/screens-nav.ts +18 -2
  19. package/src/engine/registry.ts +43 -12
  20. package/src/engine/types/fields.ts +2 -1
  21. package/src/engine/types/handlers.ts +1 -1
  22. package/src/engine/types/hooks.ts +4 -1
  23. package/src/engine/validate-projection-allowlist.ts +13 -3
  24. package/src/errors/__tests__/classes.test.ts +5 -0
  25. package/src/errors/classes.ts +4 -2
  26. package/src/files/__tests__/file-ref-entity.test.ts +34 -0
  27. package/src/files/__tests__/in-memory-provider.test.ts +94 -0
  28. package/src/logging/__tests__/fallback-logger.test.ts +47 -0
  29. package/src/pipeline/__tests__/dispatcher.test.ts +53 -0
  30. package/src/pipeline/dispatcher.ts +57 -57
  31. package/src/pipeline/system-hooks.ts +17 -5
  32. package/src/random/__tests__/words.test.ts +44 -0
  33. package/src/random/generate.ts +3 -3
  34. package/src/random/words.ts +3 -3
  35. package/src/stack/table-helpers.ts +2 -2
  36. package/src/stack/test-stack.ts +3 -4
  37. package/src/errors/__tests__/field-issue-compat.test.ts +0 -16
@@ -0,0 +1,44 @@
1
+ // words.ts Invarianten-Tests (Phase 1, test-luecken-integration).
2
+ //
3
+ // Schützt die Slug-Wortlisten gegen fehlerhafte Edits (Duplikate,
4
+ // Großbuchstaben, Bindestriche, Müll-Einträge). Bewusst NICHT an die
5
+ // veralteten Inline-Kommentare gebunden ("150 × 150", "4-8 Buchstaben") —
6
+ // real sind es mehr Wörter und weitere Längen; getestet werden die echten
7
+ // harten Invarianten + die dokumentierte Mindest-Diversität.
8
+
9
+ import { describe, expect, test } from "bun:test";
10
+ import { ADJECTIVES, NOUNS } from "../index";
11
+
12
+ const LISTS: ReadonlyArray<readonly [string, readonly string[]]> = [
13
+ ["ADJECTIVES", ADJECTIVES],
14
+ ["NOUNS", NOUNS],
15
+ ];
16
+
17
+ describe("words — Slug-Wortlisten Invarianten", () => {
18
+ for (const [name, list] of LISTS) {
19
+ describe(name, () => {
20
+ test("nur lowercase a-z (keine Ziffern, Bindestriche, Whitespace, Umlaute)", () => {
21
+ const offenders = list.filter((w) => !/^[a-z]+$/.test(w));
22
+ expect(offenders).toEqual([]);
23
+ });
24
+
25
+ test("keine Duplikate", () => {
26
+ const dupes = list.filter((w, i) => list.indexOf(w) !== i);
27
+ expect(dupes).toEqual([]);
28
+ });
29
+
30
+ test("mindestens 150 Wörter (untere Schranke für Combo-Diversität)", () => {
31
+ expect(list.length).toBeGreaterThanOrEqual(150);
32
+ });
33
+
34
+ test("Wortlänge im Müll-Schutz-Korridor 3..12 (fängt leere/Satz-Einträge)", () => {
35
+ const outliers = list.filter((w) => w.length < 3 || w.length > 12);
36
+ expect(outliers).toEqual([]);
37
+ });
38
+ });
39
+ }
40
+
41
+ test("ergibt ≥ 22.500 saubere Kombinationen (dokumentierte Mindest-Diversität)", () => {
42
+ expect(ADJECTIVES.length * NOUNS.length).toBeGreaterThanOrEqual(22_500);
43
+ });
44
+ });
@@ -42,9 +42,9 @@ export type AdjNounNameOptions = {
42
42
  * (no-confusable-Alphabet). Empfohlen 3 Zeichen = 32^3 = 32.768
43
43
  * zusätzliche Combinations pro Wortpaar. */
44
44
  readonly suffix?: { readonly length: number };
45
- /** Custom Adjective-Liste — default ADJECTIVES (150 generic). */
45
+ /** Custom Adjective-Liste — default ADJECTIVES (191 generic). */
46
46
  readonly adjectives?: readonly string[];
47
- /** Custom Noun-Liste — default NOUNS (150 generic). Apps die Domain-
47
+ /** Custom Noun-Liste — default NOUNS (173 generic). Apps die Domain-
48
48
  * spezifische Slugs wollen (z.B. webhook-feature mit eigenen
49
49
  * -receiver/-listener-Substantiven) reichen ihre eigene Liste. */
50
50
  readonly nouns?: readonly string[];
@@ -83,7 +83,7 @@ export type GenerateUniqueNameOptions = {
83
83
  * ist (typisch: DB-Query "select where slug=$1" → row count === 0). */
84
84
  readonly isAvailable: (name: string) => Promise<boolean>;
85
85
  /** Max Versuche OHNE Suffix bevor wir auf suffix-mode wechseln.
86
- * Default 3. Bei 22.500 Default-Combos und ~150 existierenden
86
+ * Default 3. Bei 33.043 Default-Combos und ~150 existierenden
87
87
  * Tenants liegt p(Kollision) < 1% — 3 Versuche reichen weit. */
88
88
  readonly maxCleanAttempts?: number;
89
89
  /** Suffix-Länge bei Kollision-Mode. Default 3 (= 32.768 Combinations
@@ -9,12 +9,12 @@
9
9
  // - Keine Personennamen (cultural appropriation, prominenten-collision)
10
10
  // - Keine Themen-Cluster (kein Wetter-only, kein Geographie-only)
11
11
  // - Lowercase, ASCII-only, keine Bindestriche im Wort selbst
12
- // - 4-8 Buchstaben pro Wort (kompakter Slug)
12
+ // - 3-10 Buchstaben pro Wort (kompakter Slug)
13
13
  // - Aussprechbar in Deutsch UND Englisch (User-Telefon-Support)
14
14
  // - Keine Wörter mit ambiguer Bedeutung in Englisch+Deutsch
15
15
  //
16
- // 150 × 150 = 22.500 saubere Kombinationen — bei einer Standard-
17
- // Hashing-Kollision (Birthday-Bound) reicht das für ~150 Tenants ohne
16
+ // 191 × 173 = 33.043 saubere Kombinationen — bei einer Standard-
17
+ // Hashing-Kollision (Birthday-Bound) reicht das für ~180 Tenants ohne
18
18
  // Suffix. Drüber kommt der Suffix-Pfad in generateUniqueName.
19
19
  //
20
20
  // Erweiterung: weitere Wörter unten anhängen reicht (sortiert ist
@@ -60,8 +60,8 @@ function isMetaShape(v: unknown): v is EntityTableMeta {
60
60
  v !== null &&
61
61
  typeof (v as EntityTableMeta).tableName === "string" &&
62
62
  Array.isArray((v as EntityTableMeta).columns) &&
63
- "indexes" in v &&
64
- "source" in v
63
+ Array.isArray((v as EntityTableMeta).indexes) &&
64
+ ((v as EntityTableMeta).source === "managed" || (v as EntityTableMeta).source === "unmanaged")
65
65
  );
66
66
  }
67
67
 
@@ -171,8 +171,8 @@ export async function setupTestStack(options: TestStackOptions): Promise<TestSta
171
171
  // everything registered via r.projection() — keeps tests from having to
172
172
  // know which projections a feature happens to declare. Two projections
173
173
  // backed by the same physical table (e.g. an alternative apply-shape for
174
- // the same read-model in a test feature) are deduped by Drizzle-table
175
- // reference so drizzle-kit doesn't emit duplicate CREATE TABLE statements.
174
+ // the same read-model in a test feature) are deduped by table reference so
175
+ // we emit only one CREATE TABLE per physical table.
176
176
  const projectionTables: Record<string, unknown> = {};
177
177
  const seenTables = new Set<unknown>();
178
178
  for (const feature of options.features) {
@@ -205,8 +205,7 @@ export async function setupTestStack(options: TestStackOptions): Promise<TestSta
205
205
  // unsafePushTables emits raw CREATE TABLE — fine for ephemeral test DBs but
206
206
  // collides on re-boot against a persistent DB whose projection tables
207
207
  // were created during a previous run. Filter out the ones that already
208
- // exist; drizzle-kit's diff machinery would otherwise emit CREATE for
209
- // them again.
208
+ // exist so the re-boot doesn't fail on duplicate CREATE TABLE.
210
209
  const { tableExists } = await import("../db/schema-inspection");
211
210
  const missing: Record<string, unknown> = {};
212
211
  for (const [key, tbl] of Object.entries(projectionTables)) {
@@ -1,16 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type { FieldIssue as FrameworkFieldIssue } from "@cosmicdrift/kumiko-framework/errors";
3
- import type { FieldIssue as HeadlessFieldIssue } from "@cosmicdrift/kumiko-headless";
4
-
5
- describe("FieldIssue cross-package contract", () => {
6
- test("framework and headless FieldIssue shapes are assignable", () => {
7
- const frameworkIssue: FrameworkFieldIssue = {
8
- path: "title",
9
- code: "too_small",
10
- i18nKey: "errors.validation.too_small",
11
- params: { minimum: 1 },
12
- };
13
- const headlessIssue: HeadlessFieldIssue = frameworkIssue;
14
- expect(headlessIssue.path).toBe("title");
15
- });
16
- });