@objectstack/cli 4.0.4 → 4.1.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 (269) hide show
  1. package/README.md +44 -25
  2. package/dist/commands/build.d.ts +5 -0
  3. package/dist/commands/build.d.ts.map +1 -0
  4. package/dist/commands/build.js +6 -0
  5. package/dist/commands/build.js.map +1 -0
  6. package/dist/commands/cloud/login.d.ts +16 -0
  7. package/dist/commands/cloud/login.d.ts.map +1 -0
  8. package/dist/commands/cloud/login.js +166 -0
  9. package/dist/commands/cloud/login.js.map +1 -0
  10. package/dist/commands/cloud/logout.d.ts +15 -0
  11. package/dist/commands/cloud/logout.d.ts.map +1 -0
  12. package/dist/commands/cloud/logout.js +51 -0
  13. package/dist/commands/cloud/logout.js.map +1 -0
  14. package/dist/commands/cloud/whoami.d.ts +15 -0
  15. package/dist/commands/cloud/whoami.d.ts.map +1 -0
  16. package/dist/commands/cloud/whoami.js +81 -0
  17. package/dist/commands/cloud/whoami.js.map +1 -0
  18. package/dist/commands/compile.d.ts +3 -0
  19. package/dist/commands/compile.d.ts.map +1 -1
  20. package/dist/commands/compile.js +128 -6
  21. package/dist/commands/compile.js.map +1 -1
  22. package/dist/commands/create.js +1 -1
  23. package/dist/commands/data/create.js +2 -2
  24. package/dist/commands/data/create.js.map +1 -1
  25. package/dist/commands/data/delete.js +2 -2
  26. package/dist/commands/data/delete.js.map +1 -1
  27. package/dist/commands/data/get.js +2 -2
  28. package/dist/commands/data/get.js.map +1 -1
  29. package/dist/commands/data/query.js +2 -2
  30. package/dist/commands/data/query.js.map +1 -1
  31. package/dist/commands/data/update.js +2 -2
  32. package/dist/commands/data/update.js.map +1 -1
  33. package/dist/commands/dev.d.ts +9 -0
  34. package/dist/commands/dev.d.ts.map +1 -1
  35. package/dist/commands/dev.js +116 -22
  36. package/dist/commands/dev.js.map +1 -1
  37. package/dist/commands/generate.js +9 -9
  38. package/dist/commands/generate.js.map +1 -1
  39. package/dist/commands/i18n/check.d.ts +18 -0
  40. package/dist/commands/i18n/check.d.ts.map +1 -0
  41. package/dist/commands/i18n/check.js +153 -0
  42. package/dist/commands/i18n/check.js.map +1 -0
  43. package/dist/commands/init.js +2 -2
  44. package/dist/commands/lint.d.ts +3 -0
  45. package/dist/commands/lint.d.ts.map +1 -1
  46. package/dist/commands/lint.js +24 -0
  47. package/dist/commands/lint.js.map +1 -1
  48. package/dist/commands/login.d.ts +17 -0
  49. package/dist/commands/login.d.ts.map +1 -0
  50. package/dist/commands/login.js +313 -0
  51. package/dist/commands/login.js.map +1 -0
  52. package/dist/commands/logout.d.ts.map +1 -0
  53. package/dist/commands/{auth/logout.js → logout.js} +14 -2
  54. package/dist/commands/logout.js.map +1 -0
  55. package/dist/commands/meta/delete.js +2 -2
  56. package/dist/commands/meta/delete.js.map +1 -1
  57. package/dist/commands/meta/get.js +2 -2
  58. package/dist/commands/meta/get.js.map +1 -1
  59. package/dist/commands/meta/list.js +2 -2
  60. package/dist/commands/meta/list.js.map +1 -1
  61. package/dist/commands/meta/register.js +2 -2
  62. package/dist/commands/meta/register.js.map +1 -1
  63. package/dist/commands/package/publish.d.ts +32 -0
  64. package/dist/commands/package/publish.d.ts.map +1 -0
  65. package/dist/commands/package/publish.js +324 -0
  66. package/dist/commands/package/publish.js.map +1 -0
  67. package/dist/commands/projects/bind.d.ts +30 -0
  68. package/dist/commands/projects/bind.d.ts.map +1 -0
  69. package/dist/commands/projects/bind.js +132 -0
  70. package/dist/commands/projects/bind.js.map +1 -0
  71. package/dist/commands/projects/create.d.ts +28 -0
  72. package/dist/commands/projects/create.d.ts.map +1 -0
  73. package/dist/commands/projects/create.js +120 -0
  74. package/dist/commands/projects/create.js.map +1 -0
  75. package/dist/commands/projects/list.d.ts +21 -0
  76. package/dist/commands/projects/list.d.ts.map +1 -0
  77. package/dist/commands/projects/list.js +79 -0
  78. package/dist/commands/projects/list.js.map +1 -0
  79. package/dist/commands/projects/projects.test.d.ts +2 -0
  80. package/dist/commands/projects/projects.test.d.ts.map +1 -0
  81. package/dist/commands/projects/projects.test.js +56 -0
  82. package/dist/commands/projects/projects.test.js.map +1 -0
  83. package/dist/commands/projects/show.d.ts +21 -0
  84. package/dist/commands/projects/show.d.ts.map +1 -0
  85. package/dist/commands/projects/show.js +72 -0
  86. package/dist/commands/projects/show.js.map +1 -0
  87. package/dist/commands/projects/switch.d.ts +24 -0
  88. package/dist/commands/projects/switch.d.ts.map +1 -0
  89. package/dist/commands/projects/switch.js +64 -0
  90. package/dist/commands/projects/switch.js.map +1 -0
  91. package/dist/commands/publish.d.ts +17 -0
  92. package/dist/commands/publish.d.ts.map +1 -0
  93. package/dist/commands/publish.js +135 -0
  94. package/dist/commands/publish.js.map +1 -0
  95. package/dist/commands/{auth/login.d.ts → register.d.ts} +3 -2
  96. package/dist/commands/register.d.ts.map +1 -0
  97. package/dist/commands/{auth/login.js → register.js} +44 -61
  98. package/dist/commands/register.js.map +1 -0
  99. package/dist/commands/rollback.d.ts +13 -0
  100. package/dist/commands/rollback.d.ts.map +1 -0
  101. package/dist/commands/rollback.js +77 -0
  102. package/dist/commands/rollback.js.map +1 -0
  103. package/dist/commands/serve.d.ts +22 -0
  104. package/dist/commands/serve.d.ts.map +1 -1
  105. package/dist/commands/serve.js +1173 -58
  106. package/dist/commands/serve.js.map +1 -1
  107. package/dist/commands/start.d.ts +18 -0
  108. package/dist/commands/start.d.ts.map +1 -0
  109. package/dist/commands/start.js +112 -0
  110. package/dist/commands/start.js.map +1 -0
  111. package/dist/commands/whoami.d.ts.map +1 -0
  112. package/dist/commands/{auth/whoami.js → whoami.js} +5 -5
  113. package/dist/commands/whoami.js.map +1 -0
  114. package/dist/index.d.ts +11 -4
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +14 -5
  117. package/dist/index.js.map +1 -1
  118. package/dist/utils/account.d.ts +31 -0
  119. package/dist/utils/account.d.ts.map +1 -0
  120. package/dist/utils/account.js +154 -0
  121. package/dist/utils/account.js.map +1 -0
  122. package/dist/utils/api-client.d.ts +10 -4
  123. package/dist/utils/api-client.d.ts.map +1 -1
  124. package/dist/utils/api-client.js +13 -7
  125. package/dist/utils/api-client.js.map +1 -1
  126. package/dist/utils/auth-config.d.ts +6 -0
  127. package/dist/utils/auth-config.d.ts.map +1 -1
  128. package/dist/utils/auth-config.js.map +1 -1
  129. package/dist/utils/auth-flows.d.ts +31 -0
  130. package/dist/utils/auth-flows.d.ts.map +1 -0
  131. package/dist/utils/auth-flows.js +151 -0
  132. package/dist/utils/auth-flows.js.map +1 -0
  133. package/dist/utils/build-runtime.d.ts +45 -0
  134. package/dist/utils/build-runtime.d.ts.map +1 -0
  135. package/dist/utils/build-runtime.js +154 -0
  136. package/dist/utils/build-runtime.js.map +1 -0
  137. package/dist/utils/cloud-config.d.ts +24 -0
  138. package/dist/utils/cloud-config.d.ts.map +1 -0
  139. package/dist/utils/cloud-config.js +75 -0
  140. package/dist/utils/cloud-config.js.map +1 -0
  141. package/dist/utils/config.d.ts.map +1 -1
  142. package/dist/utils/config.js +17 -2
  143. package/dist/utils/config.js.map +1 -1
  144. package/dist/utils/console.d.ts +33 -0
  145. package/dist/utils/console.d.ts.map +1 -0
  146. package/dist/utils/console.js +172 -0
  147. package/dist/utils/console.js.map +1 -0
  148. package/dist/utils/extract-hook-body.d.ts +13 -0
  149. package/dist/utils/extract-hook-body.d.ts.map +1 -0
  150. package/dist/utils/extract-hook-body.js +175 -0
  151. package/dist/utils/extract-hook-body.js.map +1 -0
  152. package/dist/utils/format.d.ts +8 -0
  153. package/dist/utils/format.d.ts.map +1 -1
  154. package/dist/utils/format.js +15 -2
  155. package/dist/utils/format.js.map +1 -1
  156. package/dist/utils/i18n-coverage.d.ts +61 -0
  157. package/dist/utils/i18n-coverage.d.ts.map +1 -0
  158. package/dist/utils/i18n-coverage.js +176 -0
  159. package/dist/utils/i18n-coverage.js.map +1 -0
  160. package/dist/utils/lower-callables.d.ts +17 -0
  161. package/dist/utils/lower-callables.d.ts.map +1 -0
  162. package/dist/utils/lower-callables.js +181 -0
  163. package/dist/utils/lower-callables.js.map +1 -0
  164. package/dist/utils/plugin-detection.d.ts +1 -0
  165. package/dist/utils/plugin-detection.d.ts.map +1 -1
  166. package/dist/utils/plugin-detection.js +41 -0
  167. package/dist/utils/plugin-detection.js.map +1 -1
  168. package/dist/utils/studio.d.ts +1 -0
  169. package/dist/utils/studio.d.ts.map +1 -1
  170. package/dist/utils/studio.js +24 -9
  171. package/dist/utils/studio.js.map +1 -1
  172. package/package.json +60 -22
  173. package/.turbo/turbo-build.log +0 -4
  174. package/CHANGELOG.md +0 -821
  175. package/bin/run-dev.js +0 -5
  176. package/dist/commands/auth/login.d.ts.map +0 -1
  177. package/dist/commands/auth/login.js.map +0 -1
  178. package/dist/commands/auth/logout.d.ts.map +0 -1
  179. package/dist/commands/auth/logout.js.map +0 -1
  180. package/dist/commands/auth/whoami.d.ts.map +0 -1
  181. package/dist/commands/auth/whoami.js.map +0 -1
  182. package/dist/commands/codemod/v2-to-v3.d.ts +0 -10
  183. package/dist/commands/codemod/v2-to-v3.d.ts.map +0 -1
  184. package/dist/commands/codemod/v2-to-v3.js +0 -145
  185. package/dist/commands/codemod/v2-to-v3.js.map +0 -1
  186. package/dist/commands/plugin/add.d.ts +0 -22
  187. package/dist/commands/plugin/add.d.ts.map +0 -1
  188. package/dist/commands/plugin/add.js +0 -93
  189. package/dist/commands/plugin/add.js.map +0 -1
  190. package/dist/commands/plugin/build.d.ts +0 -29
  191. package/dist/commands/plugin/build.d.ts.map +0 -1
  192. package/dist/commands/plugin/build.js +0 -170
  193. package/dist/commands/plugin/build.js.map +0 -1
  194. package/dist/commands/plugin/info.d.ts +0 -10
  195. package/dist/commands/plugin/info.d.ts.map +0 -1
  196. package/dist/commands/plugin/info.js +0 -65
  197. package/dist/commands/plugin/info.js.map +0 -1
  198. package/dist/commands/plugin/list.d.ts +0 -13
  199. package/dist/commands/plugin/list.d.ts.map +0 -1
  200. package/dist/commands/plugin/list.js +0 -78
  201. package/dist/commands/plugin/list.js.map +0 -1
  202. package/dist/commands/plugin/publish.d.ts +0 -27
  203. package/dist/commands/plugin/publish.d.ts.map +0 -1
  204. package/dist/commands/plugin/publish.js +0 -152
  205. package/dist/commands/plugin/publish.js.map +0 -1
  206. package/dist/commands/plugin/remove.d.ts +0 -20
  207. package/dist/commands/plugin/remove.d.ts.map +0 -1
  208. package/dist/commands/plugin/remove.js +0 -79
  209. package/dist/commands/plugin/remove.js.map +0 -1
  210. package/dist/commands/plugin/validate.d.ts +0 -23
  211. package/dist/commands/plugin/validate.d.ts.map +0 -1
  212. package/dist/commands/plugin/validate.js +0 -251
  213. package/dist/commands/plugin/validate.js.map +0 -1
  214. package/src/bin.ts +0 -13
  215. package/src/commands/auth/login.ts +0 -188
  216. package/src/commands/auth/logout.ts +0 -51
  217. package/src/commands/auth/whoami.ts +0 -85
  218. package/src/commands/codemod/v2-to-v3.ts +0 -171
  219. package/src/commands/compile.ts +0 -114
  220. package/src/commands/create.ts +0 -281
  221. package/src/commands/data/create.ts +0 -110
  222. package/src/commands/data/delete.ts +0 -84
  223. package/src/commands/data/get.ts +0 -84
  224. package/src/commands/data/query.ts +0 -127
  225. package/src/commands/data/update.ts +0 -114
  226. package/src/commands/dev.ts +0 -83
  227. package/src/commands/diff.ts +0 -294
  228. package/src/commands/doctor.ts +0 -572
  229. package/src/commands/explain.ts +0 -412
  230. package/src/commands/generate.ts +0 -924
  231. package/src/commands/info.ts +0 -124
  232. package/src/commands/init.ts +0 -327
  233. package/src/commands/lint.ts +0 -315
  234. package/src/commands/meta/delete.ts +0 -79
  235. package/src/commands/meta/get.ts +0 -73
  236. package/src/commands/meta/list.ts +0 -105
  237. package/src/commands/meta/register.ts +0 -97
  238. package/src/commands/plugin/add.ts +0 -112
  239. package/src/commands/plugin/build.ts +0 -193
  240. package/src/commands/plugin/info.ts +0 -79
  241. package/src/commands/plugin/list.ts +0 -93
  242. package/src/commands/plugin/publish.ts +0 -176
  243. package/src/commands/plugin/remove.ts +0 -97
  244. package/src/commands/plugin/validate.ts +0 -268
  245. package/src/commands/serve.ts +0 -411
  246. package/src/commands/studio.ts +0 -52
  247. package/src/commands/test.ts +0 -135
  248. package/src/commands/validate.ts +0 -143
  249. package/src/index.ts +0 -22
  250. package/src/utils/api-client.ts +0 -88
  251. package/src/utils/auth-config.ts +0 -107
  252. package/src/utils/config.ts +0 -80
  253. package/src/utils/format.ts +0 -267
  254. package/src/utils/output-formatter.ts +0 -91
  255. package/src/utils/plugin-detection.ts +0 -16
  256. package/src/utils/plugin-helpers.ts +0 -37
  257. package/src/utils/studio.ts +0 -350
  258. package/test/commands.test.ts +0 -128
  259. package/test/create.test.ts +0 -25
  260. package/test/plugin-commands.test.ts +0 -44
  261. package/test/plugin.test.ts +0 -169
  262. package/test/remote-api-commands.test.ts +0 -188
  263. package/test/remote-api-utils.test.ts +0 -196
  264. package/test/serve-host-config.test.ts +0 -77
  265. package/tsconfig.build.json +0 -20
  266. package/tsconfig.json +0 -25
  267. package/tsup.config.ts +0 -23
  268. /package/dist/commands/{auth/logout.d.ts → logout.d.ts} +0 -0
  269. /package/dist/commands/{auth/whoami.d.ts → whoami.d.ts} +0 -0
