UNPKG

@dittolive/ditto

Version:

Ditto is a cross-platform SDK that allows apps to sync with and even without internet connectivity.

1,027 lines (1,018 loc) 458 kB
'use strict'; // // Copyright © 2021 DittoLive Incorporated. All rights reserved. // /** @internal */ class KeepAlive { /** @internal */ get isActive() { return this.intervalID !== null; } /** @internal */ constructor() { this.countsByID = {}; this.intervalID = null; } /** @internal */ retain(id) { if (typeof this.countsByID[id] === 'undefined') this.countsByID[id] = 0; this.countsByID[id] += 1; if (this.intervalID === null) { // Keep the process alive as long as there is at least one ID being // tracked by setting a time interval with the maximum delay. This will // prevent the process from exiting. const maxDelay = 2147483647; // Signed 32 bit integer, see docs: https://developer.mozilla.org/en-US/docs/Web/API/setInterval this.intervalID = setInterval(() => { /* no-op */ }, maxDelay); KeepAlive.finalizationRegistry.register(this, this.intervalID, this); } } /** @internal */ release(id) { if (typeof this.countsByID[id] === 'undefined') { throw new Error(`Internal inconsistency, trying to release a keep-alive ID that hasn't been retained before or isn't tracked anymore: ${id}`); } this.countsByID[id] -= 1; if (this.countsByID[id] === 0) delete this.countsByID[id]; if (Object.keys(this.countsByID).length === 0) { // Nothing is tracked anymore, it's safe to clear the interval // and let the process do what it wants. KeepAlive.finalizationRegistry.unregister(this); clearInterval(this.intervalID); this.intervalID = null; } } /** @internal */ currentIDs() { return Object.keys(this.countsByID); } /** @internal */ countForID(id) { var _a; return (_a = this.countsByID[id]) !== null && _a !== void 0 ? _a : null; } } KeepAlive.finalizationRegistry = new FinalizationRegistry(clearInterval); // // Copyright © 2021 DittoLive Incorporated. All rights reserved. // /** * Generic observer handle returned by various observation APIs. The observation * remains active until the {@link stop | stop()} method is called explicitly or * the observer instance is garbage collected. Therefore, to keep the observation * alive, you have to keep a reference to the corresponding observer. */ class Observer { /** @internal */ get token() { return this._token; } /** @internal */ constructor(observerManager, token, options = {}) { this.observerManager = observerManager; this._token = token; this.options = options; if (options.stopsWhenFinalized) { Observer.finalizationRegistry.register(this, { observerManager, token }, this); } } /** * Returns `true` if the observer has been explicitly stopped via the `stop()` * method. Otherwise returns `false`. */ get isStopped() { return (this.token !== undefined && this.observerManager.hasObserver(this.token)); } /** * Stops the observation. Calling this method multiple times has no effect. */ stop() { const token = this.token; if (token) { delete this._token; Observer.finalizationRegistry.unregister(this); this.observerManager.removeObserver(token); } } static finalize(observerManagerAndToken) { const { observerManager, token } = observerManagerAndToken; observerManager.removeObserver(token); } } Observer.finalizationRegistry = new FinalizationRegistry(Observer.finalize); // // Copyright © 2021 DittoLive Incorporated. All rights reserved. // // NOTE: we use a token to detect private invocation of the constructor. This is // not secure and just to prevent accidental private invocation on the client // side. const privateToken$1 = Symbol('privateConstructorToken'); /** * Represents a CRDT counter that can be upserted as part of a document or * assigned to a property during an update of a document. * * Not available in React Native environments. */ class Counter { /** The value of the counter. */ get value() { return this._value; } /** * Creates a new counter that can be used as part of a document's content. */ constructor() { this._value = 0.0; } /** @internal */ static '@ditto.create'(mutDoc, path, value) { // @ts-expect-error - using hidden argument const counter = mutDoc ? new MutableCounter(privateToken$1) : new Counter(); counter.mutDoc = mutDoc; counter.path = path; counter._value = value; return counter; } } // ----------------------------------------------------------------------------- /** * Represents a mutable CRDT counter that can be incremented by a specific * amount while updating a document. * * This class can't be instantiated directly, it's returned automatically for * any counter property within an update block via {@link MutableDocumentPath.counter}. */ class MutableCounter extends Counter { /** * Increments the counter by `amount`, which can be any valid number. * * Only valid within the `update` closure of * {@link PendingCursorOperation.update | PendingCursorOperation.update()} and * {@link PendingIDSpecificOperation.update | PendingIDSpecificOperation.update()}, * otherwise an exception is thrown. * * @throws {Error} when called in a React Native environment. */ increment(amount) { const mutDoc = this.mutDoc; const path = this.path; if (!mutDoc) { throw new Error(`Can't increment counter, only possible within the closure of a collection's update() method.`); } mutDoc.at(path)['@ditto.increment'](amount); // We also increment the local value to make sure that the change is // reflected locally as well as in the underlying document this._value += amount; } /** @internal */ constructor() { if (arguments[0] === privateToken$1) super(); else throw new Error(`MutableCounter constructor is for internal use only.`); } } // // Copyright © 2022 DittoLive Incorporated. All rights reserved. // // NOTE: we use a token to detect private invocation of the constructor. This is // not secure and just to prevent accidental private invocation on the client // side. const privateToken = '@ditto.ff82dae89821c5ab822a8b539056bce4'; /** * Represents a CRDT register that can be upserted as part of a document or * assigned to a property during an update of a document. * * Not available in React Native environments. */ class Register { /** Returns the value of the register. */ get value() { return this['@ditto.value']; } /** * Creates a new Register that can be used as part of a document's content. */ constructor(value) { this['@ditto.value'] = value; } /** @internal */ static '@ditto.create'(mutableDocument, path, value) { const register = mutableDocument ? new MutableRegister(value, privateToken) : new Register(value); register['@ditto.mutableDocument'] = mutableDocument; register['@ditto.path'] = path; register['@ditto.value'] = value; return register; } } // ----------------------------------------------------------------------------- /** * Represents a mutable CRDT register that can be set to a specific value when * updating a document. * * This class can't be instantiated directly, it's returned automatically for * any register property of a document within an update block via * {@link MutableDocumentPath.register}. * * Not available in React Native environments. */ class MutableRegister extends Register { /** * Returns the value of the register. * * Not available in React Native environments. */ get value() { return super.value; } /** * Convenience setter, equivalent to {@link set | set()}. * * Not available in React Native environments. */ set value(value) { this.set(value); } /** * Sets the register to the provided value. * * Only valid within the `update` closure of * {@link PendingCursorOperation.update | PendingCursorOperation.update()} and * {@link PendingIDSpecificOperation.update | PendingIDSpecificOperation.update()}, * otherwise an exception is thrown. * * Not available in React Native environments. */ set(value) { const mutableDocument = this['@ditto.mutableDocument']; const path = this['@ditto.path']; mutableDocument.at(path)['@ditto.set'](value); // We also set the local value to make sure that the change is // reflected locally as well as in the underlying document. this['@ditto.value'] = value; } /** @internal */ constructor(value) { if (arguments[1] === privateToken) super(value); else throw new Error(`MutableRegister constructor is for internal use only.`); } } const ditto = (function () { // IMPORTANT: most packagers perform _static_ analysis to identity native // Node modules to copy them into the right directory within the // final package. This only works if we use constant strings with // require(). Therefore, we use an if for all supported targets and // explicitly require each instead of dynamically building the require // path. const target = process.platform + '-' + process.arch; try { if (target === 'darwin-x64') return require('./ditto.darwin-x64.node') if (target === 'darwin-arm64') return require('./ditto.darwin-arm64.node') if (target === 'linux-x64') return require('./ditto.linux-x64.node') if (target === 'linux-arm64') return require('./ditto.linux-arm64.node') if (target === 'win32-x64') return require('./ditto.win32-x64.node') } catch (error) { throw new Error("Couldn't load native module 'ditto." + target + ".node' due to error:" + error.toString()) } throw new Error("No native module 'ditto." + target + ".node' found. Please check the Ditto documentation for supported platforms.") })(); function boxCBytesIntoBuffer(...args) { return ditto.boxCBytesIntoBuffer(...args) } function boxCStringIntoString(...args) { return ditto.boxCStringIntoString(...args) } function cStringVecToStringArray(...args) { return ditto.cStringVecToStringArray(...args) } function ditto_add_subscription(...args) { return ditto.ditto_add_subscription(...args) } function ditto_auth_client_get_app_id(...args) { return ditto.ditto_auth_client_get_app_id(...args) } function ditto_auth_client_get_site_id(...args) { return ditto.ditto_auth_client_get_site_id(...args) } function ditto_auth_client_is_web_valid(...args) { return ditto.ditto_auth_client_is_web_valid(...args) } function ditto_auth_client_login_with_credentials(...args) { return ditto.ditto_auth_client_login_with_credentials(...args) } function ditto_auth_client_login_with_token(...args) { return ditto.ditto_auth_client_login_with_token(...args) } function ditto_auth_client_login_with_token_and_feedback(...args) { return ditto.ditto_auth_client_login_with_token_and_feedback(...args) } function ditto_auth_client_logout(...args) { return ditto.ditto_auth_client_logout(...args) } function ditto_auth_client_make_login_provider(...args) { return ditto.ditto_auth_client_make_login_provider(...args) } function ditto_auth_client_user_id(...args) { return ditto.ditto_auth_client_user_id(...args) } function ditto_auth_set_login_provider(...args) { return ditto.ditto_auth_set_login_provider(...args) } function ditto_cancel_resolve_attachment(...args) { return ditto.ditto_cancel_resolve_attachment(...args) } function ditto_clear_presence_callback(...args) { return ditto.ditto_clear_presence_callback(...args) } function ditto_clear_presence_v3_callback(...args) { return ditto.ditto_clear_presence_v3_callback(...args) } function ditto_collection_evict(...args) { return ditto.ditto_collection_evict(...args) } function ditto_collection_evict_query_str(...args) { return ditto.ditto_collection_evict_query_str(...args) } function ditto_collection_exec_query_str(...args) { return ditto.ditto_collection_exec_query_str(...args) } function ditto_collection_get(...args) { return ditto.ditto_collection_get(...args) } function ditto_collection_get_with_write_transaction(...args) { return ditto.ditto_collection_get_with_write_transaction(...args) } function ditto_collection_insert_value(...args) { return ditto.ditto_collection_insert_value(...args) } function ditto_collection_remove(...args) { return ditto.ditto_collection_remove(...args) } function ditto_collection_remove_query_str(...args) { return ditto.ditto_collection_remove_query_str(...args) } function ditto_collection_update(...args) { return ditto.ditto_collection_update(...args) } function ditto_collection_update_multiple(...args) { return ditto.ditto_collection_update_multiple(...args) } function ditto_disable_sync_with_v3(...args) { return ditto.ditto_disable_sync_with_v3(...args) } function ditto_document_free(...args) { return ditto.ditto_document_free(...args) } function ditto_document_get_cbor_with_path_type(...args) { return ditto.ditto_document_get_cbor_with_path_type(...args) } function ditto_document_id(...args) { return ditto.ditto_document_id(...args) } function ditto_document_id_query_compatible(...args) { return ditto.ditto_document_id_query_compatible(...args) } function ditto_document_increment_counter(...args) { return ditto.ditto_document_increment_counter(...args) } function ditto_document_remove(...args) { return ditto.ditto_document_remove(...args) } function ditto_document_set_cbor(...args) { return ditto.ditto_document_set_cbor(...args) } function ditto_document_set_cbor_with_timestamp(...args) { return ditto.ditto_document_set_cbor_with_timestamp(...args) } function ditto_documents_hash(...args) { return ditto.ditto_documents_hash(...args) } function ditto_documents_hash_mnemonic(...args) { return ditto.ditto_documents_hash_mnemonic(...args) } function ditto_error_message(...args) { return ditto.ditto_error_message(...args) } function ditto_free(...args) { return ditto.ditto_free(...args) } function ditto_free_attachment_handle(...args) { return ditto.ditto_free_attachment_handle(...args) } function ditto_get_collection_names(...args) { return ditto.ditto_get_collection_names(...args) } function ditto_get_complete_attachment_path(...args) { return ditto.ditto_get_complete_attachment_path(...args) } function ditto_get_sdk_version(...args) { return ditto.ditto_get_sdk_version(...args) } function ditto_identity_config_make_manual_v0(...args) { return ditto.ditto_identity_config_make_manual_v0(...args) } function ditto_identity_config_make_offline_playground(...args) { return ditto.ditto_identity_config_make_offline_playground(...args) } function ditto_identity_config_make_online_playground(...args) { return ditto.ditto_identity_config_make_online_playground(...args) } function ditto_identity_config_make_online_with_authentication(...args) { return ditto.ditto_identity_config_make_online_with_authentication(...args) } function ditto_identity_config_make_shared_key(...args) { return ditto.ditto_identity_config_make_shared_key(...args) } function ditto_init_sdk_version(...args) { return ditto.ditto_init_sdk_version(...args) } function ditto_live_query_register_str_detached(...args) { return ditto.ditto_live_query_register_str_detached(...args) } function ditto_live_query_signal_available_next(...args) { return ditto.ditto_live_query_signal_available_next(...args) } function ditto_live_query_start(...args) { return ditto.ditto_live_query_start(...args) } function ditto_live_query_stop(...args) { return ditto.ditto_live_query_stop(...args) } function ditto_log(...args) { return ditto.ditto_log(...args) } function ditto_logger_emoji_headings_enabled(...args) { return ditto.ditto_logger_emoji_headings_enabled(...args) } function ditto_logger_emoji_headings_enabled_get(...args) { return ditto.ditto_logger_emoji_headings_enabled_get(...args) } function ditto_logger_enabled(...args) { return ditto.ditto_logger_enabled(...args) } function ditto_logger_enabled_get(...args) { return ditto.ditto_logger_enabled_get(...args) } function ditto_logger_init(...args) { return ditto.ditto_logger_init(...args) } function ditto_logger_minimum_log_level(...args) { return ditto.ditto_logger_minimum_log_level(...args) } function ditto_logger_minimum_log_level_get(...args) { return ditto.ditto_logger_minimum_log_level_get(...args) } function ditto_logger_set_custom_log_cb(...args) { return ditto.ditto_logger_set_custom_log_cb(...args) } function ditto_logger_set_log_file(...args) { return ditto.ditto_logger_set_log_file(...args) } function ditto_new_attachment_from_bytes(...args) { return ditto.ditto_new_attachment_from_bytes(...args) } function ditto_new_attachment_from_file(...args) { return ditto.ditto_new_attachment_from_file(...args) } function ditto_presence_v1(...args) { return ditto.ditto_presence_v1(...args) } function ditto_presence_v3(...args) { return ditto.ditto_presence_v3(...args) } function ditto_read_transaction(...args) { return ditto.ditto_read_transaction(...args) } function ditto_read_transaction_free(...args) { return ditto.ditto_read_transaction_free(...args) } function ditto_register_presence_v1_callback(...args) { return ditto.ditto_register_presence_v1_callback(...args) } function ditto_register_presence_v3_callback(...args) { return ditto.ditto_register_presence_v3_callback(...args) } function ditto_register_transport_condition_changed_callback(...args) { return ditto.ditto_register_transport_condition_changed_callback(...args) } function ditto_remove_subscription(...args) { return ditto.ditto_remove_subscription(...args) } function ditto_resolve_attachment(...args) { return ditto.ditto_resolve_attachment(...args) } function ditto_run_garbage_collection(...args) { return ditto.ditto_run_garbage_collection(...args) } function ditto_sdk_transports_error_free(...args) { return ditto.ditto_sdk_transports_error_free(...args) } function ditto_sdk_transports_error_new(...args) { return ditto.ditto_sdk_transports_error_new(...args) } function ditto_sdk_transports_error_value(...args) { return ditto.ditto_sdk_transports_error_value(...args) } function ditto_sdk_transports_init(...args) { return ditto.ditto_sdk_transports_init(...args) } function ditto_set_device_name(...args) { return ditto.ditto_set_device_name(...args) } function ditto_shutdown(...args) { return ditto.ditto_shutdown(...args) } function ditto_small_peer_info_get_is_enabled(...args) { return ditto.ditto_small_peer_info_get_is_enabled(...args) } function ditto_small_peer_info_get_metadata(...args) { return ditto.ditto_small_peer_info_get_metadata(...args) } function ditto_small_peer_info_get_sync_scope(...args) { return ditto.ditto_small_peer_info_get_sync_scope(...args) } function ditto_small_peer_info_set_enabled(...args) { return ditto.ditto_small_peer_info_set_enabled(...args) } function ditto_small_peer_info_set_metadata(...args) { return ditto.ditto_small_peer_info_set_metadata(...args) } function ditto_small_peer_info_set_sync_scope(...args) { return ditto.ditto_small_peer_info_set_sync_scope(...args) } function ditto_validate_document_id(...args) { return ditto.ditto_validate_document_id(...args) } function ditto_write_transaction(...args) { return ditto.ditto_write_transaction(...args) } function ditto_write_transaction_commit(...args) { return ditto.ditto_write_transaction_commit(...args) } function ditto_write_transaction_rollback(...args) { return ditto.ditto_write_transaction_rollback(...args) } function dittoffi_authentication_status_free(...args) { return ditto.dittoffi_authentication_status_free(...args) } function dittoffi_authentication_status_is_authenticated(...args) { return ditto.dittoffi_authentication_status_is_authenticated(...args) } function dittoffi_authentication_status_user_id(...args) { return ditto.dittoffi_authentication_status_user_id(...args) } function dittoffi_base64_encode(...args) { return ditto.dittoffi_base64_encode(...args) } function dittoffi_connection_request_authorize(...args) { return ditto.dittoffi_connection_request_authorize(...args) } function dittoffi_connection_request_connection_type(...args) { return ditto.dittoffi_connection_request_connection_type(...args) } function dittoffi_connection_request_free(...args) { return ditto.dittoffi_connection_request_free(...args) } function dittoffi_connection_request_identity_service_metadata_json(...args) { return ditto.dittoffi_connection_request_identity_service_metadata_json(...args) } function dittoffi_connection_request_peer_key_string(...args) { return ditto.dittoffi_connection_request_peer_key_string(...args) } function dittoffi_connection_request_peer_metadata_json(...args) { return ditto.dittoffi_connection_request_peer_metadata_json(...args) } function dittoffi_crypto_generate_secure_random_token(...args) { return ditto.dittoffi_crypto_generate_secure_random_token(...args) } function dittoffi_differ_diff(...args) { return ditto.dittoffi_differ_diff(...args) } function dittoffi_differ_free(...args) { return ditto.dittoffi_differ_free(...args) } function dittoffi_differ_new(...args) { return ditto.dittoffi_differ_new(...args) } function dittoffi_ditto_is_activated(...args) { return ditto.dittoffi_ditto_is_activated(...args) } function dittoffi_ditto_is_sync_active(...args) { return ditto.dittoffi_ditto_is_sync_active(...args) } function dittoffi_ditto_set_authentication_status_handler(...args) { return ditto.dittoffi_ditto_set_authentication_status_handler(...args) } function dittoffi_ditto_set_cloud_sync_enabled(...args) { return ditto.dittoffi_ditto_set_cloud_sync_enabled(...args) } function dittoffi_ditto_stop_sync(...args) { return ditto.dittoffi_ditto_stop_sync(...args) } function dittoffi_ditto_transport_config(...args) { return ditto.dittoffi_ditto_transport_config(...args) } function dittoffi_ditto_try_new_blocking(...args) { return ditto.dittoffi_ditto_try_new_blocking(...args) } function dittoffi_ditto_try_set_transport_config(...args) { return ditto.dittoffi_ditto_try_set_transport_config(...args) } function dittoffi_ditto_try_start_sync(...args) { return ditto.dittoffi_ditto_try_start_sync(...args) } function dittoffi_error_code(...args) { return ditto.dittoffi_error_code(...args) } function dittoffi_error_description(...args) { return ditto.dittoffi_error_description(...args) } function dittoffi_error_free(...args) { return ditto.dittoffi_error_free(...args) } function dittoffi_get_sdk_semver(...args) { return ditto.dittoffi_get_sdk_semver(...args) } function dittoffi_logger_try_export_to_file_async(...args) { return ditto.dittoffi_logger_try_export_to_file_async(...args) } function dittoffi_presence_peer_metadata_json(...args) { return ditto.dittoffi_presence_peer_metadata_json(...args) } function dittoffi_presence_set_connection_request_handler(...args) { return ditto.dittoffi_presence_set_connection_request_handler(...args) } function dittoffi_presence_try_set_peer_metadata_json(...args) { return ditto.dittoffi_presence_try_set_peer_metadata_json(...args) } function dittoffi_query_result_free(...args) { return ditto.dittoffi_query_result_free(...args) } function dittoffi_query_result_item_at(...args) { return ditto.dittoffi_query_result_item_at(...args) } function dittoffi_query_result_item_cbor(...args) { return ditto.dittoffi_query_result_item_cbor(...args) } function dittoffi_query_result_item_count(...args) { return ditto.dittoffi_query_result_item_count(...args) } function dittoffi_query_result_item_free(...args) { return ditto.dittoffi_query_result_item_free(...args) } function dittoffi_query_result_item_json(...args) { return ditto.dittoffi_query_result_item_json(...args) } function dittoffi_query_result_item_new(...args) { return ditto.dittoffi_query_result_item_new(...args) } function dittoffi_query_result_mutated_document_id_at(...args) { return ditto.dittoffi_query_result_mutated_document_id_at(...args) } function dittoffi_query_result_mutated_document_id_count(...args) { return ditto.dittoffi_query_result_mutated_document_id_count(...args) } function dittoffi_store_begin_transaction_async_throws(...args) { return ditto.dittoffi_store_begin_transaction_async_throws(...args) } function dittoffi_store_transactions(...args) { return ditto.dittoffi_store_transactions(...args) } function dittoffi_transaction_complete_async_throws(...args) { return ditto.dittoffi_transaction_complete_async_throws(...args) } function dittoffi_transaction_execute_async_throws(...args) { return ditto.dittoffi_transaction_execute_async_throws(...args) } function dittoffi_transaction_free(...args) { return ditto.dittoffi_transaction_free(...args) } function dittoffi_transaction_info(...args) { return ditto.dittoffi_transaction_info(...args) } function dittoffi_try_add_sync_subscription(...args) { return ditto.dittoffi_try_add_sync_subscription(...args) } function dittoffi_try_base64_decode(...args) { return ditto.dittoffi_try_base64_decode(...args) } function dittoffi_try_exec_statement(...args) { return ditto.dittoffi_try_exec_statement(...args) } function dittoffi_try_experimental_register_change_observer_str_detached(...args) { return ditto.dittoffi_try_experimental_register_change_observer_str_detached(...args) } function dittoffi_try_remove_sync_subscription(...args) { return ditto.dittoffi_try_remove_sync_subscription(...args) } function dittoffi_try_verify_license(...args) { return ditto.dittoffi_try_verify_license(...args) } function getDeadlockTimeout$1(...args) { return ditto.getDeadlockTimeout(...args) } function jsDocsToCDocs(...args) { return ditto.jsDocsToCDocs(...args) } function refCBytesIntoBuffer(...args) { return ditto.refCBytesIntoBuffer(...args) } function refCStringToString(...args) { return ditto.refCStringToString(...args) } function setDeadlockTimeout$1(...args) { return ditto.setDeadlockTimeout(...args) } function withOutBoxCBytes(...args) { return ditto.withOutBoxCBytes(...args) } // // Copyright © 2023 DittoLive Incorporated. All rights reserved. // // This internal module contains the Ditto FFI error type and helper functions // for working with it. Except for the feature flag helper, this module should // not depend on any other parts of the Ditto Javascript SDK. /** Matches a `<...>` prefix in an FFI result error message, e.g. `<dql> ...`. **/ const PREFIX_REGEX = new RegExp(/^<.*?>\s*/); /** * Represents an exception that occurred during a call into the Ditto FFI. * * Use the {@link throwOnErrorStatus | throwOnErrorStatus()} helper to * automatically throw this error when an FFI call returns with a non-zero * return value. * * @internal */ class DittoFFIError extends Error { /** * Only call this constructor after having called `FFI.ensureInitialized()` * and `FFI.trace()`. * * @param code numerical status code returned by an FFI call or an * {@link FFIResultErrorCode} for errors returned on FFI result objects * @param messageOverride overrides the thread-local error message set in * Ditto core * @param messageFallback fallback message to use if the thread-local error * message is empty */ constructor(code, messageOverride, messageFallback) { // Call `ffiErrorMessage()` even when an override is provided to ensure that // the thread-local error message is cleared. const threadLocalErrorMessage = ffiErrorMessage(); super(messageOverride || threadLocalErrorMessage || messageFallback); this.code = code; } } /** * Throws a {@link DittoFFIError} if the given `ffiError` is not `null`. * * Removes the thread-local error message from Ditto core. If an error description * is available on the given `ffiError`, it is used as the error message of the * thrown {@link DittoFFIError}. A prefix in the error message is removed, e.g. * `<dql> ...`. * * If no error description is available, the given `ffiFunctionName` is used to * produce a fallback error message. * * @internal */ function throwOnErrorResult(ffiError, ffiFunctionName) { if (ffiError !== null) { let errorCode; let errorMsg; try { errorCode = dittoffi_error_code(ffiError); errorMsg = boxCStringIntoString(dittoffi_error_description(ffiError)); dittoffi_error_free(ffiError); } catch (err) { throw new DittoFFIError(-1, `Failed to retrieve Ditto core error message: ${err.message}`); } if (errorMsg == null) { errorMsg = `${ffiFunctionName}() failed with error code: ${errorCode}`; } else { // Remove prefix from error message, e.g. `<dql> ...`. errorMsg = errorMsg.replace(PREFIX_REGEX, ''); } throw new DittoFFIError(errorCode, errorMsg); } } /** * Retrieves last thread-local error message and removes it. * * Subsequent call to this function (if no new error message has been set) will * always return `null`. * * This function is not calling `FFI.ensureInitialized()` to avoid a circular * dependency. This is okay as long as this function is only called from a * context where `FFI.ensureInitialized()` has already been called. * * @internal */ function ffiErrorMessage() { const errorMessageCString = ditto_error_message(); return boxCStringIntoString(errorMessageCString); } // // Copyright © 2023 DittoLive Incorporated. All rights reserved. // // NOTE: This is a temporary *hand-written* shim file for glue code for the // Ditto native Node module. Mid to long term, we'll either move all of this // code to the native side or generate this file via a companion CLI tool or // something equivalent. // // IMPORTANT: please leave this file self-contained and do not import any // sources from the JS SDK. /** @internal */ const DittoCRDTTypeKey = '_ditto_internal_type_jkb12973t4b'; /** @internal */ const DittoCRDTValueKey = '_value'; /** @internal */ var DittoCRDTType; (function (DittoCRDTType) { DittoCRDTType[DittoCRDTType["counter"] = 0] = "counter"; DittoCRDTType[DittoCRDTType["register"] = 1] = "register"; DittoCRDTType[DittoCRDTType["attachment"] = 2] = "attachment"; DittoCRDTType[DittoCRDTType["rga"] = 3] = "rga"; DittoCRDTType[DittoCRDTType["rwMap"] = 4] = "rwMap"; })(DittoCRDTType || (DittoCRDTType = {})); // ------------------------------------------------------------- Differ -------- function differNew() { ensureInitialized(); return dittoffi_differ_new(); } function differDiff(differ, items) { ensureInitialized(); return dittoffi_differ_diff(differ, items); } function differFree(differ) { ensureInitialized(); dittoffi_differ_free(differ); } // HACK: underlying safer-ffi doesn't equivalent a CDitto with dittoffi_store_t // although they are C aliases so we need to force it's type. /** @internal */ function dittoPointerToStorePointer(dittoHandle) { return { addr: dittoHandle.addr, type: 'dittoffi_store_t const *', }; } // ------------------------------------------------------------- Document ------ /** @internal */ function documentSetCBORWithTimestamp(document, path, cbor, timestamp) { ensureInitialized(); const pathX = bytesFromString(path); const errorCode = ditto_document_set_cbor_with_timestamp(document, pathX, cbor, timestamp); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_document_set_cbor_with_timestamp() failed with error code: ${errorCode}`); } } /** @internal */ function documentSetCBOR(document, path, cbor) { ensureInitialized(); // NOTE: not sure if this should be async or not. const pathX = bytesFromString(path); const errorCode = ditto_document_set_cbor(document, pathX, cbor); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_document_set_cbor() failed with error code: ${errorCode}`); } } /** @internal */ function documentID(self) { ensureInitialized(); // REFACTOR: add proper error handling. const documentIDX = ditto_document_id(self); return boxCBytesIntoBuffer(documentIDX); } /** @internal */ function documentGetCBORWithPathType(document, path, pathType) { ensureInitialized(); const pathBytes = bytesFromString(path); const cborPathResultRaw = ditto_document_get_cbor_with_path_type(document, pathBytes, pathType); const cborPathResult = { statusCode: cborPathResultRaw.status_code, cbor: boxCBytesIntoBuffer(cborPathResultRaw.cbor), }; return cborPathResult; } /** @internal */ function documentRemove(document, path) { ensureInitialized(); const pathBytes = bytesFromString(path); const errorCode = ditto_document_remove(document, pathBytes); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_document_remove() failed with error code: ${errorCode}`); } } /** @internal */ function documentIncrementCounter(document, path, amount) { ensureInitialized(); const pathBytes = bytesFromString(path); const errorCode = ditto_document_increment_counter(document, pathBytes, amount); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_document_increment_counter() failed with error code: ${errorCode}`); } } /** @internal */ function documentFree(self) { ensureInitialized(); // REFACTOR: add proper error handling. ditto_document_free(self); } // ----------------------------------------------------------- DocumentID ------ /** @internal */ function documentIDQueryCompatible(docID, stringPrimitiveFormat) { ensureInitialized(); const docIDString = ditto_document_id_query_compatible(docID, stringPrimitiveFormat); return boxCStringIntoString(docIDString); } /** @internal */ function validateDocumentID(docID) { ensureInitialized(); const cborCBytes = withOutBoxCBytes((outCBOR) => { const errorCode = ditto_validate_document_id(docID, outCBOR); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_validate_document_id() failed with error code: ${errorCode}`); } return outCBOR; }); return boxCBytesIntoBuffer(cborCBytes); } // ----------------------------------------------------------- Collection ------ /** @internal */ async function collectionGet(ditto, collectionName, documentID, readTransaction) { ensureInitialized(); // REFACTOR: add proper error handling. const collectionNamePointer = bytesFromString(collectionName); const { status_code: errorCode, document } = await ditto_collection_get(ditto, collectionNamePointer, documentID, readTransaction); if (errorCode === NOT_FOUND_ERROR_CODE) return null; if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_get() failed with error code: ${errorCode}`); } return document; } /** @internal */ async function collectionGetWithWriteTransaction(ditto, collectionName, documentID, writeTransaction) { ensureInitialized(); const collectionNamePointer = bytesFromString(collectionName); const { status_code: errorCode, document } = await ditto_collection_get_with_write_transaction(ditto, collectionNamePointer, documentID, writeTransaction); if (errorCode === NOT_FOUND_ERROR_CODE) return null; if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_get_with_write_transaction() failed with error code: ${errorCode}`); } return document; } /** @internal */ async function collectionInsertValue(ditto, collectionName, doc_cbor, writeStrategy, writeTransaction) { ensureInitialized(); // REFACTOR: add proper error handling. const collectionNameX = bytesFromString(collectionName); let strategy; switch (writeStrategy) { case 'merge': strategy = 'Merge'; break; case 'insertIfAbsent': strategy = 'InsertIfAbsent'; break; case 'insertDefaultIfAbsent': strategy = 'InsertDefaultIfAbsent'; break; case 'updateDifferentValues': strategy = 'UpdateDifferentValues'; break; default: throw new Error(`Unsupported write strategy '${writeStrategy}' provided.`); } const { status_code: errorCode, id } = await ditto_collection_insert_value(ditto, collectionNameX, doc_cbor, strategy, null, writeTransaction !== null && writeTransaction !== void 0 ? writeTransaction : null); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_insert_value() failed with error code: ${errorCode}`); } return boxCBytesIntoBuffer(id); } /** @internal */ async function collectionRemove(ditto, collectionName, writeTransaction, documentID) { ensureInitialized(); // REFACTOR: add proper error handling. const collectionNameX = bytesFromString(collectionName); const { status_code: errorCode, bool_value: didRemove } = await ditto_collection_remove(ditto, collectionNameX, writeTransaction, documentID); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_remove() failed with error code: ${errorCode}`); } return didRemove; } /** @internal */ async function collectionEvict(ditto, collectionName, writeTransaction, documentID) { ensureInitialized(); // REFACTOR: add proper error handling. const collectionNameX = bytesFromString(collectionName); const { status_code: errorCode, bool_value: didEvict } = await ditto_collection_evict(ditto, collectionNameX, writeTransaction, documentID); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_evict() failed with error code: ${errorCode}`); } return didEvict; } /** @internal */ async function collectionUpdate(ditto, collectionName, writeTransaction, document) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const errorCode = await ditto_collection_update(ditto, collectionNameX, writeTransaction, document); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_update() failed with error code: ${errorCode}`); } } /** @internal */ async function collectionUpdateMultiple(ditto, collectionName, writeTransaction, documents) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const cDocuments = jsDocsToCDocs(documents); const errorCode = await ditto_collection_update_multiple(ditto, collectionNameX, writeTransaction, cDocuments); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_collection_update_multiple() failed with error code: ${errorCode}`); } } /** @internal */ async function collectionExecQueryStr(ditto, collectionName, writeTransaction, query, queryArgsCBOR, orderBy, limit, offset) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const queryX = bytesFromString(query); return await ditto_collection_exec_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset); } /** @internal */ async function collectionRemoveQueryStr(ditto, collectionName, writeTransaction, query, queryArgsCBOR, orderBy, limit, offset) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const queryX = bytesFromString(query); return await ditto_collection_remove_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset); } /** @internal */ async function collectionEvictQueryStr(ditto, collectionName, writeTransaction, query, queryArgsCBOR, orderBy, limit, offset) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const queryX = bytesFromString(query); return await ditto_collection_evict_query_str(ditto, collectionNameX, writeTransaction, queryX, queryArgsCBOR, orderBy, limit, offset); } /** * This FFI can error: * - DQL parser error * - Incorrect arguments to query parameters * - Collection is not found. * * @internal */ async function tryExecStatement(ditto, query, queryArgsCBOR) { ensureInitialized(); const queryBytesPointer = bytesFromString(query); const result = await dittoffi_try_exec_statement(ditto, queryBytesPointer, queryArgsCBOR); throwOnErrorResult(result.error, 'dittoffi_try_exec_statement'); return result.success; } /** @internal */ function addSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const queryX = bytesFromString(query); const statusCode = ditto_add_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset); if (statusCode !== 0) { throw new Error(errorMessage() || `ditto_add_subscription() failed with error code: ${statusCode}`); } } /** @internal */ function removeSubscription(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset) { ensureInitialized(); const collectionNameX = bytesFromString(collectionName); const queryX = bytesFromString(query); const statusCode = ditto_remove_subscription(ditto, collectionNameX, queryX, queryArgsCBOR, orderBy, limit, offset); if (statusCode !== 0) { throw new Error(errorMessage() || `ditto_remove_subscription() failed with error code: ${statusCode}`); } } /** @internal */ function tryAddSyncSubscription(dittoPointer, query, queryArgsCBOR) { ensureInitialized(); const queryBuffer = bytesFromString(query); const result = dittoffi_try_add_sync_subscription(dittoPointer, queryBuffer, queryArgsCBOR); throwOnErrorResult(result.error, 'dittoffi_try_add_sync_subscription'); } /** @internal */ function tryRemoveSyncSubscription(dittoPointer, query, queryArgsCBOR) { ensureInitialized(); const queryBuffer = bytesFromString(query); const result = dittoffi_try_remove_sync_subscription(dittoPointer, queryBuffer, queryArgsCBOR); throwOnErrorResult(result.error, 'dittoffi_try_remove_sync_subscription'); } // ----------------------------------------------------------- QueryResult ------ /** * Doesn't error * * @internal */ function queryResultFree(queryResultPointer) { ensureInitialized(); dittoffi_query_result_free(queryResultPointer); } /** * Doesn't error * * @internal */ function queryResultItemFree(queryResultItemPointer) { ensureInitialized(); dittoffi_query_result_item_free(queryResultItemPointer); } /** * Can error only on internal bug. * * @internal */ function queryResultItems(queryResultPointer) { ensureInitialized(); const rv = []; const resultCount = dittoffi_query_result_item_count(queryResultPointer); for (let i = 0; i < resultCount; i++) rv.push(dittoffi_query_result_item_at(queryResultPointer, i)); return rv; } /** * Doesn't error * * @internal */ function queryResultMutatedDocumentIDs(queryResultPointer) { ensureInitialized(); const rv = []; const resultCount = dittoffi_query_result_mutated_document_id_count(queryResultPointer); for (let i = 0; i < resultCount; i++) { const cborBytes = dittoffi_query_result_mutated_document_id_at(queryResultPointer, i); rv.push(boxCBytesIntoBuffer(cborBytes)); } return rv; } /** * The result CBOR contains a map/object with fields and values. No CRDTs are * present there as they are not needed. By default only values from registers * are returned and non-register fields are ignored. * * Doesn't error * * @internal */ function queryResultItemCBOR(queryResultItemPointer) { ensureInitialized(); const cborBytes = dittoffi_query_result_item_cbor(queryResultItemPointer); return boxCBytesIntoBuffer(cborBytes); } /** * Returns JSON-encoded results given a DQL result item pointer. * * Compare for {@link queryResultItemCBOR} above. * * Doesn't error * * @internal */ function queryResultItemJSON(queryResultItemPointer) { ensureInitialized(); const jsonBytes = dittoffi_query_result_item_json(queryResultItemPointer); return boxCStringIntoString(jsonBytes); } function queryResultItemNew(jsonData) { ensureInitialized(); const result = dittoffi_query_result_item_new(jsonData); throwOnErrorResult(result.error, 'dittoffi_query_result_item_new'); return result.success; } // ------------------------------------------------------------ LiveQuery ------ /** @internal */ function liveQueryRegister(ditto, collectionName, query, queryArgsCBOR, orderBy, limit, offset, eventHandler, // Cb may be called in parallel at any point, so let's use // an optional error handler (which defaults to the ditto logger at 'Error' level). onError) { ensureInitialized(); const collectionNameBuffer = bytesFromString(collectionName); const queryBuffer = bytesFromString(query); // Note(Daniel): the callback is now registered to be called in a detached // manner: if the FFI / Rust does `cb()`, then when that call returns, the js // callback itself may not have completed. This is fine, since `signalNext()` // shall be the proper way to let the Rust core know the FFI call completed. const { status_code: errorCode, i64: id } = ditto_live_query_register_str_detached(ditto, collectionNameBuffer, queryBuffer, queryArgsCBOR, orderBy, limit, offset, wrapBackgroundCbForFFI(onError, eventHandler)); if (errorCode !== 0) { throw new Error(errorMessage() || `\`ditto_live_query_register_str()\` failed with error code: ${errorCode}`); } return id; } /** @internal */ function tryExperimentalRegisterChangeObserver(ditto, query, queryArgsCBOR, changeHandler) { ensureInitialized(); const errorHandler = (err) => log('Error', `The registered store observer callback failed with ${err}`); const wrappedCallback = wrapBackgroundCbForFFI(errorHandler, changeHandler); const queryBuffer = bytesFromString(query); // Note(Daniel): the callback is registered to be called in a detached manner: // if the FFI / Rust does `cb()`, then when that call returns, the js callback // itself may not have completed. This is fine, since `signalNext()` shall be // the proper way to let the Rust core know the FFI call completed. const result = dittoffi_try_experimental_register_change_observer_str_detached(ditto, queryBuffer, queryArgsCBOR, wrappedCallback); throwOnErrorResult(result.error, 'dittoffi_try_experimental_register_change_observer_str_detached'); return result.success; } /** @internal */ async function liveQueryStart(ditto, liveQueryID) { ensureInitialized(); const errorCode = await ditto_live_query_start(ditto, liveQueryID); if (errorCode !== 0) { throw new Error(errorMessage() || `\`ditto_live_query_start()\` failed with error code: ${errorCode}`); } } /** @internal */ function liveQueryStop(ditto, liveQueryID) { ensureInitialized(); ditto_live_query_stop(ditto, liveQueryID); } /** @internal */ async function liveQuerySignalAvailableNext(ditto, liveQueryID) { ensureInitialized(); await ditto_live_query_signal_available_next(ditto, liveQueryID); } // ------------------------------------------------------ ReadTransaction ------ /** @internal */ async function readTransaction(ditto) { ensureInitialized(); // REFACTOR: add proper error handling. const { status_code: errorCode, txn: readTransaction } = await ditto_read_transaction(ditto); if (errorCode !== 0) { throw new Error(errorMessage() || `\`ditto_read_transaction()\` failed with error code: ${errorCode}`); } return readTransaction; } /** @internal */ function readTransactionFree(self) { ensureInitialized(); // REFACTOR: add proper error handling. return ditto_read_transaction_free(self); } // ----------------------------------------------------- WriteTransaction ------ /** @internal */ async function writeTransaction(ditto) { ensureInitialized(); // REFACTOR: add proper error handling. const { status_code: errorCode, txn: writeTransaction } = await ditto_write_transaction(ditto, null); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_write_transaction() failed with error code: ${errorCode}`); } return writeTransaction; } /** @internal */ async function writeTransactionCommit(ditto, self) { ensureInitialized(); const errorCode = await ditto_write_transaction_commit(ditto, self); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_write_transaction_commit() failed with error code: ${errorCode}`); } } /** @internal */ async function writeTransactionRollback(ditto, transaction) { ensureInitialized(); const errorCode = await ditto_write_transaction_rollback(ditto, transaction); if (errorCode !== 0) { throw new Error(errorMessage() || `ditto_write_transaction_rollback() failed with error code: ${errorCode}`); } } // --------------------------------------------------------------- Logger ------ /** @internal */ function loggerInit() { ensureInitialized(); ditto_logger_init(); } /** @internal */ async function loggerSetCustomLogCb(cb) { ensureInitialize