@beechcms/core 0.6.0-preview.4 → 0.6.0

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 (236) 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 +208 -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/{types.d.ts → engine/types.d.ts} +102 -63
  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 +115 -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 +127 -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 +333 -0
  83. package/dist/engine/validation/primitives.d.ts +33 -0
  84. package/dist/engine/validation/primitives.d.ts.map +1 -0
  85. package/dist/engine/validation/primitives.js +43 -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 +193 -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 +322 -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.map +0 -1
  170. package/dist/validation.d.ts +0 -50
  171. package/dist/validation.d.ts.map +0 -1
  172. package/dist/validation.js +0 -659
  173. package/dist/webhook-validation.d.ts.map +0 -1
  174. /package/dist/{automations-grammar-words.d.ts → automations/automations-grammar-words.d.ts} +0 -0
  175. /package/dist/{automations-grammar-words.js → automations/automations-grammar-words.js} +0 -0
  176. /package/dist/{automations.repository.interface.d.ts → automations/automations.repository.interface.d.ts} +0 -0
  177. /package/dist/{automations.repository.interface.js → automations/automations.repository.interface.js} +0 -0
  178. /package/dist/{automations.runner.interface.d.ts → automations/automations.runner.interface.d.ts} +0 -0
  179. /package/dist/{automations.runner.interface.js → automations/automations.runner.interface.js} +0 -0
  180. /package/dist/{automations.types.d.ts → automations/automations.types.d.ts} +0 -0
  181. /package/dist/{automations.types.js → automations/automations.types.js} +0 -0
  182. /package/dist/{clock.d.ts → common/clock.d.ts} +0 -0
  183. /package/dist/{clock.js → common/clock.js} +0 -0
  184. /package/dist/{hooks.js → common/hooks.js} +0 -0
  185. /package/dist/{id-generator.d.ts → common/id-generator.d.ts} +0 -0
  186. /package/dist/{id-generator.js → common/id-generator.js} +0 -0
  187. /package/dist/{storage.d.ts → common/storage.d.ts} +0 -0
  188. /package/dist/{storage.js → common/storage.js} +0 -0
  189. /package/dist/{content-scan.repository.js → content/content-scan.repository.js} +0 -0
  190. /package/dist/{content.repository.js → content/content.repository.js} +0 -0
  191. /package/dist/{demo-data.repository.d.ts → content/demo-data.repository.d.ts} +0 -0
  192. /package/dist/{demo-data.repository.js → content/demo-data.repository.js} +0 -0
  193. /package/dist/{idempotency.repository.d.ts → content/idempotency.repository.d.ts} +0 -0
  194. /package/dist/{idempotency.repository.js → content/idempotency.repository.js} +0 -0
  195. /package/dist/{richtext.d.ts → content/richtext/richtext.d.ts} +0 -0
  196. /package/dist/{richtext.js → content/richtext/richtext.js} +0 -0
  197. /package/dist/{seed.repository.js → content/seed.repository.js} +0 -0
  198. /package/dist/{slug-utils.d.ts → content/slug-utils.d.ts} +0 -0
  199. /package/dist/{slug-utils.js → content/slug-utils.js} +0 -0
  200. /package/dist/{dashboard-layout.js → dashboard-layout/dashboard-layout.js} +0 -0
  201. /package/dist/{dashboard-layout.repository.d.ts → dashboard-layout/dashboard-layout.repository.d.ts} +0 -0
  202. /package/dist/{dashboard-layout.repository.js → dashboard-layout/dashboard-layout.repository.js} +0 -0
  203. /package/dist/{dashboard-permissions.d.ts → dashboard-layout/dashboard-permissions.d.ts} +0 -0
  204. /package/dist/{dashboard-permissions.js → dashboard-layout/dashboard-permissions.js} +0 -0
  205. /package/dist/{dashboard-scopes.d.ts → dashboard-layout/dashboard-scopes.d.ts} +0 -0
  206. /package/dist/{dashboard-scopes.js → dashboard-layout/dashboard-scopes.js} +0 -0
  207. /package/dist/{media.repository.js → dashboard-layout/kanban/kanban-position.repository.js} +0 -0
  208. /package/dist/{layout-permissions.d.ts → dashboard-layout/layout-permissions.d.ts} +0 -0
  209. /package/dist/{layout-permissions.js → dashboard-layout/layout-permissions.js} +0 -0
  210. /package/dist/{seed-layout.repository.js → dashboard-layout/seed-layout.repository.js} +0 -0
  211. /package/dist/{define-seed.d.ts → engine/define-seed.d.ts} +0 -0
  212. /package/dist/{define-seed.js → engine/define-seed.js} +0 -0
  213. /package/dist/{policies.d.ts → engine/policies.d.ts} +0 -0
  214. /package/dist/{policies.js → engine/policies.js} +0 -0
  215. /package/dist/{relations.d.ts → engine/relations.d.ts} +0 -0
  216. /package/dist/{relations.js → engine/relations.js} +0 -0
  217. /package/dist/{schema-mutator.js → engine/schema-mutator.js} +0 -0
  218. /package/dist/{seed-ddl-destructive.d.ts → engine/seed-ddl-destructive.d.ts} +0 -0
  219. /package/dist/{seed-ddl-destructive.js → engine/seed-ddl-destructive.js} +0 -0
  220. /package/dist/{seed-ddl.d.ts → engine/seed-ddl.d.ts} +0 -0
  221. /package/dist/{seed-ddl.js → engine/seed-ddl.js} +0 -0
  222. /package/dist/{seed-registry.d.ts → engine/seed-registry.d.ts} +0 -0
  223. /package/dist/{seed-validation.d.ts → engine/seed-validation.d.ts} +0 -0
  224. /package/dist/{seeds.d.ts → engine/seeds.d.ts} +0 -0
  225. /package/dist/{seeds.js → engine/seeds.js} +0 -0
  226. /package/dist/{types.js → engine/types.js} +0 -0
  227. /package/dist/{file-types.js → media/file-types.js} +0 -0
  228. /package/dist/{media.repository.d.ts → media/media.repository.d.ts} +0 -0
  229. /package/dist/{scheduler.interface.js → media/media.repository.js} +0 -0
  230. /package/dist/{site-settings.repository.js → queue/queue.interface.js} +0 -0
  231. /package/dist/{scheduler.interface.d.ts → queue/scheduler.interface.d.ts} +0 -0
  232. /package/dist/{scheduler.stub.d.ts → queue/scheduler.stub.d.ts} +0 -0
  233. /package/dist/{scheduler.stub.js → queue/scheduler.stub.js} +0 -0
  234. /package/dist/{site-settings.repository.d.ts → settings/site-settings.repository.d.ts} +0 -0
  235. /package/dist/{webhook-validation.d.ts → webhooks/webhook-validation.d.ts} +0 -0
  236. /package/dist/{webhook-validation.js → webhooks/webhook-validation.js} +0 -0
