@wwdrew/expo-spotify-sdk 2.0.0 → 2.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 (49) hide show
  1. package/README.md +62 -344
  2. package/android/src/main/java/expo/modules/spotifysdk/ExpoSpotifySDKModule.kt +3 -5
  3. package/android/src/main/java/expo/modules/spotifysdk/SpotifyAppRemoteCoordinator.kt +6 -64
  4. package/android/src/main/java/expo/modules/spotifysdk/SpotifyAppRemoteErrorMapping.kt +106 -0
  5. package/android/src/main/java/expo/modules/spotifysdk/SpotifyTokenSwapClient.kt +2 -7
  6. package/build/app-remote/index.d.ts.map +1 -1
  7. package/build/app-remote/index.js +11 -24
  8. package/build/app-remote/index.js.map +1 -1
  9. package/build/auth/index.d.ts.map +1 -1
  10. package/build/auth/index.js +17 -34
  11. package/build/auth/index.js.map +1 -1
  12. package/build/content/index.d.ts.map +1 -1
  13. package/build/content/index.js +12 -25
  14. package/build/content/index.js.map +1 -1
  15. package/build/hooks/index.d.ts +1 -1
  16. package/build/hooks/index.d.ts.map +1 -1
  17. package/build/hooks/index.js +38 -115
  18. package/build/hooks/index.js.map +1 -1
  19. package/build/images/index.d.ts +2 -2
  20. package/build/images/index.d.ts.map +1 -1
  21. package/build/images/index.js +12 -25
  22. package/build/images/index.js.map +1 -1
  23. package/build/index.d.ts +0 -18
  24. package/build/index.d.ts.map +1 -1
  25. package/build/index.js +0 -27
  26. package/build/index.js.map +1 -1
  27. package/build/internal/native-errors.d.ts +14 -0
  28. package/build/internal/native-errors.d.ts.map +1 -0
  29. package/build/internal/native-errors.js +29 -0
  30. package/build/internal/native-errors.js.map +1 -0
  31. package/build/internal/sync-external-store.d.ts +14 -0
  32. package/build/internal/sync-external-store.d.ts.map +1 -0
  33. package/build/internal/sync-external-store.js +36 -0
  34. package/build/internal/sync-external-store.js.map +1 -0
  35. package/build/player/index.d.ts.map +1 -1
  36. package/build/player/index.js +14 -27
  37. package/build/player/index.js.map +1 -1
  38. package/build/user/index.d.ts +2 -2
  39. package/build/user/index.d.ts.map +1 -1
  40. package/build/user/index.js +16 -26
  41. package/build/user/index.js.map +1 -1
  42. package/ios/ExpoSpotifySDKModule.swift +1 -1
  43. package/ios/SpotifyAppRemoteCoordinator.swift +29 -404
  44. package/ios/SpotifyAppRemoteErrorMapping.swift +110 -0
  45. package/ios/SpotifyAppRemoteErrors.swift +221 -0
  46. package/ios/SpotifyAppRemoteMappers.swift +88 -0
  47. package/package.json +4 -2
  48. package/plugin/index.d.ts +2 -0
  49. package/plugin/index.js +1 -0
@@ -1,80 +1,36 @@
1
1
  import { useSyncExternalStore } from "react";
2
2
  import { AppRemote } from "../app-remote";
3
3
  import { Auth } from "../auth";
4
+ import { createSyncExternalStore } from "../internal/sync-external-store";
4
5
  import { Player } from "../player";
5
6
  import { User } from "../user";
6
7
  // ---------------------------------------------------------------------------
7
8
  // 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
9
  // ---------------------------------------------------------------------------
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.
10
+ const connectionStore = createSyncExternalStore("disconnected", (store) => {
21
11
  AppRemote.getConnectionState().then((state) => {
22
- if (state !== _connectionState) {
23
- _connectionState = state;
24
- _connectionListeners.forEach((l) => l());
25
- }
12
+ store.update(state);
26
13
  });
27
14
  AppRemote.addListener("connectionStateChange", ({ state }) => {
28
- _connectionState = state;
29
- _connectionListeners.forEach((l) => l());
15
+ store.update(state);
30
16
  });
31
- }
32
- function subscribeConnectionState(listener) {
33
- initConnectionStore();
34
- _connectionListeners.add(listener);
35
- return () => _connectionListeners.delete(listener);
36
- }
37
- function getConnectionSnapshot() {
38
- return _connectionState;
39
- }
17
+ });
40
18
  // ---------------------------------------------------------------------------
41
19
  // Session store
42
20
  // ---------------------------------------------------------------------------
