@beechcms/core 0.5.0 → 0.6.0-preview.2

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 (141) hide show
  1. package/dist/auth/hash-provider.d.ts.map +1 -1
  2. package/dist/auth/hash-provider.js +2 -0
  3. package/dist/auth/password-reset-token.repository.d.ts.map +1 -1
  4. package/dist/auth/password-reset-token.repository.js +2 -0
  5. package/dist/auth/session.repository.d.ts.map +1 -1
  6. package/dist/auth/session.repository.js +2 -0
  7. package/dist/auth/token-service.d.ts +2 -0
  8. package/dist/auth/token-service.d.ts.map +1 -1
  9. package/dist/auth/token-service.js +2 -0
  10. package/dist/auth/user.repository.d.ts +4 -1
  11. package/dist/auth/user.repository.d.ts.map +1 -1
  12. package/dist/auth/user.repository.js +2 -0
  13. package/dist/automations-grammar-words.d.ts.map +1 -1
  14. package/dist/automations-grammar-words.js +2 -0
  15. package/dist/automations.repository.interface.d.ts.map +1 -1
  16. package/dist/automations.repository.interface.js +2 -0
  17. package/dist/automations.runner.interface.d.ts.map +1 -1
  18. package/dist/automations.runner.interface.js +2 -0
  19. package/dist/automations.runner.stub.d.ts.map +1 -1
  20. package/dist/automations.runner.stub.js +2 -0
  21. package/dist/automations.types.d.ts +2 -0
  22. package/dist/automations.types.d.ts.map +1 -1
  23. package/dist/automations.types.js +2 -0
  24. package/dist/clock.d.ts.map +1 -1
  25. package/dist/clock.js +2 -0
  26. package/dist/content-scan.repository.d.ts.map +1 -1
  27. package/dist/content-scan.repository.js +2 -0
  28. package/dist/content.repository.d.ts +62 -0
  29. package/dist/content.repository.d.ts.map +1 -1
  30. package/dist/content.repository.js +18 -0
  31. package/dist/dashboard-layout.d.ts +154 -0
  32. package/dist/dashboard-layout.d.ts.map +1 -0
  33. package/dist/dashboard-layout.js +186 -0
  34. package/dist/dashboard-layout.repository.d.ts +18 -0
  35. package/dist/dashboard-layout.repository.d.ts.map +1 -0
  36. package/dist/dashboard-layout.repository.js +3 -0
  37. package/dist/dashboard-permissions.d.ts +3 -0
  38. package/dist/dashboard-permissions.d.ts.map +1 -0
  39. package/dist/dashboard-permissions.js +11 -0
  40. package/dist/dashboard-scopes.d.ts +12 -0
  41. package/dist/dashboard-scopes.d.ts.map +1 -0
  42. package/dist/dashboard-scopes.js +30 -0
  43. package/dist/define-seed.d.ts.map +1 -1
  44. package/dist/define-seed.js +2 -0
  45. package/dist/demo-data.repository.d.ts +5 -0
  46. package/dist/demo-data.repository.d.ts.map +1 -0
  47. package/dist/demo-data.repository.js +3 -0
  48. package/dist/engine.d.ts +76 -0
  49. package/dist/engine.d.ts.map +1 -1
  50. package/dist/engine.js +259 -5
  51. package/dist/file-types.d.ts +13 -0
  52. package/dist/file-types.d.ts.map +1 -0
  53. package/dist/file-types.js +85 -0
  54. package/dist/id-generator.d.ts +7 -0
  55. package/dist/id-generator.d.ts.map +1 -1
  56. package/dist/id-generator.js +5 -0
  57. package/dist/idempotency.repository.d.ts.map +1 -1
  58. package/dist/idempotency.repository.js +2 -0
  59. package/dist/index.d.ts +16 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +18 -1
  62. package/dist/layout-permissions.d.ts +3 -0
  63. package/dist/layout-permissions.d.ts.map +1 -0
  64. package/dist/layout-permissions.js +11 -0
  65. package/dist/media.repository.d.ts.map +1 -1
  66. package/dist/media.repository.js +2 -0
  67. package/dist/notifications/notification-service.d.ts.map +1 -1
  68. package/dist/notifications/notification-service.js +2 -0
  69. package/dist/notifications/notification.repository.d.ts.map +1 -1
  70. package/dist/notifications/notification.repository.js +2 -9
  71. package/dist/observability/activity-log.repository.d.ts.map +1 -1
  72. package/dist/observability/activity-log.repository.js +2 -0
  73. package/dist/observability/activity-logger.d.ts +2 -2
  74. package/dist/observability/activity-logger.d.ts.map +1 -1
  75. package/dist/observability/activity-logger.js +2 -11
  76. package/dist/observability/analytics.repository.d.ts.map +1 -1
  77. package/dist/observability/analytics.repository.js +2 -0
  78. package/dist/policies.d.ts.map +1 -1
  79. package/dist/policies.js +8 -3
  80. package/dist/rate-limit/rate-limiter.d.ts.map +1 -1
  81. package/dist/rate-limit/rate-limiter.js +2 -0
  82. package/dist/relations.d.ts +28 -0
  83. package/dist/relations.d.ts.map +1 -0
  84. package/dist/relations.js +25 -0
  85. package/dist/richtext-render.d.ts.map +1 -1
  86. package/dist/richtext-render.js +2 -0
  87. package/dist/richtext.d.ts.map +1 -1
  88. package/dist/richtext.js +2 -0
  89. package/dist/scheduler.interface.d.ts.map +1 -1
  90. package/dist/scheduler.interface.js +2 -0
  91. package/dist/scheduler.stub.d.ts.map +1 -1
  92. package/dist/scheduler.stub.js +2 -0
  93. package/dist/schema-mutator.d.ts +22 -0
  94. package/dist/schema-mutator.d.ts.map +1 -0
  95. package/dist/schema-mutator.js +3 -0
  96. package/dist/search/search.repository.d.ts.map +1 -1
  97. package/dist/search/search.repository.js +2 -0
  98. package/dist/seed-ddl-destructive.d.ts +40 -0
  99. package/dist/seed-ddl-destructive.d.ts.map +1 -0
  100. package/dist/seed-ddl-destructive.js +149 -0
  101. package/dist/seed-ddl.d.ts +40 -0
  102. package/dist/seed-ddl.d.ts.map +1 -0
  103. package/dist/seed-ddl.js +100 -0
  104. package/dist/seed-layout.d.ts +117 -0
  105. package/dist/seed-layout.d.ts.map +1 -0
  106. package/dist/seed-layout.js +169 -0
  107. package/dist/seed-layout.repository.d.ts +18 -0
  108. package/dist/seed-layout.repository.d.ts.map +1 -0
  109. package/dist/seed-layout.repository.js +3 -0
  110. package/dist/seed-registry.d.ts +23 -1
  111. package/dist/seed-registry.d.ts.map +1 -1
  112. package/dist/seed-registry.js +105 -0
  113. package/dist/seed-validation.d.ts +23 -0
  114. package/dist/seed-validation.d.ts.map +1 -0
  115. package/dist/seed-validation.js +224 -0
  116. package/dist/seed.repository.d.ts +37 -0
  117. package/dist/seed.repository.d.ts.map +1 -0
  118. package/dist/seed.repository.js +3 -0
  119. package/dist/seeds.d.ts.map +1 -1
  120. package/dist/seeds.js +2 -0
  121. package/dist/site-settings.repository.d.ts +16 -0
  122. package/dist/site-settings.repository.d.ts.map +1 -0
  123. package/dist/site-settings.repository.js +3 -0
  124. package/dist/slug-utils.d.ts.map +1 -1
  125. package/dist/slug-utils.js +2 -0
  126. package/dist/storage.d.ts +12 -38
  127. package/dist/storage.d.ts.map +1 -1
  128. package/dist/storage.js +2 -0
  129. package/dist/types.d.ts +81 -1
  130. package/dist/types.d.ts.map +1 -1
  131. package/dist/types.js +2 -0
  132. package/dist/validation.d.ts +23 -17
  133. package/dist/validation.d.ts.map +1 -1
  134. package/dist/validation.js +487 -381
  135. package/dist/webhook-validation.d.ts +3 -0
  136. package/dist/webhook-validation.d.ts.map +1 -0
  137. package/dist/webhook-validation.js +6 -0
  138. package/dist/widget/widget.repository.d.ts +32 -3
  139. package/dist/widget/widget.repository.d.ts.map +1 -1
  140. package/dist/widget/widget.repository.js +6 -1
  141. package/package.json +5 -4