@@ -1,47 +1,55 @@
1
- import type { FileAccept } from './file-types.js';
1
+ /**
2
+ * @module Types
3
+ * Core schema types for the Botanical Engine: `Seed` (content type definition),
4
+ * `Branch` (field definition), and the query types consumed by `buildSelectQuery`.
5
+ * Pure data shapes — no runtime logic lives here.
6
+ */
7
+ import type { FileAccept } from '../media/file-types.js';
8
+ import type { DashboardView } from '../dashboard-layout/view-authorization.js';
9
+ /** All supported field value types for a Branch. */
2
10
  export type BranchType = 'text' | 'number' | 'boolean' | 'json' | 'date' | 'richtext' | 'file' | 'tags' | 'relation' | 'repeater';
3
- /** Configurazioni specializzate per il tipo di branch 'number' */
11
+ /** Specialized configuration for a branch of type 'number'. */
4
12
  export interface NumberFieldOptions {
5
- /** Stile di presentazione visiva. Default: 'decimal' */
13
+ /** Visual display style. Default: 'decimal'. */
6
14
  format?: 'decimal' | 'currency' | 'percentage' | 'compact';
7
- /** Codice valuta ISO 4217 (obbligatorio se format === 'currency'). Es. 'EUR', 'USD' */
15
+ /** ISO 4217 currency code (required when format === 'currency'). E.g. 'EUR', 'USD'. */
8
16
  currency?: string;
9
- /** Testo personalizzato da anteporre al valore formattato */
17
+ /** Custom text prepended to the formatted value. */
10
18
  prefix?: string;
11
- /** Unità di misura personalizzata da appendere al valore formattato (es. 'kg', 'm²') */
19
+ /** Custom unit appended to the formatted value (e.g. 'kg', 'm²'). */
12
20
  suffix?: string;
13
- /** Numero esplicito di cifre decimali da visualizzare e imporre */
21
+ /** Explicit number of decimal digits to display and enforce. */
14
22
  decimals?: number;
15
- /** Abilita/disabilita il separatore delle migliaia. Default: true */
23
+ /** Enable/disable thousands separator grouping. Default: true. */
16
24
  grouping?: boolean;
17
- /** Meccanismo di input alternativo per l'editor delle entità. Default: 'input' */
25
+ /** Alternative input mechanism for the entry editor. Default: 'input'. */
18
26
  control?: 'input' | 'slider' | 'rating' | 'stepper';
19
- /** Valore minimo consentito */
27
+ /** Minimum allowed value. */
20
28
  min?: number;
21
- /** Valore massimo consentito */
29
+ /** Maximum allowed value. */
22
30
  max?: number;
23
- /** Incremento del valore (es. 1 per interi stretti, 0.5 per mezzi passi) */
31
+ /** Value increment (e.g. 1 for strict integers, 0.5 for half-steps). */
24
32
  step?: number;
25
33
  }
