@ramonclaudio/vexpo 0.1.4 → 0.1.5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ramon Claudio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -7,6 +7,10 @@ The setup CLI for [vexpo](https://github.com/ramonclaudio/vexpo) projects (Expo
7
7
 
8
8
  Scaffolded by [`create-vexpo`](https://www.npmjs.com/package/@ramonclaudio/create-vexpo) into your devDependencies. Run it with `npx vexpo`.
9
9
 
10
+ <p align="center">
11
+ <img src="https://raw.githubusercontent.com/ramonclaudio/vexpo/main/docs/assets/demo-doctor.gif" width="720" alt="vexpo doctor auth-checking every credential against the live services">
12
+ </p>
13
+
10
14
  ## Setup
11
15
 
12
16
  ```
@@ -19,6 +23,7 @@ vexpo full --skip-rebrand full setup, skip the rebrand wizard
19
23
  vexpo doctor cross-source drift detection
20
24
  vexpo doctor --json machine-readable output
21
25
  vexpo doctor --strict exit non-zero on any warn
26
+ vexpo doctor --redact mask identifying values (screenshots, issue reports)
22
27
 
23
28
  vexpo accounts walk Apple/Expo/Convex/Resend signups (standalone)
24
29
  vexpo rebrand replace template defaults with your identity
@@ -38,7 +38,11 @@ async function run(argv, opts = {}) {
38
38
  stdin: opts.stdin ?? "ignore",
39
39
  stdout: "pipe",
40
40
  stderr: "pipe",
41
- env: opts.env,
41
+ // run() exists to PARSE output. A FORCE_COLOR=1 in the caller's shell
42
+ // (CI, recordings) makes child CLIs wrap fields in ANSI codes and every
43
+ // regex parser downstream silently misses. Force color off; an explicit
44
+ // opts.env can still override.
45
+ env: { FORCE_COLOR: "0", NO_COLOR: "1", ...opts.env },
42
46
  cwd: opts.cwd
43
47
  });
44
48
  const [code, stdout, stderr] = await Promise.all([
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { dlx } from './chunk-3RDUQUJW.js';
3
- import { run } from './chunk-W6O77AAZ.js';
3
+ import { run } from './chunk-5BTLX335.js';
4
4
 
5
5
  // src/lib/eas-cli.ts
6
6
  function compact(argv) {
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { ensureLine, readOne, readAll, removeLines, ENV_FILE } from './chunk-3TT4CDAJ.js';
3
- import { ascStatus } from './chunk-6O5TB4L4.js';
3
+ import { ascStatus } from './chunk-BRSFTWP2.js';
4
4
  import { dlx, detectPackageManager, installCmdFor } from './chunk-3RDUQUJW.js';
5
- import { run, spawn } from './chunk-W6O77AAZ.js';
5
+ import { run, spawn } from './chunk-5BTLX335.js';
6
6
  import { Command } from 'commander';
7
- import { readFile, access, writeFile, unlink, stat, mkdir, rename } from 'fs/promises';
7
+ import { readFile, access, unlink, stat, mkdir, writeFile, rename } from 'fs/promises';
8
8
  import { createInterface } from 'readline/promises';
9
9
  import { homedir } from 'os';
10
10
  import { join } from 'path';
@@ -13,7 +13,7 @@ import { createSign } from 'crypto';
13
13
 
14
14
  // package.json
15
15
  var package_default = {
16
- version: "0.1.4"};
16
+ version: "0.1.5"};
17
17
  function deploymentName(value) {
18
18
  if (!value) return void 0;
19
19
  const m = /^(?:dev|prod|preview):(.+)$/.exec(value);
@@ -470,7 +470,7 @@ async function askYesNo(question, defaultYes) {
470
470
  return raw === "y" || raw === "yes";
471
471
  }
472
472
  async function openUrlExternal(url) {
473
- const { spawn: spawn2 } = await import('./proc-QPMIGTW6.js');
473
+ const { spawn: spawn2 } = await import('./proc-L3ORJMPB.js');
474
474
  const cmd = process.platform === "darwin" ? ["open", url] : process.platform === "win32" ? ["cmd", "/c", "start", "", url] : ["xdg-open", url];
475
475
  spawn2(cmd, { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
476
476
  }
@@ -2385,6 +2385,23 @@ async function loadAscFromState2() {
2385
2385
  if (!existsSync(p8Path)) return null;
2386
2386
  return { issuerId, keyId, p8Path };
2387
2387
  }
2388
+ async function syncAscAppIdToEasJson(ascAppId) {
2389
+ if (!ascAppId || !existsSync("eas.json")) return;
2390
+ try {
2391
+ const before = await readFile("eas.json", "utf8");
2392
+ const after = withAscAppId(before, ascAppId);
2393
+ if (after !== before) {
2394
+ await writeFile("eas.json", after);
2395
+ ok(`wrote ascAppId ${BOLD}${ascAppId}${RESET} to eas.json submit profiles`);
2396
+ note("commit this in your fork: non-interactive `eas submit` (CI) needs it");
2397
+ } else {
2398
+ nop("eas.json submit profiles already carry ascAppId");
2399
+ }
2400
+ } catch (err) {
2401
+ yep(`couldn't write ascAppId to eas.json: ${err instanceof Error ? err.message : err}`);
2402
+ note("non-interactive submit will need `ascAppId` set manually in eas.json");
2403
+ }
2404
+ }
2388
2405
  async function runAscConnect(opts = {}) {
2389
2406
  section("ASC connect");
2390
2407
  if (!opts.force) {
@@ -2399,6 +2416,7 @@ async function runAscConnect(opts = {}) {
2399
2416
  bundleId: status.appStoreConnectApp.bundleIdentifier,
2400
2417
  connectedAt: (/* @__PURE__ */ new Date()).toISOString()
2401
2418
  });
2419
+ await syncAscAppIdToEasJson(status.appStoreConnectApp.ascAppIdentifier);
2402
2420
  return 0;
2403
2421
  }
