@baack-software/baack-ts-tasks 0.0.0-stage → 0.0.1
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/LICENSE +201 -0
- package/README.md +158 -2
- package/dist/activity.d.ts +20 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +75 -0
- package/dist/activity.js.map +1 -0
- package/dist/blockers.d.ts +28 -0
- package/dist/blockers.d.ts.map +1 -0
- package/dist/blockers.js +134 -0
- package/dist/blockers.js.map +1 -0
- package/dist/conflicts.d.ts +50 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +73 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/draft.d.ts +45 -0
- package/dist/draft.d.ts.map +1 -0
- package/dist/draft.js +155 -0
- package/dist/draft.js.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/react/TaskActivity.d.ts +15 -0
- package/dist/react/TaskActivity.d.ts.map +1 -0
- package/dist/react/TaskActivity.js +60 -0
- package/dist/react/TaskActivity.js.map +1 -0
- package/dist/react/TaskBoard.d.ts +38 -0
- package/dist/react/TaskBoard.d.ts.map +1 -0
- package/dist/react/TaskBoard.js +85 -0
- package/dist/react/TaskBoard.js.map +1 -0
- package/dist/react/TaskConflicts.d.ts +26 -0
- package/dist/react/TaskConflicts.d.ts.map +1 -0
- package/dist/react/TaskConflicts.js +87 -0
- package/dist/react/TaskConflicts.js.map +1 -0
- package/dist/react/TaskCreateForm.d.ts +13 -0
- package/dist/react/TaskCreateForm.d.ts.map +1 -0
- package/dist/react/TaskCreateForm.js +68 -0
- package/dist/react/TaskCreateForm.js.map +1 -0
- package/dist/react/TaskEditor.d.ts +33 -0
- package/dist/react/TaskEditor.d.ts.map +1 -0
- package/dist/react/TaskEditor.js +172 -0
- package/dist/react/TaskEditor.js.map +1 -0
- package/dist/react/TaskFilters.d.ts +15 -0
- package/dist/react/TaskFilters.d.ts.map +1 -0
- package/dist/react/TaskFilters.js +48 -0
- package/dist/react/TaskFilters.js.map +1 -0
- package/dist/react/TaskView.d.ts +30 -0
- package/dist/react/TaskView.d.ts.map +1 -0
- package/dist/react/TaskView.js +108 -0
- package/dist/react/TaskView.js.map +1 -0
- package/dist/react/TasksAbout.d.ts +24 -0
- package/dist/react/TasksAbout.d.ts.map +1 -0
- package/dist/react/TasksAbout.js +82 -0
- package/dist/react/TasksAbout.js.map +1 -0
- package/dist/react/copy.d.ts +154 -0
- package/dist/react/copy.d.ts.map +1 -0
- package/dist/react/copy.js +173 -0
- package/dist/react/copy.js.map +1 -0
- package/dist/react/fields.d.ts +36 -0
- package/dist/react/fields.d.ts.map +1 -0
- package/dist/react/fields.js +21 -0
- package/dist/react/fields.js.map +1 -0
- package/dist/react/hooks.d.ts +9 -0
- package/dist/react/hooks.d.ts.map +1 -0
- package/dist/react/hooks.js +31 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react/index.d.ts +13 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +13 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/parts.d.ts +55 -0
- package/dist/react/parts.d.ts.map +1 -0
- package/dist/react/parts.js +92 -0
- package/dist/react/parts.js.map +1 -0
- package/dist/react/ui/alert.d.ts +11 -0
- package/dist/react/ui/alert.d.ts.map +1 -0
- package/dist/react/ui/alert.js +29 -0
- package/dist/react/ui/alert.js.map +1 -0
- package/dist/react/ui/avatar.d.ts +12 -0
- package/dist/react/ui/avatar.d.ts.map +1 -0
- package/dist/react/ui/avatar.js +24 -0
- package/dist/react/ui/avatar.js.map +1 -0
- package/dist/react/ui/badge.d.ts +10 -0
- package/dist/react/ui/badge.d.ts.map +1 -0
- package/dist/react/ui/badge.js +26 -0
- package/dist/react/ui/badge.js.map +1 -0
- package/dist/react/ui/button.d.ts +11 -0
- package/dist/react/ui/button.d.ts.map +1 -0
- package/dist/react/ui/button.js +37 -0
- package/dist/react/ui/button.js.map +1 -0
- package/dist/react/ui/card.d.ts +12 -0
- package/dist/react/ui/card.d.ts.map +1 -0
- package/dist/react/ui/card.js +26 -0
- package/dist/react/ui/card.js.map +1 -0
- package/dist/react/ui/input.d.ts +4 -0
- package/dist/react/ui/input.d.ts.map +1 -0
- package/dist/react/ui/input.js +8 -0
- package/dist/react/ui/input.js.map +1 -0
- package/dist/react/ui/label.d.ts +5 -0
- package/dist/react/ui/label.d.ts.map +1 -0
- package/dist/react/ui/label.js +10 -0
- package/dist/react/ui/label.js.map +1 -0
- package/dist/react/ui/radio-group.d.ts +6 -0
- package/dist/react/ui/radio-group.d.ts.map +1 -0
- package/dist/react/ui/radio-group.js +12 -0
- package/dist/react/ui/radio-group.js.map +1 -0
- package/dist/react/ui/select.d.ts +16 -0
- package/dist/react/ui/select.d.ts.map +1 -0
- package/dist/react/ui/select.js +37 -0
- package/dist/react/ui/select.js.map +1 -0
- package/dist/react/ui/separator.d.ts +5 -0
- package/dist/react/ui/separator.d.ts.map +1 -0
- package/dist/react/ui/separator.js +10 -0
- package/dist/react/ui/separator.js.map +1 -0
- package/dist/react/ui/sheet.d.ts +15 -0
- package/dist/react/ui/sheet.d.ts.map +1 -0
- package/dist/react/ui/sheet.js +38 -0
- package/dist/react/ui/sheet.js.map +1 -0
- package/dist/react/ui/skeleton.d.ts +3 -0
- package/dist/react/ui/skeleton.d.ts.map +1 -0
- package/dist/react/ui/skeleton.js +7 -0
- package/dist/react/ui/skeleton.js.map +1 -0
- package/dist/react/ui/table.d.ts +11 -0
- package/dist/react/ui/table.d.ts.map +1 -0
- package/dist/react/ui/table.js +29 -0
- package/dist/react/ui/table.js.map +1 -0
- package/dist/react/ui/tabs.d.ts +12 -0
- package/dist/react/ui/tabs.d.ts.map +1 -0
- package/dist/react/ui/tabs.js +31 -0
- package/dist/react/ui/tabs.js.map +1 -0
- package/dist/react/ui/textarea.d.ts +4 -0
- package/dist/react/ui/textarea.d.ts.map +1 -0
- package/dist/react/ui/textarea.js +8 -0
- package/dist/react/ui/textarea.js.map +1 -0
- package/dist/react/ui/toggle-group.d.ts +11 -0
- package/dist/react/ui/toggle-group.d.ts.map +1 -0
- package/dist/react/ui/toggle-group.js +25 -0
- package/dist/react/ui/toggle-group.js.map +1 -0
- package/dist/react/ui/toggle.d.ts +10 -0
- package/dist/react/ui/toggle.d.ts.map +1 -0
- package/dist/react/ui/toggle.js +27 -0
- package/dist/react/ui/toggle.js.map +1 -0
- package/dist/refs.d.ts +16 -0
- package/dist/refs.d.ts.map +1 -0
- package/dist/refs.js +43 -0
- package/dist/refs.js.map +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +4 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/recipes.d.ts +39 -0
- package/dist/server/recipes.d.ts.map +1 -0
- package/dist/server/recipes.js +81 -0
- package/dist/server/recipes.js.map +1 -0
- package/dist/server/webhooks.d.ts +135 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +243 -0
- package/dist/server/webhooks.js.map +1 -0
- package/dist/store.d.ts +124 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +443 -0
- package/dist/store.js.map +1 -0
- package/dist/task-client.d.ts +137 -0
- package/dist/task-client.d.ts.map +1 -0
- package/dist/task-client.js +271 -0
- package/dist/task-client.js.map +1 -0
- package/dist/tasks.d.ts +84 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +208 -0
- package/dist/tasks.js.map +1 -0
- package/dist/time.d.ts +15 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +34 -0
- package/dist/time.js.map +1 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +101 -4
- package/src/activity.ts +86 -0
- package/src/blockers.ts +134 -0
- package/src/conflicts.ts +115 -0
- package/src/draft.ts +162 -0
- package/src/errors.ts +40 -0
- package/src/index.ts +10 -0
- package/src/react/TaskActivity.tsx +187 -0
- package/src/react/TaskBoard.tsx +264 -0
- package/src/react/TaskConflicts.tsx +217 -0
- package/src/react/TaskCreateForm.tsx +165 -0
- package/src/react/TaskEditor.tsx +485 -0
- package/src/react/TaskFilters.tsx +122 -0
- package/src/react/TaskView.tsx +291 -0
- package/src/react/TasksAbout.tsx +226 -0
- package/src/react/copy.tsx +253 -0
- package/src/react/fields.tsx +104 -0
- package/src/react/hooks.ts +41 -0
- package/src/react/index.ts +12 -0
- package/src/react/parts.tsx +176 -0
- package/src/react/ui/README.md +6 -0
- package/src/react/ui/alert.tsx +75 -0
- package/src/react/ui/avatar.tsx +109 -0
- package/src/react/ui/badge.tsx +48 -0
- package/src/react/ui/button.tsx +66 -0
- package/src/react/ui/card.tsx +102 -0
- package/src/react/ui/input.tsx +18 -0
- package/src/react/ui/label.tsx +23 -0
- package/src/react/ui/radio-group.tsx +41 -0
- package/src/react/ui/select.tsx +189 -0
- package/src/react/ui/separator.tsx +27 -0
- package/src/react/ui/sheet.tsx +145 -0
- package/src/react/ui/skeleton.tsx +13 -0
- package/src/react/ui/table.tsx +113 -0
- package/src/react/ui/tabs.tsx +89 -0
- package/src/react/ui/textarea.tsx +17 -0
- package/src/react/ui/toggle-group.tsx +89 -0
- package/src/react/ui/toggle.tsx +44 -0
- package/src/refs.ts +49 -0
- package/src/server/index.ts +3 -0
- package/src/server/recipes.ts +107 -0
- package/src/server/webhooks.ts +369 -0
- package/src/store.ts +538 -0
- package/src/task-client.ts +373 -0
- package/src/tasks.ts +263 -0
- package/src/time.ts +35 -0
- package/src/types.ts +101 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { isBaackApiError } from '@baack-software/baack-ts-client';
|
|
2
|
+
import { parseTimestamp } from "../time.js";
|
|
3
|
+
/**
|
|
4
|
+
* Receiving Baack webhooks: verifying a delivery's signature, reading its event, and fetching the task it is about.
|
|
5
|
+
* Uses Web Crypto and the Fetch API types only, so it runs on Node 20+, Deno, Bun and edge runtimes.
|
|
6
|
+
*
|
|
7
|
+
* A delivery is a POST whose body is a webhook event, an envelope naming what changed (`trigger`) and where it is
|
|
8
|
+
* (`v1`), without the changed representation itself; read it from the API. Its headers:
|
|
9
|
+
*
|
|
10
|
+
* - `Signature`: base64 HMAC-SHA256, keyed with the subscription's secret, over the UTF-8 bytes of the parts
|
|
11
|
+
* `Signed-Headers` lists, in that order and without separators. Today that is the body, then `Last-Modified`, then
|
|
12
|
+
* `Idempotency-Key`.
|
|
13
|
+
* - `Last-Modified`: when the trigger happened, an ISO local date-time (UTC, without a zone, like other Baack
|
|
14
|
+
* timestamps), signed as sent.
|
|
15
|
+
* - `Idempotency-Key`: the trigger's URN. Retries carry the same key, so handle each key once.
|
|
16
|
+
* - `X-Attempt-Number` and `X-Attempts-Remaining`: where a delivery is in its retries.
|
|
17
|
+
*/
|
|
18
|
+
/** Header names, lower case as the Fetch API's `Headers` and Node's `IncomingMessage` give them. */
|
|
19
|
+
export const WEBHOOK_HEADERS = {
|
|
20
|
+
signature: 'signature',
|
|
21
|
+
signedHeaders: 'signed-headers',
|
|
22
|
+
timestamp: 'last-modified',
|
|
23
|
+
idempotencyKey: 'idempotency-key',
|
|
24
|
+
attempt: 'x-attempt-number',
|
|
25
|
+
attemptsRemaining: 'x-attempts-remaining',
|
|
26
|
+
};
|
|
27
|
+
/** What a delivery signs when it doesn't say, the order the API uses. */
|
|
28
|
+
export const DEFAULT_SIGNED_PARTS = ['body', WEBHOOK_HEADERS.timestamp, WEBHOOK_HEADERS.idempotencyKey];
|
|
29
|
+
/** Thrown when a delivery can't be trusted or read. Answer 401 (or 400 for `malformed-event`) and don't act on it. */
|
|
30
|
+
export class WebhookVerificationError extends Error {
|
|
31
|
+
reason;
|
|
32
|
+
name = 'WebhookVerificationError';
|
|
33
|
+
constructor(reason, message) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.reason = reason;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Checks a delivery's signature and returns its event. `body` must be the raw bytes or text as received: parsing and
|
|
40
|
+
* re-serialising the JSON changes the bytes and the signature won't match. Throws `WebhookVerificationError`.
|
|
41
|
+
*/
|
|
42
|
+
export async function verifyWebhook(delivery, secret, options = {}) {
|
|
43
|
+
const header = (name) => readHeader(delivery.headers, name);
|
|
44
|
+
const signature = header(WEBHOOK_HEADERS.signature);
|
|
45
|
+
if (!signature)
|
|
46
|
+
throw new WebhookVerificationError('missing-signature', 'The delivery has no Signature header.');
|
|
47
|
+
const listed = header(WEBHOOK_HEADERS.signedHeaders);
|
|
48
|
+
const parts = listed ? listed.split(',').map((part) => part.trim().toLowerCase()) : [...DEFAULT_SIGNED_PARTS];
|
|
49
|
+
// A signature that doesn't cover the body proves nothing about the event.
|
|
50
|
+
if (!parts.includes('body'))
|
|
51
|
+
throw new WebhookVerificationError('unsigned-body', 'Signed-Headers does not include the body.');
|
|
52
|
+
const body = typeof delivery.body === 'string' ? encoder.encode(delivery.body) : delivery.body;
|
|
53
|
+
const signed = [];
|
|
54
|
+
for (const part of parts) {
|
|
55
|
+
if (part === 'body') {
|
|
56
|
+
signed.push(body);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const value = header(part);
|
|
60
|
+
if (value === undefined)
|
|
61
|
+
throw new WebhookVerificationError('missing-header', `The signed header ${part} is missing.`);
|
|
62
|
+
signed.push(encoder.encode(value));
|
|
63
|
+
}
|
|
64
|
+
const expected = decodeBase64(signature);
|
|
65
|
+
const key = await hmacKey(secret, ['verify']);
|
|
66
|
+
// Web Crypto's verify compares in constant time.
|
|
67
|
+
if (!expected || !(await crypto.subtle.verify('HMAC', key, expected, concat(signed)))) {
|
|
68
|
+
throw new WebhookVerificationError('bad-signature', 'The signature does not match.');
|
|
69
|
+
}
|
|
70
|
+
const timestamp = header(WEBHOOK_HEADERS.timestamp) ?? '';
|
|
71
|
+
if (options.maxAgeMs !== undefined) {
|
|
72
|
+
// Zoneless, so parseTimestamp (UTC) rather than Date.parse, which would read it as local time.
|
|
73
|
+
const at = parseTimestamp(timestamp)?.getTime();
|
|
74
|
+
const now = (options.now ?? new Date()).getTime();
|
|
75
|
+
if (at === undefined || now - at > options.maxAgeMs) {
|
|
76
|
+
throw new WebhookVerificationError('stale', `The delivery is from ${timestamp || 'an unknown time'}, too long ago.`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const event = parseWebhookEvent(typeof delivery.body === 'string' ? delivery.body : decoder.decode(delivery.body));
|
|
80
|
+
const idempotencyKey = header(WEBHOOK_HEADERS.idempotencyKey) ?? event.trigger.urn;
|
|
81
|
+
const attempt = toNumber(header(WEBHOOK_HEADERS.attempt));
|
|
82
|
+
const attemptsRemaining = toNumber(header(WEBHOOK_HEADERS.attemptsRemaining));
|
|
83
|
+
return {
|
|
84
|
+
event,
|
|
85
|
+
idempotencyKey,
|
|
86
|
+
timestamp,
|
|
87
|
+
...(attempt === undefined ? {} : { attempt }),
|
|
88
|
+
...(attemptsRemaining === undefined ? {} : { attemptsRemaining }),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** `verifyWebhook` for a Fetch API `Request`, reading its body. */
|
|
92
|
+
export async function verifyWebhookRequest(request, secret, options) {
|
|
93
|
+
const body = new Uint8Array(await request.arrayBuffer());
|
|
94
|
+
return verifyWebhook({ body, headers: request.headers }, secret, options);
|
|
95
|
+
}
|
|
96
|
+
/** Reads a delivery's body as a webhook event, checking the parts handlers rely on. Throws `WebhookVerificationError`. */
|
|
97
|
+
export function parseWebhookEvent(body) {
|
|
98
|
+
let value;
|
|
99
|
+
try {
|
|
100
|
+
value = JSON.parse(body);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
throw new WebhookVerificationError('malformed-event', 'The body is not JSON.');
|
|
104
|
+
}
|
|
105
|
+
const trigger = value?.trigger;
|
|
106
|
+
if (!trigger || typeof trigger.urn !== 'string' || typeof trigger.subject !== 'string' || typeof trigger.event !== 'string') {
|
|
107
|
+
throw new WebhookVerificationError('malformed-event', 'The event has no trigger with a urn, subject and event.');
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
/** The URN of the representation an event is about. */
|
|
112
|
+
export function webhookTarget(event) {
|
|
113
|
+
return event.v1?.urn || event.trigger.targetUrn || undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Signs a delivery the way the API does, returning the headers to send with `body`. For tests and for replaying or
|
|
117
|
+
* simulating deliveries locally.
|
|
118
|
+
*/
|
|
119
|
+
export async function signWebhook(body, secret, { timestamp, idempotencyKey }) {
|
|
120
|
+
const key = await hmacKey(secret, ['sign']);
|
|
121
|
+
const data = concat([encoder.encode(body), encoder.encode(timestamp), encoder.encode(idempotencyKey)]);
|
|
122
|
+
const mac = new Uint8Array(await crypto.subtle.sign('HMAC', key, data));
|
|
123
|
+
return {
|
|
124
|
+
Signature: encodeBase64(mac),
|
|
125
|
+
'Signed-Headers': DEFAULT_SIGNED_PARTS.join(','),
|
|
126
|
+
'Last-Modified': timestamp,
|
|
127
|
+
'Idempotency-Key': idempotencyKey,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The task an event is about, read fresh, since events don't carry it. `null` when the task was deleted, by the event
|
|
132
|
+
* or since. Throws for events about anything but a task.
|
|
133
|
+
*/
|
|
134
|
+
export async function taskFromWebhook(event, tasks) {
|
|
135
|
+
if (event.trigger.subject !== 'TASK')
|
|
136
|
+
throw new TypeError(`The event is about a ${event.trigger.subject}, not a task.`);
|
|
137
|
+
const urn = webhookTarget(event);
|
|
138
|
+
if (!urn || event.trigger.event === 'DELETE')
|
|
139
|
+
return null;
|
|
140
|
+
try {
|
|
141
|
+
return await tasks.get(urn);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (isBaackApiError(error) && error.isNotFound)
|
|
145
|
+
return null;
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Keys held in memory for `ttlMs` (default a day). Enough for one process; use a shared store (a database row with
|
|
151
|
+
* a unique key, Redis `SET NX`) when several handle deliveries.
|
|
152
|
+
*/
|
|
153
|
+
export function memoryIdempotencyStore(ttlMs = 24 * 3600_000) {
|
|
154
|
+
const seen = new Map();
|
|
155
|
+
return {
|
|
156
|
+
claim(key) {
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
for (const [old, at] of seen)
|
|
159
|
+
if (now - at > ttlMs)
|
|
160
|
+
seen.delete(old);
|
|
161
|
+
if (seen.has(key))
|
|
162
|
+
return false;
|
|
163
|
+
seen.set(key, now);
|
|
164
|
+
return true;
|
|
165
|
+
},
|
|
166
|
+
release(key) {
|
|
167
|
+
seen.delete(key);
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A Fetch API handler (`(request) => Response`) for a webhook endpoint: verifies, skips repeats, and runs `onEvent`.
|
|
173
|
+
* Answers 401 to an untrusted delivery, 400 to an unreadable one, 200 once handled or already handled.
|
|
174
|
+
*/
|
|
175
|
+
export function webhookHandler(options) {
|
|
176
|
+
const idempotency = options.idempotency ?? memoryIdempotencyStore();
|
|
177
|
+
return async (request) => {
|
|
178
|
+
let delivery;
|
|
179
|
+
try {
|
|
180
|
+
delivery = await verifyWebhookRequest(request, options.secret, options.verify);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error instanceof WebhookVerificationError) {
|
|
184
|
+
return new Response(error.message, { status: error.reason === 'malformed-event' ? 400 : 401 });
|
|
185
|
+
}
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
if (!(await idempotency.claim(delivery.idempotencyKey)))
|
|
189
|
+
return new Response('Already handled', { status: 200 });
|
|
190
|
+
try {
|
|
191
|
+
await options.onEvent(delivery);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
// Not handled after all: let the retry run it.
|
|
195
|
+
await idempotency.release?.(delivery.idempotencyKey);
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
return new Response('OK', { status: 200 });
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const encoder = new TextEncoder();
|
|
202
|
+
const decoder = new TextDecoder();
|
|
203
|
+
function hmacKey(secret, usages) {
|
|
204
|
+
return crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, usages);
|
|
205
|
+
}
|
|
206
|
+
function readHeader(headers, name) {
|
|
207
|
+
if (typeof headers.get === 'function')
|
|
208
|
+
return headers.get(name) ?? undefined;
|
|
209
|
+
const record = headers;
|
|
210
|
+
const key = Object.keys(record).find((k) => k.toLowerCase() === name);
|
|
211
|
+
const value = key === undefined ? undefined : record[key];
|
|
212
|
+
return Array.isArray(value) ? value.join(', ') : value;
|
|
213
|
+
}
|
|
214
|
+
function concat(parts) {
|
|
215
|
+
const result = new Uint8Array(parts.reduce((length, part) => length + part.length, 0));
|
|
216
|
+
let offset = 0;
|
|
217
|
+
for (const part of parts) {
|
|
218
|
+
result.set(part, offset);
|
|
219
|
+
offset += part.length;
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
function encodeBase64(bytes) {
|
|
224
|
+
let binary = '';
|
|
225
|
+
for (const byte of bytes)
|
|
226
|
+
binary += String.fromCharCode(byte);
|
|
227
|
+
return btoa(binary);
|
|
228
|
+
}
|
|
229
|
+
function decodeBase64(text) {
|
|
230
|
+
try {
|
|
231
|
+
return Uint8Array.from(atob(text.trim()), (char) => char.charCodeAt(0));
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function toNumber(value) {
|
|
238
|
+
if (value === undefined || value === '')
|
|
239
|
+
return undefined;
|
|
240
|
+
const number = Number(value);
|
|
241
|
+
return Number.isFinite(number) ? number : undefined;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/server/webhooks.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { type SearchItem, type Task, type TaskLog, type TaskPriority, type TaskStatus, type Workflow } from '@baack-software/baack-ts-client';
|
|
2
|
+
import type { TaskField } from './conflicts.ts';
|
|
3
|
+
import { type DraftInput } from './draft.ts';
|
|
4
|
+
import type { ChangeOptions, TaskClient } from './task-client.ts';
|
|
5
|
+
import { type TaskComparator, type TaskFilter, type TaskGrouping } from './tasks.ts';
|
|
6
|
+
import type { RefLike, TaskDraft, TaskInput, TaskListField } from './types.ts';
|
|
7
|
+
export interface TaskStoreOptions {
|
|
8
|
+
/** The company whose tasks the store holds. */
|
|
9
|
+
owner: RefLike;
|
|
10
|
+
/** The identity changes are attributed to in the activity log, unless a change names another. */
|
|
11
|
+
actor?: RefLike;
|
|
12
|
+
/** Load identities and projects for names and pickers. Default true. */
|
|
13
|
+
includeRelated?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Workflows whose stages tasks can move through, loaded with the tasks for stage columns and pickers. The API has
|
|
16
|
+
* no way to list them, so the app names them. One that can't be read is left out.
|
|
17
|
+
*/
|
|
18
|
+
workflows?: readonly RefLike[];
|
|
19
|
+
/**
|
|
20
|
+
* The server can change a task after accepting a write. This long after each write, the store reads the task (and
|
|
21
|
+
* its log, if loaded) again. Default 500 ms; 0 turns it off.
|
|
22
|
+
*/
|
|
23
|
+
settleMs?: number;
|
|
24
|
+
}
|
|
25
|
+
export type LoadStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
26
|
+
export interface TaskLogState {
|
|
27
|
+
status: LoadStatus;
|
|
28
|
+
/** Newest first. */
|
|
29
|
+
logs: readonly TaskLog[];
|
|
30
|
+
/** Set while older pages remain. */
|
|
31
|
+
after?: string;
|
|
32
|
+
error?: unknown;
|
|
33
|
+
}
|
|
34
|
+
/** A snapshot of the store. Each change replaces it, so it can be compared by reference. */
|
|
35
|
+
export interface TaskStoreState {
|
|
36
|
+
status: LoadStatus;
|
|
37
|
+
error?: unknown;
|
|
38
|
+
/** Tasks as the reader should see them, pending changes applied, in search order with new tasks first. */
|
|
39
|
+
tasks: readonly Task[];
|
|
40
|
+
byUrn: ReadonlyMap<string, Task>;
|
|
41
|
+
identities: ReadonlyMap<string, SearchItem>;
|
|
42
|
+
projects: ReadonlyMap<string, SearchItem>;
|
|
43
|
+
/** The workflows named in the options, by URN, in the order named. */
|
|
44
|
+
workflows: ReadonlyMap<string, Workflow>;
|
|
45
|
+
/** URNs of tasks with a write in flight. */
|
|
46
|
+
saving: ReadonlySet<string>;
|
|
47
|
+
/** Activity logs of the tasks whose logs were loaded. */
|
|
48
|
+
logs: ReadonlyMap<string, TaskLogState>;
|
|
49
|
+
/**
|
|
50
|
+
* Fields someone else changed while this person was editing, found when one of their changes was saved, by task
|
|
51
|
+
* URN. Kept until `dismissChangedElsewhere`.
|
|
52
|
+
*/
|
|
53
|
+
changedElsewhere: ReadonlyMap<string, readonly (TaskField | TaskListField)[]>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Holds a company's tasks for a UI: loads them, applies changes straight away and rolls them back if the server
|
|
57
|
+
* refuses, and notifies subscribers. `subscribe` and `getState` fit React's `useSyncExternalStore`.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* const store = new TaskStore(tasks, { owner: companyUrn, actor: me });
|
|
61
|
+
* store.subscribe(() => render(selectTasks(store.getState(), { assignee: [me], open: true }, byUrgency)));
|
|
62
|
+
* await store.load();
|
|
63
|
+
* await store.setStatus(urn, 'COMPLETED');
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare class TaskStore {
|
|
67
|
+
#private;
|
|
68
|
+
readonly client: TaskClient;
|
|
69
|
+
readonly options: TaskStoreOptions;
|
|
70
|
+
constructor(client: TaskClient, options: TaskStoreOptions);
|
|
71
|
+
getState: () => TaskStoreState;
|
|
72
|
+
subscribe: (listener: () => void) => (() => void);
|
|
73
|
+
/** The task as the reader should see it. */
|
|
74
|
+
get(task: RefLike): Task | undefined;
|
|
75
|
+
/** Stops pending work and drops subscribers. */
|
|
76
|
+
dispose(): void;
|
|
77
|
+
/** Loads (or reloads) every task. A load already running is cancelled. Pending changes stay applied. */
|
|
78
|
+
load(): Promise<void>;
|
|
79
|
+
/** Reads one task again. Removes it when it no longer exists. */
|
|
80
|
+
reload(task: RefLike): Promise<Task | undefined>;
|
|
81
|
+
create(input: TaskDraft | Omit<DraftInput, 'owner'>, options?: ChangeOptions): Promise<Task>;
|
|
82
|
+
/**
|
|
83
|
+
* Shows `changes` at once and sends them. On success the server's task replaces the shown one; on failure the
|
|
84
|
+
* change is rolled back and the error rethrown.
|
|
85
|
+
*
|
|
86
|
+
* The change is checked against the task as shown when it was made. If someone else changed one of the same fields
|
|
87
|
+
* since, it throws `TaskConflictError`, saves nothing and shows the task as it is now; resolve the conflict with
|
|
88
|
+
* `resolveConflicts` and update again. Other fields they changed are kept and listed in `changedElsewhere`.
|
|
89
|
+
*/
|
|
90
|
+
update(task: RefLike, changes: TaskInput, options?: ChangeOptions): Promise<Task>;
|
|
91
|
+
/** Clears the note of fields someone else changed on the task. */
|
|
92
|
+
dismissChangedElsewhere(task: RefLike): void;
|
|
93
|
+
delete(task: RefLike): Promise<void>;
|
|
94
|
+
setStatus(task: RefLike, status: TaskStatus, options?: ChangeOptions): Promise<Task>;
|
|
95
|
+
setPriority(task: RefLike, priority: TaskPriority, options?: ChangeOptions): Promise<Task>;
|
|
96
|
+
assign(task: RefLike, assignee: RefLike | null, options?: ChangeOptions): Promise<Task>;
|
|
97
|
+
setDue(task: RefLike, dueTimestamp: string | null, options?: ChangeOptions): Promise<Task>;
|
|
98
|
+
setProject(task: RefLike, project: RefLike | null, options?: ChangeOptions): Promise<Task>;
|
|
99
|
+
setWorkflowStage(task: RefLike, stage: RefLike | null, options?: ChangeOptions): Promise<Task>;
|
|
100
|
+
addBlocker(task: RefLike, blocker: RefLike, options?: ChangeOptions): Promise<Task>;
|
|
101
|
+
removeBlocker(task: RefLike, blocker: RefLike, options?: ChangeOptions): Promise<Task>;
|
|
102
|
+
/** Adds a reference to one of the task's lists, building on pending changes. */
|
|
103
|
+
addTo(task: RefLike, field: TaskListField, value: RefLike, options?: ChangeOptions): Promise<Task>;
|
|
104
|
+
/** Removes a reference from one of the task's lists, building on pending changes. */
|
|
105
|
+
removeFrom(task: RefLike, field: TaskListField, value: RefLike, options?: ChangeOptions): Promise<Task>;
|
|
106
|
+
/** Loads the newest page of the task's log, replacing what was loaded. */
|
|
107
|
+
loadLogs(task: RefLike): Promise<void>;
|
|
108
|
+
/** Loads the next older page of the task's log, if there is one. */
|
|
109
|
+
loadMoreLogs(task: RefLike): Promise<void>;
|
|
110
|
+
/** Adds a comment and shows it at the top of the task's log. */
|
|
111
|
+
comment(task: RefLike, text: string, options?: {
|
|
112
|
+
actor?: RefLike;
|
|
113
|
+
replyTo?: RefLike;
|
|
114
|
+
}): Promise<TaskLog>;
|
|
115
|
+
}
|
|
116
|
+
/** Tasks matching `filter`, sorted when a comparator is given. */
|
|
117
|
+
export declare function selectTasks(state: TaskStoreState, filter?: TaskFilter, sort?: TaskComparator): Task[];
|
|
118
|
+
/** Tasks matching `filter`, grouped for a board, each group sorted when a comparator is given. */
|
|
119
|
+
export declare function selectGroups(state: TaskStoreState, by: TaskGrouping, filter?: TaskFilter, sort?: TaskComparator): Map<string, Task[]>;
|
|
120
|
+
/** The display name of an identity, project, task or workflow stage, falling back to the end of its URN. */
|
|
121
|
+
export declare function selectName(state: TaskStoreState, item: RefLike): string;
|
|
122
|
+
/** The URNs of the open tasks blocking `task`, by the store's tasks. */
|
|
123
|
+
export declare function selectOpenBlockers(state: TaskStoreState, task: Task): string[];
|
|
124
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|