@dyrected/core 2.5.24 → 2.5.25

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
@@ -109,7 +109,33 @@ var PopulationService = class {
109
109
  }
110
110
  const populatedDoc = { ...data };
111
111
  for (const field of fields) {
112
- if (field.type === "join") continue;
112
+ if (field.type === "join" && field.collection && field.on && currentDepth === 0) {
113
+ const targetCollection = this.collections.find((c) => c.slug === field.collection);
114
+ if (targetCollection) {
115
+ const docId = populatedDoc.id;
116
+ if (docId) {
117
+ const where = { [field.on]: { equals: docId } };
118
+ const joinLimit = field.limit ?? 10;
119
+ const result = await this.db.find({
120
+ collection: field.collection,
121
+ where,
122
+ limit: joinLimit
123
+ });
124
+ const populatedDocs = await this.populate({
125
+ data: result.docs.map((doc) => DefaultsService.apply(targetCollection.fields, doc)),
126
+ fields: targetCollection.fields,
127
+ currentDepth: 1,
128
+ maxDepth
129
+ });
130
+ populatedDoc[field.name] = {
131
+ docs: populatedDocs,
132
+ hasNextPage: result.page * result.limit < result.total,
133
+ totalDocs: result.total
134
+ };
135
+ }
136
+ }
137
+ continue;
138
+ }
113
139
  if (field.type === "row" && field.fields) {
114
140
  const rowPopulated = await this.populate({ data, fields: field.fields, currentDepth, maxDepth });
115
141
  Object.assign(populatedDoc, rowPopulated);
@@ -294,7 +320,7 @@ async function runCollectionHooks(hooks, args, options = {}) {
294
320
  }
295
321
  return currentPayload;
296
322
  }
297
- async function executeFieldBeforeChange(fields, data, originalDoc, user) {
323
+ async function executeFieldBeforeChange(fields, data, originalDoc, user, db) {
298
324
  if (!data || typeof data !== "object") return data;
299
325
  const result = { ...data };
300
326
  for (const field of fields) {
@@ -308,7 +334,8 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user) {
308
334
  value: updatedValue,
309
335
  originalDoc: originalDoc ?? void 0,
310
336
  data: result,
311
- user
337
+ user,
338
+ db
312
339
  });
313
340
  }
314
341
  result[field.name] = updatedValue;
@@ -319,7 +346,8 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user) {
319
346
  field.fields,
320
347
  updatedValue,
321
348
  origValue,
322
- user
349
+ user,
350
+ db
323
351
  );
324
352
  } else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
325
353
  const arrayResult = [];
@@ -327,7 +355,7 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user) {
327
355
  const item = updatedValue[i];
328
356
  const origItem = Array.isArray(origValue) ? origValue[i] : null;
329
357
  arrayResult.push(
330
- await executeFieldBeforeChange(field.fields, item, origItem, user)
358
+ await executeFieldBeforeChange(field.fields, item, origItem, user, db)
331
359
  );
332
360
  }
333
361
  result[field.name] = arrayResult;
@@ -345,7 +373,8 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user) {
345
373
  blockConfig.fields,
346
374
  blockData,
347
375
  origBlock,
348
- user
376
+ user,
377
+ db
349
378
  )
350
379
  );
351
380
  } else {
@@ -358,7 +387,7 @@ async function executeFieldBeforeChange(fields, data, originalDoc, user) {
358
387
  }
359
388
  return result;
360
389
  }
