@beechcms/core 0.6.0-preview.4 → 0.6.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 (237) hide show
  1. package/dist/automations/automations-grammar-words.d.ts.map +1 -0
  2. package/dist/automations/automations.repository.interface.d.ts.map +1 -0
  3. package/dist/automations/automations.runner.interface.d.ts.map +1 -0
  4. package/dist/automations/automations.types.d.ts.map +1 -0
  5. package/dist/common/clock.d.ts.map +1 -0
  6. package/dist/common/email.provider.d.ts +32 -0
  7. package/dist/common/email.provider.d.ts.map +1 -0
  8. package/dist/common/email.provider.js +4 -0
  9. package/dist/{hooks.d.ts → common/hooks.d.ts} +2 -2
  10. package/dist/common/hooks.d.ts.map +1 -0
  11. package/dist/common/id-generator.d.ts.map +1 -0
  12. package/dist/common/storage.d.ts.map +1 -0
  13. package/dist/{content-scan.repository.d.ts → content/content-scan.repository.d.ts} +1 -1
  14. package/dist/content/content-scan.repository.d.ts.map +1 -0
  15. package/dist/{content.repository.d.ts → content/content.repository.d.ts} +13 -4
  16. package/dist/content/content.repository.d.ts.map +1 -0
  17. package/dist/content/demo-data.repository.d.ts.map +1 -0
  18. package/dist/content/idempotency.repository.d.ts.map +1 -0
  19. package/dist/content/richtext/richtext-render.d.ts +12 -0
  20. package/dist/content/richtext/richtext-render.d.ts.map +1 -0
  21. package/dist/{richtext-render.js → content/richtext/richtext-render.js} +7 -14
  22. package/dist/content/richtext/richtext.d.ts.map +1 -0
  23. package/dist/{seed.repository.d.ts → content/seed.repository.d.ts} +1 -1
  24. package/dist/content/seed.repository.d.ts.map +1 -0
  25. package/dist/content/slug-utils.d.ts.map +1 -0
  26. package/dist/{dashboard-layout.d.ts → dashboard-layout/dashboard-layout.d.ts} +1 -1
  27. package/dist/dashboard-layout/dashboard-layout.d.ts.map +1 -0
  28. package/dist/dashboard-layout/dashboard-layout.repository.d.ts.map +1 -0
  29. package/dist/dashboard-layout/dashboard-permissions.d.ts.map +1 -0
  30. package/dist/dashboard-layout/dashboard-scopes.d.ts.map +1 -0
  31. package/dist/dashboard-layout/kanban/kanban-position.repository.d.ts +19 -0
  32. package/dist/dashboard-layout/kanban/kanban-position.repository.d.ts.map +1 -0
  33. package/dist/dashboard-layout/kanban/kanban.d.ts +75 -0
  34. package/dist/dashboard-layout/kanban/kanban.d.ts.map +1 -0
  35. package/dist/dashboard-layout/kanban/kanban.js +82 -0
  36. package/dist/dashboard-layout/layout-permissions.d.ts.map +1 -0
  37. package/dist/{seed-layout.d.ts → dashboard-layout/seed-layout.d.ts} +77 -3
  38. package/dist/dashboard-layout/seed-layout.d.ts.map +1 -0
  39. package/dist/{seed-layout.js → dashboard-layout/seed-layout.js} +101 -8
  40. package/dist/{seed-layout.repository.d.ts → dashboard-layout/seed-layout.repository.d.ts} +5 -1
  41. package/dist/dashboard-layout/seed-layout.repository.d.ts.map +1 -0
  42. package/dist/dashboard-layout/view-authorization.d.ts +18 -0
  43. package/dist/dashboard-layout/view-authorization.d.ts.map +1 -0
  44. package/dist/dashboard-layout/view-authorization.js +28 -0
  45. package/dist/engine/ddl.d.ts +173 -0
  46. package/dist/engine/ddl.d.ts.map +1 -0
  47. package/dist/engine/ddl.js +454 -0
  48. package/dist/engine/define-seed.d.ts.map +1 -0
  49. package/dist/engine/engine.d.ts +4 -0
  50. package/dist/engine/engine.d.ts.map +1 -0
  51. package/dist/engine/engine.js +4 -0
  52. package/dist/engine/policies.d.ts.map +1 -0
  53. package/dist/engine/query.d.ts +23 -0
  54. package/dist/engine/query.d.ts.map +1 -0
  55. package/dist/engine/query.js +217 -0
  56. package/dist/engine/relations.d.ts.map +1 -0
  57. package/dist/{schema-mutator.d.ts → engine/schema-mutator.d.ts} +4 -0
  58. package/dist/engine/schema-mutator.d.ts.map +1 -0
  59. package/dist/engine/seed-ddl-destructive.d.ts.map +1 -0
  60. package/dist/engine/seed-ddl.d.ts.map +1 -0
  61. package/dist/engine/seed-registry.d.ts.map +1 -0
  62. package/dist/{seed-registry.js → engine/seed-registry.js} +1 -1
  63. package/dist/engine/seed-types-generator.d.ts +10 -0
  64. package/dist/engine/seed-types-generator.d.ts.map +1 -0
  65. package/dist/engine/seed-types-generator.js +86 -0
  66. package/dist/engine/seed-validation.d.ts.map +1 -0
  67. package/dist/{seed-validation.js → engine/seed-validation.js} +14 -1
  68. package/dist/engine/seeds.d.ts.map +1 -0
  69. package/dist/engine/serialize.d.ts +20 -0
  70. package/dist/engine/serialize.d.ts.map +1 -0
  71. package/dist/engine/serialize.js +160 -0
  72. package/dist/engine/types.d.ts +278 -0
  73. package/dist/engine/types.d.ts.map +1 -0
  74. package/dist/engine/validation/cache.d.ts +12 -0
  75. package/dist/engine/validation/cache.d.ts.map +1 -0
  76. package/dist/engine/validation/cache.js +136 -0
  77. package/dist/engine/validation/file-branch.d.ts +48 -0
  78. package/dist/engine/validation/file-branch.d.ts.map +1 -0
  79. package/dist/engine/validation/file-branch.js +133 -0
  80. package/dist/engine/validation/index.d.ts +115 -0
  81. package/dist/engine/validation/index.d.ts.map +1 -0
  82. package/dist/engine/validation/index.js +336 -0
  83. package/dist/engine/validation/primitives.d.ts +34 -0
  84. package/dist/engine/validation/primitives.d.ts.map +1 -0
  85. package/dist/engine/validation/primitives.js +47 -0
  86. package/dist/engine/validation/richtext-sanitizer.d.ts +30 -0
  87. package/dist/engine/validation/richtext-sanitizer.d.ts.map +1 -0
  88. package/dist/engine/validation/richtext-sanitizer.js +203 -0
  89. package/dist/engine/validation/schema-builders.d.ts +12 -0
  90. package/dist/engine/validation/schema-builders.d.ts.map +1 -0
  91. package/dist/engine/validation/schema-builders.js +413 -0
  92. package/dist/index.d.ts +48 -44
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +48 -44
  95. package/dist/media/file-types.d.ts +13 -0
  96. package/dist/media/file-types.d.ts.map +1 -0
  97. package/dist/media/media.repository.d.ts.map +1 -0
  98. package/dist/queue/queue.interface.d.ts +39 -0
  99. package/dist/queue/queue.interface.d.ts.map +1 -0
  100. package/dist/queue/queue.stub.d.ts +6 -0
  101. package/dist/queue/queue.stub.d.ts.map +1 -0
  102. package/dist/queue/queue.stub.js +8 -0
  103. package/dist/queue/scheduler.interface.d.ts.map +1 -0
  104. package/dist/queue/scheduler.interface.js +3 -0
  105. package/dist/queue/scheduler.stub.d.ts.map +1 -0
  106. package/dist/queue.interface.d.ts +4 -4
  107. package/dist/queue.interface.d.ts.map +1 -1
  108. package/dist/queue.stub.d.ts.map +1 -1
  109. package/dist/rate-limit/rate-limiter.d.ts +5 -0
  110. package/dist/rate-limit/rate-limiter.d.ts.map +1 -1
  111. package/dist/search/search.repository.d.ts +1 -1
  112. package/dist/search/search.repository.d.ts.map +1 -1
  113. package/dist/seed-types-generator.d.ts +1 -1
  114. package/dist/seed-types-generator.d.ts.map +1 -1
  115. package/dist/settings/site-settings.repository.d.ts.map +1 -0
  116. package/dist/settings/site-settings.repository.js +3 -0
  117. package/dist/webhooks/webhook-crypto.d.ts +8 -0
  118. package/dist/webhooks/webhook-crypto.d.ts.map +1 -0
  119. package/dist/webhooks/webhook-crypto.js +37 -0
  120. package/dist/webhooks/webhook-validation.d.ts.map +1 -0
  121. package/dist/widget/widget.repository.d.ts +1 -1
  122. package/dist/widget/widget.repository.d.ts.map +1 -1
  123. package/package.json +4 -3
  124. package/dist/automations-grammar-words.d.ts.map +0 -1
  125. package/dist/automations.repository.interface.d.ts.map +0 -1
  126. package/dist/automations.runner.interface.d.ts.map +0 -1
  127. package/dist/automations.runner.stub.d.ts +0 -5
  128. package/dist/automations.runner.stub.d.ts.map +0 -1
  129. package/dist/automations.runner.stub.js +0 -7
  130. package/dist/automations.types.d.ts.map +0 -1
  131. package/dist/clock.d.ts.map +0 -1
  132. package/dist/content-scan.repository.d.ts.map +0 -1
  133. package/dist/content.repository.d.ts.map +0 -1
  134. package/dist/dashboard-layout.d.ts.map +0 -1
  135. package/dist/dashboard-layout.repository.d.ts.map +0 -1
  136. package/dist/dashboard-permissions.d.ts.map +0 -1
  137. package/dist/dashboard-scopes.d.ts.map +0 -1
  138. package/dist/define-seed.d.ts.map +0 -1
  139. package/dist/demo-data.repository.d.ts.map +0 -1
  140. package/dist/engine.d.ts +0 -144
  141. package/dist/engine.d.ts.map +0 -1
  142. package/dist/engine.js +0 -723
  143. package/dist/file-types.d.ts +0 -13
  144. package/dist/file-types.d.ts.map +0 -1
  145. package/dist/hooks.d.ts.map +0 -1
  146. package/dist/id-generator.d.ts.map +0 -1
  147. package/dist/idempotency.repository.d.ts.map +0 -1
  148. package/dist/layout-permissions.d.ts.map +0 -1
  149. package/dist/media.repository.d.ts.map +0 -1
  150. package/dist/policies.d.ts.map +0 -1
  151. package/dist/relations.d.ts.map +0 -1
  152. package/dist/richtext-render.d.ts +0 -11
  153. package/dist/richtext-render.d.ts.map +0 -1
  154. package/dist/richtext.d.ts.map +0 -1
  155. package/dist/scheduler.interface.d.ts.map +0 -1
  156. package/dist/scheduler.stub.d.ts.map +0 -1
  157. package/dist/schema-mutator.d.ts.map +0 -1
  158. package/dist/seed-ddl-destructive.d.ts.map +0 -1
  159. package/dist/seed-ddl.d.ts.map +0 -1
  160. package/dist/seed-layout.d.ts.map +0 -1
  161. package/dist/seed-layout.repository.d.ts.map +0 -1
  162. package/dist/seed-registry.d.ts.map +0 -1
  163. package/dist/seed-validation.d.ts.map +0 -1
  164. package/dist/seed.repository.d.ts.map +0 -1
  165. package/dist/seeds.d.ts.map +0 -1
  166. package/dist/site-settings.repository.d.ts.map +0 -1
  167. package/dist/slug-utils.d.ts.map +0 -1
  168. package/dist/storage.d.ts.map +0 -1
  169. package/dist/types.d.ts +0 -237
  170. package/dist/types.d.ts.map +0 -1
  171. package/dist/validation.d.ts +0 -50
  172. package/dist/validation.d.ts.map +0 -1
  173. package/dist/validation.js +0 -659
  174. package/dist/webhook-validation.d.ts.map +0 -1
  175. /package/dist/{automations-grammar-words.d.ts → automations/automations-grammar-words.d.ts} +0 -0
  176. /package/dist/{automations-grammar-words.js → automations/automations-grammar-words.js} +0 -0
  177. /package/dist/{automations.repository.interface.d.ts → automations/automations.repository.interface.d.ts} +0 -0
  178. /package/dist/{automations.repository.interface.js → automations/automations.repository.interface.js} +0 -0
  179. /package/dist/{automations.runner.interface.d.ts → automations/automations.runner.interface.d.ts} +0 -0
  180. /package/dist/{automations.runner.interface.js → automations/automations.runner.interface.js} +0 -0
  181. /package/dist/{automations.types.d.ts → automations/automations.types.d.ts} +0 -0
  182. /package/dist/{automations.types.js → automations/automations.types.js} +0 -0
  183. /package/dist/{clock.d.ts → common/clock.d.ts} +0 -0
  184. /package/dist/{clock.js → common/clock.js} +0 -0
  185. /package/dist/{hooks.js → common/hooks.js} +0 -0
  186. /package/dist/{id-generator.d.ts → common/id-generator.d.ts} +0 -0
  187. /package/dist/{id-generator.js → common/id-generator.js} +0 -0
  188. /package/dist/{storage.d.ts → common/storage.d.ts} +0 -0
  189. /package/dist/{storage.js → common/storage.js} +0 -0
  190. /package/dist/{content-scan.repository.js → content/content-scan.repository.js} +0 -0
  191. /package/dist/{content.repository.js → content/content.repository.js} +0 -0
  192. /package/dist/{demo-data.repository.d.ts → content/demo-data.repository.d.ts} +0 -0
  193. /package/dist/{demo-data.repository.js → content/demo-data.repository.js} +0 -0
  194. /package/dist/{idempotency.repository.d.ts → content/idempotency.repository.d.ts} +0 -0
  195. /package/dist/{idempotency.repository.js → content/idempotency.repository.js} +0 -0
  196. /package/dist/{richtext.d.ts → content/richtext/richtext.d.ts} +0 -0
  197. /package/dist/{richtext.js → content/richtext/richtext.js} +0 -0
  198. /package/dist/{seed.repository.js → content/seed.repository.js} +0 -0
  199. /package/dist/{slug-utils.d.ts → content/slug-utils.d.ts} +0 -0
  200. /package/dist/{slug-utils.js → content/slug-utils.js} +0 -0
  201. /package/dist/{dashboard-layout.js → dashboard-layout/dashboard-layout.js} +0 -0
  202. /package/dist/{dashboard-layout.repository.d.ts → dashboard-layout/dashboard-layout.repository.d.ts} +0 -0
  203. /package/dist/{dashboard-layout.repository.js → dashboard-layout/dashboard-layout.repository.js} +0 -0
  204. /package/dist/{dashboard-permissions.d.ts → dashboard-layout/dashboard-permissions.d.ts} +0 -0
  205. /package/dist/{dashboard-permissions.js → dashboard-layout/dashboard-permissions.js} +0 -0
  206. /package/dist/{dashboard-scopes.d.ts → dashboard-layout/dashboard-scopes.d.ts} +0 -0
  207. /package/dist/{dashboard-scopes.js → dashboard-layout/dashboard-scopes.js} +0 -0
  208. /package/dist/{media.repository.js → dashboard-layout/kanban/kanban-position.repository.js} +0 -0
  209. /package/dist/{layout-permissions.d.ts → dashboard-layout/layout-permissions.d.ts} +0 -0
  210. /package/dist/{layout-permissions.js → dashboard-layout/layout-permissions.js} +0 -0
  211. /package/dist/{seed-layout.repository.js → dashboard-layout/seed-layout.repository.js} +0 -0
  212. /package/dist/{define-seed.d.ts → engine/define-seed.d.ts} +0 -0
  213. /package/dist/{define-seed.js → engine/define-seed.js} +0 -0
  214. /package/dist/{policies.d.ts → engine/policies.d.ts} +0 -0
  215. /package/dist/{policies.js → engine/policies.js} +0 -0
  216. /package/dist/{relations.d.ts → engine/relations.d.ts} +0 -0
  217. /package/dist/{relations.js → engine/relations.js} +0 -0
  218. /package/dist/{schema-mutator.js → engine/schema-mutator.js} +0 -0
  219. /package/dist/{seed-ddl-destructive.d.ts → engine/seed-ddl-destructive.d.ts} +0 -0
  220. /package/dist/{seed-ddl-destructive.js → engine/seed-ddl-destructive.js} +0 -0
  221. /package/dist/{seed-ddl.d.ts → engine/seed-ddl.d.ts} +0 -0
  222. /package/dist/{seed-ddl.js → engine/seed-ddl.js} +0 -0
  223. /package/dist/{seed-registry.d.ts → engine/seed-registry.d.ts} +0 -0
  224. /package/dist/{seed-validation.d.ts → engine/seed-validation.d.ts} +0 -0
  225. /package/dist/{seeds.d.ts → engine/seeds.d.ts} +0 -0
  226. /package/dist/{seeds.js → engine/seeds.js} +0 -0
  227. /package/dist/{types.js → engine/types.js} +0 -0
  228. /package/dist/{file-types.js → media/file-types.js} +0 -0
  229. /package/dist/{media.repository.d.ts → media/media.repository.d.ts} +0 -0
  230. /package/dist/{scheduler.interface.js → media/media.repository.js} +0 -0
  231. /package/dist/{site-settings.repository.js → queue/queue.interface.js} +0 -0
  232. /package/dist/{scheduler.interface.d.ts → queue/scheduler.interface.d.ts} +0 -0
  233. /package/dist/{scheduler.stub.d.ts → queue/scheduler.stub.d.ts} +0 -0
  234. /package/dist/{scheduler.stub.js → queue/scheduler.stub.js} +0 -0
  235. /package/dist/{site-settings.repository.d.ts → settings/site-settings.repository.d.ts} +0 -0
  236. /package/dist/{webhook-validation.d.ts → webhooks/webhook-validation.d.ts} +0 -0
  237. /package/dist/{webhook-validation.js → webhooks/webhook-validation.js} +0 -0
