@objectstack/cli 4.0.3 → 4.0.5

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 (241) hide show
  1. package/README.md +12 -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/compile.d.ts +3 -0
  7. package/dist/commands/compile.d.ts.map +1 -1
  8. package/dist/commands/compile.js +128 -6
  9. package/dist/commands/compile.js.map +1 -1
  10. package/dist/commands/create.js +1 -1
  11. package/dist/commands/data/create.js +2 -2
  12. package/dist/commands/data/create.js.map +1 -1
  13. package/dist/commands/data/delete.js +2 -2
  14. package/dist/commands/data/delete.js.map +1 -1
  15. package/dist/commands/data/get.js +2 -2
  16. package/dist/commands/data/get.js.map +1 -1
  17. package/dist/commands/data/query.js +2 -2
  18. package/dist/commands/data/query.js.map +1 -1
  19. package/dist/commands/data/update.js +2 -2
  20. package/dist/commands/data/update.js.map +1 -1
  21. package/dist/commands/dev.d.ts +3 -0
  22. package/dist/commands/dev.d.ts.map +1 -1
  23. package/dist/commands/dev.js +48 -19
  24. package/dist/commands/dev.js.map +1 -1
  25. package/dist/commands/generate.js +9 -9
  26. package/dist/commands/generate.js.map +1 -1
  27. package/dist/commands/i18n/check.d.ts +18 -0
  28. package/dist/commands/i18n/check.d.ts.map +1 -0
  29. package/dist/commands/i18n/check.js +153 -0
  30. package/dist/commands/i18n/check.js.map +1 -0
  31. package/dist/commands/init.js +2 -2
  32. package/dist/commands/lint.d.ts +3 -0
  33. package/dist/commands/lint.d.ts.map +1 -1
  34. package/dist/commands/lint.js +24 -0
  35. package/dist/commands/lint.js.map +1 -1
  36. package/dist/commands/login.d.ts +17 -0
  37. package/dist/commands/login.d.ts.map +1 -0
  38. package/dist/commands/login.js +313 -0
  39. package/dist/commands/login.js.map +1 -0
  40. package/dist/commands/logout.d.ts.map +1 -0
  41. package/dist/commands/{auth/logout.js → logout.js} +14 -2
  42. package/dist/commands/logout.js.map +1 -0
  43. package/dist/commands/meta/delete.js +2 -2
  44. package/dist/commands/meta/delete.js.map +1 -1
  45. package/dist/commands/meta/get.js +2 -2
  46. package/dist/commands/meta/get.js.map +1 -1
  47. package/dist/commands/meta/list.js +2 -2
  48. package/dist/commands/meta/list.js.map +1 -1
  49. package/dist/commands/meta/register.js +2 -2
  50. package/dist/commands/meta/register.js.map +1 -1
  51. package/dist/commands/projects/bind.d.ts +30 -0
  52. package/dist/commands/projects/bind.d.ts.map +1 -0
  53. package/dist/commands/projects/bind.js +132 -0
  54. package/dist/commands/projects/bind.js.map +1 -0
  55. package/dist/commands/projects/create.d.ts +28 -0
  56. package/dist/commands/projects/create.d.ts.map +1 -0
  57. package/dist/commands/projects/create.js +120 -0
  58. package/dist/commands/projects/create.js.map +1 -0
  59. package/dist/commands/projects/list.d.ts +21 -0
  60. package/dist/commands/projects/list.d.ts.map +1 -0
  61. package/dist/commands/projects/list.js +79 -0
  62. package/dist/commands/projects/list.js.map +1 -0
  63. package/dist/commands/projects/projects.test.d.ts +2 -0
  64. package/dist/commands/projects/projects.test.d.ts.map +1 -0
  65. package/dist/commands/projects/projects.test.js +56 -0
  66. package/dist/commands/projects/projects.test.js.map +1 -0
  67. package/dist/commands/projects/show.d.ts +21 -0
  68. package/dist/commands/projects/show.d.ts.map +1 -0
  69. package/dist/commands/projects/show.js +72 -0
  70. package/dist/commands/projects/show.js.map +1 -0
  71. package/dist/commands/projects/switch.d.ts +24 -0
  72. package/dist/commands/projects/switch.d.ts.map +1 -0
  73. package/dist/commands/projects/switch.js +64 -0
  74. package/dist/commands/projects/switch.js.map +1 -0
  75. package/dist/commands/publish.d.ts +14 -0
  76. package/dist/commands/publish.d.ts.map +1 -0
  77. package/dist/commands/publish.js +91 -0
  78. package/dist/commands/publish.js.map +1 -0
  79. package/dist/commands/{auth/login.d.ts → register.d.ts} +3 -2
  80. package/dist/commands/register.d.ts.map +1 -0
  81. package/dist/commands/{auth/login.js → register.js} +44 -61
  82. package/dist/commands/register.js.map +1 -0
  83. package/dist/commands/serve.d.ts +8 -0
  84. package/dist/commands/serve.d.ts.map +1 -1
  85. package/dist/commands/serve.js +606 -44
  86. package/dist/commands/serve.js.map +1 -1
  87. package/dist/commands/start.d.ts +11 -0
  88. package/dist/commands/start.d.ts.map +1 -0
  89. package/dist/commands/start.js +43 -0
  90. package/dist/commands/start.js.map +1 -0
  91. package/dist/commands/whoami.d.ts.map +1 -0
  92. package/dist/commands/{auth/whoami.js → whoami.js} +5 -5
  93. package/dist/commands/whoami.js.map +1 -0
  94. package/dist/index.d.ts +7 -4
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +8 -5
  97. package/dist/index.js.map +1 -1
  98. package/dist/utils/account.d.ts +31 -0
  99. package/dist/utils/account.d.ts.map +1 -0
  100. package/dist/utils/account.js +154 -0
  101. package/dist/utils/account.js.map +1 -0
  102. package/dist/utils/api-client.d.ts +10 -4
  103. package/dist/utils/api-client.d.ts.map +1 -1
  104. package/dist/utils/api-client.js +13 -7
  105. package/dist/utils/api-client.js.map +1 -1
  106. package/dist/utils/auth-config.d.ts +6 -0
  107. package/dist/utils/auth-config.d.ts.map +1 -1
  108. package/dist/utils/auth-config.js.map +1 -1
  109. package/dist/utils/build-runtime.d.ts +45 -0
  110. package/dist/utils/build-runtime.d.ts.map +1 -0
  111. package/dist/utils/build-runtime.js +154 -0
  112. package/dist/utils/build-runtime.js.map +1 -0
  113. package/dist/utils/config.d.ts.map +1 -1
  114. package/dist/utils/config.js +17 -2
  115. package/dist/utils/config.js.map +1 -1
  116. package/dist/utils/console.d.ts +32 -0
  117. package/dist/utils/console.d.ts.map +1 -0
  118. package/dist/utils/console.js +169 -0
  119. package/dist/utils/console.js.map +1 -0
  120. package/dist/utils/extract-hook-body.d.ts +13 -0
  121. package/dist/utils/extract-hook-body.d.ts.map +1 -0
  122. package/dist/utils/extract-hook-body.js +175 -0
  123. package/dist/utils/extract-hook-body.js.map +1 -0
  124. package/dist/utils/format.d.ts +8 -0
  125. package/dist/utils/format.d.ts.map +1 -1
  126. package/dist/utils/format.js +15 -2
  127. package/dist/utils/format.js.map +1 -1
  128. package/dist/utils/i18n-coverage.d.ts +61 -0
  129. package/dist/utils/i18n-coverage.d.ts.map +1 -0
  130. package/dist/utils/i18n-coverage.js +176 -0
  131. package/dist/utils/i18n-coverage.js.map +1 -0
  132. package/dist/utils/lower-callables.d.ts +17 -0
  133. package/dist/utils/lower-callables.d.ts.map +1 -0
  134. package/dist/utils/lower-callables.js +181 -0
  135. package/dist/utils/lower-callables.js.map +1 -0
  136. package/dist/utils/plugin-detection.d.ts +1 -0
  137. package/dist/utils/plugin-detection.d.ts.map +1 -1
  138. package/dist/utils/plugin-detection.js +41 -0
  139. package/dist/utils/plugin-detection.js.map +1 -1
  140. package/dist/utils/studio.d.ts +1 -0
  141. package/dist/utils/studio.d.ts.map +1 -1
  142. package/dist/utils/studio.js +25 -9
  143. package/dist/utils/studio.js.map +1 -1
  144. package/package.json +55 -21
  145. package/.turbo/turbo-build.log +0 -4
  146. package/CHANGELOG.md +0 -805
  147. package/bin/run-dev.js +0 -5
  148. package/dist/commands/auth/login.d.ts.map +0 -1
  149. package/dist/commands/auth/login.js.map +0 -1
  150. package/dist/commands/auth/logout.d.ts.map +0 -1
  151. package/dist/commands/auth/logout.js.map +0 -1
  152. package/dist/commands/auth/whoami.d.ts.map +0 -1
  153. package/dist/commands/auth/whoami.js.map +0 -1
  154. package/dist/commands/codemod/v2-to-v3.d.ts +0 -10
  155. package/dist/commands/codemod/v2-to-v3.d.ts.map +0 -1
  156. package/dist/commands/codemod/v2-to-v3.js +0 -145
  157. package/dist/commands/codemod/v2-to-v3.js.map +0 -1
  158. package/dist/commands/plugin/add.d.ts +0 -22
  159. package/dist/commands/plugin/add.d.ts.map +0 -1
  160. package/dist/commands/plugin/add.js +0 -93
  161. package/dist/commands/plugin/add.js.map +0 -1
  162. package/dist/commands/plugin/build.d.ts +0 -29
  163. package/dist/commands/plugin/build.d.ts.map +0 -1
  164. package/dist/commands/plugin/build.js +0 -170
  165. package/dist/commands/plugin/build.js.map +0 -1
  166. package/dist/commands/plugin/info.d.ts +0 -10
  167. package/dist/commands/plugin/info.d.ts.map +0 -1
  168. package/dist/commands/plugin/info.js +0 -65
  169. package/dist/commands/plugin/info.js.map +0 -1
  170. package/dist/commands/plugin/list.d.ts +0 -13
  171. package/dist/commands/plugin/list.d.ts.map +0 -1
  172. package/dist/commands/plugin/list.js +0 -78
  173. package/dist/commands/plugin/list.js.map +0 -1
  174. package/dist/commands/plugin/publish.d.ts +0 -27
  175. package/dist/commands/plugin/publish.d.ts.map +0 -1
  176. package/dist/commands/plugin/publish.js +0 -152
  177. package/dist/commands/plugin/publish.js.map +0 -1
  178. package/dist/commands/plugin/remove.d.ts +0 -20
  179. package/dist/commands/plugin/remove.d.ts.map +0 -1
  180. package/dist/commands/plugin/remove.js +0 -79
  181. package/dist/commands/plugin/remove.js.map +0 -1
  182. package/dist/commands/plugin/validate.d.ts +0 -23
  183. package/dist/commands/plugin/validate.d.ts.map +0 -1
  184. package/dist/commands/plugin/validate.js +0 -251
  185. package/dist/commands/plugin/validate.js.map +0 -1
  186. package/src/bin.ts +0 -13
  187. package/src/commands/auth/login.ts +0 -188
  188. package/src/commands/auth/logout.ts +0 -51
  189. package/src/commands/auth/whoami.ts +0 -85
  190. package/src/commands/codemod/v2-to-v3.ts +0 -171
  191. package/src/commands/compile.ts +0 -114
  192. package/src/commands/create.ts +0 -281
  193. package/src/commands/data/create.ts +0 -110
  194. package/src/commands/data/delete.ts +0 -84
  195. package/src/commands/data/get.ts +0 -84
  196. package/src/commands/data/query.ts +0 -127
  197. package/src/commands/data/update.ts +0 -114
  198. package/src/commands/dev.ts +0 -83
  199. package/src/commands/diff.ts +0 -294
  200. package/src/commands/doctor.ts +0 -572
  201. package/src/commands/explain.ts +0 -412
  202. package/src/commands/generate.ts +0 -924
  203. package/src/commands/info.ts +0 -124
  204. package/src/commands/init.ts +0 -327
  205. package/src/commands/lint.ts +0 -315
  206. package/src/commands/meta/delete.ts +0 -79
  207. package/src/commands/meta/get.ts +0 -73
  208. package/src/commands/meta/list.ts +0 -105
  209. package/src/commands/meta/register.ts +0 -97
  210. package/src/commands/plugin/add.ts +0 -112
  211. package/src/commands/plugin/build.ts +0 -193
  212. package/src/commands/plugin/info.ts +0 -79
  213. package/src/commands/plugin/list.ts +0 -93
  214. package/src/commands/plugin/publish.ts +0 -176
  215. package/src/commands/plugin/remove.ts +0 -97
  216. package/src/commands/plugin/validate.ts +0 -268
  217. package/src/commands/serve.ts +0 -411
  218. package/src/commands/studio.ts +0 -52
  219. package/src/commands/test.ts +0 -135
  220. package/src/commands/validate.ts +0 -143
  221. package/src/index.ts +0 -22
  222. package/src/utils/api-client.ts +0 -88
  223. package/src/utils/auth-config.ts +0 -107
  224. package/src/utils/config.ts +0 -80
  225. package/src/utils/format.ts +0 -267
  226. package/src/utils/output-formatter.ts +0 -91
  227. package/src/utils/plugin-detection.ts +0 -16
  228. package/src/utils/plugin-helpers.ts +0 -37
  229. package/src/utils/studio.ts +0 -350
  230. package/test/commands.test.ts +0 -128
  231. package/test/create.test.ts +0 -25
  232. package/test/plugin-commands.test.ts +0 -44
  233. package/test/plugin.test.ts +0 -169
  234. package/test/remote-api-commands.test.ts +0 -188
  235. package/test/remote-api-utils.test.ts +0 -196
  236. package/test/serve-host-config.test.ts +0 -77
  237. package/tsconfig.build.json +0 -20
  238. package/tsconfig.json +0 -25
  239. package/tsup.config.ts +0 -23
  240. /package/dist/commands/{auth/logout.d.ts → logout.d.ts} +0 -0
  241. /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
- });