@everystack/cli 0.2.8 → 0.2.9

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.8",
3
+ "version": "0.2.9",
4
4
  "description": "CLI and OTA updates for Expo apps on everystack",
5
5
  "license": "AGPL-3.0-only",
6
6
  "publishConfig": {
@@ -41,6 +41,7 @@
41
41
  "lint": "tsc --noEmit"
42
42
  },
43
43
  "dependencies": {
44
+ "@aws-sdk/client-cloudfront": "3.1053.0",
44
45
  "@aws-sdk/client-cloudfront-keyvaluestore": "3.1053.0",
45
46
  "@aws-sdk/signature-v4a": "3.1048.0",
46
47
  "glob": "13.0.6",
@@ -50,7 +51,6 @@
50
51
  },
51
52
  "peerDependencies": {
52
53
  "@everystack/server": ">=0.1.0",
53
- "@aws-sdk/client-cloudfront": "3.1053.0",
54
54
  "@aws-sdk/client-cloudwatch-logs": "3.1053.0",
55
55
  "@aws-sdk/client-lambda": "3.1053.0",
56
56
  "@aws-sdk/client-s3": "3.1053.0",
@@ -64,9 +64,6 @@
64
64
  "@everystack/server": {
65
65
  "optional": true
66
66
  },
67
- "@aws-sdk/client-cloudfront": {
68
- "optional": true
69
- },
70
67
  "@aws-sdk/client-cloudwatch-logs": {
71
68
  "optional": true
72
69
  },
@@ -90,7 +87,6 @@
90
87
  }
91
88
  },
