@umec/core 0.1.0-alpha.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/dist/access.d.ts +18 -0
  3. package/dist/access.js +7 -0
  4. package/dist/access.js.map +1 -0
  5. package/dist/checkout.d.ts +28 -0
  6. package/dist/checkout.js +8 -0
  7. package/dist/checkout.js.map +1 -0
  8. package/dist/chunk-2NYFCO5V.js +192 -0
  9. package/dist/chunk-2NYFCO5V.js.map +1 -0
  10. package/dist/chunk-2SNGXEIL.js +37 -0
  11. package/dist/chunk-2SNGXEIL.js.map +1 -0
  12. package/dist/chunk-7TJANK6S.js +10 -0
  13. package/dist/chunk-7TJANK6S.js.map +1 -0
  14. package/dist/chunk-7ZJOZAEO.js +82 -0
  15. package/dist/chunk-7ZJOZAEO.js.map +1 -0
  16. package/dist/chunk-QRFFBMFN.js +109 -0
  17. package/dist/chunk-QRFFBMFN.js.map +1 -0
  18. package/dist/chunk-REUSLO5I.js +199 -0
  19. package/dist/chunk-REUSLO5I.js.map +1 -0
  20. package/dist/chunk-WZFM6R5J.js +47 -0
  21. package/dist/chunk-WZFM6R5J.js.map +1 -0
  22. package/dist/chunk-X2SNVVFW.js +73 -0
  23. package/dist/chunk-X2SNVVFW.js.map +1 -0
  24. package/dist/config.d.ts +67 -0
  25. package/dist/config.js +11 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/env.d.ts +15 -0
  28. package/dist/env.js +7 -0
  29. package/dist/env.js.map +1 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.js +41 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/middleware.d.ts +13 -0
  34. package/dist/middleware.js +7 -0
  35. package/dist/middleware.js.map +1 -0
  36. package/dist/notify.d.ts +23 -0
  37. package/dist/notify.js +7 -0
  38. package/dist/notify.js.map +1 -0
  39. package/dist/subscribe.d.ts +9 -0
  40. package/dist/subscribe.js +8 -0
  41. package/dist/subscribe.js.map +1 -0
  42. package/dist/webhook.d.ts +65 -0
  43. package/dist/webhook.js +10 -0
  44. package/dist/webhook.js.map +1 -0
  45. package/package.json +80 -0
