@wwdrew/expo-spotify-sdk 1.0.0 → 2.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.
package/README.md CHANGED
@@ -32,9 +32,9 @@ Install the major that matches your Expo SDK:
32
32
 
33
33
  Both lanes ship the same public API (Auth + App Remote namespaces and hooks). The major version signals **runtime lane**, not a different feature set. See [ADR-0005](./docs/adr/0005-sdk-lane-versioning.md).
34
34
 
35
- The current `main` branch targets **Expo SDK 55** and releases as **`1.0.0`**. SDK 56 migration is [Phase 7](./docs/V1_PLAN.md#phase-7--migrate-main-to-expo-sdk-56-v200) (`2.0.0` on `main`).
35
+ The current `main` branch targets **Expo SDK 56** and releases as **`2.x`**. For Expo SDK 55, install **`1.x`** from the `v1` branch ([ADR-0005](./docs/adr/0005-sdk-lane-versioning.md)).
36
36
 
37
- ## v1 namespaced API
37
+ ## Public API (Auth + App Remote)
38
38
 
39
39
  ```ts
40
40
  import {
@@ -63,9 +63,11 @@ Top-level v0-style functions (`authenticateAsync`, `isAvailable`, etc.) are stil
63
63
  ## Quick start (Expo)
64
64
 
65
65
  ```sh
66
- # 1. Install
66
+ # 1. Install (Expo SDK 56+ — matches `main` / npm `2.x`)
67
67
  npx expo install @wwdrew/expo-spotify-sdk
68
68
 
69
+ # Expo SDK 55 only: npx expo install @wwdrew/expo-spotify-sdk@1
70
+
69
71
  # 2. Add the config plugin to app.config.ts / app.json (see Configuration below)
70
72
  # 3. Regenerate native projects
71
73
  npx expo prebuild
@@ -137,7 +139,29 @@ Replace `myapp`, `spotify-auth`, and `your-spotify-client-id` with your own valu
137
139
 
138
140
  ## Configuration
139
141
 
140
- Add the plugin to your `app.config.ts` (or `app.json`):
142
+ Add the plugin to your `app.config.ts` (or `app.json`).
143
+
144
+ ### Typed plugin (Expo SDK 56+)
145
+
146
+ Import from `@wwdrew/expo-spotify-sdk/plugin` for autocomplete and type-checked options:
147
+
148
+ ```ts
149
+ import type { ExpoConfig } from "expo/config";
150
+ import withSpotifySdk from "@wwdrew/expo-spotify-sdk/plugin";
151
+
152
+ export default ({ config }: { config: ExpoConfig }): ExpoConfig => ({
153
+ ...config,
154
+ plugins: [
155
+ withSpotifySdk({
156
+ clientID: "your-spotify-client-id",
157
+ scheme: "myapp",
158
+ host: "spotify-auth",
159
+ }),
160
+ ],
161
+ });
162
+ ```
163
+
164
+ ### String tuple (all SDK versions)
141
165
 
142
166
  ```ts
143
167
  export default {
@@ -304,7 +328,7 @@ For iOS, if `connect()` fails with `CONNECTION_FAILED`, foreground the Spotify a
304
328
 
305
329
  ## Migration from v0.x
306
330
 
307
- v0.x top-level functions remain exported but are deprecated (removed in v2.0.0).
331
+ v0.x top-level functions remain exported but are deprecated (scheduled for removal in a future major).
308
332
 
309
333
  | v0.x | v1 |
310
334
  | --- | --- |
@@ -674,8 +698,8 @@ App Remote player state is limited for non-Premium users. Check `GET /v1/me` →
674
698
 
675
699
  - [CONTEXT.md](./CONTEXT.md) — terminology (Auth SDK vs App Remote vs Web API)
676
700
  - [docs/V1_PLAN.md](./docs/V1_PLAN.md) — implementation plan and release criteria
677
- - [docs/QA_CHECKLIST.md](./docs/QA_CHECKLIST.md) — manual QA sign-off before `v1.0.0`
678
- - [docs/RELEASE.md](./docs/RELEASE.md) — tag `v1.0.0` and cut the `v1` branch
701
+ - [docs/QA_CHECKLIST.md](./docs/QA_CHECKLIST.md) — manual QA before a `2.x` release on `main` (or `1.x` on `v1`)
702
+ - [docs/RELEASE.md](./docs/RELEASE.md) — Release Please on `main` (`2.x`); maintenance releases from `v1` (`1.x`)
679
703
  - [ATTRIBUTION.md](./ATTRIBUTION.md) — third-party SDKs and scope boundaries
680
704
 
681
705
  ## Acknowledgements
@@ -16,10 +16,10 @@ buildscript {
16
16
  }
17
17
 
18
18
  project.android {
19
- compileSdkVersion safeExtGet("compileSdkVersion", 35)
19
+ compileSdkVersion safeExtGet("compileSdkVersion", 36)
20
20
  defaultConfig {
21
21
  minSdkVersion safeExtGet("minSdkVersion", 24)
22
- targetSdkVersion safeExtGet("targetSdkVersion", 35)
22
+ targetSdkVersion safeExtGet("targetSdkVersion", 36)
23
23
  }
24
24
  compileOptions {
25
25
  sourceCompatibility JavaVersion.VERSION_17
@@ -8,7 +8,7 @@ import expo.modules.kotlin.functions.Coroutine
8
8
  import expo.modules.kotlin.modules.Module
9
9
  import expo.modules.kotlin.modules.ModuleDefinition
10
10
 
11
- private const val SDK_VERSION = "1.0.0" // x-release-please-version
11
+ private const val SDK_VERSION = "2.0.0" // x-release-please-version
12
12
  private const val EVENT_SESSION_CHANGE = "onSessionChange"
13
13
  private const val EVENT_CONNECTION_STATE_CHANGE = "onConnectionStateChange"
14
14
  private const val EVENT_CONNECTION_ERROR = "onConnectionError"
package/app.plugin.js CHANGED
@@ -1 +1 @@
1
- module.exports = require("./plugin/build");
1
+ module.exports = require("./plugin/build/withSpotifySdkConfig");
@@ -1,5 +1,5 @@
1
- import { EventSubscription } from "expo-modules-core";
2
- import { AppRemoteErrorCode } from "./error";
1
+ import type { EventSubscription } from "expo-modules-core";
2
+ import { type AppRemoteErrorCode } from "./error";
3
3
  export type { AppRemoteErrorCode } from "./error";
4
4
  export { AppRemoteError } from "./error";
5
5
  /** Current state of the IPC connection to the Spotify app. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/app-remote/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAkB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE7D,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAMzC,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1E,oDAAoD;AACpD,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAqCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS;IACpB;;;;;;;;;;;OAWG;oCACkB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;IAM3C;;;OAGG;+BACW,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;OAIG;gCACY,OAAO;IAItB;;;OAGG;uCACmB,OAAO,CAAC,eAAe,CAAC;IAI9C;;;;;;;;;OASG;2BACS,CAAC,SAAS,uBAAuB,GAAG,iBAAiB,SACxD,CAAC,YACE,CAAC,SAAS,uBAAuB,GACvC,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,GAC3C,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,KACxC,iBAAiB;CAUZ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/app-remote/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAMzC,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1E,oDAAoD;AACpD,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAqCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS;IACpB;;;;;;;;;;;OAWG;oCACkB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;IAM3C;;;OAGG;+BACW,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;OAIG;gCACY,OAAO;IAItB;;;OAGG;uCACmB,OAAO,CAAC,eAAe,CAAC;IAI9C;;;;;;;;;OASG;2BACS,CAAC,SAAS,uBAAuB,GAAG,iBAAiB,SACxD,CAAC,YACE,CAAC,SAAS,uBAAuB,GACvC,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,GAC3C,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,KACxC,iBAAiB;CAUZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/app-remote/index.ts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAsB,MAAM,SAAS,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAoBzC,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAqB;IACxD,mBAAmB;IACnB,iBAAiB;IACjB,eAAe;IACf,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,uBAAuB,CAAC,GAAY;IAC3C,IAAI,GAAG,YAAY,cAAc;QAAE,MAAM,GAAG,CAAC;IAC7C,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,qBAAqB,CAAC,GAAG,CAAC,SAA+B,CAAC,EAAE,CAAC;YAC5E,MAAM,IAAI,cAAc,CAAC,SAA+B,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,IAAI,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IACD,MAAM,IAAI,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,WAAmB;QACzB,OAAO,oBAAoB,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,KAAK,CAC7D,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,OAAO,oBAAoB,CAAC,mBAAmB,EAAE,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,OAAO,oBAAoB,CAAC,oBAAoB,EAAE,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,kBAAkB;QAChB,OAAO,oBAAoB,CAAC,2BAA2B,EAAE,CAAC;IAC5D,CAAC;IAED;;;;;;;;;OASG;IACH,WAAW,CACT,KAAQ,EACR,QAEyC;QAEzC,MAAM,WAAW,GACf,KAAK,KAAK,uBAAuB;YAC/B,CAAC,CAAC,yBAAyB;YAC3B,CAAC,CAAC,mBAAmB,CAAC;QAC1B,OAAO,oBAAoB,CAAC,WAAW,CACrC,WAAW,EACX,QAAoC,CAChB,CAAC;IACzB,CAAC;CACO,CAAC","sourcesContent":["import { EventSubscription } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { AppRemoteError, AppRemoteErrorCode } from \"./error\";\n\nexport type { AppRemoteErrorCode } from \"./error\";\nexport { AppRemoteError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Current state of the IPC connection to the Spotify app. */\nexport type ConnectionState = \"disconnected\" | \"connecting\" | \"connected\";\n\n/** Payload of the `connectionStateChange` event. */\nexport interface ConnectionStateChangeEvent {\n state: ConnectionState;\n}\n\n/** Payload of the `connectionError` event. */\nexport interface ConnectionErrorEvent {\n code: AppRemoteErrorCode;\n message: string;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_APPREMOTE_CODES = new Set<AppRemoteErrorCode>([\n \"CONNECTION_FAILED\",\n \"CONNECTION_LOST\",\n \"NOT_CONNECTED\",\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 rethrowAsAppRemoteError(err: unknown): never {\n if (err instanceof AppRemoteError) 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_APPREMOTE_CODES.has(maybeCode as AppRemoteErrorCode)) {\n throw new AppRemoteError(maybeCode as AppRemoteErrorCode, reason);\n }\n throw new AppRemoteError(\"UNKNOWN\", reason);\n }\n throw new AppRemoteError(\"UNKNOWN\", String(err));\n}\n\n// ---------------------------------------------------------------------------\n// AppRemote namespace\n// ---------------------------------------------------------------------------\n\n/**\n * Spotify App Remote namespace. Manages the IPC connection to the running\n * Spotify app. All `Player`, `User`, `Content`, and `Images` calls require an\n * active connection established via `AppRemote.connect()`.\n *\n * @example\n * ```ts\n * import { AppRemote } from \"@wwdrew/expo-spotify-sdk\";\n *\n * // Connect using the access token from Auth.authenticate()\n * await AppRemote.connect(session.accessToken);\n *\n * const sub = AppRemote.addListener(\"connectionStateChange\", ({ state }) => {\n * console.log(\"connection state:\", state);\n * });\n *\n * // later:\n * await AppRemote.disconnect();\n * sub.remove();\n * ```\n */\nexport const AppRemote = {\n /**\n * Opens a connection to the running Spotify app using the provided access\n * token. Resolves when the connection is established; rejects with an\n * {@link AppRemoteError} if the connection fails.\n *\n * **Android note:** The access token is accepted for API parity with iOS but\n * the Android App Remote SDK does not accept it directly — it uses the\n * session cached in the Spotify app from your earlier `Auth.authenticate()`\n * call. Ensure `Auth.authenticate()` has succeeded before calling `connect()`.\n *\n * Calling `connect()` while already connected is a no-op.\n */\n connect(accessToken: string): Promise<void> {\n return ExpoSpotifySDKModule.appRemoteConnect(accessToken).catch(\n rethrowAsAppRemoteError,\n );\n },\n\n /**\n * Disconnects from the Spotify app. Safe to call when already disconnected.\n * Resolves once the disconnection is complete.\n */\n disconnect(): Promise<void> {\n return ExpoSpotifySDKModule.appRemoteDisconnect();\n },\n\n /**\n * Returns `true` if currently connected to the Spotify app.\n * This is a synchronous snapshot — subscribe to `\"connectionStateChange\"`\n * for reactive updates.\n */\n isConnected(): boolean {\n return ExpoSpotifySDKModule.appRemoteIsConnected();\n },\n\n /**\n * Returns the current {@link ConnectionState} synchronously.\n * Subscribe to `\"connectionStateChange\"` for reactive updates.\n */\n getConnectionState(): Promise<ConnectionState> {\n return ExpoSpotifySDKModule.appRemoteGetConnectionState();\n },\n\n /**\n * Subscribes to connection lifecycle events.\n *\n * | Event | Payload | When |\n * |---|---|---|\n * | `\"connectionStateChange\"` | `{ state: ConnectionState }` | State transitions |\n * | `\"connectionError\"` | `{ code, message }` | Connection failures and drops |\n *\n * Returns a `Subscription` — call `.remove()` to unsubscribe.\n */\n addListener<E extends \"connectionStateChange\" | \"connectionError\">(\n event: E,\n listener: E extends \"connectionStateChange\"\n ? (event: ConnectionStateChangeEvent) => void\n : (event: ConnectionErrorEvent) => void,\n ): EventSubscription {\n const nativeEvent =\n event === \"connectionStateChange\"\n ? \"onConnectionStateChange\"\n : \"onConnectionError\";\n return ExpoSpotifySDKModule.addListener(\n nativeEvent,\n listener as (event: unknown) => void,\n ) as EventSubscription;\n },\n} as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/app-remote/index.ts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAA2B,MAAM,SAAS,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAoBzC,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAqB;IACxD,mBAAmB;IACnB,iBAAiB;IACjB,eAAe;IACf,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,uBAAuB,CAAC,GAAY;IAC3C,IAAI,GAAG,YAAY,cAAc;QAAE,MAAM,GAAG,CAAC;IAC7C,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,qBAAqB,CAAC,GAAG,CAAC,SAA+B,CAAC,EAAE,CAAC;YAC5E,MAAM,IAAI,cAAc,CAAC,SAA+B,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,IAAI,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IACD,MAAM,IAAI,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,WAAmB;QACzB,OAAO,oBAAoB,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,KAAK,CAC7D,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,OAAO,oBAAoB,CAAC,mBAAmB,EAAE,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,OAAO,oBAAoB,CAAC,oBAAoB,EAAE,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,kBAAkB;QAChB,OAAO,oBAAoB,CAAC,2BAA2B,EAAE,CAAC;IAC5D,CAAC;IAED;;;;;;;;;OASG;IACH,WAAW,CACT,KAAQ,EACR,QAEyC;QAEzC,MAAM,WAAW,GACf,KAAK,KAAK,uBAAuB;YAC/B,CAAC,CAAC,yBAAyB;YAC3B,CAAC,CAAC,mBAAmB,CAAC;QAC1B,OAAO,oBAAoB,CAAC,WAAW,CACrC,WAAW,EACX,QAAoC,CAChB,CAAC;IACzB,CAAC;CACO,CAAC","sourcesContent":["import type { EventSubscription } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { AppRemoteError, type AppRemoteErrorCode } from \"./error\";\n\nexport type { AppRemoteErrorCode } from \"./error\";\nexport { AppRemoteError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Current state of the IPC connection to the Spotify app. */\nexport type ConnectionState = \"disconnected\" | \"connecting\" | \"connected\";\n\n/** Payload of the `connectionStateChange` event. */\nexport interface ConnectionStateChangeEvent {\n state: ConnectionState;\n}\n\n/** Payload of the `connectionError` event. */\nexport interface ConnectionErrorEvent {\n code: AppRemoteErrorCode;\n message: string;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_APPREMOTE_CODES = new Set<AppRemoteErrorCode>([\n \"CONNECTION_FAILED\",\n \"CONNECTION_LOST\",\n \"NOT_CONNECTED\",\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 rethrowAsAppRemoteError(err: unknown): never {\n if (err instanceof AppRemoteError) 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_APPREMOTE_CODES.has(maybeCode as AppRemoteErrorCode)) {\n throw new AppRemoteError(maybeCode as AppRemoteErrorCode, reason);\n }\n throw new AppRemoteError(\"UNKNOWN\", reason);\n }\n throw new AppRemoteError(\"UNKNOWN\", String(err));\n}\n\n// ---------------------------------------------------------------------------\n// AppRemote namespace\n// ---------------------------------------------------------------------------\n\n/**\n * Spotify App Remote namespace. Manages the IPC connection to the running\n * Spotify app. All `Player`, `User`, `Content`, and `Images` calls require an\n * active connection established via `AppRemote.connect()`.\n *\n * @example\n * ```ts\n * import { AppRemote } from \"@wwdrew/expo-spotify-sdk\";\n *\n * // Connect using the access token from Auth.authenticate()\n * await AppRemote.connect(session.accessToken);\n *\n * const sub = AppRemote.addListener(\"connectionStateChange\", ({ state }) => {\n * console.log(\"connection state:\", state);\n * });\n *\n * // later:\n * await AppRemote.disconnect();\n * sub.remove();\n * ```\n */\nexport const AppRemote = {\n /**\n * Opens a connection to the running Spotify app using the provided access\n * token. Resolves when the connection is established; rejects with an\n * {@link AppRemoteError} if the connection fails.\n *\n * **Android note:** The access token is accepted for API parity with iOS but\n * the Android App Remote SDK does not accept it directly — it uses the\n * session cached in the Spotify app from your earlier `Auth.authenticate()`\n * call. Ensure `Auth.authenticate()` has succeeded before calling `connect()`.\n *\n * Calling `connect()` while already connected is a no-op.\n */\n connect(accessToken: string): Promise<void> {\n return ExpoSpotifySDKModule.appRemoteConnect(accessToken).catch(\n rethrowAsAppRemoteError,\n );\n },\n\n /**\n * Disconnects from the Spotify app. Safe to call when already disconnected.\n * Resolves once the disconnection is complete.\n */\n disconnect(): Promise<void> {\n return ExpoSpotifySDKModule.appRemoteDisconnect();\n },\n\n /**\n * Returns `true` if currently connected to the Spotify app.\n * This is a synchronous snapshot — subscribe to `\"connectionStateChange\"`\n * for reactive updates.\n */\n isConnected(): boolean {\n return ExpoSpotifySDKModule.appRemoteIsConnected();\n },\n\n /**\n * Returns the current {@link ConnectionState} synchronously.\n * Subscribe to `\"connectionStateChange\"` for reactive updates.\n */\n getConnectionState(): Promise<ConnectionState> {\n return ExpoSpotifySDKModule.appRemoteGetConnectionState();\n },\n\n /**\n * Subscribes to connection lifecycle events.\n *\n * | Event | Payload | When |\n * |---|---|---|\n * | `\"connectionStateChange\"` | `{ state: ConnectionState }` | State transitions |\n * | `\"connectionError\"` | `{ code, message }` | Connection failures and drops |\n *\n * Returns a `Subscription` — call `.remove()` to unsubscribe.\n */\n addListener<E extends \"connectionStateChange\" | \"connectionError\">(\n event: E,\n listener: E extends \"connectionStateChange\"\n ? (event: ConnectionStateChangeEvent) => void\n : (event: ConnectionErrorEvent) => void,\n ): EventSubscription {\n const nativeEvent =\n event === \"connectionStateChange\"\n ? \"onConnectionStateChange\"\n : \"onConnectionError\";\n return ExpoSpotifySDKModule.addListener(\n nativeEvent,\n listener as (event: unknown) => void,\n ) as EventSubscription;\n },\n} as const;\n"]}
