@byline/cli 0.1.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 (251) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +23 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +72 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/doctor.d.ts +2 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +36 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/init.d.ts +16 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +76 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/context.d.ts +38 -0
  16. package/dist/context.d.ts.map +1 -0
  17. package/dist/context.js +37 -0
  18. package/dist/context.js.map +1 -0
  19. package/dist/index.d.ts +5 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +4 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/pg-url.d.ts +11 -0
  24. package/dist/lib/pg-url.d.ts.map +1 -0
  25. package/dist/lib/pg-url.js +22 -0
  26. package/dist/lib/pg-url.js.map +1 -0
  27. package/dist/manifest/deps.d.ts +29 -0
  28. package/dist/manifest/deps.d.ts.map +1 -0
  29. package/dist/manifest/deps.js +97 -0
  30. package/dist/manifest/deps.js.map +1 -0
  31. package/dist/manifest/env.d.ts +18 -0
  32. package/dist/manifest/env.d.ts.map +1 -0
  33. package/dist/manifest/env.js +38 -0
  34. package/dist/manifest/env.js.map +1 -0
  35. package/dist/phases/db-init.d.ts +3 -0
  36. package/dist/phases/db-init.d.ts.map +1 -0
  37. package/dist/phases/db-init.js +163 -0
  38. package/dist/phases/db-init.js.map +1 -0
  39. package/dist/phases/db.d.ts +11 -0
  40. package/dist/phases/db.d.ts.map +1 -0
  41. package/dist/phases/db.js +93 -0
  42. package/dist/phases/db.js.map +1 -0
  43. package/dist/phases/deps.d.ts +3 -0
  44. package/dist/phases/deps.d.ts.map +1 -0
  45. package/dist/phases/deps.js +115 -0
  46. package/dist/phases/deps.js.map +1 -0
  47. package/dist/phases/env.d.ts +3 -0
  48. package/dist/phases/env.d.ts.map +1 -0
  49. package/dist/phases/env.js +172 -0
  50. package/dist/phases/env.js.map +1 -0
  51. package/dist/phases/host.d.ts +3 -0
  52. package/dist/phases/host.d.ts.map +1 -0
  53. package/dist/phases/host.js +99 -0
  54. package/dist/phases/host.js.map +1 -0
  55. package/dist/phases/index.d.ts +7 -0
  56. package/dist/phases/index.d.ts.map +1 -0
  57. package/dist/phases/index.js +40 -0
  58. package/dist/phases/index.js.map +1 -0
  59. package/dist/phases/preflight.d.ts +4 -0
  60. package/dist/phases/preflight.d.ts.map +1 -0
  61. package/dist/phases/preflight.js +81 -0
  62. package/dist/phases/preflight.js.map +1 -0
  63. package/dist/phases/routes.d.ts +3 -0
  64. package/dist/phases/routes.d.ts.map +1 -0
  65. package/dist/phases/routes.js +145 -0
  66. package/dist/phases/routes.js.map +1 -0
  67. package/dist/phases/scaffold.d.ts +3 -0
  68. package/dist/phases/scaffold.d.ts.map +1 -0
  69. package/dist/phases/scaffold.js +113 -0
  70. package/dist/phases/scaffold.js.map +1 -0
  71. package/dist/phases/stub.d.ts +3 -0
  72. package/dist/phases/stub.d.ts.map +1 -0
  73. package/dist/phases/stub.js +25 -0
  74. package/dist/phases/stub.js.map +1 -0
  75. package/dist/phases/ui.d.ts +3 -0
  76. package/dist/phases/ui.d.ts.map +1 -0
  77. package/dist/phases/ui.js +93 -0
  78. package/dist/phases/ui.js.map +1 -0
  79. package/dist/phases/wire/index.d.ts +3 -0
  80. package/dist/phases/wire/index.d.ts.map +1 -0
  81. package/dist/phases/wire/index.js +67 -0
  82. package/dist/phases/wire/index.js.map +1 -0
  83. package/dist/phases/wire/root-tsx.d.ts +3 -0
  84. package/dist/phases/wire/root-tsx.d.ts.map +1 -0
  85. package/dist/phases/wire/root-tsx.js +57 -0
  86. package/dist/phases/wire/root-tsx.js.map +1 -0
  87. package/dist/phases/wire/server-ts.d.ts +3 -0
  88. package/dist/phases/wire/server-ts.d.ts.map +1 -0
  89. package/dist/phases/wire/server-ts.js +54 -0
  90. package/dist/phases/wire/server-ts.js.map +1 -0
  91. package/dist/phases/wire/shared.d.ts +34 -0
  92. package/dist/phases/wire/shared.d.ts.map +1 -0
  93. package/dist/phases/wire/shared.js +2 -0
  94. package/dist/phases/wire/shared.js.map +1 -0
  95. package/dist/phases/wire/start-ts.d.ts +3 -0
  96. package/dist/phases/wire/start-ts.d.ts.map +1 -0
  97. package/dist/phases/wire/start-ts.js +149 -0
  98. package/dist/phases/wire/start-ts.js.map +1 -0
  99. package/dist/phases/wire/tsconfig.d.ts +3 -0
  100. package/dist/phases/wire/tsconfig.d.ts.map +1 -0
  101. package/dist/phases/wire/tsconfig.js +105 -0
  102. package/dist/phases/wire/tsconfig.js.map +1 -0
  103. package/dist/phases/wire/vite-config.d.ts +3 -0
  104. package/dist/phases/wire/vite-config.d.ts.map +1 -0
  105. package/dist/phases/wire/vite-config.js +46 -0
  106. package/dist/phases/wire/vite-config.js.map +1 -0
  107. package/dist/prompts.d.ts +34 -0
  108. package/dist/prompts.d.ts.map +1 -0
  109. package/dist/prompts.js +49 -0
  110. package/dist/prompts.js.map +1 -0
  111. package/dist/runner.d.ts +5 -0
  112. package/dist/runner.d.ts.map +1 -0
  113. package/dist/runner.js +91 -0
  114. package/dist/runner.js.map +1 -0
  115. package/dist/state.d.ts +18 -0
  116. package/dist/state.d.ts.map +1 -0
  117. package/dist/state.js +68 -0
  118. package/dist/state.js.map +1 -0
  119. package/dist/templates/byline/admin.config.ts +41 -0
  120. package/dist/templates/byline/i18n.ts +47 -0
  121. package/dist/templates/byline/routes.ts +28 -0
  122. package/dist/templates/byline/seed.ts +19 -0
  123. package/dist/templates/byline/seeds/admin.ts +62 -0
  124. package/dist/templates/byline/server.config.ts +92 -0
  125. package/dist/templates/byline-examples/admin.config.ts +74 -0
  126. package/dist/templates/byline-examples/blocks/photo-block.ts +59 -0
  127. package/dist/templates/byline-examples/blocks/richtext-block.ts +35 -0
  128. package/dist/templates/byline-examples/collections/doc-example-flat-locale-all.ts +373 -0
  129. package/dist/templates/byline-examples/collections/doc-example-flat-locale-en.ts +283 -0
  130. package/dist/templates/byline-examples/collections/doc-example-tree-locale-all.ts +278 -0
  131. package/dist/templates/byline-examples/collections/doc-example-tree-locale-en.ts +205 -0
  132. package/dist/templates/byline-examples/collections/docs/admin.tsx +204 -0
  133. package/dist/templates/byline-examples/collections/docs/components/.gitkeep +0 -0
  134. package/dist/templates/byline-examples/collections/docs/components/feature-formatter.tsx +10 -0
  135. package/dist/templates/byline-examples/collections/docs/hooks/.gitkeep +0 -0
  136. package/dist/templates/byline-examples/collections/docs/index.ts +10 -0
  137. package/dist/templates/byline-examples/collections/docs/schema.ts +209 -0
  138. package/dist/templates/byline-examples/collections/docs-categories/admin.tsx +78 -0
  139. package/dist/templates/byline-examples/collections/docs-categories/components/.gitkeep +0 -0
  140. package/dist/templates/byline-examples/collections/docs-categories/hooks/.gitkeep +0 -0
  141. package/dist/templates/byline-examples/collections/docs-categories/index.ts +10 -0
  142. package/dist/templates/byline-examples/collections/docs-categories/schema.ts +33 -0
  143. package/dist/templates/byline-examples/collections/media/admin.tsx +188 -0
  144. package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +330 -0
  145. package/dist/templates/byline-examples/collections/media/components/media-thumbnail.tsx +63 -0
  146. package/dist/templates/byline-examples/collections/media/hooks/.gitkeep +0 -0
  147. package/dist/templates/byline-examples/collections/media/index.ts +10 -0
  148. package/dist/templates/byline-examples/collections/media/schema.ts +157 -0
  149. package/dist/templates/byline-examples/collections/news/admin.tsx +192 -0
  150. package/dist/templates/byline-examples/collections/news/components/.gitkeep +0 -0
  151. package/dist/templates/byline-examples/collections/news/hooks/.gitkeep +0 -0
  152. package/dist/templates/byline-examples/collections/news/index.ts +10 -0
  153. package/dist/templates/byline-examples/collections/news/schema.ts +91 -0
  154. package/dist/templates/byline-examples/collections/news-categories/admin.tsx +78 -0
  155. package/dist/templates/byline-examples/collections/news-categories/components/.gitkeep +0 -0
  156. package/dist/templates/byline-examples/collections/news-categories/hooks/.gitkeep +0 -0
  157. package/dist/templates/byline-examples/collections/news-categories/index.ts +10 -0
  158. package/dist/templates/byline-examples/collections/news-categories/schema.ts +33 -0
  159. package/dist/templates/byline-examples/collections/pages/admin.tsx +183 -0
  160. package/dist/templates/byline-examples/collections/pages/components/.gitkeep +0 -0
  161. package/dist/templates/byline-examples/collections/pages/hooks/.gitkeep +0 -0
  162. package/dist/templates/byline-examples/collections/pages/index.ts +10 -0
  163. package/dist/templates/byline-examples/collections/pages/schema.ts +96 -0
  164. package/dist/templates/byline-examples/components/length-indicator.tsx +138 -0
  165. package/dist/templates/byline-examples/components/pill.tsx +38 -0
  166. package/dist/templates/byline-examples/components/summary-length.tsx +39 -0
  167. package/dist/templates/byline-examples/fields/available-languages-field.ts +90 -0
  168. package/dist/templates/byline-examples/fields/lexical-richtext-compact.ts +88 -0
  169. package/dist/templates/byline-examples/i18n.ts +47 -0
  170. package/dist/templates/byline-examples/routes.ts +28 -0
  171. package/dist/templates/byline-examples/scripts/regenerate-media.ts +275 -0
  172. package/dist/templates/byline-examples/seed.ts +25 -0
  173. package/dist/templates/byline-examples/seeds/admin.ts +62 -0
  174. package/dist/templates/byline-examples/seeds/doc-categories.ts +71 -0
  175. package/dist/templates/byline-examples/seeds/docs.ts +293 -0
  176. package/dist/templates/byline-examples/seeds/news-categories.ts +71 -0
  177. package/dist/templates/byline-examples/server.config.ts +179 -0
  178. package/dist/templates/host/vite.config.ts +41 -0
  179. package/dist/templates/migrations/0000_condemned_kronos.sql +324 -0
  180. package/dist/templates/migrations/0001_sudden_phantom_reporter.sql +1 -0
  181. package/dist/templates/migrations/meta/0000_snapshot.json +2793 -0
  182. package/dist/templates/migrations/meta/0001_snapshot.json +2799 -0
  183. package/dist/templates/migrations/meta/_journal.json +20 -0
  184. package/dist/templates/routes/(byline)/admin/account/index.tsx +11 -0
  185. package/dist/templates/routes/(byline)/admin/collections/$collection/$id/api.tsx +16 -0
  186. package/dist/templates/routes/(byline)/admin/collections/$collection/$id/history.tsx +19 -0
  187. package/dist/templates/routes/(byline)/admin/collections/$collection/$id/index.tsx +16 -0
  188. package/dist/templates/routes/(byline)/admin/collections/$collection/create.tsx +11 -0
  189. package/dist/templates/routes/(byline)/admin/collections/$collection/index.tsx +11 -0
  190. package/dist/templates/routes/(byline)/admin/index.tsx +11 -0
  191. package/dist/templates/routes/(byline)/admin/permissions/index.tsx +11 -0
  192. package/dist/templates/routes/(byline)/admin/roles/$id/index.tsx +11 -0
  193. package/dist/templates/routes/(byline)/admin/roles/index.tsx +11 -0
  194. package/dist/templates/routes/(byline)/admin/route.tsx +11 -0
  195. package/dist/templates/routes/(byline)/admin/users/$id/index.tsx +11 -0
  196. package/dist/templates/routes/(byline)/admin/users/index.tsx +11 -0
  197. package/dist/templates/routes/(byline)/sign-in.tsx +11 -0
  198. package/dist/templates/ui-byline/blocks/photo-block/index.tsx +80 -0
  199. package/dist/templates/ui-byline/blocks/richtext-block/index.tsx +46 -0
  200. package/dist/templates/ui-byline/components/admonition/index.tsx +40 -0
  201. package/dist/templates/ui-byline/components/code/code-serializer.tsx +20 -0
  202. package/dist/templates/ui-byline/components/code/code.tsx +50 -0
  203. package/dist/templates/ui-byline/components/code/index.module.scss +137 -0
  204. package/dist/templates/ui-byline/components/code/index.ts +2 -0
  205. package/dist/templates/ui-byline/components/code/types.ts +5 -0
  206. package/dist/templates/ui-byline/components/code/utils.ts +20 -0
  207. package/dist/templates/ui-byline/components/heading-anchor/heading-anchor.tsx +69 -0
  208. package/dist/templates/ui-byline/components/heading-anchor/index.ts +1 -0
  209. package/dist/templates/ui-byline/components/heading-anchor/utils.ts +15 -0
  210. package/dist/templates/ui-byline/components/inline-image/index.tsx +109 -0
  211. package/dist/templates/ui-byline/components/layout/index.tsx +63 -0
  212. package/dist/templates/ui-byline/components/link/lang-link.tsx +70 -0
  213. package/dist/templates/ui-byline/components/link/link-field.tsx +298 -0
  214. package/dist/templates/ui-byline/components/link/link-lexical.tsx +191 -0
  215. package/dist/templates/ui-byline/components/list/index.ts +2 -0
  216. package/dist/templates/ui-byline/components/list/list-item.tsx +32 -0
  217. package/dist/templates/ui-byline/components/list/list.tsx +17 -0
  218. package/dist/templates/ui-byline/components/responsive-image/index.tsx +205 -0
  219. package/dist/templates/ui-byline/components/richtext-lexical/index.tsx +31 -0
  220. package/dist/templates/ui-byline/components/richtext-lexical/serialize/index.tsx +249 -0
  221. package/dist/templates/ui-byline/components/richtext-lexical/serialize/richtext-node-formats.ts +66 -0
  222. package/dist/templates/ui-byline/components/richtext-lexical/serialize/types.ts +48 -0
  223. package/dist/templates/ui-byline/components/richtext-lexical/serialize/utils.ts +15 -0
  224. package/dist/templates/ui-byline/components/table-cell/index.tsx +36 -0
  225. package/dist/templates/ui-byline/components/vimeo/index.tsx +21 -0
  226. package/dist/templates/ui-byline/components/youtube/index.tsx +22 -0
  227. package/dist/templates/ui-byline/render-blocks.tsx +71 -0
  228. package/dist/templates/ui-byline/types/i18n.ts +14 -0
  229. package/dist/templates/ui-byline/utils/image-sources.ts +102 -0
  230. package/dist/templates/ui-byline/utils/to-kebab-case.ts +5 -0
  231. package/dist/types.d.ts +54 -0
  232. package/dist/types.d.ts.map +1 -0
  233. package/dist/types.js +2 -0
  234. package/dist/types.js.map +1 -0
  235. package/dist/ui/diff.d.ts +4 -0
  236. package/dist/ui/diff.d.ts.map +1 -0
  237. package/dist/ui/diff.js +23 -0
  238. package/dist/ui/diff.js.map +1 -0
  239. package/dist/ui/grid.d.ts +7 -0
  240. package/dist/ui/grid.d.ts.map +1 -0
  241. package/dist/ui/grid.js +24 -0
  242. package/dist/ui/grid.js.map +1 -0
  243. package/dist/ui/logger.d.ts +14 -0
  244. package/dist/ui/logger.d.ts.map +1 -0
  245. package/dist/ui/logger.js +30 -0
  246. package/dist/ui/logger.js.map +1 -0
  247. package/dist/ui/snippet.d.ts +2 -0
  248. package/dist/ui/snippet.d.ts.map +1 -0
  249. package/dist/ui/snippet.js +7 -0
  250. package/dist/ui/snippet.js.map +1 -0
  251. package/package.json +69 -0
