@shware/workflow 2.0.1 → 3.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/README.md +106 -2
- package/dist/apns-relay.cjs +4 -17
- package/dist/apns-relay.cjs.map +1 -1
- package/dist/apns-relay.mjs +4 -17
- package/dist/apns-relay.mjs.map +1 -1
- package/dist/aws/engine-step.cjs +104 -0
- package/dist/aws/engine-step.cjs.map +1 -0
- package/dist/aws/engine-step.d.cts +28 -0
- package/dist/aws/engine-step.d.cts.map +1 -0
- package/dist/aws/engine-step.d.mts +28 -0
- package/dist/aws/engine-step.d.mts.map +1 -0
- package/dist/aws/engine-step.mjs +102 -0
- package/dist/aws/engine-step.mjs.map +1 -0
- package/dist/aws/function-url.cjs +62 -0
- package/dist/aws/function-url.cjs.map +1 -0
- package/dist/aws/function-url.d.cts +38 -0
- package/dist/aws/function-url.d.cts.map +1 -0
- package/dist/aws/function-url.d.mts +38 -0
- package/dist/aws/function-url.d.mts.map +1 -0
- package/dist/aws/function-url.mjs +59 -0
- package/dist/aws/function-url.mjs.map +1 -0
- package/dist/aws/index.cjs +160 -0
- package/dist/aws/index.cjs.map +1 -0
- package/dist/aws/index.d.cts +68 -0
- package/dist/aws/index.d.cts.map +1 -0
- package/dist/aws/index.d.mts +68 -0
- package/dist/aws/index.d.mts.map +1 -0
- package/dist/aws/index.mjs +148 -0
- package/dist/aws/index.mjs.map +1 -0
- package/dist/aws/instances.cjs +196 -0
- package/dist/aws/instances.cjs.map +1 -0
- package/dist/aws/instances.d.cts +94 -0
- package/dist/aws/instances.d.cts.map +1 -0
- package/dist/aws/instances.d.mts +94 -0
- package/dist/aws/instances.d.mts.map +1 -0
- package/dist/aws/instances.mjs +192 -0
- package/dist/aws/instances.mjs.map +1 -0
- package/dist/aws/lambda-api.cjs +89 -0
- package/dist/aws/lambda-api.cjs.map +1 -0
- package/dist/aws/lambda-api.d.cts +43 -0
- package/dist/aws/lambda-api.d.cts.map +1 -0
- package/dist/aws/lambda-api.d.mts +43 -0
- package/dist/aws/lambda-api.d.mts.map +1 -0
- package/dist/aws/lambda-api.mjs +88 -0
- package/dist/aws/lambda-api.mjs.map +1 -0
- package/dist/client.d.cts +1 -1
- package/dist/client.d.mts +1 -1
- package/dist/cloudflare/bindings.cjs +19 -0
- package/dist/cloudflare/bindings.cjs.map +1 -1
- package/dist/cloudflare/bindings.d.cts +39 -0
- package/dist/cloudflare/bindings.d.cts.map +1 -1
- package/dist/cloudflare/bindings.d.mts +39 -0
- package/dist/cloudflare/bindings.d.mts.map +1 -1
- package/dist/cloudflare/bindings.mjs +18 -1
- package/dist/cloudflare/bindings.mjs.map +1 -1
- package/dist/cloudflare/index.cjs +32 -28
- package/dist/cloudflare/index.d.cts +8 -6
- package/dist/cloudflare/index.d.mts +8 -6
- package/dist/cloudflare/index.mjs +12 -10
- package/dist/cloudflare/runner.cjs +27 -76
- package/dist/cloudflare/runner.cjs.map +1 -1
- package/dist/cloudflare/runner.d.cts +7 -2
- package/dist/cloudflare/runner.d.cts.map +1 -1
- package/dist/cloudflare/runner.d.mts +7 -2
- package/dist/cloudflare/runner.d.mts.map +1 -1
- package/dist/cloudflare/runner.mjs +26 -75
- package/dist/cloudflare/runner.mjs.map +1 -1
- package/dist/cloudflare/worker.cjs +22 -203
- package/dist/cloudflare/worker.cjs.map +1 -1
- package/dist/cloudflare/worker.d.cts +10 -56
- package/dist/cloudflare/worker.d.cts.map +1 -1
- package/dist/cloudflare/worker.d.mts +10 -56
- package/dist/cloudflare/worker.d.mts.map +1 -1
- package/dist/cloudflare/worker.mjs +20 -199
- package/dist/cloudflare/worker.mjs.map +1 -1
- package/dist/email-sender.cjs +62 -0
- package/dist/email-sender.cjs.map +1 -0
- package/dist/email-sender.d.cts +70 -0
- package/dist/email-sender.d.cts.map +1 -0
- package/dist/email-sender.d.mts +70 -0
- package/dist/email-sender.d.mts.map +1 -0
- package/dist/email-sender.mjs +60 -0
- package/dist/email-sender.mjs.map +1 -0
- package/dist/email.cjs +21 -0
- package/dist/email.cjs.map +1 -1
- package/dist/email.d.cts +6 -0
- package/dist/email.d.cts.map +1 -1
- package/dist/email.d.mts +6 -0
- package/dist/email.d.mts.map +1 -1
- package/dist/email.mjs +21 -1
- package/dist/email.mjs.map +1 -1
- package/dist/engine/index.cjs +1 -1
- package/dist/engine/index.mjs +1 -1
- package/dist/engine/interpreter.cjs +1 -1
- package/dist/engine/interpreter.cjs.map +1 -1
- package/dist/engine/interpreter.mjs +1 -1
- package/dist/engine/interpreter.mjs.map +1 -1
- package/dist/engine/ports.d.cts +6 -1
- package/dist/engine/ports.d.cts.map +1 -1
- package/dist/engine/ports.d.mts +6 -1
- package/dist/engine/ports.d.mts.map +1 -1
- package/dist/{cloudflare → host}/channels.cjs +13 -11
- package/dist/host/channels.cjs.map +1 -0
- package/dist/{cloudflare → host}/channels.d.cts +8 -3
- package/dist/host/channels.d.cts.map +1 -0
- package/dist/{cloudflare → host}/channels.d.mts +8 -3
- package/dist/host/channels.d.mts.map +1 -0
- package/dist/{cloudflare → host}/channels.mjs +9 -7
- package/dist/host/channels.mjs.map +1 -0
- package/dist/host/execute.cjs +81 -0
- package/dist/host/execute.cjs.map +1 -0
- package/dist/host/execute.d.cts +37 -0
- package/dist/host/execute.d.cts.map +1 -0
- package/dist/host/execute.d.mts +37 -0
- package/dist/host/execute.d.mts.map +1 -0
- package/dist/host/execute.mjs +80 -0
- package/dist/host/execute.mjs.map +1 -0
- package/dist/host/journey-host.cjs +214 -0
- package/dist/host/journey-host.cjs.map +1 -0
- package/dist/host/journey-host.d.cts +105 -0
- package/dist/host/journey-host.d.cts.map +1 -0
- package/dist/host/journey-host.d.mts +105 -0
- package/dist/host/journey-host.d.mts.map +1 -0
- package/dist/host/journey-host.mjs +210 -0
- package/dist/host/journey-host.mjs.map +1 -0
- package/dist/{cloudflare → host}/router.cjs +34 -13
- package/dist/host/router.cjs.map +1 -0
- package/dist/{cloudflare → host}/router.d.cts +2 -2
- package/dist/host/router.d.cts.map +1 -0
- package/dist/{cloudflare → host}/router.d.mts +2 -2
- package/dist/host/router.d.mts.map +1 -0
- package/dist/{cloudflare → host}/router.mjs +34 -13
- package/dist/host/router.mjs.map +1 -0
- package/dist/host/senders.cjs +63 -0
- package/dist/host/senders.cjs.map +1 -0
- package/dist/host/senders.d.cts +31 -0
- package/dist/host/senders.d.cts.map +1 -0
- package/dist/host/senders.d.mts +31 -0
- package/dist/host/senders.d.mts.map +1 -0
- package/dist/host/senders.mjs +59 -0
- package/dist/host/senders.mjs.map +1 -0
- package/dist/http2-client.cjs +90 -0
- package/dist/http2-client.cjs.map +1 -0
- package/dist/http2-client.d.cts +47 -0
- package/dist/http2-client.d.cts.map +1 -0
- package/dist/http2-client.d.mts +47 -0
- package/dist/http2-client.d.mts.map +1 -0
- package/dist/http2-client.mjs +88 -0
- package/dist/http2-client.mjs.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/react-email.cjs.map +1 -1
- package/dist/react-email.d.cts +1 -2
- package/dist/react-email.d.cts.map +1 -1
- package/dist/react-email.d.mts +1 -2
- package/dist/react-email.d.mts.map +1 -1
- package/dist/react-email.mjs.map +1 -1
- package/dist/store/d1.cjs +1 -0
- package/dist/store/d1.cjs.map +1 -1
- package/dist/store/d1.d.cts.map +1 -1
- package/dist/store/d1.d.mts.map +1 -1
- package/dist/store/d1.mjs +1 -0
- package/dist/store/d1.mjs.map +1 -1
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +74 -0
- package/dist/store/index.d.cts.map +1 -1
- package/dist/store/index.d.mts +74 -0
- package/dist/store/index.d.mts.map +1 -1
- package/dist/store/index.mjs.map +1 -1
- package/dist/store/postgres.cjs +177 -12
- package/dist/store/postgres.cjs.map +1 -1
- package/dist/store/postgres.d.cts +10 -1
- package/dist/store/postgres.d.cts.map +1 -1
- package/dist/store/postgres.d.mts +10 -1
- package/dist/store/postgres.d.mts.map +1 -1
- package/dist/store/postgres.mjs +177 -12
- package/dist/store/postgres.mjs.map +1 -1
- package/dist/store/tables.cjs +31 -2
- package/dist/store/tables.cjs.map +1 -1
- package/dist/store/tables.d.cts +1 -0
- package/dist/store/tables.d.cts.map +1 -1
- package/dist/store/tables.d.mts +1 -0
- package/dist/store/tables.d.mts.map +1 -1
- package/dist/store/tables.mjs +31 -2
- package/dist/store/tables.mjs.map +1 -1
- package/dist/transports/cloudflare-email.cjs +59 -0
- package/dist/transports/cloudflare-email.cjs.map +1 -0
- package/dist/transports/cloudflare-email.d.cts +38 -0
- package/dist/transports/cloudflare-email.d.cts.map +1 -0
- package/dist/transports/cloudflare-email.d.mts +38 -0
- package/dist/transports/cloudflare-email.d.mts.map +1 -0
- package/dist/transports/cloudflare-email.mjs +57 -0
- package/dist/transports/cloudflare-email.mjs.map +1 -0
- package/dist/transports/ses.cjs +91 -0
- package/dist/transports/ses.cjs.map +1 -0
- package/dist/transports/ses.d.cts +39 -0
- package/dist/transports/ses.d.cts.map +1 -0
- package/dist/transports/ses.d.mts +39 -0
- package/dist/transports/ses.d.mts.map +1 -0
- package/dist/transports/ses.mjs +89 -0
- package/dist/transports/ses.mjs.map +1 -0
- package/dist/transports/sigv4.cjs +83 -0
- package/dist/transports/sigv4.cjs.map +1 -0
- package/dist/transports/sigv4.d.cts +27 -0
- package/dist/transports/sigv4.d.cts.map +1 -0
- package/dist/transports/sigv4.d.mts +27 -0
- package/dist/transports/sigv4.d.mts.map +1 -0
- package/dist/transports/sigv4.mjs +82 -0
- package/dist/transports/sigv4.mjs.map +1 -0
- package/package.json +53 -1
- package/dist/cloudflare/channels.cjs.map +0 -1
- package/dist/cloudflare/channels.d.cts.map +0 -1
- package/dist/cloudflare/channels.d.mts.map +0 -1
- package/dist/cloudflare/channels.mjs.map +0 -1
- package/dist/cloudflare/router.cjs.map +0 -1
- package/dist/cloudflare/router.d.cts.map +0 -1
- package/dist/cloudflare/router.d.mts.map +0 -1
- package/dist/cloudflare/router.mjs.map +0 -1
- package/dist/cloudflare/senders.cjs +0 -121
- package/dist/cloudflare/senders.cjs.map +0 -1
- package/dist/cloudflare/senders.d.cts +0 -72
- package/dist/cloudflare/senders.d.cts.map +0 -1
- package/dist/cloudflare/senders.d.mts +0 -72
- package/dist/cloudflare/senders.d.mts.map +0 -1
- package/dist/cloudflare/senders.mjs +0 -115
- package/dist/cloudflare/senders.mjs.map +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let node_http2 = require("node:http2");
|
|
3
|
+
//#region src/http2-client.ts
|
|
4
|
+
function http2Sessions() {
|
|
5
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
6
|
+
return {
|
|
7
|
+
session(origin) {
|
|
8
|
+
const existing = sessions.get(origin);
|
|
9
|
+
if (existing !== void 0 && !existing.closed && !existing.destroyed) return existing;
|
|
10
|
+
const created = (0, node_http2.connect)(origin);
|
|
11
|
+
const forget = () => {
|
|
12
|
+
if (sessions.get(origin) === created) sessions.delete(origin);
|
|
13
|
+
};
|
|
14
|
+
created.on("error", forget);
|
|
15
|
+
created.on("close", forget);
|
|
16
|
+
created.on("goaway", forget);
|
|
17
|
+
sessions.set(origin, created);
|
|
18
|
+
return created;
|
|
19
|
+
},
|
|
20
|
+
discard(origin, session) {
|
|
21
|
+
session.destroy();
|
|
22
|
+
if (sessions.get(origin) === session) sessions.delete(origin);
|
|
23
|
+
},
|
|
24
|
+
close() {
|
|
25
|
+
for (const open of sessions.values()) open.close();
|
|
26
|
+
sessions.clear();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `fetch` over HTTP/2, for the senders that take one (`ApnsPushSender({ fetch })`). Only what a
|
|
32
|
+
* JSON API call needs: a method, string headers, a string or byte body, the status, headers and
|
|
33
|
+
* body back.
|
|
34
|
+
*/
|
|
35
|
+
function http2Fetch(pool = http2Sessions(), options = {}) {
|
|
36
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
37
|
+
return async (input, init) => {
|
|
38
|
+
const request = new Request(input, init);
|
|
39
|
+
const url = new URL(request.url);
|
|
40
|
+
const body = request.body === null ? void 0 : Buffer.from(await request.arrayBuffer());
|
|
41
|
+
const headers = {
|
|
42
|
+
":method": request.method,
|
|
43
|
+
":path": `${url.pathname}${url.search}`
|
|
44
|
+
};
|
|
45
|
+
request.headers.forEach((value, name) => {
|
|
46
|
+
headers[name] = value;
|
|
47
|
+
});
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
let session;
|
|
50
|
+
let stream;
|
|
51
|
+
try {
|
|
52
|
+
session = pool.session(url.origin);
|
|
53
|
+
stream = session.request(headers);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let status = 502;
|
|
59
|
+
const responseHeaders = new Headers();
|
|
60
|
+
const chunks = [];
|
|
61
|
+
stream.on("response", (received) => {
|
|
62
|
+
status = received[":status"] ?? 502;
|
|
63
|
+
for (const [name, value] of Object.entries(received)) {
|
|
64
|
+
if (name.startsWith(":") || value === void 0) continue;
|
|
65
|
+
responseHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
69
|
+
stream.on("end", () => {
|
|
70
|
+
const bytes = Buffer.concat(chunks);
|
|
71
|
+
resolve(new Response(status === 204 || status === 304 || bytes.length === 0 ? null : bytes, {
|
|
72
|
+
status,
|
|
73
|
+
headers: responseHeaders
|
|
74
|
+
}));
|
|
75
|
+
});
|
|
76
|
+
stream.on("error", reject);
|
|
77
|
+
stream.setTimeout(timeoutMs, () => {
|
|
78
|
+
stream.close();
|
|
79
|
+
pool.discard(url.origin, session);
|
|
80
|
+
reject(/* @__PURE__ */ new Error(`HTTP/2 request to ${url.origin} timed out after ${timeoutMs} ms`));
|
|
81
|
+
});
|
|
82
|
+
stream.end(body);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
exports.http2Fetch = http2Fetch;
|
|
88
|
+
exports.http2Sessions = http2Sessions;
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=http2-client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http2-client.cjs","names":["connect"],"sources":["../src/http2-client.ts"],"sourcesContent":["import { type ClientHttp2Session, connect } from 'node:http2';\n\n/**\n * A Node HTTP/2 client: one session per origin, reused while it lives. APNs speaks HTTP/2 only\n * and Node's `fetch` does not negotiate it, so a Node host (a Lambda, the development relay)\n * pushes through this instead. Node-only — a Worker's own `fetch` already speaks HTTP/2.\n *\n * Why it exists, and when the Lambda side can go (as of 2026-10):\n * - Node 24 (Lambda's `nodejs24.x`, the newest GA runtime) bundles undici 7, whose `fetch`\n * leaves HTTP/2 off (`allowH2: false`). A POST to APNs fails there with \"Response does not\n * match the HTTP/1.1 protocol\". The only switch is an undici `Agent({ allowH2: true })`\n * dispatcher, which needs the npm `undici` package (and, set globally, would change every\n * `fetch` in the host's process) — this client needs no dependency and touches nothing global.\n * - undici 8 negotiates HTTP/2 by default (nodejs/undici#4828), and Node 26 bundles it. Lambda's\n * `nodejs26.x` is in public preview only, outside the SLA; it goes GA once Node 26 reaches\n * Active LTS.\n * - Once the function runs on a GA runtime whose built-in `fetch` speaks HTTP/2 (check that\n * durable functions support it, and that a POST to api.push.apple.com answers JSON rather\n * than a protocol error), drop `apnsFetch: http2Fetch()` from `journeyLambda` (aws/index.ts):\n * APNs then uses the platform `fetch` like the other channels. The development relay\n * (`apns-relay.ts`, for workerd, which cannot make HTTP/2 subrequests) keeps using this pool.\n */\n\nexport interface Http2Sessions {\n session(origin: string): ClientHttp2Session;\n /**\n * Drop a session that stopped answering, so the next request reconnects. Only that session:\n * the pool may already hold a newer one for the origin (after a GOAWAY), which stays.\n */\n discard(origin: string, session: ClientHttp2Session): void;\n close(): void;\n}\n\nexport function http2Sessions(): Http2Sessions {\n const sessions = new Map<string, ClientHttp2Session>();\n return {\n session(origin) {\n const existing = sessions.get(origin);\n if (existing !== undefined && !existing.closed && !existing.destroyed) return existing;\n const created = connect(origin);\n const forget = () => {\n if (sessions.get(origin) === created) sessions.delete(origin);\n };\n created.on('error', forget);\n created.on('close', forget);\n created.on('goaway', forget);\n sessions.set(origin, created);\n return created;\n },\n discard(origin, session) {\n session.destroy();\n if (sessions.get(origin) === session) sessions.delete(origin);\n },\n close() {\n for (const open of sessions.values()) open.close();\n sessions.clear();\n },\n };\n}\n\n/**\n * `fetch` over HTTP/2, for the senders that take one (`ApnsPushSender({ fetch })`). Only what a\n * JSON API call needs: a method, string headers, a string or byte body, the status, headers and\n * body back.\n */\nexport function http2Fetch(\n pool: Http2Sessions = http2Sessions(),\n options: {\n /**\n * How long a request may go without activity: a session a frozen Lambda left half-dead\n * answers nothing and closes nothing, so without a limit the request hangs until the\n * function's timeout. 10 seconds by default.\n */\n timeoutMs?: number;\n } = {}\n): typeof fetch {\n const timeoutMs = options.timeoutMs ?? 10_000;\n return async (input: string | URL | Request, init?: RequestInit) => {\n const request = new Request(input, init);\n const url = new URL(request.url);\n const body = request.body === null ? undefined : Buffer.from(await request.arrayBuffer());\n const headers: Record<string, string> = {\n ':method': request.method,\n ':path': `${url.pathname}${url.search}`,\n };\n request.headers.forEach((value, name) => {\n headers[name] = value;\n });\n return new Promise<Response>((resolve, reject) => {\n let session: ClientHttp2Session;\n let stream;\n try {\n session = pool.session(url.origin);\n stream = session.request(headers);\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)));\n return;\n }\n let status = 502;\n const responseHeaders = new Headers();\n const chunks: Buffer[] = [];\n stream.on('response', (received) => {\n status = received[':status'] ?? 502;\n for (const [name, value] of Object.entries(received)) {\n if (name.startsWith(':') || value === undefined) continue;\n responseHeaders.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n });\n stream.on('data', (chunk: Buffer) => chunks.push(chunk));\n stream.on('end', () => {\n const bytes = Buffer.concat(chunks);\n resolve(\n new Response(status === 204 || status === 304 || bytes.length === 0 ? null : bytes, {\n status,\n headers: responseHeaders,\n })\n );\n });\n stream.on('error', reject);\n stream.setTimeout(timeoutMs, () => {\n stream.close();\n pool.discard(url.origin, session);\n reject(new Error(`HTTP/2 request to ${url.origin} timed out after ${timeoutMs} ms`));\n });\n stream.end(body);\n });\n };\n}\n"],"mappings":";;;AAiCA,SAAgB,gBAA+B;CAC7C,MAAM,2BAAW,IAAI,IAAgC;CACrD,OAAO;EACL,QAAQ,QAAQ;GACd,MAAM,WAAW,SAAS,IAAI,MAAM;GACpC,IAAI,aAAa,KAAA,KAAa,CAAC,SAAS,UAAU,CAAC,SAAS,WAAW,OAAO;GAC9E,MAAM,WAAA,GAAUA,WAAAA,QAAAA,CAAQ,MAAM;GAC9B,MAAM,eAAe;IACnB,IAAI,SAAS,IAAI,MAAM,MAAM,SAAS,SAAS,OAAO,MAAM;GAC9D;GACA,QAAQ,GAAG,SAAS,MAAM;GAC1B,QAAQ,GAAG,SAAS,MAAM;GAC1B,QAAQ,GAAG,UAAU,MAAM;GAC3B,SAAS,IAAI,QAAQ,OAAO;GAC5B,OAAO;EACT;EACA,QAAQ,QAAQ,SAAS;GACvB,QAAQ,QAAQ;GAChB,IAAI,SAAS,IAAI,MAAM,MAAM,SAAS,SAAS,OAAO,MAAM;EAC9D;EACA,QAAQ;GACN,KAAK,MAAM,QAAQ,SAAS,OAAO,GAAG,KAAK,MAAM;GACjD,SAAS,MAAM;EACjB;CACF;AACF;;;;;;AAOA,SAAgB,WACd,OAAsB,cAAc,GACpC,UAOI,CAAC,GACS;CACd,MAAM,YAAY,QAAQ,aAAa;CACvC,OAAO,OAAO,OAA+B,SAAuB;EAClE,MAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;EACvC,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;EAC/B,MAAM,OAAO,QAAQ,SAAS,OAAO,KAAA,IAAY,OAAO,KAAK,MAAM,QAAQ,YAAY,CAAC;EACxF,MAAM,UAAkC;GACtC,WAAW,QAAQ;GACnB,SAAS,GAAG,IAAI,WAAW,IAAI;EACjC;EACA,QAAQ,QAAQ,SAAS,OAAO,SAAS;GACvC,QAAQ,QAAQ;EAClB,CAAC;EACD,OAAO,IAAI,SAAmB,SAAS,WAAW;GAChD,IAAI;GACJ,IAAI;GACJ,IAAI;IACF,UAAU,KAAK,QAAQ,IAAI,MAAM;IACjC,SAAS,QAAQ,QAAQ,OAAO;GAClC,SAAS,OAAO;IACd,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;IAChE;GACF;GACA,IAAI,SAAS;GACb,MAAM,kBAAkB,IAAI,QAAQ;GACpC,MAAM,SAAmB,CAAC;GAC1B,OAAO,GAAG,aAAa,aAAa;IAClC,SAAS,SAAS,cAAc;IAChC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,GAAG;KACpD,IAAI,KAAK,WAAW,GAAG,KAAK,UAAU,KAAA,GAAW;KACjD,gBAAgB,IAAI,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK;IAC3E;GACF,CAAC;GACD,OAAO,GAAG,SAAS,UAAkB,OAAO,KAAK,KAAK,CAAC;GACvD,OAAO,GAAG,aAAa;IACrB,MAAM,QAAQ,OAAO,OAAO,MAAM;IAClC,QACE,IAAI,SAAS,WAAW,OAAO,WAAW,OAAO,MAAM,WAAW,IAAI,OAAO,OAAO;KAClF;KACA,SAAS;IACX,CAAC,CACH;GACF,CAAC;GACD,OAAO,GAAG,SAAS,MAAM;GACzB,OAAO,WAAW,iBAAiB;IACjC,OAAO,MAAM;IACb,KAAK,QAAQ,IAAI,QAAQ,OAAO;IAChC,uBAAO,IAAI,MAAM,qBAAqB,IAAI,OAAO,mBAAmB,UAAU,IAAI,CAAC;GACrF,CAAC;GACD,OAAO,IAAI,IAAI;EACjB,CAAC;CACH;AACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ClientHttp2Session } from "node:http2";
|
|
2
|
+
//#region src/http2-client.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A Node HTTP/2 client: one session per origin, reused while it lives. APNs speaks HTTP/2 only
|
|
5
|
+
* and Node's `fetch` does not negotiate it, so a Node host (a Lambda, the development relay)
|
|
6
|
+
* pushes through this instead. Node-only — a Worker's own `fetch` already speaks HTTP/2.
|
|
7
|
+
*
|
|
8
|
+
* Why it exists, and when the Lambda side can go (as of 2026-10):
|
|
9
|
+
* - Node 24 (Lambda's `nodejs24.x`, the newest GA runtime) bundles undici 7, whose `fetch`
|
|
10
|
+
* leaves HTTP/2 off (`allowH2: false`). A POST to APNs fails there with "Response does not
|
|
11
|
+
* match the HTTP/1.1 protocol". The only switch is an undici `Agent({ allowH2: true })`
|
|
12
|
+
* dispatcher, which needs the npm `undici` package (and, set globally, would change every
|
|
13
|
+
* `fetch` in the host's process) — this client needs no dependency and touches nothing global.
|
|
14
|
+
* - undici 8 negotiates HTTP/2 by default (nodejs/undici#4828), and Node 26 bundles it. Lambda's
|
|
15
|
+
* `nodejs26.x` is in public preview only, outside the SLA; it goes GA once Node 26 reaches
|
|
16
|
+
* Active LTS.
|
|
17
|
+
* - Once the function runs on a GA runtime whose built-in `fetch` speaks HTTP/2 (check that
|
|
18
|
+
* durable functions support it, and that a POST to api.push.apple.com answers JSON rather
|
|
19
|
+
* than a protocol error), drop `apnsFetch: http2Fetch()` from `journeyLambda` (aws/index.ts):
|
|
20
|
+
* APNs then uses the platform `fetch` like the other channels. The development relay
|
|
21
|
+
* (`apns-relay.ts`, for workerd, which cannot make HTTP/2 subrequests) keeps using this pool.
|
|
22
|
+
*/
|
|
23
|
+
export interface Http2Sessions {
|
|
24
|
+
session(origin: string): ClientHttp2Session;
|
|
25
|
+
/**
|
|
26
|
+
* Drop a session that stopped answering, so the next request reconnects. Only that session:
|
|
27
|
+
* the pool may already hold a newer one for the origin (after a GOAWAY), which stays.
|
|
28
|
+
*/
|
|
29
|
+
discard(origin: string, session: ClientHttp2Session): void;
|
|
30
|
+
close(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function http2Sessions(): Http2Sessions;
|
|
33
|
+
/**
|
|
34
|
+
* `fetch` over HTTP/2, for the senders that take one (`ApnsPushSender({ fetch })`). Only what a
|
|
35
|
+
* JSON API call needs: a method, string headers, a string or byte body, the status, headers and
|
|
36
|
+
* body back.
|
|
37
|
+
*/
|
|
38
|
+
export declare function http2Fetch(pool?: Http2Sessions, options?: {
|
|
39
|
+
/**
|
|
40
|
+
* How long a request may go without activity: a session a frozen Lambda left half-dead
|
|
41
|
+
* answers nothing and closes nothing, so without a limit the request hangs until the
|
|
42
|
+
* function's timeout. 10 seconds by default.
|
|
43
|
+
*/
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
}): typeof fetch;
|
|
46
|
+
//#endregion
|
|
47
|
+
//# sourceMappingURL=http2-client.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http2-client.d.cts","names":[],"sources":["../src/http2-client.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBAuBiB;EACf,QAAQ,iBAAiB;;;;;EAKzB,QAAQ,gBAAgB,SAAS;EACjC;;wBAGc,iBAAiB;;;;;;wBAgCjB,WACd,OAAM,eACN;;;;;;EAME;WAEM"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ClientHttp2Session } from "node:http2";
|
|
2
|
+
//#region src/http2-client.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A Node HTTP/2 client: one session per origin, reused while it lives. APNs speaks HTTP/2 only
|
|
5
|
+
* and Node's `fetch` does not negotiate it, so a Node host (a Lambda, the development relay)
|
|
6
|
+
* pushes through this instead. Node-only — a Worker's own `fetch` already speaks HTTP/2.
|
|
7
|
+
*
|
|
8
|
+
* Why it exists, and when the Lambda side can go (as of 2026-10):
|
|
9
|
+
* - Node 24 (Lambda's `nodejs24.x`, the newest GA runtime) bundles undici 7, whose `fetch`
|
|
10
|
+
* leaves HTTP/2 off (`allowH2: false`). A POST to APNs fails there with "Response does not
|
|
11
|
+
* match the HTTP/1.1 protocol". The only switch is an undici `Agent({ allowH2: true })`
|
|
12
|
+
* dispatcher, which needs the npm `undici` package (and, set globally, would change every
|
|
13
|
+
* `fetch` in the host's process) — this client needs no dependency and touches nothing global.
|
|
14
|
+
* - undici 8 negotiates HTTP/2 by default (nodejs/undici#4828), and Node 26 bundles it. Lambda's
|
|
15
|
+
* `nodejs26.x` is in public preview only, outside the SLA; it goes GA once Node 26 reaches
|
|
16
|
+
* Active LTS.
|
|
17
|
+
* - Once the function runs on a GA runtime whose built-in `fetch` speaks HTTP/2 (check that
|
|
18
|
+
* durable functions support it, and that a POST to api.push.apple.com answers JSON rather
|
|
19
|
+
* than a protocol error), drop `apnsFetch: http2Fetch()` from `journeyLambda` (aws/index.ts):
|
|
20
|
+
* APNs then uses the platform `fetch` like the other channels. The development relay
|
|
21
|
+
* (`apns-relay.ts`, for workerd, which cannot make HTTP/2 subrequests) keeps using this pool.
|
|
22
|
+
*/
|
|
23
|
+
export interface Http2Sessions {
|
|
24
|
+
session(origin: string): ClientHttp2Session;
|
|
25
|
+
/**
|
|
26
|
+
* Drop a session that stopped answering, so the next request reconnects. Only that session:
|
|
27
|
+
* the pool may already hold a newer one for the origin (after a GOAWAY), which stays.
|
|
28
|
+
*/
|
|
29
|
+
discard(origin: string, session: ClientHttp2Session): void;
|
|
30
|
+
close(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function http2Sessions(): Http2Sessions;
|
|
33
|
+
/**
|
|
34
|
+
* `fetch` over HTTP/2, for the senders that take one (`ApnsPushSender({ fetch })`). Only what a
|
|
35
|
+
* JSON API call needs: a method, string headers, a string or byte body, the status, headers and
|
|
36
|
+
* body back.
|
|
37
|
+
*/
|
|
38
|
+
export declare function http2Fetch(pool?: Http2Sessions, options?: {
|
|
39
|
+
/**
|
|
40
|
+
* How long a request may go without activity: a session a frozen Lambda left half-dead
|
|
41
|
+
* answers nothing and closes nothing, so without a limit the request hangs until the
|
|
42
|
+
* function's timeout. 10 seconds by default.
|
|
43
|
+
*/
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
}): typeof fetch;
|
|
46
|
+
//#endregion
|
|
47
|
+
//# sourceMappingURL=http2-client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http2-client.d.mts","names":[],"sources":["../src/http2-client.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBAuBiB;EACf,QAAQ,iBAAiB;;;;;EAKzB,QAAQ,gBAAgB,SAAS;EACjC;;wBAGc,iBAAiB;;;;;;wBAgCjB,WACd,OAAM,eACN;;;;;;EAME;WAEM"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { connect } from "node:http2";
|
|
2
|
+
//#region src/http2-client.ts
|
|
3
|
+
function http2Sessions() {
|
|
4
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
5
|
+
return {
|
|
6
|
+
session(origin) {
|
|
7
|
+
const existing = sessions.get(origin);
|
|
8
|
+
if (existing !== void 0 && !existing.closed && !existing.destroyed) return existing;
|
|
9
|
+
const created = connect(origin);
|
|
10
|
+
const forget = () => {
|
|
11
|
+
if (sessions.get(origin) === created) sessions.delete(origin);
|
|
12
|
+
};
|
|
13
|
+
created.on("error", forget);
|
|
14
|
+
created.on("close", forget);
|
|
15
|
+
created.on("goaway", forget);
|
|
16
|
+
sessions.set(origin, created);
|
|
17
|
+
return created;
|
|
18
|
+
},
|
|
19
|
+
discard(origin, session) {
|
|
20
|
+
session.destroy();
|
|
21
|
+
if (sessions.get(origin) === session) sessions.delete(origin);
|
|
22
|
+
},
|
|
23
|
+
close() {
|
|
24
|
+
for (const open of sessions.values()) open.close();
|
|
25
|
+
sessions.clear();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `fetch` over HTTP/2, for the senders that take one (`ApnsPushSender({ fetch })`). Only what a
|
|
31
|
+
* JSON API call needs: a method, string headers, a string or byte body, the status, headers and
|
|
32
|
+
* body back.
|
|
33
|
+
*/
|
|
34
|
+
function http2Fetch(pool = http2Sessions(), options = {}) {
|
|
35
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
36
|
+
return async (input, init) => {
|
|
37
|
+
const request = new Request(input, init);
|
|
38
|
+
const url = new URL(request.url);
|
|
39
|
+
const body = request.body === null ? void 0 : Buffer.from(await request.arrayBuffer());
|
|
40
|
+
const headers = {
|
|
41
|
+
":method": request.method,
|
|
42
|
+
":path": `${url.pathname}${url.search}`
|
|
43
|
+
};
|
|
44
|
+
request.headers.forEach((value, name) => {
|
|
45
|
+
headers[name] = value;
|
|
46
|
+
});
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
let session;
|
|
49
|
+
let stream;
|
|
50
|
+
try {
|
|
51
|
+
session = pool.session(url.origin);
|
|
52
|
+
stream = session.request(headers);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let status = 502;
|
|
58
|
+
const responseHeaders = new Headers();
|
|
59
|
+
const chunks = [];
|
|
60
|
+
stream.on("response", (received) => {
|
|
61
|
+
status = received[":status"] ?? 502;
|
|
62
|
+
for (const [name, value] of Object.entries(received)) {
|
|
63
|
+
if (name.startsWith(":") || value === void 0) continue;
|
|
64
|
+
responseHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
68
|
+
stream.on("end", () => {
|
|
69
|
+
const bytes = Buffer.concat(chunks);
|
|
70
|
+
resolve(new Response(status === 204 || status === 304 || bytes.length === 0 ? null : bytes, {
|
|
71
|
+
status,
|
|
72
|
+
headers: responseHeaders
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
stream.on("error", reject);
|
|
76
|
+
stream.setTimeout(timeoutMs, () => {
|
|
77
|
+
stream.close();
|
|
78
|
+
pool.discard(url.origin, session);
|
|
79
|
+
reject(/* @__PURE__ */ new Error(`HTTP/2 request to ${url.origin} timed out after ${timeoutMs} ms`));
|
|
80
|
+
});
|
|
81
|
+
stream.end(body);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { http2Fetch, http2Sessions };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=http2-client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http2-client.mjs","names":[],"sources":["../src/http2-client.ts"],"sourcesContent":["import { type ClientHttp2Session, connect } from 'node:http2';\n\n/**\n * A Node HTTP/2 client: one session per origin, reused while it lives. APNs speaks HTTP/2 only\n * and Node's `fetch` does not negotiate it, so a Node host (a Lambda, the development relay)\n * pushes through this instead. Node-only — a Worker's own `fetch` already speaks HTTP/2.\n *\n * Why it exists, and when the Lambda side can go (as of 2026-10):\n * - Node 24 (Lambda's `nodejs24.x`, the newest GA runtime) bundles undici 7, whose `fetch`\n * leaves HTTP/2 off (`allowH2: false`). A POST to APNs fails there with \"Response does not\n * match the HTTP/1.1 protocol\". The only switch is an undici `Agent({ allowH2: true })`\n * dispatcher, which needs the npm `undici` package (and, set globally, would change every\n * `fetch` in the host's process) — this client needs no dependency and touches nothing global.\n * - undici 8 negotiates HTTP/2 by default (nodejs/undici#4828), and Node 26 bundles it. Lambda's\n * `nodejs26.x` is in public preview only, outside the SLA; it goes GA once Node 26 reaches\n * Active LTS.\n * - Once the function runs on a GA runtime whose built-in `fetch` speaks HTTP/2 (check that\n * durable functions support it, and that a POST to api.push.apple.com answers JSON rather\n * than a protocol error), drop `apnsFetch: http2Fetch()` from `journeyLambda` (aws/index.ts):\n * APNs then uses the platform `fetch` like the other channels. The development relay\n * (`apns-relay.ts`, for workerd, which cannot make HTTP/2 subrequests) keeps using this pool.\n */\n\nexport interface Http2Sessions {\n session(origin: string): ClientHttp2Session;\n /**\n * Drop a session that stopped answering, so the next request reconnects. Only that session:\n * the pool may already hold a newer one for the origin (after a GOAWAY), which stays.\n */\n discard(origin: string, session: ClientHttp2Session): void;\n close(): void;\n}\n\nexport function http2Sessions(): Http2Sessions {\n const sessions = new Map<string, ClientHttp2Session>();\n return {\n session(origin) {\n const existing = sessions.get(origin);\n if (existing !== undefined && !existing.closed && !existing.destroyed) return existing;\n const created = connect(origin);\n const forget = () => {\n if (sessions.get(origin) === created) sessions.delete(origin);\n };\n created.on('error', forget);\n created.on('close', forget);\n created.on('goaway', forget);\n sessions.set(origin, created);\n return created;\n },\n discard(origin, session) {\n session.destroy();\n if (sessions.get(origin) === session) sessions.delete(origin);\n },\n close() {\n for (const open of sessions.values()) open.close();\n sessions.clear();\n },\n };\n}\n\n/**\n * `fetch` over HTTP/2, for the senders that take one (`ApnsPushSender({ fetch })`). Only what a\n * JSON API call needs: a method, string headers, a string or byte body, the status, headers and\n * body back.\n */\nexport function http2Fetch(\n pool: Http2Sessions = http2Sessions(),\n options: {\n /**\n * How long a request may go without activity: a session a frozen Lambda left half-dead\n * answers nothing and closes nothing, so without a limit the request hangs until the\n * function's timeout. 10 seconds by default.\n */\n timeoutMs?: number;\n } = {}\n): typeof fetch {\n const timeoutMs = options.timeoutMs ?? 10_000;\n return async (input: string | URL | Request, init?: RequestInit) => {\n const request = new Request(input, init);\n const url = new URL(request.url);\n const body = request.body === null ? undefined : Buffer.from(await request.arrayBuffer());\n const headers: Record<string, string> = {\n ':method': request.method,\n ':path': `${url.pathname}${url.search}`,\n };\n request.headers.forEach((value, name) => {\n headers[name] = value;\n });\n return new Promise<Response>((resolve, reject) => {\n let session: ClientHttp2Session;\n let stream;\n try {\n session = pool.session(url.origin);\n stream = session.request(headers);\n } catch (error) {\n reject(error instanceof Error ? error : new Error(String(error)));\n return;\n }\n let status = 502;\n const responseHeaders = new Headers();\n const chunks: Buffer[] = [];\n stream.on('response', (received) => {\n status = received[':status'] ?? 502;\n for (const [name, value] of Object.entries(received)) {\n if (name.startsWith(':') || value === undefined) continue;\n responseHeaders.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n });\n stream.on('data', (chunk: Buffer) => chunks.push(chunk));\n stream.on('end', () => {\n const bytes = Buffer.concat(chunks);\n resolve(\n new Response(status === 204 || status === 304 || bytes.length === 0 ? null : bytes, {\n status,\n headers: responseHeaders,\n })\n );\n });\n stream.on('error', reject);\n stream.setTimeout(timeoutMs, () => {\n stream.close();\n pool.discard(url.origin, session);\n reject(new Error(`HTTP/2 request to ${url.origin} timed out after ${timeoutMs} ms`));\n });\n stream.end(body);\n });\n };\n}\n"],"mappings":";;AAiCA,SAAgB,gBAA+B;CAC7C,MAAM,2BAAW,IAAI,IAAgC;CACrD,OAAO;EACL,QAAQ,QAAQ;GACd,MAAM,WAAW,SAAS,IAAI,MAAM;GACpC,IAAI,aAAa,KAAA,KAAa,CAAC,SAAS,UAAU,CAAC,SAAS,WAAW,OAAO;GAC9E,MAAM,UAAU,QAAQ,MAAM;GAC9B,MAAM,eAAe;IACnB,IAAI,SAAS,IAAI,MAAM,MAAM,SAAS,SAAS,OAAO,MAAM;GAC9D;GACA,QAAQ,GAAG,SAAS,MAAM;GAC1B,QAAQ,GAAG,SAAS,MAAM;GAC1B,QAAQ,GAAG,UAAU,MAAM;GAC3B,SAAS,IAAI,QAAQ,OAAO;GAC5B,OAAO;EACT;EACA,QAAQ,QAAQ,SAAS;GACvB,QAAQ,QAAQ;GAChB,IAAI,SAAS,IAAI,MAAM,MAAM,SAAS,SAAS,OAAO,MAAM;EAC9D;EACA,QAAQ;GACN,KAAK,MAAM,QAAQ,SAAS,OAAO,GAAG,KAAK,MAAM;GACjD,SAAS,MAAM;EACjB;CACF;AACF;;;;;;AAOA,SAAgB,WACd,OAAsB,cAAc,GACpC,UAOI,CAAC,GACS;CACd,MAAM,YAAY,QAAQ,aAAa;CACvC,OAAO,OAAO,OAA+B,SAAuB;EAClE,MAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;EACvC,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;EAC/B,MAAM,OAAO,QAAQ,SAAS,OAAO,KAAA,IAAY,OAAO,KAAK,MAAM,QAAQ,YAAY,CAAC;EACxF,MAAM,UAAkC;GACtC,WAAW,QAAQ;GACnB,SAAS,GAAG,IAAI,WAAW,IAAI;EACjC;EACA,QAAQ,QAAQ,SAAS,OAAO,SAAS;GACvC,QAAQ,QAAQ;EAClB,CAAC;EACD,OAAO,IAAI,SAAmB,SAAS,WAAW;GAChD,IAAI;GACJ,IAAI;GACJ,IAAI;IACF,UAAU,KAAK,QAAQ,IAAI,MAAM;IACjC,SAAS,QAAQ,QAAQ,OAAO;GAClC,SAAS,OAAO;IACd,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;IAChE;GACF;GACA,IAAI,SAAS;GACb,MAAM,kBAAkB,IAAI,QAAQ;GACpC,MAAM,SAAmB,CAAC;GAC1B,OAAO,GAAG,aAAa,aAAa;IAClC,SAAS,SAAS,cAAc;IAChC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,GAAG;KACpD,IAAI,KAAK,WAAW,GAAG,KAAK,UAAU,KAAA,GAAW;KACjD,gBAAgB,IAAI,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK;IAC3E;GACF,CAAC;GACD,OAAO,GAAG,SAAS,UAAkB,OAAO,KAAK,KAAK,CAAC;GACvD,OAAO,GAAG,aAAa;IACrB,MAAM,QAAQ,OAAO,OAAO,MAAM;IAClC,QACE,IAAI,SAAS,WAAW,OAAO,WAAW,OAAO,MAAM,WAAW,IAAI,OAAO,OAAO;KAClF;KACA,SAAS;IACX,CAAC,CACH;GACF,CAAC;GACD,OAAO,GAAG,SAAS,MAAM;GACzB,OAAO,WAAW,iBAAiB;IACjC,OAAO,MAAM;IACb,KAAK,QAAQ,IAAI,QAAQ,OAAO;IAChC,uBAAO,IAAI,MAAM,qBAAqB,IAAI,OAAO,mBAAmB,UAAU,IAAI,CAAC;GACrF,CAAC;GACD,OAAO,IAAI,IAAI;EACjB,CAAC;CACH;AACF"}
|
package/dist/index.cjs
CHANGED
|
@@ -80,6 +80,7 @@ exports.isUnsubscribeLink = require_email.isUnsubscribeLink;
|
|
|
80
80
|
exports.lookupTemplate = require_templates.lookupTemplate;
|
|
81
81
|
exports.lt = require_dsl_condition.lt;
|
|
82
82
|
exports.lte = require_dsl_condition.lte;
|
|
83
|
+
exports.mimeAddress = require_email.mimeAddress;
|
|
83
84
|
exports.ne = require_dsl_condition.ne;
|
|
84
85
|
exports.not = require_dsl_condition.not;
|
|
85
86
|
exports.notBetween = require_dsl_condition.notBetween;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.cjs";
|
|
2
|
-
import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail } from "./email.cjs";
|
|
2
|
+
import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, resolveEmail } from "./email.cjs";
|
|
3
3
|
import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.cjs";
|
|
4
4
|
import { ActionContext, ActionHandler, ActionRef, action } from "./dsl/action.cjs";
|
|
5
5
|
import { Duration, EventMap, TimeOfDay, UserPropertyBase, Weekday } from "./dsl/base.cjs";
|
|
@@ -16,4 +16,4 @@ import { JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, resolveRuntim
|
|
|
16
16
|
import { ApnsConfig, ConfigCompileError, DatabaseConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.cjs";
|
|
17
17
|
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.cjs";
|
|
18
18
|
import { BundlePlan, ChangeStatus, NodeChange, ResourceChange, WorkflowChange, isSemanticChange, plan } from "./plan.cjs";
|
|
19
|
-
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
|
19
|
+
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
|
|
2
|
-
import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail } from "./email.mjs";
|
|
2
|
+
import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, resolveEmail } from "./email.mjs";
|
|
3
3
|
import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
|
|
4
4
|
import { ActionContext, ActionHandler, ActionRef, action } from "./dsl/action.mjs";
|
|
5
5
|
import { Duration, EventMap, TimeOfDay, UserPropertyBase, Weekday } from "./dsl/base.mjs";
|
|
@@ -16,4 +16,4 @@ import { JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, resolveRuntim
|
|
|
16
16
|
import { ApnsConfig, ConfigCompileError, DatabaseConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.mjs";
|
|
17
17
|
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
|
|
18
18
|
import { BundlePlan, ChangeStatus, NodeChange, ResourceChange, WorkflowChange, isSemanticChange, plan } from "./plan.mjs";
|
|
19
|
-
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
|
19
|
+
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { action } from "./dsl/action.mjs";
|
|
|
3
3
|
import { event, user } from "./dsl/refs.mjs";
|
|
4
4
|
import { template } from "./dsl/template.mjs";
|
|
5
5
|
import { and, between, contains, eq, exists, gt, gte, inArray, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, performed, reachable, segment } from "./dsl/condition.mjs";
|
|
6
|
-
import { assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail } from "./email.mjs";
|
|
6
|
+
import { assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, resolveEmail } from "./email.mjs";
|
|
7
7
|
import { CHANNEL_OF, TemplateValidationError, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
|
|
8
8
|
import { flow } from "./dsl/flow.mjs";
|
|
9
9
|
import { trigger } from "./dsl/trigger.mjs";
|
|
@@ -15,4 +15,4 @@ import { ConfigCompileError, apnsEnvironment, compileConfig, defineConfig, isCon
|
|
|
15
15
|
import { ContractValidationError, checkEvent, defineContract, isContract } from "./contract.mjs";
|
|
16
16
|
import { isSemanticChange, plan } from "./plan.mjs";
|
|
17
17
|
import { resolveRuntime } from "./runtime.mjs";
|
|
18
|
-
export { ActionIR, BundleIR, CHANNEL_OF, ChannelIR, ConditionIR, ConfigCompileError, ContractValidationError, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TemplateValidationError, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
|
18
|
+
export { ActionIR, BundleIR, CHANNEL_OF, ChannelIR, ConditionIR, ConfigCompileError, ContractValidationError, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TemplateValidationError, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
package/dist/react-email.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-email.cjs","names":["indexTemplates","lookupTemplate","parseTemplateProps","resolveEmail","createElement","EmailProvider","renderToReadableStream"],"sources":["../src/react-email.ts"],"sourcesContent":["import { type ComponentType, type ReactElement, createElement } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport type
|
|
1
|
+
{"version":3,"file":"react-email.cjs","names":["indexTemplates","lookupTemplate","parseTemplateProps","resolveEmail","createElement","EmailProvider","renderToReadableStream"],"sources":["../src/react-email.ts"],"sourcesContent":["import { type ComponentType, type ReactElement, createElement } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport { type EmailConfig, resolveEmail } from './email';\nimport { EmailProvider } from './email-context';\nimport type { EmailRenderer } from './email-sender';\nimport {\n type EmailTemplate,\n indexTemplates,\n lookupTemplate,\n parseTemplateProps,\n} from './templates';\n\n/**\n * `@shware/workflow/react-email` — an EmailRenderer over the project's email\n * registry (`react-dom` is an optional peer dependency; import this subpath\n * only when you use it).\n *\n * The registry holds `emailTemplate({...})` definitions, looked up by their `id`. Props are\n * parsed against the template's schema before the component renders — a\n * failure throws TemplateValidationError and nothing renders — and the\n * component renders as an element inside an `EmailProvider` carrying the\n * template's resolved metadata (`resolveEmail` against the config's `email`),\n * so hooks and context work and a layout's `useEmailContext()` reads the\n * unsubscribe link the email is sent with; the render returns that metadata\n * with the subject and HTML, for the sender. Rendering\n * streams and waits for every Suspense boundary (`allReady`) — react-email's\n * `<Tailwind>` suspends while it compiles the classes, which synchronous\n * static markup cannot wait for — then prepends the XHTML doctype react-email\n * uses. @react-email/render itself is avoided because its Workers build\n * statically imports prettier and html-to-text for options never used here.\n */\n\nconst DOCTYPE =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\nexport function registryRenderer(\n emails: Readonly<Record<string, EmailTemplate>>,\n config?: EmailConfig\n): EmailRenderer {\n const index = indexTemplates('email', emails);\n return async (id, props) => {\n const template = lookupTemplate(index, 'email', id);\n const parsed = parseTemplateProps(template, props);\n const email = resolveEmail(template, config);\n const element = createElement(\n EmailProvider,\n { email },\n createElement(template.component as ComponentType<object>, parsed)\n );\n return { ...email, subject: template.subject, html: DOCTYPE + (await renderToHtml(element)) };\n };\n}\n\n/** Render to a string once every Suspense boundary has resolved; a rendering error rejects. */\nasync function renderToHtml(element: ReactElement): Promise<string> {\n let failure: unknown;\n const stream = await renderToReadableStream(element, {\n onError: (error) => {\n failure = error;\n },\n });\n await stream.allReady;\n const html = await new Response(stream).text();\n if (failure !== undefined) {\n throw failure instanceof Error ? failure : new Error('email render failed', { cause: failure });\n }\n // Streaming a document adds what static markup did not: an HTML5 doctype (the caller prepends\n // the XHTML one email clients expect) and React 19's image preload hints, which mean nothing in\n // an email. Both are dropped, as @react-email/render does.\n return html\n .replace(/^<!DOCTYPE html>/i, '')\n .replace(/<link rel=\"preload\" as=\"image\"[^>]*\\/>/g, '');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,UACJ;AAEF,SAAgB,iBACd,QACA,QACe;CACf,MAAM,QAAQA,kBAAAA,eAAe,SAAS,MAAM;CAC5C,OAAO,OAAO,IAAI,UAAU;EAC1B,MAAM,WAAWC,kBAAAA,eAAe,OAAO,SAAS,EAAE;EAClD,MAAM,SAASC,kBAAAA,mBAAmB,UAAU,KAAK;EACjD,MAAM,QAAQC,cAAAA,aAAa,UAAU,MAAM;EAC3C,MAAM,WAAA,GAAUC,MAAAA,cAAAA,CACdC,sBAAAA,eACA,EAAE,MAAM,IAAA,GACRD,MAAAA,cAAAA,CAAc,SAAS,WAAoC,MAAM,CACnE;EACA,OAAO;GAAE,GAAG;GAAO,SAAS,SAAS;GAAS,MAAM,UAAW,MAAM,aAAa,OAAO;EAAG;CAC9F;AACF;;AAGA,eAAe,aAAa,SAAwC;CAClE,IAAI;CACJ,MAAM,SAAS,OAAA,GAAME,iBAAAA,uBAAAA,CAAuB,SAAS,EACnD,UAAU,UAAU;EAClB,UAAU;CACZ,EACF,CAAC;CACD,MAAM,OAAO;CACb,MAAM,OAAO,MAAM,IAAI,SAAS,MAAM,CAAC,CAAC,KAAK;CAC7C,IAAI,YAAY,KAAA,GACd,MAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,uBAAuB,EAAE,OAAO,QAAQ,CAAC;CAKhG,OAAO,KACJ,QAAQ,qBAAqB,EAAE,CAAC,CAChC,QAAQ,2CAA2C,EAAE;AAC1D"}
|
package/dist/react-email.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EmailConfig } from "./email.cjs";
|
|
2
2
|
import { EmailTemplate } from "./templates.cjs";
|
|
3
|
-
import { EmailRenderer } from "./
|
|
4
|
-
import "./cloudflare/index.cjs";
|
|
3
|
+
import { EmailRenderer } from "./email-sender.cjs";
|
|
5
4
|
//#region src/react-email.d.ts
|
|
6
5
|
export declare function registryRenderer(emails: Readonly<Record<string, EmailTemplate>>, config?: EmailConfig): EmailRenderer;
|
|
7
6
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-email.d.cts","names":[],"sources":["../src/react-email.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"react-email.d.cts","names":[],"sources":["../src/react-email.ts"],"mappings":";;;;wBAmCgB,iBACd,QAAQ,SAAS,eAAe,iBAChC,SAAS,cACR"}
|
package/dist/react-email.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EmailConfig } from "./email.mjs";
|
|
2
2
|
import { EmailTemplate } from "./templates.mjs";
|
|
3
|
-
import { EmailRenderer } from "./
|
|
4
|
-
import "./cloudflare/index.mjs";
|
|
3
|
+
import { EmailRenderer } from "./email-sender.mjs";
|
|
5
4
|
//#region src/react-email.d.ts
|
|
6
5
|
export declare function registryRenderer(emails: Readonly<Record<string, EmailTemplate>>, config?: EmailConfig): EmailRenderer;
|
|
7
6
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-email.d.mts","names":[],"sources":["../src/react-email.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"react-email.d.mts","names":[],"sources":["../src/react-email.ts"],"mappings":";;;;wBAmCgB,iBACd,QAAQ,SAAS,eAAe,iBAChC,SAAS,cACR"}
|
package/dist/react-email.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-email.mjs","names":[],"sources":["../src/react-email.ts"],"sourcesContent":["import { type ComponentType, type ReactElement, createElement } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport type
|
|
1
|
+
{"version":3,"file":"react-email.mjs","names":[],"sources":["../src/react-email.ts"],"sourcesContent":["import { type ComponentType, type ReactElement, createElement } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport { type EmailConfig, resolveEmail } from './email';\nimport { EmailProvider } from './email-context';\nimport type { EmailRenderer } from './email-sender';\nimport {\n type EmailTemplate,\n indexTemplates,\n lookupTemplate,\n parseTemplateProps,\n} from './templates';\n\n/**\n * `@shware/workflow/react-email` — an EmailRenderer over the project's email\n * registry (`react-dom` is an optional peer dependency; import this subpath\n * only when you use it).\n *\n * The registry holds `emailTemplate({...})` definitions, looked up by their `id`. Props are\n * parsed against the template's schema before the component renders — a\n * failure throws TemplateValidationError and nothing renders — and the\n * component renders as an element inside an `EmailProvider` carrying the\n * template's resolved metadata (`resolveEmail` against the config's `email`),\n * so hooks and context work and a layout's `useEmailContext()` reads the\n * unsubscribe link the email is sent with; the render returns that metadata\n * with the subject and HTML, for the sender. Rendering\n * streams and waits for every Suspense boundary (`allReady`) — react-email's\n * `<Tailwind>` suspends while it compiles the classes, which synchronous\n * static markup cannot wait for — then prepends the XHTML doctype react-email\n * uses. @react-email/render itself is avoided because its Workers build\n * statically imports prettier and html-to-text for options never used here.\n */\n\nconst DOCTYPE =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\nexport function registryRenderer(\n emails: Readonly<Record<string, EmailTemplate>>,\n config?: EmailConfig\n): EmailRenderer {\n const index = indexTemplates('email', emails);\n return async (id, props) => {\n const template = lookupTemplate(index, 'email', id);\n const parsed = parseTemplateProps(template, props);\n const email = resolveEmail(template, config);\n const element = createElement(\n EmailProvider,\n { email },\n createElement(template.component as ComponentType<object>, parsed)\n );\n return { ...email, subject: template.subject, html: DOCTYPE + (await renderToHtml(element)) };\n };\n}\n\n/** Render to a string once every Suspense boundary has resolved; a rendering error rejects. */\nasync function renderToHtml(element: ReactElement): Promise<string> {\n let failure: unknown;\n const stream = await renderToReadableStream(element, {\n onError: (error) => {\n failure = error;\n },\n });\n await stream.allReady;\n const html = await new Response(stream).text();\n if (failure !== undefined) {\n throw failure instanceof Error ? failure : new Error('email render failed', { cause: failure });\n }\n // Streaming a document adds what static markup did not: an HTML5 doctype (the caller prepends\n // the XHTML one email clients expect) and React 19's image preload hints, which mean nothing in\n // an email. Both are dropped, as @react-email/render does.\n return html\n .replace(/^<!DOCTYPE html>/i, '')\n .replace(/<link rel=\"preload\" as=\"image\"[^>]*\\/>/g, '');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,UACJ;AAEF,SAAgB,iBACd,QACA,QACe;CACf,MAAM,QAAQ,eAAe,SAAS,MAAM;CAC5C,OAAO,OAAO,IAAI,UAAU;EAC1B,MAAM,WAAW,eAAe,OAAO,SAAS,EAAE;EAClD,MAAM,SAAS,mBAAmB,UAAU,KAAK;EACjD,MAAM,QAAQ,aAAa,UAAU,MAAM;EAC3C,MAAM,UAAU,cACd,eACA,EAAE,MAAM,GACR,cAAc,SAAS,WAAoC,MAAM,CACnE;EACA,OAAO;GAAE,GAAG;GAAO,SAAS,SAAS;GAAS,MAAM,UAAW,MAAM,aAAa,OAAO;EAAG;CAC9F;AACF;;AAGA,eAAe,aAAa,SAAwC;CAClE,IAAI;CACJ,MAAM,SAAS,MAAM,uBAAuB,SAAS,EACnD,UAAU,UAAU;EAClB,UAAU;CACZ,EACF,CAAC;CACD,MAAM,OAAO;CACb,MAAM,OAAO,MAAM,IAAI,SAAS,MAAM,CAAC,CAAC,KAAK;CAC7C,IAAI,YAAY,KAAA,GACd,MAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,uBAAuB,EAAE,OAAO,QAAQ,CAAC;CAKhG,OAAO,KACJ,QAAQ,qBAAqB,EAAE,CAAC,CAChC,QAAQ,2CAA2C,EAAE;AAC1D"}
|
package/dist/store/d1.cjs
CHANGED
|
@@ -155,6 +155,7 @@ var D1JourneyStore = class {
|
|
|
155
155
|
await this.db.batch([this.db.prepare("DELETE FROM segment_starts WHERE segment = ? AND user_id = ?").bind(segment, userId), this.db.prepare("DELETE FROM segment_members WHERE segment = ? AND user_id = ?").bind(segment, userId)]);
|
|
156
156
|
}
|
|
157
157
|
async enterJourney(entry) {
|
|
158
|
+
if (entry.execution !== void 0) throw new Error("D1JourneyStore keeps no execution starts: this runtime needs Postgres");
|
|
158
159
|
if (((await this.db.prepare("INSERT OR IGNORE INTO entries (workflow, user_id, instance_id, hash, status, ts) VALUES (?, ?, ?, ?, ?, ?)").bind(entry.workflow, entry.userId, entry.instanceId, entry.hash, "running", entry.ts).run()).meta?.changes ?? 1) !== 0) return "entered";
|
|
159
160
|
return ((await this.db.prepare("UPDATE entries SET instance_id = ?, hash = ?, status = 'running', ts = ? WHERE workflow = ? AND user_id = ? AND status = 'failed'").bind(entry.instanceId, entry.hash, entry.ts, entry.workflow, entry.userId).run()).meta?.changes ?? 0) > 0 ? "reclaimed" : null;
|
|
160
161
|
}
|
package/dist/store/d1.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"d1.cjs","names":["ConditionIR","WorkflowIR"],"sources":["../../src/store/d1.ts"],"sourcesContent":["import type { D1DatabaseLike, KVNamespaceLike } from '../cloudflare/bindings';\nimport type { PushDevice } from '../engine/ports';\nimport { type BundleIR, ConditionIR, WorkflowIR } from '../ir';\nimport type {\n DeviceInput,\n EntryInput,\n EntryOutcome,\n EventInput,\n EventWindow,\n JourneyStore,\n ProfileProps,\n SegmentTriggerRoute,\n TriggerRoute,\n} from './index';\n\n/**\n * The original data plane: D1 for the tables (see ../cloudflare/schema.sql)\n * and KV for the content-addressed workflow bodies (`wf:${contentHash}`).\n * The SQL below is exactly what the router and fact source used to inline —\n * the in-memory FakeD1 in the tests dispatches on these strings verbatim.\n */\nexport class D1JourneyStore implements JourneyStore {\n constructor(\n private readonly db: D1DatabaseLike,\n private readonly kv: KVNamespaceLike\n ) {}\n\n async insertEvent(input: EventInput): Promise<boolean> {\n const written = await this.db\n .prepare(\n 'INSERT OR IGNORE INTO events (id, user_id, name, ts, payload) VALUES (?, ?, ?, ?, ?)'\n )\n .bind(input.id, input.userId, input.event, input.ts, JSON.stringify(input.payload))\n .run();\n return (written.meta?.changes ?? 1) > 0;\n }\n\n /** The event rows of one user and name, within the window's bounds (both inclusive). */\n private eventWindow(userId: string, event: string, window?: EventWindow) {\n const params: unknown[] = [userId, event];\n let where = 'user_id = ? AND name = ?';\n if (window?.sinceMs !== undefined) {\n where += ' AND ts >= ?';\n params.push(window.sinceMs);\n }\n if (window?.untilMs !== undefined) {\n where += ' AND ts <= ?';\n params.push(window.untilMs);\n }\n return { where, params };\n }\n\n async countEvents(userId: string, event: string, window?: EventWindow): Promise<number> {\n const { where, params } = this.eventWindow(userId, event, window);\n const row = await this.db\n .prepare(`SELECT COUNT(*) AS c FROM events WHERE ${where}`)\n .bind(...params)\n .first<{ c: number }>();\n return row?.c ?? 0;\n }\n\n async listEventPayloads(\n userId: string,\n event: string,\n window?: EventWindow\n ): Promise<Record<string, unknown>[]> {\n const { where, params } = this.eventWindow(userId, event, window);\n const { results } = await this.db\n .prepare(`SELECT payload FROM events WHERE ${where}`)\n .bind(...params)\n .all<{ payload: string }>();\n return results.map((row) => JSON.parse(row.payload) as Record<string, unknown>);\n }\n\n async getProfile(userId: string): Promise<ProfileProps | undefined> {\n const row = await this.db\n .prepare('SELECT props FROM profiles WHERE user_id = ?')\n .bind(userId)\n .first<{ props: string }>();\n return row ? (JSON.parse(row.props) as ProfileProps) : undefined;\n }\n\n async mergeProfile(userId: string, props: ProfileProps): Promise<ProfileProps> {\n // SQLite's json_patch is RFC 7386 merge-patch: a null removes its key. The\n // merge happens in the statement, so two concurrent identifies cannot lose\n // each other's fields the way a read-modify-write did.\n const patch = JSON.stringify(props);\n const row = await this.db\n .prepare(\n \"INSERT INTO profiles (user_id, props) VALUES (?, json_patch('{}', ?)) ON CONFLICT(user_id) DO UPDATE SET props = json_patch(profiles.props, ?) RETURNING props\"\n )\n .bind(userId, patch, patch)\n .first<{ props: string }>();\n // RETURNING always yields the row it just wrote; the fallback only covers a\n // driver that declines to return one, and reports what was asked for.\n return row === null ? stripNulls(props) : (JSON.parse(row.props) as ProfileProps);\n }\n\n async listUserIds(): Promise<string[]> {\n const { results } = await this.db\n .prepare('SELECT user_id FROM profiles UNION SELECT DISTINCT user_id FROM events')\n .bind()\n .all<{ user_id: string }>();\n return results.map((row) => row.user_id);\n }\n\n async listDevices(userId: string): Promise<PushDevice[]> {\n const { results } = await this.db\n .prepare('SELECT token, platform FROM devices WHERE user_id = ? ORDER BY ts DESC')\n .bind(userId)\n .all<PushDevice>();\n return results;\n }\n\n async upsertDevice(device: DeviceInput): Promise<void> {\n await this.db\n .prepare(\n 'INSERT INTO devices (token, user_id, platform, ts) VALUES (?, ?, ?, ?) ON CONFLICT (token) DO UPDATE SET user_id = excluded.user_id, platform = excluded.platform, ts = excluded.ts'\n )\n .bind(device.token, device.userId, device.platform, device.ts)\n .run();\n }\n\n async requestErasure(userId: string, at: number): Promise<void> {\n await this.db\n .prepare('INSERT OR IGNORE INTO erasures (user_id, ts) VALUES (?, ?)')\n .bind(userId, at)\n .run();\n }\n\n async pendingErasures(): Promise<string[]> {\n const { results } = await this.db\n .prepare('SELECT user_id FROM erasures ORDER BY ts')\n .bind()\n .all<{ user_id: string }>();\n return results.map((row) => row.user_id);\n }\n\n async runningInstances(userId: string): Promise<string[]> {\n const { results } = await this.db\n .prepare(\"SELECT instance_id FROM entries WHERE user_id = ? AND status = 'running'\")\n .bind(userId)\n .all<{ instance_id: string }>();\n return results.map((row) => row.instance_id);\n }\n\n async forgetUser(userId: string): Promise<void> {\n // One batch: D1 runs it as a transaction; the request goes last, with the data it erases\n await this.db.batch(\n [\n 'entries',\n 'subscriptions',\n 'segment_starts',\n 'segment_members',\n 'devices',\n 'events',\n 'profiles',\n 'erasures',\n ].map((table) => this.db.prepare(`DELETE FROM ${table} WHERE user_id = ?`).bind(userId))\n );\n }\n\n async removeDevice(token: string): Promise<string | undefined> {\n const row = await this.db\n .prepare('DELETE FROM devices WHERE token = ? RETURNING user_id')\n .bind(token)\n .first<{ user_id: string | null }>();\n return row?.user_id ?? undefined;\n }\n\n async unbindDevice(token: string): Promise<string | undefined> {\n const row = await this.db\n .prepare('SELECT user_id FROM devices WHERE token = ?')\n .bind(token)\n .first<{ user_id: string | null }>();\n await this.db.prepare('UPDATE devices SET user_id = NULL WHERE token = ?').bind(token).run();\n return row?.user_id ?? undefined;\n }\n\n async listSegments(): Promise<{ id: string; condition: ConditionIR }[]> {\n const { results } = await this.db\n .prepare('SELECT id, condition FROM segments ORDER BY id')\n .bind()\n .all<{ id: string; condition: string }>();\n return results.map((row) => ({\n id: row.id,\n condition: ConditionIR.parse(JSON.parse(row.condition)),\n }));\n }\n\n async getSegmentCondition(id: string): Promise<ConditionIR | undefined> {\n const row = await this.db\n .prepare('SELECT condition FROM segments WHERE id = ?')\n .bind(id)\n .first<{ condition: string }>();\n return row ? ConditionIR.parse(JSON.parse(row.condition)) : undefined;\n }\n\n async findTriggers(event: string): Promise<TriggerRoute[]> {\n const { results } = await this.db\n .prepare(\n 'SELECT workflow, hash, \"where\" AS whereClause, filter FROM triggers WHERE event = ?'\n )\n .bind(event)\n .all<{ workflow: string; hash: string; whereClause: string | null; filter: string | null }>();\n return results.map((row) => ({\n workflow: row.workflow,\n hash: row.hash,\n where: row.whereClause === null ? null : ConditionIR.parse(JSON.parse(row.whereClause)),\n filter: row.filter === null ? null : ConditionIR.parse(JSON.parse(row.filter)),\n }));\n }\n\n async listSegmentTriggers(): Promise<SegmentTriggerRoute[]> {\n const { results } = await this.db\n .prepare('SELECT workflow, hash, segment FROM segment_triggers')\n .all<SegmentTriggerRoute>();\n return results;\n }\n\n async putWorkflowIR(workflow: WorkflowIR): Promise<void> {\n await this.kv.put(`wf:${workflow.contentHash}`, JSON.stringify(workflow));\n }\n\n async getWorkflowIR(contentHash: string): Promise<WorkflowIR | undefined> {\n const raw = await this.kv.get(`wf:${contentHash}`);\n return raw === null ? undefined : WorkflowIR.parse(JSON.parse(raw));\n }\n\n async replaceRoutes(bundle: BundleIR): Promise<void> {\n // One atomic batch: all-or-nothing, and a concurrent ingest never observes\n // the half-empty routing table a delete-then-insert loop would expose.\n await this.db.batch([\n this.db.prepare('DELETE FROM triggers'),\n this.db.prepare('DELETE FROM segment_triggers'),\n this.db.prepare('DELETE FROM segments'),\n ...bundle.segments.map((segment) =>\n this.db\n .prepare('INSERT INTO segments (id, condition, hash) VALUES (?, ?, ?)')\n .bind(segment.id, JSON.stringify(segment.condition), segment.contentHash)\n ),\n ...bundle.workflows.flatMap((workflow) => {\n if (workflow.trigger.type === 'event') {\n return [\n this.db\n .prepare(\n 'INSERT INTO triggers (workflow, hash, event, \"where\", filter) VALUES (?, ?, ?, ?, ?)'\n )\n .bind(\n workflow.id,\n workflow.contentHash,\n workflow.trigger.event,\n workflow.trigger.where !== undefined\n ? JSON.stringify(workflow.trigger.where)\n : null,\n workflow.trigger.filter !== undefined\n ? JSON.stringify(workflow.trigger.filter)\n : null\n ),\n ];\n }\n if (workflow.trigger.type === 'segment') {\n return [\n this.db\n .prepare('INSERT INTO segment_triggers (workflow, hash, segment) VALUES (?, ?, ?)')\n .bind(workflow.id, workflow.contentHash, workflow.trigger.segment),\n ];\n }\n return [];\n }),\n ]);\n }\n\n async isSegmentMember(segment: string, userId: string): Promise<boolean> {\n const row = await this.db\n .prepare('SELECT 1 AS x FROM segment_members WHERE segment = ? AND user_id = ?')\n .bind(segment, userId)\n .first<{ x: number }>();\n return row !== null;\n }\n\n async addSegmentMember(\n segment: string,\n userId: string,\n ts: number,\n workflows: readonly string[]\n ): Promise<void> {\n await this.db.batch([\n this.db\n .prepare('INSERT OR IGNORE INTO segment_members (segment, user_id, ts) VALUES (?, ?, ?)')\n .bind(segment, userId, ts),\n ...workflows.map((workflow) =>\n this.db\n .prepare(\n 'INSERT OR IGNORE INTO segment_starts (segment, workflow, user_id, ts) VALUES (?, ?, ?, ?)'\n )\n .bind(segment, workflow, userId, ts)\n ),\n ]);\n }\n\n async pendingSegmentStarts(segment: string, userId: string): Promise<string[]> {\n const { results } = await this.db\n .prepare('SELECT workflow FROM segment_starts WHERE segment = ? AND user_id = ?')\n .bind(segment, userId)\n .all<{ workflow: string }>();\n return results.map((row) => row.workflow);\n }\n\n async completeSegmentStart(segment: string, userId: string, workflow: string): Promise<void> {\n await this.db\n .prepare('DELETE FROM segment_starts WHERE segment = ? AND user_id = ? AND workflow = ?')\n .bind(segment, userId, workflow)\n .run();\n }\n\n async removeSegmentMember(segment: string, userId: string): Promise<void> {\n await this.db.batch([\n this.db\n .prepare('DELETE FROM segment_starts WHERE segment = ? AND user_id = ?')\n .bind(segment, userId),\n this.db\n .prepare('DELETE FROM segment_members WHERE segment = ? AND user_id = ?')\n .bind(segment, userId),\n ]);\n }\n\n async enterJourney(entry: EntryInput): Promise<EntryOutcome> {\n const inserted = await this.db\n .prepare(\n 'INSERT OR IGNORE INTO entries (workflow, user_id, instance_id, hash, status, ts) VALUES (?, ?, ?, ?, ?, ?)'\n )\n .bind(entry.workflow, entry.userId, entry.instanceId, entry.hash, 'running', entry.ts)\n .run();\n if ((inserted.meta?.changes ?? 1) !== 0) return 'entered';\n\n // A row already there blocks the entry, unless it records an instance that\n // died: 'failed' is reclaimed rather than barring the user forever over one\n // outage. The status test lives inside the UPDATE, so of two concurrent\n // retries exactly one sees a change.\n const claimed = await this.db\n .prepare(\n \"UPDATE entries SET instance_id = ?, hash = ?, status = 'running', ts = ? WHERE workflow = ? AND user_id = ? AND status = 'failed'\"\n )\n .bind(entry.instanceId, entry.hash, entry.ts, entry.workflow, entry.userId)\n .run();\n return (claimed.meta?.changes ?? 0) > 0 ? 'reclaimed' : null;\n }\n\n async removeEntry(instanceId: string): Promise<void> {\n await this.db.prepare('DELETE FROM entries WHERE instance_id = ?').bind(instanceId).run();\n }\n\n async setEntryStatus(instanceId: string, status: string): Promise<void> {\n await this.db\n .prepare('UPDATE entries SET status = ? WHERE instance_id = ?')\n .bind(status, instanceId)\n .run();\n }\n\n async findWakeHandles(userId: string, event: string): Promise<string[]> {\n const { results } = await this.db\n .prepare(\n 'SELECT DISTINCT wake_handle AS handle FROM subscriptions WHERE user_id = ? AND event = ?'\n )\n .bind(userId, event)\n .all<{ handle: string }>();\n return results.map((row) => row.handle);\n }\n\n async subscribe(\n handle: string,\n userId: string,\n events: readonly string[],\n ts: number\n ): Promise<void> {\n for (const event of events) {\n // OR IGNORE on the (wake_handle, event) PK: the interpreter re-subscribes on\n // every wait attempt (one-shot-callback contract) and rows persist here.\n await this.db\n .prepare(\n 'INSERT OR IGNORE INTO subscriptions (user_id, event, wake_handle, ts) VALUES (?, ?, ?, ?)'\n )\n .bind(userId, event, handle, ts)\n .run();\n }\n }\n\n async unsubscribe(handle: string): Promise<void> {\n await this.db.prepare('DELETE FROM subscriptions WHERE wake_handle = ?').bind(handle).run();\n }\n}\n\n/** What a merge-patch of `props` into an empty profile yields: the patch minus its nulls. */\nfunction stripNulls(props: ProfileProps): ProfileProps {\n return Object.fromEntries(Object.entries(props).filter(([, value]) => value !== null));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"d1.cjs","names":["ConditionIR","WorkflowIR"],"sources":["../../src/store/d1.ts"],"sourcesContent":["import type { D1DatabaseLike, KVNamespaceLike } from '../cloudflare/bindings';\nimport type { PushDevice } from '../engine/ports';\nimport { type BundleIR, ConditionIR, WorkflowIR } from '../ir';\nimport type {\n DeviceInput,\n EntryInput,\n EntryOutcome,\n EventInput,\n EventWindow,\n JourneyStore,\n ProfileProps,\n SegmentTriggerRoute,\n TriggerRoute,\n} from './index';\n\n/**\n * The original data plane: D1 for the tables (see ../cloudflare/schema.sql)\n * and KV for the content-addressed workflow bodies (`wf:${contentHash}`).\n * The SQL below is exactly what the router and fact source used to inline —\n * the in-memory FakeD1 in the tests dispatches on these strings verbatim.\n */\nexport class D1JourneyStore implements JourneyStore {\n constructor(\n private readonly db: D1DatabaseLike,\n private readonly kv: KVNamespaceLike\n ) {}\n\n async insertEvent(input: EventInput): Promise<boolean> {\n const written = await this.db\n .prepare(\n 'INSERT OR IGNORE INTO events (id, user_id, name, ts, payload) VALUES (?, ?, ?, ?, ?)'\n )\n .bind(input.id, input.userId, input.event, input.ts, JSON.stringify(input.payload))\n .run();\n return (written.meta?.changes ?? 1) > 0;\n }\n\n /** The event rows of one user and name, within the window's bounds (both inclusive). */\n private eventWindow(userId: string, event: string, window?: EventWindow) {\n const params: unknown[] = [userId, event];\n let where = 'user_id = ? AND name = ?';\n if (window?.sinceMs !== undefined) {\n where += ' AND ts >= ?';\n params.push(window.sinceMs);\n }\n if (window?.untilMs !== undefined) {\n where += ' AND ts <= ?';\n params.push(window.untilMs);\n }\n return { where, params };\n }\n\n async countEvents(userId: string, event: string, window?: EventWindow): Promise<number> {\n const { where, params } = this.eventWindow(userId, event, window);\n const row = await this.db\n .prepare(`SELECT COUNT(*) AS c FROM events WHERE ${where}`)\n .bind(...params)\n .first<{ c: number }>();\n return row?.c ?? 0;\n }\n\n async listEventPayloads(\n userId: string,\n event: string,\n window?: EventWindow\n ): Promise<Record<string, unknown>[]> {\n const { where, params } = this.eventWindow(userId, event, window);\n const { results } = await this.db\n .prepare(`SELECT payload FROM events WHERE ${where}`)\n .bind(...params)\n .all<{ payload: string }>();\n return results.map((row) => JSON.parse(row.payload) as Record<string, unknown>);\n }\n\n async getProfile(userId: string): Promise<ProfileProps | undefined> {\n const row = await this.db\n .prepare('SELECT props FROM profiles WHERE user_id = ?')\n .bind(userId)\n .first<{ props: string }>();\n return row ? (JSON.parse(row.props) as ProfileProps) : undefined;\n }\n\n async mergeProfile(userId: string, props: ProfileProps): Promise<ProfileProps> {\n // SQLite's json_patch is RFC 7386 merge-patch: a null removes its key. The\n // merge happens in the statement, so two concurrent identifies cannot lose\n // each other's fields the way a read-modify-write did.\n const patch = JSON.stringify(props);\n const row = await this.db\n .prepare(\n \"INSERT INTO profiles (user_id, props) VALUES (?, json_patch('{}', ?)) ON CONFLICT(user_id) DO UPDATE SET props = json_patch(profiles.props, ?) RETURNING props\"\n )\n .bind(userId, patch, patch)\n .first<{ props: string }>();\n // RETURNING always yields the row it just wrote; the fallback only covers a\n // driver that declines to return one, and reports what was asked for.\n return row === null ? stripNulls(props) : (JSON.parse(row.props) as ProfileProps);\n }\n\n async listUserIds(): Promise<string[]> {\n const { results } = await this.db\n .prepare('SELECT user_id FROM profiles UNION SELECT DISTINCT user_id FROM events')\n .bind()\n .all<{ user_id: string }>();\n return results.map((row) => row.user_id);\n }\n\n async listDevices(userId: string): Promise<PushDevice[]> {\n const { results } = await this.db\n .prepare('SELECT token, platform FROM devices WHERE user_id = ? ORDER BY ts DESC')\n .bind(userId)\n .all<PushDevice>();\n return results;\n }\n\n async upsertDevice(device: DeviceInput): Promise<void> {\n await this.db\n .prepare(\n 'INSERT INTO devices (token, user_id, platform, ts) VALUES (?, ?, ?, ?) ON CONFLICT (token) DO UPDATE SET user_id = excluded.user_id, platform = excluded.platform, ts = excluded.ts'\n )\n .bind(device.token, device.userId, device.platform, device.ts)\n .run();\n }\n\n async requestErasure(userId: string, at: number): Promise<void> {\n await this.db\n .prepare('INSERT OR IGNORE INTO erasures (user_id, ts) VALUES (?, ?)')\n .bind(userId, at)\n .run();\n }\n\n async pendingErasures(): Promise<string[]> {\n const { results } = await this.db\n .prepare('SELECT user_id FROM erasures ORDER BY ts')\n .bind()\n .all<{ user_id: string }>();\n return results.map((row) => row.user_id);\n }\n\n async runningInstances(userId: string): Promise<string[]> {\n const { results } = await this.db\n .prepare(\"SELECT instance_id FROM entries WHERE user_id = ? AND status = 'running'\")\n .bind(userId)\n .all<{ instance_id: string }>();\n return results.map((row) => row.instance_id);\n }\n\n async forgetUser(userId: string): Promise<void> {\n // One batch: D1 runs it as a transaction; the request goes last, with the data it erases\n await this.db.batch(\n [\n 'entries',\n 'subscriptions',\n 'segment_starts',\n 'segment_members',\n 'devices',\n 'events',\n 'profiles',\n 'erasures',\n ].map((table) => this.db.prepare(`DELETE FROM ${table} WHERE user_id = ?`).bind(userId))\n );\n }\n\n async removeDevice(token: string): Promise<string | undefined> {\n const row = await this.db\n .prepare('DELETE FROM devices WHERE token = ? RETURNING user_id')\n .bind(token)\n .first<{ user_id: string | null }>();\n return row?.user_id ?? undefined;\n }\n\n async unbindDevice(token: string): Promise<string | undefined> {\n const row = await this.db\n .prepare('SELECT user_id FROM devices WHERE token = ?')\n .bind(token)\n .first<{ user_id: string | null }>();\n await this.db.prepare('UPDATE devices SET user_id = NULL WHERE token = ?').bind(token).run();\n return row?.user_id ?? undefined;\n }\n\n async listSegments(): Promise<{ id: string; condition: ConditionIR }[]> {\n const { results } = await this.db\n .prepare('SELECT id, condition FROM segments ORDER BY id')\n .bind()\n .all<{ id: string; condition: string }>();\n return results.map((row) => ({\n id: row.id,\n condition: ConditionIR.parse(JSON.parse(row.condition)),\n }));\n }\n\n async getSegmentCondition(id: string): Promise<ConditionIR | undefined> {\n const row = await this.db\n .prepare('SELECT condition FROM segments WHERE id = ?')\n .bind(id)\n .first<{ condition: string }>();\n return row ? ConditionIR.parse(JSON.parse(row.condition)) : undefined;\n }\n\n async findTriggers(event: string): Promise<TriggerRoute[]> {\n const { results } = await this.db\n .prepare(\n 'SELECT workflow, hash, \"where\" AS whereClause, filter FROM triggers WHERE event = ?'\n )\n .bind(event)\n .all<{ workflow: string; hash: string; whereClause: string | null; filter: string | null }>();\n return results.map((row) => ({\n workflow: row.workflow,\n hash: row.hash,\n where: row.whereClause === null ? null : ConditionIR.parse(JSON.parse(row.whereClause)),\n filter: row.filter === null ? null : ConditionIR.parse(JSON.parse(row.filter)),\n }));\n }\n\n async listSegmentTriggers(): Promise<SegmentTriggerRoute[]> {\n const { results } = await this.db\n .prepare('SELECT workflow, hash, segment FROM segment_triggers')\n .all<SegmentTriggerRoute>();\n return results;\n }\n\n async putWorkflowIR(workflow: WorkflowIR): Promise<void> {\n await this.kv.put(`wf:${workflow.contentHash}`, JSON.stringify(workflow));\n }\n\n async getWorkflowIR(contentHash: string): Promise<WorkflowIR | undefined> {\n const raw = await this.kv.get(`wf:${contentHash}`);\n return raw === null ? undefined : WorkflowIR.parse(JSON.parse(raw));\n }\n\n async replaceRoutes(bundle: BundleIR): Promise<void> {\n // One atomic batch: all-or-nothing, and a concurrent ingest never observes\n // the half-empty routing table a delete-then-insert loop would expose.\n await this.db.batch([\n this.db.prepare('DELETE FROM triggers'),\n this.db.prepare('DELETE FROM segment_triggers'),\n this.db.prepare('DELETE FROM segments'),\n ...bundle.segments.map((segment) =>\n this.db\n .prepare('INSERT INTO segments (id, condition, hash) VALUES (?, ?, ?)')\n .bind(segment.id, JSON.stringify(segment.condition), segment.contentHash)\n ),\n ...bundle.workflows.flatMap((workflow) => {\n if (workflow.trigger.type === 'event') {\n return [\n this.db\n .prepare(\n 'INSERT INTO triggers (workflow, hash, event, \"where\", filter) VALUES (?, ?, ?, ?, ?)'\n )\n .bind(\n workflow.id,\n workflow.contentHash,\n workflow.trigger.event,\n workflow.trigger.where !== undefined\n ? JSON.stringify(workflow.trigger.where)\n : null,\n workflow.trigger.filter !== undefined\n ? JSON.stringify(workflow.trigger.filter)\n : null\n ),\n ];\n }\n if (workflow.trigger.type === 'segment') {\n return [\n this.db\n .prepare('INSERT INTO segment_triggers (workflow, hash, segment) VALUES (?, ?, ?)')\n .bind(workflow.id, workflow.contentHash, workflow.trigger.segment),\n ];\n }\n return [];\n }),\n ]);\n }\n\n async isSegmentMember(segment: string, userId: string): Promise<boolean> {\n const row = await this.db\n .prepare('SELECT 1 AS x FROM segment_members WHERE segment = ? AND user_id = ?')\n .bind(segment, userId)\n .first<{ x: number }>();\n return row !== null;\n }\n\n async addSegmentMember(\n segment: string,\n userId: string,\n ts: number,\n workflows: readonly string[]\n ): Promise<void> {\n await this.db.batch([\n this.db\n .prepare('INSERT OR IGNORE INTO segment_members (segment, user_id, ts) VALUES (?, ?, ?)')\n .bind(segment, userId, ts),\n ...workflows.map((workflow) =>\n this.db\n .prepare(\n 'INSERT OR IGNORE INTO segment_starts (segment, workflow, user_id, ts) VALUES (?, ?, ?, ?)'\n )\n .bind(segment, workflow, userId, ts)\n ),\n ]);\n }\n\n async pendingSegmentStarts(segment: string, userId: string): Promise<string[]> {\n const { results } = await this.db\n .prepare('SELECT workflow FROM segment_starts WHERE segment = ? AND user_id = ?')\n .bind(segment, userId)\n .all<{ workflow: string }>();\n return results.map((row) => row.workflow);\n }\n\n async completeSegmentStart(segment: string, userId: string, workflow: string): Promise<void> {\n await this.db\n .prepare('DELETE FROM segment_starts WHERE segment = ? AND user_id = ? AND workflow = ?')\n .bind(segment, userId, workflow)\n .run();\n }\n\n async removeSegmentMember(segment: string, userId: string): Promise<void> {\n await this.db.batch([\n this.db\n .prepare('DELETE FROM segment_starts WHERE segment = ? AND user_id = ?')\n .bind(segment, userId),\n this.db\n .prepare('DELETE FROM segment_members WHERE segment = ? AND user_id = ?')\n .bind(segment, userId),\n ]);\n }\n\n async enterJourney(entry: EntryInput): Promise<EntryOutcome> {\n if (entry.execution !== undefined) {\n throw new Error('D1JourneyStore keeps no execution starts: this runtime needs Postgres');\n }\n const inserted = await this.db\n .prepare(\n 'INSERT OR IGNORE INTO entries (workflow, user_id, instance_id, hash, status, ts) VALUES (?, ?, ?, ?, ?, ?)'\n )\n .bind(entry.workflow, entry.userId, entry.instanceId, entry.hash, 'running', entry.ts)\n .run();\n if ((inserted.meta?.changes ?? 1) !== 0) return 'entered';\n\n // A row already there blocks the entry, unless it records an instance that\n // died: 'failed' is reclaimed rather than barring the user forever over one\n // outage. The status test lives inside the UPDATE, so of two concurrent\n // retries exactly one sees a change.\n const claimed = await this.db\n .prepare(\n \"UPDATE entries SET instance_id = ?, hash = ?, status = 'running', ts = ? WHERE workflow = ? AND user_id = ? AND status = 'failed'\"\n )\n .bind(entry.instanceId, entry.hash, entry.ts, entry.workflow, entry.userId)\n .run();\n return (claimed.meta?.changes ?? 0) > 0 ? 'reclaimed' : null;\n }\n\n async removeEntry(instanceId: string): Promise<void> {\n await this.db.prepare('DELETE FROM entries WHERE instance_id = ?').bind(instanceId).run();\n }\n\n async setEntryStatus(instanceId: string, status: string): Promise<void> {\n await this.db\n .prepare('UPDATE entries SET status = ? WHERE instance_id = ?')\n .bind(status, instanceId)\n .run();\n }\n\n async findWakeHandles(userId: string, event: string): Promise<string[]> {\n const { results } = await this.db\n .prepare(\n 'SELECT DISTINCT wake_handle AS handle FROM subscriptions WHERE user_id = ? AND event = ?'\n )\n .bind(userId, event)\n .all<{ handle: string }>();\n return results.map((row) => row.handle);\n }\n\n async subscribe(\n handle: string,\n userId: string,\n events: readonly string[],\n ts: number\n ): Promise<void> {\n for (const event of events) {\n // OR IGNORE on the (wake_handle, event) PK: the interpreter re-subscribes on\n // every wait attempt (one-shot-callback contract) and rows persist here.\n await this.db\n .prepare(\n 'INSERT OR IGNORE INTO subscriptions (user_id, event, wake_handle, ts) VALUES (?, ?, ?, ?)'\n )\n .bind(userId, event, handle, ts)\n .run();\n }\n }\n\n async unsubscribe(handle: string): Promise<void> {\n await this.db.prepare('DELETE FROM subscriptions WHERE wake_handle = ?').bind(handle).run();\n }\n}\n\n/** What a merge-patch of `props` into an empty profile yields: the patch minus its nulls. */\nfunction stripNulls(props: ProfileProps): ProfileProps {\n return Object.fromEntries(Object.entries(props).filter(([, value]) => value !== null));\n}\n"],"mappings":"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"}
|
package/dist/store/d1.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"d1.d.cts","names":[],"sources":["../../src/store/d1.ts"],"mappings":";;;;;;;;;;;qBAqBa,0BAA0B;mBAElB;mBACA;EAFnB,YACmB,IAAI,gBACJ,IAAI;EAGjB,YAAY,OAAO,aAAa;;UAW9B;EAcF,YAAY,gBAAgB,eAAe,SAAS,cAAc;EASlE,kBACJ,gBACA,eACA,SAAS,cACR,QAAQ;EASL,WAAW,iBAAiB,QAAQ;EAQpC,aAAa,gBAAgB,OAAO,eAAe,QAAQ;EAgB3D,eAAe;EAQf,YAAY,iBAAiB,QAAQ;EAQrC,aAAa,QAAQ,cAAc;EASnC,eAAe,gBAAgB,aAAa;EAO5C,mBAAmB;EAQnB,iBAAiB,iBAAiB;EAQlC,WAAW,iBAAiB;EAgB5B,aAAa,gBAAgB;EAQ7B,aAAa,gBAAgB;EAS7B,gBAAgB;IAAU;IAAY,WAAW;;EAWjD,oBAAoB,aAAa,QAAQ;EAQzC,aAAa,gBAAgB,QAAQ;EAerC,uBAAuB,QAAQ;EAO/B,cAAc,UAAU,aAAa;EAIrC,cAAc,sBAAsB,QAAQ;EAK5C,cAAc,QAAQ,WAAW;EA4CjC,gBAAgB,iBAAiB,iBAAiB;EAQlD,iBACJ,iBACA,gBACA,YACA,+BACC;EAeG,qBAAqB,iBAAiB,iBAAiB;EAQvD,qBAAqB,iBAAiB,gBAAgB,mBAAmB;EAOzE,oBAAoB,iBAAiB,iBAAiB;EAWtD,aAAa,OAAO,aAAa,QAAQ;
|
|
1
|
+
{"version":3,"file":"d1.d.cts","names":[],"sources":["../../src/store/d1.ts"],"mappings":";;;;;;;;;;;qBAqBa,0BAA0B;mBAElB;mBACA;EAFnB,YACmB,IAAI,gBACJ,IAAI;EAGjB,YAAY,OAAO,aAAa;;UAW9B;EAcF,YAAY,gBAAgB,eAAe,SAAS,cAAc;EASlE,kBACJ,gBACA,eACA,SAAS,cACR,QAAQ;EASL,WAAW,iBAAiB,QAAQ;EAQpC,aAAa,gBAAgB,OAAO,eAAe,QAAQ;EAgB3D,eAAe;EAQf,YAAY,iBAAiB,QAAQ;EAQrC,aAAa,QAAQ,cAAc;EASnC,eAAe,gBAAgB,aAAa;EAO5C,mBAAmB;EAQnB,iBAAiB,iBAAiB;EAQlC,WAAW,iBAAiB;EAgB5B,aAAa,gBAAgB;EAQ7B,aAAa,gBAAgB;EAS7B,gBAAgB;IAAU;IAAY,WAAW;;EAWjD,oBAAoB,aAAa,QAAQ;EAQzC,aAAa,gBAAgB,QAAQ;EAerC,uBAAuB,QAAQ;EAO/B,cAAc,UAAU,aAAa;EAIrC,cAAc,sBAAsB,QAAQ;EAK5C,cAAc,QAAQ,WAAW;EA4CjC,gBAAgB,iBAAiB,iBAAiB;EAQlD,iBACJ,iBACA,gBACA,YACA,+BACC;EAeG,qBAAqB,iBAAiB,iBAAiB;EAQvD,qBAAqB,iBAAiB,gBAAgB,mBAAmB;EAOzE,oBAAoB,iBAAiB,iBAAiB;EAWtD,aAAa,OAAO,aAAa,QAAQ;EAyBzC,YAAY,qBAAqB;EAIjC,eAAe,oBAAoB,iBAAiB;EAOpD,gBAAgB,gBAAgB,gBAAgB;EAUhD,UACJ,gBACA,gBACA,2BACA,aACC;EAaG,YAAY,iBAAiB"}
|