@rocapine/rocalytics-sdk 0.0.0-stage → 0.2.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/CHANGELOG.md +79 -0
- package/LICENSE +21 -0
- package/README.md +447 -2
- package/client/package.json +1 -0
- package/core/package.json +4 -0
- package/dist/client/client.d.ts +136 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +373 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +13 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +35 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/native.d.ts +68 -0
- package/dist/client/native.d.ts.map +1 -0
- package/dist/client/native.js +64 -0
- package/dist/client/native.js.map +1 -0
- package/dist/client/onboardingSink.d.ts +63 -0
- package/dist/client/onboardingSink.d.ts.map +1 -0
- package/dist/client/onboardingSink.js +126 -0
- package/dist/client/onboardingSink.js.map +1 -0
- package/dist/client/paywallSink.d.ts +13 -0
- package/dist/client/paywallSink.d.ts.map +1 -0
- package/dist/client/paywallSink.js +26 -0
- package/dist/client/paywallSink.js.map +1 -0
- package/dist/client/requests.d.ts +66 -0
- package/dist/client/requests.d.ts.map +1 -0
- package/dist/client/requests.js +90 -0
- package/dist/client/requests.js.map +1 -0
- package/dist/client/types.d.ts +180 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +4 -0
- package/dist/client/types.js.map +1 -0
- package/dist/core/context.d.ts +39 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +45 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/delivery.d.ts +63 -0
- package/dist/core/delivery.d.ts.map +1 -0
- package/dist/core/delivery.js +133 -0
- package/dist/core/delivery.js.map +1 -0
- package/dist/core/diagnostics.d.ts +16 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +24 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/httpSink.d.ts +35 -0
- package/dist/core/httpSink.d.ts.map +1 -0
- package/dist/core/httpSink.js +65 -0
- package/dist/core/httpSink.js.map +1 -0
- package/dist/core/ids.d.ts +16 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +51 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +32 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/sink.d.ts +39 -0
- package/dist/core/sink.d.ts.map +1 -0
- package/dist/core/sink.js +16 -0
- package/dist/core/sink.js.map +1 -0
- package/dist/core/storage.d.ts +52 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +79 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/text.d.ts +9 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +47 -0
- package/dist/core/text.js.map +1 -0
- package/dist/core/time.d.ts +18 -0
- package/dist/core/time.d.ts.map +1 -0
- package/dist/core/time.js +18 -0
- package/dist/core/time.js.map +1 -0
- package/dist/onboarding/contract.d.ts +138 -0
- package/dist/onboarding/contract.d.ts.map +1 -0
- package/dist/onboarding/contract.js +11 -0
- package/dist/onboarding/contract.js.map +1 -0
- package/dist/onboarding/index.d.ts +15 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/index.js +22 -0
- package/dist/onboarding/index.js.map +1 -0
- package/dist/onboarding/input.d.ts +102 -0
- package/dist/onboarding/input.d.ts.map +1 -0
- package/dist/onboarding/input.js +216 -0
- package/dist/onboarding/input.js.map +1 -0
- package/dist/onboarding/persisted.d.ts +20 -0
- package/dist/onboarding/persisted.d.ts.map +1 -0
- package/dist/onboarding/persisted.js +75 -0
- package/dist/onboarding/persisted.js.map +1 -0
- package/dist/onboarding/state.d.ts +65 -0
- package/dist/onboarding/state.d.ts.map +1 -0
- package/dist/onboarding/state.js +207 -0
- package/dist/onboarding/state.js.map +1 -0
- package/dist/onboarding/tracker.d.ts +135 -0
- package/dist/onboarding/tracker.d.ts.map +1 -0
- package/dist/onboarding/tracker.js +734 -0
- package/dist/onboarding/tracker.js.map +1 -0
- package/dist/paywall/contract.d.ts +67 -0
- package/dist/paywall/contract.d.ts.map +1 -0
- package/dist/paywall/contract.js +3 -0
- package/dist/paywall/contract.js.map +1 -0
- package/dist/paywall/index.d.ts +14 -0
- package/dist/paywall/index.d.ts.map +1 -0
- package/dist/paywall/index.js +19 -0
- package/dist/paywall/index.js.map +1 -0
- package/dist/paywall/observer.d.ts +38 -0
- package/dist/paywall/observer.d.ts.map +1 -0
- package/dist/paywall/observer.js +6 -0
- package/dist/paywall/observer.js.map +1 -0
- package/dist/paywall/state.d.ts +26 -0
- package/dist/paywall/state.d.ts.map +1 -0
- package/dist/paywall/state.js +132 -0
- package/dist/paywall/state.js.map +1 -0
- package/dist/paywall/tracker.d.ts +36 -0
- package/dist/paywall/tracker.d.ts.map +1 -0
- package/dist/paywall/tracker.js +180 -0
- package/dist/paywall/tracker.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/docs/onboarding-run-contract.md +1013 -0
- package/docs/onboarding-run.examples.ts +306 -0
- package/docs/onboarding-run.schema.json +224 -0
- package/docs/onboarding-run.types.ts +144 -0
- package/docs/paywall-presentation-contract.md +139 -0
- package/docs/paywall-presentation.examples.ts +42 -0
- package/docs/paywall-presentation.schema.json +268 -0
- package/docs/paywall-presentation.types.ts +73 -0
- package/onboarding/package.json +4 -0
- package/package.json +120 -4
- package/paywall/package.json +1 -0
- package/src/client/client.ts +405 -0
- package/src/client/index.ts +54 -0
- package/src/client/native.ts +109 -0
- package/src/client/onboardingSink.ts +121 -0
- package/src/client/paywallSink.ts +25 -0
- package/src/client/requests.ts +126 -0
- package/src/client/types.ts +198 -0
- package/src/core/context.ts +69 -0
- package/src/core/delivery.ts +181 -0
- package/src/core/diagnostics.ts +30 -0
- package/src/core/httpSink.ts +70 -0
- package/src/core/ids.ts +46 -0
- package/src/core/index.ts +20 -0
- package/src/core/sink.ts +46 -0
- package/src/core/storage.ts +112 -0
- package/src/core/text.ts +37 -0
- package/src/core/time.ts +26 -0
- package/src/onboarding/contract.ts +144 -0
- package/src/onboarding/index.ts +39 -0
- package/src/onboarding/input.ts +246 -0
- package/src/onboarding/persisted.ts +93 -0
- package/src/onboarding/state.ts +233 -0
- package/src/onboarding/tracker.ts +855 -0
- package/src/paywall/contract.ts +73 -0
- package/src/paywall/index.ts +36 -0
- package/src/paywall/observer.ts +43 -0
- package/src/paywall/state.ts +125 -0
- package/src/paywall/tracker.ts +200 -0
- package/src/version.ts +2 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** The members of `expo-application` the client reads. The module itself fits as is. */
|
|
2
|
+
export interface ApplicationModule {
|
|
3
|
+
readonly applicationId: string | null;
|
|
4
|
+
readonly applicationName: string | null;
|
|
5
|
+
readonly nativeApplicationVersion: string | null;
|
|
6
|
+
readonly nativeBuildVersion: string | null;
|
|
7
|
+
getIosIdForVendorAsync(): Promise<string | null>;
|
|
8
|
+
getAndroidId(): string;
|
|
9
|
+
getInstallationTimeAsync(): Promise<Date>;
|
|
10
|
+
}
|
|
11
|
+
/** The native capabilities the client uses, shaped like the Expo modules and react-native, which fit as they are. */
|
|
12
|
+
export interface RocalyticsModules {
|
|
13
|
+
application: ApplicationModule;
|
|
14
|
+
crypto: {
|
|
15
|
+
randomUUID(): string;
|
|
16
|
+
};
|
|
17
|
+
device: {
|
|
18
|
+
readonly brand: string | null;
|
|
19
|
+
readonly modelName: string | null;
|
|
20
|
+
readonly manufacturer: string | null;
|
|
21
|
+
readonly osName: string | null;
|
|
22
|
+
readonly osVersion: string | null;
|
|
23
|
+
};
|
|
24
|
+
network: {
|
|
25
|
+
getIpAddressAsync(): Promise<string>;
|
|
26
|
+
};
|
|
27
|
+
secureStore: {
|
|
28
|
+
getItemAsync(key: string): Promise<string | null>;
|
|
29
|
+
setItemAsync(key: string, value: string): Promise<void>;
|
|
30
|
+
};
|
|
31
|
+
/** react-native's `Platform`. */
|
|
32
|
+
platform: {
|
|
33
|
+
readonly OS: string;
|
|
34
|
+
readonly Version: string | number;
|
|
35
|
+
};
|
|
36
|
+
/** react-native's `Dimensions`. */
|
|
37
|
+
dimensions: {
|
|
38
|
+
get(dim: "screen"): {
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
scale: number;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** How each package is loaded. Injected by tests; the defaults are the literal requires. */
|
|
46
|
+
export interface ExpoModuleLoaders {
|
|
47
|
+
core: () => unknown;
|
|
48
|
+
application: () => unknown;
|
|
49
|
+
crypto: () => unknown;
|
|
50
|
+
device: () => unknown;
|
|
51
|
+
network: () => unknown;
|
|
52
|
+
secureStore: () => unknown;
|
|
53
|
+
reactNative: () => unknown;
|
|
54
|
+
}
|
|
55
|
+
export type LoadResult = {
|
|
56
|
+
ok: true;
|
|
57
|
+
modules: RocalyticsModules;
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
reason: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Loads the Expo modules and react-native, or explains why it could not.
|
|
64
|
+
* Never throws: a missing package, a native module absent from the binary, or
|
|
65
|
+
* a wrapper that throws while loading all come back as `{ ok: false, reason }`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function loadExpoModules(loaders?: ExpoModuleLoaders): LoadResult;
|
|
68
|
+
//# sourceMappingURL=native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../src/client/native.ts"],"names":[],"mappings":"AAuBA,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,sBAAsB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACjD,YAAY,IAAI,MAAM,CAAC;IACvB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,qHAAqH;AACrH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,MAAM,EAAE;QAAE,UAAU,IAAI,MAAM,CAAA;KAAE,CAAC;IACjC,MAAM,EAAE;QACN,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,CAAC;IACF,OAAO,EAAE;QAAE,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC;IAClD,WAAW,EAAE;QACX,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAClD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACzD,CAAC;IACF,iCAAiC;IACjC,QAAQ,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACrE,mCAAmC;IACnC,UAAU,EAAE;QAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACtF;AAED,4FAA4F;AAC5F,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,WAAW,EAAE,MAAM,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAelG;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,iBAAmC,GAAG,UAAU,CAqBxF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The one file in the package that loads another package.
|
|
3
|
+
//
|
|
4
|
+
// The Rocalytics client needs five Expo native modules and react-native. They
|
|
5
|
+
// are optional peer dependencies: an app that only uses /onboarding installs
|
|
6
|
+
// none of them, and nothing outside this file references them.
|
|
7
|
+
//
|
|
8
|
+
// They are loaded lazily, at client start, never at import time. An Expo
|
|
9
|
+
// module's JS wrapper calls `requireNativeModule("ExpoXxx")` when it is first
|
|
10
|
+
// evaluated, which throws when the app binary predates the module (a JS update
|
|
11
|
+
// shipped over the air onto an older build). So each native module is first
|
|
12
|
+
// probed through `requireOptionalNativeModule` (expo-modules-core), which
|
|
13
|
+
// returns null instead of throwing, and no wrapper is required unless every
|
|
14
|
+
// probe succeeds. The probe must not be `TurboModuleRegistry.get`: on the New
|
|
15
|
+
// Architecture, Expo modules are registered with the Expo modules registry,
|
|
16
|
+
// not the TurboModule registry, so that probe reports a linked module as
|
|
17
|
+
// missing.
|
|
18
|
+
//
|
|
19
|
+
// Every require is a string literal, so a bundler can see it. Metro resolves
|
|
20
|
+
// them at bundle time: an app that imports /client without installing a
|
|
21
|
+
// peer fails to bundle, rather than going inert at run time.
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.loadExpoModules = loadExpoModules;
|
|
24
|
+
/** The native module names the wrappers pass to `requireNativeModule`. */
|
|
25
|
+
const NATIVE_MODULES = ["ExpoApplication", "ExpoCrypto", "ExpoDevice", "ExpoNetwork", "ExpoSecureStore"];
|
|
26
|
+
const DEFAULT_LOADERS = {
|
|
27
|
+
core: () => require("expo-modules-core"),
|
|
28
|
+
application: () => require("expo-application"),
|
|
29
|
+
crypto: () => require("expo-crypto"),
|
|
30
|
+
device: () => require("expo-device"),
|
|
31
|
+
network: () => require("expo-network"),
|
|
32
|
+
secureStore: () => require("expo-secure-store"),
|
|
33
|
+
reactNative: () => require("react-native"),
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Loads the Expo modules and react-native, or explains why it could not.
|
|
37
|
+
* Never throws: a missing package, a native module absent from the binary, or
|
|
38
|
+
* a wrapper that throws while loading all come back as `{ ok: false, reason }`.
|
|
39
|
+
*/
|
|
40
|
+
function loadExpoModules(loaders = DEFAULT_LOADERS) {
|
|
41
|
+
try {
|
|
42
|
+
const core = loaders.core();
|
|
43
|
+
const missing = NATIVE_MODULES.find((name) => core.requireOptionalNativeModule(name) == null);
|
|
44
|
+
if (missing)
|
|
45
|
+
return { ok: false, reason: `native module ${missing} is not in this binary` };
|
|
46
|
+
const reactNative = loaders.reactNative();
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
modules: {
|
|
50
|
+
application: loaders.application(),
|
|
51
|
+
crypto: loaders.crypto(),
|
|
52
|
+
device: loaders.device(),
|
|
53
|
+
network: loaders.network(),
|
|
54
|
+
secureStore: loaders.secureStore(),
|
|
55
|
+
platform: reactNative.Platform,
|
|
56
|
+
dimensions: reactNative.Dimensions,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../../src/client/native.ts"],"names":[],"mappings":";AAAA,0DAA0D;AAC1D,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,+DAA+D;AAC/D,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAC9E,+EAA+E;AAC/E,4EAA4E;AAC5E,0EAA0E;AAC1E,4EAA4E;AAC5E,8EAA8E;AAC9E,4EAA4E;AAC5E,yEAAyE;AACzE,WAAW;AACX,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,6DAA6D;;AAoE7D,0CAqBC;AAvCD,0EAA0E;AAC1E,MAAM,cAAc,GAAG,CAAC,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAC;AAEzG,MAAM,eAAe,GAAsB;IACzC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC;IACxC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC;IAC9C,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;IACpC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC;IACtC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC;IAC/C,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC;CAC3C,CAAC;AAEF;;;;GAIG;AACH,SAAgB,eAAe,CAAC,UAA6B,eAAe;IAC1E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAA4D,CAAC;QACtF,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;QAC9F,IAAI,OAAO;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,OAAO,wBAAwB,EAAE,CAAC;QAC5F,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,EAA8F,CAAC;QACtI,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO,EAAE;gBACP,WAAW,EAAE,OAAO,CAAC,WAAW,EAAsC;gBACtE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAiC;gBACvD,MAAM,EAAE,OAAO,CAAC,MAAM,EAAiC;gBACvD,OAAO,EAAE,OAAO,CAAC,OAAO,EAAkC;gBAC1D,WAAW,EAAE,OAAO,CAAC,WAAW,EAAsC;gBACtE,QAAQ,EAAE,WAAW,CAAC,QAAQ;gBAC9B,UAAU,EAAE,WAAW,CAAC,UAAU;aACnC;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACvF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SinkResult, Sink } from "../core/sink";
|
|
2
|
+
import type { OnboardingRunSnapshot } from "../onboarding/contract";
|
|
3
|
+
import type { RocalyticsClient } from "./client";
|
|
4
|
+
import type { OnboardingResponsePayload } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* A v1 onboarding run snapshot in the pre-v1 `/onboarding-response` shape,
|
|
7
|
+
* which is the only onboarding payload the Rocalytics ingest reads. The keys
|
|
8
|
+
* follow docs/onboarding-run-contract.md, section 9, which is how the ingest
|
|
9
|
+
* reads such a payload back into a run.
|
|
10
|
+
*
|
|
11
|
+
* - Each entry is a response: `step_key` becomes `step_id`, timestamps are kept.
|
|
12
|
+
* - Answers become `{ [question_key]: value }`. A numeric answer's `unit` has
|
|
13
|
+
* no place in that shape and is dropped.
|
|
14
|
+
* - `onboarding_metadata` carries the keys section 9 reads: `onboardingId`
|
|
15
|
+
* (the Studio onboarding) or, with none, `onboarding_id` (the onboarding
|
|
16
|
+
* key, as a hand-coded sender sets it), `audienceId`, `deployment_id`,
|
|
17
|
+
* `locale` (from the run's context) and `draft: true` for a draft. It also
|
|
18
|
+
* carries `onboarding_key`, `onboarding_version`, `variant_key`, `run_id`
|
|
19
|
+
* and `seq`, which section 9 does not read.
|
|
20
|
+
* - `sent_at` is NOT the device's send time. The ingest keeps one snapshot
|
|
21
|
+
* per sender and replaces it only when the incoming `sent_at` is strictly
|
|
22
|
+
* later, answering 2xx either way, so two sends in one millisecond, or a
|
|
23
|
+
* device clock stepping back, would drop a snapshot (the completion
|
|
24
|
+
* included) while it reads as delivered. So `sent_at` is the run's latest
|
|
25
|
+
* recorded timestamp plus `seq` milliseconds. A snapshot only ever adds
|
|
26
|
+
* timestamps to the one before it and `seq` rises with every send, so this
|
|
27
|
+
* strictly increases across a run's sends whatever the device clock does.
|
|
28
|
+
* Only within a run: the ingest keeps one row per sender, so after the
|
|
29
|
+
* clock steps back between runs, a later run's snapshots can still be
|
|
30
|
+
* dropped while answered 2xx, as pre-v1 reporting already could.
|
|
31
|
+
* It depends on the snapshot alone, so a retry, even after a relaunch,
|
|
32
|
+
* resends the identical body.
|
|
33
|
+
*/
|
|
34
|
+
export declare function toOnboardingResponsePayload(snapshot: OnboardingRunSnapshot): OnboardingResponsePayload;
|
|
35
|
+
/**
|
|
36
|
+
* Reads an `/onboarding-response` answer as a sink outcome. The endpoint
|
|
37
|
+
* answers success with a 2xx and no body, so a 2xx is accepted. Otherwise,
|
|
38
|
+
* as the contract's section 5 requires, only a body that says
|
|
39
|
+
* `{"outcome": "rejected"}` is permanent: every other status, whatever its
|
|
40
|
+
* body, is transient and the snapshot is retried.
|
|
41
|
+
*/
|
|
42
|
+
export declare function rocalyticsOutcome(status: number, body?: unknown): SinkResult;
|
|
43
|
+
/**
|
|
44
|
+
* A sink for the onboarding run tracker (`@rocapine/rocalytics-sdk/onboarding`)
|
|
45
|
+
* that delivers each snapshot to Rocalytics through `client`, as the pre-v1
|
|
46
|
+
* onboarding payload (see `toOnboardingResponsePayload`).
|
|
47
|
+
*
|
|
48
|
+
* Once a completed snapshot is accepted, the sink also sends the
|
|
49
|
+
* `onboarding_completed` event, which is where consumers of the pre-v1 data
|
|
50
|
+
* read completion from. It is deduplicated per device, like any `track`
|
|
51
|
+
* event; if it fails, the send is transient, so the tracker's retry sends it.
|
|
52
|
+
*
|
|
53
|
+
* The ingest keeps one onboarding snapshot per roca id, so a new run replaces
|
|
54
|
+
* the previous one there, and an app should report a flow through this sink
|
|
55
|
+
* or through `client.trackOnboarding`, not both.
|
|
56
|
+
*
|
|
57
|
+
* While the client is inert every send is transient: the tracker keeps the
|
|
58
|
+
* snapshot, persisted when it has storage, for a launch where the client works.
|
|
59
|
+
*
|
|
60
|
+
* Its `destination` is the client's `onboardingRunDestination`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function createRocalyticsOnboardingSink(client: RocalyticsClient): Sink<OnboardingRunSnapshot>;
|
|
63
|
+
//# sourceMappingURL=onboardingSink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboardingSink.d.ts","sourceRoot":"","sources":["../../src/client/onboardingSink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAU,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,KAAK,EAAsB,yBAAyB,EAAyB,MAAM,SAAS,CAAC;AAEpG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,yBAAyB,CA2BtG;AAsBD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,CAO5E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAEpG"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toOnboardingResponsePayload = toOnboardingResponsePayload;
|
|
4
|
+
exports.rocalyticsOutcome = rocalyticsOutcome;
|
|
5
|
+
exports.createRocalyticsOnboardingSink = createRocalyticsOnboardingSink;
|
|
6
|
+
/**
|
|
7
|
+
* A v1 onboarding run snapshot in the pre-v1 `/onboarding-response` shape,
|
|
8
|
+
* which is the only onboarding payload the Rocalytics ingest reads. The keys
|
|
9
|
+
* follow docs/onboarding-run-contract.md, section 9, which is how the ingest
|
|
10
|
+
* reads such a payload back into a run.
|
|
11
|
+
*
|
|
12
|
+
* - Each entry is a response: `step_key` becomes `step_id`, timestamps are kept.
|
|
13
|
+
* - Answers become `{ [question_key]: value }`. A numeric answer's `unit` has
|
|
14
|
+
* no place in that shape and is dropped.
|
|
15
|
+
* - `onboarding_metadata` carries the keys section 9 reads: `onboardingId`
|
|
16
|
+
* (the Studio onboarding) or, with none, `onboarding_id` (the onboarding
|
|
17
|
+
* key, as a hand-coded sender sets it), `audienceId`, `deployment_id`,
|
|
18
|
+
* `locale` (from the run's context) and `draft: true` for a draft. It also
|
|
19
|
+
* carries `onboarding_key`, `onboarding_version`, `variant_key`, `run_id`
|
|
20
|
+
* and `seq`, which section 9 does not read.
|
|
21
|
+
* - `sent_at` is NOT the device's send time. The ingest keeps one snapshot
|
|
22
|
+
* per sender and replaces it only when the incoming `sent_at` is strictly
|
|
23
|
+
* later, answering 2xx either way, so two sends in one millisecond, or a
|
|
24
|
+
* device clock stepping back, would drop a snapshot (the completion
|
|
25
|
+
* included) while it reads as delivered. So `sent_at` is the run's latest
|
|
26
|
+
* recorded timestamp plus `seq` milliseconds. A snapshot only ever adds
|
|
27
|
+
* timestamps to the one before it and `seq` rises with every send, so this
|
|
28
|
+
* strictly increases across a run's sends whatever the device clock does.
|
|
29
|
+
* Only within a run: the ingest keeps one row per sender, so after the
|
|
30
|
+
* clock steps back between runs, a later run's snapshots can still be
|
|
31
|
+
* dropped while answered 2xx, as pre-v1 reporting already could.
|
|
32
|
+
* It depends on the snapshot alone, so a retry, even after a relaunch,
|
|
33
|
+
* resends the identical body.
|
|
34
|
+
*/
|
|
35
|
+
function toOnboardingResponsePayload(snapshot) {
|
|
36
|
+
var _a, _b, _c;
|
|
37
|
+
const metadata = {};
|
|
38
|
+
const set = (key, value) => {
|
|
39
|
+
if (value !== undefined && value !== null)
|
|
40
|
+
metadata[key] = value;
|
|
41
|
+
};
|
|
42
|
+
const studioOnboardingId = (_a = snapshot.studio) === null || _a === void 0 ? void 0 : _a.onboarding_id;
|
|
43
|
+
if (studioOnboardingId != null)
|
|
44
|
+
set("onboardingId", studioOnboardingId);
|
|
45
|
+
else
|
|
46
|
+
set("onboarding_id", snapshot.onboarding.key);
|
|
47
|
+
set("audienceId", (_b = snapshot.studio) === null || _b === void 0 ? void 0 : _b.audience_id);
|
|
48
|
+
set("deployment_id", (_c = snapshot.studio) === null || _c === void 0 ? void 0 : _c.deployment_id);
|
|
49
|
+
set("locale", snapshot.context.locale);
|
|
50
|
+
if (snapshot.onboarding.version === "draft")
|
|
51
|
+
set("draft", true);
|
|
52
|
+
set("onboarding_key", snapshot.onboarding.key);
|
|
53
|
+
set("onboarding_version", snapshot.onboarding.version);
|
|
54
|
+
set("variant_key", snapshot.onboarding.variant_key);
|
|
55
|
+
set("run_id", snapshot.run_id);
|
|
56
|
+
set("seq", snapshot.seq);
|
|
57
|
+
return {
|
|
58
|
+
onboarding_metadata: metadata,
|
|
59
|
+
sent_at: new Date(latestRecorded(snapshot) + snapshot.seq).toISOString(),
|
|
60
|
+
responses: snapshot.steps.map((entry) => ({
|
|
61
|
+
step_id: entry.step_key,
|
|
62
|
+
entered_at: entry.entered_at,
|
|
63
|
+
exited_at: entry.exited_at,
|
|
64
|
+
answers: toAnswers(entry.answers),
|
|
65
|
+
})),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** The latest timestamp the run has recorded: its start, entries, exits and completion. Not `sent_at`. */
|
|
69
|
+
function latestRecorded(snapshot) {
|
|
70
|
+
let latest = Date.parse(snapshot.started_at);
|
|
71
|
+
const consider = (ts) => {
|
|
72
|
+
if (ts !== null)
|
|
73
|
+
latest = Math.max(latest, Date.parse(ts));
|
|
74
|
+
};
|
|
75
|
+
for (const entry of snapshot.steps) {
|
|
76
|
+
consider(entry.entered_at);
|
|
77
|
+
consider(entry.exited_at);
|
|
78
|
+
}
|
|
79
|
+
consider(snapshot.completed_at);
|
|
80
|
+
return latest;
|
|
81
|
+
}
|
|
82
|
+
function toAnswers(answers) {
|
|
83
|
+
const out = {};
|
|
84
|
+
for (const answer of answers)
|
|
85
|
+
out[answer.question_key] = Array.isArray(answer.value) ? [...answer.value] : answer.value;
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reads an `/onboarding-response` answer as a sink outcome. The endpoint
|
|
90
|
+
* answers success with a 2xx and no body, so a 2xx is accepted. Otherwise,
|
|
91
|
+
* as the contract's section 5 requires, only a body that says
|
|
92
|
+
* `{"outcome": "rejected"}` is permanent: every other status, whatever its
|
|
93
|
+
* body, is transient and the snapshot is retried.
|
|
94
|
+
*/
|
|
95
|
+
function rocalyticsOutcome(status, body) {
|
|
96
|
+
if (status >= 200 && status < 300)
|
|
97
|
+
return { outcome: "accepted" };
|
|
98
|
+
if (body && typeof body === "object" && body.outcome === "rejected") {
|
|
99
|
+
const reason = body.reason;
|
|
100
|
+
return typeof reason === "string" ? { outcome: "rejected", reason } : { outcome: "rejected" };
|
|
101
|
+
}
|
|
102
|
+
return { outcome: "transient", reason: `status ${status}` };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A sink for the onboarding run tracker (`@rocapine/rocalytics-sdk/onboarding`)
|
|
106
|
+
* that delivers each snapshot to Rocalytics through `client`, as the pre-v1
|
|
107
|
+
* onboarding payload (see `toOnboardingResponsePayload`).
|
|
108
|
+
*
|
|
109
|
+
* Once a completed snapshot is accepted, the sink also sends the
|
|
110
|
+
* `onboarding_completed` event, which is where consumers of the pre-v1 data
|
|
111
|
+
* read completion from. It is deduplicated per device, like any `track`
|
|
112
|
+
* event; if it fails, the send is transient, so the tracker's retry sends it.
|
|
113
|
+
*
|
|
114
|
+
* The ingest keeps one onboarding snapshot per roca id, so a new run replaces
|
|
115
|
+
* the previous one there, and an app should report a flow through this sink
|
|
116
|
+
* or through `client.trackOnboarding`, not both.
|
|
117
|
+
*
|
|
118
|
+
* While the client is inert every send is transient: the tracker keeps the
|
|
119
|
+
* snapshot, persisted when it has storage, for a launch where the client works.
|
|
120
|
+
*
|
|
121
|
+
* Its `destination` is the client's `onboardingRunDestination`.
|
|
122
|
+
*/
|
|
123
|
+
function createRocalyticsOnboardingSink(client) {
|
|
124
|
+
return { destination: client.onboardingRunDestination, send: (snapshot) => client.sendOnboardingRun(snapshot) };
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=onboardingSink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboardingSink.js","sourceRoot":"","sources":["../../src/client/onboardingSink.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Sink, SinkResult } from "../core/sink";
|
|
2
|
+
import type { PaywallPresentationSnapshot } from "../paywall/contract";
|
|
3
|
+
import type { RocalyticsClient } from "./client";
|
|
4
|
+
/**
|
|
5
|
+
* Reads a `/paywall-presentations` answer per the paywall contract's section 5
|
|
6
|
+
* (onboarding D27): the body's `outcome` decides. A rejected body is permanent
|
|
7
|
+
* whatever the status. Accepted or ignored counts only on a 2xx. Anything
|
|
8
|
+
* else, including a 2xx without an outcome, is transient and retried.
|
|
9
|
+
*/
|
|
10
|
+
export declare function paywallIngestOutcome(status: number, body?: unknown): SinkResult;
|
|
11
|
+
/** Delivers the paywall tracker's snapshots to Rocalytics. Its `destination` is the client's `paywallPresentationDestination`. */
|
|
12
|
+
export declare function createRocalyticsPaywallSink(client: RocalyticsClient): Sink<PaywallPresentationSnapshot>;
|
|
13
|
+
//# sourceMappingURL=paywallSink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paywallSink.d.ts","sourceRoot":"","sources":["../../src/client/paywallSink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIjD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,CAO/E;AAED,kIAAkI;AAClI,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC,2BAA2B,CAAC,CAEvG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.paywallIngestOutcome = paywallIngestOutcome;
|
|
4
|
+
exports.createRocalyticsPaywallSink = createRocalyticsPaywallSink;
|
|
5
|
+
const OUTCOMES = new Set(["accepted", "ignored", "rejected"]);
|
|
6
|
+
/**
|
|
7
|
+
* Reads a `/paywall-presentations` answer per the paywall contract's section 5
|
|
8
|
+
* (onboarding D27): the body's `outcome` decides. A rejected body is permanent
|
|
9
|
+
* whatever the status. Accepted or ignored counts only on a 2xx. Anything
|
|
10
|
+
* else, including a 2xx without an outcome, is transient and retried.
|
|
11
|
+
*/
|
|
12
|
+
function paywallIngestOutcome(status, body) {
|
|
13
|
+
const o = body && typeof body === "object" ? body : undefined;
|
|
14
|
+
const outcome = typeof (o === null || o === void 0 ? void 0 : o.outcome) === "string" && OUTCOMES.has(o.outcome) ? o.outcome : undefined;
|
|
15
|
+
const reason = typeof (o === null || o === void 0 ? void 0 : o.reason) === "string" ? o.reason : undefined;
|
|
16
|
+
if (outcome === "rejected")
|
|
17
|
+
return reason ? { outcome, reason } : { outcome };
|
|
18
|
+
if (outcome && status >= 200 && status < 300)
|
|
19
|
+
return { outcome };
|
|
20
|
+
return { outcome: "transient", reason: `status ${status}` };
|
|
21
|
+
}
|
|
22
|
+
/** Delivers the paywall tracker's snapshots to Rocalytics. Its `destination` is the client's `paywallPresentationDestination`. */
|
|
23
|
+
function createRocalyticsPaywallSink(client) {
|
|
24
|
+
return { destination: client.paywallPresentationDestination, send: (s) => client.sendPaywallPresentation(s) };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=paywallSink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paywallSink.js","sourceRoot":"","sources":["../../src/client/paywallSink.ts"],"names":[],"mappings":";;AAYA,oDAOC;AAGD,kEAEC;AApBD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;AAE9D;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,MAAc,EAAE,IAAc;IACjE,MAAM,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAgD,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3G,MAAM,OAAO,GAAG,OAAO,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,CAAA,KAAK,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,OAA+C,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3I,MAAM,MAAM,GAAG,OAAO,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,MAAM,CAAA,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,OAAO,KAAK,UAAU;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;IAC9E,IAAI,OAAO,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG;QAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACjE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,MAAM,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,kIAAkI;AAClI,SAAgB,2BAA2B,CAAC,MAAwB;IAClE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,8BAA8B,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;AAChH,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { DemandScoreSignals, IdentifyParams, OnboardingResponsePayload } from "./types";
|
|
2
|
+
import type { PaywallPresentationSnapshot } from "../paywall/contract";
|
|
3
|
+
export declare const ROCALYTICS_API_BASE = "https://rocalytics-api.rocapine.io";
|
|
4
|
+
/** What every request carries: the device's roca id, the app id and the platform. There is no auth token. */
|
|
5
|
+
export interface RequestContext {
|
|
6
|
+
rocaId: string;
|
|
7
|
+
/** The app's bundle id or package name; sent as `unknown` when null. */
|
|
8
|
+
applicationId: string | null;
|
|
9
|
+
/** `Platform.OS`. */
|
|
10
|
+
platform: string;
|
|
11
|
+
/** Defaults to `ROCALYTICS_API_BASE`. */
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A request, ready to hand to `fetch(url, init)`. */
|
|
15
|
+
export interface RocalyticsRequest {
|
|
16
|
+
url: string;
|
|
17
|
+
init: {
|
|
18
|
+
method: "POST";
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
body?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface FetchResponseLike {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
status: number;
|
|
26
|
+
json(): Promise<unknown>;
|
|
27
|
+
}
|
|
28
|
+
/** The subset of `fetch` the client uses. */
|
|
29
|
+
export type FetchLike = (url: string, init: RocalyticsRequest["init"]) => Promise<FetchResponseLike>;
|
|
30
|
+
export declare function rocalyticsHeaders(context: RequestContext): Record<string, string>;
|
|
31
|
+
/** `/identify`. The payload is sent as given: drop null identifiers first (the client does). */
|
|
32
|
+
export declare function buildIdentifyRequest(context: RequestContext, payload: Partial<IdentifyParams>): RocalyticsRequest;
|
|
33
|
+
export interface TrackRequestInput {
|
|
34
|
+
name: string;
|
|
35
|
+
properties: Record<string, unknown>;
|
|
36
|
+
deviceContext: Record<string, unknown> | null;
|
|
37
|
+
/** Defaults to `${rocaId}-${name}`. */
|
|
38
|
+
deduplicationId?: string;
|
|
39
|
+
/** A custom event is not stored as an analytics event: the API passes it on to drive automations. */
|
|
40
|
+
customEvent?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** `/track`. */
|
|
43
|
+
export declare function buildTrackRequest(context: RequestContext, input: TrackRequestInput): RocalyticsRequest;
|
|
44
|
+
/** `/onboarding-response`. */
|
|
45
|
+
export declare function buildOnboardingResponseRequest(context: RequestContext, payload: OnboardingResponsePayload): RocalyticsRequest;
|
|
46
|
+
/** `/demand-score`. The body is optional server-side, so none is sent without signals. */
|
|
47
|
+
export declare function buildDemandScoreRequest(context: RequestContext, signals?: DemandScoreSignals): RocalyticsRequest;
|
|
48
|
+
/**
|
|
49
|
+
* Sends a request and resolves its response, or rejects with
|
|
50
|
+
* `[ROCALYTICS] <endpoint> failed: <status>` (plus ` (<detail>)` when given)
|
|
51
|
+
* when the status is not 2xx.
|
|
52
|
+
*/
|
|
53
|
+
export declare function sendRocalyticsRequest(fetch: FetchLike, request: RocalyticsRequest, endpoint: string, detail?: string): Promise<FetchResponseLike>;
|
|
54
|
+
/**
|
|
55
|
+
* The cross-network event id for an event, or `undefined` when its properties
|
|
56
|
+
* carry no transaction identifier: `${name}-${originalTransactionIdentifier}`.
|
|
57
|
+
*
|
|
58
|
+
* Use it as the `event_id` (Meta CAPI or Pixel, TikTok Events API) or
|
|
59
|
+
* `callback_id` (Adjust S2S) when the app also fires the conversion to those
|
|
60
|
+
* networks itself. Rocalytics derives the same id when it forwards the event,
|
|
61
|
+
* so the network deduplicates the two.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getEventId(name: string, properties?: Record<string, unknown> | null): string | undefined;
|
|
64
|
+
/** `/paywall-presentations` (I3): the body is the bare snapshot; identity is in the headers `post` sets. */
|
|
65
|
+
export declare function buildPaywallPresentationRequest(context: RequestContext, snapshot: PaywallPresentationSnapshot): RocalyticsRequest;
|
|
66
|
+
//# sourceMappingURL=requests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.d.ts","sourceRoot":"","sources":["../../src/client/requests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAEvE,eAAO,MAAM,mBAAmB,uCAAuC,CAAC;AAExE,6GAA6G;AAC7G,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1E;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED,6CAA6C;AAC7C,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAErG,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOjF;AAWD,gGAAgG;AAChG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,iBAAiB,CAEjH;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9C,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qGAAqG;IACrG,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,gBAAgB;AAChB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,CAQtG;AAED,8BAA8B;AAC9B,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,yBAAyB,GAAG,iBAAiB,CAE7H;AAED,0FAA0F;AAC1F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CAEhH;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAQxG;AAED,4GAA4G;AAC5G,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,2BAA2B,GAAG,iBAAiB,CAEjI"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ROCALYTICS_API_BASE = void 0;
|
|
4
|
+
exports.rocalyticsHeaders = rocalyticsHeaders;
|
|
5
|
+
exports.buildIdentifyRequest = buildIdentifyRequest;
|
|
6
|
+
exports.buildTrackRequest = buildTrackRequest;
|
|
7
|
+
exports.buildOnboardingResponseRequest = buildOnboardingResponseRequest;
|
|
8
|
+
exports.buildDemandScoreRequest = buildDemandScoreRequest;
|
|
9
|
+
exports.sendRocalyticsRequest = sendRocalyticsRequest;
|
|
10
|
+
exports.getEventId = getEventId;
|
|
11
|
+
exports.buildPaywallPresentationRequest = buildPaywallPresentationRequest;
|
|
12
|
+
exports.ROCALYTICS_API_BASE = "https://rocalytics-api.rocapine.io";
|
|
13
|
+
function rocalyticsHeaders(context) {
|
|
14
|
+
var _a;
|
|
15
|
+
return {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
"X-Roca-ID": context.rocaId,
|
|
18
|
+
"X-Application-ID": (_a = context.applicationId) !== null && _a !== void 0 ? _a : "unknown",
|
|
19
|
+
"X-Platform": context.platform,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const post = (context, endpoint, body) => {
|
|
23
|
+
var _a;
|
|
24
|
+
return ({
|
|
25
|
+
url: `${(_a = context.baseUrl) !== null && _a !== void 0 ? _a : exports.ROCALYTICS_API_BASE}/functions/v1/${endpoint}`,
|
|
26
|
+
init: {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: rocalyticsHeaders(context),
|
|
29
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
/** `/identify`. The payload is sent as given: drop null identifiers first (the client does). */
|
|
34
|
+
function buildIdentifyRequest(context, payload) {
|
|
35
|
+
return post(context, "identify", payload);
|
|
36
|
+
}
|
|
37
|
+
/** `/track`. */
|
|
38
|
+
function buildTrackRequest(context, input) {
|
|
39
|
+
var _a;
|
|
40
|
+
return post(context, "track", {
|
|
41
|
+
name: input.name,
|
|
42
|
+
deduplication_id: (_a = input.deduplicationId) !== null && _a !== void 0 ? _a : `${context.rocaId}-${input.name}`,
|
|
43
|
+
properties: input.properties,
|
|
44
|
+
device_context: input.deviceContext,
|
|
45
|
+
...(input.customEvent ? { custom_event: true } : {}),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** `/onboarding-response`. */
|
|
49
|
+
function buildOnboardingResponseRequest(context, payload) {
|
|
50
|
+
return post(context, "onboarding-response", payload);
|
|
51
|
+
}
|
|
52
|
+
/** `/demand-score`. The body is optional server-side, so none is sent without signals. */
|
|
53
|
+
function buildDemandScoreRequest(context, signals) {
|
|
54
|
+
return post(context, "demand-score", signals && Object.keys(signals).length > 0 ? signals : undefined);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sends a request and resolves its response, or rejects with
|
|
58
|
+
* `[ROCALYTICS] <endpoint> failed: <status>` (plus ` (<detail>)` when given)
|
|
59
|
+
* when the status is not 2xx.
|
|
60
|
+
*/
|
|
61
|
+
async function sendRocalyticsRequest(fetch, request, endpoint, detail) {
|
|
62
|
+
const response = await fetch(request.url, request.init);
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`[ROCALYTICS] ${endpoint} failed: ${response.status}${detail ? ` (${detail})` : ""}`);
|
|
65
|
+
}
|
|
66
|
+
return response;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The cross-network event id for an event, or `undefined` when its properties
|
|
70
|
+
* carry no transaction identifier: `${name}-${originalTransactionIdentifier}`.
|
|
71
|
+
*
|
|
72
|
+
* Use it as the `event_id` (Meta CAPI or Pixel, TikTok Events API) or
|
|
73
|
+
* `callback_id` (Adjust S2S) when the app also fires the conversion to those
|
|
74
|
+
* networks itself. Rocalytics derives the same id when it forwards the event,
|
|
75
|
+
* so the network deduplicates the two.
|
|
76
|
+
*/
|
|
77
|
+
function getEventId(name, properties) {
|
|
78
|
+
var _a, _b, _c;
|
|
79
|
+
if (!properties)
|
|
80
|
+
return undefined;
|
|
81
|
+
const txId = (_b = (_a = properties.original_transaction_identifier) !== null && _a !== void 0 ? _a : properties.originalTransactionIdentifier) !== null && _b !== void 0 ? _b : (_c = properties.transaction) === null || _c === void 0 ? void 0 : _c.originalTransactionIdentifier;
|
|
82
|
+
if (!txId)
|
|
83
|
+
return undefined;
|
|
84
|
+
return `${name}-${txId}`;
|
|
85
|
+
}
|
|
86
|
+
/** `/paywall-presentations` (I3): the body is the bare snapshot; identity is in the headers `post` sets. */
|
|
87
|
+
function buildPaywallPresentationRequest(context, snapshot) {
|
|
88
|
+
return post(context, "paywall-presentations", snapshot);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.js","sourceRoot":"","sources":["../../src/client/requests.ts"],"names":[],"mappings":"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"}
|