opacacms 0.1.21 → 0.2.1

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 (138) hide show
  1. package/README.md +792 -50
  2. package/dist/admin/auth-client.d.ts +39 -39
  3. package/dist/admin/index.js +2360 -1392
  4. package/dist/admin/react.d.ts +1 -1
  5. package/dist/admin/react.js +8 -0
  6. package/dist/admin/router.d.ts +1 -0
  7. package/dist/admin/stores/ui.d.ts +10 -0
  8. package/dist/admin/ui/admin-layout.d.ts +4 -4
  9. package/dist/admin/ui/components/DataDetailView.d.ts +1 -1
  10. package/dist/admin/ui/components/DetailSheet.d.ts +19 -0
  11. package/dist/admin/ui/components/PluginSettingsForm.d.ts +11 -0
  12. package/dist/admin/ui/components/fields/BooleanField.d.ts +2 -1
  13. package/dist/admin/ui/components/fields/DateField.d.ts +1 -1
  14. package/dist/admin/ui/components/fields/FieldLabel.d.ts +11 -0
  15. package/dist/admin/ui/components/fields/FileField.d.ts +1 -1
  16. package/dist/admin/ui/components/fields/NumberField.d.ts +1 -1
  17. package/dist/admin/ui/components/fields/RadioField.d.ts +1 -1
  18. package/dist/admin/ui/components/fields/RelationshipField.d.ts +3 -1
  19. package/dist/admin/ui/components/fields/SelectField.d.ts +1 -1
  20. package/dist/admin/ui/components/fields/TextAreaField.d.ts +1 -1
  21. package/dist/admin/ui/components/fields/TextField.d.ts +1 -1
  22. package/dist/admin/ui/components/fields/VirtualField.d.ts +1 -0
  23. package/dist/admin/ui/components/fields/index.d.ts +16 -16
  24. package/dist/admin/ui/components/fields/richtext-editor/index.d.ts +1 -1
  25. package/dist/admin/ui/components/media/AssetManagerModal.d.ts +1 -1
  26. package/dist/admin/ui/components/toast.d.ts +1 -1
  27. package/dist/admin/ui/components/ui/accordion.d.ts +1 -1
  28. package/dist/admin/ui/components/ui/button.d.ts +1 -1
  29. package/dist/admin/ui/components/ui/collapsible.d.ts +1 -1
  30. package/dist/admin/ui/components/ui/dialog.d.ts +1 -1
  31. package/dist/admin/ui/components/ui/group.d.ts +1 -1
  32. package/dist/admin/ui/components/ui/index.d.ts +17 -17
  33. package/dist/admin/ui/components/ui/input.d.ts +1 -1
  34. package/dist/admin/ui/components/ui/label.d.ts +1 -1
  35. package/dist/admin/ui/components/ui/radio-group.d.ts +1 -1
  36. package/dist/admin/ui/components/ui/relationship.d.ts +4 -4
  37. package/dist/admin/ui/components/ui/select.d.ts +1 -1
  38. package/dist/admin/ui/components/ui/sheet.d.ts +1 -1
  39. package/dist/admin/ui/components/ui/tabs.d.ts +1 -1
  40. package/dist/admin/ui/components/versions-sheet.d.ts +11 -0
  41. package/dist/admin/ui/views/media-registry-view.d.ts +1 -1
  42. package/dist/admin/ui/views/settings-view.d.ts +2 -2
  43. package/dist/admin/vue.js +8 -0
  44. package/dist/admin/webcomponent.js +20 -2
  45. package/dist/admin.css +1 -1
  46. package/dist/auth/index.d.ts +101 -41
  47. package/dist/{chunk-fqastxq9.js → chunk-06ks4ggh.js} +133 -44
  48. package/dist/{chunk-xrfhhz85.js → chunk-2es275xs.js} +480 -85
  49. package/dist/{chunk-v521d72w.js → chunk-3rdhbedb.js} +1 -1
  50. package/dist/chunk-51z3x7kq.js +20 -0
  51. package/dist/{chunk-7fyepksb.js → chunk-526a3gqx.js} +1 -1
  52. package/dist/{chunk-0sdceeys.js → chunk-6d1vdfwa.js} +121 -31
  53. package/dist/{chunk-wmvjvn7b.js → chunk-6qq3ne6b.js} +39 -1
  54. package/dist/{chunk-0am1m47g.js → chunk-6v1fw7q7.js} +5 -5
  55. package/dist/{chunk-t9v845m2.js → chunk-7y1nbmw6.js} +34 -3
  56. package/dist/chunk-8scgdznr.js +44 -0
  57. package/dist/{chunk-mycmsjd9.js → chunk-b3kr8w41.js} +57 -6
  58. package/dist/chunk-bexcv7xe.js +36 -0
  59. package/dist/{chunk-ekxkvqjm.js → chunk-bygjkgrx.js} +124 -34
  60. package/dist/{chunk-16vgcf3k.js → chunk-byq8g0rd.js} +1 -1
  61. package/dist/{chunk-cpw2y3pn.js → chunk-dykn5hr6.js} +7 -7
  62. package/dist/chunk-fj19qccp.js +78 -0
  63. package/dist/{chunk-n1xraw7j.js → chunk-g1jb60xd.js} +1 -1
  64. package/dist/{chunk-xa7rjsn2.js → chunk-j53pz21t.js} +2 -2
  65. package/dist/{chunk-nb7ctdg8.js → chunk-jdfw4v3r.js} +1 -1
  66. package/dist/chunk-mkn49zmy.js +102 -0
  67. package/dist/{chunk-59sg3pw9.js → chunk-n133qpsm.js} +128 -34
  68. package/dist/{chunk-2kyhqvhc.js → chunk-qxt9vge8.js} +1 -1
  69. package/dist/chunk-r39em4yj.js +29 -0
  70. package/dist/chunk-rqyjjqgy.js +91 -0
  71. package/dist/chunk-rsf0tpy1.js +8 -0
  72. package/dist/chunk-t0zg026p.js +71 -0
  73. package/dist/{chunk-61kwqve4.js → chunk-tfnaf41w.js} +118 -37
  74. package/dist/chunk-twpvxfce.js +64 -0
  75. package/dist/{chunk-ybbbqj63.js → chunk-v9z61v3g.js} +15 -0
  76. package/dist/{chunk-jwjk85ze.js → chunk-ywm4t2gm.js} +6 -2
  77. package/dist/cli/commands/plugin-build.d.ts +1 -0
  78. package/dist/cli/commands/plugin-init.d.ts +1 -0
  79. package/dist/cli/commands/plugin-sync.d.ts +1 -0
  80. package/dist/cli/index.js +24 -6
  81. package/dist/config-utils.d.ts +1 -1
  82. package/dist/config.d.ts +21 -4
  83. package/dist/db/adapter.d.ts +2 -2
  84. package/dist/db/better-sqlite.d.ts +2 -1
  85. package/dist/db/better-sqlite.js +5 -5
  86. package/dist/db/bun-sqlite.d.ts +2 -1
  87. package/dist/db/bun-sqlite.js +5 -5
  88. package/dist/db/d1.d.ts +1 -1
  89. package/dist/db/d1.js +5 -5
  90. package/dist/db/index.js +9 -9
  91. package/dist/db/postgres.d.ts +3 -3
  92. package/dist/db/postgres.js +5 -5
  93. package/dist/db/sqlite.d.ts +2 -1
  94. package/dist/db/sqlite.js +5 -5
  95. package/dist/index.js +4 -3
  96. package/dist/plugins/index.d.ts +1 -0
  97. package/dist/plugins/ui-bridge.d.ts +12 -0
  98. package/dist/plugins/utils.d.ts +5 -0
  99. package/dist/runtimes/bun.js +13 -7
  100. package/dist/runtimes/cloudflare-workers.js +5 -5
  101. package/dist/runtimes/next.js +5 -5
  102. package/dist/runtimes/node.js +13 -7
  103. package/dist/schema/collection.d.ts +9 -26
  104. package/dist/schema/fields/base.d.ts +3 -2
  105. package/dist/schema/fields/index.d.ts +12 -0
  106. package/dist/schema/fields/validation.test.d.ts +1 -0
  107. package/dist/schema/global.d.ts +10 -7
  108. package/dist/schema/index.js +22 -6
  109. package/dist/server/admin-router.d.ts +2 -2
  110. package/dist/server/admin.d.ts +2 -1
  111. package/dist/server/collection-router.d.ts +1 -1
  112. package/dist/server/handlers.d.ts +10 -0
  113. package/dist/server/middlewares/admin.d.ts +2 -2
  114. package/dist/server/middlewares/auth.d.ts +1 -1
  115. package/dist/server/middlewares/context.d.ts +2 -0
  116. package/dist/server/middlewares/rate-limit.d.ts +1 -1
  117. package/dist/server/openapi.d.ts +2 -0
  118. package/dist/server/plugins-loader.d.ts +6 -0
  119. package/dist/server/router.d.ts +3 -3
  120. package/dist/server/routers/admin.d.ts +2 -2
  121. package/dist/server/routers/auth.d.ts +1 -1
  122. package/dist/server/routers/collections.d.ts +1 -1
  123. package/dist/server/routers/plugins.d.ts +18 -0
  124. package/dist/server/setup-middlewares.d.ts +2 -2
  125. package/dist/server/system-router.d.ts +1 -1
  126. package/dist/server.js +11 -7
  127. package/dist/storage/adapters/local.d.ts +1 -1
  128. package/dist/storage/adapters/s3.d.ts +1 -1
  129. package/dist/storage/index.js +34 -25
  130. package/dist/types.d.ts +224 -17
  131. package/dist/utils/logger.d.ts +13 -35
  132. package/dist/validation.d.ts +40 -0
  133. package/dist/validator.d.ts +1 -1
  134. package/package.json +41 -27
  135. package/dist/admin/ui/components/DataDetailSheet.d.ts +0 -13
  136. package/dist/admin/ui/components/ui/relationship-detail-sheet.d.ts +0 -9
  137. package/dist/chunk-62ev8gnc.js +0 -41
  138. package/dist/chunk-j4d50hrx.js +0 -20