@@ -1,5 +1,5 @@
1
- import { EventSubscription } from "expo-modules-core";
2
- import { AuthErrorCode } from "./error";
1
+ import type { EventSubscription } from "expo-modules-core";
2
+ import { type AuthErrorCode } from "./error";
3
3
  export type { AuthErrorCode } from "./error";
4
4
  export { AuthError } from "./error";
5
5
  export interface SpotifySession {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAY,MAAM,mBAAmB,CAAC;AAGhE,OAAO,EAAa,aAAa,EAAE,MAAM,SAAS,CAAC;AAEnD,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMpC,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,0BAA0B,GAC1B,4BAA4B,GAC5B,6BAA6B,GAC7B,oBAAoB,GACpB,WAAW,GACX,uBAAuB,GACvB,6BAA6B,GAC7B,yBAAyB,GACzB,wBAAwB,GACxB,oBAAoB,GACpB,kBAAkB,GAClB,eAAe,GACf,2BAA2B,GAC3B,qBAAqB,GACrB,mBAAmB,GACnB,iBAAiB,GACjB,mBAAmB,CAAC;AAExB,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAgFzE;;;;;;;;;GASG;AACH,eAAO,MAAM,IAAI;IACf;;OAEG;gCACY,OAAO;IAItB;;;OAGG;oCACkB,kBAAkB,KAAG,OAAO,CAAC,cAAc,CAAC;IAmBjE;;;;OAIG;+BACa,aAAa,KAAG,OAAO,CAAC,cAAc,CAAC;IAgBvD;;;;;;;OAOG;kCACc,OAAO,CAAC,IAAI,CAAC;IAO9B;;;;;;;;;;;;;;;;;;;OAmBG;kCAEM,eAAe,YACZ,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,KAC5C,iBAAiB;CAMZ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAExD,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMpC,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,0BAA0B,GAC1B,4BAA4B,GAC5B,6BAA6B,GAC7B,oBAAoB,GACpB,WAAW,GACX,uBAAuB,GACvB,6BAA6B,GAC7B,yBAAyB,GACzB,wBAAwB,GACxB,oBAAoB,GACpB,kBAAkB,GAClB,eAAe,GACf,2BAA2B,GAC3B,qBAAqB,GACrB,mBAAmB,GACnB,iBAAiB,GACjB,mBAAmB,CAAC;AAExB,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAgFzE;;;;;;;;;GASG;AACH,eAAO,MAAM,IAAI;IACf;;OAEG;gCACY,OAAO;IAItB;;;OAGG;oCACkB,kBAAkB,KAAG,OAAO,CAAC,cAAc,CAAC;IAmBjE;;;;OAIG;+BACa,aAAa,KAAG,OAAO,CAAC,cAAc,CAAC;IAgBvD;;;;;;;OAOG;kCACc,OAAO,CAAC,IAAI,CAAC;IAO9B;;;;;;;;;;;;;;;;;;;OAmBG;kCAEM,eAAe,YACZ,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,KAC5C,iBAAiB;CAMZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventSubscription, Platform } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { AuthError, AuthErrorCode } from \"./error\";\n\nexport type { AuthErrorCode } from \"./error\";\nexport { AuthError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Auth-specific types\n// ---------------------------------------------------------------------------\n\nexport interface SpotifySession {\n /** OAuth access token. */\n accessToken: string;\n /**\n * OAuth refresh token. `null` on Android when no `tokenSwapURL` is provided\n * (the Spotify Android SDK does not expose a refresh token for implicit\n * grants).\n */\n refreshToken: string | null;\n /** Expiration timestamp as Unix epoch milliseconds. */\n expirationDate: number;\n /** Scopes the access token was granted (or requested, on Android implicit). */\n scopes: SpotifyScope[];\n}\n\nexport type SpotifyScope =\n | \"ugc-image-upload\"\n | \"user-read-playback-state\"\n | \"user-modify-playback-state\"\n | \"user-read-currently-playing\"\n | \"app-remote-control\"\n | \"streaming\"\n | \"playlist-read-private\"\n | \"playlist-read-collaborative\"\n | \"playlist-modify-private\"\n | \"playlist-modify-public\"\n | \"user-follow-modify\"\n | \"user-follow-read\"\n | \"user-top-read\"\n | \"user-read-recently-played\"\n | \"user-library-modify\"\n | \"user-library-read\"\n | \"user-read-email\"\n | \"user-read-private\";\n\nexport interface AuthenticateConfig {\n /** OAuth scopes to request. Must contain at least one entry. */\n scopes: SpotifyScope[];\n /**\n * If supplied, requests an authorization code rather than an implicit\n * token, then POSTs the code to this URL to exchange it for tokens.\n * **Required on Android** to receive a usable `refreshToken`.\n */\n tokenSwapURL?: string;\n /**\n * Used by the iOS SDK to refresh access tokens automatically, and by\n * `Auth.refresh()` on both platforms.\n */\n tokenRefreshURL?: string;\n /**\n * If `true`, forces Spotify to show the authorization dialog even when the\n * user already has an active session. Defaults to `false`.\n */\n showDialog?: boolean;\n}\n\nexport interface RefreshConfig {\n /** The refresh token from a previous `Auth.authenticate()` call. */\n refreshToken: string;\n /** URL of your token refresh server endpoint. */\n tokenRefreshURL: string;\n /**\n * Scopes that were granted by the previous session. Used as a fallback when\n * the refresh response omits the `scope` field.\n */\n scopes?: SpotifyScope[];\n}\n\n/**\n * Payload delivered to `Auth.addListener(\"sessionChange\", ...)` subscribers.\n */\nexport type SessionChangeEvent =\n | { type: \"didInitiate\"; session: SpotifySession }\n | { type: \"didRenew\"; session: SpotifySession }\n | { type: \"didFail\"; error: { code: AuthErrorCode; message: string } };\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst ANDROID_TOKEN_FLOW_WARNING =\n \"[expo-spotify-sdk] You are using Auth.authenticate on Android without a \" +\n \"tokenSwapURL. The Spotify Android SDK does NOT return a refresh token or \" +\n \"the actual granted scopes through this path; see the README's \" +\n \"'Android implicit (TOKEN) flow is not recommended' section.\";\n\nlet warnedAboutAndroidTokenFlow = false;\n\nconst VALID_AUTH_CODES = new Set<AuthErrorCode>([\n \"USER_CANCELLED\",\n \"AUTH_IN_PROGRESS\",\n \"INVALID_CONFIG\",\n \"NETWORK_ERROR\",\n \"TOKEN_SWAP_FAILED\",\n \"TOKEN_SWAP_PARSE_ERROR\",\n \"SPOTIFY_NOT_INSTALLED\",\n \"AUTH_ERROR\",\n \"UNKNOWN\",\n]);\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\nfunction rethrowAsAuthError(err: unknown): never {\n if (err instanceof AuthError) 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_AUTH_CODES.has(maybeCode as AuthErrorCode)) {\n throw new AuthError(maybeCode as AuthErrorCode, reason);\n }\n const m = reason.match(LEGACY_CODE_PREFIX_RE);\n if (m && VALID_AUTH_CODES.has(m[1] as AuthErrorCode)) {\n throw new AuthError(m[1] as AuthErrorCode, m[2]);\n }\n throw new AuthError(\"UNKNOWN\", reason);\n }\n throw new AuthError(\"UNKNOWN\", String(err));\n}\n\nfunction normaliseSession(raw: unknown): SpotifySession {\n if (!raw || typeof raw !== \"object\") {\n throw new AuthError(\"UNKNOWN\", \"Native module returned a non-object session\");\n }\n const r = raw as Record<string, unknown>;\n const accessToken = r.accessToken;\n if (typeof accessToken !== \"string\" || accessToken.length === 0) {\n throw new AuthError(\"UNKNOWN\", \"Session is missing accessToken\");\n }\n const expirationDate = r.expirationDate;\n if (typeof expirationDate !== \"number\") {\n throw new AuthError(\"UNKNOWN\", \"Session is missing expirationDate\");\n }\n const refreshTokenRaw = r.refreshToken;\n const refreshToken =\n typeof refreshTokenRaw === \"string\" && refreshTokenRaw.length > 0\n ? refreshTokenRaw\n : null;\n const scopesRaw = r.scopes;\n const scopes = Array.isArray(scopesRaw)\n ? (scopesRaw.filter((s) => typeof s === \"string\") as SpotifyScope[])\n : [];\n return { accessToken, refreshToken, expirationDate, scopes };\n}\n\n// ---------------------------------------------------------------------------\n// Auth namespace\n// ---------------------------------------------------------------------------\n\n/**\n * Spotify Auth namespace. Handles OAuth authentication and session lifecycle.\n *\n * @example\n * ```ts\n * import { Auth } from \"@wwdrew/expo-spotify-sdk\";\n *\n * const session = await Auth.authenticate({ scopes: [\"streaming\"] });\n * ```\n */\nexport const Auth = {\n /**\n * Returns `true` if the Spotify app is installed on the device.\n */\n isAvailable(): boolean {\n return ExpoSpotifySDKModule.isAvailable();\n },\n\n /**\n * Starts a Spotify OAuth flow. Resolves with a {@link SpotifySession};\n * rejects with an {@link AuthError} carrying a `code`.\n */\n authenticate(config: AuthenticateConfig): Promise<SpotifySession> {\n if (!config.scopes || config.scopes.length === 0) {\n return Promise.reject(\n new AuthError(\"INVALID_CONFIG\", \"scopes must contain at least one entry\"),\n );\n }\n if (\n Platform.OS === \"android\" &&\n !config.tokenSwapURL &&\n !warnedAboutAndroidTokenFlow\n ) {\n warnedAboutAndroidTokenFlow = true;\n console.warn(ANDROID_TOKEN_FLOW_WARNING);\n }\n return ExpoSpotifySDKModule.authenticateAsync(config)\n .then(normaliseSession)\n .catch(rethrowAsAuthError);\n },\n\n /**\n * Exchanges a refresh token for a new access token via your token refresh\n * server. Resolves with a fresh {@link SpotifySession}; rejects with an\n * {@link AuthError}.\n */\n refresh(config: RefreshConfig): Promise<SpotifySession> {\n if (!config.refreshToken) {\n return Promise.reject(\n new AuthError(\"INVALID_CONFIG\", \"refreshToken is required\"),\n );\n }\n if (!config.tokenRefreshURL) {\n return Promise.reject(\n new AuthError(\"INVALID_CONFIG\", \"tokenRefreshURL is required\"),\n );\n }\n return ExpoSpotifySDKModule.refreshSessionAsync(config)\n .then(normaliseSession)\n .catch(rethrowAsAuthError);\n },\n\n /**\n * Forcibly cancel any in-flight `Auth.authenticate()` call. No-op on\n * Android (the Android coordinator self-cleans via structured concurrency).\n *\n * Use before `Auth.authenticate()` to defensively clear any leaked iOS\n * coordinator state (the `SPTSessionManager` delegate callbacks are not\n * guaranteed to fire).\n */\n cancelPending(): Promise<void> {\n if (Platform.OS !== \"ios\") {\n return Promise.resolve();\n }\n return ExpoSpotifySDKModule.cancelPendingAuthAsync();\n },\n\n /**\n * Subscribes to session lifecycle events.\n *\n * Events fire for every `Auth.authenticate()` and `Auth.refresh()` call,\n * regardless of whether the call was awaited. Useful for persisting tokens\n * in a central store without coupling the store to the call sites.\n *\n * Returns a `Subscription` — call `.remove()` to unsubscribe.\n *\n * @example\n * ```ts\n * const sub = Auth.addListener(\"sessionChange\", (event) => {\n * if (event.type === \"didInitiate\" || event.type === \"didRenew\") {\n * store.setSession(event.session);\n * }\n * });\n * // later:\n * sub.remove();\n * ```\n */\n addListener(\n event: \"sessionChange\",\n listener: (event: SessionChangeEvent) => void,\n ): EventSubscription {\n return ExpoSpotifySDKModule.addListener(\n \"onSessionChange\",\n listener,\n ) as EventSubscription;\n },\n} as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAsB,MAAM,SAAS,CAAC;AAGxD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAkFpC,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,0BAA0B,GAC9B,0EAA0E;IAC1E,2EAA2E;IAC3E,gEAAgE;IAChE,6DAA6D,CAAC;AAEhE,IAAI,2BAA2B,GAAG,KAAK,CAAC;AAExC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAgB;IAC9C,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,eAAe;IACf,mBAAmB;IACnB,wBAAwB;IACxB,uBAAuB;IACvB,YAAY;IACZ,SAAS;CACV,CAAC,CAAC;AAEH,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;AAED,SAAS,kBAAkB,CAAC,GAAY;IACtC,IAAI,GAAG,YAAY,SAAS;QAAE,MAAM,GAAG,CAAC;IACxC,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,gBAAgB,CAAC,GAAG,CAAC,SAA0B,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,SAAS,CAAC,SAA0B,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAkB,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAkB,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,IAAI,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CAAC,SAAS,EAAE,6CAA6C,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;IAClC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC;IACxC,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,SAAS,CAAC,SAAS,EAAE,mCAAmC,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,eAAe,GAAG,CAAC,CAAC,YAAY,CAAC;IACvC,MAAM,YAAY,GAChB,OAAO,eAAe,KAAK,QAAQ,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC;QAC/D,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,IAAI,CAAC;IACX,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QACrC,CAAC,CAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAoB;QACpE,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC;AAC/D,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB;;OAEG;IACH,WAAW;QACT,OAAO,oBAAoB,CAAC,WAAW,EAAE,CAAC;IAC5C,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,MAA0B;QACrC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,SAAS,CAAC,gBAAgB,EAAE,wCAAwC,CAAC,CAC1E,CAAC;QACJ,CAAC;QACD,IACE,QAAQ,CAAC,EAAE,KAAK,SAAS;YACzB,CAAC,MAAM,CAAC,YAAY;YACpB,CAAC,2BAA2B,EAC5B,CAAC;YACD,2BAA2B,GAAG,IAAI,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,oBAAoB,CAAC,iBAAiB,CAAC,MAAM,CAAC;aAClD,IAAI,CAAC,gBAAgB,CAAC;aACtB,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,MAAqB;QAC3B,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,SAAS,CAAC,gBAAgB,EAAE,0BAA0B,CAAC,CAC5D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,SAAS,CAAC,gBAAgB,EAAE,6BAA6B,CAAC,CAC/D,CAAC;QACJ,CAAC;QACD,OAAO,oBAAoB,CAAC,mBAAmB,CAAC,MAAM,CAAC;aACpD,IAAI,CAAC,gBAAgB,CAAC;aACtB,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;;OAOG;IACH,aAAa;QACX,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,oBAAoB,CAAC,sBAAsB,EAAE,CAAC;IACvD,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CACT,KAAsB,EACtB,QAA6C;QAE7C,OAAO,oBAAoB,CAAC,WAAW,CACrC,iBAAiB,EACjB,QAAQ,CACY,CAAC;IACzB,CAAC;CACO,CAAC","sourcesContent":["import type { EventSubscription } from \"expo-modules-core\";\nimport { Platform } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { AuthError, type AuthErrorCode } from \"./error\";\n\nexport type { AuthErrorCode } from \"./error\";\nexport { AuthError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Auth-specific types\n// ---------------------------------------------------------------------------\n\nexport interface SpotifySession {\n /** OAuth access token. */\n accessToken: string;\n /**\n * OAuth refresh token. `null` on Android when no `tokenSwapURL` is provided\n * (the Spotify Android SDK does not expose a refresh token for implicit\n * grants).\n */\n refreshToken: string | null;\n /** Expiration timestamp as Unix epoch milliseconds. */\n expirationDate: number;\n /** Scopes the access token was granted (or requested, on Android implicit). */\n scopes: SpotifyScope[];\n}\n\nexport type SpotifyScope =\n | \"ugc-image-upload\"\n | \"user-read-playback-state\"\n | \"user-modify-playback-state\"\n | \"user-read-currently-playing\"\n | \"app-remote-control\"\n | \"streaming\"\n | \"playlist-read-private\"\n | \"playlist-read-collaborative\"\n | \"playlist-modify-private\"\n | \"playlist-modify-public\"\n | \"user-follow-modify\"\n | \"user-follow-read\"\n | \"user-top-read\"\n | \"user-read-recently-played\"\n | \"user-library-modify\"\n | \"user-library-read\"\n | \"user-read-email\"\n | \"user-read-private\";\n\nexport interface AuthenticateConfig {\n /** OAuth scopes to request. Must contain at least one entry. */\n scopes: SpotifyScope[];\n /**\n * If supplied, requests an authorization code rather than an implicit\n * token, then POSTs the code to this URL to exchange it for tokens.\n * **Required on Android** to receive a usable `refreshToken`.\n */\n tokenSwapURL?: string;\n /**\n * Used by the iOS SDK to refresh access tokens automatically, and by\n * `Auth.refresh()` on both platforms.\n */\n tokenRefreshURL?: string;\n /**\n * If `true`, forces Spotify to show the authorization dialog even when the\n * user already has an active session. Defaults to `false`.\n */\n showDialog?: boolean;\n}\n\nexport interface RefreshConfig {\n /** The refresh token from a previous `Auth.authenticate()` call. */\n refreshToken: string;\n /** URL of your token refresh server endpoint. */\n tokenRefreshURL: string;\n /**\n * Scopes that were granted by the previous session. Used as a fallback when\n * the refresh response omits the `scope` field.\n */\n scopes?: SpotifyScope[];\n}\n\n/**\n * Payload delivered to `Auth.addListener(\"sessionChange\", ...)` subscribers.\n */\nexport type SessionChangeEvent =\n | { type: \"didInitiate\"; session: SpotifySession }\n | { type: \"didRenew\"; session: SpotifySession }\n | { type: \"didFail\"; error: { code: AuthErrorCode; message: string } };\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst ANDROID_TOKEN_FLOW_WARNING =\n \"[expo-spotify-sdk] You are using Auth.authenticate on Android without a \" +\n \"tokenSwapURL. The Spotify Android SDK does NOT return a refresh token or \" +\n \"the actual granted scopes through this path; see the README's \" +\n \"'Android implicit (TOKEN) flow is not recommended' section.\";\n\nlet warnedAboutAndroidTokenFlow = false;\n\nconst VALID_AUTH_CODES = new Set<AuthErrorCode>([\n \"USER_CANCELLED\",\n \"AUTH_IN_PROGRESS\",\n \"INVALID_CONFIG\",\n \"NETWORK_ERROR\",\n \"TOKEN_SWAP_FAILED\",\n \"TOKEN_SWAP_PARSE_ERROR\",\n \"SPOTIFY_NOT_INSTALLED\",\n \"AUTH_ERROR\",\n \"UNKNOWN\",\n]);\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\nfunction rethrowAsAuthError(err: unknown): never {\n if (err instanceof AuthError) 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_AUTH_CODES.has(maybeCode as AuthErrorCode)) {\n throw new AuthError(maybeCode as AuthErrorCode, reason);\n }\n const m = reason.match(LEGACY_CODE_PREFIX_RE);\n if (m && VALID_AUTH_CODES.has(m[1] as AuthErrorCode)) {\n throw new AuthError(m[1] as AuthErrorCode, m[2]!);\n }\n throw new AuthError(\"UNKNOWN\", reason);\n }\n throw new AuthError(\"UNKNOWN\", String(err));\n}\n\nfunction normaliseSession(raw: unknown): SpotifySession {\n if (!raw || typeof raw !== \"object\") {\n throw new AuthError(\"UNKNOWN\", \"Native module returned a non-object session\");\n }\n const r = raw as Record<string, unknown>;\n const accessToken = r.accessToken;\n if (typeof accessToken !== \"string\" || accessToken.length === 0) {\n throw new AuthError(\"UNKNOWN\", \"Session is missing accessToken\");\n }\n const expirationDate = r.expirationDate;\n if (typeof expirationDate !== \"number\") {\n throw new AuthError(\"UNKNOWN\", \"Session is missing expirationDate\");\n }\n const refreshTokenRaw = r.refreshToken;\n const refreshToken =\n typeof refreshTokenRaw === \"string\" && refreshTokenRaw.length > 0\n ? refreshTokenRaw\n : null;\n const scopesRaw = r.scopes;\n const scopes = Array.isArray(scopesRaw)\n ? (scopesRaw.filter((s) => typeof s === \"string\") as SpotifyScope[])\n : [];\n return { accessToken, refreshToken, expirationDate, scopes };\n}\n\n// ---------------------------------------------------------------------------\n// Auth namespace\n// ---------------------------------------------------------------------------\n\n/**\n * Spotify Auth namespace. Handles OAuth authentication and session lifecycle.\n *\n * @example\n * ```ts\n * import { Auth } from \"@wwdrew/expo-spotify-sdk\";\n *\n * const session = await Auth.authenticate({ scopes: [\"streaming\"] });\n * ```\n */\nexport const Auth = {\n /**\n * Returns `true` if the Spotify app is installed on the device.\n */\n isAvailable(): boolean {\n return ExpoSpotifySDKModule.isAvailable();\n },\n\n /**\n * Starts a Spotify OAuth flow. Resolves with a {@link SpotifySession};\n * rejects with an {@link AuthError} carrying a `code`.\n */\n authenticate(config: AuthenticateConfig): Promise<SpotifySession> {\n if (!config.scopes || config.scopes.length === 0) {\n return Promise.reject(\n new AuthError(\"INVALID_CONFIG\", \"scopes must contain at least one entry\"),\n );\n }\n if (\n Platform.OS === \"android\" &&\n !config.tokenSwapURL &&\n !warnedAboutAndroidTokenFlow\n ) {\n warnedAboutAndroidTokenFlow = true;\n console.warn(ANDROID_TOKEN_FLOW_WARNING);\n }\n return ExpoSpotifySDKModule.authenticateAsync(config)\n .then(normaliseSession)\n .catch(rethrowAsAuthError);\n },\n\n /**\n * Exchanges a refresh token for a new access token via your token refresh\n * server. Resolves with a fresh {@link SpotifySession}; rejects with an\n * {@link AuthError}.\n */\n refresh(config: RefreshConfig): Promise<SpotifySession> {\n if (!config.refreshToken) {\n return Promise.reject(\n new AuthError(\"INVALID_CONFIG\", \"refreshToken is required\"),\n );\n }\n if (!config.tokenRefreshURL) {\n return Promise.reject(\n new AuthError(\"INVALID_CONFIG\", \"tokenRefreshURL is required\"),\n );\n }\n return ExpoSpotifySDKModule.refreshSessionAsync(config)\n .then(normaliseSession)\n .catch(rethrowAsAuthError);\n },\n\n /**\n * Forcibly cancel any in-flight `Auth.authenticate()` call. No-op on\n * Android (the Android coordinator self-cleans via structured concurrency).\n *\n * Use before `Auth.authenticate()` to defensively clear any leaked iOS\n * coordinator state (the `SPTSessionManager` delegate callbacks are not\n * guaranteed to fire).\n */\n cancelPending(): Promise<void> {\n if (Platform.OS !== \"ios\") {\n return Promise.resolve();\n }\n return ExpoSpotifySDKModule.cancelPendingAuthAsync();\n },\n\n /**\n * Subscribes to session lifecycle events.\n *\n * Events fire for every `Auth.authenticate()` and `Auth.refresh()` call,\n * regardless of whether the call was awaited. Useful for persisting tokens\n * in a central store without coupling the store to the call sites.\n *\n * Returns a `Subscription` — call `.remove()` to unsubscribe.\n *\n * @example\n * ```ts\n * const sub = Auth.addListener(\"sessionChange\", (event) => {\n * if (event.type === \"didInitiate\" || event.type === \"didRenew\") {\n * store.setSession(event.session);\n * }\n * });\n * // later:\n * sub.remove();\n * ```\n */\n addListener(\n event: \"sessionChange\",\n listener: (event: SessionChangeEvent) => void,\n ): EventSubscription {\n return ExpoSpotifySDKModule.addListener(\n \"onSessionChange\",\n listener,\n ) as EventSubscription;\n },\n} as const;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAoB,MAAM,SAAS,CAAC;AAkBzD,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAmB;IACpD,eAAe;IACf,iBAAiB;IACjB,yBAAyB;IACzB,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,qBAAqB,CAAC,GAAY;IACzC,IAAI,GAAG,YAAY,YAAY;QAAE,MAAM,GAAG,CAAC;IAC3C,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,mBAAmB,CAAC,GAAG,CAAC,SAA6B,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,YAAY,CAAC,SAA6B,EAAE,MAAM,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB;;OAEG;IACH,0BAA0B,CAAC,IAAiB;QAC1C,OAAO,oBAAoB,CAAC,iCAAiC,CAAC,IAAI,CAAC,CAAC,KAAK,CACvE,qBAAqB,CACtB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,IAAiB;QAC3B,OAAO,oBAAoB,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACpF,CAAC;CACO,CAAC","sourcesContent":["export type { ContentErrorCode } from \"./error\";\nexport { ContentError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { ContentError, ContentErrorCode } from \"./error\";\n\nexport type ContentType = \"default\" | \"navigation\" | \"fitness\" | \"gaming\";\n\nexport interface ContentItem {\n title: string | null;\n subtitle: string | null;\n contentDescription: string | null;\n identifier: string;\n uri: string;\n imageIdentifier: string | null;\n isAvailableOffline: boolean;\n isPlayable: boolean;\n isContainer: boolean;\n isPinned: boolean;\n children?: ContentItem[];\n}\n\nconst VALID_CONTENT_CODES = new Set<ContentErrorCode>([\n \"NOT_CONNECTED\",\n \"CONNECTION_LOST\",\n \"CONTENT_API_UNAVAILABLE\",\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 rethrowAsContentError(err: unknown): never {\n if (err instanceof ContentError) 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_CONTENT_CODES.has(maybeCode as ContentErrorCode)) {\n throw new ContentError(maybeCode as ContentErrorCode, reason);\n }\n throw new ContentError(\"UNKNOWN\", reason);\n }\n throw new ContentError(\"UNKNOWN\", String(err));\n}\n\n/**\n * Spotify Content namespace. Browse Spotify's curated content tree.\n * Requires `AppRemote.connect()` to be resolved before any call.\n *\n * @example\n * ```ts\n * import { Content } from \"@wwdrew/expo-spotify-sdk\";\n *\n * const items = await Content.getRecommendedContentItems(\"default\");\n * ```\n */\nexport const Content = {\n /**\n * Returns Spotify-curated recommended content for the given feed type.\n */\n getRecommendedContentItems(type: ContentType): Promise<ContentItem[]> {\n return ExpoSpotifySDKModule.contentGetRecommendedContentItems(type).catch(\n rethrowAsContentError,\n );\n },\n\n /**\n * Returns children of a browsable (container) content item.\n */\n getChildren(item: ContentItem): Promise<ContentItem[]> {\n return ExpoSpotifySDKModule.contentGetChildren(item).catch(rethrowAsContentError);\n },\n} as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAyB,MAAM,SAAS,CAAC;AAkB9D,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAmB;IACpD,eAAe;IACf,iBAAiB;IACjB,yBAAyB;IACzB,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,qBAAqB,CAAC,GAAY;IACzC,IAAI,GAAG,YAAY,YAAY;QAAE,MAAM,GAAG,CAAC;IAC3C,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,mBAAmB,CAAC,GAAG,CAAC,SAA6B,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,YAAY,CAAC,SAA6B,EAAE,MAAM,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB;;OAEG;IACH,0BAA0B,CAAC,IAAiB;QAC1C,OAAO,oBAAoB,CAAC,iCAAiC,CAAC,IAAI,CAAC,CAAC,KAAK,CACvE,qBAAqB,CACtB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,IAAiB;QAC3B,OAAO,oBAAoB,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACpF,CAAC;CACO,CAAC","sourcesContent":["export type { ContentErrorCode } from \"./error\";\nexport { ContentError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { ContentError, type ContentErrorCode } from \"./error\";\n\nexport type ContentType = \"default\" | \"navigation\" | \"fitness\" | \"gaming\";\n\nexport interface ContentItem {\n title: string | null;\n subtitle: string | null;\n contentDescription: string | null;\n identifier: string;\n uri: string;\n imageIdentifier: string | null;\n isAvailableOffline: boolean;\n isPlayable: boolean;\n isContainer: boolean;\n isPinned: boolean;\n children?: ContentItem[];\n}\n\nconst VALID_CONTENT_CODES = new Set<ContentErrorCode>([\n \"NOT_CONNECTED\",\n \"CONNECTION_LOST\",\n \"CONTENT_API_UNAVAILABLE\",\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 rethrowAsContentError(err: unknown): never {\n if (err instanceof ContentError) 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_CONTENT_CODES.has(maybeCode as ContentErrorCode)) {\n throw new ContentError(maybeCode as ContentErrorCode, reason);\n }\n throw new ContentError(\"UNKNOWN\", reason);\n }\n throw new ContentError(\"UNKNOWN\", String(err));\n}\n\n/**\n * Spotify Content namespace. Browse Spotify's curated content tree.\n * Requires `AppRemote.connect()` to be resolved before any call.\n *\n * @example\n * ```ts\n * import { Content } from \"@wwdrew/expo-spotify-sdk\";\n *\n * const items = await Content.getRecommendedContentItems(\"default\");\n * ```\n */\nexport const Content = {\n /**\n * Returns Spotify-curated recommended content for the given feed type.\n */\n getRecommendedContentItems(type: ContentType): Promise<ContentItem[]> {\n return ExpoSpotifySDKModule.contentGetRecommendedContentItems(type).catch(\n rethrowAsContentError,\n );\n },\n\n /**\n * Returns children of a browsable (container) content item.\n */\n getChildren(item: ContentItem): Promise<ContentItem[]> {\n return ExpoSpotifySDKModule.contentGetChildren(item).catch(rethrowAsContentError);\n },\n} as const;\n"]}
@@ -1,7 +1,7 @@
1
- import { ConnectionState } from "../app-remote";
2
- import { SpotifySession } from "../auth";
3
- import { PlayerState, Track } from "../player";
4
- import { Capabilities, LibraryState } from "../user";
1
+ import { type ConnectionState } from "../app-remote";
2
+ import { type SpotifySession } from "../auth";
3
+ import { type PlayerState, type Track } from "../player";
4
+ import { type Capabilities, type LibraryState } from "../user";
5
5
  import type { SpotifyURI as SpotifyURIType } from "../uri";
6
6
  /**
7
7
  * Returns the current Spotify OAuth session, or `null` when not authenticated.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,eAAe,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAQ,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAU,WAAW,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAQ,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,QAAQ,CAAC;AAmR3D;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,cAAc,GAAG,IAAI,CAElD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAMpD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,KAAK,GAAG,IAAI,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAMrD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY,GAAG,IAAI,CAMxE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAQ,MAAM,SAAS,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,QAAQ,CAAC;AAmR3D;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,cAAc,GAAG,IAAI,CAElD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAMpD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,KAAK,GAAG,IAAI,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAMrD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY,GAAG,IAAI,CAMxE"}
@@ -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, 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"]}
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 +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"]}
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,4 +1,4 @@
1
- import { EventSubscription } from "expo-modules-core";
1
+ import type { EventSubscription } from "expo-modules-core";
2
2
  import { SpotifyURI } from "../uri";
3
3
  export type { PlayerErrorCode } from "./error";
4
4
  export { PlayerError } from "./error";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/player/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/player/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import ExpoSpotifySDKModule from "../ExpoSpotifySDKModule";
2
+ import { SpotifyURI } from "../uri";
2
3
  import { PlayerError } from "./error";
3
4
  export { PlayerError } from "./error";
4
5
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/player/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventSubscription } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { SpotifyURI } from \"../uri\";\nimport { PlayerError, PlayerErrorCode } from \"./error\";\n\nexport type { PlayerErrorCode } from \"./error\";\nexport { PlayerError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Repeat mode for the Spotify player. */\nexport type RepeatMode =\n | 0 /** off */\n | 1 /** repeat current track */\n | 2; /** repeat current context */\n\n/** Valid podcast playback speed multipliers. */\nexport type PodcastPlaybackSpeed = 0.5 | 0.8 | 1.0 | 1.2 | 1.5 | 2.0 | 3.0;\n\n/** A Spotify artist. */\nexport interface Artist {\n name: string;\n uri: string;\n}\n\n/** A Spotify album. */\nexport interface Album {\n name: string;\n uri: string;\n}\n\n/** A track currently loaded in the Spotify player. */\nexport interface Track {\n uri: string;\n name: string;\n /** Identifier used by `Images.load(...)`. */\n imageIdentifier?: string;\n /** Duration in milliseconds. */\n duration: number;\n artist: Artist;\n album: Album;\n isSaved: boolean;\n isEpisode: boolean;\n isPodcast: boolean;\n isAdvertisement: boolean;\n}\n\n/** Current playback options (shuffle / repeat). */\nexport interface PlaybackOptions {\n isShuffling: boolean;\n /** 0 = off, 1 = repeat track, 2 = repeat context. */\n repeatMode: RepeatMode;\n}\n\n/** Actions currently permitted by Spotify (gate UI buttons on these). */\nexport interface PlaybackRestrictions {\n canSkipNext: boolean;\n canSkipPrevious: boolean;\n canRepeatTrack: boolean;\n canRepeatContext: boolean;\n canToggleShuffle: boolean;\n canSeek: boolean;\n}\n\n/** Full snapshot of the Spotify player at a point in time. */\nexport interface PlayerState {\n track: Track;\n /** Current playback position in milliseconds. */\n playbackPosition: number;\n playbackSpeed: number;\n isPaused: boolean;\n playbackOptions: PlaybackOptions;\n playbackRestrictions: PlaybackRestrictions;\n contextTitle: string;\n contextUri: string;\n}\n\n/** Crossfade configuration from the Spotify app. */\nexport interface CrossfadeState {\n isEnabled: boolean;\n /** Crossfade duration in milliseconds (only meaningful when enabled). */\n duration: number;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_PLAYER_CODES = new Set<PlayerErrorCode>([\n \"NOT_CONNECTED\",\n \"CONNECTION_LOST\",\n \"PREMIUM_REQUIRED\",\n \"INVALID_URI\",\n \"INVALID_PARAMETER\",\n \"OPERATION_NOT_ALLOWED\",\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 rethrowAsPlayerError(err: unknown): never {\n if (err instanceof PlayerError) 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_PLAYER_CODES.has(maybeCode as PlayerErrorCode)) {\n throw new PlayerError(maybeCode as PlayerErrorCode, reason);\n }\n throw new PlayerError(\"UNKNOWN\", reason);\n }\n throw new PlayerError(\"UNKNOWN\", String(err));\n}\n\n// ---------------------------------------------------------------------------\n// Player namespace\n// ---------------------------------------------------------------------------\n\n/**\n * Spotify Player namespace. Transport controls, queue management, and\n * player-state subscriptions. Requires `AppRemote.connect()` to be resolved\n * before any call.\n *\n * @example\n * ```ts\n * import { Player, SpotifyURI } from \"@wwdrew/expo-spotify-sdk\";\n *\n * await Player.play(SpotifyURI.from(\"spotify:track:4uLU6hMCjMI75M1A2tKUQC\"));\n * const state = await Player.getPlayerState();\n *\n * const sub = Player.addListener(\"playerStateChange\", (state) => {\n * console.log(\"now playing:\", state.track.name, \"paused:\", state.isPaused);\n * });\n * ```\n */\nexport const Player = {\n /**\n * Asks Spotify to play the entity identified by the given URI.\n * Requires Spotify Premium for on-demand track playback; throws\n * `PlayerError(\"PREMIUM_REQUIRED\", ...)` for Free users.\n */\n play(uri: SpotifyURI): Promise<void> {\n return ExpoSpotifySDKModule.playerPlay(uri).catch(rethrowAsPlayerError);\n },\n\n /** Pauses playback. */\n pause(): Promise<void> {\n return ExpoSpotifySDKModule.playerPause().catch(rethrowAsPlayerError);\n },\n\n /** Resumes paused playback. */\n resume(): Promise<void> {\n return ExpoSpotifySDKModule.playerResume().catch(rethrowAsPlayerError);\n },\n\n /** Skips to the next track in the queue or context. */\n skipNext(): Promise<void> {\n return ExpoSpotifySDKModule.playerSkipNext().catch(rethrowAsPlayerError);\n },\n\n /** Skips to the previous track. */\n skipPrevious(): Promise<void> {\n return ExpoSpotifySDKModule.playerSkipPrevious().catch(rethrowAsPlayerError);\n },\n\n /**\n * Seeks to the given position in milliseconds.\n * Only valid when `PlaybackRestrictions.canSeek` is `true`.\n */\n seekTo(positionMs: number): Promise<void> {\n return ExpoSpotifySDKModule.playerSeekTo(positionMs).catch(rethrowAsPlayerError);\n },\n\n /** Enables or disables shuffle. */\n setShuffle(enabled: boolean): Promise<void> {\n return ExpoSpotifySDKModule.playerSetShuffle(enabled).catch(rethrowAsPlayerError);\n },\n\n /**\n * Sets the repeat mode.\n * @param mode 0 = off, 1 = repeat track, 2 = repeat context.\n */\n setRepeatMode(mode: RepeatMode): Promise<void> {\n return ExpoSpotifySDKModule.playerSetRepeatMode(mode).catch(rethrowAsPlayerError);\n },\n\n /**\n * Sets the podcast playback speed. Only takes effect when a podcast episode\n * is playing; valid speeds are `0.5 | 0.8 | 1.0 | 1.2 | 1.5 | 2.0 | 3.0`.\n */\n setPodcastPlaybackSpeed(speed: PodcastPlaybackSpeed): Promise<void> {\n return ExpoSpotifySDKModule.playerSetPodcastPlaybackSpeed(speed).catch(\n rethrowAsPlayerError,\n );\n },\n\n /** Adds a track URI to the end of the current playback queue. */\n queue(uri: SpotifyURI): Promise<void> {\n return ExpoSpotifySDKModule.playerQueue(uri).catch(rethrowAsPlayerError);\n },\n\n /** Returns the current {@link PlayerState} as a one-shot pull. */\n getPlayerState(): Promise<PlayerState> {\n return ExpoSpotifySDKModule.playerGetPlayerState().catch(rethrowAsPlayerError);\n },\n\n /** Returns the current {@link CrossfadeState} as a one-shot pull. */\n getCrossfadeState(): Promise<CrossfadeState> {\n return ExpoSpotifySDKModule.playerGetCrossfadeState().catch(rethrowAsPlayerError);\n },\n\n /**\n * Subscribes to player state changes. The callback fires whenever the\n * Spotify app reports a state update (track change, pause/resume, seek,\n * shuffle/repeat change, etc.).\n *\n * Returns a `Subscription` — call `.remove()` to unsubscribe.\n *\n * @example\n * ```ts\n * const sub = Player.addListener(\"playerStateChange\", (state) => {\n * console.log(\"track:\", state.track.name, \"paused:\", state.isPaused);\n * });\n * // ...later:\n * sub.remove();\n * ```\n */\n addListener(\n event: \"playerStateChange\",\n listener: (state: PlayerState) => void,\n ): EventSubscription {\n return ExpoSpotifySDKModule.addListener(\n \"onPlayerStateChange\",\n listener as (event: unknown) => void,\n ) as EventSubscription;\n },\n} as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/player/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EventSubscription } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport { SpotifyURI } from \"../uri\";\nimport { PlayerError, type PlayerErrorCode } from \"./error\";\n\nexport type { PlayerErrorCode } from \"./error\";\nexport { PlayerError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Repeat mode for the Spotify player. */\nexport type RepeatMode =\n | 0 /** off */\n | 1 /** repeat current track */\n | 2; /** repeat current context */\n\n/** Valid podcast playback speed multipliers. */\nexport type PodcastPlaybackSpeed = 0.5 | 0.8 | 1.0 | 1.2 | 1.5 | 2.0 | 3.0;\n\n/** A Spotify artist. */\nexport interface Artist {\n name: string;\n uri: string;\n}\n\n/** A Spotify album. */\nexport interface Album {\n name: string;\n uri: string;\n}\n\n/** A track currently loaded in the Spotify player. */\nexport interface Track {\n uri: string;\n name: string;\n /** Identifier used by `Images.load(...)`. */\n imageIdentifier?: string;\n /** Duration in milliseconds. */\n duration: number;\n artist: Artist;\n album: Album;\n isSaved: boolean;\n isEpisode: boolean;\n isPodcast: boolean;\n isAdvertisement: boolean;\n}\n\n/** Current playback options (shuffle / repeat). */\nexport interface PlaybackOptions {\n isShuffling: boolean;\n /** 0 = off, 1 = repeat track, 2 = repeat context. */\n repeatMode: RepeatMode;\n}\n\n/** Actions currently permitted by Spotify (gate UI buttons on these). */\nexport interface PlaybackRestrictions {\n canSkipNext: boolean;\n canSkipPrevious: boolean;\n canRepeatTrack: boolean;\n canRepeatContext: boolean;\n canToggleShuffle: boolean;\n canSeek: boolean;\n}\n\n/** Full snapshot of the Spotify player at a point in time. */\nexport interface PlayerState {\n track: Track;\n /** Current playback position in milliseconds. */\n playbackPosition: number;\n playbackSpeed: number;\n isPaused: boolean;\n playbackOptions: PlaybackOptions;\n playbackRestrictions: PlaybackRestrictions;\n contextTitle: string;\n contextUri: string;\n}\n\n/** Crossfade configuration from the Spotify app. */\nexport interface CrossfadeState {\n isEnabled: boolean;\n /** Crossfade duration in milliseconds (only meaningful when enabled). */\n duration: number;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_PLAYER_CODES = new Set<PlayerErrorCode>([\n \"NOT_CONNECTED\",\n \"CONNECTION_LOST\",\n \"PREMIUM_REQUIRED\",\n \"INVALID_URI\",\n \"INVALID_PARAMETER\",\n \"OPERATION_NOT_ALLOWED\",\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 rethrowAsPlayerError(err: unknown): never {\n if (err instanceof PlayerError) 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_PLAYER_CODES.has(maybeCode as PlayerErrorCode)) {\n throw new PlayerError(maybeCode as PlayerErrorCode, reason);\n }\n throw new PlayerError(\"UNKNOWN\", reason);\n }\n throw new PlayerError(\"UNKNOWN\", String(err));\n}\n\n// ---------------------------------------------------------------------------\n// Player namespace\n// ---------------------------------------------------------------------------\n\n/**\n * Spotify Player namespace. Transport controls, queue management, and\n * player-state subscriptions. Requires `AppRemote.connect()` to be resolved\n * before any call.\n *\n * @example\n * ```ts\n * import { Player, SpotifyURI } from \"@wwdrew/expo-spotify-sdk\";\n *\n * await Player.play(SpotifyURI.from(\"spotify:track:4uLU6hMCjMI75M1A2tKUQC\"));\n * const state = await Player.getPlayerState();\n *\n * const sub = Player.addListener(\"playerStateChange\", (state) => {\n * console.log(\"now playing:\", state.track.name, \"paused:\", state.isPaused);\n * });\n * ```\n */\nexport const Player = {\n /**\n * Asks Spotify to play the entity identified by the given URI.\n * Requires Spotify Premium for on-demand track playback; throws\n * `PlayerError(\"PREMIUM_REQUIRED\", ...)` for Free users.\n */\n play(uri: SpotifyURI): Promise<void> {\n return ExpoSpotifySDKModule.playerPlay(uri).catch(rethrowAsPlayerError);\n },\n\n /** Pauses playback. */\n pause(): Promise<void> {\n return ExpoSpotifySDKModule.playerPause().catch(rethrowAsPlayerError);\n },\n\n /** Resumes paused playback. */\n resume(): Promise<void> {\n return ExpoSpotifySDKModule.playerResume().catch(rethrowAsPlayerError);\n },\n\n /** Skips to the next track in the queue or context. */\n skipNext(): Promise<void> {\n return ExpoSpotifySDKModule.playerSkipNext().catch(rethrowAsPlayerError);\n },\n\n /** Skips to the previous track. */\n skipPrevious(): Promise<void> {\n return ExpoSpotifySDKModule.playerSkipPrevious().catch(rethrowAsPlayerError);\n },\n\n /**\n * Seeks to the given position in milliseconds.\n * Only valid when `PlaybackRestrictions.canSeek` is `true`.\n */\n seekTo(positionMs: number): Promise<void> {\n return ExpoSpotifySDKModule.playerSeekTo(positionMs).catch(rethrowAsPlayerError);\n },\n\n /** Enables or disables shuffle. */\n setShuffle(enabled: boolean): Promise<void> {\n return ExpoSpotifySDKModule.playerSetShuffle(enabled).catch(rethrowAsPlayerError);\n },\n\n /**\n * Sets the repeat mode.\n * @param mode 0 = off, 1 = repeat track, 2 = repeat context.\n */\n setRepeatMode(mode: RepeatMode): Promise<void> {\n return ExpoSpotifySDKModule.playerSetRepeatMode(mode).catch(rethrowAsPlayerError);\n },\n\n /**\n * Sets the podcast playback speed. Only takes effect when a podcast episode\n * is playing; valid speeds are `0.5 | 0.8 | 1.0 | 1.2 | 1.5 | 2.0 | 3.0`.\n */\n setPodcastPlaybackSpeed(speed: PodcastPlaybackSpeed): Promise<void> {\n return ExpoSpotifySDKModule.playerSetPodcastPlaybackSpeed(speed).catch(\n rethrowAsPlayerError,\n );\n },\n\n /** Adds a track URI to the end of the current playback queue. */\n queue(uri: SpotifyURI): Promise<void> {\n return ExpoSpotifySDKModule.playerQueue(uri).catch(rethrowAsPlayerError);\n },\n\n /** Returns the current {@link PlayerState} as a one-shot pull. */\n getPlayerState(): Promise<PlayerState> {\n return ExpoSpotifySDKModule.playerGetPlayerState().catch(rethrowAsPlayerError);\n },\n\n /** Returns the current {@link CrossfadeState} as a one-shot pull. */\n getCrossfadeState(): Promise<CrossfadeState> {\n return ExpoSpotifySDKModule.playerGetCrossfadeState().catch(rethrowAsPlayerError);\n },\n\n /**\n * Subscribes to player state changes. The callback fires whenever the\n * Spotify app reports a state update (track change, pause/resume, seek,\n * shuffle/repeat change, etc.).\n *\n * Returns a `Subscription` — call `.remove()` to unsubscribe.\n *\n * @example\n * ```ts\n * const sub = Player.addListener(\"playerStateChange\", (state) => {\n * console.log(\"track:\", state.track.name, \"paused:\", state.isPaused);\n * });\n * // ...later:\n * sub.remove();\n * ```\n */\n addListener(\n event: \"playerStateChange\",\n listener: (state: PlayerState) => void,\n ): EventSubscription {\n return ExpoSpotifySDKModule.addListener(\n \"onPlayerStateChange\",\n listener as (event: unknown) => void,\n ) as EventSubscription;\n },\n} as const;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/uri/index.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,GACV,qEAAqE,CAAC;AAExE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;;;;OAIG;IACH,IAAI,CAAC,GAAW;QACd,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACb,yBAAyB,GAAG,wCAAwC;gBAClE,kEAAkE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO,GAAiB,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAW;QAChB,OAAO,GAAiB,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAe;QACnB,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,GAAG,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAwB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAyB,EAAE,EAAU;QACzC,OAAO,WAAW,IAAI,IAAI,EAAE,EAAgB,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,GAAW;QACjB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;CACO,CAAC","sourcesContent":["/**\n * The six Spotify resource types accepted by App Remote APIs.\n */\nexport type SpotifyResourceType =\n | \"track\"\n | \"album\"\n | \"playlist\"\n | \"artist\"\n | \"show\"\n | \"episode\";\n\n/**\n * Branded string type for Spotify URIs (`spotify:<type>:<id>`).\n *\n * Construct via `SpotifyURI.from(str)` (validates) or\n * `SpotifyURI.unsafe(str)` (skips validation — use only when the source is\n * trusted, e.g. a URI that came back from the Spotify SDK itself).\n */\nexport type SpotifyURI = string & { readonly __brand: \"SpotifyURI\" };\n\nconst URI_RE =\n /^spotify:(track|album|playlist|artist|show|episode):([A-Za-z0-9]+)$/;\n\n/**\n * Helpers for constructing, validating and decomposing {@link SpotifyURI}\n * values.\n *\n * @example\n * ```ts\n * const uri = SpotifyURI.from(\"spotify:track:4uLU6hMCjMI75M1A2tKUQC\");\n * const { type, id } = SpotifyURI.parse(uri);\n * const rebuilt = SpotifyURI.build(\"track\", id);\n * ```\n */\nexport const SpotifyURI = {\n /**\n * Cast `uri` to {@link SpotifyURI} after validation.\n * Throws a plain `Error` if the URI does not match the `spotify:<type>:<id>`\n * format — use at app-code call sites where you want early feedback.\n */\n from(uri: string): SpotifyURI {\n if (!SpotifyURI.isValid(uri)) {\n throw new Error(\n `Invalid Spotify URI: \"${uri}\". Expected spotify:<type>:<id> where ` +\n `<type> is one of: track, album, playlist, artist, show, episode.`,\n );\n }\n return uri as SpotifyURI;\n },\n\n /**\n * Cast `uri` to {@link SpotifyURI} without validation.\n * Use only for URIs that originate from the Spotify SDK itself (i.e. already\n * known-valid values coming back over the bridge).\n */\n unsafe(uri: string): SpotifyURI {\n return uri as SpotifyURI;\n },\n\n /**\n * Decompose a {@link SpotifyURI} into its `{ type, id }` parts.\n */\n parse(uri: SpotifyURI): { type: SpotifyResourceType; id: string } {\n const m = uri.match(URI_RE);\n if (!m) {\n throw new Error(`Cannot parse Spotify URI: \"${uri}\"`);\n }\n return { type: m[1] as SpotifyResourceType, id: m[2] };\n },\n\n /**\n * Build a {@link SpotifyURI} from a resource type and ID.\n */\n build(type: SpotifyResourceType, id: string): SpotifyURI {\n return `spotify:${type}:${id}` as SpotifyURI;\n },\n\n /**\n * Type-guard: returns `true` if `uri` is a valid Spotify URI string.\n */\n isValid(uri: string): uri is SpotifyURI {\n return URI_RE.test(uri);\n },\n} as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/uri/index.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,GACV,qEAAqE,CAAC;AAExE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;;;;OAIG;IACH,IAAI,CAAC,GAAW;QACd,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACb,yBAAyB,GAAG,wCAAwC;gBAClE,kEAAkE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO,GAAiB,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAW;QAChB,OAAO,GAAiB,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAe;QACnB,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,GAAG,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAwB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAyB,EAAE,EAAU;QACzC,OAAO,WAAW,IAAI,IAAI,EAAE,EAAgB,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,GAAW;QACjB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;CACO,CAAC","sourcesContent":["/**\n * The six Spotify resource types accepted by App Remote APIs.\n */\nexport type SpotifyResourceType =\n | \"track\"\n | \"album\"\n | \"playlist\"\n | \"artist\"\n | \"show\"\n | \"episode\";\n\n/**\n * Branded string type for Spotify URIs (`spotify:<type>:<id>`).\n *\n * Construct via `SpotifyURI.from(str)` (validates) or\n * `SpotifyURI.unsafe(str)` (skips validation — use only when the source is\n * trusted, e.g. a URI that came back from the Spotify SDK itself).\n */\nexport type SpotifyURI = string & { readonly __brand: \"SpotifyURI\" };\n\nconst URI_RE =\n /^spotify:(track|album|playlist|artist|show|episode):([A-Za-z0-9]+)$/;\n\n/**\n * Helpers for constructing, validating and decomposing {@link SpotifyURI}\n * values.\n *\n * @example\n * ```ts\n * const uri = SpotifyURI.from(\"spotify:track:4uLU6hMCjMI75M1A2tKUQC\");\n * const { type, id } = SpotifyURI.parse(uri);\n * const rebuilt = SpotifyURI.build(\"track\", id);\n * ```\n */\nexport const SpotifyURI = {\n /**\n * Cast `uri` to {@link SpotifyURI} after validation.\n * Throws a plain `Error` if the URI does not match the `spotify:<type>:<id>`\n * format — use at app-code call sites where you want early feedback.\n */\n from(uri: string): SpotifyURI {\n if (!SpotifyURI.isValid(uri)) {\n throw new Error(\n `Invalid Spotify URI: \"${uri}\". Expected spotify:<type>:<id> where ` +\n `<type> is one of: track, album, playlist, artist, show, episode.`,\n );\n }\n return uri as SpotifyURI;\n },\n\n /**\n * Cast `uri` to {@link SpotifyURI} without validation.\n * Use only for URIs that originate from the Spotify SDK itself (i.e. already\n * known-valid values coming back over the bridge).\n */\n unsafe(uri: string): SpotifyURI {\n return uri as SpotifyURI;\n },\n\n /**\n * Decompose a {@link SpotifyURI} into its `{ type, id }` parts.\n */\n parse(uri: SpotifyURI): { type: SpotifyResourceType; id: string } {\n const m = uri.match(URI_RE);\n if (!m) {\n throw new Error(`Cannot parse Spotify URI: \"${uri}\"`);\n }\n return { type: m[1] as SpotifyResourceType, id: m[2]! };\n },\n\n /**\n * Build a {@link SpotifyURI} from a resource type and ID.\n */\n build(type: SpotifyResourceType, id: string): SpotifyURI {\n return `spotify:${type}:${id}` as SpotifyURI;\n },\n\n /**\n * Type-guard: returns `true` if `uri` is a valid Spotify URI string.\n */\n isValid(uri: string): uri is SpotifyURI {\n return URI_RE.test(uri);\n },\n} as const;\n"]}
@@ -1,6 +1,6 @@
1
1
  export type { UserErrorCode } from "./error";
2
2
  export { UserError } from "./error";
3
- import { EventSubscription } from "expo-modules-core";
3
+ import type { EventSubscription } from "expo-modules-core";
4
4
  import type { SpotifyURI as SpotifyURIType } from "../uri";
5
5
  /** Capabilities of the current user in Spotify's App Remote context. */
6
6
  export interface Capabilities {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/user/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,QAAQ,CAAC;AAG3D,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;AA2CD;;;GAGG;AACH,eAAO,MAAM,IAAI;IACf,+CAA+C;oCAC5B,OAAO,CAAC,YAAY,CAAC;IAIxC,kEAAkE;oCAC7C,cAAc,KAAG,OAAO,CAAC,YAAY,CAAC;IAI3D,uDAAuD;iCAC/B,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IAOtD,4DAA4D;sCAC/B,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IAO3D;;;OAGG;kCAEM,oBAAoB,YACjB,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KACtC,iBAAiB;IAOpB;;;;;;;OAOG;4CAEI,cAAc,YACT,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KACtC,iBAAiB;CAiBZ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/user/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,QAAQ,CAAC;AAG3D,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;AA2CD;;;GAGG;AACH,eAAO,MAAM,IAAI;IACf,+CAA+C;oCAC5B,OAAO,CAAC,YAAY,CAAC;IAIxC,kEAAkE;oCAC7C,cAAc,KAAG,OAAO,CAAC,YAAY,CAAC;IAI3D,uDAAuD;iCAC/B,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IAOtD,4DAA4D;sCAC/B,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IAO3D;;;OAGG;kCAEM,oBAAoB,YACjB,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KACtC,iBAAiB;IAOpB;;;;;;;OAOG;4CAEI,cAAc,YACT,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KACtC,iBAAiB;CAiBZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user/index.ts"],"names":[],"mappings":"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","sourcesContent":["export type { UserErrorCode } from \"./error\";\nexport { UserError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nimport { EventSubscription } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport type { SpotifyURI as SpotifyURIType } from \"../uri\";\nimport { UserError, UserErrorCode } from \"./error\";\n\n/** Capabilities of the current user in Spotify's App Remote context. */\nexport interface Capabilities {\n /** `true` for Premium users with on-demand playback capability. */\n canPlayOnDemand: boolean;\n}\n\n/** Library save state for a specific album/track URI. */\nexport interface LibraryState {\n uri: string;\n isAdded: boolean;\n canAdd: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_USER_CODES = new Set<UserErrorCode>([\n \"NOT_CONNECTED\",\n \"CONNECTION_LOST\",\n \"INVALID_URI\",\n \"OPERATION_NOT_ALLOWED\",\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 rethrowAsUserError(err: unknown): never {\n if (err instanceof UserError) 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_USER_CODES.has(maybeCode as UserErrorCode)) {\n throw new UserError(maybeCode as UserErrorCode, reason);\n }\n throw new UserError(\"UNKNOWN\", reason);\n }\n throw new UserError(\"UNKNOWN\", String(err));\n}\n\ntype LibraryListener = (state: LibraryState) => void;\nconst libraryListeners = new Map<string, Set<LibraryListener>>();\n\nfunction notifyLibraryState(state: LibraryState) {\n const set = libraryListeners.get(state.uri);\n if (!set) return;\n set.forEach((listener) => listener(state));\n}\n\n/**\n * Spotify User namespace. Capabilities and library-state operations.\n * Requires `AppRemote.connect()` to be resolved before any call.\n */\nexport const User = {\n /** Returns the current user's capabilities. */\n getCapabilities(): Promise<Capabilities> {\n return ExpoSpotifySDKModule.userGetCapabilities().catch(rethrowAsUserError);\n },\n\n /** Returns the current library state for a track or album URI. */\n getLibraryState(uri: SpotifyURIType): Promise<LibraryState> {\n return ExpoSpotifySDKModule.userGetLibraryState(uri).catch(rethrowAsUserError);\n },\n\n /** Adds a track or album URI to the user's library. */\n async addToLibrary(uri: SpotifyURIType): Promise<void> {\n const state = (await ExpoSpotifySDKModule.userAddToLibrary(uri).catch(\n rethrowAsUserError,\n )) as LibraryState;\n notifyLibraryState(state);\n },\n\n /** Removes a track or album URI from the user's library. */\n async removeFromLibrary(uri: SpotifyURIType): Promise<void> {\n const state = (await ExpoSpotifySDKModule.userRemoveFromLibrary(uri).catch(\n rethrowAsUserError,\n )) as LibraryState;\n notifyLibraryState(state);\n },\n\n /**\n * Subscribes to user-scoped events.\n * Supported event: `\"capabilitiesChange\"`.\n */\n addListener(\n event: \"capabilitiesChange\",\n listener: (event: Capabilities) => void,\n ): EventSubscription {\n return ExpoSpotifySDKModule.addListener(\n \"onCapabilitiesChange\",\n listener as (event: unknown) => void,\n ) as EventSubscription;\n },\n\n /**\n * Subscribes to library state changes for a specific URI.\n *\n * There is no native push stream for per-URI library state updates on all\n * platforms, so this listener is updated whenever library mutations are\n * performed via this SDK and can be manually seeded by calling\n * `User.getLibraryState(uri)` before subscribing.\n */\n addLibraryStateListener(\n uri: SpotifyURIType,\n listener: (state: LibraryState) => void,\n ): EventSubscription {\n const key = String(uri);\n let set = libraryListeners.get(key);\n if (!set) {\n set = new Set();\n libraryListeners.set(key, set);\n }\n set.add(listener);\n return {\n remove() {\n const current = libraryListeners.get(key);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) libraryListeners.delete(key);\n },\n } as EventSubscription;\n },\n} as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user/index.ts"],"names":[],"mappings":"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","sourcesContent":["export type { UserErrorCode } from \"./error\";\nexport { UserError } from \"./error\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nimport type { EventSubscription } from \"expo-modules-core\";\n\nimport ExpoSpotifySDKModule from \"../ExpoSpotifySDKModule\";\nimport type { SpotifyURI as SpotifyURIType } from \"../uri\";\nimport { UserError, type UserErrorCode } from \"./error\";\n\n/** Capabilities of the current user in Spotify's App Remote context. */\nexport interface Capabilities {\n /** `true` for Premium users with on-demand playback capability. */\n canPlayOnDemand: boolean;\n}\n\n/** Library save state for a specific album/track URI. */\nexport interface LibraryState {\n uri: string;\n isAdded: boolean;\n canAdd: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_USER_CODES = new Set<UserErrorCode>([\n \"NOT_CONNECTED\",\n \"CONNECTION_LOST\",\n \"INVALID_URI\",\n \"OPERATION_NOT_ALLOWED\",\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 rethrowAsUserError(err: unknown): never {\n if (err instanceof UserError) 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_USER_CODES.has(maybeCode as UserErrorCode)) {\n throw new UserError(maybeCode as UserErrorCode, reason);\n }\n throw new UserError(\"UNKNOWN\", reason);\n }\n throw new UserError(\"UNKNOWN\", String(err));\n}\n\ntype LibraryListener = (state: LibraryState) => void;\nconst libraryListeners = new Map<string, Set<LibraryListener>>();\n\nfunction notifyLibraryState(state: LibraryState) {\n const set = libraryListeners.get(state.uri);\n if (!set) return;\n set.forEach((listener) => listener(state));\n}\n\n/**\n * Spotify User namespace. Capabilities and library-state operations.\n * Requires `AppRemote.connect()` to be resolved before any call.\n */\nexport const User = {\n /** Returns the current user's capabilities. */\n getCapabilities(): Promise<Capabilities> {\n return ExpoSpotifySDKModule.userGetCapabilities().catch(rethrowAsUserError);\n },\n\n /** Returns the current library state for a track or album URI. */\n getLibraryState(uri: SpotifyURIType): Promise<LibraryState> {\n return ExpoSpotifySDKModule.userGetLibraryState(uri).catch(rethrowAsUserError);\n },\n\n /** Adds a track or album URI to the user's library. */\n async addToLibrary(uri: SpotifyURIType): Promise<void> {\n const state = (await ExpoSpotifySDKModule.userAddToLibrary(uri).catch(\n rethrowAsUserError,\n )) as LibraryState;\n notifyLibraryState(state);\n },\n\n /** Removes a track or album URI from the user's library. */\n async removeFromLibrary(uri: SpotifyURIType): Promise<void> {\n const state = (await ExpoSpotifySDKModule.userRemoveFromLibrary(uri).catch(\n rethrowAsUserError,\n )) as LibraryState;\n notifyLibraryState(state);\n },\n\n /**\n * Subscribes to user-scoped events.\n * Supported event: `\"capabilitiesChange\"`.\n */\n addListener(\n event: \"capabilitiesChange\",\n listener: (event: Capabilities) => void,\n ): EventSubscription {\n return ExpoSpotifySDKModule.addListener(\n \"onCapabilitiesChange\",\n listener as (event: unknown) => void,\n ) as EventSubscription;\n },\n\n /**\n * Subscribes to library state changes for a specific URI.\n *\n * There is no native push stream for per-URI library state updates on all\n * platforms, so this listener is updated whenever library mutations are\n * performed via this SDK and can be manually seeded by calling\n * `User.getLibraryState(uri)` before subscribing.\n */\n addLibraryStateListener(\n uri: SpotifyURIType,\n listener: (state: LibraryState) => void,\n ): EventSubscription {\n const key = String(uri);\n let set = libraryListeners.get(key);\n if (!set) {\n set = new Set();\n libraryListeners.set(key, set);\n }\n set.add(listener);\n return {\n remove() {\n const current = libraryListeners.get(key);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) libraryListeners.delete(key);\n },\n } as EventSubscription;\n },\n} as const;\n"]}
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
- s.platform = :ios, '15.1'
13
+ s.platform = :ios, '16.4'
14
14
  s.swift_version = '5.9'
15
15
  s.source = { git: 'https://github.com/wwdrew/expo-spotify-sdk' }
16
16
  s.static_framework = true
@@ -1,7 +1,7 @@
1
1
  import ExpoModulesCore
2
2
  import SpotifyiOS
3
3
 
4
- private let SDK_VERSION = "1.0.0" // x-release-please-version
4
+ private let SDK_VERSION = "2.0.0" // x-release-please-version
5
5
  private let EVENT_SESSION_CHANGE = "onSessionChange"
6
6
  private let EVENT_CONNECTION_STATE_CHANGE = "onConnectionStateChange"
7
7
  private let EVENT_CONNECTION_ERROR = "onConnectionError"
package/package.json CHANGED
@@ -1,18 +1,27 @@
1
1
  {
2
2
  "name": "@wwdrew/expo-spotify-sdk",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Expo module wrapping the native Spotify iOS (v5) and Android (v4) SDKs for OAuth authentication and App Remote playback control",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./build/index.d.ts",
10
+ "default": "./build/index.js"
11
+ },
12
+ "./plugin": {
13
+ "types": "./plugin/build/index.d.ts",
14
+ "default": "./plugin/index.js"
15
+ }
16
+ },
7
17
  "scripts": {
8
- "build": "expo-module build",
9
- "clean": "expo-module clean",
10
- "lint": "expo-module lint",
11
- "test": "expo-module test plugin",
12
- "prepare": "expo-module prepare",
13
- "prepublishOnly": "expo-module prepublishOnly",
14
- "typecheck": "expo-module typecheck",
15
- "expo-module": "expo-module",
18
+ "build": "tsc",
19
+ "build:plugin": "tsc -p plugin/tsconfig.json",
20
+ "clean": "rm -rf build plugin/build",
21
+ "lint": "eslint src",
22
+ "test": "jest --rootDir plugin --config plugin/jest.config.js",
23
+ "prepublishOnly": "yarn clean && yarn build && yarn build:plugin",
24
+ "typecheck": "tsc --noEmit",
16
25
  "open:ios": "open -a \"Xcode\" example/ios",
17
26
  "open:android": "open -a \"Android Studio\" example/android"
18
27
  },
@@ -39,13 +48,16 @@
39
48
  "license": "MIT",
40
49
  "homepage": "https://github.com/wwdrew/expo-spotify-sdk#readme",
41
50
  "devDependencies": {
42
- "@expo/config-plugins": "^55.0.8",
43
- "@types/react": "^18.0.25",
51
+ "@babel/runtime": "^7.29.7",
52
+ "@expo/config-plugins": "^56.0.8",
53
+ "@types/react": "^19.0.0",
54
+ "babel-preset-expo": "~56.0.0",
44
55
  "eslint": "^9.0.0",
45
- "expo-module-scripts": "^55.0.2",
46
- "expo-modules-core": "^3.0.30",
56
+ "expo-module-scripts": "^56.0.2",
57
+ "expo-modules-core": "^56.0.13",
47
58
  "jest": "^29.7.0",
48
- "prettier": "^3.0.0"
59
+ "prettier": "^3.0.0",
60
+ "typescript": "^5.9.0"
49
61
  },
50
62
  "peerDependencies": {
51
63
  "expo": "*",
@@ -1,4 +1,21 @@
1
- import { ConfigPlugin } from "@expo/config-plugins";
2
1
  import { SpotifyConfig } from "./types";
3
- export declare const withSpotifySdkConfig: ConfigPlugin<SpotifyConfig>;
4
- export default withSpotifySdkConfig;
2
+ /**
3
+ * Typed config plugin helper for `app.config.ts` (Expo SDK 56+).
4
+ *
5
+ * @example
6
+ * import withSpotifySdk from "@wwdrew/expo-spotify-sdk/plugin";
7
+ *
8
+ * export default ({ config }) => ({
9
+ * ...config,
10
+ * plugins: [
11
+ * withSpotifySdk({
12
+ * clientID: "your-spotify-client-id",
13
+ * scheme: "myapp",
14
+ * host: "spotify-auth",
15
+ * }),
16
+ * ],
17
+ * });
18
+ */
19
+ declare const _default: (props: SpotifyConfig) => [string, SpotifyConfig];
20
+ export default _default;
21
+ export type { SpotifyConfig, SpotifyScopes } from "./types";
@@ -1,27 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withSpotifySdkConfig = void 0;
4
- const withSpotifyAndroidAppBuildGradle_1 = require("./android/withSpotifyAndroidAppBuildGradle");
5
- const withSpotifyConfigValues_1 = require("./ios/withSpotifyConfigValues");
6
- const withSpotifyQueryScheme_1 = require("./ios/withSpotifyQueryScheme");
7
- const withSpotifyURLScheme_1 = require("./ios/withSpotifyURLScheme");
8
- const withSpotifySdkConfig = (config, spotifyConfig) => {
9
- if (!spotifyConfig.host) {
10
- throw new Error("Missing required Spotify config value: host");
11
- }
12
- if (!spotifyConfig.scheme) {
13
- throw new Error("Missing required Spotify config value: scheme");
14
- }
15
- if (!spotifyConfig.clientID) {
16
- throw new Error("Missing required Spotify config value: clientID");
17
- }
18
- // Android specific
19
- config = (0, withSpotifyAndroidAppBuildGradle_1.withSpotifyAndroidAppBuildGradle)(config, spotifyConfig);
20
- // iOS specific
21
- config = (0, withSpotifyConfigValues_1.withSpotifyConfigValues)(config, spotifyConfig);
22
- config = (0, withSpotifyQueryScheme_1.withSpotifyQueryScheme)(config, spotifyConfig);
23
- config = (0, withSpotifyURLScheme_1.withSpotifyURLScheme)(config, spotifyConfig);
24
- return config;
25
- };
26
- exports.withSpotifySdkConfig = withSpotifySdkConfig;
27
- exports.default = exports.withSpotifySdkConfig;
3
+ const PACKAGE_NAME = "@wwdrew/expo-spotify-sdk";
4
+ /**
5
+ * Typed config plugin helper for `app.config.ts` (Expo SDK 56+).
6
+ *
7
+ * @example
8
+ * import withSpotifySdk from "@wwdrew/expo-spotify-sdk/plugin";
9
+ *
10
+ * export default ({ config }) => ({
11
+ * ...config,
12
+ * plugins: [
13
+ * withSpotifySdk({
14
+ * clientID: "your-spotify-client-id",
15
+ * scheme: "myapp",
16
+ * host: "spotify-auth",
17
+ * }),
18
+ * ],
19
+ * });
20
+ */
21
+ exports.default = (props) => [
22
+ PACKAGE_NAME,
23
+ props,
24
+ ];
@@ -8,7 +8,23 @@ export type SpotifyScopes = "ugc-image-upload" | "user-read-playback-state" | "u
8
8
  * Configuration accepted by the `@wwdrew/expo-spotify-sdk` Expo config plugin.
9
9
  *
10
10
  * @example
11
- * // app.config.ts
11
+ * // app.config.ts (Expo SDK 56+ — typed import)
12
+ * import withSpotifySdk from "@wwdrew/expo-spotify-sdk/plugin";
13
+ *
14
+ * export default ({ config }) => ({
15
+ * ...config,
16
+ * plugins: [
17
+ * withSpotifySdk({
18
+ * clientID: "<spotify-client-id>",
19
+ * scheme: "myapp",
20
+ * host: "spotify-auth",
21
+ * redirectPathPattern: ".*",
22
+ * }),
23
+ * ],
24
+ * });
25
+ *
26
+ * @example
27
+ * // app.config.ts / app.json (string tuple — all SDK versions)
12
28
  * export default {
13
29
  * plugins: [
14
30
  * ["@wwdrew/expo-spotify-sdk", {
@@ -0,0 +1,4 @@
1
+ import { ConfigPlugin } from "@expo/config-plugins";
2
+ import { SpotifyConfig } from "./types";
3
+ export declare const withSpotifySdkConfig: ConfigPlugin<SpotifyConfig>;
4
+ export default withSpotifySdkConfig;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withSpotifySdkConfig = void 0;
4
+ const withSpotifyAndroidAppBuildGradle_1 = require("./android/withSpotifyAndroidAppBuildGradle");
5
+ const withSpotifyConfigValues_1 = require("./ios/withSpotifyConfigValues");
6
+ const withSpotifyQueryScheme_1 = require("./ios/withSpotifyQueryScheme");
7
+ const withSpotifyURLScheme_1 = require("./ios/withSpotifyURLScheme");
8
+ const withSpotifySdkConfig = (config, spotifyConfig) => {
9
+ if (!spotifyConfig.host) {
10
+ throw new Error("Missing required Spotify config value: host");
11
+ }
12
+ if (!spotifyConfig.scheme) {
13
+ throw new Error("Missing required Spotify config value: scheme");
14
+ }
15
+ if (!spotifyConfig.clientID) {
16
+ throw new Error("Missing required Spotify config value: clientID");
17
+ }
18
+ // Android specific
19
+ config = (0, withSpotifyAndroidAppBuildGradle_1.withSpotifyAndroidAppBuildGradle)(config, spotifyConfig);
20
+ // iOS specific
21
+ config = (0, withSpotifyConfigValues_1.withSpotifyConfigValues)(config, spotifyConfig);
22
+ config = (0, withSpotifyQueryScheme_1.withSpotifyQueryScheme)(config, spotifyConfig);
23
+ config = (0, withSpotifyURLScheme_1.withSpotifyURLScheme)(config, spotifyConfig);
24
+ return config;
25
+ };
26
+ exports.withSpotifySdkConfig = withSpotifySdkConfig;
27
+ exports.default = exports.withSpotifySdkConfig;