@oh-my-pi/pi-ai 18.4.2 → 18.4.4
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 +30 -41
- package/dist/types/auth-broker/protocol.d.ts +12 -0
- package/dist/types/dialect/rendering.d.ts +4 -0
- package/dist/types/images/openai-hosted.d.ts +1 -1
- package/dist/types/images/shared.d.ts +5 -2
- package/dist/types/providers/anthropic-wire.d.ts +9 -1
- package/dist/types/providers/anthropic.d.ts +17 -0
- package/dist/types/providers/aws-sigv4.d.ts +5 -0
- package/dist/types/providers/bedrock-anthropic.d.ts +9 -0
- package/dist/types/providers/bedrock-request-metadata.d.ts +2 -0
- package/dist/types/providers/cursor/interaction-query.d.ts +10 -0
- package/dist/types/providers/openai-chat-wire.d.ts +2 -2
- package/dist/types/providers/openai-codex/request-transformer.d.ts +1 -1
- package/dist/types/providers/openai-responses-wire.d.ts +2 -2
- package/dist/types/providers/xai-base-url.d.ts +17 -0
- package/dist/types/types.d.ts +14 -3
- package/dist/types/usage/commandcode.d.ts +4 -0
- package/dist/types/usage/shared.d.ts +13 -1
- package/dist/types/utils/event-stream.d.ts +7 -0
- package/dist/types/utils/openai-http.d.ts +7 -1
- package/package.json +6 -6
- package/src/auth-broker/client.ts +1 -12
- package/src/auth-broker/protocol.ts +32 -0
- package/src/auth-broker/remote-store.ts +4 -40
- package/src/auth-broker/server.ts +1 -20
- package/src/auth-broker/snapshot-cache.ts +1 -9
- package/src/dialect/anthropic.ts +3 -25
- package/src/dialect/minimax.ts +3 -24
- package/src/dialect/rendering.ts +18 -0
- package/src/dialect/thinking.ts +24 -6
- package/src/dialect/xml.ts +3 -19
- package/src/images/openai-hosted.ts +5 -2
- package/src/images/openai-images.ts +10 -4
- package/src/images/shared.ts +9 -4
- package/src/providers/amazon-bedrock.ts +61 -22
- package/src/providers/anthropic-compaction.ts +10 -1
- package/src/providers/anthropic-wire.ts +12 -1
- package/src/providers/anthropic.ts +58 -15
- package/src/providers/aws-eventstream.ts +4 -3
- package/src/providers/aws-sigv4.ts +1 -1
- package/src/providers/azure-openai-responses.ts +3 -4
- package/src/providers/bedrock-anthropic.ts +30 -0
- package/src/providers/bedrock-request-metadata.ts +6 -0
- package/src/providers/connect-error-detail.ts +1 -5
- package/src/providers/cursor/interaction-query.ts +4 -2
- package/src/providers/cursor.ts +30 -26
- package/src/providers/google-gemini-cli.ts +9 -2
- package/src/providers/google-shared.ts +16 -6
- package/src/providers/openai-chat-wire.ts +2 -2
- package/src/providers/openai-codex/request-transformer.ts +1 -1
- package/src/providers/openai-codex-responses.ts +27 -16
- package/src/providers/openai-completions.ts +51 -36
- package/src/providers/openai-responses-wire.ts +2 -2
- package/src/providers/openai-responses.ts +3 -4
- package/src/providers/openai-shared.ts +130 -18
- package/src/providers/xai-base-url.ts +32 -0
- package/src/registry/engine/api-key.ts +8 -3
- package/src/types.ts +35 -4
- package/src/usage/claude.ts +4 -11
- package/src/usage/cline-pass.ts +2 -14
- package/src/usage/commandcode.ts +209 -0
- package/src/usage/cursor.ts +9 -1
- package/src/usage/openai-codex.ts +3 -5
- package/src/usage/registry.ts +3 -0
- package/src/usage/shared.ts +28 -1
- package/src/usage/synthetic.ts +4 -40
- package/src/usage/umans.ts +8 -36
- package/src/usage/zai.ts +10 -38
- package/src/utils/event-stream.ts +38 -2
- package/src/utils/http-inspector.ts +4 -8
- package/src/utils/openai-http.ts +10 -3
- package/src/utils/schema/json-schema-validator.ts +23 -26
- package/src/utils/schema/meta-validator.ts +4 -7
- package/src/utils/schema/wire.ts +17 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import { getLogsDir, isBunTestRuntime } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { getLogsDir, isBunTestRuntime, isRecord } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import * as AIError from "../error/flags";
|
|
4
4
|
import { formatErrorMessageWithRetryAfter } from "./retry-after.js";
|
|
5
5
|
|
|
@@ -252,13 +252,13 @@ function formatCapturedHttpError(captured: CapturedHttpErrorResponse | undefined
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
function parseCapturedErrorPayload(captured: CapturedHttpErrorResponse): Record<string, unknown> | undefined {
|
|
255
|
-
if (
|
|
255
|
+
if (isRecord(captured.bodyJson)) {
|
|
256
256
|
return captured.bodyJson;
|
|
257
257
|
}
|
|
258
258
|
if (!captured.bodyText) return undefined;
|
|
259
259
|
try {
|
|
260
260
|
const parsed = JSON.parse(captured.bodyText);
|
|
261
|
-
return
|
|
261
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
262
262
|
} catch {
|
|
263
263
|
return undefined;
|
|
264
264
|
}
|
|
@@ -266,14 +266,10 @@ function parseCapturedErrorPayload(captured: CapturedHttpErrorResponse): Record<
|
|
|
266
266
|
|
|
267
267
|
function getObjectProperty(value: Record<string, unknown>, key: string): Record<string, unknown> | undefined {
|
|
268
268
|
const property = value[key];
|
|
269
|
-
return
|
|
269
|
+
return isRecord(property) ? property : undefined;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
function getStringProperty(value: Record<string, unknown>, key: string): string | undefined {
|
|
273
273
|
const property = value[key];
|
|
274
274
|
return typeof property === "string" && property.trim().length > 0 ? property : undefined;
|
|
275
275
|
}
|
|
276
|
-
|
|
277
|
-
function isObject(value: unknown): value is Record<string, unknown> {
|
|
278
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
279
|
-
}
|
package/src/utils/openai-http.ts
CHANGED
|
@@ -70,8 +70,14 @@ export interface OpenAIStreamRequestInit {
|
|
|
70
70
|
fetch?: FetchImpl;
|
|
71
71
|
/** Optional caller-specific gate composed with shared transport retry exclusions. */
|
|
72
72
|
shouldRetryResponse?: (response: Response, bodyText: string) => boolean | Promise<boolean>;
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Raw wire-frame observer (`onSseEvent` debug pipeline). Leave it unset
|
|
75
|
+
* when no diagnostic listener exists: any observer turns on per-line raw
|
|
76
|
+
* capture for every frame.
|
|
77
|
+
*/
|
|
74
78
|
onSseEvent?: SseEventObserver;
|
|
79
|
+
/** Called when the stream ends on the OpenAI `[DONE]` sentinel; independent of {@link onSseEvent}. */
|
|
80
|
+
onDoneSentinel?: () => void;
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
export interface OpenAIStreamHandle<TEvent> {
|
|
@@ -117,7 +123,7 @@ export async function postOpenAIStream<TEvent>(init: OpenAIStreamRequestInit): P
|
|
|
117
123
|
});
|
|
118
124
|
}
|
|
119
125
|
return {
|
|
120
|
-
events: decodeStream<TEvent>(response.body, init.signal, init.onSseEvent),
|
|
126
|
+
events: decodeStream<TEvent>(response.body, init.signal, init.onSseEvent, init.onDoneSentinel),
|
|
121
127
|
response,
|
|
122
128
|
requestId: response.headers.get("x-request-id"),
|
|
123
129
|
};
|
|
@@ -140,8 +146,9 @@ async function* decodeStream<TEvent>(
|
|
|
140
146
|
body: ReadableStream<Uint8Array>,
|
|
141
147
|
signal: AbortSignal | undefined,
|
|
142
148
|
onSseEvent: SseEventObserver | undefined,
|
|
149
|
+
onDoneSentinel: (() => void) | undefined,
|
|
143
150
|
): AsyncGenerator<TEvent> {
|
|
144
|
-
for await (const frame of readSseJsonOrText<TEvent>(body, signal, onSseEvent)) {
|
|
151
|
+
for await (const frame of readSseJsonOrText<TEvent>(body, signal, onSseEvent, onDoneSentinel)) {
|
|
145
152
|
if (typeof frame === "string") {
|
|
146
153
|
const inBand = AIError.createInBandProviderErrorFromText(frame);
|
|
147
154
|
if (inBand) throw inBand;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Compared to AJV this is single-pass, synchronous, dependency-free, and
|
|
13
13
|
* tolerates non-standard shapes (`nullable`) that LLM-emitted schemas carry.
|
|
14
14
|
*/
|
|
15
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
15
|
+
import { isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
16
16
|
import { areJsonValuesEqual } from "./equality";
|
|
17
17
|
|
|
18
18
|
export interface JsonSchemaValidationIssue {
|
|
@@ -65,9 +65,6 @@ function getValueIdentity(ctx: ValidationContext, value: object): number {
|
|
|
65
65
|
return id;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
function isJsonObject(value: unknown): value is Record<string, unknown> {
|
|
69
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70
|
-
}
|
|
71
68
|
/**
|
|
72
69
|
* Whether `value` matches every `const`/`enum` discriminator property the
|
|
73
70
|
* branch declares (with at least one such property present and matching).
|
|
@@ -76,13 +73,13 @@ function isJsonObject(value: unknown): value is Record<string, unknown> {
|
|
|
76
73
|
* repairs to them; without a unique tag, branch issues are guesses.
|
|
77
74
|
*/
|
|
78
75
|
function isTagSelectedBranch(branch: unknown, value: unknown): boolean {
|
|
79
|
-
if (!
|
|
76
|
+
if (!isRecord(branch) || !isRecord(value)) return false;
|
|
80
77
|
const props = branch.properties;
|
|
81
|
-
if (!
|
|
78
|
+
if (!isRecord(props)) return false;
|
|
82
79
|
let matched = false;
|
|
83
80
|
for (const key in props) {
|
|
84
81
|
const propSchema = props[key];
|
|
85
|
-
if (!
|
|
82
|
+
if (!isRecord(propSchema)) continue;
|
|
86
83
|
const hasConst = Object.hasOwn(propSchema, "const");
|
|
87
84
|
const enumValues = Array.isArray(propSchema.enum) ? propSchema.enum : undefined;
|
|
88
85
|
if (!hasConst && !enumValues) continue;
|
|
@@ -138,7 +135,7 @@ function matchesJsonSchemaType(value: unknown, type: string): boolean {
|
|
|
138
135
|
case "boolean":
|
|
139
136
|
return typeof value === "boolean";
|
|
140
137
|
case "object":
|
|
141
|
-
return
|
|
138
|
+
return isRecord(value);
|
|
142
139
|
case "array":
|
|
143
140
|
return Array.isArray(value);
|
|
144
141
|
case "null":
|
|
@@ -178,7 +175,7 @@ function resolveLocalRef(root: unknown, ref: string): unknown | undefined {
|
|
|
178
175
|
let current: unknown = root;
|
|
179
176
|
for (const rawToken of ref.slice(2).split("/")) {
|
|
180
177
|
const token = decodePointerToken(rawToken);
|
|
181
|
-
if (!
|
|
178
|
+
if (!isRecord(current) && !Array.isArray(current)) return undefined;
|
|
182
179
|
current = (current as Record<string, unknown>)[token];
|
|
183
180
|
}
|
|
184
181
|
return current;
|
|
@@ -186,7 +183,7 @@ function resolveLocalRef(root: unknown, ref: string): unknown | undefined {
|
|
|
186
183
|
|
|
187
184
|
/** A presence-only constraint, as distinct from restrictions on a property's value. */
|
|
188
185
|
function requiresOnlyPropertyPresence(schema: unknown, key: string, root: unknown, depth = 0): boolean {
|
|
189
|
-
if (!
|
|
186
|
+
if (!isRecord(schema) || depth >= MAX_REF_DEPTH) return false;
|
|
190
187
|
for (const keyword of Object.keys(schema)) {
|
|
191
188
|
switch (keyword) {
|
|
192
189
|
case "required":
|
|
@@ -217,17 +214,17 @@ export function schemaDefinesProperty(schema: unknown, key: string): boolean {
|
|
|
217
214
|
const root = schema;
|
|
218
215
|
const visited = new WeakMap<object, number>();
|
|
219
216
|
const visit = (node: unknown, context: "positive" | "negative" | "predicate" = "positive"): boolean => {
|
|
220
|
-
if (!
|
|
217
|
+
if (!isRecord(node)) return false;
|
|
221
218
|
const contextBit = context === "positive" ? 1 : context === "negative" ? 2 : 4;
|
|
222
219
|
const previousContexts = visited.get(node) ?? 0;
|
|
223
220
|
if (previousContexts & contextBit) return false;
|
|
224
221
|
visited.set(node, previousContexts | contextBit);
|
|
225
|
-
if (
|
|
226
|
-
if (Array.isArray(node.enum) && node.enum.some(value =>
|
|
222
|
+
if (isRecord(node.const) && Object.hasOwn(node.const, key)) return true;
|
|
223
|
+
if (Array.isArray(node.enum) && node.enum.some(value => isRecord(value) && Object.hasOwn(value, key))) {
|
|
227
224
|
return true;
|
|
228
225
|
}
|
|
229
226
|
const properties = node.properties;
|
|
230
|
-
if (
|
|
227
|
+
if (isRecord(properties) && Object.hasOwn(properties, key)) {
|
|
231
228
|
return properties[key] !== false || context !== "positive";
|
|
232
229
|
}
|
|
233
230
|
const required = node.required;
|
|
@@ -240,7 +237,7 @@ export function schemaDefinesProperty(schema: unknown, key: string): boolean {
|
|
|
240
237
|
return true;
|
|
241
238
|
}
|
|
242
239
|
const patternProperties = node.patternProperties;
|
|
243
|
-
if (
|
|
240
|
+
if (isRecord(patternProperties)) {
|
|
244
241
|
for (const [pattern, patternSchema] of Object.entries(patternProperties)) {
|
|
245
242
|
try {
|
|
246
243
|
if (new RegExp(pattern).test(key)) {
|
|
@@ -251,8 +248,8 @@ export function schemaDefinesProperty(schema: unknown, key: string): boolean {
|
|
|
251
248
|
}
|
|
252
249
|
}
|
|
253
250
|
}
|
|
254
|
-
if (
|
|
255
|
-
if (
|
|
251
|
+
if (isRecord(node.unevaluatedProperties)) return true;
|
|
252
|
+
if (isRecord(node.additionalProperties)) return true;
|
|
256
253
|
for (const keyword of ["anyOf", "oneOf", "allOf"] as const) {
|
|
257
254
|
const branches = node[keyword];
|
|
258
255
|
if (Array.isArray(branches) && branches.some(branch => visit(branch, context))) return true;
|
|
@@ -270,19 +267,19 @@ export function schemaDefinesProperty(schema: unknown, key: string): boolean {
|
|
|
270
267
|
return true;
|
|
271
268
|
}
|
|
272
269
|
const dependentSchemas = node.dependentSchemas;
|
|
273
|
-
if (
|
|
270
|
+
if (isRecord(dependentSchemas)) {
|
|
274
271
|
if (Object.hasOwn(dependentSchemas, key)) return true;
|
|
275
272
|
if (Object.values(dependentSchemas).some(dependency => visit(dependency, context))) return true;
|
|
276
273
|
}
|
|
277
274
|
const dependentRequired = node.dependentRequired;
|
|
278
|
-
if (
|
|
275
|
+
if (isRecord(dependentRequired)) {
|
|
279
276
|
if (Object.hasOwn(dependentRequired, key)) return true;
|
|
280
277
|
for (const dependencies of Object.values(dependentRequired)) {
|
|
281
278
|
if (Array.isArray(dependencies) && dependencies.includes(key)) return true;
|
|
282
279
|
}
|
|
283
280
|
}
|
|
284
281
|
const dependencies = node.dependencies;
|
|
285
|
-
if (
|
|
282
|
+
if (isRecord(dependencies)) {
|
|
286
283
|
if (Object.hasOwn(dependencies, key)) return true;
|
|
287
284
|
for (const dependency of Object.values(dependencies)) {
|
|
288
285
|
if (Array.isArray(dependency) && dependency.includes(key)) return true;
|
|
@@ -324,7 +321,7 @@ function validateSchemaNode(
|
|
|
324
321
|
pushIssue(issues, path, "must not match false schema", { keyword: "false" });
|
|
325
322
|
return false;
|
|
326
323
|
}
|
|
327
|
-
if (!
|
|
324
|
+
if (!isRecord(schema)) {
|
|
328
325
|
pushIssue(issues, path, "schema must be an object or boolean", { keyword: "schema" });
|
|
329
326
|
return false;
|
|
330
327
|
}
|
|
@@ -469,7 +466,7 @@ function validateSchemaNode(
|
|
|
469
466
|
);
|
|
470
467
|
}
|
|
471
468
|
|
|
472
|
-
if (
|
|
469
|
+
if (isRecord(value)) {
|
|
473
470
|
valid = validateObjectKeywords(schema, value, path, ctx, issues) && valid;
|
|
474
471
|
}
|
|
475
472
|
if (Array.isArray(value)) {
|
|
@@ -494,7 +491,7 @@ function validateObjectKeywords(
|
|
|
494
491
|
issues: JsonSchemaValidationIssue[],
|
|
495
492
|
): boolean {
|
|
496
493
|
let valid = true;
|
|
497
|
-
const properties =
|
|
494
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
498
495
|
if (isRequiredSet(schema.required)) {
|
|
499
496
|
for (const key of schema.required) {
|
|
500
497
|
if (!(key in value)) {
|
|
@@ -516,7 +513,7 @@ function validateObjectKeywords(
|
|
|
516
513
|
}
|
|
517
514
|
|
|
518
515
|
const known = new Set(Object.keys(properties));
|
|
519
|
-
if (
|
|
516
|
+
if (isRecord(schema.patternProperties)) {
|
|
520
517
|
const patternProperties = schema.patternProperties;
|
|
521
518
|
for (const pattern in patternProperties) {
|
|
522
519
|
const patternSchema = patternProperties[pattern];
|
|
@@ -536,7 +533,7 @@ function validateObjectKeywords(
|
|
|
536
533
|
}
|
|
537
534
|
}
|
|
538
535
|
|
|
539
|
-
if (
|
|
536
|
+
if (isRecord(schema.dependentRequired)) {
|
|
540
537
|
const dependentRequired = schema.dependentRequired;
|
|
541
538
|
for (const key in dependentRequired) {
|
|
542
539
|
const deps = dependentRequired[key];
|
|
@@ -554,7 +551,7 @@ function validateObjectKeywords(
|
|
|
554
551
|
}
|
|
555
552
|
}
|
|
556
553
|
|
|
557
|
-
if (
|
|
554
|
+
if (isRecord(schema.dependentSchemas)) {
|
|
558
555
|
const dependentSchemas = schema.dependentSchemas;
|
|
559
556
|
for (const key in dependentSchemas) {
|
|
560
557
|
if (!(key in value)) continue;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord } from "@oh-my-pi/pi-utils";
|
|
1
2
|
import { areJsonValuesEqual } from "./equality";
|
|
2
3
|
import { epochNext, once } from "./stamps";
|
|
3
4
|
|
|
@@ -12,10 +13,6 @@ import { epochNext, once } from "./stamps";
|
|
|
12
13
|
|
|
13
14
|
type Json = unknown;
|
|
14
15
|
|
|
15
|
-
function isPlainObject(value: Json): value is Record<string, Json> {
|
|
16
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
16
|
const TYPE_NAMES: Record<string, true> = {
|
|
20
17
|
string: true,
|
|
21
18
|
number: true,
|
|
@@ -55,7 +52,7 @@ function checkSchemaArray(value: Json, epoch: number): boolean {
|
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
function checkSchemaMap(value: Json, epoch: number): boolean {
|
|
58
|
-
if (!
|
|
55
|
+
if (!isRecord(value)) return false;
|
|
59
56
|
for (const k in value) {
|
|
60
57
|
if (!checkNode(value[k], epoch)) return false;
|
|
61
58
|
}
|
|
@@ -66,7 +63,7 @@ function checkSchemaMap(value: Json, epoch: number): boolean {
|
|
|
66
63
|
function checkNode(node: Json, epoch: number): boolean {
|
|
67
64
|
// Boolean schemas (`true` / `false`) are valid JSON Schema.
|
|
68
65
|
if (node === true || node === false) return true;
|
|
69
|
-
if (!
|
|
66
|
+
if (!isRecord(node)) return false;
|
|
70
67
|
if (!once(node, epoch)) return true;
|
|
71
68
|
|
|
72
69
|
if ("type" in node && !checkTypeKeyword(node.type)) return false;
|
|
@@ -115,7 +112,7 @@ function checkNode(node: Json, epoch: number): boolean {
|
|
|
115
112
|
if ("dependentSchemas" in node && !checkSchemaMap(node.dependentSchemas, epoch)) return false;
|
|
116
113
|
if ("dependentRequired" in node) {
|
|
117
114
|
const value = node.dependentRequired;
|
|
118
|
-
if (!
|
|
115
|
+
if (!isRecord(value)) return false;
|
|
119
116
|
for (const k in value) {
|
|
120
117
|
const entry = value[k];
|
|
121
118
|
if (!Array.isArray(entry) || !entry.every(item => typeof item === "string")) return false;
|
package/src/utils/schema/wire.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Type } from "@oh-my-pi/omptype";
|
|
10
|
-
import { structuredCloneJSON } from "@oh-my-pi/pi-utils";
|
|
10
|
+
import { isRecord, structuredCloneJSON } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import type { Tool, TSchema } from "../../types";
|
|
12
12
|
import { upgradeJsonSchemaTo202012 } from "./draft";
|
|
13
13
|
import { stamp } from "./stamps";
|
|
@@ -28,13 +28,13 @@ export function isArkSchema(value: unknown): value is Type {
|
|
|
28
28
|
|
|
29
29
|
function isArkJsonAst(value: unknown): boolean {
|
|
30
30
|
if (Array.isArray(value)) return value.some(isArkJsonAst);
|
|
31
|
-
if (!
|
|
31
|
+
if (!isRecord(value)) return false;
|
|
32
32
|
if (typeof value.domain === "string" || Object.hasOwn(value, "unit")) return true;
|
|
33
33
|
if (value.proto === "Array" && Object.hasOwn(value, "sequence")) return true;
|
|
34
34
|
const required = value.required;
|
|
35
35
|
return (
|
|
36
36
|
Array.isArray(required) &&
|
|
37
|
-
required.some(entry =>
|
|
37
|
+
required.some(entry => isRecord(entry) && typeof entry.key === "string" && "value" in entry)
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -46,7 +46,7 @@ function parseArkObjectKey(key: string): { name: string; description?: string }
|
|
|
46
46
|
|
|
47
47
|
function withArkKeyDescription(schema: unknown, description: string | undefined): unknown {
|
|
48
48
|
if (!description) return schema;
|
|
49
|
-
if (
|
|
49
|
+
if (isRecord(schema)) {
|
|
50
50
|
if (typeof schema.description !== "string") schema.description = description;
|
|
51
51
|
return schema;
|
|
52
52
|
}
|
|
@@ -70,13 +70,13 @@ function arkJsonAstToWire(value: unknown): unknown {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (Array.isArray(value)) {
|
|
73
|
-
if (value.every(item =>
|
|
73
|
+
if (value.every(item => isRecord(item) && Object.hasOwn(item, "unit"))) {
|
|
74
74
|
return { enum: value.map(item => (item as { unit: unknown }).unit) };
|
|
75
75
|
}
|
|
76
76
|
return { anyOf: value.map(arkJsonAstToWire) };
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
if (!
|
|
79
|
+
if (!isRecord(value)) return {};
|
|
80
80
|
|
|
81
81
|
if (Object.hasOwn(value, "unit")) return { const: value.unit };
|
|
82
82
|
|
|
@@ -88,7 +88,7 @@ function arkJsonAstToWire(value: unknown): unknown {
|
|
|
88
88
|
const properties: Record<string, unknown> = {};
|
|
89
89
|
const required: string[] = [];
|
|
90
90
|
const addEntry = (entry: unknown, isRequired: boolean): void => {
|
|
91
|
-
if (!
|
|
91
|
+
if (!isRecord(entry) || typeof entry.key !== "string" || !("value" in entry)) return;
|
|
92
92
|
const key = parseArkObjectKey(entry.key);
|
|
93
93
|
properties[key.name] = withArkKeyDescription(arkJsonAstToWire(entry.value), key.description);
|
|
94
94
|
if (isRequired) required.push(key.name);
|
|
@@ -144,10 +144,6 @@ const SCHEMA_DEFINING_SIBLING_KEYS = new Set([
|
|
|
144
144
|
"unevaluatedProperties",
|
|
145
145
|
]);
|
|
146
146
|
|
|
147
|
-
function isSchemaRecord(value: unknown): value is Record<string, unknown> {
|
|
148
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
147
|
function hasSchemaDefiningSibling(schema: Record<string, unknown>): boolean {
|
|
152
148
|
for (const key in schema) {
|
|
153
149
|
if (key !== "anyOf" && SCHEMA_DEFINING_SIBLING_KEYS.has(key)) return true;
|
|
@@ -188,7 +184,7 @@ function rewriteNullableScalarAnyOf(schema: Record<string, unknown>): void {
|
|
|
188
184
|
let scalarType: string | undefined;
|
|
189
185
|
let sawNull = false;
|
|
190
186
|
for (const variant of variants) {
|
|
191
|
-
if (!
|
|
187
|
+
if (!isRecord(variant)) return;
|
|
192
188
|
if (isNullVariant(variant)) {
|
|
193
189
|
if (sawNull) return;
|
|
194
190
|
sawNull = true;
|
|
@@ -206,7 +202,7 @@ function rewriteNullableScalarAnyOf(schema: Record<string, unknown>): void {
|
|
|
206
202
|
}
|
|
207
203
|
|
|
208
204
|
function isExclusiveRequiredBranch(branch: unknown): boolean {
|
|
209
|
-
if (!
|
|
205
|
+
if (!isRecord(branch)) return false;
|
|
210
206
|
if (Object.hasOwn(branch, "type")) return false;
|
|
211
207
|
if (!Array.isArray(branch.required) || branch.required.length === 0) return false;
|
|
212
208
|
if (!branch.required.every(name => typeof name === "string" && name.length > 0)) return false;
|
|
@@ -229,7 +225,7 @@ export function flattenExclusiveRequiredRootUnion(schema: Record<string, unknown
|
|
|
229
225
|
const union = schema[unionKey];
|
|
230
226
|
if (!Array.isArray(union) || union.length === 0) return schema;
|
|
231
227
|
const typedObject = schema.type === "object" || (Array.isArray(schema.type) && schema.type.includes("object"));
|
|
232
|
-
if (!typedObject && !
|
|
228
|
+
if (!typedObject && !isRecord(schema.properties)) return schema;
|
|
233
229
|
if (!union.every(isExclusiveRequiredBranch)) return schema;
|
|
234
230
|
const flattened = { ...schema };
|
|
235
231
|
delete flattened[unionKey];
|
|
@@ -261,11 +257,11 @@ function normalizeArkPropertyComments(node: unknown): void {
|
|
|
261
257
|
for (const child of node) normalizeArkPropertyComments(child);
|
|
262
258
|
return;
|
|
263
259
|
}
|
|
264
|
-
if (!
|
|
260
|
+
if (!isRecord(node)) return;
|
|
265
261
|
const obj = node as Record<string, unknown>;
|
|
266
262
|
|
|
267
263
|
const properties = obj.properties;
|
|
268
|
-
if (
|
|
264
|
+
if (isRecord(properties)) {
|
|
269
265
|
const required = Array.isArray(obj.required) ? obj.required : undefined;
|
|
270
266
|
if (required) {
|
|
271
267
|
obj.required = required.map(key => (typeof key === "string" ? parseArkObjectKey(key).name : key));
|
|
@@ -289,7 +285,7 @@ function normalizeArkPropertyComments(node: unknown): void {
|
|
|
289
285
|
for (const mapKey of SCHEMA_MAP_KEYS) {
|
|
290
286
|
if (mapKey === "properties") continue;
|
|
291
287
|
const map = obj[mapKey];
|
|
292
|
-
if (
|
|
288
|
+
if (isRecord(map)) {
|
|
293
289
|
for (const key in map) normalizeArkPropertyComments(map[key]);
|
|
294
290
|
}
|
|
295
291
|
}
|
|
@@ -382,7 +378,7 @@ function collapseConstUnionAnyOf(obj: Record<string, unknown>): void {
|
|
|
382
378
|
let branchDescription: string | undefined;
|
|
383
379
|
let describedCount = 0;
|
|
384
380
|
for (const variant of variants) {
|
|
385
|
-
if (!
|
|
381
|
+
if (!isRecord(variant) || !Object.hasOwn(variant, "const")) return;
|
|
386
382
|
for (const key in variant) {
|
|
387
383
|
if (key !== "const" && key !== "description") return; // extra constraints — not a bare const
|
|
388
384
|
}
|
|
@@ -553,7 +549,7 @@ function pruneArkUndefinedUnionBranches(node: unknown): void {
|
|
|
553
549
|
const concrete = branches.filter(branch => !isUnconstrainedSchema(branch));
|
|
554
550
|
if (concrete.length === branches.length || concrete.length === 0) continue;
|
|
555
551
|
const only = concrete.length === 1 ? concrete[0] : undefined;
|
|
556
|
-
if (only !== undefined &&
|
|
552
|
+
if (only !== undefined && isRecord(only)) {
|
|
557
553
|
delete obj[unionKey];
|
|
558
554
|
for (const key in only) {
|
|
559
555
|
if (!(key in obj)) obj[key] = only[key];
|
|
@@ -651,14 +647,14 @@ function stripSchemaDescriptionsInPlace(node: unknown): void {
|
|
|
651
647
|
for (const child of node) stripSchemaDescriptionsInPlace(child);
|
|
652
648
|
return;
|
|
653
649
|
}
|
|
654
|
-
if (!
|
|
650
|
+
if (!isRecord(node)) return;
|
|
655
651
|
delete node.description;
|
|
656
652
|
for (const key of STRIP_SCHEMA_VALUE_KEYS) {
|
|
657
653
|
if (Object.hasOwn(node, key)) stripSchemaDescriptionsInPlace(node[key]);
|
|
658
654
|
}
|
|
659
655
|
for (const mapKey of STRIP_SCHEMA_MAP_KEYS) {
|
|
660
656
|
const map = node[mapKey];
|
|
661
|
-
if (
|
|
657
|
+
if (isRecord(map)) {
|
|
662
658
|
for (const key in map) stripSchemaDescriptionsInPlace(map[key]);
|
|
663
659
|
}
|
|
664
660
|
}
|