package/dist/runner.js ADDED
@@ -0,0 +1,91 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { execa } from 'execa';
4
+ import { renderDiff } from './ui/diff.js';
5
+ export async function runPhase(phase, ctx) {
6
+ const detected = await phase.detect(ctx);
7
+ if (detected === 'done' && !ctx.cliFlags.force) {
8
+ ctx.logger.success(`${phase.id} — already complete`);
9
+ return;
10
+ }
11
+ if (detected === 'blocked') {
12
+ ctx.logger.error(`${phase.id} — blocked; cannot proceed`);
13
+ return;
14
+ }
15
+ ctx.logger.step(`${phase.id} — planning`);
16
+ const plan = await phase.plan(ctx);
17
+ if (plan.writes.length === 0 && plan.commands.length === 0 && plan.notes.length === 0) {
18
+ ctx.logger.info(`${phase.id} — nothing to do`);
19
+ ctx.state.markPhaseComplete(phase.id);
20
+ ctx.state.flush();
21
+ return;
22
+ }
23
+ previewPlan(phase, plan, ctx);
24
+ if (ctx.dryRun) {
25
+ ctx.logger.info(`${phase.id} — dry-run; skipping apply`);
26
+ return;
27
+ }
28
+ const shouldApply = await decideApply(phase, ctx);
29
+ if (!shouldApply) {
30
+ ctx.logger.warn(`${phase.id} — skipped by user`);
31
+ return;
32
+ }
33
+ const result = await phase.apply(plan, ctx);
34
+ if (result.notes)
35
+ for (const n of result.notes)
36
+ ctx.logger.info(n);
37
+ if (result.state === 'done') {
38
+ ctx.state.markPhaseComplete(phase.id);
39
+ ctx.logger.success(`${phase.id} — done`);
40
+ }
41
+ else if (result.state === 'partial') {
42
+ ctx.logger.warn(`${phase.id} — partial; re-run to finish`);
43
+ }
44
+ else if (result.state === 'blocked') {
45
+ ctx.logger.error(`${phase.id} — blocked`);
46
+ }
47
+ ctx.state.flush();
48
+ }
49
+ function previewPlan(phase, plan, ctx) {
50
+ ctx.logger.raw('');
51
+ ctx.logger.raw(` ${phase.title}`);
52
+ for (const note of plan.notes)
53
+ ctx.logger.raw(` • ${note}`);
54
+ for (const w of plan.writes) {
55
+ const before = w.before ?? readIfExists(w.path);
56
+ const label = before ? 'modify' : 'create';
57
+ ctx.logger.raw(` ${label} ${w.path}`);
58
+ if (before !== w.contents) {
59
+ ctx.logger.raw(renderDiff(w.path, before, w.contents));
60
+ }
61
+ }
62
+ for (const c of plan.commands) {
63
+ ctx.logger.raw(` run ${c.command} ${c.args.join(' ')}`);
64
+ }
65
+ ctx.logger.raw('');
66
+ }
67
+ async function decideApply(phase, ctx) {
68
+ if (ctx.apply && phase.defaultMode === 'confirm')
69
+ return true;
70
+ if (phase.defaultMode === 'auto' && ctx.apply !== false)
71
+ return true;
72
+ if (ctx.yes)
73
+ return true;
74
+ return ctx.prompter.confirm({
75
+ message: `Apply ${phase.id} changes?`,
76
+ defaultValue: phase.defaultMode === 'auto',
77
+ });
78
+ }
79
+ function readIfExists(path) {
80
+ return existsSync(path) ? readFileSync(path, 'utf8') : '';
81
+ }
82
+ export async function executePlan(plan, ctx) {
83
+ for (const w of plan.writes) {
84
+ mkdirSync(dirname(w.path), { recursive: true });
85
+ writeFileSync(w.path, w.contents, 'utf8');
86
+ }
87
+ for (const c of plan.commands) {
88
+ await execa(c.command, c.args, { cwd: c.cwd ?? ctx.cwd, stdio: 'inherit' });
89
+ }
90
+ }
91
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { InstallState, PhaseId } from './types.js';
2
+ export declare class StateStore {
3
+ private state;
4
+ private readonly path;
5
+ private dirty;
6
+ constructor(cwd: string);
7
+ private fresh;
8
+ private load;
9
+ get(): Readonly<InstallState>;
10
+ patch(partial: Partial<InstallState>): void;
11
+ patchAnswers(partial: InstallState['answers']): void;
12
+ markPhaseComplete(id: PhaseId): void;
13
+ isComplete(id: PhaseId): boolean;
14
+ setWireSubEdit(key: string, status: 'pending' | 'done' | 'manual' | 'skipped'): void;
15
+ flush(): void;
16
+ filePath(): string;
17
+ }
18
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAIvD,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;IAC7B,OAAO,CAAC,KAAK,CAAQ;gBAET,GAAG,EAAE,MAAM;IAKvB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,IAAI;IAWZ,GAAG,IAAI,QAAQ,CAAC,YAAY,CAAC;IAI7B,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI;IAK3C,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,GAAG,IAAI;IAKpD,iBAAiB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;IAOpC,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO;IAIhC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,IAAI;IAKpF,KAAK,IAAI,IAAI;IAMb,QAAQ,IAAI,MAAM;CAGnB"}
package/dist/state.js ADDED
@@ -0,0 +1,68 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ const STATE_FILE = '.byline-install.json';
4
+ export class StateStore {
5
+ state;
6
+ path;
7
+ dirty = false;
8
+ constructor(cwd) {
9
+ this.path = resolve(cwd, STATE_FILE);
10
+ this.state = this.load() ?? this.fresh();
11
+ }
12
+ fresh() {
13
+ return {
14
+ version: 1,
15
+ startedAt: new Date().toISOString(),
16
+ completedPhases: [],
17
+ answers: {},
18
+ wireSubEdits: {},
19
+ };
20
+ }
21
+ load() {
22
+ if (!existsSync(this.path))
23
+ return null;
24
+ try {
25
+ const raw = JSON.parse(readFileSync(this.path, 'utf8'));
26
+ if (raw.version !== 1)
27
+ return null;
28
+ return raw;
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ get() {
35
+ return this.state;
36
+ }
37
+ patch(partial) {
38
+ this.state = { ...this.state, ...partial };
39
+ this.dirty = true;
40
+ }
41
+ patchAnswers(partial) {
42
+ this.state.answers = { ...this.state.answers, ...partial };
43
+ this.dirty = true;
44
+ }
45
+ markPhaseComplete(id) {
46
+ if (!this.state.completedPhases.includes(id)) {
47
+ this.state.completedPhases.push(id);
48
+ this.dirty = true;
49
+ }
50
+ }
51
+ isComplete(id) {
52
+ return this.state.completedPhases.includes(id);
53
+ }
54
+ setWireSubEdit(key, status) {
55
+ this.state.wireSubEdits[key] = status;
56
+ this.dirty = true;
57
+ }
58
+ flush() {
59
+ if (!this.dirty)
60
+ return;
61
+ writeFileSync(this.path, `${JSON.stringify(this.state, null, 2)}\n`, 'utf8');
62
+ this.dirty = false;
63
+ }
64
+ filePath() {
65
+ return this.path;
66
+ }
67
+ }
68
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC,MAAM,UAAU,GAAG,sBAAsB,CAAA;AAEzC,MAAM,OAAO,UAAU;IACb,KAAK,CAAc;IACV,IAAI,CAAQ;IACrB,KAAK,GAAG,KAAK,CAAA;IAErB,YAAY,GAAW;QACrB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAA;IAC1C,CAAC;IAEO,KAAK;QACX,OAAO;YACL,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,eAAe,EAAE,EAAE;YACnB,OAAO,EAAE,EAAE;YACX,YAAY,EAAE,EAAE;SACjB,CAAA;IACH,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACvC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAiB,CAAA;YACvE,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;YAClC,OAAO,GAAG,CAAA;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,KAAK,CAAC,OAA8B;QAClC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,OAAO,EAAE,CAAA;QAC1C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAED,YAAY,CAAC,OAAgC;QAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;QAC1D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAED,iBAAiB,CAAC,EAAW;QAC3B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACnC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACnB,CAAC;IACH,CAAC;IAED,UAAU,CAAC,EAAW;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAChD,CAAC;IAED,cAAc,CAAC,GAAW,EAAE,MAAiD;QAC3E,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,MAAM,CAAA;QACrC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAM;QACvB,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC5E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;CACF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Registers Byline's client-side config (collection admin UI configs,
11
+ * field editors, i18n, routes) in the current module graph. Imported as
12
+ * a side-effect from `src/routes/__root.tsx` — that module runs in both
13
+ * the SSR render and client module graphs, so a single import there
14
+ * covers both contexts.
15
+ *
16
+ * This is the minimal scaffold: no example collections registered. Add
17
+ * collection schemas + admin configs to the `collections` and `admin`
18
+ * arrays as you create them.
19
+ */
20
+
21
+ import type { ClientConfig } from '@byline/core'
22
+ import { defineClientConfig } from '@byline/core'
23
+ import { RichTextField as LexicalRichTextField } from '@byline/richtext-lexical'
24
+
25
+ import { i18n } from './i18n.js'
26
+ import { DEFAULT_SERVER_URL, routes } from './routes.js'
27
+
28
+ const serverURL = import.meta.env.VITE_SERVER_URL || DEFAULT_SERVER_URL
29
+
30
+ export const config: ClientConfig = {
31
+ serverURL,
32
+ i18n,
33
+ routes,
34
+ collections: [],
35
+ admin: [],
36
+ fields: {
37
+ richText: { editor: LexicalRichTextField },
38
+ },
39
+ }
40
+
41
+ defineClientConfig(config)
@@ -0,0 +1,47 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Shared i18n locale configuration — no upstream dependencies, safe to import
11
+ * from both config entry-points and collection schema files without circular
12
+ * references.
13
+ *
14
+ * `interface` locales govern the CMS admin UI language.
15
+ * `content` locales govern the languages a document can be published in.
16
+ */
17
+
18
+ export interface LocaleDefinition {
19
+ code: string
20
+ label: string
21
+ }
22
+
23
+ /** Locales available in the CMS admin interface. */
24
+ export const interfaceLocales: LocaleDefinition[] = [
25
+ { code: 'en', label: 'English' },
26
+ { code: 'fr', label: 'Français' },
27
+ ]
28
+
29
+ /** Locales a document can be published in. */
30
+ export const contentLocales = [
31
+ { code: 'en', label: 'English' },
32
+ { code: 'fr', label: 'Français' },
33
+ { code: 'es', label: 'Español' },
34
+ { code: 'de', label: 'Deutsch' },
35
+ ] as const
36
+
37
+ /** Derived config object — passed directly to defineServerConfig / defineClientConfig. */
38
+ export const i18n = {
39
+ interface: {
40
+ defaultLocale: 'en',
41
+ locales: interfaceLocales.map((l) => l.code),
42
+ },
43
+ content: {
44
+ defaultLocale: 'en',
45
+ locales: contentLocales.map((l) => l.code),
46
+ },
47
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * URL segments for admin and (future) public API routes. Defaults of
11
+ * `/admin` and `/api` are applied automatically by `resolveRoutes()` —
12
+ * keys only need to be set here when overriding either default.
13
+ */
14
+
15
+ import type { RoutesConfig } from '@byline/core'
16
+
17
+ export const routes: Partial<RoutesConfig> = {
18
+ admin: '/admin',
19
+ api: '/api',
20
+ }
21
+
22
+ /**
23
+ * Fallback used by both server and admin entry points when no
24
+ * `VITE_SERVER_URL` env var is set. Each entry resolves the env var
25
+ * itself (Vite's `import.meta.env` on the client, Node's `process.env`
26
+ * on the server) and falls back to this literal.
27
+ */
28
+ export const DEFAULT_SERVER_URL = 'http://localhost:5173/'
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ // Initialize Byline config by importing the server config
10
+ import 'dotenv/config'
11
+ import './server.config.js'
12
+
13
+ import { seedAdmin } from './seeds/admin.js'
14
+
15
+ async function run() {
16
+ await seedAdmin()
17
+ }
18
+
19
+ run()
@@ -0,0 +1,62 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Super-admin bootstrap seed.
11
+ *
12
+ * Idempotent. Reads credentials from env (`BYLINE_SUPERADMIN_EMAIL`,
13
+ * `BYLINE_SUPERADMIN_PASSWORD`) and calls the built-in
14
+ * `seedSuperAdmin` helper from `@byline/admin/admin-users` against an
15
+ * `AdminStore` built on the application's single connection pool.
16
+ *
17
+ * The admin account it produces:
18
+ * - `is_super_admin: true` — bypasses every ability check
19
+ * - `is_enabled: true` — ready to sign in immediately
20
+ * - `is_email_verified: true` — skip the verification gate for bootstrap
21
+ *
22
+ * In any non-dev deployment: change the credentials in env, run the seed
23
+ * once, then immediately change the password from inside the admin UI.
24
+ */
25
+
26
+ import type { AdminStore } from '@byline/admin'
27
+ import { seedSuperAdmin } from '@byline/admin/admin-users'
28
+ import { getBylineCore } from '@byline/core'
29
+
30
+ export async function seedAdmin() {
31
+ const email = process.env.BYLINE_SUPERADMIN_EMAIL
32
+ const password = process.env.BYLINE_SUPERADMIN_PASSWORD
33
+
34
+ if (!email || !password) {
35
+ console.warn(
36
+ 'Skipping admin seed: BYLINE_SUPERADMIN_EMAIL and BYLINE_SUPERADMIN_PASSWORD ' +
37
+ 'must both be set (see .env.example).'
38
+ )
39
+ return
40
+ }
41
+
42
+ const bylineCore = getBylineCore<AdminStore>()
43
+
44
+ if (!bylineCore.adminStore) {
45
+ throw new Error(
46
+ 'seedAdmin: bylineCore.adminStore is not configured. ' +
47
+ 'Pass adminStore to initBylineCore() in byline/server.config.ts.'
48
+ )
49
+ }
50
+
51
+ const result = await seedSuperAdmin(bylineCore.adminStore, { email, password })
52
+
53
+ const parts: string[] = []
54
+ if (result.created.role) parts.push('role')
55
+ if (result.created.user) parts.push('user')
56
+ if (result.created.assignment) parts.push('assignment')
57
+ if (parts.length === 0) {
58
+ console.log(`Super-admin already present (${email}) — no changes.`)
59
+ } else {
60
+ console.log(`Super-admin seed: created ${parts.join(', ')} for ${email}.`)
61
+ }
62
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Server-side Byline bootstrap. Imported as a side-effect from
11
+ * `src/server.ts` (and from any seed / migration script that needs the
12
+ * configured runtime). Resolves the composed `BylineCore` and registers
13
+ * it on the process global via `initBylineCore()` — server-side callers
14
+ * read it back with `getBylineCore<AdminStore>()`.
15
+ *
16
+ * This is the minimal scaffold: no example collections registered. Add
17
+ * collection schemas to the `collections` array as you create them under
18
+ * `byline/collections/<name>/schema.ts`.
19
+ */
20
+
21
+ import { type AdminStore, registerAdminAbilities } from '@byline/admin'
22
+ import { JwtSessionProvider } from '@byline/admin/auth'
23
+ import { type BylineCore, initBylineCore } from '@byline/core'
24
+ import { pgAdapter } from '@byline/db-postgres'
25
+ import { createAdminStore } from '@byline/db-postgres/admin'
26
+ import { getAdminBylineClient } from '@byline/host-tanstack-start/integrations/byline-client'
27
+ import { lexicalEditorServer } from '@byline/richtext-lexical/server'
28
+ import { localStorageProvider } from '@byline/storage-local'
29
+
30
+ import { i18n } from './i18n.js'
31
+ import { DEFAULT_SERVER_URL, routes } from './routes.js'
32
+
33
+ const serverURL = process.env.VITE_SERVER_URL || DEFAULT_SERVER_URL
34
+
35
+ const collections: Parameters<typeof pgAdapter>[0]['collections'] = []
36
+
37
+ // HMR-safe singleton. Vite's program reload re-evaluates this module
38
+ // without disposing the previous module's resources — every reload
39
+ // would otherwise allocate a fresh pg `Pool` (max: 20) inside
40
+ // `pgAdapter`, the previous pool would orphan but stay alive, and
41
+ // after a handful of HMR cycles Postgres' `max_connections` is
42
+ // exhausted. Stashing the resolving `Promise` lets module reloads
43
+ // reuse the same pool. Production has no HMR so this guard is a no-op.
44
+ declare global {
45
+ // biome-ignore lint: globalThis augmentation requires `var` rather than `let`
46
+ var __bylineCoreSingleton__: Promise<BylineCore<AdminStore>> | undefined
47
+ }
48
+
49
+ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
50
+ const db = pgAdapter({
51
+ connectionString: process.env.DB_CONNECTION_STRING || '',
52
+ collections,
53
+ })
54
+
55
+ const adminStore = createAdminStore(db.drizzle)
56
+
57
+ const signingSecret = process.env.BYLINE_JWT_SECRET
58
+ if (!signingSecret || signingSecret.length < 32) {
59
+ throw new Error(
60
+ 'BYLINE_JWT_SECRET must be set and carry at least 32 bytes of entropy. ' +
61
+ 'Generate one with `openssl rand -base64 48` and add it to your .env.'
62
+ )
63
+ }
64
+
65
+ const sessionProvider = new JwtSessionProvider({
66
+ store: adminStore,
67
+ signingSecret,
68
+ })
69
+
70
+ const core = await initBylineCore<AdminStore>({
71
+ serverURL,
72
+ i18n,
73
+ routes,
74
+ collections,
75
+ db,
76
+ adminStore,
77
+ storage: localStorageProvider({
78
+ uploadDir: './public/uploads',
79
+ baseUrl: '/uploads',
80
+ }),
81
+ sessionProvider,
82
+ fields: {
83
+ richText: { populate: lexicalEditorServer({ getClient: getAdminBylineClient }) },
84
+ },
85
+ })
86
+
87
+ registerAdminAbilities(core.abilities)
88
+ return core
89
+ }
90
+
91
+ globalThis.__bylineCoreSingleton__ ??= buildBylineCore()
92
+ await globalThis.__bylineCoreSingleton__
@@ -0,0 +1,74 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Registers Byline's client-side config (collection admin UI configs,
11
+ * field editors, i18n, routes) in the current module graph. Imported as
12
+ * a side-effect from `src/routes/__root.tsx` — that module runs in both
13
+ * the SSR render and client module graphs, so a single import there
14
+ * covers both contexts.
15
+ *
16
+ * In TanStack Start with Vite 6 the server entry (`src/server.ts`) and
17
+ * the SSR rendering context run in separate Vite environments, so
18
+ * importing this file from `src/server.ts` would NOT propagate the
19
+ * registration into the SSR render module graph.
20
+ */
21
+
22
+ import type { ClientConfig } from '@byline/core'
23
+ import { defineClientConfig } from '@byline/core'
24
+ import { RichTextField as LexicalRichTextField } from '@byline/richtext-lexical'
25
+
26
+ // Import `lexicalEditor` instead of (or alongside) `RichTextField` if you
27
+ // want to register the editor with site-wide custom settings. See the
28
+ // commented `richText` block below for the exact shape.
29
+ // import { lexicalEditor } from '@byline/richtext-lexical'
30
+
31
+ import { Docs, DocsAdmin } from './collections/docs/index.js'
32
+ import { DocsCategories, DocsCategoriesAdmin } from './collections/docs-categories/index.js'
33
+ import { Media, MediaAdmin } from './collections/media/index.js'
34
+ import { News, NewsAdmin } from './collections/news/index.js'
35
+ import { NewsCategories, NewsCategoriesAdmin } from './collections/news-categories/index.js'
36
+ import { Pages, PagesAdmin } from './collections/pages/index.js'
37
+ import { i18n } from './i18n.js'
38
+ import { DEFAULT_SERVER_URL, routes } from './routes.js'
39
+
40
+ const serverURL = import.meta.env.VITE_SERVER_URL || DEFAULT_SERVER_URL
41
+
42
+ export const config: ClientConfig = {
43
+ serverURL,
44
+ i18n,
45
+ routes,
46
+ collections: [Docs, News, Pages, Media, DocsCategories, NewsCategories],
47
+ admin: [DocsAdmin, NewsAdmin, PagesAdmin, MediaAdmin, DocsCategoriesAdmin, NewsCategoriesAdmin],
48
+ fields: {
49
+ // Default registration — every `type: 'richText'` field gets the full
50
+ // Lexical feature set unless overridden per-field via
51
+ // `RichTextField.editorConfig` (see `byline/fields/lexical-richtext-compact.ts`
52
+ // for the per-field pattern).
53
+ richText: { editor: LexicalRichTextField },
54
+
55
+ // ---------------------------------------------------------------------
56
+ // Alternatively — register the editor with site-wide custom settings.
57
+ // The `configure` callback receives a deep clone of `defaultEditorConfig`,
58
+ // so mutating it is safe. Per-field `editorConfig` continues to take
59
+ // precedence over whatever is baked in here.
60
+ //
61
+ // richText: {
62
+ // editor: lexicalEditor((c) => {
63
+ // c.settings.options.tablePlugin = false
64
+ // c.settings.options.codeHighlightPlugin = false
65
+ // c.settings.options.admonitionPlugin = false
66
+ // c.settings.placeholderText = 'Start writing...'
67
+ // return c
68
+ // }),
69
+ // },
70
+ // ---------------------------------------------------------------------
71
+ },
72
+ }
73
+
74
+ defineClientConfig(config)
@@ -0,0 +1,59 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { type BlockData, type BlockFieldData, defineBlock } from '@byline/core'
10
+
11
+ export const PhotoBlock = defineBlock({
12
+ blockType: 'photoBlock',
13
+ label: 'Photo Block',
14
+ helpText: 'A block for displaying a photo with optional caption and alt text.',
15
+ fields: [
16
+ {
17
+ name: 'display',
18
+ label: 'Display',
19
+ type: 'select',
20
+ optional: true,
21
+ defaultValue: 'default',
22
+ helpText: 'Select a display option for the photo.',
23
+ options: [
24
+ { label: 'Default', value: 'default' },
25
+ { label: 'Wide', value: 'wide' },
26
+ { label: 'Full Width', value: 'full_width' },
27
+ ],
28
+ },
29
+ {
30
+ name: 'photo',
31
+ label: 'Photo',
32
+ type: 'relation',
33
+ targetCollection: 'media',
34
+ displayField: 'title',
35
+ optional: true,
36
+ },
37
+ { name: 'alt', label: 'Alt', type: 'text', localized: false },
38
+ {
39
+ name: 'caption',
40
+ label: 'Caption',
41
+ type: 'richText',
42
+ optional: true,
43
+ localized: true,
44
+ },
45
+ ],
46
+ })
47
+
48
+ /**
49
+ * Field-only data shape for the photo block — useful for forms or
50
+ * block-internal helpers.
51
+ */
52
+ export type PhotoBlockFields = BlockFieldData<typeof PhotoBlock>
53
+
54
+ /**
55
+ * Full block instance shape (`_id`, `_type` + fields) as it appears
56
+ * inside a document tree. Use this as the prop type for the photo
57
+ * block renderer.
58
+ */
59
+ export type PhotoBlockData = BlockData<typeof PhotoBlock>
@@ -0,0 +1,35 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { type BlockData, type BlockFieldData, defineBlock } from '@byline/core'
10
+
11
+ export const RichTextBlock = defineBlock({
12
+ blockType: 'richTextBlock',
13
+ label: 'Richtext Block',
14
+ helpText: 'A block for displaying a richtext editor.',
15
+ fields: [
16
+ {
17
+ name: 'richText',
18
+ label: 'Richtext',
19
+ type: 'richText',
20
+ localized: true,
21
+ },
22
+ {
23
+ name: 'constrainedWidth',
24
+ label: 'Constrained Width',
25
+ type: 'checkbox',
26
+ optional: true,
27
+ defaultValue: true,
28
+ helpText:
29
+ 'If enabled, the richtext content will be constrained to a maximum width for better readability.',
30
+ },
31
+ ],
32
+ })
33
+
34
+ export type RichTextBlockFields = BlockFieldData<typeof RichTextBlock>
35
+ export type RichTextBlockData = BlockData<typeof RichTextBlock>