@digia-engage/core 2.0.0-rc.1 → 2.0.0-rc.2

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 (39) hide show
  1. package/android/build.gradle +1 -1
  2. package/lib/commonjs/Digia.js +90 -2
  3. package/lib/commonjs/Digia.js.map +1 -1
  4. package/lib/commonjs/DigiaAnchorView.js +35 -3
  5. package/lib/commonjs/DigiaAnchorView.js.map +1 -1
  6. package/lib/commonjs/DigiaProvider.js +2 -0
  7. package/lib/commonjs/DigiaProvider.js.map +1 -1
  8. package/lib/commonjs/frequencyEvaluator.js +70 -0
  9. package/lib/commonjs/frequencyEvaluator.js.map +1 -0
  10. package/lib/commonjs/frequencyStore.js +70 -0
  11. package/lib/commonjs/frequencyStore.js.map +1 -0
  12. package/lib/module/Digia.js +89 -2
  13. package/lib/module/Digia.js.map +1 -1
  14. package/lib/module/DigiaAnchorView.js +33 -1
  15. package/lib/module/DigiaAnchorView.js.map +1 -1
  16. package/lib/module/DigiaProvider.js +2 -0
  17. package/lib/module/DigiaProvider.js.map +1 -1
  18. package/lib/module/frequencyEvaluator.js +61 -0
  19. package/lib/module/frequencyEvaluator.js.map +1 -0
  20. package/lib/module/frequencyStore.js +64 -0
  21. package/lib/module/frequencyStore.js.map +1 -0
  22. package/lib/typescript/Digia.d.ts +6 -1
  23. package/lib/typescript/Digia.d.ts.map +1 -1
  24. package/lib/typescript/DigiaAnchorView.d.ts +5 -1
  25. package/lib/typescript/DigiaAnchorView.d.ts.map +1 -1
  26. package/lib/typescript/DigiaProvider.d.ts.map +1 -1
  27. package/lib/typescript/frequencyEvaluator.d.ts +14 -0
  28. package/lib/typescript/frequencyEvaluator.d.ts.map +1 -0
  29. package/lib/typescript/frequencyStore.d.ts +7 -0
  30. package/lib/typescript/frequencyStore.d.ts.map +1 -0
  31. package/lib/typescript/types.d.ts +23 -1
  32. package/lib/typescript/types.d.ts.map +1 -1
  33. package/package.json +5 -1
  34. package/src/Digia.ts +99 -1
  35. package/src/DigiaAnchorView.tsx +30 -2
  36. package/src/DigiaProvider.tsx +2 -0
  37. package/src/frequencyEvaluator.ts +57 -0
  38. package/src/frequencyStore.ts +79 -0
  39. package/src/types.ts +30 -1
