@produtype/core 1.23.0 → 1.24.0

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.
@@ -258,6 +258,33 @@ async function detectMobile(ctx) {
258
258
  });
259
259
  }
260
260
  }
261
+ /**
262
+ * Xcode has generated the Info.plist from build settings since version 13.
263
+ *
264
+ * damus declares five purpose strings and its `Info.plist` holds none of them:
265
+ * they are `INFOPLIST_KEY_NSCameraUsageDescription` and its four siblings in
266
+ * `project.pbxproj`, and again in `de.lproj/InfoPlist.strings` for every language
267
+ * it ships. The report told a careful application, at `high`, that it gives no
268
+ * reason for any permission it asks for.
269
+ *
270
+ * `INFOPLIST_KEY_` is Xcode's prefix and `InfoPlist.strings` is Apple's
271
+ * localisation file; the sentence after the equals sign is the author's. An empty
272
+ * one is treated the same way as an empty `<string>` in the plist — it passes the
273
+ * compiler and fails review.
274
+ */
275
+ for (const file of ctx.files.all.filter((f) => /project\.pbxproj$|(^|\/)InfoPlist\.strings$/i.test(f)).slice(0, 8)) {
276
+ const text = (await (0, readTextFileSafe_1.readTextFileSafe)(ctx.root, file)) ?? '';
277
+ const declared = [...text.matchAll(/(?:INFOPLIST_KEY_)?"?(NS\w*UsageDescription)"?\s*=\s*"([^"]*)"/g)];
278
+ const explained = declared.filter(([, , reason]) => reason.trim().length > 0);
279
+ if (!explained.length)
280
+ continue;
281
+ permissionsExplained = true;
282
+ permissionEvidence.push({
283
+ type: 'file',
284
+ value: `${explained.length} permission${explained.length === 1 ? '' : 's'} with a reason in ${file}`,
285
+ file,
286
+ });
287
+ }
261
288
  const runtimeRequests = await (0, textSearch_1.searchInFiles)(ctx.root, ctx.files.source, [
262
289
  /requestPermissions?\(/,
263
290
  /ActivityCompat\.requestPermissions/,
@@ -317,6 +344,22 @@ async function detectMobile(ctx) {
317
344
  */
318
345
  const connectivityChecks = await (0, textSearch_1.searchInFiles)(ctx.root, ctx.files.source, [/Connectivity\(\)/, /connectivity_plus/, /NetInfo\./, /navigator\.onLine/, /NWPathMonitor/, /isReachable/], 3);
319
346
  const platformStores = localStores.uses;
347
+ /**
348
+ * Watching the network is not storing anything, and it was the citation under
349
+ * "no local database".
350
+ *
351
+ * `NWPathMonitor`, `NetInfo` and `navigator.onLine` tell an application whether it
352
+ * is connected. They were pushed into this evidence list while counting for nothing
353
+ * in the verdict, so damus — which was found to have no store this can read — was
354
+ * shown `let network_monitor = NWPathMonitor()` as the reason. A line answering one
355
+ * question, offered as the evidence for another.
356
+ *
357
+ * Where a store *is* found they belong: knowing you are offline is part of working
358
+ * offline. Where none is, the search that came up empty is the honest evidence, and
359
+ * a reader who keeps their notes in something this cannot read can see what was
360
+ * looked for and say so.
361
+ */
362
+ const hasALocalStore = offlineDeps.length > 0 || platformStores.length > 0;
320
363
  const offlineEvidence = [
321
364
  ...offlineDeps.map((dep) => ({ type: 'dependency', value: dep })),
322
365
  ...platformStores.map((match) => ({
@@ -325,7 +368,7 @@ async function detectMobile(ctx) {
325
368
  file: match.file,
326
369
  line: match.line,
327
370
  })),
328
- ...connectivityChecks.map((match) => ({
371
+ ...(hasALocalStore ? connectivityChecks : []).map((match) => ({
329
372
  type: 'snippet',
330
373
  value: match.snippet,
331
374
  file: match.file,
@@ -369,7 +412,7 @@ async function detectMobile(ctx) {
369
412
  },
370
413
  {
371
414
  key: 'mobile.offline',
372
- present: offlineDeps.length > 0 || platformStores.length > 0,
415
+ present: hasALocalStore,
373
416
  evidence: (0, absenceEvidence_1.evidenceOrSearch)(offlineEvidence, 'a local database the app can read with no network', ['sqflite', 'drift', 'hive', 'isar', 'objectbox', 'realm', 'androidx.room', 'sqldelight', 'grdb.swift', 'sqlite.swift', 'NSManagedObjectContext', 'SQLiteOpenHelper', 'getWritableDatabase(']),
374
417
  },
375
418
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@produtype/core",
3
- "version": "1.23.0",
3
+ "version": "1.24.0",
4
4
  "description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
5
5
  "license": "MIT",
6
6
  "bin": {