standdown 0.1.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/LICENSE +21 -0
- package/POLICIES.md +44 -0
- package/README.md +171 -0
- package/dist/chunk-25A7QG5Z.mjs +274 -0
- package/dist/chunk-25A7QG5Z.mjs.map +1 -0
- package/dist/chunk-SEZFAOUU.mjs +382 -0
- package/dist/chunk-SEZFAOUU.mjs.map +1 -0
- package/dist/chunk-SVN6UWA4.mjs +857 -0
- package/dist/chunk-SVN6UWA4.mjs.map +1 -0
- package/dist/content.cjs +1288 -0
- package/dist/content.cjs.map +1 -0
- package/dist/content.d.cts +61 -0
- package/dist/content.d.ts +61 -0
- package/dist/content.mjs +201 -0
- package/dist/content.mjs.map +1 -0
- package/dist/index.cjs +1375 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.mjs +242 -0
- package/dist/index.mjs.map +1 -0
- package/dist/policies.cjs +620 -0
- package/dist/policies.cjs.map +1 -0
- package/dist/policies.d.cts +1517 -0
- package/dist/policies.d.ts +1517 -0
- package/dist/policies.mjs +606 -0
- package/dist/policies.mjs.map +1 -0
- package/dist/session-C5gH-LaQ.d.ts +21 -0
- package/dist/session-n0px3Agb.d.cts +21 -0
- package/dist/stores-CELX9aHN.d.ts +70 -0
- package/dist/stores-CKCdKDdX.d.cts +70 -0
- package/dist/types-A9OJ7SWj.d.cts +190 -0
- package/dist/types-A9OJ7SWj.d.ts +190 -0
- package/dist/webext.cjs +1733 -0
- package/dist/webext.cjs.map +1 -0
- package/dist/webext.d.cts +110 -0
- package/dist/webext.d.ts +110 -0
- package/dist/webext.mjs +346 -0
- package/dist/webext.mjs.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// src/stores.ts
|
|
2
|
+
var DEFAULT_STATE_KEY = "standdown:state:v1";
|
|
3
|
+
var DEFAULT_SESSION_ID_KEY = "standdown:session-id:v1";
|
|
4
|
+
var ChromeLocalStateStore = class {
|
|
5
|
+
#localStorage;
|
|
6
|
+
#sessionStorage;
|
|
7
|
+
#runtime;
|
|
8
|
+
#key;
|
|
9
|
+
#identityKey;
|
|
10
|
+
#fixedSessionId;
|
|
11
|
+
#now;
|
|
12
|
+
#createSessionId;
|
|
13
|
+
#cachedSessionId;
|
|
14
|
+
constructor(localStorage, opts = {}) {
|
|
15
|
+
this.#localStorage = localStorage;
|
|
16
|
+
this.#sessionStorage = opts.sessionStorage;
|
|
17
|
+
this.#runtime = opts.runtime;
|
|
18
|
+
this.#key = opts.key ?? DEFAULT_STATE_KEY;
|
|
19
|
+
this.#identityKey = opts.identityKey ?? DEFAULT_SESSION_ID_KEY;
|
|
20
|
+
this.#fixedSessionId = opts.sessionId;
|
|
21
|
+
this.#now = opts.now ?? Date.now;
|
|
22
|
+
this.#createSessionId = opts.createSessionId ?? createSessionId;
|
|
23
|
+
}
|
|
24
|
+
async load() {
|
|
25
|
+
const sessionId = await this.#currentSessionId();
|
|
26
|
+
const items = await chromeGet(this.#localStorage, this.#key, this.#runtime);
|
|
27
|
+
const envelope = items[this.#key];
|
|
28
|
+
if (envelope === void 0) {
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
const persisted = parsePersistedState(envelope);
|
|
32
|
+
const state = cloneState(persisted.state);
|
|
33
|
+
if (sessionIdentityMismatch(persisted.sessionId, sessionId)) {
|
|
34
|
+
return dropSessionBoundRecords(state, this.#now());
|
|
35
|
+
}
|
|
36
|
+
return state;
|
|
37
|
+
}
|
|
38
|
+
async save(state) {
|
|
39
|
+
const sessionId = await this.#currentSessionId();
|
|
40
|
+
const envelope = {
|
|
41
|
+
schemaVersion: 1,
|
|
42
|
+
state: cloneState(state)
|
|
43
|
+
};
|
|
44
|
+
if (sessionId !== void 0) {
|
|
45
|
+
envelope.sessionId = sessionId;
|
|
46
|
+
}
|
|
47
|
+
await chromeSet(
|
|
48
|
+
this.#localStorage,
|
|
49
|
+
{
|
|
50
|
+
[this.#key]: envelope
|
|
51
|
+
},
|
|
52
|
+
this.#runtime
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
async #currentSessionId() {
|
|
56
|
+
if (this.#fixedSessionId !== void 0) {
|
|
57
|
+
return this.#fixedSessionId;
|
|
58
|
+
}
|
|
59
|
+
if (this.#cachedSessionId !== void 0) {
|
|
60
|
+
return this.#cachedSessionId;
|
|
61
|
+
}
|
|
62
|
+
if (this.#sessionStorage === void 0) {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
const items = await chromeGet(
|
|
66
|
+
this.#sessionStorage,
|
|
67
|
+
this.#identityKey,
|
|
68
|
+
this.#runtime
|
|
69
|
+
);
|
|
70
|
+
const existing = items[this.#identityKey];
|
|
71
|
+
if (typeof existing === "string" && existing.length > 0) {
|
|
72
|
+
this.#cachedSessionId = existing;
|
|
73
|
+
return existing;
|
|
74
|
+
}
|
|
75
|
+
const next = this.#createSessionId();
|
|
76
|
+
await chromeSet(
|
|
77
|
+
this.#sessionStorage,
|
|
78
|
+
{ [this.#identityKey]: next },
|
|
79
|
+
this.#runtime
|
|
80
|
+
);
|
|
81
|
+
this.#cachedSessionId = next;
|
|
82
|
+
return next;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var SessionStorageStateStore = class {
|
|
86
|
+
#storage;
|
|
87
|
+
#key;
|
|
88
|
+
constructor(storage, opts = {}) {
|
|
89
|
+
this.#storage = storage;
|
|
90
|
+
this.#key = opts.key ?? DEFAULT_STATE_KEY;
|
|
91
|
+
}
|
|
92
|
+
async load() {
|
|
93
|
+
const raw = this.#storage.getItem(this.#key);
|
|
94
|
+
if (raw === null) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
return cloneState(parseState(JSON.parse(raw)));
|
|
98
|
+
}
|
|
99
|
+
async save(state) {
|
|
100
|
+
this.#storage.setItem(this.#key, JSON.stringify(cloneState(state)));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var LocalStorageTtlStateStore = class {
|
|
104
|
+
#localStorage;
|
|
105
|
+
#identityStorage;
|
|
106
|
+
#key;
|
|
107
|
+
#identityKey;
|
|
108
|
+
#ttlMs;
|
|
109
|
+
#fixedSessionId;
|
|
110
|
+
#now;
|
|
111
|
+
#createSessionId;
|
|
112
|
+
#cachedSessionId;
|
|
113
|
+
constructor(localStorage, opts) {
|
|
114
|
+
this.#localStorage = localStorage;
|
|
115
|
+
this.#identityStorage = opts.identityStorage ?? localStorage;
|
|
116
|
+
this.#key = opts.key ?? DEFAULT_STATE_KEY;
|
|
117
|
+
this.#identityKey = opts.identityKey ?? DEFAULT_SESSION_ID_KEY;
|
|
118
|
+
this.#ttlMs = opts.ttlMs;
|
|
119
|
+
this.#fixedSessionId = opts.sessionId;
|
|
120
|
+
this.#now = opts.now ?? Date.now;
|
|
121
|
+
this.#createSessionId = opts.createSessionId ?? createSessionId;
|
|
122
|
+
}
|
|
123
|
+
async load() {
|
|
124
|
+
const sessionId = this.#currentSessionId();
|
|
125
|
+
const raw = this.#localStorage.getItem(this.#key);
|
|
126
|
+
if (raw === null) {
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
const persisted = parsePersistedState(JSON.parse(raw));
|
|
130
|
+
const state = cloneState(persisted.state);
|
|
131
|
+
if (this.#now() - persisted.savedAt >= this.#ttlMs) {
|
|
132
|
+
return {
|
|
133
|
+
sessions: {},
|
|
134
|
+
auditLog: state.auditLog
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (sessionIdentityMismatch(persisted.sessionId, sessionId)) {
|
|
138
|
+
return dropSessionBoundRecords(state, this.#now());
|
|
139
|
+
}
|
|
140
|
+
return state;
|
|
141
|
+
}
|
|
142
|
+
async save(state) {
|
|
143
|
+
const sessionId = this.#currentSessionId();
|
|
144
|
+
this.#localStorage.setItem(
|
|
145
|
+
this.#key,
|
|
146
|
+
JSON.stringify({
|
|
147
|
+
schemaVersion: 1,
|
|
148
|
+
sessionId,
|
|
149
|
+
savedAt: this.#now(),
|
|
150
|
+
state: cloneState(state)
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
#currentSessionId() {
|
|
155
|
+
if (this.#fixedSessionId !== void 0) {
|
|
156
|
+
return this.#fixedSessionId;
|
|
157
|
+
}
|
|
158
|
+
if (this.#cachedSessionId !== void 0) {
|
|
159
|
+
return this.#cachedSessionId;
|
|
160
|
+
}
|
|
161
|
+
const existing = this.#identityStorage.getItem(this.#identityKey);
|
|
162
|
+
if (existing !== null && existing.length > 0) {
|
|
163
|
+
this.#cachedSessionId = existing;
|
|
164
|
+
return existing;
|
|
165
|
+
}
|
|
166
|
+
const next = this.#createSessionId();
|
|
167
|
+
this.#identityStorage.setItem(this.#identityKey, next);
|
|
168
|
+
this.#cachedSessionId = next;
|
|
169
|
+
return next;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
function dropSessionBoundRecords(state, now) {
|
|
173
|
+
const next = cloneState(state);
|
|
174
|
+
for (const [host, record] of Object.entries(next.sessions)) {
|
|
175
|
+
if (record.sessionRule === "session-or-min" && now >= record.startedAt + record.minDurationMs) {
|
|
176
|
+
delete next.sessions[host];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return next;
|
|
180
|
+
}
|
|
181
|
+
function createSessionId() {
|
|
182
|
+
const cryptoApi = globalThis.crypto;
|
|
183
|
+
if (typeof cryptoApi?.randomUUID === "function") {
|
|
184
|
+
return cryptoApi.randomUUID();
|
|
185
|
+
}
|
|
186
|
+
return `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
187
|
+
}
|
|
188
|
+
function parsePersistedState(value) {
|
|
189
|
+
if (!isRecord(value)) {
|
|
190
|
+
throw new Error("invalid persisted state");
|
|
191
|
+
}
|
|
192
|
+
const sessionId = value.sessionId;
|
|
193
|
+
const savedAt = value.savedAt ?? 0;
|
|
194
|
+
const state = value.state;
|
|
195
|
+
if (sessionId !== void 0 && typeof sessionId !== "string" || typeof savedAt !== "number") {
|
|
196
|
+
throw new Error("invalid persisted state envelope");
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
sessionId,
|
|
200
|
+
savedAt,
|
|
201
|
+
state: parseState(state)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function parseState(value) {
|
|
205
|
+
if (!isRecord(value) || !isRecord(value.sessions) || !Array.isArray(value.auditLog)) {
|
|
206
|
+
throw new Error("invalid standdown state");
|
|
207
|
+
}
|
|
208
|
+
const sessions = {};
|
|
209
|
+
for (const [host, record] of Object.entries(value.sessions)) {
|
|
210
|
+
sessions[host] = parseSessionRecord(record);
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
sessions,
|
|
214
|
+
auditLog: value.auditLog.map(parseAuditEntry)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function parseSessionRecord(value) {
|
|
218
|
+
if (!isRecord(value)) {
|
|
219
|
+
throw new Error("invalid session record");
|
|
220
|
+
}
|
|
221
|
+
const {
|
|
222
|
+
advertiserHost,
|
|
223
|
+
policyId,
|
|
224
|
+
startedAt,
|
|
225
|
+
lastActivityAt,
|
|
226
|
+
expiresAt,
|
|
227
|
+
sessionRule,
|
|
228
|
+
minDurationMs,
|
|
229
|
+
inactivityMs,
|
|
230
|
+
behaviors
|
|
231
|
+
} = value;
|
|
232
|
+
if (typeof advertiserHost !== "string" || typeof policyId !== "string" || typeof startedAt !== "number" || typeof lastActivityAt !== "number" || expiresAt !== void 0 && typeof expiresAt !== "number" || sessionRule !== "session-or-min" && sessionRule !== "inactivity-window" || typeof minDurationMs !== "number" || inactivityMs !== void 0 && typeof inactivityMs !== "number" || !Array.isArray(behaviors) || !behaviors.every(isBehavior)) {
|
|
233
|
+
throw new Error("invalid session record");
|
|
234
|
+
}
|
|
235
|
+
const record = {
|
|
236
|
+
advertiserHost,
|
|
237
|
+
policyId,
|
|
238
|
+
startedAt,
|
|
239
|
+
lastActivityAt,
|
|
240
|
+
sessionRule,
|
|
241
|
+
minDurationMs,
|
|
242
|
+
behaviors
|
|
243
|
+
};
|
|
244
|
+
if (expiresAt !== void 0) {
|
|
245
|
+
record.expiresAt = expiresAt;
|
|
246
|
+
}
|
|
247
|
+
if (inactivityMs !== void 0) {
|
|
248
|
+
record.inactivityMs = inactivityMs;
|
|
249
|
+
}
|
|
250
|
+
return record;
|
|
251
|
+
}
|
|
252
|
+
function parseAuditEntry(value) {
|
|
253
|
+
if (!isRecord(value)) {
|
|
254
|
+
throw new Error("invalid audit entry");
|
|
255
|
+
}
|
|
256
|
+
const { time, action, advertiserHost, detection, decision } = value;
|
|
257
|
+
if (typeof time !== "number" || action !== "ingest" && action !== "shouldStandDown" && action !== "recordActivity" && action !== "refresh" || advertiserHost !== void 0 && typeof advertiserHost !== "string") {
|
|
258
|
+
throw new Error("invalid audit entry");
|
|
259
|
+
}
|
|
260
|
+
const entry = { time, action };
|
|
261
|
+
if (advertiserHost !== void 0) {
|
|
262
|
+
entry.advertiserHost = advertiserHost;
|
|
263
|
+
}
|
|
264
|
+
if (detection !== void 0) {
|
|
265
|
+
entry.detection = detection;
|
|
266
|
+
}
|
|
267
|
+
if (decision !== void 0) {
|
|
268
|
+
entry.decision = decision;
|
|
269
|
+
}
|
|
270
|
+
return entry;
|
|
271
|
+
}
|
|
272
|
+
function cloneState(state) {
|
|
273
|
+
return {
|
|
274
|
+
sessions: Object.fromEntries(
|
|
275
|
+
Object.entries(state.sessions).map(([host, record]) => [
|
|
276
|
+
host,
|
|
277
|
+
cloneSessionRecord(record)
|
|
278
|
+
])
|
|
279
|
+
),
|
|
280
|
+
auditLog: state.auditLog.map(cloneAuditEntry)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function cloneSessionRecord(record) {
|
|
284
|
+
const next = {
|
|
285
|
+
advertiserHost: record.advertiserHost,
|
|
286
|
+
policyId: record.policyId,
|
|
287
|
+
startedAt: record.startedAt,
|
|
288
|
+
lastActivityAt: record.lastActivityAt,
|
|
289
|
+
sessionRule: record.sessionRule,
|
|
290
|
+
minDurationMs: record.minDurationMs,
|
|
291
|
+
behaviors: [...record.behaviors]
|
|
292
|
+
};
|
|
293
|
+
if (record.expiresAt !== void 0) {
|
|
294
|
+
next.expiresAt = record.expiresAt;
|
|
295
|
+
}
|
|
296
|
+
if (record.inactivityMs !== void 0) {
|
|
297
|
+
next.inactivityMs = record.inactivityMs;
|
|
298
|
+
}
|
|
299
|
+
return next;
|
|
300
|
+
}
|
|
301
|
+
function cloneAuditEntry(entry) {
|
|
302
|
+
const next = {
|
|
303
|
+
time: entry.time,
|
|
304
|
+
action: entry.action
|
|
305
|
+
};
|
|
306
|
+
if (entry.advertiserHost !== void 0) {
|
|
307
|
+
next.advertiserHost = entry.advertiserHost;
|
|
308
|
+
}
|
|
309
|
+
if (entry.detection !== void 0) {
|
|
310
|
+
next.detection = {
|
|
311
|
+
matched: entry.detection.matched.map((match) => ({ ...match })),
|
|
312
|
+
selfMatch: entry.detection.selfMatch
|
|
313
|
+
};
|
|
314
|
+
if (entry.detection.strongest !== void 0) {
|
|
315
|
+
next.detection.strongest = { ...entry.detection.strongest };
|
|
316
|
+
}
|
|
317
|
+
if (entry.detection.failClosedReason !== void 0) {
|
|
318
|
+
next.detection.failClosedReason = entry.detection.failClosedReason;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (entry.decision !== void 0) {
|
|
322
|
+
next.decision = {
|
|
323
|
+
...entry.decision,
|
|
324
|
+
behaviors: [...entry.decision.behaviors]
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return next;
|
|
328
|
+
}
|
|
329
|
+
function chromeGet(storage, key, runtime) {
|
|
330
|
+
return new Promise((resolve, reject) => {
|
|
331
|
+
try {
|
|
332
|
+
const result = storage.get(key, (items) => {
|
|
333
|
+
const error = runtime?.lastError;
|
|
334
|
+
if (error !== void 0) {
|
|
335
|
+
reject(new Error(error.message ?? "chrome storage error"));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
resolve(items ?? {});
|
|
339
|
+
});
|
|
340
|
+
if (isPromiseLike(result)) {
|
|
341
|
+
result.then(resolve, reject);
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
reject(error);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function chromeSet(storage, items, runtime) {
|
|
349
|
+
return new Promise((resolve, reject) => {
|
|
350
|
+
try {
|
|
351
|
+
const result = storage.set(items, () => {
|
|
352
|
+
const error = runtime?.lastError;
|
|
353
|
+
if (error !== void 0) {
|
|
354
|
+
reject(new Error(error.message ?? "chrome storage error"));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
resolve();
|
|
358
|
+
});
|
|
359
|
+
if (isPromiseLike(result)) {
|
|
360
|
+
result.then(resolve, reject);
|
|
361
|
+
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
reject(error);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function isPromiseLike(value) {
|
|
368
|
+
return isRecord(value) && typeof value.then === "function" && typeof value.catch === "function";
|
|
369
|
+
}
|
|
370
|
+
function isRecord(value) {
|
|
371
|
+
return typeof value === "object" && value !== null;
|
|
372
|
+
}
|
|
373
|
+
function sessionIdentityMismatch(persistedSessionId, currentSessionId) {
|
|
374
|
+
return persistedSessionId !== void 0 && currentSessionId !== void 0 && persistedSessionId !== currentSessionId;
|
|
375
|
+
}
|
|
376
|
+
function isBehavior(value) {
|
|
377
|
+
return value === "suppress-prompts" || value === "no-cookie-write" || value === "no-redirect" || value === "no-background-tracking";
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export { ChromeLocalStateStore, LocalStorageTtlStateStore, SessionStorageStateStore };
|
|
381
|
+
//# sourceMappingURL=chunk-SEZFAOUU.mjs.map
|
|
382
|
+
//# sourceMappingURL=chunk-SEZFAOUU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores.ts"],"names":[],"mappings":"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","file":"chunk-SEZFAOUU.mjs","sourcesContent":["import type {\n AuditEntry,\n Behavior,\n Decision,\n Detection,\n SessionRecord,\n StanddownState,\n StateStore,\n} from './types';\n\nconst DEFAULT_STATE_KEY = 'standdown:state:v1';\nconst DEFAULT_SESSION_ID_KEY = 'standdown:session-id:v1';\n\nexport interface ChromeRuntimeLike {\n lastError?: { message?: string };\n}\n\nexport interface ChromeStorageAreaLike {\n get(\n keys?: string | string[] | Record<string, unknown> | null,\n callback?: (items: Record<string, unknown>) => void,\n ): undefined | Promise<Record<string, unknown>>;\n set(\n items: Record<string, unknown>,\n callback?: () => void,\n ): undefined | Promise<void>;\n remove?(\n keys: string | string[],\n callback?: () => void,\n ): undefined | Promise<void>;\n}\n\nexport interface WebStorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface ChromeLocalStateStoreOptions {\n key?: string;\n identityKey?: string;\n runtime?: ChromeRuntimeLike;\n sessionStorage?: ChromeStorageAreaLike;\n sessionId?: string;\n now?: () => number;\n createSessionId?: () => string;\n}\n\nexport class ChromeLocalStateStore implements StateStore {\n readonly #localStorage: ChromeStorageAreaLike;\n readonly #sessionStorage: ChromeStorageAreaLike | undefined;\n readonly #runtime: ChromeRuntimeLike | undefined;\n readonly #key: string;\n readonly #identityKey: string;\n readonly #fixedSessionId: string | undefined;\n readonly #now: () => number;\n readonly #createSessionId: () => string;\n #cachedSessionId: string | undefined;\n\n constructor(\n localStorage: ChromeStorageAreaLike,\n opts: ChromeLocalStateStoreOptions = {},\n ) {\n this.#localStorage = localStorage;\n this.#sessionStorage = opts.sessionStorage;\n this.#runtime = opts.runtime;\n this.#key = opts.key ?? DEFAULT_STATE_KEY;\n this.#identityKey = opts.identityKey ?? DEFAULT_SESSION_ID_KEY;\n this.#fixedSessionId = opts.sessionId;\n this.#now = opts.now ?? Date.now;\n this.#createSessionId = opts.createSessionId ?? createSessionId;\n }\n\n async load(): Promise<StanddownState | undefined> {\n const sessionId = await this.#currentSessionId();\n const items = await chromeGet(this.#localStorage, this.#key, this.#runtime);\n const envelope = items[this.#key];\n\n if (envelope === undefined) {\n return undefined;\n }\n\n const persisted = parsePersistedState(envelope);\n const state = cloneState(persisted.state);\n\n if (sessionIdentityMismatch(persisted.sessionId, sessionId)) {\n return dropSessionBoundRecords(state, this.#now());\n }\n\n return state;\n }\n\n async save(state: StanddownState): Promise<void> {\n const sessionId = await this.#currentSessionId();\n const envelope: {\n schemaVersion: 1;\n sessionId?: string;\n state: StanddownState;\n } = {\n schemaVersion: 1,\n state: cloneState(state),\n };\n\n if (sessionId !== undefined) {\n envelope.sessionId = sessionId;\n }\n\n await chromeSet(\n this.#localStorage,\n {\n [this.#key]: envelope,\n },\n this.#runtime,\n );\n }\n\n async #currentSessionId(): Promise<string | undefined> {\n if (this.#fixedSessionId !== undefined) {\n return this.#fixedSessionId;\n }\n\n if (this.#cachedSessionId !== undefined) {\n return this.#cachedSessionId;\n }\n\n if (this.#sessionStorage === undefined) {\n return undefined;\n }\n\n const items = await chromeGet(\n this.#sessionStorage,\n this.#identityKey,\n this.#runtime,\n );\n const existing = items[this.#identityKey];\n\n if (typeof existing === 'string' && existing.length > 0) {\n this.#cachedSessionId = existing;\n return existing;\n }\n\n const next = this.#createSessionId();\n await chromeSet(\n this.#sessionStorage,\n { [this.#identityKey]: next },\n this.#runtime,\n );\n this.#cachedSessionId = next;\n return next;\n }\n}\n\nexport interface SessionStorageStateStoreOptions {\n key?: string;\n}\n\nexport class SessionStorageStateStore implements StateStore {\n readonly #storage: WebStorageLike;\n readonly #key: string;\n\n constructor(storage: WebStorageLike, opts: SessionStorageStateStoreOptions = {}) {\n this.#storage = storage;\n this.#key = opts.key ?? DEFAULT_STATE_KEY;\n }\n\n async load(): Promise<StanddownState | undefined> {\n const raw = this.#storage.getItem(this.#key);\n\n if (raw === null) {\n return undefined;\n }\n\n return cloneState(parseState(JSON.parse(raw)));\n }\n\n async save(state: StanddownState): Promise<void> {\n this.#storage.setItem(this.#key, JSON.stringify(cloneState(state)));\n }\n}\n\nexport interface LocalStorageTtlStateStoreOptions {\n key?: string;\n identityKey?: string;\n /**\n * Sliding envelope TTL for persisted session records. Every save refreshes\n * `savedAt`; expiry removes session state only. Audit entries remain\n * available so per-policy decisions can still be inspected. Policy-specific\n * stand-down durations remain enforced by the core state machine.\n */\n ttlMs: number;\n /**\n * Optional shared identity storage. Defaults to `localStorage` so multiple\n * tabs in the same browser session do not invalidate each other's records.\n */\n identityStorage?: WebStorageLike;\n /** @deprecated Local-TTL identity is browser-session scoped, not tab scoped. */\n sessionStorage?: WebStorageLike;\n sessionId?: string;\n now?: () => number;\n createSessionId?: () => string;\n}\n\nexport class LocalStorageTtlStateStore implements StateStore {\n readonly #localStorage: WebStorageLike;\n readonly #identityStorage: WebStorageLike;\n readonly #key: string;\n readonly #identityKey: string;\n readonly #ttlMs: number;\n readonly #fixedSessionId: string | undefined;\n readonly #now: () => number;\n readonly #createSessionId: () => string;\n #cachedSessionId: string | undefined;\n\n constructor(\n localStorage: WebStorageLike,\n opts: LocalStorageTtlStateStoreOptions,\n ) {\n this.#localStorage = localStorage;\n this.#identityStorage = opts.identityStorage ?? localStorage;\n this.#key = opts.key ?? DEFAULT_STATE_KEY;\n this.#identityKey = opts.identityKey ?? DEFAULT_SESSION_ID_KEY;\n this.#ttlMs = opts.ttlMs;\n this.#fixedSessionId = opts.sessionId;\n this.#now = opts.now ?? Date.now;\n this.#createSessionId = opts.createSessionId ?? createSessionId;\n }\n\n async load(): Promise<StanddownState | undefined> {\n const sessionId = this.#currentSessionId();\n const raw = this.#localStorage.getItem(this.#key);\n\n if (raw === null) {\n return undefined;\n }\n\n const persisted = parsePersistedState(JSON.parse(raw));\n const state = cloneState(persisted.state);\n\n if (this.#now() - persisted.savedAt >= this.#ttlMs) {\n return {\n sessions: {},\n auditLog: state.auditLog,\n };\n }\n\n if (sessionIdentityMismatch(persisted.sessionId, sessionId)) {\n return dropSessionBoundRecords(state, this.#now());\n }\n\n return state;\n }\n\n async save(state: StanddownState): Promise<void> {\n const sessionId = this.#currentSessionId();\n this.#localStorage.setItem(\n this.#key,\n JSON.stringify({\n schemaVersion: 1,\n sessionId,\n savedAt: this.#now(),\n state: cloneState(state),\n }),\n );\n }\n\n #currentSessionId(): string {\n if (this.#fixedSessionId !== undefined) {\n return this.#fixedSessionId;\n }\n\n if (this.#cachedSessionId !== undefined) {\n return this.#cachedSessionId;\n }\n\n const existing = this.#identityStorage.getItem(this.#identityKey);\n\n if (existing !== null && existing.length > 0) {\n this.#cachedSessionId = existing;\n return existing;\n }\n\n const next = this.#createSessionId();\n this.#identityStorage.setItem(this.#identityKey, next);\n this.#cachedSessionId = next;\n return next;\n }\n}\n\nexport function dropSessionBoundRecords(\n state: StanddownState,\n now: number,\n): StanddownState {\n const next = cloneState(state);\n\n for (const [host, record] of Object.entries(next.sessions)) {\n if (\n record.sessionRule === 'session-or-min' &&\n now >= record.startedAt + record.minDurationMs\n ) {\n delete next.sessions[host];\n }\n }\n\n return next;\n}\n\nexport function createSessionId(): string {\n const cryptoApi = globalThis.crypto as Crypto | undefined;\n\n if (typeof cryptoApi?.randomUUID === 'function') {\n return cryptoApi.randomUUID();\n }\n\n return `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction parsePersistedState(value: unknown): {\n sessionId: string | undefined;\n savedAt: number;\n state: StanddownState;\n} {\n if (!isRecord(value)) {\n throw new Error('invalid persisted state');\n }\n\n const sessionId = value.sessionId;\n const savedAt = value.savedAt ?? 0;\n const state = value.state;\n\n if (\n (sessionId !== undefined && typeof sessionId !== 'string') ||\n typeof savedAt !== 'number'\n ) {\n throw new Error('invalid persisted state envelope');\n }\n\n return {\n sessionId,\n savedAt,\n state: parseState(state),\n };\n}\n\nfunction parseState(value: unknown): StanddownState {\n if (!isRecord(value) || !isRecord(value.sessions) || !Array.isArray(value.auditLog)) {\n throw new Error('invalid standdown state');\n }\n\n const sessions: Record<string, SessionRecord> = {};\n\n for (const [host, record] of Object.entries(value.sessions)) {\n sessions[host] = parseSessionRecord(record);\n }\n\n return {\n sessions,\n auditLog: value.auditLog.map(parseAuditEntry),\n };\n}\n\nfunction parseSessionRecord(value: unknown): SessionRecord {\n if (!isRecord(value)) {\n throw new Error('invalid session record');\n }\n\n const {\n advertiserHost,\n policyId,\n startedAt,\n lastActivityAt,\n expiresAt,\n sessionRule,\n minDurationMs,\n inactivityMs,\n behaviors,\n } = value;\n\n if (\n typeof advertiserHost !== 'string' ||\n typeof policyId !== 'string' ||\n typeof startedAt !== 'number' ||\n typeof lastActivityAt !== 'number' ||\n (expiresAt !== undefined && typeof expiresAt !== 'number') ||\n (sessionRule !== 'session-or-min' && sessionRule !== 'inactivity-window') ||\n typeof minDurationMs !== 'number' ||\n (inactivityMs !== undefined && typeof inactivityMs !== 'number') ||\n !Array.isArray(behaviors) ||\n !behaviors.every(isBehavior)\n ) {\n throw new Error('invalid session record');\n }\n\n const record: SessionRecord = {\n advertiserHost,\n policyId,\n startedAt,\n lastActivityAt,\n sessionRule,\n minDurationMs,\n behaviors,\n };\n\n if (expiresAt !== undefined) {\n record.expiresAt = expiresAt;\n }\n\n if (inactivityMs !== undefined) {\n record.inactivityMs = inactivityMs;\n }\n\n return record;\n}\n\nfunction parseAuditEntry(value: unknown): AuditEntry {\n if (!isRecord(value)) {\n throw new Error('invalid audit entry');\n }\n\n const { time, action, advertiserHost, detection, decision } = value;\n\n if (\n typeof time !== 'number' ||\n (action !== 'ingest' &&\n action !== 'shouldStandDown' &&\n action !== 'recordActivity' &&\n action !== 'refresh') ||\n (advertiserHost !== undefined && typeof advertiserHost !== 'string')\n ) {\n throw new Error('invalid audit entry');\n }\n\n const entry: AuditEntry = { time, action };\n\n if (advertiserHost !== undefined) {\n entry.advertiserHost = advertiserHost;\n }\n\n if (detection !== undefined) {\n entry.detection = detection as Detection;\n }\n\n if (decision !== undefined) {\n entry.decision = decision as Decision;\n }\n\n return entry;\n}\n\nfunction cloneState(state: StanddownState): StanddownState {\n return {\n sessions: Object.fromEntries(\n Object.entries(state.sessions).map(([host, record]) => [\n host,\n cloneSessionRecord(record),\n ]),\n ),\n auditLog: state.auditLog.map(cloneAuditEntry),\n };\n}\n\nfunction cloneSessionRecord(record: SessionRecord): SessionRecord {\n const next: SessionRecord = {\n advertiserHost: record.advertiserHost,\n policyId: record.policyId,\n startedAt: record.startedAt,\n lastActivityAt: record.lastActivityAt,\n sessionRule: record.sessionRule,\n minDurationMs: record.minDurationMs,\n behaviors: [...record.behaviors],\n };\n\n if (record.expiresAt !== undefined) {\n next.expiresAt = record.expiresAt;\n }\n\n if (record.inactivityMs !== undefined) {\n next.inactivityMs = record.inactivityMs;\n }\n\n return next;\n}\n\nfunction cloneAuditEntry(entry: AuditEntry): AuditEntry {\n const next: AuditEntry = {\n time: entry.time,\n action: entry.action,\n };\n\n if (entry.advertiserHost !== undefined) {\n next.advertiserHost = entry.advertiserHost;\n }\n\n if (entry.detection !== undefined) {\n next.detection = {\n matched: entry.detection.matched.map((match) => ({ ...match })),\n selfMatch: entry.detection.selfMatch,\n };\n\n if (entry.detection.strongest !== undefined) {\n next.detection.strongest = { ...entry.detection.strongest };\n }\n\n if (entry.detection.failClosedReason !== undefined) {\n next.detection.failClosedReason = entry.detection.failClosedReason;\n }\n }\n\n if (entry.decision !== undefined) {\n next.decision = {\n ...entry.decision,\n behaviors: [...entry.decision.behaviors],\n };\n }\n\n return next;\n}\n\nfunction chromeGet(\n storage: ChromeStorageAreaLike,\n key: string,\n runtime: ChromeRuntimeLike | undefined,\n): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n try {\n const result = storage.get(key, (items) => {\n const error = runtime?.lastError;\n\n if (error !== undefined) {\n reject(new Error(error.message ?? 'chrome storage error'));\n return;\n }\n\n resolve(items ?? {});\n });\n\n if (isPromiseLike(result)) {\n result.then(resolve, reject);\n }\n } catch (error) {\n reject(error);\n }\n });\n}\n\nfunction chromeSet(\n storage: ChromeStorageAreaLike,\n items: Record<string, unknown>,\n runtime: ChromeRuntimeLike | undefined,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const result = storage.set(items, () => {\n const error = runtime?.lastError;\n\n if (error !== undefined) {\n reject(new Error(error.message ?? 'chrome storage error'));\n return;\n }\n\n resolve();\n });\n\n if (isPromiseLike(result)) {\n result.then(resolve, reject);\n }\n } catch (error) {\n reject(error);\n }\n });\n}\n\nfunction isPromiseLike<T>(value: unknown): value is Promise<T> {\n return (\n isRecord(value) &&\n typeof value.then === 'function' &&\n typeof value.catch === 'function'\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction sessionIdentityMismatch(\n persistedSessionId: string | undefined,\n currentSessionId: string | undefined,\n): boolean {\n return (\n persistedSessionId !== undefined &&\n currentSessionId !== undefined &&\n persistedSessionId !== currentSessionId\n );\n}\n\nfunction isBehavior(value: unknown): value is Behavior {\n return (\n value === 'suppress-prompts' ||\n value === 'no-cookie-write' ||\n value === 'no-redirect' ||\n value === 'no-background-tracking'\n );\n}\n"]}
|