@codemation/core-nodes 0.6.0 → 0.7.1
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 +31 -0
- package/dist/index.cjs +81 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +81 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/http/HttpBodyBuilder.ts +45 -17
- package/src/http/HttpRequestExecutor.ts +4 -1
- package/src/http/httpRequest.types.ts +27 -0
- package/src/nodes/HttpRequestNodeFactory.ts +69 -1
- package/src/nodes/SubWorkflowNode.ts +4 -0
- package/src/nodes/httpRequest.ts +44 -0
- package/src/nodes/subWorkflow.ts +1 -0
package/dist/index.d.cts
CHANGED
|
@@ -942,6 +942,15 @@ interface NodeExecutionStatePublisher {
|
|
|
942
942
|
error: Error;
|
|
943
943
|
}): Promise<void>;
|
|
944
944
|
appendConnectionInvocation(args: ConnectionInvocationAppendArgs): Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* Annotates the current snapshot for `nodeId` with the id of the child run spawned by a
|
|
947
|
+
* SubWorkflow invocation. Called from `SubWorkflowNode.execute` after `runById` resolves.
|
|
948
|
+
* The engine's subsequent `markCompleted` call preserves the value via `previous.childRunId`.
|
|
949
|
+
*/
|
|
950
|
+
setChildRunId?(args: {
|
|
951
|
+
nodeId: NodeId;
|
|
952
|
+
childRunId: RunId;
|
|
953
|
+
}): Promise<void>;
|
|
945
954
|
}
|
|
946
955
|
type BinaryBody = ReadableStream<Uint8Array> | AsyncIterable<Uint8Array> | Uint8Array | ArrayBuffer;
|
|
947
956
|
interface BinaryStorageReadResult {
|
|
@@ -1398,6 +1407,15 @@ type HttpBodySpec = Readonly<{
|
|
|
1398
1407
|
kind: "multipart";
|
|
1399
1408
|
fields: Readonly<Record<string, string>>;
|
|
1400
1409
|
binaries?: Readonly<Record<string, BinaryRef>>;
|
|
1410
|
+
}> | Readonly<{
|
|
1411
|
+
/**
|
|
1412
|
+
* Send raw bytes from a binary slot as the request body.
|
|
1413
|
+
* The binary attachment's `mimeType` is used as `Content-Type` unless
|
|
1414
|
+
* the request `headers` map already contains `content-type`.
|
|
1415
|
+
*/
|
|
1416
|
+
kind: "binary";
|
|
1417
|
+
/** Key into `item.binary` to read the request body bytes from. */
|
|
1418
|
+
slot: string;
|
|
1401
1419
|
}>;
|
|
1402
1420
|
/**
|
|
1403
1421
|
* Session interface that credential types implement.
|
|
@@ -1429,6 +1447,15 @@ type HttpRequestSpec = Readonly<{
|
|
|
1429
1447
|
mode: "auto" | "always" | "never";
|
|
1430
1448
|
binaryName: string;
|
|
1431
1449
|
}>;
|
|
1450
|
+
/**
|
|
1451
|
+
* When set to `"binary"`, the response body is written to a binary slot
|
|
1452
|
+
* instead of being parsed as JSON/text. Overrides `download` mode.
|
|
1453
|
+
*/
|
|
1454
|
+
responseFormat?: "json" | "text" | "binary";
|
|
1455
|
+
/** Binary slot name for the response body when `responseFormat === "binary"`. Defaults to `"response"`. */
|
|
1456
|
+
responseBinarySlot?: string;
|
|
1457
|
+
/** Maximum allowed response size in bytes (checked against Content-Length before allocating). Defaults to 100 MiB. */
|
|
1458
|
+
responseSizeCapBytes?: number;
|
|
1432
1459
|
/** Execution context — needed for binary attach. */
|
|
1433
1460
|
ctx: NodeExecutionContext<RunnableNodeConfig<unknown, unknown>>;
|
|
1434
1461
|
}>;
|
|
@@ -1446,6 +1473,14 @@ type HttpRequestResult = Readonly<{
|
|
|
1446
1473
|
json?: unknown;
|
|
1447
1474
|
text?: string;
|
|
1448
1475
|
bodyBinaryName?: string;
|
|
1476
|
+
/** Set when `responseFormat === "binary"`. Name of the binary slot the response body was written to. */
|
|
1477
|
+
binarySlot?: string;
|
|
1478
|
+
/** Set when `responseFormat === "binary"`. The MIME type of the stored response. */
|
|
1479
|
+
contentType?: string;
|
|
1480
|
+
/** Set when `responseFormat === "binary"`. Size in bytes of the stored response. */
|
|
1481
|
+
size?: number;
|
|
1482
|
+
/** Set when `responseFormat === "binary"`. Filename inferred from URL or Content-Disposition. */
|
|
1483
|
+
filename?: string;
|
|
1449
1484
|
}>;
|
|
1450
1485
|
//#endregion
|
|
1451
1486
|
//#region src/credentials/ApiKeyCredentialType.d.ts
|
|
@@ -2300,6 +2335,7 @@ declare class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
|
|
|
2300
2335
|
readonly outputPorts: readonly ["main"];
|
|
2301
2336
|
execute(args: RunnableNodeExecuteArgs<HttpRequest<any, any>>): Promise<unknown>;
|
|
2302
2337
|
private executeItem;
|
|
2338
|
+
private handleBinaryResponse;
|
|
2303
2339
|
private resolveCredential;
|
|
2304
2340
|
private resolveUrl;
|
|
2305
2341
|
private asRecord;
|
|
@@ -2325,6 +2361,14 @@ type HttpRequestOutputJson = Readonly<{
|
|
|
2325
2361
|
json?: unknown;
|
|
2326
2362
|
text?: string;
|
|
2327
2363
|
bodyBinaryName?: string;
|
|
2364
|
+
/** Set when `responseFormat === "binary"`. Name of the binary slot the response was stored in. */
|
|
2365
|
+
binarySlot?: string;
|
|
2366
|
+
/** Set when `responseFormat === "binary"`. MIME type of the stored response. */
|
|
2367
|
+
contentType?: string;
|
|
2368
|
+
/** Set when `responseFormat === "binary"`. Size in bytes of the stored response. */
|
|
2369
|
+
size?: number;
|
|
2370
|
+
/** Set when `responseFormat === "binary"`. Filename inferred from URL or Content-Disposition. */
|
|
2371
|
+
filename?: string;
|
|
2328
2372
|
}>;
|
|
2329
2373
|
/**
|
|
2330
2374
|
* The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
|
|
@@ -2359,6 +2403,27 @@ declare class HttpRequest<TInputJson$1 = Readonly<{
|
|
|
2359
2403
|
credentialSlot?: string;
|
|
2360
2404
|
binaryName?: string;
|
|
2361
2405
|
downloadMode?: HttpRequestDownloadMode;
|
|
2406
|
+
/**
|
|
2407
|
+
* Controls how the response body is handled.
|
|
2408
|
+
* - `"json"` / `"text"`: existing behaviour (parse + emit on `item.json`).
|
|
2409
|
+
* - `"binary"`: read the response as raw bytes and store via `ctx.binary.attach`.
|
|
2410
|
+
* The output JSON contains `{ status, headers, binarySlot, contentType, size, filename }`
|
|
2411
|
+
* but NOT the raw bytes. Use `responseBinarySlot` to name the slot (default `"response"`).
|
|
2412
|
+
*
|
|
2413
|
+
* When omitted, the existing `downloadMode` logic applies (backward-compatible).
|
|
2414
|
+
*/
|
|
2415
|
+
responseFormat?: "json" | "text" | "binary";
|
|
2416
|
+
/**
|
|
2417
|
+
* Name of the binary slot to write the response body into when `responseFormat === "binary"`.
|
|
2418
|
+
* Defaults to `"response"`.
|
|
2419
|
+
*/
|
|
2420
|
+
responseBinarySlot?: string;
|
|
2421
|
+
/**
|
|
2422
|
+
* Maximum response size in bytes for binary mode. Checked against the `Content-Length`
|
|
2423
|
+
* response header before allocating memory. Defaults to 100 MiB (104857600).
|
|
2424
|
+
* Requests whose `Content-Length` exceeds this cap are rejected before the body is read.
|
|
2425
|
+
*/
|
|
2426
|
+
responseSizeCapBytes?: number;
|
|
2362
2427
|
id?: string;
|
|
2363
2428
|
}>;
|
|
2364
2429
|
readonly retryPolicy: RetryPolicySpec;
|
|
@@ -2392,6 +2457,27 @@ declare class HttpRequest<TInputJson$1 = Readonly<{
|
|
|
2392
2457
|
credentialSlot?: string;
|
|
2393
2458
|
binaryName?: string;
|
|
2394
2459
|
downloadMode?: HttpRequestDownloadMode;
|
|
2460
|
+
/**
|
|
2461
|
+
* Controls how the response body is handled.
|
|
2462
|
+
* - `"json"` / `"text"`: existing behaviour (parse + emit on `item.json`).
|
|
2463
|
+
* - `"binary"`: read the response as raw bytes and store via `ctx.binary.attach`.
|
|
2464
|
+
* The output JSON contains `{ status, headers, binarySlot, contentType, size, filename }`
|
|
2465
|
+
* but NOT the raw bytes. Use `responseBinarySlot` to name the slot (default `"response"`).
|
|
2466
|
+
*
|
|
2467
|
+
* When omitted, the existing `downloadMode` logic applies (backward-compatible).
|
|
2468
|
+
*/
|
|
2469
|
+
responseFormat?: "json" | "text" | "binary";
|
|
2470
|
+
/**
|
|
2471
|
+
* Name of the binary slot to write the response body into when `responseFormat === "binary"`.
|
|
2472
|
+
* Defaults to `"response"`.
|
|
2473
|
+
*/
|
|
2474
|
+
responseBinarySlot?: string;
|
|
2475
|
+
/**
|
|
2476
|
+
* Maximum response size in bytes for binary mode. Checked against the `Content-Length`
|
|
2477
|
+
* response header before allocating memory. Defaults to 100 MiB (104857600).
|
|
2478
|
+
* Requests whose `Content-Length` exceeds this cap are rejected before the body is read.
|
|
2479
|
+
*/
|
|
2480
|
+
responseSizeCapBytes?: number;
|
|
2395
2481
|
id?: string;
|
|
2396
2482
|
}>, retryPolicy?: RetryPolicySpec);
|
|
2397
2483
|
get id(): string | undefined;
|
|
@@ -2399,6 +2485,9 @@ declare class HttpRequest<TInputJson$1 = Readonly<{
|
|
|
2399
2485
|
get urlField(): string;
|
|
2400
2486
|
get binaryName(): string;
|
|
2401
2487
|
get downloadMode(): HttpRequestDownloadMode;
|
|
2488
|
+
get responseFormat(): "json" | "text" | "binary" | undefined;
|
|
2489
|
+
get responseBinarySlot(): string;
|
|
2490
|
+
get responseSizeCapBytes(): number;
|
|
2402
2491
|
getCredentialRequirements(): ReadonlyArray<CredentialRequirement>;
|
|
2403
2492
|
}
|
|
2404
2493
|
//#endregion
|
|
@@ -2732,6 +2821,7 @@ declare class SubWorkflow<TInputJson$1 = unknown, TOutputJson$1 = unknown> imple
|
|
|
2732
2821
|
readonly id?: string | undefined;
|
|
2733
2822
|
readonly kind: "node";
|
|
2734
2823
|
readonly type: TypeToken<unknown>;
|
|
2824
|
+
readonly icon = "lucide:workflow";
|
|
2735
2825
|
constructor(name: string, workflowId: string, upstreamRefs?: Array<{
|
|
2736
2826
|
nodeId: NodeId;
|
|
2737
2827
|
} | UpstreamRefPlaceholder> | undefined, startAt?: NodeId | undefined, id?: string | undefined);
|
package/dist/index.d.ts
CHANGED
|
@@ -942,6 +942,15 @@ interface NodeExecutionStatePublisher {
|
|
|
942
942
|
error: Error;
|
|
943
943
|
}): Promise<void>;
|
|
944
944
|
appendConnectionInvocation(args: ConnectionInvocationAppendArgs): Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* Annotates the current snapshot for `nodeId` with the id of the child run spawned by a
|
|
947
|
+
* SubWorkflow invocation. Called from `SubWorkflowNode.execute` after `runById` resolves.
|
|
948
|
+
* The engine's subsequent `markCompleted` call preserves the value via `previous.childRunId`.
|
|
949
|
+
*/
|
|
950
|
+
setChildRunId?(args: {
|
|
951
|
+
nodeId: NodeId;
|
|
952
|
+
childRunId: RunId;
|
|
953
|
+
}): Promise<void>;
|
|
945
954
|
}
|
|
946
955
|
type BinaryBody = ReadableStream<Uint8Array> | AsyncIterable<Uint8Array> | Uint8Array | ArrayBuffer;
|
|
947
956
|
interface BinaryStorageReadResult {
|
|
@@ -1398,6 +1407,15 @@ type HttpBodySpec = Readonly<{
|
|
|
1398
1407
|
kind: "multipart";
|
|
1399
1408
|
fields: Readonly<Record<string, string>>;
|
|
1400
1409
|
binaries?: Readonly<Record<string, BinaryRef>>;
|
|
1410
|
+
}> | Readonly<{
|
|
1411
|
+
/**
|
|
1412
|
+
* Send raw bytes from a binary slot as the request body.
|
|
1413
|
+
* The binary attachment's `mimeType` is used as `Content-Type` unless
|
|
1414
|
+
* the request `headers` map already contains `content-type`.
|
|
1415
|
+
*/
|
|
1416
|
+
kind: "binary";
|
|
1417
|
+
/** Key into `item.binary` to read the request body bytes from. */
|
|
1418
|
+
slot: string;
|
|
1401
1419
|
}>;
|
|
1402
1420
|
/**
|
|
1403
1421
|
* Session interface that credential types implement.
|
|
@@ -1429,6 +1447,15 @@ type HttpRequestSpec = Readonly<{
|
|
|
1429
1447
|
mode: "auto" | "always" | "never";
|
|
1430
1448
|
binaryName: string;
|
|
1431
1449
|
}>;
|
|
1450
|
+
/**
|
|
1451
|
+
* When set to `"binary"`, the response body is written to a binary slot
|
|
1452
|
+
* instead of being parsed as JSON/text. Overrides `download` mode.
|
|
1453
|
+
*/
|
|
1454
|
+
responseFormat?: "json" | "text" | "binary";
|
|
1455
|
+
/** Binary slot name for the response body when `responseFormat === "binary"`. Defaults to `"response"`. */
|
|
1456
|
+
responseBinarySlot?: string;
|
|
1457
|
+
/** Maximum allowed response size in bytes (checked against Content-Length before allocating). Defaults to 100 MiB. */
|
|
1458
|
+
responseSizeCapBytes?: number;
|
|
1432
1459
|
/** Execution context — needed for binary attach. */
|
|
1433
1460
|
ctx: NodeExecutionContext<RunnableNodeConfig<unknown, unknown>>;
|
|
1434
1461
|
}>;
|
|
@@ -1446,6 +1473,14 @@ type HttpRequestResult = Readonly<{
|
|
|
1446
1473
|
json?: unknown;
|
|
1447
1474
|
text?: string;
|
|
1448
1475
|
bodyBinaryName?: string;
|
|
1476
|
+
/** Set when `responseFormat === "binary"`. Name of the binary slot the response body was written to. */
|
|
1477
|
+
binarySlot?: string;
|
|
1478
|
+
/** Set when `responseFormat === "binary"`. The MIME type of the stored response. */
|
|
1479
|
+
contentType?: string;
|
|
1480
|
+
/** Set when `responseFormat === "binary"`. Size in bytes of the stored response. */
|
|
1481
|
+
size?: number;
|
|
1482
|
+
/** Set when `responseFormat === "binary"`. Filename inferred from URL or Content-Disposition. */
|
|
1483
|
+
filename?: string;
|
|
1449
1484
|
}>;
|
|
1450
1485
|
//#endregion
|
|
1451
1486
|
//#region src/credentials/ApiKeyCredentialType.d.ts
|
|
@@ -2300,6 +2335,7 @@ declare class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
|
|
|
2300
2335
|
readonly outputPorts: readonly ["main"];
|
|
2301
2336
|
execute(args: RunnableNodeExecuteArgs<HttpRequest<any, any>>): Promise<unknown>;
|
|
2302
2337
|
private executeItem;
|
|
2338
|
+
private handleBinaryResponse;
|
|
2303
2339
|
private resolveCredential;
|
|
2304
2340
|
private resolveUrl;
|
|
2305
2341
|
private asRecord;
|
|
@@ -2325,6 +2361,14 @@ type HttpRequestOutputJson = Readonly<{
|
|
|
2325
2361
|
json?: unknown;
|
|
2326
2362
|
text?: string;
|
|
2327
2363
|
bodyBinaryName?: string;
|
|
2364
|
+
/** Set when `responseFormat === "binary"`. Name of the binary slot the response was stored in. */
|
|
2365
|
+
binarySlot?: string;
|
|
2366
|
+
/** Set when `responseFormat === "binary"`. MIME type of the stored response. */
|
|
2367
|
+
contentType?: string;
|
|
2368
|
+
/** Set when `responseFormat === "binary"`. Size in bytes of the stored response. */
|
|
2369
|
+
size?: number;
|
|
2370
|
+
/** Set when `responseFormat === "binary"`. Filename inferred from URL or Content-Disposition. */
|
|
2371
|
+
filename?: string;
|
|
2328
2372
|
}>;
|
|
2329
2373
|
/**
|
|
2330
2374
|
* The built-in HTTP request credential type IDs accepted by the `HttpRequest` node.
|
|
@@ -2359,6 +2403,27 @@ declare class HttpRequest<TInputJson$1 = Readonly<{
|
|
|
2359
2403
|
credentialSlot?: string;
|
|
2360
2404
|
binaryName?: string;
|
|
2361
2405
|
downloadMode?: HttpRequestDownloadMode;
|
|
2406
|
+
/**
|
|
2407
|
+
* Controls how the response body is handled.
|
|
2408
|
+
* - `"json"` / `"text"`: existing behaviour (parse + emit on `item.json`).
|
|
2409
|
+
* - `"binary"`: read the response as raw bytes and store via `ctx.binary.attach`.
|
|
2410
|
+
* The output JSON contains `{ status, headers, binarySlot, contentType, size, filename }`
|
|
2411
|
+
* but NOT the raw bytes. Use `responseBinarySlot` to name the slot (default `"response"`).
|
|
2412
|
+
*
|
|
2413
|
+
* When omitted, the existing `downloadMode` logic applies (backward-compatible).
|
|
2414
|
+
*/
|
|
2415
|
+
responseFormat?: "json" | "text" | "binary";
|
|
2416
|
+
/**
|
|
2417
|
+
* Name of the binary slot to write the response body into when `responseFormat === "binary"`.
|
|
2418
|
+
* Defaults to `"response"`.
|
|
2419
|
+
*/
|
|
2420
|
+
responseBinarySlot?: string;
|
|
2421
|
+
/**
|
|
2422
|
+
* Maximum response size in bytes for binary mode. Checked against the `Content-Length`
|
|
2423
|
+
* response header before allocating memory. Defaults to 100 MiB (104857600).
|
|
2424
|
+
* Requests whose `Content-Length` exceeds this cap are rejected before the body is read.
|
|
2425
|
+
*/
|
|
2426
|
+
responseSizeCapBytes?: number;
|
|
2362
2427
|
id?: string;
|
|
2363
2428
|
}>;
|
|
2364
2429
|
readonly retryPolicy: RetryPolicySpec;
|
|
@@ -2392,6 +2457,27 @@ declare class HttpRequest<TInputJson$1 = Readonly<{
|
|
|
2392
2457
|
credentialSlot?: string;
|
|
2393
2458
|
binaryName?: string;
|
|
2394
2459
|
downloadMode?: HttpRequestDownloadMode;
|
|
2460
|
+
/**
|
|
2461
|
+
* Controls how the response body is handled.
|
|
2462
|
+
* - `"json"` / `"text"`: existing behaviour (parse + emit on `item.json`).
|
|
2463
|
+
* - `"binary"`: read the response as raw bytes and store via `ctx.binary.attach`.
|
|
2464
|
+
* The output JSON contains `{ status, headers, binarySlot, contentType, size, filename }`
|
|
2465
|
+
* but NOT the raw bytes. Use `responseBinarySlot` to name the slot (default `"response"`).
|
|
2466
|
+
*
|
|
2467
|
+
* When omitted, the existing `downloadMode` logic applies (backward-compatible).
|
|
2468
|
+
*/
|
|
2469
|
+
responseFormat?: "json" | "text" | "binary";
|
|
2470
|
+
/**
|
|
2471
|
+
* Name of the binary slot to write the response body into when `responseFormat === "binary"`.
|
|
2472
|
+
* Defaults to `"response"`.
|
|
2473
|
+
*/
|
|
2474
|
+
responseBinarySlot?: string;
|
|
2475
|
+
/**
|
|
2476
|
+
* Maximum response size in bytes for binary mode. Checked against the `Content-Length`
|
|
2477
|
+
* response header before allocating memory. Defaults to 100 MiB (104857600).
|
|
2478
|
+
* Requests whose `Content-Length` exceeds this cap are rejected before the body is read.
|
|
2479
|
+
*/
|
|
2480
|
+
responseSizeCapBytes?: number;
|
|
2395
2481
|
id?: string;
|
|
2396
2482
|
}>, retryPolicy?: RetryPolicySpec);
|
|
2397
2483
|
get id(): string | undefined;
|
|
@@ -2399,6 +2485,9 @@ declare class HttpRequest<TInputJson$1 = Readonly<{
|
|
|
2399
2485
|
get urlField(): string;
|
|
2400
2486
|
get binaryName(): string;
|
|
2401
2487
|
get downloadMode(): HttpRequestDownloadMode;
|
|
2488
|
+
get responseFormat(): "json" | "text" | "binary" | undefined;
|
|
2489
|
+
get responseBinarySlot(): string;
|
|
2490
|
+
get responseSizeCapBytes(): number;
|
|
2402
2491
|
getCredentialRequirements(): ReadonlyArray<CredentialRequirement>;
|
|
2403
2492
|
}
|
|
2404
2493
|
//#endregion
|
|
@@ -2732,6 +2821,7 @@ declare class SubWorkflow<TInputJson$1 = unknown, TOutputJson$1 = unknown> imple
|
|
|
2732
2821
|
readonly id?: string | undefined;
|
|
2733
2822
|
readonly kind: "node";
|
|
2734
2823
|
readonly type: TypeToken<unknown>;
|
|
2824
|
+
readonly icon = "lucide:workflow";
|
|
2735
2825
|
constructor(name: string, workflowId: string, upstreamRefs?: Array<{
|
|
2736
2826
|
nodeId: NodeId;
|
|
2737
2827
|
} | UpstreamRefPlaceholder> | undefined, startAt?: NodeId | undefined, id?: string | undefined);
|
package/dist/index.js
CHANGED
|
@@ -295,7 +295,8 @@ var HttpRequestExecutor = class {
|
|
|
295
295
|
...credentialDelta.query ?? {}
|
|
296
296
|
};
|
|
297
297
|
const encodedBody = await this.bodyBuilder.build(spec.body, item, spec.ctx);
|
|
298
|
-
|
|
298
|
+
const hasExplicitContentType = Object.keys(mergedHeaders).some((k) => k.toLowerCase() === "content-type");
|
|
299
|
+
if (encodedBody && encodedBody.contentType && !hasExplicitContentType) mergedHeaders["content-type"] = encodedBody.contentType;
|
|
299
300
|
return {
|
|
300
301
|
url: this.urlBuilder.build(spec.url, mergedQuery),
|
|
301
302
|
init: {
|
|
@@ -394,21 +395,7 @@ var HttpBodyBuilder = class {
|
|
|
394
395
|
if (attachment) {
|
|
395
396
|
const readResult = await ctx.binary.openReadStream(attachment);
|
|
396
397
|
if (readResult) {
|
|
397
|
-
const
|
|
398
|
-
const chunks = [];
|
|
399
|
-
let done = false;
|
|
400
|
-
while (!done) {
|
|
401
|
-
const result = await reader.read();
|
|
402
|
-
done = result.done;
|
|
403
|
-
if (result.value) chunks.push(result.value);
|
|
404
|
-
}
|
|
405
|
-
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
406
|
-
const merged = new Uint8Array(totalLength);
|
|
407
|
-
let offset = 0;
|
|
408
|
-
for (const chunk of chunks) {
|
|
409
|
-
merged.set(chunk, offset);
|
|
410
|
-
offset += chunk.length;
|
|
411
|
-
}
|
|
398
|
+
const merged = await this.readStreamToBuffer(readResult.body);
|
|
412
399
|
const blob = new Blob([merged], { type: attachment.mimeType });
|
|
413
400
|
formData.append(fieldName, blob, attachment.filename ?? binaryRef);
|
|
414
401
|
}
|
|
@@ -419,6 +406,34 @@ var HttpBodyBuilder = class {
|
|
|
419
406
|
contentType: ""
|
|
420
407
|
};
|
|
421
408
|
}
|
|
409
|
+
if (spec.kind === "binary") {
|
|
410
|
+
const attachment = item.binary?.[spec.slot];
|
|
411
|
+
if (!attachment) throw new Error(`HttpRequest bodyFormat "binary": no binary attachment found at slot "${spec.slot}". Ensure a previous node attached binary data at that slot.`);
|
|
412
|
+
const readResult = await ctx.binary.openReadStream(attachment);
|
|
413
|
+
if (!readResult) throw new Error(`HttpRequest bodyFormat "binary": could not open read stream for slot "${spec.slot}".`);
|
|
414
|
+
return {
|
|
415
|
+
body: readResult.body,
|
|
416
|
+
contentType: attachment.mimeType
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async readStreamToBuffer(stream) {
|
|
421
|
+
const reader = stream.getReader();
|
|
422
|
+
const chunks = [];
|
|
423
|
+
let done = false;
|
|
424
|
+
while (!done) {
|
|
425
|
+
const result = await reader.read();
|
|
426
|
+
done = result.done;
|
|
427
|
+
if (result.value) chunks.push(result.value);
|
|
428
|
+
}
|
|
429
|
+
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
430
|
+
const merged = new Uint8Array(new ArrayBuffer(totalLength));
|
|
431
|
+
let offset = 0;
|
|
432
|
+
for (const chunk of chunks) {
|
|
433
|
+
merged.set(chunk, offset);
|
|
434
|
+
offset += chunk.length;
|
|
435
|
+
}
|
|
436
|
+
return merged;
|
|
422
437
|
}
|
|
423
438
|
};
|
|
424
439
|
|
|
@@ -6512,12 +6527,16 @@ let HttpRequestNode = class HttpRequestNode$1 {
|
|
|
6512
6527
|
mode: ctx.config.downloadMode,
|
|
6513
6528
|
binaryName: ctx.config.binaryName
|
|
6514
6529
|
},
|
|
6530
|
+
responseFormat: ctx.config.responseFormat,
|
|
6531
|
+
responseBinarySlot: ctx.config.responseBinarySlot,
|
|
6532
|
+
responseSizeCapBytes: ctx.config.responseSizeCapBytes,
|
|
6515
6533
|
ctx
|
|
6516
6534
|
};
|
|
6517
6535
|
const { url: resolvedUrl, init } = await new HttpRequestExecutor(globalThis.fetch, new HttpBodyBuilder(), new HttpUrlBuilder()).buildRequest(spec, item);
|
|
6518
6536
|
const response = await globalThis.fetch(resolvedUrl, init);
|
|
6519
6537
|
const headers = this.readHeaders(response.headers);
|
|
6520
6538
|
const mimeType = this.resolveMimeType(headers);
|
|
6539
|
+
if (ctx.config.responseFormat === "binary") return await this.handleBinaryResponse(response, resolvedUrl, headers, mimeType, ctx);
|
|
6521
6540
|
const binaryName = ctx.config.binaryName;
|
|
6522
6541
|
if (this.shouldAttachBody(ctx.config.downloadMode, mimeType)) {
|
|
6523
6542
|
const outputJson = {
|
|
@@ -6561,6 +6580,36 @@ let HttpRequestNode = class HttpRequestNode$1 {
|
|
|
6561
6580
|
...text !== void 0 ? { text } : {}
|
|
6562
6581
|
} };
|
|
6563
6582
|
}
|
|
6583
|
+
async handleBinaryResponse(response, resolvedUrl, headers, mimeType, ctx) {
|
|
6584
|
+
const slotName = ctx.config.responseBinarySlot;
|
|
6585
|
+
const sizeCap = ctx.config.responseSizeCapBytes;
|
|
6586
|
+
const contentLengthHeader = headers["content-length"];
|
|
6587
|
+
if (contentLengthHeader) {
|
|
6588
|
+
const declaredSize = parseInt(contentLengthHeader, 10);
|
|
6589
|
+
if (!isNaN(declaredSize) && declaredSize > sizeCap) throw new Error(`HttpRequest responseFormat "binary": response Content-Length (${declaredSize} bytes) exceeds responseSizeCapBytes (${sizeCap} bytes).`);
|
|
6590
|
+
}
|
|
6591
|
+
const filename = this.resolveFilename(resolvedUrl, headers);
|
|
6592
|
+
const attachment = await ctx.binary.attach({
|
|
6593
|
+
name: slotName,
|
|
6594
|
+
body: response.body ? response.body : new Uint8Array(await response.arrayBuffer()),
|
|
6595
|
+
mimeType,
|
|
6596
|
+
filename
|
|
6597
|
+
});
|
|
6598
|
+
let outputItem = { json: {
|
|
6599
|
+
url: resolvedUrl,
|
|
6600
|
+
method: ctx.config.method,
|
|
6601
|
+
ok: response.ok,
|
|
6602
|
+
status: response.status,
|
|
6603
|
+
statusText: response.statusText,
|
|
6604
|
+
headers,
|
|
6605
|
+
binarySlot: slotName,
|
|
6606
|
+
contentType: mimeType,
|
|
6607
|
+
size: attachment.size,
|
|
6608
|
+
...filename !== void 0 ? { filename } : {}
|
|
6609
|
+
} };
|
|
6610
|
+
outputItem = ctx.binary.withAttachment(outputItem, slotName, attachment);
|
|
6611
|
+
return outputItem;
|
|
6612
|
+
}
|
|
6564
6613
|
async resolveCredential(ctx) {
|
|
6565
6614
|
const slotKey = ctx.config.args.credentialSlot;
|
|
6566
6615
|
if (!slotKey) return;
|
|
@@ -6632,6 +6681,8 @@ const HTTP_REQUEST_ACCEPTED_CREDENTIAL_TYPES = [
|
|
|
6632
6681
|
basicAuthCredentialType.definition.typeId,
|
|
6633
6682
|
oauth2ClientCredentialsType.definition.typeId
|
|
6634
6683
|
];
|
|
6684
|
+
/** Default maximum response size for binary mode: 100 MiB. */
|
|
6685
|
+
const DEFAULT_RESPONSE_SIZE_CAP_BYTES = 100 * 1024 * 1024;
|
|
6635
6686
|
var HttpRequest = class {
|
|
6636
6687
|
kind = "node";
|
|
6637
6688
|
type = HttpRequestNode;
|
|
@@ -6657,6 +6708,15 @@ var HttpRequest = class {
|
|
|
6657
6708
|
get downloadMode() {
|
|
6658
6709
|
return this.args.downloadMode ?? "auto";
|
|
6659
6710
|
}
|
|
6711
|
+
get responseFormat() {
|
|
6712
|
+
return this.args.responseFormat;
|
|
6713
|
+
}
|
|
6714
|
+
get responseBinarySlot() {
|
|
6715
|
+
return this.args.responseBinarySlot ?? "response";
|
|
6716
|
+
}
|
|
6717
|
+
get responseSizeCapBytes() {
|
|
6718
|
+
return this.args.responseSizeCapBytes ?? DEFAULT_RESPONSE_SIZE_CAP_BYTES;
|
|
6719
|
+
}
|
|
6660
6720
|
getCredentialRequirements() {
|
|
6661
6721
|
if (!this.args.credentialSlot) return [];
|
|
6662
6722
|
return [{
|
|
@@ -7150,6 +7210,10 @@ let SubWorkflowNode = class SubWorkflowNode$1 {
|
|
|
7150
7210
|
engineMaxSubworkflowDepth: args.ctx.engineMaxSubworkflowDepth
|
|
7151
7211
|
}
|
|
7152
7212
|
});
|
|
7213
|
+
await args.ctx.nodeState?.setChildRunId?.({
|
|
7214
|
+
nodeId: args.ctx.nodeId,
|
|
7215
|
+
childRunId: result.runId
|
|
7216
|
+
});
|
|
7153
7217
|
if (result.status !== "completed") throw new Error(`Subworkflow ${args.ctx.config.workflowId} did not complete (status=${result.status})`);
|
|
7154
7218
|
const out = [];
|
|
7155
7219
|
for (const produced of result.outputs) {
|
|
@@ -7181,6 +7245,7 @@ SubWorkflowNode = __decorate([
|
|
|
7181
7245
|
var SubWorkflow = class {
|
|
7182
7246
|
kind = "node";
|
|
7183
7247
|
type = SubWorkflowNode;
|
|
7248
|
+
icon = "lucide:workflow";
|
|
7184
7249
|
constructor(name, workflowId, upstreamRefs, startAt, id) {
|
|
7185
7250
|
this.name = name;
|
|
7186
7251
|
this.workflowId = workflowId;
|