@chipmobilesdk/rn-ads 0.2.1 → 0.4.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/CHANGELOG.md +94 -1
- package/README.md +879 -671
- package/package.json +49 -49
- package/skills/sdk-rn-ads/SKILL.md +93 -10
- package/src/config/types.ts +40 -0
- package/src/config/validate.ts +211 -188
- package/src/core/adsInstance.ts +1890 -1554
- package/src/core/consentFlow.ts +14 -1
- package/src/core/initMachine.ts +13 -0
- package/src/core/requestThrottle.ts +176 -0
- package/src/diagnostics/events.ts +13 -0
- package/src/engine/admobEngine.ts +26 -0
- package/src/engine/port.ts +9 -0
- package/src/index.ts +103 -91
- package/src/react/AdsBanner.tsx +337 -292
- package/src/testing/index.ts +101 -2
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chipmobilesdk/rn-ads",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"private": false,
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"main": "src/index.ts",
|
|
7
|
-
"types": "src/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./src/index.ts",
|
|
11
|
-
"react-native": "./src/index.ts",
|
|
12
|
-
"default": "./src/index.ts"
|
|
13
|
-
},
|
|
14
|
-
"./testing": {
|
|
15
|
-
"types": "./src/testing/index.ts",
|
|
16
|
-
"react-native": "./src/testing/index.ts",
|
|
17
|
-
"default": "./src/testing/index.ts"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"src/index.ts",
|
|
22
|
-
"src/config",
|
|
23
|
-
"src/core",
|
|
24
|
-
"src/diagnostics",
|
|
25
|
-
"src/engine",
|
|
26
|
-
"src/react",
|
|
27
|
-
"src/testing",
|
|
28
|
-
"skills",
|
|
29
|
-
"README.md",
|
|
30
|
-
"CHANGELOG.md"
|
|
31
|
-
],
|
|
32
|
-
"sideEffects": false,
|
|
33
|
-
"scripts": {
|
|
34
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
-
"pack:dry-run": "npm pack --dry-run"
|
|
36
|
-
},
|
|
37
|
-
"peerDependencies": {
|
|
38
|
-
"react": ">=19",
|
|
39
|
-
"react-native": ">=0.85",
|
|
40
|
-
"react-native-google-mobile-ads": ">=16.0.3 <16.1.0"
|
|
41
|
-
},
|
|
42
|
-
"engines": {
|
|
43
|
-
"node": ">=22.11.0"
|
|
44
|
-
},
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public",
|
|
47
|
-
"registry": "https://registry.npmjs.org/"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@chipmobilesdk/rn-ads",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"react-native": "./src/index.ts",
|
|
12
|
+
"default": "./src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"./testing": {
|
|
15
|
+
"types": "./src/testing/index.ts",
|
|
16
|
+
"react-native": "./src/testing/index.ts",
|
|
17
|
+
"default": "./src/testing/index.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src/index.ts",
|
|
22
|
+
"src/config",
|
|
23
|
+
"src/core",
|
|
24
|
+
"src/diagnostics",
|
|
25
|
+
"src/engine",
|
|
26
|
+
"src/react",
|
|
27
|
+
"src/testing",
|
|
28
|
+
"skills",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md"
|
|
31
|
+
],
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"scripts": {
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"pack:dry-run": "npm pack --dry-run"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=19",
|
|
39
|
+
"react-native": ">=0.85",
|
|
40
|
+
"react-native-google-mobile-ads": ">=16.0.3 <16.1.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22.11.0"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"registry": "https://registry.npmjs.org/"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
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."
|
|
3
|
+
description: "Advertising via @chipmobilesdk/rn-ads: AdMob banner/interstitial/rewarded/app-open behind a provider-neutral contract, placement keys, optional consent orchestration incl. the privacy options (change my consent) form, three-state enablement, bounded retry, per-environment test-ad substitution, per-placement request throttling. Use when adding ads to a screen, running a consent flow without importing AdMob, adding a Privacy options entry to Settings, deferring ads until a purchase check resolves, debugging an ad that never loads or reports throttled, placing a banner inside a list or a view the user swipes between, mapping ad units per environment, or answering a Data safety question about advertising."
|
|
4
4
|
metadata:
|
|
5
5
|
package: "@chipmobilesdk/rn-ads"
|
|
6
|
-
packageVersion: "0.
|
|
6
|
+
packageVersion: "0.4.0"
|
|
7
7
|
user-invocable: true
|
|
8
8
|
disable-model-invocation: false
|
|
9
9
|
---
|
|
@@ -19,6 +19,7 @@ consent.
|
|
|
19
19
|
|
|
20
20
|
- Adding a banner, interstitial, rewarded or app-open ad to a screen
|
|
21
21
|
- Running a consent flow in a region that requires one, without importing AdMob
|
|
22
|
+
- Letting a user change their consent later (a "Privacy options" entry in Settings)
|
|
22
23
|
- Deferring ads at startup until a purchase or entitlement check answers
|
|
23
24
|
- Turning ads back on after a subscription lapses or a refund lands
|
|
24
25
|
- Mapping AdMob ad units to placement keys per environment
|
|
@@ -30,7 +31,7 @@ consent.
|
|
|
30
31
|
|
|
31
32
|
- **Consent UI or copy** — the package *orchestrates* the provider's flow and normalizes
|
|
32
33
|
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"
|
|
34
|
+
surrounding copy, and where the "change your mind" entry lives stay yours.
|
|
34
35
|
- **Where or how often ads appear** — no frequency cap, no placement rule, no auto-insert.
|
|
35
36
|
The package never shows an ad on its own, app-open included.
|
|
36
37
|
- **Granting the reward** — it reports `reward_earned`; entitlement is the app's.
|
|
@@ -45,8 +46,21 @@ gate withheld, **zero** provider calls occur — not even configuration. `childD
|
|
|
45
46
|
`underAgeOfConsent` and `maxAdContentRating` take effect only if they reach
|
|
46
47
|
`initialize()` *before* the provider starts; changing one later returns
|
|
47
48
|
`requires_reinitialization` rather than silently not applying.
|
|
49
|
+
Once you call `ads.consent.*`, the flow is part of that gate: `initialize()` returns
|
|
50
|
+
`blocked` · `consent_flow_required` until it permits, and the package initializes by
|
|
51
|
+
itself when it does — do not retry `initialize()` in a loop.
|
|
48
52
|
See `## Initialize — consent comes first` and `## Consent`.
|
|
49
53
|
|
|
54
|
+
## "Change my consent" is covered — never `reset()` + re-show
|
|
55
|
+
|
|
56
|
+
New in 0.4.0. Show your Settings entry when `consent.getState().privacyOptionsRequirement`
|
|
57
|
+
is `'required'` (hide it on `'unknown'` and `'not_required'`), and call
|
|
58
|
+
`ads.consent.showPrivacyOptionsForm()` from it. Where the entry lives, and its copy, stay
|
|
59
|
+
yours. The result feeds the gate by itself. A withdrawal moves `ready` → `blocked` and
|
|
60
|
+
cancels in-flight loads; a re-grant resumes with **no second native initialization**.
|
|
61
|
+
`consent.reset()` is testing-only — it wipes the stored decision while the form is open.
|
|
62
|
+
See `### Letting users change their choice` under `## Consent`.
|
|
63
|
+
|
|
50
64
|
## Three gates, one reason — check this order first
|
|
51
65
|
|
|
52
66
|
A refused request reports exactly one reason, and the precedence is fixed:
|
|
@@ -85,12 +99,38 @@ defect and it is fixed. The result now says which happened:
|
|
|
85
99
|
|
|
86
100
|
Existing `ads.setEnabled(false);` call sites still compile untouched.
|
|
87
101
|
|
|
88
|
-
##
|
|
102
|
+
## A banner in a list or a swipeable view gets throttled — by design
|
|
103
|
+
|
|
104
|
+
New in 0.3.0. A banner mounted inside a view the user navigates between — a day in a
|
|
105
|
+
calendar, a tab, a row that scrolls in and out — is unmounted and remounted by React on
|
|
106
|
+
every navigation, and **each remount is a billable request**. Ten quick swipes used to be
|
|
107
|
+
ten requests in a few seconds, which ad networks read as invalid traffic. The penalty is
|
|
108
|
+
account-level and no shipped fix recovers it.
|
|
109
|
+
|
|
110
|
+
So each placement now has a minimum interval between *requests*: **60s for a banner**
|
|
111
|
+
(AdMob's own refresh floor), **off for full-screen formats** — those are loaded by an
|
|
112
|
+
explicit call, not mounted by a renderer, so they never had the problem. A refusal reports
|
|
113
|
+
`throttled` on the placement and emits `request_throttled` with `retryAfterMs`.
|
|
114
|
+
|
|
115
|
+
Three things to know before you reach for `minRequestIntervalMs: 0`:
|
|
116
|
+
|
|
117
|
+
- **`throttled` is not `failed` and not a no-fill.** Nothing is wrong and nothing was
|
|
118
|
+
requested. Collapse the space if you like, but do not log it as an error.
|
|
119
|
+
- **Rotation is exempt.** A width re-adaptation inside one mount is not charged, so a
|
|
120
|
+
rotated screen keeps its banner.
|
|
121
|
+
- **The better fix is architectural.** Keep the banner outside the subtree that remounts
|
|
122
|
+
and the throttle never fires. It is a safety net, not a licence to ignore the problem.
|
|
123
|
+
|
|
124
|
+
This is **not** a frequency cap. It limits how often a placement may *ask*; how often you
|
|
125
|
+
*show* is yours, and the package deliberately ships no pacing (see `## Do not use this for`).
|
|
126
|
+
|
|
127
|
+
## Five things not to "fix"
|
|
89
128
|
|
|
90
129
|
- **The bridge pin.** `react-native-google-mobile-ads` is peered at `>=16.0.3 <16.1.0`;
|
|
91
130
|
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 —
|
|
93
|
-
app-open are
|
|
131
|
+
read, and the Android build fails outright. `0.2.0` and `0.4.0` needed no bump —
|
|
132
|
+
consent, app-open and privacy options are all already in `16.0.3`. Read
|
|
133
|
+
`## Compatibility` before widening it.
|
|
94
134
|
- **Test ads in dev.** Outside `production`, non-official ad units are replaced with
|
|
95
135
|
Google's test units and reported via `config_warning` / `getSubstitutedPlacements()`.
|
|
96
136
|
That is the safety guarantee working, not a misconfiguration. Never click a production
|
|
@@ -101,6 +141,9 @@ Existing `ads.setEnabled(false);` call sites still compile untouched.
|
|
|
101
141
|
- **A declined consent still reporting `obtained` / `canRequestAds: true`.** That is UMP's
|
|
102
142
|
meaning, not a bug: `OBTAINED` means *an answer was recorded*, and non-personalized ads
|
|
103
143
|
stay servable. The package forwards the provider's verdict rather than inventing one.
|
|
144
|
+
- **A banner reporting `throttled` right after a navigation.** That is 0.3.0's request
|
|
145
|
+
floor working — see the section above. Lower it knowingly or move the banner out of the
|
|
146
|
+
remounting subtree; do not set `0` to make the symptom go away.
|
|
104
147
|
|
|
105
148
|
## Retry is off unless you ask
|
|
106
149
|
|
|
@@ -120,7 +163,7 @@ on a return **from an ad** — or from a browser an ad opened — is refused wit
|
|
|
120
163
|
return apart from any other; the package can. It cannot survive process death, and a
|
|
121
164
|
browser opened without a preceding click is undetectable. See `## App open`.
|
|
122
165
|
|
|
123
|
-
## Public API (v0.
|
|
166
|
+
## Public API (v0.4.0)
|
|
124
167
|
|
|
125
168
|
Runtime: `createAds` (throws `AdsConfigError` on a bad catalog or an out-of-range retry
|
|
126
169
|
policy; everything else returns a typed result rather than throwing) · `AdsProvider`,
|
|
@@ -129,7 +172,9 @@ policy; everything else returns a typed result rather than throwing) · `AdsProv
|
|
|
129
172
|
`DEFAULT_SHOW_TIMEOUT_MS`, `DEFAULT_AD_VALIDITY_MS`, `DEFAULT_APP_OPEN_VALIDITY_MS`,
|
|
130
173
|
`DEFAULT_RETRY_POLICY`, `DEFAULT_UNDECIDED_WARNING_MS`, `LOAD_TIMEOUT_BOUNDS`,
|
|
131
174
|
`SHOW_TIMEOUT_BOUNDS`, `AD_VALIDITY_BOUNDS`, `APP_OPEN_VALIDITY_BOUNDS`, `RETRY_BOUNDS`,
|
|
132
|
-
`UNDECIDED_WARNING_BOUNDS
|
|
175
|
+
`UNDECIDED_WARNING_BOUNDS`, `DEFAULT_BANNER_MIN_REQUEST_INTERVAL_MS`,
|
|
176
|
+
`DEFAULT_FULL_SCREEN_MIN_REQUEST_INTERVAL_MS`, `MIN_REQUEST_INTERVAL_BOUNDS`,
|
|
177
|
+
`defaultMinRequestIntervalMs` · `createFakeAdsEngine`, `createTestClock` from
|
|
133
178
|
`@chipmobilesdk/rn-ads/testing` (test doubles only).
|
|
134
179
|
|
|
135
180
|
Instance: `initialize`, `updatePrivacy`, `setEnabled`, `setEnablement`, `setAppActive`,
|
|
@@ -138,11 +183,13 @@ Instance: `initialize`, `updatePrivacy`, `setEnabled`, `setEnablement`, `setAppA
|
|
|
138
183
|
`getIgnoredConsentTools`, `dispose`.
|
|
139
184
|
|
|
140
185
|
Consent namespace (`ads.consent.*`, opt-in — untouched, nothing starts):
|
|
141
|
-
`requestInfoUpdate`, `showFormIfRequired`, `
|
|
186
|
+
`requestInfoUpdate`, `showFormIfRequired`, `showPrivacyOptionsForm`, `getState`,
|
|
187
|
+
`subscribe`, `reset`.
|
|
142
188
|
|
|
143
189
|
Types: `AdsInstance`, `AdsConfig`, `PlacementDefinition`, `PrivacyOptions`, `AdFormat`,
|
|
144
190
|
`FullScreenFormat`, `Environment`, `Enablement`, `EnablementResult`, `ConsentApi`,
|
|
145
|
-
`ConsentState`, `ConsentSnapshot`, `ConsentTestingOptions`, `
|
|
191
|
+
`ConsentState`, `ConsentSnapshot`, `ConsentTestingOptions`, `PrivacyOptionsRequirement`,
|
|
192
|
+
`PrivacyOptionsFormResult`, `RetryPolicy`, `InitStatus`,
|
|
146
193
|
`PlacementState`, `InitResult`, `LoadResult`, `ShowResult`, `PrivacyUpdateResult`,
|
|
147
194
|
`Reward`, `AdEvent`, `AdEventType`, `AdEventListener`, `ConfigWarning`, `ImpressionSource`,
|
|
148
195
|
`AdsErrorCode`, `AdsOperation`, `TimeoutConfig`, `AdContentRating`, `PrivacyBlockReason`,
|
|
@@ -151,6 +198,40 @@ Types: `AdsInstance`, `AdsConfig`, `PlacementDefinition`, `PrivacyOptions`, `AdF
|
|
|
151
198
|
Call `dispose()` when the owning screen unmounts; it is idempotent, and so is every
|
|
152
199
|
`Unsubscribe`.
|
|
153
200
|
|
|
201
|
+
## Upgrading 0.3.x → 0.4.0
|
|
202
|
+
|
|
203
|
+
No code change: no existing union gained a member. New: `showPrivacyOptionsForm`,
|
|
204
|
+
`ConsentSnapshot.privacyOptionsRequirement`, and the types above. A hand-written
|
|
205
|
+
`ConsentSnapshot` literal (a test mock) needs the new field.
|
|
206
|
+
|
|
207
|
+
**Three behaviour changes:** `initialize()` after a declined or failed consent flow
|
|
208
|
+
returns `blocked` and initializes nothing (0.3.0 returned `ready` and initialized); a
|
|
209
|
+
consent gate closing while `ready` now reports `blocked` and cancels in-flight loads
|
|
210
|
+
(0.3.0 stayed `ready` and a mounted banner kept requesting); and reopening a gate
|
|
211
|
+
resumes with no second native initialization (0.3.0 initialized again), unless an
|
|
212
|
+
init-time flag changed in between.
|
|
213
|
+
|
|
214
|
+
**Fixed:** a `load()` in flight when ads are disabled, undecided or disposed now resolves
|
|
215
|
+
(`disabled` / `undecided` / `unavailable`) instead of never settling.
|
|
216
|
+
|
|
217
|
+
## Upgrading 0.2.x → 0.3.0
|
|
218
|
+
|
|
219
|
+
One compile-time change and one behaviour change.
|
|
220
|
+
|
|
221
|
+
| Union | New member |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `PlacementState` | `'throttled'` |
|
|
224
|
+
| `LoadResult` | `{ status: 'throttled', retryAfterMs }` |
|
|
225
|
+
| `AdEventType` | `'request_throttled'` |
|
|
226
|
+
|
|
227
|
+
**The behaviour change:** banner placements now have a 60s floor between requests. If your
|
|
228
|
+
banner lives inside a list row, a tab, or a view the user swipes between, it will report
|
|
229
|
+
`throttled` after a quick navigation where 0.2.x silently issued another request. That was
|
|
230
|
+
the bug; this is the fix. Read the throttle section above before setting
|
|
231
|
+
`minRequestIntervalMs: 0`.
|
|
232
|
+
|
|
233
|
+
Full-screen formats are unchanged — their default floor is `0`.
|
|
234
|
+
|
|
154
235
|
## Upgrading 0.1.0 → 0.2.0
|
|
155
236
|
|
|
156
237
|
One required change, compile-time only: five unions gained members, so an exhaustive
|
|
@@ -177,11 +258,13 @@ Read one section of `node_modules/@chipmobilesdk/rn-ads/README.md`:
|
|
|
177
258
|
| First integration | `## Install`, `## Configure` |
|
|
178
259
|
| Consent ordering | `## Initialize — consent comes first` |
|
|
179
260
|
| Running the consent flow | `## Consent` |
|
|
261
|
+
| A "Privacy options" entry, withdrawal and re-grant | `### Letting users change their choice` |
|
|
180
262
|
| Rendering a banner | `## Banner` |
|
|
181
263
|
| Full-screen formats | `## Interstitial and rewarded` |
|
|
182
264
|
| App-open and ad-originated return | `## App open` |
|
|
183
265
|
| Turning ads off, undecided, re-enabling | `## Turning ads off — three states` |
|
|
184
266
|
| Retry and reconciling it against a bill | `## Retry` |
|
|
267
|
+
| Request throttling, banners in lists | `## Request throttling` |
|
|
185
268
|
| Why a request was refused | `## Why an ad did not show` |
|
|
186
269
|
| Results, events, errors, timeouts, cleanup | `## State, events, and errors` |
|
|
187
270
|
| Tests without a device | `## Testing` |
|
package/src/config/types.ts
CHANGED
|
@@ -65,6 +65,18 @@ export interface PlacementDefinition {
|
|
|
65
65
|
* override can narrow behavior but never exceed a ceiling (FR-136a).
|
|
66
66
|
*/
|
|
67
67
|
readonly retry?: RetryPolicy | false;
|
|
68
|
+
/**
|
|
69
|
+
* Minimum milliseconds between two provider requests for this placement
|
|
70
|
+
* (FR-140), added in 0.3.0.
|
|
71
|
+
*
|
|
72
|
+
* Absent takes the format-aware default: 60s for a banner, off for full-screen
|
|
73
|
+
* formats. `0` turns it off here while leaving it on elsewhere.
|
|
74
|
+
*
|
|
75
|
+
* This is an **account-safety** floor, not a frequency cap: it limits how often
|
|
76
|
+
* a placement may *ask* for an ad, never how often the app may *show* one. If
|
|
77
|
+
* you are reaching for this to pace interstitials, that belongs in the app.
|
|
78
|
+
*/
|
|
79
|
+
readonly minRequestIntervalMs?: number;
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
/**
|
|
@@ -139,11 +151,29 @@ export type ConsentState =
|
|
|
139
151
|
*/
|
|
140
152
|
| 'error';
|
|
141
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Whether the user must be offered a way to change their consent choice
|
|
156
|
+
* (FR-150), added in 0.4.0.
|
|
157
|
+
*
|
|
158
|
+
* The provider's verdict, forwarded — never the package's judgement about a
|
|
159
|
+
* region, exactly as `not_required` in `ConsentState` is not. `required` is the
|
|
160
|
+
* app's cue to show its own "Privacy options" entry; where that entry lives and
|
|
161
|
+
* what it says stay the app's.
|
|
162
|
+
*
|
|
163
|
+
* `unknown` before the first successful `requestInfoUpdate()`, after `reset()`,
|
|
164
|
+
* after a failed update, and on a platform with no adapter. It is not `not_required`:
|
|
165
|
+
* an app that hides the entry on `unknown` is right to, but it must not conclude
|
|
166
|
+
* the user has no right to one.
|
|
167
|
+
*/
|
|
168
|
+
export type PrivacyOptionsRequirement = 'required' | 'not_required' | 'unknown';
|
|
169
|
+
|
|
142
170
|
export interface ConsentSnapshot {
|
|
143
171
|
readonly state: ConsentState;
|
|
144
172
|
/** Always false when `state` is `error`. */
|
|
145
173
|
readonly canRequestAds: boolean;
|
|
146
174
|
readonly isFormAvailable: boolean;
|
|
175
|
+
/** Whether to offer a "change my choice" entry (FR-150). Added in 0.4.0. */
|
|
176
|
+
readonly privacyOptionsRequirement: PrivacyOptionsRequirement;
|
|
147
177
|
readonly error: AdsError | null;
|
|
148
178
|
}
|
|
149
179
|
|
|
@@ -269,6 +299,16 @@ export type PlacementState =
|
|
|
269
299
|
| 'failed'
|
|
270
300
|
/** Mirrors `InitStatus.undecided` so the banner host can report it directly. */
|
|
271
301
|
| 'undecided'
|
|
302
|
+
/**
|
|
303
|
+
* Refused by the per-placement request throttle (FR-140), added in 0.3.0.
|
|
304
|
+
*
|
|
305
|
+
* Deliberately distinct from `failed` and from a no-fill: nothing is wrong,
|
|
306
|
+
* nothing was requested, and the same placement will work again shortly. An
|
|
307
|
+
* app that collapses its ad slot on `failed` will usually want to collapse on
|
|
308
|
+
* this too, but it must be able to tell the two apart when diagnosing why a
|
|
309
|
+
* banner is absent.
|
|
310
|
+
*/
|
|
311
|
+
| 'throttled'
|
|
272
312
|
| 'unavailable';
|
|
273
313
|
|
|
274
314
|
/** FR-047. The provider's reward descriptor, normalized. */
|