diff --git a/packages/react-native-contentpass-cmp-consentmanager/README.md b/packages/react-native-contentpass-cmp-consentmanager/README.md new file mode 100644 index 0000000..9563d3b --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/README.md @@ -0,0 +1,146 @@ +# @contentpass/react-native-contentpass-cmp-consentmanager + +A [Consentmanager](https://www.consentmanager.net/) CMP adapter for `@contentpass/react-native-contentpass`. Bridges the Consentmanager SDK (`cm-sdk-react-native-v3`) to the Contentpass `CmpAdapter` interface, so the Contentpass consent layer can manage consent through Consentmanager. + +## Installation + +```bash +npm install @contentpass/react-native-contentpass-cmp-consentmanager +# or +yarn add @contentpass/react-native-contentpass-cmp-consentmanager +``` + +### Peer dependencies + +- `@contentpass/react-native-contentpass` +- `cm-sdk-react-native-v3` — the Consentmanager React Native SDK must be installed and configured in your project + +## Usage + +First, initialize the Consentmanager SDK, then create the adapter using `createConsentmanagerCmpAdapter`: + +```tsx +import CmSdkReactNativeV3, { + setUrlConfig, + setWebViewConfig, + checkAndOpen, + WebViewPosition, + BackgroundStyle, + BlurEffectStyle, +} from 'cm-sdk-react-native-v3'; +import { createConsentmanagerCmpAdapter } from '@contentpass/react-native-contentpass-cmp-consentmanager'; +import type { CmpAdapter } from '@contentpass/react-native-contentpass'; + +// 1. Configure the Consentmanager SDK +await setWebViewConfig({ + position: WebViewPosition.FullScreen, + backgroundStyle: BackgroundStyle.dimmed('black', 0.5), +}); + +await setUrlConfig({ + id: 'YOUR_CODE_ID', + domain: 'delivery.consentmanager.net', + language: 'EN', + appName: 'MyApp', +}); + +// 2. Initialize consent checking +await checkAndOpen(false); + +// 3. Create the CMP adapter +const cmpAdapter: CmpAdapter = await createConsentmanagerCmpAdapter(CmSdkReactNativeV3); +``` + +The returned `cmpAdapter` can then be passed to `ContentpassConsentGate` from `@contentpass/react-native-contentpass-ui`, or used directly via the `CmpAdapter` interface. + +### Full example + +```tsx +import { useEffect, useState } from 'react'; +import { View, Text } from 'react-native'; +import CmSdkReactNativeV3, { + setUrlConfig, + setWebViewConfig, + checkAndOpen, + WebViewPosition, + BackgroundStyle, +} from 'cm-sdk-react-native-v3'; +import { ContentpassSdkProvider } from '@contentpass/react-native-contentpass'; +import { ContentpassConsentGate } from '@contentpass/react-native-contentpass-ui'; +import { createConsentmanagerCmpAdapter } from '@contentpass/react-native-contentpass-cmp-consentmanager'; +import type { CmpAdapter } from '@contentpass/react-native-contentpass'; + +export default function App() { + const [cmpAdapter, setCmpAdapter] = useState(null); + + useEffect(() => { + async function init() { + await setWebViewConfig({ + position: WebViewPosition.FullScreen, + backgroundStyle: BackgroundStyle.dimmed('black', 0.5), + }); + await setUrlConfig({ + id: 'YOUR_CODE_ID', + domain: 'delivery.consentmanager.net', + language: 'EN', + appName: 'MyApp', + }); + await checkAndOpen(false); + const adapter = await createConsentmanagerCmpAdapter(CmSdkReactNativeV3); + setCmpAdapter(adapter); + } + + init().catch((error) => console.error('Failed to initialize CMP', error)); + }, []); + + if (!cmpAdapter) { + return Loading...; + } + + return ( + + + + Your app content + + + + ); +} +``` + +For further details on the Consentmanager SDK, see their [official documentation](https://help.consentmanager.net/books/cmp/page/reactnative-1-consentmanager-sdk-integration-9b0). + +## API + +### `createConsentmanagerCmpAdapter(sdk)` + +Factory function that creates a `CmpAdapter` from an initialized Consentmanager SDK instance. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `sdk` | `CmSdkReactNativeV3Module` | The Consentmanager SDK default export (after `setUrlConfig` and `checkAndOpen` have been called). | + +Returns `Promise`. + +The adapter fetches user status from the Consentmanager SDK during creation and automatically extracts purpose IDs and the vendor count. + +### `CmpAdapter` methods provided + +| Method | Description | +|--------|-------------| +| `acceptAll()` | Programmatically accepts all consent purposes via Consentmanager. | +| `denyAll()` | Programmatically rejects all consent purposes via Consentmanager. | +| `hasFullConsent()` | Checks whether all consent purposes are granted by querying each purpose status. | +| `onConsentStatusChange(callback)` | Registers a listener that fires whenever full-consent status changes. Returns an unsubscribe function. | +| `showSecondLayer(view)` | Opens the Consentmanager consent layer settings page via `forceOpen(true)`. The returned promise resolves when the user dismisses it. | +| `getRequiredPurposes()` | Returns the list of purpose identifiers extracted from the Consentmanager user status. | +| `getNumberOfVendors()` | Returns the vendor count from the Consentmanager user status. | +| `waitForInit()` | Resolves immediately (Consentmanager initialization is handled before adapter creation). | + +## License + +MIT diff --git a/packages/react-native-contentpass-cmp-consentmanager/babel.config.js b/packages/react-native-contentpass-cmp-consentmanager/babel.config.js new file mode 100644 index 0000000..4c05afb --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/babel.config.js @@ -0,0 +1 @@ +module.exports = require('../../babel.config'); diff --git a/packages/react-native-contentpass-cmp-consentmanager/jest-setup.ts b/packages/react-native-contentpass-cmp-consentmanager/jest-setup.ts new file mode 100644 index 0000000..c3b62e7 --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/jest-setup.ts @@ -0,0 +1 @@ +// Jest setup file diff --git a/packages/react-native-contentpass-cmp-consentmanager/jest.config.json b/packages/react-native-contentpass-cmp-consentmanager/jest.config.json new file mode 100644 index 0000000..9212b68 --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/jest.config.json @@ -0,0 +1,15 @@ +{ + "preset": "react-native", + "setupFilesAfterEnv": ["./jest-setup.ts"], + "transform": { + "^.+\\.(js|ts|tsx)$": "babel-jest" + }, + "modulePathIgnorePatterns": [ + "./lib/", + "./ios/", + "./android/" + ], + "moduleNameMapper": { + "^cm-sdk-react-native-v3$": "/src/__mocks__/cm-sdk-react-native-v3.ts" + } +} diff --git a/packages/react-native-contentpass-cmp-consentmanager/package.json b/packages/react-native-contentpass-cmp-consentmanager/package.json new file mode 100644 index 0000000..0eaafcd --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/package.json @@ -0,0 +1,86 @@ +{ + "name": "@contentpass/react-native-contentpass-cmp-consentmanager", + "version": "0.1.0", + "description": "Contentpass Consentmanager CMP adapter", + "source": "./src/index.ts", + "main": "./lib/commonjs/index.js", + "module": "./lib/module/index.js", + "exports": { + ".": { + "import": { + "types": "./lib/typescript/react-native-contentpass-cmp-consentmanager/src/index.d.ts", + "default": "./lib/module/index.js" + }, + "require": { + "types": "./lib/typescript/react-native-contentpass-cmp-consentmanager/src/index.d.ts", + "default": "./lib/commonjs/index.js" + } + } + }, + "types": "./lib/typescript/react-native-contentpass-cmp-consentmanager/src/index.d.ts", + "files": [ + "lib", + "!**/*.test.ts", + "!**/*.test.tsx", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "test": "jest --coverage", + "typecheck": "tsc", + "prepare": "bob build" + }, + "keywords": [ + "contentpass", + "react-native", + "cmp", + "consentmanager" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/contentpass/react-native-contentpass.git" + }, + "author": "contentpass (https://github.com/contentpass)", + "license": "MIT", + "bugs": { + "url": "https://github.com/contentpass/react-native-contentpass/issues" + }, + "homepage": "https://github.com/contentpass/react-native-contentpass#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "peerDependencies": { + "@contentpass/react-native-contentpass": "*", + "cm-sdk-react-native-v3": "*" + }, + "devDependencies": { + "@contentpass/react-native-contentpass": "workspace:*", + "@react-native-community/cli": "20.1.1", + "@react-native/babel-preset": "0.83.1", + "@types/jest": "^30.0.0", + "@types/react": "^19.2.10", + "jest": "^30.2.0", + "react": "19.2.4", + "react-native": "0.83.1", + "react-native-builder-bob": "^0.40.17", + "typescript": "^5.9.3" + }, + "react-native-builder-bob": { + "source": "src", + "exclude": "**/{__tests__,__fixtures__,__mocks__}/**", + "output": "lib", + "targets": [ + "commonjs", + "module", + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + } +} diff --git a/packages/react-native-contentpass-cmp-consentmanager/src/ConsentmanagerCmpAdapter.test.ts b/packages/react-native-contentpass-cmp-consentmanager/src/ConsentmanagerCmpAdapter.test.ts new file mode 100644 index 0000000..6cc8798 --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/src/ConsentmanagerCmpAdapter.test.ts @@ -0,0 +1,250 @@ +import { + addConsentListener, + addCloseConsentLayerListener, +} from 'cm-sdk-react-native-v3'; +import type { CmSdkReactNativeV3Module } from 'cm-sdk-react-native-v3'; +import ConsentmanagerCmpAdapter, { + createConsentmanagerCmpAdapter, +} from './ConsentmanagerCmpAdapter'; + +const mockAddConsentListener = addConsentListener as jest.Mock; +const mockAddCloseConsentLayerListener = + addCloseConsentLayerListener as jest.Mock; + +function createMockSdk( + overrides: Partial = {} +): CmSdkReactNativeV3Module { + return { + checkAndOpen: jest.fn().mockResolvedValue(true), + forceOpen: jest.fn().mockResolvedValue(true), + getUserStatus: jest.fn().mockResolvedValue({ + status: 'consentGiven', + vendors: { s2789: 'granted', s2790: 'granted' }, + purposes: { c51: 'granted', c52: 'granted', c53: 'denied' }, + tcf: '', + addtlConsent: '', + regulation: 'gdpr', + }), + acceptAll: jest.fn().mockResolvedValue(true), + rejectAll: jest.fn().mockResolvedValue(true), + getStatusForPurpose: jest.fn().mockResolvedValue('granted'), + getStatusForVendor: jest.fn().mockResolvedValue('granted'), + ...overrides, + }; +} + +describe('createConsentmanagerCmpAdapter', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should create an adapter from the SDK', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + expect(adapter).toBeInstanceOf(ConsentmanagerCmpAdapter); + expect(sdk.getUserStatus).toHaveBeenCalled(); + }); + + it('should throw if getUserStatus fails', async () => { + const sdk = createMockSdk({ + getUserStatus: jest.fn().mockRejectedValue(new Error('SDK not ready')), + }); + + await expect(createConsentmanagerCmpAdapter(sdk)).rejects.toThrow( + 'SDK not ready' + ); + }); +}); + +describe('ConsentmanagerCmpAdapter', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockAddConsentListener.mockReturnValue({ remove: jest.fn() }); + mockAddCloseConsentLayerListener.mockReturnValue({ remove: jest.fn() }); + }); + + describe('waitForInit', () => { + it('should resolve immediately', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.waitForInit()).resolves.toBeUndefined(); + }); + }); + + describe('acceptAll', () => { + it('should call sdk.acceptAll', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await adapter.acceptAll(); + expect(sdk.acceptAll).toHaveBeenCalled(); + }); + + it('should emit consent status change after accepting', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + const listener = jest.fn(); + adapter.onConsentStatusChange(listener); + + await new Promise((resolve) => setTimeout(resolve, 10)); + listener.mockClear(); + + await adapter.acceptAll(); + expect(listener).toHaveBeenCalledWith(true); + }); + }); + + describe('denyAll', () => { + it('should call sdk.rejectAll', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await adapter.denyAll(); + expect(sdk.rejectAll).toHaveBeenCalled(); + }); + }); + + describe('getNumberOfVendors', () => { + it('should return the vendor count from initial user status', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.getNumberOfVendors()).resolves.toBe(2); + }); + + it('should return 0 when no vendors exist', async () => { + const sdk = createMockSdk({ + getUserStatus: jest.fn().mockResolvedValue({ + status: 'consentGiven', + vendors: {}, + purposes: { c51: 'granted' }, + tcf: '', + addtlConsent: '', + regulation: 'gdpr', + }), + }); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.getNumberOfVendors()).resolves.toBe(0); + }); + }); + + describe('getRequiredPurposes', () => { + it('should return purpose IDs from initial user status', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.getRequiredPurposes()).resolves.toEqual([ + 'c51', + 'c52', + 'c53', + ]); + }); + }); + + describe('hasFullConsent', () => { + it('should return true when all purposes are granted', async () => { + const sdk = createMockSdk({ + getStatusForPurpose: jest.fn().mockResolvedValue('granted'), + }); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.hasFullConsent()).resolves.toBe(true); + }); + + it('should return false when any purpose is denied', async () => { + const sdk = createMockSdk({ + getStatusForPurpose: jest + .fn() + .mockResolvedValueOnce('granted') + .mockResolvedValueOnce('denied') + .mockResolvedValueOnce('granted'), + }); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.hasFullConsent()).resolves.toBe(false); + }); + + it('should return false when choice does not exist', async () => { + const sdk = createMockSdk({ + getStatusForPurpose: jest + .fn() + .mockResolvedValue('choiceDoesntExist'), + }); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.hasFullConsent()).resolves.toBe(false); + }); + + it('should return false when no purposes exist', async () => { + const sdk = createMockSdk({ + getUserStatus: jest.fn().mockResolvedValue({ + status: '', + vendors: {}, + purposes: {}, + tcf: '', + addtlConsent: '', + regulation: '', + }), + }); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await expect(adapter.hasFullConsent()).resolves.toBe(false); + }); + }); + + describe('onConsentStatusChange', () => { + it('should emit initial consent status asynchronously', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + const listener = jest.fn(); + + adapter.onConsentStatusChange(listener); + expect(listener).not.toHaveBeenCalled(); + + await new Promise((resolve) => setTimeout(resolve, 10)); + expect(listener).toHaveBeenCalledWith(true); + }); + + it('should return an unsubscribe function', async () => { + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + const listener = jest.fn(); + + const unsubscribe = adapter.onConsentStatusChange(listener); + unsubscribe(); + + await new Promise((resolve) => setTimeout(resolve, 10)); + expect(listener).not.toHaveBeenCalled(); + }); + }); + + describe('showSecondLayer', () => { + it('should call forceOpen(true) on the SDK', async () => { + const mockRemove = jest.fn(); + mockAddCloseConsentLayerListener.mockImplementation( + (cb: () => void) => { + setTimeout(cb, 0); + return { remove: mockRemove }; + } + ); + + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + await adapter.showSecondLayer('purpose'); + + expect(sdk.forceOpen).toHaveBeenCalledWith(true); + expect(mockRemove).toHaveBeenCalled(); + }); + + it('should resolve when the consent layer is closed', async () => { + let closeCallback: (() => void) | undefined; + mockAddCloseConsentLayerListener.mockImplementation( + (cb: () => void) => { + closeCallback = cb; + return { remove: jest.fn() }; + } + ); + + const sdk = createMockSdk(); + const adapter = await createConsentmanagerCmpAdapter(sdk); + + const promise = adapter.showSecondLayer('vendor'); + expect(closeCallback).toBeDefined(); + + closeCallback!(); + await expect(promise).resolves.toBeUndefined(); + }); + }); +}); diff --git a/packages/react-native-contentpass-cmp-consentmanager/src/ConsentmanagerCmpAdapter.ts b/packages/react-native-contentpass-cmp-consentmanager/src/ConsentmanagerCmpAdapter.ts new file mode 100644 index 0000000..3bf18e3 --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/src/ConsentmanagerCmpAdapter.ts @@ -0,0 +1,134 @@ +import type { CmSdkReactNativeV3Module } from 'cm-sdk-react-native-v3'; +import { + addConsentListener, + addCloseConsentLayerListener, +} from 'cm-sdk-react-native-v3'; +import type { CmpAdapter } from '@contentpass/react-native-contentpass'; + +const GRANTED_STATUS = 'granted'; + +export async function createConsentmanagerCmpAdapter( + sdk: CmSdkReactNativeV3Module +): Promise { + try { + const userStatus = await sdk.getUserStatus(); + const purposeIds = Object.keys(userStatus.purposes ?? {}); + const vendorCount = Object.keys(userStatus.vendors ?? {}).length; + + return new ConsentmanagerCmpAdapter(sdk, purposeIds, vendorCount); + } catch (error: any) { + console.error('Error initializing Consentmanager CMP adapter', error); + throw error; + } +} + +export default class ConsentmanagerCmpAdapter implements CmpAdapter { + private readonly eventSubscriptions: Array<{ remove: () => void }> = []; + private readonly consentStatusChangeListeners = new Set< + (fullConsent: boolean) => void + >(); + + constructor( + private readonly sdk: CmSdkReactNativeV3Module, + private readonly purposeIds: string[], + private readonly numVendors: number + ) { + this.initializeEventBridge(); + } + + async waitForInit(): Promise { + return Promise.resolve(); + } + + async acceptAll(): Promise { + console.debug('[ConsentmanagerCmpAdapter::acceptAll]'); + await this.sdk.acceptAll(); + const fullConsent = await this.hasFullConsent(); + this.emitConsentStatusChange(fullConsent); + } + + async denyAll(): Promise { + console.debug('[ConsentmanagerCmpAdapter::denyAll]'); + await this.sdk.rejectAll(); + const fullConsent = await this.hasFullConsent(); + this.emitConsentStatusChange(fullConsent); + } + + getNumberOfVendors(): Promise { + return Promise.resolve(this.numVendors); + } + + getRequiredPurposes(): Promise { + return Promise.resolve(this.purposeIds); + } + + showSecondLayer(_view: 'vendor' | 'purpose'): Promise { + console.debug('[ConsentmanagerCmpAdapter::showSecondLayer]', _view); + return new Promise((resolve) => { + const subscription = addCloseConsentLayerListener(() => { + subscription.remove(); + this.hasFullConsent().then((fullConsent) => { + this.emitConsentStatusChange(fullConsent); + resolve(); + }); + }); + + this.sdk.forceOpen(true); + }); + } + + hasFullConsent = async (): Promise => { + console.debug('[ConsentmanagerCmpAdapter::hasFullConsent]'); + if (this.purposeIds.length === 0) { + return false; + } + + const statuses = await Promise.all( + this.purposeIds.map((id) => this.sdk.getStatusForPurpose(id)) + ); + + return statuses.every((status: string) => status === GRANTED_STATUS); + }; + + onConsentStatusChange(callback: (fullConsent: boolean) => void): () => void { + this.consentStatusChangeListeners.add(callback); + setTimeout(() => { + if (!this.consentStatusChangeListeners.has(callback)) { + return; + } + this.hasFullConsent().then((fullConsent) => + this.emitConsentStatusChangeEventSingle(fullConsent, callback) + ); + }, 0); + return () => this.consentStatusChangeListeners.delete(callback); + } + + private initializeEventBridge(): void { + const subscription = addConsentListener(() => { + this.hasFullConsent().then((fullConsent) => { + this.emitConsentStatusChange(fullConsent); + }); + }); + this.eventSubscriptions.push(subscription); + } + + private emitConsentStatusChange(fullConsent: boolean): void { + this.consentStatusChangeListeners.forEach((listener) => + this.emitConsentStatusChangeEventSingle(fullConsent, listener) + ); + } + + private emitConsentStatusChangeEventSingle( + fullConsent: boolean, + listener: (fullConsent: boolean) => void + ): void { + try { + listener(fullConsent); + } catch (error) { + console.error( + '[ConsentmanagerCmpAdapter::emitConsentStatusChangeEventSingle] listener failed', + error + ); + } + } +} diff --git a/packages/react-native-contentpass-cmp-consentmanager/src/__mocks__/cm-sdk-react-native-v3.ts b/packages/react-native-contentpass-cmp-consentmanager/src/__mocks__/cm-sdk-react-native-v3.ts new file mode 100644 index 0000000..11afa81 --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/src/__mocks__/cm-sdk-react-native-v3.ts @@ -0,0 +1,17 @@ +export const addConsentListener = jest.fn(() => ({ remove: jest.fn() })); +export const addCloseConsentLayerListener = jest.fn(() => ({ + remove: jest.fn(), +})); +export const addErrorListener = jest.fn(() => ({ remove: jest.fn() })); + +const CmSdkReactNativeV3 = { + checkAndOpen: jest.fn(), + forceOpen: jest.fn(), + getUserStatus: jest.fn(), + acceptAll: jest.fn(), + rejectAll: jest.fn(), + getStatusForPurpose: jest.fn(), + getStatusForVendor: jest.fn(), +}; + +export default CmSdkReactNativeV3; diff --git a/packages/react-native-contentpass-cmp-consentmanager/src/cm-sdk-react-native-v3.d.ts b/packages/react-native-contentpass-cmp-consentmanager/src/cm-sdk-react-native-v3.d.ts new file mode 100644 index 0000000..67a817c --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/src/cm-sdk-react-native-v3.d.ts @@ -0,0 +1,39 @@ +declare module 'cm-sdk-react-native-v3' { + export interface UserStatus { + status: string; + vendors: Record; + purposes: Record; + tcf: string; + addtlConsent: string; + regulation: string; + } + + export interface CmSdkReactNativeV3Module { + checkAndOpen(jumpToSettings: boolean): Promise; + forceOpen(jumpToSettings: boolean): Promise; + getUserStatus(): Promise; + acceptAll(): Promise; + rejectAll(): Promise; + getStatusForPurpose(purposeId: string): Promise; + getStatusForVendor(vendorId: string): Promise; + } + + interface EmitterSubscription { + remove(): void; + } + + export function addConsentListener( + callback: (consent: string, jsonObject: Object) => void + ): EmitterSubscription; + + export function addCloseConsentLayerListener( + callback: () => void + ): EmitterSubscription; + + export function addErrorListener( + callback: (error: string) => void + ): EmitterSubscription; + + const CmSdkReactNativeV3: CmSdkReactNativeV3Module; + export default CmSdkReactNativeV3; +} diff --git a/packages/react-native-contentpass-cmp-consentmanager/src/index.ts b/packages/react-native-contentpass-cmp-consentmanager/src/index.ts new file mode 100644 index 0000000..f1c8ff5 --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/src/index.ts @@ -0,0 +1,7 @@ +import ConsentmanagerCmpAdapter, { + createConsentmanagerCmpAdapter, +} from './ConsentmanagerCmpAdapter'; + +export default ConsentmanagerCmpAdapter; + +export { createConsentmanagerCmpAdapter }; diff --git a/packages/react-native-contentpass-cmp-consentmanager/tsconfig.build.json b/packages/react-native-contentpass-cmp-consentmanager/tsconfig.build.json new file mode 100644 index 0000000..ccb13aa --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "lib", + "jest-setup.ts", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "node_modules" + ] +} diff --git a/packages/react-native-contentpass-cmp-consentmanager/tsconfig.json b/packages/react-native-contentpass-cmp-consentmanager/tsconfig.json new file mode 100644 index 0000000..dbf311a --- /dev/null +++ b/packages/react-native-contentpass-cmp-consentmanager/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "paths": { + "@contentpass/react-native-contentpass-cmp-consentmanager": ["./src/index"], + "@contentpass/react-native-contentpass": [ + "../react-native-contentpass/src/index" + ], + "cm-sdk-react-native-v3": [ + "./src/cm-sdk-react-native-v3.d.ts" + ] + } + } +} diff --git a/yarn.lock b/yarn.lock index 4b624da..0c4910c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3228,6 +3228,26 @@ __metadata: languageName: unknown linkType: soft +"@contentpass/react-native-contentpass-cmp-consentmanager@workspace:packages/react-native-contentpass-cmp-consentmanager": + version: 0.0.0-use.local + resolution: "@contentpass/react-native-contentpass-cmp-consentmanager@workspace:packages/react-native-contentpass-cmp-consentmanager" + dependencies: + "@contentpass/react-native-contentpass": "workspace:*" + "@react-native-community/cli": 20.1.1 + "@react-native/babel-preset": 0.83.1 + "@types/jest": ^30.0.0 + "@types/react": ^19.2.10 + jest: ^30.2.0 + react: 19.2.4 + react-native: 0.83.1 + react-native-builder-bob: ^0.40.17 + typescript: ^5.9.3 + peerDependencies: + "@contentpass/react-native-contentpass": "*" + cm-sdk-react-native-v3: "*" + languageName: unknown + linkType: soft + "@contentpass/react-native-contentpass-cmp-onetrust@workspace:*, @contentpass/react-native-contentpass-cmp-onetrust@workspace:packages/react-native-contentpass-cmp-onetrust": version: 0.0.0-use.local resolution: "@contentpass/react-native-contentpass-cmp-onetrust@workspace:packages/react-native-contentpass-cmp-onetrust"