@dyrected/core 2.22.0 → 2.24.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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as Field, B as Block, C as CollectionConfig, D as DyrectedConfig, A as AdminAuthConfig, P as PublicAdminAuthConfig, G as GlobalConfig, a as DeclarativeHookExpression, W as WorkflowConfig, b as AuthenticatedUser, c as WorkflowTransition, L as LifecycleEventName, d as LifecycleEvent, e as DatabaseAdapter, f as BaseDocument, H as HookRequestContext, T as TaskRunResult, g as TaskConfig, h as DetailActionOptions, i as DetailAction, j as DetailComputedOptions, k as ComputedHandler, l as DetailComputed, m as DetailCustomOptions, n as DetailCustom, o as DetailDividerOptions, p as DetailDivider, q as DisplayFieldOptions, r as DetailField, s as DetailItem, t as DetailGridOptions, u as DetailGrid, v as DetailRepeatOptions, w as DetailRepeat, x as DetailSectionOptions, y as DetailSection, z as DetailTabOptions, E as DetailTab, I as DetailTabsOptions, J as DetailTabs, K as DetailTextOptions, M as DetailText, N as DetailSchema, O as DyrectedContext, Q as AIConfig, R as AIPIIConfig, S as IdStrategy, U as CompiledNavTree, V as CompiledNavItem, X as UserNavigationPreferences, Y as DefineWorkspaceOptions, Z as ViewConfig, _ as DateTimeField, $ as RelationshipField, a0 as SelectField, a1 as ArrayField, a2 as FieldType, a3 as BooleanField, a4 as AuthConfig, a5 as AccessPolicyResolver, a6 as DateField, a7 as TextField, a8 as EmailField, a9 as IconField, aa as ImageField, ab as JoinField, ac as JsonField, ad as MoneyField, ae as MultiSelectField, af as NumberField, ag as ObjectField, ah as RadioField, ai as RichTextField, aj as RowField, ak as TextareaField, al as TimeField, am as UrlField } from './app-BBHBP-6V.js';
2
- export { an as AIAction, ao as AIActionStatus, ap as AIActionType, aq as AIAuditActionType, ar as AIAuditRecord, as as AIChunk, at as AIMessage, au as AIPIIPattern, av as AIRateLimitResult, aw as AIRateLimitStore, ax as AIThread, ay as AIToolContext, az as AIToolDefinition, aA as AI_ACTIONS_COLLECTION, aB as AI_AUDIT_COLLECTION, aC as AI_CHUNKS_COLLECTION, aD as AI_MESSAGES_COLLECTION, aE as AI_THREADS_COLLECTION, aF as AccessConfig, aG as AccessFunction, aH as AccessFunctionArgs, aI as AccessResult, aJ as AccessRule, aK as ActionConfig, aL as ActionConfigBase, aM as ActionConfigWithHandler, aN as ActionConfigWithMutation, aO as ActionContext, aP as ActionType, aQ as AdminAuthAccessResolution, aR as AdminAuthClaimMapping, aS as AdminAuthMember, aT as AdminAuthProvider, aU as AdminAuthProviderMembersConfig, aV as AdminAuthProvisioningMode, aW as AdminAuthResolveAccessArgs, aX as AdminAuthResolvedIdentity, aY as AdminConfig, aZ as AdminDashboardComponentSlots, a_ as AdminIconName, a$ as AggregateArgs, b0 as AggregateCastType, b1 as AggregateCollectionResult, b2 as AggregateInput, b3 as AggregateOperation, b4 as AggregateResult, b5 as AppTrashConfig, b6 as BaseAdminAuthProvider, b7 as BaseFieldAdmin, b8 as BlockVariant, b9 as BlocksField, ba as BooleanFieldAdmin, bb as BooleanFormat, bc as CURRENT_NAV_PREFERENCES_VERSION, bd as CalendarViewConfig, be as CardsViewConfig, bf as CharacterLimitFieldAdmin, bg as CharacterLimitFieldConfig, bh as CollectionAfterChangeHook, bi as CollectionAfterDeleteHook, bj as CollectionAfterReadHook, bk as CollectionAfterReadHookEntry, bl as CollectionAfterRestoreHook, bm as CollectionAfterTransitionHook, bn as CollectionAfterTrashHook, bo as CollectionBeforeChangeHook, bp as CollectionBeforeChangeHookEntry, bq as CollectionBeforeCommitHook, br as CollectionBeforeDeleteHook, bs as CollectionBeforeReadHook, bt as CollectionBeforeReadHookEntry, bu as CollectionBeforeRestoreHook, bv as CollectionBeforeTransitionHook, bw as CollectionBeforeTrashHook, bx as CollectionEmailConfig, by as CollectionIndex, bz as CollectionListComponentSlots, bA as CollectionRAGConfig, bB as CollectionSchemaResult, bC as CollectionSummaryResult, bD as CollectionViewComponentSlots, bE as CompiledNavGroup, bF as CountOperation, bG as CustomAdminAuthProvider, bH as DEFAULT_USER_NAV_PREFERENCES, bI as DateFieldAdmin, bJ as DateFormat, bK as DefaultValueContext, bL as DefineActionOptions, bM as DefineCalendarViewOptions, bN as DefineCardsViewOptions, bO as DefineGanttViewOptions, bP as DefineKanbanViewOptions, bQ as DefineTableViewOptions, bR as DefineViewBaseOptions, bS as DefineViewOptions, bT as DetailSpan, bU as DisplayTone, bV as DisplayVariant, bW as DistinctCountOperation, bX as DistinctValuesOperation, bY as DynamicOptionItem, bZ as DynamicOptionsConfig, b_ as DynamicOptionsResolver, b$ as DynamicOptionsResolverArgs, c0 as DyrectedAIContext, c1 as DyrectedLoggerConfig, c2 as DyrectedObservabilityConfig, c3 as EmailFieldAdmin, c4 as EmailTemplateArgs, c5 as EmailTemplateResult, c6 as FieldAdminHooks, c7 as FieldAdminOnChangeHook, c8 as FieldAdminOnChangeHookArgs, c9 as FieldAdminOptionsHook, ca as FieldAdminOptionsHookArgs, cb as FieldAdminOptionsHookResult, cc as FieldAfterReadHook, cd as FieldAfterReadHookArgs, ce as FieldBase, cf as FieldBeforeChangeHook, cg as FieldBeforeChangeHookArgs, ch as FieldHook, ci as FieldHooks, cj as FileData, ck as GanttViewConfig, cl as GetDocumentResult, cm as GlobalAfterChangeHook, cn as GlobalAfterReadHook, co as GlobalAfterReadHookEntry, cp as GlobalBeforeChangeHook, cq as GlobalBeforeChangeHookEntry, cr as GlobalBeforeReadHook, cs as GlobalBeforeReadHookEntry, ct as GlobalRAGConfig, cu as GlobalSchemaResult, cv as GlobalSummaryResult, cw as HeadingLevel, cx as HookFunction, cy as IconFieldAdmin, cz as ImageService, cA as ImageTransformOptions, cB as InferAggregateResult, cC as JoinFieldAdmin, cD as JsonFieldAdmin, cE as JsonFormat, cF as KanbanViewConfig, cG as LIFECYCLE_EVENT_NAMES, cH as LifecycleEventHandler, cI as LinkFormat, cJ as MetricColor, cK as MoneyFieldAdmin, cL as MoneyFieldConfig, cM as MultiSelectFieldAdmin, cN as NamedAccessPolicy, cO as NavBadgeConfig, cP as NavGroup, cQ as NumberFieldAdmin, cR as NumberFormat, cS as NumberLimitFieldAdmin, cT as NumberLimitFieldConfig, cU as NumericOperation, cV as NumericUpdateOperator, cW as OIDCAdminAuthProvider, cX as OptionFormat, cY as OutboundEmail, cZ as PaginatedResult, c_ as PinnedNavItemRef, c$ as PublicAdminAuthProvider, d0 as QueryCollectionResult, d1 as RAGSearchResult, d2 as RadioFieldAdmin, d3 as RateLimitConfig, d4 as ReadonlyDatabaseAdapter, d5 as RichTextFeature, d6 as RichTextFieldConfig, d7 as SelectFieldAdmin, d8 as SqlWhereResult, d9 as StorageAdapter, da as TableViewConfig, db as TaskContext, dc as TaskLogger, dd as TaskRunStatus, de as TextFieldAdmin, df as TextFormat, dg as TextareaFieldAdmin, dh as TrashConfig, di as TrustProxyConfig, dj as TypedField, dk as UploadConfig, dl as UrlFieldAdmin, dm as UrlLinkValue, dn as ViewActionFeatures, dp as ViewComponentSlots, dq as ViewConfigBase, dr as ViewLayout, ds as ViewMetric, dt as ViewMetricGroupBy, du as ViewMetricGroupByConfig, dv as ViewMetricTrendConfig, dw as ViewSubMetric, dx as WhereClause, dy as WhereOperator, dz as WhereOperatorName, dA as WordLimitFieldAdmin, dB as WordLimitFieldConfig, dC as WorkflowMetadata, dD as WorkflowOnTransitionContext, dE as WorkflowOnTransitionHook, dF as WorkflowRole, dG as WorkflowState, dH as WorkflowTransitionContext, dI as coerceBooleanWhere, dJ as defineAction, dK as defineCalendarView, dL as defineCardsView, dM as defineGanttView, dN as defineKanbanView, dO as defineTableView, dP as defineView, dQ as defineWorkspace, dR as isAICollection, dS as parseMongoWhere, dT as parseSqlWhere } from './app-BBHBP-6V.js';
1
+ import { F as Field, B as Block, C as CollectionConfig, D as DyrectedConfig, A as AdminAuthConfig, P as PublicAdminAuthConfig, G as GlobalConfig, a as DeclarativeHookExpression, W as WorkflowConfig, b as AuthenticatedUser, c as WorkflowTransition, L as LifecycleEventName, d as LifecycleEvent, e as DatabaseAdapter, f as BaseDocument, H as HookRequestContext, T as TaskRunResult, g as TaskConfig, h as DetailActionOptions, i as DetailAction, j as DetailComputedOptions, k as ComputedHandler, l as DetailComputed, m as DetailCustomOptions, n as DetailCustom, o as DetailDividerOptions, p as DetailDivider, q as DisplayFieldOptions, r as DetailField, s as DetailItem, t as DetailGridOptions, u as DetailGrid, v as DetailRepeatOptions, w as DetailRepeat, x as DetailSectionOptions, y as DetailSection, z as DetailTabOptions, E as DetailTab, I as DetailTabsOptions, J as DetailTabs, K as DetailTextOptions, M as DetailText, N as DetailSchema, O as DyrectedContext, Q as AIConfig, R as AIPIIConfig, S as IdStrategy, U as CompiledNavTree, V as CompiledNavItem, X as UserNavigationPreferences, Y as DefineWorkspaceOptions, Z as ViewConfig, _ as DateTimeField, $ as RelationshipField, a0 as SelectField, a1 as ArrayField, a2 as FieldType, a3 as BooleanField, a4 as AuthConfig, a5 as AccessPolicyResolver, a6 as DateField, a7 as TextField, a8 as EmailField, a9 as IconField, aa as ImageField, ab as JoinField, ac as JsonField, ad as MoneyField, ae as MultiSelectField, af as NumberField, ag as ObjectField, ah as RadioField, ai as RichTextField, aj as RowField, ak as TextareaField, al as TimeField, am as UrlField } from './app-BpQPL2hY.js';
2
+ export { an as AIAction, ao as AIActionStatus, ap as AIActionType, aq as AIAuditActionType, ar as AIAuditRecord, as as AIChunk, at as AIMessage, au as AIPIIPattern, av as AIRateLimitResult, aw as AIRateLimitStore, ax as AIThread, ay as AIToolContext, az as AIToolDefinition, aA as AI_ACTIONS_COLLECTION, aB as AI_AUDIT_COLLECTION, aC as AI_CHUNKS_COLLECTION, aD as AI_MESSAGES_COLLECTION, aE as AI_THREADS_COLLECTION, aF as AccessConfig, aG as AccessFunction, aH as AccessFunctionArgs, aI as AccessResult, aJ as AccessRule, aK as ActionConfig, aL as ActionConfigBase, aM as ActionConfigWithHandler, aN as ActionConfigWithMutation, aO as ActionContext, aP as ActionType, aQ as AdminAuthAccessResolution, aR as AdminAuthClaimMapping, aS as AdminAuthMember, aT as AdminAuthProvider, aU as AdminAuthProviderMembersConfig, aV as AdminAuthProvisioningMode, aW as AdminAuthResolveAccessArgs, aX as AdminAuthResolvedIdentity, aY as AdminConfig, aZ as AdminDashboardComponentSlots, a_ as AdminIconName, a$ as AggregateArgs, b0 as AggregateCastType, b1 as AggregateCollectionResult, b2 as AggregateInput, b3 as AggregateOperation, b4 as AggregateResult, b5 as AppTrashConfig, b6 as BaseAdminAuthProvider, b7 as BaseFieldAdmin, b8 as BlockVariant, b9 as BlocksField, ba as BooleanFieldAdmin, bb as BooleanFormat, bc as CURRENT_NAV_PREFERENCES_VERSION, bd as CUSTOM_VIEW_FETCH_ALL_LIMIT, be as CalendarViewConfig, bf as CardsViewConfig, bg as CharacterLimitFieldAdmin, bh as CharacterLimitFieldConfig, bi as CollectionAfterChangeHook, bj as CollectionAfterDeleteHook, bk as CollectionAfterReadHook, bl as CollectionAfterReadHookEntry, bm as CollectionAfterRestoreHook, bn as CollectionAfterTransitionHook, bo as CollectionAfterTrashHook, bp as CollectionBeforeChangeHook, bq as CollectionBeforeChangeHookEntry, br as CollectionBeforeCommitHook, bs as CollectionBeforeDeleteHook, bt as CollectionBeforeReadHook, bu as CollectionBeforeReadHookEntry, bv as CollectionBeforeRestoreHook, bw as CollectionBeforeTransitionHook, bx as CollectionBeforeTrashHook, by as CollectionEmailConfig, bz as CollectionIndex, bA as CollectionListComponentSlots, bB as CollectionRAGConfig, bC as CollectionSchemaResult, bD as CollectionSummaryResult, bE as CollectionViewComponentSlots, bF as CompiledNavGroup, bG as CountOperation, bH as CustomAdminAuthProvider, bI as CustomViewConfig, bJ as CustomViewDataConfig, bK as DEFAULT_USER_NAV_PREFERENCES, bL as DateFieldAdmin, bM as DateFormat, bN as DefaultValueContext, bO as DefineActionOptions, bP as DefineCalendarViewOptions, bQ as DefineCardsViewOptions, bR as DefineCustomViewOptions, bS as DefineGanttViewOptions, bT as DefineKanbanViewOptions, bU as DefinePageInput, bV as DefineTableViewOptions, bW as DefineViewBaseOptions, bX as DefineViewOptions, bY as DetailSpan, bZ as DisplayTone, b_ as DisplayVariant, b$ as DistinctCountOperation, c0 as DistinctValuesOperation, c1 as DocumentFormComponentSlots, c2 as DynamicOptionItem, c3 as DynamicOptionsConfig, c4 as DynamicOptionsResolver, c5 as DynamicOptionsResolverArgs, c6 as DyrectedAIContext, c7 as DyrectedLoggerConfig, c8 as DyrectedObservabilityConfig, c9 as EmailFieldAdmin, ca as EmailTemplateArgs, cb as EmailTemplateResult, cc as FORM_SHELLS, cd as FieldAdminHooks, ce as FieldAdminOnChangeHook, cf as FieldAdminOnChangeHookArgs, cg as FieldAdminOptionsHook, ch as FieldAdminOptionsHookArgs, ci as FieldAdminOptionsHookResult, cj as FieldAfterReadHook, ck as FieldAfterReadHookArgs, cl as FieldBase, cm as FieldBeforeChangeHook, cn as FieldBeforeChangeHookArgs, co as FieldHook, cp as FieldHooks, cq as FileData, cr as FormShell, cs as GanttViewConfig, ct as GetDocumentResult, cu as GlobalAfterChangeHook, cv as GlobalAfterReadHook, cw as GlobalAfterReadHookEntry, cx as GlobalBeforeChangeHook, cy as GlobalBeforeChangeHookEntry, cz as GlobalBeforeReadHook, cA as GlobalBeforeReadHookEntry, cB as GlobalFormComponentSlots, cC as GlobalRAGConfig, cD as GlobalSchemaResult, cE as GlobalSummaryResult, cF as HeadingLevel, cG as HookFunction, cH as IconFieldAdmin, cI as ImageService, cJ as ImageTransformOptions, cK as InferAggregateResult, cL as JoinFieldAdmin, cM as JsonFieldAdmin, cN as JsonFormat, cO as KanbanViewConfig, cP as LIFECYCLE_EVENT_NAMES, cQ as LifecycleEventHandler, cR as LinkFormat, cS as MetricColor, cT as MoneyFieldAdmin, cU as MoneyFieldConfig, cV as MultiSelectFieldAdmin, cW as NamedAccessPolicy, cX as NavBadgeConfig, cY as NavGroup, cZ as NumberFieldAdmin, c_ as NumberFormat, c$ as NumberLimitFieldAdmin, d0 as NumberLimitFieldConfig, d1 as NumericOperation, d2 as NumericUpdateOperator, d3 as OIDCAdminAuthProvider, d4 as OptionFormat, d5 as OutboundEmail, d6 as PageAction, d7 as PageBreadcrumbItem, d8 as PageConfig, d9 as PaginatedResult, da as PinnedNavItemRef, db as PublicAdminAuthProvider, dc as QueryCollectionResult, dd as RAGSearchResult, de as RadioFieldAdmin, df as RateLimitConfig, dg as ReadonlyDatabaseAdapter, dh as RichTextFeature, di as RichTextFieldConfig, dj as SelectFieldAdmin, dk as SqlWhereResult, dl as StorageAdapter, dm as TableViewConfig, dn as TaskContext, dp as TaskLogger, dq as TaskRunStatus, dr as TextFieldAdmin, ds as TextFormat, dt as TextareaFieldAdmin, du as TrashConfig, dv as TrustProxyConfig, dw as TypedField, dx as UploadConfig, dy as UrlFieldAdmin, dz as UrlLinkValue, dA as ViewActionFeatures, dB as ViewComponentSlots, dC as ViewConfigBase, dD as ViewLayout, dE as ViewMetric, dF as ViewMetricGroupBy, dG as ViewMetricGroupByConfig, dH as ViewMetricTrendConfig, dI as ViewSubMetric, dJ as WhereClause, dK as WhereOperator, dL as WhereOperatorName, dM as WordLimitFieldAdmin, dN as WordLimitFieldConfig, dO as WorkflowMetadata, dP as WorkflowOnTransitionContext, dQ as WorkflowOnTransitionHook, dR as WorkflowRole, dS as WorkflowState, dT as WorkflowTransitionContext, dU as coerceBooleanWhere, dV as defineAction, dW as defineCalendarView, dX as defineCardsView, dY as defineCustomView, dZ as defineGanttView, d_ as defineKanbanView, d$ as definePage, e0 as defineTableView, e1 as defineView, e2 as defineWorkspace, e3 as isAICollection, e4 as parseMongoWhere, e5 as parseSqlWhere } from './app-BpQPL2hY.js';
3
3
  import jexl from 'jexl';
4
4
  import { Context } from 'hono';
5
5
  export { z } from 'zod';
@@ -280,7 +280,7 @@ interface DeclarativePreviewValidationIssue {
280
280
  }
281
281
  interface ConfigDiagnostic {
282
282
  severity: "error" | "warning";
283
- source: "declarativeHook" | "declarativeAccess" | "adminCondition" | "previewUrl";
283
+ source: "declarativeHook" | "declarativeAccess" | "adminCondition" | "previewUrl" | "adminComponents";
284
284
  path: string;
285
285
  surface: string;
286
286
  message: string;
@@ -292,7 +292,7 @@ type DiagnosticFormatOptions = {
292
292
  type DeclarativeValidationConfig = {
293
293
  blocks?: Array<Pick<Block, "fields"> & Partial<Pick<Block, "slug">>>;
294
294
  collections: Array<Pick<CollectionConfig, "fields" | "hooks" | "access" | "admin"> & Partial<Pick<CollectionConfig, "slug">>>;
295
- globals?: Array<Pick<GlobalConfig, "fields" | "hooks" | "access"> & Partial<Pick<GlobalConfig, "slug">>>;
295
+ globals?: Array<Pick<GlobalConfig, "fields" | "hooks" | "access" | "admin"> & Partial<Pick<GlobalConfig, "slug">>>;
296
296
  accessPolicies?: Record<string, unknown>;
297
297
  };
298
298
  declare function validateDeclarativeHookExpression(expression: string, surface: DeclarativeHookSurface, path: string): DeclarativeHookValidationIssue[];
@@ -305,6 +305,14 @@ declare function collectDeclarativeHookValidationIssues(config: DeclarativeValid
305
305
  declare function collectDeclarativeAccessValidationIssues(config: DeclarativeValidationConfig): DeclarativeAccessValidationIssue[];
306
306
  declare function collectAdminConditionValidationIssues(config: DeclarativeValidationConfig): DeclarativeAdminValidationIssue[];
307
307
  declare function collectPreviewUrlValidationIssues(config: DeclarativeValidationConfig): DeclarativePreviewValidationIssue[];
308
+ interface AdminComponentsValidationIssue {
309
+ path: string;
310
+ surface: "collection.admin.components" | "global.admin.components";
311
+ message: string;
312
+ expression: string;
313
+ }
314
+ /** Flags malformed `createForm`, `editForm`, `form` and `formShell` settings. */
315
+ declare function collectAdminComponentsValidationIssues(config: DeclarativeValidationConfig): AdminComponentsValidationIssue[];
308
316
  declare function assertValidAdminConditionsInConfig(config: DeclarativeValidationConfig, source?: string): void;
309
317
  declare function assertValidPreviewUrlsInConfig(config: DeclarativeValidationConfig, source?: string): void;
310
318
  declare function collectConfigDiagnostics(config: DeclarativeValidationConfig): ConfigDiagnostic[];
@@ -318,6 +326,38 @@ declare function isConfigValidationError(error: unknown): error is ConfigValidat
318
326
  declare function assertValidDeclarativeHooksInConfig(config: DeclarativeValidationConfig, source?: string): void;
319
327
  declare function assertValidDeclarativeAccessInConfig(config: DeclarativeValidationConfig, source?: string): void;
320
328
 
329
+ /**
330
+ * Component keys a schema references, grouped by the `AdminComponents`
331
+ * registry that must supply them.
332
+ *
333
+ * Registration happens in the host app's admin bundle, so this scan is the
334
+ * single source of truth for both type generation (CLI) and the admin's
335
+ * unused-registration warning.
336
+ */
337
+ interface ReferencedComponentKeys {
338
+ /** `field.admin.component` keys, resolved against `components.fields`. */
339
+ fields: Set<string>;
340
+ /** `admin.components.before/afterDashboard` keys. */
341
+ dashboard: Set<string>;
342
+ /** `collection.admin.components` list slots. */
343
+ collectionList: Set<string>;
344
+ /** Operational view slots (`collectionView` and per-view `components`). */
345
+ collectionView: Set<string>;
346
+ /** `createForm`, `editForm` and `form` keys on collections, and `editForm`/`form` on globals. */
347
+ forms: Set<string>;
348
+ /** `component` of views with `layout: "custom"`. */
349
+ viewLayouts: Set<string>;
350
+ /** `admin.pages[].component` keys. */
351
+ pages: Set<string>;
352
+ /** `displayCustom(name)` keys used in Detail View layouts. */
353
+ customComponents: Set<string>;
354
+ }
355
+ /**
356
+ * Collects every component key a schema (or a `DyrectedConfig`) references.
357
+ * Tolerant of partial or unknown shapes: anything unrecognised is skipped.
358
+ */
359
+ declare function collectReferencedComponentKeys(schema: unknown): ReferencedComponentKeys;
360
+
321
361
  /**
322
362
  * Transforms a string into a clean, URL-safe slug.
323
363
  * Strips accents, lowercases, removes non-alphanumeric characters, and hyphens whitespace.
@@ -1522,7 +1562,7 @@ declare function reconcileNavigation(baseTree: CompiledNavTree | null | undefine
1522
1562
  */
1523
1563
  declare const RESERVED_NAVIGATION_SLUGS: Set<string>;
1524
1564
  /**
1525
- * Validates that no navigation item uses a reserved system route slug.
1565
+ * Validates that no navigation item uses a reserved system route slug or duplicate slug.
1526
1566
  */
1527
1567
  declare function assertValidNavigationSlugs(navigation: DefineWorkspaceOptions[]): void;
1528
1568
  /**
@@ -2340,4 +2380,4 @@ declare function defineTab<const T extends readonly Field[]>(args: {
2340
2380
  fields: T;
2341
2381
  }): T;
2342
2382
 
2343
- export { AIConfig, type AIErrorCode, AIPIIConfig, type AccessConditions, AccessPolicyResolver, AdminAuthConfig, ArrayField, AuthConfig, type AuthDocFields, AuthenticatedUser, BASE62_ALPHABET, BUILTIN_JEXL_HELPERS, BaseDocument, Block, BooleanField, CROCKFORD_BASE32, CollectionConfig, CompiledNavItem, CompiledNavTree, ComputedHandler, type ConfigDiagnostic, ConfigValidationError, ConflictError, type CreatedAtFieldOptions, type CreatedByFieldOptions, DatabaseAdapter, DateField, DateTimeField, type DeclarativeAccessSurface, type DeclarativeAccessValidationIssue, type DeclarativeAdminSurface, type DeclarativeAdminValidationIssue, DeclarativeHookExpression, type DeclarativeHookSurface, type DeclarativeHookValidationIssue, type DeclarativePreviewSurface, type DeclarativePreviewValidationIssue, DefineWorkspaceOptions, DetailAction, DetailActionOptions, DetailComputed, DetailComputedOptions, DetailCustom, DetailCustomOptions, DetailDivider, DetailDividerOptions, DetailField, DetailGrid, DetailGridOptions, DetailItem, DetailRepeat, DetailRepeatOptions, DetailSchema, DetailSection, DetailSectionOptions, DetailTab, DetailTabOptions, DetailTabs, DetailTabsOptions, DetailText, DetailTextOptions, DisplayFieldOptions, DuplicateKeyError, DyrectedAIError, DyrectedConfig, DyrectedError, EMAIL_TEMPLATES_COLLECTION_CONFIG, EMAIL_TEMPLATES_COLLECTION_SLUG, EmailField, type ExprFunction, type ExtractCollectionSlugs, Field, FieldConditionBuilder, FieldType, ForbiddenError, type GenerateIdOptions, GlobalConfig, HookRequestContext, INTERNAL_SYSTEM_COLLECTIONS, IconField, IdStrategy, ImageField, type InferDocShape, JoinField, JsonField, LIFECYCLE_EVENTS_COLLECTION, type LayoutOptions, LifecycleEvent, LifecycleEventName, MatchBuilder, MoneyField, MultiSelectField, NANOID_ALPHABET, NON_NEGOTIABLE_CREDENTIAL_FIELDS, NotFoundError, NumberField, ObjectField, PII_REGEX_PATTERNS, type Prettify, PublicAdminAuthConfig, type PublishingWorkflowOptions, RESERVED_NAVIGATION_SLUGS, RadioField, type ReconcileNavigationOptions, type ReconciledNavTree, RelationshipField, type ResolvedTrashConfig, type RestoreDocumentOptions, RichTextField, type RoleOption, type RolesFieldOptions, RowField, type SanitizeDocForAIOptions, SelectField, type SoftDeleteOptions, type SortClause, type SortDirection, type SqlSortDialect, type SystemDocFields, TASK_LOCKS_COLLECTION, TASK_LOCKS_COLLECTION_CONFIG, TRASH_COLLECTION, TRASH_COLLECTION_CONFIG, TaskConfig, TaskRunResult, type TaskRunner, type TemplateBlock, TextField, TextareaField, TimeField, UNASSIGNED_GROUP_KEY, type UpdatedAtFieldOptions, type UpdatedByFieldOptions, type UploadDocFields, UrlField, UserNavigationPreferences, ValidationError, ViewConfig, WORKFLOW_HISTORY_COLLECTION, type WhenFunction, WorkflowConfig, WorkflowTransition, abs, addDays, alertBox, asCollection, assertValidAdminConditionsInConfig, assertValidDeclarativeAccessInConfig, assertValidDeclarativeHookExpression, assertValidDeclarativeHookResult, assertValidDeclarativeHooksInConfig, assertValidNavigationSlugs, assertValidPreviewUrlsInConfig, assertValidTrashInConfig, availableWorkflowTransitions, buildSqlOrderBy, canViewWorkflowDraft, capitalize, ceil, clamp, coalesce, collectAdminConditionValidationIssues, collectConfigDiagnostics, collectDeclarativeAccessValidationIssues, collectDeclarativeHookValidationIssues, collectPreviewUrlValidationIssues, compact, compileNavigation, count, createLifecycleEvent, createTaskRunner, createTrashPurgeTask, createWorkflowDocument, ctaButton, defaultValue, defineArrayField, defineBlock, defineBlocksField, defineBooleanField, defineCollection, defineConfig, defineCreatedAtField, defineCreatedByField, defineDateField, defineDateTimeField, defineEmailField, defineField, defineGlobal, defineIconField, defineImageField, defineJoinField, defineJsonField, defineMoneyField, defineMultiSelectField, defineNumberField, defineObjectField, definePublishingWorkflow, defineRadioField, defineRelationshipField, defineRichTextField, defineRoles, defineRolesField, defineRowField, defineSelectField, defineTab, defineTask, defineTextField, defineTextareaField, defineTimeField, defineUpdatedAtField, defineUpdatedByField, defineUrlField, detailBox, diffDays, dispatchLifecycleEvent, dispatchPendingLifecycleEvents, displayAction, displayComputed, displayCustom, displayCustomComponent, displayDivider, displayField, displayGrid, displayRepeat, displaySection, displayTab, displayTabs, displayText, divider, emailTokens, endsWith, escapeHtml, evaluateDetailComputed, evaluateJexl, evaluateJexlSync, eventList, executeFieldAfterRead, executeFieldBeforeChange, expr, first, floor, formatConfigDiagnostics, formatDate, formatJexlValue, generateDefaultDetailSchema, generateDocumentId, generateId, generateOpenApi, get, getAdminAuthCollection, getAdminRoleForCollection, getAllowedSitesForUser, getDefaultEmailTemplate, getPublicAdminAuthConfig, getTrashEntryId, heading, includes, initializeWorkflowDocument, isConfigValidationError, isDetailItemVisible, isEmpty, isFuture, isPast, isSiteAuthorized, isUserAdmin, join, last, layout, length, lower, maskEmail, maskPhone, maskTextPII, materializeWorkflowDocument, max, min, nanoid, nextCronRun, normalizeConfig, normalizeDetailItem, normalizeGroupKey, now, numericFieldNames, paragraph, parseCron, parseSort, pow, prefixedId, pruneNavigationForUser, publishedStateName, publishingWorkflow, readingTime, reconcileNavigation, registerJexlHelpers, replace, resolveAdminAuthCollection, resolveAllCollectionViews, resolveAuthorizedSiteId, resolveBlocks, resolveDocumentTitle, resolveIndexName, resolvePrefix, resolveTrashConfig, restoreDocument, round, row, runCollectionHooks, safeLinkDetailBox, sanitizeDocForAI, saveWorkflowDraft, sectionLabel, simplePublishingWorkflow, slugify, softDeleteDocument, spacer, sqrt, startsWith, sum, table, today, transitionWorkflow, trim, truncate, ulid, unique, upper, uuid, validateAdminConditionExpression, validateDeclarativeAccessExpression, validateDeclarativeHookExpression, validatePreviewUrlExpression, when, wordCount, workflowCapabilities };
2383
+ export { AIConfig, type AIErrorCode, AIPIIConfig, type AccessConditions, AccessPolicyResolver, AdminAuthConfig, type AdminComponentsValidationIssue, ArrayField, AuthConfig, type AuthDocFields, AuthenticatedUser, BASE62_ALPHABET, BUILTIN_JEXL_HELPERS, BaseDocument, Block, BooleanField, CROCKFORD_BASE32, CollectionConfig, CompiledNavItem, CompiledNavTree, ComputedHandler, type ConfigDiagnostic, ConfigValidationError, ConflictError, type CreatedAtFieldOptions, type CreatedByFieldOptions, DatabaseAdapter, DateField, DateTimeField, type DeclarativeAccessSurface, type DeclarativeAccessValidationIssue, type DeclarativeAdminSurface, type DeclarativeAdminValidationIssue, DeclarativeHookExpression, type DeclarativeHookSurface, type DeclarativeHookValidationIssue, type DeclarativePreviewSurface, type DeclarativePreviewValidationIssue, DefineWorkspaceOptions, DetailAction, DetailActionOptions, DetailComputed, DetailComputedOptions, DetailCustom, DetailCustomOptions, DetailDivider, DetailDividerOptions, DetailField, DetailGrid, DetailGridOptions, DetailItem, DetailRepeat, DetailRepeatOptions, DetailSchema, DetailSection, DetailSectionOptions, DetailTab, DetailTabOptions, DetailTabs, DetailTabsOptions, DetailText, DetailTextOptions, DisplayFieldOptions, DuplicateKeyError, DyrectedAIError, DyrectedConfig, DyrectedError, EMAIL_TEMPLATES_COLLECTION_CONFIG, EMAIL_TEMPLATES_COLLECTION_SLUG, EmailField, type ExprFunction, type ExtractCollectionSlugs, Field, FieldConditionBuilder, FieldType, ForbiddenError, type GenerateIdOptions, GlobalConfig, HookRequestContext, INTERNAL_SYSTEM_COLLECTIONS, IconField, IdStrategy, ImageField, type InferDocShape, JoinField, JsonField, LIFECYCLE_EVENTS_COLLECTION, type LayoutOptions, LifecycleEvent, LifecycleEventName, MatchBuilder, MoneyField, MultiSelectField, NANOID_ALPHABET, NON_NEGOTIABLE_CREDENTIAL_FIELDS, NotFoundError, NumberField, ObjectField, PII_REGEX_PATTERNS, type Prettify, PublicAdminAuthConfig, type PublishingWorkflowOptions, RESERVED_NAVIGATION_SLUGS, RadioField, type ReconcileNavigationOptions, type ReconciledNavTree, type ReferencedComponentKeys, RelationshipField, type ResolvedTrashConfig, type RestoreDocumentOptions, RichTextField, type RoleOption, type RolesFieldOptions, RowField, type SanitizeDocForAIOptions, SelectField, type SoftDeleteOptions, type SortClause, type SortDirection, type SqlSortDialect, type SystemDocFields, TASK_LOCKS_COLLECTION, TASK_LOCKS_COLLECTION_CONFIG, TRASH_COLLECTION, TRASH_COLLECTION_CONFIG, TaskConfig, TaskRunResult, type TaskRunner, type TemplateBlock, TextField, TextareaField, TimeField, UNASSIGNED_GROUP_KEY, type UpdatedAtFieldOptions, type UpdatedByFieldOptions, type UploadDocFields, UrlField, UserNavigationPreferences, ValidationError, ViewConfig, WORKFLOW_HISTORY_COLLECTION, type WhenFunction, WorkflowConfig, WorkflowTransition, abs, addDays, alertBox, asCollection, assertValidAdminConditionsInConfig, assertValidDeclarativeAccessInConfig, assertValidDeclarativeHookExpression, assertValidDeclarativeHookResult, assertValidDeclarativeHooksInConfig, assertValidNavigationSlugs, assertValidPreviewUrlsInConfig, assertValidTrashInConfig, availableWorkflowTransitions, buildSqlOrderBy, canViewWorkflowDraft, capitalize, ceil, clamp, coalesce, collectAdminComponentsValidationIssues, collectAdminConditionValidationIssues, collectConfigDiagnostics, collectDeclarativeAccessValidationIssues, collectDeclarativeHookValidationIssues, collectPreviewUrlValidationIssues, collectReferencedComponentKeys, compact, compileNavigation, count, createLifecycleEvent, createTaskRunner, createTrashPurgeTask, createWorkflowDocument, ctaButton, defaultValue, defineArrayField, defineBlock, defineBlocksField, defineBooleanField, defineCollection, defineConfig, defineCreatedAtField, defineCreatedByField, defineDateField, defineDateTimeField, defineEmailField, defineField, defineGlobal, defineIconField, defineImageField, defineJoinField, defineJsonField, defineMoneyField, defineMultiSelectField, defineNumberField, defineObjectField, definePublishingWorkflow, defineRadioField, defineRelationshipField, defineRichTextField, defineRoles, defineRolesField, defineRowField, defineSelectField, defineTab, defineTask, defineTextField, defineTextareaField, defineTimeField, defineUpdatedAtField, defineUpdatedByField, defineUrlField, detailBox, diffDays, dispatchLifecycleEvent, dispatchPendingLifecycleEvents, displayAction, displayComputed, displayCustom, displayCustomComponent, displayDivider, displayField, displayGrid, displayRepeat, displaySection, displayTab, displayTabs, displayText, divider, emailTokens, endsWith, escapeHtml, evaluateDetailComputed, evaluateJexl, evaluateJexlSync, eventList, executeFieldAfterRead, executeFieldBeforeChange, expr, first, floor, formatConfigDiagnostics, formatDate, formatJexlValue, generateDefaultDetailSchema, generateDocumentId, generateId, generateOpenApi, get, getAdminAuthCollection, getAdminRoleForCollection, getAllowedSitesForUser, getDefaultEmailTemplate, getPublicAdminAuthConfig, getTrashEntryId, heading, includes, initializeWorkflowDocument, isConfigValidationError, isDetailItemVisible, isEmpty, isFuture, isPast, isSiteAuthorized, isUserAdmin, join, last, layout, length, lower, maskEmail, maskPhone, maskTextPII, materializeWorkflowDocument, max, min, nanoid, nextCronRun, normalizeConfig, normalizeDetailItem, normalizeGroupKey, now, numericFieldNames, paragraph, parseCron, parseSort, pow, prefixedId, pruneNavigationForUser, publishedStateName, publishingWorkflow, readingTime, reconcileNavigation, registerJexlHelpers, replace, resolveAdminAuthCollection, resolveAllCollectionViews, resolveAuthorizedSiteId, resolveBlocks, resolveDocumentTitle, resolveIndexName, resolvePrefix, resolveTrashConfig, restoreDocument, round, row, runCollectionHooks, safeLinkDetailBox, sanitizeDocForAI, saveWorkflowDraft, sectionLabel, simplePublishingWorkflow, slugify, softDeleteDocument, spacer, sqrt, startsWith, sum, table, today, transitionWorkflow, trim, truncate, ulid, unique, upper, uuid, validateAdminConditionExpression, validateDeclarativeAccessExpression, validateDeclarativeHookExpression, validatePreviewUrlExpression, when, wordCount, workflowCapabilities };
package/dist/index.js CHANGED
@@ -1780,6 +1780,9 @@ function asCollection(config) {
1780
1780
  return config;
1781
1781
  }
1782
1782
 
1783
+ // src/types/admin.ts
1784
+ var FORM_SHELLS = ["full", "header", "none"];
1785
+
1783
1786
  // src/types/workflows.ts
1784
1787
  var LIFECYCLE_EVENT_NAMES = [
1785
1788
  "revision.created",
@@ -1789,9 +1792,19 @@ var LIFECYCLE_EVENT_NAMES = [
1789
1792
  ];
1790
1793
 
1791
1794
  // src/types/views.ts
1795
+ var CUSTOM_VIEW_FETCH_ALL_LIMIT = 1e3;
1792
1796
  function defineView(config) {
1797
+ assertCustomViewComponent(config);
1793
1798
  return config;
1794
1799
  }
1800
+ function assertCustomViewComponent(config) {
1801
+ if (config.layout !== "custom") return;
1802
+ if (typeof config.component !== "string" || config.component.trim() === "") {
1803
+ throw new Error(
1804
+ `View "${config.slug ?? "(unnamed)"}" uses layout: "custom" but has no \`component\`. Set \`component\` to a key registered in AdminComponents.viewLayouts.`
1805
+ );
1806
+ }
1807
+ }
1795
1808
  function defineTableView(config) {
1796
1809
  return config;
1797
1810
  }
@@ -1804,6 +1817,10 @@ function defineCalendarView(config) {
1804
1817
  function defineGanttView(config) {
1805
1818
  return config;
1806
1819
  }
1820
+ function defineCustomView(config) {
1821
+ assertCustomViewComponent(config);
1822
+ return config;
1823
+ }
1807
1824
  function defineCardsView(config) {
1808
1825
  return config;
1809
1826
  }
@@ -1860,6 +1877,23 @@ var DEFAULT_USER_NAV_PREFERENCES = {
1860
1877
  items: []
1861
1878
  };
1862
1879
 
1880
+ // src/types/pages.ts
1881
+ function definePage(config) {
1882
+ return {
1883
+ slug: config.slug,
1884
+ label: config.label,
1885
+ icon: config.icon,
1886
+ group: config.group,
1887
+ order: config.order,
1888
+ after: config.after,
1889
+ before: config.before,
1890
+ position: config.position,
1891
+ badge: config.badge,
1892
+ access: config.access && typeof config.access === "object" && "read" in config.access ? config.access.read : config.access,
1893
+ page: config
1894
+ };
1895
+ }
1896
+
1863
1897
  // src/auth/token.ts
1864
1898
  import { SignJWT, jwtVerify, decodeJwt } from "jose";
1865
1899
 
@@ -2699,6 +2733,54 @@ function collectPreviewUrlValidationIssues(config) {
2699
2733
  });
2700
2734
  return issues;
2701
2735
  }
2736
+ var FORM_COMPONENT_KEYS = ["createForm", "editForm", "form"];
2737
+ var GLOBAL_FORM_COMPONENT_KEYS = ["editForm", "form"];
2738
+ function collectFormComponentIssues(components, path, surface, keys, issues) {
2739
+ if (!isPlainObject(components)) return;
2740
+ for (const key of keys) {
2741
+ const value = components[key];
2742
+ if (value === void 0) continue;
2743
+ if (typeof value !== "string" || value.length === 0) {
2744
+ issues.push({
2745
+ path: `${path}.admin.components.${key}`,
2746
+ surface,
2747
+ message: `\`${key}\` must be the registry key of a component, such as "check_ins.scanForm".`,
2748
+ expression: String(value)
2749
+ });
2750
+ }
2751
+ }
2752
+ const shell = components.formShell;
2753
+ if (shell !== void 0 && !FORM_SHELLS.includes(shell)) {
2754
+ issues.push({
2755
+ path: `${path}.admin.components.formShell`,
2756
+ surface,
2757
+ message: `Unknown formShell "${String(shell)}". Use ${FORM_SHELLS.map((value) => `"${value}"`).join(", ")}.`,
2758
+ expression: String(shell)
2759
+ });
2760
+ }
2761
+ }
2762
+ function collectAdminComponentsValidationIssues(config) {
2763
+ const issues = [];
2764
+ config.collections.forEach((collection, index) => {
2765
+ collectFormComponentIssues(
2766
+ collection.admin?.components,
2767
+ collectionSegment(collection, index),
2768
+ "collection.admin.components",
2769
+ FORM_COMPONENT_KEYS,
2770
+ issues
2771
+ );
2772
+ });
2773
+ config.globals?.forEach((global, index) => {
2774
+ collectFormComponentIssues(
2775
+ global.admin?.components,
2776
+ globalSegment(global, index),
2777
+ "global.admin.components",
2778
+ GLOBAL_FORM_COMPONENT_KEYS,
2779
+ issues
2780
+ );
2781
+ });
2782
+ return issues;
2783
+ }
2702
2784
  function assertValidAdminConditionsInConfig(config, source = "config") {
2703
2785
  const diagnostics = collectAdminConditionValidationIssues(config).map((issue) => ({
2704
2786
  severity: "error",
@@ -2756,7 +2838,15 @@ function collectConfigDiagnostics(config) {
2756
2838
  message: issue.message,
2757
2839
  expression: issue.expression
2758
2840
  }));
2759
- return [...hookIssues, ...accessIssues, ...adminConditionIssues, ...previewUrlIssues];
2841
+ const adminComponentIssues = collectAdminComponentsValidationIssues(config).map((issue) => ({
2842
+ severity: "error",
2843
+ source: "adminComponents",
2844
+ path: issue.path,
2845
+ surface: issue.surface,
2846
+ message: issue.message,
2847
+ expression: issue.expression
2848
+ }));
2849
+ return [...hookIssues, ...accessIssues, ...adminConditionIssues, ...previewUrlIssues, ...adminComponentIssues];
2760
2850
  }
2761
2851
  function formatConfigDiagnostics(source, diagnostics, options = {}) {
2762
2852
  const color = options.color ?? false;
@@ -5189,6 +5279,12 @@ var DuplicateKeyError = class _DuplicateKeyError extends ConflictError {
5189
5279
  function assertNever(op, context) {
5190
5280
  throw new Error(`[dyrected/core] Unhandled where operator "${op}" in ${context}`);
5191
5281
  }
5282
+ function escapeLike(value) {
5283
+ return value.replace(/[!%_]/g, "!$&");
5284
+ }
5285
+ function escapeRegex(value) {
5286
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5287
+ }
5192
5288
  function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = placeholder === "pg" ? "ILIKE" : "LIKE") {
5193
5289
  const params = [];
5194
5290
  let pgIndex = 1;
@@ -5198,6 +5294,19 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = pl
5198
5294
  function col(field) {
5199
5295
  return field === "id" ? "id" : getJsonField(field);
5200
5296
  }
5297
+ function buildLike(column, pattern, escaped = false) {
5298
+ const esc = escaped ? ` ESCAPE '!'` : "";
5299
+ if (placeholder === "pg") {
5300
+ params.push(pattern);
5301
+ return `(${column})::text ${likeOperator} ${next()}${esc}`;
5302
+ }
5303
+ if (likeOperator === "ILIKE") {
5304
+ params.push(pattern);
5305
+ return `${column} ILIKE ${next()}${esc}`;
5306
+ }
5307
+ params.push(pattern.toLowerCase());
5308
+ return `LOWER(${column}) LIKE ${next()}${esc}`;
5309
+ }
5201
5310
  function buildOperator(field, value) {
5202
5311
  const c = col(field);
5203
5312
  const isJsonExtract = c.startsWith("data->>") || c.startsWith("json_extract(") || c.startsWith("JSON_UNQUOTE(");
@@ -5340,13 +5449,12 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = pl
5340
5449
  case "less_than_or_equal":
5341
5450
  params.push(operand);
5342
5451
  return `${c} <= ${next()}`;
5343
- case "contains":
5344
5452
  case "like":
5345
- params.push(`%${operand}%`);
5346
- return placeholder === "pg" ? `(${c})::text ${likeOperator} ${next()}` : `${c} ${likeOperator} ${next()}`;
5453
+ return buildLike(c, `%${String(operand)}%`);
5454
+ case "contains":
5455
+ return buildLike(c, `%${escapeLike(String(operand))}%`, true);
5347
5456
  case "starts_with":
5348
- params.push(`${operand}%`);
5349
- return placeholder === "pg" ? `(${c})::text ${likeOperator} ${next()}` : `${c} ${likeOperator} ${next()}`;
5457
+ return buildLike(c, `${escapeLike(String(operand))}%`, true);
5350
5458
  case "exists":
5351
5459
  return operand ? `${c} IS NOT NULL` : `${c} IS NULL`;
5352
5460
  default:
@@ -5444,9 +5552,9 @@ function parseMongoWhere(where) {
5444
5552
  return { [field]: { $lte: operand } };
5445
5553
  case "contains":
5446
5554
  case "like":
5447
- return { [field]: { $regex: operand, $options: "i" } };
5555
+ return { [field]: { $regex: escapeRegex(String(operand)), $options: "i" } };
5448
5556
  case "starts_with":
5449
- return { [field]: { $regex: `^${operand}`, $options: "i" } };
5557
+ return { [field]: { $regex: `^${escapeRegex(String(operand))}`, $options: "i" } };
5450
5558
  case "exists":
5451
5559
  return { [field]: { $exists: operand } };
5452
5560
  default:
@@ -5556,6 +5664,99 @@ function buildSqlOrderBy(sort, existingColumns, numericFields, dialect) {
5556
5664
  }).join(", ");
5557
5665
  }
5558
5666
 
5667
+ // src/utils/component-keys.ts
5668
+ var DASHBOARD_SLOTS = ["beforeDashboard", "afterDashboard"];
5669
+ var LIST_SLOTS = ["beforeList", "beforeListTable", "afterListTable", "afterList"];
5670
+ var FORM_SLOTS = ["createForm", "editForm", "form"];
5671
+ var VIEW_SLOTS = ["beforeViewHeader", "afterViewHeader", "beforeViewContent", "afterViewContent"];
5672
+ function addKeys(target, value) {
5673
+ for (const key of Array.isArray(value) ? value : [value]) {
5674
+ if (typeof key === "string" && key) target.add(key);
5675
+ }
5676
+ }
5677
+ function addSlots(target, slots, names) {
5678
+ if (!slots || typeof slots !== "object") return;
5679
+ for (const name of names) addKeys(target, slots[name]);
5680
+ }
5681
+ function scanFields(fields, keys) {
5682
+ if (!Array.isArray(fields)) return;
5683
+ for (const field of fields) {
5684
+ if (!field || typeof field !== "object") continue;
5685
+ addKeys(keys.fields, field.admin?.component);
5686
+ scanFields(field.fields, keys);
5687
+ if (Array.isArray(field.blocks)) {
5688
+ for (const block of field.blocks) scanFields(block?.fields, keys);
5689
+ }
5690
+ if (Array.isArray(field.tabs)) {
5691
+ for (const tab of field.tabs) scanFields(tab?.fields, keys);
5692
+ }
5693
+ }
5694
+ }
5695
+ function scanDetail(items, keys) {
5696
+ if (!Array.isArray(items)) return;
5697
+ for (const item of items) {
5698
+ if (!item || typeof item !== "object") continue;
5699
+ if (item.type === "custom") addKeys(keys.customComponents, item.name);
5700
+ scanDetail(item.items, keys);
5701
+ if (Array.isArray(item.tabs)) {
5702
+ for (const tab of item.tabs) scanDetail(tab?.items, keys);
5703
+ }
5704
+ }
5705
+ }
5706
+ function scanViews(views, keys) {
5707
+ if (!Array.isArray(views)) return;
5708
+ for (const view of views) {
5709
+ addSlots(keys.collectionView, view?.components, VIEW_SLOTS);
5710
+ if (view?.layout === "custom") addKeys(keys.viewLayouts, view.component);
5711
+ }
5712
+ }
5713
+ function collectReferencedComponentKeys(schema) {
5714
+ const keys = {
5715
+ fields: /* @__PURE__ */ new Set(),
5716
+ dashboard: /* @__PURE__ */ new Set(),
5717
+ collectionList: /* @__PURE__ */ new Set(),
5718
+ collectionView: /* @__PURE__ */ new Set(),
5719
+ forms: /* @__PURE__ */ new Set(),
5720
+ viewLayouts: /* @__PURE__ */ new Set(),
5721
+ pages: /* @__PURE__ */ new Set(),
5722
+ customComponents: /* @__PURE__ */ new Set()
5723
+ };
5724
+ const root = schema;
5725
+ if (!root || typeof root !== "object") return keys;
5726
+ addSlots(keys.dashboard, root.admin?.components, DASHBOARD_SLOTS);
5727
+ if (Array.isArray(root.admin?.navigation)) {
5728
+ for (const item of root.admin.navigation) {
5729
+ if (item?.page) {
5730
+ if (typeof item.page === "string") {
5731
+ addKeys(keys.pages, item.page);
5732
+ } else if (item.page?.component) {
5733
+ addKeys(keys.pages, item.page.component);
5734
+ }
5735
+ }
5736
+ }
5737
+ }
5738
+ if (Array.isArray(root.admin?.pages)) {
5739
+ for (const page of root.admin.pages) addKeys(keys.pages, page?.component);
5740
+ }
5741
+ for (const block of root.blocks ?? []) scanFields(block?.fields, keys);
5742
+ for (const collection of root.collections ?? []) {
5743
+ scanFields(collection.fields, keys);
5744
+ scanDetail(collection.detail, keys);
5745
+ scanViews(collection.views, keys);
5746
+ const slots = collection.admin?.components;
5747
+ addSlots(keys.collectionList, slots, LIST_SLOTS);
5748
+ addSlots(keys.collectionView, slots, VIEW_SLOTS);
5749
+ addSlots(keys.collectionView, slots?.collectionView, VIEW_SLOTS);
5750
+ addSlots(keys.forms, slots, FORM_SLOTS);
5751
+ }
5752
+ for (const global of root.globals ?? []) {
5753
+ scanFields(global.fields, keys);
5754
+ scanDetail(global.detail, keys);
5755
+ addSlots(keys.forms, global.admin?.components, FORM_SLOTS);
5756
+ }
5757
+ return keys;
5758
+ }
5759
+
5559
5760
  // src/utils/openapi.ts
5560
5761
  function getCollectionLabels(collection) {
5561
5762
  return collection.labels || { singular: collection.slug, plural: collection.slug };
@@ -8043,11 +8244,32 @@ var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
8043
8244
  "admin",
8044
8245
  "preferences",
8045
8246
  "auth",
8046
- "trash"
8247
+ "trash",
8248
+ "pages"
8047
8249
  ]);
8048
8250
  function assertValidNavigationSlugs(navigation) {
8251
+ const seenWorkspaceSlugs = /* @__PURE__ */ new Set();
8252
+ const seenPageSlugs = /* @__PURE__ */ new Set();
8049
8253
  for (const item of navigation) {
8050
8254
  if (item.dashboard || item.trash) continue;
8255
+ if (item.page) {
8256
+ const pageSlug = typeof item.page === "string" ? item.slug || item.page : item.slug || item.page.slug;
8257
+ if (pageSlug) {
8258
+ const normalized = pageSlug.toLowerCase().trim().replace(/^\//, "");
8259
+ if (RESERVED_NAVIGATION_SLUGS.has(normalized)) {
8260
+ throw new Error(
8261
+ `[Dyrected Config] The slug '${pageSlug}' in custom page '${item.label || pageSlug}' is a reserved system path and cannot be used as a page slug.`
8262
+ );
8263
+ }
8264
+ if (seenPageSlugs.has(normalized)) {
8265
+ throw new Error(
8266
+ `[Dyrected Config] Duplicate page slug '${pageSlug}' in navigation. Page slugs must be unique.`
8267
+ );
8268
+ }
8269
+ seenPageSlugs.add(normalized);
8270
+ }
8271
+ continue;
8272
+ }
8051
8273
  if (item.slug) {
8052
8274
  const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
8053
8275
  if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
@@ -8055,6 +8277,12 @@ function assertValidNavigationSlugs(navigation) {
8055
8277
  `[Dyrected Config] The slug '${item.slug}' in navigation item '${item.label || item.slug}' is a reserved system path and cannot be used as an operational workspace slug.`
8056
8278
  );
8057
8279
  }
8280
+ if (seenWorkspaceSlugs.has(normalizedSlug)) {
8281
+ throw new Error(
8282
+ `[Dyrected Config] Duplicate workspace slug '${item.slug}' in navigation. Workspace slugs must be unique.`
8283
+ );
8284
+ }
8285
+ seenWorkspaceSlugs.add(normalizedSlug);
8058
8286
  }
8059
8287
  }
8060
8288
  }
@@ -8197,6 +8425,43 @@ function compileNavigation(config) {
8197
8425
  });
8198
8426
  continue;
8199
8427
  }
8428
+ if (item.page) {
8429
+ const pageConfig = typeof item.page === "string" ? {
8430
+ slug: item.slug || item.page,
8431
+ label: item.label || item.page,
8432
+ icon: item.icon,
8433
+ component: item.page,
8434
+ group: typeof item.group === "string" ? item.group : item.group?.name,
8435
+ order: item.order,
8436
+ badge: item.badge,
8437
+ access: item.access
8438
+ } : {
8439
+ ...item.page,
8440
+ slug: item.slug || item.page.slug || "page",
8441
+ label: item.label || item.page.label || item.slug || "Page",
8442
+ icon: item.icon || item.page.icon,
8443
+ group: item.group ? typeof item.group === "string" ? item.group : item.group?.name : item.page.group,
8444
+ order: item.order ?? item.page.order,
8445
+ badge: item.badge || item.page.badge,
8446
+ access: item.access || (item.page.access && typeof item.page.access === "object" && "read" in item.page.access ? item.page.access.read : item.page.access)
8447
+ };
8448
+ const pageSlug = pageConfig.slug || "page";
8449
+ registerItem(groupName, {
8450
+ id: `page:${pageSlug}`,
8451
+ type: "page",
8452
+ slug: pageSlug,
8453
+ label: pageConfig.label || pageSlug,
8454
+ icon: pageConfig.icon || "FileText",
8455
+ href: `/pages/${pageSlug}`,
8456
+ group: groupName,
8457
+ order: pageConfig.order ?? 100,
8458
+ views: [],
8459
+ badge: pageConfig.badge,
8460
+ access: pageConfig.access && typeof pageConfig.access === "object" && "read" in pageConfig.access ? pageConfig.access.read : pageConfig.access,
8461
+ page: pageConfig
8462
+ });
8463
+ continue;
8464
+ }
8200
8465
  const workspaceSlug = item.slug || "workspace";
8201
8466
  registerItem(groupName, {
8202
8467
  id: `workspace_${workspaceSlug}`,
@@ -8578,6 +8843,7 @@ export {
8578
8843
  BUILTIN_JEXL_HELPERS,
8579
8844
  CROCKFORD_BASE32,
8580
8845
  CURRENT_NAV_PREFERENCES_VERSION,
8846
+ CUSTOM_VIEW_FETCH_ALL_LIMIT,
8581
8847
  ConfigValidationError,
8582
8848
  ConflictError,
8583
8849
  DEFAULT_USER_NAV_PREFERENCES,
@@ -8586,6 +8852,7 @@ export {
8586
8852
  DyrectedError,
8587
8853
  EMAIL_TEMPLATES_COLLECTION_CONFIG,
8588
8854
  EMAIL_TEMPLATES_COLLECTION_SLUG,
8855
+ FORM_SHELLS,
8589
8856
  FieldConditionBuilder,
8590
8857
  ForbiddenError,
8591
8858
  INTERNAL_SYSTEM_COLLECTIONS,
@@ -8624,11 +8891,13 @@ export {
8624
8891
  clamp,
8625
8892
  coalesce,
8626
8893
  coerceBooleanWhere,
8894
+ collectAdminComponentsValidationIssues,
8627
8895
  collectAdminConditionValidationIssues,
8628
8896
  collectConfigDiagnostics,
8629
8897
  collectDeclarativeAccessValidationIssues,
8630
8898
  collectDeclarativeHookValidationIssues,
8631
8899
  collectPreviewUrlValidationIssues,
8900
+ collectReferencedComponentKeys,
8632
8901
  compact,
8633
8902
  compileNavigation,
8634
8903
  count,
@@ -8649,6 +8918,7 @@ export {
8649
8918
  defineConfig,
8650
8919
  defineCreatedAtField,
8651
8920
  defineCreatedByField,
8921
+ defineCustomView,
8652
8922
  defineDateField,
8653
8923
  defineDateTimeField,
8654
8924
  defineEmailField,
@@ -8664,6 +8934,7 @@ export {
8664
8934
  defineMultiSelectField,
8665
8935
  defineNumberField,
8666
8936
  defineObjectField,
8937
+ definePage,
8667
8938
  definePublishingWorkflow,
8668
8939
  defineRadioField,
8669
8940
  defineRelationshipField,