eas-cli 18.9.0 → 18.10.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 +223 -93
- package/build/build/utils/repository.js +7 -3
- package/build/commandUtils/convex.d.ts +11 -0
- package/build/commandUtils/convex.js +71 -0
- package/build/commands/build/download.js +1 -0
- package/build/commands/build/resign.js +0 -1
- package/build/commands/integrations/convex/connect.d.ts +24 -0
- package/build/commands/integrations/convex/connect.js +258 -0
- package/build/commands/integrations/convex/dashboard.d.ts +9 -0
- package/build/commands/integrations/convex/dashboard.js +42 -0
- package/build/commands/integrations/convex/project/delete.d.ts +13 -0
- package/build/commands/integrations/convex/project/delete.js +65 -0
- package/build/commands/integrations/convex/project.d.ts +9 -0
- package/build/commands/integrations/convex/project.js +28 -0
- package/build/commands/integrations/convex/team/delete.d.ts +17 -0
- package/build/commands/integrations/convex/team/delete.js +93 -0
- package/build/commands/integrations/convex/team/invite.d.ts +19 -0
- package/build/commands/integrations/convex/team/invite.js +108 -0
- package/build/commands/integrations/convex/team.d.ts +9 -0
- package/build/commands/integrations/convex/team.js +35 -0
- package/build/fingerprint/cli.js +1 -0
- package/build/graphql/generated.d.ts +241 -1
- package/build/graphql/mutations/ConvexMutation.d.ts +10 -0
- package/build/graphql/mutations/ConvexMutation.js +89 -0
- package/build/graphql/queries/ConvexQuery.d.ts +6 -0
- package/build/graphql/queries/ConvexQuery.js +49 -0
- package/build/graphql/types/ConvexTeamConnection.d.ts +11 -0
- package/build/graphql/types/ConvexTeamConnection.js +42 -0
- package/oclif.manifest.json +3884 -3333
- package/package.json +4 -4
|
@@ -108,9 +108,13 @@ async function makeProjectTarballAsync(vcsClient) {
|
|
|
108
108
|
(0, timer_1.startTimer)(compressTimerLabel);
|
|
109
109
|
try {
|
|
110
110
|
await vcsClient.makeShallowCopyAsync(shallowClonePath);
|
|
111
|
-
await tar.create({
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
await tar.create({
|
|
112
|
+
cwd: shallowClonePath,
|
|
113
|
+
file: tarPath,
|
|
114
|
+
prefix: 'project',
|
|
115
|
+
gzip: true,
|
|
116
|
+
portable: true,
|
|
117
|
+
}, ['.']);
|
|
114
118
|
}
|
|
115
119
|
catch (err) {
|
|
116
120
|
clearTimeout(timer);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ConvexProjectData, ConvexTeamConnectionData } from '../graphql/types/ConvexTeamConnection';
|
|
2
|
+
export declare function getConvexTeamDashboardUrl(connection: ConvexTeamConnectionData): string;
|
|
3
|
+
export declare function getConvexProjectDashboardUrl(project: ConvexProjectData): string;
|
|
4
|
+
export declare function formatConvexTeam(connection: ConvexTeamConnectionData): string;
|
|
5
|
+
export declare function formatConvexTeamConnection(connection: ConvexTeamConnectionData): string;
|
|
6
|
+
export declare function formatConvexProject(project: ConvexProjectData): string;
|
|
7
|
+
export declare function logNoConvexTeams(accountName: string): void;
|
|
8
|
+
export declare function logNoConvexProject(projectName: string): void;
|
|
9
|
+
export declare function confirmRecentConvexInviteAsync(connection: ConvexTeamConnectionData, { nonInteractive }: {
|
|
10
|
+
nonInteractive: boolean;
|
|
11
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getConvexTeamDashboardUrl = getConvexTeamDashboardUrl;
|
|
4
|
+
exports.getConvexProjectDashboardUrl = getConvexProjectDashboardUrl;
|
|
5
|
+
exports.formatConvexTeam = formatConvexTeam;
|
|
6
|
+
exports.formatConvexTeamConnection = formatConvexTeamConnection;
|
|
7
|
+
exports.formatConvexProject = formatConvexProject;
|
|
8
|
+
exports.logNoConvexTeams = logNoConvexTeams;
|
|
9
|
+
exports.logNoConvexProject = logNoConvexProject;
|
|
10
|
+
exports.confirmRecentConvexInviteAsync = confirmRecentConvexInviteAsync;
|
|
11
|
+
const tslib_1 = require("tslib");
|
|
12
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
13
|
+
const log_1 = tslib_1.__importStar(require("../log"));
|
|
14
|
+
const prompts_1 = require("../prompts");
|
|
15
|
+
const CONVEX_DASHBOARD_HOST = 'https://dashboard.convex.dev';
|
|
16
|
+
const RECENT_INVITE_THRESHOLD_MS = 60 * 60 * 1000;
|
|
17
|
+
function getConvexTeamDashboardUrl(connection) {
|
|
18
|
+
return `${CONVEX_DASHBOARD_HOST}/t/${encodeURIComponent(connection.convexTeamSlug)}`;
|
|
19
|
+
}
|
|
20
|
+
function getConvexProjectDashboardUrl(project) {
|
|
21
|
+
return `${getConvexTeamDashboardUrl(project.convexTeamConnection)}/${encodeURIComponent(project.convexProjectSlug)}`;
|
|
22
|
+
}
|
|
23
|
+
function formatConvexTeam(connection) {
|
|
24
|
+
return `${connection.convexTeamName} / ${connection.convexTeamSlug}`;
|
|
25
|
+
}
|
|
26
|
+
function formatConvexTeamConnection(connection) {
|
|
27
|
+
const lines = [
|
|
28
|
+
`${chalk_1.default.bold('Team')}: ${formatConvexTeam(connection)}`,
|
|
29
|
+
`${chalk_1.default.bold('Dashboard')}: ${(0, log_1.link)(getConvexTeamDashboardUrl(connection), { dim: false })}`,
|
|
30
|
+
];
|
|
31
|
+
if (connection.invitedEmail) {
|
|
32
|
+
lines.push(`${chalk_1.default.bold('Invited email')}: ${connection.invitedEmail}`);
|
|
33
|
+
}
|
|
34
|
+
if (connection.invitedAt) {
|
|
35
|
+
lines.push(`${chalk_1.default.bold('Invited at')}: ${connection.invitedAt}`);
|
|
36
|
+
}
|
|
37
|
+
return lines.join('\n');
|
|
38
|
+
}
|
|
39
|
+
function formatConvexProject(project) {
|
|
40
|
+
return [
|
|
41
|
+
`${chalk_1.default.bold('Name')}: ${project.convexProjectName}`,
|
|
42
|
+
`${chalk_1.default.bold('Slug')}: ${project.convexProjectSlug}`,
|
|
43
|
+
`${chalk_1.default.bold('Identifier')}: ${project.convexProjectIdentifier}`,
|
|
44
|
+
`${chalk_1.default.bold('Team')}: ${formatConvexTeam(project.convexTeamConnection)}`,
|
|
45
|
+
`${chalk_1.default.bold('Dashboard')}: ${(0, log_1.link)(getConvexProjectDashboardUrl(project), { dim: false })}`,
|
|
46
|
+
].join('\n');
|
|
47
|
+
}
|
|
48
|
+
function logNoConvexTeams(accountName) {
|
|
49
|
+
log_1.default.warn(`No Convex team is linked to account ${chalk_1.default.bold(accountName)} on EAS.`);
|
|
50
|
+
}
|
|
51
|
+
function logNoConvexProject(projectName) {
|
|
52
|
+
log_1.default.warn(`No Convex project is linked to Expo app ${chalk_1.default.bold(projectName)} on EAS.`);
|
|
53
|
+
}
|
|
54
|
+
async function confirmRecentConvexInviteAsync(connection, { nonInteractive }) {
|
|
55
|
+
const invitedAt = connection.invitedAt ? new Date(connection.invitedAt) : null;
|
|
56
|
+
if (!invitedAt || Number.isNaN(invitedAt.getTime())) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
const timeSinceInviteMs = Date.now() - invitedAt.getTime();
|
|
60
|
+
if (timeSinceInviteMs >= RECENT_INVITE_THRESHOLD_MS) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
const previousInvite = `A Convex team invite was already sent${connection.invitedEmail ? ` to ${connection.invitedEmail}` : ''} at ${connection.invitedAt}.`;
|
|
64
|
+
if (nonInteractive) {
|
|
65
|
+
log_1.default.warn(`${previousInvite} Sending another invite because this command is running in non-interactive mode.`);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return await (0, prompts_1.confirmAsync)({
|
|
69
|
+
message: `${previousInvite} Are you sure you want to send another invite?`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -22,6 +22,7 @@ class Download extends EasCommand_1.default {
|
|
|
22
22
|
static description = 'download a simulator/emulator build by build ID or fingerprint hash';
|
|
23
23
|
static flags = {
|
|
24
24
|
'build-id': core_1.Flags.string({
|
|
25
|
+
aliases: ['id'],
|
|
25
26
|
description: 'ID of the build to download. Mutually exclusive with --fingerprint, --platform, and --dev-client; the platform is derived from the build itself.',
|
|
26
27
|
exclusive: ['fingerprint', 'platform', 'dev-client'],
|
|
27
28
|
}),
|
|
@@ -143,7 +143,6 @@ class BuildResign extends EasCommand_1.default {
|
|
|
143
143
|
},
|
|
144
144
|
},
|
|
145
145
|
secrets: (0, prepareJob_1.prepareCredentialsToResign)(credentialsResult.credentials),
|
|
146
|
-
builderEnvironment: { image: 'default' },
|
|
147
146
|
};
|
|
148
147
|
const newBuild = await BuildMutation_1.BuildMutation.retryIosBuildAsync(graphqlClient, {
|
|
149
148
|
buildId: build.id,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import EasCommand from '../../../commandUtils/EasCommand';
|
|
2
|
+
export default class IntegrationsConvexConnect extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static contextDefinition: {
|
|
5
|
+
loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
|
|
6
|
+
privateProjectConfig: import("../../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
region: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
'team-name': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
'project-name': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
runAsync(): Promise<void>;
|
|
15
|
+
private resolveRegionAsync;
|
|
16
|
+
private resolveTeamNameAsync;
|
|
17
|
+
private resolveProjectNameAsync;
|
|
18
|
+
private selectConnectionAsync;
|
|
19
|
+
private getActorEmail;
|
|
20
|
+
private sendTeamInviteAsync;
|
|
21
|
+
private installConvexPackageAsync;
|
|
22
|
+
private writeEnvLocalAsync;
|
|
23
|
+
private mergeEnvContent;
|
|
24
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const dotenv_1 = tslib_1.__importDefault(require("dotenv"));
|
|
8
|
+
const fs = tslib_1.__importStar(require("fs-extra"));
|
|
9
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
10
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
|
|
11
|
+
const convex_1 = require("../../../commandUtils/convex");
|
|
12
|
+
const flags_1 = require("../../../commandUtils/flags");
|
|
13
|
+
const ConvexMutation_1 = require("../../../graphql/mutations/ConvexMutation");
|
|
14
|
+
const ConvexQuery_1 = require("../../../graphql/queries/ConvexQuery");
|
|
15
|
+
const log_1 = tslib_1.__importDefault(require("../../../log"));
|
|
16
|
+
const ora_1 = require("../../../ora");
|
|
17
|
+
const projectUtils_1 = require("../../../project/projectUtils");
|
|
18
|
+
const prompts_1 = require("../../../prompts");
|
|
19
|
+
const CONVEX_REGIONS = [
|
|
20
|
+
{ title: 'US East (aws-us-east-1)', value: 'aws-us-east-1' },
|
|
21
|
+
{ title: 'EU West (aws-eu-west-1)', value: 'aws-eu-west-1' },
|
|
22
|
+
];
|
|
23
|
+
const DEFAULT_REGION = 'aws-us-east-1';
|
|
24
|
+
class IntegrationsConvexConnect extends EasCommand_1.default {
|
|
25
|
+
static description = 'connect Convex to your Expo project';
|
|
26
|
+
static contextDefinition = {
|
|
27
|
+
...this.ContextOptions.ProjectConfig,
|
|
28
|
+
};
|
|
29
|
+
static flags = {
|
|
30
|
+
...flags_1.EASNonInteractiveFlag,
|
|
31
|
+
region: core_1.Flags.string({
|
|
32
|
+
description: 'Convex deployment region (e.g. aws-us-east-1, aws-eu-west-1)',
|
|
33
|
+
options: CONVEX_REGIONS.map(r => r.value),
|
|
34
|
+
}),
|
|
35
|
+
'team-name': core_1.Flags.string({
|
|
36
|
+
description: 'Name for the new Convex team (defaults to EAS account name)',
|
|
37
|
+
}),
|
|
38
|
+
'project-name': core_1.Flags.string({
|
|
39
|
+
description: 'Name for the Convex project (defaults to app slug)',
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
async runAsync() {
|
|
43
|
+
const { flags: { region: regionFlag, 'team-name': teamNameFlag, 'project-name': projectNameFlag, 'non-interactive': nonInteractive, }, } = await this.parse(IntegrationsConvexConnect);
|
|
44
|
+
const { privateProjectConfig: { projectId, exp, projectDir }, loggedIn: { graphqlClient, actor }, } = await this.getContextAsync(IntegrationsConvexConnect, {
|
|
45
|
+
nonInteractive,
|
|
46
|
+
withServerSideEnvironment: null,
|
|
47
|
+
});
|
|
48
|
+
const account = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId);
|
|
49
|
+
// 1. Check for existing Convex team connections
|
|
50
|
+
const existingConnections = await ConvexQuery_1.ConvexQuery.getConvexTeamConnectionsByAccountIdAsync(graphqlClient, account.id);
|
|
51
|
+
const region = await this.resolveRegionAsync(regionFlag, nonInteractive);
|
|
52
|
+
let connection = null;
|
|
53
|
+
let teamName = null;
|
|
54
|
+
if (existingConnections.length === 0) {
|
|
55
|
+
// 2a. No connection - resolve a team name and create it after local package install succeeds
|
|
56
|
+
teamName = await this.resolveTeamNameAsync(teamNameFlag, account.name, nonInteractive);
|
|
57
|
+
}
|
|
58
|
+
else if (existingConnections.length === 1) {
|
|
59
|
+
// 2b. Single existing connection
|
|
60
|
+
connection = existingConnections[0];
|
|
61
|
+
log_1.default.withTick(`Using existing Convex team ${chalk_1.default.bold((0, convex_1.formatConvexTeam)(connection))}`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// 2c. Multiple connections - prompt to select
|
|
65
|
+
connection = await this.selectConnectionAsync(existingConnections, nonInteractive);
|
|
66
|
+
}
|
|
67
|
+
// 3. Resolve project name before project setup mutation
|
|
68
|
+
const projectName = await this.resolveProjectNameAsync(projectNameFlag, exp.slug, nonInteractive);
|
|
69
|
+
// 4. Install the Convex package before creating new server-side resources
|
|
70
|
+
await this.installConvexPackageAsync(projectDir);
|
|
71
|
+
if (!connection) {
|
|
72
|
+
const spinner = (0, ora_1.ora)('Creating Convex team').start();
|
|
73
|
+
try {
|
|
74
|
+
connection = await ConvexMutation_1.ConvexMutation.createConvexTeamConnectionAsync(graphqlClient, {
|
|
75
|
+
accountId: account.id,
|
|
76
|
+
deploymentRegion: region,
|
|
77
|
+
convexTeamName: teamName ?? account.name,
|
|
78
|
+
});
|
|
79
|
+
spinner.succeed(`Created Convex team ${chalk_1.default.bold((0, convex_1.formatConvexTeam)(connection))}`);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
spinner.fail('Failed to create Convex team');
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// 5. Set up Convex project
|
|
87
|
+
const spinner = (0, ora_1.ora)('Setting up Convex project').start();
|
|
88
|
+
let setupResult;
|
|
89
|
+
try {
|
|
90
|
+
setupResult = await ConvexMutation_1.ConvexMutation.setupConvexProjectAsync(graphqlClient, {
|
|
91
|
+
appId: projectId,
|
|
92
|
+
convexTeamConnectionId: connection.id,
|
|
93
|
+
deploymentRegion: region,
|
|
94
|
+
projectName,
|
|
95
|
+
});
|
|
96
|
+
spinner.succeed(`Created Convex project ${chalk_1.default.bold(projectName)} with deployment ${chalk_1.default.bold(setupResult.convexDeploymentName)}`);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
spinner.fail('Failed to set up Convex project');
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
// 6. Send team invite (non-fatal)
|
|
103
|
+
await this.sendTeamInviteAsync(graphqlClient, connection, actor, { nonInteractive });
|
|
104
|
+
// 7. Write deploy key and URL to .env.local
|
|
105
|
+
await this.writeEnvLocalAsync(projectDir, setupResult.deployKey, setupResult.convexDeploymentUrl, nonInteractive);
|
|
106
|
+
// 8. Success message
|
|
107
|
+
log_1.default.addNewLineIfNone();
|
|
108
|
+
log_1.default.log(chalk_1.default.green('Convex is ready!'));
|
|
109
|
+
log_1.default.newLine();
|
|
110
|
+
log_1.default.log('Next steps:');
|
|
111
|
+
log_1.default.log(` 1. Start the Convex dev server: ${chalk_1.default.cyan('npx convex dev')}`);
|
|
112
|
+
log_1.default.newLine();
|
|
113
|
+
if (this.getActorEmail(actor)) {
|
|
114
|
+
log_1.default.log(`Check your email for an invitation to join your Convex team. Accept it for full dashboard access.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async resolveRegionAsync(flagValue, nonInteractive) {
|
|
118
|
+
if (flagValue) {
|
|
119
|
+
return flagValue;
|
|
120
|
+
}
|
|
121
|
+
if (nonInteractive) {
|
|
122
|
+
return DEFAULT_REGION;
|
|
123
|
+
}
|
|
124
|
+
return await (0, prompts_1.selectAsync)('Select a Convex deployment region', CONVEX_REGIONS);
|
|
125
|
+
}
|
|
126
|
+
async resolveTeamNameAsync(flagValue, accountName, nonInteractive) {
|
|
127
|
+
if (flagValue) {
|
|
128
|
+
return flagValue;
|
|
129
|
+
}
|
|
130
|
+
if (nonInteractive) {
|
|
131
|
+
return accountName;
|
|
132
|
+
}
|
|
133
|
+
const { teamName } = await (0, prompts_1.promptAsync)({
|
|
134
|
+
type: 'text',
|
|
135
|
+
name: 'teamName',
|
|
136
|
+
message: 'Convex team name',
|
|
137
|
+
initial: accountName,
|
|
138
|
+
validate: (value) => (value.trim() ? true : 'Team name cannot be empty'),
|
|
139
|
+
});
|
|
140
|
+
return teamName;
|
|
141
|
+
}
|
|
142
|
+
async resolveProjectNameAsync(flagValue, slug, nonInteractive) {
|
|
143
|
+
if (flagValue) {
|
|
144
|
+
return flagValue;
|
|
145
|
+
}
|
|
146
|
+
if (nonInteractive) {
|
|
147
|
+
return slug;
|
|
148
|
+
}
|
|
149
|
+
const { projectName } = await (0, prompts_1.promptAsync)({
|
|
150
|
+
type: 'text',
|
|
151
|
+
name: 'projectName',
|
|
152
|
+
message: 'Convex project name',
|
|
153
|
+
initial: slug,
|
|
154
|
+
validate: (value) => (value.trim() ? true : 'Project name cannot be empty'),
|
|
155
|
+
});
|
|
156
|
+
return projectName;
|
|
157
|
+
}
|
|
158
|
+
async selectConnectionAsync(connections, nonInteractive) {
|
|
159
|
+
if (nonInteractive) {
|
|
160
|
+
return connections[0];
|
|
161
|
+
}
|
|
162
|
+
const choices = connections.map(c => ({
|
|
163
|
+
title: `${(0, convex_1.formatConvexTeam)(c)} (created ${new Date(c.createdAt).toLocaleDateString()})`,
|
|
164
|
+
value: c,
|
|
165
|
+
}));
|
|
166
|
+
return await (0, prompts_1.selectAsync)('Select a Convex team connection', choices);
|
|
167
|
+
}
|
|
168
|
+
getActorEmail(actor) {
|
|
169
|
+
return actor.__typename === 'User' ? actor.email : null;
|
|
170
|
+
}
|
|
171
|
+
async sendTeamInviteAsync(graphqlClient, connection, actor, { nonInteractive }) {
|
|
172
|
+
const email = this.getActorEmail(actor);
|
|
173
|
+
if (!email) {
|
|
174
|
+
log_1.default.warn(`Could not determine your verified email address, so no Convex team invitation was sent. Run ${chalk_1.default.cyan('eas integrations:convex:team:invite')} after signing in with a user account.`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!(await (0, convex_1.confirmRecentConvexInviteAsync)(connection, { nonInteractive }))) {
|
|
178
|
+
log_1.default.warn('Skipped sending Convex team invitation.');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
await ConvexMutation_1.ConvexMutation.sendConvexTeamInviteToVerifiedEmailAsync(graphqlClient, {
|
|
183
|
+
convexTeamConnectionId: connection.id,
|
|
184
|
+
});
|
|
185
|
+
log_1.default.withTick(`Sent Convex team invitation to ${chalk_1.default.bold(email)}`);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
log_1.default.warn(`Failed to send Convex team invitation to ${email}. Run ${chalk_1.default.cyan('eas integrations:convex:team:invite')} to retry.`);
|
|
189
|
+
log_1.default.warn(error instanceof Error ? error.message : String(error));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async installConvexPackageAsync(projectDir) {
|
|
193
|
+
log_1.default.newLine();
|
|
194
|
+
log_1.default.log(`Running ${chalk_1.default.bold('npx expo install convex')}`);
|
|
195
|
+
log_1.default.newLine();
|
|
196
|
+
try {
|
|
197
|
+
await (0, spawn_async_1.default)('npx', ['expo', 'install', 'convex'], {
|
|
198
|
+
cwd: projectDir,
|
|
199
|
+
stdio: 'inherit',
|
|
200
|
+
});
|
|
201
|
+
log_1.default.withTick(`Installed the ${chalk_1.default.bold('convex')} npm package`);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
log_1.default.warn(`Failed to install the ${chalk_1.default.bold('convex')} npm package. Run ${chalk_1.default.cyan('npx expo install convex')} from your project directory, then run ${chalk_1.default.cyan('npx convex dev')}.`);
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async writeEnvLocalAsync(projectDir, deployKey, convexUrl, nonInteractive) {
|
|
209
|
+
const envPath = path_1.default.join(projectDir, '.env.local');
|
|
210
|
+
let existingContent = {};
|
|
211
|
+
let rawContent = '';
|
|
212
|
+
if (await fs.pathExists(envPath)) {
|
|
213
|
+
rawContent = await fs.readFile(envPath, 'utf8');
|
|
214
|
+
existingContent = dotenv_1.default.parse(rawContent);
|
|
215
|
+
if (existingContent.CONVEX_DEPLOY_KEY && !nonInteractive) {
|
|
216
|
+
const overwrite = await (0, prompts_1.confirmAsync)({
|
|
217
|
+
message: `.env.local already contains CONVEX_DEPLOY_KEY. Overwrite Convex values?`,
|
|
218
|
+
});
|
|
219
|
+
if (!overwrite) {
|
|
220
|
+
log_1.default.log('Skipping .env.local update. Deploy key:');
|
|
221
|
+
log_1.default.log(` CONVEX_DEPLOY_KEY=${deployKey}`);
|
|
222
|
+
log_1.default.log(` EXPO_PUBLIC_CONVEX_URL=${convexUrl}`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const updatedContent = this.mergeEnvContent(rawContent, {
|
|
228
|
+
CONVEX_DEPLOY_KEY: deployKey,
|
|
229
|
+
EXPO_PUBLIC_CONVEX_URL: convexUrl,
|
|
230
|
+
});
|
|
231
|
+
await fs.writeFile(envPath, updatedContent);
|
|
232
|
+
log_1.default.withTick(`Wrote Convex config to ${chalk_1.default.bold('.env.local')}`);
|
|
233
|
+
}
|
|
234
|
+
mergeEnvContent(rawContent, newVars) {
|
|
235
|
+
let content = rawContent;
|
|
236
|
+
const keysToAdd = { ...newVars };
|
|
237
|
+
for (const [key, value] of Object.entries(newVars)) {
|
|
238
|
+
// Replace existing line if present
|
|
239
|
+
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
240
|
+
if (regex.test(content)) {
|
|
241
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
242
|
+
delete keysToAdd[key];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Append any keys that weren't already in the file
|
|
246
|
+
const remaining = Object.entries(keysToAdd);
|
|
247
|
+
if (remaining.length > 0) {
|
|
248
|
+
if (content.length > 0 && !content.endsWith('\n')) {
|
|
249
|
+
content += '\n';
|
|
250
|
+
}
|
|
251
|
+
for (const [key, value] of remaining) {
|
|
252
|
+
content += `${key}=${value}\n`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return content;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.default = IntegrationsConvexConnect;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import EasCommand from '../../../commandUtils/EasCommand';
|
|
2
|
+
export default class IntegrationsConvexDashboard extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static contextDefinition: {
|
|
5
|
+
loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
|
|
6
|
+
privateProjectConfig: import("../../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
7
|
+
};
|
|
8
|
+
runAsync(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const better_opn_1 = tslib_1.__importDefault(require("better-opn"));
|
|
5
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
|
|
6
|
+
const convex_1 = require("../../../commandUtils/convex");
|
|
7
|
+
const ConvexQuery_1 = require("../../../graphql/queries/ConvexQuery");
|
|
8
|
+
const ora_1 = require("../../../ora");
|
|
9
|
+
class IntegrationsConvexDashboard extends EasCommand_1.default {
|
|
10
|
+
static description = 'open the Convex dashboard for the linked Convex project';
|
|
11
|
+
static contextDefinition = {
|
|
12
|
+
...this.ContextOptions.ProjectConfig,
|
|
13
|
+
};
|
|
14
|
+
async runAsync() {
|
|
15
|
+
const { privateProjectConfig: { projectId, exp }, loggedIn: { graphqlClient }, } = await this.getContextAsync(IntegrationsConvexDashboard, {
|
|
16
|
+
nonInteractive: false,
|
|
17
|
+
withServerSideEnvironment: null,
|
|
18
|
+
});
|
|
19
|
+
const convexProject = await ConvexQuery_1.ConvexQuery.getConvexProjectByAppIdAsync(graphqlClient, projectId);
|
|
20
|
+
if (!convexProject) {
|
|
21
|
+
(0, convex_1.logNoConvexProject)(exp.slug);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const dashboardUrl = (0, convex_1.getConvexProjectDashboardUrl)(convexProject);
|
|
25
|
+
const failedMessage = `Unable to open a web browser. Convex dashboard is available at: ${dashboardUrl}`;
|
|
26
|
+
const spinner = (0, ora_1.ora)(`Opening ${dashboardUrl}`).start();
|
|
27
|
+
try {
|
|
28
|
+
const opened = await (0, better_opn_1.default)(dashboardUrl);
|
|
29
|
+
if (opened) {
|
|
30
|
+
spinner.succeed(`Opened ${dashboardUrl}`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
spinner.fail(failedMessage);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
spinner.fail(failedMessage);
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.default = IntegrationsConvexDashboard;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import EasCommand from '../../../../commandUtils/EasCommand';
|
|
2
|
+
export default class IntegrationsConvexProjectDelete extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
6
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
static contextDefinition: {
|
|
9
|
+
loggedIn: import("../../../../commandUtils/context/LoggedInContextField").default;
|
|
10
|
+
privateProjectConfig: import("../../../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
11
|
+
};
|
|
12
|
+
runAsync(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
6
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../../../commandUtils/EasCommand"));
|
|
7
|
+
const convex_1 = require("../../../../commandUtils/convex");
|
|
8
|
+
const flags_1 = require("../../../../commandUtils/flags");
|
|
9
|
+
const ConvexMutation_1 = require("../../../../graphql/mutations/ConvexMutation");
|
|
10
|
+
const ConvexQuery_1 = require("../../../../graphql/queries/ConvexQuery");
|
|
11
|
+
const log_1 = tslib_1.__importStar(require("../../../../log"));
|
|
12
|
+
const ora_1 = require("../../../../ora");
|
|
13
|
+
const prompts_1 = require("../../../../prompts");
|
|
14
|
+
class IntegrationsConvexProjectDelete extends EasCommand_1.default {
|
|
15
|
+
static description = 'remove the Convex project link for the current Expo app from EAS servers';
|
|
16
|
+
static flags = {
|
|
17
|
+
...flags_1.EASNonInteractiveFlag,
|
|
18
|
+
yes: core_1.Flags.boolean({
|
|
19
|
+
char: 'y',
|
|
20
|
+
description: 'Skip confirmation prompt',
|
|
21
|
+
default: false,
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
static contextDefinition = {
|
|
25
|
+
...this.ContextOptions.ProjectConfig,
|
|
26
|
+
};
|
|
27
|
+
async runAsync() {
|
|
28
|
+
const { flags: { 'non-interactive': nonInteractive, yes }, } = await this.parse(IntegrationsConvexProjectDelete);
|
|
29
|
+
const { privateProjectConfig: { projectId, exp }, loggedIn: { graphqlClient }, } = await this.getContextAsync(IntegrationsConvexProjectDelete, {
|
|
30
|
+
nonInteractive,
|
|
31
|
+
withServerSideEnvironment: null,
|
|
32
|
+
});
|
|
33
|
+
const convexProject = await ConvexQuery_1.ConvexQuery.getConvexProjectByAppIdAsync(graphqlClient, projectId);
|
|
34
|
+
if (!convexProject) {
|
|
35
|
+
(0, convex_1.logNoConvexProject)(exp.slug);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
log_1.default.addNewLineIfNone();
|
|
39
|
+
log_1.default.log((0, convex_1.formatConvexProject)(convexProject));
|
|
40
|
+
log_1.default.newLine();
|
|
41
|
+
const dashboardUrl = (0, convex_1.getConvexProjectDashboardUrl)(convexProject);
|
|
42
|
+
if (!nonInteractive && !yes) {
|
|
43
|
+
const confirmed = await (0, prompts_1.confirmAsync)({
|
|
44
|
+
message: `Remove this Convex project link from EAS servers? This does not destroy resources on Convex. Convex dashboard: ${(0, log_1.link)(dashboardUrl, { dim: false })}`,
|
|
45
|
+
});
|
|
46
|
+
if (!confirmed) {
|
|
47
|
+
log_1.default.error('Canceled deletion of the Convex project link');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
log_1.default.warn(`Removing the Convex project link from EAS servers. This does not destroy resources on Convex: ${dashboardUrl}`);
|
|
53
|
+
}
|
|
54
|
+
const spinner = (0, ora_1.ora)('Removing Convex project link').start();
|
|
55
|
+
try {
|
|
56
|
+
await ConvexMutation_1.ConvexMutation.deleteConvexProjectAsync(graphqlClient, convexProject.id);
|
|
57
|
+
spinner.succeed(`Removed Convex project ${chalk_1.default.bold(convexProject.convexProjectName)} from EAS servers`);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
spinner.fail('Failed to remove Convex project link');
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.default = IntegrationsConvexProjectDelete;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import EasCommand from '../../../commandUtils/EasCommand';
|
|
2
|
+
export default class IntegrationsConvexProject extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static contextDefinition: {
|
|
5
|
+
loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
|
|
6
|
+
privateProjectConfig: import("../../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
7
|
+
};
|
|
8
|
+
runAsync(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
5
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
|
|
6
|
+
const convex_1 = require("../../../commandUtils/convex");
|
|
7
|
+
const ConvexQuery_1 = require("../../../graphql/queries/ConvexQuery");
|
|
8
|
+
const log_1 = tslib_1.__importDefault(require("../../../log"));
|
|
9
|
+
class IntegrationsConvexProject extends EasCommand_1.default {
|
|
10
|
+
static description = 'display the Convex project linked to the current Expo app';
|
|
11
|
+
static contextDefinition = {
|
|
12
|
+
...this.ContextOptions.ProjectConfig,
|
|
13
|
+
};
|
|
14
|
+
async runAsync() {
|
|
15
|
+
const { privateProjectConfig: { projectId, exp }, loggedIn: { graphqlClient }, } = await this.getContextAsync(IntegrationsConvexProject, {
|
|
16
|
+
nonInteractive: false,
|
|
17
|
+
withServerSideEnvironment: null,
|
|
18
|
+
});
|
|
19
|
+
const convexProject = await ConvexQuery_1.ConvexQuery.getConvexProjectByAppIdAsync(graphqlClient, projectId);
|
|
20
|
+
if (!convexProject) {
|
|
21
|
+
(0, convex_1.logNoConvexProject)(exp.slug);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
log_1.default.log(chalk_1.default.bold(`Convex project linked to ${exp.slug}`));
|
|
25
|
+
log_1.default.log((0, convex_1.formatConvexProject)(convexProject));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.default = IntegrationsConvexProject;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import EasCommand from '../../../../commandUtils/EasCommand';
|
|
2
|
+
export default class IntegrationsConvexTeamDelete extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
CONVEX_TEAM: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static flags: {
|
|
8
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
static contextDefinition: {
|
|
12
|
+
loggedIn: import("../../../../commandUtils/context/LoggedInContextField").default;
|
|
13
|
+
privateProjectConfig: import("../../../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
14
|
+
};
|
|
15
|
+
runAsync(): Promise<void>;
|
|
16
|
+
private resolveConnectionAsync;
|
|
17
|
+
}
|