n8n-nodes-formbase 0.2.1 → 0.3.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/README.md +7 -0
- package/dist/nodes/Formbase/FormbaseTrigger.node.js +26 -6
- package/dist/nodes/Formbase/FormbaseTrigger.node.js.map +1 -1
- package/dist/nodes/Formbase/FormbaseWebhookSignature.d.ts +3 -0
- package/dist/nodes/Formbase/FormbaseWebhookSignature.js +46 -0
- package/dist/nodes/Formbase/FormbaseWebhookSignature.js.map +1 -0
- package/dist/package.json +3 -2
- package/examples/formbase-submission.json +77 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Community node for [n8n](https://n8n.io) that starts workflows when a [formbase]
|
|
|
8
8
|
- Trigger on abandoned submissions when partial submission tracking is enabled.
|
|
9
9
|
- Load forms dynamically from every workspace available to the credential.
|
|
10
10
|
- Register and remove formbase webhook subscriptions with the n8n workflow lifecycle.
|
|
11
|
+
- Verify every webhook with HMAC-SHA256 and reject stale or forged requests.
|
|
11
12
|
- Connect through workspace-scoped OAuth 2.1 with PKCE and automatic refresh-token rotation.
|
|
12
13
|
|
|
13
14
|
## Install
|
|
@@ -42,6 +43,12 @@ Self-hosted n8n must use configured HTTPS public URL for OAuth callback. Loopbac
|
|
|
42
43
|
|
|
43
44
|
n8n webhook URL must be publicly reachable over HTTPS. For reverse-proxy or tunnel deployments, configure n8n's `WEBHOOK_URL` so generated webhook URLs use public origin.
|
|
44
45
|
|
|
46
|
+
n8n generates a separate 256-bit signing secret for each registration. Incoming requests must contain a valid `X-formbase-Signature` header with a timestamp no more than five minutes old. Missing, stale, or invalid signatures receive `401 Unauthorized` and do not start the workflow.
|
|
47
|
+
|
|
48
|
+
## Example workflow
|
|
49
|
+
|
|
50
|
+
Import [`examples/formbase-submission.json`](examples/formbase-submission.json), connect formbase credential, select form, then activate workflow. Example maps event ID, submission ID, respondent email, and form name into stable output fields.
|
|
51
|
+
|
|
45
52
|
## Output
|
|
46
53
|
|
|
47
54
|
Each webhook produces one n8n item containing formbase payload:
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FormbaseTrigger = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const constants_1 = require("./constants");
|
|
6
|
+
const FormbaseWebhookSignature_1 = require("./FormbaseWebhookSignature");
|
|
6
7
|
const GenericFunctions_1 = require("./GenericFunctions");
|
|
7
8
|
const FORMS_PAGE_SIZE = 100;
|
|
8
9
|
async function listWorkspaceForms(context, workspace) {
|
|
@@ -27,6 +28,10 @@ async function listWorkspaceForms(context, workspace) {
|
|
|
27
28
|
function findWebhookSubscription(subscriptions, targetUrl, eventType) {
|
|
28
29
|
return subscriptions.find((subscription) => subscription.targetUrl === targetUrl && subscription.provider === 'n8n' && subscription.eventType === eventType);
|
|
29
30
|
}
|
|
31
|
+
function clearWebhookRegistration(webhookData) {
|
|
32
|
+
delete webhookData.subscriptionId;
|
|
33
|
+
delete webhookData.webhookSecret;
|
|
34
|
+
}
|
|
30
35
|
class FormbaseTrigger {
|
|
31
36
|
constructor() {
|
|
32
37
|
this.description = {
|
|
@@ -37,7 +42,6 @@ class FormbaseTrigger {
|
|
|
37
42
|
version: 1,
|
|
38
43
|
subtitle: '={{ $parameter["event"] === "submission_created" ? "On submission created" : "On submission abandoned" }}',
|
|
39
44
|
description: 'Starts the workflow when a formbase form receives a submission',
|
|
40
|
-
usableAsTool: true,
|
|
41
45
|
defaults: {
|
|
42
46
|
name: 'formbase Trigger',
|
|
43
47
|
},
|
|
@@ -131,10 +135,17 @@ class FormbaseTrigger {
|
|
|
131
135
|
formId,
|
|
132
136
|
}));
|
|
133
137
|
const match = findWebhookSubscription(result.items, webhookUrl, eventType);
|
|
134
|
-
if (match) {
|
|
135
|
-
webhookData
|
|
136
|
-
return
|
|
138
|
+
if (!match) {
|
|
139
|
+
clearWebhookRegistration(webhookData);
|
|
140
|
+
return false;
|
|
137
141
|
}
|
|
142
|
+
const registrationMatchesStaticData = webhookData.subscriptionId === match.subscriptionId && typeof webhookData.webhookSecret === 'string';
|
|
143
|
+
if (registrationMatchesStaticData)
|
|
144
|
+
return true;
|
|
145
|
+
await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.delete', {
|
|
146
|
+
subscriptionId: match.subscriptionId,
|
|
147
|
+
});
|
|
148
|
+
clearWebhookRegistration(webhookData);
|
|
138
149
|
return false;
|
|
139
150
|
},
|
|
140
151
|
async create() {
|
|
@@ -143,21 +154,26 @@ class FormbaseTrigger {
|
|
|
143
154
|
return false;
|
|
144
155
|
const formId = this.getNodeParameter('formId');
|
|
145
156
|
const eventType = this.getNodeParameter('event');
|
|
157
|
+
const webhookSecret = (0, FormbaseWebhookSignature_1.createFormbaseWebhookSecret)();
|
|
146
158
|
const created = (await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.create', {
|
|
147
159
|
formId,
|
|
148
160
|
targetUrl: webhookUrl,
|
|
149
161
|
provider: 'n8n',
|
|
150
162
|
eventType,
|
|
163
|
+
signingSecret: webhookSecret,
|
|
151
164
|
}));
|
|
152
165
|
const webhookData = this.getWorkflowStaticData('node');
|
|
153
166
|
webhookData.subscriptionId = created.subscriptionId;
|
|
167
|
+
webhookData.webhookSecret = webhookSecret;
|
|
154
168
|
return true;
|
|
155
169
|
},
|
|
156
170
|
async delete() {
|
|
157
171
|
const webhookData = this.getWorkflowStaticData('node');
|
|
158
172
|
const subscriptionId = webhookData.subscriptionId;
|
|
159
|
-
if (typeof subscriptionId !== 'string')
|
|
173
|
+
if (typeof subscriptionId !== 'string') {
|
|
174
|
+
clearWebhookRegistration(webhookData);
|
|
160
175
|
return true;
|
|
176
|
+
}
|
|
161
177
|
try {
|
|
162
178
|
await GenericFunctions_1.formbaseApiRequest.call(this, 'webhooks.delete', {
|
|
163
179
|
subscriptionId,
|
|
@@ -168,13 +184,17 @@ class FormbaseTrigger {
|
|
|
168
184
|
return false;
|
|
169
185
|
}
|
|
170
186
|
}
|
|
171
|
-
|
|
187
|
+
clearWebhookRegistration(webhookData);
|
|
172
188
|
return true;
|
|
173
189
|
},
|
|
174
190
|
},
|
|
175
191
|
};
|
|
176
192
|
}
|
|
177
193
|
async webhook() {
|
|
194
|
+
if (!(0, FormbaseWebhookSignature_1.verifyFormbaseWebhookSignature)(this)) {
|
|
195
|
+
this.getResponseObject().status(401).send('Unauthorized').end();
|
|
196
|
+
return { noWebhookResponse: true };
|
|
197
|
+
}
|
|
178
198
|
const body = this.getBodyData();
|
|
179
199
|
return {
|
|
180
200
|
workflowData: [this.helpers.returnJsonArray(body)],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormbaseTrigger.node.js","sourceRoot":"","sources":["../../../nodes/Formbase/FormbaseTrigger.node.ts"],"names":[],"mappings":";;;AAUA,+CAAgE;AAEhE,2CAAiH;AACjH,yDAAuD;AA8BvD,MAAM,eAAe,GAAG,GAAG,CAAA;AAE3B,KAAK,UAAU,kBAAkB,CAAC,OAA8B,EAAE,SAA2B;IAC3F,MAAM,KAAK,GAAkB,EAAE,CAAA;IAC/B,IAAI,MAA0B,CAAA;IAE9B,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,MAAM,qCAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE;YACnE,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,KAAK,EAAE,eAAe;YACtB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9B,CAAC,CAA8B,CAAA;QAEhC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAC3B,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAK;QAC1B,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACvB,MAAM,IAAI,2BAAY,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,4CAA4C,EAAE,CAAC,CAAA;QACtG,CAAC;QACD,MAAM,GAAG,MAAM,CAAC,UAAU,CAAA;IAC5B,CAAC,QAAQ,MAAM,EAAC;IAEhB,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,uBAAuB,CAC9B,aAAoC,EACpC,SAAiB,EACjB,SAA+B;IAE/B,OAAO,aAAa,CAAC,IAAI,CACvB,CAAC,YAAY,EAAE,EAAE,CACf,YAAY,CAAC,SAAS,KAAK,SAAS,IAAI,YAAY,CAAC,QAAQ,KAAK,KAAK,IAAI,YAAY,CAAC,SAAS,KAAK,SAAS,CAClH,CAAA;AACH,CAAC;AAED,MAAa,eAAe;IAA5B;QACE,gBAAW,GAAyB;YAClC,WAAW,EAAE,kBAAkB;YAC/B,IAAI,EAAE,iBAAiB;YACvB,IAAI,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,IAAI,EAAE,6BAA6B,EAAE;YAC9E,KAAK,EAAE,CAAC,SAAS,CAAC;YAClB,OAAO,EAAE,CAAC;YACV,QAAQ,EACN,2GAA2G;YAC7G,WAAW,EAAE,gEAAgE;YAC7E,
|
|
1
|
+
{"version":3,"file":"FormbaseTrigger.node.js","sourceRoot":"","sources":["../../../nodes/Formbase/FormbaseTrigger.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFormbaseWebhookSecret = createFormbaseWebhookSecret;
|
|
4
|
+
exports.verifyFormbaseWebhookSignature = verifyFormbaseWebhookSignature;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const SIGNATURE_HEADER_PATTERN = /^t=(\d+),sha256=([a-f0-9]{64})$/;
|
|
7
|
+
const SIGNATURE_MAX_AGE_SECONDS = 5 * 60;
|
|
8
|
+
function getRawBody(request) {
|
|
9
|
+
if (!('rawBody' in request))
|
|
10
|
+
return undefined;
|
|
11
|
+
const rawBody = request.rawBody;
|
|
12
|
+
if (Buffer.isBuffer(rawBody))
|
|
13
|
+
return rawBody;
|
|
14
|
+
if (typeof rawBody === 'string')
|
|
15
|
+
return Buffer.from(rawBody);
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
function createFormbaseWebhookSecret() {
|
|
19
|
+
return `whsec_${(0, node_crypto_1.randomBytes)(32).toString('hex')}`;
|
|
20
|
+
}
|
|
21
|
+
function verifyFormbaseWebhookSignature(context) {
|
|
22
|
+
const webhookData = context.getWorkflowStaticData('node');
|
|
23
|
+
const secret = webhookData.webhookSecret;
|
|
24
|
+
if (typeof secret !== 'string' || secret.length === 0)
|
|
25
|
+
return false;
|
|
26
|
+
const signatureHeader = context.getHeaderData()['x-formbase-signature'];
|
|
27
|
+
if (typeof signatureHeader !== 'string')
|
|
28
|
+
return false;
|
|
29
|
+
const match = SIGNATURE_HEADER_PATTERN.exec(signatureHeader);
|
|
30
|
+
if (!match)
|
|
31
|
+
return false;
|
|
32
|
+
const [, timestamp, signatureHex] = match;
|
|
33
|
+
const timestampSeconds = Number(timestamp);
|
|
34
|
+
if (!Number.isSafeInteger(timestampSeconds))
|
|
35
|
+
return false;
|
|
36
|
+
const currentTimestampSeconds = Math.floor(Date.now() / 1000);
|
|
37
|
+
if (Math.abs(currentTimestampSeconds - timestampSeconds) > SIGNATURE_MAX_AGE_SECONDS)
|
|
38
|
+
return false;
|
|
39
|
+
const rawBody = getRawBody(context.getRequestObject());
|
|
40
|
+
if (!rawBody)
|
|
41
|
+
return false;
|
|
42
|
+
const expectedSignature = (0, node_crypto_1.createHmac)('sha256', secret).update(timestamp).update('.').update(rawBody).digest();
|
|
43
|
+
const receivedSignature = Buffer.from(signatureHex, 'hex');
|
|
44
|
+
return expectedSignature.length === receivedSignature.length && (0, node_crypto_1.timingSafeEqual)(expectedSignature, receivedSignature);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=FormbaseWebhookSignature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormbaseWebhookSignature.js","sourceRoot":"","sources":["../../../nodes/Formbase/FormbaseWebhookSignature.ts"],"names":[],"mappings":";;AAgBA,kEAEC;AAED,wEAyBC;AA7CD,6CAAsE;AAItE,MAAM,wBAAwB,GAAG,iCAAiC,CAAA;AAClE,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,CAAA;AAExC,SAAS,UAAU,CAAC,OAA0D;IAC5E,IAAI,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IAE7C,MAAM,OAAO,GAAY,OAAO,CAAC,OAAO,CAAA;IACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC5D,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAgB,2BAA2B;IACzC,OAAO,SAAS,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAA;AACnD,CAAC;AAED,SAAgB,8BAA8B,CAAC,OAA0B;IACvE,MAAM,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,aAAa,CAAA;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEnE,MAAM,eAAe,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,sBAAsB,CAAC,CAAA;IACvE,IAAI,OAAO,eAAe,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAErD,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAExB,MAAM,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAA;IACzC,MAAM,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC;QAAE,OAAO,KAAK,CAAA;IAEzD,MAAM,uBAAuB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAC7D,IAAI,IAAI,CAAC,GAAG,CAAC,uBAAuB,GAAG,gBAAgB,CAAC,GAAG,yBAAyB;QAAE,OAAO,KAAK,CAAA;IAElG,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IACtD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAA;IAE1B,MAAM,iBAAiB,GAAG,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAA;IAC7G,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;IAE1D,OAAO,iBAAiB,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM,IAAI,IAAA,6BAAe,EAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAA;AACvH,CAAC"}
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-formbase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "n8n community node for formbase — trigger workflows from form submissions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"prepublishOnly": "npm test && npm run build && npm run lint"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"examples"
|
|
36
37
|
],
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=22"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Handle formbase submission",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {
|
|
6
|
+
"formId": "",
|
|
7
|
+
"event": "submission_created"
|
|
8
|
+
},
|
|
9
|
+
"type": "n8n-nodes-formbase.formbaseTrigger",
|
|
10
|
+
"typeVersion": 1,
|
|
11
|
+
"position": [0, 0],
|
|
12
|
+
"id": "16e911f0-6385-4acc-8286-1afb15eb035c",
|
|
13
|
+
"name": "formbase Trigger"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"parameters": {
|
|
17
|
+
"assignments": {
|
|
18
|
+
"assignments": [
|
|
19
|
+
{
|
|
20
|
+
"id": "cdcb57d7-377e-45e2-8da9-63e52f4690b4",
|
|
21
|
+
"name": "eventId",
|
|
22
|
+
"value": "={{ $json.eventId }}",
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "b27b42be-fc5d-491f-a93b-02a7fa4e63f8",
|
|
27
|
+
"name": "submissionId",
|
|
28
|
+
"value": "={{ $json.submission.id }}",
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "2b171786-cb32-4e73-96e5-d60f39060fe1",
|
|
33
|
+
"name": "respondentEmail",
|
|
34
|
+
"value": "={{ $json.submission.respondentEmail }}",
|
|
35
|
+
"type": "string"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "a6366cdf-c3b9-4bdc-be3c-723d2bfab7a4",
|
|
39
|
+
"name": "formName",
|
|
40
|
+
"value": "={{ $json.form.name }}",
|
|
41
|
+
"type": "string"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"options": {}
|
|
46
|
+
},
|
|
47
|
+
"type": "n8n-nodes-base.set",
|
|
48
|
+
"typeVersion": 3.4,
|
|
49
|
+
"position": [240, 0],
|
|
50
|
+
"id": "27abc7e3-21e9-4654-9078-b12b60420f6d",
|
|
51
|
+
"name": "Prepare submission"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"pinData": {},
|
|
55
|
+
"connections": {
|
|
56
|
+
"formbase Trigger": {
|
|
57
|
+
"main": [
|
|
58
|
+
[
|
|
59
|
+
{
|
|
60
|
+
"node": "Prepare submission",
|
|
61
|
+
"type": "main",
|
|
62
|
+
"index": 0
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"active": false,
|
|
69
|
+
"settings": {
|
|
70
|
+
"executionOrder": "v1"
|
|
71
|
+
},
|
|
72
|
+
"versionId": "0f0cc0ea-543e-43ac-bf55-9256b1ee5f85",
|
|
73
|
+
"meta": {
|
|
74
|
+
"templateCredsSetupCompleted": false
|
|
75
|
+
},
|
|
76
|
+
"tags": []
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-formbase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "n8n community node for formbase — trigger workflows from form submissions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"prepublishOnly": "npm test && npm run build && npm run lint"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"examples"
|
|
36
37
|
],
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=22"
|