opacacms 0.1.7 → 0.1.8

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 (46) hide show
  1. package/dist/admin/index.js +255 -20
  2. package/dist/admin/webcomponent.js +291 -46
  3. package/dist/cli/index.js +3638 -30
  4. package/dist/client.js +126 -5
  5. package/dist/db/bun-sqlite.js +790 -21
  6. package/dist/db/d1.js +788 -19
  7. package/dist/db/index.js +53 -4
  8. package/dist/db/postgres.js +792 -23
  9. package/dist/db/sqlite.js +788 -19
  10. package/dist/index.js +456 -8
  11. package/dist/runtimes/bun.js +1909 -8
  12. package/dist/runtimes/cloudflare-workers.js +1910 -9
  13. package/dist/runtimes/next.js +1908 -7
  14. package/dist/runtimes/node.js +1909 -8
  15. package/dist/schema/collection.d.ts +3 -7
  16. package/dist/schema/fields/index.d.ts +24 -25
  17. package/dist/schema/global.d.ts +9 -9
  18. package/dist/schema/index.d.ts +30 -4
  19. package/dist/schema/index.js +546 -1
  20. package/dist/server.js +2246 -17
  21. package/dist/storage/index.js +40 -1
  22. package/package.json +1 -1
  23. package/dist/chunk-16vgcf3k.js +0 -88
  24. package/dist/chunk-2yz1nsxs.js +0 -126
  25. package/dist/chunk-5gvbp2qa.js +0 -167
  26. package/dist/chunk-62ev8gnc.js +0 -41
  27. package/dist/chunk-6ew02s0c.js +0 -472
  28. package/dist/chunk-7a9kn0np.js +0 -116
  29. package/dist/chunk-8sqjbsgt.js +0 -42
  30. package/dist/chunk-9kxpbcb1.js +0 -85
  31. package/dist/chunk-cvdd4eqh.js +0 -110
  32. package/dist/chunk-d3ffeqp9.js +0 -87
  33. package/dist/chunk-fa5mg0hr.js +0 -96
  34. package/dist/chunk-j4d50hrx.js +0 -20
  35. package/dist/chunk-jwjk85ze.js +0 -15
  36. package/dist/chunk-m09hahe2.js +0 -250
  37. package/dist/chunk-s8mqwnm1.js +0 -14
  38. package/dist/chunk-srsac177.js +0 -85
  39. package/dist/chunk-v521d72w.js +0 -10
  40. package/dist/chunk-vtvqfhgy.js +0 -2442
  41. package/dist/chunk-xa7rjsn2.js +0 -20
  42. package/dist/chunk-xg35h5a3.js +0 -15
  43. package/dist/chunk-y8hc6nm4.js +0 -17
  44. package/dist/chunk-ybbbqj63.js +0 -130
  45. package/dist/chunk-yr32cp7h.js +0 -1603
  46. package/dist/chunk-zvwb67nd.js +0 -332
