@voltom/contracts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +486 -0
- package/dist/index.d.mts +464 -0
- package/dist/index.d.ts +464 -0
- package/dist/index.js +246 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +206 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# @voltom/contracts
|
|
2
|
+
|
|
3
|
+
Universal contract language for voltom products — primitive types, response shapes, error codes, and the `defineContract()` engine.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- **Node.js** 20+ ([download](https://nodejs.org))
|
|
10
|
+
- **npm** 10+ (included with Node.js)
|
|
11
|
+
- **Git** (for cloning and versioning)
|
|
12
|
+
|
|
13
|
+
### Installation & Setup
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 1. Navigate to the project
|
|
17
|
+
cd /Users/jzegarram/product-factory/@voltom-contracts
|
|
18
|
+
|
|
19
|
+
# 2. Install dependencies
|
|
20
|
+
npm install
|
|
21
|
+
|
|
22
|
+
# 3. Verify everything works
|
|
23
|
+
npm run test
|
|
24
|
+
|
|
25
|
+
# 4. Start developing
|
|
26
|
+
npm run dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Development Workflow
|
|
30
|
+
|
|
31
|
+
#### Watch Mode (Recommended for development)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm run dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This runs TypeScript compiler in watch mode. Any changes to `src/` files trigger a rebuild automatically.
|
|
38
|
+
|
|
39
|
+
#### Run Tests
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Run tests once
|
|
43
|
+
npm run test
|
|
44
|
+
|
|
45
|
+
# Run tests in watch mode (re-run on file changes)
|
|
46
|
+
npm run test:watch
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Type Checking
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Check TypeScript without emitting
|
|
53
|
+
npm run type-check
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run this before committing to catch type errors early.
|
|
57
|
+
|
|
58
|
+
#### Build for Production
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Create dist/ with CJS, ESM, and .d.ts
|
|
62
|
+
npm run build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Output:
|
|
66
|
+
- `dist/index.js` — CommonJS (Node.js)
|
|
67
|
+
- `dist/index.mjs` — ES Modules (browsers, modern tools)
|
|
68
|
+
- `dist/index.d.ts` — TypeScript declarations
|
|
69
|
+
|
|
70
|
+
### Project Structure
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
src/
|
|
74
|
+
├── primitives.ts ← t.* type builders (add new types here)
|
|
75
|
+
├── responses.ts ← Response envelopes (SingleResponse, etc.)
|
|
76
|
+
├── errors.ts ← ErrorCode + HTTP status map
|
|
77
|
+
├── contract.ts ← defineContract() engine
|
|
78
|
+
├── helpers.ts ← Production patterns (auditFields, etc.)
|
|
79
|
+
├── index.ts ← Public API exports
|
|
80
|
+
└── types/
|
|
81
|
+
├── contract.types.ts
|
|
82
|
+
└── pagination.types.ts
|
|
83
|
+
__tests__/
|
|
84
|
+
└── primitives.test.ts ← Test examples
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Common Development Tasks
|
|
88
|
+
|
|
89
|
+
#### Add a New Primitive Type
|
|
90
|
+
|
|
91
|
+
1. **Define in `src/primitives.ts`**:
|
|
92
|
+
```typescript
|
|
93
|
+
export const t = {
|
|
94
|
+
// ... existing types ...
|
|
95
|
+
myNewType: (opts?: { /* options */ }) => {
|
|
96
|
+
// Use Zod under the hood
|
|
97
|
+
return z.string().custom(/* validation */)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
2. **Add tests to `src/__tests__/primitives.test.ts`**:
|
|
103
|
+
```typescript
|
|
104
|
+
describe('t.myNewType()', () => {
|
|
105
|
+
it('validates correct input', () => {
|
|
106
|
+
const schema = t.myNewType()
|
|
107
|
+
expect(schema.parse('valid-input')).toBe('valid-input')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('rejects invalid input', () => {
|
|
111
|
+
const schema = t.myNewType()
|
|
112
|
+
expect(() => schema.parse('invalid')).toThrow()
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
3. **Verify tests pass**:
|
|
118
|
+
```bash
|
|
119
|
+
npm run test
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
4. **Update `src/index.ts` if needed** (usually auto-exported from primitives)
|
|
123
|
+
|
|
124
|
+
5. **Create a changeset**:
|
|
125
|
+
```bash
|
|
126
|
+
npx changeset
|
|
127
|
+
# Select: patch (bug fix), minor (new type), or major (breaking change)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### Add a New Error Code
|
|
131
|
+
|
|
132
|
+
1. **Update `src/errors.ts`**:
|
|
133
|
+
```typescript
|
|
134
|
+
export type ErrorCode =
|
|
135
|
+
| 'EXISTING_ERROR'
|
|
136
|
+
| 'MY_NEW_ERROR' // ← Add here
|
|
137
|
+
|
|
138
|
+
export const ERROR_STATUS_MAP: Record<ErrorCode, number> = {
|
|
139
|
+
EXISTING_ERROR: 400,
|
|
140
|
+
MY_NEW_ERROR: 409, // ← Add HTTP status
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const ERROR_MESSAGES: Record<ErrorCode, string> = {
|
|
144
|
+
EXISTING_ERROR: 'Error message',
|
|
145
|
+
MY_NEW_ERROR: 'My error message', // ← Add default message
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
2. **Test it**:
|
|
150
|
+
```bash
|
|
151
|
+
npm run type-check
|
|
152
|
+
npm run test
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
3. **Create a changeset** (⚠️ This is a breaking change):
|
|
156
|
+
```bash
|
|
157
|
+
npx changeset
|
|
158
|
+
# Select: major (breaking change — all products must update)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Update Response Shapes
|
|
162
|
+
|
|
163
|
+
**⚠️ WARNING:** Response shapes are locked. Changes require major version bump and coordination with all products.
|
|
164
|
+
|
|
165
|
+
Do NOT change existing fields. If you need a new response type:
|
|
166
|
+
|
|
167
|
+
1. **Create new envelope in `src/responses.ts`**
|
|
168
|
+
2. **Export from `src/index.ts`**
|
|
169
|
+
3. **Document in README**
|
|
170
|
+
4. **Changeset as major version**
|
|
171
|
+
5. **Notify all products**
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Before You Commit
|
|
176
|
+
|
|
177
|
+
Run this checklist:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# 1. Format & lint (if you add linting rules)
|
|
181
|
+
npm run lint
|
|
182
|
+
|
|
183
|
+
# 2. Type check
|
|
184
|
+
npm run type-check
|
|
185
|
+
|
|
186
|
+
# 3. Run all tests
|
|
187
|
+
npm run test
|
|
188
|
+
|
|
189
|
+
# 4. Build
|
|
190
|
+
npm run build
|
|
191
|
+
|
|
192
|
+
# 5. Verify bundle size
|
|
193
|
+
ls -lh dist/
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
If all pass, you're ready to commit.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Publishing to npm
|
|
201
|
+
|
|
202
|
+
### Step-by-Step
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# 1. Make your changes
|
|
206
|
+
# (edit src/primitives.ts, src/errors.ts, etc.)
|
|
207
|
+
|
|
208
|
+
# 2. Write tests for new types
|
|
209
|
+
# (edit src/__tests__/primitives.test.ts)
|
|
210
|
+
|
|
211
|
+
# 3. Verify everything locally
|
|
212
|
+
npm run test
|
|
213
|
+
npm run type-check
|
|
214
|
+
npm run build
|
|
215
|
+
|
|
216
|
+
# 4. Create a changeset
|
|
217
|
+
npx changeset
|
|
218
|
+
# Follow the prompts:
|
|
219
|
+
# - Select type: patch | minor | major
|
|
220
|
+
# - Write summary of changes
|
|
221
|
+
# - Describe why in more detail
|
|
222
|
+
|
|
223
|
+
# 5. Commit your changes
|
|
224
|
+
git add .
|
|
225
|
+
git commit -m "feat: add new type or error code"
|
|
226
|
+
|
|
227
|
+
# 6. Push to GitHub
|
|
228
|
+
git push origin your-branch
|
|
229
|
+
|
|
230
|
+
# 7. Create PR, get reviewed, merge to main
|
|
231
|
+
# (CI automatically publishes on merge)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### CI/CD (Automatic on Merge)
|
|
235
|
+
|
|
236
|
+
When you merge to `main`, GitHub Actions automatically:
|
|
237
|
+
|
|
238
|
+
1. ✅ Runs all tests
|
|
239
|
+
2. ✅ Builds the package
|
|
240
|
+
3. ✅ Bumps version (`package.json`, `CHANGELOG.md`)
|
|
241
|
+
4. ✅ Publishes to GitHub Packages
|
|
242
|
+
|
|
243
|
+
You don't need to do anything else.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Troubleshooting
|
|
248
|
+
|
|
249
|
+
### Tests Failing
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Clear node_modules and reinstall
|
|
253
|
+
rm -rf node_modules package-lock.json
|
|
254
|
+
npm install
|
|
255
|
+
npm run test
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Type Errors
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Run type checker for detailed errors
|
|
262
|
+
npm run type-check
|
|
263
|
+
|
|
264
|
+
# Fix TypeScript errors in the code
|
|
265
|
+
# (usually in src/ files)
|
|
266
|
+
|
|
267
|
+
# Re-run type-check
|
|
268
|
+
npm run type-check
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Build Failing
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Check tsup config
|
|
275
|
+
cat tsup.config.ts
|
|
276
|
+
|
|
277
|
+
# Try rebuilding with clean slate
|
|
278
|
+
npm run build
|
|
279
|
+
|
|
280
|
+
# Check dist/ was created
|
|
281
|
+
ls dist/
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Changeset Issues
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# If you haven't created a changeset
|
|
288
|
+
npx changeset
|
|
289
|
+
|
|
290
|
+
# If changeset is malformed, fix it
|
|
291
|
+
# (changesets are in .changeset/*.md files)
|
|
292
|
+
|
|
293
|
+
# Verify changeset is valid
|
|
294
|
+
cat .changeset/*.md
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## File Reference
|
|
300
|
+
|
|
301
|
+
| File | Purpose | When to edit |
|
|
302
|
+
|------|---------|--------------|
|
|
303
|
+
| `src/primitives.ts` | Type builders | Adding new `t.*` types |
|
|
304
|
+
| `src/responses.ts` | Response envelopes | (Locked — don't edit) |
|
|
305
|
+
| `src/errors.ts` | Error codes | Adding new ErrorCode |
|
|
306
|
+
| `src/helpers.ts` | Production patterns | Adding new helpers |
|
|
307
|
+
| `src/index.ts` | Public API | Exporting new code |
|
|
308
|
+
| `src/__tests__/primitives.test.ts` | Tests | Every new type needs tests |
|
|
309
|
+
| `package.json` | Dependencies, scripts | Adding dev dependencies |
|
|
310
|
+
| `tsconfig.json` | TypeScript config | Changing TS behavior |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Next Steps
|
|
315
|
+
|
|
316
|
+
1. **Read [CLAUDE.md](CLAUDE.md)** for detailed rules and patterns
|
|
317
|
+
2. **Read [PRODUCTION-GUIDE.md](PRODUCTION-GUIDE.md)** for production best practices
|
|
318
|
+
3. **Review [QUICK-REFERENCE.md](QUICK-REFERENCE.md)** for quick lookups
|
|
319
|
+
4. **Start developing** — pick a task and follow the workflow above
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Quick Start
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { defineContract, t } from '@voltom/contracts'
|
|
327
|
+
import type { SingleResponse, PaginatedResponse, ApiError } from '@voltom/contracts'
|
|
328
|
+
|
|
329
|
+
// Define a contract
|
|
330
|
+
export const usersContract = defineContract({
|
|
331
|
+
resource: 'users',
|
|
332
|
+
basePath: '/api/v1/users',
|
|
333
|
+
entity: {
|
|
334
|
+
id: t.uuid(),
|
|
335
|
+
email: t.email(),
|
|
336
|
+
name: t.string({ min: 1, max: 255 }),
|
|
337
|
+
createdAt: t.datetime(),
|
|
338
|
+
},
|
|
339
|
+
endpoints: {
|
|
340
|
+
list: { method: 'GET', path: '/', response: 'paginated' },
|
|
341
|
+
show: { method: 'GET', path: '/:id', response: 'single' },
|
|
342
|
+
create: { method: 'POST', path: '/', body: 'CreateUser' },
|
|
343
|
+
delete: { method: 'DELETE', path: '/:id', response: 'deleted' },
|
|
344
|
+
},
|
|
345
|
+
bodies: {
|
|
346
|
+
CreateUser: {
|
|
347
|
+
email: t.email(),
|
|
348
|
+
name: t.string({ min: 1, max: 255 }),
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
})
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## What's Included
|
|
355
|
+
|
|
356
|
+
### 1. Primitive Type Builders (`t.*`)
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
t.uuid() // UUID string
|
|
360
|
+
t.string({ min, max }) // string with constraints
|
|
361
|
+
t.email() // email format
|
|
362
|
+
t.number() // number
|
|
363
|
+
t.int() // integer
|
|
364
|
+
t.boolean() // boolean
|
|
365
|
+
t.datetime() // ISO 8601 datetime
|
|
366
|
+
t.date() // ISO 8601 date
|
|
367
|
+
t.enum(['a', 'b']) // enum
|
|
368
|
+
t.optional(schema) // optional<T>
|
|
369
|
+
t.nullable(schema) // nullable<T>
|
|
370
|
+
t.array(schema) // T[]
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 2. Response Shapes (Canonical)
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
SingleResponse<T> // { data: T, timestamp: string }
|
|
377
|
+
PaginatedResponse<T> // { data: T[], meta: PaginationMeta, timestamp: string }
|
|
378
|
+
DeletedResponse // { deleted: true, id: string, timestamp: string }
|
|
379
|
+
ApiError // { error: { code: ErrorCode, message: string }, timestamp: string }
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 3. Error Codes
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
type ErrorCode =
|
|
386
|
+
| 'VALIDATION_ERROR' // 400 — invalid input
|
|
387
|
+
| 'UNAUTHORIZED' // 401 — no session
|
|
388
|
+
| 'FORBIDDEN' // 403 — not allowed
|
|
389
|
+
| 'NOT_FOUND' // 404 — missing resource
|
|
390
|
+
| 'CONFLICT' // 409 — duplicate or bad state
|
|
391
|
+
| 'UNPROCESSABLE' // 422 — business logic rejected
|
|
392
|
+
| 'INTERNAL_ERROR' // 500 — server error
|
|
393
|
+
|
|
394
|
+
// Map to HTTP status
|
|
395
|
+
import { ERROR_STATUS_MAP } from '@voltom/contracts'
|
|
396
|
+
const status = ERROR_STATUS_MAP['VALIDATION_ERROR'] // 400
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### 4. `defineContract()` Engine
|
|
400
|
+
|
|
401
|
+
Type-safe contract definition for every resource in your product:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
interface ContractConfig {
|
|
405
|
+
resource: string // e.g., 'users'
|
|
406
|
+
basePath: string // e.g., '/api/v1/users'
|
|
407
|
+
entity: Record<string, z.ZodTypeAny> // the shape
|
|
408
|
+
endpoints?: Record<string, EndpointDef> // GET, POST, etc.
|
|
409
|
+
bodies?: Record<string, Record<string, z.ZodTypeAny>> // request bodies
|
|
410
|
+
filters?: Record<string, z.ZodTypeAny> // query filters
|
|
411
|
+
actions?: Record<string, ActionDef> // custom actions
|
|
412
|
+
permissions?: Record<string, string> // access control hints
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Installation
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
npm install @voltom/contracts zod
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Usage in Different Contexts
|
|
423
|
+
|
|
424
|
+
### In Product Contracts (`{product}-contracts`)
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import { defineContract, t } from '@voltom/contracts'
|
|
428
|
+
|
|
429
|
+
export const teamsContract = defineContract({
|
|
430
|
+
// ... your contract definition
|
|
431
|
+
})
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### In Backend (NestJS)
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import type { SingleResponse, ApiError, ErrorCode } from '@voltom/contracts'
|
|
438
|
+
import { ERROR_STATUS_MAP } from '@voltom/contracts'
|
|
439
|
+
|
|
440
|
+
// Use in controllers, services, interceptors
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### In Frontend (React/Next.js)
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
import type { PaginatedResponse, ApiError } from '@voltom/contracts'
|
|
447
|
+
|
|
448
|
+
// Use in API calls, type inference
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Versioning
|
|
452
|
+
|
|
453
|
+
- **patch**: bug fixes (1.0.x)
|
|
454
|
+
- **minor**: new types, additive changes (1.x.0)
|
|
455
|
+
- **major**: breaking changes — requires coordination (x.0.0)
|
|
456
|
+
|
|
457
|
+
## Development
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
npm install # Install dependencies
|
|
461
|
+
npm run dev # Watch mode
|
|
462
|
+
npm run build # Build (CJS + ESM + .d.ts)
|
|
463
|
+
npm run type-check # TypeScript check
|
|
464
|
+
npm run test # Run tests
|
|
465
|
+
npm run test:watch # Watch mode for tests
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Publishing
|
|
469
|
+
|
|
470
|
+
1. Make changes
|
|
471
|
+
2. `npx changeset` — create changeset
|
|
472
|
+
3. PR → review → merge to main
|
|
473
|
+
4. CI publishes automatically to GitHub Packages
|
|
474
|
+
5. Version bumped, CHANGELOG updated
|
|
475
|
+
|
|
476
|
+
## See Also
|
|
477
|
+
|
|
478
|
+
- [Platform Overview](../docs/00-PLATFORM-OVERVIEW.md)
|
|
479
|
+
- [Project Identity](../docs/PROJECT-IDENTITY.md)
|
|
480
|
+
- [Full Contract Documentation](../docs/01-CONTRACTS-BASE.md)
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
**Organization:** voltom
|
|
485
|
+
**Status:** Active
|
|
486
|
+
**License:** MIT
|