@chipmobilesdk/rn-ads 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chipmobilesdk/rn-ads",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "main": "src/index.ts",
@@ -1,98 +1,191 @@
1
- ---
2
- name: sdk-rn-ads
3
- description: "Advertising via @chipmobilesdk/rn-ads: AdMob banner/interstitial/rewarded behind a provider-neutral contract, placement keys, consent gating, per-environment test-ad substitution. Use when adding ads to a screen, debugging an ad that never loads, mapping ad units per environment, or answering a Data safety question about advertising."
4
- metadata:
5
- package: "@chipmobilesdk/rn-ads"
6
- packageVersion: "0.1.0"
7
- user-invocable: true
8
- disable-model-invocation: false
9
- ---
10
-
11
- # @chipmobilesdk/rn-ads
12
-
13
- Adaptive banner, interstitial and rewarded ads on Android via Google AdMob, behind a
14
- contract that names no provider. The app names a placement key; the package owns the
15
- request. Consuming code never imports the Google Mobile Ads SDK.
16
-
17
- ## Use this when
18
-
19
- - Adding a banner, interstitial or rewarded ad to a screen
20
- - Mapping AdMob ad units to placement keys per environment
21
- - Debugging an ad that never loads, or a `blocked` / `unavailable` / `provider_error`
22
- - Wiring consent so requests cannot precede it
23
- - Answering a Data safety / App Privacy question about advertising
24
-
25
- ## Do not use this for
26
-
27
- - **Consent UI or copy** — the package consumes a gate outcome and enforces ordering; it
28
- renders no dialog and ships no legal text.
29
- - **Where or how often ads appear** — no frequency cap, no placement rule, no auto-insert.
30
- - **Granting the reward** — it reports `reward_earned`; entitlement is the app's.
31
- - iOS — not implemented; every call returns `unavailable` / `unsupported_platform`.
32
-
33
- ## Nothing loads until consent and initialization complete
34
-
35
- This is enforced, and it is the answer to almost every "the ad never appears". With the
36
- gate withheld, **zero** provider calls occur — not even configuration. `childDirected`,
37
- `underAgeOfConsent` and `maxAdContentRating` take effect only if they reach
38
- `initialize()` *before* the provider starts; changing one later returns
39
- `requires_reinitialization` rather than silently not applying.
40
- See `## Initialize — consent comes first`.
41
-
42
- ## Never gate `<AdsBanner>` on its own placement state
43
-
44
- `AdsBanner` is what measures the container and starts the load, so it must be mounted
45
- unconditionally. Rendering it only when the state is `loading`/`ready` deadlocks at
46
- `idle` and no ad ever appears. Use the state for the *surrounding* placeholder and to
47
- decide whether to hold or reclaim space — never to decide whether to mount the host. It
48
- renders `null` on its own when there is nothing to show.
49
-
50
- ## Two things not to "fix"
51
-
52
- - **The bridge pin.** `react-native-google-mobile-ads` is peered at `>=16.0.3 <16.1.0`;
53
- 16.1.0+ carries Kotlin 2.3 metadata that React Native 0.85's pinned compiler cannot
54
- read, and the Android build fails outright. Read `## Compatibility` before widening it.
55
- - **Test ads in dev.** Outside `production`, non-official ad units are replaced with
56
- Google's test units and reported via `config_warning` / `getSubstitutedPlacements()`.
57
- That is the safety guarantee working, not a misconfiguration. Never click a production
58
- ad to verify anything.
59
-
60
- ## Public API (v0.1.0)
61
-
62
- Runtime: `createAds` (throws `AdsConfigError` on a bad catalog; everything else returns a
63
- typed result rather than throwing) · `AdsProvider`, `useAds` · `AdsBanner` ·
64
- `usePlacementState`, `useAdsStatus`, `useAdsEvents` · `AdsError`, `AdsConfigError`,
65
- `isAdsError`, `isFullScreenFormat` · `DEFAULT_LOAD_TIMEOUT_MS`, `DEFAULT_SHOW_TIMEOUT_MS`,
66
- `DEFAULT_AD_VALIDITY_MS`, `LOAD_TIMEOUT_BOUNDS`, `SHOW_TIMEOUT_BOUNDS`,
67
- `AD_VALIDITY_BOUNDS` · `createFakeAdsEngine` from `@chipmobilesdk/rn-ads/testing`
68
- (test doubles only).
69
-
70
- Instance: `initialize`, `updatePrivacy`, `setEnabled`, `setAppActive`, `getStatus`,
71
- `subscribeStatus`, `load`, `show`, `getPlacementState`, `subscribePlacement`,
72
- `subscribeEvents`, `getSubstitutedPlacements`, `dispose`.
73
-
74
- Types: `AdsInstance`, `AdsConfig`, `PlacementDefinition`, `PrivacyOptions`, `AdFormat`,
75
- `FullScreenFormat`, `Environment`, `InitStatus`, `PlacementState`, `InitResult`,
76
- `LoadResult`, `ShowResult`, `PrivacyUpdateResult`, `Reward`, `AdEvent`, `AdEventType`,
77
- `AdEventListener`, `ImpressionSource`, `AdsErrorCode`, `AdsOperation`, `TimeoutConfig`,
78
- `AdContentRating`, `PrivacyBlockReason`, `UnavailableReason`, `Unsubscribe`,
79
- `AdsBannerProps`, `AdsProviderProps`.
80
-
81
- Call `dispose()` when the owning screen unmounts; it is idempotent, and so is every
82
- `Unsubscribe`.
83
-
84
- ## Where the detail lives
85
-
86
- Read one section of `node_modules/@chipmobilesdk/rn-ads/README.md`:
87
-
88
- | Need | Section |
89
- |---|---|
90
- | First integration | `## Install`, `## Configure` |
91
- | Consent ordering | `## Initialize — consent comes first` |
92
- | Rendering a banner | `## Banner` |
93
- | Full-screen formats | `## Interstitial and rewarded` |
94
- | Results, events, errors, timeouts, cleanup | `## State, events, and errors` |
95
- | Tests without a device | `## Testing` |
96
- | Permissions, Data safety, families | `## Install`, `## Release checklists` |
97
- | Build failure after a bridge upgrade | `## Compatibility` |
98
- | What is deferred | `## Known limitations`, `## Extending to iOS` |
1
+ ---
2
+ name: sdk-rn-ads
3
+ description: "Advertising via @chipmobilesdk/rn-ads: AdMob banner/interstitial/rewarded/app-open behind a provider-neutral contract, placement keys, optional consent orchestration, three-state enablement, bounded retry, per-environment test-ad substitution. Use when adding ads to a screen, running a consent flow without importing AdMob, deferring ads until a purchase check resolves, debugging an ad that never loads, mapping ad units per environment, or answering a Data safety question about advertising."
4
+ metadata:
5
+ package: "@chipmobilesdk/rn-ads"
6
+ packageVersion: "0.2.0"
7
+ user-invocable: true
8
+ disable-model-invocation: false
9
+ ---
10
+
11
+ # @chipmobilesdk/rn-ads
12
+
13
+ Adaptive banner, interstitial, rewarded and app-open ads on Android via Google AdMob,
14
+ behind a contract that names no provider. The app names a placement key; the package owns
15
+ the request. Consuming code never imports the Google Mobile Ads SDK — including for
16
+ consent.
17
+
18
+ ## Use this when
19
+
20
+ - Adding a banner, interstitial, rewarded or app-open ad to a screen
21
+ - Running a consent flow in a region that requires one, without importing AdMob
22
+ - Deferring ads at startup until a purchase or entitlement check answers
23
+ - Turning ads back on after a subscription lapses or a refund lands
24
+ - Mapping AdMob ad units to placement keys per environment
25
+ - Debugging an ad that never loads, or a `blocked` / `undecided` / `unavailable` /
26
+ `ad_originated_return` / `provider_error`
27
+ - Answering a Data safety / App Privacy question about advertising
28
+
29
+ ## Do not use this for
30
+
31
+ - **Consent UI or copy** — the package *orchestrates* the provider's flow and normalizes
32
+ the outcome, but the form is drawn by the provider and the legal text is theirs. Timing,
33
+ surrounding copy and the "change your mind" path stay yours.
34
+ - **Where or how often ads appear** — no frequency cap, no placement rule, no auto-insert.
35
+ The package never shows an ad on its own, app-open included.
36
+ - **Granting the reward** — it reports `reward_earned`; entitlement is the app's.
37
+ - **Anything about payments** — the undecided state exists *because* apps sell ad removal,
38
+ but the package receives a decision and never a reason, receipt or entitlement.
39
+ - iOS — not implemented; every call returns `unavailable` / `unsupported_platform`.
40
+
41
+ ## Nothing loads until consent and initialization complete
42
+
43
+ This is enforced, and it is the answer to almost every "the ad never appears". With the
44
+ gate withheld, **zero** provider calls occur — not even configuration. `childDirected`,
45
+ `underAgeOfConsent` and `maxAdContentRating` take effect only if they reach
46
+ `initialize()` *before* the provider starts; changing one later returns
47
+ `requires_reinitialization` rather than silently not applying.
48
+ See `## Initialize — consent comes first` and `## Consent`.
49
+
50
+ ## Three gates, one reason — check this order first
51
+
52
+ A refused request reports exactly one reason, and the precedence is fixed:
53
+ **platform availability → enablement → consent**, most immovable first. So
54
+ `unsupported_platform` on a disabled instance is reported as `unsupported_platform` —
55
+ turning ads on would not have helped. Full table in `## Why an ad did not show`.
56
+
57
+ ## Never gate `<AdsBanner>` on its own placement state
58
+
59
+ `AdsBanner` is what measures the container and starts the load, so it must be mounted
60
+ unconditionally. Rendering it only when the state is `loading`/`ready` deadlocks at
61
+ `idle` and no ad ever appears. Use the state for the *surrounding* placeholder and to
62
+ decide whether to hold or reclaim space — never to decide whether to mount the host. It
63
+ renders `null` on its own when there is nothing to show.
64
+
65
+ ## `undecided` is not `disabled`, and that is the whole point
66
+
67
+ `enabled` takes `true`, `false` or `'undecided'`. All three send zero requests except
68
+ `true`. `'undecided'` reports itself distinctly — in `getStatus()` and in the banner's
69
+ placement state — so an app that has not yet heard back from the store can **hold** the
70
+ banner's space instead of reclaiming it, and a paying user never sees an ad flash in and
71
+ out. `disabled` means a settled no; reclaim the space.
72
+
73
+ The package never resolves it for you. Leave it unresolved past `undecidedWarningMs`
74
+ (default 10s) and you get exactly one `config_warning`, never a stream.
75
+
76
+ ## `setEnabled` returns a result — read it
77
+
78
+ In 0.1.0 it returned `void` and re-enabling after a disable silently dropped the init
79
+ status, so every later `load` and `show` was refused with nothing to say so. That was a
80
+ defect and it is fixed. The result now says which happened:
81
+
82
+ - `resumed` — the provider was never un-initialized; load away, zero provider calls made.
83
+ - `requires_initialization` — call `initialize()` yourself. The package will **not** do it
84
+ implicitly, because that would hide an async, billable operation behind a setter.
85
+
86
+ Existing `ads.setEnabled(false);` call sites still compile untouched.
87
+
88
+ ## Four things not to "fix"
89
+
90
+ - **The bridge pin.** `react-native-google-mobile-ads` is peered at `>=16.0.3 <16.1.0`;
91
+ 16.1.0+ carries Kotlin 2.3 metadata that React Native 0.85's pinned compiler cannot
92
+ read, and the Android build fails outright. `0.2.0` needed no bump — consent and
93
+ app-open are both already in `16.0.3`. Read `## Compatibility` before widening it.
94
+ - **Test ads in dev.** Outside `production`, non-official ad units are replaced with
95
+ Google's test units and reported via `config_warning` / `getSubstitutedPlacements()`.
96
+ That is the safety guarantee working, not a misconfiguration. Never click a production
97
+ ad to verify anything.
98
+ - **Consent tools going quiet in production.** `consentTesting.debugGeography` and
99
+ `resetOnStart` are stripped at config validation in `production` and listed by
100
+ `getIgnoredConsentTools()`. Working as designed.
101
+ - **A declined consent still reporting `obtained` / `canRequestAds: true`.** That is UMP's
102
+ meaning, not a bug: `OBTAINED` means *an answer was recorded*, and non-personalized ads
103
+ stay servable. The package forwards the provider's verdict rather than inventing one.
104
+
105
+ ## Retry is off unless you ask
106
+
107
+ No `retry` in config means the request count is byte-identical to 0.1.0 — a test in the
108
+ package proves it. When enabled it is bounded (default 2 retries, 2s then 4s, no jitter),
109
+ retries only errors already flagged `retryable`, and cancels everything on disable,
110
+ undecided, consent loss or `dispose()`. Every attempt emits `retry_scheduled` carrying the
111
+ load cycle's `correlationId`, because every attempt is a billable request.
112
+ Per-placement `retry: false` turns it off for one placement.
113
+
114
+ ## App-open: the one rule you cannot implement yourself
115
+
116
+ App-open shares the single full-screen lock with interstitial and rewarded, and expires on
117
+ its own shorter window (30 min default, vs 60). The part worth knowing: a show requested
118
+ on a return **from an ad** — or from a browser an ad opened — is refused with
119
+ `ad_originated_return`, distinct from `busy` and `not_ready`. Your app cannot tell that
120
+ return apart from any other; the package can. It cannot survive process death, and a
121
+ browser opened without a preceding click is undetectable. See `## App open`.
122
+
123
+ ## Public API (v0.2.0)
124
+
125
+ Runtime: `createAds` (throws `AdsConfigError` on a bad catalog or an out-of-range retry
126
+ policy; everything else returns a typed result rather than throwing) · `AdsProvider`,
127
+ `useAds` · `AdsBanner` · `usePlacementState`, `useAdsStatus`, `useAdsEvents` · `AdsError`,
128
+ `AdsConfigError`, `isAdsError`, `isFullScreenFormat` · `DEFAULT_LOAD_TIMEOUT_MS`,
129
+ `DEFAULT_SHOW_TIMEOUT_MS`, `DEFAULT_AD_VALIDITY_MS`, `DEFAULT_APP_OPEN_VALIDITY_MS`,
130
+ `DEFAULT_RETRY_POLICY`, `DEFAULT_UNDECIDED_WARNING_MS`, `LOAD_TIMEOUT_BOUNDS`,
131
+ `SHOW_TIMEOUT_BOUNDS`, `AD_VALIDITY_BOUNDS`, `APP_OPEN_VALIDITY_BOUNDS`, `RETRY_BOUNDS`,
132
+ `UNDECIDED_WARNING_BOUNDS` · `createFakeAdsEngine`, `createTestClock` from
133
+ `@chipmobilesdk/rn-ads/testing` (test doubles only).
134
+
135
+ Instance: `initialize`, `updatePrivacy`, `setEnabled`, `setEnablement`, `setAppActive`,
136
+ `consent`, `getStatus`, `subscribeStatus`, `load`, `show`, `getPlacementState`,
137
+ `subscribePlacement`, `subscribeEvents`, `getSubstitutedPlacements`,
138
+ `getIgnoredConsentTools`, `dispose`.
139
+
140
+ Consent namespace (`ads.consent.*`, opt-in — untouched, nothing starts):
141
+ `requestInfoUpdate`, `showFormIfRequired`, `getState`, `subscribe`, `reset`.
142
+
143
+ Types: `AdsInstance`, `AdsConfig`, `PlacementDefinition`, `PrivacyOptions`, `AdFormat`,
144
+ `FullScreenFormat`, `Environment`, `Enablement`, `EnablementResult`, `ConsentApi`,
145
+ `ConsentState`, `ConsentSnapshot`, `ConsentTestingOptions`, `RetryPolicy`, `InitStatus`,
146
+ `PlacementState`, `InitResult`, `LoadResult`, `ShowResult`, `PrivacyUpdateResult`,
147
+ `Reward`, `AdEvent`, `AdEventType`, `AdEventListener`, `ConfigWarning`, `ImpressionSource`,
148
+ `AdsErrorCode`, `AdsOperation`, `TimeoutConfig`, `AdContentRating`, `PrivacyBlockReason`,
149
+ `UnavailableReason`, `Unsubscribe`, `AdsBannerProps`, `AdsProviderProps`.
150
+
151
+ Call `dispose()` when the owning screen unmounts; it is idempotent, and so is every
152
+ `Unsubscribe`.
153
+
154
+ ## Upgrading 0.1.0 → 0.2.0
155
+
156
+ One required change, compile-time only: five unions gained members, so an exhaustive
157
+ `switch` over any of them needs the new cases or a `default`.
158
+
159
+ | Union | New members |
160
+ |---|---|
161
+ | `AdFormat` | `'app_open'` |
162
+ | `InitStatus` | `'undecided'` |
163
+ | `PlacementState` | `'undecided'` |
164
+ | `PrivacyBlockReason` | `'consent_flow_required'`, `'consent_flow_error'` |
165
+ | `AdsErrorCode` | `'undecided'`, `'ad_originated_return'`, `'consent_flow_required'`, `'consent_flow_failed'`, `'retry_out_of_bounds'` |
166
+
167
+ Everything else is opt-in: consent, `'undecided'`, app-open and retry are all off or absent
168
+ until configured. One behavior change needs no action — on an unavailable platform with
169
+ ads also disabled, the reason is now `unsupported_platform` where 0.1.0 said `disabled`.
170
+
171
+ ## Where the detail lives
172
+
173
+ Read one section of `node_modules/@chipmobilesdk/rn-ads/README.md`:
174
+
175
+ | Need | Section |
176
+ |---|---|
177
+ | First integration | `## Install`, `## Configure` |
178
+ | Consent ordering | `## Initialize — consent comes first` |
179
+ | Running the consent flow | `## Consent` |
180
+ | Rendering a banner | `## Banner` |
181
+ | Full-screen formats | `## Interstitial and rewarded` |
182
+ | App-open and ad-originated return | `## App open` |
183
+ | Turning ads off, undecided, re-enabling | `## Turning ads off — three states` |
184
+ | Retry and reconciling it against a bill | `## Retry` |
185
+ | Why a request was refused | `## Why an ad did not show` |
186
+ | Results, events, errors, timeouts, cleanup | `## State, events, and errors` |
187
+ | Tests without a device | `## Testing` |
188
+ | Permissions, Data safety, families | `## Install`, `## Release checklists` |
189
+ | Build failure after a bridge upgrade | `## Compatibility` |
190
+ | Migration detail | `## Versioning` |
191
+ | What is deferred | `## Known limitations`, `## Extending to iOS` |