@relayfile/adapter-linear 0.1.4 → 0.1.6
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/__tests__/linear-adapter.test.d.ts +2 -0
- package/dist/__tests__/linear-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/linear-adapter.test.js +295 -0
- package/dist/__tests__/linear-adapter.test.js.map +1 -0
- package/dist/__tests__/types.test.d.ts +2 -0
- package/dist/__tests__/types.test.d.ts.map +1 -0
- package/dist/__tests__/types.test.js +19 -0
- package/dist/__tests__/types.test.js.map +1 -0
- package/dist/__tests__/webhook-normalizer.test.d.ts +2 -0
- package/dist/__tests__/webhook-normalizer.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-normalizer.test.js +59 -0
- package/dist/__tests__/webhook-normalizer.test.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +1 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/linear-adapter.d.ts +73 -0
- package/dist/linear-adapter.d.ts.map +1 -0
- package/dist/linear-adapter.js +484 -0
- package/dist/linear-adapter.js.map +1 -0
- package/dist/path-mapper.d.ts +11 -0
- package/dist/path-mapper.d.ts.map +1 -0
- package/dist/path-mapper.js +57 -0
- package/dist/path-mapper.js.map +1 -0
- package/dist/types.d.ts +142 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook-normalizer.d.ts +43 -0
- package/dist/webhook-normalizer.d.ts.map +1 -0
- package/dist/webhook-normalizer.js +519 -0
- package/dist/webhook-normalizer.js.map +1 -0
- package/package.json +13 -4
- package/src/__tests__/linear-adapter.test.ts +0 -345
- package/src/__tests__/types.test.ts +0 -27
- package/src/__tests__/webhook-normalizer.test.ts +0 -77
- package/src/linear-adapter.ts +0 -680
- package/src/path-mapper.ts +0 -69
- package/src/types.ts +0 -164
- package/src/webhook-normalizer.ts +0 -721
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-adapter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-adapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { createHmac } from 'node:crypto';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import { LINEAR_SIGNATURE_HEADER, LinearAdapter, assertValidLinearWebhookSignature, computeLinearPath, linearCommentPath, linearCyclePath, linearIssuePath, linearProjectPath, normalizeLinearWebhook, validateLinearWebhookSignature, } from '../index.js';
|
|
5
|
+
function createAdapter(config = {}) {
|
|
6
|
+
const client = {
|
|
7
|
+
async writeFile() {
|
|
8
|
+
return { created: true };
|
|
9
|
+
},
|
|
10
|
+
async deleteFile() {
|
|
11
|
+
return undefined;
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
const provider = {
|
|
15
|
+
name: 'relayfile-test-provider',
|
|
16
|
+
async proxy(_request) {
|
|
17
|
+
return {
|
|
18
|
+
status: 200,
|
|
19
|
+
headers: {},
|
|
20
|
+
data: null,
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
async healthCheck() {
|
|
24
|
+
return true;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
return new LinearAdapter(client, provider, config);
|
|
28
|
+
}
|
|
29
|
+
test('LinearAdapter exposes the provider name and supported Linear webhook events', () => {
|
|
30
|
+
const adapter = createAdapter();
|
|
31
|
+
assert.equal(adapter.name, 'linear');
|
|
32
|
+
assert.deepEqual(adapter.supportedEvents(), [
|
|
33
|
+
'comment.create',
|
|
34
|
+
'comment.update',
|
|
35
|
+
'comment.remove',
|
|
36
|
+
'cycle.create',
|
|
37
|
+
'cycle.update',
|
|
38
|
+
'cycle.remove',
|
|
39
|
+
'issue.create',
|
|
40
|
+
'issue.update',
|
|
41
|
+
'issue.remove',
|
|
42
|
+
'project.create',
|
|
43
|
+
'project.update',
|
|
44
|
+
'project.remove',
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
test('normalizeLinearWebhook normalizes issue callbacks and preserves connection metadata', () => {
|
|
48
|
+
const normalized = normalizeLinearWebhook(JSON.stringify({
|
|
49
|
+
action: 'create',
|
|
50
|
+
type: 'Issue',
|
|
51
|
+
createdAt: '2026-03-28T10:00:00.000Z',
|
|
52
|
+
organizationId: 'org_123',
|
|
53
|
+
url: 'https://linear.app/acme/issue/ENG-123',
|
|
54
|
+
data: {
|
|
55
|
+
id: 'issue_123',
|
|
56
|
+
identifier: 'ENG-123',
|
|
57
|
+
title: 'Ship adapter tests',
|
|
58
|
+
},
|
|
59
|
+
}), {
|
|
60
|
+
'Linear-Delivery': 'delivery_123',
|
|
61
|
+
'X-Relay-Connection-Id': 'conn_linear_123',
|
|
62
|
+
'X-Relay-Provider-Config-Key': 'linear-primary',
|
|
63
|
+
'X-Request-Id': 'req_123',
|
|
64
|
+
});
|
|
65
|
+
assert.equal(normalized.provider, 'linear');
|
|
66
|
+
assert.equal(normalized.connectionId, 'conn_linear_123');
|
|
67
|
+
assert.equal(normalized.eventType, 'issue.create');
|
|
68
|
+
assert.equal(normalized.objectType, 'issue');
|
|
69
|
+
assert.equal(normalized.objectId, 'issue_123');
|
|
70
|
+
assert.deepEqual(normalized.payload._connection, {
|
|
71
|
+
connectionId: 'conn_linear_123',
|
|
72
|
+
deliveryId: 'delivery_123',
|
|
73
|
+
provider: 'linear',
|
|
74
|
+
providerConfigKey: 'linear-primary',
|
|
75
|
+
requestId: 'req_123',
|
|
76
|
+
});
|
|
77
|
+
assert.deepEqual(normalized.payload._webhook, {
|
|
78
|
+
action: 'create',
|
|
79
|
+
createdAt: '2026-03-28T10:00:00.000Z',
|
|
80
|
+
deliveryId: 'delivery_123',
|
|
81
|
+
eventType: 'issue.create',
|
|
82
|
+
objectId: 'issue_123',
|
|
83
|
+
objectType: 'issue',
|
|
84
|
+
organizationId: 'org_123',
|
|
85
|
+
url: 'https://linear.app/acme/issue/ENG-123',
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
test('normalizeLinearWebhook normalizes comment callbacks from payload metadata', () => {
|
|
89
|
+
const normalized = normalizeLinearWebhook({
|
|
90
|
+
action: 'update',
|
|
91
|
+
type: 'Comments',
|
|
92
|
+
createdAt: '2026-03-28T11:00:00.000Z',
|
|
93
|
+
metadata: {
|
|
94
|
+
provider: 'linear',
|
|
95
|
+
providerConfigKey: 'linear-secondary',
|
|
96
|
+
connectionId: 'conn_linear_456',
|
|
97
|
+
},
|
|
98
|
+
connection: {
|
|
99
|
+
id: 'conn_linear_ignored',
|
|
100
|
+
},
|
|
101
|
+
data: {
|
|
102
|
+
id: 'comment_123',
|
|
103
|
+
body: 'Looks good to me.',
|
|
104
|
+
issue: {
|
|
105
|
+
id: 'issue_456',
|
|
106
|
+
identifier: 'ENG-456',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
assert.equal(normalized.provider, 'linear');
|
|
111
|
+
assert.equal(normalized.connectionId, 'conn_linear_456');
|
|
112
|
+
assert.equal(normalized.eventType, 'comment.update');
|
|
113
|
+
assert.equal(normalized.objectType, 'comment');
|
|
114
|
+
assert.equal(normalized.objectId, 'comment_123');
|
|
115
|
+
assert.deepEqual(normalized.payload.data, {
|
|
116
|
+
id: 'comment_123',
|
|
117
|
+
body: 'Looks good to me.',
|
|
118
|
+
issue: {
|
|
119
|
+
id: 'issue_456',
|
|
120
|
+
identifier: 'ENG-456',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
assert.deepEqual(normalized.payload._connection, {
|
|
124
|
+
connectionId: 'conn_linear_456',
|
|
125
|
+
provider: 'linear',
|
|
126
|
+
providerConfigKey: 'linear-secondary',
|
|
127
|
+
});
|
|
128
|
+
assert.deepEqual(normalized.payload._webhook, {
|
|
129
|
+
action: 'update',
|
|
130
|
+
createdAt: '2026-03-28T11:00:00.000Z',
|
|
131
|
+
eventType: 'comment.update',
|
|
132
|
+
objectId: 'comment_123',
|
|
133
|
+
objectType: 'comment',
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
test('signature rejection handling is deterministic for result and throwing helpers', () => {
|
|
137
|
+
const rawPayload = JSON.stringify({
|
|
138
|
+
action: 'create',
|
|
139
|
+
type: 'Issue',
|
|
140
|
+
data: { id: 'issue_123' },
|
|
141
|
+
});
|
|
142
|
+
const secret = 'linear-secret';
|
|
143
|
+
const validSignature = createHmac('sha256', secret).update(rawPayload).digest('hex');
|
|
144
|
+
const missing = validateLinearWebhookSignature(rawPayload, {}, secret);
|
|
145
|
+
assert.deepEqual(missing, { ok: false, reason: 'missing-signature' });
|
|
146
|
+
assert.throws(() => assertValidLinearWebhookSignature(rawPayload, {}, secret), /missing-signature/);
|
|
147
|
+
const malformed = validateLinearWebhookSignature(rawPayload, {
|
|
148
|
+
[LINEAR_SIGNATURE_HEADER]: 'not-hex',
|
|
149
|
+
}, secret);
|
|
150
|
+
assert.deepEqual(malformed, {
|
|
151
|
+
ok: false,
|
|
152
|
+
reason: 'malformed-signature',
|
|
153
|
+
receivedSignature: 'not-hex',
|
|
154
|
+
});
|
|
155
|
+
const invalid = validateLinearWebhookSignature(rawPayload, {
|
|
156
|
+
[LINEAR_SIGNATURE_HEADER]: `${validSignature.slice(0, -2)}00`,
|
|
157
|
+
}, secret);
|
|
158
|
+
assert.equal(invalid.ok, false);
|
|
159
|
+
assert.equal(invalid.reason, 'invalid-signature');
|
|
160
|
+
assert.equal(invalid.expectedSignature, validSignature);
|
|
161
|
+
assert.throws(() => assertValidLinearWebhookSignature(rawPayload, {
|
|
162
|
+
[LINEAR_SIGNATURE_HEADER]: `${validSignature.slice(0, -2)}00`,
|
|
163
|
+
}, secret), /invalid-signature/);
|
|
164
|
+
const missingSecret = validateLinearWebhookSignature(rawPayload, {
|
|
165
|
+
[LINEAR_SIGNATURE_HEADER]: validSignature,
|
|
166
|
+
}, ' ');
|
|
167
|
+
assert.deepEqual(missingSecret, { ok: false, reason: 'missing-secret' });
|
|
168
|
+
});
|
|
169
|
+
test('path mapping stays deterministic for issue, comment, project, and cycle objects', () => {
|
|
170
|
+
const adapter = createAdapter();
|
|
171
|
+
assert.equal(linearIssuePath('issue 1/2'), '/linear/issues/issue%201%2F2.json');
|
|
172
|
+
assert.equal(linearCommentPath('comment:42'), '/linear/comments/comment%3A42.json');
|
|
173
|
+
assert.equal(linearProjectPath('project#7'), '/linear/projects/project%237.json');
|
|
174
|
+
assert.equal(linearCyclePath('cycle Q2'), '/linear/cycles/cycle%20Q2.json');
|
|
175
|
+
assert.equal(computeLinearPath('Issue', 'issue 1/2'), '/linear/issues/issue%201%2F2.json');
|
|
176
|
+
assert.equal(computeLinearPath('comments', 'comment:42'), '/linear/comments/comment%3A42.json');
|
|
177
|
+
assert.equal(computeLinearPath('project', 'project#7'), '/linear/projects/project%237.json');
|
|
178
|
+
assert.equal(computeLinearPath('Cycles', 'cycle Q2'), '/linear/cycles/cycle%20Q2.json');
|
|
179
|
+
assert.equal(adapter.computePath('issues', 'issue 1/2'), '/linear/issues/issue%201%2F2.json');
|
|
180
|
+
assert.equal(adapter.computePath('comment', 'comment:42'), '/linear/comments/comment%3A42.json');
|
|
181
|
+
assert.equal(adapter.computePath('projects', 'project#7'), '/linear/projects/project%237.json');
|
|
182
|
+
assert.equal(adapter.computePath('cycle', 'cycle Q2'), '/linear/cycles/cycle%20Q2.json');
|
|
183
|
+
});
|
|
184
|
+
test('computeSemantics extracts issue priority, state, labels, and relations deterministically', () => {
|
|
185
|
+
const adapter = createAdapter();
|
|
186
|
+
const semantics = adapter.computeSemantics('Issue', 'issue_123', {
|
|
187
|
+
id: 'issue_123',
|
|
188
|
+
identifier: 'ENG-123',
|
|
189
|
+
title: 'Stabilize Linear adapter coverage',
|
|
190
|
+
priority: 2,
|
|
191
|
+
state: {
|
|
192
|
+
id: 'state_in_progress',
|
|
193
|
+
name: 'In Progress',
|
|
194
|
+
type: 'started',
|
|
195
|
+
color: '#f97316',
|
|
196
|
+
},
|
|
197
|
+
labels: [
|
|
198
|
+
{ id: 'label_bug', name: 'bug' },
|
|
199
|
+
{ id: 'label_ui', name: 'ui' },
|
|
200
|
+
{ id: 'label_backend', name: 'backend' },
|
|
201
|
+
{ id: 'label_blank', name: ' ' },
|
|
202
|
+
],
|
|
203
|
+
project: {
|
|
204
|
+
id: 'project_alpha',
|
|
205
|
+
name: 'Alpha',
|
|
206
|
+
state: 'started',
|
|
207
|
+
url: 'https://linear.app/acme/project/alpha',
|
|
208
|
+
},
|
|
209
|
+
cycle: {
|
|
210
|
+
id: 'cycle_2026_06',
|
|
211
|
+
number: 6,
|
|
212
|
+
name: 'Cycle 6',
|
|
213
|
+
},
|
|
214
|
+
parent: {
|
|
215
|
+
id: 'issue_parent',
|
|
216
|
+
},
|
|
217
|
+
children: [
|
|
218
|
+
{ id: 'issue_child_b' },
|
|
219
|
+
{ id: 'issue_child_a' },
|
|
220
|
+
],
|
|
221
|
+
relations: [
|
|
222
|
+
{ relatedIssueId: 'issue_related_z' },
|
|
223
|
+
{ relatedIssueId: 'issue_child_a' },
|
|
224
|
+
],
|
|
225
|
+
team: {
|
|
226
|
+
id: 'team_eng',
|
|
227
|
+
key: 'ENG',
|
|
228
|
+
name: 'Engineering',
|
|
229
|
+
},
|
|
230
|
+
url: 'https://linear.app/acme/issue/ENG-123',
|
|
231
|
+
_webhook: {
|
|
232
|
+
action: 'update',
|
|
233
|
+
createdAt: '2026-03-28T12:00:00.000Z',
|
|
234
|
+
organizationId: 'org_123',
|
|
235
|
+
url: 'https://linear.app/webhooks/issue_123',
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
assert.deepEqual(semantics.properties, {
|
|
239
|
+
provider: 'linear',
|
|
240
|
+
'provider.object_id': 'issue_123',
|
|
241
|
+
'provider.object_type': 'issue',
|
|
242
|
+
'linear.id': 'issue_123',
|
|
243
|
+
'linear.object_type': 'issue',
|
|
244
|
+
'linear.url': 'https://linear.app/acme/issue/ENG-123',
|
|
245
|
+
'linear.webhook.action': 'update',
|
|
246
|
+
'linear.webhook.created_at': '2026-03-28T12:00:00.000Z',
|
|
247
|
+
'linear.webhook.organization_id': 'org_123',
|
|
248
|
+
'linear.webhook.url': 'https://linear.app/webhooks/issue_123',
|
|
249
|
+
'linear.identifier': 'ENG-123',
|
|
250
|
+
'linear.title': 'Stabilize Linear adapter coverage',
|
|
251
|
+
'linear.priority': '2',
|
|
252
|
+
'linear.priority_label': 'high',
|
|
253
|
+
'linear.state_id': 'state_in_progress',
|
|
254
|
+
'linear.state_name': 'In Progress',
|
|
255
|
+
'linear.state_type': 'started',
|
|
256
|
+
'linear.state_color': '#f97316',
|
|
257
|
+
'linear.labels': 'backend, bug, ui',
|
|
258
|
+
'linear.label_count': '3',
|
|
259
|
+
'linear.project_id': 'project_alpha',
|
|
260
|
+
'linear.project_name': 'Alpha',
|
|
261
|
+
'linear.project_state': 'started',
|
|
262
|
+
'linear.project_url': 'https://linear.app/acme/project/alpha',
|
|
263
|
+
'linear.cycle_id': 'cycle_2026_06',
|
|
264
|
+
'linear.cycle_number': '6',
|
|
265
|
+
'linear.cycle_name': 'Cycle 6',
|
|
266
|
+
'linear.parent_id': 'issue_parent',
|
|
267
|
+
'linear.team_id': 'team_eng',
|
|
268
|
+
'linear.team_key': 'ENG',
|
|
269
|
+
'linear.team_name': 'Engineering',
|
|
270
|
+
});
|
|
271
|
+
assert.deepEqual(semantics.relations, [
|
|
272
|
+
'/linear/cycles/cycle_2026_06.json',
|
|
273
|
+
'/linear/issues/issue_child_a.json',
|
|
274
|
+
'/linear/issues/issue_child_b.json',
|
|
275
|
+
'/linear/issues/issue_parent.json',
|
|
276
|
+
'/linear/issues/issue_related_z.json',
|
|
277
|
+
'/linear/projects/project_alpha.json',
|
|
278
|
+
]);
|
|
279
|
+
assert.equal(semantics.comments, undefined);
|
|
280
|
+
});
|
|
281
|
+
test('barrel exports import cleanly for runtime and type-checked usage', async () => {
|
|
282
|
+
const barrel = await import('../index.js');
|
|
283
|
+
assert.equal(barrel.LinearAdapter, LinearAdapter);
|
|
284
|
+
assert.equal(barrel.computeLinearPath, computeLinearPath);
|
|
285
|
+
assert.equal(typeof barrel.normalizeLinearWebhook, 'function');
|
|
286
|
+
assert.equal(typeof barrel.validateLinearWebhookSignature, 'function');
|
|
287
|
+
const config = {
|
|
288
|
+
connectionId: 'conn_linear_barrel',
|
|
289
|
+
provider: 'linear',
|
|
290
|
+
};
|
|
291
|
+
const adapter = createAdapter(config);
|
|
292
|
+
assert.equal(adapter.name, 'linear');
|
|
293
|
+
assert.equal(adapter.config.connectionId, 'conn_linear_barrel');
|
|
294
|
+
});
|
|
295
|
+
//# sourceMappingURL=linear-adapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-adapter.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-adapter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/types.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { LINEAR_WEBHOOK_ACTIONS, LINEAR_WEBHOOK_OBJECT_TYPES, } from '../index.js';
|
|
4
|
+
test('exports supported Linear webhook object types', () => {
|
|
5
|
+
assert.deepEqual(LINEAR_WEBHOOK_OBJECT_TYPES, ['comment', 'cycle', 'issue', 'project']);
|
|
6
|
+
});
|
|
7
|
+
test('exports supported Linear webhook actions', () => {
|
|
8
|
+
assert.deepEqual(LINEAR_WEBHOOK_ACTIONS, ['create', 'remove', 'update']);
|
|
9
|
+
});
|
|
10
|
+
test('LinearAdapterConfig remains usable as a typed contract', () => {
|
|
11
|
+
const config = {
|
|
12
|
+
apiUrl: 'https://api.linear.app/graphql',
|
|
13
|
+
provider: 'nango',
|
|
14
|
+
webhookSecret: 'linear-secret',
|
|
15
|
+
};
|
|
16
|
+
assert.equal(config.provider, 'nango');
|
|
17
|
+
assert.equal(config.webhookSecret, 'linear-secret');
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=types.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.test.js","sourceRoot":"","sources":["../../src/__tests__/types.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,sBAAsB,EACtB,2BAA2B,GAE5B,MAAM,aAAa,CAAC;AAErB,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,CAAC,SAAS,CAAC,2BAA2B,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AAC1F,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACpD,MAAM,CAAC,SAAS,CAAC,sBAAsB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;IAClE,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,gCAAgC;QACxC,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,eAAe;KACD,CAAC;IAEhC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-normalizer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/webhook-normalizer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { createHmac } from 'node:crypto';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import { LINEAR_DELIVERY_HEADER, LINEAR_SIGNATURE_HEADER, assertValidLinearWebhookSignature, normalizeLinearWebhook, validateLinearWebhookSignature, validateLinearWebhookTimestamp, } from '../index.js';
|
|
5
|
+
const issuePayload = {
|
|
6
|
+
action: 'create',
|
|
7
|
+
type: 'Issue',
|
|
8
|
+
createdAt: '2026-03-28T10:00:00.000Z',
|
|
9
|
+
organizationId: 'org_123',
|
|
10
|
+
webhookTimestamp: 1_743_155_200_000,
|
|
11
|
+
webhookId: 'webhook_123',
|
|
12
|
+
data: {
|
|
13
|
+
id: 'issue_123',
|
|
14
|
+
identifier: 'ENG-123',
|
|
15
|
+
title: 'Ship webhook normalizer',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
test('normalizeLinearWebhook extracts normalized event metadata and connection metadata', () => {
|
|
19
|
+
const normalized = normalizeLinearWebhook(issuePayload, {
|
|
20
|
+
[LINEAR_DELIVERY_HEADER]: 'delivery_123',
|
|
21
|
+
'Linear-Event': 'Issue',
|
|
22
|
+
'X-Relay-Connection-Id': 'conn_linear_123',
|
|
23
|
+
'X-Relay-Provider-Config-Key': 'linear',
|
|
24
|
+
});
|
|
25
|
+
assert.equal(normalized.provider, 'linear');
|
|
26
|
+
assert.equal(normalized.connectionId, 'conn_linear_123');
|
|
27
|
+
assert.equal(normalized.eventType, 'issue.create');
|
|
28
|
+
assert.equal(normalized.objectType, 'issue');
|
|
29
|
+
assert.equal(normalized.objectId, 'issue_123');
|
|
30
|
+
assert.deepEqual(normalized.payload._connection, {
|
|
31
|
+
connectionId: 'conn_linear_123',
|
|
32
|
+
deliveryId: 'delivery_123',
|
|
33
|
+
provider: 'linear',
|
|
34
|
+
providerConfigKey: 'linear',
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
test('validateLinearWebhookSignature accepts the expected HMAC and rejects invalid signatures', () => {
|
|
38
|
+
const rawPayload = JSON.stringify(issuePayload);
|
|
39
|
+
const secret = 'linear-secret';
|
|
40
|
+
const signature = createHmac('sha256', secret).update(rawPayload).digest('hex');
|
|
41
|
+
const valid = validateLinearWebhookSignature(rawPayload, {
|
|
42
|
+
[LINEAR_SIGNATURE_HEADER]: signature,
|
|
43
|
+
}, secret);
|
|
44
|
+
assert.equal(valid.ok, true);
|
|
45
|
+
const invalid = validateLinearWebhookSignature(rawPayload, {
|
|
46
|
+
[LINEAR_SIGNATURE_HEADER]: 'deadbeef',
|
|
47
|
+
}, secret);
|
|
48
|
+
assert.equal(invalid.ok, false);
|
|
49
|
+
assert.equal(invalid.reason, 'invalid-signature');
|
|
50
|
+
assert.doesNotThrow(() => assertValidLinearWebhookSignature(rawPayload, { [LINEAR_SIGNATURE_HEADER]: signature }, secret));
|
|
51
|
+
});
|
|
52
|
+
test('validateLinearWebhookTimestamp enforces freshness', () => {
|
|
53
|
+
const fresh = validateLinearWebhookTimestamp(issuePayload, 60_000, 1_743_155_230_000);
|
|
54
|
+
assert.equal(fresh.ok, true);
|
|
55
|
+
const stale = validateLinearWebhookTimestamp(issuePayload, 60_000, 1_743_155_400_001);
|
|
56
|
+
assert.equal(stale.ok, false);
|
|
57
|
+
assert.equal(stale.reason, 'stale-timestamp');
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=webhook-normalizer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-normalizer.test.js","sourceRoot":"","sources":["../../src/__tests__/webhook-normalizer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ConnectionProvider } from '@relayfile/sdk';
|
|
2
|
+
export type { ConnectionProvider, ProxyRequest, ProxyResponse } from '@relayfile/sdk';
|
|
3
|
+
import type { LinearAdapterConfig, LinearWebhookPayload } from './types.js';
|
|
4
|
+
export interface FileSemantics {
|
|
5
|
+
properties?: Record<string, string>;
|
|
6
|
+
relations?: string[];
|
|
7
|
+
permissions?: string[];
|
|
8
|
+
comments?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface IngestError {
|
|
11
|
+
path: string;
|
|
12
|
+
error: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IngestResult {
|
|
15
|
+
filesWritten: number;
|
|
16
|
+
filesUpdated: number;
|
|
17
|
+
filesDeleted: number;
|
|
18
|
+
paths: string[];
|
|
19
|
+
errors: IngestError[];
|
|
20
|
+
}
|
|
21
|
+
export interface NormalizedWebhook {
|
|
22
|
+
provider: string;
|
|
23
|
+
connectionId?: string;
|
|
24
|
+
eventType: string;
|
|
25
|
+
objectType: string;
|
|
26
|
+
objectId: string;
|
|
27
|
+
payload: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
export interface WriteFileInput {
|
|
30
|
+
workspaceId: string;
|
|
31
|
+
path: string;
|
|
32
|
+
content: string;
|
|
33
|
+
contentType?: string;
|
|
34
|
+
semantics?: FileSemantics;
|
|
35
|
+
}
|
|
36
|
+
export interface WriteFileResult {
|
|
37
|
+
created?: boolean;
|
|
38
|
+
updated?: boolean;
|
|
39
|
+
status?: 'created' | 'updated' | 'queued' | 'pending';
|
|
40
|
+
}
|
|
41
|
+
export interface DeleteFileInput {
|
|
42
|
+
workspaceId: string;
|
|
43
|
+
path: string;
|
|
44
|
+
}
|
|
45
|
+
export interface RelayFileClientLike {
|
|
46
|
+
writeFile(input: WriteFileInput): Promise<WriteFileResult | void>;
|
|
47
|
+
deleteFile?(input: DeleteFileInput): Promise<void> | void;
|
|
48
|
+
}
|
|
49
|
+
export declare abstract class IntegrationAdapter {
|
|
50
|
+
protected readonly client: RelayFileClientLike;
|
|
51
|
+
protected readonly provider: ConnectionProvider;
|
|
52
|
+
abstract readonly name: string;
|
|
53
|
+
abstract readonly version: string;
|
|
54
|
+
constructor(client: RelayFileClientLike, provider: ConnectionProvider);
|
|
55
|
+
abstract ingestWebhook(workspaceId: string, event: NormalizedWebhook | LinearWebhookPayload): Promise<IngestResult>;
|
|
56
|
+
abstract computePath(objectType: string, objectId: string): string;
|
|
57
|
+
abstract computeSemantics(objectType: string, objectId: string, payload: Record<string, unknown>): FileSemantics;
|
|
58
|
+
supportedEvents?(): string[];
|
|
59
|
+
}
|
|
60
|
+
export declare class LinearAdapter extends IntegrationAdapter {
|
|
61
|
+
readonly name = "linear";
|
|
62
|
+
readonly version = "0.1.0";
|
|
63
|
+
readonly config: LinearAdapterConfig;
|
|
64
|
+
constructor(client: RelayFileClientLike, provider: ConnectionProvider, config?: LinearAdapterConfig);
|
|
65
|
+
supportedEvents(): string[];
|
|
66
|
+
ingestWebhook(workspaceId: string, event: NormalizedWebhook | LinearWebhookPayload): Promise<IngestResult>;
|
|
67
|
+
computePath(objectType: string, objectId: string): string;
|
|
68
|
+
computeSemantics(objectType: string, objectId: string, payload: Record<string, unknown>): FileSemantics;
|
|
69
|
+
private normalizeEvent;
|
|
70
|
+
private isRemoveEvent;
|
|
71
|
+
private renderContent;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=linear-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-adapter.d.ts","sourceRoot":"","sources":["../src/linear-adapter.ts"],"names":[],"mappings":"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"}
|