@vinikjkkj/wa-wam 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/README.md +359 -0
- package/index.d.ts +30177 -0
- package/index.js +28358 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# @vinikjkkj/wa-wam
|
|
2
|
+
|
|
3
|
+
WhatsApp Web WAM (analytics/metrics) event schemas — event IDs, field IDs +
|
|
4
|
+
types + enum refs, global session attributes, private-stats bucket IDs, and
|
|
5
|
+
resolved enum value sets. Everything is daily-extracted directly from the
|
|
6
|
+
minified `WAWebWamCodegenUtils.defineEvents(...)` / `defineGlobal(...)` calls
|
|
7
|
+
in WA Web bundles.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i @vinikjkkj/wa-wam
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
WA_WAM_EVENTS,
|
|
16
|
+
WA_WAM_GLOBALS,
|
|
17
|
+
WA_WAM_ENUMS,
|
|
18
|
+
WA_WAM_PRIVATE_STATS_IDS,
|
|
19
|
+
WA_WAM_RESERVED_GLOBALS, // synthetic batch-level IDs (commitTime, eventSequenceNumber, psIdValue)
|
|
20
|
+
WA_WAM_CHANNELS,
|
|
21
|
+
WA_WAM_CHANNEL_WIRE_CODES, // { regular: 0, realtime: 1, private: 2 } — channel byte in batch header
|
|
22
|
+
WA_WAM_PROTOCOL_VERSION, // 2nd byte of every batch header (currently 5)
|
|
23
|
+
WA_WAM_WIRE_FORMAT, // marker bytes + value-encoding bits for TLV
|
|
24
|
+
WA_WAM_BUFFER_CONSTANTS // flush/upload limits (maxBufferSize, etc.)
|
|
25
|
+
} from '@vinikjkkj/wa-wam'
|
|
26
|
+
import type {
|
|
27
|
+
WaWamEventName,
|
|
28
|
+
WaWamEventArgs,
|
|
29
|
+
WaWamGlobalName,
|
|
30
|
+
WaWamEnumName,
|
|
31
|
+
WaWamField,
|
|
32
|
+
WaWamChannel,
|
|
33
|
+
WaWamReservedGlobal
|
|
34
|
+
} from '@vinikjkkj/wa-wam'
|
|
35
|
+
|
|
36
|
+
WA_WAM_EVENTS.UiAction
|
|
37
|
+
// → {
|
|
38
|
+
// id: 472,
|
|
39
|
+
// falcoName: 'wam_ui_action',
|
|
40
|
+
// channel: 'regular',
|
|
41
|
+
// privateStatsIdInt: null,
|
|
42
|
+
// weight: { default: 1, gkx26259: 100, gkx26258: 5000 },
|
|
43
|
+
// requiredFields: [],
|
|
44
|
+
// fields: {
|
|
45
|
+
// uiActionType: { id: 1, type: 'enum', enum: 'UI_ACTION_TYPE', falcoName: 'ui_action_type' },
|
|
46
|
+
// uiActionPreloaded: { id: 2, type: 'boolean', falcoName: 'ui_action_preloaded' },
|
|
47
|
+
// uiActionT: { id: 3, type: 'timer', falcoName: 'ui_action_t' },
|
|
48
|
+
// sizeBucket: { id: 4, type: 'enum', enum: 'SIZE_BUCKET', falcoName: 'size_bucket' },
|
|
49
|
+
// …23 more
|
|
50
|
+
// }
|
|
51
|
+
// }
|
|
52
|
+
|
|
53
|
+
WA_WAM_ENUMS.UI_ACTION_TYPE
|
|
54
|
+
// → {
|
|
55
|
+
// module: 'WAWebWamEnumUiActionType',
|
|
56
|
+
// export: 'UI_ACTION_TYPE',
|
|
57
|
+
// values: { OTHER: 1, APP_OPEN: 2, CHAT_OPEN: 3, IMAGE_OPEN: 4, … }
|
|
58
|
+
// }
|
|
59
|
+
|
|
60
|
+
WA_WAM_GLOBALS.platform
|
|
61
|
+
// → {
|
|
62
|
+
// id: 11,
|
|
63
|
+
// type: 'enum',
|
|
64
|
+
// enum: 'PLATFORM_TYPE',
|
|
65
|
+
// channels: ['regular', 'private'],
|
|
66
|
+
// falcoName: 'platform'
|
|
67
|
+
// }
|
|
68
|
+
|
|
69
|
+
WA_WAM_PRIVATE_STATS_IDS
|
|
70
|
+
// → [
|
|
71
|
+
// { key: 'DefaultPsId', keyHashInt: 113760892, rotationPeriodDays: -1 },
|
|
72
|
+
// { key: 'GroupExitExperienceId', keyHashInt: 152546501, rotationPeriodDays: 30 },
|
|
73
|
+
// { key: 'IdTtlDaily', keyHashInt: 248614979, rotationPeriodDays: 1 },
|
|
74
|
+
// …5 more
|
|
75
|
+
// ]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Typed payload builder
|
|
79
|
+
|
|
80
|
+
Each event's `fields` tuple is a discriminated union, so `type` narrows the
|
|
81
|
+
shape and lets `WaWamEventArgs<K>` synthesise the typed payload — enum fields
|
|
82
|
+
become the string-literal union of their enum's value keys (so consumers pass
|
|
83
|
+
`'CHAT_OPEN'` instead of the magic integer `3`), primitives map to their
|
|
84
|
+
natural JS types:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import type { WaWamEventArgs, WaWamEventName } from '@vinikjkkj/wa-wam'
|
|
88
|
+
|
|
89
|
+
type UiActionPayload = WaWamEventArgs<'UiAction'>
|
|
90
|
+
// → {
|
|
91
|
+
// readonly uiActionType?: 'OTHER' | 'APP_OPEN' | 'CHAT_OPEN' | …
|
|
92
|
+
// readonly uiActionPreloaded?: boolean
|
|
93
|
+
// readonly uiActionT?: number // ms since startMarker
|
|
94
|
+
// readonly sizeBucket?: 'SMALL' | 'MEDIUM' | 'LARGE' | …
|
|
95
|
+
// readonly uiActionType?: 'OTHER' | 'APP_OPEN' | 'CHAT_OPEN' | …
|
|
96
|
+
// …
|
|
97
|
+
// }
|
|
98
|
+
|
|
99
|
+
function commitWam<K extends WaWamEventName>(name: K, payload: WaWamEventArgs<K>) {
|
|
100
|
+
// …your encoder; convert enum keys to numeric values via
|
|
101
|
+
// WA_WAM_ENUMS[event.fields[fieldName].enum].values[stringKey].
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
commitWam('UiAction', { uiActionType: 'CHAT_OPEN', uiActionT: 142 }) // ✓
|
|
105
|
+
commitWam('UiAction', { uiActionType: 'NONEXISTENT' }) // ✗ caught at compile time
|
|
106
|
+
commitWam('UiAction', { uiActionTypo: 'CHAT_OPEN' }) // ✗ caught at compile time
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## What's in here
|
|
110
|
+
|
|
111
|
+
WAM (sometimes "WA Logger" / "Falco") is WA Web's client-side analytics
|
|
112
|
+
pipeline. Each mutation, action, latency, error, or daily snapshot the client
|
|
113
|
+
wants to report is registered through one call:
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
// from WAWebUiActionWamEvent
|
|
117
|
+
WAWebWamCodegenUtils.defineEvents({
|
|
118
|
+
UiAction: [472, {
|
|
119
|
+
uiActionType: [1, WAWebWamEnumUiActionType.UI_ACTION_TYPE],
|
|
120
|
+
uiActionPreloaded: [2, TYPES.BOOLEAN],
|
|
121
|
+
uiActionT: [3, TYPES.TIMER],
|
|
122
|
+
// …
|
|
123
|
+
}, [1, 100, 5000], 'regular']
|
|
124
|
+
}, { UiAction: [] })
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Each entry boils down to:
|
|
128
|
+
|
|
129
|
+
- an **event id** (`472`) — what the server keys batched payloads by
|
|
130
|
+
- an **event name** (`UiAction`) — the camelCase JS handle, also reduces to
|
|
131
|
+
the snake-case **falco name** (`wam_ui_action`) used in shadow-logging
|
|
132
|
+
- a **field map** — each field has its own **field id** (`1`), JS type
|
|
133
|
+
(`boolean`/`integer`/`number`/`string`/`timer`) or a **protobuf-style enum
|
|
134
|
+
reference**, and a **falco field name** (snake-case)
|
|
135
|
+
- a **weight tuple** — three sampling weights selected at commit time based
|
|
136
|
+
on gkx 26259/26258 (the runtime does `Math.random() * weight > 1`); higher
|
|
137
|
+
= more aggressive sampling
|
|
138
|
+
- a **channel** — `regular` | `private` | `realtime`. `private` events are
|
|
139
|
+
routed through a rotating pseudo-anonymous bucket (see
|
|
140
|
+
`WA_WAM_PRIVATE_STATS_IDS`) and a separate upload backend
|
|
141
|
+
- a **privateStatsIdInt** (only on `private` events) — the `keyHashInt` of
|
|
142
|
+
the bucket this event's payload belongs to
|
|
143
|
+
|
|
144
|
+
`WAWebWamGlobals.defineGlobal({...})` registers attributes attached to every
|
|
145
|
+
batch — platform, app version, network state, etc. — under their own ids
|
|
146
|
+
(`WA_WAM_GLOBALS`).
|
|
147
|
+
|
|
148
|
+
This package gives you the static metadata for all **423 events** + **46
|
|
149
|
+
globals** + **851 enums** + **8 privateStatsIds** + **1 synthetic `none`
|
|
150
|
+
bucket** + **3 reserved batch-level IDs** (commitTime / eventSequenceNumber /
|
|
151
|
+
psIdValue), so you can build wire-level encoders/decoders or replay metrics
|
|
152
|
+
without manually transcribing the client's registry.
|
|
153
|
+
|
|
154
|
+
## What's published
|
|
155
|
+
|
|
156
|
+
| File | Format | Use case |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `index.js` | CommonJS | Runtime `WA_WAM_EVENTS` / `WA_WAM_GLOBALS` / `WA_WAM_ENUMS` / `WA_WAM_PRIVATE_STATS_IDS` / `WA_WAM_RESERVED_GLOBALS` / `WA_WAM_CHANNELS` / `WA_WAM_CHANNEL_WIRE_CODES` / `WA_WAM_PROTOCOL_VERSION` / `WA_WAM_WIRE_FORMAT` / `WA_WAM_BUFFER_CONSTANTS` frozen tables |
|
|
159
|
+
| `index.d.ts` | TS declarations | Per-event/global/enum literal-typed schemas + the umbrella maps + the `WaWamEventArgs<K>` payload helper |
|
|
160
|
+
|
|
161
|
+
A raw IR file (`index.json`) is also produced — see
|
|
162
|
+
[`packages/wam/index.json`](https://github.com/vinikjkkj/wa-spec/blob/master/packages/wam/index.json)
|
|
163
|
+
for non-TS consumers (diff tools, codegen, other languages).
|
|
164
|
+
|
|
165
|
+
`index.json` shape:
|
|
166
|
+
|
|
167
|
+
```jsonc
|
|
168
|
+
{
|
|
169
|
+
"waVersion": "2.3000.xxxxx",
|
|
170
|
+
"protocolVersion": 5,
|
|
171
|
+
"channels": ["private", "realtime", "regular"],
|
|
172
|
+
"channelWireCodes": { "regular": 0, "realtime": 1, "private": 2 },
|
|
173
|
+
"wireFormat": {
|
|
174
|
+
"markers": { "globalAttribute": 0, "event": 1, "field": 2, "lastFlag": 4, "extendedIdFlag": 8 },
|
|
175
|
+
"valueEncodingBits": {
|
|
176
|
+
"null": 0, "intZero": 16, "intOne": 32, "int8": 48, "int16": 64,
|
|
177
|
+
"int32": 80, "int64": 96, "float64": 112,
|
|
178
|
+
"stringShort": 128, "stringMedium": 144, "stringLong": 160
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"bufferConstants": {
|
|
182
|
+
"maxBufferSize": 50000,
|
|
183
|
+
"maxBufferSizeForUpload": 64000,
|
|
184
|
+
"inMemoryBufferingDurationSecs": 5,
|
|
185
|
+
"bufferRotateIntervalSecs": 120,
|
|
186
|
+
"workerDataBatchSize": 100,
|
|
187
|
+
"guestInMemoryBufferingDurationSecs": 1,
|
|
188
|
+
"guestBufferRotateIntervalSecs": 2
|
|
189
|
+
},
|
|
190
|
+
"privateStatsIds": [
|
|
191
|
+
{ "key": "DefaultPsId", "keyHashInt": 113760892, "rotationPeriodDays": -1 },
|
|
192
|
+
{ "key": "none", "keyHashInt": 0, "rotationPeriodDays": -1 },
|
|
193
|
+
…
|
|
194
|
+
],
|
|
195
|
+
"reservedGlobals": [
|
|
196
|
+
{ "id": 47, "label": "commitTime" },
|
|
197
|
+
{ "id": 3433, "label": "eventSequenceNumber" },
|
|
198
|
+
{ "id": 6005, "label": "psIdValue" }
|
|
199
|
+
],
|
|
200
|
+
"enums": {
|
|
201
|
+
"UI_ACTION_TYPE": {
|
|
202
|
+
"module": "WAWebWamEnumUiActionType",
|
|
203
|
+
"export": "UI_ACTION_TYPE",
|
|
204
|
+
"values": { "OTHER": 1, "APP_OPEN": 2, "CHAT_OPEN": 3, … }
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"globals": {
|
|
208
|
+
"platform": { "id": 11, "type": "enum", "enum": "PLATFORM_TYPE",
|
|
209
|
+
"channels": ["regular","private"], "falcoName": "platform" }
|
|
210
|
+
},
|
|
211
|
+
"events": {
|
|
212
|
+
"UiAction": {
|
|
213
|
+
"id": 472,
|
|
214
|
+
"module": "WAWebUiActionWamEvent",
|
|
215
|
+
"falcoName": "wam_ui_action",
|
|
216
|
+
"channel": "regular",
|
|
217
|
+
"privateStatsIdInt": null,
|
|
218
|
+
"emittedByWorker": false,
|
|
219
|
+
"weight": { "default": 1, "gkx26259": 100, "gkx26258": 5000 },
|
|
220
|
+
"requiredFields": [],
|
|
221
|
+
"conditions": [],
|
|
222
|
+
"fields": {
|
|
223
|
+
"uiActionType": { "id": 1, "type": "enum", "enum": "UI_ACTION_TYPE",
|
|
224
|
+
"falcoName": "ui_action_type" }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Wire format
|
|
232
|
+
|
|
233
|
+
Events are buffered in-memory and flushed in batches to
|
|
234
|
+
`WAWebUploadStatsBackend` (regular) or `WAWebUploadPrivateStatsBackend`
|
|
235
|
+
(private). The on-wire encoding is a compact TLV serialisation keyed by the
|
|
236
|
+
numeric field ids — see `WAWebWamLibContext` / `WAWebWamLibProtocol` in WA
|
|
237
|
+
Web's source for the canonical writer.
|
|
238
|
+
|
|
239
|
+
For most consumers the IR alone is sufficient: pair `event.id` + per-field
|
|
240
|
+
`{id, type, enum?}` with whatever framing your transport uses. If you need
|
|
241
|
+
to speak the binary protocol directly, the schema also surfaces:
|
|
242
|
+
|
|
243
|
+
- **`WA_WAM_PROTOCOL_VERSION`** — the byte stamped right after the literal
|
|
244
|
+
`"WAM"` magic on every batch (currently `5`).
|
|
245
|
+
- **`WA_WAM_CHANNEL_WIRE_CODES`** — the channel byte at offset 5 of the
|
|
246
|
+
header (`regular: 0, realtime: 1, private: 2`).
|
|
247
|
+
- **`WA_WAM_RESERVED_GLOBALS`** — three IDs `WAWebWamLibContext` injects
|
|
248
|
+
into every batch as **global attributes** (wire marker byte `0`, disjoint
|
|
249
|
+
from the event-field marker byte `2`):
|
|
250
|
+
- `47` → `commitTime` (unix seconds, set right before each event)
|
|
251
|
+
- `3433` → `eventSequenceNumber` (Beaconing sequence, when non-null)
|
|
252
|
+
- `6005` → `psIdValue` (psId bucket value, only for `private` channel —
|
|
253
|
+
same id as the declared `psId` global, listed here for completeness)
|
|
254
|
+
- **`WA_WAM_WIRE_FORMAT`** — the magic numbers a TLV encoder/decoder
|
|
255
|
+
needs. Marker byte = (bottom 4 bits: `markers.{globalAttribute|event|
|
|
256
|
+
field}` plus `lastFlag` + `extendedIdFlag`) | (top 4 bits:
|
|
257
|
+
`valueEncodingBits.{null|intZero|intOne|int8|int16|int32|int64|
|
|
258
|
+
float64|stringShort|stringMedium|stringLong}` — selects payload type
|
|
259
|
+
and size class).
|
|
260
|
+
- **`WA_WAM_BUFFER_CONSTANTS`** — the runtime's flush/upload limits:
|
|
261
|
+
`maxBufferSize` (50KB triggers flush), `maxBufferSizeForUpload`
|
|
262
|
+
(64KB server-side cap), `bufferRotateIntervalSecs` (120s rotation),
|
|
263
|
+
`inMemoryBufferingDurationSecs` (5s), `workerDataBatchSize` (100).
|
|
264
|
+
Guest sessions (unauthenticated companion links) override the two
|
|
265
|
+
time-based thresholds with much shorter values:
|
|
266
|
+
`guestInMemoryBufferingDurationSecs` (1s) and
|
|
267
|
+
`guestBufferRotateIntervalSecs` (2s).
|
|
268
|
+
|
|
269
|
+
These IDs live in the global namespace on the wire, so an event field with
|
|
270
|
+
`id: 47` does NOT collide with `commitTime` — they're tagged with different
|
|
271
|
+
marker bytes.
|
|
272
|
+
|
|
273
|
+
## Generate locally
|
|
274
|
+
|
|
275
|
+
```sh
|
|
276
|
+
npx wa-fetcher --out dump/ # download bundles
|
|
277
|
+
npx wa-wam apply --bundles dump/raw/<version>/
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Caveats
|
|
281
|
+
|
|
282
|
+
- **Sampling weight = `1` means always emit.** Higher values reduce sampling
|
|
283
|
+
rate (the runtime gate is `Math.random() * weight > 1`). The tuple's three
|
|
284
|
+
slots map to `default` (no gkx), `gkx26259`, `gkx26258` — the latter
|
|
285
|
+
takes precedence when on.
|
|
286
|
+
- **`requiredFields` covers static nullability checks only.** Events list
|
|
287
|
+
the camelCase field names that must be non-null at commit time, taken
|
|
288
|
+
from slot 1 of every validator triple `[[predicates], [requiredFields],
|
|
289
|
+
[conditions]]` (multiple triples can chain; the union is captured).
|
|
290
|
+
- **`conditions` captures the human-readable validation messages** from
|
|
291
|
+
slot 2's `[fn, "msg"]` pairs — e.g. `"about_chat_bubble_tap_count >= 0"`,
|
|
292
|
+
`"was_sheet_seen_for_first_time != False"`. The predicate function itself
|
|
293
|
+
is a JS function literal we can't represent statically, but the
|
|
294
|
+
developer-authored message string IS the canonical rule.
|
|
295
|
+
- **Predicates (slot 0)** are conditional guards that, when truthy, suppress
|
|
296
|
+
the validator's required-fields/conditions checks. They're JS functions
|
|
297
|
+
too — not captured.
|
|
298
|
+
- **`emittedByWorker: true`** on an event means its module is listed in
|
|
299
|
+
`WAWebWamProcessWorkerData`'s dep array — the event is committed from the
|
|
300
|
+
Web Worker thread and replayed on the main thread for serialisation. The
|
|
301
|
+
worker emits 63 of the 423 events (mostly E2E/Md/AppState/Receipt/Daily
|
|
302
|
+
metrics that run in background); the other 360 are committed only from
|
|
303
|
+
the main UI thread.
|
|
304
|
+
|
|
305
|
+
## Auto-generated instance methods (derivable from the schema)
|
|
306
|
+
|
|
307
|
+
Each `WamEvent` subclass that `defineEvents` builds at module-load time has a
|
|
308
|
+
predictable instance API. The schema doesn't list these methods explicitly
|
|
309
|
+
(they're all derivable from `event.fields`) but they're worth knowing if
|
|
310
|
+
you're calling WA Web's own runtime or wrapping it:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
class UiActionWamEvent {
|
|
314
|
+
// Set on construction; one per event class
|
|
315
|
+
readonly id: 472
|
|
316
|
+
readonly $className: 'UiAction'
|
|
317
|
+
readonly weight: number
|
|
318
|
+
readonly wamChannel: 'regular'
|
|
319
|
+
eventTime: number // Date.now() at construction, override via setTime(t)
|
|
320
|
+
|
|
321
|
+
// One typed setter per field (typeof-validated against the schema type)
|
|
322
|
+
uiActionType: ...
|
|
323
|
+
uiActionPreloaded: boolean
|
|
324
|
+
uiActionT: number // TIMER field — milliseconds
|
|
325
|
+
// …
|
|
326
|
+
|
|
327
|
+
// For every TIMER field `<x>`, two helpers auto-attached:
|
|
328
|
+
startUiActionT(): void // records ts = Date.now()
|
|
329
|
+
markUiActionT(): void // sets this.uiActionT = Date.now() - ts (or eventTime)
|
|
330
|
+
|
|
331
|
+
// Generic accessors
|
|
332
|
+
getValue(fieldName: string): unknown
|
|
333
|
+
resolveEnumValue(fieldName: string, numericValue: number): string | number
|
|
334
|
+
getEventNameForFalco(): string // → 'wam_ui_action'
|
|
335
|
+
getFieldsMapForFalco(): Record<string, unknown> | null
|
|
336
|
+
|
|
337
|
+
// Commit lifecycle
|
|
338
|
+
runPreCommitValidation(): void // throws if requiredFields/conditions fail
|
|
339
|
+
commit(): void // buffered (channel + buffer constants)
|
|
340
|
+
commitAndWaitForFlush(force?: boolean): Promise<void> // force-flush all channels if true
|
|
341
|
+
setTime(t?: number): void // override eventTime
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
So consumers writing typed wrappers around WA Web's runtime can derive every
|
|
346
|
+
method name (`mark<Field>` / `start<Field>`) from `event.fields[name].type
|
|
347
|
+
=== 'timer'`, and every field setter's accepted type from
|
|
348
|
+
`WaWamFieldValueOf<event.fields[name]>`.
|
|
349
|
+
- **`channel: 'realtime'` events bypass the regular buffer.** They flush
|
|
350
|
+
immediately via `setTimeout(forceRunNow, 1)` when gkx 3237 is on.
|
|
351
|
+
- **`privateStatsIdInt` references a `keyHashInt` in `WA_WAM_PRIVATE_STATS_IDS`.**
|
|
352
|
+
The matching `key` is the rotation bucket (e.g. `IdTtlDaily` rotates every
|
|
353
|
+
1 day, `IdTtl90Days` every 90 days). `keyHashInt` is the hash the server
|
|
354
|
+
joins on; `key` is the human label.
|
|
355
|
+
- **The codegen module is `WAWebWamCodegenUtils`.** Any new event added to WA
|
|
356
|
+
Web shows up here on the next daily extract; any event id/field id change
|
|
357
|
+
surfaces in the diff.
|
|
358
|
+
|
|
359
|
+
Daily-extracted by [wa-spec](https://github.com/vinikjkkj/wa-spec).
|