@wwdrew/expo-spotify-sdk 0.8.0 → 1.0.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 (80) hide show
  1. package/README.md +378 -138
  2. package/android/build.gradle +10 -0
  3. package/android/consumer-rules.pro +5 -0
  4. package/android/libs/SETUP.md +29 -0
  5. package/android/src/main/java/expo/modules/spotifysdk/ExpoSpotifySDKModule.kt +146 -9
  6. package/android/src/main/java/expo/modules/spotifysdk/SpotifyAppRemoteCoordinator.kt +556 -0
  7. package/android/src/main/java/expo/modules/spotifysdk/SpotifyErrors.kt +112 -0
  8. package/build/ExpoSpotifySDKModule.web.d.ts +23 -0
  9. package/build/ExpoSpotifySDKModule.web.d.ts.map +1 -1
  10. package/build/ExpoSpotifySDKModule.web.js +74 -5
  11. package/build/ExpoSpotifySDKModule.web.js.map +1 -1
  12. package/build/app-remote/error.d.ts +8 -0
  13. package/build/app-remote/error.d.ts.map +1 -0
  14. package/build/app-remote/error.js +10 -0
  15. package/build/app-remote/error.js.map +1 -0
  16. package/build/app-remote/index.d.ts +79 -0
  17. package/build/app-remote/index.d.ts.map +1 -0
  18. package/build/app-remote/index.js +110 -0
  19. package/build/app-remote/index.js.map +1 -0
  20. package/build/auth/error.d.ts +8 -0
  21. package/build/auth/error.d.ts.map +1 -0
  22. package/build/auth/error.js +10 -0
  23. package/build/auth/error.js.map +1 -0
  24. package/build/auth/index.d.ts +124 -0
  25. package/build/auth/index.d.ts.map +1 -0
  26. package/build/auth/index.js +162 -0
  27. package/build/auth/index.js.map +1 -0
  28. package/build/content/error.d.ts +8 -0
  29. package/build/content/error.d.ts.map +1 -0
  30. package/build/content/error.js +10 -0
  31. package/build/content/error.js.map +1 -0
  32. package/build/content/index.d.ts +38 -0
  33. package/build/content/index.d.ts.map +1 -0
  34. package/build/content/index.js +56 -0
  35. package/build/content/index.js.map +1 -0
  36. package/build/error.d.ts +24 -0
  37. package/build/error.d.ts.map +1 -0
  38. package/build/error.js +25 -0
  39. package/build/error.js.map +1 -0
  40. package/build/hooks/index.d.ts +90 -0
  41. package/build/hooks/index.d.ts.map +1 -0
  42. package/build/hooks/index.js +326 -0
  43. package/build/hooks/index.js.map +1 -0
  44. package/build/images/error.d.ts +8 -0
  45. package/build/images/error.d.ts.map +1 -0
  46. package/build/images/error.js +10 -0
  47. package/build/images/error.js.map +1 -0
  48. package/build/images/index.d.ts +36 -0
  49. package/build/images/index.d.ts.map +1 -0
  50. package/build/images/index.js +59 -0
  51. package/build/images/index.js.map +1 -0
  52. package/build/index.d.ts +44 -56
  53. package/build/index.d.ts.map +1 -1
  54. package/build/index.js +49 -141
  55. package/build/index.js.map +1 -1
  56. package/build/player/error.d.ts +8 -0
  57. package/build/player/error.d.ts.map +1 -0
  58. package/build/player/error.js +10 -0
  59. package/build/player/error.js.map +1 -0
  60. package/build/player/index.d.ts +140 -0
  61. package/build/player/index.d.ts.map +1 -0
  62. package/build/player/index.js +136 -0
  63. package/build/player/index.js.map +1 -0
  64. package/build/uri/index.d.ts +55 -0
  65. package/build/uri/index.d.ts.map +1 -0
  66. package/build/uri/index.js +57 -0
  67. package/build/uri/index.js.map +1 -0
  68. package/build/user/error.d.ts +8 -0
  69. package/build/user/error.d.ts.map +1 -0
  70. package/build/user/error.js +10 -0
  71. package/build/user/error.js.map +1 -0
  72. package/build/user/index.d.ts +44 -0
  73. package/build/user/index.d.ts.map +1 -0
  74. package/build/user/index.js +97 -0
  75. package/build/user/index.js.map +1 -0
  76. package/ios/ExpoSpotifySDKModule.swift +227 -5
  77. package/ios/SpotifyAppRemoteCoordinator.swift +939 -0
  78. package/ios/SpotifyAuthCoordinator.swift +53 -13
  79. package/ios/SpotifyTokenRefreshClient.swift +28 -0
  80. package/package.json +4 -3
