alchemy 0.65.1 → 0.66.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 +132 -78
- 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 +14 -3
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +17 -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/bucket.js +1 -1
- package/lib/cloudflare/bucket.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/kv-namespace.js +1 -1
- package/lib/cloudflare/kv-namespace.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.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +5 -4
- package/lib/cloudflare/miniflare/miniflare-controller.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/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/redwood/plugin.js +2 -2
- package/lib/cloudflare/redwood/plugin.js.map +1 -1
- 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.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +4 -2
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.js +1 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/scope.d.ts +16 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +10 -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/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 +36 -8
- package/src/apply.ts +58 -3
- package/src/cloudflare/bucket.ts +1 -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/kv-namespace.ts +1 -1
- package/src/cloudflare/miniflare/delete.ts +19 -11
- package/src/cloudflare/miniflare/miniflare-controller.ts +10 -6
- package/src/cloudflare/miniflare/paths.ts +11 -7
- package/src/cloudflare/miniflare/symlink-miniflare-state.ts +19 -0
- package/src/cloudflare/nuxt/plugin.ts +12 -3
- package/src/cloudflare/redwood/plugin.ts +2 -2
- package/src/cloudflare/vite/plugin.ts +8 -3
- package/src/cloudflare/website.ts +5 -19
- package/src/cloudflare/worker.ts +4 -2
- package/src/cloudflare/wrangler.json.ts +1 -0
- package/src/scope.ts +26 -5
- 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/root-dir.ts +1 -0
- package/templates/nextjs/alchemy.run.ts +2 -2
- package/workers/cloudflare-state-store.js +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GetPlatformProxyOptions } from "wrangler";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
getDefaultConfigPath,
|
|
4
|
+
getDefaultPersistPath,
|
|
5
5
|
validateConfigPath,
|
|
6
6
|
validatePersistPath,
|
|
7
7
|
} from "./miniflare/paths.ts";
|
|
@@ -26,7 +26,7 @@ export const getPlatformProxyOptions = (
|
|
|
26
26
|
typeof input.persist === "object" &&
|
|
27
27
|
typeof input.persist.path === "string"
|
|
28
28
|
? input.persist.path
|
|
29
|
-
:
|
|
29
|
+
: getDefaultPersistPath(),
|
|
30
30
|
),
|
|
31
31
|
};
|
|
32
32
|
if (!persist) {
|
|
@@ -39,12 +39,12 @@ export const getPlatformProxyOptions = (
|
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
41
41
|
...input,
|
|
42
|
-
configPath: validateConfigPath(input.configPath ??
|
|
42
|
+
configPath: validateConfigPath(input.configPath ?? getDefaultConfigPath()),
|
|
43
43
|
persist,
|
|
44
44
|
experimental: (input as any).experimental ?? {
|
|
45
45
|
remoteBindings: true,
|
|
46
46
|
},
|
|
47
|
-
}
|
|
47
|
+
};
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const warned = new Set<string>();
|
|
@@ -283,6 +283,7 @@ const _D1Database = Resource(
|
|
|
283
283
|
databaseId: dev.id,
|
|
284
284
|
migrationsTable: props.migrationsTable ?? DEFAULT_MIGRATIONS_TABLE,
|
|
285
285
|
migrations: props.migrationsFiles,
|
|
286
|
+
rootDir: this.scope.rootDir,
|
|
286
287
|
});
|
|
287
288
|
}
|
|
288
289
|
return this({
|
|
@@ -301,7 +302,7 @@ const _D1Database = Resource(
|
|
|
301
302
|
|
|
302
303
|
if (this.phase === "delete") {
|
|
303
304
|
if (this.output.dev?.id) {
|
|
304
|
-
await deleteMiniflareBinding("d1", this.output.dev.id);
|
|
305
|
+
await deleteMiniflareBinding(this.scope, "d1", this.output.dev.id);
|
|
305
306
|
}
|
|
306
307
|
if (props.delete !== false && this.output?.id) {
|
|
307
308
|
await deleteDatabase(api, this.output.id);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as mf from "miniflare";
|
|
2
|
-
import
|
|
2
|
+
import { getDefaultPersistPath } from "./miniflare/paths.ts";
|
|
3
3
|
|
|
4
4
|
export interface D1LocalMigrationOptions {
|
|
5
|
+
rootDir: string;
|
|
5
6
|
databaseId: string;
|
|
6
7
|
migrationsTable: string;
|
|
7
8
|
migrations: { id: string; sql: string }[];
|
|
@@ -13,15 +14,16 @@ export const applyLocalD1Migrations = async (
|
|
|
13
14
|
const miniflare = new mf.Miniflare({
|
|
14
15
|
script: "",
|
|
15
16
|
modules: true,
|
|
16
|
-
defaultPersistRoot:
|
|
17
|
+
defaultPersistRoot: getDefaultPersistPath(options.rootDir),
|
|
17
18
|
d1Persist: true,
|
|
18
19
|
d1Databases: { DB: options.databaseId },
|
|
19
20
|
log: process.env.DEBUG ? new mf.Log(mf.LogLevel.DEBUG) : undefined,
|
|
20
21
|
});
|
|
21
22
|
try {
|
|
22
23
|
await miniflare.ready;
|
|
23
|
-
|
|
24
|
-
const
|
|
24
|
+
// TODO(sam): don't use `any` once prisma is fixed upstream
|
|
25
|
+
const db: any = await miniflare.getD1Database("DB");
|
|
26
|
+
const session: any = db.withSession("first-primary");
|
|
25
27
|
await session
|
|
26
28
|
.prepare(
|
|
27
29
|
`CREATE TABLE IF NOT EXISTS ${options.migrationsTable} (
|
|
@@ -31,11 +33,13 @@ export const applyLocalD1Migrations = async (
|
|
|
31
33
|
)`,
|
|
32
34
|
)
|
|
33
35
|
.run();
|
|
34
|
-
const appliedMigrations
|
|
36
|
+
const appliedMigrations: {
|
|
37
|
+
results: { name: string }[];
|
|
38
|
+
} = await session
|
|
35
39
|
.prepare(
|
|
36
40
|
`SELECT name FROM ${options.migrationsTable} ORDER BY applied_at ASC`,
|
|
37
41
|
)
|
|
38
|
-
.all
|
|
42
|
+
.all();
|
|
39
43
|
const insertRecord = session.prepare(
|
|
40
44
|
`INSERT INTO ${options.migrationsTable} (name) VALUES (?)`,
|
|
41
45
|
);
|
|
@@ -240,7 +240,7 @@ const _KVNamespace = Resource(
|
|
|
240
240
|
|
|
241
241
|
if (this.phase === "delete") {
|
|
242
242
|
if (this.output.dev?.id) {
|
|
243
|
-
await deleteMiniflareBinding("kv", this.output.dev.id);
|
|
243
|
+
await deleteMiniflareBinding(this.scope, "kv", this.output.dev.id);
|
|
244
244
|
}
|
|
245
245
|
if (this.output.namespaceId && props.delete !== false) {
|
|
246
246
|
await deleteKVNamespace(api, this.output.namespaceId);
|
|
@@ -2,11 +2,11 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import type { RmDirOptions } from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "pathe";
|
|
5
|
+
import type { Scope } from "../../scope.ts";
|
|
5
6
|
import type { DurableObjectNamespace } from "../durable-object-namespace.ts";
|
|
6
7
|
import type { Workflow } from "../workflow.ts";
|
|
7
|
-
import {
|
|
8
|
+
import { getDefaultPersistPath } from "./paths.ts";
|
|
8
9
|
|
|
9
|
-
const PERSIST_ROOT = path.resolve(DEFAULT_PERSIST_PATH);
|
|
10
10
|
const BINDING_TYPE_KEY = {
|
|
11
11
|
d1: "miniflare-D1DatabaseObject",
|
|
12
12
|
kv: "miniflare-KVNamespaceObject",
|
|
@@ -18,8 +18,14 @@ type BindingType = keyof typeof BINDING_TYPE_KEY;
|
|
|
18
18
|
/**
|
|
19
19
|
* Delete a binding from the miniflare directory.
|
|
20
20
|
*/
|
|
21
|
-
export async function deleteMiniflareBinding(
|
|
22
|
-
|
|
21
|
+
export async function deleteMiniflareBinding(
|
|
22
|
+
scope: Scope,
|
|
23
|
+
type: BindingType,
|
|
24
|
+
id: string,
|
|
25
|
+
) {
|
|
26
|
+
const persistRoot = path.resolve(getDefaultPersistPath(scope.rootDir));
|
|
27
|
+
|
|
28
|
+
const bindingPath = path.join(persistRoot, type, BINDING_TYPE_KEY[type]);
|
|
23
29
|
|
|
24
30
|
// delete durable object/sqlite files
|
|
25
31
|
const namespaceId = durableObjectNamespaceIdFromName(
|
|
@@ -33,29 +39,31 @@ export async function deleteMiniflareBinding(type: BindingType, id: string) {
|
|
|
33
39
|
await Promise.all(files.map((file) => fs.rm(path.join(bindingPath, file))));
|
|
34
40
|
|
|
35
41
|
// delete data directory, if present
|
|
36
|
-
await removeDirectory(path.join(
|
|
42
|
+
await removeDirectory(path.join(persistRoot, type, id), {
|
|
37
43
|
recursive: true,
|
|
38
44
|
});
|
|
39
45
|
|
|
40
46
|
// delete the miniflare directory if it is empty
|
|
41
|
-
await cleanMiniflareDirectory();
|
|
47
|
+
await cleanMiniflareDirectory(scope.dotAlchemy);
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Delete local durable object and workflow data for a given Worker script.
|
|
46
52
|
*/
|
|
47
53
|
export async function deleteMiniflareWorkerData(
|
|
54
|
+
scope: Scope,
|
|
48
55
|
scriptName: string,
|
|
49
56
|
input: {
|
|
50
57
|
durableObjects: DurableObjectNamespace[];
|
|
51
58
|
workflows: Workflow[];
|
|
52
59
|
},
|
|
53
60
|
) {
|
|
61
|
+
const persistRoot = path.resolve(getDefaultPersistPath(scope.rootDir));
|
|
54
62
|
await Promise.all(
|
|
55
63
|
input.durableObjects.map((durableObject) =>
|
|
56
64
|
removeDirectory(
|
|
57
65
|
path.join(
|
|
58
|
-
|
|
66
|
+
persistRoot,
|
|
59
67
|
"do",
|
|
60
68
|
`${scriptName}-${durableObject.className}`,
|
|
61
69
|
),
|
|
@@ -69,7 +77,7 @@ export async function deleteMiniflareWorkerData(
|
|
|
69
77
|
input.workflows.map((workflow) =>
|
|
70
78
|
removeDirectory(
|
|
71
79
|
path.join(
|
|
72
|
-
|
|
80
|
+
persistRoot,
|
|
73
81
|
"workflows",
|
|
74
82
|
`miniflare-workflows-${workflow.workflowName}`,
|
|
75
83
|
),
|
|
@@ -79,14 +87,14 @@ export async function deleteMiniflareWorkerData(
|
|
|
79
87
|
),
|
|
80
88
|
),
|
|
81
89
|
);
|
|
82
|
-
await cleanMiniflareDirectory();
|
|
90
|
+
await cleanMiniflareDirectory(persistRoot);
|
|
83
91
|
}
|
|
84
92
|
|
|
85
93
|
/**
|
|
86
94
|
* Delete the miniflare directory iff it is empty.
|
|
87
95
|
*/
|
|
88
|
-
async function cleanMiniflareDirectory() {
|
|
89
|
-
const directory = path.resolve("
|
|
96
|
+
async function cleanMiniflareDirectory(dotAlchemy: string) {
|
|
97
|
+
const directory = path.resolve(dotAlchemy, "miniflare");
|
|
90
98
|
const files = await fs
|
|
91
99
|
.readdir(directory, {
|
|
92
100
|
recursive: true,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as miniflare from "miniflare";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { Scope } from "../../scope.ts";
|
|
4
5
|
import { findOpenPort } from "../../util/find-open-port.ts";
|
|
5
6
|
import type { HTTPServer } from "../../util/http.ts";
|
|
6
7
|
import { logger } from "../../util/logger.ts";
|
|
@@ -10,7 +11,7 @@ import {
|
|
|
10
11
|
type MiniflareWorkerInput,
|
|
11
12
|
} from "./build-worker-options.ts";
|
|
12
13
|
import { MiniflareWorkerProxy } from "./miniflare-worker-proxy.ts";
|
|
13
|
-
import {
|
|
14
|
+
import { getDefaultPersistPath } from "./paths.ts";
|
|
14
15
|
|
|
15
16
|
declare global {
|
|
16
17
|
var ALCHEMY_MINIFLARE_CONTROLLER: MiniflareController | undefined;
|
|
@@ -25,8 +26,8 @@ export class MiniflareController {
|
|
|
25
26
|
mutex = new AsyncMutex();
|
|
26
27
|
|
|
27
28
|
static get singleton() {
|
|
28
|
-
globalThis.ALCHEMY_MINIFLARE_CONTROLLER ??=
|
|
29
|
-
|
|
29
|
+
return (globalThis.ALCHEMY_MINIFLARE_CONTROLLER ??=
|
|
30
|
+
new MiniflareController());
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async add(input: MiniflareWorkerInput) {
|
|
@@ -75,14 +76,17 @@ export class MiniflareController {
|
|
|
75
76
|
|
|
76
77
|
private async update() {
|
|
77
78
|
return await this.mutex.lock(async () => {
|
|
78
|
-
const options: miniflare.MiniflareOptions
|
|
79
|
+
const options: miniflare.MiniflareOptions & {
|
|
80
|
+
workers: miniflare.WorkerOptions[];
|
|
81
|
+
} = {
|
|
79
82
|
workers: [],
|
|
80
|
-
defaultPersistRoot: path.resolve(
|
|
83
|
+
defaultPersistRoot: path.resolve(
|
|
84
|
+
getDefaultPersistPath(Scope.current.rootDir),
|
|
85
|
+
),
|
|
81
86
|
unsafeDevRegistryPath: miniflare.getDefaultDevRegistryPath(),
|
|
82
87
|
log: process.env.DEBUG
|
|
83
88
|
? new miniflare.Log(miniflare.LogLevel.DEBUG)
|
|
84
89
|
: undefined,
|
|
85
|
-
|
|
86
90
|
// This is required to allow websites and other separate processes
|
|
87
91
|
// to detect Alchemy-managed Durable Objects via the Wrangler dev registry.
|
|
88
92
|
unsafeDevRegistryDurableObjectProxy: true,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { existsSync, readlinkSync, statSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { ALCHEMY_ROOT } from "../../util/root-dir.ts";
|
|
3
5
|
|
|
4
6
|
const dynamicImportContext = new AsyncLocalStorage<boolean>();
|
|
5
7
|
|
|
@@ -10,13 +12,15 @@ export const withSkipPathValidation = <T>(callback: () => T) => {
|
|
|
10
12
|
return dynamicImportContext.run(true, callback);
|
|
11
13
|
};
|
|
12
14
|
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
+
export const getDefaultConfigPath = (rootDir: string = process.cwd()) => {
|
|
16
|
+
return path.join(rootDir, ".alchemy", "local", "wrangler.jsonc");
|
|
17
|
+
};
|
|
15
18
|
|
|
16
|
-
export const
|
|
17
|
-
path
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
export const getDefaultPersistPath = (rootDir: string = ALCHEMY_ROOT) => {
|
|
20
|
+
return path.join(rootDir, ".alchemy", "miniflare", "v3");
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const validateConfigPath = (path: string, throws = true) => {
|
|
20
24
|
if (!existsSync(path)) {
|
|
21
25
|
warnOrThrow(
|
|
22
26
|
`The Wrangler config path, "${path}", could not be found. Please run \`alchemy dev\` or \`alchemy deploy\` to create it.`,
|
|
@@ -31,7 +35,7 @@ const DEFAULT_VALIDATE_PERSIST =
|
|
|
31
35
|
!process.argv.some((arg) => ["build", "prepare", "typegen"].includes(arg));
|
|
32
36
|
|
|
33
37
|
export const validatePersistPath = (
|
|
34
|
-
path
|
|
38
|
+
path: string,
|
|
35
39
|
throws = DEFAULT_VALIDATE_PERSIST,
|
|
36
40
|
) => {
|
|
37
41
|
try {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getDefaultPersistPath } from "./paths.ts";
|
|
4
|
+
|
|
5
|
+
export async function writeMiniflareSymlink(rootDir: string, cwd: string) {
|
|
6
|
+
const target = path.resolve(getDefaultPersistPath(cwd));
|
|
7
|
+
await fs.mkdir(target, { recursive: true });
|
|
8
|
+
if (cwd === process.cwd()) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const persistPath = path.resolve(cwd, getDefaultPersistPath(rootDir));
|
|
12
|
+
console.log("link", target, persistPath);
|
|
13
|
+
await fs.mkdir(path.dirname(persistPath), { recursive: true });
|
|
14
|
+
await fs.symlink(target, persistPath).catch((e) => {
|
|
15
|
+
if (e.code !== "EEXIST") {
|
|
16
|
+
throw e;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { CloudflareOptions } from "nitropack/presets/cloudflare/types";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getDefaultConfigPath,
|
|
4
|
+
getDefaultPersistPath,
|
|
5
|
+
validateConfigPath,
|
|
6
|
+
validatePersistPath,
|
|
7
|
+
} from "../miniflare/paths.ts";
|
|
3
8
|
|
|
4
9
|
const alchemy = (
|
|
5
10
|
options: Partial<CloudflareOptions> = {},
|
|
@@ -7,8 +12,12 @@ const alchemy = (
|
|
|
7
12
|
return {
|
|
8
13
|
nodeCompat: true,
|
|
9
14
|
dev: {
|
|
10
|
-
configPath: validateConfigPath(
|
|
11
|
-
|
|
15
|
+
configPath: validateConfigPath(
|
|
16
|
+
options.dev?.configPath ?? getDefaultConfigPath(),
|
|
17
|
+
),
|
|
18
|
+
persistDir: validatePersistPath(
|
|
19
|
+
options.dev?.persistDir ?? getDefaultPersistPath(),
|
|
20
|
+
),
|
|
12
21
|
...options.dev,
|
|
13
22
|
},
|
|
14
23
|
...options,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { redwood, type RedwoodPluginOptions } from "rwsdk/vite";
|
|
2
2
|
import type { PluginOption } from "vite";
|
|
3
|
-
import {
|
|
3
|
+
import { getDefaultConfigPath } from "../miniflare/paths.ts";
|
|
4
4
|
import alchemyVite from "../vite/plugin.ts";
|
|
5
5
|
|
|
6
6
|
const alchemyRedwood = (options?: RedwoodPluginOptions): PluginOption => {
|
|
7
|
-
const configPath = options?.configPath ??
|
|
7
|
+
const configPath = options?.configPath ?? getDefaultConfigPath();
|
|
8
8
|
return [
|
|
9
9
|
redwood({
|
|
10
10
|
...options,
|
|
@@ -2,7 +2,8 @@ import { cloudflare, type PluginConfig } from "@cloudflare/vite-plugin";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type { PluginOption } from "vite";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
getDefaultConfigPath,
|
|
6
|
+
getDefaultPersistPath,
|
|
6
7
|
validateConfigPath,
|
|
7
8
|
validatePersistPath,
|
|
8
9
|
} from "../miniflare/paths.ts";
|
|
@@ -12,7 +13,8 @@ const alchemy = (config?: PluginConfig): PluginOption => {
|
|
|
12
13
|
path: validatePersistPath(
|
|
13
14
|
typeof config?.persistState === "object"
|
|
14
15
|
? config.persistState.path
|
|
15
|
-
:
|
|
16
|
+
: // persist path should default to the /.alchemy/miniflare/v3
|
|
17
|
+
getDefaultPersistPath(),
|
|
16
18
|
),
|
|
17
19
|
};
|
|
18
20
|
if (typeof persistState === "object" && persistState.path.endsWith("v3")) {
|
|
@@ -20,7 +22,10 @@ const alchemy = (config?: PluginConfig): PluginOption => {
|
|
|
20
22
|
}
|
|
21
23
|
return cloudflare({
|
|
22
24
|
...config,
|
|
23
|
-
configPath: validateConfigPath(
|
|
25
|
+
configPath: validateConfigPath(
|
|
26
|
+
// config path doesn't need to be in the root, it can be in the app dir
|
|
27
|
+
config?.configPath ?? getDefaultConfigPath(),
|
|
28
|
+
),
|
|
24
29
|
persistState,
|
|
25
30
|
experimental: config?.experimental ?? {
|
|
26
31
|
remoteBindings: true,
|
|
@@ -10,7 +10,7 @@ import { Assets } from "./assets.ts";
|
|
|
10
10
|
import type { Bindings } from "./bindings.ts";
|
|
11
11
|
import { DEFAULT_COMPATIBILITY_DATE } from "./compatibility-date.gen.ts";
|
|
12
12
|
import { unionCompatibilityFlags } from "./compatibility-presets.ts";
|
|
13
|
-
import {
|
|
13
|
+
import { writeMiniflareSymlink } from "./miniflare/symlink-miniflare-state.ts";
|
|
14
14
|
import { type AssetsConfig, Worker, type WorkerProps } from "./worker.ts";
|
|
15
15
|
import { WranglerJson, type WranglerJsonSpec } from "./wrangler.json.ts";
|
|
16
16
|
|
|
@@ -267,7 +267,9 @@ export async function Website<B extends Bindings>(
|
|
|
267
267
|
await fs.writeFile(paths.entrypoint, content);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
if (Scope.current.local) {
|
|
271
|
+
await writeMiniflareSymlink(Scope.current.rootDir, paths.cwd);
|
|
272
|
+
}
|
|
271
273
|
|
|
272
274
|
await WranglerJson({
|
|
273
275
|
path: path.relative(paths.cwd, paths.wrangler.path),
|
|
@@ -293,6 +295,7 @@ export async function Website<B extends Bindings>(
|
|
|
293
295
|
...env,
|
|
294
296
|
...(typeof build === "object" ? build.env : {}),
|
|
295
297
|
NODE_ENV: "production",
|
|
298
|
+
ALCHEMY_ROOT: Scope.current.rootDir,
|
|
296
299
|
},
|
|
297
300
|
memoize: typeof build === "object" ? build.memoize : undefined,
|
|
298
301
|
});
|
|
@@ -350,20 +353,3 @@ export async function Website<B extends Bindings>(
|
|
|
350
353
|
dev: url ? { url } : undefined,
|
|
351
354
|
})) as Website<B>;
|
|
352
355
|
}
|
|
353
|
-
|
|
354
|
-
async function writeMiniflareSymlink(cwd: string) {
|
|
355
|
-
const target = path.resolve(DEFAULT_PERSIST_PATH);
|
|
356
|
-
await fs.mkdir(target, { recursive: true });
|
|
357
|
-
|
|
358
|
-
if (cwd === process.cwd()) {
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const persistPath = path.resolve(cwd, DEFAULT_PERSIST_PATH);
|
|
363
|
-
await fs.mkdir(path.dirname(persistPath), { recursive: true });
|
|
364
|
-
await fs.symlink(target, persistPath).catch((e) => {
|
|
365
|
-
if (e.code !== "EEXIST") {
|
|
366
|
-
throw e;
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
}
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -755,7 +755,9 @@ const _Worker = Resource(
|
|
|
755
755
|
compatibilityDate,
|
|
756
756
|
compatibilityFlags,
|
|
757
757
|
outdir:
|
|
758
|
-
props.bundle?.outdir ??
|
|
758
|
+
props.bundle?.outdir ??
|
|
759
|
+
// the out folder can't be moved to the root of a monorepo, it must be ${cwd} or else miniflare throws a fit
|
|
760
|
+
path.join(process.cwd(), ".alchemy", "out", workerName),
|
|
759
761
|
sourceMap: "sourceMap" in props ? props.sourceMap : undefined,
|
|
760
762
|
});
|
|
761
763
|
|
|
@@ -812,7 +814,7 @@ const _Worker = Resource(
|
|
|
812
814
|
if (options.bundle.isOk()) {
|
|
813
815
|
await options.bundle.value.delete?.();
|
|
814
816
|
}
|
|
815
|
-
await deleteMiniflareWorkerData(options.name, {
|
|
817
|
+
await deleteMiniflareWorkerData(this.scope, options.name, {
|
|
816
818
|
durableObjects: options.durableObjects,
|
|
817
819
|
workflows: options.workflows,
|
|
818
820
|
});
|
package/src/scope.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "./util/idempotent-spawn.ts";
|
|
29
29
|
import { logger } from "./util/logger.ts";
|
|
30
30
|
import { AsyncMutex } from "./util/mutex.ts";
|
|
31
|
+
import { ALCHEMY_ROOT } from "./util/root-dir.ts";
|
|
31
32
|
import type { ITelemetryClient } from "./util/telemetry/client.ts";
|
|
32
33
|
|
|
33
34
|
export class RootScopeStateAttemptError extends Error {
|
|
@@ -89,6 +90,20 @@ export interface ScopeOptions extends ProviderCredentials {
|
|
|
89
90
|
* @default false
|
|
90
91
|
*/
|
|
91
92
|
adopt?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* The path to the root directory of the project.
|
|
95
|
+
*
|
|
96
|
+
* @default process.cwd()
|
|
97
|
+
*/
|
|
98
|
+
rootDir?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Whether this is the application that was selected with `--app`
|
|
101
|
+
*
|
|
102
|
+
* `true` if the application was selected with `--app`
|
|
103
|
+
* `false` if the application was not selected with `--app`
|
|
104
|
+
* `undefined` if the program was not run with `--app`
|
|
105
|
+
*/
|
|
106
|
+
isSelected?: boolean;
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
/**
|
|
@@ -177,7 +192,9 @@ export class Scope {
|
|
|
177
192
|
public readonly logger: LoggerApi;
|
|
178
193
|
public readonly telemetryClient: ITelemetryClient;
|
|
179
194
|
public readonly dataMutex: AsyncMutex;
|
|
195
|
+
public readonly rootDir: string;
|
|
180
196
|
public readonly dotAlchemy: string;
|
|
197
|
+
public readonly isSelected: boolean | undefined;
|
|
181
198
|
|
|
182
199
|
// Provider credentials for scope-level credential overrides
|
|
183
200
|
public readonly providerCredentials: ProviderCredentials;
|
|
@@ -214,17 +231,21 @@ export class Scope {
|
|
|
214
231
|
logger,
|
|
215
232
|
adopt,
|
|
216
233
|
dotAlchemy,
|
|
234
|
+
rootDir,
|
|
235
|
+
isSelected,
|
|
217
236
|
...providerCredentials
|
|
218
237
|
} = options;
|
|
219
238
|
|
|
220
|
-
this.dotAlchemy =
|
|
221
|
-
dotAlchemy ??
|
|
222
|
-
this.parent?.dotAlchemy ??
|
|
223
|
-
path.join(process.cwd(), ".alchemy");
|
|
224
|
-
|
|
225
239
|
this.scopeName = scopeName;
|
|
226
240
|
this.name = this.scopeName;
|
|
227
241
|
this.parent = parent ?? Scope.getScope();
|
|
242
|
+
this.rootDir = rootDir ?? this.parent?.rootDir ?? ALCHEMY_ROOT;
|
|
243
|
+
this.isSelected = isSelected ?? this.parent?.isSelected;
|
|
244
|
+
|
|
245
|
+
this.dotAlchemy =
|
|
246
|
+
dotAlchemy ??
|
|
247
|
+
this.parent?.dotAlchemy ??
|
|
248
|
+
path.resolve(this.rootDir, ".alchemy");
|
|
228
249
|
|
|
229
250
|
// Store provider credentials (TypeScript ensures no conflicts with core options)
|
|
230
251
|
this.providerCredentials = providerCredentials as ProviderCredentials;
|
|
@@ -6,7 +6,6 @@ import { deserialize, serialize } from "../serde.ts";
|
|
|
6
6
|
import type { State, StateStore } from "../state.ts";
|
|
7
7
|
import { ignore } from "../util/ignore.ts";
|
|
8
8
|
|
|
9
|
-
const stateRootDir = path.join(process.cwd(), ".alchemy");
|
|
10
9
|
const ALCHEMY_SEPERATOR_CHAR = process.platform === "win32" ? "-" : ":";
|
|
11
10
|
|
|
12
11
|
export class FileSystemStateStore implements StateStore {
|
|
@@ -18,7 +17,7 @@ export class FileSystemStateStore implements StateStore {
|
|
|
18
17
|
rootDir?: string;
|
|
19
18
|
},
|
|
20
19
|
) {
|
|
21
|
-
this.dir = path.join(options?.rootDir ??
|
|
20
|
+
this.dir = path.join(options?.rootDir ?? scope.dotAlchemy, ...scope.chain);
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
async init(): Promise<void> {
|