@beechcms/cli 0.5.0 → 0.6.0-preview.2

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 (45) hide show
  1. package/coverage/base.css +224 -0
  2. package/coverage/block-navigation.js +87 -0
  3. package/coverage/favicon.png +0 -0
  4. package/coverage/index.html +116 -0
  5. package/coverage/lcov-report/base.css +224 -0
  6. package/coverage/lcov-report/block-navigation.js +87 -0
  7. package/coverage/lcov-report/favicon.png +0 -0
  8. package/coverage/lcov-report/index.html +116 -0
  9. package/coverage/lcov-report/prettify.css +1 -0
  10. package/coverage/lcov-report/prettify.js +2 -0
  11. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  12. package/coverage/lcov-report/sorter.js +210 -0
  13. package/coverage/lcov-report/validate.ts.html +325 -0
  14. package/coverage/lcov.info +80 -0
  15. package/coverage/prettify.css +1 -0
  16. package/coverage/prettify.js +2 -0
  17. package/coverage/sort-arrow-sprite.png +0 -0
  18. package/coverage/sorter.js +210 -0
  19. package/coverage/validate.ts.html +325 -0
  20. package/dist/commands/seed-load.d.ts +8 -0
  21. package/dist/commands/seed-load.d.ts.map +1 -0
  22. package/dist/commands/seed-load.js +89 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +241 -72
  26. package/dist/lib/schema-diff.d.ts +15 -0
  27. package/dist/lib/schema-diff.d.ts.map +1 -0
  28. package/dist/lib/schema-diff.js +37 -0
  29. package/dist/lib/wrangler.d.ts +17 -0
  30. package/dist/lib/wrangler.d.ts.map +1 -0
  31. package/dist/lib/wrangler.js +65 -0
  32. package/package.json +9 -5
  33. package/src/commands/deploy.ts +126 -123
  34. package/src/commands/init.ts +599 -557
  35. package/src/commands/onboard.ts +32 -0
  36. package/src/commands/seed-create.ts +192 -189
  37. package/src/commands/seed-load.ts +235 -155
  38. package/src/commands/update.ts +54 -51
  39. package/src/commands/validate.ts +80 -83
  40. package/src/index.ts +17 -12
  41. package/src/lib/schema-diff.ts +150 -64
  42. package/src/lib/wrangler.ts +129 -106
  43. package/tsconfig.json +16 -16
  44. package/tsconfig.tsbuildinfo +1 -1
  45. package/vitest.config.ts +33 -0
