@beechcms/core 0.4.3 → 0.6.0-preview.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.
- package/dist/auth/hash-provider.d.ts.map +1 -1
- package/dist/auth/hash-provider.js +2 -0
- package/dist/auth/password-reset-token.repository.d.ts.map +1 -1
- package/dist/auth/password-reset-token.repository.js +2 -0
- package/dist/auth/session.repository.d.ts.map +1 -1
- package/dist/auth/session.repository.js +2 -0
- package/dist/auth/token-service.d.ts +2 -0
- package/dist/auth/token-service.d.ts.map +1 -1
- package/dist/auth/token-service.js +2 -0
- package/dist/auth/user.repository.d.ts +4 -1
- package/dist/auth/user.repository.d.ts.map +1 -1
- package/dist/auth/user.repository.js +2 -0
- package/dist/automations-grammar-words.d.ts.map +1 -1
- package/dist/automations-grammar-words.js +2 -0
- package/dist/automations.repository.interface.d.ts.map +1 -1
- package/dist/automations.repository.interface.js +2 -0
- package/dist/automations.runner.interface.d.ts.map +1 -1
- package/dist/automations.runner.interface.js +2 -0
- package/dist/automations.runner.stub.d.ts.map +1 -1
- package/dist/automations.runner.stub.js +2 -0
- package/dist/automations.types.d.ts +2 -0
- package/dist/automations.types.d.ts.map +1 -1
- package/dist/automations.types.js +2 -0
- package/dist/clock.d.ts.map +1 -1
- package/dist/clock.js +2 -0
- package/dist/content-scan.repository.d.ts.map +1 -1
- package/dist/content-scan.repository.js +2 -0
- package/dist/content.repository.d.ts +62 -0
- package/dist/content.repository.d.ts.map +1 -1
- package/dist/content.repository.js +18 -0
- package/dist/define-seed.d.ts.map +1 -1
- package/dist/define-seed.js +2 -0
- package/dist/demo-data.repository.d.ts +5 -0
- package/dist/demo-data.repository.d.ts.map +1 -0
- package/dist/demo-data.repository.js +3 -0
- package/dist/engine.d.ts +76 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +253 -8
- package/dist/file-types.d.ts +13 -0
- package/dist/file-types.d.ts.map +1 -0
- package/dist/file-types.js +85 -0
- package/dist/id-generator.d.ts +7 -0
- package/dist/id-generator.d.ts.map +1 -1
- package/dist/id-generator.js +5 -0
- package/dist/idempotency.repository.d.ts.map +1 -1
- package/dist/idempotency.repository.js +2 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/layout-permissions.d.ts +3 -0
- package/dist/layout-permissions.d.ts.map +1 -0
- package/dist/layout-permissions.js +11 -0
- package/dist/media.repository.d.ts.map +1 -1
- package/dist/media.repository.js +2 -0
- package/dist/notifications/notification-service.d.ts.map +1 -1
- package/dist/notifications/notification-service.js +2 -0
- package/dist/notifications/notification.repository.d.ts.map +1 -1
- package/dist/notifications/notification.repository.js +2 -9
- package/dist/observability/activity-log.repository.d.ts.map +1 -1
- package/dist/observability/activity-log.repository.js +2 -0
- package/dist/observability/activity-logger.d.ts +2 -2
- package/dist/observability/activity-logger.d.ts.map +1 -1
- package/dist/observability/activity-logger.js +2 -11
- package/dist/observability/analytics.repository.d.ts.map +1 -1
- package/dist/observability/analytics.repository.js +2 -0
- package/dist/policies.d.ts.map +1 -1
- package/dist/policies.js +2 -0
- package/dist/rate-limit/rate-limiter.d.ts.map +1 -1
- package/dist/rate-limit/rate-limiter.js +2 -0
- package/dist/relations.d.ts +28 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +25 -0
- package/dist/richtext-render.d.ts.map +1 -1
- package/dist/richtext-render.js +2 -0
- package/dist/richtext.d.ts.map +1 -1
- package/dist/richtext.js +2 -0
- package/dist/scheduler.interface.d.ts.map +1 -1
- package/dist/scheduler.interface.js +2 -0
- package/dist/scheduler.stub.d.ts.map +1 -1
- package/dist/scheduler.stub.js +2 -0
- package/dist/schema-mutator.d.ts +22 -0
- package/dist/schema-mutator.d.ts.map +1 -0
- package/dist/schema-mutator.js +3 -0
- package/dist/search/search.repository.d.ts.map +1 -1
- package/dist/search/search.repository.js +2 -0
- package/dist/seed-ddl.d.ts +40 -0
- package/dist/seed-ddl.d.ts.map +1 -0
- package/dist/seed-ddl.js +100 -0
- package/dist/seed-layout.d.ts +117 -0
- package/dist/seed-layout.d.ts.map +1 -0
- package/dist/seed-layout.js +169 -0
- package/dist/seed-layout.repository.d.ts +18 -0
- package/dist/seed-layout.repository.d.ts.map +1 -0
- package/dist/seed-layout.repository.js +3 -0
- package/dist/seed-registry.d.ts +23 -1
- package/dist/seed-registry.d.ts.map +1 -1
- package/dist/seed-registry.js +105 -0
- package/dist/seed-validation.d.ts +23 -0
- package/dist/seed-validation.d.ts.map +1 -0
- package/dist/seed-validation.js +156 -0
- package/dist/seed.repository.d.ts +37 -0
- package/dist/seed.repository.d.ts.map +1 -0
- package/dist/seed.repository.js +3 -0
- package/dist/seeds.d.ts.map +1 -1
- package/dist/seeds.js +2 -0
- package/dist/site-settings.repository.d.ts +16 -0
- package/dist/site-settings.repository.d.ts.map +1 -0
- package/dist/site-settings.repository.js +3 -0
- package/dist/slug-utils.d.ts.map +1 -1
- package/dist/slug-utils.js +2 -0
- package/dist/storage.d.ts +12 -38
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +2 -0
- package/dist/types.d.ts +80 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/validation.d.ts +23 -17
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +495 -393
- package/dist/webhook-validation.d.ts +3 -0
- package/dist/webhook-validation.d.ts.map +1 -0
- package/dist/webhook-validation.js +6 -0
- package/dist/widget/widget.repository.d.ts.map +1 -1
- package/dist/widget/widget.repository.js +2 -0
- package/package.json +5 -4
package/dist/engine.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
1
3
|
const BRANCH_TYPE_SQL = {
|
|
2
4
|
text: { sqlType: 'TEXT' },
|
|
3
5
|
number: { sqlType: 'REAL' },
|
|
@@ -7,6 +9,7 @@ const BRANCH_TYPE_SQL = {
|
|
|
7
9
|
richtext: { sqlType: 'TEXT' },
|
|
8
10
|
file: { sqlType: 'TEXT' }, // URL singolo o JSON array di URL
|
|
9
11
|
tags: { sqlType: 'TEXT' }, // JSON array di stringhe
|
|
12
|
+
relation: { sqlType: 'TEXT' }, // FK reference stored as TEXT (id of the target row)
|
|
10
13
|
};
|
|
11
14
|
const SYSTEM_COLUMNS = new Set(['id', 'slug', 'status', 'created_at', 'updated_at']);
|
|
12
15
|
// ---- Private helpers ----
|
|
@@ -21,7 +24,7 @@ function isValidColumn(seed, col) {
|
|
|
21
24
|
return true;
|
|
22
25
|
return seed.branches.some(b => b.alias === col);
|
|
23
26
|
}
|
|
24
|
-
function indexableSearchBranches(seed) {
|
|
27
|
+
export function indexableSearchBranches(seed) {
|
|
25
28
|
return seed.branches.filter(b => (b.type === 'text' || b.type === 'richtext') && b.policies?.search !== false);
|
|
26
29
|
}
|
|
27
30
|
function isAssetListBranch(branch) {
|
|
@@ -69,6 +72,29 @@ function normalizeAssetListValue(rawValue) {
|
|
|
69
72
|
}
|
|
70
73
|
return [...new Set(normalized)];
|
|
71
74
|
}
|
|
75
|
+
// ---- FK helpers ----
|
|
76
|
+
const DEFAULT_ON_DELETE_RULE = 'SET NULL';
|
|
77
|
+
/**
|
|
78
|
+
* Returns the trailing FK fragment for a single-value relation branch, or ''
|
|
79
|
+
* if the branch is not a relation. Throws when a single-value relation branch
|
|
80
|
+
* is missing `targetSeed` — that is a programmer error that must surface at
|
|
81
|
+
* DDL time rather than producing silently invalid SQL.
|
|
82
|
+
*
|
|
83
|
+
* Multi-relation branches (multiple: true) produce NO column on the parent
|
|
84
|
+
* table; they live in a dedicated junction table. Returns '' for them.
|
|
85
|
+
*/
|
|
86
|
+
function buildForeignKeyClause(branch) {
|
|
87
|
+
if (branch.type !== 'relation')
|
|
88
|
+
return '';
|
|
89
|
+
// Many-to-many branches: no column on parent table — see generateJunctionTable()
|
|
90
|
+
if (branch.multiple === true)
|
|
91
|
+
return '';
|
|
92
|
+
if (!branch.targetSeed) {
|
|
93
|
+
throw new Error(`Branch "${branch.alias}" is of type 'relation' but has no targetSeed`);
|
|
94
|
+
}
|
|
95
|
+
const onDeleteRule = branch.onDelete ?? DEFAULT_ON_DELETE_RULE;
|
|
96
|
+
return ` REFERENCES content_${branch.targetSeed}(id) ON DELETE ${onDeleteRule}`;
|
|
97
|
+
}
|
|
72
98
|
// ---- DDL Generators ----
|
|
73
99
|
/**
|
|
74
100
|
* Generates `CREATE TABLE IF NOT EXISTS content_{slug}` with system columns
|
|
@@ -83,12 +109,17 @@ export function generateCreateTable(seed) {
|
|
|
83
109
|
` status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'review', 'published', 'archived')),`,
|
|
84
110
|
];
|
|
85
111
|
for (const branch of seed.branches) {
|
|
112
|
+
// Many-to-many relation branches produce no column on the parent table;
|
|
113
|
+
// they are represented by a dedicated junction table. See generateJunctionTable().
|
|
114
|
+
if (branch.type === 'relation' && branch.multiple === true)
|
|
115
|
+
continue;
|
|
86
116
|
const { sqlType } = BRANCH_TYPE_SQL[branch.type];
|
|
87
117
|
let col = ` ${branch.alias} ${sqlType}`;
|
|
88
118
|
if (branch.requiredOnCreate)
|
|
89
119
|
col += ' NOT NULL';
|
|
90
120
|
if (branch.type === 'boolean')
|
|
91
121
|
col += ` CHECK (${branch.alias} IN (0, 1))`;
|
|
122
|
+
col += buildForeignKeyClause(branch);
|
|
92
123
|
lines.push(col + ',');
|
|
93
124
|
}
|
|
94
125
|
lines.push(` created_at INTEGER NOT NULL DEFAULT (unixepoch()),`);
|
|
@@ -112,6 +143,9 @@ export function generateDraftTable(seed) {
|
|
|
112
143
|
` REFERENCES ${mainTable}(id) ON DELETE CASCADE,`,
|
|
113
144
|
];
|
|
114
145
|
for (const branch of seed.branches) {
|
|
146
|
+
// Many-to-many relation branches mirror in their own junction drafts table.
|
|
147
|
+
if (branch.type === 'relation' && branch.multiple === true)
|
|
148
|
+
continue;
|
|
115
149
|
const { sqlType } = BRANCH_TYPE_SQL[branch.type];
|
|
116
150
|
let col = ` ${branch.alias} ${sqlType}`;
|
|
117
151
|
// boolean CHECK: in SQLite, NULL IN (0,1) → NULL, which passes the CHECK (only FALSE fails it)
|
|
@@ -129,7 +163,7 @@ export function generateDraftTable(seed) {
|
|
|
129
163
|
*/
|
|
130
164
|
export function generateAddColumn(seed, branch) {
|
|
131
165
|
const { sqlType } = BRANCH_TYPE_SQL[branch.type];
|
|
132
|
-
return `ALTER TABLE ${tableName(seed)} ADD COLUMN ${branch.alias} ${sqlType};`;
|
|
166
|
+
return `ALTER TABLE ${tableName(seed)} ADD COLUMN ${branch.alias} ${sqlType}${buildForeignKeyClause(branch)};`;
|
|
133
167
|
}
|
|
134
168
|
/**
|
|
135
169
|
* Generates B-tree indexes for status, created_at and every filterable Branch
|
|
@@ -143,9 +177,15 @@ export function generateIndexes(seed) {
|
|
|
143
177
|
`CREATE INDEX IF NOT EXISTS idx_${slug}_created_at ON ${table}(created_at);`,
|
|
144
178
|
];
|
|
145
179
|
for (const branch of seed.branches) {
|
|
146
|
-
|
|
180
|
+
// Multi-relation branches have no column on the parent table; their indexes
|
|
181
|
+
// live on the junction table. See generateJunctionIndexes().
|
|
182
|
+
if (branch.type === 'relation' && branch.multiple === true)
|
|
147
183
|
continue;
|
|
148
|
-
|
|
184
|
+
const isRelation = branch.type === 'relation';
|
|
185
|
+
// Relation indexes are a system concern (JOIN performance) — bypass editorial filter policy
|
|
186
|
+
if (!isRelation && branch.policies?.filter === false)
|
|
187
|
+
continue;
|
|
188
|
+
if (['text', 'number', 'date', 'boolean', 'relation'].includes(branch.type)) {
|
|
149
189
|
indexes.push(`CREATE INDEX IF NOT EXISTS idx_${slug}_${branch.alias} ON ${table}(${branch.alias});`);
|
|
150
190
|
}
|
|
151
191
|
}
|
|
@@ -212,6 +252,7 @@ export function generateFtsTriggers(seed) {
|
|
|
212
252
|
* Never uses json_extract — every column is a real column.
|
|
213
253
|
* Unknown columns in filters/orderBy are ignored (fail-closed).
|
|
214
254
|
*/
|
|
255
|
+
//TODO: buildSelectQuery ha troppe responsabilità, va suddiviso in più funzioni
|
|
215
256
|
export function buildSelectQuery(seed, options = {}) {
|
|
216
257
|
const table = tableName(seed);
|
|
217
258
|
const { filters = [], orderBy, pagination, status, search, fields } = options;
|
|
@@ -278,6 +319,7 @@ export function buildSelectQuery(seed, options = {}) {
|
|
|
278
319
|
}
|
|
279
320
|
return { sql, bindings };
|
|
280
321
|
}
|
|
322
|
+
//TODO: normalizeFilterValue ha troppe responsabilità, va suddiviso in più funzioni
|
|
281
323
|
function normalizeFilterValue(type, value) {
|
|
282
324
|
if (value === null || value === undefined)
|
|
283
325
|
return null;
|
|
@@ -286,7 +328,7 @@ function normalizeFilterValue(type, value) {
|
|
|
286
328
|
return value;
|
|
287
329
|
if (typeof value === 'string' && value.trim() !== '') {
|
|
288
330
|
const d = new Date(value);
|
|
289
|
-
return isNaN(d.getTime()) ? null : Math.floor(d.getTime() / 1000);
|
|
331
|
+
return Number.isNaN(d.getTime()) ? null : Math.floor(d.getTime() / 1000);
|
|
290
332
|
}
|
|
291
333
|
return null;
|
|
292
334
|
}
|
|
@@ -298,12 +340,13 @@ function normalizeFilterValue(type, value) {
|
|
|
298
340
|
return value;
|
|
299
341
|
if (typeof value === 'string' && value.trim() !== '') {
|
|
300
342
|
const n = Number(value);
|
|
301
|
-
return isNaN(n) ? null : n;
|
|
343
|
+
return Number.isNaN(n) ? null : n;
|
|
302
344
|
}
|
|
303
345
|
return null;
|
|
304
346
|
}
|
|
305
347
|
return value;
|
|
306
348
|
}
|
|
349
|
+
//TODO: buildFilterCondition ha troppe responsabilità, va suddiviso in più funzioni
|
|
307
350
|
function buildFilterCondition(col, type, cond, bindings) {
|
|
308
351
|
const { op, value } = cond;
|
|
309
352
|
if (op === 'is_empty') {
|
|
@@ -377,7 +420,10 @@ export function getExpectedColumns(seed) {
|
|
|
377
420
|
{ name: 'id', sqlType: 'TEXT', notNull: true, isPk: true },
|
|
378
421
|
{ name: 'slug', sqlType: 'TEXT', notNull: true, isPk: false },
|
|
379
422
|
{ name: 'status', sqlType: 'TEXT', notNull: true, isPk: false },
|
|
380
|
-
|
|
423
|
+
// Multi-relation branches produce no column on the parent table.
|
|
424
|
+
...seed.branches
|
|
425
|
+
.filter(b => !(b.type === 'relation' && b.multiple === true))
|
|
426
|
+
.map(b => ({
|
|
381
427
|
name: b.alias,
|
|
382
428
|
sqlType: BRANCH_TYPE_SQL[b.type].sqlType,
|
|
383
429
|
notNull: b.requiredOnCreate ?? false,
|
|
@@ -392,6 +438,7 @@ export function getExpectedColumns(seed) {
|
|
|
392
438
|
* Serializes a value for writing to the DB.
|
|
393
439
|
* boolean → 0/1 | date → Unix timestamp | json/asset-list → JSON string
|
|
394
440
|
*/
|
|
441
|
+
//TODO: serializeForDb ha troppe responsabilità, va suddiviso in più funzioni
|
|
395
442
|
export function serializeForDb(branch, value) {
|
|
396
443
|
if (value === null || value === undefined)
|
|
397
444
|
return null;
|
|
@@ -407,7 +454,7 @@ export function serializeForDb(branch, value) {
|
|
|
407
454
|
return value;
|
|
408
455
|
if (typeof value === 'string') {
|
|
409
456
|
const d = new Date(value);
|
|
410
|
-
if (isNaN(d.getTime()))
|
|
457
|
+
if (Number.isNaN(d.getTime()))
|
|
411
458
|
return null;
|
|
412
459
|
if (branch.format === 'date') {
|
|
413
460
|
const midnight = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
@@ -426,6 +473,204 @@ export function serializeForDb(branch, value) {
|
|
|
426
473
|
return typeof value === 'string' ? value : typeof value === 'number' ? value : null;
|
|
427
474
|
}
|
|
428
475
|
}
|
|
476
|
+
// ---- Junction table DDL (many-to-many relations, Sprint 5) ----
|
|
477
|
+
/**
|
|
478
|
+
* Returns the junction table name for a many-to-many relation branch.
|
|
479
|
+
* Format: `rel_<seedSlug>_<branchAlias>`.
|
|
480
|
+
*/
|
|
481
|
+
export function junctionTableName(seedSlug, branchAlias) {
|
|
482
|
+
return `rel_${seedSlug}_${branchAlias}`;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Generates `CREATE TABLE IF NOT EXISTS rel_<seed>_<alias>` for a multi-relation
|
|
486
|
+
* branch. The parent FK always cascades (parent lifecycle owns the edge);
|
|
487
|
+
* the target FK uses `branch.onDelete` (default CASCADE).
|
|
488
|
+
*
|
|
489
|
+
* @throws {Error} when called on a non-multi-relation branch
|
|
490
|
+
* @throws {Error} when `branch.targetSeed` is missing
|
|
491
|
+
*/
|
|
492
|
+
export function generateJunctionTable(seed, branch) {
|
|
493
|
+
if (branch.type !== 'relation' || branch.multiple !== true) {
|
|
494
|
+
throw new Error(`Branch "${branch.alias}" is not a multi-relation`);
|
|
495
|
+
}
|
|
496
|
+
if (!branch.targetSeed) {
|
|
497
|
+
throw new Error(`Branch "${branch.alias}" has no targetSeed`);
|
|
498
|
+
}
|
|
499
|
+
const onDeleteRule = branch.onDelete ?? 'CASCADE';
|
|
500
|
+
const table = junctionTableName(seed.slug, branch.alias);
|
|
501
|
+
return [
|
|
502
|
+
`CREATE TABLE IF NOT EXISTS ${table} (`,
|
|
503
|
+
` parent_id TEXT NOT NULL REFERENCES content_${seed.slug}(id) ON DELETE CASCADE,`,
|
|
504
|
+
` target_id TEXT NOT NULL REFERENCES content_${branch.targetSeed}(id) ON DELETE ${onDeleteRule},`,
|
|
505
|
+
` position INTEGER NOT NULL DEFAULT 0,`,
|
|
506
|
+
` created_at INTEGER NOT NULL DEFAULT (unixepoch()),`,
|
|
507
|
+
` PRIMARY KEY (parent_id, target_id)`,
|
|
508
|
+
`);`,
|
|
509
|
+
].join('\n');
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Generates the two B-tree indexes for a junction table:
|
|
513
|
+
* one on `parent_id` (list entries by parent) and one on `target_id`
|
|
514
|
+
* (cascade checks from target side).
|
|
515
|
+
*/
|
|
516
|
+
export function generateJunctionIndexes(seed, branch) {
|
|
517
|
+
const table = junctionTableName(seed.slug, branch.alias);
|
|
518
|
+
return [
|
|
519
|
+
`CREATE INDEX IF NOT EXISTS idx_${table}_parent ON ${table}(parent_id);`,
|
|
520
|
+
`CREATE INDEX IF NOT EXISTS idx_${table}_target ON ${table}(target_id);`,
|
|
521
|
+
];
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Generates the drafts junction table `rel_<seed>_<alias>_drafts` for a
|
|
525
|
+
* many-to-many relation branch. Note: no FK on `target_id` — targets may be
|
|
526
|
+
* deleted between draft save and publish; the FK would be wrong here.
|
|
527
|
+
* Returns null when `seed.allowDrafts` is falsy.
|
|
528
|
+
*/
|
|
529
|
+
export function generateJunctionDraftTable(seed, branch) {
|
|
530
|
+
if (!seed.allowDrafts)
|
|
531
|
+
return null;
|
|
532
|
+
if (branch.type !== 'relation' || branch.multiple !== true)
|
|
533
|
+
return null;
|
|
534
|
+
const table = `${junctionTableName(seed.slug, branch.alias)}_drafts`;
|
|
535
|
+
const mainDraftTable = `content_${seed.slug}_drafts`;
|
|
536
|
+
return [
|
|
537
|
+
`CREATE TABLE IF NOT EXISTS ${table} (`,
|
|
538
|
+
` entry_id TEXT NOT NULL REFERENCES ${mainDraftTable}(entry_id) ON DELETE CASCADE,`,
|
|
539
|
+
` target_id TEXT NOT NULL,`,
|
|
540
|
+
` position INTEGER NOT NULL DEFAULT 0,`,
|
|
541
|
+
` PRIMARY KEY (entry_id, target_id)`,
|
|
542
|
+
`);`,
|
|
543
|
+
].join('\n');
|
|
544
|
+
}
|
|
545
|
+
// ---- Destructive DDL Generators (Sprint 06 — Danger Zone) ----
|
|
546
|
+
//
|
|
547
|
+
// Every generator below emits IRREVERSIBLE, data-destroying SQL. They are pure
|
|
548
|
+
// string builders (no DB access); the actual execution goes through
|
|
549
|
+
// ISchemaMutator's dedicated destructive methods (dropTable/dropColumn/
|
|
550
|
+
// renameColumn/execDestructive), which re-validate every identifier before
|
|
551
|
+
// interpolation. These statements MUST NEVER be passed to execDdl (additive-only).
|
|
552
|
+
function draftTableName(seed) {
|
|
553
|
+
return `content_${seed.slug}_drafts`;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Returns every `DROP TABLE IF EXISTS` needed to fully remove a content type:
|
|
557
|
+
* the main `content_{slug}` table, its `fts_{slug}` virtual table, the
|
|
558
|
+
* `content_{slug}_drafts` mirror (when drafts are enabled), and each
|
|
559
|
+
* multi-relation junction table `rel_{slug}_{alias}` (+ its `_drafts` mirror).
|
|
560
|
+
*
|
|
561
|
+
* FTS triggers reference the main table; dropping the main table also drops
|
|
562
|
+
* its triggers in SQLite, but we drop the FTS table explicitly so no orphaned
|
|
563
|
+
* virtual table survives. Order: junction (children) → drafts → fts → main.
|
|
564
|
+
*/
|
|
565
|
+
export function generateDropTable(seed) {
|
|
566
|
+
const stmts = [];
|
|
567
|
+
// Junction tables first (they FK-reference the main/draft tables).
|
|
568
|
+
for (const branch of seed.branches) {
|
|
569
|
+
if (branch.type !== 'relation' || branch.multiple !== true)
|
|
570
|
+
continue;
|
|
571
|
+
const jt = junctionTableName(seed.slug, branch.alias);
|
|
572
|
+
stmts.push(`DROP TABLE IF EXISTS ${jt}_drafts;`);
|
|
573
|
+
stmts.push(`DROP TABLE IF EXISTS ${jt};`);
|
|
574
|
+
}
|
|
575
|
+
if (seed.allowDrafts) {
|
|
576
|
+
stmts.push(`DROP TABLE IF EXISTS ${draftTableName(seed)};`);
|
|
577
|
+
}
|
|
578
|
+
// FTS triggers + virtual table.
|
|
579
|
+
if (indexableSearchBranches(seed).length > 0) {
|
|
580
|
+
const slug = seed.slug;
|
|
581
|
+
stmts.push(`DROP TRIGGER IF EXISTS fts_${slug}_insert;`);
|
|
582
|
+
stmts.push(`DROP TRIGGER IF EXISTS fts_${slug}_update;`);
|
|
583
|
+
stmts.push(`DROP TRIGGER IF EXISTS fts_${slug}_delete;`);
|
|
584
|
+
stmts.push(`DROP TABLE IF EXISTS ${ftsTableName(seed)};`);
|
|
585
|
+
}
|
|
586
|
+
stmts.push(`DROP TABLE IF EXISTS ${tableName(seed)};`);
|
|
587
|
+
return stmts;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Returns the `DROP COLUMN` statements for removing a single field.
|
|
591
|
+
*
|
|
592
|
+
* - For a multi-relation branch (no column on the parent table) it drops the
|
|
593
|
+
* junction table `rel_{slug}_{alias}` (+ `_drafts`) instead.
|
|
594
|
+
* - For a normal branch it drops the column from `content_{slug}` and, when
|
|
595
|
+
* drafts are enabled, the mirror column from `content_{slug}_drafts`.
|
|
596
|
+
* - For an orphan column (no matching branch in the definition) it falls back
|
|
597
|
+
* to a plain `ALTER TABLE … DROP COLUMN`, never touching drafts.
|
|
598
|
+
*
|
|
599
|
+
* Callers must rebuild FTS separately (planFtsRebuild) when the dropped column
|
|
600
|
+
* was searchable, since SQLite cannot ALTER an fts5 table's columns.
|
|
601
|
+
*/
|
|
602
|
+
export function generateDropColumn(seed, alias) {
|
|
603
|
+
const branch = seed.branches.find(b => b.alias === alias);
|
|
604
|
+
if (branch && branch.type === 'relation' && branch.multiple === true) {
|
|
605
|
+
const jt = junctionTableName(seed.slug, alias);
|
|
606
|
+
return [
|
|
607
|
+
`DROP TABLE IF EXISTS ${jt}_drafts;`,
|
|
608
|
+
`DROP TABLE IF EXISTS ${jt};`,
|
|
609
|
+
];
|
|
610
|
+
}
|
|
611
|
+
const stmts = [`ALTER TABLE ${tableName(seed)} DROP COLUMN ${alias};`];
|
|
612
|
+
// Drop the mirror column from the drafts table when the branch lives there.
|
|
613
|
+
if (branch && seed.allowDrafts) {
|
|
614
|
+
stmts.push(`ALTER TABLE ${draftTableName(seed)} DROP COLUMN ${alias};`);
|
|
615
|
+
}
|
|
616
|
+
return stmts;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Returns the `RENAME COLUMN` statements for renaming a field's alias while
|
|
620
|
+
* keeping the stable `branch.id`. Renames the column on `content_{slug}` and,
|
|
621
|
+
* when drafts are enabled, the mirror column on `content_{slug}_drafts`.
|
|
622
|
+
*
|
|
623
|
+
* For a multi-relation branch the alias is part of the junction table name, so
|
|
624
|
+
* the junction table (+ `_drafts`) is renamed instead of a column.
|
|
625
|
+
*
|
|
626
|
+
* Callers must rebuild FTS separately (planFtsRebuild) when the renamed column
|
|
627
|
+
* was searchable.
|
|
628
|
+
*/
|
|
629
|
+
export function generateRenameColumn(seed, from, to) {
|
|
630
|
+
const branch = seed.branches.find(b => b.alias === from);
|
|
631
|
+
if (branch && branch.type === 'relation' && branch.multiple === true) {
|
|
632
|
+
const oldJt = junctionTableName(seed.slug, from);
|
|
633
|
+
const newJt = junctionTableName(seed.slug, to);
|
|
634
|
+
const stmts = [`ALTER TABLE ${oldJt} RENAME TO ${newJt};`];
|
|
635
|
+
if (seed.allowDrafts) {
|
|
636
|
+
stmts.push(`ALTER TABLE ${oldJt}_drafts RENAME TO ${newJt}_drafts;`);
|
|
637
|
+
}
|
|
638
|
+
return stmts;
|
|
639
|
+
}
|
|
640
|
+
const stmts = [`ALTER TABLE ${tableName(seed)} RENAME COLUMN ${from} TO ${to};`];
|
|
641
|
+
if (branch && seed.allowDrafts) {
|
|
642
|
+
stmts.push(`ALTER TABLE ${draftTableName(seed)} RENAME COLUMN ${from} TO ${to};`);
|
|
643
|
+
}
|
|
644
|
+
return stmts;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Returns the add/copy/drop/rename statements that change a column's SQL type
|
|
648
|
+
* in place, preserving data via `CAST`. This is the documented simpler
|
|
649
|
+
* alternative to SQLite's full 12-step table rebuild.
|
|
650
|
+
*
|
|
651
|
+
* The `branch` passed in is the *target* definition (already carrying the new
|
|
652
|
+
* `type`); its SQL type is read from BRANCH_TYPE_SQL. Multi-relation branches
|
|
653
|
+
* have no column to retype and are rejected. Mirrors the change onto the drafts
|
|
654
|
+
* table when drafts are enabled. Callers rebuild FTS separately when needed.
|
|
655
|
+
*/
|
|
656
|
+
export function generateRetypeColumn(seed, branch) {
|
|
657
|
+
if (branch.type === 'relation' && branch.multiple === true) {
|
|
658
|
+
throw new Error(`Branch "${branch.alias}" is a multi-relation and has no column to retype`);
|
|
659
|
+
}
|
|
660
|
+
const { sqlType } = BRANCH_TYPE_SQL[branch.type];
|
|
661
|
+
const alias = branch.alias;
|
|
662
|
+
const tmp = `__retype_${alias}`;
|
|
663
|
+
const rebuild = (table) => [
|
|
664
|
+
`ALTER TABLE ${table} ADD COLUMN ${tmp} ${sqlType};`,
|
|
665
|
+
`UPDATE ${table} SET ${tmp} = CAST(${alias} AS ${sqlType});`,
|
|
666
|
+
`ALTER TABLE ${table} DROP COLUMN ${alias};`,
|
|
667
|
+
`ALTER TABLE ${table} RENAME COLUMN ${tmp} TO ${alias};`,
|
|
668
|
+
];
|
|
669
|
+
const stmts = rebuild(tableName(seed));
|
|
670
|
+
if (seed.allowDrafts)
|
|
671
|
+
stmts.push(...rebuild(draftTableName(seed)));
|
|
672
|
+
return stmts;
|
|
673
|
+
}
|
|
429
674
|
/**
|
|
430
675
|
* Deserializes a value read from the DB for the API response.
|
|
431
676
|
* 0/1 → boolean | Unix timestamp → ISO 8601 | JSON string → object
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type FileAccept = 'image' | 'document' | 'any';
|
|
2
|
+
export declare const IMAGE_EXTENSIONS: Set<string>;
|
|
3
|
+
export declare const BLOCKED_IMAGE_EXTENSIONS: Set<string>;
|
|
4
|
+
export declare const DOCUMENT_EXTENSIONS: Set<string>;
|
|
5
|
+
export declare const ARCHIVE_EXTENSIONS: Set<string>;
|
|
6
|
+
export declare const IMAGE_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp", "image/avif", "image/bmp", "image/x-icon", "image/vnd.microsoft.icon"];
|
|
7
|
+
export declare const BLOCKED_IMAGE_MIME_TYPES: readonly ["image/svg+xml", "image/svg"];
|
|
8
|
+
export declare const DOCUMENT_MIME_TYPES: readonly ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "text/plain", "text/csv", "text/markdown"];
|
|
9
|
+
export declare const ARCHIVE_MIME_TYPES: readonly ["application/zip", "application/x-zip-compressed", "application/x-7z-compressed", "application/x-tar", "application/gzip", "application/json"];
|
|
10
|
+
export declare function extensionFromUrl(url: string): string | null;
|
|
11
|
+
export declare function isExtensionAccepted(ext: string | null, accept: FileAccept): boolean;
|
|
12
|
+
export declare function isMimeAccepted(mime: string, accept: FileAccept): boolean;
|
|
13
|
+
//# sourceMappingURL=file-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-types.d.ts","sourceRoot":"","sources":["../src/file-types.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,KAAK,CAAA;AAKrD,eAAO,MAAM,gBAAgB,aAAuE,CAAA;AACpG,eAAO,MAAM,wBAAwB,aAA2B,CAAA;AAChE,eAAO,MAAM,mBAAmB,aAAoF,CAAA;AACpH,eAAO,MAAM,kBAAkB,aAA8C,CAAA;AAI7E,eAAO,MAAM,gBAAgB,wIASnB,CAAA;AAGV,eAAO,MAAM,wBAAwB,yCAA0C,CAAA;AAC/E,eAAO,MAAM,mBAAmB,yXAWtB,CAAA;AACV,eAAO,MAAM,kBAAkB,0JAOrB,CAAA;AAEV,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS3D;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAMnF;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAcxE"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
// SVG is intentionally excluded: SVG can embed <script> tags and is rendered
|
|
4
|
+
// inline by browsers when served as image/svg+xml on a same-origin route,
|
|
5
|
+
// creating a stored XSS vector (see security audit finding #4).
|
|
6
|
+
export const IMAGE_EXTENSIONS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'bmp', 'ico']);
|
|
7
|
+
export const BLOCKED_IMAGE_EXTENSIONS = new Set(['svg', 'svgz']);
|
|
8
|
+
export const DOCUMENT_EXTENSIONS = new Set(['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv', 'md']);
|
|
9
|
+
export const ARCHIVE_EXTENSIONS = new Set(['zip', '7z', 'tar', 'gz', 'json']);
|
|
10
|
+
// Explicitly enumerate safe image MIME types instead of using the wildcard
|
|
11
|
+
// `image/*` prefix, which would permit `image/svg+xml` and other XSS vectors.
|
|
12
|
+
export const IMAGE_MIME_TYPES = [
|
|
13
|
+
'image/jpeg',
|
|
14
|
+
'image/png',
|
|
15
|
+
'image/gif',
|
|
16
|
+
'image/webp',
|
|
17
|
+
'image/avif',
|
|
18
|
+
'image/bmp',
|
|
19
|
+
'image/x-icon',
|
|
20
|
+
'image/vnd.microsoft.icon',
|
|
21
|
+
];
|
|
22
|
+
// Blocked image MIME types — listed explicitly so upload code can reject them
|
|
23
|
+
// with a clear error rather than a generic "file type not allowed".
|
|
24
|
+
export const BLOCKED_IMAGE_MIME_TYPES = ['image/svg+xml', 'image/svg'];
|
|
25
|
+
export const DOCUMENT_MIME_TYPES = [
|
|
26
|
+
'application/pdf',
|
|
27
|
+
'application/msword',
|
|
28
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
29
|
+
'application/vnd.ms-excel',
|
|
30
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
31
|
+
'application/vnd.ms-powerpoint',
|
|
32
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
33
|
+
'text/plain',
|
|
34
|
+
'text/csv',
|
|
35
|
+
'text/markdown',
|
|
36
|
+
];
|
|
37
|
+
export const ARCHIVE_MIME_TYPES = [
|
|
38
|
+
'application/zip',
|
|
39
|
+
'application/x-zip-compressed',
|
|
40
|
+
'application/x-7z-compressed',
|
|
41
|
+
'application/x-tar',
|
|
42
|
+
'application/gzip',
|
|
43
|
+
'application/json',
|
|
44
|
+
];
|
|
45
|
+
export function extensionFromUrl(url) {
|
|
46
|
+
try {
|
|
47
|
+
const path = new URL(url).pathname;
|
|
48
|
+
const dot = path.lastIndexOf('.');
|
|
49
|
+
if (dot < 0 || dot === path.length - 1)
|
|
50
|
+
return null;
|
|
51
|
+
return path.slice(dot + 1).toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function isExtensionAccepted(ext, accept) {
|
|
58
|
+
if (accept === 'any')
|
|
59
|
+
return true;
|
|
60
|
+
if (ext == null)
|
|
61
|
+
return false;
|
|
62
|
+
if (accept === 'image')
|
|
63
|
+
return IMAGE_EXTENSIONS.has(ext);
|
|
64
|
+
if (accept === 'document')
|
|
65
|
+
return DOCUMENT_EXTENSIONS.has(ext);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
export function isMimeAccepted(mime, accept) {
|
|
69
|
+
// Normalise: strip parameters (e.g. "image/jpeg; charset=utf-8" → "image/jpeg")
|
|
70
|
+
const normalised = mime.split(';')[0].trim().toLowerCase();
|
|
71
|
+
// Unconditionally reject SVG regardless of accept mode — SVG can carry
|
|
72
|
+
// embedded scripts and is rendered by browsers when served inline.
|
|
73
|
+
if (BLOCKED_IMAGE_MIME_TYPES.includes(normalised))
|
|
74
|
+
return false;
|
|
75
|
+
if (accept === 'any') {
|
|
76
|
+
return IMAGE_MIME_TYPES.includes(normalised)
|
|
77
|
+
|| DOCUMENT_MIME_TYPES.includes(normalised)
|
|
78
|
+
|| ARCHIVE_MIME_TYPES.includes(normalised);
|
|
79
|
+
}
|
|
80
|
+
if (accept === 'image')
|
|
81
|
+
return IMAGE_MIME_TYPES.includes(normalised);
|
|
82
|
+
if (accept === 'document')
|
|
83
|
+
return DOCUMENT_MIME_TYPES.includes(normalised);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
package/dist/id-generator.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export interface IIdGenerator {
|
|
|
10
10
|
* Test implementations return deterministic values for snapshot assertions.
|
|
11
11
|
*/
|
|
12
12
|
uuid(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Returns true when `value` has the exact shape produced by `uuid()`.
|
|
15
|
+
* The ONLY place in the codebase that knows the id format. Never inline
|
|
16
|
+
* a regex; always go through this method when validating a relation id,
|
|
17
|
+
* a route param, or any user-supplied id.
|
|
18
|
+
*/
|
|
19
|
+
isValid(value: unknown): value is string;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* Production singleton. Delegates to the host runtime CSPRNG-backed UUID
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id-generator.d.ts","sourceRoot":"","sources":["../src/id-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"id-generator.d.ts","sourceRoot":"","sources":["../src/id-generator.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,IAAI,IAAI,MAAM,CAAA;IAEd;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAA;CACzC;AAMD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAI/B,CAAA"}
|
package/dist/id-generator.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
// Internal — never exported. Callers depend on the interface, not the shape.
|
|
4
|
+
const SYSTEM_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1
5
|
/**
|
|
2
6
|
* Production singleton. Delegates to the host runtime CSPRNG-backed UUID
|
|
3
7
|
* generator. Stateless and therefore safe to share across requests.
|
|
4
8
|
*/
|
|
5
9
|
export const SystemIdGenerator = {
|
|
6
10
|
uuid: () => crypto.randomUUID(),
|
|
11
|
+
isValid: (value) => typeof value === 'string' && SYSTEM_ID_REGEX.test(value),
|
|
7
12
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.repository.d.ts","sourceRoot":"","sources":["../src/idempotency.repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"idempotency.repository.d.ts","sourceRoot":"","sources":["../src/idempotency.repository.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;IACtD,KAAK,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module @beechcms/core
|
|
9
9
|
*/
|
|
10
10
|
export * from './types.js';
|
|
11
|
+
export * from './file-types.js';
|
|
11
12
|
export * from './define-seed.js';
|
|
12
13
|
export * from './seeds.js';
|
|
13
14
|
export * from './engine.js';
|
|
@@ -41,7 +42,17 @@ export * from './automations.types.js';
|
|
|
41
42
|
export * from './automations-grammar-words.js';
|
|
42
43
|
export * from './automations.runner.interface.js';
|
|
43
44
|
export * from './automations.repository.interface.js';
|
|
44
|
-
export * from './automations.runner.stub.js';
|
|
45
45
|
export * from './scheduler.interface.js';
|
|
46
46
|
export * from './scheduler.stub.js';
|
|
47
|
+
export * from './webhook-validation.js';
|
|
48
|
+
export * from './relations.js';
|
|
49
|
+
export * from './site-settings.repository.js';
|
|
50
|
+
export * from './demo-data.repository.js';
|
|
51
|
+
export * from './seed-layout.js';
|
|
52
|
+
export * from './seed-layout.repository.js';
|
|
53
|
+
export * from './layout-permissions.js';
|
|
54
|
+
export * from './seed.repository.js';
|
|
55
|
+
export * from './seed-validation.js';
|
|
56
|
+
export * from './seed-ddl.js';
|
|
57
|
+
export * from './schema-mutator.js';
|
|
47
58
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2CAA2C,CAAA;AACzD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,oCAAoC,CAAA;AAClD,cAAc,4CAA4C,CAAA;AAC1D,cAAc,yCAAyC,CAAA;AACvD,cAAc,4CAA4C,CAAA;AAC1D,cAAc,yCAAyC,CAAA;AACvD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,mCAAmC,CAAA;AACjD,cAAc,uCAAuC,CAAA;AACrD,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
1
3
|
/**
|
|
2
4
|
* @beechcms/core - Botanical Engine
|
|
3
5
|
*
|
|
@@ -8,6 +10,7 @@
|
|
|
8
10
|
* @module @beechcms/core
|
|
9
11
|
*/
|
|
10
12
|
export * from './types.js';
|
|
13
|
+
export * from './file-types.js';
|
|
11
14
|
export * from './define-seed.js';
|
|
12
15
|
export * from './seeds.js';
|
|
13
16
|
export * from './engine.js';
|
|
@@ -41,6 +44,16 @@ export * from './automations.types.js';
|
|
|
41
44
|
export * from './automations-grammar-words.js';
|
|
42
45
|
export * from './automations.runner.interface.js';
|
|
43
46
|
export * from './automations.repository.interface.js';
|
|
44
|
-
export * from './automations.runner.stub.js';
|
|
45
47
|
export * from './scheduler.interface.js';
|
|
46
48
|
export * from './scheduler.stub.js';
|
|
49
|
+
export * from './webhook-validation.js';
|
|
50
|
+
export * from './relations.js';
|
|
51
|
+
export * from './site-settings.repository.js';
|
|
52
|
+
export * from './demo-data.repository.js';
|
|
53
|
+
export * from './seed-layout.js';
|
|
54
|
+
export * from './seed-layout.repository.js';
|
|
55
|
+
export * from './layout-permissions.js';
|
|
56
|
+
export * from './seed.repository.js';
|
|
57
|
+
export * from './seed-validation.js';
|
|
58
|
+
export * from './seed-ddl.js';
|
|
59
|
+
export * from './schema-mutator.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-permissions.d.ts","sourceRoot":"","sources":["../src/layout-permissions.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,4BAA4B,EAAE,aAAa,CAAC,MAAM,CAAa,CAAA;AAE5E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAGtE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
// NOTE: change this list to extend write-access to other roles
|
|
4
|
+
// (e.g. add 'editor', or introduce a fine-grained 'layout:edit' permission).
|
|
5
|
+
// Single source of truth — used by both API guards and dashboard buttons.
|
|
6
|
+
export const ROLES_ALLOWED_TO_EDIT_LAYOUT = ['admin'];
|
|
7
|
+
export function canEditLayout(role) {
|
|
8
|
+
if (!role)
|
|
9
|
+
return false;
|
|
10
|
+
return ROLES_ALLOWED_TO_EDIT_LAYOUT.includes(role);
|
|
11
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.repository.d.ts","sourceRoot":"","sources":["../src/media.repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media.repository.d.ts","sourceRoot":"","sources":["../src/media.repository.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAEnD;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEnG;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC;;OAEG;IACH,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACpC"}
|
package/dist/media.repository.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-service.d.ts","sourceRoot":"","sources":["../../src/notifications/notification-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notification-service.d.ts","sourceRoot":"","sources":["../../src/notifications/notification-service.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAEpE,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAC7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification.repository.d.ts","sourceRoot":"","sources":["../../src/notifications/notification.repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notification.repository.d.ts","sourceRoot":"","sources":["../../src/notifications/notification.repository.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAEvE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,gBAAgB,CAAA;IACtB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;IAElD;;;;;;OAMG;IACH,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAEnC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAExF,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C"}
|