@cat-factory/worker 0.171.2 → 0.172.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.
- package/dist/infrastructure/repositories/D1NotificationWebhookRepository.d.ts +9 -7
- package/dist/infrastructure/repositories/D1NotificationWebhookRepository.d.ts.map +1 -1
- package/dist/infrastructure/repositories/D1NotificationWebhookRepository.js +61 -27
- package/dist/infrastructure/repositories/D1NotificationWebhookRepository.js.map +1 -1
- package/migrations/0085_notification_webhook_collection.sql +37 -0
- package/package.json +18 -18
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import type { NotificationWebhookRecord, NotificationWebhookRepository } from '@cat-factory/kernel';
|
|
1
|
+
import type { NotificationWebhookPutOutcome, NotificationWebhookRecord, NotificationWebhookRepository } from '@cat-factory/kernel';
|
|
2
2
|
import type { D1Database } from '@cloudflare/workers-types';
|
|
3
3
|
/**
|
|
4
|
-
* A workspace's outbound
|
|
5
|
-
* 0072, `alert_events` in 0080). All three JSON filter
|
|
6
|
-
* parsers the Drizzle repo uses, so the columns can't drift
|
|
4
|
+
* A workspace's outbound webhooks, keyed by (workspace, endpoint id) (migration 0061; `run_events`
|
|
5
|
+
* added in 0072, `alert_events` in 0080, the named-collection key in 0085). All three JSON filter
|
|
6
|
+
* columns are decoded through the SHARED parsers the Drizzle repo uses, so the columns can't drift
|
|
7
|
+
* between runtimes.
|
|
7
8
|
*/
|
|
8
9
|
export declare class D1NotificationWebhookRepository implements NotificationWebhookRepository {
|
|
9
10
|
private readonly db;
|
|
10
11
|
constructor({ db }: {
|
|
11
12
|
db: D1Database;
|
|
12
13
|
});
|
|
13
|
-
get(workspaceId: string): Promise<NotificationWebhookRecord | null>;
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
get(workspaceId: string, id: string): Promise<NotificationWebhookRecord | null>;
|
|
15
|
+
list(workspaceId: string): Promise<NotificationWebhookRecord[]>;
|
|
16
|
+
put(record: NotificationWebhookRecord, limit: number): Promise<NotificationWebhookPutOutcome>;
|
|
17
|
+
delete(workspaceId: string, id: string): Promise<void>;
|
|
16
18
|
}
|
|
17
19
|
//# sourceMappingURL=D1NotificationWebhookRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"D1NotificationWebhookRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1NotificationWebhookRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"D1NotificationWebhookRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1NotificationWebhookRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,yBAAyB,EACzB,6BAA6B,EAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAoB3D;;;;;GAKG;AACH,qBAAa,+BAAgC,YAAW,6BAA6B;IACnF,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAE/B,YAAY,EAAE,EAAE,EAAE,EAAE;QAAE,EAAE,EAAE,UAAU,CAAA;KAAE,EAErC;IAEK,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAMpF;IAEK,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAYpE;IAEK,GAAG,CACP,MAAM,EAAE,yBAAyB,EACjC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,6BAA6B,CAAC,CAmDxC;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3D;CACF"}
|
|
@@ -1,38 +1,55 @@
|
|
|
1
1
|
import { parseNotificationWebhookTypes, parsePlatformAlertEvents, parseRunLifecycleEvents, } from '@cat-factory/server';
|
|
2
2
|
/**
|
|
3
|
-
* A workspace's outbound
|
|
4
|
-
* 0072, `alert_events` in 0080). All three JSON filter
|
|
5
|
-
* parsers the Drizzle repo uses, so the columns can't drift
|
|
3
|
+
* A workspace's outbound webhooks, keyed by (workspace, endpoint id) (migration 0061; `run_events`
|
|
4
|
+
* added in 0072, `alert_events` in 0080, the named-collection key in 0085). All three JSON filter
|
|
5
|
+
* columns are decoded through the SHARED parsers the Drizzle repo uses, so the columns can't drift
|
|
6
|
+
* between runtimes.
|
|
6
7
|
*/
|
|
7
8
|
export class D1NotificationWebhookRepository {
|
|
8
9
|
db;
|
|
9
10
|
constructor({ db }) {
|
|
10
11
|
this.db = db;
|
|
11
12
|
}
|
|
12
|
-
async get(workspaceId) {
|
|
13
|
+
async get(workspaceId, id) {
|
|
13
14
|
const row = await this.db
|
|
14
|
-
.prepare(`SELECT * FROM notification_webhooks WHERE workspace_id = ?`)
|
|
15
|
-
.bind(workspaceId)
|
|
15
|
+
.prepare(`SELECT * FROM notification_webhooks WHERE workspace_id = ? AND id = ?`)
|
|
16
|
+
.bind(workspaceId, id)
|
|
16
17
|
.first();
|
|
17
|
-
|
|
18
|
-
return null;
|
|
19
|
-
return {
|
|
20
|
-
workspaceId: row.workspace_id,
|
|
21
|
-
url: row.url,
|
|
22
|
-
types: parseNotificationWebhookTypes(row.types),
|
|
23
|
-
runEvents: parseRunLifecycleEvents(row.run_events),
|
|
24
|
-
alertEvents: parsePlatformAlertEvents(row.alert_events),
|
|
25
|
-
enabled: row.enabled === 1,
|
|
26
|
-
secretSealed: row.secret_sealed,
|
|
27
|
-
updatedAt: row.updated_at,
|
|
28
|
-
};
|
|
18
|
+
return row ? toRecord(row) : null;
|
|
29
19
|
}
|
|
30
|
-
async
|
|
31
|
-
|
|
20
|
+
async list(workspaceId) {
|
|
21
|
+
// `COLLATE BINARY` is SQLite's default for a text column and is spelled out only to name the
|
|
22
|
+
// order this port promises: the Drizzle mirror has to ask Postgres for `COLLATE "C"` to match,
|
|
23
|
+
// because its database-locale default would sort `web-hook` after `webhook` by ignoring the
|
|
24
|
+
// punctuation. Byte order on both, so the two runtimes return one sequence.
|
|
25
|
+
const { results } = await this.db
|
|
26
|
+
.prepare(`SELECT * FROM notification_webhooks WHERE workspace_id = ? ORDER BY id COLLATE BINARY`)
|
|
27
|
+
.bind(workspaceId)
|
|
28
|
+
.all();
|
|
29
|
+
return (results ?? []).map(toRecord);
|
|
30
|
+
}
|
|
31
|
+
async put(record, limit) {
|
|
32
|
+
// ONE statement, so the cap and the write cannot be separated by another writer. The row
|
|
33
|
+
// source is a `SELECT ... WHERE` rather than `VALUES` precisely so the admission test rides
|
|
34
|
+
// inside the insert: SQLite serializes statements, so a concurrent enrolment either sees this
|
|
35
|
+
// row or is seen by it, and can never read the same free slot twice.
|
|
36
|
+
//
|
|
37
|
+
// The predicate admits an existing endpoint unconditionally (`EXISTS`) before it consults the
|
|
38
|
+
// count, because the count includes that row: without the first half, a workspace sitting
|
|
39
|
+
// exactly at the limit could no longer edit, disable or re-point what it had already
|
|
40
|
+
// registered, which are the only ways back under it.
|
|
41
|
+
//
|
|
42
|
+
// The `WHERE` also disambiguates the parse. SQLite cannot otherwise tell an upsert's `ON
|
|
43
|
+
// CONFLICT` from a join's `ON` in an `INSERT ... SELECT`, and its documented fix is exactly a
|
|
44
|
+
// `WHERE` clause on the SELECT, which this needs anyway.
|
|
45
|
+
const result = await this.db
|
|
32
46
|
.prepare(`INSERT INTO notification_webhooks
|
|
33
|
-
(workspace_id, url, types, run_events, alert_events, enabled, secret_sealed, updated_at)
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
(workspace_id, id, name, url, types, run_events, alert_events, enabled, secret_sealed, updated_at)
|
|
48
|
+
SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
49
|
+
WHERE EXISTS (SELECT 1 FROM notification_webhooks WHERE workspace_id = ? AND id = ?)
|
|
50
|
+
OR (SELECT COUNT(*) FROM notification_webhooks WHERE workspace_id = ?) < ?
|
|
51
|
+
ON CONFLICT (workspace_id, id) DO UPDATE SET
|
|
52
|
+
name = excluded.name,
|
|
36
53
|
url = excluded.url,
|
|
37
54
|
types = excluded.types,
|
|
38
55
|
run_events = excluded.run_events,
|
|
@@ -40,14 +57,31 @@ export class D1NotificationWebhookRepository {
|
|
|
40
57
|
enabled = excluded.enabled,
|
|
41
58
|
secret_sealed = excluded.secret_sealed,
|
|
42
59
|
updated_at = excluded.updated_at`)
|
|
43
|
-
.bind(record.workspaceId, record.url, JSON.stringify(record.types), JSON.stringify(record.runEvents), JSON.stringify(record.alertEvents), record.enabled ? 1 : 0, record.secretSealed, record.updatedAt)
|
|
60
|
+
.bind(record.workspaceId, record.id, record.name, record.url, JSON.stringify(record.types), JSON.stringify(record.runEvents), JSON.stringify(record.alertEvents), record.enabled ? 1 : 0, record.secretSealed, record.updatedAt, record.workspaceId, record.id, record.workspaceId, limit)
|
|
44
61
|
.run();
|
|
62
|
+
// A filtered-out row writes nothing, which is the refusal. An admitted one always reports a
|
|
63
|
+
// change, whether it inserted or took the conflict branch.
|
|
64
|
+
return (result.meta?.changes ?? 0) > 0 ? 'stored' : 'limit_reached';
|
|
45
65
|
}
|
|
46
|
-
async delete(workspaceId) {
|
|
66
|
+
async delete(workspaceId, id) {
|
|
47
67
|
await this.db
|
|
48
|
-
.prepare(`DELETE FROM notification_webhooks WHERE workspace_id = ?`)
|
|
49
|
-
.bind(workspaceId)
|
|
68
|
+
.prepare(`DELETE FROM notification_webhooks WHERE workspace_id = ? AND id = ?`)
|
|
69
|
+
.bind(workspaceId, id)
|
|
50
70
|
.run();
|
|
51
71
|
}
|
|
52
72
|
}
|
|
73
|
+
function toRecord(row) {
|
|
74
|
+
return {
|
|
75
|
+
workspaceId: row.workspace_id,
|
|
76
|
+
id: row.id,
|
|
77
|
+
name: row.name,
|
|
78
|
+
url: row.url,
|
|
79
|
+
types: parseNotificationWebhookTypes(row.types),
|
|
80
|
+
runEvents: parseRunLifecycleEvents(row.run_events),
|
|
81
|
+
alertEvents: parsePlatformAlertEvents(row.alert_events),
|
|
82
|
+
enabled: row.enabled === 1,
|
|
83
|
+
secretSealed: row.secret_sealed,
|
|
84
|
+
updatedAt: row.updated_at,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
53
87
|
//# sourceMappingURL=D1NotificationWebhookRepository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"D1NotificationWebhookRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1NotificationWebhookRepository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"D1NotificationWebhookRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/repositories/D1NotificationWebhookRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- Several NAMED outbound webhooks per workspace, replacing the one-row-per-workspace shape from
|
|
2
|
+
-- migration 0061. One endpoint per workspace made a second integration's enrolment a hostile act:
|
|
3
|
+
-- registering it overwrote whatever was already there, and the only symptom was that the previous
|
|
4
|
+
-- receiver went quiet. Each row now carries a caller-chosen `id` (the key, alongside the workspace)
|
|
5
|
+
-- and an operator-facing `name`.
|
|
6
|
+
--
|
|
7
|
+
-- SQLite cannot re-key a table in place, so this is the standard rebuild. Existing rows migrate to
|
|
8
|
+
-- the id `default`, which is exactly what the singular `/api/v1/notification-webhook` routes now
|
|
9
|
+
-- address: an already-registered endpoint keeps delivering, and the caller that registered it keeps
|
|
10
|
+
-- addressing it through the same route.
|
|
11
|
+
|
|
12
|
+
CREATE TABLE notification_webhooks_new (
|
|
13
|
+
workspace_id TEXT NOT NULL,
|
|
14
|
+
id TEXT NOT NULL,
|
|
15
|
+
name TEXT NOT NULL,
|
|
16
|
+
url TEXT NOT NULL,
|
|
17
|
+
types TEXT NOT NULL DEFAULT '[]',
|
|
18
|
+
run_events TEXT NOT NULL DEFAULT '[]',
|
|
19
|
+
alert_events TEXT NOT NULL DEFAULT '[]',
|
|
20
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
21
|
+
secret_sealed TEXT,
|
|
22
|
+
updated_at INTEGER NOT NULL,
|
|
23
|
+
PRIMARY KEY (workspace_id, id)
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
INSERT INTO notification_webhooks_new
|
|
27
|
+
(workspace_id, id, name, url, types, run_events, alert_events, enabled, secret_sealed, updated_at)
|
|
28
|
+
SELECT
|
|
29
|
+
workspace_id, 'default', 'Default', url, types, run_events, alert_events, enabled, secret_sealed, updated_at
|
|
30
|
+
FROM notification_webhooks;
|
|
31
|
+
|
|
32
|
+
DROP TABLE notification_webhooks;
|
|
33
|
+
|
|
34
|
+
ALTER TABLE notification_webhooks_new RENAME TO notification_webhooks;
|
|
35
|
+
|
|
36
|
+
-- No extra index on `workspace_id`: it is the leading column of the composite primary key, so the
|
|
37
|
+
-- per-workspace list every delivery reads is already served by that index.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.172.0",
|
|
4
4
|
"description": "Reusable Cloudflare Worker library (Hono + D1) exposing the Agent Architecture Board core: the `createApp()` Hono factory, the default fetch/scheduled/queue handler, and the Durable Object + Workflow classes. Deployments (see deploy/backend) supply the wrangler.toml + config and re-export these.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,22 +43,22 @@
|
|
|
43
43
|
"pino": "^10.3.1",
|
|
44
44
|
"valibot": "^1.4.2",
|
|
45
45
|
"workers-ai-provider": "^4.0.0",
|
|
46
|
-
"@cat-factory/agents": "0.117.
|
|
47
|
-
"@cat-factory/caching": "0.18.
|
|
48
|
-
"@cat-factory/
|
|
49
|
-
"@cat-factory/
|
|
50
|
-
"@cat-factory/
|
|
51
|
-
"@cat-factory/
|
|
52
|
-
"@cat-factory/
|
|
53
|
-
"@cat-factory/
|
|
54
|
-
"@cat-factory/
|
|
55
|
-
"@cat-factory/
|
|
56
|
-
"@cat-factory/
|
|
57
|
-
"@cat-factory/
|
|
58
|
-
"@cat-factory/
|
|
59
|
-
"@cat-factory/
|
|
60
|
-
"@cat-factory/
|
|
61
|
-
"@cat-factory/
|
|
46
|
+
"@cat-factory/agents": "0.117.6",
|
|
47
|
+
"@cat-factory/caching": "0.18.7",
|
|
48
|
+
"@cat-factory/eks": "0.1.264",
|
|
49
|
+
"@cat-factory/consensus": "0.14.52",
|
|
50
|
+
"@cat-factory/gitlab": "0.17.3",
|
|
51
|
+
"@cat-factory/contracts": "0.268.0",
|
|
52
|
+
"@cat-factory/gates": "0.9.32",
|
|
53
|
+
"@cat-factory/observability-langfuse": "0.10.36",
|
|
54
|
+
"@cat-factory/orchestration": "0.234.1",
|
|
55
|
+
"@cat-factory/prompt-fragments": "1.0.16",
|
|
56
|
+
"@cat-factory/provider-cloudflare": "0.7.414",
|
|
57
|
+
"@cat-factory/kernel": "0.266.0",
|
|
58
|
+
"@cat-factory/spend": "0.15.34",
|
|
59
|
+
"@cat-factory/integrations": "0.144.0",
|
|
60
|
+
"@cat-factory/observability-otel": "0.17.7",
|
|
61
|
+
"@cat-factory/server": "0.246.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@cloudflare/vitest-pool-workers": "^0.20.1",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"typescript": "7.0.2",
|
|
67
67
|
"vitest": "^4.1.10",
|
|
68
68
|
"wrangler": "^4.118.0",
|
|
69
|
-
"@cat-factory/conformance": "0.31.
|
|
69
|
+
"@cat-factory/conformance": "0.31.28"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "tsc -b tsconfig.build.json",
|