@dyrected/core 2.16.0 → 2.17.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
@@ -1845,6 +1845,21 @@ var DyrectedAIError = class _DyrectedAIError extends Error {
1845
1845
  }
1846
1846
  };
1847
1847
 
1848
+ // src/types/navigation.ts
1849
+ function defineWorkspace(options) {
1850
+ return options;
1851
+ }
1852
+ var CURRENT_NAV_PREFERENCES_VERSION = 1;
1853
+ var DEFAULT_USER_NAV_PREFERENCES = {
1854
+ _version: CURRENT_NAV_PREFERENCES_VERSION,
1855
+ pinned: [],
1856
+ hidden: [],
1857
+ groupOrder: [],
1858
+ itemOrder: {},
1859
+ groups: [],
1860
+ items: []
1861
+ };
1862
+
1848
1863
  // src/auth/token.ts
1849
1864
  import { SignJWT, jwtVerify, decodeJwt } from "jose";
1850
1865
 
@@ -3721,7 +3736,33 @@ function normalizeConfig(config) {
3721
3736
  }
3722
3737
  ];
3723
3738
  }
3739
+ if (!existingFieldNames.has("__preferences")) {
3740
+ fields = [
3741
+ ...fields,
3742
+ {
3743
+ name: "__preferences",
3744
+ type: "json",
3745
+ label: "Preferences",
3746
+ promoted: true,
3747
+ admin: {
3748
+ hidden: true,
3749
+ readOnly: true
3750
+ }
3751
+ }
3752
+ ];
3753
+ }
3724
3754
  fields = fields.map((field) => {
3755
+ if (field.name === "__preferences") {
3756
+ return {
3757
+ ...field,
3758
+ promoted: true,
3759
+ admin: {
3760
+ ...field.admin || {},
3761
+ hidden: true,
3762
+ readOnly: true
3763
+ }
3764
+ };
3765
+ }
3725
3766
  if (field.name === "email") {
3726
3767
  return {
3727
3768
  ...field,
@@ -6528,6 +6569,525 @@ function sanitizeDocForAI(options) {
6528
6569
  return result;
6529
6570
  }
6530
6571
 
6572
+ // src/utils/navigation-reconciler.ts
6573
+ var normalizeKey = (val) => (val || "").toLowerCase().replace(/[^a-z0-9]/g, "-");
6574
+ function reconcileNavigation(baseTree, prefs, schemas, options) {
6575
+ if (!baseTree) {
6576
+ return {
6577
+ groups: [],
6578
+ ungrouped: [],
6579
+ pinnedItems: []
6580
+ };
6581
+ }
6582
+ const includeHidden = options?.includeHidden ?? false;
6583
+ const effectivePrefs = prefs || DEFAULT_USER_NAV_PREFERENCES;
6584
+ const hiddenSet = new Set(effectivePrefs.hidden || []);
6585
+ const knownCollectionSlugs = new Set((schemas?.collections || []).map((c) => c.slug));
6586
+ const knownGlobalSlugs = new Set((schemas?.globals || []).map((g) => g.slug));
6587
+ const groupsList = [];
6588
+ const findGroup = (query) => {
6589
+ const qNorm = normalizeKey(query);
6590
+ return groupsList.find(
6591
+ (g) => g.id === query || g.slug === query || g.name === query || normalizeKey(g.id) === qNorm || normalizeKey(g.slug) === qNorm || normalizeKey(g.name) === qNorm
6592
+ );
6593
+ };
6594
+ for (const g of baseTree.groups || []) {
6595
+ groupsList.push({
6596
+ ...g,
6597
+ items: (g.items || []).map((item) => ({ ...item }))
6598
+ });
6599
+ }
6600
+ for (const ug of effectivePrefs.groups || []) {
6601
+ const existing = findGroup(ug.slug || ug.name);
6602
+ if (!existing) {
6603
+ groupsList.push({
6604
+ id: ug.slug ? `group_${ug.slug}` : `group_${normalizeKey(ug.name)}`,
6605
+ name: ug.name,
6606
+ slug: ug.slug || normalizeKey(ug.name),
6607
+ icon: ug.icon || "Folder",
6608
+ defaultExpanded: ug.defaultExpanded ?? true,
6609
+ order: ug.order ?? 100,
6610
+ items: []
6611
+ });
6612
+ }
6613
+ }
6614
+ for (const itemOpt of effectivePrefs.items || []) {
6615
+ const targetGroup = typeof itemOpt.group === "object" ? itemOpt.group?.name : itemOpt.group;
6616
+ const groupKey = targetGroup || "Custom";
6617
+ let group = findGroup(groupKey);
6618
+ if (!group) {
6619
+ group = {
6620
+ id: `group_${normalizeKey(groupKey)}`,
6621
+ name: groupKey,
6622
+ slug: normalizeKey(groupKey),
6623
+ icon: "Folder",
6624
+ defaultExpanded: true,
6625
+ order: 99,
6626
+ items: []
6627
+ };
6628
+ groupsList.push(group);
6629
+ }
6630
+ const itemId = itemOpt.slug ? `workspace_${itemOpt.slug}` : itemOpt.collection ? `collection_${itemOpt.collection}` : `custom_${Math.random()}`;
6631
+ for (const otherGroup of groupsList) {
6632
+ if (otherGroup !== group) {
6633
+ otherGroup.items = otherGroup.items.filter((i) => i.slug !== itemOpt.slug && i.id !== itemId);
6634
+ }
6635
+ }
6636
+ const existingIndex = group.items.findIndex((i) => i.id === itemId || i.slug === itemOpt.slug);
6637
+ const existingItem = existingIndex >= 0 ? group.items[existingIndex] : void 0;
6638
+ const col = schemas?.collections?.find((c) => c.slug === itemOpt.collection);
6639
+ const colViews = col?.views || [];
6640
+ const baseViews = existingItem?.views || [];
6641
+ const userViews = itemOpt.views || [];
6642
+ const userViewSlugs = new Set(userViews.map((v) => v.slug));
6643
+ let mergedViews = [
6644
+ ...baseViews.filter((v) => !userViewSlugs.has(v.slug)),
6645
+ ...userViews
6646
+ ];
6647
+ if (mergedViews.length === 0 && itemOpt.collection) {
6648
+ if (colViews.length > 0) {
6649
+ mergedViews = [...colViews];
6650
+ } else {
6651
+ mergedViews = [
6652
+ {
6653
+ slug: "default",
6654
+ label: col?.label || col?.labels?.plural || itemOpt.label || "All Records",
6655
+ layout: "table",
6656
+ collection: itemOpt.collection
6657
+ }
6658
+ ];
6659
+ }
6660
+ }
6661
+ mergedViews = mergedViews.map((v) => ({
6662
+ ...v,
6663
+ collection: v.collection || itemOpt.collection || existingItem?.collection
6664
+ }));
6665
+ const compiledItem = {
6666
+ id: itemId,
6667
+ type: itemOpt.slug ? "workspace" : itemOpt.collection ? "collection" : itemOpt.global ? "global" : "workspace",
6668
+ slug: itemOpt.slug || itemOpt.collection || itemOpt.global || "item",
6669
+ label: itemOpt.label || itemOpt.slug || "Item",
6670
+ icon: itemOpt.icon || existingItem?.icon || col?.admin?.icon || "Briefcase",
6671
+ group: group.name,
6672
+ order: itemOpt.order ?? existingItem?.order ?? 100,
6673
+ views: mergedViews,
6674
+ collection: itemOpt.collection || existingItem?.collection,
6675
+ global: itemOpt.global || existingItem?.global,
6676
+ badge: itemOpt.badge || existingItem?.badge,
6677
+ access: itemOpt.access || existingItem?.access
6678
+ };
6679
+ if (existingIndex >= 0) {
6680
+ group.items[existingIndex] = compiledItem;
6681
+ } else {
6682
+ group.items.push(compiledItem);
6683
+ }
6684
+ }
6685
+ const allAvailableItems = [];
6686
+ const reconciledGroups = [];
6687
+ for (const group of groupsList) {
6688
+ const isGroupHidden = hiddenSet.has(group.id) || !!group.slug && hiddenSet.has(group.slug) || hiddenSet.has(group.name);
6689
+ if (!includeHidden && isGroupHidden) {
6690
+ continue;
6691
+ }
6692
+ const visibleItems = [];
6693
+ for (const item of group.items) {
6694
+ const isItemHidden = hiddenSet.has(item.id) || !!item.slug && hiddenSet.has(item.slug);
6695
+ if (!includeHidden && isItemHidden) {
6696
+ continue;
6697
+ }
6698
+ const allViews = item.views || [];
6699
+ const visibleViews = includeHidden ? allViews : allViews.filter((v) => {
6700
+ const compoundKey = `${item.slug}_${v.slug}`;
6701
+ return !hiddenSet.has(compoundKey) && !hiddenSet.has(v.slug);
6702
+ });
6703
+ let isTombstone = false;
6704
+ if (schemas && schemas.collections && schemas.collections.length > 0) {
6705
+ if (item.collection && !knownCollectionSlugs.has(item.collection)) {
6706
+ isTombstone = true;
6707
+ }
6708
+ }
6709
+ if (schemas && schemas.globals && schemas.globals.length > 0) {
6710
+ if (item.global && !knownGlobalSlugs.has(item.global)) {
6711
+ isTombstone = true;
6712
+ }
6713
+ }
6714
+ const processedItem = {
6715
+ ...item,
6716
+ views: visibleViews,
6717
+ ...isTombstone ? { isTombstone: true } : {}
6718
+ };
6719
+ visibleItems.push(processedItem);
6720
+ allAvailableItems.push(processedItem);
6721
+ }
6722
+ const groupKey = group.slug || group.name;
6723
+ const itemOrderList = effectivePrefs.itemOrder?.[group.id] || effectivePrefs.itemOrder?.[groupKey];
6724
+ if (itemOrderList && itemOrderList.length > 0) {
6725
+ visibleItems.sort((a, b) => {
6726
+ const aIdx = itemOrderList.indexOf(a.id) >= 0 ? itemOrderList.indexOf(a.id) : a.slug ? itemOrderList.indexOf(a.slug) : -1;
6727
+ const bIdx = itemOrderList.indexOf(b.id) >= 0 ? itemOrderList.indexOf(b.id) : b.slug ? itemOrderList.indexOf(b.slug) : -1;
6728
+ if (aIdx >= 0 && bIdx >= 0) return aIdx - bIdx;
6729
+ if (aIdx >= 0) return -1;
6730
+ if (bIdx >= 0) return 1;
6731
+ return (a.order ?? 100) - (b.order ?? 100);
6732
+ });
6733
+ } else {
6734
+ visibleItems.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
6735
+ }
6736
+ reconciledGroups.push({
6737
+ ...group,
6738
+ items: visibleItems
6739
+ });
6740
+ }
6741
+ if (effectivePrefs.groupOrder && effectivePrefs.groupOrder.length > 0) {
6742
+ reconciledGroups.sort((a, b) => {
6743
+ const aIdx = effectivePrefs.groupOrder.indexOf(a.id) >= 0 ? effectivePrefs.groupOrder.indexOf(a.id) : a.slug ? effectivePrefs.groupOrder.indexOf(a.slug) : effectivePrefs.groupOrder.indexOf(a.name);
6744
+ const bIdx = effectivePrefs.groupOrder.indexOf(b.id) >= 0 ? effectivePrefs.groupOrder.indexOf(b.id) : b.slug ? effectivePrefs.groupOrder.indexOf(b.slug) : effectivePrefs.groupOrder.indexOf(b.name);
6745
+ if (aIdx >= 0 && bIdx >= 0) return aIdx - bIdx;
6746
+ if (aIdx >= 0) return -1;
6747
+ if (bIdx >= 0) return 1;
6748
+ return (a.order ?? 100) - (b.order ?? 100);
6749
+ });
6750
+ } else {
6751
+ reconciledGroups.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
6752
+ }
6753
+ const reconciledUngrouped = (baseTree.ungrouped || []).filter((item) => {
6754
+ const isItemHidden = hiddenSet.has(item.id) || !!item.slug && hiddenSet.has(item.slug);
6755
+ return includeHidden || !isItemHidden;
6756
+ }).map((item) => {
6757
+ const allViews = item.views || [];
6758
+ const visibleViews = includeHidden ? allViews : allViews.filter((v) => {
6759
+ const compoundKey = `${item.slug}_${v.slug}`;
6760
+ return !hiddenSet.has(compoundKey) && !hiddenSet.has(v.slug);
6761
+ });
6762
+ const processed = { ...item, views: visibleViews };
6763
+ allAvailableItems.push(processed);
6764
+ return processed;
6765
+ });
6766
+ const pinnedItems = [];
6767
+ for (const pin of effectivePrefs.pinned || []) {
6768
+ const match = allAvailableItems.find((i) => i.slug === pin.slug || i.id === pin.slug);
6769
+ if (match) {
6770
+ pinnedItems.push({
6771
+ ...match,
6772
+ id: `pin_${match.id}`,
6773
+ label: pin.label || match.label,
6774
+ icon: pin.icon || match.icon
6775
+ });
6776
+ } else {
6777
+ pinnedItems.push({
6778
+ id: `pin_${pin.slug}`,
6779
+ type: pin.type,
6780
+ slug: pin.slug,
6781
+ label: pin.label || pin.slug,
6782
+ icon: pin.icon || "Star",
6783
+ order: 0,
6784
+ views: []
6785
+ });
6786
+ }
6787
+ }
6788
+ return {
6789
+ groups: reconciledGroups,
6790
+ ungrouped: reconciledUngrouped,
6791
+ pinnedItems
6792
+ };
6793
+ }
6794
+
6795
+ // src/utils/navigation.ts
6796
+ var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
6797
+ "collections",
6798
+ "globals",
6799
+ "setup",
6800
+ "api",
6801
+ "login",
6802
+ "logout",
6803
+ "settings",
6804
+ "admin",
6805
+ "preferences",
6806
+ "auth"
6807
+ ]);
6808
+ function assertValidNavigationSlugs(navigation) {
6809
+ for (const item of navigation) {
6810
+ if (item.slug) {
6811
+ const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
6812
+ if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
6813
+ throw new Error(
6814
+ `[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.`
6815
+ );
6816
+ }
6817
+ }
6818
+ }
6819
+ }
6820
+ function resolveGroupMeta(groupProp, defaultGroupName = "Collections") {
6821
+ if (!groupProp) {
6822
+ return { name: defaultGroupName };
6823
+ }
6824
+ if (typeof groupProp === "string") {
6825
+ return { name: groupProp };
6826
+ }
6827
+ return { name: groupProp.name, meta: groupProp };
6828
+ }
6829
+ function compileNavigation(config) {
6830
+ const collections = config.collections || [];
6831
+ const globals = config.globals || [];
6832
+ const explicitNav = config.admin?.navigation || [];
6833
+ assertValidNavigationSlugs(explicitNav);
6834
+ const collectionsBySlug = /* @__PURE__ */ new Map();
6835
+ for (const col of collections) {
6836
+ collectionsBySlug.set(col.slug, col);
6837
+ }
6838
+ const globalsBySlug = /* @__PURE__ */ new Map();
6839
+ for (const gl of globals) {
6840
+ globalsBySlug.set(gl.slug, gl);
6841
+ }
6842
+ const mentionedCollections = /* @__PURE__ */ new Set();
6843
+ const mentionedGlobals = /* @__PURE__ */ new Set();
6844
+ const injectedViewsByCollection = /* @__PURE__ */ new Map();
6845
+ for (const item of explicitNav) {
6846
+ if (item.addToCollection) {
6847
+ const existing = injectedViewsByCollection.get(item.addToCollection) || [];
6848
+ if (item.views && item.views.length > 0) {
6849
+ injectedViewsByCollection.set(item.addToCollection, [...existing, ...item.views]);
6850
+ }
6851
+ }
6852
+ if (item.collection) mentionedCollections.add(item.collection);
6853
+ if (item.global) mentionedGlobals.add(item.global);
6854
+ }
6855
+ const groupMetadataMap = /* @__PURE__ */ new Map();
6856
+ const itemsByGroup = /* @__PURE__ */ new Map();
6857
+ const ungroupedItems = [];
6858
+ const registerItem = (groupName, item) => {
6859
+ if (!groupName) {
6860
+ ungroupedItems.push(item);
6861
+ } else {
6862
+ const list = itemsByGroup.get(groupName) || [];
6863
+ list.push(item);
6864
+ itemsByGroup.set(groupName, list);
6865
+ }
6866
+ };
6867
+ for (const item of explicitNav) {
6868
+ if (item.addToCollection && !item.slug && !item.collection && !item.global && !item.dashboard && !item.href) {
6869
+ continue;
6870
+ }
6871
+ const { name: groupName, meta: groupMeta } = resolveGroupMeta(item.group, void 0);
6872
+ if (groupName && groupMeta) {
6873
+ groupMetadataMap.set(groupName, { ...groupMetadataMap.get(groupName) || {}, ...groupMeta });
6874
+ }
6875
+ if (item.dashboard) {
6876
+ registerItem(groupName, {
6877
+ id: "dashboard",
6878
+ type: "dashboard",
6879
+ slug: "dashboard",
6880
+ label: item.label || "Dashboard",
6881
+ icon: item.icon || "LayoutDashboard",
6882
+ group: groupName,
6883
+ order: item.order ?? 0,
6884
+ views: [],
6885
+ access: item.access
6886
+ });
6887
+ continue;
6888
+ }
6889
+ if (item.collection) {
6890
+ const col = collectionsBySlug.get(item.collection);
6891
+ const combinedViews = [
6892
+ ...col?.views || [],
6893
+ ...injectedViewsByCollection.get(item.collection) || [],
6894
+ ...item.views || []
6895
+ ];
6896
+ registerItem(groupName, {
6897
+ id: item.slug || `collection_${item.collection}`,
6898
+ type: item.slug ? "workspace" : "collection",
6899
+ slug: item.slug || item.collection,
6900
+ collection: item.collection,
6901
+ label: item.label || col?.labels?.plural || item.collection,
6902
+ icon: item.icon || col?.admin?.icon || "Folder",
6903
+ group: groupName,
6904
+ order: item.order ?? col?.admin?.order ?? 100,
6905
+ views: combinedViews,
6906
+ badge: item.badge,
6907
+ access: item.access || col?.access?.read
6908
+ });
6909
+ continue;
6910
+ }
6911
+ if (item.global) {
6912
+ const gl = globalsBySlug.get(item.global);
6913
+ registerItem(groupName, {
6914
+ id: item.slug || `global_${item.global}`,
6915
+ type: "global",
6916
+ slug: item.slug || item.global,
6917
+ global: item.global,
6918
+ label: item.label || gl?.label || item.global,
6919
+ icon: item.icon || "Settings",
6920
+ group: groupName,
6921
+ order: item.order ?? 100,
6922
+ views: [],
6923
+ badge: item.badge,
6924
+ access: item.access
6925
+ });
6926
+ continue;
6927
+ }
6928
+ if (item.href) {
6929
+ registerItem(groupName, {
6930
+ id: `link_${item.slug || item.href}`,
6931
+ type: "link",
6932
+ slug: item.slug || "link",
6933
+ label: item.label || "Link",
6934
+ href: item.href,
6935
+ icon: item.icon || "ExternalLink",
6936
+ group: groupName,
6937
+ order: item.order ?? 100,
6938
+ views: [],
6939
+ badge: item.badge,
6940
+ access: item.access
6941
+ });
6942
+ continue;
6943
+ }
6944
+ const workspaceSlug = item.slug || "workspace";
6945
+ registerItem(groupName, {
6946
+ id: `workspace_${workspaceSlug}`,
6947
+ type: "workspace",
6948
+ slug: workspaceSlug,
6949
+ label: item.label || workspaceSlug,
6950
+ icon: item.icon || "Briefcase",
6951
+ group: groupName,
6952
+ order: item.order ?? 100,
6953
+ views: item.views || [],
6954
+ badge: item.badge,
6955
+ access: item.access
6956
+ });
6957
+ }
6958
+ for (const col of collections) {
6959
+ if (mentionedCollections.has(col.slug)) continue;
6960
+ if (col.admin?.hidden) continue;
6961
+ const groupProp = col.admin?.group;
6962
+ const { name: groupName, meta: groupMeta } = resolveGroupMeta(groupProp, "Collections");
6963
+ if (groupMeta) {
6964
+ groupMetadataMap.set(groupName, { ...groupMetadataMap.get(groupName) || {}, ...groupMeta });
6965
+ }
6966
+ const combinedViews = [
6967
+ ...col.views || [],
6968
+ ...injectedViewsByCollection.get(col.slug) || []
6969
+ ];
6970
+ registerItem(groupName, {
6971
+ id: `collection_${col.slug}`,
6972
+ type: "collection",
6973
+ slug: col.slug,
6974
+ collection: col.slug,
6975
+ label: col.labels?.plural || col.slug,
6976
+ icon: col.admin?.icon || "Folder",
6977
+ group: groupName,
6978
+ order: col?.admin?.order ?? 100,
6979
+ views: combinedViews,
6980
+ access: col.access?.read
6981
+ });
6982
+ }
6983
+ for (const gl of globals) {
6984
+ if (mentionedGlobals.has(gl.slug)) continue;
6985
+ const groupName = "Configuration";
6986
+ registerItem(groupName, {
6987
+ id: `global_${gl.slug}`,
6988
+ type: "global",
6989
+ slug: gl.slug,
6990
+ global: gl.slug,
6991
+ label: gl.label || gl.slug,
6992
+ icon: "Settings",
6993
+ group: groupName,
6994
+ order: 100,
6995
+ views: []
6996
+ });
6997
+ }
6998
+ const sortAndSpliceItems = (items) => {
6999
+ const sorted = [...items].sort((a, b) => a.order - b.order);
7000
+ const explicitMap = /* @__PURE__ */ new Map();
7001
+ for (const opt of explicitNav) {
7002
+ if (opt.slug) explicitMap.set(opt.slug, opt);
7003
+ if (opt.collection) explicitMap.set(opt.collection, opt);
7004
+ if (opt.global) explicitMap.set(opt.global, opt);
7005
+ }
7006
+ for (const opt of explicitNav) {
7007
+ const itemKey = opt.slug || opt.collection || opt.global;
7008
+ if (!itemKey) continue;
7009
+ if (opt.after) {
7010
+ const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
7011
+ const targetIdx = sorted.findIndex((i) => i.slug === opt.after);
7012
+ if (itemIdx !== -1 && targetIdx !== -1 && itemIdx !== targetIdx) {
7013
+ const [moved] = sorted.splice(itemIdx, 1);
7014
+ const newTargetIdx = sorted.findIndex((i) => i.slug === opt.after);
7015
+ sorted.splice(newTargetIdx + 1, 0, moved);
7016
+ }
7017
+ } else if (opt.before) {
7018
+ const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
7019
+ const targetIdx = sorted.findIndex((i) => i.slug === opt.before);
7020
+ if (itemIdx !== -1 && targetIdx !== -1 && itemIdx !== targetIdx) {
7021
+ const [moved] = sorted.splice(itemIdx, 1);
7022
+ const newTargetIdx = sorted.findIndex((i) => i.slug === opt.before);
7023
+ sorted.splice(newTargetIdx, 0, moved);
7024
+ }
7025
+ } else if (opt.position === "first") {
7026
+ const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
7027
+ if (itemIdx > 0) {
7028
+ const [moved] = sorted.splice(itemIdx, 1);
7029
+ sorted.unshift(moved);
7030
+ }
7031
+ } else if (opt.position === "last") {
7032
+ const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
7033
+ if (itemIdx !== -1 && itemIdx < sorted.length - 1) {
7034
+ const [moved] = sorted.splice(itemIdx, 1);
7035
+ sorted.push(moved);
7036
+ }
7037
+ }
7038
+ }
7039
+ return sorted;
7040
+ };
7041
+ const compiledGroups = [];
7042
+ for (const [groupName, rawItems] of itemsByGroup.entries()) {
7043
+ const meta = groupMetadataMap.get(groupName);
7044
+ const sortedItems = sortAndSpliceItems(rawItems);
7045
+ compiledGroups.push({
7046
+ id: `group_${groupName.toLowerCase().replace(/\s+/g, "_")}`,
7047
+ name: groupName,
7048
+ slug: meta?.slug || groupName.toLowerCase().replace(/\s+/g, "-"),
7049
+ icon: meta?.icon,
7050
+ defaultExpanded: meta?.defaultExpanded ?? true,
7051
+ order: meta?.order ?? 100,
7052
+ items: sortedItems
7053
+ });
7054
+ }
7055
+ compiledGroups.sort((a, b) => a.order - b.order);
7056
+ return {
7057
+ groups: compiledGroups,
7058
+ ungrouped: sortAndSpliceItems(ungroupedItems)
7059
+ };
7060
+ }
7061
+ function pruneNavigationForUser(tree, user) {
7062
+ const userRoles = /* @__PURE__ */ new Set();
7063
+ if (user?.role) userRoles.add(user.role);
7064
+ if (Array.isArray(user?.roles)) {
7065
+ for (const r of user.roles) userRoles.add(r);
7066
+ }
7067
+ const isAllowed = (item) => {
7068
+ if (!item.access) return true;
7069
+ if (Array.isArray(item.access)) {
7070
+ if (item.access.length === 0) return true;
7071
+ return item.access.some((role) => userRoles.has(role));
7072
+ }
7073
+ return true;
7074
+ };
7075
+ const prunedGroups = [];
7076
+ for (const group of tree.groups) {
7077
+ const visibleItems = group.items.filter(isAllowed);
7078
+ if (visibleItems.length > 0) {
7079
+ prunedGroups.push({
7080
+ ...group,
7081
+ items: visibleItems
7082
+ });
7083
+ }
7084
+ }
7085
+ return {
7086
+ groups: prunedGroups,
7087
+ ungrouped: tree.ungrouped.filter(isAllowed)
7088
+ };
7089
+ }
7090
+
6531
7091
  // src/index.ts
6532
7092
  import { z } from "zod";
6533
7093
  function defineCollection(config) {
@@ -6594,8 +7154,10 @@ export {
6594
7154
  BASE62_ALPHABET,
6595
7155
  BUILTIN_JEXL_HELPERS,
6596
7156
  CROCKFORD_BASE32,
7157
+ CURRENT_NAV_PREFERENCES_VERSION,
6597
7158
  ConfigValidationError,
6598
7159
  ConflictError,
7160
+ DEFAULT_USER_NAV_PREFERENCES,
6599
7161
  DuplicateKeyError,
6600
7162
  DyrectedAIError,
6601
7163
  DyrectedError,
@@ -6608,6 +7170,7 @@ export {
6608
7170
  NON_NEGOTIABLE_CREDENTIAL_FIELDS,
6609
7171
  NotFoundError,
6610
7172
  PII_REGEX_PATTERNS,
7173
+ RESERVED_NAVIGATION_SLUGS,
6611
7174
  ValidationError,
6612
7175
  WORKFLOW_HISTORY_COLLECTION,
6613
7176
  abs,
@@ -6618,6 +7181,7 @@ export {
6618
7181
  assertValidDeclarativeHookExpression,
6619
7182
  assertValidDeclarativeHookResult,
6620
7183
  assertValidDeclarativeHooksInConfig,
7184
+ assertValidNavigationSlugs,
6621
7185
  assertValidPreviewUrlsInConfig,
6622
7186
  availableWorkflowTransitions,
6623
7187
  buildSqlOrderBy,
@@ -6632,6 +7196,7 @@ export {
6632
7196
  collectDeclarativeHookValidationIssues,
6633
7197
  collectPreviewUrlValidationIssues,
6634
7198
  compact,
7199
+ compileNavigation,
6635
7200
  count,
6636
7201
  createLifecycleEvent,
6637
7202
  createWorkflowDocument,
@@ -6672,6 +7237,7 @@ export {
6672
7237
  defineTimeField,
6673
7238
  defineUrlField,
6674
7239
  defineView,
7240
+ defineWorkspace,
6675
7241
  diffDays,
6676
7242
  dispatchLifecycleEvent,
6677
7243
  dispatchPendingLifecycleEvents,
@@ -6738,9 +7304,11 @@ export {
6738
7304
  parseSqlWhere,
6739
7305
  pow,
6740
7306
  prefixedId,
7307
+ pruneNavigationForUser,
6741
7308
  publishedStateName,
6742
7309
  publishingWorkflow,
6743
7310
  readingTime,
7311
+ reconcileNavigation,
6744
7312
  registerJexlHelpers,
6745
7313
  replace,
6746
7314
  resolveAdminAuthCollection,