@coursebuilder/server 0.1.0-canary.0.0c9beb2ae

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 (156) hide show
  1. package/LICENSE +21 -0
  2. package/dist/actions/checkout.d.ts +6 -0
  3. package/dist/actions/checkout.js +9 -0
  4. package/dist/actions/checkout.js.map +1 -0
  5. package/dist/actions/claimed.d.ts +6 -0
  6. package/dist/actions/claimed.js +8 -0
  7. package/dist/actions/claimed.js.map +1 -0
  8. package/dist/actions/create-magic-link.d.ts +6 -0
  9. package/dist/actions/create-magic-link.js +9 -0
  10. package/dist/actions/create-magic-link.js.map +1 -0
  11. package/dist/actions/index.d.ts +11 -0
  12. package/dist/actions/index.js +18 -0
  13. package/dist/actions/index.js.map +1 -0
  14. package/dist/actions/prices-coupon-for-code.d.ts +6 -0
  15. package/dist/actions/prices-coupon-for-code.js +63 -0
  16. package/dist/actions/prices-coupon-for-code.js.map +1 -0
  17. package/dist/actions/prices-formatted.d.ts +6 -0
  18. package/dist/actions/prices-formatted.js +8 -0
  19. package/dist/actions/prices-formatted.js.map +1 -0
  20. package/dist/actions/process-refund.d.ts +1 -0
  21. package/dist/actions/process-refund.js +8 -0
  22. package/dist/actions/process-refund.js.map +1 -0
  23. package/dist/actions/redeem.d.ts +1 -0
  24. package/dist/actions/redeem.js +8 -0
  25. package/dist/actions/redeem.js.map +1 -0
  26. package/dist/actions/transfer-purchase.d.ts +1 -0
  27. package/dist/actions/transfer-purchase.js +8 -0
  28. package/dist/actions/transfer-purchase.js.map +1 -0
  29. package/dist/actions/user-lookup.d.ts +6 -0
  30. package/dist/actions/user-lookup.js +9 -0
  31. package/dist/actions/user-lookup.js.map +1 -0
  32. package/dist/adapter-BtDS6LDw.d.ts +9 -0
  33. package/dist/chunk-22AMOZYZ.js +67 -0
  34. package/dist/chunk-22AMOZYZ.js.map +1 -0
  35. package/dist/chunk-4EQQSW57.js +32 -0
  36. package/dist/chunk-4EQQSW57.js.map +1 -0
  37. package/dist/chunk-4LZZLN6T.js +52 -0
  38. package/dist/chunk-4LZZLN6T.js.map +1 -0
  39. package/dist/chunk-4QQA3TC2.js +29 -0
  40. package/dist/chunk-4QQA3TC2.js.map +1 -0
  41. package/dist/chunk-4TSEPUSC.js +160 -0
  42. package/dist/chunk-4TSEPUSC.js.map +1 -0
  43. package/dist/chunk-5O4UO4IR.js +93 -0
  44. package/dist/chunk-5O4UO4IR.js.map +1 -0
  45. package/dist/chunk-7FCQOLKL.js +26 -0
  46. package/dist/chunk-7FCQOLKL.js.map +1 -0
  47. package/dist/chunk-AZLUMIIF.js +8 -0
  48. package/dist/chunk-AZLUMIIF.js.map +1 -0
  49. package/dist/chunk-E6HV6TNI.js +38 -0
  50. package/dist/chunk-E6HV6TNI.js.map +1 -0
  51. package/dist/chunk-ECNJPNPU.js +54 -0
  52. package/dist/chunk-ECNJPNPU.js.map +1 -0
  53. package/dist/chunk-F3KUYN5I.js +111 -0
  54. package/dist/chunk-F3KUYN5I.js.map +1 -0
  55. package/dist/chunk-G63YAJF2.js +107 -0
  56. package/dist/chunk-G63YAJF2.js.map +1 -0
  57. package/dist/chunk-JFR7XO72.js +7 -0
  58. package/dist/chunk-JFR7XO72.js.map +1 -0
  59. package/dist/chunk-KNC7QVGZ.js +26 -0
  60. package/dist/chunk-KNC7QVGZ.js.map +1 -0
  61. package/dist/chunk-ODSSRMD2.js +270 -0
  62. package/dist/chunk-ODSSRMD2.js.map +1 -0
  63. package/dist/chunk-VEKYIR7I.js +26 -0
  64. package/dist/chunk-VEKYIR7I.js.map +1 -0
  65. package/dist/chunk-W7YAT4NR.js +12 -0
  66. package/dist/chunk-W7YAT4NR.js.map +1 -0
  67. package/dist/chunk-WGAPYIUP.js +56 -0
  68. package/dist/chunk-WGAPYIUP.js.map +1 -0
  69. package/dist/chunk-WYNH2AWD.js +7 -0
  70. package/dist/chunk-WYNH2AWD.js.map +1 -0
  71. package/dist/chunk-Y5X5J55Z.js +384 -0
  72. package/dist/chunk-Y5X5J55Z.js.map +1 -0
  73. package/dist/chunk-YFLGJIMA.js +216 -0
  74. package/dist/chunk-YFLGJIMA.js.map +1 -0
  75. package/dist/chunk-ZFZNN4E5.js +7 -0
  76. package/dist/chunk-ZFZNN4E5.js.map +1 -0
  77. package/dist/create-inngest-middleware.d.ts +635 -0
  78. package/dist/create-inngest-middleware.js +12 -0
  79. package/dist/create-inngest-middleware.js.map +1 -0
  80. package/dist/http-router.d.ts +20 -0
  81. package/dist/http-router.js +24 -0
  82. package/dist/http-router.js.map +1 -0
  83. package/dist/http.d.ts +38 -0
  84. package/dist/http.js +32 -0
  85. package/dist/http.js.map +1 -0
  86. package/dist/index.d.ts +34 -0
  87. package/dist/index.js +365 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/magic-string.es-ENO43NON.js +1389 -0
  90. package/dist/magic-string.es-ENO43NON.js.map +1 -0
  91. package/dist/runtime/actions.d.ts +5 -0
  92. package/dist/runtime/actions.js +8 -0
  93. package/dist/runtime/actions.js.map +1 -0
  94. package/dist/runtime/assert.d.ts +15 -0
  95. package/dist/runtime/assert.js +8 -0
  96. package/dist/runtime/assert.js.map +1 -0
  97. package/dist/runtime/cookie.d.ts +8 -0
  98. package/dist/runtime/cookie.js +8 -0
  99. package/dist/runtime/cookie.js.map +1 -0
  100. package/dist/runtime/env.d.ts +9 -0
  101. package/dist/runtime/env.js +11 -0
  102. package/dist/runtime/env.js.map +1 -0
  103. package/dist/runtime/env.test.d.ts +2 -0
  104. package/dist/runtime/env.test.js +14956 -0
  105. package/dist/runtime/env.test.js.map +1 -0
  106. package/dist/runtime/init.d.ts +26 -0
  107. package/dist/runtime/init.js +14 -0
  108. package/dist/runtime/init.js.map +1 -0
  109. package/dist/runtime/logger.d.ts +4 -0
  110. package/dist/runtime/logger.js +10 -0
  111. package/dist/runtime/logger.js.map +1 -0
  112. package/dist/runtime/merge.d.ts +4 -0
  113. package/dist/runtime/merge.js +8 -0
  114. package/dist/runtime/merge.js.map +1 -0
  115. package/dist/runtime/providers.d.ts +21 -0
  116. package/dist/runtime/providers.js +9 -0
  117. package/dist/runtime/providers.js.map +1 -0
  118. package/dist/runtime/web.d.ts +29 -0
  119. package/dist/runtime/web.js +16 -0
  120. package/dist/runtime/web.js.map +1 -0
  121. package/package.json +65 -0
  122. package/src/actions/actions.test.ts +232 -0
  123. package/src/actions/checkout.ts +160 -0
  124. package/src/actions/claimed.ts +40 -0
  125. package/src/actions/create-magic-link.ts +78 -0
  126. package/src/actions/index.ts +401 -0
  127. package/src/actions/prices-coupon-for-code.ts +91 -0
  128. package/src/actions/prices-formatted.ts +313 -0
  129. package/src/actions/process-refund.ts +2 -0
  130. package/src/actions/redeem.ts +2 -0
  131. package/src/actions/transfer-purchase.ts +2 -0
  132. package/src/actions/user-lookup.ts +37 -0
  133. package/src/adapter.ts +28 -0
  134. package/src/create-inngest-middleware.ts +157 -0
  135. package/src/http-actions.ts +29 -0
  136. package/src/http-router.ts +79 -0
  137. package/src/http.test.ts +46 -0
  138. package/src/http.ts +83 -0
  139. package/src/index.test.ts +21 -0
  140. package/src/index.ts +44 -0
  141. package/src/mock-adapter.ts +398 -0
  142. package/src/mock-payment-provider.ts +56 -0
  143. package/src/resource-chat.ts +371 -0
  144. package/src/runtime/actions.ts +25 -0
  145. package/src/runtime/assert.ts +55 -0
  146. package/src/runtime/cookie.ts +12 -0
  147. package/src/runtime/env.test.ts +49 -0
  148. package/src/runtime/env.ts +63 -0
  149. package/src/runtime/init.ts +112 -0
  150. package/src/runtime/logger.ts +6 -0
  151. package/src/runtime/merge.ts +25 -0
  152. package/src/runtime/providers.ts +38 -0
  153. package/src/runtime/web.ts +199 -0
  154. package/src/streaming-chat-prompt-executor.ts +43 -0
  155. package/src/type-contracts.test.ts +167 -0
  156. package/src/web-body-parse.test.ts +55 -0
