From b3bcf01561b4f3e4a56c506e1b5beb67a25576db Mon Sep 17 00:00:00 2001 From: benitav Date: Thu, 8 Feb 2024 13:17:36 +0200 Subject: [PATCH 1/9] First pass of porting comments from FLutter into sdk-common --- .../src/client/AbstractPowerSyncDatabase.ts | 45 ++++++++++++++++++- .../client/AbstractPowerSyncOpenFactory.ts | 3 ++ .../src/client/sync/bucket/CrudBatch.ts | 12 +++++ .../src/client/sync/bucket/CrudEntry.ts | 27 +++++++++++ .../src/client/sync/bucket/CrudTransaction.ts | 12 +++++ .../client/sync/bucket/SqliteBucketStorage.ts | 2 - .../src/db/crud/SyncStatus.ts | 9 ++++ .../src/db/crud/UploadQueueStatus.ts | 6 +++ 8 files changed, 112 insertions(+), 4 deletions(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index bad6431b..958cbce6 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -114,14 +114,25 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver cb.initialized?.()); } + /** + * Replace the schema with a new version. This is for advanced use cases - typically the schema should just be specified once in the constructor. + * + * Cannot be used while connected - this should only be called before [AbstractPowerSyncDatabase.connect]. + */ async updateSchema(schema: Schema) { if (this.abortController) { throw new Error('Cannot update schema while connected'); @@ -226,6 +242,11 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver(callback: (db: DBAdapter) => Promise) { @@ -466,6 +488,12 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver( callback: (tx: Transaction) => Promise, lockTimeout: number = DEFAULT_LOCK_TIMEOUT_MS @@ -481,6 +509,11 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver( callback: (tx: Transaction) => Promise, lockTimeout: number = DEFAULT_LOCK_TIMEOUT_MS @@ -496,6 +529,11 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver { //Fetch initial data yield await this.executeReadOnly(sql, parameters); @@ -582,6 +620,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver tx.execute(sql, params)); diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts index 7d7e8ab0..dfaa1e9d 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts @@ -20,6 +20,9 @@ export abstract class AbstractPowerSyncDatabaseOpenFactory { options.logger = options.logger ?? Logger.get(`PowerSync ${this.options.dbFilename}`); } + /** + * Schema used for the local database. + */ get schema() { return this.options.schema; } diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts b/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts index 51ac45fc..683f56fb 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts @@ -1,9 +1,21 @@ import { CrudEntry } from './CrudEntry'; +/** + * TODO + */ export class CrudBatch { constructor( + /** + * List of client-side changes. + */ public crud: CrudEntry[], + /** + * true if there are more changes in the local queue. + */ public haveMore: boolean, + /** + * Call to remove the changes from the local queue, once successfully uploaded. + */ public complete: (writeCheckpoint?: string) => Promise ) {} } diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts b/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts index 25732ae7..a12fe3d0 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts @@ -38,12 +38,33 @@ export type CrudEntryOutputJSON = { data: Record; }; +/** + * A single client-side change. + */ export class CrudEntry { + /** + * Auto-incrementing client-side id. + */ clientId: number; + /** + * ID of the changed row. + */ id: string; + /** + * Type of change. + */ op: UpdateType; + /** + * Data associated with the change. + */ opData?: Record; + /** + * Table that contained the change. + */ table: string; + /** + * Auto-incrementing transaction id. This is the same for all operations within the same transaction. + */ transactionId?: number; static fromRow(dbRow: CrudEntryJSON) { @@ -67,6 +88,9 @@ export class CrudEntry { this.transactionId = transactionId; } + /** + * TODO: Converts the change to JSON format, as required by the dev crud API. + */ toJSON(): CrudEntryOutputJSON { return { op_id: this.clientId, @@ -78,6 +102,9 @@ export class CrudEntry { }; } + /** + * The hash code for this object. + */ hashCode() { return hash([this.transactionId, this.clientId, this.op, this.table, this.id, this.opData]); } diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts b/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts index 1a726b23..ad5f6ee9 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts @@ -1,10 +1,22 @@ import { CrudBatch } from './CrudBatch'; import { CrudEntry } from './CrudEntry'; +/** + * TODO + */ export class CrudTransaction extends CrudBatch { constructor( + /** + * List of client-side changes. + */ public crud: CrudEntry[], + /** + * Call to remove the changes from the local queue, once successfully uploaded. + */ public complete: (checkpoint?: string) => Promise, + /** + * Unique transaction id. + */ public transactionId?: number ) { super(crud, false, complete); diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/SqliteBucketStorage.ts b/packages/powersync-sdk-common/src/client/sync/bucket/SqliteBucketStorage.ts index 6a0310d2..0c727ee9 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/SqliteBucketStorage.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/SqliteBucketStorage.ts @@ -80,8 +80,6 @@ export class SqliteBucketStorage implements BucketStorageAdapter { /** * Mark a bucket for deletion. - * - * @param bucket */ private async deleteBucket(bucket: string) { // Delete a bucket, but allow it to be re-created. diff --git a/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts b/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts index e4d5e14f..a5f30bf1 100644 --- a/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts +++ b/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts @@ -14,14 +14,23 @@ export type SyncStatusOptions = { export class SyncStatus { constructor(protected options: SyncStatusOptions) {} + /** + * true if currently connected. + */ get connected() { return this.options.connected ?? false; } + /** + * TODO + */ get lastSyncedAt() { return this.options.lastSyncedAt; } + /** + * TODO + */ get dataFlowStatus() { return ( this.options.dataFlow ?? { diff --git a/packages/powersync-sdk-common/src/db/crud/UploadQueueStatus.ts b/packages/powersync-sdk-common/src/db/crud/UploadQueueStatus.ts index 8ad7e5d2..53fcde73 100644 --- a/packages/powersync-sdk-common/src/db/crud/UploadQueueStatus.ts +++ b/packages/powersync-sdk-common/src/db/crud/UploadQueueStatus.ts @@ -1,6 +1,12 @@ export class UploadQueueStats { constructor( + /** + * Number of records in the upload queue. + */ public count: number, + /** + * Size of the upload queue in bytes. + */ public size: number = null ) {} From 9142c2c2f773f678fce0e970a9955ac228b875e8 Mon Sep 17 00:00:00 2001 From: benitav Date: Thu, 8 Feb 2024 14:48:25 +0200 Subject: [PATCH 2/9] Update packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts --- .../powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts b/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts index a12fe3d0..481104c9 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts @@ -89,7 +89,7 @@ export class CrudEntry { } /** - * TODO: Converts the change to JSON format, as required by the dev crud API. + * Converts the change to JSON format. */ toJSON(): CrudEntryOutputJSON { return { From 2af28217602b73f358dccc81384002c5a6361b9c Mon Sep 17 00:00:00 2001 From: benitav Date: Thu, 8 Feb 2024 15:41:01 +0200 Subject: [PATCH 3/9] More JSDoc comments --- .../src/hooks/usePowerSyncQuery.ts | 2 +- .../src/client/AbstractPowerSyncDatabase.ts | 6 ++++-- .../client/AbstractPowerSyncOpenFactory.ts | 1 + .../connection/PowerSyncBackendConnector.ts | 2 +- .../powersync-sdk-common/src/db/DBAdapter.ts | 20 +++++++++++-------- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/powersync-react/src/hooks/usePowerSyncQuery.ts b/packages/powersync-react/src/hooks/usePowerSyncQuery.ts index 0fed38d0..955736dd 100644 --- a/packages/powersync-react/src/hooks/usePowerSyncQuery.ts +++ b/packages/powersync-react/src/hooks/usePowerSyncQuery.ts @@ -3,7 +3,7 @@ import { usePowerSync } from './PowerSyncContext'; /** * A hook to access a single static query. - * For an updated result, use usePowerSyncWatchedQuery instead + * For an updated result, use {@link usePowerSyncWatchedQuery} instead */ export const usePowerSyncQuery = (sqlStatement: string, parameters: any[] = []): T[] => { const powerSync = usePowerSync(); diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 958cbce6..425af11d 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -21,10 +21,12 @@ import { EventIterator } from 'event-iterator'; import { quoteIdentifier } from '../utils/strings'; export interface DisconnectAndClearOptions { + /** When set to false, data in local-only tables is preserved. */ clearLocal?: boolean; } export interface PowerSyncDatabaseOptions { + /** Schema used for the local database. */ schema: Schema; database: DBAdapter; /** @@ -436,7 +438,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver(sql: string, parameters?: any[]): Promise { await this.waitForReady(); diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts index dfaa1e9d..2273605e 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncOpenFactory.ts @@ -4,6 +4,7 @@ import { Schema } from '../db/schema/Schema'; import { AbstractPowerSyncDatabase, PowerSyncDatabaseOptions } from './AbstractPowerSyncDatabase'; export interface PowerSyncOpenFactoryOptions extends Partial { + /** Schema used for the local database. */ schema: Schema; /** * Filename for the database. diff --git a/packages/powersync-sdk-common/src/client/connection/PowerSyncBackendConnector.ts b/packages/powersync-sdk-common/src/client/connection/PowerSyncBackendConnector.ts index 10c9ae11..131f3024 100644 --- a/packages/powersync-sdk-common/src/client/connection/PowerSyncBackendConnector.ts +++ b/packages/powersync-sdk-common/src/client/connection/PowerSyncBackendConnector.ts @@ -16,7 +16,7 @@ export interface PowerSyncBackendConnector { /** Upload local changes to the app backend. * - * Use [PowerSyncDatabase.getCrudBatch] to get a batch of changes to upload. See [DevConnector] for an example implementation. + * Use {@link AbstractPowerSyncDatabase.getCrudBatch} to get a batch of changes to upload. * * Any thrown errors will result in a retry after the configured wait period (default: 5 seconds). */ diff --git a/packages/powersync-sdk-common/src/db/DBAdapter.ts b/packages/powersync-sdk-common/src/db/DBAdapter.ts index 26aba198..159c2aed 100644 --- a/packages/powersync-sdk-common/src/db/DBAdapter.ts +++ b/packages/powersync-sdk-common/src/db/DBAdapter.ts @@ -11,18 +11,14 @@ import { BaseListener, BaseObserverInterface } from '../utils/BaseObserver'; */ /** - * Object returned by SQL Query executions { - * insertId: Represent the auto-generated row id if applicable - * rowsAffected: Number of affected rows if result of a update query - * message: if status === 1, here you will find error description - * rows: if status is undefined or 0 this object will contain the query results - * } - * - * @interface QueryResult + * Object returned by SQL Query executions. */ export type QueryResult = { + /** Represents the auto-generated row id if applicable. */ insertId?: number; + /** Number of affected rows if result of a update query. */ rowsAffected: number; + /** if status is undefined or 0 this object will contain the query results */ rows?: { /** Raw array with all dataset */ _array: any[]; @@ -37,12 +33,16 @@ export type QueryResult = { }; export interface DBGetUtils { + /** Execute a read-only query and return results. */ getAll(sql: string, parameters?: any[]): Promise; + /** Execute a read-only query and return the first result, or null if the ResultSet is empty. */ getOptional(sql: string, parameters?: any[]): Promise; + /** Execute a read-only query and return the first result, error if the ResultSet is empty. */ get(sql: string, parameters?: any[]): Promise; } export interface LockContext extends DBGetUtils { + /** Execute a statement and optionally return results. */ execute: (query: string, params?: any[] | undefined) => Promise; } @@ -63,6 +63,10 @@ export interface TableUpdateOperation { opType: RowUpdateType; rowId: number; } + +/** + * Notification of an update to one or more tables, for the purpose of realtime change notifications. + */ export interface UpdateNotification extends TableUpdateOperation { table: string; } From 2fbded17fa3d4068266e16374257beac15f8bbc6 Mon Sep 17 00:00:00 2001 From: benitav Date: Thu, 8 Feb 2024 16:01:34 +0200 Subject: [PATCH 4/9] Valid JSdoc links --- .../src/client/AbstractPowerSyncDatabase.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 425af11d..31594596 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -247,7 +247,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver { @@ -471,7 +471,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver(callback: (db: DBAdapter) => Promise) { await this.waitForReady(); @@ -480,7 +480,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver(callback: (db: DBAdapter) => Promise) { await this.waitForReady(); @@ -564,7 +564,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver Date: Thu, 8 Feb 2024 16:10:17 +0200 Subject: [PATCH 5/9] Another link --- .../src/client/AbstractPowerSyncDatabase.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 31594596..92df3c43 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -126,7 +126,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver Date: Thu, 8 Feb 2024 17:37:04 +0200 Subject: [PATCH 6/9] Apply suggestions from code review Co-authored-by: stevensJourney <51082125+stevensJourney@users.noreply.github.com> --- .../src/client/AbstractPowerSyncDatabase.ts | 2 +- .../powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts | 2 +- .../src/client/sync/bucket/CrudTransaction.ts | 2 +- packages/powersync-sdk-common/src/db/crud/SyncStatus.ts | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 92df3c43..c395f820 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -126,7 +126,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver Promise, /** - * Unique transaction id. + * If null, this contains a list of changes recorded without an explicit transaction associated. */ public transactionId?: number ) { diff --git a/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts b/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts index a5f30bf1..1241ab42 100644 --- a/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts +++ b/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts @@ -22,7 +22,8 @@ export class SyncStatus { } /** - * TODO + * Time that a last sync has fully completed, if any. + * Currently this is reset to null after a restart. */ get lastSyncedAt() { return this.options.lastSyncedAt; From c6639d3c705d41a1dab38d83d13f475418750a21 Mon Sep 17 00:00:00 2001 From: benitav Date: Thu, 8 Feb 2024 17:48:22 +0200 Subject: [PATCH 7/9] Resolve TODOs --- .../src/client/AbstractPowerSyncDatabase.ts | 8 +++----- .../src/client/sync/bucket/CrudTransaction.ts | 3 --- packages/powersync-sdk-common/src/db/crud/SyncStatus.ts | 9 ++++++++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index c395f820..7cfa8901 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -46,6 +46,7 @@ export interface PowerSyncDatabaseOptions { export interface SQLWatchOptions { signal?: AbortSignal; tables?: string[]; + /** The minimum interval between queries. */ throttleMs?: number; /** * Allows for watching any SQL table @@ -533,8 +534,8 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver { //Fetch initial data @@ -622,9 +623,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver tx.execute(sql, params)); diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts b/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts index 7b81ec8c..8b2955f8 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/CrudTransaction.ts @@ -1,9 +1,6 @@ import { CrudBatch } from './CrudBatch'; import { CrudEntry } from './CrudEntry'; -/** - * TODO - */ export class CrudTransaction extends CrudBatch { constructor( /** diff --git a/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts b/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts index 1241ab42..49047503 100644 --- a/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts +++ b/packages/powersync-sdk-common/src/db/crud/SyncStatus.ts @@ -30,12 +30,19 @@ export class SyncStatus { } /** - * TODO + * Upload/download status */ get dataFlowStatus() { return ( this.options.dataFlow ?? { + /** + * true if actively downloading changes. + * This is only true when {@link connected} is also true. + */ downloading: false, + /** + * true if uploading changes. + */ uploading: false } ); From 1158be11c5fac05b4831a91b9265fa9061f49643 Mon Sep 17 00:00:00 2001 From: benitav Date: Thu, 8 Feb 2024 17:49:32 +0200 Subject: [PATCH 8/9] Update packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts --- .../powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 7cfa8901..d911e683 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -493,7 +493,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver Date: Thu, 8 Feb 2024 17:52:51 +0200 Subject: [PATCH 9/9] More polishes --- .../src/client/AbstractPowerSyncDatabase.ts | 2 +- .../powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index 7cfa8901..33d33b24 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -515,7 +515,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver( callback: (tx: Transaction) => Promise, diff --git a/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts b/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts index 4aa2b328..6891f24e 100644 --- a/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts +++ b/packages/powersync-sdk-common/src/client/sync/bucket/CrudBatch.ts @@ -1,7 +1,7 @@ import { CrudEntry } from './CrudEntry'; /** -* A batch of client-side changes. + * A batch of client-side changes. */ export class CrudBatch { constructor(