@dyrected/core 2.21.0 → 2.23.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.js CHANGED
@@ -6643,20 +6643,12 @@ function isUserAdmin(user, collection) {
6643
6643
  }
6644
6644
 
6645
6645
  // src/utils/collection-search.ts
6646
- var DEFAULT_SEARCHABLE_NAMES = /* @__PURE__ */ new Set([
6647
- "title",
6648
- "name",
6649
- "label",
6650
- "slug",
6651
- "email",
6652
- "caption",
6653
- "description"
6654
- ]);
6655
6646
  var MAX_RELATION_MATCHES = 50;
6647
+ var MAX_DEFAULT_SEARCH_FIELDS = 8;
6656
6648
  var MAX_RELATION_TITLE_DEPTH = 8;
6657
6649
  var TITLE_FALLBACK_NAMES = ["title", "name", "label", "heading", "email", "subject"];
6658
6650
  function isScalarSearchField(field) {
6659
- return field.type === "text" || field.type === "textarea" || field.type === "richText" || field.type === "email";
6651
+ return field.type === "text" || field.type === "textarea" || field.type === "richText" || field.type === "email" || field.type === "url";
6660
6652
  }
6661
6653
  function findFieldByName(collection, fieldName) {
6662
6654
  if (!fieldName) return void 0;
@@ -6671,10 +6663,20 @@ function resolveCollectionTitleField(collection) {
6671
6663
  }
6672
6664
  return collection.fields.find((field) => !!field.name && field.type !== "join" && field.type !== "row");
6673
6665
  }
6666
+ function flattenNamedFields(fields) {
6667
+ const result = [];
6668
+ for (const field of fields) {
6669
+ if (field.type === "row" && field.fields) {
6670
+ result.push(...flattenNamedFields(field.fields));
6671
+ continue;
6672
+ }
6673
+ if (field.name) result.push(field);
6674
+ }
6675
+ return result;
6676
+ }
6674
6677
  function getCollectionSearchableFields(collection) {
6675
- const fieldByName = new Map(
6676
- collection.fields.filter((field) => !!field.name).map((field) => [field.name, field])
6677
- );
6678
+ const namedFields = flattenNamedFields(collection.fields);
6679
+ const fieldByName = new Map(namedFields.map((field) => [field.name, field]));
6678
6680
  const configured = collection.admin?.searchableFields?.map((fieldName) => fieldByName.get(fieldName)).filter((field) => !!field);
6679
6681
  if (configured && configured.length > 0) {
6680
6682
  return configured;
@@ -6685,12 +6687,12 @@ function getCollectionSearchableFields(collection) {
6685
6687
  if (results.some((item) => item.name === field.name)) return;
6686
6688
  results.push(field);
6687
6689
  };
6688
- pushUnique(findFieldByName(collection, collection.admin?.useAsTitle));
6689
- for (const field of collection.fields) {
6690
- if (!field.name) continue;
6691
- if (isScalarSearchField(field) && DEFAULT_SEARCHABLE_NAMES.has(field.name)) {
6692
- pushUnique(field);
6693
- }
6690
+ const titleField = collection.admin?.useAsTitle ? fieldByName.get(collection.admin.useAsTitle) : void 0;
6691
+ pushUnique(titleField);
6692
+ for (const field of namedFields) {
6693
+ if (results.length >= MAX_DEFAULT_SEARCH_FIELDS) break;
6694
+ if (field.admin?.hidden || !isScalarSearchField(field)) continue;
6695
+ pushUnique(field);
6694
6696
  }
6695
6697
  return results;
6696
6698
  }
@@ -7115,11 +7117,32 @@ var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
7115
7117
  "admin",
7116
7118
  "preferences",
7117
7119
  "auth",
7118
- "trash"
7120
+ "trash",
7121
+ "pages"
7119
7122
  ]);
