ad2app-lib 1.35.0 → 1.37.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/dist/analytics/index.d.ts +22 -1
- package/dist/analytics/index.js +17 -1
- package/package.json +1 -1
- package/src/analytics/index.test.ts +52 -2
- package/src/analytics/index.ts +35 -1
|
@@ -75,6 +75,9 @@ export declare const EVENTS: {
|
|
|
75
75
|
readonly POST_RESCHEDULED: "post_rescheduled";
|
|
76
76
|
readonly ACCOUNT_DISCONNECTED: "account_disconnected";
|
|
77
77
|
readonly NOTIFICATION_PREFERENCES_UPDATED: "notification_preferences_updated";
|
|
78
|
+
readonly NATIVE_SESSION_LAUNCHED: "native_session_launched";
|
|
79
|
+
readonly NATIVE_SESSION_RENEWED: "native_session_renewed";
|
|
80
|
+
readonly NATIVE_SESSION_ENDED: "native_session_ended";
|
|
78
81
|
};
|
|
79
82
|
export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
|
|
80
83
|
/** Which email program a send belongs to (spec 120). Wire values — do not rename. */
|
|
@@ -251,7 +254,7 @@ export interface EventProperties {
|
|
|
251
254
|
role: string;
|
|
252
255
|
};
|
|
253
256
|
[EVENTS.LOGGED_IN]: {
|
|
254
|
-
method?: 'email' | 'google';
|
|
257
|
+
method?: 'email' | 'google' | 'apple';
|
|
255
258
|
};
|
|
256
259
|
[EVENTS.SOCIAL_ACCOUNT_CONNECTED]: {
|
|
257
260
|
platform: string;
|
|
@@ -472,6 +475,13 @@ export interface EventProperties {
|
|
|
472
475
|
enabled?: boolean;
|
|
473
476
|
success: boolean;
|
|
474
477
|
};
|
|
478
|
+
[EVENTS.NATIVE_SESSION_LAUNCHED]: {
|
|
479
|
+
outcome: 'restored' | 'none' | 'degraded';
|
|
480
|
+
};
|
|
481
|
+
[EVENTS.NATIVE_SESSION_RENEWED]: Record<string, never>;
|
|
482
|
+
[EVENTS.NATIVE_SESSION_ENDED]: {
|
|
483
|
+
cause: 'rejected' | 'sign_out';
|
|
484
|
+
};
|
|
475
485
|
}
|
|
476
486
|
/** Canonical person property keys (set via identify / $set). */
|
|
477
487
|
export declare const PERSON_PROPS: {
|
|
@@ -482,3 +492,14 @@ export declare const PERSON_PROPS: {
|
|
|
482
492
|
readonly PAYWALL_HITS: "paywall_hits";
|
|
483
493
|
};
|
|
484
494
|
export type PersonPropKey = (typeof PERSON_PROPS)[keyof typeof PERSON_PROPS];
|
|
495
|
+
/**
|
|
496
|
+
* Canonical launch-context SUPER-property keys (159 US2). Registered once per
|
|
497
|
+
* session (like `is_pwa` / `display_mode` / `app_platform` in
|
|
498
|
+
* registerLaunchContext), so they ride every event without a per-event field.
|
|
499
|
+
* `APP_BUILD` is the only one governed here today — the value MUST come from
|
|
500
|
+
* the build itself (contract Rule 4), never a hand-maintained constant.
|
|
501
|
+
*/
|
|
502
|
+
export declare const LAUNCH_CONTEXT_PROPS: {
|
|
503
|
+
readonly APP_BUILD: "app_build";
|
|
504
|
+
};
|
|
505
|
+
export type LaunchContextPropKey = (typeof LAUNCH_CONTEXT_PROPS)[keyof typeof LAUNCH_CONTEXT_PROPS];
|
package/dist/analytics/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Do NOT rename events after they ship — historical data does not migrate.
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.PERSON_PROPS = exports.CONNECT_BLOCK_REASONS = exports.ONBOARDING_COMPLETED_AT = exports.EMAIL_PROPS = exports.EVENTS = void 0;
|
|
13
|
+
exports.LAUNCH_CONTEXT_PROPS = exports.PERSON_PROPS = exports.CONNECT_BLOCK_REASONS = exports.ONBOARDING_COMPLETED_AT = exports.EMAIL_PROPS = exports.EVENTS = void 0;
|
|
14
14
|
/** Canonical PostHog event names. */
|
|
15
15
|
exports.EVENTS = {
|
|
16
16
|
// Acquisition (landing)
|
|
@@ -110,6 +110,12 @@ exports.EVENTS = {
|
|
|
110
110
|
ACCOUNT_DISCONNECTED: 'account_disconnected',
|
|
111
111
|
// Settings.
|
|
112
112
|
NOTIFICATION_PREFERENCES_UPDATED: 'notification_preferences_updated',
|
|
113
|
+
// Native session lifecycle (159) — the iOS wrapper's Keychain-backed session,
|
|
114
|
+
// reported so "does it survive" is a query instead of an inference from login
|
|
115
|
+
// counts. Native only; the web session's posture is unchanged (contract Rule 5).
|
|
116
|
+
NATIVE_SESSION_LAUNCHED: 'native_session_launched',
|
|
117
|
+
NATIVE_SESSION_RENEWED: 'native_session_renewed',
|
|
118
|
+
NATIVE_SESSION_ENDED: 'native_session_ended',
|
|
113
119
|
};
|
|
114
120
|
/**
|
|
115
121
|
* Canonical email event property KEYS (spec 120 T001). Emitters and tests
|
|
@@ -157,3 +163,13 @@ exports.PERSON_PROPS = {
|
|
|
157
163
|
// stored here would rewrite the value on every historical paywall_shown event.
|
|
158
164
|
PAYWALL_HITS: 'paywall_hits',
|
|
159
165
|
};
|
|
166
|
+
/**
|
|
167
|
+
* Canonical launch-context SUPER-property keys (159 US2). Registered once per
|
|
168
|
+
* session (like `is_pwa` / `display_mode` / `app_platform` in
|
|
169
|
+
* registerLaunchContext), so they ride every event without a per-event field.
|
|
170
|
+
* `APP_BUILD` is the only one governed here today — the value MUST come from
|
|
171
|
+
* the build itself (contract Rule 4), never a hand-maintained constant.
|
|
172
|
+
*/
|
|
173
|
+
exports.LAUNCH_CONTEXT_PROPS = {
|
|
174
|
+
APP_BUILD: 'app_build',
|
|
175
|
+
};
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
9
|
import { test } from "node:test";
|
|
10
10
|
|
|
11
|
-
import { EVENTS, PERSON_PROPS, EMAIL_PROPS } from "./index";
|
|
11
|
+
import { EVENTS, PERSON_PROPS, EMAIL_PROPS, LAUNCH_CONTEXT_PROPS } from "./index";
|
|
12
12
|
import type {
|
|
13
13
|
EventProperties,
|
|
14
14
|
EmailEventProperties,
|
|
@@ -86,7 +86,9 @@ const EVENT_PROPERTY_WITNESS: { [E in keyof EventProperties]: EventProperties[E]
|
|
|
86
86
|
[EVENTS.PLAYBOOK_DOWNLOADED]: {},
|
|
87
87
|
[EVENTS.SIGNED_UP]: { method: "email", role: "creator" },
|
|
88
88
|
[EVENTS.PROFILE_COMPLETED]: { role: "creator" },
|
|
89
|
-
|
|
89
|
+
// Spec 161 T010: proves 'apple' type-checks alongside the pre-existing
|
|
90
|
+
// 'email' | 'google' union (compile-time, enforced by `tsc --noEmit`).
|
|
91
|
+
[EVENTS.LOGGED_IN]: { method: "apple" },
|
|
90
92
|
[EVENTS.SOCIAL_ACCOUNT_CONNECTED]: { platform: "instagram" },
|
|
91
93
|
[EVENTS.COMPOSE_STARTED]: {},
|
|
92
94
|
[EVENTS.COMPOSE_STEP_VIEWED]: { step: "content" },
|
|
@@ -133,6 +135,10 @@ const EVENT_PROPERTY_WITNESS: { [E in keyof EventProperties]: EventProperties[E]
|
|
|
133
135
|
[EVENTS.POST_RESCHEDULED]: { success: true },
|
|
134
136
|
[EVENTS.ACCOUNT_DISCONNECTED]: { platform: "instagram", success: true },
|
|
135
137
|
[EVENTS.NOTIFICATION_PREFERENCES_UPDATED]: { success: true },
|
|
138
|
+
// 159 native session lifecycle
|
|
139
|
+
[EVENTS.NATIVE_SESSION_LAUNCHED]: { outcome: "restored" },
|
|
140
|
+
[EVENTS.NATIVE_SESSION_RENEWED]: {},
|
|
141
|
+
[EVENTS.NATIVE_SESSION_ENDED]: { cause: "rejected" },
|
|
136
142
|
};
|
|
137
143
|
|
|
138
144
|
test("EVENTS values are 1:1 with EventProperties keys (no missing or typo'd event)", () => {
|
|
@@ -455,3 +461,47 @@ test("120 playbook events carry the edition stamp", () => {
|
|
|
455
461
|
assert.equal(opened.edition_version, "2026.08");
|
|
456
462
|
assert.equal(downloaded.format, "pdf");
|
|
457
463
|
});
|
|
464
|
+
|
|
465
|
+
// 159: the native session lifecycle + build attribution.
|
|
466
|
+
|
|
467
|
+
test("159 native session wire names are locked", () => {
|
|
468
|
+
assert.equal(EVENTS.NATIVE_SESSION_LAUNCHED, "native_session_launched");
|
|
469
|
+
assert.equal(EVENTS.NATIVE_SESSION_RENEWED, "native_session_renewed");
|
|
470
|
+
assert.equal(EVENTS.NATIVE_SESSION_ENDED, "native_session_ended");
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test("159 native_session_launched carries a closed outcome — restored, none, or degraded", () => {
|
|
474
|
+
const restored: EventProperties[typeof EVENTS.NATIVE_SESSION_LAUNCHED] = { outcome: "restored" };
|
|
475
|
+
const none: EventProperties[typeof EVENTS.NATIVE_SESSION_LAUNCHED] = { outcome: "none" };
|
|
476
|
+
const degraded: EventProperties[typeof EVENTS.NATIVE_SESSION_LAUNCHED] = { outcome: "degraded" };
|
|
477
|
+
assert.equal(restored.outcome, "restored");
|
|
478
|
+
assert.equal(none.outcome, "none");
|
|
479
|
+
assert.equal(degraded.outcome, "degraded");
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
test("159 native_session_ended carries its cause — rejected or sign_out, never bare", () => {
|
|
483
|
+
const rejected: EventProperties[typeof EVENTS.NATIVE_SESSION_ENDED] = { cause: "rejected" };
|
|
484
|
+
const signOut: EventProperties[typeof EVENTS.NATIVE_SESSION_ENDED] = { cause: "sign_out" };
|
|
485
|
+
assert.equal(rejected.cause, "rejected");
|
|
486
|
+
assert.equal(signOut.cause, "sign_out");
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("159 native_session_renewed carries no properties (the fact of the renewal is the whole report)", () => {
|
|
490
|
+
const renewed: EventProperties[typeof EVENTS.NATIVE_SESSION_RENEWED] = {};
|
|
491
|
+
assert.deepEqual(renewed, {});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("161 logged_in.method accepts all three sign-in methods, and stays optional", () => {
|
|
495
|
+
const email: EventProperties[typeof EVENTS.LOGGED_IN] = { method: "email" };
|
|
496
|
+
const google: EventProperties[typeof EVENTS.LOGGED_IN] = { method: "google" };
|
|
497
|
+
const apple: EventProperties[typeof EVENTS.LOGGED_IN] = { method: "apple" };
|
|
498
|
+
const unset: EventProperties[typeof EVENTS.LOGGED_IN] = {};
|
|
499
|
+
assert.equal(email.method, "email");
|
|
500
|
+
assert.equal(google.method, "google");
|
|
501
|
+
assert.equal(apple.method, "apple");
|
|
502
|
+
assert.deepEqual(unset, {});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test("159 LAUNCH_CONTEXT_PROPS governs the build-attribution key (no raw 'app_build' string at the call site)", () => {
|
|
506
|
+
assert.equal(LAUNCH_CONTEXT_PROPS.APP_BUILD, "app_build");
|
|
507
|
+
});
|
package/src/analytics/index.ts
CHANGED
|
@@ -121,6 +121,13 @@ export const EVENTS = {
|
|
|
121
121
|
|
|
122
122
|
// Settings.
|
|
123
123
|
NOTIFICATION_PREFERENCES_UPDATED: 'notification_preferences_updated',
|
|
124
|
+
|
|
125
|
+
// Native session lifecycle (159) — the iOS wrapper's Keychain-backed session,
|
|
126
|
+
// reported so "does it survive" is a query instead of an inference from login
|
|
127
|
+
// counts. Native only; the web session's posture is unchanged (contract Rule 5).
|
|
128
|
+
NATIVE_SESSION_LAUNCHED: 'native_session_launched',
|
|
129
|
+
NATIVE_SESSION_RENEWED: 'native_session_renewed',
|
|
130
|
+
NATIVE_SESSION_ENDED: 'native_session_ended',
|
|
124
131
|
} as const;
|
|
125
132
|
|
|
126
133
|
export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
|
|
@@ -334,7 +341,11 @@ export interface EventProperties {
|
|
|
334
341
|
[EVENTS.PLAYBOOK_DOWNLOADED]: { edition_version?: string; format?: string };
|
|
335
342
|
[EVENTS.SIGNED_UP]: { method: 'email' | 'google'; role: string };
|
|
336
343
|
[EVENTS.PROFILE_COMPLETED]: { role: string };
|
|
337
|
-
|
|
344
|
+
// Spec 161: widened to include 'apple' — the value was already missing from
|
|
345
|
+
// this union (Google/email only) even though native Apple sign-in has
|
|
346
|
+
// existed since spec 136; this closes that gap rather than inventing a new
|
|
347
|
+
// property.
|
|
348
|
+
[EVENTS.LOGGED_IN]: { method?: 'email' | 'google' | 'apple' };
|
|
338
349
|
[EVENTS.SOCIAL_ACCOUNT_CONNECTED]: {
|
|
339
350
|
platform: string;
|
|
340
351
|
account_count?: number;
|
|
@@ -500,6 +511,16 @@ export interface EventProperties {
|
|
|
500
511
|
|
|
501
512
|
// Settings — which preference toggled and its new state.
|
|
502
513
|
[EVENTS.NOTIFICATION_PREFERENCES_UPDATED]: { preference?: string; enabled?: boolean; success: boolean };
|
|
514
|
+
|
|
515
|
+
// Native session lifecycle (159, contract session-reports.md). `outcome`
|
|
516
|
+
// covers all three launch reports in one event (restored / none / degraded)
|
|
517
|
+
// rather than three near-duplicate event names — the same pattern as
|
|
518
|
+
// ACCESS_DENIED's `reason`. `cause` on the ending is mandatory (contract
|
|
519
|
+
// Rule 1): an ending without a cause tells a reader nothing a bug and a
|
|
520
|
+
// person didn't already look identical.
|
|
521
|
+
[EVENTS.NATIVE_SESSION_LAUNCHED]: { outcome: 'restored' | 'none' | 'degraded' };
|
|
522
|
+
[EVENTS.NATIVE_SESSION_RENEWED]: Record<string, never>;
|
|
523
|
+
[EVENTS.NATIVE_SESSION_ENDED]: { cause: 'rejected' | 'sign_out' };
|
|
503
524
|
}
|
|
504
525
|
|
|
505
526
|
/** Canonical person property keys (set via identify / $set). */
|
|
@@ -516,6 +537,19 @@ export const PERSON_PROPS = {
|
|
|
516
537
|
|
|
517
538
|
export type PersonPropKey = (typeof PERSON_PROPS)[keyof typeof PERSON_PROPS];
|
|
518
539
|
|
|
540
|
+
/**
|
|
541
|
+
* Canonical launch-context SUPER-property keys (159 US2). Registered once per
|
|
542
|
+
* session (like `is_pwa` / `display_mode` / `app_platform` in
|
|
543
|
+
* registerLaunchContext), so they ride every event without a per-event field.
|
|
544
|
+
* `APP_BUILD` is the only one governed here today — the value MUST come from
|
|
545
|
+
* the build itself (contract Rule 4), never a hand-maintained constant.
|
|
546
|
+
*/
|
|
547
|
+
export const LAUNCH_CONTEXT_PROPS = {
|
|
548
|
+
APP_BUILD: 'app_build',
|
|
549
|
+
} as const;
|
|
550
|
+
|
|
551
|
+
export type LaunchContextPropKey = (typeof LAUNCH_CONTEXT_PROPS)[keyof typeof LAUNCH_CONTEXT_PROPS];
|
|
552
|
+
|
|
519
553
|
/**
|
|
520
554
|
* Compile-time taxonomy contract (AD2-952). These are pure type-level assertions:
|
|
521
555
|
* if EVENTS and EventProperties ever drift, the `tsc --noEmit` CI gate fails the
|