@treeseed/sdk 0.1.1 → 0.3.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 (228) hide show
  1. package/README.md +97 -494
  2. package/dist/{src/cli-tools.d.ts → cli-tools.d.ts} +1 -1
  3. package/dist/cli-tools.js +5 -3
  4. package/dist/{src/content-store.d.ts → content-store.d.ts} +3 -2
  5. package/dist/content-store.js +52 -20
  6. package/dist/{src/d1-store.d.ts → d1-store.d.ts} +62 -1
  7. package/dist/d1-store.js +625 -65
  8. package/dist/field-aliases.d.ts +11 -0
  9. package/dist/field-aliases.js +41 -0
  10. package/dist/graph/build.d.ts +19 -0
  11. package/dist/graph/build.js +949 -0
  12. package/dist/graph/dsl.d.ts +2 -0
  13. package/dist/graph/dsl.js +243 -0
  14. package/dist/graph/query.d.ts +47 -0
  15. package/dist/graph/query.js +447 -0
  16. package/dist/graph/ranking.d.ts +3 -0
  17. package/dist/graph/ranking.js +483 -0
  18. package/dist/graph/schema.d.ts +142 -0
  19. package/dist/graph/schema.js +133 -0
  20. package/dist/graph.d.ts +52 -0
  21. package/dist/graph.js +133 -0
  22. package/dist/index.d.ts +27 -0
  23. package/dist/index.js +90 -2
  24. package/dist/model-registry.d.ts +8 -0
  25. package/dist/model-registry.js +351 -25
  26. package/dist/operations/providers/default.d.ts +10 -0
  27. package/dist/operations/providers/default.js +514 -0
  28. package/dist/operations/runtime.d.ts +7 -0
  29. package/dist/operations/runtime.js +60 -0
  30. package/dist/operations/services/config-runtime.d.ts +269 -0
  31. package/dist/operations/services/config-runtime.js +1397 -0
  32. package/dist/operations/services/d1-migration.d.ts +6 -0
  33. package/dist/operations/services/d1-migration.js +89 -0
  34. package/dist/operations/services/deploy.d.ts +371 -0
  35. package/dist/operations/services/deploy.js +981 -0
  36. package/dist/operations/services/git-workflow.d.ts +49 -0
  37. package/dist/operations/services/git-workflow.js +218 -0
  38. package/dist/operations/services/github-automation.d.ts +156 -0
  39. package/dist/operations/services/github-automation.js +256 -0
  40. package/dist/operations/services/local-dev.d.ts +9 -0
  41. package/dist/operations/services/local-dev.js +106 -0
  42. package/dist/operations/services/mailpit-runtime.d.ts +4 -0
  43. package/dist/operations/services/mailpit-runtime.js +59 -0
  44. package/dist/operations/services/railway-deploy.d.ts +53 -0
  45. package/dist/operations/services/railway-deploy.js +123 -0
  46. package/dist/operations/services/runtime-paths.d.ts +19 -0
  47. package/dist/operations/services/runtime-paths.js +54 -0
  48. package/dist/operations/services/runtime-tools.d.ts +117 -0
  49. package/dist/operations/services/runtime-tools.js +358 -0
  50. package/dist/operations/services/save-deploy-preflight.d.ts +34 -0
  51. package/dist/operations/services/save-deploy-preflight.js +76 -0
  52. package/dist/operations/services/template-registry.d.ts +88 -0
  53. package/dist/operations/services/template-registry.js +407 -0
  54. package/dist/operations/services/watch-dev.d.ts +21 -0
  55. package/dist/operations/services/watch-dev.js +284 -0
  56. package/dist/operations/services/workspace-preflight.d.ts +40 -0
  57. package/dist/operations/services/workspace-preflight.js +165 -0
  58. package/dist/operations/services/workspace-save.d.ts +42 -0
  59. package/dist/operations/services/workspace-save.js +235 -0
  60. package/dist/operations/services/workspace-tools.d.ts +16 -0
  61. package/dist/operations/services/workspace-tools.js +270 -0
  62. package/dist/operations-registry.d.ts +5 -0
  63. package/dist/operations-registry.js +68 -0
  64. package/dist/operations-types.d.ts +71 -0
  65. package/dist/operations-types.js +17 -0
  66. package/dist/operations.d.ts +6 -0
  67. package/dist/operations.js +16 -0
  68. package/dist/platform/books-data.d.ts +1 -0
  69. package/dist/platform/books-data.js +1 -0
  70. package/dist/platform/contracts.d.ts +158 -0
  71. package/dist/platform/contracts.js +0 -0
  72. package/dist/platform/deploy/config.d.ts +4 -0
  73. package/dist/platform/deploy/config.js +222 -0
  74. package/dist/platform/deploy-config.d.ts +1 -0
  75. package/dist/platform/deploy-config.js +1 -0
  76. package/dist/platform/env.yaml +394 -0
  77. package/dist/platform/environment.d.ts +130 -0
  78. package/dist/platform/environment.js +331 -0
  79. package/dist/platform/plugin.d.ts +2 -0
  80. package/dist/platform/plugin.js +4 -0
  81. package/dist/platform/plugins/constants.d.ts +22 -0
  82. package/dist/platform/plugins/constants.js +29 -0
  83. package/dist/platform/plugins/plugin.d.ts +51 -0
  84. package/dist/platform/plugins/plugin.js +6 -0
  85. package/dist/platform/plugins/runtime.d.ts +35 -0
  86. package/dist/platform/plugins/runtime.js +142 -0
  87. package/dist/platform/plugins.d.ts +5 -0
  88. package/dist/platform/plugins.js +16 -0
  89. package/dist/platform/site-config-schema.js +1 -0
  90. package/dist/platform/tenant/config.d.ts +9 -0
  91. package/dist/platform/tenant/config.js +154 -0
  92. package/dist/platform/tenant/runtime-config.d.ts +4 -0
  93. package/dist/platform/tenant/runtime-config.js +20 -0
  94. package/dist/platform/tenant-config.d.ts +1 -0
  95. package/dist/platform/tenant-config.js +1 -0
  96. package/dist/platform/utils/books-data.d.ts +29 -0
  97. package/dist/platform/utils/books-data.js +82 -0
  98. package/dist/platform/utils/site-config-schema.js +321 -0
  99. package/dist/remote.d.ts +175 -0
  100. package/dist/remote.js +202 -0
  101. package/dist/runtime.js +50 -3
  102. package/dist/scripts/aggregate-book.js +121 -0
  103. package/dist/scripts/build-dist.js +57 -13
  104. package/dist/scripts/build-tenant-worker.js +36 -0
  105. package/dist/scripts/cleanup-markdown.js +373 -0
  106. package/dist/scripts/cli-test-fixtures.js +48 -0
  107. package/dist/scripts/config-treeseed.js +95 -0
  108. package/dist/scripts/ensure-mailpit.js +29 -0
  109. package/dist/scripts/local-dev.js +129 -0
  110. package/dist/scripts/logs-mailpit.js +2 -0
  111. package/dist/scripts/patch-starlight-content-path.js +172 -0
  112. package/dist/scripts/release-verify.js +34 -5
  113. package/dist/scripts/run-fixture-astro-command.js +18 -0
  114. package/dist/scripts/scaffold-site.js +65 -0
  115. package/dist/scripts/stop-mailpit.js +5 -0
  116. package/dist/scripts/sync-dev-vars.js +6 -0
  117. package/dist/scripts/sync-template.js +20 -0
  118. package/dist/scripts/template-catalog.test.js +100 -0
  119. package/dist/scripts/template-command.js +31 -0
  120. package/dist/scripts/tenant-astro-command.js +3 -0
  121. package/dist/scripts/tenant-build.js +16 -0
  122. package/dist/scripts/tenant-check.js +7 -0
  123. package/dist/scripts/tenant-d1-migrate-local.js +11 -0
  124. package/dist/scripts/tenant-deploy.js +180 -0
  125. package/dist/scripts/tenant-destroy.js +104 -0
  126. package/dist/scripts/tenant-dev.js +171 -0
  127. package/dist/scripts/tenant-lint.js +4 -0
  128. package/dist/scripts/tenant-test.js +4 -0
  129. package/dist/scripts/test-cloudflare-local.js +212 -0
  130. package/dist/scripts/test-scaffold.js +314 -0
  131. package/dist/scripts/test-smoke.js +71 -13
  132. package/dist/scripts/treeseed-assert-release-tag-version.js +21 -0
  133. package/dist/scripts/treeseed-build-dist.js +134 -0
  134. package/dist/scripts/treeseed-publish-package.js +19 -0
  135. package/dist/scripts/treeseed-release-verify.js +131 -0
  136. package/dist/scripts/treeseed-run-ts.js +45 -0
  137. package/dist/scripts/validate-templates.js +6 -0
  138. package/dist/scripts/verify-driver.js +29 -0
  139. package/dist/scripts/workflow-commands.test.js +39 -0
  140. package/dist/scripts/workspace-close.js +24 -0
  141. package/dist/scripts/workspace-command-e2e.js +718 -0
  142. package/dist/scripts/workspace-lint.js +9 -0
  143. package/dist/scripts/workspace-preflight.js +22 -0
  144. package/dist/scripts/workspace-publish-changed-packages.js +16 -0
  145. package/dist/scripts/workspace-release-verify.js +81 -0
  146. package/dist/scripts/workspace-release.js +42 -0
  147. package/dist/scripts/workspace-save.js +124 -0
  148. package/dist/scripts/workspace-start-warning.js +3 -0
  149. package/dist/scripts/workspace-start.js +71 -0
  150. package/dist/scripts/workspace-test-unit.js +4 -0
  151. package/dist/scripts/workspace-test.js +11 -0
  152. package/dist/sdk-fields.d.ts +11 -0
  153. package/dist/sdk-fields.js +169 -0
  154. package/dist/sdk-filters.d.ts +4 -0
  155. package/dist/sdk-filters.js +12 -15
  156. package/dist/sdk-types.d.ts +796 -0
  157. package/dist/sdk-types.js +7 -1
  158. package/dist/sdk-version.d.ts +2 -0
  159. package/dist/sdk-version.js +42 -0
  160. package/dist/sdk.d.ts +215 -0
  161. package/dist/sdk.js +235 -11
  162. package/dist/stores/cursor-store.js +9 -3
  163. package/dist/stores/lease-store.js +8 -2
  164. package/dist/{src/stores → stores}/message-store.d.ts +1 -1
  165. package/dist/stores/message-store.js +27 -3
  166. package/dist/stores/operational-store.d.ts +24 -0
  167. package/dist/stores/operational-store.js +279 -0
  168. package/dist/stores/run-store.js +8 -1
  169. package/dist/stores/subscription-store.js +7 -5
  170. package/dist/template-catalog.d.ts +13 -0
  171. package/dist/template-catalog.js +141 -0
  172. package/dist/treeseed/services/compose.yml +7 -0
  173. package/dist/treeseed/template-catalog/catalog.fixture.json +55 -0
  174. package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.d.ts +2 -0
  175. package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.ts +3 -0
  176. package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +32 -0
  177. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/config.yaml +40 -0
  178. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/empty/.gitkeep +1 -0
  179. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/knowledge/handbook/index.mdx +11 -0
  180. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/pages/welcome.mdx +11 -0
  181. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.d.ts +1 -0
  182. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.ts +3 -0
  183. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/env.yaml +1 -0
  184. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/manifest.yaml +19 -0
  185. package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +26 -0
  186. package/dist/treeseed/template-catalog/templates/starter-basic/template/tsconfig.json +9 -0
  187. package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +90 -0
  188. package/dist/verification.d.ts +20 -0
  189. package/dist/verification.js +98 -0
  190. package/dist/workflow/operations.d.ts +396 -0
  191. package/dist/workflow/operations.js +841 -0
  192. package/dist/workflow-state.d.ts +56 -0
  193. package/dist/workflow-state.js +195 -0
  194. package/dist/workflow-support.d.ts +9 -0
  195. package/dist/workflow-support.js +176 -0
  196. package/dist/workflow.d.ts +111 -0
  197. package/dist/workflow.js +97 -0
  198. package/package.json +97 -5
  199. package/scripts/verify-driver.mjs +29 -0
  200. package/dist/scripts/.ts-run-1775616845195-odh4xzphk3l.js +0 -22
  201. package/dist/scripts/.ts-run-1775616848931-9386s6kwrl.js +0 -126
  202. package/dist/scripts/assert-release-tag-version.d.ts +0 -1
  203. package/dist/scripts/build-dist.d.ts +0 -1
  204. package/dist/scripts/package-tools.d.ts +0 -15
  205. package/dist/scripts/publish-package.d.ts +0 -1
  206. package/dist/scripts/release-verify.d.ts +0 -1
  207. package/dist/scripts/test-smoke.d.ts +0 -1
  208. package/dist/src/index.d.ts +0 -6
  209. package/dist/src/model-registry.d.ts +0 -4
  210. package/dist/src/sdk-filters.d.ts +0 -4
  211. package/dist/src/sdk-types.d.ts +0 -285
  212. package/dist/src/sdk.d.ts +0 -109
  213. package/dist/test/test-fixture.d.ts +0 -1
  214. package/dist/test/utils/envelopes.test.d.ts +0 -1
  215. package/dist/test/utils/sdk.test.d.ts +0 -1
  216. package/dist/vitest.config.d.ts +0 -2
  217. /package/dist/{src/frontmatter.d.ts → frontmatter.d.ts} +0 -0
  218. /package/dist/{src/git-runtime.d.ts → git-runtime.d.ts} +0 -0
  219. /package/dist/{src/runtime.d.ts → runtime.d.ts} +0 -0
  220. /package/dist/{src/stores → stores}/cursor-store.d.ts +0 -0
  221. /package/dist/{src/stores → stores}/envelopes.d.ts +0 -0
  222. /package/dist/{src/stores → stores}/helpers.d.ts +0 -0
  223. /package/dist/{src/stores → stores}/lease-store.d.ts +0 -0
  224. /package/dist/{src/stores → stores}/run-store.d.ts +0 -0
  225. /package/dist/{src/stores → stores}/subscription-store.d.ts +0 -0
  226. /package/dist/{src/types → types}/agents.d.ts +0 -0
  227. /package/dist/{src/types → types}/cloudflare.d.ts +0 -0
  228. /package/dist/{src/wrangler-d1.d.ts → wrangler-d1.d.ts} +0 -0