@@ -1,7 +1,37 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // Copyright (c) 2024–2026 Flavio De Musso
3
3
  import { z } from 'zod';
4
- import { findBranchById } from './seed-registry.js';
4
+ import { findBranchById } from '../engine/seed-registry.js';
5
+ // ---------------------------------------------------------------------------
6
+ // View config — per-seed, per-view dashboard preferences (KB-S02)
7
+ // ---------------------------------------------------------------------------
8
+ export const kanbanViewConfigSchema = z.object({
9
+ axisBranchId: z.string().nullable(),
10
+ sort: z.object({ branchId: z.string(), dir: z.enum(['ASC', 'DESC']) }).nullable(),
11
+ hiddenColumnValues: z.array(z.string()).optional(),
12
+ collapsedColumnValues: z.array(z.string()).optional(),
13
+ });
14
+ // ---------------------------------------------------------------------------
15
+ // Kanban card layout (view_config.card)
16
+ // ---------------------------------------------------------------------------
17
+ export const cardSlotFieldSchema = z.object({
18
+ branchId: z.string().regex(/^br_[A-Za-z0-9]+$/),
19
+ });
20
+ export const kanbanCardConfigSchema = z.object({
21
+ version: z.literal(1),
22
+ /** Optional media/avatar slot. Full width. Max 1. */
23
+ media: cardSlotFieldSchema.nullable().optional(),
24
+ /** Full-width primary line. Max 1. */
25
+ header: cardSlotFieldSchema.nullable().optional(),
26
+ /** Full-width secondary line. Max 1. */
27
+ subtitle: cardSlotFieldSchema.nullable().optional(),
28
+ /** 2-column grid. Hard cap enforced by validator (see METADATA_SLOT_CAP). */
29
+ metadata: z.array(cardSlotFieldSchema).max(6).default([]),
30
+ });
31
+ export const seedViewConfigSchema = z.object({
32
+ kanban: kanbanViewConfigSchema.optional(),
33
+ card: kanbanCardConfigSchema.optional(),
34
+ }).passthrough();
5
35
  // ---------------------------------------------------------------------------
