@gajae-code/ai 0.15.6 → 0.16.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/CHANGELOG.md +25 -0
- package/dist/types/adapter-internals/aws-region.d.ts +7 -0
- package/dist/types/core.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/providers/anthropic.d.ts +1 -1
- package/dist/types/providers/google-gemini-headers.d.ts +1 -1
- package/dist/types/providers/openai-codex-responses.d.ts +6 -0
- package/dist/types/utils/schema/normalize.d.ts +0 -5
- package/dist/types/utils/sqlite-errors.d.ts +4 -0
- package/package.json +3 -3
- package/src/adapter-internals/aws-region.d.ts +7 -0
- package/src/adapter-internals/aws-region.ts +14 -0
- package/src/auth-storage.ts +14 -14
- package/src/core.ts +1 -0
- package/src/index.ts +1 -0
- package/src/model-thinking.ts +8 -0
- package/src/models.json +201 -3
- package/src/provider-models/openai-compat.ts +61 -0
- package/src/providers/amazon-bedrock.ts +5 -1
- package/src/providers/anthropic.d.ts +1 -1
- package/src/providers/anthropic.ts +1 -1
- package/src/providers/aws-credentials.ts +6 -0
- package/src/providers/google-gemini-headers.d.ts +1 -1
- package/src/providers/google-gemini-headers.ts +1 -1
- package/src/providers/kiro-api-key.ts +33 -8
- package/src/providers/kiro-codewhisperer.ts +4 -1
- package/src/providers/openai-codex-responses.d.ts +6 -0
- package/src/providers/openai-codex-responses.ts +17 -2
- package/src/providers/pi-native-client.ts +24 -1
- package/src/utils/oauth/kiro.ts +91 -22
- package/src/utils/schema/dereference.ts +169 -49
- package/src/utils/schema/draft.ts +46 -23
- package/src/utils/schema/normalize.d.ts +0 -5
- package/src/utils/schema/normalize.ts +396 -119
- package/src/utils/schema/types.ts +3 -1
- package/src/utils/schema/zod-decontaminate.ts +83 -29
- package/src/utils/sqlite-errors.d.ts +4 -0
- package/src/utils/sqlite-errors.ts +13 -0
- package/src/utils/tool-choice-capability.ts +2 -3
|
@@ -11,69 +11,190 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { isJsonObject, type JsonObject } from "./types";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
const JSON_SCHEMA_LITERAL_PAYLOAD_KEYS = new Set(["default", "const", "enum", "examples"]);
|
|
15
|
+
const JSON_SCHEMA_MAP_KEYS = new Set([
|
|
16
|
+
"properties",
|
|
17
|
+
"patternProperties",
|
|
18
|
+
"dependencies",
|
|
19
|
+
"dependentSchemas",
|
|
20
|
+
"$defs",
|
|
21
|
+
"definitions",
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
function setOwnKey(target: JsonObject, key: string, value: unknown): void {
|
|
25
|
+
if (key === "__proto__") {
|
|
26
|
+
Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
target[key] = value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type DereferenceState = {
|
|
33
|
+
unresolvedRef: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Resolve any local JSON Pointer, including escaped names and boolean schemas. */
|
|
37
|
+
function resolveLocalRef(ref: string, root: JsonObject): unknown | undefined {
|
|
38
|
+
if (!ref.startsWith("#")) return undefined;
|
|
39
|
+
let pointer: string;
|
|
40
|
+
try {
|
|
41
|
+
pointer = decodeURIComponent(ref.slice(1));
|
|
42
|
+
} catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
if (pointer === "") return root;
|
|
46
|
+
if (!pointer.startsWith("/")) return undefined;
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
48
|
+
let current: unknown = root;
|
|
49
|
+
for (const encodedSegment of pointer.slice(1).split("/")) {
|
|
50
|
+
const segment = encodedSegment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
51
|
+
if (Array.isArray(current)) {
|
|
52
|
+
if (!/^(0|[1-9]\d*)$/.test(segment) || !Object.hasOwn(current, segment)) return undefined;
|
|
53
|
+
current = current[Number(segment)];
|
|
54
|
+
} else if (isJsonObject(current)) {
|
|
55
|
+
if (!Object.hasOwn(current, segment)) return undefined;
|
|
56
|
+
current = current[segment];
|
|
57
|
+
} else {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
26
63
|
|
|
27
|
-
|
|
28
|
-
|
|
64
|
+
/** Find definition maps anywhere in a schema graph, excluding literal instance data. */
|
|
65
|
+
function hasDefinitionMapDeep(value: unknown, seen: Set<object>, inSchemaMap = false): boolean {
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
if (seen.has(value)) return false;
|
|
68
|
+
seen.add(value);
|
|
69
|
+
return value.some(entry => hasDefinitionMapDeep(entry, seen, false));
|
|
70
|
+
}
|
|
71
|
+
if (!isJsonObject(value) || seen.has(value)) return false;
|
|
72
|
+
seen.add(value);
|
|
73
|
+
for (const key in value) {
|
|
74
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
75
|
+
if (!inSchemaMap && (key === "$defs" || key === "definitions")) return true;
|
|
76
|
+
if (!inSchemaMap && JSON_SCHEMA_LITERAL_PAYLOAD_KEYS.has(key)) continue;
|
|
77
|
+
const childInSchemaMap = !inSchemaMap && JSON_SCHEMA_MAP_KEYS.has(key);
|
|
78
|
+
if (hasDefinitionMapDeep(value[key], seen, childInSchemaMap)) return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function dereferenceSchemaMap(
|
|
84
|
+
schemaMap: JsonObject,
|
|
85
|
+
root: JsonObject,
|
|
86
|
+
visitingRefs: Set<string>,
|
|
87
|
+
visitingNodes: Set<object>,
|
|
88
|
+
state: DereferenceState,
|
|
89
|
+
): JsonObject {
|
|
90
|
+
const result: JsonObject = {};
|
|
91
|
+
for (const key in schemaMap) {
|
|
92
|
+
if (!Object.hasOwn(schemaMap, key)) continue;
|
|
93
|
+
setOwnKey(result, key, dereferenceNode(schemaMap[key], root, visitingRefs, visitingNodes, state));
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
29
96
|
}
|
|
30
97
|
|
|
31
98
|
/**
|
|
32
99
|
* Recursively dereference a JSON Schema node, inlining all local `$ref` pointers.
|
|
100
|
+
* Object and array path tracking preserves the existing `{}` cycle boundary.
|
|
33
101
|
*/
|
|
34
|
-
function dereferenceNode(
|
|
35
|
-
|
|
36
|
-
|
|
102
|
+
function dereferenceNode(
|
|
103
|
+
node: unknown,
|
|
104
|
+
root: JsonObject,
|
|
105
|
+
visitingRefs: Set<string>,
|
|
106
|
+
visitingNodes: Set<object>,
|
|
107
|
+
state: DereferenceState,
|
|
108
|
+
inSchemaMap = false,
|
|
109
|
+
): unknown {
|
|
110
|
+
if (!node || typeof node !== "object") return node;
|
|
111
|
+
if (visitingNodes.has(node)) return {};
|
|
112
|
+
visitingNodes.add(node);
|
|
113
|
+
|
|
114
|
+
if (Array.isArray(node)) {
|
|
115
|
+
const result = node.map(item => dereferenceNode(item, root, visitingRefs, visitingNodes, state));
|
|
116
|
+
visitingNodes.delete(node);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
37
119
|
|
|
38
|
-
const
|
|
120
|
+
const schemaNode = node as JsonObject;
|
|
121
|
+
const ref = Object.hasOwn(schemaNode, "$ref") ? schemaNode.$ref : undefined;
|
|
39
122
|
if (typeof ref === "string") {
|
|
40
|
-
|
|
41
|
-
|
|
123
|
+
if (visitingRefs.has(ref)) {
|
|
124
|
+
visitingNodes.delete(node);
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
42
127
|
const resolved = resolveLocalRef(ref, root);
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
128
|
+
if (resolved !== undefined) {
|
|
129
|
+
visitingRefs.add(ref);
|
|
130
|
+
const inlined = dereferenceNode(resolved, root, visitingRefs, visitingNodes, state);
|
|
131
|
+
visitingRefs.delete(ref);
|
|
47
132
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
133
|
+
// Merge sibling keywords (e.g. description, default) from the
|
|
134
|
+
// referencing node. In draft 2020-12 these are valid alongside `$ref`.
|
|
135
|
+
let hasSiblings = false;
|
|
136
|
+
for (const key in schemaNode) {
|
|
137
|
+
if (Object.hasOwn(schemaNode, key) && key !== "$ref") {
|
|
138
|
+
hasSiblings = true;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (inlined === false) {
|
|
143
|
+
visitingNodes.delete(node);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (!hasSiblings || (!isJsonObject(inlined) && inlined !== true)) {
|
|
147
|
+
visitingNodes.delete(node);
|
|
148
|
+
return inlined;
|
|
55
149
|
}
|
|
150
|
+
const merged: JsonObject = {};
|
|
151
|
+
if (isJsonObject(inlined)) {
|
|
152
|
+
for (const key in inlined) {
|
|
153
|
+
if (Object.hasOwn(inlined, key)) setOwnKey(merged, key, inlined[key]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const key in schemaNode) {
|
|
157
|
+
if (!Object.hasOwn(schemaNode, key) || key === "$ref") continue;
|
|
158
|
+
const siblingValue = schemaNode[key];
|
|
159
|
+
const sibling =
|
|
160
|
+
!inSchemaMap && JSON_SCHEMA_LITERAL_PAYLOAD_KEYS.has(key)
|
|
161
|
+
? schemaNode[key]
|
|
162
|
+
: !inSchemaMap && JSON_SCHEMA_MAP_KEYS.has(key) && isJsonObject(siblingValue)
|
|
163
|
+
? dereferenceSchemaMap(siblingValue, root, visitingRefs, visitingNodes, state)
|
|
164
|
+
: dereferenceNode(siblingValue, root, visitingRefs, visitingNodes, state);
|
|
165
|
+
setOwnKey(merged, key, sibling);
|
|
166
|
+
}
|
|
167
|
+
if (!state.unresolvedRef) {
|
|
168
|
+
delete merged.$defs;
|
|
169
|
+
delete merged.definitions;
|
|
170
|
+
}
|
|
171
|
+
visitingNodes.delete(node);
|
|
172
|
+
return merged;
|
|
56
173
|
}
|
|
57
|
-
|
|
58
|
-
const merged: JsonObject = { ...inlined, ...node };
|
|
59
|
-
delete merged.$ref;
|
|
60
|
-
return merged;
|
|
174
|
+
state.unresolvedRef = true;
|
|
61
175
|
}
|
|
62
176
|
|
|
63
177
|
const result: JsonObject = {};
|
|
64
|
-
for (const key in
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
result
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
result
|
|
178
|
+
for (const key in schemaNode) {
|
|
179
|
+
if (!Object.hasOwn(schemaNode, key)) continue;
|
|
180
|
+
const value = schemaNode[key];
|
|
181
|
+
// Literal instance data is not schema-shaped, even when it contains an
|
|
182
|
+
// object that happens to use schema-looking keys or a local `$ref`.
|
|
183
|
+
if (!inSchemaMap && JSON_SCHEMA_LITERAL_PAYLOAD_KEYS.has(key)) {
|
|
184
|
+
setOwnKey(result, key, value);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (!inSchemaMap && JSON_SCHEMA_MAP_KEYS.has(key) && isJsonObject(value)) {
|
|
188
|
+
setOwnKey(result, key, dereferenceSchemaMap(value, root, visitingRefs, visitingNodes, state));
|
|
189
|
+
continue;
|
|
75
190
|
}
|
|
191
|
+
setOwnKey(result, key, dereferenceNode(value, root, visitingRefs, visitingNodes, state));
|
|
192
|
+
}
|
|
193
|
+
if (!state.unresolvedRef) {
|
|
194
|
+
delete result.$defs;
|
|
195
|
+
delete result.definitions;
|
|
76
196
|
}
|
|
197
|
+
visitingNodes.delete(node);
|
|
77
198
|
return result;
|
|
78
199
|
}
|
|
79
200
|
|
|
@@ -90,9 +211,8 @@ function dereferenceNode(node: unknown, root: JsonObject, visiting: Set<string>)
|
|
|
90
211
|
export function dereferenceJsonSchema(schema: unknown): unknown {
|
|
91
212
|
if (!isJsonObject(schema)) return schema;
|
|
92
213
|
|
|
93
|
-
// Fast path: nothing to dereference
|
|
94
|
-
|
|
95
|
-
if (!hasDefs) return schema;
|
|
214
|
+
// Fast path: nothing to dereference anywhere in the schema graph
|
|
215
|
+
if (!hasDefinitionMapDeep(schema, new Set())) return schema;
|
|
96
216
|
|
|
97
|
-
return dereferenceNode(schema, schema, new Set());
|
|
217
|
+
return dereferenceNode(schema, schema, new Set(), new Set(), { unresolvedRef: false });
|
|
98
218
|
}
|
|
@@ -17,7 +17,7 @@ const DRAFT_07_SCHEMA_URIS: Record<string, true> = {
|
|
|
17
17
|
* entry rather than the map object itself so legacy `definitions`-style refs
|
|
18
18
|
* inside property schemas get rewritten.
|
|
19
19
|
*/
|
|
20
|
-
const SCHEMA_MAP_KEYS
|
|
20
|
+
const SCHEMA_MAP_KEYS = new Set(["properties", "patternProperties", "dependentSchemas"]);
|
|
21
21
|
/**
|
|
22
22
|
* Keys whose values are JSON-Schema *values*, not nested schemas. The upgrade
|
|
23
23
|
* walker must NOT descend into these — `type: ["string","null"]` is not a
|
|
@@ -34,6 +34,14 @@ const NON_SCHEMA_VALUE_KEYS: Record<string, true> = {
|
|
|
34
34
|
type: true,
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
function setOwnKey(target: JsonObject, key: string, value: unknown): void {
|
|
38
|
+
if (key === "__proto__") {
|
|
39
|
+
Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
target[key] = value;
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
/** Rewrite draft-07's `#/definitions/Foo` ref form to draft 2020-12's `#/$defs/Foo`. External refs (`http://…`) pass through. */
|
|
38
46
|
function convertRef(value: string): string {
|
|
39
47
|
return value.startsWith("#/definitions/") ? `#/$defs/${value.slice("#/definitions/".length)}` : value;
|
|
@@ -41,10 +49,10 @@ function convertRef(value: string): string {
|
|
|
41
49
|
|
|
42
50
|
/** Get-or-create a child object map on `target[key]`. Used to lazily build up `$defs`/`dependentRequired`/`dependentSchemas` during conversion. */
|
|
43
51
|
function getObjectMap(target: JsonObject, key: string): JsonObject {
|
|
44
|
-
const existing = target[key];
|
|
52
|
+
const existing = Object.hasOwn(target, key) ? target[key] : undefined;
|
|
45
53
|
if (isJsonObject(existing)) return existing;
|
|
46
54
|
const next: JsonObject = {};
|
|
47
|
-
target
|
|
55
|
+
setOwnKey(target, key, next);
|
|
48
56
|
return next;
|
|
49
57
|
}
|
|
50
58
|
|
|
@@ -52,13 +60,14 @@ function getObjectMap(target: JsonObject, key: string): JsonObject {
|
|
|
52
60
|
function mergeSchemaMap(target: JsonObject, key: string, value: JsonObject, cache: WeakMap<object, unknown>): void {
|
|
53
61
|
const map = getObjectMap(target, key);
|
|
54
62
|
for (const name in value) {
|
|
55
|
-
|
|
63
|
+
if (!Object.hasOwn(value, name)) continue;
|
|
64
|
+
setOwnKey(map, name, upgradeJsonSchemaTo202012Impl(value[name], cache));
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
/** Copy a schema-map field with upgrade; non-object values are passed through verbatim. */
|
|
59
68
|
function copySchemaMap(target: JsonObject, key: string, value: unknown, cache: WeakMap<object, unknown>): void {
|
|
60
69
|
if (!isJsonObject(value)) {
|
|
61
|
-
target
|
|
70
|
+
setOwnKey(target, key, value);
|
|
62
71
|
return;
|
|
63
72
|
}
|
|
64
73
|
mergeSchemaMap(target, key, value, cache);
|
|
@@ -107,20 +116,21 @@ function mergePrefixItems(existing: unknown, convertedItems: unknown[]): unknown
|
|
|
107
116
|
/** Record `key → deps` in `dependentRequired`, unioning with any existing array. */
|
|
108
117
|
function mergeDependentRequired(target: JsonObject, key: string, deps: unknown[]): void {
|
|
109
118
|
const dependentRequired = getObjectMap(target, "dependentRequired");
|
|
110
|
-
const existing = dependentRequired[key];
|
|
119
|
+
const existing = Object.hasOwn(dependentRequired, key) ? dependentRequired[key] : undefined;
|
|
111
120
|
if (existing === undefined) {
|
|
112
|
-
dependentRequired
|
|
121
|
+
setOwnKey(dependentRequired, key, deps);
|
|
113
122
|
return;
|
|
114
123
|
}
|
|
115
124
|
if (Array.isArray(existing)) {
|
|
116
|
-
dependentRequired
|
|
125
|
+
setOwnKey(dependentRequired, key, mergeArrayValues(existing, deps));
|
|
117
126
|
}
|
|
118
127
|
}
|
|
119
128
|
|
|
120
129
|
/** Record `key → schema` in `dependentSchemas`, intersecting with any existing entry. */
|
|
121
130
|
function mergeDependentSchema(target: JsonObject, key: string, schema: unknown): void {
|
|
122
131
|
const dependentSchemas = getObjectMap(target, "dependentSchemas");
|
|
123
|
-
|
|
132
|
+
const existing = Object.hasOwn(dependentSchemas, key) ? dependentSchemas[key] : undefined;
|
|
133
|
+
setOwnKey(dependentSchemas, key, combineSchemas(existing, schema));
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
/**
|
|
@@ -130,9 +140,10 @@ function mergeDependentSchema(target: JsonObject, key: string, schema: unknown):
|
|
|
130
140
|
* - schema value → `dependentSchemas`
|
|
131
141
|
*/
|
|
132
142
|
function convertDependencies(source: JsonObject, target: JsonObject, cache: WeakMap<object, unknown>): void {
|
|
133
|
-
const dependencies = source.dependencies;
|
|
143
|
+
const dependencies = Object.hasOwn(source, "dependencies") ? source.dependencies : undefined;
|
|
134
144
|
if (!isJsonObject(dependencies)) return;
|
|
135
145
|
for (const key in dependencies) {
|
|
146
|
+
if (!Object.hasOwn(dependencies, key)) continue;
|
|
136
147
|
const dependency = dependencies[key];
|
|
137
148
|
const converted = upgradeJsonSchemaTo202012Impl(dependency, cache);
|
|
138
149
|
if (Array.isArray(converted)) {
|
|
@@ -150,7 +161,9 @@ function hasNullType(type: unknown): boolean {
|
|
|
150
161
|
|
|
151
162
|
/** True if any variant in `anyOf` declares (only) a null type. Used to avoid double-adding `{type:"null"}`. */
|
|
152
163
|
function hasNullVariant(variants: unknown[]): boolean {
|
|
153
|
-
return variants.some(
|
|
164
|
+
return variants.some(
|
|
165
|
+
variant => isJsonObject(variant) && Object.hasOwn(variant, "type") && hasNullType(variant.type),
|
|
166
|
+
);
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
/**
|
|
@@ -182,6 +195,7 @@ function makeNullable(schema: JsonObject): JsonObject {
|
|
|
182
195
|
function schemaMapNeedsDraft202012Upgrade(value: unknown, epoch: number): boolean {
|
|
183
196
|
if (!isJsonObject(value)) return false;
|
|
184
197
|
for (const k in value) {
|
|
198
|
+
if (!Object.hasOwn(value, k)) continue;
|
|
185
199
|
if (schemaNeedsDraft202012UpgradeImpl(value[k], epoch)) return true;
|
|
186
200
|
}
|
|
187
201
|
return false;
|
|
@@ -203,9 +217,10 @@ function schemaNeedsDraft202012UpgradeImpl(value: unknown, epoch: number): boole
|
|
|
203
217
|
if (!once(value, epoch)) return false;
|
|
204
218
|
|
|
205
219
|
for (const key in value) {
|
|
220
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
206
221
|
const entry = value[key];
|
|
207
222
|
if (key === "$schema") {
|
|
208
|
-
if (typeof entry === "string" && entry
|
|
223
|
+
if (typeof entry === "string" && Object.hasOwn(DRAFT_07_SCHEMA_URIS, entry)) return true;
|
|
209
224
|
continue;
|
|
210
225
|
}
|
|
211
226
|
if (key === "definitions" || key === "dependencies" || key === "additionalItems" || key === "nullable") {
|
|
@@ -216,11 +231,11 @@ function schemaNeedsDraft202012UpgradeImpl(value: unknown, epoch: number): boole
|
|
|
216
231
|
continue;
|
|
217
232
|
}
|
|
218
233
|
if (key === "items" && Array.isArray(entry)) return true;
|
|
219
|
-
if (key === "$defs" || key
|
|
234
|
+
if (key === "$defs" || SCHEMA_MAP_KEYS.has(key)) {
|
|
220
235
|
if (schemaMapNeedsDraft202012Upgrade(entry, epoch)) return true;
|
|
221
236
|
continue;
|
|
222
237
|
}
|
|
223
|
-
if (key
|
|
238
|
+
if (Object.hasOwn(NON_SCHEMA_VALUE_KEYS, key)) continue;
|
|
224
239
|
if (schemaNeedsDraft202012UpgradeImpl(entry, epoch)) return true;
|
|
225
240
|
}
|
|
226
241
|
|
|
@@ -253,6 +268,7 @@ function upgradeJsonSchemaTo202012Impl(value: unknown, cache: WeakMap<object, un
|
|
|
253
268
|
// Seed cache before recursion so back-edges in cyclic graphs resolve.
|
|
254
269
|
cache.set(value, result);
|
|
255
270
|
for (const key in value) {
|
|
271
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
256
272
|
const entry = value[key];
|
|
257
273
|
// `definitions` is the draft-07 name; merge under the canonical `$defs`.
|
|
258
274
|
// `$defs` may appear pre-upgraded — still walk entries to upgrade their bodies.
|
|
@@ -261,13 +277,13 @@ function upgradeJsonSchemaTo202012Impl(value: unknown, cache: WeakMap<object, un
|
|
|
261
277
|
continue;
|
|
262
278
|
}
|
|
263
279
|
// Recurse into each entry; the map shape itself is preserved.
|
|
264
|
-
if (key
|
|
280
|
+
if (SCHEMA_MAP_KEYS.has(key)) {
|
|
265
281
|
copySchemaMap(result, key, entry, cache);
|
|
266
282
|
continue;
|
|
267
283
|
}
|
|
268
284
|
// JSON-Schema *value* keywords — copy verbatim.
|
|
269
|
-
if (key
|
|
270
|
-
result
|
|
285
|
+
if (Object.hasOwn(NON_SCHEMA_VALUE_KEYS, key)) {
|
|
286
|
+
setOwnKey(result, key, entry);
|
|
271
287
|
continue;
|
|
272
288
|
}
|
|
273
289
|
// Draft-07-only keywords with no draft 2020-12 spelling — drop entirely.
|
|
@@ -278,7 +294,9 @@ function upgradeJsonSchemaTo202012Impl(value: unknown, cache: WeakMap<object, un
|
|
|
278
294
|
// Rewrite `$schema` URI to the 2020-12 form; non-draft-07 URIs pass through.
|
|
279
295
|
if (key === "$schema") {
|
|
280
296
|
result.$schema =
|
|
281
|
-
typeof entry === "string" &&
|
|
297
|
+
typeof entry === "string" && Object.hasOwn(DRAFT_07_SCHEMA_URIS, entry)
|
|
298
|
+
? JSON_SCHEMA_DRAFT_2020_12_URI
|
|
299
|
+
: entry;
|
|
282
300
|
continue;
|
|
283
301
|
}
|
|
284
302
|
// `#/definitions/Foo` → `#/$defs/Foo`.
|
|
@@ -290,15 +308,20 @@ function upgradeJsonSchemaTo202012Impl(value: unknown, cache: WeakMap<object, un
|
|
|
290
308
|
if (key === "items" && Array.isArray(entry)) {
|
|
291
309
|
continue;
|
|
292
310
|
}
|
|
293
|
-
result
|
|
311
|
+
setOwnKey(result, key, upgradeJsonSchemaTo202012Impl(entry, cache));
|
|
294
312
|
}
|
|
295
313
|
|
|
296
314
|
// Draft-07 tuple form: `items: [a, b]` (+ optional `additionalItems`) becomes
|
|
297
315
|
// draft 2020-12 `prefixItems: [a, b]` (+ optional `items` for the rest).
|
|
298
|
-
|
|
299
|
-
|
|
316
|
+
const tupleItems = Object.hasOwn(value, "items") && Array.isArray(value.items) ? value.items : undefined;
|
|
317
|
+
if (tupleItems !== undefined) {
|
|
318
|
+
const convertedItems = upgradeJsonSchemaTo202012Impl(tupleItems, cache) as unknown[];
|
|
300
319
|
result.prefixItems = mergePrefixItems(result.prefixItems, convertedItems);
|
|
301
|
-
if (
|
|
320
|
+
if (
|
|
321
|
+
Object.hasOwn(value, "additionalItems") &&
|
|
322
|
+
value.additionalItems !== undefined &&
|
|
323
|
+
value.additionalItems !== true
|
|
324
|
+
) {
|
|
302
325
|
result.items = upgradeJsonSchemaTo202012Impl(value.additionalItems, cache);
|
|
303
326
|
} else {
|
|
304
327
|
// `additionalItems: true` (or absent) in draft-07 == no `items` in 2020-12.
|
|
@@ -311,7 +334,7 @@ function upgradeJsonSchemaTo202012Impl(value: unknown, cache: WeakMap<object, un
|
|
|
311
334
|
// OpenAPI 3.0 `nullable: true` → 2020-12 nullability. `makeNullable` may
|
|
312
335
|
// return a fresh wrapper object, in which case update the cache so callers
|
|
313
336
|
// referring to the same input see the wrapper instead of the inner result.
|
|
314
|
-
if (value.nullable === true) {
|
|
337
|
+
if (Object.hasOwn(value, "nullable") && value.nullable === true) {
|
|
315
338
|
const nullable = makeNullable(result);
|
|
316
339
|
if (nullable !== result) cache.set(value, nullable);
|
|
317
340
|
return nullable;
|
|
@@ -25,11 +25,6 @@ export interface NormalizeSchemaOptions {
|
|
|
25
25
|
}
|
|
26
26
|
/** Copy all keys from a schema except the specified combiner key. */
|
|
27
27
|
export declare function copySchemaWithout(schema: JsonObject, combiner: string): JsonObject;
|
|
28
|
-
/**
|
|
29
|
-
* Recursively strip any remaining anyOf/oneOf that same-type or mixed-type
|
|
30
|
-
* collapse can handle. This is needed because object-combiner merging can
|
|
31
|
-
* create new anyOf in merged subtrees after child normalization already ran.
|
|
32
|
-
*/
|
|
33
28
|
export declare function stripResidualCombiners(value: unknown, epoch?: number): unknown;
|
|
34
29
|
export declare function normalizeSchema(value: unknown, options: NormalizeSchemaOptions): unknown;
|
|
35
30
|
export declare function normalizeSchemaForGoogle(value: unknown): unknown;
|