7120
7123
  function assertValidNavigationSlugs(navigation) {
7124
+ const seenWorkspaceSlugs = /* @__PURE__ */ new Set();
7125
+ const seenPageSlugs = /* @__PURE__ */ new Set();
7121
7126
  for (const item of navigation) {
7122
7127
  if (item.dashboard || item.trash) continue;
7128
+ if (item.page) {
7129
+ const pageSlug = typeof item.page === "string" ? item.slug || item.page : item.slug || item.page.slug;
7130
+ if (pageSlug) {
7131
+ const normalized = pageSlug.toLowerCase().trim().replace(/^\//, "");
7132
+ if (RESERVED_NAVIGATION_SLUGS.has(normalized)) {
7133
+ throw new Error(
7134
+ `[Dyrected Config] The slug '${pageSlug}' in custom page '${item.label || pageSlug}' is a reserved system path and cannot be used as a page slug.`
7135
+ );
7136
+ }
7137
+ if (seenPageSlugs.has(normalized)) {
7138
+ throw new Error(
7139
+ `[Dyrected Config] Duplicate page slug '${pageSlug}' in navigation. Page slugs must be unique.`
7140
+ );
7141
+ }
7142
+ seenPageSlugs.add(normalized);
7143
+ }
7144
+ continue;
7145
+ }
7123
7146
  if (item.slug) {
7124
7147
  const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
7125
7148
  if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
@@ -7127,6 +7150,12 @@ function assertValidNavigationSlugs(navigation) {
7127
7150
  `[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.`
7128
7151
  );
7129
7152
  }
7153
+ if (seenWorkspaceSlugs.has(normalizedSlug)) {
7154
+ throw new Error(
7155
+ `[Dyrected Config] Duplicate workspace slug '${item.slug}' in navigation. Workspace slugs must be unique.`
7156
+ );
7157
+ }
7158
+ seenWorkspaceSlugs.add(normalizedSlug);
7130
7159
  }
7131
7160
  }
7132
7161
  }
@@ -7269,6 +7298,43 @@ function compileNavigation(config) {
7269
7298
  });
7270
7299
  continue;
7271
7300
  }
7301
+ if (item.page) {
7302
+ const pageConfig = typeof item.page === "string" ? {
7303
+ slug: item.slug || item.page,
7304
+ label: item.label || item.page,
7305
+ icon: item.icon,
7306
+ component: item.page,
7307
+ group: typeof item.group === "string" ? item.group : item.group?.name,
7308
+ order: item.order,
7309
+ badge: item.badge,
7310
+ access: item.access
7311
+ } : {
7312
+ ...item.page,
7313
+ slug: item.slug || item.page.slug || "page",
7314
+ label: item.label || item.page.label || item.slug || "Page",
7315
+ icon: item.icon || item.page.icon,
7316
+ group: item.group ? typeof item.group === "string" ? item.group : item.group?.name : item.page.group,
7317
+ order: item.order ?? item.page.order,
7318
+ badge: item.badge || item.page.badge,
7319
+ access: item.access || (item.page.access && typeof item.page.access === "object" && "read" in item.page.access ? item.page.access.read : item.page.access)
7320
+ };
7321
+ const pageSlug = pageConfig.slug || "page";
7322
+ registerItem(groupName, {
7323
+ id: `page:${pageSlug}`,
7324
+ type: "page",
7325
+ slug: pageSlug,
7326
+ label: pageConfig.label || pageSlug,
7327
+ icon: pageConfig.icon || "FileText",
7328
+ href: `/pages/${pageSlug}`,
7329
+ group: groupName,
7330
+ order: pageConfig.order ?? 100,
7331
+ views: [],
7332
+ badge: pageConfig.badge,
7333
+ access: pageConfig.access && typeof pageConfig.access === "object" && "read" in pageConfig.access ? pageConfig.access.read : pageConfig.access,
7334
+ page: pageConfig
7335
+ });
7336
+ continue;
7337
+ }
7272
7338
  const workspaceSlug = item.slug || "workspace";
7273
7339
  registerItem(groupName, {
7274
7340
  id: `workspace_${workspaceSlug}`,
@@ -16776,6 +16842,9 @@ async function serializeViewForApi(view, serializeAccess) {
16776
16842
  dateField: view.dateField,
16777
16843
  startDateField: view.startDateField,
16778
16844
  endDateField: view.endDateField,
16845
+ component: view.layout === "custom" ? view.component : void 0,
16846
+ options: view.layout === "custom" ? view.options : void 0,
16847
+ data: view.layout === "custom" ? view.data : void 0,
16779
16848
  columns: view.columns,
16780
16849
  sort: view.sort,
16781
16850
  metrics: view.metrics,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/core",
3
- "version": "2.21.0",
3
+ "version": "2.23.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",