@amalgm/channels 0.1.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/PURPOSE.md +50 -0
- package/README.md +17 -0
- package/dist/host/auth.d.ts +10 -0
- package/dist/host/auth.js +76 -0
- package/dist/host/auth.js.map +1 -0
- package/dist/host/config.d.ts +10 -0
- package/dist/host/config.js +32 -0
- package/dist/host/config.js.map +1 -0
- package/dist/host/index.d.ts +6 -0
- package/dist/host/index.js +7 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/main.d.ts +1 -0
- package/dist/host/main.js +29 -0
- package/dist/host/main.js.map +1 -0
- package/dist/host/recipient.d.ts +7 -0
- package/dist/host/recipient.js +17 -0
- package/dist/host/recipient.js.map +1 -0
- package/dist/host/repository.d.ts +13 -0
- package/dist/host/repository.js +103 -0
- package/dist/host/repository.js.map +1 -0
- package/dist/host/resend.d.ts +10 -0
- package/dist/host/resend.js +33 -0
- package/dist/host/resend.js.map +1 -0
- package/dist/host/server.d.ts +12 -0
- package/dist/host/server.js +57 -0
- package/dist/host/server.js.map +1 -0
- package/dist/src/client.d.ts +7 -0
- package/dist/src/client.js +41 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/contract.d.ts +83 -0
- package/dist/src/contract.js +2 -0
- package/dist/src/contract.js.map +1 -0
- package/dist/src/errors.d.ts +5 -0
- package/dist/src/errors.js +9 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/http.d.ts +6 -0
- package/dist/src/http.js +64 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp.d.ts +3 -0
- package/dist/src/mcp.js +66 -0
- package/dist/src/mcp.js.map +1 -0
- package/dist/src/service.d.ts +2 -0
- package/dist/src/service.js +29 -0
- package/dist/src/service.js.map +1 -0
- package/dist/src/validation.d.ts +4 -0
- package/dist/src/validation.js +48 -0
- package/dist/src/validation.js.map +1 -0
- package/dist/src/worker.d.ts +15 -0
- package/dist/src/worker.js +46 -0
- package/dist/src/worker.js.map +1 -0
- package/package.json +40 -0
- package/skills/channels/SKILL.md +29 -0
- package/supabase/migrations/20260829000000_create_channels.sql +222 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
2
|
+
|
|
3
|
+
CREATE TABLE IF NOT EXISTS amalgm_channel_deliveries (
|
|
4
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
5
|
+
user_id uuid NOT NULL,
|
|
6
|
+
channel text NOT NULL CHECK (channel = 'email'),
|
|
7
|
+
title text,
|
|
8
|
+
message text NOT NULL CHECK (length(message) BETWEEN 1 AND 4000),
|
|
9
|
+
idempotency_key text NOT NULL CHECK (length(idempotency_key) BETWEEN 1 AND 200),
|
|
10
|
+
status text NOT NULL DEFAULT 'pending'
|
|
11
|
+
CHECK (status IN ('pending', 'sending', 'delivered', 'failed')),
|
|
12
|
+
attempts integer NOT NULL DEFAULT 0 CHECK (attempts BETWEEN 0 AND 5),
|
|
13
|
+
lease_token uuid,
|
|
14
|
+
lease_expires_at timestamptz,
|
|
15
|
+
provider_id text,
|
|
16
|
+
error text,
|
|
17
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
18
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
19
|
+
delivered_at timestamptz,
|
|
20
|
+
UNIQUE (user_id, idempotency_key),
|
|
21
|
+
CHECK (
|
|
22
|
+
(status = 'sending' AND lease_token IS NOT NULL AND lease_expires_at IS NOT NULL)
|
|
23
|
+
OR (status <> 'sending' AND lease_token IS NULL AND lease_expires_at IS NULL)
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS amalgm_channel_deliveries_history
|
|
28
|
+
ON amalgm_channel_deliveries(user_id, created_at DESC, id DESC);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS amalgm_channel_deliveries_work
|
|
30
|
+
ON amalgm_channel_deliveries(status, lease_expires_at, created_at, id)
|
|
31
|
+
WHERE status IN ('pending', 'sending');
|
|
32
|
+
|
|
33
|
+
ALTER TABLE amalgm_channel_deliveries ENABLE ROW LEVEL SECURITY;
|
|
34
|
+
REVOKE ALL ON amalgm_channel_deliveries FROM anon, authenticated;
|
|
35
|
+
GRANT SELECT, INSERT, UPDATE ON amalgm_channel_deliveries TO service_role;
|
|
36
|
+
|
|
37
|
+
CREATE OR REPLACE FUNCTION amalgm_channel_delivery_json(p_row amalgm_channel_deliveries)
|
|
38
|
+
RETURNS jsonb
|
|
39
|
+
LANGUAGE sql
|
|
40
|
+
IMMUTABLE
|
|
41
|
+
SET search_path = public
|
|
42
|
+
AS $$
|
|
43
|
+
SELECT jsonb_strip_nulls(jsonb_build_object(
|
|
44
|
+
'id', p_row.id,
|
|
45
|
+
'channel', p_row.channel,
|
|
46
|
+
'title', p_row.title,
|
|
47
|
+
'message', p_row.message,
|
|
48
|
+
'idempotency_key', p_row.idempotency_key,
|
|
49
|
+
'status', p_row.status,
|
|
50
|
+
'attempts', p_row.attempts,
|
|
51
|
+
'created_at', p_row.created_at,
|
|
52
|
+
'updated_at', p_row.updated_at,
|
|
53
|
+
'delivered_at', p_row.delivered_at,
|
|
54
|
+
'provider_id', p_row.provider_id,
|
|
55
|
+
'error', p_row.error
|
|
56
|
+
));
|
|
57
|
+
$$;
|
|
58
|
+
|
|
59
|
+
CREATE OR REPLACE FUNCTION enqueue_amalgm_channel_delivery(
|
|
60
|
+
p_user_id uuid,
|
|
61
|
+
p_delivery jsonb
|
|
62
|
+
) RETURNS jsonb
|
|
63
|
+
LANGUAGE plpgsql
|
|
64
|
+
SECURITY DEFINER
|
|
65
|
+
SET search_path = public
|
|
66
|
+
AS $$
|
|
67
|
+
DECLARE
|
|
68
|
+
v_row public.amalgm_channel_deliveries;
|
|
69
|
+
v_channel text := CASE WHEN p_delivery->>'channel' = 'auto' THEN 'email'
|
|
70
|
+
ELSE p_delivery->>'channel' END;
|
|
71
|
+
BEGIN
|
|
72
|
+
INSERT INTO public.amalgm_channel_deliveries (
|
|
73
|
+
user_id, channel, title, message, idempotency_key
|
|
74
|
+
) VALUES (
|
|
75
|
+
p_user_id,
|
|
76
|
+
v_channel,
|
|
77
|
+
NULLIF(p_delivery->>'title', ''),
|
|
78
|
+
p_delivery->>'message',
|
|
79
|
+
p_delivery->>'idempotencyKey'
|
|
80
|
+
)
|
|
81
|
+
ON CONFLICT (user_id, idempotency_key) DO NOTHING
|
|
82
|
+
RETURNING * INTO v_row;
|
|
83
|
+
|
|
84
|
+
IF NOT FOUND THEN
|
|
85
|
+
SELECT * INTO v_row
|
|
86
|
+
FROM public.amalgm_channel_deliveries
|
|
87
|
+
WHERE user_id = p_user_id
|
|
88
|
+
AND idempotency_key = p_delivery->>'idempotencyKey';
|
|
89
|
+
IF v_row.channel <> v_channel
|
|
90
|
+
OR v_row.title IS DISTINCT FROM NULLIF(p_delivery->>'title', '')
|
|
91
|
+
OR v_row.message <> p_delivery->>'message'
|
|
92
|
+
THEN
|
|
93
|
+
RAISE EXCEPTION 'idempotency key already names another delivery'
|
|
94
|
+
USING ERRCODE = '23505';
|
|
95
|
+
END IF;
|
|
96
|
+
END IF;
|
|
97
|
+
RETURN public.amalgm_channel_delivery_json(v_row);
|
|
98
|
+
END;
|
|
99
|
+
$$;
|
|
100
|
+
|
|
101
|
+
CREATE OR REPLACE FUNCTION list_amalgm_channel_deliveries(
|
|
102
|
+
p_user_id uuid,
|
|
103
|
+
p_status text,
|
|
104
|
+
p_limit integer,
|
|
105
|
+
p_offset integer
|
|
106
|
+
) RETURNS jsonb
|
|
107
|
+
LANGUAGE sql
|
|
108
|
+
SECURITY DEFINER
|
|
109
|
+
SET search_path = public
|
|
110
|
+
AS $$
|
|
111
|
+
WITH filtered AS (
|
|
112
|
+
SELECT * FROM public.amalgm_channel_deliveries
|
|
113
|
+
WHERE user_id = p_user_id AND (p_status IS NULL OR status = p_status)
|
|
114
|
+
), page_rows AS (
|
|
115
|
+
SELECT * FROM filtered
|
|
116
|
+
ORDER BY created_at DESC, id DESC
|
|
117
|
+
LIMIT p_limit OFFSET p_offset
|
|
118
|
+
)
|
|
119
|
+
SELECT jsonb_build_object(
|
|
120
|
+
'items', COALESCE(
|
|
121
|
+
(SELECT jsonb_agg(public.amalgm_channel_delivery_json(page_rows)) FROM page_rows),
|
|
122
|
+
'[]'::jsonb
|
|
123
|
+
),
|
|
124
|
+
'total', (SELECT count(*) FROM filtered)
|
|
125
|
+
);
|
|
126
|
+
$$;
|
|
127
|
+
|
|
128
|
+
CREATE OR REPLACE FUNCTION get_amalgm_channel_delivery(p_user_id uuid, p_delivery_id uuid)
|
|
129
|
+
RETURNS jsonb
|
|
130
|
+
LANGUAGE sql
|
|
131
|
+
SECURITY DEFINER
|
|
132
|
+
SET search_path = public
|
|
133
|
+
AS $$
|
|
134
|
+
SELECT public.amalgm_channel_delivery_json(d)
|
|
135
|
+
FROM public.amalgm_channel_deliveries d
|
|
136
|
+
WHERE d.user_id = p_user_id AND d.id = p_delivery_id;
|
|
137
|
+
$$;
|
|
138
|
+
|
|
139
|
+
CREATE OR REPLACE FUNCTION claim_amalgm_channel_deliveries(
|
|
140
|
+
p_limit integer,
|
|
141
|
+
p_lease_seconds integer
|
|
142
|
+
) RETURNS SETOF amalgm_channel_deliveries
|
|
143
|
+
LANGUAGE sql
|
|
144
|
+
SECURITY DEFINER
|
|
145
|
+
SET search_path = public
|
|
146
|
+
AS $$
|
|
147
|
+
WITH candidates AS (
|
|
148
|
+
SELECT id
|
|
149
|
+
FROM public.amalgm_channel_deliveries
|
|
150
|
+
WHERE attempts < 5
|
|
151
|
+
AND (
|
|
152
|
+
status = 'pending'
|
|
153
|
+
OR (status = 'sending' AND lease_expires_at <= now())
|
|
154
|
+
)
|
|
155
|
+
ORDER BY created_at, id
|
|
156
|
+
FOR UPDATE SKIP LOCKED
|
|
157
|
+
LIMIT LEAST(GREATEST(p_limit, 1), 100)
|
|
158
|
+
)
|
|
159
|
+
UPDATE public.amalgm_channel_deliveries d SET
|
|
160
|
+
status = 'sending',
|
|
161
|
+
attempts = attempts + 1,
|
|
162
|
+
lease_token = gen_random_uuid(),
|
|
163
|
+
lease_expires_at = now() + make_interval(secs => LEAST(GREATEST(p_lease_seconds, 10), 600)),
|
|
164
|
+
updated_at = now()
|
|
165
|
+
FROM candidates
|
|
166
|
+
WHERE d.id = candidates.id
|
|
167
|
+
RETURNING d.*;
|
|
168
|
+
$$;
|
|
169
|
+
|
|
170
|
+
CREATE OR REPLACE FUNCTION complete_amalgm_channel_delivery(
|
|
171
|
+
p_delivery_id uuid,
|
|
172
|
+
p_lease_token uuid,
|
|
173
|
+
p_provider_id text,
|
|
174
|
+
p_now timestamptz
|
|
175
|
+
) RETURNS boolean
|
|
176
|
+
LANGUAGE plpgsql
|
|
177
|
+
SECURITY DEFINER
|
|
178
|
+
SET search_path = public
|
|
179
|
+
AS $$
|
|
180
|
+
BEGIN
|
|
181
|
+
UPDATE public.amalgm_channel_deliveries SET
|
|
182
|
+
status = 'delivered', provider_id = p_provider_id, delivered_at = p_now,
|
|
183
|
+
error = NULL, lease_token = NULL, lease_expires_at = NULL, updated_at = p_now
|
|
184
|
+
WHERE id = p_delivery_id AND status = 'sending' AND lease_token = p_lease_token;
|
|
185
|
+
RETURN FOUND;
|
|
186
|
+
END;
|
|
187
|
+
$$;
|
|
188
|
+
|
|
189
|
+
CREATE OR REPLACE FUNCTION fail_amalgm_channel_delivery(
|
|
190
|
+
p_delivery_id uuid,
|
|
191
|
+
p_lease_token uuid,
|
|
192
|
+
p_error text,
|
|
193
|
+
p_now timestamptz
|
|
194
|
+
) RETURNS boolean
|
|
195
|
+
LANGUAGE plpgsql
|
|
196
|
+
SECURITY DEFINER
|
|
197
|
+
SET search_path = public
|
|
198
|
+
AS $$
|
|
199
|
+
BEGIN
|
|
200
|
+
UPDATE public.amalgm_channel_deliveries SET
|
|
201
|
+
status = CASE WHEN attempts >= 5 THEN 'failed' ELSE 'pending' END,
|
|
202
|
+
error = left(p_error, 500), lease_token = NULL, lease_expires_at = NULL,
|
|
203
|
+
updated_at = p_now
|
|
204
|
+
WHERE id = p_delivery_id AND status = 'sending' AND lease_token = p_lease_token;
|
|
205
|
+
RETURN FOUND;
|
|
206
|
+
END;
|
|
207
|
+
$$;
|
|
208
|
+
|
|
209
|
+
REVOKE ALL ON FUNCTION amalgm_channel_delivery_json(amalgm_channel_deliveries) FROM PUBLIC;
|
|
210
|
+
REVOKE ALL ON FUNCTION enqueue_amalgm_channel_delivery(uuid, jsonb) FROM PUBLIC;
|
|
211
|
+
REVOKE ALL ON FUNCTION list_amalgm_channel_deliveries(uuid, text, integer, integer) FROM PUBLIC;
|
|
212
|
+
REVOKE ALL ON FUNCTION get_amalgm_channel_delivery(uuid, uuid) FROM PUBLIC;
|
|
213
|
+
REVOKE ALL ON FUNCTION claim_amalgm_channel_deliveries(integer, integer) FROM PUBLIC;
|
|
214
|
+
REVOKE ALL ON FUNCTION complete_amalgm_channel_delivery(uuid, uuid, text, timestamptz) FROM PUBLIC;
|
|
215
|
+
REVOKE ALL ON FUNCTION fail_amalgm_channel_delivery(uuid, uuid, text, timestamptz) FROM PUBLIC;
|
|
216
|
+
|
|
217
|
+
GRANT EXECUTE ON FUNCTION enqueue_amalgm_channel_delivery(uuid, jsonb) TO service_role;
|
|
218
|
+
GRANT EXECUTE ON FUNCTION list_amalgm_channel_deliveries(uuid, text, integer, integer) TO service_role;
|
|
219
|
+
GRANT EXECUTE ON FUNCTION get_amalgm_channel_delivery(uuid, uuid) TO service_role;
|
|
220
|
+
GRANT EXECUTE ON FUNCTION claim_amalgm_channel_deliveries(integer, integer) TO service_role;
|
|
221
|
+
GRANT EXECUTE ON FUNCTION complete_amalgm_channel_delivery(uuid, uuid, text, timestamptz) TO service_role;
|
|
222
|
+
GRANT EXECUTE ON FUNCTION fail_amalgm_channel_delivery(uuid, uuid, text, timestamptz) TO service_role;
|