@apex-inc/capacitor-plugin 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.
Files changed (83) hide show
  1. package/ApexCapacitorPlugin.podspec +17 -0
  2. package/LICENSE +17 -0
  3. package/README.md +136 -0
  4. package/android/build.gradle +68 -0
  5. package/android/src/main/AndroidManifest.xml +8 -0
  6. package/android/src/main/java/inc/apex/capacitor/ApexCapacitorPlugin.kt +325 -0
  7. package/android/src/main/java/inc/apex/capacitor/DeepLinkManager.kt +47 -0
  8. package/android/src/main/java/inc/apex/capacitor/InstallReferrerParser.kt +123 -0
  9. package/android/src/main/java/inc/apex/capacitor/OfflineQueue.kt +150 -0
  10. package/android/src/main/java/inc/apex/capacitor/SessionManager.kt +108 -0
  11. package/dist/batch-sender.d.ts +60 -0
  12. package/dist/batch-sender.d.ts.map +1 -0
  13. package/dist/batch-sender.js +115 -0
  14. package/dist/batch-sender.js.map +1 -0
  15. package/dist/definitions.d.ts +224 -0
  16. package/dist/definitions.d.ts.map +1 -0
  17. package/dist/definitions.js +14 -0
  18. package/dist/definitions.js.map +1 -0
  19. package/dist/esm/batch-sender.d.ts +60 -0
  20. package/dist/esm/batch-sender.d.ts.map +1 -0
  21. package/dist/esm/batch-sender.js +111 -0
  22. package/dist/esm/batch-sender.js.map +1 -0
  23. package/dist/esm/definitions.d.ts +224 -0
  24. package/dist/esm/definitions.d.ts.map +1 -0
  25. package/dist/esm/definitions.js +13 -0
  26. package/dist/esm/definitions.js.map +1 -0
  27. package/dist/esm/event-id.d.ts +17 -0
  28. package/dist/esm/event-id.d.ts.map +1 -0
  29. package/dist/esm/event-id.js +57 -0
  30. package/dist/esm/event-id.js.map +1 -0
  31. package/dist/esm/index.d.ts +29 -0
  32. package/dist/esm/index.d.ts.map +1 -0
  33. package/dist/esm/index.js +30 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/offline-queue.d.ts +111 -0
  36. package/dist/esm/offline-queue.d.ts.map +1 -0
  37. package/dist/esm/offline-queue.js +240 -0
  38. package/dist/esm/offline-queue.js.map +1 -0
  39. package/dist/esm/session-manager.d.ts +63 -0
  40. package/dist/esm/session-manager.d.ts.map +1 -0
  41. package/dist/esm/session-manager.js +100 -0
  42. package/dist/esm/session-manager.js.map +1 -0
  43. package/dist/esm/web.d.ts +65 -0
  44. package/dist/esm/web.d.ts.map +1 -0
  45. package/dist/esm/web.js +203 -0
  46. package/dist/esm/web.js.map +1 -0
  47. package/dist/event-id.d.ts +17 -0
  48. package/dist/event-id.d.ts.map +1 -0
  49. package/dist/event-id.js +61 -0
  50. package/dist/event-id.js.map +1 -0
  51. package/dist/index.d.ts +29 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +76 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/offline-queue.d.ts +111 -0
  56. package/dist/offline-queue.d.ts.map +1 -0
  57. package/dist/offline-queue.js +246 -0
  58. package/dist/offline-queue.js.map +1 -0
  59. package/dist/session-manager.d.ts +63 -0
  60. package/dist/session-manager.d.ts.map +1 -0
  61. package/dist/session-manager.js +104 -0
  62. package/dist/session-manager.js.map +1 -0
  63. package/dist/web.d.ts +65 -0
  64. package/dist/web.d.ts.map +1 -0
  65. package/dist/web.js +207 -0
  66. package/dist/web.js.map +1 -0
  67. package/ios/Package.swift +34 -0
  68. package/ios/Sources/ApexCapacitorPlugin/AdvertisingIdProvider.swift +66 -0
  69. package/ios/Sources/ApexCapacitorPlugin/AttManager.swift +82 -0
  70. package/ios/Sources/ApexCapacitorPlugin/DeepLinkManager.swift +64 -0
  71. package/ios/Sources/ApexCapacitorPlugin/DeviceInfo.swift +107 -0
  72. package/ios/Sources/ApexCapacitorPlugin/OfflineQueue.swift +191 -0
  73. package/ios/Sources/ApexCapacitorPlugin/SessionManager.swift +113 -0
  74. package/ios/Sources/ApexCapacitorPlugin/SkanManager.swift +95 -0
  75. package/ios/Sources/ApexCapacitorPluginBridge/ApexCapacitorPlugin.swift +269 -0
  76. package/ios/Tests/ApexCapacitorPluginTests/AdvertisingIdProviderTests.swift +74 -0
  77. package/ios/Tests/ApexCapacitorPluginTests/AttManagerTests.swift +82 -0
  78. package/ios/Tests/ApexCapacitorPluginTests/DeepLinkManagerTests.swift +69 -0
  79. package/ios/Tests/ApexCapacitorPluginTests/DeviceInfoTests.swift +52 -0
  80. package/ios/Tests/ApexCapacitorPluginTests/OfflineQueueTests.swift +134 -0
  81. package/ios/Tests/ApexCapacitorPluginTests/SessionManagerTests.swift +98 -0
  82. package/ios/Tests/ApexCapacitorPluginTests/SkanManagerTests.swift +91 -0
  83. package/package.json +82 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Public TypeScript interface for `@apex-inc/capacitor-plugin`.
