@amplytools/react-native-amply-sdk 0.13.0 → 0.15.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 (45) hide show
  1. package/AmplyReactNative.podspec +1 -1
  2. package/README.md +47 -0
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/java/tools/amply/sdk/reactnative/AmplyModule.kt +30 -0
  5. package/android/src/main/java/tools/amply/sdk/reactnative/core/AmplyClient.kt +13 -0
  6. package/android/src/main/java/tools/amply/sdk/reactnative/core/DefaultAmplyClient.kt +46 -0
  7. package/android/src/main/java/tools/amply/sdk/reactnative/model/AmplyInitializationOptions.kt +2 -0
  8. package/android/src/main/java/tools/amply/sdk/reactnative/model/CampaignTouchPayload.kt +12 -0
  9. package/android/src/newarch/java/tools/amply/sdk/reactnative/NativeAmplyModuleSpec.java +12 -0
  10. package/android/src/newarch/jni/AmplyReactNative-generated.cpp +18 -0
  11. package/dist/src/__tests__/appLanguage.test.d.ts +2 -0
  12. package/dist/src/__tests__/appLanguage.test.d.ts.map +1 -0
  13. package/dist/src/__tests__/appLanguage.test.js +211 -0
  14. package/dist/src/__tests__/handleUrl.test.d.ts +2 -0
  15. package/dist/src/__tests__/handleUrl.test.d.ts.map +1 -0
  16. package/dist/src/__tests__/handleUrl.test.js +114 -0
  17. package/dist/src/__tests__/lastCampaignTouch.test.d.ts +2 -0
  18. package/dist/src/__tests__/lastCampaignTouch.test.d.ts.map +1 -0
  19. package/dist/src/__tests__/lastCampaignTouch.test.js +119 -0
  20. package/dist/src/__tests__/systemEventUtils.test.js +16 -0
  21. package/dist/src/campaignTouch.d.ts +36 -0
  22. package/dist/src/campaignTouch.d.ts.map +1 -0
  23. package/dist/src/campaignTouch.js +39 -0
  24. package/dist/src/index.d.ts +50 -0
  25. package/dist/src/index.d.ts.map +1 -1
  26. package/dist/src/index.js +198 -1
  27. package/dist/src/nativeSpecs/NativeAmplyModule.d.ts +40 -0
  28. package/dist/src/nativeSpecs/NativeAmplyModule.d.ts.map +1 -1
  29. package/dist/src/systemEventUtils.d.ts.map +1 -1
  30. package/dist/src/systemEventUtils.js +3 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/docs/ARCHITECTURE.md +4 -0
  33. package/docs/DEVELOPMENT.md +2 -1
  34. package/ios/Sources/AmplyReactNative/AmplyModule.mm +81 -0
  35. package/ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative-generated.mm +21 -0
  36. package/ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative.h +11 -0
  37. package/package.json +1 -1
  38. package/src/__tests__/appLanguage.test.ts +298 -0
  39. package/src/__tests__/handleUrl.test.ts +155 -0
  40. package/src/__tests__/lastCampaignTouch.test.ts +151 -0
  41. package/src/__tests__/systemEventUtils.test.ts +22 -0
  42. package/src/campaignTouch.ts +74 -0
  43. package/src/index.ts +223 -1
  44. package/src/nativeSpecs/NativeAmplyModule.ts +41 -0
  45. package/src/systemEventUtils.ts +3 -1
