@dyrected/core 2.15.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/{app-Cmp5lFLJ.d.cts → app-CLdT_0se.d.cts} +1342 -1178
- package/dist/{app-Cmp5lFLJ.d.ts → app-CLdT_0se.d.ts} +1342 -1178
- package/dist/index.cjs +758 -1
- package/dist/index.d.cts +133 -3
- package/dist/index.d.ts +133 -3
- package/dist/index.js +739 -1
- package/dist/server.cjs +1052 -24
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1052 -24
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -6406,6 +6406,134 @@ function isPlainObject2(value) {
|
|
|
6406
6406
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6407
6407
|
}
|
|
6408
6408
|
|
|
6409
|
+
// src/utils/id.ts
|
|
6410
|
+
import { randomBytes as randomBytes2, randomUUID } from "crypto";
|
|
6411
|
+
var NANOID_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-";
|
|
6412
|
+
var BASE62_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
6413
|
+
var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
6414
|
+
var COMMON_SLUG_PREFIXES = {
|
|
6415
|
+
users: "usr",
|
|
6416
|
+
members: "mem",
|
|
6417
|
+
accounts: "acc",
|
|
6418
|
+
applications: "app",
|
|
6419
|
+
customers: "cus",
|
|
6420
|
+
products: "prd",
|
|
6421
|
+
orders: "ord",
|
|
6422
|
+
reports: "rpt",
|
|
6423
|
+
sessions: "ses",
|
|
6424
|
+
transactions: "txn",
|
|
6425
|
+
wallets: "wal",
|
|
6426
|
+
media: "med",
|
|
6427
|
+
files: "fil",
|
|
6428
|
+
posts: "post",
|
|
6429
|
+
pages: "page",
|
|
6430
|
+
tags: "tag",
|
|
6431
|
+
categories: "cat",
|
|
6432
|
+
comments: "com"
|
|
6433
|
+
};
|
|
6434
|
+
function nanoid(size = 21, alphabet = NANOID_ALPHABET) {
|
|
6435
|
+
const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
|
|
6436
|
+
const step = Math.ceil(1.6 * mask * size / alphabet.length);
|
|
6437
|
+
let id = "";
|
|
6438
|
+
while (true) {
|
|
6439
|
+
const bytes = randomBytes2(step);
|
|
6440
|
+
for (let i = 0; i < step; i++) {
|
|
6441
|
+
const byte = bytes[i] & mask;
|
|
6442
|
+
if (alphabet[byte]) {
|
|
6443
|
+
id += alphabet[byte];
|
|
6444
|
+
if (id.length === size) return id;
|
|
6445
|
+
}
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6448
|
+
}
|
|
6449
|
+
function encodeUlidTime(now2, len = 10) {
|
|
6450
|
+
let str = "";
|
|
6451
|
+
let time = Math.floor(now2);
|
|
6452
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
6453
|
+
const mod = time % 32;
|
|
6454
|
+
str = CROCKFORD_BASE32[mod] + str;
|
|
6455
|
+
time = (time - mod) / 32;
|
|
6456
|
+
}
|
|
6457
|
+
return str;
|
|
6458
|
+
}
|
|
6459
|
+
function encodeUlidRandom(bytes) {
|
|
6460
|
+
let str = "";
|
|
6461
|
+
let bitBuffer = 0;
|
|
6462
|
+
let bitsInBuffer = 0;
|
|
6463
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
6464
|
+
bitBuffer = bitBuffer << 8 | bytes[i];
|
|
6465
|
+
bitsInBuffer += 8;
|
|
6466
|
+
while (bitsInBuffer >= 5) {
|
|
6467
|
+
bitsInBuffer -= 5;
|
|
6468
|
+
const index = bitBuffer >> bitsInBuffer & 31;
|
|
6469
|
+
str += CROCKFORD_BASE32[index];
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
return str;
|
|
6473
|
+
}
|
|
6474
|
+
function ulid(seedTime = Date.now()) {
|
|
6475
|
+
const timePart = encodeUlidTime(seedTime);
|
|
6476
|
+
const randPart = encodeUlidRandom(randomBytes2(10));
|
|
6477
|
+
return timePart + randPart;
|
|
6478
|
+
}
|
|
6479
|
+
function uuid() {
|
|
6480
|
+
return randomUUID();
|
|
6481
|
+
}
|
|
6482
|
+
function resolvePrefix(slug, explicitPrefix) {
|
|
6483
|
+
if (explicitPrefix && typeof explicitPrefix === "string" && explicitPrefix.trim().length > 0) {
|
|
6484
|
+
const cleaned = explicitPrefix.trim().toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
6485
|
+
if (cleaned.length > 0) return cleaned;
|
|
6486
|
+
}
|
|
6487
|
+
if (!slug || typeof slug !== "string") return "col";
|
|
6488
|
+
const s = slug.trim().toLowerCase();
|
|
6489
|
+
if (COMMON_SLUG_PREFIXES[s]) return COMMON_SLUG_PREFIXES[s];
|
|
6490
|
+
const pieces = s.split(/[-_]/).filter(Boolean);
|
|
6491
|
+
if (pieces.length > 1 && pieces[0].length >= 2) {
|
|
6492
|
+
const first2 = pieces[0].replace(/[^a-z0-9]/g, "");
|
|
6493
|
+
if (first2.length >= 2) return first2.slice(0, 4);
|
|
6494
|
+
}
|
|
6495
|
+
const cleanWord = s.replace(/[^a-z0-9]/g, "");
|
|
6496
|
+
if (cleanWord.length <= 4) return cleanWord || "col";
|
|
6497
|
+
return cleanWord.slice(0, 3) || "col";
|
|
6498
|
+
}
|
|
6499
|
+
function prefixedId(prefix = "col", size = 16) {
|
|
6500
|
+
const cleanPrefix = (prefix || "col").toLowerCase().replace(/[^a-z0-9_]/g, "") || "col";
|
|
6501
|
+
return `${cleanPrefix}_${nanoid(size, BASE62_ALPHABET)}`;
|
|
6502
|
+
}
|
|
6503
|
+
function generateId(options) {
|
|
6504
|
+
if (typeof options === "string") {
|
|
6505
|
+
return prefixedId(resolvePrefix(options));
|
|
6506
|
+
}
|
|
6507
|
+
const opts = options || {};
|
|
6508
|
+
const type = opts.type || "prefixed-nanoid";
|
|
6509
|
+
switch (type) {
|
|
6510
|
+
case "uuid":
|
|
6511
|
+
return uuid();
|
|
6512
|
+
case "ulid":
|
|
6513
|
+
return ulid();
|
|
6514
|
+
case "nanoid":
|
|
6515
|
+
return nanoid(opts.size || 21);
|
|
6516
|
+
case "prefixed-nanoid":
|
|
6517
|
+
default:
|
|
6518
|
+
return prefixedId(opts.prefix || "col", opts.size || 16);
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
function generateDocumentId(collectionOrSlug) {
|
|
6522
|
+
if (!collectionOrSlug) {
|
|
6523
|
+
return prefixedId("col");
|
|
6524
|
+
}
|
|
6525
|
+
if (typeof collectionOrSlug === "string") {
|
|
6526
|
+
const prefix2 = resolvePrefix(collectionOrSlug);
|
|
6527
|
+
return prefixedId(prefix2);
|
|
6528
|
+
}
|
|
6529
|
+
if (typeof collectionOrSlug.idGenerator === "function") {
|
|
6530
|
+
return collectionOrSlug.idGenerator(collectionOrSlug.slug);
|
|
6531
|
+
}
|
|
6532
|
+
const type = collectionOrSlug.idType || "prefixed-nanoid";
|
|
6533
|
+
const prefix = resolvePrefix(collectionOrSlug.slug, collectionOrSlug.idPrefix);
|
|
6534
|
+
return generateId({ prefix, type });
|
|
6535
|
+
}
|
|
6536
|
+
|
|
6409
6537
|
// src/workflows.ts
|
|
6410
6538
|
var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
|
|
6411
6539
|
var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
|
|
@@ -7374,6 +7502,9 @@ var CollectionController = class {
|
|
|
7374
7502
|
surface: "collection.beforeChange",
|
|
7375
7503
|
path: `collection:${this.collection.slug}.hooks.beforeChange`
|
|
7376
7504
|
});
|
|
7505
|
+
if (!data.id) {
|
|
7506
|
+
data = { ...data, id: generateDocumentId(this.collection) };
|
|
7507
|
+
}
|
|
7377
7508
|
const doc = this.collection.workflow ? (await createWorkflowDocument({
|
|
7378
7509
|
config,
|
|
7379
7510
|
collection: this.collection,
|
|
@@ -7524,6 +7655,9 @@ var CollectionController = class {
|
|
|
7524
7655
|
surface: "collection.beforeChange",
|
|
7525
7656
|
path: `collection:${this.collection.slug}.hooks.beforeChange`
|
|
7526
7657
|
});
|
|
7658
|
+
if (!data.id) {
|
|
7659
|
+
data = { ...data, id: generateDocumentId(this.collection) };
|
|
7660
|
+
}
|
|
7527
7661
|
const doc = await db.create({
|
|
7528
7662
|
collection: this.collection.slug,
|
|
7529
7663
|
data
|
|
@@ -7789,7 +7923,8 @@ var CollectionController = class {
|
|
|
7789
7923
|
}
|
|
7790
7924
|
const body = await c.req.json().catch(() => ({}));
|
|
7791
7925
|
const requestedIds = Array.isArray(body?.ids) ? body.ids.filter((value) => typeof value === "string") : typeof body?.id === "string" ? [body.id] : [];
|
|
7792
|
-
|
|
7926
|
+
const isHeaderAction = action.type === "header";
|
|
7927
|
+
if (!requestedIds.length && !isHeaderAction) {
|
|
7793
7928
|
return c.json(
|
|
7794
7929
|
{ error: true, message: "Provide an `id` or an `ids` array of documents to act on." },
|
|
7795
7930
|
400
|
|
@@ -7817,6 +7952,30 @@ var CollectionController = class {
|
|
|
7817
7952
|
return c.json({ error: true, message: `Access denied: action "${action.name}"` }, 403);
|
|
7818
7953
|
}
|
|
7819
7954
|
}
|
|
7955
|
+
if (isHeaderAction && !requestedIds.length) {
|
|
7956
|
+
if (typeof action.handler === "function") {
|
|
7957
|
+
try {
|
|
7958
|
+
const handlerResult = await action.handler({
|
|
7959
|
+
doc: null,
|
|
7960
|
+
docs: [],
|
|
7961
|
+
user: user ?? null,
|
|
7962
|
+
input,
|
|
7963
|
+
collection: {
|
|
7964
|
+
slug: this.collection.slug,
|
|
7965
|
+
label: this.collection.labels?.singular ?? this.collection.slug
|
|
7966
|
+
}
|
|
7967
|
+
});
|
|
7968
|
+
return c.json(handlerResult ?? { success: true });
|
|
7969
|
+
} catch (error) {
|
|
7970
|
+
const status = typeof error.statusCode === "number" ? error.statusCode : 500;
|
|
7971
|
+
return c.json(
|
|
7972
|
+
{ error: true, message: error instanceof Error ? error.message : String(error) },
|
|
7973
|
+
status
|
|
7974
|
+
);
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
return c.json({ success: true });
|
|
7978
|
+
}
|
|
7820
7979
|
const results = [];
|
|
7821
7980
|
for (const id of requestedIds) {
|
|
7822
7981
|
const targetDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
@@ -9225,7 +9384,7 @@ var MediaFolderController = class {
|
|
|
9225
9384
|
};
|
|
9226
9385
|
|
|
9227
9386
|
// src/controllers/auth.controller.ts
|
|
9228
|
-
import { randomBytes as
|
|
9387
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
9229
9388
|
|
|
9230
9389
|
// src/auth/lockout.ts
|
|
9231
9390
|
var DEFAULT_MAX_LOGIN_ATTEMPTS = 5;
|
|
@@ -9536,7 +9695,7 @@ var AuthController = class {
|
|
|
9536
9695
|
const data = {
|
|
9537
9696
|
...safeExtraFields,
|
|
9538
9697
|
email,
|
|
9539
|
-
password: await hashPassword(
|
|
9698
|
+
password: await hashPassword(randomBytes3(32).toString("hex"))
|
|
9540
9699
|
};
|
|
9541
9700
|
if (this.hasField("status")) {
|
|
9542
9701
|
data.status = "pending";
|
|
@@ -10139,7 +10298,7 @@ var AuthController = class {
|
|
|
10139
10298
|
};
|
|
10140
10299
|
|
|
10141
10300
|
// src/controllers/admin-auth.controller.ts
|
|
10142
|
-
import { randomBytes as
|
|
10301
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
10143
10302
|
import { TextEncoder as TextEncoder2 } from "util";
|
|
10144
10303
|
import {
|
|
10145
10304
|
SignJWT as SignJWT2,
|
|
@@ -10354,7 +10513,7 @@ var AdminAuthController = class {
|
|
|
10354
10513
|
access.roles,
|
|
10355
10514
|
access.data
|
|
10356
10515
|
),
|
|
10357
|
-
password: await hashPassword(
|
|
10516
|
+
password: await hashPassword(randomBytes4(32).toString("hex"))
|
|
10358
10517
|
}
|
|
10359
10518
|
});
|
|
10360
10519
|
} else if (user) {
|
|
@@ -10441,7 +10600,7 @@ var AdminAuthController = class {
|
|
|
10441
10600
|
}
|
|
10442
10601
|
async buildOidcStartUrl(provider, returnTo, siteId, origin) {
|
|
10443
10602
|
const discovery = await this.getOidcDiscovery(provider);
|
|
10444
|
-
const nonce =
|
|
10603
|
+
const nonce = randomBytes4(16).toString("hex");
|
|
10445
10604
|
const state = await this.signState({
|
|
10446
10605
|
providerId: provider.id,
|
|
10447
10606
|
returnTo,
|
|
@@ -11002,7 +11161,7 @@ function formatAIErrorMessage(error) {
|
|
|
11002
11161
|
}
|
|
11003
11162
|
return msg;
|
|
11004
11163
|
}
|
|
11005
|
-
function
|
|
11164
|
+
function generateId2() {
|
|
11006
11165
|
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
11007
11166
|
}
|
|
11008
11167
|
function buildDyrectedSystemPrompt(context) {
|
|
@@ -11148,7 +11307,7 @@ var AIAgent = class {
|
|
|
11148
11307
|
}
|
|
11149
11308
|
async createThread(title, id) {
|
|
11150
11309
|
const thread = {
|
|
11151
|
-
id: id ||
|
|
11310
|
+
id: id || generateId2(),
|
|
11152
11311
|
projectId: this.projectId,
|
|
11153
11312
|
userId: this.userId,
|
|
11154
11313
|
title: title || "New Conversation",
|
|
@@ -11215,7 +11374,7 @@ var AIAgent = class {
|
|
|
11215
11374
|
}
|
|
11216
11375
|
async persistUserMessage(threadId, content) {
|
|
11217
11376
|
const message = {
|
|
11218
|
-
id:
|
|
11377
|
+
id: generateId2(),
|
|
11219
11378
|
threadId,
|
|
11220
11379
|
role: "user",
|
|
11221
11380
|
content,
|
|
@@ -11231,7 +11390,7 @@ var AIAgent = class {
|
|
|
11231
11390
|
}
|
|
11232
11391
|
async persistAssistantMessage(threadId, content, metadata, parts) {
|
|
11233
11392
|
const message = {
|
|
11234
|
-
id:
|
|
11393
|
+
id: generateId2(),
|
|
11235
11394
|
threadId,
|
|
11236
11395
|
role: "assistant",
|
|
11237
11396
|
content,
|
|
@@ -13615,6 +13774,537 @@ function getSwaggerHtml(specUrl) {
|
|
|
13615
13774
|
`;
|
|
13616
13775
|
}
|
|
13617
13776
|
|
|
13777
|
+
// src/types/navigation.ts
|
|
13778
|
+
var CURRENT_NAV_PREFERENCES_VERSION = 1;
|
|
13779
|
+
var DEFAULT_USER_NAV_PREFERENCES = {
|
|
13780
|
+
_version: CURRENT_NAV_PREFERENCES_VERSION,
|
|
13781
|
+
pinned: [],
|
|
13782
|
+
hidden: [],
|
|
13783
|
+
groupOrder: [],
|
|
13784
|
+
itemOrder: {},
|
|
13785
|
+
groups: [],
|
|
13786
|
+
items: []
|
|
13787
|
+
};
|
|
13788
|
+
|
|
13789
|
+
// src/utils/navigation-reconciler.ts
|
|
13790
|
+
var normalizeKey = (val) => (val || "").toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
13791
|
+
function reconcileNavigation(baseTree, prefs, schemas, options) {
|
|
13792
|
+
if (!baseTree) {
|
|
13793
|
+
return {
|
|
13794
|
+
groups: [],
|
|
13795
|
+
ungrouped: [],
|
|
13796
|
+
pinnedItems: []
|
|
13797
|
+
};
|
|
13798
|
+
}
|
|
13799
|
+
const includeHidden = options?.includeHidden ?? false;
|
|
13800
|
+
const effectivePrefs = prefs || DEFAULT_USER_NAV_PREFERENCES;
|
|
13801
|
+
const hiddenSet = new Set(effectivePrefs.hidden || []);
|
|
13802
|
+
const knownCollectionSlugs = new Set((schemas?.collections || []).map((c) => c.slug));
|
|
13803
|
+
const knownGlobalSlugs = new Set((schemas?.globals || []).map((g) => g.slug));
|
|
13804
|
+
const groupsList = [];
|
|
13805
|
+
const findGroup = (query) => {
|
|
13806
|
+
const qNorm = normalizeKey(query);
|
|
13807
|
+
return groupsList.find(
|
|
13808
|
+
(g) => g.id === query || g.slug === query || g.name === query || normalizeKey(g.id) === qNorm || normalizeKey(g.slug) === qNorm || normalizeKey(g.name) === qNorm
|
|
13809
|
+
);
|
|
13810
|
+
};
|
|
13811
|
+
for (const g of baseTree.groups || []) {
|
|
13812
|
+
groupsList.push({
|
|
13813
|
+
...g,
|
|
13814
|
+
items: (g.items || []).map((item) => ({ ...item }))
|
|
13815
|
+
});
|
|
13816
|
+
}
|
|
13817
|
+
for (const ug of effectivePrefs.groups || []) {
|
|
13818
|
+
const existing = findGroup(ug.slug || ug.name);
|
|
13819
|
+
if (!existing) {
|
|
13820
|
+
groupsList.push({
|
|
13821
|
+
id: ug.slug ? `group_${ug.slug}` : `group_${normalizeKey(ug.name)}`,
|
|
13822
|
+
name: ug.name,
|
|
13823
|
+
slug: ug.slug || normalizeKey(ug.name),
|
|
13824
|
+
icon: ug.icon || "Folder",
|
|
13825
|
+
defaultExpanded: ug.defaultExpanded ?? true,
|
|
13826
|
+
order: ug.order ?? 100,
|
|
13827
|
+
items: []
|
|
13828
|
+
});
|
|
13829
|
+
}
|
|
13830
|
+
}
|
|
13831
|
+
for (const itemOpt of effectivePrefs.items || []) {
|
|
13832
|
+
const targetGroup = typeof itemOpt.group === "object" ? itemOpt.group?.name : itemOpt.group;
|
|
13833
|
+
const groupKey = targetGroup || "Custom";
|
|
13834
|
+
let group = findGroup(groupKey);
|
|
13835
|
+
if (!group) {
|
|
13836
|
+
group = {
|
|
13837
|
+
id: `group_${normalizeKey(groupKey)}`,
|
|
13838
|
+
name: groupKey,
|
|
13839
|
+
slug: normalizeKey(groupKey),
|
|
13840
|
+
icon: "Folder",
|
|
13841
|
+
defaultExpanded: true,
|
|
13842
|
+
order: 99,
|
|
13843
|
+
items: []
|
|
13844
|
+
};
|
|
13845
|
+
groupsList.push(group);
|
|
13846
|
+
}
|
|
13847
|
+
const itemId = itemOpt.slug ? `workspace_${itemOpt.slug}` : itemOpt.collection ? `collection_${itemOpt.collection}` : `custom_${Math.random()}`;
|
|
13848
|
+
for (const otherGroup of groupsList) {
|
|
13849
|
+
if (otherGroup !== group) {
|
|
13850
|
+
otherGroup.items = otherGroup.items.filter((i) => i.slug !== itemOpt.slug && i.id !== itemId);
|
|
13851
|
+
}
|
|
13852
|
+
}
|
|
13853
|
+
const existingIndex = group.items.findIndex((i) => i.id === itemId || i.slug === itemOpt.slug);
|
|
13854
|
+
const existingItem = existingIndex >= 0 ? group.items[existingIndex] : void 0;
|
|
13855
|
+
const col = schemas?.collections?.find((c) => c.slug === itemOpt.collection);
|
|
13856
|
+
const colViews = col?.views || [];
|
|
13857
|
+
const baseViews = existingItem?.views || [];
|
|
13858
|
+
const userViews = itemOpt.views || [];
|
|
13859
|
+
const userViewSlugs = new Set(userViews.map((v) => v.slug));
|
|
13860
|
+
let mergedViews = [
|
|
13861
|
+
...baseViews.filter((v) => !userViewSlugs.has(v.slug)),
|
|
13862
|
+
...userViews
|
|
13863
|
+
];
|
|
13864
|
+
if (mergedViews.length === 0 && itemOpt.collection) {
|
|
13865
|
+
if (colViews.length > 0) {
|
|
13866
|
+
mergedViews = [...colViews];
|
|
13867
|
+
} else {
|
|
13868
|
+
mergedViews = [
|
|
13869
|
+
{
|
|
13870
|
+
slug: "default",
|
|
13871
|
+
label: col?.label || col?.labels?.plural || itemOpt.label || "All Records",
|
|
13872
|
+
layout: "table",
|
|
13873
|
+
collection: itemOpt.collection
|
|
13874
|
+
}
|
|
13875
|
+
];
|
|
13876
|
+
}
|
|
13877
|
+
}
|
|
13878
|
+
mergedViews = mergedViews.map((v) => ({
|
|
13879
|
+
...v,
|
|
13880
|
+
collection: v.collection || itemOpt.collection || existingItem?.collection
|
|
13881
|
+
}));
|
|
13882
|
+
const compiledItem = {
|
|
13883
|
+
id: itemId,
|
|
13884
|
+
type: itemOpt.slug ? "workspace" : itemOpt.collection ? "collection" : itemOpt.global ? "global" : "workspace",
|
|
13885
|
+
slug: itemOpt.slug || itemOpt.collection || itemOpt.global || "item",
|
|
13886
|
+
label: itemOpt.label || itemOpt.slug || "Item",
|
|
13887
|
+
icon: itemOpt.icon || existingItem?.icon || col?.admin?.icon || "Briefcase",
|
|
13888
|
+
group: group.name,
|
|
13889
|
+
order: itemOpt.order ?? existingItem?.order ?? 100,
|
|
13890
|
+
views: mergedViews,
|
|
13891
|
+
collection: itemOpt.collection || existingItem?.collection,
|
|
13892
|
+
global: itemOpt.global || existingItem?.global,
|
|
13893
|
+
badge: itemOpt.badge || existingItem?.badge,
|
|
13894
|
+
access: itemOpt.access || existingItem?.access
|
|
13895
|
+
};
|
|
13896
|
+
if (existingIndex >= 0) {
|
|
13897
|
+
group.items[existingIndex] = compiledItem;
|
|
13898
|
+
} else {
|
|
13899
|
+
group.items.push(compiledItem);
|
|
13900
|
+
}
|
|
13901
|
+
}
|
|
13902
|
+
const allAvailableItems = [];
|
|
13903
|
+
const reconciledGroups = [];
|
|
13904
|
+
for (const group of groupsList) {
|
|
13905
|
+
const isGroupHidden = hiddenSet.has(group.id) || !!group.slug && hiddenSet.has(group.slug) || hiddenSet.has(group.name);
|
|
13906
|
+
if (!includeHidden && isGroupHidden) {
|
|
13907
|
+
continue;
|
|
13908
|
+
}
|
|
13909
|
+
const visibleItems = [];
|
|
13910
|
+
for (const item of group.items) {
|
|
13911
|
+
const isItemHidden = hiddenSet.has(item.id) || !!item.slug && hiddenSet.has(item.slug);
|
|
13912
|
+
if (!includeHidden && isItemHidden) {
|
|
13913
|
+
continue;
|
|
13914
|
+
}
|
|
13915
|
+
const allViews = item.views || [];
|
|
13916
|
+
const visibleViews = includeHidden ? allViews : allViews.filter((v) => {
|
|
13917
|
+
const compoundKey = `${item.slug}_${v.slug}`;
|
|
13918
|
+
return !hiddenSet.has(compoundKey) && !hiddenSet.has(v.slug);
|
|
13919
|
+
});
|
|
13920
|
+
let isTombstone = false;
|
|
13921
|
+
if (schemas && schemas.collections && schemas.collections.length > 0) {
|
|
13922
|
+
if (item.collection && !knownCollectionSlugs.has(item.collection)) {
|
|
13923
|
+
isTombstone = true;
|
|
13924
|
+
}
|
|
13925
|
+
}
|
|
13926
|
+
if (schemas && schemas.globals && schemas.globals.length > 0) {
|
|
13927
|
+
if (item.global && !knownGlobalSlugs.has(item.global)) {
|
|
13928
|
+
isTombstone = true;
|
|
13929
|
+
}
|
|
13930
|
+
}
|
|
13931
|
+
const processedItem = {
|
|
13932
|
+
...item,
|
|
13933
|
+
views: visibleViews,
|
|
13934
|
+
...isTombstone ? { isTombstone: true } : {}
|
|
13935
|
+
};
|
|
13936
|
+
visibleItems.push(processedItem);
|
|
13937
|
+
allAvailableItems.push(processedItem);
|
|
13938
|
+
}
|
|
13939
|
+
const groupKey = group.slug || group.name;
|
|
13940
|
+
const itemOrderList = effectivePrefs.itemOrder?.[group.id] || effectivePrefs.itemOrder?.[groupKey];
|
|
13941
|
+
if (itemOrderList && itemOrderList.length > 0) {
|
|
13942
|
+
visibleItems.sort((a, b) => {
|
|
13943
|
+
const aIdx = itemOrderList.indexOf(a.id) >= 0 ? itemOrderList.indexOf(a.id) : a.slug ? itemOrderList.indexOf(a.slug) : -1;
|
|
13944
|
+
const bIdx = itemOrderList.indexOf(b.id) >= 0 ? itemOrderList.indexOf(b.id) : b.slug ? itemOrderList.indexOf(b.slug) : -1;
|
|
13945
|
+
if (aIdx >= 0 && bIdx >= 0) return aIdx - bIdx;
|
|
13946
|
+
if (aIdx >= 0) return -1;
|
|
13947
|
+
if (bIdx >= 0) return 1;
|
|
13948
|
+
return (a.order ?? 100) - (b.order ?? 100);
|
|
13949
|
+
});
|
|
13950
|
+
} else {
|
|
13951
|
+
visibleItems.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
13952
|
+
}
|
|
13953
|
+
reconciledGroups.push({
|
|
13954
|
+
...group,
|
|
13955
|
+
items: visibleItems
|
|
13956
|
+
});
|
|
13957
|
+
}
|
|
13958
|
+
if (effectivePrefs.groupOrder && effectivePrefs.groupOrder.length > 0) {
|
|
13959
|
+
reconciledGroups.sort((a, b) => {
|
|
13960
|
+
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);
|
|
13961
|
+
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);
|
|
13962
|
+
if (aIdx >= 0 && bIdx >= 0) return aIdx - bIdx;
|
|
13963
|
+
if (aIdx >= 0) return -1;
|
|
13964
|
+
if (bIdx >= 0) return 1;
|
|
13965
|
+
return (a.order ?? 100) - (b.order ?? 100);
|
|
13966
|
+
});
|
|
13967
|
+
} else {
|
|
13968
|
+
reconciledGroups.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
13969
|
+
}
|
|
13970
|
+
const reconciledUngrouped = (baseTree.ungrouped || []).filter((item) => {
|
|
13971
|
+
const isItemHidden = hiddenSet.has(item.id) || !!item.slug && hiddenSet.has(item.slug);
|
|
13972
|
+
return includeHidden || !isItemHidden;
|
|
13973
|
+
}).map((item) => {
|
|
13974
|
+
const allViews = item.views || [];
|
|
13975
|
+
const visibleViews = includeHidden ? allViews : allViews.filter((v) => {
|
|
13976
|
+
const compoundKey = `${item.slug}_${v.slug}`;
|
|
13977
|
+
return !hiddenSet.has(compoundKey) && !hiddenSet.has(v.slug);
|
|
13978
|
+
});
|
|
13979
|
+
const processed = { ...item, views: visibleViews };
|
|
13980
|
+
allAvailableItems.push(processed);
|
|
13981
|
+
return processed;
|
|
13982
|
+
});
|
|
13983
|
+
const pinnedItems = [];
|
|
13984
|
+
for (const pin of effectivePrefs.pinned || []) {
|
|
13985
|
+
const match = allAvailableItems.find((i) => i.slug === pin.slug || i.id === pin.slug);
|
|
13986
|
+
if (match) {
|
|
13987
|
+
pinnedItems.push({
|
|
13988
|
+
...match,
|
|
13989
|
+
id: `pin_${match.id}`,
|
|
13990
|
+
label: pin.label || match.label,
|
|
13991
|
+
icon: pin.icon || match.icon
|
|
13992
|
+
});
|
|
13993
|
+
} else {
|
|
13994
|
+
pinnedItems.push({
|
|
13995
|
+
id: `pin_${pin.slug}`,
|
|
13996
|
+
type: pin.type,
|
|
13997
|
+
slug: pin.slug,
|
|
13998
|
+
label: pin.label || pin.slug,
|
|
13999
|
+
icon: pin.icon || "Star",
|
|
14000
|
+
order: 0,
|
|
14001
|
+
views: []
|
|
14002
|
+
});
|
|
14003
|
+
}
|
|
14004
|
+
}
|
|
14005
|
+
return {
|
|
14006
|
+
groups: reconciledGroups,
|
|
14007
|
+
ungrouped: reconciledUngrouped,
|
|
14008
|
+
pinnedItems
|
|
14009
|
+
};
|
|
14010
|
+
}
|
|
14011
|
+
|
|
14012
|
+
// src/utils/navigation.ts
|
|
14013
|
+
var RESERVED_NAVIGATION_SLUGS = /* @__PURE__ */ new Set([
|
|
14014
|
+
"collections",
|
|
14015
|
+
"globals",
|
|
14016
|
+
"setup",
|
|
14017
|
+
"api",
|
|
14018
|
+
"login",
|
|
14019
|
+
"logout",
|
|
14020
|
+
"settings",
|
|
14021
|
+
"admin",
|
|
14022
|
+
"preferences",
|
|
14023
|
+
"auth"
|
|
14024
|
+
]);
|
|
14025
|
+
function assertValidNavigationSlugs(navigation) {
|
|
14026
|
+
for (const item of navigation) {
|
|
14027
|
+
if (item.slug) {
|
|
14028
|
+
const normalizedSlug = item.slug.toLowerCase().trim().replace(/^\//, "");
|
|
14029
|
+
if (RESERVED_NAVIGATION_SLUGS.has(normalizedSlug)) {
|
|
14030
|
+
throw new Error(
|
|
14031
|
+
`[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.`
|
|
14032
|
+
);
|
|
14033
|
+
}
|
|
14034
|
+
}
|
|
14035
|
+
}
|
|
14036
|
+
}
|
|
14037
|
+
function resolveGroupMeta(groupProp, defaultGroupName = "Collections") {
|
|
14038
|
+
if (!groupProp) {
|
|
14039
|
+
return { name: defaultGroupName };
|
|
14040
|
+
}
|
|
14041
|
+
if (typeof groupProp === "string") {
|
|
14042
|
+
return { name: groupProp };
|
|
14043
|
+
}
|
|
14044
|
+
return { name: groupProp.name, meta: groupProp };
|
|
14045
|
+
}
|
|
14046
|
+
function compileNavigation(config) {
|
|
14047
|
+
const collections = config.collections || [];
|
|
14048
|
+
const globals = config.globals || [];
|
|
14049
|
+
const explicitNav = config.admin?.navigation || [];
|
|
14050
|
+
assertValidNavigationSlugs(explicitNav);
|
|
14051
|
+
const collectionsBySlug = /* @__PURE__ */ new Map();
|
|
14052
|
+
for (const col of collections) {
|
|
14053
|
+
collectionsBySlug.set(col.slug, col);
|
|
14054
|
+
}
|
|
14055
|
+
const globalsBySlug = /* @__PURE__ */ new Map();
|
|
14056
|
+
for (const gl of globals) {
|
|
14057
|
+
globalsBySlug.set(gl.slug, gl);
|
|
14058
|
+
}
|
|
14059
|
+
const mentionedCollections = /* @__PURE__ */ new Set();
|
|
14060
|
+
const mentionedGlobals = /* @__PURE__ */ new Set();
|
|
14061
|
+
const injectedViewsByCollection = /* @__PURE__ */ new Map();
|
|
14062
|
+
for (const item of explicitNav) {
|
|
14063
|
+
if (item.addToCollection) {
|
|
14064
|
+
const existing = injectedViewsByCollection.get(item.addToCollection) || [];
|
|
14065
|
+
if (item.views && item.views.length > 0) {
|
|
14066
|
+
injectedViewsByCollection.set(item.addToCollection, [...existing, ...item.views]);
|
|
14067
|
+
}
|
|
14068
|
+
}
|
|
14069
|
+
if (item.collection) mentionedCollections.add(item.collection);
|
|
14070
|
+
if (item.global) mentionedGlobals.add(item.global);
|
|
14071
|
+
}
|
|
14072
|
+
const groupMetadataMap = /* @__PURE__ */ new Map();
|
|
14073
|
+
const itemsByGroup = /* @__PURE__ */ new Map();
|
|
14074
|
+
const ungroupedItems = [];
|
|
14075
|
+
const registerItem = (groupName, item) => {
|
|
14076
|
+
if (!groupName) {
|
|
14077
|
+
ungroupedItems.push(item);
|
|
14078
|
+
} else {
|
|
14079
|
+
const list = itemsByGroup.get(groupName) || [];
|
|
14080
|
+
list.push(item);
|
|
14081
|
+
itemsByGroup.set(groupName, list);
|
|
14082
|
+
}
|
|
14083
|
+
};
|
|
14084
|
+
for (const item of explicitNav) {
|
|
14085
|
+
if (item.addToCollection && !item.slug && !item.collection && !item.global && !item.dashboard && !item.href) {
|
|
14086
|
+
continue;
|
|
14087
|
+
}
|
|
14088
|
+
const { name: groupName, meta: groupMeta } = resolveGroupMeta(item.group, void 0);
|
|
14089
|
+
if (groupName && groupMeta) {
|
|
14090
|
+
groupMetadataMap.set(groupName, { ...groupMetadataMap.get(groupName) || {}, ...groupMeta });
|
|
14091
|
+
}
|
|
14092
|
+
if (item.dashboard) {
|
|
14093
|
+
registerItem(groupName, {
|
|
14094
|
+
id: "dashboard",
|
|
14095
|
+
type: "dashboard",
|
|
14096
|
+
slug: "dashboard",
|
|
14097
|
+
label: item.label || "Dashboard",
|
|
14098
|
+
icon: item.icon || "LayoutDashboard",
|
|
14099
|
+
group: groupName,
|
|
14100
|
+
order: item.order ?? 0,
|
|
14101
|
+
views: [],
|
|
14102
|
+
access: item.access
|
|
14103
|
+
});
|
|
14104
|
+
continue;
|
|
14105
|
+
}
|
|
14106
|
+
if (item.collection) {
|
|
14107
|
+
const col = collectionsBySlug.get(item.collection);
|
|
14108
|
+
const combinedViews = [
|
|
14109
|
+
...col?.views || [],
|
|
14110
|
+
...injectedViewsByCollection.get(item.collection) || [],
|
|
14111
|
+
...item.views || []
|
|
14112
|
+
];
|
|
14113
|
+
registerItem(groupName, {
|
|
14114
|
+
id: item.slug || `collection_${item.collection}`,
|
|
14115
|
+
type: item.slug ? "workspace" : "collection",
|
|
14116
|
+
slug: item.slug || item.collection,
|
|
14117
|
+
collection: item.collection,
|
|
14118
|
+
label: item.label || col?.labels?.plural || item.collection,
|
|
14119
|
+
icon: item.icon || col?.admin?.icon || "Folder",
|
|
14120
|
+
group: groupName,
|
|
14121
|
+
order: item.order ?? col?.admin?.order ?? 100,
|
|
14122
|
+
views: combinedViews,
|
|
14123
|
+
badge: item.badge,
|
|
14124
|
+
access: item.access || col?.access?.read
|
|
14125
|
+
});
|
|
14126
|
+
continue;
|
|
14127
|
+
}
|
|
14128
|
+
if (item.global) {
|
|
14129
|
+
const gl = globalsBySlug.get(item.global);
|
|
14130
|
+
registerItem(groupName, {
|
|
14131
|
+
id: item.slug || `global_${item.global}`,
|
|
14132
|
+
type: "global",
|
|
14133
|
+
slug: item.slug || item.global,
|
|
14134
|
+
global: item.global,
|
|
14135
|
+
label: item.label || gl?.label || item.global,
|
|
14136
|
+
icon: item.icon || "Settings",
|
|
14137
|
+
group: groupName,
|
|
14138
|
+
order: item.order ?? 100,
|
|
14139
|
+
views: [],
|
|
14140
|
+
badge: item.badge,
|
|
14141
|
+
access: item.access
|
|
14142
|
+
});
|
|
14143
|
+
continue;
|
|
14144
|
+
}
|
|
14145
|
+
if (item.href) {
|
|
14146
|
+
registerItem(groupName, {
|
|
14147
|
+
id: `link_${item.slug || item.href}`,
|
|
14148
|
+
type: "link",
|
|
14149
|
+
slug: item.slug || "link",
|
|
14150
|
+
label: item.label || "Link",
|
|
14151
|
+
href: item.href,
|
|
14152
|
+
icon: item.icon || "ExternalLink",
|
|
14153
|
+
group: groupName,
|
|
14154
|
+
order: item.order ?? 100,
|
|
14155
|
+
views: [],
|
|
14156
|
+
badge: item.badge,
|
|
14157
|
+
access: item.access
|
|
14158
|
+
});
|
|
14159
|
+
continue;
|
|
14160
|
+
}
|
|
14161
|
+
const workspaceSlug = item.slug || "workspace";
|
|
14162
|
+
registerItem(groupName, {
|
|
14163
|
+
id: `workspace_${workspaceSlug}`,
|
|
14164
|
+
type: "workspace",
|
|
14165
|
+
slug: workspaceSlug,
|
|
14166
|
+
label: item.label || workspaceSlug,
|
|
14167
|
+
icon: item.icon || "Briefcase",
|
|
14168
|
+
group: groupName,
|
|
14169
|
+
order: item.order ?? 100,
|
|
14170
|
+
views: item.views || [],
|
|
14171
|
+
badge: item.badge,
|
|
14172
|
+
access: item.access
|
|
14173
|
+
});
|
|
14174
|
+
}
|
|
14175
|
+
for (const col of collections) {
|
|
14176
|
+
if (mentionedCollections.has(col.slug)) continue;
|
|
14177
|
+
if (col.admin?.hidden) continue;
|
|
14178
|
+
const groupProp = col.admin?.group;
|
|
14179
|
+
const { name: groupName, meta: groupMeta } = resolveGroupMeta(groupProp, "Collections");
|
|
14180
|
+
if (groupMeta) {
|
|
14181
|
+
groupMetadataMap.set(groupName, { ...groupMetadataMap.get(groupName) || {}, ...groupMeta });
|
|
14182
|
+
}
|
|
14183
|
+
const combinedViews = [
|
|
14184
|
+
...col.views || [],
|
|
14185
|
+
...injectedViewsByCollection.get(col.slug) || []
|
|
14186
|
+
];
|
|
14187
|
+
registerItem(groupName, {
|
|
14188
|
+
id: `collection_${col.slug}`,
|
|
14189
|
+
type: "collection",
|
|
14190
|
+
slug: col.slug,
|
|
14191
|
+
collection: col.slug,
|
|
14192
|
+
label: col.labels?.plural || col.slug,
|
|
14193
|
+
icon: col.admin?.icon || "Folder",
|
|
14194
|
+
group: groupName,
|
|
14195
|
+
order: col?.admin?.order ?? 100,
|
|
14196
|
+
views: combinedViews,
|
|
14197
|
+
access: col.access?.read
|
|
14198
|
+
});
|
|
14199
|
+
}
|
|
14200
|
+
for (const gl of globals) {
|
|
14201
|
+
if (mentionedGlobals.has(gl.slug)) continue;
|
|
14202
|
+
const groupName = "Configuration";
|
|
14203
|
+
registerItem(groupName, {
|
|
14204
|
+
id: `global_${gl.slug}`,
|
|
14205
|
+
type: "global",
|
|
14206
|
+
slug: gl.slug,
|
|
14207
|
+
global: gl.slug,
|
|
14208
|
+
label: gl.label || gl.slug,
|
|
14209
|
+
icon: "Settings",
|
|
14210
|
+
group: groupName,
|
|
14211
|
+
order: 100,
|
|
14212
|
+
views: []
|
|
14213
|
+
});
|
|
14214
|
+
}
|
|
14215
|
+
const sortAndSpliceItems = (items) => {
|
|
14216
|
+
const sorted = [...items].sort((a, b) => a.order - b.order);
|
|
14217
|
+
const explicitMap = /* @__PURE__ */ new Map();
|
|
14218
|
+
for (const opt of explicitNav) {
|
|
14219
|
+
if (opt.slug) explicitMap.set(opt.slug, opt);
|
|
14220
|
+
if (opt.collection) explicitMap.set(opt.collection, opt);
|
|
14221
|
+
if (opt.global) explicitMap.set(opt.global, opt);
|
|
14222
|
+
}
|
|
14223
|
+
for (const opt of explicitNav) {
|
|
14224
|
+
const itemKey = opt.slug || opt.collection || opt.global;
|
|
14225
|
+
if (!itemKey) continue;
|
|
14226
|
+
if (opt.after) {
|
|
14227
|
+
const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
|
|
14228
|
+
const targetIdx = sorted.findIndex((i) => i.slug === opt.after);
|
|
14229
|
+
if (itemIdx !== -1 && targetIdx !== -1 && itemIdx !== targetIdx) {
|
|
14230
|
+
const [moved] = sorted.splice(itemIdx, 1);
|
|
14231
|
+
const newTargetIdx = sorted.findIndex((i) => i.slug === opt.after);
|
|
14232
|
+
sorted.splice(newTargetIdx + 1, 0, moved);
|
|
14233
|
+
}
|
|
14234
|
+
} else if (opt.before) {
|
|
14235
|
+
const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
|
|
14236
|
+
const targetIdx = sorted.findIndex((i) => i.slug === opt.before);
|
|
14237
|
+
if (itemIdx !== -1 && targetIdx !== -1 && itemIdx !== targetIdx) {
|
|
14238
|
+
const [moved] = sorted.splice(itemIdx, 1);
|
|
14239
|
+
const newTargetIdx = sorted.findIndex((i) => i.slug === opt.before);
|
|
14240
|
+
sorted.splice(newTargetIdx, 0, moved);
|
|
14241
|
+
}
|
|
14242
|
+
} else if (opt.position === "first") {
|
|
14243
|
+
const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
|
|
14244
|
+
if (itemIdx > 0) {
|
|
14245
|
+
const [moved] = sorted.splice(itemIdx, 1);
|
|
14246
|
+
sorted.unshift(moved);
|
|
14247
|
+
}
|
|
14248
|
+
} else if (opt.position === "last") {
|
|
14249
|
+
const itemIdx = sorted.findIndex((i) => i.slug === itemKey);
|
|
14250
|
+
if (itemIdx !== -1 && itemIdx < sorted.length - 1) {
|
|
14251
|
+
const [moved] = sorted.splice(itemIdx, 1);
|
|
14252
|
+
sorted.push(moved);
|
|
14253
|
+
}
|
|
14254
|
+
}
|
|
14255
|
+
}
|
|
14256
|
+
return sorted;
|
|
14257
|
+
};
|
|
14258
|
+
const compiledGroups = [];
|
|
14259
|
+
for (const [groupName, rawItems] of itemsByGroup.entries()) {
|
|
14260
|
+
const meta = groupMetadataMap.get(groupName);
|
|
14261
|
+
const sortedItems = sortAndSpliceItems(rawItems);
|
|
14262
|
+
compiledGroups.push({
|
|
14263
|
+
id: `group_${groupName.toLowerCase().replace(/\s+/g, "_")}`,
|
|
14264
|
+
name: groupName,
|
|
14265
|
+
slug: meta?.slug || groupName.toLowerCase().replace(/\s+/g, "-"),
|
|
14266
|
+
icon: meta?.icon,
|
|
14267
|
+
defaultExpanded: meta?.defaultExpanded ?? true,
|
|
14268
|
+
order: meta?.order ?? 100,
|
|
14269
|
+
items: sortedItems
|
|
14270
|
+
});
|
|
14271
|
+
}
|
|
14272
|
+
compiledGroups.sort((a, b) => a.order - b.order);
|
|
14273
|
+
return {
|
|
14274
|
+
groups: compiledGroups,
|
|
14275
|
+
ungrouped: sortAndSpliceItems(ungroupedItems)
|
|
14276
|
+
};
|
|
14277
|
+
}
|
|
14278
|
+
function pruneNavigationForUser(tree, user) {
|
|
14279
|
+
const userRoles = /* @__PURE__ */ new Set();
|
|
14280
|
+
if (user?.role) userRoles.add(user.role);
|
|
14281
|
+
if (Array.isArray(user?.roles)) {
|
|
14282
|
+
for (const r of user.roles) userRoles.add(r);
|
|
14283
|
+
}
|
|
14284
|
+
const isAllowed = (item) => {
|
|
14285
|
+
if (!item.access) return true;
|
|
14286
|
+
if (Array.isArray(item.access)) {
|
|
14287
|
+
if (item.access.length === 0) return true;
|
|
14288
|
+
return item.access.some((role) => userRoles.has(role));
|
|
14289
|
+
}
|
|
14290
|
+
return true;
|
|
14291
|
+
};
|
|
14292
|
+
const prunedGroups = [];
|
|
14293
|
+
for (const group of tree.groups) {
|
|
14294
|
+
const visibleItems = group.items.filter(isAllowed);
|
|
14295
|
+
if (visibleItems.length > 0) {
|
|
14296
|
+
prunedGroups.push({
|
|
14297
|
+
...group,
|
|
14298
|
+
items: visibleItems
|
|
14299
|
+
});
|
|
14300
|
+
}
|
|
14301
|
+
}
|
|
14302
|
+
return {
|
|
14303
|
+
groups: prunedGroups,
|
|
14304
|
+
ungrouped: tree.ungrouped.filter(isAllowed)
|
|
14305
|
+
};
|
|
14306
|
+
}
|
|
14307
|
+
|
|
13618
14308
|
// src/router.ts
|
|
13619
14309
|
init_access_control();
|
|
13620
14310
|
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
@@ -14114,6 +14804,7 @@ function registerRoutes(app, config) {
|
|
|
14114
14804
|
const user = c.get("user");
|
|
14115
14805
|
const key = c.req.param("key");
|
|
14116
14806
|
const scope = c.req.query("scope");
|
|
14807
|
+
const roleParam = c.req.query("role");
|
|
14117
14808
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
14118
14809
|
if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
14119
14810
|
const getGlobalPreference = async () => {
|
|
@@ -14123,27 +14814,61 @@ function registerRoutes(app, config) {
|
|
|
14123
14814
|
});
|
|
14124
14815
|
return globalDoc ? globalDoc.value : null;
|
|
14125
14816
|
};
|
|
14126
|
-
|
|
14817
|
+
const getRolePreference = async (r) => {
|
|
14818
|
+
const roleDoc = await db.findOne({
|
|
14819
|
+
collection: "__role_preferences",
|
|
14820
|
+
id: `${r}_${key}`
|
|
14821
|
+
});
|
|
14822
|
+
return roleDoc ? roleDoc.value : null;
|
|
14823
|
+
};
|
|
14824
|
+
const getUserPreferences = async () => {
|
|
14825
|
+
if (!user?.collection || !user.sub) return null;
|
|
14826
|
+
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
14827
|
+
if (!doc) return null;
|
|
14828
|
+
return typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
|
|
14829
|
+
};
|
|
14830
|
+
if (scope === "global") {
|
|
14127
14831
|
const globalValue2 = await getGlobalPreference();
|
|
14128
14832
|
return c.json({ key, value: globalValue2 });
|
|
14129
14833
|
}
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14834
|
+
if (scope === "role") {
|
|
14835
|
+
if (roleParam) {
|
|
14836
|
+
const val = await getRolePreference(roleParam);
|
|
14837
|
+
return c.json({ key, value: val, role: roleParam });
|
|
14838
|
+
}
|
|
14839
|
+
const userRoles2 = Array.isArray(user?.roles) ? user.roles : typeof user?.role === "string" ? [user.role] : [];
|
|
14840
|
+
for (const r of userRoles2) {
|
|
14841
|
+
const val = await getRolePreference(r);
|
|
14842
|
+
if (val !== null && val !== void 0) {
|
|
14843
|
+
return c.json({ key, value: val, role: r });
|
|
14844
|
+
}
|
|
14845
|
+
}
|
|
14846
|
+
return c.json({ key, value: null });
|
|
14134
14847
|
}
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
return c.json({ key, value:
|
|
14848
|
+
if (scope === "personal") {
|
|
14849
|
+
const prefs2 = await getUserPreferences();
|
|
14850
|
+
return c.json({ key, value: prefs2 && key in prefs2 ? prefs2[key] ?? null : null });
|
|
14851
|
+
}
|
|
14852
|
+
const prefs = await getUserPreferences();
|
|
14853
|
+
if (prefs && key in prefs && prefs[key] !== null && prefs[key] !== void 0) {
|
|
14854
|
+
return c.json({ key, value: prefs[key] });
|
|
14855
|
+
}
|
|
14856
|
+
const userRoles = Array.isArray(user?.roles) ? user.roles : typeof user?.role === "string" ? [user.role] : [];
|
|
14857
|
+
for (const r of userRoles) {
|
|
14858
|
+
const val = await getRolePreference(r);
|
|
14859
|
+
if (val !== null && val !== void 0) {
|
|
14860
|
+
return c.json({ key, value: val });
|
|
14861
|
+
}
|
|
14138
14862
|
}
|
|
14139
14863
|
const globalValue = await getGlobalPreference();
|
|
14140
|
-
return c.json({ key, value: globalValue });
|
|
14864
|
+
return c.json({ key, value: globalValue ?? null });
|
|
14141
14865
|
});
|
|
14142
14866
|
app.put("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
14143
14867
|
const db = config.db;
|
|
14144
14868
|
const user = c.get("user");
|
|
14145
14869
|
const key = c.req.param("key");
|
|
14146
|
-
const scope = c.req.query("scope");
|
|
14870
|
+
const scope = c.req.query("scope") || "personal";
|
|
14871
|
+
const roleParam = c.req.query("role");
|
|
14147
14872
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
14148
14873
|
if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
|
|
14149
14874
|
if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
@@ -14176,7 +14901,47 @@ function registerRoutes(app, config) {
|
|
|
14176
14901
|
data: { id: key, value: body.value }
|
|
14177
14902
|
});
|
|
14178
14903
|
}
|
|
14179
|
-
return c.json({ key, value: body.value });
|
|
14904
|
+
return c.json({ key, value: body.value, scope: "global" });
|
|
14905
|
+
}
|
|
14906
|
+
if (scope === "role") {
|
|
14907
|
+
const userCol = config.collections?.find((col) => col.slug === user?.collection);
|
|
14908
|
+
const isAdminUser = isUserAdmin(user, userCol);
|
|
14909
|
+
if (!isAdminUser) {
|
|
14910
|
+
return c.json(
|
|
14911
|
+
{
|
|
14912
|
+
error: true,
|
|
14913
|
+
message: "Only administrators can save role preferences."
|
|
14914
|
+
},
|
|
14915
|
+
403
|
|
14916
|
+
);
|
|
14917
|
+
}
|
|
14918
|
+
if (!roleParam) {
|
|
14919
|
+
return c.json(
|
|
14920
|
+
{
|
|
14921
|
+
error: true,
|
|
14922
|
+
message: "Role query parameter is required for role-scoped preferences."
|
|
14923
|
+
},
|
|
14924
|
+
400
|
|
14925
|
+
);
|
|
14926
|
+
}
|
|
14927
|
+
const roleDocId = `${roleParam}_${key}`;
|
|
14928
|
+
const existing = await db.findOne({
|
|
14929
|
+
collection: "__role_preferences",
|
|
14930
|
+
id: roleDocId
|
|
14931
|
+
});
|
|
14932
|
+
if (existing) {
|
|
14933
|
+
await db.update({
|
|
14934
|
+
collection: "__role_preferences",
|
|
14935
|
+
id: roleDocId,
|
|
14936
|
+
data: { value: body.value, role: roleParam, key }
|
|
14937
|
+
});
|
|
14938
|
+
} else {
|
|
14939
|
+
await db.create({
|
|
14940
|
+
collection: "__role_preferences",
|
|
14941
|
+
data: { id: roleDocId, role: roleParam, key, value: body.value }
|
|
14942
|
+
});
|
|
14943
|
+
}
|
|
14944
|
+
return c.json({ key, value: body.value, role: roleParam, scope: "role" });
|
|
14180
14945
|
}
|
|
14181
14946
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
14182
14947
|
if (!doc) return c.json({ error: true, message: "User not found." }, 404);
|
|
@@ -14187,13 +14952,14 @@ function registerRoutes(app, config) {
|
|
|
14187
14952
|
id: user.sub,
|
|
14188
14953
|
data: { __preferences: nextPreferences }
|
|
14189
14954
|
});
|
|
14190
|
-
return c.json({ key, value: body.value });
|
|
14955
|
+
return c.json({ key, value: body.value, scope: "personal" });
|
|
14191
14956
|
});
|
|
14192
14957
|
app.delete("/api/preferences/:key", requireAuth(config), async (c) => {
|
|
14193
14958
|
const db = config.db;
|
|
14194
14959
|
const user = c.get("user");
|
|
14195
14960
|
const key = c.req.param("key");
|
|
14196
|
-
const scope = c.req.query("scope");
|
|
14961
|
+
const scope = c.req.query("scope") || "personal";
|
|
14962
|
+
const roleParam = c.req.query("role");
|
|
14197
14963
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
14198
14964
|
if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
|
|
14199
14965
|
if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
|
|
@@ -14212,6 +14978,30 @@ function registerRoutes(app, config) {
|
|
|
14212
14978
|
await db.delete({ collection: "__global_preferences", id: key });
|
|
14213
14979
|
return c.json({ success: true });
|
|
14214
14980
|
}
|
|
14981
|
+
if (scope === "role") {
|
|
14982
|
+
const userCol = config.collections?.find((col) => col.slug === user?.collection);
|
|
14983
|
+
const isAdminUser = isUserAdmin(user, userCol);
|
|
14984
|
+
if (!isAdminUser) {
|
|
14985
|
+
return c.json(
|
|
14986
|
+
{
|
|
14987
|
+
error: true,
|
|
14988
|
+
message: "Only administrators can delete role preferences."
|
|
14989
|
+
},
|
|
14990
|
+
403
|
|
14991
|
+
);
|
|
14992
|
+
}
|
|
14993
|
+
if (!roleParam) {
|
|
14994
|
+
return c.json(
|
|
14995
|
+
{
|
|
14996
|
+
error: true,
|
|
14997
|
+
message: "Role query parameter is required for role-scoped preferences."
|
|
14998
|
+
},
|
|
14999
|
+
400
|
|
15000
|
+
);
|
|
15001
|
+
}
|
|
15002
|
+
await db.delete({ collection: "__role_preferences", id: `${roleParam}_${key}` });
|
|
15003
|
+
return c.json({ success: true });
|
|
15004
|
+
}
|
|
14215
15005
|
const doc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
14216
15006
|
if (!doc) return c.json({ error: true, message: "User not found." }, 404);
|
|
14217
15007
|
const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? { ...doc.__preferences } : {};
|
|
@@ -14223,6 +15013,198 @@ function registerRoutes(app, config) {
|
|
|
14223
15013
|
});
|
|
14224
15014
|
return c.json({ success: true });
|
|
14225
15015
|
});
|
|
15016
|
+
app.get("/api/admin/navigation", optionalAuth(config), async (c) => {
|
|
15017
|
+
const user = c.get("user");
|
|
15018
|
+
const compiled = compileNavigation(config);
|
|
15019
|
+
const pruned = pruneNavigationForUser(compiled, user);
|
|
15020
|
+
return c.json(pruned);
|
|
15021
|
+
});
|
|
15022
|
+
app.on(["GET", "POST"], "/api/admin/navigation/badges", optionalAuth(config), async (c) => {
|
|
15023
|
+
const db = config.db;
|
|
15024
|
+
if (!db) return c.json({ badges: {} });
|
|
15025
|
+
const user = c.get("user");
|
|
15026
|
+
const compiled = compileNavigation(config);
|
|
15027
|
+
let userNavPrefs = null;
|
|
15028
|
+
const prefsQuery = c.req.query("prefs");
|
|
15029
|
+
if (prefsQuery) {
|
|
15030
|
+
try {
|
|
15031
|
+
userNavPrefs = JSON.parse(decodeURIComponent(prefsQuery));
|
|
15032
|
+
} catch {
|
|
15033
|
+
}
|
|
15034
|
+
}
|
|
15035
|
+
if (!userNavPrefs && c.req.method === "POST") {
|
|
15036
|
+
try {
|
|
15037
|
+
const body = await c.req.json();
|
|
15038
|
+
if (body?.preferences) userNavPrefs = body.preferences;
|
|
15039
|
+
} catch {
|
|
15040
|
+
}
|
|
15041
|
+
}
|
|
15042
|
+
if (!userNavPrefs && user?.collection && user?.sub) {
|
|
15043
|
+
try {
|
|
15044
|
+
const userDoc = await db.findOne({ collection: user.collection, id: user.sub });
|
|
15045
|
+
if (userDoc?.__preferences && typeof userDoc.__preferences === "object") {
|
|
15046
|
+
userNavPrefs = userDoc.__preferences["admin:navigation"];
|
|
15047
|
+
}
|
|
15048
|
+
} catch {
|
|
15049
|
+
}
|
|
15050
|
+
}
|
|
15051
|
+
if (!userNavPrefs) {
|
|
15052
|
+
try {
|
|
15053
|
+
const globalDoc = await db.findOne({
|
|
15054
|
+
collection: "__global_preferences",
|
|
15055
|
+
id: "admin:navigation"
|
|
15056
|
+
});
|
|
15057
|
+
if (globalDoc?.value) {
|
|
15058
|
+
userNavPrefs = globalDoc.value;
|
|
15059
|
+
}
|
|
15060
|
+
} catch {
|
|
15061
|
+
}
|
|
15062
|
+
}
|
|
15063
|
+
let allItems;
|
|
15064
|
+
if (userNavPrefs) {
|
|
15065
|
+
const reconciled = reconcileNavigation(compiled, userNavPrefs, {
|
|
15066
|
+
collections: config.collections,
|
|
15067
|
+
globals: config.globals
|
|
15068
|
+
});
|
|
15069
|
+
allItems = [
|
|
15070
|
+
...reconciled.ungrouped || [],
|
|
15071
|
+
...reconciled.pinnedItems || [],
|
|
15072
|
+
...(reconciled.groups || []).flatMap((g) => g.items || [])
|
|
15073
|
+
];
|
|
15074
|
+
} else {
|
|
15075
|
+
const pruned = pruneNavigationForUser(compiled, user);
|
|
15076
|
+
allItems = [
|
|
15077
|
+
...pruned.ungrouped,
|
|
15078
|
+
...pruned.groups.flatMap((g) => g.items)
|
|
15079
|
+
];
|
|
15080
|
+
}
|
|
15081
|
+
const badges = {};
|
|
15082
|
+
const requestsByCollection = /* @__PURE__ */ new Map();
|
|
15083
|
+
let reqCounter = 0;
|
|
15084
|
+
for (const item of allItems) {
|
|
15085
|
+
if (item.badge) {
|
|
15086
|
+
const itemBadge = item.badge;
|
|
15087
|
+
const badgeKey = item.slug || item.collection || item.global || item.id;
|
|
15088
|
+
if (typeof itemBadge === "string") {
|
|
15089
|
+
badges[badgeKey] = { count: itemBadge, variant: "default" };
|
|
15090
|
+
} else {
|
|
15091
|
+
const badgeConfig = itemBadge;
|
|
15092
|
+
const variant = badgeConfig.variant ?? "default";
|
|
15093
|
+
if (badgeConfig.text) {
|
|
15094
|
+
badges[badgeKey] = { count: badgeConfig.text, variant };
|
|
15095
|
+
} else {
|
|
15096
|
+
let targetCol;
|
|
15097
|
+
let targetWhere;
|
|
15098
|
+
if (badgeConfig.aggregate) {
|
|
15099
|
+
targetCol = badgeConfig.aggregate.collection;
|
|
15100
|
+
targetWhere = badgeConfig.aggregate.where;
|
|
15101
|
+
} else if (badgeConfig.count) {
|
|
15102
|
+
targetCol = item.collection || item.views?.[0]?.collection;
|
|
15103
|
+
const primaryView = item.views?.[0];
|
|
15104
|
+
if (primaryView?.filter) {
|
|
15105
|
+
targetWhere = primaryView.filter;
|
|
15106
|
+
}
|
|
15107
|
+
}
|
|
15108
|
+
if (targetCol) {
|
|
15109
|
+
const reqId = `agg_${reqCounter++}`;
|
|
15110
|
+
const list = requestsByCollection.get(targetCol) || [];
|
|
15111
|
+
list.push({
|
|
15112
|
+
id: reqId,
|
|
15113
|
+
where: targetWhere,
|
|
15114
|
+
onResult: (count2) => {
|
|
15115
|
+
badges[badgeKey] = { count: count2, variant };
|
|
15116
|
+
}
|
|
15117
|
+
});
|
|
15118
|
+
requestsByCollection.set(targetCol, list);
|
|
15119
|
+
}
|
|
15120
|
+
}
|
|
15121
|
+
}
|
|
15122
|
+
}
|
|
15123
|
+
if (item.views && item.views.length > 0) {
|
|
15124
|
+
for (const view of item.views) {
|
|
15125
|
+
if (!view.badge) continue;
|
|
15126
|
+
const viewBadge = view.badge;
|
|
15127
|
+
const viewKey = `${item.slug}:${view.slug}`;
|
|
15128
|
+
const altKey = `${item.slug}_${view.slug}`;
|
|
15129
|
+
const plainKey = view.slug;
|
|
15130
|
+
const setBadgeResult = (res) => {
|
|
15131
|
+
badges[viewKey] = res;
|
|
15132
|
+
badges[altKey] = res;
|
|
15133
|
+
if (!badges[plainKey]) {
|
|
15134
|
+
badges[plainKey] = res;
|
|
15135
|
+
}
|
|
15136
|
+
if (view.collection) {
|
|
15137
|
+
badges[`${view.collection}:${view.slug}`] = res;
|
|
15138
|
+
badges[`${view.collection}_${view.slug}`] = res;
|
|
15139
|
+
}
|
|
15140
|
+
};
|
|
15141
|
+
if (typeof viewBadge === "string") {
|
|
15142
|
+
setBadgeResult({ count: viewBadge, variant: "default" });
|
|
15143
|
+
continue;
|
|
15144
|
+
}
|
|
15145
|
+
const badgeConfig = viewBadge;
|
|
15146
|
+
const variant = badgeConfig.variant ?? "default";
|
|
15147
|
+
if (badgeConfig.text) {
|
|
15148
|
+
setBadgeResult({ count: badgeConfig.text, variant });
|
|
15149
|
+
continue;
|
|
15150
|
+
}
|
|
15151
|
+
const targetCol = badgeConfig.aggregate?.collection || view.collection || item.collection || (item.type === "collection" ? item.slug : void 0);
|
|
15152
|
+
const targetWhere = badgeConfig.aggregate?.where || (typeof view.filter === "object" ? view.filter : void 0);
|
|
15153
|
+
if (!targetCol) continue;
|
|
15154
|
+
const reqId = `agg_${reqCounter++}`;
|
|
15155
|
+
const list = requestsByCollection.get(targetCol) || [];
|
|
15156
|
+
list.push({
|
|
15157
|
+
id: reqId,
|
|
15158
|
+
where: targetWhere,
|
|
15159
|
+
onResult: (count2) => {
|
|
15160
|
+
setBadgeResult({ count: count2, variant });
|
|
15161
|
+
}
|
|
15162
|
+
});
|
|
15163
|
+
requestsByCollection.set(targetCol, list);
|
|
15164
|
+
}
|
|
15165
|
+
}
|
|
15166
|
+
}
|
|
15167
|
+
const collectionTasks = Array.from(requestsByCollection.entries()).map(async ([col, reqs]) => {
|
|
15168
|
+
if (typeof db.aggregate === "function") {
|
|
15169
|
+
try {
|
|
15170
|
+
const aggregates = {};
|
|
15171
|
+
for (const req of reqs) {
|
|
15172
|
+
aggregates[req.id] = { count: "*", where: req.where };
|
|
15173
|
+
}
|
|
15174
|
+
const agg = await db.aggregate({
|
|
15175
|
+
collection: col,
|
|
15176
|
+
aggregates
|
|
15177
|
+
});
|
|
15178
|
+
for (const req of reqs) {
|
|
15179
|
+
const val = agg?.[req.id];
|
|
15180
|
+
req.onResult(typeof val === "number" ? val : 0);
|
|
15181
|
+
}
|
|
15182
|
+
} catch {
|
|
15183
|
+
for (const req of reqs) {
|
|
15184
|
+
req.onResult(0);
|
|
15185
|
+
}
|
|
15186
|
+
}
|
|
15187
|
+
} else {
|
|
15188
|
+
await Promise.allSettled(
|
|
15189
|
+
reqs.map(async (req) => {
|
|
15190
|
+
try {
|
|
15191
|
+
const res = await db.find({
|
|
15192
|
+
collection: col,
|
|
15193
|
+
where: req.where,
|
|
15194
|
+
limit: 1
|
|
15195
|
+
});
|
|
15196
|
+
const total = res?.totalDocs ?? res?.total ?? 0;
|
|
15197
|
+
req.onResult(total);
|
|
15198
|
+
} catch {
|
|
15199
|
+
req.onResult(0);
|
|
15200
|
+
}
|
|
15201
|
+
})
|
|
15202
|
+
);
|
|
15203
|
+
}
|
|
15204
|
+
});
|
|
15205
|
+
await Promise.allSettled(collectionTasks);
|
|
15206
|
+
return c.json({ badges });
|
|
15207
|
+
});
|
|
14226
15208
|
app.get("/api/media/folders", async (c) => {
|
|
14227
15209
|
const folderController = new MediaFolderController("media");
|
|
14228
15210
|
return folderController.list(c);
|
|
@@ -14702,7 +15684,33 @@ function normalizeConfig(config) {
|
|
|
14702
15684
|
}
|
|
14703
15685
|
];
|
|
14704
15686
|
}
|
|
15687
|
+
if (!existingFieldNames.has("__preferences")) {
|
|
15688
|
+
fields = [
|
|
15689
|
+
...fields,
|
|
15690
|
+
{
|
|
15691
|
+
name: "__preferences",
|
|
15692
|
+
type: "json",
|
|
15693
|
+
label: "Preferences",
|
|
15694
|
+
promoted: true,
|
|
15695
|
+
admin: {
|
|
15696
|
+
hidden: true,
|
|
15697
|
+
readOnly: true
|
|
15698
|
+
}
|
|
15699
|
+
}
|
|
15700
|
+
];
|
|
15701
|
+
}
|
|
14705
15702
|
fields = fields.map((field) => {
|
|
15703
|
+
if (field.name === "__preferences") {
|
|
15704
|
+
return {
|
|
15705
|
+
...field,
|
|
15706
|
+
promoted: true,
|
|
15707
|
+
admin: {
|
|
15708
|
+
...field.admin || {},
|
|
15709
|
+
hidden: true,
|
|
15710
|
+
readOnly: true
|
|
15711
|
+
}
|
|
15712
|
+
};
|
|
15713
|
+
}
|
|
14706
15714
|
if (field.name === "email") {
|
|
14707
15715
|
return {
|
|
14708
15716
|
...field,
|
|
@@ -14793,11 +15801,31 @@ function normalizeConfig(config) {
|
|
|
14793
15801
|
}
|
|
14794
15802
|
const updatedFieldNames = new Set(fields.map((f) => f.name));
|
|
14795
15803
|
const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
|
|
15804
|
+
const indexedFieldNames = /* @__PURE__ */ new Set();
|
|
15805
|
+
if (col.indexes) {
|
|
15806
|
+
for (const idx of col.indexes) {
|
|
15807
|
+
if (Array.isArray(idx.fields)) {
|
|
15808
|
+
for (const f of idx.fields) {
|
|
15809
|
+
indexedFieldNames.add(f);
|
|
15810
|
+
}
|
|
15811
|
+
}
|
|
15812
|
+
}
|
|
15813
|
+
}
|
|
15814
|
+
const allFields = [...fields, ...fieldsToInject].map((field) => {
|
|
15815
|
+
if (field.unique || field.name && indexedFieldNames.has(field.name)) {
|
|
15816
|
+
return { ...field, promoted: true };
|
|
15817
|
+
}
|
|
15818
|
+
return field;
|
|
15819
|
+
});
|
|
14796
15820
|
const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
|
|
15821
|
+
const idPrefix = col.idPrefix || resolvePrefix(col.slug);
|
|
15822
|
+
const idType = col.idType || "prefixed-nanoid";
|
|
14797
15823
|
return {
|
|
14798
15824
|
...col,
|
|
15825
|
+
idPrefix,
|
|
15826
|
+
idType,
|
|
14799
15827
|
workflow,
|
|
14800
|
-
fields:
|
|
15828
|
+
fields: allFields
|
|
14801
15829
|
};
|
|
14802
15830
|
});
|
|
14803
15831
|
const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
|