@notionx/create-notionx-app 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +139 -0
  2. package/dist/answers.js +332 -0
  3. package/dist/answers.js.map +1 -0
  4. package/dist/cli-notionx.js +388 -0
  5. package/dist/cli-notionx.js.map +1 -0
  6. package/dist/cli-notionx.test.js +277 -0
  7. package/dist/cli-notionx.test.js.map +1 -0
  8. package/dist/diff.js +40 -0
  9. package/dist/diff.js.map +1 -0
  10. package/dist/diff.test.js +90 -0
  11. package/dist/diff.test.js.map +1 -0
  12. package/dist/index.js +99 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/locale-add/apply.js +39 -0
  15. package/dist/locale-add/apply.js.map +1 -0
  16. package/dist/locale-add/format.js +38 -0
  17. package/dist/locale-add/format.js.map +1 -0
  18. package/dist/locale-add/list.js +44 -0
  19. package/dist/locale-add/list.js.map +1 -0
  20. package/dist/locale-add/list.test.js +45 -0
  21. package/dist/locale-add/list.test.js.map +1 -0
  22. package/dist/locale-add/plan.js +128 -0
  23. package/dist/locale-add/plan.js.map +1 -0
  24. package/dist/locale-add/validate.js +46 -0
  25. package/dist/locale-add/validate.js.map +1 -0
  26. package/dist/metadata.js +41 -0
  27. package/dist/metadata.js.map +1 -0
  28. package/dist/notion-translation-sources/apply.js +61 -0
  29. package/dist/notion-translation-sources/apply.js.map +1 -0
  30. package/dist/notion-translation-sources/index.js +3 -0
  31. package/dist/notion-translation-sources/index.js.map +1 -0
  32. package/dist/notion-translation-sources/plan.js +33 -0
  33. package/dist/notion-translation-sources/plan.js.map +1 -0
  34. package/dist/notionx-source.js +142 -0
  35. package/dist/notionx-source.js.map +1 -0
  36. package/dist/notionx-source.test.js +144 -0
  37. package/dist/notionx-source.test.js.map +1 -0
  38. package/dist/password.js +18 -0
  39. package/dist/password.js.map +1 -0
  40. package/dist/presets.js +83 -0
  41. package/dist/presets.js.map +1 -0
  42. package/dist/presets.test.js +50 -0
  43. package/dist/presets.test.js.map +1 -0
  44. package/dist/prompt.js +218 -0
  45. package/dist/prompt.js.map +1 -0
  46. package/dist/provision/cloudflare.js +236 -0
  47. package/dist/provision/cloudflare.js.map +1 -0
  48. package/dist/provision/dependencies.js +219 -0
  49. package/dist/provision/dependencies.js.map +1 -0
  50. package/dist/provision/index.js +681 -0
  51. package/dist/provision/index.js.map +1 -0
  52. package/dist/provision/index.test.js +54 -0
  53. package/dist/provision/index.test.js.map +1 -0
  54. package/dist/provision/inspect.js +109 -0
  55. package/dist/provision/inspect.js.map +1 -0
  56. package/dist/provision/inspect.test.js +75 -0
  57. package/dist/provision/inspect.test.js.map +1 -0
  58. package/dist/provision/notion.js +1981 -0
  59. package/dist/provision/notion.js.map +1 -0
  60. package/dist/provision/notion.test.js +542 -0
  61. package/dist/provision/notion.test.js.map +1 -0
  62. package/dist/provision/ntn-credentials.js +198 -0
  63. package/dist/provision/ntn-credentials.js.map +1 -0
  64. package/dist/provision/options.js +15 -0
  65. package/dist/provision/options.js.map +1 -0
  66. package/dist/provision/password-hash.js +78 -0
  67. package/dist/provision/password-hash.js.map +1 -0
  68. package/dist/provision/prompts.js +115 -0
  69. package/dist/provision/prompts.js.map +1 -0
  70. package/dist/provision/repair.js +48 -0
  71. package/dist/provision/repair.js.map +1 -0
  72. package/dist/provision/repair.test.js +141 -0
  73. package/dist/provision/repair.test.js.map +1 -0
  74. package/dist/provision/shell.js +84 -0
  75. package/dist/provision/shell.js.map +1 -0
  76. package/dist/provision/wire.js +78 -0
  77. package/dist/provision/wire.js.map +1 -0
  78. package/dist/registry/doctor.js +181 -0
  79. package/dist/registry/doctor.js.map +1 -0
  80. package/dist/registry/doctor.test.js +180 -0
  81. package/dist/registry/doctor.test.js.map +1 -0
  82. package/dist/registry/install.js +217 -0
  83. package/dist/registry/install.js.map +1 -0
  84. package/dist/registry/install.test.js +168 -0
  85. package/dist/registry/install.test.js.map +1 -0
  86. package/dist/registry/load-registry.js +24 -0
  87. package/dist/registry/load-registry.js.map +1 -0
  88. package/dist/registry/load-registry.test.js +59 -0
  89. package/dist/registry/load-registry.test.js.map +1 -0
  90. package/dist/registry/migration-planner.js +204 -0
  91. package/dist/registry/migration-planner.js.map +1 -0
  92. package/dist/registry/migration-planner.test.js +340 -0
  93. package/dist/registry/migration-planner.test.js.map +1 -0
  94. package/dist/registry/migrations-store.js +125 -0
  95. package/dist/registry/migrations-store.js.map +1 -0
  96. package/dist/registry/migrations-store.test.js +163 -0
  97. package/dist/registry/migrations-store.test.js.map +1 -0
  98. package/dist/registry/migrations-types.js +25 -0
  99. package/dist/registry/migrations-types.js.map +1 -0
  100. package/dist/registry/project-meta.js +84 -0
  101. package/dist/registry/project-meta.js.map +1 -0
  102. package/dist/registry/registry-items.js +354 -0
  103. package/dist/registry/registry-items.js.map +1 -0
  104. package/dist/registry/registry-items.test.js +99 -0
  105. package/dist/registry/registry-items.test.js.map +1 -0
  106. package/dist/registry/registry-store.js +232 -0
  107. package/dist/registry/registry-store.js.map +1 -0
  108. package/dist/registry/registry-store.test.js +136 -0
  109. package/dist/registry/registry-store.test.js.map +1 -0
  110. package/dist/registry/registry-types.js +18 -0
  111. package/dist/registry/registry-types.js.map +1 -0
  112. package/dist/registry/registry-types.test.js +146 -0
  113. package/dist/registry/registry-types.test.js.map +1 -0
  114. package/dist/registry/render-content-source-files.js +158 -0
  115. package/dist/registry/render-content-source-files.js.map +1 -0
  116. package/dist/registry/render-multi-source.js +296 -0
  117. package/dist/registry/render-multi-source.js.map +1 -0
  118. package/dist/registry/render-multi-source.test.js +110 -0
  119. package/dist/registry/render-multi-source.test.js.map +1 -0
  120. package/dist/registry/text-utils.js +42 -0
  121. package/dist/registry/text-utils.js.map +1 -0
  122. package/dist/registry/uninstall.js +250 -0
  123. package/dist/registry/uninstall.js.map +1 -0
  124. package/dist/registry/uninstall.test.js +264 -0
  125. package/dist/registry/uninstall.test.js.map +1 -0
  126. package/dist/registry/update.js +280 -0
  127. package/dist/registry/update.js.map +1 -0
  128. package/dist/registry/update.test.js +229 -0
  129. package/dist/registry/update.test.js.map +1 -0
  130. package/dist/render.js +549 -0
  131. package/dist/render.js.map +1 -0
  132. package/dist/render.test.js +414 -0
  133. package/dist/render.test.js.map +1 -0
  134. package/dist/templates/.dev.vars.example.tmpl +32 -0
  135. package/dist/templates/.gitignore.tmpl +58 -0
  136. package/dist/templates/README.md.tmpl +417 -0
  137. package/dist/templates/app/[slug]/page.tsx.tmpl +55 -0
  138. package/dist/templates/app/admin/account/page.tsx.tmpl +18 -0
  139. package/dist/templates/app/admin/content-models/page.tsx.tmpl +6 -0
  140. package/dist/templates/app/admin/layout.tsx.tmpl +90 -0
  141. package/dist/templates/app/admin/loading.tsx.tmpl +6 -0
  142. package/dist/templates/app/admin/page.tsx.tmpl +17 -0
  143. package/dist/templates/app/api/auth/google/callback/route.ts.tmpl +3 -0
  144. package/dist/templates/app/api/auth/google/route.ts.tmpl +3 -0
  145. package/dist/templates/app/api/auth/verify-email/route.ts.tmpl +3 -0
  146. package/dist/templates/app/api/auth/viewer/route.ts.tmpl +3 -0
  147. package/dist/templates/app/api/health/route.ts.tmpl +3 -0
  148. package/dist/templates/app/api/{{contentSourceId}}/[slug]/route.ts.tmpl +27 -0
  149. package/dist/templates/app/api/{{contentSourceId}}/route.ts.tmpl +18 -0
  150. package/dist/templates/app/globals.css.tmpl +109 -0
  151. package/dist/templates/app/layout.tsx.tmpl +56 -0
  152. package/dist/templates/app/login/page.tsx.tmpl +154 -0
  153. package/dist/templates/app/page.fallback.tsx.tmpl +31 -0
  154. package/dist/templates/app/page.tsx.tmpl +42 -0
  155. package/dist/templates/app/register/page.tsx.tmpl +138 -0
  156. package/dist/templates/app/{{contentSourceListPath}}/[slug]/page.tsx.tmpl +113 -0
  157. package/dist/templates/app/{{contentSourceListPath}}/page.tsx.tmpl +74 -0
  158. package/dist/templates/components/content/post-card.tsx.tmpl +80 -0
  159. package/dist/templates/components/notion-blocks.tsx.tmpl +668 -0
  160. package/dist/templates/components/page-blocks/feature-grid-block.tsx.tmpl +68 -0
  161. package/dist/templates/components/page-blocks/hero-block.tsx.tmpl +73 -0
  162. package/dist/templates/components/page-blocks/latest-posts-block.tsx.tmpl +59 -0
  163. package/dist/templates/components/page-blocks/story-block.tsx.tmpl +70 -0
  164. package/dist/templates/components/page-blocks.fallback.tsx.tmpl +17 -0
  165. package/dist/templates/components/page-blocks.tsx.tmpl +32 -0
  166. package/dist/templates/components/search/search-dialog.tsx.tmpl +171 -0
  167. package/dist/templates/components/site/locale-switcher.tsx.tmpl +65 -0
  168. package/dist/templates/components/site/site-footer.tsx.tmpl +106 -0
  169. package/dist/templates/components/site/site-header.tsx.tmpl +80 -0
  170. package/dist/templates/components/site/site-shell.tsx.tmpl +20 -0
  171. package/dist/templates/components/site/theme-bootstrap.tsx.tmpl +51 -0
  172. package/dist/templates/components/theme-provider.tsx.tmpl +14 -0
  173. package/dist/templates/components/theme-toggle.tsx.tmpl +38 -0
  174. package/dist/templates/components/ui/accordion.tsx.tmpl +56 -0
  175. package/dist/templates/components/ui/alert.tsx.tmpl +59 -0
  176. package/dist/templates/components/ui/aspect-ratio.tsx.tmpl +8 -0
  177. package/dist/templates/components/ui/avatar.tsx.tmpl +44 -0
  178. package/dist/templates/components/ui/badge.tsx.tmpl +33 -0
  179. package/dist/templates/components/ui/button.tsx.tmpl +56 -0
  180. package/dist/templates/components/ui/card.tsx.tmpl +61 -0
  181. package/dist/templates/components/ui/checkbox.tsx.tmpl +28 -0
  182. package/dist/templates/components/ui/dialog.tsx.tmpl +104 -0
  183. package/dist/templates/components/ui/dropdown-menu.tsx.tmpl +183 -0
  184. package/dist/templates/components/ui/input.tsx.tmpl +21 -0
  185. package/dist/templates/components/ui/label.tsx.tmpl +25 -0
  186. package/dist/templates/components/ui/popover.tsx.tmpl +30 -0
  187. package/dist/templates/components/ui/radio-group.tsx.tmpl +44 -0
  188. package/dist/templates/components/ui/select.tsx.tmpl +150 -0
  189. package/dist/templates/components/ui/separator.tsx.tmpl +30 -0
  190. package/dist/templates/components/ui/sheet.tsx.tmpl +125 -0
  191. package/dist/templates/components/ui/skeleton.tsx.tmpl +15 -0
  192. package/dist/templates/components/ui/sonner.tsx.tmpl +30 -0
  193. package/dist/templates/components/ui/switch.tsx.tmpl +29 -0
  194. package/dist/templates/components/ui/table.tsx.tmpl +107 -0
  195. package/dist/templates/components/ui/tabs.tsx.tmpl +55 -0
  196. package/dist/templates/components/ui/textarea.tsx.tmpl +24 -0
  197. package/dist/templates/components/ui/tooltip.tsx.tmpl +30 -0
  198. package/dist/templates/components.json.tmpl +21 -0
  199. package/dist/templates/env.d.ts.tmpl +32 -0
  200. package/dist/templates/lib/admin/actions.ts.tmpl +43 -0
  201. package/dist/templates/lib/admin/context.tsx.tmpl +209 -0
  202. package/dist/templates/lib/admin/nav.ts.tmpl +23 -0
  203. package/dist/templates/lib/auth.config.fallback.ts.tmpl +10 -0
  204. package/dist/templates/lib/auth.config.ts.tmpl +45 -0
  205. package/dist/templates/lib/blocks/translations.ts.tmpl +44 -0
  206. package/dist/templates/lib/blog/translations.ts.tmpl +52 -0
  207. package/dist/templates/lib/content/models.ts.tmpl +53 -0
  208. package/dist/templates/lib/i18n/config.ts.tmpl +18 -0
  209. package/dist/templates/lib/i18n/index.ts.tmpl +1 -0
  210. package/dist/templates/lib/locale-contract/built-in.ts.tmpl +19 -0
  211. package/dist/templates/lib/locale-contract/index.ts.tmpl +3 -0
  212. package/dist/templates/lib/locale-contract/paths.ts.tmpl +29 -0
  213. package/dist/templates/lib/pages/model.ts.tmpl +16 -0
  214. package/dist/templates/lib/pages/source.ts.tmpl +566 -0
  215. package/dist/templates/lib/pages/translations.ts.tmpl +34 -0
  216. package/dist/templates/lib/search/config.fallback.ts.tmpl +11 -0
  217. package/dist/templates/lib/search/config.ts.tmpl +25 -0
  218. package/dist/templates/lib/site/config.ts.tmpl +120 -0
  219. package/dist/templates/lib/site/request-env.ts.tmpl +71 -0
  220. package/dist/templates/lib/site/settings.fallback.ts.tmpl +21 -0
  221. package/dist/templates/lib/site/settings.ts.tmpl +320 -0
  222. package/dist/templates/lib/site/translations.ts.tmpl +30 -0
  223. package/dist/templates/lib/utils.ts.tmpl +9 -0
  224. package/dist/templates/migrations/0001_init.sql.tmpl +57 -0
  225. package/dist/templates/migrations/0002_admin_seed.sql.tmpl +30 -0
  226. package/dist/templates/migrations/0003_search_index.sql.tmpl +29 -0
  227. package/dist/templates/next.config.ts.tmpl +18 -0
  228. package/dist/templates/package.json.tmpl +40 -0
  229. package/dist/templates/shims/cloudflare-workers-empty.mjs +4 -0
  230. package/dist/templates/shims/next-headers-empty.mjs +4 -0
  231. package/dist/templates/tests/smoke.test.ts.tmpl +83 -0
  232. package/dist/templates/tsconfig.json.tmpl +31 -0
  233. package/dist/templates/vite.config.ts.tmpl +53 -0
  234. package/dist/templates/vitest.config.ts.tmpl +13 -0
  235. package/dist/templates/worker/index.ts.tmpl +52 -0
  236. package/dist/templates/wrangler.jsonc.tmpl +44 -0
  237. package/dist/ui-presets.js +60 -0
  238. package/dist/ui-presets.js.map +1 -0
  239. package/package.json +60 -0
