@beechcms/core 0.4.0-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/define-seed.d.ts +3 -0
- package/dist/define-seed.d.ts.map +1 -0
- package/dist/define-seed.js +3 -0
- package/dist/engine.d.ts +68 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +401 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/policies.d.ts +10 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +28 -0
- package/dist/richtext-render.d.ts +11 -0
- package/dist/richtext-render.d.ts.map +1 -0
- package/dist/richtext-render.js +85 -0
- package/dist/richtext.d.ts +11 -0
- package/dist/richtext.d.ts.map +1 -0
- package/dist/richtext.js +11 -0
- package/dist/seeds.d.ts +36 -0
- package/dist/seeds.d.ts.map +1 -0
- package/dist/seeds.js +179 -0
- package/dist/slug-utils.d.ts +18 -0
- package/dist/slug-utils.d.ts.map +1 -0
- package/dist/slug-utils.js +29 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +44 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +571 -0
- package/package.json +35 -0
- package/src/define-seed.ts +5 -0
- package/src/engine.ts +465 -0
- package/src/index.ts +19 -0
- package/src/policies.test.ts +127 -0
- package/src/policies.ts +32 -0
- package/src/richtext-render.ts +87 -0
- package/src/richtext.ts +16 -0
- package/src/seeds.ts +194 -0
- package/src/slug-utils.ts +33 -0
- package/src/types.ts +121 -0
- package/src/validation.ts +667 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenzione storage richtext TipTap nel Content Engine.
|
|
3
|
+
* @see docs/Sprints/tiptap-elevation.md
|
|
4
|
+
*/
|
|
5
|
+
export declare const RICHTEXT_SCHEMA_VERSION: 1;
|
|
6
|
+
export type RichtextEnvelopeV1 = {
|
|
7
|
+
schemaVersion: typeof RICHTEXT_SCHEMA_VERSION;
|
|
8
|
+
doc: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export declare function isRichtextEnvelopeV1(value: unknown): value is RichtextEnvelopeV1;
|
|
11
|
+
//# sourceMappingURL=richtext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"richtext.d.ts","sourceRoot":"","sources":["../src/richtext.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAG,CAAU,CAAA;AAEjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,OAAO,uBAAuB,CAAA;IAC7C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAIhF"}
|
package/dist/richtext.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenzione storage richtext TipTap nel Content Engine.
|
|
3
|
+
* @see docs/Sprints/tiptap-elevation.md
|
|
4
|
+
*/
|
|
5
|
+
export const RICHTEXT_SCHEMA_VERSION = 1;
|
|
6
|
+
export function isRichtextEnvelopeV1(value) {
|
|
7
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
8
|
+
return false;
|
|
9
|
+
const o = value;
|
|
10
|
+
return o.schemaVersion === RICHTEXT_SCHEMA_VERSION && typeof o.doc === 'object' && o.doc !== null;
|
|
11
|
+
}
|
package/dist/seeds.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Registry: configurazione degli schemi di contenuto.
|
|
3
|
+
* In v0.4.0 ogni Seed genera una tabella `content_{slug}` con colonne reali.
|
|
4
|
+
* `branch.alias` è il nome della colonna SQL.
|
|
5
|
+
*/
|
|
6
|
+
import type { Seed } from './types.js';
|
|
7
|
+
/** Articolo: contenuto editoriale con richtext, immagine e SEO */
|
|
8
|
+
export declare const ARTICOLO_SEED: Seed;
|
|
9
|
+
/** Prodotto: scheda prodotto e-commerce con richtext, galleria e SEO */
|
|
10
|
+
export declare const PRODOTTO_SEED: Seed;
|
|
11
|
+
/** Membro del team: profilo con foto e link social */
|
|
12
|
+
export declare const TEAM_SEED: Seed;
|
|
13
|
+
/** Testimonianza: recensione cliente con valutazione e foto */
|
|
14
|
+
export declare const TESTIMONIANZA_SEED: Seed;
|
|
15
|
+
/** Pagina: pagina statica con richtext, immagine hero e SEO */
|
|
16
|
+
export declare const PAGINA_SEED: Seed;
|
|
17
|
+
/**
|
|
18
|
+
* Cliente: dimostra tutte le assi di Branch.policies.
|
|
19
|
+
*
|
|
20
|
+
* email → visibility:masked, search:false, public:false
|
|
21
|
+
* passwordHash → privacy:hash, visibility:hidden, search/filter/sort/public: false
|
|
22
|
+
* phone → visibility:masked, filter:false, public:false
|
|
23
|
+
* internalNote → visibility:hidden, search:false, public:false
|
|
24
|
+
* registeredAt → public:false (data interna, non esposta)
|
|
25
|
+
*/
|
|
26
|
+
export declare const CLIENTE_SEED: Seed;
|
|
27
|
+
/**
|
|
28
|
+
* TODO: Rimuovere al termine dei test del testsite
|
|
29
|
+
* Messaggio di contatto: per form contatti esterni
|
|
30
|
+
*/
|
|
31
|
+
export declare const MESSAGGI_SEED: Seed;
|
|
32
|
+
/** Registro: slug -> Seed */
|
|
33
|
+
export declare const SEED_REGISTRY: Record<string, Seed>;
|
|
34
|
+
/** Ritorna il Seed per lo slug dato, o null se non esiste. */
|
|
35
|
+
export declare function getSeed(slug: string): Seed | null;
|
|
36
|
+
//# sourceMappingURL=seeds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeds.d.ts","sourceRoot":"","sources":["../src/seeds.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,kEAAkE;AAClE,eAAO,MAAM,aAAa,EAAE,IAgB3B,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,aAAa,EAAE,IAiB3B,CAAA;AAED,sDAAsD;AACtD,eAAO,MAAM,SAAS,EAAE,IAgBvB,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,EAAE,IAiBhC,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,IAczB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,IA8C1B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,IAc3B,CAAA;AAED,6BAA6B;AAC7B,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAQ9C,CAAA;AAED,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEjD"}
|
package/dist/seeds.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/** Articolo: contenuto editoriale con richtext, immagine e SEO */
|
|
2
|
+
export const ARTICOLO_SEED = {
|
|
3
|
+
slug: 'articoli',
|
|
4
|
+
label: 'Articolo',
|
|
5
|
+
labelPlural: 'Articoli',
|
|
6
|
+
displayNameAlias: 'title',
|
|
7
|
+
allowPublicRead: true,
|
|
8
|
+
allowDrafts: true,
|
|
9
|
+
branches: [
|
|
10
|
+
{ alias: 'title', label: 'Titolo', type: 'text' },
|
|
11
|
+
{ alias: 'publishedAt', label: 'Data pubblicazione', type: 'date' },
|
|
12
|
+
{ alias: 'coverImage', label: 'Immagine copertina', type: 'file' },
|
|
13
|
+
{ alias: 'tags', label: 'Tag', type: 'json', options: ['cms', 'tutorial', 'release', 'annuncio', 'guida', 'news', 'aggiornamento'] },
|
|
14
|
+
{ alias: 'body', label: 'Corpo articolo', type: 'richtext' },
|
|
15
|
+
{ alias: 'metaTitle', label: 'Meta titolo (SEO)', type: 'text' },
|
|
16
|
+
{ alias: 'metaDescription', label: 'Meta descrizione (SEO)', type: 'text' },
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
/** Prodotto: scheda prodotto e-commerce con richtext, galleria e SEO */
|
|
20
|
+
export const PRODOTTO_SEED = {
|
|
21
|
+
slug: 'prodotti',
|
|
22
|
+
label: 'Prodotto',
|
|
23
|
+
labelPlural: 'Prodotti',
|
|
24
|
+
displayNameAlias: 'name',
|
|
25
|
+
allowPublicRead: true,
|
|
26
|
+
branches: [
|
|
27
|
+
{ alias: 'name', label: 'Nome', type: 'text' },
|
|
28
|
+
{ alias: 'price', label: 'Prezzo (€)', type: 'number' },
|
|
29
|
+
{ alias: 'stock', label: 'Quantità disponibile', type: 'number' },
|
|
30
|
+
{ alias: 'active', label: 'In vendita', type: 'boolean' },
|
|
31
|
+
{ alias: 'coverImage', label: 'Immagine principale', type: 'file' },
|
|
32
|
+
{ alias: 'images', label: 'Galleria immagini', type: 'file', multiple: true, format: 'asset-list' },
|
|
33
|
+
{ alias: 'description', label: 'Descrizione', type: 'richtext' },
|
|
34
|
+
{ alias: 'metaTitle', label: 'Meta titolo (SEO)', type: 'text' },
|
|
35
|
+
{ alias: 'metaDescription', label: 'Meta descrizione (SEO)', type: 'text' },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
/** Membro del team: profilo con foto e link social */
|
|
39
|
+
export const TEAM_SEED = {
|
|
40
|
+
slug: 'team',
|
|
41
|
+
label: 'Membro',
|
|
42
|
+
labelPlural: 'Team',
|
|
43
|
+
displayNameAlias: 'name',
|
|
44
|
+
allowPublicRead: true,
|
|
45
|
+
branches: [
|
|
46
|
+
{ alias: 'name', label: 'Nome', type: 'text' },
|
|
47
|
+
{ alias: 'role', label: 'Ruolo', type: 'text' },
|
|
48
|
+
{ alias: 'bio', label: 'Bio breve', type: 'text' },
|
|
49
|
+
{ alias: 'photo', label: 'Foto', type: 'file' },
|
|
50
|
+
{ alias: 'linkedIn', label: 'URL LinkedIn', type: 'text' },
|
|
51
|
+
{ alias: 'active', label: 'Visibile', type: 'boolean' },
|
|
52
|
+
{ alias: 'metaTitle', label: 'Meta titolo (SEO)', type: 'text' },
|
|
53
|
+
{ alias: 'metaDescription', label: 'Meta descrizione (SEO)', type: 'text' },
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
/** Testimonianza: recensione cliente con valutazione e foto */
|
|
57
|
+
export const TESTIMONIANZA_SEED = {
|
|
58
|
+
slug: 'testimonianze',
|
|
59
|
+
label: 'Testimonianza',
|
|
60
|
+
labelPlural: 'Testimonianze',
|
|
61
|
+
displayNameAlias: 'author',
|
|
62
|
+
allowPublicRead: true,
|
|
63
|
+
branches: [
|
|
64
|
+
{ alias: 'author', label: 'Autore', type: 'text' },
|
|
65
|
+
{ alias: 'company', label: 'Azienda', type: 'text' },
|
|
66
|
+
{ alias: 'quote', label: 'Citazione', type: 'text' },
|
|
67
|
+
{ alias: 'rating', label: 'Valutazione (1-5)', type: 'number' },
|
|
68
|
+
{ alias: 'date', label: 'Data', type: 'date' },
|
|
69
|
+
{ alias: 'photo', label: 'Foto autore', type: 'file' },
|
|
70
|
+
{ alias: 'active', label: 'Pubblica', type: 'boolean' },
|
|
71
|
+
{ alias: 'metaTitle', label: 'Meta titolo (SEO)', type: 'text' },
|
|
72
|
+
{ alias: 'metaDescription', label: 'Meta descrizione (SEO)', type: 'text' },
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
/** Pagina: pagina statica con richtext, immagine hero e SEO */
|
|
76
|
+
export const PAGINA_SEED = {
|
|
77
|
+
slug: 'pagine',
|
|
78
|
+
label: 'Pagina',
|
|
79
|
+
labelPlural: 'Pagine',
|
|
80
|
+
displayNameAlias: 'title',
|
|
81
|
+
allowPublicRead: true,
|
|
82
|
+
allowDrafts: true,
|
|
83
|
+
branches: [
|
|
84
|
+
{ alias: 'title', label: 'Titolo', type: 'text' },
|
|
85
|
+
{ alias: 'coverImage', label: 'Immagine hero', type: 'file' },
|
|
86
|
+
{ alias: 'body', label: 'Contenuto', type: 'richtext' },
|
|
87
|
+
{ alias: 'metaTitle', label: 'Meta titolo (SEO)', type: 'text' },
|
|
88
|
+
{ alias: 'metaDescription', label: 'Meta descrizione (SEO)', type: 'text' },
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Cliente: dimostra tutte le assi di Branch.policies.
|
|
93
|
+
*
|
|
94
|
+
* email → visibility:masked, search:false, public:false
|
|
95
|
+
* passwordHash → privacy:hash, visibility:hidden, search/filter/sort/public: false
|
|
96
|
+
* phone → visibility:masked, filter:false, public:false
|
|
97
|
+
* internalNote → visibility:hidden, search:false, public:false
|
|
98
|
+
* registeredAt → public:false (data interna, non esposta)
|
|
99
|
+
*/
|
|
100
|
+
export const CLIENTE_SEED = {
|
|
101
|
+
slug: 'clienti',
|
|
102
|
+
label: 'Cliente',
|
|
103
|
+
labelPlural: 'Clienti',
|
|
104
|
+
displayNameAlias: 'name',
|
|
105
|
+
allowPublicRead: true,
|
|
106
|
+
branches: [
|
|
107
|
+
{ alias: 'name', label: 'Nome', type: 'text' },
|
|
108
|
+
{
|
|
109
|
+
alias: 'email',
|
|
110
|
+
label: 'Email',
|
|
111
|
+
type: 'text',
|
|
112
|
+
policies: { visibility: 'masked', search: false, public: false },
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
alias: 'passwordHash',
|
|
116
|
+
label: 'Password (hash)',
|
|
117
|
+
type: 'text',
|
|
118
|
+
policies: { privacy: 'hash', visibility: 'hidden', search: false, filter: false, sort: false, public: false },
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
alias: 'tier',
|
|
122
|
+
label: 'Piano',
|
|
123
|
+
type: 'text',
|
|
124
|
+
options: ['free', 'starter', 'pro', 'enterprise'],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
alias: 'phone',
|
|
128
|
+
label: 'Telefono',
|
|
129
|
+
type: 'text',
|
|
130
|
+
policies: { visibility: 'masked', filter: false, public: false },
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
alias: 'internalNote',
|
|
134
|
+
label: 'Note interne',
|
|
135
|
+
type: 'text',
|
|
136
|
+
policies: { visibility: 'hidden', search: false, public: false },
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
alias: 'registeredAt',
|
|
140
|
+
label: 'Data iscrizione',
|
|
141
|
+
type: 'date',
|
|
142
|
+
policies: { public: false },
|
|
143
|
+
},
|
|
144
|
+
{ alias: 'active', label: 'Attivo', type: 'boolean' },
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* TODO: Rimuovere al termine dei test del testsite
|
|
149
|
+
* Messaggio di contatto: per form contatti esterni
|
|
150
|
+
*/
|
|
151
|
+
export const MESSAGGI_SEED = {
|
|
152
|
+
slug: 'messaggi',
|
|
153
|
+
label: 'Messaggio',
|
|
154
|
+
labelPlural: 'Messaggi',
|
|
155
|
+
displayNameAlias: 'name',
|
|
156
|
+
allowPublicPost: true,
|
|
157
|
+
allowPublicEdit: true,
|
|
158
|
+
branches: [
|
|
159
|
+
{ alias: 'name', label: 'Nome mittente', type: 'text', requiredOnCreate: true },
|
|
160
|
+
{ alias: 'email', label: 'Email mittente', type: 'text', requiredOnCreate: true },
|
|
161
|
+
{ alias: 'subject', label: 'Oggetto', type: 'text', requiredOnCreate: true },
|
|
162
|
+
{ alias: 'message', label: 'Messaggio', type: 'richtext', requiredOnCreate: true },
|
|
163
|
+
{ alias: 'read', label: 'Letto', type: 'boolean' },
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
/** Registro: slug -> Seed */
|
|
167
|
+
export const SEED_REGISTRY = {
|
|
168
|
+
articoli: ARTICOLO_SEED,
|
|
169
|
+
prodotti: PRODOTTO_SEED,
|
|
170
|
+
team: TEAM_SEED,
|
|
171
|
+
testimonianze: TESTIMONIANZA_SEED,
|
|
172
|
+
pagine: PAGINA_SEED,
|
|
173
|
+
clienti: CLIENTE_SEED,
|
|
174
|
+
messaggi: MESSAGGI_SEED,
|
|
175
|
+
};
|
|
176
|
+
/** Ritorna il Seed per lo slug dato, o null se non esiste. */
|
|
177
|
+
export function getSeed(slug) {
|
|
178
|
+
return SEED_REGISTRY[slug] ?? null;
|
|
179
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility per la gestione degli slug.
|
|
3
|
+
* Condivisa tra Dashboard e API pubbliche.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Converte una stringa in uno slug URL-safe.
|
|
7
|
+
* Limita la lunghezza a 15 caratteri come richiesto.
|
|
8
|
+
*/
|
|
9
|
+
export declare function slugify(value: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Genera uno slug da un input (es. titolo o nome) o un fallback UUID.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateEntrySlug(input: {
|
|
14
|
+
slug?: string;
|
|
15
|
+
title?: unknown;
|
|
16
|
+
name?: unknown;
|
|
17
|
+
}): string;
|
|
18
|
+
//# sourceMappingURL=slug-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug-utils.d.ts","sourceRoot":"","sources":["../src/slug-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS7C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CASnG"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility per la gestione degli slug.
|
|
3
|
+
* Condivisa tra Dashboard e API pubbliche.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Converte una stringa in uno slug URL-safe.
|
|
7
|
+
* Limita la lunghezza a 15 caratteri come richiesto.
|
|
8
|
+
*/
|
|
9
|
+
export function slugify(value) {
|
|
10
|
+
return value
|
|
11
|
+
.normalize('NFKD') // Normalizza caratteri accentati
|
|
12
|
+
.replace(/[^\w\s-]/g, '') // Rimuove tutto ciò che non è alfanumerico (accetta underscore temporaneamente)
|
|
13
|
+
.trim()
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.replace(/[\s_-]+/g, '-') // Sostituisce spazi, underscore e trattini multipli con singolo trattino
|
|
16
|
+
.replace(/^-+|-+$/g, '') // Rimuove trattini all'inizio o alla fine
|
|
17
|
+
.slice(0, 15); // Limita a 15 caratteri
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Genera uno slug da un input (es. titolo o nome) o un fallback UUID.
|
|
21
|
+
*/
|
|
22
|
+
export function generateEntrySlug(input) {
|
|
23
|
+
const candidate = (typeof input.slug === 'string' && input.slug) ||
|
|
24
|
+
(typeof input.title === 'string' && input.title) ||
|
|
25
|
+
(typeof input.name === 'string' && input.name) ||
|
|
26
|
+
Math.random().toString(36).slice(2, 10); // Fallback compatto se crypto non disponibile (o slice 8)
|
|
27
|
+
const normalized = slugify(candidate);
|
|
28
|
+
return normalized || Math.random().toString(36).slice(2, 10);
|
|
29
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export type BranchType = 'text' | 'number' | 'boolean' | 'json' | 'date' | 'richtext' | 'file';
|
|
2
|
+
/** Branch: definizione di una proprietà. alias = nome colonna SQL. */
|
|
3
|
+
export interface Branch {
|
|
4
|
+
/** Alias human-readable, usato nel payload API e come nome colonna SQL nella tabella dedicata */
|
|
5
|
+
alias: string;
|
|
6
|
+
/** Etichetta per la UI */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Tipo del valore */
|
|
9
|
+
type: BranchType;
|
|
10
|
+
/**
|
|
11
|
+
* Variante semantica opzionale del campo per UI/validazione.
|
|
12
|
+
* `asset-list` su `file` multiplo abilita la gestione galleria.
|
|
13
|
+
*/
|
|
14
|
+
format?: 'plain' | 'markdown' | 'html' | 'date' | 'datetime' | 'asset-list';
|
|
15
|
+
/**
|
|
16
|
+
* Cardinalità opzionale per campi media:
|
|
17
|
+
* - false/undefined: singolo asset (string URL)
|
|
18
|
+
* - true: lista asset (string[] URL)
|
|
19
|
+
*/
|
|
20
|
+
multiple?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Vocabolario predefinito per campi tag/select/multiselect.
|
|
23
|
+
* Lista statica definita nel Seed (non salvata nel DB).
|
|
24
|
+
*/
|
|
25
|
+
options?: string[];
|
|
26
|
+
/** Campo obbligatorio in creazione — genera NOT NULL in generateCreateTable */
|
|
27
|
+
requiredOnCreate?: boolean;
|
|
28
|
+
/** Campo obbligatorio in update */
|
|
29
|
+
requiredOnUpdate?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Policy di accesso e trattamento del campo.
|
|
32
|
+
* Tutti i valori sono opzionali — `resolvePolicies(branch)` fornisce i default.
|
|
33
|
+
*/
|
|
34
|
+
policies?: {
|
|
35
|
+
/** Come il valore viene memorizzato. Default: 'plain' */
|
|
36
|
+
privacy?: 'plain' | 'hash' | 'encrypt';
|
|
37
|
+
/** Come il valore viene restituito nelle risposte API. Default: 'full' */
|
|
38
|
+
visibility?: 'full' | 'masked' | 'hidden';
|
|
39
|
+
/** Il campo è incluso nelle query di ricerca full-text. Default: true */
|
|
40
|
+
search?: boolean;
|
|
41
|
+
/** Il campo è disponibile come colonna di filtro nella dashboard. Default: true */
|
|
42
|
+
filter?: boolean;
|
|
43
|
+
/** Il campo è disponibile come colonna di ordinamento nella dashboard. Default: true */
|
|
44
|
+
sort?: boolean;
|
|
45
|
+
/** Il campo è incluso nelle risposte della Public API. Default: true */
|
|
46
|
+
public?: boolean;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Seed: definizione dello schema di un tipo di contenuto */
|
|
50
|
+
export interface Seed {
|
|
51
|
+
/** Slug identificativo — anche nome tabella: `content_{slug}` */
|
|
52
|
+
slug: string;
|
|
53
|
+
/** Etichetta singolare per la UI */
|
|
54
|
+
label: string;
|
|
55
|
+
/** Etichetta plurale per la UI. Se assente si usa `label`. */
|
|
56
|
+
labelPlural?: string;
|
|
57
|
+
/** Abilita lettura dalla Public API (`GET /api/v1/public/:seed`). Default: false */
|
|
58
|
+
allowPublicRead?: boolean;
|
|
59
|
+
/** Abilita creazione dalla Public API (`POST /api/v1/public/:seed/add`). Default: false */
|
|
60
|
+
allowPublicPost?: boolean;
|
|
61
|
+
/** Abilita modifica dalla Public API (`PUT /api/v1/public/:seed/edit/:id`). Default: false */
|
|
62
|
+
allowPublicEdit?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Abilita la feature "bozza in attesa" per questo seed.
|
|
65
|
+
* Quando true, genera tabella `content_{slug}_drafts` e abilita gli endpoint `/draft`.
|
|
66
|
+
* Default: false
|
|
67
|
+
*/
|
|
68
|
+
allowDrafts?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Alias del branch usato come nome leggibile dell'entry (es. "title", "name", "author").
|
|
71
|
+
* Obbligatorio — le UI lo usano per display senza euristica.
|
|
72
|
+
*/
|
|
73
|
+
displayNameAlias: string;
|
|
74
|
+
/** Lista dei campi (Branch) */
|
|
75
|
+
branches: Branch[];
|
|
76
|
+
}
|
|
77
|
+
export type FilterOperator = 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'is_empty' | 'is_not_empty';
|
|
78
|
+
export type FilterType = 'text' | 'number' | 'date' | 'boolean' | 'tags' | 'select' | 'system';
|
|
79
|
+
export interface FilterCondition {
|
|
80
|
+
op: FilterOperator;
|
|
81
|
+
value: string | number | boolean | null;
|
|
82
|
+
}
|
|
83
|
+
export interface FilterGroup {
|
|
84
|
+
/** Nome colonna: system column (id/slug/status/created_at/updated_at) o branch alias */
|
|
85
|
+
column: string;
|
|
86
|
+
type: FilterType;
|
|
87
|
+
conditions: FilterCondition[];
|
|
88
|
+
}
|
|
89
|
+
export interface SelectOptions {
|
|
90
|
+
filters?: FilterGroup[];
|
|
91
|
+
orderBy?: {
|
|
92
|
+
column: string;
|
|
93
|
+
dir: 'ASC' | 'DESC';
|
|
94
|
+
};
|
|
95
|
+
pagination?: {
|
|
96
|
+
limit: number;
|
|
97
|
+
offset: number;
|
|
98
|
+
};
|
|
99
|
+
/** Filtra per status. null = nessun filtro status. */
|
|
100
|
+
status?: string | null;
|
|
101
|
+
/** Full-text search — usa FTS5 se il seed ha branch richtext indicizzabili */
|
|
102
|
+
search?: string;
|
|
103
|
+
/** Proiezione colonne. Vuoto = SELECT * */
|
|
104
|
+
fields?: string[];
|
|
105
|
+
}
|
|
106
|
+
export interface ParameterizedQuery {
|
|
107
|
+
sql: string;
|
|
108
|
+
bindings: (string | number | boolean | null)[];
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;AAE9F,sEAAsE;AACtE,MAAM,WAAW,MAAM;IACrB,iGAAiG;IACjG,KAAK,EAAE,MAAM,CAAA;IACb,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,sBAAsB;IACtB,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,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,yDAAyD;QACzD,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;QACtC,0EAA0E;QAC1E,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;QACzC,yEAAyE;QACzE,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,mFAAmF;QACnF,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,wFAAwF;QACxF,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,wEAAwE;QACxE,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,CAAA;CACF;AAED,6DAA6D;AAC7D,MAAM,WAAW,IAAI;IACnB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAA;IACZ,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,2FAA2F;IAC3F,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;CACnB;AAID,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,UAAU,GACV,UAAU,GACV,cAAc,CAAA;AAElB,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE9F,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,cAAc,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,UAAU,EAAE,eAAe,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;IACvB,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAA;IACjD,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAA;CAC/C"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Seed } from './types.js';
|
|
2
|
+
export interface ValidationDetail {
|
|
3
|
+
field: string;
|
|
4
|
+
expected: string;
|
|
5
|
+
received: string;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ValidateSeedPayloadOptions {
|
|
9
|
+
allowNull?: boolean;
|
|
10
|
+
operation?: 'create' | 'update';
|
|
11
|
+
requireAtLeastOneValidField?: boolean;
|
|
12
|
+
enforceRequiredFields?: boolean;
|
|
13
|
+
maxTextLength?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ValidateSeedPayloadResult {
|
|
16
|
+
data: Record<string, unknown>;
|
|
17
|
+
details: ValidationDetail[];
|
|
18
|
+
unknownAliases: string[];
|
|
19
|
+
dangerousFields: string[];
|
|
20
|
+
requiredFieldsMissing: string[];
|
|
21
|
+
hasAnyValidField: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Foundation comune per validazione e sanitizzazione payload schema-driven.
|
|
25
|
+
* Usata da Public API e riusabile nel Botanical Engine.
|
|
26
|
+
*
|
|
27
|
+
* Ordine di esecuzione obbligatorio al momento della scrittura:
|
|
28
|
+
* validate raw → hash (privacy policy) → store
|
|
29
|
+
* L'hashing avviene DOPO la validazione, non prima.
|
|
30
|
+
* Questo garantisce che il valore validato sia il valore raw originale,
|
|
31
|
+
* non il digest — evitando false failure su campi con formato (es. email).
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateAndSanitizeSeedPayload(seed: Seed, payload: Record<string, unknown>, options?: ValidateSeedPayloadOptions): ValidateSeedPayloadResult;
|
|
34
|
+
/**
|
|
35
|
+
* Valida status content supportati dal CMS.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isValidContentStatus(value: unknown): value is 'draft' | 'review' | 'published';
|
|
38
|
+
/**
|
|
39
|
+
* Full Zod runtime compiler:
|
|
40
|
+
* - compile per-seed/per-operation with cache
|
|
41
|
+
* - strict unknown aliases
|
|
42
|
+
* - required fields by operation
|
|
43
|
+
*/
|
|
44
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAU,IAAI,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC/B,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAufD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,CA6G3B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,GAAG,QAAQ,GAAG,WAAW,CAE9F;AAED;;;;;GAKG"}
|