@@ -0,0 +1,186 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ import { z } from 'zod';
4
+ // ---------------------------------------------------------------------------
5
+ // Shared rules used by both the Zod schemas and the semantic validator.
6
+ // ---------------------------------------------------------------------------
7
+ /** Namespaced widget type — lowercase npm-name-compatible.
8
+ * Built-ins use the 'core/' prefix; custom widgets '@scope/name'. */
9
+ export const WIDGET_TYPE_REGEX = /^[a-z0-9@][a-z0-9@/_-]*$/;
10
+ /** Max serialized size of a single widget `config`
11
+ * (`JSON.stringify(widget.config).length`) — guards D1 row bloat. */
12
+ export const MAX_WIDGET_CONFIG_BYTES = 8192;
13
+ // ---------------------------------------------------------------------------
14
+ // Zod schemas for shape validation
15
+ // ---------------------------------------------------------------------------
16
+ export const dashboardWidgetInstanceSchema = z.object({
17
+ id: z.string().min(1),
18
+ type: z.string().regex(WIDGET_TYPE_REGEX),
19
+ title: z.string().max(80).optional(),
20
+ config: z.record(z.string(), z.unknown()),
21
+ });
22
+ export const dashboardColumnSchema = z.object({
23
+ id: z.string().min(1),
24
+ widgets: z.array(dashboardWidgetInstanceSchema),
25
+ });
26
+ export const dashboardSectionSchema = z.object({
27
+ id: z.string().min(1),
28
+ label: z.string().max(60).optional(),
29
+ hideLabel: z.boolean().optional(),
30
+ collapsible: z.boolean().optional(),
31
+ columns: z.array(dashboardColumnSchema).min(1).max(4),
32
+ columnSpans: z.array(z.number().int().min(1).max(12)).optional(),
33
+ });
34
+ export const dashboardPageSchema = z.object({
35
+ id: z.string().min(1),
36
+ slug: z.string().regex(/^[a-z0-9][a-z0-9-]*$/).max(40),
37
+ label: z.string().min(1).max(60),
38
+ icon: z.string().max(40).optional(),
39
+ sections: z.array(dashboardSectionSchema).min(1),
40
+ });
41
+ export const dashboardLayoutSchema = z.object({
42
+ version: z.literal(1),
43
+ pages: z.array(dashboardPageSchema).min(1).max(8),
44
+ });
45
+ // ---------------------------------------------------------------------------
46
+ // Default dashboard layout generator
47
+ // ---------------------------------------------------------------------------
48
+ const defaultIdFactory = () => crypto.randomUUID();
49
+ /** Structural port of the legacy hardcoded dashboard config into a single
50
+ * 'Overview' page. Widgets fetch their own data: configs carry only the
51
+ * variant and (where relevant) the default seed binding. */
52
+ export function generateDefaultDashboardLayout(seeds, opts) {
53
+ const newId = opts?.newId ?? defaultIdFactory;
54
+ const widget = (type, config) => ({ id: newId(), type, config });
55
+ const singleWidgetColumn = (w) => ({
56
+ id: newId(),
57
+ widgets: [w],
58
+ });
59
+ const sections = [];
60
+ // Setup checklist, full width.
61
+ sections.push({
62
+ id: newId(),
63
+ hideLabel: true,
64
+ columns: [singleWidgetColumn(widget('core/setup-checklist', { variant: 'full' }))],
65
+ });
66
+ // Status row: equal columns; quick-draft needs at least one seed to target.
67
+ const statusWidgets = [
68
+ widget('core/site-status', { variant: 'badge' }),
69
+ widget('core/storage', { variant: 'gauge' }),
70
+ widget('core/publication-stats', { variant: 'trio' }),
71
+ ];
72
+ if (seeds.length > 0) {
73
+ statusWidgets.push(widget('core/quick-draft', { variant: 'minimal' }));
74
+ }
75
+ sections.push({
76
+ id: newId(),
77
+ hideLabel: true,
78
+ columns: statusWidgets.map(singleWidgetColumn),
79
+ });
80
+ if (seeds.length > 0) {
81
+ const seedSlug = seeds.find((s) => s.slug === 'articoli')?.slug ?? seeds[0].slug;
82
+ // Content row: recent content | pending drafts.
83
+ sections.push({
84
+ id: newId(),
85
+ hideLabel: true,
86
+ columns: [
87
+ singleWidgetColumn(widget('core/recent-content', { seedSlug, variant: 'list' })),
88
+ singleWidgetColumn(widget('core/pending-drafts', { seedSlug, variant: 'list' })),
89
+ ],
90
+ columnSpans: [6, 6],
91
+ });
92
+ // Media & activity row: media gallery | activity feed.
93
+ sections.push({
94
+ id: newId(),
95
+ hideLabel: true,
96
+ columns: [
97
+ singleWidgetColumn(widget('core/media-gallery', { seedSlug, variant: 'grid' })),
98
+ singleWidgetColumn(widget('core/activity-feed', { seedSlug, variant: 'feed' })),
99
+ ],
100
+ columnSpans: [6, 6],
101
+ });
102
+ }
103
+ return {
104
+ version: 1,
105
+ pages: [
106
+ {
107
+ id: newId(),
108
+ slug: 'overview',
109
+ label: 'Overview',
110
+ icon: 'LayoutDashboard',
111
+ sections,
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ /** Semantic validation on top of the Zod shape check (the caller's job).
117
+ * Widgets bound to a seed that no longer exists are silently stripped
118
+ * (auto-cleanup, recorded as a warning). Unknown widget types only warn —
119
+ * custom widgets are invisible to the Worker and must never be stripped.
120
+ * `cleaned` is always returned, errors or not. */
121
+ export function validateDashboardLayout(layout, ctx) {
122
+ const errors = [];
123
+ const warnings = [];
124
+ // Auto-cleanup: drop widgets whose config.seedSlug references a missing seed.
125
+ const cleanedPages = layout.pages.map((page) => ({
126
+ ...page,
127
+ sections: page.sections.map((section) => ({
128
+ ...section,
129
+ columns: section.columns.map((col) => ({
130
+ ...col,
131
+ widgets: (col.widgets ?? []).filter((w) => {
132
+ const seedSlug = (w.config ?? {})['seedSlug'];
133
+ if (typeof seedSlug === 'string' && !ctx.seedSlugs.has(seedSlug)) {
134
+ warnings.push(`Widget '${w.type}' (id=${w.id}) removed: seed '${seedSlug}' is not registered.`);
135
+ return false;
136
+ }
137
+ return true;
138
+ }),
139
+ })),
140
+ })),
141
+ }));
142
+ // Semantic checks on the cleaned layout
143
+ const seenWidgetIds = new Set();
144
+ const seenPageSlugs = new Set();
145
+ for (const page of cleanedPages) {
146
+ if (seenPageSlugs.has(page.slug)) {
147
+ errors.push(`Page slug '${page.slug}' appears more than once in the layout.`);
148
+ }
149
+ else {
150
+ seenPageSlugs.add(page.slug);
151
+ }
152
+ for (const section of page.sections) {
153
+ if (section.columnSpans !== undefined) {
154
+ if (section.columnSpans.length !== section.columns.length) {
155
+ errors.push(`Section ${section.id}: columnSpans has ${section.columnSpans.length} entries for ${section.columns.length} columns.`);
156
+ }
157
+ else {
158
+ const sum = section.columnSpans.reduce((acc, span) => acc + span, 0);
159
+ if (sum !== 12) {
160
+ errors.push(`Section ${section.id}: columnSpans must sum to 12, got ${sum}.`);
161
+ }
162
+ }
163
+ }
164
+ for (const col of section.columns) {
165
+ for (const w of col.widgets) {
166
+ if (seenWidgetIds.has(w.id)) {
167
+ errors.push(`Widget id '${w.id}' appears more than once in the layout.`);
168
+ }
169
+ else {
170
+ seenWidgetIds.add(w.id);
171
+ }
172
+ if (JSON.stringify(w.config ?? {}).length > MAX_WIDGET_CONFIG_BYTES) {
173
+ errors.push(`Widget '${w.type}' (id=${w.id}): config exceeds ${MAX_WIDGET_CONFIG_BYTES} bytes when serialized.`);
174
+ }
175
+ if (ctx.knownWidgetTypes !== undefined && !ctx.knownWidgetTypes.has(w.type)) {
176
+ warnings.push(`Widget '${w.type}' (id=${w.id}) is not a known widget type.`);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ const cleaned = { ...layout, pages: cleanedPages };
183
+ if (errors.length > 0)
184
+ return { ok: false, errors, cleaned, warnings };
185
+ return { ok: true, cleaned, warnings };
186
+ }
@@ -0,0 +1,18 @@
1
+ import type { DashboardLayout } from './dashboard-layout.js';
2
+ export interface DashboardLayoutRecord {
3
+ scope: string;
4
+ layout: DashboardLayout;
5
+ updatedAt: number;
6
+ updatedBy: string;
7
+ }
8
+ export interface IDashboardLayoutRepository {
9
+ /** Stored layout for a scope, or null if none was ever saved. */
10
+ get(scope: string): Promise<DashboardLayoutRecord | null>;
11
+ /** Scopes that currently have a stored row — used by the Sprint 06 builder UI. */
12
+ listScopes(): Promise<string[]>;
13
+ /** Upsert. `updatedBy` is the writer's user id. */
14
+ upsert(scope: string, layout: DashboardLayout, updatedBy: string): Promise<void>;
15
+ /** Remove the stored row — the "Reset" action. */
16
+ remove(scope: string): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=dashboard-layout.repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-layout.repository.d.ts","sourceRoot":"","sources":["../src/dashboard-layout.repository.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,eAAe,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,iEAAiE;IACjE,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAA;IACzD,kFAAkF;IAClF,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC/B,mDAAmD;IACnD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChF,kDAAkD;IAClD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC"}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const ROLES_ALLOWED_TO_EDIT_DASHBOARD: ReadonlyArray<string>;
2
+ export declare function canEditDashboard(role: string | undefined | null): boolean;
3
+ //# sourceMappingURL=dashboard-permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-permissions.d.ts","sourceRoot":"","sources":["../src/dashboard-permissions.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,+BAA+B,EAAE,aAAa,CAAC,MAAM,CAAa,CAAA;AAE/E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAGzE"}
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ // NOTE: change this list to extend write-access to other roles
4
+ // (e.g. add 'editor', or introduce a fine-grained 'dashboard:edit' permission).
5
+ // Single source of truth — used by both API guards and dashboard buttons.
6
+ export const ROLES_ALLOWED_TO_EDIT_DASHBOARD = ['admin'];
7
+ export function canEditDashboard(role) {
8
+ if (!role)
9
+ return false;
10
+ return ROLES_ALLOWED_TO_EDIT_DASHBOARD.includes(role);
11
+ }
@@ -0,0 +1,12 @@
1
+ /** Roles that may have their own dashboard layout. Single source of truth —
2
+ * extend this list if a new role is introduced. */
3
+ export declare const KNOWN_DASHBOARD_ROLES: ReadonlyArray<string>;
4
+ export declare const DEFAULT_DASHBOARD_SCOPE = "default";
5
+ /** Builds the per-role scope string, e.g. `roleScope('editor') === 'role:editor'`. */
6
+ export declare function roleScope(role: string): string;
7
+ /** Whether `scope` is one of the closed set: `'default' | 'role:<known role>'`. */
8
+ export declare function isValidDashboardScope(scope: string): boolean;
9
+ /** Read-resolution order for a caller's role: `['role:<role>', 'default']`,
10
+ * or just `['default']` for unknown/missing roles. */
11
+ export declare function resolveDashboardScopeChain(role: string | undefined | null): string[];
12
+ //# sourceMappingURL=dashboard-scopes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-scopes.d.ts","sourceRoot":"","sources":["../src/dashboard-scopes.ts"],"names":[],"mappings":"AAOA;oDACoD;AACpD,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAAuB,CAAA;AAE/E,eAAO,MAAM,uBAAuB,YAAY,CAAA;AAEhD,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAK5D;AAED;uDACuD;AACvD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,EAAE,CAKpF"}
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ // ---------------------------------------------------------------------------
4
+ // Dashboard layout scopes (Sprint 06: role-based dashboards)
5
+ // ---------------------------------------------------------------------------
6
+ /** Roles that may have their own dashboard layout. Single source of truth —
7
+ * extend this list if a new role is introduced. */
8
+ export const KNOWN_DASHBOARD_ROLES = ['admin', 'editor'];
9
+ export const DEFAULT_DASHBOARD_SCOPE = 'default';
10
+ /** Builds the per-role scope string, e.g. `roleScope('editor') === 'role:editor'`. */
11
+ export function roleScope(role) {
12
+ return `role:${role}`;
13
+ }
14
+ /** Whether `scope` is one of the closed set: `'default' | 'role:<known role>'`. */
15
+ export function isValidDashboardScope(scope) {
16
+ if (scope === DEFAULT_DASHBOARD_SCOPE)
17
+ return true;
18
+ const match = /^role:(.+)$/.exec(scope);
19
+ if (!match)
20
+ return false;
21
+ return KNOWN_DASHBOARD_ROLES.includes(match[1]);
22
+ }
23
+ /** Read-resolution order for a caller's role: `['role:<role>', 'default']`,
24
+ * or just `['default']` for unknown/missing roles. */
25
+ export function resolveDashboardScopeChain(role) {
26
+ if (role && KNOWN_DASHBOARD_ROLES.includes(role)) {
27
+ return [roleScope(role), DEFAULT_DASHBOARD_SCOPE];
28
+ }
29
+ return [DEFAULT_DASHBOARD_SCOPE];
30
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"define-seed.d.ts","sourceRoot":"","sources":["../src/define-seed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAE3C"}
1
+ {"version":3,"file":"define-seed.d.ts","sourceRoot":"","sources":["../src/define-seed.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAE3C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
1
3
  export function defineSeed(seed) {
2
4
  return seed;
3
5
  }
@@ -0,0 +1,5 @@
1
+ export interface IDemoDataRepository {
2
+ /** Executes the compiled demo dataset SQL against the database. */
3
+ loadDemoData(): Promise<void>;
4
+ }
5
+ //# sourceMappingURL=demo-data.repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-data.repository.d.ts","sourceRoot":"","sources":["../src/demo-data.repository.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B"}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ export {};
package/dist/engine.d.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  * It does not know about HTTP, auth, or UI — it's a pure TypeScript library.
7
7
  */
8
8
  import type { Seed, Branch, SelectOptions, ParameterizedQuery } from './types.js';
9
+ export declare function indexableSearchBranches(seed: Seed): Branch[];
9
10
  /**
10
11
  * Generates `CREATE TABLE IF NOT EXISTS content_{slug}` with system columns
11
12
  * + one column per Branch. Pure function: same Seed → same SQL.
@@ -60,6 +61,81 @@ export declare function getExpectedColumns(seed: Seed): SchemaColumn[];
60
61
  * boolean → 0/1 | date → Unix timestamp | json/asset-list → JSON string
61
62
  */
62
63
  export declare function serializeForDb(branch: Branch, value: unknown): string | number | null;
64
+ /**
65
+ * Returns the junction table name for a many-to-many relation branch.
66
+ * Format: `rel_<seedSlug>_<branchAlias>`.
67
+ */
68
+ export declare function junctionTableName(seedSlug: string, branchAlias: string): string;
69
+ /**
70
+ * Generates `CREATE TABLE IF NOT EXISTS rel_<seed>_<alias>` for a multi-relation
71
+ * branch. The parent FK always cascades (parent lifecycle owns the edge);
72
+ * the target FK uses `branch.onDelete` (default CASCADE).
73
+ *
74
+ * @throws {Error} when called on a non-multi-relation branch
75
+ * @throws {Error} when `branch.targetSeed` is missing
76
+ */
77
+ export declare function generateJunctionTable(seed: Seed, branch: Branch): string;
78
+ /**
79
+ * Generates the two B-tree indexes for a junction table:
80
+ * one on `parent_id` (list entries by parent) and one on `target_id`
81
+ * (cascade checks from target side).
82
+ */
83
+ export declare function generateJunctionIndexes(seed: Seed, branch: Branch): string[];
84
+ /**
85
+ * Generates the drafts junction table `rel_<seed>_<alias>_drafts` for a
86
+ * many-to-many relation branch. Note: no FK on `target_id` — targets may be
87
+ * deleted between draft save and publish; the FK would be wrong here.
88
+ * Returns null when `seed.allowDrafts` is falsy.
89
+ */
90
+ export declare function generateJunctionDraftTable(seed: Seed, branch: Branch): string | null;
91
+ /**
92
+ * Returns every `DROP TABLE IF EXISTS` needed to fully remove a content type:
93
+ * the main `content_{slug}` table, its `fts_{slug}` virtual table, the
94
+ * `content_{slug}_drafts` mirror (when drafts are enabled), and each
95
+ * multi-relation junction table `rel_{slug}_{alias}` (+ its `_drafts` mirror).
96
+ *
97
+ * FTS triggers reference the main table; dropping the main table also drops
98
+ * its triggers in SQLite, but we drop the FTS table explicitly so no orphaned
99
+ * virtual table survives. Order: junction (children) → drafts → fts → main.
100
+ */
101
+ export declare function generateDropTable(seed: Seed): string[];
102
+ /**
103
+ * Returns the `DROP COLUMN` statements for removing a single field.
104
+ *
105
+ * - For a multi-relation branch (no column on the parent table) it drops the
106
+ * junction table `rel_{slug}_{alias}` (+ `_drafts`) instead.
107
+ * - For a normal branch it drops the column from `content_{slug}` and, when
108
+ * drafts are enabled, the mirror column from `content_{slug}_drafts`.
109
+ * - For an orphan column (no matching branch in the definition) it falls back
110
+ * to a plain `ALTER TABLE … DROP COLUMN`, never touching drafts.
111
+ *
112
+ * Callers must rebuild FTS separately (planFtsRebuild) when the dropped column
113
+ * was searchable, since SQLite cannot ALTER an fts5 table's columns.
114
+ */
115
+ export declare function generateDropColumn(seed: Seed, alias: string): string[];
116
+ /**
117
+ * Returns the `RENAME COLUMN` statements for renaming a field's alias while
118
+ * keeping the stable `branch.id`. Renames the column on `content_{slug}` and,
119
+ * when drafts are enabled, the mirror column on `content_{slug}_drafts`.
120
+ *
121
+ * For a multi-relation branch the alias is part of the junction table name, so
122
+ * the junction table (+ `_drafts`) is renamed instead of a column.
123
+ *
124
+ * Callers must rebuild FTS separately (planFtsRebuild) when the renamed column
125
+ * was searchable.
126
+ */
127
+ export declare function generateRenameColumn(seed: Seed, from: string, to: string): string[];
128
+ /**
129
+ * Returns the add/copy/drop/rename statements that change a column's SQL type
130
+ * in place, preserving data via `CAST`. This is the documented simpler
131
+ * alternative to SQLite's full 12-step table rebuild.
132
+ *
133
+ * The `branch` passed in is the *target* definition (already carrying the new
134
+ * `type`); its SQL type is read from BRANCH_TYPE_SQL. Multi-relation branches
135
+ * have no column to retype and are rejected. Mirrors the change onto the drafts
136
+ * table when drafts are enabled. Callers rebuild FTS separately when needed.
137
+ */
138
+ export declare function generateRetypeColumn(seed: Seed, branch: Branch): string[];
63
139
  /**
64
140
  * Deserializes a value read from the DB for the API response.
65
141
  * 0/1 → boolean | Unix timestamp → ISO 8601 | JSON string → object
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,IAAI,EACJ,MAAM,EAKN,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAgFnB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAsBtD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAuB5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAkBpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAc1D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAgCxD;AAID;;;;GAIG;AAGH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,aAAkB,GAAG,kBAAkB,CAqE5F;AAgHD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,EAAE,CAc7D;AAID;;;GAGG;AAEH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAmCrF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA6BzE"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,IAAI,EACJ,MAAM,EAKN,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAsCnB,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAI5D;AAkED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CA0BtD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAyB5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAuBpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAc1D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAgCxD;AAID;;;;GAIG;AAGH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,aAAkB,GAAG,kBAAkB,CAqE5F;AAgHD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,EAAE,CAiB7D;AAID;;;GAGG;AAEH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAwCrF;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBxE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAM5E;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAapF;AAcD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CA0BtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBzE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAqCzE"}