3
+ *
4
+ * These types define the contract between the JS layer (this package) and
5
+ * the native iOS (Swift, MMP-004) and Android (Kotlin, MMP-005) implementations.
6
+ * Shape changes are breaking after 1.0.0.
7
+ *
8
+ * Pairs with `apex.js` (the web snippet) when loaded inside a Capacitor
9
+ * WebView — shared visitor ID, shared session, shared events via one backend.
10
+ * See `plans/mobile-measurement-platform.md` Phase 1b for scope and rationale.
11
+ */
12
+ import type { PluginListenerHandle } from "@capacitor/core";
13
+ /** iOS App Tracking Transparency authorization status. */
14
+ export type AttStatus = "authorized" | "denied" | "restricted" | "not-determined";
15
+ /** Supported tracking platforms. */
16
+ export type ApexPlatform = "ios" | "android" | "web";
17
+ /** Fallback source when the primary advertising identifier is unavailable. */
18
+ export type AdvertisingIdFallback = "idfv" | "android_id" | null;
19
+ /** Coarse SKAN 4.0 conversion value. */
20
+ export type SkanCoarseValue = "low" | "medium" | "high";
21
+ /**
22
+ * Cross-platform tracking event mirroring the server `TrackingEvent` shape.
23
+ * The plugin batches and sends these to `/api/events` via {@link ApexCapacitorPlugin.track}.
24
+ */
25
+ export interface ApexEvent {
26
+ /** Event type — matches server `TrackingEventType`. */
27
+ type: string;
28
+ /** Client-generated UUIDv4. Auto-filled by `track()` if omitted. */
29
+ id?: string;
30
+ /** Session ID — auto-managed by session manager when omitted. */
31
+ sessionId?: string;
32
+ /** ISO timestamp of when the event occurred (client clock). Auto-filled if omitted. */
33
+ timestamp?: string;
34
+ /** URL or deep-link path associated with the event (default: current route). */
35
+ url?: string;
36
+ utmSource?: string;
37
+ utmMedium?: string;
38
+ utmCampaign?: string;
39
+ experimentId?: string;
40
+ variant?: string;
41
+ /** Typed payload for in-app purchases. */
42
+ purchase?: {
43
+ productId: string;
44
+ amount: number;
45
+ currency: string;
46
+ transactionId: string;
47
+ receiptData?: string;
48
+ isRestored?: boolean;
49
+ };
50
+ /** Typed payload for subscription lifecycle. */
51
+ subscription?: {
52
+ productId: string;
53
+ action: "started" | "renewed" | "cancelled" | "expired" | "grace_period" | "paused" | "resumed" | "trial_started" | "trial_converted" | "trial_expired";
54
+ amount?: number;
55
+ currency?: string;
56
+ periodType?: "monthly" | "yearly" | "weekly" | "custom";
57
+ transactionId?: string;
58
+ expiresAt?: string;
59
+ isTrial?: boolean;
60
+ };
61
+ /** Unstructured additional context. */
62
+ data?: Record<string, unknown>;
63
+ }
64
+ /** Options for {@link ApexCapacitorPlugin.initialize}. */
65
+ export interface InitializeOptions {
66
+ /** Project key from the Apex dashboard. Required. */
67
+ projectKey: string;
68
+ /** API base URL. Defaults to `https://api.apex.inc`. */
69
+ apiUrl?: string;
70
+ /** Tag subsequent events as test mode (excluded from production analytics). */
71
+ testMode?: boolean;
72
+ /** Session timeout in minutes. Default 30. */
73
+ sessionTimeoutMinutes?: number;
74
+ /** Maximum queued events before FIFO eviction kicks in. Default 1000. */
75
+ offlineQueueMaxSize?: number;
76
+ /** Enable verbose console logging. Default false. */
77
+ debug?: boolean;
78
+ }
79
+ /** Return value of {@link ApexCapacitorPlugin.getAdvertisingId}. */
80
+ export interface AdvertisingIdResult {
81
+ /** The advertising identifier, or null when completely unavailable. */
82
+ id: string | null;
83
+ /** When `id` is null/zero, indicates which fallback is in use. */
84
+ fallback: AdvertisingIdFallback;
85
+ }
86
+ /** Device metadata returned by {@link ApexCapacitorPlugin.getDeviceInfo}. */
87
+ export interface DeviceInfo {
88
+ platform: "ios" | "android";
89
+ osVersion: string;
90
+ model: string;
91
+ appVersion: string;
92
+ bundleId: string;
93
+ timezone: string;
94
+ locale: string;
95
+ }
96
+ /** Summary of offline-queue state. */
97
+ export interface QueueStatus {
98
+ /** Number of events currently pending send. */
99
+ count: number;
100
+ /** ISO timestamp of the oldest queued event, or null if queue is empty. */
101
+ oldestEventAt: string | null;
102
+ }
103
+ /** Outcome of {@link ApexCapacitorPlugin.flushQueue}. */
104
+ export interface FlushResult {
105
+ /** Events successfully sent during this flush. */
106
+ flushed: number;
107
+ /** Events that remain in the queue (failed + not attempted). */
108
+ remaining: number;
109
+ }
110
+ /** Current session summary. */
111
+ export interface SessionInfo {
112
+ sessionId: string | null;
113
+ startedAt: string | null;
114
+ }
115
+ /**
116
+ * The primary plugin contract. Matches Phase 1b API surface locked in
117
+ * `plans/mobile-measurement-platform.md`.
118
+ */
119
+ export interface ApexCapacitorPlugin {
120
+ /**
121
+ * Initializes the plugin. Must be called once before any other method.
122
+ * Registers push notification handlers, seeds the offline queue from native
123
+ * storage, and starts the session manager.
124
+ */
125
+ initialize(options: InitializeOptions): Promise<void>;
126
+ /**
127
+ * Requests ATT permission on iOS (presents the system prompt once per
128
+ * install). No-op on Android — returns `"authorized"`.
129
+ */
130
+ requestTrackingAuthorization(): Promise<{
131
+ status: AttStatus;
132
+ }>;
133
+ /**
134
+ * Returns the current ATT status without prompting. Android always returns
135
+ * `"authorized"`.
136
+ */
137
+ getTrackingStatus(): Promise<{
138
+ status: AttStatus;
139
+ }>;
140
+ /**
141
+ * Returns IDFA (iOS) or GAID (Android). When unavailable (ATT denied or LAT
142
+ * enabled), returns a fallback with `fallback` indicating which identifier
143
+ * was substituted.
144
+ */
145
+ getAdvertisingId(): Promise<AdvertisingIdResult>;
146
+ /**
147
+ * Android: returns decoded Play Install Referrer string on first launch.
148
+ * iOS: returns `{ referrer: null }` (no equivalent).
149
+ */
150
+ getInstallReferrer(): Promise<{
151
+ referrer: string | null;
152
+ }>;
153
+ /** Gets the plugin-managed visitor ID. */
154
+ getVisitorId(): Promise<{
155
+ visitorId: string;
156
+ }>;
157
+ /** Overrides the visitor ID (e.g. after login). Persists to native storage. */
158
+ setVisitorId(options: {
159
+ visitorId: string;
160
+ }): Promise<void>;
161
+ /**
162
+ * Updates the SKAdNetwork 4.0 postback conversion value. No-op on Android.
163
+ * `fineValue` is 0-63 (fine conversion value); `coarseValue` is optional
164
+ * for SKAN 4.0's coarse-grained reporting.
165
+ */
166
+ updateConversionValue(options: {
167
+ fineValue: number;
168
+ coarseValue?: SkanCoarseValue;
169
+ }): Promise<void>;
170
+ /**
171
+ * Returns the URL the app was opened with on cold start (Universal Link on
172
+ * iOS, App Link on Android). Returns `{ url: null }` if launched without a
173
+ * link. Use the `deepLink` listener for subsequent warm-start links.
174
+ */
175
+ getInitialDeepLink(): Promise<{
176
+ url: string | null;
177
+ }>;
178
+ getDeviceInfo(): Promise<DeviceInfo>;
179
+ /** Force-starts a new session, ending any current one. */
180
+ startSession(): Promise<{
181
+ sessionId: string;
182
+ }>;
183
+ /** Ends the current session immediately. */
184
+ endSession(): Promise<void>;
185
+ /** Returns info about the current session, or null fields if none. */
186
+ getCurrentSession(): Promise<SessionInfo>;
187
+ /**
188
+ * Enqueues an event for delivery. If network is available, events are
189
+ * batched and sent within ~1s; otherwise they persist in the offline queue
190
+ * until `flushQueue()` succeeds.
191
+ */
192
+ track(event: ApexEvent): Promise<void>;
193
+ /** Current offline queue status (event count + oldest timestamp). */
194
+ getQueueSize(): Promise<QueueStatus>;
195
+ /** Attempts to send all queued events. Returns counts of flushed + remaining. */
196
+ flushQueue(): Promise<FlushResult>;
197
+ /**
198
+ * Toggles test mode at runtime. Test-mode events are tagged so the server
199
+ * routes them to the TESTEVT# store, excluded from production analytics.
200
+ */
201
+ setTestMode(options: {
202
+ enabled: boolean;
203
+ }): Promise<void>;
204
+ /** Fires when a deep link opens the app while it is already running. */
205
+ addListener(eventName: "deepLink", listenerFunc: (event: {
206
+ url: string;
207
+ }) => void): Promise<PluginListenerHandle>;
208
+ /** Fires when the app moves between foreground and background. */
209
+ addListener(eventName: "appStateChange", listenerFunc: (event: {
210
+ isActive: boolean;
211
+ }) => void): Promise<PluginListenerHandle>;
212
+ /** Fires when a new session starts. */
213
+ addListener(eventName: "sessionStart", listenerFunc: (event: {
214
+ sessionId: string;
215
+ }) => void): Promise<PluginListenerHandle>;
216
+ /** Fires when the current session ends. */
217
+ addListener(eventName: "sessionEnd", listenerFunc: (event: {
218
+ sessionId: string;
219
+ durationSeconds: number;
220
+ }) => void): Promise<PluginListenerHandle>;
221
+ /** Removes all listeners registered by this plugin instance. */
222
+ removeAllListeners(): Promise<void>;
223
+ }
224
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../src/definitions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Public TypeScript interface for `@apex-inc/capacitor-plugin`.
4
+ *
5
+ * These types define the contract between the JS layer (this package) and
6
+ * the native iOS (Swift, MMP-004) and Android (Kotlin, MMP-005) implementations.
7
+ * Shape changes are breaking after 1.0.0.
8
+ *
9
+ * Pairs with `apex.js` (the web snippet) when loaded inside a Capacitor
10
+ * WebView — shared visitor ID, shared session, shared events via one backend.
11
+ * See `plans/mobile-measurement-platform.md` Phase 1b for scope and rationale.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../src/definitions.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Batch sender with exponential-backoff retries.
3
+ *
4
+ * Drains the offline queue by sending events in batches to `/api/events`.
5
+ * Retries on 5xx and network errors with exponential backoff (1s, 2s, 4s).
6
+ * Gives up after 3 attempts per batch — events remain in the queue for a
7
+ * future flush attempt but are marked with incremented `attempts` so old
8
+ * failing events don't block new ones indefinitely.
9
+ *
10
+ * The fetch function is injectable for testing and to allow future runtimes
11
+ * to swap in native HTTP clients (e.g. on restricted networks).
12
+ */
13
+ import type { OfflineQueue } from "./offline-queue";
14
+ export interface BatchSenderOptions {
15
+ /** API base URL. Default `https://api.apex.inc`. */
16
+ apiUrl?: string;
17
+ /** Project key attached to each request. Required. */
18
+ projectKey: string;
19
+ /** Max events sent per batch. Default 50. */
20
+ batchSize?: number;
21
+ /** Max retry attempts before giving up on a batch. Default 3. */
22
+ maxRetries?: number;
23
+ /** Base backoff delay in milliseconds. Default 1000. */
24
+ baseBackoffMs?: number;
25
+ /** Injectable fetch (Node, test mocks, custom runtimes). */
26
+ fetchFn?: typeof fetch;
27
+ /** Injectable sleep for testing (must return a Promise that resolves). */
28
+ sleepFn?: (ms: number) => Promise<void>;
29
+ /** Platform header to include (`ios` / `android`). */
30
+ platformHeader?: "ios" | "android" | "web";
31
+ /** Enable verbose logs. */
32
+ debug?: boolean;
33
+ }
34
+ export interface FlushResult {
35
+ flushed: number;
36
+ remaining: number;
37
+ attemptedBatches: number;
38
+ lastError?: string;
39
+ }
40
+ export declare class BatchSender {
41
+ private readonly apiUrl;
42
+ private readonly projectKey;
43
+ private readonly batchSize;
44
+ private readonly maxRetries;
45
+ private readonly baseBackoffMs;
46
+ private readonly fetchFn;
47
+ private readonly sleepFn;
48
+ private readonly platformHeader?;
49
+ private readonly debug;
50
+ constructor(options: BatchSenderOptions);
51
+ /**
52
+ * Flushes as many events from the queue as possible, one batch at a time,
53
+ * with exponential backoff on retryable failures. Stops early on
54
+ * non-retryable errors (400/401/403) and leaves remaining events queued.
55
+ */
56
+ flush(queue: OfflineQueue): Promise<FlushResult>;
57
+ private sendBatchWithRetry;
58
+ private postBatch;
59
+ }
60
+ //# sourceMappingURL=batch-sender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-sender.d.ts","sourceRoot":"","sources":["../../src/batch-sender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,iBAAiB,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,sDAAsD;IACtD,cAAc,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;IAC3C,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAA4B;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;gBAEpB,OAAO,EAAE,kBAAkB;IAiBvC;;;;OAIG;IACG,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;YAgCxC,kBAAkB;YAyClB,SAAS;CAoBxB"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Batch sender with exponential-backoff retries.
3
+ *
4
+ * Drains the offline queue by sending events in batches to `/api/events`.
5
+ * Retries on 5xx and network errors with exponential backoff (1s, 2s, 4s).
6
+ * Gives up after 3 attempts per batch — events remain in the queue for a
7
+ * future flush attempt but are marked with incremented `attempts` so old
8
+ * failing events don't block new ones indefinitely.
9
+ *
10
+ * The fetch function is injectable for testing and to allow future runtimes
11
+ * to swap in native HTTP clients (e.g. on restricted networks).
12
+ */
13
+ export class BatchSender {
14
+ constructor(options) {
15
+ this.apiUrl = (options.apiUrl ?? "https://api.apex.inc").replace(/\/+$/, "");
16
+ this.projectKey = options.projectKey;
17
+ this.batchSize = options.batchSize ?? 50;
18
+ this.maxRetries = options.maxRetries ?? 3;
19
+ this.baseBackoffMs = options.baseBackoffMs ?? 1000;
20
+ this.fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
21
+ this.sleepFn =
22
+ options.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
23
+ this.platformHeader = options.platformHeader;
24
+ this.debug = options.debug ?? false;
25
+ if (!this.fetchFn) {
26
+ throw new Error("BatchSender: fetch is not available. Provide options.fetchFn.");
27
+ }
28
+ }
29
+ /**
30
+ * Flushes as many events from the queue as possible, one batch at a time,
31
+ * with exponential backoff on retryable failures. Stops early on
32
+ * non-retryable errors (400/401/403) and leaves remaining events queued.
33
+ */
34
+ async flush(queue) {
35
+ let flushed = 0;
36
+ let attemptedBatches = 0;
37
+ let lastError;
38
+ while (true) {
39
+ const batch = await queue.peek(this.batchSize);
40
+ if (batch.length === 0)
41
+ break;
42
+ attemptedBatches += 1;
43
+ const outcome = await this.sendBatchWithRetry(batch);
44
+ if (outcome.status === "success") {
45
+ const eventIds = batch.map((q) => q.event.id).filter((id) => !!id);
46
+ await queue.markSent(eventIds);
47
+ flushed += batch.length;
48
+ }
49
+ else if (outcome.status === "retryable_exhausted") {
50
+ const eventIds = batch.map((q) => q.event.id).filter((id) => !!id);
51
+ await queue.markFailed(eventIds);
52
+ lastError = outcome.error;
53
+ break; // leave for next flush cycle
54
+ }
55
+ else {
56
+ // Non-retryable: leave events in place but stop flushing.
57
+ lastError = outcome.error;
58
+ break;
59
+ }
60
+ }
61
+ const remaining = await queue.size();
62
+ return { flushed, remaining, attemptedBatches, lastError };
63
+ }
64
+ async sendBatchWithRetry(batch) {
65
+ let lastError = "unknown";
66
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
67
+ try {
68
+ const response = await this.postBatch(batch);
69
+ if (response.ok) {
70
+ return { status: "success" };
71
+ }
72
+ // Non-retryable client errors — leave events, surface to caller.
73
+ if (response.status >= 400 && response.status < 500) {
74
+ return { status: "non_retryable", error: `HTTP ${response.status}` };
75
+ }
76
+ // Retryable: 5xx or unknown. Fall through to backoff.
77
+ lastError = `HTTP ${response.status}`;
78
+ }
79
+ catch (err) {
80
+ lastError = err instanceof Error ? err.message : String(err);
81
+ }
82
+ if (attempt < this.maxRetries - 1) {
83
+ const backoff = this.baseBackoffMs * 2 ** attempt;
84
+ if (this.debug) {
85
+ console.log(`[apex-capacitor] batch-sender retry ${attempt + 1}/${this.maxRetries - 1} in ${backoff}ms (${lastError})`);
86
+ }
87
+ await this.sleepFn(backoff);
88
+ }
89
+ }
90
+ return { status: "retryable_exhausted", error: lastError };
91
+ }
92
+ async postBatch(batch) {
93
+ const body = {
94
+ projectKey: this.projectKey,
95
+ events: batch.map((q) => q.event),
96
+ };
97
+ const headers = {
98
+ "Content-Type": "application/json",
99
+ "X-Apex-Project-Key": this.projectKey,
100
+ };
101
+ if (this.platformHeader) {
102
+ headers["X-Apex-Platform"] = this.platformHeader;
103
+ }
104
+ return this.fetchFn(`${this.apiUrl}/api/events`, {
105
+ method: "POST",
106
+ headers,
107
+ body: JSON.stringify(body),
108
+ });
109
+ }
110
+ }
111
+ //# sourceMappingURL=batch-sender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-sender.js","sourceRoot":"","sources":["../../src/batch-sender.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Public TypeScript interface for `@apex-inc/capacitor-plugin`.
3
+ *
4
+ * These types define the contract between the JS layer (this package) and
5
+ * the native iOS (Swift, MMP-004) and Android (Kotlin, MMP-005) implementations.
6
+ * Shape changes are breaking after 1.0.0.
7
+ *
8
+ * Pairs with `apex.js` (the web snippet) when loaded inside a Capacitor
9
+ * WebView — shared visitor ID, shared session, shared events via one backend.
10
+ * See `plans/mobile-measurement-platform.md` Phase 1b for scope and rationale.
11
+ */
12
+ import type { PluginListenerHandle } from "@capacitor/core";
13
+ /** iOS App Tracking Transparency authorization status. */
14
+ export type AttStatus = "authorized" | "denied" | "restricted" | "not-determined";
15
+ /** Supported tracking platforms. */
16
+ export type ApexPlatform = "ios" | "android" | "web";
17
+ /** Fallback source when the primary advertising identifier is unavailable. */
18
+ export type AdvertisingIdFallback = "idfv" | "android_id" | null;
19
+ /** Coarse SKAN 4.0 conversion value. */
20
+ export type SkanCoarseValue = "low" | "medium" | "high";
21
+ /**
22
+ * Cross-platform tracking event mirroring the server `TrackingEvent` shape.
23
+ * The plugin batches and sends these to `/api/events` via {@link ApexCapacitorPlugin.track}.
24
+ */
25
+ export interface ApexEvent {
26
+ /** Event type — matches server `TrackingEventType`. */
27
+ type: string;
28
+ /** Client-generated UUIDv4. Auto-filled by `track()` if omitted. */
29
+ id?: string;
30
+ /** Session ID — auto-managed by session manager when omitted. */
31
+ sessionId?: string;
32
+ /** ISO timestamp of when the event occurred (client clock). Auto-filled if omitted. */
33
+ timestamp?: string;
34
+ /** URL or deep-link path associated with the event (default: current route). */
35
+ url?: string;
36
+ utmSource?: string;
37
+ utmMedium?: string;
38
+ utmCampaign?: string;
39
+ experimentId?: string;
40
+ variant?: string;
41
+ /** Typed payload for in-app purchases. */
42
+ purchase?: {
43
+ productId: string;
44
+ amount: number;
45
+ currency: string;
46
+ transactionId: string;
47
+ receiptData?: string;
48
+ isRestored?: boolean;
49
+ };
50
+ /** Typed payload for subscription lifecycle. */
51
+ subscription?: {
52
+ productId: string;
53
+ action: "started" | "renewed" | "cancelled" | "expired" | "grace_period" | "paused" | "resumed" | "trial_started" | "trial_converted" | "trial_expired";
54
+ amount?: number;
55
+ currency?: string;
56
+ periodType?: "monthly" | "yearly" | "weekly" | "custom";
57
+ transactionId?: string;
58
+ expiresAt?: string;
59
+ isTrial?: boolean;
60
+ };
61
+ /** Unstructured additional context. */
62
+ data?: Record<string, unknown>;
63
+ }
64
+ /** Options for {@link ApexCapacitorPlugin.initialize}. */
65
+ export interface InitializeOptions {
66
+ /** Project key from the Apex dashboard. Required. */
67
+ projectKey: string;
68
+ /** API base URL. Defaults to `https://api.apex.inc`. */
69
+ apiUrl?: string;
70
+ /** Tag subsequent events as test mode (excluded from production analytics). */
71
+ testMode?: boolean;
72
+ /** Session timeout in minutes. Default 30. */
73
+ sessionTimeoutMinutes?: number;
74
+ /** Maximum queued events before FIFO eviction kicks in. Default 1000. */
75
+ offlineQueueMaxSize?: number;
76
+ /** Enable verbose console logging. Default false. */
77
+ debug?: boolean;
78
+ }
79
+ /** Return value of {@link ApexCapacitorPlugin.getAdvertisingId}. */
80
+ export interface AdvertisingIdResult {
81
+ /** The advertising identifier, or null when completely unavailable. */
82
+ id: string | null;
83
+ /** When `id` is null/zero, indicates which fallback is in use. */
84
+ fallback: AdvertisingIdFallback;
85
+ }
86
+ /** Device metadata returned by {@link ApexCapacitorPlugin.getDeviceInfo}. */
87
+ export interface DeviceInfo {
88
+ platform: "ios" | "android";
89
+ osVersion: string;
90
+ model: string;
91
+ appVersion: string;
92
+ bundleId: string;
93
+ timezone: string;
94
+ locale: string;
95
+ }
96
+ /** Summary of offline-queue state. */
97
+ export interface QueueStatus {
98
+ /** Number of events currently pending send. */
99
+ count: number;
100
+ /** ISO timestamp of the oldest queued event, or null if queue is empty. */
101
+ oldestEventAt: string | null;
102
+ }
103
+ /** Outcome of {@link ApexCapacitorPlugin.flushQueue}. */
104
+ export interface FlushResult {
105
+ /** Events successfully sent during this flush. */
106
+ flushed: number;
107
+ /** Events that remain in the queue (failed + not attempted). */
108
+ remaining: number;
109
+ }
110
+ /** Current session summary. */
111
+ export interface SessionInfo {
112
+ sessionId: string | null;
113
+ startedAt: string | null;
114
+ }
115
+ /**
116
+ * The primary plugin contract. Matches Phase 1b API surface locked in
117
+ * `plans/mobile-measurement-platform.md`.
118
+ */
119
+ export interface ApexCapacitorPlugin {
120
+ /**
121
+ * Initializes the plugin. Must be called once before any other method.
122
+ * Registers push notification handlers, seeds the offline queue from native
123
+ * storage, and starts the session manager.
124
+ */
125
+ initialize(options: InitializeOptions): Promise<void>;
126
+ /**
127
+ * Requests ATT permission on iOS (presents the system prompt once per
128
+ * install). No-op on Android — returns `"authorized"`.
129
+ */
130
+ requestTrackingAuthorization(): Promise<{
131
+ status: AttStatus;
132
+ }>;
133
+ /**
134
+ * Returns the current ATT status without prompting. Android always returns
135
+ * `"authorized"`.
136
+ */
137
+ getTrackingStatus(): Promise<{
138
+ status: AttStatus;
139
+ }>;
140
+ /**
141
+ * Returns IDFA (iOS) or GAID (Android). When unavailable (ATT denied or LAT
142
+ * enabled), returns a fallback with `fallback` indicating which identifier
143
+ * was substituted.
144
+ */
145
+ getAdvertisingId(): Promise<AdvertisingIdResult>;
146
+ /**
147
+ * Android: returns decoded Play Install Referrer string on first launch.
148
+ * iOS: returns `{ referrer: null }` (no equivalent).
149
+ */
150
+ getInstallReferrer(): Promise<{
151
+ referrer: string | null;
152
+ }>;
153
+ /** Gets the plugin-managed visitor ID. */
154
+ getVisitorId(): Promise<{
155
+ visitorId: string;
156
+ }>;
157
+ /** Overrides the visitor ID (e.g. after login). Persists to native storage. */
158
+ setVisitorId(options: {
159
+ visitorId: string;
160
+ }): Promise<void>;
161
+ /**
162
+ * Updates the SKAdNetwork 4.0 postback conversion value. No-op on Android.
163
+ * `fineValue` is 0-63 (fine conversion value); `coarseValue` is optional
164
+ * for SKAN 4.0's coarse-grained reporting.
165
+ */
166
+ updateConversionValue(options: {
167
+ fineValue: number;
168
+ coarseValue?: SkanCoarseValue;
169
+ }): Promise<void>;
170
+ /**
171
+ * Returns the URL the app was opened with on cold start (Universal Link on
172
+ * iOS, App Link on Android). Returns `{ url: null }` if launched without a
173
+ * link. Use the `deepLink` listener for subsequent warm-start links.
174
+ */
175
+ getInitialDeepLink(): Promise<{
176
+ url: string | null;
177
+ }>;
178
+ getDeviceInfo(): Promise<DeviceInfo>;
179
+ /** Force-starts a new session, ending any current one. */
180
+ startSession(): Promise<{
181
+ sessionId: string;
182
+ }>;
183
+ /** Ends the current session immediately. */
184
+ endSession(): Promise<void>;
185
+ /** Returns info about the current session, or null fields if none. */
186
+ getCurrentSession(): Promise<SessionInfo>;
187
+ /**
188
+ * Enqueues an event for delivery. If network is available, events are
189
+ * batched and sent within ~1s; otherwise they persist in the offline queue
190
+ * until `flushQueue()` succeeds.
191
+ */
192
+ track(event: ApexEvent): Promise<void>;
193
+ /** Current offline queue status (event count + oldest timestamp). */
194
+ getQueueSize(): Promise<QueueStatus>;
195
+ /** Attempts to send all queued events. Returns counts of flushed + remaining. */
196
+ flushQueue(): Promise<FlushResult>;
197
+ /**
198
+ * Toggles test mode at runtime. Test-mode events are tagged so the server
199
+ * routes them to the TESTEVT# store, excluded from production analytics.
200
+ */
201
+ setTestMode(options: {
202
+ enabled: boolean;
203
+ }): Promise<void>;
204
+ /** Fires when a deep link opens the app while it is already running. */
205
+ addListener(eventName: "deepLink", listenerFunc: (event: {
206
+ url: string;
207
+ }) => void): Promise<PluginListenerHandle>;
208
+ /** Fires when the app moves between foreground and background. */
209
+ addListener(eventName: "appStateChange", listenerFunc: (event: {
210
+ isActive: boolean;
211
+ }) => void): Promise<PluginListenerHandle>;
212
+ /** Fires when a new session starts. */
213
+ addListener(eventName: "sessionStart", listenerFunc: (event: {
214
+ sessionId: string;
215
+ }) => void): Promise<PluginListenerHandle>;
216
+ /** Fires when the current session ends. */
217
+ addListener(eventName: "sessionEnd", listenerFunc: (event: {
218
+ sessionId: string;
219
+ durationSeconds: number;
220
+ }) => void): Promise<PluginListenerHandle>;
221
+ /** Removes all listeners registered by this plugin instance. */
222
+ removeAllListeners(): Promise<void>;
223
+ }
224
+ //# sourceMappingURL=definitions.d.ts.map