@@ -0,0 +1,326 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { AppRemote } from "../app-remote";
3
+ import { Auth } from "../auth";
4
+ import { Player } from "../player";
5
+ import { User } from "../user";
6
+ // ---------------------------------------------------------------------------
7
+ // Connection-state store
8
+ //
9
+ // A module-level singleton that caches the latest ConnectionState, exposes a
10
+ // subscribe function for useSyncExternalStore, and stays in sync via the
11
+ // native onConnectionStateChange event. Initialised lazily on first use.
12
+ // ---------------------------------------------------------------------------
13
+ let _connectionState = "disconnected";
14
+ const _connectionListeners = new Set();
15
+ let _connectionStoreInitialised = false;
16
+ function initConnectionStore() {
17
+ if (_connectionStoreInitialised)
18
+ return;
19
+ _connectionStoreInitialised = true;
20
+ // Seed with the current native state so the first snapshot is accurate.
21
+ AppRemote.getConnectionState().then((state) => {
22
+ if (state !== _connectionState) {
23
+ _connectionState = state;
24
+ _connectionListeners.forEach((l) => l());
25
+ }
26
+ });
27
+ AppRemote.addListener("connectionStateChange", ({ state }) => {
28
+ _connectionState = state;
29
+ _connectionListeners.forEach((l) => l());
30
+ });
31
+ }
32
+ function subscribeConnectionState(listener) {
33
+ initConnectionStore();
34
+ _connectionListeners.add(listener);
35
+ return () => _connectionListeners.delete(listener);
36
+ }
37
+ function getConnectionSnapshot() {
38
+ return _connectionState;
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Session store
42
+ // ---------------------------------------------------------------------------
43
+ let _session = null;
44
+ const _sessionListeners = new Set();
45
+ let _sessionStoreInitialised = false;
46
+ function initSessionStore() {
47
+ if (_sessionStoreInitialised)
48
+ return;
49
+ _sessionStoreInitialised = true;
50
+ Auth.addListener("sessionChange", (event) => {
51
+ if (event.type === "didInitiate" || event.type === "didRenew") {
52
+ _session = event.session;
53
+ }
54
+ else {
55
+ _session = null;
56
+ }
57
+ _sessionListeners.forEach((l) => l());
58
+ });
59
+ }
60
+ function subscribeSession(listener) {
61
+ initSessionStore();
62
+ _sessionListeners.add(listener);
63
+ return () => _sessionListeners.delete(listener);
64
+ }
65
+ function getSessionSnapshot() {
66
+ return _session;
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Player-state store
70
+ //
71
+ // Seeded from the first playerStateChange event after subscription. The
72
+ // native side automatically starts streaming player state updates once the
73
+ // App Remote connection is established.
74
+ // ---------------------------------------------------------------------------
75
+ let _playerState = null;
76
+ const _playerListeners = new Set();
77
+ let _playerStoreInitialised = false;
78
+ function normalizePlayerState(nextState, previousState) {
79
+ const nextName = nextState.track.name?.trim() ?? "";
80
+ const previousName = previousState?.track.name?.trim() ?? "";
81
+ const sameTrack = previousState != null && previousState.track.uri === nextState.track.uri;
82
+ // App Remote can occasionally emit a transient blank title between valid
83
+ // snapshots for the same URI; keep the last non-empty title to avoid UI
84
+ // flicker/regression in hooks consumers.
85
+ if (sameTrack && nextName.length === 0 && previousName.length > 0) {
86
+ return {
87
+ ...nextState,
88
+ track: {
89
+ ...nextState.track,
90
+ name: previousState.track.name,
91
+ },
92
+ };
93
+ }
94
+ return nextState;
95
+ }
96
+ function notifyPlayerListeners() {
97
+ _playerListeners.forEach((l) => l());
98
+ }
99
+ async function hydratePlayerState() {
100
+ try {
101
+ const state = await Player.getPlayerState();
102
+ _playerState = normalizePlayerState(state, _playerState);
103
+ notifyPlayerListeners();
104
+ }
105
+ catch {
106
+ // Ignore one-shot hydration failures (e.g., connection races). Event stream
107
+ // updates still populate this store once available.
108
+ }
109
+ }
110
+ function initPlayerStore() {
111
+ if (_playerStoreInitialised)
112
+ return;
113
+ _playerStoreInitialised = true;
114
+ // Hydrate immediately if the module is already connected before any consumer
115
+ // subscribes to player events.
116
+ AppRemote.getConnectionState().then((state) => {
117
+ if (state === "connected") {
118
+ void hydratePlayerState();
119
+ }
120
+ });
121
+ // Refresh the one-shot snapshot whenever App Remote reconnects, and clear on
122
+ // disconnect so stale "now playing" data is not retained.
123
+ AppRemote.addListener("connectionStateChange", ({ state }) => {
124
+ if (state === "connected") {
125
+ void hydratePlayerState();
126
+ return;
127
+ }
128
+ if (_playerState !== null) {
129
+ _playerState = null;
130
+ notifyPlayerListeners();
131
+ }
132
+ });
133
+ Player.addListener("playerStateChange", (state) => {
134
+ _playerState = normalizePlayerState(state, _playerState);
135
+ notifyPlayerListeners();
136
+ });
137
+ }
138
+ function subscribePlayerState(listener) {
139
+ initPlayerStore();
140
+ _playerListeners.add(listener);
141
+ return () => _playerListeners.delete(listener);
142
+ }
143
+ function getPlayerSnapshot() {
144
+ return _playerState;
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // Capabilities store
148
+ // ---------------------------------------------------------------------------
149
+ let _capabilities = null;
150
+ const _capabilitiesListeners = new Set();
151
+ let _capabilitiesStoreInitialised = false;
152
+ function initCapabilitiesStore() {
153
+ if (_capabilitiesStoreInitialised)
154
+ return;
155
+ _capabilitiesStoreInitialised = true;
156
+ User.getCapabilities()
157
+ .then((capabilities) => {
158
+ _capabilities = capabilities;
159
+ _capabilitiesListeners.forEach((l) => l());
160
+ })
161
+ .catch(() => {
162
+ // Swallow initial read failures (e.g., not connected yet). The event
163
+ // stream will hydrate this later.
164
+ });
165
+ User.addListener("capabilitiesChange", (capabilities) => {
166
+ _capabilities = capabilities;
167
+ _capabilitiesListeners.forEach((l) => l());
168
+ });
169
+ }
170
+ function subscribeCapabilities(listener) {
171
+ initCapabilitiesStore();
172
+ _capabilitiesListeners.add(listener);
173
+ return () => _capabilitiesListeners.delete(listener);
174
+ }
175
+ function getCapabilitiesSnapshot() {
176
+ return _capabilities;
177
+ }
178
+ const _libraryStores = new Map();
179
+ function getOrCreateLibraryStore(uri) {
180
+ const key = String(uri);
181
+ let store = _libraryStores.get(key);
182
+ if (!store) {
183
+ store = { state: null, listeners: new Set(), initialised: false };
184
+ _libraryStores.set(key, store);
185
+ }
186
+ return store;
187
+ }
188
+ function initLibraryStore(uri) {
189
+ const store = getOrCreateLibraryStore(uri);
190
+ if (store.initialised)
191
+ return;
192
+ store.initialised = true;
193
+ User.getLibraryState(uri)
194
+ .then((state) => {
195
+ store.state = state;
196
+ store.listeners.forEach((l) => l());
197
+ })
198
+ .catch(() => {
199
+ // Not connected / unavailable yet; listener updates can still arrive later.
200
+ });
201
+ User.addLibraryStateListener(uri, (state) => {
202
+ const next = getOrCreateLibraryStore(uri);
203
+ next.state = state;
204
+ next.listeners.forEach((l) => l());
205
+ });
206
+ }
207
+ function subscribeLibraryState(uri, listener) {
208
+ const key = String(uri);
209
+ initLibraryStore(uri);
210
+ const store = getOrCreateLibraryStore(uri);
211
+ store.listeners.add(listener);
212
+ return () => {
213
+ const current = _libraryStores.get(key);
214
+ if (!current)
215
+ return;
216
+ current.listeners.delete(listener);
217
+ };
218
+ }
219
+ function getLibrarySnapshot(uri) {
220
+ return _libraryStores.get(String(uri))?.state ?? null;
221
+ }
222
+ // ---------------------------------------------------------------------------
223
+ // Public hooks
224
+ // ---------------------------------------------------------------------------
225
+ /**
226
+ * Returns the current Spotify OAuth session, or `null` when not authenticated.
227
+ * Updates automatically whenever `Auth.authenticate()` or `Auth.refresh()`
228
+ * resolves, and on session failure.
229
+ *
230
+ * Built on `useSyncExternalStore` for tearing-free React rendering.
231
+ */
232
+ export function useSession() {
233
+ return useSyncExternalStore(subscribeSession, getSessionSnapshot, getSessionSnapshot);
234
+ }
235
+ /**
236
+ * Returns the current App Remote {@link ConnectionState}
237
+ * (`"disconnected"` | `"connecting"` | `"connected"`). Updates automatically
238
+ * on every state transition driven by `AppRemote.connect()` and `disconnect()`.
239
+ *
240
+ * Built on `useSyncExternalStore` for tearing-free React rendering.
241
+ *
242
+ * @example
243
+ * ```tsx
244
+ * function ConnectionBanner() {
245
+ * const state = useConnectionState();
246
+ * return <Text>{state === "connected" ? "Connected" : "Disconnected"}</Text>;
247
+ * }
248
+ * ```
249
+ */
250
+ export function useConnectionState() {
251
+ return useSyncExternalStore(subscribeConnectionState, getConnectionSnapshot, getConnectionSnapshot);
252
+ }
253
+ /**
254
+ * Returns the latest {@link PlayerState} from the Spotify app, or `null`
255
+ * before the first update arrives (i.e., before `AppRemote.connect()` resolves
256
+ * and the native subscription emits its first event).
257
+ *
258
+ * Updates on every state change reported by the Spotify app (track change,
259
+ * pause/resume, seek, shuffle/repeat toggle, etc.).
260
+ *
261
+ * Built on `useSyncExternalStore` for tearing-free React rendering.
262
+ *
263
+ * @example
264
+ * ```tsx
265
+ * function NowPlaying() {
266
+ * const state = usePlayerState();
267
+ * if (!state) return <Text>Not playing</Text>;
268
+ * return <Text>{state.track.name} — {state.isPaused ? "Paused" : "Playing"}</Text>;
269
+ * }
270
+ * ```
271
+ */
272
+ export function usePlayerState() {
273
+ return useSyncExternalStore(subscribePlayerState, getPlayerSnapshot, getPlayerSnapshot);
274
+ }
275
+ /**
276
+ * Returns the currently playing {@link Track}, or `null` when nothing is
277
+ * playing or before the first state update arrives.
278
+ *
279
+ * Derived from `usePlayerState`.
280
+ */
281
+ export function useCurrentTrack() {
282
+ return usePlayerState()?.track ?? null;
283
+ }
284
+ /**
285
+ * Returns `true` when the Spotify player is actively playing (not paused),
286
+ * and `false` otherwise (including before the first state update arrives).
287
+ *
288
+ * Derived from `usePlayerState`.
289
+ */
290
+ export function useIsPlaying() {
291
+ const state = usePlayerState();
292
+ return state !== null && !state.isPaused;
293
+ }
294
+ /**
295
+ * Returns the current playback position in milliseconds. Returns `0` before
296
+ * the first state update arrives.
297
+ *
298
+ * **Note:** This value updates whenever the native side emits a player state
299
+ * change (track transitions, pause/resume, seek, etc.) — not on a fixed timer.
300
+ * For a progress bar that ticks smoothly, combine this with a local `Date.now`
301
+ * offset and `requestAnimationFrame`.
302
+ *
303
+ * Derived from `usePlayerState`.
304
+ */
305
+ export function usePlaybackPosition() {
306
+ return usePlayerState()?.playbackPosition ?? 0;
307
+ }
308
+ /**
309
+ * Returns the latest Spotify user capabilities, or `null` before the first
310
+ * snapshot arrives.
311
+ *
312
+ * Derived from `User.getCapabilities()` + `User.addListener("capabilitiesChange")`.
313
+ */
314
+ export function useCapabilities() {
315
+ return useSyncExternalStore(subscribeCapabilities, getCapabilitiesSnapshot, getCapabilitiesSnapshot);
316
+ }
317
+ /**
318
+ * Returns the library state for a specific URI, or `null` before the first
319
+ * snapshot arrives.
320
+ *
321
+ * Derived from `User.getLibraryState(uri)` + `User.addLibraryStateListener(uri, ...)`.
322
+ */
323
+ export function useLibraryState(uri) {
324
+ return useSyncExternalStore((listener) => subscribeLibraryState(uri, listener), () => getLibrarySnapshot(uri), () => getLibrarySnapshot(uri));
325
+ }
326
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { useSyncExternalStore } from \"react\";\n\nimport { AppRemote, ConnectionState } from \"../app-remote\";\nimport { Auth, SpotifySession } from \"../auth\";\nimport { Player, PlayerState, Track } from \"../player\";\nimport { Capabilities, LibraryState, User } from \"../user\";\nimport type { SpotifyURI as SpotifyURIType } from \"../uri\";\n\n// ---------------------------------------------------------------------------\n// Shared utilities\n// ---------------------------------------------------------------------------\n\ntype Listener = () => void;\n\n// ---------------------------------------------------------------------------\n// Connection-state store\n//\n// A module-level singleton that caches the latest ConnectionState, exposes a\n// subscribe function for useSyncExternalStore, and stays in sync via the\n// native onConnectionStateChange event. Initialised lazily on first use.\n// ---------------------------------------------------------------------------\n\nlet _connectionState: ConnectionState = \"disconnected\";\nconst _connectionListeners = new Set<Listener>();\nlet _connectionStoreInitialised = false;\n\nfunction initConnectionStore() {\n if (_connectionStoreInitialised) return;\n _connectionStoreInitialised = true;\n\n // Seed with the current native state so the first snapshot is accurate.\n AppRemote.getConnectionState().then((state) => {\n if (state !== _connectionState) {\n _connectionState = state;\n _connectionListeners.forEach((l) => l());\n }\n });\n\n AppRemote.addListener(\"connectionStateChange\", ({ state }) => {\n _connectionState = state;\n _connectionListeners.forEach((l) => l());\n });\n}\n\nfunction subscribeConnectionState(listener: Listener): () => void {\n initConnectionStore();\n _connectionListeners.add(listener);\n return () => _connectionListeners.delete(listener);\n}\n\nfunction getConnectionSnapshot(): ConnectionState {\n return _connectionState;\n}\n\n// ---------------------------------------------------------------------------\n// Session store\n// ---------------------------------------------------------------------------\n\nlet _session: SpotifySession | null = null;\nconst _sessionListeners = new Set<Listener>();\nlet _sessionStoreInitialised = false;\n\nfunction initSessionStore() {\n if (_sessionStoreInitialised) return;\n _sessionStoreInitialised = true;\n\n Auth.addListener(\"sessionChange\", (event) => {\n if (event.type === \"didInitiate\" || event.type === \"didRenew\") {\n _session = event.session;\n } else {\n _session = null;\n }\n _sessionListeners.forEach((l) => l());\n });\n}\n\nfunction subscribeSession(listener: Listener): () => void {\n initSessionStore();\n _sessionListeners.add(listener);\n return () => _sessionListeners.delete(listener);\n}\n\nfunction getSessionSnapshot(): SpotifySession | null {\n return _session;\n}\n\n// ---------------------------------------------------------------------------\n// Player-state store\n//\n// Seeded from the first playerStateChange event after subscription. The\n// native side automatically starts streaming player state updates once the\n// App Remote connection is established.\n// ---------------------------------------------------------------------------\n\nlet _playerState: PlayerState | null = null;\nconst _playerListeners = new Set<Listener>();\nlet _playerStoreInitialised = false;\n\nfunction normalizePlayerState(\n nextState: PlayerState,\n previousState: PlayerState | null,\n): PlayerState {\n const nextName = nextState.track.name?.trim() ?? \"\";\n const previousName = previousState?.track.name?.trim() ?? \"\";\n const sameTrack =\n previousState != null && previousState.track.uri === nextState.track.uri;\n\n // App Remote can occasionally emit a transient blank title between valid\n // snapshots for the same URI; keep the last non-empty title to avoid UI\n // flicker/regression in hooks consumers.\n if (sameTrack && nextName.length === 0 && previousName.length > 0) {\n return {\n ...nextState,\n track: {\n ...nextState.track,\n name: previousState.track.name,\n },\n };\n }\n\n return nextState;\n}\n\nfunction notifyPlayerListeners() {\n _playerListeners.forEach((l) => l());\n}\n\nasync function hydratePlayerState() {\n try {\n const state = await Player.getPlayerState();\n _playerState = normalizePlayerState(state, _playerState);\n notifyPlayerListeners();\n } catch {\n // Ignore one-shot hydration failures (e.g., connection races). Event stream\n // updates still populate this store once available.\n }\n}\n\nfunction initPlayerStore() {\n if (_playerStoreInitialised) return;\n _playerStoreInitialised = true;\n\n // Hydrate immediately if the module is already connected before any consumer\n // subscribes to player events.\n AppRemote.getConnectionState().then((state) => {\n if (state === \"connected\") {\n void hydratePlayerState();\n }\n });\n\n // Refresh the one-shot snapshot whenever App Remote reconnects, and clear on\n // disconnect so stale \"now playing\" data is not retained.\n AppRemote.addListener(\"connectionStateChange\", ({ state }) => {\n if (state === \"connected\") {\n void hydratePlayerState();\n return;\n }\n\n if (_playerState !== null) {\n _playerState = null;\n notifyPlayerListeners();\n }\n });\n\n Player.addListener(\"playerStateChange\", (state) => {\n _playerState = normalizePlayerState(state, _playerState);\n notifyPlayerListeners();\n });\n}\n\nfunction subscribePlayerState(listener: Listener): () => void {\n initPlayerStore();\n _playerListeners.add(listener);\n return () => _playerListeners.delete(listener);\n}\n\nfunction getPlayerSnapshot(): PlayerState | null {\n return _playerState;\n}\n\n// ---------------------------------------------------------------------------\n// Capabilities store\n// ---------------------------------------------------------------------------\n\nlet _capabilities: Capabilities | null = null;\nconst _capabilitiesListeners = new Set<Listener>();\nlet _capabilitiesStoreInitialised = false;\n\nfunction initCapabilitiesStore() {\n if (_capabilitiesStoreInitialised) return;\n _capabilitiesStoreInitialised = true;\n\n User.getCapabilities()\n .then((capabilities) => {\n _capabilities = capabilities;\n _capabilitiesListeners.forEach((l) => l());\n })\n .catch(() => {\n // Swallow initial read failures (e.g., not connected yet). The event\n // stream will hydrate this later.\n });\n\n User.addListener(\"capabilitiesChange\", (capabilities) => {\n _capabilities = capabilities;\n _capabilitiesListeners.forEach((l) => l());\n });\n}\n\nfunction subscribeCapabilities(listener: Listener): () => void {\n initCapabilitiesStore();\n _capabilitiesListeners.add(listener);\n return () => _capabilitiesListeners.delete(listener);\n}\n\nfunction getCapabilitiesSnapshot(): Capabilities | null {\n return _capabilities;\n}\n\n// ---------------------------------------------------------------------------\n// Per-URI library-state store\n// ---------------------------------------------------------------------------\n\ninterface LibraryStore {\n state: LibraryState | null;\n listeners: Set<Listener>;\n initialised: boolean;\n}\n\nconst _libraryStores = new Map<string, LibraryStore>();\n\nfunction getOrCreateLibraryStore(uri: SpotifyURIType): LibraryStore {\n const key = String(uri);\n let store = _libraryStores.get(key);\n if (!store) {\n store = { state: null, listeners: new Set(), initialised: false };\n _libraryStores.set(key, store);\n }\n return store;\n}\n\nfunction initLibraryStore(uri: SpotifyURIType) {\n const store = getOrCreateLibraryStore(uri);\n if (store.initialised) return;\n store.initialised = true;\n\n User.getLibraryState(uri)\n .then((state) => {\n store.state = state;\n store.listeners.forEach((l) => l());\n })\n .catch(() => {\n // Not connected / unavailable yet; listener updates can still arrive later.\n });\n\n User.addLibraryStateListener(uri, (state) => {\n const next = getOrCreateLibraryStore(uri);\n next.state = state;\n next.listeners.forEach((l) => l());\n });\n}\n\nfunction subscribeLibraryState(uri: SpotifyURIType, listener: Listener): () => void {\n const key = String(uri);\n initLibraryStore(uri);\n const store = getOrCreateLibraryStore(uri);\n store.listeners.add(listener);\n return () => {\n const current = _libraryStores.get(key);\n if (!current) return;\n current.listeners.delete(listener);\n };\n}\n\nfunction getLibrarySnapshot(uri: SpotifyURIType): LibraryState | null {\n return _libraryStores.get(String(uri))?.state ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Public hooks\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current Spotify OAuth session, or `null` when not authenticated.\n * Updates automatically whenever `Auth.authenticate()` or `Auth.refresh()`\n * resolves, and on session failure.\n *\n * Built on `useSyncExternalStore` for tearing-free React rendering.\n */\nexport function useSession(): SpotifySession | null {\n return useSyncExternalStore(subscribeSession, getSessionSnapshot, getSessionSnapshot);\n}\n\n/**\n * Returns the current App Remote {@link ConnectionState}\n * (`\"disconnected\"` | `\"connecting\"` | `\"connected\"`). Updates automatically\n * on every state transition driven by `AppRemote.connect()` and `disconnect()`.\n *\n * Built on `useSyncExternalStore` for tearing-free React rendering.\n *\n * @example\n * ```tsx\n * function ConnectionBanner() {\n * const state = useConnectionState();\n * return <Text>{state === \"connected\" ? \"Connected\" : \"Disconnected\"}</Text>;\n * }\n * ```\n */\nexport function useConnectionState(): ConnectionState {\n return useSyncExternalStore(\n subscribeConnectionState,\n getConnectionSnapshot,\n getConnectionSnapshot,\n );\n}\n\n/**\n * Returns the latest {@link PlayerState} from the Spotify app, or `null`\n * before the first update arrives (i.e., before `AppRemote.connect()` resolves\n * and the native subscription emits its first event).\n *\n * Updates on every state change reported by the Spotify app (track change,\n * pause/resume, seek, shuffle/repeat toggle, etc.).\n *\n * Built on `useSyncExternalStore` for tearing-free React rendering.\n *\n * @example\n * ```tsx\n * function NowPlaying() {\n * const state = usePlayerState();\n * if (!state) return <Text>Not playing</Text>;\n * return <Text>{state.track.name} — {state.isPaused ? \"Paused\" : \"Playing\"}</Text>;\n * }\n * ```\n */\nexport function usePlayerState(): PlayerState | null {\n return useSyncExternalStore(subscribePlayerState, getPlayerSnapshot, getPlayerSnapshot);\n}\n\n/**\n * Returns the currently playing {@link Track}, or `null` when nothing is\n * playing or before the first state update arrives.\n *\n * Derived from `usePlayerState`.\n */\nexport function useCurrentTrack(): Track | null {\n return usePlayerState()?.track ?? null;\n}\n\n/**\n * Returns `true` when the Spotify player is actively playing (not paused),\n * and `false` otherwise (including before the first state update arrives).\n *\n * Derived from `usePlayerState`.\n */\nexport function useIsPlaying(): boolean {\n const state = usePlayerState();\n return state !== null && !state.isPaused;\n}\n\n/**\n * Returns the current playback position in milliseconds. Returns `0` before\n * the first state update arrives.\n *\n * **Note:** This value updates whenever the native side emits a player state\n * change (track transitions, pause/resume, seek, etc.) — not on a fixed timer.\n * For a progress bar that ticks smoothly, combine this with a local `Date.now`\n * offset and `requestAnimationFrame`.\n *\n * Derived from `usePlayerState`.\n */\nexport function usePlaybackPosition(): number {\n return usePlayerState()?.playbackPosition ?? 0;\n}\n\n/**\n * Returns the latest Spotify user capabilities, or `null` before the first\n * snapshot arrives.\n *\n * Derived from `User.getCapabilities()` + `User.addListener(\"capabilitiesChange\")`.\n */\nexport function useCapabilities(): Capabilities | null {\n return useSyncExternalStore(\n subscribeCapabilities,\n getCapabilitiesSnapshot,\n getCapabilitiesSnapshot,\n );\n}\n\n/**\n * Returns the library state for a specific URI, or `null` before the first\n * snapshot arrives.\n *\n * Derived from `User.getLibraryState(uri)` + `User.addLibraryStateListener(uri, ...)`.\n */\nexport function useLibraryState(uri: SpotifyURIType): LibraryState | null {\n return useSyncExternalStore(\n (listener) => subscribeLibraryState(uri, listener),\n () => getLibrarySnapshot(uri),\n () => getLibrarySnapshot(uri),\n );\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { SpotifyError } from "../error";
2
+ export type ImagesErrorCode = "NOT_CONNECTED" | "INVALID_URI" | "IMAGE_LOAD_FAILED" | "UNKNOWN";
3
+ export declare class ImagesError extends SpotifyError {
4
+ readonly namespace: "Images";
5
+ readonly code: ImagesErrorCode;
6
+ constructor(code: ImagesErrorCode, message: string);
7
+ }
8
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/images/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,aAAa,GACb,mBAAmB,GACnB,SAAS,CAAC;AAEd,qBAAa,WAAY,SAAQ,YAAY;IAC3C,QAAQ,CAAC,SAAS,EAAG,QAAQ,CAAU;IACvC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBAEnB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM;CAInD"}
@@ -0,0 +1,10 @@
1
+ import { SpotifyError } from "../error";
2
+ export class ImagesError extends SpotifyError {
3
+ namespace = "Images";
4
+ code;
5
+ constructor(code, message) {
6
+ super(message);
7
+ this.code = code;
8
+ }
9
+ }
10
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/images/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAQxC,MAAM,OAAO,WAAY,SAAQ,YAAY;IAClC,SAAS,GAAG,QAAiB,CAAC;IAC9B,IAAI,CAAkB;IAE/B,YAAY,IAAqB,EAAE,OAAe;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF","sourcesContent":["import { SpotifyError } from \"../error\";\n\nexport type ImagesErrorCode =\n | \"NOT_CONNECTED\"\n | \"INVALID_URI\"\n | \"IMAGE_LOAD_FAILED\"\n | \"UNKNOWN\";\n\nexport class ImagesError extends SpotifyError {\n readonly namespace = \"Images\" as const;\n readonly code: ImagesErrorCode;\n\n constructor(code: ImagesErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n"]}
@@ -0,0 +1,36 @@
1
+ export type { ImagesErrorCode } from "./error";
2
+ export { ImagesError } from "./error";
3
+ import type { ContentItem } from "../content";
4
+ import type { Track } from "../player";
5
+ export type ImageSize = "small" | "medium" | "large";
6
+ export interface ImageResult {
7
+ uri: string;
8
+ }
9
+ /** Minimal shape required to fetch an image by identifier. */
10
+ interface HasImageIdentifier {
11
+ imageIdentifier?: string | null;
12
+ }
13
+ /** Minimal album/artist representation for image loading. */
14
+ interface BasicImageEntity {
15
+ imageIdentifier?: string | null;
16
+ }
17
+ export type ImageRepresentable = Track | ContentItem | BasicImageEntity | HasImageIdentifier;
18
+ /**
19
+ * Spotify Images namespace. Fetches cover art for tracks, albums, artists,
20
+ * and content items via the App Remote SDK, writing the bitmap to a temp file
21
+ * and returning its local URI. Requires `AppRemote.connect()` to be resolved.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { Images } from "@wwdrew/expo-spotify-sdk";
26
+ *
27
+ * const { uri } = await Images.load(track, "large");
28
+ * ```
29
+ */
30
+ export declare const Images: {
31
+ /**
32
+ * Loads a Spotify image and returns a local file URI.
33
+ */
34
+ readonly load: (item: ImageRepresentable, size: ImageSize) => Promise<ImageResult>;
35
+ };
36
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/images/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAOtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGvC,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8DAA8D;AAC9D,UAAU,kBAAkB;IAC1B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,6DAA6D;AAC7D,UAAU,gBAAgB;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,WAAW,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;AAwC7F;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM;IACjB;;OAEG;0BACQ,kBAAkB,QAAQ,SAAS,KAAG,OAAO,CAAC,WAAW,CAAC;CAM7D,CAAC"}
@@ -0,0 +1,59 @@
1
+ export { ImagesError } from "./error";
2
+ // ---------------------------------------------------------------------------
3
+ // Types
4
+ // ---------------------------------------------------------------------------
5
+ import ExpoSpotifySDKModule from "../ExpoSpotifySDKModule";
6
+ import { ImagesError } from "./error";
7
+ const VALID_IMAGE_CODES = new Set([
8
+ "NOT_CONNECTED",
9
+ "INVALID_URI",
10
+ "IMAGE_LOAD_FAILED",
11
+ "UNKNOWN",
12
+ ]);
13
+ const CAUSE_SEPARATOR = "→ Caused by: ";
14
+ function unwrapReason(message) {
15
+ const idx = message.lastIndexOf(CAUSE_SEPARATOR);
16
+ return idx === -1 ? message : message.slice(idx + CAUSE_SEPARATOR.length);
17
+ }
18
+ function rethrowAsImagesError(err) {
19
+ if (err instanceof ImagesError)
20
+ throw err;
21
+ if (err instanceof Error) {
22
+ const reason = unwrapReason(err.message);
23
+ const maybeCode = err.code;
24
+ if (maybeCode && VALID_IMAGE_CODES.has(maybeCode)) {
25
+ throw new ImagesError(maybeCode, reason);
26
+ }
27
+ throw new ImagesError("UNKNOWN", reason);
28
+ }
29
+ throw new ImagesError("UNKNOWN", String(err));
30
+ }
31
+ function getImageIdentifier(item) {
32
+ const value = item?.imageIdentifier;
33
+ if (!value || !value.trim()) {
34
+ throw new ImagesError("INVALID_URI", "Images.load(): item does not contain a valid imageIdentifier");
35
+ }
36
+ return value;
37
+ }
38
+ /**
39
+ * Spotify Images namespace. Fetches cover art for tracks, albums, artists,
40
+ * and content items via the App Remote SDK, writing the bitmap to a temp file
41
+ * and returning its local URI. Requires `AppRemote.connect()` to be resolved.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { Images } from "@wwdrew/expo-spotify-sdk";
46
+ *
47
+ * const { uri } = await Images.load(track, "large");
48
+ * ```
49
+ */
50
+ export const Images = {
51
+ /**
52
+ * Loads a Spotify image and returns a local file URI.
53
+ */
54
+ load(item, size) {
55
+ const imageIdentifier = getImageIdentifier(item);
56
+ return ExpoSpotifySDKModule.imagesLoad(imageIdentifier, size).catch(rethrowAsImagesError);
57
+ },
58
+ };
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/images/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AAoBvD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAkB;IACjD,eAAe;IACf,aAAa;IACb,mBAAmB;IACnB,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,eAAe,CAAC;AAExC,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IACjD,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,GAAG,YAAY,WAAW;QAAE,MAAM,GAAG,CAAC;IAC1C,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,SAAS,GAAI,GAAiC,CAAC,IAAI,CAAC;QAC1D,IAAI,SAAS,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAA4B,CAAC,EAAE,CAAC;YACrE,MAAM,IAAI,WAAW,CAAC,SAA4B,EAAE,MAAM,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAwB;IAClD,MAAM,KAAK,GAAG,IAAI,EAAE,eAAe,CAAC;IACpC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,WAAW,CACnB,aAAa,EACb,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB;;OAEG;IACH,IAAI,CAAC,IAAwB,EAAE,IAAe;QAC5C,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACjD,OAAO,oBAAoB,CAAC,UAAU,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,KAAK,CACjE,oBAAoB,CACrB,CAAC;IACJ,CAAC;CACO,CAAC","sourcesContent":["export type { ImagesErrorCode } from \"./error\";\nexport { ImagesError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport type { ContentItem } from \"../content\";\nimport type { Track } from \"../player\";\nimport { ImagesError, ImagesErrorCode } from \"./error\";\n\nexport type ImageSize = \"small\" | \"medium\" | \"large\";\n\nexport interface ImageResult {\n uri: string;\n}\n\n/** Minimal shape required to fetch an image by identifier. */\ninterface HasImageIdentifier {\n imageIdentifier?: string | null;\n}\n\n/** Minimal album/artist representation for image loading. */\ninterface BasicImageEntity {\n imageIdentifier?: string | null;\n}\n\nexport type ImageRepresentable = Track | ContentItem | BasicImageEntity | HasImageIdentifier;\n\nconst VALID_IMAGE_CODES = new Set<ImagesErrorCode>([\n \"NOT_CONNECTED\",\n \"INVALID_URI\",\n \"IMAGE_LOAD_FAILED\",\n \"UNKNOWN\",\n]);\n\nconst CAUSE_SEPARATOR = \"→ Caused by: \";\n\nfunction unwrapReason(message: string): string {\n const idx = message.lastIndexOf(CAUSE_SEPARATOR);\n return idx === -1 ? message : message.slice(idx + CAUSE_SEPARATOR.length);\n}\n\nfunction rethrowAsImagesError(err: unknown): never {\n if (err instanceof ImagesError) throw err;\n if (err instanceof Error) {\n const reason = unwrapReason(err.message);\n const maybeCode = (err as Error & { code?: string }).code;\n if (maybeCode && VALID_IMAGE_CODES.has(maybeCode as ImagesErrorCode)) {\n throw new ImagesError(maybeCode as ImagesErrorCode, reason);\n }\n throw new ImagesError(\"UNKNOWN\", reason);\n }\n throw new ImagesError(\"UNKNOWN\", String(err));\n}\n\nfunction getImageIdentifier(item: ImageRepresentable): string {\n const value = item?.imageIdentifier;\n if (!value || !value.trim()) {\n throw new ImagesError(\n \"INVALID_URI\",\n \"Images.load(): item does not contain a valid imageIdentifier\",\n );\n }\n return value;\n}\n\n/**\n * Spotify Images namespace. Fetches cover art for tracks, albums, artists,\n * and content items via the App Remote SDK, writing the bitmap to a temp file\n * and returning its local URI. Requires `AppRemote.connect()` to be resolved.\n *\n * @example\n * ```ts\n * import { Images } from \"@wwdrew/expo-spotify-sdk\";\n *\n * const { uri } = await Images.load(track, \"large\");\n * ```\n */\nexport const Images = {\n /**\n * Loads a Spotify image and returns a local file URI.\n */\n load(item: ImageRepresentable, size: ImageSize): Promise<ImageResult> {\n const imageIdentifier = getImageIdentifier(item);\n return ExpoSpotifySDKModule.imagesLoad(imageIdentifier, size).catch(\n rethrowAsImagesError,\n );\n },\n} as const;\n"]}
package/build/index.d.ts CHANGED
@@ -1,59 +1,47 @@
1
- import { EventSubscription } from "expo-modules-core";
2
- import { SpotifyConfig, SpotifyError, SpotifyRefreshConfig, SpotifySession, SpotifySessionChangeEvent } from "./ExpoSpotifySDK.types";
1
+ export { useSession, useConnectionState, usePlayerState, useCurrentTrack, useIsPlaying, usePlaybackPosition, useCapabilities, useLibraryState, } from "./hooks";
2
+ export { Auth } from "./auth";
3
+ export { AppRemote } from "./app-remote";
4
+ export { Player } from "./player";
5
+ export { User } from "./user";
6
+ export { Content } from "./content";
7
+ export { Images } from "./images";
8
+ export { SpotifyError } from "./error";
9
+ export { AuthError } from "./auth";
10
+ export { AppRemoteError } from "./app-remote";
11
+ export { PlayerError } from "./player";
12
+ export { UserError } from "./user";
13
+ export { ContentError } from "./content";
14
+ export { ImagesError } from "./images";
15
+ export type { AuthErrorCode } from "./auth";
16
+ export type { AppRemoteErrorCode } from "./app-remote";
17
+ export type { PlayerErrorCode } from "./player";
18
+ export type { UserErrorCode } from "./user";
19
+ export type { ContentErrorCode } from "./content";
20
+ export type { ImagesErrorCode } from "./images";
21
+ export { SpotifyURI } from "./uri";
22
+ export type { SpotifyResourceType, SpotifyURI as SpotifyURIType } from "./uri";
23
+ export type { SpotifySession, SpotifyScope, AuthenticateConfig, RefreshConfig, SessionChangeEvent, } from "./auth";
24
+ export type { ConnectionState, ConnectionStateChangeEvent, ConnectionErrorEvent, } from "./app-remote";
25
+ export type { RepeatMode, PodcastPlaybackSpeed, Artist, Album, Track, PlaybackOptions, PlaybackRestrictions, PlayerState, CrossfadeState, } from "./player";
26
+ export type { Capabilities, LibraryState } from "./user";
27
+ export type { ContentType, ContentItem } from "./content";
28
+ export type { ImageSize, ImageResult, ImageRepresentable } from "./images";
29
+ import { Auth } from "./auth";
30
+ import type { AuthenticateConfig as SpotifyConfig, RefreshConfig as SpotifyRefreshConfig } from "./auth";
31
+ /** @deprecated Use `Auth.isAvailable()` */
32
+ export declare function isAvailable(): boolean;
33
+ /** @deprecated Use `Auth.authenticate(config)` */
34
+ export declare function authenticateAsync(config: SpotifyConfig): Promise<import("./auth").SpotifySession>;
35
+ /** @deprecated Use `Auth.cancelPending()` */
36
+ export declare function cancelPendingAuthAsync(): Promise<void>;
37
+ /** @deprecated Use `Auth.refresh(config)` */
38
+ export declare function refreshSessionAsync(config: SpotifyRefreshConfig): Promise<import("./auth").SpotifySession>;
39
+ /** @deprecated Use `Auth.addListener("sessionChange", cb)` */
40
+ export declare function addSessionChangeListener(listener: Parameters<typeof Auth.addListener>[1]): import("expo-modules-core").EventSubscription;
41
+ export type { SpotifyConfig, SpotifyRefreshConfig };
3
42
  /**
4
- * Returns `true` if the Spotify app is installed on the device.
5
- * Always returns `false` on web.
43
+ * @deprecated `SpotifyErrorCode` is now `AuthErrorCode`. Import via:
44
+ * `import type { AuthErrorCode } from "@wwdrew/expo-spotify-sdk"`
6
45
  */
