@beechcms/core 0.9.4 → 0.9.5

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 (63) hide show
  1. package/dist/content/content-lock.repository.d.ts +63 -0
  2. package/dist/content/content-lock.repository.d.ts.map +1 -0
  3. package/dist/content/content-lock.repository.js +15 -0
  4. package/dist/dashboard-layout/kanban/kanban-position.repository.d.ts +19 -0
  5. package/dist/dashboard-layout/kanban/kanban-position.repository.d.ts.map +1 -0
  6. package/dist/dashboard-layout/kanban/kanban-position.repository.js +3 -0
  7. package/dist/engine/ddl/seed-ddl-destructive.d.ts +40 -0
  8. package/dist/engine/ddl/seed-ddl-destructive.d.ts.map +1 -0
  9. package/dist/engine/ddl/seed-ddl-destructive.js +149 -0
  10. package/dist/engine/ddl.d.ts +216 -0
  11. package/dist/engine/ddl.d.ts.map +1 -0
  12. package/dist/engine/ddl.js +574 -0
  13. package/dist/engine/define-seed.d.ts +3 -0
  14. package/dist/engine/define-seed.d.ts.map +1 -0
  15. package/dist/engine/define-seed.js +5 -0
  16. package/dist/engine/introspection.d.ts +101 -0
  17. package/dist/engine/introspection.d.ts.map +1 -0
  18. package/dist/engine/introspection.js +127 -0
  19. package/dist/engine/policies.d.ts +58 -0
  20. package/dist/engine/policies.d.ts.map +1 -0
  21. package/dist/engine/policies.js +168 -0
  22. package/dist/engine/privacy.service.d.ts +65 -0
  23. package/dist/engine/privacy.service.d.ts.map +1 -0
  24. package/dist/engine/privacy.service.js +114 -0
  25. package/dist/engine/query/relations.d.ts +15 -0
  26. package/dist/engine/query/relations.d.ts.map +1 -1
  27. package/dist/engine/query.d.ts +23 -0
  28. package/dist/engine/query.d.ts.map +1 -0
  29. package/dist/engine/query.js +248 -0
  30. package/dist/engine/relations.d.ts +28 -0
  31. package/dist/engine/relations.d.ts.map +1 -0
  32. package/dist/engine/relations.js +25 -0
  33. package/dist/engine/schema-fingerprint.d.ts +71 -0
  34. package/dist/engine/schema-fingerprint.d.ts.map +1 -0
  35. package/dist/engine/schema-fingerprint.js +95 -0
  36. package/dist/engine/schema-mutator.d.ts +26 -0
  37. package/dist/engine/schema-mutator.d.ts.map +1 -0
  38. package/dist/engine/schema-mutator.js +3 -0
  39. package/dist/engine/seed-ddl-destructive.d.ts +40 -0
  40. package/dist/engine/seed-ddl-destructive.d.ts.map +1 -0
  41. package/dist/engine/seed-ddl-destructive.js +149 -0
  42. package/dist/engine/seed-ddl.d.ts +43 -0
  43. package/dist/engine/seed-ddl.d.ts.map +1 -0
  44. package/dist/engine/seed-ddl.js +115 -0
  45. package/dist/engine/seed-registry.d.ts +71 -0
  46. package/dist/engine/seed-registry.d.ts.map +1 -0
  47. package/dist/engine/seed-registry.js +167 -0
  48. package/dist/engine/seed-types-generator.d.ts +25 -0
  49. package/dist/engine/seed-types-generator.d.ts.map +1 -0
  50. package/dist/engine/seed-types-generator.js +117 -0
  51. package/dist/engine/seed-validation.d.ts +30 -0
  52. package/dist/engine/seed-validation.d.ts.map +1 -0
  53. package/dist/engine/seed-validation.js +329 -0
  54. package/dist/engine/seeds.d.ts +20 -0
  55. package/dist/engine/seeds.d.ts.map +1 -0
  56. package/dist/engine/seeds.js +21 -0
  57. package/dist/engine/serialize.d.ts +20 -0
  58. package/dist/engine/serialize.d.ts.map +1 -0
  59. package/dist/engine/serialize.js +160 -0
  60. package/dist/seed-types-generator.d.ts +10 -0
  61. package/dist/seed-types-generator.d.ts.map +1 -0
  62. package/dist/seed-types-generator.js +86 -0
  63. package/package.json +2 -2
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Checks if a branch represents a multiple asset list field.
3
+ *
4
+ * @param branch The branch definition.
5
+ * @returns True if it's an asset list, false otherwise.
6
+ */
7
+ function isAssetListBranch(branch) {
8
+ return branch.type === 'file' && (branch.multiple === true || branch.format === 'asset-list');
9
+ }
10
+ /**
11
+ * Normalizes an unknown value to a valid HTTP URL string, or null.
12
+ *
13
+ * @param value The value to normalize.
14
+ * @returns The normalized URL string, or null if invalid.
15
+ */
16
+ function normalizeHttpUrl(value) {
17
+ if (typeof value !== 'string')
18
+ return null;
19
+ const cleaned = value.trim();
20
+ if (!cleaned)
21
+ return null;
22
+ try {
23
+ const parsed = new URL(cleaned);
24
+ return parsed.protocol.startsWith('http') ? cleaned : null;
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ /**
31
+ * Safely parses a JSON string, falling back to the raw string on error.
32
+ *
33
+ * @param value The string to parse.
34
+ * @returns The parsed object, or the original string.
35
+ */
36
+ function parseJsonSafe(value) {
37
+ try {
38
+ return JSON.parse(value);
39
+ }
40
+ catch {
41
+ return value;
42
+ }
43
+ }
44
+ /**
45
+ * Normalizes raw asset list values to a flat list of valid URL strings.
46
+ *
47
+ * @param rawValue The raw value (string, array, or object) to normalize.
48
+ * @returns An array of unique normalized URL strings.
49
+ */
50
+ function normalizeAssetListValue(rawValue) {
51
+ const input = typeof rawValue === 'string' ? parseJsonSafe(rawValue) : rawValue;
52
+ const values = Array.isArray(input) ? input : [input];
53
+ const normalized = [];
54
+ for (const item of values) {
55
+ if (item == null)
56
+ continue;
57
+ const direct = normalizeHttpUrl(item);
58
+ if (direct) {
59
+ normalized.push(direct);
60
+ continue;
61
+ }
62
+ if (typeof item === 'object' && !Array.isArray(item)) {
63
+ const fromObj = normalizeHttpUrl(item.url);
64
+ if (fromObj)
65
+ normalized.push(fromObj);
66
+ }
67
+ }
68
+ return [...new Set(normalized)];
69
+ }
70
+ /**
71
+ * Serializes a value for writing to the DB.
72
+ * boolean → 0/1 | date → Unix timestamp | json/tags/richtext/repeater → JSON string
73
+ *
74
+ * @param branch The branch definition.
75
+ * @param value The value to serialize.
76
+ * @returns The serialized DB value (string, number, or null).
77
+ */
78
+ export function serializeForDb(branch, value) {
79
+ if (value === null || value === undefined)
80
+ return null;
81
+ switch (branch.type) {
82
+ case 'boolean':
83
+ return value ? 1 : 0;
84
+ case 'json':
85
+ case 'tags':
86
+ case 'richtext':
87
+ return typeof value === 'string' ? value : JSON.stringify(value);
88
+ case 'date': {
89
+ if (typeof value === 'number')
90
+ return value;
91
+ if (typeof value === 'string') {
92
+ const d = new Date(value);
93
+ if (Number.isNaN(d.getTime()))
94
+ return null;
95
+ if (branch.format === 'date') {
96
+ const midnight = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
97
+ return Math.floor(midnight / 1000);
98
+ }
99
+ return Math.floor(d.getTime() / 1000);
100
+ }
101
+ return null;
102
+ }
103
+ case 'file':
104
+ if (isAssetListBranch(branch)) {
105
+ return Array.isArray(value) ? JSON.stringify(value) : typeof value === 'string' ? value : null;
106
+ }
107
+ return typeof value === 'string' ? value : null;
108
+ case 'repeater':
109
+ return JSON.stringify(Array.isArray(value) ? value : []);
110
+ default:
111
+ return typeof value === 'string' ? value : typeof value === 'number' ? value : null;
112
+ }
113
+ }
114
+ /**
115
+ * Deserializes a value read from the DB to its API/JS representation.
116
+ * 0/1 → boolean | Unix timestamp → ISO 8601 | JSON string → object/array
117
+ *
118
+ * @param branch The branch definition.
119
+ * @param value The raw database value.
120
+ * @returns The deserialized value.
121
+ */
122
+ export function deserializeFromDb(branch, value) {
123
+ if (branch.type === 'repeater') {
124
+ if (typeof value !== 'string' || value.length === 0)
125
+ return [];
126
+ const parsed = parseJsonSafe(value);
127
+ return Array.isArray(parsed) ? parsed : [];
128
+ }
129
+ if (value === null || value === undefined)
130
+ return null;
131
+ switch (branch.type) {
132
+ case 'boolean':
133
+ return value === 1 || value === true;
134
+ case 'json':
135
+ case 'tags':
136
+ case 'richtext': {
137
+ if (typeof value === 'string') {
138
+ try {
139
+ return JSON.parse(value);
140
+ }
141
+ catch {
142
+ return value;
143
+ }
144
+ }
145
+ return value;
146
+ }
147
+ case 'date': {
148
+ if (typeof value !== 'number')
149
+ return null;
150
+ const d = new Date(value * 1000);
151
+ return branch.format === 'date' ? d.toISOString().slice(0, 10) : d.toISOString();
152
+ }
153
+ case 'file':
154
+ if (isAssetListBranch(branch))
155
+ return normalizeAssetListValue(value);
156
+ return typeof value === 'string' ? value : null;
157
+ default:
158
+ return value;
159
+ }
160
+ }
@@ -0,0 +1,10 @@
1
+ import type { Seed, Branch } from './engine/types.js';
2
+ /** slug/alias → PascalCase identifier. 'blog-posts' → 'BlogPosts'. */
3
+ export declare function pascalCase(input: string): string;
4
+ /** Maps a single Branch to its TypeScript type expression (no optional marker). */
5
+ export declare function tsTypeForBranch(branch: Branch): string;
6
+ /** Emits one exported interface for a single Seed. */
7
+ export declare function interfaceForSeed(seed: Seed): string;
8
+ /** Pure entry point. Deterministic: sorts seeds by slug for stable diffs. */
9
+ export declare function generateSeedTypes(seeds: Seed[]): string;
10
+ //# sourceMappingURL=seed-types-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-types-generator.d.ts","sourceRoot":"","sources":["../src/seed-types-generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAerD,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAMD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAoCtD;AAWD,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAYnD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CASvD"}
@@ -0,0 +1,86 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ const HEADER = '// Questo file è generato automaticamente da BeechCMS CLI. Non modificarlo direttamente.\n';
4
+ /** System columns emitted by generateCreateTable — always present, never optional. */
5
+ const SYSTEM_FIELDS = ` id: string\n` +
6
+ ` slug: string\n` +
7
+ ` status: 'draft' | 'review' | 'published' | 'archived'\n`;
8
+ const SYSTEM_TIMESTAMPS = ` created_at: number\n` +
9
+ ` updated_at: number\n`;
10
+ /** slug/alias → PascalCase identifier. 'blog-posts' → 'BlogPosts'. */
11
+ export function pascalCase(input) {
12
+ return input
13
+ .split(/[^A-Za-z0-9]+/)
14
+ .filter(Boolean)
15
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
16
+ .join('');
17
+ }
18
+ function literalUnion(options) {
19
+ return options.map(o => `'${o.replace(/'/g, "\\'")}'`).join(' | ');
20
+ }
21
+ /** Maps a single Branch to its TypeScript type expression (no optional marker). */
22
+ export function tsTypeForBranch(branch) {
23
+ switch (branch.type) {
24
+ case 'number':
25
+ case 'date':
26
+ return 'number';
27
+ case 'boolean':
28
+ return 'boolean';
29
+ case 'json':
30
+ return 'unknown';
31
+ case 'text':
32
+ case 'richtext':
33
+ case 'file': {
34
+ const base = branch.type === 'text' && branch.options?.length
35
+ ? literalUnion(branch.options)
36
+ : 'string';
37
+ return branch.type === 'file' && branch.multiple ? `${base}[]` : base;
38
+ }
39
+ case 'tags': {
40
+ const base = branch.options?.length ? `(${literalUnion(branch.options)})` : 'string';
41
+ return `${base}[]`;
42
+ }
43
+ case 'relation':
44
+ return branch.multiple ? 'string[]' : 'string';
45
+ case 'repeater': {
46
+ const inner = (branch.fields ?? [])
47
+ .map(f => `${propName(f)}${optional(f)}: ${tsTypeForBranch(f)}`)
48
+ .join('; ');
49
+ return `Array<{ ${inner} }>`;
50
+ }
51
+ default: {
52
+ // Exhaustiveness guard — fails the build if BranchType gains a member.
53
+ const _never = branch.type;
54
+ return _never;
55
+ }
56
+ }
57
+ }
58
+ function optional(branch) {
59
+ return branch.requiredOnCreate ? '' : '?';
60
+ }
61
+ function propName(branch) {
62
+ // alias is a valid SQL column; quote only if not a plain identifier.
63
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(branch.alias) ? branch.alias : `'${branch.alias}'`;
64
+ }
65
+ /** Emits one exported interface for a single Seed. */
66
+ export function interfaceForSeed(seed) {
67
+ const name = pascalCase(seed.slug);
68
+ const props = seed.branches
69
+ .map(b => ` ${propName(b)}${optional(b)}: ${tsTypeForBranch(b)}`)
70
+ .join('\n');
71
+ return (`export interface ${name} {\n` +
72
+ SYSTEM_FIELDS +
73
+ (props ? props + '\n' : '') +
74
+ SYSTEM_TIMESTAMPS +
75
+ `}\n`);
76
+ }
77
+ /** Pure entry point. Deterministic: sorts seeds by slug for stable diffs. */
78
+ export function generateSeedTypes(seeds) {
79
+ const sorted = [...seeds].sort((a, b) => a.slug.localeCompare(b.slug));
80
+ const interfaces = sorted.map(interfaceForSeed).join('\n');
81
+ const registry = `export interface SeedRegistryTypes {\n` +
82
+ sorted.map(s => ` ${propName({ alias: s.slug })}: ${pascalCase(s.slug)}`).join('\n') +
83
+ (sorted.length ? '\n' : '') +
84
+ `}\n`;
85
+ return `${HEADER}\n${interfaces}\n${registry}`;
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beechcms/core",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@types/node": "^26.5.1",
32
32
  "@vitest/coverage-v8": "^5.0.0",
33
- "vitest": "^5.0.0"
33
+ "vitest": "^5.0.5"
34
34
  },
35
35
  "dependencies": {
36
36
  "@tiptap/core": "^3.31.3",