@cronwatch/sdk 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +8 -2
  2. package/dist/anthropic.d.cts +1 -1
  3. package/dist/anthropic.d.ts +1 -1
  4. package/dist/bugsnag.cjs +108 -0
  5. package/dist/bugsnag.cjs.map +1 -0
  6. package/dist/bugsnag.d.cts +26 -0
  7. package/dist/bugsnag.d.ts +26 -0
  8. package/dist/bugsnag.js +106 -0
  9. package/dist/bugsnag.js.map +1 -0
  10. package/dist/client-B7bqnkI5.d.cts +450 -0
  11. package/dist/client-pmqH3apS.d.ts +450 -0
  12. package/dist/d1.cjs +214 -0
  13. package/dist/d1.cjs.map +1 -0
  14. package/dist/d1.d.cts +42 -0
  15. package/dist/d1.d.ts +42 -0
  16. package/dist/d1.js +212 -0
  17. package/dist/d1.js.map +1 -0
  18. package/dist/datadog.cjs +95 -0
  19. package/dist/datadog.cjs.map +1 -0
  20. package/dist/datadog.d.cts +24 -0
  21. package/dist/datadog.d.ts +24 -0
  22. package/dist/datadog.js +93 -0
  23. package/dist/datadog.js.map +1 -0
  24. package/dist/discord.cjs +2 -0
  25. package/dist/discord.cjs.map +1 -1
  26. package/dist/discord.d.cts +1 -1
  27. package/dist/discord.d.ts +1 -1
  28. package/dist/discord.js +2 -0
  29. package/dist/discord.js.map +1 -1
  30. package/dist/email-BJ-z_epD.d.cts +21 -0
  31. package/dist/email-_H0faCh3.d.ts +21 -0
  32. package/dist/honeybadger.cjs +101 -0
  33. package/dist/honeybadger.cjs.map +1 -0
  34. package/dist/honeybadger.d.cts +23 -0
  35. package/dist/honeybadger.d.ts +23 -0
  36. package/dist/honeybadger.js +99 -0
  37. package/dist/honeybadger.js.map +1 -0
  38. package/dist/index.cjs +462 -45
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +5 -260
  41. package/dist/index.d.ts +5 -260
  42. package/dist/index.js +462 -45
  43. package/dist/index.js.map +1 -1
  44. package/dist/mailgun.cjs +115 -0
  45. package/dist/mailgun.cjs.map +1 -0
  46. package/dist/mailgun.d.cts +21 -0
  47. package/dist/mailgun.d.ts +21 -0
  48. package/dist/mailgun.js +113 -0
  49. package/dist/mailgun.js.map +1 -0
  50. package/dist/newrelic.cjs +88 -0
  51. package/dist/newrelic.cjs.map +1 -0
  52. package/dist/newrelic.d.cts +25 -0
  53. package/dist/newrelic.d.ts +25 -0
  54. package/dist/newrelic.js +86 -0
  55. package/dist/newrelic.js.map +1 -0
  56. package/dist/node.cjs +191 -0
  57. package/dist/node.cjs.map +1 -0
  58. package/dist/node.d.cts +97 -0
  59. package/dist/node.d.ts +97 -0
  60. package/dist/node.js +186 -0
  61. package/dist/node.js.map +1 -0
  62. package/dist/pg-cron.cjs +286 -0
  63. package/dist/pg-cron.cjs.map +1 -0
  64. package/dist/pg-cron.d.cts +93 -0
  65. package/dist/pg-cron.d.ts +93 -0
  66. package/dist/pg-cron.js +280 -0
  67. package/dist/pg-cron.js.map +1 -0
  68. package/dist/postgres.cjs +20 -0
  69. package/dist/postgres.cjs.map +1 -1
  70. package/dist/postgres.d.cts +1 -1
  71. package/dist/postgres.d.ts +1 -1
  72. package/dist/postgres.js +20 -0
  73. package/dist/postgres.js.map +1 -1
  74. package/dist/postmark.cjs +106 -0
  75. package/dist/postmark.cjs.map +1 -0
  76. package/dist/postmark.d.cts +18 -0
  77. package/dist/postmark.d.ts +18 -0
  78. package/dist/postmark.js +104 -0
  79. package/dist/postmark.js.map +1 -0
  80. package/dist/resend.cjs +114 -0
  81. package/dist/resend.cjs.map +1 -0
  82. package/dist/resend.d.cts +16 -0
  83. package/dist/resend.d.ts +16 -0
  84. package/dist/resend.js +112 -0
  85. package/dist/resend.js.map +1 -0
  86. package/dist/rollbar.cjs +108 -0
  87. package/dist/rollbar.cjs.map +1 -0
  88. package/dist/rollbar.d.cts +20 -0
  89. package/dist/rollbar.d.ts +20 -0
  90. package/dist/rollbar.js +106 -0
  91. package/dist/rollbar.js.map +1 -0
  92. package/dist/sendgrid.cjs +114 -0
  93. package/dist/sendgrid.cjs.map +1 -0
  94. package/dist/sendgrid.d.cts +19 -0
  95. package/dist/sendgrid.d.ts +19 -0
  96. package/dist/sendgrid.js +112 -0
  97. package/dist/sendgrid.js.map +1 -0
  98. package/dist/sentry.cjs +130 -0
  99. package/dist/sentry.cjs.map +1 -0
  100. package/dist/sentry.d.cts +28 -0
  101. package/dist/sentry.d.ts +28 -0
  102. package/dist/sentry.js +127 -0
  103. package/dist/sentry.js.map +1 -0
  104. package/dist/ses.cjs +170 -0
  105. package/dist/ses.cjs.map +1 -0
  106. package/dist/ses.d.cts +25 -0
  107. package/dist/ses.d.ts +25 -0
  108. package/dist/ses.js +168 -0
  109. package/dist/ses.js.map +1 -0
  110. package/dist/slack.cjs +2 -0
  111. package/dist/slack.cjs.map +1 -1
  112. package/dist/slack.d.cts +1 -1
  113. package/dist/slack.d.ts +1 -1
  114. package/dist/slack.js +2 -0
  115. package/dist/slack.js.map +1 -1
  116. package/dist/sqlite.cjs +18 -0
  117. package/dist/sqlite.cjs.map +1 -1
  118. package/dist/sqlite.d.cts +1 -1
  119. package/dist/sqlite.d.ts +1 -1
  120. package/dist/sqlite.js +18 -0
  121. package/dist/sqlite.js.map +1 -1
  122. package/dist/twilio.cjs +168 -0
  123. package/dist/twilio.cjs.map +1 -0
  124. package/dist/twilio.d.cts +52 -0
  125. package/dist/twilio.d.ts +52 -0
  126. package/dist/twilio.js +162 -0
  127. package/dist/twilio.js.map +1 -0
  128. package/dist/{types-DUt2apl0.d.cts → types-Ddq1MUPL.d.cts} +29 -2
  129. package/dist/{types-DUt2apl0.d.ts → types-Ddq1MUPL.d.ts} +29 -2
  130. package/dist/webhook.cjs +11 -5
  131. package/dist/webhook.cjs.map +1 -1
  132. package/dist/webhook.d.cts +6 -3
  133. package/dist/webhook.d.ts +6 -3
  134. package/dist/webhook.js +11 -6
  135. package/dist/webhook.js.map +1 -1
  136. package/package.json +208 -2
