osury 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # osury
2
+
3
+ Generate ReScript types with [Sury](https://github.com/DZakh/sury) schemas from OpenAPI specifications.
4
+
5
+ ## Features
6
+
7
+ - OpenAPI 3.x → ReScript types
8
+ - `@schema` annotations for Sury PPX validation
9
+ - `@genType` for TypeScript interop
10
+ - Union types extracted as proper variants with `@tag("_tag")`
11
+ - Automatic deduplication of identical union structures
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install -D osury
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### CLI
22
+
23
+ ```bash
24
+ # Generate to default ./Generated.res
25
+ npx osury openapi.json
26
+
27
+ # Generate to specific file
28
+ npx osury openapi.json src/API.res
29
+
30
+ # With explicit output flag
31
+ npx osury generate openapi.json -o src/Schema.res
32
+ ```
33
+
34
+ ### Output Example
35
+
36
+ Input:
37
+ ```json
38
+ {
39
+ "components": {
40
+ "schemas": {
41
+ "User": {
42
+ "type": "object",
43
+ "properties": {
44
+ "id": { "type": "integer" },
45
+ "name": { "type": "string" },
46
+ "role": {
47
+ "anyOf": [
48
+ { "$ref": "#/components/schemas/Admin" },
49
+ { "$ref": "#/components/schemas/Guest" }
50
+ ]
51
+ }
52
+ },
53
+ "required": ["id", "name"]
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ Output:
61
+ ```rescript
62
+ @genType
63
+ @tag("_tag")
64
+ @schema
65
+ type adminOrGuest = Admin(admin) | Guest(guest)
66
+
67
+ @genType
68
+ @schema
69
+ type user = {
70
+ id: int,
71
+ name: string,
72
+ role: option<adminOrGuest>
73
+ }
74
+ ```
75
+
76
+ ## Generated Annotations
77
+
78
+ | Annotation | Purpose |
79
+ |------------|---------|
80
+ | `@genType` | TypeScript type generation |
81
+ | `@schema` | Sury PPX validation schema |
82
+ | `@tag("_tag")` | Discriminated union support |
83
+
84
+ ## Requirements
85
+
86
+ For the generated code to compile, your project needs:
87
+
88
+ - [rescript](https://rescript-lang.org/) >= 12.0
89
+ - [sury](https://github.com/DZakh/sury) >= 11.0 (for `@schema`)
90
+ - [sury-ppx](https://github.com/DZakh/sury) >= 11.0 (for `@schema` PPX)
91
+ - [gentype](https://github.com/rescript-lang/gentype) (for `@genType`)
92
+
93
+ ## Type Mapping
94
+
95
+ | OpenAPI | ReScript |
96
+ |---------|----------|
97
+ | `string` | `string` |
98
+ | `number` | `float` |
99
+ | `integer` | `int` |
100
+ | `boolean` | `bool` |
101
+ | `array` | `array<T>` |
102
+ | `object` | `{ field: T }` |
103
+ | `$ref` | type reference |
104
+ | `anyOf` (nullable) | `option<T>` |
105
+ | `anyOf` (union) | variant type |
106
+ | `oneOf` (tagged) | poly variant |
107
+ | `additionalProperties` | `Dict.t<T>` |
108
+ | `default` value | field is required |
109
+
110
+ ## License
111
+
112
+ MIT
package/bin/osury.mjs ADDED
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as OpenAPIParser from "../src/OpenAPIParser.res.mjs";
4
+ import * as Codegen from "../src/Codegen.res.mjs";
5
+ import fs from "fs";
6
+ import path from "path";
7
+
8
+ const args = process.argv.slice(2);
9
+
10
+ function printHelp() {
11
+ console.log(`
12
+ osury - Generate ReScript types from OpenAPI schema
13
+
14
+ Usage:
15
+ osury <input.json> [output.res]
16
+ osury generate <input.json> -o <output.res>
17
+
18
+ Arguments:
19
+ input.json Path to OpenAPI/JSON Schema file
20
+ output.res Output ReScript file (default: ./Generated.res)
21
+
22
+ Options:
23
+ -o, --output Output file path
24
+ -h, --help Show this help
25
+
26
+ Examples:
27
+ osury openapi.json
28
+ osury openapi.json src/API.res
29
+ osury generate ./schema.json -o ./src/Schema.res
30
+ `);
31
+ }
32
+
33
+ function parseArgs(args) {
34
+ const options = {
35
+ input: null,
36
+ output: "./Generated.res",
37
+ };
38
+
39
+ let i = 0;
40
+ while (i < args.length) {
41
+ const arg = args[i];
42
+
43
+ if (arg === "-h" || arg === "--help") {
44
+ printHelp();
45
+ process.exit(0);
46
+ } else if (arg === "-o" || arg === "--output") {
47
+ options.output = args[++i];
48
+ } else if (arg === "generate") {
49
+ // Skip 'generate' command word
50
+ } else if (!options.input) {
51
+ options.input = arg;
52
+ } else if (options.output === "./Generated.res") {
53
+ options.output = arg;
54
+ }
55
+ i++;
56
+ }
57
+
58
+ return options;
59
+ }
60
+
61
+ function generate(inputPath, outputPath) {
62
+ // Read input file
63
+ if (!fs.existsSync(inputPath)) {
64
+ console.error(`Error: Input file not found: ${inputPath}`);
65
+ process.exit(1);
66
+ }
67
+
68
+ const doc = JSON.parse(fs.readFileSync(inputPath, "utf8"));
69
+
70
+ // Parse and generate
71
+ const result = OpenAPIParser.parseDocument(doc);
72
+
73
+ if (result.TAG === "Ok") {
74
+ const code = Codegen.generateModule(result._0);
75
+
76
+ // Ensure output directory exists
77
+ const outputDir = path.dirname(outputPath);
78
+ if (outputDir && !fs.existsSync(outputDir)) {
79
+ fs.mkdirSync(outputDir, { recursive: true });
80
+ }
81
+
82
+ fs.writeFileSync(outputPath, code);
83
+ console.log(`Generated ${result._0.length} types to ${outputPath}`);
84
+ } else {
85
+ console.error("Parse errors:");
86
+ result._0.forEach((err) => {
87
+ const location = err.location?.path?.join(".") || "root";
88
+ console.error(` [${location}] ${err.kind.TAG}: ${err.kind._0 || ""}`);
89
+ });
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ // Main
95
+ const options = parseArgs(args);
96
+
97
+ if (!options.input) {
98
+ console.error("Error: Input file required\n");
99
+ printHelp();
100
+ process.exit(1);
101
+ }
102
+
103
+ generate(options.input, options.output);
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "osury",
3
+ "type": "module",
4
+ "description": "Generate ReScript types with Sury schemas from OpenAPI specifications",
5
+ "version": "0.2.0",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "osury": "./bin/osury.mjs"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "src/Codegen.res.mjs",
13
+ "src/Errors.res.mjs",
14
+ "src/OpenAPIParser.res.mjs",
15
+ "src/Schema.res.mjs",
16
+ "src/Schema.gen.tsx",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "res:clean": "rescript clean",
21
+ "res:build": "rescript",
22
+ "res:dev": "rescript watch",
23
+ "codegen": "node scripts/codegen.mjs",
24
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
25
+ "prepublishOnly": "npm run res:build && npm test"
26
+ },
27
+ "devDependencies": {
28
+ "@rescript/core": "^1.6.1",
29
+ "gentype": "^4.5.0",
30
+ "jest": "^30.2.0",
31
+ "rescript": "^12.1.0",
32
+ "sury": "^11.0.0-alpha.4",
33
+ "sury-ppx": "^11.0.0-alpha.2"
34
+ },
35
+ "peerDependencies": {
36
+ "sury": ">=11.0.0-alpha.4",
37
+ "sury-ppx": ">=11.0.0-alpha.2"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "sury": {
41
+ "optional": true
42
+ },
43
+ "sury-ppx": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "keywords": [
48
+ "openapi",
49
+ "rescript",
50
+ "codegen",
51
+ "sury",
52
+ "schema",
53
+ "typescript",
54
+ "types"
55
+ ],
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "https://github.com/anthropic/osury"
59
+ }
60
+ }