@relayfile/core 0.8.16 → 0.8.18
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/webhooks.d.ts +2 -2
- package/dist/webhooks.js +90 -14
- package/dist/webhooks.test.d.ts +1 -0
- package/dist/webhooks.test.js +265 -0
- package/package.json +1 -1
package/dist/webhooks.d.ts
CHANGED
|
@@ -87,6 +87,6 @@ export interface WebhookStorageAdapter extends StorageAdapter {
|
|
|
87
87
|
}
|
|
88
88
|
export declare function ingestWebhook(storage: StorageAdapter, input: IngestWebhookInput, options?: IngestWebhookOptions): IngestResult;
|
|
89
89
|
export declare function normalizeEnvelope(input: IngestWebhookInput, now?: () => string): Partial<EnvelopeRow>;
|
|
90
|
-
export declare function normalizeEnvelopeEvent(envelope: Pick<EnvelopeRow, "payload" | "receivedAt">): EnvelopeEvent | null;
|
|
91
|
-
export declare function normalizeEnvelopePath(envelope: Pick<EnvelopeRow, "payload">): string | null;
|
|
90
|
+
export declare function normalizeEnvelopeEvent(envelope: Pick<EnvelopeRow, "payload" | "receivedAt"> & Partial<Pick<EnvelopeRow, "provider">>): EnvelopeEvent | null;
|
|
91
|
+
export declare function normalizeEnvelopePath(envelope: Pick<EnvelopeRow, "payload"> & Partial<Pick<EnvelopeRow, "provider">>): string | null;
|
|
92
92
|
export declare function applyWebhookEnvelope(storage: StorageAdapter, envelope: EnvelopeRow, options?: ApplyEnvelopeOptions): ApplyEnvelopeResult;
|
package/dist/webhooks.js
CHANGED
|
@@ -43,7 +43,7 @@ export function ingestWebhook(storage, input, options = {}) {
|
|
|
43
43
|
const normalized = normalizeEnvelope(input, now);
|
|
44
44
|
const provider = asOptionalString(normalized.provider) ?? "";
|
|
45
45
|
const payload = asRecord(normalized.payload);
|
|
46
|
-
const path = normalizeEnvelopePath({ payload });
|
|
46
|
+
const path = normalizeEnvelopePath({ payload, provider });
|
|
47
47
|
const receivedAt = asOptionalString(normalized.receivedAt) ?? now();
|
|
48
48
|
const workspaceId = storage.getWorkspaceId();
|
|
49
49
|
const deliveryId = asOptionalString(normalized.deliveryId) ??
|
|
@@ -69,7 +69,7 @@ export function ingestWebhook(storage, input, options = {}) {
|
|
|
69
69
|
status: "queued",
|
|
70
70
|
limit: 100,
|
|
71
71
|
}).items;
|
|
72
|
-
const coalesced = findCoalescedEnvelope(queued, payload, receivedAt, options.coalesceWindowMs ?? DEFAULT_COALESCE_WINDOW_MS);
|
|
72
|
+
const coalesced = findCoalescedEnvelope(queued, payload, provider, receivedAt, options.coalesceWindowMs ?? DEFAULT_COALESCE_WINDOW_MS);
|
|
73
73
|
if (coalesced) {
|
|
74
74
|
const updated = {
|
|
75
75
|
...coalesced,
|
|
@@ -115,11 +115,13 @@ export function ingestWebhook(storage, input, options = {}) {
|
|
|
115
115
|
export function normalizeEnvelope(input, now = nowIso) {
|
|
116
116
|
const provider = normalizeProvider(input.provider);
|
|
117
117
|
const eventType = normalizeEventType(input.eventType);
|
|
118
|
-
const
|
|
118
|
+
const canonicalPath = input.path?.trim()
|
|
119
|
+
? canonicalProviderEnvelopePath(provider, input.path)
|
|
120
|
+
: null;
|
|
119
121
|
const correlationId = input.correlationId?.trim() ?? "";
|
|
120
122
|
const receivedAt = normalizeIsoDate(input.timestamp) ?? now();
|
|
121
123
|
const deliveryId = input.deliveryId?.trim();
|
|
122
|
-
if (!provider || !eventType || !input.path?.trim()) {
|
|
124
|
+
if (!provider || !eventType || !input.path?.trim() || !canonicalPath) {
|
|
123
125
|
return {
|
|
124
126
|
provider,
|
|
125
127
|
deliveryId,
|
|
@@ -137,7 +139,7 @@ export function normalizeEnvelope(input, now = nowIso) {
|
|
|
137
139
|
payload: {
|
|
138
140
|
provider,
|
|
139
141
|
event_type: eventType,
|
|
140
|
-
path,
|
|
142
|
+
path: canonicalPath,
|
|
141
143
|
timestamp: receivedAt,
|
|
142
144
|
data: asRecord(input.data),
|
|
143
145
|
delivery_id: deliveryId,
|
|
@@ -151,7 +153,10 @@ export function normalizeEnvelopeEvent(envelope) {
|
|
|
151
153
|
if (!eventType) {
|
|
152
154
|
return null;
|
|
153
155
|
}
|
|
154
|
-
const path =
|
|
156
|
+
const path = normalizeEnvelopePath(envelope);
|
|
157
|
+
if (!path) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
155
160
|
const data = asRecord(payload.data);
|
|
156
161
|
const body = Object.keys(data).length > 0 ? data : payload;
|
|
157
162
|
const timestamp = normalizeIsoDate(asOptionalString(payload.timestamp) ?? envelope.receivedAt) ??
|
|
@@ -179,11 +184,13 @@ export function normalizeEnvelopeEvent(envelope) {
|
|
|
179
184
|
}
|
|
180
185
|
export function normalizeEnvelopePath(envelope) {
|
|
181
186
|
const path = asOptionalString(envelope.payload.path);
|
|
182
|
-
|
|
187
|
+
const provider = asOptionalString(envelope.provider) ??
|
|
188
|
+
asOptionalString(envelope.payload.provider);
|
|
189
|
+
return path ? canonicalProviderEnvelopePath(provider, path) : null;
|
|
183
190
|
}
|
|
184
191
|
export function applyWebhookEnvelope(storage, envelope, options = {}) {
|
|
185
|
-
const
|
|
186
|
-
if (!
|
|
192
|
+
const normalizedEvent = normalizeEnvelopeEvent(envelope);
|
|
193
|
+
if (!normalizedEvent) {
|
|
187
194
|
return {
|
|
188
195
|
status: "ignored",
|
|
189
196
|
eventType: null,
|
|
@@ -191,6 +198,7 @@ export function applyWebhookEnvelope(storage, envelope, options = {}) {
|
|
|
191
198
|
revision: null,
|
|
192
199
|
};
|
|
193
200
|
}
|
|
201
|
+
const event = canonicalizeEnvelopeEventPath(storage, envelope.provider, normalizedEvent);
|
|
194
202
|
if (options.shouldSuppress?.(envelope, event)) {
|
|
195
203
|
const revision = appendSyncEvent(storage, "sync.suppressed", event.path, envelope.provider, envelope.correlationId, options.now?.() ?? event.timestamp);
|
|
196
204
|
return {
|
|
@@ -356,14 +364,14 @@ function appendSyncEvent(storage, type, path, provider, correlationId, timestamp
|
|
|
356
364
|
});
|
|
357
365
|
return revision;
|
|
358
366
|
}
|
|
359
|
-
function findCoalescedEnvelope(envelopes, payload, receivedAt, windowMs) {
|
|
360
|
-
const key = coalesceObjectKey(payload);
|
|
367
|
+
function findCoalescedEnvelope(envelopes, payload, provider, receivedAt, windowMs) {
|
|
368
|
+
const key = coalesceObjectKey(payload, provider);
|
|
361
369
|
if (!key) {
|
|
362
370
|
return null;
|
|
363
371
|
}
|
|
364
372
|
let match = null;
|
|
365
373
|
for (const envelope of envelopes) {
|
|
366
|
-
if (coalesceObjectKey(envelope.payload) !== key) {
|
|
374
|
+
if (coalesceObjectKey(envelope.payload, envelope.provider) !== key) {
|
|
367
375
|
continue;
|
|
368
376
|
}
|
|
369
377
|
if (!withinCoalesceWindow(envelope.receivedAt, receivedAt, windowMs)) {
|
|
@@ -381,7 +389,7 @@ function mergeDeliveryIds(envelope, deliveryId) {
|
|
|
381
389
|
function deliveryMatches(envelope, deliveryId) {
|
|
382
390
|
return envelope.deliveryId === deliveryId || envelope.deliveryIds?.includes(deliveryId) === true;
|
|
383
391
|
}
|
|
384
|
-
function coalesceObjectKey(payload) {
|
|
392
|
+
function coalesceObjectKey(payload, provider) {
|
|
385
393
|
const data = asRecord(payload.data);
|
|
386
394
|
const providerObjectId = asOptionalString(data.providerObjectId) ??
|
|
387
395
|
asOptionalString(data.provider_object_id) ??
|
|
@@ -392,9 +400,77 @@ function coalesceObjectKey(payload) {
|
|
|
392
400
|
if (providerObjectId) {
|
|
393
401
|
return `object:${providerObjectId}`;
|
|
394
402
|
}
|
|
395
|
-
const path = normalizeEnvelopePath({ payload });
|
|
403
|
+
const path = normalizeEnvelopePath({ payload, provider });
|
|
396
404
|
return path && path !== "/" ? `path:${path}` : "";
|
|
397
405
|
}
|
|
406
|
+
const PROVIDER_RELATIVE_PATH_ROOTS = {
|
|
407
|
+
slack: new Set(["channels", "dms", "teams", "users"]),
|
|
408
|
+
};
|
|
409
|
+
function canonicalProviderEnvelopePath(provider, rawPath) {
|
|
410
|
+
const path = normalizePath(rawPath);
|
|
411
|
+
const normalizedProvider = normalizeProvider(provider);
|
|
412
|
+
if (!normalizedProvider || path === "/") {
|
|
413
|
+
return path;
|
|
414
|
+
}
|
|
415
|
+
const relativeRoots = PROVIDER_RELATIVE_PATH_ROOTS[normalizedProvider];
|
|
416
|
+
if (!relativeRoots) {
|
|
417
|
+
return path;
|
|
418
|
+
}
|
|
419
|
+
const trimmed = path.slice(1);
|
|
420
|
+
if (trimmed === normalizedProvider ||
|
|
421
|
+
trimmed.startsWith(`${normalizedProvider}/`)) {
|
|
422
|
+
return path;
|
|
423
|
+
}
|
|
424
|
+
const [firstSegment] = trimmed.split("/", 1);
|
|
425
|
+
if (firstSegment && relativeRoots.has(firstSegment)) {
|
|
426
|
+
return normalizePath(`/${normalizedProvider}/${trimmed}`);
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
function canonicalizeEnvelopeEventPath(storage, provider, event) {
|
|
431
|
+
const path = canonicalizeExistingProviderAliasPath(storage, provider, event.path);
|
|
432
|
+
return path === event.path ? event : { ...event, path };
|
|
433
|
+
}
|
|
434
|
+
function canonicalizeExistingProviderAliasPath(storage, provider, rawPath) {
|
|
435
|
+
const path = normalizePath(rawPath);
|
|
436
|
+
if (normalizeProvider(provider) !== "slack") {
|
|
437
|
+
return path;
|
|
438
|
+
}
|
|
439
|
+
if (!parseRawSlackChannelAliasPath(path)) {
|
|
440
|
+
return path;
|
|
441
|
+
}
|
|
442
|
+
return canonicalizeSlackChannelAliasPath(storage.listFiles(), path);
|
|
443
|
+
}
|
|
444
|
+
function canonicalizeSlackChannelAliasPath(files, rawPath) {
|
|
445
|
+
const parsed = parseRawSlackChannelAliasPath(rawPath);
|
|
446
|
+
if (!parsed) {
|
|
447
|
+
return normalizePath(rawPath);
|
|
448
|
+
}
|
|
449
|
+
const { path, parts, channelSegment } = parsed;
|
|
450
|
+
const prefix = `/slack/channels/${channelSegment}__`;
|
|
451
|
+
const candidates = files
|
|
452
|
+
.map((file) => normalizePath(file.path))
|
|
453
|
+
.filter((filePath) => filePath.startsWith(prefix))
|
|
454
|
+
.map((filePath) => filePath.slice(1).split("/")[2])
|
|
455
|
+
.sort();
|
|
456
|
+
if (candidates.length === 0) {
|
|
457
|
+
return path;
|
|
458
|
+
}
|
|
459
|
+
parts[2] = candidates[0];
|
|
460
|
+
return normalizePath(`/${parts.join("/")}`);
|
|
461
|
+
}
|
|
462
|
+
function parseRawSlackChannelAliasPath(rawPath) {
|
|
463
|
+
const path = normalizePath(rawPath);
|
|
464
|
+
const parts = path.slice(1).split("/");
|
|
465
|
+
if (parts.length < 3 || parts[0] !== "slack" || parts[1] !== "channels") {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
const channelSegment = parts[2];
|
|
469
|
+
if (!channelSegment || channelSegment.includes("__")) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
return { path, parts, channelSegment };
|
|
473
|
+
}
|
|
398
474
|
function withinCoalesceWindow(existingReceivedAt, incomingReceivedAt, windowMs) {
|
|
399
475
|
if (windowMs <= 0) {
|
|
400
476
|
return false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { applyWebhookEnvelope, ingestWebhook, normalizeEnvelopeEvent, normalizeEnvelopePath, } from "./webhooks.js";
|
|
3
|
+
describe("webhook Slack path canonicalization", () => {
|
|
4
|
+
it("canonicalizes Slack provider-relative envelope paths", () => {
|
|
5
|
+
const event = normalizeEnvelopeEvent({
|
|
6
|
+
provider: "slack",
|
|
7
|
+
receivedAt: "2026-06-07T20:00:00.000Z",
|
|
8
|
+
payload: {
|
|
9
|
+
provider: "slack",
|
|
10
|
+
event_type: "file.updated",
|
|
11
|
+
path: "/channels/C123/messages/1711111111_000100/meta.json",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
expect(event?.path).toBe("/slack/channels/C123/messages/1711111111_000100/meta.json");
|
|
15
|
+
expect(normalizeEnvelopePath({
|
|
16
|
+
provider: "slack",
|
|
17
|
+
payload: {
|
|
18
|
+
provider: "slack",
|
|
19
|
+
path: "/channels/C123/messages/1711111111_000100/meta.json",
|
|
20
|
+
},
|
|
21
|
+
})).toBe("/slack/channels/C123/messages/1711111111_000100/meta.json");
|
|
22
|
+
});
|
|
23
|
+
it("resolves raw Slack channel IDs to existing channelId__name aliases before writes and events", () => {
|
|
24
|
+
const storage = new MemoryStorage([
|
|
25
|
+
fileRow("/slack/channels/C123__engineering/meta.json", {
|
|
26
|
+
content: '{"id":"C123","name":"engineering"}',
|
|
27
|
+
provider: "slack",
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
30
|
+
const result = applyWebhookEnvelope(storage, {
|
|
31
|
+
envelopeId: "env_1",
|
|
32
|
+
workspaceId: "ws_core",
|
|
33
|
+
provider: "slack",
|
|
34
|
+
deliveryId: "delivery_1",
|
|
35
|
+
receivedAt: "2026-06-07T20:00:00.000Z",
|
|
36
|
+
payload: {
|
|
37
|
+
provider: "slack",
|
|
38
|
+
event_type: "file.updated",
|
|
39
|
+
path: "/slack/channels/C123/messages/1711111111_000100/meta.json",
|
|
40
|
+
content: '{"text":"hello"}',
|
|
41
|
+
contentType: "application/json",
|
|
42
|
+
},
|
|
43
|
+
correlationId: "corr_1",
|
|
44
|
+
status: "queued",
|
|
45
|
+
attemptCount: 0,
|
|
46
|
+
lastError: null,
|
|
47
|
+
});
|
|
48
|
+
const canonicalPath = "/slack/channels/C123__engineering/messages/1711111111_000100/meta.json";
|
|
49
|
+
expect(result).toMatchObject({
|
|
50
|
+
status: "processed",
|
|
51
|
+
path: canonicalPath,
|
|
52
|
+
});
|
|
53
|
+
expect(storage.getFile(canonicalPath)?.content).toBe('{"text":"hello"}');
|
|
54
|
+
expect(storage.getFile("/slack/channels/C123/messages/1711111111_000100/meta.json")).toBeNull();
|
|
55
|
+
expect(storage.events.at(-1)?.path).toBe(canonicalPath);
|
|
56
|
+
expect(storage.listFilesCalls).toBe(1);
|
|
57
|
+
});
|
|
58
|
+
it("does not scan storage when Slack channel alias resolution is unnecessary", () => {
|
|
59
|
+
const storage = new MemoryStorage([
|
|
60
|
+
fileRow("/slack/channels/C123__engineering/meta.json", {
|
|
61
|
+
content: '{"id":"C123","name":"engineering"}',
|
|
62
|
+
provider: "slack",
|
|
63
|
+
}),
|
|
64
|
+
]);
|
|
65
|
+
const aliasedResult = applyWebhookEnvelope(storage, {
|
|
66
|
+
envelopeId: "env_aliased",
|
|
67
|
+
workspaceId: "ws_core",
|
|
68
|
+
provider: "slack",
|
|
69
|
+
deliveryId: "delivery_aliased",
|
|
70
|
+
receivedAt: "2026-06-07T20:00:00.000Z",
|
|
71
|
+
payload: {
|
|
72
|
+
provider: "slack",
|
|
73
|
+
event_type: "file.updated",
|
|
74
|
+
path: "/slack/channels/C123__engineering/messages/1711111111_000100/meta.json",
|
|
75
|
+
content: '{"text":"hello"}',
|
|
76
|
+
contentType: "application/json",
|
|
77
|
+
},
|
|
78
|
+
correlationId: "corr_aliased",
|
|
79
|
+
status: "queued",
|
|
80
|
+
attemptCount: 0,
|
|
81
|
+
lastError: null,
|
|
82
|
+
});
|
|
83
|
+
const dmResult = applyWebhookEnvelope(storage, {
|
|
84
|
+
envelopeId: "env_dm",
|
|
85
|
+
workspaceId: "ws_core",
|
|
86
|
+
provider: "slack",
|
|
87
|
+
deliveryId: "delivery_dm",
|
|
88
|
+
receivedAt: "2026-06-07T20:00:01.000Z",
|
|
89
|
+
payload: {
|
|
90
|
+
provider: "slack",
|
|
91
|
+
event_type: "file.updated",
|
|
92
|
+
path: "/slack/dms/D123/messages/1711111111_000100/meta.json",
|
|
93
|
+
content: '{"text":"dm"}',
|
|
94
|
+
contentType: "application/json",
|
|
95
|
+
},
|
|
96
|
+
correlationId: "corr_dm",
|
|
97
|
+
status: "queued",
|
|
98
|
+
attemptCount: 0,
|
|
99
|
+
lastError: null,
|
|
100
|
+
});
|
|
101
|
+
expect(aliasedResult).toMatchObject({
|
|
102
|
+
status: "processed",
|
|
103
|
+
path: "/slack/channels/C123__engineering/messages/1711111111_000100/meta.json",
|
|
104
|
+
});
|
|
105
|
+
expect(dmResult).toMatchObject({
|
|
106
|
+
status: "processed",
|
|
107
|
+
path: "/slack/dms/D123/messages/1711111111_000100/meta.json",
|
|
108
|
+
});
|
|
109
|
+
expect(storage.listFilesCalls).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
it("ignores Slack envelopes targeting paths outside the Slack provider root", () => {
|
|
112
|
+
const storage = new MemoryStorage();
|
|
113
|
+
const result = applyWebhookEnvelope(storage, {
|
|
114
|
+
envelopeId: "env_1",
|
|
115
|
+
workspaceId: "ws_core",
|
|
116
|
+
provider: "slack",
|
|
117
|
+
deliveryId: "delivery_1",
|
|
118
|
+
receivedAt: "2026-06-07T20:00:00.000Z",
|
|
119
|
+
payload: {
|
|
120
|
+
provider: "slack",
|
|
121
|
+
event_type: "file.updated",
|
|
122
|
+
path: "/github/repos/acme/cloud/issues/1.json",
|
|
123
|
+
content: '{"title":"wrong provider"}',
|
|
124
|
+
},
|
|
125
|
+
correlationId: "corr_1",
|
|
126
|
+
status: "queued",
|
|
127
|
+
attemptCount: 0,
|
|
128
|
+
lastError: null,
|
|
129
|
+
});
|
|
130
|
+
expect(result).toEqual({
|
|
131
|
+
status: "ignored",
|
|
132
|
+
eventType: null,
|
|
133
|
+
path: null,
|
|
134
|
+
revision: null,
|
|
135
|
+
});
|
|
136
|
+
expect(storage.listFiles()).toEqual([]);
|
|
137
|
+
expect(storage.events).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
it("coalesces Slack provider-relative and canonical paths by canonical path", () => {
|
|
140
|
+
const storage = new MemoryStorage();
|
|
141
|
+
let nextEnvelopeId = 1;
|
|
142
|
+
const first = ingestWebhook(storage, {
|
|
143
|
+
provider: "slack",
|
|
144
|
+
eventType: "file.updated",
|
|
145
|
+
path: "/channels/C123/messages/1711111111_000100/meta.json",
|
|
146
|
+
deliveryId: "delivery_1",
|
|
147
|
+
timestamp: "2026-06-07T20:00:00.000Z",
|
|
148
|
+
correlationId: "corr_1",
|
|
149
|
+
}, {
|
|
150
|
+
generateEnvelopeId: () => `env_${nextEnvelopeId++}`,
|
|
151
|
+
coalesceWindowMs: 10_000,
|
|
152
|
+
});
|
|
153
|
+
const second = ingestWebhook(storage, {
|
|
154
|
+
provider: "slack",
|
|
155
|
+
eventType: "file.updated",
|
|
156
|
+
path: "/slack/channels/C123/messages/1711111111_000100/meta.json",
|
|
157
|
+
deliveryId: "delivery_2",
|
|
158
|
+
timestamp: "2026-06-07T20:00:01.000Z",
|
|
159
|
+
correlationId: "corr_2",
|
|
160
|
+
}, {
|
|
161
|
+
generateEnvelopeId: () => `env_${nextEnvelopeId++}`,
|
|
162
|
+
coalesceWindowMs: 10_000,
|
|
163
|
+
});
|
|
164
|
+
expect(first).toMatchObject({ status: "queued", envelopeId: "env_1" });
|
|
165
|
+
expect(second).toMatchObject({ status: "queued", envelopeId: "env_1" });
|
|
166
|
+
expect(Array.from(storage.envelopes)).toHaveLength(1);
|
|
167
|
+
const envelope = storage.envelopes.get("env_1");
|
|
168
|
+
expect(envelope?.deliveryIds).toEqual(["delivery_1", "delivery_2"]);
|
|
169
|
+
expect(envelope?.payload.path).toBe("/slack/channels/C123/messages/1711111111_000100/meta.json");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
function fileRow(path, overrides = {}) {
|
|
173
|
+
return {
|
|
174
|
+
path,
|
|
175
|
+
revision: "rev_1",
|
|
176
|
+
contentType: "application/json",
|
|
177
|
+
content: "{}",
|
|
178
|
+
encoding: "utf-8",
|
|
179
|
+
provider: "",
|
|
180
|
+
lastEditedAt: "2026-06-07T20:00:00.000Z",
|
|
181
|
+
semantics: {},
|
|
182
|
+
...overrides,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
class MemoryStorage {
|
|
186
|
+
files = new Map();
|
|
187
|
+
events = [];
|
|
188
|
+
envelopes = new Map();
|
|
189
|
+
deliveryAliases = new Map();
|
|
190
|
+
revisionCounter = 1;
|
|
191
|
+
eventCounter = 1;
|
|
192
|
+
listFilesCalls = 0;
|
|
193
|
+
constructor(files = []) {
|
|
194
|
+
for (const file of files) {
|
|
195
|
+
this.files.set(file.path, file);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
getFile(path) {
|
|
199
|
+
return this.files.get(path) ?? null;
|
|
200
|
+
}
|
|
201
|
+
listFiles() {
|
|
202
|
+
this.listFilesCalls += 1;
|
|
203
|
+
return Array.from(this.files.values());
|
|
204
|
+
}
|
|
205
|
+
putFile(file) {
|
|
206
|
+
this.files.set(file.path, file);
|
|
207
|
+
}
|
|
208
|
+
deleteFile(path) {
|
|
209
|
+
this.files.delete(path);
|
|
210
|
+
}
|
|
211
|
+
appendEvent(event) {
|
|
212
|
+
this.events.push(event);
|
|
213
|
+
}
|
|
214
|
+
listEvents(_options) {
|
|
215
|
+
return { items: this.events, nextCursor: null };
|
|
216
|
+
}
|
|
217
|
+
getRecentEvents(limit) {
|
|
218
|
+
return this.events.slice(-limit);
|
|
219
|
+
}
|
|
220
|
+
getOperation(_opId) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
putOperation(_op) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
listOperations(_options) {
|
|
227
|
+
return { items: [], nextCursor: null };
|
|
228
|
+
}
|
|
229
|
+
nextRevision() {
|
|
230
|
+
return `rev_${this.revisionCounter++}`;
|
|
231
|
+
}
|
|
232
|
+
nextOperationId() {
|
|
233
|
+
return "op_1";
|
|
234
|
+
}
|
|
235
|
+
nextEventId() {
|
|
236
|
+
return `evt_${this.eventCounter++}`;
|
|
237
|
+
}
|
|
238
|
+
enqueueWriteback(_item) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
getPendingWritebacks() {
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
getEnvelopeByDelivery(workspaceId, provider, deliveryId) {
|
|
245
|
+
const envelopeId = this.deliveryAliases.get(`${workspaceId}|${provider}|${deliveryId}`);
|
|
246
|
+
return envelopeId ? this.envelopes.get(envelopeId) ?? null : null;
|
|
247
|
+
}
|
|
248
|
+
putEnvelope(envelope) {
|
|
249
|
+
this.envelopes.set(envelope.envelopeId, envelope);
|
|
250
|
+
}
|
|
251
|
+
putEnvelopeDeliveryAlias(workspaceId, provider, deliveryId, envelopeId) {
|
|
252
|
+
this.deliveryAliases.set(`${workspaceId}|${provider}|${deliveryId}`, envelopeId);
|
|
253
|
+
}
|
|
254
|
+
listEnvelopes(options) {
|
|
255
|
+
return {
|
|
256
|
+
items: Array.from(this.envelopes.values()).filter((envelope) => (!options.workspaceId || envelope.workspaceId === options.workspaceId) &&
|
|
257
|
+
(!options.provider || envelope.provider === options.provider) &&
|
|
258
|
+
(!options.status || envelope.status === options.status)),
|
|
259
|
+
nextCursor: null,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
getWorkspaceId() {
|
|
263
|
+
return "ws_core";
|
|
264
|
+
}
|
|
265
|
+
}
|
package/package.json
CHANGED