7
- declare function isAvailable(): boolean;
8
- /**
9
- * Starts a Spotify OAuth flow. Resolves with a {@link SpotifySession};
10
- * rejects with a {@link SpotifyError} carrying a `code`.
11
- */
12
- declare function authenticateAsync(config: SpotifyConfig): Promise<SpotifySession>;
13
- /**
14
- * Forcibly cancel any in-flight `authenticateAsync` call. Resolves once the
15
- * native coordinator's pending continuation has been cleared. No-op when
16
- * nothing is in flight, and a no-op on Android (the Android coordinator
17
- * self-cleans via structured concurrency).
18
- *
19
- * Recovery hatch for the iOS coordinator's stuck-state class of bugs: the
20
- * SPTSessionManager delegate callbacks are not guaranteed to fire — e.g. when
21
- * Spotify never redirects back to the host app — leaving the coordinator's
22
- * `pending` continuation set forever and every subsequent `authenticateAsync`
23
- * rejecting with `AUTH_IN_PROGRESS` until the process restarts. Call this
24
- * before `authenticateAsync` to defensively clear any leaked state.
25
- */
26
- declare function cancelPendingAuthAsync(): Promise<void>;
27
- /**
28
- * Exchanges a refresh token for a new access token via your token refresh
29
- * server. Resolves with a fresh {@link SpotifySession}; rejects with a
30
- * {@link SpotifyError}.
31
- */
32
- declare function refreshSessionAsync(config: SpotifyRefreshConfig): Promise<SpotifySession>;
33
- /**
34
- * Subscribes to session lifecycle events emitted by the native module.
35
- *
36
- * Events are fired for every `authenticateAsync` and `refreshSessionAsync`
37
- * call, regardless of whether the call was awaited. Useful for persisting
38
- * tokens in a central store without coupling the store to the call sites.
39
- *
40
- * Returns a {@link Subscription} — call `.remove()` to unsubscribe.
41
- *
42
- * @example
43
- * ```ts
44
- * const sub = addSessionChangeListener((event) => {
45
- * if (event.type === "didInitiate" || event.type === "didRenew") {
46
- * store.setSession(event.session);
47
- * }
48
- * });
49
- * // later:
50
- * sub.remove();
51
- * ```
52
- */
53
- declare function addSessionChangeListener(listener: (event: SpotifySessionChangeEvent) => void): EventSubscription;
54
- declare const Authenticate: {
55
- authenticateAsync: typeof authenticateAsync;
56
- };
57
- export { isAvailable, authenticateAsync, cancelPendingAuthAsync, refreshSessionAsync, addSessionChangeListener, Authenticate, SpotifyError, };
58
- export type { SpotifyConfig, SpotifyRefreshConfig, SpotifySession, SpotifySessionChangeEvent, SpotifyErrorCode, SpotifyScope, } from "./ExpoSpotifySDK.types";
46
+ export type { AuthErrorCode as SpotifyErrorCode } from "./auth";
59
47
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAY,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EACL,aAAa,EACb,YAAY,EAEZ,oBAAoB,EACpB,cAAc,EACd,yBAAyB,EAC1B,MAAM,wBAAwB,CAAC;AAWhC;;;GAGG;AACH,iBAAS,WAAW,IAAI,OAAO,CAE9B;AAED;;;GAGG;AACH,iBAAS,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAqBzE;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAK/C;AAgCD;;;;GAIG;AACH,iBAAS,mBAAmB,CAC1B,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,cAAc,CAAC,CAczB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,wBAAwB,CAC/B,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,GACnD,iBAAiB,CAKnB;AAgCD,QAAA,MAAM,YAAY;;CAEjB,CAAC;AAEF,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACZ,YAAY,GACb,CAAC;AACF,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,YAAY,GACb,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,SAAS,CAAC;AAMjB,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAMlC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAMvC,YAAY,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAMhD,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,YAAY,EAAE,mBAAmB,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,OAAO,CAAC;AAM/E,YAAY,EACV,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,kBAAkB,GACnB,MAAM,QAAQ,CAAC;AAMhB,YAAY,EACV,eAAe,EACf,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAMtB,YAAY,EACV,UAAU,EACV,oBAAoB,EACpB,MAAM,EACN,KAAK,EACL,KAAK,EACL,eAAe,EACf,oBAAoB,EACpB,WAAW,EACX,cAAc,GACf,MAAM,UAAU,CAAC;AAMlB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMzD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAM1D,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAS3E,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,KAAK,EACV,kBAAkB,IAAI,aAAa,EACnC,aAAa,IAAI,oBAAoB,EACtC,MAAM,QAAQ,CAAC;AAEhB,2CAA2C;AAC3C,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,kDAAkD;AAClD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,4CAEtD;AAED,6CAA6C;AAC7C,wBAAgB,sBAAsB,kBAErC;AAED,6CAA6C;AAC7C,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB,4CAE/D;AAED,8DAA8D;AAC9D,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,iDAGjD;AAID,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,CAAC;AAEpD;;;GAGG;AACH,YAAY,EAAE,aAAa,IAAI,gBAAgB,EAAE,MAAM,QAAQ,CAAC"}