43
- let _session = null;
44
- const _sessionListeners = new Set();
45
- let _sessionStoreInitialised = false;
46
- function initSessionStore() {
47
- if (_sessionStoreInitialised)
48
- return;
49
- _sessionStoreInitialised = true;
21
+ const sessionStore = createSyncExternalStore(null, (store) => {
50
22
  Auth.addListener("sessionChange", (event) => {
51
23
  if (event.type === "didInitiate" || event.type === "didRenew") {
52
- _session = event.session;
24
+ store.update(event.session);
53
25
  }
54
26
  else {
55
- _session = null;
27
+ store.update(null);
56
28
  }
57
- _sessionListeners.forEach((l) => l());
58
29
  });
59
- }
60
- function subscribeSession(listener) {
61
- initSessionStore();
62
- _sessionListeners.add(listener);
63
- return () => _sessionListeners.delete(listener);
64
- }
65
- function getSessionSnapshot() {
66
- return _session;
67
- }
30
+ });
68
31
  // ---------------------------------------------------------------------------
69
32
  // 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
33
  // ---------------------------------------------------------------------------
75
- let _playerState = null;
76
- const _playerListeners = new Set();
77
- let _playerStoreInitialised = false;
78
34
  function normalizePlayerState(nextState, previousState) {
79
35
  const nextName = nextState.track.name?.trim() ?? "";
80
36
  const previousName = previousState?.track.name?.trim() ?? "";
@@ -93,95 +49,62 @@ function normalizePlayerState(nextState, previousState) {
93
49
  }
94
50
  return nextState;
95
51
  }
96
- function notifyPlayerListeners() {
97
- _playerListeners.forEach((l) => l());
98
- }
99
- async function hydratePlayerState() {
52
+ let playerHydrationVersion = 0;
53
+ async function hydratePlayerState(store, version) {
100
54
  try {
101
55
  const state = await Player.getPlayerState();
102
- _playerState = normalizePlayerState(state, _playerState);
103
- notifyPlayerListeners();
56
+ if (version !== playerHydrationVersion)
57
+ return;
58
+ store.update((previous) => normalizePlayerState(state, previous));
104
59
  }
105
60
  catch {
106
61
  // Ignore one-shot hydration failures (e.g., connection races). Event stream
107
62
  // updates still populate this store once available.
108
63
  }
109
64
  }
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.
65
+ const playerStore = createSyncExternalStore(null, (store) => {
116
66
  AppRemote.getConnectionState().then((state) => {
117
67
  if (state === "connected") {
118
- void hydratePlayerState();
68
+ const version = ++playerHydrationVersion;
69
+ hydratePlayerState(store, version).catch(() => { });
119
70
  }
120
71
  });
121
- // Refresh the one-shot snapshot whenever App Remote reconnects, and clear on
122
- // disconnect so stale "now playing" data is not retained.
123
72
  AppRemote.addListener("connectionStateChange", ({ state }) => {
124
73
  if (state === "connected") {
125
- void hydratePlayerState();
74
+ const version = ++playerHydrationVersion;
75
+ hydratePlayerState(store, version).catch(() => { });
126
76
  return;
127
77
  }
128
- if (_playerState !== null) {
129
- _playerState = null;
130
- notifyPlayerListeners();
131
- }
78
+ ++playerHydrationVersion;
79
+ store.update((current) => (current === null ? current : null));
132
80
  });
133
81
  Player.addListener("playerStateChange", (state) => {
134
- _playerState = normalizePlayerState(state, _playerState);
135
- notifyPlayerListeners();
82
+ store.update((previous) => normalizePlayerState(state, previous));
136
83
  });
137
- }
138
- function subscribePlayerState(listener) {
139
- initPlayerStore();
140
- _playerListeners.add(listener);
141
- return () => _playerListeners.delete(listener);
142
- }
143
- function getPlayerSnapshot() {
144
- return _playerState;
145
- }
84
+ });
146
85
  // ---------------------------------------------------------------------------
147
86
  // Capabilities store
148
87
  // ---------------------------------------------------------------------------
