@consentera/react-native-consent 2.0.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/package.json ADDED
@@ -0,0 +1,130 @@
1
+ {
2
+ "name": "@consentera/react-native-consent",
3
+ "version": "2.0.0",
4
+ "description": "Consentera Consent SDK for React Native \u2014 DPDP Act 2023 compliant consent collection",
5
+ "main": "lib/commonjs/index",
6
+ "module": "lib/module/index",
7
+ "types": "lib/typescript/src/index.d.ts",
8
+ "react-native": "src/index",
9
+ "source": "src/index",
10
+ "files": [
11
+ "src",
12
+ "lib",
13
+ "!**/__tests__",
14
+ "!**/__fixtures__",
15
+ "!**/__mocks__",
16
+ "!**/.*"
17
+ ],
18
+ "scripts": {
19
+ "example": "npm run --workspace @consentera/react-native-consent-example",
20
+ "test": "jest",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "eslint .",
23
+ "clean": "del-cli android/build lib",
24
+ "prepare": "bob build"
25
+ },
26
+ "keywords": [
27
+ "react-native",
28
+ "ios",
29
+ "android",
30
+ "consent",
31
+ "privacy",
32
+ "dpdp",
33
+ "india",
34
+ "consentera"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/consentera-platform/consentera-sdks.git",
39
+ "directory": "react-native"
40
+ },
41
+ "author": "ConsentEra <sdk@consentera.in>",
42
+ "license": "MIT",
43
+ "bugs": {
44
+ "url": "https://github.com/consentera-platform/consentera-sdks/issues"
45
+ },
46
+ "homepage": "https://github.com/consentera-platform/consentera-sdks/tree/main/react-native#readme",
47
+ "publishConfig": {
48
+ "registry": "https://registry.npmjs.org/"
49
+ },
50
+ "devDependencies": {
51
+ "@commitlint/config-conventional": "^18.4.3",
52
+ "@evilmartians/lefthook": "^1.5.5",
53
+ "@types/jest": "^29.5.14",
54
+ "@types/react": "^18.3.12",
55
+ "commitlint": "^18.4.3",
56
+ "del-cli": "^5.1.0",
57
+ "eslint": "^9.14.0",
58
+ "eslint-config-prettier": "^9.1.0",
59
+ "eslint-plugin-prettier": "^5.1.2",
60
+ "jest": "^29.7.0",
61
+ "prettier": "^3.3.3",
62
+ "react": "18.3.1",
63
+ "react-native": "0.76.5",
64
+ "react-native-builder-bob": "^0.23.2",
65
+ "typescript": "^5.6.3",
66
+ "@eslint/js": "^9.14.0",
67
+ "typescript-eslint": "^8.13.0"
68
+ },
69
+ "peerDependencies": {
70
+ "react": ">=18.2.0",
71
+ "react-native": ">=0.73.0"
72
+ },
73
+ "workspaces": [
74
+ "example"
75
+ ],
76
+ "jest": {
77
+ "preset": "react-native",
78
+ "modulePathIgnorePatterns": [
79
+ "<rootDir>/example/node_modules",
80
+ "<rootDir>/lib/"
81
+ ]
82
+ },
83
+ "commitlint": {
84
+ "extends": [
85
+ "@commitlint/config-conventional"
86
+ ]
87
+ },
88
+ "prettier": {
89
+ "quoteProps": "consistent",
90
+ "singleQuote": true,
91
+ "tabWidth": 2,
92
+ "trailingComma": "es5",
93
+ "useTabs": false
94
+ },
95
+ "react-native-builder-bob": {
96
+ "source": "src",
97
+ "output": "lib",
98
+ "targets": [
99
+ "commonjs",
100
+ "module",
101
+ [
102
+ "typescript",
103
+ {
104
+ "project": "tsconfig.build.json"
105
+ }
106
+ ]
107
+ ]
108
+ },
109
+ "engines": {
110
+ "node": ">=20"
111
+ },
112
+ "sideEffects": false,
113
+ "exports": {
114
+ ".": {
115
+ "types": "./lib/typescript/src/index.d.ts",
116
+ "react-native": "./src/index.ts",
117
+ "import": "./lib/module/index.js",
118
+ "require": "./lib/commonjs/index.js"
119
+ },
120
+ "./package.json": "./package.json"
121
+ },
122
+ "peerDependenciesMeta": {
123
+ "react-native-inappbrowser-reborn": {
124
+ "optional": true
125
+ },
126
+ "react-native-get-random-values": {
127
+ "optional": true
128
+ }
129
+ }
130
+ }
package/src/gate.ts ADDED
@@ -0,0 +1,81 @@
1
+ import type { ConsenteraSession, PrincipalRef } from './session';
2
+
3
+ /**
4
+ * ConsentGate — the mobile equivalent of cookie consent (RN twin of the
5
+ * Android SDK's gate package). Gate third-party SDKs (analytics, ads,
6
+ * attribution) and advertising identifiers on the DP's live consent state:
7
+ * `init` runs only after the purpose validates ALLOW; `revoke` runs when a
8
+ * previously-allowed purpose stops validating (stop the SDK, flush queues,
9
+ * clear stored identifiers). Never "initialize then opt out".
10
+ *
11
+ * Call refresh() on app start and whenever the app returns to the foreground
12
+ * (AppState 'active') — the deep-link callback is best-effort only.
13
+ */
14
+ export interface Tracker {
15
+ purposeCode: string;
16
+ name: string;
17
+ init: () => void;
18
+ revoke: () => void;
19
+ }
20
+
21
+ export interface GateState {
22
+ name: string;
23
+ purposeCode: string;
24
+ allowed: boolean;
25
+ }
26
+
27
+ export class ConsentGate {
28
+ private trackers: Tracker[] = [];
29
+ private started = new Set<string>();
30
+ private listeners = new Set<(states: GateState[]) => void>();
31
+ states: GateState[] = [];
32
+
33
+ /**
34
+ * @param who the person this gate is about, in the ONE shape the lifecycle
35
+ * roads accept. It was a bare `dataPrincipalRef: string` until 2026-09-22,
36
+ * which no longer type-checked against `validate` (tsc: "Argument of type
37
+ * 'string' is not assignable to parameter of type 'PrincipalRef'") and could
38
+ * not express the identifiers the API now requires.
39
+ */
40
+ constructor(private session: ConsenteraSession, private who: PrincipalRef) {}
41
+
42
+ register(tracker: Tracker): void {
43
+ this.trackers.push(tracker);
44
+ this.states = [...this.states, { name: tracker.name, purposeCode: tracker.purposeCode, allowed: false }];
45
+ }
46
+
47
+ onChange(fn: (states: GateState[]) => void): () => void {
48
+ this.listeners.add(fn);
49
+ return () => this.listeners.delete(fn);
50
+ }
51
+
52
+ /** Re-validate every registered purpose; fail-closed on errors. */
53
+ async refresh(): Promise<GateState[]> {
54
+ const next: GateState[] = [];
55
+ for (const t of this.trackers) {
56
+ let allowed = false;
57
+ try {
58
+ const d = await this.session.validate(this.who, t.purposeCode);
59
+ allowed = (d.decision ?? '').toUpperCase() === 'ALLOW';
60
+ } catch {
61
+ allowed = false;
62
+ }
63
+ const wasStarted = this.started.has(t.name);
64
+ if (allowed && !wasStarted) {
65
+ try { t.init(); } catch { /* tracker init must never crash the app */ }
66
+ this.started.add(t.name);
67
+ } else if (!allowed && wasStarted) {
68
+ try { t.revoke(); } catch { /* ditto */ }
69
+ this.started.delete(t.name);
70
+ }
71
+ next.push({ name: t.name, purposeCode: t.purposeCode, allowed });
72
+ }
73
+ this.states = next;
74
+ this.listeners.forEach((fn) => fn(next));
75
+ return next;
76
+ }
77
+
78
+ isAllowed(purposeCode: string): boolean {
79
+ return this.states.some((s) => s.purposeCode === purposeCode && s.allowed);
80
+ }
81
+ }
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @consentera/react-native-consent — DPDP consent for React Native apps.
3
+ *
4
+ * Reoriented 2026-07-20 to the proven hosted-collect session flow (the old
5
+ * native-module cookie/ad-category model targeted a phantom /v1/sdk/* API and
6
+ * could never collect DPDP consent — see ../MOBILE-P0-FINDINGS.md).
7
+ *
8
+ * Pure JS — no native module, no pod install, works in any RN app:
9
+ * const session = new ConsenteraSession({ backendBaseUrl, callbackScheme });
10
+ * const s = await session.createSession({ dataPrincipalRef, noticeInternalName });
11
+ * session.presentConsent(s.consent_url); // hosted page in the browser
12
+ * // on AppState 'active': re-validate (deep link is best-effort)
13
+ * const d = await session.validate(dataPrincipalRef, 'product_analytics');
14
+ * await session.withdraw(dataPrincipalRef, ['product_analytics']);
15
+ * await session.openPortal(dataPrincipalRef); // full DP portal, one tap
16
+ *
17
+ * ConsentGate = the mobile equivalent of cookie consent: register each
18
+ * third-party SDK with init/revoke hooks; refresh() starts/stops them on the
19
+ * live consent state.
20
+ */
21
+ export {
22
+ ConsenteraSession,
23
+ ConsenteraError,
24
+ SDK_VERSION,
25
+ SDK_IDENTIFIER,
26
+ SDK_USER_AGENT,
27
+ setInAppBrowser,
28
+ resolveInAppBrowser,
29
+ newCallbackState,
30
+ parseRetryAfter,
31
+ callbackUrlFor,
32
+ callbackStatusOf,
33
+ portalIdentifier,
34
+ readErrorEnvelope,
35
+ } from './session';
36
+ export type {
37
+ SessionConfig,
38
+ ConsentSession,
39
+ ConsentCallback,
40
+ CallbackStatus,
41
+ ConsentDecision,
42
+ PortalSession,
43
+ CreateSessionRequest,
44
+ PrincipalRef,
45
+ DataPrincipalIdentifiers,
46
+ GuardianVerificationPending,
47
+ InAppBrowserAdapter,
48
+ } from './session';
49
+ export { ConsentGate } from './gate';
50
+ export type { Tracker, GateState } from './gate';