poe-code 3.0.285 → 3.0.287
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/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/packages/toolcraft-openapi/dist/generate.d.ts +3 -0
- package/packages/toolcraft-openapi/dist/generate.js +3 -0
- package/packages/toolcraft-openapi/dist/http.js +55 -1
- package/packages/toolcraft-openapi/dist/index.d.ts +1 -0
- package/packages/toolcraft-openapi/dist/index.js +1 -0
- package/packages/toolcraft-openapi/dist/interpreter.d.ts +7 -1
- package/packages/toolcraft-openapi/dist/interpreter.js +103 -0
package/package.json
CHANGED
|
@@ -230,6 +230,9 @@ export type GeneratedPreflightBlock = {
|
|
|
230
230
|
jsonParamName: string;
|
|
231
231
|
nullParamName?: string;
|
|
232
232
|
resolvedName: string;
|
|
233
|
+
definition: Extract<GeneratedParamDefinition, {
|
|
234
|
+
kind: "array";
|
|
235
|
+
}>;
|
|
233
236
|
required: boolean;
|
|
234
237
|
};
|
|
235
238
|
interface SchemaOptionEntry {
|
|
@@ -870,6 +870,7 @@ function createArrayParam(options) {
|
|
|
870
870
|
jsonParamName,
|
|
871
871
|
...(emitsNullHelper ? { nullParamName } : {}),
|
|
872
872
|
resolvedName,
|
|
873
|
+
definition: directDefinition,
|
|
873
874
|
required: !optional
|
|
874
875
|
}
|
|
875
876
|
],
|
|
@@ -1386,11 +1387,13 @@ function createCommandFile(options) {
|
|
|
1386
1387
|
const usesMultipartFileInputs = options.bodyMode === "multipart" &&
|
|
1387
1388
|
options.multipartBinaryFields !== undefined &&
|
|
1388
1389
|
options.multipartBinaryFields.length > 0;
|
|
1390
|
+
const usesArrayJsonValidation = options.preflightBlocks.some((block) => block.kind === "array");
|
|
1389
1391
|
const usesBinaryOutput = options.responseMode === "binary";
|
|
1390
1392
|
const usesRequestShapeVariable = usesMultipartFileInputs || usesBinaryOutput;
|
|
1391
1393
|
const openApiImports = [
|
|
1392
1394
|
"requestJson",
|
|
1393
1395
|
"defineApiCommand",
|
|
1396
|
+
...(usesArrayJsonValidation ? ["validateArrayJsonHelperValue"] : []),
|
|
1394
1397
|
...(usesMultipartFileInputs ? ["prepareMultipartFileInputs"] : []),
|
|
1395
1398
|
...(usesBinaryOutput ? ["writeBinaryResponseOutput"] : [])
|
|
1396
1399
|
];
|
|
@@ -317,8 +317,57 @@ function decodeBase64Body(body) {
|
|
|
317
317
|
if (typeof body !== "string") {
|
|
318
318
|
throw new UserError("Base64 request bodies must be strings.");
|
|
319
319
|
}
|
|
320
|
+
if (!isValidBase64(body)) {
|
|
321
|
+
throw new UserError("Base64 request bodies must contain valid base64 text.");
|
|
322
|
+
}
|
|
320
323
|
return Uint8Array.from(Buffer.from(body, "base64")).buffer;
|
|
321
324
|
}
|
|
325
|
+
function isValidBase64(value) {
|
|
326
|
+
const paddingLength = getBase64PaddingLength(value);
|
|
327
|
+
if (paddingLength === null) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
const unpaddedLength = value.length - paddingLength;
|
|
331
|
+
if (unpaddedLength % 4 === 1) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
if (paddingLength > 0 && value.length % 4 !== 0) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
for (let index = 0; index < unpaddedLength; index += 1) {
|
|
338
|
+
if (!isBase64Character(value[index] ?? "")) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const normalized = value.padEnd(value.length + ((4 - (value.length % 4)) % 4), "=");
|
|
343
|
+
return Buffer.from(value, "base64").toString("base64") === normalized;
|
|
344
|
+
}
|
|
345
|
+
function getBase64PaddingLength(value) {
|
|
346
|
+
let paddingLength = 0;
|
|
347
|
+
for (let index = value.length - 1; index >= 0 && value[index] === "="; index -= 1) {
|
|
348
|
+
paddingLength += 1;
|
|
349
|
+
}
|
|
350
|
+
if (paddingLength > 2) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
for (let index = 0; index < value.length - paddingLength; index += 1) {
|
|
354
|
+
if (value[index] === "=") {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return paddingLength;
|
|
359
|
+
}
|
|
360
|
+
function isBase64Character(value) {
|
|
361
|
+
if (value.length !== 1) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
const codePoint = value.charCodeAt(0);
|
|
365
|
+
return ((codePoint >= 0x41 && codePoint <= 0x5a) ||
|
|
366
|
+
(codePoint >= 0x61 && codePoint <= 0x7a) ||
|
|
367
|
+
(codePoint >= 0x30 && codePoint <= 0x39) ||
|
|
368
|
+
value === "+" ||
|
|
369
|
+
value === "/");
|
|
370
|
+
}
|
|
322
371
|
function decodeMultipartBinaryValue(value, fallbackFilename) {
|
|
323
372
|
if (typeof value === "string") {
|
|
324
373
|
return {
|
|
@@ -483,7 +532,12 @@ function parseResponseBody(text, contentType) {
|
|
|
483
532
|
if (!isJsonContentType(contentType)) {
|
|
484
533
|
return text;
|
|
485
534
|
}
|
|
486
|
-
|
|
535
|
+
try {
|
|
536
|
+
return JSON.parse(text);
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
return text;
|
|
540
|
+
}
|
|
487
541
|
}
|
|
488
542
|
function extractErrorCode(body) {
|
|
489
543
|
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
@@ -19,4 +19,5 @@ export type { AuthProvider, CommandContributor, TokenSource } from "./auth/types
|
|
|
19
19
|
export { bearerTokenAuth } from "./auth/bearer-token-auth.js";
|
|
20
20
|
export type { BearerTokenAuthOptions } from "./auth/bearer-token-auth.js";
|
|
21
21
|
export { HttpError, prepareMultipartFileInputs, requestJson, writeBinaryResponseOutput } from "./http.js";
|
|
22
|
+
export { validateArrayJsonHelperValue } from "./interpreter.js";
|
|
22
23
|
export type { BinaryHttpResponse, HttpErrorRequest, HttpErrorResponse, HttpRequestOptions, QueryValue } from "./http.js";
|
|
@@ -10,3 +10,4 @@ export { renderOpenApiInspection } from "./render-inspection.js";
|
|
|
10
10
|
export { commandsFromSpec, defineClientFromSpec, resolveOpenApiBaseUrl } from "./runtime.js";
|
|
11
11
|
export { bearerTokenAuth } from "./auth/bearer-token-auth.js";
|
|
12
12
|
export { HttpError, prepareMultipartFileInputs, requestJson, writeBinaryResponseOutput } from "./http.js";
|
|
13
|
+
export { validateArrayJsonHelperValue } from "./interpreter.js";
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { type RequestSectionKey } from "./request-shape.js";
|
|
2
|
-
import type { GeneratedPreflightBlock, GeneratedRequestField, GeneratedRequestLocation, GeneratedRequestSectionRenders } from "./generate.js";
|
|
2
|
+
import type { GeneratedParamDefinition, GeneratedPreflightBlock, GeneratedRequestField, GeneratedRequestLocation, GeneratedRequestSectionRenders } from "./generate.js";
|
|
3
|
+
type GeneratedArrayDefinition = GeneratedParamDefinition & {
|
|
4
|
+
kind: "array";
|
|
5
|
+
itemDefinition: GeneratedParamDefinition;
|
|
6
|
+
};
|
|
3
7
|
export declare function renderPreflightBlock(block: GeneratedPreflightBlock): string[];
|
|
4
8
|
export declare function executePreflightBlocks(blocks: ReadonlyArray<GeneratedPreflightBlock>, params: Readonly<Record<string, unknown>>): Record<string, unknown>;
|
|
9
|
+
export declare function validateArrayJsonHelperValue(value: unknown, definition: GeneratedArrayDefinition, jsonParamName: string): void;
|
|
5
10
|
export declare function renderRequestShape(requestFields: ReadonlyArray<GeneratedRequestField>, sectionRenders: GeneratedRequestSectionRenders, optionalSections: ReadonlySet<GeneratedRequestLocation>): string[];
|
|
6
11
|
export declare function buildRequestShape(requestFields: ReadonlyArray<GeneratedRequestField>, sectionRenders: GeneratedRequestSectionRenders, optionalSections: ReadonlySet<GeneratedRequestLocation>, params: Readonly<Record<string, unknown>>, resolvedValues: Readonly<Record<string, unknown>>): Partial<Record<RequestSectionKey, unknown>>;
|
|
12
|
+
export {};
|
|
@@ -86,6 +86,7 @@ const PREFLIGHT_BLOCK_OPERATIONS = {
|
|
|
86
86
|
" if (!Array.isArray(parsedJson)) {",
|
|
87
87
|
` throw new UserError(${JSON.stringify(getInvalidArrayJsonMessage(block.jsonParamName, "Expected a JSON array."))});`,
|
|
88
88
|
" }",
|
|
89
|
+
` validateArrayJsonHelperValue(parsedJson, ${JSON.stringify(block.definition)}, ${JSON.stringify(block.jsonParamName)});`,
|
|
89
90
|
` ${block.resolvedName} = parsedJson;`,
|
|
90
91
|
" }",
|
|
91
92
|
...(block.nullParamName === undefined
|
|
@@ -126,6 +127,7 @@ const PREFLIGHT_BLOCK_OPERATIONS = {
|
|
|
126
127
|
if (!Array.isArray(parsedJson)) {
|
|
127
128
|
throw new UserError(getInvalidArrayJsonMessage(block.jsonParamName, "Expected a JSON array."));
|
|
128
129
|
}
|
|
130
|
+
validateArrayJsonHelperValue(parsedJson, block.definition, block.jsonParamName);
|
|
129
131
|
resolved = parsedJson;
|
|
130
132
|
}
|
|
131
133
|
if (nullRequested) {
|
|
@@ -208,6 +210,107 @@ export function executePreflightBlocks(blocks, params) {
|
|
|
208
210
|
}
|
|
209
211
|
return resolvedValues;
|
|
210
212
|
}
|
|
213
|
+
export function validateArrayJsonHelperValue(value, definition, jsonParamName) {
|
|
214
|
+
const issue = findDefinitionIssue(value, definition, []);
|
|
215
|
+
if (issue !== undefined) {
|
|
216
|
+
throw new UserError(getInvalidArrayJsonMessage(jsonParamName, issue));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function findDefinitionIssue(value, definition, path) {
|
|
220
|
+
if (value === null) {
|
|
221
|
+
return definition.nullable === true || definition.kind === "json"
|
|
222
|
+
? undefined
|
|
223
|
+
: `Expected ${expectedDefinition(definition)} at ${formatJsonPath(path)}.`;
|
|
224
|
+
}
|
|
225
|
+
switch (definition.kind) {
|
|
226
|
+
case "array":
|
|
227
|
+
return findArrayDefinitionIssue(value, definition, path);
|
|
228
|
+
case "boolean":
|
|
229
|
+
return typeof value === "boolean"
|
|
230
|
+
? undefined
|
|
231
|
+
: `Expected boolean at ${formatJsonPath(path)}.`;
|
|
232
|
+
case "enum":
|
|
233
|
+
return definition.enumValues.includes(value)
|
|
234
|
+
? undefined
|
|
235
|
+
: `Expected one of ${definition.enumValues.join(", ")} at ${formatJsonPath(path)}.`;
|
|
236
|
+
case "json":
|
|
237
|
+
return undefined;
|
|
238
|
+
case "number":
|
|
239
|
+
return findNumberDefinitionIssue(value, definition, path);
|
|
240
|
+
case "string":
|
|
241
|
+
return findStringDefinitionIssue(value, definition, path);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function findArrayDefinitionIssue(value, definition, path) {
|
|
245
|
+
if (!Array.isArray(value)) {
|
|
246
|
+
return `Expected array at ${formatJsonPath(path)}.`;
|
|
247
|
+
}
|
|
248
|
+
if (definition.minItems !== undefined && value.length < definition.minItems) {
|
|
249
|
+
return `Expected array with at least ${definition.minItems} items at ${formatJsonPath(path)}.`;
|
|
250
|
+
}
|
|
251
|
+
if (definition.maxItems !== undefined && value.length > definition.maxItems) {
|
|
252
|
+
return `Expected array with at most ${definition.maxItems} items at ${formatJsonPath(path)}.`;
|
|
253
|
+
}
|
|
254
|
+
for (const [index, item] of value.entries()) {
|
|
255
|
+
const issue = findDefinitionIssue(item, definition.itemDefinition, [...path, String(index)]);
|
|
256
|
+
if (issue !== undefined) {
|
|
257
|
+
return issue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
function findNumberDefinitionIssue(value, definition, path) {
|
|
263
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
264
|
+
return `Expected ${definition.jsonType === "integer" ? "integer" : "number"} at ${formatJsonPath(path)}.`;
|
|
265
|
+
}
|
|
266
|
+
if (definition.jsonType === "integer" && !Number.isInteger(value)) {
|
|
267
|
+
return `Expected integer at ${formatJsonPath(path)}.`;
|
|
268
|
+
}
|
|
269
|
+
if (definition.minimum !== undefined && value < definition.minimum) {
|
|
270
|
+
return `Expected number greater than or equal to ${definition.minimum} at ${formatJsonPath(path)}.`;
|
|
271
|
+
}
|
|
272
|
+
if (definition.maximum !== undefined && value > definition.maximum) {
|
|
273
|
+
return `Expected number less than or equal to ${definition.maximum} at ${formatJsonPath(path)}.`;
|
|
274
|
+
}
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
function findStringDefinitionIssue(value, definition, path) {
|
|
278
|
+
if (typeof value !== "string") {
|
|
279
|
+
return `Expected string at ${formatJsonPath(path)}.`;
|
|
280
|
+
}
|
|
281
|
+
if (definition.minLength !== undefined && value.length < definition.minLength) {
|
|
282
|
+
return `Expected string with length at least ${definition.minLength} at ${formatJsonPath(path)}.`;
|
|
283
|
+
}
|
|
284
|
+
if (definition.maxLength !== undefined && value.length > definition.maxLength) {
|
|
285
|
+
return `Expected string with length at most ${definition.maxLength} at ${formatJsonPath(path)}.`;
|
|
286
|
+
}
|
|
287
|
+
if (definition.pattern !== undefined) {
|
|
288
|
+
const pattern = compileDefinitionPattern(definition.pattern);
|
|
289
|
+
if (pattern === undefined || !pattern.test(value)) {
|
|
290
|
+
return `Expected string matching pattern ${definition.pattern} at ${formatJsonPath(path)}.`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
function compileDefinitionPattern(pattern) {
|
|
296
|
+
try {
|
|
297
|
+
return new RegExp(pattern, "u");
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function expectedDefinition(definition) {
|
|
304
|
+
return definition.kind === "number" && definition.jsonType === "integer"
|
|
305
|
+
? "integer"
|
|
306
|
+
: definition.kind;
|
|
307
|
+
}
|
|
308
|
+
function formatJsonPath(path) {
|
|
309
|
+
if (path.length === 0) {
|
|
310
|
+
return "the JSON array";
|
|
311
|
+
}
|
|
312
|
+
return path.map((segment) => `[${segment}]`).join("");
|
|
313
|
+
}
|
|
211
314
|
export function renderRequestShape(requestFields, sectionRenders, optionalSections) {
|
|
212
315
|
const lines = [];
|
|
213
316
|
for (const section of REQUEST_PARAM_SECTIONS) {
|