@everystack/cli 0.2.14 → 0.2.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everystack/cli",
3
- "version": "0.2.14",
3
+ "version": "0.2.17",
4
4
  "description": "CLI and OTA updates for Expo apps on everystack",
5
5
  "license": "AGPL-3.0-only",
6
6
  "publishConfig": {
@@ -9,8 +9,8 @@ import { resolveConfig, type CliConfig } from '../config.js';
9
9
  import { uploadToS3, invokeAction } from '../aws.js';
10
10
  import { step, success, warn, fail, info } from '../output.js';
11
11
  import { exportApp } from '../utils/export.js';
12
- import { loadSstSecrets } from '../utils/secrets.js';
13
12
  import { walkDirectory } from '../utils/walk.js';
13
+ import { loadSstSecrets } from '../utils/secrets.js';
14
14
 
15
15
  export interface UpdateFlags {
16
16
  channel?: string;
@@ -60,47 +60,27 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
60
60
  // Stage may not be deployed yet — HOST_URL falls back to loadSstOutputs
61
61
  }
62
62
 
63
- // Load EXPO_PUBLIC_* secrets for this stage and inject into process.env.
64
- // Only public secrets are injectedserver secrets (DATABASE_URL, JWT_SECRET, etc.)
65
- // are provided by SST Resource linking at Lambda runtime.
66
- // app.config.js merges { ...easInfo, ...process.env }, so these win over .env.local.
63
+ // Inject SST secrets into process.env so app.config.js / Metro can read them.
64
+ // The CLI injects ALL secretsthe app's deny-by-default filter (e.g.
65
+ // expo-secrets.config.js) is the security boundary that gates what reaches
66
+ // the bundle. This avoids requiring `sst shell` in deploy scripts.
67
67
  try {
68
- step('Loading SST secrets...');
69
68
  const { parseAppName } = await import('../discover.js');
70
69
  const appName = await parseAppName();
71
- const region = resolvedConfig?.region
72
- || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
70
+ const region = resolvedConfig?.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
71
+ step('Loading SST secrets...');
73
72
  const secrets = await loadSstSecrets({ appName, stage: flags.stage, region });
74
- const allNames = Object.keys(secrets);
75
- const publicSecrets = Object.entries(secrets).filter(([k]) => k.startsWith('EXPO_PUBLIC_'));
76
- const skipped = allNames.length - publicSecrets.length;
77
- if (publicSecrets.length > 0) {
78
- for (const [key, value] of publicSecrets) {
79
- process.env[key] ||= value;
73
+ let injected = 0;
74
+ for (const [k, v] of Object.entries(secrets)) {
75
+ if (!process.env[k]) {
76
+ process.env[k] = v;
77
+ injected++;
80
78
  }
81
- success(`Injected ${publicSecrets.length} EXPO_PUBLIC_* secrets: ${publicSecrets.map(([k]) => k).join(', ')}`);
82
- } else if (allNames.length > 0) {
83
- info('No EXPO_PUBLIC_* secrets found (server secrets are provided by SST at runtime)');
84
- } else {
85
- info('No SST secrets found for this stage');
86
- }
87
- if (skipped > 0) {
88
- info(`Skipped ${skipped} server secret(s) — provided by SST Resource linking at Lambda runtime`);
89
79
  }
80
+ success(`${injected} secret(s) injected into env (${Object.keys(secrets).length} total)`);
90
81
  } catch (err: any) {
91
- // Secret loading is best-effort — the user may not have permissions
92
- // or may not use SST secrets (e.g. env vars set manually).
93
- const msg = err.message || String(err);
94
- if (msg.includes("Cannot find package '@aws-sdk/client-ssm'")) {
95
- warn('Secrets skipped: @aws-sdk/client-ssm not installed');
96
- info('Install with: pnpm add -D @aws-sdk/client-ssm');
97
- } else if (msg.includes("Cannot find package '@aws-sdk/client-s3'")) {
98
- warn('Secrets skipped: @aws-sdk/client-s3 not installed');
99
- info('Install with: pnpm add -D @aws-sdk/client-s3');
100
- } else {
101
- warn(`Could not load SST secrets: ${msg}`);
102
- }
103
- info('Set secrets with: everystack secrets set KEY "value" --stage ' + flags.stage);
82
+ warn(`Could not load SST secrets: ${err.message}`);
83
+ info('Secrets may be unavailable during export. Provide env vars from the caller if needed.');
104
84
  }
105
85
  }
106
86
 
@@ -114,21 +94,6 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
114
94
  const appConfig = await loadAppConfig();
115
95
  const runtimeVersion = appConfig.runtimeVersion;
116
96
 
117
- // Validate: SSR flags in the expo-router plugin config will crash native devices.
118
- // The bundle is compiled with server rendering code paths that have no server at runtime.
119
- const ssrFlags = getExpoRouterSsrFlags(appConfig);
120
- if (ssrFlags.useServerRendering || ssrFlags.useServerDataLoaders) {
121
- fail('Export contains server rendering flags that will crash native devices.');
122
- info('expo-router plugin has unstable_useServerRendering or unstable_useServerDataLoaders enabled.');
123
- info('Use SSR_ENABLED to opt in only for web server exports:');
124
- info('');
125
- info(" ['expo-router', {");
126
- info(' unstable_useServerRendering: !!process.env.SSR_ENABLED,');
127
- info(' unstable_useServerDataLoaders: !!process.env.SSR_ENABLED,');
128
- info(' }]');
129
- process.exit(1);
130
- }
131
-
132
97
  if (!runtimeVersion) {
133
98
  fail('No runtimeVersion found in app.json/app.config.js');
134
99
  info('Add "runtimeVersion" to your app.json, e.g.:');
@@ -141,6 +106,23 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
141
106
  const distDir = path.resolve('dist');
142
107
  const needsMobile = platformFilter !== 'web';
143
108
 
109
+ // Validate: SSR flags in the expo-router plugin config will crash native devices.
110
+ // Only check when mobile platforms are involved — web-only updates want SSR enabled.
111
+ if (needsMobile) {
112
+ const ssrFlags = getExpoRouterSsrFlags(appConfig);
113
+ if (ssrFlags.useServerRendering || ssrFlags.useServerDataLoaders) {
114
+ fail('Export contains server rendering flags that will crash native devices.');
115
+ info('expo-router plugin has unstable_useServerRendering or unstable_useServerDataLoaders enabled.');
116
+ info('Use SSR_ENABLED to opt in only for web server exports:');
117
+ info('');
118
+ info(" ['expo-router', {");
119
+ info(' unstable_useServerRendering: !!process.env.SSR_ENABLED,');
120
+ info(' unstable_useServerDataLoaders: !!process.env.SSR_ENABLED,');
121
+ info(' }]');
122
+ process.exit(1);
123
+ }
124
+ }
125
+
144
126
  // First export: mobile-safe bundles (no SSR_ENABLED).
145
127
  // Always re-export — stale dist/ is the #1 cause of broken OTA pushes.
146
128
  if (needsMobile) {
@@ -3,7 +3,7 @@ import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
 
5
5
  export function exportApp(platform?: string): Promise<void> {
6
- const args = ['expo', 'export', '--output-dir', 'dist'];
6
+ const args = ['expo', 'export', '--output-dir', 'dist', '--clear'];
7
7
  if (platform) args.push('--platform', platform);
8
8
 
9
9
  return new Promise((resolve, reject) => {