@defai.digital/cli 13.4.4 → 13.4.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/bundled/templates/monorepo/contract-index.ts.hbs +7 -0
- package/bundled/templates/monorepo/contract-test.ts.hbs +130 -0
- package/bundled/templates/monorepo/contracts-package.json.hbs +29 -0
- package/bundled/templates/monorepo/domain-index.ts.hbs +115 -0
- package/bundled/templates/monorepo/domain-package.json.hbs +27 -0
- package/bundled/templates/monorepo/gitignore.hbs +32 -0
- package/bundled/templates/monorepo/invariants.md.hbs +43 -0
- package/bundled/templates/monorepo/package.json.hbs +28 -0
- package/bundled/templates/monorepo/pnpm-workspace.yaml.hbs +5 -0
- package/bundled/templates/monorepo/schema.ts.hbs +82 -0
- package/bundled/templates/monorepo/template.json +106 -0
- package/bundled/templates/monorepo/tsconfig.json.hbs +22 -0
- package/bundled/templates/standalone/contract-index.ts.hbs +5 -0
- package/bundled/templates/standalone/contract-test.ts.hbs +95 -0
- package/bundled/templates/standalone/contracts-root-index.ts.hbs +7 -0
- package/bundled/templates/standalone/domain-index.ts.hbs +6 -0
- package/bundled/templates/standalone/domain-repository.ts.hbs +44 -0
- package/bundled/templates/standalone/domain-service.ts.hbs +102 -0
- package/bundled/templates/standalone/gitignore.hbs +27 -0
- package/bundled/templates/standalone/invariants.md.hbs +35 -0
- package/bundled/templates/standalone/package.json.hbs +41 -0
- package/bundled/templates/standalone/schema.ts.hbs +61 -0
- package/bundled/templates/standalone/src-index.ts.hbs +11 -0
- package/bundled/templates/standalone/template.json +91 -0
- package/bundled/templates/standalone/tsconfig.json.hbs +20 -0
- package/bundled/templates/standalone/vitest.config.ts.hbs +8 -0
- package/bundled/workflows/adversarial-debate.yaml +222 -0
- package/bundled/workflows/analyst.yaml +115 -0
- package/bundled/workflows/assistant.yaml +74 -0
- package/bundled/workflows/code-review-discussion.yaml +166 -0
- package/bundled/workflows/code-reviewer.yaml +94 -0
- package/bundled/workflows/contract-first-project.yaml +356 -0
- package/bundled/workflows/debugger.yaml +107 -0
- package/bundled/workflows/designer.yaml +113 -0
- package/bundled/workflows/developer.yaml +105 -0
- package/bundled/workflows/discuss-step-examples.yaml +153 -0
- package/bundled/workflows/infrastructure-automation.yaml +283 -0
- package/bundled/workflows/ml-ab-testing.yaml +311 -0
- package/bundled/workflows/ml-experiment-tracker.yaml +150 -0
- package/bundled/workflows/ml-feature-engineering.yaml +242 -0
- package/bundled/workflows/ml-model-evaluation.yaml +234 -0
- package/bundled/workflows/ml-model-monitoring.yaml +227 -0
- package/bundled/workflows/ml-model-registry.yaml +232 -0
- package/bundled/workflows/mlops-deployment.yaml +267 -0
- package/bundled/workflows/mobile-development.yaml +312 -0
- package/bundled/workflows/multi-model-discussion.yaml +243 -0
- package/bundled/workflows/product-discovery.yaml +295 -0
- package/bundled/workflows/qa-specialist.yaml +116 -0
- package/bundled/workflows/refactoring.yaml +105 -0
- package/bundled/workflows/security-audit.yaml +135 -0
- package/bundled/workflows/std/analysis.yaml +190 -0
- package/bundled/workflows/std/code-review.yaml +117 -0
- package/bundled/workflows/std/debugging.yaml +155 -0
- package/bundled/workflows/std/documentation.yaml +180 -0
- package/bundled/workflows/std/implementation.yaml +197 -0
- package/bundled/workflows/std/refactoring.yaml +180 -0
- package/bundled/workflows/std/testing.yaml +200 -0
- package/bundled/workflows/strategic-planning.yaml +235 -0
- package/bundled/workflows/technology-research.yaml +239 -0
- package/dist/commands/scaffold.d.ts.map +1 -1
- package/dist/commands/scaffold.js +6 -3
- package/dist/commands/scaffold.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +13 -6
- package/dist/web/api.js.map +1 -1
- package/package.json +21 -21
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{pascalCase domainName}} Contract Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests schema validation and invariant enforcement.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import {
|
|
9
|
+
{{pascalCase domainName}}Schema,
|
|
10
|
+
{{pascalCase domainName}}IdSchema,
|
|
11
|
+
{{pascalCase domainName}}StatusSchema,
|
|
12
|
+
{{pascalCase domainName}}EventSchema,
|
|
13
|
+
validate{{pascalCase domainName}},
|
|
14
|
+
} from '{{scope}}/contracts/{{domainName}}';
|
|
15
|
+
|
|
16
|
+
describe('{{pascalCase domainName}} Contracts', () => {
|
|
17
|
+
// ==========================================================================
|
|
18
|
+
// INV-{{upperCase (substring domainName 0 3)}}-001: Valid ID Format
|
|
19
|
+
// ==========================================================================
|
|
20
|
+
describe('INV-{{upperCase (substring domainName 0 3)}}-001: Valid ID Format', () => {
|
|
21
|
+
it('accepts valid UUID v4', () => {
|
|
22
|
+
const validId = '550e8400-e29b-41d4-a716-446655440000';
|
|
23
|
+
expect({{pascalCase domainName}}IdSchema.safeParse(validId).success).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('rejects invalid UUID', () => {
|
|
27
|
+
const invalidIds = ['', 'not-a-uuid', '12345', null, undefined];
|
|
28
|
+
for (const id of invalidIds) {
|
|
29
|
+
expect({{pascalCase domainName}}IdSchema.safeParse(id).success).toBe(false);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ==========================================================================
|
|
35
|
+
// INV-{{upperCase (substring domainName 0 3)}}-002: Valid Status
|
|
36
|
+
// ==========================================================================
|
|
37
|
+
describe('INV-{{upperCase (substring domainName 0 3)}}-002: Valid Status', () => {
|
|
38
|
+
it('accepts valid status values', () => {
|
|
39
|
+
const validStatuses = ['draft', 'active', 'completed', 'cancelled'];
|
|
40
|
+
for (const status of validStatuses) {
|
|
41
|
+
expect({{pascalCase domainName}}StatusSchema.safeParse(status).success).toBe(true);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('rejects invalid status', () => {
|
|
46
|
+
const invalidStatuses = ['', 'pending', 'done', 'ACTIVE', null];
|
|
47
|
+
for (const status of invalidStatuses) {
|
|
48
|
+
expect({{pascalCase domainName}}StatusSchema.safeParse(status).success).toBe(false);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ==========================================================================
|
|
54
|
+
// Schema Validation
|
|
55
|
+
// ==========================================================================
|
|
56
|
+
describe('{{pascalCase domainName}}Schema', () => {
|
|
57
|
+
const validEntity = {
|
|
58
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
59
|
+
status: 'draft',
|
|
60
|
+
createdAt: '2024-01-01T00:00:00.000Z',
|
|
61
|
+
updatedAt: '2024-01-01T00:00:00.000Z',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
it('accepts valid entity', () => {
|
|
65
|
+
expect({{pascalCase domainName}}Schema.safeParse(validEntity).success).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('rejects entity with missing required fields', () => {
|
|
69
|
+
const { id, ...withoutId } = validEntity;
|
|
70
|
+
expect({{pascalCase domainName}}Schema.safeParse(withoutId).success).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('rejects entity with invalid datetime', () => {
|
|
74
|
+
const invalidEntity = { ...validEntity, createdAt: 'not-a-date' };
|
|
75
|
+
expect({{pascalCase domainName}}Schema.safeParse(invalidEntity).success).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ==========================================================================
|
|
80
|
+
// Event Schema Validation
|
|
81
|
+
// ==========================================================================
|
|
82
|
+
describe('{{pascalCase domainName}}EventSchema', () => {
|
|
83
|
+
it('accepts created event', () => {
|
|
84
|
+
const event = {
|
|
85
|
+
type: '{{domainName}}.created',
|
|
86
|
+
{{domainName}}Id: '550e8400-e29b-41d4-a716-446655440000',
|
|
87
|
+
occurredAt: '2024-01-01T00:00:00.000Z',
|
|
88
|
+
};
|
|
89
|
+
expect({{pascalCase domainName}}EventSchema.safeParse(event).success).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('accepts updated event', () => {
|
|
93
|
+
const event = {
|
|
94
|
+
type: '{{domainName}}.updated',
|
|
95
|
+
{{domainName}}Id: '550e8400-e29b-41d4-a716-446655440000',
|
|
96
|
+
occurredAt: '2024-01-01T00:00:00.000Z',
|
|
97
|
+
};
|
|
98
|
+
expect({{pascalCase domainName}}EventSchema.safeParse(event).success).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('rejects unknown event type', () => {
|
|
102
|
+
const event = {
|
|
103
|
+
type: '{{domainName}}.deleted',
|
|
104
|
+
{{domainName}}Id: '550e8400-e29b-41d4-a716-446655440000',
|
|
105
|
+
occurredAt: '2024-01-01T00:00:00.000Z',
|
|
106
|
+
};
|
|
107
|
+
expect({{pascalCase domainName}}EventSchema.safeParse(event).success).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ==========================================================================
|
|
112
|
+
// Validate Function
|
|
113
|
+
// ==========================================================================
|
|
114
|
+
describe('validate{{pascalCase domainName}}', () => {
|
|
115
|
+
it('returns parsed entity for valid data', () => {
|
|
116
|
+
const data = {
|
|
117
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
118
|
+
status: 'active',
|
|
119
|
+
createdAt: '2024-01-01T00:00:00.000Z',
|
|
120
|
+
updatedAt: '2024-01-02T00:00:00.000Z',
|
|
121
|
+
};
|
|
122
|
+
const result = validate{{pascalCase domainName}}(data);
|
|
123
|
+
expect(result).toEqual(data);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('throws ZodError for invalid data', () => {
|
|
127
|
+
expect(() => validate{{pascalCase domainName}}({})).toThrow();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{scope}}/contracts",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Contract schemas for {{projectName}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./{{domainName}}": {
|
|
14
|
+
"import": "./dist/{{domainName}}/v1/index.js",
|
|
15
|
+
"types": "./dist/{{domainName}}/v1/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc --build",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"zod": "^3.22.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.3.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{pascalCase domainName}} Domain Implementation
|
|
3
|
+
*
|
|
4
|
+
* @module {{scope}}/{{domainName}}-domain
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type {{pascalCase domainName}},
|
|
9
|
+
type {{pascalCase domainName}}Id,
|
|
10
|
+
type {{pascalCase domainName}}Status,
|
|
11
|
+
{{pascalCase domainName}}Schema,
|
|
12
|
+
{{pascalCase domainName}}ErrorCode,
|
|
13
|
+
} from '{{scope}}/contracts/{{domainName}}';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Repository Interface
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
export interface {{pascalCase domainName}}Repository {
|
|
20
|
+
findById(id: {{pascalCase domainName}}Id): Promise<{{pascalCase domainName}} | null>;
|
|
21
|
+
save(entity: {{pascalCase domainName}}): Promise<void>;
|
|
22
|
+
delete(id: {{pascalCase domainName}}Id): Promise<boolean>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Service Implementation
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
export class {{pascalCase domainName}}Service {
|
|
30
|
+
constructor(private readonly repository: {{pascalCase domainName}}Repository) {}
|
|
31
|
+
|
|
32
|
+
async getById(id: {{pascalCase domainName}}Id): Promise<{{pascalCase domainName}}> {
|
|
33
|
+
const entity = await this.repository.findById(id);
|
|
34
|
+
if (!entity) {
|
|
35
|
+
throw new {{pascalCase domainName}}NotFoundError(id);
|
|
36
|
+
}
|
|
37
|
+
return entity;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async create(data: Omit<{{pascalCase domainName}}, 'id' | 'createdAt' | 'updatedAt'>): Promise<{{pascalCase domainName}}> {
|
|
41
|
+
const now = new Date().toISOString();
|
|
42
|
+
const entity: {{pascalCase domainName}} = {
|
|
43
|
+
...data,
|
|
44
|
+
id: crypto.randomUUID(),
|
|
45
|
+
createdAt: now,
|
|
46
|
+
updatedAt: now,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Validate against schema
|
|
50
|
+
{{pascalCase domainName}}Schema.parse(entity);
|
|
51
|
+
|
|
52
|
+
await this.repository.save(entity);
|
|
53
|
+
return entity;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async updateStatus(id: {{pascalCase domainName}}Id, newStatus: {{pascalCase domainName}}Status): Promise<{{pascalCase domainName}}> {
|
|
57
|
+
const entity = await this.getById(id);
|
|
58
|
+
|
|
59
|
+
// INV-{{upperCase (substring domainName 0 3)}}-101: Validate status transition
|
|
60
|
+
this.validateStatusTransition(entity.status, newStatus);
|
|
61
|
+
|
|
62
|
+
const updated: {{pascalCase domainName}} = {
|
|
63
|
+
...entity,
|
|
64
|
+
status: newStatus,
|
|
65
|
+
updatedAt: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
await this.repository.save(updated);
|
|
69
|
+
return updated;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private validateStatusTransition(
|
|
73
|
+
currentStatus: {{pascalCase domainName}}Status,
|
|
74
|
+
newStatus: {{pascalCase domainName}}Status
|
|
75
|
+
): void {
|
|
76
|
+
const validTransitions: Record<{{pascalCase domainName}}Status, {{pascalCase domainName}}Status[]> = {
|
|
77
|
+
draft: ['active', 'cancelled'],
|
|
78
|
+
active: ['completed', 'cancelled'],
|
|
79
|
+
completed: [],
|
|
80
|
+
cancelled: [],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (!validTransitions[currentStatus].includes(newStatus)) {
|
|
84
|
+
throw new {{pascalCase domainName}}InvalidStatusError(currentStatus, newStatus);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Domain Errors
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
export class {{pascalCase domainName}}NotFoundError extends Error {
|
|
94
|
+
readonly code = {{pascalCase domainName}}ErrorCode.NOT_FOUND;
|
|
95
|
+
|
|
96
|
+
constructor(id: {{pascalCase domainName}}Id) {
|
|
97
|
+
super(`{{pascalCase domainName}} not found: ${id}`);
|
|
98
|
+
this.name = '{{pascalCase domainName}}NotFoundError';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class {{pascalCase domainName}}InvalidStatusError extends Error {
|
|
103
|
+
readonly code = {{pascalCase domainName}}ErrorCode.INVALID_STATUS;
|
|
104
|
+
|
|
105
|
+
constructor(currentStatus: {{pascalCase domainName}}Status, newStatus: {{pascalCase domainName}}Status) {
|
|
106
|
+
super(`Invalid status transition: ${currentStatus} → ${newStatus}`);
|
|
107
|
+
this.name = '{{pascalCase domainName}}InvalidStatusError';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// Re-exports
|
|
113
|
+
// ============================================================================
|
|
114
|
+
|
|
115
|
+
export type { {{pascalCase domainName}}, {{pascalCase domainName}}Id, {{pascalCase domainName}}Status };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{scope}}/{{domainName}}-domain",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{pascalCase domainName}} domain implementation for {{projectName}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc --build",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"test": "vitest run"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"{{scope}}/contracts": "workspace:*"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.3.0",
|
|
25
|
+
"vitest": "^1.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Build output
|
|
5
|
+
dist/
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
|
|
8
|
+
# IDE
|
|
9
|
+
.idea/
|
|
10
|
+
.vscode/
|
|
11
|
+
*.swp
|
|
12
|
+
*.swo
|
|
13
|
+
|
|
14
|
+
# OS
|
|
15
|
+
.DS_Store
|
|
16
|
+
Thumbs.db
|
|
17
|
+
|
|
18
|
+
# Logs
|
|
19
|
+
*.log
|
|
20
|
+
npm-debug.log*
|
|
21
|
+
pnpm-debug.log*
|
|
22
|
+
|
|
23
|
+
# Test coverage
|
|
24
|
+
coverage/
|
|
25
|
+
|
|
26
|
+
# Environment
|
|
27
|
+
.env
|
|
28
|
+
.env.local
|
|
29
|
+
.env.*.local
|
|
30
|
+
|
|
31
|
+
# AutomatosX data
|
|
32
|
+
.automatosx/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# {{pascalCase domainName}} Domain Invariants
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document defines the behavioral invariants for the {{pascalCase domainName}} domain.
|
|
6
|
+
|
|
7
|
+
## Schema Invariants
|
|
8
|
+
|
|
9
|
+
### INV-{{upperCase (substring domainName 0 3)}}-001: Valid ID Format
|
|
10
|
+
{{pascalCase domainName}} ID MUST be a valid UUID v4.
|
|
11
|
+
- **Enforcement**: schema
|
|
12
|
+
- **Test**: `z.string().uuid()` rejects invalid UUIDs
|
|
13
|
+
|
|
14
|
+
### INV-{{upperCase (substring domainName 0 3)}}-002: Valid Status
|
|
15
|
+
Status MUST be one of the defined enum values.
|
|
16
|
+
- **Enforcement**: schema
|
|
17
|
+
- **Test**: `z.enum([...])` rejects invalid values
|
|
18
|
+
|
|
19
|
+
## Runtime Invariants
|
|
20
|
+
|
|
21
|
+
### INV-{{upperCase (substring domainName 0 3)}}-101: Status Transitions
|
|
22
|
+
Status transitions MUST follow the defined state machine.
|
|
23
|
+
- **Enforcement**: runtime
|
|
24
|
+
- **Valid Transitions**:
|
|
25
|
+
```
|
|
26
|
+
draft → active, cancelled
|
|
27
|
+
active → completed, cancelled
|
|
28
|
+
completed → (terminal)
|
|
29
|
+
cancelled → (terminal)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### INV-{{upperCase (substring domainName 0 3)}}-102: Timestamp Consistency
|
|
33
|
+
UpdatedAt MUST be >= CreatedAt.
|
|
34
|
+
- **Enforcement**: runtime
|
|
35
|
+
- **Test**: Update with earlier timestamp → error
|
|
36
|
+
|
|
37
|
+
## Business Invariants
|
|
38
|
+
|
|
39
|
+
### INV-{{upperCase (substring domainName 0 3)}}-201: [Business Rule]
|
|
40
|
+
[Description]
|
|
41
|
+
- **Enforcement**: [schema|runtime|test]
|
|
42
|
+
- **Test**: [Verification method]
|
|
43
|
+
- **Owner**: [Team]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Contract-first monorepo for {{domainName}} domain",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"packageManager": "pnpm@8.15.0",
|
|
8
|
+
{{#if author}}
|
|
9
|
+
"author": "{{author}}",
|
|
10
|
+
{{/if}}
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "pnpm -r build",
|
|
13
|
+
"clean": "pnpm -r clean",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"typecheck": "pnpm -r typecheck",
|
|
17
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
18
|
+
"lint:fix": "eslint . --ext .ts,.tsx --fix"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.10.0",
|
|
22
|
+
"typescript": "^5.3.0",
|
|
23
|
+
"vitest": "^1.0.0",
|
|
24
|
+
"eslint": "^8.55.0",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
|
26
|
+
"@typescript-eslint/parser": "^6.13.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{pascalCase domainName}} Domain Contracts v1
|
|
3
|
+
*
|
|
4
|
+
* @module {{scope}}/contracts/{{domainName}}/v1
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Value Objects
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export const {{pascalCase domainName}}IdSchema = z.string().uuid();
|
|
14
|
+
export type {{pascalCase domainName}}Id = z.infer<typeof {{pascalCase domainName}}IdSchema>;
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Enums
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export const {{pascalCase domainName}}StatusSchema = z.enum([
|
|
21
|
+
'draft',
|
|
22
|
+
'active',
|
|
23
|
+
'completed',
|
|
24
|
+
'cancelled',
|
|
25
|
+
]);
|
|
26
|
+
export type {{pascalCase domainName}}Status = z.infer<typeof {{pascalCase domainName}}StatusSchema>;
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Entities
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* {{pascalCase domainName}} entity - Aggregate Root
|
|
34
|
+
*
|
|
35
|
+
* Invariants:
|
|
36
|
+
* - INV-{{upperCase (substring domainName 0 3)}}-001: ID must be valid UUID
|
|
37
|
+
* - INV-{{upperCase (substring domainName 0 3)}}-002: Status must be valid
|
|
38
|
+
*/
|
|
39
|
+
export const {{pascalCase domainName}}Schema = z.object({
|
|
40
|
+
id: {{pascalCase domainName}}IdSchema,
|
|
41
|
+
status: {{pascalCase domainName}}StatusSchema,
|
|
42
|
+
createdAt: z.string().datetime(),
|
|
43
|
+
updatedAt: z.string().datetime(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type {{pascalCase domainName}} = z.infer<typeof {{pascalCase domainName}}Schema>;
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Domain Events
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
export const {{pascalCase domainName}}EventSchema = z.discriminatedUnion('type', [
|
|
53
|
+
z.object({
|
|
54
|
+
type: z.literal('{{domainName}}.created'),
|
|
55
|
+
{{domainName}}Id: {{pascalCase domainName}}IdSchema,
|
|
56
|
+
occurredAt: z.string().datetime(),
|
|
57
|
+
}),
|
|
58
|
+
z.object({
|
|
59
|
+
type: z.literal('{{domainName}}.updated'),
|
|
60
|
+
{{domainName}}Id: {{pascalCase domainName}}IdSchema,
|
|
61
|
+
occurredAt: z.string().datetime(),
|
|
62
|
+
}),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
export type {{pascalCase domainName}}Event = z.infer<typeof {{pascalCase domainName}}EventSchema>;
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Validation
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
export function validate{{pascalCase domainName}}(data: unknown): {{pascalCase domainName}} {
|
|
72
|
+
return {{pascalCase domainName}}Schema.parse(data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Error Codes
|
|
77
|
+
// ============================================================================
|
|
78
|
+
|
|
79
|
+
export const {{pascalCase domainName}}ErrorCode = {
|
|
80
|
+
NOT_FOUND: '{{upperCase (substring domainName 0 3)}}_NOT_FOUND',
|
|
81
|
+
INVALID_STATUS: '{{upperCase (substring domainName 0 3)}}_INVALID_STATUS',
|
|
82
|
+
} as const;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "monorepo",
|
|
3
|
+
"displayName": "Contract-First Monorepo",
|
|
4
|
+
"description": "A monorepo structure with contracts, core domains, and adapters",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"category": "project",
|
|
7
|
+
"variables": {
|
|
8
|
+
"projectName": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Project name (kebab-case)",
|
|
11
|
+
"required": true,
|
|
12
|
+
"pattern": "^[a-z][a-z0-9-]*$"
|
|
13
|
+
},
|
|
14
|
+
"scope": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "NPM package scope",
|
|
17
|
+
"default": "@myorg"
|
|
18
|
+
},
|
|
19
|
+
"domainName": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Initial domain name",
|
|
22
|
+
"required": true
|
|
23
|
+
},
|
|
24
|
+
"author": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Author name",
|
|
27
|
+
"default": ""
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"structure": [
|
|
31
|
+
{
|
|
32
|
+
"type": "directory",
|
|
33
|
+
"path": "packages/contracts/src/{{domainName}}/v1"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "directory",
|
|
37
|
+
"path": "packages/core/{{domainName}}-domain/src"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "directory",
|
|
41
|
+
"path": "packages/adapters"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "directory",
|
|
45
|
+
"path": "tests/contract"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"type": "directory",
|
|
49
|
+
"path": "tests/core"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"type": "file",
|
|
53
|
+
"path": "package.json",
|
|
54
|
+
"template": "package.json.hbs"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "file",
|
|
58
|
+
"path": "pnpm-workspace.yaml",
|
|
59
|
+
"template": "pnpm-workspace.yaml.hbs"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "file",
|
|
63
|
+
"path": "tsconfig.json",
|
|
64
|
+
"template": "tsconfig.json.hbs"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "file",
|
|
68
|
+
"path": "packages/contracts/package.json",
|
|
69
|
+
"template": "contracts-package.json.hbs"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"type": "file",
|
|
73
|
+
"path": "packages/contracts/src/{{domainName}}/v1/schema.ts",
|
|
74
|
+
"template": "schema.ts.hbs"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "file",
|
|
78
|
+
"path": "packages/contracts/src/{{domainName}}/v1/invariants.md",
|
|
79
|
+
"template": "invariants.md.hbs"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"type": "file",
|
|
83
|
+
"path": "packages/contracts/src/{{domainName}}/v1/index.ts",
|
|
84
|
+
"template": "contract-index.ts.hbs"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "file",
|
|
88
|
+
"path": "packages/core/{{domainName}}-domain/package.json",
|
|
89
|
+
"template": "domain-package.json.hbs"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"type": "file",
|
|
93
|
+
"path": "packages/core/{{domainName}}-domain/src/index.ts",
|
|
94
|
+
"template": "domain-index.ts.hbs"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"type": "file",
|
|
98
|
+
"path": "tests/contract/{{domainName}}.test.ts",
|
|
99
|
+
"template": "contract-test.ts.hbs"
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"postCreate": [
|
|
103
|
+
"pnpm install",
|
|
104
|
+
"pnpm build"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"composite": true,
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
},
|
|
18
|
+
"references": [
|
|
19
|
+
{ "path": "./packages/contracts" },
|
|
20
|
+
{ "path": "./packages/core/{{domainName}}-domain" }
|
|
21
|
+
]
|
|
22
|
+
}
|