@dyrected/core 2.5.60 → 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.
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,
@@ -65,6 +66,7 @@ __export(index_exports, {
65
66
  initializeWorkflowDocument: () => initializeWorkflowDocument,
66
67
  materializeWorkflowDocument: () => materializeWorkflowDocument,
67
68
  normalizeConfig: () => normalizeConfig,
69
+ numericFieldNames: () => numericFieldNames,
68
70
  parseMongoWhere: () => parseMongoWhere,
69
71
  parseSort: () => parseSort,
70
72
  parseSqlWhere: () => parseSqlWhere,
@@ -72,6 +74,7 @@ __export(index_exports, {
72
74
  resolveAdminAuthCollection: () => resolveAdminAuthCollection,
73
75
  runCollectionHooks: () => runCollectionHooks,
74
76
  saveWorkflowDraft: () => saveWorkflowDraft,
77
+ simplePublishingWorkflow: () => simplePublishingWorkflow,
75
78
  transitionWorkflow: () => transitionWorkflow,
76
79
  workflowCapabilities: () => workflowCapabilities
77
80
  });
@@ -110,6 +113,20 @@ function publishingWorkflow() {
110
113
  ]
111
114
  };
112
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
+ }
113
130
  function publicMetadata(meta) {
114
131
  const { availableTransitions: _availableTransitions, ...safe } = meta;
115
132
  return safe;
@@ -143,9 +160,23 @@ function initializeWorkflowDocument(data, workflow) {
143
160
  __workflow: { state: workflow.initialState, revision: 1 }
144
161
  };
145
162
  }
163
+ function publishedStateName(workflow) {
164
+ return workflow.states.find((state) => state.published)?.name ?? workflow.states[workflow.states.length - 1]?.name ?? workflow.initialState;
165
+ }
146
166
  function materializeWorkflowDocument(doc, workflow, user) {
147
167
  const meta = doc.__workflow;
148
- 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
+ }
149
180
  const { __published, __workflow, ...working } = doc;