361
- async function executeFieldAfterRead(fields, doc, user) {
390
+ async function executeFieldAfterRead(fields, doc, user, db) {
362
391
  if (!doc || typeof doc !== "object") return doc;
363
392
  const result = { ...doc };
364
393
  for (const field of fields) {
@@ -370,7 +399,8 @@ async function executeFieldAfterRead(fields, doc, user) {
370
399
  updatedValue = await hook({
371
400
  value: updatedValue,
372
401
  doc: result,
373
- user
402
+ user,
403
+ db
374
404
  });
375
405
  }
376
406
  result[field.name] = updatedValue;
@@ -380,7 +410,8 @@ async function executeFieldAfterRead(fields, doc, user) {
380
410
  result[field.name] = await executeFieldAfterRead(
381
411
  field.fields,
382
412
  updatedValue,
383
- user
413
+ user,
414
+ db
384
415
  );
385
416
  } else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
386
417
  const arrayResult = [];
@@ -389,7 +420,8 @@ async function executeFieldAfterRead(fields, doc, user) {
389
420
  await executeFieldAfterRead(
390
421
  field.fields,
391
422
  item,
392
- user
423
+ user,
424
+ db
393
425
  )
394
426
  );
395
427
  }
@@ -406,7 +438,8 @@ async function executeFieldAfterRead(fields, doc, user) {
406
438
  await executeFieldAfterRead(
407
439
  blockConfig.fields,
408
440
  typedBlock,
409
- user
441
+ user,
442
+ db
410
443
  )
411
444
  );
412
445
  } else {
@@ -420,6 +453,23 @@ async function executeFieldAfterRead(fields, doc, user) {
420
453
  return result;
421
454
  }
422
455
 
456
+ // src/utils/readonly-db.ts
457
+ var WRITE_METHODS = ["create", "update", "delete", "updateGlobal", "execute"];
458
+ function createReadonlyDb(db) {
459
+ return new Proxy(db, {
460
+ get(target, prop) {
461
+ if (WRITE_METHODS.includes(prop)) {
462
+ return () => {
463
+ throw new Error(
464
+ `[dyrected] Write operation "${String(prop)}" is not allowed in this hook phase. Use afterChange/afterDelete hooks for write operations.`
465
+ );
466
+ };
467
+ }
468
+ return Reflect.get(target, prop);
469
+ }
470
+ });
471
+ }
472
+
423
473
  // src/controllers/collection.controller.ts
424
474
  var CollectionController = class {
425
475
  collection;
@@ -430,6 +480,7 @@ var CollectionController = class {
430
480
  const config = c.get("config");
431
481
  const db = config.db;
432
482
  if (!db) return c.json({ message: "Database not configured" }, 500);
483
+ const readonlyDb = createReadonlyDb(db);
433
484
  const limit = Number(c.req.query("limit")) || 10;
434
485
  const page = Number(c.req.query("page")) || 1;
435
486
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 2;
@@ -446,7 +497,8 @@ var CollectionController = class {
446
497
  const beforeReadResult = await runCollectionHooks(this.collection.hooks?.beforeRead, {
447
498
  req: c.req,
448
499
  query: where,
449
- user
500
+ user,
501
+ db: readonlyDb
450
502
  });
451
503
  if (beforeReadResult !== void 0) {
452
504
  where = beforeReadResult;
@@ -477,9 +529,10 @@ var CollectionController = class {
477
529
  const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
478
530
  doc,
479
531
  req: c.req,
480
- user
532
+ user,
533
+ db: readonlyDb
481
534
  });
482
- const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user);
535
+ const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
483
536
  processedDocs.push(docWithFieldHooks);
484
537
  }
485
538
  result.docs = processedDocs;
@@ -493,6 +546,7 @@ var CollectionController = class {
493
546
  const config = c.get("config");
494
547
  const db = config.db;
495
548
  if (!db) return c.json({ message: "Database not configured" }, 500);
549
+ const readonlyDb = createReadonlyDb(db);
496
550
  const id = c.req.param("id");
497
551
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
498
552
  const user = c.get("user");
@@ -503,9 +557,10 @@ var CollectionController = class {
503
557
  const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
504
558
  doc: docWithDefaults,
505
559
  req: c.req,
506
- user
560
+ user,
561
+ db: readonlyDb
507
562
  });
508
- const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user);
563
+ const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user, readonlyDb);
509
564
  if (depth > 0 && docWithFieldHooks) {
510
565
  const populationService = new PopulationService(db, config.collections);
511
566
  const populatedDoc = await populationService.populate({
@@ -522,6 +577,7 @@ var CollectionController = class {
522
577
  const config = c.get("config");
523
578
  const db = config.db;
524
579
  if (!db) return c.json({ message: "Database not configured" }, 500);
580
+ const readonlyDb = createReadonlyDb(db);
525
581
  const contentType = c.req.header("Content-Type") || "";
526
582
  if (contentType.toLowerCase().includes("multipart/form-data")) {
527
583
  return this.upload(c);
@@ -539,12 +595,13 @@ var CollectionController = class {
539
595
  if (this.collection.auth && data.password) {
540
596
  data.password = await hashPassword(data.password);
541
597
  }
542
- data = await executeFieldBeforeChange(this.collection.fields, data, null, user);
598
+ data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
543
599
  data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
544
600
  data,
545
601
  req: c.req,
546
602
  user,
547
- operation: "create"
603
+ operation: "create",
604
+ db: readonlyDb
548
605
  });
549
606
  const doc = await db.create({ collection: this.collection.slug, data });
550
607
  if (this.collection.audit && db) {
@@ -561,20 +618,25 @@ var CollectionController = class {
561
618
  doc,
562
619
  user,
563
620
  req: c.req,
564
- operation: "create"
621
+ operation: "create",
622
+ db
565
623
  }, { isolated: true });
566
624
  const readDoc = await runCollectionHooks(this.collection.hooks?.afterRead, {
567
625
  doc,
568
626
  req: c.req,
569
- user
627
+ user,
628
+ db: readonlyDb
570
629
  });
571
- const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user);
630
+ const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
572
631
  return c.json(finalDoc, 201);
573
632
  }
574
633
  async upload(c) {
575
634
  const config = c.get("config");
576
635
  const storage = config.storage;
577
636
  if (!storage) return c.json({ message: "Storage not configured" }, 500);
637
+ const db = config.db;
638
+ if (!db) return c.json({ message: "Database not configured" }, 500);
639
+ const readonlyDb = createReadonlyDb(db);
578
640
  const formData = await c.req.formData();
579
641
  const file = formData.get("file");
580
642
  if (!file) return c.json({ message: "No file uploaded" }, 400);
@@ -604,14 +666,15 @@ var CollectionController = class {
604
666
  createdBy: user?.sub ?? null,
605
667
  updatedBy: user?.sub ?? null
606
668
  };
607
- data = await executeFieldBeforeChange(this.collection.fields, data, null, user);
669
+ data = await executeFieldBeforeChange(this.collection.fields, data, null, user, readonlyDb);
608
670
  data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
609
671
  data,
610
672
  req: c.req,
611
673
  user,
612
- operation: "create"
674
+ operation: "create",
675
+ db: readonlyDb
613
676
  });
614
- const doc = await config.db.create({
677
+ const doc = await db.create({
615
678
  collection: this.collection.slug,
616
679
  data
617
680
  });
@@ -619,20 +682,23 @@ var CollectionController = class {
619
682
  doc,
620
683
  user,
621
684
  req: c.req,
622
- operation: "create"
685
+ operation: "create",
686
+ db
623
687
  }, { isolated: true });
624
688
  const readDoc = await runCollectionHooks(this.collection.hooks?.afterRead, {
625
689
  doc,
626
690
  req: c.req,
627
- user
691
+ user,
692
+ db: readonlyDb
628
693
  });
629
- const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user);
694
+ const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
630
695
  return c.json(finalDoc, 201);
631
696
  }
632
697
  async update(c) {
633
698
  const config = c.get("config");
634
699
  const db = config.db;
635
700
  if (!db) return c.json({ message: "Database not configured" }, 500);
701
+ const readonlyDb = createReadonlyDb(db);
636
702
  const id = c.req.param("id");
637
703
  if (!id) return c.json({ message: "Missing ID" }, 400);
638
704
  const body = await c.req.json();
@@ -653,13 +719,14 @@ var CollectionController = class {
653
719
  if (this.collection.audit) {
654
720
  before = originalDoc;
655
721
  }
656
- data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user);
722
+ data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user, readonlyDb);
657
723
  data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
658
724
  data,
659
725
  doc: originalDoc,
660
726
  req: c.req,
661
727
  user,
662
- operation: "update"
728
+ operation: "update",
729
+ db: readonlyDb
663
730
  });