149
- let _capabilities = null;
150
- const _capabilitiesListeners = new Set();
151
- let _capabilitiesStoreInitialised = false;
152
- function initCapabilitiesStore() {
153
- if (_capabilitiesStoreInitialised)
154
- return;
155
- _capabilitiesStoreInitialised = true;
88
+ const capabilitiesStore = createSyncExternalStore(null, (store) => {
156
89
  User.getCapabilities()
157
90
  .then((capabilities) => {
158
- _capabilities = capabilities;
159
- _capabilitiesListeners.forEach((l) => l());
91
+ store.update(capabilities);
160
92
  })
161
93
  .catch(() => {
162
94
  // Swallow initial read failures (e.g., not connected yet). The event
163
95
  // stream will hydrate this later.
164
96
  });
165
97
  User.addListener("capabilitiesChange", (capabilities) => {
166
- _capabilities = capabilities;
167
- _capabilitiesListeners.forEach((l) => l());
98
+ store.update(capabilities);
168
99
  });
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();
100
+ });
101
+ const libraryStores = new Map();
179
102
  function getOrCreateLibraryStore(uri) {
180
103
  const key = String(uri);
181
- let store = _libraryStores.get(key);
104
+ let store = libraryStores.get(key);
182
105
  if (!store) {
183
106
  store = { state: null, listeners: new Set(), initialised: false };
184
- _libraryStores.set(key, store);
107
+ libraryStores.set(key, store);
185
108
  }
186
109
  return store;
187
110
  }
@@ -193,7 +116,7 @@ function initLibraryStore(uri) {
193
116
  User.getLibraryState(uri)
194
117
  .then((state) => {
195
118
  store.state = state;
196
- store.listeners.forEach((l) => l());
119
+ store.listeners.forEach((listener) => listener());
197
120
  })
198
121
  .catch(() => {
199
122
  // Not connected / unavailable yet; listener updates can still arrive later.
@@ -201,7 +124,7 @@ function initLibraryStore(uri) {
201
124
  User.addLibraryStateListener(uri, (state) => {
202
125
  const next = getOrCreateLibraryStore(uri);
203
126
  next.state = state;
204
- next.listeners.forEach((l) => l());
127
+ next.listeners.forEach((listener) => listener());
205
128
  });
206
129
  }
207
130
  function subscribeLibraryState(uri, listener) {
@@ -210,14 +133,14 @@ function subscribeLibraryState(uri, listener) {
210
133
  const store = getOrCreateLibraryStore(uri);
211
134
  store.listeners.add(listener);
212
135
  return () => {
213
- const current = _libraryStores.get(key);
136
+ const current = libraryStores.get(key);
214
137
  if (!current)
215
138
  return;
216
139
  current.listeners.delete(listener);
217
140
  };
218
141
  }
219
142
  function getLibrarySnapshot(uri) {
220
- return _libraryStores.get(String(uri))?.state ?? null;
143
+ return libraryStores.get(String(uri))?.state ?? null;
221
144
  }
222
145
  // ---------------------------------------------------------------------------
223
146
  // Public hooks
@@ -230,7 +153,7 @@ function getLibrarySnapshot(uri) {
230
153
  * Built on `useSyncExternalStore` for tearing-free React rendering.
231
154
  */
232
155
  export function useSession() {
233
- return useSyncExternalStore(subscribeSession, getSessionSnapshot, getSessionSnapshot);
156
+ return useSyncExternalStore(sessionStore.subscribe, sessionStore.getSnapshot, sessionStore.getSnapshot);
234
157
  }
235
158
  /**
236
159
  * Returns the current App Remote {@link ConnectionState}
@@ -248,7 +171,7 @@ export function useSession() {
248
171
  * ```
249
172
  */
250
173
  export function useConnectionState() {
251
- return useSyncExternalStore(subscribeConnectionState, getConnectionSnapshot, getConnectionSnapshot);
174
+ return useSyncExternalStore(connectionStore.subscribe, connectionStore.getSnapshot, connectionStore.getSnapshot);
252
175
  }
253
176
  /**
254
177
  * Returns the latest {@link PlayerState} from the Spotify app, or `null`
@@ -270,7 +193,7 @@ export function useConnectionState() {
270
193
  * ```
271
194
  */
272
195
  export function usePlayerState() {
273
- return useSyncExternalStore(subscribePlayerState, getPlayerSnapshot, getPlayerSnapshot);
196
+ return useSyncExternalStore(playerStore.subscribe, playerStore.getSnapshot, playerStore.getSnapshot);
274
197
  }
275
198
  /**
276
199
  * Returns the currently playing {@link Track}, or `null` when nothing is
@@ -312,7 +235,7 @@ export function usePlaybackPosition() {
312
235
  * Derived from `User.getCapabilities()` + `User.addListener("capabilitiesChange")`.
313
236
  */
314
237
  export function useCapabilities() {
315
- return useSyncExternalStore(subscribeCapabilities, getCapabilitiesSnapshot, getCapabilitiesSnapshot);
238
+ return useSyncExternalStore(capabilitiesStore.subscribe, capabilitiesStore.getSnapshot, capabilitiesStore.getSnapshot);
316
239
  }
317
240
  /**
318
241
  * Returns the library state for a specific URI, or `null` before the first
@@ -1 +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, type ConnectionState } from \"../app-remote\";\nimport { Auth, type SpotifySession } from \"../auth\";\nimport { Player, type PlayerState, type Track } from \"../player\";\nimport { type Capabilities, type 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"]}
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, type ConnectionState } from \"../app-remote\";\nimport { Auth, type SpotifySession } from \"../auth\";\nimport { createSyncExternalStore } from \"../internal/sync-external-store\";\nimport { Player, type PlayerState, type Track } from \"../player\";\nimport type { SpotifyURI as SpotifyURIType } from \"../uri\";\nimport { type Capabilities, type LibraryState, User } from \"../user\";\n\ntype Listener = () => void;\n\n// ---------------------------------------------------------------------------\n// Connection-state store\n// ---------------------------------------------------------------------------\n\nconst connectionStore = createSyncExternalStore<ConnectionState>(\n \"disconnected\",\n (store) => {\n AppRemote.getConnectionState().then((state) => {\n store.update(state);\n });\n\n AppRemote.addListener(\"connectionStateChange\", ({ state }) => {\n store.update(state);\n });\n },\n);\n\n// ---------------------------------------------------------------------------\n// Session store\n// ---------------------------------------------------------------------------\n\nconst sessionStore = createSyncExternalStore<SpotifySession | null>(\n null,\n (store) => {\n Auth.addListener(\"sessionChange\", (event) => {\n if (event.type === \"didInitiate\" || event.type === \"didRenew\") {\n store.update(event.session);\n } else {\n store.update(null);\n }\n });\n },\n);\n\n// ---------------------------------------------------------------------------\n// Player-state store\n// ---------------------------------------------------------------------------\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\nlet playerHydrationVersion = 0;\n\nasync function hydratePlayerState(\n store: ReturnType<typeof createSyncExternalStore<PlayerState | null>>,\n version: number,\n) {\n try {\n const state = await Player.getPlayerState();\n if (version !== playerHydrationVersion) return;\n store.update((previous) => normalizePlayerState(state, previous));\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\nconst playerStore = createSyncExternalStore<PlayerState | null>(\n null,\n (store) => {\n AppRemote.getConnectionState().then((state) => {\n if (state === \"connected\") {\n const version = ++playerHydrationVersion;\n hydratePlayerState(store, version).catch(() => {});\n }\n });\n\n AppRemote.addListener(\"connectionStateChange\", ({ state }) => {\n if (state === \"connected\") {\n const version = ++playerHydrationVersion;\n hydratePlayerState(store, version).catch(() => {});\n return;\n }\n\n ++playerHydrationVersion;\n store.update((current) => (current === null ? current : null));\n });\n\n Player.addListener(\"playerStateChange\", (state) => {\n store.update((previous) => normalizePlayerState(state, previous));\n });\n },\n);\n\n// ---------------------------------------------------------------------------\n// Capabilities store\n// ---------------------------------------------------------------------------\n\nconst capabilitiesStore = createSyncExternalStore<Capabilities | null>(\n null,\n (store) => {\n User.getCapabilities()\n .then((capabilities) => {\n store.update(capabilities);\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 store.update(capabilities);\n });\n },\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((listener) => listener());\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((listener) => listener());\n });\n}\n\nfunction subscribeLibraryState(\n uri: SpotifyURIType,\n listener: Listener,\n): () => 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(\n sessionStore.subscribe,\n sessionStore.getSnapshot,\n sessionStore.getSnapshot,\n );\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 connectionStore.subscribe,\n connectionStore.getSnapshot,\n connectionStore.getSnapshot,\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(\n playerStore.subscribe,\n playerStore.getSnapshot,\n playerStore.getSnapshot,\n );\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 capabilitiesStore.subscribe,\n capabilitiesStore.getSnapshot,\n capabilitiesStore.getSnapshot,\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"]}
@@ -1,7 +1,7 @@
1
- export type { ImagesErrorCode } from "./error";
2
- export { ImagesError } from "./error";
3
1
  import type { ContentItem } from "../content";
4
2
  import type { Track } from "../player";
3
+ export type { ImagesErrorCode } from "./error";
4
+ export { ImagesError } from "./error";
5
5
  export type ImageSize = "small" | "medium" | "large";
6
6
  export interface ImageResult {
7
7
  uri: string;
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/images/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGvC,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,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,GAC1B,KAAK,GACL,WAAW,GACX,gBAAgB,GAChB,kBAAkB,CAAC;AAwBvB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM;IACjB;;OAEG;0BACQ,kBAAkB,QAAQ,SAAS,KAAG,OAAO,CAAC,WAAW,CAAC;CAM7D,CAAC"}
@@ -1,33 +1,20 @@
1
- export { ImagesError } from "./error";
2
1
  // ---------------------------------------------------------------------------
3
2
  // Types
4
3
  // ---------------------------------------------------------------------------
5
4
  import ExpoSpotifySDKModule from "../ExpoSpotifySDKModule";
5
+ import { createNativeErrorRethrow } from "../internal/native-errors";
6
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
- }
7
+ export { ImagesError } from "./error";
8
+ const rethrowAsImagesError = createNativeErrorRethrow({
9
+ ErrorClass: ImagesError,
10
+ unknownCode: "UNKNOWN",
11
+ validCodes: new Set([
12
+ "NOT_CONNECTED",
13
+ "INVALID_URI",
14
+ "IMAGE_LOAD_FAILED",
15
+ "UNKNOWN",
16
+ ]),
17
+ });
31
18
  function getImageIdentifier(item) {
32
19
  const value = item?.imageIdentifier;
33
20
  if (!value || !value.trim()) {
@@ -1 +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,EAAwB,MAAM,SAAS,CAAC;AAoB5D,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, type 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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/images/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EAAE,WAAW,EAAwB,MAAM,SAAS,CAAC;AAG5D,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAwBtC,MAAM,oBAAoB,GAAG,wBAAwB,CAAC;IACpD,UAAU,EAAE,WAAW;IACvB,WAAW,EAAE,SAAS;IACtB,UAAU,EAAE,IAAI,GAAG,CAAkB;QACnC,eAAe;QACf,aAAa;QACb,mBAAmB;QACnB,SAAS;KACV,CAAC;CACH,CAAC,CAAC;AAEH,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":["// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport type { ContentItem } from \"../content\";\nimport { createNativeErrorRethrow } from \"../internal/native-errors\";\nimport type { Track } from \"../player\";\nimport { ImagesError, type ImagesErrorCode } from \"./error\";\n\nexport type { ImagesErrorCode } from \"./error\";\nexport { ImagesError } 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 =\n | Track\n | ContentItem\n | BasicImageEntity\n | HasImageIdentifier;\n\nconst rethrowAsImagesError = createNativeErrorRethrow({\n ErrorClass: ImagesError,\n unknownCode: \"UNKNOWN\",\n validCodes: new Set<ImagesErrorCode>([\n \"NOT_CONNECTED\",\n \"INVALID_URI\",\n \"IMAGE_LOAD_FAILED\",\n \"UNKNOWN\",\n ]),\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
@@ -26,22 +26,4 @@ export type { RepeatMode, PodcastPlaybackSpeed, Artist, Album, Track, PlaybackOp
26
26
  export type { Capabilities, LibraryState } from "./user";
27
27
  export type { ContentType, ContentItem } from "./content";
28
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 };
42
- /**
43
- * @deprecated `SpotifyErrorCode` is now `AuthErrorCode`. Import via:
44
- * `import type { AuthErrorCode } from "@wwdrew/expo-spotify-sdk"`
45
- */
46
- export type { AuthErrorCode as SpotifyErrorCode } from "./auth";
47
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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"}
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"}
package/build/index.js CHANGED
@@ -25,31 +25,4 @@ export { ImagesError } from "./images";
25
25
  // URI helpers
26
26
  // ---------------------------------------------------------------------------
27
27
  export { SpotifyURI } from "./uri";
28
- // ---------------------------------------------------------------------------
29
- // v0.x backward-compatible exports (deprecated — remove at v2.0.0)
30
- //
31
- // These shims let existing callers continue to compile after upgrading to v1.
32
- // Migrate to the namespaced API: see docs/V1_PLAN.md §8 (Migration).
33
- // ---------------------------------------------------------------------------
34
- import { Auth } from "./auth";
35
- /** @deprecated Use `Auth.isAvailable()` */
36
- export function isAvailable() {
37
- return Auth.isAvailable();
38
- }
39
- /** @deprecated Use `Auth.authenticate(config)` */
40
- export function authenticateAsync(config) {
41
- return Auth.authenticate(config);
42
- }
43
- /** @deprecated Use `Auth.cancelPending()` */
44
- export function cancelPendingAuthAsync() {
45
- return Auth.cancelPending();
46
- }
47
- /** @deprecated Use `Auth.refresh(config)` */
48
- export function refreshSessionAsync(config) {
49
- return Auth.refresh(config);
50
- }
51
- /** @deprecated Use `Auth.addListener("sessionChange", cb)` */
52
- export function addSessionChangeListener(listener) {
53
- return Auth.addListener("sessionChange", listener);
54
- }
55
28
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E,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;AAElC,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,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;AAavC,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AA2DnC,8EAA8E;AAC9E,mEAAmE;AACnE,EAAE;AACF,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAE9E,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAM9B,2CAA2C;AAC3C,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,sBAAsB;IACpC,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;AAC9B,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,mBAAmB,CAAC,MAA4B;IAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,wBAAwB,CACtC,QAAgD;IAEhD,OAAO,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC","sourcesContent":["// ---------------------------------------------------------------------------\n// Hooks (v1 public API)\n// ---------------------------------------------------------------------------\n\nexport {\n useSession,\n useConnectionState,\n usePlayerState,\n useCurrentTrack,\n useIsPlaying,\n usePlaybackPosition,\n useCapabilities,\n useLibraryState,\n} from \"./hooks\";\n\n// ---------------------------------------------------------------------------\n// Namespaces (v1 public API)\n// ---------------------------------------------------------------------------\n\nexport { Auth } from \"./auth\";\nexport { AppRemote } from \"./app-remote\";\nexport { Player } from \"./player\";\nexport { User } from \"./user\";\nexport { Content } from \"./content\";\nexport { Images } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// Error hierarchy\n// ---------------------------------------------------------------------------\n\nexport { SpotifyError } from \"./error\";\nexport { AuthError } from \"./auth\";\nexport { AppRemoteError } from \"./app-remote\";\nexport { PlayerError } from \"./player\";\nexport { UserError } from \"./user\";\nexport { ContentError } from \"./content\";\nexport { ImagesError } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// Error code types\n// ---------------------------------------------------------------------------\n\nexport type { AuthErrorCode } from \"./auth\";\nexport type { AppRemoteErrorCode } from \"./app-remote\";\nexport type { PlayerErrorCode } from \"./player\";\nexport type { UserErrorCode } from \"./user\";\nexport type { ContentErrorCode } from \"./content\";\nexport type { ImagesErrorCode } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// URI helpers\n// ---------------------------------------------------------------------------\n\nexport { SpotifyURI } from \"./uri\";\nexport type { SpotifyResourceType, SpotifyURI as SpotifyURIType } from \"./uri\";\n\n// ---------------------------------------------------------------------------\n// Auth types\n// ---------------------------------------------------------------------------\n\nexport type {\n SpotifySession,\n SpotifyScope,\n AuthenticateConfig,\n RefreshConfig,\n SessionChangeEvent,\n} from \"./auth\";\n\n// ---------------------------------------------------------------------------\n// App Remote types\n// ---------------------------------------------------------------------------\n\nexport type {\n ConnectionState,\n ConnectionStateChangeEvent,\n ConnectionErrorEvent,\n} from \"./app-remote\";\n\n// ---------------------------------------------------------------------------\n// Player types\n// ---------------------------------------------------------------------------\n\nexport type {\n RepeatMode,\n PodcastPlaybackSpeed,\n Artist,\n Album,\n Track,\n PlaybackOptions,\n PlaybackRestrictions,\n PlayerState,\n CrossfadeState,\n} from \"./player\";\n\n// ---------------------------------------------------------------------------\n// User types\n// ---------------------------------------------------------------------------\n\nexport type { Capabilities, LibraryState } from \"./user\";\n\n// ---------------------------------------------------------------------------\n// Content types\n// ---------------------------------------------------------------------------\n\nexport type { ContentType, ContentItem } from \"./content\";\n\n// ---------------------------------------------------------------------------\n// Images types\n// ---------------------------------------------------------------------------\n\nexport type { ImageSize, ImageResult, ImageRepresentable } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// v0.x backward-compatible exports (deprecated — remove at v2.0.0)\n//\n// These shims let existing callers continue to compile after upgrading to v1.\n// Migrate to the namespaced API: see docs/V1_PLAN.md §8 (Migration).\n// ---------------------------------------------------------------------------\n\nimport { Auth } from \"./auth\";\nimport type {\n AuthenticateConfig as SpotifyConfig,\n RefreshConfig as SpotifyRefreshConfig,\n} from \"./auth\";\n\n/** @deprecated Use `Auth.isAvailable()` */\nexport function isAvailable(): boolean {\n return Auth.isAvailable();\n}\n\n/** @deprecated Use `Auth.authenticate(config)` */\nexport function authenticateAsync(config: SpotifyConfig) {\n return Auth.authenticate(config);\n}\n\n/** @deprecated Use `Auth.cancelPending()` */\nexport function cancelPendingAuthAsync() {\n return Auth.cancelPending();\n}\n\n/** @deprecated Use `Auth.refresh(config)` */\nexport function refreshSessionAsync(config: SpotifyRefreshConfig) {\n return Auth.refresh(config);\n}\n\n/** @deprecated Use `Auth.addListener(\"sessionChange\", cb)` */\nexport function addSessionChangeListener(\n listener: Parameters<typeof Auth.addListener>[1],\n) {\n return Auth.addListener(\"sessionChange\", listener);\n}\n\n// Re-export legacy type aliases so existing `import type { SpotifyConfig }`\n// consumers don't break.\nexport type { SpotifyConfig, SpotifyRefreshConfig };\n\n/**\n * @deprecated `SpotifyErrorCode` is now `AuthErrorCode`. Import via:\n * `import type { AuthErrorCode } from \"@wwdrew/expo-spotify-sdk\"`\n */\nexport type { AuthErrorCode as SpotifyErrorCode } from \"./auth\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E,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;AAElC,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,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;AAavC,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC","sourcesContent":["// ---------------------------------------------------------------------------\n// Hooks (v1 public API)\n// ---------------------------------------------------------------------------\n\nexport {\n useSession,\n useConnectionState,\n usePlayerState,\n useCurrentTrack,\n useIsPlaying,\n usePlaybackPosition,\n useCapabilities,\n useLibraryState,\n} from \"./hooks\";\n\n// ---------------------------------------------------------------------------\n// Namespaces (v1 public API)\n// ---------------------------------------------------------------------------\n\nexport { Auth } from \"./auth\";\nexport { AppRemote } from \"./app-remote\";\nexport { Player } from \"./player\";\nexport { User } from \"./user\";\nexport { Content } from \"./content\";\nexport { Images } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// Error hierarchy\n// ---------------------------------------------------------------------------\n\nexport { SpotifyError } from \"./error\";\nexport { AuthError } from \"./auth\";\nexport { AppRemoteError } from \"./app-remote\";\nexport { PlayerError } from \"./player\";\nexport { UserError } from \"./user\";\nexport { ContentError } from \"./content\";\nexport { ImagesError } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// Error code types\n// ---------------------------------------------------------------------------\n\nexport type { AuthErrorCode } from \"./auth\";\nexport type { AppRemoteErrorCode } from \"./app-remote\";\nexport type { PlayerErrorCode } from \"./player\";\nexport type { UserErrorCode } from \"./user\";\nexport type { ContentErrorCode } from \"./content\";\nexport type { ImagesErrorCode } from \"./images\";\n\n// ---------------------------------------------------------------------------\n// URI helpers\n// ---------------------------------------------------------------------------\n\nexport { SpotifyURI } from \"./uri\";\nexport type { SpotifyResourceType, SpotifyURI as SpotifyURIType } from \"./uri\";\n\n// ---------------------------------------------------------------------------\n// Auth types\n// ---------------------------------------------------------------------------\n\nexport type {\n SpotifySession,\n SpotifyScope,\n AuthenticateConfig,\n RefreshConfig,\n SessionChangeEvent,\n} from \"./auth\";\n\n// ---------------------------------------------------------------------------\n// App Remote types\n// ---------------------------------------------------------------------------\n\nexport type {\n ConnectionState,\n ConnectionStateChangeEvent,\n ConnectionErrorEvent,\n} from \"./app-remote\";\n\n// ---------------------------------------------------------------------------\n// Player types\n// ---------------------------------------------------------------------------\n\nexport type {\n RepeatMode,\n PodcastPlaybackSpeed,\n Artist,\n Album,\n Track,\n PlaybackOptions,\n PlaybackRestrictions,\n PlayerState,\n CrossfadeState,\n} from \"./player\";\n\n// ---------------------------------------------------------------------------\n// User types\n// ---------------------------------------------------------------------------\n\nexport type { Capabilities, LibraryState } from \"./user\";\n\n// ---------------------------------------------------------------------------\n// Content types\n// ---------------------------------------------------------------------------\n\nexport type { ContentType, ContentItem } from \"./content\";\n\n// ---------------------------------------------------------------------------\n// Images types\n// ---------------------------------------------------------------------------\n\nexport type { ImageSize, ImageResult, ImageRepresentable } from \"./images\";\n"]}
@@ -0,0 +1,14 @@
1
+ import type { SpotifyError } from "../error";
2
+ export interface NativeErrorRethrowOptions<C extends string, E extends SpotifyError & {
3
+ code: C;
4
+ }> {
5
+ ErrorClass: new (code: C, message: string) => E;
6
+ validCodes: ReadonlySet<C>;
7
+ /** Parse legacy `"CODE: message"` prefixes embedded in native error reasons. */
8
+ legacyCodePrefix?: boolean;
9
+ unknownCode: C;
10
+ }
11
+ export declare function createNativeErrorRethrow<C extends string, E extends SpotifyError & {
12
+ code: C;
13
+ }>(options: NativeErrorRethrowOptions<C, E>): (err: unknown) => never;
14
+ //# sourceMappingURL=native-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-errors.d.ts","sourceRoot":"","sources":["../../src/internal/native-errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAU7C,MAAM,WAAW,yBAAyB,CACxC,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,YAAY,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE;IAEpC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC;IAChD,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAC3B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,CAAC,CAAC;CAChB;AAED,wBAAgB,wBAAwB,CACtC,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,YAAY,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,EACpC,OAAO,EAAE,yBAAyB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,CAqBnE"}
@@ -0,0 +1,29 @@
1
+ const CAUSE_SEPARATOR = "→ Caused by: ";
2
+ const LEGACY_CODE_PREFIX_RE = /^([A-Z_][A-Z0-9_]*):\s*(.*)$/s;
3
+ function unwrapReason(message) {
4
+ const idx = message.lastIndexOf(CAUSE_SEPARATOR);
5
+ return idx === -1 ? message : message.slice(idx + CAUSE_SEPARATOR.length);
6
+ }
7
+ export function createNativeErrorRethrow(options) {
8
+ const { ErrorClass, validCodes, legacyCodePrefix, unknownCode } = options;
9
+ return function rethrowNativeError(err) {
10
+ if (err instanceof ErrorClass)
11
+ throw err;
12
+ if (err instanceof Error) {
13
+ const reason = unwrapReason(err.message);
14
+ const maybeCode = err.code;
15
+ if (maybeCode && validCodes.has(maybeCode)) {
16
+ throw new ErrorClass(maybeCode, reason);
17
+ }
18
+ if (legacyCodePrefix) {
19
+ const match = reason.match(LEGACY_CODE_PREFIX_RE);
20
+ if (match && validCodes.has(match[1])) {
21
+ throw new ErrorClass(match[1], match[2]);
22
+ }
23
+ }
24
+ throw new ErrorClass(unknownCode, reason);
25
+ }
26
+ throw new ErrorClass(unknownCode, String(err));
27
+ };
28
+ }
29
+ //# sourceMappingURL=native-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-errors.js","sourceRoot":"","sources":["../../src/internal/native-errors.ts"],"names":[],"mappings":"AAEA,MAAM,eAAe,GAAG,eAAe,CAAC;AACxC,MAAM,qBAAqB,GAAG,+BAA+B,CAAC;AAE9D,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;AAaD,MAAM,UAAU,wBAAwB,CAGtC,OAAwC;IACxC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAE1E,OAAO,SAAS,kBAAkB,CAAC,GAAY;QAC7C,IAAI,GAAG,YAAY,UAAU;YAAE,MAAM,GAAG,CAAC;QACzC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzC,MAAM,SAAS,GAAI,GAAiC,CAAC,IAAI,CAAC;YAC1D,IAAI,SAAS,IAAI,UAAU,CAAC,GAAG,CAAC,SAAc,CAAC,EAAE,CAAC;gBAChD,MAAM,IAAI,UAAU,CAAC,SAAc,EAAE,MAAM,CAAC,CAAC;YAC/C,CAAC;YACD,IAAI,gBAAgB,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;gBAClD,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAM,CAAC,EAAE,CAAC;oBAC3C,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAM,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;YACD,MAAM,IAAI,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,IAAI,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { SpotifyError } from \"../error\";\n\nconst CAUSE_SEPARATOR = \"→ Caused by: \";\nconst LEGACY_CODE_PREFIX_RE = /^([A-Z_][A-Z0-9_]*):\\s*(.*)$/s;\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\nexport interface NativeErrorRethrowOptions<\n C extends string,\n E extends SpotifyError & { code: C },\n> {\n ErrorClass: new (code: C, message: string) => E;\n validCodes: ReadonlySet<C>;\n /** Parse legacy `\"CODE: message\"` prefixes embedded in native error reasons. */\n legacyCodePrefix?: boolean;\n unknownCode: C;\n}\n\nexport function createNativeErrorRethrow<\n C extends string,\n E extends SpotifyError & { code: C },\n>(options: NativeErrorRethrowOptions<C, E>): (err: unknown) => never {\n const { ErrorClass, validCodes, legacyCodePrefix, unknownCode } = options;\n\n return function rethrowNativeError(err: unknown): never {\n if (err instanceof ErrorClass) 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 && validCodes.has(maybeCode as C)) {\n throw new ErrorClass(maybeCode as C, reason);\n }\n if (legacyCodePrefix) {\n const match = reason.match(LEGACY_CODE_PREFIX_RE);\n if (match && validCodes.has(match[1] as C)) {\n throw new ErrorClass(match[1] as C, match[2]!);\n }\n }\n throw new ErrorClass(unknownCode, reason);\n }\n throw new ErrorClass(unknownCode, String(err));\n };\n}\n"]}