@objectstack/cli 0.9.0 โ 0.9.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.
- package/CHANGELOG.md +25 -0
- package/README.md +495 -0
- package/dist/bin.js +127 -52
- package/package.json +11 -6
- package/src/bin.ts +5 -1
- package/src/commands/create.ts +10 -10
- package/src/commands/dev.ts +38 -4
- package/src/commands/serve.ts +70 -2
- package/test/commands.test.ts +39 -0
- package/test/create.test.ts +25 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @objectstack/cli
|
|
2
2
|
|
|
3
|
+
## 0.9.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @objectstack/spec@0.9.2
|
|
9
|
+
- @objectstack/core@0.9.2
|
|
10
|
+
- @objectstack/objectql@0.9.2
|
|
11
|
+
- @objectstack/driver-memory@0.9.2
|
|
12
|
+
- @objectstack/plugin-hono-server@0.9.2
|
|
13
|
+
- @objectstack/runtime@0.9.2
|
|
14
|
+
|
|
15
|
+
## 0.9.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Patch release for maintenance and stability improvements. All packages updated with unified versioning.
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
- @objectstack/spec@0.9.1
|
|
22
|
+
- @objectstack/core@0.9.1
|
|
23
|
+
- @objectstack/objectql@0.9.1
|
|
24
|
+
- @objectstack/runtime@0.9.1
|
|
25
|
+
- @objectstack/driver-memory@0.9.1
|
|
26
|
+
- @objectstack/plugin-hono-server@0.9.1
|
|
27
|
+
|
|
3
28
|
## 0.8.2
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# @objectstack/cli
|
|
2
|
+
|
|
3
|
+
Command Line Interface for ObjectStack Protocol - Development tools for building, serving, and managing ObjectStack applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ๐ **Development Server** - Hot-reload development server with auto-discovery
|
|
8
|
+
- ๐จ **Build & Compile** - Validate and compile ObjectStack configurations to JSON
|
|
9
|
+
- ๐ฅ **Environment Check** - Health check for development environment
|
|
10
|
+
- ๐ฆ **Project Scaffolding** - Generate plugins and examples from templates
|
|
11
|
+
- ๐งช **Test Runner** - Execute Quality Protocol test scenarios
|
|
12
|
+
- โก **Auto-Configuration** - Smart defaults with minimal configuration
|
|
13
|
+
|
|
14
|
+
## ๐ค AI Development Context
|
|
15
|
+
|
|
16
|
+
**Role**: Developer Tools & Terminal Interface
|
|
17
|
+
**Usage**:
|
|
18
|
+
- Runs the development server (`serve` command).
|
|
19
|
+
- Compiles metadata.
|
|
20
|
+
- Scaffolds new projects (`create`).
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
### Global Installation (Recommended)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g @objectstack/cli
|
|
28
|
+
# or
|
|
29
|
+
pnpm add -g @objectstack/cli
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Local Development
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm add -D @objectstack/cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
### `objectstack serve`
|
|
41
|
+
|
|
42
|
+
Start an ObjectStack server with automatic plugin loading and configuration.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Start server with default config (objectstack.config.ts)
|
|
46
|
+
objectstack serve
|
|
47
|
+
|
|
48
|
+
# Specify custom config file
|
|
49
|
+
objectstack serve my-config.ts
|
|
50
|
+
|
|
51
|
+
# Custom port
|
|
52
|
+
objectstack serve --port 8080
|
|
53
|
+
|
|
54
|
+
# Development mode (loads devPlugins)
|
|
55
|
+
objectstack serve --dev
|
|
56
|
+
|
|
57
|
+
# Skip HTTP server plugin
|
|
58
|
+
objectstack serve --no-server
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Features:**
|
|
62
|
+
- Auto-detects and loads `objectstack.config.ts`
|
|
63
|
+
- Auto-injects ObjectQL Engine if objects are defined
|
|
64
|
+
- Auto-injects Memory Driver in development mode
|
|
65
|
+
- Auto-registers AppPlugin for app configurations
|
|
66
|
+
- Finds available port if requested port is in use
|
|
67
|
+
- Pretty logging in development mode
|
|
68
|
+
- Graceful shutdown on SIGINT (Ctrl+C)
|
|
69
|
+
|
|
70
|
+
**Example:**
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// objectstack.config.ts
|
|
74
|
+
import { defineStack } from '@objectstack/spec';
|
|
75
|
+
|
|
76
|
+
export default defineStack({
|
|
77
|
+
manifest: {
|
|
78
|
+
name: 'my-app',
|
|
79
|
+
version: '1.0.0'
|
|
80
|
+
},
|
|
81
|
+
objects: [
|
|
82
|
+
{
|
|
83
|
+
name: 'todo_task',
|
|
84
|
+
fields: {
|
|
85
|
+
subject: { type: 'text', label: 'Subject' },
|
|
86
|
+
completed: { type: 'boolean', default: false }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
$ objectstack serve --dev
|
|
95
|
+
|
|
96
|
+
๐ ObjectStack Server
|
|
97
|
+
------------------------
|
|
98
|
+
๐ Config: objectstack.config.ts
|
|
99
|
+
๐ Port: 3000
|
|
100
|
+
|
|
101
|
+
๐ฆ Loading configuration...
|
|
102
|
+
โ Configuration loaded
|
|
103
|
+
๐ง Initializing ObjectStack kernel...
|
|
104
|
+
Auto-injecting ObjectQL Engine...
|
|
105
|
+
โ Registered ObjectQL Plugin (auto-detected)
|
|
106
|
+
Auto-injecting Memory Driver (Dev Mode)...
|
|
107
|
+
โ Registered Memory Driver (auto-detected)
|
|
108
|
+
โ Registered App Plugin (auto-detected)
|
|
109
|
+
โ Registered HTTP server plugin (port: 3000)
|
|
110
|
+
|
|
111
|
+
๐ Starting ObjectStack...
|
|
112
|
+
|
|
113
|
+
โ
ObjectStack server is running!
|
|
114
|
+
Press Ctrl+C to stop
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `objectstack dev`
|
|
118
|
+
|
|
119
|
+
Start development mode with watch and hot-reload capabilities.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Single package mode (if objectstack.config.ts exists in CWD)
|
|
123
|
+
objectstack dev
|
|
124
|
+
|
|
125
|
+
# Monorepo mode - run dev for all packages
|
|
126
|
+
objectstack dev
|
|
127
|
+
|
|
128
|
+
# Monorepo mode - filter specific package
|
|
129
|
+
objectstack dev @objectstack/core
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Behavior:**
|
|
133
|
+
- **Single Package Mode**: If `objectstack.config.ts` exists in current directory, delegates to `objectstack serve --dev` to start a development server with hot reload
|
|
134
|
+
- **Monorepo Mode**: If `pnpm-workspace.yaml` exists in current directory (workspace root), executes `pnpm dev` with optional package filter to run development builds across multiple packages in the workspace. The command uses pnpm's workspace filtering to target specific packages or all packages.
|
|
135
|
+
|
|
136
|
+
### `objectstack compile`
|
|
137
|
+
|
|
138
|
+
Compile and validate ObjectStack configuration files.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Compile default config
|
|
142
|
+
objectstack compile
|
|
143
|
+
|
|
144
|
+
# Custom source and output
|
|
145
|
+
objectstack compile src/config.ts build/app.json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Features:**
|
|
149
|
+
- Bundles TypeScript configuration files
|
|
150
|
+
- Validates against ObjectStack Protocol using Zod schemas
|
|
151
|
+
- Outputs optimized JSON artifact
|
|
152
|
+
- Shows validation errors with exact field paths
|
|
153
|
+
- Build time and artifact size reporting
|
|
154
|
+
|
|
155
|
+
**Example:**
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
$ objectstack compile
|
|
159
|
+
|
|
160
|
+
๐น ObjectStack Compiler v0.1
|
|
161
|
+
------------------------------
|
|
162
|
+
๐ Source: objectstack.config.ts
|
|
163
|
+
๐ฆ Bundling Configuration...
|
|
164
|
+
๐ Validating Protocol Compliance...
|
|
165
|
+
|
|
166
|
+
โ
Build Success (234ms)
|
|
167
|
+
๐ฆ Artifact: dist/objectstack.json (12.45 KB)
|
|
168
|
+
โจ Ready for Deployment
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `objectstack doctor`
|
|
172
|
+
|
|
173
|
+
Check development environment health and dependencies.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Quick health check
|
|
177
|
+
objectstack doctor
|
|
178
|
+
|
|
179
|
+
# Detailed information with fix suggestions
|
|
180
|
+
objectstack doctor --verbose
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Checks:**
|
|
184
|
+
- โ Node.js version (>= 18.0.0 required)
|
|
185
|
+
- โ pnpm package manager
|
|
186
|
+
- โ TypeScript compiler
|
|
187
|
+
- โ Dependencies installation status
|
|
188
|
+
- โ @objectstack/spec build status
|
|
189
|
+
- โ Git installation
|
|
190
|
+
|
|
191
|
+
**Example Output:**
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
$ objectstack doctor
|
|
195
|
+
|
|
196
|
+
๐ฅ ObjectStack Environment Health Check
|
|
197
|
+
-----------------------------------------
|
|
198
|
+
|
|
199
|
+
โ Node.js Version v20.10.0
|
|
200
|
+
โ pnpm Version 10.28.1
|
|
201
|
+
โ TypeScript Version 5.3.3
|
|
202
|
+
โ Dependencies Installed
|
|
203
|
+
โ @objectstack/spec Built
|
|
204
|
+
โ Git git version 2.39.0
|
|
205
|
+
|
|
206
|
+
โ
Environment is healthy and ready for development!
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### `objectstack create`
|
|
210
|
+
|
|
211
|
+
Create new plugins or examples from templates.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Create a plugin
|
|
215
|
+
objectstack create plugin my-feature
|
|
216
|
+
|
|
217
|
+
# Create an example
|
|
218
|
+
objectstack create example todo-app
|
|
219
|
+
|
|
220
|
+
# Custom directory
|
|
221
|
+
objectstack create plugin auth --dir custom/path
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Templates:**
|
|
225
|
+
|
|
226
|
+
#### Plugin Template
|
|
227
|
+
Creates a fully-configured ObjectStack plugin with:
|
|
228
|
+
- `package.json` with dependencies
|
|
229
|
+
- TypeScript configuration
|
|
230
|
+
- Plugin implementation boilerplate
|
|
231
|
+
- README with usage examples
|
|
232
|
+
- Test setup with Vitest
|
|
233
|
+
|
|
234
|
+
#### Example Template
|
|
235
|
+
Creates an ObjectStack example application with:
|
|
236
|
+
- `objectstack.config.ts` configuration
|
|
237
|
+
- Build and dev scripts
|
|
238
|
+
- TypeScript configuration
|
|
239
|
+
- Example README
|
|
240
|
+
|
|
241
|
+
**Example:**
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
$ objectstack create plugin authentication
|
|
245
|
+
|
|
246
|
+
๐ฆ ObjectStack Project Creator
|
|
247
|
+
-------------------------------
|
|
248
|
+
๐ Creating plugin: authentication
|
|
249
|
+
๐ Location: packages/plugins/plugin-authentication
|
|
250
|
+
|
|
251
|
+
โ Created package.json
|
|
252
|
+
โ Created tsconfig.json
|
|
253
|
+
โ Created src/index.ts
|
|
254
|
+
โ Created README.md
|
|
255
|
+
|
|
256
|
+
โ
Project created successfully!
|
|
257
|
+
|
|
258
|
+
Next steps:
|
|
259
|
+
cd packages/plugins/plugin-authentication
|
|
260
|
+
pnpm install
|
|
261
|
+
pnpm build
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### `objectstack test:run`
|
|
265
|
+
|
|
266
|
+
Run Quality Protocol test scenarios against your ObjectStack server.
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Run all tests in qa/ directory
|
|
270
|
+
objectstack test:run
|
|
271
|
+
|
|
272
|
+
# Specific test file or pattern
|
|
273
|
+
objectstack test:run qa/api-tests.json
|
|
274
|
+
|
|
275
|
+
# Custom target URL
|
|
276
|
+
objectstack test:run --url http://staging.example.com
|
|
277
|
+
|
|
278
|
+
# With authentication
|
|
279
|
+
objectstack test:run --token "Bearer xyz..."
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Features:**
|
|
283
|
+
- Executes test scenarios from JSON files
|
|
284
|
+
- HTTP-based test adapter
|
|
285
|
+
- Detailed step-by-step output
|
|
286
|
+
- Pass/fail reporting with timing
|
|
287
|
+
- Authentication support
|
|
288
|
+
|
|
289
|
+
## Configuration File
|
|
290
|
+
|
|
291
|
+
The CLI expects an `objectstack.config.ts` file in your project root:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { defineStack } from '@objectstack/spec';
|
|
295
|
+
import type { ObjectStackDefinition } from '@objectstack/spec';
|
|
296
|
+
|
|
297
|
+
export default defineStack({
|
|
298
|
+
// App metadata
|
|
299
|
+
manifest: {
|
|
300
|
+
name: 'my-app',
|
|
301
|
+
version: '1.0.0',
|
|
302
|
+
description: 'My ObjectStack Application'
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// Data objects
|
|
306
|
+
objects: [
|
|
307
|
+
{
|
|
308
|
+
name: 'customer',
|
|
309
|
+
label: 'Customer',
|
|
310
|
+
fields: {
|
|
311
|
+
name: { type: 'text', label: 'Name', required: true },
|
|
312
|
+
email: { type: 'email', label: 'Email' },
|
|
313
|
+
phone: { type: 'phone', label: 'Phone' }
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
],
|
|
317
|
+
|
|
318
|
+
// UI applications
|
|
319
|
+
apps: [
|
|
320
|
+
{
|
|
321
|
+
id: 'crm',
|
|
322
|
+
name: 'CRM',
|
|
323
|
+
objects: ['customer', 'opportunity']
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
|
|
327
|
+
// Runtime plugins
|
|
328
|
+
plugins: [
|
|
329
|
+
// Your production plugins
|
|
330
|
+
],
|
|
331
|
+
|
|
332
|
+
// Development-only plugins
|
|
333
|
+
devPlugins: [
|
|
334
|
+
// Loaded only with --dev flag
|
|
335
|
+
]
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Usage in Monorepo
|
|
340
|
+
|
|
341
|
+
The CLI is monorepo-aware and works seamlessly in pnpm workspaces:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
# From workspace root
|
|
345
|
+
pnpm --filter @objectstack/spec build
|
|
346
|
+
pnpm --filter my-app dev
|
|
347
|
+
|
|
348
|
+
# Or use the CLI shorthand
|
|
349
|
+
objectstack dev @objectstack/spec
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Environment Variables
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Server port (overridden by --port flag)
|
|
356
|
+
PORT=3000
|
|
357
|
+
|
|
358
|
+
# Node environment
|
|
359
|
+
NODE_ENV=development|production
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Aliases
|
|
363
|
+
|
|
364
|
+
The CLI binary is available as both `objectstack` and `os`:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
objectstack serve
|
|
368
|
+
# or
|
|
369
|
+
os serve
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Development Workflow
|
|
373
|
+
|
|
374
|
+
### New Project Setup
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# 1. Create a new example
|
|
378
|
+
objectstack create example my-project
|
|
379
|
+
|
|
380
|
+
# 2. Navigate to project
|
|
381
|
+
cd examples/my-project
|
|
382
|
+
|
|
383
|
+
# 3. Install dependencies
|
|
384
|
+
pnpm install
|
|
385
|
+
|
|
386
|
+
# 4. Start development server
|
|
387
|
+
objectstack dev
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Plugin Development
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# 1. Create plugin
|
|
394
|
+
objectstack create plugin my-feature
|
|
395
|
+
|
|
396
|
+
# 2. Implement plugin logic
|
|
397
|
+
# Edit packages/plugins/plugin-my-feature/src/index.ts
|
|
398
|
+
|
|
399
|
+
# 3. Build and test
|
|
400
|
+
cd packages/plugins/plugin-my-feature
|
|
401
|
+
pnpm build
|
|
402
|
+
pnpm test
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Production Build
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# 1. Compile configuration
|
|
409
|
+
objectstack compile
|
|
410
|
+
|
|
411
|
+
# 2. Run production server
|
|
412
|
+
NODE_ENV=production objectstack serve --port 8080
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Integration with Package Scripts
|
|
416
|
+
|
|
417
|
+
Add CLI commands to your `package.json`:
|
|
418
|
+
|
|
419
|
+
```json
|
|
420
|
+
{
|
|
421
|
+
"scripts": {
|
|
422
|
+
"build": "objectstack compile",
|
|
423
|
+
"dev": "objectstack dev",
|
|
424
|
+
"serve": "objectstack serve",
|
|
425
|
+
"test": "objectstack test:run",
|
|
426
|
+
"doctor": "objectstack doctor"
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Troubleshooting
|
|
432
|
+
|
|
433
|
+
### Port Already in Use
|
|
434
|
+
|
|
435
|
+
The CLI automatically finds an available port if the requested port is in use:
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
๐ Port: 3001 (requested: 3000 in use)
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Configuration Not Found
|
|
442
|
+
|
|
443
|
+
Ensure your config file exists and is properly named:
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
โ Configuration file not found: /path/to/objectstack.config.ts
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Default file name: `objectstack.config.ts`
|
|
450
|
+
|
|
451
|
+
### Validation Errors
|
|
452
|
+
|
|
453
|
+
The compiler will show detailed validation errors:
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
โ Validation Failed!
|
|
457
|
+
- [objects.0.name] Expected snake_case, received "MyObject"
|
|
458
|
+
- [objects.0.fields.name.type] Invalid enum value. Expected 'text' | 'number' | ...
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Environment Issues
|
|
462
|
+
|
|
463
|
+
Run the doctor command to diagnose:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
objectstack doctor --verbose
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## API Reference
|
|
470
|
+
|
|
471
|
+
### Command Options
|
|
472
|
+
|
|
473
|
+
All commands support `--help` for detailed usage:
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
objectstack --help
|
|
477
|
+
objectstack serve --help
|
|
478
|
+
objectstack compile --help
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Exit Codes
|
|
482
|
+
|
|
483
|
+
- `0` - Success
|
|
484
|
+
- `1` - Error (validation failed, file not found, etc.)
|
|
485
|
+
|
|
486
|
+
## Related Packages
|
|
487
|
+
|
|
488
|
+
- [@objectstack/spec](../spec) - Protocol definitions and schemas
|
|
489
|
+
- [@objectstack/core](../core) - Microkernel runtime
|
|
490
|
+
- [@objectstack/objectql](../objectql) - Data query engine
|
|
491
|
+
- [@objectstack/runtime](../runtime) - Runtime utilities and plugins
|
|
492
|
+
|
|
493
|
+
## License
|
|
494
|
+
|
|
495
|
+
MIT
|
package/dist/bin.js
CHANGED
|
@@ -4,20 +4,44 @@ import {
|
|
|
4
4
|
} from "./chunk-2YXVEYO7.js";
|
|
5
5
|
|
|
6
6
|
// src/bin.ts
|
|
7
|
+
import { createRequire } from "module";
|
|
7
8
|
import { Command as Command6 } from "commander";
|
|
8
9
|
|
|
9
10
|
// src/commands/dev.ts
|
|
10
11
|
import { Command } from "commander";
|
|
11
12
|
import chalk from "chalk";
|
|
12
|
-
import { execSync } from "child_process";
|
|
13
|
-
|
|
13
|
+
import { execSync, spawn } from "child_process";
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
var devCommand = new Command("dev").description("Start development mode for a package").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
|
|
14
17
|
console.log(chalk.bold(`
|
|
15
18
|
\u{1F680} ObjectStack Development Mode`));
|
|
16
19
|
console.log(chalk.dim(`-------------------------------`));
|
|
20
|
+
const configPath = path.resolve(process.cwd(), "objectstack.config.ts");
|
|
21
|
+
if (packageName === "all" && fs.existsSync(configPath)) {
|
|
22
|
+
console.log(chalk.blue(`\u{1F4C2} Detected package config: ${configPath}`));
|
|
23
|
+
console.log(chalk.green(`\u26A1 Starting Dev Server...`));
|
|
24
|
+
console.log("");
|
|
25
|
+
const binPath = process.argv[1];
|
|
26
|
+
const child = spawn(process.execPath, [binPath, "serve", "--dev", ...options.verbose ? ["--verbose"] : []], {
|
|
27
|
+
stdio: "inherit",
|
|
28
|
+
env: { ...process.env, NODE_ENV: "development" }
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
17
32
|
try {
|
|
18
33
|
const cwd = process.cwd();
|
|
19
|
-
const
|
|
20
|
-
|
|
34
|
+
const workspaceConfigPath = path.resolve(cwd, "pnpm-workspace.yaml");
|
|
35
|
+
const isWorkspaceRoot = fs.existsSync(workspaceConfigPath);
|
|
36
|
+
if (packageName === "all" && !isWorkspaceRoot) {
|
|
37
|
+
console.error(chalk.red(`
|
|
38
|
+
\u274C Configuration file (objectstack.config.ts) not found in ${cwd}`));
|
|
39
|
+
console.error(chalk.yellow(` To start development mode, run this command in a directory with objectstack.config.ts`));
|
|
40
|
+
console.error(chalk.yellow(` OR run from the monorepo root to start all packages.`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const filter = packageName === "all" ? "" : `--filter ${packageName}`;
|
|
44
|
+
console.log(`\u{1F4E6} Package: ${chalk.blue(packageName === "all" ? "All packages" : packageName)}`);
|
|
21
45
|
console.log(`\u{1F504} Watch mode: ${chalk.green("enabled")}`);
|
|
22
46
|
console.log("");
|
|
23
47
|
const command = `pnpm ${filter} dev`.trim();
|
|
@@ -39,8 +63,8 @@ var devCommand = new Command("dev").description("Start development mode for a pa
|
|
|
39
63
|
import { Command as Command2 } from "commander";
|
|
40
64
|
import chalk2 from "chalk";
|
|
41
65
|
import { execSync as execSync2 } from "child_process";
|
|
42
|
-
import
|
|
43
|
-
import
|
|
66
|
+
import fs2 from "fs";
|
|
67
|
+
import path2 from "path";
|
|
44
68
|
var doctorCommand = new Command2("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
|
|
45
69
|
console.log(chalk2.bold(`
|
|
46
70
|
\u{1F3E5} ObjectStack Environment Health Check`));
|
|
@@ -103,8 +127,8 @@ var doctorCommand = new Command2("doctor").description("Check development enviro
|
|
|
103
127
|
});
|
|
104
128
|
}
|
|
105
129
|
const cwd = process.cwd();
|
|
106
|
-
const nodeModulesPath =
|
|
107
|
-
if (
|
|
130
|
+
const nodeModulesPath = path2.join(cwd, "node_modules");
|
|
131
|
+
if (fs2.existsSync(nodeModulesPath)) {
|
|
108
132
|
results.push({
|
|
109
133
|
name: "Dependencies",
|
|
110
134
|
status: "ok",
|
|
@@ -118,8 +142,8 @@ var doctorCommand = new Command2("doctor").description("Check development enviro
|
|
|
118
142
|
fix: "Run: pnpm install"
|
|
119
143
|
});
|
|
120
144
|
}
|
|
121
|
-
const specDistPath =
|
|
122
|
-
if (
|
|
145
|
+
const specDistPath = path2.join(cwd, "packages/spec/dist");
|
|
146
|
+
if (fs2.existsSync(specDistPath)) {
|
|
123
147
|
results.push({
|
|
124
148
|
name: "@objectstack/spec",
|
|
125
149
|
status: "ok",
|
|
@@ -177,8 +201,8 @@ var doctorCommand = new Command2("doctor").description("Check development enviro
|
|
|
177
201
|
// src/commands/create.ts
|
|
178
202
|
import { Command as Command3 } from "commander";
|
|
179
203
|
import chalk3 from "chalk";
|
|
180
|
-
import
|
|
181
|
-
import
|
|
204
|
+
import fs3 from "fs";
|
|
205
|
+
import path3 from "path";
|
|
182
206
|
var templates = {
|
|
183
207
|
plugin: {
|
|
184
208
|
description: "Create a new ObjectStack plugin",
|
|
@@ -270,13 +294,13 @@ MIT
|
|
|
270
294
|
description: "Create a new ObjectStack example application",
|
|
271
295
|
files: {
|
|
272
296
|
"package.json": (name) => ({
|
|
273
|
-
name: `@
|
|
297
|
+
name: `@example/${name}`,
|
|
274
298
|
version: "0.1.0",
|
|
275
299
|
private: true,
|
|
276
300
|
description: `ObjectStack Example: ${name}`,
|
|
277
301
|
scripts: {
|
|
278
302
|
build: "objectstack compile",
|
|
279
|
-
dev: "
|
|
303
|
+
dev: "objectstack dev",
|
|
280
304
|
test: "vitest"
|
|
281
305
|
},
|
|
282
306
|
dependencies: {
|
|
@@ -294,20 +318,20 @@ MIT
|
|
|
294
318
|
"objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
|
|
295
319
|
|
|
296
320
|
export default defineStack({
|
|
297
|
-
|
|
321
|
+
manifest: {
|
|
298
322
|
name: '${name}',
|
|
299
323
|
version: '0.1.0',
|
|
300
324
|
description: '${name} example application',
|
|
301
325
|
},
|
|
302
326
|
|
|
303
|
-
objects:
|
|
327
|
+
objects: [
|
|
304
328
|
// Define your data objects here
|
|
305
|
-
|
|
329
|
+
// { name: 'my_object', fields: { ... } }
|
|
330
|
+
],
|
|
306
331
|
|
|
307
|
-
|
|
308
|
-
apps
|
|
309
|
-
|
|
310
|
-
},
|
|
332
|
+
apps: [
|
|
333
|
+
// Define your apps here
|
|
334
|
+
],
|
|
311
335
|
});
|
|
312
336
|
`,
|
|
313
337
|
"README.md": (name) => `# ${name} Example
|
|
@@ -367,13 +391,13 @@ var createCommand = new Command3("create").description("Create a new package, pl
|
|
|
367
391
|
const cwd = process.cwd();
|
|
368
392
|
let targetDir;
|
|
369
393
|
if (options?.dir) {
|
|
370
|
-
targetDir =
|
|
394
|
+
targetDir = path3.resolve(cwd, options.dir);
|
|
371
395
|
} else {
|
|
372
396
|
const baseDir = type === "plugin" ? "packages/plugins" : "examples";
|
|
373
397
|
const projectName = type === "plugin" ? `plugin-${name}` : name;
|
|
374
|
-
targetDir =
|
|
398
|
+
targetDir = path3.join(cwd, baseDir, projectName);
|
|
375
399
|
}
|
|
376
|
-
if (
|
|
400
|
+
if (fs3.existsSync(targetDir)) {
|
|
377
401
|
console.error(chalk3.red(`
|
|
378
402
|
\u274C Directory already exists: ${targetDir}`));
|
|
379
403
|
process.exit(1);
|
|
@@ -382,31 +406,31 @@ var createCommand = new Command3("create").description("Create a new package, pl
|
|
|
382
406
|
console.log(`\u{1F4C2} Location: ${chalk3.dim(targetDir)}`);
|
|
383
407
|
console.log("");
|
|
384
408
|
try {
|
|
385
|
-
|
|
409
|
+
fs3.mkdirSync(targetDir, { recursive: true });
|
|
386
410
|
for (const [filePath, contentFn] of Object.entries(template.files)) {
|
|
387
|
-
const fullPath =
|
|
388
|
-
const dir =
|
|
389
|
-
if (!
|
|
390
|
-
|
|
411
|
+
const fullPath = path3.join(targetDir, filePath);
|
|
412
|
+
const dir = path3.dirname(fullPath);
|
|
413
|
+
if (!fs3.existsSync(dir)) {
|
|
414
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
391
415
|
}
|
|
392
416
|
const content = contentFn(name);
|
|
393
417
|
const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
394
|
-
|
|
418
|
+
fs3.writeFileSync(fullPath, fileContent);
|
|
395
419
|
console.log(chalk3.green(`\u2713 Created ${filePath}`));
|
|
396
420
|
}
|
|
397
421
|
console.log("");
|
|
398
422
|
console.log(chalk3.green("\u2705 Project created successfully!"));
|
|
399
423
|
console.log("");
|
|
400
424
|
console.log(chalk3.bold("Next steps:"));
|
|
401
|
-
console.log(chalk3.dim(` cd ${
|
|
425
|
+
console.log(chalk3.dim(` cd ${path3.relative(cwd, targetDir)}`));
|
|
402
426
|
console.log(chalk3.dim(" pnpm install"));
|
|
403
427
|
console.log(chalk3.dim(" pnpm build"));
|
|
404
428
|
console.log("");
|
|
405
429
|
} catch (error) {
|
|
406
430
|
console.error(chalk3.red("\n\u274C Failed to create project:"));
|
|
407
431
|
console.error(error.message || error);
|
|
408
|
-
if (
|
|
409
|
-
|
|
432
|
+
if (fs3.existsSync(targetDir)) {
|
|
433
|
+
fs3.rmSync(targetDir, { recursive: true });
|
|
410
434
|
}
|
|
411
435
|
process.exit(1);
|
|
412
436
|
}
|
|
@@ -414,8 +438,8 @@ var createCommand = new Command3("create").description("Create a new package, pl
|
|
|
414
438
|
|
|
415
439
|
// src/commands/serve.ts
|
|
416
440
|
import { Command as Command4 } from "commander";
|
|
417
|
-
import
|
|
418
|
-
import
|
|
441
|
+
import path4 from "path";
|
|
442
|
+
import fs4 from "fs";
|
|
419
443
|
import net from "net";
|
|
420
444
|
import chalk4 from "chalk";
|
|
421
445
|
import { bundleRequire } from "bundle-require";
|
|
@@ -441,7 +465,7 @@ var getAvailablePort = async (startPort) => {
|
|
|
441
465
|
}
|
|
442
466
|
return port;
|
|
443
467
|
};
|
|
444
|
-
var serveCommand = new Command4("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
|
|
468
|
+
var serveCommand = new Command4("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
|
|
445
469
|
let port = parseInt(options.port);
|
|
446
470
|
try {
|
|
447
471
|
const availablePort = await getAvailablePort(port);
|
|
@@ -460,8 +484,8 @@ var serveCommand = new Command4("serve").description("Start ObjectStack server w
|
|
|
460
484
|
console.log(`\u{1F310} Port: ${chalk4.blue(port)}`);
|
|
461
485
|
}
|
|
462
486
|
console.log("");
|
|
463
|
-
const absolutePath =
|
|
464
|
-
if (!
|
|
487
|
+
const absolutePath = path4.resolve(process.cwd(), configPath);
|
|
488
|
+
if (!fs4.existsSync(absolutePath)) {
|
|
465
489
|
console.error(chalk4.red(`
|
|
466
490
|
\u274C Configuration file not found: ${absolutePath}`));
|
|
467
491
|
process.exit(1);
|
|
@@ -478,16 +502,65 @@ var serveCommand = new Command4("serve").description("Start ObjectStack server w
|
|
|
478
502
|
console.log(chalk4.green(`\u2713 Configuration loaded`));
|
|
479
503
|
const { ObjectKernel } = await import("@objectstack/core");
|
|
480
504
|
console.log(chalk4.yellow(`\u{1F527} Initializing ObjectStack kernel...`));
|
|
505
|
+
const isDev = options.dev || process.env.NODE_ENV === "development";
|
|
506
|
+
const loggerConfig = isDev ? { format: "pretty" } : void 0;
|
|
481
507
|
const kernel = new ObjectKernel({
|
|
482
508
|
metadata: config.metadata || {},
|
|
483
|
-
objects: config.objects || {}
|
|
509
|
+
objects: config.objects || {},
|
|
510
|
+
logger: loggerConfig
|
|
484
511
|
});
|
|
485
|
-
|
|
512
|
+
let plugins = config.plugins || [];
|
|
513
|
+
if (options.dev && config.devPlugins) {
|
|
514
|
+
console.log(chalk4.blue(`\u{1F4E6} Loading development plugins...`));
|
|
515
|
+
plugins = [...plugins, ...config.devPlugins];
|
|
516
|
+
}
|
|
517
|
+
const hasObjectQL = plugins.some((p) => p.name?.includes("objectql") || p.constructor?.name?.includes("ObjectQL"));
|
|
518
|
+
if (config.objects && !hasObjectQL) {
|
|
519
|
+
try {
|
|
520
|
+
console.log(chalk4.dim(` Auto-injecting ObjectQL Engine...`));
|
|
521
|
+
const { ObjectQLPlugin } = await import("@objectstack/objectql");
|
|
522
|
+
kernel.use(new ObjectQLPlugin());
|
|
523
|
+
console.log(chalk4.green(` \u2713 Registered ObjectQL Plugin (auto-detected)`));
|
|
524
|
+
} catch (e) {
|
|
525
|
+
console.warn(chalk4.yellow(` \u26A0 Could not auto-load ObjectQL: ${e.message}`));
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const hasDriver = plugins.some((p) => p.name?.includes("driver") || p.constructor?.name?.includes("Driver"));
|
|
529
|
+
if (isDev && !hasDriver && config.objects) {
|
|
530
|
+
try {
|
|
531
|
+
console.log(chalk4.dim(` Auto-injecting Memory Driver (Dev Mode)...`));
|
|
532
|
+
const { DriverPlugin } = await import("@objectstack/runtime");
|
|
533
|
+
const { InMemoryDriver } = await import("@objectstack/driver-memory");
|
|
534
|
+
kernel.use(new DriverPlugin(new InMemoryDriver()));
|
|
535
|
+
console.log(chalk4.green(` \u2713 Registered Memory Driver (auto-detected)`));
|
|
536
|
+
} catch (e) {
|
|
537
|
+
console.log(chalk4.dim(` \u2139 No default driver loaded: ${e.message}`));
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (config.objects || config.manifest || config.apps) {
|
|
541
|
+
try {
|
|
542
|
+
const { AppPlugin } = await import("@objectstack/runtime");
|
|
543
|
+
kernel.use(new AppPlugin(config));
|
|
544
|
+
console.log(chalk4.green(` \u2713 Registered App Plugin (auto-detected)`));
|
|
545
|
+
} catch (e) {
|
|
546
|
+
console.warn(chalk4.yellow(` \u26A0 Could not auto-load AppPlugin: ${e.message}`));
|
|
547
|
+
}
|
|
548
|
+
}
|
|
486
549
|
if (plugins.length > 0) {
|
|
487
550
|
console.log(chalk4.yellow(`\u{1F4E6} Loading ${plugins.length} plugin(s)...`));
|
|
488
551
|
for (const plugin of plugins) {
|
|
489
552
|
try {
|
|
490
|
-
|
|
553
|
+
let pluginToLoad = plugin;
|
|
554
|
+
if (typeof plugin === "string") {
|
|
555
|
+
console.log(chalk4.dim(` Trying to resolve plugin: ${plugin}`));
|
|
556
|
+
try {
|
|
557
|
+
const imported = await import(plugin);
|
|
558
|
+
pluginToLoad = imported.default || imported;
|
|
559
|
+
} catch (importError) {
|
|
560
|
+
throw new Error(`Failed to import plugin '${plugin}': ${importError.message}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
kernel.use(pluginToLoad);
|
|
491
564
|
const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
|
|
492
565
|
console.log(chalk4.green(` \u2713 Registered plugin: ${pluginName}`));
|
|
493
566
|
} catch (e) {
|
|
@@ -532,8 +605,8 @@ var serveCommand = new Command4("serve").description("Start ObjectStack server w
|
|
|
532
605
|
// src/commands/test.ts
|
|
533
606
|
import { Command as Command5 } from "commander";
|
|
534
607
|
import chalk5 from "chalk";
|
|
535
|
-
import
|
|
536
|
-
import
|
|
608
|
+
import path5 from "path";
|
|
609
|
+
import fs5 from "fs";
|
|
537
610
|
import { QA as CoreQA } from "@objectstack/core";
|
|
538
611
|
var testCommand = new Command5("test:run").description("Run Quality Protocol test scenarios").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
|
|
539
612
|
console.log(chalk5.bold(`
|
|
@@ -544,14 +617,14 @@ var testCommand = new Command5("test:run").description("Run Quality Protocol tes
|
|
|
544
617
|
const runner = new CoreQA.TestRunner(adapter);
|
|
545
618
|
const cwd = process.cwd();
|
|
546
619
|
const testFiles = [];
|
|
547
|
-
if (
|
|
620
|
+
if (fs5.existsSync(filesPattern)) {
|
|
548
621
|
testFiles.push(filesPattern);
|
|
549
622
|
} else {
|
|
550
|
-
const dir =
|
|
551
|
-
const ext =
|
|
552
|
-
if (
|
|
553
|
-
const files =
|
|
554
|
-
files.forEach((f) => testFiles.push(
|
|
623
|
+
const dir = path5.dirname(filesPattern);
|
|
624
|
+
const ext = path5.extname(filesPattern);
|
|
625
|
+
if (fs5.existsSync(dir)) {
|
|
626
|
+
const files = fs5.readdirSync(dir).filter((f) => f.endsWith(ext) || f.endsWith(".json"));
|
|
627
|
+
files.forEach((f) => testFiles.push(path5.join(dir, f)));
|
|
555
628
|
}
|
|
556
629
|
}
|
|
557
630
|
if (testFiles.length === 0) {
|
|
@@ -563,9 +636,9 @@ var testCommand = new Command5("test:run").description("Run Quality Protocol tes
|
|
|
563
636
|
let totalFailed = 0;
|
|
564
637
|
for (const file of testFiles) {
|
|
565
638
|
console.log(`
|
|
566
|
-
\u{1F4C4} Running suite: ${chalk5.bold(
|
|
639
|
+
\u{1F4C4} Running suite: ${chalk5.bold(path5.basename(file))}`);
|
|
567
640
|
try {
|
|
568
|
-
const content =
|
|
641
|
+
const content = fs5.readFileSync(file, "utf-8");
|
|
569
642
|
const suite = JSON.parse(content);
|
|
570
643
|
const results = await runner.runSuite(suite);
|
|
571
644
|
for (const result of results) {
|
|
@@ -602,8 +675,10 @@ var testCommand = new Command5("test:run").description("Run Quality Protocol tes
|
|
|
602
675
|
});
|
|
603
676
|
|
|
604
677
|
// src/bin.ts
|
|
678
|
+
var require2 = createRequire(import.meta.url);
|
|
679
|
+
var pkg = require2("../package.json");
|
|
605
680
|
var program = new Command6();
|
|
606
|
-
program.name("objectstack").description("CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture").version(
|
|
681
|
+
program.name("objectstack").description("CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture").version(pkg.version);
|
|
607
682
|
program.addCommand(compileCommand);
|
|
608
683
|
program.addCommand(serveCommand);
|
|
609
684
|
program.addCommand(devCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Command Line Interface for ObjectStack Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,21 +22,26 @@
|
|
|
22
22
|
"commander": "^11.1.0",
|
|
23
23
|
"tsx": "^4.7.1",
|
|
24
24
|
"zod": "^4.3.6",
|
|
25
|
-
"@objectstack/
|
|
26
|
-
"@objectstack/
|
|
27
|
-
"@objectstack/
|
|
25
|
+
"@objectstack/core": "0.9.2",
|
|
26
|
+
"@objectstack/driver-memory": "^0.9.2",
|
|
27
|
+
"@objectstack/objectql": "^0.9.2",
|
|
28
|
+
"@objectstack/plugin-hono-server": "0.9.2",
|
|
29
|
+
"@objectstack/runtime": "^0.9.2",
|
|
30
|
+
"@objectstack/spec": "0.9.2"
|
|
28
31
|
},
|
|
29
32
|
"peerDependencies": {
|
|
30
|
-
"@objectstack/core": "^0.9.
|
|
33
|
+
"@objectstack/core": "^0.9.2"
|
|
31
34
|
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"@types/node": "^25.1.0",
|
|
34
37
|
"tsup": "^8.0.2",
|
|
35
|
-
"typescript": "^5.3.3"
|
|
38
|
+
"typescript": "^5.3.3",
|
|
39
|
+
"vitest": "^4.0.18"
|
|
36
40
|
},
|
|
37
41
|
"scripts": {
|
|
38
42
|
"build": "tsup",
|
|
39
43
|
"dev": "tsup --watch",
|
|
44
|
+
"test": "vitest run",
|
|
40
45
|
"lint": "eslint src"
|
|
41
46
|
}
|
|
42
47
|
}
|
package/src/bin.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
1
2
|
import { Command } from 'commander';
|
|
2
3
|
import { compileCommand } from './commands/compile.js';
|
|
3
4
|
import { devCommand } from './commands/dev.js';
|
|
@@ -6,12 +7,15 @@ import { createCommand } from './commands/create.js';
|
|
|
6
7
|
import { serveCommand } from './commands/serve.js';
|
|
7
8
|
import { testCommand } from './commands/test.js';
|
|
8
9
|
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const pkg = require('../package.json');
|
|
12
|
+
|
|
9
13
|
const program = new Command();
|
|
10
14
|
|
|
11
15
|
program
|
|
12
16
|
.name('objectstack')
|
|
13
17
|
.description('CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture')
|
|
14
|
-
.version(
|
|
18
|
+
.version(pkg.version);
|
|
15
19
|
|
|
16
20
|
// Add all commands
|
|
17
21
|
program.addCommand(compileCommand);
|
package/src/commands/create.ts
CHANGED
|
@@ -4,7 +4,7 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
6
|
|
|
7
|
-
const templates = {
|
|
7
|
+
export const templates = {
|
|
8
8
|
plugin: {
|
|
9
9
|
description: 'Create a new ObjectStack plugin',
|
|
10
10
|
files: {
|
|
@@ -96,13 +96,13 @@ MIT
|
|
|
96
96
|
description: 'Create a new ObjectStack example application',
|
|
97
97
|
files: {
|
|
98
98
|
'package.json': (name: string) => ({
|
|
99
|
-
name: `@
|
|
99
|
+
name: `@example/${name}`,
|
|
100
100
|
version: '0.1.0',
|
|
101
101
|
private: true,
|
|
102
102
|
description: `ObjectStack Example: ${name}`,
|
|
103
103
|
scripts: {
|
|
104
104
|
build: 'objectstack compile',
|
|
105
|
-
dev: '
|
|
105
|
+
dev: 'objectstack dev',
|
|
106
106
|
test: 'vitest',
|
|
107
107
|
},
|
|
108
108
|
dependencies: {
|
|
@@ -120,20 +120,20 @@ MIT
|
|
|
120
120
|
'objectstack.config.ts': (name: string) => `import { defineStack } from '@objectstack/spec';
|
|
121
121
|
|
|
122
122
|
export default defineStack({
|
|
123
|
-
|
|
123
|
+
manifest: {
|
|
124
124
|
name: '${name}',
|
|
125
125
|
version: '0.1.0',
|
|
126
126
|
description: '${name} example application',
|
|
127
127
|
},
|
|
128
128
|
|
|
129
|
-
objects:
|
|
129
|
+
objects: [
|
|
130
130
|
// Define your data objects here
|
|
131
|
-
|
|
131
|
+
// { name: 'my_object', fields: { ... } }
|
|
132
|
+
],
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
apps
|
|
135
|
-
|
|
136
|
-
},
|
|
134
|
+
apps: [
|
|
135
|
+
// Define your apps here
|
|
136
|
+
],
|
|
137
137
|
});
|
|
138
138
|
`,
|
|
139
139
|
'README.md': (name: string) => `# ${name} Example
|
package/src/commands/dev.ts
CHANGED
|
@@ -1,24 +1,58 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { execSync } from 'child_process';
|
|
3
|
+
import { execSync, spawn } from 'child_process';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
|
|
8
8
|
export const devCommand = new Command('dev')
|
|
9
9
|
.description('Start development mode for a package')
|
|
10
|
-
.argument('[package]', 'Package name
|
|
10
|
+
.argument('[package]', 'Package name or filter pattern', 'all')
|
|
11
11
|
.option('-w, --watch', 'Enable watch mode (default)', true)
|
|
12
12
|
.option('-v, --verbose', 'Verbose output')
|
|
13
13
|
.action(async (packageName, options) => {
|
|
14
14
|
console.log(chalk.bold(`\n๐ ObjectStack Development Mode`));
|
|
15
15
|
console.log(chalk.dim(`-------------------------------`));
|
|
16
16
|
|
|
17
|
+
// Check if we are running inside a package (Single Package Mode)
|
|
18
|
+
// If "package" argument is 'all' (default) AND objectstack.config.ts exists in CWD
|
|
19
|
+
const configPath = path.resolve(process.cwd(), 'objectstack.config.ts');
|
|
20
|
+
if (packageName === 'all' && fs.existsSync(configPath)) {
|
|
21
|
+
console.log(chalk.blue(`๐ Detected package config: ${configPath}`));
|
|
22
|
+
console.log(chalk.green(`โก Starting Dev Server...`));
|
|
23
|
+
console.log('');
|
|
24
|
+
|
|
25
|
+
// Delegate to 'serve --dev'
|
|
26
|
+
// We spawn a new process to ensure clean environment and watch capabilities (plugin-loader etc)
|
|
27
|
+
// usage: objectstack serve --dev
|
|
28
|
+
const binPath = process.argv[1]; // path to objectstack bin
|
|
29
|
+
|
|
30
|
+
const child = spawn(process.execPath, [binPath, 'serve', '--dev', ...(options.verbose ? ['--verbose'] : [])], {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
env: { ...process.env, NODE_ENV: 'development' }
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Monorepo Orchestration Mode
|
|
17
39
|
try {
|
|
18
40
|
const cwd = process.cwd();
|
|
19
|
-
const filter = packageName === 'all' ? '' : `--filter @objectstack/${packageName}`;
|
|
20
41
|
|
|
21
|
-
|
|
42
|
+
// Only attempt monorepo orchestration if we are in a workspace root
|
|
43
|
+
const workspaceConfigPath = path.resolve(cwd, 'pnpm-workspace.yaml');
|
|
44
|
+
const isWorkspaceRoot = fs.existsSync(workspaceConfigPath);
|
|
45
|
+
|
|
46
|
+
if (packageName === 'all' && !isWorkspaceRoot) {
|
|
47
|
+
console.error(chalk.red(`\nโ Configuration file (objectstack.config.ts) not found in ${cwd}`));
|
|
48
|
+
console.error(chalk.yellow(` To start development mode, run this command in a directory with objectstack.config.ts`));
|
|
49
|
+
console.error(chalk.yellow(` OR run from the monorepo root to start all packages.`));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const filter = packageName === 'all' ? '' : `--filter ${packageName}`;
|
|
54
|
+
|
|
55
|
+
console.log(`๐ฆ Package: ${chalk.blue(packageName === 'all' ? 'All packages' : packageName)}`);
|
|
22
56
|
console.log(`๐ Watch mode: ${chalk.green('enabled')}`);
|
|
23
57
|
console.log('');
|
|
24
58
|
|
package/src/commands/serve.ts
CHANGED
|
@@ -34,6 +34,7 @@ export const serveCommand = new Command('serve')
|
|
|
34
34
|
.description('Start ObjectStack server with plugins from configuration')
|
|
35
35
|
.argument('[config]', 'Configuration file path', 'objectstack.config.ts')
|
|
36
36
|
.option('-p, --port <port>', 'Server port', '3000')
|
|
37
|
+
.option('--dev', 'Run in development mode (load devPlugins)')
|
|
37
38
|
.option('--no-server', 'Skip starting HTTP server plugin')
|
|
38
39
|
.action(async (configPath, options) => {
|
|
39
40
|
let port = parseInt(options.port);
|
|
@@ -84,20 +85,87 @@ export const serveCommand = new Command('serve')
|
|
|
84
85
|
|
|
85
86
|
// Create kernel instance
|
|
86
87
|
console.log(chalk.yellow(`๐ง Initializing ObjectStack kernel...`));
|
|
88
|
+
|
|
89
|
+
// Auto-configure pretty logging in development mode
|
|
90
|
+
const isDev = options.dev || process.env.NODE_ENV === 'development';
|
|
91
|
+
const loggerConfig = isDev ? { format: 'pretty' } : undefined;
|
|
92
|
+
|
|
87
93
|
const kernel = new ObjectKernel({
|
|
88
94
|
metadata: config.metadata || {},
|
|
89
95
|
objects: config.objects || {},
|
|
96
|
+
logger: loggerConfig
|
|
90
97
|
});
|
|
91
98
|
|
|
92
99
|
// Load plugins from configuration
|
|
93
|
-
|
|
100
|
+
let plugins = config.plugins || [];
|
|
101
|
+
|
|
102
|
+
// Merge devPlugins if in dev mode
|
|
103
|
+
if (options.dev && config.devPlugins) {
|
|
104
|
+
console.log(chalk.blue(`๐ฆ Loading development plugins...`));
|
|
105
|
+
plugins = [...plugins, ...config.devPlugins];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 1. Auto-register ObjectQL Plugin if objects define but plugins missing
|
|
109
|
+
const hasObjectQL = plugins.some((p: any) => p.name?.includes('objectql') || p.constructor?.name?.includes('ObjectQL'));
|
|
110
|
+
if (config.objects && !hasObjectQL) {
|
|
111
|
+
try {
|
|
112
|
+
console.log(chalk.dim(` Auto-injecting ObjectQL Engine...`));
|
|
113
|
+
const { ObjectQLPlugin } = await import('@objectstack/objectql');
|
|
114
|
+
kernel.use(new ObjectQLPlugin());
|
|
115
|
+
console.log(chalk.green(` โ Registered ObjectQL Plugin (auto-detected)`));
|
|
116
|
+
} catch (e: any) {
|
|
117
|
+
console.warn(chalk.yellow(` โ Could not auto-load ObjectQL: ${e.message}`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 2. Auto-register Memory Driver if in Dev and no driver configured
|
|
122
|
+
const hasDriver = plugins.some((p: any) => p.name?.includes('driver') || p.constructor?.name?.includes('Driver'));
|
|
123
|
+
if (isDev && !hasDriver && config.objects) {
|
|
124
|
+
try {
|
|
125
|
+
console.log(chalk.dim(` Auto-injecting Memory Driver (Dev Mode)...`));
|
|
126
|
+
const { DriverPlugin } = await import('@objectstack/runtime');
|
|
127
|
+
const { InMemoryDriver } = await import('@objectstack/driver-memory');
|
|
128
|
+
kernel.use(new DriverPlugin(new InMemoryDriver()));
|
|
129
|
+
console.log(chalk.green(` โ Registered Memory Driver (auto-detected)`));
|
|
130
|
+
} catch (e: any) {
|
|
131
|
+
// Silent fail - maybe they don't want a driver or don't have the package
|
|
132
|
+
console.log(chalk.dim(` โน No default driver loaded: ${e.message}`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 3. Auto-register AppPlugin if config contains app definitions
|
|
137
|
+
if (config.objects || config.manifest || config.apps) {
|
|
138
|
+
try {
|
|
139
|
+
const { AppPlugin } = await import('@objectstack/runtime');
|
|
140
|
+
kernel.use(new AppPlugin(config));
|
|
141
|
+
console.log(chalk.green(` โ Registered App Plugin (auto-detected)`));
|
|
142
|
+
} catch (e: any) {
|
|
143
|
+
console.warn(chalk.yellow(` โ Could not auto-load AppPlugin: ${e.message}`));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
94
147
|
|
|
95
148
|
if (plugins.length > 0) {
|
|
96
149
|
console.log(chalk.yellow(`๐ฆ Loading ${plugins.length} plugin(s)...`));
|
|
97
150
|
|
|
98
151
|
for (const plugin of plugins) {
|
|
99
152
|
try {
|
|
100
|
-
|
|
153
|
+
let pluginToLoad = plugin;
|
|
154
|
+
|
|
155
|
+
// Resolve string references (package names)
|
|
156
|
+
if (typeof plugin === 'string') {
|
|
157
|
+
console.log(chalk.dim(` Trying to resolve plugin: ${plugin}`));
|
|
158
|
+
try {
|
|
159
|
+
// Try dynamic import for packages
|
|
160
|
+
const imported = await import(plugin);
|
|
161
|
+
pluginToLoad = imported.default || imported;
|
|
162
|
+
} catch (importError: any) {
|
|
163
|
+
// Fallback: try bundleRequire for local paths if needed, otherwise throw
|
|
164
|
+
throw new Error(`Failed to import plugin '${plugin}': ${importError.message}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
kernel.use(pluginToLoad);
|
|
101
169
|
const pluginName = plugin.name || plugin.constructor?.name || 'unnamed';
|
|
102
170
|
console.log(chalk.green(` โ Registered plugin: ${pluginName}`));
|
|
103
171
|
} catch (e: any) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { compileCommand } from '../src/commands/compile';
|
|
3
|
+
import { serveCommand } from '../src/commands/serve';
|
|
4
|
+
import { devCommand } from '../src/commands/dev';
|
|
5
|
+
import { doctorCommand } from '../src/commands/doctor';
|
|
6
|
+
import { createCommand } from '../src/commands/create';
|
|
7
|
+
import { testCommand } from '../src/commands/test';
|
|
8
|
+
|
|
9
|
+
describe('CLI Commands', () => {
|
|
10
|
+
it('should have compile command', () => {
|
|
11
|
+
expect(compileCommand.name()).toBe('compile');
|
|
12
|
+
expect(compileCommand.description()).toContain('Compile');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should have serve command', () => {
|
|
16
|
+
expect(serveCommand.name()).toBe('serve');
|
|
17
|
+
expect(serveCommand.description()).toContain('server');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should have dev command', () => {
|
|
21
|
+
expect(devCommand.name()).toBe('dev');
|
|
22
|
+
expect(devCommand.description()).toContain('development mode');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should have doctor command', () => {
|
|
26
|
+
expect(doctorCommand.name()).toBe('doctor');
|
|
27
|
+
expect(doctorCommand.description()).toContain('health');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should have create command', () => {
|
|
31
|
+
expect(createCommand.name()).toBe('create');
|
|
32
|
+
expect(createCommand.description()).toContain('Create');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should have test command', () => {
|
|
36
|
+
expect(testCommand.name()).toBe('test:run');
|
|
37
|
+
expect(testCommand.description()).toContain('Quality Protocol');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
});
|