@dasasian/firebase-structured-logger 0.7.0 → 0.9.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 +183 -17
- package/dist/client/logger.d.ts +16 -0
- package/dist/client/logger.d.ts.map +1 -1
- package/dist/client/logger.js +137 -18
- package/dist/client/logger.js.map +1 -1
- package/dist/client/rateLimiter.d.ts +98 -10
- package/dist/client/rateLimiter.d.ts.map +1 -1
- package/dist/client/rateLimiter.js +282 -18
- package/dist/client/rateLimiter.js.map +1 -1
- package/dist/functions/httpHandler.d.ts.map +1 -1
- package/dist/functions/httpHandler.js +3 -0
- package/dist/functions/httpHandler.js.map +1 -1
- package/dist/functions/logHandler.d.ts +2 -1
- package/dist/functions/logHandler.d.ts.map +1 -1
- package/dist/functions/logHandler.js +16 -3
- 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 +299 -14
- package/dist/functions/logger.js.map +1 -1
- package/dist/functions/sourceMapCache.d.ts +14 -4
- package/dist/functions/sourceMapCache.d.ts.map +1 -1
- package/dist/functions/sourceMapCache.js +95 -6
- package/dist/functions/sourceMapCache.js.map +1 -1
- package/dist/functions/traceContext.d.ts +21 -7
- package/dist/functions/traceContext.d.ts.map +1 -1
- package/dist/functions/traceContext.js +60 -12
- package/dist/functions/traceContext.js.map +1 -1
- package/dist/shared/types.d.ts +7 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +11 -5
- package/skills/query-logs/SKILL.md +35 -0
|
@@ -1,7 +1,63 @@
|
|
|
1
|
+
import type { LogSeverity } from '../shared/types';
|
|
1
2
|
export interface RateLimitConfig {
|
|
3
|
+
/** The budget: how many logs can go out at once. Default 50. */
|
|
2
4
|
sessionLimit?: number;
|
|
5
|
+
/** How fast the budget comes back, per minute. Default 1. */
|
|
6
|
+
refillPerMinute?: number;
|
|
7
|
+
/** Share of `sessionLimit` only ERROR and above may spend. Default 0.2. */
|
|
8
|
+
errorReserve?: number;
|
|
9
|
+
/** Full copies of one error before it is only counted. Default 3. */
|
|
3
10
|
duplicateLimit?: number;
|
|
4
11
|
storageKey?: string;
|
|
12
|
+
/** How often a running count of repeats is sent as a summary. Default 60. */
|
|
13
|
+
summaryIntervalMinutes?: number;
|
|
14
|
+
/** How long an unsent summary is kept before it is deleted, not sent. Default 7. */
|
|
15
|
+
summaryMaxAgeDays?: number;
|
|
16
|
+
/** Most pending summaries kept at once; oldest are dropped first. Default 50. */
|
|
17
|
+
maxPendingSummaries?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A repeat count waiting to be sent as a WARNING summary. Queued in
|
|
21
|
+
* `localStorage` so it survives the tab closing — `sessionStorage` does not.
|
|
22
|
+
*
|
|
23
|
+
* `bootId` is what `sentLate` is built from: it is the module's own load-time
|
|
24
|
+
* id, not anything persisted. A summary queued during an earlier page load
|
|
25
|
+
* carries a different `bootId` than the one currently running, so popping it
|
|
26
|
+
* here reveals it as "from a previous visit" without needing a server
|
|
27
|
+
* round-trip or a wall-clock guess.
|
|
28
|
+
*
|
|
29
|
+
* `message`/`errorType`/`screen`/`releaseId`/`userId` are short fields
|
|
30
|
+
* rebuilt once from the signature's key when the summary is created (see
|
|
31
|
+
* `parseSignatureKey`) — not a full label copy. Everything else about the
|
|
32
|
+
* error (browser, platform, stack, breadcrumbs…) is already in Cloud Logging
|
|
33
|
+
* on the full copies `repeatOf` points back to.
|
|
34
|
+
*/
|
|
35
|
+
export interface PendingSummary {
|
|
36
|
+
id: string;
|
|
37
|
+
message: string;
|
|
38
|
+
errorType?: string;
|
|
39
|
+
screen?: string;
|
|
40
|
+
releaseId?: string;
|
|
41
|
+
userId?: string;
|
|
42
|
+
repeatOf: string;
|
|
43
|
+
repeatCount: number;
|
|
44
|
+
firstSeen: string;
|
|
45
|
+
lastSeen: string;
|
|
46
|
+
createdAt: number;
|
|
47
|
+
bootId: string;
|
|
48
|
+
}
|
|
49
|
+
export interface SentSummary {
|
|
50
|
+
id: string;
|
|
51
|
+
message: string;
|
|
52
|
+
errorType?: string;
|
|
53
|
+
screen?: string;
|
|
54
|
+
releaseId?: string;
|
|
55
|
+
userId?: string;
|
|
56
|
+
repeatOf: string;
|
|
57
|
+
repeatCount: number;
|
|
58
|
+
firstSeen: string;
|
|
59
|
+
lastSeen: string;
|
|
60
|
+
sentLate: boolean;
|
|
5
61
|
}
|
|
6
62
|
export declare function configureRateLimiter(options: RateLimitConfig): void;
|
|
7
63
|
/**
|
|
@@ -15,25 +71,57 @@ export declare function configureRateLimiter(options: RateLimitConfig): void;
|
|
|
15
71
|
* without the staleness of a span someone has to remember to clear.
|
|
16
72
|
*/
|
|
17
73
|
export declare function signatureFor(error: Error | string, screen?: string): string;
|
|
74
|
+
/** A signature as a person reads it, for the console: `TypeError: cannot read 'id' | checkout`. */
|
|
75
|
+
export declare function describeSignature(signature: string): string;
|
|
18
76
|
export type RateLimitDecision = {
|
|
19
77
|
allowed: true;
|
|
78
|
+
repeatKey?: string;
|
|
20
79
|
} | {
|
|
21
80
|
allowed: false;
|
|
22
|
-
reason: 'session-limit' | 'duplicate';
|
|
81
|
+
reason: 'session-limit' | 'reserve' | 'duplicate';
|
|
23
82
|
signature?: string;
|
|
24
83
|
};
|
|
84
|
+
export interface AllowOptions {
|
|
85
|
+
signature?: string;
|
|
86
|
+
severity: LogSeverity;
|
|
87
|
+
labels?: Record<string, string | undefined>;
|
|
88
|
+
}
|
|
89
|
+
export declare function allow(options: AllowOptions): RateLimitDecision;
|
|
25
90
|
/**
|
|
26
|
-
*
|
|
91
|
+
* Move any signature's accumulated repeats into the pending-summary queue —
|
|
92
|
+
* every one whose window (`summaryIntervalMinutes`) has elapsed, or every one
|
|
93
|
+
* with anything to send when `force` is true (the tab going hidden).
|
|
27
94
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
95
|
+
* Only the counting resets; `count` (full copies already sent) is untouched,
|
|
96
|
+
* so a signature stays in "counting" mode across summary windows rather than
|
|
97
|
+
* sending three more full copies before the next summary.
|
|
98
|
+
*/
|
|
99
|
+
export declare function flushDueSummaries(force?: boolean): void;
|
|
100
|
+
/**
|
|
101
|
+
* Look at every pending summary not past `summaryMaxAgeDays`, WITHOUT
|
|
102
|
+
* removing it from the queue. A summary may only leave the queue once its
|
|
103
|
+
* send has actually succeeded — see `acknowledgeSummary` — so a failed send
|
|
104
|
+
* (offline, or the tab tearing down right after the `visibilitychange` that
|
|
105
|
+
* triggered this) leaves it queued for the next flush or the next visit to
|
|
106
|
+
* retry, instead of losing it. `sentLate` is true for anything queued by an
|
|
107
|
+
* earlier page load (a different `bootId`): the case the README's "next
|
|
108
|
+
* visit sends them" describes.
|
|
33
109
|
*
|
|
34
|
-
*
|
|
35
|
-
|
|
110
|
+
* Expired entries ARE removed here — there is no send to wait on for those.
|
|
111
|
+
*/
|
|
112
|
+
export declare function peekPendingSummaries(): SentSummary[];
|
|
113
|
+
/**
|
|
114
|
+
* Remove one summary from the queue by id, once its send has resolved. A
|
|
115
|
+
* retry that calls this again for an id already gone is a no-op — it never
|
|
116
|
+
* re-adds anything, so it cannot create a second entry.
|
|
117
|
+
*/
|
|
118
|
+
export declare function acknowledgeSummary(id: string): void;
|
|
119
|
+
/**
|
|
120
|
+
* Test-only reset. There is no `beforeunload` wiring any more — the budget
|
|
121
|
+
* lives in `sessionStorage` on purpose, so a reload keeps it — but tests still
|
|
122
|
+
* need a way to start a clean session, and to simulate a genuinely new visit
|
|
123
|
+
* (a new tab gets a new `bootId` for free, since the module reloads with it;
|
|
124
|
+
* a test in the same process cannot, so this rotates it explicitly).
|
|
36
125
|
*/
|
|
37
|
-
export declare function allow(signature?: string): RateLimitDecision;
|
|
38
126
|
export declare function resetRateLimiter(): void;
|
|
39
127
|
//# sourceMappingURL=rateLimiter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,28 +2,66 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.configureRateLimiter = configureRateLimiter;
|
|
4
4
|
exports.signatureFor = signatureFor;
|
|
5
|
+
exports.describeSignature = describeSignature;
|
|
5
6
|
exports.allow = allow;
|
|
7
|
+
exports.flushDueSummaries = flushDueSummaries;
|
|
8
|
+
exports.peekPendingSummaries = peekPendingSummaries;
|
|
9
|
+
exports.acknowledgeSummary = acknowledgeSummary;
|
|
6
10
|
exports.resetRateLimiter = resetRateLimiter;
|
|
11
|
+
const severity_1 = require("../shared/severity");
|
|
7
12
|
const DEFAULT_SESSION_LIMIT = 50;
|
|
13
|
+
const DEFAULT_REFILL_PER_MINUTE = 1;
|
|
14
|
+
const DEFAULT_ERROR_RESERVE = 0.2;
|
|
8
15
|
const DEFAULT_DUPLICATE_LIMIT = 3;
|
|
9
16
|
const DEFAULT_STORAGE_KEY = 'fsl_ratelimit';
|
|
17
|
+
const DEFAULT_SUMMARY_INTERVAL_MINUTES = 60;
|
|
18
|
+
const DEFAULT_SUMMARY_MAX_AGE_DAYS = 7;
|
|
19
|
+
const DEFAULT_MAX_PENDING_SUMMARIES = 50;
|
|
20
|
+
// Fixed, not configurable — the seven knobs in RateLimitConfig are the whole
|
|
21
|
+
// documented surface (README, "Volume controls"). A second storage key for the
|
|
22
|
+
// summary queue would be an eighth nobody asked for.
|
|
23
|
+
const SUMMARY_STORAGE_KEY = 'fsl_pending_summaries';
|
|
24
|
+
/**
|
|
25
|
+
* The flood this feature exists to survive is exactly "many distinct
|
|
26
|
+
* messages" — a URL, an id, anything with a variable in the string — so
|
|
27
|
+
* `state.signatures` cannot be left to grow one entry per message forever:
|
|
28
|
+
* that eventually fills sessionStorage (a quota error, then the state is
|
|
29
|
+
* silently lost) and makes every `allow()` parse an ever-larger map. Not a
|
|
30
|
+
* config knob: nobody tunes this per app, it is a safety valve.
|
|
31
|
+
*/
|
|
32
|
+
const MAX_SIGNATURES = 200;
|
|
10
33
|
// Session-scoped by design: one browser session, one budget. Deliberately not
|
|
11
34
|
// per-Logger — see the module-scoped state rule in CLAUDE.md.
|
|
12
35
|
let config = {
|
|
13
36
|
sessionLimit: DEFAULT_SESSION_LIMIT,
|
|
37
|
+
refillPerMinute: DEFAULT_REFILL_PER_MINUTE,
|
|
38
|
+
errorReserve: DEFAULT_ERROR_RESERVE,
|
|
14
39
|
duplicateLimit: DEFAULT_DUPLICATE_LIMIT,
|
|
15
40
|
storageKey: DEFAULT_STORAGE_KEY,
|
|
41
|
+
summaryIntervalMinutes: DEFAULT_SUMMARY_INTERVAL_MINUTES,
|
|
42
|
+
summaryMaxAgeDays: DEFAULT_SUMMARY_MAX_AGE_DAYS,
|
|
43
|
+
maxPendingSummaries: DEFAULT_MAX_PENDING_SUMMARIES,
|
|
16
44
|
};
|
|
17
45
|
function configureRateLimiter(options) {
|
|
18
46
|
config = { ...config, ...options };
|
|
19
47
|
}
|
|
48
|
+
// This tab's identity for `sentLate`. Regenerated by `resetRateLimiter`, which
|
|
49
|
+
// is how tests simulate a new visit — a real reload gets a new one for free,
|
|
50
|
+
// since the module (and this `let`) is re-evaluated from scratch.
|
|
51
|
+
let bootId = generateId();
|
|
52
|
+
function generateId() {
|
|
53
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
54
|
+
}
|
|
55
|
+
function defaultState() {
|
|
56
|
+
return { available: config.sessionLimit, lastRefillAt: Date.now(), signatures: {} };
|
|
57
|
+
}
|
|
20
58
|
function readState() {
|
|
21
59
|
try {
|
|
22
60
|
const stored = sessionStorage.getItem(config.storageKey);
|
|
23
|
-
return stored ? JSON.parse(stored) :
|
|
61
|
+
return stored ? JSON.parse(stored) : defaultState();
|
|
24
62
|
}
|
|
25
63
|
catch {
|
|
26
|
-
return
|
|
64
|
+
return defaultState();
|
|
27
65
|
}
|
|
28
66
|
}
|
|
29
67
|
function writeState(state) {
|
|
@@ -34,6 +72,38 @@ function writeState(state) {
|
|
|
34
72
|
// sessionStorage full or unavailable — rate limiting degrades, logging continues
|
|
35
73
|
}
|
|
36
74
|
}
|
|
75
|
+
function readSummaryQueue() {
|
|
76
|
+
try {
|
|
77
|
+
const stored = localStorage.getItem(SUMMARY_STORAGE_KEY);
|
|
78
|
+
return stored ? JSON.parse(stored) : [];
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function writeSummaryQueue(queue) {
|
|
85
|
+
try {
|
|
86
|
+
localStorage.setItem(SUMMARY_STORAGE_KEY, JSON.stringify(queue));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// localStorage full or unavailable — summaries degrade, logging continues
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function isExpired(summary, now) {
|
|
93
|
+
return now - summary.createdAt >= config.summaryMaxAgeDays * 24 * 60 * 60 * 1000;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Refill the budget for the time elapsed since it was last touched, capped at
|
|
97
|
+
* `sessionLimit`. Mutates `state` in place; the caller decides whether the
|
|
98
|
+
* result is worth persisting.
|
|
99
|
+
*/
|
|
100
|
+
function refill(state, now) {
|
|
101
|
+
const elapsedMinutes = (now - state.lastRefillAt) / 60_000;
|
|
102
|
+
if (elapsedMinutes <= 0)
|
|
103
|
+
return;
|
|
104
|
+
state.available = Math.min(config.sessionLimit, state.available + elapsedMinutes * config.refillPerMinute);
|
|
105
|
+
state.lastRefillAt = now;
|
|
106
|
+
}
|
|
37
107
|
/**
|
|
38
108
|
* Build the key used to recognise a repeat of the same problem. Two occurrences
|
|
39
109
|
* count as duplicates only if the message and the screen both match, so the same
|
|
@@ -45,8 +115,51 @@ function writeState(state) {
|
|
|
45
115
|
* without the staleness of a span someone has to remember to clear.
|
|
46
116
|
*/
|
|
47
117
|
function signatureFor(error, screen) {
|
|
48
|
-
|
|
49
|
-
|
|
118
|
+
// A JSON list, not a joined string: a summary rebuilds the name, message and
|
|
119
|
+
// screen from this key (parseSignatureKey), and a message containing `:` or
|
|
120
|
+
// `|` — "upload failed: timeout" — must not split into the wrong fields.
|
|
121
|
+
const errorType = error instanceof Error ? error.name : null;
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
return JSON.stringify([errorType, message, screen ?? '']);
|
|
124
|
+
}
|
|
125
|
+
/** A signature as a person reads it, for the console: `TypeError: cannot read 'id' | checkout`. */
|
|
126
|
+
function describeSignature(signature) {
|
|
127
|
+
const { errorType, message, screen } = parseSignature(signature);
|
|
128
|
+
return `${errorType ? `${errorType}: ` : ''}${message}${screen ? ` | ${screen}` : ''}`;
|
|
129
|
+
}
|
|
130
|
+
function parseSignature(signature) {
|
|
131
|
+
try {
|
|
132
|
+
const [errorType, message, screen] = JSON.parse(signature);
|
|
133
|
+
return { errorType: errorType ?? undefined, message, screen: screen || undefined };
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Not one of ours — keep it whole rather than guess at its parts.
|
|
137
|
+
return { message: signature };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A signature is further scoped to the release and the user before it is
|
|
142
|
+
* counted — two releases, or two people on one machine, must never share one
|
|
143
|
+
* running count. See README, "Repeats are counted, not dropped".
|
|
144
|
+
*/
|
|
145
|
+
function compoundKey(signature, labels) {
|
|
146
|
+
return JSON.stringify([signature, labels?.releaseId ?? '', labels?.userId ?? '']);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The inverse of `compoundKey` + `signatureFor`: recover the name, message,
|
|
150
|
+
* screen, releaseId and userId a summary needs from the key that already
|
|
151
|
+
* holds them, rather than storing a second copy per signature — see
|
|
152
|
+
* `SignatureState`'s doc for why that copy has to go. Both are JSON lists, so
|
|
153
|
+
* the fields come back exactly, whatever characters a message contains.
|
|
154
|
+
*/
|
|
155
|
+
function parseSignatureKey(key) {
|
|
156
|
+
try {
|
|
157
|
+
const [signature, releaseId, userId] = JSON.parse(key);
|
|
158
|
+
return { ...parseSignature(signature), releaseId: releaseId || undefined, userId: userId || undefined };
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return { message: key };
|
|
162
|
+
}
|
|
50
163
|
}
|
|
51
164
|
/**
|
|
52
165
|
* Decide whether one log may be sent, and consume its budget if so.
|
|
@@ -58,24 +171,177 @@ function signatureFor(error, screen) {
|
|
|
58
171
|
* cannot be checked without consuming.
|
|
59
172
|
*
|
|
60
173
|
* Pass a `signature` to opt this log into duplicate suppression. Any severity
|
|
61
|
-
* may do so; it is not reserved for errors.
|
|
174
|
+
* may do so; it is not reserved for errors. Once `duplicateLimit` full copies
|
|
175
|
+
* have gone out, further occurrences are neither sent nor refused outright —
|
|
176
|
+
* they are counted here, toward the next repeat summary, and cost no budget.
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* Keep `state.signatures` at or under `MAX_SIGNATURES`. Evicts the oldest
|
|
180
|
+
* (by `lastTouched`) signatures that have no pending repeat count first;
|
|
181
|
+
* a signature with an unsent `repeatCount` is never dropped, even if that
|
|
182
|
+
* leaves the map over the cap — losing a count already promised to a summary
|
|
183
|
+
* is worse than a temporarily oversized map.
|
|
62
184
|
*/
|
|
63
|
-
function
|
|
185
|
+
function pruneSignatures(state) {
|
|
186
|
+
const keys = Object.keys(state.signatures);
|
|
187
|
+
let overflow = keys.length - MAX_SIGNATURES;
|
|
188
|
+
if (overflow <= 0)
|
|
189
|
+
return;
|
|
190
|
+
const evictable = keys
|
|
191
|
+
.filter((k) => state.signatures[k].repeatCount === undefined)
|
|
192
|
+
.sort((a, b) => state.signatures[a].lastTouched - state.signatures[b].lastTouched);
|
|
193
|
+
for (const k of evictable) {
|
|
194
|
+
if (overflow <= 0)
|
|
195
|
+
break;
|
|
196
|
+
delete state.signatures[k];
|
|
197
|
+
overflow--;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function allow(options) {
|
|
201
|
+
const now = Date.now();
|
|
64
202
|
const state = readState();
|
|
65
|
-
|
|
203
|
+
refill(state, now);
|
|
204
|
+
let key;
|
|
205
|
+
let sig;
|
|
206
|
+
if (options.signature !== undefined) {
|
|
207
|
+
key = compoundKey(options.signature, options.labels);
|
|
208
|
+
sig = state.signatures[key] ?? { count: 0, repeatKey: generateId(), lastTouched: now };
|
|
209
|
+
sig.lastTouched = now;
|
|
210
|
+
}
|
|
211
|
+
if (sig && key && sig.count >= config.duplicateLimit) {
|
|
212
|
+
sig.repeatCount = (sig.repeatCount ?? 0) + 1;
|
|
213
|
+
if (sig.firstSeen === undefined)
|
|
214
|
+
sig.firstSeen = now;
|
|
215
|
+
sig.lastSeen = now;
|
|
216
|
+
state.signatures[key] = sig;
|
|
217
|
+
pruneSignatures(state);
|
|
218
|
+
writeState(state);
|
|
219
|
+
return { allowed: false, reason: 'duplicate', signature: options.signature };
|
|
220
|
+
}
|
|
221
|
+
const reserveThreshold = config.sessionLimit * config.errorReserve;
|
|
222
|
+
if (state.available < 1) {
|
|
223
|
+
writeState(state);
|
|
66
224
|
return { allowed: false, reason: 'session-limit' };
|
|
67
225
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
226
|
+
// "ERROR and above" as a rank, not the literal string 'ERROR' — so the check
|
|
227
|
+
// stays right if a more severe level is ever added to SEVERITY_ORDER.
|
|
228
|
+
if (state.available <= reserveThreshold && severity_1.SEVERITY_ORDER[options.severity] > severity_1.SEVERITY_ORDER.ERROR) {
|
|
229
|
+
writeState(state);
|
|
230
|
+
return { allowed: false, reason: 'reserve' };
|
|
231
|
+
}
|
|
232
|
+
state.available -= 1;
|
|
233
|
+
if (sig && key) {
|
|
234
|
+
sig.count += 1;
|
|
235
|
+
state.signatures[key] = sig;
|
|
236
|
+
pruneSignatures(state);
|
|
74
237
|
}
|
|
75
|
-
state.logCount++;
|
|
76
238
|
writeState(state);
|
|
77
|
-
return { allowed: true };
|
|
239
|
+
return { allowed: true, repeatKey: sig?.repeatKey };
|
|
78
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Move any signature's accumulated repeats into the pending-summary queue —
|
|
243
|
+
* every one whose window (`summaryIntervalMinutes`) has elapsed, or every one
|
|
244
|
+
* with anything to send when `force` is true (the tab going hidden).
|
|
245
|
+
*
|
|
246
|
+
* Only the counting resets; `count` (full copies already sent) is untouched,
|
|
247
|
+
* so a signature stays in "counting" mode across summary windows rather than
|
|
248
|
+
* sending three more full copies before the next summary.
|
|
249
|
+
*/
|
|
250
|
+
function flushDueSummaries(force = false) {
|
|
251
|
+
const now = Date.now();
|
|
252
|
+
const state = readState();
|
|
253
|
+
refill(state, now);
|
|
254
|
+
const intervalMs = config.summaryIntervalMinutes * 60_000;
|
|
255
|
+
let changed = false;
|
|
256
|
+
for (const key of Object.keys(state.signatures)) {
|
|
257
|
+
const sig = state.signatures[key];
|
|
258
|
+
if (!sig.repeatCount || sig.firstSeen === undefined || sig.lastSeen === undefined)
|
|
259
|
+
continue;
|
|
260
|
+
if (!force && now - sig.firstSeen < intervalMs)
|
|
261
|
+
continue;
|
|
262
|
+
const { errorType, message, screen, releaseId, userId } = parseSignatureKey(key);
|
|
263
|
+
const maxAgeMs = config.summaryMaxAgeDays * 24 * 60 * 60 * 1000;
|
|
264
|
+
const queue = readSummaryQueue().filter((s) => now - s.createdAt < maxAgeMs);
|
|
265
|
+
queue.push({
|
|
266
|
+
id: generateId(),
|
|
267
|
+
message,
|
|
268
|
+
errorType,
|
|
269
|
+
screen,
|
|
270
|
+
releaseId,
|
|
271
|
+
userId,
|
|
272
|
+
repeatOf: sig.repeatKey,
|
|
273
|
+
repeatCount: sig.repeatCount,
|
|
274
|
+
firstSeen: new Date(sig.firstSeen).toISOString(),
|
|
275
|
+
lastSeen: new Date(sig.lastSeen).toISOString(),
|
|
276
|
+
createdAt: now,
|
|
277
|
+
bootId,
|
|
278
|
+
});
|
|
279
|
+
// Keep the newest when the queue is over its cap.
|
|
280
|
+
const trimmed = queue.length > config.maxPendingSummaries
|
|
281
|
+
? queue.slice(queue.length - config.maxPendingSummaries)
|
|
282
|
+
: queue;
|
|
283
|
+
writeSummaryQueue(trimmed);
|
|
284
|
+
sig.repeatCount = undefined;
|
|
285
|
+
sig.firstSeen = undefined;
|
|
286
|
+
sig.lastSeen = undefined;
|
|
287
|
+
changed = true;
|
|
288
|
+
}
|
|
289
|
+
if (changed)
|
|
290
|
+
writeState(state);
|
|
291
|
+
}
|
|
292
|
+
function toSentSummary(s) {
|
|
293
|
+
return {
|
|
294
|
+
id: s.id,
|
|
295
|
+
message: s.message,
|
|
296
|
+
errorType: s.errorType,
|
|
297
|
+
screen: s.screen,
|
|
298
|
+
releaseId: s.releaseId,
|
|
299
|
+
userId: s.userId,
|
|
300
|
+
repeatOf: s.repeatOf,
|
|
301
|
+
repeatCount: s.repeatCount,
|
|
302
|
+
firstSeen: s.firstSeen,
|
|
303
|
+
lastSeen: s.lastSeen,
|
|
304
|
+
sentLate: s.bootId !== bootId,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Look at every pending summary not past `summaryMaxAgeDays`, WITHOUT
|
|
309
|
+
* removing it from the queue. A summary may only leave the queue once its
|
|
310
|
+
* send has actually succeeded — see `acknowledgeSummary` — so a failed send
|
|
311
|
+
* (offline, or the tab tearing down right after the `visibilitychange` that
|
|
312
|
+
* triggered this) leaves it queued for the next flush or the next visit to
|
|
313
|
+
* retry, instead of losing it. `sentLate` is true for anything queued by an
|
|
314
|
+
* earlier page load (a different `bootId`): the case the README's "next
|
|
315
|
+
* visit sends them" describes.
|
|
316
|
+
*
|
|
317
|
+
* Expired entries ARE removed here — there is no send to wait on for those.
|
|
318
|
+
*/
|
|
319
|
+
function peekPendingSummaries() {
|
|
320
|
+
const now = Date.now();
|
|
321
|
+
const queue = readSummaryQueue();
|
|
322
|
+
const fresh = queue.filter((s) => !isExpired(s, now));
|
|
323
|
+
if (fresh.length !== queue.length)
|
|
324
|
+
writeSummaryQueue(fresh);
|
|
325
|
+
return fresh.map(toSentSummary);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Remove one summary from the queue by id, once its send has resolved. A
|
|
329
|
+
* retry that calls this again for an id already gone is a no-op — it never
|
|
330
|
+
* re-adds anything, so it cannot create a second entry.
|
|
331
|
+
*/
|
|
332
|
+
function acknowledgeSummary(id) {
|
|
333
|
+
const queue = readSummaryQueue();
|
|
334
|
+
const next = queue.filter((s) => s.id !== id);
|
|
335
|
+
if (next.length !== queue.length)
|
|
336
|
+
writeSummaryQueue(next);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Test-only reset. There is no `beforeunload` wiring any more — the budget
|
|
340
|
+
* lives in `sessionStorage` on purpose, so a reload keeps it — but tests still
|
|
341
|
+
* need a way to start a clean session, and to simulate a genuinely new visit
|
|
342
|
+
* (a new tab gets a new `bootId` for free, since the module reloads with it;
|
|
343
|
+
* a test in the same process cannot, so this rotates it explicitly).
|
|
344
|
+
*/
|
|
79
345
|
function resetRateLimiter() {
|
|
80
346
|
try {
|
|
81
347
|
sessionStorage.removeItem(config.storageKey);
|
|
@@ -83,8 +349,6 @@ function resetRateLimiter() {
|
|
|
83
349
|
catch {
|
|
84
350
|
// Silently fail
|
|
85
351
|
}
|
|
86
|
-
|
|
87
|
-
if (typeof window !== 'undefined') {
|
|
88
|
-
window.addEventListener('beforeunload', resetRateLimiter);
|
|
352
|
+
bootId = generateId();
|
|
89
353
|
}
|
|
90
354
|
//# sourceMappingURL=rateLimiter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/client/rateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +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,
|
|
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,CA+D9D"}
|
|
@@ -96,6 +96,9 @@ function createHttpLogHandler(config) {
|
|
|
96
96
|
// only attaches one inside its own wrapper, so out here we read the
|
|
97
97
|
// headers ourselves — otherwise the logs arrive uncorrelated and nothing
|
|
98
98
|
// says why.
|
|
99
|
+
// Once per process, and a no-op off Cloud Run: the trace needs the project
|
|
100
|
+
// id, which Cloud Run does not put in the environment.
|
|
101
|
+
await (0, traceContext_1.resolveTraceProject)();
|
|
99
102
|
await (0, traceContext_1.runWithTrace)((0, traceContext_1.traceIdFromHeaders)(req.headers), () => handler({ data: req.body }));
|
|
100
103
|
return send(res, 204);
|
|
101
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpHandler.js","sourceRoot":"","sources":["../../src/functions/httpHandler.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAgGH,
|
|
1
|
+
{"version":3,"file":"httpHandler.js","sourceRoot":"","sources":["../../src/functions/httpHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type * as FirebaseHttps from 'firebase-functions/v2/https';
|
|
1
2
|
import type { LogPayload } from '../shared/types';
|
|
2
3
|
export interface ClientLogHandlerConfig {
|
|
3
4
|
/**
|
|
@@ -74,5 +75,5 @@ export declare function createClientLogHandler(config: ClientLogHandlerConfig):
|
|
|
74
75
|
export declare function createClientLogFunction(config: ClientLogHandlerConfig & {
|
|
75
76
|
cors?: boolean | string | string[];
|
|
76
77
|
maxInstances?: number;
|
|
77
|
-
}):
|
|
78
|
+
}): FirebaseHttps.CallableFunction<LogPayload, Promise<void>, unknown>;
|
|
78
79
|
//# sourceMappingURL=logHandler.d.ts.map
|
|
@@ -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":"AAAA,OAAO,KAAK,KAAK,aAAa,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,iBAAiB,CAAA;AAuB/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,CAsCxD;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,sEAiB/F"}
|
|
@@ -3,11 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ClientLogError = void 0;
|
|
4
4
|
exports.createClientLogHandler = createClientLogHandler;
|
|
5
5
|
exports.createClientLogFunction = createClientLogFunction;
|
|
6
|
-
const https_1 = require("firebase-functions/v2/https");
|
|
7
6
|
const severity_1 = require("../shared/severity");
|
|
8
7
|
const logger_1 = require("./logger");
|
|
9
8
|
const sourceMapCache_1 = require("./sourceMapCache");
|
|
10
9
|
const symbolicate_1 = require("./symbolicate");
|
|
10
|
+
/**
|
|
11
|
+
* OPTIONAL PEER — firebase-functions is loaded lazily, on purpose.
|
|
12
|
+
*
|
|
13
|
+
* Only createClientLogFunction needs it, and that only runs on Cloud Functions, where
|
|
14
|
+
* it is always installed. A top-level import made the whole `/functions` entry point
|
|
15
|
+
* fail on `require` on a backend that wants createHttpLogHandler and nothing else
|
|
16
|
+
* (#39). `require` rather than `await import`: createClientLogFunction must return
|
|
17
|
+
* the function synchronously, because `firebase deploy` reads the export at load.
|
|
18
|
+
*/
|
|
19
|
+
function loadFirebaseHttps() {
|
|
20
|
+
return require('firebase-functions/v2/https');
|
|
21
|
+
}
|
|
11
22
|
const VALID_SEVERITIES = new Set(severity_1.SEVERITIES);
|
|
12
23
|
/**
|
|
13
24
|
* What the bare handler throws.
|
|
@@ -106,6 +117,7 @@ function createClientLogHandler(config) {
|
|
|
106
117
|
labels: (0, logger_1.cleanLabels)(labels),
|
|
107
118
|
jsonPayload: { ...jsonPayload, error: processedError },
|
|
108
119
|
attachments: request.data.attachments,
|
|
120
|
+
timestamp: request.data.timestamp,
|
|
109
121
|
});
|
|
110
122
|
}
|
|
111
123
|
catch (err) {
|
|
@@ -123,7 +135,8 @@ function createClientLogHandler(config) {
|
|
|
123
135
|
*/
|
|
124
136
|
function createClientLogFunction(config) {
|
|
125
137
|
const handler = createClientLogHandler(config);
|
|
126
|
-
|
|
138
|
+
const { onCall, HttpsError } = loadFirebaseHttps();
|
|
139
|
+
return onCall({ cors: config.cors ?? true, maxInstances: config.maxInstances ?? 1 }, async (request) => {
|
|
127
140
|
try {
|
|
128
141
|
await handler(request);
|
|
129
142
|
}
|
|
@@ -131,7 +144,7 @@ function createClientLogFunction(config) {
|
|
|
131
144
|
// Convert back at the Firebase boundary, so a callable client sees the
|
|
132
145
|
// same error it always did.
|
|
133
146
|
if (err instanceof ClientLogError)
|
|
134
|
-
throw new
|
|
147
|
+
throw new HttpsError(err.code, err.message);
|
|
135
148
|
throw err;
|
|
136
149
|
}
|
|
137
150
|
});
|