6
36
  // Branch-type rules used by both the default generator and the validator.
7
37
  // ---------------------------------------------------------------------------
@@ -37,6 +67,58 @@ export function isSeoBranch(branch) {
37
67
  return branch.alias.startsWith('meta');
38
68
  }
39
69
  // ---------------------------------------------------------------------------
70
+ // Kanban card eligibility + pure validator
71
+ // ---------------------------------------------------------------------------
72
+ export const METADATA_SLOT_CAP = 6;
73
+ const CARD_FORBIDDEN_TYPES = new Set(['richtext', 'json', 'repeater']);
74
+ export function isCardEligibleBranch(branch) {
75
+ if (SYSTEM_ALIASES.has(branch.alias))
76
+ return false;
77
+ if (branch.policies?.visibility === 'hidden')
78
+ return false;
79
+ if (CARD_FORBIDDEN_TYPES.has(branch.type))
80
+ return false;
81
+ return true;
82
+ }
83
+ export function validateCardConfigAgainstSeed(config, seed) {
84
+ const errors = [];
85
+ const seen = new Set();
86
+ const keepSingle = (f) => {
87
+ if (!f)
88
+ return null;
89
+ const b = findBranchById(seed, f.branchId);
90
+ if (!b || !isCardEligibleBranch(b))
91
+ return null;
92
+ if (seen.has(f.branchId)) {
93
+ errors.push(`Branch ${f.branchId} placed more than once.`);
94
+ return null;
95
+ }
96
+ seen.add(f.branchId);
97
+ return { branchId: f.branchId };
98
+ };
99
+ const media = keepSingle(config.media);
100
+ const header = keepSingle(config.header);
101
+ const subtitle = keepSingle(config.subtitle);
102
+ const metadata = [];
103
+ for (const f of config.metadata ?? []) {
104
+ const b = findBranchById(seed, f.branchId);
105
+ if (!b || !isCardEligibleBranch(b))
106
+ continue;
107
+ if (seen.has(f.branchId)) {
108
+ errors.push(`Branch ${f.branchId} placed more than once.`);
109
+ continue;
110
+ }
111
+ seen.add(f.branchId);
112
+ if (metadata.length >= METADATA_SLOT_CAP) {
113
+ errors.push(`Metadata slot exceeds cap ${METADATA_SLOT_CAP}.`);
114
+ continue;
115
+ }
116
+ metadata.push({ branchId: f.branchId });
117
+ }
118
+ const cleaned = { version: 1, media, header, subtitle, metadata };
119
+ return errors.length ? { ok: false, errors, cleaned } : { ok: true, cleaned };
120
+ }
121
+ // ---------------------------------------------------------------------------
40
122
  // Zod schema for shape validation
