wardx 0.4.0 → 0.5.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 CHANGED
@@ -315,6 +315,43 @@ one step, use `distinct(stepName, dims).add(userId)`. It hashes with the require
315
315
  local `privacySalt` and sends only a 512-register HLL sketch (`p=9`, about 4.6%
316
316
  standard error). This still does not create a Mixpanel-style per-user funnel.
317
317
 
318
+ ## User retention: D1 / D7 / D30
319
+
320
+ Call `wardx.retentionActivity(userId)` on the activity that defines a return,
321
+ for example opening the app or starting a game. Use the same definition in all
322
+ clients of the project. `userId` is required on every call, must be nonblank,
323
+ and must remain stable across sessions and devices. `identify()` is not used
324
+ as an implicit fallback.
325
+
326
+ ```js
327
+ wardx.retentionActivity(userId);
328
+ ```
329
+
330
+ The SDK sends a salted subject hash, never the raw ID. Keep `privacySalt` stable
331
+ and identical across project clients; the server pins its fingerprint on the
332
+ first accepted activity and rejects a different salt. Cohorts and returns are
333
+ project-wide across roles and environments; use separate projects for separate
334
+ populations such as production and testing.
335
+
336
+ The server persists the earliest received activity date as the cohort and
337
+ counts each user once on each UTC calendar day. D7 means activity **on** the
338
+ seventh calendar day after the cohort date, not activity on or after D7.
339
+ Duplicate activities do not increase the count. Delayed earlier activity can
340
+ correct the cohort and its returns; subsequent activity never advances it.
341
+
342
+ Query MCP `get_retention` with `project`, inclusive `from`, and exclusive `to`
343
+ as `YYYY-MM-DD` cohort dates. Return dates need not fall inside that range.
344
+ Each cohort contains `users` and D1/D7/D30 `returns` with `users`, `rate` (0–1),
345
+ and `status`. Until the entire target UTC day has elapsed, the return is
346
+ `pending` with null count and rate. Empty cohorts are omitted.
347
+
348
+ Counts are exact for received activity, not proof of complete delivery. This
349
+ uses the existing bounded, in-memory event buffer and at-most-once sync: lost
350
+ batches can lose initial activity or returns. There is no historical backfill
351
+ from ordinary events or `distinct`. Existing timestamp and late-data limits
352
+ apply. A mature result can still change when accepted delayed activity arrives.
353
+ Retention requires a server with this feature; older servers cannot compute it.
354
+
318
355
  ## Use case 8: Detect abnormal point accumulation
319
356
 
320
357
  **When:** A game grants points, coins, or XP. You need to see whether the economy is consistent, or whether grants jumped outside the normal range.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wardx",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Node.js SDK for Wardx telemetry, Remote Config, and experiments.",
5
5
  "keywords": [
6
6
  "wardx",
@@ -37,6 +37,6 @@
37
37
  "src"
38
38
  ],
39
39
  "dependencies": {
40
- "@wardx/core": "0.4.0"
40
+ "@wardx/core": "0.5.0"
41
41
  }
42
- }
42
+ }
package/src/WardxNode.js CHANGED
@@ -43,6 +43,10 @@ export class WardxNode {
43
43
  this._enqueueSync({ bootstrap: true });
44
44
  }
45
45
 
46
+ retentionActivity(userId) {
47
+ this._core.retentionActivity(userId);
48
+ }
49
+
46
50
  identify(subjectId) {
47
51
  this._core.identify(subjectId);
48
52
  }
package/src/index.d.ts CHANGED
@@ -76,6 +76,7 @@ export class WardxNode {
76
76
  config: ConfigApi;
77
77
  experiment: ExperimentApi;
78
78
  constructor(settings: ResolvedSettings);
79
+ retentionActivity(userId: string): void;
79
80
  identify(subjectId: string | null | undefined): void;
80
81
  counter(name: string, dims?: Dimensions | null): CounterHandle;
81
82
  gauge(name: string, dims?: Dimensions | null): GaugeHandle;