@fjall/deploy-core 0.94.1 → 0.96.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/dist/.minified +1 -1
- package/dist/src/aws/organisations/accounts.js +1 -99
- package/dist/src/aws/organisations/backup.js +1 -30
- package/dist/src/aws/organisations/costAllocation.js +1 -28
- package/dist/src/aws/organisations/delegatedAdmin.js +3 -43
- package/dist/src/aws/organisations/identityCentre.js +1 -23
- package/dist/src/aws/organisations/ipam.js +1 -20
- package/dist/src/aws/organisations/organisation.js +1 -103
- package/dist/src/aws/organisations/organisationalUnits.js +1 -239
- package/dist/src/aws/organisations/policies.js +1 -37
- package/dist/src/aws/organisations/ram.js +1 -19
- package/dist/src/aws/organisations/serviceAccess.js +1 -44
- package/dist/src/aws/organisations/trustedAccess.js +1 -19
- package/dist/src/aws/utils/regions.js +1 -1
- package/dist/src/index.js +1 -65
- package/dist/src/orchestration/__tests__/cascadeTestHelpers.js +1 -78
- package/dist/src/orchestration/activeDeploymentGuard.js +5 -39
- package/dist/src/orchestration/applicationDeploy.js +1 -149
- package/dist/src/orchestration/applicationDeployHelpers.js +4 -223
- package/dist/src/orchestration/applicationDestroy.js +1 -131
- package/dist/src/orchestration/builders/dockerBuilder.js +1 -98
- package/dist/src/orchestration/builders/openNextBuilder.js +1 -144
- package/dist/src/orchestration/cascadeHelpers.js +1 -160
- package/dist/src/orchestration/contextHelpers.js +1 -107
- package/dist/src/orchestration/deploy.js +1 -42
- package/dist/src/orchestration/destroy.js +1 -67
- package/dist/src/orchestration/detectionPipeline.js +1 -84
- package/dist/src/orchestration/dockerBuildHelper.js +1 -49
- package/dist/src/orchestration/dockerInterface.js +0 -1
- package/dist/src/orchestration/domainInterface.js +0 -1
- package/dist/src/orchestration/openNextBuild.js +3 -243
- package/dist/src/orchestration/organisationDeploy.js +3 -284
- package/dist/src/orchestration/organisationDestroy.js +3 -189
- package/dist/src/orchestration/organisationSetup.js +1 -247
- package/dist/src/orchestration/resolveOperation.js +1 -123
- package/dist/src/orchestration/welcomeImageHelper.js +1 -64
- package/dist/src/services/application/ApplicationStackService.js +1 -218
- package/dist/src/services/application/applicationStackHelpers.js +4 -248
- package/dist/src/services/infrastructure/CdkCommandRunner.js +2 -244
- package/dist/src/services/infrastructure/CdkOutputAnalyser.js +1 -125
- package/dist/src/services/infrastructure/CdkProcessManager.js +3 -278
- package/dist/src/services/infrastructure/CdkService.js +3 -213
- package/dist/src/services/infrastructure/CloudFormationService.js +1 -248
- package/dist/src/services/infrastructure/ICdkProcessManager.js +0 -1
- package/dist/src/services/supporting/CdkContextBuilder.js +1 -44
- package/dist/src/services/supporting/TemplateHashService.js +1 -152
- package/dist/src/steps/stepRegistry.js +1 -505
- package/dist/src/types/apiClient.js +0 -1
- package/dist/src/types/detection.js +0 -1
- package/dist/src/types/frameworkBuilder.js +0 -8
- package/dist/src/types/params.js +0 -1
- package/dist/src/types/patternDetection.js +1 -88
- package/dist/src/types/stepDefinitions.js +1 -98
- package/package.json +4 -4
|
@@ -1,223 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { hasDockerfile } from "../util/dockerfileDetection.js";
|
|
6
|
-
import { runDockerBuild } from "./dockerBuildHelper.js";
|
|
7
|
-
import { runWelcomeImageSetup } from "./welcomeImageHelper.js";
|
|
8
|
-
/**
|
|
9
|
-
* Identify Phase 2 stacks (Storage, Messaging, Database) that are consecutive
|
|
10
|
-
* in the deploy order and have changes. Returns them for parallel deployment
|
|
11
|
-
* only if there are at least 2.
|
|
12
|
-
*/
|
|
13
|
-
export function getParallelPhase2Stacks(deployOrder, currentIndex, stackChanges, force) {
|
|
14
|
-
const phase2Set = new Set(PARALLEL_DEPLOY_GROUPS.PHASE_2.stacks);
|
|
15
|
-
const candidates = [];
|
|
16
|
-
for (let j = currentIndex; j < deployOrder.length; j++) {
|
|
17
|
-
if (phase2Set.has(deployOrder[j])) {
|
|
18
|
-
candidates.push(deployOrder[j]);
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
if (candidates.length < 2)
|
|
25
|
-
return [];
|
|
26
|
-
// Only include stacks that actually have changes (unless forced)
|
|
27
|
-
if (!force) {
|
|
28
|
-
const withChanges = candidates.filter((stack) => {
|
|
29
|
-
// stackChanges is keyed by full stack name but we only have the type here;
|
|
30
|
-
// since we don't know the app name, check all entries ending with the stack type
|
|
31
|
-
for (const [key, changed] of stackChanges) {
|
|
32
|
-
if (key.endsWith(stack) && !changed)
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
});
|
|
37
|
-
return withChanges.length >= 2 ? withChanges : [];
|
|
38
|
-
}
|
|
39
|
-
return candidates;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Deploy a group of stacks in parallel, reporting step events for each.
|
|
43
|
-
*/
|
|
44
|
-
export async function deployParallelPhase(stacks, operation, services, context, callbacks, startIndex, totalSteps, detection, allOutputs, deployedHashes) {
|
|
45
|
-
// Emit step start for each parallel stack
|
|
46
|
-
for (let k = 0; k < stacks.length; k++) {
|
|
47
|
-
const stack = stacks[k];
|
|
48
|
-
const stepId = getApplicationStepId(stack, "deploy");
|
|
49
|
-
const stepName = getApplicationStepName(stack, "deploy");
|
|
50
|
-
callbacks.onStepStart?.(stepId, stepName, startIndex + k, totalSteps);
|
|
51
|
-
}
|
|
52
|
-
emitProgress(callbacks, "Deploying infrastructure in parallel…");
|
|
53
|
-
callbacks.onParallelPhaseStart?.(stacks, "Storage and database resources (parallel)");
|
|
54
|
-
const parallelResult = await services.stackService.deployStacksInParallel(stacks, context, {
|
|
55
|
-
onOutput: forwardOutput(callbacks),
|
|
56
|
-
onResourceProgress: (event, stackId) => {
|
|
57
|
-
callbacks.onResourceProgress?.(event);
|
|
58
|
-
if (stackId) {
|
|
59
|
-
callbacks.onParallelStackResourceProgress?.(stackId, event);
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
onStackComplete: (stack, stackSuccess, _duration, error) => {
|
|
63
|
-
const stepId = getApplicationStepId(stack, "deploy");
|
|
64
|
-
const stepName = getApplicationStepName(stack, "deploy");
|
|
65
|
-
const idx = startIndex + stacks.indexOf(stack);
|
|
66
|
-
callbacks.onStepComplete?.(stepId, stepName, stackSuccess ? "completed" : "error", idx, totalSteps);
|
|
67
|
-
if (!stackSuccess && error) {
|
|
68
|
-
callbacks.onError?.(error);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
if (!parallelResult.success) {
|
|
73
|
-
callbacks.onParallelPhaseComplete?.([]);
|
|
74
|
-
return failure(parallelResult.error);
|
|
75
|
-
}
|
|
76
|
-
// Check for any failures
|
|
77
|
-
const failures = parallelResult.data.filter((r) => !r.success);
|
|
78
|
-
callbacks.onParallelPhaseComplete?.(parallelResult.data.map((r) => ({
|
|
79
|
-
stack: r.stack,
|
|
80
|
-
success: r.success,
|
|
81
|
-
error: r.error
|
|
82
|
-
})));
|
|
83
|
-
if (failures.length > 0) {
|
|
84
|
-
const failedStacks = failures.map((f) => f.stack).join(", ");
|
|
85
|
-
const errorDetails = failures
|
|
86
|
-
.map((f) => `${f.stack}: ${f.error?.message ?? "Unknown error"}`)
|
|
87
|
-
.join("\n");
|
|
88
|
-
return failure(new Error(`Failed to deploy stacks: ${failedStacks}\n\n${errorDetails}`));
|
|
89
|
-
}
|
|
90
|
-
// Collect outputs and track hashes for successful parallel stacks
|
|
91
|
-
for (const result of parallelResult.data) {
|
|
92
|
-
if (result.success && result.outputs) {
|
|
93
|
-
for (const [key, value] of Object.entries(result.outputs)) {
|
|
94
|
-
allOutputs[key] = String(value);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const stackName = getApplicationStackName(operation.appName, result.stack);
|
|
98
|
-
const hash = detection.currentHashes.get(stackName);
|
|
99
|
-
if (hash) {
|
|
100
|
-
deployedHashes.set(stackName, hash);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return success(undefined);
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Deploy a single stack with step lifecycle events and output collection.
|
|
107
|
-
*/
|
|
108
|
-
export async function deployStackSequential(stack, services, context, callbacks, stepIndex, totalSteps, allOutputs) {
|
|
109
|
-
const stepId = getApplicationStepId(stack, "deploy");
|
|
110
|
-
const stepName = getApplicationStepName(stack, "deploy");
|
|
111
|
-
callbacks.onStepStart?.(stepId, stepName, stepIndex, totalSteps);
|
|
112
|
-
const result = await services.stackService.deployStack(stack, context, {
|
|
113
|
-
onOutput: forwardOutput(callbacks),
|
|
114
|
-
onResourceProgress: forwardResourceProgress(callbacks)
|
|
115
|
-
});
|
|
116
|
-
if (!result.success) {
|
|
117
|
-
callbacks.onStepComplete?.(stepId, stepName, "error", stepIndex, totalSteps);
|
|
118
|
-
callbacks.onError?.(result.error);
|
|
119
|
-
return failure(result.error);
|
|
120
|
-
}
|
|
121
|
-
callbacks.onStepComplete?.(stepId, stepName, "completed", stepIndex, totalSteps);
|
|
122
|
-
if (result.data.outputs) {
|
|
123
|
-
for (const [key, value] of Object.entries(result.data.outputs)) {
|
|
124
|
-
allOutputs[key] = String(value);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return success(undefined);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Run Docker build/push or welcome image setup before the Compute stack.
|
|
131
|
-
* Returns a failure Result if Docker operations fail, or null if not applicable/successful.
|
|
132
|
-
*/
|
|
133
|
-
export async function runDockerPreCompute(stack, params, services, operation, callbacks, hasDockerfileOnDisk) {
|
|
134
|
-
if (stack !== APPLICATION_STACKS.COMPUTE || !params.dockerProvider)
|
|
135
|
-
return null;
|
|
136
|
-
if (hasDockerfileOnDisk) {
|
|
137
|
-
const dockerResult = await runDockerBuild(params, services, operation, callbacks);
|
|
138
|
-
if (!dockerResult.success)
|
|
139
|
-
return failure(dockerResult.error);
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
const ecrInitResult = await runWelcomeImageSetup(params, services, operation, callbacks);
|
|
143
|
-
if (!ecrInitResult.success)
|
|
144
|
-
return failure(ecrInitResult.error);
|
|
145
|
-
}
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Deploy all stacks unconditionally (deployOnly mode).
|
|
150
|
-
* Skips CDK synth/hash comparison — uses the framework registry to determine
|
|
151
|
-
* deploy order, then deploys every stack sequentially.
|
|
152
|
-
*/
|
|
153
|
-
export async function deployAllStacks(params, services, operation, context, startTime, plan) {
|
|
154
|
-
const { callbacks } = params;
|
|
155
|
-
// Use plan from registry if available, otherwise resolve now
|
|
156
|
-
let deployOrder;
|
|
157
|
-
if (plan) {
|
|
158
|
-
deployOrder = plan.deployOrder;
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
const resolved = services.frameworkRegistry.resolve({
|
|
162
|
-
appPath: operation.path
|
|
163
|
-
});
|
|
164
|
-
if (resolved) {
|
|
165
|
-
const freshPlan = resolved.builder.plan({ appPath: operation.path }, resolved.detection);
|
|
166
|
-
deployOrder = freshPlan.deployOrder;
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
deployOrder = getApplicationDeployOrder();
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const totalSteps = deployOrder.length;
|
|
173
|
-
// Bootstrap
|
|
174
|
-
const bsResult = await bootstrapOrFail(services, context, callbacks);
|
|
175
|
-
if (!bsResult.success)
|
|
176
|
-
return bsResult;
|
|
177
|
-
// Deploy all stacks sequentially
|
|
178
|
-
const allOutputs = {};
|
|
179
|
-
for (let i = 0; i < deployOrder.length; i++) {
|
|
180
|
-
const stack = deployOrder[i];
|
|
181
|
-
// Docker operations before Compute stack
|
|
182
|
-
const dockerFailed = await runDockerPreCompute(stack, params, services, operation, callbacks, hasDockerfile(operation.path));
|
|
183
|
-
if (dockerFailed)
|
|
184
|
-
return dockerFailed;
|
|
185
|
-
const deployResult = await deployStackSequential(stack, services, context, callbacks, i, totalSteps, allOutputs);
|
|
186
|
-
if (!deployResult.success)
|
|
187
|
-
return failure(deployResult.error);
|
|
188
|
-
}
|
|
189
|
-
// Resolve website URL
|
|
190
|
-
const websiteUrl = await services.stackService.resolveWebsiteUrl(operation.appName);
|
|
191
|
-
if (websiteUrl) {
|
|
192
|
-
allOutputs.websiteUrl = websiteUrl;
|
|
193
|
-
}
|
|
194
|
-
return success({
|
|
195
|
-
target: operation.appName,
|
|
196
|
-
deploymentType: "application",
|
|
197
|
-
outputs: Object.keys(allOutputs).length > 0 ? allOutputs : undefined,
|
|
198
|
-
durationMs: Date.now() - startTime
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Bridge DeployCallbacks to BuildCallbacks.
|
|
203
|
-
* Fires both legacy OpenNext-specific callbacks and generic build callbacks
|
|
204
|
-
* for backwards compatibility during the transition period.
|
|
205
|
-
*/
|
|
206
|
-
export function createBuildCallbacks(callbacks) {
|
|
207
|
-
return {
|
|
208
|
-
onBuildStart: (builderName) => {
|
|
209
|
-
callbacks.onOpenNextBuildStart?.();
|
|
210
|
-
callbacks.onLog?.(`${builderName} build started`, "info");
|
|
211
|
-
},
|
|
212
|
-
onBuildProgress: (_builderName, message) => {
|
|
213
|
-
callbacks.onOpenNextProgress?.(message);
|
|
214
|
-
},
|
|
215
|
-
onBuildComplete: (builderName) => {
|
|
216
|
-
callbacks.onOpenNextBuildComplete?.();
|
|
217
|
-
callbacks.onLog?.(`${builderName} build complete`, "info");
|
|
218
|
-
},
|
|
219
|
-
onBuildError: (_builderName, error) => {
|
|
220
|
-
callbacks.onOpenNextBuildError?.(error);
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
}
|
|
1
|
+
import{success as h,failure as P}from"@fjall/generator";import{APPLICATION_STACKS as R,getApplicationDeployOrder as A,getApplicationStackName as B,getApplicationStepName as y,getApplicationStepId as O,PARALLEL_DEPLOY_GROUPS as E}from"../types/operations.js";import{emitProgress as k}from"../services/supporting/helpers.js";import{bootstrapOrFail as x,forwardOutput as w,forwardResourceProgress as D}from"./contextHelpers.js";import{hasDockerfile as L}from"../util/dockerfileDetection.js";import{runDockerBuild as _}from"./dockerBuildHelper.js";import{runWelcomeImageSetup as j}from"./welcomeImageHelper.js";function q(t,o,s,i){const n=new Set(E.PHASE_2.stacks),a=[];for(let r=o;r<t.length&&n.has(t[r]);r++)a.push(t[r]);if(a.length<2)return[];if(!i){const r=a.filter(l=>{for(const[f,c]of s)if(f.endsWith(l)&&!c)return!1;return!0});return r.length>=2?r:[]}return a}async function G(t,o,s,i,n,a,r,l,f,c){for(let e=0;e<t.length;e++){const u=t[e],p=O(u,"deploy"),m=y(u,"deploy");n.onStepStart?.(p,m,a+e,r)}k(n,"Deploying infrastructure in parallel\u2026"),n.onParallelPhaseStart?.(t,"Storage and database resources (parallel)");const d=await s.stackService.deployStacksInParallel(t,i,{onOutput:w(n),onResourceProgress:(e,u)=>{n.onResourceProgress?.(e),u&&n.onParallelStackResourceProgress?.(u,e)},onStackComplete:(e,u,p,m)=>{const S=O(e,"deploy"),N=y(e,"deploy"),C=a+t.indexOf(e);n.onStepComplete?.(S,N,u?"completed":"error",C,r),!u&&m&&n.onError?.(m)}});if(!d.success)return n.onParallelPhaseComplete?.([]),P(d.error);const g=d.data.filter(e=>!e.success);if(n.onParallelPhaseComplete?.(d.data.map(e=>({stack:e.stack,success:e.success,error:e.error}))),g.length>0){const e=g.map(p=>p.stack).join(", "),u=g.map(p=>`${p.stack}: ${p.error?.message??"Unknown error"}`).join(`
|
|
2
|
+
`);return P(new Error(`Failed to deploy stacks: ${e}
|
|
3
|
+
|
|
4
|
+
${u}`))}for(const e of d.data){if(e.success&&e.outputs)for(const[m,S]of Object.entries(e.outputs))f[m]=String(S);const u=B(o.appName,e.stack),p=l.currentHashes.get(u);p&&c.set(u,p)}return h(void 0)}async function U(t,o,s,i,n,a,r){const l=O(t,"deploy"),f=y(t,"deploy");i.onStepStart?.(l,f,n,a);const c=await o.stackService.deployStack(t,s,{onOutput:w(i),onResourceProgress:D(i)});if(!c.success)return i.onStepComplete?.(l,f,"error",n,a),i.onError?.(c.error),P(c.error);if(i.onStepComplete?.(l,f,"completed",n,a),c.data.outputs)for(const[d,g]of Object.entries(c.data.outputs))r[d]=String(g);return h(void 0)}async function $(t,o,s,i,n,a){if(t!==R.COMPUTE||!o.dockerProvider)return null;if(a){const r=await _(o,s,i,n);if(!r.success)return P(r.error)}else{const r=await j(o,s,i,n);if(!r.success)return P(r.error)}return null}async function K(t,o,s,i,n,a){const{callbacks:r}=t;let l;if(a)l=a.deployOrder;else{const e=o.frameworkRegistry.resolve({appPath:s.path});e?l=e.builder.plan({appPath:s.path},e.detection).deployOrder:l=A()}const f=l.length,c=await x(o,i,r);if(!c.success)return c;const d={};for(let e=0;e<l.length;e++){const u=l[e],p=await $(u,t,o,s,r,L(s.path));if(p)return p;const m=await U(u,o,i,r,e,f,d);if(!m.success)return P(m.error)}const g=await o.stackService.resolveWebsiteUrl(s.appName);return g&&(d.websiteUrl=g),h({target:s.appName,deploymentType:"application",outputs:Object.keys(d).length>0?d:void 0,durationMs:Date.now()-n})}function Y(t){return{onBuildStart:o=>{t.onOpenNextBuildStart?.(),t.onLog?.(`${o} build started`,"info")},onBuildProgress:(o,s)=>{t.onOpenNextProgress?.(s)},onBuildComplete:o=>{t.onOpenNextBuildComplete?.(),t.onLog?.(`${o} build complete`,"info")},onBuildError:(o,s)=>{t.onOpenNextBuildError?.(s)}}}export{Y as createBuildCallbacks,K as deployAllStacks,G as deployParallelPhase,U as deployStackSequential,q as getParallelPhase2Stacks,$ as runDockerPreCompute};
|
|
@@ -1,131 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { logger } from "@fjall/util/logger";
|
|
3
|
-
import { getApplicationDestroyOrder, getApplicationStepName, getApplicationStepId } from "../types/operations.js";
|
|
4
|
-
import { stubCallerIdentity } from "../types/deployment/index.js";
|
|
5
|
-
import { deriveResourcesFromManifestStacks } from "../types/patternDetection.js";
|
|
6
|
-
import { CdkContextBuilder } from "../services/supporting/CdkContextBuilder.js";
|
|
7
|
-
import { buildParamsContext } from "./contextHelpers.js";
|
|
8
|
-
import { deleteStateFile, readStateFile } from "../types/FjallState.js";
|
|
9
|
-
/**
|
|
10
|
-
* Core application destruction orchestration.
|
|
11
|
-
*
|
|
12
|
-
* Detects application pattern, determines destroy order (reverse of deploy),
|
|
13
|
-
* then destroys stacks sequentially or in parallel groups. Delegates all
|
|
14
|
-
* CDK operations to the ApplicationStackService.destroyAllStacks() method,
|
|
15
|
-
* which handles "stack doesn't exist" → success conversion and parallel
|
|
16
|
-
* phase orchestration for OpenNext patterns.
|
|
17
|
-
*/
|
|
18
|
-
export async function destroyApplication(params, services, operation) {
|
|
19
|
-
const { callbacks } = params;
|
|
20
|
-
const startTime = Date.now();
|
|
21
|
-
// 1. Detect pattern for correct destroy order
|
|
22
|
-
const resolved = services.frameworkRegistry.resolve({
|
|
23
|
-
appPath: operation.path
|
|
24
|
-
});
|
|
25
|
-
const pattern = resolved?.detection.pattern ?? null;
|
|
26
|
-
// Derive resources from state file (if available from prior deploy)
|
|
27
|
-
let destroyResources;
|
|
28
|
-
try {
|
|
29
|
-
const stateFile = await readStateFile(operation.path);
|
|
30
|
-
if (stateFile !== null) {
|
|
31
|
-
const stackNames = Object.keys(stateFile.templateHashes);
|
|
32
|
-
if (stackNames.length > 0) {
|
|
33
|
-
destroyResources = deriveResourcesFromManifestStacks(stackNames);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
logger.debug("applicationDestroy", "Could not read state file for resource detection", { error: err instanceof Error ? err.message : String(err) });
|
|
39
|
-
callbacks.onLog?.("Could not read state file for resource detection — falling back to pattern detection", "warn");
|
|
40
|
-
}
|
|
41
|
-
// 2. Build deployment context
|
|
42
|
-
const context = CdkContextBuilder.buildDeploymentContext({
|
|
43
|
-
deployType: "application",
|
|
44
|
-
target: operation.appName,
|
|
45
|
-
path: operation.path,
|
|
46
|
-
region: services.awsProvider.getRegion(),
|
|
47
|
-
callerIdentity: stubCallerIdentity(services.awsProvider.getAccountId()),
|
|
48
|
-
...buildParamsContext({
|
|
49
|
-
orgConfig: params.orgConfig,
|
|
50
|
-
identity: params.identity
|
|
51
|
-
})
|
|
52
|
-
}, {
|
|
53
|
-
verbose: params.options?.verbose
|
|
54
|
-
}, params.orgConfig);
|
|
55
|
-
// 3. Determine destroy order
|
|
56
|
-
const destroyOrder = getApplicationDestroyOrder({
|
|
57
|
-
pattern,
|
|
58
|
-
resources: destroyResources
|
|
59
|
-
});
|
|
60
|
-
const totalSteps = destroyOrder.length;
|
|
61
|
-
callbacks.onLog?.(`Destroying ${operation.appName} (${totalSteps} stacks, ${pattern ?? "standard"} pattern)`, "info");
|
|
62
|
-
// 4. Destroy stacks via ApplicationStackService
|
|
63
|
-
const stacksDestroyed = [];
|
|
64
|
-
const skippedStacks = [];
|
|
65
|
-
const destroyResult = await services.stackService.destroyAllStacks(context, {
|
|
66
|
-
onOutput: (chunk) => {
|
|
67
|
-
callbacks.onOutput?.(chunk);
|
|
68
|
-
},
|
|
69
|
-
onResourceProgress: (event, stackId) => {
|
|
70
|
-
callbacks.onResourceProgress?.(event);
|
|
71
|
-
if (stackId) {
|
|
72
|
-
callbacks.onParallelStackResourceProgress?.(stackId, event);
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
onStackStart: (stackType, _stackName) => {
|
|
76
|
-
const stepId = getApplicationStepId(stackType, "destroy");
|
|
77
|
-
const stepName = getApplicationStepName(stackType, "destroy");
|
|
78
|
-
const stepIndex = destroyOrder.indexOf(stackType);
|
|
79
|
-
callbacks.onStepStart?.(stepId, stepName, stepIndex, totalSteps);
|
|
80
|
-
},
|
|
81
|
-
onStackComplete: async (stackType, result) => {
|
|
82
|
-
const stepId = getApplicationStepId(stackType, "destroy");
|
|
83
|
-
const stepName = getApplicationStepName(stackType, "destroy");
|
|
84
|
-
const stepIndex = destroyOrder.indexOf(stackType);
|
|
85
|
-
if (result.success) {
|
|
86
|
-
if (result.data?.skipped) {
|
|
87
|
-
skippedStacks.push(result.data.stackName || stackType);
|
|
88
|
-
callbacks.onStepComplete?.(stepId, stepName, "skipped", stepIndex, totalSteps);
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
stacksDestroyed.push(result.data?.stackName || stackType);
|
|
92
|
-
callbacks.onStepComplete?.(stepId, stepName, "completed", stepIndex, totalSteps);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
callbacks.onStepComplete?.(stepId, stepName, "error", stepIndex, totalSteps);
|
|
97
|
-
callbacks.onError?.(result.error);
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
onParallelPhaseStart: (stacks, description) => {
|
|
101
|
-
callbacks.onLog?.(`Parallel phase: ${description}`, "info");
|
|
102
|
-
callbacks.onParallelPhaseStart?.(stacks, description);
|
|
103
|
-
},
|
|
104
|
-
onParallelPhaseComplete: (results) => {
|
|
105
|
-
const failed = results.filter((r) => !r.success);
|
|
106
|
-
if (failed.length > 0) {
|
|
107
|
-
callbacks.onLog?.(`Parallel phase completed with ${failed.length} failure(s)`, "warn");
|
|
108
|
-
}
|
|
109
|
-
callbacks.onParallelPhaseComplete?.(results);
|
|
110
|
-
}
|
|
111
|
-
}, destroyResources);
|
|
112
|
-
if (!destroyResult.success) {
|
|
113
|
-
callbacks.onError?.(destroyResult.error);
|
|
114
|
-
return failure(destroyResult.error);
|
|
115
|
-
}
|
|
116
|
-
// 5. Clean up state file after successful destroy
|
|
117
|
-
try {
|
|
118
|
-
await deleteStateFile(operation.path);
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
logger.debug("applicationDestroy", "Failed to delete state file (non-critical)", { error: err instanceof Error ? err.message : String(err) });
|
|
122
|
-
callbacks.onLog?.("Failed to delete state file (non-critical)", "warn");
|
|
123
|
-
}
|
|
124
|
-
return success({
|
|
125
|
-
target: operation.appName,
|
|
126
|
-
deploymentType: "application",
|
|
127
|
-
stacksDestroyed,
|
|
128
|
-
skippedStacks,
|
|
129
|
-
durationMs: Date.now() - startTime
|
|
130
|
-
});
|
|
131
|
-
}
|
|
1
|
+
import{success as k,failure as w}from"@fjall/generator";import{logger as y}from"@fjall/util/logger";import{getApplicationDestroyOrder as b,getApplicationStepName as h,getApplicationStepId as S}from"../types/operations.js";import{stubCallerIdentity as N}from"../types/deployment/index.js";import{deriveResourcesFromManifestStacks as x}from"../types/patternDetection.js";import{CdkContextBuilder as D}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as R}from"./contextHelpers.js";import{deleteStateFile as I,readStateFile as O}from"../types/FjallState.js";async function H(n,c,r){const{callbacks:o}=n,P=Date.now(),f=c.frameworkRegistry.resolve({appPath:r.path})?.detection.pattern??null;let p;try{const e=await O(r.path);if(e!==null){const t=Object.keys(e.templateHashes);t.length>0&&(p=x(t))}}catch(e){y.debug("applicationDestroy","Could not read state file for resource detection",{error:e instanceof Error?e.message:String(e)}),o.onLog?.("Could not read state file for resource detection \u2014 falling back to pattern detection","warn")}const C=D.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:c.awsProvider.getRegion(),callerIdentity:N(c.awsProvider.getAccountId()),...R({orgConfig:n.orgConfig,identity:n.identity})},{verbose:n.options?.verbose},n.orgConfig),d=b({pattern:f,resources:p}),s=d.length;o.onLog?.(`Destroying ${r.appName} (${s} stacks, ${f??"standard"} pattern)`,"info");const g=[],u=[],m=await c.stackService.destroyAllStacks(C,{onOutput:e=>{o.onOutput?.(e)},onResourceProgress:(e,t)=>{o.onResourceProgress?.(e),t&&o.onParallelStackResourceProgress?.(t,e)},onStackStart:(e,t)=>{const a=S(e,"destroy"),l=h(e,"destroy"),i=d.indexOf(e);o.onStepStart?.(a,l,i,s)},onStackComplete:async(e,t)=>{const a=S(e,"destroy"),l=h(e,"destroy"),i=d.indexOf(e);t.success?t.data?.skipped?(u.push(t.data.stackName||e),o.onStepComplete?.(a,l,"skipped",i,s)):(g.push(t.data?.stackName||e),o.onStepComplete?.(a,l,"completed",i,s)):(o.onStepComplete?.(a,l,"error",i,s),o.onError?.(t.error))},onParallelPhaseStart:(e,t)=>{o.onLog?.(`Parallel phase: ${t}`,"info"),o.onParallelPhaseStart?.(e,t)},onParallelPhaseComplete:e=>{const t=e.filter(a=>!a.success);t.length>0&&o.onLog?.(`Parallel phase completed with ${t.length} failure(s)`,"warn"),o.onParallelPhaseComplete?.(e)}},p);if(!m.success)return o.onError?.(m.error),w(m.error);try{await I(r.path)}catch(e){y.debug("applicationDestroy","Failed to delete state file (non-critical)",{error:e instanceof Error?e.message:String(e)}),o.onLog?.("Failed to delete state file (non-critical)","warn")}return k({target:r.appName,deploymentType:"application",stacksDestroyed:g,skippedStacks:u,durationMs:Date.now()-P})}export{H as destroyApplication};
|
|
@@ -1,98 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* DockerBuilder — FrameworkBuilder implementation for standard Docker apps.
|
|
3
|
-
*
|
|
4
|
-
* Fallback builder (priority 100) that detects any Fjall app with an
|
|
5
|
-
* infrastructure.ts file. Docker builds are NOT handled here — they are
|
|
6
|
-
* handled by DockerProvider in the orchestration layer. This builder only
|
|
7
|
-
* produces the deployment plan and resource detection.
|
|
8
|
-
*/
|
|
9
|
-
import { existsSync, readFileSync } from "fs";
|
|
10
|
-
import { join } from "path";
|
|
11
|
-
import { success } from "@fjall/generator";
|
|
12
|
-
import { logger } from "@fjall/util/logger";
|
|
13
|
-
import { getErrorMessage } from "@fjall/util";
|
|
14
|
-
import { hasDockerfile } from "../../util/dockerfileDetection.js";
|
|
15
|
-
import { INFRASTRUCTURE_FILENAME } from "../../types/constants.js";
|
|
16
|
-
import { getApplicationDeployOrder, getApplicationDestroyOrder } from "../../types/operations.js";
|
|
17
|
-
/** Default resources when infrastructure.ts cannot be parsed */
|
|
18
|
-
const DEFAULT_RESOURCES = {
|
|
19
|
-
hasNetwork: true,
|
|
20
|
-
hasCompute: true,
|
|
21
|
-
hasDatabase: false,
|
|
22
|
-
hasStorage: false,
|
|
23
|
-
hasMessaging: false,
|
|
24
|
-
hasCdn: false
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Read infrastructure.ts and derive resource flags from its content.
|
|
28
|
-
*/
|
|
29
|
-
function detectResources(appPath) {
|
|
30
|
-
try {
|
|
31
|
-
const filePath = join(appPath, INFRASTRUCTURE_FILENAME);
|
|
32
|
-
if (!existsSync(filePath))
|
|
33
|
-
return { exists: false, hasDatabase: false };
|
|
34
|
-
const content = readFileSync(filePath, "utf-8");
|
|
35
|
-
const hasDatabase = content.includes("DatabaseFactory.build(");
|
|
36
|
-
return { exists: true, hasDatabase };
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
logger.debug("dockerBuilder", "Failed to read infrastructure file", {
|
|
40
|
-
appPath,
|
|
41
|
-
error: getErrorMessage(error)
|
|
42
|
-
});
|
|
43
|
-
return { exists: false, hasDatabase: false };
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Docker FrameworkBuilder for standard containerised applications.
|
|
48
|
-
*
|
|
49
|
-
* Priority 100 — checked last as the fallback builder. Any Fjall app with
|
|
50
|
-
* an infrastructure.ts file is a valid Docker app unless a higher-priority
|
|
51
|
-
* builder (e.g. OpenNext) claims it first.
|
|
52
|
-
*/
|
|
53
|
-
export const dockerBuilder = {
|
|
54
|
-
name: "docker",
|
|
55
|
-
priority: 100,
|
|
56
|
-
detect(context) {
|
|
57
|
-
const { exists, hasDatabase } = detectResources(context.appPath);
|
|
58
|
-
if (!exists) {
|
|
59
|
-
return {
|
|
60
|
-
detected: false,
|
|
61
|
-
pattern: null,
|
|
62
|
-
reason: "No infrastructure.ts found",
|
|
63
|
-
resources: DEFAULT_RESOURCES,
|
|
64
|
-
hasDatabase: false,
|
|
65
|
-
hasDockerfile: false
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
const dockerfile = hasDockerfile(context.appPath);
|
|
69
|
-
return {
|
|
70
|
-
detected: true,
|
|
71
|
-
pattern: null,
|
|
72
|
-
reason: dockerfile
|
|
73
|
-
? "infrastructure.ts found with Dockerfile"
|
|
74
|
-
: "infrastructure.ts found (no Dockerfile)",
|
|
75
|
-
resources: { ...DEFAULT_RESOURCES, hasDatabase },
|
|
76
|
-
hasDatabase,
|
|
77
|
-
hasDockerfile: dockerfile
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
plan(_context, detection) {
|
|
81
|
-
const resources = detection.resources;
|
|
82
|
-
return {
|
|
83
|
-
builderName: "docker",
|
|
84
|
-
deployOrder: getApplicationDeployOrder({ resources }),
|
|
85
|
-
destroyOrder: getApplicationDestroyOrder({ resources }),
|
|
86
|
-
parallelDestroy: false,
|
|
87
|
-
preBuildCommands: [],
|
|
88
|
-
buildCommand: null,
|
|
89
|
-
requiresDockerBuild: detection.hasDockerfile,
|
|
90
|
-
resources
|
|
91
|
-
};
|
|
92
|
-
},
|
|
93
|
-
async build(_appPath, _plan, _callbacks, _options) {
|
|
94
|
-
// Docker builds are handled by DockerProvider in the orchestration layer,
|
|
95
|
-
// not by the builder. This is the correct separation of concerns.
|
|
96
|
-
return success(undefined);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
1
|
+
import{existsSync as o,readFileSync as n}from"fs";import{join as i}from"path";import{success as l}from"@fjall/generator";import{logger as c}from"@fjall/util/logger";import{getErrorMessage as u}from"@fjall/util";import{hasDockerfile as f}from"../../util/dockerfileDetection.js";import{INFRASTRUCTURE_FILENAME as d}from"../../types/constants.js";import{getApplicationDeployOrder as p,getApplicationDestroyOrder as m}from"../../types/operations.js";const a={hasNetwork:!0,hasCompute:!0,hasDatabase:!1,hasStorage:!1,hasMessaging:!1,hasCdn:!1};function h(s){try{const e=i(s,d);return o(e)?{exists:!0,hasDatabase:n(e,"utf-8").includes("DatabaseFactory.build(")}:{exists:!1,hasDatabase:!1}}catch(e){return c.debug("dockerBuilder","Failed to read infrastructure file",{appPath:s,error:u(e)}),{exists:!1,hasDatabase:!1}}}const C={name:"docker",priority:100,detect(s){const{exists:e,hasDatabase:r}=h(s.appPath);if(!e)return{detected:!1,pattern:null,reason:"No infrastructure.ts found",resources:a,hasDatabase:!1,hasDockerfile:!1};const t=f(s.appPath);return{detected:!0,pattern:null,reason:t?"infrastructure.ts found with Dockerfile":"infrastructure.ts found (no Dockerfile)",resources:{...a,hasDatabase:r},hasDatabase:r,hasDockerfile:t}},plan(s,e){const r=e.resources;return{builderName:"docker",deployOrder:p({resources:r}),destroyOrder:m({resources:r}),parallelDestroy:!1,preBuildCommands:[],buildCommand:null,requiresDockerBuild:e.hasDockerfile,resources:r}},async build(s,e,r,t){return l(void 0)}};export{C as dockerBuilder};
|
|
@@ -1,144 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* OpenNextBuilder — FrameworkBuilder implementation for Next.js and Payload apps.
|
|
3
|
-
*
|
|
4
|
-
* Detects OpenNext patterns from infrastructure.ts, produces a build plan with
|
|
5
|
-
* the correct stack ordering, and delegates build execution to `runOpenNextBuild()`.
|
|
6
|
-
*
|
|
7
|
-
* BUILD ORDERING NOTE: The OpenNext build runs BEFORE the detection pipeline
|
|
8
|
-
* because CDK synth needs the `.open-next/` directory to exist. This builder's
|
|
9
|
-
* `build()` method is called early in the deployment flow, not after detection.
|
|
10
|
-
* The plan's `buildCommand` is declarative — actual execution goes through
|
|
11
|
-
* `runOpenNextBuild()` which handles binary resolution, timeouts, and cleanup.
|
|
12
|
-
*/
|
|
13
|
-
import { basename } from "path";
|
|
14
|
-
import { success } from "@fjall/generator";
|
|
15
|
-
import { logger } from "@fjall/util/logger";
|
|
16
|
-
import { OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER } from "../../types/operations.js";
|
|
17
|
-
import { readInfrastructureContent } from "../../types/patternDetection.js";
|
|
18
|
-
import { runOpenNextBuild, BUILD_TIMEOUT_MS, IMPORT_MAP_TIMEOUT_MS } from "../openNextBuild.js";
|
|
19
|
-
/** Resource flags for all OpenNext apps (hasDatabase is dynamic) */
|
|
20
|
-
function openNextResources(hasDatabase) {
|
|
21
|
-
return {
|
|
22
|
-
hasNetwork: true,
|
|
23
|
-
hasCompute: true,
|
|
24
|
-
hasDatabase,
|
|
25
|
-
hasStorage: true,
|
|
26
|
-
hasMessaging: true,
|
|
27
|
-
hasCdn: true
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* OpenNext FrameworkBuilder for Next.js and Payload applications.
|
|
32
|
-
*
|
|
33
|
-
* Priority 10 — checked before Docker (priority 50) since OpenNext is more
|
|
34
|
-
* specific. Apps matching an OpenNext pattern use Lambda-based deployment
|
|
35
|
-
* with S3, DynamoDB, SQS, and CloudFront stacks.
|
|
36
|
-
*/
|
|
37
|
-
export const openNextBuilder = {
|
|
38
|
-
name: "opennext",
|
|
39
|
-
priority: 10,
|
|
40
|
-
detect(context) {
|
|
41
|
-
const notDetected = {
|
|
42
|
-
detected: false,
|
|
43
|
-
pattern: null,
|
|
44
|
-
reason: "No OpenNext pattern found",
|
|
45
|
-
resources: openNextResources(false),
|
|
46
|
-
hasDatabase: false,
|
|
47
|
-
hasDockerfile: false
|
|
48
|
-
};
|
|
49
|
-
const content = readInfrastructureContent(context.appPath);
|
|
50
|
-
if (!content) {
|
|
51
|
-
return {
|
|
52
|
-
...notDetected,
|
|
53
|
-
reason: "No infrastructure.ts found"
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
if (!content.includes("PatternFactory")) {
|
|
57
|
-
return {
|
|
58
|
-
...notDetected,
|
|
59
|
-
reason: "No PatternFactory reference in infrastructure.ts"
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
if (content.includes('type: "payload"')) {
|
|
63
|
-
return {
|
|
64
|
-
detected: true,
|
|
65
|
-
pattern: "payload",
|
|
66
|
-
reason: 'PatternFactory with type: "payload" detected',
|
|
67
|
-
resources: openNextResources(true),
|
|
68
|
-
hasDatabase: true,
|
|
69
|
-
hasDockerfile: false
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
if (content.includes('type: "nextjs"')) {
|
|
73
|
-
// Next.js database detection is dynamic — check for explicit database usage
|
|
74
|
-
const hasDatabase = content.includes("DatabaseFactory.build(") ||
|
|
75
|
-
content.includes("database:");
|
|
76
|
-
return {
|
|
77
|
-
detected: true,
|
|
78
|
-
pattern: "nextjs",
|
|
79
|
-
reason: 'PatternFactory with type: "nextjs" detected',
|
|
80
|
-
resources: openNextResources(hasDatabase),
|
|
81
|
-
hasDatabase,
|
|
82
|
-
hasDockerfile: false
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
...notDetected,
|
|
87
|
-
reason: "PatternFactory found but no recognised pattern type"
|
|
88
|
-
};
|
|
89
|
-
},
|
|
90
|
-
plan(_context, detection) {
|
|
91
|
-
const preBuildCommands = detection.pattern === "payload"
|
|
92
|
-
? [
|
|
93
|
-
{
|
|
94
|
-
name: "payload-import-map",
|
|
95
|
-
binary: "npx",
|
|
96
|
-
args: ["payload", "generate:importmap"],
|
|
97
|
-
timeoutMs: IMPORT_MAP_TIMEOUT_MS
|
|
98
|
-
}
|
|
99
|
-
]
|
|
100
|
-
: [];
|
|
101
|
-
return {
|
|
102
|
-
builderName: "opennext",
|
|
103
|
-
deployOrder: OPENNEXT_DEPLOY_ORDER,
|
|
104
|
-
destroyOrder: OPENNEXT_DESTROY_ORDER,
|
|
105
|
-
parallelDestroy: true,
|
|
106
|
-
preBuildCommands,
|
|
107
|
-
buildCommand: {
|
|
108
|
-
name: "open-next-build",
|
|
109
|
-
binary: "open-next",
|
|
110
|
-
args: ["build"],
|
|
111
|
-
timeoutMs: BUILD_TIMEOUT_MS
|
|
112
|
-
},
|
|
113
|
-
requiresDockerBuild: false,
|
|
114
|
-
resources: detection.resources
|
|
115
|
-
};
|
|
116
|
-
},
|
|
117
|
-
async build(appPath, plan, callbacks, options) {
|
|
118
|
-
if (options?.skipBuild || options?.infraOnly) {
|
|
119
|
-
logger.debug("openNextBuilder", "Build skipped", {
|
|
120
|
-
skipBuild: options.skipBuild,
|
|
121
|
-
infraOnly: options.infraOnly
|
|
122
|
-
});
|
|
123
|
-
return success(undefined);
|
|
124
|
-
}
|
|
125
|
-
// Delegate to existing runOpenNextBuild which handles binary resolution,
|
|
126
|
-
// import map generation, timeouts, stream cleanup, and artefact validation.
|
|
127
|
-
// We build an ApplicationOperation and map BuildCallbacks to DeployCallbacks.
|
|
128
|
-
const operation = {
|
|
129
|
-
kind: "application",
|
|
130
|
-
appName: basename(appPath) || "app",
|
|
131
|
-
path: appPath
|
|
132
|
-
};
|
|
133
|
-
// Determine pattern from the plan's pre-build commands
|
|
134
|
-
const isPayload = plan.preBuildCommands.some((cmd) => cmd.name === "payload-import-map");
|
|
135
|
-
const pattern = isPayload ? "payload" : "nextjs";
|
|
136
|
-
const result = await runOpenNextBuild(operation, pattern, {
|
|
137
|
-
onOpenNextBuildStart: () => callbacks.onBuildStart?.("opennext"),
|
|
138
|
-
onOpenNextProgress: (message) => callbacks.onBuildProgress?.("opennext", message),
|
|
139
|
-
onOpenNextBuildComplete: () => callbacks.onBuildComplete?.("opennext"),
|
|
140
|
-
onOpenNextBuildError: (error) => callbacks.onBuildError?.("opennext", error)
|
|
141
|
-
});
|
|
142
|
-
return result;
|
|
143
|
-
}
|
|
144
|
-
};
|
|
1
|
+
import{basename as i}from"path";import{success as p}from"@fjall/generator";import{logger as d}from"@fjall/util/logger";import{OPENNEXT_DEPLOY_ORDER as l,OPENNEXT_DESTROY_ORDER as c}from"../../types/operations.js";import{readInfrastructureContent as m}from"../../types/patternDetection.js";import{runOpenNextBuild as f,BUILD_TIMEOUT_MS as y,IMPORT_MAP_TIMEOUT_MS as x}from"../openNextBuild.js";function o(r){return{hasNetwork:!0,hasCompute:!0,hasDatabase:r,hasStorage:!0,hasMessaging:!0,hasCdn:!0}}const E={name:"opennext",priority:10,detect(r){const t={detected:!1,pattern:null,reason:"No OpenNext pattern found",resources:o(!1),hasDatabase:!1,hasDockerfile:!1},e=m(r.appPath);if(!e)return{...t,reason:"No infrastructure.ts found"};if(!e.includes("PatternFactory"))return{...t,reason:"No PatternFactory reference in infrastructure.ts"};if(e.includes('type: "payload"'))return{detected:!0,pattern:"payload",reason:'PatternFactory with type: "payload" detected',resources:o(!0),hasDatabase:!0,hasDockerfile:!1};if(e.includes('type: "nextjs"')){const n=e.includes("DatabaseFactory.build(")||e.includes("database:");return{detected:!0,pattern:"nextjs",reason:'PatternFactory with type: "nextjs" detected',resources:o(n),hasDatabase:n,hasDockerfile:!1}}return{...t,reason:"PatternFactory found but no recognised pattern type"}},plan(r,t){const e=t.pattern==="payload"?[{name:"payload-import-map",binary:"npx",args:["payload","generate:importmap"],timeoutMs:x}]:[];return{builderName:"opennext",deployOrder:l,destroyOrder:c,parallelDestroy:!0,preBuildCommands:e,buildCommand:{name:"open-next-build",binary:"open-next",args:["build"],timeoutMs:y},requiresDockerBuild:!1,resources:t.resources}},async build(r,t,e,n){if(n?.skipBuild||n?.infraOnly)return d.debug("openNextBuilder","Build skipped",{skipBuild:n.skipBuild,infraOnly:n.infraOnly}),p(void 0);const s={kind:"application",appName:i(r)||"app",path:r},u=t.preBuildCommands.some(a=>a.name==="payload-import-map")?"payload":"nextjs";return await f(s,u,{onOpenNextBuildStart:()=>e.onBuildStart?.("opennext"),onOpenNextProgress:a=>e.onBuildProgress?.("opennext",a),onOpenNextBuildComplete:()=>e.onBuildComplete?.("opennext"),onOpenNextBuildError:a=>e.onBuildError?.("opennext",a)})}};export{E as openNextBuilder};
|