@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 +119 -0
- package/index.d.ts +91 -0
- package/index.js +706 -0
- package/package.json +46 -0
- package/types.d.ts +487 -0
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
|
+
}
|