runcloud 0.2.154 → 0.2.155

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.
@@ -1,13 +1,13 @@
1
1
  import { existsSync, readFileSync, readdirSync, statSync, } from 'node:fs';
2
2
  import path from 'node:path';
3
+ const PREVIEW_APP_ID = 'app.newly.preview';
3
4
  const CONFIG_RULES = {
4
5
  app_name: { path: ['expo', 'name'], absent: 'fail' },
5
6
  slug: { path: ['expo', 'slug'], absent: 'fail' },
6
- ios_bundle_id: { path: ['expo', 'ios', 'bundleIdentifier'], absent: 'fail' },
7
- android_package: { path: ['expo', 'android', 'package'], absent: 'fail' },
7
+ ios_bundle_id: { path: ['expo', 'ios', 'bundleIdentifier'], absent: 'info', storeId: /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+$/ },
8
+ android_package: { path: ['expo', 'android', 'package'], absent: 'info', storeId: /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/ },
8
9
  version: { path: ['expo', 'version'], absent: 'warning' },
9
10
  scheme: { path: ['expo', 'scheme'], absent: 'info' },
10
- ios_encryption: { path: ['expo', 'ios', 'infoPlist', 'ITSAppUsesNonExemptEncryption'], absent: 'warning' },
11
11
  user_interface_style: { path: ['expo', 'userInterfaceStyle'], absent: 'info' },
12
12
  orientation: { path: ['expo', 'orientation'], absent: 'info' },
13
13
  ios_supports_tablet: { path: ['expo', 'ios', 'supportsTablet'], absent: 'info' },
@@ -183,6 +183,8 @@ function evaluateLocalItem(root, appJson, item) {
183
183
  return evaluateExpoLayout(root, item);
184
184
  case 'expo_duplicate_route':
185
185
  return evaluateDuplicateRoutes(root, item);
186
+ case 'ios_encryption':
187
+ return evaluateEncryption(appJson, item);
186
188
  case 'plugin_permission_missing':
187
189
  return evaluatePluginPermissions(root, appJson, item);
188
190
  case 'permission_descriptions':
@@ -202,10 +204,25 @@ function evaluateConfigItem(appJson, item, rule) {
202
204
  const value = getPath(appJson, rule.path);
203
205
  const present = hasValue(value);
204
206
  const evidence = `app.json ${rule.path.join('.')} = ${value === undefined ? 'undefined' : JSON.stringify(value)}`;
207
+ if (rule.storeId) {
208
+ const id = stringValue(value);
209
+ if (!id || id.toLowerCase() === PREVIEW_APP_ID)
210
+ return makeItem(item, rule.absent, `${item.label} is assigned when you publish.`, evidence);
211
+ if (!rule.storeId.test(id) || id.length > 255)
212
+ return makeItem(item, 'fail', `${item.label} is not valid. Use a reverse-domain ID like com.yourname.app.`, evidence);
213
+ }
205
214
  if (present)
206
215
  return makeItem(item, 'pass', `${item.label} is set.`, evidence);
207
216
  return makeItem(item, rule.absent, `${item.label} is not set.`, evidence);
208
217
  }
218
+ function evaluateEncryption(appJson, item) {
219
+ const plist = getPath(appJson, ['expo', 'ios', 'infoPlist', 'ITSAppUsesNonExemptEncryption']);
220
+ const config = getPath(appJson, ['expo', 'ios', 'config', 'usesNonExemptEncryption']);
221
+ const evidence = `app.json expo.ios.infoPlist.ITSAppUsesNonExemptEncryption = ${JSON.stringify(plist) ?? 'undefined'}, expo.ios.config.usesNonExemptEncryption = ${JSON.stringify(config) ?? 'undefined'}`;
222
+ if (hasValue(plist) || hasValue(config))
223
+ return makeItem(item, 'pass', `${item.label} is set.`, evidence);
224
+ return makeItem(item, 'info', `${item.label} is declared exempt (no non-exempt encryption) when you publish.`, evidence);
225
+ }
209
226
  function evaluateRestorePurchases(root, item) {
210
227
  const iap = scan(root, /react-native-purchases|expo-in-app-purchases|StoreKit/i);
211
228
  if (!iap.length)
@@ -242,14 +259,14 @@ function evaluateExpoLayout(root, item) {
242
259
  return makeItem(item, 'fail', 'Expo Router directories without _layout files found.', missing.slice(0, 8).join(', '));
243
260
  }
244
261
  function evaluateDuplicateRoutes(root, item) {
245
- const appDir = path.join(root, 'app');
262
+ const appDir = routerDir(root);
246
263
  if (!existsSync(appDir))
247
264
  return makeItem(item, 'pass', 'No Expo Router app directory found.', 'scan: app/ directory absent');
248
265
  const routes = new Map();
249
266
  for (const file of walkFiles(appDir)) {
250
267
  if (!/\.(tsx|ts|jsx|js)$/.test(file) || /(^|\/)_layout\.(tsx|ts|jsx|js)$/.test(toRel(root, file)))
251
268
  continue;
252
- const route = routePath(root, file);
269
+ const route = routePath(appDir, file);
253
270
  const current = routes.get(route) ?? [];
254
271
  current.push(toRel(root, file));
255
272
  routes.set(route, current);
@@ -422,14 +439,10 @@ function unresolvedReason(item) {
422
439
  return `no deterministic evaluator is registered for ${item.check} item`;
423
440
  }
424
441
  function targetPlatformsFromAppJson(appJson) {
425
- const ios = stringValue(getPath(appJson, ['expo', 'ios', 'bundleIdentifier']));
426
- const android = stringValue(getPath(appJson, ['expo', 'android', 'package']));
427
- const platforms = [];
428
- if (ios)
429
- platforms.push('ios');
430
- if (android)
431
- platforms.push('android');
432
- return platforms.length ? platforms : ['ios', 'android'];
442
+ const declared = getPath(appJson, ['expo', 'platforms']);
443
+ if (!Array.isArray(declared))
444
+ return ['ios', 'android'];
445
+ return ['ios', 'android'].filter((platform) => declared.includes(platform));
433
446
  }
434
447
  function targetsItem(item, targetPlatforms) {
435
448
  return item.platform.some((platform) => targetPlatforms.includes(platform));
@@ -526,8 +539,12 @@ function sourceText(root, dirs) {
526
539
  function hasLayoutFile(dir) {
527
540
  return ['_layout.tsx', '_layout.ts', '_layout.jsx', '_layout.js'].some((name) => existsSync(path.join(dir, name)));
528
541
  }
529
- function routePath(root, file) {
530
- const rel = toRel(root, file).replace(/^app\//, '').replace(/\.(tsx|ts|jsx|js)$/, '');
542
+ function routerDir(root) {
543
+ const src = path.join(root, 'src', 'app');
544
+ return safeStat(src)?.isDirectory() ? src : path.join(root, 'app');
545
+ }
546
+ function routePath(appDir, file) {
547
+ const rel = toRel(appDir, file).replace(/\.(tsx|ts|jsx|js)$/, '');
531
548
  const parts = rel.split('/').filter((part) => part && !/^\(.+\)$/.test(part));
532
549
  if (parts[parts.length - 1] === 'index')
533
550
  parts.pop();
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const CLI_VERSION = '0.2.154';
1
+ export const CLI_VERSION = '0.2.155';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcloud",
3
- "version": "0.2.154",
3
+ "version": "0.2.155",
4
4
  "description": "Create and control run.cloud remote mobile simulators and cloud sandboxes",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
Binary file
Binary file