@edulib-france/expo-yawl 0.0.1
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/.eslintrc.js +8 -0
- package/.mise.toml +3 -0
- package/README.md +35 -0
- package/build/Yawl.types.d.ts +8 -0
- package/build/Yawl.types.d.ts.map +1 -0
- package/build/Yawl.types.js +2 -0
- package/build/Yawl.types.js.map +1 -0
- package/build/YawlModule.d.ts +2 -0
- package/build/YawlModule.d.ts.map +1 -0
- package/build/YawlModule.js +2 -0
- package/build/YawlModule.js.map +1 -0
- package/build/core/api.d.ts +13 -0
- package/build/core/api.d.ts.map +1 -0
- package/build/core/api.js +18 -0
- package/build/core/api.js.map +1 -0
- package/build/core/api.test.d.ts +2 -0
- package/build/core/api.test.d.ts.map +1 -0
- package/build/core/api.test.js +17 -0
- package/build/core/api.test.js.map +1 -0
- package/build/core/deviceInfo.d.ts +9 -0
- package/build/core/deviceInfo.d.ts.map +1 -0
- package/build/core/deviceInfo.js +12 -0
- package/build/core/deviceInfo.js.map +1 -0
- package/build/core/generateUUID.d.ts +3 -0
- package/build/core/generateUUID.d.ts.map +1 -0
- package/build/core/generateUUID.js +8 -0
- package/build/core/generateUUID.js.map +1 -0
- package/build/core/index.d.ts +27 -0
- package/build/core/index.d.ts.map +1 -0
- package/build/core/index.js +169 -0
- package/build/core/index.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +5 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +3 -0
- package/package.json +57 -0
- package/src/Yawl.types.ts +7 -0
- package/src/YawlModule.ts +1 -0
- package/src/core/api.test.ts +18 -0
- package/src/core/api.ts +32 -0
- package/src/core/deviceInfo.ts +19 -0
- package/src/core/generateUUID.ts +11 -0
- package/src/core/index.ts +200 -0
- package/src/index.ts +4 -0
- package/tsconfig.json +9 -0
package/.eslintrc.js
ADDED
package/.mise.toml
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @edulib-france/expo-yawl
|
|
2
|
+
|
|
3
|
+
Yawl for React Native
|
|
4
|
+
|
|
5
|
+
# API documentation
|
|
6
|
+
|
|
7
|
+
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/@edulib-france/yawl/)
|
|
8
|
+
- [Documentation for the main branch](https://docs.expo.dev/versions/unversioned/sdk/@edulib-france/yawl/)
|
|
9
|
+
|
|
10
|
+
# Installation in managed Expo projects
|
|
11
|
+
|
|
12
|
+
For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release.
|
|
13
|
+
|
|
14
|
+
# Installation in bare React Native projects
|
|
15
|
+
|
|
16
|
+
For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
|
|
17
|
+
|
|
18
|
+
### Add the package to your npm dependencies
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
npm install @edulib-france/expo-yawl
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Configure for Android
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Configure for iOS
|
|
30
|
+
|
|
31
|
+
Run `npx pod-install` after installing the npm package.
|
|
32
|
+
|
|
33
|
+
# Contributing
|
|
34
|
+
|
|
35
|
+
Contributions are very welcome! Please refer to guidelines described in the [contributing guide]( https://github.com/expo/expo#contributing).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Yawl.types.d.ts","sourceRoot":"","sources":["../src/Yawl.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Yawl.types.js","sourceRoot":"","sources":["../src/Yawl.types.ts"],"names":[],"mappings":"","sourcesContent":["export type YawlEvent = {\n name: string;\n ean?: string;\n establishment_account_id: string;\n properties?: Record<string, unknown>;\n user_type: string;\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"YawlModule.d.ts","sourceRoot":"","sources":["../src/YawlModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"YawlModule.js","sourceRoot":"","sources":["../src/YawlModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export { default } from \"./core/index\";\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Env = "prod" | "staging";
|
|
2
|
+
export declare const API_URL: {
|
|
3
|
+
[key in Env]: string;
|
|
4
|
+
};
|
|
5
|
+
export type YawlApi = {
|
|
6
|
+
sendVisit: (data: object) => Promise<any>;
|
|
7
|
+
sendEvent: (data: object) => Promise<any>;
|
|
8
|
+
};
|
|
9
|
+
export declare const yawlApi: ({ apiKey, env, }: {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
env?: Env;
|
|
12
|
+
}) => YawlApi;
|
|
13
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,SAAS,CAAC;AACrC,eAAO,MAAM,OAAO,EAAE;KAAG,GAAG,IAAI,GAAG,GAAG,MAAM;CAGlC,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC3C,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,kBAGrB;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,GAAG,CAAC;CACX,KAAG,OAYH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import wretch from "wretch";
|
|
2
|
+
export const API_URL = {
|
|
3
|
+
prod: "https://www.edulib.fr",
|
|
4
|
+
staging: "https://staging.edulib.fr",
|
|
5
|
+
};
|
|
6
|
+
export const yawlApi = ({ apiKey, env = "prod", }) => {
|
|
7
|
+
async function fetchGuard(url, data) {
|
|
8
|
+
return await wretch(url).headers({ "Api-Key": apiKey }).post(data).json();
|
|
9
|
+
}
|
|
10
|
+
async function sendVisit(data) {
|
|
11
|
+
return fetchGuard(`${API_URL[env]}/ahoy/visits`, data);
|
|
12
|
+
}
|
|
13
|
+
async function sendEvent(data) {
|
|
14
|
+
return fetchGuard(`${API_URL[env]}/ahoy/events`, data);
|
|
15
|
+
}
|
|
16
|
+
return { sendVisit, sendEvent };
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,CAAC,MAAM,OAAO,GAA6B;IAC/C,IAAI,EAAE,uBAAuB;IAC7B,OAAO,EAAE,2BAA2B;CAC5B,CAAC;AAOX,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EACtB,MAAM,EACN,GAAG,GAAG,MAAM,GAIb,EAAW,EAAE;IACZ,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,IAAY;QACjD,OAAO,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5E,CAAC;IACD,KAAK,UAAU,SAAS,CAAC,IAAY;QACnC,OAAO,UAAU,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,IAAY;QACnC,OAAO,UAAU,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC,CAAC","sourcesContent":["import wretch from \"wretch\";\n\nexport type Env = \"prod\" | \"staging\";\nexport const API_URL: { [key in Env]: string } = {\n prod: \"https://www.edulib.fr\",\n staging: \"https://staging.edulib.fr\",\n} as const;\n\nexport type YawlApi = {\n sendVisit: (data: object) => Promise<any>;\n sendEvent: (data: object) => Promise<any>;\n};\n\nexport const yawlApi = ({\n apiKey,\n env = \"prod\",\n}: {\n apiKey: string;\n env?: Env;\n}): YawlApi => {\n async function fetchGuard(url: string, data: object) {\n return await wretch(url).headers({ \"Api-Key\": apiKey }).post(data).json();\n }\n async function sendVisit(data: object) {\n return fetchGuard(`${API_URL[env]}/ahoy/visits`, data);\n }\n\n async function sendEvent(data: object) {\n return fetchGuard(`${API_URL[env]}/ahoy/events`, data);\n }\n return { sendVisit, sendEvent };\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.test.d.ts","sourceRoot":"","sources":["../../src/core/api.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { yawlApi } from "./api";
|
|
3
|
+
describe("API Tests", () => {
|
|
4
|
+
const apiConfig = { apiKey: "test-key", env: "staging" };
|
|
5
|
+
const data = { visit: { id: "test-id", visitor_id: "test-visitor-id" } };
|
|
6
|
+
it("should failed when wrong key", async () => {
|
|
7
|
+
const api = yawlApi(apiConfig);
|
|
8
|
+
await expect(api.sendVisit(data)).rejects.toThrowError("Unauthorized");
|
|
9
|
+
});
|
|
10
|
+
it("should send visit", async () => {
|
|
11
|
+
const api = yawlApi(apiConfig);
|
|
12
|
+
await expect(api.sendVisit(data)).resolves.toMatchObject({
|
|
13
|
+
message: "Ahoy::Visit created",
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=api.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.test.js","sourceRoot":"","sources":["../../src/core/api.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,MAAM,SAAS,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,SAAkB,EAAE,CAAC;IAClE,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,CAAC;IACzE,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/B,MAAM,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QACjC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/B,MAAM,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;YACvD,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from \"vitest\";\n\nimport { yawlApi } from \"./api\";\n\ndescribe(\"API Tests\", () => {\n const apiConfig = { apiKey: \"test-key\", env: \"staging\" as const };\n const data = { visit: { id: \"test-id\", visitor_id: \"test-visitor-id\" } };\n it(\"should failed when wrong key\", async () => {\n const api = yawlApi(apiConfig);\n await expect(api.sendVisit(data)).rejects.toThrowError(\"Unauthorized\");\n });\n it(\"should send visit\", async () => {\n const api = yawlApi(apiConfig);\n await expect(api.sendVisit(data)).resolves.toMatchObject({\n message: \"Ahoy::Visit created\",\n });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deviceInfo.d.ts","sourceRoot":"","sources":["../../src/core/deviceInfo.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AACF,eAAO,MAAM,aAAa,QAAO,cAQhC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { nativeApplicationVersion } from "expo-application";
|
|
2
|
+
import { deviceType, DeviceType, osName, osVersion } from "expo-device";
|
|
3
|
+
export const getDeviceInfo = () => {
|
|
4
|
+
return {
|
|
5
|
+
app_version: nativeApplicationVersion ?? undefined,
|
|
6
|
+
device_type: DeviceType[deviceType ?? 0],
|
|
7
|
+
os: osName ?? undefined,
|
|
8
|
+
os_version: osVersion ?? undefined,
|
|
9
|
+
platform: "react-native",
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=deviceInfo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deviceInfo.js","sourceRoot":"","sources":["../../src/core/deviceInfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AASxE,MAAM,CAAC,MAAM,aAAa,GAAG,GAAmB,EAAE;IAChD,OAAO;QACL,WAAW,EAAE,wBAAwB,IAAI,SAAS;QAClD,WAAW,EAAE,UAAU,CAAC,UAAU,IAAI,CAAC,CAAC;QACxC,EAAE,EAAE,MAAM,IAAI,SAAS;QACvB,UAAU,EAAE,SAAS,IAAI,SAAS;QAClC,QAAQ,EAAE,cAAc;KACzB,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { nativeApplicationVersion } from \"expo-application\";\nimport { deviceType, DeviceType, osName, osVersion } from \"expo-device\";\n\nexport type YawlDeviceInfo = {\n app_version?: string;\n device_type?: string;\n os?: string;\n os_version?: string;\n platform: string;\n};\nexport const getDeviceInfo = (): YawlDeviceInfo => {\n return {\n app_version: nativeApplicationVersion ?? undefined,\n device_type: DeviceType[deviceType ?? 0],\n os: osName ?? undefined,\n os_version: osVersion ?? undefined,\n platform: \"react-native\",\n };\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateUUID.d.ts","sourceRoot":"","sources":["../../src/core/generateUUID.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,cAAqB,CAAC;AAE/C,eAAO,MAAM,oBAAoB,cAM7B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { randomUUID } from "expo-crypto";
|
|
2
|
+
export const generateUUID = () => randomUUID();
|
|
3
|
+
export const unsecureGenerateUUID = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
4
|
+
const r = (Math.random() * 16) | 0;
|
|
5
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
6
|
+
return v.toString(16);
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=generateUUID.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateUUID.js","sourceRoot":"","sources":["../../src/core/generateUUID.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,EAAE,CACvC,sCAAsC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;IAC5D,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAE1C,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC,CAAC,CAAC","sourcesContent":["import { randomUUID } from \"expo-crypto\";\n\nexport const generateUUID = () => randomUUID();\n\nexport const unsecureGenerateUUID = () =>\n \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n\n return v.toString(16);\n });\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { YawlEvent } from "../Yawl.types";
|
|
2
|
+
import { Env } from "./api";
|
|
3
|
+
export default class Yawl {
|
|
4
|
+
private visitId;
|
|
5
|
+
private visitorId;
|
|
6
|
+
private offlineMode;
|
|
7
|
+
private hasInternetAccess;
|
|
8
|
+
private queue;
|
|
9
|
+
private api;
|
|
10
|
+
constructor({ apiKey, env }: {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
env?: Env;
|
|
13
|
+
});
|
|
14
|
+
init: () => Promise<void>;
|
|
15
|
+
setVisitId: (visitId: string) => Promise<void>;
|
|
16
|
+
track: (event: YawlEvent) => YawlEvent;
|
|
17
|
+
private loadVisitorId;
|
|
18
|
+
private getVisitData;
|
|
19
|
+
private initConnection;
|
|
20
|
+
private initQueue;
|
|
21
|
+
private trackVisit;
|
|
22
|
+
private createJob;
|
|
23
|
+
private prepareProperties;
|
|
24
|
+
private addTrackingWorker;
|
|
25
|
+
private addVisitorWorker;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAoB,MAAM,OAAO,CAAC;AA6B9C,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,GAAG,CAAU;gBAET,EAAE,MAAM,EAAE,GAAY,EAAE,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE;IAMnE,IAAI,sBAMF;IAEF,UAAU,GAAU,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAMjD;IAEF,KAAK,GAAI,OAAO,SAAS,eAgBvB;YAEY,aAAa;YASb,YAAY;IAU1B,OAAO,CAAC,cAAc,CAMpB;IAEF,OAAO,CAAC,SAAS,CAIf;IAEF,OAAO,CAAC,UAAU,CAAwC;IAE1D,OAAO,CAAC,SAAS,CAQf;IAEF,OAAO,CAAC,iBAAiB,CAUvB;IAEF,OAAO,CAAC,iBAAiB,CA4BvB;IAEF,OAAO,CAAC,gBAAgB,CA6BtB;CACH"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
2
|
+
import { addNetworkStateListener, getNetworkStateAsync } from "expo-network";
|
|
3
|
+
import queueFactory from "react-native-queue";
|
|
4
|
+
import { yawlApi } from "./api";
|
|
5
|
+
import { getDeviceInfo } from "./deviceInfo";
|
|
6
|
+
import { generateUUID } from "./generateUUID";
|
|
7
|
+
/*
|
|
8
|
+
|
|
9
|
+
INPUT PARAMETERS:
|
|
10
|
+
{
|
|
11
|
+
visitorId: string - visitor id
|
|
12
|
+
visitData: object - extra visit data to be sent to ahoy server
|
|
13
|
+
onTracking: {
|
|
14
|
+
started: async (event) - function that will be invoked after tracking start
|
|
15
|
+
succeeded: async (event) - function that will be invoked when tracking success
|
|
16
|
+
failure: async (event) - function that will be invoked when tracking failure (before failed)
|
|
17
|
+
failed: async (event) - function that will be invoked if tracking fails
|
|
18
|
+
error: async ({ name, error }) - function that will be invoked when error occured
|
|
19
|
+
}
|
|
20
|
+
offlineMode: boolean - ONLY FOR TESTING PURPOSES, indicates if you want to test with no internet
|
|
21
|
+
}
|
|
22
|
+
*/
|
|
23
|
+
const JOB_VISITOR = "visitor";
|
|
24
|
+
const JOB_TRACKING = "tracking";
|
|
25
|
+
const WORKERS_OPTIONS = {
|
|
26
|
+
timeout: 20000,
|
|
27
|
+
attempts: 1000,
|
|
28
|
+
concurrency: 1,
|
|
29
|
+
};
|
|
30
|
+
export default class Yawl {
|
|
31
|
+
visitId;
|
|
32
|
+
visitorId;
|
|
33
|
+
offlineMode = false;
|
|
34
|
+
hasInternetAccess = true;
|
|
35
|
+
queue;
|
|
36
|
+
api;
|
|
37
|
+
constructor({ apiKey, env = "prod" }) {
|
|
38
|
+
this.api = yawlApi({ apiKey, env });
|
|
39
|
+
this.visitId = generateUUID();
|
|
40
|
+
this.visitorId = generateUUID();
|
|
41
|
+
}
|
|
42
|
+
init = async () => {
|
|
43
|
+
await this.initConnection();
|
|
44
|
+
await this.initQueue();
|
|
45
|
+
await this.loadVisitorId();
|
|
46
|
+
const data = await this.getVisitData();
|
|
47
|
+
this.createJob(JOB_VISITOR, data);
|
|
48
|
+
};
|
|
49
|
+
setVisitId = async (visitId) => {
|
|
50
|
+
this.visitId = visitId;
|
|
51
|
+
if (this.hasInternetAccess) {
|
|
52
|
+
this.trackVisit(await this.getVisitData());
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
track = (event) => {
|
|
56
|
+
const _event = {
|
|
57
|
+
event: {
|
|
58
|
+
id: generateUUID(),
|
|
59
|
+
visit_token: this.visitId,
|
|
60
|
+
visitor_token: this.visitorId,
|
|
61
|
+
timestamp: new Date().getTime() / 1000.0,
|
|
62
|
+
...event,
|
|
63
|
+
// properties: this.prepareProperties(event.properties),
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
// Send event to all services before ahoy queue
|
|
67
|
+
// await this.onTrackingInvoke("started", event);
|
|
68
|
+
// Create ahoy job and add to queue
|
|
69
|
+
this.createJob(JOB_TRACKING, _event);
|
|
70
|
+
return event;
|
|
71
|
+
};
|
|
72
|
+
async loadVisitorId() {
|
|
73
|
+
const visitorIdKey = "yawl_visitorId";
|
|
74
|
+
const visitorId = await AsyncStorage.getItem(visitorIdKey);
|
|
75
|
+
if (!visitorId)
|
|
76
|
+
return await AsyncStorage.setItem(visitorIdKey, this.visitorId);
|
|
77
|
+
this.visitorId = visitorId;
|
|
78
|
+
}
|
|
79
|
+
async getVisitData() {
|
|
80
|
+
return {
|
|
81
|
+
visit: {
|
|
82
|
+
visit_token: this.visitId,
|
|
83
|
+
visitor_token: this.visitorId,
|
|
84
|
+
...getDeviceInfo(),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
initConnection = async () => {
|
|
89
|
+
const state = await getNetworkStateAsync();
|
|
90
|
+
this.hasInternetAccess = state.isConnected;
|
|
91
|
+
addNetworkStateListener((state) => (this.hasInternetAccess = state.isConnected));
|
|
92
|
+
};
|
|
93
|
+
initQueue = async () => {
|
|
94
|
+
this.queue = await queueFactory(true);
|
|
95
|
+
this.addTrackingWorker();
|
|
96
|
+
this.addVisitorWorker();
|
|
97
|
+
};
|
|
98
|
+
trackVisit = (event) => this.api.sendVisit(event);
|
|
99
|
+
createJob = (name, event) => {
|
|
100
|
+
if (this.offlineMode) {
|
|
101
|
+
this.hasInternetAccess = false;
|
|
102
|
+
}
|
|
103
|
+
if (this.queue) {
|
|
104
|
+
this.queue.createJob(name, event);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
prepareProperties = (inter) => {
|
|
108
|
+
const properties = {
|
|
109
|
+
// applicationBundleId: this.applicationBundleId,
|
|
110
|
+
};
|
|
111
|
+
Object.keys(inter).map((name, key) => {
|
|
112
|
+
const k = name.replace(/[^a-z0-9_]/gi, "");
|
|
113
|
+
const val = inter[name];
|
|
114
|
+
properties[k] = typeof val === "boolean" ? val.toString() : val;
|
|
115
|
+
});
|
|
116
|
+
return properties;
|
|
117
|
+
};
|
|
118
|
+
addTrackingWorker = () => {
|
|
119
|
+
this.queue.addWorker(JOB_TRACKING, async (id, event) => {
|
|
120
|
+
if (this.hasInternetAccess) {
|
|
121
|
+
const res = await this.api.sendEvent(event);
|
|
122
|
+
// TODO: parse response
|
|
123
|
+
console.debug("🚀 ===> ~ res:", res);
|
|
124
|
+
return { ok: true };
|
|
125
|
+
}
|
|
126
|
+
throw new Error("Network request failed");
|
|
127
|
+
}, {
|
|
128
|
+
onSuccess: async (id, event) => {
|
|
129
|
+
console.debug("🚀 ===> JOB_TRACKING onSuccess", id, event);
|
|
130
|
+
// await this.onTrackingInvoke("succeeded", event);
|
|
131
|
+
},
|
|
132
|
+
onFailure: async (id, event, error) => {
|
|
133
|
+
console.debug("🚀 ===> JOB_TRACKING onFailure", id, event, error);
|
|
134
|
+
// await this.onTrackingInvoke("failure", event, error);
|
|
135
|
+
},
|
|
136
|
+
onFailed: async (id, event, error) => {
|
|
137
|
+
console.debug("🚀 ===> JOB_TRACKING onFailed", id, event, error);
|
|
138
|
+
// await this.onTrackingInvoke("failed", event, error);
|
|
139
|
+
},
|
|
140
|
+
...WORKERS_OPTIONS,
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
addVisitorWorker = () => {
|
|
144
|
+
this.queue.addWorker(JOB_VISITOR, async (id, event) => {
|
|
145
|
+
if (this.hasInternetAccess) {
|
|
146
|
+
const res = await this.trackVisit(event);
|
|
147
|
+
// TODO: parse response
|
|
148
|
+
console.debug("🚀 ===> ~ res:", res);
|
|
149
|
+
return { ok: true };
|
|
150
|
+
}
|
|
151
|
+
throw new Error("Network request failed");
|
|
152
|
+
}, {
|
|
153
|
+
onSuccess: async (id, event) => {
|
|
154
|
+
console.debug("🚀 ===> JOB_VISITOR ~ onSuccess: ~ event:", id, event);
|
|
155
|
+
// await this.onTrackingInvoke("succeeded", event);
|
|
156
|
+
},
|
|
157
|
+
onFailure: async (id, event, error) => {
|
|
158
|
+
console.debug("🚀 ===> JOB_VISITOR ~ onFailure: ~ error:", id, error);
|
|
159
|
+
// await this.onTrackingInvoke("failure", event, error);
|
|
160
|
+
},
|
|
161
|
+
onFailed: async (id, event, error) => {
|
|
162
|
+
console.debug("🚀 ===> JOB_VISITOR ~ onFailed: ~ error:", id, error);
|
|
163
|
+
// await this.onTrackingInvoke("failed", event, error);
|
|
164
|
+
},
|
|
165
|
+
...WORKERS_OPTIONS,
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from \"@react-native-async-storage/async-storage\";\nimport { addNetworkStateListener, getNetworkStateAsync } from \"expo-network\";\nimport queueFactory from \"react-native-queue\";\n\nimport { YawlEvent } from \"../Yawl.types\";\nimport { Env, YawlApi, yawlApi } from \"./api\";\nimport { getDeviceInfo } from \"./deviceInfo\";\nimport { generateUUID } from \"./generateUUID\";\n\n/*\n\nINPUT PARAMETERS:\n{\n visitorId: string - visitor id\n visitData: object - extra visit data to be sent to ahoy server\n onTracking: {\n started: async (event) - function that will be invoked after tracking start\n succeeded: async (event) - function that will be invoked when tracking success\n failure: async (event) - function that will be invoked when tracking failure (before failed)\n failed: async (event) - function that will be invoked if tracking fails\n error: async ({ name, error }) - function that will be invoked when error occured\n }\n offlineMode: boolean - ONLY FOR TESTING PURPOSES, indicates if you want to test with no internet\n}\n*/\n\nconst JOB_VISITOR = \"visitor\";\nconst JOB_TRACKING = \"tracking\";\ntype JOB_TYPES = typeof JOB_TRACKING | typeof JOB_VISITOR;\nconst WORKERS_OPTIONS = {\n timeout: 20000,\n attempts: 1000,\n concurrency: 1,\n};\nexport default class Yawl {\n private visitId: string;\n private visitorId: string;\n private offlineMode: boolean = false;\n private hasInternetAccess: boolean | undefined = true;\n private queue: any;\n private api: YawlApi;\n\n constructor({ apiKey, env = \"prod\" }: { apiKey: string; env?: Env }) {\n this.api = yawlApi({ apiKey, env });\n this.visitId = generateUUID();\n this.visitorId = generateUUID();\n }\n\n init = async () => {\n await this.initConnection();\n await this.initQueue();\n await this.loadVisitorId();\n const data = await this.getVisitData();\n this.createJob(JOB_VISITOR, data);\n };\n\n setVisitId = async (visitId: string): Promise<void> => {\n this.visitId = visitId;\n\n if (this.hasInternetAccess) {\n this.trackVisit(await this.getVisitData());\n }\n };\n\n track = (event: YawlEvent) => {\n const _event = {\n event: {\n id: generateUUID(),\n visit_token: this.visitId,\n visitor_token: this.visitorId,\n timestamp: new Date().getTime() / 1000.0,\n ...event,\n // properties: this.prepareProperties(event.properties),\n },\n };\n // Send event to all services before ahoy queue\n // await this.onTrackingInvoke(\"started\", event);\n // Create ahoy job and add to queue\n this.createJob(JOB_TRACKING, _event);\n return event;\n };\n\n private async loadVisitorId(): Promise<void> {\n const visitorIdKey = \"yawl_visitorId\";\n const visitorId = await AsyncStorage.getItem(visitorIdKey);\n if (!visitorId)\n return await AsyncStorage.setItem(visitorIdKey, this.visitorId);\n\n this.visitorId = visitorId;\n }\n\n private async getVisitData(): Promise<object> {\n return {\n visit: {\n visit_token: this.visitId,\n visitor_token: this.visitorId,\n ...getDeviceInfo(),\n },\n };\n }\n\n private initConnection = async () => {\n const state = await getNetworkStateAsync();\n this.hasInternetAccess = state.isConnected;\n addNetworkStateListener(\n (state) => (this.hasInternetAccess = state.isConnected)\n );\n };\n\n private initQueue = async () => {\n this.queue = await queueFactory(true);\n this.addTrackingWorker();\n this.addVisitorWorker();\n };\n\n private trackVisit = (event) => this.api.sendVisit(event);\n\n private createJob = (name: JOB_TYPES, event: object) => {\n if (this.offlineMode) {\n this.hasInternetAccess = false;\n }\n\n if (this.queue) {\n this.queue.createJob(name, event);\n }\n };\n\n private prepareProperties = (inter: object) => {\n const properties = {\n // applicationBundleId: this.applicationBundleId,\n };\n Object.keys(inter).map((name, key) => {\n const k = name.replace(/[^a-z0-9_]/gi, \"\");\n const val = inter[name];\n properties[k] = typeof val === \"boolean\" ? val.toString() : val;\n });\n return properties;\n };\n\n private addTrackingWorker = () => {\n this.queue.addWorker(\n JOB_TRACKING,\n async (id, event) => {\n if (this.hasInternetAccess) {\n const res = await this.api.sendEvent(event);\n // TODO: parse response\n console.debug(\"🚀 ===> ~ res:\", res);\n return { ok: true };\n }\n throw new Error(\"Network request failed\");\n },\n {\n onSuccess: async (id, event) => {\n console.debug(\"🚀 ===> JOB_TRACKING onSuccess\", id, event);\n // await this.onTrackingInvoke(\"succeeded\", event);\n },\n onFailure: async (id, event, error) => {\n console.debug(\"🚀 ===> JOB_TRACKING onFailure\", id, event, error);\n // await this.onTrackingInvoke(\"failure\", event, error);\n },\n onFailed: async (id, event, error) => {\n console.debug(\"🚀 ===> JOB_TRACKING onFailed\", id, event, error);\n // await this.onTrackingInvoke(\"failed\", event, error);\n },\n ...WORKERS_OPTIONS,\n }\n );\n };\n\n private addVisitorWorker = () => {\n this.queue.addWorker(\n JOB_VISITOR,\n async (id, event) => {\n if (this.hasInternetAccess) {\n const res = await this.trackVisit(event);\n // TODO: parse response\n console.debug(\"🚀 ===> ~ res:\", res);\n return { ok: true };\n }\n\n throw new Error(\"Network request failed\");\n },\n {\n onSuccess: async (id, event) => {\n console.debug(\"🚀 ===> JOB_VISITOR ~ onSuccess: ~ event:\", id, event);\n // await this.onTrackingInvoke(\"succeeded\", event);\n },\n onFailure: async (id, event, error) => {\n console.debug(\"🚀 ===> JOB_VISITOR ~ onFailure: ~ error:\", id, error);\n // await this.onTrackingInvoke(\"failure\", event, error);\n },\n onFailed: async (id, event, error) => {\n console.debug(\"🚀 ===> JOB_VISITOR ~ onFailed: ~ error:\", id, error);\n // await this.onTrackingInvoke(\"failed\", event, error);\n },\n ...WORKERS_OPTIONS,\n }\n );\n };\n}\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,cAAc,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,2CAA2C;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,cAAc,CAAC","sourcesContent":["// Reexport the native module. On web, it will be resolved to YawlModule.web.ts\n// and on native platforms to YawlModule.ts\nexport { default } from \"./YawlModule\";\nexport * from \"./Yawl.types\";\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edulib-france/expo-yawl",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "Yawl for React Native",
|
|
9
|
+
"main": "build/index.js",
|
|
10
|
+
"types": "build/index.d.ts",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "expo-module build",
|
|
13
|
+
"clean": "expo-module clean",
|
|
14
|
+
"lint": "expo-module lint",
|
|
15
|
+
"test": "vitest",
|
|
16
|
+
"prepare": "expo-module prepare",
|
|
17
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
18
|
+
"expo-module": "expo-module",
|
|
19
|
+
"open:ios": "xed example/ios",
|
|
20
|
+
"open:android": "open -a \"Android Studio\" example/android"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"react-native",
|
|
24
|
+
"expo",
|
|
25
|
+
"@edulib-france/expo-yawl",
|
|
26
|
+
"Yawl"
|
|
27
|
+
],
|
|
28
|
+
"repository": "https://github.com/edulib-france/expo-yawl",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/edulib-france/expo-yawl/issues"
|
|
31
|
+
},
|
|
32
|
+
"author": "Edulib <felix.salazar@edulib.fr> (https://github.com/edulib-france)",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"homepage": "https://github.com/edulib-france/expo-yawl#readme",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@react-native-async-storage/async-storage": "1.23.1",
|
|
37
|
+
"react-native-queue": "https://github.com/Bravado-network/react-native-queue-asyncstorage.git#async-storage-galatea-384",
|
|
38
|
+
"wretch": "^2.11.0",
|
|
39
|
+
"expo-device": "~7.0.3",
|
|
40
|
+
"expo-application": "~6.0.2",
|
|
41
|
+
"expo-network": "~7.0.5",
|
|
42
|
+
"expo-crypto": "~14.0.2"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/react": "~18.3.12",
|
|
46
|
+
"expo": "~52.0.0",
|
|
47
|
+
"expo-module-scripts": "^4.0.4",
|
|
48
|
+
"react-native": "0.76.0",
|
|
49
|
+
"vitest": "^3.1.1"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"expo": "*",
|
|
53
|
+
"react": "*",
|
|
54
|
+
"react-native": "*"
|
|
55
|
+
},
|
|
56
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./core/index";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { yawlApi } from "./api";
|
|
4
|
+
|
|
5
|
+
describe("API Tests", () => {
|
|
6
|
+
const apiConfig = { apiKey: "test-key", env: "staging" as const };
|
|
7
|
+
const data = { visit: { id: "test-id", visitor_id: "test-visitor-id" } };
|
|
8
|
+
it("should failed when wrong key", async () => {
|
|
9
|
+
const api = yawlApi(apiConfig);
|
|
10
|
+
await expect(api.sendVisit(data)).rejects.toThrowError("Unauthorized");
|
|
11
|
+
});
|
|
12
|
+
it("should send visit", async () => {
|
|
13
|
+
const api = yawlApi(apiConfig);
|
|
14
|
+
await expect(api.sendVisit(data)).resolves.toMatchObject({
|
|
15
|
+
message: "Ahoy::Visit created",
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
package/src/core/api.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import wretch from "wretch";
|
|
2
|
+
|
|
3
|
+
export type Env = "prod" | "staging";
|
|
4
|
+
export const API_URL: { [key in Env]: string } = {
|
|
5
|
+
prod: "https://www.edulib.fr",
|
|
6
|
+
staging: "https://staging.edulib.fr",
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export type YawlApi = {
|
|
10
|
+
sendVisit: (data: object) => Promise<any>;
|
|
11
|
+
sendEvent: (data: object) => Promise<any>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const yawlApi = ({
|
|
15
|
+
apiKey,
|
|
16
|
+
env = "prod",
|
|
17
|
+
}: {
|
|
18
|
+
apiKey: string;
|
|
19
|
+
env?: Env;
|
|
20
|
+
}): YawlApi => {
|
|
21
|
+
async function fetchGuard(url: string, data: object) {
|
|
22
|
+
return await wretch(url).headers({ "Api-Key": apiKey }).post(data).json();
|
|
23
|
+
}
|
|
24
|
+
async function sendVisit(data: object) {
|
|
25
|
+
return fetchGuard(`${API_URL[env]}/ahoy/visits`, data);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function sendEvent(data: object) {
|
|
29
|
+
return fetchGuard(`${API_URL[env]}/ahoy/events`, data);
|
|
30
|
+
}
|
|
31
|
+
return { sendVisit, sendEvent };
|
|
32
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { nativeApplicationVersion } from "expo-application";
|
|
2
|
+
import { deviceType, DeviceType, osName, osVersion } from "expo-device";
|
|
3
|
+
|
|
4
|
+
export type YawlDeviceInfo = {
|
|
5
|
+
app_version?: string;
|
|
6
|
+
device_type?: string;
|
|
7
|
+
os?: string;
|
|
8
|
+
os_version?: string;
|
|
9
|
+
platform: string;
|
|
10
|
+
};
|
|
11
|
+
export const getDeviceInfo = (): YawlDeviceInfo => {
|
|
12
|
+
return {
|
|
13
|
+
app_version: nativeApplicationVersion ?? undefined,
|
|
14
|
+
device_type: DeviceType[deviceType ?? 0],
|
|
15
|
+
os: osName ?? undefined,
|
|
16
|
+
os_version: osVersion ?? undefined,
|
|
17
|
+
platform: "react-native",
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { randomUUID } from "expo-crypto";
|
|
2
|
+
|
|
3
|
+
export const generateUUID = () => randomUUID();
|
|
4
|
+
|
|
5
|
+
export const unsecureGenerateUUID = () =>
|
|
6
|
+
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
7
|
+
const r = (Math.random() * 16) | 0;
|
|
8
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
9
|
+
|
|
10
|
+
return v.toString(16);
|
|
11
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
2
|
+
import { addNetworkStateListener, getNetworkStateAsync } from "expo-network";
|
|
3
|
+
import queueFactory from "react-native-queue";
|
|
4
|
+
|
|
5
|
+
import { YawlEvent } from "../Yawl.types";
|
|
6
|
+
import { Env, YawlApi, yawlApi } from "./api";
|
|
7
|
+
import { getDeviceInfo } from "./deviceInfo";
|
|
8
|
+
import { generateUUID } from "./generateUUID";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
|
|
12
|
+
INPUT PARAMETERS:
|
|
13
|
+
{
|
|
14
|
+
visitorId: string - visitor id
|
|
15
|
+
visitData: object - extra visit data to be sent to ahoy server
|
|
16
|
+
onTracking: {
|
|
17
|
+
started: async (event) - function that will be invoked after tracking start
|
|
18
|
+
succeeded: async (event) - function that will be invoked when tracking success
|
|
19
|
+
failure: async (event) - function that will be invoked when tracking failure (before failed)
|
|
20
|
+
failed: async (event) - function that will be invoked if tracking fails
|
|
21
|
+
error: async ({ name, error }) - function that will be invoked when error occured
|
|
22
|
+
}
|
|
23
|
+
offlineMode: boolean - ONLY FOR TESTING PURPOSES, indicates if you want to test with no internet
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const JOB_VISITOR = "visitor";
|
|
28
|
+
const JOB_TRACKING = "tracking";
|
|
29
|
+
type JOB_TYPES = typeof JOB_TRACKING | typeof JOB_VISITOR;
|
|
30
|
+
const WORKERS_OPTIONS = {
|
|
31
|
+
timeout: 20000,
|
|
32
|
+
attempts: 1000,
|
|
33
|
+
concurrency: 1,
|
|
34
|
+
};
|
|
35
|
+
export default class Yawl {
|
|
36
|
+
private visitId: string;
|
|
37
|
+
private visitorId: string;
|
|
38
|
+
private offlineMode: boolean = false;
|
|
39
|
+
private hasInternetAccess: boolean | undefined = true;
|
|
40
|
+
private queue: any;
|
|
41
|
+
private api: YawlApi;
|
|
42
|
+
|
|
43
|
+
constructor({ apiKey, env = "prod" }: { apiKey: string; env?: Env }) {
|
|
44
|
+
this.api = yawlApi({ apiKey, env });
|
|
45
|
+
this.visitId = generateUUID();
|
|
46
|
+
this.visitorId = generateUUID();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
init = async () => {
|
|
50
|
+
await this.initConnection();
|
|
51
|
+
await this.initQueue();
|
|
52
|
+
await this.loadVisitorId();
|
|
53
|
+
const data = await this.getVisitData();
|
|
54
|
+
this.createJob(JOB_VISITOR, data);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
setVisitId = async (visitId: string): Promise<void> => {
|
|
58
|
+
this.visitId = visitId;
|
|
59
|
+
|
|
60
|
+
if (this.hasInternetAccess) {
|
|
61
|
+
this.trackVisit(await this.getVisitData());
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
track = (event: YawlEvent) => {
|
|
66
|
+
const _event = {
|
|
67
|
+
event: {
|
|
68
|
+
id: generateUUID(),
|
|
69
|
+
visit_token: this.visitId,
|
|
70
|
+
visitor_token: this.visitorId,
|
|
71
|
+
timestamp: new Date().getTime() / 1000.0,
|
|
72
|
+
...event,
|
|
73
|
+
// properties: this.prepareProperties(event.properties),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
// Send event to all services before ahoy queue
|
|
77
|
+
// await this.onTrackingInvoke("started", event);
|
|
78
|
+
// Create ahoy job and add to queue
|
|
79
|
+
this.createJob(JOB_TRACKING, _event);
|
|
80
|
+
return event;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
private async loadVisitorId(): Promise<void> {
|
|
84
|
+
const visitorIdKey = "yawl_visitorId";
|
|
85
|
+
const visitorId = await AsyncStorage.getItem(visitorIdKey);
|
|
86
|
+
if (!visitorId)
|
|
87
|
+
return await AsyncStorage.setItem(visitorIdKey, this.visitorId);
|
|
88
|
+
|
|
89
|
+
this.visitorId = visitorId;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private async getVisitData(): Promise<object> {
|
|
93
|
+
return {
|
|
94
|
+
visit: {
|
|
95
|
+
visit_token: this.visitId,
|
|
96
|
+
visitor_token: this.visitorId,
|
|
97
|
+
...getDeviceInfo(),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private initConnection = async () => {
|
|
103
|
+
const state = await getNetworkStateAsync();
|
|
104
|
+
this.hasInternetAccess = state.isConnected;
|
|
105
|
+
addNetworkStateListener(
|
|
106
|
+
(state) => (this.hasInternetAccess = state.isConnected)
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
private initQueue = async () => {
|
|
111
|
+
this.queue = await queueFactory(true);
|
|
112
|
+
this.addTrackingWorker();
|
|
113
|
+
this.addVisitorWorker();
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
private trackVisit = (event) => this.api.sendVisit(event);
|
|
117
|
+
|
|
118
|
+
private createJob = (name: JOB_TYPES, event: object) => {
|
|
119
|
+
if (this.offlineMode) {
|
|
120
|
+
this.hasInternetAccess = false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (this.queue) {
|
|
124
|
+
this.queue.createJob(name, event);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
private prepareProperties = (inter: object) => {
|
|
129
|
+
const properties = {
|
|
130
|
+
// applicationBundleId: this.applicationBundleId,
|
|
131
|
+
};
|
|
132
|
+
Object.keys(inter).map((name, key) => {
|
|
133
|
+
const k = name.replace(/[^a-z0-9_]/gi, "");
|
|
134
|
+
const val = inter[name];
|
|
135
|
+
properties[k] = typeof val === "boolean" ? val.toString() : val;
|
|
136
|
+
});
|
|
137
|
+
return properties;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
private addTrackingWorker = () => {
|
|
141
|
+
this.queue.addWorker(
|
|
142
|
+
JOB_TRACKING,
|
|
143
|
+
async (id, event) => {
|
|
144
|
+
if (this.hasInternetAccess) {
|
|
145
|
+
const res = await this.api.sendEvent(event);
|
|
146
|
+
// TODO: parse response
|
|
147
|
+
console.debug("🚀 ===> ~ res:", res);
|
|
148
|
+
return { ok: true };
|
|
149
|
+
}
|
|
150
|
+
throw new Error("Network request failed");
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
onSuccess: async (id, event) => {
|
|
154
|
+
console.debug("🚀 ===> JOB_TRACKING onSuccess", id, event);
|
|
155
|
+
// await this.onTrackingInvoke("succeeded", event);
|
|
156
|
+
},
|
|
157
|
+
onFailure: async (id, event, error) => {
|
|
158
|
+
console.debug("🚀 ===> JOB_TRACKING onFailure", id, event, error);
|
|
159
|
+
// await this.onTrackingInvoke("failure", event, error);
|
|
160
|
+
},
|
|
161
|
+
onFailed: async (id, event, error) => {
|
|
162
|
+
console.debug("🚀 ===> JOB_TRACKING onFailed", id, event, error);
|
|
163
|
+
// await this.onTrackingInvoke("failed", event, error);
|
|
164
|
+
},
|
|
165
|
+
...WORKERS_OPTIONS,
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
private addVisitorWorker = () => {
|
|
171
|
+
this.queue.addWorker(
|
|
172
|
+
JOB_VISITOR,
|
|
173
|
+
async (id, event) => {
|
|
174
|
+
if (this.hasInternetAccess) {
|
|
175
|
+
const res = await this.trackVisit(event);
|
|
176
|
+
// TODO: parse response
|
|
177
|
+
console.debug("🚀 ===> ~ res:", res);
|
|
178
|
+
return { ok: true };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
throw new Error("Network request failed");
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
onSuccess: async (id, event) => {
|
|
185
|
+
console.debug("🚀 ===> JOB_VISITOR ~ onSuccess: ~ event:", id, event);
|
|
186
|
+
// await this.onTrackingInvoke("succeeded", event);
|
|
187
|
+
},
|
|
188
|
+
onFailure: async (id, event, error) => {
|
|
189
|
+
console.debug("🚀 ===> JOB_VISITOR ~ onFailure: ~ error:", id, error);
|
|
190
|
+
// await this.onTrackingInvoke("failure", event, error);
|
|
191
|
+
},
|
|
192
|
+
onFailed: async (id, event, error) => {
|
|
193
|
+
console.debug("🚀 ===> JOB_VISITOR ~ onFailed: ~ error:", id, error);
|
|
194
|
+
// await this.onTrackingInvoke("failed", event, error);
|
|
195
|
+
},
|
|
196
|
+
...WORKERS_OPTIONS,
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
}
|
package/src/index.ts
ADDED
package/tsconfig.json
ADDED