@@ -1,24 +1,323 @@
1
- import {
2
- buildKyselyWhere,
3
- flattenPayload,
4
- pushSchema,
5
- unflattenRow
6
- } from "../chunk-6ew02s0c.js";
7
- import {
8
- logger
9
- } from "../chunk-62ev8gnc.js";
10
- import {
11
- toSnakeCase
12
- } from "../chunk-cvdd4eqh.js";
13
- import"../chunk-ybbbqj63.js";
14
- import {
15
- BaseDatabaseAdapter
16
- } from "../chunk-s8mqwnm1.js";
17
- import {
18
- __require
19
- } from "../chunk-8sqjbsgt.js";
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toCommonJS = (from) => {
10
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
11
+ if (entry)
12
+ return entry;
13
+ entry = __defProp({}, "__esModule", { value: true });
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(entry, key))
17
+ __defProp(entry, key, {
18
+ get: __accessProp.bind(from, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ __moduleCache.set(from, entry);
23
+ return entry;
24
+ };
25
+ var __moduleCache;
26
+ var __returnValue = (v) => v;
27
+ function __exportSetter(name, newValue) {
28
+ this[name] = __returnValue.bind(null, newValue);
29
+ }
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, {
33
+ get: all[name],
34
+ enumerable: true,
35
+ configurable: true,
36
+ set: __exportSetter.bind(all, name)
37
+ });
38
+ };
39
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
40
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
41
+
42
+ // src/utils/logger.ts
43
+ var RESET = "\x1B[0m", BLUE = "\x1B[34m", GREEN = "\x1B[32m", YELLOW = "\x1B[33m", RED = "\x1B[31m", GRAY = "\x1B[90m", PREFIX, logger;
44
+ var init_logger = __esm(() => {
45
+ PREFIX = `${BLUE}[OpacaCMS]${RESET}`;
46
+ logger = {
47
+ info: (message, ...args) => {
48
+ console.log(`${PREFIX} ${message}`, ...args);
49
+ },
50
+ success: (message, ...args) => {
51
+ console.log(`${PREFIX} ${GREEN}${message}${RESET}`, ...args);
52
+ },
53
+ debug: (message, ...args) => {
54
+ console.log(`${PREFIX} ${GRAY}${message}${RESET}`, ...args);
55
+ },
56
+ warn: (message, ...args) => {
57
+ console.warn(`${PREFIX} ${YELLOW}Warning: ${message}${RESET}`, ...args);
58
+ },
59
+ error: (message, ...args) => {
60
+ console.error(`${PREFIX} ${RED}Error: ${message}${RESET}`, ...args);
61
+ },
62
+ format: (color, msg) => {
63
+ switch (color) {
64
+ case "green":
65
+ return `${GREEN}${msg}${RESET}`;
66
+ case "red":
67
+ return `${RED}${msg}${RESET}`;
68
+ case "yellow":
69
+ return `${YELLOW}${msg}${RESET}`;
70
+ case "gray":
71
+ return `${GRAY}${msg}${RESET}`;
72
+ default:
73
+ return msg;
74
+ }
75
+ }
76
+ };
77
+ });
78
+
79
+ // src/db/kysely/field-mapper.ts
80
+ var exports_field_mapper = {};
81
+ __export(exports_field_mapper, {
82
+ toSnakeCase: () => toSnakeCase,
83
+ mapFieldToSQLiteType: () => mapFieldToSQLiteType,
84
+ mapFieldToPostgresType: () => mapFieldToPostgresType,
85
+ getRelationalFields: () => getRelationalFields,
86
+ flattenFields: () => flattenFields
87
+ });
88
+ function toSnakeCase(str) {
89
+ return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
90
+ }
91
+ function mapFieldToPostgresType(field) {
92
+ switch (field.type) {
93
+ case "text":
94
+ case "richtext":
95
+ case "select":
96
+ case "radio":
97
+ case "relationship":
98
+ return "text";
99
+ case "number":
100
+ return "double precision";
101
+ case "boolean":
102
+ return "boolean";
103
+ case "date":
104
+ return "timestamp with time zone";
105
+ case "json":
106
+ case "file":
107
+ return "jsonb";
108
+ default:
109
+ return "text";
110
+ }
111
+ }
112
+ function mapFieldToSQLiteType(field) {
113
+ switch (field.type) {
114
+ case "text":
115
+ case "richtext":
116
+ case "select":
117
+ case "radio":
118
+ case "relationship":
119
+ case "date":
120
+ case "json":
121
+ case "file":
122
+ return "text";
123
+ case "number":
124
+ return "numeric";
125
+ case "boolean":
126
+ return "integer";
127
+ default:
128
+ return "text";
129
+ }
130
+ }
131
+ function flattenFields(fields, prefix = "") {
132
+ const result = [];
133
+ for (const field of fields) {
134
+ if (field.type === "join" || field.type === "virtual")
135
+ continue;
136
+ const currentName = field.name ? `${prefix}${field.name}` : undefined;
137
+ if (field.type === "group") {
138
+ if (field.fields && Array.isArray(field.fields)) {
139
+ const nextPrefix = currentName ? `${currentName}__` : "";
140
+ result.push(...flattenFields(field.fields, nextPrefix));
141
+ }
142
+ continue;
143
+ }
144
+ if (field.type === "blocks") {
145
+ continue;
146
+ }
147
+ if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
148
+ continue;
149
+ }
150
+ if (currentName) {
151
+ result.push({ ...field, name: currentName });
152
+ }
153
+ if (field.type === "row" || field.type === "collapsible") {
154
+ if (field.fields && Array.isArray(field.fields)) {
155
+ result.push(...flattenFields(field.fields, prefix));
156
+ }
157
+ }
158
+ if (field.type === "tabs" && field.tabs && Array.isArray(field.tabs)) {
159
+ for (const tab of field.tabs) {
160
+ if (tab.fields && Array.isArray(tab.fields)) {
161
+ result.push(...flattenFields(tab.fields, prefix));
162
+ }
163
+ }
164
+ }
165
+ }
166
+ return result;
167
+ }
168
+ function getRelationalFields(fields, prefix = "") {
169
+ const result = [];
170
+ for (const field of fields) {
171
+ const currentName = field.name ? `${prefix}${field.name}` : undefined;
172
+ if (field.type === "relationship" && "hasMany" in field && field.hasMany || field.type === "blocks") {
173
+ if (currentName) {
174
+ result.push({ ...field, name: currentName });
175
+ }
176
+ continue;
177
+ }
178
+ if (field.type === "group" || field.type === "row" || field.type === "collapsible") {
179
+ if (field.fields && Array.isArray(field.fields)) {
180
+ const nextPrefix = field.type === "group" && field.name ? `${currentName}__` : prefix;
181
+ result.push(...getRelationalFields(field.fields, nextPrefix));
182
+ }
183
+ continue;
184
+ }
185
+ if (field.type === "tabs" && field.tabs && Array.isArray(field.tabs)) {
186
+ for (const tab of field.tabs) {
187
+ if (tab.fields && Array.isArray(tab.fields)) {
188
+ result.push(...getRelationalFields(tab.fields, prefix));
189
+ }
190
+ }
191
+ }
192
+ }
193
+ return result;
194
+ }
195
+
196
+ // src/db/system-schema.ts
197
+ var exports_system_schema = {};
198
+ __export(exports_system_schema, {
199
+ getSystemCollections: () => getSystemCollections
200
+ });
201
+ var getSystemCollections = () => [
202
+ {
203
+ slug: "_opaca_assets",
204
+ label: "Assets",
205
+ apiPath: "assets",
206
+ fields: [
207
+ { name: "id", type: "text", required: true },
208
+ { name: "key", type: "text", required: true },
209
+ { name: "filename", type: "text", required: true },
210
+ { name: "originalFilename", type: "text", required: true },
211
+ { name: "mimeType", type: "text", required: true },
212
+ { name: "filesize", type: "number", required: true },
213
+ { name: "bucket", type: "text", required: true },
214
+ { name: "folder", type: "text" },
215
+ { name: "altText", type: "text" },
216
+ { name: "caption", type: "text" },
217
+ { name: "uploadedBy", type: "text" }
218
+ ],
219
+ timestamps: true
220
+ },
221
+ {
222
+ slug: "_users",
223
+ apiPath: "users",
224
+ fields: [
225
+ { name: "id", type: "text", required: true },
226
+ { name: "name", type: "text", required: true },
227
+ { name: "email", type: "text", required: true, unique: true },
228
+ { name: "emailVerified", type: "boolean", required: true, defaultValue: false },
229
+ { name: "image", type: "text" },
230
+ { name: "role", type: "text" },
231
+ { name: "banned", type: "boolean" },
232
+ { name: "banReason", type: "text" },
233
+ { name: "banExpires", type: "date" }
234
+ ],
235
+ timestamps: true
236
+ },
237
+ {
238
+ slug: "_sessions",
239
+ fields: [
240
+ { name: "id", type: "text", required: true },
241
+ { name: "expiresAt", type: "date", required: true },
242
+ { name: "token", type: "text", required: true, unique: true },
243
+ { name: "ipAddress", type: "text" },
244
+ { name: "userAgent", type: "text" },
245
+ {
246
+ name: "userId",
247
+ type: "text",
248
+ required: true,
249
+ references: { table: "_users", column: "id", onDelete: "cascade" }
250
+ },
251
+ { name: "impersonatedBy", type: "text" }
252
+ ],
253
+ timestamps: true,
254
+ hidden: true
255
+ },
256
+ {
257
+ slug: "_accounts",
258
+ fields: [
259
+ { name: "id", type: "text", required: true },
260
+ { name: "accountId", type: "text", required: true },
261
+ { name: "providerId", type: "text", required: true },
262
+ {
263
+ name: "userId",
264
+ type: "text",
265
+ required: true,
266
+ references: { table: "_users", column: "id", onDelete: "cascade" }
267
+ },
268
+ { name: "accessToken", type: "text" },
269
+ { name: "refreshToken", type: "text" },
270
+ { name: "idToken", type: "text" },
271
+ { name: "accessTokenExpiresAt", type: "date" },
272
+ { name: "refreshTokenExpiresAt", type: "date" },
273
+ { name: "scope", type: "text" },
274
+ { name: "password", type: "text" }
275
+ ],
276
+ timestamps: true,
277
+ hidden: true
278
+ },
279
+ {
280
+ slug: "_verifications",
281
+ fields: [
282
+ { name: "id", type: "text", required: true },
283
+ { name: "identifier", type: "text", required: true },
284
+ { name: "value", type: "text", required: true },
285
+ { name: "expiresAt", type: "date", required: true }
286
+ ],
287
+ timestamps: true,
288
+ hidden: true
289
+ },
290
+ {
291
+ slug: "_api_keys",
292
+ fields: [
293
+ { name: "id", type: "text", required: true },
294
+ { name: "configId", type: "text", required: true },
295
+ { name: "name", type: "text" },
296
+ { name: "start", type: "text" },
297
+ { name: "prefix", type: "text" },
298
+ { name: "key", type: "text", required: true },
299
+ { name: "referenceId", type: "text", required: true },
300
+ { name: "refillInterval", type: "number" },
301
+ { name: "refillAmount", type: "number" },
302
+ { name: "lastRefillAt", type: "date" },
303
+ { name: "enabled", type: "boolean", required: true },
304
+ { name: "rateLimitEnabled", type: "boolean", required: true },
305
+ { name: "rateLimitTimeWindow", type: "number" },
306
+ { name: "rateLimitMax", type: "number" },
307
+ { name: "requestCount", type: "number", required: true },
308
+ { name: "remaining", type: "number" },
309
+ { name: "lastRequest", type: "date" },
310
+ { name: "expiresAt", type: "date" },
311
+ { name: "permissions", type: "text" },
312
+ { name: "metadata", type: "text" }
313
+ ],
314
+ timestamps: { createdAt: "createdAt", updatedAt: "updatedAt" },
315
+ hidden: true
316
+ }
317
+ ];
20
318
 
