@beechcms/core 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/antivirus/noop-antivirus.provider.d.ts.map +1 -1
  3. package/dist/antivirus/virustotal-antivirus.provider.d.ts.map +1 -1
  4. package/dist/automations-grammar-words.d.ts +2 -0
  5. package/dist/automations-grammar-words.d.ts.map +1 -0
  6. package/dist/automations-grammar-words.js +8 -0
  7. package/dist/automations.repository.interface.d.ts +19 -0
  8. package/dist/automations.repository.interface.d.ts.map +1 -0
  9. package/dist/automations.repository.interface.js +3 -0
  10. package/dist/automations.runner.interface.d.ts +10 -0
  11. package/dist/automations.runner.interface.d.ts.map +1 -0
  12. package/dist/automations.runner.interface.js +3 -0
  13. package/dist/automations.runner.stub.d.ts +5 -0
  14. package/dist/automations.runner.stub.d.ts.map +1 -0
  15. package/dist/automations.runner.stub.js +7 -0
  16. package/dist/automations.types.d.ts +75 -0
  17. package/dist/automations.types.d.ts.map +1 -0
  18. package/dist/automations.types.js +3 -0
  19. package/dist/clock.d.ts +25 -0
  20. package/dist/clock.d.ts.map +1 -0
  21. package/dist/clock.js +11 -0
  22. package/dist/common/canonical-json.d.ts +30 -0
  23. package/dist/common/canonical-json.d.ts.map +1 -0
  24. package/dist/common/canonical-json.js +89 -0
  25. package/dist/content/content.repository.d.ts +2 -2
  26. package/dist/content/content.repository.d.ts.map +1 -1
  27. package/dist/content/deletion-ledger.d.ts +39 -0
  28. package/dist/content/deletion-ledger.d.ts.map +1 -0
  29. package/dist/content/deletion-ledger.js +3 -0
  30. package/dist/content-scan.repository.d.ts +9 -0
  31. package/dist/content-scan.repository.d.ts.map +1 -0
  32. package/dist/content-scan.repository.js +3 -0
  33. package/dist/content.repository.d.ts +247 -0
  34. package/dist/content.repository.d.ts.map +1 -0
  35. package/dist/content.repository.js +59 -0
  36. package/dist/dashboard-layout.d.ts +154 -0
  37. package/dist/dashboard-layout.d.ts.map +1 -0
  38. package/dist/dashboard-layout.js +186 -0
  39. package/dist/dashboard-layout.repository.d.ts +18 -0
  40. package/dist/dashboard-layout.repository.d.ts.map +1 -0
  41. package/dist/dashboard-layout.repository.js +3 -0
  42. package/dist/dashboard-permissions.d.ts +3 -0
  43. package/dist/dashboard-permissions.d.ts.map +1 -0
  44. package/dist/dashboard-permissions.js +11 -0
  45. package/dist/dashboard-scopes.d.ts +12 -0
  46. package/dist/dashboard-scopes.d.ts.map +1 -0
  47. package/dist/dashboard-scopes.js +30 -0
  48. package/dist/define-seed.d.ts +3 -0
  49. package/dist/define-seed.d.ts.map +1 -0
  50. package/dist/define-seed.js +5 -0
  51. package/dist/demo-data.repository.d.ts +5 -0
  52. package/dist/demo-data.repository.d.ts.map +1 -0
  53. package/dist/demo-data.repository.js +3 -0
  54. package/dist/email.provider.d.ts +32 -0
  55. package/dist/email.provider.d.ts.map +1 -0
  56. package/dist/email.provider.js +4 -0
  57. package/dist/engine/ddl/ddl.d.ts +216 -0
  58. package/dist/engine/ddl/ddl.d.ts.map +1 -0
  59. package/dist/engine/ddl/ddl.js +574 -0
  60. package/dist/engine/ddl/schema-mutator.d.ts +26 -0
  61. package/dist/engine/ddl/schema-mutator.d.ts.map +1 -0
  62. package/dist/engine/ddl/schema-mutator.js +3 -0
  63. package/dist/engine/ddl/seed-ddl-destructive.d.ts +40 -0
  64. package/dist/engine/ddl/seed-ddl-destructive.d.ts.map +1 -0
  65. package/dist/engine/ddl/seed-ddl-destructive.js +149 -0
  66. package/dist/engine/ddl/seed-ddl.d.ts +43 -0
  67. package/dist/engine/ddl/seed-ddl.d.ts.map +1 -0
  68. package/dist/engine/ddl/seed-ddl.js +115 -0
  69. package/dist/engine/introspection/introspection.d.ts +101 -0
  70. package/dist/engine/introspection/introspection.d.ts.map +1 -0
  71. package/dist/engine/introspection/introspection.js +127 -0
  72. package/dist/engine/introspection/schema-fingerprint.d.ts +73 -0
  73. package/dist/engine/introspection/schema-fingerprint.d.ts.map +1 -0
  74. package/dist/engine/introspection/schema-fingerprint.js +97 -0
  75. package/dist/engine/introspection.d.ts +101 -0
  76. package/dist/engine/introspection.d.ts.map +1 -0
  77. package/dist/engine/introspection.js +127 -0
  78. package/dist/engine/localization/localization.d.ts +127 -0
  79. package/dist/engine/localization/localization.d.ts.map +1 -0
  80. package/dist/engine/localization/localization.js +212 -0
  81. package/dist/engine/localization.d.ts +127 -0
  82. package/dist/engine/localization.d.ts.map +1 -0
  83. package/dist/engine/localization.js +212 -0
  84. package/dist/engine/privacy/policies.d.ts +58 -0
  85. package/dist/engine/privacy/policies.d.ts.map +1 -0
  86. package/dist/engine/privacy/policies.js +185 -0
  87. package/dist/engine/privacy/privacy.service.d.ts +65 -0
  88. package/dist/engine/privacy/privacy.service.d.ts.map +1 -0
  89. package/dist/engine/privacy/privacy.service.js +114 -0
  90. package/dist/engine/privacy.service.d.ts.map +1 -1
  91. package/dist/engine/query/query.d.ts +23 -0
  92. package/dist/engine/query/query.d.ts.map +1 -0
  93. package/dist/engine/query/query.js +301 -0
  94. package/dist/engine/query/relations.d.ts +28 -0
  95. package/dist/engine/query/relations.d.ts.map +1 -0
  96. package/dist/engine/query/relations.js +25 -0
  97. package/dist/engine/query/serialize.d.ts +22 -0
  98. package/dist/engine/query/serialize.d.ts.map +1 -0
  99. package/dist/engine/query/serialize.js +175 -0
  100. package/dist/engine/schema-fingerprint.d.ts +73 -0
  101. package/dist/engine/schema-fingerprint.d.ts.map +1 -0
  102. package/dist/engine/schema-fingerprint.js +97 -0
  103. package/dist/engine/seed-registry.d.ts.map +1 -1
  104. package/dist/engine/seeds/define-seed.d.ts +3 -0
  105. package/dist/engine/seeds/define-seed.d.ts.map +1 -0
  106. package/dist/engine/seeds/define-seed.js +5 -0
  107. package/dist/engine/seeds/seed-registry.d.ts +71 -0
  108. package/dist/engine/seeds/seed-registry.d.ts.map +1 -0
  109. package/dist/engine/seeds/seed-registry.js +167 -0
  110. package/dist/engine/seeds/seed-types-generator.d.ts +25 -0
  111. package/dist/engine/seeds/seed-types-generator.d.ts.map +1 -0
  112. package/dist/engine/seeds/seed-types-generator.js +117 -0
  113. package/dist/engine/seeds/seed-validation.d.ts +30 -0
  114. package/dist/engine/seeds/seed-validation.d.ts.map +1 -0
  115. package/dist/engine/seeds/seed-validation.js +364 -0
  116. package/dist/engine/seeds/seeds.d.ts +20 -0
  117. package/dist/engine/seeds/seeds.d.ts.map +1 -0
  118. package/dist/engine/seeds/seeds.js +21 -0
  119. package/dist/engine/validation/emptiness.d.ts +12 -0
  120. package/dist/engine/validation/emptiness.d.ts.map +1 -0
  121. package/dist/engine/validation/emptiness.js +33 -0
  122. package/dist/engine/validation/index.d.ts +1 -1
  123. package/dist/engine/validation/index.d.ts.map +1 -1
  124. package/dist/engine.d.ts +144 -0
  125. package/dist/engine.d.ts.map +1 -0
  126. package/dist/engine.js +741 -0
  127. package/dist/file-types.d.ts +13 -0
  128. package/dist/file-types.d.ts.map +1 -0
  129. package/dist/file-types.js +85 -0
  130. package/dist/hooks.d.ts +29 -0
  131. package/dist/hooks.d.ts.map +1 -0
  132. package/dist/hooks.js +3 -0
  133. package/dist/id-generator.d.ts +26 -0
  134. package/dist/id-generator.d.ts.map +1 -0
  135. package/dist/id-generator.js +12 -0
  136. package/dist/idempotency.repository.d.ts +13 -0
  137. package/dist/idempotency.repository.d.ts.map +1 -0
  138. package/dist/idempotency.repository.js +3 -0
  139. package/dist/kanban-position.repository.d.ts +19 -0
  140. package/dist/kanban-position.repository.d.ts.map +1 -0
  141. package/dist/kanban-position.repository.js +3 -0
  142. package/dist/kanban.d.ts +75 -0
  143. package/dist/kanban.d.ts.map +1 -0
  144. package/dist/kanban.js +82 -0
  145. package/dist/layout-permissions.d.ts +3 -0
  146. package/dist/layout-permissions.d.ts.map +1 -0
  147. package/dist/layout-permissions.js +11 -0
  148. package/dist/media/image-transformer.d.ts +34 -0
  149. package/dist/media/image-transformer.d.ts.map +1 -0
  150. package/dist/media/image-transformer.js +3 -0
  151. package/dist/media/media-transform.d.ts +88 -0
  152. package/dist/media/media-transform.d.ts.map +1 -0
  153. package/dist/media/media-transform.js +193 -0
  154. package/dist/media.repository.d.ts +61 -0
  155. package/dist/media.repository.d.ts.map +1 -0
  156. package/dist/media.repository.js +3 -0
  157. package/dist/notifications/notification-service.d.ts +1 -1
  158. package/dist/oauth/pkce.d.ts +1 -1
  159. package/dist/oauth/role-guard.d.ts.map +1 -1
  160. package/dist/oauth/scopes.d.ts +1 -1
  161. package/dist/oauth/scopes.d.ts.map +1 -1
  162. package/dist/policies.d.ts +10 -0
  163. package/dist/policies.d.ts.map +1 -0
  164. package/dist/policies.js +33 -0
  165. package/dist/queue/queue.stub.d.ts.map +1 -1
  166. package/dist/queue/scheduler.stub.d.ts.map +1 -1
  167. package/dist/queue.interface.d.ts +39 -0
  168. package/dist/queue.interface.d.ts.map +1 -0
  169. package/dist/queue.interface.js +3 -0
  170. package/dist/queue.stub.d.ts +6 -0
  171. package/dist/queue.stub.d.ts.map +1 -0
  172. package/dist/queue.stub.js +8 -0
  173. package/dist/rate-limit/token-bucket-rate-limiter.d.ts.map +1 -1
  174. package/dist/rbac/evaluate.d.ts +57 -0
  175. package/dist/rbac/evaluate.d.ts.map +1 -0
  176. package/dist/rbac/evaluate.js +113 -0
  177. package/dist/rbac/invitation.repository.d.ts +66 -0
  178. package/dist/rbac/invitation.repository.d.ts.map +1 -0
  179. package/dist/rbac/invitation.repository.js +3 -0
  180. package/dist/rbac/permissions.d.ts +39 -0
  181. package/dist/rbac/permissions.d.ts.map +1 -0
  182. package/dist/rbac/permissions.js +43 -0
  183. package/dist/rbac/types.d.ts +118 -0
  184. package/dist/rbac/types.d.ts.map +1 -0
  185. package/dist/rbac/types.js +3 -0
  186. package/dist/relations.d.ts +28 -0
  187. package/dist/relations.d.ts.map +1 -0
  188. package/dist/relations.js +25 -0
  189. package/dist/richtext-render.d.ts +11 -0
  190. package/dist/richtext-render.d.ts.map +1 -0
  191. package/dist/richtext-render.js +87 -0
  192. package/dist/richtext.d.ts +11 -0
  193. package/dist/richtext.d.ts.map +1 -0
  194. package/dist/richtext.js +13 -0
  195. package/dist/scheduler.interface.d.ts +4 -0
  196. package/dist/scheduler.interface.d.ts.map +1 -0
  197. package/dist/scheduler.interface.js +3 -0
  198. package/dist/scheduler.stub.d.ts +5 -0
  199. package/dist/scheduler.stub.d.ts.map +1 -0
  200. package/dist/scheduler.stub.js +7 -0
  201. package/dist/schema/canonical.d.ts +15 -0
  202. package/dist/schema/canonical.d.ts.map +1 -0
  203. package/dist/schema/canonical.js +37 -0
  204. package/dist/schema/define.d.ts +42 -0
  205. package/dist/schema/define.d.ts.map +1 -0
  206. package/dist/schema/define.js +56 -0
  207. package/dist/schema/emit.d.ts +14 -0
  208. package/dist/schema/emit.d.ts.map +1 -0
  209. package/dist/schema/emit.js +43 -0
  210. package/dist/schema/index.d.ts +12 -0
  211. package/dist/schema/index.d.ts.map +1 -0
  212. package/dist/schema/index.js +13 -0
  213. package/dist/schema/manifest-seeds.d.ts +12 -0
  214. package/dist/schema/manifest-seeds.d.ts.map +1 -0
  215. package/dist/schema/manifest-seeds.js +48 -0
  216. package/dist/schema/manifest-validation.d.ts +13 -0
  217. package/dist/schema/manifest-validation.d.ts.map +1 -0
  218. package/dist/schema/manifest-validation.js +41 -0
  219. package/dist/schema/manifest.types.d.ts +54 -0
  220. package/dist/schema/manifest.types.d.ts.map +1 -0
  221. package/dist/schema/manifest.types.js +7 -0
  222. package/dist/schema-mutator.d.ts +26 -0
  223. package/dist/schema-mutator.d.ts.map +1 -0
  224. package/dist/schema-mutator.js +3 -0
  225. package/dist/seed-ddl-destructive.d.ts +40 -0
  226. package/dist/seed-ddl-destructive.d.ts.map +1 -0
  227. package/dist/seed-ddl-destructive.js +149 -0
  228. package/dist/seed-ddl.d.ts +40 -0
  229. package/dist/seed-ddl.d.ts.map +1 -0
  230. package/dist/seed-ddl.js +100 -0
  231. package/dist/seed-layout.d.ts +145 -0
  232. package/dist/seed-layout.d.ts.map +1 -0
  233. package/dist/seed-layout.js +192 -0
  234. package/dist/seed-layout.repository.d.ts +22 -0
  235. package/dist/seed-layout.repository.d.ts.map +1 -0
  236. package/dist/seed-layout.repository.js +3 -0
  237. package/dist/seed-registry.d.ts +71 -0
  238. package/dist/seed-registry.d.ts.map +1 -0
  239. package/dist/seed-registry.js +143 -0
  240. package/dist/seed-types-generator.d.ts +1 -1
  241. package/dist/seed-types-generator.d.ts.map +1 -1
  242. package/dist/seed-validation.d.ts +23 -0
  243. package/dist/seed-validation.d.ts.map +1 -0
  244. package/dist/seed-validation.js +224 -0
  245. package/dist/seed.repository.d.ts +37 -0
  246. package/dist/seed.repository.d.ts.map +1 -0
  247. package/dist/seed.repository.js +3 -0
  248. package/dist/seeds.d.ts +20 -0
  249. package/dist/seeds.d.ts.map +1 -0
  250. package/dist/seeds.js +21 -0
  251. package/dist/site-settings.repository.d.ts +16 -0
  252. package/dist/site-settings.repository.d.ts.map +1 -0
  253. package/dist/site-settings.repository.js +3 -0
  254. package/dist/slug-utils.d.ts +18 -0
  255. package/dist/slug-utils.d.ts.map +1 -0
  256. package/dist/slug-utils.js +32 -0
  257. package/dist/storage.d.ts +46 -0
  258. package/dist/storage.d.ts.map +1 -0
  259. package/dist/storage.js +3 -0
  260. package/dist/transfer/csv.d.ts +34 -0
  261. package/dist/transfer/csv.d.ts.map +1 -0
  262. package/dist/transfer/csv.js +135 -0
  263. package/dist/transfer/flat-seed.d.ts +13 -0
  264. package/dist/transfer/flat-seed.d.ts.map +1 -0
  265. package/dist/transfer/flat-seed.js +45 -0
  266. package/dist/transfer/index.d.ts +7 -0
  267. package/dist/transfer/index.d.ts.map +1 -0
  268. package/dist/transfer/index.js +8 -0
  269. package/dist/transfer/ndjson.d.ts +25 -0
  270. package/dist/transfer/ndjson.d.ts.map +1 -0
  271. package/dist/transfer/ndjson.js +62 -0
  272. package/dist/transfer/row-mapping.d.ts +36 -0
  273. package/dist/transfer/row-mapping.d.ts.map +1 -0
  274. package/dist/transfer/row-mapping.js +90 -0
  275. package/dist/transfer/transfer.constants.d.ts +20 -0
  276. package/dist/transfer/transfer.constants.d.ts.map +1 -0
  277. package/dist/transfer/transfer.constants.js +21 -0
  278. package/dist/transfer/transfer.types.d.ts +46 -0
  279. package/dist/transfer/transfer.types.d.ts.map +1 -0
  280. package/dist/transfer/transfer.types.js +6 -0
  281. package/dist/types.d.ts +243 -0
  282. package/dist/types.d.ts.map +1 -0
  283. package/dist/types.js +3 -0
  284. package/dist/validation.d.ts +50 -0
  285. package/dist/validation.d.ts.map +1 -0
  286. package/dist/validation.js +660 -0
  287. package/dist/webhook-validation.d.ts +3 -0
  288. package/dist/webhook-validation.d.ts.map +1 -0
  289. package/dist/webhook-validation.js +6 -0
  290. package/package.json +4 -4
