@kyro-cms/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +241 -0
  2. package/dist/base-CQkFzqQl.d.ts +62 -0
  3. package/dist/base-DlhVlwnN.d.cts +62 -0
  4. package/dist/chunk-3Q3FS5J4.cjs +273 -0
  5. package/dist/chunk-3Q3FS5J4.cjs.map +1 -0
  6. package/dist/chunk-3TPQ2BU6.js +423 -0
  7. package/dist/chunk-3TPQ2BU6.js.map +1 -0
  8. package/dist/chunk-3VZCX4DF.cjs +384 -0
  9. package/dist/chunk-3VZCX4DF.cjs.map +1 -0
  10. package/dist/chunk-BXMWDUED.js +115 -0
  11. package/dist/chunk-BXMWDUED.js.map +1 -0
  12. package/dist/chunk-DIC236EW.js +290 -0
  13. package/dist/chunk-DIC236EW.js.map +1 -0
  14. package/dist/chunk-DKSMFC3L.js +268 -0
  15. package/dist/chunk-DKSMFC3L.js.map +1 -0
  16. package/dist/chunk-DVD5P72E.cjs +428 -0
  17. package/dist/chunk-DVD5P72E.cjs.map +1 -0
  18. package/dist/chunk-HT6VE4NW.cjs +293 -0
  19. package/dist/chunk-HT6VE4NW.cjs.map +1 -0
  20. package/dist/chunk-K7QF2QCM.cjs +311 -0
  21. package/dist/chunk-K7QF2QCM.cjs.map +1 -0
  22. package/dist/chunk-OG3KX56O.js +308 -0
  23. package/dist/chunk-OG3KX56O.js.map +1 -0
  24. package/dist/chunk-R3XIBBAW.cjs +34 -0
  25. package/dist/chunk-R3XIBBAW.cjs.map +1 -0
  26. package/dist/chunk-RLTG4YZM.cjs +117 -0
  27. package/dist/chunk-RLTG4YZM.cjs.map +1 -0
  28. package/dist/chunk-SDMNUYVU.js +30 -0
  29. package/dist/chunk-SDMNUYVU.js.map +1 -0
  30. package/dist/chunk-UEG7KMKC.cjs +228 -0
  31. package/dist/chunk-UEG7KMKC.cjs.map +1 -0
  32. package/dist/chunk-UEYC46RL.js +374 -0
  33. package/dist/chunk-UEYC46RL.js.map +1 -0
  34. package/dist/chunk-YPAFJ7EV.js +225 -0
  35. package/dist/chunk-YPAFJ7EV.js.map +1 -0
  36. package/dist/cli/index.cjs +306 -0
  37. package/dist/cli/index.cjs.map +1 -0
  38. package/dist/cli/index.d.cts +1 -0
  39. package/dist/cli/index.d.ts +1 -0
  40. package/dist/cli/index.js +303 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/drizzle/index.cjs +25 -0
  43. package/dist/drizzle/index.cjs.map +1 -0
  44. package/dist/drizzle/index.d.cts +49 -0
  45. package/dist/drizzle/index.d.ts +49 -0
  46. package/dist/drizzle/index.js +4 -0
  47. package/dist/drizzle/index.js.map +1 -0
  48. package/dist/graphql/index.cjs +16 -0
  49. package/dist/graphql/index.cjs.map +1 -0
  50. package/dist/graphql/index.d.cts +20 -0
  51. package/dist/graphql/index.d.ts +20 -0
  52. package/dist/graphql/index.js +3 -0
  53. package/dist/graphql/index.js.map +1 -0
  54. package/dist/index-4fJKLFK2.d.ts +63 -0
  55. package/dist/index-DI0DRPNv.d.cts +63 -0
  56. package/dist/index.cjs +2506 -0
  57. package/dist/index.cjs.map +1 -0
  58. package/dist/index.d.cts +525 -0
  59. package/dist/index.d.ts +525 -0
  60. package/dist/index.js +2334 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/mongodb/index.cjs +17 -0
  63. package/dist/mongodb/index.cjs.map +1 -0
  64. package/dist/mongodb/index.d.cts +49 -0
  65. package/dist/mongodb/index.d.ts +49 -0
  66. package/dist/mongodb/index.js +4 -0
  67. package/dist/mongodb/index.js.map +1 -0
  68. package/dist/rest/index.cjs +17 -0
  69. package/dist/rest/index.cjs.map +1 -0
  70. package/dist/rest/index.d.cts +28 -0
  71. package/dist/rest/index.d.ts +28 -0
  72. package/dist/rest/index.js +4 -0
  73. package/dist/rest/index.js.map +1 -0
  74. package/dist/trpc/index.cjs +45 -0
  75. package/dist/trpc/index.cjs.map +1 -0
  76. package/dist/trpc/index.d.cts +130 -0
  77. package/dist/trpc/index.d.ts +130 -0
  78. package/dist/trpc/index.js +4 -0
  79. package/dist/trpc/index.js.map +1 -0
  80. package/dist/types-BGM5MV_K.d.cts +589 -0
  81. package/dist/types-BGM5MV_K.d.ts +589 -0
  82. package/dist/ws/index.cjs +24 -0
  83. package/dist/ws/index.cjs.map +1 -0
  84. package/dist/ws/index.d.cts +88 -0
  85. package/dist/ws/index.d.ts +88 -0
  86. package/dist/ws/index.js +3 -0
  87. package/dist/ws/index.js.map +1 -0
  88. package/package.json +120 -0
