ajsc 5.2.3 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -145
- package/dist/converter/BaseConverter.d.ts +283 -0
- package/dist/converter/BaseConverter.js +131 -0
- package/dist/converter/BaseConverter.js.map +1 -0
- package/dist/converter/Emitter.d.ts +35 -0
- package/dist/converter/Emitter.js +50 -0
- package/dist/converter/Emitter.js.map +1 -0
- package/dist/converter/discriminatedUnions.d.ts +47 -0
- package/dist/converter/discriminatedUnions.js +168 -0
- package/dist/converter/discriminatedUnions.js.map +1 -0
- package/dist/converter/formatDefault.d.ts +20 -0
- package/dist/converter/formatDefault.js +31 -0
- package/dist/converter/formatDefault.js.map +1 -0
- package/dist/converter/index.d.ts +2 -0
- package/dist/converter/index.js +2 -0
- package/dist/converter/index.js.map +1 -0
- package/dist/converter/mergeUnions.d.ts +36 -0
- package/dist/converter/mergeUnions.js +189 -0
- package/dist/converter/mergeUnions.js.map +1 -0
- package/dist/converter/naming.d.ts +29 -0
- package/dist/converter/naming.js +130 -0
- package/dist/converter/naming.js.map +1 -0
- package/dist/converter/registry.d.ts +18 -0
- package/dist/converter/registry.js +50 -0
- package/dist/converter/registry.js.map +1 -0
- package/dist/converter/walk.d.ts +9 -0
- package/dist/converter/walk.js +40 -0
- package/dist/converter/walk.js.map +1 -0
- package/dist/index.d.ts +71 -3
- package/dist/index.js +63 -3
- package/dist/index.js.map +1 -1
- package/dist/{JSONSchemaConverter.d.ts → ir/JSONSchemaConverter.d.ts} +1 -1
- package/dist/{JSONSchemaConverter.js → ir/JSONSchemaConverter.js} +9 -3
- package/dist/ir/JSONSchemaConverter.js.map +1 -0
- package/dist/ir/index.d.ts +1 -0
- package/dist/ir/index.js +2 -0
- package/dist/ir/index.js.map +1 -0
- package/dist/kotlin/KotlinBaseConverter.d.ts +18 -0
- package/dist/kotlin/KotlinBaseConverter.js +36 -0
- package/dist/kotlin/KotlinBaseConverter.js.map +1 -0
- package/dist/kotlin/KotlinConverter.d.ts +67 -0
- package/dist/kotlin/KotlinConverter.js +140 -0
- package/dist/kotlin/KotlinConverter.js.map +1 -0
- package/dist/kotlin/annotations.d.ts +26 -0
- package/dist/kotlin/annotations.js +35 -0
- package/dist/kotlin/annotations.js.map +1 -0
- package/dist/kotlin/enums.d.ts +15 -0
- package/dist/kotlin/enums.js +58 -0
- package/dist/kotlin/enums.js.map +1 -0
- package/dist/kotlin/index.d.ts +2 -0
- package/dist/kotlin/index.js +3 -0
- package/dist/kotlin/index.js.map +1 -0
- package/dist/kotlin/objectEmitter.d.ts +12 -0
- package/dist/kotlin/objectEmitter.js +74 -0
- package/dist/kotlin/objectEmitter.js.map +1 -0
- package/dist/kotlin/sealedUnion.d.ts +17 -0
- package/dist/kotlin/sealedUnion.js +74 -0
- package/dist/kotlin/sealedUnion.js.map +1 -0
- package/dist/kotlin/typeMapper.d.ts +17 -0
- package/dist/kotlin/typeMapper.js +107 -0
- package/dist/kotlin/typeMapper.js.map +1 -0
- package/dist/kotlin/unsupported.d.ts +13 -0
- package/dist/kotlin/unsupported.js +53 -0
- package/dist/kotlin/unsupported.js.map +1 -0
- package/dist/swift/SwiftBaseConverter.d.ts +18 -0
- package/dist/swift/SwiftBaseConverter.js +38 -0
- package/dist/swift/SwiftBaseConverter.js.map +1 -0
- package/dist/swift/SwiftConverter.d.ts +60 -0
- package/dist/swift/SwiftConverter.js +111 -0
- package/dist/swift/SwiftConverter.js.map +1 -0
- package/dist/swift/discriminatedEnum.d.ts +18 -0
- package/dist/swift/discriminatedEnum.js +99 -0
- package/dist/swift/discriminatedEnum.js.map +1 -0
- package/dist/swift/enums.d.ts +15 -0
- package/dist/swift/enums.js +62 -0
- package/dist/swift/enums.js.map +1 -0
- package/dist/swift/index.d.ts +2 -0
- package/dist/swift/index.js +3 -0
- package/dist/swift/index.js.map +1 -0
- package/dist/swift/structEmitter.d.ts +12 -0
- package/dist/swift/structEmitter.js +70 -0
- package/dist/swift/structEmitter.js.map +1 -0
- package/dist/swift/typeMapper.d.ts +18 -0
- package/dist/swift/typeMapper.js +106 -0
- package/dist/swift/typeMapper.js.map +1 -0
- package/dist/swift/unsupported.d.ts +19 -0
- package/dist/swift/unsupported.js +88 -0
- package/dist/swift/unsupported.js.map +1 -0
- package/dist/typescript/TypescriptBaseConverter.d.ts +25 -0
- package/dist/typescript/TypescriptBaseConverter.js +178 -0
- package/dist/typescript/TypescriptBaseConverter.js.map +1 -0
- package/dist/typescript/TypescriptConverter.d.ts +74 -0
- package/dist/typescript/TypescriptConverter.js +252 -0
- package/dist/typescript/TypescriptConverter.js.map +1 -0
- package/dist/typescript/index.d.ts +2 -0
- package/dist/typescript/index.js +3 -0
- package/dist/typescript/index.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +37 -5
- package/dist/JSONSchemaConverter.js.map +0 -1
- package/dist/JSONSchemaConverter.test.d.ts +0 -1
- package/dist/JSONSchemaConverter.test.js +0 -585
- package/dist/JSONSchemaConverter.test.js.map +0 -1
- package/dist/Typebox.test.d.ts +0 -1
- package/dist/Typebox.test.js +0 -88
- package/dist/Typebox.test.js.map +0 -1
- package/dist/TypescriptBaseConverter.d.ts +0 -75
- package/dist/TypescriptBaseConverter.js +0 -321
- package/dist/TypescriptBaseConverter.js.map +0 -1
- package/dist/TypescriptConverter.additionalProperties.test.d.ts +0 -1
- package/dist/TypescriptConverter.additionalProperties.test.js +0 -110
- package/dist/TypescriptConverter.additionalProperties.test.js.map +0 -1
- package/dist/TypescriptConverter.arrays.test.d.ts +0 -1
- package/dist/TypescriptConverter.arrays.test.js +0 -130
- package/dist/TypescriptConverter.arrays.test.js.map +0 -1
- package/dist/TypescriptConverter.composites.advanced.test.d.ts +0 -1
- package/dist/TypescriptConverter.composites.advanced.test.js +0 -924
- package/dist/TypescriptConverter.composites.advanced.test.js.map +0 -1
- package/dist/TypescriptConverter.composites.test.d.ts +0 -1
- package/dist/TypescriptConverter.composites.test.js +0 -335
- package/dist/TypescriptConverter.composites.test.js.map +0 -1
- package/dist/TypescriptConverter.d.ts +0 -163
- package/dist/TypescriptConverter.js +0 -595
- package/dist/TypescriptConverter.js.map +0 -1
- package/dist/TypescriptConverter.jsdoc.test.d.ts +0 -1
- package/dist/TypescriptConverter.jsdoc.test.js +0 -194
- package/dist/TypescriptConverter.jsdoc.test.js.map +0 -1
- package/dist/TypescriptConverter.objects.test.d.ts +0 -1
- package/dist/TypescriptConverter.objects.test.js +0 -258
- package/dist/TypescriptConverter.objects.test.js.map +0 -1
- package/dist/TypescriptConverter.options.test.d.ts +0 -1
- package/dist/TypescriptConverter.options.test.js +0 -501
- package/dist/TypescriptConverter.options.test.js.map +0 -1
- package/dist/TypescriptConverter.primitives.test.d.ts +0 -1
- package/dist/TypescriptConverter.primitives.test.js +0 -26
- package/dist/TypescriptConverter.primitives.test.js.map +0 -1
- package/dist/utils/path-utils.test.d.ts +0 -1
- package/dist/utils/path-utils.test.js +0 -92
- package/dist/utils/path-utils.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,198 +1,306 @@
|
|
|
1
|
-
# ajsc
|
|
1
|
+
# ajsc — Another JSON Schema Converter
|
|
2
2
|
|
|
3
|
-
**ajsc**
|
|
3
|
+
**ajsc** transforms JSON Schema documents into idiomatic, language-native code for **TypeScript**, **Kotlin**, and **Swift**. It exposes a small function-style API for typical codegen pipelines, plus class-based converters for advanced extension.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npm install ajsc
|
|
7
|
+
```
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
```ts
|
|
10
|
+
import { emitTypescript, emitKotlin, emitSwift } from "ajsc";
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
const schema = {
|
|
13
|
+
title: "User",
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
id: { type: "integer" },
|
|
17
|
+
name: { type: "string" },
|
|
18
|
+
email: { type: "string", format: "email" },
|
|
19
|
+
},
|
|
20
|
+
required: ["id", "name"],
|
|
21
|
+
};
|
|
11
22
|
|
|
12
|
-
|
|
13
|
-
|
|
23
|
+
const ts = emitTypescript(schema);
|
|
24
|
+
const kt = emitKotlin(schema);
|
|
25
|
+
const sw = emitSwift(schema);
|
|
26
|
+
```
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
Use the provided language plugin (currently, a [TypeScript converter](#typescriptconverter)) or write your own converter to support additional languages such as Kotlin or Swift.
|
|
28
|
+
Each emit function returns the same shape:
|
|
17
29
|
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
```ts
|
|
31
|
+
interface EmitResult {
|
|
32
|
+
code: string; // declarations only — no `import` lines
|
|
33
|
+
rootTypeName: string; // top-level emitted type ("User")
|
|
34
|
+
extractedTypeNames: string[]; // additional types emitted (nested objects, enums, variants)
|
|
35
|
+
imports: string[]; // language-native module/symbol paths to import
|
|
36
|
+
}
|
|
37
|
+
```
|
|
20
38
|
|
|
21
|
-
|
|
22
|
-
For objects and arrays, the library tracks unique signatures to avoid duplicate type definitions when converting to code.
|
|
39
|
+
`code` contains type declarations only. Consumers assemble the final source file by combining `imports` with `code`.
|
|
23
40
|
|
|
24
41
|
---
|
|
25
42
|
|
|
26
|
-
##
|
|
43
|
+
## Output examples
|
|
27
44
|
|
|
28
|
-
|
|
45
|
+
### TypeScript
|
|
29
46
|
|
|
30
|
-
```
|
|
31
|
-
|
|
47
|
+
```ts
|
|
48
|
+
import { emitTypescript } from "ajsc";
|
|
49
|
+
|
|
50
|
+
const { code } = emitTypescript({
|
|
51
|
+
title: "User", type: "object",
|
|
52
|
+
properties: { id: { type: "integer" }, name: { type: "string" } },
|
|
53
|
+
required: ["id", "name"],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// code:
|
|
57
|
+
// export type User = { id: number; name: string; };
|
|
32
58
|
```
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
### Kotlin (default: kotlinx-serialization)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { emitKotlin } from "ajsc";
|
|
64
|
+
|
|
65
|
+
const { code, imports } = emitKotlin({
|
|
66
|
+
title: "User", type: "object",
|
|
67
|
+
properties: { id: { type: "integer" }, name: { type: "string" } },
|
|
68
|
+
required: ["id", "name"],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// imports: ["kotlinx.serialization.Serializable"]
|
|
72
|
+
// code:
|
|
73
|
+
// @Serializable
|
|
74
|
+
// data class User(
|
|
75
|
+
// val id: Long,
|
|
76
|
+
// val name: String,
|
|
77
|
+
// )
|
|
78
|
+
```
|
|
35
79
|
|
|
36
|
-
|
|
80
|
+
Pass `serializer: "none"` for plain types with no annotations.
|
|
37
81
|
|
|
38
|
-
|
|
82
|
+
### Swift (default: Codable)
|
|
39
83
|
|
|
40
|
-
```
|
|
41
|
-
import {
|
|
84
|
+
```ts
|
|
85
|
+
import { emitSwift } from "ajsc";
|
|
42
86
|
|
|
43
|
-
const
|
|
44
|
-
|
|
87
|
+
const { code, imports } = emitSwift({
|
|
88
|
+
title: "User", type: "object",
|
|
89
|
+
properties: { id: { type: "integer" }, name: { type: "string" } },
|
|
90
|
+
required: ["id", "name"],
|
|
91
|
+
});
|
|
45
92
|
|
|
46
|
-
|
|
47
|
-
//
|
|
48
|
-
// {
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// path: "",
|
|
93
|
+
// imports: []
|
|
94
|
+
// code:
|
|
95
|
+
// public struct User: Codable {
|
|
96
|
+
// public let id: Int64
|
|
97
|
+
// public let name: String
|
|
52
98
|
// }
|
|
53
99
|
```
|
|
54
100
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```javascript
|
|
59
|
-
import { JSONSchemaConverter } from "ajsc";
|
|
60
|
-
|
|
61
|
-
const complexSchema = {
|
|
62
|
-
$defs: {
|
|
63
|
-
Person: {
|
|
64
|
-
type: "object",
|
|
65
|
-
properties: {
|
|
66
|
-
name: { type: "string" },
|
|
67
|
-
age: { type: "number" },
|
|
68
|
-
},
|
|
69
|
-
required: ["name"],
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
type: "object",
|
|
73
|
-
properties: {
|
|
74
|
-
person: { $ref: "#/$defs/Person" },
|
|
75
|
-
},
|
|
76
|
-
};
|
|
101
|
+
Pass `serializer: "none"` to drop `Codable` conformance.
|
|
102
|
+
|
|
103
|
+
### Assembling a final file
|
|
77
104
|
|
|
78
|
-
|
|
79
|
-
|
|
105
|
+
```ts
|
|
106
|
+
const { code, imports } = emitKotlin(schema);
|
|
107
|
+
const file = [...imports.map((i) => `import ${i}`), "", code].join("\n");
|
|
80
108
|
```
|
|
81
109
|
|
|
82
|
-
|
|
110
|
+
Swift `imports` are module names (`["Foundation"]`); Kotlin `imports` are fully-qualified symbol paths. TypeScript `imports` is always empty.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Package layout
|
|
115
|
+
|
|
116
|
+
| Subpath | Exports |
|
|
117
|
+
|------------------|--------------------------------------------------------------------------------------------------------|
|
|
118
|
+
| `ajsc` | `emitTypescript`, `emitKotlin`, `emitSwift`, `EmitResult`, plus the converter classes (re-exported) |
|
|
119
|
+
| `ajsc/typescript`| `TypescriptConverter`, `TypescriptBaseConverter`, related types |
|
|
120
|
+
| `ajsc/kotlin` | `KotlinConverter`, `KotlinBaseConverter`, `sanitizeKotlinIdentifier`, `KOTLIN_RESERVED` |
|
|
121
|
+
| `ajsc/swift` | `SwiftConverter`, `SwiftBaseConverter`, `sanitizeSwiftIdentifier`, `SWIFT_RESERVED` |
|
|
122
|
+
| `ajsc/converter` | `BaseConverter`, `Emitter`, `walkIR`, `LanguageProfile`, `BaseConverterOpts`, `RefTypeEntry`, etc. |
|
|
123
|
+
| `ajsc/ir` | `JSONSchemaConverter` (JSON Schema → IRNode tree) |
|
|
124
|
+
| `ajsc/types` | `IRNode`, `ILanguageConverter`, signature types |
|
|
125
|
+
| `ajsc/utils` | `PathUtils`, `toPascalCase` |
|
|
126
|
+
|
|
127
|
+
The root (`ajsc`) is the typical entry point. Subpaths are for advanced use — extending a converter, accessing the IR layer, building a custom language target.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Function-style vs class-style
|
|
83
132
|
|
|
84
|
-
|
|
133
|
+
```ts
|
|
134
|
+
// Function-style — recommended for most consumers.
|
|
135
|
+
import { emitKotlin } from "ajsc";
|
|
136
|
+
const result = emitKotlin(schema, opts);
|
|
85
137
|
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- Primitive Types: string, number, boolean, null
|
|
92
|
-
- Literals: Using the const keyword.
|
|
93
|
-
- Enums: Using the enum property.
|
|
94
|
-
- Unions: When type is an array (e.g., ["string", "number"]).
|
|
95
|
-
- Intersections: Using allOf to combine multiple schemas.
|
|
96
|
-
- Arrays: With type: "array" and an items schema.
|
|
97
|
-
- Objects: With type: "object" and a properties definition.
|
|
98
|
-
- $defs and $ref: For defining and referencing reusable schema parts.
|
|
138
|
+
// Class-style — for subclassing or accessing converter internals.
|
|
139
|
+
import { KotlinConverter } from "ajsc/kotlin";
|
|
140
|
+
const converter = new KotlinConverter(schema, opts);
|
|
141
|
+
console.log(converter.code, converter.imports);
|
|
142
|
+
```
|
|
99
143
|
|
|
100
|
-
|
|
144
|
+
Both produce identical output; the function form just bundles `code`, `rootTypeName`, `extractedTypeNames`, `imports` into a single return value. For codegen pipelines, the function form is usually nicer — it avoids the `new` and gives a stable result shape.
|
|
101
145
|
|
|
102
|
-
|
|
146
|
+
---
|
|
103
147
|
|
|
104
|
-
|
|
105
|
-
`new TypescriptConverter(schema: object, options?: TypescriptConverterOpts)`
|
|
106
|
-
- Properties:
|
|
107
|
-
- code: A string containing the generated TypeScript code.
|
|
148
|
+
## Options
|
|
108
149
|
|
|
109
|
-
|
|
150
|
+
All three converters accept `BaseConverterOpts` plus language-specific options.
|
|
151
|
+
|
|
152
|
+
### Shared options (`BaseConverterOpts`)
|
|
110
153
|
|
|
111
154
|
| Option | Type | Default | Description |
|
|
112
155
|
|--------|------|---------|-------------|
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
156
|
+
| `rootTypeName` | `string` | `schema.title \|\| "Root"` | Override the top-level type name. |
|
|
157
|
+
| `arrayItemNaming` | `string \| false` | `false` | Postfix for array-item type names. `false` = property name, `"Item"` = `ContactsItem`. |
|
|
158
|
+
| `depluralize` | `boolean` | `true` | Singularize array-item path segments (`entries` → `Entry`). |
|
|
159
|
+
| `uncountableWords` | `string[]` | `[]` | Words to skip when singularizing. Built-ins: `data`, `metadata`. |
|
|
160
|
+
| `unsupportedUnions` | `"throw" \| "fallback"` | `"throw"` | What to do with untagged unions: throw with a path-bearing error, or emit a language fallback type (`JsonElement` / `AnyCodable` / `any`). |
|
|
118
161
|
|
|
119
|
-
|
|
162
|
+
### TypeScript (`TypescriptConverterOpts`)
|
|
120
163
|
|
|
121
|
-
|
|
164
|
+
| Option | Type | Default | Description |
|
|
165
|
+
|--------|------|---------|-------------|
|
|
166
|
+
| `inlineTypes` | `boolean` | `false` | If true, nested object types are inlined rather than extracted to named aliases. |
|
|
167
|
+
| `enumStyle` | `"union" \| "enum"` | `"union"` | `"union"` emits `"a" \| "b" \| "c"`; `"enum"` emits `export enum Status { ... }`. |
|
|
168
|
+
| `jsdoc` | `boolean` | `false` | Emit JSDoc comments from JSON Schema `description` and `title`. Requires `inlineTypes: false`. |
|
|
122
169
|
|
|
123
|
-
|
|
124
|
-
import { TypescriptConverter } from "ajsc";
|
|
170
|
+
### Kotlin (`KotlinConverterOpts`)
|
|
125
171
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
age: { type: "number" },
|
|
131
|
-
contacts: {
|
|
132
|
-
type: "array",
|
|
133
|
-
items: {
|
|
134
|
-
type: "object",
|
|
135
|
-
properties: {
|
|
136
|
-
email: { type: "string" },
|
|
137
|
-
},
|
|
138
|
-
required: ["email"],
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
profile: {
|
|
142
|
-
type: "object",
|
|
143
|
-
properties: {
|
|
144
|
-
email: { type: "string" },
|
|
145
|
-
},
|
|
146
|
-
required: ["email"],
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
required: ["name", "age"],
|
|
150
|
-
};
|
|
172
|
+
| Option | Type | Default | Description |
|
|
173
|
+
|--------|------|---------|-------------|
|
|
174
|
+
| `serializer` | `"kotlinx" \| "none"` | `"kotlinx"` | Emit `@Serializable`/`@SerialName`/`@Contextual` annotations and matching imports. `"none"` emits plain types. |
|
|
175
|
+
| `packageName` | `string` | `undefined` | If set, emit `package <name>` at the top of the output. |
|
|
151
176
|
|
|
152
|
-
|
|
153
|
-
console.log(tsConverter.code);
|
|
177
|
+
### Swift (`SwiftConverterOpts`)
|
|
154
178
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
179
|
+
| Option | Type | Default | Description |
|
|
180
|
+
|--------|------|---------|-------------|
|
|
181
|
+
| `serializer` | `"codable" \| "none"` | `"codable"` | Emit `: Codable` conformance, `CodingKeys` enums, and discriminated-union `init(from:)`/`encode(to:)` plumbing. `"none"` emits plain types. |
|
|
182
|
+
| `accessLevel` | `"public" \| "internal"` | `"public"` | Access modifier on emitted types and members. |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Type mapping
|
|
187
|
+
|
|
188
|
+
### TypeScript
|
|
189
|
+
|
|
190
|
+
JSON Schema → idiomatic TS. `string` → `string`, `integer` → `number`, etc. Discriminated `oneOf`/`anyOf` → tagged union types. `$ref: "#"` → direct self-reference.
|
|
191
|
+
|
|
192
|
+
### Kotlin (target: JVM)
|
|
193
|
+
|
|
194
|
+
| JSON Schema | Kotlin | Notes |
|
|
195
|
+
|-------------|--------|-------|
|
|
196
|
+
| `string` | `String` | |
|
|
197
|
+
| `string, format: date-time` | `java.time.Instant` | `@Contextual` under kotlinx |
|
|
198
|
+
| `string, format: date` / `time` | `java.time.LocalDate` / `LocalTime` | `@Contextual` under kotlinx |
|
|
199
|
+
| `string, format: uuid` | `java.util.UUID` | `@Contextual` under kotlinx |
|
|
200
|
+
| `string, format: uri` | `java.net.URI` | `@Contextual` under kotlinx |
|
|
201
|
+
| `string, format: email` / `hostname` / `ipv4` / `ipv6` | `String` + KDoc note | |
|
|
202
|
+
| `integer` | `Long` | `format: int32` → `Int` |
|
|
203
|
+
| `number` | `Double` | |
|
|
204
|
+
| `boolean` | `Boolean` | |
|
|
205
|
+
| `array` | `List<T>` | |
|
|
206
|
+
| `object` | `data class` | `@Serializable` under kotlinx |
|
|
207
|
+
| `enum` (string) | `enum class` | `@Serializable` under kotlinx |
|
|
208
|
+
| `oneOf` w/ discriminator | `sealed interface` | `@JsonClassDiscriminator` under kotlinx |
|
|
209
|
+
| Tuple length 2 / 3 | `Pair<A,B>` / `Triple<A,B,C>` | length > 3 throws |
|
|
210
|
+
|
|
211
|
+
### Swift
|
|
212
|
+
|
|
213
|
+
| JSON Schema | Swift | Notes |
|
|
214
|
+
|-------------|-------|-------|
|
|
215
|
+
| `string` | `String` | |
|
|
216
|
+
| `string, format: date-time` | `Foundation.Date` | requires `JSONDecoder.dateDecodingStrategy = .iso8601` |
|
|
217
|
+
| `string, format: uuid` | `Foundation.UUID` | |
|
|
218
|
+
| `string, format: uri` | `Foundation.URL` | |
|
|
219
|
+
| `string, format: email` etc. | `String` + `///` doc note | |
|
|
220
|
+
| `integer` | `Int64` | `format: int32` → `Int32` |
|
|
221
|
+
| `number` | `Double` | |
|
|
222
|
+
| `boolean` | `Bool` | |
|
|
223
|
+
| `array` | `[T]` | |
|
|
224
|
+
| `object` | `struct` | `: Codable` by default |
|
|
225
|
+
| `enum` (string) | `enum: String, Codable` | raw-value enum |
|
|
226
|
+
| `oneOf` w/ discriminator | `enum` w/ associated values | hand-rolled `init(from:)`/`encode(to:)` |
|
|
227
|
+
| Tuple (homogeneous) | `[T]` | |
|
|
228
|
+
| Tuple (heterogeneous, codable) | throws | use `serializer: "none"` for native tuples |
|
|
229
|
+
|
|
230
|
+
Schema-level `default` values are emitted inline for primitive types in Kotlin/Swift (`val foo: String = "x"`, `let foo: Int64 = 0`).
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Working with the IR directly
|
|
161
235
|
|
|
162
|
-
|
|
236
|
+
If you need the intermediate representation (e.g., to write a custom emitter), use `JSONSchemaConverter`:
|
|
163
237
|
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
console.log(tsConverter.code);
|
|
238
|
+
```ts
|
|
239
|
+
import { JSONSchemaConverter } from "ajsc/ir";
|
|
167
240
|
|
|
168
|
-
|
|
169
|
-
// {
|
|
241
|
+
const ir = new JSONSchemaConverter({ type: "string" }).irNode;
|
|
242
|
+
// { type: "string", path: "", constraints: {} }
|
|
170
243
|
```
|
|
171
244
|
|
|
172
|
-
|
|
245
|
+
`IRNode` shape is documented in `ajsc/types`.
|
|
173
246
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
properties: {
|
|
178
|
-
status: { type: "string", enum: ["active", "inactive", "pending"] },
|
|
179
|
-
},
|
|
180
|
-
};
|
|
247
|
+
## Building a custom language emitter
|
|
248
|
+
|
|
249
|
+
To target a new language, extend `BaseConverter`:
|
|
181
250
|
|
|
182
|
-
|
|
183
|
-
|
|
251
|
+
```ts
|
|
252
|
+
import { BaseConverter, type LanguageProfile } from "ajsc/converter";
|
|
184
253
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
//
|
|
254
|
+
class DartConverter extends BaseConverter {
|
|
255
|
+
protected readonly languageProfile: LanguageProfile = {
|
|
256
|
+
language: "dart",
|
|
257
|
+
// ... per-language overrides
|
|
258
|
+
};
|
|
259
|
+
// ... implement generateObjectType and the emission orchestration
|
|
260
|
+
}
|
|
189
261
|
```
|
|
190
262
|
|
|
191
|
-
|
|
263
|
+
See `docs/architecture/README.md` for the layered design — `BaseConverter`, the `LanguageProfile` pattern, and the per-language helper module conventions used by Kotlin and Swift.
|
|
192
264
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Migrating from v6 to v7
|
|
268
|
+
|
|
269
|
+
### Breaking changes
|
|
270
|
+
|
|
271
|
+
These are output changes for existing consumers. They're behavioral fixes, not regressions, but if you've been depending on the older shapes you'll see diffs:
|
|
272
|
+
|
|
273
|
+
- **`$ref: "#"` recursion** in TypeScript now emits direct self-reference (`Array<Tree>`) rather than a buggy `Child` extraction with `Array<any>` recursion.
|
|
274
|
+
- **`anyOf` merge** preserves the parent's `required` flag. A required schema field that gets anyOf-merged is now non-optional in output (`actor: Actor` instead of `actor?: Actor`).
|
|
275
|
+
- **`oneOf` variant naming** in TypeScript now uses discriminator-derived names (`WrapperOuter`, `ScalarOuter`) rather than path-collision suffix names (`Outer`, `OuterType`). Kotlin and Swift always used the discriminator-derived form; this brings TS into alignment.
|
|
276
|
+
- **`format: int32`** is now honored: emits `Int` (Kotlin) and `Int32` (Swift) where previously these always emitted `Long`/`Int64`. `format: int64` and unset format both still emit `Long`/`Int64`.
|
|
277
|
+
- **Schema-level `default`** is now emitted in Kotlin and Swift property declarations for primitive defaults (`val foo: String = "x"`). TS output is unchanged.
|
|
278
|
+
- **Swift acronym preservation**: enum case names for all-uppercase tokens (`US`, `URL`, `ID`) are no longer lowerCamelCased (`uS`/`uRL`/`iD`). They're preserved as-is.
|
|
279
|
+
- **`./converter` extension API** has been heavily refactored. If you subclassed `BaseConverter` to override hook methods, see the new `LanguageProfile` pattern in `docs/architecture/README.md`. Most legacy hook overrides should migrate to a single `protected readonly languageProfile` field.
|
|
280
|
+
|
|
281
|
+
### New features
|
|
282
|
+
|
|
283
|
+
- **Kotlin and Swift converters.** First-class language targets, not just IR.
|
|
284
|
+
- **Top-level emit functions.** `emitTypescript`, `emitKotlin`, `emitSwift` from `"ajsc"`.
|
|
285
|
+
- **`./converter` subpath** for downstream extension authors.
|
|
286
|
+
- **Root subpath restored.** `import { TypescriptConverter } from "ajsc"` works again (was subpath-only in v6).
|
|
287
|
+
|
|
288
|
+
### Migrating v5 → v7
|
|
289
|
+
|
|
290
|
+
If you're still on v5, the v6 → v7 changes apply on top of these:
|
|
291
|
+
|
|
292
|
+
```diff
|
|
293
|
+
- import { TypescriptConverter, JSONSchemaConverter } from "ajsc";
|
|
294
|
+
+ import { TypescriptConverter } from "ajsc"; // root entry restored
|
|
295
|
+
+ import { JSONSchemaConverter } from "ajsc/ir"; // moved to subpath in v6
|
|
198
296
|
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Architecture
|
|
301
|
+
|
|
302
|
+
For contributors and downstream extension authors, see [`docs/architecture/README.md`](./docs/architecture/README.md) — the data flow, layered class hierarchy, `LanguageProfile` pattern, per-language module structure, and the steps to add a new language target.
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT
|