@everystack/cli 0.2.2 → 0.2.4

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.2",
3
+ "version": "0.2.4",
4
4
  "description": "CLI and OTA updates for Expo apps on everystack",
5
5
  "license": "AGPL-3.0-only",
6
6
  "publishConfig": {
@@ -20,6 +20,12 @@ export interface UpdateFlags {
20
20
  }
21
21
 
22
22
  export async function updateCommand(flags: UpdateFlags & Record<string, string>): Promise<void> {
23
+ // Signal publish context so app.config.js can detect it's not a dev session.
24
+ // Without this, dotenv guards that skip .env.local for EAS_BUILD/EAS_UPDATE
25
+ // don't know to skip for everystack update, causing local dev overrides
26
+ // (e.g. OUTBOUND_HOST=localhost → LAN IP) to leak into the manifest.
27
+ process.env.EVERYSTACK_UPDATE = '1';
28
+
23
29
  // Propagate --stage to ENVIRONMENT before app.config.js evaluation.
24
30
  // Without this, the manifest snapshots whatever the local shell has
25
31
  // (often unset → 'development'), baking wrong API URLs and feature
@@ -406,8 +412,17 @@ async function loadAppConfig(): Promise<any> {
406
412
  // Handle Expo's function export pattern: export default () => ({ ... })
407
413
  if (typeof resolved === 'function') resolved = resolved();
408
414
  return resolved.expo || resolved;
409
- } catch {
410
- throw new Error('Could not find app.json or app.config.js');
415
+ } catch (e) {
416
+ const configPath = path.resolve('app.config.js');
417
+ try {
418
+ await fs.access(configPath);
419
+ // File exists but failed to import — surface the real error
420
+ throw new Error(`Failed to load app.config.js: ${e instanceof Error ? e.message : e}`);
421
+ } catch (accessErr) {
422
+ if (accessErr instanceof Error && accessErr.message.startsWith('Failed to load'))
423
+ throw accessErr;
424
+ throw new Error('Could not find app.json or app.config.js');
425
+ }
411
426
  }
412
427
  }
413
428
  }
@@ -11,6 +11,21 @@ export async function handleAssets(request: Request, options: UpdatesHandlerOpti
11
11
  });
12
12
  }
13
13
 
14
+ // Redirect to a presigned S3 URL instead of proxying the bytes through Lambda.
15
+ // Lambda function URLs cap responses at 6 MB; Hermes bytecode bundles exceed that.
16
+ if (options.storage.presignGet) {
17
+ const presignedUrl = await options.storage.presignGet(key);
18
+ return new Response(null, {
19
+ status: 302,
20
+ headers: {
21
+ location: presignedUrl,
22
+ 'cache-control': 'private, max-age=300',
23
+ },
24
+ });
25
+ }
26
+
27
+ // Fallback: direct streaming for non-S3 backends (local dev, filesystem).
28
+ // No Lambda payload limit applies in these environments.
14
29
  const result = await options.storage.get(key);
15
30
 
16
31
  if (!result) {
@@ -8,6 +8,8 @@ export interface StorageAdapter {
8
8
  delete(key: string): Promise<void>;
9
9
  /** Generate a presigned PUT URL. S3-only — returns null for other backends. */
10
10
  presignPut?(key: string, contentType: string, expiresIn?: number): Promise<string>;
11
+ /** Generate a presigned GET URL. S3-only — returns undefined for other backends. */
12
+ presignGet?(key: string, expiresIn?: number): Promise<string>;
11
13
  }
12
14
 
13
15
  export type StorageOptions =
package/src/storage/s3.ts CHANGED
@@ -122,4 +122,14 @@ export class S3StorageAdapter implements StorageAdapter {
122
122
  ContentType: contentType,
123
123
  }), { expiresIn });
124
124
  }
125
+
126
+ async presignGet(key: string, expiresIn: number = 3600): Promise<string> {
127
+ const { GetObjectCommand } = await import('@aws-sdk/client-s3');
128
+ const { getSignedUrl } = await import('@aws-sdk/s3-request-presigner');
129
+ const client = await this.getClient();
130
+ return getSignedUrl(client, new GetObjectCommand({
131
+ Bucket: this.bucket,
132
+ Key: key,
133
+ }), { expiresIn });
134
+ }
125
135
  }