@@ -1,106 +1,129 @@
1
- import { spawnSync } from 'node:child_process'
2
- import { writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs'
3
- import { tmpdir } from 'node:os'
4
- import { join, resolve } from 'node:path'
5
-
6
- export interface WranglerOptions {
7
- db: string
8
- local: boolean
9
- configPath: string | null
10
- }
11
-
12
- export interface D1Row {
13
- [key: string]: unknown
14
- }
15
-
16
- interface WranglerResult {
17
- results: D1Row[]
18
- success: boolean
19
- error?: string
20
- }
21
-
22
- function buildArgs(options: WranglerOptions): string[] {
23
- const args: string[] = []
24
- if (options.configPath) args.push('--config', options.configPath)
25
- if (options.local) args.push('--local')
26
- else args.push('--remote')
27
- return args
28
- }
29
-
30
- /** Esegue SQL da file temporaneo via `wrangler d1 execute --file`. Returns true on success. */
31
- export function executeD1File(sql: string, options: WranglerOptions): boolean {
32
- const tmpFile = join(tmpdir(), `beech-seed-${Date.now()}.sql`)
33
- try {
34
- writeFileSync(tmpFile, sql, 'utf-8')
35
- const args = ['d1', 'execute', options.db, '--file', tmpFile, ...buildArgs(options)]
36
- const result = spawnSync('npx', ['wrangler', ...args], { stdio: 'inherit', cwd: process.cwd(), shell: true })
37
- return result.status === 0
38
- } finally {
39
- try { rmSync(tmpFile) } catch {}
40
- }
41
- }
42
-
43
- /** Esegue una query SQL e ritorna i risultati come array di oggetti (--json). */
44
- export function queryD1<T extends D1Row = D1Row>(sql: string, options: WranglerOptions): T[] {
45
- const args = ['d1', 'execute', options.db, '--command', sql, '--json', ...buildArgs(options)]
46
- const result = spawnSync('npx', ['wrangler', ...args], { encoding: 'utf-8', cwd: process.cwd(), shell: true })
47
-
48
- if (result.status !== 0) {
49
- throw new Error(`wrangler d1 execute failed:\n${result.stderr}`)
50
- }
51
-
52
- try {
53
- const parsed: WranglerResult[] = JSON.parse(result.stdout)
54
- return (parsed[0]?.results ?? []) as T[]
55
- } catch {
56
- throw new Error(`Failed to parse wrangler JSON output:\n${result.stdout}`)
57
- }
58
- }
59
-
60
- /** Trova il path di wrangler.jsonc risalendo l'albero da CWD fino alla root del filesystem. */
61
- export function findWranglerConfig(): string | null {
62
- let dir = process.cwd()
63
- while (true) {
64
- for (const name of ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']) {
65
- const p = resolve(dir, name)
66
- if (existsSync(p)) return p
67
- }
68
- for (const name of ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']) {
69
- const p = resolve(dir, 'apps', 'api', name)
70
- if (existsSync(p)) return p
71
- }
72
- const parent = resolve(dir, '..')
73
- if (parent === dir) break
74
- dir = parent
75
- }
76
- return null
77
- }
78
-
79
- /** Risolve il nome del database D1 da wrangler.jsonc (stripping JSONC comments). */
80
- export function resolveDbName(configPath: string | null): string {
81
- if (!configPath) return 'beech-db'
82
- try {
83
- const raw = readFileSync(configPath, 'utf-8')
84
-
85
- if (configPath.endsWith('.toml')) {
86
- // Basic regex-based TOML parsing for d1_databases
87
- // Matches both [d1_databases] and [[d1_databases]]
88
- const d1SectionMatch = raw.match(/\[\[?d1_databases\]\]?[\s\S]*?(?=\n\[|$)/)
89
- if (d1SectionMatch) {
90
- const section = d1SectionMatch[0]
91
- const dbNameMatch = section.match(/database_name\s*=\s*["'](.+?)["']/)
92
- if (dbNameMatch) return dbNameMatch[1]
93
- }
94
- return 'beech-db'
95
- }
96
-
97
- const stripped = raw
98
- .replace(/\/\/[^\n]*/g, '')
99
- .replace(/\/\*[\s\S]*?\*\//g, '')
100
- const parsed = JSON.parse(stripped)
101
- const bindings: { database_name?: string }[] = parsed?.d1_databases ?? []
102
- return bindings[0]?.database_name ?? 'beech-db'
103
- } catch {
104
- return 'beech-db'
105
- }
106
- }
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+
4
+ import { spawnSync, type SpawnSyncReturns } from 'node:child_process'
5
+ import { writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs'
6
+ import { tmpdir } from 'node:os'
7
+ import { join, resolve } from 'node:path'
8
+
9
+ export interface WranglerOptions {
10
+ db: string
11
+ local: boolean
12
+ configPath: string | null
13
+ }
14
+
15
+ export interface D1Row {
16
+ [key: string]: unknown
17
+ }
18
+
19
+ interface WranglerResult {
20
+ results: D1Row[]
21
+ success: boolean
22
+ error?: string
23
+ }
24
+
25
+ function buildArgs(options: WranglerOptions): string[] {
26
+ const args: string[] = []
27
+ if (options.configPath) args.push('--config', options.configPath)
28
+ if (options.local) args.push('--local')
29
+ else args.push('--remote')
30
+ return args
31
+ }
32
+
33
+ /** Esegue SQL da file temporaneo via `wrangler d1 execute --file`. Returns true on success. */
34
+ export function executeD1File(sql: string, options: WranglerOptions): boolean {
35
+ const tmpFile = join(tmpdir(), `beech-seed-${Date.now()}.sql`)
36
+ try {
37
+ writeFileSync(tmpFile, sql, 'utf-8')
38
+ const args = ['d1', 'execute', options.db, '--file', tmpFile, ...buildArgs(options)]
39
+ const result = spawnSync('npx', ['wrangler', ...args], { stdio: 'inherit', cwd: process.cwd(), shell: true })
40
+ return result.status === 0
41
+ } finally {
42
+ try { rmSync(tmpFile) } catch {}
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Esegue una query SQL e ritorna i risultati come array di oggetti (--json).
48
+ *
49
+ * Passa il SQL via file temporaneo (`--file`) anziché `--command`: su Windows,
50
+ * `spawnSync` con `shell: true` non preserva le virgolette/spazi/virgole interni
51
+ * a un argomento inline, e wrangler riceve il comando spezzato in token sciolti
52
+ * ("Unknown arguments: name, FROM, sqlite_master, …"). Il file evita del tutto
53
+ * il riquoting della shell — stesso approccio di `executeD1File`.
54
+ */
55
+ export function queryD1<T extends D1Row = D1Row>(sql: string, options: WranglerOptions): T[] {
56
+ const tmpFile = join(tmpdir(), `beech-query-${Date.now()}.sql`)
57
+ let result: SpawnSyncReturns<string>
58
+ try {
59
+ writeFileSync(tmpFile, sql, 'utf-8')
60
+ const args = ['d1', 'execute', options.db, '--file', tmpFile, '--json', ...buildArgs(options)]
61
+ result = spawnSync('npx', ['wrangler', ...args], { encoding: 'utf-8', cwd: process.cwd(), shell: true })
62
+ } finally {
63
+ try { rmSync(tmpFile) } catch {}
64
+ }
65
+
66
+ if (result.status !== 0) {
67
+ throw new Error(`wrangler d1 execute failed:\n${result.stderr}`)
68
+ }
69
+
70
+ try {
71
+ const parsed: WranglerResult[] = JSON.parse(result.stdout)
72
+ return (parsed[0]?.results ?? []) as T[]
73
+ } catch {
74
+ throw new Error(`Failed to parse wrangler JSON output:\n${result.stdout}`)
75
+ }
76
+ }
77
+
78
+ /** Trova il path di wrangler.jsonc risalendo l'albero da CWD fino alla root del filesystem. */
79
+ export function findWranglerConfig(): string | null {
80
+ let dir = process.cwd()
81
+ while (true) {
82
+ for (const name of ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']) {
83
+ const p = resolve(dir, name)
84
+ if (existsSync(p)) return p
85
+ }
86
+ for (const name of ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']) {
87
+ const p = resolve(dir, 'apps', 'api', name)
88
+ if (existsSync(p)) return p
89
+ }
90
+ const parent = resolve(dir, '..')
91
+ if (parent === dir) break
92
+ dir = parent
93
+ }
94
+ return null
95
+ }
96
+
97
+ /** Wraps a string value in single quotes, escaping internal single quotes for SQL literals. */
98
+ export function sqlQuote(value: string): string {
99
+ return `'${value.replace(/'/g, "''")}'`
100
+ }
101
+
102
+ /** Risolve il nome del database D1 da wrangler.jsonc (stripping JSONC comments). */
103
+ export function resolveDbName(configPath: string | null): string {
104
+ if (!configPath) return 'beech-db'
105
+ try {
106
+ const raw = readFileSync(configPath, 'utf-8')
107
+
108
+ if (configPath.endsWith('.toml')) {
109
+ // Basic regex-based TOML parsing for d1_databases
110
+ // Matches both [d1_databases] and [[d1_databases]]
111
+ const d1SectionMatch = raw.match(/\[\[?d1_databases\]\]?[\s\S]*?(?=\n\[|$)/)
112
+ if (d1SectionMatch) {
113
+ const section = d1SectionMatch[0]
114
+ const dbNameMatch = section.match(/database_name\s*=\s*["'](.+?)["']/)
115
+ if (dbNameMatch) return dbNameMatch[1]
116
+ }
117
+ return 'beech-db'
118
+ }
119
+
120
+ const stripped = raw
121
+ .replace(/\/\/[^\n]*/g, '')
122
+ .replace(/\/\*[\s\S]*?\*\//g, '')
123
+ const parsed = JSON.parse(stripped)
124
+ const bindings: { database_name?: string }[] = parsed?.d1_databases ?? []
125
+ return bindings[0]?.database_name ?? 'beech-db'
126
+ } catch {
127
+ return 'beech-db'
128
+ }
129
+ }
package/tsconfig.json CHANGED
@@ -1,16 +1,16 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "composite": true,
5
- "declaration": true,
6
- "declarationMap": true,
7
- "outDir": "dist",
8
- "rootDir": "src",
9
- "module": "ESNext",
10
- "moduleResolution": "Bundler",
11
- "target": "ES2022"
12
- },
13
- "include": ["src/**/*"],
14
- "exclude": ["node_modules", "dist"],
15
- "references": [{ "path": "../core" }]
16
- }
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "outDir": "dist",
8
+ "rootDir": "src",
9
+ "module": "ESNext",
10
+ "moduleResolution": "Bundler",
11
+ "target": "ES2022"
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "dist"],
15
+ "references": [{ "path": "../core" }]
16
+ }