@dasasian/firebase-structured-logger 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +669 -103
- package/dist/client/logger.d.ts.map +1 -1
- package/dist/client/logger.js +16 -2
- package/dist/client/logger.js.map +1 -1
- package/dist/functions/httpHandler.d.ts +89 -0
- package/dist/functions/httpHandler.d.ts.map +1 -0
- package/dist/functions/httpHandler.js +115 -0
- package/dist/functions/httpHandler.js.map +1 -0
- package/dist/functions/index.d.ts +5 -2
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +6 -1
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/logHandler.d.ts +55 -3
- package/dist/functions/logHandler.d.ts.map +1 -1
- package/dist/functions/logHandler.js +51 -8
- package/dist/functions/logHandler.js.map +1 -1
- package/dist/functions/logger.d.ts +13 -0
- package/dist/functions/logger.d.ts.map +1 -1
- package/dist/functions/logger.js +128 -14
- package/dist/functions/logger.js.map +1 -1
- package/dist/functions/sourceMapCache.d.ts +39 -3
- package/dist/functions/sourceMapCache.d.ts.map +1 -1
- package/dist/functions/sourceMapCache.js +176 -12
- package/dist/functions/sourceMapCache.js.map +1 -1
- package/dist/functions/traceContext.d.ts +54 -0
- package/dist/functions/traceContext.d.ts.map +1 -0
- package/dist/functions/traceContext.js +141 -0
- package/dist/functions/traceContext.js.map +1 -0
- package/dist/shared/paths.d.ts +59 -0
- package/dist/shared/paths.d.ts.map +1 -0
- package/dist/shared/paths.js +126 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/tools/index.js +13 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/uploadSourceMaps.d.ts +24 -1
- package/dist/tools/uploadSourceMaps.d.ts.map +1 -1
- package/dist/tools/uploadSourceMaps.js +31 -4
- package/dist/tools/uploadSourceMaps.js.map +1 -1
- package/package.json +12 -6
- package/skills/query-logs/SKILL.md +14 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request trace ids, for backends that are not Cloud Functions (#34).
|
|
3
|
+
*
|
|
4
|
+
* `firebase-functions/logger`'s `write()` attaches
|
|
5
|
+
* `logging.googleapis.com/trace` from an AsyncLocalStorage that
|
|
6
|
+
* firebase-functions populates inside its own request wrapper. Outside one —
|
|
7
|
+
* Cloud Run, or anything behind `createHttpLogHandler` — that store is empty, so
|
|
8
|
+
* no trace id is attached and Cloud Logging cannot group a request's entries.
|
|
9
|
+
*
|
|
10
|
+
* That is silent: the logs arrive, they simply do not correlate, and nothing
|
|
11
|
+
* says why. So the adapter reads the headers itself and stores the id here.
|
|
12
|
+
*
|
|
13
|
+
* Inside Cloud Functions this is dormant — the store is never populated, and
|
|
14
|
+
* firebase-functions' own value takes precedence anyway, since it overwrites the
|
|
15
|
+
* field after we set it.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Pull a trace id out of request headers, preferring Cloud Trace's own format.
|
|
19
|
+
*
|
|
20
|
+
* Returns undefined rather than a partial id when nothing matches. A malformed
|
|
21
|
+
* header is not worth guessing at: an invalid trace resource name is rejected by
|
|
22
|
+
* Cloud Logging, which would cost the whole entry rather than just its
|
|
23
|
+
* correlation.
|
|
24
|
+
*/
|
|
25
|
+
export declare function traceIdFromHeaders(headers: Record<string, string | string[] | undefined>): string | undefined;
|
|
26
|
+
/** Run `fn` with `traceId` attached to every log written inside it. */
|
|
27
|
+
export declare function runWithTrace<T>(traceId: string | undefined, fn: () => T): T;
|
|
28
|
+
/** The current request's trace id, if one was parsed. */
|
|
29
|
+
export declare function currentTraceId(): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Find the project on a Google runtime that does not put it in the environment.
|
|
32
|
+
*
|
|
33
|
+
* Why the resource name at all: Cloud Logging's LogEntry reference prefers the bare
|
|
34
|
+
* trace id, but stores whichever form it is given, and the console's "show entries
|
|
35
|
+
* for this trace" filters on the full `projects/<id>/traces/<trace>` — the form Cloud
|
|
36
|
+
* Run writes its own request log in. A bare id never meets that request log; the
|
|
37
|
+
* smoke run proved it. So the project has to be found.
|
|
38
|
+
*
|
|
39
|
+
* Asked of the metadata server, the way Google's own Cloud Run sample and its pino
|
|
40
|
+
* config do: once, cached, and only on Cloud Run (`K_SERVICE` is set) — anywhere
|
|
41
|
+
* else the lookup would only wait for a timeout. Async, so it is awaited where the
|
|
42
|
+
* request starts (createHttpLogHandler), not on the synchronous log path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveTraceProject(): Promise<void>;
|
|
45
|
+
/** Forget the looked-up project. Tests only. */
|
|
46
|
+
export declare function resetTraceProject(): void;
|
|
47
|
+
/**
|
|
48
|
+
* The `logging.googleapis.com/trace` value: the full resource name when the project
|
|
49
|
+
* is known, and the bare id when it is not. The bare id is a valid LogEntry.trace —
|
|
50
|
+
* the entry still carries its trace — it only fails to meet the request log in the
|
|
51
|
+
* console, so it beats writing nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare function traceField(): string | undefined;
|
|
54
|
+
//# sourceMappingURL=traceContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceContext.d.ts","sourceRoot":"","sources":["../../src/functions/traceContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0BH;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,GAAG,SAAS,CAepB;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE3E;AAED,yDAAyD;AACzD,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBnD;AAED,gDAAgD;AAChD,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAK/C"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Request trace ids, for backends that are not Cloud Functions (#34).
|
|
4
|
+
*
|
|
5
|
+
* `firebase-functions/logger`'s `write()` attaches
|
|
6
|
+
* `logging.googleapis.com/trace` from an AsyncLocalStorage that
|
|
7
|
+
* firebase-functions populates inside its own request wrapper. Outside one —
|
|
8
|
+
* Cloud Run, or anything behind `createHttpLogHandler` — that store is empty, so
|
|
9
|
+
* no trace id is attached and Cloud Logging cannot group a request's entries.
|
|
10
|
+
*
|
|
11
|
+
* That is silent: the logs arrive, they simply do not correlate, and nothing
|
|
12
|
+
* says why. So the adapter reads the headers itself and stores the id here.
|
|
13
|
+
*
|
|
14
|
+
* Inside Cloud Functions this is dormant — the store is never populated, and
|
|
15
|
+
* firebase-functions' own value takes precedence anyway, since it overwrites the
|
|
16
|
+
* field after we set it.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.traceIdFromHeaders = traceIdFromHeaders;
|
|
20
|
+
exports.runWithTrace = runWithTrace;
|
|
21
|
+
exports.currentTraceId = currentTraceId;
|
|
22
|
+
exports.resolveTraceProject = resolveTraceProject;
|
|
23
|
+
exports.resetTraceProject = resetTraceProject;
|
|
24
|
+
exports.traceField = traceField;
|
|
25
|
+
const async_hooks_1 = require("async_hooks");
|
|
26
|
+
const traceStore = new async_hooks_1.AsyncLocalStorage();
|
|
27
|
+
/**
|
|
28
|
+
* Cloud Trace's own header: `TRACE_ID/SPAN_ID;o=TRACE_TRUE`.
|
|
29
|
+
* Google's load balancers and Cloud Run set this.
|
|
30
|
+
*/
|
|
31
|
+
const CLOUD_TRACE_HEADER = 'x-cloud-trace-context';
|
|
32
|
+
/**
|
|
33
|
+
* W3C Trace Context: `00-<32 hex trace>-<16 hex span>-<flags>`.
|
|
34
|
+
* What OpenTelemetry and most third-party tracers send.
|
|
35
|
+
*/
|
|
36
|
+
const TRACEPARENT_HEADER = 'traceparent';
|
|
37
|
+
const CLOUD_TRACE = /^([0-9a-fA-F]{32})(?:\/(\d+))?(?:;o=[01])?$/;
|
|
38
|
+
const TRACEPARENT = /^[0-9a-fA-F]{2}-([0-9a-fA-F]{32})-[0-9a-fA-F]{16}-[0-9a-fA-F]{2}$/;
|
|
39
|
+
function firstValue(value) {
|
|
40
|
+
if (Array.isArray(value))
|
|
41
|
+
return value[0];
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Pull a trace id out of request headers, preferring Cloud Trace's own format.
|
|
46
|
+
*
|
|
47
|
+
* Returns undefined rather than a partial id when nothing matches. A malformed
|
|
48
|
+
* header is not worth guessing at: an invalid trace resource name is rejected by
|
|
49
|
+
* Cloud Logging, which would cost the whole entry rather than just its
|
|
50
|
+
* correlation.
|
|
51
|
+
*/
|
|
52
|
+
function traceIdFromHeaders(headers) {
|
|
53
|
+
// Header names are case-insensitive, and nothing guarantees the host framework
|
|
54
|
+
// lower-cased them — Node's http does, a hand-rolled object may not.
|
|
55
|
+
const lower = {};
|
|
56
|
+
for (const [k, v] of Object.entries(headers))
|
|
57
|
+
lower[k.toLowerCase()] = v;
|
|
58
|
+
const cloud = firstValue(lower[CLOUD_TRACE_HEADER]);
|
|
59
|
+
const cloudMatch = cloud && CLOUD_TRACE.exec(cloud.trim());
|
|
60
|
+
if (cloudMatch)
|
|
61
|
+
return cloudMatch[1];
|
|
62
|
+
const parent = firstValue(lower[TRACEPARENT_HEADER]);
|
|
63
|
+
const parentMatch = parent && TRACEPARENT.exec(parent.trim());
|
|
64
|
+
if (parentMatch)
|
|
65
|
+
return parentMatch[1];
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
/** Run `fn` with `traceId` attached to every log written inside it. */
|
|
69
|
+
function runWithTrace(traceId, fn) {
|
|
70
|
+
return traceId ? traceStore.run(traceId, fn) : fn();
|
|
71
|
+
}
|
|
72
|
+
/** The current request's trace id, if one was parsed. */
|
|
73
|
+
function currentTraceId() {
|
|
74
|
+
return traceStore.getStore();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The project a trace resource name belongs to, from the environment.
|
|
78
|
+
*
|
|
79
|
+
* Cloud Functions sets `GCLOUD_PROJECT`. Cloud Run sets NEITHER variable — 0.7.0
|
|
80
|
+
* assumed it set `GOOGLE_CLOUD_PROJECT`, so on the backend createHttpLogHandler
|
|
81
|
+
* exists for, the trace was silently never written (#39). See resolveTraceProject.
|
|
82
|
+
*/
|
|
83
|
+
function envProject() {
|
|
84
|
+
return process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT || undefined;
|
|
85
|
+
}
|
|
86
|
+
const METADATA_PROJECT_URL = 'http://metadata.google.internal/computeMetadata/v1/project/project-id';
|
|
87
|
+
// undefined = not looked up; null = looked up and not found.
|
|
88
|
+
let metadataProject;
|
|
89
|
+
let metadataLookup;
|
|
90
|
+
/**
|
|
91
|
+
* Find the project on a Google runtime that does not put it in the environment.
|
|
92
|
+
*
|
|
93
|
+
* Why the resource name at all: Cloud Logging's LogEntry reference prefers the bare
|
|
94
|
+
* trace id, but stores whichever form it is given, and the console's "show entries
|
|
95
|
+
* for this trace" filters on the full `projects/<id>/traces/<trace>` — the form Cloud
|
|
96
|
+
* Run writes its own request log in. A bare id never meets that request log; the
|
|
97
|
+
* smoke run proved it. So the project has to be found.
|
|
98
|
+
*
|
|
99
|
+
* Asked of the metadata server, the way Google's own Cloud Run sample and its pino
|
|
100
|
+
* config do: once, cached, and only on Cloud Run (`K_SERVICE` is set) — anywhere
|
|
101
|
+
* else the lookup would only wait for a timeout. Async, so it is awaited where the
|
|
102
|
+
* request starts (createHttpLogHandler), not on the synchronous log path.
|
|
103
|
+
*/
|
|
104
|
+
function resolveTraceProject() {
|
|
105
|
+
if (envProject() || metadataProject !== undefined)
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
if (!process.env.K_SERVICE) {
|
|
108
|
+
metadataProject = null;
|
|
109
|
+
return Promise.resolve();
|
|
110
|
+
}
|
|
111
|
+
metadataLookup ??= fetch(METADATA_PROJECT_URL, {
|
|
112
|
+
headers: { 'Metadata-Flavor': 'Google' },
|
|
113
|
+
signal: AbortSignal.timeout(1_000),
|
|
114
|
+
})
|
|
115
|
+
.then(async (res) => {
|
|
116
|
+
metadataProject = res.ok ? (await res.text()).trim() || null : null;
|
|
117
|
+
})
|
|
118
|
+
.catch(() => {
|
|
119
|
+
metadataProject = null;
|
|
120
|
+
});
|
|
121
|
+
return metadataLookup;
|
|
122
|
+
}
|
|
123
|
+
/** Forget the looked-up project. Tests only. */
|
|
124
|
+
function resetTraceProject() {
|
|
125
|
+
metadataProject = undefined;
|
|
126
|
+
metadataLookup = undefined;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The `logging.googleapis.com/trace` value: the full resource name when the project
|
|
130
|
+
* is known, and the bare id when it is not. The bare id is a valid LogEntry.trace —
|
|
131
|
+
* the entry still carries its trace — it only fails to meet the request log in the
|
|
132
|
+
* console, so it beats writing nothing.
|
|
133
|
+
*/
|
|
134
|
+
function traceField() {
|
|
135
|
+
const traceId = currentTraceId();
|
|
136
|
+
if (!traceId)
|
|
137
|
+
return undefined;
|
|
138
|
+
const project = envProject() ?? metadataProject ?? undefined;
|
|
139
|
+
return project ? `projects/${project}/traces/${traceId}` : traceId;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=traceContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceContext.js","sourceRoot":"","sources":["../../src/functions/traceContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every storage location this package reads or writes, defined once.
|
|
3
|
+
*
|
|
4
|
+
* These are contracts, not conveniences: each source-map path has a writer in
|
|
5
|
+
* `/tools` and a reader in `/functions`, and they used to be independent string
|
|
6
|
+
* literals in different entry points with nothing enforcing that they matched.
|
|
7
|
+
* When they disagree, symbolication does not fail — it falls through and returns
|
|
8
|
+
* the minified frames, which looks like "my maps never uploaded".
|
|
9
|
+
*
|
|
10
|
+
* #36 is what that costs: `.release` was written in three places, none of them
|
|
11
|
+
* the writer, so the release check never ran and every stack resolved against
|
|
12
|
+
* whatever happened to be embedded.
|
|
13
|
+
*
|
|
14
|
+
* `/functions` cannot import from `/tools` without a cross-entry-point
|
|
15
|
+
* dependency, which is why this lives in `shared/` beside `error.ts` and
|
|
16
|
+
* `severity.ts` rather than being exported from either side.
|
|
17
|
+
*
|
|
18
|
+
* Cloud Storage object names always use `/`; local paths are built with
|
|
19
|
+
* `path.join` by the caller. The two are kept apart on purpose — a Windows
|
|
20
|
+
* separator in a GCS object name is a different object.
|
|
21
|
+
*/
|
|
22
|
+
/** Cloud Storage prefix holding one directory per release. */
|
|
23
|
+
export declare const SOURCEMAP_PREFIX = "sourcemaps";
|
|
24
|
+
/** Cloud Storage prefix holding one directory per log entry. */
|
|
25
|
+
export declare const ATTACHMENT_PREFIX = "logAttachments";
|
|
26
|
+
/**
|
|
27
|
+
* Directory inside the deployed backend holding the current release's maps,
|
|
28
|
+
* relative to the process working directory. Checked before Cloud Storage so
|
|
29
|
+
* the common case costs no network call.
|
|
30
|
+
*/
|
|
31
|
+
export declare const EMBEDDED_DIR: string;
|
|
32
|
+
/**
|
|
33
|
+
* Names the release the embedded maps belong to.
|
|
34
|
+
*
|
|
35
|
+
* Without it the runtime cannot tell "this stack is from the deployed release"
|
|
36
|
+
* from "this stack is from an older one" — the embedded directory is keyed only
|
|
37
|
+
* by filename, so an old stack naming a bundle that still exists would be
|
|
38
|
+
* resolved with the current map, giving confidently wrong line numbers.
|
|
39
|
+
*/
|
|
40
|
+
export declare const RELEASE_MARKER = ".release";
|
|
41
|
+
/**
|
|
42
|
+
* Cloud Storage object name for one release's map of one bundle.
|
|
43
|
+
*
|
|
44
|
+
* `bundleFileName` is the bundle as it appears in a stack trace — `app-4f2a.js`,
|
|
45
|
+
* without the `.map`. The suffix belongs to the contract, not to either caller,
|
|
46
|
+
* which is what kept the writer and reader agreeing by accident before.
|
|
47
|
+
*/
|
|
48
|
+
export declare function storageMapPath(releaseId: string, bundleFileName: string, prefix?: string): string;
|
|
49
|
+
/** Cloud Storage object name for one attachment of one log entry. */
|
|
50
|
+
export declare function attachmentPath(logId: string, name: string, prefix?: string): string;
|
|
51
|
+
/** Local directory holding the embedded maps, under `baseDir`. */
|
|
52
|
+
export declare function embeddedDir(baseDir: string): string;
|
|
53
|
+
/** Local path of one embedded map, under `baseDir`. */
|
|
54
|
+
export declare function embeddedMapPath(baseDir: string, bundleFileName: string): string;
|
|
55
|
+
/** Local path of the release marker, under `baseDir`. */
|
|
56
|
+
export declare function embeddedMarkerPath(baseDir: string): string;
|
|
57
|
+
/** Strip the `.map` suffix from a map filename to get the bundle name. */
|
|
58
|
+
export declare function bundleNameOf(mapFileName: string): string;
|
|
59
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/shared/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,eAAe,CAAA;AAE5C,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,mBAAmB,CAAA;AAEjD;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAyC,CAAA;AAElE;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,MAAM,GAAE,MAAyB,GAChC,MAAM,CAER;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAA0B,GACjC,MAAM,CAER;AAeD,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,yDAAyD;AACzD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Every storage location this package reads or writes, defined once.
|
|
4
|
+
*
|
|
5
|
+
* These are contracts, not conveniences: each source-map path has a writer in
|
|
6
|
+
* `/tools` and a reader in `/functions`, and they used to be independent string
|
|
7
|
+
* literals in different entry points with nothing enforcing that they matched.
|
|
8
|
+
* When they disagree, symbolication does not fail — it falls through and returns
|
|
9
|
+
* the minified frames, which looks like "my maps never uploaded".
|
|
10
|
+
*
|
|
11
|
+
* #36 is what that costs: `.release` was written in three places, none of them
|
|
12
|
+
* the writer, so the release check never ran and every stack resolved against
|
|
13
|
+
* whatever happened to be embedded.
|
|
14
|
+
*
|
|
15
|
+
* `/functions` cannot import from `/tools` without a cross-entry-point
|
|
16
|
+
* dependency, which is why this lives in `shared/` beside `error.ts` and
|
|
17
|
+
* `severity.ts` rather than being exported from either side.
|
|
18
|
+
*
|
|
19
|
+
* Cloud Storage object names always use `/`; local paths are built with
|
|
20
|
+
* `path.join` by the caller. The two are kept apart on purpose — a Windows
|
|
21
|
+
* separator in a GCS object name is a different object.
|
|
22
|
+
*/
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.RELEASE_MARKER = exports.EMBEDDED_DIR = exports.ATTACHMENT_PREFIX = exports.SOURCEMAP_PREFIX = void 0;
|
|
58
|
+
exports.storageMapPath = storageMapPath;
|
|
59
|
+
exports.attachmentPath = attachmentPath;
|
|
60
|
+
exports.embeddedDir = embeddedDir;
|
|
61
|
+
exports.embeddedMapPath = embeddedMapPath;
|
|
62
|
+
exports.embeddedMarkerPath = embeddedMarkerPath;
|
|
63
|
+
exports.bundleNameOf = bundleNameOf;
|
|
64
|
+
const path = __importStar(require("path"));
|
|
65
|
+
/** Cloud Storage prefix holding one directory per release. */
|
|
66
|
+
exports.SOURCEMAP_PREFIX = 'sourcemaps';
|
|
67
|
+
/** Cloud Storage prefix holding one directory per log entry. */
|
|
68
|
+
exports.ATTACHMENT_PREFIX = 'logAttachments';
|
|
69
|
+
/**
|
|
70
|
+
* Directory inside the deployed backend holding the current release's maps,
|
|
71
|
+
* relative to the process working directory. Checked before Cloud Storage so
|
|
72
|
+
* the common case costs no network call.
|
|
73
|
+
*/
|
|
74
|
+
exports.EMBEDDED_DIR = path.join(exports.SOURCEMAP_PREFIX, 'current');
|
|
75
|
+
/**
|
|
76
|
+
* Names the release the embedded maps belong to.
|
|
77
|
+
*
|
|
78
|
+
* Without it the runtime cannot tell "this stack is from the deployed release"
|
|
79
|
+
* from "this stack is from an older one" — the embedded directory is keyed only
|
|
80
|
+
* by filename, so an old stack naming a bundle that still exists would be
|
|
81
|
+
* resolved with the current map, giving confidently wrong line numbers.
|
|
82
|
+
*/
|
|
83
|
+
exports.RELEASE_MARKER = '.release';
|
|
84
|
+
/**
|
|
85
|
+
* Cloud Storage object name for one release's map of one bundle.
|
|
86
|
+
*
|
|
87
|
+
* `bundleFileName` is the bundle as it appears in a stack trace — `app-4f2a.js`,
|
|
88
|
+
* without the `.map`. The suffix belongs to the contract, not to either caller,
|
|
89
|
+
* which is what kept the writer and reader agreeing by accident before.
|
|
90
|
+
*/
|
|
91
|
+
function storageMapPath(releaseId, bundleFileName, prefix = exports.SOURCEMAP_PREFIX) {
|
|
92
|
+
return `${trimPrefix(prefix)}/${releaseId}/${bundleFileName}.map`;
|
|
93
|
+
}
|
|
94
|
+
/** Cloud Storage object name for one attachment of one log entry. */
|
|
95
|
+
function attachmentPath(logId, name, prefix = exports.ATTACHMENT_PREFIX) {
|
|
96
|
+
return `${trimPrefix(prefix)}/${logId}/${name}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Normalise a caller-supplied prefix.
|
|
100
|
+
*
|
|
101
|
+
* A prefix is a directory, and people write directories with a trailing slash.
|
|
102
|
+
* Passing `'fsl/'` unnormalised yields `fsl//abc/app.js.map`, which is a
|
|
103
|
+
* *different* Storage object from `fsl/abc/app.js.map` — GCS keys are opaque
|
|
104
|
+
* strings, so the double slash is not collapsed and the reader would miss.
|
|
105
|
+
* Leading slashes are dropped for the same reason.
|
|
106
|
+
*/
|
|
107
|
+
function trimPrefix(prefix) {
|
|
108
|
+
return prefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
109
|
+
}
|
|
110
|
+
/** Local directory holding the embedded maps, under `baseDir`. */
|
|
111
|
+
function embeddedDir(baseDir) {
|
|
112
|
+
return path.join(baseDir, exports.EMBEDDED_DIR);
|
|
113
|
+
}
|
|
114
|
+
/** Local path of one embedded map, under `baseDir`. */
|
|
115
|
+
function embeddedMapPath(baseDir, bundleFileName) {
|
|
116
|
+
return path.join(embeddedDir(baseDir), `${bundleFileName}.map`);
|
|
117
|
+
}
|
|
118
|
+
/** Local path of the release marker, under `baseDir`. */
|
|
119
|
+
function embeddedMarkerPath(baseDir) {
|
|
120
|
+
return path.join(embeddedDir(baseDir), exports.RELEASE_MARKER);
|
|
121
|
+
}
|
|
122
|
+
/** Strip the `.map` suffix from a map filename to get the bundle name. */
|
|
123
|
+
function bundleNameOf(mapFileName) {
|
|
124
|
+
return mapFileName.replace(/\.map$/, '');
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/shared/paths.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCH,wCAMC;AAGD,wCAMC;AAgBD,kCAEC;AAGD,0CAEC;AAGD,gDAEC;AAGD,oCAEC;AAhFD,2CAA4B;AAE5B,8DAA8D;AACjD,QAAA,gBAAgB,GAAG,YAAY,CAAA;AAE5C,gEAAgE;AACnD,QAAA,iBAAiB,GAAG,gBAAgB,CAAA;AAEjD;;;;GAIG;AACU,QAAA,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,wBAAgB,EAAE,SAAS,CAAC,CAAA;AAElE;;;;;;;GAOG;AACU,QAAA,cAAc,GAAG,UAAU,CAAA;AAExC;;;;;;GAMG;AACH,SAAgB,cAAc,CAC5B,SAAiB,EACjB,cAAsB,EACtB,SAAiB,wBAAgB;IAEjC,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,SAAS,IAAI,cAAc,MAAM,CAAA;AACnE,CAAC;AAED,qEAAqE;AACrE,SAAgB,cAAc,CAC5B,KAAa,EACb,IAAY,EACZ,SAAiB,yBAAiB;IAElC,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAA;AACjD,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,MAAc;IAChC,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AACvD,CAAC;AAED,kEAAkE;AAClE,SAAgB,WAAW,CAAC,OAAe;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,oBAAY,CAAC,CAAA;AACzC,CAAC;AAED,uDAAuD;AACvD,SAAgB,eAAe,CAAC,OAAe,EAAE,cAAsB;IACrE,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,GAAG,cAAc,MAAM,CAAC,CAAA;AACjE,CAAC;AAED,yDAAyD;AACzD,SAAgB,kBAAkB,CAAC,OAAe;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,sBAAc,CAAC,CAAA;AACxD,CAAC;AAED,0EAA0E;AAC1E,SAAgB,YAAY,CAAC,WAAmB;IAC9C,OAAO,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;AAC1C,CAAC"}
|
package/dist/tools/index.js
CHANGED
|
@@ -79,9 +79,13 @@ async function main() {
|
|
|
79
79
|
switch (command) {
|
|
80
80
|
case 'upload-sourcemaps': {
|
|
81
81
|
const bucket = args.bucket ?? process.env.VITE_FIREBASE_STORAGE_BUCKET ?? process.env.FIREBASE_STORAGE_BUCKET;
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const embed = args['embed-sourcemaps'] === 'true';
|
|
83
|
+
// A bucket is only required when something is going to be uploaded.
|
|
84
|
+
// Embed-only is the whole flow for a backend that has no bucket — see #34.
|
|
85
|
+
if (!bucket && !embed) {
|
|
86
|
+
console.error('Usage: fsl upload-sourcemaps [--bucket=<name>] [--functions=<path>] [--embed-sourcemaps] [--release=<id>] [--dist=<path>] [--prefix=<path>]');
|
|
84
87
|
console.error('Bucket can also be set via VITE_FIREBASE_STORAGE_BUCKET or FIREBASE_STORAGE_BUCKET env var (loaded from .env.local automatically).');
|
|
88
|
+
console.error('Omit the bucket only with --embed-sourcemaps, to embed the current release without uploading.');
|
|
85
89
|
process.exit(1);
|
|
86
90
|
}
|
|
87
91
|
const result = await (0, uploadSourceMaps_1.uploadSourceMaps)({
|
|
@@ -89,7 +93,8 @@ async function main() {
|
|
|
89
93
|
release: args.release,
|
|
90
94
|
distDir: args.dist,
|
|
91
95
|
functionsDir: args.functions,
|
|
92
|
-
embedSourcemaps:
|
|
96
|
+
embedSourcemaps: embed,
|
|
97
|
+
prefix: args.prefix,
|
|
93
98
|
});
|
|
94
99
|
// Distinct code so a deploy chain can choose to continue:
|
|
95
100
|
// npx fsl upload-sourcemaps … || [ $? -eq 3 ]
|
|
@@ -106,11 +111,15 @@ async function main() {
|
|
|
106
111
|
firebase-structured-logger (fsl)
|
|
107
112
|
|
|
108
113
|
Commands:
|
|
109
|
-
fsl upload-sourcemaps [--bucket=<name>] [--functions=<path>] [--embed-sourcemaps] [--release=<id>] [--dist=<path>]
|
|
114
|
+
fsl upload-sourcemaps [--bucket=<name>] [--functions=<path>] [--embed-sourcemaps] [--release=<id>] [--dist=<path>] [--prefix=<path>]
|
|
110
115
|
Upload .map files from dist/ to Cloud Storage and delete them locally.
|
|
111
116
|
--bucket defaults to VITE_FIREBASE_STORAGE_BUCKET or FIREBASE_STORAGE_BUCKET (loaded from .env.local automatically).
|
|
112
117
|
--functions path to Cloud Functions directory (e.g. ./functions or ./backend).
|
|
113
118
|
--embed-sourcemaps copies maps to {functions}/sourcemaps/current/ for fast lookup of current release.
|
|
119
|
+
Given without --bucket, embeds only and uploads nothing — for a backend with no
|
|
120
|
+
bucket. Only the deployed release can then be symbolicated.
|
|
121
|
+
--prefix Cloud Storage prefix to upload under (default sourcemaps/). Must match
|
|
122
|
+
createClientLogHandler({ sourceMaps: { prefix } }) or maps are not found.
|
|
114
123
|
Authenticates via FIREBASE_SERVICE_ACCOUNT_PATH if set, otherwise uses ADC.
|
|
115
124
|
Release ID defaults to git rev-parse --short HEAD.
|
|
116
125
|
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,uCAAwB;AACxB,2CAA4B;AAC5B,yDAAsF;AACtF,mDAA+C;AAE/C,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAA;AAE9C,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;QAC3C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAC7B,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAA;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAM;IACpC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QACjD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAQ;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACvC,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACxC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC5B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACjD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;IAE/B,2CAA2C;IAC3C,WAAW,CAAC,YAAY,CAAC,CAAA;IAEzB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAA;YAC7G,IAAI,CAAC,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,11 +17,34 @@ export declare const EXIT_UPLOAD_FAILED_BUT_EMBEDDED = 3;
|
|
|
17
17
|
*/
|
|
18
18
|
export declare const EMBEDDED_RELEASE_MARKER = ".release";
|
|
19
19
|
export interface UploadOptions {
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Cloud Storage bucket to upload to. Resolved by the caller — the CLI falls
|
|
22
|
+
* back to env vars.
|
|
23
|
+
*
|
|
24
|
+
* Optional, but only alongside `embedSourcemaps`: a run that neither uploads
|
|
25
|
+
* nor embeds does nothing except delete maps, which is not a thing to let
|
|
26
|
+
* someone ask for by accident. Omitting it is how a backend with no bucket —
|
|
27
|
+
* Cloud Run, say — gets the embedded copy without inventing a bucket name to
|
|
28
|
+
* satisfy the tool (#34).
|
|
29
|
+
*/
|
|
30
|
+
bucket?: string;
|
|
21
31
|
release?: string;
|
|
22
32
|
distDir?: string;
|
|
23
33
|
functionsDir?: string;
|
|
24
34
|
embedSourcemaps?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Cloud Storage prefix to upload under. Defaults to `sourcemaps/`.
|
|
37
|
+
*
|
|
38
|
+
* The reader must be told the same value, via
|
|
39
|
+
* `createClientLogHandler({ sourceMaps: { prefix } })`. These are the two ends
|
|
40
|
+
* of one contract and nothing checks them against each other — when they
|
|
41
|
+
* disagree the maps are simply not found and stacks stay minified, which
|
|
42
|
+
* looks identical to never having uploaded them (#35).
|
|
43
|
+
*
|
|
44
|
+
* Does not affect the embedded copy: that directory ships inside the deploy
|
|
45
|
+
* artifact and nothing outside reads it, so it stays fixed.
|
|
46
|
+
*/
|
|
47
|
+
prefix?: string;
|
|
25
48
|
}
|
|
26
49
|
/**
|
|
27
50
|
* Drop `sourcesContent` from a source map, returning the JSON to store.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadSourceMaps.d.ts","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"uploadSourceMaps.d.ts","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAA;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,aAAiB,CAAA;AAErD,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CASpF;AA8BD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAkH7F"}
|
|
@@ -39,6 +39,7 @@ exports.uploadSourceMaps = uploadSourceMaps;
|
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const storage_1 = require("@google-cloud/storage");
|
|
42
|
+
const paths_js_1 = require("../shared/paths.js");
|
|
42
43
|
/**
|
|
43
44
|
* Exit code used when maps were embedded but the Storage upload failed.
|
|
44
45
|
*
|
|
@@ -56,7 +57,7 @@ exports.EXIT_UPLOAD_FAILED_BUT_EMBEDDED = 3;
|
|
|
56
57
|
* by filename, so an old stack naming a bundle that still exists would be
|
|
57
58
|
* resolved with the current map, giving confidently wrong line numbers.
|
|
58
59
|
*/
|
|
59
|
-
exports.EMBEDDED_RELEASE_MARKER =
|
|
60
|
+
exports.EMBEDDED_RELEASE_MARKER = paths_js_1.RELEASE_MARKER;
|
|
60
61
|
/**
|
|
61
62
|
* Drop `sourcesContent` from a source map, returning the JSON to store.
|
|
62
63
|
*
|
|
@@ -118,14 +119,19 @@ async function uploadSourceMaps(options) {
|
|
|
118
119
|
}
|
|
119
120
|
const distDir = options.distDir ?? path.join(process.cwd(), 'dist');
|
|
120
121
|
const mapFiles = findMapFiles(distDir);
|
|
122
|
+
if (!options.bucket && !options.embedSourcemaps) {
|
|
123
|
+
throw new Error('[fsl] Nothing to do: no --bucket to upload to and no --embed-sourcemaps. ' +
|
|
124
|
+
'Running would only delete the maps from dist/.');
|
|
125
|
+
}
|
|
121
126
|
if (mapFiles.length === 0) {
|
|
122
127
|
console.log('[fsl] No .map files found in', distDir);
|
|
123
128
|
return { uploaded: true };
|
|
124
129
|
}
|
|
125
|
-
|
|
130
|
+
const verb = options.bucket ? 'Uploading' : 'Embedding';
|
|
131
|
+
console.log(`[fsl] ${verb} ${mapFiles.length} source map(s) for release ${releaseId}...`);
|
|
126
132
|
// Embed maps into functions directory before uploading (for fast lookup of current release)
|
|
127
133
|
if (options.embedSourcemaps && options.functionsDir) {
|
|
128
|
-
const embedDir = path.join(process.cwd(), options.functionsDir
|
|
134
|
+
const embedDir = (0, paths_js_1.embeddedDir)(path.join(process.cwd(), options.functionsDir));
|
|
129
135
|
fs.mkdirSync(embedDir, { recursive: true });
|
|
130
136
|
for (const f of fs.readdirSync(embedDir)) {
|
|
131
137
|
fs.unlinkSync(path.join(embedDir, f));
|
|
@@ -139,6 +145,27 @@ async function uploadSourceMaps(options) {
|
|
|
139
145
|
const saved = before > after ? ` (${kb(before)} → ${kb(after)})` : '';
|
|
140
146
|
console.log(` ✓ embedded ${path.basename(localPath)} → ${options.functionsDir}/sourcemaps/current/${saved}`);
|
|
141
147
|
}
|
|
148
|
+
// Record which release these maps are for. Without it the runtime cannot tell
|
|
149
|
+
// a stack from the deployed release from a stack from an older one, and
|
|
150
|
+
// resolves both with whatever is embedded — see the marker's own doc above.
|
|
151
|
+
// Written after the loop, which has already cleared any stale marker.
|
|
152
|
+
fs.writeFileSync(path.join(embedDir, paths_js_1.RELEASE_MARKER), releaseId);
|
|
153
|
+
console.log(` ✓ marked ${options.functionsDir}/sourcemaps/current/ as release ${releaseId}`);
|
|
154
|
+
}
|
|
155
|
+
// Embed-only: nothing to authenticate against, so do not construct a client.
|
|
156
|
+
// The maps still leave dist/ — they are embedded, and leaving them behind
|
|
157
|
+
// would serve source maps to browsers, which is the worse outcome.
|
|
158
|
+
if (!options.bucket) {
|
|
159
|
+
for (const localPath of mapFiles) {
|
|
160
|
+
if (fs.existsSync(localPath)) {
|
|
161
|
+
fs.unlinkSync(localPath);
|
|
162
|
+
console.log(` ✗ deleted ${path.relative(process.cwd(), localPath)}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
console.log('[fsl] Embedded only — no bucket given, nothing uploaded.');
|
|
166
|
+
console.log(`[fsl] Stacks from ${releaseId} symbolicate while it is the deployed release.`);
|
|
167
|
+
console.log('[fsl] Older releases cannot be symbolicated without a bucket to read from.');
|
|
168
|
+
return { uploaded: true };
|
|
142
169
|
}
|
|
143
170
|
// Authenticate via service account key if available, otherwise fall back to ADC
|
|
144
171
|
const serviceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;
|
|
@@ -150,7 +177,7 @@ async function uploadSourceMaps(options) {
|
|
|
150
177
|
try {
|
|
151
178
|
for (const localPath of mapFiles) {
|
|
152
179
|
const fileName = path.basename(localPath);
|
|
153
|
-
const destination =
|
|
180
|
+
const destination = (0, paths_js_1.storageMapPath)(releaseId, (0, paths_js_1.bundleNameOf)(fileName), options.prefix);
|
|
154
181
|
const { json, before, after } = readStrippedMap(localPath);
|
|
155
182
|
await bucket.file(destination).save(json, { contentType: 'application/json' });
|
|
156
183
|
const saved = before > after ? ` (${kb(before)} → ${kb(after)}, -${Math.round(100 - (after / before) * 100)}%)` : '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadSourceMaps.js","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"uploadSourceMaps.js","sourceRoot":"","sources":["../../src/tools/uploadSourceMaps.ts"],"names":[],"mappings":"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"}
|