firebase-functions 6.6.0 → 7.0.0-rc.1
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/_virtual/rolldown_runtime.js +34 -0
- package/lib/bin/firebase-functions.js +78 -103
- package/lib/common/app.js +35 -55
- package/lib/common/change.js +54 -75
- package/lib/common/config.js +41 -41
- package/lib/common/debug.js +23 -47
- package/lib/common/encoding.js +59 -82
- package/lib/common/onInit.js +26 -28
- package/lib/common/options.js +22 -42
- package/lib/common/params.d.ts +6 -6
- package/lib/common/params.js +0 -23
- package/lib/common/providers/database.js +270 -300
- package/lib/common/providers/firestore.js +66 -92
- package/lib/common/providers/https.d.ts +0 -1
- package/lib/common/providers/https.js +537 -539
- package/lib/common/providers/identity.js +393 -444
- package/lib/common/providers/tasks.js +64 -98
- package/lib/common/timezone.js +544 -542
- package/lib/common/trace.d.ts +0 -1
- package/lib/common/trace.js +63 -55
- package/lib/common/utilities/assertions.d.ts +11 -0
- package/lib/common/utilities/assertions.js +18 -0
- package/lib/common/utilities/encoder.js +20 -37
- package/lib/common/utilities/path-pattern.js +106 -132
- package/lib/common/utilities/path.js +28 -27
- package/lib/common/utilities/utils.js +23 -45
- package/lib/esm/_virtual/rolldown_runtime.mjs +16 -0
- package/lib/esm/bin/firebase-functions.mjs +91 -0
- package/lib/esm/common/app.mjs +39 -0
- package/lib/esm/common/change.mjs +57 -0
- package/lib/esm/common/config.mjs +45 -0
- package/lib/esm/common/debug.mjs +28 -0
- package/lib/esm/common/encoding.mjs +69 -0
- package/lib/esm/common/onInit.mjs +33 -0
- package/lib/esm/common/options.mjs +22 -0
- package/lib/esm/common/params.mjs +1 -0
- package/lib/esm/common/providers/database.mjs +269 -0
- package/lib/esm/common/providers/firestore.mjs +78 -0
- package/lib/esm/common/providers/https.mjs +573 -0
- package/lib/esm/common/providers/identity.mjs +428 -0
- package/lib/esm/common/providers/tasks.mjs +67 -0
- package/lib/esm/common/timezone.mjs +544 -0
- package/lib/esm/common/trace.mjs +73 -0
- package/lib/esm/common/utilities/assertions.mjs +17 -0
- package/lib/esm/common/utilities/encoder.mjs +21 -0
- package/lib/esm/common/utilities/path-pattern.mjs +116 -0
- package/lib/esm/common/utilities/path.mjs +35 -0
- package/lib/esm/common/utilities/utils.mjs +29 -0
- package/lib/esm/function-configuration.mjs +1 -0
- package/lib/esm/logger/common.mjs +23 -0
- package/lib/esm/logger/compat.mjs +25 -0
- package/lib/esm/logger/index.mjs +131 -0
- package/lib/esm/params/index.mjs +160 -0
- package/lib/esm/params/types.mjs +400 -0
- package/lib/esm/runtime/loader.mjs +132 -0
- package/lib/esm/runtime/manifest.mjs +134 -0
- package/lib/esm/types/global.d.mjs +1 -0
- package/lib/esm/v1/cloud-functions.mjs +206 -0
- package/lib/esm/v1/config.mjs +14 -0
- package/lib/esm/v1/function-builder.mjs +252 -0
- package/lib/esm/v1/function-configuration.mjs +72 -0
- package/lib/esm/v1/index.mjs +27 -0
- package/lib/esm/v1/providers/analytics.mjs +212 -0
- package/lib/esm/v1/providers/auth.mjs +156 -0
- package/lib/esm/v1/providers/database.mjs +243 -0
- package/lib/esm/v1/providers/firestore.mjs +131 -0
- package/lib/esm/v1/providers/https.mjs +82 -0
- package/lib/esm/v1/providers/pubsub.mjs +175 -0
- package/lib/esm/v1/providers/remoteConfig.mjs +64 -0
- package/lib/esm/v1/providers/storage.mjs +163 -0
- package/lib/esm/v1/providers/tasks.mjs +63 -0
- package/lib/esm/v1/providers/testLab.mjs +94 -0
- package/lib/esm/v2/core.mjs +4 -0
- package/lib/esm/v2/index.mjs +28 -0
- package/lib/esm/v2/options.mjs +102 -0
- package/lib/esm/v2/providers/alerts/alerts.mjs +85 -0
- package/lib/esm/v2/providers/alerts/appDistribution.mjs +75 -0
- package/lib/esm/v2/providers/alerts/billing.mjs +51 -0
- package/lib/esm/v2/providers/alerts/crashlytics.mjs +122 -0
- package/lib/esm/v2/providers/alerts/index.mjs +22 -0
- package/lib/esm/v2/providers/alerts/performance.mjs +66 -0
- package/lib/esm/v2/providers/database.mjs +197 -0
- package/lib/esm/v2/providers/dataconnect.mjs +130 -0
- package/lib/esm/v2/providers/eventarc.mjs +51 -0
- package/lib/esm/v2/providers/firestore.mjs +294 -0
- package/lib/esm/v2/providers/https.mjs +210 -0
- package/lib/esm/v2/providers/identity.mjs +103 -0
- package/lib/esm/v2/providers/pubsub.mjs +148 -0
- package/lib/esm/v2/providers/remoteConfig.mjs +52 -0
- package/lib/esm/v2/providers/scheduler.mjs +84 -0
- package/lib/esm/v2/providers/storage.mjs +155 -0
- package/lib/esm/v2/providers/tasks.mjs +65 -0
- package/lib/esm/v2/providers/testLab.mjs +53 -0
- package/lib/esm/v2/trace.mjs +20 -0
- package/lib/function-configuration.d.ts +0 -0
- package/lib/function-configuration.js +0 -0
- package/lib/logger/common.js +21 -41
- package/lib/logger/compat.js +18 -33
- package/lib/logger/index.js +119 -130
- package/lib/params/index.d.ts +4 -2
- package/lib/params/index.js +150 -144
- package/lib/params/types.js +389 -423
- package/lib/runtime/loader.js +114 -148
- package/lib/runtime/manifest.js +106 -126
- package/lib/types/global.d.js +0 -0
- package/lib/v1/cloud-functions.d.ts +2 -2
- package/lib/v1/cloud-functions.js +193 -241
- package/lib/v1/config.d.ts +4 -7
- package/lib/v1/config.js +13 -75
- package/lib/v1/function-builder.js +239 -368
- package/lib/v1/function-configuration.js +70 -63
- package/lib/v1/index.js +118 -73
- package/lib/v1/providers/analytics.js +188 -235
- package/lib/v1/providers/auth.d.ts +2 -1
- package/lib/v1/providers/auth.js +159 -164
- package/lib/v1/providers/database.js +237 -242
- package/lib/v1/providers/firestore.js +131 -130
- package/lib/v1/providers/https.d.ts +2 -1
- package/lib/v1/providers/https.js +79 -86
- package/lib/v1/providers/pubsub.js +175 -172
- package/lib/v1/providers/remoteConfig.js +64 -68
- package/lib/v1/providers/storage.js +161 -163
- package/lib/v1/providers/tasks.d.ts +1 -1
- package/lib/v1/providers/tasks.js +65 -80
- package/lib/v1/providers/testLab.js +94 -94
- package/lib/v2/core.d.ts +1 -1
- package/lib/v2/core.js +5 -32
- package/lib/v2/index.d.ts +6 -3
- package/lib/v2/index.js +123 -75
- package/lib/v2/options.js +88 -114
- package/lib/v2/providers/alerts/alerts.js +76 -95
- package/lib/v2/providers/alerts/appDistribution.js +73 -78
- package/lib/v2/providers/alerts/billing.js +49 -53
- package/lib/v2/providers/alerts/crashlytics.js +110 -102
- package/lib/v2/providers/alerts/index.js +56 -53
- package/lib/v2/providers/alerts/performance.js +64 -74
- package/lib/v2/providers/database.js +177 -180
- package/lib/v2/providers/dataconnect.d.ts +95 -0
- package/lib/v2/providers/dataconnect.js +137 -0
- package/lib/v2/providers/eventarc.js +55 -77
- package/lib/v2/providers/firestore.js +262 -260
- package/lib/v2/providers/https.d.ts +3 -2
- package/lib/v2/providers/https.js +210 -247
- package/lib/v2/providers/identity.d.ts +2 -1
- package/lib/v2/providers/identity.js +96 -105
- package/lib/v2/providers/pubsub.js +149 -167
- package/lib/v2/providers/remoteConfig.js +54 -63
- package/lib/v2/providers/scheduler.js +84 -96
- package/lib/v2/providers/storage.js +147 -162
- package/lib/v2/providers/tasks.d.ts +1 -1
- package/lib/v2/providers/tasks.js +68 -95
- package/lib/v2/providers/testLab.js +55 -64
- package/lib/v2/trace.js +18 -19
- package/package.json +321 -88
- package/protos/compiledFirestore.mjs +3512 -0
- package/protos/update.sh +28 -7
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadStack } from "../runtime/loader.mjs";
|
|
3
|
+
import { stackToWire } from "../runtime/manifest.mjs";
|
|
4
|
+
import express from "express";
|
|
5
|
+
import fs from "fs/promises";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
|
|
8
|
+
//#region src/bin/firebase-functions.ts
|
|
9
|
+
function printUsageAndExit() {
|
|
10
|
+
console.error(`
|
|
11
|
+
Usage: firebase-functions [functionsDir]
|
|
12
|
+
|
|
13
|
+
Arguments:
|
|
14
|
+
- functionsDir: Directory containing source code for Firebase Functions.
|
|
15
|
+
`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
let functionsDir = ".";
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
if (args.length > 1) {
|
|
21
|
+
if (args[0] === "-h" || args[0] === "--help") {
|
|
22
|
+
printUsageAndExit();
|
|
23
|
+
}
|
|
24
|
+
functionsDir = args[0];
|
|
25
|
+
}
|
|
26
|
+
function handleQuitquitquit(req, res, server) {
|
|
27
|
+
res.send("ok");
|
|
28
|
+
server.close();
|
|
29
|
+
}
|
|
30
|
+
if (process.env.FUNCTIONS_MANIFEST_OUTPUT_PATH) {
|
|
31
|
+
void (async () => {
|
|
32
|
+
const outputPath = process.env.FUNCTIONS_MANIFEST_OUTPUT_PATH;
|
|
33
|
+
try {
|
|
34
|
+
const dir = path.dirname(outputPath);
|
|
35
|
+
try {
|
|
36
|
+
await fs.access(dir, fs.constants.W_OK);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.error(`Error: Cannot write to directory '${dir}': ${e instanceof Error ? e.message : String(e)}`);
|
|
39
|
+
console.error("Please ensure the directory exists and you have write permissions.");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const stack = await loadStack(functionsDir);
|
|
43
|
+
const wireFormat = stackToWire(stack);
|
|
44
|
+
await fs.writeFile(outputPath, JSON.stringify(wireFormat, null, 2));
|
|
45
|
+
process.exit(0);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e.code === "ENOENT") {
|
|
48
|
+
console.error(`Error: Directory '${path.dirname(outputPath)}' does not exist.`);
|
|
49
|
+
console.error("Please create the directory or specify a valid path.");
|
|
50
|
+
} else if (e.code === "EACCES") {
|
|
51
|
+
console.error(`Error: Permission denied writing to '${outputPath}'.`);
|
|
52
|
+
console.error("Please check file permissions or choose a different location.");
|
|
53
|
+
} else if (e.message?.includes("Failed to generate manifest")) {
|
|
54
|
+
console.error(e.message);
|
|
55
|
+
} else {
|
|
56
|
+
console.error(`Failed to generate manifest from function source: ${e instanceof Error ? e.message : String(e)}`);
|
|
57
|
+
}
|
|
58
|
+
if (e instanceof Error && e.stack) {
|
|
59
|
+
console.error(e.stack);
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
} else {
|
|
65
|
+
let server = undefined;
|
|
66
|
+
const app = express();
|
|
67
|
+
app.get("/__/quitquitquit", (req, res) => handleQuitquitquit(req, res, server));
|
|
68
|
+
app.post("/__/quitquitquit", (req, res) => handleQuitquitquit(req, res, server));
|
|
69
|
+
if (process.env.FUNCTIONS_CONTROL_API === "true") {
|
|
70
|
+
app.get("/__/functions.yaml", async (req, res) => {
|
|
71
|
+
try {
|
|
72
|
+
const stack = await loadStack(functionsDir);
|
|
73
|
+
res.setHeader("content-type", "text/yaml");
|
|
74
|
+
res.send(JSON.stringify(stackToWire(stack)));
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error(e);
|
|
77
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
78
|
+
res.status(400).send(`Failed to generate manifest from function source: ${errorMessage}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
let port = 8080;
|
|
83
|
+
if (process.env.PORT) {
|
|
84
|
+
port = Number.parseInt(process.env.PORT);
|
|
85
|
+
}
|
|
86
|
+
console.log("Serving at port", port);
|
|
87
|
+
server = app.listen(port);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { firebaseConfig } from "./config.mjs";
|
|
2
|
+
import { applicationDefault, deleteApp, getApp as getApp$1, initializeApp } from "firebase-admin/app";
|
|
3
|
+
|
|
4
|
+
//#region src/common/app.ts
|
|
5
|
+
const APP_NAME = "__FIREBASE_FUNCTIONS_SDK__";
|
|
6
|
+
let cache;
|
|
7
|
+
function getApp() {
|
|
8
|
+
if (typeof cache === "undefined") {
|
|
9
|
+
try {
|
|
10
|
+
cache = getApp$1();
|
|
11
|
+
} catch {
|
|
12
|
+
cache = initializeApp({
|
|
13
|
+
...firebaseConfig(),
|
|
14
|
+
credential: applicationDefault()
|
|
15
|
+
}, APP_NAME);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return cache;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* This function allows the Firebase Emulator Suite to override the FirebaseApp instance
|
|
22
|
+
* used by the Firebase Functions SDK. Developers should never call this function for
|
|
23
|
+
* other purposes.
|
|
24
|
+
* N.B. For clarity for use in testing this name has no mention of emulation, but
|
|
25
|
+
* it must be exported from index as app.setEmulatedAdminApp or we break the emulator.
|
|
26
|
+
* We can remove this export when:
|
|
27
|
+
* A) We complete the new emulator and no longer depend on monkeypatching
|
|
28
|
+
* B) We tweak the CLI to look for different APIs to monkeypatch depending on versions.
|
|
29
|
+
* @alpha
|
|
30
|
+
*/
|
|
31
|
+
function setApp(app) {
|
|
32
|
+
if (cache?.name === APP_NAME) {
|
|
33
|
+
void deleteApp(cache);
|
|
34
|
+
}
|
|
35
|
+
cache = app;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { getApp, setApp };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//#region src/common/change.ts
|
|
2
|
+
/** @internal */
|
|
3
|
+
function applyFieldMask(sparseBefore, after, fieldMask) {
|
|
4
|
+
const before = { ...after };
|
|
5
|
+
const masks = fieldMask.split(",");
|
|
6
|
+
for (const mask of masks) {
|
|
7
|
+
const parts = mask.split(".");
|
|
8
|
+
const head = parts[0];
|
|
9
|
+
const tail = parts.slice(1).join(".");
|
|
10
|
+
if (parts.length > 1) {
|
|
11
|
+
before[head] = applyFieldMask(sparseBefore?.[head], after[head], tail);
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const val = sparseBefore?.[head];
|
|
15
|
+
if (typeof val === "undefined") {
|
|
16
|
+
delete before[mask];
|
|
17
|
+
} else {
|
|
18
|
+
before[mask] = val;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return before;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The Cloud Functions interface for events that change state, such as
|
|
25
|
+
* Realtime Database or Cloud Firestore `onWrite` and `onUpdate` events.
|
|
26
|
+
*
|
|
27
|
+
* For more information about the format used to construct `Change` objects, see
|
|
28
|
+
* {@link ChangeJson} below.
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
var Change = class Change {
|
|
32
|
+
/**
|
|
33
|
+
* Factory method for creating a `Change` from a `before` object and an `after`
|
|
34
|
+
* object.
|
|
35
|
+
*/
|
|
36
|
+
static fromObjects(before, after) {
|
|
37
|
+
return new Change(before, after);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Factory method for creating a `Change` from JSON and an optional customizer
|
|
41
|
+
* function to be applied to both the `before` and the `after` fields.
|
|
42
|
+
*/
|
|
43
|
+
static fromJSON(json, customizer = (x) => x) {
|
|
44
|
+
let before = { ...json.before };
|
|
45
|
+
if (json.fieldMask) {
|
|
46
|
+
before = applyFieldMask(before, json.after, json.fieldMask);
|
|
47
|
+
}
|
|
48
|
+
return Change.fromObjects(customizer(before || {}), customizer(json.after || {}));
|
|
49
|
+
}
|
|
50
|
+
constructor(before, after) {
|
|
51
|
+
this.before = before;
|
|
52
|
+
this.after = after;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { Change, applyFieldMask };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { warn } from "../logger/index.mjs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
|
|
5
|
+
//#region src/common/config.ts
|
|
6
|
+
let cache = null;
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
* @alpha
|
|
10
|
+
*/
|
|
11
|
+
function resetCache(newCache = null) {
|
|
12
|
+
cache = newCache;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the fields you need to initialize a Firebase app
|
|
16
|
+
* @alpha
|
|
17
|
+
*/
|
|
18
|
+
function firebaseConfig() {
|
|
19
|
+
if (cache) {
|
|
20
|
+
return cache;
|
|
21
|
+
}
|
|
22
|
+
let env = process.env.FIREBASE_CONFIG;
|
|
23
|
+
if (env) {
|
|
24
|
+
if (!env.startsWith("{")) {
|
|
25
|
+
env = fs.readFileSync(path.join(process.env.PWD, env)).toString("utf8");
|
|
26
|
+
}
|
|
27
|
+
cache = JSON.parse(env);
|
|
28
|
+
return cache;
|
|
29
|
+
}
|
|
30
|
+
if (process.env.GCLOUD_PROJECT) {
|
|
31
|
+
warn("Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail");
|
|
32
|
+
cache = {
|
|
33
|
+
databaseURL: process.env.DATABASE_URL || `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,
|
|
34
|
+
storageBucket: process.env.STORAGE_BUCKET_URL || `${process.env.GCLOUD_PROJECT}.appspot.com`,
|
|
35
|
+
projectId: process.env.GCLOUD_PROJECT
|
|
36
|
+
};
|
|
37
|
+
return cache;
|
|
38
|
+
} else {
|
|
39
|
+
warn("Warning, FIREBASE_CONFIG and GCLOUD_PROJECT environment variables are missing. Initializing firebase-admin will fail");
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { firebaseConfig, resetCache };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/common/debug.ts
|
|
2
|
+
const debugMode = process.env.FIREBASE_DEBUG_MODE === "true";
|
|
3
|
+
function loadDebugFeatures() {
|
|
4
|
+
if (!debugMode) {
|
|
5
|
+
return {};
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
const obj = JSON.parse(process.env.FIREBASE_DEBUG_FEATURES);
|
|
9
|
+
if (typeof obj !== "object") {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
} catch (_e) {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function debugFeatureValue(feat) {
|
|
18
|
+
if (!debugMode) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
return loadDebugFeatures()[feat];
|
|
22
|
+
}
|
|
23
|
+
function isDebugFeatureEnabled(feat) {
|
|
24
|
+
return debugMode && !!debugFeatureValue(feat);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { debugFeatureValue, isDebugFeatureEnabled };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Expression } from "../params/types.mjs";
|
|
2
|
+
import "../params/index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/common/encoding.ts
|
|
5
|
+
/** Get a google.protobuf.Duration for a number of seconds. */
|
|
6
|
+
function durationFromSeconds(s) {
|
|
7
|
+
return `${s}s`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Utility function to help copy fields from type A to B.
|
|
11
|
+
* As a safety net, catches typos or fields that aren't named the same
|
|
12
|
+
* in A and B, but cannot verify that both Src and Dest have the same type for the same field.
|
|
13
|
+
*/
|
|
14
|
+
function copyIfPresent(dest, src, ...fields) {
|
|
15
|
+
if (!src) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (const field of fields) {
|
|
19
|
+
if (!Object.prototype.hasOwnProperty.call(src, field)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
dest[field] = src[field];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function convertIfPresent(dest, src, destField, srcField, converter = (from) => {
|
|
26
|
+
return from;
|
|
27
|
+
}) {
|
|
28
|
+
if (!src) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!Object.prototype.hasOwnProperty.call(src, srcField)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
dest[destField] = converter(src[srcField]);
|
|
35
|
+
}
|
|
36
|
+
function serviceAccountFromShorthand(serviceAccount) {
|
|
37
|
+
if (serviceAccount === "default") {
|
|
38
|
+
return null;
|
|
39
|
+
} else if (serviceAccount instanceof Expression) {
|
|
40
|
+
return serviceAccount;
|
|
41
|
+
} else if (serviceAccount.endsWith("@")) {
|
|
42
|
+
if (!process.env.GCLOUD_PROJECT) {
|
|
43
|
+
throw new Error(`Unable to determine email for service account '${serviceAccount}' because process.env.GCLOUD_PROJECT is not set.`);
|
|
44
|
+
}
|
|
45
|
+
return `${serviceAccount}${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
|
|
46
|
+
} else if (serviceAccount.includes("@")) {
|
|
47
|
+
return serviceAccount;
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error(`Invalid option for serviceAccount: '${serviceAccount}'. Valid options are 'default', a service account email, or '{serviceAccountName}@'`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function convertInvoker(invoker) {
|
|
53
|
+
if (typeof invoker === "string") {
|
|
54
|
+
invoker = [invoker];
|
|
55
|
+
}
|
|
56
|
+
if (invoker.length === 0) {
|
|
57
|
+
throw new Error("Invalid option for invoker: Must be a non-empty array.");
|
|
58
|
+
}
|
|
59
|
+
if (invoker.find((inv) => inv.length === 0)) {
|
|
60
|
+
throw new Error("Invalid option for invoker: Must be a non-empty string.");
|
|
61
|
+
}
|
|
62
|
+
if (invoker.length > 1 && invoker.find((inv) => inv === "public" || inv === "private")) {
|
|
63
|
+
throw new Error("Invalid option for invoker: Cannot have 'public' or 'private' in an array of service accounts.");
|
|
64
|
+
}
|
|
65
|
+
return invoker;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
export { convertIfPresent, convertInvoker, copyIfPresent, durationFromSeconds, serviceAccountFromShorthand };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { warn } from "../logger/index.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/common/onInit.ts
|
|
4
|
+
let initCallback = null;
|
|
5
|
+
let didInit = false;
|
|
6
|
+
/**
|
|
7
|
+
* Registers a callback that should be run when in a production environment
|
|
8
|
+
* before executing any functions code.
|
|
9
|
+
* Calling this function more than once leads to undefined behavior.
|
|
10
|
+
* @param callback initialization callback to be run before any function executes.
|
|
11
|
+
*/
|
|
12
|
+
function onInit(callback) {
|
|
13
|
+
if (initCallback) {
|
|
14
|
+
warn("Setting onInit callback more than once. Only the most recent callback will be called");
|
|
15
|
+
}
|
|
16
|
+
initCallback = callback;
|
|
17
|
+
didInit = false;
|
|
18
|
+
}
|
|
19
|
+
/** @internal */
|
|
20
|
+
function withInit(func) {
|
|
21
|
+
return async (...args) => {
|
|
22
|
+
if (!didInit) {
|
|
23
|
+
if (initCallback) {
|
|
24
|
+
await initCallback();
|
|
25
|
+
}
|
|
26
|
+
didInit = true;
|
|
27
|
+
}
|
|
28
|
+
return func(...args);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { onInit, withInit };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/common/options.ts
|
|
2
|
+
/**
|
|
3
|
+
* Special configuration type to reset configuration to platform default.
|
|
4
|
+
*
|
|
5
|
+
* @alpha
|
|
6
|
+
*/
|
|
7
|
+
var ResetValue = class ResetValue {
|
|
8
|
+
toJSON() {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
constructor() {}
|
|
12
|
+
static getInstance() {
|
|
13
|
+
return new ResetValue();
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Special configuration value to reset configuration to platform default.
|
|
18
|
+
*/
|
|
19
|
+
const RESET_VALUE = ResetValue.getInstance();
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { RESET_VALUE, ResetValue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { firebaseConfig } from "../config.mjs";
|
|
2
|
+
import { joinPath, pathParts } from "../utilities/path.mjs";
|
|
3
|
+
import * as database from "firebase-admin/database";
|
|
4
|
+
|
|
5
|
+
//#region src/common/providers/database.ts
|
|
6
|
+
/**
|
|
7
|
+
* Interface representing a Firebase Realtime database data snapshot.
|
|
8
|
+
*/
|
|
9
|
+
var DataSnapshot = class DataSnapshot {
|
|
10
|
+
constructor(data, path, app, instance) {
|
|
11
|
+
this.app = app;
|
|
12
|
+
const config = firebaseConfig();
|
|
13
|
+
if (instance) {
|
|
14
|
+
this.instance = instance;
|
|
15
|
+
} else if (app) {
|
|
16
|
+
this.instance = app.options.databaseURL;
|
|
17
|
+
} else if (config.databaseURL) {
|
|
18
|
+
this.instance = config.databaseURL;
|
|
19
|
+
} else if (process.env.GCLOUD_PROJECT) {
|
|
20
|
+
this.instance = "https://" + process.env.GCLOUD_PROJECT + "-default-rtdb.firebaseio.com";
|
|
21
|
+
}
|
|
22
|
+
this._path = path;
|
|
23
|
+
this._data = data;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns a [`Reference`](/docs/reference/admin/node/admin.database.Reference)
|
|
27
|
+
* to the database location where the triggering write occurred. Has
|
|
28
|
+
* full read and write access.
|
|
29
|
+
*/
|
|
30
|
+
get ref() {
|
|
31
|
+
if (!this.app) {
|
|
32
|
+
throw new Error("Please supply a Firebase app in the constructor for DataSnapshot" + " in order to use the .ref method.");
|
|
33
|
+
}
|
|
34
|
+
if (!this._ref) {
|
|
35
|
+
let db;
|
|
36
|
+
if (this.instance) {
|
|
37
|
+
db = database.getDatabaseWithUrl(this.instance, this.app);
|
|
38
|
+
} else {
|
|
39
|
+
db = database.getDatabase(this.app);
|
|
40
|
+
}
|
|
41
|
+
this._ref = db.ref(this._fullPath());
|
|
42
|
+
}
|
|
43
|
+
return this._ref;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The key (last part of the path) of the location of this `DataSnapshot`.
|
|
47
|
+
*
|
|
48
|
+
* The last token in a database location is considered its key. For example,
|
|
49
|
+
* "ada" is the key for the `/users/ada/` node. Accessing the key on any
|
|
50
|
+
* `DataSnapshot` returns the key for the location that generated it.
|
|
51
|
+
* However, accessing the key on the root URL of a database returns `null`.
|
|
52
|
+
*/
|
|
53
|
+
get key() {
|
|
54
|
+
const segments = pathParts(this._fullPath());
|
|
55
|
+
const last = segments[segments.length - 1];
|
|
56
|
+
return !last || last === "" ? null : last;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extracts a JavaScript value from a `DataSnapshot`.
|
|
60
|
+
*
|
|
61
|
+
* Depending on the data in a `DataSnapshot`, the `val()` method may return a
|
|
62
|
+
* scalar type (string, number, or boolean), an array, or an object. It may also
|
|
63
|
+
* return `null`, indicating that the `DataSnapshot` is empty (contains no
|
|
64
|
+
* data).
|
|
65
|
+
*
|
|
66
|
+
* @return The snapshot's contents as a JavaScript value (Object,
|
|
67
|
+
* Array, string, number, boolean, or `null`).
|
|
68
|
+
*/
|
|
69
|
+
val() {
|
|
70
|
+
const parts = pathParts(this._childPath);
|
|
71
|
+
let source = this._data;
|
|
72
|
+
if (source === null) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
if (parts.length) {
|
|
76
|
+
for (const part of parts) {
|
|
77
|
+
if (typeof source === "undefined" || source === null) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
source = source[part];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const node = source ?? null;
|
|
84
|
+
return this._checkAndConvertToArray(node);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Exports the entire contents of the `DataSnapshot` as a JavaScript object.
|
|
88
|
+
*
|
|
89
|
+
* @return The contents of the `DataSnapshot` as a JavaScript value
|
|
90
|
+
* (Object, Array, string, number, boolean, or `null`).
|
|
91
|
+
*/
|
|
92
|
+
exportVal() {
|
|
93
|
+
return this.val();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Gets the priority value of the data in this `DataSnapshot`.
|
|
97
|
+
*
|
|
98
|
+
* As an alternative to using priority, applications can order collections by
|
|
99
|
+
* ordinary properties. See [Sorting and filtering
|
|
100
|
+
* data](/docs/database/web/lists-of-data#sorting_and_filtering_data).
|
|
101
|
+
*
|
|
102
|
+
* @return The priority value of the data.
|
|
103
|
+
*/
|
|
104
|
+
getPriority() {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns `true` if this `DataSnapshot` contains any data. It is slightly more
|
|
109
|
+
* efficient than using `snapshot.val() !== null`.
|
|
110
|
+
*
|
|
111
|
+
* @return `true` if this `DataSnapshot` contains any data; otherwise, `false`.
|
|
112
|
+
*/
|
|
113
|
+
exists() {
|
|
114
|
+
const val = this.val();
|
|
115
|
+
if (typeof val === "undefined" || val === null) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (typeof val === "object" && Object.keys(val).length === 0) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets a `DataSnapshot` for the location at the specified relative path.
|
|
125
|
+
*
|
|
126
|
+
* The relative path can either be a simple child name (for example, "ada") or
|
|
127
|
+
* a deeper slash-separated path (for example, "ada/name/first").
|
|
128
|
+
*
|
|
129
|
+
* @param path A relative path from this location to the desired child
|
|
130
|
+
* location.
|
|
131
|
+
* @return The specified child location.
|
|
132
|
+
*/
|
|
133
|
+
child(childPath) {
|
|
134
|
+
if (!childPath) {
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
return this._dup(childPath);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Enumerates the `DataSnapshot`s of the children items.
|
|
141
|
+
*
|
|
142
|
+
* Because of the way JavaScript objects work, the ordering of data in the
|
|
143
|
+
* JavaScript object returned by `val()` is not guaranteed to match the ordering
|
|
144
|
+
* on the server nor the ordering of `child_added` events. That is where
|
|
145
|
+
* `forEach()` comes in handy. It guarantees the children of a `DataSnapshot`
|
|
146
|
+
* can be iterated in their query order.
|
|
147
|
+
*
|
|
148
|
+
* If no explicit `orderBy*()` method is used, results are returned
|
|
149
|
+
* ordered by key (unless priorities are used, in which case, results are
|
|
150
|
+
* returned by priority).
|
|
151
|
+
*
|
|
152
|
+
* @param action A function that is called for each child `DataSnapshot`.
|
|
153
|
+
* The callback can return `true` to cancel further enumeration.
|
|
154
|
+
*
|
|
155
|
+
* @return `true` if enumeration was canceled due to your callback
|
|
156
|
+
* returning `true`.
|
|
157
|
+
*/
|
|
158
|
+
forEach(action) {
|
|
159
|
+
const val = this.val() || {};
|
|
160
|
+
if (typeof val === "object") {
|
|
161
|
+
return Object.keys(val).some((key) => action(this.child(key)) === true);
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Returns `true` if the specified child path has (non-`null`) data.
|
|
167
|
+
*
|
|
168
|
+
* @param path A relative path to the location of a potential child.
|
|
169
|
+
* @return `true` if data exists at the specified child path; otherwise,
|
|
170
|
+
* `false`.
|
|
171
|
+
*/
|
|
172
|
+
hasChild(childPath) {
|
|
173
|
+
return this.child(childPath).exists();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Returns whether or not the `DataSnapshot` has any non-`null` child
|
|
177
|
+
* properties.
|
|
178
|
+
*
|
|
179
|
+
* You can use `hasChildren()` to determine if a `DataSnapshot` has any
|
|
180
|
+
* children. If it does, you can enumerate them using `forEach()`. If it
|
|
181
|
+
* doesn't, then either this snapshot contains a primitive value (which can be
|
|
182
|
+
* retrieved with `val()`) or it is empty (in which case, `val()` returns
|
|
183
|
+
* `null`).
|
|
184
|
+
*
|
|
185
|
+
* @return `true` if this snapshot has any children; else `false`.
|
|
186
|
+
*/
|
|
187
|
+
hasChildren() {
|
|
188
|
+
const val = this.val();
|
|
189
|
+
return val !== null && typeof val === "object" && Object.keys(val).length > 0;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Returns the number of child properties of this `DataSnapshot`.
|
|
193
|
+
*
|
|
194
|
+
* @return Number of child properties of this `DataSnapshot`.
|
|
195
|
+
*/
|
|
196
|
+
numChildren() {
|
|
197
|
+
const val = this.val();
|
|
198
|
+
return val !== null && typeof val === "object" ? Object.keys(val).length : 0;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Returns a JSON-serializable representation of this object.
|
|
202
|
+
*
|
|
203
|
+
* @return A JSON-serializable representation of this object.
|
|
204
|
+
*/
|
|
205
|
+
toJSON() {
|
|
206
|
+
return this.val();
|
|
207
|
+
}
|
|
208
|
+
/** Recursive function to check if keys are numeric & convert node object to array if they are
|
|
209
|
+
*
|
|
210
|
+
* @hidden
|
|
211
|
+
*/
|
|
212
|
+
_checkAndConvertToArray(node) {
|
|
213
|
+
if (node === null || typeof node === "undefined") {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
if (typeof node !== "object") {
|
|
217
|
+
return node;
|
|
218
|
+
}
|
|
219
|
+
const obj = {};
|
|
220
|
+
let numKeys = 0;
|
|
221
|
+
let maxKey = 0;
|
|
222
|
+
let allIntegerKeys = true;
|
|
223
|
+
for (const key in node) {
|
|
224
|
+
if (!node.hasOwnProperty(key)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const childNode = node[key];
|
|
228
|
+
const v = this._checkAndConvertToArray(childNode);
|
|
229
|
+
if (v === null) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
obj[key] = v;
|
|
233
|
+
numKeys++;
|
|
234
|
+
const integerRegExp = /^(0|[1-9]\d*)$/;
|
|
235
|
+
if (allIntegerKeys && integerRegExp.test(key)) {
|
|
236
|
+
maxKey = Math.max(maxKey, Number(key));
|
|
237
|
+
} else {
|
|
238
|
+
allIntegerKeys = false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (numKeys === 0) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if (allIntegerKeys && maxKey < 2 * numKeys) {
|
|
245
|
+
const array = [];
|
|
246
|
+
for (const key of Object.keys(obj)) {
|
|
247
|
+
array[key] = obj[key];
|
|
248
|
+
}
|
|
249
|
+
return array;
|
|
250
|
+
}
|
|
251
|
+
return obj;
|
|
252
|
+
}
|
|
253
|
+
/** @hidden */
|
|
254
|
+
_dup(childPath) {
|
|
255
|
+
const dup = new DataSnapshot(this._data, undefined, this.app, this.instance);
|
|
256
|
+
[dup._path, dup._childPath] = [this._path, this._childPath];
|
|
257
|
+
if (childPath) {
|
|
258
|
+
dup._childPath = joinPath(dup._childPath, childPath);
|
|
259
|
+
}
|
|
260
|
+
return dup;
|
|
261
|
+
}
|
|
262
|
+
/** @hidden */
|
|
263
|
+
_fullPath() {
|
|
264
|
+
return (this._path || "") + "/" + (this._childPath || "");
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
export { DataSnapshot };
|