create-objectstack 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.
package/dist/index.js CHANGED
@@ -4,6 +4,10 @@ import chalk from "chalk";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import { execSync } from "child_process";
7
+ import { fileURLToPath } from "url";
8
+ var __filename = fileURLToPath(import.meta.url);
9
+ var __dirname = path.dirname(__filename);
10
+ var TEMPLATES_DIR = path.resolve(__dirname, "templates");
7
11
  var TEMPLATES = {
8
12
  "minimal-api": {
9
13
  description: "Server + memory driver + 1 object + REST API",
@@ -66,7 +70,7 @@ export default defineStack({
66
70
  include: ["*.ts", "src/**/*"],
67
71
  exclude: ["dist", "node_modules"]
68
72
  }, null, 2) + "\n",
69
- "src/objects/task.ts": () => `import { Data } from '@objectstack/spec';
73
+ "src/objects/task.ts": () => `import * as Data from '@objectstack/spec/data';
70
74
 
71
75
  const task: Data.Object = {
72
76
  name: 'task',
@@ -200,7 +204,7 @@ export default defineStack({
200
204
  include: ["*.ts", "src/**/*"],
201
205
  exclude: ["dist", "node_modules"]
202
206
  }, null, 2) + "\n",
203
- "src/objects/contact.ts": () => `import { Data } from '@objectstack/spec';
207
+ "src/objects/contact.ts": () => `import * as Data from '@objectstack/spec/data';
204
208
 
205
209
  const contact: Data.Object = {
206
210
  name: 'contact',
@@ -235,7 +239,7 @@ const contact: Data.Object = {
235
239
 
236
240
  export default contact;
237
241
  `,
238
- "src/objects/company.ts": () => `import { Data } from '@objectstack/spec';
242
+ "src/objects/company.ts": () => `import * as Data from '@objectstack/spec/data';
239
243
 
240
244
  const company: Data.Object = {
241
245
  name: 'company',
@@ -266,7 +270,7 @@ const company: Data.Object = {
266
270
 
267
271
  export default company;
268
272
  `,
269
- "src/objects/deal.ts": () => `import { Data } from '@objectstack/spec';
273
+ "src/objects/deal.ts": () => `import * as Data from '@objectstack/spec/data';
270
274
 
271
275
  const deal: Data.Object = {
272
276
  name: 'deal',
@@ -317,7 +321,7 @@ export default deal;
317
321
  export { default as company } from './company';
318
322
  export { default as deal } from './deal';
319
323
  `,
320
- "src/views/contact_list.ts": () => `import { UI } from '@objectstack/spec';
324
+ "src/views/contact_list.ts": () => `import * as UI from '@objectstack/spec/ui';
321
325
 
322
326
  const contactList: UI.View = {
323
327
  name: 'contact_list',
@@ -329,7 +333,7 @@ const contactList: UI.View = {
329
333
 
330
334
  export default contactList;
331
335
  `,
332
- "src/views/company_list.ts": () => `import { UI } from '@objectstack/spec';
336
+ "src/views/company_list.ts": () => `import * as UI from '@objectstack/spec/ui';
333
337
 
334
338
  const companyList: UI.View = {
335
339
  name: 'company_list',
@@ -341,7 +345,7 @@ const companyList: UI.View = {
341
345
 
342
346
  export default companyList;
343
347
  `,
344
- "src/views/deal_list.ts": () => `import { UI } from '@objectstack/spec';
348
+ "src/views/deal_list.ts": () => `import * as UI from '@objectstack/spec/ui';
345
349
 
346
350
  const dealList: UI.View = {
347
351
  name: 'deal_list',
@@ -353,7 +357,7 @@ const dealList: UI.View = {
353
357
 
354
358
  export default dealList;
355
359
  `,
356
- "src/apps/crm.ts": () => `import { UI } from '@objectstack/spec';
360
+ "src/apps/crm.ts": () => `import * as UI from '@objectstack/spec/ui';
357
361
 
358
362
  const crm: UI.App = {
359
363
  name: 'crm',
@@ -465,7 +469,7 @@ export default defineStack({
465
469
  */
466
470
  export * as objects from './objects';
467
471
  `,
468
- "src/objects/sample.ts": (name) => `import { Data } from '@objectstack/spec';
472
+ "src/objects/sample.ts": (name) => `import * as Data from '@objectstack/spec/data';
469
473
 
470
474
  const sample: Data.Object = {
471
475
  name: '${name}_sample',
@@ -541,6 +545,12 @@ MIT
541
545
  }
542
546
  }
543
547
  };
548
+ function readTemplate(filename) {
549
+ return fs.readFileSync(path.join(TEMPLATES_DIR, filename), "utf-8");
550
+ }
551
+ var AI_CONFIG_FILES = {
552
+ ".github/copilot-instructions.md": (name) => readTemplate("copilot-instructions.md").replaceAll("{{PROJECT_NAME}}", name).replaceAll("{{PROJECT_TITLE}}", toTitleCase(name))
553
+ };
544
554
  function toTitleCase(str) {
545
555
  return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
546
556
  }
@@ -600,7 +610,8 @@ var program = new Command().name("create-objectstack").description("Create a new
600
610
  if (!fs.existsSync(targetDir)) {
601
611
  fs.mkdirSync(targetDir, { recursive: true });
602
612
  }
603
- for (const [filePath, contentFn] of Object.entries(template.files)) {
613
+ const allFiles = { ...template.files, ...AI_CONFIG_FILES };
614
+ for (const [filePath, contentFn] of Object.entries(allFiles)) {
604
615
  const fullPath = path.join(targetDir, filePath);
605
616
  const dir = path.dirname(fullPath);
606
617
  if (!fs.existsSync(dir)) {
@@ -625,6 +636,21 @@ var program = new Command().name("create-objectstack").description("Create a new
625
636
  console.log("");
626
637
  }
627
638
  }
639
+ if (!options.skipInstall) {
640
+ printStep("Installing AI skills for your coding agent...");
641
+ try {
642
+ execSync("npx -y skills add objectstack-ai/framework --all", {
643
+ stdio: "inherit",
644
+ cwd: targetDir
645
+ });
646
+ console.log("");
647
+ } catch {
648
+ printWarning(
649
+ "Skills installation skipped. Run manually:\n npx skills add objectstack-ai/framework"
650
+ );
651
+ console.log("");
652
+ }
653
+ }
628
654
  printSuccess("Project created!");
629
655
  console.log("");
630
656
  console.log(chalk.bold(" Next steps:"));
@@ -636,6 +662,11 @@ var program = new Command().name("create-objectstack").description("Create a new
636
662
  }
637
663
  console.log(chalk.dim(" npm run dev # Start development server"));
638
664
  console.log(chalk.dim(" npm run validate # Check configuration"));
665
+ if (options.skipInstall) {
666
+ console.log("");
667
+ console.log(chalk.bold(" AI Skills (recommended):"));
668
+ console.log(chalk.dim(" npx skills add objectstack-ai/framework"));
669
+ }
639
670
  console.log("");
640
671
  } catch (error) {
641
672
  printError(error.message || String(error));
@@ -0,0 +1,74 @@
1
+ # {{PROJECT_TITLE}} — Copilot Instructions
2
+
3
+ > Auto-generated by `create-objectstack`. Customise freely.
4
+
5
+ ## Project Context
6
+
7
+ This is an **ObjectStack** application — a metadata-driven low-code project
8
+ that defines business objects, views, automations, and AI agents in TypeScript.
9
+
10
+ - **Entry point:** `objectstack.config.ts` (uses `defineStack()`)
11
+ - **Spec package:** `@objectstack/spec` (Zod-first schemas and types)
12
+
13
+ ## Naming Conventions
14
+
15
+ | Context | Convention | Example |
16
+ |:--------|:-----------|:--------|
17
+ | Config keys (TS props) | `camelCase` | `maxLength`, `defaultValue` |
18
+ | Machine names (data values) | `snake_case` | `project_task`, `first_name` |
19
+ | Metadata type names | singular | `'agent'`, `'view'`, `'flow'` |
20
+ | File names | `{name}.{type}.ts` | `task.object.ts`, `main.app.ts` |
21
+
22
+ ## Key Rules
23
+
24
+ 1. **Zod First** — All schema definitions start with Zod. Types are derived via `z.infer<>`.
25
+ 2. `defineStack()` is the single configuration entry point in `objectstack.config.ts`.
26
+ 3. Use `Object.values()` barrel pattern for metadata arrays.
27
+ 4. Import from `@objectstack/spec` — never use relative paths into the spec package.
28
+
29
+ ## Project Structure
30
+
31
+ ```
32
+ {{PROJECT_NAME}}/
33
+ ├── objectstack.config.ts # defineStack() — the single entry point
34
+ ├── src/
35
+ │ ├── objects/ # Business object definitions (snake_case names)
36
+ │ ├── views/ # UI view definitions (list, form, kanban, calendar)
37
+ │ ├── apps/ # App navigation & page structure
38
+ │ ├── flows/ # Automation flows & workflows
39
+ │ ├── actions/ # Custom actions (buttons, bulk ops)
40
+ │ ├── dashboards/ # BI dashboards
41
+ │ ├── reports/ # Analytics reports
42
+ │ ├── agents/ # AI agent definitions
43
+ │ ├── i18n/ # Translation bundles
44
+ │ └── handlers/ # Runtime hook handlers
45
+ ```
46
+
47
+ ## AI Skills
48
+
49
+ This project uses ObjectStack skills from `objectstack-ai/framework`.
50
+ Install or update skills with the standard [skills CLI](https://skills.sh/):
51
+
52
+ ```bash
53
+ npx skills add objectstack-ai/framework
54
+ ```
55
+
56
+ Skills are triggered automatically based on task context:
57
+
58
+ | Skill | Trigger Context |
59
+ |:------|:----------------|
60
+ | **objectstack-schema** | Define objects, fields, relationships, validations, indexes |
61
+ | **objectstack-query** | Filters, sorting, pagination, aggregation, ObjectQL |
62
+ | **objectstack-ui** | Views, dashboards, apps, reports, actions, navigation |
63
+ | **objectstack-api** | REST endpoints, authentication, service contracts |
64
+ | **objectstack-plugin** | Plugin lifecycle, DI, services, hooks, events |
65
+ | **objectstack-automation** | Flows, workflows, triggers, approvals, state machines |
66
+ | **objectstack-ai** | Agents, tools, skills, RAG pipelines, LLM config |
67
+ | **objectstack-quickstart** | Project setup, defineStack(), driver selection |
68
+ | **objectstack-i18n** | Translation bundles, locale config, coverage detection |
69
+
70
+ ## Learn More
71
+
72
+ - [ObjectStack Documentation](https://objectstack.com/docs)
73
+ - [GitHub: objectstack-ai/framework](https://github.com/objectstack-ai/framework)
74
+ - [Skills CLI](https://skills.sh/) — Manage AI skills across agents
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-objectstack",
3
- "version": "4.0.3",
3
+ "version": "4.0.5",
4
4
  "description": "Create a new ObjectStack project — npx create-objectstack",
5
5
  "bin": {
6
6
  "create-objectstack": "./bin/create-objectstack.js"
@@ -19,9 +19,26 @@
19
19
  "commander": "^14.0.3"
20
20
  },
21
21
  "devDependencies": {
22
- "@types/node": "^25.6.0",
22
+ "@types/node": "^25.6.2",
23
23
  "tsup": "^8.5.1",
24
- "typescript": "^6.0.2"
24
+ "typescript": "^6.0.3"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/objectstack-ai/framework.git",
29
+ "directory": "packages/create-objectstack"
30
+ },
31
+ "homepage": "https://objectstack.ai/docs",
32
+ "bugs": "https://github.com/objectstack-ai/framework/issues",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18.0.0"
25
42
  },
26
43
  "scripts": {
27
44
  "build": "tsup",
@@ -1,13 +0,0 @@
1
-
2
- > create-objectstack@4.0.3 build /home/runner/work/framework/framework/packages/create-objectstack
3
- > tsup
4
-
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Using tsup config: /home/runner/work/framework/framework/packages/create-objectstack/tsup.config.ts
9
- CLI Target: es2022
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/index.js 16.79 KB
13
- ESM ⚡️ Build success in 30ms
package/CHANGELOG.md DELETED
@@ -1,43 +0,0 @@
1
- # create-objectstack
2
-
3
- ## 4.0.3
4
-
5
- ## 4.0.2
6
-
7
- ## 4.0.0
8
-
9
- ## 3.3.1
10
-
11
- ## 3.3.0
12
-
13
- ## 3.2.9
14
-
15
- ## 3.2.8
16
-
17
- ## 3.2.7
18
-
19
- ## 3.2.6
20
-
21
- ## 3.2.5
22
-
23
- ## 3.2.4
24
-
25
- ## 3.2.3
26
-
27
- ## 3.2.2
28
-
29
- ## 3.2.1
30
-
31
- ## 3.2.0
32
-
33
- ## 3.1.1
34
-
35
- ## 3.1.0
36
-
37
- ## 3.0.11
38
-
39
- ## 3.0.10
40
-
41
- ## 3.0.9
42
-
43
- ## 3.0.8
package/src/index.ts DELETED
@@ -1,720 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { Command } from 'commander';
4
- import chalk from 'chalk';
5
- import fs from 'fs';
6
- import path from 'path';
7
- import { execSync } from 'child_process';
8
-
9
- // ─── Template Registry ──────────────────────────────────────────────
10
-
11
- type TemplateFiles = Record<string, (name: string) => string>;
12
-
13
- interface Template {
14
- description: string;
15
- files: TemplateFiles;
16
- }
17
-
18
- const TEMPLATES: Record<string, Template> = {
19
- 'minimal-api': {
20
- description: 'Server + memory driver + 1 object + REST API',
21
- files: {
22
- 'objectstack.config.ts': (name) => `import { defineStack } from '@objectstack/spec';
23
- import * as objects from './src/objects';
24
-
25
- export default defineStack({
26
- manifest: {
27
- id: 'com.example.${name}',
28
- namespace: '${name}',
29
- version: '0.1.0',
30
- type: 'app',
31
- name: '${toTitleCase(name)}',
32
- description: '${toTitleCase(name)} — built with ObjectStack',
33
- },
34
-
35
- objects: Object.values(objects),
36
-
37
- api: {
38
- rest: { enabled: true, basePath: '/api' },
39
- },
40
- });
41
- `,
42
- 'package.json': (name) => JSON.stringify({
43
- name,
44
- version: '0.1.0',
45
- private: true,
46
- type: 'module',
47
- scripts: {
48
- dev: 'objectstack dev',
49
- start: 'objectstack serve',
50
- build: 'objectstack compile',
51
- validate: 'objectstack validate',
52
- typecheck: 'tsc --noEmit',
53
- },
54
- dependencies: {
55
- '@objectstack/spec': '^3.0.0',
56
- '@objectstack/runtime': '^3.0.0',
57
- '@objectstack/driver-memory': '^3.0.0',
58
- '@objectstack/plugin-hono-server': '^3.0.0',
59
- },
60
- devDependencies: {
61
- '@objectstack/cli': '^3.0.0',
62
- 'typescript': '^5.3.0',
63
- },
64
- }, null, 2) + '\n',
65
- 'tsconfig.json': () => JSON.stringify({
66
- compilerOptions: {
67
- target: 'ES2022',
68
- module: 'ESNext',
69
- moduleResolution: 'bundler',
70
- strict: true,
71
- esModuleInterop: true,
72
- skipLibCheck: true,
73
- outDir: 'dist',
74
- rootDir: '.',
75
- declaration: true,
76
- },
77
- include: ['*.ts', 'src/**/*'],
78
- exclude: ['dist', 'node_modules'],
79
- }, null, 2) + '\n',
80
- 'src/objects/task.ts': () => `import { Data } from '@objectstack/spec';
81
-
82
- const task: Data.Object = {
83
- name: 'task',
84
- label: 'Task',
85
- ownership: 'own',
86
- fields: {
87
- title: {
88
- type: 'text',
89
- label: 'Title',
90
- required: true,
91
- },
92
- description: {
93
- type: 'textarea',
94
- label: 'Description',
95
- },
96
- status: {
97
- type: 'select',
98
- label: 'Status',
99
- options: [
100
- { label: 'Open', value: 'open' },
101
- { label: 'In Progress', value: 'in_progress' },
102
- { label: 'Done', value: 'done' },
103
- ],
104
- defaultValue: 'open',
105
- },
106
- due_date: {
107
- type: 'date',
108
- label: 'Due Date',
109
- },
110
- },
111
- };
112
-
113
- export default task;
114
- `,
115
- 'src/objects/index.ts': () => `export { default as task } from './task';
116
- `,
117
- '.gitignore': () => `node_modules/
118
- dist/
119
- *.tsbuildinfo
120
- `,
121
- 'README.md': (name) => `# ${toTitleCase(name)}
122
-
123
- Built with [ObjectStack](https://objectstack.com).
124
-
125
- ## Quick Start
126
-
127
- \`\`\`bash
128
- # Install dependencies
129
- npm install
130
-
131
- # Start development server
132
- npm run dev
133
-
134
- # Validate configuration
135
- npm run validate
136
- \`\`\`
137
-
138
- ## Project Structure
139
-
140
- - \`objectstack.config.ts\` — Stack definition (objects, API, settings)
141
- - \`src/objects/\` — Object definitions
142
- - \`dist/\` — Compiled output
143
-
144
- ## Learn More
145
-
146
- - [ObjectStack Documentation](https://objectstack.com/docs)
147
- `,
148
- },
149
- },
150
-
151
- 'full-stack': {
152
- description: 'Server + UI + auth + 3 CRM objects',
153
- files: {
154
- 'objectstack.config.ts': (name) => `import { defineStack } from '@objectstack/spec';
155
- import * as objects from './src/objects';
156
- import * as apps from './src/apps';
157
-
158
- export default defineStack({
159
- manifest: {
160
- id: 'com.example.${name}',
161
- namespace: '${name}',
162
- version: '0.1.0',
163
- type: 'app',
164
- name: '${toTitleCase(name)}',
165
- description: '${toTitleCase(name)} CRM — built with ObjectStack',
166
- },
167
-
168
- objects: Object.values(objects),
169
- apps: Object.values(apps),
170
-
171
- api: {
172
- rest: { enabled: true, basePath: '/api' },
173
- },
174
- });
175
- `,
176
- 'package.json': (name) => JSON.stringify({
177
- name,
178
- version: '0.1.0',
179
- private: true,
180
- type: 'module',
181
- scripts: {
182
- dev: 'objectstack dev',
183
- start: 'objectstack serve',
184
- build: 'objectstack compile',
185
- validate: 'objectstack validate',
186
- typecheck: 'tsc --noEmit',
187
- },
188
- dependencies: {
189
- '@objectstack/spec': '^3.0.0',
190
- '@objectstack/runtime': '^3.0.0',
191
- '@objectstack/driver-memory': '^3.0.0',
192
- '@objectstack/plugin-hono-server': '^3.0.0',
193
- '@objectstack/plugin-auth': '^3.0.0',
194
- },
195
- devDependencies: {
196
- '@objectstack/cli': '^3.0.0',
197
- 'typescript': '^5.3.0',
198
- },
199
- }, null, 2) + '\n',
200
- 'tsconfig.json': () => JSON.stringify({
201
- compilerOptions: {
202
- target: 'ES2022',
203
- module: 'ESNext',
204
- moduleResolution: 'bundler',
205
- strict: true,
206
- esModuleInterop: true,
207
- skipLibCheck: true,
208
- outDir: 'dist',
209
- rootDir: '.',
210
- declaration: true,
211
- },
212
- include: ['*.ts', 'src/**/*'],
213
- exclude: ['dist', 'node_modules'],
214
- }, null, 2) + '\n',
215
- 'src/objects/contact.ts': () => `import { Data } from '@objectstack/spec';
216
-
217
- const contact: Data.Object = {
218
- name: 'contact',
219
- label: 'Contact',
220
- ownership: 'own',
221
- fields: {
222
- first_name: {
223
- type: 'text',
224
- label: 'First Name',
225
- required: true,
226
- },
227
- last_name: {
228
- type: 'text',
229
- label: 'Last Name',
230
- required: true,
231
- },
232
- email: {
233
- type: 'text',
234
- label: 'Email',
235
- },
236
- phone: {
237
- type: 'text',
238
- label: 'Phone',
239
- },
240
- company: {
241
- type: 'lookup',
242
- label: 'Company',
243
- reference: 'company',
244
- },
245
- },
246
- };
247
-
248
- export default contact;
249
- `,
250
- 'src/objects/company.ts': () => `import { Data } from '@objectstack/spec';
251
-
252
- const company: Data.Object = {
253
- name: 'company',
254
- label: 'Company',
255
- ownership: 'own',
256
- fields: {
257
- name: {
258
- type: 'text',
259
- label: 'Company Name',
260
- required: true,
261
- },
262
- website: {
263
- type: 'text',
264
- label: 'Website',
265
- },
266
- industry: {
267
- type: 'select',
268
- label: 'Industry',
269
- options: [
270
- { label: 'Technology', value: 'technology' },
271
- { label: 'Finance', value: 'finance' },
272
- { label: 'Healthcare', value: 'healthcare' },
273
- { label: 'Other', value: 'other' },
274
- ],
275
- },
276
- },
277
- };
278
-
279
- export default company;
280
- `,
281
- 'src/objects/deal.ts': () => `import { Data } from '@objectstack/spec';
282
-
283
- const deal: Data.Object = {
284
- name: 'deal',
285
- label: 'Deal',
286
- ownership: 'own',
287
- fields: {
288
- name: {
289
- type: 'text',
290
- label: 'Deal Name',
291
- required: true,
292
- },
293
- amount: {
294
- type: 'number',
295
- label: 'Amount',
296
- },
297
- stage: {
298
- type: 'select',
299
- label: 'Stage',
300
- options: [
301
- { label: 'Prospecting', value: 'prospecting' },
302
- { label: 'Qualification', value: 'qualification' },
303
- { label: 'Proposal', value: 'proposal' },
304
- { label: 'Closed Won', value: 'closed_won' },
305
- { label: 'Closed Lost', value: 'closed_lost' },
306
- ],
307
- defaultValue: 'prospecting',
308
- },
309
- contact: {
310
- type: 'lookup',
311
- label: 'Contact',
312
- reference: 'contact',
313
- },
314
- company: {
315
- type: 'lookup',
316
- label: 'Company',
317
- reference: 'company',
318
- },
319
- close_date: {
320
- type: 'date',
321
- label: 'Close Date',
322
- },
323
- },
324
- };
325
-
326
- export default deal;
327
- `,
328
- 'src/objects/index.ts': () => `export { default as contact } from './contact';
329
- export { default as company } from './company';
330
- export { default as deal } from './deal';
331
- `,
332
- 'src/views/contact_list.ts': () => `import { UI } from '@objectstack/spec';
333
-
334
- const contactList: UI.View = {
335
- name: 'contact_list',
336
- label: 'All Contacts',
337
- object: 'contact',
338
- type: 'list',
339
- columns: ['first_name', 'last_name', 'email', 'phone', 'company'],
340
- };
341
-
342
- export default contactList;
343
- `,
344
- 'src/views/company_list.ts': () => `import { UI } from '@objectstack/spec';
345
-
346
- const companyList: UI.View = {
347
- name: 'company_list',
348
- label: 'All Companies',
349
- object: 'company',
350
- type: 'list',
351
- columns: ['name', 'website', 'industry'],
352
- };
353
-
354
- export default companyList;
355
- `,
356
- 'src/views/deal_list.ts': () => `import { UI } from '@objectstack/spec';
357
-
358
- const dealList: UI.View = {
359
- name: 'deal_list',
360
- label: 'All Deals',
361
- object: 'deal',
362
- type: 'list',
363
- columns: ['name', 'amount', 'stage', 'contact', 'close_date'],
364
- };
365
-
366
- export default dealList;
367
- `,
368
- 'src/apps/crm.ts': () => `import { UI } from '@objectstack/spec';
369
-
370
- const crm: UI.App = {
371
- name: 'crm',
372
- label: 'CRM',
373
- description: 'Customer Relationship Management',
374
- navigation: [
375
- { type: 'object', object: 'contact', label: 'Contacts' },
376
- { type: 'object', object: 'company', label: 'Companies' },
377
- { type: 'object', object: 'deal', label: 'Deals' },
378
- ],
379
- };
380
-
381
- export default crm;
382
- `,
383
- 'src/apps/index.ts': () => `export { default as crm } from './crm';
384
- `,
385
- '.gitignore': () => `node_modules/
386
- dist/
387
- *.tsbuildinfo
388
- `,
389
- 'README.md': (name) => `# ${toTitleCase(name)}
390
-
391
- A full-stack CRM application built with [ObjectStack](https://objectstack.com).
392
-
393
- ## Quick Start
394
-
395
- \`\`\`bash
396
- npm install
397
- npm run dev
398
- \`\`\`
399
-
400
- ## Project Structure
401
-
402
- - \`objectstack.config.ts\` — Stack definition
403
- - \`src/objects/\` — Data objects (Contact, Company, Deal)
404
- - \`src/views/\` — List views
405
- - \`src/apps/crm.ts\` — CRM app with navigation
406
-
407
- ## Learn More
408
-
409
- - [ObjectStack Documentation](https://objectstack.com/docs)
410
- `,
411
- },
412
- },
413
-
414
- plugin: {
415
- description: 'Plugin skeleton with test setup',
416
- files: {
417
- 'objectstack.config.ts': (name) => `import { defineStack } from '@objectstack/spec';
418
- import * as objects from './src/objects';
419
-
420
- export default defineStack({
421
- manifest: {
422
- id: 'com.objectstack.plugin-${name}',
423
- namespace: 'plugin_${name}',
424
- version: '0.1.0',
425
- type: 'plugin',
426
- name: '${toTitleCase(name)} Plugin',
427
- description: 'ObjectStack Plugin: ${toTitleCase(name)}',
428
- },
429
-
430
- objects: Object.values(objects),
431
- });
432
- `,
433
- 'package.json': (name) => JSON.stringify({
434
- name: `@objectstack/plugin-${name}`,
435
- version: '0.1.0',
436
- description: `ObjectStack Plugin: ${toTitleCase(name)}`,
437
- main: 'dist/index.js',
438
- types: 'dist/index.d.ts',
439
- type: 'module',
440
- scripts: {
441
- build: 'tsc',
442
- dev: 'tsc --watch',
443
- test: 'vitest run',
444
- validate: 'objectstack validate',
445
- typecheck: 'tsc --noEmit',
446
- },
447
- keywords: ['objectstack', 'plugin', name],
448
- author: '',
449
- license: 'MIT',
450
- dependencies: {
451
- '@objectstack/spec': '^3.0.0',
452
- },
453
- devDependencies: {
454
- '@types/node': '^22.0.0',
455
- 'typescript': '^5.3.0',
456
- 'vitest': '^4.0.0',
457
- },
458
- }, null, 2) + '\n',
459
- 'tsconfig.json': () => JSON.stringify({
460
- compilerOptions: {
461
- target: 'ES2022',
462
- module: 'ESNext',
463
- moduleResolution: 'bundler',
464
- strict: true,
465
- esModuleInterop: true,
466
- skipLibCheck: true,
467
- outDir: 'dist',
468
- rootDir: '.',
469
- declaration: true,
470
- },
471
- include: ['*.ts', 'src/**/*'],
472
- exclude: ['dist', 'node_modules'],
473
- }, null, 2) + '\n',
474
- 'src/index.ts': (name) => `/**
475
- * ${toTitleCase(name)} Plugin for ObjectStack
476
- *
477
- * Entry point — re-exports all plugin metadata.
478
- */
479
- export * as objects from './objects';
480
- `,
481
- 'src/objects/sample.ts': (name) => `import { Data } from '@objectstack/spec';
482
-
483
- const sample: Data.Object = {
484
- name: '${name}_sample',
485
- label: '${toTitleCase(name)} Sample',
486
- ownership: 'own',
487
- fields: {
488
- name: {
489
- type: 'text',
490
- label: 'Name',
491
- required: true,
492
- },
493
- },
494
- };
495
-
496
- export default sample;
497
- `,
498
- 'src/objects/index.ts': () => `export { default as sample } from './sample';
499
- `,
500
- 'test/sample.test.ts': (name) => `import { describe, it, expect } from 'vitest';
501
- import sample from '../src/objects/sample';
502
-
503
- describe('${name} plugin', () => {
504
- it('should export a valid sample object', () => {
505
- expect(sample).toBeDefined();
506
- expect(sample.name).toBe('${name}_sample');
507
- expect(sample.fields).toHaveProperty('name');
508
- });
509
- });
510
- `,
511
- '.gitignore': () => `node_modules/
512
- dist/
513
- *.tsbuildinfo
514
- `,
515
- 'README.md': (name) => `# @objectstack/plugin-${name}
516
-
517
- ObjectStack Plugin: ${toTitleCase(name)}
518
-
519
- ## Installation
520
-
521
- \`\`\`bash
522
- npm install @objectstack/plugin-${name}
523
- \`\`\`
524
-
525
- ## Usage
526
-
527
- \`\`\`typescript
528
- import { defineStack } from '@objectstack/spec';
529
-
530
- export default defineStack({
531
- plugins: [
532
- '@objectstack/plugin-${name}',
533
- ],
534
- });
535
- \`\`\`
536
-
537
- ## Development
538
-
539
- \`\`\`bash
540
- # Run tests
541
- npm test
542
-
543
- # Build
544
- npm run build
545
-
546
- # Validate metadata
547
- npm run validate
548
- \`\`\`
549
-
550
- ## License
551
-
552
- MIT
553
- `,
554
- },
555
- },
556
- };
557
-
558
- // ─── Helpers ────────────────────────────────────────────────────────
559
-
560
- function toCamelCase(str: string): string {
561
- return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
562
- }
563
-
564
- function toTitleCase(str: string): string {
565
- return str.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
566
- }
567
-
568
- // ─── Formatting (matches @objectstack/cli style) ────────────────────
569
-
570
- function printHeader(title: string) {
571
- console.log(chalk.bold(`\n◆ ${title}`));
572
- console.log(chalk.dim('─'.repeat(40)));
573
- }
574
-
575
- function printKV(key: string, value: string) {
576
- console.log(` ${chalk.dim(key + ':')} ${chalk.white(value)}`);
577
- }
578
-
579
- function printSuccess(msg: string) {
580
- console.log(chalk.green(` ✓ ${msg}`));
581
- }
582
-
583
- function printError(msg: string) {
584
- console.log(chalk.red(` ✗ ${msg}`));
585
- }
586
-
587
- function printStep(msg: string) {
588
- console.log(chalk.yellow(` → ${msg}`));
589
- }
590
-
591
- function printWarning(msg: string) {
592
- console.log(chalk.yellow(` ⚠ ${msg}`));
593
- }
594
-
595
- // ─── CLI Program ────────────────────────────────────────────────────
596
-
597
- const program = new Command()
598
- .name('create-objectstack')
599
- .description('Create a new ObjectStack project')
600
- .version('3.0.0')
601
- .argument('[name]', 'Project name (defaults to current directory name)')
602
- .option(
603
- '-t, --template <template>',
604
- 'Project template: minimal-api, full-stack, plugin',
605
- 'minimal-api',
606
- )
607
- .option('--skip-install', 'Skip dependency installation')
608
- .action(async (name: string | undefined, options: { template: string; skipInstall?: boolean }) => {
609
- // Banner
610
- console.log('');
611
- console.log(chalk.bold.cyan(' ╔═══════════════════════════════════╗'));
612
- console.log(chalk.bold.cyan(' ║') + chalk.bold(' ◆ Create ObjectStack ') + chalk.dim('v3.0') + chalk.bold.cyan(' ║'));
613
- console.log(chalk.bold.cyan(' ╚═══════════════════════════════════╝'));
614
-
615
- printHeader('New Project');
616
-
617
- // Resolve template
618
- const template = TEMPLATES[options.template];
619
- if (!template) {
620
- printError(`Unknown template: ${options.template}`);
621
- console.log(chalk.dim(` Available: ${Object.keys(TEMPLATES).join(', ')}`));
622
- process.exit(1);
623
- }
624
-
625
- // Resolve project name and directory
626
- const cwd = process.cwd();
627
- const projectName = name || path.basename(cwd);
628
- const targetDir = name ? path.resolve(cwd, name) : cwd;
629
- const isCurrentDir = targetDir === cwd;
630
-
631
- printKV('Project', projectName);
632
- printKV('Template', `${options.template} — ${template.description}`);
633
- printKV('Directory', targetDir);
634
- console.log('');
635
-
636
- // Guard: if creating in a sub-directory, check it doesn't already exist
637
- if (!isCurrentDir && fs.existsSync(targetDir)) {
638
- const existing = fs.readdirSync(targetDir);
639
- if (existing.length > 0) {
640
- printError(`Directory already exists and is not empty: ${targetDir}`);
641
- process.exit(1);
642
- }
643
- }
644
-
645
- const createdFiles: string[] = [];
646
-
647
- try {
648
- // Ensure target directory exists
649
- if (!fs.existsSync(targetDir)) {
650
- fs.mkdirSync(targetDir, { recursive: true });
651
- }
652
-
653
- // Write every file defined by the template
654
- for (const [filePath, contentFn] of Object.entries(template.files)) {
655
- const fullPath = path.join(targetDir, filePath);
656
- const dir = path.dirname(fullPath);
657
-
658
- if (!fs.existsSync(dir)) {
659
- fs.mkdirSync(dir, { recursive: true });
660
- }
661
-
662
- fs.writeFileSync(fullPath, contentFn(projectName));
663
- createdFiles.push(filePath);
664
- }
665
-
666
- // Summary
667
- console.log(chalk.bold(' Created files:'));
668
- for (const f of createdFiles) {
669
- console.log(chalk.green(` + ${f}`));
670
- }
671
- console.log('');
672
-
673
- // Install dependencies
674
- if (!options.skipInstall) {
675
- printStep('Installing dependencies...');
676
- try {
677
- // Detect package manager — prefer pnpm, fall back to npm
678
- const pm = detectPackageManager();
679
- execSync(`${pm} install`, { stdio: 'inherit', cwd: targetDir });
680
- console.log('');
681
- } catch {
682
- printWarning('Dependency installation failed. Run `npm install` manually.');
683
- console.log('');
684
- }
685
- }
686
-
687
- printSuccess('Project created!');
688
- console.log('');
689
-
690
- // Next steps
691
- console.log(chalk.bold(' Next steps:'));
692
- if (!isCurrentDir) {
693
- console.log(chalk.dim(` cd ${name}`));
694
- }
695
- if (options.skipInstall) {
696
- console.log(chalk.dim(' npm install'));
697
- }
698
- console.log(chalk.dim(' npm run dev # Start development server'));
699
- console.log(chalk.dim(' npm run validate # Check configuration'));
700
- console.log('');
701
-
702
- } catch (error: any) {
703
- printError(error.message || String(error));
704
- process.exit(1);
705
- }
706
- });
707
-
708
- /**
709
- * Detect available package manager (pnpm > npm).
710
- */
711
- function detectPackageManager(): string {
712
- try {
713
- execSync('pnpm --version', { stdio: 'ignore' });
714
- return 'pnpm';
715
- } catch {
716
- return 'npm';
717
- }
718
- }
719
-
720
- program.parse();
package/tsconfig.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "lib": [
7
- "ES2022"
8
- ],
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "outDir": "dist",
14
- "rootDir": "src",
15
- "types": [
16
- "node"
17
- ],
18
- "ignoreDeprecations": "6.0"
19
- },
20
- "include": [
21
- "src"
22
- ]
23
- }
package/tsup.config.ts DELETED
@@ -1,10 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { defineConfig } from 'tsup';
4
-
5
- export default defineConfig({
6
- entry: ['src/index.ts'],
7
- format: ['esm'],
8
- clean: true,
9
- shims: true,
10
- });