@dasasian/firebase-structured-logger 0.6.0 → 0.7.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 +598 -102
- package/dist/functions/httpHandler.d.ts +89 -0
- package/dist/functions/httpHandler.d.ts.map +1 -0
- package/dist/functions/httpHandler.js +112 -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 +53 -2
- package/dist/functions/logHandler.d.ts.map +1 -1
- package/dist/functions/logHandler.js +38 -7
- package/dist/functions/logHandler.js.map +1 -1
- package/dist/functions/logger.d.ts.map +1 -1
- package/dist/functions/logger.js +54 -3
- package/dist/functions/logger.js.map +1 -1
- package/dist/functions/sourceMapCache.d.ts +27 -1
- package/dist/functions/sourceMapCache.d.ts.map +1 -1
- package/dist/functions/sourceMapCache.js +84 -9
- package/dist/functions/sourceMapCache.js.map +1 -1
- package/dist/functions/traceContext.d.ts +40 -0
- package/dist/functions/traceContext.d.ts.map +1 -0
- package/dist/functions/traceContext.js +93 -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 +3 -3
- package/skills/query-logs/SKILL.md +14 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receive client logs over plain HTTP, for a backend that is not Cloud Functions
|
|
3
|
+
* (#34).
|
|
4
|
+
*
|
|
5
|
+
* The client half was already portable — `logFunction` is any
|
|
6
|
+
* `(payload) => Promise<unknown>`, and `httpsCallable()` merely happens to fit.
|
|
7
|
+
* What was missing was a receiving end that does not require a callable.
|
|
8
|
+
*
|
|
9
|
+
* This is deliberately framework-agnostic. It reads `method`, `headers` and a
|
|
10
|
+
* parsed `body`, and writes through `statusCode` / `setHeader` / `end` — the
|
|
11
|
+
* Node core shapes, which Express, Fastify's compat layer and Cloud Run's raw
|
|
12
|
+
* server all satisfy. Parsing the body is the caller's job (`express.json()` or
|
|
13
|
+
* equivalent); reading the stream here would mean guessing at limits and
|
|
14
|
+
* encodings that the host framework already has opinions about.
|
|
15
|
+
*/
|
|
16
|
+
import { type ClientLogHandlerConfig } from './logHandler';
|
|
17
|
+
/** The parts of a request this handler reads. */
|
|
18
|
+
export interface HttpLogRequest {
|
|
19
|
+
method?: string;
|
|
20
|
+
headers: Record<string, string | string[] | undefined>;
|
|
21
|
+
body?: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** The parts of a response this handler writes. */
|
|
24
|
+
export interface HttpLogResponse {
|
|
25
|
+
statusCode: number;
|
|
26
|
+
setHeader(name: string, value: string): unknown;
|
|
27
|
+
end(body?: string): unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface HttpLogHandlerConfig extends ClientLogHandlerConfig {
|
|
30
|
+
/**
|
|
31
|
+
* Who is allowed to POST a log.
|
|
32
|
+
*
|
|
33
|
+
* Required, and with no default, because the honest default does not exist. A
|
|
34
|
+
* callable gets Firebase's own token check for free; an HTTP endpoint gets
|
|
35
|
+
* nothing, and an open one writes to the customer's Cloud Logging bill on
|
|
36
|
+
* anyone's say-so.
|
|
37
|
+
*
|
|
38
|
+
* This is a **gate, not an identity check**. The handler never reads
|
|
39
|
+
* `request.auth` — `userId` arrives self-reported in the client's own labels
|
|
40
|
+
* either way. Its job is to keep strangers out, not to establish who is
|
|
41
|
+
* speaking.
|
|
42
|
+
*
|
|
43
|
+
* Verify a Firebase ID token:
|
|
44
|
+
*
|
|
45
|
+
* import { getAuth } from 'firebase-admin/auth'
|
|
46
|
+
* authorize: async (req) => {
|
|
47
|
+
* const header = String(req.headers.authorization ?? '')
|
|
48
|
+
* if (!header.startsWith('Bearer ')) return false
|
|
49
|
+
* try { await getAuth().verifyIdToken(header.slice(7)); return true }
|
|
50
|
+
* catch { return false }
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* Pass the literal `'unauthenticated'` to open the endpoint deliberately —
|
|
54
|
+
* behind a VPC, an API gateway, or IAM that already did the work. It is spelled
|
|
55
|
+
* out at the call site so nobody arrives there by omission.
|
|
56
|
+
*/
|
|
57
|
+
authorize: ((request: HttpLogRequest) => boolean | Promise<boolean>) | 'unauthenticated';
|
|
58
|
+
/**
|
|
59
|
+
* Value for `Access-Control-Allow-Origin`. Defaults to `*`, matching
|
|
60
|
+
* `createClientLogFunction`'s `cors: true`.
|
|
61
|
+
*
|
|
62
|
+
* A browser cannot send credentials to a wildcard origin, so name your origins
|
|
63
|
+
* if the gate above reads a cookie. A bearer token in a header is unaffected.
|
|
64
|
+
*/
|
|
65
|
+
allowOrigin?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build a request handler that receives `LogPayload` over HTTP.
|
|
69
|
+
*
|
|
70
|
+
* import express from 'express'
|
|
71
|
+
* import { createHttpLogHandler } from '@dasasian/firebase-structured-logger/functions'
|
|
72
|
+
*
|
|
73
|
+
* const app = express()
|
|
74
|
+
* app.use(express.json({ limit: '10mb' })) // attachments ride in the body
|
|
75
|
+
* app.post('/log', createHttpLogHandler({ bucketName, authorize }))
|
|
76
|
+
*
|
|
77
|
+
* On the client, point `logFunction` at it:
|
|
78
|
+
*
|
|
79
|
+
* logFunction: async (payload) => {
|
|
80
|
+
* const res = await fetch('/log', {
|
|
81
|
+
* method: 'POST',
|
|
82
|
+
* headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${await user.getIdToken()}` },
|
|
83
|
+
* body: JSON.stringify(payload),
|
|
84
|
+
* })
|
|
85
|
+
* if (!res.ok) throw new Error(`log rejected: ${res.status}`)
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
export declare function createHttpLogHandler(config: HttpLogHandlerConfig): (req: HttpLogRequest, res: HttpLogResponse) => Promise<void>;
|
|
89
|
+
//# sourceMappingURL=httpHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpHandler.d.ts","sourceRoot":"","sources":["../../src/functions/httpHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAA0C,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAIlG,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IACtD,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/C,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,oBAAqB,SAAQ,sBAAsB;IAClE;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,iBAAiB,CAAA;IAExF;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAiBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CA4D9D"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Receive client logs over plain HTTP, for a backend that is not Cloud Functions
|
|
4
|
+
* (#34).
|
|
5
|
+
*
|
|
6
|
+
* The client half was already portable — `logFunction` is any
|
|
7
|
+
* `(payload) => Promise<unknown>`, and `httpsCallable()` merely happens to fit.
|
|
8
|
+
* What was missing was a receiving end that does not require a callable.
|
|
9
|
+
*
|
|
10
|
+
* This is deliberately framework-agnostic. It reads `method`, `headers` and a
|
|
11
|
+
* parsed `body`, and writes through `statusCode` / `setHeader` / `end` — the
|
|
12
|
+
* Node core shapes, which Express, Fastify's compat layer and Cloud Run's raw
|
|
13
|
+
* server all satisfy. Parsing the body is the caller's job (`express.json()` or
|
|
14
|
+
* equivalent); reading the stream here would mean guessing at limits and
|
|
15
|
+
* encodings that the host framework already has opinions about.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.createHttpLogHandler = createHttpLogHandler;
|
|
19
|
+
const logHandler_1 = require("./logHandler");
|
|
20
|
+
const traceContext_1 = require("./traceContext");
|
|
21
|
+
const STATUS = {
|
|
22
|
+
'invalid-argument': 400,
|
|
23
|
+
internal: 500,
|
|
24
|
+
};
|
|
25
|
+
function send(res, status, body) {
|
|
26
|
+
res.statusCode = status;
|
|
27
|
+
if (body) {
|
|
28
|
+
res.setHeader('Content-Type', 'application/json');
|
|
29
|
+
res.end(JSON.stringify(body));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
res.end();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a request handler that receives `LogPayload` over HTTP.
|
|
37
|
+
*
|
|
38
|
+
* import express from 'express'
|
|
39
|
+
* import { createHttpLogHandler } from '@dasasian/firebase-structured-logger/functions'
|
|
40
|
+
*
|
|
41
|
+
* const app = express()
|
|
42
|
+
* app.use(express.json({ limit: '10mb' })) // attachments ride in the body
|
|
43
|
+
* app.post('/log', createHttpLogHandler({ bucketName, authorize }))
|
|
44
|
+
*
|
|
45
|
+
* On the client, point `logFunction` at it:
|
|
46
|
+
*
|
|
47
|
+
* logFunction: async (payload) => {
|
|
48
|
+
* const res = await fetch('/log', {
|
|
49
|
+
* method: 'POST',
|
|
50
|
+
* headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${await user.getIdToken()}` },
|
|
51
|
+
* body: JSON.stringify(payload),
|
|
52
|
+
* })
|
|
53
|
+
* if (!res.ok) throw new Error(`log rejected: ${res.status}`)
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
function createHttpLogHandler(config) {
|
|
57
|
+
const handler = (0, logHandler_1.createClientLogHandler)(config);
|
|
58
|
+
const allowOrigin = config.allowOrigin ?? '*';
|
|
59
|
+
return async (req, res) => {
|
|
60
|
+
res.setHeader('Access-Control-Allow-Origin', allowOrigin);
|
|
61
|
+
res.setHeader('Vary', 'Origin');
|
|
62
|
+
// Preflight. The client sends Content-Type and usually Authorization, both
|
|
63
|
+
// of which make the request non-simple.
|
|
64
|
+
if (req.method === 'OPTIONS') {
|
|
65
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
66
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
67
|
+
res.setHeader('Access-Control-Max-Age', '3600');
|
|
68
|
+
return send(res, 204);
|
|
69
|
+
}
|
|
70
|
+
if (req.method !== 'POST') {
|
|
71
|
+
res.setHeader('Allow', 'POST, OPTIONS');
|
|
72
|
+
return send(res, 405, { error: 'Use POST' });
|
|
73
|
+
}
|
|
74
|
+
if (config.authorize !== 'unauthenticated') {
|
|
75
|
+
let allowed = false;
|
|
76
|
+
try {
|
|
77
|
+
allowed = await config.authorize(req);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
// A gate that threw is a gate that did not pass. Say so on the server
|
|
81
|
+
// and tell the caller nothing about why.
|
|
82
|
+
console.warn('[fsl] authorize() threw; rejecting the request:', err);
|
|
83
|
+
}
|
|
84
|
+
if (!allowed)
|
|
85
|
+
return send(res, 401, { error: 'Unauthorized' });
|
|
86
|
+
}
|
|
87
|
+
if (req.body === null || typeof req.body !== 'object') {
|
|
88
|
+
// Almost always a missing body parser rather than a bad client, and that
|
|
89
|
+
// is a five-minute fix once someone says it out loud.
|
|
90
|
+
return send(res, 400, {
|
|
91
|
+
error: 'Expected a parsed JSON body. Is express.json() (or equivalent) mounted before this handler?',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
// Cloud Logging groups a request's entries by trace. firebase-functions
|
|
96
|
+
// only attaches one inside its own wrapper, so out here we read the
|
|
97
|
+
// headers ourselves — otherwise the logs arrive uncorrelated and nothing
|
|
98
|
+
// says why.
|
|
99
|
+
await (0, traceContext_1.runWithTrace)((0, traceContext_1.traceIdFromHeaders)(req.headers), () => handler({ data: req.body }));
|
|
100
|
+
return send(res, 204);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (err instanceof logHandler_1.ClientLogError) {
|
|
104
|
+
// 'internal' has already been logged with its cause by the handler.
|
|
105
|
+
return send(res, STATUS[err.code], { error: err.message });
|
|
106
|
+
}
|
|
107
|
+
console.error('[fsl] Unhandled error in the HTTP log handler:', err);
|
|
108
|
+
return send(res, 500, { error: 'Failed to process log' });
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=httpHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpHandler.js","sourceRoot":"","sources":["../../src/functions/httpHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,8 +2,11 @@ import type { LogWriter } from './logger';
|
|
|
2
2
|
export { initLogger } from './logger';
|
|
3
3
|
export type { LogWriter } from './logger';
|
|
4
4
|
export { withLogging, getLogger } from './requestLogger';
|
|
5
|
-
export {
|
|
6
|
-
export
|
|
5
|
+
export { configureAttachments } from './sourceMapCache';
|
|
6
|
+
export { createClientLogHandler, createClientLogFunction, ClientLogError } from './logHandler';
|
|
7
|
+
export { createHttpLogHandler } from './httpHandler';
|
|
8
|
+
export type { ClientLogHandlerConfig, ClientLogRequest } from './logHandler';
|
|
9
|
+
export type { HttpLogHandlerConfig, HttpLogRequest, HttpLogResponse } from './httpHandler';
|
|
7
10
|
export type { LogSeverity, LogPayload, BaseLabels } from '../shared/types';
|
|
8
11
|
export declare const logError: (...args: Parameters<LogWriter["error"]>) => void;
|
|
9
12
|
export declare const logWarn: (...args: Parameters<LogWriter["warning"]>) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC5E,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC1F,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE1E,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAG,IAAkC,CAAA;AACrG,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,KAAG,IAAoC,CAAA;AACxG,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAG,IAAiC,CAAA;AAClG,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAG,IAAkC,CAAA"}
|
package/dist/functions/index.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.logDebug = exports.logInfo = exports.logWarn = exports.logError = exports.createClientLogFunction = exports.createClientLogHandler = exports.getLogger = exports.withLogging = exports.initLogger = void 0;
|
|
3
|
+
exports.logDebug = exports.logInfo = exports.logWarn = exports.logError = exports.createHttpLogHandler = exports.ClientLogError = exports.createClientLogFunction = exports.createClientLogHandler = exports.configureAttachments = exports.getLogger = exports.withLogging = exports.initLogger = void 0;
|
|
4
4
|
const requestLogger_1 = require("./requestLogger");
|
|
5
5
|
var logger_1 = require("./logger");
|
|
6
6
|
Object.defineProperty(exports, "initLogger", { enumerable: true, get: function () { return logger_1.initLogger; } });
|
|
7
7
|
var requestLogger_2 = require("./requestLogger");
|
|
8
8
|
Object.defineProperty(exports, "withLogging", { enumerable: true, get: function () { return requestLogger_2.withLogging; } });
|
|
9
9
|
Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return requestLogger_2.getLogger; } });
|
|
10
|
+
var sourceMapCache_1 = require("./sourceMapCache");
|
|
11
|
+
Object.defineProperty(exports, "configureAttachments", { enumerable: true, get: function () { return sourceMapCache_1.configureAttachments; } });
|
|
10
12
|
var logHandler_1 = require("./logHandler");
|
|
11
13
|
Object.defineProperty(exports, "createClientLogHandler", { enumerable: true, get: function () { return logHandler_1.createClientLogHandler; } });
|
|
12
14
|
Object.defineProperty(exports, "createClientLogFunction", { enumerable: true, get: function () { return logHandler_1.createClientLogFunction; } });
|
|
15
|
+
Object.defineProperty(exports, "ClientLogError", { enumerable: true, get: function () { return logHandler_1.ClientLogError; } });
|
|
16
|
+
var httpHandler_1 = require("./httpHandler");
|
|
17
|
+
Object.defineProperty(exports, "createHttpLogHandler", { enumerable: true, get: function () { return httpHandler_1.createHttpLogHandler; } });
|
|
13
18
|
const logError = (...args) => (0, requestLogger_1.getLogger)().error(...args);
|
|
14
19
|
exports.logError = logError;
|
|
15
20
|
const logWarn = (...args) => (0, requestLogger_1.getLogger)().warning(...args);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":";;;AAAA,mDAA2C;AAG3C,mCAAqC;AAA5B,oGAAA,UAAU,OAAA;AAEnB,iDAAwD;AAA/C,4GAAA,WAAW,OAAA;AAAE,0GAAA,SAAS,OAAA;AAC/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":";;;AAAA,mDAA2C;AAG3C,mCAAqC;AAA5B,oGAAA,UAAU,OAAA;AAEnB,iDAAwD;AAA/C,4GAAA,WAAW,OAAA;AAAE,0GAAA,SAAS,OAAA;AAC/B,mDAAuD;AAA9C,sHAAA,oBAAoB,OAAA;AAC7B,2CAA8F;AAArF,oHAAA,sBAAsB,OAAA;AAAE,qHAAA,uBAAuB,OAAA;AAAE,4GAAA,cAAc,OAAA;AACxE,6CAAoD;AAA3C,mHAAA,oBAAoB,OAAA;AAKtB,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAoC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;AAAxF,QAAA,QAAQ,YAAgF;AAC9F,MAAM,OAAO,GAAG,CAAC,GAAG,IAAsC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;AAA3F,QAAA,OAAO,WAAoF;AACjG,MAAM,OAAO,GAAG,CAAC,GAAG,IAAmC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;AAArF,QAAA,OAAO,WAA8E;AAC3F,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAoC,EAAQ,EAAE,CAAC,IAAA,yBAAS,GAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;AAAxF,QAAA,QAAQ,YAAgF"}
|
|
@@ -1,9 +1,60 @@
|
|
|
1
|
-
import { type CallableRequest } from 'firebase-functions/v2/https';
|
|
2
1
|
import type { LogPayload } from '../shared/types';
|
|
3
2
|
export interface ClientLogHandlerConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Storage bucket for **both** source maps and attachments.
|
|
5
|
+
*
|
|
6
|
+
* Maps go to `sourcemaps/{releaseId}/`, attachments to `logAttachments/{logId}/`.
|
|
7
|
+
* Defaults to the project's default bucket.
|
|
8
|
+
*
|
|
9
|
+
* Attachments read it as a fallback, not directly — this sets the process-wide
|
|
10
|
+
* default that `writeLog` reaches for, since the upload happens outside any
|
|
11
|
+
* handler. Override either half independently with `sourceMaps` below or
|
|
12
|
+
* `configureAttachments()`.
|
|
13
|
+
*/
|
|
4
14
|
bucketName?: string;
|
|
5
15
|
cors?: boolean | string | string[];
|
|
6
16
|
maxInstances?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Where this handler looks for source maps in Cloud Storage.
|
|
19
|
+
*
|
|
20
|
+
* Genuinely per-handler: both values travel explicitly to `getSourceMap`, so
|
|
21
|
+
* two handlers configured differently cannot resolve to whichever was
|
|
22
|
+
* constructed last. `bucket` overrides `bucketName` for map lookups only.
|
|
23
|
+
*
|
|
24
|
+
* `prefix` must match `fsl upload-sourcemaps --prefix`. They are the two ends
|
|
25
|
+
* of one contract (#35) and nothing checks them against each other — when they
|
|
26
|
+
* disagree the maps are simply not found and stacks stay minified.
|
|
27
|
+
*
|
|
28
|
+
* Attachments are configured separately, with `configureAttachments()` — the
|
|
29
|
+
* upload happens outside any handler, so a field here would be a lie.
|
|
30
|
+
*/
|
|
31
|
+
sourceMaps?: {
|
|
32
|
+
bucket?: string;
|
|
33
|
+
prefix?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* What the bare handler throws.
|
|
38
|
+
*
|
|
39
|
+
* It used to throw `HttpsError` directly, which made the handler
|
|
40
|
+
* Firebase-shaped in its failures as well as its input — a caller running it
|
|
41
|
+
* behind anything else got an error carrying a callable protocol's vocabulary.
|
|
42
|
+
* `createClientLogFunction` converts these back, so a callable client sees
|
|
43
|
+
* exactly what it saw before.
|
|
44
|
+
*/
|
|
45
|
+
export declare class ClientLogError extends Error {
|
|
46
|
+
readonly code: 'invalid-argument' | 'internal';
|
|
47
|
+
constructor(code: 'invalid-argument' | 'internal', message: string);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The minimum the handler actually reads.
|
|
51
|
+
*
|
|
52
|
+
* `CallableRequest` was in the signature and only `.data` was ever touched —
|
|
53
|
+
* not `.auth`, not `.rawRequest`. Saying so is what lets an Express adapter, or
|
|
54
|
+
* anything else, call this without constructing a callable request (#34).
|
|
55
|
+
*/
|
|
56
|
+
export interface ClientLogRequest {
|
|
57
|
+
data: LogPayload;
|
|
7
58
|
}
|
|
8
59
|
/**
|
|
9
60
|
* Create an onCall handler that receives LogPayload from the client,
|
|
@@ -12,7 +63,7 @@ export interface ClientLogHandlerConfig {
|
|
|
12
63
|
* @example
|
|
13
64
|
* export const logFrontendEvent = onCall(createClientLogHandler({ bucketName: 'my-bucket' }))
|
|
14
65
|
*/
|
|
15
|
-
export declare function createClientLogHandler(config: ClientLogHandlerConfig): (request:
|
|
66
|
+
export declare function createClientLogHandler(config: ClientLogHandlerConfig): (request: ClientLogRequest) => Promise<void>;
|
|
16
67
|
/**
|
|
17
68
|
* Convenience factory that wraps createClientLogHandler in onCall.
|
|
18
69
|
* Use when you want a ready-to-export Cloud Function.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logHandler.d.ts","sourceRoot":"","sources":["../../src/functions/logHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logHandler.d.ts","sourceRoot":"","sources":["../../src/functions/logHandler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,iBAAiB,CAAA;AAU/D,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAClD;AAID;;;;;;;;GAQG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAErC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,GAAG,UAAU;gBAArC,IAAI,EAAE,kBAAkB,GAAG,UAAU,EAC9C,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAA;CACjB;AA6DD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,IAGrD,SAAS,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAAC,CAqCxD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,8FAgB/F"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientLogError = void 0;
|
|
3
4
|
exports.createClientLogHandler = createClientLogHandler;
|
|
4
5
|
exports.createClientLogFunction = createClientLogFunction;
|
|
5
6
|
const https_1 = require("firebase-functions/v2/https");
|
|
@@ -8,6 +9,24 @@ const logger_1 = require("./logger");
|
|
|
8
9
|
const sourceMapCache_1 = require("./sourceMapCache");
|
|
9
10
|
const symbolicate_1 = require("./symbolicate");
|
|
10
11
|
const VALID_SEVERITIES = new Set(severity_1.SEVERITIES);
|
|
12
|
+
/**
|
|
13
|
+
* What the bare handler throws.
|
|
14
|
+
*
|
|
15
|
+
* It used to throw `HttpsError` directly, which made the handler
|
|
16
|
+
* Firebase-shaped in its failures as well as its input — a caller running it
|
|
17
|
+
* behind anything else got an error carrying a callable protocol's vocabulary.
|
|
18
|
+
* `createClientLogFunction` converts these back, so a callable client sees
|
|
19
|
+
* exactly what it saw before.
|
|
20
|
+
*/
|
|
21
|
+
class ClientLogError extends Error {
|
|
22
|
+
code;
|
|
23
|
+
constructor(code, message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.name = 'ClientLogError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.ClientLogError = ClientLogError;
|
|
11
30
|
/**
|
|
12
31
|
* Extract the bundle filename (e.g. "index-DnZ05f3M.js") from a frame URL.
|
|
13
32
|
*/
|
|
@@ -19,7 +38,7 @@ function bundleFileFromUrl(url) {
|
|
|
19
38
|
* Attempt to symbolicate a minified error stack trace using source maps.
|
|
20
39
|
* Loads source maps per-bundle so multiple chunks are handled correctly.
|
|
21
40
|
*/
|
|
22
|
-
async function symbolicateError(releaseId, error, bucketName) {
|
|
41
|
+
async function symbolicateError(releaseId, error, bucketName, prefix) {
|
|
23
42
|
if (!error?.stack)
|
|
24
43
|
return error;
|
|
25
44
|
try {
|
|
@@ -38,7 +57,7 @@ async function symbolicateError(releaseId, error, bucketName) {
|
|
|
38
57
|
// Load source map for each unique bundle (getSourceMap handles caching)
|
|
39
58
|
const sourceMaps = new Map();
|
|
40
59
|
await Promise.all(Array.from(bundleFiles).map(async (bundle) => {
|
|
41
|
-
const map = await (0, sourceMapCache_1.getSourceMap)(releaseId, bundle, bucketName);
|
|
60
|
+
const map = await (0, sourceMapCache_1.getSourceMap)(releaseId, bundle, bucketName, prefix);
|
|
42
61
|
if (map)
|
|
43
62
|
sourceMaps.set(bundle, map);
|
|
44
63
|
}));
|
|
@@ -69,17 +88,17 @@ function createClientLogHandler(config) {
|
|
|
69
88
|
return async (request) => {
|
|
70
89
|
const { message, severity, labels, jsonPayload } = request.data;
|
|
71
90
|
if (!message || !severity) {
|
|
72
|
-
throw new
|
|
91
|
+
throw new ClientLogError('invalid-argument', 'Missing message or severity');
|
|
73
92
|
}
|
|
74
93
|
if (!VALID_SEVERITIES.has(severity)) {
|
|
75
|
-
throw new
|
|
94
|
+
throw new ClientLogError('invalid-argument', `Invalid severity: ${severity}`);
|
|
76
95
|
}
|
|
77
96
|
const isEmulator = process.env.FUNCTIONS_EMULATOR === 'true';
|
|
78
97
|
try {
|
|
79
98
|
let processedError = jsonPayload?.error;
|
|
80
99
|
if (processedError?.stack && !isEmulator) {
|
|
81
100
|
const releaseId = labels?.releaseId ?? 'unknown';
|
|
82
|
-
processedError = await symbolicateError(releaseId, processedError, config.bucketName);
|
|
101
|
+
processedError = await symbolicateError(releaseId, processedError, config.sourceMaps?.bucket ?? config.bucketName, config.sourceMaps?.prefix);
|
|
83
102
|
}
|
|
84
103
|
(0, logger_1.writeLog)({
|
|
85
104
|
message,
|
|
@@ -91,7 +110,7 @@ function createClientLogHandler(config) {
|
|
|
91
110
|
}
|
|
92
111
|
catch (err) {
|
|
93
112
|
console.error('[fsl] Error processing client log:', err);
|
|
94
|
-
throw new
|
|
113
|
+
throw new ClientLogError('internal', 'Failed to process log');
|
|
95
114
|
}
|
|
96
115
|
};
|
|
97
116
|
}
|
|
@@ -103,6 +122,18 @@ function createClientLogHandler(config) {
|
|
|
103
122
|
* export const logFrontendEvent = createClientLogFunction({ bucketName: 'my-bucket' })
|
|
104
123
|
*/
|
|
105
124
|
function createClientLogFunction(config) {
|
|
106
|
-
|
|
125
|
+
const handler = createClientLogHandler(config);
|
|
126
|
+
return (0, https_1.onCall)({ cors: config.cors ?? true, maxInstances: config.maxInstances ?? 1 }, async (request) => {
|
|
127
|
+
try {
|
|
128
|
+
await handler(request);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
// Convert back at the Firebase boundary, so a callable client sees the
|
|
132
|
+
// same error it always did.
|
|
133
|
+
if (err instanceof ClientLogError)
|
|
134
|
+
throw new https_1.HttpsError(err.code, err.message);
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
107
138
|
}
|
|
108
139
|
//# sourceMappingURL=logHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logHandler.js","sourceRoot":"","sources":["../../src/functions/logHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logHandler.js","sourceRoot":"","sources":["../../src/functions/logHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAQ/D,eAAO,MAAM,YAAY,cAAc,CAAC;AAExC,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAKD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAQ9D;AAoFD;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASxB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,UAAU,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE,IAAI,CAuJN;AAaD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;eAuBrC,OAAO,WACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC5C,IAAI;oBAWI,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC5C,IAAI;uBAEI,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC5C,IAAI;qBAEI,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,YACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBACnB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC5C,IAAI;EAEV;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAG3D,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/functions/logger.js
CHANGED
|
@@ -45,6 +45,8 @@ const ulid_1 = require("ulid");
|
|
|
45
45
|
const severity_1 = require("../shared/severity");
|
|
46
46
|
const error_1 = require("../shared/error");
|
|
47
47
|
const sourceMapCache_1 = require("./sourceMapCache");
|
|
48
|
+
const paths_js_1 = require("../shared/paths.js");
|
|
49
|
+
const traceContext_1 = require("./traceContext");
|
|
48
50
|
const IS_EMULATOR = process.env.FUNCTIONS_EMULATOR === "true";
|
|
49
51
|
exports.LOG_FILENAME = "dev.jsonl";
|
|
50
52
|
let globalConfig = null;
|
|
@@ -102,9 +104,9 @@ function rotateLogFile(logDir, maxRotatedFiles) {
|
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
async function uploadLogAttachments(logId, logAttachments) {
|
|
105
|
-
const bucket = (0, sourceMapCache_1.
|
|
107
|
+
const bucket = (0, sourceMapCache_1.getAttachmentBucket)();
|
|
106
108
|
await Promise.all(Object.entries(logAttachments).map(async ([name, data]) => {
|
|
107
|
-
const file = bucket.file(
|
|
109
|
+
const file = bucket.file((0, paths_js_1.attachmentPath)(logId, name, (0, sourceMapCache_1.getAttachmentPrefix)()));
|
|
108
110
|
await file.save(Buffer.from(data, "base64"));
|
|
109
111
|
}));
|
|
110
112
|
}
|
|
@@ -226,11 +228,60 @@ function writeLog(payload) {
|
|
|
226
228
|
// not one of them, so it lands in jsonPayload.labels and `labels.appId="..."`
|
|
227
229
|
// filters match nothing. Verified live: the smoke run's entry labels contained only
|
|
228
230
|
// Cloud Functions' own platform labels until this changed.
|
|
231
|
+
// Cloud Error Reporting reads Cloud Logging and groups by exception type plus
|
|
232
|
+
// the five top-most frames — the fingerprint we would otherwise build. It
|
|
233
|
+
// looks for `stack_trace` at the TOP level of jsonPayload; ours lives one
|
|
234
|
+
// level down under `error`, so it has never been seen (#31).
|
|
235
|
+
//
|
|
236
|
+
// Moved, not duplicated. The stack is the largest field in an entry capped at
|
|
237
|
+
// 256 KB, and two copies of it buys nothing — so a reportable error carries its
|
|
238
|
+
// stack at `stack_trace` and its `error` object loses the `stack` key.
|
|
239
|
+
//
|
|
240
|
+
// A non-reportable entry keeps the stack where it was. It has to go somewhere,
|
|
241
|
+
// and the alternative — emitting `stack_trace` for warnings too — would likely
|
|
242
|
+
// turn every warning into something a person has to resolve in the Error
|
|
243
|
+
// Reporting console. The smoke run measures whether that is true; until it
|
|
244
|
+
// does, the cautious shape is the one that ships.
|
|
245
|
+
//
|
|
246
|
+
// ERROR and above only. A WARNING carrying a stack is not an error someone
|
|
247
|
+
// should have to resolve, and neither is a NOTICE feedback report — turning
|
|
248
|
+
// either into an Error Reporting group would be a regression of a deliberate
|
|
249
|
+
// product decision.
|
|
250
|
+
const stack = payload.jsonPayload?.error?.stack;
|
|
251
|
+
const reportable = stack && severity_1.SEVERITY_ORDER[severity] <= severity_1.SEVERITY_ORDER.ERROR && !(0, severity_1.isFeedback)(payload.labels);
|
|
252
|
+
const service = labels.appId ?? globalConfig?.appId;
|
|
253
|
+
const isReported = Boolean(reportable && service);
|
|
254
|
+
const errorReporting = isReported
|
|
255
|
+
? {
|
|
256
|
+
stack_trace: stack,
|
|
257
|
+
serviceContext: {
|
|
258
|
+
service: service,
|
|
259
|
+
...(labels.releaseId ? { version: labels.releaseId } : {}),
|
|
260
|
+
},
|
|
261
|
+
}
|
|
262
|
+
: {};
|
|
263
|
+
// Strip the now-redundant copy. Rebuilt rather than mutated: payload is the
|
|
264
|
+
// caller's object and writeLog has no business editing it.
|
|
265
|
+
const jsonPayload = isReported
|
|
266
|
+
? (() => {
|
|
267
|
+
const { error, ...rest } = payload.jsonPayload;
|
|
268
|
+
const { stack: _dropped, ...errorWithoutStack } = error;
|
|
269
|
+
return { ...rest, error: errorWithoutStack };
|
|
270
|
+
})()
|
|
271
|
+
: payload.jsonPayload;
|
|
272
|
+
// Set the trace ourselves when we have one. ffWrite attaches this from
|
|
273
|
+
// firebase-functions' own store, which is only populated inside their request
|
|
274
|
+
// wrapper — empty on Cloud Run and anything behind createHttpLogHandler. When
|
|
275
|
+
// theirs IS populated it overwrites this, which is the right precedence: inside
|
|
276
|
+
// Cloud Functions their value is authoritative.
|
|
277
|
+
const trace = (0, traceContext_1.traceResourceName)();
|
|
229
278
|
(0, logger_1.write)({
|
|
230
279
|
severity,
|
|
231
280
|
message: payload.message,
|
|
232
281
|
"logging.googleapis.com/labels": labels,
|
|
233
|
-
...
|
|
282
|
+
...(trace ? { "logging.googleapis.com/trace": trace } : {}),
|
|
283
|
+
...errorReporting,
|
|
284
|
+
...jsonPayload,
|
|
234
285
|
});
|
|
235
286
|
}
|
|
236
287
|
function logAttachmentsToBase64(logAttachments) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/functions/logger.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,gCAQC;AAuFD,kCAWC;AAKD,4BAyJC;AAgBD,0CAyDC;AA9WD,uCAAyB;AACzB,2CAA6B;AAC7B,sDAA6D;AAC7D,+BAA4B;AAE5B,iDAA2F;AAC3F,2CAA0D;AAC1D,qDAA4E;AAC5E,iDAAoD;AACpD,iDAAmD;AAEnD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,MAAM,CAAC;AACjD,QAAA,YAAY,GAAG,WAAW,CAAC;AAUxC,IAAI,YAAY,GAAiC,IAAI,CAAC;AACtD,IAAI,kBAAkB,GAAG,CAAC,CAAC;AAE3B;;;GAGG;AACH,SAAgB,UAAU,CAAC,MAA6B;IACtD,YAAY,GAAG,MAAM,CAAC;IACtB,kBAAkB,GAAG,CAAC,CAAC;IAEvB,IAAI,WAAW,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,aAAa,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,MAAc,EAAE,eAAuB;IAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAY,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,yEAAyE;QACzE,qEAAqE;QACrE,0EAA0E;QAC1E,mFAAmF;QACnF,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACjE,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,SAAS,QAAQ,CAAC,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;YAChE,0EAA0E;QAC5E,CAAC;QAED,2CAA2C;QAC3C,MAAM,OAAO,GAAG,EAAE;aACf,WAAW,CAAC,MAAM,CAAC;aACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aAC3D,IAAI,EAAE,CAAC,CAAC,0DAA0D;QAErE,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAC5B,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,eAAe,CAAC,CAC9C,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;YACtC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,GAAG,CAAC;gBAChE,2CAA2C;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,KAAa,EACb,cAAsC;IAEtC,MAAM,MAAM,GAAG,IAAA,oCAAmB,GAAE,CAAC;IACrC,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACxD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAA,yBAAc,EAAC,KAAK,EAAE,IAAI,EAAE,IAAA,oCAAmB,GAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,0CAA0C;AAC1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;AAC3C,SAAS,qBAAqB,CAAC,KAAc;IAC3C,MAAM,KAAK,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,IAAI,CACV,0BAA0B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,uBAAuB;YACpE,iBAAiB,qBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC5C,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,GAGZ;IACF,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,OAAO,EAAE,OAAO,CAAC,IAAI;IACrB,6EAA6E;IAC7E,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAC,IAAI;IACpB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,IAAI,EAAE,OAAO,CAAC,GAAG;CAClB,CAAC;AAEF;;GAEG;AACH,SAAgB,WAAW,CACzB,MAA2C;IAE3C,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAgB,QAAQ,CACtB,OAAmE;IAEnE,yEAAyE;IACzE,uEAAuE;IACvE,8EAA8E;IAC9E,8EAA8E;IAC9E,8EAA8E;IAC9E,6CAA6C;IAC7C,EAAE;IACF,0EAA0E;IAC1E,0EAA0E;IAC1E,EAAE;IACF,0EAA0E;IAC1E,8EAA8E;IAC9E,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAA,wBAAa,EAAC,OAAO,CAAC,QAAQ,CAAC;QAC9C,CAAC,CAAC,OAAO,CAAC,QAAQ;QAClB,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE5C,4EAA4E;IAC5E,0EAA0E;IAC1E,2EAA2E;IAC3E,0DAA0D;IAC1D,MAAM,WAAW,GACf,YAAY,EAAE,WAAW,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACnE,IACE,CAAC,IAAA,qBAAU,EAAC,OAAO,CAAC,MAAM,CAAC;QAC3B,yBAAc,CAAC,QAAQ,CAAC,GAAG,yBAAc,CAAC,WAAW,CAAC,EACtD,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAA,WAAI,GAAE,CAAC;IACrB,MAAM,cAAc,GAClB,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG;QACb,GAAG,OAAO,CAAC,MAAM;QACjB,KAAK;QACL,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;IAEF,IAAI,cAAc,EAAE,CAAC;QACnB,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,WAAY,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YAC9D,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,YAAY,EAAE,WAAW,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG;oBACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,QAAQ;oBACR,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,MAAM;oBACN,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,GAAG,CAAC,OAAO,CAAC,YAAY;wBACtB,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE;wBACxC,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC/D,CAAC;gBACF,MAAM,UAAU,GAAG,YAAY,CAAC,oBAAoB,IAAI,IAAI,CAAC;gBAC7D,MAAM,UAAU,GAAG,YAAY,CAAC,kBAAkB,IAAI,CAAC,CAAC;gBACxD,IAAI,kBAAkB,IAAI,UAAU,EAAE,CAAC;oBACrC,aAAa,CAAC,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;oBACpD,kBAAkB,GAAG,CAAC,CAAC;gBACzB,CAAC;gBACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,oBAAY,CAAC,CAAC;gBAClE,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;gBAClE,kBAAkB,EAAE,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,oCAAoC,EAAE,GAAG,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QAED,iDAAiD;QACjD,UAAU,CAAC,QAAQ,CAAC,CAClB,IAAI,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,EAClC,MAAM,CACP,CAAC;QACF,OAAO;IACT,CAAC;IAED,uFAAuF;IACvF,oFAAoF;IACpF,8EAA8E;IAC9E,EAAE;IACF,gFAAgF;IAChF,kFAAkF;IAClF,mFAAmF;IACnF,8EAA8E;IAC9E,oFAAoF;IACpF,2DAA2D;IAC3D,8EAA8E;IAC9E,0EAA0E;IAC1E,0EAA0E;IAC1E,6DAA6D;IAC7D,EAAE;IACF,8EAA8E;IAC9E,gFAAgF;IAChF,uEAAuE;IACvE,EAAE;IACF,+EAA+E;IAC/E,+EAA+E;IAC/E,yEAAyE;IACzE,2EAA2E;IAC3E,kDAAkD;IAClD,EAAE;IACF,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,oBAAoB;IACpB,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC;IAChD,MAAM,UAAU,GACd,KAAK,IAAI,yBAAc,CAAC,QAAQ,CAAC,IAAI,yBAAc,CAAC,KAAK,IAAI,CAAC,IAAA,qBAAU,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3F,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,IAAI,YAAY,EAAE,KAAK,CAAC;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,UAAU;QAC/B,CAAC,CAAC;YACE,WAAW,EAAE,KAAK;YAClB,cAAc,EAAE;gBACd,OAAO,EAAE,OAAQ;gBACjB,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3D;SACF;QACH,CAAC,CAAC,EAAE,CAAC;IAEP,4EAA4E;IAC5E,2DAA2D;IAC3D,MAAM,WAAW,GAAG,UAAU;QAC5B,CAAC,CAAC,CAAC,GAAG,EAAE;YACJ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,WAAY,CAAC;YAChD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,iBAAiB,EAAE,GAAG,KAAM,CAAC;YACzD,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;QAC/C,CAAC,CAAC,EAAE;QACN,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;IAExB,uEAAuE;IACvE,8EAA8E;IAC9E,8EAA8E;IAC9E,gFAAgF;IAChF,gDAAgD;IAChD,MAAM,KAAK,GAAG,IAAA,gCAAiB,GAAE,CAAC;IAElC,IAAA,cAAO,EAAC;QACN,QAAQ;QACR,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,+BAA+B,EAAE,MAAM;QACvC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,8BAA8B,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,cAAc;QACjB,GAAG,WAAW;KACf,CAAC,CAAC;AACL,CAAC;AAED,SAAS,sBAAsB,CAC7B,cAA2D;IAE3D,IAAI,CAAC,cAAc;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAE,CAAY,CAAC;IACzE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAC7B,UAA8C;IAE9C,MAAM,KAAK,GAAG,CAAC,KAA0C,EAAE,EAAE,CAC3D,CAAC,EAAE,GAAG,UAAU,EAAE,GAAG,KAAK,EAAE,CAAyB,CAAC;IAExD,MAAM,KAAK,GAAG,CACZ,QAAqB,EACrB,OAAe,EACf,MAA2C,EAC3C,OAAiC,EACjC,WAA6C,EACvC,EAAE;QACR,QAAQ,CAAC;YACP,OAAO;YACP,QAAQ;YACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;YACrB,WAAW,EAAE,EAAE,OAAO,EAAE;YACxB,WAAW,EAAE,sBAAsB,CAAC,WAAW,CAAC;SACjD,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CACH,GAAY,EACZ,MAA2C,EAC3C,OAAiC,EACjC,WAA6C;YAE7C,MAAM,KAAK,GAAG,IAAA,eAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,QAAQ,CAAC;gBACP,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;gBACnD,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAA,sBAAc,EAAC,KAAK,CAAC,EAAE;gBACtD,WAAW,EAAE,sBAAsB,CAAC,WAAW,CAAC;aACjD,CAAC,CAAC;QACL,CAAC;QACD,IAAI,EAAE,CACJ,OAAe,EACf,MAA2C,EAC3C,OAAiC,EACjC,WAA6C,EACvC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC;QAC/D,OAAO,EAAE,CACP,OAAe,EACf,MAA2C,EAC3C,OAAiC,EACjC,WAA6C,EACvC,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC;QAClE,KAAK,EAAE,CACL,OAAe,EACf,MAA2C,EAC3C,OAAiC,EACjC,WAA6C,EACvC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC;KACjE,CAAC;AACJ,CAAC"}
|