26
- /** Configurazioni specializzate per il tipo di branch 'file' */
34
+ /** Specialized configuration for a branch of type 'file'. */
27
35
  export interface FileFieldOptions {
28
36
  /**
29
- * Tipo semantico di file accettato.
30
- * - 'image': immagini renderizzabili come anteprima
37
+ * Semantic type of file accepted.
38
+ * - 'image': renderable images with preview
31
39
  * - 'document': PDF/Office/text
32
- * - 'any': qualsiasi file (default — UI mostra icona generica, nessun tentativo di render immagine)
40
+ * - 'any': any file (default — UI shows a generic icon, no image render attempt)
33
41
  * Default: 'any'.
34
42
  */
35
43
  accept?: FileAccept;
36
44
  /**
37
- * Dimensione massima del singolo file in byte.
38
- * NOTA: il backend /upload applica MAX_FILE_SIZE_BYTES (5MB) globale
39
- * questo campo è informativo per la UI; non viene enforced in upload.
45
+ * Maximum size of a single file, in bytes.
46
+ * NOTE: the backend /upload endpoint enforces the global MAX_FILE_SIZE_BYTES (5MB) —
47
+ * this field is informational for the UI only; it is not enforced on upload.
40
48
  * Default: 5_242_880.
41
49
  */
42
50
  maxSize?: number;
43
51
  }
