diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fe3ecdc..d09cdeaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Change Log +## 18.0.0 + +* Breaking: SDK now targets Appwrite 2.3 (`X-Appwrite-Response-Format: 2.3.0`) +* Breaking: removed `Organizations.getEstimation` +* Breaking: `QuerySuggestionResource.AppwritePushLedger` renamed to `PushLedger` +* Added: `Manager.updateOrganizationStatus` to block and unblock an organization +* Added: `Push` service over MQTT with `Topic`, plus `Client.setPushEndpoint` and `Client.setPushClientId` +* Added: `Growth` service with `createConversation`, `createInstallation`, `ConversationType` enum and `GrowthConversation` model +* Added: `Account.createRecoveryOTP` and `Account.updateRecoveryOTP` for code-based password recovery +* Added: `prompt` parameter on `Project.updateOAuth2*` for Auth0, Discord, GitHub, Kakao, Microsoft, Okta, Salesforce, Zoho +* Added: matching `prompt` fields and enums on the `OAuth2*` provider models +* Added: `replyToName` and `replyToEmail` on `Messaging.createEmail` and `Messaging.updateEmail` +* Added: `organizationUrl` on the `Installation` model +* Added: `ProjectEmailTemplateId.OtpRecovery` and Deno 1.21, 1.24, 1.35 in `Runtime` and `BuildRuntime` +* Updated: `Databases.getAttribute` return type now includes bigint, spatial, varchar and text attributes +* Updated: added `mqtt` and `buffer` dependencies and browser Node polyfills for `Push` +* Fixed: chunked uploads without a file in the payload now send a regular request + ## 17.0.0 * Breaking: SDK now targets Appwrite 2.2 (`X-Appwrite-Response-Format: 2.2.0`) @@ -17,7 +35,6 @@ * Added: `Account.createIdTokenSession` for native Apple and Google sign-in with `IdTokenProvider` enum * Added: `Account.createEmailVerificationOTP` and `Account.updateEmailVerificationOTP` * Added: `Messaging.createAppwriteProvider` and `Messaging.updateAppwriteProvider` for Appwrite push, with `qos` and `expiry` on `Topic` -* Added: `Organizations.getEstimation` for the current billing cycle charge estimate * Added: `Project.updatePasswordPwnedPolicy` with `PolicyPasswordPwned` model and `ProjectPolicyId.Passwordpwned` * Added: `nativeEnabled`, `nativeClientIds` parameters on `Project.updateOAuth2Apple` and `Project.updateOAuth2Google` and matching fields on `OAuth2Apple`, `OAuth2Google` * Added: `Framework.Jaspr`, `ProjectEmailTemplateId.OtpVerification`, `ProjectKeyScopes.DedicatedDatabasesExecute`, `QuerySuggestionResource.AppwritePushLedger` diff --git a/README.md b/README.md index 1c917b26..c1c963be 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Appwrite Console SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-console.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-2.2.0-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-2.3.0-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK targets Appwrite server version 2.0.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-console/releases) when APIs differ. +**This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-console/releases) when APIs differ. Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Console SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs) @@ -33,7 +33,7 @@ import { Client, Account } from '@appwrite.io/console'; To install with a CDN (content delivery network) add the following scripts to the bottom of your tag, but before you use any Appwrite services: ```html - + ``` ## Getting Started diff --git a/docs/examples/organizations/get-estimation.md b/docs/examples/account/create-recovery-otp.md similarity index 50% rename from docs/examples/organizations/get-estimation.md rename to docs/examples/account/create-recovery-otp.md index 7830f8fb..baf48cb9 100644 --- a/docs/examples/organizations/get-estimation.md +++ b/docs/examples/account/create-recovery-otp.md @@ -1,14 +1,15 @@ ```javascript -import { Client, Organizations } from '@appwrite.io/console'; +import { Client, Account } from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint .setProject(''); // Your project ID -const organizations = new Organizations(client); +const account = new Account(client); -const result = await organizations.getEstimation({ - organizationId: '', +const result = await account.createRecoveryOTP({ + email: 'email@example.com', + phrase: false, // optional }); console.log(result); diff --git a/docs/examples/account/update-recovery-otp.md b/docs/examples/account/update-recovery-otp.md new file mode 100644 index 00000000..4d23d7c6 --- /dev/null +++ b/docs/examples/account/update-recovery-otp.md @@ -0,0 +1,17 @@ +```javascript +import { Client, Account } from '@appwrite.io/console'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.updateRecoveryOTP({ + userId: '', + secret: '', + password: 'password', +}); + +console.log(result); +``` diff --git a/docs/examples/databases/create-documents.md b/docs/examples/databases/create-documents.md index e264c929..3ba08ea3 100644 --- a/docs/examples/databases/create-documents.md +++ b/docs/examples/databases/create-documents.md @@ -10,7 +10,16 @@ const databases = new Databases(client); const result = await databases.createDocuments({ databaseId: '', collectionId: '', - documents: [], + documents: [ + { + $id: 'example1', + username: 'walter.obrien', + email: 'walter.obrien@example.com', + fullName: "Walter O'Brien", + age: 30, + isAdmin: false, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/databases/create-index.md b/docs/examples/databases/create-index.md index 01a86729..788f5d2e 100644 --- a/docs/examples/databases/create-index.md +++ b/docs/examples/databases/create-index.md @@ -17,7 +17,7 @@ const result = await databases.createIndex({ collectionId: '', key: '', type: DatabasesIndexType.Key, - attributes: [], + attributes: ['username'], orders: [OrderBy.Asc], // optional lengths: [], // optional }); diff --git a/docs/examples/databases/delete-documents.md b/docs/examples/databases/delete-documents.md index fd68feeb..08fca8a1 100644 --- a/docs/examples/databases/delete-documents.md +++ b/docs/examples/databases/delete-documents.md @@ -10,7 +10,9 @@ const databases = new Databases(client); const result = await databases.deleteDocuments({ databaseId: '', collectionId: '', - queries: [], // optional + queries: [ + '{"method":"equal", "attribute":"$id", "values":[""]}', + ], // optional transactionId: '', // optional }); diff --git a/docs/examples/databases/upsert-documents.md b/docs/examples/databases/upsert-documents.md index 5ee9d710..5a62dd5c 100644 --- a/docs/examples/databases/upsert-documents.md +++ b/docs/examples/databases/upsert-documents.md @@ -10,7 +10,16 @@ const databases = new Databases(client); const result = await databases.upsertDocuments({ databaseId: '', collectionId: '', - documents: [], + documents: [ + { + $id: 'example1', + username: 'walter.obrien', + email: 'walter.obrien@example.com', + fullName: "Walter O'Brien", + age: 30, + isAdmin: false, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/documentsdb/create-documents.md b/docs/examples/documentsdb/create-documents.md index 1c961336..fc5fce8c 100644 --- a/docs/examples/documentsdb/create-documents.md +++ b/docs/examples/documentsdb/create-documents.md @@ -10,7 +10,16 @@ const documentsDB = new DocumentsDB(client); const result = await documentsDB.createDocuments({ databaseId: '', collectionId: '', - documents: [], + documents: [ + { + $id: 'example1', + username: 'walter.obrien', + email: 'walter.obrien@example.com', + fullName: "Walter O'Brien", + age: 30, + isAdmin: false, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/documentsdb/create-index.md b/docs/examples/documentsdb/create-index.md index ec398d9d..ce61944a 100644 --- a/docs/examples/documentsdb/create-index.md +++ b/docs/examples/documentsdb/create-index.md @@ -17,7 +17,7 @@ const result = await documentsDB.createIndex({ collectionId: '', key: '', type: DocumentsDBIndexType.Key, - attributes: [], + attributes: ['username'], orders: [OrderBy.Asc], // optional lengths: [], // optional }); diff --git a/docs/examples/documentsdb/delete-documents.md b/docs/examples/documentsdb/delete-documents.md index f9e603c0..125cd561 100644 --- a/docs/examples/documentsdb/delete-documents.md +++ b/docs/examples/documentsdb/delete-documents.md @@ -10,7 +10,9 @@ const documentsDB = new DocumentsDB(client); const result = await documentsDB.deleteDocuments({ databaseId: '', collectionId: '', - queries: [], // optional + queries: [ + '{"method":"equal", "attribute":"$id", "values":[""]}', + ], // optional transactionId: '', // optional }); diff --git a/docs/examples/documentsdb/upsert-documents.md b/docs/examples/documentsdb/upsert-documents.md index fb5a380a..ec7d8251 100644 --- a/docs/examples/documentsdb/upsert-documents.md +++ b/docs/examples/documentsdb/upsert-documents.md @@ -10,7 +10,16 @@ const documentsDB = new DocumentsDB(client); const result = await documentsDB.upsertDocuments({ databaseId: '', collectionId: '', - documents: [], + documents: [ + { + $id: 'example1', + username: 'walter.obrien', + email: 'walter.obrien@example.com', + fullName: "Walter O'Brien", + age: 30, + isAdmin: false, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/embeddings/create-text-embeddings.md b/docs/examples/embeddings/create-text-embeddings.md index 0b93299b..36639a80 100644 --- a/docs/examples/embeddings/create-text-embeddings.md +++ b/docs/examples/embeddings/create-text-embeddings.md @@ -8,7 +8,10 @@ const client = new Client() const embeddings = new Embeddings(client); const result = await embeddings.createTextEmbeddings({ - texts: [], + texts: [ + 'Appwrite helps developers build applications.', + 'Find documents with semantic search.', + ], model: EmbeddingModel.NomicEmbedText, // optional }); diff --git a/docs/examples/graphql/mutation.md b/docs/examples/graphql/mutation.md index 25c0ffbd..de226404 100644 --- a/docs/examples/graphql/mutation.md +++ b/docs/examples/graphql/mutation.md @@ -8,7 +8,9 @@ const client = new Client() const graphql = new Graphql(client); const result = await graphql.mutation({ - query: {}, + query: { + query: 'mutation { accountUpdateName(name: "Walter") { name } }', + }, }); console.log(result); diff --git a/docs/examples/graphql/query.md b/docs/examples/graphql/query.md index 73b3da23..5773b877 100644 --- a/docs/examples/graphql/query.md +++ b/docs/examples/graphql/query.md @@ -8,7 +8,9 @@ const client = new Client() const graphql = new Graphql(client); const result = await graphql.query({ - query: {}, + query: { + query: 'query { localeGet { ip } }', + }, }); console.log(result); diff --git a/docs/examples/growth/create-conversation.md b/docs/examples/growth/create-conversation.md new file mode 100644 index 00000000..ad321dcc --- /dev/null +++ b/docs/examples/growth/create-conversation.md @@ -0,0 +1,23 @@ +```javascript +import { Client, Growth, ConversationType } from '@appwrite.io/console'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const growth = new Growth(client); + +const result = await growth.createConversation({ + type: ConversationType.Support, + email: 'email@example.com', // optional + name: '', // optional + subject: '', // optional + message: '', // optional + organizationId: '', // optional + projectId: '', // optional + attributes: {}, // optional + attachment: document.getElementById('uploader').files[0], // optional +}); + +console.log(result); +``` diff --git a/docs/examples/growth/create-installation.md b/docs/examples/growth/create-installation.md new file mode 100644 index 00000000..a4fb466c --- /dev/null +++ b/docs/examples/growth/create-installation.md @@ -0,0 +1,25 @@ +```javascript +import { Client, Growth } from '@appwrite.io/console'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const growth = new Growth(client); + +const result = await growth.createInstallation({ + email: 'email@example.com', // optional + name: '', // optional + version: '', // optional + domain: '', // optional + database: '', // optional + hostIp: '', // optional + userAgent: '', // optional + os: '', // optional + arch: '', // optional + cpus: null, // optional + ram: null, // optional +}); + +console.log(result); +``` diff --git a/docs/examples/manager/update-organization-status.md b/docs/examples/manager/update-organization-status.md new file mode 100644 index 00000000..b1b1e19a --- /dev/null +++ b/docs/examples/manager/update-organization-status.md @@ -0,0 +1,17 @@ +```javascript +import { Client, Manager } from '@appwrite.io/console'; + +const client = new Client().setEndpoint( + 'https://.cloud.appwrite.io/v1', +); // Your API Endpoint + +const manager = new Manager(client); + +const result = await manager.updateOrganizationStatus({ + teamId: '', + status: false, + reason: '', // optional +}); + +console.log(result); +``` diff --git a/docs/examples/messaging/create-email.md b/docs/examples/messaging/create-email.md index a6da1435..bed1afc9 100644 --- a/docs/examples/messaging/create-email.md +++ b/docs/examples/messaging/create-email.md @@ -17,6 +17,8 @@ const result = await messaging.createEmail({ cc: [], // optional bcc: [], // optional attachments: [], // optional + replyToEmail: 'email@example.com', // optional + replyToName: '', // optional draft: false, // optional html: false, // optional scheduledAt: '2020-10-15T06:38:00.000+00:00', // optional diff --git a/docs/examples/messaging/update-email.md b/docs/examples/messaging/update-email.md index 77f765a4..a043d35e 100644 --- a/docs/examples/messaging/update-email.md +++ b/docs/examples/messaging/update-email.md @@ -18,6 +18,8 @@ const result = await messaging.updateEmail({ html: false, // optional cc: [], // optional bcc: [], // optional + replyToEmail: 'email@example.com', // optional + replyToName: '', // optional scheduledAt: '2020-10-15T06:38:00.000+00:00', // optional attachments: [], // optional }); diff --git a/docs/examples/organization/create-ephemeral-project-key.md b/docs/examples/organization/create-ephemeral-project-key.md index d1cb1295..4cc1194a 100644 --- a/docs/examples/organization/create-ephemeral-project-key.md +++ b/docs/examples/organization/create-ephemeral-project-key.md @@ -9,7 +9,7 @@ const organization = new Organization(client); const result = await organization.createEphemeralProjectKey({ projectId: '', - scopes: [ProjectKeyScopes.ProjectRead], + scopes: [ProjectKeyScopes.UsersRead], duration: 600, }); diff --git a/docs/examples/organization/create-project-key.md b/docs/examples/organization/create-project-key.md index 2361bb94..7e9786a8 100644 --- a/docs/examples/organization/create-project-key.md +++ b/docs/examples/organization/create-project-key.md @@ -11,7 +11,7 @@ const result = await organization.createProjectKey({ projectId: '', keyId: '', name: '', - scopes: [ProjectKeyScopes.ProjectRead], + scopes: [ProjectKeyScopes.UsersRead], expire: '2020-10-15T06:38:00.000+00:00', // optional }); diff --git a/docs/examples/organization/update-project-key.md b/docs/examples/organization/update-project-key.md index 550ecebd..dbb0c7ed 100644 --- a/docs/examples/organization/update-project-key.md +++ b/docs/examples/organization/update-project-key.md @@ -11,7 +11,7 @@ const result = await organization.updateProjectKey({ projectId: '', keyId: '', name: '', - scopes: [ProjectKeyScopes.ProjectRead], + scopes: [ProjectKeyScopes.UsersRead], expire: '2020-10-15T06:38:00.000+00:00', // optional }); diff --git a/docs/examples/project/create-ephemeral-key.md b/docs/examples/project/create-ephemeral-key.md index f5f76a42..f86455cf 100644 --- a/docs/examples/project/create-ephemeral-key.md +++ b/docs/examples/project/create-ephemeral-key.md @@ -8,7 +8,7 @@ const client = new Client() const project = new Project(client); const result = await project.createEphemeralKey({ - scopes: [ProjectKeyScopes.ProjectRead], + scopes: [ProjectKeyScopes.UsersRead], duration: 600, }); diff --git a/docs/examples/project/create-key.md b/docs/examples/project/create-key.md index 5354d732..d6341200 100644 --- a/docs/examples/project/create-key.md +++ b/docs/examples/project/create-key.md @@ -10,7 +10,7 @@ const project = new Project(client); const result = await project.createKey({ keyId: '', name: '', - scopes: [ProjectKeyScopes.ProjectRead], + scopes: [ProjectKeyScopes.UsersRead], expire: '2020-10-15T06:38:00.000+00:00', // optional }); diff --git a/docs/examples/project/create-smtp-test.md b/docs/examples/project/create-smtp-test.md index 300e972c..96653785 100644 --- a/docs/examples/project/create-smtp-test.md +++ b/docs/examples/project/create-smtp-test.md @@ -8,7 +8,7 @@ const client = new Client() const project = new Project(client); const result = await project.createSMTPTest({ - emails: [], + emails: ['recipient@example.com'], }); console.log(result); diff --git a/docs/examples/project/update-key.md b/docs/examples/project/update-key.md index 3bba96a0..0be53efe 100644 --- a/docs/examples/project/update-key.md +++ b/docs/examples/project/update-key.md @@ -10,7 +10,7 @@ const project = new Project(client); const result = await project.updateKey({ keyId: '', name: '', - scopes: [ProjectKeyScopes.ProjectRead], + scopes: [ProjectKeyScopes.UsersRead], expire: '2020-10-15T06:38:00.000+00:00', // optional }); diff --git a/docs/examples/project/update-labels.md b/docs/examples/project/update-labels.md index 8df4ffb4..12f8fceb 100644 --- a/docs/examples/project/update-labels.md +++ b/docs/examples/project/update-labels.md @@ -8,7 +8,7 @@ const client = new Client() const project = new Project(client); const result = await project.updateLabels({ - labels: [], + labels: ['production'], }); console.log(result); diff --git a/docs/examples/project/update-o-auth-2-auth-0.md b/docs/examples/project/update-o-auth-2-auth-0.md index 7c2898e4..01abc6ba 100644 --- a/docs/examples/project/update-o-auth-2-auth-0.md +++ b/docs/examples/project/update-o-auth-2-auth-0.md @@ -1,5 +1,9 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { + Client, + Project, + ProjectOAuth2Auth0Prompt, +} from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -11,6 +15,7 @@ const result = await project.updateOAuth2Auth0({ clientId: '', // optional clientSecret: '', // optional endpoint: '', // optional + prompt: [ProjectOAuth2Auth0Prompt.None], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-discord.md b/docs/examples/project/update-o-auth-2-discord.md index 1116db06..fc7aaa09 100644 --- a/docs/examples/project/update-o-auth-2-discord.md +++ b/docs/examples/project/update-o-auth-2-discord.md @@ -1,5 +1,9 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { + Client, + Project, + ProjectOAuth2DiscordPrompt, +} from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -10,6 +14,7 @@ const project = new Project(client); const result = await project.updateOAuth2Discord({ clientId: '', // optional clientSecret: '', // optional + prompt: [ProjectOAuth2DiscordPrompt.None], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-git-hub.md b/docs/examples/project/update-o-auth-2-git-hub.md index f3cc3b13..0c4e2045 100644 --- a/docs/examples/project/update-o-auth-2-git-hub.md +++ b/docs/examples/project/update-o-auth-2-git-hub.md @@ -1,5 +1,9 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { + Client, + Project, + ProjectOAuth2GitHubPrompt, +} from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -10,6 +14,7 @@ const project = new Project(client); const result = await project.updateOAuth2GitHub({ clientId: '', // optional clientSecret: '', // optional + prompt: [ProjectOAuth2GitHubPrompt.SelectAccount], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-kakao.md b/docs/examples/project/update-o-auth-2-kakao.md index b92e049d..cf304988 100644 --- a/docs/examples/project/update-o-auth-2-kakao.md +++ b/docs/examples/project/update-o-auth-2-kakao.md @@ -1,5 +1,9 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { + Client, + Project, + ProjectOAuth2KakaoPrompt, +} from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -10,6 +14,7 @@ const project = new Project(client); const result = await project.updateOAuth2Kakao({ clientId: '', // optional clientSecret: '', // optional + prompt: [ProjectOAuth2KakaoPrompt.None], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-microsoft.md b/docs/examples/project/update-o-auth-2-microsoft.md index 18f21fe9..9bbbfa13 100644 --- a/docs/examples/project/update-o-auth-2-microsoft.md +++ b/docs/examples/project/update-o-auth-2-microsoft.md @@ -1,5 +1,9 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { + Client, + Project, + ProjectOAuth2MicrosoftPrompt, +} from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -11,6 +15,7 @@ const result = await project.updateOAuth2Microsoft({ applicationId: '', // optional applicationSecret: '', // optional tenant: '', // optional + prompt: [ProjectOAuth2MicrosoftPrompt.None], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-okta.md b/docs/examples/project/update-o-auth-2-okta.md index a94ad691..da983aca 100644 --- a/docs/examples/project/update-o-auth-2-okta.md +++ b/docs/examples/project/update-o-auth-2-okta.md @@ -1,5 +1,5 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { Client, Project, ProjectOAuth2OktaPrompt } from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -12,6 +12,7 @@ const result = await project.updateOAuth2Okta({ clientSecret: '', // optional domain: 'example.com', // optional authorizationServerId: '', // optional + prompt: [ProjectOAuth2OktaPrompt.None], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-salesforce.md b/docs/examples/project/update-o-auth-2-salesforce.md index 04c3da12..8b19d764 100644 --- a/docs/examples/project/update-o-auth-2-salesforce.md +++ b/docs/examples/project/update-o-auth-2-salesforce.md @@ -1,5 +1,9 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { + Client, + Project, + ProjectOAuth2SalesforcePrompt, +} from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -10,6 +14,7 @@ const project = new Project(client); const result = await project.updateOAuth2Salesforce({ customerKey: '', // optional customerSecret: '', // optional + prompt: [ProjectOAuth2SalesforcePrompt.Login], // optional enabled: false, // optional }); diff --git a/docs/examples/project/update-o-auth-2-zoho.md b/docs/examples/project/update-o-auth-2-zoho.md index 0a1ff861..524d1185 100644 --- a/docs/examples/project/update-o-auth-2-zoho.md +++ b/docs/examples/project/update-o-auth-2-zoho.md @@ -1,5 +1,5 @@ ```javascript -import { Client, Project } from '@appwrite.io/console'; +import { Client, Project, ProjectOAuth2ZohoPrompt } from '@appwrite.io/console'; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -10,6 +10,7 @@ const project = new Project(client); const result = await project.updateOAuth2Zoho({ clientId: '', // optional clientSecret: '', // optional + prompt: [ProjectOAuth2ZohoPrompt.Consent], // optional enabled: false, // optional }); diff --git a/docs/examples/tablesdb/create-index.md b/docs/examples/tablesdb/create-index.md index 1b4f0aba..629a8ef1 100644 --- a/docs/examples/tablesdb/create-index.md +++ b/docs/examples/tablesdb/create-index.md @@ -17,7 +17,7 @@ const result = await tablesDB.createIndex({ tableId: '', key: '', type: TablesDBIndexType.Key, - columns: [], + columns: ['username'], orders: [OrderBy.Asc], // optional lengths: [], // optional }); diff --git a/docs/examples/tablesdb/create-rows.md b/docs/examples/tablesdb/create-rows.md index 2c046135..cd3ca02a 100644 --- a/docs/examples/tablesdb/create-rows.md +++ b/docs/examples/tablesdb/create-rows.md @@ -10,7 +10,16 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.createRows({ databaseId: '', tableId: '', - rows: [], + rows: [ + { + $id: 'example1', + username: 'walter.obrien', + email: 'walter.obrien@example.com', + fullName: "Walter O'Brien", + age: 30, + isAdmin: false, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/tablesdb/delete-rows.md b/docs/examples/tablesdb/delete-rows.md index 58df6f19..b528d434 100644 --- a/docs/examples/tablesdb/delete-rows.md +++ b/docs/examples/tablesdb/delete-rows.md @@ -10,7 +10,7 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.deleteRows({ databaseId: '', tableId: '', - queries: [], // optional + queries: ['{"method":"equal", "attribute":"$id", "values":[""]}'], // optional transactionId: '', // optional }); diff --git a/docs/examples/tablesdb/upsert-rows.md b/docs/examples/tablesdb/upsert-rows.md index 005f7b4e..e42d2d44 100644 --- a/docs/examples/tablesdb/upsert-rows.md +++ b/docs/examples/tablesdb/upsert-rows.md @@ -10,7 +10,16 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.upsertRows({ databaseId: '', tableId: '', - rows: [], + rows: [ + { + $id: 'example1', + username: 'walter.obrien', + email: 'walter.obrien@example.com', + fullName: "Walter O'Brien", + age: 30, + isAdmin: false, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/teams/create-membership.md b/docs/examples/teams/create-membership.md index 35581d30..d06d4894 100644 --- a/docs/examples/teams/create-membership.md +++ b/docs/examples/teams/create-membership.md @@ -9,7 +9,7 @@ const teams = new Teams(client); const result = await teams.createMembership({ teamId: '', - roles: [], + roles: ['editor'], email: 'email@example.com', // optional userId: '', // optional phone: '+12065550100', // optional diff --git a/docs/examples/teams/update-membership.md b/docs/examples/teams/update-membership.md index 6ed36583..efa11810 100644 --- a/docs/examples/teams/update-membership.md +++ b/docs/examples/teams/update-membership.md @@ -10,7 +10,7 @@ const teams = new Teams(client); const result = await teams.updateMembership({ teamId: '', membershipId: '', - roles: [], + roles: ['editor'], }); console.log(result); diff --git a/docs/examples/users/update-labels.md b/docs/examples/users/update-labels.md index bf287f36..62e2a7e7 100644 --- a/docs/examples/users/update-labels.md +++ b/docs/examples/users/update-labels.md @@ -9,7 +9,7 @@ const users = new Users(client); const result = await users.updateLabels({ userId: '', - labels: [], + labels: ['subscriber'], }); console.log(result); diff --git a/docs/examples/vectorsdb/create-collection.md b/docs/examples/vectorsdb/create-collection.md index b69be570..12996a2c 100644 --- a/docs/examples/vectorsdb/create-collection.md +++ b/docs/examples/vectorsdb/create-collection.md @@ -11,7 +11,7 @@ const result = await vectorsDB.createCollection({ databaseId: '', collectionId: '', name: '', - dimension: 1, + dimension: 4, permissions: [Permission.read(Role.any())], // optional documentSecurity: false, // optional enabled: false, // optional diff --git a/docs/examples/vectorsdb/create-documents.md b/docs/examples/vectorsdb/create-documents.md index 6bda5bf1..e49698e8 100644 --- a/docs/examples/vectorsdb/create-documents.md +++ b/docs/examples/vectorsdb/create-documents.md @@ -10,7 +10,15 @@ const vectorsDB = new VectorsDB(client); const result = await vectorsDB.createDocuments({ databaseId: '', collectionId: '', - documents: [], + documents: [ + { + $id: 'example1', + embeddings: [0.12, -0.55, 0.88, 1.02], + metadata: { + name: 'First document', + }, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/vectorsdb/create-index.md b/docs/examples/vectorsdb/create-index.md index 1f8b422d..d02efaeb 100644 --- a/docs/examples/vectorsdb/create-index.md +++ b/docs/examples/vectorsdb/create-index.md @@ -17,7 +17,7 @@ const result = await vectorsDB.createIndex({ collectionId: '', key: '', type: VectorsDBIndexType.HnswEuclidean, - attributes: [], + attributes: ['embeddings'], orders: [OrderBy.Asc], // optional lengths: [], // optional }); diff --git a/docs/examples/vectorsdb/create-operations.md b/docs/examples/vectorsdb/create-operations.md index fc90ed8b..ce27bd39 100644 --- a/docs/examples/vectorsdb/create-operations.md +++ b/docs/examples/vectorsdb/create-operations.md @@ -16,7 +16,10 @@ const result = await vectorsDB.createOperations({ collectionId: '', documentId: '', data: { - name: "Walter O'Brien", + embeddings: [0.12, -0.55, 0.88, 1.02], + metadata: { + name: 'First document', + }, }, }, ], // optional diff --git a/docs/examples/vectorsdb/delete-documents.md b/docs/examples/vectorsdb/delete-documents.md index e699485b..98e2d1bf 100644 --- a/docs/examples/vectorsdb/delete-documents.md +++ b/docs/examples/vectorsdb/delete-documents.md @@ -10,7 +10,9 @@ const vectorsDB = new VectorsDB(client); const result = await vectorsDB.deleteDocuments({ databaseId: '', collectionId: '', - queries: [], // optional + queries: [ + '{"method":"equal", "attribute":"$id", "values":[""]}', + ], // optional transactionId: '', // optional }); diff --git a/docs/examples/vectorsdb/update-collection.md b/docs/examples/vectorsdb/update-collection.md index 49102441..afa2d357 100644 --- a/docs/examples/vectorsdb/update-collection.md +++ b/docs/examples/vectorsdb/update-collection.md @@ -11,7 +11,7 @@ const result = await vectorsDB.updateCollection({ databaseId: '', collectionId: '', name: '', - dimension: 1, // optional + dimension: 4, // optional permissions: [Permission.read(Role.any())], // optional documentSecurity: false, // optional enabled: false, // optional diff --git a/docs/examples/vectorsdb/upsert-documents.md b/docs/examples/vectorsdb/upsert-documents.md index af58d1d4..6f13ae74 100644 --- a/docs/examples/vectorsdb/upsert-documents.md +++ b/docs/examples/vectorsdb/upsert-documents.md @@ -10,7 +10,15 @@ const vectorsDB = new VectorsDB(client); const result = await vectorsDB.upsertDocuments({ databaseId: '', collectionId: '', - documents: [], + documents: [ + { + $id: 'example1', + embeddings: [0.12, -0.55, 0.88, 1.02], + metadata: { + name: 'First document', + }, + }, + ], transactionId: '', // optional }); diff --git a/docs/examples/webhooks/create.md b/docs/examples/webhooks/create.md index f5817fbe..39301365 100644 --- a/docs/examples/webhooks/create.md +++ b/docs/examples/webhooks/create.md @@ -11,7 +11,7 @@ const result = await webhooks.create({ webhookId: '', url: 'https://example.com/webhook', name: '', - events: [], + events: ['users.*.create'], enabled: false, // optional tls: false, // optional authUsername: '', // optional diff --git a/docs/examples/webhooks/update.md b/docs/examples/webhooks/update.md index 78071ef4..058f73e1 100644 --- a/docs/examples/webhooks/update.md +++ b/docs/examples/webhooks/update.md @@ -11,7 +11,7 @@ const result = await webhooks.update({ webhookId: '', name: '', url: 'https://example.com/webhook', - events: [], + events: ['users.*.create'], enabled: false, // optional tls: false, // optional authUsername: '', // optional diff --git a/package-lock.json b/package-lock.json index f63c20c8..3bd09a25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,17 @@ { "name": "@appwrite.io/console", - "version": "17.0.0", + "version": "18.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@appwrite.io/console", - "version": "17.0.0", + "version": "18.0.0", "license": "BSD-3-Clause", "dependencies": { - "json-bigint": "1.0.0" + "buffer": "6.0.3", + "json-bigint": "1.0.0", + "mqtt": "5.15.2" }, "devDependencies": { "@eslint/js": "10.0.1", @@ -21,6 +23,7 @@ "playwright": "1.56.1", "prettier": "3.9.6", "rollup": "4.63.2", + "rollup-plugin-polyfill-node": "0.13.0", "serve-handler": "6.1.7", "tslib": "2.8.1", "typescript": "5.9.3", @@ -33,6 +36,15 @@ "brace-expansion": "5.0.9" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@cacheable/memory": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", @@ -290,9 +302,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -329,6 +338,29 @@ } } }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", @@ -496,9 +528,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -513,9 +542,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -530,9 +556,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -547,9 +570,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -564,9 +584,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -581,9 +598,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -598,9 +612,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -615,9 +626,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -632,9 +640,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -649,9 +654,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -666,9 +668,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -683,9 +682,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -700,9 +696,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -821,6 +814,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.2.tgz", + "integrity": "sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.24.tgz", + "integrity": "sha512-NRvUNC/JFGPJvqdAfEve8oginbM6V08u5NzLWpG8MwA2kTPOLnqk+wpwuPT+mp3aUsxyuT6m2gnrPuHYCruzEg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -828,6 +839,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.70.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz", @@ -1058,6 +1078,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", @@ -1108,6 +1140,26 @@ "node": "18 || 20 || >=22" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", @@ -1117,6 +1169,18 @@ "node": "*" } }, + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, "node_modules/brace-expansion": { "version": "5.0.9", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", @@ -1130,6 +1194,48 @@ "node": "20 || >=22" } }, + "node_modules/broker-factory": { + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.15.tgz", + "integrity": "sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.50" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -1154,6 +1260,12 @@ "qified": "^0.10.1" } }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1161,6 +1273,35 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -1190,7 +1331,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1406,6 +1546,24 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1427,6 +1585,19 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-unique-numbers": { + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1555,6 +1726,12 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", @@ -1562,6 +1739,26 @@ "dev": true, "license": "MIT" }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1582,14 +1779,29 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.3" + "hasown": "^2.0.4" }, "engines": { "node": ">= 0.4" @@ -1645,6 +1857,16 @@ "dev": true, "license": "ISC" }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", @@ -1708,6 +1930,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -1757,11 +1985,62 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mqtt": { + "version": "5.15.2", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.2.tgz", + "integrity": "sha512-VWZU2CSUY3U3oN0PSBRDE5SNsFi4zqqNeQ/uv3pZWqY3CrBXD/dhd0ZLjlsk5YnebGlrapi4lRVNJPUNQ5aZ3w==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/natural-compare": { @@ -1771,6 +2050,16 @@ "dev": true, "license": "MIT" }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1933,6 +2222,21 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1973,6 +2277,22 @@ "node": ">= 0.6" } }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -1995,6 +2315,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, "node_modules/rollup": { "version": "4.63.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.2.tgz", @@ -2041,6 +2367,39 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-polyfill-node": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.13.0.tgz", + "integrity": "sha512-FYEvpCaD5jGtyBuBFcQImEGmTxDTPbiHjJdrYIp+mFIwgXiXabxvKUK7ZT9P31ozu2Tqm9llYQMRWsfvTMTAOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-inject": "^5.0.4" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -2106,6 +2465,48 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -2153,7 +2554,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -2169,6 +2569,12 @@ "node": ">= 0.8.0" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -2207,6 +2613,12 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2217,6 +2629,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2243,6 +2661,74 @@ "node": ">=0.10.0" } }, + "node_modules/worker-factory": { + "version": "7.0.50", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.50.tgz", + "integrity": "sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-timers": { + "version": "8.0.34", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.34.tgz", + "integrity": "sha512-WXL+Dqsm0G6dnC66rQsvM3tPT2adhbqSirWUCZGglkALOr8ocS0KlpU0iuKbjflJOlu3ydZikMEVrYnHOM9suw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.18", + "worker-timers-worker": "^9.0.15" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.18", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.18.tgz", + "integrity": "sha512-FrjzDVX1wKfZN0gRbCFqv8VHuTncG4sbI/WGEg4tSSQeIsnwqg4YBYWMAHYLJtUDEYYmiK65UKFrVMVk2irDSg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "broker-factory": "^3.1.15", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.15" + } + }, + "node_modules/worker-timers-worker": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.15.tgz", + "integrity": "sha512-KKUe7lZ/Aignr51H6hOUik8LwTnIgojH/1lwhli8A8qIEIyewogZTpNpMW5B6BF7nmwBOkUoTYgf1H3QShcjSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "tslib": "^2.8.1", + "worker-factory": "^7.0.50" + } + }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "license": "MIT", + "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 + } + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 1683a871..72f45446 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@appwrite.io/console", "homepage": "https://appwrite.io/support", "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API", - "version": "17.0.0", + "version": "18.0.0", "license": "BSD-3-Clause", "main": "dist/cjs/sdk.js", "exports": { @@ -37,7 +37,9 @@ "node": ">=18.0.0" }, "dependencies": { - "json-bigint": "1.0.0" + "buffer": "6.0.3", + "json-bigint": "1.0.0", + "mqtt": "5.15.2" }, "devDependencies": { "@eslint/js": "10.0.1", @@ -49,6 +51,7 @@ "playwright": "1.56.1", "prettier": "3.9.6", "rollup": "4.63.2", + "rollup-plugin-polyfill-node": "0.13.0", "serve-handler": "6.1.7", "tslib": "2.8.1", "typescript": "5.9.3", diff --git a/rollup.config.mjs b/rollup.config.mjs index 0d5864b6..99cecd12 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -3,6 +3,7 @@ const pkg = JSON.parse(readFileSync('./package.json', 'utf8')); import typescript from '@rollup/plugin-typescript'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; +import nodePolyfills from 'rollup-plugin-polyfill-node'; const external = Object.keys(pkg.dependencies ?? {}); @@ -28,7 +29,10 @@ export default [ { input: 'src/index.ts', plugins: [ - resolve({ browser: true }), + // The Push service uses mqtt.js, which references Node built-ins + // (buffer/process/stream/…); polyfill them for the browser bundle. + nodePolyfills(), + resolve({ browser: true, preferBuiltins: false }), commonjs(), typescript({ outDir: 'dist' }), ], diff --git a/src/client.ts b/src/client.ts index 86594e53..f2eabf6f 100644 --- a/src/client.ts +++ b/src/client.ts @@ -368,6 +368,8 @@ class Client { config: { endpoint: string; endpointRealtime: string; + endpointPush: string; + pushClientId: string; project: string; key: string; organization: string; @@ -386,6 +388,8 @@ class Client { } = { endpoint: 'https://cloud.appwrite.io/v1', endpointRealtime: '', + endpointPush: '', + pushClientId: '', project: '', key: '', organization: '', @@ -409,8 +413,8 @@ class Client { 'x-sdk-name': 'Console', 'x-sdk-platform': 'console', 'x-sdk-language': 'web', - 'x-sdk-version': '17.0.0', - 'X-Appwrite-Response-Format': '2.2.0', + 'x-sdk-version': '18.0.0', + 'X-Appwrite-Response-Format': '2.3.0', }; /** @@ -479,6 +483,53 @@ class Client { return this; } + /** + * Set Push Endpoint + * + * The MQTT-over-WebSocket URL the AppwritePush service connects to. + * + * @param {string} endpointPush + * + * @returns {this} + */ + setPushEndpoint(endpointPush: string): this { + if (!endpointPush || typeof endpointPush !== 'string') { + throw new AppwriteException('Endpoint must be a valid string'); + } + + if ( + !endpointPush.startsWith('ws://') && + !endpointPush.startsWith('wss://') + ) { + throw new AppwriteException( + 'Invalid push endpoint URL: ' + endpointPush, + ); + } + + this.config.endpointPush = endpointPush; + return this; + } + + /** + * Set Push Client Id + * + * A stable client id for the AppwritePush service. The broker keys its + * offline-replay cursor on this id, so pass a stable value to resume replay across + * reloads/restarts. Defaults to a per-connection id when unset. + * + * @param {string} pushClientId + * + * @returns {this} + */ + setPushClientId(pushClientId: string): this { + if (!pushClientId || typeof pushClientId !== 'string') { + throw new AppwriteException('Client id must be a valid string'); + } + + this.config.pushClientId = pushClientId; + return this; + } + /** * Set self-signed * @@ -1097,12 +1148,9 @@ class Client { const options: RequestInit = { method, headers, + credentials: this.config.credentials, }; - if (headers['X-Appwrite-Dev-Key'] === undefined) { - options.credentials = this.config.credentials; - } - if (method === 'GET') { for (const [key, value] of Object.entries(Client.flatten(params))) { url.searchParams.append(key, value); @@ -1151,7 +1199,7 @@ class Client { ) ?? []; if (!file || !fileParam) { - throw new Error('File not found in payload'); + return await this.call(method, url, headers, originalPayload); } if (file.size <= Client.CHUNK_SIZE) { diff --git a/src/enums/build-runtime.ts b/src/enums/build-runtime.ts index 2dfd9c61..01558318 100644 --- a/src/enums/build-runtime.ts +++ b/src/enums/build-runtime.ts @@ -31,6 +31,9 @@ export enum BuildRuntime { Pythonml311 = 'python-ml-3.11', Pythonml312 = 'python-ml-3.12', Pythonml313 = 'python-ml-3.13', + Deno121 = 'deno-1.21', + Deno124 = 'deno-1.24', + Deno135 = 'deno-1.35', Deno140 = 'deno-1.40', Deno146 = 'deno-1.46', Deno20 = 'deno-2.0', diff --git a/src/enums/conversation-type.ts b/src/enums/conversation-type.ts new file mode 100644 index 00000000..924d33b2 --- /dev/null +++ b/src/enums/conversation-type.ts @@ -0,0 +1,9 @@ +export enum ConversationType { + Support = 'support', + Feedback = 'feedback', + Docs = 'docs', + Enterprise = 'enterprise', + Startup = 'startup', + Partner = 'partner', + Sponsorship = 'sponsorship', +} diff --git a/src/enums/o-auth-2-auth-0-prompt.ts b/src/enums/o-auth-2-auth-0-prompt.ts new file mode 100644 index 00000000..e015cae0 --- /dev/null +++ b/src/enums/o-auth-2-auth-0-prompt.ts @@ -0,0 +1,5 @@ +export enum OAuth2Auth0Prompt { + None = 'none', + Login = 'login', + Consent = 'consent', +} diff --git a/src/enums/o-auth-2-discord-prompt.ts b/src/enums/o-auth-2-discord-prompt.ts new file mode 100644 index 00000000..ece22e48 --- /dev/null +++ b/src/enums/o-auth-2-discord-prompt.ts @@ -0,0 +1,4 @@ +export enum OAuth2DiscordPrompt { + None = 'none', + Consent = 'consent', +} diff --git a/src/enums/o-auth-2-github-prompt.ts b/src/enums/o-auth-2-github-prompt.ts new file mode 100644 index 00000000..0519185f --- /dev/null +++ b/src/enums/o-auth-2-github-prompt.ts @@ -0,0 +1,3 @@ +export enum OAuth2GithubPrompt { + SelectAccount = 'select_account', +} diff --git a/src/enums/o-auth-2-kakao-prompt.ts b/src/enums/o-auth-2-kakao-prompt.ts new file mode 100644 index 00000000..2b5713a5 --- /dev/null +++ b/src/enums/o-auth-2-kakao-prompt.ts @@ -0,0 +1,6 @@ +export enum OAuth2KakaoPrompt { + None = 'none', + Login = 'login', + Create = 'create', + SelectAccount = 'select_account', +} diff --git a/src/enums/o-auth-2-microsoft-prompt.ts b/src/enums/o-auth-2-microsoft-prompt.ts new file mode 100644 index 00000000..53689659 --- /dev/null +++ b/src/enums/o-auth-2-microsoft-prompt.ts @@ -0,0 +1,6 @@ +export enum OAuth2MicrosoftPrompt { + None = 'none', + Login = 'login', + Consent = 'consent', + SelectAccount = 'select_account', +} diff --git a/src/enums/o-auth-2-okta-prompt.ts b/src/enums/o-auth-2-okta-prompt.ts new file mode 100644 index 00000000..8e279048 --- /dev/null +++ b/src/enums/o-auth-2-okta-prompt.ts @@ -0,0 +1,5 @@ +export enum OAuth2OktaPrompt { + None = 'none', + Login = 'login', + Consent = 'consent', +} diff --git a/src/enums/o-auth-2-salesforce-prompt.ts b/src/enums/o-auth-2-salesforce-prompt.ts new file mode 100644 index 00000000..131dd107 --- /dev/null +++ b/src/enums/o-auth-2-salesforce-prompt.ts @@ -0,0 +1,4 @@ +export enum OAuth2SalesforcePrompt { + Login = 'login', + Consent = 'consent', +} diff --git a/src/enums/o-auth-2-zoho-prompt.ts b/src/enums/o-auth-2-zoho-prompt.ts new file mode 100644 index 00000000..22ac9540 --- /dev/null +++ b/src/enums/o-auth-2-zoho-prompt.ts @@ -0,0 +1,3 @@ +export enum OAuth2ZohoPrompt { + Consent = 'consent', +} diff --git a/src/enums/project-email-template-id.ts b/src/enums/project-email-template-id.ts index b2f5a808..be742ea3 100644 --- a/src/enums/project-email-template-id.ts +++ b/src/enums/project-email-template-id.ts @@ -7,4 +7,5 @@ export enum ProjectEmailTemplateId { SessionAlert = 'sessionAlert', OtpSession = 'otpSession', OtpVerification = 'otpVerification', + OtpRecovery = 'otpRecovery', } diff --git a/src/enums/project-o-auth-2-auth-0-prompt.ts b/src/enums/project-o-auth-2-auth-0-prompt.ts new file mode 100644 index 00000000..a09f94f7 --- /dev/null +++ b/src/enums/project-o-auth-2-auth-0-prompt.ts @@ -0,0 +1,5 @@ +export enum ProjectOAuth2Auth0Prompt { + None = 'none', + Login = 'login', + Consent = 'consent', +} diff --git a/src/enums/project-o-auth-2-discord-prompt.ts b/src/enums/project-o-auth-2-discord-prompt.ts new file mode 100644 index 00000000..ec81b72b --- /dev/null +++ b/src/enums/project-o-auth-2-discord-prompt.ts @@ -0,0 +1,4 @@ +export enum ProjectOAuth2DiscordPrompt { + None = 'none', + Consent = 'consent', +} diff --git a/src/enums/project-o-auth-2-git-hub-prompt.ts b/src/enums/project-o-auth-2-git-hub-prompt.ts new file mode 100644 index 00000000..9abd5121 --- /dev/null +++ b/src/enums/project-o-auth-2-git-hub-prompt.ts @@ -0,0 +1,3 @@ +export enum ProjectOAuth2GitHubPrompt { + SelectAccount = 'select_account', +} diff --git a/src/enums/project-o-auth-2-kakao-prompt.ts b/src/enums/project-o-auth-2-kakao-prompt.ts new file mode 100644 index 00000000..6eae8b29 --- /dev/null +++ b/src/enums/project-o-auth-2-kakao-prompt.ts @@ -0,0 +1,6 @@ +export enum ProjectOAuth2KakaoPrompt { + None = 'none', + Login = 'login', + Create = 'create', + SelectAccount = 'select_account', +} diff --git a/src/enums/project-o-auth-2-microsoft-prompt.ts b/src/enums/project-o-auth-2-microsoft-prompt.ts new file mode 100644 index 00000000..27ff7b26 --- /dev/null +++ b/src/enums/project-o-auth-2-microsoft-prompt.ts @@ -0,0 +1,6 @@ +export enum ProjectOAuth2MicrosoftPrompt { + None = 'none', + Login = 'login', + Consent = 'consent', + SelectAccount = 'select_account', +} diff --git a/src/enums/project-o-auth-2-okta-prompt.ts b/src/enums/project-o-auth-2-okta-prompt.ts new file mode 100644 index 00000000..eedf1a01 --- /dev/null +++ b/src/enums/project-o-auth-2-okta-prompt.ts @@ -0,0 +1,5 @@ +export enum ProjectOAuth2OktaPrompt { + None = 'none', + Login = 'login', + Consent = 'consent', +} diff --git a/src/enums/project-o-auth-2-salesforce-prompt.ts b/src/enums/project-o-auth-2-salesforce-prompt.ts new file mode 100644 index 00000000..777d4a91 --- /dev/null +++ b/src/enums/project-o-auth-2-salesforce-prompt.ts @@ -0,0 +1,4 @@ +export enum ProjectOAuth2SalesforcePrompt { + Login = 'login', + Consent = 'consent', +} diff --git a/src/enums/project-o-auth-2-zoho-prompt.ts b/src/enums/project-o-auth-2-zoho-prompt.ts new file mode 100644 index 00000000..6f67a617 --- /dev/null +++ b/src/enums/project-o-auth-2-zoho-prompt.ts @@ -0,0 +1,3 @@ +export enum ProjectOAuth2ZohoPrompt { + Consent = 'consent', +} diff --git a/src/enums/query-suggestion-resource.ts b/src/enums/query-suggestion-resource.ts index 8e6c0019..272418af 100644 --- a/src/enums/query-suggestion-resource.ts +++ b/src/enums/query-suggestion-resource.ts @@ -40,7 +40,7 @@ export enum QuerySuggestionResource { Buckets = 'buckets', Providers = 'providers', Messages = 'messages', - AppwritePushLedger = 'appwritepushledger', + PushLedger = 'pushledger', Topics = 'topics', Subscribers = 'subscribers', Targets = 'targets', diff --git a/src/enums/runtime.ts b/src/enums/runtime.ts index 7bbcbb6f..1f3d4823 100644 --- a/src/enums/runtime.ts +++ b/src/enums/runtime.ts @@ -31,6 +31,9 @@ export enum Runtime { Pythonml311 = 'python-ml-3.11', Pythonml312 = 'python-ml-3.12', Pythonml313 = 'python-ml-3.13', + Deno121 = 'deno-1.21', + Deno124 = 'deno-1.24', + Deno135 = 'deno-1.35', Deno140 = 'deno-1.40', Deno146 = 'deno-1.46', Deno20 = 'deno-2.0', diff --git a/src/index.ts b/src/index.ts index fc8cf549..4d501ac5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ /** * Appwrite Console SDK * - * This SDK targets Appwrite server version 2.2.x as shipped on Appwrite Cloud. + * This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud. * Self-hosted releases can lag behind Cloud — if you run an older self-hosted * build, use a matching older SDK from * [previous releases](https://github.com/appwrite/sdk-for-console/releases) @@ -22,6 +22,7 @@ export { Domains } from './services/domains'; export { Embeddings } from './services/embeddings'; export { Functions } from './services/functions'; export { Graphql } from './services/graphql'; +export { Growth } from './services/growth'; export { Locale } from './services/locale'; export { Manager } from './services/manager'; export { Messaging } from './services/messaging'; @@ -50,6 +51,7 @@ export { VectorsDB } from './services/vectors-db'; export { Waf } from './services/waf'; export { Webhooks } from './services/webhooks'; export { Realtime } from './services/realtime'; +export { Push } from './services/push'; export type { Models, Payload, @@ -57,10 +59,17 @@ export type { UploadProgress, } from './client'; export type { RealtimeSubscription } from './services/realtime'; +export type { + PushMessage, + PushSubscription, + SubscribeOptions, + MessageCallback, +} from './services/push'; export type { QueryTypes, QueryTypesList } from './query'; export { Permission } from './permission'; export { Role } from './role'; export { ID } from './id'; +export { Topic, ResolvedTopic } from './topic'; export { Channel } from './channel'; export { Operator, Condition } from './operator'; export { AccountKeyScopes } from './enums/account-key-scopes'; @@ -96,6 +105,7 @@ export { TemplateReferenceType } from './enums/template-reference-type'; export { VCSReferenceType } from './enums/vcs-reference-type'; export { DeploymentDownloadType } from './enums/deployment-download-type'; export { ExecutionMethod } from './enums/execution-method'; +export { ConversationType } from './enums/conversation-type'; export { BlockResourceType } from './enums/block-resource-type'; export { BlockMode } from './enums/block-mode'; export { Region } from './enums/region'; @@ -112,8 +122,16 @@ export { OrganizationKeyScopes } from './enums/organization-key-scopes'; export { AddonKey } from './enums/addon-key'; export { UsageRange } from './enums/usage-range'; export { ProjectAuthMethodId } from './enums/project-auth-method-id'; +export { ProjectOAuth2Auth0Prompt } from './enums/project-o-auth-2-auth-0-prompt'; +export { ProjectOAuth2DiscordPrompt } from './enums/project-o-auth-2-discord-prompt'; +export { ProjectOAuth2GitHubPrompt } from './enums/project-o-auth-2-git-hub-prompt'; export { ProjectOAuth2GooglePrompt } from './enums/project-o-auth-2-google-prompt'; +export { ProjectOAuth2KakaoPrompt } from './enums/project-o-auth-2-kakao-prompt'; +export { ProjectOAuth2MicrosoftPrompt } from './enums/project-o-auth-2-microsoft-prompt'; export { ProjectOAuth2OidcPrompt } from './enums/project-o-auth-2-oidc-prompt'; +export { ProjectOAuth2OktaPrompt } from './enums/project-o-auth-2-okta-prompt'; +export { ProjectOAuth2SalesforcePrompt } from './enums/project-o-auth-2-salesforce-prompt'; +export { ProjectOAuth2ZohoPrompt } from './enums/project-o-auth-2-zoho-prompt'; export { ProjectOAuthProviderId } from './enums/project-o-auth-provider-id'; export { ProjectPolicyId } from './enums/project-policy-id'; export { ProjectProtocolId } from './enums/project-protocol-id'; @@ -153,8 +171,16 @@ export { DeploymentStatus } from './enums/deployment-status'; export { ExecutionResourceType } from './enums/execution-resource-type'; export { ExecutionTrigger } from './enums/execution-trigger'; export { ExecutionStatus } from './enums/execution-status'; +export { OAuth2GithubPrompt } from './enums/o-auth-2-github-prompt'; +export { OAuth2DiscordPrompt } from './enums/o-auth-2-discord-prompt'; export { OAuth2GooglePrompt } from './enums/o-auth-2-google-prompt'; +export { OAuth2ZohoPrompt } from './enums/o-auth-2-zoho-prompt'; +export { OAuth2SalesforcePrompt } from './enums/o-auth-2-salesforce-prompt'; +export { OAuth2Auth0Prompt } from './enums/o-auth-2-auth-0-prompt'; export { OAuth2OidcPrompt } from './enums/o-auth-2-oidc-prompt'; +export { OAuth2OktaPrompt } from './enums/o-auth-2-okta-prompt'; +export { OAuth2MicrosoftPrompt } from './enums/o-auth-2-microsoft-prompt'; +export { OAuth2KakaoPrompt } from './enums/o-auth-2-kakao-prompt'; export { PlatformType } from './enums/platform-type'; export { ProxyRuleDeploymentResourceType } from './enums/proxy-rule-deployment-resource-type'; export { ProxyRuleStatus } from './enums/proxy-rule-status'; diff --git a/src/models.ts b/src/models.ts index 59230f45..a6c959af 100644 --- a/src/models.ts +++ b/src/models.ts @@ -11,8 +11,16 @@ import { ExecutionStatus } from './enums/execution-status'; import { ProjectAuthMethodId } from './enums/project-auth-method-id'; import { ProjectServiceId } from './enums/project-service-id'; import { ProjectProtocolId } from './enums/project-protocol-id'; +import { OAuth2GithubPrompt } from './enums/o-auth-2-github-prompt'; +import { OAuth2DiscordPrompt } from './enums/o-auth-2-discord-prompt'; import { OAuth2GooglePrompt } from './enums/o-auth-2-google-prompt'; +import { OAuth2ZohoPrompt } from './enums/o-auth-2-zoho-prompt'; +import { OAuth2SalesforcePrompt } from './enums/o-auth-2-salesforce-prompt'; +import { OAuth2Auth0Prompt } from './enums/o-auth-2-auth-0-prompt'; import { OAuth2OidcPrompt } from './enums/o-auth-2-oidc-prompt'; +import { OAuth2OktaPrompt } from './enums/o-auth-2-okta-prompt'; +import { OAuth2MicrosoftPrompt } from './enums/o-auth-2-microsoft-prompt'; +import { OAuth2KakaoPrompt } from './enums/o-auth-2-kakao-prompt'; import { PlatformType } from './enums/platform-type'; import { ProxyRuleDeploymentResourceType } from './enums/proxy-rule-deployment-resource-type'; import { ProxyRuleStatus } from './enums/proxy-rule-status'; @@ -4673,6 +4681,10 @@ export namespace Models { * VCS (Version Control System) installation ID. */ providerInstallationId: string; + /** + * VCS (Version Control System) organization URL. Points at the configured instance, which is not the provider's own host for a self-hosted install. + */ + organizationUrl: string; }; /** @@ -5625,6 +5637,10 @@ export namespace Models { * GitHub OAuth2 client secret. */ clientSecret: string; + /** + * GitHub OAuth2 prompt values. + */ + prompt: OAuth2GithubPrompt[]; }; /** @@ -5647,6 +5663,10 @@ export namespace Models { * Discord OAuth2 client secret. */ clientSecret: string; + /** + * Discord OAuth2 prompt values. + */ + prompt: OAuth2DiscordPrompt[]; }; /** @@ -5879,6 +5899,10 @@ export namespace Models { * Zoho OAuth2 client secret. */ clientSecret: string; + /** + * Zoho OAuth2 prompt values. + */ + prompt: OAuth2ZohoPrompt[]; }; /** @@ -6099,6 +6123,10 @@ export namespace Models { * Salesforce OAuth2 consumer secret. */ customerSecret: string; + /** + * Salesforce OAuth2 prompt values. + */ + prompt: OAuth2SalesforcePrompt[]; }; /** @@ -6415,6 +6443,10 @@ export namespace Models { * Auth0 OAuth2 client secret. */ clientSecret: string; + /** + * Auth0 OAuth2 prompt values. + */ + prompt: OAuth2Auth0Prompt[]; /** * Auth0 OAuth2 endpoint domain. */ @@ -6497,6 +6529,10 @@ export namespace Models { * OpenID Connect OAuth2 client secret. */ clientSecret: string; + /** + * OpenID Connect prompt values controlling the authentication and consent screens. + */ + prompt: OAuth2OidcPrompt[]; /** * OpenID Connect well-known configuration URL. When set, authorization, token, and user info endpoints can be discovered automatically. */ @@ -6513,10 +6549,6 @@ export namespace Models { * OpenID Connect user info endpoint URL. */ userInfoURL: string; - /** - * OpenID Connect prompt values controlling the authentication and consent screens. - */ - prompt: OAuth2OidcPrompt[]; /** * Maximum authentication age in seconds. When set, the user must have authenticated within this many seconds. */ @@ -6543,6 +6575,10 @@ export namespace Models { * Okta OAuth2 client secret. */ clientSecret: string; + /** + * Okta OAuth2 prompt values. + */ + prompt: OAuth2OktaPrompt[]; /** * Okta OAuth2 domain. */ @@ -6633,6 +6669,10 @@ export namespace Models { * Microsoft OAuth2 application secret. */ applicationSecret: string; + /** + * Microsoft OAuth2 prompt values. + */ + prompt: OAuth2MicrosoftPrompt[]; /** * Microsoft Entra ID tenant identifier. Use 'common', 'organizations', 'consumers' or a specific tenant ID. */ @@ -6703,6 +6743,10 @@ export namespace Models { * Kakao OAuth2 client secret. */ clientSecret: string; + /** + * Kakao OAuth2 prompt values. + */ + prompt: OAuth2KakaoPrompt[]; }; /** @@ -6847,15 +6891,15 @@ export namespace Models { */ $id: string; /** - * Whether password pwned policy is enabled. + * Whether passwords are checked against known data breaches and the result recorded on the user. */ enabled: boolean; /** - * Whether passwords are checked when a session is created. + * Whether a sign-in with a breached password is refused until the password is reset. */ sessions: boolean; /** - * Whether users signing in with a breached password are blocked until they reset it. Only applies when sessions are checked. + * Whether a breached password is rejected when a user signs up or sets a new password. */ users: boolean; }; @@ -11132,6 +11176,24 @@ export namespace Models { organizationCredits: number; }; + /** + * Growth Conversation + */ + export type GrowthConversation = { + /** + * Conversation type. + */ + type: string; + /** + * Email address the conversation was filed under. For a signed in console user this is the account email. + */ + email: string; + /** + * Organization ID the conversation was filed against. Empty when none was given or the user is not a member. + */ + organizationId: string; + }; + /** * Extensions */ diff --git a/src/services/account.ts b/src/services/account.ts index 8c878fa3..6e00bad5 100644 --- a/src/services/account.ts +++ b/src/services/account.ts @@ -3616,6 +3616,174 @@ export class Account { return this.client.call('put', uri, apiHeaders, payload); } + /** + * Use this endpoint to send a 6-digit password recovery code to the user's email address. Unlike [createRecovery](https://appwrite.io/docs/references/cloud/client-web/account#createRecovery), this method requires no redirect URL, which makes it suitable for mobile and desktop apps that cannot host a recovery page. Learn more about how to [complete the recovery process](https://appwrite.io/docs/references/cloud/client-web/account#updateRecoveryOTP). The code sent to the user's email address is valid for 15 minutes. + * + * Enable the **phrase** parameter to include a randomly generated security phrase in both the email and the response. Showing that phrase in your app lets the user confirm the email genuinely came from your request, which helps protect against phishing. + * + * + * @param {string} params.email - User email. + * @param {boolean} params.phrase - Toggle for security phrase. If enabled, email will be sent with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow. + * @throws {AppwriteException} + * @returns {Promise} + */ + createRecoveryOTP(params: { + email: string; + phrase?: boolean; + }): Promise; + /** + * Use this endpoint to send a 6-digit password recovery code to the user's email address. Unlike [createRecovery](https://appwrite.io/docs/references/cloud/client-web/account#createRecovery), this method requires no redirect URL, which makes it suitable for mobile and desktop apps that cannot host a recovery page. Learn more about how to [complete the recovery process](https://appwrite.io/docs/references/cloud/client-web/account#updateRecoveryOTP). The code sent to the user's email address is valid for 15 minutes. + * + * Enable the **phrase** parameter to include a randomly generated security phrase in both the email and the response. Showing that phrase in your app lets the user confirm the email genuinely came from your request, which helps protect against phishing. + * + * + * @param {string} email - User email. + * @param {boolean} phrase - Toggle for security phrase. If enabled, email will be sent with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + createRecoveryOTP(email: string, phrase?: boolean): Promise; + createRecoveryOTP( + paramsOrFirst: { email: string; phrase?: boolean } | string, + ...rest: [boolean?] + ): Promise { + let params: { email: string; phrase?: boolean }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + email: string; + phrase?: boolean; + }; + } else { + params = { + email: paramsOrFirst as string, + phrase: rest[0] as boolean, + }; + } + + const email = params.email; + const phrase = params.phrase; + + if (typeof email === 'undefined') { + throw new AppwriteException('Missing required parameter: "email"'); + } + const apiPath = '/account/recovery/otp'; + const payload: Payload = {}; + if (typeof email !== 'undefined') { + payload['email'] = email; + } + if (typeof phrase !== 'undefined') { + payload['phrase'] = phrase; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('post', uri, apiHeaders, payload); + } + + /** + * Use this endpoint to complete the user password recovery process using the 6-digit code that was emailed by [createRecoveryOTP](https://appwrite.io/docs/references/cloud/client-web/account#createRecoveryOTP). Pass the **userId** of the user along with the **secret** code from the email and the new **password** to set. If confirmed, this route will return a 200 status code, the code is consumed and the user's password is updated. + * + * + * @param {string} params.userId - User ID. + * @param {string} params.secret - Valid recovery OTP code. + * @param {string} params.password - New user password. Must be between 8 and 256 chars. + * @throws {AppwriteException} + * @returns {Promise} + */ + updateRecoveryOTP(params: { + userId: string; + secret: string; + password: string; + }): Promise; + /** + * Use this endpoint to complete the user password recovery process using the 6-digit code that was emailed by [createRecoveryOTP](https://appwrite.io/docs/references/cloud/client-web/account#createRecoveryOTP). Pass the **userId** of the user along with the **secret** code from the email and the new **password** to set. If confirmed, this route will return a 200 status code, the code is consumed and the user's password is updated. + * + * + * @param {string} userId - User ID. + * @param {string} secret - Valid recovery OTP code. + * @param {string} password - New user password. Must be between 8 and 256 chars. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updateRecoveryOTP( + userId: string, + secret: string, + password: string, + ): Promise; + updateRecoveryOTP( + paramsOrFirst: + { userId: string; secret: string; password: string } | string, + ...rest: [string?, string?] + ): Promise { + let params: { userId: string; secret: string; password: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + userId: string; + secret: string; + password: string; + }; + } else { + params = { + userId: paramsOrFirst as string, + secret: rest[0] as string, + password: rest[1] as string, + }; + } + + const userId = params.userId; + const secret = params.secret; + const password = params.password; + + if (typeof userId === 'undefined') { + throw new AppwriteException('Missing required parameter: "userId"'); + } + if (typeof secret === 'undefined') { + throw new AppwriteException('Missing required parameter: "secret"'); + } + if (typeof password === 'undefined') { + throw new AppwriteException( + 'Missing required parameter: "password"', + ); + } + const apiPath = '/account/recovery/otp'; + const payload: Payload = {}; + if (typeof userId !== 'undefined') { + payload['userId'] = userId; + } + if (typeof secret !== 'undefined') { + payload['secret'] = secret; + } + if (typeof password !== 'undefined') { + payload['password'] = password; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('put', uri, apiHeaders, payload); + } + /** * Get the list of active sessions across different devices for the currently logged in user. * @@ -4456,7 +4624,7 @@ export class Account { } /** - * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. + * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. A session holds one push target per provider, so if one already exists this endpoint updates and returns that target instead of creating a second one, and a device that rotates its token is never notified twice. * * @param {string} params.targetId - Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. * @param {string} params.identifier - The target identifier (token, email, phone etc.) @@ -4470,7 +4638,7 @@ export class Account { providerId?: string; }): Promise; /** - * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. + * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. A session holds one push target per provider, so if one already exists this endpoint updates and returns that target instead of creating a second one, and a device that rotates its token is never notified twice. * * @param {string} targetId - Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. * @param {string} identifier - The target identifier (token, email, phone etc.) diff --git a/src/services/console.ts b/src/services/console.ts index fb2d29db..29b5cc66 100644 --- a/src/services/console.ts +++ b/src/services/console.ts @@ -1063,7 +1063,7 @@ export class Console { /** * Get the Appwrite built-in default email template for the specified type and locale. Always returns the unmodified default, ignoring any custom project overrides. * - * @param {ProjectEmailTemplateId} params.templateId - Email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification + * @param {ProjectEmailTemplateId} params.templateId - Email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification, otpRecovery * @param {ProjectEmailTemplateLocale} params.locale - Template locale. If left empty, the fallback locale (en) will be used. * @throws {AppwriteException} * @returns {Promise} @@ -1075,7 +1075,7 @@ export class Console { /** * Get the Appwrite built-in default email template for the specified type and locale. Always returns the unmodified default, ignoring any custom project overrides. * - * @param {ProjectEmailTemplateId} templateId - Email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification + * @param {ProjectEmailTemplateId} templateId - Email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification, otpRecovery * @param {ProjectEmailTemplateLocale} locale - Template locale. If left empty, the fallback locale (en) will be used. * @throws {AppwriteException} * @returns {Promise} diff --git a/src/services/databases.ts b/src/services/databases.ts index c35bd0bb..0d998723 100644 --- a/src/services/databases.ts +++ b/src/services/databases.ts @@ -7080,7 +7080,7 @@ export class Databases { * @param {string} params.collectionId - Collection ID. * @param {string} params.key - Attribute Key. * @throws {AppwriteException} - * @returns {Promise} + * @returns {Promise} * @deprecated This API has been deprecated since 1.8.0. Please use `TablesDB.getColumn` instead. */ getAttribute(params: { @@ -7089,6 +7089,7 @@ export class Databases { key: string; }): Promise< | Models.AttributeBoolean + | Models.AttributeBigint | Models.AttributeInteger | Models.AttributeFloat | Models.AttributeEmail @@ -7097,6 +7098,13 @@ export class Databases { | Models.AttributeIp | Models.AttributeDatetime | Models.AttributeRelationship + | Models.AttributePoint + | Models.AttributeLine + | Models.AttributePolygon + | Models.AttributeVarchar + | Models.AttributeText + | Models.AttributeMediumtext + | Models.AttributeLongtext | Models.AttributeString >; /** @@ -7106,7 +7114,7 @@ export class Databases { * @param {string} collectionId - Collection ID. * @param {string} key - Attribute Key. * @throws {AppwriteException} - * @returns {Promise} + * @returns {Promise} * @deprecated Use the object parameter style method for a better developer experience. */ getAttribute( @@ -7115,6 +7123,7 @@ export class Databases { key: string, ): Promise< | Models.AttributeBoolean + | Models.AttributeBigint | Models.AttributeInteger | Models.AttributeFloat | Models.AttributeEmail @@ -7123,6 +7132,13 @@ export class Databases { | Models.AttributeIp | Models.AttributeDatetime | Models.AttributeRelationship + | Models.AttributePoint + | Models.AttributeLine + | Models.AttributePolygon + | Models.AttributeVarchar + | Models.AttributeText + | Models.AttributeMediumtext + | Models.AttributeLongtext | Models.AttributeString >; getAttribute( @@ -7131,6 +7147,7 @@ export class Databases { ...rest: [string?, string?] ): Promise< | Models.AttributeBoolean + | Models.AttributeBigint | Models.AttributeInteger | Models.AttributeFloat | Models.AttributeEmail @@ -7139,6 +7156,13 @@ export class Databases { | Models.AttributeIp | Models.AttributeDatetime | Models.AttributeRelationship + | Models.AttributePoint + | Models.AttributeLine + | Models.AttributePolygon + | Models.AttributeVarchar + | Models.AttributeText + | Models.AttributeMediumtext + | Models.AttributeLongtext | Models.AttributeString > { let params: { databaseId: string; collectionId: string; key: string }; diff --git a/src/services/growth.ts b/src/services/growth.ts new file mode 100644 index 00000000..51eaaf3a --- /dev/null +++ b/src/services/growth.ts @@ -0,0 +1,411 @@ +import { + AppwriteException, + Client, + type Payload, + UploadProgress, +} from '../client'; +import type { Models } from '../models'; + +import { ConversationType } from '../enums/conversation-type'; +export class Growth { + client: Client; + + constructor(client: Client) { + this.client = client; + } + + /** + * Create a conversation with the Appwrite team: support requests, product and docs feedback, enterprise, startup and partner applications, and event sponsorship requests. Signed in console users are identified by their session or JWT; everyone else must pass an email. Attach a file of up to 5MB to support and feedback conversations. + * + * @param {ConversationType} params.type - Conversation type. + * @param {string} params.email - Email address to reply to. Required without a console session, ignored with one. + * @param {string} params.name - Name of the person filing the conversation. Required for enterprise, startup, partner and sponsorship. + * @param {string} params.subject - Conversation subject. + * @param {string} params.message - Conversation message. Required for support, feedback, enterprise and partner. + * @param {string} params.organizationId - Organization ID the conversation is about. Kept only when the session user is a member. + * @param {string} params.projectId - Project ID the conversation is about. Kept only when it belongs to the kept organization. + * @param {object} params.attributes - Additional attributes for the conversation type, as an object or a JSON string. + * @param {File} params.attachment - File attachment, support and feedback only, max 5MB. + * @throws {AppwriteException} + * @returns {Promise} + */ + createConversation(params: { + type: ConversationType; + email?: string; + name?: string; + subject?: string; + message?: string; + organizationId?: string; + projectId?: string; + attributes?: object; + attachment?: File; + onProgress?: (progress: UploadProgress) => void; + }): Promise; + /** + * Create a conversation with the Appwrite team: support requests, product and docs feedback, enterprise, startup and partner applications, and event sponsorship requests. Signed in console users are identified by their session or JWT; everyone else must pass an email. Attach a file of up to 5MB to support and feedback conversations. + * + * @param {ConversationType} type - Conversation type. + * @param {string} email - Email address to reply to. Required without a console session, ignored with one. + * @param {string} name - Name of the person filing the conversation. Required for enterprise, startup, partner and sponsorship. + * @param {string} subject - Conversation subject. + * @param {string} message - Conversation message. Required for support, feedback, enterprise and partner. + * @param {string} organizationId - Organization ID the conversation is about. Kept only when the session user is a member. + * @param {string} projectId - Project ID the conversation is about. Kept only when it belongs to the kept organization. + * @param {object} attributes - Additional attributes for the conversation type, as an object or a JSON string. + * @param {File} attachment - File attachment, support and feedback only, max 5MB. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + createConversation( + type: ConversationType, + email?: string, + name?: string, + subject?: string, + message?: string, + organizationId?: string, + projectId?: string, + attributes?: object, + attachment?: File, + onProgress?: (progress: UploadProgress) => void, + ): Promise; + createConversation( + paramsOrFirst: + | { + type: ConversationType; + email?: string; + name?: string; + subject?: string; + message?: string; + organizationId?: string; + projectId?: string; + attributes?: object; + attachment?: File; + onProgress?: (progress: UploadProgress) => void; + } + | ConversationType, + ...rest: [ + string?, + string?, + string?, + string?, + string?, + string?, + object?, + File?, + ((progress: UploadProgress) => void)?, + ] + ): Promise { + let params: { + type: ConversationType; + email?: string; + name?: string; + subject?: string; + message?: string; + organizationId?: string; + projectId?: string; + attributes?: object; + attachment?: File; + }; + let onProgress: (progress: UploadProgress) => void; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) && + ('type' in paramsOrFirst || + 'email' in paramsOrFirst || + 'name' in paramsOrFirst || + 'subject' in paramsOrFirst || + 'message' in paramsOrFirst || + 'organizationId' in paramsOrFirst || + 'projectId' in paramsOrFirst || + 'attributes' in paramsOrFirst || + 'attachment' in paramsOrFirst || + 'onProgress' in paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + type: ConversationType; + email?: string; + name?: string; + subject?: string; + message?: string; + organizationId?: string; + projectId?: string; + attributes?: object; + attachment?: File; + }; + onProgress = paramsOrFirst?.onProgress as ( + progress: UploadProgress, + ) => void; + } else { + params = { + type: paramsOrFirst as ConversationType, + email: rest[0] as string, + name: rest[1] as string, + subject: rest[2] as string, + message: rest[3] as string, + organizationId: rest[4] as string, + projectId: rest[5] as string, + attributes: rest[6] as object, + attachment: rest[7] as File, + }; + onProgress = rest[8] as (progress: UploadProgress) => void; + } + + const type = params.type; + const email = params.email; + const name = params.name; + const subject = params.subject; + const message = params.message; + const organizationId = params.organizationId; + const projectId = params.projectId; + const attributes = params.attributes; + const attachment = params.attachment; + + if (typeof type === 'undefined') { + throw new AppwriteException('Missing required parameter: "type"'); + } + const apiPath = '/growth/conversations'; + const payload: Payload = {}; + if (typeof type !== 'undefined') { + payload['type'] = type; + } + if (typeof email !== 'undefined') { + payload['email'] = email; + } + if (typeof name !== 'undefined') { + payload['name'] = name; + } + if (typeof subject !== 'undefined') { + payload['subject'] = subject; + } + if (typeof message !== 'undefined') { + payload['message'] = message; + } + if (typeof organizationId !== 'undefined') { + payload['organizationId'] = organizationId; + } + if (typeof projectId !== 'undefined') { + payload['projectId'] = projectId; + } + if (typeof attributes !== 'undefined') { + payload['attributes'] = attributes; + } + if (typeof attachment !== 'undefined') { + payload['attachment'] = attachment; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'multipart/form-data', + accept: 'application/json', + }; + + return this.client.chunkedUpload( + 'post', + uri, + apiHeaders, + payload, + onProgress, + ); + } + + /** + * Record a self-hosted installation. Every report is stored as its own row; the admin email is optional because headless CLI installs create no account. + * + * @param {string} params.email - Admin email from the installation, when an account was created. + * @param {string} params.name - Admin name. + * @param {string} params.version - Appwrite version. + * @param {string} params.domain - Installation domain. + * @param {string} params.database - Database adapter. + * @param {string} params.hostIp - Resolved installation host IP address. + * @param {string} params.userAgent - Installation user agent. + * @param {string} params.os - Installation operating system. + * @param {string} params.arch - Installation CPU architecture. + * @param {number} params.cpus - Installation CPU count. + * @param {number} params.ram - Installation RAM in MB. + * @throws {AppwriteException} + * @returns {Promise<{}>} + */ + createInstallation(params?: { + email?: string; + name?: string; + version?: string; + domain?: string; + database?: string; + hostIp?: string; + userAgent?: string; + os?: string; + arch?: string; + cpus?: number; + ram?: number; + }): Promise<{}>; + /** + * Record a self-hosted installation. Every report is stored as its own row; the admin email is optional because headless CLI installs create no account. + * + * @param {string} email - Admin email from the installation, when an account was created. + * @param {string} name - Admin name. + * @param {string} version - Appwrite version. + * @param {string} domain - Installation domain. + * @param {string} database - Database adapter. + * @param {string} hostIp - Resolved installation host IP address. + * @param {string} userAgent - Installation user agent. + * @param {string} os - Installation operating system. + * @param {string} arch - Installation CPU architecture. + * @param {number} cpus - Installation CPU count. + * @param {number} ram - Installation RAM in MB. + * @throws {AppwriteException} + * @returns {Promise<{}>} + * @deprecated Use the object parameter style method for a better developer experience. + */ + createInstallation( + email?: string, + name?: string, + version?: string, + domain?: string, + database?: string, + hostIp?: string, + userAgent?: string, + os?: string, + arch?: string, + cpus?: number, + ram?: number, + ): Promise<{}>; + createInstallation( + paramsOrFirst?: + | { + email?: string; + name?: string; + version?: string; + domain?: string; + database?: string; + hostIp?: string; + userAgent?: string; + os?: string; + arch?: string; + cpus?: number; + ram?: number; + } + | string, + ...rest: [ + string?, + string?, + string?, + string?, + string?, + string?, + string?, + string?, + number?, + number?, + ] + ): Promise<{}> { + let params: { + email?: string; + name?: string; + version?: string; + domain?: string; + database?: string; + hostIp?: string; + userAgent?: string; + os?: string; + arch?: string; + cpus?: number; + ram?: number; + }; + + if ( + (typeof paramsOrFirst === 'undefined' && rest.length === 0) || + (paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst)) + ) { + params = (paramsOrFirst || {}) as { + email?: string; + name?: string; + version?: string; + domain?: string; + database?: string; + hostIp?: string; + userAgent?: string; + os?: string; + arch?: string; + cpus?: number; + ram?: number; + }; + } else { + params = { + email: paramsOrFirst as string, + name: rest[0] as string, + version: rest[1] as string, + domain: rest[2] as string, + database: rest[3] as string, + hostIp: rest[4] as string, + userAgent: rest[5] as string, + os: rest[6] as string, + arch: rest[7] as string, + cpus: rest[8] as number, + ram: rest[9] as number, + }; + } + + const email = params.email; + const name = params.name; + const version = params.version; + const domain = params.domain; + const database = params.database; + const hostIp = params.hostIp; + const userAgent = params.userAgent; + const os = params.os; + const arch = params.arch; + const cpus = params.cpus; + const ram = params.ram; + + const apiPath = '/growth/installations'; + const payload: Payload = {}; + if (typeof email !== 'undefined') { + payload['email'] = email; + } + if (typeof name !== 'undefined') { + payload['name'] = name; + } + if (typeof version !== 'undefined') { + payload['version'] = version; + } + if (typeof domain !== 'undefined') { + payload['domain'] = domain; + } + if (typeof database !== 'undefined') { + payload['database'] = database; + } + if (typeof hostIp !== 'undefined') { + payload['hostIp'] = hostIp; + } + if (typeof userAgent !== 'undefined') { + payload['userAgent'] = userAgent; + } + if (typeof os !== 'undefined') { + payload['os'] = os; + } + if (typeof arch !== 'undefined') { + payload['arch'] = arch; + } + if (typeof cpus !== 'undefined') { + payload['cpus'] = cpus; + } + if (typeof ram !== 'undefined') { + payload['ram'] = ram; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('post', uri, apiHeaders, payload); + } +} diff --git a/src/services/manager.ts b/src/services/manager.ts index bb85bed7..ed58a586 100644 --- a/src/services/manager.ts +++ b/src/services/manager.ts @@ -451,6 +451,97 @@ export class Manager { return this.client.call('delete', uri, apiHeaders, payload); } + /** + * Blocks or unblocks an organization, the same way a billing-limit block does. Blocking makes the organization read-only, disables its backup policies and spins down its dedicated databases. + * + * @param {string} params.teamId - Organization ID. + * @param {boolean} params.status - Organization status. Set to `false` to block and `true` to unblock. + * @param {string} params.reason - Optional reason when blocking, kept in the audit trail. + * @throws {AppwriteException} + * @returns {Promise>} + */ + updateOrganizationStatus< + Preferences extends Models.Preferences = Models.DefaultPreferences, + >(params: { + teamId: string; + status: boolean; + reason?: string; + }): Promise>; + /** + * Blocks or unblocks an organization, the same way a billing-limit block does. Blocking makes the organization read-only, disables its backup policies and spins down its dedicated databases. + * + * @param {string} teamId - Organization ID. + * @param {boolean} status - Organization status. Set to `false` to block and `true` to unblock. + * @param {string} reason - Optional reason when blocking, kept in the audit trail. + * @throws {AppwriteException} + * @returns {Promise>} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updateOrganizationStatus< + Preferences extends Models.Preferences = Models.DefaultPreferences, + >( + teamId: string, + status: boolean, + reason?: string, + ): Promise>; + updateOrganizationStatus< + Preferences extends Models.Preferences = Models.DefaultPreferences, + >( + paramsOrFirst: + { teamId: string; status: boolean; reason?: string } | string, + ...rest: [boolean?, string?] + ): Promise> { + let params: { teamId: string; status: boolean; reason?: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + teamId: string; + status: boolean; + reason?: string; + }; + } else { + params = { + teamId: paramsOrFirst as string, + status: rest[0] as boolean, + reason: rest[1] as string, + }; + } + + const teamId = params.teamId; + const status = params.status; + const reason = params.reason; + + if (typeof teamId === 'undefined') { + throw new AppwriteException('Missing required parameter: "teamId"'); + } + if (typeof status === 'undefined') { + throw new AppwriteException('Missing required parameter: "status"'); + } + const apiPath = '/manager/organizations/status'; + const payload: Payload = {}; + if (typeof teamId !== 'undefined') { + payload['teamId'] = teamId; + } + if (typeof status !== 'undefined') { + payload['status'] = status; + } + if (typeof reason !== 'undefined') { + payload['reason'] = reason; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('patch', uri, apiHeaders, payload); + } + /** * Updates a console user status using a user ID or email address. * diff --git a/src/services/messaging.ts b/src/services/messaging.ts index 73aefb81..664f7220 100644 --- a/src/services/messaging.ts +++ b/src/services/messaging.ts @@ -102,6 +102,8 @@ export class Messaging { * @param {string[]} params.cc - Array of target IDs to be added as CC. * @param {string[]} params.bcc - Array of target IDs to be added as BCC. * @param {string[]} params.attachments - Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as :. + * @param {string} params.replyToEmail - Email address to reply to. If not set, defaults to the sender email address. + * @param {string} params.replyToName - Name of the reply to recipient. If not set, defaults to the sender name. * @param {boolean} params.draft - Is message a draft * @param {boolean} params.html - Is content of type HTML * @param {string} params.scheduledAt - Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future. @@ -118,6 +120,8 @@ export class Messaging { cc?: string[]; bcc?: string[]; attachments?: string[]; + replyToEmail?: string; + replyToName?: string; draft?: boolean; html?: boolean; scheduledAt?: string; @@ -134,6 +138,8 @@ export class Messaging { * @param {string[]} cc - Array of target IDs to be added as CC. * @param {string[]} bcc - Array of target IDs to be added as BCC. * @param {string[]} attachments - Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as :. + * @param {string} replyToEmail - Email address to reply to. If not set, defaults to the sender email address. + * @param {string} replyToName - Name of the reply to recipient. If not set, defaults to the sender name. * @param {boolean} draft - Is message a draft * @param {boolean} html - Is content of type HTML * @param {string} scheduledAt - Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future. @@ -151,6 +157,8 @@ export class Messaging { cc?: string[], bcc?: string[], attachments?: string[], + replyToEmail?: string, + replyToName?: string, draft?: boolean, html?: boolean, scheduledAt?: string, @@ -167,6 +175,8 @@ export class Messaging { cc?: string[]; bcc?: string[]; attachments?: string[]; + replyToEmail?: string; + replyToName?: string; draft?: boolean; html?: boolean; scheduledAt?: string; @@ -181,6 +191,8 @@ export class Messaging { string[]?, string[]?, string[]?, + string?, + string?, boolean?, boolean?, string?, @@ -196,6 +208,8 @@ export class Messaging { cc?: string[]; bcc?: string[]; attachments?: string[]; + replyToEmail?: string; + replyToName?: string; draft?: boolean; html?: boolean; scheduledAt?: string; @@ -216,6 +230,8 @@ export class Messaging { cc?: string[]; bcc?: string[]; attachments?: string[]; + replyToEmail?: string; + replyToName?: string; draft?: boolean; html?: boolean; scheduledAt?: string; @@ -231,9 +247,11 @@ export class Messaging { cc: rest[5] as string[], bcc: rest[6] as string[], attachments: rest[7] as string[], - draft: rest[8] as boolean, - html: rest[9] as boolean, - scheduledAt: rest[10] as string, + replyToEmail: rest[8] as string, + replyToName: rest[9] as string, + draft: rest[10] as boolean, + html: rest[11] as boolean, + scheduledAt: rest[12] as string, }; } @@ -246,6 +264,8 @@ export class Messaging { const cc = params.cc; const bcc = params.bcc; const attachments = params.attachments; + const replyToEmail = params.replyToEmail; + const replyToName = params.replyToName; const draft = params.draft; const html = params.html; const scheduledAt = params.scheduledAt; @@ -294,6 +314,12 @@ export class Messaging { if (typeof attachments !== 'undefined') { payload['attachments'] = attachments; } + if (typeof replyToEmail !== 'undefined') { + payload['replyToEmail'] = replyToEmail; + } + if (typeof replyToName !== 'undefined') { + payload['replyToName'] = replyToName; + } if (typeof draft !== 'undefined') { payload['draft'] = draft; } @@ -328,6 +354,8 @@ export class Messaging { * @param {boolean} params.html - Is content of type HTML * @param {string[]} params.cc - Array of target IDs to be added as CC. * @param {string[]} params.bcc - Array of target IDs to be added as BCC. + * @param {string} params.replyToEmail - Email address to reply to. Pass an empty string to restore the provider or sender default. + * @param {string} params.replyToName - Name of the reply to recipient. Pass an empty string to restore the provider or sender default. * @param {string} params.scheduledAt - Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future. * @param {string[]} params.attachments - Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as :. * @throws {AppwriteException} @@ -344,6 +372,8 @@ export class Messaging { html?: boolean; cc?: string[]; bcc?: string[]; + replyToEmail?: string; + replyToName?: string; scheduledAt?: string; attachments?: string[]; }): Promise; @@ -361,6 +391,8 @@ export class Messaging { * @param {boolean} html - Is content of type HTML * @param {string[]} cc - Array of target IDs to be added as CC. * @param {string[]} bcc - Array of target IDs to be added as BCC. + * @param {string} replyToEmail - Email address to reply to. Pass an empty string to restore the provider or sender default. + * @param {string} replyToName - Name of the reply to recipient. Pass an empty string to restore the provider or sender default. * @param {string} scheduledAt - Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future. * @param {string[]} attachments - Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as :. * @throws {AppwriteException} @@ -378,6 +410,8 @@ export class Messaging { html?: boolean, cc?: string[], bcc?: string[], + replyToEmail?: string, + replyToName?: string, scheduledAt?: string, attachments?: string[], ): Promise; @@ -394,6 +428,8 @@ export class Messaging { html?: boolean; cc?: string[]; bcc?: string[]; + replyToEmail?: string; + replyToName?: string; scheduledAt?: string; attachments?: string[]; } @@ -409,6 +445,8 @@ export class Messaging { string[]?, string[]?, string?, + string?, + string?, string[]?, ] ): Promise { @@ -423,6 +461,8 @@ export class Messaging { html?: boolean; cc?: string[]; bcc?: string[]; + replyToEmail?: string; + replyToName?: string; scheduledAt?: string; attachments?: string[]; }; @@ -443,6 +483,8 @@ export class Messaging { html?: boolean; cc?: string[]; bcc?: string[]; + replyToEmail?: string; + replyToName?: string; scheduledAt?: string; attachments?: string[]; }; @@ -458,8 +500,10 @@ export class Messaging { html: rest[6] as boolean, cc: rest[7] as string[], bcc: rest[8] as string[], - scheduledAt: rest[9] as string, - attachments: rest[10] as string[], + replyToEmail: rest[9] as string, + replyToName: rest[10] as string, + scheduledAt: rest[11] as string, + attachments: rest[12] as string[], }; } @@ -473,6 +517,8 @@ export class Messaging { const html = params.html; const cc = params.cc; const bcc = params.bcc; + const replyToEmail = params.replyToEmail; + const replyToName = params.replyToName; const scheduledAt = params.scheduledAt; const attachments = params.attachments; @@ -513,6 +559,12 @@ export class Messaging { if (typeof bcc !== 'undefined') { payload['bcc'] = bcc; } + if (typeof replyToEmail !== 'undefined') { + payload['replyToEmail'] = replyToEmail; + } + if (typeof replyToName !== 'undefined') { + payload['replyToName'] = replyToName; + } if (typeof scheduledAt !== 'undefined') { payload['scheduledAt'] = scheduledAt; } diff --git a/src/services/organizations.ts b/src/services/organizations.ts index d79e4d9b..17ac4dd7 100644 --- a/src/services/organizations.ts +++ b/src/services/organizations.ts @@ -1836,64 +1836,6 @@ export class Organizations { return this.client.call('get', uri, apiHeaders, payload); } - /** - * Get an estimate of the charge for the organization current billing cycle. - * - * @param {string} params.organizationId - Organization ID - * @throws {AppwriteException} - * @returns {Promise} - */ - getEstimation(params: { - organizationId: string; - }): Promise; - /** - * Get an estimate of the charge for the organization current billing cycle. - * - * @param {string} organizationId - Organization ID - * @throws {AppwriteException} - * @returns {Promise} - * @deprecated Use the object parameter style method for a better developer experience. - */ - getEstimation(organizationId: string): Promise; - getEstimation( - paramsOrFirst: { organizationId: string } | string, - ): Promise { - let params: { organizationId: string }; - - if ( - paramsOrFirst && - typeof paramsOrFirst === 'object' && - !Array.isArray(paramsOrFirst) - ) { - params = (paramsOrFirst || {}) as { organizationId: string }; - } else { - params = { - organizationId: paramsOrFirst as string, - }; - } - - const organizationId = params.organizationId; - - if (typeof organizationId === 'undefined' || organizationId === '') { - throw new AppwriteException( - 'Missing required parameter: "organizationId"', - ); - } - const apiPath = '/organizations/{organizationId}/estimations'.replace( - '{organizationId}', - encodeURIComponent(String(organizationId)), - ); - const payload: Payload = {}; - const uri = new URL(this.client.config.endpoint + apiPath); - - const apiHeaders: { [header: string]: string } = { - 'X-Appwrite-Project': this.client.config.project, - accept: 'application/json', - }; - - return this.client.call('get', uri, apiHeaders, payload); - } - /** * Get estimation for deleting an organization. * diff --git a/src/services/project.ts b/src/services/project.ts index b41100a8..c843de32 100644 --- a/src/services/project.ts +++ b/src/services/project.ts @@ -3,8 +3,16 @@ import type { Models } from '../models'; import { ProjectAuthMethodId } from '../enums/project-auth-method-id'; import { ProjectKeyScopes } from '../enums/project-key-scopes'; +import { ProjectOAuth2Auth0Prompt } from '../enums/project-o-auth-2-auth-0-prompt'; +import { ProjectOAuth2DiscordPrompt } from '../enums/project-o-auth-2-discord-prompt'; +import { ProjectOAuth2GitHubPrompt } from '../enums/project-o-auth-2-git-hub-prompt'; import { ProjectOAuth2GooglePrompt } from '../enums/project-o-auth-2-google-prompt'; +import { ProjectOAuth2KakaoPrompt } from '../enums/project-o-auth-2-kakao-prompt'; +import { ProjectOAuth2MicrosoftPrompt } from '../enums/project-o-auth-2-microsoft-prompt'; import { ProjectOAuth2OidcPrompt } from '../enums/project-o-auth-2-oidc-prompt'; +import { ProjectOAuth2OktaPrompt } from '../enums/project-o-auth-2-okta-prompt'; +import { ProjectOAuth2SalesforcePrompt } from '../enums/project-o-auth-2-salesforce-prompt'; +import { ProjectOAuth2ZohoPrompt } from '../enums/project-o-auth-2-zoho-prompt'; import { ProjectOAuthProviderId } from '../enums/project-o-auth-provider-id'; import { ProjectPolicyId } from '../enums/project-policy-id'; import { ProjectProtocolId } from '../enums/project-protocol-id'; @@ -1646,6 +1654,7 @@ export class Project { * @param {string} params.clientId - 'Client ID' of Auth0 OAuth2 app. For example: OaOkIA000000000000000000005KLSYq * @param {string} params.clientSecret - 'Client Secret' of Auth0 OAuth2 app. For example: zXz0000-00000000000000000000000000000-00000000000000000000PJafnF * @param {string} params.endpoint - Domain of Auth0 instance. For example: example.us.auth0.com + * @param {ProjectOAuth2Auth0Prompt[]} params.prompt - Array of Auth0 OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. Pass an empty array to use the Auth0 default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -1654,6 +1663,7 @@ export class Project { clientId?: string; clientSecret?: string; endpoint?: string; + prompt?: ProjectOAuth2Auth0Prompt[]; enabled?: boolean; }): Promise; /** @@ -1662,6 +1672,7 @@ export class Project { * @param {string} clientId - 'Client ID' of Auth0 OAuth2 app. For example: OaOkIA000000000000000000005KLSYq * @param {string} clientSecret - 'Client Secret' of Auth0 OAuth2 app. For example: zXz0000-00000000000000000000000000000-00000000000000000000PJafnF * @param {string} endpoint - Domain of Auth0 instance. For example: example.us.auth0.com + * @param {ProjectOAuth2Auth0Prompt[]} prompt - Array of Auth0 OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. Pass an empty array to use the Auth0 default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -1671,6 +1682,7 @@ export class Project { clientId?: string, clientSecret?: string, endpoint?: string, + prompt?: ProjectOAuth2Auth0Prompt[], enabled?: boolean, ): Promise; updateOAuth2Auth0( @@ -1679,15 +1691,17 @@ export class Project { clientId?: string; clientSecret?: string; endpoint?: string; + prompt?: ProjectOAuth2Auth0Prompt[]; enabled?: boolean; } | string, - ...rest: [string?, string?, boolean?] + ...rest: [string?, string?, ProjectOAuth2Auth0Prompt[]?, boolean?] ): Promise { let params: { clientId?: string; clientSecret?: string; endpoint?: string; + prompt?: ProjectOAuth2Auth0Prompt[]; enabled?: boolean; }; @@ -1701,6 +1715,7 @@ export class Project { clientId?: string; clientSecret?: string; endpoint?: string; + prompt?: ProjectOAuth2Auth0Prompt[]; enabled?: boolean; }; } else { @@ -1708,13 +1723,15 @@ export class Project { clientId: paramsOrFirst as string, clientSecret: rest[0] as string, endpoint: rest[1] as string, - enabled: rest[2] as boolean, + prompt: rest[2] as ProjectOAuth2Auth0Prompt[], + enabled: rest[3] as boolean, }; } const clientId = params.clientId; const clientSecret = params.clientSecret; const endpoint = params.endpoint; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/auth0'; @@ -1728,6 +1745,9 @@ export class Project { if (typeof endpoint !== 'undefined') { payload['endpoint'] = endpoint; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -2355,6 +2375,7 @@ export class Project { * * @param {string} params.clientId - 'Client ID' of Discord OAuth2 app. For example: 950722000000343754 * @param {string} params.clientSecret - 'Client Secret' of Discord OAuth2 app. For example: YmPXnM000000000000000000002zFg5D + * @param {ProjectOAuth2DiscordPrompt[]} params.prompt - Array with at most one Discord OAuth2 prompt value. "none" means: skip the authorization screen for users who already authorized the app with the requested scopes. "consent" means: ask users who already authorized the app to approve it again. Pass an empty array to use the Discord default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -2362,6 +2383,7 @@ export class Project { updateOAuth2Discord(params?: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2DiscordPrompt[]; enabled?: boolean; }): Promise; /** @@ -2369,6 +2391,7 @@ export class Project { * * @param {string} clientId - 'Client ID' of Discord OAuth2 app. For example: 950722000000343754 * @param {string} clientSecret - 'Client Secret' of Discord OAuth2 app. For example: YmPXnM000000000000000000002zFg5D + * @param {ProjectOAuth2DiscordPrompt[]} prompt - Array with at most one Discord OAuth2 prompt value. "none" means: skip the authorization screen for users who already authorized the app with the requested scopes. "consent" means: ask users who already authorized the app to approve it again. Pass an empty array to use the Discord default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -2377,17 +2400,24 @@ export class Project { updateOAuth2Discord( clientId?: string, clientSecret?: string, + prompt?: ProjectOAuth2DiscordPrompt[], enabled?: boolean, ): Promise; updateOAuth2Discord( paramsOrFirst?: - | { clientId?: string; clientSecret?: string; enabled?: boolean } + | { + clientId?: string; + clientSecret?: string; + prompt?: ProjectOAuth2DiscordPrompt[]; + enabled?: boolean; + } | string, - ...rest: [string?, boolean?] + ...rest: [string?, ProjectOAuth2DiscordPrompt[]?, boolean?] ): Promise { let params: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2DiscordPrompt[]; enabled?: boolean; }; @@ -2400,18 +2430,21 @@ export class Project { params = (paramsOrFirst || {}) as { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2DiscordPrompt[]; enabled?: boolean; }; } else { params = { clientId: paramsOrFirst as string, clientSecret: rest[0] as string, - enabled: rest[1] as boolean, + prompt: rest[1] as ProjectOAuth2DiscordPrompt[], + enabled: rest[2] as boolean, }; } const clientId = params.clientId; const clientSecret = params.clientSecret; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/discord'; @@ -2422,6 +2455,9 @@ export class Project { if (typeof clientSecret !== 'undefined') { payload['clientSecret'] = clientSecret; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -2963,6 +2999,7 @@ export class Project { * * @param {string} params.clientId - 'OAuth2 app Client ID, or App ID' of GitHub OAuth2 app. For example: e4d87900000000540733. Example of wrong value: 370006 * @param {string} params.clientSecret - 'Client Secret' of GitHub OAuth2 app. For example: 5e07c00000000000000000000000000000198bcc + * @param {ProjectOAuth2GitHubPrompt[]} params.prompt - Array of GitHub OAuth2 prompt values. "select_account" means: prompt the user to select an account. Pass an empty array to use the GitHub default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -2970,6 +3007,7 @@ export class Project { updateOAuth2GitHub(params?: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2GitHubPrompt[]; enabled?: boolean; }): Promise; /** @@ -2977,6 +3015,7 @@ export class Project { * * @param {string} clientId - 'OAuth2 app Client ID, or App ID' of GitHub OAuth2 app. For example: e4d87900000000540733. Example of wrong value: 370006 * @param {string} clientSecret - 'Client Secret' of GitHub OAuth2 app. For example: 5e07c00000000000000000000000000000198bcc + * @param {ProjectOAuth2GitHubPrompt[]} prompt - Array of GitHub OAuth2 prompt values. "select_account" means: prompt the user to select an account. Pass an empty array to use the GitHub default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -2985,17 +3024,24 @@ export class Project { updateOAuth2GitHub( clientId?: string, clientSecret?: string, + prompt?: ProjectOAuth2GitHubPrompt[], enabled?: boolean, ): Promise; updateOAuth2GitHub( paramsOrFirst?: - | { clientId?: string; clientSecret?: string; enabled?: boolean } + | { + clientId?: string; + clientSecret?: string; + prompt?: ProjectOAuth2GitHubPrompt[]; + enabled?: boolean; + } | string, - ...rest: [string?, boolean?] + ...rest: [string?, ProjectOAuth2GitHubPrompt[]?, boolean?] ): Promise { let params: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2GitHubPrompt[]; enabled?: boolean; }; @@ -3008,18 +3054,21 @@ export class Project { params = (paramsOrFirst || {}) as { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2GitHubPrompt[]; enabled?: boolean; }; } else { params = { clientId: paramsOrFirst as string, clientSecret: rest[0] as string, - enabled: rest[1] as boolean, + prompt: rest[1] as ProjectOAuth2GitHubPrompt[], + enabled: rest[2] as boolean, }; } const clientId = params.clientId; const clientSecret = params.clientSecret; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/github'; @@ -3030,6 +3079,9 @@ export class Project { if (typeof clientSecret !== 'undefined') { payload['clientSecret'] = clientSecret; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -3369,6 +3421,7 @@ export class Project { * * @param {string} params.clientId - 'REST API key' of Kakao OAuth2 app. For example: 839ff5000000000000000000013206de * @param {string} params.clientSecret - 'Client Secret' of Kakao OAuth2 app. For example: jLNVOK00000000000000000000yJebea. Generate it under Kakao Login > Security and set its status to enabled + * @param {ProjectOAuth2KakaoPrompt[]} params.prompt - Array of Kakao OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "create" means: prompt the user to sign up. "select_account" means: prompt the user to select an account. Pass an empty array to use the Kakao default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -3376,6 +3429,7 @@ export class Project { updateOAuth2Kakao(params?: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2KakaoPrompt[]; enabled?: boolean; }): Promise; /** @@ -3383,6 +3437,7 @@ export class Project { * * @param {string} clientId - 'REST API key' of Kakao OAuth2 app. For example: 839ff5000000000000000000013206de * @param {string} clientSecret - 'Client Secret' of Kakao OAuth2 app. For example: jLNVOK00000000000000000000yJebea. Generate it under Kakao Login > Security and set its status to enabled + * @param {ProjectOAuth2KakaoPrompt[]} prompt - Array of Kakao OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "create" means: prompt the user to sign up. "select_account" means: prompt the user to select an account. Pass an empty array to use the Kakao default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -3391,17 +3446,24 @@ export class Project { updateOAuth2Kakao( clientId?: string, clientSecret?: string, + prompt?: ProjectOAuth2KakaoPrompt[], enabled?: boolean, ): Promise; updateOAuth2Kakao( paramsOrFirst?: - | { clientId?: string; clientSecret?: string; enabled?: boolean } + | { + clientId?: string; + clientSecret?: string; + prompt?: ProjectOAuth2KakaoPrompt[]; + enabled?: boolean; + } | string, - ...rest: [string?, boolean?] + ...rest: [string?, ProjectOAuth2KakaoPrompt[]?, boolean?] ): Promise { let params: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2KakaoPrompt[]; enabled?: boolean; }; @@ -3414,18 +3476,21 @@ export class Project { params = (paramsOrFirst || {}) as { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2KakaoPrompt[]; enabled?: boolean; }; } else { params = { clientId: paramsOrFirst as string, clientSecret: rest[0] as string, - enabled: rest[1] as boolean, + prompt: rest[1] as ProjectOAuth2KakaoPrompt[], + enabled: rest[2] as boolean, }; } const clientId = params.clientId; const clientSecret = params.clientSecret; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/kakao'; @@ -3436,6 +3501,9 @@ export class Project { if (typeof clientSecret !== 'undefined') { payload['clientSecret'] = clientSecret; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -3746,6 +3814,7 @@ export class Project { * @param {string} params.applicationId - 'Entra ID Application ID, also known as Client ID' of Microsoft OAuth2 app. For example: 00001111-aaaa-2222-bbbb-3333cccc4444 * @param {string} params.applicationSecret - 'Entra ID Application Secret, also known as Client Secret' of Microsoft OAuth2 app. For example: A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u * @param {string} params.tenant - Microsoft Entra ID tenant identifier. Use 'common', 'organizations', 'consumers' or a specific tenant ID. For example: common + * @param {ProjectOAuth2MicrosoftPrompt[]} params.prompt - Array of Microsoft OAuth2 prompt values. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. "select_account" means: prompt the user to select an account. Pass an empty array to use the Microsoft default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -3754,6 +3823,7 @@ export class Project { applicationId?: string; applicationSecret?: string; tenant?: string; + prompt?: ProjectOAuth2MicrosoftPrompt[]; enabled?: boolean; }): Promise; /** @@ -3762,6 +3832,7 @@ export class Project { * @param {string} applicationId - 'Entra ID Application ID, also known as Client ID' of Microsoft OAuth2 app. For example: 00001111-aaaa-2222-bbbb-3333cccc4444 * @param {string} applicationSecret - 'Entra ID Application Secret, also known as Client Secret' of Microsoft OAuth2 app. For example: A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u * @param {string} tenant - Microsoft Entra ID tenant identifier. Use 'common', 'organizations', 'consumers' or a specific tenant ID. For example: common + * @param {ProjectOAuth2MicrosoftPrompt[]} prompt - Array of Microsoft OAuth2 prompt values. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. "select_account" means: prompt the user to select an account. Pass an empty array to use the Microsoft default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -3771,6 +3842,7 @@ export class Project { applicationId?: string, applicationSecret?: string, tenant?: string, + prompt?: ProjectOAuth2MicrosoftPrompt[], enabled?: boolean, ): Promise; updateOAuth2Microsoft( @@ -3779,15 +3851,17 @@ export class Project { applicationId?: string; applicationSecret?: string; tenant?: string; + prompt?: ProjectOAuth2MicrosoftPrompt[]; enabled?: boolean; } | string, - ...rest: [string?, string?, boolean?] + ...rest: [string?, string?, ProjectOAuth2MicrosoftPrompt[]?, boolean?] ): Promise { let params: { applicationId?: string; applicationSecret?: string; tenant?: string; + prompt?: ProjectOAuth2MicrosoftPrompt[]; enabled?: boolean; }; @@ -3801,6 +3875,7 @@ export class Project { applicationId?: string; applicationSecret?: string; tenant?: string; + prompt?: ProjectOAuth2MicrosoftPrompt[]; enabled?: boolean; }; } else { @@ -3808,13 +3883,15 @@ export class Project { applicationId: paramsOrFirst as string, applicationSecret: rest[0] as string, tenant: rest[1] as string, - enabled: rest[2] as boolean, + prompt: rest[2] as ProjectOAuth2MicrosoftPrompt[], + enabled: rest[3] as boolean, }; } const applicationId = params.applicationId; const applicationSecret = params.applicationSecret; const tenant = params.tenant; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/microsoft'; @@ -3828,6 +3905,9 @@ export class Project { if (typeof tenant !== 'undefined') { payload['tenant'] = tenant; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -4110,6 +4190,7 @@ export class Project { * @param {string} params.clientSecret - 'Client Secret' of Okta OAuth2 app. For example: Kiq0000000000000000000000000000000000000-00000000000H2L5-3SJ-vRV * @param {string} params.domain - Okta company domain. Required when enabling the provider. For example: trial-6400025.okta.com. Example of wrong value: trial-6400025-admin.okta.com, or https://trial-6400025.okta.com/ * @param {string} params.authorizationServerId - Custom Authorization Servers. Optional, can be left empty or unconfigured. For example: aus000000000000000h7z + * @param {ProjectOAuth2OktaPrompt[]} params.prompt - Array of Okta OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. Pass an empty array to use the Okta default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -4119,6 +4200,7 @@ export class Project { clientSecret?: string; domain?: string; authorizationServerId?: string; + prompt?: ProjectOAuth2OktaPrompt[]; enabled?: boolean; }): Promise; /** @@ -4128,6 +4210,7 @@ export class Project { * @param {string} clientSecret - 'Client Secret' of Okta OAuth2 app. For example: Kiq0000000000000000000000000000000000000-00000000000H2L5-3SJ-vRV * @param {string} domain - Okta company domain. Required when enabling the provider. For example: trial-6400025.okta.com. Example of wrong value: trial-6400025-admin.okta.com, or https://trial-6400025.okta.com/ * @param {string} authorizationServerId - Custom Authorization Servers. Optional, can be left empty or unconfigured. For example: aus000000000000000h7z + * @param {ProjectOAuth2OktaPrompt[]} prompt - Array of Okta OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. Pass an empty array to use the Okta default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -4138,6 +4221,7 @@ export class Project { clientSecret?: string, domain?: string, authorizationServerId?: string, + prompt?: ProjectOAuth2OktaPrompt[], enabled?: boolean, ): Promise; updateOAuth2Okta( @@ -4147,16 +4231,24 @@ export class Project { clientSecret?: string; domain?: string; authorizationServerId?: string; + prompt?: ProjectOAuth2OktaPrompt[]; enabled?: boolean; } | string, - ...rest: [string?, string?, string?, boolean?] + ...rest: [ + string?, + string?, + string?, + ProjectOAuth2OktaPrompt[]?, + boolean?, + ] ): Promise { let params: { clientId?: string; clientSecret?: string; domain?: string; authorizationServerId?: string; + prompt?: ProjectOAuth2OktaPrompt[]; enabled?: boolean; }; @@ -4171,6 +4263,7 @@ export class Project { clientSecret?: string; domain?: string; authorizationServerId?: string; + prompt?: ProjectOAuth2OktaPrompt[]; enabled?: boolean; }; } else { @@ -4179,7 +4272,8 @@ export class Project { clientSecret: rest[0] as string, domain: rest[1] as string, authorizationServerId: rest[2] as string, - enabled: rest[3] as boolean, + prompt: rest[3] as ProjectOAuth2OktaPrompt[], + enabled: rest[4] as boolean, }; } @@ -4187,6 +4281,7 @@ export class Project { const clientSecret = params.clientSecret; const domain = params.domain; const authorizationServerId = params.authorizationServerId; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/okta'; @@ -4203,6 +4298,9 @@ export class Project { if (typeof authorizationServerId !== 'undefined') { payload['authorizationServerId'] = authorizationServerId; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -4566,6 +4664,7 @@ export class Project { * * @param {string} params.customerKey - 'Consumer Key' of Salesforce OAuth2 app. For example: 3MVG9I0000000000000000000000000000000000000000000000000000000000000000000000000C5Aejq * @param {string} params.customerSecret - 'Consumer Secret' of Salesforce OAuth2 app. For example: 3w000000000000e2 + * @param {ProjectOAuth2SalesforcePrompt[]} params.prompt - Array of Salesforce OAuth2 prompt values. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. Pass an empty array to use the Salesforce default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -4573,6 +4672,7 @@ export class Project { updateOAuth2Salesforce(params?: { customerKey?: string; customerSecret?: string; + prompt?: ProjectOAuth2SalesforcePrompt[]; enabled?: boolean; }): Promise; /** @@ -4580,6 +4680,7 @@ export class Project { * * @param {string} customerKey - 'Consumer Key' of Salesforce OAuth2 app. For example: 3MVG9I0000000000000000000000000000000000000000000000000000000000000000000000000C5Aejq * @param {string} customerSecret - 'Consumer Secret' of Salesforce OAuth2 app. For example: 3w000000000000e2 + * @param {ProjectOAuth2SalesforcePrompt[]} prompt - Array of Salesforce OAuth2 prompt values. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. Pass an empty array to use the Salesforce default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -4588,6 +4689,7 @@ export class Project { updateOAuth2Salesforce( customerKey?: string, customerSecret?: string, + prompt?: ProjectOAuth2SalesforcePrompt[], enabled?: boolean, ): Promise; updateOAuth2Salesforce( @@ -4595,14 +4697,16 @@ export class Project { | { customerKey?: string; customerSecret?: string; + prompt?: ProjectOAuth2SalesforcePrompt[]; enabled?: boolean; } | string, - ...rest: [string?, boolean?] + ...rest: [string?, ProjectOAuth2SalesforcePrompt[]?, boolean?] ): Promise { let params: { customerKey?: string; customerSecret?: string; + prompt?: ProjectOAuth2SalesforcePrompt[]; enabled?: boolean; }; @@ -4615,18 +4719,21 @@ export class Project { params = (paramsOrFirst || {}) as { customerKey?: string; customerSecret?: string; + prompt?: ProjectOAuth2SalesforcePrompt[]; enabled?: boolean; }; } else { params = { customerKey: paramsOrFirst as string, customerSecret: rest[0] as string, - enabled: rest[1] as boolean, + prompt: rest[1] as ProjectOAuth2SalesforcePrompt[], + enabled: rest[2] as boolean, }; } const customerKey = params.customerKey; const customerSecret = params.customerSecret; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/salesforce'; @@ -4637,6 +4744,9 @@ export class Project { if (typeof customerSecret !== 'undefined') { payload['customerSecret'] = customerSecret; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -5610,6 +5720,7 @@ export class Project { * * @param {string} params.clientId - 'Client ID' of Zoho OAuth2 app. For example: 1000.83C178000000000000000000RPNX0B * @param {string} params.clientSecret - 'Client Secret' of Zoho OAuth2 app. For example: fb5cac000000000000000000000000000000a68f6e + * @param {ProjectOAuth2ZohoPrompt[]} params.prompt - Array of Zoho OAuth2 prompt values. "consent" means: prompt the user for consent. Pass an empty array to use the Zoho default. * @param {boolean} params.enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -5617,6 +5728,7 @@ export class Project { updateOAuth2Zoho(params?: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2ZohoPrompt[]; enabled?: boolean; }): Promise; /** @@ -5624,6 +5736,7 @@ export class Project { * * @param {string} clientId - 'Client ID' of Zoho OAuth2 app. For example: 1000.83C178000000000000000000RPNX0B * @param {string} clientSecret - 'Client Secret' of Zoho OAuth2 app. For example: fb5cac000000000000000000000000000000a68f6e + * @param {ProjectOAuth2ZohoPrompt[]} prompt - Array of Zoho OAuth2 prompt values. "consent" means: prompt the user for consent. Pass an empty array to use the Zoho default. * @param {boolean} enabled - OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. * @throws {AppwriteException} * @returns {Promise} @@ -5632,17 +5745,24 @@ export class Project { updateOAuth2Zoho( clientId?: string, clientSecret?: string, + prompt?: ProjectOAuth2ZohoPrompt[], enabled?: boolean, ): Promise; updateOAuth2Zoho( paramsOrFirst?: - | { clientId?: string; clientSecret?: string; enabled?: boolean } + | { + clientId?: string; + clientSecret?: string; + prompt?: ProjectOAuth2ZohoPrompt[]; + enabled?: boolean; + } | string, - ...rest: [string?, boolean?] + ...rest: [string?, ProjectOAuth2ZohoPrompt[]?, boolean?] ): Promise { let params: { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2ZohoPrompt[]; enabled?: boolean; }; @@ -5655,18 +5775,21 @@ export class Project { params = (paramsOrFirst || {}) as { clientId?: string; clientSecret?: string; + prompt?: ProjectOAuth2ZohoPrompt[]; enabled?: boolean; }; } else { params = { clientId: paramsOrFirst as string, clientSecret: rest[0] as string, - enabled: rest[1] as boolean, + prompt: rest[1] as ProjectOAuth2ZohoPrompt[], + enabled: rest[2] as boolean, }; } const clientId = params.clientId; const clientSecret = params.clientSecret; + const prompt = params.prompt; const enabled = params.enabled; const apiPath = '/project/oauth2/zoho'; @@ -5677,6 +5800,9 @@ export class Project { if (typeof clientSecret !== 'undefined') { payload['clientSecret'] = clientSecret; } + if (typeof prompt !== 'undefined') { + payload['prompt'] = prompt; + } if (typeof enabled !== 'undefined') { payload['enabled'] = enabled; } @@ -7856,11 +7982,11 @@ export class Project { } /** - * Updating this policy allows you to control if passwords are checked against the Have I Been Pwned breach database. When enabled, a password that appears in any known breach cannot be set or changed. Enable `sessions` to check passwords on sign-in too. Only the first five characters of the password hash are ever shared with the service. Options left out keep their current value. + * Updating this policy allows you to control if passwords are checked against the Have I Been Pwned breach database. When enabled, every password a user signs up, signs in or resets with is checked and the result is recorded on the user as `passwordPwned`. On its own the policy only records. Enable `users` to reject a breached password when a user signs up or sets a new password, and `sessions` to refuse a sign-in with a breached password until it is reset. Only the first five characters of the password hash are ever shared with the service. Options left out keep their current value. * - * @param {boolean} params.enabled - Toggle password pwned policy. Set to true to block passwords exposed in known data breaches, or false to allow them. Default is true. When changing this policy, existing passwords remain valid. - * @param {boolean} params.sessions - Whether passwords are checked when a session is created. When enabled, signing in records whether the password appears in a known data breach. Default is false. - * @param {boolean} params.users - Whether users signing in with a breached password are blocked until they reset it. Only applies when sessions are checked. Default is false, which allows the sign-in. + * @param {boolean} params.enabled - Toggle password pwned policy. Set to true to check passwords against known data breaches and record the result on the user, or false to never check. Default is true. On its own this only records; use `users` and `sessions` to block. When changing this policy, existing passwords remain valid. + * @param {boolean} params.sessions - Whether a sign-in with a breached password is refused until the password is reset. Default is false, which allows the sign-in and only records the result. + * @param {boolean} params.users - Whether a breached password is rejected when a user signs up or sets a new password. Default is false, which allows the password and only records the result. * @throws {AppwriteException} * @returns {Promise} */ @@ -7870,11 +7996,11 @@ export class Project { users?: boolean; }): Promise; /** - * Updating this policy allows you to control if passwords are checked against the Have I Been Pwned breach database. When enabled, a password that appears in any known breach cannot be set or changed. Enable `sessions` to check passwords on sign-in too. Only the first five characters of the password hash are ever shared with the service. Options left out keep their current value. + * Updating this policy allows you to control if passwords are checked against the Have I Been Pwned breach database. When enabled, every password a user signs up, signs in or resets with is checked and the result is recorded on the user as `passwordPwned`. On its own the policy only records. Enable `users` to reject a breached password when a user signs up or sets a new password, and `sessions` to refuse a sign-in with a breached password until it is reset. Only the first five characters of the password hash are ever shared with the service. Options left out keep their current value. * - * @param {boolean} enabled - Toggle password pwned policy. Set to true to block passwords exposed in known data breaches, or false to allow them. Default is true. When changing this policy, existing passwords remain valid. - * @param {boolean} sessions - Whether passwords are checked when a session is created. When enabled, signing in records whether the password appears in a known data breach. Default is false. - * @param {boolean} users - Whether users signing in with a breached password are blocked until they reset it. Only applies when sessions are checked. Default is false, which allows the sign-in. + * @param {boolean} enabled - Toggle password pwned policy. Set to true to check passwords against known data breaches and record the result on the user, or false to never check. Default is true. On its own this only records; use `users` and `sessions` to block. When changing this policy, existing passwords remain valid. + * @param {boolean} sessions - Whether a sign-in with a breached password is refused until the password is reset. Default is false, which allows the sign-in and only records the result. + * @param {boolean} users - Whether a breached password is rejected when a user signs up or sets a new password. Default is false, which allows the password and only records the result. * @throws {AppwriteException} * @returns {Promise} * @deprecated Use the object parameter style method for a better developer experience. @@ -8927,7 +9053,7 @@ export class Project { /** * Update a custom email template for the specified locale and type. Use this endpoint to modify the content of your email templates. * - * @param {ProjectEmailTemplateId} params.templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification + * @param {ProjectEmailTemplateId} params.templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification, otpRecovery * @param {ProjectEmailTemplateLocale} params.locale - Custom email template locale. If left empty, the fallback locale (en) will be used. * @param {string} params.subject - Subject of the email template. Can be up to 255 characters. * @param {string} params.message - Plain or HTML body of the email template message. Can be up to 10MB of content. @@ -8951,7 +9077,7 @@ export class Project { /** * Update a custom email template for the specified locale and type. Use this endpoint to modify the content of your email templates. * - * @param {ProjectEmailTemplateId} templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification + * @param {ProjectEmailTemplateId} templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification, otpRecovery * @param {ProjectEmailTemplateLocale} locale - Custom email template locale. If left empty, the fallback locale (en) will be used. * @param {string} subject - Subject of the email template. Can be up to 255 characters. * @param {string} message - Plain or HTML body of the email template message. Can be up to 10MB of content. @@ -9097,7 +9223,7 @@ export class Project { /** * Get a custom email template for the specified locale and type. This endpoint returns the template content, subject, and other configuration details. * - * @param {ProjectEmailTemplateId} params.templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification + * @param {ProjectEmailTemplateId} params.templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification, otpRecovery * @param {ProjectEmailTemplateLocale} params.locale - Custom email template locale. If left empty, the fallback locale (en) will be used. * @throws {AppwriteException} * @returns {Promise} @@ -9109,7 +9235,7 @@ export class Project { /** * Get a custom email template for the specified locale and type. This endpoint returns the template content, subject, and other configuration details. * - * @param {ProjectEmailTemplateId} templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification + * @param {ProjectEmailTemplateId} templateId - Custom email template type. Can be one of: verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, otpSession, otpVerification, otpRecovery * @param {ProjectEmailTemplateLocale} locale - Custom email template locale. If left empty, the fallback locale (en) will be used. * @throws {AppwriteException} * @returns {Promise} diff --git a/src/services/push.ts b/src/services/push.ts new file mode 100644 index 00000000..2d197c90 --- /dev/null +++ b/src/services/push.ts @@ -0,0 +1,754 @@ +import { Buffer } from 'buffer'; +import mqtt, { + type IClientOptions, + type IConnackPacket, + type IPublishPacket, + type MqttClient, +} from 'mqtt'; + +import { Client } from '../client'; +import { Service } from '../service'; +import type { ResolvedTopic, Topic } from '../topic'; + +/** A message delivered on a subscribed topic (the MQTT analog of a Realtime event). */ +export interface PushMessage { + topic: string; + /** The payload decoded as UTF-8 text, as it was sent. */ + data: string; + /** The raw payload, for binary messages. */ + payload: Uint8Array; + qos: number; +} + +export type MessageCallback = (message: PushMessage) => void | Promise; + +/** Per-subscription options passed to `subscribe` and `PushSubscription.update`. */ +export interface SubscribeOptions { + /** + * Also surface each matching message as a browser notification while the tab is + * open. Defaults to false. + */ + background?: boolean; + /** Notification title for this subscription. Defaults to the message topic. */ + title?: string; + /** + * Retry delivery of messages missed while disconnected. `true` (the default) subscribes + * at QoS 1 so the broker holds this topic's messages and redelivers them on reconnect; + * `false` uses QoS 0 (at-most-once, may be lost). Per-subscription — the connection + * always keeps its session (clean start off). + */ + retry?: boolean; +} + +/** + * Handle for a live subscription. `unsubscribe()` drops it (and closes the connection + * once the last one is gone); `update()` toggles this subscription's per-message options + * without resubscribing. + */ +export interface PushSubscription { + unsubscribe(): void; + update(options: SubscribeOptions): void; +} + +type AuthMethod = 'appwrite-jwt' | 'appwrite-session' | ''; + +// Fixed connection tuning — not exposed as options. +const KEEP_ALIVE_SECONDS = 60; +const RECONNECT_PERIOD_MS = 2000; + +/** + * Appwrite native push service — the Realtime analog over an MQTT broker, without + * FCM or APNS. In the browser the transport is MQTT 5 over WebSocket: while the page is + * open the connection stays up and each message is handed to your callback, and a + * subscription may also opt into a browser notification per message. + * + * The connection has no tunables — it always keeps its session (clean start off) so the + * broker can redeliver missed messages, and reliability is chosen per subscription via + * `retry`. `subscribe` opens the connection lazily and returns a + * {@link PushSubscription} handle (unlike Realtime's bare unsubscribe callable — Push + * carries per-subscription state). The credential is read off the client — set a JWT or + * session on it (`Client.setJWT` / `Client.setSession`), the same way every other service + * reads auth. + * + * const client = new Client().setEndpoint(...).setProject(...).setJWT(jwt); + * const push = new Push(client); + * const sub = await push.subscribe('user/123/#', (m) => + * console.log(m.topic, m.data), + * ); + * // sub.update({ background: true }); sub.unsubscribe(); + */ +export class Push extends Service { + private mqtt: MqttClient | null = null; + private connecting: Promise | null = null; + /** Set once the first CONNACK lands, so 'connect' events after are reconnects. */ + private everConnected = false; + + private onOpenCb?: () => void; + private onCloseCb?: () => void; + private onErrorCb?: (error: Error) => void; + /** Errors already handed to onError, so one failure is reported once. */ + private reported = new WeakSet(); + /** mqtt.js connect errors mapped to the error handed out for them (see connectError). */ + private connectErrors = new WeakMap(); + /** True while the first connect is in flight: its errors go to the waiting caller. */ + private opening = false; + + // Local id -> subscription state. The id is never sent to the broker; it only lets + // the same topic carry more than one callback (each with its own options). + /** + * The client id when the credential names no user (and none was set): random, but kept for + * this instance so reconnects resume the same broker session. + */ + private readonly fallbackClientId = randomId(); + + private readonly subscriptions = new Map< + string, + { + topic: string; + callback: MessageCallback; + background: boolean; + title?: string; + qos: 0 | 1; + } + >(); + + constructor(client: Client) { + super(client); + } + + // The broker subscription for a filter uses the highest QoS any local subscription on + // it wants, so a QoS-0 subscription never downgrades a QoS-1 one sharing the filter. + private effectiveQos(filter: string): 0 | 1 { + for (const sub of this.subscriptions.values()) { + if (sub.topic === filter && sub.qos === 1) { + return 1; + } + } + return 0; + } + + /** Register a callback invoked when the connection opens (CONNACK success). */ + onOpen(callback: () => void): this { + this.onOpenCb = callback; + return this; + } + + /** Register a callback invoked when the connection closes. */ + onClose(callback: () => void): this { + this.onCloseCb = callback; + return this; + } + + /** Register a callback invoked on a connection error. */ + onError(callback: (error: Error) => void): this { + this.onErrorCb = callback; + return this; + } + + /** + * The signed-in user's own topic, `users/`, for a topic-less subscribe. The id comes + * from the credential the connection authenticates with: the JWT when one is set (never + * the session, which could belong to a different user), else the session. + */ + private userTopic(): string { + const { jwt, session } = this.client.config; + const userId = jwt ? userIdFromJwt(jwt) : userIdFromSession(session); + if (!userId) { + throw new Error( + 'subscribe() without a topic needs a signed-in user: set a JWT or session on the client', + ); + } + return `users/${userId}`; + } + + /** + * Hand an error to the onError callback, once per error. These errors happen outside any + * call (reconnects, broker DISCONNECTs, background re-subscribes), so they are never + * thrown, which would crash the app from inside mqtt.js: with no callback registered + * they are logged, like Realtime does. + */ + private report(error: unknown): void { + const err = toError(error); + if (this.reported.has(err)) { + return; + } + this.reported.add(err); + if (!this.onErrorCb) { + console.error('Appwrite Push error:', err); + return; + } + try { + this.onErrorCb(err); + } catch (callbackError) { + console.error('Appwrite Push onError threw:', callbackError); + } + } + + /** + * For a call that fails to its caller: report the error to onError when a callback is + * registered, then throw it to the caller, who can catch it. + */ + private fail(error: unknown): never { + const err = toError(error); + if (this.onErrorCb) { + this.report(err); + } + throw err; + } + + // --- realtime-like API --- + + /** + * Subscribe to one or more topics. Resolves to a {@link PushSubscription} handle. + * + * Each topic is a string or a `Topic` builder, e.g. + * `Topic.path(['user', userId]).all()`. + * + * Called with only a callback, `subscribe((message) => {...}, options)` subscribes to the + * signed-in user's own topic, `users/`, like FCM/APNs delivery to a device. The + * user id is read from the JWT (or session) set on the client. This form defaults to + * `background: true` and `retry: true` (QoS 1); pass either in the options to override. + * + * Awaiting resolves only once the broker has acked every SUBSCRIBE (SUBACK): + * the broker only starts routing a topic once its subscription is registered, + * so publishing before the ack races the message ahead of the subscription and + * it is dropped (no retained replay on MQTT). Awaiting here makes + * subscribe-then-publish reliable. + * + * Pass `{ background: true, title }` to also show a browser notification per matching + * message while the tab is open, and `{ retry: false }` for at-most-once + * (QoS 0) delivery; toggle either later with the handle's `update`. + */ + subscribe( + callback: MessageCallback, + options?: SubscribeOptions, + ): Promise; + subscribe( + topics: + string | Topic | ResolvedTopic | (string | Topic | ResolvedTopic)[], + callback: MessageCallback, + options?: SubscribeOptions, + ): Promise; + async subscribe( + topicsOrCallback: + | string + | Topic + | ResolvedTopic + | (string | Topic | ResolvedTopic)[] + | MessageCallback, + callbackOrOptions?: MessageCallback | SubscribeOptions, + topicOptions: SubscribeOptions = {}, + ): Promise { + // Every failure reaches onError (when registered) and is thrown to the caller. + try { + return await this.subscribeWith( + topicsOrCallback, + callbackOrOptions, + topicOptions, + ); + } catch (err) { + this.fail(err); + } + } + + private async subscribeWith( + topicsOrCallback: + | string + | Topic + | ResolvedTopic + | (string | Topic | ResolvedTopic)[] + | MessageCallback, + callbackOrOptions: MessageCallback | SubscribeOptions | undefined, + topicOptions: SubscribeOptions, + ): Promise { + // Topic-less form: subscribe(callback, options) targets the signed-in user's topic. + const userForm = typeof topicsOrCallback === 'function'; + const topics = userForm ? this.userTopic() : topicsOrCallback; + const callback = ( + userForm ? topicsOrCallback : callbackOrOptions + ) as MessageCallback; + const options = ( + userForm ? (callbackOrOptions ?? {}) : topicOptions + ) as SubscribeOptions; + const topicList = (Array.isArray(topics) ? topics : [topics]).map( + (topic) => topic.toString(), + ); + // The user topic defaults to background delivery, like FCM/APNs; topics default off. + const background = options.background ?? userForm; + const qos: 0 | 1 = (options.retry ?? true) ? 1 : 0; + + if (background) { + this.requestNotificationPermission(); + } + + const client = await this.connect(); + + const ids: string[] = []; + const acks: Promise[] = []; + for (const topic of topicList) { + const id = randomId(); + ids.push(id); + this.subscriptions.set(id, { + topic, + callback, + background, + title: options.title, + qos, + }); + acks.push( + new Promise((resolve, reject) => { + client.subscribe( + topic, + { qos: this.effectiveQos(topic) }, + (err) => (err ? reject(err) : resolve()), + ); + }), + ); + } + + const unsubscribe = () => { + // Filters whose removed sub was QoS 1: their effective QoS may now drop. + const maybeDowngrade = new Set(); + for (const id of ids) { + const entry = this.subscriptions.get(id); + this.subscriptions.delete(id); + if (entry?.qos === 1) { + maybeDowngrade.add(entry.topic); + } + // Only unsubscribe the broker filter once its last local callback is gone. + const stillUsed = [...this.subscriptions.values()].some( + (s) => s.topic === entry?.topic, + ); + if (entry && this.mqtt && !stillUsed) { + this.mqtt.unsubscribe(entry.topic, (err) => { + if (err) { + this.report(err); + } + }); + } + } + // A filter that lost its last QoS-1 sub but still has QoS-0 subs must be + // re-SUBSCRIBEd at the lower QoS, or the broker keeps replaying to at-most-once subs. + for (const filter of maybeDowngrade) { + const stillUsed = [...this.subscriptions.values()].some( + (s) => s.topic === filter, + ); + if (stillUsed && this.effectiveQos(filter) === 0) { + this.mqtt?.subscribe(filter, { qos: 0 }, (err) => { + if (err) { + this.report(err); + } + }); + } + } + if (this.subscriptions.size === 0) { + this.close(); + } + }; + + // If any topic's subscribe fails, roll this call's registrations back (and drop + // any now-unused broker filter) so a failed subscribe leaks no callback, then rethrow. + try { + await Promise.all(acks); + } catch (err) { + unsubscribe(); + throw err; + } + + const update = (next: SubscribeOptions) => { + const enabling = next.background === true; + const changedFilters = new Set(); + // Remember the pre-update QoS per id so a rejected re-SUBSCRIBE can be rolled back. + const previous = new Map(); + for (const id of ids) { + const entry = this.subscriptions.get(id); + if (!entry) { + continue; + } + if (next.background !== undefined) { + entry.background = next.background; + } + if (next.title !== undefined) { + entry.title = next.title; + } + if (next.retry !== undefined) { + const q: 0 | 1 = next.retry ? 1 : 0; + if (q !== entry.qos) { + previous.set(id, entry.qos); + entry.qos = q; + changedFilters.add(entry.topic); + } + } + } + if (enabling) { + this.requestNotificationPermission(); + } + // Re-SUBSCRIBE the affected filters at their new effective QoS (a SUBSCRIBE to an + // existing filter just updates its QoS — no unsubscribe gap). If the broker rejects + // it, the old subscription still stands, so restore the local QoS to match and + // surface the error rather than silently assuming the change took effect. + for (const filter of changedFilters) { + this.mqtt?.subscribe( + filter, + { qos: this.effectiveQos(filter) }, + (err) => { + if (!err) { + return; + } + for (const [id, q] of previous) { + const entry = this.subscriptions.get(id); + if (entry && entry.topic === filter) { + entry.qos = q; + } + } + this.report(err); + }, + ); + } + }; + + return { unsubscribe, update }; + } + + /** Tear down the connection and drop all active subscriptions. */ + close(): void { + if (this.mqtt) { + this.mqtt.end(true); + this.mqtt = null; + } + this.connecting = null; + this.everConnected = false; + this.subscriptions.clear(); + } + + /** + * Re-register every active subscription. Called on each reconnect so a client that + * reconnects with a fresh (or cleared) session does not silently stop receiving; + * re-subscribing an existing filter is idempotent. + */ + private resubscribeAll(): void { + const client = this.mqtt; + if (!client) { + return; + } + const filters = new Set( + [...this.subscriptions.values()].map((s) => s.topic), + ); + for (const filter of filters) { + client.subscribe( + filter, + { qos: this.effectiveQos(filter) }, + (err) => { + if (err) { + this.report(err); + } + }, + ); + } + } + + // --- internals --- + + private connect(): Promise { + if (this.mqtt) { + return Promise.resolve(this.mqtt); + } + if (!this.connecting) { + this.connecting = this.open(); + // A failed open must not poison future attempts — clear the cached + // in-flight promise so a later subscribe/publish can retry. + this.connecting.catch(() => { + this.connecting = null; + }); + } + return this.connecting.then(() => this.mqtt!); + } + + private endpointUrl(): string { + // The push endpoint set on the client (Client.setPushEndpoint) wins. + if (this.client.config.endpointPush !== '') { + return this.client.config.endpointPush; + } + // Otherwise default to a `push.` host on the regular API endpoint (not the + // independently-configurable realtime endpoint), scheme switched to ws/wss. + const wsEndpoint = this.client.config.endpoint + .replace('https://', 'wss://') + .replace('http://', 'ws://'); + // Prefix the host with `push.` (scheme://host/path -> scheme://push.host/path). + const separator = '://'; + const at = wsEndpoint.indexOf(separator); + return at === -1 + ? wsEndpoint + : `${wsEndpoint.slice(0, at + separator.length)}push.${wsEndpoint.slice(at + separator.length)}`; + } + + private async open(): Promise { + // Read the credential set on the client (via Client.setJWT / setSession). + let authMethod: AuthMethod; + let credential: string; + if (this.client.config.jwt) { + authMethod = 'appwrite-jwt'; + credential = this.client.config.jwt; + } else if (this.client.config.session) { + authMethod = 'appwrite-session'; + credential = this.client.config.session; + } else { + throw new Error( + 'No credential set on the client; call Client.setJWT() or Client.setSession() first.', + ); + } + + const project = this.client.config.project ?? ''; + // Client id, when the app did not set one with Client.setPushClientId(). Unlike the + // native SDKs (which send an empty id and let the broker derive a stable one), mqtt.js + // rejects an empty id with clean start off, so derive a stable per-user id here from the + // credential this connection authenticates with: the JWT's userId, or the session's user + // id. A stable id keeps the broker's replay cursor resuming across reloads. The + // credential itself is never used as the id. + const clientId = + this.client.config.pushClientId || + (authMethod === 'appwrite-jwt' + ? userIdFromJwt(credential) + : userIdFromSession(credential)) || + this.fallbackClientId; + + const options: IClientOptions = { + clientId, + protocolVersion: 5, + clean: false, // always keep the session so the broker can replay QoS-1 topics + keepalive: KEEP_ALIVE_SECONDS, + reconnectPeriod: RECONNECT_PERIOD_MS, + manualConnect: true, + // Enhanced auth carried in CONNECT properties. + properties: { + authenticationMethod: authMethod, + authenticationData: Buffer.from(credential), + userProperties: { projectId: project }, + }, + }; + + const client = mqtt.connect(this.endpointUrl(), options); + this.mqtt = client; + + client.on('message', (_topic, _payload, packet) => { + this.dispatch(packet).catch((err) => this.report(err)); + }); + + // Persistent lifecycle listeners. Re-register every subscription on reconnect + // (harmless if the broker resumed our session), and surface the lifecycle to the + // caller's onOpen/onClose/onError hooks. + client.on('connect', () => { + if (this.everConnected) { + this.resubscribeAll(); + } + this.everConnected = true; + this.onOpenCb?.(); + }); + client.on('close', () => this.onCloseCb?.()); + // The broker explains a refused CONNECT (CONNACK) or a server-initiated DISCONNECT + // with an MQTT 5 Reason String. mqtt.js reports only the reason code, so capture the + // string off the packet (packetreceive fires before mqtt.js handles it) and hand it + // to onError as the error message. + let refusedReason: string | undefined; + client.on('packetreceive', (packet) => { + if (packet.cmd === 'connack') { + refusedReason = + (packet.reasonCode ?? 0) !== 0 + ? packet.properties?.reasonString + : undefined; + } + }); + client.on('error', (err) => { + const error = this.connectError(err, refusedReason); + // While the first connect is pending its caller receives the error (and reports it + // through fail()); afterwards, e.g. on reconnect, report it here. + if (!this.opening) { + this.report(error); + } + }); + client.on('disconnect', (packet) => { + if ((packet.reasonCode ?? 0) !== 0) { + this.report( + new Error( + packet.properties?.reasonString ?? + `Disconnected by the broker (reason ${packet.reasonCode})`, + ), + ); + } + }); + + this.opening = true; + await new Promise((resolve, reject) => { + const onConnect = (packet: IConnackPacket) => { + cleanup(); + const rc = packet.reasonCode ?? packet.returnCode ?? 0; + if (rc !== 0) { + this.close(); + reject( + new Error( + refusedReason ?? + `MQTT authentication failed (CONNACK reason ${rc})`, + ), + ); + } else { + resolve(); + } + }; + const onError = (err: Error) => { + cleanup(); + this.close(); + reject(this.connectError(err, refusedReason)); + }; + const cleanup = () => { + this.opening = false; + client.removeListener('connect', onConnect); + client.removeListener('error', onError); + }; + client.on('connect', onConnect); + client.on('error', onError); + client.connect(); + }); + } + + /** + * The error for a failed connect: the broker's Reason String when it sent one (a refused + * CONNACK), else mqtt.js's error. Memoized so both error listeners hand out the same + * object and it is reported once. + */ + private connectError(err: unknown, reason: string | undefined): Error { + const source = toError(err); + let error = this.connectErrors.get(source); + if (!error) { + error = reason ? new Error(reason) : source; + this.connectErrors.set(source, error); + } + return error; + } + + /** Fan a delivered packet out to every subscription whose filter matches. */ + private async dispatch(packet: IPublishPacket): Promise { + const payload = Buffer.isBuffer(packet.payload) + ? packet.payload + : Buffer.from(packet.payload); + const message: PushMessage = { + topic: packet.topic.toString(), + data: new TextDecoder().decode(payload), + payload, + qos: packet.qos, + }; + for (const sub of this.subscriptions.values()) { + if (matches(sub.topic, message.topic)) { + await sub.callback(message); + // Notification is per-subscription: only subs that opted in post one, + // each with its own title. + if (sub.background) { + this.notify(message, sub.title); + } + } + } + } + + private requestNotificationPermission(): void { + if ( + typeof Notification !== 'undefined' && + Notification.permission === 'default' + ) { + Promise.resolve(Notification.requestPermission()).catch((err) => + this.report(err), + ); + } + } + + /** Post a browser notification for a message a background subscription matched. */ + private notify(message: PushMessage, title?: string): void { + if ( + typeof Notification === 'undefined' || + Notification.permission !== 'granted' + ) { + return; + } + try { + new Notification(title ?? message.topic, { + body: message.data, + }); + } catch { + // Some browsers only allow notifications from a service worker; ignore. + } + } +} + +/** Normalize anything thrown or passed to an error handler into an `Error`. */ +function toError(error: unknown): Error { + return error instanceof Error ? error : new Error(String(error)); +} + +/** + * The userId claim from an Appwrite JWT, used as the default MQTT client id so the + * broker's replay cursor is stable per user. Returns '' when the token is not a decodable JWT + * (e.g. a plain string), so the caller can fall back to the session or raw credential. + */ +function userIdFromJwt(jwt?: string): string { + if (!jwt) { + return ''; + } + const parts = jwt.split('.'); + if (parts.length < 2) { + return ''; + } + try { + const json = Buffer.from( + parts[1].replace(/-/g, '+').replace(/_/g, '/'), + 'base64', + ).toString('utf-8'); + const payload = JSON.parse(json); + return typeof payload.userId === 'string' ? payload.userId : ''; + } catch { + return ''; + } +} + +/** + * The user id inside a session secret, which is base64 of JSON `{"id": , "secret": ...}`. + * Returns '' when the value is not such a secret. + */ +function userIdFromSession(session?: string): string { + if (!session) { + return ''; + } + try { + const payload = JSON.parse( + Buffer.from(session, 'base64').toString('utf-8'), + ); + return typeof payload.id === 'string' ? payload.id : ''; + } catch { + return ''; + } +} + +/** A hex id, uuid4-like enough for a client-generated client id suffix. */ +function randomId(): string { + let out = ''; + for (let i = 0; i < 32; i++) { + out += Math.floor(Math.random() * 16).toString(16); + } + return out; +} + +/** MQTT topic-filter match with '+' (single level) and '#' (multi level). */ +function matches(filter: string, topic: string): boolean { + const filterParts = filter.split('/'); + const topicParts = topic.split('/'); + for (let i = 0; i < filterParts.length; i++) { + const part = filterParts[i]; + if (part === '#') { + return true; + } + if (i >= topicParts.length) { + return false; + } + if (part !== '+' && part !== topicParts[i]) { + return false; + } + } + return filterParts.length === topicParts.length; +} diff --git a/src/services/tables-db.ts b/src/services/tables-db.ts index 2f2636e1..dd0cb4f8 100644 --- a/src/services/tables-db.ts +++ b/src/services/tables-db.ts @@ -6292,6 +6292,129 @@ export class TablesDB { return this.client.call('post', uri, apiHeaders, payload); } + /** + * Update relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). + * + * + * @param {string} params.databaseId - Database ID. + * @param {string} params.tableId - Table ID. + * @param {string} params.key - Column Key. + * @param {RelationMutate} params.onDelete - Delete constraint. Possible values are: cascade, restrict, setNull. + * @param {string} params.newKey - New Column Key. + * @throws {AppwriteException} + * @returns {Promise} + */ + updateRelationshipColumn(params: { + databaseId: string; + tableId: string; + key: string; + onDelete?: RelationMutate; + newKey?: string; + }): Promise; + /** + * Update relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). + * + * + * @param {string} databaseId - Database ID. + * @param {string} tableId - Table ID. + * @param {string} key - Column Key. + * @param {RelationMutate} onDelete - Delete constraint. Possible values are: cascade, restrict, setNull. + * @param {string} newKey - New Column Key. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updateRelationshipColumn( + databaseId: string, + tableId: string, + key: string, + onDelete?: RelationMutate, + newKey?: string, + ): Promise; + updateRelationshipColumn( + paramsOrFirst: + | { + databaseId: string; + tableId: string; + key: string; + onDelete?: RelationMutate; + newKey?: string; + } + | string, + ...rest: [string?, string?, RelationMutate?, string?] + ): Promise { + let params: { + databaseId: string; + tableId: string; + key: string; + onDelete?: RelationMutate; + newKey?: string; + }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + databaseId: string; + tableId: string; + key: string; + onDelete?: RelationMutate; + newKey?: string; + }; + } else { + params = { + databaseId: paramsOrFirst as string, + tableId: rest[0] as string, + key: rest[1] as string, + onDelete: rest[2] as RelationMutate, + newKey: rest[3] as string, + }; + } + + const databaseId = params.databaseId; + const tableId = params.tableId; + const key = params.key; + const onDelete = params.onDelete; + const newKey = params.newKey; + + if (typeof databaseId === 'undefined' || databaseId === '') { + throw new AppwriteException( + 'Missing required parameter: "databaseId"', + ); + } + if (typeof tableId === 'undefined' || tableId === '') { + throw new AppwriteException( + 'Missing required parameter: "tableId"', + ); + } + if (typeof key === 'undefined' || key === '') { + throw new AppwriteException('Missing required parameter: "key"'); + } + const apiPath = + '/tablesdb/{databaseId}/tables/{tableId}/columns/relationship/{key}' + .replace('{databaseId}', encodeURIComponent(String(databaseId))) + .replace('{tableId}', encodeURIComponent(String(tableId))) + .replace('{key}', encodeURIComponent(String(key))); + const payload: Payload = {}; + if (typeof onDelete !== 'undefined') { + payload['onDelete'] = onDelete; + } + if (typeof newKey !== 'undefined') { + payload['newKey'] = newKey; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('patch', uri, apiHeaders, payload); + } + /** * Create a string column. * @@ -7552,7 +7675,7 @@ export class TablesDB { * @param {string} params.tableId - Table ID. * @param {string} params.key - Column Key. * @throws {AppwriteException} - * @returns {Promise} + * @returns {Promise} */ getColumn(params: { databaseId: string; @@ -7560,6 +7683,7 @@ export class TablesDB { key: string; }): Promise< | Models.ColumnBoolean + | Models.ColumnBigint | Models.ColumnInteger | Models.ColumnFloat | Models.ColumnEmail @@ -7568,6 +7692,13 @@ export class TablesDB { | Models.ColumnIp | Models.ColumnDatetime | Models.ColumnRelationship + | Models.ColumnPoint + | Models.ColumnLine + | Models.ColumnPolygon + | Models.ColumnVarchar + | Models.ColumnText + | Models.ColumnMediumtext + | Models.ColumnLongtext | Models.ColumnString >; /** @@ -7577,7 +7708,7 @@ export class TablesDB { * @param {string} tableId - Table ID. * @param {string} key - Column Key. * @throws {AppwriteException} - * @returns {Promise} + * @returns {Promise} * @deprecated Use the object parameter style method for a better developer experience. */ getColumn( @@ -7586,6 +7717,7 @@ export class TablesDB { key: string, ): Promise< | Models.ColumnBoolean + | Models.ColumnBigint | Models.ColumnInteger | Models.ColumnFloat | Models.ColumnEmail @@ -7594,6 +7726,13 @@ export class TablesDB { | Models.ColumnIp | Models.ColumnDatetime | Models.ColumnRelationship + | Models.ColumnPoint + | Models.ColumnLine + | Models.ColumnPolygon + | Models.ColumnVarchar + | Models.ColumnText + | Models.ColumnMediumtext + | Models.ColumnLongtext | Models.ColumnString >; getColumn( @@ -7602,6 +7741,7 @@ export class TablesDB { ...rest: [string?, string?] ): Promise< | Models.ColumnBoolean + | Models.ColumnBigint | Models.ColumnInteger | Models.ColumnFloat | Models.ColumnEmail @@ -7610,6 +7750,13 @@ export class TablesDB { | Models.ColumnIp | Models.ColumnDatetime | Models.ColumnRelationship + | Models.ColumnPoint + | Models.ColumnLine + | Models.ColumnPolygon + | Models.ColumnVarchar + | Models.ColumnText + | Models.ColumnMediumtext + | Models.ColumnLongtext | Models.ColumnString > { let params: { databaseId: string; tableId: string; key: string }; @@ -7747,129 +7894,6 @@ export class TablesDB { return this.client.call('delete', uri, apiHeaders, payload); } - /** - * Update relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). - * - * - * @param {string} params.databaseId - Database ID. - * @param {string} params.tableId - Table ID. - * @param {string} params.key - Column Key. - * @param {RelationMutate} params.onDelete - Delete constraint. Possible values are: cascade, restrict, setNull. - * @param {string} params.newKey - New Column Key. - * @throws {AppwriteException} - * @returns {Promise} - */ - updateRelationshipColumn(params: { - databaseId: string; - tableId: string; - key: string; - onDelete?: RelationMutate; - newKey?: string; - }): Promise; - /** - * Update relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). - * - * - * @param {string} databaseId - Database ID. - * @param {string} tableId - Table ID. - * @param {string} key - Column Key. - * @param {RelationMutate} onDelete - Delete constraint. Possible values are: cascade, restrict, setNull. - * @param {string} newKey - New Column Key. - * @throws {AppwriteException} - * @returns {Promise} - * @deprecated Use the object parameter style method for a better developer experience. - */ - updateRelationshipColumn( - databaseId: string, - tableId: string, - key: string, - onDelete?: RelationMutate, - newKey?: string, - ): Promise; - updateRelationshipColumn( - paramsOrFirst: - | { - databaseId: string; - tableId: string; - key: string; - onDelete?: RelationMutate; - newKey?: string; - } - | string, - ...rest: [string?, string?, RelationMutate?, string?] - ): Promise { - let params: { - databaseId: string; - tableId: string; - key: string; - onDelete?: RelationMutate; - newKey?: string; - }; - - if ( - paramsOrFirst && - typeof paramsOrFirst === 'object' && - !Array.isArray(paramsOrFirst) - ) { - params = (paramsOrFirst || {}) as { - databaseId: string; - tableId: string; - key: string; - onDelete?: RelationMutate; - newKey?: string; - }; - } else { - params = { - databaseId: paramsOrFirst as string, - tableId: rest[0] as string, - key: rest[1] as string, - onDelete: rest[2] as RelationMutate, - newKey: rest[3] as string, - }; - } - - const databaseId = params.databaseId; - const tableId = params.tableId; - const key = params.key; - const onDelete = params.onDelete; - const newKey = params.newKey; - - if (typeof databaseId === 'undefined' || databaseId === '') { - throw new AppwriteException( - 'Missing required parameter: "databaseId"', - ); - } - if (typeof tableId === 'undefined' || tableId === '') { - throw new AppwriteException( - 'Missing required parameter: "tableId"', - ); - } - if (typeof key === 'undefined' || key === '') { - throw new AppwriteException('Missing required parameter: "key"'); - } - const apiPath = - '/tablesdb/{databaseId}/tables/{tableId}/columns/{key}/relationship' - .replace('{databaseId}', encodeURIComponent(String(databaseId))) - .replace('{tableId}', encodeURIComponent(String(tableId))) - .replace('{key}', encodeURIComponent(String(key))); - const payload: Payload = {}; - if (typeof onDelete !== 'undefined') { - payload['onDelete'] = onDelete; - } - if (typeof newKey !== 'undefined') { - payload['newKey'] = newKey; - } - const uri = new URL(this.client.config.endpoint + apiPath); - - const apiHeaders: { [header: string]: string } = { - 'X-Appwrite-Project': this.client.config.project, - 'content-type': 'application/json', - accept: 'application/json', - }; - - return this.client.call('patch', uri, apiHeaders, payload); - } - /** * List indexes on the table. * diff --git a/src/services/users.ts b/src/services/users.ts index 78f9795f..ea6678f5 100644 --- a/src/services/users.ts +++ b/src/services/users.ts @@ -1554,7 +1554,7 @@ export class Users { } /** - * Enable or disable whether a user can impersonate other users. When impersonation headers are used, the request runs as the target user for API behavior, while internal audit logs still attribute the action to the original impersonator and store the impersonated target details only in internal audit payload data. + * Enable or disable whether a user can impersonate other users. When impersonation headers are used, the request runs as the target user for API behavior, while internal audit logs still attribute the action to the original impersonator and store the impersonated target details only in internal audit payload data. Account endpoints are read-only while impersonating: they report the target's account, and anything that would change it is refused, so an impersonator cannot alter the target's credentials or delete their account. * * * @param {string} params.userId - User ID. @@ -1569,7 +1569,7 @@ export class Users { impersonator: boolean; }): Promise>; /** - * Enable or disable whether a user can impersonate other users. When impersonation headers are used, the request runs as the target user for API behavior, while internal audit logs still attribute the action to the original impersonator and store the impersonated target details only in internal audit payload data. + * Enable or disable whether a user can impersonate other users. When impersonation headers are used, the request runs as the target user for API behavior, while internal audit logs still attribute the action to the original impersonator and store the impersonated target details only in internal audit payload data. Account endpoints are read-only while impersonating: they report the target's account, and anything that would change it is refused, so an impersonator cannot alter the target's credentials or delete their account. * * * @param {string} userId - User ID. diff --git a/src/topic.ts b/src/topic.ts new file mode 100644 index 00000000..cd6cea01 --- /dev/null +++ b/src/topic.ts @@ -0,0 +1,103 @@ +/** + * Validate the levels passed to `path()`: a non-empty array of non-empty strings, none holding + * the level separator `/` or the wildcards `+` and `#` (use `any()` and `all()` for those). + */ +function levelsOf(levels: unknown): string[] { + if (!Array.isArray(levels)) { + throw new Error("path() expects an array, e.g. path(['user'])"); + } + if (levels.length === 0) { + throw new Error('path() needs at least one level'); + } + return levels.map((level: unknown, index: number) => { + if (typeof level !== 'string') { + const type = level === null ? 'null' : typeof level; + throw new Error( + `level at index ${index} must be a string, got ${type}`, + ); + } + if (level === '') { + throw new Error(`empty level at index ${index}`); + } + if (level.includes('/')) { + const parts = level + .split('/') + .map((part) => `'${part}'`) + .join(', '); + throw new Error( + `"${level}" contains "/", split it: path([${parts}])`, + ); + } + if (level === '+') { + throw new Error('"+" is reserved, use any()'); + } + if (level.includes('+')) { + throw new Error(`"${level}" contains "+", use any() for wildcards`); + } + if (level === '#') { + throw new Error('"#" is reserved, use all()'); + } + if (level.includes('#')) { + throw new Error(`"${level}" contains "#", use all() for wildcards`); + } + return level; + }); +} + +/** + * Helper to build MQTT topics for `Push`, level by level. + * + * Topic.path(['user', userId, 'notification']); // "user/123/notification" + * Topic.path(['user']).any().path(['notification']); // "user/+/notification" + * Topic.path(['org']).any().path(['logs']).all(); // "org/+/logs/#" + * + * `any()` matches exactly one level and `all()` everything below, at any depth (including the + * parent itself). `all()` ends the topic, so it returns a {@link ResolvedTopic}. + */ +export class Topic { + private constructor(private readonly levels: readonly string[]) {} + + /** Start a topic with the given levels, e.g. `Topic.path(['user', userId])`. */ + static path(levels: string[]): Topic { + return new Topic(levelsOf(levels)); + } + + /** Start a topic with a single-level wildcard (`+`). */ + static any(): Topic { + return new Topic(['+']); + } + + /** The multi-level wildcard alone (`#`): matches every topic. */ + static all(): ResolvedTopic { + return new ResolvedTopic(['#']); + } + + /** Append the given levels. */ + path(levels: string[]): Topic { + return new Topic([...this.levels, ...levelsOf(levels)]); + } + + /** Append a single-level wildcard (`+`): matches exactly one level. */ + any(): Topic { + return new Topic([...this.levels, '+']); + } + + /** Append the multi-level wildcard (`#`) and end the topic. */ + all(): ResolvedTopic { + return new ResolvedTopic([...this.levels, '#']); + } + + toString(): string { + return this.levels.join('/'); + } +} + +/** A topic ended by `all()`: nothing can be appended after the `#` wildcard. */ +export class ResolvedTopic { + /** @internal Created by {@link Topic.all}. */ + constructor(private readonly levels: readonly string[]) {} + + toString(): string { + return this.levels.join('/'); + } +}