firebase-tools 12.9.1 → 13.0.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/lib/apiv2.js +7 -0
- package/lib/auth.js +8 -4
- package/lib/commands/frameworks-backends-delete.js +66 -6
- package/lib/commands/frameworks-backends-get.js +57 -18
- package/lib/commands/frameworks-backends-list.js +42 -15
- package/lib/commands/index.js +0 -1
- package/lib/commands/login-use.js +25 -6
- package/lib/config.js +10 -8
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/lifecycleHooks.js +5 -0
- package/lib/fetchMOTD.js +1 -1
- package/lib/firestore/api.js +6 -6
- package/lib/frameworks/angular/index.js +22 -24
- package/lib/frameworks/angular/utils.js +217 -97
- package/lib/frameworks/constants.js +6 -3
- package/lib/frameworks/next/index.js +4 -0
- package/lib/frameworks/next/utils.js +6 -2
- package/lib/functionsShellCommandAction.js +3 -3
- package/lib/gcp/cloudbuild.js +24 -3
- package/lib/gcp/storage.js +11 -5
- package/lib/hosting/runTags.js +2 -1
- package/lib/init/features/frameworks/index.js +24 -22
- package/lib/init/features/frameworks/repo.js +108 -36
- package/lib/localFunction.js +77 -14
- package/lib/utils.js +28 -1
- package/package.json +2 -3
- package/templates/popup.html +64 -0
- package/lib/commands/ext-dev-publish.js +0 -32
|
@@ -3,15 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createBackend = exports.getOrCreateBackend = exports.doSetup = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const utils = require("../../../utils");
|
|
6
|
-
const logger_1 = require("../../../logger");
|
|
7
|
-
const prompt_1 = require("../../../prompt");
|
|
8
|
-
const constants_1 = require("./constants");
|
|
9
6
|
const repo = require("./repo");
|
|
10
7
|
const poller = require("../../../operation-poller");
|
|
11
|
-
const api_1 = require("../../../api");
|
|
12
8
|
const gcp = require("../../../gcp/frameworks");
|
|
9
|
+
const api_1 = require("../../../api");
|
|
13
10
|
const frameworks_1 = require("../../../gcp/frameworks");
|
|
14
11
|
const error_1 = require("../../../error");
|
|
12
|
+
const logger_1 = require("../../../logger");
|
|
13
|
+
const prompt_1 = require("../../../prompt");
|
|
14
|
+
const constants_1 = require("./constants");
|
|
15
15
|
const frameworksPollerOptions = {
|
|
16
16
|
apiOrigin: api_1.frameworksOrigin,
|
|
17
17
|
apiVersion: frameworks_1.API_VERSION,
|
|
@@ -20,12 +20,12 @@ const frameworksPollerOptions = {
|
|
|
20
20
|
};
|
|
21
21
|
async function doSetup(setup, projectId) {
|
|
22
22
|
setup.frameworks = {};
|
|
23
|
-
utils.logBullet("First we need a few details to create your
|
|
23
|
+
utils.logBullet("First we need a few details to create your backend.");
|
|
24
24
|
await (0, prompt_1.promptOnce)({
|
|
25
25
|
name: "serviceName",
|
|
26
26
|
type: "input",
|
|
27
27
|
default: "acme-inc-web",
|
|
28
|
-
message: "Create a name for your
|
|
28
|
+
message: "Create a name for your backend [1-30 characters]",
|
|
29
29
|
}, setup.frameworks);
|
|
30
30
|
await (0, prompt_1.promptOnce)({
|
|
31
31
|
name: "region",
|
|
@@ -36,17 +36,15 @@ async function doSetup(setup, projectId) {
|
|
|
36
36
|
choices: constants_1.ALLOWED_REGIONS,
|
|
37
37
|
}, setup.frameworks);
|
|
38
38
|
utils.logSuccess(`Region set to ${setup.frameworks.region}.`);
|
|
39
|
-
logger_1.logger.info(clc.bold(`\n${clc.white("===")} Deploy Setup`));
|
|
40
|
-
await (0, prompt_1.promptOnce)({
|
|
41
|
-
name: "deployMethod",
|
|
42
|
-
type: "list",
|
|
43
|
-
default: constants_1.DEFAULT_DEPLOY_METHOD,
|
|
44
|
-
message: "How do you want to deploy",
|
|
45
|
-
choices: constants_1.ALLOWED_DEPLOY_METHODS,
|
|
46
|
-
}, setup.frameworks);
|
|
47
39
|
const backend = await getOrCreateBackend(projectId, setup);
|
|
48
40
|
if (backend) {
|
|
49
|
-
|
|
41
|
+
logger_1.logger.info();
|
|
42
|
+
utils.logSuccess(`Successfully created backend:\n ${backend.name}`);
|
|
43
|
+
logger_1.logger.info();
|
|
44
|
+
utils.logSuccess(`Your site is being deployed at:\n https://${backend.uri}`);
|
|
45
|
+
logger_1.logger.info();
|
|
46
|
+
utils.logSuccess(`View the rollout status by running:\n firebase backends:get --backend=${backend.name}`);
|
|
47
|
+
logger_1.logger.info();
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
exports.doSetup = doSetup;
|
|
@@ -61,18 +59,22 @@ function toBackend(cloudBuildConnRepo) {
|
|
|
61
59
|
}
|
|
62
60
|
async function getOrCreateBackend(projectId, setup) {
|
|
63
61
|
const location = setup.frameworks.region;
|
|
64
|
-
const deployMethod = setup.frameworks.deployMethod;
|
|
65
62
|
try {
|
|
66
63
|
return await getExistingBackend(projectId, setup, location);
|
|
67
64
|
}
|
|
68
65
|
catch (err) {
|
|
69
66
|
if (err.status === 404) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
const cloudBuildConnRepo = await repo.linkGitHubRepository(projectId, location);
|
|
68
|
+
logger_1.logger.info();
|
|
69
|
+
await (0, prompt_1.promptOnce)({
|
|
70
|
+
name: "branchName",
|
|
71
|
+
type: "input",
|
|
72
|
+
default: "main",
|
|
73
|
+
message: "Which branch do you want to deploy?",
|
|
74
|
+
}, setup.frameworks);
|
|
75
|
+
const backendDetails = toBackend(cloudBuildConnRepo);
|
|
76
|
+
logger_1.logger.info(clc.bold(`\n${clc.white("===")} Creating your backend`));
|
|
77
|
+
return await createBackend(projectId, location, backendDetails, setup.frameworks.serviceName);
|
|
76
78
|
}
|
|
77
79
|
else {
|
|
78
80
|
throw new error_1.FirebaseError(`Failed to get or create a backend using the given initialization details: ${err}`);
|
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getOrCreateRepository = exports.getOrCreateConnection = exports.linkGitHubRepository = void 0;
|
|
4
|
-
const
|
|
5
|
-
const error_1 = require("../../../error");
|
|
3
|
+
exports.listFrameworksConnections = exports.getOrCreateRepository = exports.getOrCreateConnection = exports.createConnection = exports.linkGitHubRepository = exports.parseConnectionName = void 0;
|
|
4
|
+
const clc = require("colorette");
|
|
6
5
|
const gcb = require("../../../gcp/cloudbuild");
|
|
7
|
-
const logger_1 = require("../../../logger");
|
|
8
6
|
const poller = require("../../../operation-poller");
|
|
9
7
|
const utils = require("../../../utils");
|
|
8
|
+
const api_1 = require("../../../api");
|
|
9
|
+
const error_1 = require("../../../error");
|
|
10
|
+
const logger_1 = require("../../../logger");
|
|
10
11
|
const prompt_1 = require("../../../prompt");
|
|
12
|
+
const FRAMEWORKS_CONN_PATTERN = /.+\/frameworks-github-conn-.+$/;
|
|
13
|
+
const FRAMEWORKS_OAUTH_CONN_NAME = "frameworks-github-oauth";
|
|
14
|
+
const CONNECTION_NAME_REGEX = /^projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/connections\/(?<id>[^\/]+)$/;
|
|
15
|
+
function parseConnectionName(name) {
|
|
16
|
+
const match = name.match(CONNECTION_NAME_REGEX);
|
|
17
|
+
if (!match || typeof match.groups === undefined) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { projectId, location, id } = match.groups;
|
|
21
|
+
return {
|
|
22
|
+
projectId,
|
|
23
|
+
location,
|
|
24
|
+
id,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.parseConnectionName = parseConnectionName;
|
|
11
28
|
const gcbPollerOptions = {
|
|
12
29
|
apiOrigin: api_1.cloudbuildOrigin,
|
|
13
30
|
apiVersion: "v2",
|
|
14
31
|
masterTimeout: 25 * 60 * 1000,
|
|
15
32
|
maxBackoff: 10000,
|
|
16
33
|
};
|
|
17
|
-
function
|
|
34
|
+
function extractRepoSlugFromUri(remoteUri) {
|
|
18
35
|
const match = /github.com\/(.+).git/.exec(remoteUri);
|
|
19
36
|
if (!match) {
|
|
20
37
|
return undefined;
|
|
@@ -23,75 +40,123 @@ function extractRepoSlugFromURI(remoteUri) {
|
|
|
23
40
|
}
|
|
24
41
|
function generateRepositoryId(remoteUri) {
|
|
25
42
|
var _a;
|
|
26
|
-
return (_a =
|
|
43
|
+
return (_a = extractRepoSlugFromUri(remoteUri)) === null || _a === void 0 ? void 0 : _a.replaceAll("/", "-");
|
|
27
44
|
}
|
|
28
|
-
function generateConnectionId(
|
|
29
|
-
|
|
45
|
+
function generateConnectionId() {
|
|
46
|
+
const randomHash = Math.random().toString(36).slice(6);
|
|
47
|
+
return `frameworks-github-conn-${randomHash}`;
|
|
30
48
|
}
|
|
31
49
|
async function linkGitHubRepository(projectId, location) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
var _a, _b, _c;
|
|
51
|
+
logger_1.logger.info(clc.bold(`\n${clc.yellow("===")} Connect a GitHub repository`));
|
|
52
|
+
const existingConns = await listFrameworksConnections(projectId);
|
|
53
|
+
if (existingConns.length < 1) {
|
|
54
|
+
let oauthConn = await getOrCreateConnection(projectId, location, FRAMEWORKS_OAUTH_CONN_NAME);
|
|
55
|
+
while (oauthConn.installationState.stage === "PENDING_USER_OAUTH") {
|
|
56
|
+
oauthConn = await promptConnectionAuth(oauthConn);
|
|
57
|
+
}
|
|
58
|
+
const connectionId = generateConnectionId();
|
|
59
|
+
const conn = await createConnection(projectId, location, connectionId, {
|
|
60
|
+
authorizerCredential: (_a = oauthConn.githubConfig) === null || _a === void 0 ? void 0 : _a.authorizerCredential,
|
|
61
|
+
});
|
|
62
|
+
let refreshedConn = conn;
|
|
63
|
+
while (refreshedConn.installationState.stage !== "COMPLETE") {
|
|
64
|
+
refreshedConn = await promptAppInstall(conn);
|
|
65
|
+
}
|
|
66
|
+
existingConns.push(refreshedConn);
|
|
67
|
+
}
|
|
68
|
+
let { remoteUri, connection } = await promptRepositoryUri(projectId, location, existingConns);
|
|
35
69
|
while (remoteUri === "") {
|
|
36
70
|
await utils.openInBrowser("https://github.com/apps/google-cloud-build/installations/new");
|
|
37
71
|
await (0, prompt_1.promptOnce)({
|
|
38
72
|
type: "input",
|
|
39
73
|
message: "Press ENTER once you have finished configuring your installation's access settings.",
|
|
40
74
|
});
|
|
41
|
-
|
|
75
|
+
const selection = await promptRepositoryUri(projectId, location, existingConns);
|
|
76
|
+
remoteUri = selection.remoteUri;
|
|
77
|
+
connection = selection.connection;
|
|
42
78
|
}
|
|
79
|
+
const { id: connectionId } = parseConnectionName(connection.name);
|
|
80
|
+
await getOrCreateConnection(projectId, location, connectionId, {
|
|
81
|
+
authorizerCredential: (_b = connection.githubConfig) === null || _b === void 0 ? void 0 : _b.authorizerCredential,
|
|
82
|
+
appInstallationId: (_c = connection.githubConfig) === null || _c === void 0 ? void 0 : _c.appInstallationId,
|
|
83
|
+
});
|
|
43
84
|
const repo = await getOrCreateRepository(projectId, location, connectionId, remoteUri);
|
|
44
|
-
logger_1.logger.info(
|
|
85
|
+
logger_1.logger.info();
|
|
86
|
+
utils.logSuccess(`Successfully linked GitHub repository at remote URI:\n ${remoteUri}`);
|
|
45
87
|
return repo;
|
|
46
88
|
}
|
|
47
89
|
exports.linkGitHubRepository = linkGitHubRepository;
|
|
48
|
-
async function
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
90
|
+
async function promptRepositoryUri(projectId, location, connections) {
|
|
91
|
+
const remoteUriToConnection = {};
|
|
92
|
+
for (const conn of connections) {
|
|
93
|
+
const { id } = parseConnectionName(conn.name);
|
|
94
|
+
const resp = await gcb.fetchLinkableRepositories(projectId, location, id);
|
|
95
|
+
if (resp.repositories && resp.repositories.length > 1) {
|
|
96
|
+
for (const repo of resp.repositories) {
|
|
97
|
+
remoteUriToConnection[repo.remoteUri] = conn;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
53
100
|
}
|
|
54
|
-
const choices =
|
|
55
|
-
name:
|
|
56
|
-
value:
|
|
101
|
+
const choices = Object.keys(remoteUriToConnection).map((remoteUri) => ({
|
|
102
|
+
name: extractRepoSlugFromUri(remoteUri) || remoteUri,
|
|
103
|
+
value: remoteUri,
|
|
57
104
|
}));
|
|
58
105
|
choices.push({
|
|
59
106
|
name: "Missing a repo? Select this option to configure your installation's access settings",
|
|
60
107
|
value: "",
|
|
61
108
|
});
|
|
62
|
-
|
|
109
|
+
const remoteUri = await (0, prompt_1.promptOnce)({
|
|
63
110
|
type: "list",
|
|
64
|
-
message: "Which of the following repositories would you like to
|
|
111
|
+
message: "Which of the following repositories would you like to deploy?",
|
|
65
112
|
choices,
|
|
66
113
|
});
|
|
114
|
+
return { remoteUri, connection: remoteUriToConnection[remoteUri] };
|
|
67
115
|
}
|
|
68
|
-
async function promptConnectionAuth(conn
|
|
69
|
-
logger_1.logger.info(
|
|
70
|
-
logger_1.logger.info(
|
|
71
|
-
|
|
116
|
+
async function promptConnectionAuth(conn) {
|
|
117
|
+
logger_1.logger.info("You must authorize the Cloud Build GitHub app.");
|
|
118
|
+
logger_1.logger.info();
|
|
119
|
+
logger_1.logger.info("First, sign in to GitHub and authorize Cloud Build GitHub app:");
|
|
120
|
+
const cleanup = await utils.openInBrowserPopup(conn.installationState.actionUri, "Authorize the GitHub app");
|
|
72
121
|
await (0, prompt_1.promptOnce)({
|
|
73
122
|
type: "input",
|
|
74
|
-
message: "Press
|
|
123
|
+
message: "Press Enter once you have authorized the app",
|
|
75
124
|
});
|
|
76
|
-
|
|
125
|
+
cleanup();
|
|
126
|
+
const { projectId, location, id } = parseConnectionName(conn.name);
|
|
127
|
+
return await gcb.getConnection(projectId, location, id);
|
|
77
128
|
}
|
|
78
|
-
async function
|
|
129
|
+
async function promptAppInstall(conn) {
|
|
130
|
+
logger_1.logger.info("Now, install the Cloud Build GitHub app:");
|
|
131
|
+
const targetUri = conn.installationState.actionUri.replace("install_v2", "direct_install_v2");
|
|
132
|
+
logger_1.logger.info(targetUri);
|
|
133
|
+
await utils.openInBrowser(targetUri);
|
|
134
|
+
await (0, prompt_1.promptOnce)({
|
|
135
|
+
type: "input",
|
|
136
|
+
message: "Press Enter once you have installed or configured the Cloud Build GitHub app to access your GitHub repo.",
|
|
137
|
+
});
|
|
138
|
+
const { projectId, location, id } = parseConnectionName(conn.name);
|
|
139
|
+
return await gcb.getConnection(projectId, location, id);
|
|
140
|
+
}
|
|
141
|
+
async function createConnection(projectId, location, connectionId, githubConfig) {
|
|
142
|
+
const op = await gcb.createConnection(projectId, location, connectionId, githubConfig);
|
|
143
|
+
const conn = await poller.pollOperation(Object.assign(Object.assign({}, gcbPollerOptions), { pollerName: `create-${location}-${connectionId}`, operationResourceName: op.name }));
|
|
144
|
+
return conn;
|
|
145
|
+
}
|
|
146
|
+
exports.createConnection = createConnection;
|
|
147
|
+
async function getOrCreateConnection(projectId, location, connectionId, githubConfig) {
|
|
79
148
|
let conn;
|
|
80
149
|
try {
|
|
81
150
|
conn = await gcb.getConnection(projectId, location, connectionId);
|
|
82
151
|
}
|
|
83
152
|
catch (err) {
|
|
84
153
|
if (err.status === 404) {
|
|
85
|
-
|
|
86
|
-
conn = await poller.pollOperation(Object.assign(Object.assign({}, gcbPollerOptions), { pollerName: `create-${location}-${connectionId}`, operationResourceName: op.name }));
|
|
154
|
+
conn = await createConnection(projectId, location, connectionId, githubConfig);
|
|
87
155
|
}
|
|
88
156
|
else {
|
|
89
157
|
throw err;
|
|
90
158
|
}
|
|
91
159
|
}
|
|
92
|
-
while (conn.installationState.stage !== "COMPLETE") {
|
|
93
|
-
conn = await promptConnectionAuth(conn, projectId, location, connectionId);
|
|
94
|
-
}
|
|
95
160
|
return conn;
|
|
96
161
|
}
|
|
97
162
|
exports.getOrCreateConnection = getOrCreateConnection;
|
|
@@ -103,7 +168,7 @@ async function getOrCreateRepository(projectId, location, connectionId, remoteUr
|
|
|
103
168
|
let repo;
|
|
104
169
|
try {
|
|
105
170
|
repo = await gcb.getRepository(projectId, location, connectionId, repositoryId);
|
|
106
|
-
const repoSlug =
|
|
171
|
+
const repoSlug = extractRepoSlugFromUri(repo.remoteUri);
|
|
107
172
|
if (repoSlug) {
|
|
108
173
|
throw new error_1.FirebaseError(`${repoSlug} has already been linked.`);
|
|
109
174
|
}
|
|
@@ -120,3 +185,10 @@ async function getOrCreateRepository(projectId, location, connectionId, remoteUr
|
|
|
120
185
|
return repo;
|
|
121
186
|
}
|
|
122
187
|
exports.getOrCreateRepository = getOrCreateRepository;
|
|
188
|
+
async function listFrameworksConnections(projectId) {
|
|
189
|
+
const conns = await gcb.listConnections(projectId, "-");
|
|
190
|
+
return conns.filter((conn) => FRAMEWORKS_CONN_PATTERN.test(conn.name) &&
|
|
191
|
+
conn.installationState.stage === "COMPLETE" &&
|
|
192
|
+
!conn.disabled);
|
|
193
|
+
}
|
|
194
|
+
exports.listFrameworksConnections = listFrameworksConnections;
|
package/lib/localFunction.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HTTPS_SENTINEL = void 0;
|
|
3
4
|
const uuid = require("uuid");
|
|
4
|
-
const request = require("request");
|
|
5
5
|
const encodeFirestoreValue_1 = require("./firestore/encodeFirestoreValue");
|
|
6
6
|
const utils = require("./utils");
|
|
7
|
+
const apiv2_1 = require("./apiv2");
|
|
8
|
+
exports.HTTPS_SENTINEL = "Request sent to function.";
|
|
7
9
|
class LocalFunction {
|
|
8
10
|
constructor(trigger, urls, controller) {
|
|
9
11
|
this.trigger = trigger;
|
|
@@ -27,15 +29,73 @@ class LocalFunction {
|
|
|
27
29
|
if (opts.instanceIdToken) {
|
|
28
30
|
headers["Firebase-Instance-ID-Token"] = opts.instanceIdToken;
|
|
29
31
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (!this.url) {
|
|
33
|
+
throw new Error("No URL provided");
|
|
34
|
+
}
|
|
35
|
+
const client = new apiv2_1.Client({ urlPrefix: this.url, auth: false });
|
|
36
|
+
void client
|
|
37
|
+
.post("", data, { headers })
|
|
38
|
+
.then((res) => {
|
|
39
|
+
this.requestCallBack(undefined, res, res.body);
|
|
40
|
+
})
|
|
41
|
+
.catch((err) => {
|
|
42
|
+
this.requestCallBack(err);
|
|
37
43
|
});
|
|
38
44
|
}
|
|
45
|
+
constructHttpsFunc() {
|
|
46
|
+
if (!this.url) {
|
|
47
|
+
throw new Error("No URL provided");
|
|
48
|
+
}
|
|
49
|
+
const callClient = new apiv2_1.Client({ urlPrefix: this.url, auth: false });
|
|
50
|
+
const verbFactory = (hasRequestBody, method) => {
|
|
51
|
+
return async (pathOrOptions, options) => {
|
|
52
|
+
const { path, opts } = this.extractArgs(pathOrOptions, options);
|
|
53
|
+
try {
|
|
54
|
+
const res = hasRequestBody
|
|
55
|
+
? await method(path, opts.body, toClientVerbOptions(opts))
|
|
56
|
+
: await method(path, toClientVerbOptions(opts));
|
|
57
|
+
this.requestCallBack(undefined, res, res.body);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
this.requestCallBack(err);
|
|
61
|
+
}
|
|
62
|
+
return exports.HTTPS_SENTINEL;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
const shim = verbFactory(true, (path, json, opts) => {
|
|
66
|
+
const req = Object.assign(opts || {}, {
|
|
67
|
+
path: path,
|
|
68
|
+
body: json,
|
|
69
|
+
method: (opts === null || opts === void 0 ? void 0 : opts.method) || "GET",
|
|
70
|
+
});
|
|
71
|
+
return callClient.request(req);
|
|
72
|
+
});
|
|
73
|
+
const verbs = {
|
|
74
|
+
post: verbFactory(true, (path, json, opts) => callClient.post(path, json, opts)),
|
|
75
|
+
put: verbFactory(true, (path, json, opts) => callClient.put(path, json, opts)),
|
|
76
|
+
patch: verbFactory(true, (path, json, opts) => callClient.patch(path, json, opts)),
|
|
77
|
+
get: verbFactory(false, (path, opts) => callClient.get(path, opts)),
|
|
78
|
+
del: verbFactory(false, (path, opts) => callClient.delete(path, opts)),
|
|
79
|
+
delete: verbFactory(false, (path, opts) => callClient.delete(path, opts)),
|
|
80
|
+
options: verbFactory(false, (path, opts) => callClient.options(path, opts)),
|
|
81
|
+
};
|
|
82
|
+
return Object.assign(shim, verbs);
|
|
83
|
+
}
|
|
84
|
+
extractArgs(pathOrOptions, options) {
|
|
85
|
+
if (!pathOrOptions && !options) {
|
|
86
|
+
return { path: "/", opts: {} };
|
|
87
|
+
}
|
|
88
|
+
if (typeof pathOrOptions === "string") {
|
|
89
|
+
return { path: pathOrOptions, opts: options || {} };
|
|
90
|
+
}
|
|
91
|
+
if (typeof pathOrOptions !== "string" && !!pathOrOptions && !options) {
|
|
92
|
+
return { path: "/", opts: pathOrOptions };
|
|
93
|
+
}
|
|
94
|
+
if (typeof pathOrOptions !== "string" || !options) {
|
|
95
|
+
throw new Error(`Invalid argument combination: Expected a string and/or HttpsOptions, got ${typeof pathOrOptions} and ${typeof options}`);
|
|
96
|
+
}
|
|
97
|
+
return { path: "/", opts: {} };
|
|
98
|
+
}
|
|
39
99
|
constructAuth(auth, authType) {
|
|
40
100
|
var _a, _b, _c;
|
|
41
101
|
if ((auth === null || auth === void 0 ? void 0 : auth.admin) || (auth === null || auth === void 0 ? void 0 : auth.variable)) {
|
|
@@ -99,7 +159,7 @@ class LocalFunction {
|
|
|
99
159
|
if (err) {
|
|
100
160
|
return console.warn("\nERROR SENDING REQUEST: " + err);
|
|
101
161
|
}
|
|
102
|
-
const status = response ? response.
|
|
162
|
+
const status = response ? response.status + ", " : "";
|
|
103
163
|
let bodyString = body;
|
|
104
164
|
if (typeof bodyString === "string") {
|
|
105
165
|
try {
|
|
@@ -207,11 +267,7 @@ class LocalFunction {
|
|
|
207
267
|
return (data, opt) => this.constructCallableFunc(data, opt);
|
|
208
268
|
}
|
|
209
269
|
else {
|
|
210
|
-
return
|
|
211
|
-
callback: (...args) => this.requestCallBack(...args),
|
|
212
|
-
baseUrl: this.url,
|
|
213
|
-
uri: "",
|
|
214
|
-
});
|
|
270
|
+
return this.constructHttpsFunc();
|
|
215
271
|
}
|
|
216
272
|
}
|
|
217
273
|
else {
|
|
@@ -220,3 +276,10 @@ class LocalFunction {
|
|
|
220
276
|
}
|
|
221
277
|
}
|
|
222
278
|
exports.default = LocalFunction;
|
|
279
|
+
function toClientVerbOptions(opts) {
|
|
280
|
+
return {
|
|
281
|
+
method: opts.method,
|
|
282
|
+
headers: opts.headers,
|
|
283
|
+
queryParams: opts.qs,
|
|
284
|
+
};
|
|
285
|
+
}
|
package/lib/utils.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isVSCodeExtension = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
|
|
3
|
+
exports.openInBrowserPopup = exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isVSCodeExtension = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
4
6
|
const _ = require("lodash");
|
|
5
7
|
const url = require("url");
|
|
8
|
+
const http = require("http");
|
|
6
9
|
const clc = require("colorette");
|
|
7
10
|
const open = require("open");
|
|
8
11
|
const ora = require("ora");
|
|
@@ -12,6 +15,7 @@ const winston = require("winston");
|
|
|
12
15
|
const triple_beam_1 = require("triple-beam");
|
|
13
16
|
const assert_1 = require("assert");
|
|
14
17
|
const stripAnsi = require("strip-ansi");
|
|
18
|
+
const portfinder_1 = require("portfinder");
|
|
15
19
|
const configstore_1 = require("./configstore");
|
|
16
20
|
const error_1 = require("./error");
|
|
17
21
|
const logger_1 = require("./logger");
|
|
@@ -486,3 +490,26 @@ async function openInBrowser(url) {
|
|
|
486
490
|
await open(url);
|
|
487
491
|
}
|
|
488
492
|
exports.openInBrowser = openInBrowser;
|
|
493
|
+
async function openInBrowserPopup(url, buttonText) {
|
|
494
|
+
const popupPage = fs
|
|
495
|
+
.readFileSync(path.join(__dirname, "../templates/popup.html"), { encoding: "utf-8" })
|
|
496
|
+
.replace("${url}", url)
|
|
497
|
+
.replace("${buttonText}", buttonText);
|
|
498
|
+
const port = await (0, portfinder_1.getPortPromise)();
|
|
499
|
+
const server = http.createServer((req, res) => {
|
|
500
|
+
res.writeHead(200, {
|
|
501
|
+
"Content-Length": popupPage.length,
|
|
502
|
+
"Content-Type": "text/html",
|
|
503
|
+
});
|
|
504
|
+
res.end(popupPage);
|
|
505
|
+
req.socket.destroy();
|
|
506
|
+
});
|
|
507
|
+
server.listen(port);
|
|
508
|
+
const popupPageUri = `http://localhost:${port}`;
|
|
509
|
+
logger_1.logger.info(popupPageUri);
|
|
510
|
+
await openInBrowser(popupPageUri);
|
|
511
|
+
return () => {
|
|
512
|
+
server.close();
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
exports.openInBrowserPopup = openInBrowserPopup;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
],
|
|
30
30
|
"preferGlobal": true,
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
32
|
+
"node": ">=18.0.0 || >=20.0.0"
|
|
33
33
|
},
|
|
34
34
|
"author": "Firebase (https://firebase.google.com/)",
|
|
35
35
|
"license": "MIT",
|
|
@@ -100,7 +100,6 @@
|
|
|
100
100
|
"portfinder": "^1.0.32",
|
|
101
101
|
"progress": "^2.0.3",
|
|
102
102
|
"proxy-agent": "^6.3.0",
|
|
103
|
-
"request": "^2.87.0",
|
|
104
103
|
"retry": "^0.13.1",
|
|
105
104
|
"rimraf": "^3.0.0",
|
|
106
105
|
"semver": "^7.5.2",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<head>
|
|
2
|
+
<style>
|
|
3
|
+
div {
|
|
4
|
+
font-family: sans-serif;
|
|
5
|
+
}
|
|
6
|
+
#popup {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
align-items: center;
|
|
11
|
+
height: 100vh;
|
|
12
|
+
width: 100vw;
|
|
13
|
+
text-align: center;
|
|
14
|
+
}
|
|
15
|
+
#popup button {
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
border-radius: 0.375rem;
|
|
20
|
+
font-size: 1.5rem;
|
|
21
|
+
font-weight: 500;
|
|
22
|
+
height: 2.5rem;
|
|
23
|
+
padding: 0.5rem 1rem;
|
|
24
|
+
background-color: black;
|
|
25
|
+
color: white;
|
|
26
|
+
outline: none;
|
|
27
|
+
transition: background-color 150ms ease-in-out;
|
|
28
|
+
margin-bottom: 20px;
|
|
29
|
+
}
|
|
30
|
+
#popup button:hover {
|
|
31
|
+
background-color: #333;
|
|
32
|
+
}
|
|
33
|
+
#popup button:disabled {
|
|
34
|
+
opacity: 0.5;
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
}
|
|
37
|
+
#message {
|
|
38
|
+
height: 20px;
|
|
39
|
+
margin-bottom: 20px;
|
|
40
|
+
color: gray;
|
|
41
|
+
font-size: 1.5rem;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
<div id="popup">
|
|
47
|
+
<button id="popupBtn" onclick="openPopup()">${buttonText}</button>
|
|
48
|
+
<div id="message">(Close this window after authorizing the app)</div>
|
|
49
|
+
</div>
|
|
50
|
+
<script>
|
|
51
|
+
function openPopup() {
|
|
52
|
+
var w = window.open("${url}", "newwindow", "popup=1");
|
|
53
|
+
var m = document.getElementById("message");
|
|
54
|
+
var d = document.getElementById("popupBtn");
|
|
55
|
+
var timer = setInterval(function () {
|
|
56
|
+
if (w.closed) {
|
|
57
|
+
m.innerText = "You can close this page now";
|
|
58
|
+
d.disabled = true;
|
|
59
|
+
clearInterval(timer);
|
|
60
|
+
}
|
|
61
|
+
}, 1000);
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
</body>
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.command = void 0;
|
|
4
|
-
const marked_1 = require("marked");
|
|
5
|
-
const TerminalRenderer = require("marked-terminal");
|
|
6
|
-
const command_1 = require("../command");
|
|
7
|
-
const requireAuth_1 = require("../requireAuth");
|
|
8
|
-
const ext_dev_upload_1 = require("./ext-dev-upload");
|
|
9
|
-
const utils_1 = require("../utils");
|
|
10
|
-
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
11
|
-
marked_1.marked.setOptions({
|
|
12
|
-
renderer: new TerminalRenderer(),
|
|
13
|
-
});
|
|
14
|
-
exports.command = new command_1.Command("ext:dev:publish <extensionRef>")
|
|
15
|
-
.description(`Deprecated. Use ext:dev:upload instead`)
|
|
16
|
-
.option(`-s, --stage <stage>`, `release stage (supports "alpha", "beta", "rc", and "stable")`)
|
|
17
|
-
.option(`--repo <repo>`, `Public GitHub repo URI that contains the extension source`)
|
|
18
|
-
.option(`--ref <ref>`, `commit hash, branch, or tag to build from the repo (defaults to HEAD)`)
|
|
19
|
-
.option(`--root <root>`, `root directory that contains this extension (defaults to last uploaded root or "/" if none set)`)
|
|
20
|
-
.withForce()
|
|
21
|
-
.help("if you have not previously uploaded a version of this extension, this will " +
|
|
22
|
-
"create the extension. If you have previously uploaded a version of this extension, this version must " +
|
|
23
|
-
"be greater than previous versions.")
|
|
24
|
-
.before(requireAuth_1.requireAuth)
|
|
25
|
-
.before(extensionsHelper_1.ensureExtensionsPublisherApiEnabled)
|
|
26
|
-
.action(async (extensionRef, options) => {
|
|
27
|
-
(0, utils_1.logLabeledWarning)("Extensions", "ext:dev:publish has been deprecated and will be removed in the future. Please use ext:dev:upload instead.");
|
|
28
|
-
if (!options.repo && !options.ref && !options.root) {
|
|
29
|
-
options.local = true;
|
|
30
|
-
}
|
|
31
|
-
return (0, ext_dev_upload_1.uploadExtensionAction)(extensionRef, options);
|
|
32
|
-
});
|