@dyrected/core 2.5.59 → 2.5.61

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 (39) hide show
  1. package/dist/app-config-9hs8DRED.d.cts +1718 -0
  2. package/dist/app-config-9hs8DRED.d.ts +1718 -0
  3. package/dist/app-config-C0twGIaF.d.cts +1597 -0
  4. package/dist/app-config-C0twGIaF.d.ts +1597 -0
  5. package/dist/app-config-C0uxEU7Q.d.cts +1874 -0
  6. package/dist/app-config-C0uxEU7Q.d.ts +1874 -0
  7. package/dist/app-config-CBOn8IyZ.d.cts +1838 -0
  8. package/dist/app-config-CBOn8IyZ.d.ts +1838 -0
  9. package/dist/app-config-CstocO9M.d.cts +1580 -0
  10. package/dist/app-config-CstocO9M.d.ts +1580 -0
  11. package/dist/app-config-CwaU1de2.d.cts +1868 -0
  12. package/dist/app-config-CwaU1de2.d.ts +1868 -0
  13. package/dist/app-config-DIwvzbpe.d.cts +1555 -0
  14. package/dist/app-config-DIwvzbpe.d.ts +1555 -0
  15. package/dist/app-config-DVdSospO.d.cts +1842 -0
  16. package/dist/app-config-DVdSospO.d.ts +1842 -0
  17. package/dist/app-config-Dkndq5ni.d.cts +1580 -0
  18. package/dist/app-config-Dkndq5ni.d.ts +1580 -0
  19. package/dist/app-config-P-SF5nnR.d.cts +1568 -0
  20. package/dist/app-config-P-SF5nnR.d.ts +1568 -0
  21. package/dist/app-config-ouBRb6Bf.d.cts +1716 -0
  22. package/dist/app-config-ouBRb6Bf.d.ts +1716 -0
  23. package/dist/chunk-2WODKOUB.js +1627 -0
  24. package/dist/chunk-35NM2WPO.js +1658 -0
  25. package/dist/chunk-3CMG2BH2.js +1618 -0
  26. package/dist/chunk-CUOPCOU2.js +1644 -0
  27. package/dist/chunk-DCY7QHRB.js +1637 -0
  28. package/dist/chunk-EOW4YMAV.js +1637 -0
  29. package/dist/chunk-JFA22YFB.js +1625 -0
  30. package/dist/chunk-WF5A6PDJ.js +1618 -0
  31. package/dist/index.cjs +209 -47
  32. package/dist/index.d.cts +79 -4
  33. package/dist/index.d.ts +79 -4
  34. package/dist/index.js +42 -4
  35. package/dist/server.cjs +388 -56
  36. package/dist/server.d.cts +1 -1
  37. package/dist/server.d.ts +1 -1
  38. package/dist/server.js +224 -13
  39. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  LIFECYCLE_EVENT_NAMES: () => LIFECYCLE_EVENT_NAMES,
25
25
  WORKFLOW_HISTORY_COLLECTION: () => WORKFLOW_HISTORY_COLLECTION,
26
26
  availableWorkflowTransitions: () => availableWorkflowTransitions,
27
+ buildSqlOrderBy: () => buildSqlOrderBy,
27
28
  canViewWorkflowDraft: () => canViewWorkflowDraft,
28
29
  createLifecycleEvent: () => createLifecycleEvent,
29
30
  createWorkflowDocument: () => createWorkflowDocument,