@@ -3,17 +3,18 @@ import {
3
3
  flattenPayload,
4
4
  pushSchema,
5
5
  unflattenRow
6
- } from "./chunk-cpw2y3pn.js";
6
+ } from "./chunk-dykn5hr6.js";
7
7
  import {
8
8
  BaseDatabaseAdapter
9
9
  } from "./chunk-s8mqwnm1.js";
10
- import {
11
- logger
12
- } from "./chunk-62ev8gnc.js";
13
10
  import {
14
11
  flattenFields,
12
+ getRelationalFields,
15
13
  toSnakeCase
16
- } from "./chunk-2kyhqvhc.js";
14
+ } from "./chunk-qxt9vge8.js";
15
+ import {
16
+ logger
17
+ } from "./chunk-t0zg026p.js";
17
18
  import {
18
19
  __require
19
20
  } from "./chunk-8sqjbsgt.js";
@@ -21,15 +22,12 @@ import {
21
22
  // src/db/better-sqlite.ts
22
23
  import fs from "node:fs/promises";
23
24
  import path from "node:path";
24
- import { createRequire } from "node:module";
25
25
  import { CompiledQuery, FileMigrationProvider, Kysely, Migrator, SqliteDialect } from "kysely";
26
- var require2 = createRequire(import.meta.url);
27
- var Database = require2("better-sqlite3");
28
-
29
26
  class BetterSQLiteAdapter extends BaseDatabaseAdapter {
27
+ path;
30
28
  name = "better-sqlite3";
31
29
  _rawDb;
32
- _db;
30
+ _db = null;
33
31
  _collections = [];
34
32
  _globals = [];
35
33
  push;
@@ -39,27 +37,39 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
39
37
  return this._rawDb;
40
38
  }
41
39
  get db() {
40
+ if (!this._db)
41
+ throw new Error("Database not connected. Call connect() first.");
42
42
  return this._db;
43
43
  }
44
44
  constructor(path2, options) {
45
45
  super();
46
- this._rawDb = new Database(path2);
46
+ this.path = path2;
47
+ this.push = options?.push ?? true;
48
+ this.pushDestructive = options?.pushDestructive ?? false;
49
+ this.migrationDir = options?.migrationDir ?? "./migrations";
50
+ }
51
+ async connect() {
52
+ if (this._db)
53
+ return;
54
+ const { createRequire } = await import("node:module");
55
+ const require2 = createRequire(import.meta.url);
56
+ const Database = require2("better-sqlite3");
57
+ this._rawDb = new Database(this.path);
47
58
  this._db = new Kysely({
48
59
  dialect: new SqliteDialect({
49
60
  database: this._rawDb
50
61
  })
51
62
  });
52
- this.push = options?.push ?? true;
53
- this.pushDestructive = options?.pushDestructive ?? false;
54
- this.migrationDir = options?.migrationDir ?? "./migrations";
55
63
  }
56
- async connect() {}
57
64
  async disconnect() {
58
- await this._db.destroy();
65
+ if (this._db)
66
+ await this.db.destroy();
59
67
  }
60
68
  async unsafe(query, params) {
69
+ if (!this._db)
70
+ await this.connect();
61
71
  const compiled = CompiledQuery.raw(query, params || []);
62
- const result = await this._db.executeQuery(compiled);
72
+ const result = await this.db.executeQuery(compiled);
63
73
  return result.rows;
64
74
  }
65
75
  async coerceData(collection, data) {
@@ -67,7 +77,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
67
77
  if (!colDef)
68
78
  return data;
69
79
  const result = { ...data };
70
- const { flattenFields: flattenFields2 } = await import("./chunk-7fyepksb.js");
80
+ const { flattenFields: flattenFields2 } = await import("./chunk-526a3gqx.js");
71
81
  const allFields = flattenFields2(colDef.fields);
72
82
  for (const field of allFields) {
73
83
  const colName = toSnakeCase(field.name);
@@ -103,7 +113,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
103
113
  }
104
114
  async count(collection, query) {
105
115
  const tableName = toSnakeCase(collection);
106
- let qb = this._db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
116
+ let qb = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
107
117
  if (query && Object.keys(query).length > 0) {
108
118
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
109
119
  }
@@ -112,7 +122,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
112
122
  }
113
123
  async create(collection, data) {
114
124
  const tableName = toSnakeCase(collection);
115
- return this._db.transaction().execute(async (tx) => {
125
+ return this.db.transaction().execute(async (tx) => {
116
126
  const colDef = this._collections.find((c) => c.slug === collection);
117
127
  const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
118
128
  const flatData = flattenPayload(data, "", jsonFields);
@@ -193,7 +203,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
193
203
  }
194
204
  async findOne(collection, query, tx) {
195
205
  const tableName = toSnakeCase(collection);
196
- const executor = tx || this._db;
206
+ const executor = tx || this.db;
197
207
  let qb = executor.selectFrom(tableName).selectAll();
198
208
  if (query && Object.keys(query).length > 0) {
199
209
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
@@ -204,8 +214,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
204
214
  const unflattened = unflattenRow(row);
205
215
  const colDef = this._collections.find((c) => c.slug === collection);
206
216
  if (colDef) {
207
- const { getRelationalFields, toSnakeCase: toSnakeCase2 } = await import("./chunk-7fyepksb.js");
208
- const relationalFields = getRelationalFields(colDef.fields);
217
+ const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
218
+ const relationalFields = getRelationalFields2(colDef.fields);
209
219
  for (const field of relationalFields) {
210
220
  if (!field.name)
211
221
  continue;
@@ -246,7 +256,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
246
256
  const offset = (page - 1) * limit;
247
257
  const total = await this.count(collection, query);
248
258
  const tableName = toSnakeCase(collection);
249
- let qb = this._db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
259
+ let qb = this.db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
250
260
  if (query && Object.keys(query).length > 0) {
251
261
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
252
262
  }
@@ -258,7 +268,87 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
258
268
  qb = qb.orderBy("created_at", "desc");
259
269
  }
260
270
  const rows = await qb.execute();
261
- const docs = await Promise.all(rows.map((row) => this.findOne(collection, { id: row.id })));
271
+ if (rows.length === 0) {
272
+ const totalPages2 = Math.ceil(total / limit);
273
+ return {
274
+ docs: [],
275
+ totalDocs: total,
276
+ limit,
277
+ totalPages: totalPages2,
278
+ page,
279
+ pagingCounter: offset + 1,
280
+ hasNextPage: page * limit < total,
281
+ hasPrevPage: page > 1,
282
+ prevPage: page > 1 ? page - 1 : null,
283
+ nextPage: page < totalPages2 ? page + 1 : null
284
+ };
285
+ }
286
+ const rowIds = rows.map((r) => r.id);
287
+ const docs = rows.map((r) => unflattenRow(r));
288
+ const colDef = this._collections.find((c) => c.slug === collection);
289
+ if (colDef) {
290
+ const relationalFields = getRelationalFields(colDef.fields);
291
+ for (const field of relationalFields) {
292
+ if (!field.name)
293
+ continue;
294
+ const snakeName = toSnakeCase(field.name);
295
+ if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
296
+ const joinTableName = `${toSnakeCase(collection)}_${snakeName}_relations`.toLowerCase();
297
+ try {
298
+ const allRelations = await this.db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
299
+ const relationsBySource = allRelations.reduce((acc, r) => {
300
+ if (!acc[r.source_id])
301
+ acc[r.source_id] = [];
302
+ acc[r.source_id].push(r.target_id);
303
+ return acc;
304
+ }, {});
305
+ const parts = field.name.split("__");
306
+ for (let docIdx = 0;docIdx < docs.length; docIdx++) {
307
+ const rowId = rowIds[docIdx];
308
+ let current = docs[docIdx];
309
+ for (let i = 0;i < parts.length - 1; i++) {
310
+ if (!current[parts[i]])
311
+ current[parts[i]] = {};
312
+ current = current[parts[i]];
313
+ }
314
+ current[parts[parts.length - 1]] = relationsBySource[rowId] || [];
315
+ }
316
+ } catch (e) {}
317
+ } else if (field.type === "blocks" && field.blocks) {
318
+ const blocksBySource = {};
319
+ for (const b of field.blocks) {
320
+ const blockTableName = `${collection}_${snakeName}_${toSnakeCase(b.slug)}`.toLowerCase();
321
+ try {
322
+ const allBlocks = await this.db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
323
+ for (const blk of allBlocks) {
324
+ const uf = unflattenRow(blk);
325
+ uf.blockType = blk.block_type;
326
+ if (!blocksBySource[blk._parent_id])
327
+ blocksBySource[blk._parent_id] = [];
328
+ blocksBySource[blk._parent_id].push(uf);
329
+ }
330
+ } catch (e) {}
331
+ }
332
+ const parts = field.name.split("__");
333
+ for (let docIdx = 0;docIdx < docs.length; docIdx++) {
334
+ const rowId = rowIds[docIdx];
335
+ const blockData = blocksBySource[rowId] || [];
336
+ blockData.sort((a, b) => a._order - b._order);
337
+ blockData.forEach((b) => {
338
+ delete b._order;
339
+ delete b._parentId;
340
+ });
341
+ let current = docs[docIdx];
342
+ for (let i = 0;i < parts.length - 1; i++) {
343
+ if (!current[parts[i]])
344
+ current[parts[i]] = {};
345
+ current = current[parts[i]];
346
+ }
347
+ current[parts[parts.length - 1]] = blockData;
348
+ }
349
+ }
350
+ }
351
+ }
262
352
  const totalPages = Math.ceil(total / limit);
263
353
  return {
264
354
  docs: docs.filter(Boolean),
@@ -275,7 +365,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
275
365
  }
276
366
  async update(collection, query, data) {
277
367
  const tableName = toSnakeCase(collection);
278
- return this._db.transaction().execute(async (tx) => {
368
+ return this.db.transaction().execute(async (tx) => {
279
369
  let normalizedQuery = query;
280
370
  if (typeof query !== "object" || query === null) {
281
371
  normalizedQuery = { id: query };
@@ -366,7 +456,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
366
456
  const current = await this.findOne(collection, normalizedQuery);
367
457
  if (!current)
368
458
  return false;
369
- await this._db.transaction().execute(async (tx) => {
459
+ await this.db.transaction().execute(async (tx) => {
370
460
  const colDef = this._collections.find((c) => c.slug === collection);
371
461
  if (colDef) {
372
462
  for (const field of colDef.fields) {
@@ -392,7 +482,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
392
482
  }
393
483
  async updateMany(collection, query, data) {
394
484
  const tableName = toSnakeCase(collection);
395
- return this._db.transaction().execute(async (tx) => {
485
+ return this.db.transaction().execute(async (tx) => {
396
486
  let qb = tx.updateTable(tableName);
397
487
  if (query && Object.keys(query).length > 0) {
398
488
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
@@ -421,7 +511,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
421
511
  }
422
512
  async deleteMany(collection, query) {
423
513
  const tableName = toSnakeCase(collection);
424
- return this._db.transaction().execute(async (tx) => {
514
+ return this.db.transaction().execute(async (tx) => {
425
515
  let selectQb = tx.selectFrom(tableName).select("id");
426
516
  if (query && Object.keys(query).length > 0) {
427
517
  selectQb = selectQb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
@@ -455,7 +545,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
455
545
  }
456
546
  async findGlobal(slug) {
457
547
  const tableName = toSnakeCase(slug);
458
- const row = await this._db.selectFrom(tableName).selectAll().limit(1).executeTakeFirst();
548
+ const row = await this.db.selectFrom(tableName).selectAll().limit(1).executeTakeFirst();
459
549
  return row ? unflattenRow(row) : null;
460
550
  }
461
551
  async updateGlobal(slug, data) {
@@ -469,15 +559,15 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
469
559
  if (!flatData.id)
470
560
  flatData.id = "global";
471
561
  flatData[toSnakeCase("createdAt")] = now;
472
- await this._db.insertInto(tableName).values(flatData).execute();
562
+ await this.db.insertInto(tableName).values(flatData).execute();
473
563
  } else {
474
- await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
564
+ await this.db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
475
565
  }
476
566
  return this.findGlobal(slug);
477
567
  }
478
568
  async runMigrations() {
479
569
  const migrator = new Migrator({
480
- db: this._db,
570
+ db: this.db,
481
571
  provider: new FileMigrationProvider({
482
572
  fs,
483
573
  path,
@@ -500,7 +590,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
500
590
  this._collections = collections;
501
591
  this._globals = globals;
502
592
  if (this.push) {
503
- await pushSchema(this._db, "sqlite", collections, globals, {
593
+ await pushSchema(this.db, "sqlite", collections, globals, {
504
594
  pushDestructive: this.pushDestructive
505
595
  });
506
596
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  notify
3
- } from "./chunk-j4d50hrx.js";
3
+ } from "./chunk-8scgdznr.js";
4
4
 
5
5
  // src/admin/stores/auth.ts
6
6
  import { atom, computed } from "nanostores";
@@ -1,23 +1,23 @@
1
- import {
2
- logger
3
- } from "./chunk-62ev8gnc.js";
4
1
  import {
5
2
  flattenFields,
6
3
  getRelationalFields,
7
4
  mapFieldToPostgresType,
8
5
  mapFieldToSQLiteType,
9
6
  toSnakeCase
10
- } from "./chunk-2kyhqvhc.js";
7
+ } from "./chunk-qxt9vge8.js";
11
8
  import {
12
9
  getSystemCollections,
13
10
  init_system_schema
14
- } from "./chunk-ybbbqj63.js";
11
+ } from "./chunk-v9z61v3g.js";
12
+ import {
13
+ logger
14
+ } from "./chunk-t0zg026p.js";
15
15
 
16
16
  // src/db/kysely/data-mapper.ts
17
17
  function toCamelCase(str) {
18
18
  if (str.startsWith("_"))
19
19
  return str;
20
- return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
20
+ return str.replace(/_+([a-z])/g, (_, letter) => letter.toUpperCase());
21
21
  }
22
22
  function flattenPayload(payload, prefix = "", excludeKeys = []) {
23
23
  if (typeof payload !== "object" || payload === null || Array.isArray(payload))
@@ -39,7 +39,7 @@ function flattenPayload(payload, prefix = "", excludeKeys = []) {
39
39
  if (value === undefined)
40
40
  continue;
41
41
  const colName = `${prefix}${toSnakeCase(key)}`;
42
- if (typeof value === "object" && value !== null) {
42
+ if (typeof value === "object" && value !== null && !(value instanceof Date)) {
43
43
  result[colName] = JSON.stringify(value);
44
44
  } else {
45
45
  result[colName] = value;
@@ -0,0 +1,78 @@
1
+ import {
2
+ pluginSyncCommand
3
+ } from "./chunk-twpvxfce.js";
4
+ import {
5
+ logger
6
+ } from "./chunk-t0zg026p.js";
7
+ import {
8
+ __require
9
+ } from "./chunk-8sqjbsgt.js";
10
+
11
+ // src/cli/commands/plugin-build.ts
12
+ import fs from "node:fs";
13
+ import path from "node:path";
14
+ async function pluginBuildCommand(projectRoot) {
15
+ const srcDir = path.resolve(projectRoot, "src");
16
+ const distDir = path.resolve(projectRoot, "dist");
17
+ if (!fs.existsSync(srcDir)) {
18
+ logger.error(`No 'src' directory found in ${projectRoot}.`);
19
+ process.exit(1);
20
+ }
21
+ logger.info("Syncing plugin UI...");
22
+ await pluginSyncCommand(projectRoot, false);
23
+ const entryFiles = ["index.tsx", "index.ts", "plugin.tsx", "plugin.ts"];
24
+ let mainEntry = "";
25
+ for (const file of entryFiles) {
26
+ const fullPath = path.join(srcDir, file);
27
+ if (fs.existsSync(fullPath)) {
28
+ mainEntry = fullPath;
29
+ break;
30
+ }
31
+ }
32
+ if (!mainEntry) {
33
+ logger.error(`Could not find a main entry file in ${srcDir}`);
34
+ process.exit(1);
35
+ }
36
+ logger.info(`Building plugin entry: ${path.basename(mainEntry)}...`);
37
+ if (!fs.existsSync(distDir)) {
38
+ fs.mkdirSync(distDir, { recursive: true });
39
+ }
40
+ const buildResult = await Bun.build({
41
+ entrypoints: [mainEntry],
42
+ outdir: distDir,
43
+ target: "node",
44
+ format: "esm",
45
+ external: [
46
+ "opacacms",
47
+ "opacacms/types",
48
+ "opacacms/plugins",
49
+ "opacacms/schema",
50
+ "hono",
51
+ "zod",
52
+ "react",
53
+ "react-dom"
54
+ ]
55
+ });
56
+ if (!buildResult.success) {
57
+ logger.error("Plugin build failed.");
58
+ for (const msg of buildResult.logs) {
59
+ console.error(msg);
60
+ }
61
+ process.exit(1);
62
+ }
63
+ try {
64
+ logger.info("Generating type declarations...");
65
+ const { execSync } = await import("node:child_process");
66
+ execSync("bunx tsc --emitDeclarationOnly --outDir dist", {
67
+ cwd: projectRoot,
68
+ stdio: "ignore"
69
+ });
70
+ logger.success("Types generated.");
71
+ } catch (err) {
72
+ logger.warn("Failed to generate types. Make sure typescript is installed.");
73
+ }
74
+ logger.success(`Plugin built successfully in ${distDir}!`);
75
+ }
76
+ export {
77
+ pluginBuildCommand
78
+ };
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/cli/commands/seed-command.ts
6
6
  async function seedCommand(opaca, count, reset, type) {
7
- const { autoSeed } = await import("./chunk-nb7ctdg8.js");
7
+ const { autoSeed } = await import("./chunk-jdfw4v3r.js");
8
8
  try {
9
9
  await autoSeed(opaca, count, reset, type);
10
10
  } catch (error) {
@@ -6,8 +6,8 @@ import {
6
6
  login,
7
7
  logout,
8
8
  syncSession
9
- } from "./chunk-16vgcf3k.js";
10
- import"./chunk-j4d50hrx.js";
9
+ } from "./chunk-byq8g0rd.js";
10
+ import"./chunk-8scgdznr.js";
11
11
  import"./chunk-8sqjbsgt.js";
12
12
  export {
13
13
  syncSession,
@@ -203,7 +203,7 @@ function sortCollections(collections) {
203
203
  async function autoSeed(config, countPerCollection = 10, reset = false, type = "all") {
204
204
  const { collections, db, globals, storages, serverURL } = config;
205
205
  console.log(`\uD83C\uDF31 Starting automatic seed (${countPerCollection} records per collection)...`);
206
- const { getSystemCollections } = await import("./chunk-v521d72w.js");
206
+ const { getSystemCollections } = await import("./chunk-3rdhbedb.js");
207
207
  const systemCollections = getSystemCollections().filter((c) => c.slug === "_opaca_assets");
208
208
  const allCollections = [...systemCollections, ...collections];
209
209
  let collectionsToSeed = sortCollections(allCollections);
@@ -0,0 +1,102 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-t0zg026p.js";
4
+ import"./chunk-8sqjbsgt.js";
5
+
6
+ // src/cli/commands/plugin-init.ts
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ async function pluginInitCommand(pluginName) {
10
+ if (!pluginName) {
11
+ logger.error("Please provide a plugin name. Usage: opacacms plugin:init <plugin_name>");
12
+ process.exit(1);
13
+ }
14
+ const targetDir = path.resolve(process.cwd(), pluginName);
15
+ if (fs.existsSync(targetDir)) {
16
+ logger.error(`Directory ${targetDir} already exists.`);
17
+ process.exit(1);
18
+ }
19
+ logger.info(`Initializing new plugin in ${targetDir}...`);
20
+ fs.mkdirSync(targetDir, { recursive: true });
21
+ fs.mkdirSync(path.join(targetDir, "src"), { recursive: true });
22
+ const packageJson = {
23
+ name: pluginName,
24
+ version: "1.0.0",
25
+ description: "An OpacaCMS plugin",
26
+ main: "dist/index.js",
27
+ types: "dist/index.d.ts",
28
+ scripts: {
29
+ build: "opacacms plugin:build",
30
+ dev: "bun run build --watch"
31
+ },
32
+ peerDependencies: {
33
+ opacacms: "workspace:*"
34
+ },
35
+ devDependencies: {
36
+ typescript: "^5.0.0",
37
+ "@types/node": "^20.0.0"
38
+ }
39
+ };
40
+ fs.writeFileSync(path.join(targetDir, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8");
41
+ const tsconfig = {
42
+ compilerOptions: {
43
+ target: "ESNext",
44
+ module: "ESNext",
45
+ moduleResolution: "bundler",
46
+ strict: true,
47
+ esModuleInterop: true,
48
+ skipLibCheck: true,
49
+ forceConsistentCasingInFileNames: true,
50
+ outDir: "./dist",
51
+ declaration: true,
52
+ jsx: "react"
53
+ },
54
+ include: ["src/**/*"]
55
+ };
56
+ fs.writeFileSync(path.join(targetDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2), "utf-8");
57
+ const indexTsx = `import type { OpacaPlugin, OpacaPluginContext } from 'opacacms/types';
58
+ import { definePlugin } from 'opacacms/plugins';
59
+
60
+ export const myPlugin = (): OpacaPlugin => definePlugin({
61
+ name: '${pluginName}',
62
+ label: 'My Custom Plugin',
63
+ description: 'A great new plugin for OpacaCMS.',
64
+ version: '1.0.0',
65
+ icon: 'Box',
66
+
67
+ onInit: (ctx: OpacaPluginContext) => {
68
+ ctx.logger.info('[${pluginName}] Plugin initialized!');
69
+ return ctx.config;
70
+ },
71
+
72
+ adminUI: {
73
+ component: 'my-plugin-dashboard',
74
+ // The CLI will automatically compile dashboard.tsx into this 'source' field during build.
75
+ source: '',
76
+ }
77
+ });
78
+ `;
79
+ fs.writeFileSync(path.join(targetDir, "src", "index.tsx"), indexTsx, "utf-8");
80
+ const dashboardTsx = `class MyPluginDashboard extends HTMLElement {
81
+ connectedCallback() {
82
+ this.innerHTML = \`
83
+ <div style="padding: 2rem; font-family: sans-serif;">
84
+ <h2>Welcome to ${pluginName} Dashboard!</h2>
85
+ <p>This UI component is built as a self-contained Web Component.</p>
86
+ </div>
87
+ \`;
88
+ }
89
+ }
90
+
91
+ customElements.define('my-plugin-dashboard', MyPluginDashboard);
92
+ `;
93
+ fs.writeFileSync(path.join(targetDir, "src", "dashboard.tsx"), dashboardTsx, "utf-8");
94
+ logger.success(`Plugin ${pluginName} initialized successfully!`);
95
+ logger.info(`Next steps:
96
+ cd ${pluginName}
97
+ bun install
98
+ bun run build`);
99
+ }
100
+ export {
101
+ pluginInitCommand
102
+ };