@manifesto-ai/codegen 0.1.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/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/header.d.ts +13 -0
- package/dist/header.d.ts.map +1 -0
- package/dist/header.js +18 -0
- package/dist/header.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/path-safety.d.ts +16 -0
- package/dist/path-safety.d.ts.map +1 -0
- package/dist/path-safety.js +47 -0
- package/dist/path-safety.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/ts-plugin.d.ts +11 -0
- package/dist/plugins/ts-plugin.d.ts.map +1 -0
- package/dist/plugins/ts-plugin.js +202 -0
- package/dist/plugins/ts-plugin.js.map +1 -0
- package/dist/plugins/zod-plugin.d.ts +6 -0
- package/dist/plugins/zod-plugin.d.ts.map +1 -0
- package/dist/plugins/zod-plugin.js +138 -0
- package/dist/plugins/zod-plugin.js.map +1 -0
- package/dist/runner.d.ts +13 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +126 -0
- package/dist/runner.js.map +1 -0
- package/dist/stable-hash.d.ts +7 -0
- package/dist/stable-hash.d.ts.map +1 -0
- package/dist/stable-hash.js +11 -0
- package/dist/stable-hash.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/virtual-fs.d.ts +27 -0
- package/dist/virtual-fs.d.ts.map +1 -0
- package/dist/virtual-fs.js +74 -0
- package/dist/virtual-fs.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Manifesto AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# @manifesto-ai/codegen
|
|
2
|
+
|
|
3
|
+
> **Codegen** generates TypeScript types and Zod schemas from a Manifesto DomainSchema through a deterministic plugin pipeline.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What is Codegen?
|
|
8
|
+
|
|
9
|
+
Codegen transforms a DomainSchema into type-safe code artifacts. It runs plugins sequentially, each producing file patches that are validated, collision-checked, and flushed to disk.
|
|
10
|
+
|
|
11
|
+
In the Manifesto architecture:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
DomainSchema -> CODEGEN -> Generated Files
|
|
15
|
+
|
|
|
16
|
+
Plugin pipeline
|
|
17
|
+
(deterministic, no runtime deps)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## What Codegen Does
|
|
23
|
+
|
|
24
|
+
| Responsibility | Description |
|
|
25
|
+
|----------------|-------------|
|
|
26
|
+
| Generate TypeScript types | DomainSchema types -> `export interface` / `export type` |
|
|
27
|
+
| Generate Zod schemas | DomainSchema types -> Zod validators with type annotations |
|
|
28
|
+
| Plugin pipeline | Run plugins sequentially with shared artifacts |
|
|
29
|
+
| Path safety | Validate and normalize output file paths |
|
|
30
|
+
| Collision detection | Prevent multiple plugins from writing to the same file |
|
|
31
|
+
| Deterministic output | Same schema always produces identical files |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## What Codegen Does NOT Do
|
|
36
|
+
|
|
37
|
+
| NOT Responsible For | Who Is |
|
|
38
|
+
|--------------------|--------|
|
|
39
|
+
| Define schemas | App (DomainSchema authoring) |
|
|
40
|
+
| Runtime validation | Application code using generated Zod schemas |
|
|
41
|
+
| Bundling or compilation | Build tools (tsc, esbuild, etc.) |
|
|
42
|
+
| Schema versioning | `@manifesto-ai/core` |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm add @manifesto-ai/codegen
|
|
50
|
+
# or
|
|
51
|
+
npm install @manifesto-ai/codegen
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Peer dependency:** `@manifesto-ai/core` must be installed separately.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Quick Example
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { generate, createTsPlugin, createZodPlugin } from "@manifesto-ai/codegen";
|
|
62
|
+
import type { DomainSchema } from "@manifesto-ai/core";
|
|
63
|
+
|
|
64
|
+
const schema: DomainSchema = { /* your domain schema */ };
|
|
65
|
+
|
|
66
|
+
const result = await generate({
|
|
67
|
+
schema,
|
|
68
|
+
outDir: "./generated",
|
|
69
|
+
plugins: [createTsPlugin(), createZodPlugin()],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// result.files -> [{ path: "types.ts", content: "..." }, { path: "base.ts", content: "..." }]
|
|
73
|
+
// result.diagnostics -> [] (empty = no warnings or errors)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This produces two files:
|
|
77
|
+
|
|
78
|
+
**types.ts** -- TypeScript type definitions:
|
|
79
|
+
```typescript
|
|
80
|
+
export interface Todo {
|
|
81
|
+
completed: boolean;
|
|
82
|
+
id: string;
|
|
83
|
+
title: string;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**base.ts** -- Zod schemas with type annotations:
|
|
88
|
+
```typescript
|
|
89
|
+
import { z } from "zod";
|
|
90
|
+
import type { Todo } from "./types";
|
|
91
|
+
|
|
92
|
+
export const TodoSchema: z.ZodType<Todo> = z.object({
|
|
93
|
+
completed: z.boolean(),
|
|
94
|
+
id: z.string(),
|
|
95
|
+
title: z.string(),
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> See [GUIDE.md](docs/GUIDE.md) for the full tutorial.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## API Reference
|
|
104
|
+
|
|
105
|
+
### Main Exports
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Entry point
|
|
109
|
+
function generate(options: GenerateOptions): Promise<GenerateResult>;
|
|
110
|
+
|
|
111
|
+
// Built-in plugins
|
|
112
|
+
function createTsPlugin(options?: TsPluginOptions): CodegenPlugin;
|
|
113
|
+
function createZodPlugin(options?: ZodPluginOptions): CodegenPlugin;
|
|
114
|
+
|
|
115
|
+
// Key types
|
|
116
|
+
type GenerateOptions = {
|
|
117
|
+
schema: DomainSchema;
|
|
118
|
+
outDir: string;
|
|
119
|
+
plugins: CodegenPlugin[];
|
|
120
|
+
sourceId?: string; // Embedded in @generated header
|
|
121
|
+
stamp?: boolean; // Add timestamp to header (breaks determinism)
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
type GenerateResult = {
|
|
125
|
+
files: Array<{ path: string; content: string }>;
|
|
126
|
+
artifacts: Record<string, unknown>;
|
|
127
|
+
diagnostics: Diagnostic[];
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
type CodegenPlugin = {
|
|
131
|
+
name: string;
|
|
132
|
+
generate(ctx: CodegenContext): CodegenOutput;
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
> See [SPEC-v0.1.1.md](docs/SPEC-v0.1.1.md) for complete API reference.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Core Concepts
|
|
141
|
+
|
|
142
|
+
### Plugin Pipeline
|
|
143
|
+
|
|
144
|
+
Plugins run in array order. Each plugin receives a context containing the schema and artifacts from all previous plugins. The TS plugin publishes type names; the Zod plugin reads them to generate type-annotated schemas.
|
|
145
|
+
|
|
146
|
+
### Artifacts
|
|
147
|
+
|
|
148
|
+
Plugins communicate through artifacts -- a namespaced key-value store. Plugin _i_ sees frozen artifacts from plugins 0..i-1. This enables cross-plugin coordination without coupling.
|
|
149
|
+
|
|
150
|
+
### Deterministic Output
|
|
151
|
+
|
|
152
|
+
Same DomainSchema always produces byte-identical output files. Fields and types are lexicographically sorted. No timestamps are included by default.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Relationship with Other Packages
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
@manifesto-ai/core -> CODEGEN -> Generated .ts files
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Relationship | Package | How |
|
|
163
|
+
|--------------|---------|-----|
|
|
164
|
+
| Depends on | `@manifesto-ai/core` | Reads DomainSchema, TypeDefinition, TypeSpec |
|
|
165
|
+
| Used by | App build scripts | Called during build to generate type-safe code |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## When to Use Codegen
|
|
170
|
+
|
|
171
|
+
Use Codegen when:
|
|
172
|
+
- You want type-safe TypeScript interfaces from your DomainSchema
|
|
173
|
+
- You want Zod runtime validators that match your schema types
|
|
174
|
+
- You need deterministic, reproducible code generation in CI
|
|
175
|
+
- You are building a custom plugin for additional output formats
|
|
176
|
+
|
|
177
|
+
For schema authoring, see [`@manifesto-ai/core`](../core/).
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Documentation
|
|
182
|
+
|
|
183
|
+
| Document | Purpose |
|
|
184
|
+
|----------|---------|
|
|
185
|
+
| [GUIDE.md](docs/GUIDE.md) | Step-by-step usage guide |
|
|
186
|
+
| [SPEC-v0.1.1.md](docs/SPEC-v0.1.1.md) | Complete specification |
|
|
187
|
+
| [ADR-CODEGEN-001.md](docs/ADR-CODEGEN-001.md) | Architecture decisions |
|
|
188
|
+
| [VERSION-INDEX.md](docs/VERSION-INDEX.md) | Version tracking |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
[MIT](../../LICENSE)
|
package/dist/header.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface HeaderOptions {
|
|
2
|
+
readonly sourceId?: string;
|
|
3
|
+
readonly schemaHash: string;
|
|
4
|
+
readonly stamp?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Generate the @generated file header (DET-2, DET-3, DET-4).
|
|
8
|
+
*
|
|
9
|
+
* Default mode: no timestamp (deterministic).
|
|
10
|
+
* With stamp=true: appends ISO 8601 timestamp line.
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateHeader(options: HeaderOptions): string;
|
|
13
|
+
//# sourceMappingURL=header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../src/header.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAY7D"}
|
package/dist/header.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate the @generated file header (DET-2, DET-3, DET-4).
|
|
3
|
+
*
|
|
4
|
+
* Default mode: no timestamp (deterministic).
|
|
5
|
+
* With stamp=true: appends ISO 8601 timestamp line.
|
|
6
|
+
*/
|
|
7
|
+
export function generateHeader(options) {
|
|
8
|
+
const source = options.sourceId ?? "unknown";
|
|
9
|
+
const lines = [
|
|
10
|
+
"// @generated by @manifesto-ai/codegen \u2014 DO NOT EDIT",
|
|
11
|
+
`// Source: ${source} | Schema hash: ${options.schemaHash}`,
|
|
12
|
+
];
|
|
13
|
+
if (options.stamp) {
|
|
14
|
+
lines.push(`// Generated at: ${new Date().toISOString()}`);
|
|
15
|
+
}
|
|
16
|
+
return lines.join("\n") + "\n";
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header.js","sourceRoot":"","sources":["../src/header.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAsB;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC;IAC7C,MAAM,KAAK,GAAG;QACZ,2DAA2D;QAC3D,cAAc,MAAM,mBAAmB,OAAO,CAAC,UAAU,EAAE;KAC5D,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,oBAAoB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type { Diagnostic, FilePatch, CodegenPlugin, CodegenContext, CodegenOutput, CodegenHelpers, GenerateOptions, GenerateResult, } from "./types.js";
|
|
2
|
+
export { generate } from "./runner.js";
|
|
3
|
+
export { createTsPlugin, createZodPlugin } from "./plugins/index.js";
|
|
4
|
+
export type { TsPluginOptions, TsPluginArtifacts, ZodPluginOptions } from "./plugins/index.js";
|
|
5
|
+
export { validatePath } from "./path-safety.js";
|
|
6
|
+
export type { PathValidationResult } from "./path-safety.js";
|
|
7
|
+
export { stableHash } from "./stable-hash.js";
|
|
8
|
+
export { generateHeader } from "./header.js";
|
|
9
|
+
export type { HeaderOptions } from "./header.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,YAAY,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG/F,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @manifesto-ai/codegen
|
|
2
|
+
// Plugin-based code generation from DomainSchema
|
|
3
|
+
//
|
|
4
|
+
// See: docs/SPEC-v0.1.1.md for specification
|
|
5
|
+
// See: docs/ADR-CODEGEN-001.md for architecture decisions
|
|
6
|
+
// Runner
|
|
7
|
+
export { generate } from "./runner.js";
|
|
8
|
+
// Plugins
|
|
9
|
+
export { createTsPlugin, createZodPlugin } from "./plugins/index.js";
|
|
10
|
+
// Utilities (for custom plugin authors)
|
|
11
|
+
export { validatePath } from "./path-safety.js";
|
|
12
|
+
export { stableHash } from "./stable-hash.js";
|
|
13
|
+
export { generateHeader } from "./header.js";
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wBAAwB;AACxB,iDAAiD;AACjD,EAAE;AACF,6CAA6C;AAC7C,0DAA0D;AAc1D,SAAS;AACT,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,UAAU;AACV,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrE,wCAAwC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type PathValidationResult = {
|
|
2
|
+
valid: true;
|
|
3
|
+
normalized: string;
|
|
4
|
+
} | {
|
|
5
|
+
valid: false;
|
|
6
|
+
reason: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Validate and normalize a file path per FP-1, FP-2, GEN-6.
|
|
10
|
+
*
|
|
11
|
+
* - MUST be a POSIX relative path
|
|
12
|
+
* - MUST NOT contain `..`, absolute prefixes, drive letters, or null bytes
|
|
13
|
+
* - Normalizes backslashes, multiple slashes, and leading `./`
|
|
14
|
+
*/
|
|
15
|
+
export declare function validatePath(path: string): PathValidationResult;
|
|
16
|
+
//# sourceMappingURL=path-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../src/path-safety.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAgD/D"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate and normalize a file path per FP-1, FP-2, GEN-6.
|
|
3
|
+
*
|
|
4
|
+
* - MUST be a POSIX relative path
|
|
5
|
+
* - MUST NOT contain `..`, absolute prefixes, drive letters, or null bytes
|
|
6
|
+
* - Normalizes backslashes, multiple slashes, and leading `./`
|
|
7
|
+
*/
|
|
8
|
+
export function validatePath(path) {
|
|
9
|
+
if (!path) {
|
|
10
|
+
return { valid: false, reason: "Path must not be empty" };
|
|
11
|
+
}
|
|
12
|
+
if (path.includes("\0")) {
|
|
13
|
+
return { valid: false, reason: "Path must not contain null bytes" };
|
|
14
|
+
}
|
|
15
|
+
// Normalize backslashes to forward slashes (GEN-6)
|
|
16
|
+
let normalized = path.replace(/\\/g, "/");
|
|
17
|
+
// Check for drive letters (e.g., C:/)
|
|
18
|
+
if (/^[a-zA-Z]:/.test(normalized)) {
|
|
19
|
+
return { valid: false, reason: "Path must not contain drive letters" };
|
|
20
|
+
}
|
|
21
|
+
// Check for absolute path
|
|
22
|
+
if (normalized.startsWith("/")) {
|
|
23
|
+
return { valid: false, reason: "Path must be relative, not absolute" };
|
|
24
|
+
}
|
|
25
|
+
// Collapse multiple slashes
|
|
26
|
+
normalized = normalized.replace(/\/+/g, "/");
|
|
27
|
+
// Remove leading ./
|
|
28
|
+
if (normalized.startsWith("./")) {
|
|
29
|
+
normalized = normalized.slice(2);
|
|
30
|
+
}
|
|
31
|
+
// Remove trailing slash
|
|
32
|
+
if (normalized.endsWith("/") && normalized.length > 1) {
|
|
33
|
+
normalized = normalized.slice(0, -1);
|
|
34
|
+
}
|
|
35
|
+
// Check for .. traversal (after normalization)
|
|
36
|
+
const segments = normalized.split("/");
|
|
37
|
+
for (const segment of segments) {
|
|
38
|
+
if (segment === "..") {
|
|
39
|
+
return { valid: false, reason: "Path must not contain '..' traversal" };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!normalized) {
|
|
43
|
+
return { valid: false, reason: "Path resolves to empty after normalization" };
|
|
44
|
+
}
|
|
45
|
+
return { valid: true, normalized };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=path-safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-safety.js","sourceRoot":"","sources":["../src/path-safety.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;IAC5D,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,EAAE,CAAC;IACtE,CAAC;IAED,mDAAmD;IACnD,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE1C,sCAAsC;IACtC,IAAI,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;IACzE,CAAC;IAED,0BAA0B;IAC1B,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;IACzE,CAAC;IAED,4BAA4B;IAC5B,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE7C,oBAAoB;IACpB,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,wBAAwB;IACxB,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,sCAAsC,EAAE,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;IAChF,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createTsPlugin } from "./ts-plugin.js";
|
|
2
|
+
export type { TsPluginOptions, TsPluginArtifacts } from "./ts-plugin.js";
|
|
3
|
+
export { createZodPlugin } from "./zod-plugin.js";
|
|
4
|
+
export type { ZodPluginOptions } from "./zod-plugin.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CodegenPlugin } from "../types.js";
|
|
2
|
+
export interface TsPluginOptions {
|
|
3
|
+
readonly typesFile?: string;
|
|
4
|
+
readonly actionsFile?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TsPluginArtifacts {
|
|
7
|
+
readonly typeNames: string[];
|
|
8
|
+
readonly typeImportPath: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function createTsPlugin(options?: TsPluginOptions): CodegenPlugin;
|
|
11
|
+
//# sourceMappingURL=ts-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/ts-plugin.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAGV,aAAa,EAEd,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,aAAa,CAmDvE"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
const PLUGIN_NAME = "codegen-plugin-ts";
|
|
2
|
+
export function createTsPlugin(options) {
|
|
3
|
+
const typesFile = options?.typesFile ?? "types.ts";
|
|
4
|
+
const actionsFile = options?.actionsFile ?? "actions.ts";
|
|
5
|
+
return {
|
|
6
|
+
name: PLUGIN_NAME,
|
|
7
|
+
generate(ctx) {
|
|
8
|
+
const diagnostics = [];
|
|
9
|
+
const typeNames = [];
|
|
10
|
+
// Generate types from schema.types (GEN-10, TS-6: lexicographic order)
|
|
11
|
+
const sortedTypeNames = Object.keys(ctx.schema.types).sort();
|
|
12
|
+
const typeDecls = [];
|
|
13
|
+
for (const name of sortedTypeNames) {
|
|
14
|
+
const spec = ctx.schema.types[name];
|
|
15
|
+
typeNames.push(name);
|
|
16
|
+
typeDecls.push(renderNamedType(name, spec, diagnostics));
|
|
17
|
+
}
|
|
18
|
+
const typesContent = typeDecls.join("\n\n") + "\n";
|
|
19
|
+
// Generate action input types
|
|
20
|
+
const sortedActionNames = Object.keys(ctx.schema.actions).sort();
|
|
21
|
+
const actionDecls = [];
|
|
22
|
+
for (const actionName of sortedActionNames) {
|
|
23
|
+
const spec = ctx.schema.actions[actionName];
|
|
24
|
+
if (spec.input) {
|
|
25
|
+
const typeName = pascalCase(actionName) + "Input";
|
|
26
|
+
actionDecls.push(renderActionInputType(typeName, spec, diagnostics));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const patches = [
|
|
30
|
+
{ op: "set", path: typesFile, content: typesContent },
|
|
31
|
+
];
|
|
32
|
+
if (actionDecls.length > 0) {
|
|
33
|
+
const actionsContent = actionDecls.join("\n\n") + "\n";
|
|
34
|
+
patches.push({ op: "set", path: actionsFile, content: actionsContent });
|
|
35
|
+
}
|
|
36
|
+
const artifacts = {
|
|
37
|
+
typeNames,
|
|
38
|
+
typeImportPath: `./${typesFile.replace(/\.ts$/, "")}`,
|
|
39
|
+
};
|
|
40
|
+
return { patches, artifacts: artifacts, diagnostics };
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// --- Type rendering ---
|
|
45
|
+
function renderNamedType(name, spec, diagnostics) {
|
|
46
|
+
const def = spec.definition;
|
|
47
|
+
// TS-3: top-level named object -> export interface
|
|
48
|
+
if (def.kind === "object") {
|
|
49
|
+
return renderInterface(name, def.fields, diagnostics);
|
|
50
|
+
}
|
|
51
|
+
// TS-3: all other named types -> export type
|
|
52
|
+
const tsType = mapTypeDefinition(def, diagnostics);
|
|
53
|
+
return `export type ${name} = ${tsType};`;
|
|
54
|
+
}
|
|
55
|
+
function renderInterface(name, fields, diagnostics) {
|
|
56
|
+
const sortedFields = Object.keys(fields).sort();
|
|
57
|
+
const lines = [];
|
|
58
|
+
for (const fieldName of sortedFields) {
|
|
59
|
+
const field = fields[fieldName];
|
|
60
|
+
const tsType = mapTypeDefinition(field.type, diagnostics);
|
|
61
|
+
// TS-5: optional -> ?
|
|
62
|
+
const opt = field.optional ? "?" : "";
|
|
63
|
+
lines.push(` ${fieldName}${opt}: ${tsType};`);
|
|
64
|
+
}
|
|
65
|
+
return `export interface ${name} {\n${lines.join("\n")}\n}`;
|
|
66
|
+
}
|
|
67
|
+
function mapTypeDefinition(def, diagnostics) {
|
|
68
|
+
switch (def.kind) {
|
|
69
|
+
case "primitive":
|
|
70
|
+
return mapPrimitive(def.type);
|
|
71
|
+
case "literal":
|
|
72
|
+
return renderLiteral(def.value);
|
|
73
|
+
case "array":
|
|
74
|
+
return `${wrapComplex(mapTypeDefinition(def.element, diagnostics), def.element)}[]`;
|
|
75
|
+
case "record":
|
|
76
|
+
return `Record<${mapTypeDefinition(def.key, diagnostics)}, ${mapTypeDefinition(def.value, diagnostics)}>`;
|
|
77
|
+
case "object": {
|
|
78
|
+
const sortedFields = Object.keys(def.fields).sort();
|
|
79
|
+
const parts = [];
|
|
80
|
+
for (const fieldName of sortedFields) {
|
|
81
|
+
const field = def.fields[fieldName];
|
|
82
|
+
const tsType = mapTypeDefinition(field.type, diagnostics);
|
|
83
|
+
const opt = field.optional ? "?" : "";
|
|
84
|
+
parts.push(`${fieldName}${opt}: ${tsType}`);
|
|
85
|
+
}
|
|
86
|
+
return `{ ${parts.join("; ")} }`;
|
|
87
|
+
}
|
|
88
|
+
case "union":
|
|
89
|
+
// TS-2: nullable uses T | null
|
|
90
|
+
return def.types.map((t) => mapTypeDefinition(t, diagnostics)).join(" | ");
|
|
91
|
+
case "ref":
|
|
92
|
+
return def.name;
|
|
93
|
+
default: {
|
|
94
|
+
// PLG-3, TS-1: unknown kind fallback
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
level: "warn",
|
|
97
|
+
plugin: PLUGIN_NAME,
|
|
98
|
+
message: `Unknown TypeDefinition kind: "${def.kind}". Emitting "unknown".`,
|
|
99
|
+
});
|
|
100
|
+
return "unknown";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function mapPrimitive(type) {
|
|
105
|
+
switch (type) {
|
|
106
|
+
case "string":
|
|
107
|
+
return "string";
|
|
108
|
+
case "number":
|
|
109
|
+
return "number";
|
|
110
|
+
case "boolean":
|
|
111
|
+
return "boolean";
|
|
112
|
+
case "null":
|
|
113
|
+
return "null";
|
|
114
|
+
default:
|
|
115
|
+
return "unknown";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function renderLiteral(value) {
|
|
119
|
+
if (typeof value === "string") {
|
|
120
|
+
return JSON.stringify(value);
|
|
121
|
+
}
|
|
122
|
+
return String(value);
|
|
123
|
+
}
|
|
124
|
+
/** Wrap union types in parens when used as array element */
|
|
125
|
+
function wrapComplex(tsType, def) {
|
|
126
|
+
if (def.kind === "union") {
|
|
127
|
+
return `(${tsType})`;
|
|
128
|
+
}
|
|
129
|
+
return tsType;
|
|
130
|
+
}
|
|
131
|
+
// --- Action input rendering ---
|
|
132
|
+
function renderActionInputType(typeName, spec, diagnostics) {
|
|
133
|
+
if (!spec.input) {
|
|
134
|
+
return "";
|
|
135
|
+
}
|
|
136
|
+
const tsType = mapFieldSpec(spec.input, diagnostics);
|
|
137
|
+
return `export type ${typeName} = ${tsType};`;
|
|
138
|
+
}
|
|
139
|
+
function mapFieldSpec(spec, diagnostics) {
|
|
140
|
+
const baseType = mapFieldType(spec.type, spec, diagnostics);
|
|
141
|
+
// GEN-12: degrade for unknown structures
|
|
142
|
+
if (!spec.required && baseType !== "unknown") {
|
|
143
|
+
return `${baseType} | null`;
|
|
144
|
+
}
|
|
145
|
+
return baseType;
|
|
146
|
+
}
|
|
147
|
+
function mapFieldType(type, spec, diagnostics) {
|
|
148
|
+
if (typeof type === "object" && "enum" in type) {
|
|
149
|
+
// Enum -> union of literals
|
|
150
|
+
return type.enum.map((v) => renderLiteral(v)).join(" | ");
|
|
151
|
+
}
|
|
152
|
+
switch (type) {
|
|
153
|
+
case "string":
|
|
154
|
+
return "string";
|
|
155
|
+
case "number":
|
|
156
|
+
return "number";
|
|
157
|
+
case "boolean":
|
|
158
|
+
return "boolean";
|
|
159
|
+
case "null":
|
|
160
|
+
return "null";
|
|
161
|
+
case "object": {
|
|
162
|
+
if (spec.fields) {
|
|
163
|
+
const sortedFields = Object.keys(spec.fields).sort();
|
|
164
|
+
const parts = [];
|
|
165
|
+
for (const name of sortedFields) {
|
|
166
|
+
const field = spec.fields[name];
|
|
167
|
+
const fieldType = mapFieldSpec(field, diagnostics);
|
|
168
|
+
const opt = field.required ? "" : "?";
|
|
169
|
+
parts.push(`${name}${opt}: ${fieldType}`);
|
|
170
|
+
}
|
|
171
|
+
return `{ ${parts.join("; ")} }`;
|
|
172
|
+
}
|
|
173
|
+
// GEN-12: unstructured object -> unknown
|
|
174
|
+
diagnostics.push({
|
|
175
|
+
level: "warn",
|
|
176
|
+
plugin: PLUGIN_NAME,
|
|
177
|
+
message: "Object field without fields spec, degrading to Record<string, unknown>",
|
|
178
|
+
});
|
|
179
|
+
return "Record<string, unknown>";
|
|
180
|
+
}
|
|
181
|
+
case "array": {
|
|
182
|
+
if (spec.items) {
|
|
183
|
+
return `${mapFieldSpec(spec.items, diagnostics)}[]`;
|
|
184
|
+
}
|
|
185
|
+
diagnostics.push({
|
|
186
|
+
level: "warn",
|
|
187
|
+
plugin: PLUGIN_NAME,
|
|
188
|
+
message: "Array field without items spec, degrading to unknown[]",
|
|
189
|
+
});
|
|
190
|
+
return "unknown[]";
|
|
191
|
+
}
|
|
192
|
+
default:
|
|
193
|
+
return "unknown";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function pascalCase(str) {
|
|
197
|
+
return str
|
|
198
|
+
.split(/[-_\s]+/)
|
|
199
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
200
|
+
.join("");
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=ts-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts-plugin.js","sourceRoot":"","sources":["../../src/plugins/ts-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/zod-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,aAAa,EAEd,MAAM,aAAa,CAAC;AAMrB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,CA2CzE"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const PLUGIN_NAME = "codegen-plugin-zod";
|
|
2
|
+
const TS_PLUGIN_NAME = "codegen-plugin-ts";
|
|
3
|
+
export function createZodPlugin(options) {
|
|
4
|
+
const schemasFile = options?.schemasFile ?? "base.ts";
|
|
5
|
+
return {
|
|
6
|
+
name: PLUGIN_NAME,
|
|
7
|
+
generate(ctx) {
|
|
8
|
+
const diagnostics = [];
|
|
9
|
+
// PLG-11: Optional TS artifacts dependency
|
|
10
|
+
const tsArtifacts = ctx.artifacts[TS_PLUGIN_NAME];
|
|
11
|
+
const sortedTypeNames = Object.keys(ctx.schema.types).sort();
|
|
12
|
+
const schemaDecls = [];
|
|
13
|
+
// Collect all type names for forward declarations with z.lazy
|
|
14
|
+
const allTypeNames = new Set(sortedTypeNames);
|
|
15
|
+
for (const name of sortedTypeNames) {
|
|
16
|
+
const spec = ctx.schema.types[name];
|
|
17
|
+
schemaDecls.push(renderNamedSchema(name, spec, allTypeNames, tsArtifacts, diagnostics));
|
|
18
|
+
}
|
|
19
|
+
// Build imports
|
|
20
|
+
const imports = ['import { z } from "zod";'];
|
|
21
|
+
// ZOD-4: Import TS types for annotations when available
|
|
22
|
+
if (tsArtifacts && tsArtifacts.typeNames.length > 0) {
|
|
23
|
+
const typeImports = sortedTypeNames
|
|
24
|
+
.filter((n) => tsArtifacts.typeNames.includes(n))
|
|
25
|
+
.join(", ");
|
|
26
|
+
if (typeImports) {
|
|
27
|
+
imports.push(`import type { ${typeImports} } from "${tsArtifacts.typeImportPath}";`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const content = imports.join("\n") + "\n\n" + schemaDecls.join("\n\n") + "\n";
|
|
31
|
+
return {
|
|
32
|
+
patches: [{ op: "set", path: schemasFile, content }],
|
|
33
|
+
diagnostics,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// --- Schema rendering ---
|
|
39
|
+
function renderNamedSchema(name, spec, allTypeNames, tsArtifacts, diagnostics) {
|
|
40
|
+
const schemaName = `${name}Schema`;
|
|
41
|
+
const zodExpr = mapTypeDefinition(spec.definition, allTypeNames, diagnostics);
|
|
42
|
+
// ZOD-4/ZOD-5: Type annotation when TS artifacts available
|
|
43
|
+
const hasTypeAnnotation = tsArtifacts && tsArtifacts.typeNames.includes(name);
|
|
44
|
+
const annotation = hasTypeAnnotation ? `: z.ZodType<${name}>` : "";
|
|
45
|
+
return `export const ${schemaName}${annotation} = ${zodExpr};`;
|
|
46
|
+
}
|
|
47
|
+
function mapTypeDefinition(def, allTypeNames, diagnostics) {
|
|
48
|
+
switch (def.kind) {
|
|
49
|
+
case "primitive":
|
|
50
|
+
return mapPrimitiveZod(def.type);
|
|
51
|
+
case "literal":
|
|
52
|
+
return `z.literal(${renderLiteralValue(def.value)})`;
|
|
53
|
+
case "array":
|
|
54
|
+
return `z.array(${mapTypeDefinition(def.element, allTypeNames, diagnostics)})`;
|
|
55
|
+
case "record":
|
|
56
|
+
return handleRecord(def, allTypeNames, diagnostics);
|
|
57
|
+
case "object":
|
|
58
|
+
return renderZodObject(def.fields, allTypeNames, diagnostics);
|
|
59
|
+
case "union":
|
|
60
|
+
return handleUnion(def.types, allTypeNames, diagnostics);
|
|
61
|
+
case "ref":
|
|
62
|
+
// ZOD-2: Always z.lazy for circular reference support
|
|
63
|
+
return `z.lazy(() => ${def.name}Schema)`;
|
|
64
|
+
default: {
|
|
65
|
+
// ZOD-1: Unknown kind fallback
|
|
66
|
+
diagnostics.push({
|
|
67
|
+
level: "warn",
|
|
68
|
+
plugin: PLUGIN_NAME,
|
|
69
|
+
message: `Unknown TypeDefinition kind: "${def.kind}". Emitting "z.unknown()".`,
|
|
70
|
+
});
|
|
71
|
+
return "z.unknown()";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function mapPrimitiveZod(type) {
|
|
76
|
+
switch (type) {
|
|
77
|
+
case "string":
|
|
78
|
+
return "z.string()";
|
|
79
|
+
case "number":
|
|
80
|
+
return "z.number()";
|
|
81
|
+
case "boolean":
|
|
82
|
+
return "z.boolean()";
|
|
83
|
+
case "null":
|
|
84
|
+
return "z.null()";
|
|
85
|
+
default:
|
|
86
|
+
return "z.unknown()";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function renderLiteralValue(value) {
|
|
90
|
+
if (typeof value === "string") {
|
|
91
|
+
return JSON.stringify(value);
|
|
92
|
+
}
|
|
93
|
+
if (value === null) {
|
|
94
|
+
return "null";
|
|
95
|
+
}
|
|
96
|
+
return String(value);
|
|
97
|
+
}
|
|
98
|
+
function handleRecord(def, allTypeNames, diagnostics) {
|
|
99
|
+
const valueSchema = mapTypeDefinition(def.value, allTypeNames, diagnostics);
|
|
100
|
+
// ZOD-7: Non-string record key -> degrade to z.record(z.string(), ...)
|
|
101
|
+
if (def.key.kind !== "primitive" || def.key.type !== "string") {
|
|
102
|
+
diagnostics.push({
|
|
103
|
+
level: "warn",
|
|
104
|
+
plugin: PLUGIN_NAME,
|
|
105
|
+
message: `Record key type is not string (got ${JSON.stringify(def.key)}). Degrading to z.record(z.string(), ...).`,
|
|
106
|
+
});
|
|
107
|
+
return `z.record(z.string(), ${valueSchema})`;
|
|
108
|
+
}
|
|
109
|
+
return `z.record(z.string(), ${valueSchema})`;
|
|
110
|
+
}
|
|
111
|
+
function renderZodObject(fields, allTypeNames, diagnostics) {
|
|
112
|
+
const sortedFields = Object.keys(fields).sort();
|
|
113
|
+
const parts = [];
|
|
114
|
+
for (const fieldName of sortedFields) {
|
|
115
|
+
const field = fields[fieldName];
|
|
116
|
+
let zodType = mapTypeDefinition(field.type, allTypeNames, diagnostics);
|
|
117
|
+
// ZOD-6: optional -> .optional()
|
|
118
|
+
if (field.optional) {
|
|
119
|
+
zodType += ".optional()";
|
|
120
|
+
}
|
|
121
|
+
parts.push(` ${fieldName}: ${zodType},`);
|
|
122
|
+
}
|
|
123
|
+
return `z.object({\n${parts.join("\n")}\n})`;
|
|
124
|
+
}
|
|
125
|
+
function handleUnion(types, allTypeNames, diagnostics) {
|
|
126
|
+
// ZOD-3: 2-variant union with null -> z.nullable(T)
|
|
127
|
+
if (types.length === 2) {
|
|
128
|
+
const nullIdx = types.findIndex((t) => t.kind === "primitive" && t.type === "null");
|
|
129
|
+
if (nullIdx !== -1) {
|
|
130
|
+
const otherIdx = nullIdx === 0 ? 1 : 0;
|
|
131
|
+
const otherSchema = mapTypeDefinition(types[otherIdx], allTypeNames, diagnostics);
|
|
132
|
+
return `z.nullable(${otherSchema})`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const schemas = types.map((t) => mapTypeDefinition(t, allTypeNames, diagnostics));
|
|
136
|
+
return `z.union([${schemas.join(", ")}])`;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=zod-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-plugin.js","sourceRoot":"","sources":["../../src/plugins/zod-plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GenerateOptions, GenerateResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate typed artifacts from a DomainSchema using plugins.
|
|
4
|
+
*
|
|
5
|
+
* This is the sole entry point for codegen. It orchestrates:
|
|
6
|
+
* - Plugin name uniqueness validation (GEN-2)
|
|
7
|
+
* - Sequential plugin execution (GEN-3, GEN-7)
|
|
8
|
+
* - FilePatch composition with collision detection
|
|
9
|
+
* - Error gating (GEN-5, GEN-8)
|
|
10
|
+
* - outDir clean + file flush (GEN-1)
|
|
11
|
+
*/
|
|
12
|
+
export declare function generate(opts: GenerateOptions): Promise<GenerateResult>;
|
|
13
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIV,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAqG7E"}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as nodePath from "node:path";
|
|
3
|
+
import { VirtualFS } from "./virtual-fs.js";
|
|
4
|
+
import { validatePath } from "./path-safety.js";
|
|
5
|
+
import { stableHash } from "./stable-hash.js";
|
|
6
|
+
import { generateHeader } from "./header.js";
|
|
7
|
+
/**
|
|
8
|
+
* Generate typed artifacts from a DomainSchema using plugins.
|
|
9
|
+
*
|
|
10
|
+
* This is the sole entry point for codegen. It orchestrates:
|
|
11
|
+
* - Plugin name uniqueness validation (GEN-2)
|
|
12
|
+
* - Sequential plugin execution (GEN-3, GEN-7)
|
|
13
|
+
* - FilePatch composition with collision detection
|
|
14
|
+
* - Error gating (GEN-5, GEN-8)
|
|
15
|
+
* - outDir clean + file flush (GEN-1)
|
|
16
|
+
*/
|
|
17
|
+
export async function generate(opts) {
|
|
18
|
+
const diagnostics = [];
|
|
19
|
+
const allArtifacts = {};
|
|
20
|
+
const vfs = new VirtualFS();
|
|
21
|
+
// GEN-2: Validate plugin name uniqueness
|
|
22
|
+
const nameError = validatePluginNames(opts.plugins);
|
|
23
|
+
if (nameError) {
|
|
24
|
+
return {
|
|
25
|
+
files: [],
|
|
26
|
+
artifacts: {},
|
|
27
|
+
diagnostics: [nameError],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// GEN-3, GEN-7: Execute plugins sequentially in array order
|
|
31
|
+
for (const plugin of opts.plugins) {
|
|
32
|
+
const ctx = {
|
|
33
|
+
schema: opts.schema,
|
|
34
|
+
sourceId: opts.sourceId,
|
|
35
|
+
outDir: opts.outDir,
|
|
36
|
+
artifacts: Object.freeze({ ...allArtifacts }), // PLG-9: frozen snapshot
|
|
37
|
+
helpers: { stableHash },
|
|
38
|
+
};
|
|
39
|
+
let output;
|
|
40
|
+
try {
|
|
41
|
+
output = await plugin.generate(ctx);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
// Convert plugin exceptions to error diagnostics (errors as values)
|
|
45
|
+
diagnostics.push({
|
|
46
|
+
level: "error",
|
|
47
|
+
plugin: plugin.name,
|
|
48
|
+
message: `Plugin threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// GEN-4: Merge plugin diagnostics
|
|
53
|
+
if (output.diagnostics) {
|
|
54
|
+
diagnostics.push(...output.diagnostics);
|
|
55
|
+
}
|
|
56
|
+
// Validate and apply patches
|
|
57
|
+
for (const patch of output.patches) {
|
|
58
|
+
const validation = validatePath(patch.path);
|
|
59
|
+
if (!validation.valid) {
|
|
60
|
+
diagnostics.push({
|
|
61
|
+
level: "error",
|
|
62
|
+
plugin: plugin.name,
|
|
63
|
+
message: `Invalid path "${patch.path}": ${validation.reason}`,
|
|
64
|
+
});
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Apply with normalized path
|
|
68
|
+
const normalizedPatch = patch.op === "set"
|
|
69
|
+
? { op: "set", path: validation.normalized, content: patch.content }
|
|
70
|
+
: { op: "delete", path: validation.normalized };
|
|
71
|
+
const collision = vfs.applyPatch(normalizedPatch, plugin.name);
|
|
72
|
+
if (collision) {
|
|
73
|
+
diagnostics.push(collision);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// PLG-8: Store artifacts at allArtifacts[plugin.name]
|
|
77
|
+
if (output.artifacts) {
|
|
78
|
+
allArtifacts[plugin.name] = output.artifacts;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Collect files from VFS
|
|
82
|
+
const files = vfs.getFiles();
|
|
83
|
+
// GEN-5, GEN-8: Error gate — no disk mutation on error
|
|
84
|
+
const hasErrors = diagnostics.some((d) => d.level === "error");
|
|
85
|
+
if (hasErrors) {
|
|
86
|
+
return { files, artifacts: allArtifacts, diagnostics };
|
|
87
|
+
}
|
|
88
|
+
// GEN-1: Clean outDir before write
|
|
89
|
+
await fs.rm(opts.outDir, { recursive: true, force: true });
|
|
90
|
+
// Build header
|
|
91
|
+
const header = generateHeader({
|
|
92
|
+
sourceId: opts.sourceId,
|
|
93
|
+
schemaHash: opts.schema.hash,
|
|
94
|
+
stamp: opts.stamp,
|
|
95
|
+
});
|
|
96
|
+
// Flush files to disk (GEN-6: OS path conversion only at write time)
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
const absPath = nodePath.join(opts.outDir, ...file.path.split("/"));
|
|
99
|
+
const dir = nodePath.dirname(absPath);
|
|
100
|
+
await fs.mkdir(dir, { recursive: true });
|
|
101
|
+
await fs.writeFile(absPath, header + file.content, "utf-8");
|
|
102
|
+
}
|
|
103
|
+
return { files, artifacts: allArtifacts, diagnostics };
|
|
104
|
+
}
|
|
105
|
+
function validatePluginNames(plugins) {
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
for (const plugin of plugins) {
|
|
108
|
+
if (!plugin.name) {
|
|
109
|
+
return {
|
|
110
|
+
level: "error",
|
|
111
|
+
plugin: "",
|
|
112
|
+
message: "Plugin name must not be empty (PLG-1)",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (seen.has(plugin.name)) {
|
|
116
|
+
return {
|
|
117
|
+
level: "error",
|
|
118
|
+
plugin: plugin.name,
|
|
119
|
+
message: `Duplicate plugin name "${plugin.name}" (GEN-2)`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
seen.add(plugin.name);
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic hash function (DET-1).
|
|
3
|
+
* Same input always produces the same output.
|
|
4
|
+
* Uses Core's canonical form (sorted keys, no undefined) + SHA-256.
|
|
5
|
+
*/
|
|
6
|
+
export declare function stableHash(input: unknown): string;
|
|
7
|
+
//# sourceMappingURL=stable-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-hash.d.ts","sourceRoot":"","sources":["../src/stable-hash.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { toCanonical, sha256Sync } from "@manifesto-ai/core";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic hash function (DET-1).
|
|
4
|
+
* Same input always produces the same output.
|
|
5
|
+
* Uses Core's canonical form (sorted keys, no undefined) + SHA-256.
|
|
6
|
+
*/
|
|
7
|
+
export function stableHash(input) {
|
|
8
|
+
const canonical = toCanonical(input);
|
|
9
|
+
return sha256Sync(canonical);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=stable-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-hash.js","sourceRoot":"","sources":["../src/stable-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE7D;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AAC/B,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DomainSchema } from "@manifesto-ai/core";
|
|
2
|
+
export type { DomainSchema };
|
|
3
|
+
export type Diagnostic = {
|
|
4
|
+
readonly level: "warn" | "error";
|
|
5
|
+
readonly plugin: string;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
};
|
|
8
|
+
export type FilePatch = {
|
|
9
|
+
readonly op: "set";
|
|
10
|
+
readonly path: string;
|
|
11
|
+
readonly content: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly op: "delete";
|
|
14
|
+
readonly path: string;
|
|
15
|
+
};
|
|
16
|
+
export interface CodegenHelpers {
|
|
17
|
+
stableHash(input: unknown): string;
|
|
18
|
+
}
|
|
19
|
+
export interface CodegenContext {
|
|
20
|
+
readonly schema: DomainSchema;
|
|
21
|
+
readonly sourceId?: string;
|
|
22
|
+
readonly outDir: string;
|
|
23
|
+
readonly artifacts: Readonly<Record<string, unknown>>;
|
|
24
|
+
readonly helpers: CodegenHelpers;
|
|
25
|
+
}
|
|
26
|
+
export interface CodegenOutput {
|
|
27
|
+
readonly patches: readonly FilePatch[];
|
|
28
|
+
readonly artifacts?: Readonly<Record<string, unknown>>;
|
|
29
|
+
readonly diagnostics?: readonly Diagnostic[];
|
|
30
|
+
}
|
|
31
|
+
export interface CodegenPlugin {
|
|
32
|
+
readonly name: string;
|
|
33
|
+
generate(ctx: CodegenContext): CodegenOutput | Promise<CodegenOutput>;
|
|
34
|
+
}
|
|
35
|
+
export interface GenerateOptions {
|
|
36
|
+
readonly schema: DomainSchema;
|
|
37
|
+
readonly outDir: string;
|
|
38
|
+
readonly plugins: readonly CodegenPlugin[];
|
|
39
|
+
readonly sourceId?: string;
|
|
40
|
+
readonly stamp?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface GenerateResult {
|
|
43
|
+
readonly files: ReadonlyArray<{
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly content: string;
|
|
46
|
+
}>;
|
|
47
|
+
readonly artifacts: Readonly<Record<string, unknown>>;
|
|
48
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;CAC7C"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Diagnostic, FilePatch } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Virtual filesystem for composing FilePatch operations.
|
|
4
|
+
*
|
|
5
|
+
* Maintains an in-memory FS and detects collisions per FP-5, FP-6, FP-7.
|
|
6
|
+
* Paths are assumed to be already validated/normalized.
|
|
7
|
+
*/
|
|
8
|
+
export declare class VirtualFS {
|
|
9
|
+
private readonly files;
|
|
10
|
+
private readonly deleted;
|
|
11
|
+
/**
|
|
12
|
+
* Apply a FilePatch to the virtual FS.
|
|
13
|
+
* Returns a Diagnostic if a collision/warning condition is detected.
|
|
14
|
+
*/
|
|
15
|
+
applyPatch(patch: FilePatch, pluginName: string): Diagnostic | undefined;
|
|
16
|
+
private applySet;
|
|
17
|
+
private applyDelete;
|
|
18
|
+
/**
|
|
19
|
+
* Get all files in deterministic (lexicographic) order (DET-5).
|
|
20
|
+
*/
|
|
21
|
+
getFiles(): Array<{
|
|
22
|
+
path: string;
|
|
23
|
+
content: string;
|
|
24
|
+
}>;
|
|
25
|
+
has(path: string): boolean;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=virtual-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-fs.d.ts","sourceRoot":"","sources":["../src/virtual-fs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAOxD;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAOxE,OAAO,CAAC,QAAQ;IAqBhB,OAAO,CAAC,WAAW;IA4BnB;;OAEG;IACH,QAAQ,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAMpD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG3B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Virtual filesystem for composing FilePatch operations.
|
|
3
|
+
*
|
|
4
|
+
* Maintains an in-memory FS and detects collisions per FP-5, FP-6, FP-7.
|
|
5
|
+
* Paths are assumed to be already validated/normalized.
|
|
6
|
+
*/
|
|
7
|
+
export class VirtualFS {
|
|
8
|
+
files = new Map();
|
|
9
|
+
deleted = new Set();
|
|
10
|
+
/**
|
|
11
|
+
* Apply a FilePatch to the virtual FS.
|
|
12
|
+
* Returns a Diagnostic if a collision/warning condition is detected.
|
|
13
|
+
*/
|
|
14
|
+
applyPatch(patch, pluginName) {
|
|
15
|
+
if (patch.op === "set") {
|
|
16
|
+
return this.applySet(patch.path, patch.content, pluginName);
|
|
17
|
+
}
|
|
18
|
+
return this.applyDelete(patch.path, pluginName);
|
|
19
|
+
}
|
|
20
|
+
applySet(path, content, pluginName) {
|
|
21
|
+
const existing = this.files.get(path);
|
|
22
|
+
if (existing) {
|
|
23
|
+
// FP-5: Duplicate set on same path -> error
|
|
24
|
+
const samePlugin = existing.source === pluginName;
|
|
25
|
+
return {
|
|
26
|
+
level: "error",
|
|
27
|
+
plugin: pluginName,
|
|
28
|
+
message: samePlugin
|
|
29
|
+
? `Duplicate set on "${path}" within plugin "${pluginName}"`
|
|
30
|
+
: `File "${path}" already set by plugin "${existing.source}", cannot be set again by "${pluginName}"`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// delete-then-set is allowed (intentional regeneration)
|
|
34
|
+
this.deleted.delete(path);
|
|
35
|
+
this.files.set(path, { content, source: pluginName });
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
applyDelete(path, pluginName) {
|
|
39
|
+
const existing = this.files.get(path);
|
|
40
|
+
if (!existing && !this.deleted.has(path)) {
|
|
41
|
+
// FP-7: Delete on nonexistent path -> warn
|
|
42
|
+
return {
|
|
43
|
+
level: "warn",
|
|
44
|
+
plugin: pluginName,
|
|
45
|
+
message: `Delete on nonexistent path "${path}"`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (existing) {
|
|
49
|
+
// FP-6: set-then-delete -> allowed with warning
|
|
50
|
+
this.files.delete(path);
|
|
51
|
+
this.deleted.add(path);
|
|
52
|
+
return {
|
|
53
|
+
level: "warn",
|
|
54
|
+
plugin: pluginName,
|
|
55
|
+
message: `File "${path}" set by plugin "${existing.source}" is being deleted by "${pluginName}". Prior work is voided.`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Already deleted - just track it
|
|
59
|
+
this.deleted.add(path);
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get all files in deterministic (lexicographic) order (DET-5).
|
|
64
|
+
*/
|
|
65
|
+
getFiles() {
|
|
66
|
+
const entries = Array.from(this.files.entries());
|
|
67
|
+
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
68
|
+
return entries.map(([path, { content }]) => ({ path, content }));
|
|
69
|
+
}
|
|
70
|
+
has(path) {
|
|
71
|
+
return this.files.has(path);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=virtual-fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-fs.js","sourceRoot":"","sources":["../src/virtual-fs.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@manifesto-ai/codegen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Manifesto Codegen - Plugin-based code generation from DomainSchema",
|
|
5
|
+
"author": "eggplantiny <eggplantiny@gmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/manifesto-ai/core.git",
|
|
10
|
+
"directory": "packages/codegen"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/manifesto-ai/core/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/manifesto-ai/core/tree/main/packages/codegen#readme",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"manifesto",
|
|
18
|
+
"codegen",
|
|
19
|
+
"schema",
|
|
20
|
+
"typescript",
|
|
21
|
+
"zod"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@manifesto-ai/core": "~2.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^25.2.0",
|
|
40
|
+
"typescript": "^5.9.3",
|
|
41
|
+
"vitest": "^4.0.18",
|
|
42
|
+
"@manifesto-ai/core": "2.4.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc",
|
|
46
|
+
"dev": "tsc --watch",
|
|
47
|
+
"clean": "rm -rf dist",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest"
|
|
50
|
+
}
|
|
51
|
+
}
|