664
731
  const doc = await db.update({ collection: this.collection.slug, id, data });
665
732
  if (this.collection.audit && db) {
@@ -677,14 +744,16 @@ var CollectionController = class {
677
744
  previousDoc: originalDoc,
678
745
  user,
679
746
  req: c.req,
680
- operation: "update"
747
+ operation: "update",
748
+ db
681
749
  }, { isolated: true });
682
750
  const readDoc = await runCollectionHooks(this.collection.hooks?.afterRead, {
683
751
  doc,
684
752
  req: c.req,
685
- user
753
+ user,
754
+ db: readonlyDb
686
755
  });
687
- const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user);
756
+ const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user, readonlyDb);
688
757
  return c.json(finalDoc);
689
758
  }
690
759
  /**
@@ -762,6 +831,7 @@ var CollectionController = class {
762
831
  const config = c.get("config");
763
832
  const db = config.db;
764
833
  if (!db) return c.json({ message: "Database not configured" }, 500);
834
+ const readonlyDb = createReadonlyDb(db);
765
835
  const id = c.req.param("id");
766
836
  if (!id) return c.json({ message: "Missing ID" }, 400);
767
837
  const user = c.get("user");
@@ -775,7 +845,8 @@ var CollectionController = class {
775
845
  id,
776
846
  doc,
777
847
  user,
778
- req: c.req
848
+ req: c.req,
849
+ db: readonlyDb
779
850
  });
780
851
  await db.delete({ collection: this.collection.slug, id });
781
852
  if (this.collection.audit && db) {
@@ -792,7 +863,8 @@ var CollectionController = class {
792
863
  id,
793
864
  doc,
794
865
  user,
795
- req: c.req
866
+ req: c.req,
867
+ db
796
868
  }, { isolated: true });
797
869
  return c.json({ message: "Deleted" });
798
870
  }
@@ -800,6 +872,7 @@ var CollectionController = class {
800
872
  const config = c.get("config");
801
873
  const db = config.db;
802
874
  if (!db) return c.json({ message: "Database not configured" }, 500);
875
+ const readonlyDb = createReadonlyDb(db);
803
876
  const user = c.get("user");
804
877
  let ids = [];
805
878
  try {
@@ -831,7 +904,8 @@ var CollectionController = class {
831
904
  id,
832
905
  doc,
833
906
  user,
834
- req: c.req
907
+ req: c.req,
908
+ db: readonlyDb
835
909
  });
836
910
  await db.delete({ collection: this.collection.slug, id });
837
911
  deleted.push(id);
@@ -849,7 +923,8 @@ var CollectionController = class {
849
923
  id,
850
924
  doc,
851
925
  user,
852
- req: c.req
926
+ req: c.req,
927
+ db
853
928
  }, { isolated: true });
854
929
  } catch (err) {
855
930
  failed.push({ id, error: err?.message ?? "Unknown error" });
@@ -894,13 +969,15 @@ var GlobalController = class {
894
969
  const config = c.get("config");
895
970
  const db = config.db;
896
971
  if (!db) return c.json({ message: "Database not configured" }, 500);
972
+ const readonlyDb = createReadonlyDb(db);
897
973
  const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
898
974
  const user = c.get("user");
899
975
  let query = void 0;
900
976
  const beforeReadResult = await runCollectionHooks(this.global.hooks?.beforeRead, {
901
977
  req: c.req,
902
978
  query,
903
- user
979
+ user,
980
+ db: readonlyDb
904
981
  });
905
982
  if (beforeReadResult !== void 0) {
906
983
  query = beforeReadResult;
@@ -916,9 +993,10 @@ var GlobalController = class {
916
993
  const docWithCollectionHooks = await runCollectionHooks(this.global.hooks?.afterRead, {
917
994
  doc: dataWithDefaults,
918
995
  req: c.req,
919
- user
996
+ user,
997
+ db: readonlyDb
920
998
  });
921
- const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user);
999
+ const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user, readonlyDb);
922
1000
  if (depth > 0 && docWithFieldHooks) {
923
1001
  const populationService = new PopulationService(db, config.collections);
924
1002
  const populatedData = await populationService.populate({
@@ -932,18 +1010,21 @@ var GlobalController = class {
932
1010
  return c.json(docWithFieldHooks);
933
1011
  }
934
1012
  async update(c) {
935
- const db = c.get("config").db;
1013
+ const config = c.get("config");
1014
+ const db = config.db;
936
1015
  if (!db) return c.json({ message: "Database not configured" }, 500);
1016
+ const readonlyDb = createReadonlyDb(db);
937
1017
  const body = await c.req.json();
938
1018
  const user = c.get("user");
939
1019
  const originalDoc = await db.getGlobal({ slug: this.global.slug }) || {};
940
- let data = await executeFieldBeforeChange(this.global.fields, body, originalDoc, user);
1020
+ let data = await executeFieldBeforeChange(this.global.fields, body, originalDoc, user, readonlyDb);
941
1021
  data = await runCollectionHooks(this.global.hooks?.beforeChange, {
942
1022
  data,
943
1023
  doc: originalDoc,
944
1024
  req: c.req,
945
1025
  user,
946
- operation: "update"
1026
+ operation: "update",
1027
+ db: readonlyDb
947
1028
  });
948
1029
  const updated = await db.updateGlobal({ slug: this.global.slug, data });
949
1030
  await runCollectionHooks(this.global.hooks?.afterChange, {
@@ -951,14 +1032,16 @@ var GlobalController = class {
951
1032
  previousDoc: originalDoc,
952
1033
  user,
953
1034
  req: c.req,
954
- operation: "update"
1035
+ operation: "update",
1036
+ db
955
1037
  }, { isolated: true });
956
1038
  const readDoc = await runCollectionHooks(this.global.hooks?.afterRead, {
957
1039
  doc: updated,
958
1040
  req: c.req,
959
- user
1041
+ user,
1042
+ db: readonlyDb
960
1043
  });
961
- const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user);
1044
+ const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user, readonlyDb);
962
1045
  return c.json(finalDoc);
963
1046
  }
964
1047
  async seed(c) {
@@ -2207,6 +2290,9 @@ function registerRoutes(app, config) {
2207
2290
  hasMany: f.hasMany,
2208
2291
  fields: f.fields,
2209
2292
  blocks: f.blocks,
2293
+ collection: f.collection,
2294
+ on: f.on,
2295
+ limit: f.limit,
2210
2296
  admin: f.admin,
2211
2297
  access: {
2212
2298
  read: await serializeAccess(f.access?.read),
@@ -2235,6 +2321,9 @@ function registerRoutes(app, config) {
2235
2321
  hasMany: f.hasMany,
2236
2322
  fields: f.fields,
2237
2323
  blocks: f.blocks,
2324
+ collection: f.collection,
2325
+ on: f.on,
2326
+ limit: f.limit,
2238
2327
  admin: f.admin,
2239
2328
  access: {
2240
2329
  read: await serializeAccess(f.access?.read),
package/dist/server.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { p as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-C-HgyliB.cjs';
2
- export { M as createDyrectedApp } from './app-C-HgyliB.cjs';
1
+ import { p as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-BibuoHQG.cjs';
2
+ export { M as createDyrectedApp } from './app-BibuoHQG.cjs';
3
3
  import * as hono from 'hono';
4
4
  import { Hono, Context } from 'hono';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { p as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-C-HgyliB.js';
2
- export { M as createDyrectedApp } from './app-C-HgyliB.js';
1
+ import { p as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-BibuoHQG.js';
2
+ export { M as createDyrectedApp } from './app-BibuoHQG.js';
3
3
  import * as hono from 'hono';
4
4
  import { Hono, Context } from 'hono';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
package/dist/server.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  PreviewController,
7
7
  createDyrectedApp,
8
8
  registerRoutes
9
- } from "./chunk-TUUHGLB5.js";
9
+ } from "./chunk-FDQYPPG3.js";
10
10
  export {
11
11
  AuthController,
12
12
  CollectionController,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/core",
3
- "version": "2.5.24",
3
+ "version": "2.5.25",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",