From d8b8f9487a8f154f051696b9b5edd9958917d75e Mon Sep 17 00:00:00 2001 From: jayant dhingra Date: Mon, 22 Dec 2025 18:41:21 +0530 Subject: [PATCH 1/3] Add React Native v1 documentation updates --- docs/DataSync/remote-sync-gateway.md | 173 +++++++--- docs/ProductNotes/release-notes.md | 36 ++ docs/Queries/live-queries.md | 49 ++- docs/Troubleshooting/using-logs.md | 168 +++++++++- docs/change-listeners.md | 481 +++++++++++++++++++++++++++ docs/databases.md | 12 +- docs/documents.md | 32 +- docs/intro.md | 4 +- docs/migration-guide-v1.md | 250 ++++++++++++++ docs/scopes-collections.md | 24 +- yarn.lock | 379 +++++++++++++-------- 11 files changed, 1388 insertions(+), 220 deletions(-) create mode 100644 docs/change-listeners.md create mode 100644 docs/migration-guide-v1.md diff --git a/docs/DataSync/remote-sync-gateway.md b/docs/DataSync/remote-sync-gateway.md index 6b8b578..21e7745 100644 --- a/docs/DataSync/remote-sync-gateway.md +++ b/docs/DataSync/remote-sync-gateway.md @@ -130,37 +130,97 @@ If you’re using a Sync Gateway release that is older than version 3.1, you won #### Example 1. Replication configuration and initialization ```typescript -//assumes you are running sync gateway locally, if you are - //running app services, replace enpoint with proper url and creditentials - const target = new URLEndpoint('ws://localhost:4984/projects'); - const auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12'); - const config = new ReplicatorConfiguration(target); - config.addCollection(collectionName); - config.setAuthenticator(auth); - - const replicator = await Replicator.create(config); - - //listen to the replicator change events - const token = await replicator.addChangeListener((change) => { - //check to see if there was an error - const error = change.status.getError(); - if (error !== undefined) { - //do something with the error - } - //get the status of the replicator using ReplicatorActivityLevel enum - if (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) { - //do something because the replicator is now IDLE - } - }); - - // start the replicator without making a new checkpoint - await replicator.start(false); - - //remember you must clean up the replicator when done with it by - //doing the following lines - - //await replicator.removeChangeListener(token); - //await replicator.stop(); +import { + ReplicatorConfiguration, + CollectionConfiguration, + URLEndpoint, + BasicAuthenticator, + Replicator, + ListenerToken +} from 'cbl-reactnative'; + +// Create endpoint and authenticator +const endpoint = new URLEndpoint('ws://localhost:4984/projects'); +const auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12'); + +// NEW API: Create collection configuration +const collectionConfig = new CollectionConfiguration(collection); + +// Pass collection configurations in constructor +const config = new ReplicatorConfiguration( + [collectionConfig], // Collections passed during initialization + endpoint +); + +config.setAuthenticator(auth); + +// Create replicator +const replicator = await Replicator.create(config); + +// Listen to replicator change events +const token: ListenerToken = await replicator.addChangeListener((change) => { + // Check for errors + const error = change.status.getError(); + if (error) { + console.error('Replication error:', error); + } + + // Check activity level + if (change.status.getActivityLevel() === 3) { // IDLE + console.log('Replication is idle'); + } +}); + +// Start replication +await replicator.start(false); + +// Remember to clean up when done: +// await token.remove(); +// await replicator.stop(); +``` + +:::important Version 1.0 API Change +Collections are now passed during `ReplicatorConfiguration` construction using `CollectionConfiguration` objects. + +**NEW API (Recommended):** +```typescript +const collectionConfig = new CollectionConfiguration(collection); +const config = new ReplicatorConfiguration([collectionConfig], endpoint); +``` + +**OLD API (Deprecated):** +```typescript +const config = new ReplicatorConfiguration(endpoint); +config.addCollection(collection); // Deprecated +``` + +The old `addCollection()` method is deprecated but still works for backward compatibility. +::: + +#### Example 1b. Multiple Collections + +The new API allows each collection to have its own replication settings: + +```typescript +import { CollectionConfiguration } from 'cbl-reactnative'; + +// Configure users collection +const usersConfig = new CollectionConfiguration(usersCollection) + .setChannels(['public', 'users']); + +// Configure orders collection with different settings +const ordersConfig = new CollectionConfiguration(ordersCollection) + .setChannels(['orders', 'admin']) + .setDocumentIDs(['order-1', 'order-2']); // Only specific documents + +// Pass both configurations during initialization +const config = new ReplicatorConfiguration( + [usersConfig, ordersConfig], + endpoint +); + +config.setAuthenticator(auth); +const replicator = await Replicator.create(config); ``` ## Configure @@ -662,15 +722,20 @@ The returned *ReplicationStatus* structure comprises: #### Example 14. Monitor replication ```typescript -// Optionally add a change listener -// Retain token for use in deletion -const token = replicator.addChangeListener((change) => { - if (change.status.getActivityLevel() === 'STOPPED') { - console.log("Replication stopped"); - } else { - console.log(`Replicator is currently : ${change.status.getActivityLevel()}`); - } +import { ListenerToken } from 'cbl-reactnative'; + +const token: ListenerToken = await replicator.addChangeListener((change) => { + const status = change.status; + const activityLevel = status.getActivityLevel(); + const progress = status.getProgress(); + + const levelNames = ['stopped', 'offline', 'connecting', 'idle', 'busy']; + console.log(`Status: ${levelNames[activityLevel]}`); + console.log(`Progress: ${progress.getCompleted()}/${progress.getTotal()}`); }); + +// Remove listener when done +await token.remove(); ``` ### Replication States @@ -706,32 +771,40 @@ For example, the code snippet in [Example 15](#example-15-register-a-document-li #### Example 15. Register a document listener ```typescript -const token = await replicator.addDocumentChangeListener((replication) => { - console.log(`Replication type :: ${replication.isPush ? "Push" : "Pull"}`); +import { ListenerToken } from 'cbl-reactnative'; + +const token: ListenerToken = await replicator.addDocumentChangeListener((replication) => { + const direction = replication.isPush ? "Push" : "Pull"; + console.log(`${direction}: ${replication.documents.length} documents`); + for (const document of replication.documents) { if (document.error === undefined) { - console.log(`Doc ID :: ${document.id}`); + console.log(` Doc ID: ${document.id}`); + if (document.flags.includes('DELETED')) { - console.log("Successfully replicated a deleted document"); + console.log(" Successfully replicated a deleted document"); } } else { - console.error("Error replicating document:", document.error); + console.error(` Error: ${document.error.message}`); } } }); -// Start the replicator without resetting the checkpoint +// Start the replicator await replicator.start(false); ``` #### Example 16. Stop document listener -This code snippet shows how to stop the document listener using the token from the previous example. - ```typescript -await this.replicator.removeChangeListener(token); +// Remove listener using new API +await token.remove(); ``` +:::caution Deprecated +The old `replicator.removeChangeListener(token)` method is deprecated. Use `token.remove()` instead. +::: + ### Document Access Removal Behavior When access to a document is removed on Sync Gateway (see: Sync Gateway’s [Sync Function](https://docs.couchbase.com/sync-gateway/current/sync-function-api.html)), the document replication listener sends a notification with the `AccessRemoved` flag set to `true` and subsequently purges the document from the database. @@ -763,10 +836,10 @@ You can find further information on database operations in [Databases](../databa ```typescript // Remove the change listener -await this.replicator.removeChangeListener(token) +await token.remove() // Stop the replicator -await this.replicator.stop() +await replicator.stop() ``` Here we initiate the stopping of the replication using the `stop()` method. It will stop any active `change listener` once the replication is stopped. diff --git a/docs/ProductNotes/release-notes.md b/docs/ProductNotes/release-notes.md index f8b153c..b3ea415 100644 --- a/docs/ProductNotes/release-notes.md +++ b/docs/ProductNotes/release-notes.md @@ -5,6 +5,42 @@ sidebar_position: 1 # Release Notes +**1.0.0** (December 2025) + +New Features: +- Log Sink API - Console, File, and Custom log sinks with configurable levels and domains +- LogDomain.ALL - New domain to enable all log categories at once +- Listener Token Management - New `ListenerToken` class with `token.remove()` API +- Collection Change Listeners - Monitor all documents in a collection +- Document Change Listeners - Monitor specific documents by ID +- Query Change Listeners (Live Queries) - Real-time query results +- Replicator Status Change Listeners - Monitor replication state and progress +- Replicator Document Change Listeners - Track individual document replication +- New ReplicatorConfiguration API - Collections passed during initialization using CollectionConfiguration +- Collection.fullName() method - Get fully qualified collection name (scope.collection) +- Couchbase Lite 3.3.0 - Updated iOS and Android SDKs to latest stable version + +Breaking Changes: +- REMOVED: Database.setLogLevel() - Use LogSinks.setConsole() instead +- API CHANGE: ReplicatorConfiguration - Collections now passed in constructor, not via addCollection() + +Deprecated (Still Functional): +- config.addCollection(collection) - Pass CollectionConfiguration array in constructor instead +- removeChangeListener() methods - Use token.remove() instead + +Bug Fixes: +- Fixed encryption key crash when key not required +- Fixed Kotlin import paths and enhanced logging methods +- Improved blob data validation and array handling +- Fixed custom delete issues + +Migration from 0.6.x: +1. Replace Database.setLogLevel() with LogSinks.setConsole() +2. Update ReplicatorConfiguration to use new constructor pattern +3. Update listener cleanup to use token.remove() (optional - old way still works) + +--- + **0.6.3** - Array handling and improve blob data validation in DataAdapter [null-pointer issue](https://github.com/Couchbase-Ecosystem/cbl-reactnative/pull/73) - Fix a crash caused by improper handling of encryption key diff --git a/docs/Queries/live-queries.md b/docs/Queries/live-queries.md index 6f9fd05..82b670e 100644 --- a/docs/Queries/live-queries.md +++ b/docs/Queries/live-queries.md @@ -23,25 +23,46 @@ Each time you start watching a live query, the query is executed and an initial #### Example 1. Starting a Live Query - Change Listener ```typescript -// Register a change listener and await the Promise returned from the registration call. -const token = await query.addChangeListener((change) => { - if (change.error !== null && change.error !== undefined) { - // deal with error... - } else { - const results = change.results; - //loop through ResultSet - for (const doc of results) { - //do something with doc - } +import { ListenerToken } from 'cbl-reactnative'; + +// Register a change listener +const token: ListenerToken = await query.addChangeListener((change) => { + if (change.error) { + console.error('Query error:', change.error); + return; + } + + const results = change.results; + // results is an array of result objects + for (const doc of results) { + console.log('Result:', doc); } }); ``` -To stop receiving notifications, call `Query.removeChangeListener` with the token that was returned from the registration call. Regardless of the whether the API is synchronous or asynchronous, listeners will stop receiving notifications immediately: +:::note Version 1.0 +Change listeners now return a `ListenerToken` object with a `remove()` method for cleanup. +::: #### Example 2. Stopping a Live Query - Change Listener ```typescript -const token = await query.addChangeListener((change) => { ... }); -await query.removeChangeListener(token); -``` \ No newline at end of file +// Remove listener using new API +await token.remove(); +``` + +:::caution Deprecated +The old `query.removeChangeListener(token)` method is deprecated but still works: + +**Old way (deprecated):** +```typescript +await query.removeChangeListener(token); // Still works +``` + +**New way (recommended):** +```typescript +await token.remove(); // Use this +``` +::: + +For complete information on query change listeners and all other listener types, see [Change Listeners](../change-listeners.md). \ No newline at end of file diff --git a/docs/Troubleshooting/using-logs.md b/docs/Troubleshooting/using-logs.md index ede2ae0..fdf5cd0 100644 --- a/docs/Troubleshooting/using-logs.md +++ b/docs/Troubleshooting/using-logs.md @@ -5,8 +5,8 @@ sidebar_position: 1 # Using Logs for Troubleshooting -> Description — _Couchbase Lite on React Native — Using Logs for Troubleshooting_ -> Related Content — [Troubleshooting Queries](troubeshoot-queries.md) | [Troubleshooting Crashes](troubleshoot-crashes.md) +> Description — _Couchbase Lite on React Native — Using Logs for Troubleshooting_ +> Related Content — [Troubleshooting Queries](troubeshoot-queries.md) | [Troubleshooting Crashes](troubleshoot-crashes.md) :::note * The retrieval of logs from the device is out of scope of this feature. @@ -14,7 +14,7 @@ sidebar_position: 1 ## Introduction -Couchbase Lite provides a robust Logging API — see: API References for Logging classes — which make debugging and troubleshooting easier during development and in production. It delivers flexibility in terms of how logs are generated and retained, whilst also maintaining the level of logging required by Couchbase Support for investigation of issues. +Couchbase Lite provides a robust Logging API — see: API References for Logging classes — which make debugging and troubleshooting easier during development and in production. It delivers flexibility in terms of how logs are generated and retained, whilst also maintaining the level of logging required by Couchbase Support for investigation of issues. Log output is split into the following streams: @@ -30,16 +30,166 @@ Log output is split into the following streams: For greater flexibility you can implement a custom logging class using the ILogger interface. -## Console based logging +## Log Sink API -Console based logging is often used to facilitate troubleshooting during development. +Version 1.0 introduces the Log Sink API which provides three types of log sinks for flexible logging control. -Console logs are your go-to resource for diagnostic information. You can easily fine-tune their diagnostic content to meet the needs of a particular debugging scenario, perhaps by increasing the verbosity and-or choosing to focus on messages from a specific domain; to better focus on the problem area. +### Log Levels -Changes to console logging are independent of file logging, so you can make change without compromising any files logging streams. It is enabled by default. To change default settings use database’s setLogLevel method to set the required values — see Example 1. +| Level | Value | Description | +|-------|-------|-------------| +| LogLevel.DEBUG | 0 | Most verbose - all logs | +| LogLevel.VERBOSE | 1 | Detailed diagnostic logs | +| LogLevel.INFO | 2 | Informational messages | +| LogLevel.WARNING | 3 | Warning messages only | +| LogLevel.ERROR | 4 | Error messages only | +| LogLevel.NONE | 5 | No logging | -#### Example 1. Change Console Logging Settings +### Log Domains + +| Domain | Description | +|--------|-------------| +| LogDomain.DATABASE | Database operations | +| LogDomain.QUERY | Query execution and planning | +| LogDomain.REPLICATOR | Replication activity | +| LogDomain.NETWORK | Network operations | +| LogDomain.LISTENER | Change listeners | +| LogDomain.ALL | All domains (new in 1.0) | + +## Console Log Sink + +Console based logging outputs logs to the system console (stdout/stderr), useful for development and debugging. + +#### Example 1. Enable Console Logging + +```typescript +import { LogSinks, LogLevel, LogDomain } from 'cbl-reactnative'; + +// Enable verbose logging for all domains +await LogSinks.setConsole({ + level: LogLevel.VERBOSE, + domains: [LogDomain.ALL] +}); +``` + +#### Example 2. Console with Specific Domains + +```typescript +// Log only replication and network activity +await LogSinks.setConsole({ + level: LogLevel.INFO, + domains: [LogDomain.REPLICATOR, LogDomain.NETWORK] +}); +``` + +#### Example 3. Disable Console Logging + +```typescript +// Disable console logging +await LogSinks.setConsole(null); +``` + +## File Log Sink + +File logging writes logs to files on the device with automatic rotation and retention policies. + +#### Example 4. Enable File Logging + +```typescript +import { Platform } from 'react-native'; +import RNFS from 'react-native-fs'; + +// Determine platform-specific log directory +const logDirectory = Platform.OS === 'ios' + ? RNFS.DocumentDirectoryPath + '/logs' + : RNFS.ExternalDirectoryPath + '/logs'; + +await LogSinks.setFile({ + level: LogLevel.INFO, + directory: logDirectory, + maxKeptFiles: 5, // Keep 5 old log files + maxFileSize: 1024 * 1024, // 1MB max file size + usePlaintext: true // Use plaintext format +}); +``` + +:::note File Rotation +When a log file reaches `maxFileSize`, it's closed and a new one is created. Old files exceeding `maxKeptFiles` are automatically deleted. +::: + +#### Example 5. Disable File Logging + +```typescript +await LogSinks.setFile(null); +``` + +## Custom Log Sink + +Custom logging allows you to implement your own logging logic with a callback function. + +#### Example 6. Custom Logging with Callback + +```typescript +await LogSinks.setCustom({ + level: LogLevel.ERROR, + domains: [LogDomain.ALL], + callback: (level, domain, message) => { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] [${domain}] ${message}`); + + // You can also send to analytics, log to database, etc. + } +}); +``` + +#### Example 7. Disable Custom Logging ```typescript -Database.setLogLevel(LogDomain.ALL, Loglevel.VERBOSE); +await LogSinks.setCustom(null); ``` + +## Using Multiple Log Sinks + +You can enable multiple log sinks simultaneously for different purposes. + +#### Example 8. Development and Production Configuration + +```typescript +if (__DEV__) { + // Development: Verbose console logging + await LogSinks.setConsole({ + level: LogLevel.VERBOSE, + domains: [LogDomain.ALL] + }); +} else { + // Production: File logging for warnings and errors + await LogSinks.setFile({ + level: LogLevel.WARNING, + directory: logDirectory, + maxKeptFiles: 7, + maxFileSize: 2 * 1024 * 1024, + usePlaintext: true + }); + + // Also send errors to analytics + await LogSinks.setCustom({ + level: LogLevel.ERROR, + domains: [LogDomain.ALL], + callback: (level, domain, message) => { + Analytics.logError({ level, domain, message }); + } + }); +} +``` + +## Platform Considerations + +**iOS:** +- Log files are stored in the app's Documents directory +- Path: `RNFS.DocumentDirectoryPath + '/logs'` +- Accessible via iTunes File Sharing if enabled in Info.plist + +**Android:** +- Log files are stored in the app's external directory +- Path: `RNFS.ExternalDirectoryPath + '/logs'` +- May require storage permissions in AndroidManifest.xml diff --git a/docs/change-listeners.md b/docs/change-listeners.md new file mode 100644 index 0000000..eaf2d6d --- /dev/null +++ b/docs/change-listeners.md @@ -0,0 +1,481 @@ +--- +id: change-listeners +sidebar_position: 8 +--- + +# Change Listeners + +> Description — _Couchbase Lite for React Native — Real-time Data Change Notifications_ +> Related Content — [Documents](documents.md) | [Live Queries](Queries/live-queries.md) | [Remote Sync](DataSync/remote-sync-gateway.md) + +:::important AT A GLANCE +Couchbase Lite provides 5 types of change listeners for real-time data notifications: + +* **Collection Change Listeners** - Monitor all document changes in a collection +* **Document Change Listeners** - Monitor a specific document by ID +* **Query Change Listeners** - Monitor query results in real-time (Live Queries) +* **Replicator Status Listeners** - Monitor replication state and progress +* **Replicator Document Listeners** - Monitor individual document replication + +All listeners return a `ListenerToken` object for lifecycle management. +::: + +## Listener Token API + +Version 1.0 introduces the `ListenerToken` class for improved listener management. + +All `addChangeListener` methods return a `ListenerToken` object with these methods: + +**remove()** - Removes the listener (recommended) + +```typescript +const token = await collection.addChangeListener(...); +await token.remove(); +``` + +**getUuidToken()** - Gets the internal UUID string (for debugging) + +```typescript +const uuid = token.getUuidToken(); +console.log('Token:', uuid); // "listener-abc-123-def-456" +``` + +**isRemoved()** - Checks if listener was already removed + +```typescript +if (!token.isRemoved()) { + await token.remove(); +} +``` + +:::caution Deprecated Methods +The old `removeChangeListener()` methods are deprecated but still functional for backward compatibility: + +```typescript +// OLD WAY (deprecated but still works) +await collection.removeChangeListener(token); +await query.removeChangeListener(token); +await replicator.removeChangeListener(token); + +// NEW WAY (recommended) +await token.remove(); +``` + +Use `token.remove()` for all new code. +::: + +## Collection Change Listeners + +Monitor all changes to any document in a collection. + +### When to Use + +- Refresh list views when data changes +- Trigger background sync after local updates +- Audit logging of collection activity +- Real-time collaboration features + +### Data Structure + +When a collection changes, your callback receives a `CollectionChange` object: + +```typescript +interface CollectionChange { + documentIDs: string[]; // Array of changed document IDs + collection: Collection; // Reference to the collection +} +``` + +:::caution Property Names +The property is `documentIDs` (capital IDs), not `documentIds`. + +There is NO direct `database` property. Access it via `change.collection.database`. +::: + +#### Example 1. Basic Collection Listener + +```typescript +import { Collection, ListenerToken } from 'cbl-reactnative'; + +const collection = await database.createCollection('users'); + +const token: ListenerToken = await collection.addChangeListener((change) => { + console.log('Collection changed!'); + console.log('Changed documents:', change.documentIDs); + // Example output: ['user-123', 'user-456'] + + console.log('Collection name:', change.collection.name); + // Output: users + + console.log('Database name:', change.collection.database.getName()); + // Output: mydb +}); + +// Remove listener when done +await token.remove(); +``` + +#### Example 2. React Hook Pattern + +```typescript +import { useEffect } from 'react'; +import { ListenerToken } from 'cbl-reactnative'; + +function UserListScreen({ collection }) { + useEffect(() => { + if (!collection) return; + + let token; + + const setup = async () => { + token = await collection.addChangeListener((change) => { + console.log(`${change.documentIDs.length} documents changed`); + refreshUserList(); + }); + }; + setup(); + + // Cleanup on unmount + return () => { + if (token) token.remove(); + }; + }, [collection]); +} +``` + +:::important Limitation +Only ONE collection-wide listener is allowed per collection instance. Calling `addChangeListener` twice will throw an error. + +However, you can have MULTIPLE document listeners (see next section). +::: + +## Document Change Listeners + +Monitor changes to a specific document by ID. + +### When to Use + +- Watch a user profile for updates +- Monitor a shopping cart +- Track order status changes +- Real-time document collaboration + +### Data Structure + +```typescript +interface DocumentChange { + documentId: string; // Document ID (lowercase Id) + collection: Collection; // Collection reference + database: Database; // Database reference (available!) +} +``` + +:::note +Unlike collection listeners, document change listeners DO have a direct `database` property. +::: + +#### Example 3. Monitor Specific Document + +```typescript +import { ListenerToken } from 'cbl-reactnative'; + +const token: ListenerToken = await collection.addDocumentChangeListener( + 'user-123', + async (change) => { + console.log(`Document ${change.documentId} changed`); + + // Fetch latest version + const doc = await collection.document(change.documentId); + if (doc) { + console.log('New data:', doc.getData()); + } else { + console.log('Document was deleted'); + } + } +); + +// Remove when done +await token.remove(); +``` + +#### Example 4. Multiple Document Listeners + +```typescript +// Monitor multiple specific documents +const userToken = await collection.addDocumentChangeListener('user-123', (change) => { + console.log('User changed'); +}); + +const cartToken = await collection.addDocumentChangeListener('cart-abc', (change) => { + console.log('Cart changed'); +}); + +// Cleanup all +await Promise.all([userToken.remove(), cartToken.remove()]); +``` + +## Query Change Listeners + +Monitor query results in real-time. Also known as "Live Queries". + +### Data Structure + +```typescript +interface QueryChange { + error: string; // Error message if query failed + query: Query; // Reference to the query + results: ResultSet; // Array of result objects +} +``` + +#### Example 5. Live Query + +```typescript +import { ListenerToken } from 'cbl-reactnative'; + +const query = database.createQuery( + 'SELECT META().id, name, email FROM _default.users WHERE isActive = true' +); + +const token: ListenerToken = await query.addChangeListener((change) => { + if (change.error) { + console.error('Query error:', change.error); + return; + } + + console.log(`Found ${change.results.length} active users`); + change.results.forEach(result => { + console.log(`User: ${result.name} - ${result.email}`); + }); +}); + +// Remove when done +await token.remove(); +``` + +For more information on live queries, see [Live Queries](Queries/live-queries.md). + +## Replicator Status Change Listeners + +Monitor replication status, progress, and errors. + +### Data Structure + +```typescript +interface ReplicatorStatusChange { + status: ReplicatorStatus; +} +``` + +**ReplicatorStatus Methods:** +- `getActivityLevel()` - Returns 0-4 (see table below) +- `getProgress()` - Returns ReplicatorProgress object +- `getError()` - Returns error message string or undefined + +**ReplicatorProgress Methods:** +- `getCompleted()` - Returns number of changes completed +- `getTotal()` - Returns total number of changes + +**Activity Levels:** + +| Value | Constant | Meaning | +|-------|----------|---------| +| 0 | STOPPED | Replication stopped or fatal error | +| 1 | OFFLINE | Cannot reach remote server | +| 2 | CONNECTING | Connecting to server | +| 3 | IDLE | Connected, no changes to sync | +| 4 | BUSY | Actively transferring data | + +#### Example 6. Monitor Replication Status + +```typescript +import { ListenerToken, ReplicatorActivityLevel } from 'cbl-reactnative'; + +const token: ListenerToken = await replicator.addChangeListener((change) => { + const status = change.status; + const level = status.getActivityLevel(); + const progress = status.getProgress(); + + console.log(`Activity: ${level}`); + console.log(`Progress: ${progress.getCompleted()}/${progress.getTotal()}`); + + if (status.getError()) { + console.error('Replication error:', status.getError()); + } + + // Check specific states + if (level === ReplicatorActivityLevel.IDLE) { + console.log('Sync complete'); + } else if (level === ReplicatorActivityLevel.BUSY) { + console.log('Syncing data...'); + } +}); + +// Remove when done +await token.remove(); +``` + +#### Example 7. Deprecated Removal Pattern + +```typescript +const token = await replicator.addChangeListener(...); + +// OLD WAY (deprecated but still works) +await replicator.removeChangeListener(token); + +// NEW WAY (recommended) +await token.remove(); +``` + +:::caution Deprecated +`replicator.removeChangeListener(token)` is deprecated. Use `token.remove()` instead. +::: + +## Replicator Document Change Listeners + +Monitor individual documents being replicated. + +### When to Use + +- Track which specific documents are syncing +- Detect replication conflicts +- Handle document-level replication errors +- Monitor deleted documents during sync + +### Data Structure + +```typescript +interface DocumentReplicationRepresentation { + isPush: boolean; // true = push, false = pull + documents: ReplicatedDocument[]; +} + +interface ReplicatedDocument { + id: string; // Document ID + scopeName: string; // Scope name + collectionName: string; // Collection name + flags: string[]; // ['DELETED', 'ACCESS_REMOVED'] + error?: { message: string }; // Present if replication failed +} +``` + +**Document Flags:** +- `'DELETED'` - Document was deleted +- `'ACCESS_REMOVED'` - User lost access to document + +#### Example 8. Monitor Document Replication + +```typescript +import { ListenerToken } from 'cbl-reactnative'; + +const token: ListenerToken = await replicator.addDocumentChangeListener((change) => { + const direction = change.isPush ? 'PUSH' : 'PULL'; + console.log(`${direction}: ${change.documents.length} documents`); + + change.documents.forEach(doc => { + console.log(` ${doc.id}`); + + if (doc.flags.includes('DELETED')) { + console.log(' Document was deleted'); + } + + if (doc.error) { + console.error(` Error: ${doc.error.message}`); + } + }); +}); + +// Remove when done +await token.remove(); +``` + +## Best Practices + +**Always Remove Listeners** + +In React components, use the cleanup function: + +```typescript +useEffect(() => { + let token; + + const setup = async () => { + token = await collection.addChangeListener((change) => { + // Handle changes + }); + }; + setup(); + + return () => { + if (token && !token.isRemoved()) { + token.remove(); + } + }; +}, [collection]); +``` + +**Check Before Removing** + +```typescript +if (!token.isRemoved()) { + await token.remove(); +} +``` + +## Common Pitfalls + +:::caution Property Name Gotchas +**Collection Change Listener:** +```typescript +change.documentIDs // CORRECT (capital IDs) +change.documentIds // WRONG - will be undefined + +change.collection.database // CORRECT way to access database +change.database // WRONG - property doesn't exist +``` + +**Document Change Listener:** +```typescript +change.documentId // CORRECT (lowercase Id) +change.documentID // WRONG - will be undefined + +change.database // CORRECT - direct property exists +``` + +Getting these property names wrong is the most common source of errors. +::: + +**Multiple Collection Listeners** + +```typescript +// WRONG - will throw error +const token1 = await collection.addChangeListener(...); +const token2 = await collection.addChangeListener(...); // ERROR! + +// CORRECT - only one collection listener per instance +const token = await collection.addChangeListener(...); + +// CORRECT - multiple document listeners are fine +const t1 = await collection.addDocumentChangeListener('user-123', ...); +const t2 = await collection.addDocumentChangeListener('user-456', ...); +``` + +**Async Handling in Callbacks** + +```typescript +// WRONG - promise not awaited +const token = await collection.addChangeListener((change) => { + change.documentIDs.forEach(id => { + collection.document(id); // Returns promise, not awaited! + }); +}); + +// CORRECT - proper async handling +const token = await collection.addChangeListener(async (change) => { + const docs = await Promise.all( + change.documentIDs.map(id => collection.document(id)) + ); + console.log('Documents:', docs); +}); +``` + diff --git a/docs/databases.md b/docs/databases.md index 6855fc8..6bcb724 100644 --- a/docs/databases.md +++ b/docs/databases.md @@ -178,16 +178,22 @@ You can download and build it from the couchbaselabs [GitHub repository](https:/ You should use console logs as your first source of diagnostic information. If the information in the default logging level is insufficient you can focus it on database errors and generate more verbose messages. - ```typescript +import { LogSinks, LogLevel, LogDomain } from 'cbl-reactnative'; + try { - await db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE); - console.log('Database log level set to VERBOSE.'); + await LogSinks.setConsole({ + level: LogLevel.VERBOSE, + domains: [LogDomain.DATABASE] + }); + console.log('Database logging enabled.'); } catch (error) { console.error('Setting log level failed:', error); } ``` +For more on using logs for troubleshooting, see [Using Logs](Troubleshooting/using-logs.md). + diff --git a/docs/documents.md b/docs/documents.md index 35a39b4..7d654a6 100644 --- a/docs/documents.md +++ b/docs/documents.md @@ -336,20 +336,36 @@ await collection.save(document); ## Document change events -It is possible to register for document changes. The following example registers for changes to the document with ID user.john and prints the verified_account property when a change is detected. +Couchbase Lite allows you to register listeners to be notified when documents change. Version 1.0 introduces the `ListenerToken` API for better listener lifecycle management. + +#### Example 7. Document Change Listener ```typescript -const token = collection.addDocumentChangeListener('user.john', async (change) => { - const document = await collection.document(change.documentID); - if (document !== null) { - console.log(`Status: ${document.getString('verified_account')}`); +import { ListenerToken } from 'cbl-reactnative'; + +const token: ListenerToken = await collection.addDocumentChangeListener( + 'user.john', + async (change) => { + // Note: Property is 'documentId' (lowercase Id), not 'documentID' + const document = await collection.document(change.documentId); + if (document !== null) { + console.log(`Status: ${document.getString('verified_account')}`); + } } -}); +); -// Remove the change listener when it is no longer needed -await collection.removeDocumentChangeListener(token); +// Remove listener when no longer needed +await token.remove(); ``` +:::tip New in Version 1.0 +Change listeners now return a `ListenerToken` object. Use `token.remove()` to remove the listener. + +The old `collection.removeDocumentChangeListener(token)` method is deprecated but still works. +::: + +For complete information on all change listener types including collection-wide listeners, query listeners, and replicator listeners, see [Change Listeners](change-listeners.md). + ## Document Expiration Document expiration allows users to set the expiration date for a document. When the document expires, it is purged from the database. The purge is not replicated to Sync Gateway or Capella App Services. diff --git a/docs/intro.md b/docs/intro.md index ff4dea9..ed79c8a 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -57,6 +57,4 @@ Some of the features supported by other platform implementations of Couchbase Li - This is still in beta for the native platforms and is not yet supported in the plugin. * Peer-to-Peer Sync - - There is no "platform" specific code built into the plugin to allow you to find other peers. - -* Current beta release does not support Event Listeners for changes (Change Notifications). \ No newline at end of file + - There is no "platform" specific code built into the plugin to allow you to find other peers. \ No newline at end of file diff --git a/docs/migration-guide-v1.md b/docs/migration-guide-v1.md new file mode 100644 index 0000000..9176972 --- /dev/null +++ b/docs/migration-guide-v1.md @@ -0,0 +1,250 @@ +--- +id: migration-guide-v1 +sidebar_position: 9 +--- + +# Migration Guide: Version 1.0 + +> Description — _Quick reference for all API changes in cbl-reactnative version 1.0_ +> Related Content — [Release Notes](ProductNotes/release-notes.md) | [Change Listeners](change-listeners.md) | [Using Logs](Troubleshooting/using-logs.md) + +:::important AT A GLANCE +Version 1.0 introduces three major API changes: + +1. **Log Sink API** - Replaces `Database.setLogLevel()` with `LogSinks` API (BREAKING) +2. **Listener Token API** - New `token.remove()` replaces `removeChangeListener()` (DEPRECATED) +3. **New ReplicatorConfiguration** - Collections passed in constructor (DEPRECATED old way) +::: + +## Breaking Changes + +### 1. Logging API Replaced + +**OLD (Removed in 1.0):** +```typescript +// This no longer works +await db.setLogLevel(LogDomain.ALL, LogLevel.VERBOSE); +await Database.setLogLevel(LogDomain.DATABASE, LogLevel.INFO); +``` + +**NEW (Required in 1.0):** +```typescript +import { LogSinks, LogLevel, LogDomain } from 'cbl-reactnative'; + +await LogSinks.setConsole({ + level: LogLevel.VERBOSE, + domains: [LogDomain.ALL] +}); +``` + +**Migration:** Search your code for `setLogLevel` and replace with `LogSinks.setConsole`. + +See: [Using Logs](Troubleshooting/using-logs.md) + +--- + +### 2. ReplicatorConfiguration API Changed + +**OLD (Deprecated but works):** +```typescript +const config = new ReplicatorConfiguration(endpoint); +config.addCollection(collection); +const replicator = await Replicator.create(config); +``` + +**NEW (Recommended in 1.0):** +```typescript +import { CollectionConfiguration } from 'cbl-reactnative'; + +const collectionConfig = new CollectionConfiguration(collection); +const config = new ReplicatorConfiguration([collectionConfig], endpoint); +const replicator = await Replicator.create(config); +``` + +**Migration:** Old way still works but update when convenient. + +See: [Remote Sync](DataSync/remote-sync-gateway.md#example-1-replication-configuration-and-initialization) + +--- + +## Deprecated APIs (Still Work) + +### Listener Removal Methods + +**OLD (Deprecated in 1.0):** +```typescript +await collection.removeChangeListener(token); +await query.removeChangeListener(token); +await replicator.removeChangeListener(token); +``` + +**NEW (Recommended in 1.0):** +```typescript +await token.remove(); +``` + +**Migration:** Old way still works, update when convenient. + +See: [Change Listeners](change-listeners.md#listener-token-api) + +--- + +## New Features in 1.0 + +### 1. Three Log Sink Types + +```typescript +// Console Sink - for development +await LogSinks.setConsole({ + level: LogLevel.VERBOSE, + domains: [LogDomain.ALL] +}); + +// File Sink - for production logging +await LogSinks.setFile({ + level: LogLevel.WARNING, + directory: logDir, + maxKeptFiles: 5, + maxFileSize: 1024 * 1024, + usePlaintext: true +}); + +// Custom Sink - for analytics +await LogSinks.setCustom({ + level: LogLevel.ERROR, + domains: [LogDomain.ALL], + callback: (level, domain, message) => { + Analytics.log({ level, domain, message }); + } +}); +``` + +See: [Using Logs](Troubleshooting/using-logs.md) + +--- + +### 2. All 5 Change Listener Types + +Now fully documented with examples: + +```typescript +// 1. Collection Change Listener +const token1 = await collection.addChangeListener((change) => { + console.log('Changed docs:', change.documentIDs); // Capital IDs! +}); + +// 2. Document Change Listener +const token2 = await collection.addDocumentChangeListener('doc-id', (change) => { + console.log('Doc changed:', change.documentId); // Lowercase Id! +}); + +// 3. Query Change Listener (Live Query) +const token3 = await query.addChangeListener((change) => { + console.log('Results:', change.results); +}); + +// 4. Replicator Status Listener +const token4 = await replicator.addChangeListener((change) => { + const status = change.status; + console.log('Level:', status.getActivityLevel()); + console.log('Progress:', status.getProgress().getCompleted()); +}); + +// 5. Replicator Document Listener +const token5 = await replicator.addDocumentChangeListener((change) => { + console.log('Direction:', change.isPush ? 'Push' : 'Pull'); +}); + +// All use same removal pattern +await token1.remove(); +``` + +See: [Change Listeners](change-listeners.md) + +--- + +### 3. Collection.fullName() + +```typescript +const fullName = await collection.fullName(); +console.log(fullName); // "production.users" +``` + +See: [Scopes and Collections](scopes-collections.md#get-collection-full-name) + +--- + +## Quick Migration Steps + +**Step 1: Update Logging (REQUIRED)** + +Find and replace in your code: + +```bash +# Search for old API +grep -r "setLogLevel" . + +# Replace with LogSinks.setConsole +``` + +**Step 2: Update ReplicatorConfiguration (Optional)** + +```typescript +// Old (works but deprecated) +const config = new ReplicatorConfiguration(endpoint); +config.addCollection(collection); + +// New (recommended) +const collectionConfig = new CollectionConfiguration(collection); +const config = new ReplicatorConfiguration([collectionConfig], endpoint); +``` + +**Step 3: Update Listener Cleanup (Optional)** + +```typescript +// Old (works but deprecated) +await collection.removeChangeListener(token); + +// New (recommended) +await token.remove(); +``` + +--- + +## Important Property Names + +| Listener Type | Property | Correct | Wrong | +|--------------|----------|---------|-------| +| Collection Change | Document IDs | `documentIDs` | `documentIds` | +| Document Change | Document ID | `documentId` | `documentID` | +| Collection Change | Database | `collection.database` | `database` | +| Document Change | Database | `database` | N/A | +| Replicator Progress | Completed | `getCompleted()` | `completed` | +| Replicator Progress | Total | `getTotal()` | `total` | + +--- + +## Files Updated in Documentation + +| File | Change Type | +|------|-------------| +| [using-logs.md](Troubleshooting/using-logs.md) | BREAKING | +| [databases.md](databases.md) | BREAKING | +| [remote-sync-gateway.md](DataSync/remote-sync-gateway.md) | BREAKING + DEPRECATED | +| [change-listeners.md](change-listeners.md) | NEW | +| [documents.md](documents.md) | DEPRECATED + BUG FIX | +| [live-queries.md](Queries/live-queries.md) | DEPRECATED | +| [scopes-collections.md](scopes-collections.md) | NEW | +| [intro.md](intro.md) | UPDATE | +| [release-notes.md](ProductNotes/release-notes.md) | NEW | +| [migration-guide-v1.md](migration-guide-v1.md) | NEW | + +--- + +## Need Help? + +- **Full API Reference:** [Change Listeners](change-listeners.md) +- **Log Sink Examples:** [Using Logs](Troubleshooting/using-logs.md) +- **Replication Setup:** [Remote Sync](DataSync/remote-sync-gateway.md) +- **Full Release Notes:** [Version 1.0](ProductNotes/release-notes.md) + diff --git a/docs/scopes-collections.md b/docs/scopes-collections.md index e963655..cd9ff52 100644 --- a/docs/scopes-collections.md +++ b/docs/scopes-collections.md @@ -102,4 +102,26 @@ const scopes = await database.scopes(); // Get Collections of a particular Scope const collections = await database.collections(scopeName); -``` \ No newline at end of file +``` + +## Get Collection Full Name + +The `fullName()` method returns the fully qualified name of a collection in the format "scope.collection". + +**Example 5. Get Collection Full Name** + +```typescript +// User-defined collection +const collection = await database.createCollection('users', 'production'); +const fullName = await collection.fullName(); +console.log(fullName); +// Output: "production.users" + +// Default collection +const defaultCol = await database.defaultCollection(); +const defaultName = await defaultCol.fullName(); +console.log(defaultName); +// Output: "_default._default" +``` + +The full name is useful for logging, debugging, and displaying collection information in your application. \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b557435..9db1f8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -100,6 +100,11 @@ resolved "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.15.0.tgz" integrity sha512-IofrVh213VLsDkPoSKMeM9Dshrv28jhDlBDLRcVJQvlL8pzue7PEB1EZ4UoJFYS3NSn7JOcJ/V+olRQzXlJj1w== +"@algolia/client-common@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.46.1.tgz" + integrity sha512-3u6AuZ1Kiss6V5JPuZfVIUYfPi8im06QBCgKqLg82GUBJ3SwhiTdSZFIEgz2mzFuitFdW1PQi3c/65zE/3FgIw== + "@algolia/client-insights@5.15.0": version "5.15.0" resolved "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.15.0.tgz" @@ -139,6 +144,16 @@ "@algolia/requester-fetch" "5.15.0" "@algolia/requester-node-http" "5.15.0" +"@algolia/client-search@>= 4.9.1 < 6": + version "5.46.1" + resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.46.1.tgz" + integrity sha512-NL76o/BoEgU4ObY5oBEC3o6KSPpuXsnSta00tAxTm1iKUWOGR34DQEKhUt8xMHhMKleUNPM/rLPFiIVtfsGU8w== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + "@algolia/client-search@4.24.0": version "4.24.0" resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz" @@ -236,6 +251,13 @@ dependencies: "@algolia/client-common" "5.15.0" +"@algolia/requester-browser-xhr@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.46.1.tgz" + integrity sha512-3GfCwudeW6/3caKSdmOP6RXZEL4F3GiemCaXEStkTt2Re8f7NcGYAAZnGlHsCzvhlNEuDzPYdYxh4UweY8l/2w== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-common@4.24.0": version "4.24.0" resolved "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz" @@ -248,6 +270,13 @@ dependencies: "@algolia/client-common" "5.15.0" +"@algolia/requester-fetch@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.46.1.tgz" + integrity sha512-JUAxYfmnLYTVtAOFxVvXJ4GDHIhMuaP7JGyZXa/nCk3P8RrN5FCNTdRyftSnxyzwSIAd8qH3CjdBS9WwxxqcHQ== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-node-http@4.24.0": version "4.24.0" resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz" @@ -262,6 +291,13 @@ dependencies: "@algolia/client-common" "5.15.0" +"@algolia/requester-node-http@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.46.1.tgz" + integrity sha512-VwbhV1xvTGiek3d2pOS6vNBC4dtbNadyRT+i1niZpGhOJWz1XnfhxNboVbXPGAyMJYz7kDrolbDvEzIDT93uUA== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/transporter@4.24.0": version "4.24.0" resolved "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.24.0.tgz" @@ -293,7 +329,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz" integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg== -"@babel/core@^7.21.3", "@babel/core@^7.25.9": +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.21.3", "@babel/core@^7.25.9", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0": version "7.26.0" resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== @@ -1718,7 +1754,7 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@3.6.3": +"@docusaurus/plugin-content-docs@*", "@docusaurus/plugin-content-docs@3.6.3": version "3.6.3" resolved "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.3.tgz" integrity sha512-r2wS8y/fsaDcxkm20W5bbYJFPzdWdEaTWVYjNxlHlcmX086eqQR1Fomlg9BHTJ0dLXPzAlbC8EN4XqMr3QzNCQ== @@ -2097,7 +2133,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -2231,7 +2267,7 @@ "@svgr/babel-plugin-transform-react-native-svg" "8.1.0" "@svgr/babel-plugin-transform-svg-component" "8.0.0" -"@svgr/core@8.1.0": +"@svgr/core@*", "@svgr/core@8.1.0": version "8.1.0" resolved "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz" integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA== @@ -2542,14 +2578,13 @@ "@types/history" "^4.7.11" "@types/react" "*" -"@types/react@*": - version "18.2.70" - resolved "https://registry.npmjs.org/@types/react/-/react-18.2.70.tgz" - integrity sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ== +"@types/react@*", "@types/react@>= 16.8.0 < 19.0.0", "@types/react@>=16": + version "18.3.27" + resolved "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz" + integrity sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" + csstype "^3.2.2" "@types/retry@0.12.0": version "0.12.0" @@ -2563,11 +2598,6 @@ dependencies: "@types/node" "*" -"@types/scheduler@*": - version "0.16.8" - resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/send@*": version "0.17.4" resolved "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz" @@ -2633,7 +2663,7 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": +"@webassemblyjs/ast@^1.12.1", "@webassemblyjs/ast@1.12.1": version "1.12.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz" integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== @@ -2734,7 +2764,7 @@ "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": +"@webassemblyjs/wasm-parser@^1.12.1", "@webassemblyjs/wasm-parser@1.12.1": version "1.12.1" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz" integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== @@ -2784,10 +2814,10 @@ acorn-walk@^8.0.0: dependencies: acorn "^8.11.0" -acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2: - version "8.14.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" - integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2: + version "8.15.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== address@^1.0.1, address@^1.1.2: version "1.2.2" @@ -2809,7 +2839,12 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: +ajv-keywords@^3.4.1: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== @@ -2821,7 +2856,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.5: +ajv@^6.12.2, ajv@^6.12.5, ajv@^6.9.1: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2831,7 +2866,7 @@ ajv@^6.12.2, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -2848,7 +2883,7 @@ algoliasearch-helper@^3.13.3: dependencies: "@algolia/events" "^4.0.1" -algoliasearch@^4.18.0: +algoliasearch@^4.18.0, "algoliasearch@>= 3.1 < 6", "algoliasearch@>= 4.9.1 < 6": version "4.24.0" resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz" integrity sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g== @@ -3124,7 +3159,7 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2, "browserslist@>= 4.21.0": version "4.24.2" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz" integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== @@ -3752,30 +3787,37 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== debounce@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2.6.9, debug@^2.6.0: +debug@^2.6.0: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + decode-named-character-reference@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz" @@ -3849,16 +3891,16 @@ del@^6.1.1: rimraf "^3.0.2" slash "^3.0.0" -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + dequal@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" @@ -4374,7 +4416,7 @@ figures@^3.2.0: dependencies: escape-string-regexp "^1.0.5" -file-loader@^6.2.0: +file-loader@*, file-loader@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== @@ -4521,11 +4563,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -4668,16 +4705,16 @@ got@^12.1.0: p-cancelable "^3.0.0" responselike "^3.0.0" -graceful-fs@4.2.10: - version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graceful-fs@4.2.10: + version "4.2.10" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + gray-matter@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz" @@ -4969,6 +5006,16 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -4980,16 +5027,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" @@ -5093,7 +5130,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5103,16 +5140,16 @@ inherits@2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz" @@ -5135,16 +5172,16 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - ipaddr.js@^2.0.1: version "2.1.0" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz" integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-alphabetical@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz" @@ -5308,16 +5345,16 @@ is-yarn-global@^0.4.0: resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz" integrity sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ== -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" @@ -6282,7 +6319,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -6292,14 +6329,40 @@ mime-db@~1.33.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== -mime-types@2.1.18, mime-types@~2.1.17: +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^2.1.31: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@~2.1.17, mime-types@2.1.18: version "2.1.18" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== dependencies: mime-db "~1.33.0" -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@~2.1.24: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -6339,7 +6402,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -6718,6 +6781,13 @@ path-parse@^1.0.7: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" @@ -6728,13 +6798,6 @@ path-to-regexp@3.3.0: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz" integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw== -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -7311,7 +7374,7 @@ postcss-zindex@^6.0.2: resolved "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz" integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg== -postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38: +"postcss@^7.0.0 || ^8.0.1", postcss@^8, postcss@^8.0.3, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.2.2, postcss@^8.4, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.31, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.6: version "8.4.49" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== @@ -7429,16 +7492,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" - integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -range-parser@^1.2.1, range-parser@~1.2.1: +range-parser@~1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + raw-body@2.5.2: version "2.5.2" resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" @@ -7489,7 +7557,7 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@^18.3.1: +react-dom@*, "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", react-dom@^18.0.0, react-dom@^18.3.1, "react-dom@>= 16.8.0 < 19.0.0": version "18.3.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -7535,7 +7603,7 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1: dependencies: "@babel/runtime" "^7.10.3" -"react-loadable@npm:@docusaurus/react-loadable@6.0.0": +react-loadable@*, "react-loadable@npm:@docusaurus/react-loadable@6.0.0": version "6.0.0" resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz" integrity sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ== @@ -7562,7 +7630,7 @@ react-router-dom@^5.3.4: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.3.4, react-router@^5.3.4: +react-router@^5.3.4, react-router@>=5, react-router@5.3.4: version "5.3.4" resolved "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz" integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA== @@ -7577,7 +7645,7 @@ react-router@5.3.4, react-router@^5.3.4: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react@^18.3.1: +react@*, "react@^16.13.1 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.3.1, "react@>= 16.8.0 < 19.0.0", react@>=15, react@>=16, react@>=16.0.0: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -7939,15 +8007,20 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -7966,16 +8039,25 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== +schema-utils@^3.0.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.1.1: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: +schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -7994,6 +8076,20 @@ schema-utils@^4.0.0, schema-utils@^4.0.1: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +"search-insights@>= 1 < 3": + version "2.17.3" + resolved "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz" + integrity sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ== + section-matter@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz" @@ -8247,7 +8343,7 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@~0.6.0: +source-map@^0.6.0: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -8257,6 +8353,11 @@ source-map@^0.7.0: resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== +source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + space-separated-tokens@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" @@ -8295,22 +8396,45 @@ srcset@^4.0.0: resolved "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz" integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw== -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - "statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + std-env@^3.7.0: version "3.8.0" resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz" integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== -string-width@^4.1.0, string-width@^4.2.0: +string_decoder@^1.1.1: + 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== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.2.0: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -8328,20 +8452,6 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string_decoder@^1.1.1: - 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== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - stringify-entities@^4.0.0: version "4.0.4" resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz" @@ -8565,6 +8675,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +"typescript@>= 2.7", typescript@>=4.9.5: + version "5.9.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" @@ -8668,7 +8783,7 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -8884,7 +8999,7 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.88.1, webpack@^5.95.0: +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.88.1, webpack@^5.95.0, "webpack@>= 4", "webpack@>=4.41.1 || 5.x", webpack@>=5, "webpack@3 || 4 || 5": version "5.96.1" resolved "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz" integrity sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA== @@ -8927,7 +9042,7 @@ webpackbar@^6.0.1: std-env "^3.7.0" wrap-ansi "^7.0.0" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: +websocket-driver@^0.7.4, websocket-driver@>=0.5.1: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== From 249a1a685fa7231581ef2eab51b9cec92401243a Mon Sep 17 00:00:00 2001 From: jayant dhingra Date: Wed, 24 Dec 2025 17:18:26 +0530 Subject: [PATCH 2/3] Update documentation for change listeners and logging improvements; remove deprecated migration guide --- docs/DataSync/remote-sync-gateway.md | 95 +++- docs/Guides/Migration/_category_.json | 7 + .../Migration/v1.md} | 115 +++-- docs/Guides/_category_.json | 7 + docs/ProductNotes/release-notes.md | 16 +- docs/Queries/live-queries.md | 121 ++++- docs/Queries/sqlplusplus.md | 2 +- docs/change-listeners.md | 481 ------------------ docs/databases.md | 48 +- docs/documents.md | 89 +++- docs/scopes-collections.md | 91 +++- yarn.lock | 5 + 12 files changed, 513 insertions(+), 564 deletions(-) create mode 100644 docs/Guides/Migration/_category_.json rename docs/{migration-guide-v1.md => Guides/Migration/v1.md} (58%) create mode 100644 docs/Guides/_category_.json delete mode 100644 docs/change-listeners.md diff --git a/docs/DataSync/remote-sync-gateway.md b/docs/DataSync/remote-sync-gateway.md index 21e7745..8986995 100644 --- a/docs/DataSync/remote-sync-gateway.md +++ b/docs/DataSync/remote-sync-gateway.md @@ -194,7 +194,7 @@ const config = new ReplicatorConfiguration(endpoint); config.addCollection(collection); // Deprecated ``` -The old `addCollection()` method is deprecated but still works for backward compatibility. +The `addCollection()` method is deprecated. It remains available for backward compatibility, but new applications should use the new constructor pattern. Existing applications are strongly encouraged to migrate. ::: #### Example 1b. Multiple Collections @@ -738,6 +738,54 @@ const token: ListenerToken = await replicator.addChangeListener((change) => { await token.remove(); ``` +### Replicator Status Data Structure + +When replication status changes, your callback receives a `ReplicatorStatusChange` object: + +```typescript +interface ReplicatorStatusChange { + status: ReplicatorStatus; +} +``` + +**ReplicatorStatus Methods:** +- `getActivityLevel()` - Returns 0-4 (see activity levels table below) +- `getProgress()` - Returns ReplicatorProgress object +- `getError()` - Returns error message string or undefined + +**ReplicatorProgress Methods:** +- `getCompleted()` - Returns number of changes completed +- `getTotal()` - Returns total number of changes + +#### Example 14b. Advanced Replication Status Monitoring + +```typescript +import { ListenerToken, ReplicatorActivityLevel } from 'cbl-reactnative'; + +const token: ListenerToken = await replicator.addChangeListener((change) => { + const status = change.status; + const level = status.getActivityLevel(); + const progress = status.getProgress(); + + console.log(`Activity: ${level}`); + console.log(`Progress: ${progress.getCompleted()}/${progress.getTotal()}`); + + if (status.getError()) { + console.error('Replication error:', status.getError()); + } + + // Check specific states + if (level === ReplicatorActivityLevel.IDLE) { + console.log('Sync complete'); + } else if (level === ReplicatorActivityLevel.BUSY) { + console.log('Syncing data...'); + } +}); + +// Remove when done +await token.remove(); +``` + ### Replication States [Table 5](#table-5-replicator-activity-levels) shows the different states, or activity levels, reported in the API; and the meaning of each. @@ -766,6 +814,34 @@ On other platforms, Couchbase Lite doesn’t react to OS backgrounding or foregr You can choose to register for document updates during a replication. +#### When to Use Document Listeners + +- Track which specific documents are syncing +- Detect replication conflicts +- Handle document-level replication errors +- Monitor deleted documents during sync + +#### Document Replication Data Structure + +```typescript +interface DocumentReplicationRepresentation { + isPush: boolean; // true = push, false = pull + documents: ReplicatedDocument[]; +} + +interface ReplicatedDocument { + id: string; // Document ID + scopeName: string; // Scope name + collectionName: string; // Collection name + flags: string[]; // ['DELETED', 'ACCESS_REMOVED'] + error?: { message: string }; // Present if replication failed +} +``` + +**Document Flags:** +- `'DELETED'` - Document was deleted +- `'ACCESS_REMOVED'` - User lost access to document + For example, the code snippet in [Example 15](#example-15-register-a-document-listener) registers a listener to monitor document replication performed by the replicator referenced by the variable `replicator`. It prints the document ID of each document received and sent. Stop the listener as shown in [Example 16](#example-16-stop-document-listener). #### Example 15. Register a document listener @@ -802,7 +878,7 @@ await token.remove(); ``` :::caution Deprecated -The old `replicator.removeChangeListener(token)` method is deprecated. Use `token.remove()` instead. +The `replicator.removeChangeListener(token)` method is deprecated. It remains available for backward compatibility, but new applications should use `token.remove()`. Existing applications are strongly encouraged to migrate. ::: ### Document Access Removal Behavior @@ -892,15 +968,20 @@ As always, when there is a problem with replication, logging is your friend. You #### Example 21. Set logging verbosity - ```typescript -// Verbose / Replicator -database.setLogLevel(LogDomain.REPLICATOR, Loglevel.VERBOSE); +import { LogSinks, LogLevel, LogDomain } from 'cbl-reactnative'; -// Verbose / Network -database.setLogLevel(LogDomain.NETWORK, Loglevel.VERBOSE); +// Verbose / Replicator and Network +await LogSinks.setConsole({ + level: LogLevel.VERBOSE, + domains: [LogDomain.REPLICATOR, LogDomain.NETWORK] +}); ``` +:::caution +Enable VERBOSE logging only during development or debugging. Avoid using it in production builds. +::: + For more on troubleshooting with logs, see: [Using Logs](../Troubleshooting/using-logs.md). ### Authentication Errors diff --git a/docs/Guides/Migration/_category_.json b/docs/Guides/Migration/_category_.json new file mode 100644 index 0000000..a937a07 --- /dev/null +++ b/docs/Guides/Migration/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Migration", + "position": 1, + "collapsible": true, + "collapsed": false +} + diff --git a/docs/migration-guide-v1.md b/docs/Guides/Migration/v1.md similarity index 58% rename from docs/migration-guide-v1.md rename to docs/Guides/Migration/v1.md index 9176972..70bb7f8 100644 --- a/docs/migration-guide-v1.md +++ b/docs/Guides/Migration/v1.md @@ -1,28 +1,30 @@ --- id: migration-guide-v1 -sidebar_position: 9 +sidebar_position: 1 --- # Migration Guide: Version 1.0 > Description — _Quick reference for all API changes in cbl-reactnative version 1.0_ -> Related Content — [Release Notes](ProductNotes/release-notes.md) | [Change Listeners](change-listeners.md) | [Using Logs](Troubleshooting/using-logs.md) +> Related Content — [Release Notes](../../ProductNotes/release-notes.md) | [Using Logs](../../Troubleshooting/using-logs.md) :::important AT A GLANCE -Version 1.0 introduces three major API changes: +Version 1.0 introduces several API changes: -1. **Log Sink API** - Replaces `Database.setLogLevel()` with `LogSinks` API (BREAKING) -2. **Listener Token API** - New `token.remove()` replaces `removeChangeListener()` (DEPRECATED) -3. **New ReplicatorConfiguration** - Collections passed in constructor (DEPRECATED old way) +1. **Log Sink API** - Replaces `Database.setLogLevel()` with `LogSinks` API +2. **Listener Token API** - New `token.remove()` replaces `removeChangeListener()` +3. **New ReplicatorConfiguration** - Collections passed in constructor ::: -## Breaking Changes +## Deprecated APIs -### 1. Logging API Replaced +### 1. Logging API -**OLD (Removed in 1.0):** +The old `Database.setLogLevel()` API is deprecated and replaced with the new `LogSinks` API. + +**OLD (Deprecated):** ```typescript -// This no longer works +// This API is deprecated await db.setLogLevel(LogDomain.ALL, LogLevel.VERBOSE); await Database.setLogLevel(LogDomain.DATABASE, LogLevel.INFO); ``` @@ -37,15 +39,21 @@ await LogSinks.setConsole({ }); ``` +:::caution Important +The old and new logging APIs cannot be used in tandem. If you are migrating to version 1.0, you must replace all instances of `Database.setLogLevel()` with `LogSinks.setConsole()`. +::: + **Migration:** Search your code for `setLogLevel` and replace with `LogSinks.setConsole`. -See: [Using Logs](Troubleshooting/using-logs.md) +See: [Using Logs](../../Troubleshooting/using-logs.md) --- -### 2. ReplicatorConfiguration API Changed +### 2. ReplicatorConfiguration API -**OLD (Deprecated but works):** +The old constructor pattern using `addCollection()` is deprecated. + +**OLD (Deprecated):** ```typescript const config = new ReplicatorConfiguration(endpoint); config.addCollection(collection); @@ -61,15 +69,13 @@ const config = new ReplicatorConfiguration([collectionConfig], endpoint); const replicator = await Replicator.create(config); ``` -**Migration:** Old way still works but update when convenient. +This method is deprecated. It remains available for backward compatibility, but new applications should use the new constructor pattern. Existing applications are strongly encouraged to migrate. -See: [Remote Sync](DataSync/remote-sync-gateway.md#example-1-replication-configuration-and-initialization) +See: [Remote Sync](../../DataSync/remote-sync-gateway.md#example-1-replication-configuration-and-initialization) --- -## Deprecated APIs (Still Work) - -### Listener Removal Methods +### 3. Listener Removal Methods **OLD (Deprecated in 1.0):** ```typescript @@ -83,9 +89,28 @@ await replicator.removeChangeListener(token); await token.remove(); ``` -**Migration:** Old way still works, update when convenient. +This method is deprecated. It remains available for backward compatibility, but new applications should use `token.remove()`. Existing applications are strongly encouraged to migrate. + +--- + +### 4. Listener Token Return Type (TypeScript) + +We changed all `addChangeListener` functions to return a `ListenerToken` instead of a `string`, enabling the new `token.remove()` API and aligning with other platforms. -See: [Change Listeners](change-listeners.md#listener-token-api) +This is a small breaking change **only for TypeScript code** that explicitly typed the token as `string`. JavaScript users and untyped TypeScript won't be affected. + +**TypeScript Migration Example:** + +```typescript +// BEFORE (TypeScript compilation error in 1.0) +const token: string = await collection.addChangeListener(listener); + +// AFTER +const token: ListenerToken = await collection.addChangeListener(listener); +token.remove(); +``` + +The existing `removeChangeListener(token)` methods remain available for backward compatibility but are deprecated. --- @@ -119,7 +144,7 @@ await LogSinks.setCustom({ }); ``` -See: [Using Logs](Troubleshooting/using-logs.md) +See: [Using Logs](../../Troubleshooting/using-logs.md) --- @@ -159,7 +184,7 @@ const token5 = await replicator.addDocumentChangeListener((change) => { await token1.remove(); ``` -See: [Change Listeners](change-listeners.md) +See: [Scopes and Collections](../../scopes-collections.md#collection-change-listeners) | [Documents](../../documents.md#document-change-listeners) | [Live Queries](../../Queries/live-queries.md) | [Remote Sync](../../DataSync/remote-sync-gateway.md#change-listeners) --- @@ -170,13 +195,13 @@ const fullName = await collection.fullName(); console.log(fullName); // "production.users" ``` -See: [Scopes and Collections](scopes-collections.md#get-collection-full-name) +See: [Scopes and Collections](../../scopes-collections.md#get-collection-full-name) --- ## Quick Migration Steps -**Step 1: Update Logging (REQUIRED)** +**Step 1: Update Logging (Required)** Find and replace in your code: @@ -187,10 +212,10 @@ grep -r "setLogLevel" . # Replace with LogSinks.setConsole ``` -**Step 2: Update ReplicatorConfiguration (Optional)** +**Step 2: Update ReplicatorConfiguration (Recommended)** ```typescript -// Old (works but deprecated) +// Old (deprecated) const config = new ReplicatorConfiguration(endpoint); config.addCollection(collection); @@ -199,16 +224,26 @@ const collectionConfig = new CollectionConfiguration(collection); const config = new ReplicatorConfiguration([collectionConfig], endpoint); ``` -**Step 3: Update Listener Cleanup (Optional)** +**Step 3: Update Listener Cleanup (Recommended)** ```typescript -// Old (works but deprecated) +// Old (deprecated) await collection.removeChangeListener(token); // New (recommended) await token.remove(); ``` +**Step 4: Update TypeScript Token Types (Required for TypeScript)** + +```typescript +// Old (compilation error) +const token: string = await collection.addChangeListener(...); + +// New +const token: ListenerToken = await collection.addChangeListener(...); +``` + --- ## Important Property Names @@ -224,27 +259,9 @@ await token.remove(); --- -## Files Updated in Documentation - -| File | Change Type | -|------|-------------| -| [using-logs.md](Troubleshooting/using-logs.md) | BREAKING | -| [databases.md](databases.md) | BREAKING | -| [remote-sync-gateway.md](DataSync/remote-sync-gateway.md) | BREAKING + DEPRECATED | -| [change-listeners.md](change-listeners.md) | NEW | -| [documents.md](documents.md) | DEPRECATED + BUG FIX | -| [live-queries.md](Queries/live-queries.md) | DEPRECATED | -| [scopes-collections.md](scopes-collections.md) | NEW | -| [intro.md](intro.md) | UPDATE | -| [release-notes.md](ProductNotes/release-notes.md) | NEW | -| [migration-guide-v1.md](migration-guide-v1.md) | NEW | - ---- - ## Need Help? -- **Full API Reference:** [Change Listeners](change-listeners.md) -- **Log Sink Examples:** [Using Logs](Troubleshooting/using-logs.md) -- **Replication Setup:** [Remote Sync](DataSync/remote-sync-gateway.md) -- **Full Release Notes:** [Version 1.0](ProductNotes/release-notes.md) +- **Log Sink Examples:** [Using Logs](../../Troubleshooting/using-logs.md) +- **Replication Setup:** [Remote Sync](../../DataSync/remote-sync-gateway.md) +- **Full Release Notes:** [Version 1.0](../../ProductNotes/release-notes.md) diff --git a/docs/Guides/_category_.json b/docs/Guides/_category_.json new file mode 100644 index 0000000..55db52b --- /dev/null +++ b/docs/Guides/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Guides", + "position": 10, + "collapsible": true, + "collapsed": false +} + diff --git a/docs/ProductNotes/release-notes.md b/docs/ProductNotes/release-notes.md index b3ea415..19d8b81 100644 --- a/docs/ProductNotes/release-notes.md +++ b/docs/ProductNotes/release-notes.md @@ -21,12 +21,13 @@ New Features: - Couchbase Lite 3.3.0 - Updated iOS and Android SDKs to latest stable version Breaking Changes: -- REMOVED: Database.setLogLevel() - Use LogSinks.setConsole() instead -- API CHANGE: ReplicatorConfiguration - Collections now passed in constructor, not via addCollection() +- TypeScript: ListenerToken type changed from string to ListenerToken object (affects explicitly typed code only) -Deprecated (Still Functional): +Deprecated APIs (Remain available for backward compatibility): +- Database.setLogLevel() - Use LogSinks.setConsole() instead. Note: Old and new logging APIs cannot be used in tandem. - config.addCollection(collection) - Pass CollectionConfiguration array in constructor instead - removeChangeListener() methods - Use token.remove() instead +- ListenerToken type changed from string to ListenerToken object (TypeScript breaking change for explicitly typed code) Bug Fixes: - Fixed encryption key crash when key not required @@ -35,9 +36,12 @@ Bug Fixes: - Fixed custom delete issues Migration from 0.6.x: -1. Replace Database.setLogLevel() with LogSinks.setConsole() -2. Update ReplicatorConfiguration to use new constructor pattern -3. Update listener cleanup to use token.remove() (optional - old way still works) +1. Replace Database.setLogLevel() with LogSinks.setConsole() (required - APIs cannot be mixed) +2. Update ReplicatorConfiguration to use new constructor pattern (recommended) +3. Update listener cleanup to use token.remove() (recommended) +4. Update TypeScript code that explicitly typed tokens as string to use ListenerToken (required for TypeScript) + +See [Migration Guide](../Guides/Migration/v1.md) for detailed instructions. --- diff --git a/docs/Queries/live-queries.md b/docs/Queries/live-queries.md index 82b670e..6f2059f 100644 --- a/docs/Queries/live-queries.md +++ b/docs/Queries/live-queries.md @@ -52,17 +52,124 @@ await token.remove(); ``` :::caution Deprecated -The old `query.removeChangeListener(token)` method is deprecated but still works: +The `query.removeChangeListener(token)` method is deprecated. It remains available for backward compatibility, but new applications should use `token.remove()`. Existing applications are strongly encouraged to migrate. -**Old way (deprecated):** ```typescript -await query.removeChangeListener(token); // Still works +// DEPRECATED +await query.removeChangeListener(token); + +// RECOMMENDED +await token.remove(); ``` +::: + +## Query Change Data Structure + +When a query result changes, your callback receives a `QueryChange` object: -**New way (recommended):** ```typescript -await token.remove(); // Use this +interface QueryChange { + error: string; // Error message if query failed + query: Query; // Reference to the query + results: ResultSet; // Array of result objects +} ``` -::: -For complete information on query change listeners and all other listener types, see [Change Listeners](../change-listeners.md). \ No newline at end of file +#### Example 3. Complete Live Query with Error Handling + +```typescript +import { ListenerToken } from 'cbl-reactnative'; + +const query = database.createQuery( + 'SELECT META().id, name, email FROM _default.users WHERE isActive = true' +); + +const token: ListenerToken = await query.addChangeListener((change) => { + if (change.error) { + console.error('Query error:', change.error); + return; + } + + console.log(`Found ${change.results.length} active users`); + change.results.forEach(result => { + console.log(`User: ${result.name} - ${result.email}`); + }); +}); + +// Remove when done +await token.remove(); +``` + +#### Example 4. React Hook Pattern for Live Queries + +```typescript +import { useEffect, useState } from 'react'; +import { ListenerToken } from 'cbl-reactnative'; + +function ActiveUsersScreen({ database }) { + const [users, setUsers] = useState([]); + + useEffect(() => { + if (!database) return; + + let token; + + const setup = async () => { + const query = database.createQuery( + 'SELECT META().id, name FROM _default.users WHERE isActive = true' + ); + + token = await query.addChangeListener((change) => { + if (!change.error) { + setUsers(change.results); + } + }); + }; + setup(); + + // Cleanup on unmount + return () => { + if (token && !token.isRemoved()) { + token.remove(); + } + }; + }, [database]); + + return ( + // Render users list + ); +} +``` + +## Best Practices + +**Always Remove Listeners** + +In React components, use the cleanup function to prevent memory leaks: + +```typescript +useEffect(() => { + let token; + + const setup = async () => { + token = await query.addChangeListener((change) => { + // Handle changes + }); + }; + setup(); + + return () => { + if (token && !token.isRemoved()) { + token.remove(); + } + }; +}, [query]); +``` + +**Check Before Removing** + +```typescript +if (!token.isRemoved()) { + await token.remove(); +} +``` \ No newline at end of file diff --git a/docs/Queries/sqlplusplus.md b/docs/Queries/sqlplusplus.md index 65d1a94..61e0ec8 100644 --- a/docs/Queries/sqlplusplus.md +++ b/docs/Queries/sqlplusplus.md @@ -6,7 +6,7 @@ sidebar_position: 1 # SQL++ for Mobile > Description - _How to use SQL++ Query Strings to build effective queries with Couchbase Lite for React Native_ -> Related Content - [Live Queries](../live-queries.md) | [Indexes](../indexes.md) +> Related Content - [Live Queries](live-queries.md) | [Indexes](../indexes.md) :::info N1QL is Couchbase's implementation of the developing SQL++ standard. As such the terms N1QL and SQL++ are used interchangeably in all Couchbase documentation unless explicitly stated otherwise. diff --git a/docs/change-listeners.md b/docs/change-listeners.md deleted file mode 100644 index eaf2d6d..0000000 --- a/docs/change-listeners.md +++ /dev/null @@ -1,481 +0,0 @@ ---- -id: change-listeners -sidebar_position: 8 ---- - -# Change Listeners - -> Description — _Couchbase Lite for React Native — Real-time Data Change Notifications_ -> Related Content — [Documents](documents.md) | [Live Queries](Queries/live-queries.md) | [Remote Sync](DataSync/remote-sync-gateway.md) - -:::important AT A GLANCE -Couchbase Lite provides 5 types of change listeners for real-time data notifications: - -* **Collection Change Listeners** - Monitor all document changes in a collection -* **Document Change Listeners** - Monitor a specific document by ID -* **Query Change Listeners** - Monitor query results in real-time (Live Queries) -* **Replicator Status Listeners** - Monitor replication state and progress -* **Replicator Document Listeners** - Monitor individual document replication - -All listeners return a `ListenerToken` object for lifecycle management. -::: - -## Listener Token API - -Version 1.0 introduces the `ListenerToken` class for improved listener management. - -All `addChangeListener` methods return a `ListenerToken` object with these methods: - -**remove()** - Removes the listener (recommended) - -```typescript -const token = await collection.addChangeListener(...); -await token.remove(); -``` - -**getUuidToken()** - Gets the internal UUID string (for debugging) - -```typescript -const uuid = token.getUuidToken(); -console.log('Token:', uuid); // "listener-abc-123-def-456" -``` - -**isRemoved()** - Checks if listener was already removed - -```typescript -if (!token.isRemoved()) { - await token.remove(); -} -``` - -:::caution Deprecated Methods -The old `removeChangeListener()` methods are deprecated but still functional for backward compatibility: - -```typescript -// OLD WAY (deprecated but still works) -await collection.removeChangeListener(token); -await query.removeChangeListener(token); -await replicator.removeChangeListener(token); - -// NEW WAY (recommended) -await token.remove(); -``` - -Use `token.remove()` for all new code. -::: - -## Collection Change Listeners - -Monitor all changes to any document in a collection. - -### When to Use - -- Refresh list views when data changes -- Trigger background sync after local updates -- Audit logging of collection activity -- Real-time collaboration features - -### Data Structure - -When a collection changes, your callback receives a `CollectionChange` object: - -```typescript -interface CollectionChange { - documentIDs: string[]; // Array of changed document IDs - collection: Collection; // Reference to the collection -} -``` - -:::caution Property Names -The property is `documentIDs` (capital IDs), not `documentIds`. - -There is NO direct `database` property. Access it via `change.collection.database`. -::: - -#### Example 1. Basic Collection Listener - -```typescript -import { Collection, ListenerToken } from 'cbl-reactnative'; - -const collection = await database.createCollection('users'); - -const token: ListenerToken = await collection.addChangeListener((change) => { - console.log('Collection changed!'); - console.log('Changed documents:', change.documentIDs); - // Example output: ['user-123', 'user-456'] - - console.log('Collection name:', change.collection.name); - // Output: users - - console.log('Database name:', change.collection.database.getName()); - // Output: mydb -}); - -// Remove listener when done -await token.remove(); -``` - -#### Example 2. React Hook Pattern - -```typescript -import { useEffect } from 'react'; -import { ListenerToken } from 'cbl-reactnative'; - -function UserListScreen({ collection }) { - useEffect(() => { - if (!collection) return; - - let token; - - const setup = async () => { - token = await collection.addChangeListener((change) => { - console.log(`${change.documentIDs.length} documents changed`); - refreshUserList(); - }); - }; - setup(); - - // Cleanup on unmount - return () => { - if (token) token.remove(); - }; - }, [collection]); -} -``` - -:::important Limitation -Only ONE collection-wide listener is allowed per collection instance. Calling `addChangeListener` twice will throw an error. - -However, you can have MULTIPLE document listeners (see next section). -::: - -## Document Change Listeners - -Monitor changes to a specific document by ID. - -### When to Use - -- Watch a user profile for updates -- Monitor a shopping cart -- Track order status changes -- Real-time document collaboration - -### Data Structure - -```typescript -interface DocumentChange { - documentId: string; // Document ID (lowercase Id) - collection: Collection; // Collection reference - database: Database; // Database reference (available!) -} -``` - -:::note -Unlike collection listeners, document change listeners DO have a direct `database` property. -::: - -#### Example 3. Monitor Specific Document - -```typescript -import { ListenerToken } from 'cbl-reactnative'; - -const token: ListenerToken = await collection.addDocumentChangeListener( - 'user-123', - async (change) => { - console.log(`Document ${change.documentId} changed`); - - // Fetch latest version - const doc = await collection.document(change.documentId); - if (doc) { - console.log('New data:', doc.getData()); - } else { - console.log('Document was deleted'); - } - } -); - -// Remove when done -await token.remove(); -``` - -#### Example 4. Multiple Document Listeners - -```typescript -// Monitor multiple specific documents -const userToken = await collection.addDocumentChangeListener('user-123', (change) => { - console.log('User changed'); -}); - -const cartToken = await collection.addDocumentChangeListener('cart-abc', (change) => { - console.log('Cart changed'); -}); - -// Cleanup all -await Promise.all([userToken.remove(), cartToken.remove()]); -``` - -## Query Change Listeners - -Monitor query results in real-time. Also known as "Live Queries". - -### Data Structure - -```typescript -interface QueryChange { - error: string; // Error message if query failed - query: Query; // Reference to the query - results: ResultSet; // Array of result objects -} -``` - -#### Example 5. Live Query - -```typescript -import { ListenerToken } from 'cbl-reactnative'; - -const query = database.createQuery( - 'SELECT META().id, name, email FROM _default.users WHERE isActive = true' -); - -const token: ListenerToken = await query.addChangeListener((change) => { - if (change.error) { - console.error('Query error:', change.error); - return; - } - - console.log(`Found ${change.results.length} active users`); - change.results.forEach(result => { - console.log(`User: ${result.name} - ${result.email}`); - }); -}); - -// Remove when done -await token.remove(); -``` - -For more information on live queries, see [Live Queries](Queries/live-queries.md). - -## Replicator Status Change Listeners - -Monitor replication status, progress, and errors. - -### Data Structure - -```typescript -interface ReplicatorStatusChange { - status: ReplicatorStatus; -} -``` - -**ReplicatorStatus Methods:** -- `getActivityLevel()` - Returns 0-4 (see table below) -- `getProgress()` - Returns ReplicatorProgress object -- `getError()` - Returns error message string or undefined - -**ReplicatorProgress Methods:** -- `getCompleted()` - Returns number of changes completed -- `getTotal()` - Returns total number of changes - -**Activity Levels:** - -| Value | Constant | Meaning | -|-------|----------|---------| -| 0 | STOPPED | Replication stopped or fatal error | -| 1 | OFFLINE | Cannot reach remote server | -| 2 | CONNECTING | Connecting to server | -| 3 | IDLE | Connected, no changes to sync | -| 4 | BUSY | Actively transferring data | - -#### Example 6. Monitor Replication Status - -```typescript -import { ListenerToken, ReplicatorActivityLevel } from 'cbl-reactnative'; - -const token: ListenerToken = await replicator.addChangeListener((change) => { - const status = change.status; - const level = status.getActivityLevel(); - const progress = status.getProgress(); - - console.log(`Activity: ${level}`); - console.log(`Progress: ${progress.getCompleted()}/${progress.getTotal()}`); - - if (status.getError()) { - console.error('Replication error:', status.getError()); - } - - // Check specific states - if (level === ReplicatorActivityLevel.IDLE) { - console.log('Sync complete'); - } else if (level === ReplicatorActivityLevel.BUSY) { - console.log('Syncing data...'); - } -}); - -// Remove when done -await token.remove(); -``` - -#### Example 7. Deprecated Removal Pattern - -```typescript -const token = await replicator.addChangeListener(...); - -// OLD WAY (deprecated but still works) -await replicator.removeChangeListener(token); - -// NEW WAY (recommended) -await token.remove(); -``` - -:::caution Deprecated -`replicator.removeChangeListener(token)` is deprecated. Use `token.remove()` instead. -::: - -## Replicator Document Change Listeners - -Monitor individual documents being replicated. - -### When to Use - -- Track which specific documents are syncing -- Detect replication conflicts -- Handle document-level replication errors -- Monitor deleted documents during sync - -### Data Structure - -```typescript -interface DocumentReplicationRepresentation { - isPush: boolean; // true = push, false = pull - documents: ReplicatedDocument[]; -} - -interface ReplicatedDocument { - id: string; // Document ID - scopeName: string; // Scope name - collectionName: string; // Collection name - flags: string[]; // ['DELETED', 'ACCESS_REMOVED'] - error?: { message: string }; // Present if replication failed -} -``` - -**Document Flags:** -- `'DELETED'` - Document was deleted -- `'ACCESS_REMOVED'` - User lost access to document - -#### Example 8. Monitor Document Replication - -```typescript -import { ListenerToken } from 'cbl-reactnative'; - -const token: ListenerToken = await replicator.addDocumentChangeListener((change) => { - const direction = change.isPush ? 'PUSH' : 'PULL'; - console.log(`${direction}: ${change.documents.length} documents`); - - change.documents.forEach(doc => { - console.log(` ${doc.id}`); - - if (doc.flags.includes('DELETED')) { - console.log(' Document was deleted'); - } - - if (doc.error) { - console.error(` Error: ${doc.error.message}`); - } - }); -}); - -// Remove when done -await token.remove(); -``` - -## Best Practices - -**Always Remove Listeners** - -In React components, use the cleanup function: - -```typescript -useEffect(() => { - let token; - - const setup = async () => { - token = await collection.addChangeListener((change) => { - // Handle changes - }); - }; - setup(); - - return () => { - if (token && !token.isRemoved()) { - token.remove(); - } - }; -}, [collection]); -``` - -**Check Before Removing** - -```typescript -if (!token.isRemoved()) { - await token.remove(); -} -``` - -## Common Pitfalls - -:::caution Property Name Gotchas -**Collection Change Listener:** -```typescript -change.documentIDs // CORRECT (capital IDs) -change.documentIds // WRONG - will be undefined - -change.collection.database // CORRECT way to access database -change.database // WRONG - property doesn't exist -``` - -**Document Change Listener:** -```typescript -change.documentId // CORRECT (lowercase Id) -change.documentID // WRONG - will be undefined - -change.database // CORRECT - direct property exists -``` - -Getting these property names wrong is the most common source of errors. -::: - -**Multiple Collection Listeners** - -```typescript -// WRONG - will throw error -const token1 = await collection.addChangeListener(...); -const token2 = await collection.addChangeListener(...); // ERROR! - -// CORRECT - only one collection listener per instance -const token = await collection.addChangeListener(...); - -// CORRECT - multiple document listeners are fine -const t1 = await collection.addDocumentChangeListener('user-123', ...); -const t2 = await collection.addDocumentChangeListener('user-456', ...); -``` - -**Async Handling in Callbacks** - -```typescript -// WRONG - promise not awaited -const token = await collection.addChangeListener((change) => { - change.documentIDs.forEach(id => { - collection.document(id); // Returns promise, not awaited! - }); -}); - -// CORRECT - proper async handling -const token = await collection.addChangeListener(async (change) => { - const docs = await Promise.all( - change.documentIDs.map(id => collection.document(id)) - ); - console.log('Documents:', docs); -}); -``` - diff --git a/docs/databases.md b/docs/databases.md index 6bcb724..7f85300 100644 --- a/docs/databases.md +++ b/docs/databases.md @@ -176,7 +176,7 @@ You can download and build it from the couchbaselabs [GitHub repository](https:/ ## Troubleshooting -You should use console logs as your first source of diagnostic information. If the information in the default logging level is insufficient you can focus it on database errors and generate more verbose messages. +You should use console logs as your first source of diagnostic information. If the default logging level is insufficient, you can enable verbose logging scoped specifically to database operations. ```typescript import { LogSinks, LogLevel, LogDomain } from 'cbl-reactnative'; @@ -192,8 +192,54 @@ try { } ``` +:::caution +Enable VERBOSE logging only during development or debugging. Avoid using it in production builds. +::: + For more on using logs for troubleshooting, see [Using Logs](Troubleshooting/using-logs.md). +## Listener Token API + +Version 1.0 introduces the `ListenerToken` class for improved listener management across all change listener types. + +All `addChangeListener` methods return a `ListenerToken` object with these methods: + +**remove()** - Removes the listener (recommended) + +```typescript +const token = await collection.addChangeListener(...); +await token.remove(); +``` + +**getUuidToken()** - Gets the internal UUID string (for debugging) + +```typescript +const uuid = token.getUuidToken(); +console.log('Token:', uuid); // "listener-abc-123-def-456" +``` + +**isRemoved()** - Checks if listener was already removed + +```typescript +if (!token.isRemoved()) { + await token.remove(); +} +``` + +:::caution Deprecated Methods +The `removeChangeListener()` methods on Collection, Query, and Replicator are deprecated. This method is deprecated. It remains available for backward compatibility, but new applications should use `token.remove()`. Existing applications are strongly encouraged to migrate. + +```typescript +// DEPRECATED +await collection.removeChangeListener(token); +await query.removeChangeListener(token); +await replicator.removeChangeListener(token); + +// RECOMMENDED +await token.remove(); +``` +::: + diff --git a/docs/documents.md b/docs/documents.md index 7d654a6..3cda1e5 100644 --- a/docs/documents.md +++ b/docs/documents.md @@ -334,37 +334,104 @@ document.setDate('createdAt', new Date()); await collection.save(document); ``` -## Document change events +## Document Change Listeners -Couchbase Lite allows you to register listeners to be notified when documents change. Version 1.0 introduces the `ListenerToken` API for better listener lifecycle management. +Monitor changes to a specific document by ID. Couchbase Lite allows you to register listeners to be notified when documents change. Version 1.0 introduces the `ListenerToken` API for better listener lifecycle management. -#### Example 7. Document Change Listener +### When to Use + +- Watch a user profile for updates +- Monitor a shopping cart +- Track order status changes +- Real-time document collaboration + +### Data Structure + +```typescript +interface DocumentChange { + documentId: string; // Document ID (lowercase Id) + collection: Collection; // Collection reference + database: Database; // Database reference (available!) +} +``` + +:::note +Unlike collection listeners, document change listeners DO have a direct `database` property. +::: + +#### Example 7. Monitor Specific Document ```typescript import { ListenerToken } from 'cbl-reactnative'; const token: ListenerToken = await collection.addDocumentChangeListener( - 'user.john', + 'user-123', async (change) => { - // Note: Property is 'documentId' (lowercase Id), not 'documentID' - const document = await collection.document(change.documentId); - if (document !== null) { - console.log(`Status: ${document.getString('verified_account')}`); + console.log(`Document ${change.documentId} changed`); + + // Fetch latest version + const doc = await collection.document(change.documentId); + if (doc) { + console.log('New data:', doc.getData()); + } else { + console.log('Document was deleted'); } } ); -// Remove listener when no longer needed +// Remove when done await token.remove(); ``` +#### Example 8. Multiple Document Listeners + +```typescript +// Monitor multiple specific documents +const userToken = await collection.addDocumentChangeListener('user-123', (change) => { + console.log('User changed'); +}); + +const cartToken = await collection.addDocumentChangeListener('cart-abc', (change) => { + console.log('Cart changed'); +}); + +// Cleanup all +await Promise.all([userToken.remove(), cartToken.remove()]); +``` + :::tip New in Version 1.0 Change listeners now return a `ListenerToken` object. Use `token.remove()` to remove the listener. +::: -The old `collection.removeDocumentChangeListener(token)` method is deprecated but still works. +:::caution Deprecated +The `collection.removeDocumentChangeListener(token)` method is deprecated. It remains available for backward compatibility, but new applications should use `token.remove()`. Existing applications are strongly encouraged to migrate. ::: -For complete information on all change listener types including collection-wide listeners, query listeners, and replicator listeners, see [Change Listeners](change-listeners.md). +### Common Pitfalls + +:::caution Property Name +```typescript +change.documentId // CORRECT (lowercase Id) +change.documentID // WRONG - will be undefined +``` + +Getting this property name wrong is a common source of errors. +::: + +**Async Handling in Callbacks** + +```typescript +// WRONG - promise not awaited +const token = await collection.addDocumentChangeListener('doc-id', (change) => { + collection.document(change.documentId); // Returns promise, not awaited! +}); + +// CORRECT - proper async handling +const token = await collection.addDocumentChangeListener('doc-id', async (change) => { + const doc = await collection.document(change.documentId); + console.log('Document:', doc); +}); +``` ## Document Expiration diff --git a/docs/scopes-collections.md b/docs/scopes-collections.md index cd9ff52..e276d93 100644 --- a/docs/scopes-collections.md +++ b/docs/scopes-collections.md @@ -108,6 +108,8 @@ const collections = await database.collections(scopeName); The `fullName()` method returns the fully qualified name of a collection in the format "scope.collection". +The full name is useful for logging, debugging, and displaying collection information in your application. + **Example 5. Get Collection Full Name** ```typescript @@ -124,4 +126,91 @@ console.log(defaultName); // Output: "_default._default" ``` -The full name is useful for logging, debugging, and displaying collection information in your application. \ No newline at end of file +## Collection Change Listeners + +Monitor all changes to any document in a collection. + +### When to Use + +- Refresh list views when data changes +- Trigger background sync after local updates +- Audit logging of collection activity +- Real-time collaboration features + +### Data Structure + +When a collection changes, your callback receives a `CollectionChange` object: + +```typescript +interface CollectionChange { + documentIDs: string[]; // Array of changed document IDs + collection: Collection; // Reference to the collection +} +``` + +:::caution Property Names +The property is `documentIDs` (capital IDs), not `documentIds`. + +There is NO direct `database` property. Access it via `change.collection.database`. +::: + +#### Example 6. Basic Collection Listener + +```typescript +import { Collection, ListenerToken } from 'cbl-reactnative'; + +const collection = await database.createCollection('users'); + +const token: ListenerToken = await collection.addChangeListener((change) => { + console.log('Collection changed!'); + console.log('Changed documents:', change.documentIDs); + // Example output: ['user-123', 'user-456'] + + console.log('Collection name:', change.collection.name); + // Output: users + + console.log('Database name:', change.collection.database.getName()); + // Output: mydb +}); + +// Remove listener when done +await token.remove(); +``` + +#### Example 7. React Hook Pattern + +```typescript +import { useEffect } from 'react'; +import { ListenerToken } from 'cbl-reactnative'; + +function UserListScreen({ collection }) { + useEffect(() => { + if (!collection) return; + + let token; + + const setup = async () => { + token = await collection.addChangeListener((change) => { + console.log(`${change.documentIDs.length} documents changed`); + refreshUserList(); + }); + }; + setup(); + + // Cleanup on unmount + return () => { + if (token) token.remove(); + }; + }, [collection]); +} +``` + +:::important Limitation +Only ONE collection-wide listener is allowed per collection instance. Calling `addChangeListener` twice will throw an error. + +However, you can have MULTIPLE document listeners on the same collection. +::: + +:::caution Deprecated +The `collection.removeChangeListener(token)` method is deprecated. It remains available for backward compatibility, but new applications should use `token.remove()`. Existing applications are strongly encouraged to migrate. +::: \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9db1f8d..69aa7b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4563,6 +4563,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" From c8d2efa6328313296b900191247f78eceb8e8441 Mon Sep 17 00:00:00 2001 From: jayant dhingra Date: Wed, 24 Dec 2025 19:04:27 +0530 Subject: [PATCH 3/3] Enhance documentation with migration guide for version 1.0, update breaking changes section, and adjust category positions for guides and product notes. --- docs/Guides/Migration/v1.md | 58 +++++++++++++----------------- docs/Guides/_category_.json | 2 +- docs/ProductNotes/_category_.json | 2 +- docs/ProductNotes/release-notes.md | 2 +- docs/intro.md | 4 +++ 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/docs/Guides/Migration/v1.md b/docs/Guides/Migration/v1.md index 70bb7f8..62f8970 100644 --- a/docs/Guides/Migration/v1.md +++ b/docs/Guides/Migration/v1.md @@ -3,7 +3,7 @@ id: migration-guide-v1 sidebar_position: 1 --- -# Migration Guide: Version 1.0 +# Version 1.0 > Description — _Quick reference for all API changes in cbl-reactnative version 1.0_ > Related Content — [Release Notes](../../ProductNotes/release-notes.md) | [Using Logs](../../Troubleshooting/using-logs.md) @@ -16,6 +16,29 @@ Version 1.0 introduces several API changes: 3. **New ReplicatorConfiguration** - Collections passed in constructor ::: +## Breaking Changes + +### Listener Token Return Type (TypeScript) + +We changed all `addChangeListener` functions to return a `ListenerToken` instead of a `string`, enabling the new `token.remove()` API and aligning with other platforms. + +This is a breaking change **only for TypeScript code** that explicitly typed the token as `string`. JavaScript users and untyped TypeScript won't be affected. + +**TypeScript Migration Example:** + +```typescript +// BEFORE (TypeScript compilation error in 1.0) +const token: string = await collection.addChangeListener(listener); + +// AFTER +const token: ListenerToken = await collection.addChangeListener(listener); +token.remove(); +``` + +The existing `removeChangeListener(token)` methods remain available for backward compatibility but are deprecated. + +--- + ## Deprecated APIs ### 1. Logging API @@ -75,7 +98,7 @@ See: [Remote Sync](../../DataSync/remote-sync-gateway.md#example-1-replication-c --- -### 3. Listener Removal Methods +### 3. Listener Removal Methods (Deprecated) **OLD (Deprecated in 1.0):** ```typescript @@ -93,27 +116,6 @@ This method is deprecated. It remains available for backward compatibility, but --- -### 4. Listener Token Return Type (TypeScript) - -We changed all `addChangeListener` functions to return a `ListenerToken` instead of a `string`, enabling the new `token.remove()` API and aligning with other platforms. - -This is a small breaking change **only for TypeScript code** that explicitly typed the token as `string`. JavaScript users and untyped TypeScript won't be affected. - -**TypeScript Migration Example:** - -```typescript -// BEFORE (TypeScript compilation error in 1.0) -const token: string = await collection.addChangeListener(listener); - -// AFTER -const token: ListenerToken = await collection.addChangeListener(listener); -token.remove(); -``` - -The existing `removeChangeListener(token)` methods remain available for backward compatibility but are deprecated. - ---- - ## New Features in 1.0 ### 1. Three Log Sink Types @@ -234,16 +236,6 @@ await collection.removeChangeListener(token); await token.remove(); ``` -**Step 4: Update TypeScript Token Types (Required for TypeScript)** - -```typescript -// Old (compilation error) -const token: string = await collection.addChangeListener(...); - -// New -const token: ListenerToken = await collection.addChangeListener(...); -``` - --- ## Important Property Names diff --git a/docs/Guides/_category_.json b/docs/Guides/_category_.json index 55db52b..3ced3d2 100644 --- a/docs/Guides/_category_.json +++ b/docs/Guides/_category_.json @@ -1,6 +1,6 @@ { "label": "Guides", - "position": 10, + "position": 17, "collapsible": true, "collapsed": false } diff --git a/docs/ProductNotes/_category_.json b/docs/ProductNotes/_category_.json index bc28e72..b1052d5 100644 --- a/docs/ProductNotes/_category_.json +++ b/docs/ProductNotes/_category_.json @@ -1,6 +1,6 @@ { "label": "Product Notes", - "position": 17, + "position": 18, "link": { "type": "generated-index", "description": "Learn more about releases, compatibility, and supported platforms." diff --git a/docs/ProductNotes/release-notes.md b/docs/ProductNotes/release-notes.md index 19d8b81..b0514ca 100644 --- a/docs/ProductNotes/release-notes.md +++ b/docs/ProductNotes/release-notes.md @@ -18,7 +18,7 @@ New Features: - Replicator Document Change Listeners - Track individual document replication - New ReplicatorConfiguration API - Collections passed during initialization using CollectionConfiguration - Collection.fullName() method - Get fully qualified collection name (scope.collection) -- Couchbase Lite 3.3.0 - Updated iOS and Android SDKs to latest stable version +- Couchbase Lite 3.3.0 - Updated iOS and Android SDKs to Couchbase Lite 3.3.0 Breaking Changes: - TypeScript: ListenerToken type changed from string to ListenerToken object (affects explicitly typed code only) diff --git a/docs/intro.md b/docs/intro.md index ed79c8a..7a4db9a 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -50,6 +50,10 @@ Couchbase Lite for React Native is a community provided solution that is activel * Encryption - Full Database +## Upgrading from 0.6.x? + +See the [Migration Guide](Guides/Migration/v1.md) for detailed instructions on upgrading to version 1.0. + ## Limitations Some of the features supported by other platform implementations of Couchbase Lite are currently not supported: