@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/server.cjs CHANGED
@@ -1784,6 +1784,243 @@ var require_Jexl = __commonJS({
1784
1784
  }
1785
1785
  });
1786
1786
 
1787
+ // src/types/access.ts
1788
+ var init_access = __esm({
1789
+ "src/types/access.ts"() {
1790
+ "use strict";
1791
+ }
1792
+ });
1793
+
1794
+ // src/types/adapters.ts
1795
+ var init_adapters = __esm({
1796
+ "src/types/adapters.ts"() {
1797
+ "use strict";
1798
+ }
1799
+ });
1800
+
1801
+ // src/types/aggregate.ts
1802
+ var init_aggregate = __esm({
1803
+ "src/types/aggregate.ts"() {
1804
+ "use strict";
1805
+ }
1806
+ });
1807
+
1808
+ // src/types/admin.ts
1809
+ var FORM_SHELLS;
1810
+ var init_admin = __esm({
1811
+ "src/types/admin.ts"() {
1812
+ "use strict";
1813
+ FORM_SHELLS = ["full", "header", "none"];
1814
+ }
1815
+ });
1816
+
1817
+ // src/types/admin-auth.ts
1818
+ var init_admin_auth = __esm({
1819
+ "src/types/admin-auth.ts"() {
1820
+ "use strict";
1821
+ }
1822
+ });
1823
+
1824
+ // src/types/app-config.ts
1825
+ var init_app_config = __esm({
1826
+ "src/types/app-config.ts"() {
1827
+ "use strict";
1828
+ }
1829
+ });
1830
+
1831
+ // src/types/documents.ts
1832
+ var init_documents = __esm({
1833
+ "src/types/documents.ts"() {
1834
+ "use strict";
1835
+ }
1836
+ });
1837
+
1838
+ // src/types/hooks.ts
1839
+ var init_hooks = __esm({
1840
+ "src/types/hooks.ts"() {
1841
+ "use strict";
1842
+ }
1843
+ });
1844
+
1845
+ // src/types/request.ts
1846
+ var init_request = __esm({
1847
+ "src/types/request.ts"() {
1848
+ "use strict";
1849
+ }
1850
+ });
1851
+
1852
+ // src/types/schema-config.ts
1853
+ var init_schema_config = __esm({
1854
+ "src/types/schema-config.ts"() {
1855
+ "use strict";
1856
+ }
1857
+ });
1858
+
1859
+ // src/types/schema-core.ts
1860
+ var init_schema_core = __esm({
1861
+ "src/types/schema-core.ts"() {
1862
+ "use strict";
1863
+ }
1864
+ });
1865
+
1866
+ // src/types/schema-inference.ts
1867
+ var init_schema_inference = __esm({
1868
+ "src/types/schema-inference.ts"() {
1869
+ "use strict";
1870
+ }
1871
+ });
1872
+
1873
+ // src/types/workflows.ts
1874
+ var init_workflows = __esm({
1875
+ "src/types/workflows.ts"() {
1876
+ "use strict";
1877
+ }
1878
+ });
1879
+
1880
+ // src/types/tasks.ts
1881
+ var init_tasks = __esm({
1882
+ "src/types/tasks.ts"() {
1883
+ "use strict";
1884
+ }
1885
+ });
1886
+
1887
+ // src/types/detail.ts
1888
+ var init_detail = __esm({
1889
+ "src/types/detail.ts"() {
1890
+ "use strict";
1891
+ }
1892
+ });
1893
+
1894
+ // src/types/views.ts
1895
+ var init_views = __esm({
1896
+ "src/types/views.ts"() {
1897
+ "use strict";
1898
+ }
1899
+ });
1900
+
1901
+ // src/types/ai.ts
1902
+ var ai_exports = {};
1903
+ __export(ai_exports, {
1904
+ AI_ACTIONS_COLLECTION: () => AI_ACTIONS_COLLECTION,
1905
+ AI_AUDIT_COLLECTION: () => AI_AUDIT_COLLECTION,
1906
+ AI_CHUNKS_COLLECTION: () => AI_CHUNKS_COLLECTION,
1907
+ AI_MESSAGES_COLLECTION: () => AI_MESSAGES_COLLECTION,
1908
+ AI_THREADS_COLLECTION: () => AI_THREADS_COLLECTION,
1909
+ isAICollection: () => isAICollection
1910
+ });
1911
+ function isAICollection(slug) {
1912
+ return slug === AI_THREADS_COLLECTION || slug === AI_MESSAGES_COLLECTION || slug === AI_CHUNKS_COLLECTION || slug === AI_ACTIONS_COLLECTION || slug === AI_AUDIT_COLLECTION;
1913
+ }
1914
+ var AI_THREADS_COLLECTION, AI_MESSAGES_COLLECTION, AI_CHUNKS_COLLECTION, AI_ACTIONS_COLLECTION, AI_AUDIT_COLLECTION;
1915
+ var init_ai = __esm({
1916
+ "src/types/ai.ts"() {
1917
+ "use strict";
1918
+ AI_THREADS_COLLECTION = "_dyrected_ai_threads";
1919
+ AI_MESSAGES_COLLECTION = "_dyrected_ai_messages";
1920
+ AI_CHUNKS_COLLECTION = "_dyrected_ai_chunks";
1921
+ AI_ACTIONS_COLLECTION = "_dyrected_ai_actions";
1922
+ AI_AUDIT_COLLECTION = "_dyrected_ai_audit";
1923
+ }
1924
+ });
1925
+
1926
+ // src/types/ai-errors.ts
1927
+ var DyrectedAIError;
1928
+ var init_ai_errors = __esm({
1929
+ "src/types/ai-errors.ts"() {
1930
+ "use strict";
1931
+ DyrectedAIError = class _DyrectedAIError extends Error {
1932
+ code;
1933
+ status;
1934
+ details;
1935
+ constructor(code, message, status = 500, details) {
1936
+ super(message);
1937
+ this.name = "DyrectedAIError";
1938
+ this.code = code;
1939
+ this.status = status;
1940
+ this.details = details;
1941
+ Object.setPrototypeOf(this, _DyrectedAIError.prototype);
1942
+ }
1943
+ toJSON() {
1944
+ return {
1945
+ error: true,
1946
+ code: this.code,
1947
+ message: this.message,
1948
+ status: this.status,
1949
+ details: this.details
1950
+ };
1951
+ }
1952
+ };
1953
+ }
1954
+ });
1955
+
1956
+ // src/types/readonly-fix.ts
1957
+ var init_readonly_fix = __esm({
1958
+ "src/types/readonly-fix.ts"() {
1959
+ "use strict";
1960
+ }
1961
+ });
1962
+
1963
+ // src/types/navigation.ts
1964
+ var CURRENT_NAV_PREFERENCES_VERSION, DEFAULT_USER_NAV_PREFERENCES;
1965
+ var init_navigation = __esm({
1966
+ "src/types/navigation.ts"() {
1967
+ "use strict";
1968
+ CURRENT_NAV_PREFERENCES_VERSION = 1;
1969
+ DEFAULT_USER_NAV_PREFERENCES = {
1970
+ _version: CURRENT_NAV_PREFERENCES_VERSION,
1971
+ pinned: [],
1972
+ hidden: [],
1973
+ groupOrder: [],
1974
+ itemOrder: {},
1975
+ groups: [],
1976
+ items: []
1977
+ };
1978
+ }
1979
+ });
1980
+
1981
+ // src/types/email.ts
1982
+ var init_email = __esm({
1983
+ "src/types/email.ts"() {
1984
+ "use strict";
1985
+ }
1986
+ });
1987
+
1988
+ // src/types/pages.ts
1989
+ var init_pages = __esm({
1990
+ "src/types/pages.ts"() {
1991
+ "use strict";
1992
+ }
1993
+ });
1994
+
1995
+ // src/types/index.ts
1996
+ var init_types = __esm({
1997
+ "src/types/index.ts"() {
1998
+ "use strict";
1999
+ init_access();
2000
+ init_adapters();
2001
+ init_aggregate();
2002
+ init_admin();
2003
+ init_admin_auth();
2004
+ init_app_config();
2005
+ init_documents();
2006
+ init_hooks();
2007
+ init_request();
2008
+ init_schema_config();
2009
+ init_schema_core();
2010
+ init_schema_inference();
2011
+ init_workflows();
2012
+ init_tasks();
2013
+ init_detail();
2014
+ init_views();
2015
+ init_ai();
2016
+ init_ai_errors();
2017
+ init_readonly_fix();
2018
+ init_navigation();
2019
+ init_email();
2020
+ init_pages();
2021
+ }
2022
+ });
2023
+
1787
2024
  // src/utils/jexl-helpers.ts