@@ -50,6 +51,7 @@ __export(index_exports, {
50
51
  defineRichTextField: () => defineRichTextField,
51
52
  defineRowField: () => defineRowField,
52
53
  defineSelectField: () => defineSelectField,
54
+ defineTab: () => defineTab,
53
55
  defineTextField: () => defineTextField,
54
56
  defineTextareaField: () => defineTextareaField,
55
57
  defineTimeField: () => defineTimeField,
@@ -64,6 +66,7 @@ __export(index_exports, {
64
66
  initializeWorkflowDocument: () => initializeWorkflowDocument,
65
67
  materializeWorkflowDocument: () => materializeWorkflowDocument,
66
68
  normalizeConfig: () => normalizeConfig,
69
+ numericFieldNames: () => numericFieldNames,
67
70
  parseMongoWhere: () => parseMongoWhere,
68
71
  parseSort: () => parseSort,
69
72
  parseSqlWhere: () => parseSqlWhere,
@@ -71,6 +74,7 @@ __export(index_exports, {
71
74
  resolveAdminAuthCollection: () => resolveAdminAuthCollection,
72
75
  runCollectionHooks: () => runCollectionHooks,
73
76
  saveWorkflowDraft: () => saveWorkflowDraft,
77
+ simplePublishingWorkflow: () => simplePublishingWorkflow,
74
78
  transitionWorkflow: () => transitionWorkflow,
75
79
  workflowCapabilities: () => workflowCapabilities
76
80
  });
@@ -109,6 +113,20 @@ function publishingWorkflow() {
109
113
  ]
110
114
  };
111
115
  }
116
+ function simplePublishingWorkflow() {
117
+ return {
118
+ initialState: "draft",
119
+ draftState: "draft",
120
+ states: [
121
+ { name: "draft", label: "Draft", color: "neutral" },
122
+ { name: "published", label: "Published", color: "success", published: true }
123
+ ],
124
+ transitions: [
125
+ { name: "publish", label: "Publish", from: "draft", to: "published" },
126
+ { name: "unpublish", label: "Unpublish", from: "published", to: "draft", unpublish: true }
127
+ ]
128
+ };
129
+ }
112
130
  function publicMetadata(meta) {
113
131
  const { availableTransitions: _availableTransitions, ...safe } = meta;
114
132
  return safe;
@@ -142,9 +160,23 @@ function initializeWorkflowDocument(data, workflow) {
142
160
  __workflow: { state: workflow.initialState, revision: 1 }
143
161
  };
144
162
  }
163
+ function publishedStateName(workflow) {
164
+ return workflow.states.find((state) => state.published)?.name ?? workflow.states[workflow.states.length - 1]?.name ?? workflow.initialState;
165
+ }
145
166
  function materializeWorkflowDocument(doc, workflow, user) {
146
167
  const meta = doc.__workflow;
147
- if (!meta) return doc;
168
+ if (!meta) {
169
+ const { __published: _legacyPublished, __workflow: _legacyWorkflow, ...working2 } = doc;
170
+ const state = publishedStateName(workflow);
171
+ return {
172
+ ...working2,
173
+ _workflow: {
174
+ state,
175
+ revision: 1,
176
+ availableTransitions: availableWorkflowTransitions(workflow, state, user).map((item) => item.name)
177
+ }
178
+ };
179
+ }
148
180
  const { __published, __workflow, ...working } = doc;
149
181
  if (!canViewWorkflowDraft(workflow, user)) {
150
182
  if (!__published || typeof __published !== "object") return null;
@@ -419,6 +451,7 @@ var AUDIT_COLLECTION = {
419
451
  { name: "timestamp", type: "date", label: "Timestamp", required: true },
420
452
  { name: "changes", type: "json", label: "Changes" }
421
453
  ],
454
+ access: { read: () => false, create: () => false, update: () => false, delete: () => false },
422
455
  admin: { hidden: true }
423
456
  };
424
457
  var WORKFLOW_HISTORY_COLLECTION_CONFIG = {
@@ -461,7 +494,7 @@ function normalizeConfig(config) {
461
494
  const collections = config?.collections || [];
462
495
  const globals = config?.globals || [];
463
496
  const needsAudit = collections.some((col) => col.audit);
464
- const needsWorkflow = collections.some((col) => col.workflow);
497
+ const needsWorkflow = collections.some((col) => col.workflow || col.drafts);
465
498
  const adminAuthCollectionSlug = getAdminAuthCollection({
466
499
  collections,
467
500
  adminAuth: config.adminAuth
@@ -523,6 +556,12 @@ function normalizeConfig(config) {
523
556
  if (field.name === "email") {
524
557
  return {
525
558
  ...field,
559
+ // Email is the login identifier. Keep the integrity constraints
560
+ // auth relies on even when the field is explicitly redefined, so a
561
+ // custom `email` field can never silently drop uniqueness.
562
+ required: true,
563
+ unique: true,
564
+ promoted: true,
526
565
  access: {
527
566
  ...field.access || {},
528
567
  update: "!id"
@@ -532,6 +571,9 @@ function normalizeConfig(config) {
532
571
  if (field.name === "password") {
533
572
  return {
534
573
  ...field,
574
+ // Password is required to authenticate; enforce it regardless of
575
+ // how the field was declared.
576
+ required: true,
535
577
  admin: { ...field.admin || {} },
536
578
  access: {
537
579
  ...field.access || {},
@@ -587,8 +629,10 @@ function normalizeConfig(config) {
587
629
  }
588
630
  const updatedFieldNames = new Set(fields.map((f) => f.name));
589
631
  const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
632
+ const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
590
633
  return {
591
634
  ...col,
635
+ workflow,
592
636
  fields: [...fields, ...fieldsToInject]
593
637
  };
594
638
  });
@@ -612,7 +656,7 @@ function normalizeConfig(config) {
612
656
  function assertNever(op, context) {
613
657
  throw new Error(`[dyrected/core] Unhandled where operator "${op}" in ${context}`);
614
658
  }
615
- function parseSqlWhere(where, getJsonField, placeholder = "?") {
659
+ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "LIKE") {
616
660
  const params = [];
617
661
  let pgIndex = 1;
618
662
  function next() {
@@ -674,10 +718,10 @@ function parseSqlWhere(where, getJsonField, placeholder = "?") {
674
718
  return `${c} <= ${next()}`;
675
719
  case "contains":
676
720
  params.push(`%${operand}%`);
677
- return `${c} LIKE ${next()}`;
721
+ return `${c} ${likeOperator} ${next()}`;
678
722
  case "starts_with":
679
723
  params.push(`${operand}%`);
680
- return `${c} LIKE ${next()}`;
724
+ return `${c} ${likeOperator} ${next()}`;
681
725
  case "exists":
682
726
  return operand ? `${c} IS NOT NULL` : `${c} IS NULL`;
683
727
  default:
@@ -782,6 +826,33 @@ function parseSort(sort, fallback = { field: "createdAt", direction: "DESC" }) {
782
826
  }).filter((clause) => Boolean(clause));
783
827
  return clauses.length > 0 ? clauses : [fallback];
784
828
  }
829
+ function numericFieldNames(fields) {
830
+ const names = /* @__PURE__ */ new Set();
831
+ for (const field of fields ?? []) {
832
+ if (field.type === "number" && "name" in field && field.name) {
833
+ names.add(field.name);
834
+ }
835
+ }
836
+ return names;
837
+ }
838
+ var TIMESTAMP_COLUMNS = {
839
+ createdat: "created_at",
840
+ created_at: "created_at",
841
+ updatedat: "updated_at",
842
+ updated_at: "updated_at"
843
+ };
844
+ function buildSqlOrderBy(sort, existingColumns, numericFields, dialect) {
845
+ return parseSort(sort).map(({ field, direction }) => {
846
+ const timestamp = TIMESTAMP_COLUMNS[field.toLowerCase()];
847
+ if (timestamp) return `${dialect.quoteIdent(timestamp)} ${direction}`;
848
+ if (existingColumns.includes(field) && field !== "id" && field !== "data") {
849
+ return `${dialect.quoteIdent(field)} ${direction}`;
850
+ }
851
+ const extracted = dialect.jsonExtract(field);
852
+ const expr = numericFields.has(field) ? dialect.castNumeric(extracted) : extracted;
853
+ return `${expr} ${direction}`;
854
+ }).join(", ");
855
+ }
785
856
 
786
857
  // src/utils/hooks.ts
787
858
  async function runCollectionHooks(hooks, args, options = {}) {
@@ -947,6 +1018,15 @@ async function executeFieldAfterRead(fields, doc, user, db) {
947
1018
  }
948
1019
 
949
1020
  // src/utils/openapi.ts
1021
+ function getCollectionLabels(collection) {
1022
+ return collection.labels || { singular: collection.slug, plural: collection.slug };
1023
+ }
1024
+ function getCollectionTag(collection) {
1025
+ return `Collection: ${getCollectionLabels(collection).plural}`;
1026
+ }
1027
+ function getGlobalTag(global) {
1028
+ return `Global: ${global.label || global.slug}`;
1029
+ }
950
1030
  function generateOpenApi(config) {
951
1031
  const spec = {
952
1032
  openapi: "3.0.0",
@@ -978,15 +1058,7 @@ function generateOpenApi(config) {
978
1058
  },
979
1059
  WorkflowHistoryEntry: {
980
1060
  type: "object",
981
- required: [
982
- "collection",
983
- "documentId",
984
- "transition",
985
- "from",
986
- "to",
987
- "revision",
988
- "createdAt"
989
- ],
1061
+ required: ["collection", "documentId", "transition", "from", "to", "revision", "createdAt"],
990
1062
  properties: {
991
1063
  id: { type: "string" },
992
1064
  collection: { type: "string" },
@@ -1000,6 +1072,21 @@ function generateOpenApi(config) {
1000
1072
  createdAt: { type: "string", format: "date-time" }
1001
1073
  }
1002
1074
  },
1075
+ AuditEntry: {
1076
+ type: "object",
1077
+ required: ["collection", "operation", "timestamp"],
1078
+ properties: {
1079
+ id: { type: "string" },
1080
+ collection: { type: "string" },
1081
+ documentId: { type: "string", nullable: true },
1082
+ operation: { type: "string" },
1083
+ user: { type: "string", nullable: true },
1084
+ timestamp: { type: "string", format: "date-time" },
1085
+ changes: {
1086
+ oneOf: [{ type: "string" }, { type: "object", additionalProperties: true }, { type: "null" }]
1087
+ }
1088
+ }
1089
+ },
1003
1090
  Error: {
1004
1091
  type: "object",
1005
1092
  properties: {
@@ -1088,17 +1175,13 @@ function generateOpenApi(config) {
1088
1175
  get: {
1089
1176
  tags: ["Preferences"],
1090
1177
  summary: "Get an authenticated user preference",
1091
- parameters: [
1092
- { name: "key", in: "path", required: true, schema: { type: "string" } }
1093
- ],
1178
+ parameters: [{ name: "key", in: "path", required: true, schema: { type: "string" } }],
1094
1179
  responses: { 200: { description: "Preference value" } }
1095
1180
  },
1096
1181
  put: {
1097
1182
  tags: ["Preferences"],
1098
1183
  summary: "Set an authenticated user preference",
1099
- parameters: [
1100
- { name: "key", in: "path", required: true, schema: { type: "string" } }
1101
- ],
1184
+ parameters: [{ name: "key", in: "path", required: true, schema: { type: "string" } }],
1102
1185
  requestBody: {
1103
1186
  required: true,
1104
1187
  content: { "application/json": { schema: { type: "object" } } }
@@ -1129,6 +1212,39 @@ function generateOpenApi(config) {
1129
1212
  responses: { 200: { description: "Preview document data" } }
1130
1213
  }
1131
1214
  };
1215
+ spec.paths["/api/audit"] = {
1216
+ get: {
1217
+ tags: ["Audit"],
1218
+ summary: "Get audit entries across all readable audited collections",
1219
+ parameters: [
1220
+ { name: "limit", in: "query", schema: { type: "integer", default: 50, maximum: 100 } },
1221
+ { name: "page", in: "query", schema: { type: "integer", default: 1 } },
1222
+ { name: "where", in: "query", schema: { type: "string" }, description: "JSON filter" },
1223
+ { name: "sort", in: "query", schema: { type: "string", default: "-timestamp" } }
1224
+ ],
1225
+ responses: {
1226
+ 200: {
1227
+ description: "Audit entries",
1228
+ content: {
1229
+ "application/json": {
1230
+ schema: {
1231
+ type: "object",
1232
+ properties: {
1233
+ docs: {
1234
+ type: "array",
1235
+ items: { $ref: "#/components/schemas/AuditEntry" }
1236
+ },
1237
+ total: { type: "integer" },
1238
+ limit: { type: "integer" },
1239
+ page: { type: "integer" }
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ }
1247
+ };
1132
1248
  for (const collection of config.collections) {
1133
1249
  spec.components.schemas[collection.slug] = collectionToSchema(collection);
1134
1250
  }
@@ -1138,10 +1254,11 @@ function generateOpenApi(config) {
1138
1254
  for (const collection of config.collections) {
1139
1255
  const slug = collection.slug;
1140
1256
  const path = `/api/collections/${slug}`;
1141
- const labels = collection.labels || { singular: slug, plural: `${slug}s` };
1257
+ const labels = getCollectionLabels(collection);
1258
+ const collectionTag = getCollectionTag(collection);
1142
1259
  spec.paths[path] = {
1143
1260
  get: {
1144
- tags: ["Collections"],
1261
+ tags: [collectionTag],
1145
1262
  summary: `Find ${labels.plural}`,
1146
1263
  parameters: [
1147
1264
  {
@@ -1190,7 +1307,7 @@ function generateOpenApi(config) {
1190
1307
  }
1191
1308
  },
1192
1309
  post: {
1193
- tags: ["Collections"],
1310
+ tags: [collectionTag],
1194
1311
  summary: `Create ${labels.singular}`,
1195
1312
  requestBody: {
1196
1313
  required: true,
@@ -1214,7 +1331,7 @@ function generateOpenApi(config) {
1214
1331
  };
1215
1332
  spec.paths[`${path}/{id}`] = {
1216
1333
  get: {
1217
- tags: ["Collections"],
1334
+ tags: [collectionTag],
1218
1335
  summary: `Get a single ${labels.singular}`,
1219
1336
  parameters: [
1220
1337
  {
@@ -1236,7 +1353,7 @@ function generateOpenApi(config) {
1236
1353
  }
1237
1354
  },
1238
1355
  patch: {
1239
- tags: ["Collections"],
1356
+ tags: [collectionTag],
1240
1357
  summary: `Update ${labels.singular}`,
1241
1358
  parameters: [
1242
1359
  {
@@ -1266,7 +1383,7 @@ function generateOpenApi(config) {
1266
1383
  }
1267
1384
  },
1268
1385
  delete: {
1269
- tags: ["Collections"],
1386
+ tags: [collectionTag],
1270
1387
  summary: `Delete ${labels.singular}`,
1271
1388
  parameters: [
1272
1389
  {
@@ -1283,7 +1400,7 @@ function generateOpenApi(config) {
1283
1400
  };
1284
1401
  spec.paths[`${path}/delete-many`] = {
1285
1402
  delete: {
1286
- tags: ["Collections"],
1403
+ tags: [collectionTag],
1287
1404
  summary: `Delete multiple ${labels.plural}`,
1288
1405
  requestBody: {
1289
1406
  required: true,
@@ -1302,9 +1419,46 @@ function generateOpenApi(config) {
1302
1419
  responses: { 200: { description: "Deleted and failed document IDs" } }
1303
1420
  }
1304
1421
  };
1422
+ if (collection.audit) {
1423
+ spec.paths[`${path}/__audit`] = {
1424
+ get: {
1425
+ tags: [collectionTag],
1426
+ summary: `Get ${labels.singular} audit entries`,
1427
+ parameters: [
1428
+ { name: "limit", in: "query", schema: { type: "integer", default: 50, maximum: 100 } },
1429
+ { name: "page", in: "query", schema: { type: "integer", default: 1 } },
1430
+ { name: "where", in: "query", schema: { type: "string" }, description: "JSON filter" },
1431
+ { name: "sort", in: "query", schema: { type: "string", default: "-timestamp" } }
1432
+ ],
1433
+ responses: {
1434
+ 200: {
1435
+ description: "Audit entries for this collection",
1436
+ content: {
1437
+ "application/json": {
1438
+ schema: {
1439
+ type: "object",
1440
+ properties: {
1441
+ docs: {
1442
+ type: "array",
1443
+ items: { $ref: "#/components/schemas/AuditEntry" }
1444
+ },
1445
+ total: { type: "integer" },
1446
+ limit: { type: "integer" },
1447
+ page: { type: "integer" }
1448
+ }
1449
+ }
1450
+ }
1451
+ }
1452
+ },
1453
+ 403: { description: "Collection read access denied" },
1454
+ 404: { description: "Audit is not enabled for this collection" }
1455
+ }
1456
+ }
1457
+ };
1458
+ }
1305
1459
  spec.paths[`${path}/seed`] = {
1306
1460
  post: {
1307
- tags: ["Collections"],
1461
+ tags: [collectionTag],
1308
1462
  summary: `Seed initial ${labels.plural}`,
1309
1463
  responses: { 200: { description: "Seeded documents" } }
1310
1464
  }
@@ -1312,12 +1466,12 @@ function generateOpenApi(config) {
1312
1466
  if (collection.upload) {
1313
1467
  spec.paths[`${path}/media`] = {
1314
1468
  get: {
1315
- tags: ["Media"],
1469
+ tags: [collectionTag],
1316
1470
  summary: `List ${labels.plural}`,
1317
1471
  responses: { 200: { description: "Paginated media documents" } }
1318
1472
  },
1319
1473
  post: {
1320
- tags: ["Media"],
1474
+ tags: [collectionTag],
1321
1475
  summary: `Upload ${labels.singular}`,
1322
1476
  requestBody: {
1323
1477
  required: true,
@@ -1336,7 +1490,7 @@ function generateOpenApi(config) {
1336
1490
  };
1337
1491
  spec.paths[`${path}/media/{filename}`] = {
1338
1492
  get: {
1339
- tags: ["Media"],
1493
+ tags: [collectionTag],
1340
1494
  summary: `Serve ${labels.singular} bytes`,
1341
1495
  parameters: [
1342
1496
  {
@@ -1356,7 +1510,7 @@ function generateOpenApi(config) {
1356
1510
  }
1357
1511
  if (collection.auth) {
1358
1512
  const publicAuthPost = (summary) => ({
1359
- tags: ["Authentication"],
1513
+ tags: [collectionTag],
1360
1514
  summary,
1361
1515
  security: [],
1362
1516
  requestBody: {
@@ -1398,14 +1552,14 @@ function generateOpenApi(config) {
1398
1552
  };
1399
1553
  spec.paths[`${path}/logout`] = {
1400
1554
  post: {
1401
- tags: ["Authentication"],
1555
+ tags: [collectionTag],
1402
1556
  summary: `Log out of ${labels.plural}`,
1403
1557
  responses: { 200: { description: "Logged out" } }
1404
1558
  }
1405
1559
  };
1406
1560
  spec.paths[`${path}/init`] = {
1407
1561
  get: {
1408
- tags: ["Authentication"],
1562
+ tags: [collectionTag],
1409
1563
  summary: `Get ${labels.plural} initialization state`,
1410
1564
  security: [],
1411
1565
  responses: { 200: { description: "Initialization state" } }
@@ -1416,14 +1570,14 @@ function generateOpenApi(config) {
1416
1570
  };
1417
1571
  spec.paths[`${path}/me`] = {
1418
1572
  get: {
1419
- tags: ["Authentication"],
1573
+ tags: [collectionTag],
1420
1574
  summary: `Get the current ${labels.singular}`,
1421
1575
  responses: { 200: { description: "Authenticated user" } }
1422
1576
  }
1423
1577
  };
1424
1578
  spec.paths[`${path}/refresh-token`] = {
1425
1579
  post: {
1426
- tags: ["Authentication"],
1580
+ tags: [collectionTag],
1427
1581
  summary: "Refresh an authentication token",
1428
1582
  responses: { 200: { description: "Refreshed token" } }
1429
1583
  }
@@ -1436,7 +1590,7 @@ function generateOpenApi(config) {
1436
1590
  };
1437
1591
  spec.paths[`${path}/invite`] = {
1438
1592
  post: {
1439
- tags: ["Authentication"],
1593
+ tags: [collectionTag],
1440
1594
  summary: `Invite a ${labels.singular}`,
1441
1595
  responses: { 200: { description: "Invitation sent" } }
1442
1596
  }
@@ -1446,7 +1600,7 @@ function generateOpenApi(config) {
1446
1600
  };
1447
1601
  spec.paths[`${path}/{id}/change-password`] = {
1448
1602
  post: {
1449
- tags: ["Authentication"],
1603
+ tags: [collectionTag],
1450
1604
  summary: `Change a ${labels.singular} password`,
1451
1605
  parameters: [
1452
1606
  {
@@ -1463,7 +1617,7 @@ function generateOpenApi(config) {
1463
1617
  if (collection.workflow) {
1464
1618
  spec.paths[`${path}/{id}/transitions/{transition}`] = {
1465
1619
  post: {
1466
- tags: ["Workflows"],
1620
+ tags: [collectionTag],
1467
1621
  summary: `Transition ${labels.singular} workflow`,
1468
1622
  parameters: [
1469
1623
  {
@@ -1508,7 +1662,7 @@ function generateOpenApi(config) {
1508
1662
  };
1509
1663
  spec.paths[`${path}/{id}/workflow-history`] = {
1510
1664
  get: {
1511
- tags: ["Workflows"],
1665
+ tags: [collectionTag],
1512
1666
  summary: `Get ${labels.singular} workflow history`,
1513
1667
  parameters: [
1514
1668
  {
@@ -1554,9 +1708,10 @@ function generateOpenApi(config) {
1554
1708
  for (const global of config.globals) {
1555
1709
  const slug = global.slug;
1556
1710
  const path = `/api/globals/${slug}`;
1711
+ const globalTag = getGlobalTag(global);
1557
1712
  spec.paths[path] = {
1558
1713
  get: {
1559
- tags: ["Globals"],
1714
+ tags: [globalTag],
1560
1715
  summary: `Get ${global.label || slug}`,
1561
1716
  responses: {
1562
1717
  200: {
@@ -1570,7 +1725,7 @@ function generateOpenApi(config) {
1570
1725
  }
1571
1726
  },
1572
1727
  patch: {
1573
- tags: ["Globals"],
1728
+ tags: [globalTag],
1574
1729
  summary: `Update ${global.label || slug}`,
1575
1730
  requestBody: {
1576
1731
  required: true,
@@ -1594,7 +1749,7 @@ function generateOpenApi(config) {
1594
1749
  };
1595
1750
  spec.paths[`${path}/seed`] = {
1596
1751
  post: {
1597
- tags: ["Globals"],
1752
+ tags: [globalTag],
1598
1753
  summary: `Seed ${global.label || slug}`,
1599
1754
  responses: { 200: { description: "Seeded global" } }
1600
1755
  }
@@ -1673,10 +1828,7 @@ function fieldToSchema(field) {
1673
1828
  break;
1674
1829
  case "url":
1675
1830
  schema = {
1676
- oneOf: [
1677
- { type: "string" },
1678
- { type: "object", additionalProperties: true }
1679
- ]
1831
+ oneOf: [{ type: "string" }, { type: "object", additionalProperties: true }]
1680
1832
  };
1681
1833
  break;
1682
1834
  case "icon":
@@ -1812,12 +1964,19 @@ var defineUrlField = createFieldDefiner("url");
1812
1964
  var defineIconField = createFieldDefiner("icon");
1813
1965
  var defineJoinField = createFieldDefiner("join");
1814
1966
  var defineRowField = createFieldDefiner("row");
1967
+ function defineTab(args) {
1968
+ return args.fields.map((field) => ({
1969
+ ...field,
1970
+ admin: { ...field.admin ?? {}, tab: args.label }
1971
+ }));
1972
+ }
1815
1973
  // Annotate the CommonJS export names for ESM import in node:
1816
1974
  0 && (module.exports = {
1817
1975
  LIFECYCLE_EVENTS_COLLECTION,
1818
1976
  LIFECYCLE_EVENT_NAMES,
1819
1977
  WORKFLOW_HISTORY_COLLECTION,
1820
1978
  availableWorkflowTransitions,
1979
+ buildSqlOrderBy,
1821
1980
  canViewWorkflowDraft,
1822
1981
  createLifecycleEvent,
1823
1982
  createWorkflowDocument,
@@ -1844,6 +2003,7 @@ var defineRowField = createFieldDefiner("row");
1844
2003
  defineRichTextField,
1845
2004
  defineRowField,
1846
2005
  defineSelectField,
2006
+ defineTab,
1847
2007
  defineTextField,
1848
2008
  defineTextareaField,
1849
2009
  defineTimeField,
@@ -1858,6 +2018,7 @@ var defineRowField = createFieldDefiner("row");
1858
2018
  initializeWorkflowDocument,
1859
2019
  materializeWorkflowDocument,
1860
2020
  normalizeConfig,
2021
+ numericFieldNames,
1861
2022
  parseMongoWhere,
1862
2023
  parseSort,
1863
2024
  parseSqlWhere,
@@ -1865,6 +2026,7 @@ var defineRowField = createFieldDefiner("row");
1865
2026
  resolveAdminAuthCollection,
1866
2027
  runCollectionHooks,
1867
2028
  saveWorkflowDraft,
2029
+ simplePublishingWorkflow,
1868
2030
  transitionWorkflow,
1869
2031
  workflowCapabilities
1870
2032
  });