package/dist/index.js ADDED
@@ -0,0 +1,2334 @@
1
+ import { createKyroServer } from './chunk-UEYC46RL.js';
2
+ export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-UEYC46RL.js';
3
+ import { buildGraphQLSchema } from './chunk-OG3KX56O.js';
4
+ export { buildGraphQLSchema, createGraphQLSchema } from './chunk-OG3KX56O.js';
5
+ import { createHonoApp } from './chunk-YPAFJ7EV.js';
6
+ export { createHonoApp, createRESTAPI } from './chunk-YPAFJ7EV.js';
7
+ export { evaluateAccess, getWhereClause, mergeWhereClauses } from './chunk-SDMNUYVU.js';
8
+ import { KyroPubSub, createWSServer } from './chunk-3TPQ2BU6.js';
9
+ export { KyroPubSub, KyroWSServer, PubSub, createWSServer } from './chunk-3TPQ2BU6.js';
10
+ export { DrizzleAdapter, collectionToDrizzleSchema, createDrizzleAdapter, fieldToDrizzleType } from './chunk-DKSMFC3L.js';
11
+ export { MongoDBAdapter, createMongoDBAdapter } from './chunk-DIC236EW.js';
12
+ import { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
13
+ export { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
14
+ import { z } from 'zod';
15
+ export { z } from 'zod';
16
+ import bcrypt from 'bcrypt';
17
+ import jwt from 'jsonwebtoken';
18
+ import { randomUUID } from 'crypto';
19
+
20
+ // src/registry/validator.ts
21
+ var ConfigValidationError = class extends Error {
22
+ errors;
23
+ constructor(errors) {
24
+ super(`Configuration validation failed:
25
+ ${errors.join("\n")}`);
26
+ this.name = "ConfigValidationError";
27
+ this.errors = errors;
28
+ }
29
+ };
30
+ function validateCollection(config) {
31
+ const errors = [];
32
+ if (!config.slug) {
33
+ errors.push(`Collection is missing a "slug" property`);
34
+ } else if (!/^[a-z][a-z0-9_-]*$/.test(config.slug)) {
35
+ errors.push(`Collection slug "${config.slug}" must be lowercase alphanumeric with dashes`);
36
+ }
37
+ if (!config.fields || config.fields.length === 0) {
38
+ errors.push(`Collection "${config.slug}" has no fields defined`);
39
+ } else {
40
+ const fieldErrors = validateFields(config.fields, config.slug);
41
+ errors.push(...fieldErrors);
42
+ }
43
+ if (config.access) {
44
+ for (const [action, handler] of Object.entries(config.access)) {
45
+ if (typeof handler !== "boolean" && typeof handler !== "function") {
46
+ errors.push(`Collection "${config.slug}" has invalid access.${action} (must be boolean or function)`);
47
+ }
48
+ }
49
+ }
50
+ if (config.admin?.useAsTitle) {
51
+ const fieldExists = config.fields.some((f) => f.name === config.admin.useAsTitle);
52
+ if (!fieldExists) {
53
+ errors.push(`Collection "${config.slug}" admin.useAsTitle references unknown field "${config.admin.useAsTitle}"`);
54
+ }
55
+ }
56
+ if (config.admin?.defaultColumns) {
57
+ for (const col of config.admin.defaultColumns) {
58
+ const fieldExists = config.fields.some((f) => f.name === col);
59
+ if (!fieldExists) {
60
+ errors.push(`Collection "${config.slug}" admin.defaultColumns references unknown field "${col}"`);
61
+ }
62
+ }
63
+ }
64
+ if (config.upload) {
65
+ if (config.upload.fileSize && config.upload.fileSize <= 0) {
66
+ errors.push(`Collection "${config.slug}" upload.fileSize must be positive`);
67
+ }
68
+ }
69
+ if (config.versions) {
70
+ if (config.versions.maxPerDoc && config.versions.maxPerDoc <= 0) {
71
+ errors.push(`Collection "${config.slug}" versions.maxPerDoc must be positive`);
72
+ }
73
+ }
74
+ if (config.auth) {
75
+ const hasEmailField = config.fields.some((f) => f.name === "email");
76
+ const hasPasswordField = config.fields.some((f) => f.name === "password");
77
+ if (!hasEmailField) {
78
+ errors.push(`Collection "${config.slug}" with auth enabled requires an "email" field`);
79
+ }
80
+ if (!hasPasswordField) {
81
+ errors.push(`Collection "${config.slug}" with auth enabled requires a "password" field`);
82
+ }
83
+ }
84
+ return errors;
85
+ }
86
+ function validateGlobal(config) {
87
+ const errors = [];
88
+ if (!config.slug) {
89
+ errors.push(`Global is missing a "slug" property`);
90
+ } else if (!/^[a-z][a-z0-9_-]*$/.test(config.slug)) {
91
+ errors.push(`Global slug "${config.slug}" must be lowercase alphanumeric with dashes`);
92
+ }
93
+ if (!config.fields || config.fields.length === 0) {
94
+ errors.push(`Global "${config.slug}" has no fields defined`);
95
+ } else {
96
+ const fieldErrors = validateFields(config.fields, `global:${config.slug}`);
97
+ errors.push(...fieldErrors);
98
+ }
99
+ return errors;
100
+ }
101
+ function validateFields(fields, context) {
102
+ const errors = [];
103
+ const fieldNames = /* @__PURE__ */ new Set();
104
+ for (let i = 0; i < fields.length; i++) {
105
+ const field = fields[i];
106
+ if (field.type === "row" || field.type === "collapsible" || field.type === "tabs") {
107
+ if ("fields" in field && field.fields) {
108
+ const nestedErrors = validateFields(field.fields, context);
109
+ errors.push(...nestedErrors);
110
+ } else if ("tabs" in field) {
111
+ for (const tab of field.tabs) {
112
+ const tabErrors = validateFields(tab.fields, context);
113
+ errors.push(...tabErrors);
114
+ }
115
+ }
116
+ continue;
117
+ }
118
+ const fieldName = field.name;
119
+ if (!fieldName) {
120
+ errors.push(`${context}: Field at index ${i} is missing a "name" property`);
121
+ continue;
122
+ }
123
+ if (fieldNames.has(fieldName)) {
124
+ errors.push(`${context}: Duplicate field name "${fieldName}"`);
125
+ }
126
+ fieldNames.add(fieldName);
127
+ if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(fieldName)) {
128
+ errors.push(`${context}: Field name "${fieldName}" must be alphanumeric with underscores`);
129
+ }
130
+ if (!field.type) {
131
+ errors.push(`${context}: Field "${fieldName}" is missing a "type" property`);
132
+ continue;
133
+ }
134
+ switch (field.type) {
135
+ case "relationship":
136
+ if (!field.relationTo) {
137
+ errors.push(`${context}: Relationship field "${fieldName}" is missing "relationTo"`);
138
+ }
139
+ break;
140
+ case "array":
141
+ if (!field.fields || field.fields.length === 0) {
142
+ errors.push(`${context}: Array field "${fieldName}" has no fields defined`);
143
+ } else {
144
+ const arrayErrors = validateFields(field.fields, `${context}.${fieldName}`);
145
+ errors.push(...arrayErrors);
146
+ }
147
+ break;
148
+ case "group":
149
+ if (!field.fields || field.fields.length === 0) {
150
+ errors.push(`${context}: Group field "${fieldName}" has no fields defined`);
151
+ } else {
152
+ const groupErrors = validateFields(field.fields, `${context}.${fieldName}`);
153
+ errors.push(...groupErrors);
154
+ }
155
+ break;
156
+ case "blocks":
157
+ if (!field.blocks || field.blocks.length === 0) {
158
+ errors.push(`${context}: Blocks field "${fieldName}" has no blocks defined`);
159
+ } else {
160
+ const blockErrors = validateBlocks(field.blocks, `${context}.${fieldName}`);
161
+ errors.push(...blockErrors);
162
+ }
163
+ break;
164
+ case "select":
165
+ case "radio":
166
+ if (!field.options || field.options.length === 0) {
167
+ errors.push(`${context}: ${field.type} field "${fieldName}" has no options defined`);
168
+ } else {
169
+ const values = field.options.map((o) => o.value);
170
+ const uniqueValues = new Set(values);
171
+ if (values.length !== uniqueValues.size) {
172
+ errors.push(`${context}: ${field.type} field "${fieldName}" has duplicate option values`);
173
+ }
174
+ }
175
+ break;
176
+ case "upload":
177
+ if (!field.relationTo) {
178
+ errors.push(`${context}: Upload field "${fieldName}" is missing "relationTo"`);
179
+ }
180
+ break;
181
+ }
182
+ if ("min" in field && "max" in field && field.min > field.max) {
183
+ errors.push(`${context}: Field "${fieldName}" has min greater than max`);
184
+ }
185
+ if ("minLength" in field && "maxLength" in field && field.minLength > field.maxLength) {
186
+ errors.push(`${context}: Field "${fieldName}" has minLength greater than maxLength`);
187
+ }
188
+ if ("minRows" in field && "maxRows" in field && field.minRows > field.maxRows) {
189
+ errors.push(`${context}: Field "${fieldName}" has minRows greater than maxRows`);
190
+ }
191
+ }
192
+ return errors;
193
+ }
194
+ function validateBlocks(blocks, context) {
195
+ const errors = [];
196
+ const slugs = /* @__PURE__ */ new Set();
197
+ for (const block of blocks) {
198
+ if (!block.slug) {
199
+ errors.push(`${context}: Block is missing a "slug" property`);
200
+ continue;
201
+ }
202
+ if (slugs.has(block.slug)) {
203
+ errors.push(`${context}: Duplicate block slug "${block.slug}"`);
204
+ }
205
+ slugs.add(block.slug);
206
+ if (!block.label) {
207
+ errors.push(`${context}: Block "${block.slug}" is missing a "label" property`);
208
+ }
209
+ if (!block.fields || block.fields.length === 0) {
210
+ errors.push(`${context}: Block "${block.slug}" has no fields defined`);
211
+ } else {
212
+ const blockErrors = validateFields(block.fields, `${context}.${block.slug}`);
213
+ errors.push(...blockErrors);
214
+ }
215
+ }
216
+ return errors;
217
+ }
218
+ function validateConfig(collections, globals = []) {
219
+ const errors = [];
220
+ const slugs = /* @__PURE__ */ new Set();
221
+ for (const collection of collections) {
222
+ if (slugs.has(collection.slug)) {
223
+ errors.push(`Duplicate collection slug "${collection.slug}"`);
224
+ }
225
+ slugs.add(collection.slug);
226
+ }
227
+ for (const global of globals) {
228
+ if (slugs.has(global.slug)) {
229
+ errors.push(`Duplicate global slug "${global.slug}"`);
230
+ }
231
+ slugs.add(global.slug);
232
+ }
233
+ for (const collection of collections) {
234
+ const collectionErrors = validateCollection(collection);
235
+ errors.push(...collectionErrors);
236
+ }
237
+ for (const global of globals) {
238
+ const globalErrors = validateGlobal(global);
239
+ errors.push(...globalErrors);
240
+ }
241
+ for (const collection of collections) {
242
+ const relationshipErrors = validateRelationships(collection.fields, collections);
243
+ errors.push(...relationshipErrors);
244
+ }
245
+ if (errors.length > 0) {
246
+ throw new ConfigValidationError(errors);
247
+ }
248
+ }
249
+ function validateRelationships(fields, collections) {
250
+ const errors = [];
251
+ const collectionSlugs = new Set(collections.map((c) => c.slug));
252
+ for (const field of fields) {
253
+ if (field.type === "relationship") {
254
+ const targets = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
255
+ for (const target of targets) {
256
+ if (!collectionSlugs.has(target)) {
257
+ errors.push(`Relationship field "${field.name}" references unknown collection "${target}"`);
258
+ }
259
+ }
260
+ }
261
+ if (field.type === "upload") {
262
+ const targets = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
263
+ for (const target of targets) {
264
+ if (!collectionSlugs.has(target)) {
265
+ errors.push(`Upload field "${field.name}" references unknown collection "${target}"`);
266
+ }
267
+ }
268
+ }
269
+ if ("fields" in field && field.fields) {
270
+ const nestedErrors = validateRelationships(field.fields, collections);
271
+ errors.push(...nestedErrors);
272
+ }
273
+ if ("tabs" in field) {
274
+ for (const tab of field.tabs) {
275
+ const tabErrors = validateRelationships(tab.fields, collections);
276
+ errors.push(...tabErrors);
277
+ }
278
+ }
279
+ if ("blocks" in field) {
280
+ for (const block of field.blocks) {
281
+ const blockErrors = validateRelationships(block.fields, collections);
282
+ errors.push(...blockErrors);
283
+ }
284
+ }
285
+ }
286
+ return errors;
287
+ }
288
+ function fieldToZod(field) {
289
+ switch (field.type) {
290
+ case "text":
291
+ return textToZod(field);
292
+ case "number":
293
+ return numberToZod(field);
294
+ case "checkbox":
295
+ return checkboxToZod(field);
296
+ case "date":
297
+ return dateToZod(field);
298
+ case "email":
299
+ return emailToZod(field);
300
+ case "password":
301
+ return passwordToZod(field);
302
+ case "textarea":
303
+ return textareaToZod(field);
304
+ case "select":
305
+ return selectToZod(field);
306
+ case "radio":
307
+ return radioToZod(field);
308
+ case "color":
309
+ return colorToZod(field);
310
+ case "richtext":
311
+ return richTextToZod(field);
312
+ case "json":
313
+ return jsonToZod(field);
314
+ case "code":
315
+ return codeToZod(field);
316
+ case "upload":
317
+ return uploadToZod(field);
318
+ case "markdown":
319
+ return markdownToZod(field);
320
+ case "relationship":
321
+ return relationshipToZod(field);
322
+ case "array":
323
+ return arrayToZod(field);
324
+ case "group":
325
+ return groupToZod(field);
326
+ case "blocks":
327
+ return blocksToZod(field);
328
+ case "row":
329
+ return rowToZod(field);
330
+ case "collapsible":
331
+ return collapsibleToZod(field);
332
+ case "tabs":
333
+ return tabsToZod(field);
334
+ default:
335
+ return z.any();
336
+ }
337
+ }
338
+ function textToZod(field) {
339
+ let schema = z.string();
340
+ if (field.minLength) schema = schema.min(field.minLength);
341
+ if (field.maxLength) schema = schema.max(field.maxLength);
342
+ if (field.pattern) schema = schema.regex(new RegExp(field.pattern));
343
+ if (field.variant === "email") schema = schema.email();
344
+ if (field.variant === "url") schema = schema.url();
345
+ if (field.hasMany) schema = z.array(schema);
346
+ if (!field.required) schema = schema.optional();
347
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
348
+ return schema;
349
+ }
350
+ function numberToZod(field) {
351
+ let schema = field.integer ? z.number().int() : z.number();
352
+ if (field.min !== void 0) schema = schema.min(field.min);
353
+ if (field.max !== void 0) schema = schema.max(field.max);
354
+ if (field.step) {
355
+ schema = schema.refine(
356
+ (val) => Number.isInteger(val / field.step),
357
+ `Value must be divisible by ${field.step}`
358
+ );
359
+ }
360
+ if (field.hasMany) schema = z.array(schema);
361
+ if (!field.required) schema = schema.optional();
362
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
363
+ return schema;
364
+ }
365
+ function checkboxToZod(field) {
366
+ let schema = z.boolean();
367
+ if (!field.required) schema = schema.optional();
368
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
369
+ return schema;
370
+ }
371
+ function dateToZod(field) {
372
+ let schema = z.string().refine(
373
+ (val) => !isNaN(Date.parse(val)),
374
+ "Invalid date format"
375
+ );
376
+ if (field.minDate) {
377
+ schema = schema.refine(
378
+ (val) => new Date(val) >= new Date(field.minDate),
379
+ `Date must be after ${field.minDate}`
380
+ );
381
+ }
382
+ if (field.maxDate) {
383
+ schema = schema.refine(
384
+ (val) => new Date(val) <= new Date(field.maxDate),
385
+ `Date must be before ${field.maxDate}`
386
+ );
387
+ }
388
+ if (!field.required) schema = schema.optional();
389
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
390
+ return schema;
391
+ }
392
+ function emailToZod(field) {
393
+ let schema = z.string().email("Invalid email");
394
+ if (!field.required) schema = schema.optional();
395
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
396
+ return schema;
397
+ }
398
+ function passwordToZod(field) {
399
+ let schema = z.string().min(6, "Password must be at least 6 characters");
400
+ if (!field.required) schema = schema.optional();
401
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
402
+ return schema;
403
+ }
404
+ function textareaToZod(field) {
405
+ let schema = z.string();
406
+ if (field.minLength) schema = schema.min(field.minLength);
407
+ if (field.maxLength) schema = schema.max(field.maxLength);
408
+ if (!field.required) schema = schema.optional();
409
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
410
+ return schema;
411
+ }
412
+ function selectToZod(field) {
413
+ const values = field.options.map((opt) => opt.value);
414
+ let schema;
415
+ if (field.hasMany) {
416
+ schema = z.array(z.enum(values));
417
+ } else {
418
+ schema = z.enum(values);
419
+ }
420
+ if (!field.required) schema = schema.optional();
421
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
422
+ return schema;
423
+ }
424
+ function radioToZod(field) {
425
+ const values = field.options.map((opt) => opt.value);
426
+ let schema = z.enum(values);
427
+ if (!field.required) schema = schema.optional();
428
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
429
+ return schema;
430
+ }
431
+ function colorToZod(field) {
432
+ let schema = z.string();
433
+ if (field.format === "hex") schema = schema.regex(/^#[0-9A-Fa-f]{6}$/);
434
+ if (field.format === "rgb") schema = schema.regex(/^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/);
435
+ if (field.format === "hsl") schema = schema.regex(/^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*\)$/);
436
+ if (!field.required) schema = schema.optional();
437
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
438
+ return schema;
439
+ }
440
+ function richTextToZod(field) {
441
+ let schema = z.array(z.object({
442
+ type: z.string(),
443
+ data: z.record(z.any()),
444
+ children: z.array(z.any()).optional()
445
+ }));
446
+ if (!field.required) schema = schema.optional();
447
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
448
+ return schema;
449
+ }
450
+ function jsonToZod(field) {
451
+ let schema = z.record(z.any());
452
+ if (!field.required) schema = schema.optional();
453
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
454
+ return schema;
455
+ }
456
+ function codeToZod(field) {
457
+ let schema = z.string();
458
+ if (!field.required) schema = schema.optional();
459
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
460
+ return schema;
461
+ }
462
+ function uploadToZod(field) {
463
+ let schema = z.string();
464
+ if (field.hasMany) schema = z.array(z.string());
465
+ if (!field.required) schema = schema.optional();
466
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
467
+ return schema;
468
+ }
469
+ function markdownToZod(field) {
470
+ let schema = z.string();
471
+ if (!field.required) schema = schema.optional();
472
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
473
+ return schema;
474
+ }
475
+ function relationshipToZod(field) {
476
+ let schema = z.string();
477
+ if (Array.isArray(field.relationTo)) {
478
+ schema = z.object({
479
+ relationTo: z.enum(field.relationTo),
480
+ value: z.string()
481
+ });
482
+ }
483
+ if (field.hasMany) schema = z.array(schema);
484
+ if (!field.required) schema = schema.optional();
485
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
486
+ return schema;
487
+ }
488
+ function arrayToZod(field) {
489
+ const itemSchema = z.object(
490
+ Object.fromEntries(
491
+ field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
492
+ )
493
+ );
494
+ let schema = z.array(itemSchema);
495
+ if (field.minRows) schema = schema.min(field.minRows);
496
+ if (field.maxRows) schema = schema.max(field.maxRows);
497
+ if (!field.required) schema = schema.optional();
498
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
499
+ return schema;
500
+ }
501
+ function groupToZod(field) {
502
+ const schema = z.object(
503
+ Object.fromEntries(
504
+ field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
505
+ )
506
+ );
507
+ if (!field.required) return schema.optional();
508
+ return schema;
509
+ }
510
+ function blocksToZod(field) {
511
+ const blockSchemas = field.blocks.map((block) => {
512
+ return z.object({
513
+ blockType: z.literal(block.slug),
514
+ ...Object.fromEntries(
515
+ block.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
516
+ )
517
+ });
518
+ });
519
+ let schema = z.array(z.discriminatedUnion("blockType", blockSchemas));
520
+ if (field.minRows) schema = schema.min(field.minRows);
521
+ if (field.maxRows) schema = schema.max(field.maxRows);
522
+ if (!field.required) schema = schema.optional();
523
+ if (field.validate) schema = addCustomValidation(schema, field.validate);
524
+ return schema;
525
+ }
526
+ function rowToZod(field) {
527
+ const schema = z.object(
528
+ Object.fromEntries(
529
+ field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
530
+ )
531
+ );
532
+ return schema;
533
+ }
534
+ function collapsibleToZod(field) {
535
+ const schema = z.object(
536
+ Object.fromEntries(
537
+ field.fields.filter((f) => f.name).map((f) => [f.name, fieldToZod(f)])
538
+ )
539
+ );
540
+ return schema;
541
+ }
542
+ function tabsToZod(field) {
543
+ const schemas = {};
544
+ for (const tab of field.tabs) {
545
+ for (const f of tab.fields) {
546
+ if (f.name) {
547
+ schemas[f.name] = fieldToZod(f);
548
+ }
549
+ }
550
+ }
551
+ return z.object(schemas);
552
+ }
553
+ function addCustomValidation(schema, validate) {
554
+ return schema.refine(
555
+ async (val) => {
556
+ const result = await validate(val, { required: false });
557
+ return result === true;
558
+ },
559
+ {
560
+ message: "Custom validation failed"
561
+ }
562
+ );
563
+ }
564
+ function collectionToZod(collection) {
565
+ const shape = {};
566
+ for (const field of collection.fields) {
567
+ if (field.name) {
568
+ shape[field.name] = fieldToZod(field);
569
+ }
570
+ }
571
+ if (collection.timestamps) {
572
+ shape["createdAt"] = z.string().optional();
573
+ shape["updatedAt"] = z.string().optional();
574
+ }
575
+ if (collection.tenantScoped) {
576
+ shape["tenantID"] = z.string().optional();
577
+ }
578
+ shape["id"] = z.string().optional();
579
+ return z.object(shape);
580
+ }
581
+ function collectionToCreateZod(collection) {
582
+ const shape = {};
583
+ for (const field of collection.fields) {
584
+ if (field.name) {
585
+ shape[field.name] = fieldToZod(field);
586
+ }
587
+ }
588
+ return z.object(shape);
589
+ }
590
+ function collectionToUpdateZod(collection) {
591
+ const shape = {};
592
+ for (const field of collection.fields) {
593
+ if (field.name) {
594
+ shape[field.name] = fieldToZod(field).optional();
595
+ }
596
+ }
597
+ return z.object(shape);
598
+ }
599
+ function collectionToWhereZod(collection) {
600
+ const shape = {};
601
+ for (const field of collection.fields) {
602
+ if (field.name) {
603
+ shape[field.name] = z.object({
604
+ equals: z.any().optional(),
605
+ not_equals: z.any().optional(),
606
+ in: z.array(z.any()).optional(),
607
+ not_in: z.array(z.any()).optional(),
608
+ greater_than: z.number().optional(),
609
+ greater_than_equal: z.number().optional(),
610
+ less_than: z.number().optional(),
611
+ less_than_equal: z.number().optional(),
612
+ like: z.string().optional(),
613
+ not_like: z.string().optional(),
614
+ contains: z.string().optional(),
615
+ exists: z.boolean().optional()
616
+ }).optional();
617
+ }
618
+ }
619
+ shape["AND"] = z.array(z.lazy(() => z.object(shape))).optional();
620
+ shape["OR"] = z.array(z.lazy(() => z.object(shape))).optional();
621
+ return z.object(shape).optional();
622
+ }
623
+ function globalToZod(global) {
624
+ const shape = {};
625
+ for (const field of global.fields) {
626
+ if (field.name) {
627
+ shape[field.name] = fieldToZod(field);
628
+ }
629
+ }
630
+ shape["id"] = z.string().optional();
631
+ return z.object(shape);
632
+ }
633
+
634
+ // src/registry/index.ts
635
+ var Registry = class {
636
+ collections = /* @__PURE__ */ new Map();
637
+ globals = /* @__PURE__ */ new Map();
638
+ plugins = [];
639
+ schemaCache = /* @__PURE__ */ new Map();
640
+ initialized = false;
641
+ // ========================================================================
642
+ // Collection Management
643
+ // ========================================================================
644
+ addCollection(config) {
645
+ if (this.initialized) {
646
+ throw new Error("Cannot add collections after Registry has been initialized");
647
+ }
648
+ const errors = validateCollection(config);
649
+ if (errors.length > 0) {
650
+ throw new Error(`Invalid collection config: ${errors.join(", ")}`);
651
+ }
652
+ let finalConfig = { ...config };
653
+ for (const plugin of this.plugins) {
654
+ if (plugin.extendCollection) {
655
+ finalConfig = plugin.extendCollection(finalConfig.slug, finalConfig);
656
+ }
657
+ }
658
+ finalConfig.fields = this.applyFieldDefaults(finalConfig);
659
+ this.collections.set(finalConfig.slug, finalConfig);
660
+ this.clearSchemaCache(finalConfig.slug);
661
+ }
662
+ addCollections(configs) {
663
+ for (const config of configs) {
664
+ this.addCollection(config);
665
+ }
666
+ }
667
+ getCollection(slug) {
668
+ return this.collections.get(slug);
669
+ }
670
+ getCollections() {
671
+ return Array.from(this.collections.values());
672
+ }
673
+ getCollectionSlugs() {
674
+ return Array.from(this.collections.keys());
675
+ }
676
+ hasCollection(slug) {
677
+ return this.collections.has(slug);
678
+ }
679
+ removeCollection(slug) {
680
+ if (this.initialized) {
681
+ throw new Error("Cannot remove collections after Registry has been initialized");
682
+ }
683
+ this.clearSchemaCache(slug);
684
+ return this.collections.delete(slug);
685
+ }
686
+ // ========================================================================
687
+ // Global Management
688
+ // ========================================================================
689
+ addGlobal(config) {
690
+ if (this.initialized) {
691
+ throw new Error("Cannot add globals after Registry has been initialized");
692
+ }
693
+ const errors = validateGlobal(config);
694
+ if (errors.length > 0) {
695
+ throw new Error(`Invalid global config: ${errors.join(", ")}`);
696
+ }
697
+ let finalConfig = { ...config };
698
+ for (const plugin of this.plugins) {
699
+ if (plugin.extendGlobal) {
700
+ finalConfig = plugin.extendGlobal(finalConfig.slug, finalConfig);
701
+ }
702
+ }
703
+ this.globals.set(finalConfig.slug, finalConfig);
704
+ this.clearSchemaCache(`global:${finalConfig.slug}`);
705
+ }
706
+ addGlobals(configs) {
707
+ for (const config of configs) {
708
+ this.addGlobal(config);
709
+ }
710
+ }
711
+ getGlobal(slug) {
712
+ return this.globals.get(slug);
713
+ }
714
+ getGlobals() {
715
+ return Array.from(this.globals.values());
716
+ }
717
+ getGlobalSlugs() {
718
+ return Array.from(this.globals.keys());
719
+ }
720
+ hasGlobal(slug) {
721
+ return this.globals.has(slug);
722
+ }
723
+ removeGlobal(slug) {
724
+ if (this.initialized) {
725
+ throw new Error("Cannot remove globals after Registry has been initialized");
726
+ }
727
+ this.clearSchemaCache(`global:${slug}`);
728
+ return this.globals.delete(slug);
729
+ }
730
+ // ========================================================================
731
+ // Plugin Management
732
+ // ========================================================================
733
+ addPlugin(plugin) {
734
+ if (this.initialized) {
735
+ throw new Error("Cannot add plugins after Registry has been initialized");
736
+ }
737
+ this.plugins.push(plugin);
738
+ }
739
+ getPlugins() {
740
+ return [...this.plugins];
741
+ }
742
+ // ========================================================================
743
+ // Schema Generation
744
+ // ========================================================================
745
+ getZodSchema(slug) {
746
+ const cached = this.schemaCache.get(slug);
747
+ if (cached) return cached;
748
+ const collection = this.collections.get(slug);
749
+ if (collection) {
750
+ const schema = collectionToZod(collection);
751
+ this.schemaCache.set(slug, schema);
752
+ return schema;
753
+ }
754
+ const global = this.globals.get(slug);
755
+ if (global) {
756
+ const schema = globalToZod(global);
757
+ this.schemaCache.set(`global:${slug}`, schema);
758
+ return schema;
759
+ }
760
+ throw new Error(`No collection or global found with slug "${slug}"`);
761
+ }
762
+ getCreateZodSchema(slug) {
763
+ const cacheKey = `${slug}:create`;
764
+ const cached = this.schemaCache.get(cacheKey);
765
+ if (cached) return cached;
766
+ const collection = this.collections.get(slug);
767
+ if (collection) {
768
+ const schema = collectionToCreateZod(collection);
769
+ this.schemaCache.set(cacheKey, schema);
770
+ return schema;
771
+ }
772
+ throw new Error(`No collection found with slug "${slug}"`);
773
+ }
774
+ getUpdateZodSchema(slug) {
775
+ const cacheKey = `${slug}:update`;
776
+ const cached = this.schemaCache.get(cacheKey);
777
+ if (cached) return cached;
778
+ const collection = this.collections.get(slug);
779
+ if (collection) {
780
+ const schema = collectionToUpdateZod(collection);
781
+ this.schemaCache.set(cacheKey, schema);
782
+ return schema;
783
+ }
784
+ throw new Error(`No collection found with slug "${slug}"`);
785
+ }
786
+ getWhereZodSchema(slug) {
787
+ const cacheKey = `${slug}:where`;
788
+ const cached = this.schemaCache.get(cacheKey);
789
+ if (cached) return cached;
790
+ const collection = this.collections.get(slug);
791
+ if (collection) {
792
+ const schema = collectionToWhereZod(collection);
793
+ this.schemaCache.set(cacheKey, schema);
794
+ return schema;
795
+ }
796
+ throw new Error(`No collection found with slug "${slug}"`);
797
+ }
798
+ getFieldZodSchema(field) {
799
+ return fieldToZod(field);
800
+ }
801
+ clearSchemaCache(slug) {
802
+ this.schemaCache.delete(slug);
803
+ this.schemaCache.delete(`${slug}:create`);
804
+ this.schemaCache.delete(`${slug}:update`);
805
+ this.schemaCache.delete(`${slug}:where`);
806
+ }
807
+ // ========================================================================
808
+ // Field Helpers
809
+ // ========================================================================
810
+ applyFieldDefaults(config) {
811
+ const fields = [...config.fields];
812
+ if (!fields.some((f) => f.name === "id")) {
813
+ fields.unshift({
814
+ name: "id",
815
+ type: "text",
816
+ admin: { readOnly: true, hidden: true }
817
+ });
818
+ }
819
+ if (config.tenantScoped && !fields.some((f) => f.name === "tenantID")) {
820
+ fields.push({
821
+ name: "tenantID",
822
+ type: "text",
823
+ required: true,
824
+ admin: { readOnly: true, hidden: true }
825
+ });
826
+ }
827
+ if (config.timestamps && !fields.some((f) => f.name === "createdAt")) {
828
+ fields.push({
829
+ name: "createdAt",
830
+ type: "date",
831
+ admin: { readOnly: true, hidden: true }
832
+ });
833
+ fields.push({
834
+ name: "updatedAt",
835
+ type: "date",
836
+ admin: { readOnly: true, hidden: true }
837
+ });
838
+ }
839
+ return fields;
840
+ }
841
+ getFields(slug) {
842
+ const collection = this.collections.get(slug);
843
+ if (collection) return collection.fields;
844
+ const global = this.globals.get(slug);
845
+ if (global) return global.fields;
846
+ throw new Error(`No collection or global found with slug "${slug}"`);
847
+ }
848
+ getFieldMap(slug) {
849
+ const fields = this.getFields(slug);
850
+ const map = /* @__PURE__ */ new Map();
851
+ const addFields = (fields2) => {
852
+ for (const field of fields2) {
853
+ if (field.name) {
854
+ map.set(field.name, field);
855
+ }
856
+ if ("fields" in field && field.fields) {
857
+ addFields(field.fields);
858
+ }
859
+ if ("tabs" in field) {
860
+ for (const tab of field.tabs) {
861
+ addFields(tab.fields);
862
+ }
863
+ }
864
+ if ("blocks" in field) {
865
+ for (const block of field.blocks) {
866
+ addFields(block.fields);
867
+ }
868
+ }
869
+ }
870
+ };
871
+ addFields(fields);
872
+ return map;
873
+ }
874
+ getVisibleFields(slug) {
875
+ const fields = this.getFields(slug);
876
+ return fields.filter((f) => !f.admin?.hidden);
877
+ }
878
+ // ========================================================================
879
+ // Initialization
880
+ // ========================================================================
881
+ validate() {
882
+ const collections = this.getCollections();
883
+ const globals = this.getGlobals();
884
+ validateConfig(collections, globals);
885
+ }
886
+ async init() {
887
+ this.validate();
888
+ for (const plugin of this.plugins) {
889
+ if (plugin.init) {
890
+ await plugin.init(this);
891
+ }
892
+ }
893
+ this.initialized = true;
894
+ }
895
+ isInitialized() {
896
+ return this.initialized;
897
+ }
898
+ // ========================================================================
899
+ // Query Helpers
900
+ // ========================================================================
901
+ getPaginationDefaults(slug) {
902
+ const collection = this.collections.get(slug);
903
+ return {
904
+ defaultLimit: collection?.admin?.pagination?.defaultLimit || 10,
905
+ limits: collection?.admin?.pagination?.limits || [10, 25, 50, 100]
906
+ };
907
+ }
908
+ getDefaultSort(slug) {
909
+ const collection = this.collections.get(slug);
910
+ const useAsTitle = collection?.admin?.useAsTitle;
911
+ if (useAsTitle) return useAsTitle;
912
+ return "createdAt";
913
+ }
914
+ getDefaultColumns(slug) {
915
+ const collection = this.collections.get(slug);
916
+ if (collection?.admin?.defaultColumns) {
917
+ return collection.admin.defaultColumns;
918
+ }
919
+ const fields = this.getVisibleFields(slug);
920
+ return fields.slice(0, 4).map((f) => f.name);
921
+ }
922
+ // ========================================================================
923
+ // Admin Helpers
924
+ // ========================================================================
925
+ getAdminTitle(slug) {
926
+ const collection = this.collections.get(slug);
927
+ return collection?.label || collection?.admin?.description || slug;
928
+ }
929
+ getAdminLabel(slug) {
930
+ const collection = this.collections.get(slug);
931
+ return collection?.singularLabel || collection?.label || slug;
932
+ }
933
+ getAdminGroup(slug) {
934
+ return this.collections.get(slug)?.admin?.group;
935
+ }
936
+ // ========================================================================
937
+ // Debug / Stats
938
+ // ========================================================================
939
+ getStats() {
940
+ let totalFields = 0;
941
+ for (const collection of this.collections.values()) {
942
+ totalFields += collection.fields.length;
943
+ }
944
+ for (const global of this.globals.values()) {
945
+ totalFields += global.fields.length;
946
+ }
947
+ return {
948
+ collections: this.collections.size,
949
+ globals: this.globals.size,
950
+ plugins: this.plugins.length,
951
+ fields: totalFields
952
+ };
953
+ }
954
+ toJSON() {
955
+ return {
956
+ collections: this.getCollections(),
957
+ globals: this.getGlobals()
958
+ };
959
+ }
960
+ };
961
+ var instance = null;
962
+ function getRegistry() {
963
+ if (!instance) {
964
+ instance = new Registry();
965
+ }
966
+ return instance;
967
+ }
968
+ function resetRegistry() {
969
+ instance = null;
970
+ }
971
+ function createRegistry() {
972
+ instance = new Registry();
973
+ return instance;
974
+ }
975
+ var Kyro = class {
976
+ registry;
977
+ db;
978
+ pubsub;
979
+ wsServer;
980
+ config;
981
+ constructor(config) {
982
+ this.config = config;
983
+ this.registry = createRegistry();
984
+ this.db = config.adapter;
985
+ this.pubsub = new KyroPubSub(this.registry);
986
+ if (config.collections) {
987
+ this.registry.addCollections(config.collections);
988
+ }
989
+ if (config.globals) {
990
+ this.registry.addGlobals(config.globals);
991
+ }
992
+ if (config.plugins) {
993
+ for (const plugin of config.plugins) {
994
+ this.registry.addPlugin(plugin);
995
+ }
996
+ }
997
+ }
998
+ async init() {
999
+ await this.registry.init();
1000
+ await this.db.init(this.registry.getCollections(), this.registry.getGlobals());
1001
+ this.pubsub.autoRegisterHooks();
1002
+ console.log("\u2705 Kyro CMS initialized");
1003
+ console.log(` Collections: ${this.registry.getCollections().length}`);
1004
+ console.log(` Globals: ${this.registry.getGlobals().length}`);
1005
+ }
1006
+ // ============================================================================
1007
+ // API Methods
1008
+ // ============================================================================
1009
+ getREST(options) {
1010
+ return createHonoApp({
1011
+ registry: this.registry,
1012
+ db: this.db,
1013
+ ...options,
1014
+ cors: this.config.cors
1015
+ });
1016
+ }
1017
+ getGraphQL(options) {
1018
+ return buildGraphQLSchema({
1019
+ registry: this.registry,
1020
+ db: this.db,
1021
+ ...options
1022
+ });
1023
+ }
1024
+ getTRPC(options) {
1025
+ return createKyroServer({
1026
+ registry: this.registry,
1027
+ db: this.db,
1028
+ req: options?.req || { headers: {} },
1029
+ ...options
1030
+ });
1031
+ }
1032
+ async startWebSocket(options) {
1033
+ this.wsServer = createWSServer({
1034
+ pubsub: this.pubsub,
1035
+ port: options?.port || 8080,
1036
+ requireAuth: options?.requireAuth,
1037
+ verifyToken: options?.verifyToken
1038
+ });
1039
+ console.log(`\u{1F50C} WebSocket server started on port ${options?.port || 8080}`);
1040
+ return this.wsServer;
1041
+ }
1042
+ // ============================================================================
1043
+ // Lifecycle
1044
+ // ============================================================================
1045
+ async shutdown() {
1046
+ if (this.wsServer) {
1047
+ await this.wsServer.close();
1048
+ }
1049
+ await this.db.disconnect();
1050
+ console.log("\u{1F44B} Kyro CMS shut down");
1051
+ }
1052
+ };
1053
+ function createKyro(config) {
1054
+ return new Kyro(config);
1055
+ }
1056
+
1057
+ // src/fields/types.ts
1058
+ function isTextField(field) {
1059
+ return field.type === "text";
1060
+ }
1061
+ function isNumberField(field) {
1062
+ return field.type === "number";
1063
+ }
1064
+ function isRelationshipField(field) {
1065
+ return field.type === "relationship";
1066
+ }
1067
+ function isArrayField(field) {
1068
+ return field.type === "array";
1069
+ }
1070
+ function isGroupField(field) {
1071
+ return field.type === "group";
1072
+ }
1073
+ function isBlocksField(field) {
1074
+ return field.type === "blocks";
1075
+ }
1076
+ function isUploadField(field) {
1077
+ return field.type === "upload";
1078
+ }
1079
+ function isRichTextField(field) {
1080
+ return field.type === "richtext";
1081
+ }
1082
+ function isSelectField(field) {
1083
+ return field.type === "select";
1084
+ }
1085
+ function isLayoutField(field) {
1086
+ return field.type === "row" || field.type === "collapsible" || field.type === "tabs";
1087
+ }
1088
+ var PRIMITIVE_FIELD_TYPES = [
1089
+ "text",
1090
+ "number",
1091
+ "checkbox",
1092
+ "date",
1093
+ "email",
1094
+ "password",
1095
+ "textarea",
1096
+ "select",
1097
+ "radio",
1098
+ "color"
1099
+ ];
1100
+ var COMPLEX_FIELD_TYPES = [
1101
+ "richtext",
1102
+ "json",
1103
+ "code",
1104
+ "upload",
1105
+ "markdown"
1106
+ ];
1107
+ var RELATIONAL_FIELD_TYPES = [
1108
+ "relationship",
1109
+ "array",
1110
+ "group",
1111
+ "blocks"
1112
+ ];
1113
+ var LAYOUT_FIELD_TYPES = [
1114
+ "row",
1115
+ "collapsible",
1116
+ "tabs"
1117
+ ];
1118
+ var ALL_FIELD_TYPES = [
1119
+ ...PRIMITIVE_FIELD_TYPES,
1120
+ ...COMPLEX_FIELD_TYPES,
1121
+ ...RELATIONAL_FIELD_TYPES,
1122
+ ...LAYOUT_FIELD_TYPES
1123
+ ];
1124
+
1125
+ // src/hooks/types.ts
1126
+ async function runHooks(hooks, args) {
1127
+ let result = args.data;
1128
+ for (const hook of hooks) {
1129
+ const hookResult = await hook({
1130
+ ...args,
1131
+ data: result
1132
+ });
1133
+ if (hookResult !== void 0) {
1134
+ result = hookResult;
1135
+ }
1136
+ }
1137
+ return result;
1138
+ }
1139
+ async function runFieldHooks(hooks, args) {
1140
+ return runHooks(hooks, args);
1141
+ }
1142
+
1143
+ // src/database/local/adapter.ts
1144
+ var LocalAdapter = class extends AbstractBaseAdapter {
1145
+ db;
1146
+ migrations = /* @__PURE__ */ new Map();
1147
+ constructor(options) {
1148
+ super();
1149
+ if (options.db) {
1150
+ this.db = options.db;
1151
+ } else if (options.path) {
1152
+ this.db = null;
1153
+ } else {
1154
+ this.db = null;
1155
+ }
1156
+ }
1157
+ async connect() {
1158
+ if (!this.db) {
1159
+ const Database = (await import('better-sqlite3')).default;
1160
+ this.db = new Database(":memory:");
1161
+ }
1162
+ this.db.pragma("journal_mode = WAL");
1163
+ this.db.pragma("foreign_keys = ON");
1164
+ this.connected = true;
1165
+ console.log("[LocalAdapter] Connected to SQLite");
1166
+ }
1167
+ async disconnect() {
1168
+ if (this.db) {
1169
+ this.db.close();
1170
+ }
1171
+ this.connected = false;
1172
+ console.log("[LocalAdapter] Disconnected");
1173
+ }
1174
+ // ========================================================================
1175
+ // Schema Management
1176
+ // ========================================================================
1177
+ ensureTable(config) {
1178
+ const tableName = this.getTableNameFor(config.slug);
1179
+ if (this.migrations.has(tableName)) return;
1180
+ const columns = [
1181
+ `id TEXT PRIMARY KEY`
1182
+ ];
1183
+ for (const field of config.fields) {
1184
+ if (!field.name || field.name === "id") continue;
1185
+ const colDef = this.fieldToSQL(field);
1186
+ if (colDef) columns.push(colDef);
1187
+ }
1188
+ if (config.timestamps) {
1189
+ columns.push(`created_at TEXT DEFAULT (datetime('now'))`);
1190
+ columns.push(`updated_at TEXT DEFAULT (datetime('now'))`);
1191
+ }
1192
+ if (config.tenantScoped) {
1193
+ columns.push(`tenant_id TEXT NOT NULL`);
1194
+ }
1195
+ const createSQL = `CREATE TABLE IF NOT EXISTS ${tableName} (${columns.join(", ")})`;
1196
+ this.db.exec(createSQL);
1197
+ for (const field of config.fields) {
1198
+ if (field.name && field.indexed) {
1199
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${tableName}_${field.name} ON ${tableName}(${field.name})`);
1200
+ }
1201
+ if (field.name && field.unique) {
1202
+ this.db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_${tableName}_${field.name}_unique ON ${tableName}(${field.name})`);
1203
+ }
1204
+ }
1205
+ this.migrations.set(tableName, true);
1206
+ }
1207
+ fieldToSQL(field) {
1208
+ switch (field.type) {
1209
+ case "text":
1210
+ case "email":
1211
+ case "password":
1212
+ case "textarea":
1213
+ case "color":
1214
+ case "code":
1215
+ case "markdown":
1216
+ case "url":
1217
+ return `${field.name} TEXT`;
1218
+ case "number":
1219
+ return `${field.name} REAL`;
1220
+ case "checkbox":
1221
+ return `${field.name} INTEGER DEFAULT 0`;
1222
+ case "date":
1223
+ return `${field.name} TEXT`;
1224
+ case "select":
1225
+ case "radio":
1226
+ return `${field.name} TEXT`;
1227
+ case "relationship":
1228
+ case "upload":
1229
+ return `${field.name} TEXT`;
1230
+ case "json":
1231
+ case "richtext":
1232
+ case "array":
1233
+ case "group":
1234
+ case "blocks":
1235
+ return `${field.name} TEXT`;
1236
+ default:
1237
+ return null;
1238
+ }
1239
+ }
1240
+ // ========================================================================
1241
+ // CRUD Operations
1242
+ // ========================================================================
1243
+ async find(args) {
1244
+ const { collection: slug, where = {}, sort, limit = 10, page = 1, tenantID } = args;
1245
+ const config = this.getCollection(slug);
1246
+ this.ensureTable(config);
1247
+ const tableName = this.getTableNameFor(slug);
1248
+ let sql = `SELECT * FROM ${tableName}`;
1249
+ const params = [];
1250
+ const conditions = [];
1251
+ if (tenantID && config.tenantScoped) {
1252
+ conditions.push(`tenant_id = ?`);
1253
+ params.push(tenantID);
1254
+ }
1255
+ for (const [key, value] of Object.entries(where)) {
1256
+ if (key === "AND" || key === "OR") continue;
1257
+ if (typeof value === "object" && value !== null) {
1258
+ if (value.equals !== void 0) {
1259
+ conditions.push(`${key} = ?`);
1260
+ params.push(value.equals);
1261
+ }
1262
+ if (value.in !== void 0) {
1263
+ conditions.push(`${key} IN (${value.in.map(() => "?").join(", ")})`);
1264
+ params.push(...value.in);
1265
+ }
1266
+ if (value.not_equals !== void 0) {
1267
+ conditions.push(`${key} != ?`);
1268
+ params.push(value.not_equals);
1269
+ }
1270
+ } else {
1271
+ conditions.push(`${key} = ?`);
1272
+ params.push(value);
1273
+ }
1274
+ }
1275
+ if (conditions.length > 0) {
1276
+ sql += ` WHERE ${conditions.join(" AND ")}`;
1277
+ }
1278
+ const sortField = sort?.replace("-", "") || "created_at";
1279
+ const sortDir = sort?.startsWith("-") ? "DESC" : "ASC";
1280
+ sql += ` ORDER BY ${sortField} ${sortDir}`;
1281
+ const countSql = sql.replace("SELECT *", "SELECT COUNT(*) as count");
1282
+ const countResult = this.db.prepare(countSql).get(...params);
1283
+ const totalDocs = countResult?.count || 0;
1284
+ sql += ` LIMIT ? OFFSET ?`;
1285
+ params.push(limit, (page - 1) * limit);
1286
+ const rows = this.db.prepare(sql).all(...params);
1287
+ const docs = rows.map((row) => this.rowToDoc(row, config));
1288
+ return {
1289
+ docs,
1290
+ totalDocs,
1291
+ limit,
1292
+ totalPages: Math.ceil(totalDocs / limit),
1293
+ page,
1294
+ pagingCounter: (page - 1) * limit + 1,
1295
+ hasPrevPage: page > 1,
1296
+ hasNextPage: page < Math.ceil(totalDocs / limit),
1297
+ prevPage: page > 1 ? page - 1 : null,
1298
+ nextPage: page < Math.ceil(totalDocs / limit) ? page + 1 : null
1299
+ };
1300
+ }
1301
+ async findByID(args) {
1302
+ const { collection: slug, id, tenantID } = args;
1303
+ const config = this.getCollection(slug);
1304
+ this.ensureTable(config);
1305
+ const tableName = this.getTableNameFor(slug);
1306
+ let sql = `SELECT * FROM ${tableName} WHERE id = ?`;
1307
+ const params = [id];
1308
+ if (tenantID && config.tenantScoped) {
1309
+ sql += ` AND tenant_id = ?`;
1310
+ params.push(tenantID);
1311
+ }
1312
+ const row = this.db.prepare(sql).get(...params);
1313
+ if (!row) return null;
1314
+ return this.rowToDoc(row, config);
1315
+ }
1316
+ async create(args) {
1317
+ const { collection: slug, data, tenantID } = args;
1318
+ const config = this.getCollection(slug);
1319
+ this.ensureTable(config);
1320
+ const tableName = this.getTableNameFor(slug);
1321
+ const id = data.id || this.generateId();
1322
+ const insertData = this.prepareData(data, config);
1323
+ insertData.id = id;
1324
+ insertData.created_at = (/* @__PURE__ */ new Date()).toISOString();
1325
+ insertData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
1326
+ if (tenantID && config.tenantScoped) {
1327
+ insertData.tenant_id = tenantID;
1328
+ }
1329
+ const columns = Object.keys(insertData);
1330
+ const placeholders = columns.map(() => "?").join(", ");
1331
+ const values = Object.values(insertData).map(
1332
+ (v) => typeof v === "object" ? JSON.stringify(v) : v
1333
+ );
1334
+ this.db.prepare(
1335
+ `INSERT INTO ${tableName} (${columns.join(", ")}) VALUES (${placeholders})`
1336
+ ).run(...values);
1337
+ return { ...insertData, id };
1338
+ }
1339
+ async update(args) {
1340
+ const { collection: slug, id, data, tenantID } = args;
1341
+ const config = this.getCollection(slug);
1342
+ this.ensureTable(config);
1343
+ const tableName = this.getTableNameFor(slug);
1344
+ const updateData = this.prepareData(data, config);
1345
+ updateData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
1346
+ const columns = Object.keys(updateData);
1347
+ const setClause = columns.map((c) => `${c} = ?`).join(", ");
1348
+ const values = Object.values(updateData).map(
1349
+ (v) => typeof v === "object" ? JSON.stringify(v) : v
1350
+ );
1351
+ let sql = `UPDATE ${tableName} SET ${setClause} WHERE id = ?`;
1352
+ const params = [...values, id];
1353
+ if (tenantID && config.tenantScoped) {
1354
+ sql += ` AND tenant_id = ?`;
1355
+ params.push(tenantID);
1356
+ }
1357
+ this.db.prepare(sql).run(...params);
1358
+ return this.findByID({ collection: slug, id, tenantID });
1359
+ }
1360
+ async delete(args) {
1361
+ const { collection: slug, id, tenantID } = args;
1362
+ const config = this.getCollection(slug);
1363
+ this.ensureTable(config);
1364
+ const doc = await this.findByID({ collection: slug, id, tenantID });
1365
+ if (!doc) throw new Error(`Document not found: ${slug}/${id}`);
1366
+ const tableName = this.getTableNameFor(slug);
1367
+ let sql = `DELETE FROM ${tableName} WHERE id = ?`;
1368
+ const params = [id];
1369
+ if (tenantID && config.tenantScoped) {
1370
+ sql += ` AND tenant_id = ?`;
1371
+ params.push(tenantID);
1372
+ }
1373
+ this.db.prepare(sql).run(...params);
1374
+ return doc;
1375
+ }
1376
+ async count(args) {
1377
+ const { collection: slug, tenantID } = args;
1378
+ const config = this.getCollection(slug);
1379
+ this.ensureTable(config);
1380
+ const tableName = this.getTableNameFor(slug);
1381
+ let sql = `SELECT COUNT(*) as count FROM ${tableName}`;
1382
+ const params = [];
1383
+ if (tenantID && config.tenantScoped) {
1384
+ sql += ` WHERE tenant_id = ?`;
1385
+ params.push(tenantID);
1386
+ }
1387
+ const result = this.db.prepare(sql).get(...params);
1388
+ return result?.count || 0;
1389
+ }
1390
+ async findOne(args) {
1391
+ const result = await this.find({ ...args, limit: 1 });
1392
+ return result.docs[0] || null;
1393
+ }
1394
+ // ========================================================================
1395
+ // Version Support (Placeholder)
1396
+ // ========================================================================
1397
+ async findVersions() {
1398
+ return { docs: [], totalDocs: 0, limit: 10, totalPages: 0, page: 1, pagingCounter: 0, hasPrevPage: false, hasNextPage: false, prevPage: null, nextPage: null };
1399
+ }
1400
+ async findVersionByID() {
1401
+ return null;
1402
+ }
1403
+ async createVersion() {
1404
+ return {};
1405
+ }
1406
+ async deleteVersions() {
1407
+ }
1408
+ // ========================================================================
1409
+ // Helpers
1410
+ // ========================================================================
1411
+ rowToDoc(row, config) {
1412
+ const doc = { id: row.id };
1413
+ for (const field of config.fields) {
1414
+ if (!field.name || field.name === "id") continue;
1415
+ let value = row[field.name];
1416
+ if (field.type === "json" || field.type === "richtext" || field.type === "array" || field.type === "group" || field.type === "blocks") {
1417
+ try {
1418
+ value = value ? JSON.parse(value) : null;
1419
+ } catch {
1420
+ value = null;
1421
+ }
1422
+ }
1423
+ if (field.type === "checkbox") {
1424
+ value = Boolean(value);
1425
+ }
1426
+ if (field.type === "date" && value) {
1427
+ value = new Date(value).toISOString();
1428
+ }
1429
+ doc[field.name] = value;
1430
+ }
1431
+ if (config.timestamps) {
1432
+ doc.createdAt = row.created_at;
1433
+ doc.updatedAt = row.updated_at;
1434
+ }
1435
+ if (config.tenantScoped) {
1436
+ doc.tenantID = row.tenant_id;
1437
+ }
1438
+ return doc;
1439
+ }
1440
+ generateId() {
1441
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
1442
+ }
1443
+ getTableNameFor(slug) {
1444
+ return slug.replace(/-/g, "_");
1445
+ }
1446
+ // ========================================================================
1447
+ // Migrations
1448
+ // ========================================================================
1449
+ async migrate() {
1450
+ for (const config of this.collections.values()) {
1451
+ this.ensureTable(config);
1452
+ }
1453
+ console.log("[LocalAdapter] Migrations complete");
1454
+ }
1455
+ async rollback() {
1456
+ console.log("[LocalAdapter] Rollback not supported for schema changes");
1457
+ }
1458
+ // ========================================================================
1459
+ // Transaction Support
1460
+ // ========================================================================
1461
+ async transaction(fn) {
1462
+ return new Promise((resolve, reject) => {
1463
+ const tx = this.db.transaction(async () => {
1464
+ return fn({ db: this.db });
1465
+ });
1466
+ try {
1467
+ const result = tx();
1468
+ resolve(result);
1469
+ } catch (error) {
1470
+ reject(error);
1471
+ }
1472
+ });
1473
+ }
1474
+ // ========================================================================
1475
+ // Direct DB Access
1476
+ // ========================================================================
1477
+ getDatabase() {
1478
+ return this.db;
1479
+ }
1480
+ exec(sql) {
1481
+ this.db.exec(sql);
1482
+ }
1483
+ prepare(sql) {
1484
+ return this.db.prepare(sql);
1485
+ }
1486
+ };
1487
+ function createLocalAdapter(options) {
1488
+ return new LocalAdapter(options || {});
1489
+ }
1490
+
1491
+ // src/plugins/index.ts
1492
+ var KyroPlugin = class {
1493
+ name;
1494
+ version;
1495
+ description;
1496
+ hooks = {};
1497
+ collections = [];
1498
+ globals = [];
1499
+ fields = [];
1500
+ extensions = { collections: [], globals: [] };
1501
+ adminComponents = {};
1502
+ adminStyles = [];
1503
+ serverMiddleware;
1504
+ clientMiddleware;
1505
+ constructor(name) {
1506
+ this.name = name;
1507
+ }
1508
+ async init(api) {
1509
+ }
1510
+ async beforeInit(api) {
1511
+ }
1512
+ async afterInit(api) {
1513
+ }
1514
+ getCollections() {
1515
+ return this.collections;
1516
+ }
1517
+ getGlobals() {
1518
+ return this.globals;
1519
+ }
1520
+ getHooks() {
1521
+ return this.hooks;
1522
+ }
1523
+ };
1524
+ var PluginManager = class {
1525
+ plugins = /* @__PURE__ */ new Map();
1526
+ hooks = /* @__PURE__ */ new Map();
1527
+ register(plugin) {
1528
+ if (this.plugins.has(plugin.name)) {
1529
+ throw new Error(`Plugin "${plugin.name}" is already registered`);
1530
+ }
1531
+ this.plugins.set(plugin.name, plugin);
1532
+ const pluginHooks = plugin.getHooks?.() || {};
1533
+ for (const [event, handlers] of Object.entries(pluginHooks)) {
1534
+ if (Array.isArray(handlers)) {
1535
+ for (const handler of handlers) {
1536
+ this.registerHook(event, handler);
1537
+ }
1538
+ }
1539
+ }
1540
+ console.log(`[PluginManager] Registered plugin: ${plugin.name}`);
1541
+ }
1542
+ unregister(name) {
1543
+ const plugin = this.plugins.get(name);
1544
+ if (!plugin) return;
1545
+ const pluginHooks = plugin.getHooks?.() || {};
1546
+ for (const [event, handlers] of Object.entries(pluginHooks)) {
1547
+ if (Array.isArray(handlers)) {
1548
+ for (const handler of handlers) {
1549
+ this.unregisterHook(event, handler);
1550
+ }
1551
+ }
1552
+ }
1553
+ this.plugins.delete(name);
1554
+ console.log(`[PluginManager] Unregistered plugin: ${name}`);
1555
+ }
1556
+ get(name) {
1557
+ return this.plugins.get(name);
1558
+ }
1559
+ getAll() {
1560
+ return Array.from(this.plugins.values());
1561
+ }
1562
+ has(name) {
1563
+ return this.plugins.has(name);
1564
+ }
1565
+ registerHook(event, handler) {
1566
+ if (!this.hooks.has(event)) {
1567
+ this.hooks.set(event, []);
1568
+ }
1569
+ this.hooks.get(event).push(handler);
1570
+ }
1571
+ unregisterHook(event, handler) {
1572
+ const handlers = this.hooks.get(event);
1573
+ if (handlers) {
1574
+ const index = handlers.indexOf(handler);
1575
+ if (index > -1) {
1576
+ handlers.splice(index, 1);
1577
+ }
1578
+ }
1579
+ }
1580
+ async executeHook(event, args) {
1581
+ const handlers = this.hooks.get(event) || [];
1582
+ let result = args;
1583
+ for (const handler of handlers) {
1584
+ try {
1585
+ const hookResult = await handler({ ...args, data: result });
1586
+ if (hookResult !== void 0) {
1587
+ result = hookResult;
1588
+ }
1589
+ } catch (error) {
1590
+ console.error(`[PluginManager] Error in hook "${event}":`, error);
1591
+ }
1592
+ }
1593
+ return result;
1594
+ }
1595
+ // ========================================================================
1596
+ // Collection/Field Extensions
1597
+ // ========================================================================
1598
+ getAllCollections() {
1599
+ const collections = [];
1600
+ for (const plugin of this.plugins.values()) {
1601
+ const pluginCollections = plugin.getCollections?.() || [];
1602
+ collections.push(...pluginCollections);
1603
+ }
1604
+ return collections;
1605
+ }
1606
+ getAllGlobals() {
1607
+ const globals = [];
1608
+ for (const plugin of this.plugins.values()) {
1609
+ const pluginGlobals = plugin.getGlobals?.() || [];
1610
+ globals.push(...pluginGlobals);
1611
+ }
1612
+ return globals;
1613
+ }
1614
+ getAllFields() {
1615
+ const fields = [];
1616
+ for (const plugin of this.plugins.values()) {
1617
+ fields.push(...plugin.fields);
1618
+ }
1619
+ return fields;
1620
+ }
1621
+ getAdminComponents() {
1622
+ const components = {};
1623
+ for (const plugin of this.plugins.values()) {
1624
+ Object.assign(components, plugin.adminComponents);
1625
+ }
1626
+ return components;
1627
+ }
1628
+ getAdminStyles() {
1629
+ const styles = [];
1630
+ for (const plugin of this.plugins.values()) {
1631
+ styles.push(...plugin.adminStyles);
1632
+ }
1633
+ return styles;
1634
+ }
1635
+ };
1636
+ var SEOPLugin = class extends KyroPlugin {
1637
+ constructor() {
1638
+ super("seo");
1639
+ this.description = "Advanced SEO features including sitemaps, robots.txt, and structured data";
1640
+ this.collections.push({
1641
+ slug: "seo-settings",
1642
+ label: "SEO Settings",
1643
+ fields: [
1644
+ { name: "sitemap", type: "checkbox", label: "Enable Sitemap", defaultValue: true },
1645
+ { name: "robotsTxt", type: "textarea", label: "robots.txt Content" },
1646
+ { name: "canonicalUrl", type: "text", variant: "url", label: "Canonical URL" },
1647
+ { name: "ogImage", type: "text", label: "Default OG Image URL" }
1648
+ ]
1649
+ });
1650
+ }
1651
+ };
1652
+ var AnalyticsPlugin = class extends KyroPlugin {
1653
+ constructor() {
1654
+ super("analytics");
1655
+ this.description = "Analytics integration for tracking page views and events";
1656
+ this.collections.push({
1657
+ slug: "analytics-events",
1658
+ label: "Analytics Events",
1659
+ fields: [
1660
+ { name: "name", type: "text", required: true },
1661
+ { name: "properties", type: "json", label: "Event Properties" },
1662
+ { name: "timestamp", type: "date", required: true },
1663
+ { name: "userId", type: "text", label: "User ID" },
1664
+ { name: "sessionId", type: "text", label: "Session ID" }
1665
+ ]
1666
+ });
1667
+ this.adminComponents["AnalyticsDashboard"] = {};
1668
+ }
1669
+ };
1670
+ var CommentsPlugin = class extends KyroPlugin {
1671
+ constructor() {
1672
+ super("comments");
1673
+ this.description = "Commenting system for products and posts";
1674
+ this.collections.push({
1675
+ slug: "comments",
1676
+ label: "Comments",
1677
+ fields: [
1678
+ { name: "content", type: "textarea", required: true },
1679
+ { name: "author", type: "text", required: true },
1680
+ { name: "email", type: "email" },
1681
+ { name: "approved", type: "checkbox", defaultValue: false },
1682
+ { name: "parent", type: "text", label: "Parent Comment ID" },
1683
+ { name: "resourceType", type: "text", required: true },
1684
+ { name: "resourceId", type: "text", required: true }
1685
+ ]
1686
+ });
1687
+ this.adminComponents["CommentModeration"] = {};
1688
+ }
1689
+ };
1690
+ var ReviewsPlugin = class extends KyroPlugin {
1691
+ constructor() {
1692
+ super("reviews");
1693
+ this.description = "Product reviews and ratings";
1694
+ this.collections.push({
1695
+ slug: "reviews",
1696
+ label: "Reviews",
1697
+ fields: [
1698
+ { name: "rating", type: "number", required: true, min: 1, max: 5 },
1699
+ { name: "title", type: "text" },
1700
+ { name: "content", type: "textarea", required: true },
1701
+ { name: "author", type: "relationship", relationTo: "customers" },
1702
+ { name: "product", type: "relationship", relationTo: "products", required: true },
1703
+ { name: "approved", type: "checkbox", defaultValue: false },
1704
+ { name: "verified", type: "checkbox", label: "Verified Purchase" },
1705
+ { name: "helpful", type: "number", label: "Helpful Count", defaultValue: 0 }
1706
+ ]
1707
+ });
1708
+ this.adminComponents["ReviewModeration"] = {};
1709
+ }
1710
+ };
1711
+ var WishlistPlugin = class extends KyroPlugin {
1712
+ constructor() {
1713
+ super("wishlist");
1714
+ this.description = "Customer wishlists";
1715
+ this.collections.push({
1716
+ slug: "wishlists",
1717
+ label: "Wishlists",
1718
+ fields: [
1719
+ { name: "customer", type: "relationship", relationTo: "customers", required: true },
1720
+ { name: "name", type: "text", label: "Wishlist Name", defaultValue: "My Wishlist" },
1721
+ { name: "items", type: "blocks", label: "Items", blocks: [
1722
+ {
1723
+ slug: "wishlist-item",
1724
+ label: "Item",
1725
+ fields: [
1726
+ { name: "product", type: "relationship", relationTo: "products" },
1727
+ { name: "quantity", type: "number", defaultValue: 1 },
1728
+ { name: "addedAt", type: "date" },
1729
+ { name: "priority", type: "select", options: [
1730
+ { label: "Low", value: "low" },
1731
+ { label: "Medium", value: "medium" },
1732
+ { label: "High", value: "high" }
1733
+ ] }
1734
+ ]
1735
+ }
1736
+ ] }
1737
+ ]
1738
+ });
1739
+ }
1740
+ };
1741
+ var presetPlugins = {
1742
+ SEO: SEOPLugin,
1743
+ Analytics: AnalyticsPlugin,
1744
+ Comments: CommentsPlugin,
1745
+ Reviews: ReviewsPlugin,
1746
+ Wishlist: WishlistPlugin
1747
+ };
1748
+
1749
+ // src/styling/index.ts
1750
+ var CSSGenerator = class {
1751
+ constructor(config) {
1752
+ this.config = config;
1753
+ }
1754
+ config;
1755
+ css = [];
1756
+ addRule(selector, properties) {
1757
+ const props = Object.entries(properties).map(([k, v]) => ` ${k}: ${v};`).join("\n");
1758
+ this.css.push(`${selector} {
1759
+ ${props}
1760
+ }`);
1761
+ return this;
1762
+ }
1763
+ addMediaQuery(breakpoint, rules) {
1764
+ this.css.push(`@media (min-width: ${breakpoint}) {
1765
+ ${rules.join("\n ")}
1766
+ }`);
1767
+ return this;
1768
+ }
1769
+ generate() {
1770
+ return this.css.join("\n\n");
1771
+ }
1772
+ };
1773
+ function generateTailwindConfig(theme) {
1774
+ return {
1775
+ theme: {
1776
+ extend: {
1777
+ colors: theme.colors || {},
1778
+ fontFamily: theme.fonts || {},
1779
+ spacing: theme.spacing || {},
1780
+ borderRadius: theme.borderRadius || {},
1781
+ boxShadow: theme.shadows || {},
1782
+ screens: theme.breakpoints || {}
1783
+ }
1784
+ }
1785
+ };
1786
+ }
1787
+ var defaultLightTheme = {
1788
+ colors: {
1789
+ primary: "#3b82f6",
1790
+ secondary: "#6366f1",
1791
+ accent: "#ec4899",
1792
+ background: "#ffffff",
1793
+ surface: "#f9fafb",
1794
+ text: "#111827",
1795
+ textMuted: "#6b7280",
1796
+ border: "#e5e7eb",
1797
+ error: "#ef4444",
1798
+ warning: "#f59e0b",
1799
+ success: "#10b981",
1800
+ info: "#3b82f6"
1801
+ },
1802
+ fonts: {
1803
+ sans: "system-ui, -apple-system, sans-serif",
1804
+ serif: "Georgia, serif",
1805
+ mono: "Menlo, monospace"
1806
+ },
1807
+ spacing: {
1808
+ xs: "0.25rem",
1809
+ sm: "0.5rem",
1810
+ md: "1rem",
1811
+ lg: "1.5rem",
1812
+ xl: "2rem",
1813
+ "2xl": "3rem",
1814
+ "3xl": "4rem"
1815
+ },
1816
+ borderRadius: {
1817
+ sm: "0.125rem",
1818
+ md: "0.375rem",
1819
+ lg: "0.5rem",
1820
+ xl: "0.75rem",
1821
+ full: "9999px"
1822
+ },
1823
+ shadows: {
1824
+ sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
1825
+ md: "0 4px 6px -1px rgb(0 0 0 / 0.1)",
1826
+ lg: "0 10px 15px -3px rgb(0 0 0 / 0.1)",
1827
+ xl: "0 20px 25px -5px rgb(0 0 0 / 0.1)"
1828
+ }
1829
+ };
1830
+ var defaultDarkTheme = {
1831
+ colors: {
1832
+ primary: "#60a5fa",
1833
+ secondary: "#818cf8",
1834
+ accent: "#f472b6",
1835
+ background: "#111827",
1836
+ surface: "#1f2937",
1837
+ text: "#f9fafb",
1838
+ textMuted: "#9ca3af",
1839
+ border: "#374151",
1840
+ error: "#f87171",
1841
+ warning: "#fbbf24",
1842
+ success: "#34d399",
1843
+ info: "#60a5fa"
1844
+ },
1845
+ fonts: defaultLightTheme.fonts,
1846
+ spacing: defaultLightTheme.spacing,
1847
+ borderRadius: defaultLightTheme.borderRadius,
1848
+ shadows: {
1849
+ sm: "0 1px 2px 0 rgb(0 0 0 / 0.3)",
1850
+ md: "0 4px 6px -1px rgb(0 0 0 / 0.4)",
1851
+ lg: "0 10px 15px -3px rgb(0 0 0 / 0.5)",
1852
+ xl: "0 20px 25px -5px rgb(0 0 0 / 0.6)"
1853
+ }
1854
+ };
1855
+ var ecommerce2026Theme = {
1856
+ colors: {
1857
+ primary: "#FF6B35",
1858
+ secondary: "#1A1A2E",
1859
+ accent: "#16C79A",
1860
+ background: "#FFFFFF",
1861
+ surface: "#F8F9FA",
1862
+ text: "#1A1A2E",
1863
+ textMuted: "#6B7280",
1864
+ border: "#E5E7EB",
1865
+ error: "#EF4444",
1866
+ warning: "#F59E0B",
1867
+ success: "#16C79A",
1868
+ info: "#3B82F6"
1869
+ },
1870
+ fonts: {
1871
+ sans: '"Inter", "Satoshi", system-ui, sans-serif',
1872
+ serif: '"Playfair Display", Georgia, serif',
1873
+ mono: '"JetBrains Mono", monospace'
1874
+ },
1875
+ spacing: {
1876
+ xs: "0.125rem",
1877
+ sm: "0.25rem",
1878
+ md: "0.5rem",
1879
+ lg: "1rem",
1880
+ xl: "1.5rem",
1881
+ "2xl": "2rem",
1882
+ "3xl": "3rem",
1883
+ "4xl": "4rem"
1884
+ },
1885
+ borderRadius: {
1886
+ sm: "0",
1887
+ md: "0",
1888
+ lg: "0",
1889
+ xl: "0",
1890
+ full: "9999px"
1891
+ },
1892
+ shadows: {
1893
+ sm: "0 1px 2px rgba(0,0,0,0.05)",
1894
+ md: "0 4px 6px rgba(0,0,0,0.07)",
1895
+ lg: "0 10px 15px rgba(0,0,0,0.1)",
1896
+ xl: "0 20px 25px rgba(0,0,0,0.15)"
1897
+ }
1898
+ };
1899
+ function generateCSSVariables(theme) {
1900
+ const variables = [];
1901
+ if (theme.colors) {
1902
+ for (const [key, value] of Object.entries(theme.colors)) {
1903
+ variables.push(` --color-${key}: ${value};`);
1904
+ }
1905
+ }
1906
+ if (theme.fonts) {
1907
+ for (const [key, value] of Object.entries(theme.fonts)) {
1908
+ variables.push(` --font-${key}: ${value};`);
1909
+ }
1910
+ }
1911
+ if (theme.spacing) {
1912
+ for (const [key, value] of Object.entries(theme.spacing)) {
1913
+ variables.push(` --spacing-${key}: ${value};`);
1914
+ }
1915
+ }
1916
+ if (theme.borderRadius) {
1917
+ for (const [key, value] of Object.entries(theme.borderRadius)) {
1918
+ variables.push(` --radius-${key}: ${value};`);
1919
+ }
1920
+ }
1921
+ if (theme.shadows) {
1922
+ for (const [key, value] of Object.entries(theme.shadows)) {
1923
+ variables.push(` --shadow-${key}: ${value};`);
1924
+ }
1925
+ }
1926
+ return `:root {
1927
+ ${variables.join("\n")}
1928
+ }`;
1929
+ }
1930
+ function createAdminStyling(config) {
1931
+ const cssVars = generateCSSVariables(config.theme || defaultLightTheme);
1932
+ const componentStyles = [];
1933
+ if (config.componentOverrides) {
1934
+ for (const [selector, styles] of Object.entries(config.componentOverrides)) {
1935
+ const props = Object.entries(styles).map(([k, v]) => ` ${k}: ${v};`).join("\n");
1936
+ componentStyles.push(`${selector} {
1937
+ ${props}
1938
+ }`);
1939
+ }
1940
+ }
1941
+ return `
1942
+ ${cssVars}
1943
+ ${config.customStyles || ""}
1944
+ ${componentStyles.join("\n")}
1945
+ `;
1946
+ }
1947
+ var defaultFieldStyling = {
1948
+ text: {
1949
+ wrapper: { marginBottom: "var(--spacing-md)" },
1950
+ label: {
1951
+ display: "block",
1952
+ marginBottom: "var(--spacing-xs)",
1953
+ fontWeight: "500",
1954
+ color: "var(--color-text)"
1955
+ },
1956
+ input: {
1957
+ width: "100%",
1958
+ padding: "var(--spacing-sm) var(--spacing-md)",
1959
+ border: "1px solid var(--color-border)",
1960
+ borderRadius: "var(--radius-md)",
1961
+ fontSize: "0.875rem"
1962
+ },
1963
+ error: {
1964
+ color: "var(--color-error)",
1965
+ fontSize: "0.75rem",
1966
+ marginTop: "var(--spacing-xs)"
1967
+ }
1968
+ },
1969
+ number: {
1970
+ wrapper: { marginBottom: "var(--spacing-md)" },
1971
+ label: { display: "block", marginBottom: "var(--spacing-xs)", fontWeight: "500" },
1972
+ input: {
1973
+ width: "100%",
1974
+ padding: "var(--spacing-sm) var(--spacing-md)",
1975
+ border: "1px solid var(--color-border)",
1976
+ borderRadius: "var(--radius-md)"
1977
+ }
1978
+ },
1979
+ checkbox: {
1980
+ wrapper: { display: "flex", alignItems: "center", gap: "var(--spacing-sm)" },
1981
+ input: { width: "1rem", height: "1rem" },
1982
+ label: { cursor: "pointer" }
1983
+ },
1984
+ select: {
1985
+ wrapper: { marginBottom: "var(--spacing-md)" },
1986
+ input: {
1987
+ width: "100%",
1988
+ padding: "var(--spacing-sm) var(--spacing-md)",
1989
+ border: "1px solid var(--color-border)",
1990
+ borderRadius: "var(--radius-md)",
1991
+ backgroundColor: "white"
1992
+ }
1993
+ }
1994
+ };
1995
+ var DEFAULT_SALT_ROUNDS = 12;
1996
+ var DEFAULT_EXPIRES_IN = "24h";
1997
+ var DEFAULT_REFRESH_EXPIRES_IN = "7d";
1998
+ var Auth = class {
1999
+ adapter;
2000
+ config;
2001
+ constructor(adapter, config) {
2002
+ this.adapter = adapter;
2003
+ this.config = {
2004
+ secret: config.secret,
2005
+ expiresIn: config.expiresIn ?? DEFAULT_EXPIRES_IN,
2006
+ refreshExpiresIn: config.refreshExpiresIn ?? DEFAULT_REFRESH_EXPIRES_IN,
2007
+ issuer: config.issuer ?? "kyro-cms",
2008
+ audience: config.audience ?? [],
2009
+ saltRounds: config.saltRounds ?? DEFAULT_SALT_ROUNDS
2010
+ };
2011
+ }
2012
+ async register(data) {
2013
+ try {
2014
+ const existing = await this.adapter.findUserByEmail(data.email);
2015
+ if (existing) {
2016
+ return { success: false, error: "Email already registered" };
2017
+ }
2018
+ const passwordHash = await this.hashPassword(data.password);
2019
+ const user = await this.adapter.createUser({
2020
+ email: data.email,
2021
+ passwordHash,
2022
+ role: data.role ?? "customer",
2023
+ tenant: data.tenant
2024
+ });
2025
+ return this.createSessionForUser(user);
2026
+ } catch (error) {
2027
+ return { success: false, error: String(error) };
2028
+ }
2029
+ }
2030
+ async login(credentials) {
2031
+ try {
2032
+ const user = await this.adapter.findUserByEmail(credentials.email);
2033
+ if (!user || !user.passwordHash) {
2034
+ return { success: false, error: "Invalid credentials" };
2035
+ }
2036
+ const valid = await this.adapter.verifyPassword(
2037
+ credentials.password,
2038
+ user.passwordHash
2039
+ );
2040
+ if (!valid) {
2041
+ return { success: false, error: "Invalid credentials" };
2042
+ }
2043
+ return this.createSessionForUser(user);
2044
+ } catch (error) {
2045
+ return { success: false, error: String(error) };
2046
+ }
2047
+ }
2048
+ async logout(token) {
2049
+ await this.adapter.deleteSession(token);
2050
+ }
2051
+ async refreshToken(refreshToken) {
2052
+ try {
2053
+ const session = await this.adapter.findSessionByToken(refreshToken);
2054
+ if (!session || session.expiresAt < /* @__PURE__ */ new Date()) {
2055
+ return { success: false, error: "Invalid or expired refresh token" };
2056
+ }
2057
+ const user = await this.adapter.findUserById(session.userId);
2058
+ if (!user) {
2059
+ return { success: false, error: "User not found" };
2060
+ }
2061
+ await this.adapter.deleteSession(refreshToken);
2062
+ return this.createSessionForUser(user);
2063
+ } catch (error) {
2064
+ return { success: false, error: String(error) };
2065
+ }
2066
+ }
2067
+ async verifyToken(token) {
2068
+ try {
2069
+ const decoded = jwt.verify(token, this.config.secret, {
2070
+ issuer: this.config.issuer,
2071
+ audience: this.config.audience.length > 0 ? this.config.audience[0] : void 0
2072
+ });
2073
+ return decoded;
2074
+ } catch {
2075
+ return null;
2076
+ }
2077
+ }
2078
+ async getUserFromToken(token) {
2079
+ const payload = await this.verifyToken(token);
2080
+ if (!payload) return null;
2081
+ return this.adapter.findUserById(payload.sub);
2082
+ }
2083
+ async changePassword(userId, currentPassword, newPassword) {
2084
+ try {
2085
+ const user = await this.adapter.findUserById(userId);
2086
+ if (!user || !user.passwordHash) {
2087
+ return { success: false, error: "User not found" };
2088
+ }
2089
+ const valid = await this.adapter.verifyPassword(
2090
+ currentPassword,
2091
+ user.passwordHash
2092
+ );
2093
+ if (!valid) {
2094
+ return { success: false, error: "Current password is incorrect" };
2095
+ }
2096
+ const newHash = await this.hashPassword(newPassword);
2097
+ await this.adapter.updateUser(userId, { passwordHash: newHash });
2098
+ await this.adapter.deleteUserSessions(userId);
2099
+ return { success: true, user };
2100
+ } catch (error) {
2101
+ return { success: false, error: String(error) };
2102
+ }
2103
+ }
2104
+ async resetPassword(email, newPassword) {
2105
+ try {
2106
+ const user = await this.adapter.findUserByEmail(email);
2107
+ if (!user) {
2108
+ return { success: false, error: "User not found" };
2109
+ }
2110
+ const passwordHash = await this.hashPassword(newPassword);
2111
+ await this.adapter.updateUser(user.id, { passwordHash });
2112
+ await this.adapter.deleteUserSessions(user.id);
2113
+ return { success: true, user };
2114
+ } catch (error) {
2115
+ return { success: false, error: String(error) };
2116
+ }
2117
+ }
2118
+ async deleteAccount(userId) {
2119
+ try {
2120
+ const user = await this.adapter.findUserById(userId);
2121
+ if (!user) {
2122
+ return { success: false, error: "User not found" };
2123
+ }
2124
+ await this.adapter.deleteUserSessions(userId);
2125
+ await this.adapter.deleteUser(userId);
2126
+ return { success: true };
2127
+ } catch (error) {
2128
+ return { success: false, error: String(error) };
2129
+ }
2130
+ }
2131
+ async createSessionForUser(user) {
2132
+ const token = this.generateToken(user);
2133
+ const refreshToken = randomUUID();
2134
+ const expiresAt = new Date(Date.now() + this.parseExpiresIn(this.config.refreshExpiresIn));
2135
+ const session = await this.adapter.createSession(user.id, refreshToken, expiresAt);
2136
+ return {
2137
+ success: true,
2138
+ user,
2139
+ session,
2140
+ token
2141
+ };
2142
+ }
2143
+ generateToken(user) {
2144
+ const payload = {
2145
+ sub: user.id,
2146
+ email: user.email,
2147
+ role: user.role,
2148
+ tenant: user.tenant
2149
+ };
2150
+ const signOptions = {
2151
+ expiresIn: this.parseExpiresIn(this.config.expiresIn) / 1e3,
2152
+ issuer: this.config.issuer
2153
+ };
2154
+ if (this.config.audience.length > 0) {
2155
+ signOptions.audience = this.config.audience[0];
2156
+ }
2157
+ return jwt.sign(payload, this.config.secret, signOptions);
2158
+ }
2159
+ async hashPassword(password) {
2160
+ return bcrypt.hash(password, this.config.saltRounds);
2161
+ }
2162
+ parseExpiresIn(value) {
2163
+ if (typeof value === "number") return value;
2164
+ const match = value.match(/^(\d+)([smhd])$/);
2165
+ if (!match) return 864e5;
2166
+ const num = parseInt(match[1], 10);
2167
+ switch (match[2]) {
2168
+ case "s":
2169
+ return num * 1e3;
2170
+ case "m":
2171
+ return num * 6e4;
2172
+ case "h":
2173
+ return num * 36e5;
2174
+ case "d":
2175
+ return num * 864e5;
2176
+ default:
2177
+ return 864e5;
2178
+ }
2179
+ }
2180
+ };
2181
+ function createAuth(adapter, config) {
2182
+ return new Auth(adapter, config);
2183
+ }
2184
+
2185
+ // src/versions/types.ts
2186
+ function getDefaultDraftPublishConfig() {
2187
+ return {
2188
+ enabled: true,
2189
+ draftsEnabled: true,
2190
+ publishEnabled: true,
2191
+ scheduleEnabled: false,
2192
+ versioningEnabled: true,
2193
+ maxVersionsPerDocument: 50,
2194
+ autoPublish: false,
2195
+ requirePublishPermission: true
2196
+ };
2197
+ }
2198
+
2199
+ // src/versions/index.ts
2200
+ var VersionManager = class {
2201
+ adapter;
2202
+ config;
2203
+ constructor(adapter, config) {
2204
+ this.adapter = adapter;
2205
+ this.config = { ...getDefaultDraftPublishConfig(), ...config };
2206
+ }
2207
+ async createVersion(options) {
2208
+ const latestVersion = await this.adapter.getLatestVersion(options.collection, options.documentId);
2209
+ const nextVersion = (latestVersion?.version ?? 0) + 1;
2210
+ const versionOptions = {
2211
+ ...options,
2212
+ version: nextVersion
2213
+ };
2214
+ const version = await this.adapter.createVersion(versionOptions);
2215
+ if (this.config.maxVersionsPerDocument > 0) {
2216
+ await this.pruneOldVersions(options.collection, options.documentId);
2217
+ }
2218
+ return version;
2219
+ }
2220
+ async publishVersion(options) {
2221
+ const version = await this.adapter.getVersion(options.collection, options.versionId);
2222
+ if (!version) {
2223
+ throw new Error("Version not found");
2224
+ }
2225
+ if (version.status === "published") {
2226
+ throw new Error("Version is already published");
2227
+ }
2228
+ await this.adapter.publishVersion(options);
2229
+ }
2230
+ async unpublishDocument(collection, documentId) {
2231
+ const versions = await this.adapter.getVersions({
2232
+ collection,
2233
+ documentId,
2234
+ limit: 1e3
2235
+ });
2236
+ for (const version of versions) {
2237
+ if (version.status === "published") {
2238
+ await this.createVersion({
2239
+ collection,
2240
+ documentId,
2241
+ data: version.data,
2242
+ status: "draft",
2243
+ createdBy: "system",
2244
+ changeDescription: "Unpublished document"
2245
+ });
2246
+ break;
2247
+ }
2248
+ }
2249
+ }
2250
+ async revertToVersion(collection, documentId, versionId, userId) {
2251
+ const targetVersion = await this.adapter.getVersion(collection, versionId);
2252
+ if (!targetVersion) {
2253
+ throw new Error("Version not found");
2254
+ }
2255
+ const newVersion = await this.adapter.revertToVersion({
2256
+ collection,
2257
+ documentId,
2258
+ versionId,
2259
+ userId
2260
+ });
2261
+ return newVersion;
2262
+ }
2263
+ async getVersionHistory(collection, documentId, limit = 20, offset = 0) {
2264
+ return this.adapter.getVersions({
2265
+ collection,
2266
+ documentId,
2267
+ limit,
2268
+ offset
2269
+ });
2270
+ }
2271
+ async compareTwoVersions(collection, documentId, versionA, versionB) {
2272
+ return this.adapter.compareVersions({
2273
+ collection,
2274
+ documentId,
2275
+ versionA,
2276
+ versionB
2277
+ });
2278
+ }
2279
+ async getLatestDraft(collection, documentId) {
2280
+ return this.adapter.getLatestVersion(collection, documentId);
2281
+ }
2282
+ async getPublishedVersion(collection, documentId) {
2283
+ return this.adapter.getPublishedVersion(collection, documentId);
2284
+ }
2285
+ async getVersion(collection, versionId) {
2286
+ return this.adapter.getVersion(collection, versionId);
2287
+ }
2288
+ async schedulePublish(collection, documentId, versionId, scheduledFor) {
2289
+ if (!this.config.scheduleEnabled) {
2290
+ throw new Error("Scheduled publishing is not enabled");
2291
+ }
2292
+ const version = await this.adapter.getVersion(collection, versionId);
2293
+ if (!version) {
2294
+ throw new Error("Version not found");
2295
+ }
2296
+ }
2297
+ async deleteVersionHistory(collection, documentId) {
2298
+ await this.adapter.deleteVersions(collection, documentId);
2299
+ }
2300
+ async pruneOldVersions(collection, documentId) {
2301
+ const versions = await this.adapter.getVersions({
2302
+ collection,
2303
+ documentId,
2304
+ limit: this.config.maxVersionsPerDocument + 100
2305
+ });
2306
+ if (versions.length <= this.config.maxVersionsPerDocument) {
2307
+ return;
2308
+ }
2309
+ versions.slice(0, this.config.maxVersionsPerDocument);
2310
+ const versionsToDelete = versions.slice(this.config.maxVersionsPerDocument);
2311
+ for (const version of versionsToDelete) {
2312
+ if (version.status !== "published") {
2313
+ await this.adapter.deleteVersions(collection, documentId);
2314
+ break;
2315
+ }
2316
+ }
2317
+ }
2318
+ };
2319
+ function createVersionManager(adapter, config) {
2320
+ return new VersionManager(adapter, config);
2321
+ }
2322
+ function isPublished(status) {
2323
+ return status === "published";
2324
+ }
2325
+ function isDraft(status) {
2326
+ return status === "draft";
2327
+ }
2328
+ function isArchived(status) {
2329
+ return status === "archived";
2330
+ }
2331
+
2332
+ export { ALL_FIELD_TYPES, AnalyticsPlugin, Auth, COMPLEX_FIELD_TYPES, CSSGenerator, CommentsPlugin, ConfigValidationError, Kyro, KyroPlugin, LAYOUT_FIELD_TYPES, LocalAdapter, PRIMITIVE_FIELD_TYPES, PluginManager, RELATIONAL_FIELD_TYPES, Registry, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createAdminStyling, createAuth, createKyro, createLocalAdapter, createRegistry, createVersionManager, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, ecommerce2026Theme, fieldToZod, generateCSSVariables, generateTailwindConfig, getDefaultDraftPublishConfig, getRegistry, globalToZod, isArchived, isArrayField, isBlocksField, isDraft, isGroupField, isLayoutField, isNumberField, isPublished, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, presetPlugins, resetRegistry, runFieldHooks, runHooks, validateCollection, validateConfig, validateFields, validateGlobal };
2333
+ //# sourceMappingURL=index.js.map
2334
+ //# sourceMappingURL=index.js.map