92
89
  "devDependencies": {
93
- "@aws-sdk/client-cloudfront": "3.1053.0",
94
90
  "@aws-sdk/client-cloudwatch-logs": "3.1053.0",
95
91
  "@aws-sdk/client-lambda": "3.1053.0",
96
92
  "@aws-sdk/client-s3": "3.1053.0",
@@ -1,10 +1,13 @@
1
1
  import type { UpdatesHandlerOptions } from './types';
2
2
 
3
+ const debug = process.env.DEBUG_UPDATES === '1';
4
+
3
5
  export async function handleAssets(request: Request, options: UpdatesHandlerOptions): Promise<Response> {
4
6
  const url = new URL(request.url);
5
7
  const key = url.searchParams.get('key');
6
8
 
7
9
  if (!key) {
10
+ console.warn('[updates] asset request rejected: no key');
8
11
  return new Response(JSON.stringify({ error: 'No asset key provided.' }), {
9
12
  status: 400,
10
13
  headers: { 'content-type': 'application/json' },
@@ -15,6 +18,7 @@ export async function handleAssets(request: Request, options: UpdatesHandlerOpti
15
18
  // Lambda function URLs cap responses at 6 MB; Hermes bytecode bundles exceed that.
16
19
  if (options.storage.presignGet) {
17
20
  const presignedUrl = await options.storage.presignGet(key);
21
+ debug && console.log('[updates] asset redirect', { key });
18
22
  return new Response(null, {
19
23
  status: 302,
20
24
  headers: {
@@ -29,6 +33,7 @@ export async function handleAssets(request: Request, options: UpdatesHandlerOpti
29
33
  const result = await options.storage.get(key);
30
34
 
31
35
  if (!result) {
36
+ console.warn('[updates] asset not found', { key });
32
37
  return new Response(JSON.stringify({ error: `Asset "${key}" not found.` }), {
33
38
  status: 404,
34
39
  headers: { 'content-type': 'application/json' },
@@ -71,6 +71,7 @@ export function createUpdatesHandler(options: UpdatesHandlerOptions): (request:
71
71
  return handleEditChannel(request, options, channelName);
72
72
  }
73
73
 
74
+ console.warn('[updates] 404 unmatched route', { method: request.method, pathname });
74
75
  return new Response(JSON.stringify({ error: 'Not found' }), {
75
76
  status: 404,
76
77
  headers: { 'content-type': 'application/json' },
@@ -6,6 +6,8 @@ import { buildMultipartResponse } from './multipart';
6
6
  import { signRSASHA256, convertSHA256HashToUUID, convertToDictionaryItemsRepresentation } from './signing';
7
7
  import { jsonError } from './helpers';
8
8
 
9
+ const debug = process.env.DEBUG_UPDATES === '1';
10
+
9
11
  export async function handleManifest(request: Request, options: UpdatesHandlerOptions): Promise<Response> {
10
12
  const headers = request.headers;
11
13
 
@@ -14,24 +16,46 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
14
16
 
15
17
  const platform = headers.get('expo-platform');
16
18
  if (platform !== 'ios' && platform !== 'android') {
19
+ console.warn('[updates] manifest request rejected: unsupported platform', {
20
+ platform: headers.get('expo-platform'),
21
+ });
17
22
  return jsonError(400, 'Unsupported platform. Expected either ios or android.');
18
23
  }
19
24
 
20
25
  const runtimeVersion = headers.get('expo-runtime-version');
21
26
  if (!runtimeVersion) {
27
+ console.warn('[updates] manifest request rejected: no runtimeVersion');
22
28
  return jsonError(400, 'No runtimeVersion provided.');
23
29
  }
24
30
 
25
31
  const channelName = headers.get('expo-channel-name') || options.defaultChannel || 'production';
26
32
 
33
+ debug && console.log('[updates] manifest request', {
34
+ platform,
35
+ runtimeVersion,
36
+ channelName,
37
+ currentUpdateId: headers.get('expo-current-update-id'),
38
+ protocolVersion,
39
+ expectSignature: !!headers.get('expo-expect-signature'),
40
+ });
41
+
27
42
  // Try S3 flat-file manifest first (works without DB)
43
+ const s3Key = `${channelName}/${platform}/${runtimeVersion}/manifest.json`;
28
44
  const s3Manifest = await tryS3Manifest(options, channelName, platform, runtimeVersion);
29
45
  if (s3Manifest) {
46
+ debug && console.log('[updates] S3 manifest found', {
47
+ key: s3Key,
48
+ updateId: s3Manifest.id,
49
+ assetCount: s3Manifest.assets?.length,
50
+ hasLaunchAsset: !!s3Manifest.launchAsset,
51
+ });
30
52
  return buildManifestResponse(request, s3Manifest, options, protocolVersion);
31
53
  }
54
+ debug && console.log('[updates] S3 manifest not found', { key: s3Key });
32
55
 
33
56
  // Fall back to DB-based lookup (V2)
34
57
  if (!options.db) {
58
+ debug && console.log('[updates] no DB configured, returning 404');
35
59
  return jsonError(404, `No update found for ${channelName}/${platform}/${runtimeVersion}`);
36
60
  }
37
61
 
@@ -50,13 +74,16 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
50
74
  .limit(1);
51
75
 
52
76
  if (!channel) {
77
+ console.warn('[updates] DB channel not found', { channelName });
53
78
  return jsonError(404, `Channel "${channelName}" not found.`);
54
79
  }
55
80
 
56
81
  const branchRef = (channel.data as Record<string, unknown>)?.branchRef as string;
57
82
  if (!branchRef) {
83
+ console.warn('[updates] DB channel has no branch mapping', { channelName });
58
84
  return jsonError(404, `Channel "${channelName}" has no branch mapping.`);
59
85
  }
86
+ debug && console.log('[updates] DB channel resolved', { channelName, branchRef });
60
87
 
61
88
  // Look up latest release (buildArtifact type='ota') via run branchRef
62
89
  const releaseResults = await options.db
@@ -74,9 +101,15 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
74
101
  .limit(1);
75
102
 
76
103
  if (!releaseResults[0]) {
104
+ console.warn('[updates] DB release not found', { branchRef, runtimeVersion, platform });
77
105
  return jsonError(404, `No update found for runtime version: ${runtimeVersion}`);
78
106
  }
79
107
  release = releaseResults[0].release;
108
+ debug && console.log('[updates] DB release found', {
109
+ releaseId: release.id,
110
+ runId: release.runId,
111
+ updateId: (release.data as Record<string, unknown>)?.updateId,
112
+ });
80
113
 
81
114
  // Look up assets (buildArtifacts type='ota_asset') for this release's run
82
115
  assets = await options.db
@@ -86,8 +119,12 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
86
119
  eq(buildArtifacts.type, 'ota_asset'),
87
120
  eq(buildArtifacts.runId, release.runId),
88
121
  ));
89
- } catch {
122
+ } catch (err) {
90
123
  // DB unavailable or tables don't exist — not an error in V1
124
+ console.error('[updates] DB lookup error', {
125
+ error: err instanceof Error ? err.message : String(err),
126
+ channelName, platform, runtimeVersion,
127
+ });
91
128
  return jsonError(404, `No update found for ${channelName}/${platform}/${runtimeVersion}`);
92
129
  }
93
130
 
@@ -95,6 +132,7 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
95
132
 
96
133
  // Handle rollback
97
134
  if (releaseData.isRollback) {
135
+ debug && console.log('[updates] response: rollback', { protocolVersion });
98
136
  if (protocolVersion === 0) {
99
137
  return jsonError(400, 'Rollbacks not supported on protocol version 0');
100
138
  }
@@ -106,6 +144,9 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
106
144
  const updateId = releaseData.updateId as string;
107
145
  const releaseUUID = convertSHA256HashToUUID(updateId);
108
146
  if (currentUpdateId === releaseUUID && protocolVersion === 1) {
147
+ debug && console.log('[updates] response: noUpdateAvailable', {
148
+ currentUpdateId, releaseUUID,
149
+ });
109
150
  return buildNoUpdateAvailableResponse(request, options, protocolVersion);
110
151
  }
111
152
 
@@ -148,6 +189,13 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
148
189
  },
149
190
  };
150
191
 
192
+ debug && console.log('[updates] response: manifest', {
193
+ updateId: releaseUUID,
194
+ assetCount: regularAssets.length,
195
+ hasLaunchAsset: !!launchAsset,
196
+ source: 'db',
197
+ });
198
+
151
199
  return buildManifestResponse(request, manifest, options, protocolVersion);
152
200
  }
153
201