@@ -0,0 +1,114 @@
1
+ function loadWith(native) {
2
+ // Absent isInitialized means "SDK up" for the plain pass-through tests below.
3
+ const withState = { isInitialized: () => true, ...native };
4
+ let Amply;
5
+ jest.isolateModules(() => {
6
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ require('../nativeModule').__setNativeModule(withState);
8
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
9
+ Amply = require('../index');
10
+ });
11
+ return Amply;
12
+ }
13
+ const DEBUG_URL = 'myapp://amply-debug/campaign?t=abc';
14
+ describe('handleUrl', () => {
15
+ it('passes the DEBUG_URL to the native module and returns its answer', () => {
16
+ const handleUrl = jest.fn(() => true);
17
+ const Amply = loadWith({ handleUrl });
18
+ expect(Amply.handleUrl(DEBUG_URL)).toBe(true);
19
+ expect(handleUrl).toHaveBeenCalledWith(DEBUG_URL);
20
+ });
21
+ it('returns false when the native module does not recognise the DEBUG_URL', () => {
22
+ const Amply = loadWith({ handleUrl: jest.fn(() => false) });
23
+ expect(Amply.handleUrl('myapp://settings')).toBe(false);
24
+ });
25
+ it('returns false without throwing when the native module has no handleUrl', () => {
26
+ const Amply = loadWith({});
27
+ expect(Amply.handleUrl(DEBUG_URL)).toBe(false);
28
+ });
29
+ it('returns false without throwing when the native call throws', () => {
30
+ const Amply = loadWith({
31
+ handleUrl: jest.fn(() => {
32
+ throw new Error('boom');
33
+ }),
34
+ });
35
+ expect(Amply.handleUrl(DEBUG_URL)).toBe(false);
36
+ });
37
+ it('returns false for a non-string DEBUG_URL', () => {
38
+ const handleUrl = jest.fn(() => true);
39
+ const Amply = loadWith({ handleUrl });
40
+ expect(Amply.handleUrl(null)).toBe(false);
41
+ expect(handleUrl).not.toHaveBeenCalled();
42
+ });
43
+ it('is on the default export', () => {
44
+ const Amply = loadWith({ handleUrl: jest.fn(() => true) });
45
+ expect(Amply.default.handleUrl(DEBUG_URL)).toBe(true);
46
+ });
47
+ });
48
+ describe('handleUrl before initialize', () => {
49
+ function loadUninitialised(initialize) {
50
+ let up = false;
51
+ const handleUrl = jest.fn(() => true);
52
+ const native = {
53
+ handleUrl,
54
+ isInitialized: jest.fn(() => up),
55
+ initialize: jest.fn(initialize !== null && initialize !== void 0 ? initialize : (async () => {
56
+ up = true;
57
+ })),
58
+ addListener: jest.fn(),
59
+ removeListeners: jest.fn(),
60
+ };
61
+ return { Amply: loadWith(native), handleUrl };
62
+ }
63
+ const KEYS = { appId: 'id', apiKeyPublic: 'public', apiKeySecret: 'secret' };
64
+ it('parks a debug link, returns true and does not call native', () => {
65
+ const { Amply, handleUrl } = loadUninitialised();
66
+ expect(Amply.handleUrl(DEBUG_URL)).toBe(true);
67
+ expect(handleUrl).not.toHaveBeenCalled();
68
+ });
69
+ it('replays the parked link to native once after initialize', async () => {
70
+ const { Amply, handleUrl } = loadUninitialised();
71
+ Amply.handleUrl(DEBUG_URL);
72
+ await Amply.initialize(KEYS);
73
+ expect(handleUrl).toHaveBeenCalledTimes(1);
74
+ expect(handleUrl).toHaveBeenCalledWith(DEBUG_URL);
75
+ });
76
+ it('replays only the newer of two parked links', async () => {
77
+ const { Amply, handleUrl } = loadUninitialised();
78
+ Amply.handleUrl('myapp://amply-debug/campaign?t=old');
79
+ Amply.handleUrl('myapp://amply-debug/campaign?t=new');
80
+ await Amply.initialize(KEYS);
81
+ expect(handleUrl).toHaveBeenCalledTimes(1);
82
+ expect(handleUrl).toHaveBeenCalledWith('myapp://amply-debug/campaign?t=new');
83
+ });
84
+ it('does not park a non-debug link', async () => {
85
+ const { Amply, handleUrl } = loadUninitialised();
86
+ expect(Amply.handleUrl('myapp://settings')).toBe(false);
87
+ await Amply.initialize(KEYS);
88
+ expect(handleUrl).not.toHaveBeenCalled();
89
+ });
90
+ it('drops the parked link when initialize fails', async () => {
91
+ const { Amply, handleUrl } = loadUninitialised(async () => {
92
+ throw new Error('init failed');
93
+ });
94
+ Amply.handleUrl(DEBUG_URL);
95
+ await expect(Amply.initialize(KEYS)).rejects.toThrow('init failed');
96
+ expect(handleUrl).not.toHaveBeenCalled();
97
+ });
98
+ it.each([
99
+ '',
100
+ 'not a url',
101
+ 'myapp://amply-debug/campaign',
102
+ 'myapp://amply-debug/campaign?t=',
103
+ 'myapp://amply-debug/other?t=abc',
104
+ 'myapp://other/campaign?t=abc',
105
+ 'amply-debug/campaign?t=abc',
106
+ '://amply-debug/campaign?t=abc',
107
+ ])('returns false without throwing for %p', async (url) => {
108
+ const { Amply, handleUrl } = loadUninitialised();
109
+ expect(Amply.handleUrl(url)).toBe(false);
110
+ await Amply.initialize(KEYS);
111
+ expect(handleUrl).not.toHaveBeenCalled();
112
+ });
113
+ });
114
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=lastCampaignTouch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lastCampaignTouch.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lastCampaignTouch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,119 @@
1
+ import * as Amply from '../index';
2
+ import { __setNativeModule } from '../nativeModule';
3
+ const mockNativeModule = {
4
+ isInitialized: jest.fn(() => true),
5
+ getLastCampaignTouch: jest.fn(),
6
+ };
7
+ const ONE_HOUR_MS = 60 * 60 * 1000;
8
+ describe('getLastCampaignTouch', () => {
9
+ let warn;
10
+ beforeEach(() => {
11
+ __setNativeModule(mockNativeModule);
12
+ warn = jest.spyOn(console, 'warn').mockImplementation(() => { });
13
+ });
14
+ afterEach(() => {
15
+ warn.mockRestore();
16
+ jest.clearAllMocks();
17
+ __setNativeModule(null);
18
+ });
19
+ it('passes withinMs through to the native module and returns the touch', async () => {
20
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce({
21
+ campaignId: 'cmp_1',
22
+ campaignName: 'Spring offer',
23
+ kind: 'shown',
24
+ atMs: 1760000000000,
25
+ });
26
+ const touch = await Amply.getLastCampaignTouch(ONE_HOUR_MS);
27
+ expect(mockNativeModule.getLastCampaignTouch).toHaveBeenCalledWith(ONE_HOUR_MS);
28
+ expect(touch).toEqual({
29
+ campaignId: 'cmp_1',
30
+ campaignName: 'Spring offer',
31
+ kind: 'shown',
32
+ atMs: 1760000000000,
33
+ });
34
+ });
35
+ it('is exposed on the default export too', async () => {
36
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce(null);
37
+ await expect(Amply.default.getLastCampaignTouch(0)).resolves.toBeNull();
38
+ });
39
+ it('resolves null when the native side has no touch', async () => {
40
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce(null);
41
+ await expect(Amply.getLastCampaignTouch(ONE_HOUR_MS)).resolves.toBeNull();
42
+ });
43
+ it('resolves null when the native side resolves undefined', async () => {
44
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce(undefined);
45
+ await expect(Amply.getLastCampaignTouch(ONE_HOUR_MS)).resolves.toBeNull();
46
+ });
47
+ it.each([
48
+ ['shown', 'shown'],
49
+ ['engaged', 'engaged'],
50
+ ['SHOWN', 'shown'],
51
+ ['ENGAGED', 'engaged'],
52
+ ])('maps native kind %s to %s', async (nativeKind, expected) => {
53
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce({
54
+ campaignId: 'cmp_1',
55
+ campaignName: 'Spring offer',
56
+ kind: nativeKind,
57
+ atMs: 1,
58
+ });
59
+ const touch = await Amply.getLastCampaignTouch(ONE_HOUR_MS);
60
+ expect(touch === null || touch === void 0 ? void 0 : touch.kind).toBe(expected);
61
+ });
62
+ it('resolves null and warns for a kind this bridge does not know', async () => {
63
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce({
64
+ campaignId: 'cmp_1',
65
+ campaignName: 'Spring offer',
66
+ kind: 'clicked',
67
+ atMs: 1,
68
+ });
69
+ await expect(Amply.getLastCampaignTouch(ONE_HOUR_MS)).resolves.toBeNull();
70
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('getLastCampaignTouch'), expect.anything());
71
+ });
72
+ it.each([
73
+ ['a missing campaignId', { campaignName: 'x', kind: 'shown', atMs: 1 }],
74
+ ['a non-numeric atMs', { campaignId: 'c', campaignName: 'x', kind: 'shown', atMs: '1' }],
75
+ ])('resolves null for a payload with %s', async (_label, payload) => {
76
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce(payload);
77
+ await expect(Amply.getLastCampaignTouch(ONE_HOUR_MS)).resolves.toBeNull();
78
+ });
79
+ it('keeps an empty campaign name rather than dropping the touch', async () => {
80
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce({
81
+ campaignId: 'cmp_1',
82
+ campaignName: '',
83
+ kind: 'engaged',
84
+ atMs: 5,
85
+ });
86
+ await expect(Amply.getLastCampaignTouch(ONE_HOUR_MS)).resolves.toEqual({
87
+ campaignId: 'cmp_1',
88
+ campaignName: '',
89
+ kind: 'engaged',
90
+ atMs: 5,
91
+ });
92
+ });
93
+ it('resolves null instead of rejecting when the native call fails', async () => {
94
+ mockNativeModule.getLastCampaignTouch.mockRejectedValueOnce(new Error('bridge gone'));
95
+ await expect(Amply.getLastCampaignTouch(ONE_HOUR_MS)).resolves.toBeNull();
96
+ expect(warn).toHaveBeenCalled();
97
+ });
98
+ it('accepts zero', async () => {
99
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce(null);
100
+ await Amply.getLastCampaignTouch(0);
101
+ expect(mockNativeModule.getLastCampaignTouch).toHaveBeenCalledWith(0);
102
+ });
103
+ it('leaves a large window to the native clamp', async () => {
104
+ mockNativeModule.getLastCampaignTouch.mockResolvedValueOnce(null);
105
+ const ninetyDaysMs = 90 * 24 * ONE_HOUR_MS;
106
+ await Amply.getLastCampaignTouch(ninetyDaysMs);
107
+ expect(mockNativeModule.getLastCampaignTouch).toHaveBeenCalledWith(ninetyDaysMs);
108
+ });
109
+ it.each([
110
+ ['negative', -1],
111
+ ['NaN', Number.NaN],
112
+ ['Infinity', Number.POSITIVE_INFINITY],
113
+ ['a string', '3600000'],
114
+ ['undefined', undefined],
115
+ ])('rejects with a RangeError for %s and never calls native', async (_label, value) => {
116
+ await expect(Amply.getLastCampaignTouch(value)).rejects.toBeInstanceOf(RangeError);
117
+ expect(mockNativeModule.getLastCampaignTouch).not.toHaveBeenCalled();
118
+ });
119
+ });
@@ -29,6 +29,22 @@ describe('formatSystemEventLabel', () => {
29
29
  });