21
319
  // src/db/postgres.ts
320
+ init_logger();
22
321
  import fs from "node:fs/promises";
23
322
  import path from "node:path";
24
323
  import {
@@ -29,6 +328,476 @@ import {
29
328
  PostgresDialect
30
329
  } from "kysely";
31
330
  import postgres from "postgres";
331
+
332
+ // src/db/adapter.ts
333
+ class BaseDatabaseAdapter {
334
+ get raw() {
335
+ return;
336
+ }
337
+ get db() {
338
+ return;
339
+ }
340
+ push;
341
+ pushDestructive;
342
+ migrationDir;
343
+ }
344
+
345
+ // src/db/kysely/data-mapper.ts
346
+ function toCamelCase(str) {
347
+ if (str.startsWith("_"))
348
+ return str;
349
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
350
+ }
351
+ function flattenPayload(payload, prefix = "", excludeKeys = []) {
352
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload))
353
+ return payload;
354
+ const result = {};
355
+ for (const key in payload) {
356
+ if (Object.hasOwn(payload, key)) {
357
+ const value = payload[key];
358
+ if (value === undefined)
359
+ continue;
360
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !excludeKeys.includes(key)) {
361
+ if (key.startsWith("_") && prefix === "") {
362
+ result[key] = value;
363
+ } else {
364
+ const flatNested = flattenPayload(value, `${prefix}${key}__`);
365
+ Object.assign(result, flatNested);
366
+ }
367
+ } else {
368
+ if (value === undefined)
369
+ continue;
370
+ const colName = `${prefix}${toSnakeCase(key)}`;
371
+ if (typeof value === "object" && value !== null) {
372
+ result[colName] = JSON.stringify(value);
373
+ } else {
374
+ result[colName] = value;
375
+ }
376
+ }
377
+ }
378
+ }
379
+ return result;
380
+ }
381
+ function unflattenRow(row) {
382
+ if (typeof row !== "object" || row === null || Array.isArray(row))
383
+ return row;
384
+ const result = {};
385
+ for (const key in row) {
386
+ if (Object.hasOwn(row, key)) {
387
+ let value = row[key];
388
+ if (typeof value === "string") {
389
+ const trimmed = value.trim();
390
+ if (trimmed.startsWith("[") && trimmed.endsWith("]") || trimmed.startsWith("{") && trimmed.endsWith("}")) {
391
+ try {
392
+ value = JSON.parse(value);
393
+ } catch (e) {}
394
+ }
395
+ }
396
+ if (key === "created_at" || key === "updated_at" || key === "createdAt" || key === "updatedAt") {
397
+ if (typeof value === "string" && !value.includes("T") && value.includes(" ")) {
398
+ value = `${value.replace(" ", "T")}Z`;
399
+ }
400
+ result[toCamelCase(key)] = value;
401
+ continue;
402
+ }
403
+ if (key.startsWith("_")) {
404
+ result[key] = value;
405
+ continue;
406
+ }
407
+ const parts = key.split("__");
408
+ if (parts.length === 1) {
409
+ result[toCamelCase(key)] = value;
410
+ continue;
411
+ }
412
+ let current = result;
413
+ let collision = false;
414
+ const path = [];
415
+ for (let i = 0;i < parts.length - 1; i++) {
416
+ const part = toCamelCase(parts[i]);
417
+ if (current[part] !== undefined && (typeof current[part] !== "object" || current[part] === null)) {
418
+ collision = true;
419
+ break;
420
+ }
421
+ path.push({ obj: current, key: part });
422
+ if (!current[part]) {
423
+ current[part] = {};
424
+ }
425
+ current = current[part];
426
+ }
427
+ if (!collision) {
428
+ const lastPart = toCamelCase(parts[parts.length - 1]);
429
+ if (current[lastPart] !== undefined && typeof current[lastPart] === "object") {
430
+ collision = true;
431
+ } else {
432
+ current[lastPart] = value;
433
+ }
434
+ }
435
+ if (collision) {
436
+ result[toCamelCase(key)] = value;
437
+ }
438
+ }
439
+ }
440
+ return result;
441
+ }
442
+ // src/db/kysely/query-builder.ts
443
+ function buildKyselyWhere(eb, query) {
444
+ if (!query || Object.keys(query).length === 0)
445
+ return;
446
+ const conditions = [];
447
+ for (const [key, value] of Object.entries(query)) {
448
+ if (value === undefined)
449
+ continue;
450
+ if (key === "or" && Array.isArray(value)) {
451
+ const orConditions = value.map((v) => buildKyselyWhere(eb, v)).filter((c) => c !== undefined);
452
+ if (orConditions.length > 0) {
453
+ conditions.push(eb.or(orConditions));
454
+ }
455
+ continue;
456
+ }
457
+ if (key === "and" && Array.isArray(value)) {
458
+ const andConditions = value.map((v) => buildKyselyWhere(eb, v)).filter((c) => c !== undefined);
459
+ if (andConditions.length > 0) {
460
+ conditions.push(eb.and(andConditions));
461
+ }
462
+ continue;
463
+ }
464
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
465
+ for (const [op, val] of Object.entries(value)) {
466
+ if (val === undefined)
467
+ continue;
468
+ switch (op) {
469
+ case "gt":
470
+ conditions.push(eb(key, ">", val));
471
+ break;
472
+ case "gte":
473
+ conditions.push(eb(key, ">=", val));
474
+ break;
475
+ case "lt":
476
+ conditions.push(eb(key, "<", val));
477
+ break;
478
+ case "lte":
479
+ conditions.push(eb(key, "<=", val));
480
+ break;
481
+ case "like":
482
+ conditions.push(eb(key, "like", String(val)));
483
+ break;
484
+ case "ne":
485
+ if (val === null)
486
+ conditions.push(eb(key, "is not", null));
487
+ else
488
+ conditions.push(eb(key, "!=", val));
489
+ break;
490
+ case "in":
491
+ if (Array.isArray(val))
492
+ conditions.push(eb(key, "in", val));
493
+ break;
494
+ case "is":
495
+ if (val === null)
496
+ conditions.push(eb(key, "is", null));
497
+ break;
498
+ default:
499
+ if (val === null)
500
+ conditions.push(eb(key, "is", null));
501
+ else
502
+ conditions.push(eb(key, "=", val));
503
+ break;
504
+ }
505
+ }
506
+ } else {
507
+ if (value === null) {
508
+ conditions.push(eb(key, "is", null));
509
+ } else {
510
+ conditions.push(eb(key, "=", value));
511
+ }
512
+ }
513
+ }
514
+ if (conditions.length === 0)
515
+ return;
516
+ if (conditions.length === 1)
517
+ return conditions[0];
518
+ return eb.and(conditions);
519
+ }
520
+
521
+ // src/db/kysely/schema-builder.ts
522
+ init_logger();
523
+ import { sql } from "kysely";
524
+
525
+ // src/db/kysely/sql-utils.ts
526
+ function assertSafeIdentifier(identifier) {
527
+ if (typeof identifier !== "string" || !identifier) {
528
+ throw new Error(`Invalid identifier: must be a non-empty string. Got: ${identifier}`);
529
+ }
530
+ const isValid = /^[a-zA-Z0-9_-]+$/.test(identifier);
531
+ if (!isValid) {
532
+ throw new Error(`API Error: Unsafe SQL identifier detected: "${identifier}". Identifiers can only contain alphanumeric characters, underscores, and hyphens.`);
533
+ }
534
+ }
535
+
536
+ // src/db/kysely/schema-builder.ts
537
+ async function pushSchema(db, dialect, collections, globals = [], options = {}) {
538
+ const rawSchemas = [...getSystemCollections(), ...collections, ...globals];
539
+ const allSchemas = [];
540
+ const seenSlugs = new Set;
541
+ for (const schema of rawSchemas) {
542
+ if (!seenSlugs.has(schema.slug)) {
543
+ seenSlugs.add(schema.slug);
544
+ allSchemas.push(schema);
545
+ }
546
+ }
547
+ const { pushDestructive } = options;
548
+ logger.debug(`Starting schema push via Kysely (${dialect})...`);
549
+ const mapType = dialect === "postgres" ? mapFieldToPostgresType : mapFieldToSQLiteType;
550
+ let existingTables = [];
551
+ try {
552
+ if (dialect === "postgres") {
553
+ const res = await sql`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'`.execute(db);
554
+ existingTables = res.rows.map((r) => r.table_name);
555
+ } else {
556
+ const res = await sql`SELECT name FROM sqlite_master WHERE type='table'`.execute(db);
557
+ existingTables = res.rows.map((r) => r.name);
558
+ }
559
+ } catch (_) {
560
+ logger.warn("Failed to fetch existing tables:");
561
+ }
562
+ logger.info(`Existing tables: ${existingTables.join(", ")}`);
563
+ const existingTableSet = new Set(existingTables);
564
+ const expectedTableNames = new Set;
565
+ for (const collection of allSchemas) {
566
+ const slug = collection.slug;
567
+ const tableName = toSnakeCase(slug);
568
+ assertSafeIdentifier(tableName);
569
+ expectedTableNames.add(tableName);
570
+ const flattenedFields = flattenFields(collection.fields);
571
+ const hasTimestamps = collection.timestamps !== false;
572
+ const versions = collection.versions;
573
+ if (!existingTableSet.has(tableName)) {
574
+ logger.info(` -> Creating table: ${logger.format("green", `"${tableName}"`)}`);
575
+ let builder = db.schema.createTable(tableName).ifNotExists();
576
+ builder = builder.addColumn("id", "text", (col) => col.primaryKey());
577
+ for (const field of flattenedFields) {
578
+ if (field.type === "relationship" && "hasMany" in field && field.hasMany)
579
+ continue;
580
+ const colName = toSnakeCase(field.name);
581
+ if (colName === "id")
582
+ continue;
583
+ assertSafeIdentifier(colName);
584
+ const colType = mapType(field);
585
+ builder = builder.addColumn(colName, colType, (col) => {
586
+ let c = col;
587
+ if (field.unique)
588
+ c = c.unique();
589
+ if (field.required)
590
+ c = c.notNull();
591
+ if (field.defaultValue !== undefined)
592
+ c = c.defaultTo(field.defaultValue);
593
+ return c;
594
+ });
595
+ }
596
+ if (versions?.drafts) {
597
+ builder = builder.addColumn("_status", "text", (col) => col.defaultTo("draft"));
598
+ }
599
+ if (hasTimestamps) {
600
+ const ts = collection.timestamps ?? {};
601
+ const config = typeof ts === "object" ? ts : {};
602
+ const createdField = toSnakeCase(config.createdAt ?? "createdAt");
603
+ const updatedField = toSnakeCase(config.updatedAt ?? "updatedAt");
604
+ const tsType = dialect === "postgres" ? "timestamp with time zone" : "text";
605
+ builder = builder.addColumn(createdField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
606
+ builder = builder.addColumn(updatedField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
607
+ }
608
+ await builder.execute();
609
+ if (versions) {
610
+ const versionsTable = `${slug}_versions`.toLowerCase();
611
+ assertSafeIdentifier(versionsTable);
612
+ expectedTableNames.add(versionsTable);
613
+ if (!existingTableSet.has(versionsTable)) {
614
+ logger.info(` -> Creating versions table: ${logger.format("green", `"${versionsTable}"`)}`);
615
+ await db.schema.createTable(versionsTable).addColumn("id", "text", (col) => col.primaryKey()).addColumn("_parent_id", "text", (col) => col.notNull()).addColumn("_version_data", dialect === "postgres" ? "jsonb" : "text", (col) => col.notNull()).addColumn("_status", "text").addColumn("created_at", dialect === "postgres" ? "timestamp with time zone" : "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
616
+ }
617
+ }
618
+ } else {
619
+ let existingColumns = [];
620
+ try {
621
+ if (dialect === "postgres") {
622
+ const res = await sql`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = ${slug}`.execute(db);
623
+ existingColumns = res.rows.map((r) => r.column_name);
624
+ } else {
625
+ const res = await sql`PRAGMA table_info(${sql.id(slug)})`.execute(db);
626
+ existingColumns = res.rows.map((r) => r.name);
627
+ }
628
+ } catch (e) {
629
+ logger.warn(`Failed to fetch columns for ${slug}:`, e);
630
+ }
631
+ const existingColSet = new Set(existingColumns);
632
+ for (const field of flattenedFields) {
633
+ if (field.type === "relationship" && "hasMany" in field && field.hasMany)
634
+ continue;
635
+ const colName = toSnakeCase(field.name);
636
+ if (colName === "id")
637
+ continue;
638
+ assertSafeIdentifier(colName);
639
+ if (!existingColSet.has(colName)) {
640
+ logger.info(` -> Adding missing column: ${logger.format("green", `${slug}.${colName}`)}`);
641
+ const colType = mapType(field);
642
+ await db.schema.alterTable(slug).addColumn(colName, colType).execute();
643
+ }
644
+ }
645
+ if (versions?.drafts && !existingColSet.has("_status")) {
646
+ logger.info(` -> Adding missing status column to: ${slug}`);
647
+ await db.schema.alterTable(slug).addColumn("_status", "text", (col) => col.defaultTo("draft")).execute();
648
+ }
649
+ if (hasTimestamps) {
650
+ const ts = collection.timestamps ?? {};
651
+ const config = typeof ts === "object" ? ts : {};
652
+ const createdField = toSnakeCase(config.createdAt ?? "createdAt");
653
+ const updatedField = toSnakeCase(config.updatedAt ?? "updatedAt");
654
+ const tsType = dialect === "postgres" ? "timestamp with time zone" : "text";
655
+ if (!existingColSet.has(createdField)) {
656
+ logger.info(` -> Adding missing timestamp column: ${slug}.${createdField}`);
657
+ await db.schema.alterTable(slug).addColumn(createdField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
658
+ }
659
+ if (!existingColSet.has(updatedField)) {
660
+ logger.info(` -> Adding missing timestamp column: ${slug}.${updatedField}`);
661
+ await db.schema.alterTable(slug).addColumn(updatedField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
662
+ }
663
+ }
664
+ if (versions) {
665
+ const versionsTable = `${slug}_versions`.toLowerCase();
666
+ expectedTableNames.add(versionsTable);
667
+ if (!existingTableSet.has(versionsTable)) {
668
+ logger.info(` -> Creating missing versions table for: ${slug}`);
669
+ await db.schema.createTable(versionsTable).addColumn("id", "text", (col) => col.primaryKey()).addColumn("_parent_id", "text", (col) => col.notNull()).addColumn("_version_data", dialect === "postgres" ? "jsonb" : "text", (col) => col.notNull()).addColumn("_status", "text").addColumn("created_at", dialect === "postgres" ? "timestamp with time zone" : "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
670
+ }
671
+ }
672
+ if (pushDestructive) {
673
+ const expectedCols = new Set(flattenedFields.filter((f) => !(f.type === "relationship" && f.hasMany)).map((f) => toSnakeCase(f.name)));
674
+ if (hasTimestamps) {
675
+ const ts = collection.timestamps ?? {};
676
+ const config = typeof ts === "object" ? ts : {};
677
+ expectedCols.add(toSnakeCase(config.createdAt ?? "createdAt"));
678
+ expectedCols.add(toSnakeCase(config.updatedAt ?? "updatedAt"));
679
+ }
680
+ if (versions?.drafts)
681
+ expectedCols.add("_status");
682
+ expectedCols.add("id");
683
+ for (const existingCol of existingColumns) {
684
+ if (!expectedCols.has(existingCol)) {
685
+ logger.info(` -> Dropping stale column: ${logger.format("red", `${slug}.${existingCol}`)}`);
686
+ try {
687
+ await db.schema.alterTable(slug).dropColumn(existingCol).execute();
688
+ } catch (_) {
689
+ logger.warn(`Failed to drop column ${existingCol}.`);
690
+ }
691
+ }
692
+ }
693
+ }
694
+ }
695
+ const relationalFields = getRelationalFields(collection.fields);
696
+ for (const field of relationalFields) {
697
+ if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
698
+ const sourceIdType = "text";
699
+ const colName = toSnakeCase(field.name);
700
+ const joinTableName = `${slug}_${colName}_relations`.toLowerCase();
701
+ assertSafeIdentifier(joinTableName);
702
+ expectedTableNames.add(joinTableName);
703
+ if (!existingTableSet.has(joinTableName)) {
704
+ logger.info(` -> Creating relation table: ${logger.format("green", `"${joinTableName}"`)}`);
705
+ await db.schema.createTable(joinTableName).ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("source_id", sourceIdType, (col) => col.notNull()).addColumn("target_id", "text", (col) => col.notNull()).addColumn("order", "integer", (col) => col.defaultTo(0)).execute();
706
+ }
707
+ }
708
+ }
709
+ for (const field of relationalFields) {
710
+ if (field.type === "blocks" && field.blocks && Array.isArray(field.blocks)) {
711
+ const parentIdType = "text";
712
+ for (const block of field.blocks) {
713
+ const blockName = toSnakeCase(field.name);
714
+ const blockSlug = toSnakeCase(block.slug);
715
+ assertSafeIdentifier(blockName);
716
+ assertSafeIdentifier(blockSlug);
717
+ const blockTableName = `${slug}_${blockName}_${blockSlug}`.toLowerCase();
718
+ assertSafeIdentifier(blockTableName);
719
+ expectedTableNames.add(blockTableName);
720
+ if (!existingTableSet.has(blockTableName)) {
721
+ logger.info(` -> Creating block table: ${logger.format("green", `"${blockTableName}"`)}`);
722
+ let bBuilder = db.schema.createTable(blockTableName).ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("_parent_id", parentIdType, (col) => col.notNull()).addColumn("_order", "integer", (col) => col.defaultTo(0)).addColumn("block_type", "text", (col) => col.notNull().defaultTo(block.slug));
723
+ const blockFlattened = flattenFields(block.fields);
724
+ for (const bField of blockFlattened) {
725
+ const bColName = toSnakeCase(bField.name);
726
+ if (bColName === "id")
727
+ continue;
728
+ assertSafeIdentifier(bColName);
729
+ const bColType = mapType(bField);
730
+ bBuilder = bBuilder.addColumn(bColName, bColType, (col) => {
731
+ let c = col;
732
+ if (bField.unique)
733
+ c = c.unique();
734
+ if (bField.required)
735
+ c = c.notNull();
736
+ if (bField.defaultValue !== undefined)
737
+ c = c.defaultTo(bField.defaultValue);
738
+ return c;
739
+ });
740
+ }
741
+ const tsType = dialect === "postgres" ? "timestamp with time zone" : "text";
742
+ bBuilder = bBuilder.addColumn("created_at", tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
743
+ bBuilder = bBuilder.addColumn("updated_at", tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
744
+ await bBuilder.execute();
745
+ } else {
746
+ let existingBCols = [];
747
+ try {
748
+ if (dialect === "postgres") {
749
+ const res = await sql`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = ${blockTableName}`.execute(db);
750
+ existingBCols = res.rows.map((r) => r.column_name);
751
+ } else {
752
+ const res = await sql`PRAGMA table_info(${sql.id(blockTableName)})`.execute(db);
753
+ existingBCols = res.rows.map((r) => r.name);
754
+ }
755
+ } catch (_) {}
756
+ const existingBColSet = new Set(existingBCols);
757
+ const blockFlattened = flattenFields(block.fields);
758
+ for (const bField of blockFlattened) {
759
+ const bColName = toSnakeCase(bField.name);
760
+ if (bColName === "id")
761
+ continue;
762
+ if (!existingBColSet.has(bColName)) {
763
+ const bColType = mapType(bField);
764
+ await db.schema.alterTable(blockTableName).addColumn(bColName, bColType).execute();
765
+ }
766
+ }
767
+ if (!existingBColSet.has("_parent_id")) {
768
+ await db.schema.alterTable(blockTableName).addColumn("_parent_id", "text", (col) => col.notNull().defaultTo("")).execute();
769
+ }
770
+ if (!existingBColSet.has("_order")) {
771
+ await db.schema.alterTable(blockTableName).addColumn("_order", "integer", (col) => col.defaultTo(0)).execute();
772
+ }
773
+ if (!existingBColSet.has("block_type")) {
774
+ await db.schema.alterTable(blockTableName).addColumn("block_type", "text", (col) => col.notNull().defaultTo(block.slug)).execute();
775
+ }
776
+ }
777
+ }
778
+ }
779
+ }
780
+ }
781
+ if (pushDestructive) {
782
+ for (const tableName of existingTables) {
783
+ if (!expectedTableNames.has(tableName) && !tableName.startsWith("better_auth") && !tableName.startsWith("__kysely")) {
784
+ logger.info(` -> Dropping stale table: ${logger.format("red", `"${tableName}"`)}`);
785
+ try {
786
+ await db.schema.dropTable(tableName).ifExists().cascade().execute();
787
+ } catch (_) {
788
+ try {
789
+ await db.schema.dropTable(tableName).ifExists().execute();
790
+ } catch (_2) {
791
+ logger.warn(`Failed to drop table ${tableName}`);
792
+ }
793
+ }
794
+ }
795
+ }
796
+ }
797
+ logger.success(`Schema push to Kysely (${dialect}) completed.`);
798
+ }
799
+
800
+ // src/db/postgres.ts
32
801
  class PostgresAdapter extends BaseDatabaseAdapter {
33
802
  name = "postgres";
34
803
  _rawDb = null;
@@ -185,8 +954,8 @@ class PostgresAdapter extends BaseDatabaseAdapter {
185
954
  const unflattened = unflattenRow(row);
186
955
  const colDef = this._collections.find((c) => c.slug === collection);
187
956
  if (colDef) {
188
- const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("../chunk-xg35h5a3.js");
189
- const relationalFields = getRelationalFields(colDef.fields);
957
+ const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await Promise.resolve().then(() => exports_field_mapper);
958
+ const relationalFields = getRelationalFields2(colDef.fields);
190
959
  for (const field of relationalFields) {
191
960
  if (!field.name)
192
961
  continue;
@@ -501,8 +1270,8 @@ class PostgresAdapter extends BaseDatabaseAdapter {
501
1270
  }
502
1271
  }
503
1272
  async migrate(collections, globals = []) {
504
- const { getSystemCollections } = await import("../chunk-v521d72w.js");
505
- this._collections = [...getSystemCollections(), ...collections];
1273
+ const { getSystemCollections: getSystemCollections2 } = await Promise.resolve().then(() => exports_system_schema);
1274
+ this._collections = [...getSystemCollections2(), ...collections];
506
1275
  this._globals = globals;
507
1276
  if (this.push && this._db) {
508
1277
  await pushSchema(this._db, "postgres", collections, globals, {