alchemy 0.65.1 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/alchemy.js +139 -79
- package/bin/commands/init.ts +6 -3
- package/bin/services/cdp-manager/server.ts +3 -2
- package/bin/services/execute-alchemy.ts +33 -3
- package/bin/services/find-workspace-root.ts +48 -0
- package/lib/alchemy.d.ts +20 -3
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +18 -5
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +51 -1
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +17 -5
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js +3 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +4 -3
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +1 -1
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/internal-worker-bundle.d.ts +1 -1
- package/lib/cloudflare/bundle/internal-worker-bundle.d.ts.map +1 -1
- package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.js +3 -3
- package/lib/cloudflare/cloudflare-env-proxy.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +2 -1
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/d1-local-migrations.d.ts +1 -0
- package/lib/cloudflare/d1-local-migrations.d.ts.map +1 -1
- package/lib/cloudflare/d1-local-migrations.js +3 -2
- package/lib/cloudflare/d1-local-migrations.js.map +1 -1
- package/lib/cloudflare/hyperdrive-ref.d.ts +23 -0
- package/lib/cloudflare/hyperdrive-ref.d.ts.map +1 -0
- package/lib/cloudflare/hyperdrive-ref.js +52 -0
- package/lib/cloudflare/hyperdrive-ref.js.map +1 -0
- package/lib/cloudflare/index.d.ts +2 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +2 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.js +1 -1
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts +2 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +11 -6
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/delete.d.ts +3 -2
- package/lib/cloudflare/miniflare/delete.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/delete.js +13 -12
- package/lib/cloudflare/miniflare/delete.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts +3 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +29 -13
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts +24 -13
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +95 -79
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/miniflare/paths.d.ts +4 -4
- package/lib/cloudflare/miniflare/paths.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/paths.js +10 -4
- package/lib/cloudflare/miniflare/paths.js.map +1 -1
- package/lib/cloudflare/miniflare/symlink-miniflare-state.d.ts +2 -0
- package/lib/cloudflare/miniflare/symlink-miniflare-state.d.ts.map +1 -0
- package/lib/cloudflare/miniflare/symlink-miniflare-state.js +19 -0
- package/lib/cloudflare/miniflare/symlink-miniflare-state.js.map +1 -0
- package/lib/cloudflare/miniflare/tunnel.d.ts +18 -0
- package/lib/cloudflare/miniflare/tunnel.d.ts.map +1 -0
- package/lib/cloudflare/miniflare/tunnel.js +79 -0
- package/lib/cloudflare/miniflare/tunnel.js.map +1 -0
- package/lib/cloudflare/next.d.ts.map +1 -1
- package/lib/cloudflare/next.js +3 -0
- package/lib/cloudflare/next.js.map +1 -1
- package/lib/cloudflare/nuxt/plugin.d.ts.map +1 -1
- package/lib/cloudflare/nuxt/plugin.js +3 -3
- package/lib/cloudflare/nuxt/plugin.js.map +1 -1
- package/lib/cloudflare/quick-tunnel.d.ts +1 -1
- package/lib/cloudflare/redwood/plugin.js +2 -2
- package/lib/cloudflare/redwood/plugin.js.map +1 -1
- package/lib/cloudflare/secret-ref.d.ts +38 -0
- package/lib/cloudflare/secret-ref.d.ts.map +1 -0
- package/lib/cloudflare/secret-ref.js +38 -0
- package/lib/cloudflare/secret-ref.js.map +1 -0
- package/lib/cloudflare/vite/plugin.d.ts.map +1 -1
- package/lib/cloudflare/vite/plugin.js +6 -3
- package/lib/cloudflare/vite/plugin.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +5 -16
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +7 -9
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +16 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +16 -2
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +2 -1
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +5 -6
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/llms.cloudflare.txt +1 -1
- package/lib/scope.d.ts +26 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +12 -5
- package/lib/scope.js.map +1 -1
- package/lib/state/file-system-state-store.d.ts.map +1 -1
- package/lib/state/file-system-state-store.js +1 -2
- package/lib/state/file-system-state-store.js.map +1 -1
- package/lib/state/sqlite-state-store.d.ts +11 -0
- package/lib/state/sqlite-state-store.d.ts.map +1 -1
- package/lib/state/sqlite-state-store.js +22 -14
- package/lib/state/sqlite-state-store.js.map +1 -1
- package/lib/stripe/customer.d.ts +0 -8
- package/lib/stripe/customer.d.ts.map +1 -1
- package/lib/stripe/customer.js +0 -6
- package/lib/stripe/customer.js.map +1 -1
- package/lib/stripe/price.d.ts +23 -23
- package/lib/stripe/price.d.ts.map +1 -1
- package/lib/stripe/price.js +6 -22
- package/lib/stripe/price.js.map +1 -1
- package/lib/test/vitest.d.ts.map +1 -1
- package/lib/test/vitest.js +1 -1
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/assert-never.js +1 -1
- package/lib/util/assert-never.js.map +1 -1
- package/lib/util/cli.d.ts.map +1 -1
- package/lib/util/cli.js +12 -13
- package/lib/util/cli.js.map +1 -1
- package/lib/util/idempotent-spawn.d.ts.map +1 -1
- package/lib/util/idempotent-spawn.js +5 -8
- package/lib/util/idempotent-spawn.js.map +1 -1
- package/lib/util/root-dir.d.ts +2 -0
- package/lib/util/root-dir.d.ts.map +1 -0
- package/lib/util/root-dir.js +2 -0
- package/lib/util/root-dir.js.map +1 -0
- package/package.json +4 -4
- package/src/alchemy.ts +43 -8
- package/src/apply.ts +58 -3
- package/src/cloudflare/bindings.ts +21 -4
- package/src/cloudflare/bound.ts +50 -52
- package/src/cloudflare/bucket.ts +1 -1
- package/src/cloudflare/bundle/internal-worker-bundle.ts +2 -1
- package/src/cloudflare/cloudflare-env-proxy.ts +5 -5
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/d1-database.ts +2 -1
- package/src/cloudflare/d1-local-migrations.ts +10 -6
- package/src/cloudflare/hyperdrive-ref.ts +98 -0
- package/src/cloudflare/index.ts +2 -0
- package/src/cloudflare/kv-namespace.ts +1 -1
- package/src/cloudflare/miniflare/build-worker-options.ts +21 -12
- package/src/cloudflare/miniflare/delete.ts +19 -11
- package/src/cloudflare/miniflare/miniflare-controller.ts +33 -14
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +122 -102
- package/src/cloudflare/miniflare/paths.ts +11 -7
- package/src/cloudflare/miniflare/symlink-miniflare-state.ts +19 -0
- package/src/cloudflare/miniflare/tunnel.ts +116 -0
- package/src/cloudflare/next.ts +3 -0
- package/src/cloudflare/nuxt/plugin.ts +12 -3
- package/src/cloudflare/redwood/plugin.ts +2 -2
- package/src/cloudflare/secret-ref.ts +56 -0
- package/src/cloudflare/vite/plugin.ts +8 -3
- package/src/cloudflare/website.ts +5 -19
- package/src/cloudflare/worker-metadata.ts +14 -12
- package/src/cloudflare/worker.ts +36 -3
- package/src/cloudflare/wrangler.json.ts +14 -11
- package/src/llms.cloudflare.txt +1 -1
- package/src/scope.ts +38 -6
- package/src/state/file-system-state-store.ts +1 -2
- package/src/state/sqlite-state-store.ts +41 -14
- package/src/stripe/customer.ts +0 -14
- package/src/stripe/price.ts +59 -56
- package/src/test/vitest.ts +4 -1
- package/src/util/assert-never.ts +1 -1
- package/src/util/cli.ts +15 -13
- package/src/util/idempotent-spawn.ts +5 -6
- package/src/util/root-dir.ts +1 -0
- package/templates/nextjs/alchemy.run.ts +2 -2
- package/templates/rwsdk/.cursor/rules/alchemy_cloudflare.mdc +1 -1
- package/templates/rwsdk/package.json +3 -3
- package/workers/cloudflare-state-store.js +1 -1
- package/workers/tunnel-proxy.js +110 -0
- package/workers/tunnel-proxy.ts +121 -0
package/src/stripe/price.ts
CHANGED
|
@@ -4,41 +4,60 @@ import { Resource } from "../resource.ts";
|
|
|
4
4
|
import type { Secret } from "../secret.ts";
|
|
5
5
|
import { logger } from "../util/logger.ts";
|
|
6
6
|
import { createStripeClient, handleStripeDeleteError } from "./client.ts";
|
|
7
|
+
import type { Meter } from "./meter.ts";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Properties for price recurring configuration
|
|
10
11
|
*/
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
12
|
+
export type PriceRecurring =
|
|
13
|
+
// If usageType is 'metered', meter is required
|
|
14
|
+
| {
|
|
15
|
+
/**
|
|
16
|
+
* Specifies billing frequency. Either 'day', 'week', 'month' or 'year'.
|
|
17
|
+
*/
|
|
18
|
+
interval: Stripe.PriceCreateParams.Recurring.Interval;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The number of intervals between subscription billings. For example, interval=month and interval_count=3 bills every 3 months.
|
|
22
|
+
*/
|
|
23
|
+
intervalCount?: number;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configures how the quantity per period should be determined, can be either 'metered' or 'licensed'.
|
|
27
|
+
* 'metered' will aggregate the total usage based on usage records.
|
|
28
|
+
*/
|
|
29
|
+
usageType: "metered";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The ID of the billing meter this price is associated with.
|
|
33
|
+
* Required when usageType = 'metered'.
|
|
34
|
+
*/
|
|
35
|
+
meter: string | Meter;
|
|
36
|
+
}
|
|
37
|
+
// If usageType is 'licensed' or not provided, meter is not allowed
|
|
38
|
+
| {
|
|
39
|
+
/**
|
|
40
|
+
* Specifies billing frequency. Either 'day', 'week', 'month' or 'year'.
|
|
41
|
+
*/
|
|
42
|
+
interval: Stripe.PriceCreateParams.Recurring.Interval;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The number of intervals between subscription billings. For example, interval=month and interval_count=3 bills every 3 months.
|
|
46
|
+
*/
|
|
47
|
+
intervalCount?: number;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configures how the quantity per period should be determined, can be either 'metered' or 'licensed'.
|
|
51
|
+
* 'licensed' will automatically bill the quantity set for a plan when adding it to a subscription.
|
|
52
|
+
*/
|
|
53
|
+
usageType?: "licensed" | undefined;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The ID of the billing meter this price is associated with.
|
|
57
|
+
* Not applicable when usageType is not 'metered'.
|
|
58
|
+
*/
|
|
59
|
+
meter?: never;
|
|
60
|
+
};
|
|
42
61
|
|
|
43
62
|
type TaxBehavior = Stripe.PriceCreateParams.TaxBehavior;
|
|
44
63
|
type BillingScheme = Stripe.PriceCreateParams.BillingScheme;
|
|
@@ -262,19 +281,6 @@ export interface Price extends Resource<"stripe::Price">, PriceProps {
|
|
|
262
281
|
* });
|
|
263
282
|
*
|
|
264
283
|
* @example
|
|
265
|
-
* // Create a metered price for usage-based billing
|
|
266
|
-
* const meteredPrice = await Price("storage", {
|
|
267
|
-
* currency: "usd",
|
|
268
|
-
* unitAmount: 25, // $0.25 per GB
|
|
269
|
-
* product: "prod_xyz",
|
|
270
|
-
* recurring: {
|
|
271
|
-
* interval: "month",
|
|
272
|
-
* usageType: "metered",
|
|
273
|
-
* aggregateUsage: "sum"
|
|
274
|
-
* }
|
|
275
|
-
* });
|
|
276
|
-
*
|
|
277
|
-
* @example
|
|
278
284
|
* // Create a graduated tiered price with usage-based billing
|
|
279
285
|
* const tieredPrice = await Price("api-usage", {
|
|
280
286
|
* currency: "usd",
|
|
@@ -428,22 +434,21 @@ export const Price = Resource(
|
|
|
428
434
|
interval: props.recurring.interval,
|
|
429
435
|
interval_count: props.recurring.intervalCount,
|
|
430
436
|
usage_type: props.recurring.usageType,
|
|
431
|
-
aggregate_usage: props.recurring.aggregateUsage,
|
|
432
437
|
};
|
|
433
438
|
|
|
434
439
|
// Add meter to recurring if present (only for metered usage type)
|
|
435
|
-
if (
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
// Extend the recurring params with meter
|
|
440
|
+
if (
|
|
441
|
+
props.recurring.usageType === "metered" &&
|
|
442
|
+
props.recurring.meter
|
|
443
|
+
) {
|
|
442
444
|
(
|
|
443
445
|
createParams.recurring as Stripe.PriceCreateParams.Recurring & {
|
|
444
446
|
meter: string;
|
|
445
447
|
}
|
|
446
|
-
).meter =
|
|
448
|
+
).meter =
|
|
449
|
+
typeof props.recurring.meter === "string"
|
|
450
|
+
? props.recurring.meter
|
|
451
|
+
: props.recurring.meter.id;
|
|
447
452
|
}
|
|
448
453
|
}
|
|
449
454
|
|
|
@@ -514,8 +519,6 @@ export const Price = Resource(
|
|
|
514
519
|
intervalCount: price.recurring.interval_count,
|
|
515
520
|
usageType: price.recurring
|
|
516
521
|
.usage_type as Stripe.PriceCreateParams.Recurring.UsageType,
|
|
517
|
-
aggregateUsage: price.recurring
|
|
518
|
-
.aggregate_usage as Stripe.PriceCreateParams.Recurring.AggregateUsage,
|
|
519
522
|
meter: (
|
|
520
523
|
price.recurring as Stripe.Price.Recurring & { meter?: string }
|
|
521
524
|
).meter,
|
|
@@ -552,7 +555,7 @@ export const Price = Resource(
|
|
|
552
555
|
active: price.active,
|
|
553
556
|
billingScheme: price.billing_scheme as BillingScheme,
|
|
554
557
|
nickname: price.nickname || undefined,
|
|
555
|
-
recurring: recurring,
|
|
558
|
+
recurring: recurring as PriceRecurring,
|
|
556
559
|
metadata: price.metadata || undefined,
|
|
557
560
|
taxBehavior: price.tax_behavior as TaxBehavior,
|
|
558
561
|
createdAt: price.created,
|
package/src/test/vitest.ts
CHANGED
|
@@ -112,7 +112,10 @@ export function test(
|
|
|
112
112
|
return new D1StateStore(scope);
|
|
113
113
|
default:
|
|
114
114
|
return new SQLiteStateStore(scope, {
|
|
115
|
-
filename:
|
|
115
|
+
filename: path.join(
|
|
116
|
+
scope.dotAlchemy,
|
|
117
|
+
`${path.relative(process.cwd(), meta.filename)}.sqlite`,
|
|
118
|
+
),
|
|
116
119
|
});
|
|
117
120
|
}
|
|
118
121
|
};
|
package/src/util/assert-never.ts
CHANGED
package/src/util/cli.ts
CHANGED
|
@@ -55,16 +55,16 @@ interface AlchemyInfo {
|
|
|
55
55
|
appName: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
let
|
|
58
|
+
let loggers: {
|
|
59
|
+
[appName: string]: LoggerApi;
|
|
60
|
+
} = {};
|
|
61
|
+
|
|
59
62
|
export const createLoggerInstance = (
|
|
60
63
|
alchemyInfo: AlchemyInfo,
|
|
61
64
|
customLogger?: LoggerApi,
|
|
62
65
|
) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Use custom logger if provided, otherwise use the basic fallback logger
|
|
66
|
-
loggerApi = customLogger ?? createFallbackLogger(alchemyInfo);
|
|
67
|
-
return loggerApi;
|
|
66
|
+
return (loggers[alchemyInfo.appName] ??=
|
|
67
|
+
customLogger ?? createFallbackLogger(alchemyInfo));
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
export const createDummyLogger = (): LoggerApi => {
|
|
@@ -79,13 +79,15 @@ export const createDummyLogger = (): LoggerApi => {
|
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
export const createFallbackLogger = (alchemyInfo: AlchemyInfo): LoggerApi => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
if (alchemyInfo.phase !== "read") {
|
|
83
|
+
console.log(dedent`
|
|
84
|
+
${colorize("Alchemy", "cyanBright")} (v${packageJson.version})
|
|
85
|
+
App: ${alchemyInfo.appName}
|
|
86
|
+
Phase: ${alchemyInfo.phase}
|
|
87
|
+
Stage: ${alchemyInfo.stage}
|
|
88
|
+
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
89
91
|
|
|
90
92
|
return {
|
|
91
93
|
log: console.log,
|
|
@@ -161,7 +161,7 @@ export async function idempotentSpawn({
|
|
|
161
161
|
{
|
|
162
162
|
write,
|
|
163
163
|
chunkSize = 64 * 1024,
|
|
164
|
-
|
|
164
|
+
tickMs = 100,
|
|
165
165
|
}: {
|
|
166
166
|
stateKey: string;
|
|
167
167
|
write: (buf: Buffer) => boolean;
|
|
@@ -229,15 +229,14 @@ export async function idempotentSpawn({
|
|
|
229
229
|
await drain();
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// }, tickMs);
|
|
232
|
+
const tick = setInterval(() => {
|
|
233
|
+
drain().catch(() => {});
|
|
234
|
+
}, tickMs);
|
|
236
235
|
|
|
237
236
|
return async function stop() {
|
|
238
237
|
closed = true;
|
|
239
238
|
watcher.close();
|
|
240
|
-
|
|
239
|
+
clearInterval(tick);
|
|
241
240
|
await fh.close().catch(() => {});
|
|
242
241
|
await persist();
|
|
243
242
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ALCHEMY_ROOT = process.env.ALCHEMY_ROOT ?? process.cwd();
|
|
@@ -5,12 +5,12 @@ import { Nextjs } from "alchemy/cloudflare";
|
|
|
5
5
|
|
|
6
6
|
const app = await alchemy("{projectName}");
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const worker = await Nextjs("website", {
|
|
9
9
|
name: `${app.name}-${app.stage}-website`,
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
console.log({
|
|
13
|
-
url:
|
|
13
|
+
url: worker.url,
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
await app.finalize();
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"worker:run": "rw-scripts worker-run"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@prisma/adapter-d1": "~6.
|
|
31
|
-
"@prisma/client": "~6.
|
|
30
|
+
"@prisma/adapter-d1": "~6.16.1",
|
|
31
|
+
"@prisma/client": "~6.16.1",
|
|
32
32
|
"@simplewebauthn/browser": "^13.1.0",
|
|
33
33
|
"@simplewebauthn/server": "^13.1.1",
|
|
34
34
|
"rwsdk": "^0.1.35"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@types/node": "^22.14.0",
|
|
40
40
|
"@types/react": "^19.1.2",
|
|
41
41
|
"@types/react-dom": "^19.1.2",
|
|
42
|
-
"prisma": "~6.
|
|
42
|
+
"prisma": "~6.16.1",
|
|
43
43
|
"typescript": "^5.8.3",
|
|
44
44
|
"vibe-rules": "^0.2.31",
|
|
45
45
|
"alchemy": "workspace:*",
|
|
@@ -5397,7 +5397,7 @@ var migrations_default = {
|
|
|
5397
5397
|
|
|
5398
5398
|
// src/util/assert-never.ts
|
|
5399
5399
|
function assertNever(value, message) {
|
|
5400
|
-
throw new Error(message ?? `Unexpected value: ${value}`);
|
|
5400
|
+
throw new Error(message ?? `Unexpected value: ${JSON.stringify(value)}`);
|
|
5401
5401
|
}
|
|
5402
5402
|
|
|
5403
5403
|
// src/state/schema.ts
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// workers/tunnel-proxy.ts
|
|
2
|
+
var tunnel_proxy_default = {
|
|
3
|
+
async fetch(request, env) {
|
|
4
|
+
const url = new URL(request.url);
|
|
5
|
+
url.host = env.TUNNEL_HOST;
|
|
6
|
+
const headers = new Headers(request.headers);
|
|
7
|
+
headers.set("alchemy-worker-name", env.WORKER_NAME);
|
|
8
|
+
const response = await fetch(url, {
|
|
9
|
+
method: request.method,
|
|
10
|
+
headers,
|
|
11
|
+
body: request.body,
|
|
12
|
+
redirect: "manual"
|
|
13
|
+
});
|
|
14
|
+
if (response.status === 530) {
|
|
15
|
+
const error = await extractResponseError(response);
|
|
16
|
+
const headers2 = new Headers(response.headers);
|
|
17
|
+
headers2.set("Content-Type", "text/html");
|
|
18
|
+
return new Response(
|
|
19
|
+
renderErrorHtml({
|
|
20
|
+
name: env.WORKER_NAME,
|
|
21
|
+
error,
|
|
22
|
+
rayId: response.headers.get("cf-ray") ?? "Unknown"
|
|
23
|
+
}),
|
|
24
|
+
{
|
|
25
|
+
status: 530,
|
|
26
|
+
headers: headers2
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return response;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var extractResponseError = async (response) => {
|
|
34
|
+
const error = response.headers.get("Alchemy-Error");
|
|
35
|
+
if (error) {
|
|
36
|
+
return error;
|
|
37
|
+
}
|
|
38
|
+
let code;
|
|
39
|
+
let message;
|
|
40
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
41
|
+
const rewriter = new HTMLRewriter();
|
|
42
|
+
rewriter.on("h1", {
|
|
43
|
+
text(text) {
|
|
44
|
+
if (code) return;
|
|
45
|
+
const match = text.text.match(/(\d+)/);
|
|
46
|
+
if (match) {
|
|
47
|
+
code = match[1];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
rewriter.on("h2", {
|
|
52
|
+
text(text) {
|
|
53
|
+
if (message) return;
|
|
54
|
+
message = text.text;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
rewriter.onDocument({
|
|
58
|
+
end() {
|
|
59
|
+
resolve([code, message].filter(Boolean).join(": ") || "Unknown");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
await rewriter.transform(response).text();
|
|
63
|
+
return promise;
|
|
64
|
+
};
|
|
65
|
+
var renderErrorHtml = (props) => `
|
|
66
|
+
<!DOCTYPE html>
|
|
67
|
+
<html lang="en">
|
|
68
|
+
|
|
69
|
+
<head>
|
|
70
|
+
<title>Alchemy | Tunnel Error</title>
|
|
71
|
+
<meta charset="utf-8">
|
|
72
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
73
|
+
<link rel="shortcut icon" href="https://alchemy.run/potion.png" type="image/png" />
|
|
74
|
+
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"><\/script>
|
|
75
|
+
</head>
|
|
76
|
+
|
|
77
|
+
<body class="font-sans bg-slate-100 text-slate-950">
|
|
78
|
+
<div class="flex flex-col items-center justify-center h-screen">
|
|
79
|
+
<div class="border-t-3 border-red-600 px-5 py-7 bg-white gap-3 flex flex-col w-full max-w-lg">
|
|
80
|
+
<h1 class="text-2xl font-semibold">530: Tunnel Error</h1>
|
|
81
|
+
<p class="text-slate-800">The worker "${props.name}" could not be reached.</p>
|
|
82
|
+
<p class="text-slate-800">Please ensure that <code
|
|
83
|
+
class="bg-slate-100 text-slate-800 p-1 rounded-md text-sm">alchemy dev</code>
|
|
84
|
+
is running and tunnel mode is enabled for the worker.</p>
|
|
85
|
+
<div class="flex flex-col gap-2 border-l-2 border-slate-300 pl-3 my-1.5">
|
|
86
|
+
<div class="text-sm flex flex-col gap-1">
|
|
87
|
+
<div class="text-slate-500">Error:</div>
|
|
88
|
+
<div class="text-slate-800 font-mono">${props.error}</div>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="text-sm flex flex-col gap-1">
|
|
91
|
+
<div class="text-slate-500">Ray ID:</div>
|
|
92
|
+
<div class="text-slate-800 font-mono">${props.rayId}</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<p class="text-slate-800"><a href="https://alchemy.run/docs/guides/cloudflare-tunnel"
|
|
96
|
+
class="text-slate-800 underline underline-offset-2 hover:no-underline" target="_blank">Learn more about tunnel
|
|
97
|
+
mode in
|
|
98
|
+
Alchemy</a>.</p>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
|
|
101
|
+
<p class="text-sm text-slate-500">Alchemy ${"0.67.0"}</p>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</body>
|
|
105
|
+
|
|
106
|
+
</html>
|
|
107
|
+
`;
|
|
108
|
+
export {
|
|
109
|
+
tunnel_proxy_default as default
|
|
110
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
|
|
3
|
+
interface Env {
|
|
4
|
+
WORKER_NAME: string;
|
|
5
|
+
TUNNEL_HOST: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare const ALCHEMY_VERSION: string; // injected during `bun run build:workers`
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
async fetch(request: Request, env: Env) {
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
url.host = env.TUNNEL_HOST;
|
|
14
|
+
const headers = new Headers(request.headers);
|
|
15
|
+
headers.set("alchemy-worker-name", env.WORKER_NAME);
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
method: request.method,
|
|
18
|
+
headers,
|
|
19
|
+
body: request.body,
|
|
20
|
+
redirect: "manual",
|
|
21
|
+
});
|
|
22
|
+
if (response.status === 530) {
|
|
23
|
+
const error = await extractResponseError(response);
|
|
24
|
+
const headers = new Headers(response.headers);
|
|
25
|
+
headers.set("Content-Type", "text/html");
|
|
26
|
+
return new Response(
|
|
27
|
+
renderErrorHtml({
|
|
28
|
+
name: env.WORKER_NAME,
|
|
29
|
+
error,
|
|
30
|
+
rayId: response.headers.get("cf-ray") ?? "Unknown",
|
|
31
|
+
}),
|
|
32
|
+
{
|
|
33
|
+
status: 530,
|
|
34
|
+
headers,
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return response;
|
|
39
|
+
},
|
|
40
|
+
} as ExportedHandler<Env>;
|
|
41
|
+
|
|
42
|
+
const extractResponseError = async (response: Response) => {
|
|
43
|
+
const error = response.headers.get("Alchemy-Error");
|
|
44
|
+
if (error) {
|
|
45
|
+
return error;
|
|
46
|
+
}
|
|
47
|
+
let code: string | undefined;
|
|
48
|
+
let message: string | undefined;
|
|
49
|
+
const { promise, resolve } = Promise.withResolvers<string>();
|
|
50
|
+
const rewriter = new HTMLRewriter();
|
|
51
|
+
rewriter.on("h1", {
|
|
52
|
+
text(text) {
|
|
53
|
+
if (code) return;
|
|
54
|
+
const match = text.text.match(/(\d+)/);
|
|
55
|
+
if (match) {
|
|
56
|
+
code = match[1];
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
rewriter.on("h2", {
|
|
61
|
+
text(text) {
|
|
62
|
+
if (message) return;
|
|
63
|
+
message = text.text;
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
rewriter.onDocument({
|
|
67
|
+
end() {
|
|
68
|
+
resolve([code, message].filter(Boolean).join(": ") || "Unknown");
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
await rewriter.transform(response).text(); // consume response body so rewriter runs
|
|
72
|
+
return promise;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const renderErrorHtml = (props: {
|
|
76
|
+
name: string;
|
|
77
|
+
error: string;
|
|
78
|
+
rayId: string;
|
|
79
|
+
}) => `
|
|
80
|
+
<!DOCTYPE html>
|
|
81
|
+
<html lang="en">
|
|
82
|
+
|
|
83
|
+
<head>
|
|
84
|
+
<title>Alchemy | Tunnel Error</title>
|
|
85
|
+
<meta charset="utf-8">
|
|
86
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
87
|
+
<link rel="shortcut icon" href="https://alchemy.run/potion.png" type="image/png" />
|
|
88
|
+
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
89
|
+
</head>
|
|
90
|
+
|
|
91
|
+
<body class="font-sans bg-slate-100 text-slate-950">
|
|
92
|
+
<div class="flex flex-col items-center justify-center h-screen">
|
|
93
|
+
<div class="border-t-3 border-red-600 px-5 py-7 bg-white gap-3 flex flex-col w-full max-w-lg">
|
|
94
|
+
<h1 class="text-2xl font-semibold">530: Tunnel Error</h1>
|
|
95
|
+
<p class="text-slate-800">The worker "${props.name}" could not be reached.</p>
|
|
96
|
+
<p class="text-slate-800">Please ensure that <code
|
|
97
|
+
class="bg-slate-100 text-slate-800 p-1 rounded-md text-sm">alchemy dev</code>
|
|
98
|
+
is running and tunnel mode is enabled for the worker.</p>
|
|
99
|
+
<div class="flex flex-col gap-2 border-l-2 border-slate-300 pl-3 my-1.5">
|
|
100
|
+
<div class="text-sm flex flex-col gap-1">
|
|
101
|
+
<div class="text-slate-500">Error:</div>
|
|
102
|
+
<div class="text-slate-800 font-mono">${props.error}</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="text-sm flex flex-col gap-1">
|
|
105
|
+
<div class="text-slate-500">Ray ID:</div>
|
|
106
|
+
<div class="text-slate-800 font-mono">${props.rayId}</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<p class="text-slate-800"><a href="https://alchemy.run/docs/guides/cloudflare-tunnel"
|
|
110
|
+
class="text-slate-800 underline underline-offset-2 hover:no-underline" target="_blank">Learn more about tunnel
|
|
111
|
+
mode in
|
|
112
|
+
Alchemy</a>.</p>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
|
|
115
|
+
<p class="text-sm text-slate-500">Alchemy ${ALCHEMY_VERSION}</p>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</body>
|
|
119
|
+
|
|
120
|
+
</html>
|
|
121
|
+
`;
|