@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.
Files changed (56) hide show
  1. package/PURPOSE.md +50 -0
  2. package/README.md +17 -0
  3. package/dist/host/auth.d.ts +10 -0
  4. package/dist/host/auth.js +76 -0
  5. package/dist/host/auth.js.map +1 -0
  6. package/dist/host/config.d.ts +10 -0
  7. package/dist/host/config.js +32 -0
  8. package/dist/host/config.js.map +1 -0
  9. package/dist/host/index.d.ts +6 -0
  10. package/dist/host/index.js +7 -0
  11. package/dist/host/index.js.map +1 -0
  12. package/dist/host/main.d.ts +1 -0
  13. package/dist/host/main.js +29 -0
  14. package/dist/host/main.js.map +1 -0
  15. package/dist/host/recipient.d.ts +7 -0
  16. package/dist/host/recipient.js +17 -0
  17. package/dist/host/recipient.js.map +1 -0
  18. package/dist/host/repository.d.ts +13 -0
  19. package/dist/host/repository.js +103 -0
  20. package/dist/host/repository.js.map +1 -0
  21. package/dist/host/resend.d.ts +10 -0
  22. package/dist/host/resend.js +33 -0
  23. package/dist/host/resend.js.map +1 -0
  24. package/dist/host/server.d.ts +12 -0
  25. package/dist/host/server.js +57 -0
  26. package/dist/host/server.js.map +1 -0
  27. package/dist/src/client.d.ts +7 -0
  28. package/dist/src/client.js +41 -0
  29. package/dist/src/client.js.map +1 -0
  30. package/dist/src/contract.d.ts +83 -0
  31. package/dist/src/contract.js +2 -0
  32. package/dist/src/contract.js.map +1 -0
  33. package/dist/src/errors.d.ts +5 -0
  34. package/dist/src/errors.js +9 -0
  35. package/dist/src/errors.js.map +1 -0
  36. package/dist/src/http.d.ts +6 -0
  37. package/dist/src/http.js +64 -0
  38. package/dist/src/http.js.map +1 -0
  39. package/dist/src/index.d.ts +7 -0
  40. package/dist/src/index.js +8 -0
  41. package/dist/src/index.js.map +1 -0
  42. package/dist/src/mcp.d.ts +3 -0
  43. package/dist/src/mcp.js +66 -0
  44. package/dist/src/mcp.js.map +1 -0
  45. package/dist/src/service.d.ts +2 -0
  46. package/dist/src/service.js +29 -0
  47. package/dist/src/service.js.map +1 -0
  48. package/dist/src/validation.d.ts +4 -0
  49. package/dist/src/validation.js +48 -0
  50. package/dist/src/validation.js.map +1 -0
  51. package/dist/src/worker.d.ts +15 -0
  52. package/dist/src/worker.js +46 -0
  53. package/dist/src/worker.js.map +1 -0
  54. package/package.json +40 -0
  55. package/skills/channels/SKILL.md +29 -0
  56. 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;