@dyrected/core 2.5.14 → 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-B2tg7Djj.d.cts +1575 -0
- package/dist/app-B2tg7Djj.d.ts +1575 -0
- package/dist/app-Bh4_Opv0.d.cts +1522 -0
- package/dist/app-Bh4_Opv0.d.ts +1522 -0
- package/dist/app-Bv9gaDAN.d.cts +561 -0
- package/dist/app-Bv9gaDAN.d.ts +561 -0
- package/dist/app-BvG3bRc8.d.cts +419 -0
- package/dist/app-BvG3bRc8.d.ts +419 -0
- package/dist/app-C3B9N1KR.d.cts +1522 -0
- package/dist/app-C3B9N1KR.d.ts +1522 -0
- package/dist/app-DDJJa0ep.d.cts +1621 -0
- package/dist/app-DDJJa0ep.d.ts +1621 -0
- package/dist/app-DO1s9YW1.d.cts +1621 -0
- package/dist/app-DO1s9YW1.d.ts +1621 -0
- package/dist/app-DTP3-9PJ.d.cts +561 -0
- package/dist/app-DTP3-9PJ.d.ts +561 -0
- package/dist/app-DbKDGYTI.d.cts +566 -0
- package/dist/app-DbKDGYTI.d.ts +566 -0
- package/dist/app-DqRO-CMi.d.cts +1457 -0
- package/dist/app-DqRO-CMi.d.ts +1457 -0
- package/dist/app-DvaFpOtj.d.cts +398 -0
- package/dist/app-DvaFpOtj.d.ts +398 -0
- package/dist/app-FGzip4XM.d.cts +1563 -0
- package/dist/app-FGzip4XM.d.ts +1563 -0
- package/dist/app-T0alZAE0.d.cts +383 -0
- package/dist/app-T0alZAE0.d.ts +383 -0
- package/dist/app-oQt5-9MU.d.cts +1560 -0
- package/dist/app-oQt5-9MU.d.ts +1560 -0
- package/dist/app-rZj1VFer.d.cts +1621 -0
- package/dist/app-rZj1VFer.d.ts +1621 -0
- package/dist/app-wo82JRHl.d.cts +445 -0
- package/dist/app-wo82JRHl.d.ts +445 -0
- package/dist/chunk-23URSKPI.js +2371 -0
- package/dist/chunk-2JMA3M5S.js +2475 -0
- package/dist/chunk-3FZEUK36.js +2470 -0
- package/dist/chunk-DOJHZ7XN.js +2394 -0
- package/dist/chunk-PKNFV7KE.js +2469 -0
- package/dist/chunk-UBTRANFX.js +2476 -0
- package/dist/chunk-W6KURRMW.js +2471 -0
- package/dist/index.cjs +457 -48
- package/dist/index.d.cts +117 -8
- package/dist/index.d.ts +117 -8
- package/dist/index.js +9 -3
- package/dist/server.cjs +449 -46
- package/dist/server.d.cts +57 -15
- package/dist/server.d.ts +57 -15
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,11 +34,14 @@ __export(index_exports, {
|
|
|
34
34
|
defineCollection: () => defineCollection,
|
|
35
35
|
defineConfig: () => defineConfig,
|
|
36
36
|
defineGlobal: () => defineGlobal,
|
|
37
|
+
executeFieldAfterRead: () => executeFieldAfterRead,
|
|
38
|
+
executeFieldBeforeChange: () => executeFieldBeforeChange,
|
|
37
39
|
generateAIPrompt: () => generateAIPrompt,
|
|
38
40
|
generateFreshSetupPrompt: () => generateFreshSetupPrompt,
|
|
39
41
|
normalizeConfig: () => normalizeConfig,
|
|
40
42
|
parseMongoWhere: () => parseMongoWhere,
|
|
41
|
-
parseSqlWhere: () => parseSqlWhere
|
|
43
|
+
parseSqlWhere: () => parseSqlWhere,
|
|
44
|
+
runCollectionHooks: () => runCollectionHooks
|
|
42
45
|
});
|
|
43
46
|
module.exports = __toCommonJS(index_exports);
|
|
44
47
|
|
|
@@ -1166,6 +1169,150 @@ function parseMongoWhere(where) {
|
|
|
1166
1169
|
return buildClause(where);
|
|
1167
1170
|
}
|
|
1168
1171
|
|
|
1172
|
+
// src/utils/hooks.ts
|
|
1173
|
+
async function runCollectionHooks(hooks, args) {
|
|
1174
|
+
if (!hooks || hooks.length === 0) {
|
|
1175
|
+
return args.data ?? args.doc ?? void 0;
|
|
1176
|
+
}
|
|
1177
|
+
let currentPayload = args.data ?? args.doc ?? void 0;
|
|
1178
|
+
for (const hook of hooks) {
|
|
1179
|
+
const result = await hook({
|
|
1180
|
+
...args,
|
|
1181
|
+
data: args.data !== void 0 ? currentPayload : void 0,
|
|
1182
|
+
doc: args.doc !== void 0 ? currentPayload : void 0
|
|
1183
|
+
});
|
|
1184
|
+
if (result !== void 0) {
|
|
1185
|
+
currentPayload = result;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return currentPayload;
|
|
1189
|
+
}
|
|
1190
|
+
async function executeFieldBeforeChange(fields, data, originalDoc, user) {
|
|
1191
|
+
if (!data || typeof data !== "object") return data;
|
|
1192
|
+
const result = { ...data };
|
|
1193
|
+
for (const field of fields) {
|
|
1194
|
+
if (!field.name) continue;
|
|
1195
|
+
const value = result[field.name];
|
|
1196
|
+
const origValue = originalDoc?.[field.name];
|
|
1197
|
+
let updatedValue = value;
|
|
1198
|
+
if (field.hooks?.beforeChange) {
|
|
1199
|
+
for (const hook of field.hooks.beforeChange) {
|
|
1200
|
+
updatedValue = await hook({
|
|
1201
|
+
value: updatedValue,
|
|
1202
|
+
originalDoc: originalDoc ?? void 0,
|
|
1203
|
+
data: result,
|
|
1204
|
+
user
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
result[field.name] = updatedValue;
|
|
1208
|
+
}
|
|
1209
|
+
if (updatedValue !== void 0 && updatedValue !== null) {
|
|
1210
|
+
if (field.type === "object" && field.fields) {
|
|
1211
|
+
result[field.name] = await executeFieldBeforeChange(
|
|
1212
|
+
field.fields,
|
|
1213
|
+
updatedValue,
|
|
1214
|
+
origValue,
|
|
1215
|
+
user
|
|
1216
|
+
);
|
|
1217
|
+
} else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
|
|
1218
|
+
const arrayResult = [];
|
|
1219
|
+
for (let i = 0; i < updatedValue.length; i++) {
|
|
1220
|
+
const item = updatedValue[i];
|
|
1221
|
+
const origItem = Array.isArray(origValue) ? origValue[i] : null;
|
|
1222
|
+
arrayResult.push(
|
|
1223
|
+
await executeFieldBeforeChange(field.fields, item, origItem, user)
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
result[field.name] = arrayResult;
|
|
1227
|
+
} else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
|
|
1228
|
+
const blocksResult = [];
|
|
1229
|
+
for (let i = 0; i < updatedValue.length; i++) {
|
|
1230
|
+
const blockData = updatedValue[i];
|
|
1231
|
+
const origBlock = Array.isArray(origValue) ? origValue[i] : null;
|
|
1232
|
+
const blockConfig = field.blocks.find(
|
|
1233
|
+
(b) => b.slug === blockData.blockType
|
|
1234
|
+
);
|
|
1235
|
+
if (blockConfig) {
|
|
1236
|
+
blocksResult.push(
|
|
1237
|
+
await executeFieldBeforeChange(
|
|
1238
|
+
blockConfig.fields,
|
|
1239
|
+
blockData,
|
|
1240
|
+
origBlock,
|
|
1241
|
+
user
|
|
1242
|
+
)
|
|
1243
|
+
);
|
|
1244
|
+
} else {
|
|
1245
|
+
blocksResult.push(blockData);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
result[field.name] = blocksResult;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return result;
|
|
1253
|
+
}
|
|
1254
|
+
async function executeFieldAfterRead(fields, doc, user) {
|
|
1255
|
+
if (!doc || typeof doc !== "object") return doc;
|
|
1256
|
+
const result = { ...doc };
|
|
1257
|
+
for (const field of fields) {
|
|
1258
|
+
if (!field.name) continue;
|
|
1259
|
+
const value = result[field.name];
|
|
1260
|
+
let updatedValue = value;
|
|
1261
|
+
if (field.hooks?.afterRead) {
|
|
1262
|
+
for (const hook of field.hooks.afterRead) {
|
|
1263
|
+
updatedValue = await hook({
|
|
1264
|
+
value: updatedValue,
|
|
1265
|
+
doc: result,
|
|
1266
|
+
user
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
result[field.name] = updatedValue;
|
|
1270
|
+
}
|
|
1271
|
+
if (updatedValue !== void 0 && updatedValue !== null) {
|
|
1272
|
+
if (field.type === "object" && field.fields) {
|
|
1273
|
+
result[field.name] = await executeFieldAfterRead(
|
|
1274
|
+
field.fields,
|
|
1275
|
+
updatedValue,
|
|
1276
|
+
user
|
|
1277
|
+
);
|
|
1278
|
+
} else if (field.type === "array" && field.fields && Array.isArray(updatedValue)) {
|
|
1279
|
+
const arrayResult = [];
|
|
1280
|
+
for (const item of updatedValue) {
|
|
1281
|
+
arrayResult.push(
|
|
1282
|
+
await executeFieldAfterRead(
|
|
1283
|
+
field.fields,
|
|
1284
|
+
item,
|
|
1285
|
+
user
|
|
1286
|
+
)
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
result[field.name] = arrayResult;
|
|
1290
|
+
} else if (field.type === "blocks" && field.blocks && Array.isArray(updatedValue)) {
|
|
1291
|
+
const blocksResult = [];
|
|
1292
|
+
for (const blockData of updatedValue) {
|
|
1293
|
+
const typedBlock = blockData;
|
|
1294
|
+
const blockConfig = field.blocks.find(
|
|
1295
|
+
(b) => b.slug === typedBlock.blockType
|
|
1296
|
+
);
|
|
1297
|
+
if (blockConfig) {
|
|
1298
|
+
blocksResult.push(
|
|
1299
|
+
await executeFieldAfterRead(
|
|
1300
|
+
blockConfig.fields,
|
|
1301
|
+
typedBlock,
|
|
1302
|
+
user
|
|
1303
|
+
)
|
|
1304
|
+
);
|
|
1305
|
+
} else {
|
|
1306
|
+
blocksResult.push(blockData);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
result[field.name] = blocksResult;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
return result;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1169
1316
|
// src/app.ts
|
|
1170
1317
|
var import_hono = require("hono");
|
|
1171
1318
|
var import_cors = require("hono/cors");
|
|
@@ -1408,6 +1555,7 @@ var CollectionController = class {
|
|
|
1408
1555
|
const page = Number(c.req.query("page")) || 1;
|
|
1409
1556
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 2;
|
|
1410
1557
|
const sort = c.req.query("sort") || void 0;
|
|
1558
|
+
const user = c.get("user");
|
|
1411
1559
|
let where = void 0;
|
|
1412
1560
|
const whereRaw = c.req.query("where");
|
|
1413
1561
|
if (whereRaw) {
|
|
@@ -1416,6 +1564,14 @@ var CollectionController = class {
|
|
|
1416
1564
|
} catch {
|
|
1417
1565
|
}
|
|
1418
1566
|
}
|
|
1567
|
+
const beforeReadResult = await runCollectionHooks(this.collection.hooks?.beforeRead, {
|
|
1568
|
+
req: c.req,
|
|
1569
|
+
query: where,
|
|
1570
|
+
user
|
|
1571
|
+
});
|
|
1572
|
+
if (beforeReadResult !== void 0) {
|
|
1573
|
+
where = beforeReadResult;
|
|
1574
|
+
}
|
|
1419
1575
|
let result = await db.find({
|
|
1420
1576
|
collection: this.collection.slug,
|
|
1421
1577
|
limit,
|
|
@@ -1437,6 +1593,17 @@ var CollectionController = class {
|
|
|
1437
1593
|
});
|
|
1438
1594
|
}
|
|
1439
1595
|
result.docs = result.docs.map((doc) => DefaultsService.apply(this.collection.fields, doc));
|
|
1596
|
+
const processedDocs = [];
|
|
1597
|
+
for (const doc of result.docs) {
|
|
1598
|
+
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1599
|
+
doc,
|
|
1600
|
+
req: c.req,
|
|
1601
|
+
user
|
|
1602
|
+
});
|
|
1603
|
+
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user);
|
|
1604
|
+
processedDocs.push(docWithFieldHooks);
|
|
1605
|
+
}
|
|
1606
|
+
result.docs = processedDocs;
|
|
1440
1607
|
if (depth > 0) {
|
|
1441
1608
|
const populationService = new PopulationService(db, config.collections);
|
|
1442
1609
|
result = await populationService.populateResult(result, this.collection.fields, depth);
|
|
@@ -1449,21 +1616,28 @@ var CollectionController = class {
|
|
|
1449
1616
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1450
1617
|
const id = c.req.param("id");
|
|
1451
1618
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
1619
|
+
const user = c.get("user");
|
|
1452
1620
|
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1453
1621
|
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1454
1622
|
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1455
1623
|
const docWithDefaults = DefaultsService.apply(this.collection.fields, doc);
|
|
1456
|
-
|
|
1624
|
+
const docWithCollectionHooks = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1625
|
+
doc: docWithDefaults,
|
|
1626
|
+
req: c.req,
|
|
1627
|
+
user
|
|
1628
|
+
});
|
|
1629
|
+
const docWithFieldHooks = await executeFieldAfterRead(this.collection.fields, docWithCollectionHooks, user);
|
|
1630
|
+
if (depth > 0 && docWithFieldHooks) {
|
|
1457
1631
|
const populationService = new PopulationService(db, config.collections);
|
|
1458
1632
|
const populatedDoc = await populationService.populate({
|
|
1459
|
-
data:
|
|
1633
|
+
data: docWithFieldHooks,
|
|
1460
1634
|
fields: this.collection.fields,
|
|
1461
1635
|
currentDepth: 0,
|
|
1462
1636
|
maxDepth: depth
|
|
1463
1637
|
});
|
|
1464
1638
|
return c.json(populatedDoc);
|
|
1465
1639
|
}
|
|
1466
|
-
return c.json(
|
|
1640
|
+
return c.json(docWithFieldHooks);
|
|
1467
1641
|
}
|
|
1468
1642
|
async create(c) {
|
|
1469
1643
|
const config = c.get("config");
|
|
@@ -1476,7 +1650,7 @@ var CollectionController = class {
|
|
|
1476
1650
|
const body = await c.req.json();
|
|
1477
1651
|
const user = c.get("user");
|
|
1478
1652
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1479
|
-
|
|
1653
|
+
let data = {
|
|
1480
1654
|
...body,
|
|
1481
1655
|
createdAt: now,
|
|
1482
1656
|
updatedAt: now,
|
|
@@ -1486,6 +1660,13 @@ var CollectionController = class {
|
|
|
1486
1660
|
if (this.collection.auth && data.password) {
|
|
1487
1661
|
data.password = await hashPassword(data.password);
|
|
1488
1662
|
}
|
|
1663
|
+
data = await executeFieldBeforeChange(this.collection.fields, data, null, user);
|
|
1664
|
+
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1665
|
+
data,
|
|
1666
|
+
req: c.req,
|
|
1667
|
+
user,
|
|
1668
|
+
operation: "create"
|
|
1669
|
+
});
|
|
1489
1670
|
const doc = await db.create({ collection: this.collection.slug, data });
|
|
1490
1671
|
if (this.collection.audit && db) {
|
|
1491
1672
|
AuditService.log(db, {
|
|
@@ -1497,7 +1678,19 @@ var CollectionController = class {
|
|
|
1497
1678
|
after: doc
|
|
1498
1679
|
});
|
|
1499
1680
|
}
|
|
1500
|
-
|
|
1681
|
+
await runCollectionHooks(this.collection.hooks?.afterChange, {
|
|
1682
|
+
doc,
|
|
1683
|
+
user,
|
|
1684
|
+
req: c.req,
|
|
1685
|
+
operation: "create"
|
|
1686
|
+
});
|
|
1687
|
+
const readDoc = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1688
|
+
doc,
|
|
1689
|
+
req: c.req,
|
|
1690
|
+
user
|
|
1691
|
+
});
|
|
1692
|
+
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user);
|
|
1693
|
+
return c.json(finalDoc, 201);
|
|
1501
1694
|
}
|
|
1502
1695
|
async upload(c) {
|
|
1503
1696
|
const config = c.get("config");
|
|
@@ -1524,18 +1717,38 @@ var CollectionController = class {
|
|
|
1524
1717
|
});
|
|
1525
1718
|
const user = c.get("user");
|
|
1526
1719
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1720
|
+
let data = {
|
|
1721
|
+
...otherData,
|
|
1722
|
+
...fileData,
|
|
1723
|
+
createdAt: now,
|
|
1724
|
+
updatedAt: now,
|
|
1725
|
+
createdBy: user?.sub ?? null,
|
|
1726
|
+
updatedBy: user?.sub ?? null
|
|
1727
|
+
};
|
|
1728
|
+
data = await executeFieldBeforeChange(this.collection.fields, data, null, user);
|
|
1729
|
+
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1730
|
+
data,
|
|
1731
|
+
req: c.req,
|
|
1732
|
+
user,
|
|
1733
|
+
operation: "create"
|
|
1734
|
+
});
|
|
1527
1735
|
const doc = await config.db.create({
|
|
1528
1736
|
collection: this.collection.slug,
|
|
1529
|
-
data
|
|
1530
|
-
...otherData,
|
|
1531
|
-
...fileData,
|
|
1532
|
-
createdAt: now,
|
|
1533
|
-
updatedAt: now,
|
|
1534
|
-
createdBy: user?.sub ?? null,
|
|
1535
|
-
updatedBy: user?.sub ?? null
|
|
1536
|
-
}
|
|
1737
|
+
data
|
|
1537
1738
|
});
|
|
1538
|
-
|
|
1739
|
+
await runCollectionHooks(this.collection.hooks?.afterChange, {
|
|
1740
|
+
doc,
|
|
1741
|
+
user,
|
|
1742
|
+
req: c.req,
|
|
1743
|
+
operation: "create"
|
|
1744
|
+
});
|
|
1745
|
+
const readDoc = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1746
|
+
doc,
|
|
1747
|
+
req: c.req,
|
|
1748
|
+
user
|
|
1749
|
+
});
|
|
1750
|
+
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user);
|
|
1751
|
+
return c.json(finalDoc, 201);
|
|
1539
1752
|
}
|
|
1540
1753
|
async update(c) {
|
|
1541
1754
|
const config = c.get("config");
|
|
@@ -1545,7 +1758,7 @@ var CollectionController = class {
|
|
|
1545
1758
|
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1546
1759
|
const body = await c.req.json();
|
|
1547
1760
|
const user = c.get("user");
|
|
1548
|
-
|
|
1761
|
+
let data = { ...body };
|
|
1549
1762
|
if (this.collection.auth) {
|
|
1550
1763
|
delete data.password;
|
|
1551
1764
|
delete data.oldPassword;
|
|
@@ -1555,10 +1768,20 @@ var CollectionController = class {
|
|
|
1555
1768
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1556
1769
|
updatedBy: user?.sub ?? null
|
|
1557
1770
|
});
|
|
1771
|
+
const originalDoc = await db.findOne({ collection: this.collection.slug, id });
|
|
1772
|
+
if (!originalDoc) return c.json({ message: "Not Found" }, 404);
|
|
1558
1773
|
let before = null;
|
|
1559
1774
|
if (this.collection.audit) {
|
|
1560
|
-
before =
|
|
1561
|
-
}
|
|
1775
|
+
before = originalDoc;
|
|
1776
|
+
}
|
|
1777
|
+
data = await executeFieldBeforeChange(this.collection.fields, data, originalDoc, user);
|
|
1778
|
+
data = await runCollectionHooks(this.collection.hooks?.beforeChange, {
|
|
1779
|
+
data,
|
|
1780
|
+
doc: originalDoc,
|
|
1781
|
+
req: c.req,
|
|
1782
|
+
user,
|
|
1783
|
+
operation: "update"
|
|
1784
|
+
});
|
|
1562
1785
|
const doc = await db.update({ collection: this.collection.slug, id, data });
|
|
1563
1786
|
if (this.collection.audit && db) {
|
|
1564
1787
|
AuditService.log(db, {
|
|
@@ -1570,7 +1793,20 @@ var CollectionController = class {
|
|
|
1570
1793
|
after: doc
|
|
1571
1794
|
});
|
|
1572
1795
|
}
|
|
1573
|
-
|
|
1796
|
+
await runCollectionHooks(this.collection.hooks?.afterChange, {
|
|
1797
|
+
doc,
|
|
1798
|
+
previousDoc: originalDoc,
|
|
1799
|
+
user,
|
|
1800
|
+
req: c.req,
|
|
1801
|
+
operation: "update"
|
|
1802
|
+
});
|
|
1803
|
+
const readDoc = await runCollectionHooks(this.collection.hooks?.afterRead, {
|
|
1804
|
+
doc,
|
|
1805
|
+
req: c.req,
|
|
1806
|
+
user
|
|
1807
|
+
});
|
|
1808
|
+
const finalDoc = await executeFieldAfterRead(this.collection.fields, readDoc, user);
|
|
1809
|
+
return c.json(finalDoc);
|
|
1574
1810
|
}
|
|
1575
1811
|
/**
|
|
1576
1812
|
* POST /api/collections/:slug/:id/change-password
|
|
@@ -1650,10 +1886,18 @@ var CollectionController = class {
|
|
|
1650
1886
|
const id = c.req.param("id");
|
|
1651
1887
|
if (!id) return c.json({ message: "Missing ID" }, 400);
|
|
1652
1888
|
const user = c.get("user");
|
|
1889
|
+
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1890
|
+
if (!doc) return c.json({ message: "Not Found" }, 404);
|
|
1653
1891
|
let before = null;
|
|
1654
1892
|
if (this.collection.audit) {
|
|
1655
|
-
before =
|
|
1893
|
+
before = doc;
|
|
1656
1894
|
}
|
|
1895
|
+
await runCollectionHooks(this.collection.hooks?.beforeDelete, {
|
|
1896
|
+
id,
|
|
1897
|
+
doc,
|
|
1898
|
+
user,
|
|
1899
|
+
req: c.req
|
|
1900
|
+
});
|
|
1657
1901
|
await db.delete({ collection: this.collection.slug, id });
|
|
1658
1902
|
if (this.collection.audit && db) {
|
|
1659
1903
|
AuditService.log(db, {
|
|
@@ -1665,6 +1909,12 @@ var CollectionController = class {
|
|
|
1665
1909
|
after: null
|
|
1666
1910
|
});
|
|
1667
1911
|
}
|
|
1912
|
+
await runCollectionHooks(this.collection.hooks?.afterDelete, {
|
|
1913
|
+
id,
|
|
1914
|
+
doc,
|
|
1915
|
+
user,
|
|
1916
|
+
req: c.req
|
|
1917
|
+
});
|
|
1668
1918
|
return c.json({ message: "Deleted" });
|
|
1669
1919
|
}
|
|
1670
1920
|
async deleteMany(c) {
|
|
@@ -1689,10 +1939,21 @@ var CollectionController = class {
|
|
|
1689
1939
|
const failed = [];
|
|
1690
1940
|
for (const id of ids) {
|
|
1691
1941
|
try {
|
|
1942
|
+
const doc = await db.findOne({ collection: this.collection.slug, id });
|
|
1943
|
+
if (!doc) {
|
|
1944
|
+
failed.push({ id, error: "Not Found" });
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1692
1947
|
let before = null;
|
|
1693
1948
|
if (this.collection.audit) {
|
|
1694
|
-
before =
|
|
1949
|
+
before = doc;
|
|
1695
1950
|
}
|
|
1951
|
+
await runCollectionHooks(this.collection.hooks?.beforeDelete, {
|
|
1952
|
+
id,
|
|
1953
|
+
doc,
|
|
1954
|
+
user,
|
|
1955
|
+
req: c.req
|
|
1956
|
+
});
|
|
1696
1957
|
await db.delete({ collection: this.collection.slug, id });
|
|
1697
1958
|
deleted.push(id);
|
|
1698
1959
|
if (this.collection.audit) {
|
|
@@ -1705,6 +1966,12 @@ var CollectionController = class {
|
|
|
1705
1966
|
after: null
|
|
1706
1967
|
});
|
|
1707
1968
|
}
|
|
1969
|
+
await runCollectionHooks(this.collection.hooks?.afterDelete, {
|
|
1970
|
+
id,
|
|
1971
|
+
doc,
|
|
1972
|
+
user,
|
|
1973
|
+
req: c.req
|
|
1974
|
+
});
|
|
1708
1975
|
} catch (err) {
|
|
1709
1976
|
failed.push({ id, error: err?.message ?? "Unknown error" });
|
|
1710
1977
|
}
|
|
@@ -1749,6 +2016,16 @@ var GlobalController = class {
|
|
|
1749
2016
|
const db = config.db;
|
|
1750
2017
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1751
2018
|
const depth = c.req.query("depth") !== void 0 ? Number(c.req.query("depth")) : 10;
|
|
2019
|
+
const user = c.get("user");
|
|
2020
|
+
let query = void 0;
|
|
2021
|
+
const beforeReadResult = await runCollectionHooks(this.global.hooks?.beforeRead, {
|
|
2022
|
+
req: c.req,
|
|
2023
|
+
query,
|
|
2024
|
+
user
|
|
2025
|
+
});
|
|
2026
|
+
if (beforeReadResult !== void 0) {
|
|
2027
|
+
query = beforeReadResult;
|
|
2028
|
+
}
|
|
1752
2029
|
let data = await db.getGlobal({ slug: this.global.slug });
|
|
1753
2030
|
const isEmpty = !data || Object.keys(data).length === 0;
|
|
1754
2031
|
if (isEmpty && this.global.initialData) {
|
|
@@ -1757,24 +2034,53 @@ var GlobalController = class {
|
|
|
1757
2034
|
data = this.global.initialData;
|
|
1758
2035
|
}
|
|
1759
2036
|
const dataWithDefaults = DefaultsService.apply(this.global.fields, data);
|
|
1760
|
-
|
|
2037
|
+
const docWithCollectionHooks = await runCollectionHooks(this.global.hooks?.afterRead, {
|
|
2038
|
+
doc: dataWithDefaults,
|
|
2039
|
+
req: c.req,
|
|
2040
|
+
user
|
|
2041
|
+
});
|
|
2042
|
+
const docWithFieldHooks = await executeFieldAfterRead(this.global.fields, docWithCollectionHooks, user);
|
|
2043
|
+
if (depth > 0 && docWithFieldHooks) {
|
|
1761
2044
|
const populationService = new PopulationService(db, config.collections);
|
|
1762
2045
|
const populatedData = await populationService.populate({
|
|
1763
|
-
data:
|
|
2046
|
+
data: docWithFieldHooks,
|
|
1764
2047
|
fields: this.global.fields,
|
|
1765
2048
|
currentDepth: 0,
|
|
1766
2049
|
maxDepth: depth
|
|
1767
2050
|
});
|
|
1768
2051
|
return c.json(populatedData);
|
|
1769
2052
|
}
|
|
1770
|
-
return c.json(
|
|
2053
|
+
return c.json(docWithFieldHooks);
|
|
1771
2054
|
}
|
|
1772
2055
|
async update(c) {
|
|
1773
2056
|
const db = c.get("config").db;
|
|
1774
2057
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
1775
2058
|
const body = await c.req.json();
|
|
1776
|
-
const
|
|
1777
|
-
|
|
2059
|
+
const user = c.get("user");
|
|
2060
|
+
const originalDoc = await db.getGlobal({ slug: this.global.slug }) || {};
|
|
2061
|
+
let data = await executeFieldBeforeChange(this.global.fields, body, originalDoc, user);
|
|
2062
|
+
data = await runCollectionHooks(this.global.hooks?.beforeChange, {
|
|
2063
|
+
data,
|
|
2064
|
+
doc: originalDoc,
|
|
2065
|
+
req: c.req,
|
|
2066
|
+
user,
|
|
2067
|
+
operation: "update"
|
|
2068
|
+
});
|
|
2069
|
+
const updated = await db.updateGlobal({ slug: this.global.slug, data });
|
|
2070
|
+
await runCollectionHooks(this.global.hooks?.afterChange, {
|
|
2071
|
+
doc: updated,
|
|
2072
|
+
previousDoc: originalDoc,
|
|
2073
|
+
user,
|
|
2074
|
+
req: c.req,
|
|
2075
|
+
operation: "update"
|
|
2076
|
+
});
|
|
2077
|
+
const readDoc = await runCollectionHooks(this.global.hooks?.afterRead, {
|
|
2078
|
+
doc: updated,
|
|
2079
|
+
req: c.req,
|
|
2080
|
+
user
|
|
2081
|
+
});
|
|
2082
|
+
const finalDoc = await executeFieldAfterRead(this.global.fields, readDoc, user);
|
|
2083
|
+
return c.json(finalDoc);
|
|
1778
2084
|
}
|
|
1779
2085
|
async seed(c) {
|
|
1780
2086
|
const config = c.get("config");
|
|
@@ -2683,12 +2989,13 @@ function fieldToSchema(field) {
|
|
|
2683
2989
|
schema = { type: "string", format: "date-time" };
|
|
2684
2990
|
break;
|
|
2685
2991
|
case "select":
|
|
2686
|
-
|
|
2992
|
+
case "radio":
|
|
2993
|
+
schema = { type: "string", enum: Array.isArray(field.options) ? field.options.map((o) => typeof o === "string" ? o : o.value) : void 0 };
|
|
2687
2994
|
break;
|
|
2688
2995
|
case "multiSelect":
|
|
2689
2996
|
schema = {
|
|
2690
2997
|
type: "array",
|
|
2691
|
-
items: { type: "string", enum: field.options
|
|
2998
|
+
items: { type: "string", enum: Array.isArray(field.options) ? field.options.map((o) => typeof o === "string" ? o : o.value) : void 0 }
|
|
2692
2999
|
};
|
|
2693
3000
|
break;
|
|
2694
3001
|
case "relationship":
|
|
@@ -2812,6 +3119,49 @@ function accessGate(target, action) {
|
|
|
2812
3119
|
await next();
|
|
2813
3120
|
};
|
|
2814
3121
|
}
|
|
3122
|
+
async function checkAccess(access, accessArgs) {
|
|
3123
|
+
if (access === void 0 || access === null) return true;
|
|
3124
|
+
if (typeof access === "function") {
|
|
3125
|
+
try {
|
|
3126
|
+
const result = await access(accessArgs);
|
|
3127
|
+
return typeof result === "boolean" ? result : !!result;
|
|
3128
|
+
} catch (err) {
|
|
3129
|
+
console.error("[dyrected/core] Functional access check failed:", err);
|
|
3130
|
+
return false;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
if (typeof access === "string" || typeof access === "boolean") {
|
|
3134
|
+
return evaluateAccess(access, accessArgs);
|
|
3135
|
+
}
|
|
3136
|
+
return true;
|
|
3137
|
+
}
|
|
3138
|
+
function serializeFieldForApi(f) {
|
|
3139
|
+
if (!f) return f;
|
|
3140
|
+
const serialized = { ...f };
|
|
3141
|
+
if (serialized.admin?.hooks) {
|
|
3142
|
+
const hooks = { ...serialized.admin.hooks };
|
|
3143
|
+
if (typeof hooks.onChange === "function") {
|
|
3144
|
+
hooks.onChange = hooks.onChange.toString();
|
|
3145
|
+
}
|
|
3146
|
+
if (typeof hooks.options === "function") {
|
|
3147
|
+
hooks.options = hooks.options.toString();
|
|
3148
|
+
}
|
|
3149
|
+
serialized.admin = { ...serialized.admin, hooks };
|
|
3150
|
+
}
|
|
3151
|
+
if (typeof serialized.options === "function" || serialized.options && typeof serialized.options === "object" && "resolve" in serialized.options) {
|
|
3152
|
+
serialized.options = { _dynamic: true };
|
|
3153
|
+
}
|
|
3154
|
+
if (serialized.fields) {
|
|
3155
|
+
serialized.fields = serialized.fields.map(serializeFieldForApi);
|
|
3156
|
+
}
|
|
3157
|
+
if (serialized.blocks) {
|
|
3158
|
+
serialized.blocks = serialized.blocks.map((b) => ({
|
|
3159
|
+
...b,
|
|
3160
|
+
fields: b.fields?.map(serializeFieldForApi)
|
|
3161
|
+
}));
|
|
3162
|
+
}
|
|
3163
|
+
return serialized;
|
|
3164
|
+
}
|
|
2815
3165
|
function registerRoutes(app, config) {
|
|
2816
3166
|
app.get("/api/schemas", optionalAuth(), async (c) => {
|
|
2817
3167
|
const siteId = c.req.header("X-Site-Id");
|
|
@@ -2827,26 +3177,10 @@ function registerRoutes(app, config) {
|
|
|
2827
3177
|
}
|
|
2828
3178
|
const user = c.get("user");
|
|
2829
3179
|
const accessArgs = { user, req: c.req, doc: null };
|
|
2830
|
-
const resolveAccess = async (access) => {
|
|
2831
|
-
if (access === void 0 || access === null) return true;
|
|
2832
|
-
if (typeof access === "function") {
|
|
2833
|
-
try {
|
|
2834
|
-
const result = await access(accessArgs);
|
|
2835
|
-
return typeof result === "boolean" ? result : !!result;
|
|
2836
|
-
} catch (err) {
|
|
2837
|
-
console.error("[dyrected/core] Functional access check failed:", err);
|
|
2838
|
-
return false;
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2841
|
-
if (typeof access === "string" || typeof access === "boolean") {
|
|
2842
|
-
return evaluateAccess(access, accessArgs);
|
|
2843
|
-
}
|
|
2844
|
-
return true;
|
|
2845
|
-
};
|
|
2846
3180
|
const serializeAccess = async (access) => {
|
|
2847
3181
|
if (typeof access === "string") return access;
|
|
2848
3182
|
if (typeof access === "boolean") return access;
|
|
2849
|
-
return
|
|
3183
|
+
return checkAccess(access, accessArgs);
|
|
2850
3184
|
};
|
|
2851
3185
|
const filteredCollections = await Promise.all(collections.filter((col) => !siteId || col.shared || !col.siteId || col.siteId === siteId).map(async (col) => ({
|
|
2852
3186
|
slug: col.slug,
|
|
@@ -2857,7 +3191,7 @@ function registerRoutes(app, config) {
|
|
|
2857
3191
|
update: await serializeAccess(col.access?.update),
|
|
2858
3192
|
delete: await serializeAccess(col.access?.delete)
|
|
2859
3193
|
},
|
|
2860
|
-
fields: await Promise.all(col.fields.map(async (f) => ({
|
|
3194
|
+
fields: await Promise.all(col.fields.map(serializeFieldForApi).map(async (f) => ({
|
|
2861
3195
|
name: f.name,
|
|
2862
3196
|
type: f.type,
|
|
2863
3197
|
label: f.label,
|
|
@@ -2885,7 +3219,7 @@ function registerRoutes(app, config) {
|
|
|
2885
3219
|
read: await serializeAccess(glb.access?.read),
|
|
2886
3220
|
update: await serializeAccess(glb.access?.update)
|
|
2887
3221
|
},
|
|
2888
|
-
fields: await Promise.all(glb.fields.map(async (f) => ({
|
|
3222
|
+
fields: await Promise.all(glb.fields.map(serializeFieldForApi).map(async (f) => ({
|
|
2889
3223
|
name: f.name,
|
|
2890
3224
|
type: f.type,
|
|
2891
3225
|
label: f.label,
|
|
@@ -2910,6 +3244,78 @@ function registerRoutes(app, config) {
|
|
|
2910
3244
|
admin: config.admin || {}
|
|
2911
3245
|
});
|
|
2912
3246
|
});
|
|
3247
|
+
app.get("/api/dyrected/options/:collection/:field", optionalAuth(), async (c) => {
|
|
3248
|
+
const { collection: colSlug, field: fieldName } = c.req.param();
|
|
3249
|
+
const siteId = c.req.header("X-Site-Id");
|
|
3250
|
+
let collections = [...config.collections];
|
|
3251
|
+
if (siteId && config.onSchemaFetch) {
|
|
3252
|
+
const dynamic = await config.onSchemaFetch(siteId);
|
|
3253
|
+
if (dynamic.collections) collections = [...collections, ...dynamic.collections];
|
|
3254
|
+
}
|
|
3255
|
+
const user = c.get("user");
|
|
3256
|
+
let collection = collections.find((col) => col.slug === colSlug);
|
|
3257
|
+
let field;
|
|
3258
|
+
if (collection) {
|
|
3259
|
+
const accessExpr = collection.access?.read;
|
|
3260
|
+
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
3261
|
+
const accessArgs = { user, req: c.req, doc: null };
|
|
3262
|
+
const allowed = await checkAccess(accessExpr, accessArgs);
|
|
3263
|
+
if (!allowed) {
|
|
3264
|
+
return c.json({ error: true, message: `Access denied: read on ${colSlug}` }, 403);
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
field = collection.fields.find((f) => f.name === fieldName);
|
|
3268
|
+
} else {
|
|
3269
|
+
let globals = [...config.globals];
|
|
3270
|
+
if (siteId && config.onSchemaFetch) {
|
|
3271
|
+
const dynamic = await config.onSchemaFetch(siteId);
|
|
3272
|
+
if (dynamic.globals) globals = [...globals, ...dynamic.globals];
|
|
3273
|
+
}
|
|
3274
|
+
const glb = globals.find((g) => g.slug === colSlug);
|
|
3275
|
+
if (!glb) {
|
|
3276
|
+
return c.json({ error: true, message: `${colSlug} not found as collection or global` }, 404);
|
|
3277
|
+
}
|
|
3278
|
+
const accessExpr = glb.access?.read;
|
|
3279
|
+
if (accessExpr !== void 0 && accessExpr !== null) {
|
|
3280
|
+
const accessArgs = { user, req: c.req, doc: null };
|
|
3281
|
+
const allowed = await checkAccess(accessExpr, accessArgs);
|
|
3282
|
+
if (!allowed) {
|
|
3283
|
+
return c.json({ error: true, message: `Access denied: read on global ${colSlug}` }, 403);
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
field = glb.fields.find((f) => f.name === fieldName);
|
|
3287
|
+
}
|
|
3288
|
+
if (!field) {
|
|
3289
|
+
return c.json({ error: true, message: `Field ${fieldName} not found in ${colSlug}` }, 404);
|
|
3290
|
+
}
|
|
3291
|
+
let resolver;
|
|
3292
|
+
if (typeof field.options === "function") {
|
|
3293
|
+
resolver = field.options;
|
|
3294
|
+
} else if (field.options && typeof field.options === "object" && "resolve" in field.options) {
|
|
3295
|
+
resolver = field.options.resolve;
|
|
3296
|
+
}
|
|
3297
|
+
if (!resolver) {
|
|
3298
|
+
return c.json({ error: true, message: `Field ${fieldName} in ${colSlug} is not dynamic` }, 400);
|
|
3299
|
+
}
|
|
3300
|
+
try {
|
|
3301
|
+
const db = c.get("db") || config.db;
|
|
3302
|
+
const queryParams = c.req.query();
|
|
3303
|
+
const reqContext = {
|
|
3304
|
+
query: queryParams,
|
|
3305
|
+
headers: c.req.header(),
|
|
3306
|
+
raw: c.req.raw
|
|
3307
|
+
};
|
|
3308
|
+
const result = await resolver({
|
|
3309
|
+
db,
|
|
3310
|
+
user,
|
|
3311
|
+
req: reqContext
|
|
3312
|
+
});
|
|
3313
|
+
return c.json(result);
|
|
3314
|
+
} catch (err) {
|
|
3315
|
+
console.error(`[dyrected/core] Failed to resolve dynamic options for field ${fieldName}:`, err);
|
|
3316
|
+
return c.json({ error: true, message: err.message || "Failed to resolve dynamic options" }, 500);
|
|
3317
|
+
}
|
|
3318
|
+
});
|
|
2913
3319
|
app.get("/api/openapi.json", (c) => {
|
|
2914
3320
|
return c.json(generateOpenApi(config));
|
|
2915
3321
|
});
|
|
@@ -3097,9 +3503,12 @@ function defineConfig(config) {
|
|
|
3097
3503
|
defineCollection,
|
|
3098
3504
|
defineConfig,
|
|
3099
3505
|
defineGlobal,
|
|
3506
|
+
executeFieldAfterRead,
|
|
3507
|
+
executeFieldBeforeChange,
|
|
3100
3508
|
generateAIPrompt,
|
|
3101
3509
|
generateFreshSetupPrompt,
|
|
3102
3510
|
normalizeConfig,
|
|
3103
3511
|
parseMongoWhere,
|
|
3104
|
-
parseSqlWhere
|
|
3512
|
+
parseSqlWhere,
|
|
3513
|
+
runCollectionHooks
|
|
3105
3514
|
});
|