@kalutskii/contract 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 +370 -0
- package/dist/cli.entrypoint.d.ts +1 -0
- package/dist/cli.entrypoint.js +1024 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +0 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# contract
|
|
2
|
+
|
|
3
|
+
A TypeScript CLI tool for building contract packages that define shared types and interfaces for distribution via npm.
|
|
4
|
+
|
|
5
|
+
Instead of syncing contracts over HTTP between services, this library generates publishable npm packages containing bundled TypeScript type definitions. Services consume these packages via standard package managers.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# npm / yarn / pnpm
|
|
13
|
+
npm install -g @kalutskii/contract
|
|
14
|
+
|
|
15
|
+
# bun
|
|
16
|
+
bun add -g @kalutskii/contract
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or run without installing:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bunx @kalutskii/contract <command>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> Requires `typescript >= 5.9` and `jiti >= 2.6` as peer dependencies.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What is a contract?
|
|
30
|
+
|
|
31
|
+
A contract is a versioned set of TypeScript type definitions that one service publishes and other services consume. For example:
|
|
32
|
+
|
|
33
|
+
- **Service A** defines types for "API responses", "request models", etc.
|
|
34
|
+
- **Service A** publishes a contract package `@company-contracts/service-a`
|
|
35
|
+
- **Service B** installs and imports: `import type * as ServiceAContracts from '@company-contracts/service-a/api'`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Initialize
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bunx contract init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This creates:
|
|
48
|
+
|
|
49
|
+
- `contract.config.ts` - Configuration file
|
|
50
|
+
- `contract/manifests/` - Directory for contract definitions
|
|
51
|
+
- `contract/generated/` - Output directory for bundled declarations
|
|
52
|
+
- `contract/package/` - Output directory for publishable package
|
|
53
|
+
|
|
54
|
+
### Define Contracts
|
|
55
|
+
|
|
56
|
+
Edit `contract/manifests/contract.<name>.manifest.ts`:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// contract/manifests/contract.api.manifest.ts
|
|
60
|
+
|
|
61
|
+
export interface UserCreateRequest {
|
|
62
|
+
email: string;
|
|
63
|
+
name: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface UserCreateResponse {
|
|
67
|
+
id: string;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For emitted runtime values, prefer this shape:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
export { PBACPermissionsRecord } from '@/app/domain/permissions/permissions.constants';
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Keep `permissions.constants.ts` as a leaf runtime file with no unrelated runtime imports.
|
|
79
|
+
|
|
80
|
+
### Build Declarations
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bunx contract build
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This bundles each manifest into a standalone `.d.ts` file using `dts-bundle-generator`.
|
|
87
|
+
If a contract name is listed in `emit` inside `contract.config.ts`, the build also emits a runtime `.js` file for that manifest.
|
|
88
|
+
|
|
89
|
+
For `emit` contracts, keep runtime exports narrow:
|
|
90
|
+
|
|
91
|
+
- re-export directly from the concrete file that owns the value
|
|
92
|
+
- avoid barrel files for runtime exports
|
|
93
|
+
- keep that source file free of unrelated runtime imports when possible
|
|
94
|
+
|
|
95
|
+
### Prepare Package
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bunx contract prepare:package
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This creates a publishable package in `contract/package/`:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
contract/package/
|
|
105
|
+
├── package.json # Package metadata
|
|
106
|
+
├── index.d.ts # Exports all contracts
|
|
107
|
+
├── index.js # Runtime entrypoint for emitted contracts
|
|
108
|
+
├── api.d.ts # Contract: api
|
|
109
|
+
├── api.js # Runtime contract module when emitted
|
|
110
|
+
├── types.d.ts # Contract: types
|
|
111
|
+
└── types.js # Stub
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Hash state is stored at `contract/.contract-package-state.json`.
|
|
115
|
+
|
|
116
|
+
**Automatic versioning:**
|
|
117
|
+
|
|
118
|
+
The command automatically bumps the patch version if the generated contract files have changed:
|
|
119
|
+
|
|
120
|
+
- First run: stores a content hash, version unchanged
|
|
121
|
+
- Content unchanged: version stays the same
|
|
122
|
+
- Content changed: patch version bumps (e.g., `1.0.0 → 1.0.1`)
|
|
123
|
+
|
|
124
|
+
**Manual version overrides:**
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
bunx contract prepare:package --bump minor
|
|
128
|
+
bunx contract prepare:package --bump major
|
|
129
|
+
bunx contract prepare:package --no-bump
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The `--no-bump` flag disables automatic version bumping.
|
|
133
|
+
|
|
134
|
+
### Pack Package
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
bunx contract pack:package
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Creates a `.tgz` archive of the prepared package in `contract/package/`.
|
|
141
|
+
|
|
142
|
+
### Publish Package
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
bunx contract publish:package
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Publishes the prepared package to npm. The CLI writes `.npmrc` inside `contract/package` and publishes with public access enabled.
|
|
149
|
+
|
|
150
|
+
If the current version already exists on npm, publishing fails with a clear message and you should run:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
bunx contract prepare:package --bump patch
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Token priority:**
|
|
157
|
+
|
|
158
|
+
1. `config.npm.token`
|
|
159
|
+
2. `NPM_TOKEN`
|
|
160
|
+
3. `NODE_AUTH_TOKEN`
|
|
161
|
+
|
|
162
|
+
The package can also be prepared and published in one step:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
bunx contract publish:package --prepare
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The `--prepare` flag will rebuild the package before publishing.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
`contract.config.ts`:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import type { Config } from 'contract';
|
|
178
|
+
|
|
179
|
+
const contractConfig: Config = {
|
|
180
|
+
app: 'admin-service',
|
|
181
|
+
contracts: ['api', 'types', 'events'],
|
|
182
|
+
emit: ['events'],
|
|
183
|
+
package: {
|
|
184
|
+
name: '@company-contracts/admin-service',
|
|
185
|
+
version: '1.0.0',
|
|
186
|
+
},
|
|
187
|
+
npm: {
|
|
188
|
+
token: process.env.NPM_TOKEN ?? '',
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export default contractConfig;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Fields:**
|
|
196
|
+
|
|
197
|
+
- `app` - Service/app name (used in generated filenames)
|
|
198
|
+
- `contracts` - List of contract names to generate
|
|
199
|
+
- `emit` - Subset of contracts that should also publish runtime JavaScript
|
|
200
|
+
- `package.name` - NPM package name
|
|
201
|
+
- `package.version` - Semantic version
|
|
202
|
+
- `package.exports` - (Optional) Custom export field configuration
|
|
203
|
+
- `npm.token` - (Optional) NPM auth token used for publishing
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Commands
|
|
208
|
+
|
|
209
|
+
| Command | Purpose |
|
|
210
|
+
| ----------------------------- | --------------------------------------------------------- |
|
|
211
|
+
| `contract init` | Initialize contract environment and create default config |
|
|
212
|
+
| `contract update:environment` | Update directories and manifests based on current config |
|
|
213
|
+
| `contract build` | Bundle manifest files into `.d.ts` declarations |
|
|
214
|
+
| `contract prepare:package` | Generate publishable npm package directory |
|
|
215
|
+
| `contract pack:package` | Pack prepared package into a `.tgz` archive |
|
|
216
|
+
| `contract publish:package` | Publish package to npm using config/env token |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Directory Structure
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
contract/
|
|
224
|
+
├── manifests/ # Your contract definitions (source)
|
|
225
|
+
│ ├── contract.api.manifest.ts
|
|
226
|
+
│ └── contract.types.manifest.ts
|
|
227
|
+
├── generated/ # Built .d.ts files (output)
|
|
228
|
+
│ ├── app.contract.api.d.ts
|
|
229
|
+
│ ├── app.contract.events.js
|
|
230
|
+
│ └── app.contract.types.d.ts
|
|
231
|
+
└── package/ # Publishable npm package (output)
|
|
232
|
+
├── package.json
|
|
233
|
+
├── index.d.ts
|
|
234
|
+
├── index.js
|
|
235
|
+
├── api.d.ts
|
|
236
|
+
├── events.js
|
|
237
|
+
└── types.d.ts
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Consumer Usage
|
|
243
|
+
|
|
244
|
+
After publishing your contract package, consumers install and import it:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// Consumer service
|
|
248
|
+
import type { UserCreateRequest } from '@company-contracts/admin-service/api';
|
|
249
|
+
|
|
250
|
+
const user: UserCreateRequest = {
|
|
251
|
+
email: 'user@example.com',
|
|
252
|
+
name: 'John Doe',
|
|
253
|
+
};
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Development Workflow
|
|
259
|
+
|
|
260
|
+
### Producer Service (publishes contracts)
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Define contracts in contract/manifests/
|
|
264
|
+
# Update contract.config.ts
|
|
265
|
+
|
|
266
|
+
bunx contract update:environment # Sync manifest files
|
|
267
|
+
bunx contract build # Generate .d.ts from manifests
|
|
268
|
+
bunx contract prepare:package # Create npm package (auto-versions if content changed)
|
|
269
|
+
bunx contract publish:package # Publish to npm
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Versioning behavior:**
|
|
273
|
+
|
|
274
|
+
- `prepare:package` detects content changes and bumps patch version automatically
|
|
275
|
+
- `publish:package` checks whether target version already exists on npm
|
|
276
|
+
- if version exists, publish fails and asks for manual bump (`--bump patch|minor|major`)
|
|
277
|
+
- Use `--bump major|minor` to manually override during prepare
|
|
278
|
+
- Use `--no-bump` to disable automatic bumping
|
|
279
|
+
|
|
280
|
+
**Requirements:**
|
|
281
|
+
|
|
282
|
+
- provide `npm.token` in `contract.config.ts`, or set `NPM_TOKEN` / `NODE_AUTH_TOKEN`
|
|
283
|
+
|
|
284
|
+
### Consumer Service (uses contracts)
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Install the contract package
|
|
288
|
+
bun add @company-contracts/admin-service
|
|
289
|
+
|
|
290
|
+
# Import types
|
|
291
|
+
import type * as AdminAPI from '@company-contracts/admin-service/api';
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Notes
|
|
297
|
+
|
|
298
|
+
- This library is **local-only** — it does not perform remote synchronization or automatic publishing
|
|
299
|
+
- Publishing uses a temporary `.npmrc` in `contract/package` from `config.npm.token`, `NPM_TOKEN`, or `NODE_AUTH_TOKEN` and removes it after the publish attempt
|
|
300
|
+
- Contract manifests can export runtime values for contracts listed in `emit`
|
|
301
|
+
- For `emit`, import or re-export from direct leaf files instead of barrels or service modules with broader dependency graphs
|
|
302
|
+
- Use `contract update:environment` to regenerate missing files (e.g., after adding new contracts)
|
|
303
|
+
- Versions are automatically managed based on content changes and npm registry state
|
|
304
|
+
- Content hash is stored in `contract/.contract-package-state.json` for change detection
|
|
305
|
+
- If npm version already exists, bump version manually via `contract prepare:package --bump ...`
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Contributing
|
|
310
|
+
|
|
311
|
+
### Prerequisites
|
|
312
|
+
|
|
313
|
+
- [Bun](https://bun.sh) >= 1.0
|
|
314
|
+
- Node.js >= 20 (for tooling compatibility)
|
|
315
|
+
|
|
316
|
+
### Setup
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
git clone https://github.com/kalutskii/contract.git
|
|
320
|
+
cd contract
|
|
321
|
+
bun install
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Scripts
|
|
325
|
+
|
|
326
|
+
| Script | Purpose |
|
|
327
|
+
| ------------------- | ---------------------------------------- |
|
|
328
|
+
| `bun run build` | Compile CLI and library via tsup |
|
|
329
|
+
| `bun run typecheck` | Run TypeScript compiler without emitting |
|
|
330
|
+
| `bun run lint` | Run ESLint across all TypeScript sources |
|
|
331
|
+
|
|
332
|
+
### Project Layout
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
src/
|
|
336
|
+
adapters/ # CLI framework wiring (Clipanion)
|
|
337
|
+
environment/ # Config loading, validation, and env helpers
|
|
338
|
+
modules/
|
|
339
|
+
build/ # contract build command
|
|
340
|
+
init/ # contract init command
|
|
341
|
+
pack/ # contract pack:package command
|
|
342
|
+
prepare/ # contract prepare:package command
|
|
343
|
+
publish/ # contract publish:package command
|
|
344
|
+
versioning/ # Content hashing and semver bump logic
|
|
345
|
+
utilities/ # Shared utility functions
|
|
346
|
+
cli.entrypoint.ts # CLI entry point
|
|
347
|
+
index.ts # Library public API
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Tech Stack
|
|
351
|
+
|
|
352
|
+
- **tsup** — bundle and emit TypeScript declarations
|
|
353
|
+
- **dts-bundle-generator** — bundle manifest files into single `.d.ts` files
|
|
354
|
+
- **Bun** — runtime and package manager
|
|
355
|
+
- **Clipanion** — CLI framework
|
|
356
|
+
- **Zod** — config schema validation
|
|
357
|
+
- **@clack/prompts** — interactive terminal prompts
|
|
358
|
+
|
|
359
|
+
### Making Changes
|
|
360
|
+
|
|
361
|
+
1. Edit source under `src/`
|
|
362
|
+
2. Run `bun run typecheck` and `bun run lint` to validate
|
|
363
|
+
3. Run `bun run build` to compile
|
|
364
|
+
4. Test the CLI locally: `./dist/cli.entrypoint.js <command>`
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## License
|
|
369
|
+
|
|
370
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|