@ramonclaudio/vexpo 0.1.3 → 0.1.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/README.md +54 -56
- package/dist/{chunk-PYXH4J77.js → chunk-3RDUQUJW.js} +1 -27
- package/dist/{chunk-VOL7YISA.js → chunk-6O5TB4L4.js} +2 -2
- package/dist/{chunk-QFP5R25M.js → chunk-W6O77AAZ.js} +2 -20
- package/dist/cli.js +165 -213
- package/dist/eas-integrations-5FVP7DI6.js +4 -0
- package/dist/{pkg-manager-PU7UPAID.js → pkg-manager-DOOC6W2C.js} +1 -1
- package/dist/proc-QPMIGTW6.js +2 -0
- package/package.json +1 -1
- package/dist/eas-integrations-2QVR45NE.js +0 -4
- package/dist/proc-TEOPRZZ2.js +0 -2
package/README.md
CHANGED
|
@@ -3,73 +3,74 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ramonclaudio/vexpo)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
The setup CLI for [vexpo](https://github.com/ramonclaudio/vexpo) projects (Expo + Convex + Better Auth + Resend, end-to-end iOS). It creates or links your Convex deployment, signs and rotates the Apple keys (the P8 dance), and keeps your env in sync everywhere. It covers the App Store Connect last mile to a first ship. EAS does the heavy lifting (builds, updates, submission), vexpo covers the setup around it.
|
|
7
7
|
|
|
8
|
-
Scaffolded by [`create-vexpo`](https://www.npmjs.com/package/@ramonclaudio/create-vexpo) into your
|
|
9
|
-
|
|
10
|
-
## Design rule: don't reinvent EAS
|
|
11
|
-
|
|
12
|
-
If `eas <subcommand>` is the canonical answer, the recipe is `npx eas <subcommand>`, not `vexpo`. This CLI only surfaces what `eas-cli` doesn't do: setup orchestration, cross-source drift detection, Apple SIWA work, and the last App Store Connect mile to a first ship. Scope test for any command: does it help an empty directory become a first shipped, authenticated, backed iOS app? Post-launch ops (customer reviews, IAP sandbox testing, release management) are out, that's `eas` and App Store Connect once you have users. Wrapping every `eas` command would add no value over `eas-cli` itself, expand the maintenance surface, and signal a lack of trust in the platform.
|
|
8
|
+
Scaffolded by [`create-vexpo`](https://www.npmjs.com/package/@ramonclaudio/create-vexpo) into your devDependencies. Run it with `npx vexpo`.
|
|
13
9
|
|
|
14
10
|
## Setup
|
|
15
11
|
|
|
16
12
|
```
|
|
17
|
-
vexpo lite Convex + Better Auth only, simulator-ready (60s)
|
|
18
|
-
vexpo lite --new same + Convex signup walkthrough for first-
|
|
13
|
+
vexpo lite Convex + Better Auth only, simulator-ready (~60s)
|
|
14
|
+
vexpo lite --new same + Convex signup walkthrough for first-timers
|
|
19
15
|
vexpo full full provisioning (Convex + Better Auth + Resend + Apple + EAS init + rebrand)
|
|
20
16
|
vexpo full --new same + walks Apple/Convex/Expo/Resend signups
|
|
21
17
|
vexpo full --skip-rebrand full setup, skip the rebrand wizard
|
|
22
18
|
|
|
23
|
-
vexpo doctor
|
|
24
|
-
vexpo doctor --json
|
|
25
|
-
vexpo doctor --strict
|
|
26
|
-
|
|
27
|
-
vexpo accounts
|
|
28
|
-
vexpo rebrand
|
|
29
|
-
vexpo review-account
|
|
30
|
-
vexpo convex
|
|
31
|
-
vexpo better-auth
|
|
32
|
-
vexpo resend
|
|
33
|
-
vexpo env push .env.local + .env.prod
|
|
34
|
-
vexpo env convex-key
|
|
35
|
-
vexpo adopt
|
|
36
|
-
vexpo convex:migrate
|
|
37
|
-
vexpo asc:connect
|
|
19
|
+
vexpo doctor cross-source drift detection
|
|
20
|
+
vexpo doctor --json machine-readable output
|
|
21
|
+
vexpo doctor --strict exit non-zero on any warn
|
|
22
|
+
|
|
23
|
+
vexpo accounts walk Apple/Expo/Convex/Resend signups (standalone)
|
|
24
|
+
vexpo rebrand replace template defaults with your identity
|
|
25
|
+
vexpo review-account seed the App Review demo account on Convex
|
|
26
|
+
vexpo convex provision or connect a Convex deployment
|
|
27
|
+
vexpo better-auth set SITE_URL, BETTER_AUTH_SECRET, APP_NAME on Convex
|
|
28
|
+
vexpo resend provision Resend sending key + webhook, write to Convex env
|
|
29
|
+
vexpo env push push .env.local + .env.prod to Convex + EAS env
|
|
30
|
+
vexpo env convex-key sync Convex deploy key + selector to EAS (post-migration fix)
|
|
31
|
+
vexpo adopt finish a project created by `eas integrations:convex:connect`
|
|
32
|
+
vexpo convex:migrate copy server-side Convex env from another deployment
|
|
33
|
+
vexpo asc:connect link the EAS project to its ASC app (wraps `eas integrations:asc:connect`)
|
|
38
34
|
```
|
|
39
35
|
|
|
40
36
|
## Apple
|
|
41
37
|
|
|
42
38
|
```
|
|
43
|
-
vexpo apple asc-key
|
|
44
|
-
vexpo apple
|
|
45
|
-
vexpo apple
|
|
46
|
-
vexpo apple
|
|
47
|
-
vexpo apple jwt
|
|
48
|
-
vexpo apple
|
|
39
|
+
vexpo apple asc-key validate an ASC API key against /v1/apps
|
|
40
|
+
vexpo apple asc-key --revalidate re-check the cached key without re-prompting
|
|
41
|
+
vexpo apple credentials wrap `eas credentials:configure-build` with the cached ASC key
|
|
42
|
+
vexpo apple services-id detect SIWA Services ID + attach APPLE_ID_AUTH capability
|
|
43
|
+
vexpo apple jwt sign the SIWA ES256 client_secret JWT (180-day expiry)
|
|
44
|
+
vexpo apple jwt --rotate re-sign the JWT only
|
|
45
|
+
vexpo apple eas-rotation-secrets push the 5 EAS production secrets the JWT cron needs
|
|
49
46
|
```
|
|
50
47
|
|
|
51
|
-
## App Store Connect
|
|
48
|
+
## App Store Connect
|
|
52
49
|
|
|
53
|
-
TestFlight
|
|
50
|
+
Picks up after `eas submit` hands a build to TestFlight: groups, testers, release notes, plus the privacy and accessibility labels Apple requires before review.
|
|
54
51
|
|
|
55
52
|
```
|
|
56
|
-
vexpo testflight groups list
|
|
57
|
-
vexpo testflight groups create <name>
|
|
58
|
-
vexpo testflight groups view <id>
|
|
59
|
-
vexpo testflight groups delete <id>
|
|
60
|
-
vexpo testflight testers list
|
|
61
|
-
vexpo testflight invite <email>
|
|
62
|
-
vexpo testflight whats-new <buildId> <text>
|
|
63
|
-
|
|
64
|
-
vexpo asc:privacy show [file]
|
|
65
|
-
vexpo asc:privacy lint <file>
|
|
66
|
-
vexpo asc:accessibility show
|
|
67
|
-
vexpo asc:accessibility lint <file>
|
|
53
|
+
vexpo testflight groups list list beta groups
|
|
54
|
+
vexpo testflight groups create <name> create a beta group
|
|
55
|
+
vexpo testflight groups view <id> view a beta group + its testers
|
|
56
|
+
vexpo testflight groups delete <id> delete a beta group
|
|
57
|
+
vexpo testflight testers list list beta testers
|
|
58
|
+
vexpo testflight invite <email> add a tester + send a TestFlight invite
|
|
59
|
+
vexpo testflight whats-new <buildId> <text> set the "What's new" notes
|
|
60
|
+
|
|
61
|
+
vexpo asc:privacy show [file] show the declared privacy.config.json
|
|
62
|
+
vexpo asc:privacy lint <file> validate privacy.config.json against Apple's enums
|
|
63
|
+
vexpo asc:accessibility show fetch the app's accessibility declarations
|
|
64
|
+
vexpo asc:accessibility lint <file> validate accessibility.config.json against Apple's enums
|
|
68
65
|
```
|
|
69
66
|
|
|
67
|
+
## Design rule: don't reinvent EAS
|
|
68
|
+
|
|
69
|
+
vexpo only covers what `eas-cli` doesn't: setup orchestration, cross-source drift detection, Apple SIWA work, and the last App Store Connect mile to a first ship. If `eas <subcommand>` is the canonical answer, run `npx eas <subcommand>`.
|
|
70
|
+
|
|
70
71
|
## What `vexpo` doesn't wrap
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
Reach for `eas` directly for the canonical platform surface.
|
|
73
74
|
|
|
74
75
|
```bash
|
|
75
76
|
npx eas init # EAS project init
|
|
@@ -89,26 +90,23 @@ npx eas env [...] # env:push, env:pull, env:get, env:delete, env:
|
|
|
89
90
|
npx eas integrations:asc [...] # status, connect, disconnect
|
|
90
91
|
```
|
|
91
92
|
|
|
92
|
-
`vexpo full`
|
|
93
|
-
|
|
94
|
-
Earlier vexpo versions passed `--api-key-id <apple-key-id>` to `integrations:asc:connect`. That flag matches against EAS's uploaded key resources, not Apple-side identifiers, so it failed with `No App Store Connect API key found with Apple key identifier ...` whenever the key hadn't been uploaded to EAS yet (the common case on fresh projects). The current orchestration drops the flag and relies on the env vars + the wizard's "Create new or use existing" prompt instead.
|
|
93
|
+
`vexpo full` drives `eas init`, `eas env:push`, `eas credentials`, and the ASC link internally using the cached ASC key. Only the ASC link is also standalone, as `vexpo asc:connect`.
|
|
95
94
|
|
|
96
95
|
## Architecture
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
- One file per top-level command in `src/commands/`. Each exports `run<Name>(options)` returning a numeric exit code.
|
|
100
|
-
- `src/lib/eas-cli.ts` is the shared shell-out helper: `easJson<T>(argv)` parses `--json --non-interactive` output, `easSpawn(argv)` forwards stdio for interactive commands, `easText(argv)` returns raw streams.
|
|
101
|
-
- Built with tsup → single ESM bundle in `dist/`. Node 20+.
|
|
97
|
+
Commander tree in `src/cli.ts`, one file per top-level command in `src/commands/`. `src/lib/eas-cli.ts` shells out to `eas-cli`. Built with tsup to ESM, a `cli.js` entry plus shared chunks. Node 20+.
|
|
102
98
|
|
|
103
99
|
## Apple ASC API workarounds
|
|
104
100
|
|
|
105
|
-
Apple changed several ASC API behaviors after the initial CLI release. The CLI handles each one
|
|
101
|
+
Apple changed several ASC API behaviors after the initial CLI release. The CLI handles each one.
|
|
106
102
|
|
|
107
|
-
- `POST /v1/bundleIds` rejects `platform: "SERVICES"`. `services-id` walks
|
|
108
|
-
- App bundle IDs report `platform: "UNIVERSAL"` for newer accounts.
|
|
109
|
-
- Relationship endpoints reject `limit`.
|
|
110
|
-
- `filter[platform]=SERVICES` returns 400. `doctor`
|
|
103
|
+
- `POST /v1/bundleIds` rejects `platform: "SERVICES"`. `services-id` walks you through manual creation in the developer portal, then re-polls.
|
|
104
|
+
- App bundle IDs report `platform: "UNIVERSAL"` for newer accounts. The App ID lookup matches any non-SERVICES platform.
|
|
105
|
+
- Relationship endpoints reject `limit`. The capability list fetches without pagination.
|
|
106
|
+
- `filter[platform]=SERVICES` returns 400. `doctor` filters by identifier alone.
|
|
111
107
|
|
|
112
108
|
## Repo
|
|
113
109
|
|
|
114
110
|
[github.com/ramonclaudio/vexpo](https://github.com/ramonclaudio/vexpo)
|
|
111
|
+
|
|
112
|
+
Working on the CLI itself? See [CONTRIBUTING.md](https://github.com/ramonclaudio/vexpo/blob/main/CONTRIBUTING.md).
|
|
@@ -19,19 +19,6 @@ async function detectPackageManager() {
|
|
|
19
19
|
function dlx() {
|
|
20
20
|
return process.versions.bun ? "bunx" : "npx";
|
|
21
21
|
}
|
|
22
|
-
function dlxFor(pm) {
|
|
23
|
-
switch (pm) {
|
|
24
|
-
case "bun":
|
|
25
|
-
return "bunx";
|
|
26
|
-
case "pnpm":
|
|
27
|
-
return "pnpm dlx";
|
|
28
|
-
case "yarn":
|
|
29
|
-
return "yarn dlx";
|
|
30
|
-
case "npm":
|
|
31
|
-
default:
|
|
32
|
-
return "npx";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
22
|
function installCmdFor(pm) {
|
|
36
23
|
switch (pm) {
|
|
37
24
|
case "bun":
|
|
@@ -45,19 +32,6 @@ function installCmdFor(pm) {
|
|
|
45
32
|
return "npm install";
|
|
46
33
|
}
|
|
47
34
|
}
|
|
48
|
-
function runCmdFor(pm) {
|
|
49
|
-
switch (pm) {
|
|
50
|
-
case "bun":
|
|
51
|
-
return "bun run";
|
|
52
|
-
case "pnpm":
|
|
53
|
-
return "pnpm run";
|
|
54
|
-
case "yarn":
|
|
55
|
-
return "yarn";
|
|
56
|
-
case "npm":
|
|
57
|
-
default:
|
|
58
|
-
return "npm run";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
35
|
function currentRuntime() {
|
|
62
36
|
return process.versions.bun ? "bun" : "node";
|
|
63
37
|
}
|
|
@@ -65,4 +39,4 @@ function currentRuntimeVersion() {
|
|
|
65
39
|
return process.versions.bun ?? process.versions.node ?? "?";
|
|
66
40
|
}
|
|
67
41
|
|
|
68
|
-
export { currentRuntime, currentRuntimeVersion, detectPackageManager, dlx,
|
|
42
|
+
export { currentRuntime, currentRuntimeVersion, detectPackageManager, dlx, installCmdFor };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn as spawn$1
|
|
2
|
+
import { spawn as spawn$1 } from 'child_process';
|
|
3
3
|
|
|
4
4
|
function spawn(argv, opts = {}) {
|
|
5
5
|
const stdio = opts.stdio ?? [
|
|
@@ -25,24 +25,6 @@ function spawn(argv, opts = {}) {
|
|
|
25
25
|
kill: (signal) => proc.kill(signal)
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
function spawnSync(argv, opts = {}) {
|
|
29
|
-
const stdio = opts.stdio ?? [
|
|
30
|
-
opts.stdin ?? "inherit",
|
|
31
|
-
opts.stdout ?? "pipe",
|
|
32
|
-
opts.stderr ?? "pipe"
|
|
33
|
-
];
|
|
34
|
-
const result = spawnSync$1(argv[0], argv.slice(1), {
|
|
35
|
-
stdio,
|
|
36
|
-
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
37
|
-
cwd: opts.cwd,
|
|
38
|
-
encoding: "utf8"
|
|
39
|
-
});
|
|
40
|
-
return {
|
|
41
|
-
code: result.status ?? (result.signal ? 1 : 0),
|
|
42
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
43
|
-
stderr: typeof result.stderr === "string" ? result.stderr : ""
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
28
|
async function streamText(stream) {
|
|
47
29
|
if (!stream) return "";
|
|
48
30
|
const chunks = [];
|
|
@@ -67,4 +49,4 @@ async function run(argv, opts = {}) {
|
|
|
67
49
|
return { code, stdout, stderr };
|
|
68
50
|
}
|
|
69
51
|
|
|
70
|
-
export { run, spawn,
|
|
52
|
+
export { run, spawn, streamText };
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { ensureLine, readOne, readAll, removeLines, ENV_FILE } from './chunk-3TT4CDAJ.js';
|
|
3
|
-
import { ascStatus } from './chunk-
|
|
4
|
-
import { dlx, detectPackageManager, installCmdFor } from './chunk-
|
|
5
|
-
import { run, spawn } from './chunk-
|
|
3
|
+
import { ascStatus } from './chunk-6O5TB4L4.js';
|
|
4
|
+
import { dlx, detectPackageManager, installCmdFor } from './chunk-3RDUQUJW.js';
|
|
5
|
+
import { run, spawn } from './chunk-W6O77AAZ.js';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { readFile, access, writeFile, unlink, stat, mkdir, rename } from 'fs/promises';
|
|
8
8
|
import { createInterface } from 'readline/promises';
|
|
@@ -13,7 +13,7 @@ import { createSign } from 'crypto';
|
|
|
13
13
|
|
|
14
14
|
// package.json
|
|
15
15
|
var package_default = {
|
|
16
|
-
version: "0.1.
|
|
16
|
+
version: "0.1.4"};
|
|
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-
|
|
473
|
+
const { spawn: spawn2 } = await import('./proc-QPMIGTW6.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
|
}
|
|
@@ -1228,7 +1228,7 @@ async function ensureIdentity(localEnv) {
|
|
|
1228
1228
|
ok(`wrote EXPO_PUBLIC_APP_BUNDLE_ID=${bundleId}`);
|
|
1229
1229
|
}
|
|
1230
1230
|
} else {
|
|
1231
|
-
|
|
1231
|
+
ok(`EXPO_PUBLIC_APP_BUNDLE_ID=${bundleId} (from .env.local); syncing to Convex`);
|
|
1232
1232
|
}
|
|
1233
1233
|
if (!haveTeam) {
|
|
1234
1234
|
if (!process.stdin.isTTY) {
|
|
@@ -1829,13 +1829,6 @@ function makeAscClient(creds) {
|
|
|
1829
1829
|
};
|
|
1830
1830
|
const res = await request("POST", "/v1/bundleIds", body);
|
|
1831
1831
|
return res.data;
|
|
1832
|
-
},
|
|
1833
|
-
async get(id) {
|
|
1834
|
-
const res = await request("GET", `/v1/bundleIds/${id}`);
|
|
1835
|
-
return res.data;
|
|
1836
|
-
},
|
|
1837
|
-
async delete(id) {
|
|
1838
|
-
await request("DELETE", `/v1/bundleIds/${id}`);
|
|
1839
1832
|
}
|
|
1840
1833
|
},
|
|
1841
1834
|
bundleIdCapabilities: {
|
|
@@ -1864,80 +1857,17 @@ function makeAscClient(creds) {
|
|
|
1864
1857
|
body
|
|
1865
1858
|
);
|
|
1866
1859
|
return res.data;
|
|
1867
|
-
},
|
|
1868
|
-
async delete(id) {
|
|
1869
|
-
await request("DELETE", `/v1/bundleIdCapabilities/${id}`);
|
|
1870
1860
|
}
|
|
1871
1861
|
},
|
|
1872
1862
|
apps: {
|
|
1873
|
-
async list() {
|
|
1874
|
-
return paginatedList("/v1/apps");
|
|
1875
|
-
},
|
|
1876
|
-
async get(id) {
|
|
1877
|
-
const res = await request("GET", `/v1/apps/${id}`);
|
|
1878
|
-
return res.data;
|
|
1879
|
-
}
|
|
1880
|
-
},
|
|
1881
|
-
certificates: {
|
|
1882
|
-
async list(filter) {
|
|
1883
|
-
const query = {};
|
|
1884
|
-
if (filter?.certificateType) query["filter[certificateType]"] = filter.certificateType;
|
|
1885
|
-
return paginatedList("/v1/certificates", query);
|
|
1886
|
-
},
|
|
1887
|
-
async create(args) {
|
|
1888
|
-
const body = {
|
|
1889
|
-
data: {
|
|
1890
|
-
type: "certificates",
|
|
1891
|
-
attributes: {
|
|
1892
|
-
csrContent: args.csrContent,
|
|
1893
|
-
certificateType: args.certificateType
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
};
|
|
1897
|
-
const res = await request("POST", "/v1/certificates", body);
|
|
1898
|
-
return res.data;
|
|
1899
|
-
},
|
|
1900
|
-
async delete(id) {
|
|
1901
|
-
await request("DELETE", `/v1/certificates/${id}`);
|
|
1902
|
-
}
|
|
1903
|
-
},
|
|
1904
|
-
profiles: {
|
|
1905
1863
|
async list(filter) {
|
|
1906
1864
|
const query = {};
|
|
1907
|
-
if (filter?.
|
|
1908
|
-
|
|
1909
|
-
return paginatedList("/v1/profiles", query);
|
|
1865
|
+
if (filter?.bundleId) query["filter[bundleId]"] = filter.bundleId;
|
|
1866
|
+
return paginatedList("/v1/apps", query);
|
|
1910
1867
|
},
|
|
1911
|
-
async
|
|
1912
|
-
const
|
|
1913
|
-
data: {
|
|
1914
|
-
type: "profiles",
|
|
1915
|
-
attributes: { name: args.name, profileType: args.profileType },
|
|
1916
|
-
relationships: {
|
|
1917
|
-
bundleId: { data: { type: "bundleIds", id: args.bundleIdResourceId } },
|
|
1918
|
-
certificates: {
|
|
1919
|
-
data: args.certificateIds.map((id) => ({ type: "certificates", id }))
|
|
1920
|
-
},
|
|
1921
|
-
...args.deviceIds && args.deviceIds.length > 0 ? {
|
|
1922
|
-
devices: {
|
|
1923
|
-
data: args.deviceIds.map((id) => ({ type: "devices", id }))
|
|
1924
|
-
}
|
|
1925
|
-
} : {}
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
};
|
|
1929
|
-
const res = await request("POST", "/v1/profiles", body);
|
|
1868
|
+
async get(id) {
|
|
1869
|
+
const res = await request("GET", `/v1/apps/${id}`);
|
|
1930
1870
|
return res.data;
|
|
1931
|
-
},
|
|
1932
|
-
async delete(id) {
|
|
1933
|
-
await request("DELETE", `/v1/profiles/${id}`);
|
|
1934
|
-
}
|
|
1935
|
-
},
|
|
1936
|
-
devices: {
|
|
1937
|
-
async list(filter) {
|
|
1938
|
-
const query = {};
|
|
1939
|
-
if (filter?.status) query["filter[status]"] = filter.status;
|
|
1940
|
-
return paginatedList("/v1/devices", query);
|
|
1941
1871
|
}
|
|
1942
1872
|
}
|
|
1943
1873
|
};
|
|
@@ -2367,6 +2297,36 @@ async function runServicesId(options2) {
|
|
|
2367
2297
|
return 1;
|
|
2368
2298
|
}
|
|
2369
2299
|
}
|
|
2300
|
+
async function loadAscCreds2() {
|
|
2301
|
+
const state = await load();
|
|
2302
|
+
const rec = state.steps["asc-key"];
|
|
2303
|
+
if (!rec?.outputs) return null;
|
|
2304
|
+
const out = rec.outputs;
|
|
2305
|
+
const issuerId = out.issuerId;
|
|
2306
|
+
const keyId = out.keyId;
|
|
2307
|
+
const rawPath = out.p8Path;
|
|
2308
|
+
if (!issuerId || !keyId || !rawPath) return null;
|
|
2309
|
+
const p8Path = expandTilde(rawPath);
|
|
2310
|
+
if (!existsSync(p8Path)) return null;
|
|
2311
|
+
return { issuerId, keyId, privateKey: { path: p8Path } };
|
|
2312
|
+
}
|
|
2313
|
+
async function ascBootstrap() {
|
|
2314
|
+
const creds = await loadAscCreds2();
|
|
2315
|
+
if (!creds) {
|
|
2316
|
+
throw new Error("no cached ASC creds. run `vexpo apple asc-key` first");
|
|
2317
|
+
}
|
|
2318
|
+
const client = makeAscClient(creds);
|
|
2319
|
+
const bundleId = await readOne("EXPO_PUBLIC_APP_BUNDLE_ID") ?? await readOne("APP_BUNDLE_ID") ?? void 0;
|
|
2320
|
+
let ascAppId;
|
|
2321
|
+
if (bundleId) {
|
|
2322
|
+
try {
|
|
2323
|
+
const apps = await client.paginatedList("/v1/apps", { "filter[bundleId]": bundleId }, 5);
|
|
2324
|
+
ascAppId = apps[0]?.id;
|
|
2325
|
+
} catch {
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
return { client, bundleId, ascAppId, creds };
|
|
2329
|
+
}
|
|
2370
2330
|
|
|
2371
2331
|
// src/lib/eas-submit.ts
|
|
2372
2332
|
function isObject(value) {
|
|
@@ -2402,6 +2362,16 @@ function withAscAppId(easJson, ascAppId) {
|
|
|
2402
2362
|
}
|
|
2403
2363
|
|
|
2404
2364
|
// src/commands/asc.ts
|
|
2365
|
+
async function ascAppExists(bundleId) {
|
|
2366
|
+
const creds = await loadAscCreds2();
|
|
2367
|
+
if (!creds) return "unknown";
|
|
2368
|
+
try {
|
|
2369
|
+
const apps = await makeAscClient(creds).apps.list({ bundleId });
|
|
2370
|
+
return apps.length > 0 ? "proceed" : "defer";
|
|
2371
|
+
} catch {
|
|
2372
|
+
return "unknown";
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2405
2375
|
async function loadAscFromState2() {
|
|
2406
2376
|
const state = await load();
|
|
2407
2377
|
const rec = state.steps["asc-key"];
|
|
@@ -2449,6 +2419,15 @@ async function runAscConnect(opts = {}) {
|
|
|
2449
2419
|
return 1;
|
|
2450
2420
|
}
|
|
2451
2421
|
ok(`bundle id: ${BOLD}${bundleId}${RESET}`);
|
|
2422
|
+
if (await ascAppExists(bundleId) === "defer") {
|
|
2423
|
+
yep("no App Store Connect app record for this bundle id yet, NOT connected");
|
|
2424
|
+
note("the ASC app record only appears after the first `eas submit`. run:");
|
|
2425
|
+
note(
|
|
2426
|
+
` ${BOLD}npx eas build -p ios --profile production --auto-submit-with-profile testflight${RESET}`
|
|
2427
|
+
);
|
|
2428
|
+
note("then re-run `npx vexpo asc:connect` to finish the EAS\u2194ASC link");
|
|
2429
|
+
return 0;
|
|
2430
|
+
}
|
|
2452
2431
|
if (!process.stdin.isTTY) {
|
|
2453
2432
|
bad("ASC connect needs a TTY: eas integrations:asc:connect can't run headless");
|
|
2454
2433
|
note("run `vexpo asc:connect` in an interactive terminal to finish the EAS\u2194ASC link");
|
|
@@ -2505,36 +2484,6 @@ async function runAscConnect(opts = {}) {
|
|
|
2505
2484
|
}
|
|
2506
2485
|
return 0;
|
|
2507
2486
|
}
|
|
2508
|
-
async function loadAscCreds2() {
|
|
2509
|
-
const state = await load();
|
|
2510
|
-
const rec = state.steps["asc-key"];
|
|
2511
|
-
if (!rec?.outputs) return null;
|
|
2512
|
-
const out = rec.outputs;
|
|
2513
|
-
const issuerId = out.issuerId;
|
|
2514
|
-
const keyId = out.keyId;
|
|
2515
|
-
const rawPath = out.p8Path;
|
|
2516
|
-
if (!issuerId || !keyId || !rawPath) return null;
|
|
2517
|
-
const p8Path = expandTilde(rawPath);
|
|
2518
|
-
if (!existsSync(p8Path)) return null;
|
|
2519
|
-
return { issuerId, keyId, privateKey: { path: p8Path } };
|
|
2520
|
-
}
|
|
2521
|
-
async function ascBootstrap() {
|
|
2522
|
-
const creds = await loadAscCreds2();
|
|
2523
|
-
if (!creds) {
|
|
2524
|
-
throw new Error("no cached ASC creds. run `vexpo apple asc-key` first");
|
|
2525
|
-
}
|
|
2526
|
-
const client = makeAscClient(creds);
|
|
2527
|
-
const bundleId = await readOne("EXPO_PUBLIC_APP_BUNDLE_ID") ?? await readOne("APP_BUNDLE_ID") ?? void 0;
|
|
2528
|
-
let ascAppId;
|
|
2529
|
-
if (bundleId) {
|
|
2530
|
-
try {
|
|
2531
|
-
const apps = await client.paginatedList("/v1/apps", { "filter[bundleId]": bundleId }, 5);
|
|
2532
|
-
ascAppId = apps[0]?.id;
|
|
2533
|
-
} catch {
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
return { client, bundleId, ascAppId, creds };
|
|
2537
|
-
}
|
|
2538
2487
|
|
|
2539
2488
|
// src/lib/asc-accessibility.ts
|
|
2540
2489
|
var ACCESSIBILITY_FEATURES = [
|
|
@@ -3167,6 +3116,9 @@ var ROUTING = {
|
|
|
3167
3116
|
RESEND_TEST_MODE: {
|
|
3168
3117
|
routes: (c) => [{ type: "convex", key: "RESEND_TEST_MODE", channel: c }]
|
|
3169
3118
|
},
|
|
3119
|
+
REQUIRE_EMAIL_VERIFICATION: {
|
|
3120
|
+
routes: (c) => [{ type: "convex", key: "REQUIRE_EMAIL_VERIFICATION", channel: c }]
|
|
3121
|
+
},
|
|
3170
3122
|
APP_BUNDLE_ID: { routes: (c) => [{ type: "convex", key: "APP_BUNDLE_ID", channel: c }] },
|
|
3171
3123
|
APPLE_CLIENT_ID: { routes: (c) => [{ type: "convex", key: "APPLE_CLIENT_ID", channel: c }] },
|
|
3172
3124
|
APPLE_CLIENT_SECRET: {
|
|
@@ -3934,7 +3886,7 @@ async function verifyEas(ctx) {
|
|
|
3934
3886
|
"eas",
|
|
3935
3887
|
"asc-submit-id",
|
|
3936
3888
|
`submit profile${missing.length === 1 ? "" : "s"} ${missing.join(", ")} missing ascAppId`,
|
|
3937
|
-
"run `vexpo asc` to write it; non-interactive `eas submit` (CI) fails without it"
|
|
3889
|
+
"run `vexpo asc:connect` to write it; non-interactive `eas submit` (CI) fails without it"
|
|
3938
3890
|
)
|
|
3939
3891
|
);
|
|
3940
3892
|
} else if (existsSync("eas.json")) {
|
|
@@ -4078,7 +4030,7 @@ async function readContext(channel) {
|
|
|
4078
4030
|
() => /* @__PURE__ */ new Map()
|
|
4079
4031
|
) : Promise.resolve(/* @__PURE__ */ new Map()),
|
|
4080
4032
|
readAppConfigFacts(),
|
|
4081
|
-
|
|
4033
|
+
loadAscCreds2()
|
|
4082
4034
|
]
|
|
4083
4035
|
);
|
|
4084
4036
|
return {
|
|
@@ -4111,21 +4063,6 @@ async function readAppConfigFacts() {
|
|
|
4111
4063
|
return {};
|
|
4112
4064
|
}
|
|
4113
4065
|
}
|
|
4114
|
-
async function loadAscCreds3() {
|
|
4115
|
-
try {
|
|
4116
|
-
const state = await load();
|
|
4117
|
-
const rec = state.steps["asc-key"];
|
|
4118
|
-
if (!rec?.outputs) return null;
|
|
4119
|
-
const out = rec.outputs;
|
|
4120
|
-
const issuerId = out.issuerId;
|
|
4121
|
-
const keyId = out.keyId;
|
|
4122
|
-
const p8Path = out.p8Path;
|
|
4123
|
-
if (!issuerId || !keyId || !p8Path) return null;
|
|
4124
|
-
return { issuerId, keyId, privateKey: { path: p8Path } };
|
|
4125
|
-
} catch {
|
|
4126
|
-
return null;
|
|
4127
|
-
}
|
|
4128
|
-
}
|
|
4129
4066
|
async function verifyAll(ctx) {
|
|
4130
4067
|
const [files, convex, resend, apple2, eas] = await Promise.all([
|
|
4131
4068
|
Promise.resolve(verifyFiles(ctx)),
|
|
@@ -4606,7 +4543,6 @@ async function runEnvPush(options2) {
|
|
|
4606
4543
|
return 2;
|
|
4607
4544
|
}
|
|
4608
4545
|
ok("nothing to do. all source values match destinations");
|
|
4609
|
-
await recordStep("accounts", { mode: "lite", verifiedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4610
4546
|
return 0;
|
|
4611
4547
|
}
|
|
4612
4548
|
const prodConvexWrites = entries.some(
|
|
@@ -4651,12 +4587,6 @@ async function runEnvPush(options2) {
|
|
|
4651
4587
|
return 1;
|
|
4652
4588
|
}
|
|
4653
4589
|
ok(`${appliedTotal} value${appliedTotal === 1 ? "" : "s"} synced`);
|
|
4654
|
-
await recordStep("accounts", {
|
|
4655
|
-
mode: "lite",
|
|
4656
|
-
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4657
|
-
sources: sources.map((s) => ({ path: s.path, channel: s.channel, keys: s.entries.size })),
|
|
4658
|
-
applied: appliedTotal
|
|
4659
|
-
});
|
|
4660
4590
|
if (!options2.noVerify) {
|
|
4661
4591
|
const haveProd = sources.some((s) => s.channel === "prod");
|
|
4662
4592
|
const verifyChannels = haveProd ? ["dev", "prod"] : ["dev"];
|
|
@@ -4716,19 +4646,21 @@ function bundleSlug(value) {
|
|
|
4716
4646
|
return value.toLowerCase().normalize("NFKD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "").slice(0, 32);
|
|
4717
4647
|
}
|
|
4718
4648
|
async function promptInputs(overrides) {
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4649
|
+
const interactive = process.stdin.isTTY === true;
|
|
4650
|
+
if (interactive) {
|
|
4651
|
+
line();
|
|
4652
|
+
note(
|
|
4653
|
+
`${DIM}4 prompts. Everything else is derived. Override any with flags or edit later.${RESET}`
|
|
4654
|
+
);
|
|
4655
|
+
line();
|
|
4656
|
+
}
|
|
4657
|
+
const appName2 = overrides.appName ?? (interactive ? (await ask(` ${BOLD}App name${RESET} ${DIM}(e.g. Foobar)${RESET} > `)).trim() : "");
|
|
4726
4658
|
if (!appName2) fail("app name required");
|
|
4727
4659
|
const defaultPkg = slug(appName2);
|
|
4728
4660
|
const bundleHint = `com.${slug(appName2).replace(/-/g, "")}.${bundleSlug(defaultPkg)}`;
|
|
4729
|
-
const bundleId = overrides.bundleId ?? ((await ask(` ${BOLD}Bundle ID${RESET} ${DIM}[${bundleHint}]${RESET} > `)).trim() || bundleHint);
|
|
4730
|
-
const ownerName = overrides.ownerName ?? ((await ask(` ${BOLD}Your name${RESET} > `)).trim() || "Owner");
|
|
4731
|
-
const reviewEmail = overrides.reviewEmail ?? (await ask(` ${BOLD}Apple review contact email${RESET} > `)).trim();
|
|
4661
|
+
const bundleId = overrides.bundleId ?? (interactive ? (await ask(` ${BOLD}Bundle ID${RESET} ${DIM}[${bundleHint}]${RESET} > `)).trim() || bundleHint : bundleHint);
|
|
4662
|
+
const ownerName = overrides.ownerName ?? (interactive ? (await ask(` ${BOLD}Your name${RESET} > `)).trim() || "Owner" : "Owner");
|
|
4663
|
+
const reviewEmail = overrides.reviewEmail ?? (interactive ? (await ask(` ${BOLD}Apple review contact email${RESET} > `)).trim() : "");
|
|
4732
4664
|
if (!reviewEmail) fail("review email required");
|
|
4733
4665
|
const packageName = overrides.packageName ?? defaultPkg;
|
|
4734
4666
|
const scheme2 = overrides.scheme ?? bundleSlug(packageName);
|
|
@@ -4759,6 +4691,20 @@ async function promptInputs(overrides) {
|
|
|
4759
4691
|
expoOwner
|
|
4760
4692
|
};
|
|
4761
4693
|
}
|
|
4694
|
+
async function syncBundleId(bundleId) {
|
|
4695
|
+
const env2 = await readAll();
|
|
4696
|
+
const current = env2.get("EXPO_PUBLIC_APP_BUNDLE_ID");
|
|
4697
|
+
if (current === bundleId) return;
|
|
4698
|
+
if (current !== void 0) await removeLines(["EXPO_PUBLIC_APP_BUNDLE_ID"]);
|
|
4699
|
+
await ensureLine("EXPO_PUBLIC_APP_BUNDLE_ID", bundleId);
|
|
4700
|
+
ok(`wrote EXPO_PUBLIC_APP_BUNDLE_ID=${bundleId} to .env.local`);
|
|
4701
|
+
if (env2.has("CONVEX_DEPLOYMENT")) {
|
|
4702
|
+
await envSet("APP_BUNDLE_ID", bundleId);
|
|
4703
|
+
ok(`Convex env: APP_BUNDLE_ID=${bundleId}`);
|
|
4704
|
+
} else {
|
|
4705
|
+
note("no Convex deployment yet; the next `vexpo convex` run carries APP_BUNDLE_ID");
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4762
4708
|
async function backup(files, stamp) {
|
|
4763
4709
|
const dir = `.rebrand-backup/${stamp}`;
|
|
4764
4710
|
await mkdir(dir, { recursive: true });
|
|
@@ -4960,6 +4906,7 @@ async function runRebrand(options2) {
|
|
|
4960
4906
|
await rewriteAppJson();
|
|
4961
4907
|
await rewritePackageJson(inputs);
|
|
4962
4908
|
await rewriteStoreConfig(inputs);
|
|
4909
|
+
await syncBundleId(inputs.bundleId);
|
|
4963
4910
|
if (inputs.expoOwner) {
|
|
4964
4911
|
await ensureLine("EXPO_PUBLIC_EXPO_OWNER", inputs.expoOwner);
|
|
4965
4912
|
ok(`wrote EXPO_PUBLIC_EXPO_OWNER=${inputs.expoOwner} to .env.local`);
|
|
@@ -5337,67 +5284,63 @@ async function runEas(options2) {
|
|
|
5337
5284
|
ok(`signed in as ${BOLD}${who}${RESET}`);
|
|
5338
5285
|
}
|
|
5339
5286
|
let projectId = await resolveProjectId();
|
|
5340
|
-
if (
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5287
|
+
if (projectId) {
|
|
5288
|
+
ok(`EAS project linked: ${projectId}`);
|
|
5289
|
+
} else {
|
|
5290
|
+
const result = await init();
|
|
5291
|
+
if (!result.ok) {
|
|
5292
|
+
bad("eas init failed");
|
|
5293
|
+
return 1;
|
|
5294
|
+
}
|
|
5295
|
+
projectId = result.projectId ?? null;
|
|
5296
|
+
ok(`EAS project created: ${projectId}`);
|
|
5297
|
+
}
|
|
5298
|
+
const channels = ["development", "preview", "production"];
|
|
5299
|
+
const createdChannels = await ensureChannels(channels);
|
|
5300
|
+
if (createdChannels.length > 0) ok(`channels created: ${createdChannels.join(", ")}`);
|
|
5301
|
+
else nop(`channels already exist (${channels.join(", ")})`);
|
|
5302
|
+
const branches = ["development", "preview", "production"];
|
|
5303
|
+
const createdBranches = await ensureBranches(branches);
|
|
5304
|
+
if (createdBranches.length > 0) ok(`branches created: ${createdBranches.join(", ")}`);
|
|
5305
|
+
else nop(`branches already exist (${branches.join(", ")})`);
|
|
5306
|
+
if (await fileExists7(".env.local")) {
|
|
5307
|
+
try {
|
|
5308
|
+
const pushed = await pushEasRoutedKeys(".env.local", ["development"]);
|
|
5309
|
+
if (pushed.length > 0) {
|
|
5310
|
+
ok(
|
|
5311
|
+
`pushed ${pushed.length} EXPO_PUBLIC_* var${pushed.length === 1 ? "" : "s"} \u2192 EAS env (development)`
|
|
5312
|
+
);
|
|
5313
|
+
} else {
|
|
5314
|
+
nop(".env.local has no EAS-routed keys yet (run `vexpo convex` first)");
|
|
5348
5315
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5316
|
+
} catch (err) {
|
|
5317
|
+
bad(err instanceof Error ? err.message : String(err));
|
|
5351
5318
|
}
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
const
|
|
5357
|
-
|
|
5358
|
-
if (createdBranches.length > 0) ok(`branches created: ${createdBranches.join(", ")}`);
|
|
5359
|
-
else nop(`branches already exist (${branches.join(", ")})`);
|
|
5360
|
-
}
|
|
5361
|
-
if (!options2.skipEnv) {
|
|
5362
|
-
if (await fileExists7(".env.local")) {
|
|
5319
|
+
} else {
|
|
5320
|
+
nop(".env.local missing. skipping development env push (run `vexpo convex` first)");
|
|
5321
|
+
}
|
|
5322
|
+
if (options2.withProd) {
|
|
5323
|
+
const prodFile = await fileExists7(".env.prod") ? ".env.prod" : await fileExists7(".env.production") ? ".env.production" : null;
|
|
5324
|
+
if (prodFile) {
|
|
5363
5325
|
try {
|
|
5364
|
-
const pushed = await pushEasRoutedKeys("
|
|
5326
|
+
const pushed = await pushEasRoutedKeys(prodFile, ["production", "preview"]);
|
|
5365
5327
|
if (pushed.length > 0) {
|
|
5366
5328
|
ok(
|
|
5367
|
-
`pushed ${pushed.length} EXPO_PUBLIC_* var${pushed.length === 1 ? "" : "s"} \u2192 EAS env (
|
|
5329
|
+
`pushed ${pushed.length} EXPO_PUBLIC_* var${pushed.length === 1 ? "" : "s"} \u2192 EAS env (production, preview)`
|
|
5368
5330
|
);
|
|
5369
5331
|
} else {
|
|
5370
|
-
nop(
|
|
5332
|
+
nop(`${prodFile} has no EAS-routed keys`);
|
|
5371
5333
|
}
|
|
5372
5334
|
} catch (err) {
|
|
5373
5335
|
bad(err instanceof Error ? err.message : String(err));
|
|
5374
5336
|
}
|
|
5375
5337
|
} else {
|
|
5376
|
-
nop("
|
|
5338
|
+
nop("--with-prod set but no .env.prod or .env.production found");
|
|
5377
5339
|
}
|
|
5378
|
-
if (options2.withProd) {
|
|
5379
|
-
const prodFile = await fileExists7(".env.prod") ? ".env.prod" : await fileExists7(".env.production") ? ".env.production" : null;
|
|
5380
|
-
if (prodFile) {
|
|
5381
|
-
try {
|
|
5382
|
-
const pushed = await pushEasRoutedKeys(prodFile, ["production", "preview"]);
|
|
5383
|
-
if (pushed.length > 0) {
|
|
5384
|
-
ok(
|
|
5385
|
-
`pushed ${pushed.length} EXPO_PUBLIC_* var${pushed.length === 1 ? "" : "s"} \u2192 EAS env (production, preview)`
|
|
5386
|
-
);
|
|
5387
|
-
} else {
|
|
5388
|
-
nop(`${prodFile} has no EAS-routed keys`);
|
|
5389
|
-
}
|
|
5390
|
-
} catch (err) {
|
|
5391
|
-
bad(err instanceof Error ? err.message : String(err));
|
|
5392
|
-
}
|
|
5393
|
-
} else {
|
|
5394
|
-
nop("--with-prod set but no .env.prod or .env.production found");
|
|
5395
|
-
}
|
|
5396
|
-
}
|
|
5397
|
-
note(
|
|
5398
|
-
`server-side secrets route to Convex, not EAS. run ${BOLD}vexpo env push${RESET} to sync those`
|
|
5399
|
-
);
|
|
5400
5340
|
}
|
|
5341
|
+
note(
|
|
5342
|
+
`server-side secrets route to Convex, not EAS. run ${BOLD}vexpo env push${RESET} to sync those`
|
|
5343
|
+
);
|
|
5401
5344
|
if (projectId) {
|
|
5402
5345
|
await recordStep("eas", {
|
|
5403
5346
|
projectId,
|
|
@@ -5556,7 +5499,7 @@ async function liveCheckEas() {
|
|
|
5556
5499
|
}
|
|
5557
5500
|
async function liveCheckAscLink() {
|
|
5558
5501
|
try {
|
|
5559
|
-
const { ascStatus: ascStatus2 } = await import('./eas-integrations-
|
|
5502
|
+
const { ascStatus: ascStatus2 } = await import('./eas-integrations-5FVP7DI6.js');
|
|
5560
5503
|
const status = await ascStatus2();
|
|
5561
5504
|
return status.status === "connected";
|
|
5562
5505
|
} catch {
|
|
@@ -5575,16 +5518,20 @@ async function liveCheckRotationSecrets() {
|
|
|
5575
5518
|
"CONVEX_DEPLOY_KEY"
|
|
5576
5519
|
].every((k) => eas.has(k));
|
|
5577
5520
|
}
|
|
5521
|
+
var LOCAL_ENV_LITE_CORE = [
|
|
5522
|
+
"CONVEX_DEPLOYMENT",
|
|
5523
|
+
"EXPO_PUBLIC_CONVEX_URL",
|
|
5524
|
+
"EXPO_PUBLIC_CONVEX_SITE_URL",
|
|
5525
|
+
"EXPO_PUBLIC_SITE_URL",
|
|
5526
|
+
"EXPO_PUBLIC_APP_BUNDLE_ID"
|
|
5527
|
+
];
|
|
5528
|
+
var LOCAL_ENV_TEAM_ID = "EXPO_PUBLIC_APPLE_TEAM_ID";
|
|
5529
|
+
function classifyLocalEnv(env2) {
|
|
5530
|
+
if (!LOCAL_ENV_LITE_CORE.every((k) => env2.has(k))) return "missing";
|
|
5531
|
+
return env2.has(LOCAL_ENV_TEAM_ID) ? "ok" : "partial";
|
|
5532
|
+
}
|
|
5578
5533
|
async function liveCheckLocalEnv() {
|
|
5579
|
-
|
|
5580
|
-
return [
|
|
5581
|
-
"CONVEX_DEPLOYMENT",
|
|
5582
|
-
"EXPO_PUBLIC_CONVEX_URL",
|
|
5583
|
-
"EXPO_PUBLIC_CONVEX_SITE_URL",
|
|
5584
|
-
"EXPO_PUBLIC_SITE_URL",
|
|
5585
|
-
"EXPO_PUBLIC_APP_BUNDLE_ID",
|
|
5586
|
-
"EXPO_PUBLIC_APPLE_TEAM_ID"
|
|
5587
|
-
].every((k) => env2.has(k));
|
|
5534
|
+
return classifyLocalEnv(await readAll());
|
|
5588
5535
|
}
|
|
5589
5536
|
async function stepPrerequisites() {
|
|
5590
5537
|
section("Prerequisites");
|
|
@@ -5605,12 +5552,17 @@ async function stepPrerequisites() {
|
|
|
5605
5552
|
async function stepProbe() {
|
|
5606
5553
|
section("Probe");
|
|
5607
5554
|
const installOk = await nodeModulesPresent();
|
|
5608
|
-
const
|
|
5609
|
-
const
|
|
5555
|
+
const localEnvState = await liveCheckLocalEnv();
|
|
5556
|
+
const convexLive = localEnvState !== "missing";
|
|
5557
|
+
const convex = convexLive ? await envMap() : /* @__PURE__ */ new Map();
|
|
5610
5558
|
const rows = /* @__PURE__ */ new Map();
|
|
5611
5559
|
rows.set("accounts", await shouldRun("accounts", async () => true));
|
|
5612
5560
|
rows.set("rebrand", await shouldRun("rebrand", async () => false));
|
|
5613
|
-
rows.set("convex", {
|
|
5561
|
+
rows.set("convex", {
|
|
5562
|
+
step: "convex",
|
|
5563
|
+
label: "convex",
|
|
5564
|
+
status: convexLive ? "live" : "missing"
|
|
5565
|
+
});
|
|
5614
5566
|
rows.set("better-auth", await shouldRun("better-auth", () => liveCheckBetterAuth(convex)));
|
|
5615
5567
|
rows.set("resend", await shouldRun("resend", () => liveCheckResend(convex)));
|
|
5616
5568
|
rows.set("asc-key", await shouldRun("asc-key", async () => false));
|
|
@@ -5631,9 +5583,8 @@ async function stepProbe() {
|
|
|
5631
5583
|
line(
|
|
5632
5584
|
` ${BOLD}${"node_modules".padEnd(w)}${RESET} ${installOk ? `${GREEN}ok${RESET}` : `${RED}missing${RESET}`}`
|
|
5633
5585
|
);
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
);
|
|
5586
|
+
const localEnvMark = localEnvState === "ok" ? `${GREEN}ok${RESET}` : localEnvState === "partial" ? `${YELLOW}partial (lite)${RESET}` : `${RED}missing${RESET}`;
|
|
5587
|
+
line(` ${BOLD}${".env.local".padEnd(w)}${RESET} ${localEnvMark}`);
|
|
5637
5588
|
for (const [key, row] of rows) {
|
|
5638
5589
|
const label = key === "convex" ? "Convex / .env.local" : key === "better-auth" ? "Better Auth" : key === "resend" ? "Resend" : key === "asc-key" ? "ASC API key" : key === "apple-services-id" ? "Sign In Services ID" : key === "apple-sign-in" ? "Sign In JWT" : key === "apple-credentials" ? "EAS iOS credentials" : key === "apple-asc-link" ? "EAS \u2194 ASC link" : key === "apple-eas-rotation-secrets" ? "EAS rotation secrets" : key === "eas" ? "EAS project + env" : key === "rebrand" ? "Rebrand" : key === "accounts" ? "Accounts" : key;
|
|
5639
5590
|
line(` ${BOLD}${label.padEnd(w)}${RESET} ${mark(row.status)}`);
|
|
@@ -5643,7 +5594,7 @@ async function stepProbe() {
|
|
|
5643
5594
|
);
|
|
5644
5595
|
const needs = /* @__PURE__ */ new Map();
|
|
5645
5596
|
for (const [k, row] of rows) needs.set(k, row.status === "missing");
|
|
5646
|
-
return { rows, needs, install: !installOk, localEnv:
|
|
5597
|
+
return { rows, needs, install: !installOk, localEnv: localEnvState };
|
|
5647
5598
|
}
|
|
5648
5599
|
async function describePhase(step, probe) {
|
|
5649
5600
|
const status = probe.rows.get(step)?.status;
|
|
@@ -6096,13 +6047,13 @@ async function runStep(name, state) {
|
|
|
6096
6047
|
}
|
|
6097
6048
|
if (state) completed.push(state);
|
|
6098
6049
|
}
|
|
6099
|
-
async function maybeRunStep(name, prompt, state) {
|
|
6050
|
+
async function maybeRunStep(name, prompt, state, defaultYes = true) {
|
|
6100
6051
|
if (!process.stdin.isTTY) {
|
|
6101
6052
|
nop(`non-TTY: skipping ${name} (run \`${name}\` later)`);
|
|
6102
6053
|
if (state) skipped.push(state);
|
|
6103
6054
|
return;
|
|
6104
6055
|
}
|
|
6105
|
-
if (!await askYesNo(prompt,
|
|
6056
|
+
if (!await askYesNo(prompt, defaultYes)) {
|
|
6106
6057
|
nop(`skipped ${name} (run \`${name}\` later)`);
|
|
6107
6058
|
if (state) skipped.push(state);
|
|
6108
6059
|
return;
|
|
@@ -6124,7 +6075,7 @@ function printShipNextSteps() {
|
|
|
6124
6075
|
}
|
|
6125
6076
|
async function stepExpoDoctor() {
|
|
6126
6077
|
section("expo-doctor");
|
|
6127
|
-
const { dlx: dlx2 } = await import('./pkg-manager-
|
|
6078
|
+
const { dlx: dlx2 } = await import('./pkg-manager-DOOC6W2C.js');
|
|
6128
6079
|
const { code, stdout, stderr } = await run([dlx2(), "expo-doctor"]);
|
|
6129
6080
|
if (stdout.trim()) process.stderr.write(stdout);
|
|
6130
6081
|
if (stderr.trim()) process.stderr.write(stderr);
|
|
@@ -6370,8 +6321,9 @@ async function runSetup(opts) {
|
|
|
6370
6321
|
nop("vexpo apple services-id cached");
|
|
6371
6322
|
}
|
|
6372
6323
|
const status = probe.rows.get("apple-sign-in")?.status;
|
|
6373
|
-
const
|
|
6374
|
-
|
|
6324
|
+
const healthy = status === "live" || status === "cached";
|
|
6325
|
+
const prompt = healthy ? "Apple Sign In is configured, rotate the JWT now?" : "Sign the Apple Sign In JWT now?";
|
|
6326
|
+
await maybeRunStep("vexpo apple jwt", prompt, "apple-sign-in", !healthy);
|
|
6375
6327
|
if (options.force || probe.needs.get("apple-eas-rotation-secrets")) {
|
|
6376
6328
|
await maybeRunStep(
|
|
6377
6329
|
"vexpo apple eas-rotation-secrets",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { currentRuntime, currentRuntimeVersion, detectPackageManager, dlx,
|
|
2
|
+
export { currentRuntime, currentRuntimeVersion, detectPackageManager, dlx, installCmdFor } from './chunk-3RDUQUJW.js';
|
package/package.json
CHANGED
package/dist/proc-TEOPRZZ2.js
DELETED