@metamask-previews/tooling-insight 1.0.1-preview-898fae5
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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/dist/daily-anonymizer.cjs +8 -0
- package/dist/daily-anonymizer.cjs.map +1 -0
- package/dist/daily-anonymizer.d.cts +3 -0
- package/dist/daily-anonymizer.d.cts.map +1 -0
- package/dist/daily-anonymizer.d.mts +3 -0
- package/dist/daily-anonymizer.d.mts.map +1 -0
- package/dist/daily-anonymizer.mjs +6 -0
- package/dist/daily-anonymizer.mjs.map +1 -0
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/allowlist.cjs +159 -0
- package/dist/lib/allowlist.cjs.map +1 -0
- package/dist/lib/allowlist.d.cts +31 -0
- package/dist/lib/allowlist.d.cts.map +1 -0
- package/dist/lib/allowlist.d.mts +31 -0
- package/dist/lib/allowlist.d.mts.map +1 -0
- package/dist/lib/allowlist.mjs +155 -0
- package/dist/lib/allowlist.mjs.map +1 -0
- package/dist/lib/csv.cjs +152 -0
- package/dist/lib/csv.cjs.map +1 -0
- package/dist/lib/csv.d.cts +16 -0
- package/dist/lib/csv.d.cts.map +1 -0
- package/dist/lib/csv.d.mts +16 -0
- package/dist/lib/csv.d.mts.map +1 -0
- package/dist/lib/csv.mjs +149 -0
- package/dist/lib/csv.mjs.map +1 -0
- package/dist/lib/exposition.cjs +102 -0
- package/dist/lib/exposition.cjs.map +1 -0
- package/dist/lib/exposition.d.cts +9 -0
- package/dist/lib/exposition.d.cts.map +1 -0
- package/dist/lib/exposition.d.mts +9 -0
- package/dist/lib/exposition.d.mts.map +1 -0
- package/dist/lib/exposition.mjs +99 -0
- package/dist/lib/exposition.mjs.map +1 -0
- package/dist/lib/fold.cjs +294 -0
- package/dist/lib/fold.cjs.map +1 -0
- package/dist/lib/fold.d.cts +32 -0
- package/dist/lib/fold.d.cts.map +1 -0
- package/dist/lib/fold.d.mts +32 -0
- package/dist/lib/fold.d.mts.map +1 -0
- package/dist/lib/fold.mjs +288 -0
- package/dist/lib/fold.mjs.map +1 -0
- package/dist/lib/log.cjs +116 -0
- package/dist/lib/log.cjs.map +1 -0
- package/dist/lib/log.d.cts +32 -0
- package/dist/lib/log.d.cts.map +1 -0
- package/dist/lib/log.d.mts +32 -0
- package/dist/lib/log.d.mts.map +1 -0
- package/dist/lib/log.mjs +113 -0
- package/dist/lib/log.mjs.map +1 -0
- package/dist/lib/paths.cjs +91 -0
- package/dist/lib/paths.cjs.map +1 -0
- package/dist/lib/paths.d.cts +45 -0
- package/dist/lib/paths.d.cts.map +1 -0
- package/dist/lib/paths.d.mts +45 -0
- package/dist/lib/paths.d.mts.map +1 -0
- package/dist/lib/paths.mjs +82 -0
- package/dist/lib/paths.mjs.map +1 -0
- package/dist/lib/push.cjs +122 -0
- package/dist/lib/push.cjs.map +1 -0
- package/dist/lib/push.d.cts +58 -0
- package/dist/lib/push.d.cts.map +1 -0
- package/dist/lib/push.d.mts +58 -0
- package/dist/lib/push.d.mts.map +1 -0
- package/dist/lib/push.mjs +116 -0
- package/dist/lib/push.mjs.map +1 -0
- package/dist/lib/remoteWrite.cjs +177 -0
- package/dist/lib/remoteWrite.cjs.map +1 -0
- package/dist/lib/remoteWrite.d.cts +24 -0
- package/dist/lib/remoteWrite.d.cts.map +1 -0
- package/dist/lib/remoteWrite.d.mts +24 -0
- package/dist/lib/remoteWrite.d.mts.map +1 -0
- package/dist/lib/remoteWrite.mjs +172 -0
- package/dist/lib/remoteWrite.mjs.map +1 -0
- package/dist/lib/state.cjs +100 -0
- package/dist/lib/state.cjs.map +1 -0
- package/dist/lib/state.d.cts +28 -0
- package/dist/lib/state.d.cts.map +1 -0
- package/dist/lib/state.d.mts +28 -0
- package/dist/lib/state.d.mts.map +1 -0
- package/dist/lib/state.mjs +95 -0
- package/dist/lib/state.mjs.map +1 -0
- package/dist/lib/types.cjs +3 -0
- package/dist/lib/types.cjs.map +1 -0
- package/dist/lib/types.d.cts +82 -0
- package/dist/lib/types.d.cts.map +1 -0
- package/dist/lib/types.d.mts +82 -0
- package/dist/lib/types.d.mts.map +1 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/run.cjs +137 -0
- package/dist/run.cjs.map +1 -0
- package/dist/run.d.cts +20 -0
- package/dist/run.d.cts.map +1 -0
- package/dist/run.d.mts +20 -0
- package/dist/run.d.mts.map +1 -0
- package/dist/run.mjs +134 -0
- package/dist/run.mjs.map +1 -0
- package/package.json +100 -0
package/dist/run.cjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.run = run;
|
|
4
|
+
const allowlist_1 = require("./lib/allowlist.cjs");
|
|
5
|
+
const csv_1 = require("./lib/csv.cjs");
|
|
6
|
+
const exposition_1 = require("./lib/exposition.cjs");
|
|
7
|
+
const fold_1 = require("./lib/fold.cjs");
|
|
8
|
+
const log_1 = require("./lib/log.cjs");
|
|
9
|
+
const paths_1 = require("./lib/paths.cjs");
|
|
10
|
+
const push_1 = require("./lib/push.cjs");
|
|
11
|
+
const remoteWrite_1 = require("./lib/remoteWrite.cjs");
|
|
12
|
+
const state_1 = require("./lib/state.cjs");
|
|
13
|
+
const TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;
|
|
14
|
+
/**
|
|
15
|
+
* Parse the CLI flags from the argument list.
|
|
16
|
+
*
|
|
17
|
+
* @param args - The argument list to parse.
|
|
18
|
+
* @returns The parsed flag values.
|
|
19
|
+
*/
|
|
20
|
+
function parseFlags(args) {
|
|
21
|
+
const repoRootArg = args.find((arg) => arg.startsWith('--repo-root='));
|
|
22
|
+
return {
|
|
23
|
+
dryRun: args.includes('--dry-run'),
|
|
24
|
+
force: args.includes('--force'),
|
|
25
|
+
verbose: args.includes('--verbose'),
|
|
26
|
+
repoRoot: repoRootArg === undefined
|
|
27
|
+
? null
|
|
28
|
+
: repoRootArg.slice('--repo-root='.length),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns true when no prior run exists or the last run was at least 24h ago.
|
|
33
|
+
*
|
|
34
|
+
* @param lastRunIso - ISO-8601 timestamp of the last run, or null.
|
|
35
|
+
* @param nowMs - Current timestamp in milliseconds.
|
|
36
|
+
* @returns True when a new run is due.
|
|
37
|
+
*/
|
|
38
|
+
function isDue(lastRunIso, nowMs) {
|
|
39
|
+
if (lastRunIso === null) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const lastMs = Date.parse(lastRunIso);
|
|
43
|
+
if (Number.isNaN(lastMs)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return nowMs - lastMs >= TWENTY_FOUR_HOURS_MS;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run the daily anonymizer: fold local CSV events into per-day anonymized
|
|
50
|
+
* counters and push them to the Prometheus remote-write endpoint.
|
|
51
|
+
*
|
|
52
|
+
* @param options - Optional overrides for args, repo root, git executor, and fetch.
|
|
53
|
+
*/
|
|
54
|
+
async function run(options = {}) {
|
|
55
|
+
const args = options.args ?? process.argv.slice(2);
|
|
56
|
+
const { dryRun, force, verbose, repoRoot: repoRootFlag } = parseFlags(args);
|
|
57
|
+
const logger = (0, log_1.createLogger)('anonymizer');
|
|
58
|
+
if ((0, paths_1.isCollectionDisabled)()) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const state = (0, state_1.readState)();
|
|
63
|
+
if (!force && !isDue(state.last_run_at, Date.now())) {
|
|
64
|
+
logger.info('skipping — within 24h window', undefined, verbose);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const repoRoot = repoRootFlag ?? options.repoRoot ?? (0, allowlist_1.findRepoRoot)(process.cwd());
|
|
68
|
+
// exactOptionalPropertyTypes: omit optional keys when undefined.
|
|
69
|
+
const allowlist = (0, allowlist_1.readAllowlist)({
|
|
70
|
+
repoRoot,
|
|
71
|
+
...(options.execGit === undefined ? {} : { execGit: options.execGit }),
|
|
72
|
+
});
|
|
73
|
+
const todayUtc = (0, fold_1.todayUtcDay)();
|
|
74
|
+
const events = (0, csv_1.readAllEvents)({
|
|
75
|
+
/**
|
|
76
|
+
* Log malformed CSV rows for diagnostics without interrupting the run.
|
|
77
|
+
*
|
|
78
|
+
* @param line - The raw CSV row text that could not be parsed.
|
|
79
|
+
* @param reason - A description of why the row failed to parse.
|
|
80
|
+
*/
|
|
81
|
+
onMalformedRow: (line, reason) => {
|
|
82
|
+
logger.error('malformed csv row skipped', { line, reason }, verbose);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const samples = (0, fold_1.fold)(events, allowlist, state.instance_uuid, state.last_pushed_day, todayUtc);
|
|
86
|
+
if (dryRun) {
|
|
87
|
+
// Inspect only: show human-readable text exposition; never push or advance state.
|
|
88
|
+
const textBody = (0, exposition_1.buildExpositionBody)(samples);
|
|
89
|
+
logger.info('dry-run body', { sampleCount: samples.length, body: textBody }, verbose);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Claim the 24h window up front so a second near-simultaneous spawn skips.
|
|
93
|
+
(0, state_1.markRunStart)(new Date().toISOString());
|
|
94
|
+
if (samples.length === 0) {
|
|
95
|
+
logger.info('nothing to push', undefined, verbose);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const config = (0, push_1.loadPushConfig)(repoRoot);
|
|
99
|
+
// Encode as snappy-compressed protobuf WriteRequest for Mimir remote_write.
|
|
100
|
+
const body = (0, remoteWrite_1.encodeRemoteWrite)(samples);
|
|
101
|
+
await (0, push_1.pushBatch)(body, {
|
|
102
|
+
url: config.url,
|
|
103
|
+
// Spread optional credentials only when present — required by exactOptionalPropertyTypes.
|
|
104
|
+
...(config.username !== undefined && { username: config.username }),
|
|
105
|
+
...(config.password !== undefined && { password: config.password }),
|
|
106
|
+
dryRun: false,
|
|
107
|
+
...(options.fetchImpl !== undefined && { fetchImpl: options.fetchImpl }),
|
|
108
|
+
logger: {
|
|
109
|
+
/**
|
|
110
|
+
* Forward informational messages to the anonymizer logger with the current verbose flag.
|
|
111
|
+
*
|
|
112
|
+
* @param message - The message to log.
|
|
113
|
+
* @param extra - Optional extra data to attach.
|
|
114
|
+
* @returns void.
|
|
115
|
+
*/
|
|
116
|
+
info: (message, extra) => logger.info(message, extra, verbose),
|
|
117
|
+
/**
|
|
118
|
+
* Forward error messages to the anonymizer logger with the current verbose flag.
|
|
119
|
+
*
|
|
120
|
+
* @param message - The message to log.
|
|
121
|
+
* @param extra - Optional extra data to attach.
|
|
122
|
+
* @returns void.
|
|
123
|
+
*/
|
|
124
|
+
error: (message, extra) => logger.error(message, extra, verbose),
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// Only advance the cursor on a confirmed push; a failed push re-folds next run.
|
|
128
|
+
// maxDay is non-null here: we checked samples.length === 0 above, so the
|
|
129
|
+
// string type assertion is safe.
|
|
130
|
+
(0, state_1.markPushed)((0, fold_1.maxDay)(samples));
|
|
131
|
+
logger.info('publish complete', { latestDay: (0, fold_1.maxDay)(samples), sampleCount: samples.length }, verbose);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
logger.error('anonymizer failed', { error: error instanceof Error ? error.message : String(error) }, verbose);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=run.cjs.map
|
package/dist/run.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.cjs","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":";;AAgFA,kBA+GC;AA/LD,mDAA8D;AAE9D,uCAA0C;AAC1C,qDAAuD;AACvD,yCAAuD;AACvD,uCAAyC;AACzC,2CAAmD;AACnD,yCAAuD;AACvD,uDAAsD;AACtD,2CAAkE;AAElE,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AA0BjD;;;;;GAKG;AACH,SAAS,UAAU,CAAC,IAAc;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;IACvE,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;QAClC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC/B,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;QACnC,QAAQ,EACN,WAAW,KAAK,SAAS;YACvB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,KAAK,CAAC,UAAyB,EAAE,KAAa;IACrD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,GAAG,MAAM,IAAI,oBAAoB,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,GAAG,CAAC,UAAsB,EAAE;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,IAAA,kBAAY,EAAC,YAAY,CAAC,CAAC;IAE1C,IAAI,IAAA,4BAAoB,GAAE,EAAE,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAA,iBAAS,GAAE,CAAC;QAE1B,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GACZ,YAAY,IAAI,OAAO,CAAC,QAAQ,IAAI,IAAA,wBAAY,EAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAClE,iEAAiE;QACjE,MAAM,SAAS,GAAG,IAAA,yBAAa,EAAC;YAC9B,QAAQ;YACR,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;SACvE,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAA,kBAAW,GAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAA,mBAAa,EAAC;YAC3B;;;;;eAKG;YACH,cAAc,EAAE,CAAC,IAAY,EAAE,MAAc,EAAE,EAAE;gBAC/C,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;YACvE,CAAC;SACF,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,WAAI,EAClB,MAAM,EACN,SAAS,EACT,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,eAAe,EACrB,QAAQ,CACT,CAAC;QAEF,IAAI,MAAM,EAAE,CAAC;YACX,kFAAkF;YAClF,MAAM,QAAQ,GAAG,IAAA,gCAAmB,EAAC,OAAO,CAAC,CAAC;YAC9C,MAAM,CAAC,IAAI,CACT,cAAc,EACd,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAC/C,OAAO,CACR,CAAC;YACF,OAAO;QACT,CAAC;QAED,2EAA2E;QAC3E,IAAA,oBAAY,EAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAEvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,qBAAc,EAAC,QAAQ,CAAC,CAAC;QACxC,4EAA4E;QAC5E,MAAM,IAAI,GAAG,IAAA,+BAAiB,EAAC,OAAO,CAAC,CAAC;QACxC,MAAM,IAAA,gBAAS,EAAC,IAAI,EAAE;YACpB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,0FAA0F;YAC1F,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;YACnE,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;YACnE,MAAM,EAAE,KAAK;YACb,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;YACxE,MAAM,EAAE;gBACN;;;;;;mBAMG;gBACH,IAAI,EAAE,CAAC,OAAe,EAAE,KAA+B,EAAE,EAAE,CACzD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;gBACtC;;;;;;mBAMG;gBACH,KAAK,EAAE,CAAC,OAAe,EAAE,KAA+B,EAAE,EAAE,CAC1D,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;aACxC;SACF,CAAC,CAAC;QAEH,gFAAgF;QAChF,yEAAyE;QACzE,iCAAiC;QACjC,IAAA,kBAAU,EAAC,IAAA,aAAM,EAAC,OAAO,CAAW,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,EAAE,SAAS,EAAE,IAAA,aAAM,EAAC,OAAO,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,EAC3D,OAAO,CACR,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CACV,mBAAmB,EACnB,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACjE,OAAO,CACR,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import { findRepoRoot, readAllowlist } from './lib/allowlist';\nimport type { ExecGit } from './lib/allowlist';\nimport { readAllEvents } from './lib/csv';\nimport { buildExpositionBody } from './lib/exposition';\nimport { fold, maxDay, todayUtcDay } from './lib/fold';\nimport { createLogger } from './lib/log';\nimport { isCollectionDisabled } from './lib/paths';\nimport { loadPushConfig, pushBatch } from './lib/push';\nimport { encodeRemoteWrite } from './lib/remoteWrite';\nimport { markPushed, markRunStart, readState } from './lib/state';\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\n\n/** Options for the `run` function. */\nexport type RunOptions = {\n /** CLI argument list; defaults to `process.argv.slice(2)`. */\n args?: string[];\n /** Absolute path to the repo root; defaults to `findRepoRoot(process.cwd())`. */\n repoRoot?: string;\n /** Git executor; defaults to the real `execFileSync` wrapper. */\n execGit?: ExecGit;\n /** Fetch implementation; defaults to the global `fetch`. */\n fetchImpl?: typeof fetch;\n};\n\n/** Parsed CLI flags. */\ntype Flags = {\n /** Whether to run without pushing (inspect only). */\n dryRun: boolean;\n /** Whether to skip the 24h cadence check. */\n force: boolean;\n /** Whether to write log output to stdout. */\n verbose: boolean;\n /** Explicit repo root path from `--repo-root=<path>`; null when absent. */\n repoRoot: string | null;\n};\n\n/**\n * Parse the CLI flags from the argument list.\n *\n * @param args - The argument list to parse.\n * @returns The parsed flag values.\n */\nfunction parseFlags(args: string[]): Flags {\n const repoRootArg = args.find((arg) => arg.startsWith('--repo-root='));\n return {\n dryRun: args.includes('--dry-run'),\n force: args.includes('--force'),\n verbose: args.includes('--verbose'),\n repoRoot:\n repoRootArg === undefined\n ? null\n : repoRootArg.slice('--repo-root='.length),\n };\n}\n\n/**\n * Returns true when no prior run exists or the last run was at least 24h ago.\n *\n * @param lastRunIso - ISO-8601 timestamp of the last run, or null.\n * @param nowMs - Current timestamp in milliseconds.\n * @returns True when a new run is due.\n */\nfunction isDue(lastRunIso: string | null, nowMs: number): boolean {\n if (lastRunIso === null) {\n return true;\n }\n const lastMs = Date.parse(lastRunIso);\n if (Number.isNaN(lastMs)) {\n return true;\n }\n return nowMs - lastMs >= TWENTY_FOUR_HOURS_MS;\n}\n\n/**\n * Run the daily anonymizer: fold local CSV events into per-day anonymized\n * counters and push them to the Prometheus remote-write endpoint.\n *\n * @param options - Optional overrides for args, repo root, git executor, and fetch.\n */\nexport async function run(options: RunOptions = {}): Promise<void> {\n const args = options.args ?? process.argv.slice(2);\n const { dryRun, force, verbose, repoRoot: repoRootFlag } = parseFlags(args);\n const logger = createLogger('anonymizer');\n\n if (isCollectionDisabled()) {\n return;\n }\n\n try {\n const state = readState();\n\n if (!force && !isDue(state.last_run_at, Date.now())) {\n logger.info('skipping — within 24h window', undefined, verbose);\n return;\n }\n\n const repoRoot =\n repoRootFlag ?? options.repoRoot ?? findRepoRoot(process.cwd());\n // exactOptionalPropertyTypes: omit optional keys when undefined.\n const allowlist = readAllowlist({\n repoRoot,\n ...(options.execGit === undefined ? {} : { execGit: options.execGit }),\n });\n const todayUtc = todayUtcDay();\n const events = readAllEvents({\n /**\n * Log malformed CSV rows for diagnostics without interrupting the run.\n *\n * @param line - The raw CSV row text that could not be parsed.\n * @param reason - A description of why the row failed to parse.\n */\n onMalformedRow: (line: string, reason: string) => {\n logger.error('malformed csv row skipped', { line, reason }, verbose);\n },\n });\n const samples = fold(\n events,\n allowlist,\n state.instance_uuid,\n state.last_pushed_day,\n todayUtc,\n );\n\n if (dryRun) {\n // Inspect only: show human-readable text exposition; never push or advance state.\n const textBody = buildExpositionBody(samples);\n logger.info(\n 'dry-run body',\n { sampleCount: samples.length, body: textBody },\n verbose,\n );\n return;\n }\n\n // Claim the 24h window up front so a second near-simultaneous spawn skips.\n markRunStart(new Date().toISOString());\n\n if (samples.length === 0) {\n logger.info('nothing to push', undefined, verbose);\n return;\n }\n\n const config = loadPushConfig(repoRoot);\n // Encode as snappy-compressed protobuf WriteRequest for Mimir remote_write.\n const body = encodeRemoteWrite(samples);\n await pushBatch(body, {\n url: config.url,\n // Spread optional credentials only when present — required by exactOptionalPropertyTypes.\n ...(config.username !== undefined && { username: config.username }),\n ...(config.password !== undefined && { password: config.password }),\n dryRun: false,\n ...(options.fetchImpl !== undefined && { fetchImpl: options.fetchImpl }),\n logger: {\n /**\n * Forward informational messages to the anonymizer logger with the current verbose flag.\n *\n * @param message - The message to log.\n * @param extra - Optional extra data to attach.\n * @returns void.\n */\n info: (message: string, extra?: Record<string, unknown>) =>\n logger.info(message, extra, verbose),\n /**\n * Forward error messages to the anonymizer logger with the current verbose flag.\n *\n * @param message - The message to log.\n * @param extra - Optional extra data to attach.\n * @returns void.\n */\n error: (message: string, extra?: Record<string, unknown>) =>\n logger.error(message, extra, verbose),\n },\n });\n\n // Only advance the cursor on a confirmed push; a failed push re-folds next run.\n // maxDay is non-null here: we checked samples.length === 0 above, so the\n // string type assertion is safe.\n markPushed(maxDay(samples) as string);\n logger.info(\n 'publish complete',\n { latestDay: maxDay(samples), sampleCount: samples.length },\n verbose,\n );\n } catch (error) {\n logger.error(\n 'anonymizer failed',\n { error: error instanceof Error ? error.message : String(error) },\n verbose,\n );\n }\n}\n"]}
|
package/dist/run.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ExecGit } from "./lib/allowlist.cjs";
|
|
2
|
+
/** Options for the `run` function. */
|
|
3
|
+
export type RunOptions = {
|
|
4
|
+
/** CLI argument list; defaults to `process.argv.slice(2)`. */
|
|
5
|
+
args?: string[];
|
|
6
|
+
/** Absolute path to the repo root; defaults to `findRepoRoot(process.cwd())`. */
|
|
7
|
+
repoRoot?: string;
|
|
8
|
+
/** Git executor; defaults to the real `execFileSync` wrapper. */
|
|
9
|
+
execGit?: ExecGit;
|
|
10
|
+
/** Fetch implementation; defaults to the global `fetch`. */
|
|
11
|
+
fetchImpl?: typeof fetch;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Run the daily anonymizer: fold local CSV events into per-day anonymized
|
|
15
|
+
* counters and push them to the Prometheus remote-write endpoint.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Optional overrides for args, repo root, git executor, and fetch.
|
|
18
|
+
*/
|
|
19
|
+
export declare function run(options?: RunOptions): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=run.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.cts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,4BAAwB;AAY/C,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG;IACvB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,CAAC;AAmDF;;;;;GAKG;AACH,wBAAsB,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA+GjE"}
|
package/dist/run.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ExecGit } from "./lib/allowlist.mjs";
|
|
2
|
+
/** Options for the `run` function. */
|
|
3
|
+
export type RunOptions = {
|
|
4
|
+
/** CLI argument list; defaults to `process.argv.slice(2)`. */
|
|
5
|
+
args?: string[];
|
|
6
|
+
/** Absolute path to the repo root; defaults to `findRepoRoot(process.cwd())`. */
|
|
7
|
+
repoRoot?: string;
|
|
8
|
+
/** Git executor; defaults to the real `execFileSync` wrapper. */
|
|
9
|
+
execGit?: ExecGit;
|
|
10
|
+
/** Fetch implementation; defaults to the global `fetch`. */
|
|
11
|
+
fetchImpl?: typeof fetch;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Run the daily anonymizer: fold local CSV events into per-day anonymized
|
|
15
|
+
* counters and push them to the Prometheus remote-write endpoint.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Optional overrides for args, repo root, git executor, and fetch.
|
|
18
|
+
*/
|
|
19
|
+
export declare function run(options?: RunOptions): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=run.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.mts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,4BAAwB;AAY/C,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAAG;IACvB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,CAAC;AAmDF;;;;;GAKG;AACH,wBAAsB,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA+GjE"}
|
package/dist/run.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { findRepoRoot, readAllowlist } from "./lib/allowlist.mjs";
|
|
2
|
+
import { readAllEvents } from "./lib/csv.mjs";
|
|
3
|
+
import { buildExpositionBody } from "./lib/exposition.mjs";
|
|
4
|
+
import { fold, maxDay, todayUtcDay } from "./lib/fold.mjs";
|
|
5
|
+
import { createLogger } from "./lib/log.mjs";
|
|
6
|
+
import { isCollectionDisabled } from "./lib/paths.mjs";
|
|
7
|
+
import { loadPushConfig, pushBatch } from "./lib/push.mjs";
|
|
8
|
+
import { encodeRemoteWrite } from "./lib/remoteWrite.mjs";
|
|
9
|
+
import { markPushed, markRunStart, readState } from "./lib/state.mjs";
|
|
10
|
+
const TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;
|
|
11
|
+
/**
|
|
12
|
+
* Parse the CLI flags from the argument list.
|
|
13
|
+
*
|
|
14
|
+
* @param args - The argument list to parse.
|
|
15
|
+
* @returns The parsed flag values.
|
|
16
|
+
*/
|
|
17
|
+
function parseFlags(args) {
|
|
18
|
+
const repoRootArg = args.find((arg) => arg.startsWith('--repo-root='));
|
|
19
|
+
return {
|
|
20
|
+
dryRun: args.includes('--dry-run'),
|
|
21
|
+
force: args.includes('--force'),
|
|
22
|
+
verbose: args.includes('--verbose'),
|
|
23
|
+
repoRoot: repoRootArg === undefined
|
|
24
|
+
? null
|
|
25
|
+
: repoRootArg.slice('--repo-root='.length),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns true when no prior run exists or the last run was at least 24h ago.
|
|
30
|
+
*
|
|
31
|
+
* @param lastRunIso - ISO-8601 timestamp of the last run, or null.
|
|
32
|
+
* @param nowMs - Current timestamp in milliseconds.
|
|
33
|
+
* @returns True when a new run is due.
|
|
34
|
+
*/
|
|
35
|
+
function isDue(lastRunIso, nowMs) {
|
|
36
|
+
if (lastRunIso === null) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const lastMs = Date.parse(lastRunIso);
|
|
40
|
+
if (Number.isNaN(lastMs)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return nowMs - lastMs >= TWENTY_FOUR_HOURS_MS;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run the daily anonymizer: fold local CSV events into per-day anonymized
|
|
47
|
+
* counters and push them to the Prometheus remote-write endpoint.
|
|
48
|
+
*
|
|
49
|
+
* @param options - Optional overrides for args, repo root, git executor, and fetch.
|
|
50
|
+
*/
|
|
51
|
+
export async function run(options = {}) {
|
|
52
|
+
const args = options.args ?? process.argv.slice(2);
|
|
53
|
+
const { dryRun, force, verbose, repoRoot: repoRootFlag } = parseFlags(args);
|
|
54
|
+
const logger = createLogger('anonymizer');
|
|
55
|
+
if (isCollectionDisabled()) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const state = readState();
|
|
60
|
+
if (!force && !isDue(state.last_run_at, Date.now())) {
|
|
61
|
+
logger.info('skipping — within 24h window', undefined, verbose);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const repoRoot = repoRootFlag ?? options.repoRoot ?? findRepoRoot(process.cwd());
|
|
65
|
+
// exactOptionalPropertyTypes: omit optional keys when undefined.
|
|
66
|
+
const allowlist = readAllowlist({
|
|
67
|
+
repoRoot,
|
|
68
|
+
...(options.execGit === undefined ? {} : { execGit: options.execGit }),
|
|
69
|
+
});
|
|
70
|
+
const todayUtc = todayUtcDay();
|
|
71
|
+
const events = readAllEvents({
|
|
72
|
+
/**
|
|
73
|
+
* Log malformed CSV rows for diagnostics without interrupting the run.
|
|
74
|
+
*
|
|
75
|
+
* @param line - The raw CSV row text that could not be parsed.
|
|
76
|
+
* @param reason - A description of why the row failed to parse.
|
|
77
|
+
*/
|
|
78
|
+
onMalformedRow: (line, reason) => {
|
|
79
|
+
logger.error('malformed csv row skipped', { line, reason }, verbose);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
const samples = fold(events, allowlist, state.instance_uuid, state.last_pushed_day, todayUtc);
|
|
83
|
+
if (dryRun) {
|
|
84
|
+
// Inspect only: show human-readable text exposition; never push or advance state.
|
|
85
|
+
const textBody = buildExpositionBody(samples);
|
|
86
|
+
logger.info('dry-run body', { sampleCount: samples.length, body: textBody }, verbose);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Claim the 24h window up front so a second near-simultaneous spawn skips.
|
|
90
|
+
markRunStart(new Date().toISOString());
|
|
91
|
+
if (samples.length === 0) {
|
|
92
|
+
logger.info('nothing to push', undefined, verbose);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const config = loadPushConfig(repoRoot);
|
|
96
|
+
// Encode as snappy-compressed protobuf WriteRequest for Mimir remote_write.
|
|
97
|
+
const body = encodeRemoteWrite(samples);
|
|
98
|
+
await pushBatch(body, {
|
|
99
|
+
url: config.url,
|
|
100
|
+
// Spread optional credentials only when present — required by exactOptionalPropertyTypes.
|
|
101
|
+
...(config.username !== undefined && { username: config.username }),
|
|
102
|
+
...(config.password !== undefined && { password: config.password }),
|
|
103
|
+
dryRun: false,
|
|
104
|
+
...(options.fetchImpl !== undefined && { fetchImpl: options.fetchImpl }),
|
|
105
|
+
logger: {
|
|
106
|
+
/**
|
|
107
|
+
* Forward informational messages to the anonymizer logger with the current verbose flag.
|
|
108
|
+
*
|
|
109
|
+
* @param message - The message to log.
|
|
110
|
+
* @param extra - Optional extra data to attach.
|
|
111
|
+
* @returns void.
|
|
112
|
+
*/
|
|
113
|
+
info: (message, extra) => logger.info(message, extra, verbose),
|
|
114
|
+
/**
|
|
115
|
+
* Forward error messages to the anonymizer logger with the current verbose flag.
|
|
116
|
+
*
|
|
117
|
+
* @param message - The message to log.
|
|
118
|
+
* @param extra - Optional extra data to attach.
|
|
119
|
+
* @returns void.
|
|
120
|
+
*/
|
|
121
|
+
error: (message, extra) => logger.error(message, extra, verbose),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
// Only advance the cursor on a confirmed push; a failed push re-folds next run.
|
|
125
|
+
// maxDay is non-null here: we checked samples.length === 0 above, so the
|
|
126
|
+
// string type assertion is safe.
|
|
127
|
+
markPushed(maxDay(samples));
|
|
128
|
+
logger.info('publish complete', { latestDay: maxDay(samples), sampleCount: samples.length }, verbose);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
logger.error('anonymizer failed', { error: error instanceof Error ? error.message : String(error) }, verbose);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=run.mjs.map
|
package/dist/run.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.mjs","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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","sourcesContent":["import { findRepoRoot, readAllowlist } from './lib/allowlist';\nimport type { ExecGit } from './lib/allowlist';\nimport { readAllEvents } from './lib/csv';\nimport { buildExpositionBody } from './lib/exposition';\nimport { fold, maxDay, todayUtcDay } from './lib/fold';\nimport { createLogger } from './lib/log';\nimport { isCollectionDisabled } from './lib/paths';\nimport { loadPushConfig, pushBatch } from './lib/push';\nimport { encodeRemoteWrite } from './lib/remoteWrite';\nimport { markPushed, markRunStart, readState } from './lib/state';\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\n\n/** Options for the `run` function. */\nexport type RunOptions = {\n /** CLI argument list; defaults to `process.argv.slice(2)`. */\n args?: string[];\n /** Absolute path to the repo root; defaults to `findRepoRoot(process.cwd())`. */\n repoRoot?: string;\n /** Git executor; defaults to the real `execFileSync` wrapper. */\n execGit?: ExecGit;\n /** Fetch implementation; defaults to the global `fetch`. */\n fetchImpl?: typeof fetch;\n};\n\n/** Parsed CLI flags. */\ntype Flags = {\n /** Whether to run without pushing (inspect only). */\n dryRun: boolean;\n /** Whether to skip the 24h cadence check. */\n force: boolean;\n /** Whether to write log output to stdout. */\n verbose: boolean;\n /** Explicit repo root path from `--repo-root=<path>`; null when absent. */\n repoRoot: string | null;\n};\n\n/**\n * Parse the CLI flags from the argument list.\n *\n * @param args - The argument list to parse.\n * @returns The parsed flag values.\n */\nfunction parseFlags(args: string[]): Flags {\n const repoRootArg = args.find((arg) => arg.startsWith('--repo-root='));\n return {\n dryRun: args.includes('--dry-run'),\n force: args.includes('--force'),\n verbose: args.includes('--verbose'),\n repoRoot:\n repoRootArg === undefined\n ? null\n : repoRootArg.slice('--repo-root='.length),\n };\n}\n\n/**\n * Returns true when no prior run exists or the last run was at least 24h ago.\n *\n * @param lastRunIso - ISO-8601 timestamp of the last run, or null.\n * @param nowMs - Current timestamp in milliseconds.\n * @returns True when a new run is due.\n */\nfunction isDue(lastRunIso: string | null, nowMs: number): boolean {\n if (lastRunIso === null) {\n return true;\n }\n const lastMs = Date.parse(lastRunIso);\n if (Number.isNaN(lastMs)) {\n return true;\n }\n return nowMs - lastMs >= TWENTY_FOUR_HOURS_MS;\n}\n\n/**\n * Run the daily anonymizer: fold local CSV events into per-day anonymized\n * counters and push them to the Prometheus remote-write endpoint.\n *\n * @param options - Optional overrides for args, repo root, git executor, and fetch.\n */\nexport async function run(options: RunOptions = {}): Promise<void> {\n const args = options.args ?? process.argv.slice(2);\n const { dryRun, force, verbose, repoRoot: repoRootFlag } = parseFlags(args);\n const logger = createLogger('anonymizer');\n\n if (isCollectionDisabled()) {\n return;\n }\n\n try {\n const state = readState();\n\n if (!force && !isDue(state.last_run_at, Date.now())) {\n logger.info('skipping — within 24h window', undefined, verbose);\n return;\n }\n\n const repoRoot =\n repoRootFlag ?? options.repoRoot ?? findRepoRoot(process.cwd());\n // exactOptionalPropertyTypes: omit optional keys when undefined.\n const allowlist = readAllowlist({\n repoRoot,\n ...(options.execGit === undefined ? {} : { execGit: options.execGit }),\n });\n const todayUtc = todayUtcDay();\n const events = readAllEvents({\n /**\n * Log malformed CSV rows for diagnostics without interrupting the run.\n *\n * @param line - The raw CSV row text that could not be parsed.\n * @param reason - A description of why the row failed to parse.\n */\n onMalformedRow: (line: string, reason: string) => {\n logger.error('malformed csv row skipped', { line, reason }, verbose);\n },\n });\n const samples = fold(\n events,\n allowlist,\n state.instance_uuid,\n state.last_pushed_day,\n todayUtc,\n );\n\n if (dryRun) {\n // Inspect only: show human-readable text exposition; never push or advance state.\n const textBody = buildExpositionBody(samples);\n logger.info(\n 'dry-run body',\n { sampleCount: samples.length, body: textBody },\n verbose,\n );\n return;\n }\n\n // Claim the 24h window up front so a second near-simultaneous spawn skips.\n markRunStart(new Date().toISOString());\n\n if (samples.length === 0) {\n logger.info('nothing to push', undefined, verbose);\n return;\n }\n\n const config = loadPushConfig(repoRoot);\n // Encode as snappy-compressed protobuf WriteRequest for Mimir remote_write.\n const body = encodeRemoteWrite(samples);\n await pushBatch(body, {\n url: config.url,\n // Spread optional credentials only when present — required by exactOptionalPropertyTypes.\n ...(config.username !== undefined && { username: config.username }),\n ...(config.password !== undefined && { password: config.password }),\n dryRun: false,\n ...(options.fetchImpl !== undefined && { fetchImpl: options.fetchImpl }),\n logger: {\n /**\n * Forward informational messages to the anonymizer logger with the current verbose flag.\n *\n * @param message - The message to log.\n * @param extra - Optional extra data to attach.\n * @returns void.\n */\n info: (message: string, extra?: Record<string, unknown>) =>\n logger.info(message, extra, verbose),\n /**\n * Forward error messages to the anonymizer logger with the current verbose flag.\n *\n * @param message - The message to log.\n * @param extra - Optional extra data to attach.\n * @returns void.\n */\n error: (message: string, extra?: Record<string, unknown>) =>\n logger.error(message, extra, verbose),\n },\n });\n\n // Only advance the cursor on a confirmed push; a failed push re-folds next run.\n // maxDay is non-null here: we checked samples.length === 0 above, so the\n // string type assertion is safe.\n markPushed(maxDay(samples) as string);\n logger.info(\n 'publish complete',\n { latestDay: maxDay(samples), sampleCount: samples.length },\n verbose,\n );\n } catch (error) {\n logger.error(\n 'anonymizer failed',\n { error: error instanceof Error ? error.message : String(error) },\n verbose,\n );\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamask-previews/tooling-insight",
|
|
3
|
+
"version": "1.0.1-preview-898fae5",
|
|
4
|
+
"description": "Daily anonymizer for MetaMask developer tooling usage metrics",
|
|
5
|
+
"homepage": "https://github.com/MetaMask/experimental-tooling-insight#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/MetaMask/experimental-tooling-insight/issues"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/MetaMask/experimental-tooling-insight.git"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"metamask-daily-anonymizer": "./dist/daily-anonymizer.mjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"main": "./dist/index.cjs",
|
|
22
|
+
"module": "./dist/index.mjs",
|
|
23
|
+
"types": "./dist/index.d.cts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./dist/index.d.mts",
|
|
28
|
+
"default": "./dist/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./dist/index.d.cts",
|
|
32
|
+
"default": "./dist/index.cjs"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"registry": "https://registry.npmjs.org/",
|
|
40
|
+
"tag": "alpha"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "ts-bridge --project tsconfig.build.json --clean",
|
|
44
|
+
"lint": "yarn lint:eslint && yarn lint:constraints && yarn lint:misc --check && yarn lint:dependencies --check && yarn lint:changelog",
|
|
45
|
+
"lint:changelog": "auto-changelog validate --formatter oxfmt",
|
|
46
|
+
"lint:constraints": "yarn constraints",
|
|
47
|
+
"lint:dependencies": "depcheck && yarn dedupe --check",
|
|
48
|
+
"lint:dependencies:fix": "depcheck && yarn dedupe",
|
|
49
|
+
"lint:eslint": "eslint .",
|
|
50
|
+
"lint:fix": "yarn lint:eslint --fix && yarn lint:constraints --fix && yarn lint:misc --write && yarn lint:dependencies:fix && yarn lint:changelog --fix",
|
|
51
|
+
"lint:misc": "oxfmt --ignore-path .gitignore",
|
|
52
|
+
"prepack": "./scripts/prepack.sh",
|
|
53
|
+
"test": "vitest && attw --pack",
|
|
54
|
+
"test:watch": "vitest --watch"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@arethetypeswrong/cli": "^0.15.3",
|
|
58
|
+
"@lavamoat/allow-scripts": "^3.0.4",
|
|
59
|
+
"@lavamoat/preinstall-always-fail": "^2.0.0",
|
|
60
|
+
"@metamask/auto-changelog": "^6.1.0",
|
|
61
|
+
"@metamask/eslint-config": "^15.0.0",
|
|
62
|
+
"@metamask/eslint-config-nodejs": "^15.0.0",
|
|
63
|
+
"@metamask/eslint-config-typescript": "^15.0.0",
|
|
64
|
+
"@metamask/eslint-config-vitest": "^15.0.0",
|
|
65
|
+
"@ts-bridge/cli": "^0.6.3",
|
|
66
|
+
"@types/node": "^20",
|
|
67
|
+
"@typescript-eslint/utils": "^8.6.0",
|
|
68
|
+
"@vitest/coverage-istanbul": "^4.1.4",
|
|
69
|
+
"@vitest/eslint-plugin": "^1.1.4",
|
|
70
|
+
"@yarnpkg/types": "^4.0.0-rc.52",
|
|
71
|
+
"depcheck": "^1.4.3",
|
|
72
|
+
"eslint": "^9.11.0",
|
|
73
|
+
"eslint-config-prettier": "^9.1.0",
|
|
74
|
+
"eslint-import-resolver-typescript": "^3.6.3",
|
|
75
|
+
"eslint-plugin-import-x": "^4.3.0",
|
|
76
|
+
"eslint-plugin-jsdoc": "^50.2.4",
|
|
77
|
+
"eslint-plugin-n": "^17.10.3",
|
|
78
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
79
|
+
"eslint-plugin-promise": "^7.1.0",
|
|
80
|
+
"oxfmt": "^0.45.0",
|
|
81
|
+
"ts-node": "^10.7.0",
|
|
82
|
+
"typescript": "~5.7.3",
|
|
83
|
+
"typescript-eslint": "^8.48.1",
|
|
84
|
+
"vite": "^8.0.16",
|
|
85
|
+
"vitest": "^4.1.4"
|
|
86
|
+
},
|
|
87
|
+
"resolutions": {
|
|
88
|
+
"fflate": "0.8.2"
|
|
89
|
+
},
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": "^20 || ^22 || >=24"
|
|
92
|
+
},
|
|
93
|
+
"packageManager": "yarn@4.16.0+sha256.ba05224324578801b9cc98170d64aa50b9a36733b440fb0942306da3fbbdc7d1",
|
|
94
|
+
"lavamoat": {
|
|
95
|
+
"allowScripts": {
|
|
96
|
+
"@lavamoat/preinstall-always-fail": false,
|
|
97
|
+
"eslint-plugin-import-x>unrs-resolver": false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|