@t0mer/n8n-nodes-ssllabs 2026.9.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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +234 -0
  3. package/dist/assets/demo/ssllabs-demo.png +0 -0
  4. package/dist/credentials/SslLabsApi.credentials.d.ts +10 -0
  5. package/dist/credentials/SslLabsApi.credentials.js +46 -0
  6. package/dist/credentials/SslLabsApi.credentials.js.map +1 -0
  7. package/dist/credentials/sslLabs.dark.svg +9 -0
  8. package/dist/credentials/sslLabs.svg +9 -0
  9. package/dist/nodes/SslLabs/SslLabs.node.d.ts +5 -0
  10. package/dist/nodes/SslLabs/SslLabs.node.js +91 -0
  11. package/dist/nodes/SslLabs/SslLabs.node.js.map +1 -0
  12. package/dist/nodes/SslLabs/SslLabs.node.json +18 -0
  13. package/dist/nodes/SslLabs/resources/assessment.d.ts +11 -0
  14. package/dist/nodes/SslLabs/resources/assessment.js +258 -0
  15. package/dist/nodes/SslLabs/resources/assessment.js.map +1 -0
  16. package/dist/nodes/SslLabs/resources/endpoint.d.ts +2 -0
  17. package/dist/nodes/SslLabs/resources/endpoint.js +77 -0
  18. package/dist/nodes/SslLabs/resources/endpoint.js.map +1 -0
  19. package/dist/nodes/SslLabs/resources/registration.d.ts +2 -0
  20. package/dist/nodes/SslLabs/resources/registration.js +71 -0
  21. package/dist/nodes/SslLabs/resources/registration.js.map +1 -0
  22. package/dist/nodes/SslLabs/resources/service.d.ts +3 -0
  23. package/dist/nodes/SslLabs/resources/service.js +86 -0
  24. package/dist/nodes/SslLabs/resources/service.js.map +1 -0
  25. package/dist/nodes/SslLabs/shared.d.ts +11 -0
  26. package/dist/nodes/SslLabs/shared.js +26 -0
  27. package/dist/nodes/SslLabs/shared.js.map +1 -0
  28. package/dist/nodes/SslLabs/sslLabs.dark.svg +9 -0
  29. package/dist/nodes/SslLabs/sslLabs.svg +9 -0
  30. package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.d.ts +5 -0
  31. package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.js +237 -0
  32. package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.js.map +1 -0
  33. package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.json +18 -0
  34. package/dist/nodes/SslLabsTrigger/sslLabs.dark.svg +9 -0
  35. package/dist/nodes/SslLabsTrigger/sslLabs.svg +9 -0
  36. package/dist/nodes/SslLabsTrigger/state.d.ts +35 -0
  37. package/dist/nodes/SslLabsTrigger/state.js +122 -0
  38. package/dist/nodes/SslLabsTrigger/state.js.map +1 -0
  39. package/dist/package.json +73 -0
  40. package/dist/shared/batch.d.ts +24 -0
  41. package/dist/shared/batch.js +66 -0
  42. package/dist/shared/batch.js.map +1 -0
  43. package/dist/shared/grades.d.ts +8 -0
  44. package/dist/shared/grades.js +38 -0
  45. package/dist/shared/grades.js.map +1 -0
  46. package/dist/shared/host.d.ts +2 -0
  47. package/dist/shared/host.js +38 -0
  48. package/dist/shared/host.js.map +1 -0
  49. package/dist/shared/poll.d.ts +29 -0
  50. package/dist/shared/poll.js +81 -0
  51. package/dist/shared/poll.js.map +1 -0
  52. package/dist/shared/summary.d.ts +8 -0
  53. package/dist/shared/summary.js +87 -0
  54. package/dist/shared/summary.js.map +1 -0
  55. package/dist/shared/transport.d.ts +37 -0
  56. package/dist/shared/transport.js +161 -0
  57. package/dist/shared/transport.js.map +1 -0
  58. package/dist/shared/types.d.ts +88 -0
  59. package/dist/shared/types.js +3 -0
  60. package/dist/shared/types.js.map +1 -0
  61. package/dist/tsconfig.tsbuildinfo +1 -0
  62. package/package.json +73 -0
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SslLabsTrigger = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const grades_1 = require("../../shared/grades");
6
+ const batch_1 = require("../../shared/batch");
7
+ const host_1 = require("../../shared/host");
8
+ const poll_1 = require("../../shared/poll");
9
+ const summary_1 = require("../../shared/summary");
10
+ const transport_1 = require("../../shared/transport");
11
+ const state_1 = require("./state");
12
+ const BUDGET_SHARE = 0.8;
13
+ const properties = [
14
+ {
15
+ displayName: 'Hosts',
16
+ name: 'hosts',
17
+ type: 'string',
18
+ typeOptions: { multipleValues: true, multipleValueButtonText: 'Add Host' },
19
+ default: [],
20
+ placeholder: 'example.com',
21
+ description: 'Hostnames to watch. Only watch hosts you own or are authorized to test.',
22
+ },
23
+ {
24
+ displayName: 'Event',
25
+ name: 'event',
26
+ type: 'options',
27
+ options: [
28
+ {
29
+ name: 'Certificate Expiring',
30
+ value: 'certificateExpiring',
31
+ description: 'Fires once per certificate when it expires within the given number of days',
32
+ },
33
+ {
34
+ name: 'Grade Below Threshold',
35
+ value: 'gradeBelowThreshold',
36
+ description: 'Fires once when the grade drops below the threshold; re-arms when the grade recovers',
37
+ },
38
+ {
39
+ name: 'Grade Changed',
40
+ value: 'gradeChanged',
41
+ description: "Fires when a host's grade differs from the last seen grade",
42
+ },
43
+ ],
44
+ default: 'gradeChanged',
45
+ },
46
+ {
47
+ displayName: 'Threshold Grade',
48
+ name: 'thresholdGrade',
49
+ type: 'options',
50
+ options: grades_1.GRADE_ORDER.map((grade) => ({ name: grade, value: grade })),
51
+ default: 'A',
52
+ description: 'Fire when the grade is worse than this. T (trust issues) and M (name mismatch) rank below F.',
53
+ displayOptions: { show: { event: ['gradeBelowThreshold'] } },
54
+ },
55
+ {
56
+ displayName: 'Days Before Expiry',
57
+ name: 'daysBeforeExpiry',
58
+ type: 'number',
59
+ default: 21,
60
+ typeOptions: { minValue: 0 },
61
+ description: 'Fire when the certificate expires within this many days',
62
+ displayOptions: { show: { event: ['certificateExpiring'] } },
63
+ },
64
+ {
65
+ displayName: 'Max Result Age (Hours)',
66
+ name: 'maxAge',
67
+ type: 'number',
68
+ default: 24,
69
+ typeOptions: { minValue: 1 },
70
+ description: 'Cached SSL Labs results up to this old are reused; a host is re-assessed at most this often',
71
+ },
72
+ {
73
+ displayName: 'Options',
74
+ name: 'options',
75
+ type: 'collection',
76
+ placeholder: 'Add Option',
77
+ default: {},
78
+ options: [
79
+ {
80
+ displayName: 'Emit Errors',
81
+ name: 'emitErrors',
82
+ type: 'boolean',
83
+ default: false,
84
+ description: 'Whether to emit an item when an assessment ends in ERROR',
85
+ },
86
+ ],
87
+ },
88
+ ];
89
+ function readHosts(ctx) {
90
+ const raw = ctx.getNodeParameter('hosts', []);
91
+ const values = (Array.isArray(raw) ? raw : [raw]).flatMap((v) => String(v).split(/[\s,]+/));
92
+ const hosts = values.filter((v) => v.trim() !== '').map((v) => (0, host_1.normalizeHost)(v, ctx.getNode()));
93
+ return [...new Set(hosts)];
94
+ }
95
+ function classify(error) {
96
+ if (error instanceof n8n_workflow_1.NodeApiError) {
97
+ if (error.message === transport_1.UNREGISTERED_EMAIL_MESSAGE)
98
+ return 'fatal';
99
+ if ((0, transport_1.isBusyError)(error))
100
+ return 'busy';
101
+ const status = Number(error.httpCode);
102
+ if (status === 400)
103
+ return 'host';
104
+ return status >= 400 && status < 500 ? 'fatal' : 'transient';
105
+ }
106
+ const code = error === null || error === void 0 ? void 0 : error.code;
107
+ const isNetwork = (error === null || error === void 0 ? void 0 : error.isAxiosError) === true;
108
+ return isNetwork || (typeof code === 'string' && /^E[A-Z]+/.test(code)) ? 'transient' : 'fatal';
109
+ }
110
+ class SslLabsTrigger {
111
+ constructor() {
112
+ this.description = {
113
+ displayName: 'SSL Labs Trigger',
114
+ name: 'sslLabsTrigger',
115
+ icon: { light: 'file:sslLabs.svg', dark: 'file:sslLabs.dark.svg' },
116
+ group: ['trigger'],
117
+ version: 1,
118
+ subtitle: '={{$parameter["event"]}}',
119
+ description: 'Watch SSL Labs grades and certificates of your hosts',
120
+ defaults: { name: 'SSL Labs Trigger' },
121
+ polling: true,
122
+ inputs: [],
123
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
124
+ credentials: [{ name: 'sslLabsApi', required: true }],
125
+ properties: [...properties],
126
+ };
127
+ }
128
+ async poll() {
129
+ var _a;
130
+ const hosts = readHosts(this);
131
+ const maxAge = this.getNodeParameter('maxAge', 24);
132
+ const options = this.getNodeParameter('options', {});
133
+ const threshold = this.getNodeParameter('thresholdGrade', 'A');
134
+ const config = {
135
+ event: this.getNodeParameter('event', 'gradeChanged'),
136
+ emitErrors: options.emitErrors === true,
137
+ thresholdGrade: (0, grades_1.isGrade)(threshold) ? threshold : 'A',
138
+ daysBeforeExpiry: this.getNodeParameter('daysBeforeExpiry', 21),
139
+ };
140
+ const analyzeOnce = async (host) => await (0, poll_1.analyze)(this, { host, fromCache: true, maxAge, all: 'done' }, true, { retry: false });
141
+ let coolOffMs;
142
+ const waitCoolOff = async () => {
143
+ if (coolOffMs === undefined) {
144
+ coolOffMs = batch_1.DEFAULT_COOL_OFF_MS;
145
+ try {
146
+ const { body } = await (0, transport_1.sslLabsRequest)(this, { path: 'info', retry: false });
147
+ const value = Number(body === null || body === void 0 ? void 0 : body.newAssessmentCoolOff);
148
+ if (value > 0)
149
+ coolOffMs = value + 100;
150
+ }
151
+ catch {
152
+ }
153
+ }
154
+ await (0, n8n_workflow_1.sleep)(coolOffMs);
155
+ };
156
+ if (this.getMode() === 'manual') {
157
+ const items = [];
158
+ for (const [index, host] of hosts.entries()) {
159
+ const result = await analyzeOnce(host);
160
+ items.push({ json: { ...(0, summary_1.toSummary)({ ...result, host }), event: 'test' } });
161
+ if (!(0, poll_1.isFinished)(result) && index < hosts.length - 1)
162
+ await waitCoolOff();
163
+ }
164
+ return items.length ? [items] : null;
165
+ }
166
+ const state = (0, state_1.loadState)(this.getWorkflowStaticData('node'));
167
+ const started = Date.now();
168
+ const budgetMs = typeof this.getPollBudgetMs === 'function'
169
+ ? this.getPollBudgetMs()
170
+ : Number.POSITIVE_INFINITY;
171
+ const ordered = [...hosts].sort((a, b) => { var _a, _b, _c, _d; return ((_b = (_a = state.hosts[a]) === null || _a === void 0 ? void 0 : _a.lastCheckedAt) !== null && _b !== void 0 ? _b : 0) - ((_d = (_c = state.hosts[b]) === null || _c === void 0 ? void 0 : _c.lastCheckedAt) !== null && _d !== void 0 ? _d : 0); });
172
+ const emitted = [];
173
+ let fatal;
174
+ let checked = 0;
175
+ let transientFailures = 0;
176
+ let lastTransient;
177
+ for (const [index, host] of ordered.entries()) {
178
+ if (checked++ > 0 && Date.now() - started >= budgetMs * BUDGET_SHARE)
179
+ break;
180
+ let result;
181
+ try {
182
+ result = await analyzeOnce(host);
183
+ }
184
+ catch (error) {
185
+ const failure = classify(error);
186
+ if (failure === 'fatal') {
187
+ fatal =
188
+ error instanceof n8n_workflow_1.NodeApiError || error instanceof n8n_workflow_1.NodeOperationError
189
+ ? error
190
+ : new n8n_workflow_1.NodeOperationError(this.getNode(), error);
191
+ break;
192
+ }
193
+ (0, state_1.touch)(state, host, Date.now());
194
+ if (failure === 'busy')
195
+ break;
196
+ if (failure === 'transient') {
197
+ transientFailures++;
198
+ lastTransient = error;
199
+ continue;
200
+ }
201
+ const summary = (0, summary_1.toSummary)({
202
+ host,
203
+ status: 'ERROR',
204
+ statusMessage: error.message,
205
+ });
206
+ emitted.push(...(0, state_1.applyResult)(state, summary, config, Date.now()));
207
+ continue;
208
+ }
209
+ const summary = (0, summary_1.toSummary)({ ...result, host });
210
+ if (!(0, poll_1.isFinished)(result)) {
211
+ const wasPending = ((_a = state.hosts[host]) === null || _a === void 0 ? void 0 : _a.pending) === true;
212
+ (0, state_1.markPending)(state, host, Date.now());
213
+ if (!wasPending && index < ordered.length - 1)
214
+ await waitCoolOff();
215
+ continue;
216
+ }
217
+ emitted.push(...(0, state_1.applyResult)(state, summary, config, Date.now()));
218
+ }
219
+ if (!fatal && ordered.length > 0 && transientFailures === ordered.length) {
220
+ fatal =
221
+ lastTransient instanceof n8n_workflow_1.NodeApiError
222
+ ? lastTransient
223
+ : new n8n_workflow_1.NodeOperationError(this.getNode(), lastTransient, {
224
+ description: 'Check the Base URL in the SSL Labs API credential and your network.',
225
+ });
226
+ }
227
+ if (fatal)
228
+ throw fatal;
229
+ for (const host of Object.keys(state.hosts)) {
230
+ if (!hosts.includes(host))
231
+ delete state.hosts[host];
232
+ }
233
+ return emitted.length ? [emitted.map((e) => ({ json: e }))] : null;
234
+ }
235
+ }
236
+ exports.SslLabsTrigger = SslLabsTrigger;
237
+ //# sourceMappingURL=SslLabsTrigger.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SslLabsTrigger.node.js","sourceRoot":"","sources":["../../../nodes/SslLabsTrigger/SslLabsTrigger.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ {
2
+ "node": "@t0mer/n8n-nodes-ssllabs.sslLabsTrigger",
3
+ "nodeVersion": "1.0",
4
+ "codexVersion": "1.0",
5
+ "categories": ["Development", "Developer Tools"],
6
+ "resources": {
7
+ "credentialDocumentation": [
8
+ {
9
+ "url": "https://github.com/t0mer/n8n-nodes-ssllabs#credentials"
10
+ }
11
+ ],
12
+ "primaryDocumentation": [
13
+ {
14
+ "url": "https://github.com/t0mer/n8n-nodes-ssllabs#readme"
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
2
+ <path d="M20 28v-7a12 12 0 0 1 24 0v7" fill="none" stroke="#58a6ff" stroke-width="5" stroke-linecap="round"/>
3
+ <rect x="10" y="27" width="44" height="31" rx="6" fill="#58a6ff"/>
4
+ <path d="M19 51a13 13 0 0 1 26 0" fill="none" stroke="#0d1117" stroke-width="3" stroke-linecap="round"/>
5
+ <path d="M19 51a13 13 0 0 1 6.5-11.26" fill="none" stroke="#e5534b" stroke-width="3" stroke-linecap="round"/>
6
+ <path d="M38.5 39.74A13 13 0 0 1 45 51" fill="none" stroke="#3fb950" stroke-width="3" stroke-linecap="round"/>
7
+ <line x1="32" y1="51" x2="39" y2="42" stroke="#0d1117" stroke-width="3" stroke-linecap="round"/>
8
+ <circle cx="32" cy="51" r="3" fill="#0d1117"/>
9
+ </svg>
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
2
+ <path d="M20 28v-7a12 12 0 0 1 24 0v7" fill="none" stroke="#1f5f8b" stroke-width="5" stroke-linecap="round"/>
3
+ <rect x="10" y="27" width="44" height="31" rx="6" fill="#1f5f8b"/>
4
+ <path d="M19 51a13 13 0 0 1 26 0" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
5
+ <path d="M19 51a13 13 0 0 1 6.5-11.26" fill="none" stroke="#e5534b" stroke-width="3" stroke-linecap="round"/>
6
+ <path d="M38.5 39.74A13 13 0 0 1 45 51" fill="none" stroke="#3fb950" stroke-width="3" stroke-linecap="round"/>
7
+ <line x1="32" y1="51" x2="39" y2="42" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
8
+ <circle cx="32" cy="51" r="3" fill="#ffffff"/>
9
+ </svg>
@@ -0,0 +1,35 @@
1
+ import { type Grade } from '../../shared/grades';
2
+ import type { Summary, SummaryCertificate } from '../../shared/types';
3
+ export declare const STATE_VERSION = 1;
4
+ export type TriggerEvent = 'gradeChanged' | 'gradeBelowThreshold' | 'certificateExpiring';
5
+ export interface HostState {
6
+ grade: string | null;
7
+ testTime: string | null;
8
+ threshold: string | null;
9
+ belowThreshold: boolean;
10
+ certificate: SummaryCertificate | null;
11
+ certAlerted: boolean;
12
+ pending: boolean;
13
+ error: string | null;
14
+ lastCheckedAt: number;
15
+ }
16
+ export interface TriggerState {
17
+ stateVersion: typeof STATE_VERSION;
18
+ hosts: Record<string, HostState>;
19
+ }
20
+ export interface EventConfig {
21
+ event: TriggerEvent;
22
+ emitErrors: boolean;
23
+ thresholdGrade: Grade;
24
+ daysBeforeExpiry: number;
25
+ }
26
+ export type EventItem = Summary & {
27
+ event: TriggerEvent | 'assessmentError';
28
+ previousGrade?: string | null;
29
+ thresholdGrade?: string;
30
+ previousCertificate?: SummaryCertificate | null;
31
+ };
32
+ export declare function loadState(staticData: Record<string, unknown>): TriggerState;
33
+ export declare function touch(state: TriggerState, host: string, now: number): void;
34
+ export declare function markPending(state: TriggerState, host: string, now: number): void;
35
+ export declare function applyResult(state: TriggerState, summary: Summary, config: EventConfig, now: number): EventItem[];
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STATE_VERSION = void 0;
4
+ exports.loadState = loadState;
5
+ exports.touch = touch;
6
+ exports.markPending = markPending;
7
+ exports.applyResult = applyResult;
8
+ const grades_1 = require("../../shared/grades");
9
+ exports.STATE_VERSION = 1;
10
+ const DAY_MS = 86400000;
11
+ function daysUntil(iso, now) {
12
+ const ms = iso ? Date.parse(iso) : Number.NaN;
13
+ return Number.isNaN(ms) ? null : Math.floor((ms - now) / DAY_MS);
14
+ }
15
+ const EMPTY_HOST = {
16
+ grade: null,
17
+ testTime: null,
18
+ threshold: null,
19
+ belowThreshold: false,
20
+ certificate: null,
21
+ certAlerted: false,
22
+ pending: false,
23
+ error: null,
24
+ };
25
+ function loadState(staticData) {
26
+ if (staticData.stateVersion !== exports.STATE_VERSION ||
27
+ typeof staticData.hosts !== 'object' ||
28
+ !staticData.hosts) {
29
+ staticData.stateVersion = exports.STATE_VERSION;
30
+ staticData.hosts = {};
31
+ }
32
+ return staticData;
33
+ }
34
+ function touch(state, host, now) {
35
+ var _a;
36
+ state.hosts[host] = { ...((_a = state.hosts[host]) !== null && _a !== void 0 ? _a : EMPTY_HOST), lastCheckedAt: now };
37
+ }
38
+ function markPending(state, host, now) {
39
+ const prev = state.hosts[host];
40
+ state.hosts[host] = prev
41
+ ? { ...prev, pending: true, lastCheckedAt: now }
42
+ : { ...EMPTY_HOST, pending: true, lastCheckedAt: now };
43
+ }
44
+ function applyResult(state, summary, config, now) {
45
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
46
+ const prev = state.hosts[summary.host];
47
+ const baseline = !prev || (prev.grade === null && prev.testTime === null);
48
+ const events = [];
49
+ if (summary.status === 'ERROR') {
50
+ const error = (_a = summary.statusMessage) !== null && _a !== void 0 ? _a : 'Assessment failed';
51
+ const hadResult = !!prev && (prev.testTime !== null || prev.error !== null);
52
+ if (config.emitErrors && hadResult && prev.error !== error) {
53
+ events.push({ ...summary, event: 'assessmentError' });
54
+ }
55
+ state.hosts[summary.host] = {
56
+ ...(prev !== null && prev !== void 0 ? prev : EMPTY_HOST),
57
+ pending: false,
58
+ error,
59
+ lastCheckedAt: now,
60
+ };
61
+ return events;
62
+ }
63
+ if (!baseline &&
64
+ config.event === 'gradeChanged' &&
65
+ prev.grade !== null &&
66
+ summary.grade !== null &&
67
+ prev.grade !== summary.grade) {
68
+ events.push({ ...summary, event: 'gradeChanged', previousGrade: prev.grade });
69
+ }
70
+ let threshold = null;
71
+ let belowThreshold = false;
72
+ if (config.event === 'gradeBelowThreshold') {
73
+ const tracked = !baseline && prev.threshold === config.thresholdGrade;
74
+ if (summary.grade !== null) {
75
+ const below = (0, grades_1.isWorseThan)(summary.grade, config.thresholdGrade);
76
+ if (tracked && below && !prev.belowThreshold) {
77
+ events.push({
78
+ ...summary,
79
+ event: 'gradeBelowThreshold',
80
+ previousGrade: prev.grade,
81
+ thresholdGrade: config.thresholdGrade,
82
+ });
83
+ }
84
+ threshold = config.thresholdGrade;
85
+ belowThreshold = below;
86
+ }
87
+ else if (tracked) {
88
+ threshold = config.thresholdGrade;
89
+ belowThreshold = prev.belowThreshold;
90
+ }
91
+ }
92
+ const certificate = (_c = (_b = summary.certificate) !== null && _b !== void 0 ? _b : prev === null || prev === void 0 ? void 0 : prev.certificate) !== null && _c !== void 0 ? _c : null;
93
+ const newCert = ((_d = certificate === null || certificate === void 0 ? void 0 : certificate.fingerprint) !== null && _d !== void 0 ? _d : null) !== ((_f = (_e = prev === null || prev === void 0 ? void 0 : prev.certificate) === null || _e === void 0 ? void 0 : _e.fingerprint) !== null && _f !== void 0 ? _f : null);
94
+ let certAlerted = newCert ? false : ((_g = prev === null || prev === void 0 ? void 0 : prev.certAlerted) !== null && _g !== void 0 ? _g : false);
95
+ const days = daysUntil(certificate === null || certificate === void 0 ? void 0 : certificate.notAfter, now);
96
+ if (!baseline &&
97
+ config.event === 'certificateExpiring' &&
98
+ !certAlerted &&
99
+ days !== null &&
100
+ days <= config.daysBeforeExpiry) {
101
+ events.push({
102
+ ...summary,
103
+ certificate: certificate && { ...certificate, daysUntilExpiry: days },
104
+ event: 'certificateExpiring',
105
+ ...(newCert && (prev === null || prev === void 0 ? void 0 : prev.certificate) ? { previousCertificate: prev.certificate } : {}),
106
+ });
107
+ certAlerted = true;
108
+ }
109
+ state.hosts[summary.host] = {
110
+ grade: (_j = (_h = summary.grade) !== null && _h !== void 0 ? _h : prev === null || prev === void 0 ? void 0 : prev.grade) !== null && _j !== void 0 ? _j : null,
111
+ testTime: summary.testTime,
112
+ threshold,
113
+ belowThreshold,
114
+ certificate,
115
+ certAlerted,
116
+ pending: false,
117
+ error: null,
118
+ lastCheckedAt: now,
119
+ };
120
+ return events;
121
+ }
122
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../nodes/SslLabsTrigger/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@t0mer/n8n-nodes-ssllabs",
3
+ "version": "2026.9.0",
4
+ "description": "n8n community node for the Qualys SSL Labs API v4: run SSL/TLS assessments, fetch grades, and watch for grade or certificate changes.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/t0mer/n8n-nodes-ssllabs#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/t0mer/n8n-nodes-ssllabs/issues"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "provenance": true
13
+ },
14
+ "keywords": [
15
+ "n8n-community-node-package",
16
+ "n8n",
17
+ "ssllabs",
18
+ "ssl",
19
+ "tls",
20
+ "certificate",
21
+ "security"
22
+ ],
23
+ "author": {
24
+ "name": "t0mer",
25
+ "email": "tomer.klein@gmail.com"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/t0mer/n8n-nodes-ssllabs.git"
30
+ },
31
+ "scripts": {
32
+ "build": "n8n-node build",
33
+ "build:watch": "tsc --watch",
34
+ "dev": "n8n-node dev",
35
+ "lint": "n8n-node lint",
36
+ "lint:fix": "n8n-node lint --fix",
37
+ "release": "n8n-node release",
38
+ "prepublishOnly": "n8n-node prerelease",
39
+ "test": "vitest run"
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "n8n": {
45
+ "n8nNodesApiVersion": 1,
46
+ "strict": true,
47
+ "credentials": [
48
+ "dist/credentials/SslLabsApi.credentials.js"
49
+ ],
50
+ "nodes": [
51
+ "dist/nodes/SslLabs/SslLabs.node.js",
52
+ "dist/nodes/SslLabsTrigger/SslLabsTrigger.node.js"
53
+ ]
54
+ },
55
+ "devDependencies": {
56
+ "@n8n/node-cli": "*",
57
+ "eslint": "9.32.0",
58
+ "prettier": "3.6.2",
59
+ "release-it": "^19.0.4",
60
+ "typescript": "5.9.2",
61
+ "vitest": "^3.2.7"
62
+ },
63
+ "peerDependencies": {
64
+ "n8n-workflow": "*"
65
+ },
66
+ "allowScripts": {
67
+ "isolated-vm@7.0.1": true,
68
+ "cpu-features@0.0.10": true,
69
+ "ssh2@1.15.0": true,
70
+ "unrs-resolver@1.12.2": true,
71
+ "eslint-plugin-n8n-nodes-base": false
72
+ }
73
+ }
@@ -0,0 +1,24 @@
1
+ export type PoolResult<T> = {
2
+ ok: true;
3
+ value: T;
4
+ } | {
5
+ ok: false;
6
+ error: Error;
7
+ };
8
+ export interface PoolOptions {
9
+ signal?: AbortSignal;
10
+ stopOnError: boolean;
11
+ }
12
+ export interface PoolOutcome<T> {
13
+ results: Array<PoolResult<T> | undefined>;
14
+ firstError?: {
15
+ index: number;
16
+ error: Error;
17
+ };
18
+ }
19
+ export declare function runPool<T>(count: number, concurrency: number, worker: (index: number, signal: AbortSignal) => Promise<T>, options: PoolOptions): Promise<PoolOutcome<T>>;
20
+ export declare function createStartGate(coolOffMs: number, sleep?: (ms: number, signal?: AbortSignal) => Promise<void>, now?: () => number): (signal?: AbortSignal) => Promise<void>;
21
+ export declare const DEFAULT_BATCH_CONCURRENCY = 3;
22
+ export declare const MAX_BATCH_CONCURRENCY = 10;
23
+ export declare const DEFAULT_COOL_OFF_MS = 1100;
24
+ export declare function batchConcurrency(requested: unknown, maxAssessments: unknown, currentAssessments: unknown): number;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_COOL_OFF_MS = exports.MAX_BATCH_CONCURRENCY = exports.DEFAULT_BATCH_CONCURRENCY = void 0;
4
+ exports.runPool = runPool;
5
+ exports.createStartGate = createStartGate;
6
+ exports.batchConcurrency = batchConcurrency;
7
+ const n8n_workflow_1 = require("n8n-workflow");
8
+ async function runPool(count, concurrency, worker, options) {
9
+ var _a, _b, _c;
10
+ const controller = new AbortController();
11
+ const onParentAbort = () => controller.abort();
12
+ if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted)
13
+ controller.abort();
14
+ (_b = options.signal) === null || _b === void 0 ? void 0 : _b.addEventListener('abort', onParentAbort, { once: true });
15
+ const outcome = { results: new Array(count).fill(undefined) };
16
+ let next = 0;
17
+ const runWorker = async () => {
18
+ while (next < count && !controller.signal.aborted) {
19
+ const index = next++;
20
+ try {
21
+ outcome.results[index] = { ok: true, value: await worker(index, controller.signal) };
22
+ }
23
+ catch (error) {
24
+ outcome.results[index] = { ok: false, error: error };
25
+ if (options.stopOnError && !outcome.firstError) {
26
+ outcome.firstError = { index, error: error };
27
+ controller.abort();
28
+ }
29
+ }
30
+ }
31
+ };
32
+ try {
33
+ const workers = Math.max(1, Math.min(concurrency, count));
34
+ await Promise.all(Array.from({ length: workers }, runWorker));
35
+ }
36
+ finally {
37
+ (_c = options.signal) === null || _c === void 0 ? void 0 : _c.removeEventListener('abort', onParentAbort);
38
+ }
39
+ return outcome;
40
+ }
41
+ function createStartGate(coolOffMs, sleep = n8n_workflow_1.sleep, now = Date.now) {
42
+ let lastStart = Number.NEGATIVE_INFINITY;
43
+ let queue = Promise.resolve();
44
+ return async (signal) => {
45
+ const turn = queue.then(async () => {
46
+ const wait = lastStart + coolOffMs - now();
47
+ if (wait > 0)
48
+ await sleep(wait, signal);
49
+ lastStart = now();
50
+ });
51
+ queue = turn.catch(() => undefined);
52
+ return await turn;
53
+ };
54
+ }
55
+ exports.DEFAULT_BATCH_CONCURRENCY = 3;
56
+ exports.MAX_BATCH_CONCURRENCY = 10;
57
+ exports.DEFAULT_COOL_OFF_MS = 1100;
58
+ function batchConcurrency(requested, maxAssessments, currentAssessments) {
59
+ const wanted = Math.min(exports.MAX_BATCH_CONCURRENCY, Math.max(1, Math.floor(Number(requested) || exports.DEFAULT_BATCH_CONCURRENCY)));
60
+ const max = Number(maxAssessments);
61
+ if (!Number.isFinite(max) || max <= 0)
62
+ return wanted;
63
+ const free = max - (Number(currentAssessments) || 0);
64
+ return Math.max(1, Math.min(wanted, free));
65
+ }
66
+ //# sourceMappingURL=batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.js","sourceRoot":"","sources":["../../shared/batch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare const GRADE_ORDER: readonly ["A+", "A", "A-", "B", "C", "D", "E", "F", "T", "M"];
2
+ export type Grade = (typeof GRADE_ORDER)[number];
3
+ export declare function isGrade(value: unknown): value is Grade;
4
+ export declare function gradeRank(grade: unknown): number | undefined;
5
+ export declare function compareGrades(a: unknown, b: unknown): number;
6
+ export declare function worstGrade(grades: unknown[]): Grade | null;
7
+ export declare function bestGrade(grades: unknown[]): Grade | null;
8
+ export declare function isWorseThan(grade: unknown, threshold: Grade): boolean;