@craft-ts/deploy-alchemy 0.7.0-beta.15
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 +21 -0
- package/README.md +38 -0
- package/package.json +53 -0
- package/src/index.d.ts +20 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +17 -0
- package/src/index.js.map +1 -0
- package/src/lib/alchemy-runtime.d.ts +22 -0
- package/src/lib/alchemy-runtime.d.ts.map +1 -0
- package/src/lib/alchemy-runtime.js +150 -0
- package/src/lib/alchemy-runtime.js.map +1 -0
- package/src/lib/credentials.d.ts +6 -0
- package/src/lib/credentials.d.ts.map +1 -0
- package/src/lib/credentials.js +78 -0
- package/src/lib/credentials.js.map +1 -0
- package/src/lib/naming.d.ts +8 -0
- package/src/lib/naming.d.ts.map +1 -0
- package/src/lib/naming.js +16 -0
- package/src/lib/naming.js.map +1 -0
- package/src/lib/plan.d.ts +24 -0
- package/src/lib/plan.d.ts.map +1 -0
- package/src/lib/plan.js +116 -0
- package/src/lib/plan.js.map +1 -0
- package/src/lib/presets/aws.d.ts +13 -0
- package/src/lib/presets/aws.d.ts.map +1 -0
- package/src/lib/presets/aws.js +133 -0
- package/src/lib/presets/aws.js.map +1 -0
- package/src/lib/presets/cloudflare.d.ts +11 -0
- package/src/lib/presets/cloudflare.d.ts.map +1 -0
- package/src/lib/presets/cloudflare.js +121 -0
- package/src/lib/presets/cloudflare.js.map +1 -0
- package/src/lib/presets/preset.d.ts +13 -0
- package/src/lib/presets/preset.d.ts.map +1 -0
- package/src/lib/presets/preset.js +16 -0
- package/src/lib/presets/preset.js.map +1 -0
- package/src/lib/provider.d.ts +22 -0
- package/src/lib/provider.d.ts.map +1 -0
- package/src/lib/provider.js +146 -0
- package/src/lib/provider.js.map +1 -0
- package/src/lib/runtime.d.ts +51 -0
- package/src/lib/runtime.d.ts.map +1 -0
- package/src/lib/runtime.js +9 -0
- package/src/lib/runtime.js.map +1 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { findCraftDeploymentProvider, } from '@craft-ts/deploy';
|
|
4
|
+
import { checkAlchemyCredentials, } from './credentials.js';
|
|
5
|
+
import { planAlchemyDeployment } from './plan.js';
|
|
6
|
+
import { loadAlchemyRuntime } from './alchemy-runtime.js';
|
|
7
|
+
const PROVIDER_NAME = 'alchemy';
|
|
8
|
+
/**
|
|
9
|
+
* The Alchemy provider.
|
|
10
|
+
*
|
|
11
|
+
* It consumes the manifest CraftTS produced and never rebuilds routes,
|
|
12
|
+
* contracts or layers. Everything platform-specific lives in a preset, and
|
|
13
|
+
* every mutation goes through the runtime port, so `preview` provably touches
|
|
14
|
+
* nothing.
|
|
15
|
+
*/
|
|
16
|
+
export function createAlchemyDeploymentProvider(options = {}) {
|
|
17
|
+
const loadRuntime = options.runtime ?? loadAlchemyRuntime;
|
|
18
|
+
const environment = options.environment ?? process.env;
|
|
19
|
+
return {
|
|
20
|
+
name: PROVIDER_NAME,
|
|
21
|
+
capabilities: findCraftDeploymentProvider(PROVIDER_NAME)?.capabilities ?? [],
|
|
22
|
+
async check(request) {
|
|
23
|
+
const diagnostics = [
|
|
24
|
+
...checkAlchemyCredentials(request.manifest.platform, environment),
|
|
25
|
+
...planAlchemyDeployment({ request, existing: [] }).diagnostics,
|
|
26
|
+
...missingArtifacts(request),
|
|
27
|
+
];
|
|
28
|
+
try {
|
|
29
|
+
await loadRuntime();
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
diagnostics.push({
|
|
33
|
+
code: 'CRAFT_DEPLOY_PROVIDER_TOOLCHAIN_MISSING',
|
|
34
|
+
severity: 'error',
|
|
35
|
+
provider: PROVIDER_NAME,
|
|
36
|
+
platform: request.manifest.platform,
|
|
37
|
+
message: `Alchemy could not be loaded: ${messageOf(error)}`,
|
|
38
|
+
fix: 'Install `alchemy` in the project, or deploy with a provider that needs no infrastructure engine.',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return diagnostics;
|
|
42
|
+
},
|
|
43
|
+
async preview(request) {
|
|
44
|
+
const runtime = await loadRuntime();
|
|
45
|
+
const scope = await runtime.open({
|
|
46
|
+
app: request.manifest.name,
|
|
47
|
+
stage: request.stage,
|
|
48
|
+
// The read phase is what makes a preview safe: Alchemy resolves the
|
|
49
|
+
// recorded state and creates nothing.
|
|
50
|
+
phase: 'read',
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
const { plan, diagnostics } = planAlchemyDeployment({
|
|
54
|
+
request,
|
|
55
|
+
existing: await scope.read(),
|
|
56
|
+
});
|
|
57
|
+
return withRuntimeNote(plan, runtime, diagnostics);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
await scope.dispose();
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
async deploy(request) {
|
|
64
|
+
const runtime = await loadRuntime();
|
|
65
|
+
const scope = await runtime.open({
|
|
66
|
+
app: request.manifest.name,
|
|
67
|
+
stage: request.stage,
|
|
68
|
+
phase: 'up',
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
return await applyPlan(request, scope);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
await scope.dispose();
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function applyPlan(request, scope) {
|
|
81
|
+
const { resources, diagnostics } = planAlchemyDeployment({
|
|
82
|
+
request,
|
|
83
|
+
existing: await scope.read(),
|
|
84
|
+
});
|
|
85
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === 'error');
|
|
86
|
+
if (errors.length > 0) {
|
|
87
|
+
throw new Error(`Alchemy refuses to deploy: ${errors.map((error) => `${error.code} ${error.message}`).join(' ')}`);
|
|
88
|
+
}
|
|
89
|
+
const outputs = {};
|
|
90
|
+
let url;
|
|
91
|
+
for (const resource of resources) {
|
|
92
|
+
const state = await scope.apply(resource);
|
|
93
|
+
for (const [key, value] of Object.entries(state.outputs)) {
|
|
94
|
+
outputs[`${state.name}.${key}`] = value;
|
|
95
|
+
if (key === 'url' && url === undefined)
|
|
96
|
+
url = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Finalizing is what deletes the resources the manifest no longer declares,
|
|
100
|
+
// so it happens once every declared resource exists.
|
|
101
|
+
await scope.finalize();
|
|
102
|
+
return {
|
|
103
|
+
provider: 'alchemy',
|
|
104
|
+
stage: request.stage,
|
|
105
|
+
...(url ? { url } : {}),
|
|
106
|
+
outputs,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function withRuntimeNote(plan, runtime, diagnostics) {
|
|
110
|
+
return {
|
|
111
|
+
...plan,
|
|
112
|
+
notes: [
|
|
113
|
+
`Alchemy ${runtime.version}, stage \`${plan.stage}\`.`,
|
|
114
|
+
...plan.notes,
|
|
115
|
+
...diagnostics.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`),
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Artefacts the plan would upload and that the build has not produced. */
|
|
120
|
+
function missingArtifacts(request) {
|
|
121
|
+
const manifest = request.manifest;
|
|
122
|
+
const candidates = [
|
|
123
|
+
manifest.artifact.publicDir
|
|
124
|
+
? { path: 'artifact.publicDir', value: manifest.artifact.publicDir }
|
|
125
|
+
: null,
|
|
126
|
+
manifest.artifact.serverEntry
|
|
127
|
+
? { path: 'artifact.serverEntry', value: manifest.artifact.serverEntry }
|
|
128
|
+
: null,
|
|
129
|
+
].filter((candidate) => Boolean(candidate));
|
|
130
|
+
return candidates
|
|
131
|
+
.filter((candidate) => !existsSync(resolve(request.rootDir, candidate.value)))
|
|
132
|
+
.map((candidate) => ({
|
|
133
|
+
code: 'CRAFT_DEPLOY_ARTIFACT_MISSING',
|
|
134
|
+
severity: 'error',
|
|
135
|
+
provider: PROVIDER_NAME,
|
|
136
|
+
runtime: manifest.runtime,
|
|
137
|
+
path: candidate.path,
|
|
138
|
+
file: candidate.value,
|
|
139
|
+
message: `Alchemy would upload \`${candidate.value}\`, which does not exist.`,
|
|
140
|
+
fix: 'Run the declared build command before deploying.',
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
function messageOf(error) {
|
|
144
|
+
return error instanceof Error ? error.message : String(error);
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../../../libs/deploy-alchemy/src/lib/provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n findCraftDeploymentProvider,\n type CraftDeploymentDiagnostic,\n type CraftDeploymentPlan,\n type CraftDeploymentProvider,\n type CraftDeploymentRequest,\n type CraftDeploymentResult,\n} from '@craft-ts/deploy';\nimport {\n checkAlchemyCredentials,\n type AlchemyEnvironment,\n} from './credentials.js';\nimport { planAlchemyDeployment } from './plan.js';\nimport { loadAlchemyRuntime } from './alchemy-runtime.js';\nimport type {\n AlchemyRuntime,\n AlchemyRuntimeLoader,\n AlchemyScope,\n} from './runtime.js';\n\nexport type AlchemyProviderOptions = Readonly<{\n /**\n * Resolves the Alchemy runtime. Defaults to importing the optional peer\n * dependency; tests and dry runs replace it.\n */\n runtime?: AlchemyRuntimeLoader;\n /** Environment the credentials are read from. Defaults to `process.env`. */\n environment?: AlchemyEnvironment;\n}>;\n\nconst PROVIDER_NAME = 'alchemy';\n\n/**\n * The Alchemy provider.\n *\n * It consumes the manifest CraftTS produced and never rebuilds routes,\n * contracts or layers. Everything platform-specific lives in a preset, and\n * every mutation goes through the runtime port, so `preview` provably touches\n * nothing.\n */\nexport function createAlchemyDeploymentProvider(\n options: AlchemyProviderOptions = {},\n): CraftDeploymentProvider {\n const loadRuntime = options.runtime ?? loadAlchemyRuntime;\n const environment = options.environment ?? process.env;\n\n return {\n name: PROVIDER_NAME,\n capabilities:\n findCraftDeploymentProvider(PROVIDER_NAME)?.capabilities ?? [],\n\n async check(request) {\n const diagnostics: CraftDeploymentDiagnostic[] = [\n ...checkAlchemyCredentials(request.manifest.platform, environment),\n ...planAlchemyDeployment({ request, existing: [] }).diagnostics,\n ...missingArtifacts(request),\n ];\n\n try {\n await loadRuntime();\n } catch (error) {\n diagnostics.push({\n code: 'CRAFT_DEPLOY_PROVIDER_TOOLCHAIN_MISSING',\n severity: 'error',\n provider: PROVIDER_NAME,\n platform: request.manifest.platform,\n message: `Alchemy could not be loaded: ${messageOf(error)}`,\n fix: 'Install `alchemy` in the project, or deploy with a provider that needs no infrastructure engine.',\n });\n }\n\n return diagnostics;\n },\n\n async preview(request) {\n const runtime = await loadRuntime();\n const scope = await runtime.open({\n app: request.manifest.name,\n stage: request.stage,\n // The read phase is what makes a preview safe: Alchemy resolves the\n // recorded state and creates nothing.\n phase: 'read',\n });\n try {\n const { plan, diagnostics } = planAlchemyDeployment({\n request,\n existing: await scope.read(),\n });\n return withRuntimeNote(plan, runtime, diagnostics);\n } finally {\n await scope.dispose();\n }\n },\n\n async deploy(request) {\n const runtime = await loadRuntime();\n const scope = await runtime.open({\n app: request.manifest.name,\n stage: request.stage,\n phase: 'up',\n });\n try {\n return await applyPlan(request, scope);\n } catch (error) {\n await scope.dispose();\n throw error;\n }\n },\n };\n}\n\nasync function applyPlan(\n request: CraftDeploymentRequest,\n scope: AlchemyScope,\n): Promise<CraftDeploymentResult> {\n const { resources, diagnostics } = planAlchemyDeployment({\n request,\n existing: await scope.read(),\n });\n\n const errors = diagnostics.filter(\n (diagnostic) => diagnostic.severity === 'error',\n );\n if (errors.length > 0) {\n throw new Error(\n `Alchemy refuses to deploy: ${errors.map((error) => `${error.code} ${error.message}`).join(' ')}`,\n );\n }\n\n const outputs: Record<string, string> = {};\n let url: string | undefined;\n\n for (const resource of resources) {\n const state = await scope.apply(resource);\n for (const [key, value] of Object.entries(state.outputs)) {\n outputs[`${state.name}.${key}`] = value;\n if (key === 'url' && url === undefined) url = value;\n }\n }\n\n // Finalizing is what deletes the resources the manifest no longer declares,\n // so it happens once every declared resource exists.\n await scope.finalize();\n\n return {\n provider: 'alchemy',\n stage: request.stage,\n ...(url ? { url } : {}),\n outputs,\n };\n}\n\nfunction withRuntimeNote(\n plan: CraftDeploymentPlan,\n runtime: AlchemyRuntime,\n diagnostics: readonly CraftDeploymentDiagnostic[],\n): CraftDeploymentPlan {\n return {\n ...plan,\n notes: [\n `Alchemy ${runtime.version}, stage \\`${plan.stage}\\`.`,\n ...plan.notes,\n ...diagnostics.map(\n (diagnostic) => `${diagnostic.code}: ${diagnostic.message}`,\n ),\n ],\n };\n}\n\n/** Artefacts the plan would upload and that the build has not produced. */\nfunction missingArtifacts(\n request: CraftDeploymentRequest,\n): readonly CraftDeploymentDiagnostic[] {\n const manifest = request.manifest;\n const candidates = [\n manifest.artifact.publicDir\n ? { path: 'artifact.publicDir', value: manifest.artifact.publicDir }\n : null,\n manifest.artifact.serverEntry\n ? { path: 'artifact.serverEntry', value: manifest.artifact.serverEntry }\n : null,\n ].filter((candidate): candidate is { path: string; value: string } =>\n Boolean(candidate),\n );\n\n return candidates\n .filter(\n (candidate) => !existsSync(resolve(request.rootDir, candidate.value)),\n )\n .map((candidate) => ({\n code: 'CRAFT_DEPLOY_ARTIFACT_MISSING',\n severity: 'error',\n provider: PROVIDER_NAME,\n runtime: manifest.runtime,\n path: candidate.path,\n file: candidate.value,\n message: `Alchemy would upload \\`${candidate.value}\\`, which does not exist.`,\n fix: 'Run the declared build command before deploying.',\n }));\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The narrow port this provider needs from Alchemy.
|
|
3
|
+
*
|
|
4
|
+
* Alchemy is an optional peer dependency: keeping the surface to four
|
|
5
|
+
* operations lets the preset logic be tested without installing an
|
|
6
|
+
* infrastructure engine, and lets the real adapter stay a thin, replaceable
|
|
7
|
+
* translation.
|
|
8
|
+
*/
|
|
9
|
+
export type AlchemyPhase = 'read' | 'up';
|
|
10
|
+
export type AlchemyResourceRequest = Readonly<{
|
|
11
|
+
/** Namespaced resource type, e.g. `cloudflare:Worker`. */
|
|
12
|
+
type: string;
|
|
13
|
+
name: string;
|
|
14
|
+
/** Declared properties. Never contains a secret value. */
|
|
15
|
+
properties: Readonly<Record<string, unknown>>;
|
|
16
|
+
}>;
|
|
17
|
+
export type AlchemyResourceState = Readonly<{
|
|
18
|
+
type: string;
|
|
19
|
+
name: string;
|
|
20
|
+
/** Outputs recorded by Alchemy, such as a URL or a resource identifier. */
|
|
21
|
+
outputs: Readonly<Record<string, string>>;
|
|
22
|
+
/**
|
|
23
|
+
* Properties the recorded resource was created with, when the state keeps
|
|
24
|
+
* them. Without them a preview cannot tell an update from an unchanged
|
|
25
|
+
* resource, so it reports the safer of the two.
|
|
26
|
+
*/
|
|
27
|
+
properties?: Readonly<Record<string, unknown>>;
|
|
28
|
+
}>;
|
|
29
|
+
export type AlchemyScope = Readonly<{
|
|
30
|
+
/** Resources already recorded for this application and stage. */
|
|
31
|
+
read(): Promise<readonly AlchemyResourceState[]>;
|
|
32
|
+
/** Creates or updates one resource. Only called during the `up` phase. */
|
|
33
|
+
apply(resource: AlchemyResourceRequest): Promise<AlchemyResourceState>;
|
|
34
|
+
/** Commits the scope, letting Alchemy delete what is no longer declared. */
|
|
35
|
+
finalize(): Promise<void>;
|
|
36
|
+
/** Releases the scope without committing. */
|
|
37
|
+
dispose(): Promise<void>;
|
|
38
|
+
}>;
|
|
39
|
+
export type AlchemyOpenOptions = Readonly<{
|
|
40
|
+
app: string;
|
|
41
|
+
stage: string;
|
|
42
|
+
phase: AlchemyPhase;
|
|
43
|
+
}>;
|
|
44
|
+
export type AlchemyRuntime = Readonly<{
|
|
45
|
+
/** Version of the installed Alchemy, reported in diagnostics. */
|
|
46
|
+
version: string;
|
|
47
|
+
open(options: AlchemyOpenOptions): Promise<AlchemyScope>;
|
|
48
|
+
}>;
|
|
49
|
+
/** Resolves the runtime lazily, so importing the provider installs nothing. */
|
|
50
|
+
export type AlchemyRuntimeLoader = () => Promise<AlchemyRuntime>;
|
|
51
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../libs/deploy-alchemy/src/lib/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,CAAC;AAEzC,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC/C,CAAC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAChD,CAAC,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAClC,iEAAiE;IACjE,IAAI,IAAI,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IACjD,0EAA0E;IAC1E,KAAK,CAAC,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvE,4EAA4E;IAC5E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,6CAA6C;IAC7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1D,CAAC,CAAC;AAEH,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The narrow port this provider needs from Alchemy.
|
|
3
|
+
*
|
|
4
|
+
* Alchemy is an optional peer dependency: keeping the surface to four
|
|
5
|
+
* operations lets the preset logic be tested without installing an
|
|
6
|
+
* infrastructure engine, and lets the real adapter stay a thin, replaceable
|
|
7
|
+
* translation.
|
|
8
|
+
*/
|
|
9
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../../../libs/deploy-alchemy/src/lib/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG","sourcesContent":["/**\n * The narrow port this provider needs from Alchemy.\n *\n * Alchemy is an optional peer dependency: keeping the surface to four\n * operations lets the preset logic be tested without installing an\n * infrastructure engine, and lets the real adapter stay a thin, replaceable\n * translation.\n */\n\nexport type AlchemyPhase = 'read' | 'up';\n\nexport type AlchemyResourceRequest = Readonly<{\n /** Namespaced resource type, e.g. `cloudflare:Worker`. */\n type: string;\n name: string;\n /** Declared properties. Never contains a secret value. */\n properties: Readonly<Record<string, unknown>>;\n}>;\n\nexport type AlchemyResourceState = Readonly<{\n type: string;\n name: string;\n /** Outputs recorded by Alchemy, such as a URL or a resource identifier. */\n outputs: Readonly<Record<string, string>>;\n /**\n * Properties the recorded resource was created with, when the state keeps\n * them. Without them a preview cannot tell an update from an unchanged\n * resource, so it reports the safer of the two.\n */\n properties?: Readonly<Record<string, unknown>>;\n}>;\n\nexport type AlchemyScope = Readonly<{\n /** Resources already recorded for this application and stage. */\n read(): Promise<readonly AlchemyResourceState[]>;\n /** Creates or updates one resource. Only called during the `up` phase. */\n apply(resource: AlchemyResourceRequest): Promise<AlchemyResourceState>;\n /** Commits the scope, letting Alchemy delete what is no longer declared. */\n finalize(): Promise<void>;\n /** Releases the scope without committing. */\n dispose(): Promise<void>;\n}>;\n\nexport type AlchemyOpenOptions = Readonly<{\n app: string;\n stage: string;\n phase: AlchemyPhase;\n}>;\n\nexport type AlchemyRuntime = Readonly<{\n /** Version of the installed Alchemy, reported in diagnostics. */\n version: string;\n open(options: AlchemyOpenOptions): Promise<AlchemyScope>;\n}>;\n\n/** Resolves the runtime lazily, so importing the provider installs nothing. */\nexport type AlchemyRuntimeLoader = () => Promise<AlchemyRuntime>;\n"]}
|