2404
2422
  } catch {
@@ -2464,23 +2482,13 @@ async function runAscConnect(opts = {}) {
2464
2482
  connectedAt: (/* @__PURE__ */ new Date()).toISOString()
2465
2483
  });
2466
2484
  if (existsSync("eas.json")) {
2485
+ let postStatus = null;
2467
2486
  try {
2468
- const ascAppId = (await ascStatus()).appStoreConnectApp?.ascAppIdentifier;
2469
- if (ascAppId) {
2470
- const before = await readFile("eas.json", "utf8");
2471
- const after = withAscAppId(before, ascAppId);
2472
- if (after !== before) {
2473
- await writeFile("eas.json", after);
2474
- ok(`wrote ascAppId ${BOLD}${ascAppId}${RESET} to eas.json submit profiles`);
2475
- note("commit this in your fork: non-interactive `eas submit` (CI) needs it");
2476
- } else {
2477
- nop("eas.json submit profiles already carry ascAppId");
2478
- }
2479
- }
2480
- } catch (err) {
2481
- yep(`couldn't write ascAppId to eas.json: ${err instanceof Error ? err.message : err}`);
2482
- note("non-interactive submit will need `ascAppId` set manually in eas.json");
2487
+ postStatus = await ascStatus();
2488
+ } catch {
2489
+ postStatus = null;
2483
2490
  }
2491
+ await syncAscAppIdToEasJson(postStatus?.appStoreConnectApp?.ascAppIdentifier);
2484
2492
  }
2485
2493
  return 0;
2486
2494
  }
@@ -4107,10 +4115,25 @@ function icon(severity) {
4107
4115
  return `${DIM}-${RESET}`;
4108
4116
  }
4109
4117
  }
4118
+ var REDACTIONS = [
4119
+ [/https?:\/\/[a-z0-9-]+\.convex\.(cloud|site)[^\s]*/g, "https://<deployment>.convex.$1"],
4120
+ [/\b[a-z]+-[a-z]+-\d{3}\b/g, "<deployment>"],
4121
+ [/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, "<project-id>"],
4122
+ [/\b[\w.+-]+@[\w-]+\.[\w.]+\b/g, "<email>"],
4123
+ [/\b(?:[a-z0-9-]+\.){1,}[a-z]{2,}\b(?= verified)/g, "<domain>"],
4124
+ [/\b(?:com|io|dev|app|net|org)(?:\.[a-z0-9-]+){2,}\b/gi, "<bundle-id>"],
4125
+ [/\b[A-Z0-9]{10}\b/g, "<id>"],
4126
+ [/(@)[\w-]+(\/)/g, "$1<owner>$2"]
4127
+ ];
4128
+ function redactValue(text) {
4129
+ let out = text;
4130
+ for (const [re, sub] of REDACTIONS) out = out.replace(re, sub);
4131
+ return out;
4132
+ }
4110
4133
  function categoryHeader(c) {
4111
4134
  return c.charAt(0).toUpperCase() + c.slice(1);
4112
4135
  }
