@symbiote-native/tracking-transparency 3.0.2 → 3.1.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.
Files changed (62) hide show
  1. package/README.md +78 -73
  2. package/build/angular/index.d.ts +1 -1
  3. package/build/angular/index.js +2 -3
  4. package/build/angular/tracking-permissions.service.d.ts +5 -0
  5. package/build/angular/{services/permissions.service/index.js → tracking-permissions.service.js} +13 -48
  6. package/build/core/tracking-permission-api.d.ts +5 -0
  7. package/build/core/tracking-permission-api.js +7 -0
  8. package/build/react/index.d.ts +1 -1
  9. package/build/react/index.js +2 -5
  10. package/build/react/use-tracking-permissions.d.ts +8 -0
  11. package/build/react/use-tracking-permissions.js +4 -0
  12. package/build/solid/index.d.ts +1 -1
  13. package/build/solid/index.js +2 -6
  14. package/build/solid/use-tracking-permissions.d.ts +9 -0
  15. package/build/solid/use-tracking-permissions.js +4 -0
  16. package/build/svelte/index.d.ts +1 -1
  17. package/build/svelte/index.js +2 -5
  18. package/build/svelte/use-tracking-permissions.svelte.d.ts +8 -0
  19. package/build/svelte/use-tracking-permissions.svelte.js +4 -0
  20. package/build/vue/index.d.ts +1 -1
  21. package/build/vue/index.js +2 -5
  22. package/build/vue/use-tracking-permissions.d.ts +9 -0
  23. package/build/vue/use-tracking-permissions.js +4 -0
  24. package/build-ngc/angular/index.d.ts +1 -1
  25. package/build-ngc/angular/index.js +2 -3
  26. package/build-ngc/angular/index.js.map +1 -1
  27. package/build-ngc/angular/tracking-permissions.service.d.ts +8 -0
  28. package/build-ngc/angular/tracking-permissions.service.js +17 -0
  29. package/build-ngc/angular/tracking-permissions.service.js.map +1 -0
  30. package/build-ngc/core/tracking-permission-api.d.ts +5 -0
  31. package/build-ngc/core/tracking-permission-api.js +8 -0
  32. package/build-ngc/core/tracking-permission-api.js.map +1 -0
  33. package/package.json +15 -15
  34. package/src/angular/index.ts +2 -3
  35. package/src/angular/tracking-permissions.service.ts +16 -0
  36. package/src/core/tracking-permission-api.ts +12 -0
  37. package/src/react/index.ts +5 -6
  38. package/src/react/use-tracking-permissions.ts +16 -0
  39. package/src/solid/index.ts +5 -7
  40. package/src/solid/use-tracking-permissions.ts +17 -0
  41. package/src/svelte/index.ts +5 -6
  42. package/src/svelte/svelte-compile.test-helper.ts +6 -0
  43. package/src/svelte/use-tracking-permissions.svelte.ts +16 -0
  44. package/src/vue/index.ts +5 -6
  45. package/src/vue/use-tracking-permissions.ts +17 -0
  46. package/build/angular/services/permissions.service/index.d.ts +0 -12
  47. package/build/react/hooks/use-permissions/index.d.ts +0 -7
  48. package/build/react/hooks/use-permissions/index.js +0 -43
  49. package/build/solid/primitives/create-permissions.d.ts +0 -8
  50. package/build/solid/primitives/create-permissions.js +0 -41
  51. package/build/svelte/runes/use-permissions.svelte.d.ts +0 -7
  52. package/build/svelte/runes/use-permissions.svelte.js +0 -49
  53. package/build/vue/composables/use-permissions/index.d.ts +0 -8
  54. package/build/vue/composables/use-permissions/index.js +0 -33
  55. package/build-ngc/angular/services/permissions.service/index.d.ts +0 -15
  56. package/build-ngc/angular/services/permissions.service/index.js +0 -55
  57. package/build-ngc/angular/services/permissions.service/index.js.map +0 -1
  58. package/src/angular/services/permissions.service/index.ts +0 -57
  59. package/src/react/hooks/use-permissions/index.ts +0 -57
  60. package/src/solid/primitives/create-permissions.ts +0 -56
  61. package/src/svelte/runes/use-permissions.svelte.ts +0 -63
  62. package/src/vue/composables/use-permissions/index.ts +0 -47
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # @symbiote-native/tracking-transparency
2
2
 
3
3
  A wrapper package for [SymbioteNative](../../README.md) that makes
