@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,232 @@
1
+ // packages/create-notionx-app/src/registry/registry-store.ts
2
+ //
3
+ // Read / write `.notionx/registry.json` atomically.
4
+ //
5
+ // Why atomic: a partial write would leave the project un-loadable on
6
+ // the next `notionx` command. We write to `<file>.tmp` first, then
7
+ // `rename` into place — `rename` is atomic on POSIX filesystems.
8
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
9
+ import path from "node:path";
10
+ import { REGISTRY_SCHEMA_V2, } from "./registry-types.js";
11
+ /**
12
+ * Canonical location of the v2 manifest, relative to a project root.
13
+ * Kept as a constant so the CLI and the doctor agree on the path.
14
+ */
15
+ export const REGISTRY_FILE = ".notionx/registry.json";
16
+ /**
17
+ * Read `registry.json` from `<projectDir>/.notionx/registry.json`.
18
+ *
19
+ * Returns `null` when the file does not exist. Throws on parse errors
20
+ * or schema mismatches — we never want to silently load garbage.
21
+ */
22
+ export async function readRegistryManifest(projectDir) {
23
+ const filePath = path.join(projectDir, REGISTRY_FILE);
24
+ let raw;
25
+ try {
26
+ raw = await readFile(filePath, "utf8");
27
+ }
28
+ catch (err) {
29
+ if (isENOENT(err))
30
+ return null;
31
+ throw err;
32
+ }
33
+ const parsed = JSON.parse(raw);
34
+ if (parsed.$schema !== REGISTRY_SCHEMA_V2) {
35
+ throw new Error(`Registry manifest at ${filePath} uses unknown schema "${parsed.$schema ?? "<missing>"}" (expected ${REGISTRY_SCHEMA_V2}).`);
36
+ }
37
+ return parsed;
38
+ }
39
+ /**
40
+ * Write the v2 manifest atomically.
41
+ *
42
+ * Strategy: write to `<file>.tmp` with `fsync` to make sure bytes hit
43
+ * disk, then `rename` over the target. If anything fails before the
44
+ * rename, the original `registry.json` (if any) is untouched.
45
+ */
46
+ export async function writeRegistryManifest(projectDir, manifest) {
47
+ const target = path.join(projectDir, REGISTRY_FILE);
48
+ const tmp = `${target}.tmp`;
49
+ await mkdir(path.dirname(target), { recursive: true });
50
+ await writeFile(tmp, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
51
+ await rename(tmp, target);
52
+ }
53
+ function isENOENT(err) {
54
+ return (typeof err === "object" &&
55
+ err !== null &&
56
+ "code" in err &&
57
+ err.code === "ENOENT");
58
+ }
59
+ /**
60
+ * Build the initial `RegistryManifest` for a freshly scaffolded
61
+ * project. Called by `render.ts` at the end of `create-notionx-app`.
62
+ *
63
+ * The manifest records:
64
+ * - the scaffold version + core dependency range
65
+ * - the primary content source the user picked
66
+ * - the default managed-files ownership map
67
+ * - a single `InstalledItem` for the primary content source
68
+ *
69
+ * `notionx add` will append to `installed`; `notionx update` will
70
+ * bump `scaffoldVersion` and rewrite `managedFiles` as needed.
71
+ */
72
+ export function buildInitialRegistryManifest(input) {
73
+ const officialSource = {
74
+ kind: "official",
75
+ name: "@notionx/official",
76
+ };
77
+ const primaryItem = {
78
+ id: input.contentSource.id,
79
+ kind: "content-source",
80
+ version: 1,
81
+ source: officialSource,
82
+ params: {
83
+ contentSourceId: input.contentSource.id,
84
+ ...(input.contentSource.id === "blog"
85
+ ? {}
86
+ : { basePath: `/${input.contentSource.id}` }),
87
+ },
88
+ installedAt: new Date().toISOString(),
89
+ };
90
+ // When the scaffold enables site-settings / blocks, register
91
+ // them as InstalledItem entries so `notionx remove` can find
92
+ // them. The file paths mirror the catalog declarations in
93
+ // `registry-items.ts` — kept in sync manually (the catalog is
94
+ // the source of truth; this is a snapshot for the manifest).
95
+ const installed = [primaryItem];
96
+ if (input.enableSiteSettings) {
97
+ installed.push({
98
+ id: "site-settings",
99
+ kind: "feature-module",
100
+ version: 1,
101
+ source: officialSource,
102
+ params: {},
103
+ installedAt: new Date().toISOString(),
104
+ files: ["lib/site/settings.ts"],
105
+ });
106
+ }
107
+ if (input.enableBlocks) {
108
+ installed.push({
109
+ id: "blocks",
110
+ kind: "feature-module",
111
+ version: 1,
112
+ source: officialSource,
113
+ params: {},
114
+ installedAt: new Date().toISOString(),
115
+ files: [
116
+ "components/page-blocks.tsx",
117
+ "components/page-blocks/hero-block.tsx",
118
+ "components/page-blocks/feature-grid-block.tsx",
119
+ "components/page-blocks/story-block.tsx",
120
+ "components/page-blocks/latest-posts-block.tsx",
121
+ ],
122
+ });
123
+ }
124
+ if (input.enableAuth) {
125
+ installed.push({
126
+ id: "auth",
127
+ kind: "feature-module",
128
+ version: 1,
129
+ source: officialSource,
130
+ params: {},
131
+ installedAt: new Date().toISOString(),
132
+ files: [
133
+ "lib/auth.config.ts",
134
+ "app/api/auth/google/route.ts",
135
+ "app/api/auth/google/callback/route.ts",
136
+ "app/api/auth/verify-email/route.ts",
137
+ "app/api/auth/viewer/route.ts",
138
+ "app/login/page.tsx",
139
+ "app/register/page.tsx",
140
+ ],
141
+ });
142
+ }
143
+ if (input.enableAdmin) {
144
+ installed.push({
145
+ id: "admin",
146
+ kind: "feature-module",
147
+ version: 1,
148
+ source: officialSource,
149
+ params: {},
150
+ installedAt: new Date().toISOString(),
151
+ files: [
152
+ "app/admin/layout.tsx",
153
+ "app/admin/page.tsx",
154
+ "app/admin/loading.tsx",
155
+ "app/admin/account/page.tsx",
156
+ "app/admin/content-models/page.tsx",
157
+ "lib/admin/nav.ts",
158
+ "lib/admin/actions.ts",
159
+ "lib/admin/context.tsx",
160
+ ],
161
+ });
162
+ }
163
+ if (input.enablePages) {
164
+ installed.push({
165
+ id: "pages",
166
+ kind: "feature-module",
167
+ version: 1,
168
+ source: officialSource,
169
+ params: {},
170
+ installedAt: new Date().toISOString(),
171
+ files: [
172
+ "lib/pages/model.ts",
173
+ "lib/pages/source.ts",
174
+ "app/[slug]/page.tsx",
175
+ "app/page.tsx",
176
+ ],
177
+ });
178
+ }
179
+ if (input.enableSearch) {
180
+ installed.push({
181
+ id: "search",
182
+ kind: "feature-module",
183
+ version: 1,
184
+ source: officialSource,
185
+ params: {},
186
+ installedAt: new Date().toISOString(),
187
+ files: [
188
+ "lib/search/config.ts",
189
+ "components/search/search-dialog.tsx",
190
+ "migrations/0003_search_index.sql",
191
+ ],
192
+ });
193
+ }
194
+ installed.sort((a, b) => a.id.localeCompare(b.id));
195
+ return {
196
+ $schema: REGISTRY_SCHEMA_V2,
197
+ projectKind: "notionx",
198
+ projectName: input.projectName,
199
+ scaffoldVersion: input.scaffoldVersion,
200
+ notionxCore: input.notionxCore,
201
+ defaultLocale: input.defaultLocale,
202
+ supportedLocales: [...input.supportedLocales],
203
+ enableSiteSettings: input.enableSiteSettings,
204
+ enableBlocks: input.enableBlocks,
205
+ enableAuth: input.enableAuth,
206
+ enableAdmin: input.enableAdmin,
207
+ enablePages: input.enablePages,
208
+ enableSearch: input.enableSearch,
209
+ contentSource: {
210
+ id: input.contentSource.id,
211
+ title: input.contentSource.title,
212
+ fields: input.contentSource.fields.map((f) => ({
213
+ key: f.key,
214
+ notionName: f.notionName,
215
+ })),
216
+ },
217
+ compat: { mode: "v2-native" },
218
+ registries: {
219
+ "@notionx/official": {
220
+ url: input.officialRegistryUrl ??
221
+ "https://registry.notionx.dev/official.json",
222
+ },
223
+ },
224
+ installed,
225
+ managedFiles: {
226
+ platform: [...input.managedFiles.platform],
227
+ bridge: [...input.managedFiles.bridge],
228
+ user: [...input.managedFiles.user],
229
+ },
230
+ };
231
+ }
232
+ //# sourceMappingURL=registry-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-store.js","sourceRoot":"","sources":["../../src/registry/registry-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ // packages/create-notionx-app/src/registry/registry-store.test.ts
2
+ import { mkdir, mkdtemp, readFile, rm, writeFile } 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 { REGISTRY_SCHEMA_V2 } from "./registry-types.js";
7
+ import { readRegistryManifest, REGISTRY_FILE, writeRegistryManifest, } from "./registry-store.js";
8
+ describe("registry-store", () => {
9
+ let dir;
10
+ beforeEach(async () => {
11
+ dir = await mkdtemp(path.join(os.tmpdir(), "notionx-registry-store-"));
12
+ });
13
+ afterEach(async () => {
14
+ await rm(dir, { recursive: true, force: true });
15
+ });
16
+ it("exposes the canonical .notionx/registry.json path", () => {
17
+ expect(REGISTRY_FILE).toBe(".notionx/registry.json");
18
+ });
19
+ it("returns null when registry.json does not exist", async () => {
20
+ const result = await readRegistryManifest(dir);
21
+ expect(result).toBeNull();
22
+ });
23
+ it("rejects a manifest that does not match the v2 schema URL", async () => {
24
+ // Intentionally construct a manifest with the wrong schema URL
25
+ // to verify the loader rejects it. We use `as unknown as` to
26
+ // bypass the literal-type narrowing — this fixture is *meant*
27
+ // to violate the type.
28
+ const manifest = {
29
+ $schema: "https://example.com/wrong-schema.json",
30
+ projectKind: "notionx",
31
+ projectName: "demo",
32
+ scaffoldVersion: "1.0.0",
33
+ notionxCore: "^2.0.0",
34
+ defaultLocale: "en",
35
+ supportedLocales: ["en"],
36
+ enableSiteSettings: true,
37
+ enableBlocks: true,
38
+ enableAuth: true,
39
+ enableAdmin: true,
40
+ enablePages: true,
41
+ enableSearch: true,
42
+ contentSource: {
43
+ id: "blog",
44
+ title: "Blog",
45
+ fields: [{ key: "title", notionName: "Title" }],
46
+ },
47
+ compat: { mode: "v2-native" },
48
+ registries: {},
49
+ installed: [],
50
+ managedFiles: { platform: [], bridge: [], user: [] },
51
+ };
52
+ await mkdir(path.join(dir, ".notionx"), { recursive: true });
53
+ await writeFile(path.join(dir, REGISTRY_FILE), JSON.stringify(manifest), "utf8");
54
+ await expect(readRegistryManifest(dir)).rejects.toThrow(/v2/);
55
+ });
56
+ it("round-trips a valid manifest through write + read", async () => {
57
+ const manifest = {
58
+ $schema: REGISTRY_SCHEMA_V2,
59
+ projectKind: "notionx",
60
+ projectName: "demo",
61
+ scaffoldVersion: "1.0.0",
62
+ notionxCore: "^2.0.0",
63
+ defaultLocale: "en",
64
+ supportedLocales: ["en"],
65
+ enableSiteSettings: true,
66
+ enableBlocks: true,
67
+ enableAuth: true,
68
+ enableAdmin: true,
69
+ enablePages: true,
70
+ enableSearch: true,
71
+ contentSource: {
72
+ id: "blog",
73
+ title: "Blog",
74
+ fields: [{ key: "title", notionName: "Title" }],
75
+ },
76
+ compat: { mode: "v2-native" },
77
+ registries: {
78
+ "@notionx/official": {
79
+ url: "https://registry.notionx.dev/official.json",
80
+ },
81
+ },
82
+ installed: [
83
+ {
84
+ id: "blog",
85
+ kind: "content-source",
86
+ version: 1,
87
+ source: { kind: "official", name: "@notionx/official" },
88
+ params: { contentSourceId: "blog" },
89
+ installedAt: "2026-06-10T08:00:00.000Z",
90
+ },
91
+ ],
92
+ managedFiles: {
93
+ platform: ["package.json", "wrangler.jsonc"],
94
+ bridge: ["worker/index.ts"],
95
+ user: ["app/page.tsx", "lib/content/models.ts"],
96
+ },
97
+ };
98
+ await writeRegistryManifest(dir, manifest);
99
+ const read = await readRegistryManifest(dir);
100
+ expect(read).toEqual(manifest);
101
+ });
102
+ it("uses an atomic write (write to .tmp, then rename)", async () => {
103
+ const manifest = {
104
+ $schema: REGISTRY_SCHEMA_V2,
105
+ projectKind: "notionx",
106
+ projectName: "demo",
107
+ scaffoldVersion: "1.0.0",
108
+ notionxCore: "^2.0.0",
109
+ defaultLocale: "en",
110
+ supportedLocales: ["en"],
111
+ enableSiteSettings: true,
112
+ enableBlocks: true,
113
+ enableAuth: true,
114
+ enableAdmin: true,
115
+ enablePages: true,
116
+ enableSearch: true,
117
+ contentSource: {
118
+ id: "blog",
119
+ title: "Blog",
120
+ fields: [{ key: "title", notionName: "Title" }],
121
+ },
122
+ compat: { mode: "v2-native" },
123
+ registries: {},
124
+ installed: [],
125
+ managedFiles: { platform: [], bridge: [], user: [] },
126
+ };
127
+ await writeRegistryManifest(dir, manifest);
128
+ // No leftover .tmp file
129
+ const entries = await readFile(path.join(dir, REGISTRY_FILE), "utf8");
130
+ expect(entries).toContain(REGISTRY_SCHEMA_V2);
131
+ // Final file is valid JSON
132
+ const parsed = JSON.parse(entries);
133
+ expect(parsed.$schema).toBe(REGISTRY_SCHEMA_V2);
134
+ });
135
+ });
136
+ //# sourceMappingURL=registry-store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-store.test.js","sourceRoot":"","sources":["../../src/registry/registry-store.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ // packages/create-notionx-app/src/registry/registry-types.ts
2
+ //
3
+ // v2 Registry Protocol types — the single source of truth for everything
4
+ // a project has installed, what version, from which registry, and how
5
+ // to migrate when those things change.
6
+ //
7
+ // Design notes:
8
+ // - This file has **no runtime dependencies** on Node APIs or other
9
+ // modules in the package, so it can be imported from anywhere
10
+ // (including the compiled `dist/`) without pulling in fs/path/etc.
11
+ // - All shapes are JSON-serializable. The `registry.json` file is
12
+ // written verbatim from a `RegistryManifest`.
13
+ /**
14
+ * Schema URL baked into `registry.json` so future readers can detect
15
+ * "this manifest uses an older or newer protocol version".
16
+ */
17
+ export const REGISTRY_SCHEMA_V2 = "https://notionx.dev/schemas/registry.v2.json";
18
+ //# sourceMappingURL=registry-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-types.js","sourceRoot":"","sources":["../../src/registry/registry-types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,uCAAuC;AACvC,EAAE;AACF,gBAAgB;AAChB,oEAAoE;AACpE,gEAAgE;AAChE,qEAAqE;AACrE,kEAAkE;AAClE,gDAAgD;AAEhD;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,8CAAuD,CAAC"}
@@ -0,0 +1,146 @@
1
+ // packages/create-notionx-app/src/registry/registry-types.test.ts
2
+ //
3
+ // Pure type-shape tests. No fs / no Node APIs. These exist to lock in
4
+ // the JSON shape we read/write on disk so a refactor that accidentally
5
+ // renames a field is caught immediately.
6
+ import { describe, expect, it } from "vitest";
7
+ import { REGISTRY_SCHEMA_V2, } from "./registry-types.js";
8
+ /** Minimal valid manifest fixture with overridable fields. */
9
+ function fixture(overrides = {}) {
10
+ return {
11
+ $schema: REGISTRY_SCHEMA_V2,
12
+ projectKind: "notionx",
13
+ projectName: "demo",
14
+ scaffoldVersion: "1.0.0",
15
+ notionxCore: "^2.0.0",
16
+ defaultLocale: "en",
17
+ supportedLocales: ["en"],
18
+ enableSiteSettings: true,
19
+ enableBlocks: true,
20
+ enableAuth: true,
21
+ enableAdmin: true,
22
+ enablePages: true,
23
+ enableSearch: true,
24
+ contentSource: {
25
+ id: "blog",
26
+ title: "Blog",
27
+ fields: [{ key: "title", notionName: "Title" }],
28
+ },
29
+ compat: { mode: "v2-native" },
30
+ registries: {},
31
+ installed: [],
32
+ managedFiles: { platform: [], bridge: [], user: [] },
33
+ ...overrides,
34
+ };
35
+ }
36
+ describe("RegistryManifest", () => {
37
+ it("uses the v2 schema URL", () => {
38
+ const manifest = fixture({
39
+ registries: {
40
+ "@notionx/official": { url: "https://registry.notionx.dev/official.json" },
41
+ },
42
+ });
43
+ expect(manifest.$schema).toBe("https://notionx.dev/schemas/registry.v2.json");
44
+ });
45
+ it("supports the legacy-vinext compatibility marker", () => {
46
+ const manifest = fixture({
47
+ notionxCore: "workspace:*",
48
+ compat: { mode: "legacy-vinext" },
49
+ });
50
+ expect(manifest.compat.mode).toBe("legacy-vinext");
51
+ expect(manifest.notionxCore).toBe("workspace:*");
52
+ });
53
+ it("preserves extras through the round-trip", () => {
54
+ const manifest = fixture({
55
+ extras: { customBindingName: "D1_AUTH_DB" },
56
+ });
57
+ expect(manifest.extras?.customBindingName).toBe("D1_AUTH_DB");
58
+ });
59
+ it("stores projectName and contentSource from the scaffold flow", () => {
60
+ const manifest = fixture({
61
+ projectName: "my-site",
62
+ contentSource: {
63
+ id: "docs",
64
+ title: "Docs",
65
+ fields: [
66
+ { key: "title", notionName: "Title" },
67
+ { key: "slug", notionName: "Slug" },
68
+ ],
69
+ },
70
+ });
71
+ expect(manifest.projectName).toBe("my-site");
72
+ expect(manifest.contentSource.id).toBe("docs");
73
+ expect(manifest.contentSource.fields).toHaveLength(2);
74
+ });
75
+ });
76
+ describe("InstalledItem", () => {
77
+ it("captures the four fields the loader snapshots", () => {
78
+ const installed = {
79
+ id: "blog",
80
+ kind: "content-source",
81
+ version: 1,
82
+ source: { kind: "official", name: "@notionx/official" },
83
+ params: { contentSourceId: "blog" },
84
+ installedAt: "2026-06-10T08:00:00.000Z",
85
+ };
86
+ expect(installed.id).toBe("blog");
87
+ expect(installed.kind).toBe("content-source");
88
+ expect(installed.source.kind).toBe("official");
89
+ });
90
+ });
91
+ describe("RegistryItem", () => {
92
+ it("models requires / supersedes / migrations as optional", () => {
93
+ const item = {
94
+ id: "search",
95
+ kind: "feature-module",
96
+ version: 1,
97
+ source: { kind: "official", name: "@notionx/official" },
98
+ publishedAt: "2026-06-10T00:00:00.000Z",
99
+ params: { scope: "docs" },
100
+ files: [],
101
+ capabilities: {},
102
+ migrations: [],
103
+ };
104
+ expect(item.requires).toBeUndefined();
105
+ expect(item.supersedes).toBeUndefined();
106
+ expect(item.migrations).toEqual([]);
107
+ });
108
+ it("supports content-source items with notion data sources", () => {
109
+ const blog = {
110
+ id: "blog",
111
+ kind: "content-source",
112
+ version: 1,
113
+ source: { kind: "official", name: "@notionx/official" },
114
+ publishedAt: "2026-06-01T00:00:00.000Z",
115
+ params: { contentSourceId: "blog" },
116
+ files: [
117
+ { path: "app/blog/page.tsx", ownership: "user" },
118
+ { path: "lib/content/models.ts", ownership: "bridge" },
119
+ ],
120
+ capabilities: {
121
+ publicRoutes: ["/blog", "/blog/[slug]"],
122
+ apiRoutes: ["/api/posts"],
123
+ envVars: ["NOTION_BLOG_DATA_SOURCE_ID"],
124
+ notionDataSources: ["NOTION_BLOG_DATA_SOURCE_ID"],
125
+ contentSourceIds: ["blog"],
126
+ },
127
+ migrations: [
128
+ {
129
+ from: "blog@1",
130
+ to: "blog@2",
131
+ steps: [
132
+ {
133
+ kind: "notion-field-add",
134
+ source: "NOTION_BLOG_DATA_SOURCE_ID",
135
+ property: "Author",
136
+ type: "people",
137
+ },
138
+ ],
139
+ },
140
+ ],
141
+ };
142
+ expect(blog.capabilities.contentSourceIds).toEqual(["blog"]);
143
+ expect(blog.migrations[0]?.steps[0]?.kind).toBe("notion-field-add");
144
+ });
145
+ });
146
+ //# sourceMappingURL=registry-types.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-types.test.js","sourceRoot":"","sources":["../../src/registry/registry-types.test.ts"],"names":[],"mappings":"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"}