@kywi-software/core 0.3.0 → 0.3.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.
|
@@ -27,6 +27,16 @@ export declare function loadAdminTypes(db: KywiDb): Promise<Array<ContentTypeCon
|
|
|
27
27
|
* the FULL content model (config + admin), and admin-created `kywi_ct_*` tables
|
|
28
28
|
* are never treated as droppable orphans (#38). Mirrors the admin-type merge in
|
|
29
29
|
* `createKywiApiHandler`'s `rebuildSchemaMaps`.
|
|
30
|
+
*
|
|
31
|
+
* MUST tolerate databases whose schema predates the running package version:
|
|
32
|
+
* `kywi migrate` calls this BEFORE the migration that adds new columns (e.g.
|
|
33
|
+
* 0.3.0's `title_required` / `related_type`), so a drizzle `select()` over the
|
|
34
|
+
* current table objects would throw undefined_column, get mistaken for "DB not
|
|
35
|
+
* reachable", and regenerate the exact unprotected schema #38 exists to prevent.
|
|
36
|
+
* Columns are therefore probed via information_schema and selected explicitly;
|
|
37
|
+
* missing new columns fall back to their defaults. A database with no
|
|
38
|
+
* `kywi_content_types` table at all (fresh install, pre-first-migration)
|
|
39
|
+
* returns [] — there is nothing to protect yet.
|
|
30
40
|
*/
|
|
31
41
|
export declare function loadAdminContentTypes(db: KywiDb): Promise<Array<ContentTypeConfig & {
|
|
32
42
|
source: 'admin';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-type-queries.d.ts","sourceRoot":"","sources":["../../src/db/content-type-queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAY,iBAAiB,
|
|
1
|
+
{"version":3,"file":"content-type-queries.d.ts","sourceRoot":"","sources":["../../src/db/content-type-queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAY,iBAAiB,EAAoC,MAAM,oBAAoB,CAAA;AAGvG,MAAM,MAAM,cAAc,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,iBAAiB,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAY7F;AAED,sFAAsF;AACtF,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAGxG;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAoDzD;AAQD,oFAAoF;AACpF,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpF;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMhF;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CACpC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAUlB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { eq } from 'drizzle-orm';
|
|
1
|
+
import { eq, sql } from 'drizzle-orm';
|
|
2
2
|
import { kywiContentTypes } from '../schema/base.js';
|
|
3
|
-
import { loadAdminFields } from './schema-field-queries.js';
|
|
4
3
|
/**
|
|
5
4
|
* Map an admin content-type row to a ContentTypeConfig. Fields are always empty
|
|
6
5
|
* here — they are loaded separately (loadAdminFields) and merged at startup.
|
|
@@ -24,6 +23,17 @@ export async function loadAdminTypes(db) {
|
|
|
24
23
|
const rows = await db.select().from(kywiContentTypes).orderBy(kywiContentTypes.name);
|
|
25
24
|
return rows.map(rowToContentType);
|
|
26
25
|
}
|
|
26
|
+
/** Column names present on a table, per information_schema. Empty set = table absent. */
|
|
27
|
+
async function existingColumns(db, table) {
|
|
28
|
+
const result = await db.execute(sql `
|
|
29
|
+
SELECT column_name FROM information_schema.columns
|
|
30
|
+
WHERE table_schema = 'public' AND table_name = ${table}
|
|
31
|
+
`);
|
|
32
|
+
const rows = Array.isArray(result)
|
|
33
|
+
? result
|
|
34
|
+
: (result.rows ?? []);
|
|
35
|
+
return new Set(rows.map(r => String(r.column_name)));
|
|
36
|
+
}
|
|
27
37
|
/**
|
|
28
38
|
* Load every admin-created content type with its persisted fields merged in —
|
|
29
39
|
* the schema-generation view of the runtime registry. This is the single lookup
|
|
@@ -31,11 +41,66 @@ export async function loadAdminTypes(db) {
|
|
|
31
41
|
* the FULL content model (config + admin), and admin-created `kywi_ct_*` tables
|
|
32
42
|
* are never treated as droppable orphans (#38). Mirrors the admin-type merge in
|
|
33
43
|
* `createKywiApiHandler`'s `rebuildSchemaMaps`.
|
|
44
|
+
*
|
|
45
|
+
* MUST tolerate databases whose schema predates the running package version:
|
|
46
|
+
* `kywi migrate` calls this BEFORE the migration that adds new columns (e.g.
|
|
47
|
+
* 0.3.0's `title_required` / `related_type`), so a drizzle `select()` over the
|
|
48
|
+
* current table objects would throw undefined_column, get mistaken for "DB not
|
|
49
|
+
* reachable", and regenerate the exact unprotected schema #38 exists to prevent.
|
|
50
|
+
* Columns are therefore probed via information_schema and selected explicitly;
|
|
51
|
+
* missing new columns fall back to their defaults. A database with no
|
|
52
|
+
* `kywi_content_types` table at all (fresh install, pre-first-migration)
|
|
53
|
+
* returns [] — there is nothing to protect yet.
|
|
34
54
|
*/
|
|
35
55
|
export async function loadAdminContentTypes(db) {
|
|
36
|
-
const [
|
|
56
|
+
const [typeCols, fieldCols] = await Promise.all([
|
|
57
|
+
existingColumns(db, 'kywi_content_types'),
|
|
58
|
+
existingColumns(db, 'kywi_content_type_fields'),
|
|
59
|
+
]);
|
|
60
|
+
if (typeCols.size === 0)
|
|
61
|
+
return [];
|
|
62
|
+
const hasTitleRequired = typeCols.has('title_required');
|
|
63
|
+
const typeRows = await db.execute(sql.raw(`SELECT name, label, base_type, icon${hasTitleRequired ? ', title_required' : ''}
|
|
64
|
+
FROM kywi_content_types ORDER BY name`));
|
|
65
|
+
const types = rowsOf(typeRows).map(r => ({
|
|
66
|
+
name: String(r['name']),
|
|
67
|
+
label: String(r['label']),
|
|
68
|
+
baseType: String(r['base_type']),
|
|
69
|
+
...(r['icon'] ? { icon: String(r['icon']) } : {}),
|
|
70
|
+
titleRequired: hasTitleRequired ? Boolean(r['title_required'] ?? true) : true,
|
|
71
|
+
fields: [],
|
|
72
|
+
source: 'admin',
|
|
73
|
+
}));
|
|
74
|
+
if (fieldCols.size === 0)
|
|
75
|
+
return types;
|
|
76
|
+
const hasRelatedType = fieldCols.has('related_type');
|
|
77
|
+
const fieldRows = await db.execute(sql.raw(`SELECT content_type_name, name, type, label, options, required, validation,
|
|
78
|
+
sort_order${hasRelatedType ? ', related_type' : ''}
|
|
79
|
+
FROM kywi_content_type_fields ORDER BY sort_order`));
|
|
80
|
+
const fieldMap = new Map();
|
|
81
|
+
for (const r of rowsOf(fieldRows)) {
|
|
82
|
+
const def = {
|
|
83
|
+
name: String(r['name']),
|
|
84
|
+
type: String(r['type']),
|
|
85
|
+
label: String(r['label']),
|
|
86
|
+
required: Boolean(r['required']),
|
|
87
|
+
...(r['options'] != null && { options: r['options'] }),
|
|
88
|
+
...(r['validation'] != null && { validation: r['validation'] }),
|
|
89
|
+
...(hasRelatedType && r['related_type'] != null && { relatedType: String(r['related_type']) }),
|
|
90
|
+
source: 'admin',
|
|
91
|
+
};
|
|
92
|
+
const key = String(r['content_type_name']);
|
|
93
|
+
const existing = fieldMap.get(key) ?? [];
|
|
94
|
+
existing.push(def);
|
|
95
|
+
fieldMap.set(key, existing);
|
|
96
|
+
}
|
|
37
97
|
return types.map(t => ({ ...t, fields: fieldMap.get(t.name) ?? [] }));
|
|
38
98
|
}
|
|
99
|
+
function rowsOf(result) {
|
|
100
|
+
return Array.isArray(result)
|
|
101
|
+
? result
|
|
102
|
+
: (result.rows ?? []);
|
|
103
|
+
}
|
|
39
104
|
/** Create an admin content type. Throws on a duplicate name (unique constraint). */
|
|
40
105
|
export async function saveAdminType(db, input) {
|
|
41
106
|
await db.insert(kywiContentTypes).values({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-type-queries.js","sourceRoot":"","sources":["../../src/db/content-type-queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"content-type-queries.js","sourceRoot":"","sources":["../../src/db/content-type-queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAErC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAapD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAClD,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAoB;QAClC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,6EAA6E;QAC7E,sDAAsD;QACtD,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI;QACxC,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,OAAO;KAChB,CAAA;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAU;IAC7C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;IACpF,OAAO,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;AACnC,CAAC;AAED,yFAAyF;AACzF,KAAK,UAAU,eAAe,CAAC,EAAU,EAAE,KAAa;IACtD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAA;;qDAEgB,KAAK;GACvD,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAChC,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,CAAE,MAA+B,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IACjD,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAE,CAA8B,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;AACpF,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,EAAU;IAEV,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9C,eAAe,CAAC,EAAE,EAAE,oBAAoB,CAAC;QACzC,eAAe,CAAC,EAAE,EAAE,0BAA0B,CAAC;KAChD,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAElC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IACvD,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAC/B,GAAG,CAAC,GAAG,CACL,sCAAsC,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE;6CACzC,CACxC,CACF,CAAA;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAa;QAC5C,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC7E,MAAM,EAAE,EAAuB;QAC/B,MAAM,EAAE,OAAgB;KACzB,CAAC,CAAC,CAAA;IAEH,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEtC,MAAM,cAAc,GAAG,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IACpD,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAChC,GAAG,CAAC,GAAG,CACL;0BACoB,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE;yDACP,CACpD,CACF,CAAA;IACD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAA;IACrD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,GAAG,GAAoB;YAC3B,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAA4B;YAClD,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACzB,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YAChC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,CAAa,EAAE,CAAC;YAClE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,YAAY,CAAoB,EAAE,CAAC;YAClF,GAAG,CAAC,cAAc,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,IAAI,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;YAC9F,MAAM,EAAE,OAAO;SAChB,CAAA;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAA;QACxC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAClB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;AACvE,CAAC;AAED,SAAS,MAAM,CAAC,MAAe;IAC7B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1B,CAAC,CAAE,MAAyC;QAC5C,CAAC,CAAE,CAAE,MAA+B,CAAC,IAAI,IAAI,EAAE,CAAoC,CAAA;AACvF,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAU,EAAE,KAAqB;IACnE,MAAM,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC;QACvC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;KACzB,CAAC,CAAA;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAU,EAAE,IAAY;IAC5D,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,gBAAgB,CAAC;SACxB,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;SACtC,SAAS,EAAE,CAAA;IACd,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;AAC1B,CAAC;AAMD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,EAAU,EACV,IAAY,EACZ,KAA6B;IAE7B,MAAM,MAAM,GAAG,MAAM,EAAE;SACpB,MAAM,CAAC,gBAAgB,CAAC;SACxB,GAAG,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,SAAS,EAAE,IAAI,IAAI,EAAE;KACtB,CAAC;SACD,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;SACtC,SAAS,EAAE,CAAA;IACd,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;AAC1B,CAAC"}
|