4113
- function printResults(checks) {
4136
+ function printResults(checks, redact) {
4114
4137
  const byCategory = /* @__PURE__ */ new Map();
4115
4138
  for (const c of checks) {
4116
4139
  if (!byCategory.has(c.category)) byCategory.set(c.category, []);
@@ -4123,8 +4146,9 @@ function printResults(checks) {
4123
4146
  section(categoryHeader(cat));
4124
4147
  const w = Math.max(...items.map((c) => c.name.length));
4125
4148
  for (const c of items) {
4126
- line(` ${icon(c.severity)} ${BOLD}${c.name.padEnd(w)}${RESET} ${c.message}`);
4127
- if (c.details) line(` ${DIM}${c.details}${RESET}`);
4149
+ const message = redact ? redactValue(c.message) : c.message;
4150
+ line(` ${icon(c.severity)} ${BOLD}${c.name.padEnd(w)}${RESET} ${message}`);
4151
+ if (c.details) line(` ${DIM}${redact ? redactValue(c.details) : c.details}${RESET}`);
4128
4152
  }
4129
4153
  }
4130
4154
  }
@@ -4159,7 +4183,7 @@ async function runDoctor(options2) {
4159
4183
  process.stdout.write(JSON.stringify({ channel, summary, checks }, null, 2) + "\n");
4160
4184
  } else {
4161
4185
  section(`Verify (${channel})`);
4162
- printResults(checks);
4186
+ printResults(checks, options2.redact === true);
4163
4187
  line();
4164
4188
  const parts = [
4165
4189
  `${GREEN}${summary.ok} ok${RESET}`,
@@ -5499,7 +5523,7 @@ async function liveCheckEas() {
5499
5523
  }
5500
5524
  async function liveCheckAscLink() {
5501
5525
  try {
5502
- const { ascStatus: ascStatus2 } = await import('./eas-integrations-5FVP7DI6.js');
5526
+ const { ascStatus: ascStatus2 } = await import('./eas-integrations-ZULIUD4T.js');
5503
5527
  const status = await ascStatus2();
5504
5528
  return status.status === "connected";
5505
5529
  } catch {
@@ -6440,7 +6464,7 @@ program.command("rebrand").description("Replace template defaults with your fork
6440
6464
  program.command("review-account").description("Seed the App Review demo account on Convex.").option("--email <email>", "override demo email").option("--password <password>", "override demo password").option("--name <name>", "override demo display name", "App Review").option("--username <username>", "optional username").action((options2) => exitWith(runReviewAccount(options2)));
6441
6465
  program.command("doctor").description(
6442
6466
  "Cross-source drift detection. Auth-checks every credential, confirms IDs match across `.env.local`, Convex env, EAS env, `app.config.ts`. No eas-cli equivalent."
6443
- ).option("--channel <channel>", "dev | prod", "dev").option("--json", "machine-readable output", false).option("--strict", "exit non-zero on any warn", false).action((options2) => {
6467
+ ).option("--channel <channel>", "dev | prod", "dev").option("--json", "machine-readable output", false).option("--strict", "exit non-zero on any warn", false).option("--redact", "mask identifying values (for screenshots and issue reports)", false).action((options2) => {
6444
6468
  exitWith(runDoctor(options2));
6445
6469
  });
6446
6470
  program.command("adopt").description(
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ export { ascStatus } from './chunk-BRSFTWP2.js';
3
+ import './chunk-3RDUQUJW.js';
4
+ import './chunk-5BTLX335.js';
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { run, spawn, streamText } from './chunk-5BTLX335.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramonclaudio/vexpo",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Operational CLI for vexpo projects: setup orchestration, drift detection, env sync, Apple JWT signing, ASC API integration.",
5
5
  "keywords": [
6
6
  "apple-sign-in",
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- export { ascStatus } from './chunk-6O5TB4L4.js';
3
- import './chunk-3RDUQUJW.js';
4
- import './chunk-W6O77AAZ.js';
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export { run, spawn, streamText } from './chunk-W6O77AAZ.js';