@@ -0,0 +1,160 @@
1
+ import {
2
+ isCourseBuilderAction
3
+ } from "./chunk-VEKYIR7I.js";
4
+ import {
5
+ logger
6
+ } from "./chunk-AZLUMIIF.js";
7
+
8
+ // src/runtime/web.ts
9
+ import { parse as parseCookie, serialize } from "cookie";
10
+ import { CourseBuilderError } from "@coursebuilder/core/errors";
11
+ async function getBody(req, config) {
12
+ const headers = Object.fromEntries(req.headers);
13
+ const isStripeWebhook = ["stripe-signature"].every((prop) => {
14
+ return prop in headers;
15
+ });
16
+ if (isStripeWebhook) {
17
+ let parsedBody;
18
+ const stripeProvider = config.providers.find((p) => p.id === "stripe");
19
+ parsedBody = await req.text();
20
+ stripeProvider?.options?.paymentsAdapter.verifyWebhookSignature(
21
+ parsedBody,
22
+ headers["stripe-signature"]
23
+ );
24
+ parsedBody = JSON.parse(parsedBody);
25
+ return parsedBody;
26
+ }
27
+ if (!("body" in req) || !req.body || req.method !== "POST")
28
+ return;
29
+ const contentType = req.headers.get("content-type");
30
+ if (contentType?.includes("application/json")) {
31
+ const rawBody = await req.text();
32
+ if (rawBody.trim().length === 0)
33
+ return void 0;
34
+ try {
35
+ return JSON.parse(rawBody);
36
+ } catch (e) {
37
+ logger.error(e instanceof Error ? e : new Error(String(e)), {
38
+ context: "web.body-parse",
39
+ method: req.method,
40
+ contentType
41
+ });
42
+ return void 0;
43
+ }
44
+ } else if (contentType?.includes("application/x-www-form-urlencoded")) {
45
+ const params = new URLSearchParams(await req.text());
46
+ return Object.fromEntries(params);
47
+ }
48
+ }
49
+ async function toInternalRequest(req, config) {
50
+ try {
51
+ config.basePath ?? (config.basePath = "/coursebuilder");
52
+ const url = new URL(req.url);
53
+ const { action, providerId } = parseActionAndProviderId(
54
+ url.pathname,
55
+ config.basePath
56
+ );
57
+ logger.debug("web.action-parsed", {
58
+ pathname: url.pathname,
59
+ action,
60
+ providerId: providerId ?? null,
61
+ searchParamCount: url.searchParams.size
62
+ });
63
+ const parsedCookies = parseCookie(req.headers.get("cookie") ?? "") ?? {};
64
+ logger.debug("web.request-parsed", {
65
+ action,
66
+ method: req.method,
67
+ providerId: providerId ?? null,
68
+ hasCookieHeader: Boolean(req.headers.get("cookie")),
69
+ parsedCookieCount: Object.keys(parsedCookies).length,
70
+ hasSl_ref: "sl_ref" in parsedCookies
71
+ });
72
+ return {
73
+ url,
74
+ action,
75
+ providerId,
76
+ method: req.method,
77
+ headers: Object.fromEntries(req.headers),
78
+ body: req.body ? await getBody(req, config) : void 0,
79
+ cookies: parsedCookies,
80
+ error: url.searchParams.get("error") ?? void 0,
81
+ query: Object.fromEntries(url.searchParams)
82
+ };
83
+ } catch (e) {
84
+ logger.error(e instanceof Error ? e : new Error(String(e)), {
85
+ context: "web.to-internal-request",
86
+ method: req.method,
87
+ url: req.url,
88
+ basePath: config.basePath
89
+ });
90
+ logger.debug("web.request-context", {
91
+ method: req.method,
92
+ url: req.url,
93
+ hasBody: Boolean(req.body),
94
+ hasCookieHeader: Boolean(req.headers.get("cookie"))
95
+ });
96
+ }
97
+ }
98
+ function parseActionAndProviderId(pathname, base) {
99
+ const a = pathname.match(new RegExp(`^${base}(.+)`));
100
+ if (a === null)
101
+ throw new UnknownAction(`Cannot parse action at ${pathname}`);
102
+ const [_, actionAndProviderId] = a;
103
+ const b = actionAndProviderId.replace(/^\//, "").split("/");
104
+ if (b.length !== 1 && b.length !== 2)
105
+ throw new UnknownAction(`**Cannot parse action at ${pathname}`);
106
+ const [action, providerId] = b;
107
+ if (!isCourseBuilderAction(action))
108
+ throw new UnknownAction(`***Cannot parse action at ${pathname}`);
109
+ if (providerId && ![
110
+ "webhook",
111
+ "srt",
112
+ "session",
113
+ "subscribe-to-list",
114
+ "checkout",
115
+ "redeem",
116
+ "subscriber",
117
+ "lookup",
118
+ "claimed",
119
+ "nameUpdate",
120
+ "transfer",
121
+ "refund",
122
+ "create-magic-link"
123
+ ].includes(action))
124
+ throw new UnknownAction(
125
+ `**** Cannot parse action at ${pathname}. add it to the list in web.ts?`
126
+ );
127
+ return { action, providerId };
128
+ }
129
+ var UnknownAction = class extends CourseBuilderError {
130
+ };
131
+ UnknownAction.type = "UnknownAction";
132
+ function toResponse(res) {
133
+ const headers = new Headers(res.headers);
134
+ res.cookies?.forEach((cookie) => {
135
+ const { name, value, options } = cookie;
136
+ const cookieHeader = serialize(name, value, options);
137
+ if (headers.has("Set-Cookie"))
138
+ headers.append("Set-Cookie", cookieHeader);
139
+ else
140
+ headers.set("Set-Cookie", cookieHeader);
141
+ });
142
+ let body = res.body;
143
+ if (headers.get("content-type") === "application/json")
144
+ body = JSON.stringify(res.body);
145
+ else if (headers.get("content-type") === "application/x-www-form-urlencoded")
146
+ body = new URLSearchParams(res.body).toString();
147
+ const status = res.redirect ? 302 : res.status ?? 200;
148
+ const response = new Response(body, { headers, status });
149
+ if (res.redirect)
150
+ response.headers.set("Location", res.redirect);
151
+ return response;
152
+ }
153
+
154
+ export {
155
+ toInternalRequest,
156
+ parseActionAndProviderId,
157
+ UnknownAction,
158
+ toResponse
159
+ };
160
+ //# sourceMappingURL=chunk-4TSEPUSC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/web.ts"],"sourcesContent":["import { parse as parseCookie, serialize } from 'cookie'\n\nimport { CourseBuilderError } from '@coursebuilder/core/errors'\nimport {\n\tCourseBuilderAction,\n\tRequestInternal,\n\tResponseInternal,\n} from '@coursebuilder/core/types'\n\nimport { CourseBuilderConfig } from '../http'\nimport { isCourseBuilderAction } from './actions'\nimport { logger } from './logger'\n\nasync function getBody(\n\treq: Request,\n\tconfig: CourseBuilderConfig,\n): Promise<Record<string, any> | undefined> {\n\tconst headers = Object.fromEntries(req.headers)\n\tconst isStripeWebhook = ['stripe-signature'].every((prop: string) => {\n\t\treturn prop in headers\n\t})\n\n\tif (isStripeWebhook) {\n\t\tlet parsedBody\n\t\tconst stripeProvider = config.providers.find((p: any) => p.id === 'stripe')\n\t\tparsedBody = await req.text()\n\t\tstripeProvider?.options?.paymentsAdapter.verifyWebhookSignature(\n\t\t\tparsedBody,\n\t\t\theaders['stripe-signature'],\n\t\t)\n\t\tparsedBody = JSON.parse(parsedBody)\n\t\treturn parsedBody\n\t}\n\n\tif (!('body' in req) || !req.body || req.method !== 'POST') return\n\n\tconst contentType = req.headers.get('content-type')\n\tif (contentType?.includes('application/json')) {\n\t\tconst rawBody = await req.text()\n\t\tif (rawBody.trim().length === 0) return undefined\n\n\t\ttry {\n\t\t\treturn JSON.parse(rawBody)\n\t\t} catch (e) {\n\t\t\tlogger.error(e instanceof Error ? e : new Error(String(e)), {\n\t\t\t\tcontext: 'web.body-parse',\n\t\t\t\tmethod: req.method,\n\t\t\t\tcontentType,\n\t\t\t})\n\t\t\treturn undefined\n\t\t}\n\t} else if (contentType?.includes('application/x-www-form-urlencoded')) {\n\t\tconst params = new URLSearchParams(await req.text())\n\t\treturn Object.fromEntries(params)\n\t}\n}\n\n/**\n * takes a `Request` object and converts it into an internal request object\n * that we use throughout the system\n * @param req\n * @param config\n * @returns\n */\nexport async function toInternalRequest(\n\treq: Request,\n\tconfig: CourseBuilderConfig,\n): Promise<RequestInternal | undefined> {\n\ttry {\n\t\tconfig.basePath ??= '/coursebuilder'\n\n\t\tconst url = new URL(req.url)\n\n\t\tconst { action, providerId } = parseActionAndProviderId(\n\t\t\turl.pathname,\n\t\t\tconfig.basePath,\n\t\t)\n\n\t\tlogger.debug('web.action-parsed', {\n\t\t\tpathname: url.pathname,\n\t\t\taction,\n\t\t\tproviderId: providerId ?? null,\n\t\t\tsearchParamCount: url.searchParams.size,\n\t\t})\n\n\t\tconst parsedCookies = parseCookie(req.headers.get('cookie') ?? '') ?? {}\n\n\t\tlogger.debug('web.request-parsed', {\n\t\t\taction,\n\t\t\tmethod: req.method,\n\t\t\tproviderId: providerId ?? null,\n\t\t\thasCookieHeader: Boolean(req.headers.get('cookie')),\n\t\t\tparsedCookieCount: Object.keys(parsedCookies).length,\n\t\t\thasSl_ref: 'sl_ref' in parsedCookies,\n\t\t})\n\n\t\treturn {\n\t\t\turl,\n\t\t\taction,\n\t\t\tproviderId,\n\t\t\tmethod: req.method as 'POST' | 'GET',\n\t\t\theaders: Object.fromEntries(req.headers),\n\t\t\tbody: req.body ? await getBody(req, config) : undefined,\n\t\t\tcookies: parsedCookies,\n\t\t\terror: url.searchParams.get('error') ?? undefined,\n\t\t\tquery: Object.fromEntries(url.searchParams),\n\t\t}\n\t} catch (e) {\n\t\tlogger.error(e instanceof Error ? e : new Error(String(e)), {\n\t\t\tcontext: 'web.to-internal-request',\n\t\t\tmethod: req.method,\n\t\t\turl: req.url,\n\t\t\tbasePath: config.basePath,\n\t\t})\n\t\tlogger.debug('web.request-context', {\n\t\t\tmethod: req.method,\n\t\t\turl: req.url,\n\t\t\thasBody: Boolean(req.body),\n\t\t\thasCookieHeader: Boolean(req.headers.get('cookie')),\n\t\t})\n\t}\n}\n\nexport function parseActionAndProviderId(\n\tpathname: string,\n\tbase: string,\n): {\n\taction: CourseBuilderAction\n\tproviderId?: string\n} {\n\tconst a = pathname.match(new RegExp(`^${base}(.+)`))\n\n\tif (a === null) throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n\tconst [_, actionAndProviderId] = a\n\n\tconst b = actionAndProviderId.replace(/^\\//, '').split('/')\n\n\tif (b.length !== 1 && b.length !== 2)\n\t\tthrow new UnknownAction(`**Cannot parse action at ${pathname}`)\n\n\tconst [action, providerId] = b\n\n\tif (!isCourseBuilderAction(action))\n\t\tthrow new UnknownAction(`***Cannot parse action at ${pathname}`)\n\n\tif (\n\t\tproviderId &&\n\t\t![\n\t\t\t'webhook',\n\t\t\t'srt',\n\t\t\t'session',\n\t\t\t'subscribe-to-list',\n\t\t\t'checkout',\n\t\t\t'redeem',\n\t\t\t'subscriber',\n\t\t\t'lookup',\n\t\t\t'claimed',\n\t\t\t'nameUpdate',\n\t\t\t'transfer',\n\t\t\t'refund',\n\t\t\t'create-magic-link',\n\t\t].includes(action)\n\t)\n\t\tthrow new UnknownAction(\n\t\t\t`**** Cannot parse action at ${pathname}. add it to the list in web.ts?`,\n\t\t)\n\n\treturn { action, providerId }\n}\n\nexport class UnknownAction extends CourseBuilderError {\n\tstatic type = 'UnknownAction'\n}\n\nexport function toResponse(res: ResponseInternal): Response {\n\tconst headers = new Headers(res.headers)\n\n\tres.cookies?.forEach((cookie) => {\n\t\tconst { name, value, options } = cookie\n\t\tconst cookieHeader = serialize(name, value, options)\n\t\tif (headers.has('Set-Cookie')) headers.append('Set-Cookie', cookieHeader)\n\t\telse headers.set('Set-Cookie', cookieHeader)\n\t})\n\n\tlet body = res.body\n\n\tif (headers.get('content-type') === 'application/json')\n\t\tbody = JSON.stringify(res.body)\n\telse if (headers.get('content-type') === 'application/x-www-form-urlencoded')\n\t\tbody = new URLSearchParams(res.body).toString()\n\n\tconst status = res.redirect ? 302 : (res.status ?? 200)\n\tconst response = new Response(body, { headers, status })\n\n\tif (res.redirect) response.headers.set('Location', res.redirect)\n\n\treturn response\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,93 @@
1
+ import {
2
+ defaultCookies
3
+ } from "./chunk-W7YAT4NR.js";
4
+ import {
5
+ parseProviders
6
+ } from "./chunk-4QQA3TC2.js";
7
+ import {
8
+ merge
9
+ } from "./chunk-KNC7QVGZ.js";
10
+ import {
11
+ logger
12
+ } from "./chunk-AZLUMIIF.js";
13
+
14
+ // src/runtime/init.ts
15
+ import { AdapterError } from "@coursebuilder/core/errors";
16
+ async function init({
17
+ url,
18
+ courseBuilderOptions,
19
+ providerId,
20
+ action,
21
+ isPost,
22
+ cookies: reqCookies
23
+ }) {
24
+ const { providers, provider } = parseProviders({
25
+ providers: courseBuilderOptions.providers,
26
+ url,
27
+ providerId,
28
+ options: courseBuilderOptions
29
+ });
30
+ const maxAge = 30 * 24 * 60 * 60;
31
+ const options = {
32
+ debug: false,
33
+ pages: {},
34
+ ...courseBuilderOptions,
35
+ url,
36
+ action,
37
+ // @ts-expect-errors
38
+ provider,
39
+ cookies: merge(
40
+ {},
41
+ defaultCookies(
42
+ courseBuilderOptions.useSecureCookies ?? url.protocol === "https:"
43
+ ),
44
+ courseBuilderOptions.cookies || {},
45
+ reqCookies
46
+ ),
47
+ providers,
48
+ adapter: adapterErrorHandler(courseBuilderOptions.adapter, logger),
49
+ callbacks: { ...defaultCallbacks, ...courseBuilderOptions.callbacks },
50
+ logger,
51
+ getCurrentUser: courseBuilderOptions.getCurrentUser
52
+ };
53
+ const cookies = Object.entries(reqCookies || {}).filter((entry) => typeof entry[1] === "string").map(([name, value]) => ({
54
+ name,
55
+ value,
56
+ options: {}
57
+ }));
58
+ logger.debug("init.request-cookies", {
59
+ rawCookieKeys: Object.keys(reqCookies || {}),
60
+ convertedCount: cookies.length,
61
+ hasSl_ref: cookies.some((c) => c.name === "sl_ref")
62
+ });
63
+ return { options, cookies };
64
+ }
65
+ var defaultCallbacks = {
66
+ session(payload) {
67
+ return payload;
68
+ }
69
+ };
70
+ function adapterErrorHandler(adapter, logger2) {
71
+ if (!adapter)
72
+ return;
73
+ return Object.keys(adapter).reduce((acc, name) => {
74
+ acc[name] = async (...args) => {
75
+ try {
76
+ logger2.debug(`adapter_${name}`, { args });
77
+ const method = adapter[name];
78
+ return await method(...args);
79
+ } catch (e) {
80
+ const error = new AdapterError(e);
81
+ logger2.error(error);
82
+ throw error;
83
+ }
84
+ };
85
+ return acc;
86
+ }, {});
87
+ }
88
+
89
+ export {
90
+ init,
91
+ defaultCallbacks
92
+ };
93
+ //# sourceMappingURL=chunk-5O4UO4IR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/init.ts"],"sourcesContent":["import { AdapterError } from '@coursebuilder/core/errors'\nimport {\n\tCallbacksOptions,\n\tInternalOptions,\n\tRequestInternal,\n} from '@coursebuilder/core/types'\n\nimport { CourseBuilderConfig } from '../http'\nimport * as cookie from './cookie.js'\nimport { logger, LoggerInstance } from './logger'\nimport { merge } from './merge'\nimport parseProviders from './providers'\n\ninterface InitParams {\n\turl: URL\n\tcourseBuilderOptions: CourseBuilderConfig\n\tproviderId?: string\n\taction: InternalOptions['action']\n\tisPost: boolean\n\tcookies: RequestInternal['cookies']\n}\n\nexport async function init({\n\turl,\n\tcourseBuilderOptions,\n\tproviderId,\n\taction,\n\tisPost,\n\tcookies: reqCookies,\n}: InitParams): Promise<{\n\toptions: InternalOptions\n\tcookies: cookie.Cookie[]\n}> {\n\tconst { providers, provider } = parseProviders({\n\t\tproviders: courseBuilderOptions.providers,\n\t\turl,\n\t\tproviderId,\n\t\toptions: courseBuilderOptions,\n\t})\n\n\tconst maxAge = 30 * 24 * 60 * 60\n\n\tconst options: InternalOptions = {\n\t\tdebug: false,\n\t\tpages: {},\n\t\t...courseBuilderOptions,\n\t\turl,\n\t\taction,\n\t\t// @ts-expect-errors\n\t\tprovider,\n\t\tcookies: merge(\n\t\t\t{},\n\t\t\tcookie.defaultCookies(\n\t\t\t\tcourseBuilderOptions.useSecureCookies ?? url.protocol === 'https:',\n\t\t\t),\n\t\t\tcourseBuilderOptions.cookies || {},\n\t\t\treqCookies,\n\t\t),\n\t\tproviders,\n\t\tadapter: adapterErrorHandler(courseBuilderOptions.adapter, logger),\n\t\tcallbacks: { ...defaultCallbacks, ...courseBuilderOptions.callbacks },\n\t\tlogger,\n\t\tgetCurrentUser: courseBuilderOptions.getCurrentUser,\n\t}\n\n\t// Convert request cookies to Cookie[] format for actions\n\tconst cookies: cookie.Cookie[] = Object.entries(reqCookies || {})\n\t\t.filter((entry): entry is [string, string] => typeof entry[1] === 'string')\n\t\t.map(([name, value]) => ({\n\t\t\tname,\n\t\t\tvalue,\n\t\t\toptions: {},\n\t\t}))\n\n\tlogger.debug('init.request-cookies', {\n\t\trawCookieKeys: Object.keys(reqCookies || {}),\n\t\tconvertedCount: cookies.length,\n\t\thasSl_ref: cookies.some((c) => c.name === 'sl_ref'),\n\t})\n\n\treturn { options, cookies }\n}\n\ntype Method = (...args: any[]) => Promise<any>\n\nexport const defaultCallbacks: CallbacksOptions = {\n\tsession(payload) {\n\t\treturn payload\n\t},\n}\n\nfunction adapterErrorHandler(\n\tadapter: CourseBuilderConfig['adapter'],\n\tlogger: LoggerInstance,\n) {\n\tif (!adapter) return\n\n\treturn Object.keys(adapter).reduce<any>((acc, name) => {\n\t\tacc[name] = async (...args: any[]) => {\n\t\t\ttry {\n\t\t\t\tlogger.debug(`adapter_${name}`, { args })\n\t\t\t\tconst method: Method = adapter[name as keyof Method]\n\t\t\t\treturn await method(...args)\n\t\t\t} catch (e) {\n\t\t\t\tconst error = new AdapterError(e as Error)\n\t\t\t\tlogger.error(error)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t\treturn acc\n\t}, {})\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,oBAAoB;AAsB7B,eAAsB,KAAK;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACV,GAGG;AACF,QAAM,EAAE,WAAW,SAAS,IAAI,eAAe;AAAA,IAC9C,WAAW,qBAAqB;AAAA,IAChC;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACV,CAAC;AAED,QAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,QAAM,UAA2B;AAAA,IAChC,OAAO;AAAA,IACP,OAAO,CAAC;AAAA,IACR,GAAG;AAAA,IACH;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA,SAAS;AAAA,MACR,CAAC;AAAA,MACM;AAAA,QACN,qBAAqB,oBAAoB,IAAI,aAAa;AAAA,MAC3D;AAAA,MACA,qBAAqB,WAAW,CAAC;AAAA,MACjC;AAAA,IACD;AAAA,IACA;AAAA,IACA,SAAS,oBAAoB,qBAAqB,SAAS,MAAM;AAAA,IACjE,WAAW,EAAE,GAAG,kBAAkB,GAAG,qBAAqB,UAAU;AAAA,IACpE;AAAA,IACA,gBAAgB,qBAAqB;AAAA,EACtC;AAGA,QAAM,UAA2B,OAAO,QAAQ,cAAc,CAAC,CAAC,EAC9D,OAAO,CAAC,UAAqC,OAAO,MAAM,CAAC,MAAM,QAAQ,EACzE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;AAAA,IACxB;AAAA,IACA;AAAA,IACA,SAAS,CAAC;AAAA,EACX,EAAE;AAEH,SAAO,MAAM,wBAAwB;AAAA,IACpC,eAAe,OAAO,KAAK,cAAc,CAAC,CAAC;AAAA,IAC3C,gBAAgB,QAAQ;AAAA,IACxB,WAAW,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AAAA,EACnD,CAAC;AAED,SAAO,EAAE,SAAS,QAAQ;AAC3B;AAIO,IAAM,mBAAqC;AAAA,EACjD,QAAQ,SAAS;AAChB,WAAO;AAAA,EACR;AACD;AAEA,SAAS,oBACR,SACAA,SACC;AACD,MAAI,CAAC;AAAS;AAEd,SAAO,OAAO,KAAK,OAAO,EAAE,OAAY,CAAC,KAAK,SAAS;AACtD,QAAI,IAAI,IAAI,UAAU,SAAgB;AACrC,UAAI;AACH,QAAAA,QAAO,MAAM,WAAW,IAAI,IAAI,EAAE,KAAK,CAAC;AACxC,cAAM,SAAiB,QAAQ,IAAoB;AACnD,eAAO,MAAM,OAAO,GAAG,IAAI;AAAA,MAC5B,SAAS,GAAG;AACX,cAAM,QAAQ,IAAI,aAAa,CAAU;AACzC,QAAAA,QAAO,MAAM,KAAK;AAClB,cAAM;AAAA,MACP;AAAA,IACD;AACA,WAAO;AAAA,EACR,GAAG,CAAC,CAAC;AACN;","names":["logger"]}
@@ -0,0 +1,26 @@
1
+ // src/actions/claimed.ts
2
+ import { z } from "zod";
3
+ async function claimed(request, cookies, options) {
4
+ if (!options.adapter)
5
+ throw new Error("Adapter not found");
6
+ const bulkCouponId = z.string().optional().parse(request.query?.bulkCouponId || request.body?.bulkCouponId);
7
+ if (!bulkCouponId)
8
+ throw new Error("Bulk coupon ID is required");
9
+ const purchases = await options.adapter.getPurchasesForBulkCouponId(bulkCouponId);
10
+ const users = purchases.map((purchase) => {
11
+ return {
12
+ name: purchase.user.name,
13
+ email: purchase.user.email
14
+ };
15
+ });
16
+ return {
17
+ body: users,
18
+ headers: { "Content-Type": "application/json" },
19
+ cookies
20
+ };
21
+ }
22
+
23
+ export {
24
+ claimed
25
+ };
26
+ //# sourceMappingURL=chunk-7FCQOLKL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/actions/claimed.ts"],"sourcesContent":["import { z } from 'zod'\n\nimport type {\n\tInternalOptions,\n\tRequestInternal,\n\tResponseInternal,\n} from '@coursebuilder/core/types'\n\nimport type { Cookie } from '../runtime/cookie'\n\nexport async function claimed(\n\trequest: RequestInternal,\n\tcookies: Cookie[],\n\toptions: InternalOptions,\n): Promise<ResponseInternal> {\n\tif (!options.adapter) throw new Error('Adapter not found')\n\n\tconst bulkCouponId = z\n\t\t.string()\n\t\t.optional()\n\t\t.parse(request.query?.bulkCouponId || request.body?.bulkCouponId)\n\n\tif (!bulkCouponId) throw new Error('Bulk coupon ID is required')\n\n\tconst purchases =\n\t\tawait options.adapter.getPurchasesForBulkCouponId(bulkCouponId)\n\n\tconst users = purchases.map((purchase) => {\n\t\treturn {\n\t\t\tname: purchase.user.name,\n\t\t\temail: purchase.user.email,\n\t\t}\n\t})\n\n\treturn {\n\t\tbody: users,\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tcookies,\n\t}\n}\n"],"mappings":";AAAA,SAAS,SAAS;AAUlB,eAAsB,QACrB,SACA,SACA,SAC4B;AAC5B,MAAI,CAAC,QAAQ;AAAS,UAAM,IAAI,MAAM,mBAAmB;AAEzD,QAAM,eAAe,EACnB,OAAO,EACP,SAAS,EACT,MAAM,QAAQ,OAAO,gBAAgB,QAAQ,MAAM,YAAY;AAEjE,MAAI,CAAC;AAAc,UAAM,IAAI,MAAM,4BAA4B;AAE/D,QAAM,YACL,MAAM,QAAQ,QAAQ,4BAA4B,YAAY;AAE/D,QAAM,QAAQ,UAAU,IAAI,CAAC,aAAa;AACzC,WAAO;AAAA,MACN,MAAM,SAAS,KAAK;AAAA,MACpB,OAAO,SAAS,KAAK;AAAA,IACtB;AAAA,EACD,CAAC;AAED,SAAO;AAAA,IACN,MAAM;AAAA,IACN,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C;AAAA,EACD;AACD;","names":[]}
@@ -0,0 +1,8 @@
1
+ // src/runtime/logger.ts
2
+ import { logger, setLogger } from "@coursebuilder/utils/logger";
3
+
4
+ export {
5
+ logger,
6
+ setLogger
7
+ };
8
+ //# sourceMappingURL=chunk-AZLUMIIF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/logger.ts"],"sourcesContent":["export { logger, setLogger } from '@coursebuilder/utils/logger'\nexport type {\n\tLogAttributes,\n\tLoggerInstance,\n\tWarningCode,\n} from '@coursebuilder/utils/logger'\n"],"mappings":";AAAA,SAAS,QAAQ,iBAAiB;","names":[]}
@@ -0,0 +1,38 @@
1
+ // src/runtime/assert.ts
2
+ import {
3
+ MissingAdapterMethods
4
+ } from "@coursebuilder/core/errors";
5
+ var hasTranscript = false;
6
+ var warned = false;
7
+ function assertConfig(request, options) {
8
+ const { url } = request;
9
+ const warnings = [];
10
+ if (!warned && options.debug)
11
+ warnings.push("debug-enabled");
12
+ for (const p of options.providers) {
13
+ const provider = typeof p === "function" ? p() : p;
14
+ if (provider.type === "transcription") {
15
+ hasTranscript = true;
16
+ }
17
+ }
18
+ const { adapter } = options;
19
+ let requiredMethods = [];
20
+ if (hasTranscript) {
21
+ }
22
+ if (adapter) {
23
+ const missing = requiredMethods.filter((m) => !(m in adapter));
24
+ if (missing.length) {
25
+ return new MissingAdapterMethods(
26
+ `Required adapter methods were missing: ${missing.join(", ")}`
27
+ );
28
+ }
29
+ }
30
+ if (!warned)
31
+ warned = true;
32
+ return warnings;
33
+ }
34
+
35
+ export {
36
+ assertConfig
37
+ };
38
+ //# sourceMappingURL=chunk-E6HV6TNI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/assert.ts"],"sourcesContent":["import type { ServerAdapter } from '../adapter'\nimport {\n\tMissingAdapter,\n\tMissingAdapterMethods,\n\tUnsupportedStrategy,\n} from '@coursebuilder/core/errors'\nimport { RequestInternal } from '@coursebuilder/core/types'\n\nimport { CourseBuilderConfig } from '../http'\nimport { WarningCode } from './logger'\n\ntype ConfigError = MissingAdapter | MissingAdapterMethods | UnsupportedStrategy\n\nlet hasTranscript = false\nlet warned = false\n\nexport function assertConfig(\n\trequest: RequestInternal,\n\toptions: CourseBuilderConfig,\n): ConfigError | WarningCode[] {\n\tconst { url } = request\n\tconst warnings: WarningCode[] = []\n\n\tif (!warned && options.debug) warnings.push('debug-enabled')\n\n\t// Keep track of webauthn providers that use conditional UI\n\n\tfor (const p of options.providers) {\n\t\tconst provider = typeof p === 'function' ? p() : p\n\t\tif (provider.type === 'transcription') {\n\t\t\thasTranscript = true\n\t\t}\n\t}\n\n\tconst { adapter } = options\n\n\tlet requiredMethods: (keyof ServerAdapter)[] = []\n\n\tif (hasTranscript) {\n\t}\n\n\tif (adapter) {\n\t\tconst missing = requiredMethods.filter((m) => !(m in adapter))\n\n\t\tif (missing.length) {\n\t\t\treturn new MissingAdapterMethods(\n\t\t\t\t`Required adapter methods were missing: ${missing.join(', ')}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tif (!warned) warned = true\n\n\treturn warnings\n}\n"],"mappings":";AACA;AAAA,EAEC;AAAA,OAEM;AAQP,IAAI,gBAAgB;AACpB,IAAI,SAAS;AAEN,SAAS,aACf,SACA,SAC8B;AAC9B,QAAM,EAAE,IAAI,IAAI;AAChB,QAAM,WAA0B,CAAC;AAEjC,MAAI,CAAC,UAAU,QAAQ;AAAO,aAAS,KAAK,eAAe;AAI3D,aAAW,KAAK,QAAQ,WAAW;AAClC,UAAM,WAAW,OAAO,MAAM,aAAa,EAAE,IAAI;AACjD,QAAI,SAAS,SAAS,iBAAiB;AACtC,sBAAgB;AAAA,IACjB;AAAA,EACD;AAEA,QAAM,EAAE,QAAQ,IAAI;AAEpB,MAAI,kBAA2C,CAAC;AAEhD,MAAI,eAAe;AAAA,EACnB;AAEA,MAAI,SAAS;AACZ,UAAM,UAAU,gBAAgB,OAAO,CAAC,MAAM,EAAE,KAAK,QAAQ;AAE7D,QAAI,QAAQ,QAAQ;AACnB,aAAO,IAAI;AAAA,QACV,0CAA0C,QAAQ,KAAK,IAAI,CAAC;AAAA,MAC7D;AAAA,IACD;AAAA,EACD;AAEA,MAAI,CAAC;AAAQ,aAAS;AAEtB,SAAO;AACR;","names":[]}
@@ -0,0 +1,54 @@
1
+ import {
2
+ assertConfig
3
+ } from "./chunk-E6HV6TNI.js";
4
+ import {
5
+ CourseBuilderInternal
6
+ } from "./chunk-G63YAJF2.js";
7
+ import {
8
+ toInternalRequest,
9
+ toResponse
10
+ } from "./chunk-4TSEPUSC.js";
11
+ import {
12
+ logger,
13
+ setLogger
14
+ } from "./chunk-AZLUMIIF.js";
15
+
16
+ // src/http.ts
17
+ async function CourseBuilder(request, config) {
18
+ setLogger(config.logger, config.debug);
19
+ const internalRequest = await toInternalRequest(request, config);
20
+ if (!internalRequest) {
21
+ return Response.json("Bad request", { status: 400 });
22
+ }
23
+ const warningsOrError = assertConfig(internalRequest, config);
24
+ if (Array.isArray(warningsOrError)) {
25
+ warningsOrError.forEach((code) => logger.warn(code));
26
+ } else if (warningsOrError) {
27
+ logger.error(warningsOrError);
28
+ return Response.json("Internal Server Error", { status: 500 });
29
+ }
30
+ const isRedirect = request.headers?.has("X-Course-Builder-Return-Redirect");
31
+ try {
32
+ const internalResponse = await CourseBuilderInternal(
33
+ internalRequest,
34
+ config
35
+ );
36
+ const response = toResponse(internalResponse);
37
+ const url = response.headers.get("Location");
38
+ if (!isRedirect || !url)
39
+ return response;
40
+ return Response.json({ url }, { headers: response.headers });
41
+ } catch (e) {
42
+ logger.error(e);
43
+ if (request.method === "POST" && internalRequest.action === "session")
44
+ return Response.json(null, { status: 400 });
45
+ return Response.json(`something broke: ${e.message}`, {
46
+ status: 400
47
+ });
48
+ }
49
+ }
50
+
51
+ export {
52
+ CourseBuilder
53
+ };
54
+ //# sourceMappingURL=chunk-ECNJPNPU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/http.ts"],"sourcesContent":["import type { Inngest } from 'inngest'\n\nimport type { ServerAdapter } from './adapter'\nimport type { CourseBuilderAuthConfig } from '@coursebuilder/core/auth'\nimport type { Provider } from '@coursebuilder/core/providers'\nimport type { User } from '@coursebuilder/core/schemas'\nimport type {\n\tCallbacksOptions,\n\tCookiesOptions,\n} from '@coursebuilder/core/types'\n\nimport { CourseBuilderInternal } from './http-router'\nimport { assertConfig } from './runtime/assert'\nimport { createActionURL, setEnvDefaults } from './runtime/env.js'\nimport { logger, setLogger, type LoggerInstance } from './runtime/logger'\nimport { toInternalRequest, toResponse } from './runtime/web'\n\nexport { createActionURL, setEnvDefaults }\n\n/**\n * Handle a Course Builder HTTP request.\n *\n * @param request - Web-standard request to route.\n * @param config - Course Builder server runtime configuration.\n * @returns The web-standard response produced by the selected action.\n */\nexport async function CourseBuilder(\n\trequest: Request,\n\tconfig: CourseBuilderConfig,\n): Promise<Response> {\n\tsetLogger(config.logger, config.debug)\n\n\tconst internalRequest = await toInternalRequest(request, config)\n\tif (!internalRequest) {\n\t\treturn Response.json('Bad request', { status: 400 })\n\t}\n\n\tconst warningsOrError = assertConfig(internalRequest, config)\n\n\tif (Array.isArray(warningsOrError)) {\n\t\twarningsOrError.forEach((code) => logger.warn(code))\n\t} else if (warningsOrError) {\n\t\tlogger.error(warningsOrError)\n\t\treturn Response.json('Internal Server Error', { status: 500 })\n\t}\n\tconst isRedirect = request.headers?.has('X-Course-Builder-Return-Redirect')\n\n\ttry {\n\t\tconst internalResponse = await CourseBuilderInternal(\n\t\t\tinternalRequest,\n\t\t\tconfig,\n\t\t)\n\n\t\tconst response = toResponse(internalResponse)\n\t\tconst url = response.headers.get('Location')\n\t\tif (!isRedirect || !url) return response\n\n\t\treturn Response.json({ url }, { headers: response.headers })\n\t} catch (e) {\n\t\tlogger.error(e as Error)\n\t\tif (request.method === 'POST' && internalRequest.action === 'session')\n\t\t\treturn Response.json(null, { status: 400 })\n\t\treturn Response.json(`something broke: ${(e as Error).message}`, {\n\t\t\tstatus: 400,\n\t\t})\n\t}\n}\n\n/** Runtime configuration consumed by the server-owned HTTP entry point. */\nexport interface CourseBuilderConfig {\n\tcallbacks?: Partial<CallbacksOptions>\n\tadapter?: ServerAdapter\n\tproviders: Provider[]\n\tdebug?: boolean\n\tbasePath?: string\n\tlogger?: LoggerInstance\n\tcookies?: Partial<CookiesOptions>\n\tuseSecureCookies?: boolean\n\tinngest?: Inngest\n\tgetCurrentUser?: () => Promise<User | null>\n\tauthConfig: CourseBuilderAuthConfig\n\tbaseUrl: string\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0BA,eAAsB,cACrB,SACA,QACoB;AACpB,YAAU,OAAO,QAAQ,OAAO,KAAK;AAErC,QAAM,kBAAkB,MAAM,kBAAkB,SAAS,MAAM;AAC/D,MAAI,CAAC,iBAAiB;AACrB,WAAO,SAAS,KAAK,eAAe,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpD;AAEA,QAAM,kBAAkB,aAAa,iBAAiB,MAAM;AAE5D,MAAI,MAAM,QAAQ,eAAe,GAAG;AACnC,oBAAgB,QAAQ,CAAC,SAAS,OAAO,KAAK,IAAI,CAAC;AAAA,EACpD,WAAW,iBAAiB;AAC3B,WAAO,MAAM,eAAe;AAC5B,WAAO,SAAS,KAAK,yBAAyB,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9D;AACA,QAAM,aAAa,QAAQ,SAAS,IAAI,kCAAkC;AAE1E,MAAI;AACH,UAAM,mBAAmB,MAAM;AAAA,MAC9B;AAAA,MACA;AAAA,IACD;AAEA,UAAM,WAAW,WAAW,gBAAgB;AAC5C,UAAM,MAAM,SAAS,QAAQ,IAAI,UAAU;AAC3C,QAAI,CAAC,cAAc,CAAC;AAAK,aAAO;AAEhC,WAAO,SAAS,KAAK,EAAE,IAAI,GAAG,EAAE,SAAS,SAAS,QAAQ,CAAC;AAAA,EAC5D,SAAS,GAAG;AACX,WAAO,MAAM,CAAU;AACvB,QAAI,QAAQ,WAAW,UAAU,gBAAgB,WAAW;AAC3D,aAAO,SAAS,KAAK,MAAM,EAAE,QAAQ,IAAI,CAAC;AAC3C,WAAO,SAAS,KAAK,oBAAqB,EAAY,OAAO,IAAI;AAAA,MAChE,QAAQ;AAAA,IACT,CAAC;AAAA,EACF;AACD;","names":[]}
@@ -0,0 +1,111 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-AZLUMIIF.js";
4
+
5
+ // src/actions/checkout.ts
6
+ import { buildCheckoutAttribution } from "@coursebuilder/commerce/checkout-attribution";
7
+ import { CheckoutParamsSchema } from "@coursebuilder/commerce/stripe-checkout";
8
+ var searchParamsWithoutNil = (params) => {
9
+ return new URLSearchParams(
10
+ Object.entries(params).flatMap(([key, value]) => {
11
+ if (value === void 0 || value === null)
12
+ return [];
13
+ return [[key, String(value)]];
14
+ })
15
+ );
16
+ };
17
+ async function checkout(request, cookies, options) {
18
+ const response = {
19
+ body: null,
20
+ headers: { "Content-Type": "application/json" },
21
+ cookies
22
+ };
23
+ const shortlinkRefCookie = cookies.find((c) => c.name === "sl_ref");
24
+ const shortlinkRef = shortlinkRefCookie?.value;
25
+ const ftAttrCookie = cookies.find((c) => c.name === "ft_attr");
26
+ let firstTouch = {};
27
+ try {
28
+ if (ftAttrCookie?.value) {
29
+ firstTouch = JSON.parse(decodeURIComponent(ftAttrCookie.value));
30
+ }
31
+ } catch {
32
+ }
33
+ const ltAttrCookie = cookies.find((c) => c.name === "lt_attr");
34
+ let lastTouch = {};
35
+ try {
36
+ if (ltAttrCookie?.value) {
37
+ lastTouch = JSON.parse(decodeURIComponent(ltAttrCookie.value));
38
+ }
39
+ } catch {
40
+ }
41
+ const selfReportedSource = typeof request.query?.selfReportedSource === "string" ? request.query.selfReportedSource.slice(0, 500) : void 0;
42
+ const checkoutAttribution = buildCheckoutAttribution({
43
+ firstTouch,
44
+ lastTouch,
45
+ shortlinkRef,
46
+ selfReportedSource
47
+ });
48
+ logger.debug("checkout.cookies-received", {
49
+ cookieCount: cookies.length,
50
+ cookieNames: cookies.map((c) => c.name),
51
+ hasShortlinkRef: Boolean(shortlinkRef),
52
+ hasAttributionSnapshot: Boolean(checkoutAttribution.attributionSnapshot)
53
+ });
54
+ const checkoutParamsParsed = CheckoutParamsSchema.safeParse({
55
+ ...request.query,
56
+ country: request.query?.country || request.headers?.["x-vercel-ip-country"] || process.env.DEFAULT_COUNTRY || "US",
57
+ ip_address: request.query?.ip_address || request.headers?.["x-forwarded-for"] || "0.0.0.0",
58
+ organizationId: request.query?.organizationId,
59
+ ...selfReportedSource && { selfReportedSource },
60
+ ...checkoutAttribution
61
+ });
62
+ if (!checkoutParamsParsed.success) {
63
+ const parseError = new Error("Error parsing checkout params");
64
+ logger.error(parseError, {
65
+ context: "checkout.params-parse",
66
+ issueCount: checkoutParamsParsed.error.issues.length,
67
+ issuePaths: checkoutParamsParsed.error.issues.map(
68
+ (issue) => issue.path.join(".")
69
+ ),
70
+ productId: typeof request.query?.productId === "string" ? request.query.productId : void 0,
71
+ organizationId: typeof request.query?.organizationId === "string" ? request.query.organizationId : void 0
72
+ });
73
+ throw new Error("Error parsing checkout params");
74
+ }
75
+ const checkoutParams = checkoutParamsParsed.data;
76
+ try {
77
+ const stripe = await options.provider.createCheckoutSession(
78
+ checkoutParams,
79
+ options.adapter
80
+ );
81
+ const product = await options.adapter?.getProduct(checkoutParams.productId);
82
+ if (product?.type && ["membership", "cohort", "cohort-archive", "live"].includes(product.type)) {
83
+ return Response.redirect(
84
+ `${options.baseUrl}/subscribe/verify-login?${searchParamsWithoutNil({
85
+ ...request.query,
86
+ country: request.query?.country || request.headers?.["x-vercel-ip-country"] || process.env.DEFAULT_COUNTRY || "US",
87
+ ip_address: request.query?.ip_address || request.headers?.["x-forwarded-for"] || "0.0.0.0",
88
+ organizationId: request.query?.organizationId,
89
+ productId: checkoutParams.productId,
90
+ checkoutUrl: stripe.redirect,
91
+ cancelUrl: checkoutParams.cancelUrl,
92
+ selfReportedSource
93
+ })}`
94
+ );
95
+ }
96
+ return Response.redirect(stripe.redirect);
97
+ } catch (e) {
98
+ logger.error(e instanceof Error ? e : new Error(String(e)), {
99
+ context: "checkout.create-session",
100
+ productId: checkoutParams.productId,
101
+ organizationId: checkoutParams.organizationId,
102
+ country: checkoutParams.country
103
+ });
104
+ }
105
+ return response;
106
+ }
107
+
108
+ export {
109
+ checkout
110
+ };
111
+ //# sourceMappingURL=chunk-F3KUYN5I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/actions/checkout.ts"],"sourcesContent":["import { buildCheckoutAttribution } from '@coursebuilder/commerce/checkout-attribution'\nimport { CheckoutParamsSchema } from '@coursebuilder/commerce/stripe-checkout'\nimport type {\n\tInternalOptions,\n\tRequestInternal,\n\tResponseInternal,\n} from '@coursebuilder/core/types'\n\nimport type { Cookie } from '../runtime/cookie'\nimport { logger } from '../runtime/logger'\n\nconst searchParamsWithoutNil = (params: Record<string, unknown>) => {\n\treturn new URLSearchParams(\n\t\tObject.entries(params).flatMap(([key, value]) => {\n\t\t\tif (value === undefined || value === null) return []\n\t\t\treturn [[key, String(value)]]\n\t\t}),\n\t)\n}\n\nexport async function checkout(\n\trequest: RequestInternal,\n\tcookies: Cookie[],\n\toptions: InternalOptions<'payment'>,\n): Promise<ResponseInternal> {\n\tconst response: ResponseInternal<any | null> = {\n\t\tbody: null,\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tcookies,\n\t}\n\n\t// Extract shortlink reference from cookie\n\tconst shortlinkRefCookie = cookies.find((c) => c.name === 'sl_ref')\n\tconst shortlinkRef = shortlinkRefCookie?.value\n\n\t// Extract first-touch attribution from cookie\n\tconst ftAttrCookie = cookies.find((c) => c.name === 'ft_attr')\n\tlet firstTouch: Record<string, unknown> = {}\n\ttry {\n\t\tif (ftAttrCookie?.value) {\n\t\t\tfirstTouch = JSON.parse(decodeURIComponent(ftAttrCookie.value))\n\t\t}\n\t} catch {\n\t\t// Malformed cookie, ignore\n\t}\n\n\t// Extract last-touch attribution from cookie\n\tconst ltAttrCookie = cookies.find((c) => c.name === 'lt_attr')\n\tlet lastTouch: Record<string, string> = {}\n\ttry {\n\t\tif (ltAttrCookie?.value) {\n\t\t\tlastTouch = JSON.parse(decodeURIComponent(ltAttrCookie.value))\n\t\t}\n\t} catch {\n\t\t// Malformed cookie, ignore\n\t}\n\n\tconst selfReportedSource =\n\t\ttypeof request.query?.selfReportedSource === 'string'\n\t\t\t? request.query.selfReportedSource.slice(0, 500)\n\t\t\t: undefined\n\tconst checkoutAttribution = buildCheckoutAttribution({\n\t\tfirstTouch,\n\t\tlastTouch,\n\t\tshortlinkRef,\n\t\tselfReportedSource,\n\t})\n\n\tlogger.debug('checkout.cookies-received', {\n\t\tcookieCount: cookies.length,\n\t\tcookieNames: cookies.map((c) => c.name),\n\t\thasShortlinkRef: Boolean(shortlinkRef),\n\t\thasAttributionSnapshot: Boolean(checkoutAttribution.attributionSnapshot),\n\t})\n\n\tconst checkoutParamsParsed = CheckoutParamsSchema.safeParse({\n\t\t...request.query,\n\t\tcountry:\n\t\t\trequest.query?.country ||\n\t\t\trequest.headers?.['x-vercel-ip-country'] ||\n\t\t\tprocess.env.DEFAULT_COUNTRY ||\n\t\t\t'US',\n\t\tip_address:\n\t\t\trequest.query?.ip_address ||\n\t\t\trequest.headers?.['x-forwarded-for'] ||\n\t\t\t'0.0.0.0',\n\t\torganizationId: request.query?.organizationId,\n\t\t...(selfReportedSource && { selfReportedSource }),\n\t\t...checkoutAttribution,\n\t})\n\n\tif (!checkoutParamsParsed.success) {\n\t\tconst parseError = new Error('Error parsing checkout params')\n\t\tlogger.error(parseError, {\n\t\t\tcontext: 'checkout.params-parse',\n\t\t\tissueCount: checkoutParamsParsed.error.issues.length,\n\t\t\tissuePaths: checkoutParamsParsed.error.issues.map((issue) =>\n\t\t\t\tissue.path.join('.'),\n\t\t\t),\n\t\t\tproductId:\n\t\t\t\ttypeof request.query?.productId === 'string'\n\t\t\t\t\t? request.query.productId\n\t\t\t\t\t: undefined,\n\t\t\torganizationId:\n\t\t\t\ttypeof request.query?.organizationId === 'string'\n\t\t\t\t\t? request.query.organizationId\n\t\t\t\t\t: undefined,\n\t\t})\n\t\tthrow new Error('Error parsing checkout params')\n\t}\n\n\tconst checkoutParams = checkoutParamsParsed.data\n\n\ttry {\n\t\tconst stripe = await options.provider.createCheckoutSession(\n\t\t\tcheckoutParams,\n\t\t\toptions.adapter,\n\t\t)\n\n\t\tconst product = await options.adapter?.getProduct(checkoutParams.productId)\n\n\t\t// if the product is a membership or cohort, we need to redirect to the login page\n\t\t// if the user isn't logged in. we want to ensure an org id is set.\n\t\tif (\n\t\t\tproduct?.type &&\n\t\t\t['membership', 'cohort', 'cohort-archive', 'live'].includes(product.type)\n\t\t) {\n\t\t\treturn Response.redirect(\n\t\t\t\t`${options.baseUrl}/subscribe/verify-login?${searchParamsWithoutNil({\n\t\t\t\t\t...request.query,\n\t\t\t\t\tcountry:\n\t\t\t\t\t\trequest.query?.country ||\n\t\t\t\t\t\trequest.headers?.['x-vercel-ip-country'] ||\n\t\t\t\t\t\tprocess.env.DEFAULT_COUNTRY ||\n\t\t\t\t\t\t'US',\n\t\t\t\t\tip_address:\n\t\t\t\t\t\trequest.query?.ip_address ||\n\t\t\t\t\t\trequest.headers?.['x-forwarded-for'] ||\n\t\t\t\t\t\t'0.0.0.0',\n\t\t\t\t\torganizationId: request.query?.organizationId,\n\t\t\t\t\tproductId: checkoutParams.productId,\n\t\t\t\t\tcheckoutUrl: stripe.redirect,\n\t\t\t\t\tcancelUrl: checkoutParams.cancelUrl,\n\t\t\t\t\tselfReportedSource,\n\t\t\t\t})}`,\n\t\t\t)\n\t\t}\n\n\t\treturn Response.redirect(stripe.redirect)\n\t} catch (e) {\n\t\tlogger.error(e instanceof Error ? e : new Error(String(e)), {\n\t\t\tcontext: 'checkout.create-session',\n\t\t\tproductId: checkoutParams.productId,\n\t\t\torganizationId: checkoutParams.organizationId,\n\t\t\tcountry: checkoutParams.country,\n\t\t})\n\t}\n\n\treturn response\n}\n"],"mappings":"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","names":[]}