ts-procedures 8.3.0 → 8.5.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/agent_config/claude-code/skills/ts-procedures/SKILL.md +26 -8
- package/agent_config/claude-code/skills/ts-procedures/templates/client.md +3 -3
- package/agent_config/claude-code/skills/ts-procedures/templates/hono.md +3 -3
- package/agent_config/claude-code/skills/ts-procedures/templates/procedure.md +3 -3
- package/agent_config/claude-code/skills/ts-procedures/templates/stream-procedure.md +3 -3
- package/build/client/call.js +1 -1
- package/build/client/call.js.map +1 -1
- package/build/client/index.d.ts +1 -1
- package/build/client/index.js +23 -1
- package/build/client/index.js.map +1 -1
- package/build/client/index.test.js +87 -0
- package/build/client/index.test.js.map +1 -1
- package/build/client/resolve-options.d.ts +5 -4
- package/build/client/resolve-options.js +18 -7
- package/build/client/resolve-options.js.map +1 -1
- package/build/client/resolve-options.test.js +53 -24
- package/build/client/resolve-options.test.js.map +1 -1
- package/build/client/stream.js +1 -1
- package/build/client/stream.js.map +1 -1
- package/build/client/types.d.ts +31 -3
- package/build/codegen/__fixtures__/make-envelope.d.ts +41 -0
- package/build/codegen/__fixtures__/make-envelope.js +38 -0
- package/build/codegen/__fixtures__/make-envelope.js.map +1 -0
- package/build/codegen/bin/cli.d.ts +15 -0
- package/build/codegen/bin/cli.js +46 -21
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/bin/cli.test.js +54 -1
- package/build/codegen/bin/cli.test.js.map +1 -1
- package/build/codegen/bin/flag-specs.d.ts +10 -0
- package/build/codegen/bin/flag-specs.js +62 -0
- package/build/codegen/bin/flag-specs.js.map +1 -0
- package/build/codegen/bin/flag-specs.test.d.ts +1 -0
- package/build/codegen/bin/flag-specs.test.js +35 -0
- package/build/codegen/bin/flag-specs.test.js.map +1 -0
- package/build/codegen/collect-models.d.ts +48 -0
- package/build/codegen/collect-models.js +84 -0
- package/build/codegen/collect-models.js.map +1 -0
- package/build/codegen/collect-models.test.d.ts +1 -0
- package/build/codegen/collect-models.test.js +59 -0
- package/build/codegen/collect-models.test.js.map +1 -0
- package/build/codegen/emit-client-runtime.js +1 -0
- package/build/codegen/emit-client-runtime.js.map +1 -1
- package/build/codegen/emit-models.d.ts +26 -0
- package/build/codegen/emit-models.js +53 -0
- package/build/codegen/emit-models.js.map +1 -0
- package/build/codegen/emit-models.test.d.ts +1 -0
- package/build/codegen/emit-models.test.js +42 -0
- package/build/codegen/emit-models.test.js.map +1 -0
- package/build/codegen/emit-scope.d.ts +10 -0
- package/build/codegen/emit-scope.js +119 -34
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-types.d.ts +26 -1
- package/build/codegen/emit-types.js +27 -5
- package/build/codegen/emit-types.js.map +1 -1
- package/build/codegen/index.d.ts +15 -0
- package/build/codegen/index.js +5 -0
- package/build/codegen/index.js.map +1 -1
- package/build/codegen/model-refs.d.ts +27 -0
- package/build/codegen/model-refs.js +49 -0
- package/build/codegen/model-refs.js.map +1 -0
- package/build/codegen/model-refs.test.d.ts +1 -0
- package/build/codegen/model-refs.test.js +33 -0
- package/build/codegen/model-refs.test.js.map +1 -0
- package/build/codegen/pipeline.d.ts +7 -0
- package/build/codegen/pipeline.js +6 -1
- package/build/codegen/pipeline.js.map +1 -1
- package/build/codegen/schema-walk.d.ts +13 -0
- package/build/codegen/schema-walk.js +26 -0
- package/build/codegen/schema-walk.js.map +1 -0
- package/build/codegen/schema-walk.test.d.ts +1 -0
- package/build/codegen/schema-walk.test.js +35 -0
- package/build/codegen/schema-walk.test.js.map +1 -0
- package/build/codegen/targets/_shared/target-run.d.ts +15 -0
- package/build/codegen/targets/ts/run.js +37 -1
- package/build/codegen/targets/ts/run.js.map +1 -1
- package/build/codegen/targets/ts/shared-models.test.d.ts +1 -0
- package/build/codegen/targets/ts/shared-models.test.js +354 -0
- package/build/codegen/targets/ts/shared-models.test.js.map +1 -0
- package/build/doc-envelope.d.ts +13 -0
- package/build/doc-envelope.js +23 -0
- package/build/doc-envelope.js.map +1 -0
- package/build/doc-envelope.test.d.ts +1 -0
- package/build/doc-envelope.test.js +31 -0
- package/build/doc-envelope.test.js.map +1 -0
- package/build/exports.d.ts +2 -0
- package/build/exports.js +1 -0
- package/build/exports.js.map +1 -1
- package/docs/client-and-codegen.md +163 -0
- package/docs/handoffs/ajsc-named-type-collision.md +134 -0
- package/docs/handoffs/ajsc-named-type-support.md +181 -0
- package/docs/handoffs/shared-models-auto-resolve-response.md +181 -0
- package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +1292 -0
- package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +659 -0
- package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +285 -0
- package/package.json +2 -2
- package/src/client/call.ts +1 -1
- package/src/client/index.test.ts +98 -0
- package/src/client/index.ts +32 -1
- package/src/client/resolve-options.test.ts +73 -26
- package/src/client/resolve-options.ts +23 -9
- package/src/client/stream.ts +1 -1
- package/src/client/types.ts +34 -3
- package/src/codegen/__fixtures__/make-envelope.ts +89 -0
- package/src/codegen/bin/cli.test.ts +65 -1
- package/src/codegen/bin/cli.ts +51 -22
- package/src/codegen/bin/flag-specs.test.ts +38 -0
- package/src/codegen/bin/flag-specs.ts +71 -0
- package/src/codegen/collect-models.test.ts +68 -0
- package/src/codegen/collect-models.ts +125 -0
- package/src/codegen/emit-client-runtime.ts +1 -0
- package/src/codegen/emit-models.test.ts +48 -0
- package/src/codegen/emit-models.ts +63 -0
- package/src/codegen/emit-scope.ts +145 -33
- package/src/codegen/emit-types.ts +48 -7
- package/src/codegen/index.ts +20 -0
- package/src/codegen/model-refs.test.ts +37 -0
- package/src/codegen/model-refs.ts +57 -0
- package/src/codegen/pipeline.ts +13 -1
- package/src/codegen/schema-walk.test.ts +37 -0
- package/src/codegen/schema-walk.ts +23 -0
- package/src/codegen/targets/_shared/target-run.ts +15 -0
- package/src/codegen/targets/ts/run.ts +50 -0
- package/src/codegen/targets/ts/shared-models.test.ts +391 -0
- package/src/doc-envelope.test.ts +35 -0
- package/src/doc-envelope.ts +30 -0
- package/src/exports.ts +2 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Response: shared-models auto-resolve (re: `--shared-types-from` request)
|
|
2
|
+
|
|
3
|
+
> **STATUS: SHIPPED (v8.5.0).** Three targeted improvements shipped in place of source-file scanning. See below for what landed and why the requested mechanism was declined.
|
|
4
|
+
|
|
5
|
+
**To:** downstream developer
|
|
6
|
+
**From:** ts-procedures codegen maintainers
|
|
7
|
+
**Date:** 2026-06-06
|
|
8
|
+
**Reference:** feedback on `sharedTypesImport` DX — pain points 1–3
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## TL;DR
|
|
13
|
+
|
|
14
|
+
The goal — "define it once, no second table, no silent drift" — was right. We shipped three targeted fixes that deliver those ergonomics from the DocEnvelope side rather than by scanning TypeScript source files, which would introduce a worse class of silent failure. The per-`$id` `sharedTypesImport` map is unchanged and remains available as a precision escape hatch.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## What shipped
|
|
19
|
+
|
|
20
|
+
### 1. `sharedModelsModule` — one line replaces the entire map
|
|
21
|
+
|
|
22
|
+
A single module path tells codegen: *"re-export every `$id` model from this module under its derived name."*
|
|
23
|
+
|
|
24
|
+
```jsonc
|
|
25
|
+
// ts-procedures-codegen.config.json
|
|
26
|
+
{ "sharedModelsModule": "@app/schemas" }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# CLI
|
|
31
|
+
npx ts-procedures-codegen --url http://localhost:3000/doc --out ./generated --shared-models-module @app/schemas
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// generateClient() API
|
|
36
|
+
await generateClient({ url, outDir, sharedModelsModule: '@app/schemas' })
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Convention: the derived name is the model's `$id`/`title` (PascalCase), which is the same identifier your schema file already exports. If the names align — they usually do — the entire `sharedTypesImport` map collapses to this single value.
|
|
40
|
+
|
|
41
|
+
Precedence: explicit `sharedTypesImport[$id]` entry → `sharedModelsModule` convention → generate locally. The map remains available to override individual entries (rename, different package, multi-consumer split).
|
|
42
|
+
|
|
43
|
+
### 2. Summary log (CLI)
|
|
44
|
+
|
|
45
|
+
Every CLI run that finds `$id`-bearing models now prints:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
[ts-procedures-codegen] Shared models: 15 total — 15 re-exported, 0 generated locally.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This makes silent generated-twin drift visible without requiring any additional flags or contract tests. (Calling `generateClient` programmatically is silent by default — pass `logger: console.log` to opt in — so the summary never leaks into your own build-script output.)
|
|
52
|
+
|
|
53
|
+
### 3. `--strict-shared-models` — hard-fail on local twins
|
|
54
|
+
|
|
55
|
+
Fails the run and lists every `$id` that would be generated as a local structural twin instead of re-exported:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx ts-procedures-codegen ... --strict-shared-models
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```jsonc
|
|
62
|
+
{ "strictSharedModels": true }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
await generateClient({ url, outDir, sharedModelsModule: '@app/schemas', strictSharedModels: true })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This is a **CI flag, not a development default** — run it in your pipeline and codegen becomes the drift gate. It replaces a hand-written `toEqualTypeOf` contract test.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## How this maps to your three pain points
|
|
74
|
+
|
|
75
|
+
### Pain 1: Two places to keep in lockstep (`$id` and the map entry)
|
|
76
|
+
|
|
77
|
+
`sharedModelsModule` derives the import from the model's own `$id`/`title` — you define the convention once and it applies to every model. There is no second table to maintain.
|
|
78
|
+
|
|
79
|
+
### Pain 2: Silent fallback — a generated structural twin that typechecks but isn't the real type
|
|
80
|
+
|
|
81
|
+
Two layers now make this visible:
|
|
82
|
+
|
|
83
|
+
- The CLI summary log surfaces the "generated locally" count on every codegen run.
|
|
84
|
+
- `--strict-shared-models` promotes that to a hard failure, listing the specific `$id`s. Add it to your CI pipeline and the generated-twin state becomes impossible to ship.
|
|
85
|
+
|
|
86
|
+
This replaces your hand-written `toEqualTypeOf` contract test with a single flag — the codegen itself owns the invariant.
|
|
87
|
+
|
|
88
|
+
### Pain 3: The map is redundant — `module` is "where the schema lives" and `name` is usually the `$id`
|
|
89
|
+
|
|
90
|
+
`sharedModelsModule` makes that redundancy disappear. `name` is derived from the model's `$id`/`title`; `module` is supplied once for all models. The 15-entry map becomes one line.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Why `--shared-types-from <dir|glob>` was declined
|
|
95
|
+
|
|
96
|
+
The goal was right. The mechanism is what we changed.
|
|
97
|
+
|
|
98
|
+
ts-procedures codegen has one explicit input contract: a **DocEnvelope** obtained via `--url`, `--file`, or a passed object. The envelope is a self-contained JSON document — it contains schemas, routes, and error shapes. Codegen never reads the consumer's TypeScript source tree. That decoupling is what makes codegen work against a live remote server, a serialized offline file, or a published package with no local source.
|
|
99
|
+
|
|
100
|
+
Source-file scanning would break that contract in four concrete ways:
|
|
101
|
+
|
|
102
|
+
**(a) Silent desync in `--url` / offline `--file` cases.** The live server's envelope and the local source tree can disagree at any point in time — a schema field renamed in the server but not yet deployed, an `$id` changed in source but not rebuilt. Source scanning would produce a "shared" reference that names a type the server actually never returns. The result typechecks and the single-source-of-truth claim is restored, but the types are silently WRONG — a worse outcome than today's duplicate-but-correct structural twin.
|
|
103
|
+
|
|
104
|
+
**(b) Fails for published-package consumers.** If `@app/schemas` lives in `node_modules`, there is no TypeScript source tree to scan — only compiled `.d.ts` files (if any) or nothing. The scan would silently skip models that are genuinely shared from a published package and generate local twins for them anyway, with no signal.
|
|
105
|
+
|
|
106
|
+
**(c) Requires a TS parser, glob runner, and export-resolution heuristics in codegen.** This is a substantial dependency and surface area. It introduces failure modes (barrel re-exports, `export * from`, conditional exports, `.d.ts` vs. `.ts`) whose edge cases are hard to exhaustively enumerate and maintain. The DocEnvelope contract specifically exists to avoid this category of tooling dependency.
|
|
107
|
+
|
|
108
|
+
**(d) Does not solve module-path correctness.** A file path discovered by glob scanning (`src/schemas/message.ts`) is not a valid TypeScript import specifier for a consumer who imports `@app/schemas`. Path-to-module-alias resolution requires TypeScript's full compiler configuration. Without it, any path the scan produces must be corrected by hand — which brings back a second table.
|
|
109
|
+
|
|
110
|
+
The `sharedModelsModule` convention solves the same "define once, no second table" problem. The module specifier (`@app/schemas`) is typed once; names are derived from the model's own `$id`/`title`. It works in all deployment topologies: live server, offline file, published package.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Escape hatch: `sharedTypesImport` unchanged
|
|
115
|
+
|
|
116
|
+
The per-`$id` map is unchanged and remains available for cases where `sharedModelsModule` is not enough:
|
|
117
|
+
|
|
118
|
+
- A model whose TypeScript name differs from its `$id` (rename case).
|
|
119
|
+
- Different models imported from different packages (`@app/schemas` and `@app/internal-schemas`).
|
|
120
|
+
- Multi-consumer split where a subset of models come from a shared package and others are generated locally.
|
|
121
|
+
|
|
122
|
+
```jsonc
|
|
123
|
+
{
|
|
124
|
+
"sharedModelsModule": "@app/schemas",
|
|
125
|
+
"sharedTypesImport": {
|
|
126
|
+
"urn:internal-msg": { "module": "@app/internal-schemas", "name": "InternalMessage" }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`sharedTypesImport` entries take precedence over `sharedModelsModule`, so you can cover the general case with the convention and override individual outliers explicitly.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Migration example
|
|
136
|
+
|
|
137
|
+
**Before (v8.3 and earlier) — 15-entry hand-maintained map + contract test:**
|
|
138
|
+
|
|
139
|
+
```jsonc
|
|
140
|
+
// ts-procedures-codegen.config.json
|
|
141
|
+
{
|
|
142
|
+
"shareModels": true,
|
|
143
|
+
"sharedTypesImport": {
|
|
144
|
+
"urn:message": { "module": "@app/schemas", "name": "Message" },
|
|
145
|
+
"urn:user": { "module": "@app/schemas", "name": "User" },
|
|
146
|
+
"urn:thread": { "module": "@app/schemas", "name": "Thread" },
|
|
147
|
+
"urn:attachment": { "module": "@app/schemas", "name": "Attachment" },
|
|
148
|
+
"urn:reaction": { "module": "@app/schemas", "name": "Reaction" },
|
|
149
|
+
"urn:channel": { "module": "@app/schemas", "name": "Channel" },
|
|
150
|
+
"urn:workspace": { "module": "@app/schemas", "name": "Workspace" },
|
|
151
|
+
"urn:notification": { "module": "@app/schemas", "name": "Notification" },
|
|
152
|
+
"urn:role": { "module": "@app/schemas", "name": "Role" },
|
|
153
|
+
"urn:permission": { "module": "@app/schemas", "name": "Permission" },
|
|
154
|
+
"urn:bot": { "module": "@app/schemas", "name": "Bot" },
|
|
155
|
+
"urn:webhook": { "module": "@app/schemas", "name": "Webhook" },
|
|
156
|
+
"urn:app": { "module": "@app/schemas", "name": "App" },
|
|
157
|
+
"urn:token": { "module": "@app/schemas", "name": "Token" },
|
|
158
|
+
"urn:audit-log": { "module": "@app/schemas", "name": "AuditLog" }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// contract test to catch silent twins
|
|
165
|
+
expect(generatedMessage).toEqualTypeOf<Message>()
|
|
166
|
+
expect(generatedUser).toEqualTypeOf<User>()
|
|
167
|
+
// ... × 15
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**After (v8.5.0) — one config line + one CI flag:**
|
|
171
|
+
|
|
172
|
+
```jsonc
|
|
173
|
+
// ts-procedures-codegen.config.json
|
|
174
|
+
{
|
|
175
|
+
"shareModels": true,
|
|
176
|
+
"sharedModelsModule": "@app/schemas",
|
|
177
|
+
"strictSharedModels": true
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
No map. No contract tests. `--strict-shared-models` (or `"strictSharedModels": true`) in CI is the drift gate: codegen fails and names the offender if any `$id` model would silently generate as a local twin.
|