gencow 0.1.259 → 0.1.260

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.
@@ -47,6 +47,14 @@ export function resolveProdAppTarget({ explicitAppId = null, prodApp = null, acc
47
47
  return { appId: prodApp };
48
48
  }
49
49
 
50
+ // Single rule for the implicit prod environment: an explicit app id that is this project's
51
+ // prod app selects the prod environment even without --prod. Every command that picks a
52
+ // cloud app (cloud targets, deploy) must use this helper instead of repeating the condition.
53
+ export function resolveImplicitProdEnvironment({ explicitApp = false, envTarget = "dev", appId = null, prodApp = null }) {
54
+ if (explicitApp && envTarget === "dev" && prodApp && appId === prodApp) return "prod";
55
+ return envTarget;
56
+ }
57
+
50
58
  // Shared human message for a failed prod target; falls back to the call site's own text.
51
59
  export function describeProdTargetFailure(result, fallbackMessage) {
52
60
  if (result?.reason === "explicit_app_not_prod_app") {
@@ -81,10 +89,12 @@ export function resolveCloudAppTarget(restArgs, options = {}) {
81
89
  if (projectTargets.invalid) {
82
90
  return { error: "MIGRATION_PROJECT_CONFIG_INVALID" };
83
91
  }
84
- // The project's own prod app id names the prod environment even without --prod.
85
- if (explicitApp && envTarget === "dev" && projectTargets.prodApp && appId === projectTargets.prodApp) {
86
- envTarget = "prod";
87
- }
92
+ envTarget = resolveImplicitProdEnvironment({
93
+ explicitApp,
94
+ envTarget,
95
+ appId,
96
+ prodApp: projectTargets.prodApp,
97
+ });
88
98
  if (!appId) {
89
99
  appId = envTarget === "prod" ? projectTargets.prodApp : projectTargets.appId;
90
100
  }
@@ -17,22 +17,36 @@ import { createDbCheckCommand, createDbPushCommand } from "./db-push-command.mjs
17
17
  import { logEnvFileOperationWithType } from "./backend-env-resolver.mjs";
18
18
  import { runDrizzleGenerateWithFallback } from "./drizzle-generate.mjs";
19
19
  import { BOLD, CYAN, DIM, GREEN, RED, RESET, error, info, log, success, warn } from "./output.mjs";
20
- import { describeProdTargetFailure, resolveProdAppTarget } from "./cloud-targets.mjs";
20
+ import {
21
+ describeProdTargetFailure,
22
+ resolveImplicitProdEnvironment,
23
+ resolveProdAppTarget,
24
+ } from "./cloud-targets.mjs";
21
25
  import { platformFetch, requireCreds } from "./platform-client.mjs";
22
26
  import { readProjectMetadata } from "./project-context.mjs";
23
27
 
24
28
  export function resolveDbCloudTarget(args, gencowJson = null) {
25
- const isProd = args.includes("--prod");
26
29
  let explicitAppId = null;
30
+ let hasExplicitApp = false;
27
31
  let platformUrl = null;
28
32
 
29
33
  for (let i = 0; i < args.length; i++) {
30
34
  if (args[i] === "--app" || args[i] === "-a") {
31
35
  explicitAppId = args[i + 1] ?? null;
36
+ hasExplicitApp = true;
32
37
  i++;
33
38
  }
34
39
  }
35
40
 
41
+ // Same implicit prod rule as deploy and static: an --app naming the project's prodApp is prod even without --prod.
42
+ const isProd =
43
+ resolveImplicitProdEnvironment({
44
+ explicitApp: hasExplicitApp,
45
+ envTarget: args.includes("--prod") ? "prod" : "dev",
46
+ appId: explicitAppId,
47
+ prodApp: gencowJson?.prodApp || null,
48
+ }) === "prod";
49
+
36
50
  if (gencowJson) {
37
51
  platformUrl = gencowJson.platformUrl || null;
38
52
  if (isProd) {
@@ -0,0 +1,37 @@
1
+ import { unlinkSync } from "fs";
2
+
3
+ // Local-only backend source preparation for a first `deploy --prod`. Every step here
4
+ // reads the project on disk (capability check, migration generation, codegen, the
5
+ // packaging audits and the bundle with its round-trip check). It runs once, before the
6
+ // production app is created, so a rejection leaves no remote app behind.
7
+ export async function prepareBackendSourceForProdCreate(
8
+ runtime,
9
+ { forceDeploy = false, requestId = null } = {},
10
+ ) {
11
+ if ((await runtime.preflightBackendCapability?.()) === false) return null;
12
+ const generatorVersion = await runtime.prepareDeployProjectSource?.({ existingBundleMode: false });
13
+ const packageResult = await runtime.packageDeployProject({
14
+ forceDeploy,
15
+ existingBundleMode: false,
16
+ requestId,
17
+ sourcePrepared: true,
18
+ preparedGeneratorVersion: generatorVersion,
19
+ });
20
+ if (!packageResult) return null;
21
+ return { runtime, generatorVersion, packageResult };
22
+ }
23
+
24
+ // Drops the temporary bundle file and codegen output of a prepared source that did not
25
+ // reach the backend deploy (which otherwise releases them itself). Safe to call twice.
26
+ export function releasePreparedBackendSource(prepared, { unlinkSyncImpl = unlinkSync } = {}) {
27
+ if (!prepared) return;
28
+ const tmpBundle = prepared.packageResult?.tmpBundle;
29
+ if (tmpBundle) {
30
+ try {
31
+ unlinkSyncImpl(tmpBundle);
32
+ } catch {
33
+ /* already removed */
34
+ }
35
+ }
36
+ prepared.runtime?.cleanupPreparedDeploySource?.();
37
+ }
@@ -19,6 +19,11 @@ import { detectStaticDeployBackend, preflightStaticDeployArtifact } from "./stat
19
19
  import { formatCommandError } from "./cli-command-runner.mjs";
20
20
  import { readJsonObjectResponse } from "./http-response-json.mjs";
21
21
  import { ensureProductionDeployTarget } from "./deploy-prod-target.mjs";
22
+ import {
23
+ prepareBackendSourceForProdCreate,
24
+ releasePreparedBackendSource,
25
+ } from "./deploy-prepared-source.mjs";
26
+ import { resolveImplicitProdEnvironment } from "./cloud-targets.mjs";
22
27
 
23
28
  export function parseDeployArgs(deployArgs) {
24
29
  const parsed = {
@@ -183,14 +188,6 @@ function showUnknownDeployArgument(arg, { errorImpl = error, infoImpl = info, lo
183
188
  infoImpl(" gencow static [dir] Deploy static files only");
184
189
  }
185
190
 
186
- // Returns the prepared backend source, or null when the capability check rejected it
187
- // (the rejection is already rendered and exit is already requested).
188
- async function prepareBackendSourceForProdCreate(runtime) {
189
- if ((await runtime.preflightBackendCapability?.()) === false) return null;
190
- const generatorVersion = await runtime.prepareDeployProjectSource?.({ existingBundleMode: false });
191
- return { runtime, generatorVersion };
192
- }
193
-
194
191
  async function runRollbackDeploy({
195
192
  appId,
196
193
  envTarget,
@@ -432,6 +429,14 @@ export function createDeployCommand({
432
429
  resolvePathImpl,
433
430
  });
434
431
  let { appId, displayName, gencowJsonPath, prodAppId, projectDir } = context;
432
+ // Same environment rule as cloud-targets.mjs: --app <project prod app> deploys to prod even without --prod.
433
+ // Every branch below (static, rollback, existing-bundle, backend) reads this promoted environment.
434
+ parsed.envTarget = resolveImplicitProdEnvironment({
435
+ explicitApp: Boolean(parsed.appId),
436
+ envTarget: parsed.envTarget,
437
+ appId: parsed.appId,
438
+ prodApp: prodAppId,
439
+ });
435
440
  const rootAppId = appId;
436
441
  appId = resolveEnvironmentDeployTarget({ appId, envTarget: parsed.envTarget, prodAppId });
437
442
  const backendProjectRoot = projectDir ?? projectRoot;
@@ -524,14 +529,20 @@ export function createDeployCommand({
524
529
  region: parsed.region,
525
530
  prepareLocalSource: preparesBackendSource
526
531
  ? async () => {
527
- preparedSource = await prepareBackendSourceForProdCreate(backendDeployRuntime);
532
+ preparedSource = await prepareBackendSourceForProdCreate(backendDeployRuntime, {
533
+ forceDeploy: parsed.forceDeploy,
534
+ requestId: parsed.requestId,
535
+ });
528
536
  return preparedSource !== null;
529
537
  }
530
538
  : null,
531
539
  });
532
540
  } finally {
533
- // Prepared codegen output is temporary; drop it when no production app was created.
534
- if (!prodTarget) backendDeployRuntime.cleanupPreparedDeploySource?.();
541
+ // Prepared codegen output and bundle are temporary; drop them when no production app was created.
542
+ if (!prodTarget) {
543
+ backendDeployRuntime.cleanupPreparedDeploySource?.();
544
+ releasePreparedBackendSource(preparedSource, { unlinkSyncImpl });
545
+ }
535
546
  }
536
547
  if (!prodTarget) return;
537
548
  appId = prodTarget;
@@ -544,64 +555,69 @@ export function createDeployCommand({
544
555
  }
545
556
 
546
557
  if (parsed.staticDeploy) {
547
- await runStaticDeployFlow({
548
- appId,
549
- preparedSource,
550
- backendRoot: backendProjectRoot,
551
- creds,
552
- displayName,
553
- envTarget: parsed.envTarget,
554
- forceDeploy: parsed.forceDeploy,
555
- requestId: parsed.requestId,
556
- resumeStaticOnly: parsed.resumeRequested,
557
- gencowJsonPath,
558
- hasDetectedBackend,
559
- selectedClient,
560
- staticDirArg: resolvedStaticDirArg,
561
- staticArtifactPreflight,
562
- createDeployPackageRuntimeForCwd,
563
- cwdImpl,
564
- deployPackageRuntime: backendDeployRuntime,
565
- errorImpl,
566
- existsSyncImpl,
567
- exitImpl,
568
- infoImpl,
569
- logImpl,
570
- resolvePathImpl,
571
- runStaticDeployRuntimeImpl,
572
- unlinkSyncImpl,
573
- writeFileSyncImpl,
574
- prepareReleaseAttempt: prepareReleaseAttemptImpl
575
- ? ({ appId: activeAppId, sourceRoot }) =>
576
- prepareReleaseAttemptImpl({
577
- root: sourceRoot || backendProjectRoot,
578
- sourceRootKind: "fullstack",
579
- components: ["backend", "static"],
580
- cliVersion,
581
- creds,
582
- appId: activeAppId,
583
- environment: parsed.envTarget,
584
- requestId: parsed.requestId,
585
- platformFetchImpl,
586
- capabilityTimeoutMs: 15_000,
587
- allowCapabilityTimeoutFallback: true,
588
- warnImpl,
589
- })
590
- : undefined,
591
- failReleaseAttempt: failReleaseAttemptImpl
592
- ? ({ appId: activeAppId, attemptId, failureStage, failureCode }) =>
593
- failReleaseAttemptImpl({
594
- creds,
595
- appId: activeAppId,
596
- attemptId,
597
- environment: parsed.envTarget,
598
- failureStage,
599
- failureCode,
600
- platformFetchImpl,
601
- })
602
- : undefined,
603
- successImpl,
604
- });
558
+ try {
559
+ await runStaticDeployFlow({
560
+ appId,
561
+ preparedSource,
562
+ backendRoot: backendProjectRoot,
563
+ creds,
564
+ displayName,
565
+ envTarget: parsed.envTarget,
566
+ forceDeploy: parsed.forceDeploy,
567
+ requestId: parsed.requestId,
568
+ resumeStaticOnly: parsed.resumeRequested,
569
+ gencowJsonPath,
570
+ hasDetectedBackend,
571
+ selectedClient,
572
+ staticDirArg: resolvedStaticDirArg,
573
+ staticArtifactPreflight,
574
+ createDeployPackageRuntimeForCwd,
575
+ cwdImpl,
576
+ deployPackageRuntime: backendDeployRuntime,
577
+ errorImpl,
578
+ existsSyncImpl,
579
+ exitImpl,
580
+ infoImpl,
581
+ logImpl,
582
+ resolvePathImpl,
583
+ runStaticDeployRuntimeImpl,
584
+ unlinkSyncImpl,
585
+ writeFileSyncImpl,
586
+ prepareReleaseAttempt: prepareReleaseAttemptImpl
587
+ ? ({ appId: activeAppId, sourceRoot }) =>
588
+ prepareReleaseAttemptImpl({
589
+ root: sourceRoot || backendProjectRoot,
590
+ sourceRootKind: "fullstack",
591
+ components: ["backend", "static"],
592
+ cliVersion,
593
+ creds,
594
+ appId: activeAppId,
595
+ environment: parsed.envTarget,
596
+ requestId: parsed.requestId,
597
+ platformFetchImpl,
598
+ capabilityTimeoutMs: 15_000,
599
+ allowCapabilityTimeoutFallback: true,
600
+ warnImpl,
601
+ })
602
+ : undefined,
603
+ failReleaseAttempt: failReleaseAttemptImpl
604
+ ? ({ appId: activeAppId, attemptId, failureStage, failureCode }) =>
605
+ failReleaseAttemptImpl({
606
+ creds,
607
+ appId: activeAppId,
608
+ attemptId,
609
+ environment: parsed.envTarget,
610
+ failureStage,
611
+ failureCode,
612
+ platformFetchImpl,
613
+ })
614
+ : undefined,
615
+ successImpl,
616
+ });
617
+ } finally {
618
+ // The static flow can return before the backend deploy consumes the prepared source.
619
+ releasePreparedBackendSource(preparedSource, { unlinkSyncImpl });
620
+ }
605
621
  return 0;
606
622
  }
607
623
 
@@ -125,22 +125,25 @@ export async function deployBackendPackage({
125
125
  });
126
126
  if (!activeAppId) return null;
127
127
  } else {
128
- if (!preparedSource) {
128
+ if (preparedSource) {
129
+ // Local preparation and packaging already ran once, before the production create.
130
+ packageResult = preparedSource.packageResult;
131
+ } else {
129
132
  if ((await activePackageRuntime.preflightBackendCapability?.()) === false) return null;
130
- }
131
- const preparedGeneratorVersion = preparedSource
132
- ? preparedSource.generatorVersion
133
- : await activePackageRuntime.prepareDeployProjectSource?.({ existingBundleMode });
134
- packageResult = await activePackageRuntime.packageDeployProject({
135
- forceDeploy,
136
- existingBundleMode,
137
- requestId,
138
- sourcePrepared: true,
139
- preparedGeneratorVersion,
140
- });
141
- if (!packageResult) {
142
- await markAttemptFailed("package", "PACKAGE_FAILED");
143
- return null;
133
+ const preparedGeneratorVersion = await activePackageRuntime.prepareDeployProjectSource?.({
134
+ existingBundleMode,
135
+ });
136
+ packageResult = await activePackageRuntime.packageDeployProject({
137
+ forceDeploy,
138
+ existingBundleMode,
139
+ requestId,
140
+ sourcePrepared: true,
141
+ preparedGeneratorVersion,
142
+ });
143
+ if (!packageResult) {
144
+ await markAttemptFailed("package", "PACKAGE_FAILED");
145
+ return null;
146
+ }
144
147
  }
145
148
 
146
149
  activeAppId = await activePackageRuntime.createCloudAppIfNeeded({
@@ -3,7 +3,11 @@ import { isAbsolute, resolve } from "path";
3
3
 
4
4
  import { readProjectDisplayName } from "./cli-project-runtime.mjs";
5
5
  import { formatCommandError } from "./cli-command-runner.mjs";
6
- import { describeProdTargetFailure, resolveProdAppTarget } from "./cloud-targets.mjs";
6
+ import {
7
+ describeProdTargetFailure,
8
+ resolveImplicitProdEnvironment,
9
+ resolveProdAppTarget,
10
+ } from "./cloud-targets.mjs";
7
11
  import {
8
12
  getCliInvocationSelection,
9
13
  resolveProjectMetadataPath,
@@ -291,8 +295,14 @@ export function createStaticCommand(options = {}) {
291
295
  return;
292
296
  }
293
297
  }
294
- let deployTarget = parsed.isProd ? prodAppId || appId : appId;
295
- const envTarget = parsed.isProd ? "prod" : "dev";
298
+ // Same environment rule as deploy (cloud-targets.mjs): --app <project prod app> is prod even without --prod.
299
+ const envTarget = resolveImplicitProdEnvironment({
300
+ explicitApp: Boolean(parsed.appId),
301
+ envTarget: parsed.isProd ? "prod" : "dev",
302
+ appId: parsed.appId,
303
+ prodApp: prodAppId,
304
+ });
305
+ let deployTarget = envTarget === "prod" ? prodAppId || appId : appId;
296
306
 
297
307
  if (parsed.isProd && !prodAppId) {
298
308
  deployTarget = await ensureStaticProductionTarget({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.259",
3
+ "version": "0.1.260",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,10 +41,10 @@
41
41
  "better-auth": "1.7.11",
42
42
  "postgres": "^3.4.9",
43
43
  "@gencow/client": "0.2.8",
44
- "@gencow/shared": "0.0.0",
44
+ "@gencow/react": "0.2.8",
45
45
  "@gencow/core": "0.1.47",
46
- "@gencow/server": "0.1.3",
47
- "@gencow/react": "0.2.8"
46
+ "@gencow/shared": "0.0.0",
47
+ "@gencow/server": "0.1.3"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "node scripts/bundle-core.mjs && node scripts/bundle-shared.mjs && node scripts/bundle-server.mjs",