anhedral 0.10.1 → 0.11.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.
- package/README.md +77 -38
- package/assets/{anhedral-cli-init-technical.svg → anhedral-stack-technical.svg} +6 -6
- package/assets/{anhedral-cli-init.svg → anhedral-stack.svg} +7 -7
- package/dist/bin.js +271 -154
- package/dist/capabilities.d.ts +3 -3
- package/dist/capability-contract.d.ts +47 -0
- package/dist/capability-contract.js +136 -0
- package/dist/cli-arguments.d.ts +16 -0
- package/dist/cli-arguments.js +115 -0
- package/dist/cli-output.d.ts +8 -0
- package/dist/cli-output.js +211 -0
- package/dist/cloudflare.d.ts +7 -2
- package/dist/cloudflare.js +166 -36
- package/dist/command-schema.d.ts +21 -0
- package/dist/command-schema.js +374 -0
- package/dist/delivery.d.ts +2 -2
- package/dist/delivery.js +21 -4
- package/dist/index.d.ts +23 -1
- package/dist/index.js +15 -1
- package/dist/inspector-model.d.ts +115 -0
- package/dist/inspector-model.js +1 -0
- package/dist/lifecycle-model.d.ts +15 -0
- package/dist/lifecycle-model.js +20 -0
- package/dist/lifecycle.d.ts +12 -0
- package/dist/lifecycle.js +92 -0
- package/dist/mobile-delivery.d.ts +100 -0
- package/dist/mobile-delivery.js +319 -0
- package/dist/platforms/mobile.js +8 -5
- package/dist/project-inspector.d.ts +4 -0
- package/dist/project-inspector.js +646 -0
- package/dist/project-state.d.ts +17 -0
- package/dist/project-state.js +239 -11
- package/dist/provider-auth.d.ts +41 -0
- package/dist/provider-auth.js +210 -0
- package/dist/providers/adapters.d.ts +17 -0
- package/dist/providers/adapters.js +269 -0
- package/dist/providers/api.d.ts +28 -0
- package/dist/providers/api.js +252 -0
- package/dist/providers/config.d.ts +15 -0
- package/dist/providers/config.js +184 -0
- package/dist/providers/definitions.d.ts +318 -0
- package/dist/providers/definitions.js +127 -0
- package/dist/providers/provision.d.ts +24 -0
- package/dist/providers/provision.js +76 -0
- package/dist/providers/receipts.d.ts +20 -0
- package/dist/providers/receipts.js +66 -0
- package/dist/providers/recovery-verification.d.ts +7 -0
- package/dist/providers/recovery-verification.js +112 -0
- package/dist/providers/recovery.d.ts +30 -0
- package/dist/providers/recovery.js +34 -0
- package/dist/providers/runtime.d.ts +10 -0
- package/dist/providers/runtime.js +231 -0
- package/dist/providers/schema.d.ts +28 -0
- package/dist/providers/schema.js +49 -0
- package/dist/providers/state.d.ts +56 -0
- package/dist/providers/state.js +329 -0
- package/dist/provision.d.ts +1 -1
- package/dist/provision.js +61 -3
- package/dist/registry/add.d.ts +6 -0
- package/dist/registry/add.js +29 -1
- package/dist/registry/commands.d.ts +3 -2
- package/dist/registry/commands.js +53 -14
- package/dist/registry/options.js +3 -2
- package/dist/registry/preview.d.ts +11 -0
- package/dist/registry/preview.js +91 -0
- package/dist/registry/usage.d.ts +10 -0
- package/dist/registry/usage.js +22 -37
- package/dist/scaffold/auth.js +3 -19
- package/dist/scaffold/workspace.js +11 -0
- package/dist/ship.d.ts +320 -0
- package/dist/ship.js +211 -0
- package/dist/stack-registry.d.ts +2 -2
- package/dist/stack-registry.js +7 -1
- package/dist/standard.d.ts +2 -1
- package/dist/standard.js +11 -11
- package/dist/vercel.d.ts +3 -3
- package/dist/vercel.js +27 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/application-stack-standard.md +51 -80
- package/docs/cli-design.md +67 -0
- package/docs/delivery-workflow.md +29 -15
- package/docs/plugin-ui.md +61 -0
- package/docs/provider-configuration.md +117 -0
- package/package.json +16 -12
- package/dist/managed-hosting.d.ts +0 -49
- package/dist/managed-hosting.js +0 -373
- package/docs/managed-hosting-architecture.md +0 -87
- package/docs/sponsorships.md +0 -25
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { realpathSync, existsSync, writeFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { EAS_CLI_VERSION } from './dependencies.js';
|
|
6
|
+
import { readBoundedFile, MAX_PROJECT_JSON_BYTES } from './bounded-file.js';
|
|
7
|
+
import { requiredProviders } from './provider-auth.js';
|
|
8
|
+
import { planProviderProvisioning, provisionSelectedProviders } from './providers/provision.js';
|
|
9
|
+
import { configuredRuntime, runtimeTargets, RUNTIME_KEYS } from './providers/runtime.js';
|
|
10
|
+
import { CREDENTIALS } from './providers/api.js';
|
|
11
|
+
import { atomicJson, defaultRunner } from './project-state.js';
|
|
12
|
+
import { withLifecycleOperation } from './lifecycle.js';
|
|
13
|
+
function artifactUrl(value) {
|
|
14
|
+
if (typeof value !== 'string')
|
|
15
|
+
return undefined;
|
|
16
|
+
try {
|
|
17
|
+
const url = new URL(value);
|
|
18
|
+
if (url.protocol !== 'https:' || url.username || url.password)
|
|
19
|
+
return undefined;
|
|
20
|
+
return url.href;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function pendingBuild(root, commit, options, projectId) {
|
|
27
|
+
const file = path.join(root, '.anhedral/mobile-build.pending.json');
|
|
28
|
+
if (!existsSync(file))
|
|
29
|
+
return undefined;
|
|
30
|
+
let pending;
|
|
31
|
+
try {
|
|
32
|
+
pending = JSON.parse(readBoundedFile(file, MAX_PROJECT_JSON_BYTES).toString('utf8'));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error('Invalid pending EAS build. Inspect the selected project before retrying.');
|
|
36
|
+
}
|
|
37
|
+
if (!pending ||
|
|
38
|
+
typeof pending !== 'object' ||
|
|
39
|
+
Array.isArray(pending) ||
|
|
40
|
+
pending.schemaVersion !== 1 ||
|
|
41
|
+
Object.keys(pending).some((key) => ![
|
|
42
|
+
'schemaVersion',
|
|
43
|
+
'status',
|
|
44
|
+
'commit',
|
|
45
|
+
'platform',
|
|
46
|
+
'profile',
|
|
47
|
+
'projectId',
|
|
48
|
+
'buildId',
|
|
49
|
+
].includes(key)) ||
|
|
50
|
+
![undefined, 'submitting', 'queued'].includes(pending.status) ||
|
|
51
|
+
pending.commit !== commit ||
|
|
52
|
+
pending.platform !== options.platform ||
|
|
53
|
+
pending.profile !== options.profile ||
|
|
54
|
+
pending.projectId !== projectId ||
|
|
55
|
+
(pending.status === 'submitting'
|
|
56
|
+
? Object.hasOwn(pending, 'buildId')
|
|
57
|
+
: typeof pending.buildId !== 'string' || !/^[a-zA-Z0-9-]{1,255}$/.test(pending.buildId)))
|
|
58
|
+
throw new Error('Pending EAS build differs from the selected source or profile.');
|
|
59
|
+
if (pending.status === 'submitting')
|
|
60
|
+
throw new Error('EAS submission has an unknown outcome. Reconcile the selected project before retrying.');
|
|
61
|
+
return pending;
|
|
62
|
+
}
|
|
63
|
+
/** EAS builds produce artifacts; store submission is a separate user action. */
|
|
64
|
+
export async function buildMobileApplication(root, options, run = defaultRunner) {
|
|
65
|
+
if (options.dryRun)
|
|
66
|
+
return executeMobileBuild(root, options, run);
|
|
67
|
+
// Validate local build configuration before creating an execution record.
|
|
68
|
+
await executeMobileBuild(root, { ...options, dryRun: true }, run);
|
|
69
|
+
return withLifecycleOperation(root, 'mobile', run, (session) => session.phase('mobile-build', () => executeMobileBuild(root, options, run)));
|
|
70
|
+
}
|
|
71
|
+
async function executeMobileBuild(root, options, run = defaultRunner) {
|
|
72
|
+
root = realpathSync(root);
|
|
73
|
+
if (!['ios', 'android'].includes(options.platform) ||
|
|
74
|
+
!/^[a-z][a-z0-9-]{0,63}$/.test(options.profile))
|
|
75
|
+
throw new Error('Select an explicit mobile platform and build profile.');
|
|
76
|
+
if (!requiredProviders(root).some((provider) => provider.id === 'expo'))
|
|
77
|
+
throw new Error('This project does not select Expo.');
|
|
78
|
+
const directory = realpathSync(path.join(root, 'apps/mobile'));
|
|
79
|
+
if (!directory.startsWith(root + path.sep))
|
|
80
|
+
throw new Error('Mobile app escapes project directory.');
|
|
81
|
+
let configuration;
|
|
82
|
+
try {
|
|
83
|
+
configuration = JSON.parse(readBoundedFile(path.join(directory, 'eas.json'), MAX_PROJECT_JSON_BYTES).toString('utf8'));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new Error('A valid EAS build configuration is required.');
|
|
87
|
+
}
|
|
88
|
+
const profile = configuration?.build?.[options.profile];
|
|
89
|
+
if (!profile || typeof profile !== 'object' || Array.isArray(profile))
|
|
90
|
+
throw new Error('Requested EAS profile is not configured.');
|
|
91
|
+
if (profile.extends)
|
|
92
|
+
throw new Error('Resolve inherited EAS profiles before automatic delivery.');
|
|
93
|
+
const selectedEnvironment = profile.environment ??
|
|
94
|
+
(profile.developmentClient
|
|
95
|
+
? 'development'
|
|
96
|
+
: profile.distribution === 'internal'
|
|
97
|
+
? 'preview'
|
|
98
|
+
: 'production');
|
|
99
|
+
if (!['development', 'preview', 'production'].includes(selectedEnvironment))
|
|
100
|
+
throw new Error('Select an explicit supported EAS environment.');
|
|
101
|
+
const args = [
|
|
102
|
+
'dlx',
|
|
103
|
+
`eas-cli@${EAS_CLI_VERSION}`,
|
|
104
|
+
'build',
|
|
105
|
+
'--platform',
|
|
106
|
+
options.platform,
|
|
107
|
+
'--profile',
|
|
108
|
+
options.profile,
|
|
109
|
+
'--non-interactive',
|
|
110
|
+
'--json',
|
|
111
|
+
'--no-wait',
|
|
112
|
+
];
|
|
113
|
+
if (options.dryRun)
|
|
114
|
+
return {
|
|
115
|
+
platform: options.platform,
|
|
116
|
+
profile: options.profile,
|
|
117
|
+
command: ['pnpm', ...args],
|
|
118
|
+
mutation: false,
|
|
119
|
+
storeSubmission: false,
|
|
120
|
+
integrations: planProviderProvisioning(root),
|
|
121
|
+
};
|
|
122
|
+
const app = JSON.parse(readBoundedFile(path.join(directory, 'app.json'), MAX_PROJECT_JSON_BYTES).toString('utf8')).expo;
|
|
123
|
+
if (['app.config.js', 'app.config.ts'].some((file) => existsSync(path.join(directory, file))))
|
|
124
|
+
throw new Error('Automatic EAS setup requires a static app.json with explicit owner and slug.');
|
|
125
|
+
if (typeof app?.owner !== 'string' ||
|
|
126
|
+
!/^[a-zA-Z0-9_-]+$/.test(app.owner) ||
|
|
127
|
+
typeof app.slug !== 'string' ||
|
|
128
|
+
!/^[a-zA-Z0-9_-]+$/.test(app.slug))
|
|
129
|
+
throw new Error('Set the intended Expo owner and slug in app.json before shipping.');
|
|
130
|
+
const environment = { ...process.env, CI: '1', NO_COLOR: '1' };
|
|
131
|
+
// Expo keeps its own login credential; server/provider keys never reach native builds.
|
|
132
|
+
for (const key of [...Object.values(CREDENTIALS), ...RUNTIME_KEYS])
|
|
133
|
+
if (!key.startsWith('EXPO_PUBLIC_'))
|
|
134
|
+
delete environment[key];
|
|
135
|
+
const source = async () => {
|
|
136
|
+
if (realpathSync((await run('git', ['rev-parse', '--show-toplevel'], root, environment)).trim()) !== root ||
|
|
137
|
+
(await run('git', ['status', '--porcelain'], root, environment)).trim())
|
|
138
|
+
throw new Error('Mobile delivery requires a clean application Git repository.');
|
|
139
|
+
const commit = (await run('git', ['rev-parse', 'HEAD'], root, environment)).trim();
|
|
140
|
+
if (!/^[a-f0-9]{40,64}$/.test(commit))
|
|
141
|
+
throw new Error('Invalid mobile source commit.');
|
|
142
|
+
return commit;
|
|
143
|
+
};
|
|
144
|
+
const initialCommit = await source();
|
|
145
|
+
// An unresolved queue mutation blocks every subsequent provisioning/configuration mutation.
|
|
146
|
+
pendingBuild(root, initialCommit, options, app.extra?.eas?.projectId);
|
|
147
|
+
await run('pnpm', ['run', 'check'], root, environment);
|
|
148
|
+
if ((await source()) !== initialCommit)
|
|
149
|
+
throw new Error('Source changed during mobile checks.');
|
|
150
|
+
const configured = await provisionSelectedProviders(root, false, run);
|
|
151
|
+
if (!configured.ready)
|
|
152
|
+
return {
|
|
153
|
+
state: 'provider-configuration-required',
|
|
154
|
+
deployed: false,
|
|
155
|
+
integrations: configured,
|
|
156
|
+
storeSubmission: false,
|
|
157
|
+
};
|
|
158
|
+
const runtime = configuredRuntime(root);
|
|
159
|
+
const commit = await source();
|
|
160
|
+
if (commit !== initialCommit)
|
|
161
|
+
throw new Error('Source changed during provider configuration.');
|
|
162
|
+
const cli = ['dlx', `eas-cli@${EAS_CLI_VERSION}`];
|
|
163
|
+
if (!app.extra?.eas?.projectId) {
|
|
164
|
+
await run('pnpm', [...cli, 'project:init', '--non-interactive', '--force'], directory, environment);
|
|
165
|
+
const linked = JSON.parse(readBoundedFile(path.join(directory, 'app.json'), MAX_PROJECT_JSON_BYTES).toString('utf8')).expo;
|
|
166
|
+
if (linked?.owner !== app.owner ||
|
|
167
|
+
linked.slug !== app.slug ||
|
|
168
|
+
typeof linked.extra?.eas?.projectId !== 'string' ||
|
|
169
|
+
!/^[a-f0-9-]{36}$/i.test(linked.extra.eas.projectId))
|
|
170
|
+
throw new Error('EAS did not confirm a project link for the selected owner and slug. Inspect the account before retrying.');
|
|
171
|
+
return {
|
|
172
|
+
state: 'configuration-required',
|
|
173
|
+
deployed: false,
|
|
174
|
+
storeSubmission: false,
|
|
175
|
+
nextAction: 'Review and commit the EAS project link written to app.json, then run ship again.',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const projectId = app.extra.eas.projectId;
|
|
179
|
+
if (typeof projectId !== 'string' || !/^[a-f0-9-]{36}$/i.test(projectId))
|
|
180
|
+
throw new Error('Invalid EAS project ID.');
|
|
181
|
+
const remote = await run('pnpm', [...cli, 'project:info'], directory, environment);
|
|
182
|
+
const remoteName = remote
|
|
183
|
+
.split(/\r?\n/)
|
|
184
|
+
.find((line) => /^fullName\s+/.test(line.trim()))
|
|
185
|
+
?.trim()
|
|
186
|
+
.split(/\s+/)[1];
|
|
187
|
+
const remoteId = remote
|
|
188
|
+
.split(/\r?\n/)
|
|
189
|
+
.find((line) => /^ID\s+/.test(line.trim()))
|
|
190
|
+
?.trim()
|
|
191
|
+
.split(/\s+/)[1];
|
|
192
|
+
if (remoteId !== projectId || remoteName !== `@${app.owner}/${app.slug}`)
|
|
193
|
+
throw new Error('EAS project identity differs from the selected owner and project.');
|
|
194
|
+
const values = runtimeTargets(root, runtime)['apps/mobile/.env.local'];
|
|
195
|
+
if (values && Object.keys(values).length) {
|
|
196
|
+
const file = path.join(tmpdir(), `anhedral-expo-${randomUUID()}.env`);
|
|
197
|
+
try {
|
|
198
|
+
writeFileSync(file, Object.entries(values)
|
|
199
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}\n`)
|
|
200
|
+
.join(''), { mode: 0o600, flag: 'wx' });
|
|
201
|
+
await run('pnpm', [...cli, 'env:push', '--environment', selectedEnvironment, '--path', file, '--force'], directory, environment);
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
rmSync(file, { force: true });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
let builds;
|
|
208
|
+
let expectedBuildId;
|
|
209
|
+
const pendingFile = path.join(root, '.anhedral/mobile-build.pending.json');
|
|
210
|
+
try {
|
|
211
|
+
const pending = pendingBuild(root, commit, options, projectId);
|
|
212
|
+
if (pending) {
|
|
213
|
+
const buildId = pending.buildId;
|
|
214
|
+
if (!buildId)
|
|
215
|
+
throw new Error('Pending EAS build has no verified ID.');
|
|
216
|
+
expectedBuildId = buildId;
|
|
217
|
+
builds = [
|
|
218
|
+
JSON.parse(await run('pnpm', [...cli, 'build:view', buildId, '--json'], directory, environment)),
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
if ((await source()) !== commit)
|
|
223
|
+
throw new Error('Source changed before EAS build submission.');
|
|
224
|
+
// Record intent before the remote queue mutation, including lost or invalid responses.
|
|
225
|
+
atomicJson(pendingFile, {
|
|
226
|
+
schemaVersion: 1,
|
|
227
|
+
status: 'submitting',
|
|
228
|
+
commit,
|
|
229
|
+
platform: options.platform,
|
|
230
|
+
profile: options.profile,
|
|
231
|
+
projectId,
|
|
232
|
+
});
|
|
233
|
+
builds = JSON.parse(await run('pnpm', args, directory, { ...environment, ...values }));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
throw new Error('EAS build did not return verified completion; inspect the selected Expo project before retrying.');
|
|
238
|
+
}
|
|
239
|
+
if (!Array.isArray(builds) || builds.length !== 1)
|
|
240
|
+
throw new Error('EAS returned an unexpected build selection.');
|
|
241
|
+
const build = builds[0];
|
|
242
|
+
const downloadUrl = artifactUrl(build?.artifacts?.applicationArchiveUrl ?? build?.artifacts?.buildUrl);
|
|
243
|
+
if (typeof build?.id !== 'string' || !/^[a-zA-Z0-9-]{1,255}$/.test(build.id))
|
|
244
|
+
throw new Error('EAS did not return a valid build ID.');
|
|
245
|
+
if (build.project?.id !== projectId ||
|
|
246
|
+
build.project?.ownerAccount?.name !== app.owner ||
|
|
247
|
+
build.project?.slug !== app.slug)
|
|
248
|
+
throw new Error('EAS build belongs to a different project.');
|
|
249
|
+
if (build.platform !== options.platform.toUpperCase() ||
|
|
250
|
+
build.buildProfile !== options.profile ||
|
|
251
|
+
(build.environment !== undefined && build.environment !== selectedEnvironment) ||
|
|
252
|
+
(options.platform === 'ios' && build.isForIosSimulator !== (profile.ios?.simulator === true)))
|
|
253
|
+
throw new Error('EAS build platform, profile or environment differs from the selected configuration.');
|
|
254
|
+
if (build.gitCommitHash !== commit)
|
|
255
|
+
throw new Error('EAS build source differs from the selected source commit.');
|
|
256
|
+
if (expectedBuildId && build.id !== expectedBuildId)
|
|
257
|
+
throw new Error('EAS returned a different build than the recorded selection.');
|
|
258
|
+
atomicJson(pendingFile, {
|
|
259
|
+
schemaVersion: 1,
|
|
260
|
+
status: 'queued',
|
|
261
|
+
commit,
|
|
262
|
+
platform: options.platform,
|
|
263
|
+
profile: options.profile,
|
|
264
|
+
projectId,
|
|
265
|
+
buildId: build.id,
|
|
266
|
+
});
|
|
267
|
+
const buildPage = `https://expo.dev/accounts/${app.owner}/projects/${app.slug}/builds/${build.id}`;
|
|
268
|
+
if (['NEW', 'IN_QUEUE', 'IN_PROGRESS', 'PENDING_CANCEL'].includes(build.status))
|
|
269
|
+
return {
|
|
270
|
+
schemaVersion: 1,
|
|
271
|
+
state: 'building',
|
|
272
|
+
buildId: build.id,
|
|
273
|
+
buildPageUrl: buildPage,
|
|
274
|
+
commit,
|
|
275
|
+
platform: options.platform,
|
|
276
|
+
profile: options.profile,
|
|
277
|
+
storeSubmission: false,
|
|
278
|
+
nextAction: 'Run the same ship command again to inspect this build without creating another build.',
|
|
279
|
+
};
|
|
280
|
+
if (build.status !== 'FINISHED' || !downloadUrl)
|
|
281
|
+
throw new Error('EAS has not confirmed a completed downloadable build. Inspect the recorded build before retrying.');
|
|
282
|
+
if ((await source()) !== commit)
|
|
283
|
+
throw new Error('Source changed during mobile build; do not treat this artifact as the current release.');
|
|
284
|
+
const owner = build.project?.ownerAccount?.name;
|
|
285
|
+
const slug = build.project?.slug;
|
|
286
|
+
const buildPageUrl = typeof owner === 'string' &&
|
|
287
|
+
/^[a-zA-Z0-9_-]+$/.test(owner) &&
|
|
288
|
+
typeof slug === 'string' &&
|
|
289
|
+
/^[a-zA-Z0-9_-]+$/.test(slug)
|
|
290
|
+
? `https://expo.dev/accounts/${owner}/projects/${slug}/builds/${build.id}`
|
|
291
|
+
: undefined;
|
|
292
|
+
const result = {
|
|
293
|
+
schemaVersion: 1,
|
|
294
|
+
projectId,
|
|
295
|
+
owner: app.owner,
|
|
296
|
+
slug: app.slug,
|
|
297
|
+
recordedAt: new Date().toISOString(),
|
|
298
|
+
...(buildPageUrl ? { buildPageUrl } : {}),
|
|
299
|
+
commit,
|
|
300
|
+
platform: options.platform,
|
|
301
|
+
profile: options.profile,
|
|
302
|
+
buildId: build.id,
|
|
303
|
+
downloadUrl,
|
|
304
|
+
state: 'built',
|
|
305
|
+
storeSubmission: false,
|
|
306
|
+
artifact: options.platform === 'ios'
|
|
307
|
+
? profile.ios?.simulator === true
|
|
308
|
+
? 'ios-simulator'
|
|
309
|
+
: 'ipa'
|
|
310
|
+
: profile.android?.buildType === 'apk' ||
|
|
311
|
+
profile.distribution === 'internal' ||
|
|
312
|
+
profile.developmentClient === true
|
|
313
|
+
? 'apk'
|
|
314
|
+
: 'aab',
|
|
315
|
+
};
|
|
316
|
+
atomicJson(path.join(root, '.anhedral/mobile-build.json'), result);
|
|
317
|
+
rmSync(pendingFile, { force: true });
|
|
318
|
+
return result;
|
|
319
|
+
}
|
package/dist/platforms/mobile.js
CHANGED
|
@@ -107,12 +107,11 @@ export async function scaffoldExpo({ root, options }) {
|
|
|
107
107
|
build: 'pnpm typecheck && pnpm build:web',
|
|
108
108
|
'build:web': 'expo export --platform web',
|
|
109
109
|
'eas:login': `pnpm dlx eas-cli@${EAS_CLI_VERSION} login`,
|
|
110
|
+
'build:simulator:ios': `pnpm dlx eas-cli@${EAS_CLI_VERSION} build --platform ios --profile simulator`,
|
|
110
111
|
'build:internal:ios': `pnpm dlx eas-cli@${EAS_CLI_VERSION} build --platform ios --profile preview`,
|
|
111
112
|
'build:internal:android': `pnpm dlx eas-cli@${EAS_CLI_VERSION} build --platform android --profile preview`,
|
|
112
113
|
'build:production:ios': `pnpm dlx eas-cli@${EAS_CLI_VERSION} build --platform ios --profile production`,
|
|
113
114
|
'build:production:android': `pnpm dlx eas-cli@${EAS_CLI_VERSION} build --platform android --profile production`,
|
|
114
|
-
'submit:ios': `pnpm dlx eas-cli@${EAS_CLI_VERSION} submit --platform ios --profile production --latest`,
|
|
115
|
-
'submit:android': `pnpm dlx eas-cli@${EAS_CLI_VERSION} submit --platform android --profile production --latest`,
|
|
116
115
|
typecheck: 'tsc --noEmit',
|
|
117
116
|
lint: 'eslint app components lib --max-warnings 0',
|
|
118
117
|
},
|
|
@@ -194,10 +193,14 @@ const styles = StyleSheet.create({
|
|
|
194
193
|
cli: { version: '>= 16.0.0' },
|
|
195
194
|
build: {
|
|
196
195
|
development: { developmentClient: true, distribution: 'internal' },
|
|
197
|
-
preview: {
|
|
198
|
-
|
|
196
|
+
preview: {
|
|
197
|
+
environment: 'preview',
|
|
198
|
+
distribution: 'internal',
|
|
199
|
+
android: { buildType: 'apk' },
|
|
200
|
+
},
|
|
201
|
+
simulator: { environment: 'preview', ios: { simulator: true } },
|
|
202
|
+
production: { environment: 'production', autoIncrement: true },
|
|
199
203
|
},
|
|
200
|
-
submit: { production: {} },
|
|
201
204
|
}, null, 2) + '\n');
|
|
202
205
|
appendGitignore(dir, ['.env', '.env.*', '!.env.example', '.expo', 'dist', 'node_modules']);
|
|
203
206
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProjectInspection } from './inspector-model.js';
|
|
2
|
+
/** Local read-only inspection. Each section has bounded allowlisted inputs and an
|
|
3
|
+
* independent error state. No secrets, commands, provider calls, or migrations. */
|
|
4
|
+
export declare function inspectApplication(root: string): ProjectInspection;
|