41
123
  // ---------------------------------------------------------------------------
42
124
  export const layoutFieldSchema = z.object({ branchId: z.string().regex(/^br_[A-Za-z0-9]+$/) });
@@ -116,15 +198,26 @@ export function generateDefaultLayout(seed, opts) {
116
198
  return { version: 1, tabs: [dataTab, seoTab] };
117
199
  }
118
200
  export function validateLayoutAgainstSeed(layout, seed) {
201
+ // Gracefully handle undefined/null layout, or missing tabs (e.g. from {} placeholder)
202
+ if (!layout || !Array.isArray(layout.tabs)) {
203
+ const defaultLayout = generateDefaultLayout(seed);
204
+ return {
205
+ ok: false,
206
+ errors: ['Invalid layout structure: missing tabs'],
207
+ cleaned: defaultLayout,
208
+ };
209
+ }
119
210
  const errors = [];
120
211
  const seenBranchIds = new Set();
121
- const cleanedTabs = layout.tabs.map((tab) => ({
212
+ const cleanedTabs = (layout.tabs ?? []).map((tab) => ({
122
213
  ...tab,
123
- sections: tab.sections.map((section) => ({
214
+ sections: (tab?.sections ?? []).map((section) => ({
124
215
  ...section,
125
- columns: section.columns.map((col) => ({
216
+ columns: (section?.columns ?? []).map((col) => ({
126
217
  ...col,
127
- fields: (col.fields ?? []).filter((f) => {
218
+ fields: (col?.fields ?? []).filter((f) => {
219
+ if (!f || typeof f !== 'object' || !('branchId' in f))
220
+ return false;
128
221
  const branch = findBranchById(seed, f.branchId);
129
222
  if (!branch)
130
223
  return false;
@@ -137,10 +230,10 @@ export function validateLayoutAgainstSeed(layout, seed) {
137
230
  }));
138
231
  // Duplicate and full-width constraint checks on cleaned layout
139
232
  for (const tab of cleanedTabs) {
140
- for (const section of tab.sections) {
233
+ for (const section of tab.sections ?? []) {
141
234
  const fieldsInSection = [];
142
- for (const col of section.columns) {
143
- for (const f of col.fields) {
235
+ for (const col of section.columns ?? []) {
236
+ for (const f of col.fields ?? []) {
144
237
  const branch = findBranchById(seed, f.branchId);
145
238
  if (!branch)
146
239
  continue;
@@ -1,4 +1,4 @@
1
- import type { FormLayout } from './seed-layout.js';
1
+ import type { FormLayout, SeedViewConfig } from './seed-layout.js';
2
2
  export interface SeedLayoutRecord {
3
3
  slug: string;
4
4
  layout: FormLayout;
@@ -14,5 +14,9 @@ export interface ISeedLayoutRepository {
14
14
  upsert(slug: string, layout: FormLayout, updatedBy: string): Promise<void>;
15
15
  /** Remove the stored row — used by the "Reset" action. */
16
16
  remove(slug: string): Promise<void>;
17
+ /** Return the per-view config blob for a seed, or null if none was stored. */
18
+ getViewConfig(slug: string): Promise<SeedViewConfig | null>;
19
+ /** Upsert the per-view config for a seed. */
20
+ setViewConfig(slug: string, config: SeedViewConfig, updatedBy: string): Promise<void>;
17
21
  }
18
22
  //# sourceMappingURL=seed-layout.repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-layout.repository.d.ts","sourceRoot":"","sources":["../../src/dashboard-layout/seed-layout.repository.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAElE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IACnD,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAA;IAC/C,mDAAmD;IACnD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1E,0DAA0D;IAC1D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,8EAA8E;IAC9E,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IAC3D,6CAA6C;IAC7C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtF"}
@@ -0,0 +1,18 @@
1
+ import type { Seed } from '../engine/types.js';
2
+ /** Views that can be authorized on a seed. Only rendered views (grid/chart excluded — unrendered). */
3
+ export type DashboardView = 'table' | 'gallery' | 'kanban';
4
+ /** Order drives ViewSwitcher tab order. */
5
+ export declare const AUTHORIZABLE_VIEWS: readonly DashboardView[];
6
+ /** Applied to seeds with no explicit `dashboard.views` (backward compatibility). */
7
+ export declare const DEFAULT_AUTHORIZED_VIEWS: readonly DashboardView[];
8
+ /**
9
+ * Resolves the effective, deduplicated, canonically-ordered authorized views for a seed.
10
+ * Invariants:
11
+ * - 'table' is ALWAYS present (universal fallback — relational backing).
12
+ * - Unknown/legacy values are stripped.
13
+ * - Empty/undefined config → DEFAULT_AUTHORIZED_VIEWS (then table-guaranteed).
14
+ */
15
+ export declare function resolveAuthorizedViews(seed: Pick<Seed, 'dashboard'>): DashboardView[];
16
+ /** URL-guard helper: is `view` authorized for this seed? */
17
+ export declare function isViewAuthorized(seed: Pick<Seed, 'dashboard'>, view: string): view is DashboardView;
18
+ //# sourceMappingURL=view-authorization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-authorization.d.ts","sourceRoot":"","sources":["../../src/dashboard-layout/view-authorization.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAE9C,sGAAsG;AACtG,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE1D,2CAA2C;AAC3C,eAAO,MAAM,kBAAkB,EAAE,SAAS,aAAa,EAA4C,CAAA;AAEnG,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,EAAE,SAAS,aAAa,EAAuB,CAAA;AAIpF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,aAAa,EAAE,CAOrF;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,aAAa,CAEnG"}
@@ -0,0 +1,28 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /** Order drives ViewSwitcher tab order. */
4
+ export const AUTHORIZABLE_VIEWS = ['table', 'gallery', 'kanban'];
5
+ /** Applied to seeds with no explicit `dashboard.views` (backward compatibility). */
6
+ export const DEFAULT_AUTHORIZED_VIEWS = ['table'];
7
+ const VALID = new Set(AUTHORIZABLE_VIEWS);
8
+ /**
9
+ * Resolves the effective, deduplicated, canonically-ordered authorized views for a seed.
10
+ * Invariants:
11
+ * - 'table' is ALWAYS present (universal fallback — relational backing).
12
+ * - Unknown/legacy values are stripped.
13
+ * - Empty/undefined config → DEFAULT_AUTHORIZED_VIEWS (then table-guaranteed).
14
+ */
15
+ export function resolveAuthorizedViews(seed) {
16
+ const raw = seed.dashboard?.views;
17
+ const source = Array.isArray(raw) && raw.length > 0 ? raw : DEFAULT_AUTHORIZED_VIEWS;
18
+ const allowed = new Set();
19
+ for (const v of source)
20
+ if (VALID.has(v))
21
+ allowed.add(v);
22
+ allowed.add('table');
23
+ return AUTHORIZABLE_VIEWS.filter((v) => allowed.has(v));
24
+ }
25
+ /** URL-guard helper: is `view` authorized for this seed? */
26
+ export function isViewAuthorized(seed, view) {
27
+ return VALID.has(view) && resolveAuthorizedViews(seed).includes(view);
28
+ }
@@ -0,0 +1,173 @@
1
+ import type { Seed, Branch } from './types.js';
2
+ /**
3
+ * System-defined columns that exist on all content tables.
4
+ */
5
+ export declare const SYSTEM_COLUMNS: Set<string>;
6
+ /**
7
+ * Returns the SQL table name for a given Seed.
8
+ *
9
+ * @param seed The seed definition.
10
+ * @returns The table name.
11
+ */
12
+ export declare function tableName(seed: Seed): string;
13
+ /**
14
+ * Returns the FTS (Full-Text Search) virtual table name for a given Seed.
15
+ *
16
+ * @param seed The seed definition.
17
+ * @returns The FTS table name.
18
+ */
19
+ export declare function ftsTableName(seed: Seed): string;
20
+ /**
21
+ * Determines whether a column is valid for a given Seed (either system column or branch alias).
22
+ *
23
+ * @param seed The seed definition.
24
+ * @param col The column name to check.
25
+ * @returns True if the column is valid, false otherwise.
26
+ */
27
+ export declare function isValidColumn(seed: Seed, col: string): boolean;
28
+ /**
29
+ * Filters the branches of a Seed to return only the ones that are indexable for full-text search.
30
+ *
31
+ * @param seed The seed definition.
32
+ * @returns An array of indexable branches.
33
+ */
34
+ export declare function indexableSearchBranches(seed: Seed): Branch[];
35
+ /**
36
+ * Generates the SQL `CREATE TABLE IF NOT EXISTS content_{slug}` statement
37
+ * with system columns and one column per branch.
38
+ *
39
+ * @param seed The seed definition.
40
+ * @returns The CREATE TABLE SQL statement.
41
+ */
42
+ export declare function generateCreateTable(seed: Seed): string;
43
+ /**
44
+ * Generates the SQL `CREATE TABLE IF NOT EXISTS content_{slug}_drafts` statement
45
+ * for seeds that allow drafts.
46
+ *
47
+ * @param seed The seed definition.
48
+ * @returns The draft table CREATE TABLE SQL statement, or null if drafts are disabled.
49
+ */
50
+ export declare function generateDraftTable(seed: Seed): string | null;
51
+ /**
52
+ * Generates the SQL `ALTER TABLE content_{slug} ADD COLUMN {alias} {type}` statement.
53
+ *
54
+ * @param seed The seed definition.
55
+ * @param branch The branch definition for the new column.
56
+ * @returns The ALTER TABLE SQL statement.
57
+ */
58
+ export declare function generateAddColumn(seed: Seed, branch: Branch): string;
59
+ /**
60
+ * Generates SQL index statements for system fields (status, created_at)
61
+ * and indexable branch columns.
62
+ *
63
+ * @param seed The seed definition.
64
+ * @returns An array of CREATE INDEX SQL statements.
65
+ */
66
+ export declare function generateIndexes(seed: Seed): string[];
67
+ /**
68
+ * Generates the FTS5 virtual table definition for indexable text/richtext branches.
69
+ *
70
+ * @param seed The seed definition.
71
+ * @returns The CREATE VIRTUAL TABLE SQL statement, or null if no branches are indexable.
72
+ */
73
+ export declare function generateFtsTable(seed: Seed): string | null;
74
+ /**
75
+ * Generates FTS5 triggers (insert, update, delete) to keep the FTS virtual table synchronized.
76
+ *
77
+ * @param seed The seed definition.
78
+ * @returns An array of CREATE TRIGGER SQL statements.
79
+ */
80
+ export declare function generateFtsTriggers(seed: Seed): string[];
81
+ /**
82
+ * Expected database schema column interface.
83
+ */
84
+ export interface SchemaColumn {
85
+ /** Column name. */
86
+ name: string;
87
+ /** SQLite storage type. */
88
+ sqlType: 'TEXT' | 'REAL' | 'INTEGER';
89
+ /** Whether the column is NOT NULL. */
90
+ notNull: boolean;
91
+ /** Whether the column is the PRIMARY KEY. */
92
+ isPk: boolean;
93
+ }
94
+ /**
95
+ * Returns the list of expected columns for a Seed's table.
96
+ * Used to compare current vs expected schema (diffing).
97
+ *
98
+ * @param seed The seed definition.
99
+ * @returns The array of expected schema columns.
100
+ */
101
+ export declare function getExpectedColumns(seed: Seed): SchemaColumn[];
102
+ /**
103
+ * Returns the junction table name for a many-to-many relation branch.
104
+ * Format: `rel_<seedSlug>_<branchAlias>`.
105
+ *
106
+ * @param seedSlug The slug of the parent Seed.
107
+ * @param branchAlias The alias of the relation branch.
108
+ * @returns The junction table name.
109
+ */
110
+ export declare function junctionTableName(seedSlug: string, branchAlias: string): string;
111
+ /**
112
+ * Generates `CREATE TABLE IF NOT EXISTS rel_<seed>_<alias>` statement for a multi-relation branch.
113
+ *
114
+ * @param seed The parent seed definition.
115
+ * @param branch The multi-relation branch definition.
116
+ * @throws {Error} If called on a non-multi-relation branch or if `targetSeed` is missing.
117
+ * @returns The CREATE TABLE SQL statement.
118
+ */
119
+ export declare function generateJunctionTable(seed: Seed, branch: Branch): string;
120
+ /**
121
+ * Generates the two B-tree indexes for a junction table:
122
+ * one on `parent_id` (listing entries by parent) and one on `target_id` (handling cascade checks from target side).
123
+ *
124
+ * @param seed The parent seed definition.
125
+ * @param branch The multi-relation branch definition.
126
+ * @returns An array of CREATE INDEX SQL statements.
127
+ */
128
+ export declare function generateJunctionIndexes(seed: Seed, branch: Branch): string[];
129
+ /**
130
+ * Generates the drafts junction table `rel_<seed>_<alias>_drafts` for a many-to-many relation branch.
131
+ *
132
+ * @param seed The parent seed definition.
133
+ * @param branch The multi-relation branch definition.
134
+ * @returns The CREATE TABLE SQL statement, or null if drafts are disabled.
135
+ */
136
+ export declare function generateJunctionDraftTable(seed: Seed, branch: Branch): string | null;
137
+ /**
138
+ * Returns every `DROP TABLE IF EXISTS` needed to fully remove a content type:
139
+ * the main table, the drafts table (if drafts enabled), full-text search table/triggers, and junction tables.
140
+ *
141
+ * @param seed The seed definition.
142
+ * @returns An array of DROP TABLE / DROP TRIGGER SQL statements.
143
+ */
144
+ export declare function generateDropTable(seed: Seed): string[];
145
+ /**
146
+ * Returns the `DROP COLUMN` SQL statements for removing a single field.
147
+ * Drops the junction table for multi-relations, or alters the tables to drop the column.
148
+ *
149
+ * @param seed The seed definition.
150
+ * @param alias The alias of the branch to drop.
151
+ * @returns An array of SQL statements.
152
+ */
153
+ export declare function generateDropColumn(seed: Seed, alias: string): string[];
154
+ /**
155
+ * Returns the `RENAME COLUMN` SQL statements for renaming a field's alias.
156
+ * For a multi-relation branch, renames the junction table (+ drafts junction table) instead.
157
+ *
158
+ * @param seed The seed definition.
159
+ * @param from The old alias.
160
+ * @param to The new alias.
161
+ * @returns An array of SQL statements.
162
+ */
163
+ export declare function generateRenameColumn(seed: Seed, from: string, to: string): string[];
164
+ /**
165
+ * Returns the statements that change a column's SQL type in place using CAST, bypassing SQLite rebuild limitations.
166
+ *
167
+ * @param seed The seed definition.
168
+ * @param branch The target branch definition (carrying the new type).
169
+ * @throws {Error} If called on a multi-relation branch.
170
+ * @returns An array of SQL statements.
171
+ */
172
+ export declare function generateRetypeColumn(seed: Seed, branch: Branch): string[];
173
+ //# sourceMappingURL=ddl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../../src/engine/ddl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,IAAI,EACJ,MAAM,EAOP,MAAM,YAAY,CAAC;AA6BpB;;GAEG;AACH,eAAO,MAAM,cAAc,aAAgE,CAAA;AAG3F;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE5C;AAcD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAGD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAG9D;AAGD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAI5D;AAgCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAwBtD;AAGD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAwB5D;AAGD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGpE;AAGD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAoBpD;AAGD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAc1D;AAGD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAgCxD;AAGD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,2BAA2B;IAC3B,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACpC,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,6CAA6C;IAC7C,IAAI,EAAE,OAAO,CAAA;CACd;AAGD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,EAAE,CAgB7D;AAGD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/E;AAGD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBxE;AAGD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM5E;AAGD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAapF;AAGD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAwBtD;AAGD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBtE;AAGD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBnF;AAGD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBzE"}