1788
2025
  function slugify(val) {
1789
2026
  if (val === void 0 || val === null) return "";
@@ -2587,6 +2824,52 @@ function collectPreviewUrlValidationIssues(config) {
2587
2824
  });
2588
2825
  return issues;
2589
2826
  }
2827
+ function collectFormComponentIssues(components, path, surface, keys, issues) {
2828
+ if (!isPlainObject(components)) return;
2829
+ for (const key of keys) {
2830
+ const value = components[key];
2831
+ if (value === void 0) continue;
2832
+ if (typeof value !== "string" || value.length === 0) {
2833
+ issues.push({
2834
+ path: `${path}.admin.components.${key}`,
2835
+ surface,
2836
+ message: `\`${key}\` must be the registry key of a component, such as "check_ins.scanForm".`,
2837
+ expression: String(value)
2838
+ });
2839
+ }
2840
+ }
2841
+ const shell = components.formShell;
2842
+ if (shell !== void 0 && !FORM_SHELLS.includes(shell)) {
2843
+ issues.push({
2844
+ path: `${path}.admin.components.formShell`,
2845
+ surface,
2846
+ message: `Unknown formShell "${String(shell)}". Use ${FORM_SHELLS.map((value) => `"${value}"`).join(", ")}.`,
2847
+ expression: String(shell)
2848
+ });
2849
+ }
2850
+ }
2851
+ function collectAdminComponentsValidationIssues(config) {
2852
+ const issues = [];
2853
+ config.collections.forEach((collection, index) => {
2854
+ collectFormComponentIssues(
2855
+ collection.admin?.components,
2856
+ collectionSegment(collection, index),
2857
+ "collection.admin.components",
2858
+ FORM_COMPONENT_KEYS,
2859
+ issues
2860
+ );
2861
+ });
2862
+ config.globals?.forEach((global, index) => {
2863
+ collectFormComponentIssues(
2864
+ global.admin?.components,
2865
+ globalSegment(global, index),
2866
+ "global.admin.components",
2867
+ GLOBAL_FORM_COMPONENT_KEYS,
2868
+ issues
2869
+ );
2870
+ });
2871
+ return issues;
2872
+ }
2590
2873
  function assertValidAdminConditionsInConfig(config, source = "config") {
2591
2874
  const diagnostics = collectAdminConditionValidationIssues(config).map((issue) => ({
2592
2875
  severity: "error",
@@ -2644,7 +2927,15 @@ function collectConfigDiagnostics(config) {
2644
2927
  message: issue.message,
2645
2928
  expression: issue.expression
2646
2929
  }));
2647
- return [...hookIssues, ...accessIssues, ...adminConditionIssues, ...previewUrlIssues];
2930
+ const adminComponentIssues = collectAdminComponentsValidationIssues(config).map((issue) => ({
2931
+ severity: "error",
2932
+ source: "adminComponents",
2933
+ path: issue.path,
2934
+ surface: issue.surface,
2935
+ message: issue.message,
2936
+ expression: issue.expression
2937
+ }));
2938
+ return [...hookIssues, ...accessIssues, ...adminConditionIssues, ...previewUrlIssues, ...adminComponentIssues];
2648
2939
  }
2649
2940
  function formatConfigDiagnostics(source, diagnostics, options = {}) {
2650
2941
  const color = options.color ?? false;
@@ -2684,11 +2975,12 @@ function assertValidDeclarativeAccessInConfig(config, source = "config") {
2684
2975
  if (diagnostics.length === 0) return;
2685
2976
  throw new ConfigValidationError(source, diagnostics);
2686
2977
  }
2687
- var import_jexl3, SURFACE_ROOTS, ACCESS_ROOTS, ADMIN_CONDITION_RESERVED_ROOTS, PREVIEW_URL_RESERVED_ROOTS, COMPILE_CACHE, ConfigValidationError;
2978
+ var import_jexl3, SURFACE_ROOTS, ACCESS_ROOTS, ADMIN_CONDITION_RESERVED_ROOTS, PREVIEW_URL_RESERVED_ROOTS, COMPILE_CACHE, FORM_COMPONENT_KEYS, GLOBAL_FORM_COMPONENT_KEYS, ConfigValidationError;
2688
2979
  var init_declarative_hooks = __esm({
2689
2980
  "src/utils/declarative-hooks.ts"() {
2690
2981
  "use strict";
2691
2982
  import_jexl3 = __toESM(require_Jexl(), 1);
2983
+ init_types();
2692
2984
  init_jexl_helpers();
2693
2985
  registerJexlHelpers(import_jexl3.default);
2694
2986
  SURFACE_ROOTS = {
@@ -2705,6 +2997,8 @@ var init_declarative_hooks = __esm({
2705
2997
  ADMIN_CONDITION_RESERVED_ROOTS = ["data", "siblingData", "user", "id", ...BUILTIN_JEXL_HELPERS];
2706
2998
  PREVIEW_URL_RESERVED_ROOTS = ["siteUrl", "id", ...BUILTIN_JEXL_HELPERS];
2707
2999
  COMPILE_CACHE = /* @__PURE__ */ new Map();
3000
+ FORM_COMPONENT_KEYS = ["createForm", "editForm", "form"];
3001
+ GLOBAL_FORM_COMPONENT_KEYS = ["editForm", "form"];
2708
3002
  ConfigValidationError = class extends Error {
2709
3003
  diagnostics;
2710
3004
  source;
@@ -2917,7 +3211,7 @@ async function executeFieldAfterRead(fields, doc, user, db) {
2917
3211
  return result;
2918
3212
  }
2919
3213
  var import_jexl4;
2920
- var init_hooks = __esm({
3214
+ var init_hooks2 = __esm({
2921
3215
  "src/utils/hooks.ts"() {
2922
3216
  "use strict";
2923
3217
  import_jexl4 = __toESM(require_Jexl(), 1);
@@ -2994,7 +3288,7 @@ async function isAccessAllowed(config, access, args) {
2994
3288
  if (result === void 0) return true;
2995
3289
  return typeof result === "boolean" ? result : !!result;
2996
3290
  }
2997
- var init_access = __esm({
3291
+ var init_access2 = __esm({
2998
3292
  "src/auth/access.ts"() {
2999
3293
  "use strict";
3000
3294
  init_jexl();
@@ -3174,7 +3468,7 @@ var TRASH_COLLECTION, INTERNAL_SYSTEM_COLLECTIONS, TRASH_COLLECTION_CONFIG;
3174
3468
  var init_trash = __esm({
3175
3469
  "src/trash.ts"() {
3176
3470
  "use strict";
3177
- init_hooks();
3471
+ init_hooks2();
3178
3472
  TRASH_COLLECTION = "__trash";
3179
3473
  INTERNAL_SYSTEM_COLLECTIONS = /* @__PURE__ */ new Set([
3180
3474
  "__audit",
@@ -3400,32 +3694,7 @@ function mergeWhereConstraint(where, constraint) {
3400
3694
  var init_access_control = __esm({
3401
3695
  "src/utils/access-control.ts"() {
3402
3696
  "use strict";
3403
- init_access();
3404
- }
3405
- });
3406
-
3407
- // src/types/ai.ts
3408
- var ai_exports = {};
3409
- __export(ai_exports, {
3410
- AI_ACTIONS_COLLECTION: () => AI_ACTIONS_COLLECTION,
3411
- AI_AUDIT_COLLECTION: () => AI_AUDIT_COLLECTION,
3412
- AI_CHUNKS_COLLECTION: () => AI_CHUNKS_COLLECTION,
3413
- AI_MESSAGES_COLLECTION: () => AI_MESSAGES_COLLECTION,
3414
- AI_THREADS_COLLECTION: () => AI_THREADS_COLLECTION,
3415
- isAICollection: () => isAICollection
3416
- });
3417
- function isAICollection(slug) {
3418
- return slug === AI_THREADS_COLLECTION || slug === AI_MESSAGES_COLLECTION || slug === AI_CHUNKS_COLLECTION || slug === AI_ACTIONS_COLLECTION || slug === AI_AUDIT_COLLECTION;
3419
- }
3420
- var AI_THREADS_COLLECTION, AI_MESSAGES_COLLECTION, AI_CHUNKS_COLLECTION, AI_ACTIONS_COLLECTION, AI_AUDIT_COLLECTION;
3421
- var init_ai = __esm({
3422
- "src/types/ai.ts"() {
3423
- "use strict";
3424
- AI_THREADS_COLLECTION = "_dyrected_ai_threads";
3425
- AI_MESSAGES_COLLECTION = "_dyrected_ai_messages";
3426
- AI_CHUNKS_COLLECTION = "_dyrected_ai_chunks";
3427
- AI_ACTIONS_COLLECTION = "_dyrected_ai_actions";
3428
- AI_AUDIT_COLLECTION = "_dyrected_ai_audit";
3697
+ init_access2();
3429
3698
  }
3430
3699
  });
3431
3700
 
@@ -3974,7 +4243,7 @@ var init_rag_service = __esm({
3974
4243
  "src/services/rag/rag.service.ts"() {
3975
4244
  "use strict";
3976
4245
  init_ai();
3977
- init_access();
4246
+ init_access2();
3978
4247
  init_normalizer();
3979
4248
  init_chunker();
3980
4249
  init_embedding_service();
@@ -5412,7 +5681,7 @@ var init_ai_tools = __esm({
5412
5681
  import_ai3 = require("ai");
5413
5682
  import_zod = require("zod");
5414
5683
  init_ai();
5415
- init_access();
5684
+ init_access2();
5416
5685
  init_access_control();
5417
5686
  init_rag_service();
5418
5687
  init_ai_pii();
@@ -6511,7 +6780,7 @@ function createSessionId() {
6511
6780
  }
6512
6781
 
6513
6782
  // src/controllers/collection.controller.ts
6514
- init_hooks();
6783
+ init_hooks2();
6515
6784
 
6516
6785
  // src/utils/readonly-db.ts
6517
6786
  var WRITE_METHODS = ["create", "update", "delete", "updateGlobal", "execute"];
@@ -6639,7 +6908,7 @@ function generateUniqueUploadFilename(filename) {
6639
6908
  }
6640
6909
 
6641
6910
  // src/controllers/collection.controller.ts
6642
- init_access();
6911
+ init_access2();
6643
6912
 
6644
6913
  // src/utils/admin-auth.ts
6645
6914
  function getAdminAuthCollection(config) {
@@ -6713,20 +6982,12 @@ function isUserAdmin(user, collection) {
6713
6982
  }
6714
6983
 
6715
6984
  // src/utils/collection-search.ts
6716
- var DEFAULT_SEARCHABLE_NAMES = /* @__PURE__ */ new Set([
6717
- "title",
6718
- "name",
6719
- "label",
6720
- "slug",
6721
- "email",
6722
- "caption",
6723
- "description"
6724
- ]);
6725
6985
  var MAX_RELATION_MATCHES = 50;
6986
+ var MAX_DEFAULT_SEARCH_FIELDS = 8;
6726
6987
  var MAX_RELATION_TITLE_DEPTH = 8;
6727
6988
  var TITLE_FALLBACK_NAMES = ["title", "name", "label", "heading", "email", "subject"];
6728
6989
  function isScalarSearchField(field) {
6729
- return field.type === "text" || field.type === "textarea" || field.type === "richText" || field.type === "email";
6990
+ return field.type === "text" || field.type === "textarea" || field.type === "richText" || field.type === "email" || field.type === "url";
6730
6991
  }
6731
6992
  function findFieldByName(collection, fieldName) {
6732
6993
  if (!fieldName) return void 0;
@@ -6741,10 +7002,20 @@ function resolveCollectionTitleField(collection) {
6741
7002
  }
6742
7003
  return collection.fields.find((field) => !!field.name && field.type !== "join" && field.type !== "row");
6743
7004
  }
7005
+ function flattenNamedFields(fields) {
7006
+ const result = [];
7007
+ for (const field of fields) {
7008
+ if (field.type === "row" && field.fields) {
7009
+ result.push(...flattenNamedFields(field.fields));
7010
+ continue;
7011
+ }
7012
+ if (field.name) result.push(field);
7013
+ }
7014
+ return result;
7015
+ }
6744
7016
  function getCollectionSearchableFields(collection) {
6745
- const fieldByName = new Map(
6746
- collection.fields.filter((field) => !!field.name).map((field) => [field.name, field])
6747
- );
7017
+ const namedFields = flattenNamedFields(collection.fields);
7018
+ const fieldByName = new Map(namedFields.map((field) => [field.name, field]));
6748
7019
  const configured = collection.admin?.searchableFields?.map((fieldName) => fieldByName.get(fieldName)).filter((field) => !!field);
6749
7020
  if (configured && configured.length > 0) {
6750
7021
  return configured;
@@ -6755,12 +7026,12 @@ function getCollectionSearchableFields(collection) {
6755
7026
  if (results.some((item) => item.name === field.name)) return;
6756
7027
  results.push(field);
6757
7028
  };
6758
- pushUnique(findFieldByName(collection, collection.admin?.useAsTitle));
6759
- for (const field of collection.fields) {
6760
- if (!field.name) continue;
6761
- if (isScalarSearchField(field) && DEFAULT_SEARCHABLE_NAMES.has(field.name)) {
6762
- pushUnique(field);
6763
- }
7029
+ const titleField = collection.admin?.useAsTitle ? fieldByName.get(collection.admin.useAsTitle) : void 0;
7030
+ pushUnique(titleField);
7031
+ for (const field of namedFields) {
7032
+ if (results.length >= MAX_DEFAULT_SEARCH_FIELDS) break;
7033
+ if (field.admin?.hidden || !isScalarSearchField(field)) continue;
7034
+ pushUnique(field);
6764
7035
  }
6765
7036
  return results;
6766
7037
  }
@@ -6900,19 +7171,8 @@ async function buildCollectionSearchWhere(args) {
6900
7171
  // src/utils/navigation.ts
6901
7172
  init_trash();
6902
7173
 
6903
- // src/types/navigation.ts
6904
- var CURRENT_NAV_PREFERENCES_VERSION = 1;
6905
- var DEFAULT_USER_NAV_PREFERENCES = {
6906
- _version: CURRENT_NAV_PREFERENCES_VERSION,
6907
- pinned: [],
6908
- hidden: [],
6909
- groupOrder: [],
6910
- itemOrder: {},
6911
- groups: [],
6912
- items: []
6913
- };
6914
-
6915
7174
  // src/utils/navigation-reconciler.ts
7175
+ init_navigation();
6916
7176
  var normalizeKey = (val) => (val || "").toLowerCase().replace(/[^a-z0-9]/g, "-");
6917
7177
  function reconcileNavigation(baseTree, prefs, schemas, options) {
6918
7178
  if (!baseTree) {
@@ -7185,11 +7445,32 @@ var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
7185
7445
  "admin",
7186
7446
  "preferences",
7187
7447
  "auth",
7188
- "trash"
7448
+ "trash",
7449
+ "pages"
7189
7450
  ]);
7190
7451
  function assertValidNavigationSlugs(navigation) {
7452
+ const seenWorkspaceSlugs = /* @__PURE__ */ new Set();
7453
+ const seenPageSlugs = /* @__PURE__ */ new Set();
7191
7454
  for (const item of navigation) {
7192
7455
  if (item.dashboard || item.trash) continue;
7456
+ if (item.page) {
7457
+ const pageSlug = typeof item.page === "string" ? item.slug || item.page : item.slug || item.page.slug;
7458
+ if (pageSlug) {
7459
+ const normalized = pageSlug.toLowerCase().trim().replace(/^\//, "");
7460
+ if (RESERVED_NAVIGATION_SLUGS.has(normalized)) {
7461
+ throw new Error(
7462
+ `[Dyrected Config] The slug '${pageSlug}' in custom page '${item.label || pageSlug}' is a reserved system path and cannot be used as a page slug.`
7463
+ );
7464
+ }
7465
+ if (seenPageSlugs.has(normalized)) {
7466
+ throw new Error(
7467
+ `[Dyrected Config] Duplicate page slug '${pageSlug}' in navigation. Page slugs must be unique.`
7468
+ );
7469
+ }
7470
+ seenPageSlugs.add(normalized);
7471
+ }
7472
+ continue;
7473
+ }
7193
7474
  if (item.slug) {
7194
7475
  const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
7195
7476
  if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
@@ -7197,6 +7478,12 @@ function assertValidNavigationSlugs(navigation) {
7197
7478
  `[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.`
7198
7479
  );
7199
7480
  }
7481
+ if (seenWorkspaceSlugs.has(normalizedSlug)) {
7482
+ throw new Error(
7483
+ `[Dyrected Config] Duplicate workspace slug '${item.slug}' in navigation. Workspace slugs must be unique.`
7484
+ );
7485
+ }
7486
+ seenWorkspaceSlugs.add(normalizedSlug);
7200
7487
  }
7201
7488
  }
7202
7489
  }
@@ -7339,6 +7626,43 @@ function compileNavigation(config) {
7339
7626
  });
7340
7627
  continue;
7341
7628
  }
7629
+ if (item.page) {
7630
+ const pageConfig = typeof item.page === "string" ? {
7631
+ slug: item.slug || item.page,
7632
+ label: item.label || item.page,
7633
+ icon: item.icon,
7634
+ component: item.page,
7635
+ group: typeof item.group === "string" ? item.group : item.group?.name,
7636
+ order: item.order,
7637
+ badge: item.badge,
7638
+ access: item.access
7639
+ } : {
7640
+ ...item.page,
7641
+ slug: item.slug || item.page.slug || "page",
7642
+ label: item.label || item.page.label || item.slug || "Page",
7643
+ icon: item.icon || item.page.icon,
7644
+ group: item.group ? typeof item.group === "string" ? item.group : item.group?.name : item.page.group,
7645
+ order: item.order ?? item.page.order,
7646
+ badge: item.badge || item.page.badge,
7647
+ access: item.access || (item.page.access && typeof item.page.access === "object" && "read" in item.page.access ? item.page.access.read : item.page.access)
7648
+ };
7649
+ const pageSlug = pageConfig.slug || "page";
7650
+ registerItem(groupName, {
7651
+ id: `page:${pageSlug}`,
7652
+ type: "page",
7653
+ slug: pageSlug,
7654
+ label: pageConfig.label || pageSlug,
7655
+ icon: pageConfig.icon || "FileText",
7656
+ href: `/pages/${pageSlug}`,
7657
+ group: groupName,
7658
+ order: pageConfig.order ?? 100,
7659
+ views: [],
7660
+ badge: pageConfig.badge,
7661
+ access: pageConfig.access && typeof pageConfig.access === "object" && "read" in pageConfig.access ? pageConfig.access.read : pageConfig.access,
7662
+ page: pageConfig
7663
+ });
7664
+ continue;
7665
+ }
7342
7666
  const workspaceSlug = item.slug || "workspace";
7343
7667
  registerItem(groupName, {
7344
7668
  id: `workspace_${workspaceSlug}`,
@@ -10618,7 +10942,7 @@ var CollectionController = class _CollectionController {
10618
10942
  };
10619
10943
 
10620
10944
  // src/controllers/global.controller.ts
10621
- init_hooks();
10945
+ init_hooks2();
10622
10946
  init_access_control();
10623
10947
  var import_http_exception3 = require("hono/http-exception");
10624
10948
  var GlobalController = class {
@@ -14790,7 +15114,7 @@ var AIController = class {
14790
15114
  const dynamicConfig = await activeConfig.onSchemaFetch(projectId);
14791
15115
  activeConfig = mergeDynamicConfig(activeConfig, dynamicConfig);
14792
15116
  }
14793
- const { isAccessAllowed: isAccessAllowed2 } = await Promise.resolve().then(() => (init_access(), access_exports));
15117
+ const { isAccessAllowed: isAccessAllowed2 } = await Promise.resolve().then(() => (init_access2(), access_exports));
14794
15118
  const { validatePayloadAgainstFields: validatePayloadAgainstFields2 } = await Promise.resolve().then(() => (init_ai_tools(), ai_tools_exports));
14795
15119
  if (action.targetCollection) {
14796
15120
  const col = activeConfig.collections?.find((c2) => c2.slug === action.targetCollection);
@@ -15165,31 +15489,8 @@ function optionalAuth(config) {
15165
15489
  };
15166
15490
  }
15167
15491
 
15168
- // src/types/ai-errors.ts
15169
- var DyrectedAIError = class _DyrectedAIError extends Error {
15170
- code;
15171
- status;
15172
- details;
15173
- constructor(code, message, status = 500, details) {
15174
- super(message);
15175
- this.name = "DyrectedAIError";
15176
- this.code = code;
15177
- this.status = status;
15178
- this.details = details;
15179
- Object.setPrototypeOf(this, _DyrectedAIError.prototype);
15180
- }
15181
- toJSON() {
15182
- return {
15183
- error: true,
15184
- code: this.code,
15185
- message: this.message,
15186
- status: this.status,
15187
- details: this.details
15188
- };
15189
- }
15190
- };
15191
-
15192
15492
  // src/middleware/ai-rate-limit.ts
15493
+ init_ai_errors();
15193
15494
  var InMemoryRateLimitStore = class {
15194
15495
  windows = /* @__PURE__ */ new Map();
15195
15496
  constructor() {
@@ -16841,6 +17142,9 @@ async function serializeViewForApi(view, serializeAccess) {
16841
17142
  dateField: view.dateField,
16842
17143
  startDateField: view.startDateField,
16843
17144
  endDateField: view.endDateField,
17145
+ component: view.layout === "custom" ? view.component : void 0,
17146
+ options: view.layout === "custom" ? view.options : void 0,
17147
+ data: view.layout === "custom" ? view.data : void 0,
16844
17148
  columns: view.columns,
16845
17149
  sort: view.sort,
16846
17150
  metrics: view.metrics,
package/dist/server.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, c4 as EmailTemplateArgs, c5 as EmailTemplateResult, cY as OutboundEmail, e as DatabaseAdapter, b as AuthenticatedUser, c0 as DyrectedAIContext, ax as AIThread, at as AIMessage, d1 as RAGSearchResult, aw as AIRateLimitStore, av as AIRateLimitResult } from './app-BBHBP-6V.cjs';
2
- export { dU as CollectionTokenPayload, dV as DEFAULT_INVITE_EXPIRY, dW as DEFAULT_RESET_PASSWORD_EXPIRY, dX as DEFAULT_SESSION_EXPIRY, dY as DyrectedObservabilityRuntime, dZ as RequestBodyCapture, d_ as RequestTraceContext, d$ as ResolvedObservabilityConfig, e0 as attachRequestMetrics, e1 as bindObservabilityRuntime, e2 as buildRequestLogger, e3 as captureRequestBody, e4 as createDyrectedApp, e5 as createObservabilityRuntime, e6 as createRequestSpan, e7 as decodeCollectionToken, e8 as endRequestSpan, e9 as getConfigLogger, ea as getObservabilityRuntime, eb as getRequestLogger, ec as redactBody, ed as redactHeaders, ee as renderPrometheusMetrics, ef as resolveInviteTokenExpiry, eg as resolveObservabilityConfig, eh as resolveResetPasswordTokenExpiry, ei as resolveSessionTokenExpiry, ej as shouldSampleRequest, ek as signCollectionToken, el as verifyCollectionToken } from './app-BBHBP-6V.cjs';
1
+ import { O as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, ca as EmailTemplateArgs, cb as EmailTemplateResult, d5 as OutboundEmail, e as DatabaseAdapter, b as AuthenticatedUser, c6 as DyrectedAIContext, ax as AIThread, at as AIMessage, dd as RAGSearchResult, aw as AIRateLimitStore, av as AIRateLimitResult } from './app-BpQPL2hY.cjs';
2
+ export { e6 as CollectionTokenPayload, e7 as DEFAULT_INVITE_EXPIRY, e8 as DEFAULT_RESET_PASSWORD_EXPIRY, e9 as DEFAULT_SESSION_EXPIRY, ea as DyrectedObservabilityRuntime, eb as RequestBodyCapture, ec as RequestTraceContext, ed as ResolvedObservabilityConfig, ee as attachRequestMetrics, ef as bindObservabilityRuntime, eg as buildRequestLogger, eh as captureRequestBody, ei as createDyrectedApp, ej as createObservabilityRuntime, ek as createRequestSpan, el as decodeCollectionToken, em as endRequestSpan, en as getConfigLogger, eo as getObservabilityRuntime, ep as getRequestLogger, eq as redactBody, er as redactHeaders, es as renderPrometheusMetrics, et as resolveInviteTokenExpiry, eu as resolveObservabilityConfig, ev as resolveResetPasswordTokenExpiry, ew as resolveSessionTokenExpiry, ex as shouldSampleRequest, ey as signCollectionToken, ez as verifyCollectionToken } from './app-BpQPL2hY.cjs';
3
3
  import * as hono from 'hono';
4
4
  import { Hono, Context, Next } from 'hono';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, c4 as EmailTemplateArgs, c5 as EmailTemplateResult, cY as OutboundEmail, e as DatabaseAdapter, b as AuthenticatedUser, c0 as DyrectedAIContext, ax as AIThread, at as AIMessage, d1 as RAGSearchResult, aw as AIRateLimitStore, av as AIRateLimitResult } from './app-BBHBP-6V.js';
2
- export { dU as CollectionTokenPayload, dV as DEFAULT_INVITE_EXPIRY, dW as DEFAULT_RESET_PASSWORD_EXPIRY, dX as DEFAULT_SESSION_EXPIRY, dY as DyrectedObservabilityRuntime, dZ as RequestBodyCapture, d_ as RequestTraceContext, d$ as ResolvedObservabilityConfig, e0 as attachRequestMetrics, e1 as bindObservabilityRuntime, e2 as buildRequestLogger, e3 as captureRequestBody, e4 as createDyrectedApp, e5 as createObservabilityRuntime, e6 as createRequestSpan, e7 as decodeCollectionToken, e8 as endRequestSpan, e9 as getConfigLogger, ea as getObservabilityRuntime, eb as getRequestLogger, ec as redactBody, ed as redactHeaders, ee as renderPrometheusMetrics, ef as resolveInviteTokenExpiry, eg as resolveObservabilityConfig, eh as resolveResetPasswordTokenExpiry, ei as resolveSessionTokenExpiry, ej as shouldSampleRequest, ek as signCollectionToken, el as verifyCollectionToken } from './app-BBHBP-6V.js';
1
+ import { O as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, ca as EmailTemplateArgs, cb as EmailTemplateResult, d5 as OutboundEmail, e as DatabaseAdapter, b as AuthenticatedUser, c6 as DyrectedAIContext, ax as AIThread, at as AIMessage, dd as RAGSearchResult, aw as AIRateLimitStore, av as AIRateLimitResult } from './app-BpQPL2hY.js';
2
+ export { e6 as CollectionTokenPayload, e7 as DEFAULT_INVITE_EXPIRY, e8 as DEFAULT_RESET_PASSWORD_EXPIRY, e9 as DEFAULT_SESSION_EXPIRY, ea as DyrectedObservabilityRuntime, eb as RequestBodyCapture, ec as RequestTraceContext, ed as ResolvedObservabilityConfig, ee as attachRequestMetrics, ef as bindObservabilityRuntime, eg as buildRequestLogger, eh as captureRequestBody, ei as createDyrectedApp, ej as createObservabilityRuntime, ek as createRequestSpan, el as decodeCollectionToken, em as endRequestSpan, en as getConfigLogger, eo as getObservabilityRuntime, ep as getRequestLogger, eq as redactBody, er as redactHeaders, es as renderPrometheusMetrics, et as resolveInviteTokenExpiry, eu as resolveObservabilityConfig, ev as resolveResetPasswordTokenExpiry, ew as resolveSessionTokenExpiry, ex as shouldSampleRequest, ey as signCollectionToken, ez as verifyCollectionToken } from './app-BpQPL2hY.js';
3
3
  import * as hono from 'hono';
4
4
  import { Hono, Context, Next } from 'hono';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';