@catalystiq/envoy-sdk 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.
@@ -0,0 +1,104 @@
1
+ CREATE TABLE IF NOT EXISTS sdk_contacts (
2
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
3
+ namespace TEXT NOT NULL,
4
+ email TEXT NOT NULL,
5
+ data JSONB NOT NULL DEFAULT '{}'::jsonb,
6
+ resend_contact_id TEXT,
7
+ unsubscribed BOOLEAN NOT NULL DEFAULT FALSE,
8
+ dirty_since TIMESTAMPTZ,
9
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
10
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11
+ UNIQUE (namespace, email)
12
+ );
13
+
14
+ CREATE INDEX IF NOT EXISTS sdk_contacts_dirty_idx
15
+ ON sdk_contacts (namespace, dirty_since)
16
+ WHERE dirty_since IS NOT NULL;
17
+
18
+ CREATE TABLE IF NOT EXISTS sdk_topic_consent (
19
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
20
+ namespace TEXT NOT NULL,
21
+ contact TEXT NOT NULL,
22
+ topic_key TEXT NOT NULL,
23
+ topic_id TEXT,
24
+ digest_status TEXT NOT NULL DEFAULT 'opt_in',
25
+ alert_status TEXT NOT NULL DEFAULT 'opt_in',
26
+ resend_snapshot JSONB NOT NULL DEFAULT '{}'::jsonb,
27
+ dirty_since TIMESTAMPTZ,
28
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
29
+ UNIQUE (namespace, contact, topic_key)
30
+ );
31
+
32
+ CREATE INDEX IF NOT EXISTS sdk_topic_consent_dirty_idx
33
+ ON sdk_topic_consent (namespace, dirty_since)
34
+ WHERE dirty_since IS NOT NULL;
35
+
36
+ CREATE TABLE IF NOT EXISTS sdk_program_state (
37
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
38
+ namespace TEXT NOT NULL,
39
+ program_key TEXT NOT NULL,
40
+ subject_key TEXT NOT NULL,
41
+ watermark TEXT,
42
+ issue_seq BIGINT NOT NULL DEFAULT 0,
43
+ last_fired_at TIMESTAMPTZ,
44
+ paused BOOLEAN NOT NULL DEFAULT FALSE,
45
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
46
+ UNIQUE (namespace, program_key, subject_key)
47
+ );
48
+
49
+ CREATE TABLE IF NOT EXISTS sdk_broadcast_claims (
50
+ namespace TEXT NOT NULL,
51
+ broadcast_key TEXT NOT NULL,
52
+ resend_broadcast_id TEXT,
53
+ item_ids TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
54
+ sent_at TIMESTAMPTZ,
55
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
56
+ PRIMARY KEY (namespace, broadcast_key)
57
+ );
58
+
59
+ CREATE TABLE IF NOT EXISTS sdk_enrollments (
60
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
61
+ namespace TEXT NOT NULL,
62
+ contact TEXT NOT NULL,
63
+ sequence_key TEXT NOT NULL,
64
+ status TEXT NOT NULL DEFAULT 'active',
65
+ current_step INTEGER NOT NULL DEFAULT 0,
66
+ next_run_at TIMESTAMPTZ,
67
+ data JSONB NOT NULL DEFAULT '{}'::jsonb,
68
+ enrolled_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
69
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
70
+ UNIQUE (namespace, contact, sequence_key)
71
+ );
72
+
73
+ CREATE INDEX IF NOT EXISTS sdk_enrollments_due_idx
74
+ ON sdk_enrollments (namespace, status, next_run_at);
75
+
76
+ CREATE TABLE IF NOT EXISTS sdk_steps (
77
+ id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
78
+ namespace TEXT NOT NULL,
79
+ enrollment_id BIGINT NOT NULL REFERENCES sdk_enrollments (id) ON DELETE CASCADE,
80
+ step_index INTEGER NOT NULL,
81
+ status TEXT NOT NULL DEFAULT 'pending',
82
+ agent_session_id TEXT,
83
+ resend_email_id TEXT,
84
+ sent_at TIMESTAMPTZ,
85
+ attempts INTEGER NOT NULL DEFAULT 0,
86
+ last_error TEXT,
87
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
88
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
89
+ UNIQUE (namespace, enrollment_id, step_index)
90
+ );
91
+
92
+ CREATE INDEX IF NOT EXISTS sdk_steps_enrollment_idx
93
+ ON sdk_steps (namespace, enrollment_id);
94
+
95
+ -- Fixed-window rate-limit counters (U6 unsubscribe landing). Serverless-safe: in-memory
96
+ -- counters do not survive across function invocations, so the limiter is DB-backed. `key` is
97
+ -- already namespace-prefixed by the caller (the unsubscribe landing buckets per client IP).
98
+ CREATE TABLE IF NOT EXISTS sdk_rate_limits (
99
+ namespace TEXT NOT NULL,
100
+ key TEXT NOT NULL,
101
+ count INTEGER NOT NULL DEFAULT 0,
102
+ window_start TIMESTAMPTZ NOT NULL DEFAULT NOW(),
103
+ PRIMARY KEY (namespace, key)
104
+ );
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@catalystiq/envoy-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Headless Resend drip + broadcast email SDK for Next.js — bring-your-own-Postgres, host-owns-auth.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/getcatalystiq/envoy.git",
11
+ "directory": "packages/sdk"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./client": {
22
+ "types": "./dist/client/index.d.ts",
23
+ "import": "./dist/client/index.js"
24
+ },
25
+ "./migrations/*": "./migrations/*"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "migrations"
30
+ ],
31
+ "scripts": {
32
+ "build": "rm -rf dist && tsup",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
36
+ "prepublishOnly": "npm run typecheck && npm run build"
37
+ },
38
+ "dependencies": {
39
+ "@anthropic-ai/sdk": "^0.100.1",
40
+ "@modelcontextprotocol/sdk": "^1.27.1",
41
+ "mcp-handler": "^1.0.7",
42
+ "resend": "^6.14.0",
43
+ "server-only": "^0.0.1",
44
+ "svix": "^1.45.1",
45
+ "zod": "^4.4.3"
46
+ },
47
+ "peerDependencies": {
48
+ "next": ">=14",
49
+ "react": ">=18"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "next": { "optional": true },
53
+ "react": { "optional": true }
54
+ },
55
+ "devDependencies": {
56
+ "@types/react": "^19.2.17",
57
+ "@types/react-dom": "^19.2.3",
58
+ "react": "^19.2.7",
59
+ "react-dom": "^19.2.7",
60
+ "next": "^16.2.9",
61
+ "tsup": "^8.3.5",
62
+ "typescript": "6.0.3",
63
+ "vitest": "^4.1.7"
64
+ }
65
+ }