@dyrected/core 2.5.44 → 2.5.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.cjs CHANGED
@@ -30,7 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
- // src/utils/where-sanitizer.js
33
+ // src/utils/where-sanitizer.ts
34
34
  var where_sanitizer_exports = {};
35
35
  __export(where_sanitizer_exports, {
36
36
  sanitizeWhereClause: () => sanitizeWhereClause
@@ -67,12 +67,9 @@ function sanitizeWhereClause(where, fields) {
67
67
  continue;
68
68
  }
69
69
  const fieldDef = fieldMap.get(key);
70
- if (!fieldDef)
71
- continue;
72
- if (fieldDef.admin?.filterable === false)
73
- continue;
74
- if (NEVER_FILTERABLE_TYPES.includes(fieldDef.type))
75
- continue;
70
+ if (!fieldDef) continue;
71
+ if (fieldDef.admin?.filterable === false) continue;
72
+ if (NEVER_FILTERABLE_TYPES.includes(fieldDef.type)) continue;
76
73
  result[key] = value;
77
74
  }
78
75
  return result;
@@ -81,7 +78,7 @@ function sanitizeWhereClause(where, fields) {
81
78
  }
82
79
  var NEVER_FILTERABLE_TYPES;
83
80
  var init_where_sanitizer = __esm({
84
- "src/utils/where-sanitizer.js"() {
81
+ "src/utils/where-sanitizer.ts"() {
85
82
  "use strict";
86
83
  NEVER_FILTERABLE_TYPES = [
87
84
  "password",
@@ -108,12 +105,12 @@ __export(server_exports, {
108
105
  });
109
106
  module.exports = __toCommonJS(server_exports);
110
107
 
111
- // src/app.js
108
+ // src/app.ts
112
109
  var import_hono = require("hono");
113
110
  var import_cors = require("hono/cors");
114
111
  var import_request_id = require("hono/request-id");
115
112
 
116
- // src/services/defaults.service.js
113
+ // src/services/defaults.service.ts
117
114
  var DefaultsService = class {
118
115
  /**
119
116
  * Recursively apply default values to a data object based on field definitions.
@@ -121,14 +118,12 @@ var DefaultsService = class {
121
118
  static apply(fields, data = {}) {
122
119
  const result = { ...data || {} };
123
120
  fields.forEach((field) => {
124
- if (field.type === "join")
125
- return;
121
+ if (field.type === "join") return;
126
122
  if (field.type === "row" && field.fields) {
127
123
  Object.assign(result, this.apply(field.fields, data));
128
124
  return;
129
125
  }
130
- if (!field.name)
131
- return;
126
+ if (!field.name) return;
132
127
  let value = result[field.name];
133
128
  if ((value === void 0 || value === null) && field.renameTo) {
134
129
  const legacyValue = result[field.renameTo];
@@ -141,12 +136,9 @@ var DefaultsService = class {
141
136
  if (field.defaultValue !== void 0) {
142
137
  result[field.name] = field.defaultValue;
143
138
  } else {
144
- if (field.type === "boolean")
145
- result[field.name] = false;
146
- else if (field.type === "array")
147
- result[field.name] = [];
148
- else if (field.type === "multiSelect")
149
- result[field.name] = [];
139
+ if (field.type === "boolean") result[field.name] = false;
140
+ else if (field.type === "array") result[field.name] = [];
141
+ else if (field.type === "multiSelect") result[field.name] = [];
150
142
  else if (field.type === "object") {
151
143
  result[field.name] = this.apply(field.fields || [], {});
152
144
  }
@@ -161,7 +153,7 @@ var DefaultsService = class {
161
153
  }
162
154
  };
163
155
 
164
- // src/services/population.service.js
156
+ // src/services/population.service.ts
165
157
  var PopulationService = class {
166
158
  db;
167
159
  collections;
@@ -214,31 +206,29 @@ var PopulationService = class {
214
206
  Object.assign(populatedDoc, rowPopulated);
215
207
  continue;
216
208
  }
217
- if (!field.name)
218
- continue;
209
+ if (!field.name) continue;
219
210
  const value = populatedDoc[field.name];
220
211
  if (field.type === "relationship" && field.relationTo && value) {
221
212
  const relatedCollection = this.collections.find((c) => c.slug === field.relationTo);
222
- if (!relatedCollection)
223
- continue;
213
+ if (!relatedCollection) continue;
224
214
  if (Array.isArray(value)) {
225
- populatedDoc[field.name] = await Promise.all(value.map(async (id) => {
226
- if (!id)
227
- return id;
228
- let doc = id;
229
- if (typeof id === "string") {
230
- doc = await this.db.findOne({ collection: field.relationTo, id });
231
- }
232
- if (!doc || typeof doc !== "object")
233
- return id;
234
- const docWithDefaults = DefaultsService.apply(relatedCollection.fields, doc);
235
- return this.populate({
236
- data: docWithDefaults,
237
- fields: relatedCollection.fields,
238
- currentDepth: currentDepth + 1,
239
- maxDepth
240
- });
241
- }));
215
+ populatedDoc[field.name] = await Promise.all(
216
+ value.map(async (id) => {
217
+ if (!id) return id;
218
+ let doc = id;
219
+ if (typeof id === "string") {
220
+ doc = await this.db.findOne({ collection: field.relationTo, id });
221
+ }
222
+ if (!doc || typeof doc !== "object") return id;
223
+ const docWithDefaults = DefaultsService.apply(relatedCollection.fields, doc);
224
+ return this.populate({
225
+ data: docWithDefaults,
226
+ fields: relatedCollection.fields,
227
+ currentDepth: currentDepth + 1,
228
+ maxDepth
229
+ });
230
+ })
231
+ );
242
232
  } else if (value) {
243
233
  let doc = value;
244
234
  if (typeof value === "string") {
@@ -287,17 +277,18 @@ var PopulationService = class {
287
277
  });
288
278
  }
289
279
  if (field.type === "blocks" && field.blocks && Array.isArray(value)) {
290
- populatedDoc[field.name] = await Promise.all(value.map(async (blockData) => {
291
- const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
292
- if (!blockConfig)
293
- return blockData;
294
- return this.populate({
295
- data: blockData,
296
- fields: blockConfig.fields,
297
- currentDepth,
298
- maxDepth
299
- });
300
- }));
280
+ populatedDoc[field.name] = await Promise.all(
281
+ value.map(async (blockData) => {
282
+ const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
283
+ if (!blockConfig) return blockData;
284
+ return this.populate({
285
+ data: blockData,
286
+ fields: blockConfig.fields,
287
+ currentDepth,
288
+ maxDepth
289
+ });
290
+ })
291
+ );
301
292
  }
302
293
  }
303
294
  return populatedDoc;
@@ -306,8 +297,7 @@ var PopulationService = class {
306
297
  * Helper to populate a PaginatedResult
307
298
  */
308
299
  async populateResult(result, fields, maxDepth) {
309
- if (maxDepth <= 0)
310
- return result;
300
+ if (maxDepth <= 0) return result;
311
301
  const populatedDocs = await this.populate({
312
302
  data: result.docs,
313
303
  fields,
@@ -321,7 +311,7 @@ var PopulationService = class {
321
311
  }
322
312
  };
323
313
 
324
- // src/services/audit.service.js
314
+ // src/services/audit.service.ts
325
315
  var AuditService = class {
326
316
  /**
327
317
  * Writes a single entry to the __audit collection.
@@ -349,7 +339,7 @@ var AuditService = class {
349
339
  }
350
340
  };
351
341
 
352
- // src/auth/password.js
342
+ // src/auth/password.ts
353
343
  var import_node_util = require("util");
354
344
  var import_node_crypto = require("crypto");
355
345
  var scryptAsync = (0, import_node_util.promisify)(import_node_crypto.scrypt);
@@ -362,16 +352,14 @@ async function hashPassword(plain) {
362
352
  }
363
353
  async function verifyPassword(plain, stored) {
364
354
  const [salt, storedHash] = stored.split(":");
365
- if (!salt || !storedHash)
366
- return false;
355
+ if (!salt || !storedHash) return false;
367
356
  const derivedKey = await scryptAsync(plain, salt, KEY_LEN);
368
357
  const storedBuffer = Buffer.from(storedHash, "hex");
369
- if (derivedKey.length !== storedBuffer.length)
370
- return false;
358
+ if (derivedKey.length !== storedBuffer.length) return false;
371
359
  return (0, import_node_crypto.timingSafeEqual)(derivedKey, storedBuffer);
372
360
  }
373
361
 
374
- // src/utils/hooks.js
362
+ // src/utils/hooks.ts
375
363
  async function runCollectionHooks(hooks, args, options = {}) {
376
364
  if (!hooks || hooks.length === 0) {
377
365
  return args.data ?? args.doc ?? void 0;
@@ -398,12 +386,10 @@ async function runCollectionHooks(hooks, args, options = {}) {
398
386
  return currentPayload;
399
387
  }
400
388
  async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
401
- if (!data || typeof data !== "object")
402
- return data;
389
+ if (!data || typeof data !== "object") return data;
403
390
  const result = { ...data };
404
391
  for (const field of fields) {
405
- if (!field.name)
406
- continue;
392
+ if (!field.name) continue;
407
393
  const value = result[field.name];
408
394
  const origValue = originalDoc?.[field.name];
409
395
  let updatedValue = value;
@@ -423,13 +409,21 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
423
409
  }
424
410
  if (updatedValue !== void 0 && updatedValue !== null) {
425
411
  if (field.type === "object" && field.fields) {
426
- result[field.name] = await executeFieldBeforeChange(field.fields, updatedValue, origValue, user, db);
412
+ result[field.name] = await executeFieldBeforeChange(
413
+ field.fields,
414
+ updatedValue,
415
+ origValue,
416
+ user,
417
+ db
418
+ );
427
419
  } else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
428
420
  const arrayResult = [];
429
421
  for (let i = 0; i < updatedValue.length; i++) {
430
422
  const item = updatedValue[i];
431
423
  const origItem = Array.isArray(origValue) ? origValue[i] : null;
432
- arrayResult.push(await executeFieldBeforeChange(field.fields, item, origItem, user, db));
424
+ arrayResult.push(
425
+ await executeFieldBeforeChange(field.fields, item, origItem, user, db)
426
+ );
433
427
  }
434
428
  result[field.name] = arrayResult;
435
429
  } else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
@@ -437,9 +431,19 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
437
431
  for (let i = 0; i < updatedValue.length; i++) {
438
432
  const blockData = updatedValue[i];
439
433
  const origBlock = Array.isArray(origValue) ? origValue[i] : null;
440
- const blockConfig = field.blocks.find((b) => b.slug === blockData.blockType);
434
+ const blockConfig = field.blocks.find(
435
+ (b) => b.slug === blockData.blockType
436
+ );
441
437
  if (blockConfig) {
442
- blocksResult.push(await executeFieldBeforeChange(blockConfig.fields, blockData, origBlock, user, db));
438
+ blocksResult.push(
439
+ await executeFieldBeforeChange(
440
+ blockConfig.fields,
441
+ blockData,
442
+ origBlock,
443
+ user,
444
+ db
445
+ )
446
+ );
443
447
  } else {
444
448
  blocksResult.push(blockData);
445
449
  }
@@ -451,12 +455,10 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
451
455
  return result;
452
456
  }
453
457
  async function executeFieldAfterRead(fields, doc, user, db) {
454
- if (!doc || typeof doc !== "object")
455
- return doc;
458
+ if (!doc || typeof doc !== "object") return doc;
456
459
  const result = { ...doc };
457
460
  for (const field of fields) {
458
- if (!field.name)
459
- continue;
461
+ if (!field.name) continue;
460
462
  const value = result[field.name];
461
463
  let updatedValue = value;
462
464
  if (field.hooks?.afterRead) {
@@ -474,20 +476,41 @@ async function executeFieldAfterRead(fields, doc, user, db) {
474
476
  }
475
477
  if (updatedValue !== void 0 && updatedValue !== null) {
476
478
  if (field.type === "object" && field.fields) {
477
- result[field.name] = await executeFieldAfterRead(field.fields, updatedValue, user, db);
479
+ result[field.name] = await executeFieldAfterRead(
480
+ field.fields,
481
+ updatedValue,
482
+ user,
483
+ db
484
+ );
478
485
  } else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
479
486
  const arrayResult = [];
480
487
  for (const item of updatedValue) {
481
- arrayResult.push(await executeFieldAfterRead(field.fields, item, user, db));
488
+ arrayResult.push(
489
+ await executeFieldAfterRead(
490
+ field.fields,
491
+ item,
492
+ user,
493
+ db
494
+ )
495
+ );
482
496
  }
483
497
  result[field.name] = arrayResult;
484
498
  } else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
485
499
  const blocksResult = [];
486
500
  for (const blockData of updatedValue) {
487
501
  const typedBlock = blockData;
488
- const blockConfig = field.blocks.find((b) => b.slug === typedBlock.blockType);
502
+ const blockConfig = field.blocks.find(
503
+ (b) => b.slug === typedBlock.blockType
504
+ );
489
505
  if (blockConfig) {
490
- blocksResult.push(await executeFieldAfterRead(blockConfig.fields, typedBlock, user, db));
506
+ blocksResult.push(
507
+ await executeFieldAfterRead(
508
+ blockConfig.fields,
509
+ typedBlock,
510
+ user,
511
+ db
512
+ )
513
+ );
491
514
  } else {
492
515
  blocksResult.push(blockData);
493
516
  }
@@ -499,14 +522,16 @@ async function executeFieldAfterRead(fields, doc, user, db) {
499
522
  return result;
500
523
  }
501
524
 
502
- // src/utils/readonly-db.js
525
+ // src/utils/readonly-db.ts
503
526
  var WRITE_METHODS = ["create", "update", "delete", "updateGlobal", "execute"];
504
527
  function createReadonlyDb(db) {
505
528
  return new Proxy(db, {
506
529
  get(target, prop) {
507
530
  if (WRITE_METHODS.includes(prop)) {
508
531
  return () => {
509
- throw new Error(`[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`);
532
+ throw new Error(
533
+ `[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`
534
+ );
510
535
  };
511
536
  }
512
537
  return Reflect.get(target, prop);
@@ -514,13 +539,11 @@ function createReadonlyDb(db) {
514
539
  });
515
540
  }
516
541
 
517
- // src/auth/jexl.js
542
+ // src/auth/jexl.ts
518
543
  var import_jexl = __toESM(require("jexl"), 1);
519
544
  async function evaluateAccess(expression, context) {
520
- if (expression === void 0 || expression === null)
521
- return false;
522
- if (typeof expression === "boolean")
523
- return expression;
545
+ if (expression === void 0 || expression === null) return false;
546
+ if (typeof expression === "boolean") return expression;
524
547
  try {
525
548
  const result = await import_jexl.default.eval(expression, context);
526
549
  return !!result;
@@ -530,7 +553,35 @@ async function evaluateAccess(expression, context) {
530
553
  }
531
554
  }
532
555
 
533
- // src/workflows.js
556
+ // src/utils/admin-auth.ts
557
+ function getAdminAuthCollection(config) {
558
+ const requestedSlug = config.adminAuth?.collectionSlug;
559
+ if (requestedSlug) {
560
+ return config.collections.find((collection) => collection.slug === requestedSlug) ?? null;
561
+ }
562
+ return config.collections.find((collection) => collection.slug === "__admins") ?? config.collections.find((collection) => collection.auth) ?? null;
563
+ }
564
+ function getPublicAdminAuthConfig(adminAuth) {
565
+ const providers = (adminAuth?.providers ?? []).map((provider) => ({
566
+ id: provider.id,
567
+ type: provider.type,
568
+ displayName: provider.displayName || humanizeProviderName(provider.id, provider.type),
569
+ autoRedirect: provider.autoRedirect
570
+ }));
571
+ return {
572
+ mode: adminAuth?.mode ?? "local",
573
+ collectionSlug: adminAuth?.collectionSlug,
574
+ provisioningMode: adminAuth?.provisioningMode,
575
+ providers
576
+ };
577
+ }
578
+ function humanizeProviderName(id, type) {
579
+ const cleaned = id.replace(/[-_]+/g, " ").trim();
580
+ if (!cleaned) return type.toUpperCase();
581
+ return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
582
+ }
583
+
584
+ // src/workflows.ts
534
585
  var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
535
586
  var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
536
587
  function publicMetadata(meta) {
@@ -541,18 +592,17 @@ function workflowCapabilities(workflow, user) {
541
592
  const capabilities = new Set(Array.isArray(user?.capabilities) ? user.capabilities : []);
542
593
  const roles = Array.isArray(user?.roles) ? user.roles : [];
543
594
  for (const mapping of workflow.roles ?? []) {
544
- if (roles.includes(mapping.role))
545
- mapping.capabilities.forEach((capability) => capabilities.add(capability));
595
+ if (roles.includes(mapping.role)) mapping.capabilities.forEach((capability) => capabilities.add(capability));
546
596
  }
547
597
  return capabilities;
548
598
  }
549
599
  function canViewWorkflowDraft(workflow, user) {
550
- if (!user)
551
- return false;
600
+ if (!user) return false;
552
601
  const capabilities = workflowCapabilities(workflow, user);
553
- if (capabilities.has("entry.edit"))
554
- return true;
555
- return workflow.transitions.some((transition) => (transition.requiredCapabilities ?? []).some((capability) => capabilities.has(capability)));
602
+ if (capabilities.has("entry.edit")) return true;
603
+ return workflow.transitions.some(
604
+ (transition) => (transition.requiredCapabilities ?? []).some((capability) => capabilities.has(capability))
605
+ );
556
606
  }
557
607
  function availableWorkflowTransitions(workflow, state, user) {
558
608
  const capabilities = workflowCapabilities(workflow, user);
@@ -569,12 +619,10 @@ function initializeWorkflowDocument(data, workflow) {
569
619
  }
570
620
  function materializeWorkflowDocument(doc, workflow, user) {
571
621
  const meta = doc.__workflow;
572
- if (!meta)
573
- return doc;
622
+ if (!meta) return doc;
574
623
  const { __published, __workflow, ...working } = doc;
575
624
  if (!canViewWorkflowDraft(workflow, user)) {
576
- if (!__published || typeof __published !== "object")
577
- return null;
625
+ if (!__published || typeof __published !== "object") return null;
578
626
  return { id: doc.id, ...__published, _workflow: publicMetadata(meta) };
579
627
  }
580
628
  return {
@@ -606,15 +654,13 @@ async function persistEvent(db, event) {
606
654
  }
607
655
  async function dispatchLifecycleEvent(config, event) {
608
656
  const db = config.db;
609
- if (!db || !config.events?.handlers.length)
610
- return;
657
+ if (!db || !config.events?.handlers.length) return;
611
658
  const maxAttempts = config.events.maxAttempts ?? 8;
612
659
  const retryDelayMs = config.events.retryDelayMs ?? 1e3;
613
660
  const attempts = event.attempts + 1;
614
661
  try {
615
662
  await db.update({ collection: LIFECYCLE_EVENTS_COLLECTION, id: event.id, data: { status: "processing", attempts } });
616
- for (const handler of config.events.handlers)
617
- await handler({ ...event, status: "processing", attempts });
663
+ for (const handler of config.events.handlers) await handler({ ...event, status: "processing", attempts });
618
664
  await db.update({
619
665
  collection: LIFECYCLE_EVENTS_COLLECTION,
620
666
  id: event.id,
@@ -638,8 +684,7 @@ async function saveWorkflowDraft(args) {
638
684
  const { config, collection, id, originalDoc, data, user } = args;
639
685
  const db = config.db;
640
686
  const workflow = collection.workflow;
641
- if (!db.transaction)
642
- throw new Error(`The configured database adapter does not support workflow transactions.`);
687
+ if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
643
688
  const previous = originalDoc.__workflow;
644
689
  const event = createLifecycleEvent({
645
690
  name: "revision.created",
@@ -664,8 +709,7 @@ async function saveWorkflowDraft(args) {
664
709
  async function createWorkflowDocument(args) {
665
710
  const { config, collection, data, user } = args;
666
711
  const db = config.db;
667
- if (!db.transaction)
668
- throw new Error(`The configured database adapter does not support workflow transactions.`);
712
+ if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
669
713
  let event;
670
714
  const doc = await db.transaction(async (tx) => {
671
715
  const created = await tx.create({ collection: collection.slug, data });
@@ -686,14 +730,11 @@ async function transitionWorkflow(args) {
686
730
  const { config, collection, id, transitionName, expectedRevision, comment, user, req } = args;
687
731
  const db = config.db;
688
732
  const workflow = collection.workflow;
689
- if (!db.transaction)
690
- throw new Error(`The configured database adapter does not support workflow transactions.`);
733
+ if (!db.transaction) throw new Error(`The configured database adapter does not support workflow transactions.`);
691
734
  const original = await db.findOne({ collection: collection.slug, id });
692
- if (!original)
693
- throw Object.assign(new Error("Not Found"), { statusCode: 404 });
735
+ if (!original) throw Object.assign(new Error("Not Found"), { statusCode: 404 });
694
736
  const meta = original.__workflow;
695
- if (!meta)
696
- throw Object.assign(new Error("Entry has no workflow metadata"), { statusCode: 409 });
737
+ if (!meta) throw Object.assign(new Error("Entry has no workflow metadata"), { statusCode: 409 });
697
738
  if (expectedRevision !== void 0 && expectedRevision !== meta.revision) {
698
739
  throw Object.assign(new Error("This entry changed since it was loaded. Refresh before transitioning it."), { statusCode: 409 });
699
740
  }
@@ -709,8 +750,7 @@ async function transitionWorkflow(args) {
709
750
  throw Object.assign(new Error(`A comment is required for "${transition.label}".`), { statusCode: 400 });
710
751
  }
711
752
  const hookContext = { transition, from: meta.state, to: transition.to, doc: original, user, comment, req, db };
712
- for (const hook of workflow.hooks?.beforeTransition ?? [])
713
- await hook(hookContext);
753
+ for (const hook of workflow.hooks?.beforeTransition ?? []) await hook(hookContext);
714
754
  const events = [createLifecycleEvent({
715
755
  name: "workflow.transitioned",
716
756
  collection: collection.slug,
@@ -726,8 +766,7 @@ async function transitionWorkflow(args) {
726
766
  }
727
767
  const updated = await db.transaction(async (tx) => {
728
768
  const locked = await tx.findOne({ collection: collection.slug, id });
729
- if (!locked)
730
- throw Object.assign(new Error("Not Found"), { statusCode: 404 });
769
+ if (!locked) throw Object.assign(new Error("Not Found"), { statusCode: 404 });
731
770
  const lockedMeta = locked.__workflow;
732
771
  if (lockedMeta.revision !== meta.revision || lockedMeta.state !== meta.state || expectedRevision !== void 0 && lockedMeta.revision !== expectedRevision) {
733
772
  throw Object.assign(new Error("This entry changed since it was loaded. Refresh before transitioning it."), { statusCode: 409 });
@@ -741,24 +780,20 @@ async function transitionWorkflow(args) {
741
780
  ...transition.unpublish ? { publishedRevision: void 0, publishedAt: void 0, publishedBy: void 0 } : {}
742
781
  };
743
782
  const data = { __workflow: nextMeta };
744
- if (targetState.published)
745
- data.__published = working;
746
- if (transition.unpublish)
747
- data.__published = null;
783
+ if (targetState.published) data.__published = working;
784
+ if (transition.unpublish) data.__published = null;
748
785
  const next = await tx.update({ collection: collection.slug, id, data });
749
786
  await tx.create({
750
787
  collection: WORKFLOW_HISTORY_COLLECTION,
751
788
  data: { collection: collection.slug, documentId: id, transition: transition.name, from: lockedMeta.state, to: transition.to, revision: lockedMeta.revision, comment: comment ?? null, actorId: user?.sub ?? null, createdAt: now }
752
789
  });
753
- for (const event of events)
754
- await persistEvent(tx, event);
790
+ for (const event of events) await persistEvent(tx, event);
755
791
  if (collection.audit) {
756
792
  await tx.create({ collection: "__audit", data: { collection: collection.slug, documentId: id, operation: "workflow.transition", user: user?.sub ?? null, timestamp: now, changes: JSON.stringify({ transition: transition.name, from: lockedMeta.state, to: transition.to, revision: lockedMeta.revision }) } });
757
793
  }
758
794
  return next;
759
795
  });
760
- for (const event of events)
761
- void dispatchLifecycleEvent(config, event);
796
+ for (const event of events) void dispatchLifecycleEvent(config, event);
762
797
  for (const hook of workflow.hooks?.afterTransition ?? []) {
763
798
  try {
764
799
  await hook({ ...hookContext, doc: updated, event: events[0] });
@@ -769,7 +804,7 @@ async function transitionWorkflow(args) {
769
804
  return updated;
770
805
  }
771
806
 
772
- // src/controllers/collection.controller.js
807
+ // src/controllers/collection.controller.ts
773
808
  var CollectionController = class {
774
809
  collection;
775
810
  constructor(collection) {
@@ -777,8 +812,8 @@ var CollectionController = class {
777
812
  }
778
813
  getDelegatedProvider(c) {
779
814
  const config = c.get("config");
780
- const authCollectionSlug = config.adminAuth?.collectionSlug || "__admins";
781
- if (this.collection.slug !== authCollectionSlug) {
815
+ const authCollection = getAdminAuthCollection(config);
816
+ if (!authCollection || this.collection.slug !== authCollection.slug) {
782
817
  return null;
783
818
  }
784
819
  return config.adminAuth?.providers?.find((p) => p.members) || null;
@@ -786,8 +821,7 @@ var CollectionController = class {
786
821
  async find(c) {
787
822
  const config = c.get("config");
788
823
  const db = config.db;
789
- if (!db)
790
- return c.json({ message: "Database not configured" }, 500);
824
+ if (!db) return c.json({ message: "Database not configured" }, 500);
791
825
  const provider = this.getDelegatedProvider(c);
792
826
  if (provider && provider.members?.list) {
793
827
  const limit2 = Number(c.req.query("limit")) || 10;
@@ -905,13 +939,11 @@ var CollectionController = class {
905
939
  async findOne(c) {
906
940
  const config = c.get("config");
907
941
  const db = config.db;
908
- if (!db)
909
- return c.json({ message: "Database not configured" }, 500);
942
+ if (!db) return c.json({ message: "Database not configured" }, 500);
910
943
  const provider = this.getDelegatedProvider(c);
911
944
  if (provider && (provider.members?.get || provider.members?.list)) {
912
945
  const id2 = c.req.param("id");
913
- if (!id2)
914
- return c.json({ message: "Missing ID" }, 400);
946
+ if (!id2) return c.json({ message: "Missing ID" }, 400);
915
947
  const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
916
948
  const externalSubject = localDoc?.externalSubject || id2;
917
949
  let member = null;
@@ -921,8 +953,7 @@ var CollectionController = class {
921
953
  const listResult = await provider.members.list({ req: c.req });
922
954
  member = listResult.docs.find((m) => m.id === externalSubject) || null;
923
955
  }
924
- if (!member)
925
- return c.json({ message: "Not Found" }, 404);
956
+ if (!member) return c.json({ message: "Not Found" }, 404);
926
957
  return c.json({
927
958
  ...member,
928
959
  id: localDoc ? localDoc.id : member.id,
@@ -933,15 +964,12 @@ var CollectionController = class {
933
964
  const id = c.req.param("id");
934
965
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
935
966
  const user = c.get("user");
936
- if (!id)
937
- return c.json({ message: "Missing ID" }, 400);
967
+ if (!id) return c.json({ message: "Missing ID" }, 400);
938
968
  let doc = await db.findOne({ collection: this.collection.slug, id });
939
- if (!doc)
940
- return c.json({ message: "Not Found" }, 404);
969
+ if (!doc) return c.json({ message: "Not Found" }, 404);
941
970
  if (this.collection.workflow) {
942
971
  doc = materializeWorkflowDocument(doc, this.collection.workflow, user);
943
- if (!doc)
944
- return c.json({ message: "Not Found" }, 404);
972
+ if (!doc) return c.json({ message: "Not Found" }, 404);
945
973
  }
946
974
  const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
947
975
  const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
@@ -966,8 +994,7 @@ var CollectionController = class {
966
994
  async create(c) {
967
995
  const config = c.get("config");
968
996
  const db = config.db;
969
- if (!db)
970
- return c.json({ message: "Database not configured" }, 500);
997
+ if (!db) return c.json({ message: "Database not configured" }, 500);
971
998
  const provider = this.getDelegatedProvider(c);
972
999
  if (provider && provider.members?.create) {
973
1000
  const contentType2 = c.req.header("Content-Type") || "";
@@ -1042,16 +1069,13 @@ var CollectionController = class {
1042
1069
  async upload(c) {
1043
1070
  const config = c.get("config");
1044
1071
  const storage = config.storage;
1045
- if (!storage)
1046
- return c.json({ message: "Storage not configured" }, 500);
1072
+ if (!storage) return c.json({ message: "Storage not configured" }, 500);
1047
1073
  const db = config.db;
1048
- if (!db)
1049
- return c.json({ message: "Database not configured" }, 500);
1074
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1050
1075
  const readonlyDb = createReadonlyDb(db);
1051
1076
  const formData = await c.req.formData();
1052
1077
  const file = formData.get("file");
1053
- if (!file)
1054
- return c.json({ message: "No file uploaded" }, 400);
1078
+ if (!file) return c.json({ message: "No file uploaded" }, 400);
1055
1079
  const buffer = new Uint8Array(await file.arrayBuffer());
1056
1080
  const siteId = c.get("siteId");
1057
1081
  const workspaceId = c.get("workspaceId");
@@ -1110,13 +1134,11 @@ var CollectionController = class {
1110
1134
  async update(c) {
1111
1135
  const config = c.get("config");
1112
1136
  const db = config.db;
1113
- if (!db)
1114
- return c.json({ message: "Database not configured" }, 500);
1137
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1115
1138
  const provider = this.getDelegatedProvider(c);
1116
1139
  if (provider && provider.members?.update) {
1117
1140
  const id2 = c.req.param("id");
1118
- if (!id2)
1119
- return c.json({ message: "Missing ID" }, 400);
1141
+ if (!id2) return c.json({ message: "Missing ID" }, 400);
1120
1142
  const body2 = await c.req.json();
1121
1143
  const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
1122
1144
  const externalSubject = localDoc?.externalSubject || id2;
@@ -1129,8 +1151,7 @@ var CollectionController = class {
1129
1151
  }
1130
1152
  const readonlyDb = createReadonlyDb(db);
1131
1153
  const id = c.req.param("id");
1132
- if (!id)
1133
- return c.json({ message: "Missing ID" }, 400);
1154
+ if (!id) return c.json({ message: "Missing ID" }, 400);
1134
1155
  const body = await c.req.json();
1135
1156
  const user = c.get("user");
1136
1157
  let data = { ...body };
@@ -1144,8 +1165,7 @@ var CollectionController = class {
1144
1165
  updatedBy: user?.sub ?? null
1145
1166
  });
1146
1167
  const originalDoc = await db.findOne({ collection: this.collection.slug, id });
1147
- if (!originalDoc)
1148
- return c.json({ message: "Not Found" }, 404);
1168
+ if (!originalDoc) return c.json({ message: "Not Found" }, 404);
1149
1169
  let before = null;
1150
1170
  if (this.collection.audit) {
1151
1171
  before = originalDoc;
@@ -1189,10 +1209,8 @@ var CollectionController = class {
1189
1209
  }
1190
1210
  async transition(c) {
1191
1211
  const config = c.get("config");
1192
- if (!config.db)
1193
- return c.json({ message: "Database not configured" }, 500);
1194
- if (!this.collection.workflow)
1195
- return c.json({ message: "Workflows are not enabled for this collection" }, 404);
1212
+ if (!config.db) return c.json({ message: "Database not configured" }, 500);
1213
+ if (!this.collection.workflow) return c.json({ message: "Workflows are not enabled for this collection" }, 404);
1196
1214
  const id = c.req.param("id");
1197
1215
  const transitionName = c.req.param("transition");
1198
1216
  const body = await c.req.json().catch(() => ({}));
@@ -1215,16 +1233,12 @@ var CollectionController = class {
1215
1233
  }
1216
1234
  async workflowHistory(c) {
1217
1235
  const config = c.get("config");
1218
- if (!config.db)
1219
- return c.json({ message: "Database not configured" }, 500);
1220
- if (!this.collection.workflow)
1221
- return c.json({ message: "Workflows are not enabled for this collection" }, 404);
1236
+ if (!config.db) return c.json({ message: "Database not configured" }, 500);
1237
+ if (!this.collection.workflow) return c.json({ message: "Workflows are not enabled for this collection" }, 404);
1222
1238
  const documentId = c.req.param("id");
1223
- if (!documentId)
1224
- return c.json({ message: "Missing ID" }, 400);
1239
+ if (!documentId) return c.json({ message: "Missing ID" }, 400);
1225
1240
  const document = await config.db.findOne({ collection: this.collection.slug, id: documentId });
1226
- if (!document)
1227
- return c.json({ message: "Not Found" }, 404);
1241
+ if (!document) return c.json({ message: "Not Found" }, 404);
1228
1242
  const readAccess = this.collection.access?.read;
1229
1243
  if (readAccess !== void 0 && readAccess !== null) {
1230
1244
  const args = { user: c.get("user"), req: c.req, doc: document };
@@ -1238,8 +1252,7 @@ var CollectionController = class {
1238
1252
  });
1239
1253
  allowed = match.total > 0;
1240
1254
  }
1241
- if (!allowed)
1242
- return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
1255
+ if (!allowed) return c.json({ error: true, message: `Access denied: read on ${this.collection.slug}` }, 403);
1243
1256
  }
1244
1257
  const result = await config.db.find({
1245
1258
  collection: WORKFLOW_HISTORY_COLLECTION,
@@ -1263,17 +1276,14 @@ var CollectionController = class {
1263
1276
  async changePassword(c) {
1264
1277
  const config = c.get("config");
1265
1278
  const db = config.db;
1266
- if (!db)
1267
- return c.json({ message: "Database not configured" }, 500);
1279
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1268
1280
  if (!this.collection.auth) {
1269
1281
  return c.json({ message: "This collection does not support authentication" }, 400);
1270
1282
  }
1271
1283
  const id = c.req.param("id");
1272
- if (!id)
1273
- return c.json({ message: "Missing ID" }, 400);
1284
+ if (!id) return c.json({ message: "Missing ID" }, 400);
1274
1285
  const user = c.get("user");
1275
- if (!user)
1276
- return c.json({ message: "Authentication required" }, 401);
1286
+ if (!user) return c.json({ message: "Authentication required" }, 401);
1277
1287
  const body = await c.req.json().catch(() => null);
1278
1288
  const { oldPassword, newPassword, confirmPassword } = body ?? {};
1279
1289
  if (!newPassword) {
@@ -1295,8 +1305,7 @@ var CollectionController = class {
1295
1305
  return c.json({ message: "Current password is required" }, 400);
1296
1306
  }
1297
1307
  const existing = await db.findOne({ collection: this.collection.slug, id });
1298
- if (!existing)
1299
- return c.json({ message: "User not found" }, 404);
1308
+ if (!existing) return c.json({ message: "User not found" }, 404);
1300
1309
  const valid = await verifyPassword(oldPassword, existing.password);
1301
1310
  if (!valid) {
1302
1311
  return c.json({ message: "Invalid current password" }, 400);
@@ -1327,13 +1336,11 @@ var CollectionController = class {
1327
1336
  async delete(c) {
1328
1337
  const config = c.get("config");
1329
1338
  const db = config.db;
1330
- if (!db)
1331
- return c.json({ message: "Database not configured" }, 500);
1339
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1332
1340
  const provider = this.getDelegatedProvider(c);
1333
1341
  if (provider && provider.members?.delete) {
1334
1342
  const id2 = c.req.param("id");
1335
- if (!id2)
1336
- return c.json({ message: "Missing ID" }, 400);
1343
+ if (!id2) return c.json({ message: "Missing ID" }, 400);
1337
1344
  const localDoc = await db.findOne({ collection: this.collection.slug, id: id2 });
1338
1345
  const externalSubject = localDoc?.externalSubject || id2;
1339
1346
  await provider.members.delete({ externalSubject, req: c.req });
@@ -1341,12 +1348,10 @@ var CollectionController = class {
1341
1348
  }
1342
1349
  const readonlyDb = createReadonlyDb(db);
1343
1350
  const id = c.req.param("id");
1344
- if (!id)
1345
- return c.json({ message: "Missing ID" }, 400);
1351
+ if (!id) return c.json({ message: "Missing ID" }, 400);
1346
1352
  const user = c.get("user");
1347
1353
  const doc = await db.findOne({ collection: this.collection.slug, id });
1348
- if (!doc)
1349
- return c.json({ message: "Not Found" }, 404);
1354
+ if (!doc) return c.json({ message: "Not Found" }, 404);
1350
1355
  let before = null;
1351
1356
  if (this.collection.audit) {
1352
1357
  before = doc;
@@ -1381,8 +1386,7 @@ var CollectionController = class {
1381
1386
  async deleteMany(c) {
1382
1387
  const config = c.get("config");
1383
1388
  const db = config.db;
1384
- if (!db)
1385
- return c.json({ message: "Database not configured" }, 500);
1389
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1386
1390
  const readonlyDb = createReadonlyDb(db);
1387
1391
  const user = c.get("user");
1388
1392
  let ids = [];
@@ -1397,8 +1401,7 @@ var CollectionController = class {
1397
1401
  const raw = c.req.queries("ids") ?? c.req.queries("ids[]") ?? [];
1398
1402
  ids = raw.filter(Boolean);
1399
1403
  }
1400
- if (!ids.length)
1401
- return c.json({ message: "No IDs provided" }, 400);
1404
+ if (!ids.length) return c.json({ message: "No IDs provided" }, 400);
1402
1405
  const deleted = [];
1403
1406
  const failed = [];
1404
1407
  for (const id of ids) {
@@ -1451,8 +1454,7 @@ var CollectionController = class {
1451
1454
  async seed(c) {
1452
1455
  const config = c.get("config");
1453
1456
  const db = config.db;
1454
- if (!db)
1455
- return c.json({ message: "Database not configured" }, 500);
1457
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1456
1458
  const body = await c.req.json();
1457
1459
  const initialData = body.data;
1458
1460
  if (!initialData || !Array.isArray(initialData)) {
@@ -1472,7 +1474,7 @@ var CollectionController = class {
1472
1474
  }
1473
1475
  };
1474
1476
 
1475
- // src/controllers/global.controller.js
1477
+ // src/controllers/global.controller.ts
1476
1478
  var GlobalController = class {
1477
1479
  global;
1478
1480
  constructor(global) {
@@ -1481,8 +1483,7 @@ var GlobalController = class {
1481
1483
  async get(c) {
1482
1484
  const config = c.get("config");
1483
1485
  const db = config.db;
1484
- if (!db)
1485
- return c.json({ message: "Database not configured" }, 500);
1486
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1486
1487
  const readonlyDb = createReadonlyDb(db);
1487
1488
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
1488
1489
  const user = c.get("user");
@@ -1526,8 +1527,7 @@ var GlobalController = class {
1526
1527
  async update(c) {
1527
1528
  const config = c.get("config");
1528
1529
  const db = config.db;
1529
- if (!db)
1530
- return c.json({ message: "Database not configured" }, 500);
1530
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1531
1531
  const readonlyDb = createReadonlyDb(db);
1532
1532
  const body = await c.req.json();
1533
1533
  const user = c.get("user");
@@ -1562,8 +1562,7 @@ var GlobalController = class {
1562
1562
  async seed(c) {
1563
1563
  const config = c.get("config");
1564
1564
  const db = config.db;
1565
- if (!db)
1566
- return c.json({ message: "Database not configured" }, 500);
1565
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1567
1566
  const body = await c.req.json();
1568
1567
  const initialData = body.data;
1569
1568
  if (!initialData) {
@@ -1579,23 +1578,20 @@ var GlobalController = class {
1579
1578
  }
1580
1579
  };
1581
1580
  function isFunctionallyEmpty(obj) {
1582
- if (obj === null || obj === void 0 || obj === "")
1583
- return true;
1581
+ if (obj === null || obj === void 0 || obj === "") return true;
1584
1582
  if (Array.isArray(obj)) {
1585
- if (obj.length === 0)
1586
- return true;
1583
+ if (obj.length === 0) return true;
1587
1584
  return obj.every(isFunctionallyEmpty);
1588
1585
  }
1589
1586
  if (typeof obj === "object") {
1590
1587
  const keys = Object.keys(obj);
1591
- if (keys.length === 0)
1592
- return true;
1588
+ if (keys.length === 0) return true;
1593
1589
  return keys.every((key) => isFunctionallyEmpty(obj[key]));
1594
1590
  }
1595
1591
  return false;
1596
1592
  }
1597
1593
 
1598
- // src/controllers/media.controller.js
1594
+ // src/controllers/media.controller.ts
1599
1595
  var MediaController = class {
1600
1596
  collection;
1601
1597
  constructor(collection = "media") {
@@ -1675,8 +1671,7 @@ var MediaController = class {
1675
1671
  }
1676
1672
  }
1677
1673
  const db = config.db;
1678
- if (!db)
1679
- return c.json({ message: "Database not configured" }, 500);
1674
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1680
1675
  const doc = await db.create({
1681
1676
  collection: this.collection,
1682
1677
  data: finalFileData
@@ -1685,8 +1680,7 @@ var MediaController = class {
1685
1680
  }
1686
1681
  async find(c) {
1687
1682
  const db = c.get("config").db;
1688
- if (!db)
1689
- return c.json({ message: "Database not configured" }, 500);
1683
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1690
1684
  const limit = Number(c.req.query("limit")) || 10;
1691
1685
  const page = Number(c.req.query("page")) || 1;
1692
1686
  const result = await db.find({
@@ -1700,14 +1694,11 @@ var MediaController = class {
1700
1694
  const config = c.get("config");
1701
1695
  const storage = config.storage;
1702
1696
  const db = config.db;
1703
- if (!db)
1704
- return c.json({ message: "Database not configured" }, 500);
1697
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1705
1698
  const id = c.req.param("id");
1706
- if (!id)
1707
- return c.json({ message: "Missing ID" }, 400);
1699
+ if (!id) return c.json({ message: "Missing ID" }, 400);
1708
1700
  const doc = await db.findOne({ collection: this.collection, id });
1709
- if (!doc)
1710
- return c.json({ message: "Not Found" }, 404);
1701
+ if (!doc) return c.json({ message: "Not Found" }, 404);
1711
1702
  if (storage) {
1712
1703
  await storage.delete({ filename: doc.filename });
1713
1704
  if (doc.sizes) {
@@ -1728,26 +1719,26 @@ var MediaController = class {
1728
1719
  return c.json({ message: "Storage not configured for serving" }, 404);
1729
1720
  }
1730
1721
  const filename = c.req.param("filename");
1731
- if (!filename)
1732
- return c.json({ message: "Missing filename" }, 400);
1722
+ if (!filename) return c.json({ message: "Missing filename" }, 400);
1733
1723
  let res = await storage.resolve({ filename });
1734
1724
  if (!res && !filename.includes("/")) {
1735
1725
  res = await storage.resolve({ filename: `default/${filename}` });
1736
1726
  }
1737
- if (!res)
1738
- return c.json({ message: "Not Found" }, 404);
1727
+ if (!res) return c.json({ message: "Not Found" }, 404);
1739
1728
  c.header("Content-Type", res.mimeType);
1740
1729
  return c.body(res.buffer);
1741
1730
  }
1742
1731
  };
1743
1732
 
1744
- // src/auth/token.js
1733
+ // src/auth/token.ts
1745
1734
  var import_jose = require("jose");
1746
1735
  var import_node_util2 = require("util");
1747
1736
  function getSecret() {
1748
1737
  const secret = process.env.DYRECTED_JWT_SECRET || process.env.JWT_SECRET;
1749
1738
  if (!secret) {
1750
- throw new Error("[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections.");
1739
+ throw new Error(
1740
+ "[dyrected/core] DYRECTED_JWT_SECRET is not set. Add it to your environment variables to enable auth collections."
1741
+ );
1751
1742
  }
1752
1743
  return new import_node_util2.TextEncoder().encode(secret);
1753
1744
  }
@@ -1760,7 +1751,7 @@ async function verifyCollectionToken(token) {
1760
1751
  return payload;
1761
1752
  }
1762
1753
 
1763
- // src/services/email-template.js
1754
+ // src/services/email-template.ts
1764
1755
  var emailTokens = {
1765
1756
  colors: {
1766
1757
  canvas: "#f6f7f2",
@@ -1824,9 +1815,11 @@ function detailBox(content, monospace = false) {
1824
1815
  }
1825
1816
  function ctaButton(label, href) {
1826
1817
  const safeHref = safeHttpUrl(href);
1827
- if (!safeHref)
1828
- return "";
1829
- return table(row(`<a href="${safeHref}" style="display:inline-block;padding:13px 22px;border-radius:${emailTokens.radius.control};background:${emailTokens.colors.accent};font-family:${emailTokens.font};font-size:14px;line-height:1.2;font-weight:700;color:${emailTokens.colors.text};text-decoration:none">${escapeHtml(label)}</a>`, "padding:8px 0 24px"), "width:auto");
1818
+ if (!safeHref) return "";
1819
+ return table(row(
1820
+ `<a href="${safeHref}" style="display:inline-block;padding:13px 22px;border-radius:${emailTokens.radius.control};background:${emailTokens.colors.accent};font-family:${emailTokens.font};font-size:14px;line-height:1.2;font-weight:700;color:${emailTokens.colors.text};text-decoration:none">${escapeHtml(label)}</a>`,
1821
+ "padding:8px 0 24px"
1822
+ ), "width:auto");
1830
1823
  }
1831
1824
  function alertBox(content) {
1832
1825
  return table(row(escapeHtml(content), `padding:13px 16px;font-family:${emailTokens.font};font-size:13px;line-height:1.5;color:${emailTokens.colors.dangerText}`), `width:100%;background:${emailTokens.colors.dangerSurface};border:1px solid ${emailTokens.colors.dangerBorder};border-radius:${emailTokens.radius.control}`);
@@ -1837,19 +1830,23 @@ function layout({ preheader, title, content, footer }) {
1837
1830
  <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head>
1838
1831
  <body style="margin:0;padding:0;background:${emailTokens.colors.canvas}">
1839
1832
  <div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent">${escapeHtml(preheader)}</div>
1840
- ${table(row(table(row("&nbsp;", `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) + row(`${sectionLabel("Dyrected")}${heading(title)}`, "padding:30px 32px 24px") + row(content, "padding:0 32px 32px") + row(`${divider()}${paragraph(footer, "20px 0 6px")}${paragraph("Privacy: this message contains account-related information; please avoid forwarding it.", "0")}`, "padding:0 32px 28px"), `width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`), "padding:32px 12px"), `width:100%;background:${emailTokens.colors.canvas}`)}
1833
+ ${table(row(
1834
+ table(
1835
+ row("&nbsp;", `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) + row(`${sectionLabel("Dyrected")}${heading(title)}`, "padding:30px 32px 24px") + row(content, "padding:0 32px 32px") + row(`${divider()}${paragraph(footer, "20px 0 6px")}${paragraph("Privacy: this message contains account-related information; please avoid forwarding it.", "0")}`, "padding:0 32px 28px"),
1836
+ `width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`
1837
+ ),
1838
+ "padding:32px 12px"
1839
+ ), `width:100%;background:${emailTokens.colors.canvas}`)}
1841
1840
  </body>
1842
1841
  </html>`;
1843
1842
  }
1844
1843
 
1845
- // src/services/email.service.js
1844
+ // src/services/email.service.ts
1846
1845
  var _devSend = null;
1847
1846
  var _devSendPromise = null;
1848
1847
  async function getDevSend() {
1849
- if (_devSend)
1850
- return _devSend;
1851
- if (_devSendPromise)
1852
- return _devSendPromise;
1848
+ if (_devSend) return _devSend;
1849
+ if (_devSendPromise) return _devSendPromise;
1853
1850
  _devSendPromise = (async () => {
1854
1851
  try {
1855
1852
  const nodemailer = await import("nodemailer");
@@ -1933,7 +1930,7 @@ function buildPasswordChangedEmail(config, args) {
1933
1930
  };
1934
1931
  }
1935
1932
 
1936
- // src/controllers/auth.controller.js
1933
+ // src/controllers/auth.controller.ts
1937
1934
  var AuthController = class {
1938
1935
  collection;
1939
1936
  constructor(collection) {
@@ -1945,8 +1942,7 @@ var AuthController = class {
1945
1942
  // ---------------------------------------------------------------------------
1946
1943
  async init(c) {
1947
1944
  const db = c.get("config").db;
1948
- if (!db)
1949
- return c.json({ message: "Database not configured" }, 500);
1945
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1950
1946
  const result = await db.find({
1951
1947
  collection: this.collection.slug,
1952
1948
  limit: 1
@@ -1962,8 +1958,7 @@ var AuthController = class {
1962
1958
  async registerFirstUser(c) {
1963
1959
  const config = c.get("config");
1964
1960
  const db = config.db;
1965
- if (!db)
1966
- return c.json({ message: "Database not configured" }, 500);
1961
+ if (!db) return c.json({ message: "Database not configured" }, 500);
1967
1962
  const check = await db.find({
1968
1963
  collection: this.collection.slug,
1969
1964
  limit: 1
@@ -1991,7 +1986,9 @@ var AuthController = class {
1991
1986
  collection: this.collection.slug
1992
1987
  });
1993
1988
  const { subject, html } = buildWelcomeEmail(config, { email: body.email });
1994
- sendEmail(config, { to: body.email, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send welcome email:", err));
1989
+ sendEmail(config, { to: body.email, subject, html }).catch(
1990
+ (err) => console.error("[dyrected/core] Failed to send welcome email:", err)
1991
+ );
1995
1992
  const { password: _, ...safeUser } = user;
1996
1993
  return c.json({ token, user: safeUser });
1997
1994
  }
@@ -2000,8 +1997,7 @@ var AuthController = class {
2000
1997
  // ---------------------------------------------------------------------------
2001
1998
  async login(c) {
2002
1999
  const db = c.get("config").db;
2003
- if (!db)
2004
- return c.json({ message: "Database not configured" }, 500);
2000
+ if (!db) return c.json({ message: "Database not configured" }, 500);
2005
2001
  const body = await c.req.json().catch(() => null);
2006
2002
  if (!body?.email || !body?.password) {
2007
2003
  return c.json({ error: true, message: "email and password are required." }, 400);
@@ -2040,8 +2036,7 @@ var AuthController = class {
2040
2036
  // ---------------------------------------------------------------------------
2041
2037
  async me(c) {
2042
2038
  const db = c.get("config").db;
2043
- if (!db)
2044
- return c.json({ message: "Database not configured" }, 500);
2039
+ if (!db) return c.json({ message: "Database not configured" }, 500);
2045
2040
  const requestUser = c.get("user");
2046
2041
  if (!requestUser) {
2047
2042
  return c.json({ error: true, message: "Authentication required." }, 401);
@@ -2076,8 +2071,7 @@ var AuthController = class {
2076
2071
  async forgotPassword(c) {
2077
2072
  const config = c.get("config");
2078
2073
  const db = config.db;
2079
- if (!db)
2080
- return c.json({ message: "Database not configured" }, 500);
2074
+ if (!db) return c.json({ message: "Database not configured" }, 500);
2081
2075
  const body = await c.req.json().catch(() => null);
2082
2076
  if (!body?.email) {
2083
2077
  return c.json({ error: true, message: "email is required." }, 400);
@@ -2089,7 +2083,10 @@ var AuthController = class {
2089
2083
  });
2090
2084
  const user = result.docs[0];
2091
2085
  if (user) {
2092
- const resetToken = await signCollectionToken({ sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" }, "1h");
2086
+ const resetToken = await signCollectionToken(
2087
+ { sub: user.id, email: user.email, collection: this.collection.slug, purpose: "reset" },
2088
+ "1h"
2089
+ );
2093
2090
  const resetUrl = body?.resetUrl;
2094
2091
  const url = resetUrl ? `${resetUrl}${resetUrl.includes("?") ? "&" : "?"}token=${encodeURIComponent(resetToken)}` : void 0;
2095
2092
  try {
@@ -2112,8 +2109,7 @@ var AuthController = class {
2112
2109
  async resetPassword(c) {
2113
2110
  const config = c.get("config");
2114
2111
  const db = config.db;
2115
- if (!db)
2116
- return c.json({ message: "Database not configured" }, 500);
2112
+ if (!db) return c.json({ message: "Database not configured" }, 500);
2117
2113
  const body = await c.req.json().catch(() => null);
2118
2114
  if (!body?.token || !body?.password) {
2119
2115
  return c.json({ error: true, message: "token and password are required." }, 400);
@@ -2134,7 +2130,9 @@ var AuthController = class {
2134
2130
  data: { password: hashedPassword }
2135
2131
  });
2136
2132
  const { subject, html } = buildPasswordChangedEmail(config, { email: payload.email });
2137
- sendEmail(config, { to: payload.email, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send password-changed email:", err));
2133
+ sendEmail(config, { to: payload.email, subject, html }).catch(
2134
+ (err) => console.error("[dyrected/core] Failed to send password-changed email:", err)
2135
+ );
2138
2136
  return c.json({ success: true, message: "Password has been reset. You can now log in." });
2139
2137
  }
2140
2138
  // ---------------------------------------------------------------------------
@@ -2144,8 +2142,7 @@ var AuthController = class {
2144
2142
  async invite(c) {
2145
2143
  const config = c.get("config");
2146
2144
  const db = config.db;
2147
- if (!db)
2148
- return c.json({ message: "Database not configured" }, 500);
2145
+ if (!db) return c.json({ message: "Database not configured" }, 500);
2149
2146
  const requestUser = c.get("user");
2150
2147
  if (!requestUser) {
2151
2148
  return c.json({ error: true, message: "Authentication required." }, 401);
@@ -2162,7 +2159,10 @@ var AuthController = class {
2162
2159
  if (existing.total > 0) {
2163
2160
  return c.json({ error: true, message: "An account with that email already exists." }, 409);
2164
2161
  }
2165
- const inviteToken = await signCollectionToken({ sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" }, "7d");
2162
+ const inviteToken = await signCollectionToken(
2163
+ { sub: body.email, email: body.email, collection: this.collection.slug, purpose: "invite" },
2164
+ "7d"
2165
+ );
2166
2166
  try {
2167
2167
  const { subject, html } = buildInviteEmail(config, {
2168
2168
  token: inviteToken,
@@ -2182,8 +2182,7 @@ var AuthController = class {
2182
2182
  async acceptInvite(c) {
2183
2183
  const config = c.get("config");
2184
2184
  const db = config.db;
2185
- if (!db)
2186
- return c.json({ message: "Database not configured" }, 500);
2185
+ if (!db) return c.json({ message: "Database not configured" }, 500);
2187
2186
  const body = await c.req.json().catch(() => null);
2188
2187
  if (!body?.token || !body?.password) {
2189
2188
  return c.json({ error: true, message: "token and password are required." }, 400);
@@ -2218,52 +2217,23 @@ var AuthController = class {
2218
2217
  collection: this.collection.slug
2219
2218
  });
2220
2219
  const { subject, html } = buildWelcomeEmail(config, { email: inviteeEmail });
2221
- sendEmail(config, { to: inviteeEmail, subject, html }).catch((err) => console.error("[dyrected/core] Failed to send welcome email:", err));
2220
+ sendEmail(config, { to: inviteeEmail, subject, html }).catch(
2221
+ (err) => console.error("[dyrected/core] Failed to send welcome email:", err)
2222
+ );
2222
2223
  const { password: _, ...safeUser } = user;
2223
2224
  return c.json({ token: sessionToken, user: safeUser }, 201);
2224
2225
  }
2225
2226
  };
2226
2227
 
2227
- // src/controllers/admin-auth.controller.js
2228
+ // src/controllers/admin-auth.controller.ts
2228
2229
  var import_node_crypto2 = require("crypto");
2229
2230
  var import_node_util3 = require("util");
2230
2231
  var import_jose2 = require("jose");
2231
-
2232
- // src/utils/admin-auth.js
2233
- function getAdminAuthCollection(config) {
2234
- const requestedSlug = config.adminAuth?.collectionSlug;
2235
- if (requestedSlug) {
2236
- return config.collections.find((collection) => collection.slug === requestedSlug) ?? null;
2237
- }
2238
- return config.collections.find((collection) => collection.slug === "__admins") ?? config.collections.find((collection) => collection.auth) ?? null;
2239
- }
2240
- function getPublicAdminAuthConfig(adminAuth) {
2241
- const providers = (adminAuth?.providers ?? []).map((provider) => ({
2242
- id: provider.id,
2243
- type: provider.type,
2244
- displayName: provider.displayName || humanizeProviderName(provider.id, provider.type),
2245
- autoRedirect: provider.autoRedirect
2246
- }));
2247
- return {
2248
- mode: adminAuth?.mode ?? "local",
2249
- collectionSlug: adminAuth?.collectionSlug,
2250
- provisioningMode: adminAuth?.provisioningMode,
2251
- providers
2252
- };
2253
- }
2254
- function humanizeProviderName(id, type) {
2255
- const cleaned = id.replace(/[-_]+/g, " ").trim();
2256
- if (!cleaned)
2257
- return type.toUpperCase();
2258
- return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
2259
- }
2260
-
2261
- // src/controllers/admin-auth.controller.js
2262
2232
  var AdminAuthController = class {
2263
- config;
2264
2233
  constructor(config) {
2265
2234
  this.config = config;
2266
2235
  }
2236
+ config;
2267
2237
  providers(c) {
2268
2238
  return c.json(getPublicAdminAuthConfig(this.config.adminAuth));
2269
2239
  }
@@ -2276,7 +2246,12 @@ var AdminAuthController = class {
2276
2246
  const siteId = this.getSiteId(c);
2277
2247
  const returnTo = this.normalizeReturnTo(c.req.query("returnTo"), c);
2278
2248
  if (provider.type === "oidc") {
2279
- const redirectUrl = await this.buildOidcStartUrl(provider, returnTo, siteId, new URL(c.req.url).origin);
2249
+ const redirectUrl = await this.buildOidcStartUrl(
2250
+ provider,
2251
+ returnTo,
2252
+ siteId,
2253
+ new URL(c.req.url).origin
2254
+ );
2280
2255
  return c.redirect(redirectUrl, 302);
2281
2256
  }
2282
2257
  if (!provider.startUrl) {
@@ -2284,14 +2259,16 @@ var AdminAuthController = class {
2284
2259
  }
2285
2260
  const url = this.buildCustomProviderStartUrl(provider.startUrl, new URL(c.req.url).origin);
2286
2261
  if (!url) {
2287
- return c.json({
2288
- error: true,
2289
- message: `Admin auth provider "${provider.id}" has an invalid start URL.`
2290
- }, 500);
2262
+ return c.json(
2263
+ {
2264
+ error: true,
2265
+ message: `Admin auth provider "${provider.id}" has an invalid start URL.`
2266
+ },
2267
+ 500
2268
+ );
2291
2269
  }
2292
2270
  url.searchParams.set("returnTo", returnTo);
2293
- if (siteId)
2294
- url.searchParams.set("siteId", siteId);
2271
+ if (siteId) url.searchParams.set("siteId", siteId);
2295
2272
  url.searchParams.set("provider", provider.id);
2296
2273
  return c.redirect(url.toString(), 302);
2297
2274
  }
@@ -2338,8 +2315,7 @@ var AdminAuthController = class {
2338
2315
  return c.json({ success: true, message: "Logged out. Discard your token." });
2339
2316
  }
2340
2317
  getProvider(config, id) {
2341
- if (config.adminAuth?.mode !== "external")
2342
- return void 0;
2318
+ if (config.adminAuth?.mode !== "external") return void 0;
2343
2319
  return config.adminAuth.providers.find((provider) => provider.id === id);
2344
2320
  }
2345
2321
  async completeProviderAuth(requestConfig, provider, c) {
@@ -2351,8 +2327,7 @@ var AdminAuthController = class {
2351
2327
  throw new Error("Admin auth collection is not configured.");
2352
2328
  }
2353
2329
  const db = c.get("config").db;
2354
- if (!db)
2355
- throw new Error("Database not configured.");
2330
+ if (!db) throw new Error("Database not configured.");
2356
2331
  let user = await this.findUserByExternalIdentity(db, adminCollection.slug, provider.id, identity.sub) ?? (identity.email ? (await db.find({
2357
2332
  collection: adminCollection.slug,
2358
2333
  where: { email: identity.email },
@@ -2412,8 +2387,7 @@ var AdminAuthController = class {
2412
2387
  },
2413
2388
  user
2414
2389
  });
2415
- if (resolved)
2416
- return resolved;
2390
+ if (resolved) return resolved;
2417
2391
  return { allowed: true, roles: identity.roles, data: {} };
2418
2392
  }
2419
2393
  async buildUserData(identity, providerId, roles, extraData) {
@@ -2444,8 +2418,7 @@ var AdminAuthController = class {
2444
2418
  }
2445
2419
  async getRequestConfig(c) {
2446
2420
  const siteId = this.getSiteId(c);
2447
- if (!siteId || !this.config.onSchemaFetch)
2448
- return this.config;
2421
+ if (!siteId || !this.config.onSchemaFetch) return this.config;
2449
2422
  const dynamic = await this.config.onSchemaFetch(siteId);
2450
2423
  return {
2451
2424
  ...this.config,
@@ -2476,8 +2449,7 @@ var AdminAuthController = class {
2476
2449
  async resolveOidcIdentity(provider, c) {
2477
2450
  const code = c.req.query("code");
2478
2451
  const state = c.req.query("state");
2479
- if (!code || !state)
2480
- throw new Error("Missing OIDC callback parameters.");
2452
+ if (!code || !state) throw new Error("Missing OIDC callback parameters.");
2481
2453
  const parsedState = await this.verifyState(state, provider.id);
2482
2454
  const discovery = await this.getOidcDiscovery(provider);
2483
2455
  const redirectUri = provider.redirectUri || this.defaultCallbackPath(provider.id, new URL(c.req.url).origin);
@@ -2512,8 +2484,7 @@ var AdminAuthController = class {
2512
2484
  const userInfo = await fetch(provider.userInfoEndpoint || discovery.userinfo_endpoint, {
2513
2485
  headers: { Authorization: `Bearer ${tokenBody.access_token}` }
2514
2486
  });
2515
- if (!userInfo.ok)
2516
- throw new Error("OIDC userinfo request failed.");
2487
+ if (!userInfo.ok) throw new Error("OIDC userinfo request failed.");
2517
2488
  claims = await userInfo.json();
2518
2489
  } else {
2519
2490
  throw new Error("OIDC provider did not return usable identity claims.");
@@ -2553,13 +2524,15 @@ var AdminAuthController = class {
2553
2524
  if (provider.audience) {
2554
2525
  const aud = claims.aud;
2555
2526
  const matches = Array.isArray(aud) ? aud.includes(provider.audience) : aud === provider.audience;
2556
- if (!matches)
2557
- throw new Error("External auth audience mismatch.");
2527
+ if (!matches) throw new Error("External auth audience mismatch.");
2558
2528
  }
2559
2529
  }
2560
2530
  return {
2561
2531
  identity: this.mapClaims(claims, provider.claimMapping),
2562
- returnTo: this.normalizeReturnTo(body?.returnTo || c.req.query("returnTo"), c)
2532
+ returnTo: this.normalizeReturnTo(
2533
+ body?.returnTo || c.req.query("returnTo"),
2534
+ c
2535
+ )
2563
2536
  };
2564
2537
  }
2565
2538
  mapClaims(claims, mapping) {
@@ -2589,20 +2562,19 @@ var AdminAuthController = class {
2589
2562
  return typeof value === "string" ? value : void 0;
2590
2563
  }
2591
2564
  readStringArrayClaim(value) {
2592
- if (!value)
2593
- return void 0;
2565
+ if (!value) return void 0;
2594
2566
  if (Array.isArray(value)) {
2595
2567
  return value.filter((entry) => typeof entry === "string");
2596
2568
  }
2597
- if (typeof value === "string")
2598
- return [value];
2569
+ if (typeof value === "string") return [value];
2599
2570
  return void 0;
2600
2571
  }
2601
2572
  async getOidcDiscovery(provider) {
2602
- const discoveryUrl = new URL(provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration");
2573
+ const discoveryUrl = new URL(
2574
+ provider.issuer.replace(/\/$/, "") + "/.well-known/openid-configuration"
2575
+ );
2603
2576
  const response = await fetch(discoveryUrl.toString());
2604
- if (!response.ok)
2605
- throw new Error("Failed to load OIDC discovery document.");
2577
+ if (!response.ok) throw new Error("Failed to load OIDC discovery document.");
2606
2578
  return response.json();
2607
2579
  }
2608
2580
  defaultCallbackPath(providerId, origin = "") {
@@ -2610,12 +2582,10 @@ var AdminAuthController = class {
2610
2582
  }
2611
2583
  buildCustomProviderStartUrl(startUrl, origin) {
2612
2584
  const value = startUrl.trim();
2613
- if (!value || /^(undefined|null)(\/|$)/i.test(value))
2614
- return null;
2585
+ if (!value || /^(undefined|null)(\/|$)/i.test(value)) return null;
2615
2586
  try {
2616
2587
  const url = new URL(value, origin);
2617
- if (url.protocol !== "http:" && url.protocol !== "https:")
2618
- return null;
2588
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
2619
2589
  return url;
2620
2590
  } catch {
2621
2591
  return null;
@@ -2640,14 +2610,13 @@ var AdminAuthController = class {
2640
2610
  return new import_node_util3.TextEncoder().encode(secret);
2641
2611
  }
2642
2612
  normalizeReturnTo(returnTo, c) {
2643
- if (returnTo)
2644
- return returnTo;
2613
+ if (returnTo) return returnTo;
2645
2614
  const url = new URL(c.req.url);
2646
2615
  return `${url.origin}/admin`;
2647
2616
  }
2648
2617
  };
2649
2618
 
2650
- // src/controllers/preview.controller.js
2619
+ // src/controllers/preview.controller.ts
2651
2620
  var import_jose3 = require("jose");
2652
2621
  var import_node_util4 = require("util");
2653
2622
  var PreviewController = class {
@@ -2690,7 +2659,7 @@ var PreviewController = class {
2690
2659
  }
2691
2660
  };
2692
2661
 
2693
- // src/middleware/auth.js
2662
+ // src/middleware/auth.ts
2694
2663
  function requireAuth() {
2695
2664
  return async (c, next) => {
2696
2665
  const authHeader = c.req.header("Authorization");
@@ -2722,7 +2691,7 @@ function optionalAuth() {
2722
2691
  };
2723
2692
  }
2724
2693
 
2725
- // src/utils/openapi.js
2694
+ // src/utils/openapi.ts
2726
2695
  function generateOpenApi(config) {
2727
2696
  const spec = {
2728
2697
  openapi: "3.0.0",
@@ -3431,8 +3400,7 @@ function fieldsToProperties(fields) {
3431
3400
  required.push(...nested.required);
3432
3401
  continue;
3433
3402
  }
3434
- if (!field.name)
3435
- continue;
3403
+ if (!field.name) continue;
3436
3404
  props[field.name] = fieldToSchema(field);
3437
3405
  if (field.required) {
3438
3406
  required.push(field.name);
@@ -3544,12 +3512,11 @@ function fieldToSchema(field) {
3544
3512
  default:
3545
3513
  schema = { type: "string" };
3546
3514
  }
3547
- if (field.label)
3548
- schema.description = field.label;
3515
+ if (field.label) schema.description = field.label;
3549
3516
  return schema;
3550
3517
  }
3551
3518
 
3552
- // src/utils/swagger.js
3519
+ // src/utils/swagger.ts
3553
3520
  function getSwaggerHtml(specUrl = "/api/openapi.json") {
3554
3521
  return `
3555
3522
  <!DOCTYPE html>
@@ -3598,7 +3565,7 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
3598
3565
  `;
3599
3566
  }
3600
3567
 
3601
- // src/router.js
3568
+ // src/router.ts
3602
3569
  function accessGate(target, action) {
3603
3570
  return async (c, next) => {
3604
3571
  const user = c.get("user");
@@ -3615,8 +3582,7 @@ function accessGate(target, action) {
3615
3582
  };
3616
3583
  }
3617
3584
  async function checkAccess(access, accessArgs) {
3618
- if (access === void 0 || access === null)
3619
- return true;
3585
+ if (access === void 0 || access === null) return true;
3620
3586
  if (typeof access === "function") {
3621
3587
  try {
3622
3588
  const result = await access(accessArgs);
@@ -3632,8 +3598,7 @@ async function checkAccess(access, accessArgs) {
3632
3598
  return true;
3633
3599
  }
3634
3600
  function serializeFieldForApi(f) {
3635
- if (!f)
3636
- return f;
3601
+ if (!f) return f;
3637
3602
  const serialized = { ...f };
3638
3603
  if (serialized.admin?.hooks) {
3639
3604
  const hooks = { ...serialized.admin.hooks };
@@ -3666,10 +3631,8 @@ function registerRoutes(app, config) {
3666
3631
  let globals = [...config.globals];
3667
3632
  if (siteId && config.onSchemaFetch) {
3668
3633
  const dynamic = await config.onSchemaFetch(siteId);
3669
- if (dynamic.collections)
3670
- collections = [...collections, ...dynamic.collections];
3671
- if (dynamic.globals)
3672
- globals = [...globals, ...dynamic.globals];
3634
+ if (dynamic.collections) collections = [...collections, ...dynamic.collections];
3635
+ if (dynamic.globals) globals = [...globals, ...dynamic.globals];
3673
3636
  if (dynamic.admin) {
3674
3637
  config.admin = { ...config.admin, ...dynamic.admin };
3675
3638
  }
@@ -3680,10 +3643,8 @@ function registerRoutes(app, config) {
3680
3643
  const user = c.get("user");
3681
3644
  const accessArgs = { user, req: c.req, doc: null };
3682
3645
  const serializeAccess = async (access) => {
3683
- if (typeof access === "string")
3684
- return access;
3685
- if (typeof access === "boolean")
3686
- return access;
3646
+ if (typeof access === "string") return access;
3647
+ if (typeof access === "boolean") return access;
3687
3648
  return checkAccess(access, accessArgs);
3688
3649
  };
3689
3650
  const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
@@ -3776,8 +3737,7 @@ function registerRoutes(app, config) {
3776
3737
  let collections = [...config.collections];
3777
3738
  if (siteId && config.onSchemaFetch) {
3778
3739
  const dynamic = await config.onSchemaFetch(siteId);
3779
- if (dynamic.collections)
3780
- collections = [...collections, ...dynamic.collections];
3740
+ if (dynamic.collections) collections = [...collections, ...dynamic.collections];
3781
3741
  }
3782
3742
  const user = c.get("user");
3783
3743
  let collection = collections.find((col) => col.slug === colSlug);
@@ -3796,8 +3756,7 @@ function registerRoutes(app, config) {
3796
3756
  let globals = [...config.globals];
3797
3757
  if (siteId && config.onSchemaFetch) {
3798
3758
  const dynamic = await config.onSchemaFetch(siteId);
3799
- if (dynamic.globals)
3800
- globals = [...globals, ...dynamic.globals];
3759
+ if (dynamic.globals) globals = [...globals, ...dynamic.globals];
3801
3760
  }
3802
3761
  const glb = globals.find((g) => g.slug === colSlug);
3803
3762
  if (!glb) {
@@ -3855,12 +3814,9 @@ function registerRoutes(app, config) {
3855
3814
  const user = c.get("user");
3856
3815
  const key = c.req.param("key");
3857
3816
  const scope = c.req.query("scope");
3858
- if (!db)
3859
- return c.json({ message: "Database not configured" }, 500);
3860
- if (!user?.collection || !user.sub)
3861
- return c.json({ error: true, message: "Authentication required." }, 401);
3862
- if (!key)
3863
- return c.json({ error: true, message: "Preference key is required." }, 400);
3817
+ if (!db) return c.json({ message: "Database not configured" }, 500);
3818
+ if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
3819
+ if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
3864
3820
  const getGlobalPreference = async () => {
3865
3821
  const globalDoc = await db.findOne({ collection: "__global_preferences", id: key });
3866
3822
  return globalDoc ? globalDoc.value : null;
@@ -3870,8 +3826,7 @@ function registerRoutes(app, config) {
3870
3826
  return c.json({ key, value: globalValue2 });
3871
3827
  }
3872
3828
  const doc = await db.findOne({ collection: user.collection, id: user.sub });
3873
- if (!doc)
3874
- return c.json({ error: true, message: "User not found." }, 404);
3829
+ if (!doc) return c.json({ error: true, message: "User not found." }, 404);
3875
3830
  const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
3876
3831
  if (key in preferences) {
3877
3832
  return c.json({ key, value: preferences[key] ?? null });
@@ -3884,12 +3839,9 @@ function registerRoutes(app, config) {
3884
3839
  const user = c.get("user");
3885
3840
  const key = c.req.param("key");
3886
3841
  const scope = c.req.query("scope");
3887
- if (!db)
3888
- return c.json({ message: "Database not configured" }, 500);
3889
- if (!user?.collection || !user.sub)
3890
- return c.json({ error: true, message: "Authentication required." }, 401);
3891
- if (!key)
3892
- return c.json({ error: true, message: "Preference key is required." }, 400);
3842
+ if (!db) return c.json({ message: "Database not configured" }, 500);
3843
+ if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
3844
+ if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
3893
3845
  const body = await c.req.json().catch(() => ({}));
3894
3846
  if (scope === "global") {
3895
3847
  const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
@@ -3912,8 +3864,7 @@ function registerRoutes(app, config) {
3912
3864
  return c.json({ key, value: body.value });
3913
3865
  }
3914
3866
  const doc = await db.findOne({ collection: user.collection, id: user.sub });
3915
- if (!doc)
3916
- return c.json({ error: true, message: "User not found." }, 404);
3867
+ if (!doc) return c.json({ error: true, message: "User not found." }, 404);
3917
3868
  const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? doc.__preferences : {};
3918
3869
  const nextPreferences = { ...preferences, [key]: body.value };
3919
3870
  await db.update({
@@ -3928,12 +3879,9 @@ function registerRoutes(app, config) {
3928
3879
  const user = c.get("user");
3929
3880
  const key = c.req.param("key");
3930
3881
  const scope = c.req.query("scope");
3931
- if (!db)
3932
- return c.json({ message: "Database not configured" }, 500);
3933
- if (!user?.collection || !user.sub)
3934
- return c.json({ error: true, message: "Authentication required." }, 401);
3935
- if (!key)
3936
- return c.json({ error: true, message: "Preference key is required." }, 400);
3882
+ if (!db) return c.json({ message: "Database not configured" }, 500);
3883
+ if (!user?.collection || !user.sub) return c.json({ error: true, message: "Authentication required." }, 401);
3884
+ if (!key) return c.json({ error: true, message: "Preference key is required." }, 400);
3937
3885
  if (scope === "global") {
3938
3886
  const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
3939
3887
  if (!isAdminUser) {
@@ -3943,8 +3891,7 @@ function registerRoutes(app, config) {
3943
3891
  return c.json({ success: true });
3944
3892
  }
3945
3893
  const doc = await db.findOne({ collection: user.collection, id: user.sub });
3946
- if (!doc)
3947
- return c.json({ error: true, message: "User not found." }, 404);
3894
+ if (!doc) return c.json({ error: true, message: "User not found." }, 404);
3948
3895
  const preferences = typeof doc.__preferences === "object" && doc.__preferences !== null ? { ...doc.__preferences } : {};
3949
3896
  delete preferences[key];
3950
3897
  await db.update({
@@ -3980,8 +3927,7 @@ function registerRoutes(app, config) {
3980
3927
  app.post("/api/admin/auth/:provider/exchange", (c) => adminAuthController.exchange(c));
3981
3928
  app.post("/api/admin/logout", (c) => adminAuthController.logout(c));
3982
3929
  for (const collection of config.collections) {
3983
- if (!collection.auth)
3984
- continue;
3930
+ if (!collection.auth) continue;
3985
3931
  const path = `/api/collections/${collection.slug}`;
3986
3932
  const authController = new AuthController(collection);
3987
3933
  app.post(`${path}/login`, (c) => authController.login(c));
@@ -4083,39 +4029,25 @@ function registerRoutes(app, config) {
4083
4029
  if (collection.auth && id) {
4084
4030
  const authController = new AuthController(collection);
4085
4031
  const method2 = c.req.method;
4086
- if (method2 === "POST" && id === "login")
4087
- return authController.login(c);
4088
- if (method2 === "POST" && id === "logout")
4089
- return authController.logout(c);
4090
- if (method2 === "GET" && id === "me")
4091
- return authController.me(c);
4092
- if (method2 === "POST" && id === "refresh-token")
4093
- return authController.refreshToken(c);
4094
- if (method2 === "POST" && id === "forgot-password")
4095
- return authController.forgotPassword(c);
4096
- if (method2 === "POST" && id === "reset-password")
4097
- return authController.resetPassword(c);
4032
+ if (method2 === "POST" && id === "login") return authController.login(c);
4033
+ if (method2 === "POST" && id === "logout") return authController.logout(c);
4034
+ if (method2 === "GET" && id === "me") return authController.me(c);
4035
+ if (method2 === "POST" && id === "refresh-token") return authController.refreshToken(c);
4036
+ if (method2 === "POST" && id === "forgot-password") return authController.forgotPassword(c);
4037
+ if (method2 === "POST" && id === "reset-password") return authController.resetPassword(c);
4098
4038
  }
4099
4039
  const controller = new CollectionController(collection);
4100
4040
  const method = c.req.method;
4101
4041
  if (id) {
4102
- if (method === "GET")
4103
- return controller.findOne(c);
4104
- if (method === "PATCH")
4105
- return controller.update(c);
4106
- if (method === "DELETE" && id === "delete-many")
4107
- return controller.deleteMany(c);
4108
- if (method === "DELETE")
4109
- return controller.delete(c);
4110
- if (method === "POST" && id === "media")
4111
- return controller.create(c);
4112
- if (method === "POST" && id === "seed")
4113
- return controller.seed(c);
4042
+ if (method === "GET") return controller.findOne(c);
4043
+ if (method === "PATCH") return controller.update(c);
4044
+ if (method === "DELETE" && id === "delete-many") return controller.deleteMany(c);
4045
+ if (method === "DELETE") return controller.delete(c);
4046
+ if (method === "POST" && id === "media") return controller.create(c);
4047
+ if (method === "POST" && id === "seed") return controller.seed(c);
4114
4048
  } else {
4115
- if (method === "GET")
4116
- return controller.find(c);
4117
- if (method === "POST")
4118
- return controller.create(c);
4049
+ if (method === "GET") return controller.find(c);
4050
+ if (method === "POST") return controller.create(c);
4119
4051
  }
4120
4052
  }
4121
4053
  }
@@ -4136,13 +4068,10 @@ function registerRoutes(app, config) {
4136
4068
  const controller = new GlobalController(global);
4137
4069
  const method = c.req.method;
4138
4070
  if (id) {
4139
- if (method === "POST" && id === "seed")
4140
- return controller.seed(c);
4071
+ if (method === "POST" && id === "seed") return controller.seed(c);
4141
4072
  } else {
4142
- if (method === "GET")
4143
- return controller.get(c);
4144
- if (method === "PATCH")
4145
- return controller.update(c);
4073
+ if (method === "GET") return controller.get(c);
4074
+ if (method === "PATCH") return controller.update(c);
4146
4075
  }
4147
4076
  }
4148
4077
  }
@@ -4150,7 +4079,7 @@ function registerRoutes(app, config) {
4150
4079
  });
4151
4080
  }
4152
4081
 
4153
- // src/utils/config.js
4082
+ // src/utils/config.ts
4154
4083
  var AUDIT_COLLECTION_SLUG = "__audit";
4155
4084
  var SYSTEM_FIELDS = [
4156
4085
  {
@@ -4364,8 +4293,7 @@ function normalizeConfig(config) {
4364
4293
  });
4365
4294
  const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
4366
4295
  const systemCollections = [];
4367
- if (needsAudit && !hasAuditCollection)
4368
- systemCollections.push(AUDIT_COLLECTION);
4296
+ if (needsAudit && !hasAuditCollection) systemCollections.push(AUDIT_COLLECTION);
4369
4297
  if (needsWorkflow && !normalizedCollections.some((col) => col.slug === WORKFLOW_HISTORY_COLLECTION)) {
4370
4298
  systemCollections.push(WORKFLOW_HISTORY_COLLECTION_CONFIG);
4371
4299
  }
@@ -4379,7 +4307,7 @@ function normalizeConfig(config) {
4379
4307
  };
4380
4308
  }
4381
4309
 
4382
- // src/app.js
4310
+ // src/app.ts
4383
4311
  async function createDyrectedApp(rawConfig) {
4384
4312
  const config = normalizeConfig(rawConfig);
4385
4313
  const app = new import_hono.Hono();