@@ -0,0 +1,79 @@
1
+ import type { FrequencyState } from './types';
2
+
3
+ // eslint-disable-next-line @typescript-eslint/naming-convention
4
+ const STORE_META_KEY = 'digia:freq:__meta__';
5
+
6
+ type AsyncStorageAdapter = {
7
+ getItem(key: string): Promise<string | null>;
8
+ setItem(key: string, value: string): Promise<void>;
9
+ removeItem(key: string): Promise<void>;
10
+ getAllKeys(): Promise<readonly string[]>;
11
+ multiRemove(keys: string[]): Promise<void>;
12
+ };
13
+
14
+ let _storage: AsyncStorageAdapter | null = null;
15
+
16
+ const _loadStorage = (): AsyncStorageAdapter | null => {
17
+ try {
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ const mod = require('@react-native-async-storage/async-storage');
20
+ return mod.default ?? mod;
21
+ } catch {
22
+ console.warn('[Digia] AsyncStorage unavailable — frequency state is in-memory only (resets on app restart)');
23
+ return null;
24
+ }
25
+ };
26
+
27
+ const getStorage = (): AsyncStorageAdapter | null => {
28
+ if (_storage === undefined) {
29
+ _storage = _loadStorage();
30
+ }
31
+ return _storage;
32
+ };
33
+
34
+ const _sessionStore = new Map<string, FrequencyState>();
35
+
36
+ const storeKey = (campaignKey: string) => `digia:freq:${campaignKey}`;
37
+
38
+ export const frequencyStore = {
39
+ async checkProjectId(projectId: string): Promise<void> {
40
+ const storage = getStorage();
41
+ if (!storage) return;
42
+ try {
43
+ const stored = await storage.getItem(STORE_META_KEY);
44
+ const meta = stored ? (JSON.parse(stored) as { projectId: string }) : null;
45
+ if (meta && meta.projectId !== projectId) {
46
+ const keys = await storage.getAllKeys();
47
+ const digiaKeys = keys.filter((k) => k.startsWith('digia:freq:'));
48
+ if (digiaKeys.length > 0) await storage.multiRemove([...digiaKeys]);
49
+ }
50
+ await storage.setItem(STORE_META_KEY, JSON.stringify({ projectId }));
51
+ } catch {
52
+ // non-fatal
53
+ }
54
+ },
55
+
56
+ async get(campaignKey: string, isSession: boolean): Promise<FrequencyState | null> {
57
+ if (isSession) return _sessionStore.get(campaignKey) ?? null;
58
+ const storage = getStorage();
59
+ if (!storage) return _sessionStore.get(campaignKey) ?? null;
60
+ try {
61
+ const raw = await storage.getItem(storeKey(campaignKey));
62
+ return raw ? (JSON.parse(raw) as FrequencyState) : null;
63
+ } catch {
64
+ return null;
65
+ }
66
+ },
67
+
68
+ async set(campaignKey: string, state: FrequencyState, isSession: boolean): Promise<void> {
69
+ _sessionStore.set(campaignKey, state);
70
+ if (isSession) return;
71
+ const storage = getStorage();
72
+ if (!storage) return;
73
+ try {
74
+ await storage.setItem(storeKey(campaignKey), JSON.stringify(state));
75
+ } catch {
76
+ // non-fatal: state already updated in-memory above
77
+ }
78
+ },
79
+ };
package/src/types.ts CHANGED
@@ -70,7 +70,7 @@ export type GuideLifecycleEvent =
70
70
  */
71
71
  export interface DigiaDelegate {
72
72
  /** Deliver a campaign payload into the Digia rendering engine. */
73
- onCampaignTriggered(payload: InAppPayload): void;
73
+ onCampaignTriggered(payload: InAppPayload): void | Promise<void>;
74
74
  /** Invalidate / dismiss a campaign by its ID. */
75
75
  onCampaignInvalidated(campaignId: string): void;
76
76
  }
@@ -134,6 +134,35 @@ export type InAppBrowserAdapter = {
134
134
  open: (url: string) => Promise<void>;
135
135
  };
136
136
 
137
+ // ─── Frequency capping ────────────────────────────────────────────────────────
138
+
139
+ export interface FrequencyWindow {
140
+ count: number;
141
+ window: 'session' | 'day' | 'week' | 'month';
142
+ }
143
+
144
+ export interface FrequencyPolicy {
145
+ max_total: number | null;
146
+ max_per_window: FrequencyWindow | null;
147
+ stop_on: 'click' | 'dismiss' | 'any_action' | null;
148
+ min_gap_ms?: number | null; // reserved — not evaluated in v1
149
+ }
150
+
151
+ export interface FrequencyState {
152
+ shown_count: number;
153
+ first_shown_at: number | null; // ms timestamp — set on first impression
154
+ last_shown_at: number | null; // ms timestamp — reserved for min_gap_ms
155
+ stopped_at: number | null;
156
+ stopped_reason: string | null;
157
+ }
158
+
159
+ export type FrequencySkipReason = 'max_total' | 'window' | 'stopped';
160
+
161
+ export interface FrequencyEvalResult {
162
+ allow: boolean;
163
+ reason: FrequencySkipReason | null;
164
+ }
165
+
137
166
  // ─── SDK init config ──────────────────────────────────────────────────────────
138
167
 
139
168
  /**