@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,217 @@
1
+ // packages/create-notionx-app/src/registry/install.ts
2
+ //
3
+ // Core `notionx add <id>` logic. v1 of this module wires the
4
+ // minimal subset needed to make `notionx add docs` a working
5
+ // command; richer behaviour (Notion schema sync, codemods,
6
+ // file-content merge for `user`-owned files) lives in PR 4.
7
+ //
8
+ // The contract:
9
+ // 1. Read `.notionx/registry.json`.
10
+ // 2. Reject if the item is already installed, or if a
11
+ // `requires` dependency is missing.
12
+ // 3. Render the new item's files (using the project templates
13
+ // directory, which the CLI looks up next to its own package).
14
+ // 4. Re-render `lib/content/models.ts` so the new source shows
15
+ // up in `contentSources` / `managedContentSources`.
16
+ // 5. Write the new `registry.json` and a human-readable followup
17
+ // list (Notion env vars to set, D1 migrations to apply).
18
+ //
19
+ // Everything is pure async + returns a summary object; the CLI
20
+ // layer is responsible for printing the summary.
21
+ import { mkdir, writeFile } from "node:fs/promises";
22
+ import path from "node:path";
23
+ import { getOfficialItem, listOfficialItems } from "./registry-items.js";
24
+ import { loadRegistry } from "./load-registry.js";
25
+ import { renderContentSourceFiles } from "./render-content-source-files.js";
26
+ import { writeRegistryManifest } from "./registry-store.js";
27
+ import { readProjectMeta, rerenderModelsFile, } from "./project-meta.js";
28
+ export class InstallError extends Error {
29
+ code;
30
+ constructor(message, code) {
31
+ super(message);
32
+ this.code = code;
33
+ this.name = "InstallError";
34
+ }
35
+ }
36
+ /**
37
+ * Install a registry item into the project at `projectDir`.
38
+ *
39
+ * Throws `InstallError` on the common failure modes so the CLI
40
+ * can print a stable, human-readable error.
41
+ */
42
+ export async function installItem(input) {
43
+ const { projectDir, templatesDir, itemId } = input;
44
+ const item = getOfficialItem(itemId);
45
+ if (!item) {
46
+ throw new InstallError(`Unknown registry item "${itemId}". Run \`notionx add --list\` to see available items.`, "unknown-item");
47
+ }
48
+ const finalItem = input.params
49
+ ? { ...item, params: { ...item.params, ...input.params } }
50
+ : item;
51
+ // Load the current registry.
52
+ const loaded = await loadRegistry(projectDir);
53
+ const manifest = loaded.manifest;
54
+ // Idempotency: reject re-installing the same id. `notionx update`
55
+ // handles version upgrades; `notionx add` is for new items.
56
+ if (manifest.installed.some((i) => i.id === finalItem.id)) {
57
+ throw new InstallError(`"${finalItem.id}" is already installed in this project. ` +
58
+ `Use \`notionx update ${finalItem.id}\` to upgrade it, ` +
59
+ `or \`notionx remove ${finalItem.id}\` to uninstall.`, "already-installed");
60
+ }
61
+ // Dependency check: every `requires` entry must be satisfied by
62
+ // something already installed. We use simple semver-equality
63
+ // (only ">=X" today); PR 4 will add a `semver.satisfies` check
64
+ // for ranges.
65
+ for (const req of finalItem.requires ?? []) {
66
+ const dep = manifest.installed.find((i) => i.id === req.id);
67
+ if (!dep) {
68
+ throw new InstallError(`"${finalItem.id}" requires "${req.id}" to be installed first. ` +
69
+ `Run \`notionx add ${req.id}\` and try again.`, "missing-requirement");
70
+ }
71
+ }
72
+ // Compute the new installed record. We snapshot the rendered
73
+ // file paths so `notionx remove --purge` can delete the exact
74
+ // set that was written (no heuristic path matching).
75
+ const installed = {
76
+ id: finalItem.id,
77
+ kind: finalItem.kind,
78
+ version: finalItem.version,
79
+ source: finalItem.source,
80
+ params: finalItem.params,
81
+ installedAt: new Date().toISOString(),
82
+ };
83
+ // The list of "currently installed" used for token rendering
84
+ // is the existing list **plus** the new item (in the same
85
+ // alphabetical order so generated diffs are stable).
86
+ const installedForRender = [
87
+ ...manifest.installed,
88
+ installed,
89
+ ].sort((a, b) => a.id.localeCompare(b.id));
90
+ const project = await readProjectMeta(projectDir, manifest);
91
+ // For feature-module items, the install flips the manifest flag
92
+ // to `true` so `rerenderModelsFile` includes the corresponding
93
+ // internal singleton source. For content-source items, the
94
+ // flags are unchanged.
95
+ const internalSourcesForRender = computeInternalSourcesForInstall(manifest, finalItem);
96
+ // Render the new files (currently: just the list page).
97
+ const rendered = await renderContentSourceFiles({
98
+ projectDir,
99
+ templatesDir,
100
+ item: finalItem,
101
+ installed: installedForRender,
102
+ project,
103
+ internalSources: internalSourcesForRender,
104
+ });
105
+ // Re-render `lib/content/models.ts` so the new source is
106
+ // registered in `contentSources` / `managedContentSources`.
107
+ const modelsResult = await rerenderModelsFile({
108
+ projectDir,
109
+ templatesDir,
110
+ project,
111
+ installed: installedForRender,
112
+ internalSources: internalSourcesForRender,
113
+ });
114
+ // Snapshot the rendered file paths on the InstalledItem so
115
+ // `notionx remove --purge` can delete the exact set later.
116
+ installed.files = rendered.map((f) => f.projectRelativePath);
117
+ const followup = buildFollowupTasks(finalItem, manifest);
118
+ if (input.dryRun) {
119
+ return {
120
+ item: finalItem,
121
+ installed,
122
+ files: rendered.map((f) => ({
123
+ projectRelativePath: f.projectRelativePath,
124
+ ownership: f.ownership,
125
+ existed: f.existed,
126
+ })),
127
+ rerenderedModels: modelsResult.wrote,
128
+ wroteManifest: false,
129
+ followup,
130
+ };
131
+ }
132
+ // Write the rendered files.
133
+ for (const file of rendered) {
134
+ await mkdir(path.dirname(file.absolutePath), { recursive: true });
135
+ await writeFile(file.absolutePath, file.content, "utf8");
136
+ }
137
+ // Update the manifest: add the new InstalledItem, the
138
+ // `user`/`bridge` files to `managedFiles`, and flip the
139
+ // feature flag (if this is a feature-module item).
140
+ const nextManifest = {
141
+ ...manifest,
142
+ installed: installedForRender,
143
+ managedFiles: mergeManagedFilesForInstall(manifest, finalItem),
144
+ ...applyFeatureFlag(manifest, finalItem, true),
145
+ };
146
+ await writeRegistryManifest(projectDir, nextManifest);
147
+ return {
148
+ item: finalItem,
149
+ installed,
150
+ files: rendered.map((f) => ({
151
+ projectRelativePath: f.projectRelativePath,
152
+ ownership: f.ownership,
153
+ existed: f.existed,
154
+ })),
155
+ rerenderedModels: modelsResult.wrote,
156
+ wroteManifest: true,
157
+ followup,
158
+ };
159
+ }
160
+ // ---- internals ----
161
+ function buildFollowupTasks(item, _manifest) {
162
+ const out = [];
163
+ for (const env of item.capabilities.envVars ?? []) {
164
+ out.push(`Set \`${env}\` in .dev.vars (see Notion data source for the id).`);
165
+ }
166
+ for (const ds of item.capabilities.notionDataSources ?? []) {
167
+ out.push(`Create the matching Notion data source and set \`${ds}\` in .dev.vars.`);
168
+ }
169
+ if ((item.capabilities.d1Tables ?? []).length > 0) {
170
+ out.push(`Run \`wrangler d1 migrations apply <DB_NAME> --remote\` to create the new tables.`);
171
+ }
172
+ return out;
173
+ }
174
+ function mergeManagedFilesForInstall(manifest, item) {
175
+ const user = new Set(manifest.managedFiles.user);
176
+ const bridge = new Set(manifest.managedFiles.bridge);
177
+ const platform = new Set(manifest.managedFiles.platform);
178
+ for (const file of item.files) {
179
+ if (file.ownership === "user")
180
+ user.add(file.path);
181
+ else if (file.ownership === "bridge")
182
+ bridge.add(file.path);
183
+ else
184
+ platform.add(file.path);
185
+ }
186
+ return {
187
+ platform: [...platform].sort(),
188
+ bridge: [...bridge].sort(),
189
+ user: [...user].sort(),
190
+ };
191
+ }
192
+ /**
193
+ * Compute the `internalSources` flags to pass to the renderers
194
+ * during install. For feature-module items, the flag being
195
+ * installed flips to `true` so the generated `models.ts` includes
196
+ * the corresponding internal singleton source. For other kinds,
197
+ * the flags are unchanged from the manifest.
198
+ */
199
+ function computeInternalSourcesForInstall(manifest, item) {
200
+ const siteSettings = item.featureFlag === "enableSiteSettings"
201
+ ? true
202
+ : manifest.enableSiteSettings;
203
+ const blocks = item.featureFlag === "enableBlocks" ? true : manifest.enableBlocks;
204
+ return { siteSettings, blocks };
205
+ }
206
+ /**
207
+ * Return a partial manifest patch that flips the feature flag
208
+ * controlled by `item` to `value`. Returns `{}` for items without
209
+ * a `featureFlag` (content-source / platform-extension).
210
+ */
211
+ function applyFeatureFlag(_manifest, item, value) {
212
+ if (!item.featureFlag)
213
+ return {};
214
+ return { [item.featureFlag]: value };
215
+ }
216
+ export { listOfficialItems };
217
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/registry/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,168 @@
1
+ // packages/create-notionx-app/src/registry/install.test.ts
2
+ import { promises as fs } from "node:fs";
3
+ import { existsSync } from "node:fs";
4
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
+ import { resolveTemplatesDir } from "../render.js";
9
+ import { InstallError, installItem } from "./install.js";
10
+ import { REGISTRY_SCHEMA_V2 } from "./registry-types.js";
11
+ async function writePackageJson(projectDir, name, deps = {}) {
12
+ await writeFile(path.join(projectDir, "package.json"), JSON.stringify({
13
+ name,
14
+ version: "0.0.0",
15
+ private: true,
16
+ dependencies: { "@notionx/core": "^2.0.0", ...deps },
17
+ }), "utf8");
18
+ }
19
+ async function writeV2Manifest(projectDir, installed = [
20
+ {
21
+ id: "blog",
22
+ kind: "content-source",
23
+ version: 1,
24
+ source: { kind: "official", name: "@notionx/official" },
25
+ params: { contentSourceId: "blog" },
26
+ installedAt: "2026-06-10T00:00:00.000Z",
27
+ },
28
+ ], managedFiles = {}) {
29
+ await mkdir(path.join(projectDir, ".notionx"), { recursive: true });
30
+ await writeFile(path.join(projectDir, ".notionx/registry.json"), JSON.stringify({
31
+ $schema: REGISTRY_SCHEMA_V2,
32
+ projectKind: "notionx",
33
+ scaffoldVersion: "2.0.0",
34
+ notionxCore: "^2.0.0",
35
+ compat: { mode: "v2-native" },
36
+ registries: {},
37
+ installed,
38
+ managedFiles: {
39
+ platform: managedFiles.platform ?? ["package.json"],
40
+ bridge: managedFiles.bridge ?? ["lib/notionx/content-registry.ts"],
41
+ user: managedFiles.user ?? ["lib/content/models.ts", "app/blog/page.tsx"],
42
+ },
43
+ }), "utf8");
44
+ }
45
+ describe("installItem", () => {
46
+ let dir;
47
+ let templatesDir;
48
+ beforeEach(async () => {
49
+ dir = await mkdtemp(path.join(os.tmpdir(), "notionx-install-"));
50
+ await mkdir(path.join(dir, ".notionx"), { recursive: true });
51
+ templatesDir = await resolveTemplatesDir();
52
+ });
53
+ afterEach(async () => {
54
+ await rm(dir, { recursive: true, force: true });
55
+ });
56
+ it("installs docs into a v2 project, writing all content source files and updating the manifest", async () => {
57
+ await writePackageJson(dir, "demo");
58
+ await writeV2Manifest(dir);
59
+ const summary = await installItem({
60
+ projectDir: dir,
61
+ templatesDir,
62
+ itemId: "docs",
63
+ });
64
+ expect(summary.item.id).toBe("docs");
65
+ expect(summary.wroteManifest).toBe(true);
66
+ expect(summary.rerenderedModels).toBe(true);
67
+ expect(summary.files.map((f) => f.projectRelativePath)).toEqual([
68
+ "app/docs/page.tsx",
69
+ "app/docs/[slug]/page.tsx",
70
+ "app/api/docs/route.ts",
71
+ "app/api/docs/[slug]/route.ts",
72
+ ]);
73
+ // The list page landed on disk
74
+ const listPath = path.join(dir, "app/docs/page.tsx");
75
+ expect(existsSync(listPath)).toBe(true);
76
+ const content = await fs.readFile(listPath, "utf8");
77
+ // The list page references the new content source
78
+ expect(content).toContain('"docs"');
79
+ expect(content).toContain("/docs");
80
+ // The detail page also landed
81
+ expect(existsSync(path.join(dir, "app/docs/[slug]/page.tsx"))).toBe(true);
82
+ // API routes landed
83
+ expect(existsSync(path.join(dir, "app/api/docs/route.ts"))).toBe(true);
84
+ expect(existsSync(path.join(dir, "app/api/docs/[slug]/route.ts"))).toBe(true);
85
+ // The manifest was updated
86
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
87
+ const manifest = JSON.parse(manifestRaw);
88
+ expect(manifest.installed.map((i) => i.id).sort()).toEqual([
89
+ "blog",
90
+ "docs",
91
+ ]);
92
+ // InstalledItem stores the rendered file list
93
+ const docsRecord = manifest.installed.find((i) => i.id === "docs");
94
+ expect(docsRecord?.files).toEqual([
95
+ "app/docs/page.tsx",
96
+ "app/docs/[slug]/page.tsx",
97
+ "app/api/docs/route.ts",
98
+ "app/api/docs/[slug]/route.ts",
99
+ ]);
100
+ });
101
+ it("rejects re-installing an item that is already present", async () => {
102
+ await writePackageJson(dir, "demo");
103
+ await writeV2Manifest(dir);
104
+ await expect(installItem({ projectDir: dir, templatesDir, itemId: "blog" })).rejects.toThrow(/already installed/);
105
+ });
106
+ it("returns a followup list mentioning the NOTION_DOCS_DATA_SOURCE_ID env var", async () => {
107
+ await writePackageJson(dir, "demo");
108
+ await writeV2Manifest(dir);
109
+ const summary = await installItem({
110
+ projectDir: dir,
111
+ templatesDir,
112
+ itemId: "docs",
113
+ });
114
+ const joined = summary.followup.join("\n");
115
+ expect(joined).toContain("NOTION_DOCS_DATA_SOURCE_ID");
116
+ });
117
+ it("rejects unknown item ids with the unknown-item code", async () => {
118
+ await writePackageJson(dir, "demo");
119
+ await writeV2Manifest(dir);
120
+ try {
121
+ await installItem({
122
+ projectDir: dir,
123
+ templatesDir,
124
+ itemId: "nonexistent",
125
+ });
126
+ throw new Error("expected installItem to throw");
127
+ }
128
+ catch (err) {
129
+ expect(err).toBeInstanceOf(InstallError);
130
+ expect(err.code).toBe("unknown-item");
131
+ }
132
+ });
133
+ it("dry-run does not write any file or update the manifest", async () => {
134
+ await writePackageJson(dir, "demo");
135
+ await writeV2Manifest(dir);
136
+ const summary = await installItem({
137
+ projectDir: dir,
138
+ templatesDir,
139
+ itemId: "docs",
140
+ dryRun: true,
141
+ });
142
+ expect(summary.wroteManifest).toBe(false);
143
+ expect(summary.files.map((f) => f.projectRelativePath)).toEqual([
144
+ "app/docs/page.tsx",
145
+ "app/docs/[slug]/page.tsx",
146
+ "app/api/docs/route.ts",
147
+ "app/api/docs/[slug]/route.ts",
148
+ ]);
149
+ expect(existsSync(path.join(dir, "app/docs/page.tsx"))).toBe(false);
150
+ // Manifest unchanged
151
+ const manifestRaw = await fs.readFile(path.join(dir, ".notionx/registry.json"), "utf8");
152
+ const manifest = JSON.parse(manifestRaw);
153
+ expect(manifest.installed).toHaveLength(1);
154
+ expect(manifest.installed[0]?.id).toBe("blog");
155
+ });
156
+ it("preserves existing content sources when re-rendering models.ts", async () => {
157
+ await writePackageJson(dir, "demo");
158
+ await writeV2Manifest(dir);
159
+ await installItem({ projectDir: dir, templatesDir, itemId: "docs" });
160
+ const modelsPath = path.join(dir, "lib/content/models.ts");
161
+ const models = await fs.readFile(modelsPath, "utf8");
162
+ expect(models).toContain("blogSource");
163
+ expect(models).toContain("docsSource");
164
+ expect(models).toContain("siteSettingsSource");
165
+ expect(models).toContain("blocksSource");
166
+ });
167
+ });
168
+ //# sourceMappingURL=install.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.test.js","sourceRoot":"","sources":["../../src/registry/install.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ // packages/create-notionx-app/src/registry/load-registry.ts
2
+ //
3
+ // The single entry point for reading a project's registry state.
4
+ //
5
+ // Behaviour:
6
+ // - If `.notionx/registry.json` exists, read it directly.
7
+ // - Else, throw — the directory is not a notionx project.
8
+ import { readRegistryManifest, REGISTRY_FILE } from "./registry-store.js";
9
+ /**
10
+ * Load a project's registry. Returns the `RegistryManifest` plus a
11
+ * `managedFiles` snapshot (typed view of the manifest's `managedFiles`
12
+ * block).
13
+ *
14
+ * Throws when no `registry.json` exists in the directory.
15
+ */
16
+ export async function loadRegistry(projectDir) {
17
+ const manifest = await readRegistryManifest(projectDir);
18
+ if (manifest === null) {
19
+ throw new Error(`No Notionx project state found in ${projectDir}. ` +
20
+ `Expected ${REGISTRY_FILE}.`);
21
+ }
22
+ return { manifest, managedFiles: manifest.managedFiles };
23
+ }
24
+ //# sourceMappingURL=load-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-registry.js","sourceRoot":"","sources":["../../src/registry/load-registry.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,iEAAiE;AACjE,EAAE;AACF,aAAa;AACb,4DAA4D;AAC5D,4DAA4D;AAE5D,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG1E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,UAAU,CAAC,CAAC;IACxD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,qCAAqC,UAAU,IAAI;YACjD,YAAY,aAAa,GAAG,CAC/B,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,59 @@
1
+ // packages/create-notionx-app/src/registry/load-registry.test.ts
2
+ import { mkdir, mkdtemp, 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 { loadRegistry } from "./load-registry.js";
7
+ import { REGISTRY_FILE } from "./registry-store.js";
8
+ import { REGISTRY_SCHEMA_V2 } from "./registry-types.js";
9
+ describe("loadRegistry", () => {
10
+ let dir;
11
+ beforeEach(async () => {
12
+ dir = await mkdtemp(path.join(os.tmpdir(), "notionx-load-"));
13
+ await mkdir(path.join(dir, ".notionx"), { recursive: true });
14
+ });
15
+ afterEach(async () => {
16
+ await rm(dir, { recursive: true, force: true });
17
+ });
18
+ it("throws when no registry.json exists", async () => {
19
+ await expect(loadRegistry(dir)).rejects.toThrow(/registry\.json/);
20
+ });
21
+ it("reads a v2 manifest directly", async () => {
22
+ await writeFile(path.join(dir, REGISTRY_FILE), JSON.stringify({
23
+ $schema: REGISTRY_SCHEMA_V2,
24
+ projectKind: "notionx",
25
+ projectName: "demo",
26
+ scaffoldVersion: "1.0.0",
27
+ notionxCore: "^2.0.0",
28
+ defaultLocale: "en",
29
+ supportedLocales: ["en"],
30
+ enableSiteSettings: true,
31
+ enableBlocks: true,
32
+ enableAuth: true,
33
+ enableAdmin: true,
34
+ enablePages: true,
35
+ enableSearch: true,
36
+ contentSource: {
37
+ id: "blog",
38
+ title: "Blog",
39
+ fields: [{ key: "title", notionName: "Title" }],
40
+ },
41
+ compat: { mode: "v2-native" },
42
+ registries: {},
43
+ installed: [],
44
+ managedFiles: { platform: [], bridge: [], user: [] },
45
+ }), "utf8");
46
+ const result = await loadRegistry(dir);
47
+ expect(result.manifest.$schema).toBe(REGISTRY_SCHEMA_V2);
48
+ expect(result.manifest.projectName).toBe("demo");
49
+ expect(result.managedFiles.platform).toEqual([]);
50
+ });
51
+ it("throws on schema mismatch", async () => {
52
+ await writeFile(path.join(dir, REGISTRY_FILE), JSON.stringify({
53
+ $schema: "https://example.com/wrong.json",
54
+ projectKind: "notionx",
55
+ }), "utf8");
56
+ await expect(loadRegistry(dir)).rejects.toThrow(/unknown schema/);
57
+ });
58
+ });
59
+ //# sourceMappingURL=load-registry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-registry.test.js","sourceRoot":"","sources":["../../src/registry/load-registry.test.ts"],"names":[],"mappings":"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"}