@actuate-media/cms-core 0.53.0 → 0.55.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/dist/__tests__/api/collection-introspection.test.d.ts +2 -0
- package/dist/__tests__/api/collection-introspection.test.d.ts.map +1 -0
- package/dist/__tests__/api/collection-introspection.test.js +129 -0
- package/dist/__tests__/api/collection-introspection.test.js.map +1 -0
- package/dist/__tests__/api/collections-discovery.test.js +130 -11
- package/dist/__tests__/api/collections-discovery.test.js.map +1 -1
- package/dist/__tests__/auth/password.test.js +29 -11
- package/dist/__tests__/auth/password.test.js.map +1 -1
- package/dist/__tests__/diagnostics/env.test.js +18 -0
- package/dist/__tests__/diagnostics/env.test.js.map +1 -1
- package/dist/api/collection-introspection.d.ts +46 -0
- package/dist/api/collection-introspection.d.ts.map +1 -0
- package/dist/api/collection-introspection.js +82 -0
- package/dist/api/collection-introspection.js.map +1 -0
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +6 -23
- package/dist/api/handlers.js.map +1 -1
- package/dist/db/adapters/mysql.d.ts.map +1 -1
- package/dist/db/adapters/mysql.js +13 -5
- package/dist/db/adapters/mysql.js.map +1 -1
- package/dist/db/adapters/postgres.d.ts.map +1 -1
- package/dist/db/adapters/postgres.js +7 -2
- package/dist/db/adapters/postgres.js.map +1 -1
- package/dist/db/adapters/sqlite.d.ts.map +1 -1
- package/dist/db/adapters/sqlite.js +13 -5
- package/dist/db/adapters/sqlite.js.map +1 -1
- package/dist/diagnostics/env.d.ts.map +1 -1
- package/dist/diagnostics/env.js +10 -3
- package/dist/diagnostics/env.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +2 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +3 -0
- package/dist/middleware.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/0002_media_usage_field_path/migration.sql +11 -0
- package/prisma/schema.prisma +2 -2
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-schema introspection for the `GET /api/cms/collections` discovery
|
|
3
|
+
* endpoint (roadmap WS-B4).
|
|
4
|
+
*
|
|
5
|
+
* Reduces a collection's field map to a compact, **recursive** summary an
|
|
6
|
+
* agent can plan a create/update against — including `array` element
|
|
7
|
+
* subfields and `blocks` variant subfields — WITHOUT shipping the full
|
|
8
|
+
* runtime validators, hooks, or access functions (those can carry closure
|
|
9
|
+
* state and must never reach the unauthenticated discovery surface).
|
|
10
|
+
*
|
|
11
|
+
* The shape is intentionally additive over the flat `{ name, type, required }`
|
|
12
|
+
* summary: relationship targets, select options, and nested subfield schemas
|
|
13
|
+
* appear only when present, so existing consumers keep working.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Defensive recursion cap. Configs are static and authored by the developer,
|
|
17
|
+
* so cycles are not expected, but a malformed/self-referential config must
|
|
18
|
+
* never be able to blow the stack or balloon the response. Five levels is far
|
|
19
|
+
* deeper than any realistic page/array/blocks nesting.
|
|
20
|
+
*/
|
|
21
|
+
const MAX_DEPTH = 5;
|
|
22
|
+
function isFieldMap(value) {
|
|
23
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Summarize a collection's `fields` map into the recursive {@link FieldSummary}
|
|
27
|
+
* array used by the discovery endpoint. Safe on any input — a non-object map
|
|
28
|
+
* (or a missing one) yields `[]`.
|
|
29
|
+
*/
|
|
30
|
+
export function summarizeCollectionFields(fields, depth = 0) {
|
|
31
|
+
if (!isFieldMap(fields))
|
|
32
|
+
return [];
|
|
33
|
+
return Object.entries(fields).map(([name, fieldDef]) => summarizeField(name, fieldDef, depth));
|
|
34
|
+
}
|
|
35
|
+
function summarizeField(name, fieldDef, depth) {
|
|
36
|
+
const summary = {
|
|
37
|
+
name,
|
|
38
|
+
type: typeof fieldDef?.type === 'string' ? fieldDef.type : 'unknown',
|
|
39
|
+
required: fieldDef?.required === true,
|
|
40
|
+
};
|
|
41
|
+
// Relationship target collection(s) — the agent needs this to resolve a
|
|
42
|
+
// slug→id for a relationship field.
|
|
43
|
+
if (typeof fieldDef?.relationTo === 'string' || Array.isArray(fieldDef?.relationTo)) {
|
|
44
|
+
summary.relationTo = fieldDef.relationTo;
|
|
45
|
+
}
|
|
46
|
+
if (fieldDef?.hasMany === true || fieldDef?.multiple === true)
|
|
47
|
+
summary.hasMany = true;
|
|
48
|
+
// Select options (label/value pairs or bare strings) so the agent can fill enums.
|
|
49
|
+
if (Array.isArray(fieldDef?.options))
|
|
50
|
+
summary.options = fieldDef.options;
|
|
51
|
+
// Stop recursing past the cap, but still emit the field's own metadata.
|
|
52
|
+
if (depth >= MAX_DEPTH)
|
|
53
|
+
return summary;
|
|
54
|
+
// `array`: recurse into the element subfields so an agent knows the shape of
|
|
55
|
+
// each row it must author.
|
|
56
|
+
if (summary.type === 'array' && isFieldMap(fieldDef?.fields)) {
|
|
57
|
+
summary.fields = summarizeCollectionFields(fieldDef.fields, depth + 1);
|
|
58
|
+
if (typeof fieldDef.minRows === 'number')
|
|
59
|
+
summary.minRows = fieldDef.minRows;
|
|
60
|
+
if (typeof fieldDef.maxRows === 'number')
|
|
61
|
+
summary.maxRows = fieldDef.maxRows;
|
|
62
|
+
}
|
|
63
|
+
// `blocks`: recurse into each block variant's subfields, keyed by the block
|
|
64
|
+
// slug (the discriminator the agent writes as `blockType`).
|
|
65
|
+
if (summary.type === 'blocks' && Array.isArray(fieldDef?.blocks)) {
|
|
66
|
+
summary.blocks = fieldDef.blocks
|
|
67
|
+
.filter((b) => !!b && typeof b === 'object')
|
|
68
|
+
.map((b) => {
|
|
69
|
+
const slug = typeof b.slug === 'string' ? b.slug : 'unknown';
|
|
70
|
+
const labels = b.labels;
|
|
71
|
+
const block = {
|
|
72
|
+
slug,
|
|
73
|
+
fields: summarizeCollectionFields(b.fields, depth + 1),
|
|
74
|
+
};
|
|
75
|
+
if (typeof labels?.singular === 'string')
|
|
76
|
+
block.label = labels.singular;
|
|
77
|
+
return block;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return summary;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=collection-introspection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-introspection.js","sourceRoot":"","sources":["../../src/api/collection-introspection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAqmC5C,iFAAiF;AACjF,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AA4LD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AA0VD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA25YzD"}
|
package/dist/api/handlers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { summarizeCollectionFields } from './collection-introspection.js';
|
|
1
2
|
import { listDocuments, getDocument, createDocument, updateDocument, updateDocumentSeoFields, assertCollectionAccess, deleteDocument, getGlobal, updateGlobal, buildRelationLookup, } from '../actions.js';
|
|
2
3
|
import { populateRelations, parsePopulateParam } from '../fields/relations.js';
|
|
3
4
|
import { verifyPassword, hashPassword, needsRehash, compareToDummyHash } from '../auth/password.js';
|
|
@@ -1457,30 +1458,12 @@ export function registerCMSRoutes(router) {
|
|
|
1457
1458
|
if (!config)
|
|
1458
1459
|
return errorResponse('CMS not configured', 500);
|
|
1459
1460
|
const collections = Object.entries(config.collections ?? {}).map(([slug, def]) => {
|
|
1460
|
-
const fields = (def?.fields ?? {});
|
|
1461
1461
|
// Reduce each field to `{ name, type, required }` plus, where present,
|
|
1462
|
-
// the relationship target
|
|
1463
|
-
//
|
|
1464
|
-
// WS-B4
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
name,
|
|
1468
|
-
type: typeof fieldDef?.type === 'string' ? fieldDef.type : 'unknown',
|
|
1469
|
-
required: fieldDef?.required === true,
|
|
1470
|
-
};
|
|
1471
|
-
// Relationship target collection(s) — the agent needs this to know
|
|
1472
|
-
// where to resolve a slug→id for a relationship field.
|
|
1473
|
-
if (typeof fieldDef?.relationTo === 'string' || Array.isArray(fieldDef?.relationTo)) {
|
|
1474
|
-
summary.relationTo = fieldDef.relationTo;
|
|
1475
|
-
}
|
|
1476
|
-
if (fieldDef?.hasMany === true || fieldDef?.multiple === true)
|
|
1477
|
-
summary.hasMany = true;
|
|
1478
|
-
// Select options (label/value pairs) so the agent can fill enums.
|
|
1479
|
-
if (Array.isArray(fieldDef?.options)) {
|
|
1480
|
-
summary.options = fieldDef.options;
|
|
1481
|
-
}
|
|
1482
|
-
return summary;
|
|
1483
|
-
});
|
|
1462
|
+
// the relationship target, select options, and (for `array`/`blocks`)
|
|
1463
|
+
// the nested subfield schema — enough for the agent to plan a deep
|
|
1464
|
+
// create/update AND resolve relationship fields (WS-B4 + WS-C C2)
|
|
1465
|
+
// without sending the full validator. See collection-introspection.ts.
|
|
1466
|
+
const fieldSummary = summarizeCollectionFields(def?.fields);
|
|
1484
1467
|
return {
|
|
1485
1468
|
slug,
|
|
1486
1469
|
labels: def?.labels ?? { singular: slug, plural: slug },
|