@hostwebhook/node-sdk 0.1.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/dist/code-runner.d.ts +20 -0
- package/dist/code-runner.js +138 -0
- package/dist/contratos.d.ts +121 -0
- package/dist/contratos.js +24 -0
- package/dist/dto/output-node.dto.d.ts +19 -0
- package/dist/dto/output-node.dto.js +96 -0
- package/dist/ensure-meta.d.ts +22 -0
- package/dist/ensure-meta.js +35 -0
- package/dist/execute-with-iteration.d.ts +18 -0
- package/dist/execute-with-iteration.js +66 -0
- package/dist/filter-utils.d.ts +22 -0
- package/dist/filter-utils.js +178 -0
- package/dist/handler-helpers.d.ts +21 -0
- package/dist/handler-helpers.js +53 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +73 -0
- package/dist/log-metadata.d.ts +191 -0
- package/dist/log-metadata.js +375 -0
- package/dist/node-dispatch.registry.d.ts +32 -0
- package/dist/node-dispatch.registry.js +45 -0
- package/dist/node-executors.d.ts +299 -0
- package/dist/node-executors.js +555 -0
- package/dist/node-lifecycle.d.ts +399 -0
- package/dist/node-lifecycle.js +782 -0
- package/dist/normalize-nodes.d.ts +18 -0
- package/dist/normalize-nodes.js +22 -0
- package/dist/output-node-ref.schema.d.ts +82 -0
- package/dist/output-node-ref.schema.js +90 -0
- package/dist/output-webhook-scope.d.ts +36 -0
- package/dist/output-webhook-scope.js +42 -0
- package/dist/payload-preview.d.ts +10 -0
- package/dist/payload-preview.js +39 -0
- package/dist/pipeline.constants.d.ts +29 -0
- package/dist/pipeline.constants.js +51 -0
- package/dist/pre-request-pool.d.ts +58 -0
- package/dist/pre-request-pool.js +308 -0
- package/dist/pre-request-runner-source.d.ts +28 -0
- package/dist/pre-request-runner-source.js +411 -0
- package/dist/regex-de-inquilino.d.ts +15 -0
- package/dist/regex-de-inquilino.js +98 -0
- package/dist/request-context.d.ts +18 -0
- package/dist/request-context.js +34 -0
- package/dist/retry-transient.d.ts +54 -0
- package/dist/retry-transient.js +67 -0
- package/dist/retry-utils.d.ts +17 -0
- package/dist/retry-utils.js +23 -0
- package/dist/schema-validator-utils.d.ts +9 -0
- package/dist/schema-validator-utils.js +140 -0
- package/dist/ssrf-guard.d.ts +202 -0
- package/dist/ssrf-guard.js +917 -0
- package/dist/swallow.d.ts +52 -0
- package/dist/swallow.js +55 -0
- package/dist/template-render.d.ts +33 -0
- package/dist/template-render.js +43 -0
- package/dist/try-parse.d.ts +41 -0
- package/dist/try-parse.js +69 -0
- package/dist/workspace-payloads.d.ts +66 -0
- package/dist/workspace-payloads.js +496 -0
- package/package.json +35 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getNestedValue = getNestedValue;
|
|
4
|
+
exports.evaluateFilters = evaluateFilters;
|
|
5
|
+
exports.evaluateFiltersIterable = evaluateFiltersIterable;
|
|
6
|
+
const template_engine_1 = require("@hostwebhook/template-engine");
|
|
7
|
+
const regex_de_inquilino_1 = require("./regex-de-inquilino");
|
|
8
|
+
function getNestedValue(obj, path) {
|
|
9
|
+
return path.split('.').reduce((curr, key) => {
|
|
10
|
+
if (curr !== null && typeof curr === 'object') {
|
|
11
|
+
return curr[key];
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}, obj);
|
|
15
|
+
}
|
|
16
|
+
/** Try to compare as numbers; if either is NaN, try as dates; if still NaN, compare as strings. */
|
|
17
|
+
function smartCompare(a, b) {
|
|
18
|
+
const numA = Number(a);
|
|
19
|
+
const numB = Number(b ?? 0);
|
|
20
|
+
if (!isNaN(numA) && !isNaN(numB))
|
|
21
|
+
return { left: numA, right: numB };
|
|
22
|
+
// Try date comparison (ISO strings, date strings)
|
|
23
|
+
const dA = new Date(String(a)).getTime();
|
|
24
|
+
const dB = new Date(String(b)).getTime();
|
|
25
|
+
if (!isNaN(dA) && !isNaN(dB))
|
|
26
|
+
return { left: dA, right: dB };
|
|
27
|
+
// Fallback: lexicographic string comparison via charCodeAt sum won't work — use localeCompare
|
|
28
|
+
const cmp = String(a).localeCompare(String(b));
|
|
29
|
+
return { left: cmp, right: 0 };
|
|
30
|
+
}
|
|
31
|
+
function matchFilter(f, payload, opts) {
|
|
32
|
+
// Resolve field: supports {{$now}}, {{payload.field}}, or plain dot notation
|
|
33
|
+
const fieldStr = String(f.field ?? '');
|
|
34
|
+
const val = /^\{\{/.test(fieldStr)
|
|
35
|
+
? (0, template_engine_1.resolveInput)(fieldStr, payload, opts)
|
|
36
|
+
: getNestedValue(payload, (0, template_engine_1.resolveFieldPath)(fieldStr));
|
|
37
|
+
const fv = (0, template_engine_1.resolveInput)(f.value, payload, opts);
|
|
38
|
+
switch (f.operator) {
|
|
39
|
+
// String
|
|
40
|
+
case 'eq':
|
|
41
|
+
return String(val) === String(fv ?? '');
|
|
42
|
+
case 'neq':
|
|
43
|
+
return String(val) !== String(fv ?? '');
|
|
44
|
+
case 'contains':
|
|
45
|
+
return typeof val === 'string' && val.includes(String(fv ?? ''));
|
|
46
|
+
case 'not_contains':
|
|
47
|
+
return typeof val === 'string' && !val.includes(String(fv ?? ''));
|
|
48
|
+
case 'starts_with':
|
|
49
|
+
return typeof val === 'string' && val.startsWith(String(fv ?? ''));
|
|
50
|
+
case 'not_starts_with':
|
|
51
|
+
return typeof val === 'string' && !val.startsWith(String(fv ?? ''));
|
|
52
|
+
case 'ends_with':
|
|
53
|
+
return typeof val === 'string' && val.endsWith(String(fv ?? ''));
|
|
54
|
+
case 'not_ends_with':
|
|
55
|
+
return typeof val === 'string' && !val.endsWith(String(fv ?? ''));
|
|
56
|
+
case 'is_empty':
|
|
57
|
+
return val === undefined || val === null || val === '';
|
|
58
|
+
case 'is_not_empty':
|
|
59
|
+
return val !== undefined && val !== null && val !== '';
|
|
60
|
+
case 'matches_regex':
|
|
61
|
+
/* El patron lo escribe el inquilino y la cadena viene del payload, que
|
|
62
|
+
entra por el ingress publico: con el motor de JavaScript, `(a+)+$`
|
|
63
|
+
contra treinta caracteres deja el hilo girando. Ver
|
|
64
|
+
`regex-de-inquilino.ts`. */
|
|
65
|
+
return (0, regex_de_inquilino_1.casaRegexDeInquilino)(String(fv ?? ''), val, 'matches_regex');
|
|
66
|
+
// Number / Date (smart comparison)
|
|
67
|
+
case 'gt': {
|
|
68
|
+
const c = smartCompare(val, fv);
|
|
69
|
+
return c.left > c.right;
|
|
70
|
+
}
|
|
71
|
+
case 'gte': {
|
|
72
|
+
const c = smartCompare(val, fv);
|
|
73
|
+
return c.left >= c.right;
|
|
74
|
+
}
|
|
75
|
+
case 'lt': {
|
|
76
|
+
const c = smartCompare(val, fv);
|
|
77
|
+
return c.left < c.right;
|
|
78
|
+
}
|
|
79
|
+
case 'lte': {
|
|
80
|
+
const c = smartCompare(val, fv);
|
|
81
|
+
return c.left <= c.right;
|
|
82
|
+
}
|
|
83
|
+
// Date comparison
|
|
84
|
+
case 'date_before': {
|
|
85
|
+
const dA = new Date(String(val)).getTime();
|
|
86
|
+
const dB = new Date(String(fv)).getTime();
|
|
87
|
+
return !isNaN(dA) && !isNaN(dB) && dA < dB;
|
|
88
|
+
}
|
|
89
|
+
case 'date_after': {
|
|
90
|
+
const dA = new Date(String(val)).getTime();
|
|
91
|
+
const dB = new Date(String(fv)).getTime();
|
|
92
|
+
return !isNaN(dA) && !isNaN(dB) && dA > dB;
|
|
93
|
+
}
|
|
94
|
+
case 'date_eq': {
|
|
95
|
+
const dA = new Date(String(val)).toISOString().slice(0, 10);
|
|
96
|
+
const dB = new Date(String(fv)).toISOString().slice(0, 10);
|
|
97
|
+
return dA === dB;
|
|
98
|
+
}
|
|
99
|
+
// Boolean
|
|
100
|
+
case 'is_true':
|
|
101
|
+
return val === true || val === 'true' || val === 1;
|
|
102
|
+
case 'is_false':
|
|
103
|
+
return val === false || val === 'false' || val === 0;
|
|
104
|
+
// Existence
|
|
105
|
+
case 'exists':
|
|
106
|
+
return val !== undefined && val !== null;
|
|
107
|
+
case 'not_exists':
|
|
108
|
+
return val === undefined || val === null;
|
|
109
|
+
// Array
|
|
110
|
+
case 'array_contains':
|
|
111
|
+
return (Array.isArray(val) && val.some((v) => String(v) === String(fv ?? '')));
|
|
112
|
+
case 'array_not_contains':
|
|
113
|
+
return (Array.isArray(val) && !val.some((v) => String(v) === String(fv ?? '')));
|
|
114
|
+
case 'array_empty':
|
|
115
|
+
return Array.isArray(val) && val.length === 0;
|
|
116
|
+
case 'array_not_empty':
|
|
117
|
+
return Array.isArray(val) && val.length > 0;
|
|
118
|
+
case 'array_length_eq':
|
|
119
|
+
return Array.isArray(val) && val.length === Number(fv ?? 0);
|
|
120
|
+
case 'array_length_gt':
|
|
121
|
+
return Array.isArray(val) && val.length > Number(fv ?? 0);
|
|
122
|
+
case 'array_length_lt':
|
|
123
|
+
return Array.isArray(val) && val.length < Number(fv ?? 0);
|
|
124
|
+
// Object
|
|
125
|
+
case 'has_key':
|
|
126
|
+
return (val !== null &&
|
|
127
|
+
typeof val === 'object' &&
|
|
128
|
+
!Array.isArray(val) &&
|
|
129
|
+
String(fv ?? '') in val);
|
|
130
|
+
default:
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function evaluateFilters(filters, payload, mode = 'and', opts) {
|
|
135
|
+
const active = (filters ?? []).filter((f) => f.enabled !== false);
|
|
136
|
+
if (active.length === 0)
|
|
137
|
+
return true;
|
|
138
|
+
if (mode === 'and')
|
|
139
|
+
return active.every((f) => matchFilter(f, payload, opts));
|
|
140
|
+
return active.some((f) => matchFilter(f, payload, opts));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Evaluate filters against a payload that may be iterable.
|
|
144
|
+
* If iterable: filters each item, returns a new iterable payload with only passing items.
|
|
145
|
+
* If not iterable: returns { passed, payload } with the original payload.
|
|
146
|
+
*/
|
|
147
|
+
function evaluateFiltersIterable(filters, payload, mode = 'and', outputAsSingle = false, opts) {
|
|
148
|
+
const meta = payload._meta;
|
|
149
|
+
if (meta?.iterable && meta.iterateField) {
|
|
150
|
+
const items = payload[meta.iterateField] ?? [];
|
|
151
|
+
const passing = items.filter((item) => evaluateFilters(filters, item, mode, opts));
|
|
152
|
+
const resultPayload = outputAsSingle
|
|
153
|
+
? { _meta: { iterable: false, count: 1 }, [meta.iterateField]: passing }
|
|
154
|
+
: {
|
|
155
|
+
_meta: {
|
|
156
|
+
iterable: true,
|
|
157
|
+
iterateField: meta.iterateField,
|
|
158
|
+
count: passing.length,
|
|
159
|
+
},
|
|
160
|
+
[meta.iterateField]: passing,
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
passed: passing.length > 0,
|
|
164
|
+
resultPayload,
|
|
165
|
+
totalItems: items.length,
|
|
166
|
+
passedItems: passing.length,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// Strip _meta for evaluation, keep it in result
|
|
170
|
+
const { _meta, ...cleanPayload } = payload;
|
|
171
|
+
const passed = evaluateFilters(filters, cleanPayload, mode, opts);
|
|
172
|
+
return {
|
|
173
|
+
passed,
|
|
174
|
+
resultPayload: payload,
|
|
175
|
+
totalItems: 1,
|
|
176
|
+
passedItems: passed ? 1 : 0,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for node handler definitions.
|
|
3
|
+
* Reusable getOutputPayload, buildLastPayload, and buildDeliveryRecord builders
|
|
4
|
+
* that most action/processing handlers share.
|
|
5
|
+
*/
|
|
6
|
+
type Result = {
|
|
7
|
+
statusCode: number;
|
|
8
|
+
responseBody: string;
|
|
9
|
+
latencyMs: number;
|
|
10
|
+
outputPayload?: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
/** Standard getOutputPayload — parse responseBody, preserve _meta, spread fields */
|
|
13
|
+
export declare function standardGetOutputPayload(_entity: any, result: Result, _input: Record<string, unknown>): Record<string, unknown> | null;
|
|
14
|
+
/** Standard buildLastPayload — wrap output with success/statusCode/latencyMs in _meta */
|
|
15
|
+
export declare function standardBuildLastPayload(_entity: any, result: Result, output: Record<string, unknown>): Record<string, unknown>;
|
|
16
|
+
/** Build a standard action delivery record */
|
|
17
|
+
export declare function buildActionDeliveryRecord(actionIdField: string, entity: any, result: Result, success: boolean, ctx: {
|
|
18
|
+
event: any;
|
|
19
|
+
webhook: any;
|
|
20
|
+
}, targetUrl?: string): Record<string, unknown>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared helpers for node handler definitions.
|
|
4
|
+
* Reusable getOutputPayload, buildLastPayload, and buildDeliveryRecord builders
|
|
5
|
+
* that most action/processing handlers share.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.standardGetOutputPayload = standardGetOutputPayload;
|
|
9
|
+
exports.standardBuildLastPayload = standardBuildLastPayload;
|
|
10
|
+
exports.buildActionDeliveryRecord = buildActionDeliveryRecord;
|
|
11
|
+
/** Standard getOutputPayload — parse responseBody, preserve _meta, spread fields */
|
|
12
|
+
function standardGetOutputPayload(_entity, result, _input) {
|
|
13
|
+
try {
|
|
14
|
+
const parsed = result.responseBody ? JSON.parse(result.responseBody) : {};
|
|
15
|
+
const meta = parsed?._meta ?? {};
|
|
16
|
+
return {
|
|
17
|
+
_meta: { iterable: false, count: 1, ...meta },
|
|
18
|
+
...Object.fromEntries(Object.entries(typeof parsed === 'object' && parsed !== null
|
|
19
|
+
? parsed
|
|
20
|
+
: { value: parsed }).filter(([k]) => k !== '_meta')),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return { _meta: { iterable: false, count: 1 }, raw: result.responseBody };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Standard buildLastPayload — wrap output with success/statusCode/latencyMs in _meta */
|
|
28
|
+
function standardBuildLastPayload(_entity, result, output) {
|
|
29
|
+
const meta = output._meta ?? {};
|
|
30
|
+
return {
|
|
31
|
+
_meta: {
|
|
32
|
+
...meta,
|
|
33
|
+
success: result.statusCode < 400,
|
|
34
|
+
statusCode: result.statusCode,
|
|
35
|
+
latencyMs: result.latencyMs,
|
|
36
|
+
},
|
|
37
|
+
...Object.fromEntries(Object.entries(output).filter(([k]) => k !== '_meta')),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Build a standard action delivery record */
|
|
41
|
+
function buildActionDeliveryRecord(actionIdField, entity, result, success, ctx, targetUrl) {
|
|
42
|
+
return {
|
|
43
|
+
eventId: ctx.event.id,
|
|
44
|
+
webhookId: ctx.webhook.id,
|
|
45
|
+
[actionIdField]: entity._id?.toString(),
|
|
46
|
+
statusCode: result.statusCode || (success ? 200 : 0),
|
|
47
|
+
success,
|
|
48
|
+
error: success ? undefined : result.responseBody,
|
|
49
|
+
targetUrl: targetUrl ?? '',
|
|
50
|
+
attemptedAt: new Date(),
|
|
51
|
+
attempt: 1,
|
|
52
|
+
};
|
|
53
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hostwebhook/node-sdk
|
|
3
|
+
*
|
|
4
|
+
* Lo que un servicio necesita para EJECUTAR nodos de HostWebhook, sin traerse
|
|
5
|
+
* la aplicación entera. Sale de `api/src/common/`, donde estos 27 ficheros
|
|
6
|
+
* convivían con el motor de ejecución y con los servicios de operaciones de
|
|
7
|
+
* cada integración.
|
|
8
|
+
*
|
|
9
|
+
* ## Qué NO está aquí, y por qué
|
|
10
|
+
*
|
|
11
|
+
* - **`BaseNodeService`** y el **factory de controladores**. Arrastran 19 y
|
|
12
|
+
* 37 ficheros de fuera —planes, credenciales, auditoría, decoradores de
|
|
13
|
+
* auth— por cinco y cuatro colaboradores concretos. Salen cuando esos se
|
|
14
|
+
* inviertan tras una interfaz, no antes.
|
|
15
|
+
* - **El motor** (`event-pipeline`, `pipeline-run`). Es la aplicación, no
|
|
16
|
+
* el SDK: decide QUÉ nodo corre y cuándo. Aquí está sólo el CÓMO corre uno.
|
|
17
|
+
* - Los **servicios de operaciones** por integración. Se van con los nodos.
|
|
18
|
+
*
|
|
19
|
+
* ## Los contratos
|
|
20
|
+
*
|
|
21
|
+
* `contratos.ts` declara los siete tipos que antes llegaban de entidades de
|
|
22
|
+
* la api por `import type`. La dirección se invierte: los define el SDK y la
|
|
23
|
+
* api los cumple.
|
|
24
|
+
*/
|
|
25
|
+
export * from "./code-runner";
|
|
26
|
+
export * from "./contratos";
|
|
27
|
+
export * from "./ensure-meta";
|
|
28
|
+
export * from "./execute-with-iteration";
|
|
29
|
+
export * from "./filter-utils";
|
|
30
|
+
export * from "./handler-helpers";
|
|
31
|
+
export * from "./log-metadata";
|
|
32
|
+
export * from "./node-dispatch.registry";
|
|
33
|
+
export * from "./node-executors";
|
|
34
|
+
export * from "./node-lifecycle";
|
|
35
|
+
export * from "./normalize-nodes";
|
|
36
|
+
export * from "./output-node-ref.schema";
|
|
37
|
+
export * from "./output-webhook-scope";
|
|
38
|
+
export * from "./payload-preview";
|
|
39
|
+
export * from "./pipeline.constants";
|
|
40
|
+
export * from "./pre-request-pool";
|
|
41
|
+
export * from "./pre-request-runner-source";
|
|
42
|
+
export * from "./regex-de-inquilino";
|
|
43
|
+
export * from "./request-context";
|
|
44
|
+
export { retryOnTransientError, type NodeResult } from "./retry-transient";
|
|
45
|
+
export * from "./retry-utils";
|
|
46
|
+
export * from "./schema-validator-utils";
|
|
47
|
+
export * from "./ssrf-guard";
|
|
48
|
+
export * from "./swallow";
|
|
49
|
+
export * from "./template-render";
|
|
50
|
+
export * from "./try-parse";
|
|
51
|
+
export * from "./workspace-payloads";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.retryOnTransientError = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* @hostwebhook/node-sdk
|
|
20
|
+
*
|
|
21
|
+
* Lo que un servicio necesita para EJECUTAR nodos de HostWebhook, sin traerse
|
|
22
|
+
* la aplicación entera. Sale de `api/src/common/`, donde estos 27 ficheros
|
|
23
|
+
* convivían con el motor de ejecución y con los servicios de operaciones de
|
|
24
|
+
* cada integración.
|
|
25
|
+
*
|
|
26
|
+
* ## Qué NO está aquí, y por qué
|
|
27
|
+
*
|
|
28
|
+
* - **`BaseNodeService`** y el **factory de controladores**. Arrastran 19 y
|
|
29
|
+
* 37 ficheros de fuera —planes, credenciales, auditoría, decoradores de
|
|
30
|
+
* auth— por cinco y cuatro colaboradores concretos. Salen cuando esos se
|
|
31
|
+
* inviertan tras una interfaz, no antes.
|
|
32
|
+
* - **El motor** (`event-pipeline`, `pipeline-run`). Es la aplicación, no
|
|
33
|
+
* el SDK: decide QUÉ nodo corre y cuándo. Aquí está sólo el CÓMO corre uno.
|
|
34
|
+
* - Los **servicios de operaciones** por integración. Se van con los nodos.
|
|
35
|
+
*
|
|
36
|
+
* ## Los contratos
|
|
37
|
+
*
|
|
38
|
+
* `contratos.ts` declara los siete tipos que antes llegaban de entidades de
|
|
39
|
+
* la api por `import type`. La dirección se invierte: los define el SDK y la
|
|
40
|
+
* api los cumple.
|
|
41
|
+
*/
|
|
42
|
+
__exportStar(require("./code-runner"), exports);
|
|
43
|
+
__exportStar(require("./contratos"), exports);
|
|
44
|
+
__exportStar(require("./ensure-meta"), exports);
|
|
45
|
+
__exportStar(require("./execute-with-iteration"), exports);
|
|
46
|
+
__exportStar(require("./filter-utils"), exports);
|
|
47
|
+
__exportStar(require("./handler-helpers"), exports);
|
|
48
|
+
__exportStar(require("./log-metadata"), exports);
|
|
49
|
+
__exportStar(require("./node-dispatch.registry"), exports);
|
|
50
|
+
__exportStar(require("./node-executors"), exports);
|
|
51
|
+
__exportStar(require("./node-lifecycle"), exports);
|
|
52
|
+
__exportStar(require("./normalize-nodes"), exports);
|
|
53
|
+
__exportStar(require("./output-node-ref.schema"), exports);
|
|
54
|
+
__exportStar(require("./output-webhook-scope"), exports);
|
|
55
|
+
__exportStar(require("./payload-preview"), exports);
|
|
56
|
+
__exportStar(require("./pipeline.constants"), exports);
|
|
57
|
+
__exportStar(require("./pre-request-pool"), exports);
|
|
58
|
+
__exportStar(require("./pre-request-runner-source"), exports);
|
|
59
|
+
__exportStar(require("./regex-de-inquilino"), exports);
|
|
60
|
+
__exportStar(require("./request-context"), exports);
|
|
61
|
+
/* ⚠️ `retry-transient` y `retry-utils` declaran cada uno su `RetryOptions`,
|
|
62
|
+
y NO son el mismo tipo. El barril reexporta el de `retry-utils` —el
|
|
63
|
+
general— y del otro saca sólo sus funciones; quien necesite ese tipo lo
|
|
64
|
+
importa por su módulo. Unificarlos es trabajo de la api, no del paquete. */
|
|
65
|
+
var retry_transient_1 = require("./retry-transient");
|
|
66
|
+
Object.defineProperty(exports, "retryOnTransientError", { enumerable: true, get: function () { return retry_transient_1.retryOnTransientError; } });
|
|
67
|
+
__exportStar(require("./retry-utils"), exports);
|
|
68
|
+
__exportStar(require("./schema-validator-utils"), exports);
|
|
69
|
+
__exportStar(require("./ssrf-guard"), exports);
|
|
70
|
+
__exportStar(require("./swallow"), exports);
|
|
71
|
+
__exportStar(require("./template-render"), exports);
|
|
72
|
+
__exportStar(require("./try-parse"), exports);
|
|
73
|
+
__exportStar(require("./workspace-payloads"), exports);
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que un log de un nodo guarda, y con qué nombre.
|
|
3
|
+
*
|
|
4
|
+
* ## El desorden que esto viene a cerrar
|
|
5
|
+
*
|
|
6
|
+
* Auditado el 2026-08-27 sobre los cinco sitios que escriben `metadata`:
|
|
7
|
+
*
|
|
8
|
+
* | camino | entrada | salida | error |
|
|
9
|
+
* |-----------------------|---------------------|----------------------|--------------------------------|
|
|
10
|
+
* | producción (C1) | **ninguna** | `responseBody` ≤4096 | `error` = COPIA del response |
|
|
11
|
+
* | Run Test (C3) | `payloadPreview` | **ninguna** | `error` = el response |
|
|
12
|
+
* | Run Pipeline (C2) | **ninguna** | `responseBody` ≤1024 | `error` = el response, entero |
|
|
13
|
+
* | entrega ok | `requestPayload` | `responseBody` ≤2048 | — |
|
|
14
|
+
* | entrega fallida | `requestPayload` | `responseBody` ≤1024 | `error` = mensaje de verdad |
|
|
15
|
+
*
|
|
16
|
+
* Tres defectos, y los tres se ven en pantalla:
|
|
17
|
+
*
|
|
18
|
+
* 1. **«Error» casi nunca es un error.** En tres de los cinco sitios es el
|
|
19
|
+
* response copiado tal cual. La pantalla pinta dos paneles con el mismo
|
|
20
|
+
* contenido y titula uno «ERROR», así que el usuario busca ahí la causa y
|
|
21
|
+
* encuentra otra vez la salida.
|
|
22
|
+
* 2. **La entrada no se guarda donde más se necesita.** En producción no se
|
|
23
|
+
* guarda ninguna: con un nodo que falló, el log no dice con qué datos
|
|
24
|
+
* falló.
|
|
25
|
+
* 3. **El recorte rompe el JSON.** `truncatePreview` devuelve un OBJETO si
|
|
26
|
+
* cabe y una CADENA CORTADA si no. El mismo campo cambia de tipo según el
|
|
27
|
+
* tamaño, así que el visor pinta un árbol o un churro de texto sin que
|
|
28
|
+
* nada lo anuncie. Medido con un caso real: un response de 1270 caracteres
|
|
29
|
+
* pasaba entero por el `error` (sin límite) y cortado por el
|
|
30
|
+
* `responseBody` (límite 1024) — los dos paneles, el mismo dato, uno
|
|
31
|
+
* legible y el otro no.
|
|
32
|
+
*
|
|
33
|
+
* ## Lo que se guarda ahora
|
|
34
|
+
*
|
|
35
|
+
* Dos campos con el mismo significado en todos los caminos, más una frase:
|
|
36
|
+
*
|
|
37
|
+
* - `payload` — lo que ENTRÓ al nodo.
|
|
38
|
+
* - `response` — lo que SALIÓ. Al fallar lleva dentro un `error` con la
|
|
39
|
+
* frase que lo explica, en el hueco que deja el payload
|
|
40
|
+
* repetido.
|
|
41
|
+
*
|
|
42
|
+
* La frase estuvo un rato en un campo aparte (`errorSummary`) que la pantalla
|
|
43
|
+
* pintaba en una caja roja encima del panel. Se metió dentro porque lo que se
|
|
44
|
+
* quiere leer de un log que falló es «qué contestó el nodo», y eso son el
|
|
45
|
+
* veredicto y el motivo juntos — no uno arriba y otro dentro.
|
|
46
|
+
*
|
|
47
|
+
* Las claves viejas se siguen escribiendo un tiempo (`responseBody`,
|
|
48
|
+
* `payloadPreview`, `requestPayload`) porque los logs ya guardados las llevan
|
|
49
|
+
* y la pantalla tiene que poder pintar el historial. Ver
|
|
50
|
+
* `LEER_TAMBIEN_LAS_VIEJAS` abajo.
|
|
51
|
+
*/
|
|
52
|
+
/** Tope por defecto. 16 KB: 16 veces el peor límite de antes, y de sobra para
|
|
53
|
+
* cualquier payload real —el caso que destapó esto medía 1,2 KB—. Existe un
|
|
54
|
+
* tope porque un documento de Mongo no pasa de 16 MB, no por ahorrar. */
|
|
55
|
+
export declare const TOPE_DE_LOG: number;
|
|
56
|
+
/**
|
|
57
|
+
* Recorta SIN romper la forma.
|
|
58
|
+
*
|
|
59
|
+
* `truncatePreview` corta la cadena serializada por la mitad y le pega
|
|
60
|
+
* `...[truncated]`. Eso deja un JSON inválido, y el panel que lo recibe deja
|
|
61
|
+
* de poder plegarlo, colorearlo o buscar dentro: pasa a ser texto.
|
|
62
|
+
*
|
|
63
|
+
* Aquí se recorta por DENTRO: se sustituye el valor grande por una nota que
|
|
64
|
+
* dice qué había, y lo que sale sigue siendo un objeto válido. Un campo que
|
|
65
|
+
* no cabe se lee como «aquí había 40 KB de texto» en vez de dejar el
|
|
66
|
+
* documento entero ilegible por culpa de ese campo.
|
|
67
|
+
*/
|
|
68
|
+
export declare function recortarEstructural(value: unknown, tope?: number): unknown;
|
|
69
|
+
/**
|
|
70
|
+
* Lo que se pinta en lugar del dato repetido. En inglés: acaba en pantalla.
|
|
71
|
+
*
|
|
72
|
+
* ⚠️ NO empieza por `[` ni por `{`, y no es un capricho: cuando lo que se
|
|
73
|
+
* sustituye es el response ENTERO, esta cadena llega sola al panel, y el
|
|
74
|
+
* dashboard decide si pintar el visor de JSON mirando si el texto empieza por
|
|
75
|
+
* `[` o `{` (`looksLikeJson` en `app/dashboard/logs/page.tsx`). Con corchetes
|
|
76
|
+
* intentaría parsear «[identical to…]» como JSON y no es JSON.
|
|
77
|
+
*/
|
|
78
|
+
export declare const IGUAL_QUE_LA_ENTRADA = "Same as the payload below";
|
|
79
|
+
/**
|
|
80
|
+
* ¿Estos dos llevan el mismo contenido?
|
|
81
|
+
*
|
|
82
|
+
* `_meta` se ignora a propósito: es contabilidad que el pipeline añade
|
|
83
|
+
* (`iterable`, `count`), no dato del usuario. Un filtro que deja pasar el
|
|
84
|
+
* payload tal cual devuelve el MISMO objeto pero con `_meta` puesto, así que
|
|
85
|
+
* comparar en crudo diría «distintos» justo en el caso que esto viene a
|
|
86
|
+
* detectar.
|
|
87
|
+
*
|
|
88
|
+
* Comparar por JSON con las claves ordenadas y no con `===`: el response viaja
|
|
89
|
+
* serializado y vuelve parseado, así que nunca es el mismo objeto en memoria
|
|
90
|
+
* aunque sea el mismo dato.
|
|
91
|
+
*/
|
|
92
|
+
export declare function mismoContenido(a: unknown, b: unknown): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Quita del response lo que YA se guarda como entrada.
|
|
95
|
+
*
|
|
96
|
+
* ## Por qué
|
|
97
|
+
*
|
|
98
|
+
* Un log pinta el response arriba y el payload abajo, y en los nodos que
|
|
99
|
+
* dejan pasar los datos eso es el MISMO objeto dos veces. En un filtro no
|
|
100
|
+
* iterable es literal: `evaluateFiltersIterable` devuelve `resultPayload:
|
|
101
|
+
* payload`, el propio objeto de entrada. Reportado mirando un log: «si arriba
|
|
102
|
+
* está la response y abajo el payload, estaríamos repitiendo data».
|
|
103
|
+
*
|
|
104
|
+
* ## Por qué no se arregla en el ejecutor
|
|
105
|
+
*
|
|
106
|
+
* Porque `responseBody` **no es un campo de log**: se lee en 140 sitios de 46
|
|
107
|
+
* ficheros y una veintena lo parsean para DECIDIR. `standardGetOutputPayload`
|
|
108
|
+
* —que usan 23 handlers— construye parseándolo el payload que va aguas abajo;
|
|
109
|
+
* `pipeline-run` y `test-node` caen a `JSON.parse(responseBody)` cuando el
|
|
110
|
+
* handler no transforma; `cache` lee `.action`, `conditional` `.matchedId`,
|
|
111
|
+
* `schemaValidator` `.valid`. Vaciar el response ahí es cambiar la ejecución.
|
|
112
|
+
*
|
|
113
|
+
* El log, en cambio, es una hoja: nadie lee hacia atrás desde él. Así que la
|
|
114
|
+
* poda vive aquí.
|
|
115
|
+
*
|
|
116
|
+
* ## La regla
|
|
117
|
+
*
|
|
118
|
+
* Sólo se quita lo que está DEMOSTRADO repetido, comparando contenido:
|
|
119
|
+
*
|
|
120
|
+
* - el response entero igual a la entrada → se sustituye por la nota;
|
|
121
|
+
* - `response.payload` igual a la entrada → se sustituye ese campo y el
|
|
122
|
+
* veredicto (`passed`, `totalItems`…) se queda;
|
|
123
|
+
* - distintos → intacto.
|
|
124
|
+
*
|
|
125
|
+
* Ese último caso no es teórico: con un payload iterable el filtro devuelve un
|
|
126
|
+
* objeto NUEVO con sólo los items que pasaron. Ahí `response.payload` no es la
|
|
127
|
+
* entrada, es el resultado del filtrado, y borrarlo perdería el dato que más
|
|
128
|
+
* interesa.
|
|
129
|
+
*
|
|
130
|
+
* Y se sustituye en vez de borrar: un campo que desaparece se lee como «el
|
|
131
|
+
* nodo no devolvió nada», que es otra cosa.
|
|
132
|
+
*/
|
|
133
|
+
export declare function podarLoRepetido(response: unknown, entrada: unknown,
|
|
134
|
+
/**
|
|
135
|
+
* La frase del fallo, si el nodo falló.
|
|
136
|
+
*
|
|
137
|
+
* Va DENTRO del response, en el hueco que deja el payload repetido, en vez
|
|
138
|
+
* de aparte y en rojo encima del panel. Así el panel de arriba se lee
|
|
139
|
+
* entero como «qué contestó el nodo»: el veredicto y por qué, juntos, que
|
|
140
|
+
* es lo que se quiere saber al abrir un log que falló.
|
|
141
|
+
*/
|
|
142
|
+
fallo?: string): unknown;
|
|
143
|
+
/** El response ya parseado, si es que se puede. Nunca revienta. */
|
|
144
|
+
export declare function parsearResponse(responseBody: unknown): unknown;
|
|
145
|
+
/**
|
|
146
|
+
* Qué falló, en una frase.
|
|
147
|
+
*
|
|
148
|
+
* En inglés porque es texto de pantalla, y la pantalla está en inglés — hay
|
|
149
|
+
* un guardián en el dashboard (`check-spanish-ui`) que lo exige.
|
|
150
|
+
*
|
|
151
|
+
* El objetivo NO es reemplazar al response: es que la primera línea diga qué
|
|
152
|
+
* pasó sin tener que leer JSON. El response sigue guardado al lado.
|
|
153
|
+
*/
|
|
154
|
+
export declare function explicarFallo(args: {
|
|
155
|
+
nodeType?: string;
|
|
156
|
+
nodeName?: string;
|
|
157
|
+
statusCode?: number;
|
|
158
|
+
response?: unknown;
|
|
159
|
+
}): string;
|
|
160
|
+
/**
|
|
161
|
+
* Las claves de antes, que la PANTALLA sigue teniendo que leer.
|
|
162
|
+
*
|
|
163
|
+
* No se borran de los logs ya escritos, así que el dashboard mira primero la
|
|
164
|
+
* nueva y luego estas. Se listan aquí para que el día que se dejen de
|
|
165
|
+
* escribir haya un sitio donde mirar cuáles eran.
|
|
166
|
+
*/
|
|
167
|
+
export declare const LEER_TAMBIEN_LAS_VIEJAS: {
|
|
168
|
+
readonly payload: readonly ["requestPayload", "payloadPreview"];
|
|
169
|
+
readonly response: readonly ["responseBody"];
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Los metadatos de un log de nodo, iguales vengan del camino que vengan.
|
|
173
|
+
*
|
|
174
|
+
* `extra` es lo que aporta cada nodo (`filterMode`, `attempt`, `targetUrl`…)
|
|
175
|
+
* y va primero, para que no pueda pisar a los tres campos que esta función
|
|
176
|
+
* garantiza.
|
|
177
|
+
*/
|
|
178
|
+
export declare function construirMetadatosDeLog(args: {
|
|
179
|
+
nodeType?: string;
|
|
180
|
+
nodeName?: string;
|
|
181
|
+
/** Lo que entró al nodo. */
|
|
182
|
+
payload?: unknown;
|
|
183
|
+
/** Lo que salió, en crudo o ya parseado. */
|
|
184
|
+
responseBody?: unknown;
|
|
185
|
+
statusCode?: number;
|
|
186
|
+
success: boolean;
|
|
187
|
+
extra?: Record<string, unknown>;
|
|
188
|
+
/** Escribir además las claves viejas. Por defecto sí, mientras la pantalla
|
|
189
|
+
* desplegada siga siendo la anterior. */
|
|
190
|
+
conClavesViejas?: boolean;
|
|
191
|
+
}): Record<string, unknown>;
|