@@ -1,350 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- /**
4
- * Studio UI Integration Utilities
5
- *
6
- * Handles resolving, spawning, and proxying the @objectstack/studio
7
- * frontend when the CLI is started with --ui or via the `studio` command.
8
- */
9
- import path from 'path';
10
- import fs from 'fs';
11
- import net from 'net';
12
- import { createRequire } from 'module';
13
- import { pathToFileURL } from 'url';
14
- import { spawn, type ChildProcess } from 'child_process';
15
- import chalk from 'chalk';
16
-
17
- // ─── Constants ──────────────────────────────────────────────────────
18
-
19
- /** URL mount path for the Console UI inside the ObjectStack server */
20
- export const STUDIO_PATH = '/_studio';
21
-
22
- /** Internal port range start for the Vite dev server */
23
- const VITE_PORT_START = 24678;
24
-
25
- // ─── Path Resolution ────────────────────────────────────────────────
26
-
27
- /**
28
- * Resolve the filesystem path to the @objectstack/studio package.
29
- * Searches workspace locations first, then falls back to node_modules.
30
- */
31
- export function resolveStudioPath(): string | null {
32
- const cwd = process.cwd();
33
-
34
- // Workspace candidates (monorepo layouts)
35
- const candidates = [
36
- path.resolve(cwd, 'apps/studio'),
37
- path.resolve(cwd, '../../apps/studio'),
38
- path.resolve(cwd, '../apps/studio'),
39
- ];
40
-
41
- for (const candidate of candidates) {
42
- const pkgPath = path.join(candidate, 'package.json');
43
- if (fs.existsSync(pkgPath)) {
44
- try {
45
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
46
- if (pkg.name === '@objectstack/studio') return candidate;
47
- } catch {
48
- // Skip invalid package.json
49
- }
50
- }
51
- }
52
-
53
- // Fallback: resolve from node_modules via createRequire.
54
- // Try the consumer's cwd first (pnpm strict isolation means the CLI's own
55
- // import.meta.url cannot see the consumer's dependencies), then the CLI itself.
56
- const resolutionBases = [
57
- pathToFileURL(path.join(cwd, 'package.json')).href, // consumer workspace
58
- import.meta.url, // CLI package itself
59
- ];
60
-
61
- for (const base of resolutionBases) {
62
- try {
63
- const req = createRequire(base);
64
- const resolved = req.resolve('@objectstack/studio/package.json');
65
- return path.dirname(resolved);
66
- } catch {
67
- // Not resolvable from this base — try next
68
- }
69
- }
70
-
71
- // Last resort: direct filesystem check in cwd/node_modules
72
- const directPath = path.join(cwd, 'node_modules', '@objectstack', 'studio');
73
- if (fs.existsSync(path.join(directPath, 'package.json'))) {
74
- return directPath;
75
- }
76
-
77
- return null;
78
- }
79
-
80
- /**
81
- * Check whether the Studio has a pre-built `dist/` directory.
82
- */
83
- export function hasStudioDist(studioPath: string): boolean {
84
- return fs.existsSync(path.join(studioPath, 'dist', 'index.html'));
85
- }
86
-
87
- // ─── Port Utilities ─────────────────────────────────────────────────
88
-
89
- /**
90
- * Find the next available TCP port starting from `start`.
91
- */
92
- export function findAvailablePort(start: number = VITE_PORT_START): Promise<number> {
93
- return new Promise((resolve, reject) => {
94
- const server = net.createServer();
95
- server.once('error', () => {
96
- // Port in use — try next
97
- findAvailablePort(start + 1).then(resolve, reject);
98
- });
99
- server.once('listening', () => {
100
- server.close(() => resolve(start));
101
- });
102
- server.listen(start);
103
- });
104
- }
105
-
106
- // ─── Vite Dev Server ────────────────────────────────────────────────
107
-
108
- export interface ViteDevResult {
109
- /** Port the Vite dev server is listening on */
110
- port: number;
111
- /** Child process handle */
112
- process: ChildProcess;
113
- }
114
-
115
- /**
116
- * Spawn a Vite dev server for the Console application.
117
- *
118
- * Sets environment variables so the Console runs in server mode and
119
- * connects to the ObjectStack API on the same origin.
120
- *
121
- * @param studioPath - Absolute path to the @objectstack/studio package
122
- * @param options.serverPort - The main ObjectStack server port (for display only)
123
- */
124
- export async function spawnViteDevServer(
125
- studioPath: string,
126
- options: { serverPort?: number } = {},
127
- ): Promise<ViteDevResult> {
128
- const vitePort = await findAvailablePort(VITE_PORT_START);
129
-
130
- // Resolve the Vite binary from the Studio's own dependencies
131
- const viteBinCandidates = [
132
- path.join(studioPath, 'node_modules', '.bin', 'vite'),
133
- path.join(studioPath, '..', '..', 'node_modules', '.bin', 'vite'),
134
- ];
135
-
136
- let viteBin: string | null = null;
137
- for (const candidate of viteBinCandidates) {
138
- if (fs.existsSync(candidate)) {
139
- viteBin = candidate;
140
- break;
141
- }
142
- }
143
-
144
- const command = viteBin || 'npx';
145
- const args = viteBin
146
- ? ['--port', String(vitePort), '--strictPort']
147
- : ['vite', '--port', String(vitePort), '--strictPort'];
148
-
149
- const child = spawn(command, args, {
150
- cwd: studioPath,
151
- env: {
152
- ...process.env,
153
- VITE_BASE: `${STUDIO_PATH}/`,
154
- VITE_PORT: String(vitePort),
155
- VITE_HMR_PORT: String(vitePort),
156
- VITE_RUNTIME_MODE: 'server',
157
- VITE_SERVER_URL: '', // Same-origin API
158
- NODE_ENV: 'development',
159
- },
160
- stdio: ['ignore', 'pipe', 'pipe'],
161
- });
162
-
163
- // Accumulate stderr for error reporting
164
- let stderr = '';
165
- child.stderr?.on('data', (data: Buffer) => {
166
- stderr += data.toString();
167
- });
168
-
169
- // Wait for Vite to signal readiness
170
- await new Promise<void>((resolve, reject) => {
171
- const timeout = setTimeout(() => {
172
- child.kill();
173
- reject(new Error(`Vite dev server timed out after 30 s.\n${stderr}`));
174
- }, 30_000);
175
-
176
- child.stdout?.on('data', (data: Buffer) => {
177
- const output = data.toString();
178
- // Vite prints "ready in Xms" or "Local: http://..." when ready
179
- if (output.includes('Local:') || output.includes('ready in')) {
180
- clearTimeout(timeout);
181
- resolve();
182
- }
183
- });
184
-
185
- child.on('error', (err) => {
186
- clearTimeout(timeout);
187
- reject(err);
188
- });
189
-
190
- child.on('exit', (code) => {
191
- if (code !== 0 && code !== null) {
192
- clearTimeout(timeout);
193
- reject(new Error(`Vite exited with code ${code}.\n${stderr}`));
194
- }
195
- });
196
- });
197
-
198
- return { port: vitePort, process: child };
199
- }
200
-
201
- // ─── Console Plugin Factories ───────────────────────────────────────
202
-
203
- /**
204
- * Create a lightweight kernel plugin that proxies `/_studio/*` requests
205
- * to the Vite dev server. Used in development mode.
206
- */
207
- export function createStudioProxyPlugin(vitePort: number) {
208
- return {
209
- name: 'com.objectstack.studio-proxy',
210
-
211
- init: async () => {},
212
-
213
- start: async (ctx: any) => {
214
- const httpServer = ctx.getService?.('http.server');
215
- if (!httpServer?.getRawApp) {
216
- ctx.logger?.warn?.('Studio proxy: http.server service not found — skipping');
217
- return;
218
- }
219
-
220
- const app = httpServer.getRawApp();
221
-
222
- // Redirect bare path to trailing-slash (SPA convention)
223
- app.get(STUDIO_PATH, (c: any) => c.redirect(`${STUDIO_PATH}/`));
224
-
225
- // Proxy all /_studio/* requests to the Vite dev server
226
- app.all(`${STUDIO_PATH}/*`, async (c: any) => {
227
- const targetUrl = `http://localhost:${vitePort}${c.req.path}`;
228
-
229
- try {
230
- const headers = new Headers(c.req.raw.headers);
231
- headers.delete('host');
232
-
233
- const isBodyAllowed = !['GET', 'HEAD'].includes(c.req.method);
234
-
235
- const resp = await fetch(targetUrl, {
236
- method: c.req.method,
237
- headers,
238
- body: isBodyAllowed ? c.req.raw.body : undefined,
239
- duplex: isBodyAllowed ? 'half' : undefined,
240
- } as RequestInit);
241
-
242
- // Forward the full response (status, headers, body)
243
- return new Response(resp.body, {
244
- status: resp.status,
245
- headers: resp.headers,
246
- });
247
- } catch {
248
- return c.text('Console dev server is starting…', 502);
249
- }
250
- });
251
- },
252
- };
253
- }
254
-
255
- /**
256
- * Create a lightweight kernel plugin that serves the pre-built Studio
257
- * static files at `/_studio/*`. Used in production mode.
258
- *
259
- * Uses Node.js built-in fs for static file serving to avoid external
260
- * bundling dependencies.
261
- */
262
- export function createStudioStaticPlugin(distPath: string, options?: { isDev?: boolean }) {
263
- return {
264
- name: 'com.objectstack.studio-static',
265
-
266
- init: async () => {},
267
-
268
- start: async (ctx: any) => {
269
- const httpServer = ctx.getService?.('http.server');
270
- if (!httpServer?.getRawApp) {
271
- ctx.logger?.warn?.('Studio static: http.server service not found — skipping');
272
- return;
273
- }
274
-
275
- const app = httpServer.getRawApp();
276
- const absoluteDist = path.resolve(distPath);
277
-
278
- const indexPath = path.join(absoluteDist, 'index.html');
279
- if (!fs.existsSync(indexPath)) {
280
- ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
281
- return;
282
- }
283
-
284
- // Read and rewrite index.html so asset paths respect the mount path.
285
- // The dist may have been built with base '/' (absolute paths like
286
- // /assets/...) which won't resolve when mounted under /_studio/.
287
- const rawHtml = fs.readFileSync(indexPath, 'utf-8');
288
- const rewrittenHtml = rawHtml.replace(
289
- /(\s(?:href|src))="\/(?!\/)/g,
290
- `$1="${STUDIO_PATH}/`,
291
- );
292
-
293
- // In dev mode, redirect root to Studio for convenience
294
- if (options?.isDev) {
295
- app.get('/', (c: any) => c.redirect(`${STUDIO_PATH}/`));
296
- }
297
- // Redirect bare path
298
- app.get(STUDIO_PATH, (c: any) => c.redirect(`${STUDIO_PATH}/`));
299
-
300
- // Serve static files with SPA fallback
301
- app.get(`${STUDIO_PATH}/*`, async (c: any) => {
302
- const reqPath = c.req.path.substring(STUDIO_PATH.length) || '/';
303
- const filePath = path.join(absoluteDist, reqPath);
304
-
305
- // Security: prevent path traversal
306
- if (!filePath.startsWith(absoluteDist)) {
307
- return c.text('Forbidden', 403);
308
- }
309
-
310
- // Try serving the exact file
311
- if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
312
- const content = fs.readFileSync(filePath);
313
- return new Response(content, {
314
- headers: { 'content-type': mimeType(filePath) },
315
- });
316
- }
317
-
318
- // SPA fallback: serve rewritten index.html for non-file routes
319
- return new Response(rewrittenHtml, {
320
- headers: { 'content-type': 'text/html; charset=utf-8' },
321
- });
322
- });
323
- },
324
- };
325
- }
326
-
327
- // ─── Helpers ────────────────────────────────────────────────────────
328
-
329
- const MIME_TYPES: Record<string, string> = {
330
- '.html': 'text/html; charset=utf-8',
331
- '.js': 'application/javascript; charset=utf-8',
332
- '.mjs': 'application/javascript; charset=utf-8',
333
- '.css': 'text/css; charset=utf-8',
334
- '.json': 'application/json; charset=utf-8',
335
- '.svg': 'image/svg+xml',
336
- '.png': 'image/png',
337
- '.jpg': 'image/jpeg',
338
- '.jpeg': 'image/jpeg',
339
- '.gif': 'image/gif',
340
- '.ico': 'image/x-icon',
341
- '.woff': 'font/woff',
342
- '.woff2': 'font/woff2',
343
- '.ttf': 'font/ttf',
344
- '.map': 'application/json',
345
- };
346
-
347
- function mimeType(filePath: string): string {
348
- const ext = path.extname(filePath).toLowerCase();
349
- return MIME_TYPES[ext] || 'application/octet-stream';
350
- }
@@ -1,128 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import Compile from '../src/commands/compile';
3
- import Serve from '../src/commands/serve';
4
- import Dev from '../src/commands/dev';
5
- import Doctor from '../src/commands/doctor';
6
- import Create from '../src/commands/create';
7
- import Test from '../src/commands/test';
8
- import Validate from '../src/commands/validate';
9
- import Init from '../src/commands/init';
10
- import Info from '../src/commands/info';
11
- import Generate from '../src/commands/generate';
12
- import Lint from '../src/commands/lint';
13
- import Diff from '../src/commands/diff';
14
- import Explain from '../src/commands/explain';
15
- import Studio from '../src/commands/studio';
16
- import PluginList from '../src/commands/plugin/list';
17
- import PluginInfo from '../src/commands/plugin/info';
18
- import PluginAdd from '../src/commands/plugin/add';
19
- import PluginRemove from '../src/commands/plugin/remove';
20
- import PluginBuild from '../src/commands/plugin/build';
21
- import PluginValidate from '../src/commands/plugin/validate';
22
- import PluginPublish from '../src/commands/plugin/publish';
23
- import V2ToV3 from '../src/commands/codemod/v2-to-v3';
24
-
25
- describe('CLI Commands (oclif)', () => {
26
- it('should have compile command', () => {
27
- expect(Compile.description).toContain('Compile');
28
- });
29
-
30
- it('should have serve command', () => {
31
- expect(Serve.description).toContain('server');
32
- });
33
-
34
- it('should have dev command', () => {
35
- expect(Dev.description).toContain('development mode');
36
- });
37
-
38
- it('should have doctor command', () => {
39
- expect(Doctor.description).toContain('health');
40
- });
41
-
42
- it('should have create command', () => {
43
- expect(Create.description).toContain('Create');
44
- });
45
-
46
- it('should have test command', () => {
47
- expect(Test.description).toContain('Quality Protocol');
48
- });
49
-
50
- it('should have validate command', () => {
51
- expect(Validate.description).toContain('Validate');
52
- });
53
-
54
- it('should have init command', () => {
55
- expect(Init.description).toContain('Initialize');
56
- });
57
-
58
- it('should have info command', () => {
59
- expect(Info.description).toContain('summary');
60
- });
61
-
62
- it('should have generate command with alias', () => {
63
- expect(Generate.aliases).toContain('g');
64
- expect(Generate.description).toContain('Generate');
65
- });
66
-
67
- it('should have lint command', () => {
68
- expect(Lint.description).toContain('style');
69
- });
70
-
71
- it('should have diff command', () => {
72
- expect(Diff.description).toContain('Compare');
73
- });
74
-
75
- it('should have explain command', () => {
76
- expect(Explain.description).toContain('explanation');
77
- });
78
-
79
- it('should have studio command', () => {
80
- expect(Studio.description).toContain('Studio');
81
- });
82
-
83
- it('should have codemod v2-to-v3 command', () => {
84
- expect(V2ToV3.description).toContain('v2');
85
- });
86
-
87
- describe('Plugin subcommands', () => {
88
- it('should have plugin list command', () => {
89
- expect(PluginList.description).toContain('List');
90
- });
91
-
92
- it('should have plugin info command', () => {
93
- expect(PluginInfo.description).toContain('information');
94
- });
95
-
96
- it('should have plugin add command', () => {
97
- expect(PluginAdd.description).toContain('Add');
98
- });
99
-
100
- it('should have plugin remove command', () => {
101
- expect(PluginRemove.description).toContain('Remove');
102
- });
103
-
104
- it('should have plugin remove alias', () => {
105
- expect(PluginRemove.aliases).toContain('plugin rm');
106
- });
107
-
108
- it('should have plugin list alias', () => {
109
- expect(PluginList.aliases).toContain('plugin ls');
110
- });
111
-
112
- it('should have plugin build command', () => {
113
- expect(PluginBuild.description).toContain('Build');
114
- });
115
-
116
- it('should have plugin build alias', () => {
117
- expect(PluginBuild.aliases).toContain('plugin pack');
118
- });
119
-
120
- it('should have plugin validate command', () => {
121
- expect(PluginValidate.description).toContain('Validate');
122
- });
123
-
124
- it('should have plugin publish command', () => {
125
- expect(PluginPublish.description).toContain('Publish');
126
- });
127
- });
128
- });
@@ -1,25 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { templates } from '../src/commands/create';
3
-
4
- describe('Create Command Templates', () => {
5
- describe('Example Template', () => {
6
- it('should generate correct package.json scripts', () => {
7
- const packageJsonFn = templates.example.files['package.json'];
8
- const packageJson = packageJsonFn('test-app');
9
-
10
- expect(packageJson.scripts.dev).toBe('objectstack dev');
11
- expect(packageJson.scripts.build).toBe('objectstack compile');
12
- expect(packageJson.dependencies['@objectstack/cli']).toBe('workspace:*');
13
- });
14
- });
15
-
16
- describe('Plugin Template', () => {
17
- it('should generate correct dependencies', () => {
18
- const packageJsonFn = templates.plugin.files['package.json'];
19
- const packageJson = packageJsonFn('test-plugin');
20
-
21
- expect(packageJson.dependencies).toHaveProperty('@objectstack/spec');
22
- expect(packageJson.keywords).toContain('test-plugin');
23
- });
24
- });
25
- });
@@ -1,44 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
-
3
- /**
4
- * The custom loadPluginCommands mechanism has been removed.
5
- * Plugin command extension is now handled by oclif's built-in plugin system.
6
- *
7
- * Plugins extend the CLI by:
8
- * 1. Including `oclif` config in their package.json
9
- * 2. Exporting oclif Command classes from `src/commands/`
10
- * 3. Being installed via `os plugins install <package>`
11
- *
12
- * The objectstack.config.ts no longer determines CLI command availability.
13
- */
14
-
15
- describe('oclif Plugin System', () => {
16
- it('should have oclif plugins configured in package.json', async () => {
17
- const { createRequire } = await import('module');
18
- const require = createRequire(import.meta.url);
19
- const pkg = require('../package.json');
20
-
21
- expect(pkg.oclif).toBeDefined();
22
- expect(pkg.oclif.plugins).toContain('@oclif/plugin-help');
23
- expect(pkg.oclif.plugins).toContain('@oclif/plugin-plugins');
24
- });
25
-
26
- it('should have oclif command discovery configured', async () => {
27
- const { createRequire } = await import('module');
28
- const require = createRequire(import.meta.url);
29
- const pkg = require('../package.json');
30
-
31
- expect(pkg.oclif.commands).toBeDefined();
32
- expect(pkg.oclif.commands.strategy).toBe('pattern');
33
- expect(pkg.oclif.commands.target).toBe('./dist/commands');
34
- });
35
-
36
- it('should have bin entries pointing to oclif runner', async () => {
37
- const { createRequire } = await import('module');
38
- const require = createRequire(import.meta.url);
39
- const pkg = require('../package.json');
40
-
41
- expect(pkg.bin.os).toBe('./bin/run.js');
42
- expect(pkg.bin.objectstack).toBe('./bin/run.js');
43
- });
44
- });