@dyrected/core 2.22.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/index.js CHANGED
@@ -1789,9 +1789,19 @@ var LIFECYCLE_EVENT_NAMES = [
1789
1789
  ];
1790
1790
 
1791
1791
  // src/types/views.ts
1792
+ var CUSTOM_VIEW_FETCH_ALL_LIMIT = 1e3;
1792
1793
  function defineView(config) {
1794
+ assertCustomViewComponent(config);
1793
1795
  return config;
1794
1796
  }
1797
+ function assertCustomViewComponent(config) {
1798
+ if (config.layout !== "custom") return;
1799
+ if (typeof config.component !== "string" || config.component.trim() === "") {
1800
+ throw new Error(
1801
+ `View "${config.slug ?? "(unnamed)"}" uses layout: "custom" but has no \`component\`. Set \`component\` to a key registered in AdminComponents.viewLayouts.`
1802
+ );
1803
+ }
1804
+ }
1795
1805
  function defineTableView(config) {
1796
1806
  return config;
1797
1807
  }
@@ -1804,6 +1814,10 @@ function defineCalendarView(config) {
1804
1814
  function defineGanttView(config) {
1805
1815
  return config;
1806
1816
  }
1817
+ function defineCustomView(config) {
1818
+ assertCustomViewComponent(config);
1819
+ return config;
1820
+ }
1807
1821
  function defineCardsView(config) {
1808
1822
  return config;
1809
1823
  }
@@ -1860,6 +1874,23 @@ var DEFAULT_USER_NAV_PREFERENCES = {
1860
1874
  items: []
1861
1875
  };
1862
1876
 
1877
+ // src/types/pages.ts
1878
+ function definePage(config) {
1879
+ return {
1880
+ slug: config.slug,
1881
+ label: config.label,
1882
+ icon: config.icon,
1883
+ group: config.group,
1884
+ order: config.order,
1885
+ after: config.after,
1886
+ before: config.before,
1887
+ position: config.position,
1888
+ badge: config.badge,
1889
+ access: config.access && typeof config.access === "object" && "read" in config.access ? config.access.read : config.access,
1890
+ page: config
1891
+ };
1892
+ }
1893
+
1863
1894
  // src/auth/token.ts
1864
1895
  import { SignJWT, jwtVerify, decodeJwt } from "jose";
1865
1896
 
@@ -5189,6 +5220,12 @@ var DuplicateKeyError = class _DuplicateKeyError extends ConflictError {
5189
5220
  function assertNever(op, context) {
5190
5221
  throw new Error(`[dyrected/core] Unhandled where operator "${op}" in ${context}`);
5191
5222
  }
5223
+ function escapeLike(value) {
5224
+ return value.replace(/[!%_]/g, "!$&");
5225
+ }
5226
+ function escapeRegex(value) {
5227
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5228
+ }
5192
5229
  function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = placeholder === "pg" ? "ILIKE" : "LIKE") {
5193
5230
  const params = [];
5194
5231
  let pgIndex = 1;
@@ -5198,6 +5235,19 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = pl
5198
5235
  function col(field) {
5199
5236
  return field === "id" ? "id" : getJsonField(field);
5200
5237
  }
5238
+ function buildLike(column, pattern, escaped = false) {
5239
+ const esc = escaped ? ` ESCAPE '!'` : "";
5240
+ if (placeholder === "pg") {
5241
+ params.push(pattern);
5242
+ return `(${column})::text ${likeOperator} ${next()}${esc}`;
5243
+ }
5244
+ if (likeOperator === "ILIKE") {
5245
+ params.push(pattern);
5246
+ return `${column} ILIKE ${next()}${esc}`;
5247
+ }
5248
+ params.push(pattern.toLowerCase());
5249
+ return `LOWER(${column}) LIKE ${next()}${esc}`;
5250
+ }
5201
5251
  function buildOperator(field, value) {
5202
5252
  const c = col(field);
5203
5253
  const isJsonExtract = c.startsWith("data->>") || c.startsWith("json_extract(") || c.startsWith("JSON_UNQUOTE(");
@@ -5340,13 +5390,12 @@ function parseSqlWhere(where, getJsonField, placeholder = "?", likeOperator = pl
5340
5390
  case "less_than_or_equal":
5341
5391
  params.push(operand);
5342
5392
  return `${c} <= ${next()}`;
5343
- case "contains":
5344
5393
  case "like":
5345
- params.push(`%${operand}%`);
5346
- return placeholder === "pg" ? `(${c})::text ${likeOperator} ${next()}` : `${c} ${likeOperator} ${next()}`;
5394
+ return buildLike(c, `%${String(operand)}%`);
5395
+ case "contains":
5396
+ return buildLike(c, `%${escapeLike(String(operand))}%`, true);
5347
5397
  case "starts_with":
5348
- params.push(`${operand}%`);
5349
- return placeholder === "pg" ? `(${c})::text ${likeOperator} ${next()}` : `${c} ${likeOperator} ${next()}`;
5398
+ return buildLike(c, `${escapeLike(String(operand))}%`, true);
5350
5399
  case "exists":
5351
5400
  return operand ? `${c} IS NOT NULL` : `${c} IS NULL`;
5352
5401
  default:
@@ -5444,9 +5493,9 @@ function parseMongoWhere(where) {
5444
5493
  return { [field]: { $lte: operand } };
5445
5494
  case "contains":
5446
5495
  case "like":
5447
- return { [field]: { $regex: operand, $options: "i" } };
5496
+ return { [field]: { $regex: escapeRegex(String(operand)), $options: "i" } };
5448
5497
  case "starts_with":
5449
- return { [field]: { $regex: `^${operand}`, $options: "i" } };
5498
+ return { [field]: { $regex: `^${escapeRegex(String(operand))}`, $options: "i" } };
5450
5499
  case "exists":
5451
5500
  return { [field]: { $exists: operand } };
5452
5501
  default:
@@ -5556,6 +5605,96 @@ function buildSqlOrderBy(sort, existingColumns, numericFields, dialect) {
5556
5605
  }).join(", ");
5557
5606
  }
5558
5607
 
5608
+ // src/utils/component-keys.ts
5609
+ var DASHBOARD_SLOTS = ["beforeDashboard", "afterDashboard"];
5610
+ var LIST_SLOTS = ["beforeList", "beforeListTable", "afterListTable", "afterList"];
5611
+ var VIEW_SLOTS = ["beforeViewHeader", "afterViewHeader", "beforeViewContent", "afterViewContent"];
5612
+ function addKeys(target, value) {
5613
+ for (const key of Array.isArray(value) ? value : [value]) {
5614
+ if (typeof key === "string" && key) target.add(key);
5615
+ }
5616
+ }
5617
+ function addSlots(target, slots, names) {
5618
+ if (!slots || typeof slots !== "object") return;
5619
+ for (const name of names) addKeys(target, slots[name]);
5620
+ }
5621
+ function scanFields(fields, keys) {
5622
+ if (!Array.isArray(fields)) return;
5623
+ for (const field of fields) {
5624
+ if (!field || typeof field !== "object") continue;
5625
+ addKeys(keys.fields, field.admin?.component);
5626
+ scanFields(field.fields, keys);
5627
+ if (Array.isArray(field.blocks)) {
5628
+ for (const block of field.blocks) scanFields(block?.fields, keys);
5629
+ }
5630
+ if (Array.isArray(field.tabs)) {
5631
+ for (const tab of field.tabs) scanFields(tab?.fields, keys);
5632
+ }
5633
+ }
5634
+ }
5635
+ function scanDetail(items, keys) {
5636
+ if (!Array.isArray(items)) return;
5637
+ for (const item of items) {
5638
+ if (!item || typeof item !== "object") continue;
5639
+ if (item.type === "custom") addKeys(keys.customComponents, item.name);
5640
+ scanDetail(item.items, keys);
5641
+ if (Array.isArray(item.tabs)) {
5642
+ for (const tab of item.tabs) scanDetail(tab?.items, keys);
5643
+ }
5644
+ }
5645
+ }
5646
+ function scanViews(views, keys) {
5647
+ if (!Array.isArray(views)) return;
5648
+ for (const view of views) {
5649
+ addSlots(keys.collectionView, view?.components, VIEW_SLOTS);
5650
+ if (view?.layout === "custom") addKeys(keys.viewLayouts, view.component);
5651
+ }
5652
+ }
5653
+ function collectReferencedComponentKeys(schema) {
5654
+ const keys = {
5655
+ fields: /* @__PURE__ */ new Set(),
5656
+ dashboard: /* @__PURE__ */ new Set(),
5657
+ collectionList: /* @__PURE__ */ new Set(),
5658
+ collectionView: /* @__PURE__ */ new Set(),
5659
+ forms: /* @__PURE__ */ new Set(),
5660
+ viewLayouts: /* @__PURE__ */ new Set(),
5661
+ pages: /* @__PURE__ */ new Set(),
5662
+ customComponents: /* @__PURE__ */ new Set()
5663
+ };
5664
+ const root = schema;
5665
+ if (!root || typeof root !== "object") return keys;
5666
+ addSlots(keys.dashboard, root.admin?.components, DASHBOARD_SLOTS);
5667
+ if (Array.isArray(root.admin?.navigation)) {
5668
+ for (const item of root.admin.navigation) {
5669
+ if (item?.page) {
5670
+ if (typeof item.page === "string") {
5671
+ addKeys(keys.pages, item.page);
5672
+ } else if (item.page?.component) {
5673
+ addKeys(keys.pages, item.page.component);
5674
+ }
5675
+ }
5676
+ }
5677
+ }
5678
+ if (Array.isArray(root.admin?.pages)) {
5679
+ for (const page of root.admin.pages) addKeys(keys.pages, page?.component);
5680
+ }
5681
+ for (const block of root.blocks ?? []) scanFields(block?.fields, keys);
5682
+ for (const collection of root.collections ?? []) {
5683
+ scanFields(collection.fields, keys);
5684
+ scanDetail(collection.detail, keys);
5685
+ scanViews(collection.views, keys);
5686
+ const slots = collection.admin?.components;
5687
+ addSlots(keys.collectionList, slots, LIST_SLOTS);
5688
+ addSlots(keys.collectionView, slots, VIEW_SLOTS);
5689
+ addSlots(keys.collectionView, slots?.collectionView, VIEW_SLOTS);
5690
+ }
5691
+ for (const global of root.globals ?? []) {
5692
+ scanFields(global.fields, keys);
5693
+ scanDetail(global.detail, keys);
5694
+ }
5695
+ return keys;
5696
+ }
5697
+
5559
5698
  // src/utils/openapi.ts
5560
5699
  function getCollectionLabels(collection) {
5561
5700
  return collection.labels || { singular: collection.slug, plural: collection.slug };
@@ -8043,11 +8182,32 @@ var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
8043
8182
  "admin",
8044
8183
  "preferences",
8045
8184
  "auth",
8046
- "trash"
8185
+ "trash",
8186
+ "pages"
8047
8187
  ]);
8048
8188
  function assertValidNavigationSlugs(navigation) {
8189
+ const seenWorkspaceSlugs = /* @__PURE__ */ new Set();
8190
+ const seenPageSlugs = /* @__PURE__ */ new Set();
8049
8191
  for (const item of navigation) {
8050
8192
  if (item.dashboard || item.trash) continue;
8193
+ if (item.page) {
8194
+ const pageSlug = typeof item.page === "string" ? item.slug || item.page : item.slug || item.page.slug;
8195
+ if (pageSlug) {
8196
+ const normalized = pageSlug.toLowerCase().trim().replace(/^\//, "");
8197
+ if (RESERVED_NAVIGATION_SLUGS.has(normalized)) {
8198
+ throw new Error(
8199
+ `[Dyrected Config] The slug '${pageSlug}' in custom page '${item.label || pageSlug}' is a reserved system path and cannot be used as a page slug.`
8200
+ );
8201
+ }
8202
+ if (seenPageSlugs.has(normalized)) {
8203
+ throw new Error(
8204
+ `[Dyrected Config] Duplicate page slug '${pageSlug}' in navigation. Page slugs must be unique.`
8205
+ );
8206
+ }
8207
+ seenPageSlugs.add(normalized);
8208
+ }
8209
+ continue;
8210
+ }
8051
8211
  if (item.slug) {
8052
8212
  const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
8053
8213
  if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
@@ -8055,6 +8215,12 @@ function assertValidNavigationSlugs(navigation) {
8055
8215
  `[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
8216
  );
8057
8217
  }
8218
+ if (seenWorkspaceSlugs.has(normalizedSlug)) {
8219
+ throw new Error(
8220
+ `[Dyrected Config] Duplicate workspace slug '${item.slug}' in navigation. Workspace slugs must be unique.`
8221
+ );
8222
+ }
8223
+ seenWorkspaceSlugs.add(normalizedSlug);
8058
8224
  }
8059
8225
  }
8060
8226
  }
@@ -8197,6 +8363,43 @@ function compileNavigation(config) {
8197
8363
  });
8198
8364
  continue;
8199
8365
  }
8366
+ if (item.page) {
8367
+ const pageConfig = typeof item.page === "string" ? {
8368
+ slug: item.slug || item.page,
8369
+ label: item.label || item.page,
8370
+ icon: item.icon,
8371
+ component: item.page,
8372
+ group: typeof item.group === "string" ? item.group : item.group?.name,
8373
+ order: item.order,
8374
+ badge: item.badge,
8375
+ access: item.access
8376
+ } : {
8377
+ ...item.page,
8378
+ slug: item.slug || item.page.slug || "page",
8379
+ label: item.label || item.page.label || item.slug || "Page",
8380
+ icon: item.icon || item.page.icon,
8381
+ group: item.group ? typeof item.group === "string" ? item.group : item.group?.name : item.page.group,
8382
+ order: item.order ?? item.page.order,
8383
+ badge: item.badge || item.page.badge,
8384
+ access: item.access || (item.page.access && typeof item.page.access === "object" && "read" in item.page.access ? item.page.access.read : item.page.access)
8385
+ };
8386
+ const pageSlug = pageConfig.slug || "page";
8387
+ registerItem(groupName, {
8388
+ id: `page:${pageSlug}`,
8389
+ type: "page",
8390
+ slug: pageSlug,
8391
+ label: pageConfig.label || pageSlug,
8392
+ icon: pageConfig.icon || "FileText",
8393
+ href: `/pages/${pageSlug}`,
8394
+ group: groupName,
8395
+ order: pageConfig.order ?? 100,
8396
+ views: [],
8397
+ badge: pageConfig.badge,
8398
+ access: pageConfig.access && typeof pageConfig.access === "object" && "read" in pageConfig.access ? pageConfig.access.read : pageConfig.access,
8399
+ page: pageConfig
8400
+ });
8401
+ continue;
8402
+ }
8200
8403
  const workspaceSlug = item.slug || "workspace";
8201
8404
  registerItem(groupName, {
8202
8405
  id: `workspace_${workspaceSlug}`,
@@ -8578,6 +8781,7 @@ export {
8578
8781
  BUILTIN_JEXL_HELPERS,
8579
8782
  CROCKFORD_BASE32,
8580
8783
  CURRENT_NAV_PREFERENCES_VERSION,
8784
+ CUSTOM_VIEW_FETCH_ALL_LIMIT,
8581
8785
  ConfigValidationError,
8582
8786
  ConflictError,
8583
8787
  DEFAULT_USER_NAV_PREFERENCES,
@@ -8629,6 +8833,7 @@ export {
8629
8833
  collectDeclarativeAccessValidationIssues,
8630
8834
  collectDeclarativeHookValidationIssues,
8631
8835
  collectPreviewUrlValidationIssues,
8836
+ collectReferencedComponentKeys,
8632
8837
  compact,
8633
8838
  compileNavigation,
8634
8839
  count,
@@ -8649,6 +8854,7 @@ export {
8649
8854
  defineConfig,
8650
8855
  defineCreatedAtField,
8651
8856
  defineCreatedByField,
8857
+ defineCustomView,
8652
8858
  defineDateField,
8653
8859
  defineDateTimeField,
8654
8860
  defineEmailField,
@@ -8664,6 +8870,7 @@ export {
8664
8870
  defineMultiSelectField,
8665
8871
  defineNumberField,
8666
8872
  defineObjectField,
8873
+ definePage,
8667
8874
  definePublishingWorkflow,
8668
8875
  defineRadioField,
8669
8876
  defineRelationshipField,
package/dist/server.cjs CHANGED
@@ -6713,20 +6713,12 @@ function isUserAdmin(user, collection) {
6713
6713
  }
6714
6714
 
6715
6715
  // 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
6716
  var MAX_RELATION_MATCHES = 50;
6717
+ var MAX_DEFAULT_SEARCH_FIELDS = 8;
6726
6718
  var MAX_RELATION_TITLE_DEPTH = 8;
6727
6719
  var TITLE_FALLBACK_NAMES = ["title", "name", "label", "heading", "email", "subject"];
6728
6720
  function isScalarSearchField(field) {
6729
- return field.type === "text" || field.type === "textarea" || field.type === "richText" || field.type === "email";
6721
+ return field.type === "text" || field.type === "textarea" || field.type === "richText" || field.type === "email" || field.type === "url";
6730
6722
  }
6731
6723
  function findFieldByName(collection, fieldName) {
6732
6724
  if (!fieldName) return void 0;
@@ -6741,10 +6733,20 @@ function resolveCollectionTitleField(collection) {
6741
6733
  }
6742
6734
  return collection.fields.find((field) => !!field.name && field.type !== "join" && field.type !== "row");
6743
6735
  }
6736
+ function flattenNamedFields(fields) {
6737
+ const result = [];
6738
+ for (const field of fields) {
6739
+ if (field.type === "row" && field.fields) {
6740
+ result.push(...flattenNamedFields(field.fields));
6741
+ continue;
6742
+ }
6743
+ if (field.name) result.push(field);
6744
+ }
6745
+ return result;
6746
+ }
6744
6747
  function getCollectionSearchableFields(collection) {
6745
- const fieldByName = new Map(
6746
- collection.fields.filter((field) => !!field.name).map((field) => [field.name, field])
6747
- );
6748
+ const namedFields = flattenNamedFields(collection.fields);
6749
+ const fieldByName = new Map(namedFields.map((field) => [field.name, field]));
6748
6750
  const configured = collection.admin?.searchableFields?.map((fieldName) => fieldByName.get(fieldName)).filter((field) => !!field);
6749
6751
  if (configured && configured.length > 0) {
6750
6752
  return configured;
@@ -6755,12 +6757,12 @@ function getCollectionSearchableFields(collection) {
6755
6757
  if (results.some((item) => item.name === field.name)) return;
6756
6758
  results.push(field);
6757
6759
  };
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
- }
6760
+ const titleField = collection.admin?.useAsTitle ? fieldByName.get(collection.admin.useAsTitle) : void 0;
6761
+ pushUnique(titleField);
6762
+ for (const field of namedFields) {
6763
+ if (results.length >= MAX_DEFAULT_SEARCH_FIELDS) break;
6764
+ if (field.admin?.hidden || !isScalarSearchField(field)) continue;
6765
+ pushUnique(field);
6764
6766
  }
6765
6767
  return results;
6766
6768
  }
@@ -7185,11 +7187,32 @@ var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
7185
7187
  "admin",
7186
7188
  "preferences",
7187
7189
  "auth",
7188
- "trash"
7190
+ "trash",
7191
+ "pages"
7189
7192
  ]);
7190
7193
  function assertValidNavigationSlugs(navigation) {
7194
+ const seenWorkspaceSlugs = /* @__PURE__ */ new Set();
7195
+ const seenPageSlugs = /* @__PURE__ */ new Set();
7191
7196
  for (const item of navigation) {
7192
7197
  if (item.dashboard || item.trash) continue;
7198
+ if (item.page) {
7199
+ const pageSlug = typeof item.page === "string" ? item.slug || item.page : item.slug || item.page.slug;
7200
+ if (pageSlug) {
7201
+ const normalized = pageSlug.toLowerCase().trim().replace(/^\//, "");
7202
+ if (RESERVED_NAVIGATION_SLUGS.has(normalized)) {
7203
+ throw new Error(
7204
+ `[Dyrected Config] The slug '${pageSlug}' in custom page '${item.label || pageSlug}' is a reserved system path and cannot be used as a page slug.`
7205
+ );
7206
+ }
7207
+ if (seenPageSlugs.has(normalized)) {
7208
+ throw new Error(
7209
+ `[Dyrected Config] Duplicate page slug '${pageSlug}' in navigation. Page slugs must be unique.`
7210
+ );
7211
+ }
7212
+ seenPageSlugs.add(normalized);
7213
+ }
7214
+ continue;
7215
+ }
7193
7216
  if (item.slug) {
7194
7217
  const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
7195
7218
  if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
@@ -7197,6 +7220,12 @@ function assertValidNavigationSlugs(navigation) {
7197
7220
  `[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
7221
  );
7199
7222
  }
7223
+ if (seenWorkspaceSlugs.has(normalizedSlug)) {
7224
+ throw new Error(
7225
+ `[Dyrected Config] Duplicate workspace slug '${item.slug}' in navigation. Workspace slugs must be unique.`
7226
+ );
7227
+ }
7228
+ seenWorkspaceSlugs.add(normalizedSlug);
7200
7229
  }
7201
7230
  }
7202
7231
  }
@@ -7339,6 +7368,43 @@ function compileNavigation(config) {
7339
7368
  });
7340
7369
  continue;
7341
7370
  }
7371
+ if (item.page) {
7372
+ const pageConfig = typeof item.page === "string" ? {
7373
+ slug: item.slug || item.page,
7374
+ label: item.label || item.page,
7375
+ icon: item.icon,
7376
+ component: item.page,
7377
+ group: typeof item.group === "string" ? item.group : item.group?.name,
7378
+ order: item.order,
7379
+ badge: item.badge,
7380
+ access: item.access
7381
+ } : {
7382
+ ...item.page,
7383
+ slug: item.slug || item.page.slug || "page",
7384
+ label: item.label || item.page.label || item.slug || "Page",
7385
+ icon: item.icon || item.page.icon,
7386
+ group: item.group ? typeof item.group === "string" ? item.group : item.group?.name : item.page.group,
7387
+ order: item.order ?? item.page.order,
7388
+ badge: item.badge || item.page.badge,
7389
+ access: item.access || (item.page.access && typeof item.page.access === "object" && "read" in item.page.access ? item.page.access.read : item.page.access)
7390
+ };
7391
+ const pageSlug = pageConfig.slug || "page";
7392
+ registerItem(groupName, {
7393
+ id: `page:${pageSlug}`,
7394
+ type: "page",
7395
+ slug: pageSlug,
7396
+ label: pageConfig.label || pageSlug,
7397
+ icon: pageConfig.icon || "FileText",
7398
+ href: `/pages/${pageSlug}`,
7399
+ group: groupName,
7400
+ order: pageConfig.order ?? 100,
7401
+ views: [],
7402
+ badge: pageConfig.badge,
7403
+ access: pageConfig.access && typeof pageConfig.access === "object" && "read" in pageConfig.access ? pageConfig.access.read : pageConfig.access,
7404
+ page: pageConfig
7405
+ });
7406
+ continue;
7407
+ }
7342
7408
  const workspaceSlug = item.slug || "workspace";
7343
7409
  registerItem(groupName, {
7344
7410
  id: `workspace_${workspaceSlug}`,
@@ -16841,6 +16907,9 @@ async function serializeViewForApi(view, serializeAccess) {
16841
16907
  dateField: view.dateField,
16842
16908
  startDateField: view.startDateField,
16843
16909
  endDateField: view.endDateField,
16910
+ component: view.layout === "custom" ? view.component : void 0,
16911
+ options: view.layout === "custom" ? view.options : void 0,
16912
+ data: view.layout === "custom" ? view.data : void 0,
16844
16913
  columns: view.columns,
16845
16914
  sort: view.sort,
16846
16915
  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, c9 as EmailTemplateArgs, ca as EmailTemplateResult, d1 as OutboundEmail, e as DatabaseAdapter, b as AuthenticatedUser, c5 as DyrectedAIContext, ax as AIThread, at as AIMessage, d9 as RAGSearchResult, aw as AIRateLimitStore, av as AIRateLimitResult } from './app-DregJnqR.cjs';
2
+ export { e2 as CollectionTokenPayload, e3 as DEFAULT_INVITE_EXPIRY, e4 as DEFAULT_RESET_PASSWORD_EXPIRY, e5 as DEFAULT_SESSION_EXPIRY, e6 as DyrectedObservabilityRuntime, e7 as RequestBodyCapture, e8 as RequestTraceContext, e9 as ResolvedObservabilityConfig, ea as attachRequestMetrics, eb as bindObservabilityRuntime, ec as buildRequestLogger, ed as captureRequestBody, ee as createDyrectedApp, ef as createObservabilityRuntime, eg as createRequestSpan, eh as decodeCollectionToken, ei as endRequestSpan, ej as getConfigLogger, ek as getObservabilityRuntime, el as getRequestLogger, em as redactBody, en as redactHeaders, eo as renderPrometheusMetrics, ep as resolveInviteTokenExpiry, eq as resolveObservabilityConfig, er as resolveResetPasswordTokenExpiry, es as resolveSessionTokenExpiry, et as shouldSampleRequest, eu as signCollectionToken, ev as verifyCollectionToken } from './app-DregJnqR.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, c9 as EmailTemplateArgs, ca as EmailTemplateResult, d1 as OutboundEmail, e as DatabaseAdapter, b as AuthenticatedUser, c5 as DyrectedAIContext, ax as AIThread, at as AIMessage, d9 as RAGSearchResult, aw as AIRateLimitStore, av as AIRateLimitResult } from './app-DregJnqR.js';
2
+ export { e2 as CollectionTokenPayload, e3 as DEFAULT_INVITE_EXPIRY, e4 as DEFAULT_RESET_PASSWORD_EXPIRY, e5 as DEFAULT_SESSION_EXPIRY, e6 as DyrectedObservabilityRuntime, e7 as RequestBodyCapture, e8 as RequestTraceContext, e9 as ResolvedObservabilityConfig, ea as attachRequestMetrics, eb as bindObservabilityRuntime, ec as buildRequestLogger, ed as captureRequestBody, ee as createDyrectedApp, ef as createObservabilityRuntime, eg as createRequestSpan, eh as decodeCollectionToken, ei as endRequestSpan, ej as getConfigLogger, ek as getObservabilityRuntime, el as getRequestLogger, em as redactBody, en as redactHeaders, eo as renderPrometheusMetrics, ep as resolveInviteTokenExpiry, eq as resolveObservabilityConfig, er as resolveResetPasswordTokenExpiry, es as resolveSessionTokenExpiry, et as shouldSampleRequest, eu as signCollectionToken, ev as verifyCollectionToken } from './app-DregJnqR.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';