UNPKG

appwrite

Version:

Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API

1,320 lines (1,307 loc) 263 kB
'use strict'; var JSONbigModule = require('json-bigint'); var BigNumber = require('bignumber.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var JSONbigModule__default = /*#__PURE__*/_interopDefaultLegacy(JSONbigModule); var BigNumber__default = /*#__PURE__*/_interopDefaultLegacy(BigNumber); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __classPrivateFieldGet(receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); } const JSONbig$1 = JSONbigModule__default["default"]({ useNativeBigInt: true }); /** * Helper class to generate query strings. */ class Query { /** * Constructor for Query class. * * @param {string} method * @param {AttributesTypes} attribute * @param {QueryTypes} values */ constructor(method, attribute, values) { this.method = method; this.attribute = attribute; if (values !== undefined) { if (Array.isArray(values)) { this.values = values; } else { this.values = [values]; } } } /** * Convert the query object to a JSON string. * * @returns {string} */ toString() { return JSONbig$1.stringify({ method: this.method, attribute: this.attribute, values: this.values, }); } } /** * Filter resources where attribute is equal to value. * * @param {string} attribute * @param {QueryTypes} value * @returns {string} */ Query.equal = (attribute, value) => new Query("equal", attribute, value).toString(); /** * Filter resources where attribute is not equal to value. * * @param {string} attribute * @param {QueryTypes} value * @returns {string} */ Query.notEqual = (attribute, value) => new Query("notEqual", attribute, value).toString(); /** * Filter resources where attribute matches a regular expression pattern. * * @param {string} attribute The attribute to filter on. * @param {string} pattern The regular expression pattern to match. * @returns {string} */ Query.regex = (attribute, pattern) => new Query("regex", attribute, pattern).toString(); /** * Filter resources where attribute is less than value. * * @param {string} attribute * @param {QueryTypes} value * @returns {string} */ Query.lessThan = (attribute, value) => new Query("lessThan", attribute, value).toString(); /** * Filter resources where attribute is less than or equal to value. * * @param {string} attribute * @param {QueryTypes} value * @returns {string} */ Query.lessThanEqual = (attribute, value) => new Query("lessThanEqual", attribute, value).toString(); /** * Filter resources where attribute is greater than value. * * @param {string} attribute * @param {QueryTypes} value * @returns {string} */ Query.greaterThan = (attribute, value) => new Query("greaterThan", attribute, value).toString(); /** * Filter resources where attribute is greater than or equal to value. * * @param {string} attribute * @param {QueryTypes} value * @returns {string} */ Query.greaterThanEqual = (attribute, value) => new Query("greaterThanEqual", attribute, value).toString(); /** * Filter resources where attribute is null. * * @param {string} attribute * @returns {string} */ Query.isNull = (attribute) => new Query("isNull", attribute).toString(); /** * Filter resources where attribute is not null. * * @param {string} attribute * @returns {string} */ Query.isNotNull = (attribute) => new Query("isNotNull", attribute).toString(); /** * Filter resources where the specified attributes exist. * * @param {string[]} attributes The list of attributes that must exist. * @returns {string} */ Query.exists = (attributes) => new Query("exists", undefined, attributes).toString(); /** * Filter resources where the specified attributes do not exist. * * @param {string[]} attributes The list of attributes that must not exist. * @returns {string} */ Query.notExists = (attributes) => new Query("notExists", undefined, attributes).toString(); /** * Filter resources where attribute is between start and end (inclusive). * * @param {string} attribute * @param {string | number | bigint} start * @param {string | number | bigint} end * @returns {string} */ Query.between = (attribute, start, end) => new Query("between", attribute, [start, end]).toString(); /** * Filter resources where attribute starts with value. * * @param {string} attribute * @param {string} value * @returns {string} */ Query.startsWith = (attribute, value) => new Query("startsWith", attribute, value).toString(); /** * Filter resources where attribute ends with value. * * @param {string} attribute * @param {string} value * @returns {string} */ Query.endsWith = (attribute, value) => new Query("endsWith", attribute, value).toString(); /** * Specify which attributes should be returned by the API call. * * @param {string[]} attributes * @returns {string} */ Query.select = (attributes) => new Query("select", undefined, attributes).toString(); /** * Filter resources by searching attribute for value. * A fulltext index on attribute is required for this query to work. * * @param {string} attribute * @param {string} value * @returns {string} */ Query.search = (attribute, value) => new Query("search", attribute, value).toString(); /** * Sort results by attribute descending. * * @param {string} attribute * @returns {string} */ Query.orderDesc = (attribute) => new Query("orderDesc", attribute).toString(); /** * Sort results by attribute ascending. * * @param {string} attribute * @returns {string} */ Query.orderAsc = (attribute) => new Query("orderAsc", attribute).toString(); /** * Sort results randomly. * * @returns {string} */ Query.orderRandom = () => new Query("orderRandom").toString(); /** * Return results after documentId. * * @param {string} documentId * @returns {string} */ Query.cursorAfter = (documentId) => new Query("cursorAfter", undefined, documentId).toString(); /** * Return results before documentId. * * @param {string} documentId * @returns {string} */ Query.cursorBefore = (documentId) => new Query("cursorBefore", undefined, documentId).toString(); /** * Return only limit results. * * @param {number} limit * @returns {string} */ Query.limit = (limit) => new Query("limit", undefined, limit).toString(); /** * Filter resources by skipping the first offset results. * * @param {number} offset * @returns {string} */ Query.offset = (offset) => new Query("offset", undefined, offset).toString(); /** * Filter resources where attribute contains the specified value. * * @param {string} attribute * @param {string | string[]} value * @returns {string} */ Query.contains = (attribute, value) => new Query("contains", attribute, value).toString(); /** * Filter resources where attribute does not contain the specified value. * * @param {string} attribute * @param {string | any[]} value * @returns {string} */ Query.notContains = (attribute, value) => new Query("notContains", attribute, value).toString(); /** * Filter resources by searching attribute for value (inverse of search). * A fulltext index on attribute is required for this query to work. * * @param {string} attribute * @param {string} value * @returns {string} */ Query.notSearch = (attribute, value) => new Query("notSearch", attribute, value).toString(); /** * Filter resources where attribute is not between start and end (exclusive). * * @param {string} attribute * @param {string | number | bigint} start * @param {string | number | bigint} end * @returns {string} */ Query.notBetween = (attribute, start, end) => new Query("notBetween", attribute, [start, end]).toString(); /** * Filter resources where attribute does not start with value. * * @param {string} attribute * @param {string} value * @returns {string} */ Query.notStartsWith = (attribute, value) => new Query("notStartsWith", attribute, value).toString(); /** * Filter resources where attribute does not end with value. * * @param {string} attribute * @param {string} value * @returns {string} */ Query.notEndsWith = (attribute, value) => new Query("notEndsWith", attribute, value).toString(); /** * Filter resources where document was created before date. * * @param {string} value * @returns {string} */ Query.createdBefore = (value) => Query.lessThan("$createdAt", value); /** * Filter resources where document was created after date. * * @param {string} value * @returns {string} */ Query.createdAfter = (value) => Query.greaterThan("$createdAt", value); /** * Filter resources where document was created between dates. * * @param {string} start * @param {string} end * @returns {string} */ Query.createdBetween = (start, end) => Query.between("$createdAt", start, end); /** * Filter resources where document was updated before date. * * @param {string} value * @returns {string} */ Query.updatedBefore = (value) => Query.lessThan("$updatedAt", value); /** * Filter resources where document was updated after date. * * @param {string} value * @returns {string} */ Query.updatedAfter = (value) => Query.greaterThan("$updatedAt", value); /** * Filter resources where document was updated between dates. * * @param {string} start * @param {string} end * @returns {string} */ Query.updatedBetween = (start, end) => Query.between("$updatedAt", start, end); /** * Combine multiple queries using logical OR operator. * * @param {string[]} queries * @returns {string} */ Query.or = (queries) => new Query("or", undefined, queries.map((query) => JSONbig$1.parse(query))).toString(); /** * Combine multiple queries using logical AND operator. * * @param {string[]} queries * @returns {string} */ Query.and = (queries) => new Query("and", undefined, queries.map((query) => JSONbig$1.parse(query))).toString(); /** * Filter array elements where at least one element matches all the specified queries. * * @param {string} attribute The attribute containing the array to filter on. * @param {string[]} queries The list of query strings to match against array elements. * @returns {string} */ Query.elemMatch = (attribute, queries) => new Query("elemMatch", attribute, queries.map((query) => JSONbig$1.parse(query))).toString(); /** * Filter resources where attribute is at a specific distance from the given coordinates. * * @param {string} attribute * @param {any[]} values * @param {number} distance * @param {boolean} meters * @returns {string} */ Query.distanceEqual = (attribute, values, distance, meters = true) => new Query("distanceEqual", attribute, [[values, distance, meters]]).toString(); /** * Filter resources where attribute is not at a specific distance from the given coordinates. * * @param {string} attribute * @param {any[]} values * @param {number} distance * @param {boolean} meters * @returns {string} */ Query.distanceNotEqual = (attribute, values, distance, meters = true) => new Query("distanceNotEqual", attribute, [[values, distance, meters]]).toString(); /** * Filter resources where attribute is at a distance greater than the specified value from the given coordinates. * * @param {string} attribute * @param {any[]} values * @param {number} distance * @param {boolean} meters * @returns {string} */ Query.distanceGreaterThan = (attribute, values, distance, meters = true) => new Query("distanceGreaterThan", attribute, [[values, distance, meters]]).toString(); /** * Filter resources where attribute is at a distance less than the specified value from the given coordinates. * * @param {string} attribute * @param {any[]} values * @param {number} distance * @param {boolean} meters * @returns {string} */ Query.distanceLessThan = (attribute, values, distance, meters = true) => new Query("distanceLessThan", attribute, [[values, distance, meters]]).toString(); /** * Filter resources where attribute intersects with the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.intersects = (attribute, values) => new Query("intersects", attribute, [values]).toString(); /** * Filter resources where attribute does not intersect with the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.notIntersects = (attribute, values) => new Query("notIntersects", attribute, [values]).toString(); /** * Filter resources where attribute crosses the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.crosses = (attribute, values) => new Query("crosses", attribute, [values]).toString(); /** * Filter resources where attribute does not cross the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.notCrosses = (attribute, values) => new Query("notCrosses", attribute, [values]).toString(); /** * Filter resources where attribute overlaps with the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.overlaps = (attribute, values) => new Query("overlaps", attribute, [values]).toString(); /** * Filter resources where attribute does not overlap with the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.notOverlaps = (attribute, values) => new Query("notOverlaps", attribute, [values]).toString(); /** * Filter resources where attribute touches the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.touches = (attribute, values) => new Query("touches", attribute, [values]).toString(); /** * Filter resources where attribute does not touch the given geometry. * * @param {string} attribute * @param {any[]} values * @returns {string} */ Query.notTouches = (attribute, values) => new Query("notTouches", attribute, [values]).toString(); const JSONbigParser = JSONbigModule__default["default"]({ storeAsString: false }); const JSONbigSerializer = JSONbigModule__default["default"]({ useNativeBigInt: true }); const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER); const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER); function reviver(_key, value) { if (BigNumber__default["default"].isBigNumber(value)) { if (value.isInteger()) { const str = value.toFixed(); const bi = BigInt(str); if (bi >= MIN_SAFE && bi <= MAX_SAFE) { return Number(str); } return bi; } return value.toNumber(); } return value; } const JSONbig = { parse: (text) => JSONbigParser.parse(text, reviver), stringify: JSONbigSerializer.stringify }; /** * Exception thrown by the package */ class AppwriteException extends Error { /** * Initializes a Appwrite Exception. * * @param {string} message - The error message. * @param {number} code - The error code. Default is 0. * @param {string} type - The error type. Default is an empty string. * @param {string} response - The response string. Default is an empty string. */ constructor(message, code = 0, type = '', response = '') { super(message); this.name = 'AppwriteException'; this.message = message; this.code = code; this.type = type; this.response = response; } } /** * Client that handles requests to Appwrite */ class Client { constructor() { /** * Holds configuration such as project. */ this.config = { endpoint: 'https://cloud.appwrite.io/v1', endpointRealtime: '', project: '', jwt: '', locale: '', session: '', devkey: '', }; /** * Custom headers for API requests. */ this.headers = { 'x-sdk-name': 'Web', 'x-sdk-platform': 'client', 'x-sdk-language': 'web', 'x-sdk-version': '22.3.0', 'X-Appwrite-Response-Format': '1.8.0', }; this.realtime = { socket: undefined, timeout: undefined, heartbeat: undefined, url: '', channels: new Set(), queries: new Set(), subscriptions: new Map(), slotToSubscriptionId: new Map(), subscriptionIdToSlot: new Map(), subscriptionsCounter: 0, reconnect: true, reconnectAttempts: 0, lastMessage: undefined, connect: () => { clearTimeout(this.realtime.timeout); this.realtime.timeout = window === null || window === void 0 ? void 0 : window.setTimeout(() => { this.realtime.createSocket(); }, 50); }, getTimeout: () => { switch (true) { case this.realtime.reconnectAttempts < 5: return 1000; case this.realtime.reconnectAttempts < 15: return 5000; case this.realtime.reconnectAttempts < 100: return 10000; default: return 60000; } }, createHeartbeat: () => { if (this.realtime.heartbeat) { clearTimeout(this.realtime.heartbeat); } this.realtime.heartbeat = window === null || window === void 0 ? void 0 : window.setInterval(() => { var _a; (_a = this.realtime.socket) === null || _a === void 0 ? void 0 : _a.send(JSONbig.stringify({ type: 'ping' })); }, 20000); }, createSocket: () => { var _a, _b, _c, _d; if (this.realtime.subscriptions.size < 1) { this.realtime.reconnect = false; (_a = this.realtime.socket) === null || _a === void 0 ? void 0 : _a.close(); return; } const encodedProject = encodeURIComponent((_b = this.config.project) !== null && _b !== void 0 ? _b : ''); let queryParams = 'project=' + encodedProject; this.realtime.channels.forEach(channel => { queryParams += '&channels[]=' + encodeURIComponent(channel); }); // Per-subscription queries: channel[slot][]=query so server can route events by subscription const selectAllQuery = Query.select(['*']).toString(); this.realtime.subscriptions.forEach((sub, slot) => { const queries = sub.queries.length > 0 ? sub.queries : [selectAllQuery]; sub.channels.forEach(channel => { queries.forEach(query => { queryParams += '&' + encodeURIComponent(channel) + '[' + slot + '][]=' + encodeURIComponent(query); }); }); }); const url = this.config.endpointRealtime + '/realtime?' + queryParams; if (url !== this.realtime.url || // Check if URL is present !this.realtime.socket || // Check if WebSocket has not been created ((_c = this.realtime.socket) === null || _c === void 0 ? void 0 : _c.readyState) > WebSocket.OPEN // Check if WebSocket is CLOSING (3) or CLOSED (4) ) { if (this.realtime.socket && ((_d = this.realtime.socket) === null || _d === void 0 ? void 0 : _d.readyState) < WebSocket.CLOSING // Close WebSocket if it is CONNECTING (0) or OPEN (1) ) { this.realtime.reconnect = false; this.realtime.socket.close(); } this.realtime.url = url; this.realtime.socket = new WebSocket(url); this.realtime.socket.addEventListener('message', this.realtime.onMessage); this.realtime.socket.addEventListener('open', _event => { this.realtime.reconnectAttempts = 0; this.realtime.createHeartbeat(); }); this.realtime.socket.addEventListener('close', event => { var _a, _b, _c; if (!this.realtime.reconnect || (((_b = (_a = this.realtime) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.type) === 'error' && // Check if last message was of type error ((_c = this.realtime) === null || _c === void 0 ? void 0 : _c.lastMessage.data).code === 1008 // Check for policy violation 1008 )) { this.realtime.reconnect = true; return; } const timeout = this.realtime.getTimeout(); console.error(`Realtime got disconnected. Reconnect will be attempted in ${timeout / 1000} seconds.`, event.reason); setTimeout(() => { this.realtime.reconnectAttempts++; this.realtime.createSocket(); }, timeout); }); } }, onMessage: (event) => { var _a, _b; try { const message = JSONbig.parse(event.data); this.realtime.lastMessage = message; switch (message.type) { case 'connected': { const messageData = message.data; if (messageData === null || messageData === void 0 ? void 0 : messageData.subscriptions) { this.realtime.slotToSubscriptionId.clear(); this.realtime.subscriptionIdToSlot.clear(); for (const [slotStr, subscriptionId] of Object.entries(messageData.subscriptions)) { const slot = Number(slotStr); if (!isNaN(slot) && typeof subscriptionId === 'string') { this.realtime.slotToSubscriptionId.set(slot, subscriptionId); this.realtime.subscriptionIdToSlot.set(subscriptionId, slot); } } } let session = this.config.session; if (!session) { const cookie = JSONbig.parse((_a = window.localStorage.getItem('cookieFallback')) !== null && _a !== void 0 ? _a : '{}'); session = cookie === null || cookie === void 0 ? void 0 : cookie[`a_session_${this.config.project}`]; } if (session && !(messageData === null || messageData === void 0 ? void 0 : messageData.user)) { (_b = this.realtime.socket) === null || _b === void 0 ? void 0 : _b.send(JSONbig.stringify({ type: 'authentication', data: { session } })); } break; } case 'event': { const data = message.data; if (!(data === null || data === void 0 ? void 0 : data.channels)) break; const eventSubIds = data.subscriptions; if (eventSubIds && eventSubIds.length > 0) { for (const subscriptionId of eventSubIds) { const slot = this.realtime.subscriptionIdToSlot.get(subscriptionId); if (slot !== undefined) { const subscription = this.realtime.subscriptions.get(slot); if (subscription) { setTimeout(() => subscription.callback(data)); } } } } else { const isSubscribed = data.channels.some(channel => this.realtime.channels.has(channel)); if (!isSubscribed) break; this.realtime.subscriptions.forEach(subscription => { if (data.channels.some(channel => subscription.channels.includes(channel))) { setTimeout(() => subscription.callback(data)); } }); } break; } case 'pong': break; // Handle pong response if needed case 'error': throw message.data; default: break; } } catch (e) { console.error(e); } }, cleanUp: (channels, queries) => { this.realtime.channels.forEach(channel => { if (channels.includes(channel)) { let found = Array.from(this.realtime.subscriptions).some(([_key, subscription]) => { return subscription.channels.includes(channel); }); if (!found) { this.realtime.channels.delete(channel); } } }); this.realtime.queries.forEach(query => { if (queries.includes(query)) { let found = Array.from(this.realtime.subscriptions).some(([_key, subscription]) => { var _a; return (_a = subscription.queries) === null || _a === void 0 ? void 0 : _a.includes(query); }); if (!found) { this.realtime.queries.delete(query); } } }); } }; } /** * Set Endpoint * * Your project endpoint * * @param {string} endpoint * * @returns {this} */ setEndpoint(endpoint) { if (!endpoint || typeof endpoint !== 'string') { throw new AppwriteException('Endpoint must be a valid string'); } if (!endpoint.startsWith('http://') && !endpoint.startsWith('https://')) { throw new AppwriteException('Invalid endpoint URL: ' + endpoint); } this.config.endpoint = endpoint; this.config.endpointRealtime = endpoint.replace('https://', 'wss://').replace('http://', 'ws://'); return this; } /** * Set Realtime Endpoint * * @param {string} endpointRealtime * * @returns {this} */ setEndpointRealtime(endpointRealtime) { if (!endpointRealtime || typeof endpointRealtime !== 'string') { throw new AppwriteException('Endpoint must be a valid string'); } if (!endpointRealtime.startsWith('ws://') && !endpointRealtime.startsWith('wss://')) { throw new AppwriteException('Invalid realtime endpoint URL: ' + endpointRealtime); } this.config.endpointRealtime = endpointRealtime; return this; } /** * Set Project * * Your project ID * * @param value string * * @return {this} */ setProject(value) { this.headers['X-Appwrite-Project'] = value; this.config.project = value; return this; } /** * Set JWT * * Your secret JSON Web Token * * @param value string * * @return {this} */ setJWT(value) { this.headers['X-Appwrite-JWT'] = value; this.config.jwt = value; return this; } /** * Set Locale * * @param value string * * @return {this} */ setLocale(value) { this.headers['X-Appwrite-Locale'] = value; this.config.locale = value; return this; } /** * Set Session * * The user session to authenticate with * * @param value string * * @return {this} */ setSession(value) { this.headers['X-Appwrite-Session'] = value; this.config.session = value; return this; } /** * Set DevKey * * Your secret dev API key * * @param value string * * @return {this} */ setDevKey(value) { this.headers['X-Appwrite-Dev-Key'] = value; this.config.devkey = value; return this; } /** * Subscribes to Appwrite events and passes you the payload in realtime. * * @deprecated Use the Realtime service instead. * @see Realtime * * @param {string|string[]|Channel<any>|ActionableChannel|ResolvedChannel|(Channel<any>|ActionableChannel|ResolvedChannel)[]} channels * Channel to subscribe - pass a single channel as a string or Channel builder instance, or multiple with an array. * * Possible channels are: * - account * - collections * - collections.[ID] * - collections.[ID].documents * - documents * - documents.[ID] * - files * - files.[ID] * - executions * - executions.[ID] * - functions.[ID] * - teams * - teams.[ID] * - memberships * - memberships.[ID] * * You can also use Channel builders: * - Channel.database('db').collection('col').document('doc').create() * - Channel.bucket('bucket').file('file').update() * - Channel.function('func').execution('exec').delete() * - Channel.team('team').create() * - Channel.membership('membership').update() * @param {(payload: RealtimeMessage) => void} callback Is called on every realtime update. * @returns {() => void} Unsubscribes from events. */ subscribe(channels, callback, queries = []) { const channelArray = Array.isArray(channels) ? channels : [channels]; // Convert Channel instances to strings const channelStrings = channelArray.map(ch => { if (typeof ch === 'string') { return ch; } // All Channel instances have toString() method if (ch && typeof ch.toString === 'function') { return ch.toString(); } // Fallback to generic string conversion return String(ch); }); channelStrings.forEach(channel => this.realtime.channels.add(channel)); const queryStrings = (queries !== null && queries !== void 0 ? queries : []).map(q => typeof q === 'string' ? q : q.toString()); queryStrings.forEach(query => this.realtime.queries.add(query)); const counter = this.realtime.subscriptionsCounter++; this.realtime.subscriptions.set(counter, { channels: channelStrings, queries: queryStrings, callback }); this.realtime.connect(); return () => { this.realtime.subscriptions.delete(counter); this.realtime.cleanUp(channelStrings, queryStrings); this.realtime.connect(); }; } prepareRequest(method, url, headers = {}, params = {}) { method = method.toUpperCase(); headers = Object.assign({}, this.headers, headers); if (typeof window !== 'undefined' && window.localStorage) { const cookieFallback = window.localStorage.getItem('cookieFallback'); if (cookieFallback) { headers['X-Fallback-Cookies'] = cookieFallback; } } let options = { method, headers, }; if (headers['X-Appwrite-Dev-Key'] === undefined) { options.credentials = 'include'; } if (method === 'GET') { for (const [key, value] of Object.entries(Client.flatten(params))) { url.searchParams.append(key, value); } } else { switch (headers['content-type']) { case 'application/json': options.body = JSONbig.stringify(params); break; case 'multipart/form-data': const formData = new FormData(); for (const [key, value] of Object.entries(params)) { if (value instanceof File) { formData.append(key, value, value.name); } else if (Array.isArray(value)) { for (const nestedValue of value) { formData.append(`${key}[]`, nestedValue); } } else { formData.append(key, value); } } options.body = formData; delete headers['content-type']; break; } } return { uri: url.toString(), options }; } chunkedUpload(method, url, headers = {}, originalPayload = {}, onProgress) { var _a; return __awaiter(this, void 0, void 0, function* () { const [fileParam, file] = (_a = Object.entries(originalPayload).find(([_, value]) => value instanceof File)) !== null && _a !== void 0 ? _a : []; if (!file || !fileParam) { throw new Error('File not found in payload'); } if (file.size <= Client.CHUNK_SIZE) { return yield this.call(method, url, headers, originalPayload); } let start = 0; let response = null; while (start < file.size) { let end = start + Client.CHUNK_SIZE; // Prepare end for the next chunk if (end >= file.size) { end = file.size; // Adjust for the last chunk to include the last byte } headers['content-range'] = `bytes ${start}-${end - 1}/${file.size}`; const chunk = file.slice(start, end); let payload = Object.assign({}, originalPayload); payload[fileParam] = new File([chunk], file.name); response = yield this.call(method, url, headers, payload); if (onProgress && typeof onProgress === 'function') { onProgress({ $id: response.$id, progress: Math.round((end / file.size) * 100), sizeUploaded: end, chunksTotal: Math.ceil(file.size / Client.CHUNK_SIZE), chunksUploaded: Math.ceil(end / Client.CHUNK_SIZE) }); } if (response && response.$id) { headers['x-appwrite-id'] = response.$id; } start = end; } return response; }); } ping() { return __awaiter(this, void 0, void 0, function* () { return this.call('GET', new URL(this.config.endpoint + '/ping')); }); } call(method, url, headers = {}, params = {}, responseType = 'json') { var _a, _b; return __awaiter(this, void 0, void 0, function* () { const { uri, options } = this.prepareRequest(method, url, headers, params); let data = null; const response = yield fetch(uri, options); // type opaque: No-CORS, different-origin response (CORS-issue) if (response.type === 'opaque') { throw new AppwriteException(`Invalid Origin. Register your new client (${window.location.host}) as a new Web platform on your project console dashboard`, 403, "forbidden", ""); } const warnings = response.headers.get('x-appwrite-warning'); if (warnings) { warnings.split(';').forEach((warning) => console.warn('Warning: ' + warning)); } if ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) { data = JSONbig.parse(yield response.text()); } else if (responseType === 'arrayBuffer') { data = yield response.arrayBuffer(); } else { data = { message: yield response.text() }; } if (400 <= response.status) { let responseText = ''; if (((_b = response.headers.get('content-type')) === null || _b === void 0 ? void 0 : _b.includes('application/json')) || responseType === 'arrayBuffer') { responseText = JSONbig.stringify(data); } else { responseText = data === null || data === void 0 ? void 0 : data.message; } throw new AppwriteException(data === null || data === void 0 ? void 0 : data.message, response.status, data === null || data === void 0 ? void 0 : data.type, responseText); } const cookieFallback = response.headers.get('X-Fallback-Cookies'); if (typeof window !== 'undefined' && window.localStorage && cookieFallback) { window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.'); window.localStorage.setItem('cookieFallback', cookieFallback); } return data; }); } static flatten(data, prefix = '') { let output = {}; for (const [key, value] of Object.entries(data)) { let finalKey = prefix ? prefix + '[' + key + ']' : key; if (Array.isArray(value)) { output = Object.assign(Object.assign({}, output), Client.flatten(value, finalKey)); } else { output[finalKey] = value; } } return output; } } Client.CHUNK_SIZE = 1024 * 1024 * 5; class Service { constructor(client) { this.client = client; } static flatten(data, prefix = '') { let output = {}; for (const [key, value] of Object.entries(data)) { let finalKey = prefix ? prefix + '[' + key + ']' : key; if (Array.isArray(value)) { output = Object.assign(Object.assign({}, output), Service.flatten(value, finalKey)); } else { output[finalKey] = value; } } return output; } } /** * The size for chunked uploads in bytes. */ Service.CHUNK_SIZE = 5 * 1024 * 1024; // 5MB class Account { constructor(client) { this.client = client; } /** * Get the currently logged in user. * * @throws {AppwriteException} * @returns {Promise<Models.User<Preferences>>} */ get() { const apiPath = '/account'; const payload = {}; const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = {}; return this.client.call('get', uri, apiHeaders, payload); } create(paramsOrFirst, ...rest) { let params; if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { userId: paramsOrFirst, email: rest[0], password: rest[1], name: rest[2] }; } const userId = params.userId; const email = params.email; const password = params.password; const name = params.name; if (typeof userId === 'undefined') { throw new AppwriteException('Missing required parameter: "userId"'); } if (typeof email === 'undefined') { throw new AppwriteException('Missing required parameter: "email"'); } if (typeof password === 'undefined') { throw new AppwriteException('Missing required parameter: "password"'); } const apiPath = '/account'; const payload = {}; if (typeof userId !== 'undefined') { payload['userId'] = userId; } if (typeof email !== 'undefined') { payload['email'] = email; } if (typeof password !== 'undefined') { payload['password'] = password; } if (typeof name !== 'undefined') { payload['name'] = name; } const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = { 'content-type': 'application/json', }; return this.client.call('post', uri, apiHeaders, payload); } updateEmail(paramsOrFirst, ...rest) { let params; if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { email: paramsOrFirst, password: rest[0] }; } const email = params.email; const password = params.password; if (typeof email === 'undefined') { throw new AppwriteException('Missing required parameter: "email"'); } if (typeof password === 'undefined') { throw new AppwriteException('Missing required parameter: "password"'); } const apiPath = '/account/email'; const payload = {}; if (typeof email !== 'undefined') { payload['email'] = email; } if (typeof password !== 'undefined') { payload['password'] = password; } const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = { 'content-type': 'application/json', }; return this.client.call('patch', uri, apiHeaders, payload); } listIdentities(paramsOrFirst, ...rest) { let params; if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { queries: paramsOrFirst, total: rest[0] }; } const queries = params.queries; const total = params.total; const apiPath = '/account/identities'; const payload = {}; if (typeof queries !== 'undefined') { payload['queries'] = queries; } if (typeof total !== 'undefined') { payload['total'] = total; } const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = {}; return this.client.call('get', uri, apiHeaders, payload); } deleteIdentity(paramsOrFirst) { let params; if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { identityId: paramsOrFirst }; } const identityId = params.identityId; if (typeof identityId === 'undefined') { throw new AppwriteException('Missing required parameter: "identityId"'); } const apiPath = '/account/identities/{identityId}'.replace('{identityId}', identityId); const payload = {}; const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = { 'content-type': 'application/json', }; return this.client.call('delete', uri, apiHeaders, payload); } createJWT(paramsOrFirst) { let params; if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { duration: paramsOrFirst }; } const duration = params.duration; const apiPath = '/account/jwts'; const payload = {}; if (typeof duration !== 'undefined') { payload['duration'] = duration; } const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = { 'content-type': 'application/json', }; return this.client.call('post', uri, apiHeaders, payload); } listLogs(paramsOrFirst, ...rest) { let params; if (!paramsOrFirst || (paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { queries: paramsOrFirst, total: rest[0] }; } const queries = params.queries; const total = params.total; const apiPath = '/account/logs'; const payload = {}; if (typeof queries !== 'undefined') { payload['queries'] = queries; } if (typeof total !== 'undefined') { payload['total'] = total; } const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = {}; return this.client.call('get', uri, apiHeaders, payload); } updateMFA(paramsOrFirst) { let params; if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { mfa: paramsOrFirst }; } const mfa = params.mfa; if (typeof mfa === 'undefined') { throw new AppwriteException('Missing required parameter: "mfa"'); } const apiPath = '/account/mfa'; const payload = {}; if (typeof mfa !== 'undefined') { payload['mfa'] = mfa; } const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = { 'content-type': 'application/json', }; return this.client.call('patch', uri, apiHeaders, payload); } createMfaAuthenticator(paramsOrFirst) { let params; if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst) && ('type' in paramsOrFirst))) { params = (paramsOrFirst || {}); } else { params = { type: paramsOrFirst }; } const type = params.type; if (typeof type === 'undefined') { throw new AppwriteException('Missing required parameter: "type"'); } const apiPath = '/account/mfa/authenticators/{type}'.replace('{type}', type); const payload = {}; const uri = new URL(this.client.config.endpoint + apiPath); const apiHeaders = {