@axiapps/axilog 0.1.1

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 ADDED
@@ -0,0 +1,119 @@
1
+ # @axiapps/axilog
2
+
3
+ Node bindings ([napi-rs](https://napi.rs)) for axilog's Rust GW2 arcdps WvW
4
+ parsing core. Every export is a thin wrapper around the same
5
+ decode → resolve → analyze → build_report pipeline the CLI
6
+ (`axilog-cli`) drives — see `src/lib.rs`'s module doc for the exact FFI
7
+ contract (no Rust panic ever crosses the boundary; every fallible step
8
+ becomes a `napi::Error` carrying the underlying Rust error text).
9
+
10
+ ## Usage
11
+
12
+ ```js
13
+ // CommonJS
14
+ const { parseFile, parseBuffer, parseFileEi, anonymizeFile } = require('@axiapps/axilog')
15
+ ```
16
+
17
+ ```ts
18
+ // ESM / TypeScript
19
+ import { parseFile, parseBuffer, parseFileEi, anonymizeFile } from '@axiapps/axilog'
20
+ import type { Report } from '@axiapps/axilog/types'
21
+ ```
22
+
23
+ ```js
24
+ // Native report (axilog_schema::Report — schema_version, encounter,
25
+ // players[], enemies[], timeline, warnings), typed as `Report` (see
26
+ // "Types" below).
27
+ const report = parseFile('./fight.zevtc')
28
+ console.log(report.schema_version, report.players.length)
29
+
30
+ // Same pipeline, from an already-read Buffer.
31
+ const fs = require('fs')
32
+ const reportFromBuffer = parseBuffer(fs.readFileSync('./fight.zevtc'))
33
+
34
+ // Elite Insights-compatibility JSON (axilog_ei::to_ei_json) — the shape
35
+ // axibridge-style consumers read (players[].account, dpsAll[0].damage,
36
+ // support[0].condiCleanse, buffUptimes[], targets[].enemyPlayer,
37
+ // wvWMapData.{red,blue,green}TeamID). Untyped (`any`) — see "Types" below.
38
+ const ei = parseFileEi('./fight.zevtc')
39
+
40
+ // Rewrite every player's character/account name in a .zevtc to a
41
+ // deterministic Anon<N> placeholder (does not mutate metrics — safe for
42
+ // producing PII-safe fixtures). Returns the number of player agents
43
+ // rewritten.
44
+ const rewritten = anonymizeFile('./fight.zevtc', './fight.anon.zevtc')
45
+ ```
46
+
47
+ All four functions throw a plain `Error` (never a raw Rust panic) on
48
+ failure — e.g. a missing file, a corrupt/unsupported `.evtc`.
49
+
50
+ ## Build
51
+
52
+ ```sh
53
+ npm install
54
+ npm run build # napi build --platform --release, then patches index.d.ts (see "Types")
55
+ npm run build:debug # same, unoptimized (faster iteration)
56
+ npm test # node --test __test__/*.test.mjs
57
+ ```
58
+
59
+ `npm run build`/`npm run build:debug` compile the Rust crate
60
+ (`crates/axilog-node`) to a platform-specific `.node` addon (e.g.
61
+ `axilog.linux-x64-gnu.node`) and regenerate `index.js`/`index.d.ts` via
62
+ `@napi-rs/cli`.
63
+
64
+ ## Tests
65
+
66
+ `__test__/sdk.test.mjs` (`node --test`, Node >= 18) covers, against the
67
+ committed PII-safe fixture `fixtures/wvw-small.anon.zevtc` (repo-root
68
+ relative, resolved from the test file's own location so it works from any
69
+ cwd):
70
+
71
+ - `parseFile`: `schema_version`, player count, squad damage total, one
72
+ boon uptime value, and the four support-stat squad sums — all pinned to
73
+ this fixture's exact calibrated values (see
74
+ `crates/axilog-core/tests/golden.rs`/`support_golden.rs` for how those
75
+ numbers were derived against a real dps.report EI export).
76
+ - `parseBuffer` deep-equals `parseFile`.
77
+ - `parseFileEi`: the specific keys axibridge-style consumers read
78
+ (`players[].account`, `dpsAll[0].damage`, `support[0].condiCleanse`,
79
+ non-empty `buffUptimes`, `targets[].enemyPlayer` booleans,
80
+ `wvWMapData`'s three team ids).
81
+ - `anonymizeFile` round-trip: writes to a tmpdir, re-parses, checks player
82
+ count/metrics are unchanged (anonymization only rewrites names).
83
+ - Missing-file error path: throws a real `Error` with a non-empty message.
84
+ - **Dual-path parity**: builds nothing itself, but shells out to the
85
+ already-built CLI (`cargo build -p axilog-cli`; run that first if
86
+ `target/debug/axilog` is missing) and asserts
87
+ `axilog parse <fixture> --format json` parsed, deep-equals `parseFile`'s
88
+ return value — with a small first-N-differing-paths diff helper so a
89
+ future divergence prints something readable instead of two giant JSON
90
+ blobs.
91
+
92
+ ## Types
93
+
94
+ `index.d.ts` is generated by `@napi-rs/cli` from `src/lib.rs`'s exported
95
+ function signatures. Because every export returns a plain
96
+ `serde_json::Value` (not a `#[napi(object)]`-derived struct — see
97
+ `src/lib.rs`'s module doc for why), napi's typegen has no visibility into
98
+ the actual JSON shape and can only emit `any` for `parseFile`/
99
+ `parseBuffer`'s return type.
100
+
101
+ To close that gap without hand-editing a generated file (which `napi
102
+ build` would silently overwrite), this crate adds:
103
+
104
+ - **`types.d.ts`** — a hand-maintained transcription of
105
+ `axilog_schema::Report` (`crates/axilog-schema/src/lib.rs`, the serde
106
+ struct definitions are the source of truth — keep this file in sync with
107
+ that one if the schema changes).
108
+ - **`scripts/patch-dts.mjs`** — a small idempotent postbuild step (wired
109
+ into both `npm run build` and `npm run build:debug`) that patches the
110
+ freshly-regenerated `index.d.ts` to `import type { Report } from
111
+ './types'` and retypes `parseFile`/`parseBuffer`'s return type from
112
+ `any` to `Report`. Because it runs as part of the build script itself
113
+ (not a one-off manual edit), the reference survives every future `napi
114
+ build` regeneration instead of needing to be reapplied by hand.
115
+
116
+ `parseFileEi`'s return value is a materially different, larger EI-JSON
117
+ shape (not a serialized `Report`) and is intentionally left `any` — typing
118
+ it faithfully is out of scope for this task (see `crates/axilog-ei/src/
119
+ lib.rs`'s doc comments for what fields it does/doesn't carry).
package/index.d.ts ADDED
@@ -0,0 +1,91 @@
1
+ /* auto-generated by NAPI-RS */
2
+ import type { Report } from './types'
3
+ /* eslint-disable */
4
+ /**
5
+ * Rewrites every player's character/account name in the `.zevtc` at
6
+ * `in_path` to a deterministic `Anon<N>` placeholder and writes the
7
+ * result to `out_path` (same transform as `axilog anonymize` / the core
8
+ * `anonymize_raw_evtc` function -- reused here, not duplicated). Returns
9
+ * the number of player agents rewritten.
10
+ */
11
+ export declare function anonymizeFile(inPath: string, outPath: string): number
12
+
13
+ /**
14
+ * Parses an already-read `.evtc`/`.zevtc` buffer and returns the native
15
+ * `Report` as a plain JS object. `opts.replay` (M9, Task 2) opts into
16
+ * embedding the native combat-replay block; `opts.skill_damage` (M12,
17
+ * Task 1) opts into embedding the native per-skill damage distribution
18
+ * block; `opts.missiles` (final-review fix wave) opts into embedding the
19
+ * native top-level missile analytics block.
20
+ */
21
+ export declare function parseBuffer(buf: Buffer, opts?: ParseOptions | undefined | null): Report
22
+
23
+ /**
24
+ * Parses a `.evtc`/`.zevtc` file at `path` and returns the native
25
+ * `Report` as a plain JS object (see module docs for the field-name
26
+ * behavior). `opts.replay` (M9, Task 2) opts into embedding the native
27
+ * combat-replay block; omitted entirely for back-compat with every
28
+ * existing zero-arg call site.
29
+ */
30
+ export declare function parseFile(path: string, opts?: ParseOptions | undefined | null): Report
31
+
32
+ /**
33
+ * Parses a `.evtc`/`.zevtc` file at `path` and returns the Elite
34
+ * Insights-compatibility JSON (`axilog_ei::to_ei_json`) as a plain JS
35
+ * object. `opts` (final-review fix wave) accepts the same `ParseOptions`
36
+ * shape `parseFile`/`parseBuffer` do -- `opts.skill_damage`/
37
+ * `opts.timeseries` are what actually let `totalDamageDist`/`damage1S`/
38
+ * `dpsTargets`/etc (M12, Task 3's ei-json mapping) surface in the
39
+ * returned JSON, since `axilog_ei::to_ei_json` reads them straight off
40
+ * the native `Report` this function builds internally; previously this
41
+ * function always built that `Report` with both flags forced `false`,
42
+ * silently discarding any M12 detail regardless of what a caller wanted
43
+ * (axibridge consumes ei-json exclusively through this function). `opts.
44
+ * replay`/`opts.missiles` are accepted for parity with `parseFile` but
45
+ * have no effect on the output -- EI's JSON shape has no comparable field
46
+ * for either (see `axilog_ei::to_ei_json`'s module doc). Omitting `opts`
47
+ * entirely keeps every existing zero-arg call site's behavior unchanged.
48
+ */
49
+ export declare function parseFileEi(path: string, opts?: ParseOptions | undefined | null): any
50
+
51
+ /**
52
+ * Optional per-call parse settings (M9, Task 2). `replay: true` opts into
53
+ * computing and embedding the native combat-replay block (`ReplayOut`) in
54
+ * the returned `Report`; omitted (or `false`, or the argument itself
55
+ * omitted entirely -- napi treats a trailing `Option<T>` parameter as
56
+ * optional in the generated TypeScript signature) keeps the existing
57
+ * zero-arg call shape's behavior unchanged (no `replay` key in the
58
+ * output, matching `Report.replay`'s serde skip-when-absent).
59
+ * `skill_damage: true` (M12, Task 1) opts into embedding the native
60
+ * per-skill damage distribution block (`SkillDamageOut`) on every
61
+ * `players[]` entry -- see `axilog_schema::Report::players`'s
62
+ * `PlayerOut::skill_damage` doc comment for why this defaults to opt-in
63
+ * (measured +249% JSON size on the committed fixture when always-on).
64
+ * `timeseries: true` (M12, Task 2) opts into embedding the native
65
+ * per-player per-second series block (`PlayerPerSecondOut`) AND the
66
+ * per-enemy `dps_targets` summary on every `players[]` entry -- see
67
+ * `axilog_schema::Report::players`'s `PlayerOut::per_second`/`PlayerOut::
68
+ * dps_targets` doc comments (measured +147.7%/+36.4% JSON size
69
+ * respectively on the committed fixture when always-on -- `dps_targets`
70
+ * is NOT small on a real WvW log with many enemies, so both stay behind
71
+ * this one flag).
72
+ * `missiles: true` (final-review fix wave) opts into embedding the
73
+ * native top-level missile (projectile) analytics block
74
+ * (`Report::missiles`), mirroring the CLI's `--missiles` flag -- see
75
+ * `axilog_core::analysis::missiles`'s module doc for exactly what it
76
+ * contains. Omitted (or `false`) keeps `Report.missiles` absent, matching
77
+ * its serde skip-when-`None`.
78
+ * `rotation: true` (M14, Task 1) opts into embedding the native
79
+ * per-player rotation (cast tracking) block (`SkillRotationOut[]`) on
80
+ * every `players[]` entry -- see `axilog_schema::Report::players`'s
81
+ * `PlayerOut::rotation` doc comment for why this defaults to opt-in
82
+ * (well past the ~30% size-discipline guideline on the committed fixture
83
+ * when always-on).
84
+ */
85
+ export interface ParseOptions {
86
+ replay?: boolean
87
+ skillDamage?: boolean
88
+ timeseries?: boolean
89
+ missiles?: boolean
90
+ rotation?: boolean
91
+ }