@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,198 @@
1
+ // packages/create-notionx-app/src/provision/ntn-credentials.ts
2
+ //
3
+ // Resolve a Notion API token from the `ntn` CLI's local credentials,
4
+ // so the scaffolder can skip the "paste your token" step for users
5
+ // who have already done `ntn login`.
6
+ //
7
+ // Where `ntn` stores its token:
8
+ //
9
+ // - macOS: Keychain (service "notion-cli"). We read it with
10
+ // `security find-generic-password -s notion-cli -w`.
11
+ // The user may be prompted by macOS to allow the
12
+ // first read in a session, but subsequent reads are
13
+ // silent.
14
+ // - Linux: GNOME Keyring / KWallet via `secret-tool`. The
15
+ // attribute pair is `service notion-cli`. We try
16
+ // `secret-tool` first and fall back to
17
+ // `~/.config/notion/auth.json`.
18
+ // - Windows: Credential Manager (`cmdkey /list`). We do not
19
+ // implement Windows keychain reads here — the
20
+ // file-based fallback applies when the user has set
21
+ // `NOTION_KEYRING=0`.
22
+ // - File mode: When the user has exported `NOTION_KEYRING=0`
23
+ // before running `ntn login`, ntn writes a JSON file
24
+ // to `~/.config/notion/auth.json` containing the
25
+ // active workspace's token. We read that.
26
+ //
27
+ // All three return paths are best-effort: if the local credential
28
+ // store does not contain a Notion token (or we cannot read it), the
29
+ // caller falls back to the manual `secret_…` paste prompt.
30
+ import { readFile } from "node:fs/promises";
31
+ import { existsSync } from "node:fs";
32
+ import { join } from "node:path";
33
+ import { homedir, platform } from "node:os";
34
+ import { run, runNtn } from "./shell.js";
35
+ const KEYCHAIN_SERVICE = "notion-cli";
36
+ /**
37
+ * Try every well-known location in turn. Returns the first token that
38
+ * verifies as a live Notion API token, or `null` if nothing usable
39
+ * was found.
40
+ */
41
+ export async function readNtnToken() {
42
+ // 0. Honor an explicit env override first. (This is the path
43
+ // `ntn` itself uses internally.)
44
+ if (process.env.NOTION_API_TOKEN && process.env.NOTION_API_TOKEN.length > 0) {
45
+ return { token: process.env.NOTION_API_TOKEN, source: "ntn-env" };
46
+ }
47
+ // 1. Try the platform-native credential store.
48
+ const native = await readFromNativeStore();
49
+ if (native)
50
+ return native;
51
+ // 2. Fall back to the file-based store ntn uses when the user has
52
+ // disabled the keyring via `NOTION_KEYRING=0`.
53
+ const fileBased = await readFromAuthJson();
54
+ if (fileBased)
55
+ return fileBased;
56
+ return null;
57
+ }
58
+ async function readFromNativeStore() {
59
+ const p = platform();
60
+ if (p === "darwin") {
61
+ return readFromMacosKeychain();
62
+ }
63
+ if (p === "linux") {
64
+ return readFromLinuxKeyring();
65
+ }
66
+ // Windows: skip native store — not implemented.
67
+ return null;
68
+ }
69
+ async function readFromMacosKeychain() {
70
+ const r = await run("security", [
71
+ "find-generic-password",
72
+ "-s",
73
+ KEYCHAIN_SERVICE,
74
+ "-w",
75
+ ]);
76
+ if (r.code !== 0)
77
+ return null;
78
+ const token = r.stdout.trim();
79
+ if (!token)
80
+ return null;
81
+ // `ntn` stores its token in the account field as a UUID-like value,
82
+ // not in the password field. Some installations put it in the
83
+ // password — cover both.
84
+ if (looksLikeNotionToken(token)) {
85
+ return { token, source: "ntn-macos-keychain" };
86
+ }
87
+ const accountR = await run("security", [
88
+ "find-generic-password",
89
+ "-s",
90
+ KEYCHAIN_SERVICE,
91
+ "-g",
92
+ ]);
93
+ const accountMatch = accountR.stdout.match(/"acct"<blob>="([^"]+)"/);
94
+ const account = accountMatch?.[1]?.trim();
95
+ if (account && looksLikeNotionToken(account)) {
96
+ return { token: account, source: "ntn-macos-keychain" };
97
+ }
98
+ return null;
99
+ }
100
+ async function readFromLinuxKeyring() {
101
+ // `secret-tool` is the standard D-Bus Secret Service client on
102
+ // GNOME. It prompts the user the first time per session.
103
+ const r = await run("secret-tool", [
104
+ "lookup",
105
+ "service",
106
+ KEYCHAIN_SERVICE,
107
+ ]);
108
+ if (r.code !== 0 || !r.stdout.trim())
109
+ return null;
110
+ const token = r.stdout.trim();
111
+ if (!looksLikeNotionToken(token))
112
+ return null;
113
+ return { token, source: "ntn-linux-keyring" };
114
+ }
115
+ async function readFromAuthJson() {
116
+ const file = join(homedir(), ".config", "notion", "auth.json");
117
+ if (!existsSync(file))
118
+ return null;
119
+ let raw;
120
+ try {
121
+ raw = await readFile(file, "utf8");
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ let parsed;
127
+ try {
128
+ parsed = JSON.parse(raw);
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ // Flat schema: `{ "token": "ntn_…" }`
134
+ if (parsed.token && looksLikeNotionToken(parsed.token)) {
135
+ return { token: parsed.token, source: "ntn-auth-json" };
136
+ }
137
+ // Nested schema: `{ workspaces: { prod: { <id>: { token, name } } } }`
138
+ if (parsed.workspaces) {
139
+ for (const [envKey, wsMap] of Object.entries(parsed.workspaces)) {
140
+ if (!wsMap)
141
+ continue;
142
+ // Prefer the default workspace id, fall back to the first
143
+ // entry that has a token.
144
+ const preferredId = parsed.defaultWorkspaceIds?.[envKey] ??
145
+ parsed.defaultWorkspaceId ??
146
+ Object.keys(wsMap)[0];
147
+ const ordered = [
148
+ wsMap[preferredId],
149
+ ...Object.entries(wsMap)
150
+ .filter(([id]) => id !== preferredId)
151
+ .map(([, v]) => v),
152
+ ];
153
+ for (const entry of ordered) {
154
+ if (entry?.token && looksLikeNotionToken(entry.token)) {
155
+ return {
156
+ token: entry.token,
157
+ source: "ntn-auth-json",
158
+ workspace: entry.name,
159
+ };
160
+ }
161
+ }
162
+ }
163
+ }
164
+ return null;
165
+ }
166
+ /**
167
+ * Best-effort: ask the `ntn` CLI to confirm that *some* credentials
168
+ * are present. We use this to give a clear "Run `ntn login` first"
169
+ * hint when we fail to read the token directly.
170
+ */
171
+ export async function isNtnLoggedIn() {
172
+ // `ntn whoami` is read-only but still calls libuv's
173
+ // `uv_tty_init` on startup, so we keep the PTY-aware wrapper for
174
+ // it to actually exit 0 on hosts where the libuv TTY dance is
175
+ // strict.
176
+ const r = await runNtn(["whoami"]);
177
+ return r.code === 0;
178
+ }
179
+ function looksLikeNotionToken(s) {
180
+ // Notion integration tokens start with "secret_", "ntn_", or
181
+ // (for OAuth public integrations) a UUID-like bearer. The CLI
182
+ // stores its own OAuth token, so ntn_… is the common shape.
183
+ return /^(secret_[A-Za-z0-9]{20,}|ntn_[A-Za-z0-9]{20,}|[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,})$/.test(s);
184
+ }
185
+ /** Human-friendly source label for the status card. */
186
+ export function describeNtnSource(source) {
187
+ switch (source) {
188
+ case "ntn-macos-keychain":
189
+ return "macOS Keychain (service=notion-cli)";
190
+ case "ntn-linux-keyring":
191
+ return "Linux Secret Service (service=notion-cli)";
192
+ case "ntn-auth-json":
193
+ return "~/.config/notion/auth.json";
194
+ case "ntn-env":
195
+ return "NOTION_API_TOKEN env var";
196
+ }
197
+ }
198
+ //# sourceMappingURL=ntn-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntn-credentials.js","sourceRoot":"","sources":["../../src/provision/ntn-credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export function defaultProvisionMode(name) {
2
+ if (name === "repair") {
3
+ return {
4
+ name,
5
+ deploy: false,
6
+ allowRemoteMigrations: false,
7
+ };
8
+ }
9
+ return {
10
+ name,
11
+ deploy: true,
12
+ allowRemoteMigrations: true,
13
+ };
14
+ }
15
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/provision/options.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,oBAAoB,CAAC,IAAuB;IAC1D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,KAAK;YACb,qBAAqB,EAAE,KAAK;SAC7B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI;QACJ,MAAM,EAAE,IAAI;QACZ,qBAAqB,EAAE,IAAI;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ // packages/create-notionx-app/src/provision/password-hash.ts
2
+ //
3
+ // Hashes the admin password at scaffold time so it can be baked into
4
+ // `migrations/0002_admin_seed.sql` and verified by `@notionx/core`'s
5
+ // `verifyPassword` on the first admin login.
6
+ //
7
+ // Why a local copy instead of importing from `@notionx/core`:
8
+ // - The scaffolder is a Node CLI, not a Cloudflare Worker. It must
9
+ // run on Node 22+ for the user, in a place where the runtime
10
+ // package may not be installed yet (e.g. in CI for a brand-new
11
+ // project that doesn't exist on disk).
12
+ // - PBKDF2-SHA256 is a 30-line algorithm. Keeping a local copy is
13
+ // simpler than wiring a workspace import + build dep.
14
+ // - The generated project imports the *canonical* implementation
15
+ // from `@notionx/core`; the only thing that matters is that the
16
+ // wire format matches so `verifyPassword` can decode it.
17
+ //
18
+ // Wire format (must stay in lockstep with
19
+ // `packages/notionx/src/auth/passwords.ts#hashPassword`):
20
+ //
21
+ // pbkdf2_sha256$<iterations>$<salt-base64>$<derived-key-base64>
22
+ //
23
+ // The `iterations` field is technically variable, but the runtime
24
+ // caps it at 100000 because Cloudflare Workers WebCrypto refuses
25
+ // higher counts. We bake 100000 in here so the seed row verifies
26
+ // in the same Workers environment the rest of the app runs in.
27
+ const HASH_PREFIX = "pbkdf2_sha256";
28
+ const PBKDF2_ITERATIONS = 100_000;
29
+ const SALT_BYTES = 16;
30
+ const DERIVED_KEY_BITS = 256;
31
+ function bytesToBase64(bytes) {
32
+ // Manual base64 because `Buffer` exists in Node 22 but we want
33
+ // this module to also work in any other env (e.g. edge test
34
+ // runners) that has `btoa` but not `Buffer`. `btoa` is available
35
+ // in Node 16+ and all modern browsers.
36
+ let bin = "";
37
+ for (const b of bytes)
38
+ bin += String.fromCharCode(b);
39
+ return btoa(bin);
40
+ }
41
+ async function deriveBits(password, salt, iterations) {
42
+ // Node 22 exposes `crypto.subtle` globally. The scaffolder's
43
+ // package.json already requires Node >= 22, so we don't need a
44
+ // `node:crypto` import here.
45
+ const enc = new TextEncoder();
46
+ const baseKey = await crypto.subtle.importKey("raw", enc.encode(password), "PBKDF2", false, ["deriveBits"]);
47
+ const derived = await crypto.subtle.deriveBits({
48
+ name: "PBKDF2",
49
+ hash: "SHA-256",
50
+ salt: salt,
51
+ iterations,
52
+ }, baseKey, DERIVED_KEY_BITS);
53
+ return new Uint8Array(derived);
54
+ }
55
+ /**
56
+ * Hash `password` the same way `@notionx/core`'s `hashPassword` does.
57
+ *
58
+ * The output is a single string suitable for direct interpolation
59
+ * into a SQL literal — e.g.
60
+ *
61
+ * `INSERT INTO users (email, password_hash) VALUES ('admin@example.com', '${hash}')`
62
+ *
63
+ * `render.ts` calls this exactly once per scaffold run (for the
64
+ * admin password) and bakes the result into a migration file. The
65
+ * 100k PBKDF2 iteration count takes ~200ms on a developer laptop —
66
+ * visible in the spinner, but not annoying.
67
+ */
68
+ export async function hashPasswordForScaffold(password) {
69
+ const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES));
70
+ const derived = await deriveBits(password, salt, PBKDF2_ITERATIONS);
71
+ return [
72
+ HASH_PREFIX,
73
+ String(PBKDF2_ITERATIONS),
74
+ bytesToBase64(salt),
75
+ bytesToBase64(derived),
76
+ ].join("$");
77
+ }
78
+ //# sourceMappingURL=password-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-hash.js","sourceRoot":"","sources":["../../src/provision/password-hash.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,6CAA6C;AAC7C,EAAE;AACF,8DAA8D;AAC9D,qEAAqE;AACrE,iEAAiE;AACjE,mEAAmE;AACnE,2CAA2C;AAC3C,oEAAoE;AACpE,0DAA0D;AAC1D,mEAAmE;AACnE,oEAAoE;AACpE,6DAA6D;AAC7D,EAAE;AACF,0CAA0C;AAC1C,0DAA0D;AAC1D,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,kEAAkE;AAClE,iEAAiE;AACjE,iEAAiE;AACjE,+DAA+D;AAE/D,MAAM,WAAW,GAAG,eAAe,CAAC;AACpC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAClC,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,SAAS,aAAa,CAAC,KAAiB;IACtC,+DAA+D;IAC/D,4DAA4D;IAC5D,iEAAiE;IACjE,uCAAuC;IACvC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,QAAgB,EAChB,IAAgB,EAChB,UAAkB;IAElB,6DAA6D;IAC7D,+DAA+D;IAC/D,6BAA6B;IAC7B,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC3C,KAAK,EACL,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EACpB,QAAQ,EACR,KAAK,EACL,CAAC,YAAY,CAAC,CACf,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAC5C;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,IAA+B;QACrC,UAAU;KACX,EACD,OAAO,EACP,gBAAgB,CACjB,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACpE,OAAO;QACL,WAAW;QACX,MAAM,CAAC,iBAAiB,CAAC;QACzB,aAAa,CAAC,IAAI,CAAC;QACnB,aAAa,CAAC,OAAO,CAAC;KACvB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC"}
@@ -0,0 +1,115 @@
1
+ // packages/create-notionx-app/src/provision/prompts.ts
2
+ //
3
+ // Interactive (and silent) prompts for the post-render provisioning
4
+ // stage. Each prompt is a no-op when not running in a TTY (e.g. when
5
+ // the scaffolder is invoked with `--yes` or piped). Callers must
6
+ // tolerate `null` returns — they translate to "skip this step".
7
+ import * as p from "@clack/prompts";
8
+ const SAFE_NON_TTY = null;
9
+ /** "Enable Resend email verification? (y/N)". Skipped silently if !interactive. */
10
+ export async function promptResend(ctx) {
11
+ if (!ctx.interactive)
12
+ return null;
13
+ const enable = await p.confirm({
14
+ message: "Enable Resend email verification? (no = auth falls back to no-op)",
15
+ initialValue: false,
16
+ });
17
+ if (p.isCancel(enable) || !enable)
18
+ return null;
19
+ const apiKey = await p.password({
20
+ message: "Resend API key (re_…)",
21
+ validate: (v) => !v || v.trim().length === 0 ? "Required when enabling Resend" : undefined,
22
+ });
23
+ if (p.isCancel(apiKey))
24
+ return null;
25
+ const fromAddress = await p.text({
26
+ message: "Resend sender address",
27
+ placeholder: "no-reply@example.com",
28
+ initialValue: "no-reply@example.com",
29
+ validate: (v) => (/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v) ? undefined : "Must be an email address"),
30
+ });
31
+ if (p.isCancel(fromAddress))
32
+ return null;
33
+ return { apiKey: String(apiKey), fromAddress: String(fromAddress) };
34
+ }
35
+ /** "Enable Google sign-in? (y/N)". Skipped silently if !interactive. */
36
+ export async function promptGoogle(ctx, siteUrl) {
37
+ if (!ctx.interactive)
38
+ return null;
39
+ const enable = await p.confirm({
40
+ message: "Enable Google sign-in? (you'll need a Google Cloud OAuth client)",
41
+ initialValue: false,
42
+ });
43
+ if (p.isCancel(enable) || !enable)
44
+ return null;
45
+ const redirectUri = `${siteUrl.replace(/\/$/, "")}/api/auth/google/callback`;
46
+ p.log.info(`Add this redirect URI to your Google OAuth client:\n ${redirectUri}`);
47
+ const clientId = await p.text({
48
+ message: "Google OAuth Client ID",
49
+ validate: (v) => (v && v.trim().length > 0 ? undefined : "Required"),
50
+ });
51
+ if (p.isCancel(clientId))
52
+ return null;
53
+ const clientSecret = await p.password({
54
+ message: "Google OAuth Client Secret",
55
+ validate: (v) => (v && v.trim().length > 0 ? undefined : "Required"),
56
+ });
57
+ if (p.isCancel(clientSecret))
58
+ return null;
59
+ return {
60
+ clientId: String(clientId),
61
+ clientSecret: String(clientSecret),
62
+ redirectUri,
63
+ };
64
+ }
65
+ /**
66
+ * "Wire up Notion now?" — triggered when no `NOTION_API_TOKEN` env var
67
+ * is set and we couldn't auto-detect credentials from `ntn`. The
68
+ * caller can pass a `preloadedToken` (e.g. read from the ntn CLI's
69
+ * local keychain) to skip the token prompt entirely. Returns the
70
+ * token + parent page id the user types in, or null to skip.
71
+ */
72
+ export async function promptNotion(ctx, fields, preloadedToken, seedCount = 6) {
73
+ if (!ctx.interactive)
74
+ return null;
75
+ let apiToken = preloadedToken ?? "";
76
+ if (!apiToken) {
77
+ p.log.info("Notion: create an integration at https://www.notion.so/my-integrations, share a target page with it, then paste the token below. (Tip: run `ntn login` first and the scaffolder will pick up the token automatically.)");
78
+ const input = await p.password({
79
+ message: "Notion integration token (secret_…) — Enter to skip",
80
+ });
81
+ if (p.isCancel(input) || !input)
82
+ return null;
83
+ apiToken = String(input).trim();
84
+ }
85
+ p.log.info([
86
+ "Notion parent page:",
87
+ " 1. Create or choose a Notion page.",
88
+ " 2. Add the Notion CLI / integration connection to that page.",
89
+ " 3. Paste the page URL or page id below.",
90
+ "The scaffolder will create the blog database and 6 realistic sample posts under it.",
91
+ ].join("\n"));
92
+ const parentPageId = await p.text({
93
+ message: "Parent page URL or id (the page your integration can edit)",
94
+ placeholder: "https://www.notion.so/workspace/Page-00000000000000000000000000000000",
95
+ initialValue: preloadedToken ? "" : undefined,
96
+ validate: (v) => {
97
+ const id = extractNotionPageId(v ?? "");
98
+ return id ? undefined : "Paste a Notion page URL or a 32-char page id";
99
+ },
100
+ });
101
+ if (p.isCancel(parentPageId))
102
+ return null;
103
+ return {
104
+ apiToken,
105
+ parentPageId: extractNotionPageId(String(parentPageId)),
106
+ seedCount,
107
+ };
108
+ }
109
+ function extractNotionPageId(value) {
110
+ const compact = value.trim().replace(/-/g, "");
111
+ const matches = compact.match(/[0-9a-fA-F]{32}/g);
112
+ return matches?.at(-1)?.toLowerCase() ?? null;
113
+ }
114
+ export const _internal = { SAFE_NON_TTY };
115
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/provision/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { DEFAULT_ANSWERS } from "../prompt.js";
2
+ import { provision } from "./index.js";
3
+ import { defaultProvisionMode } from "./options.js";
4
+ import { inspectProvisionRepair } from "./inspect.js";
5
+ export { inspectProvisionRepair } from "./inspect.js";
6
+ export function buildRepairAnswers(registry) {
7
+ const m = registry.manifest;
8
+ return {
9
+ projectName: m.projectName,
10
+ targetDir: process.cwd(),
11
+ defaultLocale: m.defaultLocale,
12
+ supportedLocales: [...m.supportedLocales],
13
+ notionxSource: m.notionxCore,
14
+ enableSiteSettings: m.enableSiteSettings,
15
+ enableBlocks: m.enableBlocks,
16
+ enableAuth: m.enableAuth,
17
+ enableAdmin: m.enableAdmin,
18
+ enablePages: m.enablePages,
19
+ enableSearch: m.enableSearch,
20
+ contentSource: {
21
+ id: m.contentSource.id,
22
+ title: m.contentSource.title,
23
+ fields: m.contentSource.fields.map((field) => ({
24
+ key: field.key,
25
+ notionName: field.notionName,
26
+ })),
27
+ },
28
+ adminEmail: DEFAULT_ANSWERS.adminEmail,
29
+ adminPassword: DEFAULT_ANSWERS.adminPassword,
30
+ notionParentPage: DEFAULT_ANSWERS.notionParentPage,
31
+ notionSeedCount: DEFAULT_ANSWERS.notionSeedCount,
32
+ };
33
+ }
34
+ export async function runProvisionRepair(registry, projectDir, answers = buildRepairAnswers(registry), options = {}) {
35
+ if (options.conflictChoice) {
36
+ const entries = await inspectProvisionRepair(projectDir);
37
+ const applicable = entries.filter((entry) => options.conflictChoice === "apply-all" ? true : entry.risk === "safe");
38
+ for (const entry of applicable) {
39
+ await entry.apply();
40
+ }
41
+ return { answers, appliedEntries: applicable };
42
+ }
43
+ return provision(answers, projectDir, {
44
+ interactive: false,
45
+ mode: defaultProvisionMode("repair"),
46
+ });
47
+ }
48
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../../src/provision/repair.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,UAAU,kBAAkB,CAAC,QAAwB;IACzD,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC;IAC5B,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE;QACxB,aAAa,EAAE,CAAC,CAAC,aAAa;QAC9B,gBAAgB,EAAE,CAAC,GAAG,CAAC,CAAC,gBAAgB,CAAC;QACzC,aAAa,EAAE,CAAC,CAAC,WAAW;QAC5B,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;QACxC,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,aAAa,EAAE;YACb,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,EAAE;YACtB,KAAK,EAAE,CAAC,CAAC,aAAa,CAAC,KAAK;YAC5B,MAAM,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC7C,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;SACJ;QACD,UAAU,EAAE,eAAe,CAAC,UAAU;QACtC,aAAa,EAAE,eAAe,CAAC,aAAa;QAC5C,gBAAgB,EAAE,eAAe,CAAC,gBAAgB;QAClD,eAAe,EAAE,eAAe,CAAC,eAAe;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAwB,EACxB,UAAkB,EAClB,UAAmB,kBAAkB,CAAC,QAAQ,CAAC,EAC/C,UAA0D,EAAE;IAE5D,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC;QACzD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1C,OAAO,CAAC,cAAc,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CACtE,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;IACjD,CAAC;IAED,OAAO,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE;QACpC,WAAW,EAAE,KAAK;QAClB,IAAI,EAAE,oBAAoB,CAAC,QAAQ,CAAC;KACrC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,141 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { defaultProvisionMode } from "./options.js";
3
+ import { inspectProvisionRepair, runProvisionRepair } from "./repair.js";
4
+ const provisionMock = vi.hoisted(() => vi.fn());
5
+ const inspectProvisionMock = vi.hoisted(() => vi.fn());
6
+ vi.mock("./index.js", async () => {
7
+ const actual = await vi.importActual("./index.js");
8
+ return {
9
+ ...actual,
10
+ provision: provisionMock,
11
+ };
12
+ });
13
+ vi.mock("./inspect.js", async () => {
14
+ const actual = await vi.importActual("./inspect.js");
15
+ return {
16
+ ...actual,
17
+ inspectProvisionRepair: inspectProvisionMock,
18
+ };
19
+ });
20
+ const registry = {
21
+ manifest: {
22
+ $schema: "https://notionx.dev/schemas/registry.v2.json",
23
+ projectKind: "notionx",
24
+ projectName: "demo",
25
+ scaffoldVersion: "0.4.10",
26
+ notionxCore: "^0.1.2",
27
+ defaultLocale: "en",
28
+ supportedLocales: ["en"],
29
+ enableSiteSettings: true,
30
+ enableBlocks: true,
31
+ enableAuth: true,
32
+ enableAdmin: true,
33
+ enablePages: true,
34
+ enableSearch: true,
35
+ contentSource: {
36
+ id: "blog",
37
+ title: "Blog",
38
+ fields: [{ key: "title", notionName: "Name" }],
39
+ },
40
+ compat: { mode: "v2-native" },
41
+ registries: {},
42
+ installed: [],
43
+ managedFiles: { platform: [], bridge: [], user: [] },
44
+ },
45
+ managedFiles: { platform: [], bridge: [], user: [] },
46
+ };
47
+ const projectDir = "/tmp/demo";
48
+ describe("provision mode defaults", () => {
49
+ it("disables deploy for repair mode", () => {
50
+ expect(defaultProvisionMode("repair").deploy).toBe(false);
51
+ });
52
+ it("enables deploy for create mode", () => {
53
+ expect(defaultProvisionMode("create").deploy).toBe(true);
54
+ });
55
+ });
56
+ describe("runProvisionRepair", () => {
57
+ it("invokes provision in repair mode", async () => {
58
+ const answers = {
59
+ projectName: "demo",
60
+ targetDir: "./demo",
61
+ defaultLocale: "en",
62
+ supportedLocales: ["en"],
63
+ notionxSource: "^0.1.2",
64
+ enableSiteSettings: true,
65
+ enableBlocks: true,
66
+ enableAuth: true,
67
+ enableAdmin: true,
68
+ enablePages: true,
69
+ enableSearch: true,
70
+ contentSource: {
71
+ id: "blog",
72
+ title: "Blog",
73
+ fields: [{ key: "title", notionName: "Name" }],
74
+ },
75
+ adminEmail: "admin@example.com",
76
+ adminPassword: "ChangeMe1234",
77
+ notionParentPage: "",
78
+ notionSeedCount: 3,
79
+ };
80
+ provisionMock.mockResolvedValueOnce({ deploy: { skipped: true } });
81
+ await runProvisionRepair(registry, projectDir, answers);
82
+ expect(provisionMock).toHaveBeenCalledWith(answers, "/tmp/demo", expect.objectContaining({
83
+ interactive: false,
84
+ mode: expect.objectContaining({ name: "repair", deploy: false }),
85
+ }));
86
+ });
87
+ it("can apply only safe inspected entries", async () => {
88
+ const safeApply = vi.fn();
89
+ const conflictApply = vi.fn();
90
+ inspectProvisionMock.mockResolvedValueOnce([
91
+ {
92
+ label: "cloudflare:add-var:VINEXT_KV_CACHE",
93
+ kind: "cloudflare",
94
+ group: "cloudflareBinding",
95
+ risk: "safe",
96
+ apply: safeApply,
97
+ },
98
+ {
99
+ label: "notion:update-site-settings:Nav",
100
+ kind: "notion",
101
+ group: "notionContent",
102
+ risk: "conflict",
103
+ apply: conflictApply,
104
+ },
105
+ ]);
106
+ await runProvisionRepair(registry, projectDir, undefined, {
107
+ conflictChoice: "safe-only",
108
+ });
109
+ expect(safeApply).toHaveBeenCalledTimes(1);
110
+ expect(conflictApply).not.toHaveBeenCalled();
111
+ });
112
+ });
113
+ describe("inspectProvisionRepair", () => {
114
+ it("marks additive Notion schema repairs as safe", async () => {
115
+ inspectProvisionMock.mockResolvedValueOnce([
116
+ {
117
+ label: "notion:add-property:Count",
118
+ kind: "notion",
119
+ group: "notionContent",
120
+ risk: "safe",
121
+ apply: vi.fn(),
122
+ },
123
+ ]);
124
+ const entries = await inspectProvisionRepair(projectDir);
125
+ expect(entries[0]?.risk).toBe("safe");
126
+ });
127
+ it("marks populated site settings replacements as conflicts", async () => {
128
+ inspectProvisionMock.mockResolvedValueOnce([
129
+ {
130
+ label: "notion:update-site-settings:Nav",
131
+ kind: "notion",
132
+ group: "notionContent",
133
+ risk: "conflict",
134
+ apply: vi.fn(),
135
+ },
136
+ ]);
137
+ const entries = await inspectProvisionRepair(projectDir);
138
+ expect(entries[0]?.risk).toBe("conflict");
139
+ });
140
+ });
141
+ //# sourceMappingURL=repair.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.test.js","sourceRoot":"","sources":["../../src/provision/repair.test.ts"],"names":[],"mappings":"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"}