alchemy 2.0.0-beta.70 → 2.0.0-beta.71
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/LICENSE +201 -0
- package/NOTICE +5 -0
- package/README.md +3 -1
- package/THIRD_PARTY_LICENSES.md +127 -0
- package/bin/exec.js +25 -9
- package/bin/exec.js.map +1 -1
- package/lib/AWS/DataZone/Domain.d.ts.map +1 -1
- package/lib/AWS/DataZone/Domain.js +7 -7
- package/lib/AWS/DataZone/Domain.js.map +1 -1
- package/lib/AWS/EC2/InternetGateway.d.ts.map +1 -1
- package/lib/AWS/EC2/InternetGateway.js +14 -16
- package/lib/AWS/EC2/InternetGateway.js.map +1 -1
- package/lib/AWS/EC2/LingeringEnis.d.ts +2 -2
- package/lib/AWS/EC2/LingeringEnis.d.ts.map +1 -1
- package/lib/AWS/EC2/LingeringEnis.js +9 -7
- package/lib/AWS/EC2/LingeringEnis.js.map +1 -1
- package/lib/AWS/RDSData/Marshall.d.ts +23 -0
- package/lib/AWS/RDSData/Marshall.d.ts.map +1 -0
- package/lib/AWS/RDSData/Marshall.js +87 -0
- package/lib/AWS/RDSData/Marshall.js.map +1 -0
- package/lib/AWS/RDSData/index.d.ts +1 -0
- package/lib/AWS/RDSData/index.d.ts.map +1 -1
- package/lib/AWS/RDSData/index.js +1 -0
- package/lib/AWS/RDSData/index.js.map +1 -1
- package/lib/AWS/ResourceGroups/Group.d.ts.map +1 -1
- package/lib/AWS/ResourceGroups/Group.js +3 -1
- package/lib/AWS/ResourceGroups/Group.js.map +1 -1
- package/lib/AWS/S3Tables/Namespace.d.ts.map +1 -1
- package/lib/AWS/S3Tables/Namespace.js +3 -1
- package/lib/AWS/S3Tables/Namespace.js.map +1 -1
- package/lib/AWS/S3Tables/TableBucket.d.ts.map +1 -1
- package/lib/AWS/S3Tables/TableBucket.js +8 -2
- package/lib/AWS/S3Tables/TableBucket.js.map +1 -1
- package/lib/AWS/S3Vectors/VectorBucket.d.ts.map +1 -1
- package/lib/AWS/S3Vectors/VectorBucket.js +6 -1
- package/lib/AWS/S3Vectors/VectorBucket.js.map +1 -1
- package/lib/AWS/Website/cfcode.d.ts.map +1 -1
- package/lib/AWS/Website/cfcode.js +2 -0
- package/lib/AWS/Website/cfcode.js.map +1 -1
- package/lib/Apply.d.ts.map +1 -1
- package/lib/Apply.js +22 -8
- package/lib/Apply.js.map +1 -1
- package/lib/Auth/AuthProvider.d.ts.map +1 -1
- package/lib/Auth/AuthProvider.js +1 -1
- package/lib/Auth/AuthProvider.js.map +1 -1
- package/lib/Bundle/Bundle.js +2 -2
- package/lib/Bundle/Bundle.js.map +1 -1
- package/lib/Cli/commands/cloudflare.js +1 -1
- package/lib/Cli/commands/cloudflare.js.map +1 -1
- package/lib/Cli/commands/logs.d.ts.map +1 -1
- package/lib/Cli/commands/logs.js +2 -1
- package/lib/Cli/commands/logs.js.map +1 -1
- package/lib/Cli/commands/nuke.js +1 -1
- package/lib/Cli/commands/nuke.js.map +1 -1
- package/lib/Cli/commands/tail.d.ts.map +1 -1
- package/lib/Cli/commands/tail.js +2 -1
- package/lib/Cli/commands/tail.js.map +1 -1
- package/lib/Cloudflare/AI/LanguageModel.js +1 -1
- package/lib/Cloudflare/AI/LanguageModel.js.map +1 -1
- package/lib/Cloudflare/Access.js +1 -1
- package/lib/Cloudflare/Access.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerBundle.d.ts +1 -1
- package/lib/Cloudflare/Containers/ContainerBundle.js +1 -1
- package/lib/Cloudflare/Containers/ContainerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerProvider.js +24 -2
- package/lib/Cloudflare/Containers/ContainerProvider.js.map +1 -1
- package/lib/Cloudflare/Containers/LocalContainerProvider.d.ts +1 -1
- package/lib/Cloudflare/Containers/LocalContainerProvider.js +1 -1
- package/lib/Cloudflare/D1/Database.d.ts +2 -2
- package/lib/Cloudflare/D1/Database.d.ts.map +1 -1
- package/lib/Cloudflare/D1/LocalD1Gateway.d.ts +3 -3
- package/lib/Cloudflare/D1/LocalD1Gateway.d.ts.map +1 -1
- package/lib/Cloudflare/D1/LocalD1Gateway.js +3 -3
- package/lib/Cloudflare/D1/LocalD1Gateway.js.map +1 -1
- package/lib/Cloudflare/KV/LocalKVGateway.js +2 -2
- package/lib/Cloudflare/KV/LocalKVGateway.js.map +1 -1
- package/lib/Cloudflare/LocalGateway.d.ts +1 -1
- package/lib/Cloudflare/LocalGateway.d.ts.map +1 -1
- package/lib/Cloudflare/LocalGateway.js +1 -1
- package/lib/Cloudflare/LocalGateway.js.map +1 -1
- package/lib/Cloudflare/LocalRuntime.d.ts +1 -1
- package/lib/Cloudflare/LocalRuntime.d.ts.map +1 -1
- package/lib/Cloudflare/LocalRuntime.js +5 -4
- package/lib/Cloudflare/LocalRuntime.js.map +1 -1
- package/lib/Cloudflare/Queues/Consumer.d.ts +22 -5
- package/lib/Cloudflare/Queues/Consumer.d.ts.map +1 -1
- package/lib/Cloudflare/Queues/Consumer.js +129 -38
- package/lib/Cloudflare/Queues/Consumer.js.map +1 -1
- package/lib/Cloudflare/Queues/Queue.d.ts +4 -3
- package/lib/Cloudflare/Queues/Queue.d.ts.map +1 -1
- package/lib/Cloudflare/Queues/Queue.js +115 -6
- package/lib/Cloudflare/Queues/Queue.js.map +1 -1
- package/lib/Cloudflare/R2/LocalR2Gateway.js +2 -2
- package/lib/Cloudflare/R2/LocalR2Gateway.js.map +1 -1
- package/lib/Cloudflare/SecretsStore/LocalSecretsStoreGateway.d.ts +4 -4
- package/lib/Cloudflare/SecretsStore/LocalSecretsStoreGateway.d.ts.map +1 -1
- package/lib/Cloudflare/SecretsStore/LocalSecretsStoreGateway.js +2 -2
- package/lib/Cloudflare/SecretsStore/LocalSecretsStoreGateway.js.map +1 -1
- package/lib/Cloudflare/SecretsStore/Secret.d.ts +2 -2
- package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Api.js +7 -0
- package/lib/Cloudflare/StateStore/Api.js.map +1 -1
- package/lib/Cloudflare/Website/Astro.d.ts +19 -11
- package/lib/Cloudflare/Website/Astro.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Astro.js +16 -9
- package/lib/Cloudflare/Website/Astro.js.map +1 -1
- package/lib/Cloudflare/Website/Nextjs.d.ts +7 -4
- package/lib/Cloudflare/Website/Nextjs.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Nextjs.js +10 -8
- package/lib/Cloudflare/Website/Nextjs.js.map +1 -1
- package/lib/Cloudflare/Website/Nuxt.d.ts +5 -4
- package/lib/Cloudflare/Website/Nuxt.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Nuxt.js +4 -3
- package/lib/Cloudflare/Website/Nuxt.js.map +1 -1
- package/lib/Cloudflare/Website/Octane.d.ts +4 -3
- package/lib/Cloudflare/Website/Octane.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Octane.js +5 -4
- package/lib/Cloudflare/Website/Octane.js.map +1 -1
- package/lib/Cloudflare/Website/SvelteKit.d.ts +17 -10
- package/lib/Cloudflare/Website/SvelteKit.d.ts.map +1 -1
- package/lib/Cloudflare/Website/SvelteKit.js +9 -5
- package/lib/Cloudflare/Website/SvelteKit.js.map +1 -1
- package/lib/Cloudflare/Website/Waku.d.ts +20 -9
- package/lib/Cloudflare/Website/Waku.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Waku.js +10 -4
- package/lib/Cloudflare/Website/Waku.js.map +1 -1
- package/lib/Cloudflare/Workers/Assets.d.ts +54 -1
- package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Assets.js +60 -26
- package/lib/Cloudflare/Workers/Assets.js.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +2 -2
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +153 -49
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/RuntimeBindings.d.ts +3 -3
- package/lib/Cloudflare/Workers/RuntimeBindings.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/RuntimeBindings.js +2 -2
- package/lib/Cloudflare/Workers/RuntimeBindings.js.map +1 -1
- package/lib/Cloudflare/Workers/Source.d.ts +1 -1
- package/lib/Cloudflare/Workers/Source.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Source.js +1 -1
- package/lib/Cloudflare/Workers/Source.js.map +1 -1
- package/lib/Cloudflare/Workers/Sources/Rolldown.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Sources/Rolldown.js +3 -4
- package/lib/Cloudflare/Workers/Sources/Rolldown.js.map +1 -1
- package/lib/Cloudflare/Workers/Sources/Vite.d.ts +1 -1
- package/lib/Cloudflare/Workers/Sources/Vite.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Sources/Vite.js +36 -17
- package/lib/Cloudflare/Workers/Sources/Vite.js.map +1 -1
- package/lib/Cloudflare/Workers/ViteChild.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/ViteChild.js +6 -0
- package/lib/Cloudflare/Workers/ViteChild.js.map +1 -1
- package/lib/Cloudflare/Workers/ViteChild.shared.d.ts +1 -1
- package/lib/Cloudflare/Workers/ViteChild.shared.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/ViteChildRunner.js +5 -10
- package/lib/Cloudflare/Workers/ViteChildRunner.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +11 -4
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.d.ts +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.js +12 -2
- package/lib/Cloudflare/Workers/WorkerProvider.js.map +1 -1
- package/lib/Docker/Docker.d.ts.map +1 -1
- package/lib/Docker/Docker.js +6 -2
- package/lib/Docker/Docker.js.map +1 -1
- package/lib/Drizzle/{DurableObject.d.ts → Cloudflare.d.ts} +2 -1
- package/lib/Drizzle/Cloudflare.d.ts.map +1 -0
- package/lib/Drizzle/{DurableObject.js → Cloudflare.js} +2 -1
- package/lib/Drizzle/Cloudflare.js.map +1 -0
- package/lib/Drizzle/MySQL.d.ts +2 -2
- package/lib/Drizzle/MySQL.d.ts.map +1 -1
- package/lib/Drizzle/MySQL.js +5 -3
- package/lib/Drizzle/MySQL.js.map +1 -1
- package/lib/Drizzle/Postgres.d.ts +1 -1
- package/lib/Drizzle/Postgres.d.ts.map +1 -1
- package/lib/Drizzle/Postgres.js +4 -2
- package/lib/Drizzle/Postgres.js.map +1 -1
- package/lib/Drizzle/index.d.ts +0 -3
- package/lib/Drizzle/index.d.ts.map +1 -1
- package/lib/Drizzle/index.js +5 -3
- package/lib/Drizzle/index.js.map +1 -1
- package/lib/Neon/Migrations.d.ts.map +1 -1
- package/lib/Neon/Migrations.js +1 -5
- package/lib/Neon/Migrations.js.map +1 -1
- package/lib/PhysicalName.d.ts +8 -0
- package/lib/PhysicalName.d.ts.map +1 -1
- package/lib/PhysicalName.js +5 -2
- package/lib/PhysicalName.js.map +1 -1
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +16 -9
- package/lib/Plan.js.map +1 -1
- package/lib/ProviderMode.d.ts +45 -0
- package/lib/ProviderMode.d.ts.map +1 -1
- package/lib/ProviderMode.js +51 -0
- package/lib/ProviderMode.js.map +1 -1
- package/lib/SQL/ConnectionSource.d.ts +45 -0
- package/lib/SQL/ConnectionSource.d.ts.map +1 -0
- package/lib/SQL/ConnectionSource.js +51 -0
- package/lib/SQL/ConnectionSource.js.map +1 -0
- package/lib/SQL/MySQLDriver.d.ts +21 -0
- package/lib/SQL/MySQLDriver.d.ts.map +1 -0
- package/lib/SQL/MySQLDriver.js +31 -0
- package/lib/SQL/MySQLDriver.js.map +1 -0
- package/lib/SQL/PostgresDriver.d.ts +21 -0
- package/lib/SQL/PostgresDriver.d.ts.map +1 -0
- package/lib/SQL/PostgresDriver.js +29 -0
- package/lib/SQL/PostgresDriver.js.map +1 -0
- package/lib/State/HttpStateApi.d.ts +11 -11
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +17 -1
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/ResourceState.d.ts +6 -2
- package/lib/State/ResourceState.d.ts.map +1 -1
- package/lib/State/StateEncoding.d.ts +8 -0
- package/lib/State/StateEncoding.d.ts.map +1 -1
- package/lib/State/StateEncoding.js +38 -0
- package/lib/State/StateEncoding.js.map +1 -1
- package/lib/Sync.d.ts.map +1 -1
- package/lib/Sync.js +4 -2
- package/lib/Sync.js.map +1 -1
- package/lib/Test/Alchemy.d.ts.map +1 -1
- package/lib/Test/Alchemy.js +24 -14
- package/lib/Test/Alchemy.js.map +1 -1
- package/lib/Test/Core.d.ts +25 -8
- package/lib/Test/Core.d.ts.map +1 -1
- package/lib/Test/Core.js +24 -7
- package/lib/Test/Core.js.map +1 -1
- package/package.json +49 -40
- package/src/AWS/DataZone/Domain.ts +15 -13
- package/src/AWS/EC2/InternetGateway.ts +28 -32
- package/src/AWS/EC2/LingeringEnis.ts +11 -9
- package/src/AWS/RDSData/Marshall.ts +106 -0
- package/src/AWS/RDSData/index.ts +1 -0
- package/src/AWS/ResourceGroups/Group.ts +5 -1
- package/src/AWS/S3Tables/Namespace.ts +3 -1
- package/src/AWS/S3Tables/TableBucket.ts +8 -2
- package/src/AWS/S3Vectors/VectorBucket.ts +6 -1
- package/src/AWS/Website/cfcode.ts +2 -0
- package/src/Apply.ts +22 -9
- package/src/Auth/AuthProvider.ts +4 -7
- package/src/Bundle/Bundle.ts +2 -2
- package/src/Cli/commands/cloudflare.ts +1 -1
- package/src/Cli/commands/logs.ts +2 -1
- package/src/Cli/commands/nuke.ts +1 -1
- package/src/Cli/commands/tail.ts +2 -1
- package/src/Cloudflare/AI/LanguageModel.ts +3 -0
- package/src/Cloudflare/Access.ts +1 -1
- package/src/Cloudflare/Containers/ContainerBundle.ts +1 -1
- package/src/Cloudflare/Containers/ContainerProvider.ts +35 -5
- package/src/Cloudflare/Containers/LocalContainerProvider.ts +1 -1
- package/src/Cloudflare/D1/Database.ts +1 -1
- package/src/Cloudflare/D1/LocalD1Gateway.ts +4 -4
- package/src/Cloudflare/KV/LocalKVGateway.ts +2 -2
- package/src/Cloudflare/LocalGateway.ts +1 -1
- package/src/Cloudflare/LocalRuntime.ts +6 -4
- package/src/Cloudflare/Queues/Consumer.ts +183 -45
- package/src/Cloudflare/Queues/Queue.ts +157 -5
- package/src/Cloudflare/R2/LocalR2Gateway.ts +2 -2
- package/src/Cloudflare/SecretsStore/LocalSecretsStoreGateway.ts +2 -2
- package/src/Cloudflare/SecretsStore/Secret.ts +1 -1
- package/src/Cloudflare/StateStore/Api.ts +7 -0
- package/src/Cloudflare/Website/Astro.ts +20 -12
- package/src/Cloudflare/Website/Nextjs.ts +13 -8
- package/src/Cloudflare/Website/Nuxt.ts +6 -5
- package/src/Cloudflare/Website/Octane.ts +6 -4
- package/src/Cloudflare/Website/SvelteKit.ts +18 -11
- package/src/Cloudflare/Website/Waku.ts +21 -10
- package/src/Cloudflare/Workers/Assets.ts +122 -20
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +228 -81
- package/src/Cloudflare/Workers/RuntimeBindings.ts +3 -3
- package/src/Cloudflare/Workers/Source.ts +2 -2
- package/src/Cloudflare/Workers/Sources/Rolldown.ts +3 -4
- package/src/Cloudflare/Workers/Sources/Vite.ts +46 -24
- package/src/Cloudflare/Workers/ViteChild.shared.ts +1 -1
- package/src/Cloudflare/Workers/ViteChild.ts +6 -0
- package/src/Cloudflare/Workers/ViteChildRunner.ts +5 -10
- package/src/Cloudflare/Workers/Worker.ts +26 -6
- package/src/Cloudflare/Workers/WorkerProvider.ts +19 -2
- package/src/Docker/Docker.ts +17 -11
- package/src/Drizzle/{DurableObject.ts → Cloudflare.ts} +1 -0
- package/src/Drizzle/MySQL.ts +13 -3
- package/src/Drizzle/Postgres.ts +10 -2
- package/src/Drizzle/index.ts +6 -3
- package/src/Neon/Migrations.ts +1 -10
- package/src/PhysicalName.ts +17 -1
- package/src/Plan.ts +20 -9
- package/src/ProviderMode.ts +56 -0
- package/src/SQL/ConnectionSource.ts +101 -0
- package/src/SQL/MySQLDriver.ts +49 -0
- package/src/SQL/PostgresDriver.ts +45 -0
- package/src/State/LocalState.ts +24 -0
- package/src/State/ResourceState.ts +6 -2
- package/src/State/StateEncoding.ts +37 -0
- package/src/Sync.ts +4 -2
- package/src/Test/Alchemy.ts +17 -6
- package/src/Test/Core.ts +42 -15
- package/lib/Drizzle/DurableObject.d.ts.map +0 -1
- package/lib/Drizzle/DurableObject.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import cloudflare, {
|
|
2
2
|
type CloudflareVitePluginOptions,
|
|
3
|
-
} from "@
|
|
3
|
+
} from "@alchemy.run/cloudflare-runtime/vite";
|
|
4
4
|
import * as ConsoleService from "effect/Console";
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as Path from "effect/Path";
|
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
viteBuildOutputPlugin,
|
|
14
14
|
type ViteBuildOutput,
|
|
15
15
|
} from "../../../Bundle/Vite.ts";
|
|
16
|
+
import { viteSupportsPortZero } from "@alchemy.run/cloudflare-runtime/core/internal/Port";
|
|
16
17
|
import { hashDirectory, type MemoOptions } from "../../../Command/Memo.ts";
|
|
17
|
-
import { initialCwd } from "../../../Util/Node.ts";
|
|
18
|
+
import { findAvailablePort, initialCwd } from "../../../Util/Node.ts";
|
|
18
19
|
import { sha256Object } from "../../../Util/sha256.ts";
|
|
19
20
|
import { readAssets } from "../Assets.ts";
|
|
20
21
|
import type { SourceDevHandle, SourceProvider } from "../Source.ts";
|
|
@@ -23,7 +24,7 @@ import type { ViteOptions } from "../Worker.ts";
|
|
|
23
24
|
import { isWorkerLoader } from "../WorkerLoader.ts";
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
|
-
* This module statically imports `@
|
|
27
|
+
* This module statically imports `@alchemy.run/cloudflare-runtime/vite`
|
|
27
28
|
* (~0.5s to load), which is only needed for vite-based workers. Importers
|
|
28
29
|
* MUST load it lazily (`Effect.promise(() => import("./Sources/Vite.ts"))`
|
|
29
30
|
* from the dispatch in `Source.ts`, or the legacy vite arms in the
|
|
@@ -104,27 +105,48 @@ export const viteDev = (
|
|
|
104
105
|
pluginOptions: CloudflareVitePluginOptions,
|
|
105
106
|
serverOptions: vite.ServerOptions,
|
|
106
107
|
) =>
|
|
107
|
-
Effect.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
108
|
+
Effect.gen(function* () {
|
|
109
|
+
yield* Effect.sync(() => {
|
|
110
|
+
process.env[ALCHEMY_CLOUDFLARE_VITE_INJECTED] = "1";
|
|
111
|
+
});
|
|
112
|
+
const vite = yield* Effect.promise(() => loadVite(rootDir));
|
|
113
|
+
// `port: 0` is a true OS-assigned random port on Vite >= 8.2.1
|
|
114
|
+
// (vitejs/vite#23158); older Vite treats it as "no port given" and
|
|
115
|
+
// hunts upward from the 5173 default — colliding with (or
|
|
116
|
+
// IPv6-shadowing) user-facing dev ports. Substitute a probed
|
|
117
|
+
// ephemeral port there; `strictPort: false` handles the small
|
|
118
|
+
// probe→bind race by moving to the next ephemeral port.
|
|
119
|
+
const server =
|
|
120
|
+
serverOptions.port === 0 && !viteSupportsPortZero(vite.version)
|
|
121
|
+
? {
|
|
122
|
+
...serverOptions,
|
|
123
|
+
port: yield* findAvailablePort(
|
|
124
|
+
typeof serverOptions.host === "string"
|
|
125
|
+
? serverOptions.host
|
|
126
|
+
: undefined,
|
|
127
|
+
).pipe(Effect.orDie),
|
|
128
|
+
}
|
|
129
|
+
: serverOptions;
|
|
130
|
+
return yield* Effect.acquireRelease(
|
|
131
|
+
ConsoleService.consoleWith((console) =>
|
|
132
|
+
Effect.promise(async () => {
|
|
133
|
+
const devServer = await vite.createServer({
|
|
134
|
+
root: rootDir,
|
|
135
|
+
define: getDefine(env),
|
|
136
|
+
plugins: [cloudflare(pluginOptions)],
|
|
137
|
+
server,
|
|
138
|
+
customLogger: makeViteLogger(console),
|
|
139
|
+
});
|
|
140
|
+
await devServer.listen();
|
|
141
|
+
return devServer;
|
|
142
|
+
}),
|
|
143
|
+
),
|
|
144
|
+
(devServer) =>
|
|
145
|
+
Effect.promise(async () => {
|
|
146
|
+
await devServer.close();
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
});
|
|
128
150
|
|
|
129
151
|
/**
|
|
130
152
|
* Run a production Vite build in a child process rooted at the project
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
QueueConsumer,
|
|
5
5
|
RuntimeWorker,
|
|
6
6
|
Workflow,
|
|
7
|
-
} from "@
|
|
7
|
+
} from "@alchemy.run/cloudflare-runtime/core";
|
|
8
8
|
import type { BundleOutput } from "../../Bundle/Bundle.ts";
|
|
9
9
|
import type { WorkerBinding } from "./WorkerBinding.ts";
|
|
10
10
|
import type { WorkerAssetsConfig, WorkerSourceDescriptor } from "./Worker.ts";
|
|
@@ -154,6 +154,12 @@ export const runViteBuildChild = (
|
|
|
154
154
|
stdout: "pipe",
|
|
155
155
|
stderr: "pipe",
|
|
156
156
|
extendEnv: true,
|
|
157
|
+
// A production build must not inherit the parent's NODE_ENV: a
|
|
158
|
+
// deploy driven from a test process (`bun test` sets
|
|
159
|
+
// NODE_ENV=test) would otherwise bake `import.meta.env.DEV`
|
|
160
|
+
// into the server bundle (e.g. SolidStart then ships its
|
|
161
|
+
// dev-only manifest and every SSR request 500s).
|
|
162
|
+
env: { NODE_ENV: "production" },
|
|
157
163
|
killSignal: "SIGKILL",
|
|
158
164
|
},
|
|
159
165
|
),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { layerRuntime } from "@
|
|
1
|
+
import { layerRuntime } from "@alchemy.run/cloudflare-runtime/core";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Layer from "effect/Layer";
|
|
4
4
|
import * as Stdio from "effect/Stdio";
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import { CloudflareAuth } from "../Auth/AuthProvider.ts";
|
|
14
14
|
import * as Credentials from "../Credentials.ts";
|
|
15
15
|
import * as RpcServerEnvironment from "../../Local/RpcServerEnvironment.ts";
|
|
16
|
-
import { findAvailablePort } from "../../Util/Node.ts";
|
|
17
16
|
import { PlatformServices, runMain } from "../../Util/PlatformServices.ts";
|
|
18
17
|
import { materializeRuntimeBindings } from "./RuntimeBindings.ts";
|
|
19
18
|
import { loadSource, SourceProviderError } from "./Source.ts";
|
|
@@ -71,14 +70,10 @@ const program = Effect.scoped(
|
|
|
71
70
|
);
|
|
72
71
|
const source = config.source;
|
|
73
72
|
const viteHost = "127.0.0.1";
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
// Vite release is available.
|
|
79
|
-
// `strictPort: false` still handles the small race between releasing the
|
|
80
|
-
// probe socket and Vite binding the selected port.
|
|
81
|
-
const vitePort = source ? undefined : yield* findAvailablePort(viteHost);
|
|
73
|
+
// `viteDev` resolves `port: 0` per the loaded Vite: a true OS-assigned
|
|
74
|
+
// random port on Vite >= 8.2.1 (vitejs/vite#23158), a probed ephemeral
|
|
75
|
+
// port on older Vite.
|
|
76
|
+
const vitePort = source ? undefined : 0;
|
|
82
77
|
const url = source
|
|
83
78
|
? yield* loadSource(source.descriptor).pipe(
|
|
84
79
|
// `loadSource` is typed against the full `SourceServices` union
|
|
@@ -39,14 +39,14 @@ import type { Reference as ZoneReference } from "../Zone/lookup.ts";
|
|
|
39
39
|
import { type Assets, type AssetsProps } from "./Assets.ts";
|
|
40
40
|
import { type DurableObjectExport } from "./DurableObject.ts";
|
|
41
41
|
import { Request } from "./Request.ts";
|
|
42
|
+
import type { ModuleRule } from "./Sources/Prebuilt.ts";
|
|
43
|
+
import type { WorkerBuildOptions } from "./Sources/Rolldown.ts";
|
|
42
44
|
import { bindWorkerAsyncBindings } from "./WorkerAsyncBindings.ts";
|
|
43
45
|
import type {
|
|
44
46
|
WorkerBinding,
|
|
45
47
|
WorkerBindingResource,
|
|
46
48
|
WorkerBindings,
|
|
47
49
|
} from "./WorkerBinding.ts";
|
|
48
|
-
import type { ModuleRule } from "./Sources/Prebuilt.ts";
|
|
49
|
-
import type { WorkerBuildOptions } from "./Sources/Rolldown.ts";
|
|
50
50
|
import {
|
|
51
51
|
makeWorkerRuntimeContext,
|
|
52
52
|
type WorkerRuntimeContext,
|
|
@@ -565,7 +565,10 @@ export interface WorkerVersionOptions {
|
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
export interface WorkerProps<
|
|
568
|
-
|
|
568
|
+
// PERF: unconstrained for the same reason as `Worker<Bindings>` above —
|
|
569
|
+
// the `extends WorkerBindingProps` proof is expensive for generic mapped
|
|
570
|
+
// types and the call-site overloads already constrain user input.
|
|
571
|
+
Bindings = any,
|
|
569
572
|
Assets extends WorkerAssetsConfig | undefined =
|
|
570
573
|
| WorkerAssetsConfig
|
|
571
574
|
| undefined,
|
|
@@ -628,6 +631,13 @@ export interface WorkerProps<
|
|
|
628
631
|
* that hash authoritative instead — the directory is not read during
|
|
629
632
|
* planning at all.
|
|
630
633
|
*
|
|
634
|
+
* Requests are served assets-first by default: a request matching a file
|
|
635
|
+
* never invokes the Worker. `runWorkerFirst` inverts that — `true` routes
|
|
636
|
+
* every request through the Worker ahead of the asset layer (serve files
|
|
637
|
+
* yourself via the `ASSETS` binding), and a glob array (e.g. `["/api/*"]`)
|
|
638
|
+
* routes only matching paths worker-first. The same routing applies under
|
|
639
|
+
* `alchemy dev`.
|
|
640
|
+
*
|
|
631
641
|
* When neither {@link main} nor {@link script} is provided, the Worker is
|
|
632
642
|
* deployed **assets-only**: no script is uploaded at all and Cloudflare's
|
|
633
643
|
* asset layer serves every request, applying `htmlHandling` /
|
|
@@ -1026,7 +1036,14 @@ export interface ViteOptions {
|
|
|
1026
1036
|
};
|
|
1027
1037
|
}
|
|
1028
1038
|
|
|
1029
|
-
|
|
1039
|
+
// PERF: deliberately NOT `Bindings extends WorkerBindings`. The constraint
|
|
1040
|
+
// forced the checker to prove the generic `NormalizedBindings<...>` mapped
|
|
1041
|
+
// type assignable to the ~30-member `WorkerBindingResource` union at every
|
|
1042
|
+
// `Worker<...>` instantiation — a single 28s structural relation that was 45%
|
|
1043
|
+
// of the whole program's check time. Input is already constrained at the
|
|
1044
|
+
// call boundary (`Bindings extends WorkerBindingProps`), so this type
|
|
1045
|
+
// argument is only ever produced from validated shapes.
|
|
1046
|
+
export type Worker<Bindings = any> = Resource<
|
|
1030
1047
|
WorkerTypeId,
|
|
1031
1048
|
WorkerProps<Bindings>,
|
|
1032
1049
|
{
|
|
@@ -2126,14 +2143,17 @@ export const Worker: ResourceClassLike<Worker> &
|
|
|
2126
2143
|
| Container.Application<any>
|
|
2127
2144
|
| PlatformServices
|
|
2128
2145
|
| Tag,
|
|
2146
|
+
PropsReq = never,
|
|
2129
2147
|
>(
|
|
2130
2148
|
id: Id,
|
|
2131
|
-
props:
|
|
2149
|
+
props:
|
|
2150
|
+
| InputProps<WorkerProps>
|
|
2151
|
+
| Effect.Effect<InputProps<WorkerProps>, ConfigError, PropsReq>,
|
|
2132
2152
|
impl: Effect.Effect<Shape, ConfigError, Req>,
|
|
2133
2153
|
): Effect.Effect<
|
|
2134
2154
|
Worker & Rpc<Self>,
|
|
2135
2155
|
never,
|
|
2136
|
-
Extract<Req, Container.Application<any>> | Providers
|
|
2156
|
+
Extract<Req, Container.Application<any>> | Providers | PropsReq
|
|
2137
2157
|
> &
|
|
2138
2158
|
Named<Id> & {
|
|
2139
2159
|
new (): MakeShape<Shape, WorkerShape> & Named<Id> & Tag;
|
|
@@ -29,6 +29,7 @@ import { initialCwd } from "../../Util/Node.ts";
|
|
|
29
29
|
import { sha256Object } from "../../Util/sha256.ts";
|
|
30
30
|
import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
|
|
31
31
|
import { localRuntimeServices } from "../LocalRuntime.ts";
|
|
32
|
+
import { detachQueueConsumersOfScript } from "../Queues/Consumer.ts";
|
|
32
33
|
import { CloudflareLogs } from "../Logs.ts";
|
|
33
34
|
import { resolveZoneId } from "../Zone/lookup.ts";
|
|
34
35
|
import {
|
|
@@ -762,7 +763,23 @@ const deleteWorkerScript = (
|
|
|
762
763
|
force: true,
|
|
763
764
|
});
|
|
764
765
|
}
|
|
765
|
-
return yield* workers
|
|
766
|
+
return yield* workers
|
|
767
|
+
.deleteScript({ accountId, scriptName, force: true })
|
|
768
|
+
.pipe(
|
|
769
|
+
// The script is still registered as a queue consumer (even with
|
|
770
|
+
// `force`). Normally the sibling Consumer resource detaches first,
|
|
771
|
+
// but state loss (e.g. a consumer row rewritten by a pre-stamping
|
|
772
|
+
// dev run) can strand a live consumer pointing at this script with
|
|
773
|
+
// nothing left to delete it. The script is going away, so any
|
|
774
|
+
// consumer wiring pointing at it is dead — detach and retry.
|
|
775
|
+
Effect.catchTag("QueueConsumerConflict", () =>
|
|
776
|
+
detachQueueConsumersOfScript(accountId, scriptName).pipe(
|
|
777
|
+
Effect.andThen(
|
|
778
|
+
workers.deleteScript({ accountId, scriptName, force: true }),
|
|
779
|
+
),
|
|
780
|
+
),
|
|
781
|
+
),
|
|
782
|
+
);
|
|
766
783
|
});
|
|
767
784
|
|
|
768
785
|
/**
|
|
@@ -2224,7 +2241,7 @@ export const LiveWorkerProvider = () =>
|
|
|
2224
2241
|
});
|
|
2225
2242
|
|
|
2226
2243
|
// Loaded lazily: `./Sources/Vite.ts` pulls in
|
|
2227
|
-
// `@
|
|
2244
|
+
// `@alchemy.run/cloudflare-runtime/vite` (~0.5s), which is only
|
|
2228
2245
|
// needed for vite-based workers at build time — not for every Worker
|
|
2229
2246
|
// definition at module-load time.
|
|
2230
2247
|
const loadVite = Effect.promise(() => import("./Sources/Vite.ts"));
|
package/src/Docker/Docker.ts
CHANGED
|
@@ -582,17 +582,23 @@ export const DockerLive = Layer.effect(
|
|
|
582
582
|
),
|
|
583
583
|
container: {
|
|
584
584
|
create: ({ image, env, command, context, ...options }) =>
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
...
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
585
|
+
// `--env KEY` (name-only) keeps values off CLI arguments; Docker
|
|
586
|
+
// copies each value from the CLI process environment, so the
|
|
587
|
+
// normalized container env must be passed to the child process.
|
|
588
|
+
run(
|
|
589
|
+
[
|
|
590
|
+
...formatArgs({ context }),
|
|
591
|
+
"container",
|
|
592
|
+
"create",
|
|
593
|
+
...formatArgs({
|
|
594
|
+
...options,
|
|
595
|
+
env: env ? Object.keys(env) : undefined,
|
|
596
|
+
}),
|
|
597
|
+
image,
|
|
598
|
+
...(command ?? []),
|
|
599
|
+
],
|
|
600
|
+
env,
|
|
601
|
+
),
|
|
596
602
|
inspect: (name, context) =>
|
|
597
603
|
runInspect<Docker.Container>([
|
|
598
604
|
...formatArgs({ context }),
|
package/src/Drizzle/MySQL.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
// `@effect/sql-mysql2` (and mysql2 underneath) are optional peers — value
|
|
2
|
+
// imports are deferred to first use so `alchemy/Drizzle` resolves without
|
|
3
|
+
// them installed.
|
|
4
|
+
import type * as MysqlClient from "@effect/sql-mysql2/MysqlClient";
|
|
2
5
|
import type { AnyRelations, EmptyRelations } from "drizzle-orm";
|
|
3
6
|
import type { EffectMysql2Database } from "drizzle-orm/effect-mysql2";
|
|
4
|
-
import * as MySqlDrizzle from "drizzle-orm/effect-mysql2";
|
|
5
7
|
import type { EffectDrizzleMySqlConfig } from "drizzle-orm/mysql-core/effect/utils";
|
|
6
8
|
import * as Effect from "effect/Effect";
|
|
7
9
|
import * as Layer from "effect/Layer";
|
|
8
10
|
import type * as Redacted from "effect/Redacted";
|
|
9
11
|
import { makeExecutionMemo } from "../Runtime/ExecutionMemo.ts";
|
|
10
|
-
import {
|
|
12
|
+
import type { MySQLConfig } from "../SQL/MySQL.ts";
|
|
11
13
|
import { proxyChain } from "../Util/proxy-chain.ts";
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -55,6 +57,14 @@ export const MySQL = <
|
|
|
55
57
|
Effect.map(
|
|
56
58
|
makeExecutionMemo(
|
|
57
59
|
Effect.gen(function* () {
|
|
60
|
+
const [MysqlClient, MySqlDrizzle, { resolveMySQLConfig }] =
|
|
61
|
+
yield* Effect.promise(() =>
|
|
62
|
+
Promise.all([
|
|
63
|
+
import("@effect/sql-mysql2/MysqlClient"),
|
|
64
|
+
import("drizzle-orm/effect-mysql2"),
|
|
65
|
+
import("../SQL/MySQL.ts"),
|
|
66
|
+
]),
|
|
67
|
+
);
|
|
58
68
|
const { client, ...drizzleConfig } = config ?? {};
|
|
59
69
|
const mysqlCtx = yield* Layer.build(
|
|
60
70
|
MysqlClient.layer(
|
package/src/Drizzle/Postgres.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// `@effect/sql-pg` (and pg underneath) are optional peers — value imports
|
|
2
|
+
// are deferred to first use so `alchemy/Drizzle` resolves without them
|
|
3
|
+
// installed.
|
|
4
|
+
import type * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
5
|
import type { AnyRelations, EmptyRelations } from "drizzle-orm";
|
|
3
6
|
import type { EffectPgDatabase } from "drizzle-orm/effect-postgres";
|
|
4
|
-
import * as PgDrizzle from "drizzle-orm/effect-postgres";
|
|
5
7
|
import type { EffectDrizzlePgConfig } from "drizzle-orm/pg-core/effect/utils";
|
|
6
8
|
import * as Effect from "effect/Effect";
|
|
7
9
|
import * as Layer from "effect/Layer";
|
|
@@ -56,6 +58,12 @@ export const Postgres = <
|
|
|
56
58
|
Effect.map(
|
|
57
59
|
makeExecutionMemo(
|
|
58
60
|
Effect.gen(function* () {
|
|
61
|
+
const [PgClient, PgDrizzle] = yield* Effect.promise(() =>
|
|
62
|
+
Promise.all([
|
|
63
|
+
import("@effect/sql-pg/PgClient"),
|
|
64
|
+
import("drizzle-orm/effect-postgres"),
|
|
65
|
+
]),
|
|
66
|
+
);
|
|
59
67
|
const pgCtx = yield* Layer.build(
|
|
60
68
|
PgClient.layer({ url: yield* connectionString }),
|
|
61
69
|
);
|
package/src/Drizzle/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from "./D1.ts";
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
// The tagged errors drizzle's effect drivers fail with — re-exported so
|
|
4
4
|
// consumers can reference them without reaching into drizzle-orm
|
|
5
5
|
// internals. (`Effect.catchTag` needs only the tag string; these are for
|
|
@@ -10,7 +10,10 @@ export {
|
|
|
10
10
|
EffectTransactionRollbackError,
|
|
11
11
|
MigratorInitError,
|
|
12
12
|
} from "drizzle-orm/effect-core";
|
|
13
|
-
export * from "./MySQL.ts";
|
|
14
|
-
export * from "./Postgres.ts";
|
|
15
13
|
export * from "./Providers.ts";
|
|
16
14
|
export * from "./Schema.ts";
|
|
15
|
+
// MySQL and Postgres are deliberately NOT re-exported here: their drivers
|
|
16
|
+
// (`@effect/sql-mysql2`/`mysql2`, `@effect/sql-pg`/`pg`) are optional peer
|
|
17
|
+
// dependencies, and keeping them out of the barrel keeps them out of every
|
|
18
|
+
// non-user's module graph. Import them via their subpaths instead:
|
|
19
|
+
// `alchemy/Drizzle/MySQL`, `alchemy/Drizzle/Postgres`.
|
package/src/Neon/Migrations.ts
CHANGED
|
@@ -2,18 +2,9 @@ import * as Data from "effect/Data";
|
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Redacted from "effect/Redacted";
|
|
4
4
|
import type { Client } from "pg";
|
|
5
|
+
import { importPg } from "../SQL/PostgresDriver.ts";
|
|
5
6
|
import type { SqlFile } from "../SQL/SqlFile.ts";
|
|
6
7
|
|
|
7
|
-
// `pg` is an optional peer dependency — loaded lazily so importing the Neon
|
|
8
|
-
// provider never requires the driver unless migrations actually run.
|
|
9
|
-
const importPg = () =>
|
|
10
|
-
import("pg").catch((cause) => {
|
|
11
|
-
throw new Error(
|
|
12
|
-
"Failed to load the 'pg' driver. Install the optional peer dependency 'pg' to run Neon migrations.",
|
|
13
|
-
{ cause },
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
8
|
export class PgError extends Data.TaggedError("PgError")<{
|
|
18
9
|
message: string;
|
|
19
10
|
cause?: unknown;
|
package/src/PhysicalName.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const createPhysicalName = Effect.fn(function* ({
|
|
|
19
19
|
maxLength = 64,
|
|
20
20
|
delimiter = "-",
|
|
21
21
|
lowercase = false,
|
|
22
|
+
forbiddenPrefixes = [],
|
|
22
23
|
}: {
|
|
23
24
|
id: string;
|
|
24
25
|
/**
|
|
@@ -46,6 +47,14 @@ export const createPhysicalName = Effect.fn(function* ({
|
|
|
46
47
|
delimiter?: string;
|
|
47
48
|
/** Whether to lowercase the physical name. @default false */
|
|
48
49
|
lowercase?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Service-reserved name prefixes (matched case-insensitively). When the
|
|
52
|
+
* generated prefix would start with one of these — e.g. a stack named
|
|
53
|
+
* `aws-*` colliding with S3 Tables' reserved `aws` bucket prefix — a safe
|
|
54
|
+
* `x${delimiter}` is prepended so the name stays valid. Names that don't
|
|
55
|
+
* collide are unaffected, so existing deployments never rename.
|
|
56
|
+
*/
|
|
57
|
+
forbiddenPrefixes?: string[];
|
|
49
58
|
}) {
|
|
50
59
|
// Always generate DNS-compatible names (letters, numbers, and hyphens only).
|
|
51
60
|
// This ensures physical names work across all services including S3 buckets.
|
|
@@ -56,8 +65,15 @@ export const createPhysicalName = Effect.fn(function* ({
|
|
|
56
65
|
);
|
|
57
66
|
const stack = yield* Stack;
|
|
58
67
|
const stage = yield* Stage;
|
|
59
|
-
|
|
68
|
+
let prefix =
|
|
60
69
|
_prefix ?? `${stack.name}${delimiter}${id}${delimiter}${stage}${delimiter}`;
|
|
70
|
+
if (
|
|
71
|
+
forbiddenPrefixes.some((forbidden) =>
|
|
72
|
+
prefix.toLowerCase().startsWith(forbidden.toLowerCase()),
|
|
73
|
+
)
|
|
74
|
+
) {
|
|
75
|
+
prefix = `x${delimiter}${prefix}`;
|
|
76
|
+
}
|
|
61
77
|
const randomId = base32(
|
|
62
78
|
Buffer.from(instanceId ?? (yield* InstanceId), "hex"),
|
|
63
79
|
);
|
package/src/Plan.ts
CHANGED
|
@@ -40,7 +40,11 @@ import {
|
|
|
40
40
|
tryFindProviderByType,
|
|
41
41
|
type ProviderService,
|
|
42
42
|
} from "./Provider.ts";
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
defaultProviderMode,
|
|
45
|
+
stampedMode,
|
|
46
|
+
type ProviderMode,
|
|
47
|
+
} from "./ProviderMode.ts";
|
|
44
48
|
import {
|
|
45
49
|
isResource,
|
|
46
50
|
type ResourceBinding,
|
|
@@ -343,17 +347,20 @@ export const make = <A>(
|
|
|
343
347
|
|
|
344
348
|
/**
|
|
345
349
|
* Has this resource switched provider modes since it was last
|
|
346
|
-
* reconciled? Rows without a persisted mode
|
|
347
|
-
*
|
|
348
|
-
*
|
|
350
|
+
* reconciled? Rows without a persisted mode were written by a
|
|
351
|
+
* pre-provider-mode engine (or a provider that only became dual-mode
|
|
352
|
+
* later) — their physical resource is LIVE unless its attrs carry a
|
|
353
|
+
* `dev:` identity marker proving it was reconciled locally (see
|
|
354
|
+
* {@link stampedMode}). A dev run replaces unstamped live rows exactly
|
|
355
|
+
* like stamped live rows; live runs replace unstamped marker rows.
|
|
349
356
|
*/
|
|
350
357
|
const hasModeSwitched = (
|
|
351
358
|
mode: ProviderMode | undefined,
|
|
352
359
|
oldState: ResourceState | undefined,
|
|
353
360
|
): boolean =>
|
|
354
361
|
mode !== undefined &&
|
|
355
|
-
oldState
|
|
356
|
-
oldState
|
|
362
|
+
oldState !== undefined &&
|
|
363
|
+
stampedMode(oldState) !== mode;
|
|
357
364
|
|
|
358
365
|
const resourceFqns = yield* state.list({
|
|
359
366
|
stack: stackName,
|
|
@@ -1781,12 +1788,16 @@ export const make = <A>(
|
|
|
1781
1788
|
// and the remediation instead of limping into a partial apply.
|
|
1782
1789
|
//
|
|
1783
1790
|
// Orphan deletes resolve the provider variant for the mode
|
|
1784
|
-
// that created the row (`providerMode
|
|
1791
|
+
// that created the row (`providerMode`, or the `dev:` marker
|
|
1792
|
+
// inference for legacy unstamped rows), so e.g. a local dev
|
|
1785
1793
|
// worker's row is deleted by the local provider even during a
|
|
1786
|
-
// live deploy — and vice versa.
|
|
1794
|
+
// live deploy — and vice versa. Unstamped rows are physically
|
|
1795
|
+
// live unless their attrs carry the marker (see stampedMode),
|
|
1796
|
+
// never the run default.
|
|
1797
|
+
const rowMode = stampedMode(oldState);
|
|
1787
1798
|
const providerOption = yield* tryFindProviderByType(
|
|
1788
1799
|
resourceType,
|
|
1789
|
-
|
|
1800
|
+
rowMode,
|
|
1790
1801
|
);
|
|
1791
1802
|
if (Option.isNone(providerOption)) {
|
|
1792
1803
|
return yield* Effect.die(
|
package/src/ProviderMode.ts
CHANGED
|
@@ -59,6 +59,62 @@ export class ConflictingProviderModeError extends Data.TaggedError(
|
|
|
59
59
|
conflictingMode: ProviderMode | undefined;
|
|
60
60
|
}> {}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Prefix marking a locally-emulated resource's physical identity. Local
|
|
64
|
+
* providers fabricate ids/names with this prefix (`dev:<uuid>` queue and
|
|
65
|
+
* namespace ids, `dev:`-prefixed bucket names, ...) so a resource's
|
|
66
|
+
* persisted attributes reveal which runtime hosts it even without the
|
|
67
|
+
* `providerMode` stamp.
|
|
68
|
+
*/
|
|
69
|
+
export const LOCAL_ID_PREFIX = "dev:";
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Does a persisted attributes value carry a local identity marker — any
|
|
73
|
+
* string value (at any depth) with the {@link LOCAL_ID_PREFIX}? Persisted
|
|
74
|
+
* attrs are plain JSON (state commits strip unresolved values), so a
|
|
75
|
+
* structural scan is safe.
|
|
76
|
+
*/
|
|
77
|
+
export const hasLocalIdentity = (value: unknown): boolean => {
|
|
78
|
+
if (typeof value === "string") return value.startsWith(LOCAL_ID_PREFIX);
|
|
79
|
+
if (Array.isArray(value)) return value.some(hasLocalIdentity);
|
|
80
|
+
if (typeof value === "object" && value !== null) {
|
|
81
|
+
return Object.values(value).some(hasLocalIdentity);
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The mode a persisted state row was actually reconciled with: its stamped
|
|
88
|
+
* `providerMode`, or — for legacy rows written before stamping existed —
|
|
89
|
+
* `"local"` when the persisted attributes carry a {@link LOCAL_ID_PREFIX}
|
|
90
|
+
* identity marker, `"live"` otherwise.
|
|
91
|
+
*
|
|
92
|
+
* An unstamped row without a marker predates provider modes or was written
|
|
93
|
+
* by a mode-agnostic provider — in both cases the write acted on the REAL
|
|
94
|
+
* cloud, so the row's physical resource is `"live"`. Assuming the current
|
|
95
|
+
* run's mode instead silently adopts a deployed cloud resource as a local
|
|
96
|
+
* instance during `alchemy dev`: the row noops (or restarts locally over
|
|
97
|
+
* the live attrs), gets re-stamped `"local"`, and the live resource becomes
|
|
98
|
+
* untracked — it is never deleted by a later destroy or replacement, and
|
|
99
|
+
* its deployed URL keeps serving.
|
|
100
|
+
*
|
|
101
|
+
* An unstamped row WITH a marker (e.g. a queue with a `dev:<uuid>` id
|
|
102
|
+
* written by `alchemy dev` on a pre-stamping version) is the mirror case:
|
|
103
|
+
* assuming `"live"` hands the `dev:` identity to the live provider, which
|
|
104
|
+
* sends it to the real cloud API (Cloudflare rejects it as a malformed
|
|
105
|
+
* parameter, permanently wedging the destroy). The marker proves the row
|
|
106
|
+
* was reconciled locally, so it is handled as `"local"`.
|
|
107
|
+
*
|
|
108
|
+
* For mode-agnostic providers the returned mode is harmless either way:
|
|
109
|
+
* `providerForMode` ignores the mode when the provider has a single
|
|
110
|
+
* implementation.
|
|
111
|
+
*/
|
|
112
|
+
export const stampedMode = (row: {
|
|
113
|
+
readonly providerMode?: ProviderMode | undefined;
|
|
114
|
+
readonly attr?: unknown;
|
|
115
|
+
}): ProviderMode =>
|
|
116
|
+
row.providerMode ?? (hasLocalIdentity(row.attr) ? "local" : "live");
|
|
117
|
+
|
|
62
118
|
/**
|
|
63
119
|
* Run the wrapped resources **remotely (against the real cloud) even during
|
|
64
120
|
* `alchemy dev`** — the opt-out from local emulation. During `alchemy deploy`
|