n8n-nodes-message-debounce 0.1.2

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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/dist/credentials/RedisApi.credentials.d.ts +16 -0
  4. package/dist/credentials/RedisApi.credentials.d.ts.map +1 -0
  5. package/dist/credentials/RedisApi.credentials.js +70 -0
  6. package/dist/credentials/RedisApi.credentials.js.map +1 -0
  7. package/dist/credentials/redisApi.svg +17 -0
  8. package/dist/nodes/MessageDebounce/MessageDebounce.description.d.ts +8 -0
  9. package/dist/nodes/MessageDebounce/MessageDebounce.description.d.ts.map +1 -0
  10. package/dist/nodes/MessageDebounce/MessageDebounce.description.js +247 -0
  11. package/dist/nodes/MessageDebounce/MessageDebounce.description.js.map +1 -0
  12. package/dist/nodes/MessageDebounce/MessageDebounce.node.d.ts +19 -0
  13. package/dist/nodes/MessageDebounce/MessageDebounce.node.d.ts.map +1 -0
  14. package/dist/nodes/MessageDebounce/MessageDebounce.node.js +102 -0
  15. package/dist/nodes/MessageDebounce/MessageDebounce.node.js.map +1 -0
  16. package/dist/nodes/MessageDebounce/constants.d.ts +17 -0
  17. package/dist/nodes/MessageDebounce/constants.d.ts.map +1 -0
  18. package/dist/nodes/MessageDebounce/constants.js +42 -0
  19. package/dist/nodes/MessageDebounce/constants.js.map +1 -0
  20. package/dist/nodes/MessageDebounce/execute.d.ts +13 -0
  21. package/dist/nodes/MessageDebounce/execute.d.ts.map +1 -0
  22. package/dist/nodes/MessageDebounce/execute.js +217 -0
  23. package/dist/nodes/MessageDebounce/execute.js.map +1 -0
  24. package/dist/nodes/MessageDebounce/helpers.d.ts +19 -0
  25. package/dist/nodes/MessageDebounce/helpers.d.ts.map +1 -0
  26. package/dist/nodes/MessageDebounce/helpers.js +43 -0
  27. package/dist/nodes/MessageDebounce/helpers.js.map +1 -0
  28. package/dist/nodes/MessageDebounce/messageDebounce.svg +17 -0
  29. package/dist/nodes/MessageDebounce/types.d.ts +30 -0
  30. package/dist/nodes/MessageDebounce/types.d.ts.map +1 -0
  31. package/dist/nodes/MessageDebounce/types.js +7 -0
  32. package/dist/nodes/MessageDebounce/types.js.map +1 -0
  33. package/dist/nodes/MessageDebounce/utils/RedisClient.d.ts +59 -0
  34. package/dist/nodes/MessageDebounce/utils/RedisClient.d.ts.map +1 -0
  35. package/dist/nodes/MessageDebounce/utils/RedisClient.js +270 -0
  36. package/dist/nodes/MessageDebounce/utils/RedisClient.js.map +1 -0
  37. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Reis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # n8n-nodes-message-debounce
