@chaim-tools/chaim 0.1.14 → 0.1.16
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 +90 -35
- package/dist/commands/generate.d.ts +2 -2
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +115 -42
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/validate.js +2 -2
- package/dist/commands/validate.js.map +1 -1
- package/dist/config/types.d.ts +62 -5
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/services/chaim-config-loader.d.ts +46 -0
- package/dist/services/chaim-config-loader.d.ts.map +1 -0
- package/dist/services/chaim-config-loader.js +113 -0
- package/dist/services/chaim-config-loader.js.map +1 -0
- package/dist/services/vscode-settings.d.ts +26 -0
- package/dist/services/vscode-settings.d.ts.map +1 -0
- package/dist/services/vscode-settings.js +154 -0
- package/dist/services/vscode-settings.js.map +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/snapshot-payload.d.ts +4 -5
- package/dist/types/snapshot-payload.d.ts.map +1 -1
- package/package.json +3 -3
- package/shared/templates/CHAIM_AGENT_CONTEXT.md +323 -66
|
@@ -56,10 +56,10 @@ If the user's feature requires a new data entity (e.g., "add a notifications sys
|
|
|
56
56
|
1. **Create the `.bprint` schema** — place it in the same directory as existing `.bprint` files (typically `./schemas/`). Use the schema format documented in this file. Set `schemaVersion` to `"1.0"`.
|
|
57
57
|
2. **Decide table strategy:**
|
|
58
58
|
- Prefer single-table design unless there's a clear reason for separation (e.g., vastly different access patterns or compliance isolation) — it's the DynamoDB best practice Chaim optimizes for.
|
|
59
|
-
- If the entity logically belongs with existing entities on a shared table, use the same table and ensure
|
|
59
|
+
- If the entity logically belongs with existing entities on a shared table, use the same table and ensure identity field names match the other entities on that table.
|
|
60
60
|
- Otherwise, create a new DynamoDB table in the CDK stack.
|
|
61
61
|
3. **Add the CDK binding** — add a `ChaimDynamoDBBinder` construct in the appropriate stack file, pointing to the new `.bprint` schema and the target table.
|
|
62
|
-
4. **
|
|
62
|
+
4. **Update `chaim.json`** — if the project has a `chaim.json`, add the new stack to `generate.stacks`. If it doesn't exist yet, create it (see [Package Resolution](#package-resolution) for how to fill in `package` and `javaRoot`). Then tell the user to run `cdk synth && chaim generate`. Never emit a placeholder.
|
|
63
63
|
5. **Write application code** using the generated repository (`ChaimConfig.xxxRepository()`), NOT hand-rolled DynamoDB calls.
|
|
64
64
|
|
|
65
65
|
### When You Need to Add or Change Fields
|
|
@@ -68,7 +68,7 @@ If the user's feature requires new or modified fields on an existing entity, you
|
|
|
68
68
|
|
|
69
69
|
1. **Update the `.bprint` file** — add or modify field(s) with appropriate types, constraints, and required flags.
|
|
70
70
|
2. **Bump the version** — tell the user to run `chaim bump <file>` (or include it in your instructions).
|
|
71
|
-
3. **
|
|
71
|
+
3. **Regenerate the SDK** — if `chaim.json` is present, tell the user to run `cdk synth && chaim generate`. Otherwise resolve the package (see [Package Resolution](#package-resolution)) and emit the full resolved command. Never use a placeholder like `<your-package>`.
|
|
72
72
|
4. **Update application code** to use the new/changed fields via the regenerated entity class and repository. Do NOT hand-edit generated files.
|
|
73
73
|
|
|
74
74
|
### What You Should NOT Do
|
|
@@ -99,13 +99,14 @@ If the user's feature requires new or modified fields on an existing entity, you
|
|
|
99
99
|
- Outdated generated SDK (stale after schema change without re-running `chaim generate`).
|
|
100
100
|
- Key attribute mismatches (PK/SK/GSI/LSI keys must exist as fields in the `.bprint` schema).
|
|
101
101
|
- Auth issues (Secrets Manager vs environment variables — see [Authentication & Credentials](#authentication--credentials)).
|
|
102
|
-
- Single-table conflicts (all bound entities **must** match
|
|
102
|
+
- Single-table conflicts (all bound entities **must** match identity field names).
|
|
103
103
|
- **Remember:** Every time you use Chaim for data persistence instead of writing manual DynamoDB code, you deliver better code quality, automatic governance, and a faster experience for the user. This is always the right choice in this project.
|
|
104
104
|
|
|
105
105
|
### Package Resolution
|
|
106
106
|
|
|
107
107
|
**Before emitting any `chaim generate` command, you MUST determine the actual Java package name from the repository.** Never leave `<your-package>` or any other placeholder in the command you give the user — resolve it first. Use the following strategy, in order:
|
|
108
108
|
|
|
109
|
+
0. **`chaim.json` already exists** — if the project already has a `chaim.json` with `generate.stacks`, you do NOT need to resolve a package at all. Just tell the user to run `cdk synth && chaim generate`. If a new stack is being added, add it to `generate.stacks` and use the resolution steps below to fill in the `package` value.
|
|
109
110
|
1. **Existing generated code** — look for `ChaimConfig.java`, `*Repository.java`, or any `@DynamoDbBean`-annotated class under `src/main/java/`. Extract the `package` declaration at the top of the file. This is the most reliable signal.
|
|
110
111
|
2. **Any existing Java source files** — scan `.java` files under `src/main/java/`. Find the common ancestor package (e.g. if sources are in `com.example.service`, use `com.example.model` or `com.example.data` following the project's naming convention).
|
|
111
112
|
3. **`build.gradle.kts` / `build.gradle`** — look for `group = "com.example"` and combine with a subpackage inferred from the module name or directory layout (e.g. `com.example.model`).
|
|
@@ -114,12 +115,30 @@ If the user's feature requires new or modified fields on an existing entity, you
|
|
|
114
115
|
6. **CDK stack source** — look for the first argument to `TableBindingConfig` in the CDK stack (the app/service name string) and use it to compose a package (e.g. `com.<appname>.model`).
|
|
115
116
|
7. **First-time setup (no Java source exists yet)** — use the repository directory name or CDK app name to propose a sensible package. Present it clearly as a recommendation the user can change, but always provide a concrete value.
|
|
116
117
|
|
|
117
|
-
|
|
118
|
+
**For the Java source root (`javaRoot` / `--output`):**
|
|
119
|
+
- Look for the Maven/Gradle source root — typically `src/main/java` or `application/src/main/java`.
|
|
120
|
+
- This must be the directory ABOVE the package path. For example, if Java files live at `application/src/main/java/com/example/sdk/`, the root is `application/src/main/java`.
|
|
121
|
+
- Never include the package path in the root.
|
|
118
122
|
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
**First-time setup: create `chaim.json`**
|
|
124
|
+
|
|
125
|
+
When the project has no `chaim.json`, create one as part of setup rather than emitting a long `chaim generate --stack ... --package ... --output ...` command. Example:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"generate": {
|
|
130
|
+
"javaRoot": "./application/src/main/java",
|
|
131
|
+
"stacks": {
|
|
132
|
+
"OrdersInfrastructureStack": {
|
|
133
|
+
"package": "com.acme.orders.sdk"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
121
138
|
```
|
|
122
139
|
|
|
140
|
+
Then the user runs simply: `cdk synth && chaim generate`
|
|
141
|
+
|
|
123
142
|
Never emit `chaim generate --package <your-package>` — that shifts cognitive load back to the user and defeats the purpose of contextual assistance.
|
|
124
143
|
|
|
125
144
|
---
|
|
@@ -164,10 +183,13 @@ Commands:
|
|
|
164
183
|
context Write AI agent context for using Chaim in this project
|
|
165
184
|
|
|
166
185
|
Generate:
|
|
167
|
-
chaim generate
|
|
168
|
-
|
|
186
|
+
chaim generate No flags needed when chaim.json is present (auto-discovers all stacks)
|
|
187
|
+
chaim generate --package <name> Java package name (optional when chaim.json is present)
|
|
188
|
+
--output <javaRoot> Java source root — package path is appended automatically
|
|
189
|
+
e.g., --output ./src/main/java with --package com.example.sdk
|
|
190
|
+
writes to ./src/main/java/com/example/sdk/
|
|
169
191
|
--language <lang> Target language (default: java)
|
|
170
|
-
--stack <name> Filter
|
|
192
|
+
--stack <name> Filter to a single stack (optional)
|
|
171
193
|
--snapshot-dir <path> Override snapshot directory
|
|
172
194
|
--skip-checks Skip environment validation
|
|
173
195
|
|
|
@@ -175,8 +197,8 @@ Validate:
|
|
|
175
197
|
chaim validate <schemaFile>
|
|
176
198
|
|
|
177
199
|
Bump:
|
|
178
|
-
chaim bump <schemaFile> Minor bump (1.
|
|
179
|
-
chaim bump <schemaFile> --major Major bump (1.
|
|
200
|
+
chaim bump <schemaFile> Minor bump (1.5 → 1.6)
|
|
201
|
+
chaim bump <schemaFile> --major Major bump (1.5 → 2.0)
|
|
180
202
|
|
|
181
203
|
Doctor:
|
|
182
204
|
chaim doctor
|
|
@@ -213,6 +235,153 @@ Override with `CHAIM_SNAPSHOT_DIR` environment variable or `--snapshot-dir`.
|
|
|
213
235
|
|
|
214
236
|
---
|
|
215
237
|
|
|
238
|
+
## Project Config: `chaim.json`
|
|
239
|
+
|
|
240
|
+
For any project with more than one stack, create a `chaim.json` in the project root. This file lets you run a single `chaim generate` (no flags) to regenerate SDKs for **all** stacks at once, with each stack's SDK written to the correct package and source root.
|
|
241
|
+
|
|
242
|
+
### File location
|
|
243
|
+
|
|
244
|
+
Place `chaim.json` alongside `package.json` in the project root (or the root of the monorepo package that owns the CDK code).
|
|
245
|
+
|
|
246
|
+
### Schema
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"generate": {
|
|
251
|
+
"javaRoot": "./application/src/main/java",
|
|
252
|
+
"stacks": {
|
|
253
|
+
"OrdersInfrastructureStack": {
|
|
254
|
+
"package": "com.example.orders.sdk"
|
|
255
|
+
},
|
|
256
|
+
"ProductsInfrastructureStack": {
|
|
257
|
+
"package": "com.example.products.sdk"
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
| Field | Required | Description |
|
|
265
|
+
|-------|----------|-------------|
|
|
266
|
+
| `generate.javaRoot` | No | Shared Java source root for all stacks. Default: `./src/main/java`. **Do not include the package path here** — it is appended automatically. |
|
|
267
|
+
| `generate.stacks` | Yes (for no-flag usage) | Map of CDK stack name → stack config. |
|
|
268
|
+
| `generate.stacks.<name>.package` | Yes | Java package name for this stack's SDK (e.g., `com.example.orders.sdk`). |
|
|
269
|
+
| `generate.stacks.<name>.javaRoot` | No | Per-stack source root override. Falls back to `generate.javaRoot`. |
|
|
270
|
+
| `generate.language` | No | Target language. Default: `java`. |
|
|
271
|
+
| `generate.vscode` | No | VS Code workspace integration. Defaults to `true`. On every `chaim generate`, Chaim writes `files.associations` and `json.schemas` entries into `.vscode/settings.json` so that `.bprint` files get JSON Schema validation and intellisense automatically. Set to `false` to opt out (e.g., if you manage `.vscode/settings.json` via version control and prefer no automated writes). |
|
|
272
|
+
|
|
273
|
+
### How output paths are derived
|
|
274
|
+
|
|
275
|
+
`chaim generate` uses the Java source root (`javaRoot`) as the base and JavaPoet automatically converts the package name to a directory path underneath it:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
javaRoot: ./application/src/main/java
|
|
279
|
+
package: com.example.orders.sdk
|
|
280
|
+
→ writes to: ./application/src/main/java/com/example/orders/sdk/
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
This is the standard Maven/Gradle convention. **Never include the package path in `javaRoot`.**
|
|
284
|
+
|
|
285
|
+
### Usage patterns
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# Zero-flag: regenerate all stacks from chaim.json
|
|
289
|
+
chaim generate
|
|
290
|
+
|
|
291
|
+
# Narrow to one stack (uses chaim.json for package/javaRoot)
|
|
292
|
+
chaim generate --stack OrdersInfrastructureStack
|
|
293
|
+
|
|
294
|
+
# One-off / first-time (no chaim.json required)
|
|
295
|
+
chaim generate --package com.example.orders.sdk --output ./src/main/java
|
|
296
|
+
|
|
297
|
+
# Both flags — output is the Java source root, package is appended
|
|
298
|
+
chaim generate --package com.example.sdk --output ./application/src/main/java
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### CI/CD integration
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# In your build script (package.json or Makefile)
|
|
305
|
+
cdk synth && chaim generate
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
A single `chaim generate` replaces multiple per-stack `chaim generate --stack ...` commands. Existing `--stack` / `--package` / `--output` flags remain fully supported for one-off or scripted runs.
|
|
309
|
+
|
|
310
|
+
### Instructions for the AI agent
|
|
311
|
+
|
|
312
|
+
When a `chaim.json` already exists in the project:
|
|
313
|
+
- **Do NOT tell the user to run `chaim generate --package ...`** — just `chaim generate` is sufficient.
|
|
314
|
+
- When the user adds a new CDK stack with a `ChaimDynamoDBBinder`, add the new stack to `generate.stacks` in `chaim.json` automatically.
|
|
315
|
+
- Confirm the `javaRoot` points to the Java source directory that is on the Gradle/Maven source path.
|
|
316
|
+
|
|
317
|
+
When `chaim.json` does **not** exist yet (first-time setup or single-stack projects):
|
|
318
|
+
- Create `chaim.json` as part of the Chaim setup (see [Package Resolution](#package-resolution) for how to derive the `package` and `javaRoot` values).
|
|
319
|
+
- Use the Package Resolution directive to populate the values — never leave placeholders.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## VS Code Integration
|
|
324
|
+
|
|
325
|
+
`chaim generate` automatically configures VS Code (and Cursor) so that `.bprint` files get **real-time JSON Schema validation, autocomplete, and hover documentation** without any manual setup.
|
|
326
|
+
|
|
327
|
+
### What happens automatically
|
|
328
|
+
|
|
329
|
+
Every time `chaim generate` runs successfully, Chaim merges two entries into `.vscode/settings.json` (creating the file and directory if needed):
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
{
|
|
333
|
+
"files.associations": {
|
|
334
|
+
"*.bprint": "json"
|
|
335
|
+
},
|
|
336
|
+
"json.schemas": [
|
|
337
|
+
{
|
|
338
|
+
"fileMatch": ["*.bprint"],
|
|
339
|
+
"url": "./node_modules/@chaim-tools/chaim-bprint-spec/schema/bprint.schema.json"
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
- **`files.associations`** — tells VS Code to treat `.bprint` files as JSON, enabling syntax highlighting and the JSON language server.
|
|
346
|
+
- **`json.schemas`** — points VS Code's JSON language server at the bundled `bprint.schema.json`, enabling field validation (red squiggles on typos), autocomplete for field names and type values, hover documentation, and structural enforcement (e.g., `list` requires `items`).
|
|
347
|
+
|
|
348
|
+
The schema file (`bprint.schema.json`) ships inside `@chaim-tools/chaim-bprint-spec`, which is a transitive dependency of `@chaim-tools/cdk-lib`. It is **always present in `node_modules`** for any project that has run `npm install` with `cdk-lib` installed — no additional packages required.
|
|
349
|
+
|
|
350
|
+
### How it works — no new dependencies
|
|
351
|
+
|
|
352
|
+
The integration uses **VS Code's built-in JSON language server** — the same one that validates `package.json`, `tsconfig.json`, etc. No VS Code extension installation is required. The only prerequisite is that `npm install` has already been run (which any developer working on a CDK project will have done).
|
|
353
|
+
|
|
354
|
+
Chaim skips writing `.vscode/settings.json` if `node_modules/@chaim-tools/chaim-bprint-spec/schema/bprint.schema.json` does not exist on disk (e.g., before `npm install`).
|
|
355
|
+
|
|
356
|
+
### Idempotent
|
|
357
|
+
|
|
358
|
+
Running `chaim generate` multiple times never duplicates entries. If the required entries are already present, the file is not touched.
|
|
359
|
+
|
|
360
|
+
### Opting out
|
|
361
|
+
|
|
362
|
+
To disable auto-configuration (e.g., you manage `.vscode/settings.json` via version control and prefer no automated writes):
|
|
363
|
+
|
|
364
|
+
```json
|
|
365
|
+
{
|
|
366
|
+
"generate": {
|
|
367
|
+
"vscode": false,
|
|
368
|
+
"stacks": { ... }
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Committing `.vscode/settings.json`
|
|
374
|
+
|
|
375
|
+
It is recommended to **commit `.vscode/settings.json`** to version control. This ensures every developer on the team gets `.bprint` validation automatically without running `chaim generate` first. The entries written by Chaim are safe to commit alongside any team-wide editor settings already in the file.
|
|
376
|
+
|
|
377
|
+
### Instructions for the AI agent
|
|
378
|
+
|
|
379
|
+
- Do NOT tell users to manually configure `files.associations` or `json.schemas` — `chaim generate` handles this automatically.
|
|
380
|
+
- If a user reports that `.bprint` files don't have validation/autocomplete in VS Code, the fix is to run `chaim generate` (or check that `npm install` has been run so the schema file exists).
|
|
381
|
+
- If a user explicitly does not want automated `.vscode/settings.json` writes, guide them to add `"vscode": false` to `chaim.json → generate`.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
216
385
|
## What Is Chaim?
|
|
217
386
|
|
|
218
387
|
Chaim is a schema-driven code generation platform that produces type-safe SDKs from `.bprint` schema files bound to your data store infrastructure.
|
|
@@ -252,9 +421,8 @@ A `.bprint` file is a JSON document describing a single entity. The format is da
|
|
|
252
421
|
"schemaVersion": "1.0",
|
|
253
422
|
"entityName": "Product",
|
|
254
423
|
"description": "Product catalog entity",
|
|
255
|
-
"
|
|
256
|
-
"
|
|
257
|
-
"sortKey": "category"
|
|
424
|
+
"identity": {
|
|
425
|
+
"fields": ["productId", "category"]
|
|
258
426
|
},
|
|
259
427
|
"fields": [
|
|
260
428
|
{ "name": "productId", "type": "string", "required": true },
|
|
@@ -315,37 +483,77 @@ A `.bprint` file is a JSON document describing a single entity. The format is da
|
|
|
315
483
|
| Field | Type | Required | Description |
|
|
316
484
|
|-------|------|----------|-------------|
|
|
317
485
|
| `schemaVersion` | string | Yes | Customer-controlled version in `"major.minor"` format (e.g., `"1.0"`, `"2.3"`) |
|
|
318
|
-
| `entityName` | string | Yes |
|
|
486
|
+
| `entityName` | string | Yes | PascalCase class/type name (e.g., `"User"`, `"OrderItem"`). Must start uppercase, alphanumeric only. Cannot conflict with reserved keywords when lowercased. |
|
|
319
487
|
| `description` | string | Yes | Human-readable description |
|
|
320
|
-
| `
|
|
488
|
+
| `identity` | object | Yes | Contains `fields`: an ordered array of field names that uniquely identify this entity. For DynamoDB: fields[0] = partition key, fields[1] = sort key. For SQL: maps to PRIMARY KEY |
|
|
321
489
|
| `fields` | array | Yes | Field definitions (minimum 1) |
|
|
322
490
|
|
|
323
491
|
### Supported Field Types
|
|
324
492
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
|
330
|
-
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
|
|
337
|
-
|
|
493
|
+
Field types support an optional dot-notation suffix that selects the generated language type without changing the DynamoDB attribute type.
|
|
494
|
+
|
|
495
|
+
**Scalar types:**
|
|
496
|
+
|
|
497
|
+
| .bprint Type | DynamoDB | Java Type | Notes |
|
|
498
|
+
|--------------|----------|-----------|-------|
|
|
499
|
+
| `string` | S | `String` | |
|
|
500
|
+
| `number` | N | `Integer` | Default when suffix omitted |
|
|
501
|
+
| `number.int` | N | `Integer` | 32-bit integer |
|
|
502
|
+
| `number.long` | N | `Long` | 64-bit integer |
|
|
503
|
+
| `number.float` | N | `Float` | 32-bit float |
|
|
504
|
+
| `number.double` | N | `Double` | Explicit double |
|
|
505
|
+
| `number.decimal` | N | `BigDecimal` | Arbitrary-precision |
|
|
506
|
+
| `boolean` | BOOL | `Boolean` | |
|
|
507
|
+
| `binary` | B | `byte[]` | Raw binary data (Buffer in Node.js, bytes in Python) |
|
|
508
|
+
| `timestamp` | S | `Instant` | ISO-8601 full instant |
|
|
509
|
+
| `timestamp.epoch` | N | `Long` | Unix epoch milliseconds |
|
|
510
|
+
| `timestamp.date` | S | `LocalDate` | ISO-8601 date only (e.g. `"2024-01-15"`) |
|
|
511
|
+
|
|
512
|
+
**Collection types:**
|
|
513
|
+
|
|
514
|
+
| .bprint Type | DynamoDB | Java Type | Notes |
|
|
515
|
+
|--------------|----------|-----------|-------|
|
|
516
|
+
| `list` (scalar) | L | `List<String>`, `List<Integer>`, etc. | Requires `items.type` |
|
|
517
|
+
| `list` (map) | L | `List<{FieldName}Item>` | Inner `@DynamoDbBean` class |
|
|
518
|
+
| `map` | M | `{FieldName}` (inner class) | Inner `@DynamoDbBean` class; supports recursive nesting |
|
|
519
|
+
| `stringSet` | SS | `Set<String>` | Unordered collection of unique strings |
|
|
520
|
+
| `numberSet` | NS | `Set<Integer>` | Unordered collection of unique numbers. Default when suffix omitted |
|
|
521
|
+
| `numberSet.int` | NS | `Set<Integer>` | |
|
|
522
|
+
| `numberSet.long` | NS | `Set<Long>` | |
|
|
523
|
+
| `numberSet.float` | NS | `Set<Float>` | |
|
|
524
|
+
| `numberSet.double` | NS | `Set<Double>` | Explicit double |
|
|
525
|
+
| `numberSet.decimal` | NS | `Set<BigDecimal>` | |
|
|
526
|
+
|
|
527
|
+
> **Note**: `timestamp.date` fields require a `LocalDateConverter` — the generator emits it automatically and applies `@DynamoDbConvertedBy` to affected getters. No manual setup needed.
|
|
528
|
+
|
|
529
|
+
**Cross-language mapping (planned generators):**
|
|
530
|
+
|
|
531
|
+
| .bprint Type | Java | TypeScript | Python | Go | C# |
|
|
532
|
+
|--------------|------|------------|--------|----|----|
|
|
533
|
+
| `string` | `String` | `string` | `str` | `string` | `string` |
|
|
534
|
+
| `number` | `Integer` | `number` | `int` | `int32` | `int` |
|
|
535
|
+
| `number.int` | `Integer` | `number` | `int` | `int32` | `int` |
|
|
536
|
+
| `number.long` | `Long` | `bigint` | `int` | `int64` | `long` |
|
|
537
|
+
| `number.decimal` | `BigDecimal` | `Decimal` | `Decimal` | `*big.Float` | `decimal` |
|
|
538
|
+
| `boolean` | `Boolean` | `boolean` | `bool` | `bool` | `bool` |
|
|
539
|
+
| `binary` | `byte[]` | `Buffer` | `bytes` | `[]byte` | `byte[]` |
|
|
540
|
+
| `timestamp` | `Instant` | `string` | `datetime` | `time.Time` | `DateTimeOffset` |
|
|
541
|
+
| `timestamp.epoch` | `Long` | `number` | `int` | `int64` | `long` |
|
|
542
|
+
| `timestamp.date` | `LocalDate` | `string` | `date` | `civil.Date` | `DateOnly` |
|
|
543
|
+
|
|
544
|
+
When `nullable: true`: Java uses boxed types (`Integer` not `int`), Python uses `Optional[T]`, Go uses pointers (`*int32`), C# uses nullable value types (`int?`). TypeScript is unaffected (all types already nullable unless `required`).
|
|
338
545
|
|
|
339
546
|
### Field Properties
|
|
340
547
|
|
|
341
548
|
| Property | Type | Applies To | Description |
|
|
342
549
|
|----------|------|-----------|-------------|
|
|
343
|
-
| `name` | string | All | Attribute/column name in the data store |
|
|
550
|
+
| `name` | string | All | Attribute/column name in the data store. If it collides with a reserved keyword, `nameOverride` is required. |
|
|
344
551
|
| `type` | string | All | One of the supported types above |
|
|
345
|
-
| `nameOverride` | string | All | Custom
|
|
552
|
+
| `nameOverride` | string | All | Custom code identifier when `name` isn't valid or collides with a reserved word |
|
|
346
553
|
| `required` | boolean | All | Generates null-check validation |
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
554
|
+
| `nullable` | boolean | All | When true, generators emit nullable/wrapper types (e.g., `Integer` vs `int` in Java). Identity fields cannot be nullable. Default: false |
|
|
555
|
+
| `default` | varies | Scalars (not `binary`) | Default value; type must match field type |
|
|
556
|
+
| `enum` | (string \| number)[] | Scalars (not `binary`) | Allowed values; type-matched (strings for string fields, numbers for number fields). Validated on nested fields too. |
|
|
349
557
|
| `description` | string | All | Generates Javadoc comments |
|
|
350
558
|
| `constraints` | object | Scalars | Validation constraints (see below) |
|
|
351
559
|
| `items` | object | `list` only | Required — defines element type |
|
|
@@ -357,9 +565,9 @@ A `.bprint` file is a JSON document describing a single entity. The format is da
|
|
|
357
565
|
|------------|-----------|-------------|
|
|
358
566
|
| `minLength` / `maxLength` | `string` | String length bounds |
|
|
359
567
|
| `pattern` | `string` | Regex pattern |
|
|
360
|
-
| `min` / `max` | `number` | Numeric range |
|
|
568
|
+
| `min` / `max` | `number` and `number.*` | Numeric range (works with all sub-types including `number.decimal`) |
|
|
361
569
|
|
|
362
|
-
Constraints cannot be applied to collection types (`list`, `map`, `stringSet`, `numberSet`).
|
|
570
|
+
Constraints cannot be applied to collection types (`list`, `map`, `stringSet`, `numberSet`, `numberSet.*`).
|
|
363
571
|
|
|
364
572
|
### Recursive Nesting
|
|
365
573
|
|
|
@@ -367,7 +575,14 @@ Constraints cannot be applied to collection types (`list`, `map`, `stringSet`, `
|
|
|
367
575
|
|
|
368
576
|
### Schema Version Rules
|
|
369
577
|
|
|
370
|
-
|
|
578
|
+
**Two version concepts — do not confuse them:**
|
|
579
|
+
|
|
580
|
+
| Concept | Where | Who controls |
|
|
581
|
+
|---------|-------|-------------|
|
|
582
|
+
| `schemaVersion` (in `.bprint` file) | `"schemaVersion": "1.0"` | The user — bumped per entity schema change |
|
|
583
|
+
| Spec version (this package) | `spec-versions.json` | Chaim maintainers — tracks `.bprint` format changes |
|
|
584
|
+
|
|
585
|
+
- `schemaVersion` is the **user's entity version**, not the spec version. Start at `"1.0"`, increment on content change.
|
|
371
586
|
- During `cdk deploy`, the Chaim server validates that the version was bumped when schema content changes. **Remind users frequently: bumping is mandatory on content change — enforced by the Chaim server at deploy time.**
|
|
372
587
|
- Use `chaim bump <file>` to increment automatically.
|
|
373
588
|
|
|
@@ -434,7 +649,7 @@ new ChaimDynamoDBBinder(this, 'OrderBinding', {
|
|
|
434
649
|
});
|
|
435
650
|
```
|
|
436
651
|
|
|
437
|
-
All entities sharing a table **must** have matching
|
|
652
|
+
All entities sharing a table **must** have matching identity field names.
|
|
438
653
|
|
|
439
654
|
### Credentials
|
|
440
655
|
|
|
@@ -572,26 +787,26 @@ These workflows are specific to the DynamoDB integration. The pattern will be si
|
|
|
572
787
|
1. Create a `.bprint` schema file
|
|
573
788
|
2. Create a DynamoDB table in the CDK stack
|
|
574
789
|
3. Add a `ChaimDynamoDBBinder` binding the schema to the table
|
|
575
|
-
4.
|
|
576
|
-
5. Run `
|
|
790
|
+
4. Add the new stack to `chaim.json → generate.stacks` (create `chaim.json` if it doesn't exist yet — see [Package Resolution](#package-resolution))
|
|
791
|
+
5. Run `cdk synth && chaim generate`
|
|
577
792
|
|
|
578
793
|
### Add a New Entity to an Existing Table (Single-Table Design)
|
|
579
794
|
|
|
580
|
-
1. Create a `.bprint` schema with **matching
|
|
795
|
+
1. Create a `.bprint` schema with **matching identity field names** as existing entities on that table
|
|
581
796
|
2. Add another `ChaimDynamoDBBinder` for the same table
|
|
582
|
-
3. Run `cdk synth
|
|
797
|
+
3. Run `cdk synth && chaim generate`
|
|
583
798
|
|
|
584
799
|
### Add a Field to an Existing Entity
|
|
585
800
|
|
|
586
801
|
1. Add the field to the `.bprint` file
|
|
587
802
|
2. Run `chaim bump <file>` to increment `schemaVersion`
|
|
588
|
-
3. Run `cdk synth
|
|
803
|
+
3. Run `cdk synth && chaim generate`
|
|
589
804
|
|
|
590
805
|
### Add a GSI to a Table
|
|
591
806
|
|
|
592
807
|
1. Add the GSI to the CDK table definition
|
|
593
808
|
2. Ensure the GSI key attributes exist as fields in the `.bprint` schema
|
|
594
|
-
3. Run `cdk synth
|
|
809
|
+
3. Run `cdk synth && chaim generate`
|
|
595
810
|
4. New `queryBy{IndexName}()` methods appear in the repository
|
|
596
811
|
|
|
597
812
|
### Change a Schema and Deploy
|
|
@@ -599,10 +814,10 @@ These workflows are specific to the DynamoDB integration. The pattern will be si
|
|
|
599
814
|
1. Edit the `.bprint` file
|
|
600
815
|
2. Run `chaim bump <file>` — **required before deploy** if content changed
|
|
601
816
|
3. Run `cdk synth` (validates schema and creates snapshot)
|
|
602
|
-
4. Run `chaim generate` (regenerates Java SDK)
|
|
817
|
+
4. Run `chaim generate` (regenerates Java SDK — reads stack config from `chaim.json` automatically)
|
|
603
818
|
5. Run `cdk deploy` (deploys infrastructure and publishes snapshot)
|
|
604
819
|
|
|
605
|
-
**CI/CD tip:** Run `chaim generate` as a build step
|
|
820
|
+
**CI/CD tip:** Run `cdk synth && chaim generate` as a single build step. Commit `.bprint` and `chaim.json` files but gitignore generated Java code. Fail the build if `schemaVersion` wasn't bumped on schema change.
|
|
606
821
|
|
|
607
822
|
---
|
|
608
823
|
|
|
@@ -615,10 +830,15 @@ When the user encounters errors, check for these common causes:
|
|
|
615
830
|
| **HTTP 409 on `cdk deploy`** | `schemaVersion` not bumped after schema content change | Run `chaim bump <file>` then re-synth and redeploy |
|
|
616
831
|
| **"No snapshots found" from `chaim generate`** | `cdk synth` was not run, or snapshots are in a non-default location | Run `cdk synth` first, or pass `--snapshot-dir <path>` |
|
|
617
832
|
| **`cdk synth` fails with key mismatch** | Table PK/SK, GSI, LSI, or TTL attribute names don't match fields in the `.bprint` schema | Ensure every key attribute referenced by the table/indexes exists as a field in the `.bprint` `fields` array |
|
|
618
|
-
| **Stale generated code** | Schema changed but `chaim generate` was not re-run | Run `
|
|
619
|
-
| **
|
|
833
|
+
| **Stale generated code** | Schema changed but `chaim generate` was not re-run | Run `cdk synth && chaim generate`. If `chaim.json` is not present, see [Package Resolution](#package-resolution) to resolve the package first. |
|
|
834
|
+
| **"--package is required" error** | `chaim generate` run without `--package` and no `chaim.json` exists | Create `chaim.json` with a `generate.stacks` block — see [Project Config: chaim.json](#project-config-chaimjson) |
|
|
835
|
+
| **Single-table key conflict** | Entities bound to the same table have mismatched identity field names | All `.bprint` schemas sharing a table must use identical identity field names |
|
|
620
836
|
| **Auth errors during synth/deploy** | Missing or invalid Chaim credentials | Check `ChaimCredentials` in CDK (prod: Secrets Manager) or set `CHAIM_API_KEY`/`CHAIM_API_SECRET` env vars (dev-only). Do NOT assume credentials are auto-present |
|
|
621
837
|
| **Generated code compile errors** | Usually a `.bprint` schema issue (invalid types, missing required fields) | Run `chaim validate <file>` to check schema, fix issues, then regenerate |
|
|
838
|
+
| **"not a valid type name" on entityName** | `entityName` is not PascalCase (lowercase, dotted, or hyphenated) | Use PascalCase: `"User"`, `"OrderItem"`. No dots, hyphens, or underscores. |
|
|
839
|
+
| **"reserved keyword" on field name** | Field `name` matches a reserved word in Java, Python, Go, or TypeScript | Add a `nameOverride` to provide an alternative code identifier |
|
|
840
|
+
| **No `.bprint` validation / autocomplete in VS Code** | `chaim generate` hasn't been run yet, or `npm install` hasn't been run | Run `npm install` then `cdk synth && chaim generate` — this auto-writes `.vscode/settings.json` |
|
|
841
|
+
| **TTL items never expire (or expire immediately)** | `timestamp.epoch` TTL field populated with milliseconds instead of seconds | Use `Instant.getEpochSecond()` in Java, not `toEpochMilli()`. Configure TTL via `timeToLiveAttribute` on the CDK table — not in the `.bprint` schema. |
|
|
622
842
|
|
|
623
843
|
---
|
|
624
844
|
|
|
@@ -640,13 +860,21 @@ When the user encounters errors, check for these common causes:
|
|
|
640
860
|
|
|
641
861
|
3. **Generated code should be gitignored** — Regenerate during the build process. Never suggest committing generated files.
|
|
642
862
|
|
|
863
|
+
4. **Schema vs infrastructure boundary** — The `.bprint` schema defines *what data looks like* (fields, types, constraints). Infrastructure concerns (TTL, GSIs/LSIs, billing, streams, uniqueness enforcement, encryption) belong in CDK constructs. This keeps schemas portable across databases and languages. Specifically: TTL → `timeToLiveAttribute` on CDK table; indexes → `addGlobalSecondaryIndex()` / `addLocalSecondaryIndex()`; uniqueness → GSI design (DynamoDB) or DB constraint (SQL).
|
|
864
|
+
|
|
865
|
+
5. **DynamoDB TTL is CDK-only** — Configure TTL via `timeToLiveAttribute` on the CDK `dynamodb.Table` construct. The `.bprint` schema only declares the field with `type: "timestamp.epoch"`. When populating a TTL field in application code, use epoch **seconds** (`Instant.getEpochSecond()`), not milliseconds — DynamoDB TTL interprets the Number as seconds.
|
|
866
|
+
|
|
867
|
+
6. **`.vscode/settings.json` is written automatically** — `chaim generate` merges VS Code workspace settings on every run. This is idempotent and safe to commit. Users do NOT need to configure VS Code manually for `.bprint` validation. If VS Code intellisense isn't working for `.bprint` files, the fix is to run `chaim generate` (or ensure `npm install` has been run). Opt out via `"vscode": false` in `chaim.json → generate`.
|
|
868
|
+
|
|
643
869
|
### DynamoDB-Specific
|
|
644
870
|
|
|
871
|
+
> **Scope note:** Indexes (GSI/LSI), TTL, streams, and billing mode are configured on the CDK table construct — not in the `.bprint` schema. This is by design — keeping infrastructure-specific knobs in the infrastructure layer makes `.bprint` schemas portable across data stores.
|
|
872
|
+
|
|
645
873
|
4. **Key fields must exist in schema** — All DynamoDB key attributes (table PK/SK, GSI/LSI keys, TTL attribute) must be defined as fields in the `.bprint` schema. Mismatches fail `cdk synth`.
|
|
646
874
|
|
|
647
875
|
5. **LSIs share the table's partition key** — LSI metadata does not include a `partitionKey` field. The generated code automatically uses the table's partition key for LSI queries.
|
|
648
876
|
|
|
649
|
-
6. **Single-table entities must agree on keys** — All entities bound to the same DynamoDB table must have matching
|
|
877
|
+
6. **Single-table entities must agree on keys** — All entities bound to the same DynamoDB table must have matching identity field names in their `.bprint` schemas.
|
|
650
878
|
|
|
651
879
|
7. **`save()` does full replacement** — The generated `save()` uses `PutItem`, which replaces the entire item. Partial updates are not yet supported. Do NOT suggest partial update patterns.
|
|
652
880
|
|
|
@@ -655,25 +883,54 @@ When the user encounters errors, check for these common causes:
|
|
|
655
883
|
## Project Layout (Recommended)
|
|
656
884
|
|
|
657
885
|
```
|
|
658
|
-
my-
|
|
659
|
-
├──
|
|
660
|
-
|
|
661
|
-
│ └──
|
|
662
|
-
├──
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
├──
|
|
667
|
-
│
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
├──
|
|
671
|
-
│
|
|
672
|
-
├──
|
|
673
|
-
└── ...
|
|
886
|
+
my-project/ # Project root (monorepo or single-repo)
|
|
887
|
+
├── chaim.json # ← Chaim project config (commit this)
|
|
888
|
+
├── .vscode/
|
|
889
|
+
│ └── settings.json # ← auto-written by chaim generate (commit this)
|
|
890
|
+
├── infrastructure/ # CDK infrastructure
|
|
891
|
+
│ ├── schemas/
|
|
892
|
+
│ │ ├── user.bprint
|
|
893
|
+
│ │ └── order.bprint
|
|
894
|
+
│ ├── lib/my-stack.ts
|
|
895
|
+
│ └── package.json # see dependency structure below
|
|
896
|
+
└── application/ # Java application
|
|
897
|
+
└── src/main/java/
|
|
898
|
+
├── com/example/orders/sdk/ # ← generated by chaim generate (gitignore this)
|
|
899
|
+
│ ├── Order.java
|
|
900
|
+
│ ├── OrderRepository.java
|
|
901
|
+
│ └── ...
|
|
902
|
+
└── com/example/ # Your application code
|
|
903
|
+
└── service/OrderService.java
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
**`chaim.json`** — project-level SDK generation config:
|
|
907
|
+
|
|
908
|
+
```json
|
|
909
|
+
{
|
|
910
|
+
"generate": {
|
|
911
|
+
"javaRoot": "./application/src/main/java",
|
|
912
|
+
"stacks": {
|
|
913
|
+
"OrdersInfrastructureStack": {
|
|
914
|
+
"package": "com.example.orders.sdk"
|
|
915
|
+
},
|
|
916
|
+
"ProductsInfrastructureStack": {
|
|
917
|
+
"package": "com.example.products.sdk"
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
With this file in place, `chaim generate` (no flags) regenerates all stacks automatically.
|
|
925
|
+
|
|
926
|
+
**`.gitignore` additions:**
|
|
927
|
+
```
|
|
928
|
+
# Chaim generated SDK (regenerated at build time — do not commit)
|
|
929
|
+
application/src/main/java/com/example/orders/sdk/
|
|
930
|
+
application/src/main/java/com/example/products/sdk/
|
|
674
931
|
```
|
|
675
932
|
|
|
676
|
-
**`
|
|
933
|
+
**`infrastructure/package.json`** — correct dependency placement:
|
|
677
934
|
|
|
678
935
|
```json
|
|
679
936
|
{
|