@@ -0,0 +1,36 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { build } from 'esbuild';
4
+ import { loadTreeseedDeployConfig } from '../platform/deploy/config.js';
5
+ import { loadTreeseedManifest } from '../platform/tenant/config.js';
6
+ import { parseSiteConfig } from '../platform/utils/site-config-schema.js';
7
+ import { corePackageRoot } from '../operations/services/runtime-tools.js';
8
+ const tenantRoot = process.cwd();
9
+ const workerEntry = resolve(corePackageRoot, 'dist/worker/forms-worker.js');
10
+ const outFile = resolve(tenantRoot, '.treeseed/generated/worker/index.js');
11
+ function ensureDir(filePath) {
12
+ mkdirSync(dirname(filePath), { recursive: true });
13
+ }
14
+ function loadSiteConfig(tenantConfig) {
15
+ const siteConfigPath = resolve(tenantRoot, tenantConfig.siteConfigPath);
16
+ return parseSiteConfig(readFileSync(siteConfigPath, 'utf8'));
17
+ }
18
+ const tenantConfig = loadTreeseedManifest();
19
+ const siteConfig = loadSiteConfig(tenantConfig);
20
+ const deployConfig = loadTreeseedDeployConfig();
21
+ ensureDir(outFile);
22
+ await build({
23
+ entryPoints: [workerEntry],
24
+ outfile: outFile,
25
+ bundle: true,
26
+ format: 'esm',
27
+ platform: 'browser',
28
+ target: 'es2022',
29
+ logLevel: 'silent',
30
+ external: ['cloudflare:sockets'],
31
+ define: {
32
+ __TREESEED_SITE_CONFIG__: JSON.stringify(siteConfig),
33
+ __TREESEED_DEPLOY_CONFIG__: JSON.stringify(deployConfig),
34
+ },
35
+ });
36
+ writeFileSync(resolve(tenantRoot, '.treeseed/generated/worker/package.json'), '{\n "type": "module"\n}\n', 'utf8');
@@ -0,0 +1,373 @@
1
+ import fsSync from 'node:fs';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import process from 'node:process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { unified } from 'unified';
7
+ import remarkFrontmatter from 'remark-frontmatter';
8
+ import remarkGfm from 'remark-gfm';
9
+ import remarkMath from 'remark-math';
10
+ import remarkMdx from 'remark-mdx';
11
+ import remarkParse from 'remark-parse';
12
+ import remarkStringify from 'remark-stringify';
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const DEFAULT_TARGETS = ['src/content/knowledge', 'src/content/pages', 'src/content/notes'];
15
+ const MARKDOWN_EXTENSIONS = new Set(['.md', '.mdx']);
16
+ export async function normalizeMarkdown(source, options = {}) {
17
+ const normalizedSource = source.replace(/\r\n?/g, '\n');
18
+ const { frontmatter, body } = splitFrontmatter(normalizedSource);
19
+ const preprocessed = preprocessMarkdownBody(body);
20
+ const isMdxFile = (options.filePath ?? '').toLowerCase().endsWith('.mdx');
21
+ const processor = unified().use(remarkParse).use(remarkFrontmatter, ['yaml']).use(remarkGfm).use(remarkMath);
22
+ if (isMdxFile) {
23
+ processor.use(remarkMdx);
24
+ }
25
+ processor.use(remarkStringify, {
26
+ bullet: '-',
27
+ closeAtx: false,
28
+ fences: true,
29
+ incrementListMarker: true,
30
+ listItemIndent: 'one',
31
+ resourceLink: true,
32
+ rule: '*',
33
+ ruleRepetition: 3,
34
+ ruleSpaces: false,
35
+ setext: false,
36
+ tightDefinitions: true,
37
+ });
38
+ const rendered = postprocessMarkdown(String(await processor.process({ path: options.filePath ?? 'document.md', value: preprocessed }))).trimEnd();
39
+ const segments = [];
40
+ if (frontmatter) {
41
+ segments.push(frontmatter.trimEnd());
42
+ }
43
+ if (rendered) {
44
+ segments.push(rendered);
45
+ }
46
+ return `${segments.join('\n\n')}\n`;
47
+ }
48
+ export async function collectMarkdownTargets(inputPaths, cwd = process.cwd()) {
49
+ const requested = inputPaths.length > 0 ? inputPaths : DEFAULT_TARGETS;
50
+ const resolved = requested.map((target) => resolveTargetPath(target, cwd));
51
+ const files = [];
52
+ for (const target of resolved) {
53
+ const stats = await safeStat(target);
54
+ if (!stats) {
55
+ continue;
56
+ }
57
+ if (stats.isFile()) {
58
+ if (isMarkdownFile(target)) {
59
+ files.push(target);
60
+ }
61
+ continue;
62
+ }
63
+ files.push(...(await collectMarkdownFiles(target)));
64
+ }
65
+ return [...new Set(files)].sort((left, right) => left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }));
66
+ }
67
+ async function collectMarkdownFiles(rootPath) {
68
+ const entries = await fs.readdir(rootPath, { withFileTypes: true });
69
+ const files = [];
70
+ for (const entry of entries) {
71
+ const fullPath = path.join(rootPath, entry.name);
72
+ if (entry.isDirectory()) {
73
+ files.push(...(await collectMarkdownFiles(fullPath)));
74
+ continue;
75
+ }
76
+ if (entry.isFile() && isMarkdownFile(fullPath)) {
77
+ files.push(fullPath);
78
+ }
79
+ }
80
+ return files;
81
+ }
82
+ function isMarkdownFile(filePath) {
83
+ return MARKDOWN_EXTENSIONS.has(path.extname(filePath).toLowerCase());
84
+ }
85
+ async function safeStat(targetPath) {
86
+ try {
87
+ return await fs.stat(targetPath);
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ function resolveTargetPath(target, cwd) {
94
+ const directPath = path.resolve(cwd, target);
95
+ if (path.isAbsolute(target)) {
96
+ return target;
97
+ }
98
+ if (fsSyncExists(directPath)) {
99
+ return directPath;
100
+ }
101
+ if (path.basename(cwd) === 'docs' && target.startsWith('docs/')) {
102
+ return path.resolve(cwd, target.slice('docs/'.length));
103
+ }
104
+ return directPath;
105
+ }
106
+ function fsSyncExists(targetPath) {
107
+ try {
108
+ return fsSync.existsSync(targetPath);
109
+ }
110
+ catch {
111
+ return false;
112
+ }
113
+ }
114
+ function splitFrontmatter(source) {
115
+ if (!source.startsWith('---\n')) {
116
+ return { frontmatter: '', body: source };
117
+ }
118
+ const closingIndex = source.indexOf('\n---\n', 4);
119
+ if (closingIndex === -1) {
120
+ return { frontmatter: '', body: source };
121
+ }
122
+ const frontmatter = source.slice(0, closingIndex + 5);
123
+ const body = source.slice(closingIndex + 5);
124
+ return { frontmatter, body };
125
+ }
126
+ function preprocessMarkdownBody(body) {
127
+ const lines = body.split('\n');
128
+ const output = [];
129
+ let inFence = false;
130
+ let fenceMarker = '';
131
+ let fenceLength = 0;
132
+ let previousNonBlankType = 'start';
133
+ for (const originalLine of lines) {
134
+ const line = originalLine.trimEnd();
135
+ const trimmed = line.trim();
136
+ if (inFence) {
137
+ pushLine(output, line);
138
+ if (isFenceClose(line, fenceMarker, fenceLength)) {
139
+ inFence = false;
140
+ previousNonBlankType = 'fence';
141
+ }
142
+ continue;
143
+ }
144
+ if (trimmed === '') {
145
+ pushBlankLine(output);
146
+ continue;
147
+ }
148
+ if (isFenceOpen(line)) {
149
+ if (needsBlankLineBefore(previousNonBlankType)) {
150
+ pushBlankLine(output);
151
+ }
152
+ pushLine(output, line);
153
+ ({ marker: fenceMarker, length: fenceLength } = getFenceInfo(line));
154
+ inFence = true;
155
+ previousNonBlankType = 'fence';
156
+ continue;
157
+ }
158
+ const lineType = classifyLine(line);
159
+ const previousLine = output.length > 0 ? findPreviousNonBlankLine(output) : '';
160
+ if (requiresBlankLineBetween(previousNonBlankType, lineType, previousLine, line)) {
161
+ pushBlankLine(output);
162
+ }
163
+ pushLine(output, line);
164
+ previousNonBlankType = lineType;
165
+ }
166
+ return collapseBlankLines(output).join('\n').trim();
167
+ }
168
+ function classifyLine(line) {
169
+ if (isTableLine(line))
170
+ return 'table';
171
+ if (/^\s{0,3}#{1,6}\s+\S/.test(line))
172
+ return 'heading';
173
+ if (/^\s{0,3}(?:[-*_])(?:\s*\1){2,}\s*$/.test(line))
174
+ return 'rule';
175
+ if (/^\s{0,3}>\s?/.test(line))
176
+ return 'blockquote';
177
+ if (/^\s{0,3}(?:[-+*]|\d+[.)])\s+/.test(line))
178
+ return 'list';
179
+ if (/^\s*<(?:[A-Za-z][^>]*)>\s*$/.test(line))
180
+ return 'html';
181
+ if (/^\s*(?:import|export)\s.+$/.test(line))
182
+ return 'mdx';
183
+ if (/^\s*<\/?[A-Z][^>]*>\s*$/.test(line))
184
+ return 'mdx';
185
+ return 'prose';
186
+ }
187
+ function isTableLine(line) {
188
+ const trimmed = line.trim();
189
+ return /^\|.*\|\s*$/.test(trimmed) || /^[:\-|\s]+$/.test(trimmed);
190
+ }
191
+ function isFenceOpen(line) {
192
+ return /^\s{0,3}(```+|~~~+)/.test(line);
193
+ }
194
+ function getFenceInfo(line) {
195
+ const match = line.match(/^\s{0,3}(```+|~~~+)/);
196
+ return {
197
+ length: match?.[1]?.length ?? 3,
198
+ marker: match?.[1]?.[0] ?? '`',
199
+ };
200
+ }
201
+ function isFenceClose(line, marker, requiredLength) {
202
+ return new RegExp(`^\\s{0,3}${escapeRegExp(marker)}{${requiredLength},}\\s*$`).test(line);
203
+ }
204
+ function escapeRegExp(value) {
205
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
206
+ }
207
+ function needsBlankLineBefore(previousType) {
208
+ return !['start', 'blank'].includes(previousType);
209
+ }
210
+ function requiresBlankLineBetween(previousType, currentType, previousLine, currentLine) {
211
+ if (previousType === 'start')
212
+ return false;
213
+ if (previousType === 'table' && currentType === 'table')
214
+ return false;
215
+ if (previousType === 'blockquote' && currentType === 'blockquote')
216
+ return false;
217
+ if (previousType === 'mdx' && currentType === 'mdx')
218
+ return false;
219
+ if (previousType === 'html' && currentType === 'prose' && /^\s*<a\b/i.test(currentLine))
220
+ return false;
221
+ if (previousType === 'prose' && currentType === 'prose' && looksLikeParagraphBoundary(previousLine, currentLine))
222
+ return true;
223
+ if (currentType === 'heading' || currentType === 'rule' || currentType === 'blockquote')
224
+ return true;
225
+ if (previousType === 'heading')
226
+ return true;
227
+ if (previousType === 'rule')
228
+ return true;
229
+ if (previousType === 'table' && currentType !== 'table')
230
+ return true;
231
+ if (previousType === 'list' && currentType === 'prose')
232
+ return true;
233
+ if (previousType === 'list' && ['heading', 'rule', 'table', 'html', 'mdx'].includes(currentType))
234
+ return true;
235
+ if (previousType === 'prose' && ['list', 'table', 'html', 'mdx'].includes(currentType))
236
+ return true;
237
+ if (['html', 'mdx'].includes(previousType) && currentType === 'prose')
238
+ return true;
239
+ return false;
240
+ }
241
+ function findPreviousNonBlankLine(lines) {
242
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
243
+ if (lines[index].trim() !== '') {
244
+ return lines[index];
245
+ }
246
+ }
247
+ return '';
248
+ }
249
+ function pushLine(output, line) {
250
+ output.push(line);
251
+ }
252
+ function pushBlankLine(output) {
253
+ if (output.length === 0)
254
+ return;
255
+ if (output.at(-1) === '')
256
+ return;
257
+ output.push('');
258
+ }
259
+ function collapseBlankLines(lines) {
260
+ const collapsed = [];
261
+ for (const line of lines) {
262
+ if (line === '' && collapsed.at(-1) === '') {
263
+ continue;
264
+ }
265
+ collapsed.push(line);
266
+ }
267
+ return collapsed;
268
+ }
269
+ function postprocessMarkdown(content) {
270
+ return content
271
+ .replace(/\[\\\[(\d+)\\\]\]\(#ref-\1\)/g, '[[$1]](#ref-$1)')
272
+ .replace(/<a([^>]*?)\s*\/>/g, '<a$1></a>');
273
+ }
274
+ function looksLikeParagraphBoundary(previousLine, currentLine) {
275
+ const previous = previousLine.trim();
276
+ const current = currentLine.trim();
277
+ if (!previous || !current) {
278
+ return false;
279
+ }
280
+ if (/^\*\*\d+\./.test(current) || /^\*[^*]+:\*/.test(current)) {
281
+ return true;
282
+ }
283
+ if (/^\*\*.*\*\*$/.test(previous) && /^\*[^*]+:\*/.test(current)) {
284
+ return true;
285
+ }
286
+ return endsLikeStandaloneParagraph(previous) && startsLikeStandaloneParagraph(current);
287
+ }
288
+ function endsLikeStandaloneParagraph(line) {
289
+ return /(?:[.!?]["')\]]*|\*{1,2}|\d)\s*$/.test(line);
290
+ }
291
+ function startsLikeStandaloneParagraph(line) {
292
+ return /^(?:[A-Z0-9]|\*\*|\*[^*]+:\*|\[|["'(])/.test(line);
293
+ }
294
+ function parseArgs(argv) {
295
+ const args = { check: false, write: false, targets: [] };
296
+ for (const arg of argv) {
297
+ if (arg === '--check') {
298
+ args.check = true;
299
+ continue;
300
+ }
301
+ if (arg === '--write') {
302
+ args.write = true;
303
+ continue;
304
+ }
305
+ if (arg === '--help' || arg === '-h') {
306
+ args.help = true;
307
+ continue;
308
+ }
309
+ args.targets.push(arg);
310
+ }
311
+ if (!args.check && !args.write) {
312
+ args.write = true;
313
+ }
314
+ return args;
315
+ }
316
+ function printHelp() {
317
+ console.log(`Usage: node scripts/cleanup-markdown.mjs [--check|--write] [paths...]
318
+
319
+ Normalizes Markdown/MDX files for public docs readability.
320
+
321
+ Examples:
322
+ npm run cleanup:markdown --workspace docs -- src/content/knowledge/research
323
+ npm run cleanup:markdown:check --workspace docs -- src/content/pages/status.mdx
324
+ `);
325
+ }
326
+ async function runCli(argv = process.argv.slice(2)) {
327
+ const args = parseArgs(argv);
328
+ if (args.help) {
329
+ printHelp();
330
+ return 0;
331
+ }
332
+ if (args.check && args.write) {
333
+ console.error('Choose either --check or --write, not both.');
334
+ return 1;
335
+ }
336
+ const mode = args.check ? 'check' : 'write';
337
+ const targets = await collectMarkdownTargets(args.targets);
338
+ if (targets.length === 0) {
339
+ console.error('No Markdown files found for cleanup.');
340
+ return 1;
341
+ }
342
+ const changedFiles = [];
343
+ for (const filePath of targets) {
344
+ const original = await fs.readFile(filePath, 'utf8');
345
+ const normalized = await normalizeMarkdown(original, { filePath });
346
+ if (normalized === original) {
347
+ continue;
348
+ }
349
+ changedFiles.push(filePath);
350
+ if (mode === 'write') {
351
+ await fs.writeFile(filePath, normalized, 'utf8');
352
+ }
353
+ }
354
+ if (mode === 'check') {
355
+ if (changedFiles.length > 0) {
356
+ console.error('Markdown cleanup needed in:');
357
+ for (const filePath of changedFiles) {
358
+ console.error(`- ${path.relative(process.cwd(), filePath)}`);
359
+ }
360
+ return 1;
361
+ }
362
+ console.log(`Markdown cleanup check passed for ${targets.length} file(s).`);
363
+ return 0;
364
+ }
365
+ console.log(changedFiles.length > 0
366
+ ? `Normalized ${changedFiles.length} Markdown file(s).`
367
+ : `No Markdown changes needed across ${targets.length} file(s).`);
368
+ return 0;
369
+ }
370
+ if (process.argv[1] === __filename) {
371
+ const exitCode = await runCli();
372
+ process.exit(exitCode);
373
+ }
@@ -0,0 +1,48 @@
1
+ import { cpSync, mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const scriptsRoot = dirname(fileURLToPath(import.meta.url));
8
+ const cliRoot = resolve(scriptsRoot, '..');
9
+ const sharedFixtureRoot = resolve(cliRoot, '.fixtures', 'treeseed-fixtures', 'sites', 'working-site');
10
+
11
+ export function makeWorkspaceRoot() {
12
+ const root = mkdtempSync(join(tmpdir(), 'treeseed-help-workspace-'));
13
+ writeFileSync(resolve(root, 'package.json'), JSON.stringify({
14
+ name: 'help-test',
15
+ private: true,
16
+ workspaces: ['packages/*'],
17
+ }, null, 2));
18
+ writeFileSync(resolve(root, 'treeseed.site.yaml'), `name: Help Test
19
+ slug: help-test
20
+ siteUrl: https://example.com
21
+ contactEmail: hello@example.com
22
+ cloudflare:
23
+ accountId: account-123
24
+ `, 'utf8');
25
+ return root;
26
+ }
27
+
28
+ export function makeTenantWorkspace(branch = 'staging') {
29
+ const root = makeWorkspaceRoot();
30
+ cpSync(sharedFixtureRoot, root, { recursive: true });
31
+ mkdirSync(resolve(root, 'packages', 'placeholder'), { recursive: true });
32
+ writeFileSync(resolve(root, 'packages', 'placeholder', 'package.json'), JSON.stringify({
33
+ name: '@test/placeholder',
34
+ version: '0.0.1',
35
+ }, null, 2));
36
+ spawnSync('git', ['init', '-b', branch], { cwd: root, stdio: 'ignore' });
37
+ spawnSync('git', ['config', 'user.name', 'Treeseed Test'], { cwd: root, stdio: 'ignore' });
38
+ spawnSync('git', ['config', 'user.email', 'treeseed@example.com'], { cwd: root, stdio: 'ignore' });
39
+ spawnSync('git', ['add', '-A'], { cwd: root, stdio: 'ignore' });
40
+ spawnSync('git', ['commit', '-m', 'init'], { cwd: root, stdio: 'ignore' });
41
+ return root;
42
+ }
43
+
44
+ export function makeTenantRoot() {
45
+ const root = mkdtempSync(join(tmpdir(), 'treeseed-cli-test-'));
46
+ cpSync(sharedFixtureRoot, root, { recursive: true });
47
+ return root;
48
+ }
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ import readline from 'node:readline/promises';
3
+ import { stdin as input, stdout as output } from 'node:process';
4
+ import { collectCliPreflight } from '../operations/services/workspace-preflight.js';
5
+ import { applyTreeseedEnvironmentToProcess, ensureTreeseedGitignoreEntries, getTreeseedMachineConfigPaths, rotateTreeseedMachineKey, runTreeseedConfigWizard, writeTreeseedLocalEnvironmentFiles, } from '../operations/services/config-runtime.js';
6
+ const tenantRoot = process.cwd();
7
+ function parseArgs(argv) {
8
+ const parsed = {
9
+ scopes: [],
10
+ sync: 'all',
11
+ rotateMachineKey: false,
12
+ };
13
+ const rest = [...argv];
14
+ while (rest.length) {
15
+ const current = rest.shift();
16
+ if (!current) {
17
+ continue;
18
+ }
19
+ if (current === '--environment') {
20
+ parsed.scopes.push(rest.shift() ?? '');
21
+ continue;
22
+ }
23
+ if (current.startsWith('--environment=')) {
24
+ parsed.scopes.push(current.split('=', 2)[1] ?? '');
25
+ continue;
26
+ }
27
+ if (current === '--sync') {
28
+ parsed.sync = rest.shift() ?? 'all';
29
+ continue;
30
+ }
31
+ if (current.startsWith('--sync=')) {
32
+ parsed.sync = current.split('=', 2)[1] ?? 'all';
33
+ continue;
34
+ }
35
+ if (current === '--rotate-machine-key') {
36
+ parsed.rotateMachineKey = true;
37
+ continue;
38
+ }
39
+ throw new Error(`Unknown config argument: ${current}`);
40
+ }
41
+ return parsed;
42
+ }
43
+ const options = parseArgs(process.argv.slice(2));
44
+ const scopes = options.scopes.length === 0 || options.scopes.includes('all')
45
+ ? ['local', 'staging', 'prod']
46
+ : ['local', 'staging', 'prod'].filter((scope) => options.scopes.includes(scope));
47
+ ensureTreeseedGitignoreEntries(tenantRoot);
48
+ const preflight = collectCliPreflight({ cwd: tenantRoot, requireAuth: false });
49
+ const rl = readline.createInterface({ input, output });
50
+ try {
51
+ console.log('Treeseed configuration wizard');
52
+ console.log('This command writes a local machine config, generates .env.local and .dev.vars, and can sync GitHub or Cloudflare settings.');
53
+ console.log('Enter a value to set it, press Enter to keep the current/default value, or enter "-" to clear a value.\n');
54
+ if (options.rotateMachineKey) {
55
+ const result = rotateTreeseedMachineKey(tenantRoot);
56
+ console.log('Treeseed machine key rotated.');
57
+ console.log(`Machine key: ${result.keyPath}`);
58
+ }
59
+ else {
60
+ const result = await runTreeseedConfigWizard({
61
+ tenantRoot,
62
+ scopes,
63
+ sync: options.sync,
64
+ authStatus: preflight.checks.auth,
65
+ prompt: async (message) => {
66
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
67
+ return '';
68
+ }
69
+ try {
70
+ return await rl.question(message);
71
+ }
72
+ catch {
73
+ return '';
74
+ }
75
+ },
76
+ });
77
+ writeTreeseedLocalEnvironmentFiles(tenantRoot);
78
+ applyTreeseedEnvironmentToProcess({ tenantRoot, scope: 'local', override: true });
79
+ const { configPath, keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
80
+ console.log('\nTreeseed config completed.');
81
+ console.log(`Machine config: ${configPath}`);
82
+ console.log(`Machine key: ${keyPath}`);
83
+ console.log(`Updated values: ${result.updated.length}`);
84
+ console.log(`Initialized environments: ${result.initialized.length}`);
85
+ if (result.synced.github) {
86
+ console.log(`GitHub sync: ${result.synced.github.secrets.length} secrets, ${result.synced.github.variables.length} variables (${result.synced.github.repository})`);
87
+ }
88
+ if (result.synced.cloudflare) {
89
+ console.log(`Cloudflare sync: ${result.synced.cloudflare.secrets.length} secrets, ${result.synced.cloudflare.varsManagedByWranglerConfig.length} vars via Wrangler config`);
90
+ }
91
+ }
92
+ }
93
+ finally {
94
+ rl.close();
95
+ }
@@ -0,0 +1,29 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { dockerIsAvailable, findRunningMailpitContainer } from '../operations/services/mailpit-runtime.js';
3
+ import { mailpitComposeFile, packageRoot } from '../operations/services/runtime-paths.js';
4
+ if (!dockerIsAvailable()) {
5
+ console.error('Docker is required for Treeseed form email testing. Start Docker and rerun the Mailpit command.');
6
+ process.exit(1);
7
+ }
8
+ const existingMailpit = findRunningMailpitContainer();
9
+ if (existingMailpit) {
10
+ console.log(`Reusing existing Mailpit container "${existingMailpit.name}" on ports 1025 and 8025.`);
11
+ process.exit(0);
12
+ }
13
+ const result = spawnSync('docker', ['compose', '-f', mailpitComposeFile, 'up', '-d', 'mailpit'], {
14
+ encoding: 'utf8',
15
+ cwd: packageRoot,
16
+ env: { ...process.env },
17
+ });
18
+ if (result.status !== 0) {
19
+ const reusedMailpit = findRunningMailpitContainer();
20
+ if (reusedMailpit) {
21
+ console.log(`Reusing existing Mailpit container "${reusedMailpit.name}" on ports 1025 and 8025.`);
22
+ process.exit(0);
23
+ }
24
+ if (result.stdout)
25
+ process.stdout.write(result.stdout);
26
+ if (result.stderr)
27
+ process.stderr.write(result.stderr);
28
+ }
29
+ process.exit(result.status ?? 1);