4
- [`expo-tracking-transparency`](https://github.com/expo/expo/tree/main/packages/expo-tracking-transparency)
5
- — the iOS App Tracking Transparency prompt, permission get/request, and the advertising-ID getter
6
- — usable from **every** adapter, React, Vue, Svelte, Solid, and Angular, not just React. Like
4
+ [`expo-tracking-transparency`](https://github.com/expo/expo/tree/main/packages/expo-tracking-transparency):
5
+ the iOS App Tracking Transparency prompt, permission get/request, and the advertising-ID getter,
6
+ usable from **every** adapter, React, Vue, Svelte, Solid, and Angular, not just React. Like
7
7
  [`@symbiote-native/brightness`](../brightness), this package's surface is mostly stateless free
8
8
  async functions; only the permission surface gets its own per-adapter lifecycle wrapper,
9
- mirroring upstream's own `usePermissions`. Built the same way as
9
+ mirroring upstream's own `useTrackingPermissions`. Built the same way as
10
10
  [`@symbiote-native/local-auth`](../local-auth), [`@symbiote-native/battery`](../battery), and
11
- [`@symbiote-native/brightness`](../brightness) — an `expo-modules-core`-based wrapper (see the
11
+ [`@symbiote-native/brightness`](../brightness) - an `expo-modules-core`-based wrapper (see the
12
12
  `symbiote-expo-native-module` project skill for the full mechanism: why `expo-modules-core` is
13
13
  depended on directly and never the `expo` meta-package, why the upstream JS is hand-ported into
14
14
  `core/` rather than imported, and how autolinking picks up the native module).
@@ -32,16 +32,16 @@ npx @symbiote-native/cli add --tracking-transparency
32
32
  ```
33
33
 
34
34
  Either way: installs `@symbiote-native/tracking-transparency` and wires the native autolinking
35
- automatically — see [`@symbiote-native/cli`](../cli).
35
+ automatically - see [`@symbiote-native/cli`](../cli).
36
36
 
37
37
  <details>
38
- <summary>Manual install (no CLI — installing and wiring native autolinking by hand)</summary>
38
+ <summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
39
39
 
40
40
  ```bash
41
41
  npm install @symbiote-native/tracking-transparency
42
42
  ```
43
43
 
44
- `expo-tracking-transparency` and `expo-modules-core` come along as regular, pinned dependencies —
44
+ `expo-tracking-transparency` and `expo-modules-core` come along as regular, pinned dependencies:
45
45
  never install either yourself, and never add the `expo` meta-package to this project (it bundles
46
46
  its own Metro/Babel pipeline that conflicts with this project's own).
47
47
 
@@ -49,21 +49,21 @@ its own Metro/Babel pipeline that conflicts with this project's own).
49
49
 
50
50
  Unlike a plain RN native module, `expo-tracking-transparency`'s native code is discovered by
51
51
  `expo-modules-autolinking`, not the `react-native.config.cjs`/podspec mechanism other wrappers in
52
- this repo use — this needs wiring into the native host app **once**, covering this package and
52
+ this repo use - this needs wiring into the native host app **once**, covering this package and
53
53
  every other `expo-modules-core` package with zero further changes:
54
54
 
55
55
  | Platform | Touches |
56
56
  | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
57
- | iOS | `ios/Podfile` — add `use_expo_modules!` |
58
- | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
59
- | iOS | `Info.plist` — `NSUserTrackingUsageDescription`, required to show the ATT prompt |
60
- | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
61
- | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
62
-
63
- Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
64
- peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. The Android
57
+ | iOS | `ios/Podfile` - add `use_expo_modules!` |
58
+ | iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
59
+ | iOS | `Info.plist` - `NSUserTrackingUsageDescription`, required to show the ATT prompt |
60
+ | Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
61
+ | Android | `MainApplication.kt` - Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
62
+
63
+ Full mechanics - the Podfile pieces that normally ship inside the `expo` package, the `expo`
64
+ peer-dependency exclusion list - live in the `symbiote-expo-native-module` skill. The Android
65
65
  native module also pulls in `com.google.android.gms:play-services-ads-identifier:18.0.1`
66
- transitively via `expo-tracking-transparency`'s own `android/build.gradle` — automatic once the
66
+ transitively via `expo-tracking-transparency`'s own `android/build.gradle` - automatic once the
67
67
  module project is included, no extra wiring needed on our side.
68
68
 
69
69
  </details>
@@ -74,32 +74,31 @@ module project is included, no extra wiring needed on our side.
74
74
  src/core/ getAdvertisingId, get/requestTrackingPermissionsAsync, isAvailable;
75
75
  native-module.ts resolves ExpoTrackingTransparency through
76
76
  expo-modules-core's requireNativeModule.
77
- src/react/hooks/ @symbiote-native/tracking-transparency/react — usePermissions
78
- src/vue/composables/ @symbiote-native/tracking-transparency/vue — usePermissions (same name)
79
- src/svelte/runes/ @symbiote-native/tracking-transparency/svelte — usePermissions (same name)
80
- src/solid/primitives/ @symbiote-native/tracking-transparency/solid — createPermissions
81
- (Solid reserves `use*` for consuming existing state)
82
- src/angular/services/ @symbiote-native/tracking-transparency/angular — PermissionsService
77
+ src/react/ @symbiote-native/tracking-transparency/react: useTrackingPermissions
78
+ src/vue/ @symbiote-native/tracking-transparency/vue: useTrackingPermissions
79
+ src/svelte/ @symbiote-native/tracking-transparency/svelte: useTrackingPermissions
80
+ src/solid/ @symbiote-native/tracking-transparency/solid: useTrackingPermissions
81
+ src/angular/ @symbiote-native/tracking-transparency/angular: TrackingPermissionsService
83
82
  ```
84
83
 
85
- Each adapter's wrapper puts the same one-shot `getTrackingPermissionsAsync`/
86
- `requestTrackingPermissionsAsync` pair in its own lifecycle idiom (auto-fetch when the wrapper is
87
- created, exposed as a tuple/object/signal/accessor). `getAdvertisingId`/`isAvailable` are stateless
88
- free-function re-exports, written once in `core/` and shared verbatim by every adapter.
84
+ Each adapter binds the same `getTrackingPermissionsAsync`/`requestTrackingPermissionsAsync` pair
85
+ (`core/tracking-permission-api.ts`) to the shared `createPermissionHook` of its adapter, or to
86
+ `PermissionsServiceBase` on Angular. `getAdvertisingId`/`isAvailable` are stateless free-function
87
+ re-exports, written once in `core/` and shared verbatim by every adapter.
89
88
 
90
- Upstream's own `createPermissionHook`/`useTrackingPermissions` is **not** ported — that helper is
91
- React-only (built on `useState`/`useEffect`), and this repo's convention is for each adapter to
92
- hand-roll its own permission hook instead, exactly like `brightness`/`cellular` already do.
89
+ `useTrackingPermissions` takes upstream's `{ get, request }` options: it fetches on mount by
90
+ default, `{ request: true }` asks instead, `{ get: false }` does nothing until called. Unlike the
91
+ former `usePermissions`, the hooks carry no `error` slot; Angular's service keeps `error`.
93
92
 
94
93
  ## Use it
95
94
 
96
95
  ```tsx
97
96
  // React
98
- import { usePermissions } from '@symbiote-native/tracking-transparency/react';
97
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/react';
99
98
  import { getAdvertisingId } from '@symbiote-native/tracking-transparency';
100
99
 
101
100
  function TrackingScreen() {
102
- const [permissionStatus, requestPermission] = usePermissions();
101
+ const [permissionStatus, requestPermission] = useTrackingPermissions();
103
102
 
104
103
  return (
105
104
  <view>
@@ -118,11 +117,10 @@ function TrackingScreen() {
118
117
  ```vue
119
118
  <!-- Vue -->
120
119
  <script setup lang="ts">
121
- import { usePermissions } from '@symbiote-native/tracking-transparency/vue';
120
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/vue';
122
121
  import { getAdvertisingId } from '@symbiote-native/tracking-transparency';
123
122
 
124
- const { status: permissionStatus, request: requestPermission } =
125
- usePermissions();
123
+ const [permissionStatus, requestPermission] = useTrackingPermissions();
126
124
  </script>
127
125
 
128
126
  <template>
@@ -142,7 +140,7 @@ const { status: permissionStatus, request: requestPermission } =
142
140
  // Angular
143
141
  import { Component, inject } from '@angular/core';
144
142
  import {
145
- PermissionsService,
143
+ TrackingPermissionsService,
146
144
  getAdvertisingId,
147
145
  } from '@symbiote-native/tracking-transparency/angular';
148
146
 
@@ -162,7 +160,7 @@ import {
162
160
  `,
163
161
  })
164
162
  export class TrackingScreen {
165
- protected readonly permissionsService = inject(PermissionsService);
163
+ protected readonly permissionsService = inject(TrackingPermissionsService);
166
164
  readonly permissionStatus = this.permissionsService.connect();
167
165
 
168
166
  logAdvertisingId(): void {
@@ -172,20 +170,20 @@ export class TrackingScreen {
172
170
  ```
173
171
 
174
172
  ```svelte
175
- <!-- Svelte — the rune hands back a boxed object whose `status` is a getter; destructuring it
173
+ <!-- Svelte - the rune hands back a boxed object whose `status` is a getter; destructuring it
176
174
  would freeze the value at its initial null, since Svelte 5 reactivity is lexically scoped. -->
177
175
  <script lang="ts">
178
176
  import {
179
177
  getAdvertisingId,
180
- usePermissions,
178
+ useTrackingPermissions,
181
179
  } from '@symbiote-native/tracking-transparency/svelte';
182
180
 
183
- const permissions = usePermissions();
181
+ const permissions = useTrackingPermissions();
184
182
  </script>
185
183
 
186
184
  <view>
187
185
  <text>{permissions.status?.status ?? 'checking…'}</text>
188
- <pressable onPress={() => permissions.request()}>
186
+ <pressable onPress={() => permissions.requestPermission()}>
189
187
  <text>Request tracking permission</text>
190
188
  </pressable>
191
189
  <pressable onPress={() => console.log(getAdvertisingId())}>
@@ -195,13 +193,12 @@ export class TrackingScreen {
195
193
  ```
196
194
 
197
195
  ```tsx
198
- // Solid — the accessors are CALLED; a Solid component body runs once, so a snapshot would freeze.
199
- import { createPermissions } from '@symbiote-native/tracking-transparency/solid';
196
+ // Solid - the accessors are CALLED; a Solid component body runs once, so a snapshot would freeze.
197
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/solid';
200
198
  import { getAdvertisingId } from '@symbiote-native/tracking-transparency';
201
199
 
202
200
  function TrackingScreen() {
203
- const { status: permissionStatus, request: requestPermission } =
204
- createPermissions();
201
+ const [permissionStatus, requestPermission] = useTrackingPermissions();
205
202
 
206
203
  return (
207
204
  <view>
@@ -217,7 +214,7 @@ function TrackingScreen() {
217
214
  }
218
215
  ```
219
216
 
220
- The examples above mirror the real canary demo screens —
217
+ The examples above mirror the real canary demo screens:
221
218
  `examples/expo-react/screens/TrackingTransparencyScreen.tsx`,
222
219
  `examples/expo-vue-sfc/screens/TrackingTransparencyScreen.vue`,
223
220
  `examples/expo-vue-tsx/screens/TrackingTransparencyScreen.tsx`,
@@ -234,14 +231,14 @@ getAdvertisingId(): string | null
234
231
 
235
232
  getTrackingPermissionsAsync(): Promise<PermissionResponse>
236
233
  requestTrackingPermissionsAsync(): Promise<PermissionResponse>
237
- // On Android and web these always resolve granted — there is no tracking-consent concept there.
234
+ // On Android and web these always resolve granted - there is no tracking-consent concept there.
238
235
  // On iOS these drive the real ATT prompt / read its current status.
239
236
 
240
237
  isAvailable(): boolean
241
238
  // Whether the native module resolved at all.
242
239
  ```
243
240
 
244
- Plus `PermissionStatus`/`PermissionResponse`/`PermissionExpiration`/`PermissionHookOptions` —
241
+ Plus `PermissionStatus`/`PermissionResponse`/`PermissionExpiration`/`PermissionHookOptions`:
245
242
  re-exported verbatim from `expo-modules-core`, never the `expo` meta-package.
246
243
 
247
244
  ```ts
@@ -252,57 +249,65 @@ import {
252
249
 
253
250
  // framework-scoped entry points re-export the same free functions, plus a per-adapter
254
251
  // permission wrapper:
255
- import { usePermissions } from '@symbiote-native/tracking-transparency/react';
256
- import { usePermissions } from '@symbiote-native/tracking-transparency/vue';
257
- import { usePermissions } from '@symbiote-native/tracking-transparency/svelte';
258
- import { createPermissions } from '@symbiote-native/tracking-transparency/solid';
259
- import { PermissionsService } from '@symbiote-native/tracking-transparency/angular';
252
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/react';
253
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/vue';
254
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/svelte';
255
+ import { useTrackingPermissions } from '@symbiote-native/tracking-transparency/solid';
256
+ import { TrackingPermissionsService } from '@symbiote-native/tracking-transparency/angular';
260
257
  ```
261
258
 
262
- `usePermissions` auto-fetches the current permission status on mount and returns a
263
- `[status, request, get, error]` tuple (React) / `{ status, error, request, get }` object (Vue,
264
- destructurable — `status` is a `Ref`). Solid's `createPermissions` returns the same shape with
265
- `Accessor`s instead of `Ref`s, and Angular's `PermissionsService.connect()` returns a readonly
266
- `Signal`. **Svelte's rune returns a boxed object whose `status` is a getter — do not destructure
267
- it**, or the value freezes at its initial `null` (Svelte 5 reactivity is lexically scoped to the
268
- property access):
259
+ `useTrackingPermissions` returns a `[status, request, get]` tuple: plain value (React), `Ref` (Vue),
260
+ `Accessor` (Solid). Angular's `TrackingPermissionsService.connect()` returns a readonly `Signal`.
261
+ **Svelte's rune returns a boxed object whose `status` is a getter - do not destructure it**, or
262
+ the value freezes at its initial `null` (Svelte 5 reactivity is lexically scoped to the property
263
+ access); its actions are `requestPermission()` and `getPermission()`:
269
264
 
270
265
  ```ts
271
266
  // React
272
- const [status, request] = usePermissions();
267
+ const [status, request] = useTrackingPermissions();
273
268
 
274
269
  // Vue
275
- const { status, request } = usePermissions();
270
+ const [status, request] = useTrackingPermissions();
276
271
 
277
- // Svelte — read permissions.status, never destructure it
278
- const permissions = usePermissions();
272
+ // Svelte - read permissions.status, never destructure it
273
+ const permissions = useTrackingPermissions();
279
274
  permissions.status;
280
275
 
281
- // Solid — status is an accessor, called at the read site
282
- const { status, request } = createPermissions();
276
+ // Solid - status is an accessor, called at the read site
277
+ const [status, request] = useTrackingPermissions();
283
278
 
284
279
  // Angular
285
- readonly status = inject(PermissionsService).connect();
280
+ readonly status = inject(TrackingPermissionsService).connect();
286
281
  ```
287
282
 
288
283
  ## Platform notes
289
284
 
290
285
  - **Android and web always report granted.** There is no tracking-consent concept on either
291
- platform — `getTrackingPermissionsAsync`/`requestTrackingPermissionsAsync` short-circuit to a
286
+ platform - `getTrackingPermissionsAsync`/`requestTrackingPermissionsAsync` short-circuit to a
292
287
  fixed granted response without ever calling the native module, matching upstream exactly.
293
- - **`getAdvertisingId` returns `null` on the iOS Simulator, regardless of any settings** — there is
288
+ - **`getAdvertisingId` returns `null` on the iOS Simulator, regardless of any settings** - there is
294
289
  no real IDFA to read there. This is expected Apple Simulator behavior, not a bug in this wrapper.
295
290
 
291
+ ## Common questions
292
+
293
+ - **Crash on request.** `NSUserTrackingUsageDescription` is missing from Info.plist.
294
+ - **Dialog never appears.** Request only while the app state is `active`; guard duplicates.
295
+ - **Rejection under Guideline 5.1.2.** Generic text is rejected; state a concrete user benefit.
296
+
297
+ Sources: [Expo docs: TrackingTransparency](https://docs.expo.dev/versions/latest/sdk/tracking-transparency/),
298
+ [expo/expo#13059](https://github.com/expo/expo/issues/13059),
299
+ [Implement App Tracking Transparency with Expo](https://yosukep.medium.com/implement-app-tracking-transparency-with-expo-app-58cf77cb168d).
300
+
296
301
  ## Test it
297
302
 
298
- No Fabric/Descriptor angle at all — tracking-transparency is a pure async-function + permission
303
+ No Fabric/Descriptor angle at all - tracking-transparency is a pure async-function + permission
299
304
  surface, never a view. Tests inject a fake native-module object in place of the real
300
305
  `requireNativeModule` resolution (`src/core/*.test.ts`, `src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`,
301
- `vitest`), the same pattern `expo-tracking-transparency` itself uses upstream — no
306
+ `vitest`), the same pattern `expo-tracking-transparency` itself uses upstream - no
302
307
  `installFabric()` for the core layer, no ViewConfig. Native rendering itself is verified on-device
303
308
  (see the parent [README](../../README.md) for the project's testing model).
304
309
 
305
310
  ## Known gaps
306
311
 
307
- - Tests exercise the JS layer only (fake native module, `vitest`) — no on-device/simulator
312
+ - Tests exercise the JS layer only (fake native module, `vitest`) - no on-device/simulator
308
313
  automated smoke test yet, only manual verification.
@@ -1,2 +1,2 @@
1
- export { PermissionsService } from './services/permissions.service';
1
+ export { TrackingPermissionsService } from './tracking-permissions.service';
2
2
  export * from '../core';
@@ -1,4 +1,3 @@
1
- // PermissionsService is the Angular-only lifecycle half; getAdvertisingId/isAvailable are
2
- // stateless free functions, shared with React/Vue.
3
- export { PermissionsService } from './services/permissions.service/index.js';
1
+ // @symbiote-native/tracking-transparency/angular: the core plus `TrackingPermissionsService`
2
+ export { TrackingPermissionsService } from './tracking-permissions.service.js';
4
3
  export * from '../core/index.js';
@@ -0,0 +1,5 @@
1
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
2
+ import type { PermissionResponse } from '../core';
3
+ export declare class TrackingPermissionsService extends PermissionsServiceBase<PermissionResponse> {
4
+ constructor();
5
+ }
@@ -1,3 +1,4 @@
1
+ // Angular twin of `../react`'s `useTrackingPermissions`: `inject(...).connect()` gives a signal
1
2
  var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
3
  function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
4
  var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
@@ -32,64 +33,28 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
33
  }
33
34
  return useValue ? value : void 0;
34
35
  };
35
- // Angular twin of React's usePermissions hook and Vue's usePermissions composable.
36
- // `connect()` auto-fetches once (no ongoing subscription to clean up, so no Injector/effect
37
- // needed, unlike BatteryStateService) and returns a readonly signal; get()/request() are
38
- // imperative one-shot methods.
39
- //
40
- // readonly status = inject(PermissionsService).connect();
41
- // // template: {{ status()?.granted }}
42
- //
43
- // The auto-fetch has nobody to reject to, so its failure lands in the `error` signal instead of
44
- // escaping as an unhandled rejection: a null status with a non-null error is how a consumer tells
45
- // "the fetch failed" from "not fetched yet". get()/request() still reject to their direct caller —
46
- // only the automatic call is made safe.
47
- import { Injectable, signal } from '@angular/core';
48
- import { getTrackingPermissionsAsync, requestTrackingPermissionsAsync, } from '../../../core/index.js';
49
- let PermissionsService = (() => {
36
+ import { Injectable } from '@angular/core';
37
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
38
+ import { trackingPermissionMethods } from '../core/tracking-permission-api.js';
39
+ let TrackingPermissionsService = (() => {
50
40
  let _classDecorators = [Injectable({ providedIn: 'root' })];
51
41
  let _classDescriptor;
52
42
  let _classExtraInitializers = [];
53
43
  let _classThis;
54
- var PermissionsService = class {
44
+ let _classSuper = PermissionsServiceBase;
45
+ var TrackingPermissionsService = class extends _classSuper {
55
46
  static { _classThis = this; }
56
47
  static {
57
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
48
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
58
49
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
59
- PermissionsService = _classThis = _classDescriptor.value;
50
+ TrackingPermissionsService = _classThis = _classDescriptor.value;
60
51
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
61
52
  __runInitializers(_classThis, _classExtraInitializers);
62
53
  }
63
- status = signal(null);
64
- fetchError = signal(null);
65
- isAutoFetchStarted = false;
66
- /** Why the auto-fetch left `status` at null. Cleared by the next successful get()/request(). */
67
- error = this.fetchError.asReadonly();
68
- connect() {
69
- // Latched rather than guarded on `status() === null`: a failed fetch leaves status null
70
- // forever, so the old guard turned every later connect() into another native call — unbounded
71
- // once connect() is reached from a change-detected expression instead of a field initializer.
72
- if (!this.isAutoFetchStarted) {
73
- this.isAutoFetchStarted = true;
74
- this.get().catch((cause) => {
75
- this.fetchError.set(cause instanceof Error ? cause : new Error(String(cause)));
76
- });
77
- }
78
- return this.status.asReadonly();
79
- }
80
- async get() {
81
- const response = await getTrackingPermissionsAsync();
82
- this.status.set(response);
83
- this.fetchError.set(null);
84
- return response;
85
- }
86
- async request() {
87
- const response = await requestTrackingPermissionsAsync();
88
- this.status.set(response);
89
- this.fetchError.set(null);
90
- return response;
54
+ constructor() {
55
+ super(trackingPermissionMethods.getMethod, trackingPermissionMethods.requestMethod);
91
56
  }
92
57
  };
93
- return PermissionsService = _classThis;
58
+ return TrackingPermissionsService = _classThis;
94
59
  })();
95
- export { PermissionsService };
60
+ export { TrackingPermissionsService };
@@ -0,0 +1,5 @@
1
+ import { getTrackingPermissionsAsync, requestTrackingPermissionsAsync } from './tracking-transparency';
2
+ export declare const trackingPermissionMethods: {
3
+ getMethod: typeof getTrackingPermissionsAsync;
4
+ requestMethod: typeof requestTrackingPermissionsAsync;
5
+ };
@@ -0,0 +1,7 @@
1
+ // `methods` every adapter's `useTrackingPermissions` binds to the shared
2
+ // `@symbiote-native/{react,vue,solid,svelte}` `createPermissionHook` factory
3
+ import { getTrackingPermissionsAsync, requestTrackingPermissionsAsync, } from './tracking-transparency.js';
4
+ export const trackingPermissionMethods = {
5
+ getMethod: getTrackingPermissionsAsync,
6
+ requestMethod: requestTrackingPermissionsAsync,
7
+ };
@@ -1,2 +1,2 @@
1
- export { usePermissions } from './hooks/use-permissions';
1
+ export { useTrackingPermissions, type IUseTrackingPermissionsHook, } from './use-tracking-permissions';
2
2
  export * from '../core';
@@ -1,6 +1,3 @@
1
- // @symbiote-native/tracking-transparency/react: the React entry over the framework-agnostic
2
- // core. Only the permission surface gets a lifecycle hook (there is per-instance state to seed
3
- // and refresh); getAdvertisingId/isAvailable are stateless, re-exported verbatim — mirrors the
4
- // lifecycle-bucket naming convention of adapters/react/src/hooks.
5
- export { usePermissions } from './hooks/use-permissions/index.js';
1
+ // @symbiote-native/tracking-transparency/react: the core plus `useTrackingPermissions`
2
+ export { useTrackingPermissions, } from './use-tracking-permissions.js';
6
3
  export * from '../core/index.js';
@@ -0,0 +1,8 @@
1
+ import type { PermissionResponse } from '../core';
2
+ import type { IPermissionHookBehavior } from '@symbiote-native/engine';
3
+ export type IUseTrackingPermissionsHook = (options?: IPermissionHookBehavior) => [
4
+ PermissionResponse | null,
5
+ () => Promise<PermissionResponse>,
6
+ () => Promise<PermissionResponse>
7
+ ];
8
+ export declare const useTrackingPermissions: IUseTrackingPermissionsHook;
@@ -0,0 +1,4 @@
1
+ import { createPermissionHook } from '@symbiote-native/react';
2
+ import { trackingPermissionMethods } from '../core/tracking-permission-api.js';
3
+ // React twin of `expo-tracking-transparency`'s `useTrackingPermissions`
4
+ export const useTrackingPermissions = createPermissionHook(trackingPermissionMethods);
@@ -1,2 +1,2 @@
1
- export { createPermissions } from './primitives/create-permissions';
1
+ export { useTrackingPermissions, type IUseTrackingPermissionsHook, } from './use-tracking-permissions';
2
2
  export * from '../core';
@@ -1,7 +1,3 @@
1
- // @symbiote-native/tracking-transparency/solid: the Solid entry over the framework-agnostic core.
2
- // Same reasoning as the React, Vue and Svelte entries — only the permission surface gets a
3
- // lifecycle primitive, every other export is a plain re-export — mirrors the lifecycle-bucket
4
- // naming convention of adapters/solid/src/primitives (never `hooks/`/`composables/`/`runes/`,
5
- // those are React's, Vue's and Svelte's terms).
6
- export { createPermissions } from './primitives/create-permissions.js';
1
+ // @symbiote-native/tracking-transparency/solid: the core plus `useTrackingPermissions`
2
+ export { useTrackingPermissions, } from './use-tracking-permissions.js';
7
3
  export * from '../core/index.js';
@@ -0,0 +1,9 @@
1
+ import type { PermissionResponse } from '../core';
2
+ import type { IPermissionHookBehavior } from '@symbiote-native/engine';
3
+ import type { Accessor } from 'solid-js';
4
+ export type IUseTrackingPermissionsHook = (options?: IPermissionHookBehavior) => [
5
+ Accessor<PermissionResponse | null>,
6
+ () => Promise<PermissionResponse>,
7
+ () => Promise<PermissionResponse>
8
+ ];
9
+ export declare const useTrackingPermissions: IUseTrackingPermissionsHook;
@@ -0,0 +1,4 @@
1
+ import { createPermissionHook } from '@symbiote-native/solid';
2
+ import { trackingPermissionMethods } from '../core/tracking-permission-api.js';
3
+ // Solid twin of `../react`'s `useTrackingPermissions`
4
+ export const useTrackingPermissions = createPermissionHook(trackingPermissionMethods);
@@ -1,2 +1,2 @@
1
- export { usePermissions } from './runes/use-permissions.svelte';
1
+ export { useTrackingPermissions, type IUseTrackingPermissionsHook, } from './use-tracking-permissions.svelte';
2
2
  export * from '../core';
@@ -1,6 +1,3 @@
1
- // @symbiote-native/tracking-transparency/svelte: the Svelte entry over the framework-agnostic core.
2
- // Same reasoning as the React and Vue entries — only the permission surface gets a lifecycle rune,
3
- // every other export is a plain re-export — mirrors the lifecycle-bucket naming convention of
4
- // adapters/svelte/src/runes (never `hooks/`/`composables/`, those are React's and Vue's terms).
5
- export { usePermissions } from './runes/use-permissions.svelte';
1
+ // @symbiote-native/tracking-transparency/svelte: the core plus `useTrackingPermissions`
2
+ export { useTrackingPermissions, } from './use-tracking-permissions.svelte';
6
3
  export * from '../core/index.js';
@@ -0,0 +1,8 @@
1
+ import type { PermissionResponse } from '../core';
2
+ import type { IPermissionHookBehavior } from '@symbiote-native/engine';
3
+ export type IUseTrackingPermissionsHook = (options?: IPermissionHookBehavior) => {
4
+ readonly status: PermissionResponse | null;
5
+ requestPermission: () => Promise<PermissionResponse>;
6
+ getPermission: () => Promise<PermissionResponse>;
7
+ };
8
+ export declare const useTrackingPermissions: IUseTrackingPermissionsHook;
@@ -0,0 +1,4 @@
1
+ import { createPermissionHook } from '@symbiote-native/svelte/runes/create-permission-hook';
2
+ import { trackingPermissionMethods } from '../core/tracking-permission-api.js';
3
+ // Svelte twin of `../react`'s `useTrackingPermissions`
4
+ export const useTrackingPermissions = createPermissionHook(trackingPermissionMethods);
@@ -1,2 +1,2 @@
1
- export { usePermissions } from './composables/use-permissions';
1
+ export { useTrackingPermissions, type IUseTrackingPermissionsHook, } from './use-tracking-permissions';
2
2
  export * from '../core';
@@ -1,6 +1,3 @@
1
- // @symbiote-native/tracking-transparency/vue: the Vue entry over the framework-agnostic core.
2
- // Same reasoning as the React entry — only the permission surface gets a lifecycle composable,
3
- // every other export is a plain re-export — mirrors the lifecycle-bucket naming convention of
4
- // adapters/vue/src/composables (never `hooks/`, that's React's term).
5
- export { usePermissions } from './composables/use-permissions/index.js';
1
+ // @symbiote-native/tracking-transparency/vue: the core plus `useTrackingPermissions`
2
+ export { useTrackingPermissions, } from './use-tracking-permissions.js';
6
3
  export * from '../core/index.js';
@@ -0,0 +1,9 @@
1
+ import type { PermissionResponse } from '../core';
2
+ import type { IPermissionHookBehavior } from '@symbiote-native/engine';
3
+ import type { Ref } from '@vue/runtime-core';
4
+ export type IUseTrackingPermissionsHook = (options?: IPermissionHookBehavior) => [
5
+ Ref<PermissionResponse | null>,
6
+ () => Promise<PermissionResponse>,
7
+ () => Promise<PermissionResponse>
8
+ ];
9
+ export declare const useTrackingPermissions: IUseTrackingPermissionsHook;
@@ -0,0 +1,4 @@
1
+ import { createPermissionHook } from '@symbiote-native/vue';
2
+ import { trackingPermissionMethods } from '../core/tracking-permission-api.js';
3
+ // Vue twin of `../react`'s `useTrackingPermissions`
4
+ export const useTrackingPermissions = createPermissionHook(trackingPermissionMethods);
@@ -1,2 +1,2 @@
1
- export { PermissionsService } from './services/permissions.service';
1
+ export { TrackingPermissionsService } from './tracking-permissions.service';
2
2
  export * from '../core';
@@ -1,5 +1,4 @@
1
- // PermissionsService is the Angular-only lifecycle half; getAdvertisingId/isAvailable are
2
- // stateless free functions, shared with React/Vue.
3
- export { PermissionsService } from './services/permissions.service';
1
+ // @symbiote-native/tracking-transparency/angular: the core plus `TrackingPermissionsService`
2
+ export { TrackingPermissionsService } from './tracking-permissions.service';
4
3
  export * from '../core';
5
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,mDAAmD;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAC5E,cAAc,SAAS,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
2
+ import type { PermissionResponse } from '../core';
3
+ import * as i0 from "@angular/core";
4
+ export declare class TrackingPermissionsService extends PermissionsServiceBase<PermissionResponse> {
5
+ constructor();
6
+ static ɵfac: i0.ɵɵFactoryDeclaration<TrackingPermissionsService, never>;
7
+ static ɵprov: i0.ɵɵInjectableDeclaration<TrackingPermissionsService>;
8
+ }
@@ -0,0 +1,17 @@
1
+ // Angular twin of `../react`'s `useTrackingPermissions`: `inject(...).connect()` gives a signal
2
+ import { Injectable } from '@angular/core';
3
+ import { PermissionsServiceBase } from '@symbiote-native/angular';
4
+ import { trackingPermissionMethods } from '../core/tracking-permission-api';
5
+ import * as i0 from "@angular/core";
6
+ export class TrackingPermissionsService extends PermissionsServiceBase {
7
+ constructor() {
8
+ super(trackingPermissionMethods.getMethod, trackingPermissionMethods.requestMethod);
9
+ }
10
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TrackingPermissionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TrackingPermissionsService, providedIn: 'root' });
12
+ }
13
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TrackingPermissionsService, decorators: [{
14
+ type: Injectable,
15
+ args: [{ providedIn: 'root' }]
16
+ }], ctorParameters: () => [] });
17
+ //# sourceMappingURL=tracking-permissions.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking-permissions.service.js","sourceRoot":"","sources":["../../src/angular/tracking-permissions.service.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAEhG,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;;AAI5E,MAAM,OAAO,0BAA2B,SAAQ,sBAA0C;IACxF;QACE,KAAK,CACH,yBAAyB,CAAC,SAAS,EACnC,yBAAyB,CAAC,aAAa,CACxC,CAAC;IACJ,CAAC;uGANU,0BAA0B;2GAA1B,0BAA0B,cADb,MAAM;;2FACnB,0BAA0B;kBADtC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}