@@ -0,0 +1,199 @@
1
+ import {
2
+ getServerEnv
3
+ } from "./chunk-7TJANK6S.js";
4
+
5
+ // src/webhook.ts
6
+ import { Resend } from "resend";
7
+ import Stripe from "stripe";
8
+ var STRIPE_API_VERSION = "2026-05-27.dahlia";
9
+ function parseCheckoutItems(items) {
10
+ if (!items) return [];
11
+ try {
12
+ const parsed = JSON.parse(items);
13
+ if (!Array.isArray(parsed)) return [];
14
+ return parsed.filter((item) => {
15
+ if (typeof item !== "object" || item === null) return false;
16
+ const { sku, quantity } = item;
17
+ return typeof sku === "string" && typeof quantity === "number" && Number.isSafeInteger(quantity) && quantity > 0;
18
+ });
19
+ } catch {
20
+ return [];
21
+ }
22
+ }
23
+ function isSupportedWebhookEvent(type) {
24
+ return type === "checkout.session.completed" || type === "checkout.session.expired" || type === "charge.refunded";
25
+ }
26
+ function getPaymentIntentId(paymentIntent) {
27
+ if (typeof paymentIntent === "string") return paymentIntent;
28
+ return paymentIntent?.id ?? null;
29
+ }
30
+ function providerErrorDetails(error) {
31
+ if (typeof error !== "object" || error === null) return { message: String(error) };
32
+ const value = error;
33
+ return {
34
+ name: typeof value.name === "string" ? value.name : void 0,
35
+ type: typeof value.type === "string" ? value.type : void 0,
36
+ code: typeof value.code === "string" ? value.code : void 0,
37
+ statusCode: typeof value.statusCode === "number" ? value.statusCode : void 0,
38
+ requestId: typeof value.requestId === "string" ? value.requestId : void 0,
39
+ message: typeof value.message === "string" ? value.message : void 0
40
+ };
41
+ }
42
+ async function restoreRecordedOrder(order, restoreStock, decrementOfferSold) {
43
+ const items = order?.items ?? [];
44
+ if (items.length > 0 && restoreStock) await restoreStock(items);
45
+ if (order?.offerSlug && decrementOfferSold) {
46
+ await decrementOfferSold(order.offerSlug, items);
47
+ }
48
+ }
49
+ var GET = async () => new Response("OK", { status: 200 });
50
+ function createWebhookHandler({
51
+ claimEvent,
52
+ recordRefund,
53
+ recordExpiry,
54
+ recordOrder,
55
+ restoreStock,
56
+ decrementStock,
57
+ incrementOfferSold,
58
+ decrementOfferSold,
59
+ renderOrderEmail,
60
+ orderEmailFrom,
61
+ orderEmailBcc
62
+ } = {}) {
63
+ return async ({ request }) => {
64
+ console.log("[webhook] POST received, method:", request.method);
65
+ const {
66
+ STRIPE_SECRET_KEY: stripeKey,
67
+ STRIPE_WEBHOOK_SECRET: webhookSecret,
68
+ RESEND_API_KEY: resendKey
69
+ } = getServerEnv();
70
+ if (!stripeKey || !webhookSecret) {
71
+ console.error("[webhook] Stripe env not configured");
72
+ return new Response("Server error", { status: 500 });
73
+ }
74
+ const sig = request.headers.get("stripe-signature");
75
+ if (!sig) return new Response("Bad request", { status: 400 });
76
+ const payload = await request.text();
77
+ const stripe = new Stripe(stripeKey, { apiVersion: STRIPE_API_VERSION });
78
+ let event;
79
+ try {
80
+ event = await stripe.webhooks.constructEventAsync(payload, sig, webhookSecret);
81
+ } catch {
82
+ return new Response("Invalid signature", { status: 400 });
83
+ }
84
+ if (!isSupportedWebhookEvent(event.type)) {
85
+ return new Response("OK", { status: 200 });
86
+ }
87
+ if (claimEvent) {
88
+ const claim = await claimEvent(event.id);
89
+ if (!claim) {
90
+ console.log("[webhook] duplicate event skipped:", event.id);
91
+ return new Response("OK", { status: 200 });
92
+ }
93
+ }
94
+ if (event.type === "charge.refunded") {
95
+ const charge = event.data.object;
96
+ const paymentIntent2 = getPaymentIntentId(charge.payment_intent);
97
+ if (paymentIntent2 && recordRefund) {
98
+ const order = await recordRefund(paymentIntent2);
99
+ await restoreRecordedOrder(order, restoreStock, decrementOfferSold);
100
+ }
101
+ return new Response("OK", { status: 200 });
102
+ }
103
+ const session = event.data.object;
104
+ if (event.type === "checkout.session.expired") {
105
+ if (recordExpiry) {
106
+ const order = await recordExpiry(session.id);
107
+ await restoreRecordedOrder(order, restoreStock, decrementOfferSold);
108
+ }
109
+ return new Response("OK", { status: 200 });
110
+ }
111
+ const email = session.customer_details?.email;
112
+ const customerName = session.customer_details?.name ?? null;
113
+ const name = customerName ?? "\u304A\u5BA2\u69D8";
114
+ const phone = session.customer_details?.phone ?? null;
115
+ const totalAmount = session.amount_total ?? 0;
116
+ const shippingCostAmount = session.shipping_cost?.amount_total ?? null;
117
+ const paymentIntent = getPaymentIntentId(session.payment_intent);
118
+ const checkoutItems = parseCheckoutItems(session.metadata?.items);
119
+ const cf = {};
120
+ for (const f of session.custom_fields ?? []) {
121
+ const v = f.type === "dropdown" ? f.dropdown?.value : f.text?.value;
122
+ if (typeof v === "string" && v.trim() !== "") cf[f.key] = v.trim();
123
+ }
124
+ const customFields = Object.keys(cf).length > 0 ? cf : null;
125
+ const shipping = session.collected_information?.shipping_details ?? session.shipping_details;
126
+ const offerSlugFromMeta = session.metadata?.offer_slug ?? null;
127
+ if (recordOrder) {
128
+ await recordOrder({
129
+ id: session.id,
130
+ email: email ?? "",
131
+ name: customerName,
132
+ phone,
133
+ items: checkoutItems,
134
+ amount: totalAmount,
135
+ shippingCost: shippingCostAmount,
136
+ shippingAddress: shipping,
137
+ paymentIntent,
138
+ offerSlug: offerSlugFromMeta,
139
+ customFields,
140
+ createdAt: Date.now()
141
+ });
142
+ if (checkoutItems.length > 0 && decrementStock) await decrementStock(checkoutItems);
143
+ if (offerSlugFromMeta && checkoutItems.length > 0 && incrementOfferSold) {
144
+ await incrementOfferSold(offerSlugFromMeta, checkoutItems);
145
+ }
146
+ }
147
+ let lineItems = [];
148
+ try {
149
+ const result = await stripe.checkout.sessions.listLineItems(session.id, { limit: 100 });
150
+ lineItems = result.data;
151
+ } catch (e) {
152
+ console.error("[webhook] listLineItems failed:", providerErrorDetails(e));
153
+ }
154
+ if (!email) {
155
+ console.error("[webhook] no customer email in session:", session.id);
156
+ return new Response("OK", { status: 200 });
157
+ }
158
+ if (!resendKey || !renderOrderEmail || !orderEmailFrom) {
159
+ if (!resendKey) console.error("[webhook] RESEND_API_KEY not set");
160
+ return new Response("OK", { status: 200 });
161
+ }
162
+ const { subject, html } = renderOrderEmail({
163
+ name,
164
+ email,
165
+ phone,
166
+ items: lineItems.map((item) => ({
167
+ description: item.description,
168
+ quantity: item.quantity,
169
+ amount: item.amount_total
170
+ })),
171
+ totalAmount,
172
+ shippingCostAmount,
173
+ shipping,
174
+ customFields: cf
175
+ });
176
+ try {
177
+ const resend = new Resend(resendKey);
178
+ const { error } = await resend.emails.send({
179
+ from: orderEmailFrom,
180
+ to: email,
181
+ ...orderEmailBcc ? { bcc: orderEmailBcc } : {},
182
+ subject,
183
+ html
184
+ });
185
+ if (error) {
186
+ console.error("[webhook] Resend error:", providerErrorDetails(error));
187
+ }
188
+ } catch (e) {
189
+ console.error("[webhook] Resend exception:", providerErrorDetails(e));
190
+ }
191
+ return new Response("OK", { status: 200 });
192
+ };
193
+ }
194
+
195
+ export {
196
+ GET,
197
+ createWebhookHandler
198
+ };
199
+ //# sourceMappingURL=chunk-REUSLO5I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/webhook.ts"],"sourcesContent":["import { Resend } from \"resend\";\nimport Stripe from \"stripe\";\nimport { getServerEnv } from \"./env.js\";\n\nconst STRIPE_API_VERSION = \"2026-05-27.dahlia\";\n\ntype ShippingDetails = NonNullable<\n NonNullable<Stripe.Checkout.Session[\"collected_information\"]>[\"shipping_details\"]\n>;\ntype SessionWithLegacyShipping = Stripe.Checkout.Session & {\n shipping_details?: ShippingDetails | null;\n};\nexport type WebhookItem = {\n sku: string;\n quantity: number;\n};\nexport type SupportedWebhookEvent =\n | \"checkout.session.completed\"\n | \"checkout.session.expired\"\n | \"charge.refunded\";\n\nexport type OrderEmailLineItem = {\n description: string | null;\n quantity: number | null;\n amount: number | null;\n};\n\nexport type OrderEmailData = {\n name: string;\n email: string;\n phone: string | null;\n items: OrderEmailLineItem[];\n totalAmount: number;\n shippingCostAmount: number | null;\n shipping: ShippingDetails | null | undefined;\n customFields: Record<string, string>;\n};\n\nexport type RenderOrderEmail = (order: OrderEmailData) => { subject: string; html: string };\n\nexport type WebhookOrder = {\n id: string;\n email: string;\n name: string | null;\n phone: string | null;\n items: WebhookItem[];\n amount: number;\n shippingCost: number | null;\n shippingAddress: ShippingDetails | null | undefined;\n paymentIntent: string | null;\n offerSlug: string | null;\n customFields: Record<string, string> | null;\n createdAt: number;\n};\n\nexport type WebhookRecordedOrder = {\n items: WebhookItem[];\n offerSlug: string | null;\n};\n\nexport type WebhookHandlerOptions = {\n claimEvent?: (eventId: string) => Promise<boolean>;\n recordRefund?: (paymentIntentId: string) => Promise<WebhookRecordedOrder | null>;\n recordExpiry?: (sessionId: string) => Promise<WebhookRecordedOrder | null>;\n recordOrder?: (order: WebhookOrder) => Promise<void>;\n restoreStock?: (items: WebhookItem[]) => Promise<void>;\n decrementStock?: (items: WebhookItem[]) => Promise<void>;\n incrementOfferSold?: (offerSlug: string, items: WebhookItem[]) => Promise<void>;\n decrementOfferSold?: (offerSlug: string, items: WebhookItem[]) => Promise<void>;\n renderOrderEmail?: RenderOrderEmail;\n orderEmailFrom?: string;\n orderEmailBcc?: string | string[];\n};\n\nexport type WebhookHandler = ({ request }: { request: Request }) => Promise<Response>;\n\nfunction parseCheckoutItems(items: string | null | undefined): WebhookItem[] {\n if (!items) return [];\n\n try {\n const parsed: unknown = JSON.parse(items);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter((item): item is WebhookItem => {\n if (typeof item !== \"object\" || item === null) return false;\n const { sku, quantity } = item as { sku?: unknown; quantity?: unknown };\n return typeof sku === \"string\" && typeof quantity === \"number\" && Number.isSafeInteger(quantity) && quantity > 0;\n });\n } catch {\n return [];\n }\n}\n\nfunction isSupportedWebhookEvent(type: string): type is SupportedWebhookEvent {\n return (\n type === \"checkout.session.completed\" ||\n type === \"checkout.session.expired\" ||\n type === \"charge.refunded\"\n );\n}\n\nfunction getPaymentIntentId(paymentIntent: string | Stripe.PaymentIntent | null) {\n if (typeof paymentIntent === \"string\") return paymentIntent;\n return paymentIntent?.id ?? null;\n}\n\nfunction providerErrorDetails(error: unknown) {\n if (typeof error !== \"object\" || error === null) return { message: String(error) };\n const value = error as {\n name?: unknown;\n type?: unknown;\n code?: unknown;\n statusCode?: unknown;\n requestId?: unknown;\n message?: unknown;\n };\n\n return {\n name: typeof value.name === \"string\" ? value.name : undefined,\n type: typeof value.type === \"string\" ? value.type : undefined,\n code: typeof value.code === \"string\" ? value.code : undefined,\n statusCode: typeof value.statusCode === \"number\" ? value.statusCode : undefined,\n requestId: typeof value.requestId === \"string\" ? value.requestId : undefined,\n message: typeof value.message === \"string\" ? value.message : undefined,\n };\n}\n\nasync function restoreRecordedOrder(\n order: WebhookRecordedOrder | null,\n restoreStock?: (items: WebhookItem[]) => Promise<void>,\n decrementOfferSold?: (offerSlug: string, items: WebhookItem[]) => Promise<void>,\n) {\n const items = order?.items ?? [];\n if (items.length > 0 && restoreStock) await restoreStock(items);\n if (order?.offerSlug && decrementOfferSold) {\n await decrementOfferSold(order.offerSlug, items);\n }\n}\n\nexport const GET: WebhookHandler = async () => new Response(\"OK\", { status: 200 });\n\nexport function createWebhookHandler({\n claimEvent,\n recordRefund,\n recordExpiry,\n recordOrder,\n restoreStock,\n decrementStock,\n incrementOfferSold,\n decrementOfferSold,\n renderOrderEmail,\n orderEmailFrom,\n orderEmailBcc,\n}: WebhookHandlerOptions = {}): WebhookHandler {\n return async ({ request }) => {\n console.log(\"[webhook] POST received, method:\", request.method);\n\n const {\n STRIPE_SECRET_KEY: stripeKey,\n STRIPE_WEBHOOK_SECRET: webhookSecret,\n RESEND_API_KEY: resendKey,\n } = getServerEnv();\n\n if (!stripeKey || !webhookSecret) {\n console.error(\"[webhook] Stripe env not configured\");\n return new Response(\"Server error\", { status: 500 });\n }\n\n const sig = request.headers.get(\"stripe-signature\");\n if (!sig) return new Response(\"Bad request\", { status: 400 });\n\n const payload = await request.text();\n\n const stripe = new Stripe(stripeKey, { apiVersion: STRIPE_API_VERSION });\n\n let event: Stripe.Event;\n try {\n event = await stripe.webhooks.constructEventAsync(payload, sig, webhookSecret);\n } catch {\n return new Response(\"Invalid signature\", { status: 400 });\n }\n\n if (!isSupportedWebhookEvent(event.type)) {\n return new Response(\"OK\", { status: 200 });\n }\n\n if (claimEvent) {\n const claim = await claimEvent(event.id);\n if (!claim) {\n console.log(\"[webhook] duplicate event skipped:\", event.id);\n return new Response(\"OK\", { status: 200 });\n }\n }\n\n if (event.type === \"charge.refunded\") {\n const charge = event.data.object as Stripe.Charge;\n const paymentIntent = getPaymentIntentId(charge.payment_intent);\n\n if (paymentIntent && recordRefund) {\n const order = await recordRefund(paymentIntent);\n await restoreRecordedOrder(order, restoreStock, decrementOfferSold);\n }\n\n return new Response(\"OK\", { status: 200 });\n }\n\n const session = event.data.object as Stripe.Checkout.Session;\n\n if (event.type === \"checkout.session.expired\") {\n if (recordExpiry) {\n const order = await recordExpiry(session.id);\n await restoreRecordedOrder(order, restoreStock, decrementOfferSold);\n }\n\n return new Response(\"OK\", { status: 200 });\n }\n\n const email = session.customer_details?.email;\n const customerName = session.customer_details?.name ?? null;\n const name = customerName ?? \"お客様\";\n const phone = session.customer_details?.phone ?? null;\n const totalAmount = session.amount_total ?? 0;\n const shippingCostAmount = session.shipping_cost?.amount_total ?? null;\n const paymentIntent = getPaymentIntentId(session.payment_intent);\n const checkoutItems = parseCheckoutItems(session.metadata?.items);\n const cf: Record<string, string> = {};\n for (const f of (session.custom_fields ?? []) as Array<{\n key: string;\n type: string;\n text?: { value?: string | null };\n dropdown?: { value?: string | null };\n }>) {\n const v = f.type === \"dropdown\" ? f.dropdown?.value : f.text?.value;\n if (typeof v === \"string\" && v.trim() !== \"\") cf[f.key] = v.trim();\n }\n const customFields = Object.keys(cf).length > 0 ? cf : null;\n\n const shipping =\n session.collected_information?.shipping_details ??\n (session as SessionWithLegacyShipping).shipping_details;\n\n const offerSlugFromMeta = session.metadata?.offer_slug ?? null;\n\n if (recordOrder) {\n await recordOrder({\n id: session.id,\n email: email ?? \"\",\n name: customerName,\n phone,\n items: checkoutItems,\n amount: totalAmount,\n shippingCost: shippingCostAmount,\n shippingAddress: shipping,\n paymentIntent,\n offerSlug: offerSlugFromMeta,\n customFields,\n createdAt: Date.now(),\n });\n if (checkoutItems.length > 0 && decrementStock) await decrementStock(checkoutItems);\n if (offerSlugFromMeta && checkoutItems.length > 0 && incrementOfferSold) {\n await incrementOfferSold(offerSlugFromMeta, checkoutItems);\n }\n }\n\n let lineItems: Stripe.LineItem[] = [];\n try {\n const result = await stripe.checkout.sessions.listLineItems(session.id, { limit: 100 });\n lineItems = result.data;\n } catch (e) {\n console.error(\"[webhook] listLineItems failed:\", providerErrorDetails(e));\n }\n\n if (!email) {\n console.error(\"[webhook] no customer email in session:\", session.id);\n return new Response(\"OK\", { status: 200 });\n }\n\n if (!resendKey || !renderOrderEmail || !orderEmailFrom) {\n if (!resendKey) console.error(\"[webhook] RESEND_API_KEY not set\");\n return new Response(\"OK\", { status: 200 });\n }\n\n const { subject, html } = renderOrderEmail({\n name,\n email,\n phone,\n items: lineItems.map((item) => ({\n description: item.description,\n quantity: item.quantity,\n amount: item.amount_total,\n })),\n totalAmount,\n shippingCostAmount,\n shipping,\n customFields: cf,\n });\n\n try {\n const resend = new Resend(resendKey);\n const { error } = await resend.emails.send({\n from: orderEmailFrom,\n to: email,\n ...(orderEmailBcc ? { bcc: orderEmailBcc } : {}),\n subject,\n html,\n });\n if (error) {\n console.error(\"[webhook] Resend error:\", providerErrorDetails(error));\n }\n } catch (e) {\n console.error(\"[webhook] Resend exception:\", providerErrorDetails(e));\n }\n\n return new Response(\"OK\", { status: 200 });\n };\n}\n"],"mappings":"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","names":["paymentIntent"]}
@@ -0,0 +1,47 @@
1
+ // src/middleware.ts
2
+ var SECURITY_HEADERS = {
3
+ "Strict-Transport-Security": "max-age=63072000; includeSubDomains",
4
+ "X-Content-Type-Options": "nosniff",
5
+ "X-Frame-Options": "SAMEORIGIN",
6
+ "Referrer-Policy": "strict-origin-when-cross-origin"
7
+ };
8
+ var LIMITED_DOCS = [
9
+ {
10
+ pathPrefix: "/docs/asahiko2026special",
11
+ expiresAt: Date.parse("2026-08-12T14:59:59.999Z")
12
+ }
13
+ ];
14
+ function withSecurityHeaders(response) {
15
+ const securedResponse = new Response(response.body, response);
16
+ for (const [name, value] of Object.entries(SECURITY_HEADERS)) {
17
+ securedResponse.headers.set(name, value);
18
+ }
19
+ return securedResponse;
20
+ }
21
+ function createUmecMiddleware({ maintenanceMode }) {
22
+ return async (context, next) => {
23
+ const path = context.url.pathname;
24
+ const limitedDoc = LIMITED_DOCS.find(({ pathPrefix }) => path === pathPrefix || path.startsWith(`${pathPrefix}/`));
25
+ if (limitedDoc && Date.now() > limitedDoc.expiresAt) {
26
+ return withSecurityHeaders(
27
+ new Response("Not found", {
28
+ status: 404,
29
+ headers: {
30
+ "Cache-Control": "no-store"
31
+ }
32
+ })
33
+ );
34
+ }
35
+ if (maintenanceMode && path !== "/maintenance" && !path.startsWith("/api/")) {
36
+ const response = context.redirect("/maintenance", 302);
37
+ response.headers.set("Cache-Control", "no-store");
38
+ return response;
39
+ }
40
+ return withSecurityHeaders(await next());
41
+ };
42
+ }
43
+
44
+ export {
45
+ createUmecMiddleware
46
+ };
47
+ //# sourceMappingURL=chunk-WZFM6R5J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts"],"sourcesContent":["const SECURITY_HEADERS = {\n \"Strict-Transport-Security\": \"max-age=63072000; includeSubDomains\",\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-Frame-Options\": \"SAMEORIGIN\",\n \"Referrer-Policy\": \"strict-origin-when-cross-origin\",\n};\n\nconst LIMITED_DOCS = [\n {\n pathPrefix: \"/docs/asahiko2026special\",\n expiresAt: Date.parse(\"2026-08-12T14:59:59.999Z\"),\n },\n] as const;\n\n// Astro の ValidRedirectStatus と同一のユニオンに揃える。\n// number に広げると Astro の APIContext が strict モードで代入不能になる(TS2345)\nexport type UmecRedirectStatus = 300 | 301 | 302 | 303 | 304 | 307 | 308;\n\nexport type UmecMiddlewareContext = {\n url: URL;\n redirect: (path: string, status?: UmecRedirectStatus) => Response;\n};\n\nexport type UmecMiddlewareNext = () => Response | Promise<Response>;\n\nexport type UmecMiddleware = (context: UmecMiddlewareContext, next: UmecMiddlewareNext) => Promise<Response>;\n\nexport type UmecMiddlewareOptions = {\n maintenanceMode: boolean;\n};\n\nfunction withSecurityHeaders(response: Response) {\n const securedResponse = new Response(response.body, response);\n\n for (const [name, value] of Object.entries(SECURITY_HEADERS)) {\n securedResponse.headers.set(name, value);\n }\n\n return securedResponse;\n}\n\nexport function createUmecMiddleware({ maintenanceMode }: UmecMiddlewareOptions): UmecMiddleware {\n return async (context, next) => {\n const path = context.url.pathname;\n\n const limitedDoc = LIMITED_DOCS.find(({ pathPrefix }) => path === pathPrefix || path.startsWith(`${pathPrefix}/`));\n if (limitedDoc && Date.now() > limitedDoc.expiresAt) {\n return withSecurityHeaders(\n new Response(\"Not found\", {\n status: 404,\n headers: {\n \"Cache-Control\": \"no-store\",\n },\n }),\n );\n }\n\n if (maintenanceMode && path !== \"/maintenance\" && !path.startsWith(\"/api/\")) {\n const response = context.redirect(\"/maintenance\", 302);\n response.headers.set(\"Cache-Control\", \"no-store\");\n return response;\n }\n\n return withSecurityHeaders(await next());\n };\n}\n"],"mappings":";AAAA,IAAM,mBAAmB;AAAA,EACvB,6BAA6B;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,mBAAmB;AAAA,EACnB,mBAAmB;AACrB;AAEA,IAAM,eAAe;AAAA,EACnB;AAAA,IACE,YAAY;AAAA,IACZ,WAAW,KAAK,MAAM,0BAA0B;AAAA,EAClD;AACF;AAmBA,SAAS,oBAAoB,UAAoB;AAC/C,QAAM,kBAAkB,IAAI,SAAS,SAAS,MAAM,QAAQ;AAE5D,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,gBAAgB,GAAG;AAC5D,oBAAgB,QAAQ,IAAI,MAAM,KAAK;AAAA,EACzC;AAEA,SAAO;AACT;AAEO,SAAS,qBAAqB,EAAE,gBAAgB,GAA0C;AAC/F,SAAO,OAAO,SAAS,SAAS;AAC9B,UAAM,OAAO,QAAQ,IAAI;AAEzB,UAAM,aAAa,aAAa,KAAK,CAAC,EAAE,WAAW,MAAM,SAAS,cAAc,KAAK,WAAW,GAAG,UAAU,GAAG,CAAC;AACjH,QAAI,cAAc,KAAK,IAAI,IAAI,WAAW,WAAW;AACnD,aAAO;AAAA,QACL,IAAI,SAAS,aAAa;AAAA,UACxB,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,iBAAiB;AAAA,UACnB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,mBAAmB,SAAS,kBAAkB,CAAC,KAAK,WAAW,OAAO,GAAG;AAC3E,YAAM,WAAW,QAAQ,SAAS,gBAAgB,GAAG;AACrD,eAAS,QAAQ,IAAI,iBAAiB,UAAU;AAChD,aAAO;AAAA,IACT;AAEA,WAAO,oBAAoB,MAAM,KAAK,CAAC;AAAA,EACzC;AACF;","names":[]}
@@ -0,0 +1,73 @@
1
+ import {
2
+ getServerEnv
3
+ } from "./chunk-7TJANK6S.js";
4
+
5
+ // src/subscribe.ts
6
+ import { Resend } from "resend";
7
+ function jsonError(error, status) {
8
+ return new Response(JSON.stringify({ error }), {
9
+ status,
10
+ headers: { "Content-Type": "application/json" }
11
+ });
12
+ }
13
+ function providerErrorDetails(error) {
14
+ if (typeof error !== "object" || error === null) return { message: String(error) };
15
+ const value = error;
16
+ return {
17
+ name: typeof value.name === "string" ? value.name : void 0,
18
+ type: typeof value.type === "string" ? value.type : void 0,
19
+ code: typeof value.code === "string" ? value.code : void 0,
20
+ statusCode: typeof value.statusCode === "number" ? value.statusCode : void 0,
21
+ requestId: typeof value.requestId === "string" ? value.requestId : void 0,
22
+ message: typeof value.message === "string" ? value.message : void 0
23
+ };
24
+ }
25
+ function createSubscribeHandler({ isProduction }) {
26
+ return async ({ request }) => {
27
+ let body;
28
+ try {
29
+ body = await request.json();
30
+ } catch {
31
+ return jsonError("Invalid request body", 400);
32
+ }
33
+ if (typeof body !== "object" || body === null) {
34
+ return jsonError("Invalid request body", 400);
35
+ }
36
+ const { email } = body;
37
+ if (typeof email !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
38
+ return jsonError("Invalid email", 400);
39
+ }
40
+ const { RESEND_API_KEY: apiKey, RESEND_AUDIENCE_ID: audienceId } = getServerEnv();
41
+ if (!apiKey || !audienceId) {
42
+ if (isProduction) {
43
+ console.error("[subscribe] RESEND env missing in production");
44
+ return jsonError("Subscription service unavailable", 503);
45
+ }
46
+ console.log("[subscribe] env not set (dev), skipping:", email);
47
+ return new Response(JSON.stringify({ ok: true }), {
48
+ status: 200,
49
+ headers: { "Content-Type": "application/json" }
50
+ });
51
+ }
52
+ try {
53
+ const resend = new Resend(apiKey);
54
+ const { error } = await resend.contacts.create({ email, audienceId, unsubscribed: false });
55
+ if (error) {
56
+ console.error("[subscribe] Resend error:", providerErrorDetails(error));
57
+ return jsonError("Subscription failed", 502);
58
+ }
59
+ } catch (e) {
60
+ console.error("[subscribe] Resend exception:", providerErrorDetails(e));
61
+ return jsonError("Subscription failed", 502);
62
+ }
63
+ return new Response(JSON.stringify({ ok: true }), {
64
+ status: 200,
65
+ headers: { "Content-Type": "application/json" }
66
+ });
67
+ };
68
+ }
69
+
70
+ export {
71
+ createSubscribeHandler
72
+ };
73
+ //# sourceMappingURL=chunk-X2SNVVFW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/subscribe.ts"],"sourcesContent":["import { Resend } from \"resend\";\nimport { getServerEnv } from \"./env.js\";\n\nexport type SubscribeHandler = ({ request }: { request: Request }) => Promise<Response>;\n\nexport type SubscribeHandlerOptions = {\n isProduction: boolean;\n};\n\nfunction jsonError(error: string, status: number) {\n return new Response(JSON.stringify({ error }), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction providerErrorDetails(error: unknown) {\n if (typeof error !== \"object\" || error === null) return { message: String(error) };\n const value = error as {\n name?: unknown;\n type?: unknown;\n code?: unknown;\n statusCode?: unknown;\n requestId?: unknown;\n message?: unknown;\n };\n\n return {\n name: typeof value.name === \"string\" ? value.name : undefined,\n type: typeof value.type === \"string\" ? value.type : undefined,\n code: typeof value.code === \"string\" ? value.code : undefined,\n statusCode: typeof value.statusCode === \"number\" ? value.statusCode : undefined,\n requestId: typeof value.requestId === \"string\" ? value.requestId : undefined,\n message: typeof value.message === \"string\" ? value.message : undefined,\n };\n}\n\nexport function createSubscribeHandler({ isProduction }: SubscribeHandlerOptions): SubscribeHandler {\n return async ({ request }) => {\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return jsonError(\"Invalid request body\", 400);\n }\n\n if (typeof body !== \"object\" || body === null) {\n return jsonError(\"Invalid request body\", 400);\n }\n\n const { email } = body as Record<string, unknown>;\n\n if (typeof email !== \"string\" || !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n return jsonError(\"Invalid email\", 400);\n }\n\n const { RESEND_API_KEY: apiKey, RESEND_AUDIENCE_ID: audienceId } = getServerEnv();\n\n if (!apiKey || !audienceId) {\n if (isProduction) {\n console.error(\"[subscribe] RESEND env missing in production\");\n return jsonError(\"Subscription service unavailable\", 503);\n }\n console.log(\"[subscribe] env not set (dev), skipping:\", email);\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n try {\n const resend = new Resend(apiKey);\n const { error } = await resend.contacts.create({ email, audienceId, unsubscribed: false });\n if (error) {\n console.error(\"[subscribe] Resend error:\", providerErrorDetails(error));\n return jsonError(\"Subscription failed\", 502);\n }\n } catch (e) {\n console.error(\"[subscribe] Resend exception:\", providerErrorDetails(e));\n return jsonError(\"Subscription failed\", 502);\n }\n\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n };\n}\n"],"mappings":";;;;;AAAA,SAAS,cAAc;AASvB,SAAS,UAAU,OAAe,QAAgB;AAChD,SAAO,IAAI,SAAS,KAAK,UAAU,EAAE,MAAM,CAAC,GAAG;AAAA,IAC7C;AAAA,IACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;AAEA,SAAS,qBAAqB,OAAgB;AAC5C,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO,EAAE,SAAS,OAAO,KAAK,EAAE;AACjF,QAAM,QAAQ;AASd,SAAO;AAAA,IACL,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AAAA,IACpD,YAAY,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAAA,IACtE,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAAA,IACnE,SAAS,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU;AAAA,EAC/D;AACF;AAEO,SAAS,uBAAuB,EAAE,aAAa,GAA8C;AAClG,SAAO,OAAO,EAAE,QAAQ,MAAM;AAC5B,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,QAAQ,KAAK;AAAA,IAC5B,QAAQ;AACN,aAAO,UAAU,wBAAwB,GAAG;AAAA,IAC9C;AAEA,QAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,aAAO,UAAU,wBAAwB,GAAG;AAAA,IAC9C;AAEA,UAAM,EAAE,MAAM,IAAI;AAElB,QAAI,OAAO,UAAU,YAAY,CAAC,6BAA6B,KAAK,KAAK,GAAG;AAC1E,aAAO,UAAU,iBAAiB,GAAG;AAAA,IACvC;AAEA,UAAM,EAAE,gBAAgB,QAAQ,oBAAoB,WAAW,IAAI,aAAa;AAEhF,QAAI,CAAC,UAAU,CAAC,YAAY;AAC1B,UAAI,cAAc;AAChB,gBAAQ,MAAM,8CAA8C;AAC5D,eAAO,UAAU,oCAAoC,GAAG;AAAA,MAC1D;AACA,cAAQ,IAAI,4CAA4C,KAAK;AAC7D,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,IAAI,KAAK,CAAC,GAAG;AAAA,QAChD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,SAAS,IAAI,OAAO,MAAM;AAChC,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,SAAS,OAAO,EAAE,OAAO,YAAY,cAAc,MAAM,CAAC;AACzF,UAAI,OAAO;AACT,gBAAQ,MAAM,6BAA6B,qBAAqB,KAAK,CAAC;AACtE,eAAO,UAAU,uBAAuB,GAAG;AAAA,MAC7C;AAAA,IACF,SAAS,GAAG;AACV,cAAQ,MAAM,iCAAiC,qBAAqB,CAAC,CAAC;AACtE,aAAO,UAAU,uBAAuB,GAAG;AAAA,IAC7C;AAEA,WAAO,IAAI,SAAS,KAAK,UAAU,EAAE,IAAI,KAAK,CAAC,GAAG;AAAA,MAChD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAChD,CAAC;AAAA,EACH;AACF;","names":[]}
@@ -0,0 +1,67 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const productSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ price: z.ZodInt;
6
+ description: z.ZodOptional<z.ZodString>;
7
+ active: z.ZodDefault<z.ZodBoolean>;
8
+ shippingUnits: z.ZodDefault<z.ZodInt>;
9
+ images: z.ZodOptional<z.ZodArray<z.ZodString>>;
10
+ }, z.core.$strict>;
11
+ declare const shippingSchema: z.ZodObject<{
12
+ tiers: z.ZodArray<z.ZodObject<{
13
+ maxUnits: z.ZodOptional<z.ZodInt>;
14
+ amount: z.ZodInt;
15
+ label: z.ZodString;
16
+ }, z.core.$strict>>;
17
+ freeLabel: z.ZodString;
18
+ }, z.core.$strict>;
19
+ declare const umecConfigSchema: z.ZodObject<{
20
+ schemaVersion: z.ZodLiteral<1>;
21
+ brand: z.ZodObject<{
22
+ name: z.ZodString;
23
+ url: z.ZodURL;
24
+ description: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strict>;
26
+ products: z.ZodRecord<z.ZodString, z.ZodObject<{
27
+ name: z.ZodString;
28
+ price: z.ZodInt;
29
+ description: z.ZodOptional<z.ZodString>;
30
+ active: z.ZodDefault<z.ZodBoolean>;
31
+ shippingUnits: z.ZodDefault<z.ZodInt>;
32
+ images: z.ZodOptional<z.ZodArray<z.ZodString>>;
33
+ }, z.core.$strict>>;
34
+ shipping: z.ZodOptional<z.ZodObject<{
35
+ tiers: z.ZodArray<z.ZodObject<{
36
+ maxUnits: z.ZodOptional<z.ZodInt>;
37
+ amount: z.ZodInt;
38
+ label: z.ZodString;
39
+ }, z.core.$strict>>;
40
+ freeLabel: z.ZodString;
41
+ }, z.core.$strict>>;
42
+ email: z.ZodObject<{
43
+ from: z.ZodEmail;
44
+ fromName: z.ZodOptional<z.ZodString>;
45
+ bcc: z.ZodOptional<z.ZodEmail>;
46
+ replyTo: z.ZodOptional<z.ZodEmail>;
47
+ }, z.core.$strict>;
48
+ }, z.core.$strict>;
49
+ type UmecConfig = z.infer<typeof umecConfigSchema>;
50
+ type UmecConfigInput = z.input<typeof umecConfigSchema>;
51
+ type UmecProduct = z.infer<typeof productSchema>;
52
+ type UmecShipping = z.infer<typeof shippingSchema>;
53
+ /**
54
+ * Validates and returns a typed umec.config.ts object.
55
+ * Applies defaults (e.g. product.active = true).
56
+ */
57
+ declare function defineUmecConfig(config: unknown): UmecConfig;
58
+ /**
59
+ * Formats an email config's from address with an optional display name,
60
+ * e.g. { from: "order@example.com", fromName: "emu" } -> "emu <order@example.com>".
61
+ */
62
+ declare function formatEmailFrom(email: {
63
+ from: string;
64
+ fromName?: string;
65
+ }): string;
66
+
67
+ export { type UmecConfig, type UmecConfigInput, type UmecProduct, type UmecShipping, defineUmecConfig, formatEmailFrom, umecConfigSchema };
package/dist/config.js ADDED
@@ -0,0 +1,11 @@
1
+ import {
2
+ defineUmecConfig,
3
+ formatEmailFrom,
4
+ umecConfigSchema
5
+ } from "./chunk-7ZJOZAEO.js";
6
+ export {
7
+ defineUmecConfig,
8
+ formatEmailFrom,
9
+ umecConfigSchema
10
+ };
11
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/env.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ type ServerEnv = {
2
+ PUBLIC_STRIPE_PUBLISHABLE_KEY?: string;
3
+ STRIPE_SECRET_KEY: string;
4
+ STRIPE_WEBHOOK_SECRET: string;
5
+ RESEND_API_KEY: string;
6
+ RESEND_AUDIENCE_ID: string;
7
+ DISCORD_WEBHOOK_URL?: string;
8
+ ALERT_DAYS_THRESHOLD?: string;
9
+ ACCESS_TEAM_DOMAIN?: string;
10
+ ACCESS_AUD?: string;
11
+ DB?: D1Database;
12
+ };
13
+ declare function getServerEnv(): ServerEnv;
14
+
15
+ export { type ServerEnv, getServerEnv };
package/dist/env.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ getServerEnv
3
+ } from "./chunk-7TJANK6S.js";
4
+ export {
5
+ getServerEnv
6
+ };
7
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,11 @@
1
+ export { AccessGuardOptions, AccessGuardResult, createAccessGuard } from './access.js';
2
+ export { CheckOffer, CheckStock, CheckoutHandler, CheckoutHandlerOptions, CheckoutOffer, CheckoutOfferUnavailable, CheckoutProductsMap, createCheckoutHandler } from './checkout.js';
3
+ export { UmecConfig, UmecConfigInput, UmecProduct, UmecShipping, defineUmecConfig, formatEmailFrom, umecConfigSchema } from './config.js';
4
+ export { ServerEnv, getServerEnv } from './env.js';
5
+ export { UmecMiddleware, UmecMiddlewareContext, UmecMiddlewareNext, UmecMiddlewareOptions, createUmecMiddleware } from './middleware.js';
6
+ export { NotifyEnv, NotifyProduct, NotifyProductsMap, NotifyUnshippedOptions, UnshippedOrder, notifyUnshipped } from './notify.js';
7
+ export { SubscribeHandler, SubscribeHandlerOptions, createSubscribeHandler } from './subscribe.js';
8
+ export { OrderEmailData, OrderEmailLineItem, RenderOrderEmail, SupportedWebhookEvent, WebhookHandler, WebhookHandlerOptions, WebhookItem, WebhookOrder, WebhookRecordedOrder, createWebhookHandler, GET as webhookGET } from './webhook.js';
9
+ import 'jose';
10
+ import 'zod';
11
+ import 'stripe';
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ import {
2
+ GET,
3
+ createWebhookHandler
4
+ } from "./chunk-REUSLO5I.js";
5
+ import {
6
+ createAccessGuard
7
+ } from "./chunk-2SNGXEIL.js";
8
+ import {
9
+ createCheckoutHandler
10
+ } from "./chunk-2NYFCO5V.js";
11
+ import {
12
+ defineUmecConfig,
13
+ formatEmailFrom,
14
+ umecConfigSchema
15
+ } from "./chunk-7ZJOZAEO.js";
16
+ import {
17
+ createUmecMiddleware
18
+ } from "./chunk-WZFM6R5J.js";
19
+ import {
20
+ notifyUnshipped
21
+ } from "./chunk-QRFFBMFN.js";
22
+ import {
23
+ createSubscribeHandler
24
+ } from "./chunk-X2SNVVFW.js";
25
+ import {
26
+ getServerEnv
27
+ } from "./chunk-7TJANK6S.js";
28
+ export {
29
+ createAccessGuard,
30
+ createCheckoutHandler,
31
+ createSubscribeHandler,
32
+ createUmecMiddleware,
33
+ createWebhookHandler,
34
+ defineUmecConfig,
35
+ formatEmailFrom,
36
+ getServerEnv,
37
+ notifyUnshipped,
38
+ umecConfigSchema,
39
+ GET as webhookGET
40
+ };
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,13 @@
1
+ type UmecRedirectStatus = 300 | 301 | 302 | 303 | 304 | 307 | 308;
2
+ type UmecMiddlewareContext = {
3
+ url: URL;
4
+ redirect: (path: string, status?: UmecRedirectStatus) => Response;
5
+ };
6
+ type UmecMiddlewareNext = () => Response | Promise<Response>;
7
+ type UmecMiddleware = (context: UmecMiddlewareContext, next: UmecMiddlewareNext) => Promise<Response>;
8
+ type UmecMiddlewareOptions = {
9
+ maintenanceMode: boolean;
10
+ };
11
+ declare function createUmecMiddleware({ maintenanceMode }: UmecMiddlewareOptions): UmecMiddleware;
12
+
13
+ export { type UmecMiddleware, type UmecMiddlewareContext, type UmecMiddlewareNext, type UmecMiddlewareOptions, type UmecRedirectStatus, createUmecMiddleware };
@@ -0,0 +1,7 @@
1
+ import {
2
+ createUmecMiddleware
3
+ } from "./chunk-WZFM6R5J.js";
4
+ export {
5
+ createUmecMiddleware
6
+ };
7
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,23 @@
1
+ type UnshippedOrder = {
2
+ id: string;
3
+ name: string | null;
4
+ items_json: string | null;
5
+ amount: number | null;
6
+ created_at: number | string | null;
7
+ };
8
+ type NotifyEnv = {
9
+ DISCORD_WEBHOOK_URL?: string;
10
+ RESEND_API_KEY?: string;
11
+ };
12
+ type NotifyProduct = {
13
+ name: string;
14
+ };
15
+ type NotifyProductsMap = Record<string, NotifyProduct | undefined>;
16
+ type NotifyUnshippedOptions = {
17
+ alertFrom: string;
18
+ alertTo: string;
19
+ products?: NotifyProductsMap;
20
+ };
21
+ declare function notifyUnshipped(orders: UnshippedOrder[], env: NotifyEnv, { alertFrom, alertTo, products }: NotifyUnshippedOptions): Promise<void>;
22
+
23
+ export { type NotifyEnv, type NotifyProduct, type NotifyProductsMap, type NotifyUnshippedOptions, type UnshippedOrder, notifyUnshipped };
package/dist/notify.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ notifyUnshipped
3
+ } from "./chunk-QRFFBMFN.js";
4
+ export {
5
+ notifyUnshipped
6
+ };
7
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,9 @@
1
+ type SubscribeHandler = ({ request }: {
2
+ request: Request;
3
+ }) => Promise<Response>;
4
+ type SubscribeHandlerOptions = {
5
+ isProduction: boolean;
6
+ };
7
+ declare function createSubscribeHandler({ isProduction }: SubscribeHandlerOptions): SubscribeHandler;
8
+
9
+ export { type SubscribeHandler, type SubscribeHandlerOptions, createSubscribeHandler };
@@ -0,0 +1,8 @@
1
+ import {
2
+ createSubscribeHandler
3
+ } from "./chunk-X2SNVVFW.js";
4
+ import "./chunk-7TJANK6S.js";
5
+ export {
6
+ createSubscribeHandler
7
+ };
8
+ //# sourceMappingURL=subscribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}