30
30
  expect(formatSystemEventLabel(event)).toBe('Campaign shown (abc) via click');
31
31
  });
32
+ it('names the campaign in CampaignShown when the SDK sends campaignName', () => {
33
+ const event = makeEvent('CampaignShown', {
34
+ campaignId: 'abc',
35
+ campaignName: 'Spring offer',
36
+ source: 'click',
37
+ });
38
+ expect(formatSystemEventLabel(event)).toBe('Campaign shown "Spring offer" (abc) via click');
39
+ });
40
+ it('ignores an empty campaignName in CampaignShown', () => {
41
+ const event = makeEvent('CampaignShown', {
42
+ campaignId: 'abc',
43
+ campaignName: '',
44
+ source: 'click',
45
+ });
46
+ expect(formatSystemEventLabel(event)).toBe('Campaign shown (abc) via click');
47
+ });
32
48
  it('formats EventTriggered with source event', () => {
33
49
  const event = makeEvent('EventTriggered', { sourceEvent: 'purchase' });
34
50
  expect(formatSystemEventLabel(event)).toBe('Event triggered (purchase)');
@@ -0,0 +1,36 @@
1
+ import type { CampaignTouchPayload } from './nativeSpecs/NativeAmplyModule';
2
+ /**
3
+ * - `'shown'`: the campaign was shown (`CampaignShown`).
4
+ * - `'engaged'`: the campaign resolved `Completed` (`CampaignResolved` with `result: 'Completed'`).
5
+ */
6
+ export type CampaignTouchKind = 'shown' | 'engaged';
7
+ /**
8
+ * The most recent moment a campaign reached the user, as answered by `getLastCampaignTouch`.
9
+ *
10
+ * Meant for your own analytics: attach `campaignId` / `campaignName` to a purchase (or any
11
+ * conversion) that happens shortly after a campaign, even when it did not start from the
12
+ * campaign's own link.
13
+ */
14
+ export type CampaignTouch = {
15
+ /**
16
+ * The id the SDK reports in `CampaignShown`. Inside an experiment that is the arm's own id —
17
+ * the same value `{@campaign.id}` resolves to, so it is also the right value for an explicit
18
+ * `amply_campaign_id` mark.
19
+ */
20
+ campaignId: string;
21
+ /** The campaign's name when the touch was recorded. Empty if the event carried no name. */
22
+ campaignName: string;
23
+ /** One touch per campaign: the newer of a show and a completion wins. */
24
+ kind: CampaignTouchKind;
25
+ /** When it happened, epoch milliseconds in the SDK's server-corrected clock. */
26
+ atMs: number;
27
+ };
28
+ /** Thrown (as a rejection) by `getLastCampaignTouch` for a window that is not a usable number. */
29
+ export declare function assertValidWithinMs(withinMs: unknown): asserts withinMs is number;
30
+ /**
31
+ * Narrows what the native bridge resolved to the public type. Anything it cannot read as a
32
+ * touch becomes null with a warning: the caller asked "was there a campaign", and a payload this
33
+ * bridge does not understand is not one it can vouch for.
34
+ */
35
+ export declare function toCampaignTouch(payload: CampaignTouchPayload | null | undefined): CampaignTouch | null;
36
+ //# sourceMappingURL=campaignTouch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaignTouch.d.ts","sourceRoot":"","sources":["../../src/campaignTouch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,iCAAiC,CAAC;AAE1E;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,IAAI,EAAE,iBAAiB,CAAC;IACxB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,MAAM,CAMjF;AAUD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,GAAG,IAAI,CAqBtG"}
@@ -0,0 +1,39 @@
1
+ /** Thrown (as a rejection) by `getLastCampaignTouch` for a window that is not a usable number. */
2
+ export function assertValidWithinMs(withinMs) {
3
+ if (typeof withinMs !== 'number' || !Number.isFinite(withinMs) || withinMs < 0) {
4
+ throw new RangeError(`[Amply] getLastCampaignTouch: withinMs must be a finite number >= 0, got ${String(withinMs)}`);
5
+ }
6
+ }
7
+ function toKind(raw) {
8
+ if (typeof raw !== 'string') {
9
+ return null;
10
+ }
11
+ const lowered = raw.toLowerCase();
12
+ return lowered === 'shown' || lowered === 'engaged' ? lowered : null;
13
+ }
14
+ /**
15
+ * Narrows what the native bridge resolved to the public type. Anything it cannot read as a
16
+ * touch becomes null with a warning: the caller asked "was there a campaign", and a payload this
17
+ * bridge does not understand is not one it can vouch for.
18
+ */
19
+ export function toCampaignTouch(payload) {
20
+ if (payload == null) {
21
+ return null;
22
+ }
23
+ const kind = toKind(payload.kind);
24
+ const isReadable = kind !== null &&
25
+ typeof payload.campaignId === 'string' &&
26
+ payload.campaignId.length > 0 &&
27
+ typeof payload.atMs === 'number' &&
28
+ Number.isFinite(payload.atMs);
29
+ if (!isReadable) {
30
+ console.warn('[Amply] getLastCampaignTouch: ignoring an unreadable native payload', payload);
31
+ return null;
32
+ }
33
+ return {
34
+ campaignId: payload.campaignId,
35
+ campaignName: typeof payload.campaignName === 'string' ? payload.campaignName : '',
36
+ kind,
37
+ atMs: payload.atMs,
38
+ };
39
+ }
@@ -1,6 +1,8 @@
1
1
  export { useAmplySystemEvents } from './hooks/useAmplySystemEvents';
2
2
  export { formatSystemEventLabel } from './systemEventUtils';
3
3
  export type { FormatOptions } from './systemEventUtils';
4
+ import type { CampaignTouch } from './campaignTouch';
5
+ export type { CampaignTouch, CampaignTouchKind } from './campaignTouch';
4
6
  import type { CampaignResult, GateDecision, AmplyInitializationConfig as NativeInitializationConfig, DataSetSnapshot, DataSetType, DeepLinkEvent, EventRecord, LogLevel, CampaignPresentEvent, TrackEventPayload } from './nativeSpecs/NativeAmplyModule';
5
7
  /**
6
8
  * The keys from your application's page in Amply. All three are required: the SDK signs every
@@ -17,6 +19,33 @@ export declare class AmplyConfigError extends Error {
17
19
  constructor(message: string);
18
20
  }
19
21
  export declare function initialize(config: AmplyInitializationConfig): Promise<void>;
22
+ /**
23
+ * Tell Amply the language your app's UI is in, so sheets, stories and mini-apps show in the
24
+ * matching language and campaigns can target it. Use this when the app chooses its language
25
+ * itself (an in-app language picker, i18next, …) rather than following the device.
26
+ *
27
+ * Any common tag format works — `'pt_BR'`, `'pt-BR'`, `'zh-TW'`, `'fr'`, any case. A value that
28
+ * is not a language tag is ignored by the SDK with a warning log. The value is persisted across
29
+ * launches; pass `null` to return to automatic detection. The device language is never changed.
30
+ *
31
+ * Safe to call before {@link initialize}: the value is held and applied at initialization, and
32
+ * an `appLanguage` passed to a later `initialize` call wins over it.
33
+ * @param tag The language tag, or null to clear
34
+ */
35
+ export declare function setAppLanguage(tag: string | null): void;
36
+ /**
37
+ * Hand an incoming URL to Amply. Call it for every link your app is opened with — cold start
38
+ * and while running — before your own routing; when it returns `true` Amply recognised the
39
+ * link as its own and took it over, so your app should not route it.
40
+ *
41
+ * Safe to call before {@link initialize}, which is how a cold-start link arrives: an Amply debug
42
+ * link is held (the newest one) and opened once initialization succeeds, and `true` is returned.
43
+ *
44
+ * Synchronous. Returns `false` for any other URL, for a non-string, and when the installed
45
+ * native SDK predates this method; it never throws.
46
+ * @param url The full incoming URL
47
+ */
48
+ export declare function handleUrl(url: string): boolean;
20
49
  /**
21
50
  * Set the user ID for analytics. Pass null to clear.
22
51
  * @param userId The user ID to set, or null to clear
@@ -131,6 +160,24 @@ export declare function trackGated(event: string, properties?: Record<string, un
131
160
  */
132
161
  export declare function registerGate(baseUrl: string, presenter: GatePresenter, opts?: RegisterGateOptions): Promise<() => void>;
133
162
  export declare function getRecentEvents(limit: number): Promise<EventRecord[]>;
163
+ /**
164
+ * The campaign the user was most recently touched by — shown, or completed — at most `withinMs`
165
+ * ago, or `null` if there was none.
166
+ *
167
+ * Use it to attribute a purchase (or any conversion) in your own analytics to the campaign that
168
+ * preceded it, including one made from somewhere other than the campaign's link:
169
+ *
170
+ * ```ts
171
+ * const touch = await Amply.getLastCampaignTouch(30 * 60 * 1000);
172
+ * analytics.track('Purchase', {sku, amply_campaign_id: touch?.campaignId});
173
+ * ```
174
+ *
175
+ * One touch per campaign, the newer of a show and a completion. `withinMs` is inclusive and
176
+ * capped at 30 days by the SDK. Resolves `null` before {@link initialize}, while the SDK's storage
177
+ * is off, and if the native call fails — it rejects only for a `withinMs` that is not a finite
178
+ * number >= 0, which is a bug at the call site.
179
+ */
180
+ export declare function getLastCampaignTouch(withinMs: number): Promise<CampaignTouch | null>;
134
181
  export declare function getDataSetSnapshot(type: DataSetType): Promise<DataSetSnapshot>;
135
182
  export declare function addDeepLinkListener(listener: (event: DeepLinkEvent) => void): Promise<() => void>;
136
183
  export declare function addSystemEventListener(listener: (event: EventRecord) => void): Promise<() => void>;
@@ -178,12 +225,15 @@ declare const _default: {
178
225
  trackGated: typeof trackGated;
179
226
  registerGate: typeof registerGate;
180
227
  getRecentEvents: typeof getRecentEvents;
228
+ getLastCampaignTouch: typeof getLastCampaignTouch;
181
229
  getDataSetSnapshot: typeof getDataSetSnapshot;
182
230
  addDeepLinkListener: typeof addDeepLinkListener;
183
231
  addSystemEventListener: typeof addSystemEventListener;
184
232
  addSystemEventsListener: typeof addSystemEventsListener;
185
233
  removeAllListeners: typeof removeAllListeners;
186
234
  setUserId: typeof setUserId;
235
+ setAppLanguage: typeof setAppLanguage;
236
+ handleUrl: typeof handleUrl;
187
237
  setLogLevel: typeof setLogLevel;
188
238
  getLogLevel: typeof getLogLevel;
189
239
  setCustomProperty: typeof setCustomProperty;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}