firebase-tools 15.10.1 → 15.12.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/agentSkills.js +70 -0
- package/lib/api.js +3 -1
- package/lib/appdistribution/client.js +17 -0
- package/lib/apphosting/backend.js +22 -3
- package/lib/apptesting/parseTestFiles.js +11 -8
- package/lib/bin/mcp.js +5 -1
- package/lib/commands/apphosting-backends-create.js +19 -2
- package/lib/commands/apphosting-backends-list.js +21 -5
- package/lib/commands/apptesting.js +16 -7
- package/lib/commands/functions-delete.js +1 -0
- package/lib/commands/functions-export.js +40 -0
- package/lib/commands/index.js +3 -0
- package/lib/commands/init.js +1 -0
- package/lib/commands/studio-export.js +2 -2
- package/lib/deploy/apphosting/deploy.js +11 -6
- package/lib/deploy/apphosting/prepare.js +21 -1
- package/lib/deploy/apphosting/release.js +2 -5
- package/lib/deploy/apphosting/util.js +45 -2
- package/lib/deploy/firestore/prepare.js +17 -0
- package/lib/deploy/functions/prepare.js +4 -1
- package/lib/deploy/functions/release/fabricator.js +4 -3
- package/lib/deploy/functions/release/index.js +5 -0
- package/lib/deploy/functions/runtimes/dart/index.js +282 -0
- package/lib/deploy/functions/runtimes/index.js +1 -1
- package/lib/deploy/functions/runtimes/supported/index.js +4 -0
- package/lib/deploy/functions/services/ailogic.js +68 -0
- package/lib/deploy/functions/services/index.js +4 -0
- package/lib/emulator/downloadableEmulatorInfo.json +30 -30
- package/lib/emulator/functionsEmulator.js +103 -24
- package/lib/emulator/functionsRuntimeWorker.js +21 -18
- package/lib/emulator/storage/rules/manager.js +10 -3
- package/lib/emulator/storage/rules/runtime.js +9 -7
- package/lib/experiments.js +22 -0
- package/lib/firebase_studio/migrate.js +83 -70
- package/lib/functions/iac/export.js +36 -0
- package/lib/functions/iac/terraform.js +146 -0
- package/lib/gcp/ailogic.js +108 -0
- package/lib/gcp/cloudfunctionsv2.js +24 -0
- package/lib/init/features/agentSkills.js +26 -0
- package/lib/init/features/dataconnect/sdk.js +26 -12
- package/lib/init/features/functions/dart.js +31 -0
- package/lib/init/features/functions/index.js +14 -0
- package/lib/init/features/index.js +4 -1
- package/lib/init/index.js +6 -0
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/lib/utils.js +8 -0
- package/package.json +5 -3
- package/schema/firebase-config.json +7 -0
- package/standalone/package.json +1 -1
- package/templates/firebase-studio-export/readme_template.md +2 -0
- package/templates/init/functions/dart/_gitignore +11 -0
- package/templates/init/functions/dart/pubspec.yaml +14 -0
- package/templates/init/functions/dart/server.dart +15 -0
- package/lib/deploy/functions/runtimes/dart.js +0 -42
|
@@ -1,13 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createLocalBuildTarArchive = createLocalBuildTarArchive;
|
|
4
|
+
exports.createSourceDeployArchive = createSourceDeployArchive;
|
|
4
5
|
const archiver = require("archiver");
|
|
5
6
|
const fs = require("fs");
|
|
6
7
|
const path = require("path");
|
|
8
|
+
const tar = require("tar");
|
|
7
9
|
const tmp = require("tmp");
|
|
8
10
|
const error_1 = require("../../error");
|
|
9
11
|
const fsAsync = require("../../fsAsync");
|
|
10
|
-
|
|
12
|
+
const config_1 = require("../../apphosting/config");
|
|
13
|
+
async function createLocalBuildTarArchive(config, rootDir, targetSubDir) {
|
|
14
|
+
const tmpFile = tmp.fileSync({ prefix: `${config.backendId}-`, postfix: ".tar.gz" }).name;
|
|
15
|
+
const targetDir = targetSubDir ? path.join(rootDir, targetSubDir) : rootDir;
|
|
16
|
+
const ignore = ["firebase-debug.log", "firebase-debug.*.log", ".git"];
|
|
17
|
+
const rdrFiles = await fsAsync.readdirRecursive({
|
|
18
|
+
path: targetDir,
|
|
19
|
+
ignore: ignore,
|
|
20
|
+
isGitIgnore: true,
|
|
21
|
+
});
|
|
22
|
+
const allFiles = rdrFiles.map((rdrf) => path.relative(rootDir, rdrf.name));
|
|
23
|
+
if (targetSubDir) {
|
|
24
|
+
const defaultFiles = fs.readdirSync(rootDir).filter((file) => {
|
|
25
|
+
return config_1.APPHOSTING_YAML_FILE_REGEX.test(file);
|
|
26
|
+
});
|
|
27
|
+
for (const file of defaultFiles) {
|
|
28
|
+
if (!allFiles.includes(file)) {
|
|
29
|
+
allFiles.push(file);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
fs.statSync(rootDir);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
38
|
+
throw new error_1.FirebaseError(`Could not read directory "${rootDir}"`);
|
|
39
|
+
}
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
if (!allFiles.length) {
|
|
43
|
+
throw new error_1.FirebaseError(`Cannot create a tar archive with 0 files from directory "${rootDir}"`);
|
|
44
|
+
}
|
|
45
|
+
await tar.create({
|
|
46
|
+
gzip: true,
|
|
47
|
+
file: tmpFile,
|
|
48
|
+
cwd: rootDir,
|
|
49
|
+
portable: true,
|
|
50
|
+
}, allFiles);
|
|
51
|
+
return tmpFile;
|
|
52
|
+
}
|
|
53
|
+
async function createSourceDeployArchive(config, rootDir, targetSubDir) {
|
|
11
54
|
const tmpFile = tmp.fileSync({ prefix: `${config.backendId}-`, postfix: ".zip" }).name;
|
|
12
55
|
const fileStream = fs.createWriteStream(tmpFile, {
|
|
13
56
|
flags: "w",
|
|
@@ -52,6 +52,21 @@ async function createDatabase(context, options) {
|
|
|
52
52
|
}
|
|
53
53
|
edition = upperEdition;
|
|
54
54
|
}
|
|
55
|
+
let firestoreDataAccessMode;
|
|
56
|
+
let mongodbCompatibleDataAccessMode;
|
|
57
|
+
if (firestoreCfg.dataAccessMode) {
|
|
58
|
+
if (edition !== types.DatabaseEdition.ENTERPRISE) {
|
|
59
|
+
throw new error_1.FirebaseError("dataAccessMode can only be specified for enterprise edition databases.");
|
|
60
|
+
}
|
|
61
|
+
if (firestoreCfg.dataAccessMode === "FIRESTORE_NATIVE") {
|
|
62
|
+
firestoreDataAccessMode = types.DataAccessMode.ENABLED;
|
|
63
|
+
mongodbCompatibleDataAccessMode = types.DataAccessMode.DISABLED;
|
|
64
|
+
}
|
|
65
|
+
else if (firestoreCfg.dataAccessMode === "MONGODB_COMPATIBLE") {
|
|
66
|
+
firestoreDataAccessMode = types.DataAccessMode.DISABLED;
|
|
67
|
+
mongodbCompatibleDataAccessMode = types.DataAccessMode.ENABLED;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
55
70
|
const api = new api_2.FirestoreApi();
|
|
56
71
|
try {
|
|
57
72
|
await api.getDatabase(options.projectId, firestoreCfg.database);
|
|
@@ -67,6 +82,8 @@ async function createDatabase(context, options) {
|
|
|
67
82
|
databaseEdition: edition,
|
|
68
83
|
deleteProtectionState: types.DatabaseDeleteProtectionState.DISABLED,
|
|
69
84
|
pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement.DISABLED,
|
|
85
|
+
firestoreDataAccessMode,
|
|
86
|
+
mongodbCompatibleDataAccessMode,
|
|
70
87
|
};
|
|
71
88
|
await api.createDatabase(createDatabaseReq);
|
|
72
89
|
}
|
|
@@ -13,6 +13,7 @@ const clc = require("colorette");
|
|
|
13
13
|
const proto = require("../../gcp/proto");
|
|
14
14
|
const backend = require("./backend");
|
|
15
15
|
const build = require("./build");
|
|
16
|
+
const experiments = require("../../experiments");
|
|
16
17
|
const ensureApiEnabled = require("../../ensureApiEnabled");
|
|
17
18
|
const functionsConfig = require("../../functionsConfig");
|
|
18
19
|
const functionsEnv = require("../../functions/env");
|
|
@@ -322,7 +323,9 @@ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, fil
|
|
|
322
323
|
}
|
|
323
324
|
const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
|
|
324
325
|
logger_1.logger.debug(`Validating ${runtimeDelegate.language} source`);
|
|
325
|
-
|
|
326
|
+
if (!experiments.isEnabled("bypassfunctionsdeprecationcheck")) {
|
|
327
|
+
supported.guardVersionSupport(runtimeDelegate.runtime);
|
|
328
|
+
}
|
|
326
329
|
await runtimeDelegate.validate();
|
|
327
330
|
logger_1.logger.debug(`Building ${runtimeDelegate.language} source`);
|
|
328
331
|
await runtimeDelegate.build();
|
|
@@ -57,6 +57,7 @@ class Fabricator {
|
|
|
57
57
|
constructor(args) {
|
|
58
58
|
this.executor = args.executor;
|
|
59
59
|
this.functionExecutor = args.functionExecutor;
|
|
60
|
+
this.runFunctionExecutor = args.runFunctionExecutor;
|
|
60
61
|
this.sources = args.sources;
|
|
61
62
|
this.appEngineLocation = args.appEngineLocation;
|
|
62
63
|
this.projectNumber = args.projectNumber;
|
|
@@ -521,7 +522,7 @@ class Fabricator {
|
|
|
521
522
|
generation: storageSource.generation ? String(storageSource.generation) : undefined,
|
|
522
523
|
},
|
|
523
524
|
};
|
|
524
|
-
await this.
|
|
525
|
+
await this.runFunctionExecutor
|
|
525
526
|
.run(async () => {
|
|
526
527
|
const op = await runV2.createService(endpoint.project, endpoint.region, endpoint.id, service);
|
|
527
528
|
endpoint.uri = op.uri;
|
|
@@ -546,7 +547,7 @@ class Fabricator {
|
|
|
546
547
|
generation: storageSource.generation ? String(storageSource.generation) : undefined,
|
|
547
548
|
},
|
|
548
549
|
};
|
|
549
|
-
await this.
|
|
550
|
+
await this.runFunctionExecutor
|
|
550
551
|
.run(async () => {
|
|
551
552
|
const op = await runV2.updateService(service);
|
|
552
553
|
endpoint.uri = op.uri;
|
|
@@ -556,7 +557,7 @@ class Fabricator {
|
|
|
556
557
|
await this.setInvoker(endpoint);
|
|
557
558
|
}
|
|
558
559
|
async deleteRunFunction(endpoint) {
|
|
559
|
-
await this.
|
|
560
|
+
await this.runFunctionExecutor
|
|
560
561
|
.run(async () => {
|
|
561
562
|
try {
|
|
562
563
|
await runV2.deleteService(endpoint.project, endpoint.region, endpoint.id);
|
|
@@ -70,9 +70,14 @@ async function release(context, options, payload) {
|
|
|
70
70
|
concurrency: 40,
|
|
71
71
|
maxBackoff: 100000,
|
|
72
72
|
};
|
|
73
|
+
const runThrottlerOptions = {
|
|
74
|
+
...throttlerOptions,
|
|
75
|
+
concurrency: 2,
|
|
76
|
+
};
|
|
73
77
|
const projectNumber = options.projectNumber || (await (0, getProjectNumber_1.getProjectNumber)(context.projectId));
|
|
74
78
|
const fab = new fabricator.Fabricator({
|
|
75
79
|
functionExecutor: new executor.QueueExecutor(throttlerOptions),
|
|
80
|
+
runFunctionExecutor: new executor.QueueExecutor(runThrottlerOptions),
|
|
76
81
|
executor: new executor.QueueExecutor(throttlerOptions),
|
|
77
82
|
sources: context.sources,
|
|
78
83
|
appEngineLocation: (0, functionsConfig_1.getAppEngineLocation)(context.firebaseConfig),
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Delegate = exports.DART_ENTRY_POINT = void 0;
|
|
4
|
+
exports.tryCreateDelegate = tryCreateDelegate;
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const util_1 = require("util");
|
|
8
|
+
const spawn = require("cross-spawn");
|
|
9
|
+
const discovery = require("../discovery");
|
|
10
|
+
const supported = require("../supported");
|
|
11
|
+
const logger_1 = require("../../../../logger");
|
|
12
|
+
const error_1 = require("../../../../error");
|
|
13
|
+
const utils_1 = require("../../../../utils");
|
|
14
|
+
const registry_1 = require("../../../../emulator/registry");
|
|
15
|
+
const types_1 = require("../../../../emulator/types");
|
|
16
|
+
async function tryCreateDelegate(context) {
|
|
17
|
+
const pubspecYamlPath = path.join(context.sourceDir, "pubspec.yaml");
|
|
18
|
+
if (!(await (0, util_1.promisify)(fs.exists)(pubspecYamlPath))) {
|
|
19
|
+
logger_1.logger.debug("Customer code is not Dart code.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const runtime = context.runtime ?? supported.latest("dart");
|
|
23
|
+
if (!supported.isRuntime(runtime)) {
|
|
24
|
+
throw new error_1.FirebaseError(`Runtime ${runtime} is not a valid Dart runtime`);
|
|
25
|
+
}
|
|
26
|
+
if (!supported.runtimeIsLanguage(runtime, "dart")) {
|
|
27
|
+
throw new error_1.FirebaseError(`Internal error. Trying to construct a dart runtime delegate for runtime ${runtime}`, { exit: 1 });
|
|
28
|
+
}
|
|
29
|
+
return Promise.resolve(new Delegate(context.projectId, context.sourceDir, runtime));
|
|
30
|
+
}
|
|
31
|
+
const MIN_DART_SDK_VERSION = "3.8.0";
|
|
32
|
+
exports.DART_ENTRY_POINT = "bin/server.dart";
|
|
33
|
+
class Delegate {
|
|
34
|
+
constructor(projectId, sourceDir, runtime) {
|
|
35
|
+
this.projectId = projectId;
|
|
36
|
+
this.sourceDir = sourceDir;
|
|
37
|
+
this.runtime = runtime;
|
|
38
|
+
this.language = "dart";
|
|
39
|
+
this.bin = "dart";
|
|
40
|
+
this.entryPoint = exports.DART_ENTRY_POINT;
|
|
41
|
+
this.buildRunnerProcess = null;
|
|
42
|
+
}
|
|
43
|
+
async validate() {
|
|
44
|
+
const result = spawn.sync(this.bin, ["--version"], {
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
timeout: 10000,
|
|
47
|
+
});
|
|
48
|
+
if (result.error) {
|
|
49
|
+
throw new error_1.FirebaseError(`Could not find a Dart SDK. Make sure the '${this.bin}' command is available on your PATH.`);
|
|
50
|
+
}
|
|
51
|
+
const versionOutput = (result.stdout || result.stderr || "").toString();
|
|
52
|
+
const match = /Dart SDK version:\s*(\d+\.\d+\.\d+)/.exec(versionOutput);
|
|
53
|
+
if (match) {
|
|
54
|
+
const installedVersion = match[1];
|
|
55
|
+
if (installedVersion.localeCompare(MIN_DART_SDK_VERSION, undefined, { numeric: true }) < 0) {
|
|
56
|
+
throw new error_1.FirebaseError(`Dart SDK version ${installedVersion} is not supported. ` +
|
|
57
|
+
`Firebase Functions for Dart requires Dart ${MIN_DART_SDK_VERSION} or later. ` +
|
|
58
|
+
`Please upgrade your Dart SDK.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
logger_1.logger.debug(`Could not parse Dart SDK version from: ${versionOutput}`);
|
|
63
|
+
}
|
|
64
|
+
const pubspecYamlPath = path.join(this.sourceDir, "pubspec.yaml");
|
|
65
|
+
try {
|
|
66
|
+
await fs.promises.access(pubspecYamlPath, fs.constants.R_OK);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new error_1.FirebaseError(`Failed to read pubspec.yaml at ${pubspecYamlPath}: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
const entryPointPath = path.join(this.sourceDir, this.entryPoint);
|
|
72
|
+
try {
|
|
73
|
+
await fs.promises.access(entryPointPath, fs.constants.R_OK);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new error_1.FirebaseError(`Could not find entry point at ${entryPointPath}. ` +
|
|
77
|
+
`Firebase Functions for Dart expects your main function in ${this.entryPoint}.`);
|
|
78
|
+
}
|
|
79
|
+
const packageConfigPath = path.join(this.sourceDir, ".dart_tool", "package_config.json");
|
|
80
|
+
try {
|
|
81
|
+
await fs.promises.access(packageConfigPath, fs.constants.R_OK);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
(0, utils_1.logLabeledBullet)("functions", "running dart pub get...");
|
|
85
|
+
const pubGetProcess = spawn(this.bin, ["pub", "get"], {
|
|
86
|
+
cwd: this.sourceDir,
|
|
87
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
88
|
+
});
|
|
89
|
+
pubGetProcess.stdout?.on("data", (chunk) => {
|
|
90
|
+
logger_1.logger.debug(`[dart pub get] ${chunk.toString("utf8").trim()}`);
|
|
91
|
+
});
|
|
92
|
+
pubGetProcess.stderr?.on("data", (chunk) => {
|
|
93
|
+
logger_1.logger.debug(`[dart pub get] ${chunk.toString("utf8").trim()}`);
|
|
94
|
+
});
|
|
95
|
+
await new Promise((resolve, reject) => {
|
|
96
|
+
pubGetProcess.on("exit", (code) => {
|
|
97
|
+
if (code === 0 || code === null) {
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
reject(new error_1.FirebaseError(`dart pub get failed with exit code ${code}. ` +
|
|
102
|
+
`Make sure your pubspec.yaml is valid and dependencies are available.`));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
pubGetProcess.on("error", reject);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async build() {
|
|
110
|
+
if (Delegate.watchModeActive) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
(0, utils_1.logLabeledBullet)("functions", "running build_runner...");
|
|
114
|
+
const buildRunnerProcess = spawn(this.bin, ["run", "build_runner", "build", "--delete-conflicting-outputs"], {
|
|
115
|
+
cwd: this.sourceDir,
|
|
116
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
117
|
+
});
|
|
118
|
+
buildRunnerProcess.stdout?.on("data", (chunk) => {
|
|
119
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8").trim()}`);
|
|
120
|
+
});
|
|
121
|
+
buildRunnerProcess.stderr?.on("data", (chunk) => {
|
|
122
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8").trim()}`);
|
|
123
|
+
});
|
|
124
|
+
await new Promise((resolve, reject) => {
|
|
125
|
+
buildRunnerProcess.on("exit", (code) => {
|
|
126
|
+
if (code === 0 || code === null) {
|
|
127
|
+
resolve();
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
reject(new error_1.FirebaseError(`build_runner failed with exit code ${code}. ` +
|
|
131
|
+
`Make sure your Dart project is properly configured.`));
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
buildRunnerProcess.on("error", reject);
|
|
135
|
+
});
|
|
136
|
+
if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FUNCTIONS)) {
|
|
137
|
+
logger_1.logger.debug("Skipping Dart compilation in emulator mode.");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const binDir = path.join(this.sourceDir, "bin");
|
|
141
|
+
await fs.promises.mkdir(binDir, { recursive: true });
|
|
142
|
+
(0, utils_1.logLabeledBullet)("functions", "compiling Dart to linux-x64 executable...");
|
|
143
|
+
const compileProcess = spawn(this.bin, [
|
|
144
|
+
"compile",
|
|
145
|
+
"exe",
|
|
146
|
+
this.entryPoint,
|
|
147
|
+
"-o",
|
|
148
|
+
"bin/server",
|
|
149
|
+
"--target-os=linux",
|
|
150
|
+
"--target-arch=x64",
|
|
151
|
+
], {
|
|
152
|
+
cwd: this.sourceDir,
|
|
153
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
154
|
+
});
|
|
155
|
+
compileProcess.stdout?.on("data", (chunk) => {
|
|
156
|
+
logger_1.logger.debug(`[dart compile] ${chunk.toString("utf8").trim()}`);
|
|
157
|
+
});
|
|
158
|
+
compileProcess.stderr?.on("data", (chunk) => {
|
|
159
|
+
logger_1.logger.debug(`[dart compile] ${chunk.toString("utf8").trim()}`);
|
|
160
|
+
});
|
|
161
|
+
await new Promise((resolve, reject) => {
|
|
162
|
+
compileProcess.on("exit", (code) => {
|
|
163
|
+
if (code === 0 || code === null) {
|
|
164
|
+
resolve();
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
reject(new error_1.FirebaseError(`Dart compilation failed with exit code ${code}. ` +
|
|
168
|
+
`Make sure your Dart project compiles successfully with: ` +
|
|
169
|
+
`dart compile exe ${this.entryPoint} --target-os=linux --target-arch=x64`));
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
compileProcess.on("error", reject);
|
|
173
|
+
});
|
|
174
|
+
(0, utils_1.logLabeledBullet)("functions", "Dart compilation complete.");
|
|
175
|
+
}
|
|
176
|
+
async watch(onRebuild) {
|
|
177
|
+
Delegate.watchModeActive = true;
|
|
178
|
+
logger_1.logger.debug("Starting build_runner watch for Dart functions...");
|
|
179
|
+
const buildRunnerProcess = spawn(this.bin, ["run", "build_runner", "watch", "--delete-conflicting-outputs"], {
|
|
180
|
+
cwd: this.sourceDir,
|
|
181
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
182
|
+
});
|
|
183
|
+
this.buildRunnerProcess = buildRunnerProcess;
|
|
184
|
+
let initialBuildComplete = false;
|
|
185
|
+
let resolveInitialBuild;
|
|
186
|
+
let rejectInitialBuild;
|
|
187
|
+
const initialBuildPromise = new Promise((resolve, reject) => {
|
|
188
|
+
resolveInitialBuild = resolve;
|
|
189
|
+
rejectInitialBuild = reject;
|
|
190
|
+
});
|
|
191
|
+
const buildCompletePattern = /Succeeded after|Built with build_runner/;
|
|
192
|
+
buildRunnerProcess.stdout?.on("data", (chunk) => {
|
|
193
|
+
const output = chunk.toString("utf8").trim();
|
|
194
|
+
if (output) {
|
|
195
|
+
logger_1.logger.debug(`[build_runner] ${output}`);
|
|
196
|
+
if (buildCompletePattern.test(output)) {
|
|
197
|
+
if (!initialBuildComplete) {
|
|
198
|
+
initialBuildComplete = true;
|
|
199
|
+
logger_1.logger.debug("build_runner initial build completed");
|
|
200
|
+
resolveInitialBuild();
|
|
201
|
+
}
|
|
202
|
+
else if (onRebuild) {
|
|
203
|
+
onRebuild();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
buildRunnerProcess.stderr?.on("data", (chunk) => {
|
|
209
|
+
const output = chunk.toString("utf8").trim();
|
|
210
|
+
if (output) {
|
|
211
|
+
logger_1.logger.debug(`[build_runner] ${output}`);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
buildRunnerProcess.on("exit", (code) => {
|
|
215
|
+
if (code !== 0 && code !== null) {
|
|
216
|
+
logger_1.logger.debug(`build_runner exited with code ${code}. Initial build failed.`);
|
|
217
|
+
if (!initialBuildComplete) {
|
|
218
|
+
rejectInitialBuild(new error_1.FirebaseError(`build_runner exited with code ${code}. Your Dart functions may not be deployed or emulated correctly.`));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.buildRunnerProcess = null;
|
|
222
|
+
});
|
|
223
|
+
buildRunnerProcess.on("error", (err) => {
|
|
224
|
+
logger_1.logger.debug(`Failed to start build_runner: ${err.message}. Your Dart functions may not be deployed or emulated correctly.`);
|
|
225
|
+
if (!initialBuildComplete) {
|
|
226
|
+
rejectInitialBuild(err);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
await initialBuildPromise;
|
|
230
|
+
return async () => {
|
|
231
|
+
if (this.buildRunnerProcess && !this.buildRunnerProcess.killed) {
|
|
232
|
+
this.buildRunnerProcess.kill("SIGTERM");
|
|
233
|
+
this.buildRunnerProcess = null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
async discoverBuild(_configValues, envs) {
|
|
238
|
+
const yamlDir = this.sourceDir;
|
|
239
|
+
const yamlPath = path.join(yamlDir, "functions.yaml");
|
|
240
|
+
let discovered = await discovery.detectFromYaml(yamlDir, this.projectId, this.runtime);
|
|
241
|
+
if (!discovered) {
|
|
242
|
+
logger_1.logger.debug("functions.yaml not found, running build_runner to generate it...");
|
|
243
|
+
const buildRunnerProcess = spawn(this.bin, ["run", "build_runner", "build"], {
|
|
244
|
+
cwd: this.sourceDir,
|
|
245
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
246
|
+
});
|
|
247
|
+
buildRunnerProcess.stdout?.on("data", (chunk) => {
|
|
248
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8")}`);
|
|
249
|
+
});
|
|
250
|
+
buildRunnerProcess.stderr?.on("data", (chunk) => {
|
|
251
|
+
logger_1.logger.debug(`[build_runner] ${chunk.toString("utf8")}`);
|
|
252
|
+
});
|
|
253
|
+
await new Promise((resolve, reject) => {
|
|
254
|
+
buildRunnerProcess.on("exit", (code) => {
|
|
255
|
+
if (code === 0 || code === null) {
|
|
256
|
+
resolve();
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
reject(new error_1.FirebaseError(`build_runner failed with exit code ${code}. Make sure your Dart project is properly configured.`));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
buildRunnerProcess.on("error", reject);
|
|
263
|
+
});
|
|
264
|
+
discovered = await discovery.detectFromYaml(yamlDir, this.projectId, this.runtime);
|
|
265
|
+
if (!discovered) {
|
|
266
|
+
throw new error_1.FirebaseError(`Could not find functions.yaml at ${yamlPath} after running build_runner. ` +
|
|
267
|
+
`Make sure your Dart project is properly configured with firebase_functions.`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const isEmulator = envs.FUNCTIONS_EMULATOR === "true";
|
|
271
|
+
if (!isEmulator) {
|
|
272
|
+
for (const ep of Object.values(discovered.endpoints)) {
|
|
273
|
+
if (ep.platform === "gcfv2") {
|
|
274
|
+
ep.platform = "run";
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return discovered;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
exports.Delegate = Delegate;
|
|
282
|
+
Delegate.watchModeActive = false;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getRuntimeDelegate = getRuntimeDelegate;
|
|
4
|
+
const dart = require("./dart");
|
|
4
5
|
const node = require("./node");
|
|
5
6
|
const python = require("./python");
|
|
6
7
|
const validate = require("../validate");
|
|
7
8
|
const error_1 = require("../../../error");
|
|
8
9
|
const supported = require("./supported");
|
|
9
|
-
const dart = require("./dart");
|
|
10
10
|
const experiments = require("../../../experiments");
|
|
11
11
|
const factories = [
|
|
12
12
|
node.tryCreateDelegate,
|
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.isRuntime = isRuntime;
|
|
18
18
|
exports.runtimeIsLanguage = runtimeIsLanguage;
|
|
19
|
+
exports.isLanguageRuntime = isLanguageRuntime;
|
|
19
20
|
exports.latest = latest;
|
|
20
21
|
exports.isDecommissioned = isDecommissioned;
|
|
21
22
|
exports.guardVersionSupport = guardVersionSupport;
|
|
@@ -29,6 +30,9 @@ function isRuntime(maybe) {
|
|
|
29
30
|
function runtimeIsLanguage(runtime, language) {
|
|
30
31
|
return runtime.startsWith(language);
|
|
31
32
|
}
|
|
33
|
+
function isLanguageRuntime(runtime, language) {
|
|
34
|
+
return !!runtime && runtime.startsWith(language);
|
|
35
|
+
}
|
|
32
36
|
function latest(language, runtimes = Object.keys(types_1.RUNTIMES)) {
|
|
33
37
|
const sorted = runtimes
|
|
34
38
|
.filter((s) => runtimeIsLanguage(s, language))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AILogicService = exports.AI_LOGIC_EVENTS = exports.AI_LOGIC_AFTER_GENERATE_CONTENT = exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = void 0;
|
|
4
|
+
exports.isAILogicEvent = isAILogicEvent;
|
|
5
|
+
const backend = require("../backend");
|
|
6
|
+
const error_1 = require("../../../error");
|
|
7
|
+
const ailogicApi = require("../../../gcp/ailogic");
|
|
8
|
+
exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = "firebase.vertexai.v1beta.beforeGenerateContent";
|
|
9
|
+
exports.AI_LOGIC_AFTER_GENERATE_CONTENT = "firebase.vertexai.v1beta.afterGenerateContent";
|
|
10
|
+
exports.AI_LOGIC_EVENTS = [
|
|
11
|
+
exports.AI_LOGIC_BEFORE_GENERATE_CONTENT,
|
|
12
|
+
exports.AI_LOGIC_AFTER_GENERATE_CONTENT,
|
|
13
|
+
];
|
|
14
|
+
function isAILogicEvent(endpoint) {
|
|
15
|
+
if (!backend.isBlockingTriggered(endpoint)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return exports.AI_LOGIC_EVENTS.includes(endpoint.blockingTrigger.eventType);
|
|
19
|
+
}
|
|
20
|
+
class AILogicService {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.ensureTriggerRegion = () => Promise.resolve();
|
|
23
|
+
this.name = "ailogic";
|
|
24
|
+
this.api = "firebasevertexai.googleapis.com";
|
|
25
|
+
}
|
|
26
|
+
validateTrigger(endpoint, wantBackend) {
|
|
27
|
+
if (!isAILogicEvent(endpoint)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const eventType = endpoint.blockingTrigger.eventType;
|
|
31
|
+
const regionalWebhook = !!endpoint.blockingTrigger.options?.regionalWebhook;
|
|
32
|
+
const conflict = backend.allEndpoints(wantBackend).some((ep) => {
|
|
33
|
+
if (!isAILogicEvent(ep)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (ep.blockingTrigger.eventType !== eventType || ep.id === endpoint.id) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (regionalWebhook) {
|
|
40
|
+
return ep.blockingTrigger.options?.regionalWebhook && ep.region === endpoint.region;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return !ep.blockingTrigger.options?.regionalWebhook;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
if (conflict) {
|
|
47
|
+
if (regionalWebhook) {
|
|
48
|
+
throw new error_1.FirebaseError(`Can only create at most one regional AI Logic Trigger for ${eventType} in region ${endpoint.region}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw new error_1.FirebaseError(`Can only create at most one global AI Logic Trigger for ${eventType}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async registerTrigger(ep) {
|
|
56
|
+
if (!isAILogicEvent(ep)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await ailogicApi.upsertBlockingFunction(ep);
|
|
60
|
+
}
|
|
61
|
+
async unregisterTrigger(ep) {
|
|
62
|
+
if (!isAILogicEvent(ep)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await ailogicApi.deleteBlockingFunction(ep);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.AILogicService = AILogicService;
|
|
@@ -11,6 +11,7 @@ const remoteConfig_1 = require("./remoteConfig");
|
|
|
11
11
|
const testLab_1 = require("./testLab");
|
|
12
12
|
const firestore_1 = require("./firestore");
|
|
13
13
|
const dataconnect_1 = require("./dataconnect");
|
|
14
|
+
const ailogic_1 = require("./ailogic");
|
|
14
15
|
const noop = () => Promise.resolve();
|
|
15
16
|
exports.noop = noop;
|
|
16
17
|
const noopProjectBindings = () => Promise.resolve([]);
|
|
@@ -51,6 +52,7 @@ const firebaseAlertsService = {
|
|
|
51
52
|
unregisterTrigger: exports.noop,
|
|
52
53
|
};
|
|
53
54
|
const authBlockingService = new auth_1.AuthBlockingService();
|
|
55
|
+
const aiLogicService = new ailogic_1.AILogicService();
|
|
54
56
|
const databaseService = {
|
|
55
57
|
name: "database",
|
|
56
58
|
api: "firebasedatabase.googleapis.com",
|
|
@@ -122,6 +124,8 @@ const EVENT_SERVICE_MAPPING = {
|
|
|
122
124
|
"google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService,
|
|
123
125
|
"google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService,
|
|
124
126
|
"google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService,
|
|
127
|
+
"firebase.vertexai.v1beta.beforeGenerateContent": aiLogicService,
|
|
128
|
+
"firebase.vertexai.v1beta.afterGenerateContent": aiLogicService,
|
|
125
129
|
};
|
|
126
130
|
function serviceForEndpoint(endpoint) {
|
|
127
131
|
if (backend.isEventTriggered(endpoint)) {
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
|
|
9
9
|
},
|
|
10
10
|
"firestore": {
|
|
11
|
-
"version": "1.20.
|
|
12
|
-
"expectedSize":
|
|
13
|
-
"expectedChecksum": "
|
|
14
|
-
"expectedChecksumSHA256": "
|
|
15
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.
|
|
16
|
-
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.
|
|
11
|
+
"version": "1.20.4",
|
|
12
|
+
"expectedSize": 136551710,
|
|
13
|
+
"expectedChecksum": "8397dac992e02454914dec28d23b6ed8",
|
|
14
|
+
"expectedChecksumSHA256": "49ee7814741b481ae5becfdaa7b55693bdf7a03363cd184e35c36c1595a9643",
|
|
15
|
+
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.4.jar",
|
|
16
|
+
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.4.jar"
|
|
17
17
|
},
|
|
18
18
|
"storage": {
|
|
19
19
|
"version": "1.1.3",
|
|
@@ -54,36 +54,36 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "3.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
57
|
+
"version": "3.3.1",
|
|
58
|
+
"expectedSize": 32105392,
|
|
59
|
+
"expectedChecksum": "dfe6ff725864c37889c238aac24be304",
|
|
60
|
+
"expectedChecksumSHA256": "1ccfebb4c0ff85f0503b907aa589658774bb6d4fac0180c3fa6bdff3a865090d",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.1",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "3.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
65
|
+
"version": "3.3.1",
|
|
66
|
+
"expectedSize": 30287922,
|
|
67
|
+
"expectedChecksum": "59af3cb51c78e8114d634afea811f8cc",
|
|
68
|
+
"expectedChecksumSHA256": "75bde31d15c4cc14de7071ff50cdd0d8fb644491e0c9ee3c1da82fdaf8d976a9",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.1",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "3.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
73
|
+
"version": "3.3.1",
|
|
74
|
+
"expectedSize": 32148992,
|
|
75
|
+
"expectedChecksum": "3205dc92bbc7edb4c5821641486dc70c",
|
|
76
|
+
"expectedChecksumSHA256": "fa7796077728414b0682f6ccf1df0fc7b2f56bb590f1e62698dbb7c1ddaf0fb0",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.1",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
81
|
+
"version": "3.3.1",
|
|
82
|
+
"expectedSize": 31264952,
|
|
83
|
+
"expectedChecksum": "85ffdef78810e0074ac94c453a200daa",
|
|
84
|
+
"expectedChecksumSHA256": "f232e03165c28f72bc99dfbd2dde1bcaf8298045817b60d72d407da7dae9e9f1",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.1",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.1"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|