@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.
Files changed (40) hide show
  1. package/dist/__tests__/api/collection-introspection.test.d.ts +2 -0
  2. package/dist/__tests__/api/collection-introspection.test.d.ts.map +1 -0
  3. package/dist/__tests__/api/collection-introspection.test.js +129 -0
  4. package/dist/__tests__/api/collection-introspection.test.js.map +1 -0
  5. package/dist/__tests__/api/collections-discovery.test.js +130 -11
  6. package/dist/__tests__/api/collections-discovery.test.js.map +1 -1
  7. package/dist/__tests__/auth/password.test.js +29 -11
  8. package/dist/__tests__/auth/password.test.js.map +1 -1
  9. package/dist/__tests__/diagnostics/env.test.js +18 -0
  10. package/dist/__tests__/diagnostics/env.test.js.map +1 -1
  11. package/dist/api/collection-introspection.d.ts +46 -0
  12. package/dist/api/collection-introspection.d.ts.map +1 -0
  13. package/dist/api/collection-introspection.js +82 -0
  14. package/dist/api/collection-introspection.js.map +1 -0
  15. package/dist/api/handlers.d.ts.map +1 -1
  16. package/dist/api/handlers.js +6 -23
  17. package/dist/api/handlers.js.map +1 -1
  18. package/dist/db/adapters/mysql.d.ts.map +1 -1
  19. package/dist/db/adapters/mysql.js +13 -5
  20. package/dist/db/adapters/mysql.js.map +1 -1
  21. package/dist/db/adapters/postgres.d.ts.map +1 -1
  22. package/dist/db/adapters/postgres.js +7 -2
  23. package/dist/db/adapters/postgres.js.map +1 -1
  24. package/dist/db/adapters/sqlite.d.ts.map +1 -1
  25. package/dist/db/adapters/sqlite.js +13 -5
  26. package/dist/db/adapters/sqlite.js.map +1 -1
  27. package/dist/diagnostics/env.d.ts.map +1 -1
  28. package/dist/diagnostics/env.js +10 -3
  29. package/dist/diagnostics/env.js.map +1 -1
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/middleware.d.ts +2 -1
  35. package/dist/middleware.d.ts.map +1 -1
  36. package/dist/middleware.js +3 -0
  37. package/dist/middleware.js.map +1 -1
  38. package/package.json +1 -1
  39. package/prisma/migrations/0002_media_usage_field_path/migration.sql +11 -0
  40. 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;AAomC5C,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,CA67YzD"}
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"}
@@ -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 and select options enough for the agent to
1463
- // plan a create/update AND resolve relationship fields (WS-C C2 /
1464
- // WS-B4 overlap) without sending the full validator.
1465
- const fieldSummary = Object.entries(fields).map(([name, fieldDef]) => {
1466
- const summary = {
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 },