44
- /** Branch: definizione di una proprietà. alias = nome colonna SQL. */
52
+ /** Branch: definition of a single field. `alias` is the SQL column name. */
45
53
  export interface Branch {
46
54
  /**
47
55
  * Stable logical id of this branch, e.g. 'br_01', 'br_title'.
@@ -53,55 +61,55 @@ export interface Branch {
53
61
  * The Botanical Engine still emits alias as the SQL column name; id is a logical handle.
54
62
  */
55
63
  id: string;
56
- /** Alias human-readable, usato nel payload API e come nome colonna SQL nella tabella dedicata */
64
+ /** Human-readable alias, used in the API payload and as the SQL column name in the dedicated table. */
57
65
  alias: string;
58
- /** Etichetta per la UI */
66
+ /** UI display label. */
59
67
  label: string;
60
- /** Testo di aiuto opzionale mostrato come tooltip accanto al label nella form. UI-only, ignorato dall'engine. */
68
+ /** Optional help text shown as a tooltip next to the label in the form. UI-only, ignored by the engine. */
61
69
  hint?: string;
62
- /** Tipo del valore */
70
+ /** Value type. */
63
71
  type: BranchType;
64
72
  /**
65
- * Variante semantica opzionale del campo per UI/validazione.
66
- * `asset-list` su `file` multiplo abilita la gestione galleria.
73
+ * Optional semantic variant of the field for UI/validation purposes.
74
+ * `asset-list` on a multiple `file` branch enables gallery management.
67
75
  */
68
76
  format?: 'plain' | 'markdown' | 'html' | 'date' | 'datetime' | 'asset-list';
69
77
  /**
70
- * Cardinalità opzionale per campi media:
71
- * - false/undefined: singolo asset (string URL)
72
- * - true: lista asset (string[] URL)
78
+ * Optional cardinality for media fields:
79
+ * - false/undefined: single asset (string URL)
80
+ * - true: asset list (string[] URL)
73
81
  */
74
82
  multiple?: boolean;
75
83
  /**
76
- * Vocabolario predefinito per campi tag/select/multiselect.
77
- * Lista statica definita nel Seed (non salvata nel DB).
84
+ * Predefined vocabulary for tag/select/multiselect fields.
85
+ * Static list defined in the Seed (not persisted to the DB).
78
86
  */
79
87
  options?: string[];
80
- /** Campo obbligatorio in creazione genera NOT NULL in generateCreateTable */
88
+ /** Required on creategenerates NOT NULL in generateCreateTable. */
81
89
  requiredOnCreate?: boolean;
82
- /** Campo obbligatorio in update */
90
+ /** Required on update. */
83
91
  requiredOnUpdate?: boolean;
84
92
  /**
85
- * Policy di accesso e trattamento del campo.
86
- * Tutti i valori sono opzionali — `resolvePolicies(branch)` fornisce i default.
93
+ * Access and handling policy for the field.
94
+ * All values are optional — `resolvePolicies(branch)` supplies the defaults.
87
95
  */
88
96
  policies?: {
89
- /** Come il valore viene memorizzato. Default: 'plain' */
97
+ /** How the value is stored. Default: 'plain'. */
90
98
  privacy?: 'plain' | 'hash' | 'encrypt';
91
- /** Come il valore viene restituito nelle risposte API. Default: 'full' */
99
+ /** How the value is returned in API responses. Default: 'full'. */
92
100
  visibility?: 'full' | 'masked' | 'hidden';
93
- /** Il campo è incluso nelle query di ricerca full-text. Default: true */
101
+ /** Whether the field is included in full-text search queries. Default: true. */
94
102
  search?: boolean;
95
- /** Il campo è disponibile come colonna di filtro nella dashboard. Default: true */
103
+ /** Whether the field is available as a filter column in the dashboard. Default: true. */
96
104
  filter?: boolean;
97
- /** Il campo è disponibile come colonna di ordinamento nella dashboard. Default: true */
105
+ /** Whether the field is available as a sort column in the dashboard. Default: true. */
98
106
  sort?: boolean;
99
- /** Il campo è incluso nelle risposte della Public API. Default: true */
107
+ /** Whether the field is included in Public API responses. Default: true. */
100
108
  public?: boolean;
101
109
  };
102
- /** Opzioni avanzate per i campi numerici. Ignorato se type !== 'number' */
110
+ /** Advanced options for number fields. Ignored if type !== 'number'. */
103
111
  numberOptions?: NumberFieldOptions;
104
- /** Opzioni avanzate per i campi file. Ignorato se type !== 'file' */
112
+ /** Advanced options for file fields. Ignored if type !== 'file'. */
105
113
  fileOptions?: FileFieldOptions;
106
114
  /**
107
115
  * Slug of the referenced Seed (without the `content_` prefix).
@@ -149,13 +157,13 @@ export interface Branch {
149
157
  }
150
158
  /** Dashboard-specific config embedded in a Seed. All fields optional — defaults applied by the dashboard. */
151
159
  export interface DashboardSeedConfig {
152
- /** Lucide icon name (string, resolved to component client-side). Default: 'Folder' */
160
+ /** Lucide icon name (string, resolved to component client-side). Default: 'Folder'. */
153
161
  icon?: string;
154
162
  /** Sidebar group label. Ungrouped seeds share a single 'Contents' section. */
155
163
  group?: string;
156
- /** Sort order within the group. Lower = higher. Default: 99 */
164
+ /** Sort order within the group. Lower = higher. Default: 99. */
157
165
  order?: number;
158
- /** Hide from sidebar navigation. Default: false */
166
+ /** Hide from sidebar navigation. Default: false. */
159
167
  hidden?: boolean;
160
168
  /** Tooltip description shown in the sidebar. */
161
169
  description?: string;
@@ -166,33 +174,39 @@ export interface DashboardSeedConfig {
166
174
  export?: boolean;
167
175
  bulkDelete?: boolean;
168
176
  };
177
+ /**
178
+ * Views authorized for this seed in the content manager. When omitted,
179
+ * the dashboard falls back to DEFAULT_AUTHORIZED_VIEWS. 'table' is always
180
+ * guaranteed at read time by resolveAuthorizedViews (universal fallback).
181
+ */
182
+ views?: DashboardView[];
169
183
  }
170
- /** Seed: definizione dello schema di un tipo di contenuto */
184
+ /** Seed: schema definition of a content type. */
171
185
  export interface Seed {
172
- /** Slug identificativoanche nome tabella: `content_{slug}` */
186
+ /** Identifying slugalso the table name: `content_{slug}`. */
173
187
  slug: string;
174
- /** Etichetta singolare per la UI */
188
+ /** Singular UI label. */
175
189
  label: string;
176
- /** Etichetta plurale per la UI. Se assente si usa `label`. */
190
+ /** Plural UI label. Falls back to `label` when absent. */
177
191
  labelPlural?: string;
178
- /** Abilita lettura dalla Public API (`GET /api/v1/public/:seed`). Default: false */
192
+ /** Enable reads from the Public API (`GET /api/v1/public/:seed`). Default: false. */
179
193
  allowPublicRead?: boolean;
180
- /** Abilita creazione dalla Public API (`POST /api/v1/public/:seed/add`). Default: false */
194
+ /** Enable creation from the Public API (`POST /api/v1/public/:seed/add`). Default: false. */
181
195
  allowPublicPost?: boolean;
182
- /** Abilita modifica dalla Public API (`PUT /api/v1/public/:seed/edit/:id`). Default: false */
196
+ /** Enable edits from the Public API (`PUT /api/v1/public/:seed/edit/:id`). Default: false. */
183
197
  allowPublicEdit?: boolean;
184
198
  /**
185
- * Abilita la feature "bozza in attesa" per questo seed.
186
- * Quando true, genera tabella `content_{slug}_drafts` e abilita gli endpoint `/draft`.
187
- * Default: false
199
+ * Enables the "pending draft" feature for this seed.
200
+ * When true, generates the `content_{slug}_drafts` table and enables the `/draft` endpoints.
201
+ * Default: false.
188
202
  */
189
203
  allowDrafts?: boolean;
190
204
  /**
191
- * Alias del branch usato come nome leggibile dell'entry (es. "title", "name", "author").
192
- * Obbligatoriole UI lo usano per display senza euristica.
205
+ * Alias of the branch used as the entry's human-readable name (e.g. "title", "name", "author").
206
+ * RequiredUIs use it for display without heuristics.
193
207
  */
194
208
  displayNameAlias: string;
195
- /** Lista dei campi (Branch) */
209
+ /** List of fields (Branch). */
196
210
  branches: Branch[];
197
211
  /** Optional dashboard-specific UI config. Ignored by the Botanical Engine. */
198
212
  dashboard?: DashboardSeedConfig;
@@ -201,37 +215,62 @@ export interface Seed {
201
215
  * Type matches FormLayout from seed-layout.ts — kept as unknown here to avoid circular imports. */
202
216
  layout?: unknown;
203
217
  }
218
+ /** Comparison/matching operator applied by a filter condition. */
204
219
  export type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'is_empty' | 'is_not_empty' | 'in' | 'not_in' | 'has_tag' | 'has_any_tag' | 'has_all_tags';
220
+ /** Declared value type of a filtered column, used to normalize/coerce filter values before binding. */
205
221
  export type FilterType = 'text' | 'number' | 'date' | 'boolean' | 'tags' | 'select' | 'system' | 'json';
222
+ /** A single operator + value pair applied to a FilterGroup's column. */
206
223
  export interface FilterCondition {
224
+ /** Operator to apply. */
207
225
  op: FilterOperator;
226
+ /** Comparison value(s). Arrays are only meaningful for `in`/`not_in`/`has_any_tag`/`has_all_tags`. */
208
227
  value: string | number | boolean | null | string[] | number[];
209
228
  }
210
229
  export interface FilterGroup {
211
- /** Nome colonna: system column (id/slug/status/created_at/updated_at) o branch alias */
230
+ /** Column name: system column (id/slug/status/created_at/updated_at) or branch alias. */
212
231
  column: string;
232
+ /** Declared type of the column, used to normalize condition values. */
213
233
  type: FilterType;
234
+ /** Conditions applied to this column, ANDed together. */
214
235
  conditions: FilterCondition[];
215
236
  }
216
237
  export interface SelectOptions {
238
+ /** Filter groups, ANDed together. */
217
239
  filters?: FilterGroup[];
240
+ /** Sort column and direction. Ignored when `kanbanOrder` is set. */
218
241
  orderBy?: {
219
242
  column: string;
220
243
  dir: 'ASC' | 'DESC';
221
244
  };
245
+ /** LIMIT/OFFSET pagination. */
222
246
  pagination?: {
223
247
  limit: number;
224
248
  offset: number;
225
249
  };
226
- /** Filtra per status. null = nessun filtro status. */
250
+ /** Filters by status. null = no status filter. */
227
251
  status?: string | null;
228
- /** Full-text search — usa FTS5 se il seed ha branch richtext indicizzabili */
252
+ /** Full-text search — uses FTS5 if the seed has indexable richtext/text branches. */
229
253
  search?: string;
230
- /** Proiezione colonne. Vuoto = SELECT * */
254
+ /** Column projection. Empty = SELECT *. */
231
255
  fields?: string[];
256
+ /** When set, LEFT JOIN kanban_positions and order by fractional index (KB-S04c/S05).
257
+ * Mutually exclusive with `orderBy`; if both present, `kanbanOrder` wins. */
258
+ kanbanOrder?: {
259
+ seedSlug: string;
260
+ axisBranchId: string;
261
+ };
262
+ /**
263
+ * When true, generates a COUNT(*) query instead of fetching rows.
264
+ * This omits projections, sorting/ordering clauses, and pagination limits/offsets,
265
+ * while keeping the join/where clauses intact for accurate counts.
266
+ */
267
+ isCount?: boolean;
232
268
  }
269
+ /** SQL string paired with its ordered parameter bindings, ready for a D1 `.bind(...)` call. */
233
270
  export interface ParameterizedQuery {
271
+ /** Parameterized SQL statement, using `?` placeholders. */
234
272
  sql: string;
273
+ /** Values bound to the `?` placeholders, in order. */
235
274
  bindings: (string | number | boolean | null)[];
236
275
  }
237
276
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAA;AAE9E,oDAAoD;AACpD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAA;AAEjI,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IAEjC,gDAAgD;IAChD,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAA;IAC1D,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAGlB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;IAGnD,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,4EAA4E;AAC5E,MAAM,WAAW,MAAM;IACrB;;;;;;;;OAQG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,uGAAuG;IACvG,KAAK,EAAE,MAAM,CAAA;IACb,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,2GAA2G;IAC3G,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kBAAkB;IAClB,IAAI,EAAE,UAAU,CAAA;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,CAAA;IAC3E;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,0BAA0B;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,iDAAiD;QACjD,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;QACtC,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;QACzC,gFAAgF;QAChF,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,yFAAyF;QACzF,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,uFAAuF;QACvF,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,4EAA4E;QAC5E,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,CAAA;IACD,wEAAwE;IACxE,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAClC,oEAAoE;IACpE,WAAW,CAAC,EAAE,gBAAgB,CAAA;IAE9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;IAE9C;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IAEjB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,6GAA6G;AAC7G,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB,CAAA;IACD;;;;OAIG;IACH,KAAK,CAAC,EAAE,aAAa,EAAE,CAAA;CACxB;AAED,iDAAiD;AACjD,MAAM,WAAW,IAAI;IACnB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qFAAqF;IACrF,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,6FAA6F;IAC7F,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAA;IACxB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,mBAAmB,CAAA;IAC/B;;wGAEoG;IACpG,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAID,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,UAAU,GACV,cAAc,GACd,aAAa,GACb,WAAW,GACX,UAAU,GACV,cAAc,GACd,IAAI,GACJ,QAAQ,GACR,SAAS,GACT,aAAa,GACb,cAAc,CAAA;AAElB,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEvG,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,yBAAyB;IACzB,EAAE,EAAE,cAAc,CAAA;IAClB,sGAAsG;IACtG,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAAA;CAC9D;AAED,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAA;IACd,uEAAuE;IACvE,IAAI,EAAE,UAAU,CAAA;IAChB,yDAAyD;IACzD,UAAU,EAAE,eAAe,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;IACvB,oEAAoE;IACpE,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAA;IACjD,+BAA+B;IAC/B,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;kFAC8E;IAC9E,WAAW,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAA;IACX,sDAAsD;IACtD,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAA;CAC/C"}
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ import type { Seed } from '../types.js';
3
+ import type { ResolvedOptions } from './index.js';
4
+ /**
5
+ * Compiles or retrieves from cache the full Zod validation schema for a seed structure.
6
+ *
7
+ * @param seed - The seed definition.
8
+ * @param options - The resolved validation options.
9
+ * @returns The compiled strict Zod object schema.
10
+ */
11
+ export declare function compileSeedSchema(seed: Seed, options: ResolvedOptions): z.ZodObject<Record<string, z.ZodTypeAny>>;
12
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/engine/validation/cache.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAsB,IAAI,EAAwC,MAAM,aAAa,CAAA;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAoHjD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CA2BjH"}
@@ -0,0 +1,115 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ import { z } from 'zod';
4
+ import { schemaForBranch } from './schema-builders.js';
5
+ /** Max entries kept in {@link seedSchemaCache} before evicting the least recently used. */
6
+ const SEED_SCHEMA_CACHE_MAX_SIZE = 150;
7
+ /** Cache storing compiled Zod schemas for seeds to optimize validation runs. */
8
+ const seedSchemaCache = new Map();
9
+ /**
10
+ * Retrieves a cache entry and marks it as most recently used.
11
+ *
12
+ * @param key - The cache key.
13
+ * @returns The cached schema, or undefined if absent.
14
+ */
15
+ function getCachedSchema(key) {
16
+ const cached = seedSchemaCache.get(key);
17
+ if (cached) {
18
+ seedSchemaCache.delete(key);
19
+ seedSchemaCache.set(key, cached);
20
+ }
21
+ return cached;
22
+ }
23
+ /**
24
+ * Stores a cache entry, evicting the least recently used one if over capacity.
25
+ *
26
+ * @param key - The cache key.
27
+ * @param value - The compiled schema to cache.
28
+ */
29
+ function setCachedSchema(key, value) {
30
+ if (seedSchemaCache.size >= SEED_SCHEMA_CACHE_MAX_SIZE) {
31
+ const oldestKey = seedSchemaCache.keys().next().value;
32
+ if (oldestKey !== undefined)
33
+ seedSchemaCache.delete(oldestKey);
34
+ }
35
+ seedSchemaCache.set(key, value);
36
+ }
37
+ /**
38
+ * Recursively generates a typesafe fingerprint object for a branch and its sub-branches.
39
+ *
40
+ * @param branch - The branch definition.
41
+ * @returns The structured branch fingerprint object.
42
+ */
43
+ function buildBranchFingerprint(branch) {
44
+ return {
45
+ a: branch.alias,
46
+ t: branch.type,
47
+ f: branch.format ?? null,
48
+ m: branch.multiple === true,
49
+ rc: branch.requiredOnCreate === true,
50
+ ru: branch.requiredOnUpdate === true,
51
+ n: branch.numberOptions ?? null,
52
+ fi: branch.fileOptions ?? null,
53
+ mi: branch.minItems ?? null,
54
+ ma: branch.maxItems ?? null,
55
+ sub: branch.fields?.map(buildBranchFingerprint) ?? null,
56
+ };
57
+ }
58
+ /**
59
+ * Generates a unique JSON fingerprint string representing a seed's structure for caching.
60
+ *
61
+ * @param seed - The seed definition.
62
+ * @returns The fingerprint string.
63
+ */
64
+ function buildSeedFingerprint(seed) {
65
+ const parts = seed.branches.map(buildBranchFingerprint);
66
+ return JSON.stringify({ s: seed.slug, b: parts });
67
+ }
68
+ /**
69
+ * Builds a cache key for a seed validation schema combining its fingerprint, operation, and options.
70
+ *
71
+ * @param seed - The seed definition.
72
+ * @param options - The resolved validation options.
73
+ * @returns The cache key string.
74
+ */
75
+ function buildCacheKey(seed, options) {
76
+ return [
77
+ buildSeedFingerprint(seed),
78
+ options.operation,
79
+ options.allowNull ? '1' : '0',
80
+ options.enforceRequiredFields ? '1' : '0',
81
+ String(options.maxTextLength),
82
+ ].join('|');
83
+ }
84
+ /**
85
+ * Compiles or retrieves from cache the full Zod validation schema for a seed structure.
86
+ *
87
+ * @param seed - The seed definition.
88
+ * @param options - The resolved validation options.
89
+ * @returns The compiled strict Zod object schema.
90
+ */
91
+ export function compileSeedSchema(seed, options) {
92
+ // Seeds with relation branches cannot be safely cached because the schema
93
+ // captures the idGenerator by closure, and different generators (e.g.
94
+ // SystemIdGenerator vs SequentialIdGenerator) have different isValid() semantics.
95
+ const hasRelation = seed.branches.some((b) => b.type === 'relation');
96
+ if (!hasRelation) {
97
+ const key = buildCacheKey(seed, options);
98
+ const cached = getCachedSchema(key);
99
+ if (cached)
100
+ return cached;
101
+ }
102
+ const requiredFlag = options.operation === 'create' ? 'requiredOnCreate' : 'requiredOnUpdate';
103
+ const shape = {};
104
+ for (const branch of seed.branches) {
105
+ const branchSchema = schemaForBranch(branch, options);
106
+ const isRequired = branch[requiredFlag] && options.enforceRequiredFields;
107
+ shape[branch.alias] = isRequired ? branchSchema : branchSchema.optional();
108
+ }
109
+ const compiled = z.object(shape).strict();
110
+ if (!hasRelation) {
111
+ const key = buildCacheKey(seed, options);
112
+ setCachedSchema(key, compiled);
113
+ }
114
+ return compiled;
115
+ }
@@ -0,0 +1,48 @@
1
+ import type { Branch } from '../types.js';
2
+ import type { FileAccept } from '../../media/file-types.js';
3
+ /**
4
+ * Parses a candidate value into a valid HTTP or HTTPS URL string, returning null if invalid.
5
+ *
6
+ * @param input - The value to validate.
7
+ * @returns The cleaned URL string, or null if invalid.
8
+ */
9
+ export declare function parseHttpUrl(input: unknown): string | null;
10
+ /**
11
+ * Resolves the file validation options for a branch, applying default values.
12
+ *
13
+ * @param branch - The file branch containing option overrides.
14
+ * @returns An object with resolved file acceptance rules and maximum size limit.
15
+ */
16
+ export declare function resolveFileOptions(branch: Branch): {
17
+ accept: FileAccept;
18
+ maxSize: number;
19
+ };
20
+ /**
21
+ * Checks if a branch acts as an asset list (file type with multiple upload allowed or asset-list format).
22
+ *
23
+ * @param branch - The branch to check.
24
+ * @returns True if it is an asset list, false otherwise.
25
+ */
26
+ export declare function isAssetListBranch(branch: Branch): boolean;
27
+ /** A file URL paired with its byte size, when known from payload metadata. */
28
+ export interface FileCandidate {
29
+ url: string;
30
+ size: number | null;
31
+ }
32
+ /**
33
+ * Extracts a URL and, when available, a byte size from a candidate value
34
+ * (a string URL, or an object with `url`/`size` fields, e.g. uploader output).
35
+ *
36
+ * @param candidate - The candidate value.
37
+ * @returns The extracted file candidate, or null if no valid URL was found.
38
+ */
39
+ export declare function extractFileCandidate(candidate: unknown): FileCandidate | null;
40
+ /**
41
+ * Collects and deduplicates valid file candidates (url + optional size) from a raw
42
+ * input representing an asset list.
43
+ *
44
+ * @param raw - The raw input data (can be array, JSON string, or single item).
45
+ * @returns The list of unique file candidates, or null if any item is invalid.
46
+ */
47
+ export declare function collectAssetListItems(raw: unknown): FileCandidate[] | null;
48
+ //# sourceMappingURL=file-branch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-branch.d.ts","sourceRoot":"","sources":["../../../src/engine/validation/file-branch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAoB3D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAW1D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAM1F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AA6BD,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAI7E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,EAAE,GAAG,IAAI,CAe1E"}
@@ -0,0 +1,127 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ import { cleanString, isPlainObject } from './primitives.js';
4
+ /** Default maximum file size in bytes for file branches. */
5
+ const DEFAULT_FILE_MAX_SIZE = 5 * 1024 * 1024;
6
+ /**
7
+ * Safely parses a JSON string, returning the parsed value, or the original string if parsing fails.
8
+ *
9
+ * @param raw - The string to attempt parsing.
10
+ * @returns The parsed object/array, or the original string.
11
+ */
12
+ function tryParseJson(raw) {
13
+ try {
14
+ return JSON.parse(raw);
15
+ }
16
+ catch {
17
+ return raw;
18
+ }
19
+ }
20
+ /**
21
+ * Parses a candidate value into a valid HTTP or HTTPS URL string, returning null if invalid.
22
+ *
23
+ * @param input - The value to validate.
24
+ * @returns The cleaned URL string, or null if invalid.
25
+ */
26
+ export function parseHttpUrl(input) {
27
+ if (typeof input !== 'string')
28
+ return null;
29
+ const cleaned = cleanString(input);
30
+ if (cleaned.length === 0)
31
+ return null;
32
+ let parsed;
33
+ try {
34
+ parsed = new URL(cleaned);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ return (parsed.protocol === 'http:' || parsed.protocol === 'https:') ? cleaned : null;
40
+ }
41
+ /**
42
+ * Resolves the file validation options for a branch, applying default values.
43
+ *
44
+ * @param branch - The file branch containing option overrides.
45
+ * @returns An object with resolved file acceptance rules and maximum size limit.
46
+ */
47
+ export function resolveFileOptions(branch) {
48
+ const opts = branch.fileOptions;
49
+ return {
50
+ accept: opts?.accept ?? 'any',
51
+ maxSize: opts?.maxSize ?? DEFAULT_FILE_MAX_SIZE,
52
+ };
53
+ }
54
+ /**
55
+ * Checks if a branch acts as an asset list (file type with multiple upload allowed or asset-list format).
56
+ *
57
+ * @param branch - The branch to check.
58
+ * @returns True if it is an asset list, false otherwise.
59
+ */
60
+ export function isAssetListBranch(branch) {
61
+ return branch.type === 'file' && (branch.multiple === true || branch.format === 'asset-list');
62
+ }
63
+ /**
64
+ * Extracts a URL string from a candidate value (which can be a string URL or an object containing a `url` field).
65
+ *
66
+ * @param candidate - The candidate value.
67
+ * @returns The extracted URL string, or null if not found.
68
+ */
69
+ function extractUrlFromCandidate(candidate) {
70
+ const direct = parseHttpUrl(candidate);
71
+ if (direct)
72
+ return direct;
73
+ if (isPlainObject(candidate)) {
74
+ return parseHttpUrl(candidate.url);
75
+ }
76
+ return null;
77
+ }
78
+ /**
79
+ * Extracts a byte size from a candidate value's `size` field, if present and valid.
80
+ *
81
+ * @param candidate - The candidate value.
82
+ * @returns The size in bytes, or null if not available.
83
+ */
84
+ function extractSizeFromCandidate(candidate) {
85
+ if (!isPlainObject(candidate))
86
+ return null;
87
+ const raw = candidate.size;
88
+ return typeof raw === 'number' && Number.isFinite(raw) && raw >= 0 ? raw : null;
89
+ }
90
+ /**
91
+ * Extracts a URL and, when available, a byte size from a candidate value
92
+ * (a string URL, or an object with `url`/`size` fields, e.g. uploader output).
93
+ *
94
+ * @param candidate - The candidate value.
95
+ * @returns The extracted file candidate, or null if no valid URL was found.
96
+ */
97
+ export function extractFileCandidate(candidate) {
98
+ const url = extractUrlFromCandidate(candidate);
99
+ if (!url)
100
+ return null;
101
+ return { url, size: extractSizeFromCandidate(candidate) };
102
+ }
103
+ /**
104
+ * Collects and deduplicates valid file candidates (url + optional size) from a raw
105
+ * input representing an asset list.
106
+ *
107
+ * @param raw - The raw input data (can be array, JSON string, or single item).
108
+ * @returns The list of unique file candidates, or null if any item is invalid.
109
+ */
110
+ export function collectAssetListItems(raw) {
111
+ const source = typeof raw === 'string' ? tryParseJson(raw) : raw;
112
+ const items = Array.isArray(source) ? source : [source];
113
+ const seen = new Set();
114
+ const out = [];
115
+ for (const item of items) {
116
+ if (item === null || item === undefined)
117
+ continue;
118
+ const candidate = extractFileCandidate(item);
119
+ if (!candidate)
120
+ return null;
121
+ if (!seen.has(candidate.url)) {
122
+ seen.add(candidate.url);
123
+ out.push(candidate);
124
+ }
125
+ }
126
+ return out;
127
+ }