@kyro-cms/core 0.10.4 → 0.10.6
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/api-handler-graphql.cjs +1 -49
- package/dist/api-handler-graphql.js +1 -46
- package/dist/api-handler-trpc.cjs +1 -38
- package/dist/api-handler-trpc.js +1 -36
- package/dist/api-handler.cjs +1 -44
- package/dist/api-handler.js +1 -27
- package/dist/bootstrap-HIAV5ZFT.cjs +1 -0
- package/dist/bootstrap-P7QPQYQP.js +1 -0
- package/dist/chunk-2BTRHVT3.cjs +20 -0
- package/dist/chunk-2W4FK6CJ.js +122 -0
- package/dist/chunk-4ORJMSJU.cjs +1 -0
- package/dist/chunk-65G73T7Z.cjs +2 -0
- package/dist/chunk-7N6NKCRT.js +1 -0
- package/dist/chunk-7WOX27LK.js +259 -0
- package/dist/chunk-AE6GXUT7.js +1 -0
- package/dist/chunk-AJAZ7D3W.cjs +259 -0
- package/dist/chunk-ATLSIYRH.cjs +1 -0
- package/dist/chunk-AU6IWTBO.js +2 -0
- package/dist/chunk-CDL7ARXT.js +133 -0
- package/dist/chunk-CUTFS6N2.cjs +1 -0
- package/dist/chunk-CVVW4LXZ.cjs +1 -0
- package/dist/chunk-DGJUBN33.js +1 -0
- package/dist/chunk-DV4LFLPL.cjs +1 -0
- package/dist/chunk-EEWCANFX.js +1 -0
- package/dist/chunk-ELICKLFM.cjs +3 -0
- package/dist/chunk-EQUQJBPP.cjs +2 -0
- package/dist/chunk-ET6NN2V2.cjs +122 -0
- package/dist/chunk-F7P36SLX.cjs +74 -0
- package/dist/chunk-F7SR5IDT.cjs +1 -0
- package/dist/chunk-FO3HLBC7.js +1 -0
- package/dist/chunk-FS7GL2BR.js +73 -0
- package/dist/chunk-G6A7OSIG.js +2 -0
- package/dist/chunk-GFU7AOI3.cjs +1 -0
- package/dist/chunk-GYQMNXXW.cjs +114 -0
- package/dist/chunk-HLSB2MCK.js +1 -0
- package/dist/chunk-HVXO3OFB.cjs +1 -0
- package/dist/chunk-IFJJ44OQ.cjs +133 -0
- package/dist/chunk-ISWMETHJ.cjs +1 -0
- package/dist/chunk-K6WUOU6P.js +1 -0
- package/dist/chunk-KDNYHLD2.js +1 -0
- package/dist/chunk-KIW36TG7.cjs +86 -0
- package/dist/chunk-L6CZYN3L.cjs +20 -0
- package/dist/chunk-LRGAPGG3.js +74 -0
- package/dist/chunk-LZ6YFYQM.js +20 -0
- package/dist/chunk-MIOMMB23.cjs +1 -0
- package/dist/chunk-NGBJKZMT.js +90 -0
- package/dist/chunk-NRLIGDYF.js +1 -0
- package/dist/chunk-NY47CFVP.cjs +1 -0
- package/dist/chunk-NZ35PV5R.js +2 -0
- package/dist/chunk-OG7CNQGU.js +1 -0
- package/dist/chunk-OQD3WG6Z.cjs +1 -0
- package/dist/chunk-P7CESLDJ.js +1 -0
- package/dist/chunk-PP5S2UBV.js +0 -0
- package/dist/chunk-Q3Q7BJVQ.cjs +1 -0
- package/dist/chunk-Q4CSLXCO.cjs +90 -0
- package/dist/chunk-QRXDCV6K.js +0 -0
- package/dist/chunk-R7DVVXXI.cjs +73 -0
- package/dist/chunk-RHCWCG3O.js +114 -0
- package/dist/chunk-SL3LZQCF.js +1 -0
- package/dist/chunk-UBHFUFD4.cjs +1 -0
- package/dist/chunk-UBRYJP2G.js +1 -0
- package/dist/chunk-UDSJGML2.cjs +2 -0
- package/dist/chunk-UHFDH3PG.js +20 -0
- package/dist/chunk-UJNSOOYZ.cjs +1 -0
- package/dist/chunk-URCLFIME.js +86 -0
- package/dist/chunk-UVTS2IWM.js +1 -0
- package/dist/chunk-V65NPSR5.cjs +1 -0
- package/dist/chunk-WIZ6C5QZ.cjs +1 -0
- package/dist/chunk-ZAQOSRP6.js +1 -0
- package/dist/chunk-ZELCS7IC.js +3 -0
- package/dist/cli/index.cjs +35 -405
- package/dist/cli/index.js +35 -402
- package/dist/client.cjs +1 -55
- package/dist/client.js +1 -6
- package/dist/drizzle/index.cjs +1 -50
- package/dist/drizzle/index.d.cts +1 -2
- package/dist/drizzle/index.d.ts +1 -2
- package/dist/drizzle/index.js +1 -9
- package/dist/fields/index.cjs +1 -89
- package/dist/fields/index.js +1 -4
- package/dist/graphql/index.cjs +1 -31
- package/dist/graphql/index.d.cts +1 -1
- package/dist/graphql/index.d.ts +1 -1
- package/dist/graphql/index.js +1 -6
- package/dist/{index-jLs0zlft.d.ts → index-DXr33e0x.d.ts} +1 -1
- package/dist/{index-DHbywDs-.d.cts → index-DuuUzWBq.d.cts} +1 -1
- package/dist/index.cjs +29 -2464
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +29 -1929
- package/dist/integration.cjs +1 -10
- package/dist/integration.js +1 -4
- package/dist/media-NSSMOB7N.cjs +1 -0
- package/dist/media-RNMCZYPZ.js +1 -0
- package/dist/mongo-auth-adapter-JYJ4GBZE.cjs +1 -0
- package/dist/mongo-auth-adapter-XI7EPURZ.js +1 -0
- package/dist/mongodb/index.cjs +1 -29
- package/dist/mongodb/index.d.cts +1 -2
- package/dist/mongodb/index.d.ts +1 -2
- package/dist/mongodb/index.js +1 -8
- package/dist/postgres-auth-adapter-E7OGMDD4.cjs +1 -0
- package/dist/postgres-auth-adapter-F4KLOGAG.js +1 -0
- package/dist/redis-adapter-AQJVABYT.cjs +1 -0
- package/dist/redis-adapter-AUKOV74G.js +1 -0
- package/dist/rest/index.cjs +1 -31
- package/dist/rest/index.d.cts +1 -1
- package/dist/rest/index.d.ts +1 -1
- package/dist/rest/index.js +1 -18
- package/dist/schema-5XNROI7I.js +1 -0
- package/dist/schema-6O4JTJRU.cjs +1 -0
- package/dist/sqlite-adapter-5XFLNJDH.js +1 -0
- package/dist/sqlite-adapter-W53Q32N5.cjs +1 -0
- package/dist/templates/index.cjs +1 -207
- package/dist/templates/index.js +1 -6
- package/dist/trpc/index.cjs +1 -48
- package/dist/trpc/index.js +1 -7
- package/dist/ws/index.cjs +1 -25
- package/dist/ws/index.js +1 -4
- package/package.json +44 -13
- package/dist/api-handler-graphql.cjs.map +0 -1
- package/dist/api-handler-graphql.js.map +0 -1
- package/dist/api-handler-trpc.cjs.map +0 -1
- package/dist/api-handler-trpc.js.map +0 -1
- package/dist/api-handler.cjs.map +0 -1
- package/dist/api-handler.js.map +0 -1
- package/dist/bootstrap-FUAPY2OF.cjs +0 -32
- package/dist/bootstrap-FUAPY2OF.cjs.map +0 -1
- package/dist/bootstrap-OXNVM7FP.js +0 -7
- package/dist/bootstrap-OXNVM7FP.js.map +0 -1
- package/dist/chunk-2HFJUUFZ.js +0 -250
- package/dist/chunk-2HFJUUFZ.js.map +0 -1
- package/dist/chunk-3AKCVYDB.js +0 -162
- package/dist/chunk-3AKCVYDB.js.map +0 -1
- package/dist/chunk-3KTWGODI.cjs +0 -178
- package/dist/chunk-3KTWGODI.cjs.map +0 -1
- package/dist/chunk-3YELQL7Z.cjs +0 -4
- package/dist/chunk-3YELQL7Z.cjs.map +0 -1
- package/dist/chunk-4CV4JOE5.js +0 -24
- package/dist/chunk-4CV4JOE5.js.map +0 -1
- package/dist/chunk-4OCMVGEY.cjs +0 -173
- package/dist/chunk-4OCMVGEY.cjs.map +0 -1
- package/dist/chunk-4PWRCMTQ.cjs +0 -15
- package/dist/chunk-4PWRCMTQ.cjs.map +0 -1
- package/dist/chunk-53NYVYVX.js +0 -3243
- package/dist/chunk-53NYVYVX.js.map +0 -1
- package/dist/chunk-5GFQPHGT.cjs +0 -156
- package/dist/chunk-5GFQPHGT.cjs.map +0 -1
- package/dist/chunk-5RPRLTTN.js +0 -861
- package/dist/chunk-5RPRLTTN.js.map +0 -1
- package/dist/chunk-74YSDAWE.cjs +0 -2749
- package/dist/chunk-74YSDAWE.cjs.map +0 -1
- package/dist/chunk-7OGPN7MP.js +0 -352
- package/dist/chunk-7OGPN7MP.js.map +0 -1
- package/dist/chunk-7OS7TX2Q.cjs +0 -495
- package/dist/chunk-7OS7TX2Q.cjs.map +0 -1
- package/dist/chunk-ADLJSJSN.cjs +0 -13
- package/dist/chunk-ADLJSJSN.cjs.map +0 -1
- package/dist/chunk-B6X3LN25.cjs +0 -6486
- package/dist/chunk-B6X3LN25.cjs.map +0 -1
- package/dist/chunk-BYBMTIMT.js +0 -419
- package/dist/chunk-BYBMTIMT.js.map +0 -1
- package/dist/chunk-CF7OL6HR.js +0 -355
- package/dist/chunk-CF7OL6HR.js.map +0 -1
- package/dist/chunk-CJX74IYK.js +0 -489
- package/dist/chunk-CJX74IYK.js.map +0 -1
- package/dist/chunk-CKVOU6MX.cjs +0 -52
- package/dist/chunk-CKVOU6MX.cjs.map +0 -1
- package/dist/chunk-CZLDE2OZ.cjs +0 -28
- package/dist/chunk-CZLDE2OZ.cjs.map +0 -1
- package/dist/chunk-DPA3KWPY.cjs +0 -205
- package/dist/chunk-DPA3KWPY.cjs.map +0 -1
- package/dist/chunk-EEJUFDMF.cjs +0 -424
- package/dist/chunk-EEJUFDMF.cjs.map +0 -1
- package/dist/chunk-FIAMXNTH.js +0 -220
- package/dist/chunk-FIAMXNTH.js.map +0 -1
- package/dist/chunk-GAOXD3XT.js +0 -175
- package/dist/chunk-GAOXD3XT.js.map +0 -1
- package/dist/chunk-GTGRLD4Y.js +0 -3
- package/dist/chunk-GTGRLD4Y.js.map +0 -1
- package/dist/chunk-GUUB5EAG.cjs +0 -223
- package/dist/chunk-GUUB5EAG.cjs.map +0 -1
- package/dist/chunk-GXFOGU7N.cjs +0 -359
- package/dist/chunk-GXFOGU7N.cjs.map +0 -1
- package/dist/chunk-GZS4LMR7.js +0 -422
- package/dist/chunk-GZS4LMR7.js.map +0 -1
- package/dist/chunk-HY6B7B5A.js +0 -150
- package/dist/chunk-HY6B7B5A.js.map +0 -1
- package/dist/chunk-HZOMOK5Y.js +0 -2728
- package/dist/chunk-HZOMOK5Y.js.map +0 -1
- package/dist/chunk-IDVRRRAK.cjs +0 -824
- package/dist/chunk-IDVRRRAK.cjs.map +0 -1
- package/dist/chunk-KC2GDBLS.cjs +0 -84
- package/dist/chunk-KC2GDBLS.cjs.map +0 -1
- package/dist/chunk-L4EZKIEX.js +0 -185
- package/dist/chunk-L4EZKIEX.js.map +0 -1
- package/dist/chunk-LNTFRAHF.cjs +0 -473
- package/dist/chunk-LNTFRAHF.cjs.map +0 -1
- package/dist/chunk-MTIRYI7F.cjs +0 -4
- package/dist/chunk-MTIRYI7F.cjs.map +0 -1
- package/dist/chunk-N4GN6Q3V.cjs +0 -869
- package/dist/chunk-N4GN6Q3V.cjs.map +0 -1
- package/dist/chunk-N4H37VN4.cjs +0 -259
- package/dist/chunk-N4H37VN4.cjs.map +0 -1
- package/dist/chunk-N5QSHUA6.js +0 -1135
- package/dist/chunk-N5QSHUA6.js.map +0 -1
- package/dist/chunk-NKPKR5BW.cjs +0 -188
- package/dist/chunk-NKPKR5BW.cjs.map +0 -1
- package/dist/chunk-OIRG6ZRV.cjs +0 -1073
- package/dist/chunk-OIRG6ZRV.cjs.map +0 -1
- package/dist/chunk-OJ2AM75W.cjs +0 -253
- package/dist/chunk-OJ2AM75W.cjs.map +0 -1
- package/dist/chunk-OLV7K22H.js +0 -6457
- package/dist/chunk-OLV7K22H.js.map +0 -1
- package/dist/chunk-OX6GOIPH.js +0 -516
- package/dist/chunk-OX6GOIPH.js.map +0 -1
- package/dist/chunk-P2YW545G.js +0 -11
- package/dist/chunk-P2YW545G.js.map +0 -1
- package/dist/chunk-PHJRNPHY.cjs +0 -3291
- package/dist/chunk-PHJRNPHY.cjs.map +0 -1
- package/dist/chunk-Q72BOAPK.js +0 -817
- package/dist/chunk-Q72BOAPK.js.map +0 -1
- package/dist/chunk-QU2RFFH4.js +0 -3
- package/dist/chunk-QU2RFFH4.js.map +0 -1
- package/dist/chunk-RP7VZUEL.js +0 -49
- package/dist/chunk-RP7VZUEL.js.map +0 -1
- package/dist/chunk-SHTTJMLT.cjs +0 -361
- package/dist/chunk-SHTTJMLT.cjs.map +0 -1
- package/dist/chunk-SX76DNOO.js +0 -103
- package/dist/chunk-SX76DNOO.js.map +0 -1
- package/dist/chunk-THBQG64D.cjs +0 -425
- package/dist/chunk-THBQG64D.cjs.map +0 -1
- package/dist/chunk-TQG7G7YI.cjs +0 -526
- package/dist/chunk-TQG7G7YI.cjs.map +0 -1
- package/dist/chunk-U3VVLYXG.cjs +0 -189
- package/dist/chunk-U3VVLYXG.cjs.map +0 -1
- package/dist/chunk-UUDTPZX6.js +0 -193
- package/dist/chunk-UUDTPZX6.js.map +0 -1
- package/dist/chunk-V5AWATUP.js +0 -1059
- package/dist/chunk-V5AWATUP.js.map +0 -1
- package/dist/chunk-VAXLFZCV.cjs +0 -110
- package/dist/chunk-VAXLFZCV.cjs.map +0 -1
- package/dist/chunk-VKREAIBA.cjs +0 -1139
- package/dist/chunk-VKREAIBA.cjs.map +0 -1
- package/dist/chunk-WDTTHKDS.js +0 -458
- package/dist/chunk-WDTTHKDS.js.map +0 -1
- package/dist/chunk-WXVB364T.js +0 -218
- package/dist/chunk-WXVB364T.js.map +0 -1
- package/dist/chunk-X4BPPD6K.js +0 -181
- package/dist/chunk-X4BPPD6K.js.map +0 -1
- package/dist/chunk-XEB7PH2E.js +0 -81
- package/dist/chunk-XEB7PH2E.js.map +0 -1
- package/dist/chunk-YT7HXXVN.js +0 -13
- package/dist/chunk-YT7HXXVN.js.map +0 -1
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/client.cjs.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/drizzle/index.cjs.map +0 -1
- package/dist/drizzle/index.js.map +0 -1
- package/dist/fields/index.cjs.map +0 -1
- package/dist/fields/index.js.map +0 -1
- package/dist/graphql/index.cjs.map +0 -1
- package/dist/graphql/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integration.cjs.map +0 -1
- package/dist/integration.js.map +0 -1
- package/dist/media-7WDX4BDJ.js +0 -4
- package/dist/media-7WDX4BDJ.js.map +0 -1
- package/dist/media-TUSLVRQ6.cjs +0 -17
- package/dist/media-TUSLVRQ6.cjs.map +0 -1
- package/dist/mongo-auth-adapter-GT4S7SCU.cjs +0 -17
- package/dist/mongo-auth-adapter-GT4S7SCU.cjs.map +0 -1
- package/dist/mongo-auth-adapter-M7VV4LNB.js +0 -4
- package/dist/mongo-auth-adapter-M7VV4LNB.js.map +0 -1
- package/dist/mongodb/index.cjs.map +0 -1
- package/dist/mongodb/index.js.map +0 -1
- package/dist/postgres-auth-adapter-AFAPISH7.js +0 -5
- package/dist/postgres-auth-adapter-AFAPISH7.js.map +0 -1
- package/dist/postgres-auth-adapter-SFDTLONT.cjs +0 -14
- package/dist/postgres-auth-adapter-SFDTLONT.cjs.map +0 -1
- package/dist/redis-adapter-UQX4EE3B.cjs +0 -13
- package/dist/redis-adapter-UQX4EE3B.cjs.map +0 -1
- package/dist/redis-adapter-XALOGWY3.js +0 -4
- package/dist/redis-adapter-XALOGWY3.js.map +0 -1
- package/dist/rest/index.cjs.map +0 -1
- package/dist/rest/index.js.map +0 -1
- package/dist/schema-6QL3USNB.cjs +0 -67
- package/dist/schema-6QL3USNB.cjs.map +0 -1
- package/dist/schema-FNNWEAAW.js +0 -6
- package/dist/schema-FNNWEAAW.js.map +0 -1
- package/dist/sqlite-adapter-AQB5TCGV.cjs +0 -13
- package/dist/sqlite-adapter-AQB5TCGV.cjs.map +0 -1
- package/dist/sqlite-adapter-N5H6IM2X.js +0 -4
- package/dist/sqlite-adapter-N5H6IM2X.js.map +0 -1
- package/dist/templates/index.cjs.map +0 -1
- package/dist/templates/index.js.map +0 -1
- package/dist/trpc/index.cjs.map +0 -1
- package/dist/trpc/index.js.map +0 -1
- package/dist/ws/index.cjs.map +0 -1
- package/dist/ws/index.js.map +0 -1
package/dist/chunk-74YSDAWE.cjs
DELETED
|
@@ -1,2749 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunk3KTWGODI_cjs = require('./chunk-3KTWGODI.cjs');
|
|
4
|
-
var chunkTQG7G7YI_cjs = require('./chunk-TQG7G7YI.cjs');
|
|
5
|
-
var chunkN4GN6Q3V_cjs = require('./chunk-N4GN6Q3V.cjs');
|
|
6
|
-
var chunkB6X3LN25_cjs = require('./chunk-B6X3LN25.cjs');
|
|
7
|
-
var chunkLNTFRAHF_cjs = require('./chunk-LNTFRAHF.cjs');
|
|
8
|
-
var chunk4OCMVGEY_cjs = require('./chunk-4OCMVGEY.cjs');
|
|
9
|
-
var chunkEEJUFDMF_cjs = require('./chunk-EEJUFDMF.cjs');
|
|
10
|
-
var chunkOIRG6ZRV_cjs = require('./chunk-OIRG6ZRV.cjs');
|
|
11
|
-
var chunkDPA3KWPY_cjs = require('./chunk-DPA3KWPY.cjs');
|
|
12
|
-
var chunkGUUB5EAG_cjs = require('./chunk-GUUB5EAG.cjs');
|
|
13
|
-
var zod = require('zod');
|
|
14
|
-
var graphqlYoga = require('graphql-yoga');
|
|
15
|
-
var module$1 = require('module');
|
|
16
|
-
var crypto = require('crypto');
|
|
17
|
-
|
|
18
|
-
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
19
|
-
// src/registry/validator.ts
|
|
20
|
-
function findFieldByName(fields, name) {
|
|
21
|
-
for (const field of fields) {
|
|
22
|
-
if (field.name === name) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
if ("tabs" in field && field.tabs) {
|
|
26
|
-
for (const tab of field.tabs) {
|
|
27
|
-
if (findFieldByName(tab.fields, name)) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if ("fields" in field && field.fields) {
|
|
33
|
-
if (findFieldByName(field.fields, name)) {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
var ConfigValidationError = class extends Error {
|
|
41
|
-
errors;
|
|
42
|
-
constructor(errors) {
|
|
43
|
-
super(`Configuration validation failed:
|
|
44
|
-
${errors.join("\n")}`);
|
|
45
|
-
this.name = "ConfigValidationError";
|
|
46
|
-
this.errors = errors;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
function validateCollection(config) {
|
|
50
|
-
const errors = [];
|
|
51
|
-
if (!config.slug) {
|
|
52
|
-
errors.push(`Collection is missing a "slug" property`);
|
|
53
|
-
} else if (!/^[a-z][a-z0-9_-]*$/.test(config.slug)) {
|
|
54
|
-
errors.push(`Collection slug "${config.slug}" must be lowercase alphanumeric with dashes`);
|
|
55
|
-
}
|
|
56
|
-
if (!config.fields || config.fields.length === 0) {
|
|
57
|
-
errors.push(`Collection "${config.slug}" has no fields defined`);
|
|
58
|
-
} else {
|
|
59
|
-
const fieldErrors = validateFields(config.fields, config.slug);
|
|
60
|
-
errors.push(...fieldErrors);
|
|
61
|
-
}
|
|
62
|
-
if (config.access) {
|
|
63
|
-
for (const [action, handler] of Object.entries(config.access)) {
|
|
64
|
-
if (typeof handler !== "boolean" && typeof handler !== "function") {
|
|
65
|
-
errors.push(`Collection "${config.slug}" has invalid access.${action} (must be boolean or function)`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (config.admin?.useAsTitle) {
|
|
70
|
-
const fieldExists = findFieldByName(config.fields, config.admin.useAsTitle);
|
|
71
|
-
if (!fieldExists) {
|
|
72
|
-
errors.push(`Collection "${config.slug}" admin.useAsTitle references unknown field "${config.admin.useAsTitle}"`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (config.admin?.defaultColumns) {
|
|
76
|
-
for (const col of config.admin.defaultColumns) {
|
|
77
|
-
const fieldExists = findFieldByName(config.fields, col);
|
|
78
|
-
if (!fieldExists) {
|
|
79
|
-
errors.push(`Collection "${config.slug}" admin.defaultColumns references unknown field "${col}"`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (config.upload) {
|
|
84
|
-
if (config.upload.fileSize && config.upload.fileSize <= 0) {
|
|
85
|
-
errors.push(`Collection "${config.slug}" upload.fileSize must be positive`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (config.versions) {
|
|
89
|
-
if (config.versions.maxPerDoc && config.versions.maxPerDoc <= 0) {
|
|
90
|
-
errors.push(`Collection "${config.slug}" versions.maxPerDoc must be positive`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (config.auth) {
|
|
94
|
-
const hasEmailField = config.fields.some((f) => f.name === "email");
|
|
95
|
-
const hasPasswordField = config.fields.some((f) => f.name === "password");
|
|
96
|
-
if (!hasEmailField) {
|
|
97
|
-
errors.push(`Collection "${config.slug}" with auth enabled requires an "email" field`);
|
|
98
|
-
}
|
|
99
|
-
if (!hasPasswordField) {
|
|
100
|
-
errors.push(`Collection "${config.slug}" with auth enabled requires a "password" field`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return errors;
|
|
104
|
-
}
|
|
105
|
-
function validateGlobal(config) {
|
|
106
|
-
const errors = [];
|
|
107
|
-
if (!config.slug) {
|
|
108
|
-
errors.push(`Global is missing a "slug" property`);
|
|
109
|
-
} else if (!/^[a-z][a-z0-9_-]*$/.test(config.slug)) {
|
|
110
|
-
errors.push(`Global slug "${config.slug}" must be lowercase alphanumeric with dashes`);
|
|
111
|
-
}
|
|
112
|
-
if (!config.fields || config.fields.length === 0) {
|
|
113
|
-
errors.push(`Global "${config.slug}" has no fields defined`);
|
|
114
|
-
} else {
|
|
115
|
-
const fieldErrors = validateFields(config.fields, `global:${config.slug}`);
|
|
116
|
-
errors.push(...fieldErrors);
|
|
117
|
-
}
|
|
118
|
-
return errors;
|
|
119
|
-
}
|
|
120
|
-
function validateFields(fields, context) {
|
|
121
|
-
const errors = [];
|
|
122
|
-
const fieldNames = /* @__PURE__ */ new Set();
|
|
123
|
-
for (let i = 0; i < fields.length; i++) {
|
|
124
|
-
const field = fields[i];
|
|
125
|
-
if (field.type === "row" || field.type === "collapsible" || field.type === "tabs") {
|
|
126
|
-
if ("fields" in field && field.fields) {
|
|
127
|
-
const nestedErrors = validateFields(field.fields, context);
|
|
128
|
-
errors.push(...nestedErrors);
|
|
129
|
-
} else if ("tabs" in field) {
|
|
130
|
-
for (const tab of field.tabs) {
|
|
131
|
-
const tabErrors = validateFields(tab.fields, context);
|
|
132
|
-
errors.push(...tabErrors);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
const fieldName = field.name;
|
|
138
|
-
if (!fieldName) {
|
|
139
|
-
errors.push(`${context}: Field at index ${i} is missing a "name" property`);
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
if (fieldNames.has(fieldName)) {
|
|
143
|
-
errors.push(`${context}: Duplicate field name "${fieldName}"`);
|
|
144
|
-
}
|
|
145
|
-
fieldNames.add(fieldName);
|
|
146
|
-
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(fieldName)) {
|
|
147
|
-
errors.push(`${context}: Field name "${fieldName}" must be alphanumeric with underscores`);
|
|
148
|
-
}
|
|
149
|
-
if (!field.type) {
|
|
150
|
-
errors.push(`${context}: Field "${fieldName}" is missing a "type" property`);
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
switch (field.type) {
|
|
154
|
-
case "relationship":
|
|
155
|
-
if (!field.relationTo) {
|
|
156
|
-
errors.push(`${context}: Relationship field "${fieldName}" is missing "relationTo"`);
|
|
157
|
-
}
|
|
158
|
-
break;
|
|
159
|
-
case "array":
|
|
160
|
-
if (!field.fields || field.fields.length === 0) {
|
|
161
|
-
errors.push(`${context}: Array field "${fieldName}" has no fields defined`);
|
|
162
|
-
} else {
|
|
163
|
-
const arrayErrors = validateFields(field.fields, `${context}.${fieldName}`);
|
|
164
|
-
errors.push(...arrayErrors);
|
|
165
|
-
}
|
|
166
|
-
break;
|
|
167
|
-
case "group":
|
|
168
|
-
if (!field.fields || field.fields.length === 0) {
|
|
169
|
-
errors.push(`${context}: Group field "${fieldName}" has no fields defined`);
|
|
170
|
-
} else {
|
|
171
|
-
const groupErrors = validateFields(field.fields, `${context}.${fieldName}`);
|
|
172
|
-
errors.push(...groupErrors);
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
case "blocks":
|
|
176
|
-
if (!field.blocks || field.blocks.length === 0) {
|
|
177
|
-
errors.push(`${context}: Blocks field "${fieldName}" has no blocks defined`);
|
|
178
|
-
} else {
|
|
179
|
-
const blockErrors = validateBlocks(field.blocks, `${context}.${fieldName}`);
|
|
180
|
-
errors.push(...blockErrors);
|
|
181
|
-
}
|
|
182
|
-
break;
|
|
183
|
-
case "select":
|
|
184
|
-
case "radio":
|
|
185
|
-
if ((!field.options || field.options.length === 0) && !field.dynamicOptions) {
|
|
186
|
-
errors.push(`${context}: ${field.type} field "${fieldName}" has no options defined`);
|
|
187
|
-
} else if (field.options) {
|
|
188
|
-
const values = field.options.map((o) => o.value);
|
|
189
|
-
const uniqueValues = new Set(values);
|
|
190
|
-
if (values.length !== uniqueValues.size) {
|
|
191
|
-
errors.push(`${context}: ${field.type} field "${fieldName}" has duplicate option values`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
break;
|
|
195
|
-
case "upload":
|
|
196
|
-
if (!field.relationTo) {
|
|
197
|
-
errors.push(`${context}: Upload field "${fieldName}" is missing "relationTo"`);
|
|
198
|
-
}
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
if ("min" in field && "max" in field && field.min > field.max) {
|
|
202
|
-
errors.push(`${context}: Field "${fieldName}" has min greater than max`);
|
|
203
|
-
}
|
|
204
|
-
if ("minLength" in field && "maxLength" in field && field.minLength > field.maxLength) {
|
|
205
|
-
errors.push(`${context}: Field "${fieldName}" has minLength greater than maxLength`);
|
|
206
|
-
}
|
|
207
|
-
if ("minRows" in field && "maxRows" in field && field.minRows > field.maxRows) {
|
|
208
|
-
errors.push(`${context}: Field "${fieldName}" has minRows greater than maxRows`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return errors;
|
|
212
|
-
}
|
|
213
|
-
function validateBlocks(blocks, context) {
|
|
214
|
-
const errors = [];
|
|
215
|
-
const slugs = /* @__PURE__ */ new Set();
|
|
216
|
-
for (const block of blocks) {
|
|
217
|
-
if (!block.slug) {
|
|
218
|
-
errors.push(`${context}: Block is missing a "slug" property`);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
if (slugs.has(block.slug)) {
|
|
222
|
-
errors.push(`${context}: Duplicate block slug "${block.slug}"`);
|
|
223
|
-
}
|
|
224
|
-
slugs.add(block.slug);
|
|
225
|
-
if (!block.label) {
|
|
226
|
-
errors.push(`${context}: Block "${block.slug}" is missing a "label" property`);
|
|
227
|
-
}
|
|
228
|
-
if (!block.fields || block.fields.length === 0) {
|
|
229
|
-
errors.push(`${context}: Block "${block.slug}" has no fields defined`);
|
|
230
|
-
} else {
|
|
231
|
-
const blockErrors = validateFields(block.fields, `${context}.${block.slug}`);
|
|
232
|
-
errors.push(...blockErrors);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return errors;
|
|
236
|
-
}
|
|
237
|
-
function validateConfig(collections, globals = []) {
|
|
238
|
-
const errors = [];
|
|
239
|
-
const slugs = /* @__PURE__ */ new Set();
|
|
240
|
-
for (const collection of collections) {
|
|
241
|
-
if (slugs.has(collection.slug)) {
|
|
242
|
-
errors.push(`Duplicate collection slug "${collection.slug}"`);
|
|
243
|
-
}
|
|
244
|
-
slugs.add(collection.slug);
|
|
245
|
-
}
|
|
246
|
-
for (const global of globals) {
|
|
247
|
-
if (slugs.has(global.slug)) {
|
|
248
|
-
errors.push(`Duplicate global slug "${global.slug}"`);
|
|
249
|
-
}
|
|
250
|
-
slugs.add(global.slug);
|
|
251
|
-
}
|
|
252
|
-
for (const collection of collections) {
|
|
253
|
-
const collectionErrors = validateCollection(collection);
|
|
254
|
-
errors.push(...collectionErrors);
|
|
255
|
-
}
|
|
256
|
-
for (const global of globals) {
|
|
257
|
-
const globalErrors = validateGlobal(global);
|
|
258
|
-
errors.push(...globalErrors);
|
|
259
|
-
}
|
|
260
|
-
for (const collection of collections) {
|
|
261
|
-
const relationshipErrors = validateRelationships(collection.fields, collections);
|
|
262
|
-
errors.push(...relationshipErrors);
|
|
263
|
-
}
|
|
264
|
-
if (errors.length > 0) {
|
|
265
|
-
throw new ConfigValidationError(errors);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
function validateRelationships(fields, collections) {
|
|
269
|
-
const errors = [];
|
|
270
|
-
const collectionSlugs = new Set(collections.map((c) => c.slug));
|
|
271
|
-
for (const field of fields) {
|
|
272
|
-
if (field.type === "relationship") {
|
|
273
|
-
const targets = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
|
|
274
|
-
for (const target of targets) {
|
|
275
|
-
if (!collectionSlugs.has(target)) {
|
|
276
|
-
console.warn(`[Kyro Config Warning]: Relationship field "${field.name}" references unknown collection "${target}". Select options will not be available until this collection is registered.`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if (field.type === "upload") {
|
|
281
|
-
const targets = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
|
|
282
|
-
for (const target of targets) {
|
|
283
|
-
if (!collectionSlugs.has(target)) {
|
|
284
|
-
errors.push(`Upload field "${field.name}" references unknown collection "${target}"`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
if ("fields" in field && field.fields) {
|
|
289
|
-
const nestedErrors = validateRelationships(field.fields, collections);
|
|
290
|
-
errors.push(...nestedErrors);
|
|
291
|
-
}
|
|
292
|
-
if ("tabs" in field) {
|
|
293
|
-
for (const tab of field.tabs) {
|
|
294
|
-
const tabErrors = validateRelationships(tab.fields, collections);
|
|
295
|
-
errors.push(...tabErrors);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if ("blocks" in field) {
|
|
299
|
-
for (const block of field.blocks) {
|
|
300
|
-
const blockErrors = validateRelationships(block.fields, collections);
|
|
301
|
-
errors.push(...blockErrors);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return errors;
|
|
306
|
-
}
|
|
307
|
-
function fieldToZod(field) {
|
|
308
|
-
switch (field.type) {
|
|
309
|
-
case "text":
|
|
310
|
-
return textToZod(field);
|
|
311
|
-
case "number":
|
|
312
|
-
return numberToZod(field);
|
|
313
|
-
case "checkbox":
|
|
314
|
-
return checkboxToZod(field);
|
|
315
|
-
case "date":
|
|
316
|
-
return dateToZod(field);
|
|
317
|
-
case "email":
|
|
318
|
-
return emailToZod(field);
|
|
319
|
-
case "password":
|
|
320
|
-
return passwordToZod(field);
|
|
321
|
-
case "textarea":
|
|
322
|
-
return textareaToZod(field);
|
|
323
|
-
case "select":
|
|
324
|
-
return selectToZod(field);
|
|
325
|
-
case "radio":
|
|
326
|
-
return radioToZod(field);
|
|
327
|
-
case "color":
|
|
328
|
-
return colorToZod(field);
|
|
329
|
-
case "richtext":
|
|
330
|
-
return richTextToZod(field);
|
|
331
|
-
case "json":
|
|
332
|
-
return jsonToZod(field);
|
|
333
|
-
case "code":
|
|
334
|
-
return codeToZod(field);
|
|
335
|
-
case "upload":
|
|
336
|
-
return uploadToZod(field);
|
|
337
|
-
case "image":
|
|
338
|
-
return uploadToZod(field);
|
|
339
|
-
case "markdown":
|
|
340
|
-
return markdownToZod(field);
|
|
341
|
-
case "relationship":
|
|
342
|
-
return relationshipToZod(field);
|
|
343
|
-
case "relationship-block":
|
|
344
|
-
return relationshipToZod(field);
|
|
345
|
-
case "array":
|
|
346
|
-
return arrayToZod(field);
|
|
347
|
-
case "list":
|
|
348
|
-
return listToZod(field);
|
|
349
|
-
case "group":
|
|
350
|
-
return groupToZod(field);
|
|
351
|
-
case "blocks":
|
|
352
|
-
return blocksToZod(field);
|
|
353
|
-
case "row":
|
|
354
|
-
return rowToZod(field);
|
|
355
|
-
case "collapsible":
|
|
356
|
-
return collapsibleToZod(field);
|
|
357
|
-
case "tabs":
|
|
358
|
-
return tabsToZod(field);
|
|
359
|
-
default:
|
|
360
|
-
return zod.z.any();
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
function textToZod(field) {
|
|
364
|
-
let schema = zod.z.string();
|
|
365
|
-
if (field.minLength) schema = schema.min(field.minLength);
|
|
366
|
-
if (field.maxLength) schema = schema.max(field.maxLength);
|
|
367
|
-
if (field.pattern) schema = schema.regex(new RegExp(field.pattern));
|
|
368
|
-
if (field.variant === "email") schema = schema.email();
|
|
369
|
-
if (field.variant === "url") schema = schema.url();
|
|
370
|
-
if (field.hasMany) schema = zod.z.array(schema);
|
|
371
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
372
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
373
|
-
return schema;
|
|
374
|
-
}
|
|
375
|
-
function numberToZod(field) {
|
|
376
|
-
let schema = field.integer ? zod.z.coerce.number().int() : zod.z.coerce.number();
|
|
377
|
-
if (field.min !== void 0) schema = schema.min(field.min);
|
|
378
|
-
if (field.max !== void 0) schema = schema.max(field.max);
|
|
379
|
-
if (field.step) {
|
|
380
|
-
schema = schema.refine(
|
|
381
|
-
(val) => Number.isInteger(val / field.step),
|
|
382
|
-
`Value must be divisible by ${field.step}`
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
if (field.hasMany) schema = zod.z.array(schema);
|
|
386
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
387
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
388
|
-
return schema;
|
|
389
|
-
}
|
|
390
|
-
function checkboxToZod(field) {
|
|
391
|
-
let schema = zod.z.boolean();
|
|
392
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
393
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
394
|
-
return schema;
|
|
395
|
-
}
|
|
396
|
-
function dateToZod(field) {
|
|
397
|
-
let schema = zod.z.string().refine((val) => !isNaN(Date.parse(val)), "Invalid date format");
|
|
398
|
-
if (field.minDate) {
|
|
399
|
-
schema = schema.refine(
|
|
400
|
-
(val) => new Date(val) >= new Date(field.minDate),
|
|
401
|
-
`Date must be after ${field.minDate}`
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
if (field.maxDate) {
|
|
405
|
-
schema = schema.refine(
|
|
406
|
-
(val) => new Date(val) <= new Date(field.maxDate),
|
|
407
|
-
`Date must be before ${field.maxDate}`
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
411
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
412
|
-
return schema;
|
|
413
|
-
}
|
|
414
|
-
function emailToZod(field) {
|
|
415
|
-
let schema = zod.z.string().email("Invalid email");
|
|
416
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
417
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
418
|
-
return schema;
|
|
419
|
-
}
|
|
420
|
-
function passwordToZod(field) {
|
|
421
|
-
let schema = zod.z.string().min(6, "Password must be at least 6 characters");
|
|
422
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
423
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
424
|
-
return schema;
|
|
425
|
-
}
|
|
426
|
-
function textareaToZod(field) {
|
|
427
|
-
let schema = zod.z.string();
|
|
428
|
-
if (field.minLength) schema = schema.min(field.minLength);
|
|
429
|
-
if (field.maxLength) schema = schema.max(field.maxLength);
|
|
430
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
431
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
432
|
-
return schema;
|
|
433
|
-
}
|
|
434
|
-
function selectToZod(field) {
|
|
435
|
-
let schema;
|
|
436
|
-
if (field.options && field.options.length > 0) {
|
|
437
|
-
const values = field.options.map((opt) => opt.value);
|
|
438
|
-
if (field.hasMany) {
|
|
439
|
-
schema = zod.z.array(zod.z.enum(values));
|
|
440
|
-
} else {
|
|
441
|
-
schema = zod.z.enum(values);
|
|
442
|
-
}
|
|
443
|
-
} else {
|
|
444
|
-
schema = field.hasMany ? zod.z.array(zod.z.string()) : zod.z.string();
|
|
445
|
-
}
|
|
446
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
447
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
448
|
-
return schema;
|
|
449
|
-
}
|
|
450
|
-
function radioToZod(field) {
|
|
451
|
-
const values = field.options.map((opt) => opt.value);
|
|
452
|
-
let schema = zod.z.enum(values);
|
|
453
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
454
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
455
|
-
return schema;
|
|
456
|
-
}
|
|
457
|
-
function colorToZod(field) {
|
|
458
|
-
let schema = zod.z.string();
|
|
459
|
-
if (field.format === "hex")
|
|
460
|
-
schema = schema.regex(/^#[0-9A-Fa-f]{6}$/);
|
|
461
|
-
if (field.format === "rgb")
|
|
462
|
-
schema = schema.regex(
|
|
463
|
-
/^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/
|
|
464
|
-
);
|
|
465
|
-
if (field.format === "hsl")
|
|
466
|
-
schema = schema.regex(
|
|
467
|
-
/^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*\)$/
|
|
468
|
-
);
|
|
469
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
470
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
471
|
-
return schema;
|
|
472
|
-
}
|
|
473
|
-
function richTextToZod(field) {
|
|
474
|
-
let schema = zod.z.array(zod.z.record(zod.z.any()));
|
|
475
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
476
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
477
|
-
return schema;
|
|
478
|
-
}
|
|
479
|
-
function jsonToZod(field) {
|
|
480
|
-
let schema = zod.z.union([zod.z.record(zod.z.any()), zod.z.array(zod.z.any())]);
|
|
481
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
482
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
483
|
-
return schema;
|
|
484
|
-
}
|
|
485
|
-
function codeToZod(field) {
|
|
486
|
-
let schema = zod.z.string();
|
|
487
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
488
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
489
|
-
return schema;
|
|
490
|
-
}
|
|
491
|
-
function uploadToZod(field) {
|
|
492
|
-
let schema;
|
|
493
|
-
if (field.relationTo) {
|
|
494
|
-
const mediaSchema = zod.z.object({
|
|
495
|
-
id: zod.z.string(),
|
|
496
|
-
url: zod.z.string().optional(),
|
|
497
|
-
filename: zod.z.string().optional(),
|
|
498
|
-
mimeType: zod.z.string().optional()
|
|
499
|
-
});
|
|
500
|
-
schema = zod.z.union([zod.z.string(), mediaSchema]);
|
|
501
|
-
} else {
|
|
502
|
-
schema = zod.z.string();
|
|
503
|
-
}
|
|
504
|
-
if (field.hasMany) {
|
|
505
|
-
schema = zod.z.array(schema);
|
|
506
|
-
}
|
|
507
|
-
if (!field.required) {
|
|
508
|
-
schema = schema.optional().nullable();
|
|
509
|
-
}
|
|
510
|
-
if (field.validate) {
|
|
511
|
-
schema = addCustomValidation(schema, field.validate);
|
|
512
|
-
}
|
|
513
|
-
return schema;
|
|
514
|
-
}
|
|
515
|
-
function markdownToZod(field) {
|
|
516
|
-
let schema = zod.z.string();
|
|
517
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
518
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
519
|
-
return schema;
|
|
520
|
-
}
|
|
521
|
-
function relationshipToZod(field) {
|
|
522
|
-
let schema;
|
|
523
|
-
if (Array.isArray(field.relationTo)) {
|
|
524
|
-
schema = zod.z.object({
|
|
525
|
-
relationTo: zod.z.enum(field.relationTo),
|
|
526
|
-
value: zod.z.string()
|
|
527
|
-
});
|
|
528
|
-
} else {
|
|
529
|
-
schema = zod.z.union([
|
|
530
|
-
zod.z.string(),
|
|
531
|
-
zod.z.object({ relationTo: zod.z.string(), value: zod.z.string() })
|
|
532
|
-
]);
|
|
533
|
-
}
|
|
534
|
-
if (field.hasMany) schema = zod.z.array(schema);
|
|
535
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
536
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
537
|
-
return schema;
|
|
538
|
-
}
|
|
539
|
-
function arrayToZod(field) {
|
|
540
|
-
const itemSchema = zod.z.object(
|
|
541
|
-
Object.fromEntries(
|
|
542
|
-
field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
|
|
543
|
-
)
|
|
544
|
-
);
|
|
545
|
-
let schema = zod.z.array(itemSchema);
|
|
546
|
-
if (field.minRows) schema = schema.min(field.minRows);
|
|
547
|
-
if (field.maxRows) schema = schema.max(field.maxRows);
|
|
548
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
549
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
550
|
-
return schema;
|
|
551
|
-
}
|
|
552
|
-
function listToZod(field) {
|
|
553
|
-
let schema = zod.z.array(zod.z.string());
|
|
554
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
555
|
-
return schema;
|
|
556
|
-
}
|
|
557
|
-
function groupToZod(field) {
|
|
558
|
-
const schema = zod.z.object(
|
|
559
|
-
Object.fromEntries(
|
|
560
|
-
field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
|
|
561
|
-
)
|
|
562
|
-
);
|
|
563
|
-
if (!field.required) return schema.optional().nullable();
|
|
564
|
-
return schema;
|
|
565
|
-
}
|
|
566
|
-
function blocksToZod(field) {
|
|
567
|
-
const blocks = field.blocks || [];
|
|
568
|
-
blocks.map((block) => {
|
|
569
|
-
return zod.z.object({
|
|
570
|
-
blockType: zod.z.literal(block.slug),
|
|
571
|
-
...Object.fromEntries(
|
|
572
|
-
block.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
|
|
573
|
-
)
|
|
574
|
-
});
|
|
575
|
-
});
|
|
576
|
-
const knownTypes = blocks.map((b) => b.slug);
|
|
577
|
-
let schema;
|
|
578
|
-
if (knownTypes.length > 0) {
|
|
579
|
-
const knownSchema = zod.z.object({
|
|
580
|
-
blockType: zod.z.enum(knownTypes)
|
|
581
|
-
}).catchall(zod.z.any());
|
|
582
|
-
const unknownSchema = zod.z.object({
|
|
583
|
-
blockType: zod.z.string()
|
|
584
|
-
}).catchall(zod.z.any());
|
|
585
|
-
schema = zod.z.array(zod.z.union([knownSchema, unknownSchema, zod.z.record(zod.z.any())]));
|
|
586
|
-
} else {
|
|
587
|
-
schema = zod.z.array(zod.z.union([zod.z.object({ blockType: zod.z.string() }).catchall(zod.z.any()), zod.z.record(zod.z.any())]));
|
|
588
|
-
}
|
|
589
|
-
if (field.minRows) schema = schema.min(field.minRows);
|
|
590
|
-
if (field.maxRows) schema = schema.max(field.maxRows);
|
|
591
|
-
if (!field.required) schema = schema.optional().nullable();
|
|
592
|
-
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
593
|
-
return schema;
|
|
594
|
-
}
|
|
595
|
-
function rowToZod(field) {
|
|
596
|
-
const schema = zod.z.object(
|
|
597
|
-
Object.fromEntries(
|
|
598
|
-
field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
|
|
599
|
-
)
|
|
600
|
-
);
|
|
601
|
-
return schema;
|
|
602
|
-
}
|
|
603
|
-
function collapsibleToZod(field) {
|
|
604
|
-
const schema = zod.z.object(
|
|
605
|
-
Object.fromEntries(
|
|
606
|
-
field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
|
|
607
|
-
)
|
|
608
|
-
);
|
|
609
|
-
return schema;
|
|
610
|
-
}
|
|
611
|
-
function tabsToZod(field) {
|
|
612
|
-
const schemas = {};
|
|
613
|
-
for (const tab of field.tabs) {
|
|
614
|
-
for (const f of tab.fields) {
|
|
615
|
-
if (f.name) {
|
|
616
|
-
schemas[f.name] = fieldToZod(f);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
return zod.z.object(schemas).passthrough().optional();
|
|
621
|
-
}
|
|
622
|
-
function addCustomValidation(schema, validate) {
|
|
623
|
-
return schema.refine(
|
|
624
|
-
async (val) => {
|
|
625
|
-
const result = await validate(val, { required: false });
|
|
626
|
-
return result === true;
|
|
627
|
-
},
|
|
628
|
-
{
|
|
629
|
-
message: "Custom validation failed"
|
|
630
|
-
}
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
function buildNestedShape(fields) {
|
|
634
|
-
const shape = {};
|
|
635
|
-
for (const field of fields) {
|
|
636
|
-
if (!field.name) continue;
|
|
637
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
638
|
-
const tabShape = {};
|
|
639
|
-
for (const tab of field.tabs) {
|
|
640
|
-
const nestedShape = buildNestedShape(tab.fields);
|
|
641
|
-
Object.assign(tabShape, nestedShape);
|
|
642
|
-
}
|
|
643
|
-
shape[field.name] = zod.z.object(tabShape).passthrough().optional();
|
|
644
|
-
} else if (field.type === "row" && "fields" in field) {
|
|
645
|
-
const rowShape = buildNestedShape(field.fields);
|
|
646
|
-
Object.assign(shape, rowShape);
|
|
647
|
-
} else if (field.type === "collapsible" && "fields" in field) {
|
|
648
|
-
shape[field.name] = zod.z.object(buildNestedShape(field.fields)).passthrough().optional();
|
|
649
|
-
} else {
|
|
650
|
-
shape[field.name] = fieldToZod(field);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return shape;
|
|
654
|
-
}
|
|
655
|
-
function buildUpdateNestedShape(fields) {
|
|
656
|
-
const shape = {};
|
|
657
|
-
for (const field of fields) {
|
|
658
|
-
if (!field.name) continue;
|
|
659
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
660
|
-
const tabShape = {};
|
|
661
|
-
for (const tab of field.tabs) {
|
|
662
|
-
const nestedShape = buildUpdateNestedShape(tab.fields);
|
|
663
|
-
Object.assign(tabShape, nestedShape);
|
|
664
|
-
}
|
|
665
|
-
shape[field.name] = zod.z.object(tabShape).passthrough().optional().nullable();
|
|
666
|
-
} else if (field.type === "row" && "fields" in field) {
|
|
667
|
-
const rowShape = buildUpdateNestedShape(field.fields);
|
|
668
|
-
Object.assign(shape, rowShape);
|
|
669
|
-
} else if (field.type === "collapsible" && "fields" in field) {
|
|
670
|
-
shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).passthrough().optional().nullable();
|
|
671
|
-
} else if (field.type === "group" && "fields" in field) {
|
|
672
|
-
shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).passthrough().optional().nullable();
|
|
673
|
-
} else {
|
|
674
|
-
shape[field.name] = fieldToZod(field).optional().nullable();
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return shape;
|
|
678
|
-
}
|
|
679
|
-
function collectionToZod(collection) {
|
|
680
|
-
const shape = buildNestedShape(collection.fields);
|
|
681
|
-
if (collection.timestamps) {
|
|
682
|
-
shape["createdAt"] = zod.z.string().optional();
|
|
683
|
-
shape["updatedAt"] = zod.z.string().optional();
|
|
684
|
-
}
|
|
685
|
-
if (collection.tenantScoped) {
|
|
686
|
-
shape["tenantID"] = zod.z.string().optional();
|
|
687
|
-
}
|
|
688
|
-
shape["id"] = zod.z.string().optional();
|
|
689
|
-
return zod.z.object(shape).passthrough();
|
|
690
|
-
}
|
|
691
|
-
function collectionToCreateZod(collection) {
|
|
692
|
-
const shape = buildNestedShape(collection.fields);
|
|
693
|
-
return zod.z.object(shape).passthrough();
|
|
694
|
-
}
|
|
695
|
-
function collectionToUpdateZod(collection) {
|
|
696
|
-
const shape = {};
|
|
697
|
-
for (const field of collection.fields) {
|
|
698
|
-
if (!field.name) continue;
|
|
699
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
700
|
-
const tabShape = {};
|
|
701
|
-
for (const tab of field.tabs) {
|
|
702
|
-
for (const tabField of tab.fields) {
|
|
703
|
-
if (tabField.name) {
|
|
704
|
-
tabShape[tabField.name] = fieldToZod(tabField).optional().nullable();
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
shape[field.name] = zod.z.object(tabShape).optional().nullable();
|
|
709
|
-
} else if (field.type === "row" && "fields" in field) {
|
|
710
|
-
const rowShape = {};
|
|
711
|
-
for (const rowField of field.fields) {
|
|
712
|
-
if (rowField.name) {
|
|
713
|
-
rowShape[rowField.name] = fieldToZod(rowField).optional().nullable();
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
Object.assign(shape, rowShape);
|
|
717
|
-
} else if (field.type === "collapsible" && "fields" in field) {
|
|
718
|
-
shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
|
|
719
|
-
} else if (field.type === "group" && "fields" in field) {
|
|
720
|
-
shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
|
|
721
|
-
} else {
|
|
722
|
-
shape[field.name] = fieldToZod(field).optional().nullable();
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return zod.z.object(shape).passthrough();
|
|
726
|
-
}
|
|
727
|
-
function collectionToWhereZod(collection) {
|
|
728
|
-
const shape = {};
|
|
729
|
-
for (const field of collection.fields) {
|
|
730
|
-
if (field.name) {
|
|
731
|
-
shape[field.name] = zod.z.object({
|
|
732
|
-
equals: zod.z.any().optional(),
|
|
733
|
-
not_equals: zod.z.any().optional(),
|
|
734
|
-
in: zod.z.array(zod.z.any()).optional(),
|
|
735
|
-
not_in: zod.z.array(zod.z.any()).optional(),
|
|
736
|
-
greater_than: zod.z.number().optional(),
|
|
737
|
-
greater_than_equal: zod.z.number().optional(),
|
|
738
|
-
less_than: zod.z.number().optional(),
|
|
739
|
-
less_than_equal: zod.z.number().optional(),
|
|
740
|
-
like: zod.z.string().optional(),
|
|
741
|
-
not_like: zod.z.string().optional(),
|
|
742
|
-
contains: zod.z.string().optional(),
|
|
743
|
-
exists: zod.z.boolean().optional()
|
|
744
|
-
}).optional();
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
shape["AND"] = zod.z.array(zod.z.lazy(() => zod.z.object(shape))).optional();
|
|
748
|
-
shape["OR"] = zod.z.array(zod.z.lazy(() => zod.z.object(shape))).optional();
|
|
749
|
-
return zod.z.object(shape).optional();
|
|
750
|
-
}
|
|
751
|
-
function globalToZod(global) {
|
|
752
|
-
const shape = buildNestedShape(global.fields);
|
|
753
|
-
shape["id"] = zod.z.string().optional();
|
|
754
|
-
return zod.z.object(shape).passthrough();
|
|
755
|
-
}
|
|
756
|
-
function globalToUpdateZod(global) {
|
|
757
|
-
const shape = {};
|
|
758
|
-
for (const field of global.fields) {
|
|
759
|
-
if (!field.name) continue;
|
|
760
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
761
|
-
const tabShape = {};
|
|
762
|
-
for (const tab of field.tabs) {
|
|
763
|
-
for (const tabField of tab.fields) {
|
|
764
|
-
if (tabField.name) {
|
|
765
|
-
tabShape[tabField.name] = fieldToZod(tabField).optional().nullable();
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
shape[field.name] = zod.z.object(tabShape).optional().nullable();
|
|
770
|
-
} else if (field.type === "collapsible" && "fields" in field) {
|
|
771
|
-
shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
|
|
772
|
-
} else if (field.type === "group" && "fields" in field) {
|
|
773
|
-
shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
|
|
774
|
-
} else {
|
|
775
|
-
shape[field.name] = fieldToZod(field).optional().nullable();
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
return zod.z.object(shape).passthrough();
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// src/registry/index.ts
|
|
782
|
-
var Registry = class {
|
|
783
|
-
storageProviders = chunkB6X3LN25_cjs.getDefaultRegistry();
|
|
784
|
-
collections = /* @__PURE__ */ new Map();
|
|
785
|
-
globals = /* @__PURE__ */ new Map();
|
|
786
|
-
plugins = [];
|
|
787
|
-
schemaCache = /* @__PURE__ */ new Map();
|
|
788
|
-
initialized = false;
|
|
789
|
-
// ========================================================================
|
|
790
|
-
// Collection Management
|
|
791
|
-
// ========================================================================
|
|
792
|
-
addCollection(config) {
|
|
793
|
-
if (this.initialized) {
|
|
794
|
-
throw new Error(
|
|
795
|
-
"Cannot add collections after Registry has been initialized"
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
if (this.collections.has(config.slug)) {
|
|
799
|
-
console.warn(
|
|
800
|
-
`[Registry] Duplicate collection slug "${config.slug}" \u2014 skipping`
|
|
801
|
-
);
|
|
802
|
-
return;
|
|
803
|
-
}
|
|
804
|
-
let finalConfig = { ...config };
|
|
805
|
-
for (const plugin of this.plugins) {
|
|
806
|
-
if (plugin.extendCollection) {
|
|
807
|
-
finalConfig = plugin.extendCollection(finalConfig.slug, finalConfig);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
finalConfig.fields = this.applyFieldDefaults(finalConfig);
|
|
811
|
-
const errors = validateCollection(finalConfig);
|
|
812
|
-
if (errors.length > 0) {
|
|
813
|
-
throw new Error(`Invalid collection config: ${errors.join(", ")}`);
|
|
814
|
-
}
|
|
815
|
-
this.collections.set(finalConfig.slug, finalConfig);
|
|
816
|
-
this.clearSchemaCache(finalConfig.slug);
|
|
817
|
-
}
|
|
818
|
-
addCollections(configs) {
|
|
819
|
-
for (const config of configs) {
|
|
820
|
-
this.addCollection(config);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
getCollection(slug) {
|
|
824
|
-
return this.collections.get(slug);
|
|
825
|
-
}
|
|
826
|
-
getCollections() {
|
|
827
|
-
return Array.from(this.collections.values());
|
|
828
|
-
}
|
|
829
|
-
getCollectionSlugs() {
|
|
830
|
-
return Array.from(this.collections.keys());
|
|
831
|
-
}
|
|
832
|
-
hasCollection(slug) {
|
|
833
|
-
return this.collections.has(slug);
|
|
834
|
-
}
|
|
835
|
-
removeCollection(slug) {
|
|
836
|
-
if (this.initialized) {
|
|
837
|
-
throw new Error(
|
|
838
|
-
"Cannot remove collections after Registry has been initialized"
|
|
839
|
-
);
|
|
840
|
-
}
|
|
841
|
-
this.clearSchemaCache(slug);
|
|
842
|
-
return this.collections.delete(slug);
|
|
843
|
-
}
|
|
844
|
-
// ========================================================================
|
|
845
|
-
// Global Management
|
|
846
|
-
// ========================================================================
|
|
847
|
-
addGlobal(config) {
|
|
848
|
-
if (this.initialized) {
|
|
849
|
-
throw new Error("Cannot add globals after Registry has been initialized");
|
|
850
|
-
}
|
|
851
|
-
this._addGlobalUnsafe(config);
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Add a global after the registry is already initialized.
|
|
855
|
-
* Only for internal use (e.g. storage settings form built at startup).
|
|
856
|
-
*/
|
|
857
|
-
addGlobalPostInit(config) {
|
|
858
|
-
if (this.globals.has(config.slug)) {
|
|
859
|
-
this.globals.delete(config.slug);
|
|
860
|
-
}
|
|
861
|
-
this._addGlobalUnsafe(config);
|
|
862
|
-
}
|
|
863
|
-
_addGlobalUnsafe(config) {
|
|
864
|
-
if (this.globals.has(config.slug)) {
|
|
865
|
-
console.warn(
|
|
866
|
-
`[Registry] Duplicate global slug "${config.slug}" \u2014 skipping`
|
|
867
|
-
);
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
const errors = validateGlobal(config);
|
|
871
|
-
if (errors.length > 0) {
|
|
872
|
-
throw new Error(`Invalid global config: ${errors.join(", ")}`);
|
|
873
|
-
}
|
|
874
|
-
let finalConfig = { ...config };
|
|
875
|
-
for (const plugin of this.plugins) {
|
|
876
|
-
if (plugin.extendGlobal) {
|
|
877
|
-
finalConfig = plugin.extendGlobal(finalConfig.slug, finalConfig);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
this.globals.set(finalConfig.slug, finalConfig);
|
|
881
|
-
this.clearSchemaCache(`global:${finalConfig.slug}`);
|
|
882
|
-
}
|
|
883
|
-
addGlobals(configs) {
|
|
884
|
-
for (const config of configs) {
|
|
885
|
-
this.addGlobal(config);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
getGlobal(slug) {
|
|
889
|
-
return this.globals.get(slug);
|
|
890
|
-
}
|
|
891
|
-
getGlobals() {
|
|
892
|
-
return Array.from(this.globals.values());
|
|
893
|
-
}
|
|
894
|
-
getGlobalSlugs() {
|
|
895
|
-
return Array.from(this.globals.keys());
|
|
896
|
-
}
|
|
897
|
-
hasGlobal(slug) {
|
|
898
|
-
return this.globals.has(slug);
|
|
899
|
-
}
|
|
900
|
-
removeGlobal(slug) {
|
|
901
|
-
if (this.initialized) {
|
|
902
|
-
throw new Error(
|
|
903
|
-
"Cannot remove globals after Registry has been initialized"
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
this.clearSchemaCache(`global:${slug}`);
|
|
907
|
-
return this.globals.delete(slug);
|
|
908
|
-
}
|
|
909
|
-
// ========================================================================
|
|
910
|
-
// Plugin Management
|
|
911
|
-
// ========================================================================
|
|
912
|
-
addPlugin(plugin) {
|
|
913
|
-
if (this.initialized) {
|
|
914
|
-
throw new Error("Cannot add plugins after Registry has been initialized");
|
|
915
|
-
}
|
|
916
|
-
this.plugins.push(plugin);
|
|
917
|
-
}
|
|
918
|
-
getPlugins() {
|
|
919
|
-
return [...this.plugins];
|
|
920
|
-
}
|
|
921
|
-
// ========================================================================
|
|
922
|
-
// Schema Generation
|
|
923
|
-
// ========================================================================
|
|
924
|
-
getZodSchema(slug) {
|
|
925
|
-
const cached = this.schemaCache.get(slug);
|
|
926
|
-
if (cached) return cached;
|
|
927
|
-
const collection = this.collections.get(slug);
|
|
928
|
-
if (collection) {
|
|
929
|
-
const schema = collectionToZod(collection);
|
|
930
|
-
this.schemaCache.set(slug, schema);
|
|
931
|
-
return schema;
|
|
932
|
-
}
|
|
933
|
-
const global = this.globals.get(slug);
|
|
934
|
-
if (global) {
|
|
935
|
-
const schema = globalToZod(global);
|
|
936
|
-
this.schemaCache.set(`global:${slug}`, schema);
|
|
937
|
-
return schema;
|
|
938
|
-
}
|
|
939
|
-
throw new Error(`No collection or global found with slug "${slug}"`);
|
|
940
|
-
}
|
|
941
|
-
getCreateZodSchema(slug) {
|
|
942
|
-
const cacheKey = `${slug}:create`;
|
|
943
|
-
const cached = this.schemaCache.get(cacheKey);
|
|
944
|
-
if (cached) return cached;
|
|
945
|
-
const collection = this.collections.get(slug);
|
|
946
|
-
if (collection) {
|
|
947
|
-
const schema = collectionToCreateZod(collection);
|
|
948
|
-
this.schemaCache.set(cacheKey, schema);
|
|
949
|
-
return schema;
|
|
950
|
-
}
|
|
951
|
-
throw new Error(`No collection found with slug "${slug}"`);
|
|
952
|
-
}
|
|
953
|
-
getUpdateZodSchema(slug) {
|
|
954
|
-
const cacheKey = `${slug}:update`;
|
|
955
|
-
const cached = this.schemaCache.get(cacheKey);
|
|
956
|
-
if (cached) return cached;
|
|
957
|
-
const collection = this.collections.get(slug);
|
|
958
|
-
if (collection) {
|
|
959
|
-
const schema = collectionToUpdateZod(collection);
|
|
960
|
-
this.schemaCache.set(cacheKey, schema);
|
|
961
|
-
return schema;
|
|
962
|
-
}
|
|
963
|
-
const global = this.globals.get(slug);
|
|
964
|
-
if (global) {
|
|
965
|
-
const schema = globalToUpdateZod(global);
|
|
966
|
-
this.schemaCache.set(`global:${slug}:update`, schema);
|
|
967
|
-
return schema;
|
|
968
|
-
}
|
|
969
|
-
throw new Error(`No collection or global found with slug "${slug}"`);
|
|
970
|
-
}
|
|
971
|
-
getWhereZodSchema(slug) {
|
|
972
|
-
const cacheKey = `${slug}:where`;
|
|
973
|
-
const cached = this.schemaCache.get(cacheKey);
|
|
974
|
-
if (cached) return cached;
|
|
975
|
-
const collection = this.collections.get(slug);
|
|
976
|
-
if (collection) {
|
|
977
|
-
const schema = collectionToWhereZod(collection);
|
|
978
|
-
this.schemaCache.set(cacheKey, schema);
|
|
979
|
-
return schema;
|
|
980
|
-
}
|
|
981
|
-
throw new Error(`No collection found with slug "${slug}"`);
|
|
982
|
-
}
|
|
983
|
-
getFieldZodSchema(field) {
|
|
984
|
-
return fieldToZod(field);
|
|
985
|
-
}
|
|
986
|
-
clearSchemaCache(slug) {
|
|
987
|
-
this.schemaCache.delete(slug);
|
|
988
|
-
this.schemaCache.delete(`${slug}:create`);
|
|
989
|
-
this.schemaCache.delete(`${slug}:update`);
|
|
990
|
-
this.schemaCache.delete(`${slug}:where`);
|
|
991
|
-
}
|
|
992
|
-
// ========================================================================
|
|
993
|
-
// Field Helpers
|
|
994
|
-
// ========================================================================
|
|
995
|
-
applyFieldDefaults(config) {
|
|
996
|
-
const fields = [...config.fields];
|
|
997
|
-
if (!fields.some((f) => f.name === "id")) {
|
|
998
|
-
fields.unshift({
|
|
999
|
-
name: "id",
|
|
1000
|
-
type: "text",
|
|
1001
|
-
admin: { readOnly: true, hidden: true }
|
|
1002
|
-
});
|
|
1003
|
-
}
|
|
1004
|
-
if (config.tenantScoped && !fields.some((f) => f.name === "tenantID")) {
|
|
1005
|
-
fields.push({
|
|
1006
|
-
name: "tenantID",
|
|
1007
|
-
type: "text",
|
|
1008
|
-
required: true,
|
|
1009
|
-
admin: { readOnly: true, hidden: true }
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
if (config.timestamps && !fields.some((f) => f.name === "createdAt")) {
|
|
1013
|
-
fields.push({
|
|
1014
|
-
name: "createdAt",
|
|
1015
|
-
type: "date",
|
|
1016
|
-
admin: { readOnly: true, hidden: true }
|
|
1017
|
-
});
|
|
1018
|
-
fields.push({
|
|
1019
|
-
name: "updatedAt",
|
|
1020
|
-
type: "date",
|
|
1021
|
-
admin: { readOnly: true, hidden: true }
|
|
1022
|
-
});
|
|
1023
|
-
}
|
|
1024
|
-
if (config.versions?.drafts && !fields.some((f) => f.name === "status")) {
|
|
1025
|
-
fields.push({
|
|
1026
|
-
name: "status",
|
|
1027
|
-
type: "select",
|
|
1028
|
-
options: [
|
|
1029
|
-
{ value: "draft", label: "Draft" },
|
|
1030
|
-
{ value: "published", label: "Published" }
|
|
1031
|
-
],
|
|
1032
|
-
admin: { readOnly: true, hidden: true }
|
|
1033
|
-
});
|
|
1034
|
-
}
|
|
1035
|
-
if (config.auth && !fields.some((f) => f.name === "password")) {
|
|
1036
|
-
fields.push({
|
|
1037
|
-
name: "password",
|
|
1038
|
-
type: "password",
|
|
1039
|
-
required: true,
|
|
1040
|
-
admin: { hidden: true }
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
return fields;
|
|
1044
|
-
}
|
|
1045
|
-
getFields(slug) {
|
|
1046
|
-
const collection = this.collections.get(slug);
|
|
1047
|
-
if (collection) return collection.fields;
|
|
1048
|
-
const global = this.globals.get(slug);
|
|
1049
|
-
if (global) return global.fields;
|
|
1050
|
-
throw new Error(`No collection or global found with slug "${slug}"`);
|
|
1051
|
-
}
|
|
1052
|
-
getFieldMap(slug) {
|
|
1053
|
-
const fields = this.getFields(slug);
|
|
1054
|
-
const map = /* @__PURE__ */ new Map();
|
|
1055
|
-
const addFields = (fields2) => {
|
|
1056
|
-
for (const field of fields2) {
|
|
1057
|
-
if (field.name) {
|
|
1058
|
-
map.set(field.name, field);
|
|
1059
|
-
}
|
|
1060
|
-
if ("fields" in field && field.fields) {
|
|
1061
|
-
addFields(field.fields);
|
|
1062
|
-
}
|
|
1063
|
-
if ("tabs" in field) {
|
|
1064
|
-
for (const tab of field.tabs) {
|
|
1065
|
-
addFields(tab.fields);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
if ("blocks" in field) {
|
|
1069
|
-
for (const block of field.blocks) {
|
|
1070
|
-
addFields(block.fields);
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
};
|
|
1075
|
-
addFields(fields);
|
|
1076
|
-
return map;
|
|
1077
|
-
}
|
|
1078
|
-
getVisibleFields(slug) {
|
|
1079
|
-
const fields = this.getFields(slug);
|
|
1080
|
-
return fields.filter((f) => !f.admin?.hidden);
|
|
1081
|
-
}
|
|
1082
|
-
// ========================================================================
|
|
1083
|
-
// Initialization
|
|
1084
|
-
// ========================================================================
|
|
1085
|
-
validate() {
|
|
1086
|
-
const collections = this.getCollections();
|
|
1087
|
-
const globals = this.getGlobals();
|
|
1088
|
-
validateConfig(collections, globals);
|
|
1089
|
-
}
|
|
1090
|
-
async init() {
|
|
1091
|
-
this.validate();
|
|
1092
|
-
for (const plugin of this.plugins) {
|
|
1093
|
-
if (plugin.init) {
|
|
1094
|
-
await plugin.init(this);
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
this.initialized = true;
|
|
1098
|
-
}
|
|
1099
|
-
isInitialized() {
|
|
1100
|
-
return this.initialized;
|
|
1101
|
-
}
|
|
1102
|
-
// ========================================================================
|
|
1103
|
-
// Query Helpers
|
|
1104
|
-
// ========================================================================
|
|
1105
|
-
getPaginationDefaults(slug) {
|
|
1106
|
-
const collection = this.collections.get(slug);
|
|
1107
|
-
return {
|
|
1108
|
-
defaultLimit: collection?.admin?.pagination?.defaultLimit || 10,
|
|
1109
|
-
limits: collection?.admin?.pagination?.limits || [10, 25, 50, 100]
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
getDefaultSort(slug) {
|
|
1113
|
-
const collection = this.collections.get(slug);
|
|
1114
|
-
const useAsTitle = collection?.admin?.useAsTitle;
|
|
1115
|
-
if (useAsTitle) return useAsTitle;
|
|
1116
|
-
return "createdAt";
|
|
1117
|
-
}
|
|
1118
|
-
getDefaultColumns(slug) {
|
|
1119
|
-
const collection = this.collections.get(slug);
|
|
1120
|
-
if (collection?.admin?.defaultColumns) {
|
|
1121
|
-
return collection.admin.defaultColumns;
|
|
1122
|
-
}
|
|
1123
|
-
const fields = this.getVisibleFields(slug);
|
|
1124
|
-
return fields.slice(0, 4).map((f) => f.name);
|
|
1125
|
-
}
|
|
1126
|
-
// ========================================================================
|
|
1127
|
-
// Admin Helpers
|
|
1128
|
-
// ========================================================================
|
|
1129
|
-
getAdminTitle(slug) {
|
|
1130
|
-
const collection = this.collections.get(slug);
|
|
1131
|
-
return collection?.label || collection?.admin?.description || slug;
|
|
1132
|
-
}
|
|
1133
|
-
getAdminLabel(slug) {
|
|
1134
|
-
const collection = this.collections.get(slug);
|
|
1135
|
-
return collection?.singularLabel || collection?.label || slug;
|
|
1136
|
-
}
|
|
1137
|
-
getAdminGroup(slug) {
|
|
1138
|
-
return this.collections.get(slug)?.admin?.group;
|
|
1139
|
-
}
|
|
1140
|
-
// ========================================================================
|
|
1141
|
-
// Debug / Stats
|
|
1142
|
-
// ========================================================================
|
|
1143
|
-
getStats() {
|
|
1144
|
-
let totalFields = 0;
|
|
1145
|
-
for (const collection of this.collections.values()) {
|
|
1146
|
-
totalFields += collection.fields.length;
|
|
1147
|
-
}
|
|
1148
|
-
for (const global of this.globals.values()) {
|
|
1149
|
-
totalFields += global.fields.length;
|
|
1150
|
-
}
|
|
1151
|
-
return {
|
|
1152
|
-
collections: this.collections.size,
|
|
1153
|
-
globals: this.globals.size,
|
|
1154
|
-
plugins: this.plugins.length,
|
|
1155
|
-
fields: totalFields
|
|
1156
|
-
};
|
|
1157
|
-
}
|
|
1158
|
-
toJSON() {
|
|
1159
|
-
return {
|
|
1160
|
-
collections: this.getCollections(),
|
|
1161
|
-
globals: this.getGlobals()
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
};
|
|
1165
|
-
var instance = null;
|
|
1166
|
-
function getRegistry() {
|
|
1167
|
-
if (!instance) {
|
|
1168
|
-
instance = new Registry();
|
|
1169
|
-
}
|
|
1170
|
-
return instance;
|
|
1171
|
-
}
|
|
1172
|
-
function resetRegistry() {
|
|
1173
|
-
instance = null;
|
|
1174
|
-
}
|
|
1175
|
-
function createRegistry() {
|
|
1176
|
-
instance = new Registry();
|
|
1177
|
-
return instance;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
// src/plugins/storage-s3.ts
|
|
1181
|
-
var s3Variants = {
|
|
1182
|
-
aws: {
|
|
1183
|
-
type: "aws",
|
|
1184
|
-
configKey: "s3",
|
|
1185
|
-
displayName: "S3 Compatible (AWS, Backblaze, Wasabi, etc.)",
|
|
1186
|
-
configFields: [
|
|
1187
|
-
{ name: "bucket", type: "text", label: "Bucket Name", required: true },
|
|
1188
|
-
{ name: "region", type: "text", label: "Region", defaultValue: "us-east-1", admin: { placeholder: "us-east-1" } },
|
|
1189
|
-
{ name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
|
|
1190
|
-
{ name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
|
|
1191
|
-
{ name: "endpoint", type: "text", label: "Endpoint URL", admin: { placeholder: "https://s3.custom.com" } },
|
|
1192
|
-
{ name: "cdnUrl", type: "text", label: "CDN URL", admin: { placeholder: "https://cdn.example.com" } },
|
|
1193
|
-
{ name: "prefix", type: "text", label: "Path Prefix", admin: { placeholder: "uploads" } }
|
|
1194
|
-
]
|
|
1195
|
-
},
|
|
1196
|
-
r2: {
|
|
1197
|
-
type: "r2",
|
|
1198
|
-
configKey: "r2",
|
|
1199
|
-
displayName: "Cloudflare R2",
|
|
1200
|
-
configFields: [
|
|
1201
|
-
{ name: "accountId", type: "text", label: "Account ID", required: true, admin: { placeholder: "Your Cloudflare Account ID" } },
|
|
1202
|
-
{ name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
|
|
1203
|
-
{ name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
|
|
1204
|
-
{ name: "bucket", type: "text", label: "Bucket Name", required: true },
|
|
1205
|
-
{
|
|
1206
|
-
name: "publicDevUrl",
|
|
1207
|
-
type: "text",
|
|
1208
|
-
label: "Public Dev URL ID",
|
|
1209
|
-
admin: {
|
|
1210
|
-
placeholder: "pub-xxxxxxxxxxxxxxxx",
|
|
1211
|
-
description: "Enter ONLY the ID (e.g., pub-b8d8c4cc8bcf4d868ddd95efc1b305aa). Do NOT include https:// or the full URL. Found in R2 Dashboard \u2192 Public Dev URL."
|
|
1212
|
-
}
|
|
1213
|
-
},
|
|
1214
|
-
{ name: "cdnUrl", type: "text", label: "Custom CDN URL", admin: { placeholder: "https://assets.example.com (optional)" } },
|
|
1215
|
-
{ name: "prefix", type: "text", label: "Path Prefix", admin: { placeholder: "uploads (optional)", description: "Optional prefix for all object keys. Do not use '/' as prefix." } }
|
|
1216
|
-
]
|
|
1217
|
-
},
|
|
1218
|
-
gcs: {
|
|
1219
|
-
type: "gcs",
|
|
1220
|
-
configKey: "gcs",
|
|
1221
|
-
displayName: "Google Cloud Storage",
|
|
1222
|
-
configFields: [
|
|
1223
|
-
{ name: "bucket", type: "text", label: "Bucket Name", required: true },
|
|
1224
|
-
{ name: "projectId", type: "text", label: "Project ID" },
|
|
1225
|
-
{ name: "clientEmail", type: "text", label: "Client Email" },
|
|
1226
|
-
{ name: "privateKey", type: "password", label: "Private Key" },
|
|
1227
|
-
{ name: "cdnUrl", type: "text", label: "CDN URL" },
|
|
1228
|
-
{ name: "prefix", type: "text", label: "Path Prefix" }
|
|
1229
|
-
]
|
|
1230
|
-
},
|
|
1231
|
-
digitalocean: {
|
|
1232
|
-
type: "digitalocean",
|
|
1233
|
-
configKey: "digitalocean",
|
|
1234
|
-
displayName: "DigitalOcean Spaces",
|
|
1235
|
-
configFields: [
|
|
1236
|
-
{ name: "bucket", type: "text", label: "Bucket Name", required: true },
|
|
1237
|
-
{ name: "region", type: "text", label: "Region", defaultValue: "nyc3" },
|
|
1238
|
-
{ name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
|
|
1239
|
-
{ name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
|
|
1240
|
-
{ name: "cdnUrl", type: "text", label: "CDN URL" },
|
|
1241
|
-
{ name: "prefix", type: "text", label: "Path Prefix" }
|
|
1242
|
-
]
|
|
1243
|
-
},
|
|
1244
|
-
backblaze: {
|
|
1245
|
-
type: "backblaze",
|
|
1246
|
-
configKey: "backblaze",
|
|
1247
|
-
displayName: "Backblaze B2",
|
|
1248
|
-
configFields: [
|
|
1249
|
-
{ name: "bucket", type: "text", label: "Bucket Name", required: true },
|
|
1250
|
-
{ name: "accountId", type: "text", label: "Account ID" },
|
|
1251
|
-
{ name: "applicationKeyId", type: "text", label: "Application Key ID", required: true },
|
|
1252
|
-
{ name: "applicationKey", type: "password", label: "Application Key", required: true },
|
|
1253
|
-
{ name: "cdnUrl", type: "text", label: "CDN URL" },
|
|
1254
|
-
{ name: "prefix", type: "text", label: "Path Prefix" }
|
|
1255
|
-
]
|
|
1256
|
-
},
|
|
1257
|
-
wasabi: {
|
|
1258
|
-
type: "wasabi",
|
|
1259
|
-
configKey: "wasabi",
|
|
1260
|
-
displayName: "Wasabi",
|
|
1261
|
-
configFields: [
|
|
1262
|
-
{ name: "bucket", type: "text", label: "Bucket Name", required: true },
|
|
1263
|
-
{ name: "region", type: "text", label: "Region", defaultValue: "us-east-1" },
|
|
1264
|
-
{ name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
|
|
1265
|
-
{ name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
|
|
1266
|
-
{ name: "cdnUrl", type: "text", label: "CDN URL" },
|
|
1267
|
-
{ name: "prefix", type: "text", label: "Path Prefix" }
|
|
1268
|
-
]
|
|
1269
|
-
}
|
|
1270
|
-
};
|
|
1271
|
-
function getEndpoint(type, config) {
|
|
1272
|
-
switch (type) {
|
|
1273
|
-
case "r2":
|
|
1274
|
-
return config?.endpoint || `https://${config?.accountId || ""}.r2.cloudflarestorage.com`;
|
|
1275
|
-
case "digitalocean":
|
|
1276
|
-
return config?.endpoint || `https://${config?.region || "nyc3"}.digitaloceanspaces.com`;
|
|
1277
|
-
case "backblaze":
|
|
1278
|
-
return config?.endpoint || `https://s3.backblazeb2.com`;
|
|
1279
|
-
case "wasabi":
|
|
1280
|
-
return config?.endpoint || `https://s3.${config?.region || "us-east-1"}.wasabisys.com`;
|
|
1281
|
-
default:
|
|
1282
|
-
return config?.endpoint;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
function buildS3Config(type, c) {
|
|
1286
|
-
return {
|
|
1287
|
-
provider: type,
|
|
1288
|
-
bucket: c?.bucket || "",
|
|
1289
|
-
region: c?.region || "us-east-1",
|
|
1290
|
-
accessKeyId: c?.accessKeyId || c?.clientEmail || c?.applicationKeyId || "",
|
|
1291
|
-
secretAccessKey: c?.secretAccessKey || c?.privateKey || c?.applicationKey || "",
|
|
1292
|
-
endpoint: getEndpoint(type, c),
|
|
1293
|
-
cdnUrl: c?.cdnUrl,
|
|
1294
|
-
prefix: c?.prefix,
|
|
1295
|
-
accountId: c?.accountId,
|
|
1296
|
-
publicDevUrl: c?.publicDevUrl
|
|
1297
|
-
};
|
|
1298
|
-
}
|
|
1299
|
-
function buildS3ConfigFromStorageConfig(type, def, sc) {
|
|
1300
|
-
const c = sc[def.configKey] || {};
|
|
1301
|
-
return buildS3Config(type, c);
|
|
1302
|
-
}
|
|
1303
|
-
function buildS3ConfigFromRaw(type, def, raw) {
|
|
1304
|
-
const c = raw?.[def.configKey] || raw;
|
|
1305
|
-
return buildS3Config(type, c);
|
|
1306
|
-
}
|
|
1307
|
-
var s3StoragePlugin = {
|
|
1308
|
-
name: "@kyro-cms/storage-s3",
|
|
1309
|
-
version: "1.0.0",
|
|
1310
|
-
description: "S3-compatible storage (AWS R2 GCS DigitalOcean Backblaze Wasabi)",
|
|
1311
|
-
init: (kyro) => {
|
|
1312
|
-
const registry = kyro.storageProviders;
|
|
1313
|
-
if (!registry) return;
|
|
1314
|
-
const pluginName = "@kyro-cms/storage-s3";
|
|
1315
|
-
for (const v of Object.values(s3Variants)) {
|
|
1316
|
-
registry.register({
|
|
1317
|
-
type: v.type,
|
|
1318
|
-
displayName: v.displayName,
|
|
1319
|
-
pluginName,
|
|
1320
|
-
configKey: v.configKey,
|
|
1321
|
-
configFields: v.configFields,
|
|
1322
|
-
extractConfig: (sc) => buildS3ConfigFromStorageConfig(v.type, v, sc),
|
|
1323
|
-
extractRawConfig: (raw) => buildS3ConfigFromRaw(v.type, v, raw),
|
|
1324
|
-
factory: (c) => chunkB6X3LN25_cjs.createS3Storage(c)
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
};
|
|
1329
|
-
|
|
1330
|
-
// src/plugins/storage-cloudinary.ts
|
|
1331
|
-
var cloudinaryStoragePlugin = {
|
|
1332
|
-
name: "@kyro-cms/storage-cloudinary",
|
|
1333
|
-
version: "1.0.0",
|
|
1334
|
-
description: "Cloudinary image and video storage",
|
|
1335
|
-
init: (kyro) => {
|
|
1336
|
-
const registry = kyro.storageProviders;
|
|
1337
|
-
if (!registry) return;
|
|
1338
|
-
registry.register({
|
|
1339
|
-
type: "cloudinary",
|
|
1340
|
-
displayName: "Cloudinary",
|
|
1341
|
-
pluginName: "@kyro-cms/storage-cloudinary",
|
|
1342
|
-
configKey: "cloudinary",
|
|
1343
|
-
configFields: [
|
|
1344
|
-
{ name: "cloudName", type: "text", label: "Cloud Name", required: true },
|
|
1345
|
-
{ name: "apiKey", type: "text", label: "API Key", required: true },
|
|
1346
|
-
{ name: "apiSecret", type: "password", label: "API Secret", required: true },
|
|
1347
|
-
{ name: "folder", type: "text", label: "Folder", admin: { placeholder: "Optional folder path" } },
|
|
1348
|
-
{
|
|
1349
|
-
name: "uploadPreset",
|
|
1350
|
-
type: "text",
|
|
1351
|
-
label: "Upload Preset (optional)",
|
|
1352
|
-
admin: { placeholder: "Leave empty for signed uploads", description: "If not set, uploads will be signed with API Secret" }
|
|
1353
|
-
}
|
|
1354
|
-
],
|
|
1355
|
-
extractConfig: (sc) => ({
|
|
1356
|
-
cloudName: sc.cloudinary?.cloudName || "",
|
|
1357
|
-
apiKey: sc.cloudinary?.apiKey || "",
|
|
1358
|
-
apiSecret: sc.cloudinary?.apiSecret || "",
|
|
1359
|
-
folder: sc.cloudinary?.folder,
|
|
1360
|
-
uploadPreset: sc.cloudinary?.uploadPreset
|
|
1361
|
-
}),
|
|
1362
|
-
extractRawConfig: (c) => {
|
|
1363
|
-
const cl = c?.cloudinary || c;
|
|
1364
|
-
return {
|
|
1365
|
-
cloudName: cl?.cloudName || "",
|
|
1366
|
-
apiKey: cl?.apiKey || "",
|
|
1367
|
-
apiSecret: cl?.apiSecret || "",
|
|
1368
|
-
folder: cl?.folder,
|
|
1369
|
-
uploadPreset: cl?.uploadPreset
|
|
1370
|
-
};
|
|
1371
|
-
},
|
|
1372
|
-
factory: (c) => chunkB6X3LN25_cjs.createCloudinaryStorage(c)
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
|
-
};
|
|
1376
|
-
|
|
1377
|
-
// src/plugins/storage-ftp.ts
|
|
1378
|
-
var ftpStoragePlugin = {
|
|
1379
|
-
name: "@kyro-cms/storage-ftp",
|
|
1380
|
-
version: "1.0.0",
|
|
1381
|
-
description: "FTP/SFTP storage provider",
|
|
1382
|
-
init: (kyro) => {
|
|
1383
|
-
const registry = kyro.storageProviders;
|
|
1384
|
-
if (!registry) return;
|
|
1385
|
-
registry.register({
|
|
1386
|
-
type: "ftp",
|
|
1387
|
-
displayName: "FTP",
|
|
1388
|
-
pluginName: "@kyro-cms/storage-ftp",
|
|
1389
|
-
configKey: "ftp",
|
|
1390
|
-
configFields: [
|
|
1391
|
-
{ name: "host", type: "text", label: "Host", required: true, admin: { placeholder: "ftp.example.com" } },
|
|
1392
|
-
{ name: "port", type: "number", label: "Port", defaultValue: 21, admin: { placeholder: "21 for FTP" } },
|
|
1393
|
-
{ name: "user", type: "text", label: "Username", required: true },
|
|
1394
|
-
{ name: "password", type: "password", label: "Password", required: true },
|
|
1395
|
-
{ name: "secure", type: "checkbox", label: "Use TLS/SSL", defaultValue: false, admin: { description: "Enable TLS/SSL for secure connections (FTP only)" } },
|
|
1396
|
-
{ name: "baseUrl", type: "text", label: "Base URL", required: true, admin: { placeholder: "https://files.example.com" } },
|
|
1397
|
-
{ name: "prefix", type: "text", label: "Path Prefix", admin: { placeholder: "uploads" } }
|
|
1398
|
-
],
|
|
1399
|
-
extractConfig: (sc) => ({
|
|
1400
|
-
host: sc.ftp?.host || "",
|
|
1401
|
-
port: sc.ftp?.port || 21,
|
|
1402
|
-
user: sc.ftp?.user || "",
|
|
1403
|
-
password: sc.ftp?.password || "",
|
|
1404
|
-
secure: sc.ftp?.secure || false,
|
|
1405
|
-
baseUrl: sc.ftp?.baseUrl || "",
|
|
1406
|
-
prefix: sc.ftp?.prefix,
|
|
1407
|
-
type: "ftp"
|
|
1408
|
-
}),
|
|
1409
|
-
extractRawConfig: (c) => {
|
|
1410
|
-
const ftp = c?.ftp || c;
|
|
1411
|
-
return {
|
|
1412
|
-
host: ftp?.host || "",
|
|
1413
|
-
port: ftp?.port || 21,
|
|
1414
|
-
user: ftp?.user || "",
|
|
1415
|
-
password: ftp?.password || "",
|
|
1416
|
-
secure: ftp?.secure || false,
|
|
1417
|
-
baseUrl: ftp?.baseUrl || "",
|
|
1418
|
-
prefix: ftp?.prefix,
|
|
1419
|
-
type: "ftp"
|
|
1420
|
-
};
|
|
1421
|
-
},
|
|
1422
|
-
factory: (c) => chunkB6X3LN25_cjs.createFtpStorage(c)
|
|
1423
|
-
});
|
|
1424
|
-
}
|
|
1425
|
-
};
|
|
1426
|
-
var builtinStoragePlugins = [
|
|
1427
|
-
s3StoragePlugin,
|
|
1428
|
-
cloudinaryStoragePlugin,
|
|
1429
|
-
ftpStoragePlugin
|
|
1430
|
-
];
|
|
1431
|
-
function updateFieldByPath(fields, path, updates) {
|
|
1432
|
-
const parts = path.split(".");
|
|
1433
|
-
if (parts.length === 0) return false;
|
|
1434
|
-
const currentPart = parts[0];
|
|
1435
|
-
const remainingPath = parts.slice(1).join(".");
|
|
1436
|
-
for (const field of fields) {
|
|
1437
|
-
if (field.name === currentPart) {
|
|
1438
|
-
if (remainingPath) {
|
|
1439
|
-
if (field.fields && Array.isArray(field.fields)) {
|
|
1440
|
-
return updateFieldByPath(field.fields, remainingPath, updates);
|
|
1441
|
-
}
|
|
1442
|
-
if (field.type === "array" && field.fields && Array.isArray(field.fields)) {
|
|
1443
|
-
return updateFieldByPath(field.fields, remainingPath, updates);
|
|
1444
|
-
}
|
|
1445
|
-
return false;
|
|
1446
|
-
} else {
|
|
1447
|
-
Object.assign(field, updates);
|
|
1448
|
-
return true;
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
return false;
|
|
1453
|
-
}
|
|
1454
|
-
function applyCollectionOverrides(collections, overrides) {
|
|
1455
|
-
if (!overrides) return;
|
|
1456
|
-
for (const col of collections) {
|
|
1457
|
-
const override = overrides[col.slug];
|
|
1458
|
-
if (override) {
|
|
1459
|
-
const { fields: fieldOverrides, ...adminOverrides } = override;
|
|
1460
|
-
col.admin = { ...col.admin, ...adminOverrides };
|
|
1461
|
-
if (fieldOverrides && col.fields && Array.isArray(col.fields)) {
|
|
1462
|
-
for (const [fieldPath, fieldUpdates] of Object.entries(fieldOverrides)) {
|
|
1463
|
-
updateFieldByPath(col.fields, fieldPath, fieldUpdates);
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
var Kyro = class {
|
|
1470
|
-
registry;
|
|
1471
|
-
db;
|
|
1472
|
-
pubsub;
|
|
1473
|
-
webhookService;
|
|
1474
|
-
settings;
|
|
1475
|
-
wsServer;
|
|
1476
|
-
config;
|
|
1477
|
-
constructor(config) {
|
|
1478
|
-
this.config = config;
|
|
1479
|
-
this.registry = createRegistry();
|
|
1480
|
-
this.db = config.adapter;
|
|
1481
|
-
this.pubsub = new chunkEEJUFDMF_cjs.KyroPubSub(this.registry);
|
|
1482
|
-
this.webhookService = chunkLNTFRAHF_cjs.createWebhookService(this.db);
|
|
1483
|
-
if (config.collections && config.admin?.collectionOverrides) {
|
|
1484
|
-
applyCollectionOverrides(config.collections, config.admin.collectionOverrides);
|
|
1485
|
-
}
|
|
1486
|
-
if (config.collections) {
|
|
1487
|
-
this.registry.addCollections(config.collections);
|
|
1488
|
-
}
|
|
1489
|
-
if (config.globals) {
|
|
1490
|
-
this.registry.addGlobals(config.globals);
|
|
1491
|
-
}
|
|
1492
|
-
for (const plugin of builtinStoragePlugins) {
|
|
1493
|
-
this.registry.addPlugin(plugin);
|
|
1494
|
-
}
|
|
1495
|
-
if (config.plugins) {
|
|
1496
|
-
for (const plugin of config.plugins) {
|
|
1497
|
-
this.registry.addPlugin(plugin);
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
async init() {
|
|
1502
|
-
await this.registry.init();
|
|
1503
|
-
const storageGlobal = chunk3KTWGODI_cjs.createStorageSettingsGlobal(
|
|
1504
|
-
this.registry.storageProviders,
|
|
1505
|
-
(name) => this.registry.storageProviders.isPluginEnabled(name)
|
|
1506
|
-
);
|
|
1507
|
-
this.registry.addGlobalPostInit(storageGlobal);
|
|
1508
|
-
const pluginSettingsGlobal = {
|
|
1509
|
-
slug: "plugin-settings",
|
|
1510
|
-
admin: { hidden: true },
|
|
1511
|
-
fields: [{ name: "states", type: "json" }]
|
|
1512
|
-
};
|
|
1513
|
-
this.registry.addGlobalPostInit(pluginSettingsGlobal);
|
|
1514
|
-
if (!this.db) {
|
|
1515
|
-
throw new Error(
|
|
1516
|
-
`Database adapter is null \u2014 failed to load at startup. Check the server console for the exact error.`
|
|
1517
|
-
);
|
|
1518
|
-
}
|
|
1519
|
-
const systemCollection = {
|
|
1520
|
-
slug: chunk4OCMVGEY_cjs.API_KEY_COLLECTION,
|
|
1521
|
-
fields: [
|
|
1522
|
-
{ name: "userId", type: "text", required: true },
|
|
1523
|
-
{ name: "name", type: "text", required: true },
|
|
1524
|
-
{ name: "key", type: "text", required: true },
|
|
1525
|
-
{ name: "keyPrefix", type: "text", required: true },
|
|
1526
|
-
{ name: "permissions", type: "json" },
|
|
1527
|
-
{ name: "lastUsedAt", type: "date" },
|
|
1528
|
-
{ name: "expiresAt", type: "date" }
|
|
1529
|
-
]
|
|
1530
|
-
};
|
|
1531
|
-
const webhookCollection = {
|
|
1532
|
-
slug: chunkLNTFRAHF_cjs.WEBHOOK_COLLECTION,
|
|
1533
|
-
fields: [
|
|
1534
|
-
{ name: "name", type: "text", required: true },
|
|
1535
|
-
{ name: "url", type: "text", required: true },
|
|
1536
|
-
{ name: "events", type: "json", required: true },
|
|
1537
|
-
{ name: "status", type: "text", required: true },
|
|
1538
|
-
{ name: "secret", type: "text" },
|
|
1539
|
-
{ name: "headers", type: "json" },
|
|
1540
|
-
{ name: "lastTriggered", type: "date" },
|
|
1541
|
-
{ name: "lastError", type: "text" }
|
|
1542
|
-
]
|
|
1543
|
-
};
|
|
1544
|
-
const webhookDeliveryCollection = {
|
|
1545
|
-
slug: chunkLNTFRAHF_cjs.WEBHOOK_DELIVERY_COLLECTION,
|
|
1546
|
-
fields: [
|
|
1547
|
-
{ name: "webhookId", type: "text", required: true },
|
|
1548
|
-
{ name: "event", type: "text", required: true },
|
|
1549
|
-
{ name: "payload", type: "json", required: true },
|
|
1550
|
-
{ name: "attempt", type: "number", required: true },
|
|
1551
|
-
{ name: "status", type: "text", required: true },
|
|
1552
|
-
{ name: "responseStatus", type: "number" },
|
|
1553
|
-
{ name: "responseBody", type: "text" },
|
|
1554
|
-
{ name: "error", type: "text" },
|
|
1555
|
-
{ name: "duration", type: "number" },
|
|
1556
|
-
{ name: "deliveredAt", type: "date" },
|
|
1557
|
-
{ name: "nextRetryAt", type: "date" }
|
|
1558
|
-
]
|
|
1559
|
-
};
|
|
1560
|
-
const allGlobals = this.registry.getGlobals();
|
|
1561
|
-
if (this.db instanceof chunkOIRG6ZRV_cjs.DrizzleAdapter && this.db.dialect === "postgres") {
|
|
1562
|
-
const drizzle = this.db;
|
|
1563
|
-
const tableName = chunk4OCMVGEY_cjs.API_KEY_COLLECTION.replace(/-/g, "_");
|
|
1564
|
-
if (!drizzle.schema[tableName]) {
|
|
1565
|
-
drizzle.schema[tableName] = chunkDPA3KWPY_cjs.apiKeys;
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
await this.db.init(
|
|
1569
|
-
[
|
|
1570
|
-
...this.registry.getCollections(),
|
|
1571
|
-
systemCollection,
|
|
1572
|
-
webhookCollection,
|
|
1573
|
-
webhookDeliveryCollection
|
|
1574
|
-
],
|
|
1575
|
-
allGlobals
|
|
1576
|
-
);
|
|
1577
|
-
await this.loadPluginState();
|
|
1578
|
-
this.pubsub.autoRegisterHooks();
|
|
1579
|
-
}
|
|
1580
|
-
// ============================================================================
|
|
1581
|
-
// API Methods
|
|
1582
|
-
// ============================================================================
|
|
1583
|
-
// Load settings from globals if not already loaded
|
|
1584
|
-
async loadSettings() {
|
|
1585
|
-
if (this.settings) return this.settings;
|
|
1586
|
-
try {
|
|
1587
|
-
const doc = await this.db.findOne({
|
|
1588
|
-
collection: "_globals_access-settings",
|
|
1589
|
-
where: {}
|
|
1590
|
-
});
|
|
1591
|
-
if (doc) {
|
|
1592
|
-
this.settings = { access: doc };
|
|
1593
|
-
}
|
|
1594
|
-
} catch (e) {
|
|
1595
|
-
}
|
|
1596
|
-
return this.settings || {};
|
|
1597
|
-
}
|
|
1598
|
-
async loadPluginState() {
|
|
1599
|
-
const storageRegistry = this.registry.storageProviders;
|
|
1600
|
-
const pluginNames = storageRegistry.getAllPluginNames();
|
|
1601
|
-
let pluginStates = {};
|
|
1602
|
-
try {
|
|
1603
|
-
const doc = await this.db.findOne({
|
|
1604
|
-
collection: "_globals_plugin-settings",
|
|
1605
|
-
where: {}
|
|
1606
|
-
});
|
|
1607
|
-
if (doc && doc.states) {
|
|
1608
|
-
pluginStates = doc.states;
|
|
1609
|
-
}
|
|
1610
|
-
} catch (e) {
|
|
1611
|
-
}
|
|
1612
|
-
for (const name of pluginNames) {
|
|
1613
|
-
if (pluginStates[name] !== void 0) {
|
|
1614
|
-
storageRegistry.setPluginEnabled(name, pluginStates[name]);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
getREST(options) {
|
|
1619
|
-
const authObj = typeof this.config.auth === "object" ? this.config.auth : null;
|
|
1620
|
-
const authSecret = authObj?.secret;
|
|
1621
|
-
const checkSession = authObj?.checkSession !== false;
|
|
1622
|
-
return chunkB6X3LN25_cjs.createHonoApp({
|
|
1623
|
-
registry: this.registry,
|
|
1624
|
-
db: this.db,
|
|
1625
|
-
authSecret,
|
|
1626
|
-
authAdapter: this.config.authAdapter,
|
|
1627
|
-
checkSession,
|
|
1628
|
-
...options,
|
|
1629
|
-
cors: this.config.cors,
|
|
1630
|
-
webhookService: this.webhookService,
|
|
1631
|
-
settings: this.settings
|
|
1632
|
-
});
|
|
1633
|
-
}
|
|
1634
|
-
getGraphQL(options) {
|
|
1635
|
-
const schema = chunkN4GN6Q3V_cjs.buildGraphQLSchema({
|
|
1636
|
-
registry: this.registry,
|
|
1637
|
-
db: this.db,
|
|
1638
|
-
settings: this.settings
|
|
1639
|
-
});
|
|
1640
|
-
const yoga = graphqlYoga.createYoga({
|
|
1641
|
-
schema,
|
|
1642
|
-
context: async ({ request }) => {
|
|
1643
|
-
const apiKeyRaw = chunk4OCMVGEY_cjs.extractApiKeyFromRequest(request);
|
|
1644
|
-
let gqlUser;
|
|
1645
|
-
let apiKeyCtx;
|
|
1646
|
-
if (apiKeyRaw && this.db) {
|
|
1647
|
-
const apiKeyResult = await chunk4OCMVGEY_cjs.validateApiKey(apiKeyRaw, this.db);
|
|
1648
|
-
if (apiKeyResult.user) {
|
|
1649
|
-
gqlUser = apiKeyResult.user;
|
|
1650
|
-
apiKeyCtx = chunk4OCMVGEY_cjs.createApiKeyContext(apiKeyResult);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
const tenantID = options?.tenantID;
|
|
1654
|
-
return {
|
|
1655
|
-
db: this.db,
|
|
1656
|
-
registry: this.registry,
|
|
1657
|
-
user: gqlUser,
|
|
1658
|
-
req: request,
|
|
1659
|
-
tenantID,
|
|
1660
|
-
apiKey: apiKeyCtx,
|
|
1661
|
-
relationLoader: new chunkN4GN6Q3V_cjs.RelationLoader({ db: this.db, tenantID, user: gqlUser })
|
|
1662
|
-
};
|
|
1663
|
-
},
|
|
1664
|
-
graphiql: false,
|
|
1665
|
-
plugins: []
|
|
1666
|
-
});
|
|
1667
|
-
return {
|
|
1668
|
-
fetch: yoga.fetch,
|
|
1669
|
-
schema
|
|
1670
|
-
};
|
|
1671
|
-
}
|
|
1672
|
-
getTRPC(options) {
|
|
1673
|
-
return {
|
|
1674
|
-
fetch: async (request, locals) => {
|
|
1675
|
-
const url = new URL(request.url);
|
|
1676
|
-
const path = url.pathname.replace(/^\/api\/trpc\//, "");
|
|
1677
|
-
const [slug, ...rest] = path.split(".");
|
|
1678
|
-
let procedureName = rest.join(".");
|
|
1679
|
-
procedureName = procedureName.replace(/\.(query|mutate|subscribe)$/, "");
|
|
1680
|
-
if (!slug || !procedureName) {
|
|
1681
|
-
return new Response(
|
|
1682
|
-
JSON.stringify({
|
|
1683
|
-
error: {
|
|
1684
|
-
message: "Invalid tRPC path",
|
|
1685
|
-
code: -32600,
|
|
1686
|
-
data: { code: "BAD_REQUEST", httpStatus: 400 }
|
|
1687
|
-
}
|
|
1688
|
-
}),
|
|
1689
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
1690
|
-
);
|
|
1691
|
-
}
|
|
1692
|
-
const ctx = await chunkTQG7G7YI_cjs.createContext({
|
|
1693
|
-
db: this.db,
|
|
1694
|
-
registry: this.registry,
|
|
1695
|
-
req: request,
|
|
1696
|
-
user: options?.user,
|
|
1697
|
-
tenantID: options?.tenantID,
|
|
1698
|
-
settings: this.settings
|
|
1699
|
-
});
|
|
1700
|
-
const kyroRouter = chunkTQG7G7YI_cjs.createKyroServer(ctx);
|
|
1701
|
-
const collectionRouter = kyroRouter[slug];
|
|
1702
|
-
if (!collectionRouter) {
|
|
1703
|
-
return new Response(
|
|
1704
|
-
JSON.stringify({
|
|
1705
|
-
error: {
|
|
1706
|
-
message: `Collection '${slug}' not found`,
|
|
1707
|
-
code: -32601,
|
|
1708
|
-
data: { code: "NOT_FOUND", httpStatus: 404 }
|
|
1709
|
-
}
|
|
1710
|
-
}),
|
|
1711
|
-
{ status: 404, headers: { "Content-Type": "application/json" } }
|
|
1712
|
-
);
|
|
1713
|
-
}
|
|
1714
|
-
const procedure = collectionRouter[procedureName];
|
|
1715
|
-
if (typeof procedure !== "function") {
|
|
1716
|
-
return new Response(
|
|
1717
|
-
JSON.stringify({
|
|
1718
|
-
error: {
|
|
1719
|
-
message: `Procedure '${procedureName}' not found`,
|
|
1720
|
-
code: -32601,
|
|
1721
|
-
data: { code: "NOT_FOUND", httpStatus: 404 }
|
|
1722
|
-
}
|
|
1723
|
-
}),
|
|
1724
|
-
{ status: 404, headers: { "Content-Type": "application/json" } }
|
|
1725
|
-
);
|
|
1726
|
-
}
|
|
1727
|
-
try {
|
|
1728
|
-
let raw = {};
|
|
1729
|
-
if (request.method === "POST" || request.method === "PATCH") {
|
|
1730
|
-
raw = await request.json().catch(() => ({}));
|
|
1731
|
-
} else {
|
|
1732
|
-
const qs = new URL(request.url).searchParams.get("input");
|
|
1733
|
-
if (qs) {
|
|
1734
|
-
try {
|
|
1735
|
-
raw = JSON.parse(decodeURIComponent(qs));
|
|
1736
|
-
} catch {
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
const input = raw?.["0"] ?? raw;
|
|
1741
|
-
const result = await procedure({ ...input, collection: slug });
|
|
1742
|
-
return new Response(JSON.stringify({ result: { data: result } }), {
|
|
1743
|
-
status: 200,
|
|
1744
|
-
headers: { "Content-Type": "application/json" }
|
|
1745
|
-
});
|
|
1746
|
-
} catch (err) {
|
|
1747
|
-
const msg = err.message || "Internal error";
|
|
1748
|
-
const httpStatus = msg.includes("not found") ? 404 : msg.includes("denied") || msg.includes("authentication required") ? 403 : msg.includes("conflict") ? 409 : 500;
|
|
1749
|
-
const code = httpStatus === 404 ? -32601 : httpStatus === 403 ? -32001 : httpStatus === 409 ? -32002 : -32603;
|
|
1750
|
-
return new Response(
|
|
1751
|
-
JSON.stringify({
|
|
1752
|
-
error: {
|
|
1753
|
-
message: msg,
|
|
1754
|
-
code,
|
|
1755
|
-
data: {
|
|
1756
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
1757
|
-
httpStatus
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
}),
|
|
1761
|
-
{ status: httpStatus, headers: { "Content-Type": "application/json" } }
|
|
1762
|
-
);
|
|
1763
|
-
}
|
|
1764
|
-
},
|
|
1765
|
-
router: null
|
|
1766
|
-
};
|
|
1767
|
-
}
|
|
1768
|
-
getWS() {
|
|
1769
|
-
return this.wsServer;
|
|
1770
|
-
}
|
|
1771
|
-
async startWebSocket(options) {
|
|
1772
|
-
const apiAccess = this.settings?.access?.apiAccess;
|
|
1773
|
-
if (apiAccess?.wsEnabled === false) {
|
|
1774
|
-
return null;
|
|
1775
|
-
}
|
|
1776
|
-
const defaultVerifyToken = async (token) => {
|
|
1777
|
-
const result = await chunk4OCMVGEY_cjs.validateApiKey(token, this.db);
|
|
1778
|
-
if (!result.valid) throw new Error(result.error || "Invalid API key");
|
|
1779
|
-
if (!result.user) throw new Error("API key has no associated user");
|
|
1780
|
-
return result.user;
|
|
1781
|
-
};
|
|
1782
|
-
this.wsServer = chunkEEJUFDMF_cjs.createWSServer({
|
|
1783
|
-
pubsub: this.pubsub,
|
|
1784
|
-
port: options?.port || 8080,
|
|
1785
|
-
requireAuth: options?.requireAuth ?? apiAccess?.requireAuth,
|
|
1786
|
-
verifyToken: options?.verifyToken || defaultVerifyToken
|
|
1787
|
-
});
|
|
1788
|
-
return this.wsServer;
|
|
1789
|
-
}
|
|
1790
|
-
// ============================================================================
|
|
1791
|
-
// Lifecycle
|
|
1792
|
-
// ============================================================================
|
|
1793
|
-
async shutdown() {
|
|
1794
|
-
if (this.wsServer) {
|
|
1795
|
-
await this.wsServer.close();
|
|
1796
|
-
}
|
|
1797
|
-
await this.db.disconnect();
|
|
1798
|
-
}
|
|
1799
|
-
};
|
|
1800
|
-
function createKyro(config) {
|
|
1801
|
-
return new Kyro(config);
|
|
1802
|
-
}
|
|
1803
|
-
var _require = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-74YSDAWE.cjs', document.baseURI).href)));
|
|
1804
|
-
var modPath = "node:sqlite";
|
|
1805
|
-
var { DatabaseSync } = _require(modPath);
|
|
1806
|
-
function flattenFields(fields) {
|
|
1807
|
-
const result = [];
|
|
1808
|
-
for (const field of fields) {
|
|
1809
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
1810
|
-
for (const tab of field.tabs) {
|
|
1811
|
-
result.push(...flattenFields(tab.fields));
|
|
1812
|
-
}
|
|
1813
|
-
} else if (field.type === "row" && "fields" in field) {
|
|
1814
|
-
result.push(...flattenFields(field.fields));
|
|
1815
|
-
} else if (field.type === "collapsible" && "fields" in field) {
|
|
1816
|
-
result.push(...flattenFields(field.fields));
|
|
1817
|
-
} else {
|
|
1818
|
-
result.push(field);
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
return result;
|
|
1822
|
-
}
|
|
1823
|
-
function processFieldValue(row, field) {
|
|
1824
|
-
const f = field;
|
|
1825
|
-
let value = row[f.name];
|
|
1826
|
-
if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "group" || f.type === "blocks" || f.type === "list" || f.type === "relationship-block") {
|
|
1827
|
-
try {
|
|
1828
|
-
value = value ? JSON.parse(value) : null;
|
|
1829
|
-
} catch {
|
|
1830
|
-
value = null;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
if (f.type === "checkbox") {
|
|
1834
|
-
value = Boolean(value);
|
|
1835
|
-
}
|
|
1836
|
-
if (f.type === "date" && value) {
|
|
1837
|
-
try {
|
|
1838
|
-
const d = new Date(value);
|
|
1839
|
-
if (isNaN(d.getTime())) {
|
|
1840
|
-
value = null;
|
|
1841
|
-
} else {
|
|
1842
|
-
value = d.toISOString();
|
|
1843
|
-
}
|
|
1844
|
-
} catch {
|
|
1845
|
-
value = null;
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
if ((f.type === "upload" || f.type === "image") && value) {
|
|
1849
|
-
try {
|
|
1850
|
-
const parsed = JSON.parse(value);
|
|
1851
|
-
if (Array.isArray(parsed)) {
|
|
1852
|
-
value = parsed.map((item) => {
|
|
1853
|
-
if (typeof item === "object" && item !== null) {
|
|
1854
|
-
return item;
|
|
1855
|
-
}
|
|
1856
|
-
return { id: item };
|
|
1857
|
-
});
|
|
1858
|
-
} else {
|
|
1859
|
-
value = typeof parsed === "object" ? parsed : { id: parsed };
|
|
1860
|
-
}
|
|
1861
|
-
} catch {
|
|
1862
|
-
value = { id: value };
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
if (f.type === "relationship" && value) {
|
|
1866
|
-
try {
|
|
1867
|
-
const parsed = JSON.parse(value);
|
|
1868
|
-
if (Array.isArray(parsed)) {
|
|
1869
|
-
value = parsed;
|
|
1870
|
-
} else {
|
|
1871
|
-
value = parsed;
|
|
1872
|
-
}
|
|
1873
|
-
} catch {
|
|
1874
|
-
value = { relationTo: Array.isArray(f.relationTo) ? f.relationTo[0] : f.relationTo, value };
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
if (f.type === "list" && value) {
|
|
1878
|
-
try {
|
|
1879
|
-
const parsed = JSON.parse(value);
|
|
1880
|
-
value = Array.isArray(parsed) ? parsed : [];
|
|
1881
|
-
} catch {
|
|
1882
|
-
value = [];
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
if (f.type === "relationship-block" && value) {
|
|
1886
|
-
try {
|
|
1887
|
-
const parsed = JSON.parse(value);
|
|
1888
|
-
value = Array.isArray(parsed) ? parsed : [];
|
|
1889
|
-
} catch {
|
|
1890
|
-
value = [];
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
return value;
|
|
1894
|
-
}
|
|
1895
|
-
function getTableColumns(db, tableName) {
|
|
1896
|
-
try {
|
|
1897
|
-
const rows = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
1898
|
-
return rows.map((r) => r.name);
|
|
1899
|
-
} catch {
|
|
1900
|
-
return [];
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
var LocalAdapter = class extends chunkGUUB5EAG_cjs.AbstractBaseAdapter {
|
|
1904
|
-
db;
|
|
1905
|
-
path;
|
|
1906
|
-
migrations = /* @__PURE__ */ new Map();
|
|
1907
|
-
versionsTableName = "kyro_versions";
|
|
1908
|
-
constructor(options) {
|
|
1909
|
-
super();
|
|
1910
|
-
this.path = options.path;
|
|
1911
|
-
if (options.db) {
|
|
1912
|
-
this.db = options.db;
|
|
1913
|
-
} else {
|
|
1914
|
-
this.db = null;
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
async connect() {
|
|
1918
|
-
if (!this.db) {
|
|
1919
|
-
this.db = new DatabaseSync(this.path || ":memory:");
|
|
1920
|
-
}
|
|
1921
|
-
if (this.db && typeof this.db.prepare === "function" && !this.db.prepare.__wrapped) {
|
|
1922
|
-
const originalPrepare = this.db.prepare.bind(this.db);
|
|
1923
|
-
const wrappedPrepare = (sql) => {
|
|
1924
|
-
const stmt = originalPrepare(sql);
|
|
1925
|
-
const serialize = (val) => {
|
|
1926
|
-
if (typeof val === "boolean") return val ? 1 : 0;
|
|
1927
|
-
if (val === void 0) return null;
|
|
1928
|
-
return val;
|
|
1929
|
-
};
|
|
1930
|
-
return new Proxy(stmt, {
|
|
1931
|
-
get(target, prop, receiver) {
|
|
1932
|
-
if (prop === "all") {
|
|
1933
|
-
return (...params) => target.all(...params.map(serialize));
|
|
1934
|
-
}
|
|
1935
|
-
if (prop === "get") {
|
|
1936
|
-
return (...params) => target.get(...params.map(serialize));
|
|
1937
|
-
}
|
|
1938
|
-
if (prop === "run") {
|
|
1939
|
-
return (...params) => target.run(...params.map(serialize));
|
|
1940
|
-
}
|
|
1941
|
-
const val = Reflect.get(target, prop, receiver);
|
|
1942
|
-
return typeof val === "function" ? val.bind(target) : val;
|
|
1943
|
-
}
|
|
1944
|
-
});
|
|
1945
|
-
};
|
|
1946
|
-
wrappedPrepare.__wrapped = true;
|
|
1947
|
-
this.db.prepare = wrappedPrepare;
|
|
1948
|
-
}
|
|
1949
|
-
this.db.exec("PRAGMA journal_mode = WAL");
|
|
1950
|
-
this.db.exec("PRAGMA foreign_keys = ON");
|
|
1951
|
-
this.connected = true;
|
|
1952
|
-
}
|
|
1953
|
-
async disconnect() {
|
|
1954
|
-
if (this.db) {
|
|
1955
|
-
this.db.close();
|
|
1956
|
-
}
|
|
1957
|
-
this.connected = false;
|
|
1958
|
-
}
|
|
1959
|
-
// ========================================================================
|
|
1960
|
-
// Schema Management
|
|
1961
|
-
// ========================================================================
|
|
1962
|
-
ensureTable(config, tableName) {
|
|
1963
|
-
const name = tableName || this.getTableNameFor(config.slug);
|
|
1964
|
-
const columns = [`id TEXT PRIMARY KEY`];
|
|
1965
|
-
for (const field of flattenFields(config.fields)) {
|
|
1966
|
-
if (!field.name || field.name === "id") continue;
|
|
1967
|
-
const colDef = this.fieldToSQL(field);
|
|
1968
|
-
if (colDef) columns.push(colDef);
|
|
1969
|
-
}
|
|
1970
|
-
columns.push(`${this.col("createdAt")} TEXT DEFAULT (datetime('now'))`);
|
|
1971
|
-
columns.push(`${this.col("updatedAt")} TEXT DEFAULT (datetime('now'))`);
|
|
1972
|
-
columns.push(`status TEXT DEFAULT 'draft'`);
|
|
1973
|
-
columns.push(`hasDraft INTEGER DEFAULT 0`);
|
|
1974
|
-
if (config.tenantScoped) {
|
|
1975
|
-
columns.push(`tenant_id TEXT NOT NULL`);
|
|
1976
|
-
}
|
|
1977
|
-
const existingColumns = getTableColumns(this.db, name);
|
|
1978
|
-
if (existingColumns.length === 0) {
|
|
1979
|
-
const createSQL = `CREATE TABLE IF NOT EXISTS ${name} (${columns.join(", ")})`;
|
|
1980
|
-
this.db.exec(createSQL);
|
|
1981
|
-
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${name}_status ON ${name}(status)`);
|
|
1982
|
-
for (const field of flattenFields(config.fields)) {
|
|
1983
|
-
if (field.name && field.indexed) {
|
|
1984
|
-
this.db.exec(
|
|
1985
|
-
`CREATE INDEX IF NOT EXISTS idx_${name}_${field.name} ON ${name}(${this.col(field.name)})`
|
|
1986
|
-
);
|
|
1987
|
-
}
|
|
1988
|
-
if (field.name && field.unique) {
|
|
1989
|
-
this.db.exec(
|
|
1990
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_${name}_${field.name}_unique ON ${name}(${this.col(field.name)})`
|
|
1991
|
-
);
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
} else {
|
|
1995
|
-
const existingSet = new Set(existingColumns);
|
|
1996
|
-
for (const colDef of columns) {
|
|
1997
|
-
const colName = colDef.split(" ")[0].replace(/^"/, "").replace(/"$/, "");
|
|
1998
|
-
if (!existingSet.has(colName) && colName !== "id") {
|
|
1999
|
-
try {
|
|
2000
|
-
if (colName === "status") {
|
|
2001
|
-
this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} TEXT DEFAULT 'published'`);
|
|
2002
|
-
} else if (colName === "hasDraft") {
|
|
2003
|
-
this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} INTEGER DEFAULT 0`);
|
|
2004
|
-
} else {
|
|
2005
|
-
this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} TEXT`);
|
|
2006
|
-
}
|
|
2007
|
-
} catch {
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
this.migrations.set(name, true);
|
|
2013
|
-
}
|
|
2014
|
-
ensureVersionsTable() {
|
|
2015
|
-
this.db.exec(`
|
|
2016
|
-
CREATE TABLE IF NOT EXISTS ${this.versionsTableName} (
|
|
2017
|
-
id TEXT PRIMARY KEY,
|
|
2018
|
-
collection_slug TEXT NOT NULL,
|
|
2019
|
-
document_id TEXT NOT NULL,
|
|
2020
|
-
tenant_id TEXT,
|
|
2021
|
-
version INTEGER NOT NULL,
|
|
2022
|
-
status TEXT NOT NULL DEFAULT 'draft',
|
|
2023
|
-
autosave INTEGER NOT NULL DEFAULT 0,
|
|
2024
|
-
data TEXT NOT NULL,
|
|
2025
|
-
created_by TEXT,
|
|
2026
|
-
change_description TEXT,
|
|
2027
|
-
published_at TEXT,
|
|
2028
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
2029
|
-
updated_at TEXT DEFAULT (datetime('now'))
|
|
2030
|
-
)
|
|
2031
|
-
`);
|
|
2032
|
-
try {
|
|
2033
|
-
this.db.exec(`ALTER TABLE ${this.versionsTableName} ADD COLUMN autosave INTEGER NOT NULL DEFAULT 0`);
|
|
2034
|
-
} catch {
|
|
2035
|
-
}
|
|
2036
|
-
this.db.exec(
|
|
2037
|
-
`CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_doc ON ${this.versionsTableName}(collection_slug, document_id)`
|
|
2038
|
-
);
|
|
2039
|
-
this.db.exec(
|
|
2040
|
-
`CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_status ON ${this.versionsTableName}(status)`
|
|
2041
|
-
);
|
|
2042
|
-
this.db.exec(
|
|
2043
|
-
`CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_autosave ON ${this.versionsTableName}(autosave)`
|
|
2044
|
-
);
|
|
2045
|
-
}
|
|
2046
|
-
// ========================================================================
|
|
2047
|
-
// SQL Quoting
|
|
2048
|
-
// ========================================================================
|
|
2049
|
-
col(name) {
|
|
2050
|
-
return `"${name}"`;
|
|
2051
|
-
}
|
|
2052
|
-
fieldToSQL(field) {
|
|
2053
|
-
switch (field.type) {
|
|
2054
|
-
case "text":
|
|
2055
|
-
case "email":
|
|
2056
|
-
case "password":
|
|
2057
|
-
case "textarea":
|
|
2058
|
-
case "color":
|
|
2059
|
-
case "code":
|
|
2060
|
-
case "markdown":
|
|
2061
|
-
case "url":
|
|
2062
|
-
return this.col(field.name) + " TEXT";
|
|
2063
|
-
case "number":
|
|
2064
|
-
return this.col(field.name) + " REAL";
|
|
2065
|
-
case "checkbox":
|
|
2066
|
-
return this.col(field.name) + " INTEGER DEFAULT 0";
|
|
2067
|
-
case "date":
|
|
2068
|
-
return this.col(field.name) + " TEXT";
|
|
2069
|
-
case "select":
|
|
2070
|
-
case "radio":
|
|
2071
|
-
return this.col(field.name) + " TEXT";
|
|
2072
|
-
case "relationship":
|
|
2073
|
-
case "upload":
|
|
2074
|
-
return this.col(field.name) + " TEXT";
|
|
2075
|
-
case "json":
|
|
2076
|
-
case "richtext":
|
|
2077
|
-
case "array":
|
|
2078
|
-
case "group":
|
|
2079
|
-
case "blocks":
|
|
2080
|
-
return this.col(field.name) + " TEXT";
|
|
2081
|
-
default:
|
|
2082
|
-
return null;
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
// ========================================================================
|
|
2086
|
-
// CRUD Operations
|
|
2087
|
-
// ========================================================================
|
|
2088
|
-
parseGlobalsSlug(slug) {
|
|
2089
|
-
if (slug.startsWith("_globals_")) {
|
|
2090
|
-
const globalSlug = slug.replace("_globals_", "");
|
|
2091
|
-
return {
|
|
2092
|
-
isGlobal: true,
|
|
2093
|
-
globalSlug,
|
|
2094
|
-
tableName: `global_${globalSlug.replace(/-/g, "_")}`
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
return {
|
|
2098
|
-
isGlobal: false,
|
|
2099
|
-
globalSlug: "",
|
|
2100
|
-
tableName: this.getTableNameFor(slug)
|
|
2101
|
-
};
|
|
2102
|
-
}
|
|
2103
|
-
async find(args) {
|
|
2104
|
-
const {
|
|
2105
|
-
collection: slug,
|
|
2106
|
-
where = {},
|
|
2107
|
-
sort,
|
|
2108
|
-
limit = 10,
|
|
2109
|
-
page = 1,
|
|
2110
|
-
tenantID,
|
|
2111
|
-
draft = false
|
|
2112
|
-
} = args;
|
|
2113
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
2114
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
2115
|
-
this.ensureTable(config, parsed.tableName);
|
|
2116
|
-
const tableName = parsed.tableName;
|
|
2117
|
-
let sql = `SELECT * FROM ${tableName}`;
|
|
2118
|
-
const params = [];
|
|
2119
|
-
const conditions = [];
|
|
2120
|
-
let effectiveWhere = { ...where };
|
|
2121
|
-
if (this.tenantContext && config.tenantScoped) {
|
|
2122
|
-
const rlsQuery = chunkGUUB5EAG_cjs.applyRLS({ where: effectiveWhere }, slug, this.tenantContext, chunkGUUB5EAG_cjs.DEFAULT_RLS_CONFIG);
|
|
2123
|
-
effectiveWhere = rlsQuery.where || {};
|
|
2124
|
-
}
|
|
2125
|
-
if (!draft) {
|
|
2126
|
-
conditions.push(`status = ?`);
|
|
2127
|
-
params.push("published");
|
|
2128
|
-
}
|
|
2129
|
-
if (tenantID && config.tenantScoped) {
|
|
2130
|
-
conditions.push(`tenant_id = ?`);
|
|
2131
|
-
params.push(tenantID);
|
|
2132
|
-
}
|
|
2133
|
-
for (const [key, value] of Object.entries(effectiveWhere)) {
|
|
2134
|
-
if (key === "AND" || key === "OR") continue;
|
|
2135
|
-
if (typeof value === "object" && value !== null) {
|
|
2136
|
-
if (value.equals !== void 0) {
|
|
2137
|
-
conditions.push(`${this.col(key)} = ?`);
|
|
2138
|
-
params.push(value.equals);
|
|
2139
|
-
}
|
|
2140
|
-
if (value.in !== void 0) {
|
|
2141
|
-
conditions.push(`${this.col(key)} IN (${value.in.map(() => "?").join(", ")})`);
|
|
2142
|
-
params.push(...value.in);
|
|
2143
|
-
}
|
|
2144
|
-
if (value.not_equals !== void 0) {
|
|
2145
|
-
conditions.push(`${this.col(key)} != ?`);
|
|
2146
|
-
params.push(value.not_equals);
|
|
2147
|
-
}
|
|
2148
|
-
} else {
|
|
2149
|
-
conditions.push(`${this.col(key)} = ?`);
|
|
2150
|
-
params.push(value);
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
if (conditions.length > 0) {
|
|
2154
|
-
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
2155
|
-
}
|
|
2156
|
-
const sortField = this.col(sort?.replace("-", "") || "createdAt");
|
|
2157
|
-
const sortDir = sort?.startsWith("-") ? "DESC" : "ASC";
|
|
2158
|
-
sql += ` ORDER BY ${sortField} ${sortDir}`;
|
|
2159
|
-
const countSql = sql.replace("SELECT *", "SELECT COUNT(*) as count");
|
|
2160
|
-
const countResult = this.db.prepare(countSql).get(...params);
|
|
2161
|
-
const totalDocs = countResult?.count || 0;
|
|
2162
|
-
sql += ` LIMIT ? OFFSET ?`;
|
|
2163
|
-
params.push(limit, (page - 1) * limit);
|
|
2164
|
-
const rows = this.db.prepare(sql).all(...params);
|
|
2165
|
-
let docs = rows.map((row) => this.rowToDoc(row, config));
|
|
2166
|
-
if (this.tenantContext && !this.tenantContext.isSuperAdmin) {
|
|
2167
|
-
docs = docs.filter((doc) => chunkGUUB5EAG_cjs.canAccessDocument(doc, slug, this.tenantContext, chunkGUUB5EAG_cjs.DEFAULT_RLS_CONFIG));
|
|
2168
|
-
}
|
|
2169
|
-
if (draft) {
|
|
2170
|
-
docs = await Promise.all(docs.map(async (doc) => {
|
|
2171
|
-
const version = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND tenant_id IS NULL ORDER BY version DESC LIMIT 1`).get(slug, doc.id);
|
|
2172
|
-
if (version) {
|
|
2173
|
-
const versionData = version.data ? JSON.parse(version.data) : {};
|
|
2174
|
-
return { ...doc, ...versionData, status: doc.status };
|
|
2175
|
-
}
|
|
2176
|
-
return doc;
|
|
2177
|
-
}));
|
|
2178
|
-
}
|
|
2179
|
-
return {
|
|
2180
|
-
docs,
|
|
2181
|
-
totalDocs,
|
|
2182
|
-
limit,
|
|
2183
|
-
totalPages: Math.ceil(totalDocs / limit),
|
|
2184
|
-
page,
|
|
2185
|
-
pagingCounter: (page - 1) * limit + 1,
|
|
2186
|
-
hasPrevPage: page > 1,
|
|
2187
|
-
hasNextPage: page < Math.ceil(totalDocs / limit),
|
|
2188
|
-
prevPage: page > 1 ? page - 1 : null,
|
|
2189
|
-
nextPage: page < Math.ceil(totalDocs / limit) ? page + 1 : null
|
|
2190
|
-
};
|
|
2191
|
-
}
|
|
2192
|
-
async findByID(args) {
|
|
2193
|
-
const { collection: slug, id, tenantID, draft = false } = args;
|
|
2194
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
2195
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
2196
|
-
this.ensureTable(config, parsed.tableName);
|
|
2197
|
-
const tableName = parsed.tableName;
|
|
2198
|
-
let sql = `SELECT * FROM ${tableName} WHERE id = ?`;
|
|
2199
|
-
const params = [id];
|
|
2200
|
-
if (this.tenantContext && config.tenantScoped) {
|
|
2201
|
-
const tempDoc = { id, tenant_id: this.tenantContext.tenantId };
|
|
2202
|
-
if (!chunkGUUB5EAG_cjs.canAccessDocument(tempDoc, slug, this.tenantContext, chunkGUUB5EAG_cjs.DEFAULT_RLS_CONFIG)) {
|
|
2203
|
-
return null;
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
if (!draft) {
|
|
2207
|
-
sql += ` AND status = ?`;
|
|
2208
|
-
params.push("published");
|
|
2209
|
-
}
|
|
2210
|
-
if (tenantID && config.tenantScoped) {
|
|
2211
|
-
sql += ` AND tenant_id = ?`;
|
|
2212
|
-
params.push(tenantID);
|
|
2213
|
-
}
|
|
2214
|
-
const row = this.db.prepare(sql).get(...params);
|
|
2215
|
-
if (!row) return null;
|
|
2216
|
-
let doc = this.rowToDoc(row, config);
|
|
2217
|
-
if (draft) {
|
|
2218
|
-
const version = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND tenant_id IS NULL ORDER BY version DESC LIMIT 1`).get(slug, doc.id);
|
|
2219
|
-
if (version) {
|
|
2220
|
-
const versionData = version.data ? JSON.parse(version.data) : {};
|
|
2221
|
-
doc = { ...doc, ...versionData, status: doc.status };
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
return doc;
|
|
2225
|
-
}
|
|
2226
|
-
async create(args) {
|
|
2227
|
-
const { collection: slug, data, tenantID } = args;
|
|
2228
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
2229
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
2230
|
-
this.ensureTable(config, parsed.tableName);
|
|
2231
|
-
const tableName = parsed.tableName;
|
|
2232
|
-
const id = parsed.isGlobal ? parsed.globalSlug : data.id || this.generateId();
|
|
2233
|
-
const insertData = this.prepareData(data, config);
|
|
2234
|
-
insertData.id = id;
|
|
2235
|
-
insertData.created_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2236
|
-
insertData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2237
|
-
if (tenantID && config.tenantScoped) {
|
|
2238
|
-
insertData.tenant_id = tenantID;
|
|
2239
|
-
}
|
|
2240
|
-
const columns = Object.keys(insertData);
|
|
2241
|
-
const validColumns = getTableColumns(this.db, tableName);
|
|
2242
|
-
const filteredData = {};
|
|
2243
|
-
for (const key of columns) {
|
|
2244
|
-
if (validColumns.includes(key)) {
|
|
2245
|
-
filteredData[key] = insertData[key];
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
const filteredColumns = Object.keys(filteredData);
|
|
2249
|
-
const quotedColumns = filteredColumns.map((c) => this.col(c));
|
|
2250
|
-
const placeholders = filteredColumns.map(() => "?").join(", ");
|
|
2251
|
-
const values = Object.values(filteredData).map(
|
|
2252
|
-
(v) => v !== null && typeof v === "object" ? JSON.stringify(v) : v
|
|
2253
|
-
);
|
|
2254
|
-
this.db.prepare(
|
|
2255
|
-
`INSERT OR REPLACE INTO ${tableName} (${quotedColumns.join(", ")}) VALUES (${placeholders})`
|
|
2256
|
-
).run(...values);
|
|
2257
|
-
this.ensureVersionsTable();
|
|
2258
|
-
return this.findByID({ collection: slug, id, tenantID, draft: true });
|
|
2259
|
-
}
|
|
2260
|
-
async update(args) {
|
|
2261
|
-
const { collection: slug, id, data, tenantID } = args;
|
|
2262
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
2263
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
2264
|
-
this.ensureTable(config, parsed.tableName);
|
|
2265
|
-
const tableName = parsed.tableName;
|
|
2266
|
-
const updateData = this.prepareData(data, config);
|
|
2267
|
-
updateData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2268
|
-
const validColumns = getTableColumns(this.db, tableName);
|
|
2269
|
-
const filteredData = {};
|
|
2270
|
-
for (const key of Object.keys(updateData)) {
|
|
2271
|
-
if (validColumns.includes(key)) {
|
|
2272
|
-
filteredData[key] = updateData[key];
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
const columns = Object.keys(filteredData);
|
|
2276
|
-
const setClause = columns.map((c) => `${this.col(c)} = ?`).join(", ");
|
|
2277
|
-
const values = Object.values(filteredData).map(
|
|
2278
|
-
(v) => v !== null && typeof v === "object" ? JSON.stringify(v) : v
|
|
2279
|
-
);
|
|
2280
|
-
let sql = `UPDATE ${tableName} SET ${setClause} WHERE id = ?`;
|
|
2281
|
-
const params = [...values, id];
|
|
2282
|
-
if (tenantID && config.tenantScoped) {
|
|
2283
|
-
sql += ` AND tenant_id = ?`;
|
|
2284
|
-
params.push(tenantID);
|
|
2285
|
-
}
|
|
2286
|
-
this.db.prepare(sql).run(...params);
|
|
2287
|
-
this.ensureVersionsTable();
|
|
2288
|
-
return this.findByID({ collection: slug, id, tenantID, draft: true });
|
|
2289
|
-
}
|
|
2290
|
-
async delete(args) {
|
|
2291
|
-
const { collection: slug, id, tenantID } = args;
|
|
2292
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
2293
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
2294
|
-
this.ensureTable(config, parsed.tableName);
|
|
2295
|
-
this.ensureVersionsTable();
|
|
2296
|
-
const doc = await this.findByID({ collection: slug, id, tenantID, draft: true });
|
|
2297
|
-
if (!doc) throw new Error(`Document not found: ${slug}/${id}`);
|
|
2298
|
-
const tableName = parsed.tableName;
|
|
2299
|
-
let sql = `DELETE FROM ${tableName} WHERE id = ?`;
|
|
2300
|
-
const params = [id];
|
|
2301
|
-
if (tenantID && config.tenantScoped) {
|
|
2302
|
-
sql += ` AND tenant_id = ?`;
|
|
2303
|
-
params.push(tenantID);
|
|
2304
|
-
}
|
|
2305
|
-
this.db.prepare(sql).run(...params);
|
|
2306
|
-
return doc;
|
|
2307
|
-
}
|
|
2308
|
-
async count(args) {
|
|
2309
|
-
const { collection: slug, tenantID } = args;
|
|
2310
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
2311
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
2312
|
-
this.ensureTable(config, parsed.tableName);
|
|
2313
|
-
const tableName = parsed.tableName;
|
|
2314
|
-
let sql = `SELECT COUNT(*) as count FROM ${tableName}`;
|
|
2315
|
-
const params = [];
|
|
2316
|
-
if (tenantID && config.tenantScoped) {
|
|
2317
|
-
sql += ` WHERE tenant_id = ?`;
|
|
2318
|
-
params.push(tenantID);
|
|
2319
|
-
}
|
|
2320
|
-
const result = this.db.prepare(sql).get(...params);
|
|
2321
|
-
return result?.count || 0;
|
|
2322
|
-
}
|
|
2323
|
-
async findOne(args) {
|
|
2324
|
-
const parsed = this.parseGlobalsSlug(args.collection);
|
|
2325
|
-
if (parsed.isGlobal) {
|
|
2326
|
-
const globalConfig = this.globals.get(parsed.globalSlug);
|
|
2327
|
-
if (!globalConfig) {
|
|
2328
|
-
throw new Error(`Global "${parsed.globalSlug}" not found in adapter`);
|
|
2329
|
-
}
|
|
2330
|
-
this.ensureTable(globalConfig, parsed.tableName);
|
|
2331
|
-
let sql = `SELECT * FROM ${parsed.tableName}`;
|
|
2332
|
-
const conditions = [];
|
|
2333
|
-
const params = [];
|
|
2334
|
-
if (!args.draft && globalConfig.versions) {
|
|
2335
|
-
conditions.push("status = 'published'");
|
|
2336
|
-
}
|
|
2337
|
-
if (conditions.length > 0) {
|
|
2338
|
-
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
2339
|
-
}
|
|
2340
|
-
sql += " LIMIT 1";
|
|
2341
|
-
const result2 = this.db.prepare(sql).get(...params);
|
|
2342
|
-
if (result2) {
|
|
2343
|
-
let doc = this.rowToDoc(result2, globalConfig);
|
|
2344
|
-
if (args.draft) {
|
|
2345
|
-
const version = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND tenant_id IS NULL ORDER BY version DESC LIMIT 1`).get(args.collection, parsed.globalSlug);
|
|
2346
|
-
if (version) {
|
|
2347
|
-
const versionData = version.data ? JSON.parse(version.data) : {};
|
|
2348
|
-
doc = { ...doc, ...versionData, status: doc.status };
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
return doc;
|
|
2352
|
-
}
|
|
2353
|
-
return null;
|
|
2354
|
-
}
|
|
2355
|
-
const result = await this.find({ ...args, limit: 1 });
|
|
2356
|
-
return result.docs[0] || null;
|
|
2357
|
-
}
|
|
2358
|
-
// ========================================================================
|
|
2359
|
-
// Version History
|
|
2360
|
-
// ========================================================================
|
|
2361
|
-
async findVersions(args) {
|
|
2362
|
-
this.ensureVersionsTable();
|
|
2363
|
-
const { collection, documentId, tenantID, limit = 20, page = 1 } = args;
|
|
2364
|
-
const conditions = [`collection_slug = ?`, `document_id = ?`, `autosave = 0`];
|
|
2365
|
-
const params = [collection, documentId];
|
|
2366
|
-
if (tenantID) {
|
|
2367
|
-
conditions.push(`tenant_id = ?`);
|
|
2368
|
-
params.push(tenantID);
|
|
2369
|
-
} else {
|
|
2370
|
-
conditions.push(`tenant_id IS NULL`);
|
|
2371
|
-
}
|
|
2372
|
-
const where = `WHERE ${conditions.join(" AND ")}`;
|
|
2373
|
-
const countResult = this.db.prepare(`SELECT COUNT(*) as count FROM ${this.versionsTableName} ${where}`).get(...params);
|
|
2374
|
-
const totalDocs = countResult?.count || 0;
|
|
2375
|
-
const offset = (page - 1) * limit;
|
|
2376
|
-
const rows = this.db.prepare(`SELECT * FROM ${this.versionsTableName} ${where} ORDER BY version DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
2377
|
-
const docs = rows.map((r) => this.rowToVersion(r));
|
|
2378
|
-
return {
|
|
2379
|
-
docs,
|
|
2380
|
-
totalDocs,
|
|
2381
|
-
limit,
|
|
2382
|
-
totalPages: Math.ceil(totalDocs / limit),
|
|
2383
|
-
page,
|
|
2384
|
-
pagingCounter: (page - 1) * limit + 1,
|
|
2385
|
-
hasPrevPage: page > 1,
|
|
2386
|
-
hasNextPage: page < Math.ceil(totalDocs / limit),
|
|
2387
|
-
prevPage: page > 1 ? page - 1 : null,
|
|
2388
|
-
nextPage: page < Math.ceil(totalDocs / limit) ? page + 1 : null
|
|
2389
|
-
};
|
|
2390
|
-
}
|
|
2391
|
-
async findVersionByID(args) {
|
|
2392
|
-
this.ensureVersionsTable();
|
|
2393
|
-
const row = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE id = ? AND collection_slug = ? LIMIT 1`).get(args.versionId, args.collection);
|
|
2394
|
-
return row ? this.rowToVersion(row) : null;
|
|
2395
|
-
}
|
|
2396
|
-
async createVersion(args) {
|
|
2397
|
-
this.ensureVersionsTable();
|
|
2398
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2399
|
-
if (args.autosave) {
|
|
2400
|
-
let sql = `SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND autosave = 1`;
|
|
2401
|
-
const params = [args.collection, args.documentId];
|
|
2402
|
-
if (args.tenantID) {
|
|
2403
|
-
sql += ` AND tenant_id = ?`;
|
|
2404
|
-
params.push(args.tenantID);
|
|
2405
|
-
} else {
|
|
2406
|
-
sql += ` AND tenant_id IS NULL`;
|
|
2407
|
-
}
|
|
2408
|
-
sql += ` LIMIT 1`;
|
|
2409
|
-
const existing = this.db.prepare(sql).get(...params);
|
|
2410
|
-
if (existing) {
|
|
2411
|
-
this.db.prepare(`UPDATE ${this.versionsTableName} SET data = ?, status = ?, updated_at = ? WHERE id = ?`).run(JSON.stringify(args.data), args.status, now, existing.id);
|
|
2412
|
-
const result = await this.findVersionByID({ collection: args.collection, versionId: existing.id });
|
|
2413
|
-
if (result) return result;
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
const id = this.generateId();
|
|
2417
|
-
const latestRow = this.db.prepare(`SELECT version FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND autosave = 0 ORDER BY version DESC LIMIT 1`).get(args.collection, args.documentId);
|
|
2418
|
-
const nextVersion = (latestRow?.version ?? 0) + 1;
|
|
2419
|
-
this.db.prepare(
|
|
2420
|
-
`INSERT INTO ${this.versionsTableName} (
|
|
2421
|
-
id, collection_slug, document_id, tenant_id, version, status, autosave, data, created_by, change_description, published_at, created_at, updated_at
|
|
2422
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2423
|
-
).run(
|
|
2424
|
-
id,
|
|
2425
|
-
args.collection,
|
|
2426
|
-
args.documentId,
|
|
2427
|
-
args.tenantID ?? null,
|
|
2428
|
-
nextVersion,
|
|
2429
|
-
args.status,
|
|
2430
|
-
args.autosave ? 1 : 0,
|
|
2431
|
-
JSON.stringify(args.data),
|
|
2432
|
-
args.createdBy ?? null,
|
|
2433
|
-
args.changeDescription ?? null,
|
|
2434
|
-
args.status === "published" ? now : null,
|
|
2435
|
-
now,
|
|
2436
|
-
now
|
|
2437
|
-
);
|
|
2438
|
-
if (!args.autosave) {
|
|
2439
|
-
const collectionConfig = this.collections.get(args.collection);
|
|
2440
|
-
const maxPerDoc = collectionConfig?.versions?.maxPerDoc;
|
|
2441
|
-
if (maxPerDoc && maxPerDoc > 0) {
|
|
2442
|
-
await this.deleteVersions({ collection: args.collection, documentId: args.documentId, keepLatest: maxPerDoc, tenantID: args.tenantID });
|
|
2443
|
-
}
|
|
2444
|
-
}
|
|
2445
|
-
const saved = await this.findVersionByID({ collection: args.collection, versionId: id });
|
|
2446
|
-
return saved;
|
|
2447
|
-
}
|
|
2448
|
-
async updateLatestVersion(args) {
|
|
2449
|
-
return this.createVersion({ ...args, autosave: true });
|
|
2450
|
-
}
|
|
2451
|
-
async deleteVersions(args) {
|
|
2452
|
-
this.ensureVersionsTable();
|
|
2453
|
-
const { collection, documentId, keepLatest, tenantID } = args;
|
|
2454
|
-
if (keepLatest && keepLatest > 0) {
|
|
2455
|
-
const rows = this.db.prepare(`SELECT id, status, autosave FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? ORDER BY version DESC`).all(collection, documentId);
|
|
2456
|
-
let draftCount = 0;
|
|
2457
|
-
const toDelete = [];
|
|
2458
|
-
for (const row of rows) {
|
|
2459
|
-
if (row.status === "published" || row.autosave === 1) continue;
|
|
2460
|
-
draftCount++;
|
|
2461
|
-
if (draftCount > keepLatest) toDelete.push(row.id);
|
|
2462
|
-
}
|
|
2463
|
-
for (const vid of toDelete) {
|
|
2464
|
-
this.db.prepare(`DELETE FROM ${this.versionsTableName} WHERE id = ?`).run(vid);
|
|
2465
|
-
}
|
|
2466
|
-
} else {
|
|
2467
|
-
let sql = `DELETE FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ?`;
|
|
2468
|
-
const params = [collection, documentId];
|
|
2469
|
-
if (tenantID) {
|
|
2470
|
-
sql += ` AND tenant_id = ?`;
|
|
2471
|
-
params.push(tenantID);
|
|
2472
|
-
}
|
|
2473
|
-
this.db.prepare(sql).run(...params);
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
rowToVersion(row) {
|
|
2477
|
-
return {
|
|
2478
|
-
id: String(row.id),
|
|
2479
|
-
collection: row.collection_slug,
|
|
2480
|
-
documentId: row.document_id,
|
|
2481
|
-
version: row.version,
|
|
2482
|
-
status: row.status,
|
|
2483
|
-
data: row.data ? JSON.parse(row.data) : {},
|
|
2484
|
-
createdBy: row.created_by ?? void 0,
|
|
2485
|
-
changeDescription: row.change_description ?? void 0,
|
|
2486
|
-
publishedAt: row.published_at ?? null,
|
|
2487
|
-
createdAt: row.created_at,
|
|
2488
|
-
updatedAt: row.updated_at
|
|
2489
|
-
};
|
|
2490
|
-
}
|
|
2491
|
-
// ========================================================================
|
|
2492
|
-
// Helpers
|
|
2493
|
-
// ========================================================================
|
|
2494
|
-
prepareData(data, config) {
|
|
2495
|
-
const result = {};
|
|
2496
|
-
const fields = flattenFields(config.fields);
|
|
2497
|
-
const processValue = (field, value) => {
|
|
2498
|
-
const f = field;
|
|
2499
|
-
if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "group" || f.type === "blocks" || f.type === "list" || f.type === "relationship-block") {
|
|
2500
|
-
return value !== null && value !== void 0 ? JSON.stringify(value) : null;
|
|
2501
|
-
} else if (f.type === "checkbox") {
|
|
2502
|
-
return value ? 1 : 0;
|
|
2503
|
-
} else if (f.type === "number") {
|
|
2504
|
-
return value !== null && value !== "" ? Number(value) : null;
|
|
2505
|
-
} else if (f.type === "upload" || f.type === "image") {
|
|
2506
|
-
if (value === null || value === void 0) return null;
|
|
2507
|
-
if (Array.isArray(value)) {
|
|
2508
|
-
const items = value.map((v) => {
|
|
2509
|
-
if (typeof v === "string") return v;
|
|
2510
|
-
if (typeof v === "object") return v.id || v._id || v;
|
|
2511
|
-
return String(v);
|
|
2512
|
-
});
|
|
2513
|
-
return JSON.stringify(items);
|
|
2514
|
-
}
|
|
2515
|
-
if (typeof value === "string") return value;
|
|
2516
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
2517
|
-
return String(value);
|
|
2518
|
-
} else if (field.type === "relationship") {
|
|
2519
|
-
if (value === null || value === void 0) return null;
|
|
2520
|
-
if (Array.isArray(value)) {
|
|
2521
|
-
const rels = value.map((v) => {
|
|
2522
|
-
if (typeof v === "string") return v;
|
|
2523
|
-
if (typeof v === "object") return JSON.stringify({ relationTo: field.relationTo, value: v.id || v });
|
|
2524
|
-
return String(v);
|
|
2525
|
-
});
|
|
2526
|
-
return JSON.stringify(rels);
|
|
2527
|
-
}
|
|
2528
|
-
if (typeof value === "string") return value;
|
|
2529
|
-
if (typeof value === "object") return JSON.stringify({ relationTo: field.relationTo, value: value.id || value });
|
|
2530
|
-
return String(value);
|
|
2531
|
-
}
|
|
2532
|
-
return value;
|
|
2533
|
-
};
|
|
2534
|
-
for (const field of fields) {
|
|
2535
|
-
if (!field.name || field.name === "id") continue;
|
|
2536
|
-
const isInTab = config.fields.some(
|
|
2537
|
-
(f) => f.type === "tabs" && "tabs" in f && f.tabs.some((t) => t.fields.some((tf) => tf.name === field.name))
|
|
2538
|
-
);
|
|
2539
|
-
if (isInTab) continue;
|
|
2540
|
-
const value = data[field.name];
|
|
2541
|
-
if (value !== void 0) {
|
|
2542
|
-
result[field.name] = processValue(field, value);
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
for (const field of config.fields) {
|
|
2546
|
-
if (field.type === "tabs" && "tabs" in field && field.name) {
|
|
2547
|
-
const tabData = data[field.name];
|
|
2548
|
-
if (tabData && typeof tabData === "object") {
|
|
2549
|
-
for (const tab of field.tabs) {
|
|
2550
|
-
for (const tabField of tab.fields) {
|
|
2551
|
-
if (tabField.name && tabField.name !== "id") {
|
|
2552
|
-
const value = tabData[tabField.name];
|
|
2553
|
-
if (value !== void 0) {
|
|
2554
|
-
result[tabField.name] = processValue(tabField, value);
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
return result;
|
|
2563
|
-
}
|
|
2564
|
-
rowToDoc(row, config) {
|
|
2565
|
-
const doc = { id: row.id };
|
|
2566
|
-
for (const field of flattenFields(config.fields)) {
|
|
2567
|
-
if (!field.name || field.name === "id") continue;
|
|
2568
|
-
const f = field;
|
|
2569
|
-
let value = row[f.name];
|
|
2570
|
-
if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "group" || f.type === "blocks" || f.type === "list" || f.type === "relationship-block") {
|
|
2571
|
-
try {
|
|
2572
|
-
value = value ? JSON.parse(value) : null;
|
|
2573
|
-
} catch {
|
|
2574
|
-
value = null;
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
if (field.type === "checkbox") {
|
|
2578
|
-
value = Boolean(value);
|
|
2579
|
-
}
|
|
2580
|
-
if (field.type === "date" && value) {
|
|
2581
|
-
try {
|
|
2582
|
-
const d = new Date(value);
|
|
2583
|
-
if (isNaN(d.getTime())) {
|
|
2584
|
-
console.warn(`[LocalAdapter] Invalid date value for field "${field.name}":`, value);
|
|
2585
|
-
value = null;
|
|
2586
|
-
} else {
|
|
2587
|
-
value = d.toISOString();
|
|
2588
|
-
}
|
|
2589
|
-
} catch {
|
|
2590
|
-
value = null;
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
if ((field.type === "upload" || field.type === "image") && value) {
|
|
2594
|
-
try {
|
|
2595
|
-
const parsed = JSON.parse(value);
|
|
2596
|
-
if (Array.isArray(parsed)) {
|
|
2597
|
-
value = parsed.map((item) => {
|
|
2598
|
-
if (typeof item === "object" && item !== null) {
|
|
2599
|
-
return item;
|
|
2600
|
-
}
|
|
2601
|
-
return { id: item };
|
|
2602
|
-
});
|
|
2603
|
-
} else {
|
|
2604
|
-
value = typeof parsed === "object" ? parsed : { id: parsed };
|
|
2605
|
-
}
|
|
2606
|
-
} catch {
|
|
2607
|
-
value = { id: value };
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
if (field.type === "relationship" && value) {
|
|
2611
|
-
try {
|
|
2612
|
-
const parsed = JSON.parse(value);
|
|
2613
|
-
if (Array.isArray(parsed)) {
|
|
2614
|
-
value = parsed;
|
|
2615
|
-
} else {
|
|
2616
|
-
value = parsed;
|
|
2617
|
-
}
|
|
2618
|
-
} catch {
|
|
2619
|
-
value = { relationTo: Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo, value };
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
doc[field.name] = value;
|
|
2623
|
-
}
|
|
2624
|
-
for (const field of config.fields) {
|
|
2625
|
-
if (!field.name || field.name === "id" || field.name === "row" || field.name === "collapsible") continue;
|
|
2626
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
2627
|
-
const tabData = {};
|
|
2628
|
-
for (const tab of field.tabs) {
|
|
2629
|
-
for (const tabField of tab.fields) {
|
|
2630
|
-
if (tabField.name && tabField.name !== "id") {
|
|
2631
|
-
tabData[tabField.name] = processFieldValue(row, tabField);
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
doc[field.name] = tabData;
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
if (config.timestamps) {
|
|
2639
|
-
const cAt = row.createdAt || row.created_at;
|
|
2640
|
-
const uAt = row.updatedAt || row.updated_at;
|
|
2641
|
-
if (cAt) {
|
|
2642
|
-
try {
|
|
2643
|
-
const d = new Date(cAt);
|
|
2644
|
-
if (!isNaN(d.getTime())) doc.createdAt = d.toISOString();
|
|
2645
|
-
} catch {
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2648
|
-
if (uAt) {
|
|
2649
|
-
try {
|
|
2650
|
-
const d = new Date(uAt);
|
|
2651
|
-
if (!isNaN(d.getTime())) doc.updatedAt = d.toISOString();
|
|
2652
|
-
} catch {
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
if (config.tenantScoped) {
|
|
2657
|
-
doc.tenantID = row.tenant_id;
|
|
2658
|
-
}
|
|
2659
|
-
doc.status = row.status ?? "published";
|
|
2660
|
-
doc.hasDraft = row.hasDraft ? Boolean(row.hasDraft) : false;
|
|
2661
|
-
return doc;
|
|
2662
|
-
}
|
|
2663
|
-
generateId() {
|
|
2664
|
-
const timestamp = Date.now().toString(16).padStart(12, "0");
|
|
2665
|
-
const random = crypto.randomBytes(6).toString("hex");
|
|
2666
|
-
return timestamp + random;
|
|
2667
|
-
}
|
|
2668
|
-
getMediaById(mediaId) {
|
|
2669
|
-
try {
|
|
2670
|
-
const tableName = this.getTableNameFor("media");
|
|
2671
|
-
const row = this.db.prepare(`SELECT id, url, thumbnail_url FROM ${tableName} WHERE id = ?`).get(mediaId);
|
|
2672
|
-
if (row) {
|
|
2673
|
-
return {
|
|
2674
|
-
id: row.id,
|
|
2675
|
-
url: row.url,
|
|
2676
|
-
thumbnailUrl: row.thumbnail_url || row.url
|
|
2677
|
-
};
|
|
2678
|
-
}
|
|
2679
|
-
} catch (err) {
|
|
2680
|
-
}
|
|
2681
|
-
return null;
|
|
2682
|
-
}
|
|
2683
|
-
getTableNameFor(slug) {
|
|
2684
|
-
return slug.replace(/-/g, "_");
|
|
2685
|
-
}
|
|
2686
|
-
// ========================================================================
|
|
2687
|
-
// Migrations
|
|
2688
|
-
// ========================================================================
|
|
2689
|
-
async migrate() {
|
|
2690
|
-
for (const config of this.collections.values()) {
|
|
2691
|
-
this.ensureTable(config);
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
async rollback() {
|
|
2695
|
-
}
|
|
2696
|
-
// ========================================================================
|
|
2697
|
-
// Transaction Support
|
|
2698
|
-
// ========================================================================
|
|
2699
|
-
async transaction(fn) {
|
|
2700
|
-
return new Promise((resolve, reject) => {
|
|
2701
|
-
const tx = this.db.transaction(async () => {
|
|
2702
|
-
return fn({ db: this.db });
|
|
2703
|
-
});
|
|
2704
|
-
try {
|
|
2705
|
-
const result = tx();
|
|
2706
|
-
resolve(result);
|
|
2707
|
-
} catch (error) {
|
|
2708
|
-
reject(error);
|
|
2709
|
-
}
|
|
2710
|
-
});
|
|
2711
|
-
}
|
|
2712
|
-
// ========================================================================
|
|
2713
|
-
// Direct DB Access
|
|
2714
|
-
// ========================================================================
|
|
2715
|
-
getDatabase() {
|
|
2716
|
-
return this.db;
|
|
2717
|
-
}
|
|
2718
|
-
exec(sql) {
|
|
2719
|
-
this.db.exec(sql);
|
|
2720
|
-
}
|
|
2721
|
-
prepare(sql) {
|
|
2722
|
-
return this.db.prepare(sql);
|
|
2723
|
-
}
|
|
2724
|
-
};
|
|
2725
|
-
function createLocalAdapter(options) {
|
|
2726
|
-
return new LocalAdapter(options || {});
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
|
-
exports.ConfigValidationError = ConfigValidationError;
|
|
2730
|
-
exports.Kyro = Kyro;
|
|
2731
|
-
exports.LocalAdapter = LocalAdapter;
|
|
2732
|
-
exports.Registry = Registry;
|
|
2733
|
-
exports.collectionToCreateZod = collectionToCreateZod;
|
|
2734
|
-
exports.collectionToUpdateZod = collectionToUpdateZod;
|
|
2735
|
-
exports.collectionToWhereZod = collectionToWhereZod;
|
|
2736
|
-
exports.collectionToZod = collectionToZod;
|
|
2737
|
-
exports.createKyro = createKyro;
|
|
2738
|
-
exports.createLocalAdapter = createLocalAdapter;
|
|
2739
|
-
exports.createRegistry = createRegistry;
|
|
2740
|
-
exports.fieldToZod = fieldToZod;
|
|
2741
|
-
exports.getRegistry = getRegistry;
|
|
2742
|
-
exports.globalToZod = globalToZod;
|
|
2743
|
-
exports.resetRegistry = resetRegistry;
|
|
2744
|
-
exports.validateCollection = validateCollection;
|
|
2745
|
-
exports.validateConfig = validateConfig;
|
|
2746
|
-
exports.validateFields = validateFields;
|
|
2747
|
-
exports.validateGlobal = validateGlobal;
|
|
2748
|
-
//# sourceMappingURL=chunk-74YSDAWE.cjs.map
|
|
2749
|
-
//# sourceMappingURL=chunk-74YSDAWE.cjs.map
|