@@ -0,0 +1,125 @@
1
+ // packages/create-notionx-app/src/registry/migrations-store.ts
2
+ //
3
+ // Read / write `.notionx/migrations/`.
4
+ //
5
+ // Layout:
6
+ //
7
+ // .notionx/
8
+ // ├── registry.json
9
+ // └── migrations/
10
+ // ├── _meta.json
11
+ // ├── 0001_init.sql
12
+ // ├── 0002_add_blog_status.sql
13
+ // └── 0003_rename_eyebrow.notion-diff.json
14
+ //
15
+ // `_meta.json` is the **index**. The .sql / .notion-diff.json /
16
+ // .codemod.json files are the **payloads**. Both are append-only:
17
+ // once written, a file is never edited. To "undo" a migration, run
18
+ // another one that does the opposite.
19
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
20
+ import path from "node:path";
21
+ export const MIGRATIONS_DIR = ".notionx/migrations";
22
+ export const MIGRATIONS_META = "_meta.json";
23
+ export const MIGRATIONS_SCHEMA_V1 = "https://notionx.dev/schemas/migrations-meta.v1.json";
24
+ /**
25
+ * Empty `migrations/` directory shape. Used as the default when
26
+ * the directory doesn't exist yet.
27
+ */
28
+ export function emptyMeta(nextSequence = 1) {
29
+ return {
30
+ $schema: MIGRATIONS_SCHEMA_V1,
31
+ nextSequence,
32
+ history: [],
33
+ };
34
+ }
35
+ /**
36
+ * Read `_meta.json` from `<projectDir>/.notionx/migrations/`. Returns
37
+ * `null` when the directory doesn't exist (caller should treat as
38
+ * empty migrations history).
39
+ */
40
+ export async function readMigrationsMeta(projectDir) {
41
+ const filePath = path.join(projectDir, MIGRATIONS_DIR, MIGRATIONS_META);
42
+ try {
43
+ const raw = await readFile(filePath, "utf8");
44
+ return JSON.parse(raw);
45
+ }
46
+ catch (err) {
47
+ if (isENOENT(err))
48
+ return null;
49
+ throw err;
50
+ }
51
+ }
52
+ /**
53
+ * Write `_meta.json` atomically (write to `.tmp`, then rename).
54
+ */
55
+ export async function writeMigrationsMeta(projectDir, meta) {
56
+ const dir = path.join(projectDir, MIGRATIONS_DIR);
57
+ const target = path.join(dir, MIGRATIONS_META);
58
+ const tmp = `${target}.tmp`;
59
+ await mkdir(dir, { recursive: true });
60
+ await writeFile(tmp, `${JSON.stringify(meta, null, 2)}\n`, "utf8");
61
+ await rename(tmp, target);
62
+ }
63
+ /**
64
+ * Append a single migration entry. Returns the new meta + the
65
+ * sequence number assigned to this entry.
66
+ *
67
+ * Sequence number is the **previous** `nextSequence` value, zero-
68
+ * padded to 4 digits. The meta's `nextSequence` is then
69
+ * incremented by 1.
70
+ */
71
+ export async function appendMigration(projectDir, entry) {
72
+ const meta = (await readMigrationsMeta(projectDir)) ?? emptyMeta();
73
+ const seq = String(meta.nextSequence).padStart(4, "0");
74
+ const next = {
75
+ ...meta,
76
+ nextSequence: meta.nextSequence + 1,
77
+ history: [
78
+ ...meta.history,
79
+ {
80
+ ...entry,
81
+ sequence: seq,
82
+ generatedAt: new Date().toISOString(),
83
+ },
84
+ ],
85
+ };
86
+ await writeMigrationsMeta(projectDir, next);
87
+ return { meta: next, sequence: seq };
88
+ }
89
+ /**
90
+ * Mark an existing migration as applied. Idempotent.
91
+ */
92
+ export async function markMigrationApplied(projectDir, sequence) {
93
+ const meta = await readMigrationsMeta(projectDir);
94
+ if (!meta) {
95
+ throw new Error(`Cannot mark migration ${sequence} applied: .notionx/migrations/_meta.json does not exist.`);
96
+ }
97
+ const next = {
98
+ ...meta,
99
+ history: meta.history.map((entry) => entry.sequence === sequence && !entry.applied
100
+ ? { ...entry, applied: true, appliedAt: new Date().toISOString() }
101
+ : entry),
102
+ };
103
+ await writeMigrationsMeta(projectDir, next);
104
+ return next;
105
+ }
106
+ /**
107
+ * Write a migration payload file (the .sql or .notion-diff.json
108
+ * next to `_meta.json`). Atomic. The filename is supplied by the
109
+ * caller; we don't try to derive it from the entry.
110
+ */
111
+ export async function writeMigrationPayload(projectDir, filename, content) {
112
+ const dir = path.join(projectDir, MIGRATIONS_DIR);
113
+ const target = path.join(dir, filename);
114
+ const tmp = `${target}.tmp`;
115
+ await mkdir(dir, { recursive: true });
116
+ await writeFile(tmp, content, "utf8");
117
+ await rename(tmp, target);
118
+ }
119
+ function isENOENT(err) {
120
+ return (typeof err === "object" &&
121
+ err !== null &&
122
+ "code" in err &&
123
+ err.code === "ENOENT");
124
+ }
125
+ //# sourceMappingURL=migrations-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations-store.js","sourceRoot":"","sources":["../../src/registry/migrations-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ // packages/create-notionx-app/src/registry/migrations-store.test.ts
2
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { MIGRATIONS_DIR, appendMigration, emptyMeta, markMigrationApplied, readMigrationsMeta, writeMigrationPayload, writeMigrationsMeta, } from "./migrations-store.js";
7
+ describe("migrations-store", () => {
8
+ let dir;
9
+ beforeEach(async () => {
10
+ dir = await mkdtemp(path.join(os.tmpdir(), "notionx-mig-"));
11
+ });
12
+ afterEach(async () => {
13
+ await rm(dir, { recursive: true, force: true });
14
+ });
15
+ it("exposes the canonical .notionx/migrations/ path", () => {
16
+ expect(MIGRATIONS_DIR).toBe(".notionx/migrations");
17
+ });
18
+ it("returns null when _meta.json does not exist", async () => {
19
+ const meta = await readMigrationsMeta(dir);
20
+ expect(meta).toBeNull();
21
+ });
22
+ it("emptyMeta() starts with nextSequence=1 and empty history", () => {
23
+ const m = emptyMeta();
24
+ expect(m.nextSequence).toBe(1);
25
+ expect(m.history).toEqual([]);
26
+ });
27
+ it("writeMigrationsMeta + read round-trips", async () => {
28
+ const m = emptyMeta();
29
+ await writeMigrationsMeta(dir, m);
30
+ const read = await readMigrationsMeta(dir);
31
+ expect(read).toEqual(m);
32
+ });
33
+ it("appendMigration assigns 4-digit sequence numbers and bumps nextSequence", async () => {
34
+ await writeMigrationsMeta(dir, emptyMeta());
35
+ const { sequence, meta } = await appendMigration(dir, {
36
+ itemId: "blog",
37
+ itemKind: "content-source",
38
+ file: { kind: "d1-sql", filename: "0001_init.sql" },
39
+ applied: false,
40
+ label: "init",
41
+ });
42
+ expect(sequence).toBe("0001");
43
+ expect(meta.nextSequence).toBe(2);
44
+ expect(meta.history).toHaveLength(1);
45
+ expect(meta.history[0]?.sequence).toBe("0001");
46
+ expect(meta.history[0]?.itemId).toBe("blog");
47
+ });
48
+ it("appendMigration is monotonic across many calls", async () => {
49
+ await writeMigrationsMeta(dir, emptyMeta());
50
+ for (let i = 1; i <= 5; i++) {
51
+ const { sequence } = await appendMigration(dir, {
52
+ itemId: "blog",
53
+ itemKind: "content-source",
54
+ file: { kind: "d1-sql", filename: `000${i}_x.sql` },
55
+ applied: false,
56
+ });
57
+ expect(sequence).toBe(`000${i}`);
58
+ }
59
+ const meta = await readMigrationsMeta(dir);
60
+ expect(meta?.nextSequence).toBe(6);
61
+ expect(meta?.history.map((e) => e.sequence)).toEqual([
62
+ "0001",
63
+ "0002",
64
+ "0003",
65
+ "0004",
66
+ "0005",
67
+ ]);
68
+ });
69
+ it("markMigrationApplied is idempotent (only flips once)", async () => {
70
+ await writeMigrationsMeta(dir, emptyMeta());
71
+ await appendMigration(dir, {
72
+ itemId: "blog",
73
+ itemKind: "content-source",
74
+ file: { kind: "d1-sql", filename: "0001_x.sql" },
75
+ applied: false,
76
+ });
77
+ const first = await markMigrationApplied(dir, "0001");
78
+ expect(first.history[0]?.applied).toBe(true);
79
+ expect(first.history[0]?.appliedAt).toBeDefined();
80
+ // Wait a tick so the second call's `appliedAt` would differ if
81
+ // it actually re-wrote the timestamp. We assert the timestamp
82
+ // didn't change, proving idempotency.
83
+ const originalTimestamp = first.history[0]?.appliedAt;
84
+ await new Promise((resolve) => setTimeout(resolve, 5));
85
+ const second = await markMigrationApplied(dir, "0001");
86
+ expect(second.history[0]?.appliedAt).toBe(originalTimestamp);
87
+ });
88
+ it("markMigrationApplied throws when the directory is empty", async () => {
89
+ await expect(markMigrationApplied(dir, "0001")).rejects.toThrow(/does not exist/);
90
+ });
91
+ it("writeMigrationPayload writes the payload file alongside _meta.json", async () => {
92
+ await writeMigrationsMeta(dir, emptyMeta());
93
+ await writeMigrationPayload(dir, "0001_init.sql", "CREATE TABLE x (id INTEGER);");
94
+ const raw = await readFile(path.join(dir, MIGRATIONS_DIR, "0001_init.sql"), "utf8");
95
+ expect(raw).toContain("CREATE TABLE x");
96
+ });
97
+ it("migrations are append-only: re-writing the same sequence is not allowed by the store", async () => {
98
+ // We don't expose a "rewrite" API; ensure the public surface
99
+ // doesn't let us accidentally edit a previous migration. The
100
+ // only mutators are `writeMigrationsMeta` (whole-file) and
101
+ // `appendMigration` (always uses nextSequence). A test that
102
+ // calls writeMigrationsMeta with a "rewriting" intent has to
103
+ // opt out by writing the whole meta — and even then,
104
+ // `appendMigration` would never re-use a sequence.
105
+ await writeMigrationsMeta(dir, emptyMeta());
106
+ await appendMigration(dir, {
107
+ itemId: "blog",
108
+ itemKind: "content-source",
109
+ file: { kind: "d1-sql", filename: "0001_x.sql" },
110
+ applied: false,
111
+ });
112
+ const before = await readMigrationsMeta(dir);
113
+ const firstEntryBefore = before?.history[0];
114
+ // Second append must use sequence 0002, not 0001.
115
+ await appendMigration(dir, {
116
+ itemId: "blog",
117
+ itemKind: "content-source",
118
+ file: { kind: "d1-sql", filename: "0002_y.sql" },
119
+ applied: false,
120
+ });
121
+ const after = await readMigrationsMeta(dir);
122
+ const firstEntryAfter = after?.history[0];
123
+ // The first entry is byte-identical to its pre-second-append
124
+ // form (proving append-only for the first migration's record).
125
+ expect(firstEntryAfter).toEqual(firstEntryBefore);
126
+ expect(after?.history[1]?.sequence).toBe("0002");
127
+ });
128
+ });
129
+ describe("migrations-store: dir layout", () => {
130
+ let dir;
131
+ beforeEach(async () => {
132
+ dir = await mkdtemp(path.join(os.tmpdir(), "notionx-mig-dir-"));
133
+ });
134
+ afterEach(async () => {
135
+ await rm(dir, { recursive: true, force: true });
136
+ });
137
+ it("creates .notionx/migrations/ if missing", async () => {
138
+ await writeMigrationsMeta(dir, emptyMeta());
139
+ await writeMigrationPayload(dir, "0001_x.sql", "SELECT 1;");
140
+ const sub = path.join(dir, ".notionx/migrations");
141
+ const metaRaw = await readFile(path.join(sub, "_meta.json"), "utf8");
142
+ const payload = await readFile(path.join(sub, "0001_x.sql"), "utf8");
143
+ expect(metaRaw).toContain("nextSequence");
144
+ expect(payload).toBe("SELECT 1;");
145
+ });
146
+ it("rejects writing payload without prior dir creation when mkdir fails — actually the store creates it", async () => {
147
+ // Sanity: writeMigrationsMeta creates the dir, so a payload
148
+ // write that arrives first still works (the store calls
149
+ // mkdir({ recursive: true })).
150
+ await writeMigrationPayload(dir, "0001_x.sql", "SELECT 2;");
151
+ const raw = await readFile(path.join(dir, MIGRATIONS_DIR, "0001_x.sql"), "utf8");
152
+ expect(raw).toBe("SELECT 2;");
153
+ });
154
+ it("supports arbitrary filenames (the store does not enforce .sql extension)", async () => {
155
+ // The registry may ship a `.notion-diff.json` or `.codemod.json`
156
+ // payload. The store just writes whatever filename the caller
157
+ // gives.
158
+ await writeMigrationPayload(dir, "0001_add_author.notion-diff.json", JSON.stringify({ kind: "notion-field-add", property: "Author" }));
159
+ const raw = await readFile(path.join(dir, MIGRATIONS_DIR, "0001_add_author.notion-diff.json"), "utf8");
160
+ expect(raw).toContain("notion-field-add");
161
+ });
162
+ });
163
+ //# sourceMappingURL=migrations-store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations-store.test.js","sourceRoot":"","sources":["../../src/registry/migrations-store.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ // packages/create-notionx-app/src/registry/migrations-types.ts
2
+ //
3
+ // v2 Migration Protocol — the on-disk format for
4
+ // `.notionx/migrations/`.
5
+ //
6
+ // Modeled on Prisma's `migrations/` directory: every change to a
7
+ // project's schema (D1 or Notion) is an immutable, append-only
8
+ // file with a monotonically-increasing sequence number. Once a
9
+ // migration has been applied to a real environment, **it is
10
+ // never edited**. To reverse a change, the user runs
11
+ // `notionx update` to apply a *new* migration that undoes it.
12
+ //
13
+ // Three kinds of migration live alongside each other:
14
+ // - `.sql` — D1 schema change (run by `wrangler d1 migrations apply`)
15
+ // - `.notion.json`— Notion schema change (run by hand or via the
16
+ // Notion API in CI; PR 4 ships a *plan*, not the
17
+ // run)
18
+ // - `.codemod.json`— TypeScript code transformation (run by
19
+ // `notionx update` against the user's code)
20
+ //
21
+ // A single migration usually has exactly one file. We bundle
22
+ // them in `_meta.json` so the planner can apply them in order
23
+ // and the user can see what changed.
24
+ export {};
25
+ //# sourceMappingURL=migrations-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations-types.js","sourceRoot":"","sources":["../../src/registry/migrations-types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,iDAAiD;AACjD,0BAA0B;AAC1B,EAAE;AACF,iEAAiE;AACjE,+DAA+D;AAC/D,+DAA+D;AAC/D,4DAA4D;AAC5D,qDAAqD;AACrD,8DAA8D;AAC9D,EAAE;AACF,sDAAsD;AACtD,8EAA8E;AAC9E,mEAAmE;AACnE,oEAAoE;AACpE,0BAA0B;AAC1B,8DAA8D;AAC9D,+DAA+D;AAC/D,EAAE;AACF,6DAA6D;AAC7D,8DAA8D;AAC9D,qCAAqC"}
@@ -0,0 +1,84 @@
1
+ // packages/create-notionx-app/src/registry/project-meta.ts
2
+ //
3
+ // Shared helpers used by install / uninstall / update:
4
+ // - `readProjectMeta` reads `package.json` for the project name
5
+ // and assembles the `MultiSourceProject` shape the token
6
+ // builders need.
7
+ // - `rerenderModelsFile` re-renders `lib/content/models.ts` from
8
+ // its template using the current installed-item list.
9
+ //
10
+ // Both helpers lived as private duplicates in install.ts and
11
+ // update.ts (readProjectMeta) and in install.ts and uninstall.ts
12
+ // (rerenderModelsFile). They are collected here so there is one
13
+ // authoritative implementation.
14
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
15
+ import path from "node:path";
16
+ import { buildMultiSourceTokenMap } from "./render-multi-source.js";
17
+ /**
18
+ * Read `package.json` for the project name and assemble the
19
+ * `MultiSourceProject` shape. Falls back to the directory basename
20
+ * when `package.json` is missing or has no `name` field.
21
+ */
22
+ export async function readProjectMeta(projectDir, manifest) {
23
+ const pkgPath = path.join(projectDir, "package.json");
24
+ let projectName = path.basename(projectDir);
25
+ try {
26
+ const raw = await readFile(pkgPath, "utf8");
27
+ const parsed = JSON.parse(raw);
28
+ if (typeof parsed.name === "string" && parsed.name.length > 0) {
29
+ projectName = parsed.name;
30
+ }
31
+ }
32
+ catch {
33
+ // No package.json (or unreadable). Fall back to dir basename.
34
+ }
35
+ return {
36
+ projectName,
37
+ targetDir: projectDir,
38
+ defaultLocale: "en", // TODO: read from a future v2 i18n block
39
+ supportedLocales: ["en"],
40
+ notionxSource: manifest.notionxCore,
41
+ adminEmail: "admin@example.com", // placeholder; not used by add/remove/update
42
+ adminPassword: "", // placeholder; not used by add/remove/update
43
+ scaffoldVersion: manifest.scaffoldVersion,
44
+ };
45
+ }
46
+ /**
47
+ * Re-render `lib/content/models.ts` from its template so the
48
+ * `contentSources` / `managedContentSources` arrays stay in sync
49
+ * with the installed-item list and the current internal-source
50
+ * flags (`enableSiteSettings` / `enableBlocks`).
51
+ *
52
+ * Returns `{ wrote: false }` when the template file is missing
53
+ * (e.g. a project that doesn't ship the models template).
54
+ */
55
+ export async function rerenderModelsFile(input) {
56
+ const target = path.join(input.projectDir, "lib/content/models.ts");
57
+ const tplPath = path.join(input.templatesDir, "lib/content/models.ts.tmpl");
58
+ let tpl;
59
+ try {
60
+ tpl = await readFile(tplPath, "utf8");
61
+ }
62
+ catch {
63
+ return { wrote: false };
64
+ }
65
+ const tokens = buildMultiSourceTokenMap({
66
+ project: input.project,
67
+ installed: input.installed,
68
+ internalSources: input.internalSources,
69
+ });
70
+ const flat = {
71
+ contentSourceDeclarations: tokens.contentSourceDeclarations,
72
+ contentSourceSourcesVarNames: tokens.contentSourceSourcesVarNames,
73
+ internalSourceDeclarations: tokens.internalSourceDeclarations,
74
+ internalSourceVarNames: tokens.internalSourceVarNames,
75
+ };
76
+ const rendered = tpl.replace(/\{\{\s*([a-zA-Z][a-zA-Z0-9_]*)\s*\}\}/g, (full, key) => {
77
+ const v = flat[key];
78
+ return typeof v === "string" ? v : full;
79
+ });
80
+ await mkdir(path.dirname(target), { recursive: true });
81
+ await writeFile(target, rendered, "utf8");
82
+ return { wrote: true };
83
+ }
84
+ //# sourceMappingURL=project-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-meta.js","sourceRoot":"","sources":["../../src/registry/project-meta.ts"],"names":[],"mappings":"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"}