@@ -0,0 +1,56 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /**
4
+ * @module schema/define
5
+ * Authoring DSL for `beech.schema.ts`. Pure, allocation-only helpers: they give the author
6
+ * inference and autocomplete, and they never validate, throw, or touch I/O.
7
+ */
8
+ import { MANIFEST_VERSION } from './manifest.types.js';
9
+ function isFieldGroup(input) {
10
+ return Array.isArray(input.branches);
11
+ }
12
+ /**
13
+ * Declares the root manifest. Seeds are stored in authoring order here; canonical ordering is
14
+ * applied by `toCanonicalJson`, so a reordered manifest file produces byte-identical canonical JSON.
15
+ */
16
+ export function defineSchema(input) {
17
+ return { version: MANIFEST_VERSION, seeds: input.seeds };
18
+ }
19
+ /**
20
+ * Declares one content type, flattening any `defineGroup()` bundles into the branch list.
21
+ * Branch ORDER is preserved: it is the physical column order `planCreateSeed` will emit.
22
+ */
23
+ export function defineSeed(input) {
24
+ const branches = [];
25
+ for (const item of input.branches) {
26
+ if (isFieldGroup(item))
27
+ branches.push(...item.branches);
28
+ else
29
+ branches.push(item);
30
+ }
31
+ return { ...input, branches };
32
+ }
33
+ /** Declares a reusable field bundle. Expanded at author time by `defineSeed`; never persisted. */
34
+ export function defineGroup(name, branches) {
35
+ return { name, branches };
36
+ }
37
+ function field(type) {
38
+ return (input) => ({ ...input, type });
39
+ }
40
+ /**
41
+ * Typed field constructors. `relation` and `repeater` narrow their input because the engine treats
42
+ * `targetSeed` / `fields` as required for those types — making the compiler enforce what
43
+ * `validateSeedDefinitions` would otherwise only catch at apply time.
44
+ */
45
+ export const defineField = {
46
+ text: field('text'),
47
+ number: field('number'),
48
+ boolean: field('boolean'),
49
+ json: field('json'),
50
+ date: field('date'),
51
+ richtext: field('richtext'),
52
+ file: field('file'),
53
+ tags: field('tags'),
54
+ relation: (input) => ({ ...input, type: 'relation' }),
55
+ repeater: (input) => ({ ...input, type: 'repeater' }),
56
+ };
@@ -0,0 +1,14 @@
1
+ import type { BeechSchemaManifest } from './manifest.types.js';
2
+ /**
3
+ * Emits the full module source for a `beech.schema.ts`.
4
+ *
5
+ * Seeds are sorted by slug so two exports of the same database are byte-identical and a Git diff
6
+ * shows schema change only. `version` is NOT passed to `defineSchema` — the DSL adds it, and an
7
+ * excess property would fail the consumer's own typecheck.
8
+ *
9
+ * @throws CanonicalSerializationError when a seed carries a value that cannot survive a JSON
10
+ * round-trip. That can only happen if a non-serializable value reached the `seeds` table, which is
11
+ * a corrupt-state fault worth surfacing, not worth silently dropping.
12
+ */
13
+ export declare function emitManifestModule(manifest: BeechSchemaManifest): string;
14
+ //# sourceMappingURL=emit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/schema/emit.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAU9D;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CASxE"}
@@ -0,0 +1,43 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /**
4
+ * @module schema/emit
5
+ * Renders a manifest as the source of a `beech.schema.ts` module — the inverse of the authoring DSL.
6
+ *
7
+ * The seed payload is embedded as the FROZEN canonical JSON literal (sprint 1's serializer), not as
8
+ * a tree of `defineField.*` calls. Three reasons a future reader must not "improve" this:
9
+ * 1. a call-tree emitter is a second serializer for the manifest shape, free to drift from
10
+ * `define.ts` the moment a branch type is added;
11
+ * 2. the bytes here must match what `toCanonicalJson` produces, because that is what the validator
12
+ * and the diff compare — one format, one producer;
13
+ * 3. a JSON object literal IS a valid TypeScript expression, so the emitted file typechecks in the
14
+ * consumer's project with zero extra machinery.
15
+ *
16
+ * A hand-authored manifest using the full DSL round-trips through this emitter unchanged in meaning:
17
+ * `defineSchema`/`defineSeed`/`defineField` all erase to the same `BeechSchemaManifest`.
18
+ */
19
+ import { canonicalStringify } from '../common/canonical-json.js';
20
+ /** Header written at the top of every exported manifest. */
21
+ const HEADER = `// Generated by \`beech schema export\` from live D1 state.\n` +
22
+ `//\n` +
23
+ `// This file is DESIRED STATE, not runtime authority: the Worker never imports it and no deploy\n` +
24
+ `// applies it. Edit it, review the diff in Git, then reconcile with \`beech schema plan\` /\n` +
25
+ `// \`beech schema apply\`. Re-export at any time to refresh the snapshot.\n`;
26
+ /**
27
+ * Emits the full module source for a `beech.schema.ts`.
28
+ *
29
+ * Seeds are sorted by slug so two exports of the same database are byte-identical and a Git diff
30
+ * shows schema change only. `version` is NOT passed to `defineSchema` — the DSL adds it, and an
31
+ * excess property would fail the consumer's own typecheck.
32
+ *
33
+ * @throws CanonicalSerializationError when a seed carries a value that cannot survive a JSON
34
+ * round-trip. That can only happen if a non-serializable value reached the `seeds` table, which is
35
+ * a corrupt-state fault worth surfacing, not worth silently dropping.
36
+ */
37
+ export function emitManifestModule(manifest) {
38
+ const seeds = [...manifest.seeds].sort((a, b) => a.slug.localeCompare(b.slug));
39
+ const literal = canonicalStringify({ seeds }).trimEnd();
40
+ return (`${HEADER}\n` +
41
+ `import { defineSchema } from '@beechcms/core/schema'\n\n` +
42
+ `export default defineSchema(${literal})\n`);
43
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @module schema
3
+ * Authoring surface for `beech.schema.ts`. Imported by authoring tools and the CLI — NEVER by the
4
+ * Worker, which is why this module is a separate subpath and is absent from `src/index.ts`.
5
+ */
6
+ export * from './manifest.types.js';
7
+ export * from './define.js';
8
+ export * from './canonical.js';
9
+ export * from './manifest-seeds.js';
10
+ export * from './manifest-validation.js';
11
+ export * from './emit.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,WAAW,CAAA"}
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /**
4
+ * @module schema
5
+ * Authoring surface for `beech.schema.ts`. Imported by authoring tools and the CLI — NEVER by the
6
+ * Worker, which is why this module is a separate subpath and is absent from `src/index.ts`.
7
+ */
8
+ export * from './manifest.types.js';
9
+ export * from './define.js';
10
+ export * from './canonical.js';
11
+ export * from './manifest-seeds.js';
12
+ export * from './manifest-validation.js';
13
+ export * from './emit.js';
@@ -0,0 +1,12 @@
1
+ import type { BeechSchemaManifest } from './manifest.types.js';
2
+ import type { Seed } from '../engine/types.js';
3
+ /** Manifest → engine `Seed[]`, ready for `validateSeedDefinitions` or an MCP plan candidate. */
4
+ export declare function manifestToSeeds(manifest: BeechSchemaManifest): Seed[];
5
+ /**
6
+ * Engine `Seed[]` → manifest, for a future `beech schema export`.
7
+ *
8
+ * `layout` is stripped: it is server-populated presentation state, not schema. Seeds are sorted by
9
+ * slug so an export is diffable; branch order is preserved because it is the physical column order.
10
+ */
11
+ export declare function seedsToManifest(seeds: Seed[]): BeechSchemaManifest;
12
+ //# sourceMappingURL=manifest-seeds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-seeds.d.ts","sourceRoot":"","sources":["../../src/schema/manifest-seeds.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAgC,MAAM,qBAAqB,CAAA;AAC5F,OAAO,KAAK,EAAU,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAuBtD,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,EAAE,CAKrE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAMlE"}
@@ -0,0 +1,48 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /**
4
+ * @module schema/manifest-seeds
5
+ * Bridges the manifest shape and the engine's `Seed` shape, in both directions.
6
+ */
7
+ import { nextBranchId } from '../engine/seeds/seed-registry.js';
8
+ import { MANIFEST_VERSION } from './manifest.types.js';
9
+ /**
10
+ * Fills in ids for branches authored without one, in declaration order.
11
+ *
12
+ * These ids are AUTHORING-LOCAL and non-authoritative — they exist so `validateSeedDefinitions`,
13
+ * which fatals on a missing id, has a complete `Seed` to check. At plan/apply time the stored id
14
+ * wins: `normalizeCandidate()` in the API's MCP slice matches by alias against the stored
15
+ * definition before minting anything. Never persist an id minted here as if it were stable.
16
+ */
17
+ function withBranchIds(branches) {
18
+ const accumulated = [];
19
+ for (const branch of branches) {
20
+ const resolved = {
21
+ ...branch,
22
+ id: branch.id ?? nextBranchId({ branches: accumulated }),
23
+ ...(branch.fields ? { fields: withBranchIds(branch.fields) } : {}),
24
+ };
25
+ accumulated.push(resolved);
26
+ }
27
+ return accumulated;
28
+ }
29
+ /** Manifest → engine `Seed[]`, ready for `validateSeedDefinitions` or an MCP plan candidate. */
30
+ export function manifestToSeeds(manifest) {
31
+ return manifest.seeds.map((seed) => ({
32
+ ...seed,
33
+ branches: withBranchIds(seed.branches),
34
+ }));
35
+ }
36
+ /**
37
+ * Engine `Seed[]` → manifest, for a future `beech schema export`.
38
+ *
39
+ * `layout` is stripped: it is server-populated presentation state, not schema. Seeds are sorted by
40
+ * slug so an export is diffable; branch order is preserved because it is the physical column order.
41
+ */
42
+ export function seedsToManifest(seeds) {
43
+ const sorted = [...seeds].sort((a, b) => a.slug.localeCompare(b.slug));
44
+ return {
45
+ version: MANIFEST_VERSION,
46
+ seeds: sorted.map(({ layout: _layout, ...seed }) => seed),
47
+ };
48
+ }
@@ -0,0 +1,13 @@
1
+ import type { SeedValidationIssue } from '../engine/seeds/seed-validation.js';
2
+ import type { BeechSchemaManifest } from './manifest.types.js';
3
+ /**
4
+ * Validates a manifest end to end:
5
+ * 1. format version is one this build understands;
6
+ * 2. every value survives canonical serialization (no callbacks or executable code persisted);
7
+ * 3. the derived `Seed[]` passes the engine's whole-set validation — including cross-seed relation
8
+ * targets, which is exactly why this is a manifest-level and not a seed-level function.
9
+ *
10
+ * Returns issues; never throws. A caller decides whether a non-fatal issue blocks.
11
+ */
12
+ export declare function validateManifest(manifest: BeechSchemaManifest): SeedValidationIssue[];
13
+ //# sourceMappingURL=manifest-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-validation.d.ts","sourceRoot":"","sources":["../../src/schema/manifest-validation.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAI7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,CAqBrF"}
@@ -0,0 +1,41 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /**
4
+ * @module schema/manifest-validation
5
+ * Whole-manifest validation. Reuses the engine's `validateSeedDefinitions` rather than
6
+ * reimplementing it: a manifest that passes here is a candidate set the API would also accept.
7
+ */
8
+ import { validateSeedDefinitions } from '../engine/seeds/seed-validation.js';
9
+ import { MANIFEST_VERSION } from './manifest.types.js';
10
+ import { toCanonicalJson, ManifestSerializationError } from './canonical.js';
11
+ import { manifestToSeeds } from './manifest-seeds.js';
12
+ /**
13
+ * Validates a manifest end to end:
14
+ * 1. format version is one this build understands;
15
+ * 2. every value survives canonical serialization (no callbacks or executable code persisted);
16
+ * 3. the derived `Seed[]` passes the engine's whole-set validation — including cross-seed relation
17
+ * targets, which is exactly why this is a manifest-level and not a seed-level function.
18
+ *
19
+ * Returns issues; never throws. A caller decides whether a non-fatal issue blocks.
20
+ */
21
+ export function validateManifest(manifest) {
22
+ if (manifest.version !== MANIFEST_VERSION) {
23
+ return [{
24
+ slug: '*',
25
+ fatal: true,
26
+ messages: [
27
+ `unsupported manifest version ${String(manifest.version)}; this build understands ${MANIFEST_VERSION}.`,
28
+ ],
29
+ }];
30
+ }
31
+ try {
32
+ toCanonicalJson(manifest);
33
+ }
34
+ catch (error) {
35
+ if (error instanceof ManifestSerializationError) {
36
+ return [{ slug: '*', fatal: true, messages: [error.message] }];
37
+ }
38
+ throw error;
39
+ }
40
+ return validateSeedDefinitions(manifestToSeeds(manifest));
41
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @module schema/manifest.types
3
+ * Data shapes for the `beech.schema.ts` desired-state manifest. Pure types — no runtime logic.
4
+ *
5
+ * A manifest is NOT a runtime authority: D1 remains the sole runtime source of truth. These types
6
+ * describe an artifact that is authored, reviewed in Git, and reconciled through an explicit
7
+ * plan/apply step.
8
+ */
9
+ import type { Branch, Seed } from '../engine/types.js';
10
+ /**
11
+ * Manifest format version. Bumped ONLY on a breaking change to the manifest shape — never on an
12
+ * additive field. `fromCanonicalJson` refuses any other value rather than guessing.
13
+ */
14
+ export declare const MANIFEST_VERSION = 1;
15
+ /**
16
+ * A field as written by hand in a manifest.
17
+ *
18
+ * `id` is optional at authoring time: nobody hand-writes `br_07`, and an author-invented id would
19
+ * collide with the ids the engine already minted. It is filled in by `manifestToSeeds()` purely so
20
+ * validation has a complete `Seed`; the AUTHORITATIVE assignment happens at plan/apply time, where
21
+ * `normalizeCandidate()` preserves the stored id by alias match before minting a new one.
22
+ * `fields` is recursive for `repeater` sub-fields, which carry the same rule.
23
+ */
24
+ export type ManifestBranch = Omit<Branch, 'id' | 'fields'> & {
25
+ id?: string;
26
+ fields?: ManifestBranch[];
27
+ };
28
+ /**
29
+ * A content type as written in a manifest.
30
+ *
31
+ * `layout` is deliberately absent: it is typed `unknown`, is populated server-side by
32
+ * `GET /api/schema`, and is not a schema concern. Round-tripping it would make a future
33
+ * `beech schema diff` report permanent false drift.
34
+ */
35
+ export type ManifestSeed = Omit<Seed, 'branches' | 'layout'> & {
36
+ branches: ManifestBranch[];
37
+ };
38
+ /**
39
+ * A reusable bundle of fields (e.g. an SEO block shared by several seeds).
40
+ *
41
+ * Author-time only: `defineSeed()` splices `branches` into the seed's own list and the group leaves
42
+ * ZERO trace in canonical JSON. It is a macro, not a persisted concept — there is exactly one
43
+ * grouping mechanism in D1, and this is not it.
44
+ */
45
+ export interface FieldGroup {
46
+ name: string;
47
+ branches: ManifestBranch[];
48
+ }
49
+ /** The root artifact: the whole desired schema state. */
50
+ export interface BeechSchemaManifest {
51
+ version: typeof MANIFEST_VERSION;
52
+ seeds: ManifestSeed[];
53
+ }
54
+ //# sourceMappingURL=manifest.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.types.d.ts","sourceRoot":"","sources":["../../src/schema/manifest.types.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAA;AAEjC;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC,GAAG;IAC3D,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,cAAc,EAAE,CAAA;CAC1B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC,GAAG;IAC7D,QAAQ,EAAE,cAAc,EAAE,CAAA;CAC3B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,gBAAgB,CAAA;IAChC,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB"}
@@ -0,0 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /**
4
+ * Manifest format version. Bumped ONLY on a breaking change to the manifest shape — never on an
5
+ * additive field. `fromCanonicalJson` refuses any other value rather than guessing.
6
+ */
7
+ export const MANIFEST_VERSION = 1;
@@ -0,0 +1,26 @@
1
+ /** Executes additive schema DDL against the live database and introspects columns.
2
+ * Implemented by D1SchemaMutator in apps/api/src/shared/schema-mutator.d1.ts.
3
+ * This is the ONLY sanctioned channel for runtime DDL — handlers never touch env.DB. */
4
+ export interface ISchemaMutator {
5
+ /** Column names currently on a table, or null if the table does not exist. */
6
+ getColumns(table: string): Promise<Set<string> | null>;
7
+ /** Runs the given DDL statements in order as a single D1 batch.
8
+ * All statements must be additive (CREATE … IF NOT EXISTS / ALTER … ADD COLUMN /
9
+ * CREATE INDEX IF NOT EXISTS). Throws on the first failing statement. */
10
+ execDdl(statements: string[]): Promise<void>;
11
+ /** `DROP TABLE IF EXISTS {table}`. */
12
+ dropTable(table: string): Promise<void>;
13
+ /** `ALTER TABLE {table} DROP COLUMN {column}`. */
14
+ dropColumn(table: string, column: string): Promise<void>;
15
+ /** `ALTER TABLE {table} RENAME COLUMN {from} TO {to}`. */
16
+ renameColumn(table: string, from: string, to: string): Promise<void>;
17
+ /** Runs a multi-statement destructive batch atomically (FTS rebuild,
18
+ * type-change column rebuild, full type drop). The caller assembles the
19
+ * statements; the impl validates identifiers and runs them as one D1 batch. */
20
+ execDestructive(statements: string[]): Promise<void>;
21
+ /** Reads all rows from a table for the specified columns only.
22
+ * Used before DROP TABLE to collect R2 keys for media cleanup.
23
+ * All identifiers are validated against `^[A-Za-z0-9_]+$`. */
24
+ fetchRows(table: string, columns: string[]): Promise<Record<string, unknown>[]>;
25
+ }
26
+ //# sourceMappingURL=schema-mutator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-mutator.d.ts","sourceRoot":"","sources":["../src/schema-mutator.ts"],"names":[],"mappings":"AAGA;;yFAEyF;AACzF,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;IACtD;;8EAE0E;IAC1E,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAS5C,sCAAsC;IACtC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,kDAAkD;IAClD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD,0DAA0D;IAC1D,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE;;oFAEgF;IAChF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpD;;mEAE+D;IAC/D,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;CAChF"}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ export {};
@@ -0,0 +1,40 @@
1
+ import type { Seed, BranchType } from './types.js';
2
+ /**
3
+ * Ordered DROP TABLE statements for a full hard-delete of a seed's tables.
4
+ * Order: junction drafts → junctions → FTS triggers → FTS table → drafts → main table.
5
+ * All statements use IF EXISTS (idempotent).
6
+ */
7
+ export declare function generateDropTable(seed: Seed): string[];
8
+ /**
9
+ * DROP COLUMN statements for a single branch (by alias).
10
+ * For multi-relation branches: drops the junction tables (no column on parent).
11
+ * For regular branches: drops from main table + drafts table (if allowDrafts).
12
+ * Does NOT include FTS handling — callers must prepend ftsTriggerDrops() and
13
+ * append planFtsRebuild() when the branch is searchable.
14
+ */
15
+ export declare function generateDropColumn(seed: Seed, alias: string): string[];
16
+ /**
17
+ * RENAME COLUMN statements for a branch alias change.
18
+ * Updates main table + drafts (if allowDrafts).
19
+ * Does NOT handle FTS — callers must rebuild FTS separately (SQLite auto-updates
20
+ * trigger bodies on RENAME but the FTS virtual-table columns keep the old name).
21
+ */
22
+ export declare function generateRenameColumn(seed: Seed, fromAlias: string, toAlias: string): string[];
23
+ /**
24
+ * Full FTS rebuild: drop triggers + FTS table, then recreate them, then backfill.
25
+ * Returns an empty array if the seed has no searchable branches.
26
+ * The drop steps use IF EXISTS so this is safe to call even if FTS was already dropped.
27
+ */
28
+ export declare function planFtsRebuild(seed: Seed): string[];
29
+ export interface RetypeColumnPlan {
30
+ statements: string[];
31
+ ftsRebuildNeeded: boolean;
32
+ }
33
+ /**
34
+ * Generates statements to change a branch's SQL storage type using the
35
+ * add-new-column / copy / drop-old / rename approach.
36
+ * Includes FTS rebuild statements when the branch is (or becomes) searchable.
37
+ * `updatedSeed` must already reflect the new type on the renamed branch.
38
+ */
39
+ export declare function planRetypeColumn(seed: Seed, alias: string, newType: BranchType, updatedSeed: Seed): RetypeColumnPlan;
40
+ //# sourceMappingURL=seed-ddl-destructive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-ddl-destructive.d.ts","sourceRoot":"","sources":["../src/seed-ddl-destructive.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAU,UAAU,EAAE,MAAM,YAAY,CAAA;AAkC1D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAgBtD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBtE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ7F;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAiBnD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,IAAI,GAChB,gBAAgB,CAyClB"}
@@ -0,0 +1,149 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ import { junctionTableName, generateFtsTable, generateFtsTriggers } from './engine.js';
4
+ const BRANCH_SQL_TYPE = {
5
+ text: 'TEXT',
6
+ number: 'REAL',
7
+ boolean: 'INTEGER',
8
+ date: 'INTEGER',
9
+ json: 'TEXT',
10
+ richtext: 'TEXT',
11
+ file: 'TEXT',
12
+ tags: 'TEXT',
13
+ relation: 'TEXT',
14
+ repeater: 'TEXT',
15
+ };
16
+ /** Branch types that receive a B-tree index in generateIndexes (used by retype to drop/recreate). */
17
+ const INDEXABLE_TYPES = new Set(['text', 'number', 'date', 'boolean', 'relation']);
18
+ function searchBranches(seed) {
19
+ return seed.branches.filter(b => (b.type === 'text' || b.type === 'richtext') && b.policies?.search !== false);
20
+ }
21
+ function ftsTriggerDrops(slug) {
22
+ return [
23
+ `DROP TRIGGER IF EXISTS fts_${slug}_insert;`,
24
+ `DROP TRIGGER IF EXISTS fts_${slug}_update;`,
25
+ `DROP TRIGGER IF EXISTS fts_${slug}_delete;`,
26
+ `DROP TABLE IF EXISTS fts_${slug};`,
27
+ ];
28
+ }
29
+ /**
30
+ * Ordered DROP TABLE statements for a full hard-delete of a seed's tables.
31
+ * Order: junction drafts → junctions → FTS triggers → FTS table → drafts → main table.
32
+ * All statements use IF EXISTS (idempotent).
33
+ */
34
+ export function generateDropTable(seed) {
35
+ const stmts = [];
36
+ const { slug } = seed;
37
+ for (const branch of seed.branches) {
38
+ if (branch.type !== 'relation' || branch.multiple !== true)
39
+ continue;
40
+ const jt = junctionTableName(slug, branch.alias);
41
+ stmts.push(`DROP TABLE IF EXISTS ${jt}_drafts;`);
42
+ stmts.push(`DROP TABLE IF EXISTS ${jt};`);
43
+ }
44
+ stmts.push(...ftsTriggerDrops(slug));
45
+ stmts.push(`DROP TABLE IF EXISTS content_${slug}_drafts;`);
46
+ stmts.push(`DROP TABLE IF EXISTS content_${slug};`);
47
+ return stmts;
48
+ }
49
+ /**
50
+ * DROP COLUMN statements for a single branch (by alias).
51
+ * For multi-relation branches: drops the junction tables (no column on parent).
52
+ * For regular branches: drops from main table + drafts table (if allowDrafts).
53
+ * Does NOT include FTS handling — callers must prepend ftsTriggerDrops() and
54
+ * append planFtsRebuild() when the branch is searchable.
55
+ */
56
+ export function generateDropColumn(seed, alias) {
57
+ const stmts = [];
58
+ const { slug } = seed;
59
+ const branch = seed.branches.find(b => b.alias === alias);
60
+ if (!branch)
61
+ throw new Error(`Branch '${alias}' not found in seed '${slug}'`);
62
+ if (branch.type === 'relation' && branch.multiple === true) {
63
+ const jt = junctionTableName(slug, alias);
64
+ if (seed.allowDrafts)
65
+ stmts.push(`DROP TABLE IF EXISTS ${jt}_drafts;`);
66
+ stmts.push(`DROP TABLE IF EXISTS ${jt};`);
67
+ }
68
+ else {
69
+ stmts.push(`ALTER TABLE content_${slug} DROP COLUMN ${alias};`);
70
+ if (seed.allowDrafts)
71
+ stmts.push(`ALTER TABLE content_${slug}_drafts DROP COLUMN ${alias};`);
72
+ }
73
+ return stmts;
74
+ }
75
+ /**
76
+ * RENAME COLUMN statements for a branch alias change.
77
+ * Updates main table + drafts (if allowDrafts).
78
+ * Does NOT handle FTS — callers must rebuild FTS separately (SQLite auto-updates
79
+ * trigger bodies on RENAME but the FTS virtual-table columns keep the old name).
80
+ */
81
+ export function generateRenameColumn(seed, fromAlias, toAlias) {
82
+ const stmts = [];
83
+ const { slug } = seed;
84
+ stmts.push(`ALTER TABLE content_${slug} RENAME COLUMN ${fromAlias} TO ${toAlias};`);
85
+ if (seed.allowDrafts) {
86
+ stmts.push(`ALTER TABLE content_${slug}_drafts RENAME COLUMN ${fromAlias} TO ${toAlias};`);
87
+ }
88
+ return stmts;
89
+ }
90
+ /**
91
+ * Full FTS rebuild: drop triggers + FTS table, then recreate them, then backfill.
92
+ * Returns an empty array if the seed has no searchable branches.
93
+ * The drop steps use IF EXISTS so this is safe to call even if FTS was already dropped.
94
+ */
95
+ export function planFtsRebuild(seed) {
96
+ const rtBranches = searchBranches(seed);
97
+ if (rtBranches.length === 0)
98
+ return [];
99
+ const { slug } = seed;
100
+ const stmts = [...ftsTriggerDrops(slug)];
101
+ const ftsTable = generateFtsTable(seed);
102
+ if (ftsTable)
103
+ stmts.push(ftsTable);
104
+ stmts.push(...generateFtsTriggers(seed));
105
+ const cols = rtBranches.map(b => b.alias);
106
+ stmts.push(`INSERT INTO fts_${slug}(entry_id, ${cols.join(', ')}) SELECT id, ${cols.join(', ')} FROM content_${slug};`);
107
+ return stmts;
108
+ }
109
+ /**
110
+ * Generates statements to change a branch's SQL storage type using the
111
+ * add-new-column / copy / drop-old / rename approach.
112
+ * Includes FTS rebuild statements when the branch is (or becomes) searchable.
113
+ * `updatedSeed` must already reflect the new type on the renamed branch.
114
+ */
115
+ export function planRetypeColumn(seed, alias, newType, updatedSeed) {
116
+ const branch = seed.branches.find(b => b.alias === alias);
117
+ if (!branch)
118
+ throw new Error(`Branch '${alias}' not found in seed '${seed.slug}'`);
119
+ const { slug } = seed;
120
+ const newSqlType = BRANCH_SQL_TYPE[newType];
121
+ const tmpAlias = `${alias}_migrate`;
122
+ const stmts = [];
123
+ // Always drop FTS first to avoid "column referenced in trigger" errors on DROP COLUMN.
124
+ stmts.push(...ftsTriggerDrops(slug));
125
+ // Drop the branch index if it exists on the old column (required before DROP COLUMN).
126
+ if (INDEXABLE_TYPES.has(branch.type) && branch.policies?.filter !== false) {
127
+ stmts.push(`DROP INDEX IF EXISTS idx_${slug}_${alias};`);
128
+ }
129
+ // Main table: add migration column, copy data, drop old, rename.
130
+ stmts.push(`ALTER TABLE content_${slug} ADD COLUMN ${tmpAlias} ${newSqlType};`);
131
+ stmts.push(`UPDATE content_${slug} SET ${tmpAlias} = CAST(${alias} AS ${newSqlType});`);
132
+ stmts.push(`ALTER TABLE content_${slug} DROP COLUMN ${alias};`);
133
+ stmts.push(`ALTER TABLE content_${slug} RENAME COLUMN ${tmpAlias} TO ${alias};`);
134
+ // Recreate index for the renamed column if the new type warrants it.
135
+ if (INDEXABLE_TYPES.has(newType) && branch.policies?.filter !== false) {
136
+ stmts.push(`CREATE INDEX IF NOT EXISTS idx_${slug}_${alias} ON content_${slug}(${alias});`);
137
+ }
138
+ // Drafts table (no indexes to worry about).
139
+ if (seed.allowDrafts) {
140
+ stmts.push(`ALTER TABLE content_${slug}_drafts ADD COLUMN ${tmpAlias} ${newSqlType};`);
141
+ stmts.push(`UPDATE content_${slug}_drafts SET ${tmpAlias} = CAST(${alias} AS ${newSqlType});`);
142
+ stmts.push(`ALTER TABLE content_${slug}_drafts DROP COLUMN ${alias};`);
143
+ stmts.push(`ALTER TABLE content_${slug}_drafts RENAME COLUMN ${tmpAlias} TO ${alias};`);
144
+ }
145
+ // Rebuild FTS if the seed has searchable branches in the updated definition.
146
+ const rebuild = planFtsRebuild(updatedSeed);
147
+ stmts.push(...rebuild);
148
+ return { statements: stmts, ftsRebuildNeeded: rebuild.length > 0 };
149
+ }
@@ -0,0 +1,40 @@
1
+ import type { Seed } from './types.js';
2
+ /**
3
+ * Full create-from-scratch statement set for a seed. Mirrors the CLI's buildStatements.
4
+ * Order: parent table → indexes → draft table → FTS table → FTS triggers →
5
+ * per multi-relation: junction table → junction indexes → junction draft table.
6
+ * Callers that create several seeds must order seeds with sortSeedsByDependencies
7
+ * first so relation FK targets exist.
8
+ */
9
+ export declare function planCreateSeed(seed: Seed): string[];
10
+ export interface ExtendPlan {
11
+ statements: string[];
12
+ ftsRebuildNeeded: boolean;
13
+ }
14
+ /**
15
+ * Additive extension: given the columns that already exist on content_{slug}
16
+ * (from PRAGMA table_info, passed in by the caller), return ONLY the statements
17
+ * needed to add new branches — ADD COLUMN + indexes, plus junction tables for
18
+ * new multi-relation branches. Never drops or renames.
19
+ *
20
+ * FTS: SQLite cannot ALTER an fts5 table's columns. If a new text/richtext
21
+ * searchable branch was added, ftsRebuildNeeded=true signals the caller
22
+ * (sprint 03) to handle it — no DROP is emitted.
23
+ */
24
+ export declare function planExtendSeed(seed: Seed, existingColumns: Set<string>): ExtendPlan;
25
+ /**
26
+ * Destructive FTS rebuild (sprint 06). SQLite cannot ALTER an fts5 table's
27
+ * columns, so when a searchable branch is added, renamed, retyped, or dropped
28
+ * the only correct fix is to drop and recreate the `fts_{slug}` virtual table
29
+ * (and its insert/update/delete triggers), then backfill from `content_{slug}`.
30
+ *
31
+ * Returns an empty array when the seed has no searchable branches — in that
32
+ * case the caller should instead drop any leftover FTS table via
33
+ * generateDropTable's FTS statements. The returned statements are destructive
34
+ * and must run through ISchemaMutator.execDestructive, never execDdl.
35
+ *
36
+ * Order: drop triggers → drop fts table → recreate table → recreate triggers →
37
+ * backfill existing rows.
38
+ */
39
+ export declare function planFtsRebuild(seed: Seed): string[];
40
+ //# sourceMappingURL=seed-ddl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-ddl.d.ts","sourceRoot":"","sources":["../src/seed-ddl.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AActC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAanD;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,UAAU,CAqBnF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CA0BnD"}