@hostwebhook/node-types 1.70.0 → 1.71.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/dist/bucket-operations.d.ts +88 -0
- package/dist/bucket-operations.js +140 -0
- package/dist/connections.js +1 -0
- package/dist/dispatch.js +1 -0
- package/dist/esm/bucket-operations.d.ts +88 -0
- package/dist/esm/bucket-operations.js +136 -0
- package/dist/esm/connections.js +1 -0
- package/dist/esm/dispatch.js +1 -0
- package/dist/esm/github-operations.js +67 -67
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/jira-operations.js +47 -47
- package/dist/esm/registry.d.ts +4 -4
- package/dist/esm/registry.js +540 -210
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/ui.js +2 -0
- package/dist/github-operations.js +67 -67
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -2
- package/dist/jira-operations.js +47 -47
- package/dist/registry.d.ts +4 -4
- package/dist/registry.js +540 -210
- package/dist/types.d.ts +1 -1
- package/dist/ui.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El nodo de bucket: guardar, firmar y borrar objetos en un almacenamiento
|
|
3
|
+
* compatible con S3.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué se llama `bucket` y no `r2`
|
|
6
|
+
*
|
|
7
|
+
* Porque el proveedor es un ENDPOINT, no un tipo de nodo. La credencial
|
|
8
|
+
* `aws_s3` ya guarda `endpoint`, `bucket` y `region`, así que el mismo nodo
|
|
9
|
+
* habla con Cloudflare R2, con AWS S3, con MinIO o con Backblaze sin cambiar
|
|
10
|
+
* una línea. Llamarlo `r2Action` habría obligado, el día que llegue AWS, a
|
|
11
|
+
* tener dos nodos casi idénticos o a renombrar un tipo — y el prefijo del tipo
|
|
12
|
+
* vive DENTRO de los ids de nodo ya guardados en la base de cada cliente, así
|
|
13
|
+
* que renombrar es migrar datos.
|
|
14
|
+
*
|
|
15
|
+
* ## La operación que motiva el nodo
|
|
16
|
+
*
|
|
17
|
+
* `presignDownload`. La plataforma sabe firmar peticiones S3 desde el nodo
|
|
18
|
+
* HTTP —`common/s3-signer.ts`— pero **por cabecera**, y una cabecera no cabe en
|
|
19
|
+
* un enlace que se manda por correo. Sin esto no había forma de entregarle a un
|
|
20
|
+
* comprador una descarga temporal desde un flujo.
|
|
21
|
+
*
|
|
22
|
+
* ## Por qué sólo cuatro operaciones
|
|
23
|
+
*
|
|
24
|
+
* Listar y subida multiparte son las dos que piden el SDK de AWS de verdad
|
|
25
|
+
* —paginación y reanudación— y el ejecutor hoy no tiene ni un `@aws-sdk/*`.
|
|
26
|
+
* Entrar sin esa dependencia y traerla cuando aparezca la operación que la
|
|
27
|
+
* justifique es más barato que al revés. Y un nodo con veinte operaciones es un
|
|
28
|
+
* nodo que nadie entiende.
|
|
29
|
+
*/
|
|
30
|
+
export declare const BUCKET_OPERATIONS: readonly ["presignDownload", "putObject", "headObject", "deleteObject"];
|
|
31
|
+
export type BucketOperation = (typeof BUCKET_OPERATIONS)[number];
|
|
32
|
+
export declare function isBucketOperation(value: unknown): value is BucketOperation;
|
|
33
|
+
/**
|
|
34
|
+
* Ninguna es iterable.
|
|
35
|
+
*
|
|
36
|
+
* Una operación iterable es la que devuelve una COLECCIÓN sobre la que el nodo
|
|
37
|
+
* de abajo repite. Las cuatro de aquí actúan sobre UN objeto, así que el que
|
|
38
|
+
* viene detrás recibe un resultado, no una lista. El día que entre `listObjects`
|
|
39
|
+
* ésa sí entra en esta lista.
|
|
40
|
+
*/
|
|
41
|
+
export declare const BUCKET_ITERABLE_OPERATIONS: readonly BucketOperation[];
|
|
42
|
+
export type BucketParamType =
|
|
43
|
+
/** La clave del objeto dentro del bucket. Con autocompletado de
|
|
44
|
+
* `{{payload.*}}`, porque el 90% de los casos la construye del evento:
|
|
45
|
+
* `cuadernillos/{{payload.orderId}}.pdf`. */
|
|
46
|
+
"template"
|
|
47
|
+
/** Área de texto larga, también con autocompletado. */
|
|
48
|
+
| "textarea"
|
|
49
|
+
/** `<select>` normal; las etiquetas no son los valores, `options` obliga. */
|
|
50
|
+
| "select" | "number"
|
|
51
|
+
/**
|
|
52
|
+
* `<select>` de `''` / `'true'` / `'false'` que se guarda como BOOLEANO, o
|
|
53
|
+
* no se guarda si es `''`. "— sin tocar —" tiene que significar que la clave
|
|
54
|
+
* no viaja, no que viaja en `false`.
|
|
55
|
+
*/
|
|
56
|
+
| "booleanSelect";
|
|
57
|
+
export interface BucketParamSpec {
|
|
58
|
+
/** La clave de `operationConfig`. Es lo que lee la api, no un nombre de UI. */
|
|
59
|
+
name: string;
|
|
60
|
+
label: string;
|
|
61
|
+
type: BucketParamType;
|
|
62
|
+
required?: boolean;
|
|
63
|
+
/** Ayuda bajo el campo. Texto plano: el paquete no lleva React. */
|
|
64
|
+
description?: string;
|
|
65
|
+
placeholder?: string;
|
|
66
|
+
/** Prerrellenado cuando la config guardada no trae valor para esta clave. */
|
|
67
|
+
default?: string | number;
|
|
68
|
+
/** Obligatorio en `select` y `booleanSelect`. */
|
|
69
|
+
options?: ReadonlyArray<{
|
|
70
|
+
value: string;
|
|
71
|
+
label: string;
|
|
72
|
+
}>;
|
|
73
|
+
min?: number;
|
|
74
|
+
max?: number;
|
|
75
|
+
/** Se pinta en la pestaña "Advanced" en vez de en "Config". */
|
|
76
|
+
advanced?: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface BucketOperationSpec {
|
|
79
|
+
/** Etiqueta de la fila en el desplegable de operación. */
|
|
80
|
+
label: string;
|
|
81
|
+
/** Sub-línea bajo la etiqueta. */
|
|
82
|
+
description: string;
|
|
83
|
+
/** La llamada S3 a la que mapea, para poder cotejarla con los docs. */
|
|
84
|
+
apiRoute: string;
|
|
85
|
+
/** Esquema de parámetros. El orden ES el orden en pantalla. */
|
|
86
|
+
params: BucketParamSpec[];
|
|
87
|
+
}
|
|
88
|
+
export declare const BUCKET_OPERATION_SPECS: Record<BucketOperation, BucketOperationSpec>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* El nodo de bucket: guardar, firmar y borrar objetos en un almacenamiento
|
|
4
|
+
* compatible con S3.
|
|
5
|
+
*
|
|
6
|
+
* ## Por qué se llama `bucket` y no `r2`
|
|
7
|
+
*
|
|
8
|
+
* Porque el proveedor es un ENDPOINT, no un tipo de nodo. La credencial
|
|
9
|
+
* `aws_s3` ya guarda `endpoint`, `bucket` y `region`, así que el mismo nodo
|
|
10
|
+
* habla con Cloudflare R2, con AWS S3, con MinIO o con Backblaze sin cambiar
|
|
11
|
+
* una línea. Llamarlo `r2Action` habría obligado, el día que llegue AWS, a
|
|
12
|
+
* tener dos nodos casi idénticos o a renombrar un tipo — y el prefijo del tipo
|
|
13
|
+
* vive DENTRO de los ids de nodo ya guardados en la base de cada cliente, así
|
|
14
|
+
* que renombrar es migrar datos.
|
|
15
|
+
*
|
|
16
|
+
* ## La operación que motiva el nodo
|
|
17
|
+
*
|
|
18
|
+
* `presignDownload`. La plataforma sabe firmar peticiones S3 desde el nodo
|
|
19
|
+
* HTTP —`common/s3-signer.ts`— pero **por cabecera**, y una cabecera no cabe en
|
|
20
|
+
* un enlace que se manda por correo. Sin esto no había forma de entregarle a un
|
|
21
|
+
* comprador una descarga temporal desde un flujo.
|
|
22
|
+
*
|
|
23
|
+
* ## Por qué sólo cuatro operaciones
|
|
24
|
+
*
|
|
25
|
+
* Listar y subida multiparte son las dos que piden el SDK de AWS de verdad
|
|
26
|
+
* —paginación y reanudación— y el ejecutor hoy no tiene ni un `@aws-sdk/*`.
|
|
27
|
+
* Entrar sin esa dependencia y traerla cuando aparezca la operación que la
|
|
28
|
+
* justifique es más barato que al revés. Y un nodo con veinte operaciones es un
|
|
29
|
+
* nodo que nadie entiende.
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.BUCKET_OPERATION_SPECS = exports.BUCKET_ITERABLE_OPERATIONS = exports.BUCKET_OPERATIONS = void 0;
|
|
33
|
+
exports.isBucketOperation = isBucketOperation;
|
|
34
|
+
exports.BUCKET_OPERATIONS = [
|
|
35
|
+
"presignDownload",
|
|
36
|
+
"putObject",
|
|
37
|
+
"headObject",
|
|
38
|
+
"deleteObject",
|
|
39
|
+
];
|
|
40
|
+
function isBucketOperation(value) {
|
|
41
|
+
return (typeof value === "string" &&
|
|
42
|
+
exports.BUCKET_OPERATIONS.includes(value));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Ninguna es iterable.
|
|
46
|
+
*
|
|
47
|
+
* Una operación iterable es la que devuelve una COLECCIÓN sobre la que el nodo
|
|
48
|
+
* de abajo repite. Las cuatro de aquí actúan sobre UN objeto, así que el que
|
|
49
|
+
* viene detrás recibe un resultado, no una lista. El día que entre `listObjects`
|
|
50
|
+
* ésa sí entra en esta lista.
|
|
51
|
+
*/
|
|
52
|
+
exports.BUCKET_ITERABLE_OPERATIONS = [];
|
|
53
|
+
/* Ayudantes — repetir el mismo objeto cuatro veces es cómo se desincronizan las
|
|
54
|
+
descripciones. */
|
|
55
|
+
const objectKey = (placeholder) => ({
|
|
56
|
+
name: "key",
|
|
57
|
+
label: "Object key",
|
|
58
|
+
type: "template",
|
|
59
|
+
required: true,
|
|
60
|
+
placeholder,
|
|
61
|
+
description: "The path inside the bucket. No leading slash. Supports {{payload.x}}.",
|
|
62
|
+
});
|
|
63
|
+
exports.BUCKET_OPERATION_SPECS = {
|
|
64
|
+
presignDownload: {
|
|
65
|
+
label: "Sign download link",
|
|
66
|
+
description: "Returns a temporary URL to download a private object. Does not move the file.",
|
|
67
|
+
apiRoute: "GET (presigned)",
|
|
68
|
+
params: [
|
|
69
|
+
objectKey("cuadernillos/{{payload.orderId}}.pdf"),
|
|
70
|
+
{
|
|
71
|
+
name: "expiresIn",
|
|
72
|
+
label: "Link valid for (seconds)",
|
|
73
|
+
type: "number",
|
|
74
|
+
required: true,
|
|
75
|
+
default: 3600,
|
|
76
|
+
min: 60,
|
|
77
|
+
/* El tope de SigV4 es de siete días y no se puede pasar: firmar por
|
|
78
|
+
más tiempo produce una URL que el proveedor rechaza, y el error
|
|
79
|
+
llega tarde y sin explicación. */
|
|
80
|
+
max: 604800,
|
|
81
|
+
description: "From 60 s up to 7 days, the maximum the signature allows. Past that, the link stops working.",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "downloadAs",
|
|
85
|
+
label: "Download as (filename)",
|
|
86
|
+
type: "template",
|
|
87
|
+
placeholder: "Cuadernillo 1.pdf",
|
|
88
|
+
description: "When set, the browser DOWNLOADS the file under that name instead of opening it. Leave it empty to have it opened.",
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
putObject: {
|
|
93
|
+
label: "Upload object",
|
|
94
|
+
description: "Stores an object in the bucket. Overwrites when the key already exists.",
|
|
95
|
+
apiRoute: "PUT /{bucket}/{key}",
|
|
96
|
+
params: [
|
|
97
|
+
objectKey("recibos/{{payload.orderId}}.json"),
|
|
98
|
+
{
|
|
99
|
+
name: "body",
|
|
100
|
+
label: "Contents",
|
|
101
|
+
type: "textarea",
|
|
102
|
+
required: true,
|
|
103
|
+
placeholder: "{{payload}}",
|
|
104
|
+
description: "What gets stored. Supports {{payload.x}}; an object is stored as JSON.",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "contentType",
|
|
108
|
+
label: "Content type",
|
|
109
|
+
type: "template",
|
|
110
|
+
default: "application/json",
|
|
111
|
+
placeholder: "application/pdf",
|
|
112
|
+
description: "What the browser uses to decide what to do with the file.",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
headObject: {
|
|
117
|
+
label: "Check object exists",
|
|
118
|
+
description: "Checks whether an object exists, and its size, without downloading it.",
|
|
119
|
+
apiRoute: "HEAD /{bucket}/{key}",
|
|
120
|
+
params: [
|
|
121
|
+
objectKey("cuadernillos/{{payload.orderId}}.pdf"),
|
|
122
|
+
{
|
|
123
|
+
name: "failIfMissing",
|
|
124
|
+
label: "Fail when the object is missing",
|
|
125
|
+
type: "booleanSelect",
|
|
126
|
+
options: [
|
|
127
|
+
{ value: "true", label: "Yes — stop the flow" },
|
|
128
|
+
{ value: "false", label: "No — carry on with exists: false" },
|
|
129
|
+
],
|
|
130
|
+
description: 'With "No", the step succeeds and the node below decides. With "Yes", a missing object stops the flow.',
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
deleteObject: {
|
|
135
|
+
label: "Delete object",
|
|
136
|
+
description: "Deletes an object from the bucket. It cannot be undone.",
|
|
137
|
+
apiRoute: "DELETE /{bucket}/{key}",
|
|
138
|
+
params: [objectKey("temporales/{{payload.id}}.tmp")],
|
|
139
|
+
},
|
|
140
|
+
};
|
package/dist/connections.js
CHANGED
|
@@ -81,6 +81,7 @@ exports.NODE_CONNECTIONS = {
|
|
|
81
81
|
shopifyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
82
82
|
githubAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
83
83
|
jiraAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
84
|
+
bucketAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
84
85
|
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
85
86
|
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
86
87
|
notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
package/dist/dispatch.js
CHANGED
|
@@ -56,6 +56,7 @@ exports.NODE_DISPATCH = {
|
|
|
56
56
|
shopifyAction: { service: 'shopifyActionsService', collection: 'shopifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
57
57
|
githubAction: { service: 'githubActionsService', collection: 'githubactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
58
58
|
jiraAction: { service: 'jiraActionsService', collection: 'jiraactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
59
|
+
bucketAction: { service: 'bucketActionsService', collection: 'bucketactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
59
60
|
slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
60
61
|
googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
61
62
|
googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El nodo de bucket: guardar, firmar y borrar objetos en un almacenamiento
|
|
3
|
+
* compatible con S3.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué se llama `bucket` y no `r2`
|
|
6
|
+
*
|
|
7
|
+
* Porque el proveedor es un ENDPOINT, no un tipo de nodo. La credencial
|
|
8
|
+
* `aws_s3` ya guarda `endpoint`, `bucket` y `region`, así que el mismo nodo
|
|
9
|
+
* habla con Cloudflare R2, con AWS S3, con MinIO o con Backblaze sin cambiar
|
|
10
|
+
* una línea. Llamarlo `r2Action` habría obligado, el día que llegue AWS, a
|
|
11
|
+
* tener dos nodos casi idénticos o a renombrar un tipo — y el prefijo del tipo
|
|
12
|
+
* vive DENTRO de los ids de nodo ya guardados en la base de cada cliente, así
|
|
13
|
+
* que renombrar es migrar datos.
|
|
14
|
+
*
|
|
15
|
+
* ## La operación que motiva el nodo
|
|
16
|
+
*
|
|
17
|
+
* `presignDownload`. La plataforma sabe firmar peticiones S3 desde el nodo
|
|
18
|
+
* HTTP —`common/s3-signer.ts`— pero **por cabecera**, y una cabecera no cabe en
|
|
19
|
+
* un enlace que se manda por correo. Sin esto no había forma de entregarle a un
|
|
20
|
+
* comprador una descarga temporal desde un flujo.
|
|
21
|
+
*
|
|
22
|
+
* ## Por qué sólo cuatro operaciones
|
|
23
|
+
*
|
|
24
|
+
* Listar y subida multiparte son las dos que piden el SDK de AWS de verdad
|
|
25
|
+
* —paginación y reanudación— y el ejecutor hoy no tiene ni un `@aws-sdk/*`.
|
|
26
|
+
* Entrar sin esa dependencia y traerla cuando aparezca la operación que la
|
|
27
|
+
* justifique es más barato que al revés. Y un nodo con veinte operaciones es un
|
|
28
|
+
* nodo que nadie entiende.
|
|
29
|
+
*/
|
|
30
|
+
export declare const BUCKET_OPERATIONS: readonly ["presignDownload", "putObject", "headObject", "deleteObject"];
|
|
31
|
+
export type BucketOperation = (typeof BUCKET_OPERATIONS)[number];
|
|
32
|
+
export declare function isBucketOperation(value: unknown): value is BucketOperation;
|
|
33
|
+
/**
|
|
34
|
+
* Ninguna es iterable.
|
|
35
|
+
*
|
|
36
|
+
* Una operación iterable es la que devuelve una COLECCIÓN sobre la que el nodo
|
|
37
|
+
* de abajo repite. Las cuatro de aquí actúan sobre UN objeto, así que el que
|
|
38
|
+
* viene detrás recibe un resultado, no una lista. El día que entre `listObjects`
|
|
39
|
+
* ésa sí entra en esta lista.
|
|
40
|
+
*/
|
|
41
|
+
export declare const BUCKET_ITERABLE_OPERATIONS: readonly BucketOperation[];
|
|
42
|
+
export type BucketParamType =
|
|
43
|
+
/** La clave del objeto dentro del bucket. Con autocompletado de
|
|
44
|
+
* `{{payload.*}}`, porque el 90% de los casos la construye del evento:
|
|
45
|
+
* `cuadernillos/{{payload.orderId}}.pdf`. */
|
|
46
|
+
"template"
|
|
47
|
+
/** Área de texto larga, también con autocompletado. */
|
|
48
|
+
| "textarea"
|
|
49
|
+
/** `<select>` normal; las etiquetas no son los valores, `options` obliga. */
|
|
50
|
+
| "select" | "number"
|
|
51
|
+
/**
|
|
52
|
+
* `<select>` de `''` / `'true'` / `'false'` que se guarda como BOOLEANO, o
|
|
53
|
+
* no se guarda si es `''`. "— sin tocar —" tiene que significar que la clave
|
|
54
|
+
* no viaja, no que viaja en `false`.
|
|
55
|
+
*/
|
|
56
|
+
| "booleanSelect";
|
|
57
|
+
export interface BucketParamSpec {
|
|
58
|
+
/** La clave de `operationConfig`. Es lo que lee la api, no un nombre de UI. */
|
|
59
|
+
name: string;
|
|
60
|
+
label: string;
|
|
61
|
+
type: BucketParamType;
|
|
62
|
+
required?: boolean;
|
|
63
|
+
/** Ayuda bajo el campo. Texto plano: el paquete no lleva React. */
|
|
64
|
+
description?: string;
|
|
65
|
+
placeholder?: string;
|
|
66
|
+
/** Prerrellenado cuando la config guardada no trae valor para esta clave. */
|
|
67
|
+
default?: string | number;
|
|
68
|
+
/** Obligatorio en `select` y `booleanSelect`. */
|
|
69
|
+
options?: ReadonlyArray<{
|
|
70
|
+
value: string;
|
|
71
|
+
label: string;
|
|
72
|
+
}>;
|
|
73
|
+
min?: number;
|
|
74
|
+
max?: number;
|
|
75
|
+
/** Se pinta en la pestaña "Advanced" en vez de en "Config". */
|
|
76
|
+
advanced?: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface BucketOperationSpec {
|
|
79
|
+
/** Etiqueta de la fila en el desplegable de operación. */
|
|
80
|
+
label: string;
|
|
81
|
+
/** Sub-línea bajo la etiqueta. */
|
|
82
|
+
description: string;
|
|
83
|
+
/** La llamada S3 a la que mapea, para poder cotejarla con los docs. */
|
|
84
|
+
apiRoute: string;
|
|
85
|
+
/** Esquema de parámetros. El orden ES el orden en pantalla. */
|
|
86
|
+
params: BucketParamSpec[];
|
|
87
|
+
}
|
|
88
|
+
export declare const BUCKET_OPERATION_SPECS: Record<BucketOperation, BucketOperationSpec>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El nodo de bucket: guardar, firmar y borrar objetos en un almacenamiento
|
|
3
|
+
* compatible con S3.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué se llama `bucket` y no `r2`
|
|
6
|
+
*
|
|
7
|
+
* Porque el proveedor es un ENDPOINT, no un tipo de nodo. La credencial
|
|
8
|
+
* `aws_s3` ya guarda `endpoint`, `bucket` y `region`, así que el mismo nodo
|
|
9
|
+
* habla con Cloudflare R2, con AWS S3, con MinIO o con Backblaze sin cambiar
|
|
10
|
+
* una línea. Llamarlo `r2Action` habría obligado, el día que llegue AWS, a
|
|
11
|
+
* tener dos nodos casi idénticos o a renombrar un tipo — y el prefijo del tipo
|
|
12
|
+
* vive DENTRO de los ids de nodo ya guardados en la base de cada cliente, así
|
|
13
|
+
* que renombrar es migrar datos.
|
|
14
|
+
*
|
|
15
|
+
* ## La operación que motiva el nodo
|
|
16
|
+
*
|
|
17
|
+
* `presignDownload`. La plataforma sabe firmar peticiones S3 desde el nodo
|
|
18
|
+
* HTTP —`common/s3-signer.ts`— pero **por cabecera**, y una cabecera no cabe en
|
|
19
|
+
* un enlace que se manda por correo. Sin esto no había forma de entregarle a un
|
|
20
|
+
* comprador una descarga temporal desde un flujo.
|
|
21
|
+
*
|
|
22
|
+
* ## Por qué sólo cuatro operaciones
|
|
23
|
+
*
|
|
24
|
+
* Listar y subida multiparte son las dos que piden el SDK de AWS de verdad
|
|
25
|
+
* —paginación y reanudación— y el ejecutor hoy no tiene ni un `@aws-sdk/*`.
|
|
26
|
+
* Entrar sin esa dependencia y traerla cuando aparezca la operación que la
|
|
27
|
+
* justifique es más barato que al revés. Y un nodo con veinte operaciones es un
|
|
28
|
+
* nodo que nadie entiende.
|
|
29
|
+
*/
|
|
30
|
+
export const BUCKET_OPERATIONS = [
|
|
31
|
+
"presignDownload",
|
|
32
|
+
"putObject",
|
|
33
|
+
"headObject",
|
|
34
|
+
"deleteObject",
|
|
35
|
+
];
|
|
36
|
+
export function isBucketOperation(value) {
|
|
37
|
+
return (typeof value === "string" &&
|
|
38
|
+
BUCKET_OPERATIONS.includes(value));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Ninguna es iterable.
|
|
42
|
+
*
|
|
43
|
+
* Una operación iterable es la que devuelve una COLECCIÓN sobre la que el nodo
|
|
44
|
+
* de abajo repite. Las cuatro de aquí actúan sobre UN objeto, así que el que
|
|
45
|
+
* viene detrás recibe un resultado, no una lista. El día que entre `listObjects`
|
|
46
|
+
* ésa sí entra en esta lista.
|
|
47
|
+
*/
|
|
48
|
+
export const BUCKET_ITERABLE_OPERATIONS = [];
|
|
49
|
+
/* Ayudantes — repetir el mismo objeto cuatro veces es cómo se desincronizan las
|
|
50
|
+
descripciones. */
|
|
51
|
+
const objectKey = (placeholder) => ({
|
|
52
|
+
name: "key",
|
|
53
|
+
label: "Object key",
|
|
54
|
+
type: "template",
|
|
55
|
+
required: true,
|
|
56
|
+
placeholder,
|
|
57
|
+
description: "The path inside the bucket. No leading slash. Supports {{payload.x}}.",
|
|
58
|
+
});
|
|
59
|
+
export const BUCKET_OPERATION_SPECS = {
|
|
60
|
+
presignDownload: {
|
|
61
|
+
label: "Sign download link",
|
|
62
|
+
description: "Returns a temporary URL to download a private object. Does not move the file.",
|
|
63
|
+
apiRoute: "GET (presigned)",
|
|
64
|
+
params: [
|
|
65
|
+
objectKey("cuadernillos/{{payload.orderId}}.pdf"),
|
|
66
|
+
{
|
|
67
|
+
name: "expiresIn",
|
|
68
|
+
label: "Link valid for (seconds)",
|
|
69
|
+
type: "number",
|
|
70
|
+
required: true,
|
|
71
|
+
default: 3600,
|
|
72
|
+
min: 60,
|
|
73
|
+
/* El tope de SigV4 es de siete días y no se puede pasar: firmar por
|
|
74
|
+
más tiempo produce una URL que el proveedor rechaza, y el error
|
|
75
|
+
llega tarde y sin explicación. */
|
|
76
|
+
max: 604800,
|
|
77
|
+
description: "From 60 s up to 7 days, the maximum the signature allows. Past that, the link stops working.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "downloadAs",
|
|
81
|
+
label: "Download as (filename)",
|
|
82
|
+
type: "template",
|
|
83
|
+
placeholder: "Cuadernillo 1.pdf",
|
|
84
|
+
description: "When set, the browser DOWNLOADS the file under that name instead of opening it. Leave it empty to have it opened.",
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
putObject: {
|
|
89
|
+
label: "Upload object",
|
|
90
|
+
description: "Stores an object in the bucket. Overwrites when the key already exists.",
|
|
91
|
+
apiRoute: "PUT /{bucket}/{key}",
|
|
92
|
+
params: [
|
|
93
|
+
objectKey("recibos/{{payload.orderId}}.json"),
|
|
94
|
+
{
|
|
95
|
+
name: "body",
|
|
96
|
+
label: "Contents",
|
|
97
|
+
type: "textarea",
|
|
98
|
+
required: true,
|
|
99
|
+
placeholder: "{{payload}}",
|
|
100
|
+
description: "What gets stored. Supports {{payload.x}}; an object is stored as JSON.",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "contentType",
|
|
104
|
+
label: "Content type",
|
|
105
|
+
type: "template",
|
|
106
|
+
default: "application/json",
|
|
107
|
+
placeholder: "application/pdf",
|
|
108
|
+
description: "What the browser uses to decide what to do with the file.",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
headObject: {
|
|
113
|
+
label: "Check object exists",
|
|
114
|
+
description: "Checks whether an object exists, and its size, without downloading it.",
|
|
115
|
+
apiRoute: "HEAD /{bucket}/{key}",
|
|
116
|
+
params: [
|
|
117
|
+
objectKey("cuadernillos/{{payload.orderId}}.pdf"),
|
|
118
|
+
{
|
|
119
|
+
name: "failIfMissing",
|
|
120
|
+
label: "Fail when the object is missing",
|
|
121
|
+
type: "booleanSelect",
|
|
122
|
+
options: [
|
|
123
|
+
{ value: "true", label: "Yes — stop the flow" },
|
|
124
|
+
{ value: "false", label: "No — carry on with exists: false" },
|
|
125
|
+
],
|
|
126
|
+
description: 'With "No", the step succeeds and the node below decides. With "Yes", a missing object stops the flow.',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
deleteObject: {
|
|
131
|
+
label: "Delete object",
|
|
132
|
+
description: "Deletes an object from the bucket. It cannot be undone.",
|
|
133
|
+
apiRoute: "DELETE /{bucket}/{key}",
|
|
134
|
+
params: [objectKey("temporales/{{payload.id}}.tmp")],
|
|
135
|
+
},
|
|
136
|
+
};
|
package/dist/esm/connections.js
CHANGED
|
@@ -73,6 +73,7 @@ export const NODE_CONNECTIONS = {
|
|
|
73
73
|
shopifyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
74
74
|
githubAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
75
75
|
jiraAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
76
|
+
bucketAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
76
77
|
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
77
78
|
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
78
79
|
notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
package/dist/esm/dispatch.js
CHANGED
|
@@ -51,6 +51,7 @@ export const NODE_DISPATCH = {
|
|
|
51
51
|
shopifyAction: { service: 'shopifyActionsService', collection: 'shopifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
52
52
|
githubAction: { service: 'githubActionsService', collection: 'githubactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
53
53
|
jiraAction: { service: 'jiraActionsService', collection: 'jiraactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
54
|
+
bucketAction: { service: 'bucketActionsService', collection: 'bucketactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
54
55
|
slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
55
56
|
googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
56
57
|
googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|