@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,44 @@
1
+ // packages/create-notionx-app/src/locale-add/list.ts
2
+ //
3
+ // Read-only helper that turns the scaffold metadata into a printable
4
+ // `notionx locale list` view. The view is plain data so it can be
5
+ // rendered as a table, a JSON dump, or a Markdown summary.
6
+ const BUILT_IN_MODELS = [
7
+ "blog-translations",
8
+ "page-translations",
9
+ "block-translations",
10
+ "site-settings-translations",
11
+ ];
12
+ function envVarForModel(modelId) {
13
+ switch (modelId) {
14
+ case "blog-translations":
15
+ return "NOTION_BLOG_TRANSLATIONS_DATA_SOURCE_ID";
16
+ case "page-translations":
17
+ return "NOTION_PAGES_TRANSLATIONS_DATA_SOURCE_ID";
18
+ case "block-translations":
19
+ return "NOTION_BLOCKS_TRANSLATIONS_DATA_SOURCE_ID";
20
+ case "site-settings-translations":
21
+ return "NOTION_SITE_SETTINGS_TRANSLATIONS_DATA_SOURCE_ID";
22
+ default: {
23
+ const upper = modelId.replace(/-/g, "_").toUpperCase();
24
+ return `NOTION_${upper}_DATA_SOURCE_ID`;
25
+ }
26
+ }
27
+ }
28
+ export function buildLocaleListView(input) {
29
+ const sources = input.metadata.translationSources ?? {};
30
+ const rows = input.metadata.supportedLocales.map((locale) => ({
31
+ locale,
32
+ isDefault: locale === input.metadata.defaultLocale,
33
+ translationSources: BUILT_IN_MODELS.map((modelId) => {
34
+ const ref = sources[modelId];
35
+ return {
36
+ modelId,
37
+ envVar: ref?.envVar ?? envVarForModel(modelId),
38
+ configured: Boolean(ref?.dataSourceId),
39
+ };
40
+ }),
41
+ }));
42
+ return { rows };
43
+ }
44
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/locale-add/list.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,qEAAqE;AACrE,kEAAkE;AAClE,2DAA2D;AAI3D,MAAM,eAAe,GAAG;IACtB,mBAAmB;IACnB,mBAAmB;IACnB,oBAAoB;IACpB,4BAA4B;CACpB,CAAC;AAgBX,SAAS,cAAc,CAAC,OAAe;IACrC,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,mBAAmB;YACtB,OAAO,yCAAyC,CAAC;QACnD,KAAK,mBAAmB;YACtB,OAAO,0CAA0C,CAAC;QACpD,KAAK,oBAAoB;YACvB,OAAO,2CAA2C,CAAC;QACrD,KAAK,4BAA4B;YAC/B,OAAO,kDAAkD,CAAC;QAC5D,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;YACvD,OAAO,UAAU,KAAK,iBAAiB,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAEnC;IACC,MAAM,OAAO,GACX,KAAK,CAAC,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAoB,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAC/D,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACX,MAAM;QACN,SAAS,EAAE,MAAM,KAAK,KAAK,CAAC,QAAQ,CAAC,aAAa;QAClD,kBAAkB,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO;gBACL,OAAO;gBACP,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,cAAc,CAAC,OAAO,CAAC;gBAC9C,UAAU,EAAE,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC;aACvC,CAAC;QACJ,CAAC,CAAC;KACH,CAAC,CACH,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC"}
@@ -0,0 +1,45 @@
1
+ // packages/create-notionx-app/src/locale-add/list.test.ts
2
+ import { describe, expect, it } from "vitest";
3
+ import { buildLocaleListView } from "../../src/locale-add/list.js";
4
+ const metadata = {
5
+ projectKind: "notionx",
6
+ projectName: "demo",
7
+ scaffoldVersion: "1.0.0",
8
+ defaultLocale: "en",
9
+ supportedLocales: ["en", "zh-CN"],
10
+ notionxSource: "1.0.0",
11
+ enableSiteSettings: true,
12
+ contentSource: { id: "blog", title: "Blog", fields: [] },
13
+ };
14
+ describe("buildLocaleListView", () => {
15
+ it("marks the default locale", () => {
16
+ const view = buildLocaleListView({ metadata });
17
+ expect(view.rows.find((r) => r.locale === "en")?.isDefault).toBe(true);
18
+ expect(view.rows.find((r) => r.locale === "zh-CN")?.isDefault).toBe(false);
19
+ });
20
+ it("reports whether translation sources are configured", () => {
21
+ const view = buildLocaleListView({
22
+ metadata: {
23
+ ...metadata,
24
+ translationSources: {
25
+ "blog-translations": {
26
+ dataSourceId: "ds-1",
27
+ envVar: "NOTION_BLOG_TRANSLATIONS_DATA_SOURCE_ID",
28
+ },
29
+ },
30
+ },
31
+ });
32
+ const zhRow = view.rows.find((r) => r.locale === "zh-CN");
33
+ const blogTs = zhRow?.translationSources.find((ts) => ts.modelId === "blog-translations");
34
+ expect(blogTs?.configured).toBe(true);
35
+ // Other built-in models stay unconfigured.
36
+ const pagesTs = zhRow?.translationSources.find((ts) => ts.modelId === "page-translations");
37
+ expect(pagesTs?.configured).toBe(false);
38
+ });
39
+ it("falls back to a synthesized env-var name for unconfigured sources", () => {
40
+ const view = buildLocaleListView({ metadata });
41
+ const blockTs = view.rows[1]?.translationSources.find((ts) => ts.modelId === "block-translations");
42
+ expect(blockTs?.envVar).toBe("NOTION_BLOCKS_TRANSLATIONS_DATA_SOURCE_ID");
43
+ });
44
+ });
45
+ //# sourceMappingURL=list.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.test.js","sourceRoot":"","sources":["../../src/locale-add/list.test.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGnE,MAAM,QAAQ,GAAqB;IACjC,WAAW,EAAE,SAAS;IACtB,WAAW,EAAE,MAAM;IACnB,eAAe,EAAE,OAAO;IACxB,aAAa,EAAE,IAAI;IACnB,gBAAgB,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;IACjC,aAAa,EAAE,OAAO;IACtB,kBAAkB,EAAE,IAAI;IACxB,aAAa,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;CACzD,CAAC;AAEF,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CACjE,KAAK,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,IAAI,GAAG,mBAAmB,CAAC;YAC/B,QAAQ,EAAE;gBACR,GAAG,QAAQ;gBACX,kBAAkB,EAAE;oBAClB,mBAAmB,EAAE;wBACnB,YAAY,EAAE,MAAM;wBACpB,MAAM,EAAE,yCAAyC;qBAClD;iBACF;aACF;SACF,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAC3C,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,KAAK,mBAAmB,CAC3C,CAAC;QACF,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,2CAA2C;QAC3C,MAAM,OAAO,GAAG,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAC5C,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,KAAK,mBAAmB,CAC3C,CAAC;QACF,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,kBAAkB,CAAC,IAAI,CACnD,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,KAAK,oBAAoB,CAC5C,CAAC;QACF,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAC1B,2CAA2C,CAC5C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,128 @@
1
+ // packages/create-notionx-app/src/locale-add/plan.ts
2
+ //
3
+ // Pure planning step for `notionx locale add`. Given the project
4
+ // context, a locale, and CLI flags, return the list of changes the
5
+ // runner should apply. The planner is pure: it never touches disk,
6
+ // never shells out, and never throws on missing optional inputs.
7
+ //
8
+ // The output is consumed by the runner (see `apply.ts`) which is
9
+ // the only thing that mutates state. This makes the planner
10
+ // trivially testable and lets the CLI render a dry-run summary
11
+ // without side effects.
12
+ const BUILT_IN_NOTION_SOURCES = [
13
+ "blog-translations",
14
+ "page-translations",
15
+ "block-translations",
16
+ "site-settings-translations",
17
+ ];
18
+ export function buildLocaleAddPlan(input) {
19
+ const changes = [];
20
+ const { projectDir, metadata, locale } = input;
21
+ const i18nPath = "lib/i18n/config.ts";
22
+ const siteConfigPath = "lib/site/config.ts";
23
+ const metadataPath = ".notionx/scaffold.json";
24
+ // 1. metadata: append locale to supportedLocales.
25
+ const nextMetadata = {
26
+ ...metadata,
27
+ supportedLocales: [...metadata.supportedLocales, locale],
28
+ };
29
+ changes.push({
30
+ kind: "metadata",
31
+ label: "metadata:supportedLocales",
32
+ description: `Append "${locale}" to .notionx/scaffold.json supportedLocales.`,
33
+ risk: metadata.supportedLocales.includes(locale) ? "conflict" : "safe",
34
+ async apply() {
35
+ const fs = await import("node:fs/promises");
36
+ const full = `${projectDir}/${metadataPath}`;
37
+ await fs.writeFile(full, JSON.stringify(nextMetadata, null, 2) + "\n", "utf8");
38
+ },
39
+ });
40
+ // 2. lib/i18n/config.ts — append the locale to supportedLocalesJson.
41
+ changes.push({
42
+ kind: "file",
43
+ label: `file:${i18nPath}`,
44
+ description: `Add "${locale}" to lib/i18n/config.ts supportedLocales.`,
45
+ risk: "safe",
46
+ filePath: i18nPath,
47
+ async apply() {
48
+ const fs = await import("node:fs/promises");
49
+ const full = `${projectDir}/${i18nPath}`;
50
+ let content;
51
+ try {
52
+ content = await fs.readFile(full, "utf8");
53
+ }
54
+ catch {
55
+ return;
56
+ }
57
+ const updated = content.replace(/supportedLocales:\s*\[[^\]]*\]/, (match) => match.replace(/\]$/, `, "${locale}"]`));
58
+ await fs.writeFile(full, updated, "utf8");
59
+ },
60
+ });
61
+ // 3. lib/site/config.ts — append the locale to the locales list.
62
+ changes.push({
63
+ kind: "file",
64
+ label: `file:${siteConfigPath}`,
65
+ description: `Add "${locale}" to lib/site/config.ts locales.`,
66
+ risk: "safe",
67
+ filePath: siteConfigPath,
68
+ async apply() {
69
+ const fs = await import("node:fs/promises");
70
+ const full = `${projectDir}/${siteConfigPath}`;
71
+ let content;
72
+ try {
73
+ content = await fs.readFile(full, "utf8");
74
+ }
75
+ catch {
76
+ return;
77
+ }
78
+ const updated = content.replace(/locales:\s*\[[^\]]*\]/, (match) => match.replace(/\]$/, `, "${locale}"]`));
79
+ await fs.writeFile(full, updated, "utf8");
80
+ },
81
+ });
82
+ // 4. (optional) Notion translation data sources — declared but the
83
+ // actual create/reuse happens in the translation-sources runner.
84
+ if (input.withNotion) {
85
+ for (const sourceName of BUILT_IN_NOTION_SOURCES) {
86
+ changes.push({
87
+ kind: "notion",
88
+ label: `notion:${sourceName}`,
89
+ description: `Ensure Notion data source "${sourceName}" exists (idempotent: create or reuse).`,
90
+ risk: "safe",
91
+ modelId: sourceName,
92
+ async apply() {
93
+ return null;
94
+ },
95
+ });
96
+ }
97
+ }
98
+ // 5. (optional) Cloudflare secrets for the new translation source ids.
99
+ if (input.withNotion && input.translationSourceIds) {
100
+ for (const [sourceName, dataSourceId] of Object.entries(input.translationSourceIds)) {
101
+ const envVar = sourceNameToEnvVar(sourceName);
102
+ if (!envVar)
103
+ continue;
104
+ changes.push({
105
+ kind: "cloudflare",
106
+ label: `cloudflare-secret:${envVar}`,
107
+ description: `Set worker secret ${envVar} to ${dataSourceId}.`,
108
+ risk: "safe",
109
+ envVar,
110
+ async apply() {
111
+ const { setWorkerSecret } = await import("../provision/cloudflare.js");
112
+ await setWorkerSecret(envVar, dataSourceId, projectDir, [dataSourceId]);
113
+ },
114
+ });
115
+ }
116
+ }
117
+ return { locale, changes };
118
+ }
119
+ function sourceNameToEnvVar(sourceName) {
120
+ const map = {
121
+ "blog-translations": "NOTION_BLOG_TRANSLATIONS_DATA_SOURCE_ID",
122
+ "page-translations": "NOTION_PAGES_TRANSLATIONS_DATA_SOURCE_ID",
123
+ "block-translations": "NOTION_BLOCKS_TRANSLATIONS_DATA_SOURCE_ID",
124
+ "site-settings-translations": "NOTION_SITE_SETTINGS_TRANSLATIONS_DATA_SOURCE_ID",
125
+ };
126
+ return map[sourceName] ?? null;
127
+ }
128
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/locale-add/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ // packages/create-notionx-app/src/locale-add/validate.ts
2
+ //
3
+ // Conservative validator for `notionx locale add <locale>`. The
4
+ // command only ever adds, never removes. The validator refuses:
5
+ //
6
+ // - empty / whitespace-only input
7
+ // - malformed locales (anything that is not a BCP-47-ish tag of
8
+ // letters, digits, and a single hyphen)
9
+ // - duplicates against the existing `supportedLocales` list
10
+ // - the default locale (adding the default is a no-op)
11
+ //
12
+ // A well-formed, non-duplicate locale is returned with a normalized
13
+ // casing (`zh-cn` → `zh-CN` style, where the region part is upper
14
+ // case and the language part is lower case).
15
+ const LOCALE_RE = /^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$/;
16
+ function normalize(locale) {
17
+ const trimmed = locale.trim();
18
+ if (!trimmed)
19
+ return null;
20
+ if (!LOCALE_RE.test(trimmed))
21
+ return null;
22
+ const [lang, region] = trimmed.split("-");
23
+ return region
24
+ ? `${lang.toLowerCase()}-${region.toUpperCase()}`
25
+ : lang.toLowerCase();
26
+ }
27
+ export function validateLocaleAdd(input) {
28
+ const normalized = normalize(input.locale);
29
+ if (!normalized) {
30
+ return { ok: false, reason: `Not a valid locale tag: "${input.locale}"` };
31
+ }
32
+ if (normalized === input.defaultLocale) {
33
+ return {
34
+ ok: false,
35
+ reason: `"${normalized}" is already the default locale`,
36
+ };
37
+ }
38
+ if (input.supportedLocales.includes(normalized)) {
39
+ return {
40
+ ok: false,
41
+ reason: `"${normalized}" is already in supportedLocales`,
42
+ };
43
+ }
44
+ return { ok: true, locale: normalized };
45
+ }
46
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/locale-add/validate.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,gEAAgE;AAChE,gEAAgE;AAChE,EAAE;AACF,oCAAoC;AACpC,kEAAkE;AAClE,4CAA4C;AAC5C,8DAA8D;AAC9D,yDAAyD;AACzD,EAAE;AACF,oEAAoE;AACpE,kEAAkE;AAClE,6CAA6C;AAE7C,MAAM,SAAS,GAAG,kCAAkC,CAAC;AAYrD,SAAS,SAAS,CAAC,MAAc;IAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,MAAM;QACX,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE;QACjD,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAA6B;IAE7B,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;IAC5E,CAAC;IACD,IAAI,UAAU,KAAK,KAAK,CAAC,aAAa,EAAE,CAAC;QACvC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,IAAI,UAAU,iCAAiC;SACxD,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,IAAI,UAAU,kCAAkC;SACzD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,41 @@
1
+ export const SCAFFOLD_METADATA_FILE = ".notionx/scaffold.json";
2
+ export function buildScaffoldMetadata(answers, scaffoldVersion) {
3
+ return {
4
+ projectKind: "notionx",
5
+ projectName: answers.projectName,
6
+ scaffoldVersion,
7
+ defaultLocale: answers.defaultLocale,
8
+ supportedLocales: [...answers.supportedLocales],
9
+ notionxSource: answers.notionxSource,
10
+ enableSiteSettings: answers.enableSiteSettings,
11
+ contentSource: {
12
+ id: answers.contentSource.id,
13
+ title: answers.contentSource.title,
14
+ fields: answers.contentSource.fields.map((field) => ({
15
+ key: field.key,
16
+ notionName: field.notionName,
17
+ })),
18
+ },
19
+ translationSources: {},
20
+ };
21
+ }
22
+ export function parseScaffoldMetadata(raw) {
23
+ const parsed = JSON.parse(raw);
24
+ if (parsed.projectKind !== "notionx" ||
25
+ typeof parsed.projectName !== "string" ||
26
+ typeof parsed.scaffoldVersion !== "string" ||
27
+ typeof parsed.defaultLocale !== "string" ||
28
+ !Array.isArray(parsed.supportedLocales) ||
29
+ typeof parsed.notionxSource !== "string" ||
30
+ !parsed.contentSource ||
31
+ typeof parsed.contentSource.id !== "string" ||
32
+ typeof parsed.contentSource.title !== "string" ||
33
+ !Array.isArray(parsed.contentSource.fields) ||
34
+ parsed.contentSource.fields.some((field) => !field ||
35
+ typeof field.key !== "string" ||
36
+ typeof field.notionName !== "string")) {
37
+ throw new Error("Invalid Notionx metadata payload");
38
+ }
39
+ return parsed;
40
+ }
41
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAC;AAuD/D,MAAM,UAAU,qBAAqB,CACnC,OAAgB,EAChB,eAAuB;IAEvB,OAAO;QACL,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,eAAe;QACf,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,gBAAgB,EAAE,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;QAC/C,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,aAAa,EAAE;YACb,EAAE,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE;YAC5B,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK;YAClC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACnD,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;SACJ;QACD,kBAAkB,EAAE,EAAE;KACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA8B,CAAC;IAC5D,IACE,MAAM,CAAC,WAAW,KAAK,SAAS;QAChC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;QACtC,OAAO,MAAM,CAAC,eAAe,KAAK,QAAQ;QAC1C,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ;QACxC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;QACvC,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ;QACxC,CAAC,MAAM,CAAC,aAAa;QACrB,OAAO,MAAM,CAAC,aAAa,CAAC,EAAE,KAAK,QAAQ;QAC3C,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,KAAK,QAAQ;QAC9C,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC;QAC3C,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAC9B,CAAC,KAAK,EAAE,EAAE,CACR,CAAC,KAAK;YACN,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;YAC7B,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CACvC,EACD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,MAA0B,CAAC;AACpC,CAAC"}
@@ -0,0 +1,61 @@
1
+ // packages/create-notionx-app/src/notion-translation-sources/apply.ts
2
+ //
3
+ // Applies the planned translation data source changes via the `ntn`
4
+ // CLI. This module owns the only place that shells out to Notion for
5
+ // translation sources; everything else flows through the same
6
+ // `LocaleAddChange` runner.
7
+ import { runNtn } from "../provision/shell.js";
8
+ export async function applyNotionTranslationSources(plans) {
9
+ const result = { resolved: {}, failures: [] };
10
+ for (const plan of plans) {
11
+ if (plan.action === "reuse" && plan.existingDataSourceId) {
12
+ result.resolved[plan.modelId] = {
13
+ dataSourceId: plan.existingDataSourceId,
14
+ envVar: plan.envVar,
15
+ };
16
+ continue;
17
+ }
18
+ try {
19
+ const cli = await runNtn([
20
+ "databases",
21
+ "create",
22
+ "--parent",
23
+ plan.parentPageId,
24
+ "--title",
25
+ titleFor(plan.modelId),
26
+ ]);
27
+ if (cli.code !== 0) {
28
+ result.failures.push({
29
+ modelId: plan.modelId,
30
+ error: cli.stderr || cli.stdout,
31
+ });
32
+ continue;
33
+ }
34
+ const parsed = JSON.parse(cli.stdout);
35
+ const dataSourceId = parsed.dataSourceId;
36
+ if (!dataSourceId) {
37
+ result.failures.push({
38
+ modelId: plan.modelId,
39
+ error: "ntn did not return a dataSourceId",
40
+ });
41
+ continue;
42
+ }
43
+ result.resolved[plan.modelId] = { dataSourceId, envVar: plan.envVar };
44
+ }
45
+ catch (error) {
46
+ const message = error instanceof Error ? error.message : String(error);
47
+ result.failures.push({ modelId: plan.modelId, error: message });
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+ function titleFor(modelId) {
53
+ const map = {
54
+ "blog-translations": "Blog Translations",
55
+ "page-translations": "Page Translations",
56
+ "block-translations": "Block Translations",
57
+ "site-settings-translations": "Site Settings Translations",
58
+ };
59
+ return map[modelId] ?? modelId;
60
+ }
61
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/notion-translation-sources/apply.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,oEAAoE;AACpE,qEAAqE;AACrE,8DAA8D;AAC9D,4BAA4B;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAQ/C,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,KAAoC;IAEpC,MAAM,MAAM,GAAkC,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC7E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACzD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG;gBAC9B,YAAY,EAAE,IAAI,CAAC,oBAAoB;gBACvC,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC;gBACvB,WAAW;gBACX,QAAQ;gBACR,UAAU;gBACV,IAAI,CAAC,YAAY;gBACjB,SAAS;gBACT,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;aACvB,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACnB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM;iBAChC,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAA8B,CAAC;YACnE,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;YACzC,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,mCAAmC;iBAC3C,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,GAAG,GAA2B;QAClC,mBAAmB,EAAE,mBAAmB;QACxC,mBAAmB,EAAE,mBAAmB;QACxC,oBAAoB,EAAE,oBAAoB;QAC1C,4BAA4B,EAAE,4BAA4B;KAC3D,CAAC;IACF,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;AACjC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./plan.js";
2
+ export * from "./apply.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/notion-translation-sources/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,33 @@
1
+ // packages/create-notionx-app/src/notion-translation-sources/plan.ts
2
+ //
3
+ // Pure planner for the four built-in Notion translation data
4
+ // sources. The planner never calls the `ntn` CLI; it returns a
5
+ // list of `NotionTranslationSourcePlan` records that the runner
6
+ // resolves into actual Notion resources.
7
+ const MODEL_ID_TO_ENV = {
8
+ "blog-translations": "NOTION_BLOG_TRANSLATIONS_DATA_SOURCE_ID",
9
+ "page-translations": "NOTION_PAGES_TRANSLATIONS_DATA_SOURCE_ID",
10
+ "block-translations": "NOTION_BLOCKS_TRANSLATIONS_DATA_SOURCE_ID",
11
+ "site-settings-translations": "NOTION_SITE_SETTINGS_TRANSLATIONS_DATA_SOURCE_ID",
12
+ };
13
+ const ALL_MODEL_IDS = [
14
+ "blog-translations",
15
+ "page-translations",
16
+ "block-translations",
17
+ "site-settings-translations",
18
+ ];
19
+ export function planNotionTranslationSources(input) {
20
+ return ALL_MODEL_IDS.map((modelId) => {
21
+ const envVar = MODEL_ID_TO_ENV[modelId];
22
+ const existing = input.existingTranslationSources[modelId];
23
+ return {
24
+ modelId,
25
+ envVar,
26
+ parentPageId: input.parentPageId,
27
+ copyFrom: input.copyFrom,
28
+ existingDataSourceId: existing?.dataSourceId,
29
+ action: existing ? "reuse" : "create",
30
+ };
31
+ });
32
+ }
33
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/notion-translation-sources/plan.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,6DAA6D;AAC7D,+DAA+D;AAC/D,gEAAgE;AAChE,yCAAyC;AAiBzC,MAAM,eAAe,GAGjB;IACF,mBAAmB,EAAE,yCAAyC;IAC9D,mBAAmB,EAAE,0CAA0C;IAC/D,oBAAoB,EAAE,2CAA2C;IACjE,4BAA4B,EAC1B,kDAAkD;CACrD,CAAC;AAEF,MAAM,aAAa,GAA6C;IAC9D,mBAAmB;IACnB,mBAAmB;IACnB,oBAAoB;IACpB,4BAA4B;CAC7B,CAAC;AAUF,MAAM,UAAU,4BAA4B,CAC1C,KAAwC;IAExC,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACnC,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO;YACL,OAAO;YACP,MAAM;YACN,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,oBAAoB,EAAE,QAAQ,EAAE,YAAY;YAC5C,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;SACtC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,142 @@
1
+ // packages/create-notionx-app/src/notionx-source.ts
2
+ //
3
+ // Resolves the `notionxSource` semver range that the scaffolder
4
+ // writes into the generated `package.json`'s
5
+ // `@notionx/core` / `@notionx/create-notionx-app` entries.
6
+ //
7
+ // Resolution order (highest priority first):
8
+ //
9
+ // 1. Explicit override (`--notionx-source <range>` or
10
+ // the interactive prompt).
11
+ // 2. **Monorepo dev mode** — the user is running the scaffolder
12
+ // from inside the `notionx` monorepo (or from a workspace
13
+ // consumer such as `notionx/apps/<app>`). When we can see
14
+ // `packages/notionx/package.json` two directories up from the
15
+ // target dir, we emit `workspace:*` so the generated project
16
+ // resolves `@notionx/core` against the local checkout via
17
+ // pnpm workspace symlinks. This is the path scaffolder
18
+ // authors use to iterate on `core` without publish / install
19
+ // round-trips.
20
+ // 3. Live npm registry — fetch `@notionx/core`'s `dist-tags.latest`
21
+ // and emit `^<version>`. The fetch has a 5s timeout.
22
+ // 4. Hardcoded `FALLBACK_NOTIONX_SOURCE` range — used when the
23
+ // registry is unreachable. Bump in lockstep with releases.
24
+ import { existsSync, readFileSync } from "node:fs";
25
+ import { resolve } from "node:path";
26
+ /**
27
+ * Last-known-good fallback used when the npm registry lookup
28
+ * fails (network down, registry behind a proxy, 5xx, etc.).
29
+ *
30
+ * Bump this in lockstep with the latest published release — but
31
+ * note that any *in-flight* release cycle should accept a slightly
32
+ * stale fallback over refusing to install.
33
+ *
34
+ * Exported so the prompt / answers modules can reference the same
35
+ * source of truth instead of duplicating the literal — duplicate
36
+ * strings drifted out of sync in 0.5.x and produced un-installable
37
+ * scaffolds (`ERR_PNPM_NO_MATCHING_VERSION` for `@notionx/core@^0.5.2`).
38
+ */
39
+ export const FALLBACK_NOTIONX_SOURCE = "^1.0.0";
40
+ const REGISTRY_TIMEOUT_MS = 5_000;
41
+ /**
42
+ * Marker written into the generated `.notionx/scaffold.json` and
43
+ * `package.json` so downstream tools (the `update` command, the
44
+ * doctor, etc.) can tell that this project was generated in
45
+ * monorepo dev mode and should be upgraded by re-running the
46
+ * scaffolder rather than by pulling a new semver from npm.
47
+ */
48
+ export const MONOREPO_PROTOCOL = "workspace:*";
49
+ /**
50
+ * Returns true when the scaffold target lives inside the `notionx`
51
+ * monorepo — i.e. we can see the monorepo root two directories up
52
+ * from the target. Both the monorepo root and `apps/<name>` consumers
53
+ * qualify, as do any depth-2 or shallower workspace packages.
54
+ *
55
+ * We probe the resolved real path of `packages/notionx/package.json`
56
+ * (not the symlink) to avoid false negatives inside worktrees.
57
+ */
58
+ export function isMonorepoDevMode(targetDir) {
59
+ try {
60
+ // Walk up from the target directory looking for a sibling
61
+ // `packages/notionx/package.json` whose name is `@notionx/core`.
62
+ // We probe upward (not just two levels) so the detector works
63
+ // for both shallow (`notionx/apps/digwis`) and nested
64
+ // (`notionx/apps/scratch/digwis`) layouts, and for worktrees
65
+ // checked out anywhere under the monorepo root.
66
+ let cursor = resolve(targetDir);
67
+ for (let i = 0; i < 6; i++) {
68
+ const probe = resolve(cursor, "packages", "notionx", "package.json");
69
+ if (existsSync(probe)) {
70
+ const contents = readFileSync(probe, "utf8");
71
+ if (/"name"\s*:\s*"@notionx\/core"/.test(contents)) {
72
+ return true;
73
+ }
74
+ }
75
+ const parent = resolve(cursor, "..");
76
+ if (parent === cursor)
77
+ break; // filesystem root
78
+ cursor = parent;
79
+ }
80
+ return false;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * Resolved semver range the scaffolder should write into the
88
+ * generated project.
89
+ *
90
+ * If the caller passed an explicit range (via `--notionx-source`
91
+ * or the interactive prompt), that wins. Otherwise, if the
92
+ * target is inside the `notionx` monorepo, we emit `workspace:*`
93
+ * for the local-dev fast path. Otherwise we hit the npm registry
94
+ * for `@notionx/core`'s `dist-tags.latest` and prefix it with `^`
95
+ * so the generated project picks up compatible updates.
96
+ */
97
+ export async function resolveNotionxSource(override, targetDir) {
98
+ if (override !== undefined && override !== "") {
99
+ return override;
100
+ }
101
+ if (isMonorepoDevMode(targetDir)) {
102
+ return MONOREPO_PROTOCOL;
103
+ }
104
+ try {
105
+ const version = await fetchLatestCoreVersion();
106
+ if (version) {
107
+ return `^${version}`;
108
+ }
109
+ }
110
+ catch {
111
+ // Swallow — fall through to the hardcoded fallback below.
112
+ }
113
+ return FALLBACK_NOTIONX_SOURCE;
114
+ }
115
+ /**
116
+ * Fetches `@notionx/core`'s `dist-tags.latest` from the public
117
+ * npm registry, with a hard timeout. Returns `null` on any error
118
+ * (timeout, non-2xx, malformed JSON, missing field) so the caller
119
+ * can fall back without needing to know what went wrong.
120
+ */
121
+ async function fetchLatestCoreVersion() {
122
+ const controller = new AbortController();
123
+ const timer = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);
124
+ try {
125
+ const response = await fetch("https://registry.npmjs.org/@notionx/core/latest", { signal: controller.signal });
126
+ if (!response.ok) {
127
+ return null;
128
+ }
129
+ const data = (await response.json());
130
+ if (typeof data.version !== "string" || data.version.length === 0) {
131
+ return null;
132
+ }
133
+ return data.version;
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ finally {
139
+ clearTimeout(timer);
140
+ }
141
+ }
142
+ //# sourceMappingURL=notionx-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notionx-source.js","sourceRoot":"","sources":["../src/notionx-source.ts"],"names":[],"mappings":"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"}