2
+
3
+ <p align="center">
4
+ <img src="https://uaiautomacao.com/logo.png" alt="U.ai Automação" width="200"/>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/n8n-nodes-message-debounce"><img src="https://img.shields.io/npm/v/n8n-nodes-message-debounce.svg" alt="npm version"/></a>
9
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"/></a>
10
+ <a href="https://uaiautomacao.com"><img src="https://img.shields.io/badge/by-U.ai%20Automa%C3%A7%C3%A3o-blue" alt="by U.ai Automação"/></a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ An n8n community node that groups multiple inputs within a time window before continuing the flow — preventing your automation from reacting to every single message before the user finishes writing.
16
+
17
+ > **Real-world use case:** A user sends "hi", then "how are you", then "I need help with my order". Without debounce, your flow fires three times. With this node, it waits for silence and processes everything as one consolidated message.
18
+
19
+ ---
20
+
21
+ ## How it works
22
+
23
+ Every time a message arrives, the node:
24
+
25
+ 1. Stores the message in Redis under the session's key
26
+ 2. Waits for the configured debounce window
27
+ 3. After the wait, checks if any new message arrived during that time
28
+ 4. If no new messages arrived → flushes everything as a single output
29
+ 5. If a new message arrived → silently stops (the newer execution will take over)
30
+
31
+ When the node is still waiting, **it emits nothing** — the flow simply stops there. No need for IF nodes or filters after it.
32
+
33
+ ---
34
+
35
+ ## Installation
36
+
37
+ In your n8n instance, go to **Settings → Community Nodes** and install:
38
+
39
+ ```
40
+ n8n-nodes-message-debounce
41
+ ```
42
+
43
+ > **Requirement:** A Redis instance must be accessible from your n8n environment. In production (queue mode), Redis is already required by n8n — so no extra setup needed.
44
+
45
+ ---
46
+
47
+ ## Configuration
48
+
49
+ ### Required fields
50
+
51
+ | Field | Description |
52
+ |---|---|
53
+ | **Redis Credential** | Your Redis connection configured in n8n credentials |
54
+ | **Session ID** | Unique identifier for the conversation or session (e.g. chat ID, user ID) |
55
+ | **Message** | The content of the incoming message |
56
+ | **Debounce Window** | Seconds to wait for silence before flushing (e.g. `10`) |
57
+
58
+ ### Options
59
+
60
+ | Option | Description | Default |
61
+ |---|---|---|
62
+ | **Max Messages** | Force flush after N messages, regardless of the timer | — |
63
+ | **Max Wait Time** | Maximum total seconds before forcing flush, even if messages keep arriving | — |
64
+ | **Flush Keywords** | List of words that trigger an immediate flush when detected in the message | — |
65
+ | **On Duplicate Message** | What to do when the same message arrives twice: `ignore`, `include`, or `flush` | `include` |
66
+ | **First Message Behavior** | Special behavior for the first message of a new session: `immediate` flush or `customWindow` with a shorter timer | — |
67
+ | **Session TTL** | Seconds of inactivity before a session is considered expired (child of First Message Behavior). Set to `never` to keep sessions permanently. | — |
68
+ | **Separator** | String used to join multiple messages | `\n` |
69
+
70
+ > ⚠️ **Memory warning:** If Session TTL is set to `never expire`, sessions will accumulate in Redis indefinitely. Monitor your Redis memory usage.
71
+
72
+ > 💡 **Max Messages + Max Wait Time:** If both are set, whichever condition is met first triggers the flush.
73
+
74
+ ---
75
+
76
+ ## Output
77
+
78
+ When the debounce fires, the node outputs a single item:
79
+
80
+ ```json
81
+ {
82
+ "fullMessage": "hi\nhow are you\nI need help with my order",
83
+ "messageCount": 3,
84
+ "flushReason": "debounceWindow"
85
+ }
86
+ ```
87
+
88
+ ### flushReason values
89
+
90
+ | Value | Meaning |
91
+ |---|---|
92
+ | `debounceWindow` | Silence window elapsed normally |
93
+ | `maxMessages` | Max message count reached |
94
+ | `maxWaitTime` | Max wait time reached |
95
+ | `keyword` | A flush keyword was detected |
96
+ | `firstMessage` | First message of a new session with immediate flush configured |
97
+ | `duplicate` | Duplicate message triggered flush |
98
+
99
+ ---
100
+
101
+ ## Example flow
102
+
103
+ ```
104
+ Webhook → [your processing nodes] → Message Debounce → AI Agent
105
+ ```
106
+
107
+ The node fits anywhere in your flow. Process audio transcriptions, extract documents, enrich data — then pass everything into the debounce node. It handles the rest.
108
+
109
+ ---
110
+
111
+ ## Error handling
112
+
113
+ The node throws explicit errors in the following situations:
114
+
115
+ - **`Debounce Window is required`** — field left empty
116
+ - **`Session ID is required`** — field left empty
117
+ - **`Message is required`** — field left empty
118
+ - **`Redis connection failed: <detail>`** — could not connect to Redis
119
+ - **`Redis operation failed: <detail>`** — an operation failed during execution
120
+
121
+ Errors are always explicit and descriptive — never silent.
122
+
123
+ ---
124
+
125
+ ## Technical notes
126
+
127
+ - All Redis operations use atomic Lua scripts to prevent race conditions in high-concurrency scenarios
128
+ - Messages are tracked by unique ID, not by content — so duplicate messages are handled correctly
129
+ - When suppressed (not the last message), the node emits **no items** and the flow stops cleanly
130
+
131
+ ---
132
+
133
+ ## About
134
+
135
+ Built by **[U.ai Automação](https://uaiautomacao.com)** — automation solutions for real-world workflows.
136
+
137
+ ---
138
+
139
+ ## License
140
+
141
+ [MIT](LICENSE)
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Redis API credential for the MessageDebounce node.
3
+ *
4
+ * Redis connections are validated when the node executes (via RedisClient.connect()).
5
+ * A separate HTTP-based test is not applicable for TCP protocol services like Redis,
6
+ * so the credential-test-required rule is disabled here intentionally.
7
+ */
8
+ import type { ICredentialType, INodeProperties } from 'n8n-workflow';
9
+ export declare class RedisApi implements ICredentialType {
10
+ name: string;
11
+ displayName: string;
12
+ icon: "file:../../nodes/MessageDebounce/messageDebounce.svg";
13
+ documentationUrl: string;
14
+ properties: INodeProperties[];
15
+ }
16
+ //# sourceMappingURL=RedisApi.credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/RedisApi.credentials.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,OAAO,KAAK,EACR,eAAe,EACf,eAAe,EAClB,MAAM,cAAc,CAAC;AAEtB,qBAAa,QAAS,YAAW,eAAe;IAC5C,IAAI,SAAc;IAElB,WAAW,SAAe;IAE1B,IAAI,yDAAmE;IAEvE,gBAAgB,SAAoC;IAEpD,UAAU,EAAE,eAAe,EAAE,CAwD3B;CACL"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisApi = void 0;
4
+ class RedisApi {
5
+ constructor() {
6
+ this.name = 'redisApi';
7
+ this.displayName = 'Redis API';
8
+ this.icon = 'file:../../nodes/MessageDebounce/messageDebounce.svg';
9
+ this.documentationUrl = 'https://redis.io/docs/connect/';
10
+ this.properties = [
11
+ {
12
+ displayName: 'Password',
13
+ name: 'password',
14
+ type: 'string',
15
+ typeOptions: {
16
+ password: true,
17
+ },
18
+ default: '',
19
+ description: 'The Redis password (leave empty if none)',
20
+ },
21
+ {
22
+ displayName: 'User',
23
+ name: 'username',
24
+ type: 'string',
25
+ default: '',
26
+ description: 'Leave blank for password-only auth',
27
+ },
28
+ {
29
+ displayName: 'Host',
30
+ name: 'host',
31
+ type: 'string',
32
+ default: 'localhost',
33
+ description: 'The Redis host to connect to',
34
+ required: true,
35
+ },
36
+ {
37
+ displayName: 'Port',
38
+ name: 'port',
39
+ type: 'number',
40
+ typeOptions: {
41
+ minValue: 1,
42
+ maxValue: 65535,
43
+ },
44
+ default: 6379,
45
+ description: 'The Redis port to connect to',
46
+ required: true,
47
+ },
48
+ {
49
+ displayName: 'Database Number',
50
+ name: 'database',
51
+ type: 'number',
52
+ typeOptions: {
53
+ minValue: 0,
54
+ maxValue: 15,
55
+ },
56
+ default: 0,
57
+ description: 'The Redis database index (0–15)',
58
+ },
59
+ {
60
+ displayName: 'SSL',
61
+ name: 'tls',
62
+ type: 'boolean',
63
+ default: false,
64
+ description: 'Whether to use TLS/SSL for the Redis connection',
65
+ },
66
+ ];
67
+ }
68
+ }
69
+ exports.RedisApi = RedisApi;
70
+ //# sourceMappingURL=RedisApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisApi.credentials.js","sourceRoot":"","sources":["../../credentials/RedisApi.credentials.ts"],"names":[],"mappings":";;;AAaA,MAAa,QAAQ;IAArB;QACI,SAAI,GAAG,UAAU,CAAC;QAElB,gBAAW,GAAG,WAAW,CAAC;QAE1B,SAAI,GAAG,sDAA+D,CAAC;QAEvE,qBAAgB,GAAG,gCAAgC,CAAC;QAEpD,eAAU,GAAsB;YAC5B;gBACI,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE;oBACT,QAAQ,EAAE,IAAI;iBACjB;gBACD,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,0CAA0C;aAC1D;YACD;gBACI,WAAW,EAAE,MAAM;gBACnB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,oCAAoC;aACpD;YACD;gBACI,WAAW,EAAE,MAAM;gBACnB,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,WAAW;gBACpB,WAAW,EAAE,8BAA8B;gBAC3C,QAAQ,EAAE,IAAI;aACjB;YACD;gBACI,WAAW,EAAE,MAAM;gBACnB,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE;oBACT,QAAQ,EAAE,CAAC;oBACX,QAAQ,EAAE,KAAK;iBAClB;gBACD,OAAO,EAAE,IAAI;gBACb,WAAW,EAAE,8BAA8B;gBAC3C,QAAQ,EAAE,IAAI;aACjB;YACD;gBACI,WAAW,EAAE,iBAAiB;gBAC9B,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE;oBACT,QAAQ,EAAE,CAAC;oBACX,QAAQ,EAAE,EAAE;iBACf;gBACD,OAAO,EAAE,CAAC;gBACV,WAAW,EAAE,iCAAiC;aACjD;YACD;gBACI,WAAW,EAAE,KAAK;gBAClB,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,iDAAiD;aACjE;SACJ,CAAC;IACN,CAAC;CAAA;AAlED,4BAkEC"}
@@ -0,0 +1,17 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="none">
2
+ <!-- Background circle -->
3
+ <circle cx="30" cy="30" r="28" fill="#FF6B35" opacity="0.12"/>
4
+ <!-- Clock face -->
5
+ <circle cx="30" cy="30" r="20" stroke="#FF6B35" stroke-width="2.5" fill="none"/>
6
+ <!-- Clock hands -->
7
+ <line x1="30" y1="30" x2="30" y2="16" stroke="#FF6B35" stroke-width="2.5" stroke-linecap="round"/>
8
+ <line x1="30" y1="30" x2="40" y2="35" stroke="#FF6B35" stroke-width="2" stroke-linecap="round"/>
9
+ <!-- Center dot -->
10
+ <circle cx="30" cy="30" r="2" fill="#FF6B35"/>
11
+ <!-- Message dots (debounce indicator) -->
12
+ <circle cx="10" cy="48" r="3" fill="#FF6B35" opacity="0.4"/>
13
+ <circle cx="20" cy="52" r="3" fill="#FF6B35" opacity="0.65"/>
14
+ <circle cx="30" cy="54" r="3" fill="#FF6B35" opacity="0.9"/>
15
+ <!-- Arrow suggesting consolidation -->
16
+ <path d="M38 54 L50 54 L46 50 M50 54 L46 58" stroke="#FF6B35" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.8"/>
17
+ </svg>
@@ -0,0 +1,8 @@
1
+ /**
2
+ * UI definition for the MessageDebounce node.
3
+ * Contains the complete INodeTypeDescription — what the n8n editor renders.
4
+ * Keep this file focused on structure and labels only; no business logic here.
5
+ */
6
+ import type { INodeTypeDescription } from 'n8n-workflow';
7
+ export declare const description: INodeTypeDescription;
8
+ //# sourceMappingURL=MessageDebounce.description.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageDebounce.description.d.ts","sourceRoot":"","sources":["../../../nodes/MessageDebounce/MessageDebounce.description.ts"],"names":[],"mappings":"AACA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD,eAAO,MAAM,WAAW,EAAE,oBAsPzB,CAAC"}
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ /* eslint-disable n8n-nodes-base/node-filename-against-convention */
3
+ /**
4
+ * UI definition for the MessageDebounce node.
5
+ * Contains the complete INodeTypeDescription — what the n8n editor renders.
6
+ * Keep this file focused on structure and labels only; no business logic here.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.description = void 0;
10
+ exports.description = {
11
+ displayName: 'Message Debounce',
12
+ name: 'messageDebounce',
13
+ icon: 'file:messageDebounce.svg',
14
+ group: ['transform'],
15
+ version: 1,
16
+ description: 'Groups messages from the same session within a silence window and emits a single consolidated output. Silently stops when a newer message has already arrived.',
17
+ defaults: {
18
+ name: 'Message Debounce',
19
+ },
20
+ usableAsTool: true,
21
+ inputs: ['main'],
22
+ outputs: ['main'],
23
+ credentials: [
24
+ {
25
+ name: 'redisApi',
26
+ required: true,
27
+ },
28
+ ],
29
+ properties: [
30
+ // -----------------------------------------------------------------------
31
+ // Required fields
32
+ // -----------------------------------------------------------------------
33
+ {
34
+ displayName: 'Session ID',
35
+ name: 'sessionId',
36
+ type: 'string',
37
+ default: '',
38
+ placeholder: 'e.g. {{ $json.chatId }}',
39
+ description: 'Unique identifier for the conversation or session. All messages sharing the same Session ID are grouped together.',
40
+ required: true,
41
+ },
42
+ {
43
+ displayName: 'Message',
44
+ name: 'message',
45
+ type: 'string',
46
+ default: '',
47
+ placeholder: 'e.g. {{ $json.message }}',
48
+ description: 'The content of the incoming message to buffer',
49
+ required: true,
50
+ typeOptions: {
51
+ rows: 3,
52
+ },
53
+ },
54
+ {
55
+ displayName: 'Debounce Window (Seconds)',
56
+ name: 'debounceWindow',
57
+ type: 'number',
58
+ typeOptions: {
59
+ minValue: 1,
60
+ },
61
+ default: 10,
62
+ placeholder: 'e.g. 10',
63
+ description: 'Seconds of silence to wait before flushing all buffered messages. The timer resets each time a new message arrives.',
64
+ required: true,
65
+ },
66
+ // -----------------------------------------------------------------------
67
+ // Options collection — items MUST be in alphabetical order by displayName
68
+ // -----------------------------------------------------------------------
69
+ {
70
+ displayName: 'Options',
71
+ name: 'options',
72
+ type: 'collection',
73
+ placeholder: 'Add Option',
74
+ default: {},
75
+ options: [
76
+ // F
77
+ {
78
+ displayName: 'First Message Behavior',
79
+ name: 'firstMessageBehavior',
80
+ type: 'options',
81
+ default: 'none',
82
+ description: 'Special handling for the very first message of a new or expired session',
83
+ options: [
84
+ {
85
+ name: 'None',
86
+ value: 'none',
87
+ description: 'Treat the first message like any other (default)',
88
+ },
89
+ {
90
+ name: 'Flush Immediately',
91
+ value: 'immediate',
92
+ description: 'Emit the first message right away without waiting',
93
+ },
94
+ {
95
+ name: 'Use Custom Window',
96
+ value: 'customWindow',
97
+ description: 'Apply a shorter or longer silence window for the first message only',
98
+ },
99
+ ],
100
+ },
101
+ {
102
+ displayName: 'First Message Custom Window (Seconds)',
103
+ name: 'firstMessageCustomWindow',
104
+ type: 'number',
105
+ typeOptions: { minValue: 1 },
106
+ default: 3,
107
+ placeholder: 'e.g. 3',
108
+ description: 'Silence window in seconds to use for the first message of a new session',
109
+ displayOptions: {
110
+ show: {
111
+ firstMessageBehavior: ['customWindow'],
112
+ },
113
+ },
114
+ },
115
+ {
116
+ displayName: 'Flush Keywords',
117
+ name: 'flushKeywords',
118
+ type: 'fixedCollection',
119
+ default: {},
120
+ typeOptions: {
121
+ multipleValues: true,
122
+ },
123
+ description: 'Words that trigger an immediate flush when detected anywhere in the incoming message (case-insensitive)',
124
+ options: [
125
+ {
126
+ name: 'keywords',
127
+ displayName: 'Keywords',
128
+ values: [
129
+ {
130
+ displayName: 'Keyword',
131
+ name: 'keyword',
132
+ type: 'string',
133
+ default: '',
134
+ placeholder: 'e.g. urgent',
135
+ description: 'A word that triggers immediate flush when found in the message',
136
+ },
137
+ ],
138
+ },
139
+ ],
140
+ },
141
+ // M
142
+ {
143
+ displayName: 'Max Messages',
144
+ name: 'maxMessages',
145
+ type: 'number',
146
+ typeOptions: { minValue: 1 },
147
+ default: 0,
148
+ placeholder: 'e.g. 10',
149
+ description: 'Force flush after this many messages are buffered, regardless of the silence timer. Leave at 0 to disable.',
150
+ },
151
+ {
152
+ displayName: 'Max Wait Time (Seconds)',
153
+ name: 'maxWaitTime',
154
+ type: 'number',
155
+ typeOptions: { minValue: 1 },
156
+ default: 0,
157
+ placeholder: 'e.g. 60',
158
+ description: 'Maximum total seconds before forcing a flush, even if messages keep arriving. Whichever condition (Debounce Window or Max Wait Time) is met first triggers the flush. Leave at 0 to disable.',
159
+ },
160
+ // O
161
+ {
162
+ displayName: 'On Duplicate Message',
163
+ name: 'onDuplicateMessage',
164
+ type: 'options',
165
+ default: 'include',
166
+ description: 'What to do when the incoming message is identical to the last buffered one',
167
+ options: [
168
+ {
169
+ name: 'Include',
170
+ value: 'include',
171
+ description: 'Buffer the duplicate message normally (default)',
172
+ },
173
+ {
174
+ name: 'Ignore',
175
+ value: 'ignore',
176
+ description: 'Silently discard the duplicate — no output emitted',
177
+ },
178
+ {
179
+ name: 'Flush',
180
+ value: 'flush',
181
+ description: 'Treat the duplicate as a flush signal and emit immediately',
182
+ },
183
+ ],
184
+ },
185
+ // S
186
+ {
187
+ displayName: 'Separator',
188
+ name: 'separator',
189
+ type: 'string',
190
+ default: '\n',
191
+ placeholder: 'e.g. \\n',
192
+ description: 'String used to join buffered messages in the final output',
193
+ },
194
+ {
195
+ displayName: 'Session TTL',
196
+ name: 'sessionTtlUnit',
197
+ type: 'options',
198
+ default: 'never',
199
+ description: 'How long a session stays alive in Redis after the last message. When a session expires, the next message is treated as a first message.',
200
+ hint: 'Only applies when "First Message Behavior" is not set to "None".',
201
+ options: [
202
+ {
203
+ name: 'Days',
204
+ value: 'days',
205
+ },
206
+ {
207
+ name: 'Hours',
208
+ value: 'hours',
209
+ },
210
+ {
211
+ name: 'Minutes',
212
+ value: 'minutes',
213
+ },
214
+ {
215
+ name: 'Months',
216
+ value: 'months',
217
+ },
218
+ {
219
+ name: 'Never Expire',
220
+ value: 'never',
221
+ description: 'Sessions are kept in Redis indefinitely. Monitor your Redis memory usage.',
222
+ },
223
+ {
224
+ name: 'Years',
225
+ value: 'years',
226
+ },
227
+ ],
228
+ },
229
+ {
230
+ displayName: 'Session TTL Value',
231
+ name: 'sessionTtlValue',
232
+ type: 'number',
233
+ typeOptions: { minValue: 1 },
234
+ default: 30,
235
+ placeholder: 'e.g. 30',
236
+ description: 'Amount of time before the session expires in Redis',
237
+ displayOptions: {
238
+ hide: {
239
+ sessionTtlUnit: ['never'],
240
+ },
241
+ },
242
+ },
243
+ ],
244
+ },
245
+ ],
246
+ };
247
+ //# sourceMappingURL=MessageDebounce.description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageDebounce.description.js","sourceRoot":"","sources":["../../../nodes/MessageDebounce/MessageDebounce.description.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * MessageDebounce n8n community node — entry point.
3
+ *
4
+ * Intentionally thin: this file wires together the separated modules.
5
+ *
6
+ * Module map:
7
+ * description.ts → INodeTypeDescription (UI definition)
8
+ * execute.ts → runDebounce() (core debounce business logic)
9
+ * types.ts → shared interfaces and types
10
+ * constants.ts → Lua script, TTL multipliers
11
+ * helpers.ts → pure utility functions
12
+ * utils/RedisClient.ts → native RESP2 Redis client (zero external deps)
13
+ */
14
+ import { type IExecuteFunctions, type INodeExecutionData, type INodeType } from 'n8n-workflow';
15
+ export declare class MessageDebounce implements INodeType {
16
+ description: import("n8n-workflow").INodeTypeDescription;
17
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
18
+ }
19
+ //# sourceMappingURL=MessageDebounce.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageDebounce.node.d.ts","sourceRoot":"","sources":["../../../nodes/MessageDebounce/MessageDebounce.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAEjB,MAAM,cAAc,CAAC;AAStB,qBAAa,eAAgB,YAAW,SAAS;IAC7C,WAAW,8CAAe;IAEpB,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CA2F1E"}