@chipmobilesdk/rn-ads 0.5.0 → 0.5.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.
@@ -1,144 +1,144 @@
1
- /**
2
- * The internal engine port (NFR-008, FR-006).
3
- *
4
- * This interface names no platform and no ad provider. That is what makes an
5
- * iOS adapter an additive change: it implements this, and nothing in the public
6
- * contract moves. `core/` talks only to this shape; the AdMob bridge exists
7
- * behind exactly one implementation of it, in `admobEngine.ts`.
8
- *
9
- * Banners are absent on purpose. The provider owns a banner's whole lifecycle
10
- * through a React component, so `react/AdsBanner` adapts those callbacks
11
- * directly rather than routing them through a handle model that would have to
12
- * pretend a banner is a load-then-show resource.
13
- */
14
-
15
- import type { AdContentRating, FullScreenFormat, Reward, Unsubscribe } from '../config/types';
16
-
17
- /** Privacy state as the engine needs it — no consumer-facing shape leaks in. */
18
- export interface EnginePrivacy {
19
- readonly childDirected?: boolean;
20
- readonly underAgeOfConsent?: boolean;
21
- readonly maxAdContentRating?: AdContentRating;
22
- readonly personalizedAds?: boolean;
23
- readonly testDeviceIds?: readonly string[];
24
- }
25
-
26
- /**
27
- * Privacy that applies to each ad request rather than to initialization.
28
- *
29
- * `personalizedAds` is here, not only in `EnginePrivacy`, because the provider
30
- * reads it per request and `updatePrivacy` may change it without
31
- * reinitializing: a request must carry the value current at request time.
32
- */
33
- export interface EngineRequestOptions {
34
- readonly personalizedAds: boolean;
35
- }
36
-
37
- /**
38
- * Provider callbacks, normalized to the vocabulary the core state machines use.
39
- * `rewarded` and `dismissed` are separate members because they are separate
40
- * facts that can arrive in either order (FR-049).
41
- */
42
- export type EngineAdEvent =
43
- | { readonly type: 'loaded' }
44
- | { readonly type: 'failed'; readonly cause: unknown }
45
- | { readonly type: 'opened' }
46
- | { readonly type: 'clicked' }
47
- | { readonly type: 'dismissed' }
48
- | { readonly type: 'rewarded'; readonly reward: Reward };
49
-
50
- /** An opaque reference to one loaded ad. Never exposed publicly. */
51
- export interface EngineAdHandle {
52
- readonly id: string;
53
- }
54
-
55
- /**
56
- * Consent status as the engine reports it — the provider's own vocabulary,
57
- * lowercased, with nothing provider-shaped attached. `core/consentFlow.ts`
58
- * normalizes this into the public `ConsentState`; keeping the two separate is
59
- * what lets an iOS adapter map a different mechanism onto the same public shape
60
- * without either side changing.
61
- */
62
- export interface EngineConsentInfo {
63
- readonly status: 'unknown' | 'required' | 'not_required' | 'obtained';
64
- readonly canRequestAds: boolean;
65
- readonly isFormAvailable: boolean;
66
- /** Whether the provider requires a "change my choice" entry point (FR-150). */
67
- readonly privacyOptions: 'unknown' | 'required' | 'not_required';
68
- }
69
-
70
- export interface EngineConsentOptions {
71
- /** Already stripped by config validation when the environment is production. */
72
- readonly debugGeography?: 'disabled' | 'eea' | 'not_eea' | 'regulated_us_state' | 'other';
73
- readonly underAgeOfConsent?: boolean;
74
- readonly testDeviceIds?: readonly string[];
75
- }
76
-
77
- /**
78
- * The provider's consent platform, behind the same boundary as everything else.
79
- *
80
- * It is a separate member of `AdsEngine` rather than more methods on it because
81
- * it is a genuinely separate native module - the ads SDK and the consent SDK
82
- * initialize independently and neither requires the other.
83
- *
84
- * The package orchestrates; it never renders. `showFormIfRequired` asks the
85
- * provider to present its own form and reports what came back (FR-103).
86
- */
87
- export interface ConsentPort {
88
- /** Refreshes consent information, applying any testing options. */
89
- requestInfoUpdate(options: EngineConsentOptions): Promise<EngineConsentInfo>;
90
- /** Presents the provider's form if one is required; a no-op otherwise. */
91
- showFormIfRequired(): Promise<EngineConsentInfo>;
92
- /**
93
- * Presents the provider's privacy options form, through which a user changes
94
- * an answer already given (FR-151). Resolves when the form closes, whether or
95
- * not anything changed; rejects if it could not be presented. The caller has
96
- * already checked the requirement, so the adapter does not re-check.
97
- */
98
- showPrivacyOptionsForm(): Promise<EngineConsentInfo>;
99
- /** Last known information without contacting the network. */
100
- getInfo(): Promise<EngineConsentInfo>;
101
- /** Clears the stored decision. Inert in production by configuration. */
102
- reset(): void;
103
- }
104
-
105
- export interface AdsEngine {
106
- /**
107
- * True when this platform has a working adapter. Checked before anything
108
- * else so an unsupported platform never loads the provider at all (FR-025).
109
- */
110
- isAvailable(): boolean;
111
-
112
- /**
113
- * Applies privacy configuration and then initializes the provider. The order
114
- * matters: child-directed and under-age flags only take effect if they reach
115
- * the provider before it initializes (FR-017, R9).
116
- *
117
- * The caller guarantees this is invoked at most once per instance; the
118
- * engine does not have to be idempotent itself.
119
- */
120
- initialize(privacy: EnginePrivacy): Promise<void>;
121
-
122
- /**
123
- * Creates and begins loading one full-screen ad. Resolves on request, not on
124
- * load. `request` is optional so engines written against 0.4.0 still satisfy
125
- * the port; an engine that ignores it requests personalized ads.
126
- */
127
- createFullScreen(
128
- format: FullScreenFormat,
129
- adUnitId: string,
130
- request?: EngineRequestOptions,
131
- ): Promise<EngineAdHandle>;
132
-
133
- /** Presents a loaded ad. Rejects if the provider refuses to present. */
134
- show(handle: EngineAdHandle): Promise<void>;
135
-
136
- /** Provider callbacks for one handle. Returns an unsubscribe (FR-005). */
137
- subscribe(handle: EngineAdHandle, listener: (event: EngineAdEvent) => void): Unsubscribe;
138
-
139
- /** Releases the handle's provider resources. Safe to call more than once. */
140
- dispose(handle: EngineAdHandle): void;
141
-
142
- /** The provider's consent platform. Only touched if the app opts in (FR-102). */
143
- readonly consent: ConsentPort;
144
- }
1
+ /**
2
+ * The internal engine port (NFR-008, FR-006).
3
+ *
4
+ * This interface names no platform and no ad provider. That is what makes an
5
+ * iOS adapter an additive change: it implements this, and nothing in the public
6
+ * contract moves. `core/` talks only to this shape; the AdMob bridge exists
7
+ * behind exactly one implementation of it, in `admobEngine.ts`.
8
+ *
9
+ * Banners are absent on purpose. The provider owns a banner's whole lifecycle
10
+ * through a React component, so `react/AdsBanner` adapts those callbacks
11
+ * directly rather than routing them through a handle model that would have to
12
+ * pretend a banner is a load-then-show resource.
13
+ */
14
+
15
+ import type { AdContentRating, FullScreenFormat, Reward, Unsubscribe } from '../config/types';
16
+
17
+ /** Privacy state as the engine needs it — no consumer-facing shape leaks in. */
18
+ export interface EnginePrivacy {
19
+ readonly childDirected?: boolean;
20
+ readonly underAgeOfConsent?: boolean;
21
+ readonly maxAdContentRating?: AdContentRating;
22
+ readonly personalizedAds?: boolean;
23
+ readonly testDeviceIds?: readonly string[];
24
+ }
25
+
26
+ /**
27
+ * Privacy that applies to each ad request rather than to initialization.
28
+ *
29
+ * `personalizedAds` is here, not only in `EnginePrivacy`, because the provider
30
+ * reads it per request and `updatePrivacy` may change it without
31
+ * reinitializing: a request must carry the value current at request time.
32
+ */
33
+ export interface EngineRequestOptions {
34
+ readonly personalizedAds: boolean;
35
+ }
36
+
37
+ /**
38
+ * Provider callbacks, normalized to the vocabulary the core state machines use.
39
+ * `rewarded` and `dismissed` are separate members because they are separate
40
+ * facts that can arrive in either order (FR-049).
41
+ */
42
+ export type EngineAdEvent =
43
+ | { readonly type: 'loaded' }
44
+ | { readonly type: 'failed'; readonly cause: unknown }
45
+ | { readonly type: 'opened' }
46
+ | { readonly type: 'clicked' }
47
+ | { readonly type: 'dismissed' }
48
+ | { readonly type: 'rewarded'; readonly reward: Reward };
49
+
50
+ /** An opaque reference to one loaded ad. Never exposed publicly. */
51
+ export interface EngineAdHandle {
52
+ readonly id: string;
53
+ }
54
+
55
+ /**
56
+ * Consent status as the engine reports it — the provider's own vocabulary,
57
+ * lowercased, with nothing provider-shaped attached. `core/consentFlow.ts`
58
+ * normalizes this into the public `ConsentState`; keeping the two separate is
59
+ * what lets an iOS adapter map a different mechanism onto the same public shape
60
+ * without either side changing.
61
+ */
62
+ export interface EngineConsentInfo {
63
+ readonly status: 'unknown' | 'required' | 'not_required' | 'obtained';
64
+ readonly canRequestAds: boolean;
65
+ readonly isFormAvailable: boolean;
66
+ /** Whether the provider requires a "change my choice" entry point (FR-150). */
67
+ readonly privacyOptions: 'unknown' | 'required' | 'not_required';
68
+ }
69
+
70
+ export interface EngineConsentOptions {
71
+ /** Already stripped by config validation when the environment is production. */
72
+ readonly debugGeography?: 'disabled' | 'eea' | 'not_eea' | 'regulated_us_state' | 'other';
73
+ readonly underAgeOfConsent?: boolean;
74
+ readonly testDeviceIds?: readonly string[];
75
+ }
76
+
77
+ /**
78
+ * The provider's consent platform, behind the same boundary as everything else.
79
+ *
80
+ * It is a separate member of `AdsEngine` rather than more methods on it because
81
+ * it is a genuinely separate native module - the ads SDK and the consent SDK
82
+ * initialize independently and neither requires the other.
83
+ *
84
+ * The package orchestrates; it never renders. `showFormIfRequired` asks the
85
+ * provider to present its own form and reports what came back (FR-103).
86
+ */
87
+ export interface ConsentPort {
88
+ /** Refreshes consent information, applying any testing options. */
89
+ requestInfoUpdate(options: EngineConsentOptions): Promise<EngineConsentInfo>;
90
+ /** Presents the provider's form if one is required; a no-op otherwise. */
91
+ showFormIfRequired(): Promise<EngineConsentInfo>;
92
+ /**
93
+ * Presents the provider's privacy options form, through which a user changes
94
+ * an answer already given (FR-151). Resolves when the form closes, whether or
95
+ * not anything changed; rejects if it could not be presented. The caller has
96
+ * already checked the requirement, so the adapter does not re-check.
97
+ */
98
+ showPrivacyOptionsForm(): Promise<EngineConsentInfo>;
99
+ /** Last known information without contacting the network. */
100
+ getInfo(): Promise<EngineConsentInfo>;
101
+ /** Clears the stored decision. Inert in production by configuration. */
102
+ reset(): void;
103
+ }
104
+
105
+ export interface AdsEngine {
106
+ /**
107
+ * True when this platform has a working adapter. Checked before anything
108
+ * else so an unsupported platform never loads the provider at all (FR-025).
109
+ */
110
+ isAvailable(): boolean;
111
+
112
+ /**
113
+ * Applies privacy configuration and then initializes the provider. The order
114
+ * matters: child-directed and under-age flags only take effect if they reach
115
+ * the provider before it initializes (FR-017, R9).
116
+ *
117
+ * The caller guarantees this is invoked at most once per instance; the
118
+ * engine does not have to be idempotent itself.
119
+ */
120
+ initialize(privacy: EnginePrivacy): Promise<void>;
121
+
122
+ /**
123
+ * Creates and begins loading one full-screen ad. Resolves on request, not on
124
+ * load. `request` is optional so engines written against 0.4.0 still satisfy
125
+ * the port; an engine that ignores it requests personalized ads.
126
+ */
127
+ createFullScreen(
128
+ format: FullScreenFormat,
129
+ adUnitId: string,
130
+ request?: EngineRequestOptions,
131
+ ): Promise<EngineAdHandle>;
132
+
133
+ /** Presents a loaded ad. Rejects if the provider refuses to present. */
134
+ show(handle: EngineAdHandle): Promise<void>;
135
+
136
+ /** Provider callbacks for one handle. Returns an unsubscribe (FR-005). */
137
+ subscribe(handle: EngineAdHandle, listener: (event: EngineAdEvent) => void): Unsubscribe;
138
+
139
+ /** Releases the handle's provider resources. Safe to call more than once. */
140
+ dispose(handle: EngineAdHandle): void;
141
+
142
+ /** The provider's consent platform. Only touched if the app opts in (FR-102). */
143
+ readonly consent: ConsentPort;
144
+ }
@@ -1,61 +1,61 @@
1
- /**
2
- * Google's official test ad units (FR-013).
3
- *
4
- * PRIVATE. Never exported from `src/index.ts`.
5
- *
6
- * FR-013 forbids the package from carrying production identifiers, credentials,
7
- * or host-app rules, and explicitly permits official test identifiers provided
8
- * they are documented as such — this file is that documentation.
9
- *
10
- * They are hard-coded rather than read from configuration on purpose: FR-011's
11
- * substitution guarantee must hold even when the app's configuration is the
12
- * thing that is wrong, so it cannot depend on the app supplying anything. They
13
- * are also not re-exported from the provider bridge's own `TestIds`, because
14
- * that would put a provider symbol on this package's dependency path for a
15
- * value that is really just four documented constants (FR-002).
16
- *
17
- * Source: Google Mobile Ads "Test ads" documentation, cross-checked against the
18
- * bridge's `src/TestIds.ts` at version 16.0.3. These identifiers are public,
19
- * are the same for every publisher, and always fill.
20
- */
21
-
22
- import type { AdFormat } from '../config/types';
23
-
24
- /** Android. iOS units are added alongside these when the iOS adapter lands (FR-006). */
25
- const ANDROID_TEST_AD_UNITS: Readonly<Record<AdFormat, string>> = {
26
- // The adaptive-banner unit, not the fixed-size one, because adaptive is the
27
- // format this package actually requests (FR-038).
28
- banner: 'ca-app-pub-3940256099942544/9214589741',
29
- interstitial: 'ca-app-pub-3940256099942544/1033173712',
30
- rewarded: 'ca-app-pub-3940256099942544/5224354917',
31
- // Added in 0.2.0 alongside the app-open format. Cross-checked against the
32
- // bridge's own `TestIds.APP_OPEN` at 16.0.3.
33
- app_open: 'ca-app-pub-3940256099942544/9257395921',
34
- };
35
-
36
- export function testAdUnitFor(format: AdFormat): string {
37
- return ANDROID_TEST_AD_UNITS[format];
38
- }
39
-
40
- /**
41
- * Whether an identifier is one of ours.
42
- *
43
- * This is the structural test that drives substitution: anything that is not a
44
- * known official test unit is treated as production-owned. Deliberately not a
45
- * parse of AdMob's publisher-id format — a rule like that becomes wrong the day
46
- * Google changes the format, and being wrong here means either sending a
47
- * production request or blocking a legitimate one.
48
- */
49
- export function isOfficialTestAdUnit(adUnitId: string): boolean {
50
- for (const value of Object.values(ANDROID_TEST_AD_UNITS)) {
51
- if (value === adUnitId) {
52
- return true;
53
- }
54
- }
55
- return false;
56
- }
57
-
58
- /** Exposed for the package's own tests and for documentation generation. */
59
- export function allTestAdUnits(): Readonly<Record<AdFormat, string>> {
60
- return ANDROID_TEST_AD_UNITS;
61
- }
1
+ /**
2
+ * Google's official test ad units (FR-013).
3
+ *
4
+ * PRIVATE. Never exported from `src/index.ts`.
5
+ *
6
+ * FR-013 forbids the package from carrying production identifiers, credentials,
7
+ * or host-app rules, and explicitly permits official test identifiers provided
8
+ * they are documented as such — this file is that documentation.
9
+ *
10
+ * They are hard-coded rather than read from configuration on purpose: FR-011's
11
+ * substitution guarantee must hold even when the app's configuration is the
12
+ * thing that is wrong, so it cannot depend on the app supplying anything. They
13
+ * are also not re-exported from the provider bridge's own `TestIds`, because
14
+ * that would put a provider symbol on this package's dependency path for a
15
+ * value that is really just four documented constants (FR-002).
16
+ *
17
+ * Source: Google Mobile Ads "Test ads" documentation, cross-checked against the
18
+ * bridge's `src/TestIds.ts` at version 16.0.3. These identifiers are public,
19
+ * are the same for every publisher, and always fill.
20
+ */
21
+
22
+ import type { AdFormat } from '../config/types';
23
+
24
+ /** Android. iOS units are added alongside these when the iOS adapter lands (FR-006). */
25
+ const ANDROID_TEST_AD_UNITS: Readonly<Record<AdFormat, string>> = {
26
+ // The adaptive-banner unit, not the fixed-size one, because adaptive is the
27
+ // format this package actually requests (FR-038).
28
+ banner: 'ca-app-pub-3940256099942544/9214589741',
29
+ interstitial: 'ca-app-pub-3940256099942544/1033173712',
30
+ rewarded: 'ca-app-pub-3940256099942544/5224354917',
31
+ // Added in 0.2.0 alongside the app-open format. Cross-checked against the
32
+ // bridge's own `TestIds.APP_OPEN` at 16.0.3.
33
+ app_open: 'ca-app-pub-3940256099942544/9257395921',
34
+ };
35
+
36
+ export function testAdUnitFor(format: AdFormat): string {
37
+ return ANDROID_TEST_AD_UNITS[format];
38
+ }
39
+
40
+ /**
41
+ * Whether an identifier is one of ours.
42
+ *
43
+ * This is the structural test that drives substitution: anything that is not a
44
+ * known official test unit is treated as production-owned. Deliberately not a
45
+ * parse of AdMob's publisher-id format — a rule like that becomes wrong the day
46
+ * Google changes the format, and being wrong here means either sending a
47
+ * production request or blocking a legitimate one.
48
+ */
49
+ export function isOfficialTestAdUnit(adUnitId: string): boolean {
50
+ for (const value of Object.values(ANDROID_TEST_AD_UNITS)) {
51
+ if (value === adUnitId) {
52
+ return true;
53
+ }
54
+ }
55
+ return false;
56
+ }
57
+
58
+ /** Exposed for the package's own tests and for documentation generation. */
59
+ export function allTestAdUnits(): Readonly<Record<AdFormat, string>> {
60
+ return ANDROID_TEST_AD_UNITS;
61
+ }