From 96578ca331dd9025b589e6cd15b68c2a7b40e0be Mon Sep 17 00:00:00 2001 From: Emmett Miller Date: Wed, 16 Apr 2025 18:23:54 -0400 Subject: [PATCH] Added support for universal auth, removes auth flows, adds getSessionToken to the createClient, removes the supabase adapter --- .DS_Store | Bin 0 -> 6148 bytes README.md | 167 +++++++++--------- package.json | 20 +-- pnpm-lock.yaml | 114 ------------ src/.DS_Store | Bin 0 -> 6148 bytes src/Update.ts | 45 +++++ ...pdateBillingClient.ts => UpdateBilling.ts} | 144 +++++++++------ ...tlementClient.ts => UpdateEntitlements.ts} | 51 ++++-- src/constants/index.ts | 1 - src/createClient.ts | 9 + src/index.ts | 2 + .../supabase/UpdateOrganizationClient.ts | 122 ------------- src/providers/supabase/UpdateSupabaseAuth.ts | 113 ------------ .../supabase/UpdateSupabaseClient.ts | 107 ----------- src/providers/supabase/createClient.ts | 25 --- src/providers/supabase/index.ts | 4 - src/providers/supabase/types/auth.ts | 54 ------ src/providers/supabase/types/error.ts | 5 - src/providers/supabase/types/options.ts | 9 - src/providers/supabase/types/organization.ts | 33 ---- src/providers/supabase/types/response.ts | 11 -- src/providers/supabase/types/supabase.ts | 1 - src/types/index.ts | 2 - src/types/internal.ts | 1 + src/types/membership.ts | 6 - src/types/options.ts | 14 +- src/types/organization.ts | 4 - src/{providers/supabase => }/types/request.ts | 11 +- src/utils/object.ts | 11 ++ src/utils/request.ts | 134 +++++++------- 30 files changed, 365 insertions(+), 855 deletions(-) create mode 100644 .DS_Store create mode 100644 src/.DS_Store create mode 100644 src/Update.ts rename src/{providers/supabase/UpdateBillingClient.ts => UpdateBilling.ts} (64%) rename src/{providers/supabase/UpdateEntitlementClient.ts => UpdateEntitlements.ts} (55%) delete mode 100644 src/constants/index.ts create mode 100644 src/createClient.ts delete mode 100644 src/providers/supabase/UpdateOrganizationClient.ts delete mode 100644 src/providers/supabase/UpdateSupabaseAuth.ts delete mode 100644 src/providers/supabase/UpdateSupabaseClient.ts delete mode 100644 src/providers/supabase/createClient.ts delete mode 100644 src/providers/supabase/index.ts delete mode 100644 src/providers/supabase/types/auth.ts delete mode 100644 src/providers/supabase/types/error.ts delete mode 100644 src/providers/supabase/types/options.ts delete mode 100644 src/providers/supabase/types/organization.ts delete mode 100644 src/providers/supabase/types/response.ts delete mode 100644 src/providers/supabase/types/supabase.ts delete mode 100644 src/types/membership.ts delete mode 100644 src/types/organization.ts rename src/{providers/supabase => }/types/request.ts (79%) create mode 100644 src/utils/object.ts diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5172429f264de2441865cb4700216d4256da9242 GIT binary patch literal 6148 zcmeH~J!%6%427R!7lt%jx}3%b$PET#pTHLgIFQEJ;E>dF^gR7ES*H$5cmnB-G%I%Z zD|S`@Z2$T80!#olbXV*=%*>dt@PRwdU#I)^a=X5>;#J@&VrHyNnC;iLL0pQvfVyTmjO&;ssLc!1UOG})p;=82 zR;?Ceh}WZ?+UmMqI#RP8R>OzYoz15hnq@nzF`-!xQ4j$Um=RcIKKc27r2jVm&svm< zfC&6E0=7P!4tu^-ovjbA=k?dB`g+i*aXG_}p8zI)6mRKa+;6_1_R^8c3Qa!(fk8n8 H{*=HsM+*^= literal 0 HcmV?d00001 diff --git a/README.md b/README.md index d1877c4..a612c9d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Update JS Library -Update is a library for seamless billing, authentication, and entitlement management. It extends your existing tools like Supabase and Stripe so you can integrate without migrating away from your existing stack. +Update is a library for seamless billing and entitlement management. It integrates with your existing tools, like Stripe and Supabase, so you can integrate without migrating away from your existing stack. ## 🚀 Quickstart The easiest way to get started with Update is to use the `create-update-app` command: ```bash -npx create-update-app@latest +npm create update@latest ``` This tool will help you choose a framework and set up a fully working Update application in seconds. Just provide a name and your API keys. @@ -16,7 +16,6 @@ For source code examples, check out our [examples repository](https://github.com ## ✨ Features -- **Authentication**: Easy integration with your auth providers - **Billing**: Seamless payments management - **Entitlements**: Simple access control for premium features - **Framework Support**: Built-in integration for Next.js and other SSR environments @@ -27,30 +26,54 @@ For source code examples, check out our [examples repository](https://github.com npm install @updatedev/js ``` -For SSR environments like Next.js: +## 🔐 Auth Integrations -```bash -npm install @updatedev/js @updatedev/ssr -``` +- 🐘 [Supabase](https://supabase.com?utm_source=update&utm_medium=referral&utm_campaign=update-js-readme) +- 🔥 [Firebase](https://firebase.google.com?utm_source=update&utm_medium=referral&utm_campaign=update-js-readme) +- 🔐 [Clerk](https://clerk.com?utm_source=update&utm_medium=referral&utm_campaign=update-js-readme) +- ⚙️ Custom -## 🔑 Initializing +## 🏁 Getting Started -### Basic Setup +First, you need to create an account on [Update](https://update.dev) and obtain your publishable key. This key is essential for initializing the Update client in your application. Additionally, configure your preferred authentication provider to manage user sessions and access control. -```javascript -import { createClient } from "@updatedev/js/supabase"; +## ⚙️ Initializing -const update = createClient({ - process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!, - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! -}); +### Basic Setup + +```typescript +import { createClient } from '@updatedev/js'; + +export async function createUpdateClient() { + return createClient(process.env.NEXT_PUBLIC_UPDATE_PUBLISHABLE_KEY!, { + getSessionToken: async () => { + // This must be replaced with your own logic to get your session token + // For example, with Supabase: + // + // import { createSupabaseClient } from '@/utils/supabase/client' + // ... + // const supabase = createSupabaseClient() + // const { data } = await supabase.auth.getSession() + // if (data.session == null) return + // return data.session.access_token + + // For this example, we'll just return a static token + return 'your-session-token'; + }, + environment: process.env.NODE_ENV === 'production' ? 'live' : 'test', + }); +} ``` +### Initialization options + +- `getSessionToken`: A function that returns a session token for the user. This is optional, but required for most functions that require authentication. +- `environment`: The environment to use for the client. Valid values are `live` and `test`. + ### Environment Variables (.env.local) ``` -NEXT_PUBLIC_UPDATE_PUBLIC_KEY= +NEXT_PUBLIC_UPDATE_PUBLISHABLE_KEY= NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= ``` @@ -64,66 +87,51 @@ Update works well with Next.js and other SSR environments. Create a `utils/updat #### Client (utils/update/client.ts) ```typescript -import { createBrowserClient } from '@updatedev/ssr/supabase'; - -export function createClient() { - return createBrowserClient( - process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!, - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! - ); +import { createClient } from '@updatedev/js'; + +export async function createUpdateClient() { + return createClient(process.env.NEXT_PUBLIC_UPDATE_PUBLISHABLE_KEY!, { + getSessionToken: async () => { + // This must be replaced with your own logic to get your session token + // For example, with Supabase: + // + // import { createSupabaseClient } from '@/utils/supabase/client' + // ... + // const supabase = createSupabaseClient() + // const { data } = await supabase.auth.getSession() + // if (data.session == null) return + // return data.session.access_token + + // For this example, we'll just return a static token + return 'your-session-token'; + }, + environment: process.env.NODE_ENV === 'production' ? 'live' : 'test', + }); } ``` -#### Middleware (middleware.ts in root) - -```typescript -import { type NextRequest } from 'next/server'; -import { updateSession } from '@/utils/update/middleware'; - -export async function middleware(request: NextRequest) { - return await updateSession(request); -} - -export const config = { - matcher: [ - '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', - ], -}; -``` - #### Server (utils/update/server.ts) ```typescript -import { createServerClient } from '@updatedev/ssr/supabase'; -import { cookies } from 'next/headers'; - -export async function createClient() { - const cookieStore = await cookies(); - - return createServerClient( - process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!, - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - { - cookies: { - getAll() { - return cookieStore.getAll(); - }, - setAll(cookiesToSet) { - try { - cookiesToSet.forEach(({ name, value, options }) => - cookieStore.set(name, value, options) - ); - } catch { - // The `setAll` method was called from a Server Component. - // This can be ignored if you have middleware refreshing - // user sessions. - } - }, - }, - } - ); +import { createClient } from '@updatedev/js'; + +export async function createUpdateClient() { + return createClient(process.env.NEXT_PUBLIC_UPDATE_PUBLISHABLE_KEY!, { + getSessionToken: async () => { + // This must be replaced with your own logic to get your session token + // For example, with Supabase: + // + // import { createSupabaseClient } from '@/utils/supabase/server' + // const supabase = await createSupabaseClient() + // const { data } = await supabase.auth.getSession() + // if (data.session == null) return + // return data.session.access_token + + // For this example, we'll just return a static token + return 'your-session-token'; + }, + environment: process.env.NODE_ENV === 'production' ? 'live' : 'test', + }); } ``` @@ -181,23 +189,6 @@ const { data, error } = await client.entitlements.list(); const { data, error } = await client.entitlements.check('premium'); ``` -## 🔐 Authentication Extensions - -### Creating Auth Flow Links - -```typescript -const { data, error } = await client.auth.createAuthFlowLink({ - type: 'sign-in', -}); -window.location.href = data.url; -``` - -### Verifying Auth Flow Links - -```typescript -const { error } = await client.auth.verifyAuthFlowCode(code); -``` - ## 📚 Documentation For complete documentation, visit [our documentation](https://update.dev/docs). diff --git a/package.json b/package.json index 744036e..b568322 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { "name": "@updatedev/js", - "version": "0.3.4", + "version": "0.4.0", "description": "Update JavaScript SDK", - "main": "./dist/index.js", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/types/index.d.ts", "homepage": "https://update.dev", "bugs": { "url": "https://github.com/updatedotdev/js/issues" @@ -18,18 +20,6 @@ "files": [ "dist/**/*" ], - "exports": { - ".": { - "types": "./dist/types/index.d.ts", - "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.js" - }, - "./supabase": { - "types": "./dist/types/providers/supabase/index.d.ts", - "import": "./dist/esm/providers/supabase/index.js", - "require": "./dist/cjs/providers/supabase/index.js" - } - }, "scripts": { "clean": "rimraf ./dist", "build": "npm run clean && tsup", @@ -63,8 +53,6 @@ "tsup": "^8.4.0" }, "dependencies": { - "@supabase/auth-js": "^2.68.0", - "@supabase/supabase-js": "^2.49.1", "cookie": "^1.0.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecd9388..ac2271f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,6 @@ importers: .: dependencies: - '@supabase/auth-js': - specifier: ^2.68.0 - version: 2.68.0 - '@supabase/supabase-js': - specifier: ^2.49.1 - version: 2.49.1 cookie: specifier: ^1.0.2 version: 1.0.2 @@ -361,28 +355,6 @@ packages: cpu: [x64] os: [win32] - '@supabase/auth-js@2.68.0': - resolution: {integrity: sha512-odG7nb7aOmZPUXk6SwL2JchSsn36Ppx11i2yWMIc/meUO2B2HK9YwZHPK06utD9Ql9ke7JKDbwGin/8prHKxxQ==} - - '@supabase/functions-js@2.4.4': - resolution: {integrity: sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==} - - '@supabase/node-fetch@2.6.15': - resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} - engines: {node: 4.x || >=6.0.0} - - '@supabase/postgrest-js@1.19.2': - resolution: {integrity: sha512-MXRbk4wpwhWl9IN6rIY1mR8uZCCG4MZAEji942ve6nMwIqnBgBnZhZlON6zTTs6fgveMnoCILpZv1+K91jN+ow==} - - '@supabase/realtime-js@2.11.2': - resolution: {integrity: sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==} - - '@supabase/storage-js@2.7.1': - resolution: {integrity: sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==} - - '@supabase/supabase-js@2.49.1': - resolution: {integrity: sha512-lKaptKQB5/juEF5+jzmBeZlz69MdHZuxf+0f50NwhL+IE//m4ZnOeWlsKRjjsM0fVayZiQKqLvYdBn0RLkhGiQ==} - '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -392,12 +364,6 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/phoenix@1.6.6': - resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==} - - '@types/ws@8.18.0': - resolution: {integrity: sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==} - '@typescript-eslint/eslint-plugin@4.33.0': resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1188,9 +1154,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -1251,15 +1214,9 @@ packages: v8-compile-cache@2.4.0: resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -1283,18 +1240,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.1: - resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - snapshots: '@babel/code-frame@7.12.11': @@ -1507,60 +1452,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.35.0': optional: true - '@supabase/auth-js@2.68.0': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/functions-js@2.4.4': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/node-fetch@2.6.15': - dependencies: - whatwg-url: 5.0.0 - - '@supabase/postgrest-js@1.19.2': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/realtime-js@2.11.2': - dependencies: - '@supabase/node-fetch': 2.6.15 - '@types/phoenix': 1.6.6 - '@types/ws': 8.18.0 - ws: 8.18.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@supabase/storage-js@2.7.1': - dependencies: - '@supabase/node-fetch': 2.6.15 - - '@supabase/supabase-js@2.49.1': - dependencies: - '@supabase/auth-js': 2.68.0 - '@supabase/functions-js': 2.4.4 - '@supabase/node-fetch': 2.6.15 - '@supabase/postgrest-js': 1.19.2 - '@supabase/realtime-js': 2.11.2 - '@supabase/storage-js': 2.7.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} '@types/node@12.20.55': {} - '@types/phoenix@1.6.6': {} - - '@types/ws@8.18.0': - dependencies: - '@types/node': 12.20.55 - '@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.8.2))(eslint@7.32.0)(typescript@5.8.2)': dependencies: '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@5.8.2) @@ -2353,8 +2250,6 @@ snapshots: dependencies: is-number: 7.0.0 - tr46@0.0.3: {} - tr46@1.0.1: dependencies: punycode: 2.3.1 @@ -2412,15 +2307,8 @@ snapshots: v8-compile-cache@2.4.0: {} - webidl-conversions@3.0.1: {} - webidl-conversions@4.0.2: {} - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 @@ -2446,5 +2334,3 @@ snapshots: strip-ansi: 7.1.0 wrappy@1.0.2: {} - - ws@8.18.1: {} diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 { - const { data, error } = await this.requestClient.request({ - endpoint: '/billing/checkout/create', - method: 'POST', - body: { - id, - options, - }, - headers: { - [ENVIRONMENT_HEADER]: this.environment, - }, - }); - - if (error) { - return { - data: { - url: null, - }, - error: { - message: error.message, - }, - }; - } - - return { - data: { - url: data, - }, - error: null, - }; + this.hasSessionToken = hasSessionToken; } async getProducts(): Promise { @@ -67,9 +31,6 @@ export class UpdateBillingClient { >({ endpoint: '/billing/products', method: 'GET', - headers: { - [ENVIRONMENT_HEADER]: this.environment, - }, }); if (error) { @@ -92,11 +53,25 @@ export class UpdateBillingClient { } async getSubscriptions(): Promise { + if (!this.hasSessionToken) { + console.warn( + '@updatedev/js: billing.getSubscriptions() called without a session token. You need to add `getSessionToken` to createClient().' + ); + return { + data: { + subscriptions: null, + }, + error: { + message: 'No session token', + }, + }; + } + const { data, error } = await this.requestClient.request({ endpoint: '/billing/subscriptions', method: 'GET', - headers: { - [ENVIRONMENT_HEADER]: this.environment, + extra: { + includeUser: true, }, }); @@ -123,6 +98,20 @@ export class UpdateBillingClient { id: string, { cancel_at_period_end }: UpdateSubscriptionRequest ): Promise { + if (!this.hasSessionToken) { + console.warn( + '@updatedev/js: billing.updateSubscription() called without a session token. You need to add `getSessionToken` to createClient().' + ); + return { + data: { + subscription: null, + }, + error: { + message: 'No session token', + }, + }; + } + const { data, error } = await this.requestClient.request({ endpoint: '/billing/subscriptions/update', method: 'POST', @@ -130,8 +119,8 @@ export class UpdateBillingClient { id, cancel_at_period_end, }, - headers: { - [ENVIRONMENT_HEADER]: this.environment, + extra: { + includeUser: true, }, }); @@ -153,4 +142,53 @@ export class UpdateBillingClient { error: null, }; } + + async createCheckoutSession( + id: string, + options?: CreateCheckoutSessionOptions + ): Promise { + if (!this.hasSessionToken) { + console.warn( + '@updatedev/js: billing.createCheckoutSession() called without a session token. You need to add `getSessionToken` to createClient().' + ); + return { + data: { + url: null, + }, + error: { + message: 'No session token', + }, + }; + } + + const { data, error } = await this.requestClient.request({ + endpoint: '/billing/checkout/create', + method: 'POST', + body: { + id, + options, + }, + extra: { + includeUser: true, + }, + }); + + if (error) { + return { + data: { + url: null, + }, + error: { + message: error.message, + }, + }; + } + + return { + data: { + url: data, + }, + error: null, + }; + } } diff --git a/src/providers/supabase/UpdateEntitlementClient.ts b/src/UpdateEntitlements.ts similarity index 55% rename from src/providers/supabase/UpdateEntitlementClient.ts rename to src/UpdateEntitlements.ts index 5f910c5..9418795 100644 --- a/src/providers/supabase/UpdateEntitlementClient.ts +++ b/src/UpdateEntitlements.ts @@ -1,31 +1,44 @@ -import { RequestClient } from '../../utils/request'; -import { UpdateClientBillingOptions } from '../../types/options'; import { CheckEntitlementResponse, ListEntitlementsResponse, -} from '../../types/entitlement'; -import { ENVIRONMENT_HEADER } from '../../types/internal'; +} from './types/entitlement'; +import { RequestClient } from './utils/request'; -export class UpdateEntitlementClient { - private environment: string; +export class UpdateEntitlements { private requestClient: RequestClient; + private hasSessionToken: boolean; constructor({ - environment, requestClient, - }: UpdateClientBillingOptions & { + hasSessionToken, + }: { requestClient: RequestClient; + hasSessionToken: boolean; }) { this.requestClient = requestClient; - this.environment = environment ?? 'test'; + this.hasSessionToken = hasSessionToken; } async list(): Promise { + if (!this.hasSessionToken) { + console.warn( + '@updatedev/js: entitlements.list() called without a session token. You need to add `getSessionToken` to createClient().' + ); + return { + data: { + entitlements: null, + }, + error: { + message: 'No session token', + }, + }; + } + const { data, error } = await this.requestClient.request({ endpoint: '/entitlements', method: 'GET', - headers: { - [ENVIRONMENT_HEADER]: this.environment, + extra: { + includeUser: true, }, }); @@ -49,6 +62,18 @@ export class UpdateEntitlementClient { } async check(entitlement: string): Promise { + if (!this.hasSessionToken) { + console.warn( + '@updatedev/js: entitlements.check() called without a session token. You need to add `getSessionToken` to createClient().' + ); + return { + data: null, + error: { + message: 'No session token', + }, + }; + } + const { data, error } = await this.requestClient.request<{ has_access: boolean; }>({ @@ -57,8 +82,8 @@ export class UpdateEntitlementClient { body: { entitlement, }, - headers: { - [ENVIRONMENT_HEADER]: this.environment, + extra: { + includeUser: true, }, }); diff --git a/src/constants/index.ts b/src/constants/index.ts deleted file mode 100644 index 3fa7438..0000000 --- a/src/constants/index.ts +++ /dev/null @@ -1 +0,0 @@ -export const ACTIVE_ORGANIZATION_ID_KEY = 'update_active_organization'; diff --git a/src/createClient.ts b/src/createClient.ts new file mode 100644 index 0000000..3b3e1af --- /dev/null +++ b/src/createClient.ts @@ -0,0 +1,9 @@ +import { UpdateClientOptions } from './types/options'; +import { UpdateClient } from './Update'; + +export function createClient( + updateApiKey: string, + options: UpdateClientOptions +): UpdateClient { + return new UpdateClient(updateApiKey, options); +} diff --git a/src/index.ts b/src/index.ts index fcb073f..ef809fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,3 @@ export * from './types'; +export * from './createClient'; +export * from './Update'; diff --git a/src/providers/supabase/UpdateOrganizationClient.ts b/src/providers/supabase/UpdateOrganizationClient.ts deleted file mode 100644 index 993ce30..0000000 --- a/src/providers/supabase/UpdateOrganizationClient.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { RequestClient } from '../../utils/request'; -import { MembershipResponse, OrganizationResponse } from './types/organization'; -import { StorageClient } from '../../utils/storage'; -import { ACTIVE_ORGANIZATION_ID_KEY } from '../../constants'; -import { Membership, Organization } from '../../types'; - -export class UpdateOrganizationClient { - private requestClient: RequestClient; - private storageClient: StorageClient; - - constructor({ - requestClient, - storageClient, - }: { - requestClient: RequestClient; - storageClient: StorageClient; - }) { - this.requestClient = requestClient; - this.storageClient = storageClient; - } - - async createOrganization(name: string): Promise { - const { data, error } = await this.requestClient.request({ - endpoint: '/organization/create', - method: 'POST', - body: { name }, - }); - - if (error) { - return { - data: { - organization: null, - }, - error: { - code: 'organization_create_failed', - message: error.message, - }, - }; - } - - return { - data: { - organization: data, - }, - error: null, - }; - } - - async getOrganization(): Promise { - const activeOrganization = await this.storageClient.get( - ACTIVE_ORGANIZATION_ID_KEY - ); - - if (activeOrganization == null) { - return { - data: { - organization: null, - }, - error: { - code: 'no_active_organization', - message: 'User is not logged in to any organization', - }, - }; - } - - const { data: orgData, error: orgError } = - await this.requestClient.request({ - endpoint: '/organization', - method: 'GET', - queryParams: { - organization: activeOrganization, - }, - }); - - if (orgError) { - return { - data: { - organization: null, - }, - error: { - code: 'organization_get_failed', - message: orgError.message, - }, - }; - } - - return { - data: { - organization: orgData, - }, - error: null, - }; - } - - async getMemberships(): Promise { - const { data, error } = await this.requestClient.request({ - endpoint: '/user/memberships', - method: 'GET', - }); - - if (error) { - return { - data: null, - error: { - code: 'organization_memberships_get_failed', - message: error.message, - }, - }; - } - - return { - data, - error: null, - }; - } - - async setOrganization(organizationId: string): Promise { - await this.storageClient.set(ACTIVE_ORGANIZATION_ID_KEY, organizationId, { - expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), - }); - } -} diff --git a/src/providers/supabase/UpdateSupabaseAuth.ts b/src/providers/supabase/UpdateSupabaseAuth.ts deleted file mode 100644 index 2819d0e..0000000 --- a/src/providers/supabase/UpdateSupabaseAuth.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { SupabaseClient } from '@supabase/supabase-js'; -import { RequestClient } from '../../utils/request'; -import { - AuthError, - AuthFlowResponse, - AuthResponse, - CreateAuthFlowLinkOptions, -} from './types/auth'; -import { GoTrueClient } from '@supabase/auth-js'; - -export interface UpdateSupabaseAuth extends GoTrueClient {} - -export class UpdateSupabaseAuth { - constructor( - private auth: SupabaseClient['auth'], - private requestClient: RequestClient - ) { - return new Proxy(this, { - get(target: UpdateSupabaseAuth, prop: string | symbol) { - // First check if the property exists on our class - if (prop in target) { - return target[prop as keyof UpdateSupabaseAuth]; - } - // Otherwise forward to the auth client - return target.auth[prop as keyof GoTrueClient]; - }, - }); - } - - /** - * Creates a new auth flow link - */ - async createAuthFlowLink( - options?: CreateAuthFlowLinkOptions - ): Promise { - const { data, error } = await this.requestClient.request<{ url: string }>({ - endpoint: '/auth/connect/create', - method: 'GET', - queryParams: options, - }); - - if (error) { - return { - data: { url: null }, - error: new AuthError(error.message, 500, 'auth_flow_link_failed'), - }; - } - - return { - data: { url: data.url }, - error: null, - }; - } - - /** - * Verifies the code from the external auth callback - */ - async verifyAuthFlowCode(code: string): Promise { - const { data: codeVerificationData, error: codeVerificationError } = - await this.requestClient.request<{ - access_token: string; - refresh_token: string; - }>({ - endpoint: '/auth/connect/verify', - method: 'POST', - body: { - code, - }, - }); - - if (codeVerificationError) { - return { - data: { - session: null, - user: null, - }, - error: new AuthError( - codeVerificationError.message, - 400, - 'auth_callback_failed' - ), - }; - } - - const { access_token, refresh_token } = codeVerificationData; - - const { data: setSessionData, error: setSessionError } = - await this.auth.setSession({ - access_token, - refresh_token, - }); - - if (setSessionError) { - return { - data: { - session: null, - user: null, - }, - error: setSessionError, - }; - } - - const { session, user } = setSessionData; - - return { - data: { - session, - user, - }, - error: null, - }; - } -} diff --git a/src/providers/supabase/UpdateSupabaseClient.ts b/src/providers/supabase/UpdateSupabaseClient.ts deleted file mode 100644 index 6a9d914..0000000 --- a/src/providers/supabase/UpdateSupabaseClient.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { - createClient as createSupabaseClient, - SupabaseClient, -} from '@supabase/supabase-js'; -import { GenericSchema } from '@supabase/supabase-js/dist/module/lib/types'; -import { ACTIVE_ORGANIZATION_ID_KEY } from '../../constants'; -import { UpdateBillingClient } from './UpdateBillingClient'; -import { UpdateOrganizationClient } from './UpdateOrganizationClient'; -import { UpdateSupabaseAuth } from './UpdateSupabaseAuth'; -import { SupabaseClientOptions } from '@supabase/supabase-js'; -import { RequestClient } from '../../utils/request'; -import { getAll, setAll } from '../../utils'; -import { StorageClient, StorageOptions } from '../../utils/storage'; -import { UpdateSupabaseClientOptions } from './types/options'; -import { UpdateEntitlementClient } from './UpdateEntitlementClient'; - -export class UpdateSupabaseClient< - Database = any, - SchemaName extends string & keyof Database = 'public' extends keyof Database - ? 'public' - : string & keyof Database, - Schema extends GenericSchema = Database[SchemaName] extends GenericSchema - ? Database[SchemaName] - : any, -> { - private supabase: SupabaseClient; - private storageClient: StorageClient; - auth: UpdateSupabaseAuth; - from: SupabaseClient['from']; - rpc: SupabaseClient['rpc']; - storage: SupabaseClient['storage']; - functions: SupabaseClient['functions']; - realtime: SupabaseClient['realtime']; - billing: UpdateBillingClient; - organization: UpdateOrganizationClient; - entitlements: UpdateEntitlementClient; - - constructor( - protected readonly apiKey: string, - protected readonly supabaseUrl: string, - protected readonly supabaseKey: string, - options?: UpdateSupabaseClientOptions - ) { - this.supabase = this._initializeSupabase( - supabaseUrl, - supabaseKey, - options?.supabase - ); - this.from = this.supabase.from; - this.rpc = this.supabase.rpc; - this.storage = this.supabase.storage; - this.functions = this.supabase.functions; - this.realtime = this.supabase.realtime; - - this.storageClient = this._initializeStorageClient(options?.storage); - - const requestClient = new RequestClient({ - baseUrl: options?.apiUrl ?? 'https://api.update.dev/v1', - apiKey: this.apiKey, - getActiveOrganizationId: () => this._getActiveOrganizationId(), - getAuthorizationToken: () => this._getAuthorizationToken(), - }); - - this.auth = new UpdateSupabaseAuth(this.supabase.auth, requestClient); - this.billing = new UpdateBillingClient({ - ...options?.billing, - requestClient, - }); - this.entitlements = new UpdateEntitlementClient({ - environment: options?.billing?.environment, - requestClient, - }); - this.organization = new UpdateOrganizationClient({ - requestClient, - storageClient: this.storageClient, - }); - } - - private _initializeStorageClient(storage?: StorageOptions): StorageClient { - if (storage != null && storage.getAll != null) - return new StorageClient(storage.getAll, storage.setAll); - return new StorageClient(getAll, setAll); - } - - private _initializeSupabase( - supabaseUrl: string, - supabaseKey: string, - options?: SupabaseClientOptions - ): SupabaseClient { - return createSupabaseClient( - supabaseUrl, - supabaseKey, - { - ...options, - } - ); - } - - private async _getAuthorizationToken(): Promise { - const { data } = await this.auth.getSession(); - return data.session?.access_token; - } - - private async _getActiveOrganizationId(): Promise { - return this.storageClient.get(ACTIVE_ORGANIZATION_ID_KEY); - } -} diff --git a/src/providers/supabase/createClient.ts b/src/providers/supabase/createClient.ts deleted file mode 100644 index f28880a..0000000 --- a/src/providers/supabase/createClient.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GenericSchema } from '@supabase/supabase-js/dist/module/lib/types'; -import { UpdateSupabaseClient } from './UpdateSupabaseClient'; -import { UpdateSupabaseClientOptions } from './types/options'; - -export function createClient< - Database = any, - SchemaName extends string & keyof Database = 'public' extends keyof Database - ? 'public' - : string & keyof Database, - Schema extends GenericSchema = Database[SchemaName] extends GenericSchema - ? Database[SchemaName] - : any, ->( - updateApiKey: string, - supabaseUrl: string, - supabaseKey: string, - options?: UpdateSupabaseClientOptions -): UpdateSupabaseClient { - return new UpdateSupabaseClient( - updateApiKey, - supabaseUrl, - supabaseKey, - options - ); -} diff --git a/src/providers/supabase/index.ts b/src/providers/supabase/index.ts deleted file mode 100644 index 64ce3b8..0000000 --- a/src/providers/supabase/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './types/supabase'; -export * from './types/options'; -export * from './createClient'; -export * from './UpdateSupabaseClient'; diff --git a/src/providers/supabase/types/auth.ts b/src/providers/supabase/types/auth.ts deleted file mode 100644 index a72d163..0000000 --- a/src/providers/supabase/types/auth.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - User, - Session, - AuthError as SupabaseAuthError, -} from '@supabase/supabase-js'; - -export type ErrorCode = - | SupabaseAuthError['code'] - | 'auth_callback_failed' - | 'auth_flow_link_failed'; - -export class AuthError extends SupabaseAuthError { - code: ErrorCode | (string & Record) | undefined; - constructor(message: string, status?: number, code?: string) { - super(message); - this.name = 'AuthError'; - this.status = status; - this.code = code; - } -} - -export type AuthResponse = - | { - data: { - user: User | null; - session: Session | null; - }; - error: null; - } - | { - data: { - user: null; - session: null; - }; - error: AuthError; - }; - -export type AuthFlowResponse = - | { - data: { - url: string; - }; - error: null; - } - | { - data: { - url: null; - }; - error: AuthError; - }; - -export type CreateAuthFlowLinkOptions = { - type?: 'sign-in' | 'sign-up'; -}; diff --git a/src/providers/supabase/types/error.ts b/src/providers/supabase/types/error.ts deleted file mode 100644 index a1ecdd0..0000000 --- a/src/providers/supabase/types/error.ts +++ /dev/null @@ -1,5 +0,0 @@ -type UpdateError = { - message: string; -}; - -export type { UpdateError }; diff --git a/src/providers/supabase/types/options.ts b/src/providers/supabase/types/options.ts deleted file mode 100644 index ff93f32..0000000 --- a/src/providers/supabase/types/options.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SupabaseClientOptions } from '@supabase/supabase-js/dist/module/lib/types'; - -import { StorageOptions } from '../../../utils/storage'; -import { UpdateClientOptions } from '../../../types/options'; - -export type UpdateSupabaseClientOptions = UpdateClientOptions & { - storage?: StorageOptions; - supabase?: SupabaseClientOptions; -}; diff --git a/src/providers/supabase/types/organization.ts b/src/providers/supabase/types/organization.ts deleted file mode 100644 index fceb141..0000000 --- a/src/providers/supabase/types/organization.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Membership } from "../../../types"; -import { Organization } from "../../../types/organization"; - -type ErrorCode = "no_active_organization" | "no_organization_memberships"; - -type OrganizationError = { - code: ErrorCode | (string & {}) | undefined; - message: string; -}; - -export type OrganizationResponse = - | { - data: { - organization: Organization; - }; - error: null; - } - | { - data: { - organization: null; - }; - error: OrganizationError; - }; - -export type MembershipResponse = - | { - data: Membership[]; - error: null; - } - | { - data: null; - error: OrganizationError; - }; diff --git a/src/providers/supabase/types/response.ts b/src/providers/supabase/types/response.ts deleted file mode 100644 index 609a993..0000000 --- a/src/providers/supabase/types/response.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type ClientResponse = - | { - data: T; - error: null; - } - | { - data: null; - error: { - message: string; - }; - }; diff --git a/src/providers/supabase/types/supabase.ts b/src/providers/supabase/types/supabase.ts deleted file mode 100644 index f5061b0..0000000 --- a/src/providers/supabase/types/supabase.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "@supabase/auth-js"; diff --git a/src/types/index.ts b/src/types/index.ts index aaf408e..e0f72ad 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1 @@ export * from './billing'; -export * from './membership'; -export * from './organization'; diff --git a/src/types/internal.ts b/src/types/internal.ts index ac9e61a..feaa81c 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -1 +1,2 @@ export const ENVIRONMENT_HEADER = 'X-Update-Environment'; +export const API_KEY_HEADER = 'X-Api-Key'; diff --git a/src/types/membership.ts b/src/types/membership.ts deleted file mode 100644 index 83b25aa..0000000 --- a/src/types/membership.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Organization } from './organization'; - -export type Membership = { - organization: Organization; - role: string; -}; diff --git a/src/types/options.ts b/src/types/options.ts index c36a901..0605832 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -1,8 +1,14 @@ -export interface UpdateClientBillingOptions { - environment?: 'live' | 'test'; -} +import { StorageOptions } from '../utils/storage'; + +export type GetToken = () => + | Promise + | string + | undefined + | null; export interface UpdateClientOptions { - billing?: UpdateClientBillingOptions; + getSessionToken?: GetToken; + environment?: 'live' | 'test'; apiUrl?: string; + storage?: StorageOptions; } diff --git a/src/types/organization.ts b/src/types/organization.ts deleted file mode 100644 index b172b45..0000000 --- a/src/types/organization.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type Organization = { - id: string; - name: string; -}; diff --git a/src/providers/supabase/types/request.ts b/src/types/request.ts similarity index 79% rename from src/providers/supabase/types/request.ts rename to src/types/request.ts index 628f8a8..2c40977 100644 --- a/src/providers/supabase/types/request.ts +++ b/src/types/request.ts @@ -1,14 +1,17 @@ export interface RequestOptions { endpoint: string; - method: "GET" | "POST"; + method: 'GET' | 'POST'; body?: unknown | FormData; headers?: Record; queryParams?: Record; + extra?: { + includeUser?: boolean; + }; } export interface CoreRequestOptions { endpoint: string; - method: "GET" | "POST"; + method: 'GET' | 'POST'; body?: unknown | FormData; headers?: Record; queryParams?: Record; @@ -18,12 +21,12 @@ export type ApiResponse = | { data: T; error: null; - status: "SUCCESS"; + status: 'SUCCESS'; } | { data: null; error: { message: string; }; - status: "ERROR"; + status: 'ERROR'; }; diff --git a/src/utils/object.ts b/src/utils/object.ts new file mode 100644 index 0000000..6079387 --- /dev/null +++ b/src/utils/object.ts @@ -0,0 +1,11 @@ +export function getDefinedObject>(object: T) { + let definedObject: Record = {}; + if (object) { + Object.entries(object).forEach(([key, value]) => { + if (value !== undefined && typeof value !== 'function') { + definedObject[key] = value; + } + }); + } + return definedObject; +} diff --git a/src/utils/request.ts b/src/utils/request.ts index 76c86d7..073b4a1 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,30 +1,44 @@ +import { GetToken } from '../types/options'; import { ApiResponse, CoreRequestOptions, RequestOptions, -} from '../providers/supabase/types/request'; +} from '../types/request'; export class RequestClient { private baseUrl: string; - private apiKey: string; - private getAuthorizationToken: () => Promise; - private getActiveOrganizationId: () => Promise; + private getAuthorizationToken?: GetToken; + private headers?: Record; + private baseBody?: Record; constructor({ baseUrl, - apiKey, + headers, getAuthorizationToken, - getActiveOrganizationId, + baseBody, }: { baseUrl: string; - apiKey: string; - getActiveOrganizationId: () => Promise; - getAuthorizationToken: () => Promise; + headers?: Record; + getAuthorizationToken?: GetToken; + baseBody?: Record; }) { this.baseUrl = baseUrl || 'https://api.update.dev/v1'; - this.apiKey = apiKey; + this.headers = this._getDefinedObject(headers); + this.baseBody = this._getDefinedObject(baseBody); this.getAuthorizationToken = getAuthorizationToken; - this.getActiveOrganizationId = getActiveOrganizationId; + this.baseBody = baseBody; + } + + private _getDefinedObject(headers?: Record) { + let definedHeaders: Record = {}; + if (headers) { + Object.entries(headers).forEach(([key, value]) => { + if (value !== undefined) { + definedHeaders[key] = value; + } + }); + } + return definedHeaders; } _buildQueryString( @@ -54,48 +68,31 @@ export class RequestClient { } } - _addParams( - options: RequestOptions, - params: Record - ): RequestOptions { - const newOptions = { ...options }; + async _requestCore( + options: CoreRequestOptions + ): Promise> { + const { method, endpoint } = options; + let body = options.body; + let queryParams = options.queryParams; + let requestHeaders = options.headers; - for (const [key, value] of Object.entries(params)) { - if (value == null) continue; - - if (newOptions.method === 'GET') { - newOptions.queryParams = { - ...(newOptions.queryParams || {}), - [key]: value, - }; - } + let url = `${this.baseUrl}${endpoint}`; - if (newOptions.method === 'POST') { - const existingBody = - typeof newOptions.body === 'object' && - newOptions.body !== null && - !(newOptions.body instanceof FormData) - ? newOptions.body - : {}; - - newOptions.body = { - ...existingBody, - [key]: value, - }; - } + // Add base body to GET requests + if (method === 'GET' && this.baseBody) { + queryParams = { + ...(queryParams || {}), + ...this.baseBody, + }; } - return newOptions; - } - - async _requestCore({ - endpoint, - method, - body, - queryParams, - headers: requestHeaders = {}, - }: CoreRequestOptions): Promise> { - let url = `${this.baseUrl}${endpoint}`; + // Add base body to POST requests + if (method === 'POST' && this.baseBody) { + body = { + ...(body || {}), + ...this.baseBody, + }; + } if (method === 'GET' && queryParams) { const queryString = this._buildQueryString(queryParams); @@ -104,29 +101,20 @@ export class RequestClient { const headers: HeadersInit = new Headers({ ...requestHeaders, + ...(this.headers || {}), }); - const authorizationToken = await this.getAuthorizationToken(); - - if (authorizationToken != null) { - headers.set('Authorization', `Bearer ${authorizationToken}`); - } - - headers.set('x-api-key', this.apiKey); - - const options: RequestInit = { + const requestOptions: RequestInit = { method, headers, }; - if (method === 'POST' && body instanceof FormData) { - options.body = body; - } else if (method === 'POST' && body) { + if (method === 'POST' && body) { headers.set('Content-Type', 'application/json'); - options.body = JSON.stringify(body); + requestOptions.body = JSON.stringify(body); } - const { data, status, error } = await this._execute(url, options); + const { data, status, error } = await this._execute(url, requestOptions); if (status === 'ERROR') { return { data: null, error, status }; @@ -136,8 +124,22 @@ export class RequestClient { } async request(options: RequestOptions): Promise> { - const organizationId = await this.getActiveOrganizationId(); - options = this._addParams(options, { organizationId }); - return this._requestCore(options); + let requestOptions = { ...options }; + + if ( + requestOptions.extra?.includeUser != null && + requestOptions.extra.includeUser && + this.getAuthorizationToken + ) { + const authorizationToken = await this.getAuthorizationToken(); + if (authorizationToken) { + requestOptions.headers = { + ...requestOptions.headers, + Authorization: `Bearer ${authorizationToken}`, + }; + } + } + + return this._requestCore(requestOptions); } }