ngx-appwrite
Version:
A wrapper around the Appwrite WebSDK for easier implementation in Angular 16+ projects. The goal is to make the whole SDK accessible as well as provide some convenience functionality like RxJS streams where appropriate.
1 lines • 112 kB
Source Map (JSON)
{"version":3,"file":"ngx-appwrite.mjs","sources":["../../../../libs/ngx-appwrite/src/lib/helpers.ts","../../../../libs/ngx-appwrite/src/lib/setup.ts","../../../../libs/ngx-appwrite/src/lib/account.ts","../../../../libs/ngx-appwrite/src/lib/databases.ts","../../../../libs/ngx-appwrite/src/lib/appwrite.adapter.ts","../../../../libs/ngx-appwrite/src/lib/avatars.ts","../../../../libs/ngx-appwrite/src/lib/functions.ts","../../../../libs/ngx-appwrite/src/lib/localization.ts","../../../../libs/ngx-appwrite/src/lib/messaging.ts","../../../../libs/ngx-appwrite/src/lib/storage.ts","../../../../libs/ngx-appwrite/src/lib/teams.ts","../../../../libs/ngx-appwrite/src/ngx-appwrite.ts"],"sourcesContent":["import { Client, RealtimeResponseEvent } from 'appwrite';\nimport { Observable, Subscriber } from 'rxjs';\n\nexport const watch = <T>(\n client: Client,\n channel: string | string[],\n events?: string | string[],\n): Observable<RealtimeResponseEvent<T>> => {\n const observable = new Observable<RealtimeResponseEvent<T>>(\n (observer: Subscriber<RealtimeResponseEvent<T>>) => {\n try {\n client.subscribe<T>(channel, (response: RealtimeResponseEvent<T>) => {\n if (!events) {\n observer.next(response);\n } else if (\n (typeof events === 'string' && response.events.includes(events)) ||\n intersection(response.events, events)\n ) {\n observer.next(response);\n }\n });\n } catch (error) {\n console.error('Error while watching channel: ', channel);\n if (error instanceof Error) observer.error(error.message);\n }\n },\n );\n return observable;\n};\n\nexport const wait = (seconds: number): Promise<void> => {\n return new Promise((resolve) => {\n setTimeout(resolve, seconds * 1000);\n });\n};\n\nexport const deepEqual = <T>(obj1: T, obj2: T) => {\n return JSON.stringify(obj1) === JSON.stringify(obj2);\n};\n\nfunction intersection(...args: (string | string[])[]): string[] {\n const arrays: string[][] = args.map((arg) =>\n Array.isArray(arg) ? arg : [arg],\n );\n\n if (arrays.length === 0) {\n return [];\n }\n\n const firstArray = arrays[0];\n const uniqueValues = new Set(firstArray);\n\n for (let i = 1; i < arrays.length; i++) {\n const currentArray = arrays[i];\n\n for (let j = 0; j < currentArray.length; j++) {\n const value = currentArray[j];\n if (!uniqueValues.has(value)) {\n uniqueValues.delete(value);\n }\n }\n }\n\n return Array.from(uniqueValues);\n}\n","import { APP_INITIALIZER, InjectionToken, type Provider } from '@angular/core';\nimport { Client } from 'appwrite';\nimport { AppwriteConfig } from './config';\n\nexport { ID } from 'appwrite';\n\nlet __client: Client | undefined;\nlet __defaultDatabaseId: string | undefined;\n\nexport const CLIENT = () => {\n if (!__client) {\n throw new Error(\n 'Appwrite client not initialized, did you call initializeAppwrite?',\n );\n }\n return __client;\n};\n\nexport const DEFAULT_DATABASE_ID = (): string | undefined => {\n if (!__defaultDatabaseId) {\n console.warn(\n 'Appwrite default database id not initialized, can be passed inside provideAppwrite',\n );\n }\n return __defaultDatabaseId;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst ConfigToken = new InjectionToken<any>('APPWRITE_USER_CONFIG');\n\nconst initializeAppwrite = (config: AppwriteConfig) => {\n return () => {\n __client = new Client();\n\n __client.setEndpoint(config.endpoint).setProject(config.project); //\n\n if (config.defaultDatabase) {\n console.log('Configured defaultDatabaseId: ', config.defaultDatabase);\n __defaultDatabaseId = config.defaultDatabase;\n }\n };\n};\n\nexport const provideAppwrite = (config: AppwriteConfig): Provider[] => {\n return [\n {\n provide: ConfigToken,\n useValue: config,\n },\n {\n provide: APP_INITIALIZER,\n useFactory: initializeAppwrite,\n multi: true,\n deps: [ConfigToken],\n },\n ];\n};\n","import { Injectable } from '@angular/core';\nimport {\n Account as AppwriteAccount,\n AuthenticationFactor,\n AuthenticatorType,\n ID,\n Models,\n OAuthProvider,\n} from 'appwrite';\nimport {\n Observable,\n Subject,\n debounceTime,\n distinctUntilChanged,\n map,\n merge,\n of,\n shareReplay,\n startWith,\n switchMap,\n} from 'rxjs';\nimport { deepEqual, watch } from './helpers';\nimport { CLIENT } from './setup';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class Account {\n /* -------------------------------------------------------------------------- */\n /* Setup */\n /* -------------------------------------------------------------------------- */\n\n private _account!: AppwriteAccount;\n private _client$ = of(CLIENT()).pipe(shareReplay(1));\n private _watchAuthChannel$ = this._client$.pipe(\n switchMap((client) => watch(client, 'account').pipe(startWith(null))),\n );\n private _triggerManualAuthCheck$ = new Subject<boolean>();\n\n private _auth$: unknown | undefined;\n\n /* -------------------------------------------------------------------------- */\n /* Reactive */\n /* -------------------------------------------------------------------------- */\n\n constructor() {\n this._account = new AppwriteAccount(CLIENT());\n }\n\n onAuth<\n TPrefs extends Models.Preferences,\n >(): Observable<Models.User<TPrefs> | null> {\n try {\n if (!this._auth$) {\n this._auth$ = merge(\n this._watchAuthChannel$,\n this._triggerManualAuthCheck$,\n ).pipe(\n switchMap(() => this._checkIfAuthExists()),\n debounceTime(50),\n map((account) => {\n if (!account) return null;\n return account as Models.User<TPrefs>;\n }),\n distinctUntilChanged(deepEqual),\n shareReplay(1),\n );\n }\n\n return this._auth$ as Observable<Models.User<TPrefs> | null>;\n } catch (error) {\n console.error('Error in Account > onAuth');\n throw error;\n }\n }\n\n /* -------------------------------------------------------------------------- */\n /* Default API - https://appwrite.io/docs/client/account?sdk=web-default */\n /* -------------------------------------------------------------------------- */\n\n /**\n * Get Account\n *\n * Get currently logged in user data as JSON object.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async get<TPrefs extends Models.Preferences>() {\n try {\n const account = await this._account.get();\n return account as Models.User<TPrefs>;\n } catch (error) {\n console.error('Error fetching account');\n throw error;\n }\n }\n\n /**\n * Create Account\n *\n * Use this endpoint to allow a new user to register a new account in your project.\n * After the user registration completes successfully, you can use the /account/verfication route\n * to start verifying the user email address. To allow the new user to login to their new account,\n * you need to create a new account session.\n *\n * @param {string} email\n * @param {string} password\n * @param {Models.Preferences} defaultPrefs\n * @param {string} name\n * @param {string} userId\n * Defaults to ID.unique()\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<T>>}\n */\n async create<T extends Models.Preferences>(\n email: string,\n password: string,\n defaultPrefs: T = {} as T,\n name?: string,\n userId: string = ID.unique(),\n ): Promise<Models.User<T>> {\n const account = await this._account.create(userId, email, password, name);\n this.triggerAuthCheck();\n await this.updatePrefs(defaultPrefs);\n return account as Models.User<T>;\n }\n\n /**\n * Update Email\n *\n * Update currently logged in user account email address.\n * After changing user address, the user confirmation status will get reset.\n * A new confirmation email is not sent automatically however you can use the\n * send confirmation email endpoint again to send the confirmation email.\n * For security measures, user password is required to complete this request.\n * This endpoint can also be used to convert an anonymous account to a normal one,\n * by passing an email address and a new password.\n *\n *\n * @param {string} email\n * @param {string} password\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updateEmail<TPrefs extends Models.Preferences>(\n email: string,\n password: string,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updateEmail(email, password);\n }\n\n /**\n * List Identities\n *\n * Get the list of identities for the currently logged in user.\n *\n *\n * @param {string[]} queries\n * @throws {AppwriteException}\n * @returns {Promise<Models.IdentityList>}\n */\n async listIdentities(queries: string[] = []): Promise<Models.IdentityList> {\n return this._account.listIdentities(queries);\n }\n\n /**\n * Delete Identity\n *\n * Delete a user identity by id.\n *\n *\n * @param {string} id\n * @throws {AppwriteException}\n * @returns {Promise<{}>}\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n async deleteIdentity(id: string): Promise<{}> {\n return this._account.deleteIdentity(id);\n }\n\n /**\n * Create JWT\n *\n * Use this endpoint to create a JSON Web Token. You can use the resulting JWT\n * to authenticate on behalf of the current user when working with the\n * Appwrite server-side API and SDKs. The JWT secret is valid for 15 minutes\n * from its creation and will be invalid if the user will logout in that time\n * frame.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.Jwt>}\n */\n async createJWT(): Promise<Models.Jwt> {\n return await this._account.createJWT();\n }\n\n /**\n * List Logs\n *\n * Get the list of latest security activity logs for the currently logged in user.\n * Each log returns user IP address, location and date and time of log.\n *\n * @param {string[]} queries\n * @throws {AppwriteException}\n * @returns {Promise<AppwriteLogListObject>}\n */\n async listLogs(queries: string[] = []): Promise<Models.LogList> {\n return this._account.listLogs(queries);\n }\n\n /**\n * Update MFA\n *\n * Enable or disable MFA on an account.\n *\n * @param {boolean} enableMFA\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updateMFA<TPrefs extends Models.Preferences>(\n enableMFA: boolean,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updateMFA(enableMFA);\n }\n\n /**\n * Add Authenticator\n *\n * Add an authenticator app to be used as an MFA factor.\n * Verify the authenticator using the verify authenticator method.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.MfaType>}\n */\n async createMfaAuthenticator(): Promise<Models.MfaType> {\n return this._account.createMfaAuthenticator(AuthenticatorType.Totp);\n }\n\n /**\n * Verify Authenticator\n *\n * Verify an authenticator app after adding it using the add authenticator method.\n *\n * @param {string} otp\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updateMfaAuthenticator<TPrefs extends Models.Preferences>(\n otp: string,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updateMfaAuthenticator(\n AuthenticatorType.Totp, // type\n otp, // otp\n );\n }\n\n /**\n * Delete Authenticator\n *\n * Delete an authenticator for a user.\n * Verify the authenticator using the verify authenticator method.\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n async deleteMfaAuthenticator(): Promise<{}> {\n return this._account.deleteMfaAuthenticator(AuthenticatorType.Totp);\n }\n\n /**\n * Create 2FA Challenge\n *\n * Begin the process of MFA verification after sign-in.\n * Finish the flow with updateMfaChallenge method.\n *\n * @param {AuthenticationFactor} factor\n * @throws {AppwriteException}\n * @returns {Promise<Models.MfaChallenge>}\n */\n async createMfaChallenge(\n factor: AuthenticationFactor,\n ): Promise<Models.MfaChallenge> {\n return this._account.createMfaChallenge(factor);\n }\n\n /**\n * Create MFA Challenge (confirmation)\n *\n * Complete the MFA challenge by providing the one-time password.\n * Finish the process of MFA verification by providing the one-time password.\n * To begin the flow, use createMfaChallenge method.\n *\n * @param {string} challengeId\n * @param {string} otp\n * @throws {AppwriteException}\n * @returns {Promise<{}>}\n */\n async updateMfaChallenge(\n challengeId: string,\n otp: string,\n\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n ): Promise<{}> {\n return this._account.updateMfaChallenge(challengeId, otp);\n }\n\n /**\n * List Factors\n *\n * List the factors available on the account to be used as a MFA challange.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.MfaFactors>}\n */\n async listMfaFactors(): Promise<Models.MfaFactors> {\n return this._account.listMfaFactors();\n }\n\n /**\n * Get MFA Recovery Codes\n *\n * Get recovery codes that can be used as backup for MFA flow.\n * Before getting codes, they must be generated using createMfaRecoveryCodes method.\n * An OTP challenge is required to read recovery codes.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.MfaRecoveryCodes>}\n */\n async getMfaRecoveryCodes(): Promise<Models.MfaRecoveryCodes> {\n return this._account.getMfaRecoveryCodes();\n }\n\n /**\n * Create MFA Recovery Codes\n *\n * Generate recovery codes as backup for MFA flow.\n * It's recommended to generate and show then immediately after user\n * successfully adds their authehticator. Recovery codes can be used as a MFA\n * verification type in createMfaChallenge method.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.MfaRecoveryCodes>}\n */\n async createMfaRecoveryCodes(): Promise<Models.MfaRecoveryCodes> {\n return this._account.createMfaRecoveryCodes();\n }\n\n /**\n * Regenerate MFA Recovery Codes\n *\n * Regenerate recovery codes that can be used as backup for MFA flow.\n * Before regenerating codes, they must be first generated using\n * createMfaRecoveryCodes method. An OTP challenge is required to regenreate\n * recovery codes.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.MfaRecoveryCodes>}\n */\n async updateMfaRecoveryCodes(): Promise<Models.MfaRecoveryCodes> {\n return this._account.updateMfaRecoveryCodes();\n }\n\n /**\n * Update Name\n *\n * Update currently logged in user account name.\n *\n * @param {string} name\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updateName<TPrefs extends Models.Preferences>(\n name: string,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updateName(name);\n }\n\n /**\n * Update Password\n *\n * Update currently logged in user password. For validation, user is required\n * to pass in the new password, and the old password. For users created with\n * OAuth, Team Invites and Magic URL, oldPassword is optional.\n *\n * @param {string} password\n * @param {string} oldPassword\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updatePassword<TPrefs extends Models.Preferences>(\n password: string,\n oldPassword?: string,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updatePassword(password, oldPassword);\n }\n\n /**\n * Update Phone\n *\n * Update the currently logged in user's phone number. After updating the\n * phone number, the phone verification status will be reset. A confirmation\n * SMS is not sent automatically, however you can use the [POST\n * /account/verification/phone](/docs/client/account#accountCreatePhoneVerification)\n * endpoint to send a confirmation SMS.\n *\n * @param {string} phoneNumber\n * @param {string} password\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updatePhone<TPrefs extends Models.Preferences>(\n phoneNumber: string,\n password: string,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updatePhone(phoneNumber, password);\n }\n\n /**\n * Get Account Preferences\n *\n * Get the preferences as a key-value object for the currently logged in user.\n *\n * @throws {AppwriteException}\n * @returns {Promise<TPrefs>}\n */\n async getPrefs<TPrefs extends Models.Preferences>(): Promise<TPrefs> {\n return this._account.getPrefs() as Promise<TPrefs>;\n }\n\n /**\n * Update Preferences\n *\n * Update currently logged in user account preferences. The object you pass is\n * stored as is, and replaces any previous value. The maximum allowed prefs\n * size is 64kB and throws error if exceeded.\n *\n * @param {object} prefs\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updatePrefs<TPrefs extends Models.Preferences>(\n prefs: TPrefs,\n ): Promise<Models.User<TPrefs>> {\n return this._account.updatePrefs(prefs) as Promise<Models.User<TPrefs>>;\n }\n\n /**\n * Create Password Recovery\n *\n * Sends the user an email with a temporary secret key for password reset.\n * When the user clicks the confirmation link he is redirected back to your\n * app password reset URL with the secret key and email address values\n * attached to the URL query string. Use the query string params to submit a\n * request to the [PUT\n * /account/recovery](/docs/client/account#accountUpdateRecovery) endpoint to\n * complete the process. The verification link sent to the user's email\n * address is valid for 1 hour.\n *\n * @param {string} email\n * @param {string} url\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async createRecovery(email: string, url: string): Promise<Models.Token> {\n const result = await this._account.createRecovery(email, url);\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * Create Password Recovery (confirmation)\n *\n * Use this endpoint to complete the user account password reset. Both the\n * **userId** and **secret** arguments will be passed as query parameters to\n * the redirect URL you have provided when sending your request to the [POST\n * /account/recovery](/docs/client/account#accountCreateRecovery) endpoint.\n *\n * Please note that in order to avoid a [Redirect\n * Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)\n * the only valid redirect URLs are the ones from domains you have set when\n * adding your platforms in the console interface.\n *\n * @param {string} userId\n * @param {string} secret\n * @param {string} password\n * @throws {AppwriteException}\n * @returns {Promise<Models.Toke>}\n */\n async updateRecovery(\n userId: string,\n secret: string,\n password: string,\n ): Promise<Models.Token> {\n const result = await this._account.updateRecovery(userId, secret, password);\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * List Sessions\n *\n * Get currently logged in user list of active sessions across different\n * devices.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.SessionList>}\n */\n async listSessions(): Promise<Models.SessionList> {\n return this._account.listSessions();\n }\n\n /**\n * Delete Sessions\n *\n * Delete all sessions from the user account and remove any sessions cookies\n * from the end client.\n *\n * @throws {AppwriteException}\n * @returns {Promise<{}>}\n */\n async deleteSessions(): // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n Promise<{}> {\n const result = await this._account.deleteSessions();\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * Create Anonymous Session\n *\n * Use this endpoint to allow a new user to register an anonymous account in\n * your project. This route will also create a new session for the user. To\n * allow the new user to convert an anonymous account to a normal account, you\n * need to update its [email and\n * password](/docs/client/account#accountUpdateEmail) or create an [OAuth2\n * session](/docs/client/account#accountCreateOAuth2Session).\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async createAnonymousSession(): Promise<Models.Session> {\n const session = await this._account.createAnonymousSession();\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Create email password session\n *\n * Allow the user to login into their account by providing a valid email and\n * password combination. This route will create a new session for the user.\n *\n * A user is limited to 10 active sessions at a time by default. [Learn more\n * about session limits](/docs/authentication#limits).\n *\n * @param {string} email\n * @param {string} password\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async createEmailPasswordSession(\n email: string,\n password: string,\n ): Promise<Models.Session> {\n const session = await this._account.createEmailPasswordSession(\n email,\n password,\n );\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Create Magic URL session (confirmation)\n *\n * Use this endpoint to create a session from token.\n * Provide the userId and secret parameters from the successful response\n * of authentication flows initiated by token creation. For example,\n * magic URL and phone login.\n *\n * @param {string} userId\n * @param {string} secret\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async updateMagicURLSession(\n userId: string,\n secret: string,\n ): Promise<Models.Session> {\n const session = await this._account.updateMagicURLSession(userId, secret);\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Create OAuth2 Session\n *\n * Allow the user to login to their account using the OAuth2 provider of their\n * choice. Each OAuth2 provider should be enabled from the Appwrite console\n * first. Use the success and failure arguments to provide a redirect URL's\n * back to your app when login is completed.\n *\n * If there is already an active session, the new session will be attached to\n * the logged-in account. If there are no active sessions, the server will\n * attempt to look for a user with the same email address as the email\n * received from the OAuth2 provider and attach the new session to the\n * existing user. If no matching user is found - the server will create a new\n * user.\n *\n * A user is limited to 10 active sessions at a time by default. [Learn more\n * about session limits](/docs/authentication#limits).\n *\n *\n * @param {OAuthProvider} provider\n * @param {string} success\n * @param {string} failure\n * @param {string[]} scopes\n * @throws {AppwriteException}\n * @returns {void|string}\n */\n async createOAuth2Session(\n provider: OAuthProvider,\n success?: string,\n failure?: string,\n scopes?: string[],\n ): Promise<string | void> {\n const url = this._account.createOAuth2Session(\n provider,\n success,\n failure,\n scopes,\n );\n return url;\n }\n\n /**\n * Update phone session\n *\n * Use this endpoint to create a session from token.\n * Provide the userId and secret parameters from the successful\n * response of authentication flows initiated by token creation.\n * For example, magic URL and phone login.\n *\n * @param {string} userId\n * @param {string} secret\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async updatePhoneSession(\n userId: string,\n secret: string,\n ): Promise<Models.Session> {\n const session = await this._account.updatePhoneSession(userId, secret);\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Create session\n *\n * Use this endpoint to create a session from token.\n * Provide the userId and secret parameters from the successful\n * response of authentication flows initiated by token creation.\n * For example, magic URL and phone login.\n *\n * @param {string} userId\n * @param {string} secret\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async createSession(userId: string, secret: string): Promise<Models.Session> {\n const session = await this._account.createSession(userId, secret);\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Get Session\n *\n * Use this endpoint to get a logged in user's session using a Session ID.\n * Inputting 'current' will return the current session being used.\n *\n * @param {string} sessionId\n * default is 'current' session\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async getSession(sessionId = 'current'): Promise<Models.Session> {\n try {\n const session = await this._account.getSession(sessionId);\n return session;\n } catch (error) {\n throw new Error(\n `Could not retrieve Appwrite session for id: ${sessionId} `,\n );\n }\n }\n\n /**\n * Update session\n *\n * Use this endpoint to extend a session's length.\n * Extending a session is useful when session expiry is short.\n * If the session was created using an OAuth provider,\n * this endpoint refreshes the access token from the provider.\n *\n * @param {string} sessionId\n * defaults to 'current'\n * @throws {AppwriteException}\n * @returns {Promise<Models.Session>}\n */\n async updateSession(sessionId = 'current'): Promise<Models.Session> {\n const result = await this._account.updateSession(sessionId);\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * Delete Session\n *\n * Logout the user. Use 'current' as the session ID to logout on this device,\n * use a session ID to logout on another device.\n * If you're looking to logout the user on all devices, use Delete Sessions instead.\n *\n *\n * @param {string} sessionId\n * defaults to 'current'\n * @throws {AppwriteException}\n * @returns {Promise<{}>}\n */\n async deleteSession(\n sessionId = 'current',\n ): // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n Promise<{}> {\n const result = await this._account.deleteSession(sessionId);\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * Update status\n *\n * Block the currently logged in user account. Behind the scene, the user\n * record is not deleted but permanently blocked from any access. To\n * completely delete a user, use the Users API instead.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<TPrefs>>}\n */\n async updateStatus<TPrefs extends Models.Preferences>(): Promise<\n Models.User<TPrefs>\n > {\n const account = await this._account.updateStatus();\n this.triggerAuthCheck();\n return account as Models.User<TPrefs>;\n }\n\n /**\n * Create push target\n *\n * No description at this moment\n *\n * @param {string} targetId\n * @param {string} identifier\n * @param {string} providerId\n * @throws {AppwriteException}\n * @returns {Promise<Models.Target>}\n */\n async createPushTarget(\n targetId: string,\n identifier: string,\n providerId?: string,\n ): Promise<Models.Target> {\n const account = await this._account.createPushTarget(\n targetId,\n identifier,\n providerId,\n );\n this.triggerAuthCheck();\n return account;\n }\n\n /**\n * Update push target\n *\n * No description at this moment\n *\n * @param {string} targetId\n * @param {string} identifier\n * @throws {AppwriteException}\n * @returns {Promise<Models.Target>}\n */\n async updatePushTarget(\n targetId: string,\n identifier: string,\n ): Promise<Models.Target> {\n const account = await this._account.updatePushTarget(targetId, identifier);\n this.triggerAuthCheck();\n return account;\n }\n\n /**\n * Delete push target\n *\n * No description at this moment\n *\n * @param {string} targetId\n * @throws {AppwriteException}\n * @returns {Promise<Models.Target>}\n */\n async deletePushTarget(\n targetId: string,\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n ): Promise<{}> {\n const account = await this._account.deletePushTarget(targetId);\n this.triggerAuthCheck();\n return account;\n }\n\n /**\n * Create email token (OTP)\n *\n * Sends the user an email with a secret key for creating a session.\n * If the provided user ID has not be registered, a new user will be created.\n * Use the returned user ID and secret and submit a request to the\n * POST /v1/account/sessions/token endpoint to complete the login process.\n * The secret sent to the user's email is valid for 15 minutes.\n *\n * A user is limited to 10 active sessions at a time by default. Learn more about session limits.\n *\n * @param {string} userId\n * @param {string} email\n * @param {boolean} phrase\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async createEmailToken(\n userId: string,\n email: string,\n phrase = false,\n ): Promise<Models.Token> {\n const account = await this._account.createEmailToken(userId, email, phrase);\n this.triggerAuthCheck();\n return account;\n }\n\n /**\n * Create magic URL token\n *\n * Sends the user an email with a secret key for creating a session.\n * If the provided user ID has not been registered, a new user will be created.\n * When the user clicks the link in the email, the user is redirected back to\n * the URL you provided with the secret key and userId values attached to\n * the URL query string. Use the query string parameters to submit a request\n * to the POST /v1/account/sessions/token endpoint to complete the login process.\n *\n * The link sent to the user's email address is valid for 1 hour.\n * If you are on a mobile device you can leave the URL parameter empty,\n * so that the login completion will be handled by your Appwrite instance\n * by default.\n *\n * A user is limited to 10 active sessions at a time by default. Learn more about session limits.\n *\n * @param {string} userId\n * @param {string} email\n * @param {string} url\n * Defaults to ID.unique()\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async createMagicURLToken(\n email: string,\n url?: string,\n userId: string = ID.unique(),\n phrase = true,\n ): Promise<Models.Token> {\n const session = await this._account.createMagicURLToken(\n userId,\n email,\n url,\n phrase,\n );\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Create OAuth2 token\n *\n * Allow the user to login to their account using the OAuth2 provider of their choice.\n * Each OAuth2 provider should be enabled from the Appwrite console first.\n * Use the success and failure arguments to provide a redirect URL's back to\n * your app when login is completed.\n *\n * If authentication succeeds, userId and secret of a token will be appended to the success URL as query parameters. These can be used to create a new session using the Create session endpoint.\n *\n * A user is limited to 10 active sessions at a time by default. Learn more about session limits.\n *\n *\n * @param {OAuthProvider} provider\n * @param {string} success\n * @param {string} failure\n * @param {string[]} scopes\n * @throws {AppwriteException}\n * @returns {void|string}\n */\n async createOAuth2Token(\n provider: OAuthProvider,\n success?: string,\n failure?: string,\n scopes?: string[],\n ): Promise<string | void> {\n const url = this._account.createOAuth2Token(\n provider,\n success,\n failure,\n scopes,\n );\n return url;\n }\n\n /**\n * Create Phone token\n *\n * Sends the user an SMS with a secret key for creating a session.\n * If the provided user ID has not be registered, a new user will be created.\n * Use the returned user ID and secret and submit a request to the\n * POST /v1/account/sessions/token endpoint to complete the login process.\n * The secret sent to the user's phone is valid for 15 minutes.\n *\n * A user is limited to 10 active sessions at a time by default. Learn more about session limits.\n *\n * @param {string} userId\n * @param {string} phone\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async createPhoneToken(userId: string, phone: string): Promise<Models.Token> {\n const session = await this._account.createPhoneToken(userId, phone);\n this.triggerAuthCheck();\n return session;\n }\n\n /**\n * Create Email Verification\n *\n * Use this endpoint to send a verification message to your user email address\n * to confirm they are the valid owners of that address. Both the **userId**\n * and **secret** arguments will be passed as query parameters to the URL you\n * have provided to be attached to the verification email. The provided URL\n * should redirect the user back to your app and allow you to complete the\n * verification process by verifying both the **userId** and **secret**\n * parameters. Learn more about how to [complete the verification\n * process](/docs/client/account#accountUpdateEmailVerification). The\n * verification link sent to the user's email address is valid for 7 days.\n *\n * Please note that in order to avoid a [Redirect\n * Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md),\n * the only valid redirect URLs are the ones from domains you have set when\n * adding your platforms in the console interface.\n *\n *\n * @param {string} url\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async createVerification(url: string): Promise<Models.Token> {\n const result = await this._account.createVerification(url);\n this.triggerAuthCheck();\n return result;\n }\n /**\n * Create Email Verification (confirmation)\n *\n * Use this endpoint to complete the user email verification process. Use both\n * the **userId** and **secret** parameters that were attached to your app URL\n * to verify the user email ownership. If confirmed this route will return a\n * 200 status code.\n *\n * @param {string} userId\n * @param {string} secret\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async updateVerification(\n userId: string,\n secret: string,\n ): Promise<Models.Token> {\n const result = await this._account.updateVerification(userId, secret);\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * Create Phone Verification\n *\n * Use this endpoint to send a verification SMS to the currently logged in\n * user. This endpoint is meant for use after updating a user's phone number\n * using the [accountUpdatePhone](/docs/client/account#accountUpdatePhone)\n * endpoint. Learn more about how to [complete the verification\n * process](/docs/client/account#accountUpdatePhoneVerification). The\n * verification code sent to the user's phone number is valid for 15 minutes.\n *\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async createPhoneVerification(): Promise<Models.Token> {\n const result = await this._account.createPhoneVerification();\n this.triggerAuthCheck();\n return result;\n }\n\n /**\n * Create Phone Verification (confirmation)\n *\n * Use this endpoint to complete the user phone verification process. Use the\n * **userId** and **secret** that were sent to your user's phone number to\n * verify the user email ownership. If confirmed this route will return a 200\n * status code.\n *\n * @param {string} userId\n * @param {string} secret\n * @throws {AppwriteException}\n * @returns {Promise<Models.Token>}\n */\n async updatePhoneVerification(\n userId: string,\n secret: string,\n ): Promise<Models.Token> {\n const result = await this._account.updatePhoneVerification(userId, secret);\n this.triggerAuthCheck();\n return result;\n }\n\n /* -------------------------------------------------------------------------- */\n /* Additional functionality */\n /* -------------------------------------------------------------------------- */\n\n /**\n * Convert Anonymous account with password\n *\n * This endpoint is a shortcut in order to convert an anonymous account\n * to a permanent one\n *\n * @param {string} email\n * @param {string} password\n * @param {ObjectSchema<TPrefs>} prefsSchema\n * @throws {AppwriteException}\n * @returns {Promise<Models.User<T>>}\n */\n async convertAnonymousAccountWithEmailAndPassword<\n T extends Models.Preferences,\n >(email: string, password: string): Promise<Models.User<T>> {\n const account = await this._account.updateEmail(email, password);\n this.triggerAuthCheck();\n return account as Models.User<T>;\n }\n\n /**\n * Logout - Shortcut for deletesession\n *\n * @throws {AppwriteException}\n * @returns {Promise<{}> }\n */\n\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n async logout(): Promise<{}> {\n return this.deleteSession('current');\n }\n\n /**\n * Triggering an auth-check\n *\n * Trigger a check of all account and\n * session-related actions to enable\n * reactive monitoring of authentication status\n * @returns {void}\n */\n triggerAuthCheck(): void {\n this._triggerManualAuthCheck$.next(true);\n }\n\n private async _checkIfAuthExists(): Promise<null | Models.User<Models.Preferences>> {\n try {\n const account = await this._account.get();\n return account;\n } catch (error) {\n console.warn(error);\n return null;\n }\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { Databases as AppwriteDatabases, ID, Models, Query } from 'appwrite';\nimport {\n Observable,\n distinctUntilChanged,\n map,\n of,\n shareReplay,\n startWith,\n switchMap,\n} from 'rxjs';\nimport { Account } from './account';\nimport { deepEqual, watch } from './helpers';\nimport { CLIENT, DEFAULT_DATABASE_ID } from './setup';\n\nconst DATABASE_ERROR = `No Database ID provided or database not initialized, \n use >>alternateDatabaseId << param`;\n\n@Injectable({\n providedIn: 'root',\n})\nexport class Databases {\n private accountService = inject(Account);\n private _databases: AppwriteDatabases = new AppwriteDatabases(CLIENT());\n private _client$ = of(CLIENT()).pipe(shareReplay(1));\n\n /* -------------------------------------------------------------------------- */\n /* Databases - CRUD - Appwrite API https://appwrite.io/docs/client/databases */\n /* -------------------------------------------------------------------------- */\n\n /**\n * Create Document\n *\n * Create a new Document. Before using this route, you should create a new\n * collection resource using either a [server\n * integration](/docs/server/databases#databasesCreateCollection) API or\n * directly from your database console.\n *\n \n * @param {string} collectionId\n * @param {Record<string, unknown>} data\n * @param {string[]} [permissions] \n * @param {string} [documentId]\n * defaults to ID.unique()\n * @param {string} [alternateDatabaseId]\n * @throws {AppwriteException}\n * @returns {Promise}\n */\n public async createDocument<\n CreateDocumentShape extends Record<string, unknown>,\n >(\n collectionId: string,\n data: Partial<CreateDocumentShape>,\n permissions?: string[],\n documentId: string = ID.unique(),\n alternateDatabaseId?: string,\n ): Promise<CreateDocumentShape & Models.Document> {\n const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();\n if (!databaseId) {\n throw new Error(DATABASE_ERROR);\n } else {\n this.accountService.triggerAuthCheck();\n return this._databases.createDocument(\n databaseId,\n collectionId,\n documentId,\n this._cleanData(data),\n permissions,\n );\n }\n }\n\n /**\n * Upsert Document\n *\n * Create a new Document if it can't be found (using $id), otherwise the document is updated.\n *\n * This will use incurr a read and a write to the database.\n *\n * Before using this route, you should create a new\n * collection resource using either a [server\n * integration](/docs/server/databases#databasesCreateCollection) API or\n * directly from your database console.\n *\n * @param {string} collectionId\n * @param {Record<string, unknown>} data\n * @param {string[]} [permissions]\n * @param {string} [alternateDatabaseId]\n * @throws {AppwriteException}\n * @returns {Promise}\n */\n public async upsertDocument<DocumentShape extends Models.Document>(\n collectionId: string,\n data: Partial<DocumentShape>,\n permissions?: string[],\n alternateDatabaseId?: string,\n ): Promise<Models.Document & DocumentShape> {\n const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();\n if (!databaseId) {\n throw new Error(DATABASE_ERROR);\n } else {\n this.accountService.triggerAuthCheck();\n\n const id = data.$id ?? ID.unique();\n try {\n // try to retrieve the document if it does exist update it\n const doc = await this.getDocument<DocumentShape>(collectionId, id);\n\n const merged = { ...doc, ...data };\n return this.updateDocument<DocumentShape>(collectionId, id, merged);\n } catch (error) {\n return this.createDocument(\n collectionId,\n this._cleanData(data),\n permissions,\n ID.unique(),\n databaseId,\n );\n }\n }\n }\n\n /**\n * Get Document\n *\n * Get a document by its unique ID. This endpoint response returns a JSON\n * object with the document data.\n *\n * @param {string} collectionId\n * @param {string} documentId\n * @param {string} [alternateDatabaseId]\n * @throws {AppwriteException}\n * @returns {Promise<DocumentShape>}\n */\n public async getDocument<DocumentShape extends Models.Document>(\n collectionId: string,\n documentId: string,\n alternateDatabaseId?: string,\n ): Promise<DocumentShape> {\n const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();\n if (!databaseId) {\n throw new Error(DATABASE_ERROR);\n } else {\n this.accountService.triggerAuthCheck();\n\n return this._databases.getDocument<DocumentShape>(\n databaseId,\n collectionId,\n documentId,\n );\n }\n }\n\n /**\n * List Documents\n *\n * Get a list of all the user's documents in a given collection. You can use\n * the query params to filter your results.\n *\n * @param {string} collectionId\n * @param {string[]} queries\n * @param {string} [alternateDatabaseId]\n * @throws {AppwriteException}\n * @returns {PromisePromise<{total: number; documents: (Input<typeof AppwriteDocumentSchema> & Input<typeof validationSchema>)[]; }>}\n */\n public async listDocuments<DocumentShape extends Models.Document>(\n collectionId: string,\n queries?: string[],\n alternateDatabaseId?: string,\n ): Promise<Models.DocumentList<DocumentShape>> {\n const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();\n if (!databaseId) {\n throw new Error(DATABASE_ERROR);\n } else {\n this.accountService.triggerAuthCheck();\n return this._databases.listDocuments<DocumentShape>(\n databaseId,\n collectionId,\n queries,\n );\n }\n }\n /**\n * Update Document\n *\n * Update a document by its unique ID. Using the patch method you can pass\n * only specific fields that will get updated.\n *\n * @param {string} collectionId\n * @param {string} documentId\n * @param {unknown} data\n * @param {string[]} [permissions]\n * @param {string} [alternateDatabaseId]\n * @throws {AppwriteException}\n * @returns {Promise<DocumentShape>}\n */\n public async updateDocument<DocumentShape extends Models.Document>(\n collectionId: string,\n documentId: string,\n data: Partial<DocumentShape>,\n permissions?: string[],\n alternateDatabaseId?: string,\n ): Promise<DocumentShape> {\n const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();\n if (!databaseId) {\n throw new Error(DATABASE_ERROR);\n } else {\n this.accountService.triggerAuthCheck();\n return this._databases.updateDocument<DocumentShape>(\n databaseId,\n collectionId,\n documentId,\n this._cleanData(data),\n permissions,\n );\n }\n }\n\n /**\n * Delete Document\n *\n * Delete a document by its unique ID.\n *\n * @param {string} collectionId\n * @param {string} documentId\n * @param {string} [alternateDatabaseId]\n * @throws {AppwriteException}\n * @returns {Promise<void>}\n */\n public async deleteDocument(\n collectionId: string,\n documentId: string,\n alternateDatabaseId?: string,\n ): Promise<Record<string, unknown>> {\n const databaseId = alternateDatabaseId ?? DEFAULT_DATABASE_ID();\n if (!databaseId) {\n throw new Error(DATABASE_ERROR);\n } else {\n this.accountService.triggerAuthCheck();\n return this._databases.deleteDocument(\n databaseId,\n collectionId,\n documentId,\n );\n }\n }\n\n /* -------------------------------------------------------------------------- */\n /* Databases Realtime */\n /* -------------------------------------------------------------------------- */\n /* --------------- https://appwrite.io/docs/realtime#channels --------------- */\n /* -------------------------------------------------------------------------- */\n\n /**\n * Monitor Collection\n *\n * Monitors real-time changes in a collection. Uses the configured default database\n * An alternate database can be provided if needed\n *\n * @param {string} collectionId\n * @param {string[]} [queries]\n * @param {string | string[]} [events]\n * @param {string} [alternativeDatabaseId]\n * @throws {AppwriteException}\n * @returns {Observable<(Input<typeof AppwriteDocumentSchema> & Input<typeof validationSchema>)[]>}\n */\n public collection$<DocumentShape extends Models.Document>(\n collectionId: string,\n queries: string[] = [],\n events?: string | string[],\n alternativeDatabaseId?: string,\n ): Observable<Models.DocumentList<DocumentShape>> {\n // check if required data is present runtime\n const { path } = this._generatePath(alternativeDatabaseId, collectionId);\n return this._client$.pipe(\n switchMap((client) => watch<Models.Document>(client, path, events)),\n startWith(true),\n switchMap(() => {\n return this.listDocuments<DocumentShape>(\n collectionId,\n queries,\n alternativeDatabaseId,\n );\n }),\n distinctUntilChanged((a, b) => deepEqual(a, b)),\n shareReplay(1),\n );\n }\n\n /**\n * Monitor Docuemnt\n *\n * Monitors real-time changes in a document. Uses the configured default database\n * An alternate database can be provided if needed\n *\n * @param {string} collectionId\n * @param {string} documentId\n * @param {string | string[]} [events]\n * @param {string} [alternativeDatabaseId]\n * @throws {AppwriteException}\n * @returns {Observable<(Input<typeof AppwriteDocumentSchema> & Input<typeof validationSchema>) | null>}\n */\n public document$<DocumentShape extends Models.Document>(\n collectionId: string,\n documentId: string,\n events?: string | string[],\n alternativeDatabaseId?: string,\n ): Observable<DocumentShape | null> {\n return this.collection$<DocumentShape>(\n collectionId,\n [Query.equal('$id', documentId)],\n events,\n alternativeDatabaseId,\n ).pipe(\n map((res: Models.DocumentList<DocumentShape>) => {\n if (res.documents[0]) {\n return res.documents[0];\n } else {\n return null;\n }\n }),\n shareReplay(1),\n );\n }\n\n // TODO: query listening is done manually for now\n // watch this Issue\n // https://github.com/appwrite/appwrite/issues/2490\n // https://appwrite.io/docs/databases#querying-documents