package/dist/node.cjs ADDED
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ var stream = require('stream');
4
+ var promises = require('stream/promises');
5
+
6
+ // src/node.ts
7
+ function header(value) {
8
+ return Array.isArray(value) ? value[0] : value;
9
+ }
10
+ function firstValue(value) {
11
+ const first = header(value)?.split(",")[0]?.trim();
12
+ return first ? first : void 0;
13
+ }
14
+ function originFrom(proto, host) {
15
+ try {
16
+ const url = new URL(`${proto}://${host}`);
17
+ if (url.pathname !== "/" || url.username || url.password || url.search || url.hash) return null;
18
+ return url.origin;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+ function pathAndQuery(target) {
24
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(target)) {
25
+ try {
26
+ const url = new URL(target);
27
+ return url.pathname + url.search;
28
+ } catch {
29
+ return "/";
30
+ }
31
+ }
32
+ return target.startsWith("/") ? target : `/${target}`;
33
+ }
34
+ function requestOrigin(req, trustProxy) {
35
+ const socket = req.socket;
36
+ let proto = socket?.encrypted === true ? "https" : "http";
37
+ let host = header(req.headers.host) ?? header(req.headers[":authority"]);
38
+ if (trustProxy) {
39
+ const fp = firstValue(req.headers["x-forwarded-proto"])?.toLowerCase();
40
+ if (fp === "http" || fp === "https") proto = fp;
41
+ const fh = firstValue(req.headers["x-forwarded-host"]);
42
+ if (fh && originFrom(proto, fh) !== null) host = fh;
43
+ }
44
+ return (host ? originFrom(proto, host) : null) ?? `${proto}://localhost`;
45
+ }
46
+ function unread(req) {
47
+ if (typeof req.on !== "function" || req.readable === false) return false;
48
+ return !req.readableEnded && req.readableDidRead !== true;
49
+ }
50
+ function reencode(parsed, headers) {
51
+ if (parsed === void 0 || parsed === null) return null;
52
+ if (typeof parsed === "string" || parsed instanceof Uint8Array) return parsed;
53
+ if (typeof parsed !== "object") return null;
54
+ const type = (headers.get("content-type") ?? "").toLowerCase();
55
+ let text = null;
56
+ if (type.includes("application/x-www-form-urlencoded")) {
57
+ const form = new URLSearchParams();
58
+ for (const [key, value] of Object.entries(parsed)) {
59
+ for (const v of Array.isArray(value) ? value : [value]) {
60
+ if (v !== void 0 && v !== null) form.append(key, typeof v === "object" ? JSON.stringify(v) : String(v));
61
+ }
62
+ }
63
+ text = form.toString();
64
+ } else if (type.includes("json")) {
65
+ text = JSON.stringify(parsed);
66
+ }
67
+ if (text === null) return null;
68
+ headers.delete("content-length");
69
+ headers.delete("transfer-encoding");
70
+ return text;
71
+ }
72
+ function toRequest(req, options = {}, source = {}) {
73
+ const node = req;
74
+ const target = source.url ?? node.originalUrl ?? req.url ?? "/";
75
+ const url = requestOrigin(req, options.trustProxy === true) + pathAndQuery(target);
76
+ const headers = new Headers();
77
+ for (const [name, value] of Object.entries(req.headers)) {
78
+ if (value === void 0 || name.startsWith(":")) continue;
79
+ if (Array.isArray(value)) for (const v of value) headers.append(name, v);
80
+ else headers.set(name, value);
81
+ }
82
+ const method = (req.method ?? "GET").toUpperCase();
83
+ const init = { method, headers };
84
+ if (options.signal) init.signal = options.signal;
85
+ if (method !== "GET" && method !== "HEAD") {
86
+ const raw = source.rawBody ?? node.rawBody;
87
+ if (raw instanceof Uint8Array) init.body = raw;
88
+ else if (typeof raw === "string") init.body = raw;
89
+ else if (unread(req)) {
90
+ init.body = stream.Readable.toWeb(req);
91
+ init.duplex = "half";
92
+ } else {
93
+ const body = reencode(source.body !== void 0 ? source.body : node.body, headers);
94
+ if (body !== null) init.body = body;
95
+ else {
96
+ headers.delete("content-length");
97
+ headers.delete("transfer-encoding");
98
+ }
99
+ }
100
+ }
101
+ return new Request(url, init);
102
+ }
103
+ async function writeResponse(res, response, method = "GET") {
104
+ res.statusCode = response.status;
105
+ response.headers.forEach((value, name) => {
106
+ if (name !== "set-cookie") res.setHeader(name, value);
107
+ });
108
+ const cookies = response.headers.getSetCookie();
109
+ if (cookies.length > 0) res.setHeader("set-cookie", cookies);
110
+ if (response.body === null || method.toUpperCase() === "HEAD") {
111
+ if (response.body !== null) await response.body.cancel().catch(() => {
112
+ });
113
+ res.end();
114
+ return;
115
+ }
116
+ try {
117
+ await promises.pipeline(stream.Readable.fromWeb(response.body), res);
118
+ } catch {
119
+ }
120
+ }
121
+ function mountOf(basePath) {
122
+ return basePath === void 0 ? null : basePath.replace(/\/+$/, "");
123
+ }
124
+ function inMount(pathname, mount) {
125
+ return mount === null || mount === "" || pathname === mount || pathname.startsWith(`${mount}/`);
126
+ }
127
+ function pathnameOf(target) {
128
+ const path = pathAndQuery(target);
129
+ const q = path.search(/[?#]/);
130
+ return q === -1 ? path : path.slice(0, q);
131
+ }
132
+ function disconnectSignal(res) {
133
+ const controller = new AbortController();
134
+ res.once("close", () => {
135
+ if (!res.writableFinished) controller.abort();
136
+ });
137
+ return controller.signal;
138
+ }
139
+ function toNodeHandler(handler, options = {}) {
140
+ const mount = mountOf(options.basePath);
141
+ const trustProxy = options.trustProxy === true;
142
+ return async (req, res, next) => {
143
+ const node = req;
144
+ if (!inMount(pathnameOf(node.originalUrl ?? req.url ?? "/"), mount)) {
145
+ if (next) next();
146
+ else {
147
+ res.statusCode = 404;
148
+ res.setHeader("content-type", "text/plain; charset=utf-8");
149
+ res.end("Not found");
150
+ }
151
+ return;
152
+ }
153
+ try {
154
+ const request = toRequest(req, { trustProxy, signal: disconnectSignal(res) });
155
+ await writeResponse(res, await handler(request), request.method);
156
+ } catch (error) {
157
+ if (next) next(error);
158
+ else if (!res.headersSent) {
159
+ res.statusCode = 500;
160
+ res.setHeader("content-type", "text/plain; charset=utf-8");
161
+ res.end("Internal Server Error");
162
+ } else res.destroy(error instanceof Error ? error : void 0);
163
+ }
164
+ };
165
+ }
166
+ function toKoaMiddleware(handler, options = {}) {
167
+ const mount = mountOf(options.basePath);
168
+ const trustProxy = options.trustProxy === true;
169
+ return async (ctx, next) => {
170
+ const target = ctx.originalUrl ?? ctx.req.url ?? "/";
171
+ if (!inMount(pathnameOf(target), mount)) {
172
+ await next();
173
+ return;
174
+ }
175
+ const request = toRequest(ctx.req, { trustProxy, signal: disconnectSignal(ctx.res) }, {
176
+ url: target,
177
+ rawBody: ctx.request?.rawBody,
178
+ body: ctx.request?.body
179
+ });
180
+ const response = await handler(request);
181
+ ctx.respond = false;
182
+ await writeResponse(ctx.res, response, request.method);
183
+ };
184
+ }
185
+
186
+ exports.toKoaMiddleware = toKoaMiddleware;
187
+ exports.toNodeHandler = toNodeHandler;
188
+ exports.toRequest = toRequest;
189
+ exports.writeResponse = writeResponse;
190
+ //# sourceMappingURL=node.cjs.map
191
+ //# sourceMappingURL=node.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/node.ts"],"names":["Readable","pipeline"],"mappings":";;;;;;AA+DA,SAAS,OAAO,KAAA,EAA0D;AACxE,EAAA,OAAO,MAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA;AAC3C;AAEA,SAAS,WAAW,KAAA,EAA0D;AAC5E,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA,EAAG,MAAM,GAAG,CAAA,CAAE,CAAC,CAAA,EAAG,IAAA,EAAK;AACjD,EAAA,OAAO,QAAQ,KAAA,GAAQ,MAAA;AACzB;AAGA,SAAS,UAAA,CAAW,OAAe,IAAA,EAA6B;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,KAAK,CAAA,GAAA,EAAM,IAAI,CAAA,CAAE,CAAA;AACxC,IAAA,IAAI,GAAA,CAAI,QAAA,KAAa,GAAA,IAAO,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,MAAA,IAAU,GAAA,CAAI,IAAA,EAAM,OAAO,IAAA;AAC3F,IAAA,OAAO,GAAA,CAAI,MAAA;AAAA,EACb,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGA,SAAS,aAAa,MAAA,EAAwB;AAC5C,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,MAAM,CAAA,EAAG;AAC3C,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAM,CAAA;AAC1B,MAAA,OAAO,GAAA,CAAI,WAAW,GAAA,CAAI,MAAA;AAAA,IAC5B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,OAAO,UAAA,CAAW,GAAG,CAAA,GAAI,MAAA,GAAS,IAAI,MAAM,CAAA,CAAA;AACrD;AAEA,SAAS,aAAA,CAAc,KAAsB,UAAA,EAA6B;AACxE,EAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AACnB,EAAA,IAAI,KAAA,GAAQ,MAAA,EAAQ,SAAA,KAAc,IAAA,GAAO,OAAA,GAAU,MAAA;AACnD,EAAA,IAAI,IAAA,GAAO,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,IAAI,KAAK,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,YAAY,CAAkC,CAAA;AACxG,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,MAAM,KAAK,UAAA,CAAW,GAAA,CAAI,QAAQ,mBAAmB,CAAC,GAAG,WAAA,EAAY;AACrE,IAAA,IAAI,EAAA,KAAO,MAAA,IAAU,EAAA,KAAO,OAAA,EAAS,KAAA,GAAQ,EAAA;AAC7C,IAAA,MAAM,EAAA,GAAK,UAAA,CAAW,GAAA,CAAI,OAAA,CAAQ,kBAAkB,CAAC,CAAA;AACrD,IAAA,IAAI,MAAM,UAAA,CAAW,KAAA,EAAO,EAAE,CAAA,KAAM,MAAM,IAAA,GAAO,EAAA;AAAA,EACnD;AACA,EAAA,OAAA,CAAQ,OAAO,UAAA,CAAW,KAAA,EAAO,IAAI,CAAA,GAAI,IAAA,KAAS,GAAG,KAAK,CAAA,YAAA,CAAA;AAC5D;AAGA,SAAS,OAAO,GAAA,EAA+B;AAC7C,EAAA,IAAI,OAAO,GAAA,CAAI,EAAA,KAAO,cAAc,GAAA,CAAI,QAAA,KAAa,OAAO,OAAO,KAAA;AACnE,EAAA,OAAO,CAAC,GAAA,CAAI,aAAA,IAAiB,GAAA,CAAI,eAAA,KAAoB,IAAA;AACvD;AAOA,SAAS,QAAA,CAAS,QAAiB,OAAA,EAA8C;AAC/E,EAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,IAAA,EAAM,OAAO,IAAA;AACpD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,YAAkB,YAAY,OAAO,MAAA;AACvE,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,IAAA;AACvC,EAAA,MAAM,QAAQ,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,IAAI,WAAA,EAAY;AAC7D,EAAA,IAAI,IAAA,GAAsB,IAAA;AAC1B,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,mCAAmC,CAAA,EAAG;AACtD,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,EAAgB;AACjC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,MAAA,KAAA,MAAW,CAAA,IAAK,MAAM,OAAA,CAAQ,KAAK,IAAI,KAAA,GAAQ,CAAC,KAAK,CAAA,EAAG;AACtD,QAAA,IAAI,MAAM,MAAA,IAAa,CAAA,KAAM,IAAA,EAAM,IAAA,CAAK,OAAO,GAAA,EAAK,OAAO,CAAA,KAAM,QAAA,GAAW,KAAK,SAAA,CAAU,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MAC3G;AAAA,IACF;AACA,IAAA,IAAA,GAAO,KAAK,QAAA,EAAS;AAAA,EACvB,CAAA,MAAA,IAAW,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG;AAChC,IAAA,IAAA,GAAO,IAAA,CAAK,UAAU,MAAM,CAAA;AAAA,EAC9B;AACA,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,IAAA;AAC1B,EAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,EAAA,OAAA,CAAQ,OAAO,mBAAmB,CAAA;AAClC,EAAA,OAAO,IAAA;AACT;AAUO,SAAS,UAAU,GAAA,EAAsB,OAAA,GAA0D,EAAC,EAAG,MAAA,GAAqB,EAAC,EAAY;AAC9I,EAAA,MAAM,IAAA,GAAO,GAAA;AACb,EAAA,MAAM,SAAS,MAAA,CAAO,GAAA,IAAO,IAAA,CAAK,WAAA,IAAe,IAAI,GAAA,IAAO,GAAA;AAC5D,EAAA,MAAM,GAAA,GAAM,cAAc,GAAA,EAAK,OAAA,CAAQ,eAAe,IAAI,CAAA,GAAI,aAAa,MAAM,CAAA;AAEjF,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAC5B,EAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,EAAG;AAEvD,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACjD,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAA,MAAW,KAAK,KAAA,EAAO,OAAA,CAAQ,MAAA,CAAO,IAAA,EAAM,CAAC,CAAA;AAAA,SAClE,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,MAAA,GAAA,CAAU,GAAA,CAAI,MAAA,IAAU,KAAA,EAAO,WAAA,EAAY;AACjD,EAAA,MAAM,IAAA,GAA0C,EAAE,MAAA,EAAQ,OAAA,EAAQ;AAClE,EAAA,IAAI,OAAA,CAAQ,MAAA,EAAQ,IAAA,CAAK,MAAA,GAAS,OAAA,CAAQ,MAAA;AAC1C,EAAA,IAAI,MAAA,KAAW,KAAA,IAAS,MAAA,KAAW,MAAA,EAAQ;AACzC,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,IAAW,IAAA,CAAK,OAAA;AAEnC,IAAA,IAAI,GAAA,YAAe,UAAA,EAAY,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,SAAA,IAClC,OAAO,GAAA,KAAQ,QAAA,EAAU,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,SAAA,IACrC,MAAA,CAAO,GAAG,CAAA,EAAG;AACpB,MAAA,IAAA,CAAK,IAAA,GAAOA,eAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAC9B,MAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,IAChB,CAAA,MAAO;AACL,MAAA,MAAM,IAAA,GAAO,SAAS,MAAA,CAAO,IAAA,KAAS,SAAY,MAAA,CAAO,IAAA,GAAO,IAAA,CAAK,IAAA,EAAM,OAAO,CAAA;AAClF,MAAA,IAAI,IAAA,KAAS,IAAA,EAAM,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,WAC1B;AAEH,QAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,QAAA,OAAA,CAAQ,OAAO,mBAAmB,CAAA;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,IAAI,OAAA,CAAQ,GAAA,EAAK,IAAI,CAAA;AAC9B;AAOA,eAAsB,aAAA,CAAc,GAAA,EAAqB,QAAA,EAAoB,MAAA,GAAS,KAAA,EAAsB;AAC1G,EAAA,GAAA,CAAI,aAAa,QAAA,CAAS,MAAA;AAC1B,EAAA,QAAA,CAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAA,EAAO,IAAA,KAAS;AACxC,IAAA,IAAI,IAAA,KAAS,YAAA,EAAc,GAAA,CAAI,SAAA,CAAU,MAAM,KAAK,CAAA;AAAA,EACtD,CAAC,CAAA;AACD,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,OAAA,CAAQ,YAAA,EAAa;AAC9C,EAAA,IAAI,QAAQ,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,SAAA,CAAU,cAAc,OAAO,CAAA;AAC3D,EAAA,IAAI,SAAS,IAAA,KAAS,IAAA,IAAQ,MAAA,CAAO,WAAA,OAAkB,MAAA,EAAQ;AAC7D,IAAA,IAAI,QAAA,CAAS,SAAS,IAAA,EAAM,MAAM,SAAS,IAAA,CAAK,MAAA,EAAO,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AACvE,IAAA,GAAA,CAAI,GAAA,EAAI;AACR,IAAA;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,MAAMC,kBAASD,eAAA,CAAS,OAAA,CAAQ,QAAA,CAAS,IAAiD,GAAG,GAAG,CAAA;AAAA,EAClG,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAEA,SAAS,QAAQ,QAAA,EAA6C;AAC5D,EAAA,OAAO,aAAa,MAAA,GAAY,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACpE;AAEA,SAAS,OAAA,CAAQ,UAAkB,KAAA,EAA+B;AAChE,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,EAAA,IAAM,QAAA,KAAa,SAAS,QAAA,CAAS,UAAA,CAAW,CAAA,EAAG,KAAK,CAAA,CAAA,CAAG,CAAA;AAChG;AAEA,SAAS,WAAW,MAAA,EAAwB;AAC1C,EAAA,MAAM,IAAA,GAAO,aAAa,MAAM,CAAA;AAChC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA;AAC5B,EAAA,OAAO,MAAM,EAAA,GAAK,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA;AAC1C;AAGA,SAAS,iBAAiB,GAAA,EAAkC;AAC1D,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,GAAA,CAAI,IAAA,CAAK,SAAS,MAAM;AACtB,IAAA,IAAI,CAAC,GAAA,CAAI,gBAAA,EAAkB,UAAA,CAAW,KAAA,EAAM;AAAA,EAC9C,CAAC,CAAA;AACD,EAAA,OAAO,UAAA,CAAW,MAAA;AACpB;AAaO,SAAS,aAAA,CAAc,OAAA,EAAoB,OAAA,GAA8B,EAAC,EAAgB;AAC/F,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,KAAe,IAAA;AAC1C,EAAA,OAAO,OAAO,GAAA,EAAK,GAAA,EAAK,IAAA,KAAS;AAC/B,IAAA,MAAM,IAAA,GAAO,GAAA;AACb,IAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,IAAA,CAAK,WAAA,IAAe,IAAI,GAAA,IAAO,GAAG,CAAA,EAAG,KAAK,CAAA,EAAG;AACnE,MAAA,IAAI,MAAM,IAAA,EAAK;AAAA,WACV;AACH,QAAA,GAAA,CAAI,UAAA,GAAa,GAAA;AACjB,QAAA,GAAA,CAAI,SAAA,CAAU,gBAAgB,2BAA2B,CAAA;AACzD,QAAA,GAAA,CAAI,IAAI,WAAW,CAAA;AAAA,MACrB;AACA,MAAA;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,UAAU,GAAA,EAAK,EAAE,YAAY,MAAA,EAAQ,gBAAA,CAAiB,GAAG,CAAA,EAAG,CAAA;AAC5E,MAAA,MAAM,cAAc,GAAA,EAAK,MAAM,QAAQ,OAAO,CAAA,EAAG,QAAQ,MAAM,CAAA;AAAA,IACjE,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,IAAA,OAAW,KAAK,CAAA;AAAA,WAAA,IACX,CAAC,IAAI,WAAA,EAAa;AACzB,QAAA,GAAA,CAAI,UAAA,GAAa,GAAA;AACjB,QAAA,GAAA,CAAI,SAAA,CAAU,gBAAgB,2BAA2B,CAAA;AACzD,QAAA,GAAA,CAAI,IAAI,uBAAuB,CAAA;AAAA,MACjC,OAAO,GAAA,CAAI,OAAA,CAAQ,KAAA,YAAiB,KAAA,GAAQ,QAAQ,MAAS,CAAA;AAAA,IAC/D;AAAA,EACF,CAAA;AACF;AAUO,SAAS,eAAA,CAAgB,OAAA,EAAoB,OAAA,GAA8B,EAAC,EAAyE;AAC1J,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,KAAe,IAAA;AAC1C,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,WAAA,IAAe,GAAA,CAAI,IAAI,GAAA,IAAO,GAAA;AACjD,IAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,EAAG,KAAK,CAAA,EAAG;AACvC,MAAA,MAAM,IAAA,EAAK;AACX,MAAA;AAAA,IACF;AACA,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,GAAA,CAAI,GAAA,EAAK,EAAE,UAAA,EAAY,MAAA,EAAQ,gBAAA,CAAiB,GAAA,CAAI,GAAG,CAAA,EAAE,EAAG;AAAA,MACpF,GAAA,EAAK,MAAA;AAAA,MACL,OAAA,EAAS,IAAI,OAAA,EAAS,OAAA;AAAA,MACtB,IAAA,EAAM,IAAI,OAAA,EAAS;AAAA,KACpB,CAAA;AACD,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAO,CAAA;AACtC,IAAA,GAAA,CAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,aAAA,CAAc,GAAA,CAAI,GAAA,EAAK,QAAA,EAAU,QAAQ,MAAM,CAAA;AAAA,EACvD,CAAA;AACF","file":"node.cjs","sourcesContent":["/**\n * @cronwatch/sdk/node: serve a fetch-style handler (cw.routes().handler, or\n * a job's handler()) from Node's http module and the frameworks built on it:\n * a plain http server, Express and Connect, NestJS, Koa, Strapi and Firebase\n * onRequest. The SDK core stays free of node: imports so it runs on Workers;\n * this entry point is the only one that uses Node's http and stream types.\n */\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\n\n/** A fetch-style handler: a Request in, a Response out. */\nexport type FetchLike = (request: Request) => Response | Promise<Response>;\n\nexport interface NodeHandlerOptions {\n /**\n * Build the request URL from X-Forwarded-Proto and X-Forwarded-Host (the\n * first value of each) when present, instead of the connection's scheme and\n * the Host header. Only behind a proxy that sets or overwrites both, since\n * a client can send them too. Default false.\n */\n trustProxy?: boolean;\n /**\n * Answer only requests at this path or under it, and pass the rest on: to\n * `next()` as Express or Connect middleware, to the next Koa middleware,\n * and with a 404 when there is no next. The path is read from\n * `req.originalUrl` (so Express mount paths count) or `req.url`. Leave it\n * out to answer every request the handler is given, which suits\n * `app.use(\"/cronwatch\", ...)`, a Nest controller and a Firebase function.\n */\n basePath?: string;\n}\n\n/** Called by Express and Connect for the next middleware, or with an error. */\nexport type NodeNext = (error?: unknown) => void;\n\n/** A handler for http.createServer, Express or Connect, Nest and Firebase onRequest. */\nexport type NodeHandler = (req: IncomingMessage, res: ServerResponse, next?: NodeNext) => Promise<void>;\n\n/** The parts of a Koa context the middleware uses, so the SDK needs no Koa types. */\nexport interface KoaContextLike {\n req: IncomingMessage;\n res: ServerResponse;\n respond?: boolean;\n /** Koa's own copy of the URL, kept when koa-mount rewrites req.url. */\n originalUrl?: string;\n /** Set by koa-bodyparser and similar, when they run before this middleware. */\n request?: { body?: unknown; rawBody?: unknown };\n}\n\n/** Where a request body may already be, when a parser has read the stream. */\nexport interface BodySource {\n /** The request target to use instead of req.originalUrl or req.url. */\n url?: string;\n /** The body's bytes as read (Firebase, Nest with rawBody: true, koa-bodyparser). */\n rawBody?: unknown;\n /** A body a parser has decoded (express.json, express.urlencoded). */\n body?: unknown;\n}\n\ntype NodeRequest = IncomingMessage & { originalUrl?: string; rawBody?: unknown; body?: unknown };\n\nfunction header(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction firstValue(value: string | string[] | undefined): string | undefined {\n const first = header(value)?.split(\",\")[0]?.trim();\n return first ? first : undefined;\n}\n\n/** An origin from a scheme and host, or null when the host is not a bare host[:port]. */\nfunction originFrom(proto: string, host: string): string | null {\n try {\n const url = new URL(`${proto}://${host}`);\n if (url.pathname !== \"/\" || url.username || url.password || url.search || url.hash) return null;\n return url.origin;\n } catch {\n return null;\n }\n}\n\n/** The path and query of a request target, which may be absolute-form. */\nfunction pathAndQuery(target: string): string {\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(target)) {\n try {\n const url = new URL(target);\n return url.pathname + url.search;\n } catch {\n return \"/\";\n }\n }\n // \"//x\" must stay a path, not become a host, so it is never parsed relative to a base.\n return target.startsWith(\"/\") ? target : `/${target}`;\n}\n\nfunction requestOrigin(req: IncomingMessage, trustProxy: boolean): string {\n const socket = req.socket as { encrypted?: boolean } | undefined;\n let proto = socket?.encrypted === true ? \"https\" : \"http\";\n let host = header(req.headers.host) ?? header(req.headers[\":authority\"] as string | string[] | undefined);\n if (trustProxy) {\n const fp = firstValue(req.headers[\"x-forwarded-proto\"])?.toLowerCase();\n if (fp === \"http\" || fp === \"https\") proto = fp;\n const fh = firstValue(req.headers[\"x-forwarded-host\"]);\n if (fh && originFrom(proto, fh) !== null) host = fh;\n }\n return (host ? originFrom(proto, host) : null) ?? `${proto}://localhost`;\n}\n\n/** The stream has not been read by a body parser, so it can be passed on as is. */\nfunction unread(req: IncomingMessage): boolean {\n if (typeof req.on !== \"function\" || req.readable === false) return false;\n return !req.readableEnded && req.readableDidRead !== true;\n}\n\n/**\n * A body a parser decoded, encoded again for its content type: JSON as JSON,\n * a urlencoded form as a form. Anything else (multipart, unknown types) is\n * dropped, because it cannot be rebuilt faithfully.\n */\nfunction reencode(parsed: unknown, headers: Headers): string | Uint8Array | null {\n if (parsed === undefined || parsed === null) return null;\n if (typeof parsed === \"string\" || parsed instanceof Uint8Array) return parsed;\n if (typeof parsed !== \"object\") return null;\n const type = (headers.get(\"content-type\") ?? \"\").toLowerCase();\n let text: string | null = null;\n if (type.includes(\"application/x-www-form-urlencoded\")) {\n const form = new URLSearchParams();\n for (const [key, value] of Object.entries(parsed)) {\n for (const v of Array.isArray(value) ? value : [value]) {\n if (v !== undefined && v !== null) form.append(key, typeof v === \"object\" ? JSON.stringify(v) : String(v));\n }\n }\n text = form.toString();\n } else if (type.includes(\"json\")) {\n text = JSON.stringify(parsed);\n }\n if (text === null) return null;\n headers.delete(\"content-length\");\n headers.delete(\"transfer-encoding\");\n return text;\n}\n\n/**\n * A fetch Request for a Node request. The URL is the Host header (or the\n * forwarded host and scheme with trustProxy) plus req.originalUrl or req.url.\n * The body, for anything but GET and HEAD, is the first of: `rawBody` (from\n * the source, or req.rawBody), the request stream when nothing has read it,\n * or a body a parser decoded (req.body), re-encoded as JSON or a urlencoded\n * form to match its content type.\n */\nexport function toRequest(req: IncomingMessage, options: { trustProxy?: boolean; signal?: AbortSignal } = {}, source: BodySource = {}): Request {\n const node = req as NodeRequest;\n const target = source.url ?? node.originalUrl ?? req.url ?? \"/\";\n const url = requestOrigin(req, options.trustProxy === true) + pathAndQuery(target);\n\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers)) {\n // HTTP/2 pseudo-headers (\":authority\") are not valid header names.\n if (value === undefined || name.startsWith(\":\")) continue;\n if (Array.isArray(value)) for (const v of value) headers.append(name, v);\n else headers.set(name, value);\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const init: RequestInit & { duplex?: \"half\" } = { method, headers };\n if (options.signal) init.signal = options.signal;\n if (method !== \"GET\" && method !== \"HEAD\") {\n const raw = source.rawBody ?? node.rawBody;\n // A Buffer is a Uint8Array view, which fetch reads with its offset.\n if (raw instanceof Uint8Array) init.body = raw as unknown as BodyInit;\n else if (typeof raw === \"string\") init.body = raw;\n else if (unread(req)) {\n init.body = Readable.toWeb(req) as unknown as ReadableStream<Uint8Array>;\n init.duplex = \"half\";\n } else {\n const body = reencode(source.body !== undefined ? source.body : node.body, headers);\n if (body !== null) init.body = body as unknown as BodyInit;\n else {\n // The bytes are gone, so the Request has no body and says so.\n headers.delete(\"content-length\");\n headers.delete(\"transfer-encoding\");\n }\n }\n }\n return new Request(url, init);\n}\n\n/**\n * Writes a fetch Response to a Node response: the status, every header\n * (each Set-Cookie as its own header line), and the body, streamed. A HEAD\n * request gets the headers only.\n */\nexport async function writeResponse(res: ServerResponse, response: Response, method = \"GET\"): Promise<void> {\n res.statusCode = response.status;\n response.headers.forEach((value, name) => {\n if (name !== \"set-cookie\") res.setHeader(name, value);\n });\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) res.setHeader(\"set-cookie\", cookies);\n if (response.body === null || method.toUpperCase() === \"HEAD\") {\n if (response.body !== null) await response.body.cancel().catch(() => {});\n res.end();\n return;\n }\n try {\n await pipeline(Readable.fromWeb(response.body as unknown as NodeReadableStream<Uint8Array>), res);\n } catch {\n // The client went away mid-body; there is no one left to tell.\n }\n}\n\nfunction mountOf(basePath: string | undefined): string | null {\n return basePath === undefined ? null : basePath.replace(/\\/+$/, \"\");\n}\n\nfunction inMount(pathname: string, mount: string | null): boolean {\n return mount === null || mount === \"\" || pathname === mount || pathname.startsWith(`${mount}/`);\n}\n\nfunction pathnameOf(target: string): string {\n const path = pathAndQuery(target);\n const q = path.search(/[?#]/);\n return q === -1 ? path : path.slice(0, q);\n}\n\n/** Aborts the Request's signal when the client disconnects before the response is done. */\nfunction disconnectSignal(res: ServerResponse): AbortSignal {\n const controller = new AbortController();\n res.once(\"close\", () => {\n if (!res.writableFinished) controller.abort();\n });\n return controller.signal;\n}\n\n/**\n * A Node handler for a fetch-style handler. Use it as a plain server\n * (`http.createServer(toNodeHandler(routes.handler))`), as Express or Connect\n * middleware (`app.use(\"/cronwatch\", toNodeHandler(routes.handler))`), from a\n * Nest controller, or as a Firebase `onRequest` function.\n *\n * With `basePath`, requests outside it go to `next()` (or get a 404 when\n * there is no next); without it, every request is answered. An error thrown\n * by the fetch handler goes to `next(error)` when there is a next, and is\n * otherwise answered with a plain 500.\n */\nexport function toNodeHandler(handler: FetchLike, options: NodeHandlerOptions = {}): NodeHandler {\n const mount = mountOf(options.basePath);\n const trustProxy = options.trustProxy === true;\n return async (req, res, next) => {\n const node = req as NodeRequest;\n if (!inMount(pathnameOf(node.originalUrl ?? req.url ?? \"/\"), mount)) {\n if (next) next();\n else {\n res.statusCode = 404;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(\"Not found\");\n }\n return;\n }\n try {\n const request = toRequest(req, { trustProxy, signal: disconnectSignal(res) });\n await writeResponse(res, await handler(request), request.method);\n } catch (error) {\n if (next) next(error);\n else if (!res.headersSent) {\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(\"Internal Server Error\");\n } else res.destroy(error instanceof Error ? error : undefined);\n }\n };\n}\n\n/**\n * Koa middleware for a fetch-style handler. Requests outside `basePath` (when\n * given) go on to the next middleware. The rest are answered here with\n * `ctx.respond = false`, so Koa leaves the response alone. Place it before\n * any body parser; if one runs first, its `ctx.request.rawBody` or\n * `ctx.request.body` is used. An error thrown by the fetch handler is thrown\n * on to Koa before anything is written.\n */\nexport function toKoaMiddleware(handler: FetchLike, options: NodeHandlerOptions = {}): (ctx: KoaContextLike, next: () => Promise<unknown>) => Promise<void> {\n const mount = mountOf(options.basePath);\n const trustProxy = options.trustProxy === true;\n return async (ctx, next) => {\n const target = ctx.originalUrl ?? ctx.req.url ?? \"/\";\n if (!inMount(pathnameOf(target), mount)) {\n await next();\n return;\n }\n const request = toRequest(ctx.req, { trustProxy, signal: disconnectSignal(ctx.res) }, {\n url: target,\n rawBody: ctx.request?.rawBody,\n body: ctx.request?.body,\n });\n const response = await handler(request);\n ctx.respond = false;\n await writeResponse(ctx.res, response, request.method);\n };\n}\n"]}
@@ -0,0 +1,97 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+
3
+ /**
4
+ * @cronwatch/sdk/node: serve a fetch-style handler (cw.routes().handler, or
5
+ * a job's handler()) from Node's http module and the frameworks built on it:
6
+ * a plain http server, Express and Connect, NestJS, Koa, Strapi and Firebase
7
+ * onRequest. The SDK core stays free of node: imports so it runs on Workers;
8
+ * this entry point is the only one that uses Node's http and stream types.
9
+ */
10
+
11
+ /** A fetch-style handler: a Request in, a Response out. */
12
+ type FetchLike = (request: Request) => Response | Promise<Response>;
13
+ interface NodeHandlerOptions {
14
+ /**
15
+ * Build the request URL from X-Forwarded-Proto and X-Forwarded-Host (the
16
+ * first value of each) when present, instead of the connection's scheme and
17
+ * the Host header. Only behind a proxy that sets or overwrites both, since
18
+ * a client can send them too. Default false.
19
+ */
20
+ trustProxy?: boolean;
21
+ /**
22
+ * Answer only requests at this path or under it, and pass the rest on: to
23
+ * `next()` as Express or Connect middleware, to the next Koa middleware,
24
+ * and with a 404 when there is no next. The path is read from
25
+ * `req.originalUrl` (so Express mount paths count) or `req.url`. Leave it
26
+ * out to answer every request the handler is given, which suits
27
+ * `app.use("/cronwatch", ...)`, a Nest controller and a Firebase function.
28
+ */
29
+ basePath?: string;
30
+ }
31
+ /** Called by Express and Connect for the next middleware, or with an error. */
32
+ type NodeNext = (error?: unknown) => void;
33
+ /** A handler for http.createServer, Express or Connect, Nest and Firebase onRequest. */
34
+ type NodeHandler = (req: IncomingMessage, res: ServerResponse, next?: NodeNext) => Promise<void>;
35
+ /** The parts of a Koa context the middleware uses, so the SDK needs no Koa types. */
36
+ interface KoaContextLike {
37
+ req: IncomingMessage;
38
+ res: ServerResponse;
39
+ respond?: boolean;
40
+ /** Koa's own copy of the URL, kept when koa-mount rewrites req.url. */
41
+ originalUrl?: string;
42
+ /** Set by koa-bodyparser and similar, when they run before this middleware. */
43
+ request?: {
44
+ body?: unknown;
45
+ rawBody?: unknown;
46
+ };
47
+ }
48
+ /** Where a request body may already be, when a parser has read the stream. */
49
+ interface BodySource {
50
+ /** The request target to use instead of req.originalUrl or req.url. */
51
+ url?: string;
52
+ /** The body's bytes as read (Firebase, Nest with rawBody: true, koa-bodyparser). */
53
+ rawBody?: unknown;
54
+ /** A body a parser has decoded (express.json, express.urlencoded). */
55
+ body?: unknown;
56
+ }
57
+ /**
58
+ * A fetch Request for a Node request. The URL is the Host header (or the
59
+ * forwarded host and scheme with trustProxy) plus req.originalUrl or req.url.
60
+ * The body, for anything but GET and HEAD, is the first of: `rawBody` (from
61
+ * the source, or req.rawBody), the request stream when nothing has read it,
62
+ * or a body a parser decoded (req.body), re-encoded as JSON or a urlencoded
63
+ * form to match its content type.
64
+ */
65
+ declare function toRequest(req: IncomingMessage, options?: {
66
+ trustProxy?: boolean;
67
+ signal?: AbortSignal;
68
+ }, source?: BodySource): Request;
69
+ /**
70
+ * Writes a fetch Response to a Node response: the status, every header
71
+ * (each Set-Cookie as its own header line), and the body, streamed. A HEAD
72
+ * request gets the headers only.
73
+ */
74
+ declare function writeResponse(res: ServerResponse, response: Response, method?: string): Promise<void>;
75
+ /**
76
+ * A Node handler for a fetch-style handler. Use it as a plain server
77
+ * (`http.createServer(toNodeHandler(routes.handler))`), as Express or Connect
78
+ * middleware (`app.use("/cronwatch", toNodeHandler(routes.handler))`), from a
79
+ * Nest controller, or as a Firebase `onRequest` function.
80
+ *
81
+ * With `basePath`, requests outside it go to `next()` (or get a 404 when
82
+ * there is no next); without it, every request is answered. An error thrown
83
+ * by the fetch handler goes to `next(error)` when there is a next, and is
84
+ * otherwise answered with a plain 500.
85
+ */
86
+ declare function toNodeHandler(handler: FetchLike, options?: NodeHandlerOptions): NodeHandler;
87
+ /**
88
+ * Koa middleware for a fetch-style handler. Requests outside `basePath` (when
89
+ * given) go on to the next middleware. The rest are answered here with
90
+ * `ctx.respond = false`, so Koa leaves the response alone. Place it before
91
+ * any body parser; if one runs first, its `ctx.request.rawBody` or
92
+ * `ctx.request.body` is used. An error thrown by the fetch handler is thrown
93
+ * on to Koa before anything is written.
94
+ */
95
+ declare function toKoaMiddleware(handler: FetchLike, options?: NodeHandlerOptions): (ctx: KoaContextLike, next: () => Promise<unknown>) => Promise<void>;
96
+
97
+ export { type BodySource, type FetchLike, type KoaContextLike, type NodeHandler, type NodeHandlerOptions, type NodeNext, toKoaMiddleware, toNodeHandler, toRequest, writeResponse };
package/dist/node.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+
3
+ /**
4
+ * @cronwatch/sdk/node: serve a fetch-style handler (cw.routes().handler, or
5
+ * a job's handler()) from Node's http module and the frameworks built on it:
6
+ * a plain http server, Express and Connect, NestJS, Koa, Strapi and Firebase
7
+ * onRequest. The SDK core stays free of node: imports so it runs on Workers;
8
+ * this entry point is the only one that uses Node's http and stream types.
9
+ */
10
+
11
+ /** A fetch-style handler: a Request in, a Response out. */
12
+ type FetchLike = (request: Request) => Response | Promise<Response>;
13
+ interface NodeHandlerOptions {
14
+ /**
15
+ * Build the request URL from X-Forwarded-Proto and X-Forwarded-Host (the
16
+ * first value of each) when present, instead of the connection's scheme and
17
+ * the Host header. Only behind a proxy that sets or overwrites both, since
18
+ * a client can send them too. Default false.
19
+ */
20
+ trustProxy?: boolean;
21
+ /**
22
+ * Answer only requests at this path or under it, and pass the rest on: to
23
+ * `next()` as Express or Connect middleware, to the next Koa middleware,
24
+ * and with a 404 when there is no next. The path is read from
25
+ * `req.originalUrl` (so Express mount paths count) or `req.url`. Leave it
26
+ * out to answer every request the handler is given, which suits
27
+ * `app.use("/cronwatch", ...)`, a Nest controller and a Firebase function.
28
+ */
29
+ basePath?: string;
30
+ }
31
+ /** Called by Express and Connect for the next middleware, or with an error. */
32
+ type NodeNext = (error?: unknown) => void;
33
+ /** A handler for http.createServer, Express or Connect, Nest and Firebase onRequest. */
34
+ type NodeHandler = (req: IncomingMessage, res: ServerResponse, next?: NodeNext) => Promise<void>;
35
+ /** The parts of a Koa context the middleware uses, so the SDK needs no Koa types. */
36
+ interface KoaContextLike {
37
+ req: IncomingMessage;
38
+ res: ServerResponse;
39
+ respond?: boolean;
40
+ /** Koa's own copy of the URL, kept when koa-mount rewrites req.url. */
41
+ originalUrl?: string;
42
+ /** Set by koa-bodyparser and similar, when they run before this middleware. */
43
+ request?: {
44
+ body?: unknown;
45
+ rawBody?: unknown;
46
+ };
47
+ }
48
+ /** Where a request body may already be, when a parser has read the stream. */
49
+ interface BodySource {
50
+ /** The request target to use instead of req.originalUrl or req.url. */
51
+ url?: string;
52
+ /** The body's bytes as read (Firebase, Nest with rawBody: true, koa-bodyparser). */
53
+ rawBody?: unknown;
54
+ /** A body a parser has decoded (express.json, express.urlencoded). */
55
+ body?: unknown;
56
+ }
57
+ /**
58
+ * A fetch Request for a Node request. The URL is the Host header (or the
59
+ * forwarded host and scheme with trustProxy) plus req.originalUrl or req.url.
60
+ * The body, for anything but GET and HEAD, is the first of: `rawBody` (from
61
+ * the source, or req.rawBody), the request stream when nothing has read it,
62
+ * or a body a parser decoded (req.body), re-encoded as JSON or a urlencoded
63
+ * form to match its content type.
64
+ */
65
+ declare function toRequest(req: IncomingMessage, options?: {
66
+ trustProxy?: boolean;
67
+ signal?: AbortSignal;
68
+ }, source?: BodySource): Request;
69
+ /**
70
+ * Writes a fetch Response to a Node response: the status, every header
71
+ * (each Set-Cookie as its own header line), and the body, streamed. A HEAD
72
+ * request gets the headers only.
73
+ */
74
+ declare function writeResponse(res: ServerResponse, response: Response, method?: string): Promise<void>;
75
+ /**
76
+ * A Node handler for a fetch-style handler. Use it as a plain server
77
+ * (`http.createServer(toNodeHandler(routes.handler))`), as Express or Connect
78
+ * middleware (`app.use("/cronwatch", toNodeHandler(routes.handler))`), from a
79
+ * Nest controller, or as a Firebase `onRequest` function.
80
+ *
81
+ * With `basePath`, requests outside it go to `next()` (or get a 404 when
82
+ * there is no next); without it, every request is answered. An error thrown
83
+ * by the fetch handler goes to `next(error)` when there is a next, and is
84
+ * otherwise answered with a plain 500.
85
+ */
86
+ declare function toNodeHandler(handler: FetchLike, options?: NodeHandlerOptions): NodeHandler;
87
+ /**
88
+ * Koa middleware for a fetch-style handler. Requests outside `basePath` (when
89
+ * given) go on to the next middleware. The rest are answered here with
90
+ * `ctx.respond = false`, so Koa leaves the response alone. Place it before
91
+ * any body parser; if one runs first, its `ctx.request.rawBody` or
92
+ * `ctx.request.body` is used. An error thrown by the fetch handler is thrown
93
+ * on to Koa before anything is written.
94
+ */
95
+ declare function toKoaMiddleware(handler: FetchLike, options?: NodeHandlerOptions): (ctx: KoaContextLike, next: () => Promise<unknown>) => Promise<void>;
96
+
97
+ export { type BodySource, type FetchLike, type KoaContextLike, type NodeHandler, type NodeHandlerOptions, type NodeNext, toKoaMiddleware, toNodeHandler, toRequest, writeResponse };
package/dist/node.js ADDED
@@ -0,0 +1,186 @@
1
+ import { Readable } from 'stream';
2
+ import { pipeline } from 'stream/promises';
3
+
4
+ // src/node.ts
5
+ function header(value) {
6
+ return Array.isArray(value) ? value[0] : value;
7
+ }
8
+ function firstValue(value) {
9
+ const first = header(value)?.split(",")[0]?.trim();
10
+ return first ? first : void 0;
11
+ }
12
+ function originFrom(proto, host) {
13
+ try {
14
+ const url = new URL(`${proto}://${host}`);
15
+ if (url.pathname !== "/" || url.username || url.password || url.search || url.hash) return null;
16
+ return url.origin;
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+ function pathAndQuery(target) {
22
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(target)) {
23
+ try {
24
+ const url = new URL(target);
25
+ return url.pathname + url.search;
26
+ } catch {
27
+ return "/";
28
+ }
29
+ }
30
+ return target.startsWith("/") ? target : `/${target}`;
31
+ }
32
+ function requestOrigin(req, trustProxy) {
33
+ const socket = req.socket;
34
+ let proto = socket?.encrypted === true ? "https" : "http";
35
+ let host = header(req.headers.host) ?? header(req.headers[":authority"]);
36
+ if (trustProxy) {
37
+ const fp = firstValue(req.headers["x-forwarded-proto"])?.toLowerCase();
38
+ if (fp === "http" || fp === "https") proto = fp;
39
+ const fh = firstValue(req.headers["x-forwarded-host"]);
40
+ if (fh && originFrom(proto, fh) !== null) host = fh;
41
+ }
42
+ return (host ? originFrom(proto, host) : null) ?? `${proto}://localhost`;
43
+ }
44
+ function unread(req) {
45
+ if (typeof req.on !== "function" || req.readable === false) return false;
46
+ return !req.readableEnded && req.readableDidRead !== true;
47
+ }
48
+ function reencode(parsed, headers) {
49
+ if (parsed === void 0 || parsed === null) return null;
50
+ if (typeof parsed === "string" || parsed instanceof Uint8Array) return parsed;
51
+ if (typeof parsed !== "object") return null;
52
+ const type = (headers.get("content-type") ?? "").toLowerCase();
53
+ let text = null;
54
+ if (type.includes("application/x-www-form-urlencoded")) {
55
+ const form = new URLSearchParams();
56
+ for (const [key, value] of Object.entries(parsed)) {
57
+ for (const v of Array.isArray(value) ? value : [value]) {
58
+ if (v !== void 0 && v !== null) form.append(key, typeof v === "object" ? JSON.stringify(v) : String(v));
59
+ }
60
+ }
61
+ text = form.toString();
62
+ } else if (type.includes("json")) {
63
+ text = JSON.stringify(parsed);
64
+ }
65
+ if (text === null) return null;
66
+ headers.delete("content-length");
67
+ headers.delete("transfer-encoding");
68
+ return text;
69
+ }
70
+ function toRequest(req, options = {}, source = {}) {
71
+ const node = req;
72
+ const target = source.url ?? node.originalUrl ?? req.url ?? "/";
73
+ const url = requestOrigin(req, options.trustProxy === true) + pathAndQuery(target);
74
+ const headers = new Headers();
75
+ for (const [name, value] of Object.entries(req.headers)) {
76
+ if (value === void 0 || name.startsWith(":")) continue;
77
+ if (Array.isArray(value)) for (const v of value) headers.append(name, v);
78
+ else headers.set(name, value);
79
+ }
80
+ const method = (req.method ?? "GET").toUpperCase();
81
+ const init = { method, headers };
82
+ if (options.signal) init.signal = options.signal;
83
+ if (method !== "GET" && method !== "HEAD") {
84
+ const raw = source.rawBody ?? node.rawBody;
85
+ if (raw instanceof Uint8Array) init.body = raw;
86
+ else if (typeof raw === "string") init.body = raw;
87
+ else if (unread(req)) {
88
+ init.body = Readable.toWeb(req);
89
+ init.duplex = "half";
90
+ } else {
91
+ const body = reencode(source.body !== void 0 ? source.body : node.body, headers);
92
+ if (body !== null) init.body = body;
93
+ else {
94
+ headers.delete("content-length");
95
+ headers.delete("transfer-encoding");
96
+ }
97
+ }
98
+ }
99
+ return new Request(url, init);
100
+ }
101
+ async function writeResponse(res, response, method = "GET") {
102
+ res.statusCode = response.status;
103
+ response.headers.forEach((value, name) => {
104
+ if (name !== "set-cookie") res.setHeader(name, value);
105
+ });
106
+ const cookies = response.headers.getSetCookie();
107
+ if (cookies.length > 0) res.setHeader("set-cookie", cookies);
108
+ if (response.body === null || method.toUpperCase() === "HEAD") {
109
+ if (response.body !== null) await response.body.cancel().catch(() => {
110
+ });
111
+ res.end();
112
+ return;
113
+ }
114
+ try {
115
+ await pipeline(Readable.fromWeb(response.body), res);
116
+ } catch {
117
+ }
118
+ }
119
+ function mountOf(basePath) {
120
+ return basePath === void 0 ? null : basePath.replace(/\/+$/, "");
121
+ }
122
+ function inMount(pathname, mount) {
123
+ return mount === null || mount === "" || pathname === mount || pathname.startsWith(`${mount}/`);
124
+ }
125
+ function pathnameOf(target) {
126
+ const path = pathAndQuery(target);
127
+ const q = path.search(/[?#]/);
128
+ return q === -1 ? path : path.slice(0, q);
129
+ }
130
+ function disconnectSignal(res) {
131
+ const controller = new AbortController();
132
+ res.once("close", () => {
133
+ if (!res.writableFinished) controller.abort();
134
+ });
135
+ return controller.signal;
136
+ }
137
+ function toNodeHandler(handler, options = {}) {
138
+ const mount = mountOf(options.basePath);
139
+ const trustProxy = options.trustProxy === true;
140
+ return async (req, res, next) => {
141
+ const node = req;
142
+ if (!inMount(pathnameOf(node.originalUrl ?? req.url ?? "/"), mount)) {
143
+ if (next) next();
144
+ else {
145
+ res.statusCode = 404;
146
+ res.setHeader("content-type", "text/plain; charset=utf-8");
147
+ res.end("Not found");
148
+ }
149
+ return;
150
+ }
151
+ try {
152
+ const request = toRequest(req, { trustProxy, signal: disconnectSignal(res) });
153
+ await writeResponse(res, await handler(request), request.method);
154
+ } catch (error) {
155
+ if (next) next(error);
156
+ else if (!res.headersSent) {
157
+ res.statusCode = 500;
158
+ res.setHeader("content-type", "text/plain; charset=utf-8");
159
+ res.end("Internal Server Error");
160
+ } else res.destroy(error instanceof Error ? error : void 0);
161
+ }
162
+ };
163
+ }
164
+ function toKoaMiddleware(handler, options = {}) {
165
+ const mount = mountOf(options.basePath);
166
+ const trustProxy = options.trustProxy === true;
167
+ return async (ctx, next) => {
168
+ const target = ctx.originalUrl ?? ctx.req.url ?? "/";
169
+ if (!inMount(pathnameOf(target), mount)) {
170
+ await next();
171
+ return;
172
+ }
173
+ const request = toRequest(ctx.req, { trustProxy, signal: disconnectSignal(ctx.res) }, {
174
+ url: target,
175
+ rawBody: ctx.request?.rawBody,
176
+ body: ctx.request?.body
177
+ });
178
+ const response = await handler(request);
179
+ ctx.respond = false;
180
+ await writeResponse(ctx.res, response, request.method);
181
+ };
182
+ }
183
+
184
+ export { toKoaMiddleware, toNodeHandler, toRequest, writeResponse };
185
+ //# sourceMappingURL=node.js.map
186
+ //# sourceMappingURL=node.js.map