150
181
  if (!canViewWorkflowDraft(workflow, user)) {
151
182
  if (!__published || typeof __published !== "object") return null;
@@ -463,7 +494,7 @@ function normalizeConfig(config) {
463
494
  const collections = config?.collections || [];
464
495
  const globals = config?.globals || [];
465
496
  const needsAudit = collections.some((col) => col.audit);
466
- const needsWorkflow = collections.some((col) => col.workflow);
497
+ const needsWorkflow = collections.some((col) => col.workflow || col.drafts);
467
498
  const adminAuthCollectionSlug = getAdminAuthCollection({
468
499
  collections,
469
500
  adminAuth: config.adminAuth
@@ -598,8 +629,10 @@ function normalizeConfig(config) {
598
629
  }
599
630
  const updatedFieldNames = new Set(fields.map((f) => f.name));
600
631
  const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
632
+ const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
601
633
  return {
602
634
  ...col,
635
+ workflow,
603
636
  fields: [...fields, ...fieldsToInject]
604
637
  };
605
638
  });
@@ -623,7 +656,7 @@ function normalizeConfig(config) {
623
656
  function assertNever(op, context) {
624
657
  throw new Error(`[dyrected/core] Unhandled where operator "${op}" in ${context}`);
625
658
  }
626
- function parseSqlWhere(where, getJsonField, placeholder = "?") {
659
+ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "LIKE") {
627
660
  const params = [];
628
661
  let pgIndex = 1;
629
662
  function next() {
@@ -685,10 +718,10 @@ function parseSqlWhere(where, getJsonField, placeholder = "?") {
685
718
  return `${c} <= ${next()}`;
686
719
  case "contains":
687
720
  params.push(`%${operand}%`);
688
- return `${c} LIKE ${next()}`;
721
+ return `${c} ${likeOperator} ${next()}`;
689
722
  case "starts_with":
690
723
  params.push(`${operand}%`);
691
- return `${c} LIKE ${next()}`;
724
+ return `${c} ${likeOperator} ${next()}`;
692
725
  case "exists":
693
726
  return operand ? `${c} IS NOT NULL` : `${c} IS NULL`;
694
727
  default:
@@ -793,6 +826,33 @@ function parseSort(sort, fallback = { field: "createdAt", direction: "DESC" }) {
793
826
  }).filter((clause) => Boolean(clause));
794
827
  return clauses.length > 0 ? clauses : [fallback];
795
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
+ }
796
856
 
797
857
  // src/utils/hooks.ts
798
858
  async function runCollectionHooks(hooks, args, options = {}) {
@@ -1916,6 +1976,7 @@ function defineTab(args) {
1916
1976
  LIFECYCLE_EVENT_NAMES,
1917
1977
  WORKFLOW_HISTORY_COLLECTION,
1918
1978
  availableWorkflowTransitions,
1979
+ buildSqlOrderBy,
1919
1980
  canViewWorkflowDraft,
1920
1981
  createLifecycleEvent,
1921
1982
  createWorkflowDocument,
@@ -1957,6 +2018,7 @@ function defineTab(args) {
1957
2018
  initializeWorkflowDocument,
1958
2019
  materializeWorkflowDocument,
1959
2020
  normalizeConfig,
2021
+ numericFieldNames,
1960
2022
  parseMongoWhere,
1961
2023
  parseSort,
1962
2024
  parseSqlWhere,
@@ -1964,6 +2026,7 @@ function defineTab(args) {
1964
2026
  resolveAdminAuthCollection,
1965
2027
  runCollectionHooks,
1966
2028
  saveWorkflowDraft,
2029
+ simplePublishingWorkflow,
1967
2030
  transitionWorkflow,
1968
2031
  workflowCapabilities
1969
2032
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as Field, B as Block, D as DyrectedConfig, C as CollectionConfig, A as AdminAuthConfig, P as PublicAdminAuthConfig, W as WorkflowConfig, a as AuthenticatedUser, b as WorkflowTransition, L as LifecycleEventName, c as LifecycleEvent, d as BaseDocument, H as HookRequestContext, e as ArrayField, f as BlocksField, g as BooleanField, h as DateField, i as DateTimeField, E as EmailField, G as GlobalConfig, I as IconField, j as ImageField, J as JoinField, k as JsonField, M as MultiSelectField, N as NumberField, O as ObjectField, R as RadioField, l as RelationshipField, m as RichTextField, n as RowField, S as SelectField, T as TextField, o as TextareaField, p as TimeField, U as UrlField } from './app-config-C0twGIaF.cjs';
2
- export { q as AccessFunction, r as AccessFunctionArgs, s as AccessPolicyResolver, t as AccessResult, u as AccessRule, v as AdminAuthAccessResolution, w as AdminAuthClaimMapping, x as AdminAuthMember, y as AdminAuthProvider, z as AdminAuthProviderMembersConfig, K as AdminAuthProvisioningMode, Q as AdminAuthResolveAccessArgs, V as AdminAuthResolvedIdentity, X as AdminConfig, Y as AdminDashboardComponentSlots, Z as AdminIconName, _ as BaseAdminAuthProvider, $ as BaseFieldAdmin, a0 as BlockVariant, a1 as BooleanFieldAdmin, a2 as CharacterLimitFieldAdmin, a3 as CharacterLimitFieldConfig, a4 as CollectionAfterChangeHook, a5 as CollectionAfterDeleteHook, a6 as CollectionAfterReadHook, a7 as CollectionAfterTransitionHook, a8 as CollectionBeforeChangeHook, a9 as CollectionBeforeDeleteHook, aa as CollectionBeforeReadHook, ab as CollectionBeforeTransitionHook, ac as CollectionListComponentSlots, ad as CustomAdminAuthProvider, ae as DatabaseAdapter, af as DynamicOptionItem, ag as DynamicOptionsConfig, ah as DynamicOptionsResolver, ai as DynamicOptionsResolverArgs, aj as EmailFieldAdmin, ak as FieldAdminHooks, al as FieldAdminOnChangeHook, am as FieldAdminOnChangeHookArgs, an as FieldAdminOptionsHook, ao as FieldAdminOptionsHookArgs, ap as FieldAdminOptionsHookResult, aq as FieldAfterReadHook, ar as FieldAfterReadHookArgs, as as FieldBase, at as FieldBeforeChangeHook, au as FieldBeforeChangeHookArgs, av as FieldHook, aw as FieldHooks, ax as FieldType, ay as FileData, az as GlobalAfterChangeHook, aA as GlobalAfterReadHook, aB as GlobalBeforeChangeHook, aC as GlobalBeforeReadHook, aD as HeadingLevel, aE as HookFunction, aF as IconFieldAdmin, aG as ImageService, aH as LIFECYCLE_EVENT_NAMES, aI as LifecycleEventHandler, aJ as MultiSelectFieldAdmin, aK as NamedAccessPolicy, aL as NumberFieldAdmin, aM as NumberLimitFieldAdmin, aN as NumberLimitFieldConfig, aO as OIDCAdminAuthProvider, aP as PaginatedResult, aQ as PublicAdminAuthProvider, aR as RadioFieldAdmin, aS as ReadonlyDatabaseAdapter, aT as RichTextFeature, aU as RichTextFieldConfig, aV as SelectFieldAdmin, aW as StorageAdapter, aX as TextFieldAdmin, aY as TextareaFieldAdmin, aZ as TypedField, a_ as UploadConfig, a$ as UrlFieldAdmin, b0 as UrlLinkValue, b1 as WordLimitFieldAdmin, b2 as WordLimitFieldConfig, b3 as WorkflowMetadata, b4 as WorkflowRole, b5 as WorkflowState, b6 as WorkflowTransitionContext } from './app-config-C0twGIaF.cjs';
1
+ import { F as Field, B as Block, D as DyrectedConfig, C as CollectionConfig, A as AdminAuthConfig, P as PublicAdminAuthConfig, W as WorkflowConfig, a as AuthenticatedUser, b as WorkflowTransition, L as LifecycleEventName, c as LifecycleEvent, d as BaseDocument, H as HookRequestContext, e as ArrayField, f as BlocksField, g as BooleanField, h as DateField, i as DateTimeField, E as EmailField, G as GlobalConfig, I as IconField, j as ImageField, J as JoinField, k as JsonField, M as MultiSelectField, N as NumberField, O as ObjectField, R as RadioField, l as RelationshipField, m as RichTextField, n as RowField, S as SelectField, T as TextField, o as TextareaField, p as TimeField, U as UrlField } from './app-config-C0uxEU7Q.cjs';
2
+ export { q as AccessFunction, r as AccessFunctionArgs, s as AccessPolicyResolver, t as AccessResult, u as AccessRule, v as AdminAuthAccessResolution, w as AdminAuthClaimMapping, x as AdminAuthMember, y as AdminAuthProvider, z as AdminAuthProviderMembersConfig, K as AdminAuthProvisioningMode, Q as AdminAuthResolveAccessArgs, V as AdminAuthResolvedIdentity, X as AdminConfig, Y as AdminDashboardComponentSlots, Z as AdminIconName, _ as BaseAdminAuthProvider, $ as BaseFieldAdmin, a0 as BlockVariant, a1 as BooleanFieldAdmin, a2 as BooleanFormat, a3 as CharacterLimitFieldAdmin, a4 as CharacterLimitFieldConfig, a5 as CollectionAfterChangeHook, a6 as CollectionAfterDeleteHook, a7 as CollectionAfterReadHook, a8 as CollectionAfterTransitionHook, a9 as CollectionBeforeChangeHook, aa as CollectionBeforeDeleteHook, ab as CollectionBeforeReadHook, ac as CollectionBeforeTransitionHook, ad as CollectionListComponentSlots, ae as CustomAdminAuthProvider, af as DatabaseAdapter, ag as DateFieldAdmin, ah as DateFormat, ai as DisplayTone, aj as DynamicOptionItem, ak as DynamicOptionsConfig, al as DynamicOptionsResolver, am as DynamicOptionsResolverArgs, an as EmailFieldAdmin, ao as FieldAdminHooks, ap as FieldAdminOnChangeHook, aq as FieldAdminOnChangeHookArgs, ar as FieldAdminOptionsHook, as as FieldAdminOptionsHookArgs, at as FieldAdminOptionsHookResult, au as FieldAfterReadHook, av as FieldAfterReadHookArgs, aw as FieldBase, ax as FieldBeforeChangeHook, ay as FieldBeforeChangeHookArgs, az as FieldHook, aA as FieldHooks, aB as FieldType, aC as FileData, aD as GlobalAfterChangeHook, aE as GlobalAfterReadHook, aF as GlobalBeforeChangeHook, aG as GlobalBeforeReadHook, aH as HeadingLevel, aI as HookFunction, aJ as IconFieldAdmin, aK as ImageService, aL as JsonFieldAdmin, aM as JsonFormat, aN as LIFECYCLE_EVENT_NAMES, aO as LifecycleEventHandler, aP as LinkFormat, aQ as MultiSelectFieldAdmin, aR as NamedAccessPolicy, aS as NumberFieldAdmin, aT as NumberFormat, aU as NumberLimitFieldAdmin, aV as NumberLimitFieldConfig, aW as OIDCAdminAuthProvider, aX as OptionFormat, aY as PaginatedResult, aZ as PublicAdminAuthProvider, a_ as RadioFieldAdmin, a$ as ReadonlyDatabaseAdapter, b0 as RichTextFeature, b1 as RichTextFieldConfig, b2 as SelectFieldAdmin, b3 as StorageAdapter, b4 as TextFieldAdmin, b5 as TextFormat, b6 as TextareaFieldAdmin, b7 as TypedField, b8 as UploadConfig, b9 as UrlFieldAdmin, ba as UrlLinkValue, bb as WordLimitFieldAdmin, bc as WordLimitFieldConfig, bd as WorkflowMetadata, be as WorkflowRole, bf as WorkflowState, bg as WorkflowTransitionContext } from './app-config-C0uxEU7Q.cjs';
3
3
  import 'lucide-react';
4
4
 
5
5
  type Prettify<T> = {
@@ -140,7 +140,13 @@ interface SqlWhereResult {
140
140
  * MySQL: (f) => `JSON_UNQUOTE(JSON_EXTRACT(data, '$.${f}'))`
141
141
  * @param placeholder '?' for SQLite/MySQL, 'pg' for auto-incrementing Postgres $1/$2/…
142
142
  */
143
- declare function parseSqlWhere(where: WhereClause, getJsonField: (field: string) => string, placeholder?: '?' | 'pg'): SqlWhereResult;
143
+ declare function parseSqlWhere(where: WhereClause, getJsonField: (field: string) => string, placeholder?: '?' | 'pg',
144
+ /**
145
+ * SQL keyword used for `contains`/`starts_with`. Postgres `LIKE` is
146
+ * case-sensitive, so the Postgres adapter passes `'ILIKE'` to keep substring
147
+ * matching case-insensitive like SQLite and MySQL.
148
+ */
149
+ likeOperator?: 'LIKE' | 'ILIKE'): SqlWhereResult;
144
150
  /**
145
151
  * Translates a WhereClause into a MongoDB filter object.
146
152
  * Handles nested OR/AND via $or/$and, and all operators via $eq/$in/$gt etc.
@@ -153,6 +159,41 @@ interface SortClause {
153
159
  direction: SortDirection;
154
160
  }
155
161
  declare function parseSort(sort: string | undefined, fallback?: SortClause): SortClause[];
162
+ /**
163
+ * Collects the names of top-level fields declared as numbers. SQL adapters use
164
+ * this to decide which JSON-extracted sort fields need a numeric cast.
165
+ */
166
+ declare function numericFieldNames(fields?: Field[]): Set<string>;
167
+ /**
168
+ * Dialect hooks for turning a parsed sort into a SQL `ORDER BY` body.
169
+ * Each SQL adapter supplies its own quoting, JSON extraction, and numeric cast.
170
+ */
171
+ interface SqlSortDialect {
172
+ /** Quote a real column identifier (e.g. `"created_at"` or \`created_at\`). */
173
+ quoteIdent: (identifier: string) => string;
174
+ /** SQL expression that reads a top-level field out of the JSON `data` column. */
175
+ jsonExtract: (field: string) => string;
176
+ /**
177
+ * Wrap a JSON-extracted expression so it sorts numerically instead of
178
+ * lexicographically. Adapters whose JSON extraction already preserves number
179
+ * types (SQLite) can return the expression unchanged.
180
+ */
181
+ castNumeric: (expr: string) => string;
182
+ }
183
+ /**
184
+ * Builds the `ORDER BY` body shared by the SQL adapters.
185
+ *
186
+ * Real (promoted) columns and the `created_at`/`updated_at` timestamps are
187
+ * ordered by their typed column directly. Everything else is read out of the
188
+ * JSON `data` column — and when a field is declared as a `number`, its extracted
189
+ * value is cast so it orders by magnitude rather than as text ("9" < "10").
190
+ *
191
+ * @param sort The raw sort string (e.g. `"-createdAt"`, `"priority,-views"`).
192
+ * @param existingColumns Real column names present on the table.
193
+ * @param numericFields Names of top-level fields declared with `type: 'number'`.
194
+ * @param dialect Adapter-specific quoting, extraction, and numeric cast.
195
+ */
196
+ declare function buildSqlOrderBy(sort: string | undefined, existingColumns: string[], numericFields: Set<string>, dialect: SqlSortDialect): string;
156
197
 
157
198
  type AnyHookFn = (args: any) => any;
158
199
  /**
@@ -201,6 +242,7 @@ declare function generateOpenApi(config: DyrectedConfig): any;
201
242
  declare const WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
202
243
  declare const LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
203
244
  declare function publishingWorkflow(): WorkflowConfig;
245
+ declare function simplePublishingWorkflow(): WorkflowConfig;
204
246
  declare function workflowCapabilities(workflow: WorkflowConfig, user?: AuthenticatedUser): Set<string>;
205
247
  declare function canViewWorkflowDraft(workflow: WorkflowConfig, user?: AuthenticatedUser): boolean;
206
248
  declare function availableWorkflowTransitions(workflow: WorkflowConfig, state: string, user?: AuthenticatedUser): WorkflowTransition[];
@@ -497,4 +539,4 @@ declare function defineTab<const T extends readonly Field[]>(args: {
497
539
  fields: T;
498
540
  }): T;
499
541
 
500
- export { AdminAuthConfig, ArrayField, type AuthDocFields, AuthenticatedUser, BaseDocument, Block, BlocksField, BooleanField, CollectionConfig, DateField, DateTimeField, DyrectedConfig, EmailField, Field, GlobalConfig, HookRequestContext, IconField, ImageField, type InferDocShape, JoinField, JsonField, LIFECYCLE_EVENTS_COLLECTION, LifecycleEvent, LifecycleEventName, MultiSelectField, NumberField, ObjectField, type Prettify, PublicAdminAuthConfig, RadioField, RelationshipField, RichTextField, RowField, SelectField, type SortClause, type SortDirection, type SqlWhereResult, type SystemDocFields, TextField, TextareaField, TimeField, type UploadDocFields, UrlField, WORKFLOW_HISTORY_COLLECTION, type WhereClause, type WhereOperator, type WhereOperatorName, WorkflowConfig, WorkflowTransition, availableWorkflowTransitions, canViewWorkflowDraft, createLifecycleEvent, createWorkflowDocument, defineArrayField, defineBlock, defineBlocksField, defineBooleanField, defineCollection, defineConfig, defineDateField, defineDateTimeField, defineEmailField, defineField, defineGlobal, defineIconField, defineImageField, defineJoinField, defineJsonField, defineMultiSelectField, defineNumberField, defineObjectField, defineRadioField, defineRelationshipField, defineRichTextField, defineRowField, defineSelectField, defineTab, defineTextField, defineTextareaField, defineTimeField, defineUrlField, dispatchLifecycleEvent, dispatchPendingLifecycleEvents, executeFieldAfterRead, executeFieldBeforeChange, generateOpenApi, getAdminAuthCollection, getPublicAdminAuthConfig, initializeWorkflowDocument, materializeWorkflowDocument, normalizeConfig, parseMongoWhere, parseSort, parseSqlWhere, publishingWorkflow, resolveAdminAuthCollection, runCollectionHooks, saveWorkflowDraft, transitionWorkflow, workflowCapabilities };
542
+ export { AdminAuthConfig, ArrayField, type AuthDocFields, AuthenticatedUser, BaseDocument, Block, BlocksField, BooleanField, CollectionConfig, DateField, DateTimeField, DyrectedConfig, EmailField, Field, GlobalConfig, HookRequestContext, IconField, ImageField, type InferDocShape, JoinField, JsonField, LIFECYCLE_EVENTS_COLLECTION, LifecycleEvent, LifecycleEventName, MultiSelectField, NumberField, ObjectField, type Prettify, PublicAdminAuthConfig, RadioField, RelationshipField, RichTextField, RowField, SelectField, type SortClause, type SortDirection, type SqlSortDialect, type SqlWhereResult, type SystemDocFields, TextField, TextareaField, TimeField, type UploadDocFields, UrlField, WORKFLOW_HISTORY_COLLECTION, type WhereClause, type WhereOperator, type WhereOperatorName, WorkflowConfig, WorkflowTransition, availableWorkflowTransitions, buildSqlOrderBy, canViewWorkflowDraft, createLifecycleEvent, createWorkflowDocument, defineArrayField, defineBlock, defineBlocksField, defineBooleanField, defineCollection, defineConfig, defineDateField, defineDateTimeField, defineEmailField, defineField, defineGlobal, defineIconField, defineImageField, defineJoinField, defineJsonField, defineMultiSelectField, defineNumberField, defineObjectField, defineRadioField, defineRelationshipField, defineRichTextField, defineRowField, defineSelectField, defineTab, defineTextField, defineTextareaField, defineTimeField, defineUrlField, dispatchLifecycleEvent, dispatchPendingLifecycleEvents, executeFieldAfterRead, executeFieldBeforeChange, generateOpenApi, getAdminAuthCollection, getPublicAdminAuthConfig, initializeWorkflowDocument, materializeWorkflowDocument, normalizeConfig, numericFieldNames, parseMongoWhere, parseSort, parseSqlWhere, publishingWorkflow, resolveAdminAuthCollection, runCollectionHooks, saveWorkflowDraft, simplePublishingWorkflow, transitionWorkflow, workflowCapabilities };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as Field, B as Block, D as DyrectedConfig, C as CollectionConfig, A as AdminAuthConfig, P as PublicAdminAuthConfig, W as WorkflowConfig, a as AuthenticatedUser, b as WorkflowTransition, L as LifecycleEventName, c as LifecycleEvent, d as BaseDocument, H as HookRequestContext, e as ArrayField, f as BlocksField, g as BooleanField, h as DateField, i as DateTimeField, E as EmailField, G as GlobalConfig, I as IconField, j as ImageField, J as JoinField, k as JsonField, M as MultiSelectField, N as NumberField, O as ObjectField, R as RadioField, l as RelationshipField, m as RichTextField, n as RowField, S as SelectField, T as TextField, o as TextareaField, p as TimeField, U as UrlField } from './app-config-C0twGIaF.js';
2
- export { q as AccessFunction, r as AccessFunctionArgs, s as AccessPolicyResolver, t as AccessResult, u as AccessRule, v as AdminAuthAccessResolution, w as AdminAuthClaimMapping, x as AdminAuthMember, y as AdminAuthProvider, z as AdminAuthProviderMembersConfig, K as AdminAuthProvisioningMode, Q as AdminAuthResolveAccessArgs, V as AdminAuthResolvedIdentity, X as AdminConfig, Y as AdminDashboardComponentSlots, Z as AdminIconName, _ as BaseAdminAuthProvider, $ as BaseFieldAdmin, a0 as BlockVariant, a1 as BooleanFieldAdmin, a2 as CharacterLimitFieldAdmin, a3 as CharacterLimitFieldConfig, a4 as CollectionAfterChangeHook, a5 as CollectionAfterDeleteHook, a6 as CollectionAfterReadHook, a7 as CollectionAfterTransitionHook, a8 as CollectionBeforeChangeHook, a9 as CollectionBeforeDeleteHook, aa as CollectionBeforeReadHook, ab as CollectionBeforeTransitionHook, ac as CollectionListComponentSlots, ad as CustomAdminAuthProvider, ae as DatabaseAdapter, af as DynamicOptionItem, ag as DynamicOptionsConfig, ah as DynamicOptionsResolver, ai as DynamicOptionsResolverArgs, aj as EmailFieldAdmin, ak as FieldAdminHooks, al as FieldAdminOnChangeHook, am as FieldAdminOnChangeHookArgs, an as FieldAdminOptionsHook, ao as FieldAdminOptionsHookArgs, ap as FieldAdminOptionsHookResult, aq as FieldAfterReadHook, ar as FieldAfterReadHookArgs, as as FieldBase, at as FieldBeforeChangeHook, au as FieldBeforeChangeHookArgs, av as FieldHook, aw as FieldHooks, ax as FieldType, ay as FileData, az as GlobalAfterChangeHook, aA as GlobalAfterReadHook, aB as GlobalBeforeChangeHook, aC as GlobalBeforeReadHook, aD as HeadingLevel, aE as HookFunction, aF as IconFieldAdmin, aG as ImageService, aH as LIFECYCLE_EVENT_NAMES, aI as LifecycleEventHandler, aJ as MultiSelectFieldAdmin, aK as NamedAccessPolicy, aL as NumberFieldAdmin, aM as NumberLimitFieldAdmin, aN as NumberLimitFieldConfig, aO as OIDCAdminAuthProvider, aP as PaginatedResult, aQ as PublicAdminAuthProvider, aR as RadioFieldAdmin, aS as ReadonlyDatabaseAdapter, aT as RichTextFeature, aU as RichTextFieldConfig, aV as SelectFieldAdmin, aW as StorageAdapter, aX as TextFieldAdmin, aY as TextareaFieldAdmin, aZ as TypedField, a_ as UploadConfig, a$ as UrlFieldAdmin, b0 as UrlLinkValue, b1 as WordLimitFieldAdmin, b2 as WordLimitFieldConfig, b3 as WorkflowMetadata, b4 as WorkflowRole, b5 as WorkflowState, b6 as WorkflowTransitionContext } from './app-config-C0twGIaF.js';
1
+ import { F as Field, B as Block, D as DyrectedConfig, C as CollectionConfig, A as AdminAuthConfig, P as PublicAdminAuthConfig, W as WorkflowConfig, a as AuthenticatedUser, b as WorkflowTransition, L as LifecycleEventName, c as LifecycleEvent, d as BaseDocument, H as HookRequestContext, e as ArrayField, f as BlocksField, g as BooleanField, h as DateField, i as DateTimeField, E as EmailField, G as GlobalConfig, I as IconField, j as ImageField, J as JoinField, k as JsonField, M as MultiSelectField, N as NumberField, O as ObjectField, R as RadioField, l as RelationshipField, m as RichTextField, n as RowField, S as SelectField, T as TextField, o as TextareaField, p as TimeField, U as UrlField } from './app-config-C0uxEU7Q.js';
2
+ export { q as AccessFunction, r as AccessFunctionArgs, s as AccessPolicyResolver, t as AccessResult, u as AccessRule, v as AdminAuthAccessResolution, w as AdminAuthClaimMapping, x as AdminAuthMember, y as AdminAuthProvider, z as AdminAuthProviderMembersConfig, K as AdminAuthProvisioningMode, Q as AdminAuthResolveAccessArgs, V as AdminAuthResolvedIdentity, X as AdminConfig, Y as AdminDashboardComponentSlots, Z as AdminIconName, _ as BaseAdminAuthProvider, $ as BaseFieldAdmin, a0 as BlockVariant, a1 as BooleanFieldAdmin, a2 as BooleanFormat, a3 as CharacterLimitFieldAdmin, a4 as CharacterLimitFieldConfig, a5 as CollectionAfterChangeHook, a6 as CollectionAfterDeleteHook, a7 as CollectionAfterReadHook, a8 as CollectionAfterTransitionHook, a9 as CollectionBeforeChangeHook, aa as CollectionBeforeDeleteHook, ab as CollectionBeforeReadHook, ac as CollectionBeforeTransitionHook, ad as CollectionListComponentSlots, ae as CustomAdminAuthProvider, af as DatabaseAdapter, ag as DateFieldAdmin, ah as DateFormat, ai as DisplayTone, aj as DynamicOptionItem, ak as DynamicOptionsConfig, al as DynamicOptionsResolver, am as DynamicOptionsResolverArgs, an as EmailFieldAdmin, ao as FieldAdminHooks, ap as FieldAdminOnChangeHook, aq as FieldAdminOnChangeHookArgs, ar as FieldAdminOptionsHook, as as FieldAdminOptionsHookArgs, at as FieldAdminOptionsHookResult, au as FieldAfterReadHook, av as FieldAfterReadHookArgs, aw as FieldBase, ax as FieldBeforeChangeHook, ay as FieldBeforeChangeHookArgs, az as FieldHook, aA as FieldHooks, aB as FieldType, aC as FileData, aD as GlobalAfterChangeHook, aE as GlobalAfterReadHook, aF as GlobalBeforeChangeHook, aG as GlobalBeforeReadHook, aH as HeadingLevel, aI as HookFunction, aJ as IconFieldAdmin, aK as ImageService, aL as JsonFieldAdmin, aM as JsonFormat, aN as LIFECYCLE_EVENT_NAMES, aO as LifecycleEventHandler, aP as LinkFormat, aQ as MultiSelectFieldAdmin, aR as NamedAccessPolicy, aS as NumberFieldAdmin, aT as NumberFormat, aU as NumberLimitFieldAdmin, aV as NumberLimitFieldConfig, aW as OIDCAdminAuthProvider, aX as OptionFormat, aY as PaginatedResult, aZ as PublicAdminAuthProvider, a_ as RadioFieldAdmin, a$ as ReadonlyDatabaseAdapter, b0 as RichTextFeature, b1 as RichTextFieldConfig, b2 as SelectFieldAdmin, b3 as StorageAdapter, b4 as TextFieldAdmin, b5 as TextFormat, b6 as TextareaFieldAdmin, b7 as TypedField, b8 as UploadConfig, b9 as UrlFieldAdmin, ba as UrlLinkValue, bb as WordLimitFieldAdmin, bc as WordLimitFieldConfig, bd as WorkflowMetadata, be as WorkflowRole, bf as WorkflowState, bg as WorkflowTransitionContext } from './app-config-C0uxEU7Q.js';
3
3
  import 'lucide-react';
4
4
 
5
5
  type Prettify<T> = {
@@ -140,7 +140,13 @@ interface SqlWhereResult {
140
140
  * MySQL: (f) => `JSON_UNQUOTE(JSON_EXTRACT(data, '$.${f}'))`
141
141
  * @param placeholder '?' for SQLite/MySQL, 'pg' for auto-incrementing Postgres $1/$2/…
142
142
  */
143
- declare function parseSqlWhere(where: WhereClause, getJsonField: (field: string) => string, placeholder?: '?' | 'pg'): SqlWhereResult;
143
+ declare function parseSqlWhere(where: WhereClause, getJsonField: (field: string) => string, placeholder?: '?' | 'pg',
144
+ /**
145
+ * SQL keyword used for `contains`/`starts_with`. Postgres `LIKE` is
146
+ * case-sensitive, so the Postgres adapter passes `'ILIKE'` to keep substring
147
+ * matching case-insensitive like SQLite and MySQL.
148
+ */
149
+ likeOperator?: 'LIKE' | 'ILIKE'): SqlWhereResult;
144
150
  /**
145
151
  * Translates a WhereClause into a MongoDB filter object.
146
152
  * Handles nested OR/AND via $or/$and, and all operators via $eq/$in/$gt etc.
@@ -153,6 +159,41 @@ interface SortClause {
153
159
  direction: SortDirection;
154
160
  }
155
161
  declare function parseSort(sort: string | undefined, fallback?: SortClause): SortClause[];
162
+ /**
163
+ * Collects the names of top-level fields declared as numbers. SQL adapters use
164
+ * this to decide which JSON-extracted sort fields need a numeric cast.
165
+ */
166
+ declare function numericFieldNames(fields?: Field[]): Set<string>;
167
+ /**
168
+ * Dialect hooks for turning a parsed sort into a SQL `ORDER BY` body.
169
+ * Each SQL adapter supplies its own quoting, JSON extraction, and numeric cast.
170
+ */
171
+ interface SqlSortDialect {
172
+ /** Quote a real column identifier (e.g. `"created_at"` or \`created_at\`). */
173
+ quoteIdent: (identifier: string) => string;
174
+ /** SQL expression that reads a top-level field out of the JSON `data` column. */
175
+ jsonExtract: (field: string) => string;
176
+ /**
177
+ * Wrap a JSON-extracted expression so it sorts numerically instead of
178
+ * lexicographically. Adapters whose JSON extraction already preserves number
179
+ * types (SQLite) can return the expression unchanged.
180
+ */
181
+ castNumeric: (expr: string) => string;
182
+ }
183
+ /**
184
+ * Builds the `ORDER BY` body shared by the SQL adapters.
185
+ *
186
+ * Real (promoted) columns and the `created_at`/`updated_at` timestamps are
187
+ * ordered by their typed column directly. Everything else is read out of the
188
+ * JSON `data` column — and when a field is declared as a `number`, its extracted
189
+ * value is cast so it orders by magnitude rather than as text ("9" < "10").
190
+ *
191
+ * @param sort The raw sort string (e.g. `"-createdAt"`, `"priority,-views"`).
192
+ * @param existingColumns Real column names present on the table.
193
+ * @param numericFields Names of top-level fields declared with `type: 'number'`.
194
+ * @param dialect Adapter-specific quoting, extraction, and numeric cast.
195
+ */
196
+ declare function buildSqlOrderBy(sort: string | undefined, existingColumns: string[], numericFields: Set<string>, dialect: SqlSortDialect): string;
156
197
 
157
198
  type AnyHookFn = (args: any) => any;
158
199
  /**
@@ -201,6 +242,7 @@ declare function generateOpenApi(config: DyrectedConfig): any;
201
242
  declare const WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
202
243
  declare const LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
203
244
  declare function publishingWorkflow(): WorkflowConfig;
245
+ declare function simplePublishingWorkflow(): WorkflowConfig;
204
246
  declare function workflowCapabilities(workflow: WorkflowConfig, user?: AuthenticatedUser): Set<string>;
205
247
  declare function canViewWorkflowDraft(workflow: WorkflowConfig, user?: AuthenticatedUser): boolean;
206
248
  declare function availableWorkflowTransitions(workflow: WorkflowConfig, state: string, user?: AuthenticatedUser): WorkflowTransition[];
@@ -497,4 +539,4 @@ declare function defineTab<const T extends readonly Field[]>(args: {
497
539
  fields: T;
498
540
  }): T;
499
541
 
500
- export { AdminAuthConfig, ArrayField, type AuthDocFields, AuthenticatedUser, BaseDocument, Block, BlocksField, BooleanField, CollectionConfig, DateField, DateTimeField, DyrectedConfig, EmailField, Field, GlobalConfig, HookRequestContext, IconField, ImageField, type InferDocShape, JoinField, JsonField, LIFECYCLE_EVENTS_COLLECTION, LifecycleEvent, LifecycleEventName, MultiSelectField, NumberField, ObjectField, type Prettify, PublicAdminAuthConfig, RadioField, RelationshipField, RichTextField, RowField, SelectField, type SortClause, type SortDirection, type SqlWhereResult, type SystemDocFields, TextField, TextareaField, TimeField, type UploadDocFields, UrlField, WORKFLOW_HISTORY_COLLECTION, type WhereClause, type WhereOperator, type WhereOperatorName, WorkflowConfig, WorkflowTransition, availableWorkflowTransitions, canViewWorkflowDraft, createLifecycleEvent, createWorkflowDocument, defineArrayField, defineBlock, defineBlocksField, defineBooleanField, defineCollection, defineConfig, defineDateField, defineDateTimeField, defineEmailField, defineField, defineGlobal, defineIconField, defineImageField, defineJoinField, defineJsonField, defineMultiSelectField, defineNumberField, defineObjectField, defineRadioField, defineRelationshipField, defineRichTextField, defineRowField, defineSelectField, defineTab, defineTextField, defineTextareaField, defineTimeField, defineUrlField, dispatchLifecycleEvent, dispatchPendingLifecycleEvents, executeFieldAfterRead, executeFieldBeforeChange, generateOpenApi, getAdminAuthCollection, getPublicAdminAuthConfig, initializeWorkflowDocument, materializeWorkflowDocument, normalizeConfig, parseMongoWhere, parseSort, parseSqlWhere, publishingWorkflow, resolveAdminAuthCollection, runCollectionHooks, saveWorkflowDraft, transitionWorkflow, workflowCapabilities };
542
+ export { AdminAuthConfig, ArrayField, type AuthDocFields, AuthenticatedUser, BaseDocument, Block, BlocksField, BooleanField, CollectionConfig, DateField, DateTimeField, DyrectedConfig, EmailField, Field, GlobalConfig, HookRequestContext, IconField, ImageField, type InferDocShape, JoinField, JsonField, LIFECYCLE_EVENTS_COLLECTION, LifecycleEvent, LifecycleEventName, MultiSelectField, NumberField, ObjectField, type Prettify, PublicAdminAuthConfig, RadioField, RelationshipField, RichTextField, RowField, SelectField, type SortClause, type SortDirection, type SqlSortDialect, type SqlWhereResult, type SystemDocFields, TextField, TextareaField, TimeField, type UploadDocFields, UrlField, WORKFLOW_HISTORY_COLLECTION, type WhereClause, type WhereOperator, type WhereOperatorName, WorkflowConfig, WorkflowTransition, availableWorkflowTransitions, buildSqlOrderBy, canViewWorkflowDraft, createLifecycleEvent, createWorkflowDocument, defineArrayField, defineBlock, defineBlocksField, defineBooleanField, defineCollection, defineConfig, defineDateField, defineDateTimeField, defineEmailField, defineField, defineGlobal, defineIconField, defineImageField, defineJoinField, defineJsonField, defineMultiSelectField, defineNumberField, defineObjectField, defineRadioField, defineRelationshipField, defineRichTextField, defineRowField, defineSelectField, defineTab, defineTextField, defineTextareaField, defineTimeField, defineUrlField, dispatchLifecycleEvent, dispatchPendingLifecycleEvents, executeFieldAfterRead, executeFieldBeforeChange, generateOpenApi, getAdminAuthCollection, getPublicAdminAuthConfig, initializeWorkflowDocument, materializeWorkflowDocument, normalizeConfig, numericFieldNames, parseMongoWhere, parseSort, parseSqlWhere, publishingWorkflow, resolveAdminAuthCollection, runCollectionHooks, saveWorkflowDraft, simplePublishingWorkflow, transitionWorkflow, workflowCapabilities };
package/dist/index.js CHANGED
@@ -19,9 +19,10 @@ import {
19
19
  resolveAdminAuthCollection,
20
20
  runCollectionHooks,
21
21
  saveWorkflowDraft,
22
+ simplePublishingWorkflow,
22
23
  transitionWorkflow,
23
24
  workflowCapabilities
24
- } from "./chunk-2WODKOUB.js";
25
+ } from "./chunk-35NM2WPO.js";
25
26
 
26
27
  // src/types/workflows.ts
27
28
  var LIFECYCLE_EVENT_NAMES = [
@@ -35,7 +36,7 @@ var LIFECYCLE_EVENT_NAMES = [
35
36
  function assertNever(op, context) {
36
37
  throw new Error(`[dyrected/core] Unhandled where operator "${op}" in ${context}`);
37
38
  }
38
- function parseSqlWhere(where, getJsonField, placeholder = "?") {
39
+ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = "LIKE") {
39
40
  const params = [];
40
41
  let pgIndex = 1;
41
42
  function next() {
@@ -97,10 +98,10 @@ function parseSqlWhere(where, getJsonField, placeholder = "?") {
97
98
  return `${c} <= ${next()}`;
98
99
  case "contains":
99
100
  params.push(`%${operand}%`);
100
- return `${c} LIKE ${next()}`;
101
+ return `${c} ${likeOperator} ${next()}`;
101
102
  case "starts_with":
102
103
  params.push(`${operand}%`);
103
- return `${c} LIKE ${next()}`;
104
+ return `${c} ${likeOperator} ${next()}`;
104
105
  case "exists":
105
106
  return operand ? `${c} IS NOT NULL` : `${c} IS NULL`;
106
107
  default:
@@ -205,6 +206,33 @@ function parseSort(sort, fallback = { field: "createdAt", direction: "DESC" }) {
205
206
  }).filter((clause) => Boolean(clause));
206
207
  return clauses.length > 0 ? clauses : [fallback];
207
208
  }
209
+ function numericFieldNames(fields) {
210
+ const names = /* @__PURE__ */ new Set();
211
+ for (const field of fields ?? []) {
212
+ if (field.type === "number" && "name" in field && field.name) {
213
+ names.add(field.name);
214
+ }
215
+ }
216
+ return names;
217
+ }
218
+ var TIMESTAMP_COLUMNS = {
219
+ createdat: "created_at",
220
+ created_at: "created_at",
221
+ updatedat: "updated_at",
222
+ updated_at: "updated_at"
223
+ };
224
+ function buildSqlOrderBy(sort, existingColumns, numericFields, dialect) {
225
+ return parseSort(sort).map(({ field, direction }) => {
226
+ const timestamp = TIMESTAMP_COLUMNS[field.toLowerCase()];
227
+ if (timestamp) return `${dialect.quoteIdent(timestamp)} ${direction}`;
228
+ if (existingColumns.includes(field) && field !== "id" && field !== "data") {
229
+ return `${dialect.quoteIdent(field)} ${direction}`;
230
+ }
231
+ const extracted = dialect.jsonExtract(field);
232
+ const expr = numericFields.has(field) ? dialect.castNumeric(extracted) : extracted;
233
+ return `${expr} ${direction}`;
234
+ }).join(", ");
235
+ }
208
236
 
209
237
  // src/index.ts
210
238
  function defineCollection(config) {
@@ -258,6 +286,7 @@ export {
258
286
  LIFECYCLE_EVENT_NAMES,
259
287
  WORKFLOW_HISTORY_COLLECTION,
260
288
  availableWorkflowTransitions,
289
+ buildSqlOrderBy,
261
290
  canViewWorkflowDraft,
262
291
  createLifecycleEvent,
263
292
  createWorkflowDocument,
@@ -299,6 +328,7 @@ export {
299
328
  initializeWorkflowDocument,
300
329
  materializeWorkflowDocument,
301
330
  normalizeConfig,
331
+ numericFieldNames,
302
332
  parseMongoWhere,
303
333
  parseSort,
304
334
  parseSqlWhere,
@@ -306,6 +336,7 @@ export {
306
336
  resolveAdminAuthCollection,
307
337
  runCollectionHooks,
308
338
  saveWorkflowDraft,
339
+ simplePublishingWorkflow,
309
340
  transitionWorkflow,
310
341
  workflowCapabilities
311
342
  };
package/dist/server.cjs CHANGED
@@ -827,6 +827,20 @@ function mergeWhereConstraint(where, constraint) {
827
827
  // src/workflows.ts
828
828
  var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
829
829
  var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
830
+ function simplePublishingWorkflow() {
831
+ return {
832
+ initialState: "draft",
833
+ draftState: "draft",
834
+ states: [
835
+ { name: "draft", label: "Draft", color: "neutral" },
836
+ { name: "published", label: "Published", color: "success", published: true }
837
+ ],
838
+ transitions: [
839
+ { name: "publish", label: "Publish", from: "draft", to: "published" },
840
+ { name: "unpublish", label: "Unpublish", from: "published", to: "draft", unpublish: true }
841
+ ]
842
+ };
843
+ }
830
844
  function publicMetadata(meta) {
831
845
  const { availableTransitions: _availableTransitions, ...safe } = meta;
832
846
  return safe;
@@ -860,9 +874,23 @@ function initializeWorkflowDocument(data, workflow) {
860
874
  __workflow: { state: workflow.initialState, revision: 1 }
861
875
  };
862
876
  }
877
+ function publishedStateName(workflow) {
878
+ return workflow.states.find((state) => state.published)?.name ?? workflow.states[workflow.states.length - 1]?.name ?? workflow.initialState;
879
+ }
863
880
  function materializeWorkflowDocument(doc, workflow, user) {
864
881
  const meta = doc.__workflow;
865
- if (!meta) return doc;
882
+ if (!meta) {
883
+ const { __published: _legacyPublished, __workflow: _legacyWorkflow, ...working2 } = doc;
884
+ const state = publishedStateName(workflow);
885
+ return {
886
+ ...working2,
887
+ _workflow: {
888
+ state,
889
+ revision: 1,
890
+ availableTransitions: availableWorkflowTransitions(workflow, state, user).map((item) => item.name)
891
+ }
892
+ };
893
+ }
866
894
  const { __published, __workflow, ...working } = doc;
867
895
  if (!canViewWorkflowDraft(workflow, user)) {
868
896
  if (!__published || typeof __published !== "object") return null;
@@ -1122,7 +1150,7 @@ var CollectionController = class {
1122
1150
  });
1123
1151
  }
1124
1152
  const readonlyDb = createReadonlyDb(db);
1125
- const limit = Number(c.req.query("limit")) || 10;
1153
+ const limit = Math.min(Number(c.req.query("limit")) || 10, 100);
1126
1154
  const page = Number(c.req.query("page")) || 1;
1127
1155
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 1;
1128
1156
  const sort = c.req.query("sort") || void 0;
@@ -1170,7 +1198,8 @@ var CollectionController = class {
1170
1198
  limit,
1171
1199
  page,
1172
1200
  sort,
1173
- where
1201
+ where,
1202
+ fields: this.collection.fields
1174
1203
  });
1175
1204
  if (result.total === 0 && this.collection.initialData && !where && page === 1) {
1176
1205
  console.log(`[dyrected/core] Auto-seeding collection "${this.collection.slug}" from config.initialData`);
@@ -1182,7 +1211,8 @@ var CollectionController = class {
1182
1211
  limit,
1183
1212
  page,
1184
1213
  sort,
1185
- where
1214
+ where,
1215
+ fields: this.collection.fields
1186
1216
  });
1187
1217
  }
1188
1218
  result.docs = result.docs.map((doc) => this.collection.workflow ? materializeWorkflowDocument(doc, this.collection.workflow, user) : doc).filter((doc) => doc !== null);
@@ -4352,6 +4382,8 @@ function registerRoutes(app, config) {
4352
4382
  }))),
4353
4383
  upload: !!col.upload,
4354
4384
  auth: !!col.auth,
4385
+ audit: !!col.audit,
4386
+ drafts: !!col.drafts,
4355
4387
  admin: col.admin,
4356
4388
  workflow: col.workflow ? {
4357
4389
  initialState: col.workflow.initialState,
@@ -4672,6 +4704,11 @@ function registerRoutes(app, config) {
4672
4704
  app.patch(path, (c) => controller.update(c));
4673
4705
  app.post(`${path}/seed`, (c) => controller.seed(c));
4674
4706
  }
4707
+ if (!process.env.DYRECTED_JWT_SECRET) {
4708
+ console.warn(
4709
+ '[dyrected] DYRECTED_JWT_SECRET is not set \u2014 token-mode live preview is signing with an insecure default. Set DYRECTED_JWT_SECRET before relying on `previewMode: "token"` in production.'
4710
+ );
4711
+ }
4675
4712
  const previewController = new PreviewController();
4676
4713
  app.post("/api/preview-token", requireAuth(config), (c) => previewController.createToken(c));
4677
4714
  app.get("/api/preview-data", (c) => previewController.getData(c));
@@ -4884,7 +4921,7 @@ function normalizeConfig(config) {
4884
4921
  const collections = config?.collections || [];
4885
4922
  const globals = config?.globals || [];
4886
4923
  const needsAudit = collections.some((col) => col.audit);
4887
- const needsWorkflow = collections.some((col) => col.workflow);
4924
+ const needsWorkflow = collections.some((col) => col.workflow || col.drafts);
4888
4925
  const adminAuthCollectionSlug = getAdminAuthCollection({
4889
4926
  collections,
4890
4927
  adminAuth: config.adminAuth
@@ -5019,8 +5056,10 @@ function normalizeConfig(config) {
5019
5056
  }
5020
5057
  const updatedFieldNames = new Set(fields.map((f) => f.name));
5021
5058
  const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
5059
+ const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
5022
5060
  return {
5023
5061
  ...col,
5062
+ workflow,
5024
5063
  fields: [...fields, ...fieldsToInject]
5025
5064
  };
5026
5065
  });
package/dist/server.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as hono_types from 'hono/types';
2
2
  import * as hono from 'hono';
3
3
  import { Hono, Context } from 'hono';
4
- import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-C0twGIaF.cjs';
4
+ import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-C0uxEU7Q.cjs';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
6
6
  import * as hono_utils_types from 'hono/utils/types';
7
7
  import 'lucide-react';
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as hono_types from 'hono/types';
2
2
  import * as hono from 'hono';
3
3
  import { Hono, Context } from 'hono';
4
- import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-C0twGIaF.js';
4
+ import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-C0uxEU7Q.js';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
6
6
  import * as hono_utils_types from 'hono/utils/types';
7
7
  import 'lucide-react';
package/dist/server.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  runCollectionHooks,
14
14
  saveWorkflowDraft,
15
15
  transitionWorkflow
16
- } from "./chunk-2WODKOUB.js";
16
+ } from "./chunk-35NM2WPO.js";
17
17
 
18
18
  // src/app.ts
19
19
  import { Hono } from "hono";
@@ -611,7 +611,7 @@ var CollectionController = class {
611
611
  });
612
612
  }
613
613
  const readonlyDb = createReadonlyDb(db);
614
- const limit = Number(c.req.query("limit")) || 10;
614
+ const limit = Math.min(Number(c.req.query("limit")) || 10, 100);
615
615
  const page = Number(c.req.query("page")) || 1;
616
616
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 1;
617
617
  const sort = c.req.query("sort") || void 0;
@@ -659,7 +659,8 @@ var CollectionController = class {
659
659
  limit,
660
660
  page,
661
661
  sort,
662
- where
662
+ where,
663
+ fields: this.collection.fields
663
664
  });
664
665
  if (result.total === 0 && this.collection.initialData && !where && page === 1) {
665
666
  console.log(`[dyrected/core] Auto-seeding collection "${this.collection.slug}" from config.initialData`);
@@ -671,7 +672,8 @@ var CollectionController = class {
671
672
  limit,
672
673
  page,
673
674
  sort,
674
- where
675
+ where,
676
+ fields: this.collection.fields
675
677
  });
676
678
  }
677
679
  result.docs = result.docs.map((doc) => this.collection.workflow ? materializeWorkflowDocument(doc, this.collection.workflow, user) : doc).filter((doc) => doc !== null);
@@ -2940,6 +2942,8 @@ function registerRoutes(app, config) {
2940
2942
  }))),
2941
2943
  upload: !!col.upload,
2942
2944
  auth: !!col.auth,
2945
+ audit: !!col.audit,
2946
+ drafts: !!col.drafts,
2943
2947
  admin: col.admin,
2944
2948
  workflow: col.workflow ? {
2945
2949
  initialState: col.workflow.initialState,
@@ -3260,6 +3264,11 @@ function registerRoutes(app, config) {
3260
3264
  app.patch(path, (c) => controller.update(c));
3261
3265
  app.post(`${path}/seed`, (c) => controller.seed(c));
3262
3266
  }
3267
+ if (!process.env.DYRECTED_JWT_SECRET) {
3268
+ console.warn(
3269
+ '[dyrected] DYRECTED_JWT_SECRET is not set \u2014 token-mode live preview is signing with an insecure default. Set DYRECTED_JWT_SECRET before relying on `previewMode: "token"` in production.'
3270
+ );
3271
+ }
3263
3272
  const previewController = new PreviewController();
3264
3273
  app.post("/api/preview-token", requireAuth(config), (c) => previewController.createToken(c));
3265
3274
  app.get("/api/preview-data", (c) => previewController.getData(c));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/core",
3
- "version": "2.5.60",
3
+ "version": "2.5.61",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",